diff --git a/.github/ISSUE_TEMPLATE/issue-report.md b/.github/ISSUE_TEMPLATE/issue-report.md new file mode 100644 index 00000000000..ea0f0aa4093 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue-report.md @@ -0,0 +1,62 @@ +--- +name: Issue report +about: Create a report about a bug or other issue +title: '' +labels: '' +assignees: '' + +--- + + + +#### Description of issue + + + +#### Difference between expected and actual behavior + + + +#### Steps to reproduce + + + +#### Specific information for locating + + + + +#### Length of time in which bug has been known to occur + + + + +#### Client version, Server revision & Game ID + + + + +#### Issue bingo + + +- [ ] Issue could be reproduced at least once +- [ ] Issue could be reproduced by different players +- [ ] Issue could be reproduced in multiple rounds +- [ ] Issue happened in a recent (less than 7 days ago) round +- [ ] [Couldn't find an existing issue about this](https://github.com/NebulaSS13/Nebula/issues) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c858b868b2..3940d892dd0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,3 +30,14 @@ This is a quick and dirty set of agreed-upon standards for contributions to the - If there's a personal dislike of the PR, post about it for discussion. Maybe have an 'on hold for discussion' label. Try to reach a consensus/compromise. Failing a compromise, a majority maintainer vote will decide. - First person to review approves the PR, second person to review can merge it. If 24 hours pass with no objections, first person can merge the PR themselves. - PRs can have a 24 hour grace period applied by maintainers if it seems important for discussion and responses to be involved. Don't merge for the grace period if applied (reviews are fine). + +### Footguns +A footgun is a pattern, function, assumption etc. that stands a strong chance to shoot you in the foot. They are documented here for ease of reference by new contributors. + +#### List footguns +- Adding lists to lists will actually perform a merge, rather than inserting the list as a new record. If you want to insert a list into a list, you need to either: + - double-wrap it, ex. `my_list += list(list("some_new_data" = 25))` + - set the index directly, ex. `my_list[my_list.len] = list("some_new_data" = 25)` +- Using variables and macros as associative list keys have some notable behavior. + - If declaring an associative list using a macro as a key, in a case where the macro does not exist (due to misspelling, etc.), that macro name will be treated as a string value for the associative list. You can guard against this by wrapping the macro in parens, ex. `list( (MY_MACRO_NAME) = "some_value" )`, which will fail to compile instead in cases where the macro doesn't exist. + - If a variable is used as the associative key, it *must* be wrapped in parens, or it will be used as a string key. \ No newline at end of file diff --git a/code/___compile_options.dm b/code/___compile_options.dm new file mode 100644 index 00000000000..a4f54bc99b9 --- /dev/null +++ b/code/___compile_options.dm @@ -0,0 +1,12 @@ +// If REFTRACK_IN_CI is defined, the reftracker will run in CI. +#define REFTRACK_IN_CI +#if defined(REFTRACK_IN_CI) && defined(UNIT_TEST) && !defined(SPACEMAN_DMM) +#define REFTRACKING_ENABLED +#define GC_FAILURE_HARD_LOOKUP +#define FIND_REF_NO_CHECK_TICK +#endif + +// parity with previous behavior where TESTING enabled reftracking +#ifdef TESTING +#define REFTRACKING_ENABLED +#endif \ No newline at end of file diff --git a/code/__defines/ZAS.dm b/code/__defines/ZAS.dm index 59466d88b53..6734ee68ffb 100644 --- a/code/__defines/ZAS.dm +++ b/code/__defines/ZAS.dm @@ -57,29 +57,25 @@ } #ifdef MULTIZAS - -var/global/list/csrfz_check = list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST, NORTHUP, EASTUP, WESTUP, SOUTHUP, NORTHDOWN, EASTDOWN, WESTDOWN, SOUTHDOWN) -var/global/list/gzn_check = list(NORTH, SOUTH, EAST, WEST, UP, DOWN) +#define ZAS_CSRFZ_CHECK global.cornerdirsz +#define ZAS_GZN_CHECK global.cardinalz #define ATMOS_CANPASS_TURF(ret, A, B) \ if (A.blocks_air & AIR_BLOCKED || B.blocks_air & AIR_BLOCKED) { \ ret = BLOCKED; \ } \ - else if (B.z != A.z) { \ - if (B.z < A.z) { \ - ret = (A.z_flags & ZM_ALLOW_ATMOS) ? ZONE_BLOCKED : BLOCKED; \ - } \ - else { \ - ret = (B.z_flags & ZM_ALLOW_ATMOS) ? ZONE_BLOCKED : BLOCKED; \ - } \ + else if (B.z < A.z) { \ + ret = (A.z_flags & ZM_ALLOW_ATMOS) ? ZONE_BLOCKED : BLOCKED; \ } \ - else if (A.blocks_air & ZONE_BLOCKED || B.blocks_air & ZONE_BLOCKED) { \ - ret = (A.z == B.z) ? ZONE_BLOCKED : AIR_BLOCKED; \ + else if(B.z > A.z) { \ + ret = (B.z_flags & ZM_ALLOW_ATMOS) ? ZONE_BLOCKED : BLOCKED; \ } \ - else if (A.contents.len) { \ + else if ((A.blocks_air & ZONE_BLOCKED) || (B.blocks_air & ZONE_BLOCKED)) { \ + ret = ZONE_BLOCKED; \ + } \ + else if (length(A.contents)) { \ ret = 0;\ - for (var/thing in A) { \ - var/atom/movable/AM = thing; \ + for (var/atom/movable/AM as anything in A) { \ ATMOS_CANPASS_MOVABLE(ret, AM, B); \ if (ret == BLOCKED) { \ break;\ @@ -88,8 +84,8 @@ var/global/list/gzn_check = list(NORTH, SOUTH, EAST, WEST, UP, DOWN) } #else -var/global/list/csrfz_check = list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) -var/global/list/gzn_check = list(NORTH, SOUTH, EAST, WEST) +#define ZAS_CSRFZ_CHECK global.cornerdirs +#define ZAS_GZN_CHECK global.cardinal #define ATMOS_CANPASS_TURF(ret, A, B) \ if (A.blocks_air & AIR_BLOCKED || B.blocks_air & AIR_BLOCKED) { \ @@ -98,7 +94,7 @@ var/global/list/gzn_check = list(NORTH, SOUTH, EAST, WEST) else if (A.blocks_air & ZONE_BLOCKED || B.blocks_air & ZONE_BLOCKED) { \ ret = ZONE_BLOCKED; \ } \ - else if (A.contents.len) { \ + else if (length(A.contents)) { \ ret = 0;\ for (var/thing in A) { \ var/atom/movable/AM = thing; \ diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm deleted file mode 100644 index d2f61e13dca..00000000000 --- a/code/__defines/_compile_options.dm +++ /dev/null @@ -1,3 +0,0 @@ -// The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. -// 1 will enable set background. 0 will disable set background. -#define BACKGROUND_ENABLED 0 diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index 335eb97ec2a..7e207ad9f5e 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -178,7 +178,7 @@ What is the naming convention for planes or layers? #define ABOVE_LIGHTING_PLANE 4 // laser beams, etc. that shouldn't be affected by darkness #define ABOVE_LIGHTING_LAYER 1 #define BEAM_PROJECTILE_LAYER 2 - #define SUPERMATTER_WALL_LAYER 3 + #define SUBSPACE_WALL_LAYER 3 #define OBFUSCATION_LAYER 4 #define FULLSCREEN_PLANE 5 // for fullscreen overlays that do not cover the hud. diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm index 87bf26d7fbb..bcd23383451 100644 --- a/code/__defines/flags.dm +++ b/code/__defines/flags.dm @@ -84,7 +84,7 @@ The latter will result in a linter warning and will not work correctly. #define ITEM_FLAG_NOCUFFS BITFLAG(11) // Gloves that have this flag prevent cuffs being applied #define ITEM_FLAG_CAN_HIDE_IN_SHOES BITFLAG(12) // Items that can be hidden in shoes that permit it #define ITEM_FLAG_PADDED BITFLAG(13) // When set on gloves, will act like pulling punches in unarmed combat. -#define ITEM_FLAG_CAN_TAPE BITFLAG(14) // Whether the item can be be taped onto something using tape +#define ITEM_FLAG_CAN_TAPE BITFLAG(14) // Whether the item can be taped onto something using tape #define ITEM_FLAG_IS_WEAPON BITFLAG(15) // Item is considered a weapon. Currently only used for force-based worth calculation. // Flags for pass_flags (/atom/var/pass_flags) diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 94c9d41639a..b14c47d5768 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -3,7 +3,7 @@ #define CHOOSE_GAMEMODE_RETRY 2 // The gamemode could not be chosen; we will use the next most popular option voted in, or the default. #define CHOOSE_GAMEMODE_REVOTE 3 // The gamemode could not be chosen; we need to have a revote. #define CHOOSE_GAMEMODE_RESTART 4 // The gamemode could not be chosen; we will restart the server. -#define CHOOSE_GAMEMODE_SILENT_REDO 5 // The gamemode could not be chosen; we request to have the the proc rerun on the next tick. +#define CHOOSE_GAMEMODE_SILENT_REDO 5 // The gamemode could not be chosen; we request to have the proc rerun on the next tick. //End game state, to manage round end. #define END_GAME_NOT_OVER 1 @@ -33,31 +33,6 @@ #define DEFAULT_TELECRYSTAL_AMOUNT 130 #define IMPLANT_TELECRYSTAL_AMOUNT(x) (round(x * 0.49)) // If this cost is ever greater than half of DEFAULT_TELECRYSTAL_AMOUNT then it is possible to buy more TC than you spend -// SPELL FLAGS -#define Z2NOCAST BITFLAG(0) //if this is added, the spell can't be cast at centcomm -#define INCLUDEUSER BITFLAG(1) //does the spell include the caster in its target selection? -#define IGNOREDENSE BITFLAG(2) //are dense turfs ignored in selection? - -//End split flags -#define CONSTRUCT_CHECK BITFLAG(12) //used by construct spells - checks for nullrods -#define NO_BUTTON BITFLAG(13) //spell won't show up in the HUD with this - -//invocation -#define SpI_SHOUT "shout" -#define SpI_WHISPER "whisper" -#define SpI_EMOTE "emote" -#define SpI_NONE "none" - -//upgrading -#define Sp_SPEED "speed" -#define Sp_POWER "power" -#define Sp_TOTAL "total" - -//casting costs -#define Sp_RECHARGE "recharge" -#define Sp_CHARGES "charges" -#define Sp_HOLDVAR "holdervar" - //Voting-related #define VOTE_PROCESS_ABORT 1 #define VOTE_PROCESS_COMPLETE 2 diff --git a/code/__defines/item_effects.dm b/code/__defines/item_effects.dm new file mode 100644 index 00000000000..d5c79b402f9 --- /dev/null +++ b/code/__defines/item_effects.dm @@ -0,0 +1,15 @@ +// Identifiers for various categories of item effects. +#define IE_CAT_DAMAGE "weff_damage" +#define IE_CAT_STRIKE "weff_strike" +#define IE_CAT_PARRY "weff_parry" +#define IE_CAT_USED "weff_used" +#define IE_CAT_WIELDED "weff_wield" +#define IE_CAT_VISUAL "weff_visual" +#define IE_CAT_LISTENER "weff_listener" +#define IE_CAT_EXAMINE "weff_visible" +#define IE_CAT_RANGED "weff_ranged" +#define IE_CAT_PROCESS "weff_process" + +// Identifiers for parameters for item effects. +#define IE_PAR_USES "uses" +#define IE_PAR_MAX_USES "max_uses" diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index 25f2ed8c213..d6591b17acf 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -72,7 +72,6 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define STAGE_THREE 5 #define STAGE_FOUR 7 #define STAGE_FIVE 9 -#define STAGE_SUPER 11 // NanoUI flags #define STATUS_INTERACTIVE 2 // GREEN Visability @@ -103,21 +102,6 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define ATMOS_DEFAULT_VOLUME_MIXER 500 // L. #define ATMOS_DEFAULT_VOLUME_PIPE 70 // L. -// These are used by supermatter and supermatter monitor program, mostly for UI updating purposes. Higher should always be worse! -#define SUPERMATTER_ERROR -1 // Unknown status, shouldn't happen but just in case. -#define SUPERMATTER_INACTIVE 0 // No or minimal energy -#define SUPERMATTER_NORMAL 1 // Normal operation -#define SUPERMATTER_NOTIFY 2 // Ambient temp > 80% of CRITICAL_TEMPERATURE -#define SUPERMATTER_WARNING 3 // Ambient temp > CRITICAL_TEMPERATURE OR integrity damaged -#define SUPERMATTER_DANGER 4 // Integrity < 50% -#define SUPERMATTER_EMERGENCY 5 // Integrity < 25% -#define SUPERMATTER_DELAMINATING 6 // Pretty obvious. - -#define SUPERMATTER_DATA_EER "Relative EER" -#define SUPERMATTER_DATA_TEMPERATURE "Temperature" -#define SUPERMATTER_DATA_PRESSURE "Pressure" -#define SUPERMATTER_DATA_EPR "Chamber EPR" - // Scrubber modes #define SCRUBBER_SIPHON "siphon" #define SCRUBBER_SCRUB "scrub" diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 5bf9a5b371a..55fbbecd14d 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -370,3 +370,11 @@ #define RADIAL_LABELS_NONE 0 #define RADIAL_LABELS_OFFSET 1 #define RADIAL_LABELS_CENTERED 2 + +#define CRAYON_DRAW_RUNE "rune" +#define CRAYON_DRAW_GRAFFITI "graffiti" +#define CRAYON_DRAW_LETTER "letter" +#define CRAYON_DRAW_ARROW "arrow" + +// Default UI style applied to client prefs. +#define DEFAULT_UI_STYLE /decl/ui_style/midnight diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index 48a9f0e0973..1d2654f16d2 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -5,8 +5,8 @@ #define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it. #define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference. #define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste. -#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if TESTING is not enabled in _compiler_options.dm. - //if TESTING is enabled, qdel will call this object's find_references() verb. +#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if REFTRACKING_ENABLED is not enabled in _compiler_options.dm. + //if REFTRACKING_ENABLED is enabled, qdel will call this object's find_references() verb. #define QDEL_HINT_IFFAIL_FINDREFERENCE 6 //Above but only if gc fails. //defines for the gc_destroyed var @@ -15,21 +15,31 @@ #define GC_QUEUE_HARDDELETE 3 #define GC_QUEUE_COUNT 3 //increase this when adding more steps. -#define GC_QUEUED_FOR_HARD_DEL -1 -#define GC_CURRENTLY_BEING_QDELETED -2 +// Defines for the ssgarbage queue items +#define GC_QUEUE_ITEM_QUEUE_TIME 1 //! Time this item entered the queue +#define GC_QUEUE_ITEM_REF 2 //! Ref to the item +#define GC_QUEUE_ITEM_GCD_DESTROYED 3 //! Item's gc_destroyed var value. Used to detect ref reuse. +#define GC_QUEUE_ITEM_INDEX_COUNT 3 //! Number of item indexes, used for allocating the nested lists. Don't forget to increase this if you add a new queue item index + +// Defines for the time an item has to get its reference cleaned before it fails the queue and moves to the next. +#define GC_FILTER_QUEUE (1 SECONDS) +#define GC_CHECK_QUEUE (5 MINUTES) +#define GC_DEL_QUEUE (10 SECONDS) + +#define GC_CURRENTLY_BEING_QDELETED -1 #define QDELING(X) (X.gc_destroyed) #define QDELETED(X) (isnull(X) || QDELING(X)) #define QDESTROYING(X) (isnull(X) || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) //Qdel helper macros. -#define QDEL_IN(item, time) if(!isnull(item)) {addtimer(CALLBACK(item, TYPE_PROC_REF(/datum, qdel_self)), time, TIMER_STOPPABLE)} -#define QDEL_IN_CLIENT_TIME(item, time) if(!isnull(item)) {addtimer(CALLBACK(item, TYPE_PROC_REF(/datum, qdel_self)), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)} +#define QDEL_IN(item, time) if(!isnull(item)) {addtimer(CALLBACK(item, TYPE_PROC_REF(/datum, qdel_self)), time)} +#define QDEL_IN_CLIENT_TIME(item, time) if(!isnull(item)) {addtimer(CALLBACK(item, TYPE_PROC_REF(/datum, qdel_self)), time, TIMER_CLIENT_TIME)} #define QDEL_NULL(item) if(item) {qdel(item); item = null} #define QDEL_NULL_SCREEN(item) if(client) { client.screen -= item; }; QDEL_NULL(item) #define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) }}; if(x) {x.Cut(); x = null } // Second x check to handle items that LAZYREMOVE on qdel. #define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } -#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(______qdel_list_wrapper), L), time, TIMER_STOPPABLE) +#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(______qdel_list_wrapper), L), time) #define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); } #define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } diff --git a/code/__defines/research.dm b/code/__defines/research.dm index 2fb5ac649e4..3898d8dc295 100644 --- a/code/__defines/research.dm +++ b/code/__defines/research.dm @@ -19,7 +19,8 @@ #define HOLLOW_OBJECT_MATTER_MULTIPLIER 0.05 #define BASE_OBJECT_MATTER_MULTPLIER 0.25 -#define GENERIC_SMELTING_HEAT_POINT 1350 CELSIUS +#define LOW_SMELTING_HEAT_POINT 1150 CELSIUS // Reachable with coal in a kiln on the medieval maps. +#define GENERIC_SMELTING_HEAT_POINT 1350 CELSIUS // Reachable with coal and a bellows in a kiln on medieval maps. #define HIGH_SMELTING_HEAT_POINT 4000 CELSIUS // must be at least 4074K (3800 C) to melt graphite #define TECH_MATERIAL "materials" diff --git a/code/__defines/species.dm b/code/__defines/species.dm index 0ff09486031..791e81cd17d 100644 --- a/code/__defines/species.dm +++ b/code/__defines/species.dm @@ -8,6 +8,7 @@ #define SPECIES_FLAG_NO_BLOCK BITFLAG(6) // Unable to block or defend itself from attackers. #define SPECIES_FLAG_NEED_DIRECT_ABSORB BITFLAG(7) // This species can only have their DNA taken by direct absorption. #define SPECIES_FLAG_LOW_GRAV_ADAPTED BITFLAG(8) // This species is used to lower than standard gravity, affecting stamina in high-grav +#define SPECIES_FLAG_ABSORB_ELECTRICITY BITFLAG(9) // This species can absorb electricity; snowflake flag for old slime people. // Species spawn flags #define SPECIES_IS_WHITELISTED BITFLAG(0) // Must be whitelisted to play. diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index c9880741fa9..13861c4d9f5 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -1,7 +1,6 @@ #define INITIALIZATION_INSSATOMS 0 //New should not call Initialize -#define INITIALIZATION_INSSATOMS_LATE 1 //New should not call Initialize; after the first pass is complete (handled differently) -#define INITIALIZATION_INNEW_MAPLOAD 2 //New should call Initialize(TRUE) -#define INITIALIZATION_INNEW_REGULAR 3 //New should call Initialize(FALSE) +#define INITIALIZATION_INNEW_MAPLOAD 1 //New should call Initialize(TRUE) +#define INITIALIZATION_INNEW_REGULAR 2 //New should call Initialize(FALSE) #define INITIALIZE_HINT_NORMAL 0 //Nothing happens #define INITIALIZE_HINT_LATELOAD 1 //Call LateInitialize diff --git a/code/_helpers/files.dm b/code/_helpers/files.dm index b665a162e7b..c6d4b7ea49f 100644 --- a/code/_helpers/files.dm +++ b/code/_helpers/files.dm @@ -1,7 +1,7 @@ //Sends resource files to client cache /client/proc/getFiles() for(var/file in args) - direct_output(src, browse_rsc(file)) + send_rsc(src, file, null) /client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) var/path = root diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index 5c78a7dd62f..ee74c5f11ef 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -163,7 +163,7 @@ mob // Send the icon to src's local cache send_rsc(src, getFlatIcon(src), iconName) // Display the icon in their browser - direct_output(src, browse("

")) + show_browser(src, "

") Output_Icon() set name = "2. Output Icon" diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index b8cf7af8f12..5c1744b0178 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -26,7 +26,7 @@ var/global/log_end= world.system_type == UNIX ? ascii2text(13) : "" to_world_log("## TESTING: [msg][log_end]") /proc/game_log(category, text) - direct_output(diary, "\[[time_stamp()]] [game_id] [category]: [text][log_end]") + to_file(diary, "\[[time_stamp()]] [game_id] [category]: [text][log_end]") /proc/log_admin(text) global.admin_log.Add(text) diff --git a/code/_helpers/profiling.dm b/code/_helpers/profiling.dm index ad16a2c2d27..0c478556836 100644 --- a/code/_helpers/profiling.dm +++ b/code/_helpers/profiling.dm @@ -71,7 +71,7 @@ lines += "[entry] => [num2text(data[STAT_ENTRY_TIME], 10)]ms ([data[STAT_ENTRY_COUNT]]) (avg:[num2text(data[STAT_ENTRY_TIME]/(data[STAT_ENTRY_COUNT] || 1), 99)])" if (user) - direct_output(user, browse("
  1. [lines.Join("
  2. ")]
", "window=[url_encode("stats:[ref(stats)]")]")) + show_browser(user, "
  1. [lines.Join("
  2. ")]
", "window=[url_encode("stats:[ref(stats)]")]") . = lines.Join("\n") diff --git a/code/_helpers/sorts/__main.dm b/code/_helpers/sorts/__main.dm index 09bb861f647..391630171eb 100644 --- a/code/_helpers/sorts/__main.dm +++ b/code/_helpers/sorts/__main.dm @@ -169,7 +169,7 @@ reverse a descending sequence without violating stability. var/r = 0 //becomes 1 if any bits are shifted off while(n >= MIN_MERGE) r |= (n & 1) - n >>= 1 + n = BITSHIFT_RIGHT(n, 1) return n + r //Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished: diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index f53718da51b..90210911f1e 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -72,6 +72,35 @@ var/global/next_duration_update = 0 var/global/last_round_duration = 0 var/global/round_start_time = 0 +/proc/ticks2shortreadable(tick_time, separator = ":") + var/hours = round(tick_time / (1 HOUR)) + var/minutes = round((tick_time % (1 HOUR)) / (1 MINUTE)) + var/seconds = round((tick_time % (1 MINUTE)) / (1 SECOND)) + var/out = list() + + if(hours > 0) + out += "[hours]" + + if(minutes > 0) + if(minutes < 10 && hours > 0) + out += "0[minutes]" + else + out += "[minutes]" + else if(hours > 0) + out += "00" + + if(seconds > 0) + if(seconds < 10 && (minutes > 0 || hours > 0)) + out += "0[seconds]" + else + out += "[seconds]" + else if(minutes > 0 || hours > 0) + out += "00" + + if(length(out)) + return jointext(out, separator) + return null + /proc/ticks2readable(tick_time) var/hours = round(tick_time / (1 HOUR)) var/minutes = round((tick_time % (1 HOUR)) / (1 MINUTE)) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 32eac707ce7..88f8f402528 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -692,23 +692,17 @@ Turf and target are seperate in case you want to teleport some distance from a t return zone_to_descriptor_mapping[zone] || zone //Whether or not the given item counts as sharp in terms of dealing damage -/proc/is_sharp(obj/O) - if (!O) return 0 - if (O.sharp) return 1 - if (O.edge) return 1 - return 0 +/obj/proc/is_sharp() + return FALSE //Whether or not the given item counts as cutting with an edge in terms of removing limbs -/proc/has_edge(obj/O) - if (!O) return 0 - if (O.edge) return 1 - return 0 - +/obj/proc/has_edge() + return FALSE //For items that can puncture e.g. thick plastic but aren't necessarily sharp //Returns 1 if the given item is capable of popping things like balloons, inflatable barriers, or cutting police tape. /obj/item/proc/can_puncture() - return sharp + return is_sharp() /obj/item/screwdriver/can_puncture() return 1 diff --git a/code/_macros.dm b/code/_macros.dm index 5cba5893825..11e6c146b5d 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -106,9 +106,12 @@ #define show_image(target, image) target << (image) #define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name) #define open_link(target, url) target << link(url) +#define ftp_to(target, file_entry, suggested_name) target << ftp(file_entry, suggested_name) +#define open_file_for(target, file) target << run(file) #define to_savefile(target, key, value) target[(key)] << (value) #define from_savefile(target, key, value) target[(key)] >> (value) #define to_output(target, output_content, output_args) target << output((output_content), (output_args)) +// Avoid using this where possible, prefer the other helpers instead. #define direct_output(target, value) target << (value) /proc/html_icon(var/thing) // Proc instead of macro to avoid precompiler problems. @@ -147,34 +150,35 @@ #define SPAN_STYLE(S, X) "[X]" #define SPAN_CLASS(C, X) "[X]" -#define SPAN_ITALIC(X) SPAN_CLASS("italic", X) -#define SPAN_BOLD(X) SPAN_CLASS("bold", X) -#define SPAN_NOTICE(X) SPAN_CLASS("notice", X) -#define SPAN_WARNING(X) SPAN_CLASS("warning", X) -#define SPAN_DANGER(X) SPAN_CLASS("danger", X) -#define SPAN_ROSE(X) SPAN_CLASS("rose", X) -#define SPAN_OCCULT(X) SPAN_CLASS("cult", X) -#define SPAN_MFAUNA(X) SPAN_CLASS("mfauna", X) -#define SPAN_SUBTLE(X) SPAN_CLASS("subtle", X) -#define SPAN_INFO(X) SPAN_CLASS("info", X) -#define SPAN_RED(X) SPAN_CLASS("font_red", X) -#define SPAN_ORANGE(X) SPAN_CLASS("font_orange", X) -#define SPAN_YELLOW(X) SPAN_CLASS("font_yellow", X) -#define SPAN_GREEN(X) SPAN_CLASS("font_green", X) -#define SPAN_BLUE(X) SPAN_CLASS("font_blue", X) -#define SPAN_VIOLET(X) SPAN_CLASS("font_violet", X) -#define SPAN_PURPLE(X) SPAN_CLASS("font_purple", X) -#define SPAN_GREY(X) SPAN_CLASS("font_grey", X) -#define SPAN_MAROON(X) SPAN_CLASS("font_maroon", X) -#define SPAN_PINK(X) SPAN_CLASS("font_pink", X) -#define SPAN_PALEPINK(X) SPAN_CLASS("font_palepink", X) -#define SPAN_SINISTER(X) SPAN_CLASS("sinister", X) -#define SPAN_MODERATE(X) SPAN_CLASS("moderate", X) +#define SPAN_ITALIC(X) SPAN_CLASS("italic", X) +#define SPAN_BOLD(X) SPAN_CLASS("bold", X) +#define SPAN_NOTICE(X) SPAN_CLASS("notice", X) +#define SPAN_WARNING(X) SPAN_CLASS("warning", X) +#define SPAN_DANGER(X) SPAN_CLASS("danger", X) +#define SPAN_ROSE(X) SPAN_CLASS("rose", X) +#define SPAN_OCCULT(X) SPAN_CLASS("cult", X) +#define SPAN_CULT_ANNOUNCE(X) SPAN_CLASS("cultannounce", X) +#define SPAN_MFAUNA(X) SPAN_CLASS("mfauna", X) +#define SPAN_SUBTLE(X) SPAN_CLASS("subtle", X) +#define SPAN_INFO(X) SPAN_CLASS("info", X) +#define SPAN_RED(X) SPAN_CLASS("font_red", X) +#define SPAN_ORANGE(X) SPAN_CLASS("font_orange", X) +#define SPAN_YELLOW(X) SPAN_CLASS("font_yellow", X) +#define SPAN_GREEN(X) SPAN_CLASS("font_green", X) +#define SPAN_BLUE(X) SPAN_CLASS("font_blue", X) +#define SPAN_VIOLET(X) SPAN_CLASS("font_violet", X) +#define SPAN_PURPLE(X) SPAN_CLASS("font_purple", X) +#define SPAN_GREY(X) SPAN_CLASS("font_grey", X) +#define SPAN_MAROON(X) SPAN_CLASS("font_maroon", X) +#define SPAN_PINK(X) SPAN_CLASS("font_pink", X) +#define SPAN_PALEPINK(X) SPAN_CLASS("font_palepink", X) +#define SPAN_SINISTER(X) SPAN_CLASS("sinister", X) +#define SPAN_MODERATE(X) SPAN_CLASS("moderate", X) // placeholders -#define SPAN_GOOD(X) SPAN_GREEN(X) -#define SPAN_NEUTRAL(X) SPAN_BLUE(X) -#define SPAN_BAD(X) SPAN_RED(X) -#define SPAN_HARDSUIT(X) SPAN_BLUE(X) +#define SPAN_GOOD(X) SPAN_GREEN(X) +#define SPAN_NEUTRAL(X) SPAN_BLUE(X) +#define SPAN_BAD(X) SPAN_RED(X) +#define SPAN_HARDSUIT(X) SPAN_BLUE(X) #define CSS_CLASS_RADIO "radio" @@ -194,4 +198,8 @@ #define FONT_GIANT(X) "[X]" -#define PRINT_STACK_TRACE(X) get_stack_trace(X, __FILE__, __LINE__) \ No newline at end of file +#define PRINT_STACK_TRACE(X) get_stack_trace(X, __FILE__, __LINE__) + +/// Checks if potential_weakref is a weakref of thing. +/// NOTE: These argments are the opposite order of TG's, because I think TG's are counterintuitive. +#define IS_WEAKREF_OF(potential_weakref, thing) (istype(thing, /datum) && !isnull(potential_weakref) && thing.weakref == potential_weakref) \ No newline at end of file diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 8fa2950b1b9..90653cccc97 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -282,10 +282,17 @@ return A.CtrlClick(src) /atom/proc/CtrlClick(var/mob/user) + if(get_recursive_loc_of_type(/mob) == user) + var/decl/interaction_handler/handler = get_quick_interaction_handler(user) + if(handler) + var/using_item = user.get_active_held_item() + if(handler.is_possible(src, user, using_item)) + return handler.invoked(src, user, using_item) return FALSE /atom/movable/CtrlClick(var/mob/living/user) - return try_make_grab(user, defer_hand = TRUE) || ..() + if(!(. = ..()) && loc != user) + return try_make_grab(user, defer_hand = TRUE) || ..() /* Alt click @@ -295,7 +302,7 @@ A.AltClick(src) /atom/proc/AltClick(var/mob/user) - if(try_handle_interactions(user, get_alt_interactions(user), user?.get_active_held_item())) + if(try_handle_interactions(user, get_alt_interactions(user), user?.get_active_held_item(), check_alt_interactions = TRUE)) return TRUE if(user?.get_preference_value(/datum/client_preference/show_turf_contents) == PREF_ALT_CLICK) . = show_atom_list_for_turf(user, get_turf(src)) diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index f13491832f4..5ac7ac42bd0 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -16,21 +16,21 @@ #define ui_entire_screen "WEST,SOUTH to EAST,NORTH" #define ui_center_fullscreen "CENTER-7,CENTER-7" -//Lower left, persistant menu +//Lower left, persistent menu #define ui_inventory "LEFT:6,BOTTOM:5" -//Lower center, persistant menu +//Lower center, persistent menu #define ui_sstore1 "LEFT+2:10,BOTTOM:5" -#define ui_id "LEFT+3:12,BOTTOM:5" -#define ui_belt "LEFT+4:14,BOTTOM:5" -#define ui_back "CENTER-2:14,BOTTOM:5" +#define ui_back "LEFT+3:22,BOTTOM:5" +#define ui_id "LEFT+4:26,BOTTOM:5" +#define ui_belt "RIGHT-5:16,BOTTOM:5" #define ui_rhand "CENTER-1:16,BOTTOM:5" #define ui_lhand "CENTER:16,BOTTOM:5" #define ui_equip "CENTER-1:16,BOTTOM+1:5" #define ui_swaphand1 "CENTER-1:16,BOTTOM+1:5" #define ui_swaphand2 "CENTER:16,BOTTOM+1:5" -#define ui_storage1 "CENTER+1:16,BOTTOM:5" -#define ui_storage2 "CENTER+2:16,BOTTOM:5" +#define ui_storage1 "RIGHT-3:16,BOTTOM:5" +#define ui_storage2 "RIGHT-4:16,BOTTOM:5" #define ui_alien_head "CENTER-3:12,BOTTOM:5" //aliens #define ui_alien_oclothing "CENTER-2:14,BOTTOM:5"//aliens @@ -48,13 +48,13 @@ #define ui_construct_fire "RIGHT-1:16,CENTER+1:13" //above health, slightly to the left #define ui_construct_pull "RIGHT-1:28,BOTTOM+1:10" //above the zone selector icon -//Lower right, persistant menu +//Lower right, persistent menu #define ui_dropbutton "RIGHT-4:22,BOTTOM:5" #define ui_drop_throw "RIGHT-1:28,BOTTOM+1:7" #define ui_pull_resist "RIGHT-2:26,BOTTOM+1:7" -#define ui_acti "RIGHT-2:26,BOTTOM:5" -#define ui_movi "RIGHT-3:24,BOTTOM:5" -#define ui_attack_selector "RIGHT-3:24,BOTTOM+1:6" +#define ui_acti "CENTER,BOTTOM:5" +#define ui_movi "RIGHT-2:24,BOTTOM:5" +#define ui_attack_selector "RIGHT-2:27,BOTTOM+2:9" #define ui_zonesel "RIGHT-1:28,BOTTOM:5" #define ui_acti_alt "RIGHT-1:28,BOTTOM:5" //alternative intent switcher for when the interface is hidden #define ui_stamina "RIGHT-3:24,BOTTOM+1:5" diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index 81f1abf8965..651c4d1e007 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -1,6 +1,9 @@ /mob/living/silicon/ai hud_used = /datum/hud/ai +/datum/hud/ai + action_intent_type = null // no selector + /datum/hud/ai/FinalizeInstantiation() var/list/ai_hud_data = decls_repository.get_decls_of_subtype(/decl/ai_hud) for(var/elem_type in ai_hud_data) diff --git a/code/_onclick/hud/ai_hud.dm b/code/_onclick/hud/ai_hud.dm index b8edcf540c4..fa4e2200bb6 100644 --- a/code/_onclick/hud/ai_hud.dm +++ b/code/_onclick/hud/ai_hud.dm @@ -11,70 +11,70 @@ screen_loc = ui_ai_core name = "AI Core" icon_state = "ai_core" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, core) + proc_path = /mob/living/silicon/ai/proc/core /decl/ai_hud/ai_announcement screen_loc = ui_ai_announcement name = "AI Announcement" icon_state = "announcement" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_announcement) + proc_path = /mob/living/silicon/ai/proc/ai_announcement /decl/ai_hud/ai_cam_track screen_loc = ui_ai_cam_track name = "Track With Camera" icon_state = "track" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_camera_track) + proc_path = /mob/living/silicon/ai/proc/ai_camera_track input_procs = list(/mob/living/silicon/ai/proc/trackable_mobs = (AI_BUTTON_PROC_BELONGS_TO_CALLER|AI_BUTTON_INPUT_REQUIRES_SELECTION)) /decl/ai_hud/ai_cam_light screen_loc = ui_ai_cam_light name = "Toggle Camera Lights" icon_state = "camera_light" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, toggle_camera_light) + proc_path = /mob/living/silicon/ai/proc/toggle_camera_light /decl/ai_hud/ai_cam_change_channel screen_loc = ui_ai_cam_change_channel name = "Jump to Camera Channel" icon_state = "camera" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_channel_change) + proc_path = /mob/living/silicon/ai/proc/ai_channel_change input_procs = list(/mob/living/silicon/ai/proc/get_camera_channel_list = (AI_BUTTON_PROC_BELONGS_TO_CALLER|AI_BUTTON_INPUT_REQUIRES_SELECTION)) /decl/ai_hud/ai_sensor screen_loc = ui_ai_sensor name = "Set Sensor Mode" icon_state = "ai_sensor" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, sensor_mode) + proc_path = /mob/living/silicon/ai/proc/sensor_mode /decl/ai_hud/ai_manifest screen_loc = ui_ai_crew_manifest name = "Show Crew Manifest" icon_state = "manifest" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, run_program) + proc_path = /mob/living/silicon/ai/proc/run_program input_args = list("crewmanifest") /decl/ai_hud/ai_take_image screen_loc = ui_ai_take_image name = "Toggle Camera Mode" icon_state = "take_picture" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_take_image) + proc_path = /mob/living/silicon/ai/proc/ai_take_image /decl/ai_hud/ai_view_images screen_loc = ui_ai_view_images name = "View Images" icon_state = "view_images" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_view_images) + proc_path = /mob/living/silicon/ai/proc/ai_view_images /decl/ai_hud/ai_laws screen_loc = ui_ai_state_laws name = "State Laws" icon_state = "state_laws" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_checklaws) + proc_path = /mob/living/silicon/ai/proc/ai_checklaws /decl/ai_hud/ai_call_shuttle screen_loc = ui_ai_call_shuttle name = "Call Shuttle" icon_state = "call_shuttle" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_call_shuttle) + proc_path = /mob/living/silicon/ai/proc/ai_call_shuttle /decl/ai_hud/ai_up screen_loc = ui_ai_up @@ -92,53 +92,53 @@ screen_loc = ui_ai_color name = "Change Floor Color" icon_state = "ai_floor" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, change_floor) + proc_path = /mob/living/silicon/ai/proc/change_floor /decl/ai_hud/ai_hologram screen_loc = ui_ai_holo_change name = "Change Hologram" icon_state = "ai_holo_change" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_hologram_change) + proc_path = /mob/living/silicon/ai/proc/ai_hologram_change /decl/ai_hud/ai_crew_monitor screen_loc = ui_ai_crew_mon name = "Crew Monitor" icon_state = "crew_monitor" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, run_program) + proc_path = /mob/living/silicon/ai/proc/run_program input_args = list("sensormonitor") /decl/ai_hud/ai_power_override screen_loc = ui_ai_power_override name = "Toggle Power Override" icon_state = "ai_p_override" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_power_override) + proc_path = /mob/living/silicon/ai/proc/ai_power_override /decl/ai_hud/ai_shutdown screen_loc = ui_ai_shutdown name = "Shutdown" icon_state = "ai_shutdown" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_shutdown) + proc_path = /mob/living/silicon/ai/proc/ai_shutdown /decl/ai_hud/ai_move_hologram screen_loc = ui_ai_holo_mov name = "Toggle Hologram Movement" icon_state = "ai_holo_mov" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, toggle_hologram_movement) + proc_path = /mob/living/silicon/ai/proc/toggle_hologram_movement /decl/ai_hud/ai_core_icon screen_loc = ui_ai_core_icon name = "Pick Icon" icon_state = "ai_core_pick" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, pick_icon) + proc_path = /mob/living/silicon/ai/proc/pick_icon /decl/ai_hud/ai_status screen_loc = ui_ai_status name = "Pick Status" icon_state = "ai_status" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_statuschange) + proc_path = /mob/living/silicon/ai/proc/ai_statuschange /decl/ai_hud/ai_inbuilt_comp screen_loc = ui_ai_crew_rec name = "Inbuilt Computer" icon_state = "ai_crew_rec" - proc_path = TYPE_PROC_REF(/mob/living/silicon, access_computer) + proc_path = /mob/living/silicon/proc/access_computer diff --git a/code/_onclick/hud/animal.dm b/code/_onclick/hud/animal.dm index 70b61dffe77..8b372aabdcc 100644 --- a/code/_onclick/hud/animal.dm +++ b/code/_onclick/hud/animal.dm @@ -11,6 +11,4 @@ move_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_MOVEMENT) move_intent.icon_state = mymob.move_intent.hud_icon_state adding += move_intent - action_intent = new(null, mymob, ui_style, ui_color, ui_alpha) - adding += action_intent ..() diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 10b3ccf93c1..48829da57dd 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -13,6 +13,8 @@ hud_used = initial(hud_used) if(ispath(hud_used)) hud_used = new hud_used(src) + if(istype(hud_used)) + hud_used.refresh_hud_icons() refresh_lighting_master() /datum/hud @@ -22,16 +24,18 @@ var/inventory_shown = TRUE //the inventory var/hotkey_ui_hidden = FALSE //This is to hide the buttons that can be used via hotkeys. (hotkeybuttons list of buttons) - var/default_ui_style = /decl/ui_style/midnight + var/default_ui_style = DEFAULT_UI_STYLE var/list/alerts var/list/hand_hud_objects var/list/swaphand_hud_objects - var/obj/screen/intent/action_intent var/obj/screen/movement/move_intent var/obj/screen/stamina/stamina_bar + var/action_intent_type = /obj/screen/intent + var/obj/screen/intent/action_intent + var/list/adding = list() var/list/other = list() var/list/hud_elements = list() @@ -45,6 +49,10 @@ instantiate() ..() +/datum/hud/proc/refresh_hud_icons() + for(var/obj/screen/elem in mymob?.client?.screen) + elem.queue_icon_update() + /datum/hud/Destroy() . = ..() stamina_bar = null @@ -68,19 +76,19 @@ /datum/hud/proc/hide_inventory() inventory_shown = FALSE hidden_inventory_update() - persistant_inventory_update() + persistent_inventory_update() /datum/hud/proc/show_inventory() inventory_shown = TRUE hidden_inventory_update() - persistant_inventory_update() + persistent_inventory_update() /datum/hud/proc/hidden_inventory_update() var/decl/species/species = mymob?.get_species() if(istype(species?.species_hud)) refresh_inventory_slots(species.species_hud.hidden_slots, (inventory_shown && hud_shown)) -/datum/hud/proc/persistant_inventory_update() +/datum/hud/proc/persistent_inventory_update() var/decl/species/species = mymob?.get_species() if(istype(species?.species_hud)) refresh_inventory_slots(species.species_hud.persistent_slots, hud_shown) @@ -112,9 +120,17 @@ return FALSE /datum/hud/proc/FinalizeInstantiation() + SHOULD_CALL_PARENT(TRUE) + + if(!action_intent && action_intent_type) // Everyone needs an intent selector. + action_intent = new action_intent_type(null, mymob) + adding |= action_intent + hud_elements |= action_intent + BuildInventoryUI() BuildHandsUI() + if(mymob.client) mymob.client.screen = list() if(length(hand_hud_objects)) @@ -127,6 +143,7 @@ mymob.client.screen |= adding if(length(hotkeybuttons)) mymob.client.screen |= hotkeybuttons + hide_inventory() /datum/hud/proc/get_ui_style_data() @@ -138,6 +155,9 @@ . = available_styles[1] /datum/hud/proc/get_ui_color() + var/decl/ui_style/ui_style = get_ui_style_data() + if(!ui_style?.use_ui_color) + return COLOR_WHITE return mymob?.client?.prefs?.UI_style_color || COLOR_WHITE /datum/hud/proc/get_ui_alpha() @@ -172,25 +192,17 @@ break if(!inv_box) - inv_box = new /obj/screen/inventory(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HANDS) + inv_box = new /obj/screen/inventory/hand(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HANDS) else inv_box.set_ui_style(ui_style, UI_ICON_HANDS) inv_box.color = ui_color inv_box.alpha = ui_alpha - inv_box.SetName(hand_tag) - inv_box.icon_state = "hand_base" - - inv_box.cut_overlays() - inv_box.add_overlay("hand_[inv_slot.hand_overlay || hand_tag]", TRUE) - if(inv_slot.ui_label) - inv_box.add_overlay("hand_[inv_slot.ui_label]", TRUE) - inv_box.update_icon() + LAZYDISTINCTADD(hand_hud_objects, inv_box) + inv_box.SetName(hand_tag) inv_box.slot_id = hand_tag - inv_box.appearance_flags |= KEEP_TOGETHER - - LAZYDISTINCTADD(hand_hud_objects, inv_box) + inv_box.update_icon() // Clear held item boxes with no held slot. for(var/obj/screen/inventory/inv_box in hand_hud_objects) @@ -201,7 +213,7 @@ qdel(inv_box) // Rebuild offsets for the hand elements. - var/hand_y_offset = 5 + var/hand_y_offset = 21 var/list/elements = hand_hud_objects?.Copy() while(length(elements)) var/copy_index = min(length(elements), 2)+1 @@ -363,7 +375,7 @@ client.screen += zone_sel //This one is a special snowflake hud_used.hidden_inventory_update() - hud_used.persistant_inventory_update() + hud_used.persistent_inventory_update() update_action_buttons() //Similar to minimize_hud() but keeps zone_sel, gun_setting_icon, and healths. @@ -400,7 +412,7 @@ hud_used.action_intent.screen_loc = ui_acti //Restore intent selection to the original position hud_used.hidden_inventory_update() - hud_used.persistant_inventory_update() + hud_used.persistent_inventory_update() update_action_buttons() /client/proc/reset_click_catchers() diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 44722373ecb..7dc1d837d91 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -15,12 +15,6 @@ stamina_bar = new(null, mymob) adding += stamina_bar - // Draw the attack intent dialogue. - if(hud_data.show_intent_selector) - action_intent = new(null, mymob) - src.adding += action_intent - hud_elements |= action_intent - if(hud_data.has_m_intent) move_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_MOVEMENT) move_intent.icon_state = mymob.move_intent.hud_icon_state diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 2243f9e4923..b0680d9f047 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -50,11 +50,6 @@ var/global/obj/screen/robot_inventory R.ui_drop_grab = new(null, mymob) adding += R.ui_drop_grab - //Intent - action_intent = new /obj/screen/intent/robot(null, mymob) - action_intent.icon_state = R.get_intent().icon_state - - adding += action_intent adding += new /obj/screen/robot_panel(null, mymob) adding += new /obj/screen/robot_store(null, mymob) diff --git a/code/_onclick/hud/screen/_screen.dm b/code/_onclick/hud/screen/_screen.dm index fc921c67cee..4e891170d7b 100644 --- a/code/_onclick/hud/screen/_screen.dm +++ b/code/_onclick/hud/screen/_screen.dm @@ -24,6 +24,9 @@ var/ui_style_category /// Set to false for screen objects that do not rely on UI style to set their icon. var/requires_ui_style = TRUE + /// Whether or not we look for/draw an additional detail overlay. + var/apply_screen_overlay = TRUE + /// Reference to our last set ui_style /obj/screen/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) @@ -52,8 +55,13 @@ color = ui_color if(!isnull(ui_alpha)) alpha = ui_alpha + return ..() +/obj/screen/proc/get_owner_ui_style() + var/mob/owner = owner_ref?.resolve() + return (istype(owner) && istype(owner.hud_used)) ? owner.hud_used.get_ui_style_data() : null + /obj/screen/get_color() return color @@ -68,18 +76,21 @@ ui_style_category = ui_cat if(istype(ui_style) && ui_style_category) icon = ui_style.get_icon(ui_style_category) + update_icon() /obj/screen/Destroy() - if(owner_ref) - var/mob/owner = owner_ref.resolve() - if(istype(owner) && owner?.client?.screen) - owner.client.screen -= src + var/mob/owner = owner_ref?.resolve() + if(istype(owner) && owner.client?.screen) + owner.client.screen -= src return ..() /obj/screen/proc/handle_click(mob/user, params) return TRUE /obj/screen/Click(location, control, params) + var/list/paramlist = params2list(params) + if(paramlist["shift"]) + return examine(usr, 0) if(ismob(usr) && usr.client && usr.canClick() && (!user_incapacitation_flags || !usr.incapacitated(user_incapacitation_flags))) return handle_click(usr, params) return FALSE @@ -89,3 +100,25 @@ /obj/screen/check_mousedrop_interactivity(var/mob/user) return user.client && (src in user.client.screen) + +/obj/screen/on_update_icon() + rebuild_screen_overlays() + compile_overlays() + +/obj/screen/proc/get_screen_overlay_state() + return icon_state + +/obj/screen/proc/rebuild_screen_overlays() + SHOULD_CALL_PARENT(TRUE) + cut_overlays() + if(!apply_screen_overlay) + return + var/check_for_state = "[get_screen_overlay_state()]-overlay" + if(!check_state_in_icon(check_for_state, icon)) + return + var/decl/ui_style/ui_style = get_owner_ui_style() + if(ui_style?.use_overlay_color) + var/mob/living/owner = owner_ref?.resolve() + add_overlay(overlay_image(icon, check_for_state, istype(owner) ? (owner?.client?.prefs.UI_style_highlight_color || COLOR_WHITE) : COLOR_WHITE, RESET_COLOR)) + else + add_overlay(check_for_state) diff --git a/code/_onclick/hud/screen/screen_abilities.dm b/code/_onclick/hud/screen/screen_abilities.dm deleted file mode 100644 index 414458af058..00000000000 --- a/code/_onclick/hud/screen/screen_abilities.dm +++ /dev/null @@ -1,288 +0,0 @@ -/obj/screen/ability_master - name = "Abilities" - icon = 'icons/mob/screen/spells.dmi' - icon_state = "grey_spell_ready" - screen_loc = ui_ability_master - requires_ui_style = FALSE - var/list/obj/screen/ability/ability_objects = list() - var/list/obj/screen/ability/spell_objects = list() - var/showing = FALSE // If we're 'open' or not. - var/const/abilities_per_row = 7 - var/open_state = "master_open" // What the button looks like when it's 'open', showing the other buttons. - var/closed_state = "master_closed" // Button when it's 'closed', hiding everything else. - -/obj/screen/ability_master/Initialize(mapload, mob/_owner, ui_style, ui_color, ui_alpha, ui_cat) - . = ..() - if(. != INITIALIZE_HINT_QDEL) - update_abilities(0, _owner) - -/obj/screen/ability_master/Destroy() - // Get rid of the ability objects. - remove_all_abilities() - ability_objects.Cut() - // After that, remove ourselves from the mob seeing us, so we can qdel cleanly. - var/mob/owner = owner_ref?.resolve() - if(istype(owner) && owner.ability_master == src) - owner.ability_master = null - return ..() - -/obj/screen/ability_master/handle_mouse_drop(atom/over, mob/user, params) - if(showing) - return FALSE - . = ..() - -/obj/screen/ability_master/handle_click(mob/user, params) - if(length(ability_objects)) // If we're empty for some reason. - toggle_open() - -/obj/screen/ability_master/proc/toggle_open(var/forced_state = 0) - var/mob/owner = owner_ref?.resolve() - if(!istype(owner) || QDELETED(owner)) - return - if(showing && (forced_state != 2)) // We are closing the ability master, hide the abilities. - if(owner?.client) - for(var/obj/screen/ability/O in ability_objects) - owner.client.screen -= O -// O.handle_icon_updates = 0 - showing = 0 - overlays.len = 0 - overlays.Add(closed_state) - else if(forced_state != 1) // We're opening it, show the icons. - open_ability_master() - update_abilities(1) - showing = 1 - overlays.len = 0 - overlays.Add(open_state) - update_icon() - -/obj/screen/ability_master/proc/open_ability_master() - - var/client/owner_client - var/mob/owner = owner_ref?.resolve() - if(istype(owner) && !QDELETED(owner)) - owner_client = owner.client - - for(var/i = 1 to length(ability_objects)) - var/obj/screen/ability/A = ability_objects[i] - var/row = round(i/abilities_per_row) - A.screen_loc = "RIGHT-[(i-(row*abilities_per_row))+2]:16,TOP-[row+1]:16" - if(owner_client) - owner_client.screen += A - -/obj/screen/ability_master/proc/update_abilities(forced = 0, mob/user) - update_icon() - if(user && user.client) - if(!(src in user.client.screen)) - user.client.screen += src - var/i = 1 - for(var/obj/screen/ability/ability in ability_objects) - ability.update_icon(forced) - ability.maptext = "[i]" // Slot number - i++ - -/obj/screen/ability_master/on_update_icon() - if(ability_objects.len) - set_invisibility(INVISIBILITY_NONE) - else - set_invisibility(INVISIBILITY_ABSTRACT) - -/obj/screen/ability_master/proc/add_ability(var/name_given) - if(!name_given) - return - var/obj/screen/ability/new_button = new /obj/screen/ability - new_button.ability_master = src - new_button.SetName(name_given) - new_button.ability_icon_state = name_given - new_button.update_icon(1) - ability_objects.Add(new_button) - var/mob/living/owner = owner_ref?.resolve() - if(istype(owner) && !QDELETED(owner) && owner.client) - toggle_open(2) //forces the icons to refresh on screen - -/obj/screen/ability_master/proc/remove_ability(var/obj/screen/ability/ability) - if(!ability) - return - ability_objects.Remove(ability) - if(istype(ability,/obj/screen/ability/spell)) - spell_objects.Remove(ability) - qdel(ability) - - - if(ability_objects.len) - toggle_open(showing + 1) - update_icon() -// else -// qdel(src) - -/obj/screen/ability_master/proc/remove_all_abilities() - for(var/obj/screen/ability/A in ability_objects) - remove_ability(A) - -/obj/screen/ability_master/proc/get_ability_by_name(name_to_search) - for(var/obj/screen/ability/A in ability_objects) - if(A.name == name_to_search) - return A - return null - -/obj/screen/ability_master/proc/get_ability_by_instance(var/obj/instance/) - for(var/obj/screen/ability/obj_based/O in ability_objects) - if(O.object == instance) - return O - return null - -/obj/screen/ability_master/proc/get_ability_by_spell(var/spell/s) - for(var/screen in spell_objects) - var/obj/screen/ability/spell/S = screen - if(S.spell == s) - return S - return null - -/obj/screen/ability_master/proc/synch_spells_to_mind(var/datum/mind/M) - if(!M) - return - LAZYINITLIST(M.learned_spells) - for(var/obj/screen/ability/spell/screen in spell_objects) - var/spell/S = screen.spell - M.learned_spells |= S - -///////////ACTUAL ABILITIES//////////// -//This is what you click to do things// -/////////////////////////////////////// -/obj/screen/ability - icon = 'icons/mob/screen/spells.dmi' - icon_state = "grey_spell_base" - maptext_x = 3 - requires_owner = FALSE - requires_ui_style = FALSE - var/background_base_state = "grey" - var/ability_icon_state = null - var/obj/screen/ability_master/ability_master - -/obj/screen/ability/Destroy() - if(ability_master) - ability_master.ability_objects -= src - var/mob/owner = ability_master.owner_ref?.resolve() - if(istype(owner) && owner.client) - owner.client.screen -= src - if(ability_master && !ability_master.ability_objects.len) - ability_master.update_icon() -// qdel(ability_master) - ability_master = null - return ..() - -/obj/screen/ability/on_update_icon() - overlays.Cut() - icon_state = "[background_base_state]_spell_base" - - overlays += ability_icon_state - -/obj/screen/ability/handle_click(mob/user, params) - activate() - -// Makes the ability be triggered. The subclasses of this are responsible for carrying it out in whatever way it needs to. -/obj/screen/ability/proc/activate() - to_world("[src] had activate() called.") - return - -/////////Obj Abilities//////// -//Buttons to trigger objects// -////////////////////////////// - -/obj/screen/ability/obj_based - var/obj/object = null - -/obj/screen/ability/obj_based/activate() - if(object) - object.Click() - -/obj/screen/ability/spell - var/spell/spell - var/spell_base - var/last_charge = 0 - var/icon/last_charged_icon - -/obj/screen/ability/spell/Destroy() - if(spell) - spell.connected_button = null - spell = null - return ..() - -/obj/screen/ability_master/proc/add_spell(var/spell/spell) - if(!spell) return - - if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one - return - - if(get_ability_by_spell(spell)) - return - - var/obj/screen/ability/spell/A = new(null) - A.ability_master = src - A.spell = spell - A.SetName(spell.name) - - if(!spell.override_base) //if it's not set, we do basic checks - A.spell_base = "const" //construct spells - else - A.spell_base = spell.override_base - A.update_charge(1) - spell_objects.Add(A) - ability_objects.Add(A) - var/mob/owner = owner_ref?.resolve() - if(istype(owner) && !QDELETED(owner) && owner.client) - toggle_open(2) //forces the icons to refresh on screen - -/obj/screen/ability_master/proc/update_spells(var/forced = 0) - for(var/obj/screen/ability/spell/spell in spell_objects) - spell.update_charge(forced) - -/obj/screen/ability/spell/proc/update_charge(var/forced_update = 0) - if(!spell) - qdel(src) - return - - if(last_charge == spell.charge_counter && !forced_update) - return //nothing to see here - - overlays -= spell.hud_state - - if(spell.charge_type == Sp_RECHARGE || spell.charge_type == Sp_CHARGES) - if(spell.charge_counter < spell.charge_max) - icon_state = "[spell_base]_spell_base" - if(spell.charge_counter > 0) - var/icon/partial_charge = icon(src.icon, "[spell_base]_spell_ready") - partial_charge.Crop(1, 1, partial_charge.Width(), round(partial_charge.Height() * spell.charge_counter / spell.charge_max)) - overlays += partial_charge - if(last_charged_icon) - overlays -= last_charged_icon - last_charged_icon = partial_charge - else if(last_charged_icon) - overlays -= last_charged_icon - last_charged_icon = null - else - icon_state = "[spell_base]_spell_ready" - if(last_charged_icon) - overlays -= last_charged_icon - else - icon_state = "[spell_base]_spell_ready" - - overlays += spell.hud_state - - last_charge = spell.charge_counter - - overlays -= "silence" - if(spell.silenced) - overlays += "silence" - -/obj/screen/ability/spell/on_update_icon(var/forced = 0) - update_charge(forced) - return - -/obj/screen/ability/spell/activate() - spell.perform(usr) - -/obj/screen/ability_master/proc/silence_spells(var/amount) - for(var/obj/screen/ability/spell/spell in spell_objects) - spell.spell.silenced = amount - spell.spell.process() - spell.update_charge(1) diff --git a/code/_onclick/hud/screen/screen_action_button.dm b/code/_onclick/hud/screen/screen_action_button.dm index 33bf849cb9c..df79fa29ce4 100644 --- a/code/_onclick/hud/screen/screen_action_button.dm +++ b/code/_onclick/hud/screen/screen_action_button.dm @@ -14,6 +14,7 @@ return FALSE /obj/screen/action_button/on_update_icon() + ..() if(!action) return icon = action.background_icon @@ -72,7 +73,6 @@ update_icon() user.update_action_buttons() -/obj/screen/action_button/hide_toggle/on_update_icon() - cut_overlays() +/obj/screen/action_button/hide_toggle/rebuild_screen_overlays() + ..() add_overlay(hidden ? "show" : "hide") - compile_overlays() diff --git a/code/_onclick/hud/screen/screen_ai_button.dm b/code/_onclick/hud/screen/screen_ai_button.dm index a7f2861002f..e2060d6b8a1 100644 --- a/code/_onclick/hud/screen/screen_ai_button.dm +++ b/code/_onclick/hud/screen/screen_ai_button.dm @@ -1,16 +1,18 @@ /obj/screen/ai_button icon = 'icons/mob/screen/ai.dmi' requires_ui_style = FALSE - var/mob/living/silicon/ai/ai_verb + var/ai_verb var/list/input_procs var/list/input_args var/list/template_icon = list(null, "template") var/image/template_undelay /obj/screen/ai_button/handle_click(mob/user, params) - if(!isAI(user)) - return TRUE + var/mob/living/silicon/ai/A = user + if(!istype(A)) + return TRUE + if(!(ai_verb in A.verbs)) return TRUE @@ -30,7 +32,6 @@ if(!(ai_verb in A.verbs) || A.incapacitated()) return - input_arguments += input_arg if(length(input_args)) diff --git a/code/_onclick/hud/screen/screen_attack_selector.dm b/code/_onclick/hud/screen/screen_attack_selector.dm index ea27e11ca3e..537d2ccce38 100644 --- a/code/_onclick/hud/screen/screen_attack_selector.dm +++ b/code/_onclick/hud/screen/screen_attack_selector.dm @@ -28,6 +28,7 @@ return TRUE /obj/screen/default_attack_selector/on_update_icon() + ..() var/mob/living/human/owner = owner_ref?.resolve() var/decl/natural_attack/attack = istype(owner) && owner.default_attack icon_state = attack?.selector_icon_state || "attack_none" diff --git a/code/_onclick/hud/screen/screen_cataloguer.dm b/code/_onclick/hud/screen/screen_cataloguer.dm index 6fc95896bfa..bb0965dac40 100644 --- a/code/_onclick/hud/screen/screen_cataloguer.dm +++ b/code/_onclick/hud/screen/screen_cataloguer.dm @@ -31,8 +31,8 @@ QDEL_NULL(holder_image) return ..() -/obj/screen/scan_radius/on_update_icon() - cut_overlays() +/obj/screen/scan_radius/rebuild_screen_overlays() + ..() if(scan_range <= 1) add_overlay("single") else @@ -79,5 +79,3 @@ I.pixel_x = world.icon_size * i I.pixel_y = pixel_bound add_overlay(I) - - compile_overlays() diff --git a/code/_onclick/hud/screen/screen_credits.dm b/code/_onclick/hud/screen/screen_credits.dm index 5025cc7a87d..2c04fffcf9a 100644 --- a/code/_onclick/hud/screen/screen_credits.dm +++ b/code/_onclick/hud/screen/screen_credits.dm @@ -18,15 +18,17 @@ animate(src, transform = M, time = CREDIT_ROLL_SPEED) target = M animate(src, alpha = 255, time = CREDIT_EASE_DURATION, flags = ANIMATION_PARALLEL) - spawn(CREDIT_ROLL_SPEED - CREDIT_EASE_DURATION) - if(!QDELETED(src)) - animate(src, alpha = 0, transform = target, time = CREDIT_EASE_DURATION) - sleep(CREDIT_EASE_DURATION) - qdel(src) + addtimer(CALLBACK(src, PROC_REF(ease_out)), CREDIT_ROLL_SPEED - CREDIT_EASE_DURATION) var/mob/owner = owner_ref?.resolve() if(istype(owner) && owner.client) owner.client.screen += src +/obj/screen/credit/proc/ease_out() + if(QDELETED(src)) + return + animate(src, alpha = 0, transform = target, time = CREDIT_EASE_DURATION) + QDEL_IN_CLIENT_TIME(src, CREDIT_EASE_DURATION) + /obj/screen/credit/Destroy() var/client/P = parent if(istype(P)) diff --git a/code/_onclick/hud/screen/screen_exosuit.dm b/code/_onclick/hud/screen/screen_exosuit.dm index 5677d2d93bc..ad9171ed446 100644 --- a/code/_onclick/hud/screen/screen_exosuit.dm +++ b/code/_onclick/hud/screen/screen_exosuit.dm @@ -3,6 +3,7 @@ icon = 'icons/mecha/mech_hud.dmi' icon_state = "base" requires_ui_style = FALSE + apply_screen_overlay = FALSE var/initial_maptext var/height = 14 @@ -317,7 +318,8 @@ var/mob/living/exosuit/owner = get_owning_exosuit() if(owner) toggled = owner.hatch_closed - . = ..() + . = ..() + if(owner) if(toggled) maptext = MECH_UI_STYLE("OPEN") maptext_x = 5 diff --git a/code/_onclick/hud/screen/screen_gun.dm b/code/_onclick/hud/screen/screen_gun.dm index fdf77245d1c..86bba4206e7 100644 --- a/code/_onclick/hud/screen/screen_gun.dm +++ b/code/_onclick/hud/screen/screen_gun.dm @@ -2,8 +2,15 @@ icon = 'icons/mob/screen/styles/midnight/fire_intent.dmi' dir = SOUTH abstract_type = /obj/screen/gun + var/base_icon_state var/toggle_flag +/obj/screen/gun/on_update_icon() + if(toggle_flag && base_icon_state) + var/mob/living/owner = owner_ref?.resolve() + icon_state = "[base_icon_state][!!(istype(owner) && owner.aiming && (owner.aiming.target_permissions & toggle_flag))]" + ..() + /obj/screen/gun/handle_click(mob/user, params) if(isliving(user)) var/mob/living/shooter = user @@ -17,26 +24,35 @@ /obj/screen/gun/move name = "Allow Movement" icon_state = "no_walk1" + base_icon_state = "no_walk" screen_loc = ui_gun2 toggle_flag = TARGET_CAN_MOVE /obj/screen/gun/item name = "Allow Item Use" icon_state = "no_item1" + base_icon_state = "no_item" screen_loc = ui_gun1 toggle_flag = TARGET_CAN_CLICK /obj/screen/gun/radio name = "Disallow Radio Use" icon_state = "no_radio1" + base_icon_state = "no_radio" screen_loc = ui_gun4 toggle_flag = TARGET_CAN_RADIO /obj/screen/gun/mode name = "Toggle Gun Mode" icon_state = "gun0" + base_icon_state = "gun" screen_loc = ui_gun_select +/obj/screen/gun/mode/on_update_icon() + var/mob/living/owner = owner_ref?.resolve() + icon_state = "[base_icon_state][!!(istype(owner) && owner.aiming?.active)]" + ..() + /obj/screen/gun/mode/handle_click(mob/user, params) if(..()) var/mob/living/shooter = user diff --git a/code/_onclick/hud/screen/screen_intent.dm b/code/_onclick/hud/screen/screen_intent.dm index 4682cc14836..ca75b836812 100644 --- a/code/_onclick/hud/screen/screen_intent.dm +++ b/code/_onclick/hud/screen/screen_intent.dm @@ -1,67 +1,117 @@ -/obj/screen/intent - name = "intent" - icon = 'icons/mob/screen/styles/intents.dmi' - icon_state = "intents" - screen_loc = ui_acti +// Sub-element used for clickable intent selection. +/obj/screen/intent_button + layer = FLOAT_LAYER + plane = FLOAT_PLANE + icon = 'icons/screen/intents.dmi' + icon_state = "blank" requires_ui_style = FALSE + screen_loc = null // Technically a screen element, but we use vis_contents to draw them. + var/obj/screen/intent/parent + var/decl/intent/intent + var/selected + +/obj/screen/intent_button/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat, intent_owner) + parent = intent_owner + . = ..() + +/obj/screen/intent_button/Destroy() + parent = null + intent = null + return ..() + +/obj/screen/intent_button/proc/set_selected(decl/intent/_intent) + intent = _intent + selected = TRUE + update_icon() + +/obj/screen/intent_button/proc/set_deselected(decl/intent/_intent) + intent = _intent + selected = FALSE + update_icon() + +/obj/screen/intent_button/handle_click(mob/user, params) + . = ..() + if(. && intent && parent) + parent.set_intent(intent) + +/obj/screen/intent_button/examine(mob/user, distance) + SHOULD_CALL_PARENT(FALSE) + if(desc) + to_chat(user, desc) + +/obj/screen/intent_button/on_update_icon() + . = ..() + screen_loc = null + if(intent) + name = intent.name + desc = intent.desc + icon = intent.icon + icon_state = selected ? intent.icon_state : "[intent.icon_state]_off" + +/obj/screen/intent + name = "intent" + icon = 'icons/screen/intents.dmi' + icon_state = "blank" + screen_loc = ui_acti + requires_ui_style = FALSE + apply_screen_overlay = FALSE + var/intent_width = 16 + var/intent_height = 16 + var/list/intent_selectors /obj/screen/intent/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) . = ..() update_icon() + // Hide from right-click. + name = "" + verbs.Cut() -/obj/screen/intent/handle_click(mob/user, params) +/obj/screen/intent/Destroy() + QDEL_NULL(intent_selectors) + vis_contents.Cut() + return ..() + +/obj/screen/intent/proc/set_intent(decl/intent/intent) var/mob/owner = owner_ref?.resolve() - if(!istype(owner) || QDELETED(owner) || user != owner || !params) - return - params = params2list(params) - if(owner.set_intent(get_intent_by_position(owner.get_available_intents(), text2num(params["icon-x"]), text2num(params["icon-y"])))) + if(istype(owner) && istype(intent)) + owner.set_intent(intent) update_icon() -/obj/screen/intent/proc/get_intent_by_position(list/intents, icon_x, icon_y) - if(icon_y <= 16) - if(icon_x <= 16) - return intents[1] - return intents[2] - else if(icon_x <= 16) - return intents[3] - return intents[4] - -/obj/screen/intent/proc/apply_intent_overlay_offset(image/overlay, index) - switch(index) - if(2) - overlay.pixel_x = 16 - if(3) - overlay.pixel_y = 16 - if(4) - overlay.pixel_x = 16 - overlay.pixel_y = 16 +/obj/screen/intent/proc/apply_intent_button_offset(atom/intent_button, index, intent_count) + intent_button.pixel_z = 0 + intent_button.pixel_w = 0 + intent_button.pixel_y = 0 + intent_button.pixel_x = -((intent_count * intent_width)/2) + ((index-1) * intent_width) + +/obj/screen/intent/proc/get_intent_button(index) + . = (index >= 1 && index <= length(intent_selectors)) ? intent_selectors[index] : null + if(!.) + . = new /obj/screen/intent_button(null, owner_ref?.resolve(), null, null, null, null, src) + LAZYADD(intent_selectors, .) /obj/screen/intent/on_update_icon() - cut_overlays() + ..() + var/mob/owner = owner_ref?.resolve() if(!istype(owner) || QDELETED(owner)) return + var/decl/intent/owner_intent = owner.get_intent() - var/i = 0 - var/image/I - for(var/decl/intent/intent as anything in owner.get_available_intents()) + var/i = 1 + var/list/all_intents = owner.get_available_intents() + for(var/decl/intent/intent as anything in all_intents) + var/obj/screen/intent_button/intent_button = get_intent_button(i) if(intent == owner_intent) - I = image(intent.icon, intent.icon_state) + intent_button.set_selected(intent) else - I = image(intent.icon, "[intent.icon_state]_off") + intent_button.set_deselected(intent) i++ - apply_intent_overlay_offset(I, i) - add_overlay(I) - compile_overlays() + apply_intent_button_offset(intent_button, i, length(all_intents)) + add_vis_contents(intent_button) -/obj/screen/intent/binary - icon = 'icons/mob/screen/styles/intents_wide.dmi' + if(i < length(intent_selectors)) + for(var/index = i+1 to length(intent_selectors)) + remove_vis_contents(intent_selectors[index]) -/obj/screen/intent/binary/get_intent_by_position(list/intents, icon_x, icon_y) - if(icon_y <= 16) - return intents[1] - return intents[2] - -/obj/screen/intent/binary/apply_intent_overlay_offset(image/overlay, index) - if(index == 2) - overlay.pixel_y = 16 +/obj/screen/intent/binary + intent_width = 32 diff --git a/code/_onclick/hud/screen/screen_inventory.dm b/code/_onclick/hud/screen/screen_inventory.dm index 5bacceaeef0..cc4a4835b82 100644 --- a/code/_onclick/hud/screen/screen_inventory.dm +++ b/code/_onclick/hud/screen/screen_inventory.dm @@ -40,19 +40,15 @@ mouse_over_atom_ref = null update_icon() -/obj/screen/inventory/on_update_icon() +/obj/screen/inventory/rebuild_screen_overlays() - cut_overlays() + ..() // Validate our owner still exists. var/mob/owner = owner_ref?.resolve() if(!istype(owner) || QDELETED(owner) || !(src in owner.client?.screen)) return - // Mark our selected hand. - if(owner.get_active_held_item_slot() == slot_id) - add_overlay("hand_selected") - // Mark anything we're potentially trying to equip. var/obj/item/mouse_over_atom = mouse_over_atom_ref?.resolve() if(istype(mouse_over_atom) && !QDELETED(mouse_over_atom) && !owner.get_equipped_item(slot_id)) @@ -65,9 +61,7 @@ MA.pixel_y = mouse_over_atom.default_pixel_y MA.pixel_w = mouse_over_atom.default_pixel_w MA.pixel_z = mouse_over_atom.default_pixel_z + MA.appearance_flags |= (KEEP_TOGETHER | RESET_COLOR) add_overlay(MA) else mouse_over_atom_ref = null - - // UI needs to be responsive so avoid the subsecond update delay. - compile_overlays() diff --git a/code/_onclick/hud/screen/screen_inventory_hands.dm b/code/_onclick/hud/screen/screen_inventory_hands.dm new file mode 100644 index 00000000000..7dbdbf0b152 --- /dev/null +++ b/code/_onclick/hud/screen/screen_inventory_hands.dm @@ -0,0 +1,31 @@ +/obj/screen/inventory/hand + icon_state = "hand_base" + +/obj/screen/inventory/hand/rebuild_screen_overlays() + + ..() + + // Validate our owner still exists. + var/mob/owner = owner_ref?.resolve() + if(!istype(owner) || QDELETED(owner) || !(src in owner.client?.screen)) + return + + var/overlay_color = owner?.client?.prefs.UI_style_highlight_color || COLOR_WHITE + var/decl/ui_style/ui_style = get_owner_ui_style() + if(owner.get_active_held_item_slot() == slot_id) + if(ui_style?.use_overlay_color) + add_overlay(overlay_image(icon, "hand_selected", overlay_color, RESET_COLOR)) + else + add_overlay("hand_selected") + + var/datum/inventory_slot/gripper/inv_slot = owner.get_inventory_slot_datum(slot_id) + if(istype(inv_slot)) + if(ui_style?.use_overlay_color) + add_overlay(overlay_image(icon, "hand_[inv_slot.hand_overlay || slot_id]", overlay_color, RESET_COLOR)) + else + add_overlay("hand_[inv_slot.hand_overlay || slot_id]", TRUE) + if(inv_slot.ui_label) + if(ui_style?.use_overlay_color) + add_overlay(overlay_image(icon, "hand_[inv_slot.ui_label]", overlay_color, RESET_COLOR)) + else + add_overlay("hand_[inv_slot.ui_label]", TRUE) diff --git a/code/_onclick/hud/screen/screen_maneuver.dm b/code/_onclick/hud/screen/screen_maneuver.dm index 203bffc6943..1a66910e8d5 100644 --- a/code/_onclick/hud/screen/screen_maneuver.dm +++ b/code/_onclick/hud/screen/screen_maneuver.dm @@ -9,7 +9,7 @@ user_living.prepare_maneuver() /obj/screen/maneuver/examine(mob/user, distance) - . = ..() + SHOULD_CALL_PARENT(FALSE) if(!isliving(user)) return var/mob/living/user_living = user @@ -17,3 +17,8 @@ to_chat(src, SPAN_NOTICE("You are prepared to [user_living.prepared_maneuver.name].")) else to_chat(src, SPAN_NOTICE("You are not prepared to perform a maneuver.")) + +/obj/screen/maneuver/on_update_icon() + var/mob/living/owner = owner_ref?.resolve() + icon_state = (istype(owner) && owner.prepared_maneuver) ? "maneuver_on" : "maneuver_off" + ..() diff --git a/code/_onclick/hud/screen/screen_movement.dm b/code/_onclick/hud/screen/screen_movement.dm index e389e554cc5..674882295dd 100644 --- a/code/_onclick/hud/screen/screen_movement.dm +++ b/code/_onclick/hud/screen/screen_movement.dm @@ -1,7 +1,14 @@ /obj/screen/movement name = "movement method" screen_loc = ui_movi + icon_state = "creeping" /obj/screen/movement/handle_click(mob/user, params) if(istype(user)) user.set_next_usable_move_intent() + +/obj/screen/movement/on_update_icon() + var/mob/living/owner = owner_ref?.resolve() + if(istype(owner) && istype(owner.move_intent)) + icon_state = owner.move_intent.hud_icon_state + . = ..() diff --git a/code/_onclick/hud/screen/screen_robot_intent.dm b/code/_onclick/hud/screen/screen_robot_intent.dm deleted file mode 100644 index cff12b35ea9..00000000000 --- a/code/_onclick/hud/screen/screen_robot_intent.dm +++ /dev/null @@ -1,2 +0,0 @@ -/obj/screen/intent/robot - screen_loc = ui_acti diff --git a/code/_onclick/hud/screen/screen_throw.dm b/code/_onclick/hud/screen/screen_throw.dm index 679018fba8d..1b4de10e6e8 100644 --- a/code/_onclick/hud/screen/screen_throw.dm +++ b/code/_onclick/hud/screen/screen_throw.dm @@ -6,3 +6,9 @@ /obj/screen/throw_toggle/handle_click(mob/user, params) if(!user.stat && isturf(user.loc) && !user.restrained()) user.toggle_throw_mode() + +/obj/screen/throw_toggle/on_update_icon() + var/mob/living/owner = owner_ref?.resolve() + if(istype(owner)) + icon_state = "act_throw_[owner.in_throw_mode ? "on" : "off"]" + . = ..() diff --git a/code/_onclick/hud/screen/screen_up_hint.dm b/code/_onclick/hud/screen/screen_up_hint.dm index a6e45820115..5a1a2b796fc 100644 --- a/code/_onclick/hud/screen/screen_up_hint.dm +++ b/code/_onclick/hud/screen/screen_up_hint.dm @@ -7,3 +7,9 @@ if(isliving(user)) var/mob/living/L = user L.lookup() + +/obj/screen/up_hint/on_update_icon() + var/mob/owner = owner_ref?.resolve() + var/turf/above = istype(owner) ? GetAbove(get_turf(owner)) : null + icon_state = "uphint[!!(istype(above) && TURF_IS_MIMICKING(above))]" + ..() diff --git a/code/_onclick/hud/screen/screen_zone_selector.dm b/code/_onclick/hud/screen/screen_zone_selector.dm index 008d2b90262..0850498b7f2 100644 --- a/code/_onclick/hud/screen/screen_zone_selector.dm +++ b/code/_onclick/hud/screen/screen_zone_selector.dm @@ -64,10 +64,6 @@ set_selected_zone(new_selecting) return TRUE -/obj/screen/zone_selector/Initialize(mapload, mob/_owner, ui_style, ui_color, ui_alpha) - . = ..() - update_icon() - /obj/screen/zone_selector/proc/set_selected_zone(bodypart) var/old_selecting = selecting selecting = bodypart @@ -75,5 +71,6 @@ update_icon() return TRUE -/obj/screen/zone_selector/on_update_icon() - set_overlays(image('icons/mob/zone_sel.dmi', "[selecting]")) +/obj/screen/zone_selector/rebuild_screen_overlays() + ..() + add_overlay(image('icons/mob/zone_sel.dmi', "[selecting]")) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 1dbed7ab122..b92356b4a14 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -26,12 +26,18 @@ avoid code duplication. This includes items that may sometimes act as a standard // If TRUE, prevent afterattack from running. /obj/item/proc/resolve_attackby(atom/A, mob/user, var/click_params) + if(!user.check_dexterity(get_required_attack_dexterity(user, A))) + return TRUE if(!(item_flags & ITEM_FLAG_NO_PRINT)) add_fingerprint(user) return A.attackby(src, user, click_params) // If TRUE, prevent afterattack from running. /atom/proc/attackby(obj/item/used_item, mob/user, var/click_params) + + if(try_handle_interactions(user, get_standard_interactions(user), user?.get_active_held_item(), check_alt_interactions = FALSE)) + return TRUE + if(storage) if(isrobot(user) && (used_item == user.get_active_held_item())) return FALSE //Robots can't store their modules. @@ -39,6 +45,7 @@ avoid code duplication. This includes items that may sometimes act as a standard return FALSE used_item.add_fingerprint(user) return storage.handle_item_insertion(user, used_item, click_params = click_params) + return FALSE /atom/movable/attackby(obj/item/W, mob/user) @@ -80,7 +87,7 @@ avoid code duplication. This includes items that may sometimes act as a standard if(milkable.handle_milked(used_item, user)) return TRUE - if(used_item.edge && has_extension(src, /datum/extension/shearable)) + if(used_item.has_edge() && has_extension(src, /datum/extension/shearable)) var/datum/extension/shearable/shearable = get_extension(src, /datum/extension/shearable) if(shearable.handle_sheared(used_item, user)) return TRUE @@ -165,12 +172,12 @@ avoid code duplication. This includes items that may sometimes act as a standard /obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) var/use_hitsound = hitsound if(!use_hitsound) - if(edge || sharp) + if(has_edge() || is_sharp()) use_hitsound = 'sound/weapons/bladeslice.ogg' else use_hitsound = "swing_hit" playsound(loc, use_hitsound, 50, 1, -1) - return target.hit_with_weapon(src, user, get_attack_force(user), hit_zone) + return target.hit_with_weapon(src, user, expend_attack_force(user), hit_zone) /obj/item/proc/handle_reflexive_fire(var/mob/user, var/atom/aiming_at) return istype(user) && istype(aiming_at) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index de135d672c0..f3d74569b41 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -25,6 +25,9 @@ if(handle_grab_interaction(user)) return TRUE + if(try_handle_interactions(user, get_standard_interactions(user), user?.get_active_held_item(), check_alt_interactions = FALSE)) + return TRUE + if(!LAZYLEN(climbers) || (user in climbers) || !user.check_dexterity(DEXTERITY_HOLD_ITEM, silent = TRUE)) return FALSE diff --git a/code/controllers/subsystems/air.dm b/code/controllers/subsystems/air.dm index 4da8abb538d..92e3271df80 100644 --- a/code/controllers/subsystems/air.dm +++ b/code/controllers/subsystems/air.dm @@ -138,7 +138,13 @@ SUBSYSTEM_DEF(air) if(!SHOULD_PARTICIPATE_IN_ZONES(T)) continue simulated_turf_count++ + // We also skip anything already queued, since it'll be settled when fire() runs anyway. + if(T.needs_air_update) + continue T.update_air_properties() + // air state is necessarily globally incomplete during this + // so we can't do T.post_update_air_properties(), which needs + // connections to have been settled already. CHECK_TICK report_progress({"Total Simulated Turfs: [simulated_turf_count] @@ -198,7 +204,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun T.update_air_properties() T.post_update_air_properties() - T.needs_air_update = 0 + T.needs_air_update = FALSE #ifdef ZASDBG T.remove_vis_contents(zasdbgovl_mark) #endif @@ -214,7 +220,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun T.update_air_properties() T.post_update_air_properties() - T.needs_air_update = 0 + T.needs_air_update = FALSE #ifdef ZASDBG T.remove_vis_contents(zasdbgovl_mark) #endif @@ -368,13 +374,15 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun #ifdef ZASDBG ASSERT(isturf(T)) #endif - if(T.needs_air_update) + // don't queue us if we've already been queued + // and if SSair hasn't run, every turf in the world will get updated soon anyway + if(T.needs_air_update || !SSair.initialized) return tiles_to_update += T #ifdef ZASDBG T.add_vis_contents(zasdbgovl_mark) #endif - T.needs_air_update = 1 + T.needs_air_update = TRUE /datum/controller/subsystem/air/proc/mark_zone_update(zone/Z) #ifdef ZASDBG diff --git a/code/controllers/subsystems/ambience.dm b/code/controllers/subsystems/ambience.dm index 757f846c640..8f4b462e205 100644 --- a/code/controllers/subsystems/ambience.dm +++ b/code/controllers/subsystems/ambience.dm @@ -33,6 +33,14 @@ SUBSYSTEM_DEF(ambience) /// Whether this turf has been queued for an ambient lighting update. var/ambience_queued = FALSE +/turf/proc/shows_outdoor_ambience() + return is_outside() + +// Starlight can't be blocked by stuff above a space turf. +// TODO: decide if open sky deserves the same treatment +/turf/space/shows_outdoor_ambience() + return TRUE + /turf/proc/update_ambient_light_from_z_or_area() // If we're not outside, we don't show ambient light. @@ -40,7 +48,7 @@ SUBSYSTEM_DEF(ambience) var/ambient_light_modifier // If we're indoors because of our area, OR we're outdoors and not exposed to the weather, get interior ambience. - var/outsideness = is_outside() + var/outsideness = shows_outdoor_ambience() if((!outsideness && is_outside == OUTSIDE_AREA) || (outsideness && get_weather_exposure() != WEATHER_EXPOSED)) var/area/A = get_area(src) if(isnull(A?.interior_ambient_light_modifier)) diff --git a/code/controllers/subsystems/atoms.dm b/code/controllers/subsystems/atoms.dm index 6e45e5e274b..baf065bd0fd 100644 --- a/code/controllers/subsystems/atoms.dm +++ b/code/controllers/subsystems/atoms.dm @@ -11,8 +11,10 @@ SUBSYSTEM_DEF(atoms) var/atom_init_stage = INITIALIZATION_INSSATOMS var/old_init_stage - var/list/late_loaders + /// A non-associative list of lists, with the format list(list(atom, list(Initialize arguments))). var/list/created_atoms = list() + /// A non-associative list of lists, with the format list(list(atom, list(LateInitialize arguments))). + var/list/late_loaders = list() var/list/BadInitializeCalls = list() @@ -22,21 +24,25 @@ SUBSYSTEM_DEF(atoms) return ..() /datum/controller/subsystem/atoms/proc/InitializeAtoms() - if(atom_init_stage <= INITIALIZATION_INSSATOMS_LATE) + if(atom_init_stage <= INITIALIZATION_INSSATOMS) return atom_init_stage = INITIALIZATION_INNEW_MAPLOAD - LAZYINITLIST(late_loaders) - var/list/mapload_arg = list(TRUE) - var/count = created_atoms.len - while(created_atoms.len) - var/atom/A = created_atoms[created_atoms.len] - var/list/atom_args = created_atoms[A] - created_atoms.len-- - if(!QDELETED(A) && !(A.atom_flags & ATOM_FLAG_INITIALIZED)) + var/index = 1 + // Things can add to the end of this list while we iterate, so we can't use a for loop. + while(index <= length(created_atoms)) + // Don't remove from this list while we run, that's expensive. + // That would also make it harder to handle things added while we iterate. + var/list/creation_packet = created_atoms[index++] + var/atom/A = creation_packet[1] + var/list/atom_args = creation_packet[2] + // I sure hope nothing in this list is ever hard-deleted, or else QDELING will runtime. + // If you get a null reference runtime error, just change it back to QDELETED. + // The ATOM_FLAG_INITIALIZED check is because of INITIALIZE_IMMEDIATE(). + if(!QDELING(A) && !(A.atom_flags & ATOM_FLAG_INITIALIZED)) if(atom_args) atom_args.Insert(1, TRUE) InitAtom(A, atom_args) @@ -44,26 +50,19 @@ SUBSYSTEM_DEF(atoms) InitAtom(A, mapload_arg) CHECK_TICK - // If wondering why not just store all atoms in created_atoms and use the block above: that turns out unbearably expensive. - // Instead, atoms without extra arguments in New created on server start are fished out of world directly. - // We do this exactly once. - if(!initialized) - for(var/atom/A in world) - if(!QDELETED(A) && !(A.atom_flags & ATOM_FLAG_INITIALIZED)) - InitAtom(A, mapload_arg) - ++count - CHECK_TICK - - report_progress("Initialized [count] atom\s") + report_progress("Initialized [index] atom\s") + created_atoms.Cut() atom_init_stage = INITIALIZATION_INNEW_REGULAR - if(late_loaders.len) - for(var/I in late_loaders) - var/atom/A = I - A.LateInitialize(arglist(late_loaders[A])) + if(length(late_loaders)) + index = 1 + while(index <= length(late_loaders)) + var/list/creation_packet = late_loaders[index++] + var/atom/A = creation_packet[1] + A.LateInitialize(arglist(creation_packet[2])) CHECK_TICK - report_progress("Late initialized [late_loaders.len] atom\s") + report_progress("Late initialized [index] atom\s") late_loaders.Cut() /datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments) @@ -91,7 +90,7 @@ SUBSYSTEM_DEF(atoms) EMPTY_BLOCK_GUARD if(INITIALIZE_HINT_LATELOAD) if(arguments[1]) //mapload - late_loaders[A] = arguments + late_loaders[++late_loaders.len] = list(A, arguments) else A.LateInitialize(arglist(arguments)) if(INITIALIZE_HINT_QDEL) @@ -113,7 +112,7 @@ SUBSYSTEM_DEF(atoms) /datum/controller/subsystem/atoms/proc/map_loader_begin() old_init_stage = atom_init_stage - atom_init_stage = INITIALIZATION_INSSATOMS_LATE + atom_init_stage = INITIALIZATION_INSSATOMS /datum/controller/subsystem/atoms/proc/map_loader_stop() atom_init_stage = old_init_stage diff --git a/code/controllers/subsystems/fluids.dm b/code/controllers/subsystems/fluids.dm index 1e95a63bf46..0b9faea243e 100644 --- a/code/controllers/subsystems/fluids.dm +++ b/code/controllers/subsystems/fluids.dm @@ -231,13 +231,21 @@ SUBSYSTEM_DEF(fluids) if(!istype(current_fluid_holder) || QDELETED(current_fluid_holder)) continue var/pushed_something = FALSE - if(current_fluid_holder.reagents?.total_volume > FLUID_SHALLOW && current_fluid_holder.last_flow_strength >= 10) - for(var/atom/movable/AM as anything in current_fluid_holder.get_contained_external_atoms()) - if(AM.is_fluid_pushable(current_fluid_holder.last_flow_strength)) - AM.pushed(current_fluid_holder.last_flow_dir) - pushed_something = TRUE - if(pushed_something && prob(1)) - playsound(current_fluid_holder, 'sound/effects/slosh.ogg', 25, 1) + + if(current_fluid_holder.last_flow_strength >= 10) + // Catwalks mean items will be above the turf; subtract the turf height from our volume. + // TODO: somehow handle stuff that is on a catwalk or on the turf within the same turf. + var/effective_volume = current_fluid_holder.reagents?.total_volume + if(current_fluid_holder.get_supporting_platform()) + // Depth is negative height, hence +=. TODO: positive heights? No idea how to handle that. + effective_volume += current_fluid_holder.get_physical_height() + if(effective_volume > FLUID_SHALLOW) + for(var/atom/movable/AM as anything in current_fluid_holder.get_contained_external_atoms()) + if(AM.try_fluid_push(effective_volume, current_fluid_holder.last_flow_strength)) + AM.pushed(current_fluid_holder.last_flow_dir) + pushed_something = TRUE + if(pushed_something && prob(1)) + playsound(current_fluid_holder, 'sound/effects/slosh.ogg', 25, 1) if(MC_TICK_CHECK) processing_flows.Cut(1, i+1) return diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index 47c1be9e245..ac728a55ea2 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -17,7 +17,7 @@ SUBSYSTEM_DEF(garbage) runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY init_order = SS_INIT_GARBAGE - var/list/collection_timeout = list(0, 3 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level + var/list/collection_timeout = list(GC_FILTER_QUEUE, GC_CHECK_QUEUE, GC_DEL_QUEUE) // deciseconds to wait before moving something up in the queue to the next level //Stat tracking var/delslasttick = 0 // number of del()'s we've done this tick @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(garbage) //Queue var/list/queues - #ifdef TESTING + #ifdef REFTRACKING_ENABLED var/list/reference_find_on_fail = list() #endif @@ -130,30 +130,33 @@ SUBSYSTEM_DEF(garbage) lastlevel = level +// 1 from the hard reference in the queue, and 1 from `D` in the code below +#define REFS_WE_EXPECT 2 + //We do this rather then for(var/refID in queue) because that sort of for loop copies the whole list. //Normally this isn't expensive, but the gc queue can grow to 40k items, and that gets costly/causes overrun. - for (var/refidx in 1 to length(queue)) - var/refID = queue[refidx] - if (isnull(refID)) + for (var/i in 1 to length(queue)) + var/list/L = queue[i] + if (length(L) < GC_QUEUE_ITEM_INDEX_COUNT) count++ if (MC_TICK_CHECK) return continue - var/GCd_at_time = queue[refID] - if(GCd_at_time > cut_off_time) + var/queued_at_time = L[GC_QUEUE_ITEM_QUEUE_TIME] + if(queued_at_time > cut_off_time) break // Everything else is newer, skip them count++ - var/datum/D - D = locate(refID) + var/datum/D = L[GC_QUEUE_ITEM_REF] - if (isnull(D) || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake + // If that's all we've got, send er off + if (refcount(D) == REFS_WE_EXPECT) ++gcedlasttick ++totalgcs pass_counts[level]++ - #ifdef TESTING - reference_find_on_fail -= refID //It's deleted we don't care anymore. + #ifdef REFTRACKING_ENABLED + reference_find_on_fail -= ref(D) //It's deleted we don't care anymore. #endif if (MC_TICK_CHECK) return @@ -161,14 +164,19 @@ SUBSYSTEM_DEF(garbage) // Something's still referring to the qdel'd object. fail_counts[level]++ + switch (level) if (GC_QUEUE_CHECK) - #ifdef TESTING + #ifdef REFTRACKING_ENABLED + // Decides how many refs to look for (potentially) + // Based off the remaining and the ones we can account for + var/remaining_refs = refcount(D) - REFS_WE_EXPECT + var/refID = ref(D) if(reference_find_on_fail[refID]) - D.find_references() + INVOKE_ASYNC(D, TYPE_PROC_REF(/datum, find_references), remaining_refs) #ifdef GC_FAILURE_HARD_LOOKUP else - D.find_references() + INVOKE_ASYNC(D, TYPE_PROC_REF(/datum, find_references), remaining_refs) #endif reference_find_on_fail -= refID #endif @@ -196,17 +204,17 @@ SUBSYSTEM_DEF(garbage) /datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_FILTER) if (isnull(D)) return - if (D.gc_destroyed == GC_QUEUED_FOR_HARD_DEL) - level = GC_QUEUE_HARDDELETE if (level > GC_QUEUE_COUNT) HardDelete(D) return - var/gctime = world.time - var/refid = "\ref[D]" + var/queue_time = world.time - D.gc_destroyed = gctime + if(D.gc_destroyed <= 0) // hasn't been queued yet, or is queued for harddel/actively being qdeleted + D.gc_destroyed = queue_time var/list/queue = queues[level] - queue[refid] = gctime + // not += for byond reasons + // we include D.gc_destroyed to skip things under the cutoff + queue[++queue.len] = list(queue_time, D, D.gc_destroyed) //this is mainly to separate things profile wise. /datum/controller/subsystem/garbage/proc/HardDelete(datum/D) @@ -240,11 +248,6 @@ SUBSYSTEM_DEF(garbage) message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).") postpone(time) -/datum/controller/subsystem/garbage/proc/HardQueue(datum/D) - if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) - queues[GC_QUEUE_FILTER] += D - D.gc_destroyed = GC_QUEUED_FOR_HARD_DEL - /datum/controller/subsystem/garbage/Recover() if (istype(SSgarbage.queues)) for (var/i in 1 to SSgarbage.queues.len) @@ -270,19 +273,19 @@ SUBSYSTEM_DEF(garbage) /datum/qdel_item/New(mytype) name = "[mytype]" -#ifdef TESTING +#ifdef REFTRACKING_ENABLED /proc/qdel_and_find_ref_if_fail(datum/D, force = FALSE) SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE qdel(D, force) #endif -// Should be treated as a replacement for the 'del' keyword. -// Datums passed to this will be given a chance to clean up references to allow the GC to collect them. +/// Should be treated as a replacement for the 'del' keyword. +/// Datums passed to this will be given a chance to clean up references to allow the GC to collect them. +/// Non-datums passed to this will be hard-deleted. /proc/qdel(datum/D, force=FALSE) if(isnull(D)) return if(!istype(D)) - PRINT_STACK_TRACE("qdel() can only handle /datum (sub)types, was passed: [log_info_line(D)]") del(D) return var/datum/qdel_item/I = SSgarbage.items[D.type] @@ -328,7 +331,7 @@ SUBSYSTEM_DEF(garbage) return // Returning LETMELIVE after being told to force destroy // indicates the objects Destroy() does not respect force - #ifdef TESTING + #ifdef REFTRACKING_ENABLED if(!I.no_respect_force) PRINT_STACK_TRACE("WARNING: [D.type] has been force deleted, but is \ returning an immortal QDEL_HINT, indicating it does \ @@ -341,21 +344,22 @@ SUBSYSTEM_DEF(garbage) SSgarbage.Queue(D) if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate() GC_CHECK_AM_NULLSPACE(D, "QDEL_HINT_HARDDEL") - SSgarbage.HardQueue(D) + SSgarbage.Queue(D, GC_QUEUE_HARDDELETE) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. SSgarbage.HardDelete(D) - if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. + if (QDEL_HINT_FINDREFERENCE)//qdel will, if REFTRACKING_ENABLED is enabled, display all references to this object, then queue the object for deletion. SSgarbage.Queue(D) - #ifdef TESTING - D.find_references() + #ifdef REFTRACKING_ENABLED + var/remaining_refs = refcount(D) - REFS_WE_EXPECT + D.find_references(remaining_refs) #endif if (QDEL_HINT_IFFAIL_FINDREFERENCE) SSgarbage.Queue(D) - #ifdef TESTING + #ifdef REFTRACKING_ENABLED SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE #endif else - #ifdef TESTING + #ifdef REFTRACKING_ENABLED if(!I.no_hint) PRINT_STACK_TRACE("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") #endif @@ -364,7 +368,7 @@ SUBSYSTEM_DEF(garbage) else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") -#ifdef TESTING +#ifdef REFTRACKING_ENABLED /datum/verb/find_refs() set category = "Debug" @@ -378,8 +382,9 @@ SUBSYSTEM_DEF(garbage) return find_references() -/datum/proc/find_references() +/datum/proc/find_references(references_to_clear = INFINITY) running_find_references = type + src.references_to_clear = references_to_clear if(usr && usr.client) if(usr.client.running_find_references) testing("CANCELLED search for references to a [usr.client.running_find_references].") @@ -406,13 +411,19 @@ SUBSYSTEM_DEF(garbage) normal_globals[global_var] = global.vars[global_var] DoSearchVar(normal_globals, "(global) -> ") //globals testing("Finished searching globals") + if(src.references_to_clear == 0) // Found all expected references! + return for(var/atom/atom_thing) //atoms DoSearchVar(atom_thing, "World -> [atom_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching atoms") for (var/datum/datum_thing) //datums DoSearchVar(datum_thing, "World -> [datum_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching datums") #ifndef FIND_REF_SKIP_CLIENTS @@ -420,6 +431,8 @@ SUBSYSTEM_DEF(garbage) // IT WILL CRASH!!! for (var/client/client_thing) //clients DoSearchVar(client_thing, "World -> [client_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching clients") #endif @@ -455,10 +468,13 @@ SUBSYSTEM_DEF(garbage) #define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref)-6) ) ) #define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) -/datum/proc/DoSearchVar(X, Xname, recursive_limit = 128) +/datum/proc/DoSearchVar(X, container_name, recursive_limit = 128) if(usr && usr.client && !usr.client.running_find_references) return if (!recursive_limit) + testing("Recursion limit reached. [container_name]") + return + if(references_to_clear == 0) return #ifndef FIND_REF_NO_CHECK_TICK @@ -466,26 +482,34 @@ SUBSYSTEM_DEF(garbage) #endif if(istype(X, /datum)) - var/datum/D = X - if(D.last_find_references == last_find_references) + var/datum/datum_container = X + if(datum_container.last_find_references == last_find_references) return - D.last_find_references = last_find_references - var/list/L = D.vars + datum_container.last_find_references = last_find_references + var/list/vars_list = datum_container.vars - for(var/varname in L) + var/is_atom = FALSE + var/is_area = FALSE + if(isatom(datum_container)) + is_atom = TRUE + if(isarea(datum_container)) + is_area = TRUE + for(var/varname in vars_list) #ifndef FIND_REF_NO_CHECK_TICK CHECK_TICK #endif - if (varname == "vars") + //Fun fact, vis_locs don't count for references + if(varname == "vars" || (is_atom && (varname == "vis_locs" || varname == "overlays" || varname == "underlays" || varname == "filters" || varname == "verbs" || (is_area && varname == "contents")))) continue - var/variable = L[varname] + var/variable = vars_list[varname] if(variable == src) - testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]") + testing("Found [src.type] \ref[src] in [datum_container.type]'s [varname] var. [container_name]") + references_to_clear -= 1 else if(islist(variable)) - DoSearchVar(variable, "[Xname] -> [varname] (list)", recursive_limit-1) + DoSearchVar(variable, "[container_name] -> [varname] (list)", recursive_limit-1) else if(islist(X)) var/normal = IS_NORMAL_LIST(X) @@ -494,16 +518,37 @@ SUBSYSTEM_DEF(garbage) CHECK_TICK #endif if (I == src) - testing("Found [src.type] \ref[src] in list [Xname].") + testing("Found [src.type] \ref[src] in list [container_name].") + + // This is dumb as hell I'm sorry + // I don't want the garbage subsystem to count as a ref for the purposes of this number + // If we find all other refs before it I want to early exit, and if we don't I want to keep searching past it + var/ignore_ref = FALSE + var/list/queues = SSgarbage.queues + for(var/list/queue in queues) + if(X in queue) + ignore_ref = TRUE + break + if(ignore_ref) + testing("[container_name] does not count as a ref for our count") + else + references_to_clear -= 1 + if(references_to_clear == 0) + testing("All references to [type] \ref[src] found, exiting.") + return else if (I && !isnum(I) && normal) if(X[I] == src) - testing("Found [src.type] \ref[src] in list [Xname]\[[I]\]") + testing("Found [src.type] \ref[src] in list [container_name]\[[I]\]") + references_to_clear -= 1 + if(references_to_clear == 0) + testing("All references to [type] \ref[src] found, exiting.") + return else if(islist(X[I])) - DoSearchVar(X[I], "[Xname]\[[I]\]", recursive_limit-1) + DoSearchVar(X[I], "[container_name]\[[I]\]", recursive_limit-1) else if (islist(I)) var/list/Xlist = X - DoSearchVar(I, "[Xname]\[[Xlist.Find(I)]\] -> list", recursive_limit-1) + DoSearchVar(I, "[container_name]\[[Xlist.Find(I)]\] -> list", recursive_limit-1) #endif diff --git a/code/controllers/subsystems/holomap.dm b/code/controllers/subsystems/holomap.dm index 566e9ccb218..ac6662184f6 100644 --- a/code/controllers/subsystems/holomap.dm +++ b/code/controllers/subsystems/holomap.dm @@ -81,7 +81,7 @@ SUBSYSTEM_DEF(minimap) continue if((tile.turf_flags & TURF_IS_HOLOMAP_OBSTACLE) || (locate(/obj/structure/grille) in tile)) canvas.DrawBox(COLOR_HOLOMAP_OBSTACLE, tile.x + offset_x, tile.y + offset_y) - else if((tile.turf_flags & TURF_IS_HOLOMAP_PATH) || (locate(/obj/structure/catwalk) in tile)) + else if((tile.turf_flags & TURF_IS_HOLOMAP_PATH) || tile.get_supporting_platform()) canvas.DrawBox(COLOR_HOLOMAP_PATH, tile.x + offset_x, tile.y + offset_y) CHECK_TICK return canvas diff --git a/code/controllers/subsystems/initialization/character_info.dm b/code/controllers/subsystems/initialization/character_info.dm index cb09b69f361..a874ddcb4f9 100644 --- a/code/controllers/subsystems/initialization/character_info.dm +++ b/code/controllers/subsystems/initialization/character_info.dm @@ -136,7 +136,7 @@ SUBSYSTEM_DEF(character_info) fdel(dump_file_name) text2file(JOINTEXT(SScharacter_info.get_character_manifest_html(apply_striping = FALSE)), dump_file_name) if(fexists(dump_file_name)) - direct_output(usr, ftp(dump_file_name, "dump_manifest.html")) + ftp_to(usr, dump_file_name, "dump_manifest.html") return TOPIC_HANDLED catch(var/exception/E) log_debug("Exception when dumping character relationship manifest: [E]") diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 58e71e52d0d..28244f810e9 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -413,7 +413,7 @@ SUBSYSTEM_DEF(jobs) /decl/loadout_option/proc/is_permitted(mob/living/wearer, datum/job/job) if(!istype(wearer)) return FALSE - if(allowed_roles && !(job.type in allowed_roles)) + if(allowed_roles && (!job || !(job.type in allowed_roles))) return FALSE if(allowed_branches) if(!ishuman(wearer)) @@ -441,7 +441,7 @@ SUBSYSTEM_DEF(jobs) var/decl/loadout_option/G = decls_repository.get_decl_by_id_or_var(thing, /decl/loadout_option) if(!istype(G)) continue - if(!G.is_permitted(H)) + if(!G.is_permitted(H, job)) to_chat(H, SPAN_WARNING("Your current species, job, branch, skills or whitelist status does not permit you to spawn with [thing]!")) continue if(!G.slot || !G.spawn_on_mob(H, H.client.prefs.Gear()[G.uid])) diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm index a7583ae1b2f..61f8b083a33 100644 --- a/code/controllers/subsystems/lighting.dm +++ b/code/controllers/subsystems/lighting.dm @@ -54,7 +54,12 @@ SUBSYSTEM_DEF(lighting) // Generate overlays. for (var/zlevel = 1 to world.maxz) - overlaycount += InitializeZlev(zlevel) + var/datum/level_data/level = SSmapping.levels_by_z[zlevel] + for (var/turf/tile as anything in block(1, 1, zlevel, level.level_max_width, level.level_max_height)) // include TRANSITIONEDGE turfs + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(tile)) + tile.lighting_build_overlay() + overlaycount++ + CHECK_TICK admin_notice(SPAN_DANGER("Created [overlaycount] lighting overlays in [(REALTIMEOFDAY - starttime)/10] seconds."), R_DEBUG) @@ -71,26 +76,6 @@ SUBSYSTEM_DEF(lighting) ..() -/datum/controller/subsystem/lighting/proc/InitializeZlev(zlev) - for (var/thing in Z_ALL_TURFS(zlev)) - var/turf/T = thing - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) && !T.lighting_overlay) // Can't assume that one hasn't already been created on bay/neb. - new /atom/movable/lighting_overlay(T) - . += 1 - if (T.ambient_light) - T.generate_missing_corners() // Forcibly generate corners. - - CHECK_TICK - -// It's safe to pass a list of non-turfs to this list - it'll only check turfs. -/datum/controller/subsystem/lighting/proc/InitializeTurfs(list/targets) - for (var/turf/T in (targets || world)) - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) - T.lighting_build_overlay() - - // If this isn't here, BYOND will set-background us. - CHECK_TICK - /datum/controller/subsystem/lighting/fire(resumed = FALSE, no_mc_tick = FALSE) if (!resumed) processed_lights = 0 diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm index 48d7b425037..674ec094cc6 100644 --- a/code/controllers/subsystems/machines.dm +++ b/code/controllers/subsystems/machines.dm @@ -82,6 +82,12 @@ if(current_step == this_step || (check_resumed && !resumed)) {\ #undef INTERNAL_PROCESS_STEP +/datum/controller/subsystem/machines/StartLoadingMap() + suspend() + +/datum/controller/subsystem/machines/StopLoadingMap() + wake() + // rebuild all power networks from scratch - only called at world creation or by the admin verb // The above is a lie. Turbolifts also call this proc. /datum/controller/subsystem/machines/proc/makepowernets() diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 6e1061636ec..cdba384cdf8 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -109,6 +109,8 @@ SUBSYSTEM_DEF(mapping) // Load any queued map template markers. for(var/obj/abstract/landmark/map_load_mark/queued_mark in queued_markers) queued_mark.load_subtemplate() + if(!QDELETED(queued_mark)) // for if the tile that lands on the landmark is a no-op tile + qdel(queued_mark) queued_markers.Cut() // Populate overmap. @@ -119,15 +121,9 @@ SUBSYSTEM_DEF(mapping) // This needs to be non-null even if the overmap isn't created for this map. overmap_event_handler = GET_DECL(/decl/overmap_event_handler) - var/old_maxz - for(var/z = 1 to world.maxz) - var/datum/level_data/level = levels_by_z[z] - if(!istype(level)) - level = new /datum/level_data/space(z) - PRINT_STACK_TRACE("Missing z-level data object for z[num2text(z)]!") - level.setup_level_data() + setup_data_for_levels() - old_maxz = world.maxz + var/old_maxz = world.maxz // Build away sites. global.using_map.build_away_sites() global.using_map.build_planets() @@ -150,13 +146,7 @@ SUBSYSTEM_DEF(mapping) test_load_map_templates() #endif - // Check/associated/setup our level data objects. - for(var/z = old_maxz + 1 to world.maxz) - var/datum/level_data/level = levels_by_z[z] - if(!istype(level)) - level = new /datum/level_data/space(z) - PRINT_STACK_TRACE("Missing z-level data object for z[num2text(z)]!") - level.setup_level_data() + setup_data_for_levels(min_z = old_maxz + 1) // Generate turbolifts last, since away sites may have elevators to generate too. for(var/obj/abstract/turbolift_spawner/turbolift as anything in turbolifts_to_initialize) @@ -166,6 +156,14 @@ SUBSYSTEM_DEF(mapping) . = ..() +/datum/controller/subsystem/mapping/proc/setup_data_for_levels(min_z = 1, max_z = world.maxz) + for(var/z = min_z to max_z) + var/datum/level_data/level = levels_by_z[z] + if(!istype(level)) + level = new /datum/level_data/space(z) + PRINT_STACK_TRACE("Missing z-level data object for z[num2text(z)]!") + level.setup_level_data() + /datum/controller/subsystem/mapping/Recover() flags |= SS_NO_INIT map_templates = SSmapping.map_templates @@ -198,7 +196,7 @@ SUBSYSTEM_DEF(mapping) . = list() for(var/template_type in subtypesof(/datum/map_template)) var/datum/map_template/template = template_type - if(!TYPE_IS_ABSTRACT(template) && initial(template.template_parent_type) != template_type && initial(template.name)) + if(!TYPE_IS_ABSTRACT(template)) . += new template_type(type) // send name as a param to catch people doing illegal ad hoc creation /datum/controller/subsystem/mapping/proc/get_template(var/template_name) diff --git a/code/controllers/subsystems/overlays.dm b/code/controllers/subsystems/overlays.dm index 42e919ae980..88650811392 100644 --- a/code/controllers/subsystems/overlays.dm +++ b/code/controllers/subsystems/overlays.dm @@ -209,7 +209,7 @@ SUBSYSTEM_DEF(overlays) if(cut_old) our_overlays = cached_other.Copy() else - our_overlays |= cached_other + LAZYDISTINCTADD(our_overlays, cached_other) if(NOT_QUEUED_ALREADY) QUEUE_FOR_COMPILE else if(cut_old) diff --git a/code/controllers/subsystems/processing/overmap.dm b/code/controllers/subsystems/overmap.dm similarity index 100% rename from code/controllers/subsystems/processing/overmap.dm rename to code/controllers/subsystems/overmap.dm diff --git a/code/controllers/subsystems/processing/nano.dm b/code/controllers/subsystems/processing/nano.dm index dfaae9e7999..dd358012c92 100644 --- a/code/controllers/subsystems/processing/nano.dm +++ b/code/controllers/subsystems/processing/nano.dm @@ -37,11 +37,10 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return /nanoui Returns the found ui, or null if none exists */ /datum/controller/subsystem/processing/nano/proc/get_open_ui(mob/user, src_object, ui_key) - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key] || !open_uis[src_object_key][ui_key]) + if (!open_uis[src_object] || !open_uis[src_object][ui_key]) return - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) if (ui.user == user) return ui @@ -54,12 +53,11 @@ PROCESSING_SUBSYSTEM_DEF(nano) */ /datum/controller/subsystem/processing/nano/proc/update_uis(src_object) . = 0 - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key]) + if (!open_uis[src_object]) return - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/ui_key in open_uis[src_object]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) if(ui.src_object && ui.user && ui.src_object.nano_host()) ui.try_update(1) .++ @@ -78,12 +76,11 @@ PROCESSING_SUBSYSTEM_DEF(nano) if (!length(open_uis)) return - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key]) + if (!open_uis[src_object]) return - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/ui_key in open_uis[src_object]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) ui.close() // If it's missing src_object or user, we want to close it even more. .++ @@ -134,9 +131,9 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return nothing */ /datum/controller/subsystem/processing/nano/proc/ui_opened(datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - LAZYINITLIST(open_uis[src_object_key]) - LAZYDISTINCTADD(open_uis[src_object_key][ui.ui_key], ui) + var/src_object = ui.src_object + LAZYINITLIST(open_uis[src_object]) + LAZYDISTINCTADD(open_uis[src_object][ui.ui_key], ui) LAZYDISTINCTADD(ui.user.open_uis, ui) START_PROCESSING(SSnano, ui) @@ -149,18 +146,18 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return int 0 if no ui was removed, 1 if removed successfully */ /datum/controller/subsystem/processing/nano/proc/ui_closed(var/datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - if (!open_uis[src_object_key] || !open_uis[src_object_key][ui.ui_key]) + var/src_object = ui.src_object + if (!open_uis[src_object] || !open_uis[src_object][ui.ui_key]) return 0 // wasn't open STOP_PROCESSING(SSnano, ui) if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface) LAZYREMOVE(ui.user.open_uis, ui) - open_uis[src_object_key][ui.ui_key] -= ui - if(!length(open_uis[src_object_key][ui.ui_key])) - open_uis[src_object_key] -= ui.ui_key - if(!length(open_uis[src_object_key])) - open_uis -= src_object_key + open_uis[src_object][ui.ui_key] -= ui + if(!length(open_uis[src_object][ui.ui_key])) + open_uis[src_object] -= ui.ui_key + if(!length(open_uis[src_object])) + open_uis -= src_object return 1 /** diff --git a/code/controllers/subsystems/plants.dm b/code/controllers/subsystems/processing/plants.dm similarity index 87% rename from code/controllers/subsystems/plants.dm rename to code/controllers/subsystems/processing/plants.dm index a6256b56331..c07b5e9c714 100644 --- a/code/controllers/subsystems/plants.dm +++ b/code/controllers/subsystems/processing/plants.dm @@ -1,11 +1,15 @@ -PROCESSING_SUBSYSTEM_DEF(plants) - name = "Plants" - priority = SS_PRIORITY_PLANTS - runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME - flags = SS_BACKGROUND|SS_POST_FIRE_TIMING - init_order = SS_INIT_PLANTS - wait = 60 +/datum/proc/process_plants() + SHOULD_NOT_SLEEP(TRUE) + return PROCESS_KILL +PROCESSING_SUBSYSTEM_DEF(plants) + name = "Plants" + priority = SS_PRIORITY_PLANTS + runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME + flags = SS_BACKGROUND|SS_POST_FIRE_TIMING + init_order = SS_INIT_PLANTS + wait = 1 MINUTE + process_proc = TYPE_PROC_REF(/datum, process_plants) /// Stores generated fruit descs. var/list/product_descs = list() /// All seed data stored here. diff --git a/code/controllers/subsystems/shuttle.dm b/code/controllers/subsystems/shuttle.dm index 7c6b1a07711..77f0d4083cb 100644 --- a/code/controllers/subsystems/shuttle.dm +++ b/code/controllers/subsystems/shuttle.dm @@ -125,13 +125,14 @@ SUBSYSTEM_DEF(shuttle) /datum/controller/subsystem/shuttle/proc/initialize_shuttle(var/shuttle_type, var/map_hash, var/list/add_args) var/datum/shuttle/shuttle = shuttle_type - if(initial(shuttle.category) != shuttle_type) - var/list/shuttle_args = list(map_hash) - if(length(add_args)) - shuttle_args += add_args - shuttle = new shuttle(arglist(shuttle_args)) - shuttle_areas |= shuttle.shuttle_area - return shuttle + if(TYPE_IS_ABSTRACT(shuttle)) + return null + var/list/shuttle_args = list(map_hash) + if(length(add_args)) + shuttle_args += add_args + shuttle = new shuttle(arglist(shuttle_args)) + shuttle_areas |= shuttle.shuttle_area + return shuttle /datum/controller/subsystem/shuttle/proc/hook_up_motherships(shuttles_list) for(var/datum/shuttle/S in shuttles_list) diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 6bccadcb70a..89122cad2f9 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -121,7 +121,7 @@ SUBSYSTEM_DEF(ticker) Master.SetRunLevel(RUNLEVEL_POSTGAME) end_game_state = END_GAME_READY_TO_END INVOKE_ASYNC(src, PROC_REF(declare_completion)) - if(get_config_value(/decl/config/toggle/allow_map_switching) && get_config_value(/decl/config/toggle/auto_map_vote) && global.all_maps.len > 1) + if(get_config_value(/decl/config/toggle/allow_map_switching) && get_config_value(/decl/config/toggle/auto_map_vote) && length(global.votable_maps) > 1) SSvote.initiate_vote(/datum/vote/map/end_game, automatic = 1) else if(mode_finished && (end_game_state <= END_GAME_NOT_OVER)) diff --git a/code/datums/ai/aggressive.dm b/code/datums/ai/aggressive.dm index 505017dbc0c..6c48ef305e5 100644 --- a/code/datums/ai/aggressive.dm +++ b/code/datums/ai/aggressive.dm @@ -178,7 +178,7 @@ if(!obstacle.can_open(1)) return body.face_atom(obstacle) - body.pry_door(obstacle, (obstacle.pry_mod * body.get_door_pry_time())) + body.pry_door((obstacle.pry_mod * body.get_door_pry_time()), obstacle) return /datum/mob_controller/aggressive/retaliate(atom/source) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index a0aeed07808..aa855c0b1e3 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -10,9 +10,14 @@ /// Used to avoid unnecessary refstring creation in Destroy(). var/tmp/has_state_machine = FALSE -#ifdef TESTING +#ifdef REFTRACKING_ENABLED var/tmp/running_find_references + /// When was this datum last touched by a reftracker? + /// If this value doesn't match with the start of the search + /// We know this datum has never been seen before, and we should check it var/tmp/last_find_references = 0 + /// How many references we're trying to find when searching + var/tmp/references_to_clear = 0 #endif // Default implementation of clean-up code. @@ -54,11 +59,11 @@ cleanup_events(src) if(has_state_machine) - var/list/machines = global.state_machines["\ref[src]"] + var/list/machines = global.state_machines[src] if(length(machines)) for(var/base_type in machines) qdel(machines[base_type]) - global.state_machines -= "\ref[src]" + global.state_machines -= src return QDEL_HINT_QUEUE diff --git a/code/datums/extensions/abilities/abilities.dm b/code/datums/extensions/abilities/abilities.dm index 3995c524dc4..21e02b0126b 100644 --- a/code/datums/extensions/abilities/abilities.dm +++ b/code/datums/extensions/abilities/abilities.dm @@ -26,7 +26,7 @@ /// Clicking a grab on the currently grabbed mob. /datum/extension/abilities/proc/do_grabbed_invocation(atom/target) - if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers)) + if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers) && !istype(target, /obj/screen)) for(var/datum/ability_handler/handler in ability_handlers) if(handler.can_do_grabbed_invocation(holder, target) && handler.do_grabbed_invocation(holder, target)) return TRUE @@ -34,7 +34,7 @@ /// Clicking an adjacent target (UnarmedAttack()) /datum/extension/abilities/proc/do_melee_invocation(atom/target) - if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers)) + if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers) && !istype(target, /obj/screen)) for(var/datum/ability_handler/handler in ability_handlers) if(handler.can_do_melee_invocation(holder, target) && handler.do_melee_invocation(holder, target)) return TRUE @@ -42,7 +42,7 @@ /// Clicking a distant target (RangedAttack()) /datum/extension/abilities/proc/do_ranged_invocation(atom/target) - if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers)) + if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers) && !istype(target, /obj/screen)) for(var/datum/ability_handler/handler in ability_handlers) if(handler.can_do_ranged_invocation(holder, target) && handler.do_ranged_invocation(holder, target)) return TRUE @@ -54,3 +54,8 @@ for(var/datum/ability_handler/handler in ability_handlers) handler.refresh_login() +/datum/extension/abilities/proc/refresh_element_positioning() + var/row = 0 + for(var/datum/ability_handler/handler in ability_handlers) + if(length(handler.screen_elements)) + row += handler.refresh_element_positioning(row) diff --git a/code/datums/extensions/abilities/abilities_mob.dm b/code/datums/extensions/abilities/abilities_mob.dm index b72d0ac2c5f..4879b2d05c4 100644 --- a/code/datums/extensions/abilities/abilities_mob.dm +++ b/code/datums/extensions/abilities/abilities_mob.dm @@ -18,6 +18,7 @@ return FALSE handler = new handler_type(abilities, src) LAZYADD(abilities.ability_handlers, handler) + handler.finalize_ability_handler() return handler /mob/proc/remove_ability_handler(handler_type) @@ -28,6 +29,75 @@ if(!handler) return FALSE LAZYREMOVE(abilities.ability_handlers, handler) + qdel(handler) if(!LAZYLEN(abilities.ability_handlers)) remove_extension(src, /datum/extension/abilities) return TRUE + +/mob/living/proc/copy_abilities_from(mob/living/donor) + var/datum/extension/abilities/abilities = get_extension(src, /datum/extension/abilities) + if(!abilities) + return FALSE + . = FALSE + for(var/datum/ability_handler/handler in abilities.ability_handlers) + if(handler.copy_abilities_to(src)) + . = TRUE + +/mob/living/proc/disable_abilities(var/amount = 0) + if(amount < 0) + return + var/datum/extension/abilities/abilities = get_extension(src, /datum/extension/abilities) + for(var/datum/ability_handler/handler in abilities?.ability_handlers) + handler.disable_abilities(amount) + +/mob/living/proc/copy_abilities_to(mob/living/target) + var/datum/extension/abilities/abilities = get_extension(src, /datum/extension/abilities) + for(var/datum/ability_handler/handler in abilities?.ability_handlers) + handler.copy_abilities_to(target) + +/mob/proc/add_ability(ability_type, list/metadata) + var/decl/ability/ability = GET_DECL(ability_type) + if(!istype(ability) || !ability.associated_handler_type) + return FALSE + var/datum/ability_handler/handler = get_ability_handler(ability.associated_handler_type, create_if_missing = TRUE) + return handler.add_ability(ability_type, metadata) + +/mob/proc/remove_ability(ability_type) + var/decl/ability/ability = GET_DECL(ability_type) + if(!istype(ability) || !ability.associated_handler_type) + return FALSE + var/datum/extension/abilities/abilities = get_extension(src, /datum/extension/abilities) + var/datum/ability_handler/handler = locate(ability.associated_handler_type) in abilities?.ability_handlers + return handler?.remove_ability(ability_type) + +/mob/proc/get_ability_metadata(ability_type) + var/decl/ability/ability = GET_DECL(ability_type) + if(!istype(ability) || !ability.associated_handler_type) + return null + var/datum/extension/abilities/abilities = get_extension(src, /datum/extension/abilities) + var/datum/ability_handler/handler = locate(ability.associated_handler_type) in abilities?.ability_handlers + return handler?.get_metadata(ability_type, create_if_missing = TRUE) + +/mob/proc/has_ability(ability_type) + var/decl/ability/ability = GET_DECL(ability_type) + if(!istype(ability) || !ability.associated_handler_type) + return null + var/datum/extension/abilities/abilities = get_extension(src, /datum/extension/abilities) + var/datum/ability_handler/handler = locate(ability.associated_handler_type) in abilities?.ability_handlers + return handler?.provides_ability(ability_type) + +/mob/Stat() + if((. = ..()) && client) + var/datum/extension/abilities/abilities = get_extension(src, /datum/extension/abilities) + for(var/datum/ability_handler/handler in abilities?.ability_handlers) + handler.show_stat_string(src) + +/mob/proc/get_all_abilities() + var/datum/extension/abilities/abilities_extension = get_extension(src, /datum/extension/abilities) + if(!istype(abilities_extension)) + return + for(var/datum/ability_handler/handler as anything in abilities_extension.ability_handlers) + for(var/ability_type in handler.known_abilities) + var/decl/ability/ability = GET_DECL(ability_type) + if(istype(ability)) + LAZYDISTINCTADD(., ability) diff --git a/code/datums/extensions/abilities/abilities_predator.dm b/code/datums/extensions/abilities/abilities_predator.dm index 066a3feac92..4fc37d4a828 100644 --- a/code/datums/extensions/abilities/abilities_predator.dm +++ b/code/datums/extensions/abilities/abilities_predator.dm @@ -1,11 +1,16 @@ /datum/ability_handler/predator + category_toggle_type = null var/max_dismember_size = MOB_SIZE_SMALL /datum/ability_handler/predator/can_do_melee_invocation(mob/user, atom/target) - return istype(user) && !user.incapacitated() && isatom(target) && target.Adjacent(user) + return ..() || (istype(user) && !user.incapacitated() && isatom(target) && target.Adjacent(user)) /datum/ability_handler/predator/do_melee_invocation(mob/user, atom/target) + . = ..() + if(.) + return + // Nibbles! if(user.check_intent(I_FLAG_HARM)) if(isliving(target)) diff --git a/code/datums/extensions/abilities/ability_button.dm b/code/datums/extensions/abilities/ability_button.dm new file mode 100644 index 00000000000..5ab2d44e66b --- /dev/null +++ b/code/datums/extensions/abilities/ability_button.dm @@ -0,0 +1,108 @@ +/obj/screen/ability + requires_ui_style = FALSE + requires_owner = FALSE + icon_state = "ability" + icon = 'icons/mob/screen/abilities.dmi' + abstract_type = /obj/screen/ability + var/datum/ability_handler/owning_handler + +/obj/screen/ability/Destroy() + remove_from_handler() + return ..() + +/obj/screen/ability/proc/remove_from_handler() + owning_handler = null + +/obj/screen/ability/on_update_icon() + invisibility = (isnull(owning_handler) || owning_handler.showing_abilities) ? 0 : INVISIBILITY_ABSTRACT + +/obj/screen/ability/handle_click(mob/user, params) + to_chat(user, "Click!") + +/obj/screen/ability/button + icon_state = "button" + requires_owner = TRUE + maptext_y = -3 + var/decl/ability/ability + +/obj/screen/ability/button/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) + . = ..() + START_PROCESSING(SSfastprocess, src) + on_update_icon() + +/obj/screen/ability/button/Destroy() + if(is_processing) + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/screen/ability/button/Process() + // We've been broken or deleted. + if(QDELETED(src) || !ability || !owning_handler) + return PROCESS_KILL + // No reason to run periodic updates. + if(!ability.ability_cooldown_time && !ability.max_charge) + return PROCESS_KILL + // Something is broken. + var/list/metadata = owning_handler.get_metadata(ability.type, create_if_missing = FALSE) + if(!metadata) + return PROCESS_KILL + maptext = "" + if(ability.ability_cooldown_time) + var/next_cast = metadata["next_cast"] + if(world.time < next_cast) + maptext = ticks2shortreadable(next_cast - world.time) + if(ability.max_charge) + maptext += "
" + if(ability.max_charge) + maptext += "x[max(0, metadata["charges"])]" + if(maptext) + maptext = STYLE_SMALLFONTS_OUTLINE("
[maptext]
", 7, COLOR_WHITE, COLOR_BLACK) + +/obj/screen/ability/button/remove_from_handler() + owning_handler?.remove_screen_element(src, ability) + return ..() + +/obj/screen/ability/button/handle_click(mob/user, params) + if(owning_handler.prepared_ability == ability) + owning_handler.cancel_prepared_ability() + else if(ability.use_ability(user, get_turf(user), owning_handler)) // tmp, needs better/multi-step target selection + update_icon() + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), ability.get_cooldown_time(ability.get_metadata_for_user(user)) + 1) + +/obj/screen/ability/button/proc/set_ability(decl/ability/_ability) + if(ability == _ability) + return + ability = _ability + if(istype(ability)) + SetName(ability.name) + else + SetName(initial(name)) + update_icon() + +/obj/screen/ability/button/on_update_icon() + . = ..() + icon_state = initial(icon_state) + cut_overlays() + if(istype(ability)) + if(owning_handler && owning_handler.prepared_ability == ability) + icon_state = "[icon_state]-active" + if(ability.ability_icon && ability.ability_icon_state) + add_overlay(overlay_image(ability.ability_icon, ability.ability_icon_state, COLOR_WHITE, (RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM))) + +/obj/screen/ability/category + name = "Toggle Ability Category" + icon_state = "category" + +/obj/screen/ability/category/remove_from_handler() + owning_handler?.remove_screen_element(src, "toggle") + return ..() + +/obj/screen/ability/category/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) + . = ..() + update_icon() + +/obj/screen/ability/category/handle_click(mob/user, params) + owning_handler?.toggle_category_visibility() + +/obj/screen/ability/category/on_update_icon() + icon_state = owning_handler?.showing_abilities ? initial(icon_state) : "[initial(icon_state)]-off" diff --git a/code/datums/extensions/abilities/ability_decl.dm b/code/datums/extensions/abilities/ability_decl.dm new file mode 100644 index 00000000000..9f271846750 --- /dev/null +++ b/code/datums/extensions/abilities/ability_decl.dm @@ -0,0 +1,410 @@ +/decl/ability + abstract_type = /decl/ability + /// A descriptive identifier string. + var/name + /// A descriptive string about the ability. + var/desc + /// An associated handler type, used in add_ability(). + var/associated_handler_type + /// Should this ability be copied between mobs when mind is transferred? + var/copy_with_mind = FALSE + + /// If TRUE, ability is toggled on and will fire when a target is clicked, instead of instantaneously on use. + var/prep_cast = FALSE + /// Used in conjunction with prep_cast, if TRUE, ability will be untoggled after cast. + var/end_prep_on_cast = TRUE + /// Ability items invoking this ability will GC afterwards. + var/item_end_on_cast = TRUE + + /// Does this invocation trigger on a proximity click, if prepared? + var/is_melee_invocation = FALSE + /// Does this invocation trigger on a non-proximity click, if prepared? + var/is_ranged_invocation = FALSE + + // Projectile created and used to propagate this ability, if it is a ranged ability. + /// If set, this ability will create a projectile rather than applying the effect directly. + var/projectile_type + /// Sets the projectile step_delay. + var/projectile_step_delay = 1 + /// Determines the lifetime of the projectile. + var/projectile_duration = 1 SECOND + /// If not set, the ability will have documentation generated for the codex. + var/hidden_from_codex = FALSE + /// If set, this ability will be silenced by null rod and similar mechanics. + var/is_supernatural = FALSE + + // Visual/audible state for the on-turf effect of casting the spell on something. + /// If set, will attempt to draw from this icon on the turf where the ability is used. + var/overlay_icon + /// If set, will attempt to draw this icon_state on the turf where the ability is used. + var/overlay_icon_state + /// Will delete the overlay after this time. + var/overlay_lifespan = 1 SECOND + /// If set, will play a sound when used. + var/use_sound + /// Volume for above. + var/use_sound_volume = 50 + + // Visual state for the ability HUD element. + /// Type of button to use for the UI. Should be /obj/screen/ability/button or subtype. + var/ui_element_type = /obj/screen/ability/button + /// Icon to draw on the ability HUD, if any. + var/ability_icon + /// Icon state to draw on the ability HUD, if any. + var/ability_icon_state + + // Various restrictions on how and when the ability is used. + /// A decl type that handles retrieving and validating targets. + var/decl/ability_targeting/target_selector = /decl/ability_targeting + /// If set to a numeric value, the ability cannot be used before the cooldown has expired. + var/ability_cooldown_time = 5 SECONDS + /// If set, a do_after() will be applied to this spell. + var/ability_use_channel + /// Maximum charges that can be held of this item at a time. If unset, item does not accumulate charges. + var/max_charge + /// How long it takes between charges. + var/charge_delay = 1 SECOND + /// What flags to check before an ability can be used, if any. + var/check_incapacitated = (INCAPACITATION_STUNNED|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_FORCELYING|INCAPACITATION_KNOCKOUT) + /// What type of mob is required to use this ability. + var/mob/expected_mob_type = /mob/living + /// If set, this ability can only be used while standing on a turf (not in an atom's contents or null loc). + var/requires_turf = TRUE + /// If set, this ability cannot be used on the admin z-level. + var/admin_blocked = TRUE + + // Various failure messages. + /// Failed due to purged/null rod. + var/cast_failed_purged_str = "Another power interferes with your own!" + /// Failed due to non-turf loc. + var/cast_failed_no_turf = "You must be standing on solid ground to use this ability." + /// Failed due to being on admin level + var/cast_failed_no_admin_level = "This ability cannot be used on the admin z-level." + /// Failed due to being invalid mob type + var/cast_failed_wrong_mob_type = "This ability may only be used by living creature." + /// Failed due to being downed/buckled + var/cast_failed_incapacitated = "You are in no state to use that ability." + /// Failed due to still being on cooldown from last use + var/cast_failed_on_cooldown = "You cannot use that ability again just yet." + /// Failed due to still recharging + var/cast_failed_no_charges = "You are out of charges for that ability." + /// Failed due to being silenced/disabled + var/cast_failed_disabled_str = "You are unable to use that ability for another $TIME$!" + + // Various casting messages. + /// "$USER$ begins preparing to use an ability on $TARGET$." + var/prepare_message_3p_str + /// "You begin preparing to use an ability on $TARGET$." + var/prepare_message_1p_str + /// "$USER$ uses an ability." + var/cast_message_3p_str + /// "You use an ability." + var/cast_message_1p_str + /// "You ready yourself to use an ability." + var/ready_ability_1p_str + /// "You cease readying yourself to use an ability." + var/cancel_ability_1p_str + /// "You fail to cast an ability." + var/fail_cast_1p_str + +/decl/ability/Initialize() + target_selector = GET_DECL(target_selector) + . = ..() + +/decl/ability/validate() + . = ..() + if(!istype(target_selector, /decl/ability_targeting)) + . += "null or invalid target_selector: '[target_selector || "NULL"]'" + if(!findtext(cast_failed_disabled_str, "$TIME$")) + . += "missing $TIME$ token in cast_failed_disabled_str" + if(!ispath(associated_handler_type, /datum/ability_handler)) + . += "null or invalid associated_handler_type '[associated_handler_type]'" + if(!ability_icon) + . += "null ability_icon" + else if(!istext(ability_icon_state)) + . += "null or non-text ability_icon_state" + else if(!check_state_in_icon(ability_icon_state, ability_icon)) + . += "missing ability_icon_state '[ability_icon_state]' in icon '[ability_icon]'" + +/decl/ability/proc/get_cooldown_time(list/metadata) + return ability_cooldown_time + +/decl/ability/proc/has_valid_targets(user, atom/target, list/metadata) + // Projectiles just need something to shoot at. + if(projectile_type) + return isturf(target) || isturf(target.loc) + // Abilities need at least one valid target. + return target_selector.validate_initial_target(user, target, metadata, src) + +/decl/ability/proc/get_metadata_for(mob/user) + if(!istype(user)) + CRASH("get_metadata_for() called with null or invalid user!") + var/datum/ability_handler/handler = user.get_ability_handler(associated_handler_type, create_if_missing = FALSE) + if(istype(handler)) + . = handler.get_metadata(src) + if(!islist(.)) + PRINT_STACK_TRACE("get_metadata_for() returning null or non-list metadata!") + else + PRINT_STACK_TRACE("get_metadata_for() called by mob with no handler of associated type!") + +// This is the main entrypoint for the ability use chain. +/decl/ability/proc/use_ability(mob/user, atom/target, datum/ability_handler/handler) + + if(!istype(user)) + return + + var/list/metadata = get_metadata_for(user) + if(!islist(metadata) || !can_use_ability(user, metadata)) + return + + if(prep_cast && handler.prepared_ability != src) + handler.prepare_ability(src) + return + + // Resolve our clicked target to the appropriate turf. + target = target_selector.resolve_initial_target(target) + if(!istype(target)) + to_chat(user, SPAN_WARNING("You cannot see a target for [name].")) + return + + if(!has_valid_targets(user, target, metadata)) + to_chat(user, SPAN_WARNING("You cannot use [name] on \the [target].")) + return + + if(!prepare_to_cast(user, target, metadata, handler)) + if(fail_cast_1p_str) + to_chat(user, SPAN_WARNING(capitalize(emote_replace_user_tokens(fail_cast_1p_str, user)))) + return + + if(projectile_type) + // Fire a projectile if that is how this ability works. + fire_projectile_at(user, target, metadata) + + else + // Otherwise, just apply to the target directly. + apply_effect(user, target, metadata) + + if(end_prep_on_cast && handler.prepared_ability == src) + handler.cancel_prepared_ability() + +/decl/ability/proc/fire_projectile_at(mob/user, atom/target, list/metadata) + var/obj/item/projectile/projectile = new projectile_type(get_turf(user)) + if(istype(projectile, /obj/item/projectile/ability)) + var/obj/item/projectile/ability/ability_projectile = projectile + ability_projectile.owner = user + ability_projectile.ability_metadata = metadata + ability_projectile.carried_ability = src + projectile.original = target + projectile.starting = get_turf(user) + projectile.shot_from = user + projectile.current = projectile.original + projectile.yo = target.y - user.y + projectile.xo = target.x - user.x + projectile.life_span = projectile_duration + projectile.hitscan = !projectile_step_delay + projectile.step_delay = projectile_step_delay + projectile.launch(target) + return projectile + +/decl/ability/proc/show_cast_channel_msg(mob/user, atom/target, list/metadata) + if(prepare_message_3p_str && prepare_message_1p_str) + user.visible_message( + SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(prepare_message_3p_str, user), target))), + SPAN_NOTICE(capitalize(emote_replace_target_tokens(prepare_message_1p_str, target))) + ) + else if(prepare_message_1p_str) + user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(prepare_message_1p_str, target)))) + else if(prepare_message_3p_str) + user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(prepare_message_3p_str, user), target)))) + +/decl/ability/proc/show_ability_cast_msg(mob/user, list/targets, list/metadata) + var/atom/target = targets[1] + if(cast_message_3p_str && cast_message_1p_str) + user.visible_message( + SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(cast_message_3p_str, user), target))), + SPAN_NOTICE(capitalize(emote_replace_target_tokens(cast_message_1p_str, target))) + ) + else if(cast_message_1p_str) + user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(cast_message_1p_str, target)))) + else if(cast_message_3p_str) + user.visible_message(SPAN_NOTICE(capitalize(emote_replace_target_tokens(emote_replace_user_tokens(cast_message_3p_str, user), target)))) + +/decl/ability/proc/prepare_to_cast(mob/user, atom/target, list/metadata, datum/ability_handler/handler) + var/use_cooldown_time = get_cooldown_time(metadata) + if(ability_use_channel) + if(world.time < handler.next_channel) + return FALSE + handler.next_channel = world.time + ability_use_channel + show_cast_channel_msg(user, target, metadata) + if(!do_after(user, ability_use_channel, target) || !can_use_ability(user, metadata)) + handler.next_channel = 0 // Don't make them sit out the entire channel period, it's just a debounce/duplicate ability preventative + return FALSE + if(use_cooldown_time > 0) + metadata["next_cast"] = world.time + use_cooldown_time + return TRUE + +/decl/ability/proc/check_equipment(mob/user, list/metadata, silent = FALSE) + return TRUE + +/decl/ability/proc/get_metadata_for_user(mob/user) + if(!user.has_ability(type)) + return null + + var/datum/ability_handler/handler = user.get_ability_handler(associated_handler_type, create_if_missing = FALSE) + if(!istype(handler)) + CRASH("get_metadata_for_user() called by mob with no handler of associated type!") + + return handler.get_metadata(src) + +/decl/ability/proc/can_use_ability(mob/user, list/metadata, silent = FALSE) + + if(!user.has_ability(type)) + error("\The [user] utilized the ability '[type]' without having access to it.") + if(!silent) + to_chat(user, SPAN_WARNING("You shouldn't have this ability! Please notify a developer or raise an issue ticket.")) + return FALSE + + var/turf/my_turf = get_turf(user) + if(requires_turf) + if(!istype(my_turf)) + if(!silent) + to_chat(user, SPAN_WARNING(cast_failed_no_turf)) + return FALSE + if(admin_blocked && isAdminLevel(my_turf.z)) + if(!silent) + to_chat(user, SPAN_WARNING(cast_failed_no_admin_level)) + return FALSE + + if(!istype(user, expected_mob_type)) + if(!silent) + to_chat(user, SPAN_WARNING(cast_failed_wrong_mob_type)) + return FALSE + + if(!isnull(check_incapacitated) && user.incapacitated(check_incapacitated)) + if(!silent) + to_chat(user, SPAN_WARNING(cast_failed_incapacitated)) + return FALSE + + if(!check_equipment(user, metadata, silent)) + return FALSE + + if(ability_cooldown_time && world.time < metadata["next_cast"]) + if(!silent) + to_chat(user, SPAN_WARNING(cast_failed_on_cooldown)) + return FALSE + + if(max_charge && metadata["charges"] <= 0) + if(!silent) + to_chat(user, SPAN_WARNING(cast_failed_no_charges)) + return FALSE + + if(is_supernatural) + + var/is_purged = FALSE + if(isanimal(user)) + var/mob/living/simple_animal/critter = user + is_purged = !!critter.purge + + if(!is_purged) + for(var/turf/turf in range(user, 1)) + if(turf.is_purged()) + is_purged = TRUE + break + + if(is_purged) + if(!silent) + to_chat(user, SPAN_WARNING(cast_failed_purged_str)) + return FALSE + + var/disabled_time = metadata["disabled"] + if(world.time < disabled_time) + if(!silent) + var/remaining_time = ceil((disabled_time - world.time) / 10) + to_chat(user, SPAN_WARNING(replacetext(cast_failed_disabled_str, "$TIME$", "[remaining_time] second\s"))) + return FALSE + + return TRUE + +/decl/ability/proc/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) + SHOULD_CALL_PARENT(TRUE) + if(use_sound) + playsound(get_turf(user), use_sound, use_sound_volume, 1) + if(istype(projectile)) + projectile.expended = TRUE + + admin_attacker_log(user, "attempted to use ability [src] on [hit_target]") + + var/list/targets = target_selector.get_affected(user, hit_target, metadata, src, projectile) + if(length(targets)) + show_ability_cast_msg(user, targets, metadata) + while(length(targets)) + var/target = targets[1] + apply_effect_to(user, target, metadata) + targets = prune_targets(user, target, targets, metadata) + finish_casting(user, hit_target, metadata) + +/decl/ability/proc/finish_casting(mob/user, atom/hit_target, list/metadata) + return + +/decl/ability/proc/prune_targets(user, previous_target, list/targets, list/metadata) + if(!length(targets)) + return null + if(previous_target) + LAZYREMOVE(targets, previous_target) + return targets + +/decl/ability/proc/apply_visuals(mob/user, atom/target, list/metadata) + if(!overlay_icon || !overlay_lifespan) + return + var/turf/overlay_loc = get_turf(target) + if(!isturf(overlay_loc) || locate(/obj/effect/overlay) in overlay_loc) + return + var/obj/effect/overlay/ability_overlay = new(overlay_loc) + ability_overlay.icon = overlay_icon + ability_overlay.icon_state = overlay_icon_state + ability_overlay.anchored = TRUE + ability_overlay.set_density(FALSE) + QDEL_IN(ability_overlay, overlay_lifespan) + +/decl/ability/proc/apply_effect_to(mob/user, atom/target, list/metadata) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + apply_visuals(user, target, metadata) + +/decl/ability/proc/get_default_metadata() + . = list() + if(ability_cooldown_time) + .["next_cast"] = 0 + if(max_charge) + .["charges"] = max_charge + .["next_charge"] = 0 + +/decl/ability/proc/recharge(mob/owner, list/metadata) + if(max_charge <= 0 || metadata["charges"] >= max_charge) + return FALSE + if(world.time < metadata["next_charge"]) + return TRUE + metadata["next_charge"] = world.time + charge_delay + metadata["charges"]++ + return TRUE + +/decl/ability/proc/get_stat_strings(list/metadata) + var/use_name = metadata["ability_name"] || name + if(ability_cooldown_time) + var/on_cooldown = metadata["next_cast"] - world.time + if(on_cooldown > 0) + return list( + use_name, + "[ceil(on_cooldown/10)]s" + ) + if(max_charge) + return list( + use_name, + "[metadata["charges"]]/[max_charge]" + ) + +/decl/ability/ranged + abstract_type = /decl/ability/ranged + projectile_type = /obj/item/projectile/ability + is_ranged_invocation = TRUE + prep_cast = TRUE \ No newline at end of file diff --git a/code/datums/extensions/abilities/ability_handler.dm b/code/datums/extensions/abilities/ability_handler.dm index 8dc7ee1e5de..d215868a4c5 100644 --- a/code/datums/extensions/abilities/ability_handler.dm +++ b/code/datums/extensions/abilities/ability_handler.dm @@ -1,8 +1,17 @@ /datum/ability_handler abstract_type = /datum/ability_handler + var/showing_abilities = FALSE var/mob/owner - var/list/ability_items var/datum/extension/abilities/master + var/list/ability_items + var/list/screen_elements + var/list/known_abilities + var/list/recharging_abilities + var/stat_panel_type = "Abilities" + /// UI element for showing or hiding this ability category. Should be /obj/screen/ability/category or subtype. + var/category_toggle_type = /obj/screen/ability/category + var/decl/ability/prepared_ability + var/next_channel = 0 /datum/ability_handler/New(_master) master = _master @@ -12,16 +21,179 @@ if(!istype(owner)) CRASH("Ability handler received invalid owner!") ..() + refresh_login() + +/datum/ability_handler/Process() + + if(!length(recharging_abilities)) + return PROCESS_KILL + + for(var/decl/ability/ability as anything in recharging_abilities) + if(!ability.recharge(owner, get_metadata(ability))) + LAZYREMOVE(recharging_abilities, ability) + +/datum/ability_handler/proc/get_metadata(decl/ability/ability, create_if_missing = TRUE) + if(istype(ability)) + . = known_abilities[ability.type] + if(!islist(.) && create_if_missing) + . = ability.get_default_metadata() + known_abilities[ability.type] = . + else if(ispath(ability, /decl/ability)) + . = known_abilities[ability] + if(!islist(.) && create_if_missing) + ability = GET_DECL(ability) + if(!istype(ability)) + return list() + . = ability.get_default_metadata() + known_abilities[ability] = . + else if(create_if_missing) + PRINT_STACK_TRACE("ability metadata retrieval passed invalid ability type: '[ability]'") + . = list() /datum/ability_handler/Destroy() + recharging_abilities = null + known_abilities = null QDEL_NULL_LIST(ability_items) + + for(var/ability in screen_elements) + var/obj/element = screen_elements[ability] + if(istype(element)) + qdel(element) + screen_elements = null + if(master) LAZYREMOVE(master.ability_handlers, src) master.update() master = null owner = null + if(is_processing) + STOP_PROCESSING(SSprocessing, src) return ..() +/datum/ability_handler/proc/add_ability(ability_type, list/metadata) + if(provides_ability(ability_type)) + return FALSE + var/decl/ability/ability = GET_DECL(ability_type) + if(!istype(ability)) + return FALSE + if(islist(metadata)) + metadata = metadata.Copy() // Prevent mutating during copy from other mobs. + else + metadata = ability.get_default_metadata() // Always unique, no copy needed. + + if(ability.ui_element_type && !istype(LAZYACCESS(screen_elements, ability), ability.ui_element_type)) + var/existing = screen_elements[ability] + if(existing) + remove_screen_element(existing, ability, FALSE) + var/obj/screen/ability/button/button = new ability.ui_element_type(null, owner, null, null, null, null, null) + button.set_ability(ability) + add_screen_element(button, ability, TRUE) + + LAZYSET(known_abilities, ability_type, metadata) + if(ability.max_charge) + LAZYDISTINCTADD(recharging_abilities, ability_type) + if(!is_processing) + START_PROCESSING(SSprocessing, src) + return TRUE + +/datum/ability_handler/proc/remove_ability(ability_type) + if(!provides_ability(ability_type)) + return FALSE + LAZYREMOVE(known_abilities, ability_type) + LAZYREMOVE(recharging_abilities, ability_type) + + var/decl/ability/ability = GET_DECL(ability_type) + if(ability?.ui_element_type) + var/obj/screen/existing_button = LAZYACCESS(screen_elements, ability) + if(istype(existing_button)) + remove_screen_element(existing_button, ability) + + if(!LAZYLEN(recharging_abilities) && is_processing) + STOP_PROCESSING(SSprocessing, src) + + if(!LAZYLEN(known_abilities)) + owner.remove_ability_handler(type) + + return TRUE + +/datum/ability_handler/proc/provides_ability(ability_type) + return (ability_type in known_abilities) + +/datum/ability_handler/proc/finalize_ability_handler() + if(category_toggle_type) + var/obj/screen/ability/category/category_toggle = new category_toggle_type(null, null, null, null, null, null, null) + add_screen_element(category_toggle, "toggle", TRUE) + toggle_category_visibility(TRUE) + +/datum/ability_handler/proc/refresh_element_positioning(row = 1, col = 1) + if(!LAZYLEN(screen_elements)) + return 0 + var/button_pos = col + var/button_row = row + . = 1 + for(var/ability in screen_elements) + var/obj/screen/element = screen_elements[ability] + if(istype(element, /obj/screen/ability/category)) + element.screen_loc = "RIGHT-[col]:-4,TOP-[row]" + else if(!element.invisibility) + button_pos++ + if((button_pos-col) > 5) + button_row++ + .++ + button_pos = col+1 + element.screen_loc = "RIGHT-[button_pos]:-4,TOP-[button_row]" + +/datum/ability_handler/proc/toggle_category_visibility(force_state) + showing_abilities = isnull(force_state) ? !showing_abilities : force_state + update_screen_elements() + if(master) + master.refresh_element_positioning() + +/datum/ability_handler/proc/update_screen_elements() + for(var/ability in screen_elements) + var/obj/screen/ability/ability_button = screen_elements[ability] + ability_button.update_icon() + +/datum/ability_handler/proc/copy_abilities_to(mob/living/target) + for(var/decl/ability/ability as anything in known_abilities) + if(!ability.copy_with_mind) + continue + if(target.add_ability(ability, get_metadata(ability, create_if_missing = FALSE))) + . = TRUE + +/datum/ability_handler/proc/disable_abilities(amount) + for(var/ability in known_abilities) + var/list/metadata = get_metadata(ability) + metadata["disabled"] = max(metadata["disabled"], (world.time + amount)) + +/datum/ability_handler/proc/enable_abilities(amount) + for(var/ability in known_abilities) + var/list/metadata = get_metadata(ability) + metadata["disabled"] = 0 + +/datum/ability_handler/proc/add_screen_element(atom/element, decl/ability/ability, update_positions = TRUE) + if(isnull(ability) || isnum(ability)) + return + LAZYSET(screen_elements, ability, element) + owner?.client?.screen |= element + if(istype(element, /obj/screen/ability)) + var/obj/screen/ability/ability_button = element + ability_button.owning_handler = src + if(update_positions && master && length(screen_elements)) + master.refresh_element_positioning() + +/datum/ability_handler/proc/remove_screen_element(atom/element, decl/ability/ability, update_positions = TRUE) + if(isnull(ability) || isnum(ability)) + return + LAZYREMOVE(screen_elements, ability) + owner?.client?.screen -= element + if(istype(element, /obj/screen/ability)) + var/obj/screen/ability/ability_button = element + if(ability_button.owning_handler == src) + ability_button.owning_handler = null + if(update_positions && master && LAZYLEN(screen_elements)) + master.refresh_element_positioning() + /datum/ability_handler/proc/cancel() if(LAZYLEN(ability_items)) for(var/thing in ability_items) @@ -29,9 +201,48 @@ qdel(thing) ability_items = null +/datum/ability_handler/proc/show_stat_string(mob/user) + if(!stat_panel_type || !statpanel(stat_panel_type)) + return + for(var/ability_type in known_abilities) + var/decl/ability/ability = GET_DECL(ability_type) + var/list/stat_strings = ability.get_stat_strings(get_metadata(ability)) + if(length(stat_strings) >= 2) + stat(stat_strings[1], stat_strings[2]) + /// Individual ability methods/disciplines (psioncs, etc.) so that mobs can have multiple. /datum/ability_handler/proc/refresh_login() - return + SHOULD_CALL_PARENT(TRUE) + if(LAZYLEN(screen_elements)) + var/list/add_elements = list() + for(var/ability in screen_elements) + var/atom/element = screen_elements[ability] + if(istype(element)) + add_elements |= element + if(length(add_elements)) + owner?.client?.screen |= add_elements + +/datum/ability_handler/proc/cancel_prepared_ability() + if(!prepared_ability) + return FALSE + if(prepared_ability.cancel_ability_1p_str) + to_chat(owner, capitalize(emote_replace_user_tokens(prepared_ability.cancel_ability_1p_str), owner)) + var/obj/screen/ability/button/button = LAZYACCESS(screen_elements, prepared_ability) + prepared_ability = null + if(istype(button)) + button.update_icon() + return TRUE + +/datum/ability_handler/proc/prepare_ability(decl/ability/ability) + if(prepared_ability && !cancel_prepared_ability()) + return FALSE + prepared_ability = ability + if(ability.ready_ability_1p_str) + to_chat(owner, capitalize(emote_replace_user_tokens(ability.ready_ability_1p_str), owner)) + var/obj/screen/ability/button/button = LAZYACCESS(screen_elements, ability) + if(istype(button)) + button.update_icon() + return TRUE /datum/ability_handler/proc/can_do_self_invocation(mob/user) return FALSE @@ -46,13 +257,23 @@ return FALSE /datum/ability_handler/proc/can_do_melee_invocation(mob/user, atom/target) - return FALSE + SHOULD_CALL_PARENT(TRUE) + return prepared_ability ? prepared_ability.is_melee_invocation : FALSE /datum/ability_handler/proc/do_melee_invocation(mob/user, atom/target) + SHOULD_CALL_PARENT(TRUE) + if(prepared_ability) + prepared_ability.use_ability(user, target, src) + return TRUE return FALSE /datum/ability_handler/proc/can_do_ranged_invocation(mob/user, atom/target) - return FALSE + SHOULD_CALL_PARENT(TRUE) + return prepared_ability ? prepared_ability.is_ranged_invocation : FALSE /datum/ability_handler/proc/do_ranged_invocation(mob/user, atom/target) + SHOULD_CALL_PARENT(TRUE) + if(prepared_ability) + prepared_ability.use_ability(user, target, src) + return TRUE return FALSE diff --git a/code/datums/extensions/abilities/ability_item.dm b/code/datums/extensions/abilities/ability_item.dm index 98bb2b8d43b..b4838fd4145 100644 --- a/code/datums/extensions/abilities/ability_item.dm +++ b/code/datums/extensions/abilities/ability_item.dm @@ -1,26 +1,37 @@ /obj/item/ability - simulated = 1 - obj_flags = OBJ_FLAG_NO_STORAGE - anchored = TRUE + simulated = FALSE + icon = 'icons/mob/screen/ability_inhand.dmi' + obj_flags = OBJ_FLAG_NO_STORAGE + anchored = TRUE pickup_sound = null drop_sound = null equip_sound = null is_spawnable_type = FALSE abstract_type = /obj/item/ability - var/mob/living/owner + var/decl/ability/ability + var/weakref/owner_ref var/handler_type -/obj/item/ability/Initialize() - owner = loc - if(!istype(owner)) +/obj/item/ability/Initialize(ml, decl/ability/_ability) + var/mob/living/owner = loc + var/datum/ability_handler/handler = istype(owner) && owner.get_ability_handler(handler_type, FALSE) + if(!istype(handler)) return INITIALIZE_HINT_QDEL - return ..() + if(_ability) + ability = _ability + if(!istype(ability)) + return INITIALIZE_HINT_QDEL + owner_ref = weakref(owner) + LAZYDISTINCTADD(handler.ability_items, src) + . = ..() + owner.put_in_hands(src) /obj/item/ability/Destroy() + var/mob/living/owner = owner_ref?.resolve() var/datum/ability_handler/handler = istype(owner) && owner.get_ability_handler(handler_type, FALSE) - if(handler) + if(istype(handler)) LAZYREMOVE(handler.ability_items, src) - . = ..() + return ..() /obj/item/ability/dropped() ..() @@ -29,3 +40,23 @@ /obj/item/ability/attack_self(var/mob/user) user?.drop_from_inventory(src) return TRUE + +/obj/item/ability/use_on_mob(mob/living/target, mob/living/user, animate) + return FALSE + +/obj/item/ability/afterattack(atom/target, mob/user, proximity) + if(QDELETED(src) || !istype(ability)) + return TRUE + var/list/metadata = ability.get_metadata_for(user) + if(!islist(metadata)) + return TRUE + if(ability.projectile_type) + // Fire a projectile if that is how this ability works. + ability.fire_projectile_at(user, target, metadata) + else + // Otherwise, apply to the target. Range checking etc. will be handled in apply_effect(). + ability.apply_effect(user, target, metadata) + + // Clean up our item if needed. + if(ability.item_end_on_cast) + qdel(src) diff --git a/code/datums/extensions/abilities/ability_projectile.dm b/code/datums/extensions/abilities/ability_projectile.dm new file mode 100644 index 00000000000..896acba2e95 --- /dev/null +++ b/code/datums/extensions/abilities/ability_projectile.dm @@ -0,0 +1,31 @@ +/obj/item/projectile/ability + name = "ability" + // The projectile is functionally a tracer, the ability deals the damage. + nodamage = TRUE + penetrating = FALSE + + /// Default; can be set by the ability. + life_span = 1 SECOND + + var/expended = FALSE + var/mob/owner + var/list/ability_metadata + var/decl/ability/carried_ability + +/obj/item/projectile/ability/Destroy() + owner = null + carried_ability = null + return ..() + +/obj/item/projectile/ability/explosion_act() + SHOULD_CALL_PARENT(FALSE) + +/obj/item/projectile/ability/Bump(var/atom/A, forced=0) + if(loc && carried_ability && !expended) + carried_ability.apply_effect(owner, A, ability_metadata, src) + return TRUE + +/obj/item/projectile/ability/on_impact(var/atom/A) + if(loc && carried_ability && !expended) + carried_ability.apply_effect(owner, A, ability_metadata, src) + return TRUE diff --git a/code/datums/extensions/abilities/ability_targeting.dm b/code/datums/extensions/abilities/ability_targeting.dm new file mode 100644 index 00000000000..52f7ee8166c --- /dev/null +++ b/code/datums/extensions/abilities/ability_targeting.dm @@ -0,0 +1,91 @@ +/decl/ability_targeting + abstract_type = /decl/ability_targeting + /// If set, this ability is applied to a square of this radius. + var/effect_radius = 0 + /// Set to except the inner space of the spell from target checks. + var/effect_inner_radius = -1 + /// If set, user will be excepted from targets. + var/user_is_immune = FALSE + /// If set, this ability will never target our faction. + var/faction_immune = FALSE + /// If set, this ability will only target our faction. + var/faction_only = FALSE + /// If set, this ability will resolve targets to turfs before doing any assessment or targetting. + var/target_turf = TRUE + /// If set along with target turf type, will include dense turfs. + var/ignore_dense_turfs = TRUE + /// If set along target turf type, will include space turfs. + var/ignore_space_turfs = FALSE + +/decl/ability_targeting/proc/get_effect_radius(mob/user, atom/hit_target, list/metadata) + return effect_radius + +/// This proc exists so that subtypes can override it and take advantage of the speed benefits of `for(var/mob in range())` and similar optimizations. +/// It should ONLY ever be called in get_affected(). +/decl/ability_targeting/proc/get_affected_atoms(atom/center, new_effect_radius) + PROTECTED_PROC(TRUE) + . = list() + for(var/atom/target in range(center, new_effect_radius)) + . += target + +/decl/ability_targeting/proc/get_affected(mob/user, atom/hit_target, list/metadata, decl/ability/ability, obj/item/projectile/ability/projectile) + if(effect_radius <= 0) + return validate_target(user, hit_target, metadata, ability) ? list(hit_target) : null + var/atom/center = projectile || hit_target + var/new_effect_radius = get_effect_radius(user, hit_target, metadata) + var/list/except_atoms = effect_inner_radius >= 0 ? range(effect_inner_radius, center) : null + var/list/target_candidates = get_affected_atoms(center, new_effect_radius) + for(var/atom/target in except_atoms ? target_candidates - except_atoms : target_candidates) // sloooooow... + if(validate_target(user, target, metadata, ability)) + LAZYADD(., target) + +/decl/ability_targeting/proc/resolve_initial_target(atom/target) + if(target_turf) + return get_turf(target) + return target + +/decl/ability_targeting/proc/validate_initial_target(mob/user, atom/target, list/metadata, decl/ability/ability) + return validate_target(user, target, metadata, ability) + +/decl/ability_targeting/proc/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) + if(target == user && !user_is_immune) + return FALSE + if(target_turf && !isturf(target)) + return FALSE + if(user.faction) + if(faction_immune && ismob(target)) + var/mob/target_mob = target + if(target_mob.faction == user.faction) + return FALSE + if(faction_only) + if(!ismob(target)) + return FALSE + var/mob/target_mob = target + if(target_mob.faction != user.faction) + return FALSE + else if(faction_only) + return FALSE + if(isturf(target)) + if(ignore_dense_turfs && target.density) + return FALSE + if(ignore_space_turfs && istype(target, /turf/space)) + return FALSE + return TRUE + +/decl/ability_targeting/clear_turf + ignore_dense_turfs = TRUE + +/decl/ability_targeting/clear_turf/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) + . = ..() && isturf(target) + if(.) + var/turf/target_turf = target + return !target_turf.contains_dense_objects(user) + +/decl/ability_targeting/living_mob + target_turf = FALSE + +/decl/ability_targeting/living_mob/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) + . = ..() && isliving(target) + if(.) + var/mob/living/victim = target + return victim.simulated diff --git a/code/datums/extensions/abilities/readme.dm b/code/datums/extensions/abilities/readme.dm new file mode 100644 index 00000000000..b7e17f8d59e --- /dev/null +++ b/code/datums/extensions/abilities/readme.dm @@ -0,0 +1,26 @@ +/* + + ABILITY DECL SYSTEM NOTES + + - Mobs have an extension, /datum/extension/abilities + - This extension has a list of associated handlers, /datum/ability_handler + - The handlers have a list of associated ability decls, /decl/ability, which are indexes for associative metadata lists. + - The abilities have an associated targeting handler, /decl/ability_targeting, which handles single target, turf target, AOE, etc. + - Handlers are added/removed with mob.add_ability_handler(handler_type) and mob.remove_ability_handler(handler_type) + - The extension will be added to the mob automatically when adding a handler, and removed if the last handler is removed. + - Abilities are added/removed with mob.add_ability(ability_type, preset metadata if any) and mob.remove_ability(ability_type) + - Handlers for abilities will be inferred from the /decl and added to the mob automatically. + - Metadata is retrieved with handler.get_metadata(ability type or instance) + + - Upon invocation, an ability will: + - retrieve handler and metadata from the user mob + - validate the handler/metadata/user against whatever requirements the ability has + - resolve the initial click target to the appropriate target for the ability (turf under the clicked target for example) + - check any additional requirements like charges, cooldowns, etc. + - if a projectile ability, spawn and launch a projectile that will carry the ability and metadata to the destination target. + - apply the ability to the destination target + - while applying the ability, the targeting decl will be used to grab all applicable targets at or near the point of use (projectile hit or clicked target) + - the ability effects will then get applied (fire, ice, explosion, so on) + - the ability will then set cooldown as appropriate in metadata, deduct charges, etc + +*/ \ No newline at end of file diff --git a/code/datums/extensions/holster/holster.dm b/code/datums/extensions/holster/holster.dm index 8792a616b5a..569ca88bdb1 100644 --- a/code/datums/extensions/holster/holster.dm +++ b/code/datums/extensions/holster/holster.dm @@ -111,7 +111,7 @@ to_chat(user, "It is empty.") /datum/extension/holster/proc/check_holster() - if(holstered.loc != storage) + if(holstered.loc != storage.holder) clear_holster() /atom/proc/holster_verb(var/holster_name in get_holsters()) @@ -154,4 +154,63 @@ else for(var/i = 1 to holster_accessories.len) var/holster_name = "[accessory_name] [i]" - .[holster_name] = get_extension(holster_accessories[i], /datum/extension/holster) \ No newline at end of file + .[holster_name] = get_extension(holster_accessories[i], /datum/extension/holster) + +// Basic unholster for an item at the top level. +/decl/interaction_handler/unholster + name = "Unholster" + +/decl/interaction_handler/unholster/is_possible(atom/target, mob/user, obj/item/prop) + . = ..() && !prop + if(.) + var/datum/extension/holster/holster = get_extension(target, /datum/extension/holster) + return !!holster?.holstered + +/decl/interaction_handler/unholster/invoked(atom/target, mob/user, obj/item/prop) + var/datum/extension/holster/holster = get_extension(target, /datum/extension/holster) + return holster?.unholster(user, avoid_intent = TRUE) + +// Interaction procs for getting this interaction for basic items. +/obj/item/get_quick_interaction_handler(mob/user) + if(!(. = ..())) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + if(holster?.holstered) + return GET_DECL(/decl/interaction_handler/unholster) + +// More complex version of the above that iterates clothing accessories. +/decl/interaction_handler/unholster_accessory + name = "Unholster From Accessory" + expected_target_type = /obj/item/clothing + +/decl/interaction_handler/unholster_accessory/is_possible(atom/target, mob/user, obj/item/prop) + . = ..() && !prop + if(.) + var/obj/item/clothing/clothes = target + for(var/obj/item/thing in clothes.accessories) + var/datum/extension/holster/holster = get_extension(thing, /datum/extension/holster) + if(holster?.holstered) + return TRUE + return FALSE + +/decl/interaction_handler/unholster_accessory/invoked(atom/target, mob/user, obj/item/prop) + var/obj/item/clothing/clothes = target + for(var/obj/item/thing in clothes.accessories) + var/datum/extension/holster/holster = get_extension(thing, /datum/extension/holster) + if(holster?.unholster(user, avoid_intent = TRUE)) + return TRUE + return FALSE + +// Interaction procs for getting this interaction for clothing accessories. +/obj/item/clothing/get_alt_interactions(mob/user) + . = ..() + for(var/obj/item/thing in accessories) + var/datum/extension/holster/holster = get_extension(thing, /datum/extension/holster) + if(holster?.holstered) + LAZYADD(., GET_DECL(/decl/interaction_handler/unholster_accessory)) + +/obj/item/clothing/get_quick_interaction_handler(mob/user) + if(!(. = ..())) + for(var/obj/item/thing in accessories) + var/datum/extension/holster/holster = get_extension(thing, /datum/extension/holster) + if(holster?.holstered) + return GET_DECL(/decl/interaction_handler/unholster_accessory) diff --git a/code/datums/extensions/state_machine.dm b/code/datums/extensions/state_machine.dm index 63e58f0dd67..edba84c7965 100644 --- a/code/datums/extensions/state_machine.dm +++ b/code/datums/extensions/state_machine.dm @@ -3,16 +3,15 @@ var/global/list/state_machines = list() /proc/get_state_machine(var/datum/holder, var/base_type) if(istype(holder) && base_type && holder.has_state_machine) - var/list/machines = global.state_machines["\ref[holder]"] + var/list/machines = global.state_machines[holder] return islist(machines) && machines[base_type] /proc/add_state_machine(var/datum/holder, var/datum/state_machine/fsm_type) if(istype(holder) && fsm_type) - var/holder_ref = "\ref[holder]" - var/list/machines = global.state_machines[holder_ref] + var/list/machines = global.state_machines[holder] if(!islist(machines)) machines = list() - global.state_machines[holder_ref] = machines + global.state_machines[holder] = machines var/base_type = fsm_type::base_type if(!machines[base_type]) var/datum/state_machine/machine = new fsm_type(holder) @@ -22,12 +21,11 @@ var/global/list/state_machines = list() /proc/remove_state_machine(var/datum/holder, var/base_type) if(istype(holder) && base_type && holder.has_state_machine) - var/holder_ref = "\ref[holder]" - var/list/machines = global.state_machines[holder_ref] + var/list/machines = global.state_machines[holder] if(length(machines)) machines -= base_type if(!length(machines)) - global.state_machines -= holder_ref + global.state_machines -= holder holder.has_state_machine = FALSE return TRUE return FALSE diff --git a/code/datums/extensions/storage/_storage.dm b/code/datums/extensions/storage/_storage.dm index c79e91a7357..4ff70967e1c 100644 --- a/code/datums/extensions/storage/_storage.dm +++ b/code/datums/extensions/storage/_storage.dm @@ -252,6 +252,8 @@ var/global/list/_test_storage_items = list() storage_ui?.on_insertion() /datum/storage/proc/update_ui_after_item_removal(obj/item/removed) + if(QDELETED(holder)) + return prepare_ui() storage_ui?.on_post_remove() diff --git a/code/datums/extensions/storage/_storage_ui.dm b/code/datums/extensions/storage/_storage_ui.dm index 6f4db924f32..a3e1f23bfcb 100644 --- a/code/datums/extensions/storage/_storage_ui.dm +++ b/code/datums/extensions/storage/_storage_ui.dm @@ -216,7 +216,8 @@ closer.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST + cols + 1]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" /datum/storage_ui/default/proc/space_orient_objs() - + if(QDELETED(_storage?.holder)) // don't bother if we've been deleted + return var/baseline_max_storage_space = DEFAULT_BOX_STORAGE //storage size corresponding to 224 pixels var/storage_cap_width = 2 //length of sprite for start and end of the box representing total storage space var/stored_cap_width = 4 //length of sprite for start and end of the box representing the stored item diff --git a/code/datums/extensions/storage/subtypes_mre.dm b/code/datums/extensions/storage/subtypes_mre.dm index 5b7f4910716..b5045c1fc1b 100644 --- a/code/datums/extensions/storage/subtypes_mre.dm +++ b/code/datums/extensions/storage/subtypes_mre.dm @@ -5,8 +5,11 @@ open_sound = 'sound/effects/rip1.ogg' /datum/storage/mre/open(mob/user) - if(!opened) - to_chat(user, "You tear open the bag, breaking the vacuum seal.") + var/obj/item/mre/mre = holder + if(istype(mre) && !mre.has_been_opened) + to_chat(user, SPAN_NOTICE("You tear open the bag, breaking the vacuum seal.")) + mre.has_been_opened = TRUE + mre.update_icon() . = ..() /datum/storage/mrebag diff --git a/code/datums/graph/graph.dm b/code/datums/graph/graph.dm index 249f513e419..1610a6b4397 100644 --- a/code/datums/graph/graph.dm +++ b/code/datums/graph/graph.dm @@ -35,6 +35,8 @@ /datum/graph/proc/Connect(var/datum/node/node, var/list/neighbours, var/queue = TRUE) SHOULD_NOT_SLEEP(TRUE) SHOULD_NOT_OVERRIDE(TRUE) + if(QDELETED(src)) + CRASH("Attempted to connect node [node] to a qdeleted graph!") if(!istype(neighbours)) neighbours = list(neighbours) if(!length(neighbours)) @@ -90,8 +92,7 @@ if(neighbours_to_disconnect) neighbours |= neighbours_to_disconnect - if(neighbours.len) - LAZYSET(pending_disconnections, node, neighbours) + LAZYSET(pending_disconnections, node, neighbours) if(queue) SSgraphs_update.Queue(src) @@ -193,8 +194,6 @@ neighbour_edges |= N LAZYCLEARLIST(pending_connections) - if(!LAZYLEN(pending_disconnections)) - return for(var/pending_node_disconnect in pending_disconnections) var/pending_edge_disconnects = pending_disconnections[pending_node_disconnect] @@ -211,7 +210,8 @@ if(!length(other_pending_edge_disconnects)) pending_disconnections -= connected_node - edges[pending_node_disconnect] -= pending_edge_disconnects + if(edges[pending_node_disconnect]) + edges[pending_node_disconnect] -= pending_edge_disconnects if(!length(edges[pending_node_disconnect])) edges -= pending_node_disconnect @@ -225,12 +225,15 @@ var/checked_nodes = list() var/list/nodes_to_traverse = list(root_node) while(length(nodes_to_traverse)) - var/node_to_check = nodes_to_traverse[nodes_to_traverse.len] + var/datum/node/node_to_check = nodes_to_traverse[nodes_to_traverse.len] nodes_to_traverse.len-- + if(QDELETED(node_to_check)) + continue checked_nodes += node_to_check nodes_to_traverse |= ((edges[node_to_check] || list()) - checked_nodes) - all_nodes -= checked_nodes - subgraphs[++subgraphs.len] = checked_nodes + if(length(checked_nodes)) + all_nodes -= checked_nodes + subgraphs[++subgraphs.len] = checked_nodes if(length(subgraphs) == 1) if(!length(nodes)) diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index cb9ad6c9539..1db34888001 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -75,7 +75,6 @@ /datum/mind/proc/handle_mob_deletion(mob/living/deleted_mob) if (current == deleted_mob) current = null - /datum/mind/proc/transfer_to(mob/living/new_character) if(!istype(new_character)) to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") @@ -83,6 +82,8 @@ if(current?.mind == src) current.mind = null SSnano.user_transferred(current, new_character) // transfer active NanoUI instances to new user + if(istype(current)) // exclude new_players and observers + current.copy_abilities_to(new_character) if(new_character.mind) //remove any mind currently in our new body's mind variable new_character.mind.current = null @@ -91,9 +92,6 @@ current = new_character //link ourself to our new body new_character.mind = src //and link our new body to ourself - if(learned_spells && learned_spells.len) - restore_spells(new_character) - if(active) new_character.key = key //now transfer the key to link the client to our new body diff --git a/code/datums/repositories/follow.dm b/code/datums/repositories/follow.dm index 2a71b7b3579..fb12909997c 100644 --- a/code/datums/repositories/follow.dm +++ b/code/datums/repositories/follow.dm @@ -216,10 +216,6 @@ var/global/repository/follow/follow_repository = new() followed_type = /obj/effect/blob/core suffix = "Blob" -/datum/follow_holder/supermatter - sort_order = 10 - followed_type = /obj/machinery/power/supermatter - /datum/follow_holder/singularity sort_order = 10 followed_type = /obj/effect/singularity diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm index 3c771acd1e4..e7ec3c1645b 100644 --- a/code/datums/supplypacks/engineering.dm +++ b/code/datums/supplypacks/engineering.dm @@ -155,13 +155,6 @@ containername = "shield generator construction kit crate" access = access_engine -/decl/hierarchy/supply_pack/engineering/smbig - name = "Power - Supermatter core" - contains = list(/obj/machinery/power/supermatter) - containertype = /obj/structure/closet/crate/secure/large/supermatter - containername = "\improper Supermatter crate (CAUTION)" - access = access_ce - /decl/hierarchy/supply_pack/engineering/robotics name = "Parts - Robotics assembly" contains = list(/obj/item/assembly/prox_sensor = 3, diff --git a/code/datums/supplypacks/supplypack.dm b/code/datums/supplypacks/supplypack.dm index 159c8ae31e7..1be6469edc5 100644 --- a/code/datums/supplypacks/supplypack.dm +++ b/code/datums/supplypacks/supplypack.dm @@ -24,9 +24,7 @@ var/global/list/cargoprices = list() cost = 0 for(var/entry in contains) cost += atom_info_repository.get_combined_worth_for(entry) * max(1, contains[entry]) - var/container_value = containertype ? atom_info_repository.get_single_worth_for(containertype) : 0 - if(container_value) - cost += atom_info_repository.get_single_worth_for(containertype) + cost += containertype ? atom_info_repository.get_single_worth_for(containertype) : 0 cost = max(1, NONUNIT_CEILING((cost * WORTH_TO_SUPPLY_POINTS_CONSTANT * SSsupply.price_markup), WORTH_TO_SUPPLY_POINTS_ROUND_CONSTANT)) global.cargoprices[name] = cost diff --git a/code/datums/trading/traders/misc.dm b/code/datums/trading/traders/misc.dm index dd3e0df898f..6356be83f0d 100644 --- a/code/datums/trading/traders/misc.dm +++ b/code/datums/trading/traders/misc.dm @@ -155,7 +155,7 @@ ) possible_trading_items = list( /obj/item/clothing/head/wizard/magus = TRADER_THIS_TYPE, - /obj/item/shield/buckler = TRADER_THIS_TYPE, + /obj/item/shield/crafted/buckler = TRADER_THIS_TYPE, /obj/item/clothing/head/redcoat = TRADER_THIS_TYPE, /obj/item/clothing/head/powdered_wig = TRADER_THIS_TYPE, /obj/item/clothing/head/hasturhood = TRADER_THIS_TYPE, diff --git a/code/datums/trading/traders/unique.dm b/code/datums/trading/traders/unique.dm index e7199504921..dd30cd3b4be 100644 --- a/code/datums/trading/traders/unique.dm +++ b/code/datums/trading/traders/unique.dm @@ -68,7 +68,6 @@ /obj/item/stack/material/ore = TRADER_ALL ) possible_trading_items = list( - /obj/machinery/power/supermatter = TRADER_ALL, /obj/item/aiModule = TRADER_SUBTYPES_ONLY ) want_multiplier = 5000 diff --git a/code/datums/traits/maluses/amputations.dm b/code/datums/traits/maluses/amputations.dm index 5cbd3ea7c77..8ce9bc5d418 100644 --- a/code/datums/traits/maluses/amputations.dm +++ b/code/datums/traits/maluses/amputations.dm @@ -15,8 +15,6 @@ if(trait_type == type) continue var/decl/trait/malus/amputation/trait = check_traits[trait_type] - if(!trait.name) - continue // remove when abstract decl handling from dev is merged for(var/limb in trait.apply_to_limbs) if(limb in ban_traits_relating_to_limbs) LAZYDISTINCTADD(incompatible_with, trait_type) diff --git a/code/datums/uplink/grenades.dm b/code/datums/uplink/grenades.dm index 2a20f984ebe..c91af43ad7d 100644 --- a/code/datums/uplink/grenades.dm +++ b/code/datums/uplink/grenades.dm @@ -80,17 +80,3 @@ item_cost = 40 antag_roles = list(/decl/special_role/mercenary) path = /obj/item/box/frags - -/datum/uplink_item/item/grenades/supermatter - name = "1x Supermatter Grenade" - desc = "This grenade contains a small supermatter shard which will delaminate upon activation and pull in nearby objects, irradiate lifeforms, and eventually explode." - item_cost = 15 - antag_roles = list(/decl/special_role/mercenary) - path = /obj/item/grenade/supermatter - -/datum/uplink_item/item/grenades/supermatters - name = "5x Supermatter Grenades" - desc = "These grenades contains a small supermatter shard which will delaminate upon activation and pull in nearby objects, irradiate lifeforms, and eventually explode." - item_cost = 60 - antag_roles = list(/decl/special_role/mercenary) - path = /obj/item/box/supermatters diff --git a/code/datums/vote/map.dm b/code/datums/vote/map.dm index 9e64ac63bcb..aabfdde2845 100644 --- a/code/datums/vote/map.dm +++ b/code/datums/vote/map.dm @@ -9,14 +9,14 @@ return ..() /datum/vote/map/setup_vote() - for(var/name in global.all_maps) + for(var/name in global.votable_maps) choices += name ..() /datum/vote/map/report_result() if(..()) return 1 - var/datum/map/M = global.all_maps[result[1]] + var/datum/map/M = global.votable_maps[result[1]] fdel("use_map") text2file(M.path, "use_map") diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm index 06c679edfd2..c984ebbcb77 100644 --- a/code/datums/wires/nuclearbomb.dm +++ b/code/datums/wires/nuclearbomb.dm @@ -20,7 +20,7 @@ var/global/const/NUCLEARBOMB_WIRE_SAFETY = 4 var/obj/machinery/nuclearbomb/N = holder . += ..() . += "
The device is [N.timing ? "shaking!" : "still."]
" - . += "The device is is [N.safety ? "quiet" : "whirring"].
" + . += "The device is [N.safety ? "quiet" : "whirring"].
" . += "The lights are [N.lighthack ? "static" : "functional"].
" /datum/wires/nuclearbomb/proc/toggle_hacked() diff --git a/code/datums/wires/smes.dm b/code/datums/wires/smes.dm index 15eff146d15..7cc90b2d664 100644 --- a/code/datums/wires/smes.dm +++ b/code/datums/wires/smes.dm @@ -9,68 +9,68 @@ new /datum/wire_description(SMES_WIRE_FAILSAFES, "This wire appears to connect to a failsafe mechanism.") ) -var/global/const/SMES_WIRE_RCON = 1 // Remote control (AI and consoles), cut to disable -var/global/const/SMES_WIRE_INPUT = 2 // Input wire, cut to disable input, pulse to disable for 60s -var/global/const/SMES_WIRE_OUTPUT = 4 // Output wire, cut to disable output, pulse to disable for 60s -var/global/const/SMES_WIRE_GROUNDING = 8 // Cut to quickly discharge causing sparks, pulse to only create few sparks -var/global/const/SMES_WIRE_FAILSAFES = 16 // Cut to disable failsafes, mend to reenable - - -/datum/wires/smes/CanUse(var/mob/living/L) - var/obj/machinery/power/smes/buildable/S = holder - if(!S.grounding && S.powernet && S.powernet.avail) - electrocute_mob(L, S.powernet, S, S.safeties_enabled? 0.1 : 1) - if(S.panel_open) - return 1 - return 0 +/// Remote control (AI and consoles), cut to disable +var/global/const/SMES_WIRE_RCON = BITFLAG(0) +/// Input wire, cut to disable input, pulse to disable for 60s +var/global/const/SMES_WIRE_INPUT = BITFLAG(1) +/// Output wire, cut to disable output, pulse to disable for 60s +var/global/const/SMES_WIRE_OUTPUT = BITFLAG(2) +/// Cut to quickly discharge causing sparks, pulse to only create few sparks +var/global/const/SMES_WIRE_GROUNDING = BITFLAG(3) +/// Cut to disable failsafes, mend to reenable +var/global/const/SMES_WIRE_FAILSAFES = BITFLAG(4) +/datum/wires/smes/CanUse(var/mob/living/user) + var/obj/machinery/power/smes/buildable/storage = holder + if(!storage.grounding && storage.powernet && storage.powernet.avail) + electrocute_mob(user, storage.powernet, storage, (storage.safeties_enabled? 0.1 : 1)) + return storage.panel_open /datum/wires/smes/GetInteractWindow(mob/user) - var/obj/machinery/power/smes/buildable/S = holder + var/obj/machinery/power/smes/buildable/storage = holder . += ..() - . += "The green light is [(S.input_cut || S.input_pulsed || S.output_cut || S.output_pulsed) ? "off" : "on"]
" - . += "The red light is [(S.safeties_enabled || S.grounding) ? "off" : "blinking"]
" - . += "The blue light is [S.RCon ? "on" : "off"]" - + . += "The green light is [(storage.input_cut || storage.input_pulsed || storage.output_cut || storage.output_pulsed) ? "off" : "on"]
" + . += "The red light is [(storage.safeties_enabled || storage.grounding) ? "off" : "blinking"]
" + . += "The blue light is [storage.RCon ? "on" : "off"]" /datum/wires/smes/UpdateCut(var/index, var/mended) - var/obj/machinery/power/smes/buildable/S = holder + var/obj/machinery/power/smes/buildable/storage = holder switch(index) if(SMES_WIRE_RCON) - S.RCon = mended + storage.RCon = mended if(SMES_WIRE_INPUT) - S.input_cut = !mended + storage.input_cut = !mended if(SMES_WIRE_OUTPUT) - S.output_cut = !mended + storage.output_cut = !mended if(SMES_WIRE_GROUNDING) - S.grounding = mended + storage.grounding = mended if(SMES_WIRE_FAILSAFES) - S.safeties_enabled = mended + storage.safeties_enabled = mended /datum/wires/smes/proc/reset_rcon() - var/obj/machinery/power/smes/buildable/S = holder - if(S) - S.RCon = TRUE + var/obj/machinery/power/smes/buildable/storage = holder + if(storage) + storage.RCon = TRUE /datum/wires/smes/proc/reset_safeties() - var/obj/machinery/power/smes/buildable/S = holder - if(S) - S.safeties_enabled = TRUE + var/obj/machinery/power/smes/buildable/storage = holder + if(storage) + storage.safeties_enabled = TRUE /datum/wires/smes/UpdatePulsed(var/index) - var/obj/machinery/power/smes/buildable/S = holder + var/obj/machinery/power/smes/buildable/storage = holder switch(index) if(SMES_WIRE_RCON) - if(S.RCon) - S.RCon = 0 + if(storage.RCon) + storage.RCon = 0 addtimer(CALLBACK(src, PROC_REF(reset_rcon)), 1 SECOND) if(SMES_WIRE_INPUT) - S.toggle_input() + storage.toggle_input() if(SMES_WIRE_OUTPUT) - S.toggle_output() + storage.toggle_output() if(SMES_WIRE_GROUNDING) - S.grounding = 0 + storage.grounding = 0 if(SMES_WIRE_FAILSAFES) - if(S.safeties_enabled) - S.safeties_enabled = 0 + if(storage.safeties_enabled) + storage.safeties_enabled = 0 addtimer(CALLBACK(src, PROC_REF(reset_safeties)), 1 SECOND) diff --git a/code/game/alpha_masks.dm b/code/game/alpha_masks.dm index 05b23f45eaa..3eb68a3b343 100644 --- a/code/game/alpha_masks.dm +++ b/code/game/alpha_masks.dm @@ -54,10 +54,14 @@ var/global/list/_alpha_masks = list() /atom/movable/proc/get_turf_alpha_mask_states() return 'icons/effects/alpha_mask.dmi' +/atom/movable/proc/should_have_alpha_mask() + // Mobs and obj both need to avoid this when on structures. Looks wonky. + return simulated && isturf(loc) && !(locate(/obj/structure) in loc) + // Proc called by /turf/Entered() to update a mob's mask overlay. /atom/movable/proc/update_turf_alpha_mask() set waitfor = FALSE - if(!simulated || updating_turf_alpha_mask) + if(!simulated || QDELETED(src) || updating_turf_alpha_mask) return updating_turf_alpha_mask = TRUE sleep(0) @@ -65,7 +69,7 @@ var/global/list/_alpha_masks = list() if(QDELETED(src)) return var/turf/our_turf = loc - var/mask_state = isturf(our_turf) && our_turf.get_movable_alpha_mask_state(src) + var/mask_state = isturf(our_turf) && should_have_alpha_mask() && our_turf.get_movable_alpha_mask_state(src) if(mask_state) var/atom/movable/alpha_mask/mask = get_or_create_alpha_mask(src) if(mask) diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm index be291b8ac25..042a32f12ee 100644 --- a/code/game/antagonist/antagonist.dm +++ b/code/game/antagonist/antagonist.dm @@ -19,6 +19,7 @@ // Visual references. var/antaghud_indicator = "hudsyndicate" // Used by the ghost antagHUD. var/antag_indicator // icon_state for icons/mob/mob.dm visual indicator. + var/antag_hud_icon = 'icons/screen/hud_antag.dmi' var/faction_indicator // See antag_indicator, but for factionalized people only. var/faction_invisible // Can members of the faction identify other antagonists? @@ -104,6 +105,17 @@ /decl/special_role/validate() . = ..() + + // Check for our antaghud icons. + if(faction_indicator || antag_indicator) + if(antag_hud_icon) + if(faction_indicator && !check_state_in_icon(faction_indicator, antag_hud_icon)) + . += "missing faction_indicator '[faction_indicator]' from icon 'antag_hud_icon]'" + if(antag_indicator && !check_state_in_icon(antag_indicator, antag_hud_icon)) + . += "missing antag_indicator '[antag_indicator]' from icon 'antag_hud_icon]'" + else + . += "missing antag_hud_icon" + // Grab initial in case it was already successfully loaded. var/initial_base_to_load = initial(base_to_load) if(isnull(initial_base_to_load)) diff --git a/code/game/antagonist/antagonist_update.dm b/code/game/antagonist/antagonist_update.dm index 64214125565..fcfd2c5fcdc 100644 --- a/code/game/antagonist/antagonist_update.dm +++ b/code/game/antagonist/antagonist_update.dm @@ -31,10 +31,12 @@ qdel(I) /decl/special_role/proc/get_indicator(var/datum/mind/recipient, var/datum/mind/other) - if(!antag_indicator || !other.current || !recipient.current) + if(!other.current || !recipient.current) return var/indicator = (faction_indicator && (other in faction_members)) ? faction_indicator : antag_indicator - var/image/I = image('icons/mob/hud.dmi', loc = other.current, icon_state = indicator, layer = ABOVE_HUMAN_LAYER) + if(!indicator) + return + var/image/I = image(antag_hud_icon, loc = other.current, icon_state = indicator, layer = ABOVE_HUMAN_LAYER) var/decl/bodytype/root_bodytype = other.current.get_bodytype() if(istype(root_bodytype)) I.pixel_x = root_bodytype.antaghud_offset_x diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 38163a445fd..b8c1eaa4e04 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -92,7 +92,15 @@ var/global/list/areas = list() area_blurb_category = type ..() +/area/proc/get_additional_fishing_results() + return + /area/Initialize() + var/list/additional_fishing_results = get_additional_fishing_results() + if(LAZYLEN(additional_fishing_results)) + LAZYINITLIST(fishing_results) + for(var/fish in additional_fishing_results) + fishing_results[fish] = additional_fishing_results[fish] . = ..() global.areas += src if(!requires_power || !apc) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 5dd985c6064..196f4cc44e5 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -145,10 +145,12 @@ /atom/proc/try_on_reagent_change() SHOULD_NOT_OVERRIDE(TRUE) set waitfor = FALSE - if(_reagent_update_started >= world.time) + if(QDELETED(src) ||_reagent_update_started >= world.time) return FALSE _reagent_update_started = world.time sleep(0) // Defer to end of tick so we don't drop subsequent reagent updates. + if(QDELETED(src)) + return return on_reagent_change() /atom/proc/on_reagent_change() @@ -293,6 +295,17 @@ to_chat(user, "[html_icon(src)] That's [f_name] [suffix]") to_chat(user, desc) + + var/list/alt_interactions = get_alt_interactions(user) + if(LAZYLEN(alt_interactions)) + var/list/interaction_strings = list() + for(var/interaction_type as anything in alt_interactions) + var/decl/interaction_handler/interaction = GET_DECL(interaction_type) + if(interaction.examine_desc && (interaction.always_show_on_examine || interaction.is_possible(src, user, user?.get_active_held_item()))) + interaction_strings += emote_replace_target_tokens(interaction.examine_desc, src) + if(length(interaction_strings)) + to_chat(user, SPAN_INFO("Alt-click on \the [src] to [english_list(interaction_strings, and_text = " or ")].")) + RAISE_EVENT(/decl/observ/atom_examined, src, user, distance) return TRUE @@ -500,10 +513,12 @@ - Returns: `TRUE` if qdel() was called, otherwise `FALSE` */ /atom/proc/lava_act() - visible_message(SPAN_DANGER("\The [src] sizzles and melts away, consumed by the lava!")) - playsound(src, 'sound/effects/flare.ogg', 100, 3) - qdel(src) - . = TRUE + if(simulated) + visible_message(SPAN_DANGER("\The [src] sizzles and melts away, consumed by the lava!")) + playsound(src, 'sound/effects/flare.ogg', 100, 3) + qdel(src) + return TRUE + return FALSE /** Handle this atom being hit by a thrown atom @@ -892,22 +907,6 @@ return check_loc check_loc = check_loc.loc -/** - Get a list of alt interactions for a user from this atom. - - - `user`: The mob that these alt interactions are for - - Return: A list containing the alt interactions -*/ -/atom/proc/get_alt_interactions(var/mob/user) - SHOULD_CALL_PARENT(TRUE) - RETURN_TYPE(/list) - . = list() - if(storage) - . += /decl/interaction_handler/storage_open - if(reagents?.total_volume && ATOM_IS_OPEN_CONTAINER(src)) - . += /decl/interaction_handler/wash_hands - . += /decl/interaction_handler/drink - /atom/proc/can_climb_from_below(var/mob/climber) return FALSE @@ -993,7 +992,16 @@ return /atom/proc/is_watertight() - return ATOM_IS_OPEN_CONTAINER(src) + return !ATOM_IS_OPEN_CONTAINER(src) /atom/proc/can_drink_from(mob/user) return ATOM_IS_OPEN_CONTAINER(src) && reagents?.total_volume && user.check_has_mouth() + +/atom/proc/adjust_required_attack_dexterity(mob/user, required_dexterity) + if(storage) // TODO: possibly check can_be_inserted() to avoid being able to shoot mirrors as a drake. + return DEXTERITY_HOLD_ITEM + return required_dexterity + +/atom/proc/immune_to_floor_hazards() + return !simulated + diff --git a/code/game/atoms_fluids.dm b/code/game/atoms_fluids.dm index 2984d5393f2..bf49a3469c0 100644 --- a/code/game/atoms_fluids.dm +++ b/code/game/atoms_fluids.dm @@ -16,7 +16,7 @@ /atom/proc/CanFluidPass(var/coming_from) return TRUE -/atom/movable/proc/is_fluid_pushable(var/amt) +/atom/movable/proc/try_fluid_push(volume, strength) return simulated && !anchored /atom/movable/is_flooded(var/lying_mob, var/absolute) @@ -47,7 +47,7 @@ // This override exists purely because throwing is movable-level and not atom-level, // for obvious reasons (that being that non-movable atoms cannot move). /atom/movable/submerged(depth, above_turf) - above_turf ||= !!throwing + above_turf ||= immune_to_floor_hazards() return ..() /obj/item/submerged(depth, above_turf) @@ -66,7 +66,7 @@ return ..() /mob/submerged(depth, above_turf) - above_turf ||= is_floating || !!throwing // check throwing here because of the table check coming before parent call + above_turf ||= immune_to_floor_hazards() // check throwing here because of the table check coming before parent call var/obj/structure/table/standing_on = locate(/obj/structure/table) in loc // can't stand on a table if we're floating if(!above_turf && standing_on && standing_on.mob_offset > 0) // standing atop a table that is a meaningful amount above the ground (not a bench) diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm index 3a2f3b619c1..eb08297e2ab 100644 --- a/code/game/atoms_init.dm +++ b/code/game/atoms_init.dm @@ -16,18 +16,15 @@ var/do_initialize = SSatoms.atom_init_stage var/list/created = SSatoms.created_atoms - if(do_initialize > INITIALIZATION_INSSATOMS_LATE) + if(do_initialize > INITIALIZATION_INSSATOMS) args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD if(SSatoms.InitAtom(src, args)) //we were deleted return - else if(created) - var/list/argument_list - if(length(args) > 1) - argument_list = args.Copy(2) - if(argument_list || do_initialize == INITIALIZATION_INSSATOMS_LATE) - created[src] = argument_list - + else if(length(args) > 1) + created[++created.len] = list(src, args.Copy(2)) + else + created[++created.len] = list(src, null) if(atom_flags & ATOM_FLAG_CLIMBABLE) verbs += /atom/proc/climb_on @@ -80,7 +77,7 @@ /atom/Destroy() // must be done before deletion // TODO: ADD PRE_DELETION OBSERVATION - if(isatom(loc) && loc.storage) + if(isatom(loc) && loc.storage && !QDELETED(loc.storage)) loc.storage.on_item_pre_deletion(src) UNQUEUE_TEMPERATURE_ATOM(src) QDEL_NULL(reagents) @@ -94,7 +91,7 @@ QDEL_NULL(atom_codex_ref) var/atom/oldloc = loc . = ..() - if(isatom(oldloc) && oldloc.storage) + if(isatom(oldloc) && oldloc.storage && !QDELETED(loc.storage)) oldloc.storage.on_item_post_deletion(src) // must be done after deletion // This might need to be moved onto a Del() override at some point. QDEL_NULL(storage) diff --git a/code/game/atoms_interactions.dm b/code/game/atoms_interactions.dm new file mode 100644 index 00000000000..1f8c07525c1 --- /dev/null +++ b/code/game/atoms_interactions.dm @@ -0,0 +1,44 @@ +// List of interactions used in procs below. +var/global/list/_reagent_interactions = list( + /decl/interaction_handler/wash_hands, + /decl/interaction_handler/drink, + /decl/interaction_handler/dip_item, + /decl/interaction_handler/fill_from, + /decl/interaction_handler/empty_into +) + +/** + Get a list of standard interactions (attack_hand and attackby) for a user from this atom. + At time of writing, these are really easy to have interfere with or be interfered with by + attack_hand() and attackby() overrides. Putting them on items us a bad idea due to pickup code. + + - `user`: The mob that these interactions are for + - Return: A list containing the interactions +*/ +/atom/proc/get_standard_interactions(var/mob/user) + SHOULD_CALL_PARENT(TRUE) + RETURN_TYPE(/list) + return null + +/** + Get a default interaction for a user from this atom. + + - `user`: The mob that this interaction is for + - Return: A default interaction decl, or null. +*/ +/atom/proc/get_quick_interaction_handler(mob/user) + return + +/** + Get a list of alt interactions (alt-click) for a user from this atom. + + - `user`: The mob that these alt interactions are for + - Return: A list containing the alt interactions +*/ +/atom/proc/get_alt_interactions(var/mob/user) + SHOULD_CALL_PARENT(TRUE) + RETURN_TYPE(/list) + if(storage) + LAZYADD(., /decl/interaction_handler/storage_open) + if(reagents?.maximum_volume) + LAZYADD(., global._reagent_interactions) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 4b0049e92f2..9c481be8d46 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -591,3 +591,22 @@ if(ATOM_IS_OPEN_CONTAINER(src)) return loc?.take_vaporized_reagent(reagent, amount) return null + +/atom/movable/immune_to_floor_hazards() + return ..() || !!throwing + +// TODO: make everything use this. +/atom/movable/proc/set_anchored(new_anchored) + SHOULD_CALL_PARENT(TRUE) + if(anchored != new_anchored) + anchored = new_anchored + return TRUE + return FALSE + +// updates pixel offsets, triggers fluids, etc. +/atom/movable/proc/on_turf_height_change(new_height) + if(simulated) + reset_offsets() + return TRUE + return FALSE + diff --git a/code/game/atoms_movable_interactions.dm b/code/game/atoms_movable_interactions.dm index 110c09afb82..8cf50ef5bc4 100644 --- a/code/game/atoms_movable_interactions.dm +++ b/code/game/atoms_movable_interactions.dm @@ -14,6 +14,7 @@ name = "Examine" expected_user_type = /mob interaction_flags = 0 + examine_desc = "examine $TARGET_THEM$" /decl/interaction_handler/look/invoked(atom/target, mob/user, obj/item/prop) target.examine(user, get_dist(user, target)) @@ -22,6 +23,7 @@ name = "Grab" expected_target_type = /atom/movable interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEEDS_TURF + examine_desc = "grab $TARGET_THEM$" /decl/interaction_handler/grab/is_possible(atom/movable/target, mob/user, obj/item/prop) return ..() && !target.anchored diff --git a/code/game/gamemodes/endgame/endgame.dm b/code/game/gamemodes/endgame/endgame.dm index 6d81b42d639..c1c03164c55 100644 --- a/code/game/gamemodes/endgame/endgame.dm +++ b/code/game/gamemodes/endgame/endgame.dm @@ -1,3 +1,4 @@ +var/global/universe_has_ended = 0 /********************** * ENDGAME STUFF **********************/ diff --git a/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm b/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm index e01a6995e1b..3aa8d9d84b6 100644 --- a/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm +++ b/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm @@ -38,7 +38,7 @@ SSticker.mode.station_was_nuked = 1 SSticker.mode.station_explosion_in_progress = FALSE if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is - universe_has_ended = 1 + universe_has_ended = TRUE /datum/universal_state/nuclear_explosion/OnExit() if(SSticker.mode) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index be38f8871ef..e6a2f45c19a 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -283,11 +283,10 @@ var/global/list/additional_antag_types = list() for(var/decl/special_role/antag in antag_templates) antag.reset_antag_selection() -/decl/game_mode/proc/announce_ert_disabled() - if(!ert_disabled) - return - - var/list/reasons = list( +/// Gets a list of default reasons for the ERT to be disabled. +/decl/game_mode/proc/possible_ert_disabled_reasons() + // This uses a static var so that modpacks can add default reasons, e.g. "supermatter dust". + var/static/list/reasons = list( "political instability", "quantum fluctuations", "hostile raiders", @@ -300,7 +299,6 @@ var/global/list/additional_antag_types = list() "wormholes to another dimension", "a telescience mishap", "radiation flares", - "supermatter dust", "leaks into a negative reality", "antiparticle clouds", "residual exotic energy", @@ -319,7 +317,12 @@ var/global/list/additional_antag_types = list() "classified security operations", "a gargantuan glowing goat" ) - command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") + return reasons + +/decl/game_mode/proc/announce_ert_disabled() + if(!ert_disabled) + return + command_announcement.Announce("The presence of [pick(possible_ert_disabled_reasons())] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") /decl/game_mode/proc/check_finished() if(SSevac.evacuation_controller?.round_over() || station_was_nuked) diff --git a/code/game/images.dm b/code/game/images.dm deleted file mode 100644 index e8b84493749..00000000000 --- a/code/game/images.dm +++ /dev/null @@ -1,3 +0,0 @@ -/image/Destroy() - ..() - return QDEL_HINT_HARDDEL diff --git a/code/game/jobs/job/_job.dm b/code/game/jobs/job/_job.dm index f5df1fe5197..06e985a8772 100644 --- a/code/game/jobs/job/_job.dm +++ b/code/game/jobs/job/_job.dm @@ -34,7 +34,8 @@ var/announced = TRUE // If their arrival is announced on radio var/latejoin_at_spawnpoints // If this job should use roundstart spawnpoints for latejoin (offstation jobs etc) var/forced_spawnpoint // If set to a spawnpoint name, will use that spawn point for joining as this job. - var/hud_icon // icon used for Sec HUD overlay + var/hud_icon // icon used for secHUD overlay + var/hud_icon_state // icon state used for secHUD overlay // A list of string IDs for keys to grant on join. var/list/lock_keys = list() @@ -69,7 +70,7 @@ if(type == /datum/job && global.using_map.default_job_type == type) title = "Debug Job" - hud_icon = "hudblank" + hud_icon_state = "hudblank" outfit_type = /decl/outfit/job/generic/scientist autoset_department = TRUE @@ -83,7 +84,9 @@ spawn_positions = 0 if(!hud_icon) - hud_icon = "hud[ckey(title)]" + hud_icon = global.using_map.hud_icons + if(!hud_icon_state) + hud_icon_state = "hud[ckey(title)]" ..() diff --git a/code/game/machinery/_machines_base/machinery_damage.dm b/code/game/machinery/_machines_base/machinery_damage.dm index a434776abd8..bba75011ff7 100644 --- a/code/game/machinery/_machines_base/machinery_damage.dm +++ b/code/game/machinery/_machines_base/machinery_damage.dm @@ -85,7 +85,7 @@ /obj/machinery/bash(obj/item/W, mob/user) if(!istype(W)) return FALSE - var/force = W.get_attack_force(user) + var/force = W.expend_attack_force(user) if(force <= 5) return FALSE . = ..() diff --git a/code/game/machinery/_machines_base/stock_parts/card_reader.dm b/code/game/machinery/_machines_base/stock_parts/card_reader.dm index aecd5af4e4f..3f92564a8d7 100644 --- a/code/game/machinery/_machines_base/stock_parts/card_reader.dm +++ b/code/game/machinery/_machines_base/stock_parts/card_reader.dm @@ -73,4 +73,5 @@ name = "Eject Card" icon = 'icons/screen/radial.dmi' icon_state = "radial_eject_id" - expected_component_type = /obj/item/stock_parts/item_holder/card_reader \ No newline at end of file + expected_component_type = /obj/item/stock_parts/item_holder/card_reader + examine_desc = "eject an inserted ID card" diff --git a/code/game/machinery/_machines_base/stock_parts/cupholder.dm b/code/game/machinery/_machines_base/stock_parts/cupholder.dm index 23f56f7772c..da49b9b5e9c 100644 --- a/code/game/machinery/_machines_base/stock_parts/cupholder.dm +++ b/code/game/machinery/_machines_base/stock_parts/cupholder.dm @@ -47,4 +47,5 @@ /decl/interaction_handler/remove_held_item/cup name = "Remove Cup" - expected_component_type = /obj/item/stock_parts/item_holder/cupholder \ No newline at end of file + expected_component_type = /obj/item/stock_parts/item_holder/cupholder + examine_desc = "remove a cup" diff --git a/code/game/machinery/_machines_base/stock_parts/disk_reader.dm b/code/game/machinery/_machines_base/stock_parts/disk_reader.dm index e61cf6be322..7c35679ef55 100644 --- a/code/game/machinery/_machines_base/stock_parts/disk_reader.dm +++ b/code/game/machinery/_machines_base/stock_parts/disk_reader.dm @@ -42,4 +42,5 @@ /decl/interaction_handler/remove_held_item/disk name = "Eject Disk" - expected_component_type = /obj/item/stock_parts/item_holder/disk_reader \ No newline at end of file + expected_component_type = /obj/item/stock_parts/item_holder/disk_reader + examine_desc = "remove a disk" diff --git a/code/game/machinery/_machines_base/stock_parts/item_holder.dm b/code/game/machinery/_machines_base/stock_parts/item_holder.dm index 3462f18f560..d6404088a4c 100644 --- a/code/game/machinery/_machines_base/stock_parts/item_holder.dm +++ b/code/game/machinery/_machines_base/stock_parts/item_holder.dm @@ -128,6 +128,7 @@ name = "Eject Item" icon = 'icons/screen/radial.dmi' icon_state = "radial_eject" + examine_desc = "eject an item" var/obj/item/stock_parts/item_holder/expected_component_type /decl/interaction_handler/remove_held_item/validate() diff --git a/code/game/machinery/_machines_base/stock_parts/power/terminal.dm b/code/game/machinery/_machines_base/stock_parts/power/terminal.dm index 041e68950f9..732d90839c9 100644 --- a/code/game/machinery/_machines_base/stock_parts/power/terminal.dm +++ b/code/game/machinery/_machines_base/stock_parts/power/terminal.dm @@ -135,7 +135,7 @@ if(!C.can_use(10)) to_chat(user, "You need ten lengths of cable for \the [machine].") return TRUE - user.visible_message("\The [user] adds cables to the \the [machine].", \ + user.visible_message("\The [user] adds cables to \the [machine].", \ "You start adding cables to \the [machine] frame...") playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) if(do_after(user, 20, machine)) @@ -147,8 +147,8 @@ return TRUE C.use(10) user.visible_message(\ - "\The [user] has added cables to the \the [machine]!",\ - "You add cables to the \the [machine].") + "\The [user] has added cables to \the [machine]!",\ + "You add cables to \the [machine].") make_terminal(machine) return TRUE return FALSE diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index dc389cb9dec..e571411e973 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -7,9 +7,9 @@ var/uses = 20 var/disabled = 1 var/locked = 1 - var/cooldown_time = 0 - var/cooldown_timeleft = 0 - var/cooldown_on = 0 + var/slip_cooldown_time = 0 + var/slip_cooldown_timeleft = 0 + var/slip_cooldown_on = 0 initial_access = list(access_ai_upload) /obj/machinery/ai_slipper/on_update_icon() @@ -52,13 +52,11 @@ if(!area || !isturf(loc)) return var/t = "AI Liquid Dispenser ([area.proper_name])
" - if(src.locked && (!issilicon(user))) t += "(Swipe ID card to unlock control panel.)
" else t += text("Dispenser [] - []?
\n", src.disabled?"deactivated":"activated", src, src.disabled?"Enable":"Disable") t += text("Uses Left: [uses]. Activate the dispenser?
\n") - show_browser(user, t, "window=computer;size=575x450") onclose(user, "computer") @@ -74,30 +72,26 @@ update_icon() . = TOPIC_REFRESH if (href_list["toggleUse"]) - if(!(cooldown_on || disabled)) + if(!(slip_cooldown_on || disabled)) new /obj/effect/effect/foam(src.loc) src.uses-- - cooldown_on = 1 - cooldown_time = world.timeofday + 100 + slip_cooldown_on = 1 + slip_cooldown_time = world.timeofday + 100 slip_process() . = TOPIC_REFRESH - if(. == TOPIC_REFRESH) ui_interact(user) /obj/machinery/ai_slipper/proc/slip_process() - while(cooldown_time - world.timeofday > 0) - var/ticksleft = cooldown_time - world.timeofday - + while(slip_cooldown_time - world.timeofday > 0) + var/ticksleft = slip_cooldown_time - world.timeofday if(ticksleft > 1e5) - cooldown_time = world.timeofday + 10 // midnight rollover - - - cooldown_timeleft = (ticksleft / 10) + slip_cooldown_time = world.timeofday + 10 // midnight rollover + slip_cooldown_timeleft = (ticksleft / 10) sleep(5) if (uses <= 0) return if (uses >= 0) - cooldown_on = 0 + slip_cooldown_on = 0 src.power_change() return diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index b9664324917..721eddb4ad9 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -331,8 +331,3 @@ radio_connection.post_signal(src, signal, device_tag) ..() - -/obj/machinery/computer/air_control/supermatter_core - icon = 'icons/obj/computer.dmi' - frequency = 1438 - out_pressure_mode = 1 \ No newline at end of file diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index f317c799c01..d3d06f81b00 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -207,7 +207,7 @@ EMPTY_CANISTER(phoron, /obj/machinery/portable_atmospherics/canister/phoron) /obj/machinery/portable_atmospherics/canister/bash(var/obj/item/W, var/mob/user) . = ..() if(.) - current_health -= W.get_attack_force(user) + current_health -= W.expend_attack_force(user) healthcheck() /obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/W, var/mob/user) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index ee9a28eee31..3262bf5a423 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -187,7 +187,7 @@ /obj/machinery/camera/physical_attack_hand(mob/living/human/user) if(!istype(user)) return TRUE - if(user.species.can_shred(user)) + if(user.can_shred()) user.do_attack_animation(src) visible_message(SPAN_WARNING("\The [user] slashes at [src]!")) playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 5b319d56bcb..345a6b0359a 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -52,9 +52,9 @@ else switch(W.atom_damage_type) if(BURN) - current_health -= W.get_attack_force(user) * 0.75 + current_health -= W.expend_attack_force(user) * 0.75 if(BRUTE) - current_health -= W.get_attack_force(user) * 0.5 + current_health -= W.expend_attack_force(user) * 0.5 if (current_health <= 0) explode() return TRUE diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm index cc2a3c8adf4..e76fe49edfe 100644 --- a/code/game/machinery/doors/_door.dm +++ b/code/game/machinery/doors/_door.dm @@ -335,7 +335,7 @@ return FALSE user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.do_attack_animation(src) - var/force = weapon.get_attack_force(user) + var/force = weapon.expend_attack_force(user) if(force < min_force) user.visible_message("\The [user] hits \the [src] with \the [weapon] with no visible effect.") else diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index aa341461f1c..1be24240e75 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -802,7 +802,7 @@ About the new airlock wires panel: else if((stat & (BROKEN|NOPOWER)) && isanimal(user)) var/mob/living/simple_animal/A = user var/obj/item/I = A.get_natural_weapon() - if(I?.get_attack_force(user) >= 10) + if(I?.expend_attack_force(user) >= 10) if(density) visible_message(SPAN_DANGER("\The [A] forces \the [src] open!")) open(1) @@ -822,7 +822,7 @@ About the new airlock wires panel: var/obj/item/bladed/axe/fire/F = weapon if (F.is_held_twohanded()) playsound(src, 'sound/weapons/smash.ogg', 100, 1) - current_health -= F.get_attack_force(user) * 2 + current_health -= F.expend_attack_force(user) * 2 if(current_health <= 0) user.visible_message(SPAN_DANGER("[user] smashes \the [weapon] into the airlock's control panel! It explodes in a shower of sparks!"), SPAN_DANGER("You smash \the [weapon] into the airlock's control panel! It explodes in a shower of sparks!")) current_health = 0 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 6ec853d2eb8..e6366bcc0a0 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -139,13 +139,11 @@ return /obj/machinery/door/window/physical_attack_hand(mob/user) - if(ishuman(user)) - var/mob/living/human/H = user - if(H.species.can_shred(H)) - playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) - visible_message("\The [user] smashes against \the [src].", 1) - take_damage(25) - return TRUE + if(user.can_shred()) + playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) + visible_message(SPAN_DANGER("\The [user] smashes against \the [src].")) + take_damage(25) + return TRUE return ..() /obj/machinery/door/window/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/game/machinery/pipe/pipelayer.dm b/code/game/machinery/pipe/pipelayer.dm index 647e9c0b572..fdc1b122ef3 100644 --- a/code/game/machinery/pipe/pipelayer.dm +++ b/code/game/machinery/pipe/pipelayer.dm @@ -73,7 +73,7 @@ if(m) use_metal(m) SSmaterials.create_object(/decl/material/solid/metal/steel, get_turf(src), m) - user.visible_message("[user] removes [m] sheet\s of metal from the \the [src].", "You remove [m] sheet\s of metal from \the [src]") + user.visible_message(SPAN_NOTICE("[user] removes [m] sheet\s of metal from \the [src]."), SPAN_NOTICE("You remove [m] sheet\s of metal from \the [src]")) else to_chat(user, "\The [src] is empty.") return TRUE diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 0124b95786e..286ac6310cc 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -290,7 +290,7 @@ var/global/list/turret_icons else //if the turret was attacked with the intention of harming it: - var/force = I.get_attack_force(user) * 0.5 + var/force = I.expend_attack_force(user) * 0.5 user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) take_damage(force, I.atom_damage_type) if(force > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index 550e474d0ef..831363ce53e 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -11,10 +11,6 @@ var/deploy_path = /obj/structure/supply_beacon var/deploy_time = 30 -/obj/item/supply_beacon/supermatter - name = "inactive supermatter supply beacon" - deploy_path = /obj/structure/supply_beacon/supermatter - /obj/item/supply_beacon/attack_self(var/mob/user) user.visible_message(SPAN_NOTICE("\The [user] begins setting up \the [src].")) if(!do_after(user, deploy_time, src)) @@ -45,10 +41,6 @@ if(!drop_type) drop_type = pick(supply_drop_random_loot_types()) -/obj/structure/supply_beacon/supermatter - name = "supermatter supply beacon" - drop_type = "supermatter" - /obj/structure/supply_beacon/attackby(var/obj/item/W, var/mob/user) if(!activated && IS_WRENCH(W)) anchored = !anchored diff --git a/code/game/machinery/turrets/_turrets.dm b/code/game/machinery/turrets/_turrets.dm index 3030bc2bef9..957c3407889 100644 --- a/code/game/machinery/turrets/_turrets.dm +++ b/code/game/machinery/turrets/_turrets.dm @@ -188,7 +188,7 @@ playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1) reloading_progress = 0 - else if(stored_magazine && length(stored_magazine.stored_ammo) < stored_magazine.max_ammo) + else if(stored_magazine && stored_magazine.get_stored_ammo_count() < stored_magazine.max_ammo) var/obj/item/stock_parts/ammo_box/ammo_box = get_component_of_type(/obj/item/stock_parts/ammo_box) if(ammo_box?.is_functional() && ammo_box.stored_caliber == proj_gun.caliber) var/obj/item/ammo_casing/casing = ammo_box.remove_ammo(stored_magazine) @@ -366,7 +366,7 @@ // Only reload the magazine if we're completely out of ammo or we don't have a target. if(ammo_remaining == 0) return TRUE - if(!is_valid_target(target?.resolve()) && length(proj_gun.ammo_magazine.stored_ammo) != proj_gun.ammo_magazine.max_ammo) + if(!is_valid_target(target?.resolve()) && proj_gun.ammo_magazine.get_stored_ammo_count() != proj_gun.ammo_magazine.max_ammo) return TRUE else return FALSE diff --git a/code/game/machinery/turrets/turret_ammo.dm b/code/game/machinery/turrets/turret_ammo.dm index 52cf5825df0..2ae59092307 100644 --- a/code/game/machinery/turrets/turret_ammo.dm +++ b/code/game/machinery/turrets/turret_ammo.dm @@ -50,7 +50,7 @@ if(stored_caliber && magazine.caliber != stored_caliber) to_chat(user, SPAN_WARNING("The caliber of \the [magazine] does not match the caliber stored in \the [src]!")) return TRUE - if(!length(magazine.stored_ammo)) + if(!magazine.get_stored_ammo_count()) to_chat(user, SPAN_WARNING("\The [magazine] is empty!")) return TRUE if(length(stored_ammo) >= max_ammo) @@ -58,7 +58,7 @@ return TRUE stored_caliber = magazine.caliber - for(var/obj/item/ammo_casing/casing in magazine.stored_ammo) + for(var/obj/item/ammo_casing/casing in magazine.get_stored_ammo_count()) // Just in case. if(casing.caliber != stored_caliber) continue diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index 866062f2a54..b3036e17317 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -309,10 +309,8 @@ if (href_list["vend"] && !currently_vending) var/key = text2num(href_list["vend"]) - if(!is_valid_index(key, product_records)) - return TOPIC_REFRESH - var/datum/stored_items/vending_products/R = product_records[key] - if(!istype(R)) + var/datum/stored_items/vending_products/R = LAZYACCESS(product_records, key) + if(!R) return TOPIC_REFRESH // This should not happen unless the request from NanoUI was bad diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm index 7e7e2717612..f06099ed6e3 100644 --- a/code/game/machinery/wall_frames.dm +++ b/code/game/machinery/wall_frames.dm @@ -246,7 +246,7 @@ icon = 'icons/obj/airlock_machines.dmi' icon_state = "airlock_control_off" name = "airlock controller frame" - desc = "Used to build airlock controllers. Use a multitool on the circuit to determine which type you want, and then hit this with the the circuit." + desc = "Used to build airlock controllers. Use a multitool on the circuit to determine which type you want, and then hit this with the circuit." build_machine_type = null ///Used when configuring a dummy controller var/master_controller_id_tag diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index a82e8082510..bf5c3741630 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -250,6 +250,7 @@ /decl/interaction_handler/start_washer name = "Start washer" expected_target_type = /obj/machinery/washing_machine + examine_desc = "start a wash cycle" /decl/interaction_handler/start_washer/is_possible(obj/machinery/washing_machine/washer, mob/user) . = ..() @@ -263,6 +264,7 @@ /decl/interaction_handler/toggle_open/washing_machine name = "Toggle detergent port" expected_target_type = /obj/machinery/washing_machine + examine_desc = "open the detergent port" /decl/interaction_handler/toggle_open/washing_machine/invoked(atom/target, mob/user, obj/item/prop) var/obj/machinery/washing_machine/washer = target diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 8ab41e00a57..812aaa9ddca 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -14,8 +14,7 @@ var/list/req_access var/list/matter //Used to store information about the contents of the object. var/w_class // Size of the object. - var/sharp = 0 // whether this object cuts - var/edge = 0 // whether this object is more likely to dismember + var/in_use = 0 // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! var/armor_penetration = 0 var/anchor_fall = FALSE @@ -119,12 +118,12 @@ /obj/proc/damage_flags() . = 0 - if(has_edge(src)) - . |= DAM_EDGE - if(is_sharp(src)) - . |= DAM_SHARP + if(is_sharp()) + . |= DAM_SHARP|DAM_EDGE if(atom_damage_type == BURN) . |= DAM_LASER + else if(has_edge()) + . |= DAM_EDGE /obj/attackby(obj/item/used_item, mob/user) // We need to call parent even if we lack dexterity, so that storage can work. @@ -156,8 +155,8 @@ add_fingerprint(user) return ..() -/obj/is_fluid_pushable(var/amt) - return ..() && w_class <= round(amt/20) +/obj/try_fluid_push(volume, strength) + return ..() && w_class <= round(strength/20) /obj/proc/can_embed() return FALSE diff --git a/code/game/objects/_objs_interactions.dm b/code/game/objects/_objs_interactions.dm index b295c6fd71f..65d143b39b3 100644 --- a/code/game/objects/_objs_interactions.dm +++ b/code/game/objects/_objs_interactions.dm @@ -8,6 +8,7 @@ /decl/interaction_handler/rotate name = "Rotate" expected_target_type = /obj + examine_desc = "rotate $TARGET_THEM$" /decl/interaction_handler/rotate/is_possible(atom/target, mob/user, obj/item/prop) . = ..() diff --git a/code/game/objects/auras/aura.dm b/code/game/objects/auras/aura.dm index 8d1d35a0680..5de18f3adf6 100644 --- a/code/game/objects/auras/aura.dm +++ b/code/game/objects/auras/aura.dm @@ -15,6 +15,7 @@ They should also be used for when you want to effect the ENTIRE mob, like having /obj/aura/Destroy() if(user) user.remove_aura(src) + removed() return ..() /obj/aura/proc/added_to(var/mob/living/target) diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index fc4fdc8470d..ccfd9b93b6e 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -160,12 +160,6 @@ if(LAZYLEN(chemholder.reagents.reagent_volumes)) for(var/turf/T in (wallList|targetTurfs)) chemholder.reagents.touch_turf(T) - for(var/turf/T in targetTurfs) - for(var/atom/A in T.contents) - if(istype(A, /obj/effect/effect/smoke/chem) || ismob(A)) - continue - else if(isobj(A) && !A.simulated) - chemholder.reagents.touch_obj(A) var/color = chemholder.reagents.get_color() //build smoke icon var/icon/I diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm index 5ff19547765..70d5c86bb2e 100644 --- a/code/game/objects/effects/chem/foam.dm +++ b/code/game/objects/effects/chem/foam.dm @@ -37,8 +37,6 @@ if(!metal && reagents) var/turf/T = get_turf(src) reagents.touch_turf(T) - for(var/obj/O in T) - reagents.touch_obj(O) /obj/effect/effect/foam/Process() if(--amount < 0) @@ -173,7 +171,7 @@ return TRUE /obj/structure/foamedmetal/attackby(var/obj/item/I, var/mob/user) - if(prob(I.get_attack_force(user) * 20 - metal * 25)) + if(prob(I.expend_attack_force(user) * 20 - metal * 25)) user.visible_message( SPAN_WARNING("\The [user] smashes through the foamed metal."), SPAN_NOTICE("You smash through the foamed metal with \the [I].") diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm index ef1d2ba6096..8f4dbce7bb7 100644 --- a/code/game/objects/effects/chem/water.dm +++ b/code/game/objects/effects/chem/water.dm @@ -45,8 +45,7 @@ break sleep(delay) - sleep(10) - qdel(src) + QDEL_IN(src, 1 SECOND) /obj/effect/effect/water/Move(turf/newloc) if(newloc.density) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index cbd3f166ddb..77bb9276f64 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -1,4 +1,3 @@ -#define DRYING_TIME 5 MINUTES //for 1 unit of depth in puddle (amount var) #define BLOOD_SIZE_SMALL 1 #define BLOOD_SIZE_MEDIUM 2 #define BLOOD_SIZE_BIG 3 @@ -21,6 +20,8 @@ var/base_icon = 'icons/effects/blood.dmi' var/basecolor=COLOR_BLOOD_HUMAN // Color when wet. var/amount = 5 + //for 1 unit of depth in puddle (amount var) + var/time_to_dry = 5 MINUTES var/drytime var/dryname = "dried blood" var/drydesc = "It's dry and crusty. Someone isn't doing their job." @@ -29,19 +30,21 @@ var/chemical = /decl/material/liquid/blood /obj/effect/decal/cleanable/blood/reveal_blood() - if(!fluorescent) + if(ispath(chemical, /decl/material/liquid/blood) && !fluorescent) fluorescent = FLUORESCENT_GLOWS basecolor = COLOR_LUMINOL update_icon() /obj/effect/decal/cleanable/blood/clean(clean_forensics = TRUE) - fluorescent = FALSE - if(invisibility != INVISIBILITY_ABSTRACT) - set_invisibility(INVISIBILITY_ABSTRACT) - amount = 0 - STOP_PROCESSING(SSobj, src) - remove_extension(src, /datum/extension/scent) - . = ..(clean_forensics = FALSE) + if(ispath(chemical, /decl/material/liquid/blood)) + clean_forensics = FALSE + fluorescent = FALSE + if(invisibility != INVISIBILITY_ABSTRACT) + set_invisibility(INVISIBILITY_ABSTRACT) + amount = 0 + STOP_PROCESSING(SSobj, src) + remove_extension(src, /datum/extension/scent) + . = ..(clean_forensics) /obj/effect/decal/cleanable/blood/hide() return @@ -50,8 +53,11 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/effect/decal/cleanable/blood/Initialize(mapload) +// new_chemical is a material typepath, or null +/obj/effect/decal/cleanable/blood/Initialize(ml, _age, new_chemical) . = ..() + if(!isnull(new_chemical)) + chemical = new_chemical if(merge_with_blood()) return INITIALIZE_HINT_QDEL start_drying() @@ -61,16 +67,31 @@ if(!isturf(loc) || blood_size == BLOOD_SIZE_NO_MERGE) return FALSE for(var/obj/effect/decal/cleanable/blood/B in loc) - if(B != src && B.blood_size != BLOOD_SIZE_NO_MERGE) - if(B.blood_DNA) - blood_size = BLOOD_SIZE_NO_MERGE - B.blood_DNA |= blood_DNA.Copy() - B.alpha = initial(B.alpha) // reset rain-based fading due to more drips - return TRUE + if(B == src) + continue + if(B.blood_size == BLOOD_SIZE_NO_MERGE) + continue + if(B.invisibility >= INVISIBILITY_ABSTRACT) // has been cleaned + continue + if(B.chemical != chemical) // don't mix blood and oil or oil and mud etc + continue // todo: refactor to make bloody steps use reagents and track data/size/amount on there? + if(B.blood_DNA) + blood_size = BLOOD_SIZE_NO_MERGE + B.blood_DNA |= blood_DNA.Copy() + B.alpha = initial(B.alpha) // reset rain-based fading due to more drips + return TRUE return FALSE /obj/effect/decal/cleanable/blood/proc/start_drying() - drytime = world.time + DRYING_TIME * (amount+1) + var/decl/material/our_chemical = GET_DECL(chemical) + time_to_dry = our_chemical.get_time_to_dry_stain(src) + switch(time_to_dry) + if(0) // dry instantly + dry() + return + if(-INFINITY to 0) // don't even bother trying to dry this + return + drytime = world.time + time_to_dry * (amount+1) update_icon() START_PROCESSING(SSobj, src) @@ -100,6 +121,9 @@ amount-- /obj/effect/decal/cleanable/blood/proc/dry() + var/decl/material/our_chemical = GET_DECL(chemical) + if(our_chemical.handle_stain_dry(src)) + return TRUE // prevent additional drying handling; this includes preventing processing, so be careful name = dryname desc = drydesc color = adjust_brightness(color, -50) @@ -107,6 +131,7 @@ blood_data = null remove_extension(src, /datum/extension/scent) STOP_PROCESSING(SSobj, src) + return FALSE /obj/effect/decal/cleanable/blood/attack_hand(mob/user) if(!amount || !length(blood_data) || !ishuman(user)) @@ -233,7 +258,7 @@ for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) sleep(3) if (i > 0) - var/obj/effect/decal/cleanable/blood/b = new /obj/effect/decal/cleanable/blood/splatter(loc) + var/obj/effect/decal/cleanable/blood/b = new /obj/effect/decal/cleanable/blood/splatter(loc, null, chemical) b.basecolor = src.basecolor b.update_icon() if (step_to(src, get_step(src, direction), 0)) diff --git a/code/game/objects/effects/decals/Cleanable/robots.dm b/code/game/objects/effects/decals/Cleanable/robots.dm index 330acc9f3ce..8487334c1a4 100644 --- a/code/game/objects/effects/decals/Cleanable/robots.dm +++ b/code/game/objects/effects/decals/Cleanable/robots.dm @@ -13,9 +13,6 @@ /obj/effect/decal/cleanable/blood/gibs/robot/on_update_icon() color = "#ffffff" -/obj/effect/decal/cleanable/blood/gibs/robot/dry() //pieces of robots do not dry up like - return - /obj/effect/decal/cleanable/blood/gibs/robot/streak(var/list/directions) spawn (0) var/direction = pick(directions) @@ -44,8 +41,9 @@ chemical = /decl/material/liquid/lube cleanable_scent = "industrial lubricant" -/obj/effect/decal/cleanable/blood/oil/dry() - return +/obj/effect/decal/cleanable/blood/oil/Initialize(mapload) + . = ..() + update_icon() /obj/effect/decal/cleanable/blood/oil/streak random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index 870df8fbbf1..d2e28c977da 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -10,8 +10,7 @@ #define TRACKS_GOING_EAST 64 #define TRACKS_GOING_WEST 128 -// 5 seconds -#define TRACKS_CRUSTIFY_TIME 50 +#define TRACKS_CRUSTIFY_TIME 5 SECONDS /datum/fluidtrack var/direction=0 @@ -26,7 +25,8 @@ src.wet=_wet /obj/effect/decal/cleanable/blood/tracks/reveal_blood() - if(!fluorescent) + // don't reveal non-blood tracks + if(ispath(chemical, /decl/material/liquid/blood) && !fluorescent) if(stack && stack.len) for(var/datum/fluidtrack/track in stack) track.basecolor = COLOR_LUMINOL @@ -68,6 +68,7 @@ * @param bloodcolor Color of the blood when wet. */ /obj/effect/decal/cleanable/blood/tracks/proc/AddTracks(var/list/DNA, var/comingdir, var/goingdir, var/bloodcolor=COLOR_BLOOD_HUMAN) + var/updated=0 // Shift our goingdir 4 spaces to the left so it's in the GOING bitblock. var/realgoing=BITSHIFT_LEFT(goingdir,4) @@ -124,7 +125,7 @@ update_icon() /obj/effect/decal/cleanable/blood/tracks/on_update_icon() - overlays.Cut() + cut_overlays() color = "#ffffff" var/truedir=0 @@ -145,8 +146,9 @@ track.fresh=0 track.overlay=I stack[stack_idx]=track - overlays += I + add_overlay(I) updatedtracks=0 // Clear our memory of updated tracks. + compile_overlays() /obj/effect/decal/cleanable/blood/tracks/footprints name = "wet footprints" diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index b5d5d29540a..664d7af8c8f 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -1,27 +1,30 @@ /obj/effect/decal/cleanable/crayon - name = "rune" - desc = "A rune drawn in crayon." - icon = 'icons/obj/rune.dmi' + name = "rune" + desc = "A rune drawn in crayon." + icon = 'icons/effects/crayondecal.dmi' + icon_state = "rune1" weather_sensitive = FALSE + var/shade_color = COLOR_BLACK -/obj/effect/decal/cleanable/crayon/Initialize(mapload, main = "#ffffff", shade = "#000000", var/type = "rune") - name = type - desc = "\A [type] drawn in crayon." +/obj/effect/decal/cleanable/crayon/Initialize(mapload, _color = COLOR_WHITE, _shade_color = COLOR_BLACK, _drawtype = CRAYON_DRAW_RUNE) + . = ..() + name = _drawtype + desc = "\A [_drawtype], drawn in crayon." + color = _color + shade_color = _shade_color + switch(_drawtype) + if(CRAYON_DRAW_RUNE) + icon_state = "rune[rand(1,6)]" + if(CRAYON_DRAW_GRAFFITI) + icon_state = pick("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa") + else + icon_state = _drawtype + update_icon() - switch(type) - if("rune") - type = "rune[rand(1,6)]" - if("graffiti") - type = pick("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa") - - var/icon/mainOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]",2.1) - var/icon/shadeOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]s",2.1) - - mainOverlay.Blend(main,ICON_ADD) - shadeOverlay.Blend(shade,ICON_ADD) - - overlays += mainOverlay - overlays += shadeOverlay - - add_hiddenprint(usr) - . = ..() \ No newline at end of file +/obj/effect/decal/cleanable/crayon/on_update_icon() + . = ..() + if(shade_color) + var/overlay_state = "[icon_state]s" + if(check_state_in_icon(overlay_state, icon)) + add_overlay(overlay_image(icon, overlay_state, shade_color, RESET_COLOR)) + compile_overlays() diff --git a/code/game/objects/effects/decals/posters/_poster.dm b/code/game/objects/effects/decals/posters/_poster.dm index 3d4c3bf3f3d..2f3ca709b56 100644 --- a/code/game/objects/effects/decals/posters/_poster.dm +++ b/code/game/objects/effects/decals/posters/_poster.dm @@ -7,9 +7,9 @@ icon = 'icons/obj/items/posters.dmi' icon_state = "poster0" anchored = TRUE - directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":-32}, "WEST":{"x":32}, "EAST":{"x":-32}}' + directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":-32}, "EAST":{"x":32}, "WEST":{"x":-32}}' material = /decl/material/solid/organic/paper - max_health = 10 + max_health = 10 parts_type = /obj/item/poster parts_amount = 1 diff --git a/code/game/objects/effects/dirty_floor.dm b/code/game/objects/effects/dirty_floor.dm index ae0c3c0cd9b..8c2df8b9f8b 100644 --- a/code/game/objects/effects/dirty_floor.dm +++ b/code/game/objects/effects/dirty_floor.dm @@ -9,6 +9,16 @@ alpha = 0 var/dirt_amount = 0 +/obj/effect/decal/cleanable/dirt/lava_act() + qdel(src) + return TRUE + +// 'the dirt falls through the x' is pretty silly, dirt is generated by people walking. +/obj/effect/decal/cleanable/dirt/begin_falling(lastloc, below) + SHOULD_CALL_PARENT(FALSE) + qdel(src) + return TRUE + /obj/effect/decal/cleanable/dirt/visible dirt_amount = 60 persistent = FALSE // This is a subtype for mapping. diff --git a/code/game/objects/effects/footprints.dm b/code/game/objects/effects/footprints.dm new file mode 100644 index 00000000000..09ea5bddc7b --- /dev/null +++ b/code/game/objects/effects/footprints.dm @@ -0,0 +1,56 @@ +// Effect used to render dark spot on turfs like snow/mud. +/obj/effect/footprints + icon = 'icons/mob/footprints/footprints.dmi' + icon_state = "blank" + simulated = FALSE + anchored = TRUE + is_spawnable_type = FALSE + blend_mode = BLEND_SUBTRACT + alpha = 128 + var/list/image/footprints + +/obj/effect/footprints/Initialize(mapload) + . = ..() + verbs.Cut() + name = null + +/obj/effect/footprints/Destroy() + footprints.Cut() // don't qdel images + . = ..() + +/obj/effect/footprints/on_turf_height_change(new_height) + if(simulated) + qdel(src) + return TRUE + return FALSE + +/obj/effect/footprints/on_update_icon() + set_overlays(footprints?.Copy()) + compile_overlays() + +/obj/effect/footprints/proc/add_footprints(mob/crosser, footprint_icon, movement_dir, use_state = "coming") + var/image/update_footprint + for(var/image/footprint in footprints) + if(footprint.icon == footprint_icon && footprint.dir == movement_dir && footprint.icon_state == use_state) + update_footprint = footprint + break + if(!update_footprint) + update_footprint = image(footprint_icon, icon_state = use_state, dir = movement_dir) + update_footprint.alpha = 20 + LAZYADD(footprints, update_footprint) + if(update_footprint.alpha < 120) + update_footprint.alpha += round(crosser.get_object_size() / 5) + queue_icon_update() + return TRUE + +/mob/proc/get_footprints_icon() + if(is_floating) + return null + if(buckled || current_posture?.prone) + return 'icons/mob/footprints/footprints_trail.dmi' + if(istype(buckled, /obj/structure/bed/chair)) + return 'icons/mob/footprints/footprints_wheelchair.dmi' + var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str) + if(istype(shoes) && shoes.footprint_icon) + return shoes.footprint_icon + return get_bodytype()?.get_footprints_icon() diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 7f70b3e996c..ac59e8322d4 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -34,16 +34,14 @@ if(ismob(obj)) var/mob/mob = obj mob.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) - spawn(0) - qdel(src) + qdel(src) /obj/effect/mine/proc/triggerstun(obj) if(ismob(obj)) var/mob/M = obj SET_STATUS_MAX(M, STAT_STUN, 30) spark_at(src, cardinal_only = TRUE) - spawn(0) - qdel(src) + qdel(src) /obj/effect/mine/proc/triggern2o(obj) //example: n2o triggerproc @@ -53,8 +51,7 @@ if(target.simulated && !target.blocks_air) target.assume_gas(/decl/material/gas/nitrous_oxide, 30) - spawn(0) - qdel(src) + qdel(src) /obj/effect/mine/proc/triggerflame(obj) for (var/turf/target in range(1,src)) @@ -62,21 +59,18 @@ target.assume_gas(/decl/material/gas/hydrogen, 30) target.hotspot_expose(1000, CELL_VOLUME) - spawn(0) - qdel(src) + qdel(src) /obj/effect/mine/proc/triggerkick(obj) spark_at(src, cardinal_only = TRUE) if(ismob(obj)) var/mob/victim = obj qdel(victim.client) - spawn(0) - qdel(src) + qdel(src) /obj/effect/mine/proc/explode(obj) explosion(loc, 0, 1, 2, 3) - spawn(0) - qdel(src) + qdel(src) /obj/effect/mine/dnascramble name = "Radiation Mine" diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 4a78d796efd..38cd105e6d0 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -35,11 +35,12 @@ icon = 'icons/misc/beach.dmi' icon_state = "coconuts" +// This isn't modularized because I hate modularizing layer defines. Bite me. /obj/effect/overlay/bluespacify name = "subspace" icon = 'icons/turf/space.dmi' icon_state = "bluespacify" - layer = SUPERMATTER_WALL_LAYER + layer = SUBSPACE_WALL_LAYER /obj/effect/overlay/wallrot name = "wallrot" @@ -54,3 +55,13 @@ . = ..() pixel_x += rand(-10, 10) pixel_y += rand(-10, 10) + +/// Set and cleaned up by moving projectiles for the most part. +/obj/effect/overlay/projectile_trail + var/obj/item/projectile/master + +/obj/effect/overlay/projectile_trail/Destroy() + if(master) + LAZYREMOVE(master.proj_trails, src) + master = null + return ..() diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index a1a3fd87bd3..2aae839cd61 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -41,9 +41,9 @@ else visible_message("\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]") - var/damage = W.get_attack_force(user) / 4 + var/damage = W.expend_attack_force(user) / 4 - if(W.edge) + if(W.has_edge()) damage += 5 if(IS_WELDER(W)) diff --git a/code/game/objects/effects/temporary.dm b/code/game/objects/effects/temporary.dm index b457013a103..b56d38275f1 100644 --- a/code/game/objects/effects/temporary.dm +++ b/code/game/objects/effects/temporary.dm @@ -5,7 +5,7 @@ layer = ABOVE_HUMAN_LAYER mouse_opacity = MOUSE_OPACITY_UNCLICKABLE simulated = FALSE - var/duration = 10 //in deciseconds + var/duration = 1 SECOND //in deciseconds /obj/effect/temp_visual/Initialize(mapload, set_dir) if(set_dir) @@ -17,9 +17,15 @@ icon = 'icons/effects/effects.dmi' icon_state = "empdisable" +/obj/effect/temp_visual/emppulse + name = "electromagnetic pulse" + icon = 'icons/effects/effects.dmi' + icon_state = "emppulse" + duration = 2 SECONDS + /obj/effect/temp_visual/bloodsplatter icon = 'icons/effects/bloodspatter.dmi' - duration = 5 + duration = 0.5 SECONDS layer = LYING_HUMAN_LAYER var/splatter_type = "splatter" diff --git a/code/game/objects/empulse.dm b/code/game/objects/empulse.dm index 33ea333138c..24dad4dfb5d 100644 --- a/code/game/objects/empulse.dm +++ b/code/game/objects/empulse.dm @@ -14,13 +14,7 @@ log_and_message_admins("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] ") if(heavy_range > 1) - var/obj/effect/overlay/pulse = new/obj/effect/overlay(epicenter) - pulse.icon = 'icons/effects/effects.dmi' - pulse.icon_state = "emppulse" - pulse.SetName("electromagnetic pulse") - pulse.anchored = TRUE - spawn(20) - qdel(pulse) + new /obj/effect/temp_visual/emppulse(epicenter) if(heavy_range > light_range) light_range = heavy_range diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm index d6881129c23..5739ddef4b1 100644 --- a/code/game/objects/item_mob_overlay.dm +++ b/code/game/objects/item_mob_overlay.dm @@ -8,13 +8,14 @@ var/global/list/bodypart_to_slot_lookup_table = list( ) /obj/item/proc/reconsider_single_icon(var/update_icon) - use_single_icon = check_state_in_icon(ICON_STATE_INV, icon) || check_state_in_icon(ICON_STATE_WORLD, icon) + var/list/icon_states = get_states_in_icon_cached(icon) // pre-cache to make our up-to-three checks faster + // except now we only do two because i rewrote it + has_inventory_icon = use_single_icon = icon_states[ICON_STATE_INV] + if(!has_inventory_icon) + use_single_icon = icon_states[ICON_STATE_WORLD] if(use_single_icon) - has_inventory_icon = check_state_in_icon(ICON_STATE_INV, icon) icon_state = get_world_inventory_state() . = TRUE - else - has_inventory_icon = FALSE if(. || update_icon) update_icon() @@ -27,14 +28,33 @@ var/global/list/icon_state_cache = list() // isicon() is apparently quite expensive so short-circuit out early if we can. if(!istext(checkstate) || isnull(checkicon) || !(isfile(checkicon) || isicon(checkicon))) return FALSE - var/checkkey = "\ref[checkicon]" - var/list/check = global.icon_state_cache[checkkey] + var/list/check = _fetch_icon_state_cache_entry(checkicon) // should never return null once we reach this point + return check[checkstate] + +/// A proc for getting an associative list of icon states in an icon. +/// Uses the same cache as check_state_in_icon. +/// Does not copy, MUST NOT BE MUTATED. +/proc/get_states_in_icon_cached(checkicon) /* as OD_MAP(text, OD_BOOL) */ + return _fetch_icon_state_cache_entry(checkicon) || list() + +/// get_states_in_icon_cached but it does a copy, so the return value can be mutated. +/proc/get_states_in_icon(checkicon) /* as OD_MAP(text, OD_BOOL) */ + var/list/out = get_states_in_icon_cached(checkicon) + return out.Copy() + +/proc/_fetch_icon_state_cache_entry(checkicon) + if(isnull(checkicon) || !(isfile(checkicon) || isicon(checkicon))) + return null + // if we want to let people del icons (WHY???) then we can use weakreF() + // but right now it's cheaper to just use checkicon directly + // ref doesn't even do any deduplication + var/list/check = global.icon_state_cache[checkicon] if(!check) check = list() for(var/istate in icon_states(checkicon)) check[istate] = TRUE - global.icon_state_cache[checkkey] = check - . = check[checkstate] + global.icon_state_cache[checkicon] = check + return check /obj/item/proc/update_world_inventory_state() if(use_single_icon && has_inventory_icon) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 20c732782fa..7c41d0aa13f 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -120,6 +120,20 @@ /// Can this item knock someone out if used as a weapon? Overridden for natural weapons as a nerf to simplemobs. var/weapon_can_knock_prone = TRUE +/// Returns a dexterity value required to use this item as a weapon. +/obj/item/proc/get_required_attack_dexterity(mob/user, atom/target) + // We can likely assume that if we're located inside a rig, then the wearer + // has the appropriate dexterity to wear and use the rig, even if they aren't + // manually dexterous; specifically useful for things like baxxid and drakes. + var/obj/item/rig/rig = get_recursive_loc_of_type(/obj/item/rig) + . = istype(rig) ? DEXTERITY_NONE : needs_attack_dexterity + if(istype(target)) + . = target.adjust_required_attack_dexterity(user, .) + +// Returns a dexterity value required to interact with this item at all, such as picking it up. +/obj/item/get_required_interaction_dexterity() + return needs_interaction_dexterity + /obj/item/get_color() if(paint_color) return paint_color @@ -570,12 +584,12 @@ return TRUE return FALSE -/obj/item/proc/user_can_attack_with(mob/user, silent = FALSE) - return !needs_attack_dexterity || user.check_dexterity(needs_attack_dexterity, silent = silent) +/obj/item/proc/user_can_attack_with(mob/user, atom/target, silent = FALSE) + return user.check_dexterity(get_required_attack_dexterity(user, target), silent = silent) /obj/item/attackby(obj/item/used_item, mob/user) // if can_wield is false we still need to call parent for storage objects to work properly - var/can_wield = user_can_attack_with(user, silent = TRUE) + var/can_wield = used_item.user_can_attack_with(user, silent = TRUE) if(can_wield && try_slapcrafting(used_item, user)) return TRUE @@ -815,21 +829,19 @@ LAZYSET(blood_DNA, unique_enzymes, blood_type) return TRUE -var/global/list/_coating_overlay_cache = list() +var/global/list/icon/_coating_overlay_cache = list() var/global/icon/_item_coating_mask = icon('icons/effects/blood.dmi', "itemblood") /obj/item/proc/generate_coating_overlay(force = FALSE) if(coating_overlay && !force) return - var/cache_key = "[icon]-[icon_state]" - if(global._coating_overlay_cache[cache_key]) - coating_overlay = global._coating_overlay_cache[cache_key] - return - var/icon/I = new /icon(icon, icon_state) - I.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) // Sets the icon RGB channel to pure white. - I.Blend(global._item_coating_mask, ICON_MULTIPLY) // Masks the coating overlay against the generated mask. - coating_overlay = image(I) + var/cache_key = "\ref[icon]-[icon_state]" // this needs to use ref because of stringification + if(!global._coating_overlay_cache[cache_key]) + var/icon/I = new /icon(icon, icon_state) + I.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) // Sets the icon RGB channel to pure white. + I.Blend(global._item_coating_mask, ICON_MULTIPLY) // Masks the coating overlay against the generated mask. + global._coating_overlay_cache[cache_key] = I + coating_overlay = image(global._coating_overlay_cache[cache_key]) coating_overlay.appearance_flags |= NO_CLIENT_COLOR|RESET_COLOR - global._coating_overlay_cache[cache_key] = coating_overlay /obj/item/proc/showoff(mob/user) for(var/mob/M in view(user)) @@ -1004,11 +1016,11 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /obj/item/proc/get_autopsy_descriptors() var/list/descriptors = list() descriptors += w_class_description() - if(sharp) + if(is_sharp()) descriptors += "sharp" - if(edge) + if(has_edge()) descriptors += "edged" - if(get_attack_force() >= 10 && !sharp && !edge) + if(get_attack_force() >= 10 && !is_sharp() && !has_edge()) descriptors += "heavy" if(material) descriptors += "made of [material.solid_name]" diff --git a/code/game/objects/items/_item_damage.dm b/code/game/objects/items/_item_damage.dm index 6d8803c33d3..c9103026205 100644 --- a/code/game/objects/items/_item_damage.dm +++ b/code/game/objects/items/_item_damage.dm @@ -50,7 +50,7 @@ /obj/item/bash(obj/item/weapon, mob/user) . = ..() - var/force = weapon.get_attack_force(user) + var/force = weapon.expend_attack_force(user) if(force >= 3 && .) user.setClickCooldown(weapon.attack_cooldown + weapon.w_class) take_damage(force, weapon.atom_damage_type) diff --git a/code/game/objects/items/_item_force.dm b/code/game/objects/items/_item_force.dm index 3ff63de0cc7..790d894e596 100644 --- a/code/game/objects/items/_item_force.dm +++ b/code/game/objects/items/_item_force.dm @@ -17,6 +17,13 @@ if(can_be_twohanded) . = round(. * _wielded_force_multiplier) +/obj/item/proc/expend_attack_force(mob/living/user) + . = get_attack_force(user) + var/list/item_effects = get_item_effects(IE_CAT_DAMAGE) + if(length(item_effects)) + for(var/decl/item_effect/damage_effect as anything in item_effects) + . = damage_effect.expend_attack_use(src, user, item_effects[damage_effect]) + /obj/item/proc/get_attack_force(mob/living/user) if(_base_attack_force <= 0 || (item_flags & ITEM_FLAG_NO_BLUDGEON)) return 0 @@ -31,7 +38,7 @@ return get_object_size() /obj/item/get_thrown_attack_force() - return round(get_attack_force() * _thrown_force_multiplier) + return round(expend_attack_force() * _thrown_force_multiplier) /obj/item/proc/get_base_attack_force() return _base_attack_force @@ -43,18 +50,6 @@ _cached_attack_force = null _base_attack_force = new_force -/obj/item/proc/set_edge(new_edge) - if(edge != new_edge) - edge = new_edge - return TRUE - return FALSE - -/obj/item/proc/set_sharp(new_sharp) - if(sharp != new_sharp) - sharp = new_sharp - return TRUE - return FALSE - /obj/item/proc/update_attack_force() // Get our base force. @@ -108,10 +103,17 @@ return _cached_attack_force // TODO: consider strength, athletics, mob size +// `dry_run` param used in grindstone modpack to avoid depleting sharpness on non-attacks. /mob/living/proc/modify_attack_force(obj/item/weapon, supplied_force, wield_mult) if(!istype(weapon) || !weapon.is_held_twohanded()) - return supplied_force - return round(supplied_force * wield_mult) + . = supplied_force + else + . = supplied_force * wield_mult + var/list/item_effects = weapon.get_item_effects(IE_CAT_DAMAGE) + if(length(item_effects)) + for(var/decl/item_effect/damage_effect as anything in item_effects) + . = damage_effect.modify_attack_damage(., weapon, src, item_effects[damage_effect]) + return round(.) // Debug proc - leaving in for future work. Linter hates protected var access so leave commented. /* @@ -142,7 +144,7 @@ item = new item - var/attk_force = item.get_attack_force() + var/attk_force = item.expend_attack_force() var/expected_material_mod = ((attk_force * item._weight_force_factor) + (attk_force * item._hardness_force_factor))/2 rows += jointext(list( @@ -155,11 +157,11 @@ (attk_force + expected_material_mod), (attk_force * item._wielded_force_multiplier), item.armor_penetration, - (item.sharp||item.edge) + (item.sharp|item.edge) ), "|") text2file(jointext(rows, "\n"), "weapon_stats_dump.csv") if(fexists("weapon_stats_dump.csv")) - direct_output(usr, ftp("weapon_stats_dump.csv", "weapon_stats_dump.csv")) + ftp_to(usr, "weapon_stats_dump.csv", "weapon_stats_dump.csv") to_chat(usr, "Done.") */ diff --git a/code/game/objects/items/_item_interactions.dm b/code/game/objects/items/_item_interactions.dm index b9efb6e61dc..6467b1d19d5 100644 --- a/code/game/objects/items/_item_interactions.dm +++ b/code/game/objects/items/_item_interactions.dm @@ -34,6 +34,7 @@ name = "Open Storage" expected_target_type = /atom incapacitation_flags = INCAPACITATION_DISRUPTED + examine_desc = "open $TARGET_THEIR$ storage" /decl/interaction_handler/storage_open/is_possible(atom/target, mob/user, obj/item/prop) . = ..() && (ishuman(user) || isrobot(user) || issmall(user)) && target?.storage diff --git a/code/game/objects/items/_item_reagents.dm b/code/game/objects/items/_item_reagents.dm index cf7a09c7a70..e664024c952 100644 --- a/code/game/objects/items/_item_reagents.dm +++ b/code/game/objects/items/_item_reagents.dm @@ -1,4 +1,4 @@ -/obj/item/proc/standard_dispenser_refill(var/mob/user, var/obj/structure/reagent_dispensers/target, skip_container_check = FALSE) // This goes into afterattack +/obj/item/proc/standard_dispenser_refill(mob/user, obj/structure/reagent_dispensers/target, skip_container_check = FALSE) // This goes into afterattack if(!istype(target) || (!skip_container_check && (target.atom_flags & ATOM_FLAG_OPEN_CONTAINER))) return FALSE @@ -64,12 +64,12 @@ return TRUE var/had_liquids = length(reagents.liquid_volumes) - var/trans = reagents.trans_to(target, amount) + var/transferred_amount = reagents.trans_to(target, amount) if(had_liquids) playsound(src, 'sound/effects/pour.ogg', 25, 1) else // Sounds more like pouring small pellets or dust. playsound(src, 'sound/effects/refill.ogg', 25, 1) - to_chat(user, SPAN_NOTICE("You transfer [trans] unit\s of the solution to \the [target]. \The [src] now contains [src.reagents.total_volume] units.")) + to_chat(user, SPAN_NOTICE("You transfer [transferred_amount] unit\s of the solution to \the [target]. \The [src] now contains [reagents.total_volume] unit\s.")) return TRUE diff --git a/code/game/objects/items/_item_sharpness.dm b/code/game/objects/items/_item_sharpness.dm new file mode 100644 index 00000000000..a37c1bf56fe --- /dev/null +++ b/code/game/objects/items/_item_sharpness.dm @@ -0,0 +1,21 @@ +/obj/item + VAR_PROTECTED/sharp = FALSE + VAR_PROTECTED/edge = FALSE + +/obj/item/is_sharp() + return sharp + +/obj/item/has_edge() + return edge + +/obj/item/proc/set_edge(new_edge) + if(edge != new_edge) + edge = new_edge + return TRUE + return FALSE + +/obj/item/proc/set_sharp(new_sharp) + if(sharp != new_sharp) + sharp = new_sharp + return TRUE + return FALSE diff --git a/code/game/objects/items/artifice/chain.dm b/code/game/objects/items/artifice/chain.dm new file mode 100644 index 00000000000..3a48abad475 --- /dev/null +++ b/code/game/objects/items/artifice/chain.dm @@ -0,0 +1,9 @@ +// Stub for forging. TODO crafting that uses chains. +/obj/item/chain + name = "chain" + name_prefix = "length of" + desc = "A flexible length of interconnected links forming a chain." + icon_state = ICON_STATE_WORLD + icon = 'icons/obj/items/chain.dmi' + material = /decl/material/solid/metal/iron + material_alteration = MAT_FLAG_ALTERATION_ALL diff --git a/code/game/objects/items/artifice/hook.dm b/code/game/objects/items/artifice/hook.dm new file mode 100644 index 00000000000..c20c77b1480 --- /dev/null +++ b/code/game/objects/items/artifice/hook.dm @@ -0,0 +1,8 @@ +// Stub for forging. TODO use for slapcrafting a fishing rod? +/obj/item/hook + name = "hook" + desc = "A small, sharp, curved object." + icon_state = ICON_STATE_WORLD + icon = 'icons/obj/items/hook.dmi' + material = /decl/material/solid/metal/iron + material_alteration = MAT_FLAG_ALTERATION_ALL diff --git a/code/game/objects/items/blades/_blade.dm b/code/game/objects/items/blades/_blade.dm index 60a42215399..19dbcbce1cd 100644 --- a/code/game/objects/items/blades/_blade.dm +++ b/code/game/objects/items/blades/_blade.dm @@ -15,9 +15,11 @@ slot_flags = SLOT_LOWER_BODY material = /decl/material/solid/metal/steel _base_attack_force = 10 + var/decl/material/hilt_material = /decl/material/solid/organic/wood/oak var/decl/material/guard_material = /decl/material/solid/organic/wood/oak var/decl/material/pommel_material = /decl/material/solid/organic/wood/oak + /// Cache var for blade material shine calculation. var/tmp/shine diff --git a/code/game/objects/items/blades/folding.dm b/code/game/objects/items/blades/folding.dm index 0b577d77ba1..98287a0ca7c 100644 --- a/code/game/objects/items/blades/folding.dm +++ b/code/game/objects/items/blades/folding.dm @@ -53,9 +53,8 @@ /obj/item/bladed/folding/update_attack_force() ..() - // TODO: check sharp/edge. - edge = open - sharp = open + set_edge(open) + set_sharp(open) if(open) w_class = open_item_size attack_verb = open_attack_verbs @@ -98,6 +97,7 @@ name = "Adjust Folding Knife" expected_target_type = /obj/item/bladed/folding interaction_flags = INTERACTION_NEEDS_INVENTORY | INTERACTION_NEEDS_PHYSICAL_INTERACTION + examine_desc = "adjust $TARGET_THEM$" /decl/interaction_handler/folding_knife/is_possible(atom/target, mob/user) . = ..() diff --git a/code/game/objects/items/blades/spear.dm b/code/game/objects/items/blades/spear.dm index 8659642ba91..2779646e0c8 100644 --- a/code/game/objects/items/blades/spear.dm +++ b/code/game/objects/items/blades/spear.dm @@ -3,8 +3,8 @@ desc = "A haphazardly-constructed yet still deadly weapon of ancient design." icon = 'icons/obj/items/bladed/spear.dmi' throw_speed = 3 - edge = 0 - sharp = 1 + edge = FALSE + sharp = TRUE attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") does_spin = FALSE abstract_type = /obj/item/bladed/polearm/spear diff --git a/code/game/objects/items/blades/spear_improvised.dm b/code/game/objects/items/blades/spear_improvised.dm index 2f1d0196aba..9f1a1da2425 100644 --- a/code/game/objects/items/blades/spear_improvised.dm +++ b/code/game/objects/items/blades/spear_improvised.dm @@ -34,7 +34,7 @@ hilt_material = /decl/material/solid/organic/wood/oak force_binding_color = COLOR_GREEN -/obj/item/bladed/polearm/spear/improvised/supermatter - material = /decl/material/solid/supermatter +/obj/item/bladed/polearm/spear/improvised/exotic_matter + material = /decl/material/solid/exotic_matter hilt_material = /decl/material/solid/organic/wood/ebony force_binding_color = COLOR_INDIGO diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index c9c67e53fc7..5a082bb9180 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -59,7 +59,7 @@ return FALSE name += " - [S.name]" - desc = "Blueprints of \the [S.name]. There is a \"Classified\" stamp and several coffee stains on it." + desc = "Blueprints of \the [S]. There is a \"Classified\" stamp and several coffee stains on it." valid_z_levels += S.map_z area_prefix = S.name return TRUE diff --git a/code/game/objects/items/books/manuals/engineering.dm b/code/game/objects/items/books/manuals/engineering.dm index 1dccd0022d3..bad39f2562c 100644 --- a/code/game/objects/items/books/manuals/engineering.dm +++ b/code/game/objects/items/books/manuals/engineering.dm @@ -26,13 +26,6 @@ title = "Particle Accelerator User's Guide" guide_decl = /datum/codex_entry/guide/particle_accelerator -/obj/item/book/manual/supermatter_engine - name = "supermatter engine reference manual" - icon = 'icons/obj/items/books/book_supermatter.dmi' - author = "Central Engineering Division" - title = "Supermatter Engine Operating Manual" - guide_decl = /datum/codex_entry/guide/supermatter - /obj/item/book/manual/rust_engine name = "fusion reactor reference Manual" icon_state = "bookMagazine" diff --git a/code/game/objects/items/chisel.dm b/code/game/objects/items/chisel.dm new file mode 100644 index 00000000000..8bdcf2ab1a4 --- /dev/null +++ b/code/game/objects/items/chisel.dm @@ -0,0 +1,12 @@ +// Stub for forging. TODO implement TOOL_CHISEL. +/obj/item/tool/chisel + name = "chisel" + desc = "A hard, sharpened tool used to chisel stone, wood or bone." + icon_state = ICON_STATE_WORLD + icon = 'icons/obj/items/tool/chisel.dmi' + material = /decl/material/solid/metal/steel + handle_material = /decl/material/solid/organic/plastic + binding_material = null + +/obj/item/tool/chisel/forged + handle_material = null diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 567039e9b51..f09470bc64e 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -147,7 +147,10 @@ // As it is, the effect can freely levitate over any open space. /obj/effect/dummy/chameleon/Move() . = ..() - if(. && isturf(loc) && loc.has_gravity() && !(locate(/obj/structure/catwalk) in loc) && !(locate(/obj/structure/lattice) in loc)) + if(!. || !isturf(loc) || !loc.has_gravity()) + return + var/turf/my_turf = loc + if(!my_turf.get_supporting_platform() && !(locate(/obj/structure/lattice) in loc)) disrupted() /datum/movement_handler/delay/chameleon_projector diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index c58b5bd7ae2..4508758e19d 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -377,6 +377,7 @@ var/global/list/all_gps_units = list() /decl/interaction_handler/gps_toggle name = "Toggle Tracking" expected_target_type = /obj/item/gps + examine_desc = "toggle GPS tracking" /decl/interaction_handler/gps_toggle/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/gps/G = target diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 3565259a08e..34115f49b63 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -110,7 +110,7 @@ if(!user.try_unequip(L)) return TRUE AddUses(1) - to_chat(user, "You insert \the [L.name] into \the [src]. You have [uses] light\s remaining.") + to_chat(user, "You insert \the [L] into \the [src]. You have [uses] light\s remaining.") qdel(L) return TRUE else diff --git a/code/game/objects/items/devices/paint_sprayer.dm b/code/game/objects/items/devices/paint_sprayer.dm index a9dbd8a0878..3c6f0868145 100644 --- a/code/game/objects/items/devices/paint_sprayer.dm +++ b/code/game/objects/items/devices/paint_sprayer.dm @@ -223,7 +223,7 @@ return FALSE if(!flooring.can_paint || F.is_floor_damaged()) - to_chat(user, SPAN_WARNING("\The [src] cannot paint \the [F.name].")) + to_chat(user, SPAN_WARNING("\The [src] cannot paint \the [F].")) return FALSE var/list/decal_data = decals[decal] @@ -456,6 +456,7 @@ name = "Change Color Preset" expected_target_type = /obj/item/paint_sprayer interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEEDS_INVENTORY + examine_desc = "change the color preset" /decl/interaction_handler/paint_sprayer_colour/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/paint_sprayer/sprayer = target diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index b83f35bca48..2f2506b6aa0 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -7,11 +7,27 @@ var/obj/item/tank/tank_one var/obj/item/tank/tank_two var/obj/item/assembly/attached_device - var/mob/attacher = null + var/weakref/attacher_ref = null var/valve_open = 0 var/toggle = 1 movable_flags = MOVABLE_FLAG_PROXMOVE +/obj/item/transfer_valve/Destroy() + if(!QDELETED(tank_one)) + QDEL_NULL(tank_one) + else + tank_one = null + if(!QDELETED(tank_two)) + QDEL_NULL(tank_two) + else + tank_two = null + if(!QDELETED(attached_device)) + QDEL_NULL(attached_device) + else + attached_device = null + attacher_ref = null + return ..() + /obj/item/transfer_valve/attackby(obj/item/item, mob/user) var/turf/location = get_turf(src) // For admin logs if(istype(item, /obj/item/tank)) @@ -56,7 +72,7 @@ global.bombers += "[key_name(user)] attached a [item] to a transfer valve." message_admins("[key_name_admin(user)] attached a [item] to a transfer valve. (JMP)") log_game("[key_name_admin(user)] attached a [item] to a transfer valve.") - attacher = user + attacher_ref = weakref(user) . = TRUE if(.) update_icon() @@ -189,6 +205,7 @@ var/area/A = get_area(bombturf) var/attacher_name = "" + var/mob/attacher = attacher_ref.resolve() if(!attacher) attacher_name = "Unknown" else diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm index f10c6c28f34..02f4eb078b7 100644 --- a/code/game/objects/items/devices/uplink.dm +++ b/code/game/objects/items/devices/uplink.dm @@ -69,7 +69,7 @@ do var/datum/uplink_random_selection/uplink_selection = get_uplink_random_selection_by_type(/datum/uplink_random_selection/blacklist) new_discount_item = uplink_selection.get_random_item(INFINITY, src) - // Ensures we only only get items for which we get an actual discount and that this particular uplink can actually view (can buy would risk near-infinite loops). + // Ensures we only get items for which we get an actual discount and that this particular uplink can actually view (can buy would risk near-infinite loops). while(is_improper_item(new_discount_item, discount_amount)) if(!new_discount_item) return diff --git a/code/game/objects/items/horseshoe.dm b/code/game/objects/items/horseshoe.dm new file mode 100644 index 00000000000..089d3c9cc4b --- /dev/null +++ b/code/game/objects/items/horseshoe.dm @@ -0,0 +1,8 @@ +// Stub for forging. TODO implement shoes on honse. +/obj/item/horseshoe + name = "horseshoe" + desc = "A curved length of metal, usually nailed to a horse's hoof. May bring luck." + icon_state = ICON_STATE_WORLD + icon = 'icons/obj/items/horseshoe.dmi' + material = /decl/material/solid/metal/iron + material_alteration = MAT_FLAG_ALTERATION_ALL diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index b91bf589f2c..53ec5ef987f 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -145,7 +145,7 @@ name = "toy sword" desc = "A cheap, plastic replica of an energy sword. Realistic sounds! Ages 8 and up." sharp = FALSE - edge = FALSE + edge = FALSE attack_verb = list("hit") material = /decl/material/solid/organic/plastic active_hitsound = 'sound/weapons/genhit.ogg' @@ -497,8 +497,8 @@ desc = "An arcane weapon (made of foam) wielded by the followers of the hit Saturday morning cartoon \"King Nursee and the Acolytes of Heroism\"." icon = 'icons/obj/items/weapon/swords/cult.dmi' material = /decl/material/solid/organic/plastic/foam - edge = 0 - sharp = 0 + edge = FALSE + sharp = FALSE /obj/item/inflatable_duck //#TODO: Move under obj/item/toy ? name = "inflatable duck" diff --git a/code/game/objects/items/weapons/broom.dm b/code/game/objects/items/weapons/broom.dm index b19ef3bf316..aaa799ed5a8 100644 --- a/code/game/objects/items/weapons/broom.dm +++ b/code/game/objects/items/weapons/broom.dm @@ -43,15 +43,23 @@ // Sweep up dirt. if(isturf(A)) + var/turf/cleaning = A + var/dirty = cleaning.get_dirt() if(dirty > 10) // a small amount so that you can't sweep immediately after someone walks somewhere user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.visible_message(SPAN_NOTICE("\The [user] sweeps \the [A].")) playsound(A, "sweeping", 100, TRUE) cleaning.remove_dirt(min(dirty, rand(20,30))) - else - to_chat(user, SPAN_WARNING("\The [cleaning] is not in need of sweeping.")) + return TRUE + + var/obj/effect/footprints/prints = locate() in cleaning + if(prints) + user.visible_message(SPAN_NOTICE("\The [user] sweeps away the footprints.")) + return TRUE + + to_chat(user, SPAN_WARNING("\The [cleaning] is not in need of sweeping.")) return TRUE // Sweep up dry spills. diff --git a/code/game/objects/items/weapons/circuitboards/computer/air_management.dm b/code/game/objects/items/weapons/circuitboards/computer/air_management.dm index fe637a0b0dd..0498af95839 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/air_management.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/air_management.dm @@ -7,19 +7,6 @@ var/sensor_name var/list/sensor_information = list() -/obj/item/stock_parts/circuitboard/air_management/supermatter_core - name = "circuitboard (core control)" - build_path = /obj/machinery/computer/air_control/supermatter_core - frequency = 1438 - var/input_tag - var/output_tag - - var/list/input_info = list() - var/list/output_info = list() - - var/input_flow_setting = 700 - var/pressure_setting = 100 - /obj/item/stock_parts/circuitboard/air_management/injector_control name = "circuitboard (injector control)" build_path = /obj/machinery/computer/air_control/fuel_injection @@ -42,18 +29,6 @@ C.sensor_info = sensor_information.Copy() return 1 -/obj/item/stock_parts/circuitboard/air_management/supermatter_core/construct(var/obj/machinery/computer/air_control/supermatter_core/SC) - if(..(SC)) - SC.input_tag = input_tag - SC.output_tag = output_tag - - SC.input_info = input_info.Copy() - SC.output_info = output_info.Copy() - - SC.input_flow_setting = input_flow_setting - SC.pressure_setting = input_flow_setting - return 1 - /obj/item/stock_parts/circuitboard/air_management/injector_control/construct(var/obj/machinery/computer/air_control/fuel_injection/FI) if(..(FI)) FI.device_tag = device_tag @@ -75,18 +50,6 @@ sensor_information = C.sensor_info.Copy() return 1 -/obj/item/stock_parts/circuitboard/air_management/supermatter_core/deconstruct(var/obj/machinery/computer/air_control/supermatter_core/SC) - if(..(SC)) - input_tag = SC.input_tag - output_tag = SC.output_tag - - input_info = SC.input_info.Copy() - output_info = SC.output_info.Copy() - - input_flow_setting = SC.input_flow_setting - pressure_setting = SC.input_flow_setting - return 1 - /obj/item/stock_parts/circuitboard/air_management/injector_control/deconstruct(var/obj/machinery/computer/air_control/fuel_injection/FI) if(..(FI)) device_tag = FI.device_tag diff --git a/code/game/objects/items/weapons/circuitboards/machinery/chemistry.dm b/code/game/objects/items/weapons/circuitboards/machinery/chemistry.dm index 44377add2c0..1d9cccaf50b 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/chemistry.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/chemistry.dm @@ -1,5 +1,5 @@ /obj/item/stock_parts/circuitboard/reagent_heater - name = "circuitboard (chemical heater)" + name = "circuitboard (hotplate)" build_path = /obj/machinery/reagent_temperature board_type = "machine" origin_tech = @'{"powerstorage":2,"engineering":1}' diff --git a/code/game/objects/items/weapons/ecigs.dm b/code/game/objects/items/weapons/ecigs.dm index dc05320c090..0a55a70a950 100644 --- a/code/game/objects/items/weapons/ecigs.dm +++ b/code/game/objects/items/weapons/ecigs.dm @@ -22,6 +22,10 @@ ec_cartridge = new cartridge_type(src) . = ..() +/obj/item/clothing/mask/smokable/ecig/Destroy() + QDEL_NULL(ec_cartridge) + return ..() + /obj/item/clothing/mask/smokable/ecig/setup_power_supply(loaded_cell_type, accepted_cell_type, power_supply_extension_type, charge_value) loaded_cell_type = loaded_cell_type || /obj/item/cell/device/standard accepted_cell_type = accepted_cell_type || /obj/item/cell/device diff --git a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm index a9fdaf0deab..19054df59bd 100644 --- a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm +++ b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm @@ -18,5 +18,4 @@ /obj/item/grenade/anti_photon/proc/finish() set_light(10, 10, rgb(rand(64,255), rand(64,255), rand(64,255))) playsound(loc, 'sound/effects/bang.ogg', 50, 1, 5) - sleep(1 SECOND) - qdel(src) \ No newline at end of file + QDEL_IN(src, 1 SECOND) \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/decompiler.dm b/code/game/objects/items/weapons/grenades/decompiler.dm index e3918357a66..36592f51da2 100644 --- a/code/game/objects/items/weapons/grenades/decompiler.dm +++ b/code/game/objects/items/weapons/grenades/decompiler.dm @@ -5,7 +5,7 @@ origin_tech = @'{"materials":3,"magnets":2,"exoticmatter":3}' matter = list( /decl/material/solid/phoron = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/supermatter = MATTER_AMOUNT_TRACE + /decl/material/solid/exotic_matter = MATTER_AMOUNT_TRACE ) /obj/item/grenade/decompiler/detonate() diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index d16532308ee..1f91dc035d8 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -88,16 +88,16 @@ switch(det_time) if (1) det_time = 10 - to_chat(user, SPAN_NOTICE("You set the \the [src] for 1 second detonation time.")) + to_chat(user, SPAN_NOTICE("You set \the [src] for 1 second detonation time.")) if (10) det_time = 30 - to_chat(user, SPAN_NOTICE("You set the \the [src] for 3 second detonation time.")) + to_chat(user, SPAN_NOTICE("You set \the [src] for 3 second detonation time.")) if (30) det_time = 50 - to_chat(user, SPAN_NOTICE("You set the \the [src] for 5 second detonation time.")) + to_chat(user, SPAN_NOTICE("You set \the [src] for 5 second detonation time.")) if (50) det_time = 1 - to_chat(user, SPAN_NOTICE("You set the \the [src] for instant detonation.")) + to_chat(user, SPAN_NOTICE("You set \the [src] for instant detonation.")) add_fingerprint(user) return TRUE return ..() diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 8df73ff611f..a636763ac07 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -1,5 +1,5 @@ /obj/item/grenade/spawnergrenade - desc = "It is set to detonate in 5 seconds. It will unleash unleash an unspecified anomaly into the vicinity." + desc = "It is set to detonate in 5 seconds. It will unleash an unspecified anomaly into the vicinity." name = "delivery grenade" icon = 'icons/obj/items/grenades/delivery.dmi' origin_tech = @'{"materials":3,"magnets":4}' diff --git a/code/game/objects/items/weapons/material/folding.dm b/code/game/objects/items/weapons/material/folding.dm index 46a631afe6d..38cfa3a2476 100644 --- a/code/game/objects/items/weapons/material/folding.dm +++ b/code/game/objects/items/weapons/material/folding.dm @@ -29,15 +29,14 @@ /obj/item/knife/folding/update_attack_force() ..() if(open) - // TODO: check sharp/edge. - edge = TRUE - sharp = TRUE + set_edge(TRUE) + set_sharp(TRUE) w_class = ITEM_SIZE_NORMAL attack_verb = list("slashed", "stabbed") ..() else - edge = initial(edge) - sharp = initial(sharp) + set_edge(initial(edge)) + set_sharp(initial(sharp)) w_class = initial(w_class) attack_verb = closed_attack_verbs diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index 941dd4fd8cc..8040747110e 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -1,8 +1,8 @@ /obj/item/harpoon name = "harpoon" desc = "A short throwing spear with a deep barb, specifically designed to embed itself in its target." - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE icon = 'icons/obj/items/weapon/harpoon.dmi' icon_state = "harpoon" item_state = "harpoon" @@ -47,8 +47,8 @@ desc = "A sharp and curved blade on a long fibremetal handle, this tool makes it easy to reap what you sow." icon = 'icons/obj/items/tool/scythe.dmi' icon_state = ICON_STATE_WORLD - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE throw_speed = 1 throw_range = 3 w_class = ITEM_SIZE_HUGE diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index 7ff92bf32ad..4150bd92532 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -6,8 +6,8 @@ desc = "Made of nothing. How does this even exist?" // set based on material, if this desc is visible it's a bug (shards default to being made of glass) icon_state = "large" randpixel = 8 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE w_class = ITEM_SIZE_SMALL item_state = "shard-glass" attack_verb = list("stabbed", "slashed", "sliced", "cut") @@ -87,31 +87,33 @@ if(!isliving(AM)) return - var/mob/living/M = AM - if(M.buckled) //wheelchairs, office chairs, rollerbeds + var/mob/living/victim = AM + if(victim.buckled) //wheelchairs, office chairs, rollerbeds + return + if(victim.immune_to_floor_hazards()) return playsound(src.loc, 'sound/effects/glass_step.ogg', 50, 1) // not sure how to handle metal shards with sounds - var/decl/species/walker_species = M.get_species() - if(walker_species && (walker_species.get_shock_vulnerability(M) < 0.5 || (walker_species.species_flags & (SPECIES_FLAG_NO_EMBED|SPECIES_FLAG_NO_MINOR_CUT)))) //Thick skin. + var/decl/species/walker_species = victim.get_species() + if(walker_species?.species_flags & (SPECIES_FLAG_NO_EMBED|SPECIES_FLAG_NO_MINOR_CUT)) //Thick skin. return - var/obj/item/shoes = M.get_equipped_item(slot_shoes_str) - var/obj/item/suit = M.get_equipped_item(slot_wear_suit_str) + var/obj/item/shoes = victim.get_equipped_item(slot_shoes_str) + var/obj/item/suit = victim.get_equipped_item(slot_wear_suit_str) if(shoes || (suit && (suit.body_parts_covered & SLOT_FEET))) return var/list/check = list(BP_L_FOOT, BP_R_FOOT) while(check.len) var/picked = pick_n_take(check) - var/obj/item/organ/external/affecting = GET_EXTERNAL_ORGAN(M, picked) + var/obj/item/organ/external/affecting = GET_EXTERNAL_ORGAN(victim, picked) if(!affecting || BP_IS_PROSTHETIC(affecting)) continue - to_chat(M, SPAN_DANGER("You step on \the [src]!")) + to_chat(victim, SPAN_DANGER("You step on \the [src]!")) affecting.take_external_damage(5, 0) if(affecting.can_feel_pain()) - SET_STATUS_MAX(M, STAT_WEAK, 3) + SET_STATUS_MAX(victim, STAT_WEAK, 3) return //Prevent the shard from being allowed to shatter diff --git a/code/game/objects/items/weapons/material/stick.dm b/code/game/objects/items/weapons/material/stick.dm index a124a6644bc..d752ca73f46 100644 --- a/code/game/objects/items/weapons/material/stick.dm +++ b/code/game/objects/items/weapons/material/stick.dm @@ -16,9 +16,9 @@ /obj/item/stick/attackby(obj/item/W, mob/user) - if(W.sharp && W.edge && !sharp) + if(W.is_sharp() && W.has_edge() && !sharp) user.visible_message("[user] sharpens [src] with [W].", "You sharpen [src] using [W].") - sharp = 1 //Sharpen stick + set_sharp(TRUE) SetName("sharpened " + name) update_attack_force() return TRUE diff --git a/code/game/objects/items/weapons/material/swiss.dm b/code/game/objects/items/weapons/material/swiss.dm index 81f5700ebd1..44f237dc853 100644 --- a/code/game/objects/items/weapons/material/swiss.dm +++ b/code/game/objects/items/weapons/material/swiss.dm @@ -102,8 +102,8 @@ else siemens_coefficient = initial(siemens_coefficient) else - edge = initial(edge) - sharp = initial(sharp) + set_edge(initial(edge)) + set_sharp(initial(sharp)) attack_verb = closed_attack_verbs siemens_coefficient = initial(siemens_coefficient) diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index 19a54e6ac77..094dd9767a2 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -7,8 +7,8 @@ w_class = ITEM_SIZE_LARGE item_flags = ITEM_FLAG_IS_WEAPON armor_penetration = 10 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' base_parry_chance = 50 @@ -22,13 +22,13 @@ /obj/item/edge/set_edge(new_edge) . = ..() - if(. && !edge) + if(. && !has_edge()) attack_verb = list("attacked", "stabbed", "jabbed", "smacked", "prodded") hitsound = 'sound/weapons/pierce.ogg' /obj/item/sword/set_sharp(new_sharp) . = ..() - if(. && !sharp) + if(. && !is_sharp()) attack_verb = list("attacked", "smashed", "jabbed", "smacked", "prodded", "bonked") hitsound = "chop" diff --git a/code/game/objects/items/weapons/material/thrown.dm b/code/game/objects/items/weapons/material/thrown.dm index 427fffbe102..7e878a0ac20 100644 --- a/code/game/objects/items/weapons/material/thrown.dm +++ b/code/game/objects/items/weapons/material/thrown.dm @@ -6,8 +6,8 @@ randpixel = 12 throw_speed = 10 throw_range = 15 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE material = /decl/material/solid/metal/steel material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME item_flags = ITEM_FLAG_IS_WEAPON diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 47de3c87b7b..bfc53022ae6 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -13,8 +13,6 @@ _base_attack_force = 3 // bonk throw_speed = 1 throw_range = 5 - sharp = 0 - edge = 0 armor_penetration = 0 material = /decl/material/solid/metal/steel @@ -90,8 +88,8 @@ if(active) obj_flags |= OBJ_FLAG_NO_STORAGE - sharp = active_sharp - edge = active_edge + set_sharp(active_sharp) + set_edge(active_edge) base_parry_chance = active_parry_chance armor_penetration = active_armour_pen hitsound = active_hitsound @@ -105,8 +103,8 @@ else obj_flags &= ~OBJ_FLAG_NO_STORAGE - sharp = initial(sharp) - edge = initial(edge) + set_sharp(initial(sharp)) + set_edge(initial(edge)) base_parry_chance = initial(base_parry_chance) armor_penetration = initial(armor_penetration) hitsound = initial(hitsound) diff --git a/code/game/objects/items/weapons/melee/energy_axe.dm b/code/game/objects/items/weapons/melee/energy_axe.dm index e70d70971f5..e7845e08985 100644 --- a/code/game/objects/items/weapons/melee/energy_axe.dm +++ b/code/game/objects/items/weapons/melee/energy_axe.dm @@ -12,8 +12,8 @@ origin_tech = @'{"magnets":3,"combat":4}' active_attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") inactive_attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE base_parry_chance = 30 active_parry_chance = 30 melee_accuracy_bonus = 15 diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index 70cb57c3f47..e46bd9dddc4 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -12,7 +12,11 @@ /decl/material/solid/organic/cloth = MATTER_AMOUNT_SECONDARY, ) var/mopspeed = 40 - var/list/moppable_types = list( + var/static/list/moppable_types + +/obj/item/mop/proc/populate_moppable_types() + moppable_types = list( + /turf/floor, /obj/effect/decal/cleanable, /obj/structure/catwalk ) @@ -20,6 +24,8 @@ /obj/item/mop/Initialize() . = ..() initialize_reagents() + if(!moppable_types) + populate_moppable_types() /obj/item/mop/initialize_reagents(populate = TRUE) create_reagents(30) @@ -27,45 +33,40 @@ /obj/item/mop/afterattack(atom/A, mob/user, proximity) if(!proximity) - return + return ..() + var/turf/moppable_turf = get_turf(A) + if(!istype(moppable_turf)) + return ..() - var/moppable - if(isturf(A)) - var/turf/T = A - if(T?.reagents?.total_volume > 0) - if(T.reagents.total_volume > FLUID_SHALLOW) + if(moppable_turf?.reagents?.total_volume > 0) + if(moppable_turf.reagents.total_volume > FLUID_SHALLOW) + to_chat(user, SPAN_WARNING("There is too much water here to be mopped up.")) + return TRUE + user.visible_message(SPAN_NOTICE("\The [user] begins to mop up \the [moppable_turf].")) + if(do_after(user, 40, moppable_turf) && !QDELETED(moppable_turf)) + if(moppable_turf.reagents?.total_volume > FLUID_SHALLOW) to_chat(user, SPAN_WARNING("There is too much water here to be mopped up.")) else - user.visible_message(SPAN_NOTICE("\The [user] begins to mop up \the [T].")) - if(do_after(user, 40, T) && !QDELETED(T)) - if(T.reagents?.total_volume > FLUID_SHALLOW) - to_chat(user, SPAN_WARNING("There is too much water here to be mopped up.")) - else - to_chat(user, SPAN_NOTICE("You have finished mopping!")) - T.reagents?.clear_reagents() - return - moppable = TRUE - - else if(is_type_in_list(A,moppable_types)) - moppable = TRUE - - if(moppable) - if(reagents.total_volume < 1) - to_chat(user, SPAN_WARNING("Your mop is dry!")) - return - var/turf/T = get_turf(A) - if(!T) - return - - var/trans_amt = FLUID_QDEL_POINT - if(user.check_intent(I_FLAG_HARM)) - trans_amt = round(FLUID_PUDDLE * 0.25) - user.visible_message(SPAN_DANGER("\The [user] begins to aggressively mop \the [T]!")) - else - user.visible_message(SPAN_NOTICE("\The [user] begins to clean \the [T].")) - if(do_after(user, mopspeed, T) && reagents?.total_volume) - reagents.splash(T, trans_amt) - to_chat(user, SPAN_NOTICE("You have finished mopping!")) + to_chat(user, SPAN_NOTICE("You have finished mopping!")) + moppable_turf.reagents?.clear_reagents() + return TRUE + + if(!is_type_in_list(A, moppable_types)) + return ..() + + if(reagents?.total_volume < 1) + to_chat(user, SPAN_WARNING("\The [src] is dry!")) + return TRUE + + if(user.check_intent(I_FLAG_HARM)) + user.visible_message(SPAN_DANGER("\The [user] begins to aggressively mop \the [moppable_turf]!")) + else + user.visible_message(SPAN_NOTICE("\The [user] begins to clean \the [moppable_turf].")) + if(do_after(user, mopspeed, moppable_turf) && reagents?.total_volume) + reagents.touch_turf(moppable_turf) + reagents.remove_any(1) + to_chat(user, SPAN_NOTICE("You have finished mopping!")) + return TRUE /obj/effect/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/mop) || istype(I, /obj/item/soap)) diff --git a/code/game/objects/items/weapons/secrets_disk.dm b/code/game/objects/items/weapons/secrets_disk.dm index ee152b86151..ec806c4132f 100644 --- a/code/game/objects/items/weapons/secrets_disk.dm +++ b/code/game/objects/items/weapons/secrets_disk.dm @@ -13,19 +13,39 @@ This one has a lengthy legal label on it denoting it the private, copyrighted property of the Expeditionary Corps Organisation." req_access = list(access_rd) +/obj/item/disk/secret_project/proc/get_secret_project_codenames() + var/static/list/codenames = list( + "gamma", "delta", "epsilon", "zeta", "theta", "lambda", "omicron", "sigma", "tau", + "upsilon", "omega", "echelon", "prism", "calypso", "bernoulli", "harmony", "nyx", "fresnel" + ) + +/obj/item/disk/secret_project/proc/get_secret_project_types() + var/static/list/types = list( + "an experimental design for", + "a blueprint to build", + "a long set of theoretical formulas detailing the functioning of" + ) + return types + +/obj/item/disk/secret_project/proc/get_secret_project_nouns() + var/static/list/nouns = list( + "a superluminal artillery cannon", "a fusion engine", "an atmospheric scrubber",\ + "a human cloning pod", "a microwave oven", "a wormhole generator", "a laser carbine", "an energy pistol",\ + "a wormhole", "a teleporter", "a huge mining drill", "a strange spacecraft", "a space station",\ + "a sleek-looking fighter spacecraft", "a ballistic rifle", "an energy sword", "an inanimate carbon rod" + ) + return nouns + +/obj/item/disk/secret_project/proc/get_secret_project_descriptors() + var/static/list/descriptors = list( + "that is extremely powerful", "which is highly efficient", "which is incredibly compact", "created by aliens", + "that runs off of an exotic form of matter", "that runs off of hydrogen gas", "that just looks really cool" + ) + /obj/item/disk/secret_project/Initialize() . = ..() - var/codename = pick("gamma", "delta", "epsilon", "zeta", "theta", "lambda", "omicron", "sigma", "tau",\ - "upsilon", "omega", "echelon", "prism", "calypso", "bernoulli", "harmony", "nyx", "fresnel") - name = "'[codename]' project data disk" - subject = pick("an experimental design for", "a blueprint to build",\ - "a long set of theoretical formulas detailing the functioning of") - subject += " " + pick("a superluminal artillery cannon", "a supermatter engine", "a fusion engine", "an atmospheric scrubber",\ - "a human cloning pod", "a microwave oven", "a wormhole generator", "a laser carbine", "an energy pistol",\ - "a wormhole", "a teleporter", "a huge mining drill", "a strange spacecraft", "a space station",\ - "a sleek-looking fighter spacecraft", "a ballistic rifle", "an energy sword", "an inanimate carbon rod") - subject += " " + pick("that is extremely powerful", "which is highly efficient", "which is incredibly compact",\ - "that runs off of an exotic form of matter", "that runs off of hydrogen gas", "created by aliens", "that just looks really cool") + name = "'[pick(get_secret_project_codenames())]' project data disk" + subject = "[pick(get_secret_project_types())] [pick(get_secret_project_nouns())] [pick(get_secret_project_descriptors())]" /obj/item/disk/secret_project/examine(mob/user) . = ..() diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm deleted file mode 100644 index 8ab9f986e6b..00000000000 --- a/code/game/objects/items/weapons/shields.dm +++ /dev/null @@ -1,223 +0,0 @@ -//** Shield Helpers -//These are shared by various items that have shield-like behaviour - -//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. -/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) - //check attack direction - var/attack_dir = 0 //direction from the user to the source of the attack - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - attack_dir = get_dir(get_turf(user), P.starting) - else if(attacker) - attack_dir = get_dir(get_turf(user), get_turf(attacker)) - else if(damage_source) - attack_dir = get_dir(get_turf(user), get_turf(damage_source)) - - if(!(attack_dir && (attack_dir & bad_arc))) - return 1 - return 0 - -/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) - //parry only melee attacks - if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = user.dir && global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/shield - name = "abstract shield" - abstract_type = /obj/item/shield - var/base_block_chance = 60 - -/obj/item/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - //block as long as they are not directly behind us - var/bad_arc = user.dir && global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - var/block_chance = get_block_chance(user, damage, damage_source, attacker) - if(attacker) - block_chance = max(0, block_chance - 10 * attacker.get_skill_difference(SKILL_COMBAT, user)) - if(prob(block_chance)) - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - if(max_health != ITEM_HEALTH_NO_DAMAGE) - take_damage(damage) - return 1 - return 0 - -/obj/item/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - return base_block_chance - -/obj/item/shield/riot - name = "riot shield" - desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder." - icon = 'icons/obj/items/shield/riot.dmi' - icon_state = ICON_STATE_WORLD - obj_flags = OBJ_FLAG_CONDUCTIBLE - slot_flags = SLOT_BACK - throw_speed = 1 - throw_range = 4 - w_class = ITEM_SIZE_HUGE - origin_tech = @'{"materials":2}' - material = /decl/material/solid/fiberglass - matter = list(/decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT) - attack_verb = list("shoved", "bashed") - var/cooldown = 0 //shield bash cooldown. based on world.time - var/max_block = 15 - var/can_block_lasers = FALSE - -/obj/item/shield/riot/handle_shield(mob/user) - . = ..() - if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) - -/obj/item/shield/riot/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - //plastic shields do not stop bullets or lasers, even in space. Will block beanbags, rubber bullets, and stunshots just fine though. - if(is_sharp(P) && damage >= max_block) - return 0 - if(istype(P, /obj/item/projectile/beam) && (!can_block_lasers || (P.armor_penetration >= max_block))) - return 0 - return base_block_chance - -/obj/item/shield/riot/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/baton)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - return TRUE - else - return ..() - -/obj/item/shield/riot/metal - name = "plasteel combat shield" - icon = 'icons/obj/items/shield/metal.dmi' - icon_state = ICON_STATE_WORLD - obj_flags = OBJ_FLAG_CONDUCTIBLE - slot_flags = SLOT_BACK - throw_range = 3 - w_class = ITEM_SIZE_HUGE - material = /decl/material/solid/metal/plasteel - max_block = 50 - can_block_lasers = TRUE - slowdown_general = 1.5 - _base_attack_force = 6 - -/obj/item/shield/riot/metal/security //A cosmetic difference. - icon = 'icons/obj/items/shield/metal_security.dmi' - -/obj/item/shield/buckler - name = "buckler" - desc = "A wooden buckler used to block sharp things from entering your body back in the day." - icon = 'icons/obj/items/shield/buckler.dmi' - icon_state = "buckler" - slot_flags = SLOT_BACK - base_block_chance = 60 - throw_speed = 10 - throw_range = 20 - w_class = ITEM_SIZE_HUGE - origin_tech = @'{"materials":1}' - material = /decl/material/solid/metal/steel - matter = list(/decl/material/solid/organic/wood/oak = MATTER_AMOUNT_REINFORCEMENT) - attack_verb = list("shoved", "bashed") - _base_attack_force = 8 - max_health = 250 - -/obj/item/shield/buckler/handle_shield(mob/user) - . = ..() - if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) - -/obj/item/shield/buckler/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - if(istype(damage_source, /obj/item/projectile/bullet)) - return 0 //No blocking bullets, I'm afraid. - return base_block_chance - -/* - * Energy Shield - */ - -/obj/item/shield/energy - name = "energy combat shield" - desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." - icon = 'icons/obj/items/shield/e_shield.dmi' - icon_state = "eshield0" // eshield1 for expanded - obj_flags = OBJ_FLAG_CONDUCTIBLE - throw_speed = 1 - throw_range = 4 - w_class = ITEM_SIZE_SMALL - origin_tech = @'{"materials":4,"magnets":3,"esoteric":4}' - attack_verb = list("shoved", "bashed") - material = /decl/material/solid/metal/titanium - matter = list( - /decl/material/solid/fiberglass = MATTER_AMOUNT_SECONDARY, - /decl/material/solid/metal/gold = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/silicon = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/gemstone/diamond = MATTER_AMOUNT_TRACE, - ) - _base_attack_force = 3 - var/active = 0 - var/shield_light_color = "#006aff" - -/obj/item/shield/energy/Initialize() - set_extension(src, /datum/extension/base_icon_state, copytext(initial(icon_state), 1, length(initial(icon_state)))) - . = ..() - update_icon() - -/obj/item/shield/energy/handle_shield(mob/user) - if(!active) - return 0 //turn it on first! - . = ..() - - if(.) - spark_at(user.loc, amount=5) - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - -/obj/item/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) - return (base_block_chance - round(damage / 2.5)) //block bullets and beams using the old block chance - return base_block_chance - -/obj/item/shield/energy/attack_self(mob/user) - if(user.has_genetic_condition(GENE_COND_CLUMSY) && prob(50)) - to_chat(user, SPAN_DANGER("You beat yourself in the head with [src].")) - if(isliving(user)) - var/mob/living/M = user - M.take_organ_damage(5, 0) - active = !active - if (active) - set_base_attack_force(10) - update_icon() - w_class = ITEM_SIZE_HUGE - playsound(user, 'sound/weapons/saberon.ogg', 50, 1) - to_chat(user, SPAN_NOTICE("\The [src] is now active.")) - - else - set_base_attack_force(3) - update_icon() - w_class = ITEM_SIZE_SMALL - playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) - to_chat(user, SPAN_NOTICE("\The [src] can now be concealed.")) - - if(ishuman(user)) - var/mob/living/human/H = user - H.update_inhand_overlays() - - add_fingerprint(user) - return - -/obj/item/shield/energy/on_update_icon() - . = ..() - var/datum/extension/base_icon_state/base_name = get_extension(src, /datum/extension/base_icon_state) - icon_state = "[base_name.base_icon_state][active]" //Replace 0 with current state - if(active) - set_light(1.5, 1.5, shield_light_color) - else - set_light(0) \ No newline at end of file diff --git a/code/game/objects/items/weapons/shields/_shield.dm b/code/game/objects/items/weapons/shields/_shield.dm new file mode 100644 index 00000000000..6da8605ec6b --- /dev/null +++ b/code/game/objects/items/weapons/shields/_shield.dm @@ -0,0 +1,54 @@ +//** Shield Helpers +//These are shared by various items that have shield-like behaviour + +//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. +/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) + //check attack direction + var/attack_dir = 0 //direction from the user to the source of the attack + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + attack_dir = get_dir(get_turf(user), P.starting) + else if(attacker) + attack_dir = get_dir(get_turf(user), get_turf(attacker)) + else if(damage_source) + attack_dir = get_dir(get_turf(user), get_turf(damage_source)) + + if(!(attack_dir && (attack_dir & bad_arc))) + return 1 + return 0 + +/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) + //parry only melee attacks + if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = user.dir && global.reverse_dir[user.dir] //arc of directions from which we cannot block + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + +/obj/item/shield + name = "abstract shield" + abstract_type = /obj/item/shield + var/base_block_chance = 60 + +/obj/item/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(user.incapacitated()) + return 0 + //block as long as they are not directly behind us + var/bad_arc = user.dir && global.reverse_dir[user.dir] //arc of directions from which we cannot block + if(check_shield_arc(user, bad_arc, damage_source, attacker)) + var/block_chance = get_block_chance(user, damage, damage_source, attacker) + if(attacker) + block_chance = max(0, block_chance - 10 * attacker.get_skill_difference(SKILL_COMBAT, user)) + if(prob(block_chance)) + user.visible_message("\The [user] blocks [attack_text] with \the [src]!") + if(max_health != ITEM_HEALTH_NO_DAMAGE) + take_damage(damage) + return 1 + return 0 + +/obj/item/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + return base_block_chance diff --git a/code/game/objects/items/weapons/shields/shield_crafted.dm b/code/game/objects/items/weapons/shields/shield_crafted.dm new file mode 100644 index 00000000000..5d892688b0a --- /dev/null +++ b/code/game/objects/items/weapons/shields/shield_crafted.dm @@ -0,0 +1,38 @@ +/obj/item/shield/crafted + slot_flags = SLOT_BACK + base_block_chance = 60 + throw_speed = 10 + throw_range = 20 + w_class = ITEM_SIZE_HUGE + origin_tech = @'{"materials":1}' + abstract_type = /obj/item/shield/crafted + icon_state = ICON_STATE_WORLD + attack_verb = list("shoved", "bashed") + _base_attack_force = 8 + max_health = 250 + material = /decl/material/solid/organic/wood/oak + material_alteration = MAT_FLAG_ALTERATION_ALL + var/wooden_icon + var/decl/material/reinforcement_material = /decl/material/solid/metal/iron + +/obj/item/shield/crafted/set_material(new_material) + . = ..() + if(wooden_icon) + if(istype(material, /decl/material/solid/organic/wood)) + set_icon(wooden_icon) + else + set_icon(initial(icon)) + update_icon() + +/obj/item/shield/crafted/Initialize(ml, material_key, reinf_material_key) + if(reinf_material_key) + reinforcement_material = reinf_material_key + if(ispath(reinforcement_material)) + reinforcement_material = GET_DECL(reinforcement_material) + LAZYSET(matter, reinforcement_material.type, MATTER_AMOUNT_REINFORCEMENT) + . = ..() + +/obj/item/shield/crafted/on_update_icon() + . = ..() + if(istype(reinforcement_material)) + add_overlay(overlay_image(icon, "[icon_state]-reinforcement", reinforcement_material.color, RESET_COLOR)) diff --git a/code/game/objects/items/weapons/shields/shield_crafted_buckler.dm b/code/game/objects/items/weapons/shields/shield_crafted_buckler.dm new file mode 100644 index 00000000000..6d78daef790 --- /dev/null +++ b/code/game/objects/items/weapons/shields/shield_crafted_buckler.dm @@ -0,0 +1,17 @@ +/obj/item/shield/crafted/buckler + name = "buckler" + desc = "A small, round shield used to block sharp things from entering your body." + icon = 'icons/obj/items/shield/buckler_metal.dmi' + wooden_icon = 'icons/obj/items/shield/buckler_wood.dmi' + +/obj/item/shield/crafted/buckler/improvised + name_prefix = "improvised" + +/obj/item/shield/crafted/buckler/handle_shield(mob/user) + . = ..() + if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) + +/obj/item/shield/crafted/buckler/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + if(istype(damage_source, /obj/item/projectile/bullet)) + return 0 //No blocking bullets, I'm afraid. + return base_block_chance diff --git a/code/game/objects/items/weapons/shields/shield_crafting.dm b/code/game/objects/items/weapons/shields/shield_crafting.dm new file mode 100644 index 00000000000..307db9bce88 --- /dev/null +++ b/code/game/objects/items/weapons/shields/shield_crafting.dm @@ -0,0 +1,52 @@ +// General item for 'proper' shield crafting. +/obj/item/shield_fasteners + name = "shield fasteners" + desc = "A handful of shaped fasteners used to hold a buckler or shield together." + icon_state = ICON_STATE_WORLD + icon = 'icons/obj/items/shield_fasteners.dmi' + material = /decl/material/solid/metal/iron + material_alteration = MAT_FLAG_ALTERATION_ALL + +// TODO: single-step slapcrafting +/obj/item/shield_base + w_class = ITEM_SIZE_LARGE + desc = "An unfinished collection of shield bits, waiting for fastenings." + icon_state = ICON_STATE_WORLD + abstract_type = /obj/item/shield_base + material = /decl/material/solid/organic/wood/oak + material_alteration = MAT_FLAG_ALTERATION_ALL + var/wooden_icon + var/fittings_type = /obj/item/shield_fasteners + var/finished_type + var/work_skill = SKILL_CONSTRUCTION + +/obj/item/shield_base/attackby(obj/item/used_item, mob/user) + if(fittings_type && istype(used_item, fittings_type) && finished_type && user.try_unequip(used_item)) + to_chat(user, SPAN_NOTICE("You start laying out \the [src] and affixing \the [used_item].")) + if(user.do_skilled(5 SECONDS, work_skill, src, check_holding = TRUE)) + var/was_held = (loc == user) + var/obj/item/shield/crafted/shield = new finished_type(get_turf(src), material?.type, used_item.material?.type) + user.visible_message("\The [user] secures \the [src] with \the [used_item], finishing \a [shield].") + qdel(src) + qdel(used_item) + if(was_held) + user.put_in_hands(shield) + return TRUE + return ..() + +/obj/item/shield_base/set_material(new_material) + . = ..() + if(wooden_icon) + if(istype(material, /decl/material/solid/organic/wood)) + set_icon(wooden_icon) + else + set_icon(initial(icon)) + update_icon() + +// Subtypes below. +/obj/item/shield_base/buckler + name_prefix = "unfinished" + name = "buckler" + icon = 'icons/obj/items/shield/buckler_base_metal.dmi' + wooden_icon = 'icons/obj/items/shield/buckler_base_wood.dmi' + finished_type = /obj/item/shield/crafted/buckler diff --git a/code/game/objects/items/weapons/shields/shield_energy.dm b/code/game/objects/items/weapons/shields/shield_energy.dm new file mode 100644 index 00000000000..c7d54652a56 --- /dev/null +++ b/code/game/objects/items/weapons/shields/shield_energy.dm @@ -0,0 +1,83 @@ +/* + * Energy Shield + */ + +/obj/item/shield/energy + name = "energy combat shield" + desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." + icon = 'icons/obj/items/shield/e_shield.dmi' + icon_state = "eshield0" // eshield1 for expanded + obj_flags = OBJ_FLAG_CONDUCTIBLE + throw_speed = 1 + throw_range = 4 + w_class = ITEM_SIZE_SMALL + origin_tech = @'{"materials":4,"magnets":3,"esoteric":4}' + attack_verb = list("shoved", "bashed") + material = /decl/material/solid/metal/titanium + matter = list( + /decl/material/solid/fiberglass = MATTER_AMOUNT_SECONDARY, + /decl/material/solid/metal/gold = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/silicon = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/gemstone/diamond = MATTER_AMOUNT_TRACE, + ) + _base_attack_force = 3 + var/active = 0 + var/shield_light_color = "#006aff" + +/obj/item/shield/energy/Initialize() + set_extension(src, /datum/extension/base_icon_state, copytext(initial(icon_state), 1, length(initial(icon_state)))) + . = ..() + update_icon() + +/obj/item/shield/energy/handle_shield(mob/user) + if(!active) + return 0 //turn it on first! + . = ..() + + if(.) + spark_at(user.loc, amount=5) + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + +/obj/item/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + if(((P.is_sharp() || P.has_edge()) && damage > 10) || istype(P, /obj/item/projectile/beam)) + return (base_block_chance - round(damage / 2.5)) //block bullets and beams using the old block chance + return base_block_chance + +/obj/item/shield/energy/attack_self(mob/user) + if(user.has_genetic_condition(GENE_COND_CLUMSY) && prob(50)) + to_chat(user, SPAN_DANGER("You beat yourself in the head with [src].")) + if(isliving(user)) + var/mob/living/M = user + M.take_organ_damage(5, 0) + active = !active + if (active) + set_base_attack_force(10) + update_icon() + w_class = ITEM_SIZE_HUGE + playsound(user, 'sound/weapons/saberon.ogg', 50, 1) + to_chat(user, SPAN_NOTICE("\The [src] is now active.")) + + else + set_base_attack_force(3) + update_icon() + w_class = ITEM_SIZE_SMALL + playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) + to_chat(user, SPAN_NOTICE("\The [src] can now be concealed.")) + + if(ishuman(user)) + var/mob/living/human/H = user + H.update_inhand_overlays() + + add_fingerprint(user) + return + +/obj/item/shield/energy/on_update_icon() + . = ..() + var/datum/extension/base_icon_state/base_name = get_extension(src, /datum/extension/base_icon_state) + icon_state = "[base_name.base_icon_state][active]" //Replace 0 with current state + if(active) + set_light(1.5, 1.5, shield_light_color) + else + set_light(0) diff --git a/code/game/objects/items/weapons/shields/shield_riot.dm b/code/game/objects/items/weapons/shields/shield_riot.dm new file mode 100644 index 00000000000..0cc0c9324f0 --- /dev/null +++ b/code/game/objects/items/weapons/shields/shield_riot.dm @@ -0,0 +1,58 @@ +/obj/item/shield/riot + name = "riot shield" + desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder." + icon = 'icons/obj/items/shield/riot.dmi' + icon_state = ICON_STATE_WORLD + obj_flags = OBJ_FLAG_CONDUCTIBLE + slot_flags = SLOT_BACK + throw_speed = 1 + throw_range = 4 + w_class = ITEM_SIZE_HUGE + origin_tech = @'{"materials":2}' + material = /decl/material/solid/fiberglass + matter = list(/decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT) + attack_verb = list("shoved", "bashed") + var/cooldown = 0 //shield bash cooldown. based on world.time + var/max_block = 15 + var/can_block_lasers = FALSE + +/obj/item/shield/riot/handle_shield(mob/user) + . = ..() + if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) + +/obj/item/shield/riot/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + //plastic shields do not stop bullets or lasers, even in space. Will block beanbags, rubber bullets, and stunshots just fine though. + if((P.is_sharp() || P.has_edge()) && damage >= max_block) + return 0 + if(istype(P, /obj/item/projectile/beam) && (!can_block_lasers || (P.armor_penetration >= max_block))) + return 0 + return base_block_chance + +/obj/item/shield/riot/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/baton)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + return TRUE + else + return ..() + +/obj/item/shield/riot/metal + name = "plasteel combat shield" + icon = 'icons/obj/items/shield/metal.dmi' + icon_state = ICON_STATE_WORLD + obj_flags = OBJ_FLAG_CONDUCTIBLE + slot_flags = SLOT_BACK + throw_range = 3 + w_class = ITEM_SIZE_HUGE + material = /decl/material/solid/metal/plasteel + max_block = 50 + can_block_lasers = TRUE + slowdown_general = 1.5 + _base_attack_force = 6 + +/obj/item/shield/riot/metal/security //A cosmetic difference. + icon = 'icons/obj/items/shield/metal_security.dmi' diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm index c14047c71f6..99614d0fe16 100644 --- a/code/game/objects/items/weapons/soap.dm +++ b/code/game/objects/items/weapons/soap.dm @@ -57,45 +57,42 @@ add_to_reagents(/decl/material/liquid/cleaner/soap, SOAP_CLEANER_ON_WET) /obj/item/soap/Crossed(atom/movable/AM) - if(!isliving(AM)) - return - var/mob/living/M = AM - M.slip("\the [src]", 3) + var/mob/living/victim = AM + if(istype(victim)) + victim.slip("\the [src]", 3) + return ..() /obj/item/soap/afterattack(atom/target, mob/user, proximity) - if(!proximity) return - //I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing. - //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn - var/cleaned = FALSE - if(user.client && (target in user.client.screen)) - to_chat(user, SPAN_NOTICE("You need to take that [target.name] off before cleaning it.")) - else if(istype(target,/obj/effect/decal/cleanable/blood)) - to_chat(user, SPAN_NOTICE("You scrub \the [target.name] out.")) - target.clean() //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. - cleaned = TRUE - else if(istype(target,/obj/effect/decal/cleanable)) - to_chat(user, SPAN_NOTICE("You scrub \the [target.name] out.")) - qdel(target) - cleaned = TRUE - else if(isturf(target) || istype(target, /obj/structure/catwalk)) - var/turf/T = get_turf(target) - if(!T) - return - user.visible_message(SPAN_NOTICE("\The [user] starts scrubbing \the [T].")) - if(do_after(user, 8 SECONDS, T) && reagents?.total_volume) - reagents.splash(T, FLUID_QDEL_POINT) - to_chat(user, SPAN_NOTICE("You scrub \the [target] clean.")) - cleaned = TRUE - else if(istype(target,/obj/structure/hygiene/sink)) + + if(!proximity) + return ..() + + if(istype(target,/obj/structure/hygiene/sink)) to_chat(user, SPAN_NOTICE("You wet \the [src] in the sink.")) wet() + return TRUE + + if(reagents?.total_volume < 1) + to_chat(user, SPAN_WARNING("\The [src] is too dry to clean \the [target].")) + return TRUE + + if(isturf(target) || istype(target, /obj/structure/catwalk)) + target = get_turf(target) + if(!isturf(target)) + return ..() + user.visible_message(SPAN_NOTICE("\The [user] starts scrubbing \the [target].")) + if(!do_after(user, 8 SECONDS, target) && reagents?.total_volume) + return TRUE + to_chat(user, SPAN_NOTICE("You scrub \the [target] clean.")) + else if(istype(target,/obj/effect/decal/cleanable)) + to_chat(user, SPAN_NOTICE("You scrub \the [target] out.")) else - to_chat(user, SPAN_NOTICE("You clean \the [target.name].")) - target.clean() //Clean bloodied atoms. Blood decals themselves need to be handled above. - cleaned = TRUE + to_chat(user, SPAN_NOTICE("You clean \the [target].")) - if(cleaned) - user.update_personal_goal(/datum/goal/clean, 1) + reagents.touch_atom(target) + reagents.remove_any(1) + user.update_personal_goal(/datum/goal/clean, 1) + return TRUE //attack_as_weapon /obj/item/soap/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 3a5cde951c7..e53ef9f89e0 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -24,11 +24,11 @@ . = ..() if(overlay_flags & BELT_OVERLAY_ITEMS) var/list/cur_overlays - for(var/obj/item/I in contents) - if(I.use_single_icon) - LAZYADD(cur_overlays, I.get_on_belt_overlay()) + for(var/obj/item/thing in contents) + if(thing.use_single_icon) + LAZYADD(cur_overlays, thing.get_on_belt_overlay()) else - LAZYADD(cur_overlays, overlay_image('icons/obj/clothing/obj_belt_overlays.dmi', I.icon_state)) + LAZYADD(cur_overlays, overlay_image('icons/obj/clothing/obj_belt_overlays.dmi', thing.icon_state)) if(LAZYLEN(cur_overlays)) add_overlay(cur_overlays) @@ -37,8 +37,8 @@ /obj/item/belt/get_mob_overlay(mob/user_mob, slot, bodypart, use_fallback_if_icon_missing = TRUE, skip_adjustment = FALSE) var/image/ret = ..() if(ret && slot == slot_belt_str && length(contents)) - for(var/obj/item/I in contents) - var/image/new_overlay = I.get_mob_overlay(user_mob, slot, bodypart, use_fallback_if_icon_missing, TRUE) + for(var/obj/item/thing in contents) + var/image/new_overlay = thing.get_mob_overlay(user_mob, slot, bodypart, use_fallback_if_icon_missing, TRUE) if(new_overlay) ret.overlays += new_overlay return ret @@ -57,37 +57,36 @@ . = ..() set_extension(src, /datum/extension/holster, storage, sound_in, sound_out, can_holster) -/obj/item/belt/holster/attackby(obj/item/W, mob/user) - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) - if(H.holster(W, user)) +/obj/item/belt/holster/attackby(obj/item/used_item, mob/user) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + if(holster?.holster(used_item, user)) return TRUE - else - . = ..(W, user) + return ..(used_item, user) /obj/item/belt/holster/attack_hand(mob/user) if(!user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) return ..() - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) - if(H.unholster(user)) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + if(holster?.unholster(user)) return TRUE return ..() /obj/item/belt/holster/examine(mob/user) . = ..() - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) - H.examine_holster(user) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + holster.examine_holster(user) /obj/item/belt/holster/on_update_icon() . = ..() - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) if(overlay_flags) var/list/cur_overlays - for(var/obj/item/I in contents) - if(I == H.holstered) + for(var/obj/item/thing in contents) + if(thing == holster.holstered) if(overlay_flags & BELT_OVERLAY_HOLSTER) - LAZYADD(cur_overlays, overlay_image('icons/obj/clothing/obj_belt_overlays.dmi', I.icon_state)) + LAZYADD(cur_overlays, overlay_image('icons/obj/clothing/obj_belt_overlays.dmi', thing.icon_state)) else if(overlay_flags & BELT_OVERLAY_ITEMS) - LAZYADD(cur_overlays, overlay_image('icons/obj/clothing/obj_belt_overlays.dmi', I.icon_state)) + LAZYADD(cur_overlays, overlay_image('icons/obj/clothing/obj_belt_overlays.dmi', thing.icon_state)) if(LAZYLEN(cur_overlays)) add_overlay(cur_overlays) diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index 499ffb5d858..639d83a1754 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -275,13 +275,6 @@ /obj/item/box/anti_photons/WillContain() return list(/obj/item/grenade/anti_photon = 5) -/obj/item/box/supermatters - name = "box of supermatter grenades" - desc = "A box containing 5 highly experimental supermatter grenades." - icon_state = "radbox" -/obj/item/box/supermatters/WillContain() - return list(/obj/item/grenade/supermatter = 5) - /obj/item/box/decompilers name = "box of decompiler grenades" desc = "A box containing 5 experimental decompiler grenades." diff --git a/code/game/objects/items/weapons/storage/fancy/_fancy.dm b/code/game/objects/items/weapons/storage/fancy/_fancy.dm index ec59092259d..66774d7abae 100644 --- a/code/game/objects/items/weapons/storage/fancy/_fancy.dm +++ b/code/game/objects/items/weapons/storage/fancy/_fancy.dm @@ -14,7 +14,11 @@ /obj/item/box/fancy/proc/update_icon_state() icon_state = initial(icon_state) - if(key_type && storage?.opened) + if(!length(contents)) + var/empty_state = "[icon_state]0" + if(check_state_in_icon(empty_state, icon)) + icon_state = empty_state + else if(key_type && storage?.opened) icon_state = "[icon_state][count_by_type(contents, key_type)]" /obj/item/box/fancy/proc/add_contents_overlays() diff --git a/code/game/objects/items/weapons/storage/mre.dm b/code/game/objects/items/weapons/storage/mre.dm index 8c2acb816db..70bbf23d777 100644 --- a/code/game/objects/items/weapons/storage/mre.dm +++ b/code/game/objects/items/weapons/storage/mre.dm @@ -12,6 +12,7 @@ MRE Stuff obj_flags = OBJ_FLAG_HOLLOW var/main_meal = /obj/item/mrebag var/meal_desc = "This one is menu 1, meat pizza." + var/has_been_opened = FALSE /obj/item/mre/WillContain() . = list( @@ -33,10 +34,13 @@ MRE Stuff . = ..() to_chat(user, meal_desc) +/obj/item/mre/attack_self(mob/user) + . = ..() + /obj/item/mre/on_update_icon() . = ..() icon_state = get_world_inventory_state() - if(storage?.opened) + if(has_been_opened) icon_state = "[icon_state]-open" /obj/item/mre/attack_self(mob/user) diff --git a/code/game/objects/items/weapons/storage/specialized.dm b/code/game/objects/items/weapons/storage/specialized.dm index d85bd453f5c..01481422311 100644 --- a/code/game/objects/items/weapons/storage/specialized.dm +++ b/code/game/objects/items/weapons/storage/specialized.dm @@ -12,8 +12,8 @@ /obj/item/ore name = "mining satchel" desc = "This sturdy bag can be used to store and transport ores." - icon = 'icons/obj/mining.dmi' - icon_state = "satchel" + icon = 'icons/obj/items/mining_satchel.dmi' + icon_state = ICON_STATE_WORLD slot_flags = SLOT_LOWER_BODY w_class = ITEM_SIZE_LARGE storage = /datum/storage/ore @@ -57,8 +57,8 @@ /obj/item/sheetsnatcher name = "sheet snatcher" - icon = 'icons/obj/mining.dmi' - icon_state = "sheetsnatcher" + icon = 'icons/obj/items/sheet_snatcher.dmi' + icon_state = ICON_STATE_WORLD desc = "A patented storage system designed for any kind of mineral sheet." material = /decl/material/solid/organic/plastic storage = /datum/storage/sheets diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index faf51ae0c59..5fcafd398ae 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -89,6 +89,7 @@ /decl/interaction_handler/remove_id/wallet expected_target_type = /obj/item/wallet + examine_desc = "remove an ID card" /decl/interaction_handler/remove_id/wallet/is_possible(atom/target, mob/user, obj/item/prop) . = ..() && ishuman(user) diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index eec6eb5e83a..f1d555da4d0 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -5,8 +5,6 @@ icon = 'icons/obj/items/weapon/stunbaton.dmi' icon_state = ICON_STATE_WORLD slot_flags = SLOT_LOWER_BODY - sharp = 0 - edge = 0 w_class = ITEM_SIZE_NORMAL origin_tech = @'{"combat":2}' attack_verb = list("beaten") diff --git a/code/game/objects/items/weapons/surgery_tools.dm b/code/game/objects/items/weapons/surgery_tools.dm index c5b1f30ef20..082bccc7a32 100644 --- a/code/game/objects/items/weapons/surgery_tools.dm +++ b/code/game/objects/items/weapons/surgery_tools.dm @@ -99,8 +99,8 @@ icon = 'icons/obj/surgery.dmi' icon_state = "scalpel" obj_flags = OBJ_FLAG_CONDUCTIBLE - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE w_class = ITEM_SIZE_TINY slot_flags = SLOT_EARS throw_speed = 3 @@ -160,8 +160,8 @@ /obj/item/incision_manager name = "incision management system" desc = "A true extension of the surgeon's body, this marvel combines several medical tools into one modular package." - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE atom_damage_type = BURN icon = 'icons/obj/surgery.dmi' icon_state = "scalpel_manager_on" @@ -202,8 +202,8 @@ material = /decl/material/solid/metal/steel matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) attack_verb = list("attacked", "slashed", "sawed", "cut") - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE pickup_sound = 'sound/foley/pickup2.ogg' drop_sound = 'sound/foley/knifedrop3.ogg' _base_attack_force = 15 diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index 25ecca6f856..eb8b3a9e3a8 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -19,7 +19,7 @@ /obj/item/classic_baton/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) if (user.has_genetic_condition(GENE_COND_CLUMSY) && prob(50)) - var/force = get_attack_force(user) + var/force = expend_attack_force(user) to_chat(user, SPAN_WARNING("You club yourself over the head.")) SET_STATUS_MAX(user, STAT_WEAK, (3 * force)) if(ishuman(user)) @@ -76,7 +76,7 @@ /obj/item/telebaton/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) if(on && user.has_genetic_condition(GENE_COND_CLUMSY) && prob(50)) - var/force = get_attack_force(user) + var/force = expend_attack_force(user) to_chat(user, SPAN_DANGER("You club yourself over the head.")) SET_STATUS_MAX(user, STAT_WEAK, (3 * force)) if(ishuman(user)) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 345566c0607..a2016aef379 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -193,7 +193,7 @@ var/global/list/global/tank_gauge_cache = list() var/obj/item/weldingtool/WT = W if(WT.weld(1,user)) if(!valve_welded) - to_chat(user, "You begin welding the \the [src] emergency pressure relief valve.") + to_chat(user, "You begin welding \the [src] emergency pressure relief valve.") if(do_after(user, 40,src)) to_chat(user, "You carefully weld \the [src] emergency pressure relief valve shut. \The [src] may now rupture under pressure!") valve_welded = 1 diff --git a/code/game/objects/items/weapons/tools/shears.dm b/code/game/objects/items/weapons/tools/shears.dm index c57dff7b9e2..b5958ab0025 100644 --- a/code/game/objects/items/weapons/tools/shears.dm +++ b/code/game/objects/items/weapons/tools/shears.dm @@ -8,8 +8,8 @@ material = /decl/material/solid/metal/steel center_of_mass = @'{"x":18,"y":10}' attack_verb = list("sheared", "cut") - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE material_alteration = MAT_FLAG_ALTERATION_COLOR drop_sound = 'sound/foley/singletooldrop1.ogg' diff --git a/code/game/objects/items/weapons/tools/wirecutter.dm b/code/game/objects/items/weapons/tools/wirecutter.dm index 2886de69bc3..7eda6a47b54 100644 --- a/code/game/objects/items/weapons/tools/wirecutter.dm +++ b/code/game/objects/items/weapons/tools/wirecutter.dm @@ -9,8 +9,8 @@ material = /decl/material/solid/metal/steel center_of_mass = @'{"x":18,"y":10}' attack_verb = list("pinched", "nipped") - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE material_alteration = MAT_FLAG_ALTERATION_COLOR drop_sound = 'sound/foley/singletooldrop1.ogg' diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index 07845b81c45..397257e0d29 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -1,17 +1,17 @@ /obj/item/beartrap - name = "mechanical trap" - throw_speed = 2 - throw_range = 1 - gender = PLURAL - icon = 'icons/obj/items/beartrap.dmi' - icon_state = "beartrap0" - randpixel = 0 - desc = "A mechanically activated leg trap. Low-tech, but reliable. Looks like it could really hurt if you set it off." - w_class = ITEM_SIZE_NORMAL - origin_tech = @'{"materials":1}' - material = /decl/material/solid/metal/steel - can_buckle = 0 //disallow manual un/buckling - var/deployed = 0 + name = "mechanical trap" + desc = "A mechanically activated leg trap. Low-tech, but reliable. Looks like it could really hurt if you set it off." + throw_speed = 2 + throw_range = 1 + gender = PLURAL + icon = 'icons/obj/items/beartrap.dmi' + icon_state = "beartrap0" + randpixel = 0 + w_class = ITEM_SIZE_NORMAL + origin_tech = @'{"materials":1}' + material = /decl/material/solid/metal/steel + can_buckle = FALSE //disallow manual un/buckling + var/deployed = FALSE /obj/item/beartrap/proc/can_use(mob/user) . = (user.check_dexterity(DEXTERITY_SIMPLE_MACHINES) && !issilicon(user) && !user.stat && !user.restrained()) @@ -19,12 +19,12 @@ /obj/item/beartrap/user_unbuckle_mob(mob/user) if(buckled_mob && can_use(user)) user.visible_message( - "\The [user] begins freeing \the [buckled_mob] from \the [src].", - "You carefully begin to free \the [buckled_mob] from \the [src].", - "You hear metal creaking." + SPAN_NOTICE("\The [user] begins freeing \the [buckled_mob] from \the [src]."), + SPAN_NOTICE("You carefully begin to free \the [buckled_mob] from \the [src]."), + SPAN_NOTICE("You hear metal creaking.") ) if(do_after(user, 60, src)) - user.visible_message("\The [buckled_mob] has been freed from \the [src] by \the [user].") + user.visible_message(SPAN_NOTICE("\The [buckled_mob] has been freed from \the [src] by \the [user].")) unbuckle_mob() anchored = FALSE @@ -32,19 +32,18 @@ ..() if(!deployed && can_use(user)) user.visible_message( - "[user] starts to deploy \the [src].", - "You begin deploying \the [src]!", + SPAN_DANGER("\The [user] starts to deploy \the [src]."), + SPAN_DANGER("You begin deploying \the [src]!"), "You hear the slow creaking of a spring." - ) + ) - if (do_after(user, 60, src) && user.try_unequip(src)) + if (do_after(user, 6 SECONDS, src) && user.try_unequip(src)) user.visible_message( - "\The [user] has deployed \the [src].", - "You have deployed \the [src]!", + SPAN_DANGER("\The [user] has deployed \the [src]."), + SPAN_DANGER("You have deployed \the [src]!"), "You hear a latch click loudly." - ) - - deployed = 1 + ) + deployed = TRUE update_icon() anchored = TRUE @@ -55,7 +54,7 @@ user_unbuckle_mob(user) return TRUE if(!deployed || !can_use(user)) - return FALSE + return ..() user.visible_message( SPAN_DANGER("\The [user] starts to disarm \the [src]."), SPAN_NOTICE("You begin disarming \the [src]!"), @@ -71,40 +70,45 @@ update_icon() return TRUE -/obj/item/beartrap/proc/attack_mob(mob/L) +/obj/item/beartrap/proc/attack_mob(mob/victim) + + if(victim.immune_to_floor_hazards()) + return FALSE var/target_zone - if(L.current_posture.prone) + if(victim.current_posture.prone) target_zone = ran_zone() else target_zone = pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG) - if(!L.apply_damage(30, BRUTE, target_zone, used_weapon=src)) - return 0 + if(!victim.apply_damage(30, BRUTE, target_zone, used_weapon=src)) + return FALSE //trap the victim in place - set_dir(L.dir) - buckle_mob(L) - to_chat(L, "The steel jaws of \the [src] bite into you, trapping you in place!") - deployed = 0 + set_dir(victim.dir) + buckle_mob(victim) + to_chat(victim, SPAN_DANGER("The steel jaws of \the [src] bite into you, trapping you in place!")) + deployed = FALSE + return TRUE /obj/item/beartrap/Crossed(atom/movable/AM) ..() if(!deployed || !isliving(AM)) return - var/mob/living/L = AM - if(MOVING_DELIBERATELY(L)) + var/mob/living/victim = AM + if(MOVING_DELIBERATELY(victim) || victim.immune_to_floor_hazards()) return - L.visible_message( - SPAN_DANGER("\The [L] steps on \the [src]."), + victim.visible_message( + SPAN_DANGER("\The [victim] steps on \the [src]."), SPAN_DANGER("You step on \the [src]!"), - "You hear a loud metallic snap!") - attack_mob(L) + "You hear a loud metallic snap!" + ) + attack_mob(victim) if(!buckled_mob) anchored = FALSE - deployed = 0 + deployed = FALSE update_icon() /obj/item/beartrap/on_update_icon() . = ..() - icon_state = "beartrap[deployed == TRUE]" + icon_state = "beartrap[deployed]" diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 1945ad56201..3d6b8c86fc5 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -34,8 +34,7 @@ return ..() /obj/item/nullrod/proc/holy_act(mob/living/target, mob/living/user) - if(target.mind && LAZYLEN(target.mind.learned_spells)) - target.silence_spells(30 SECONDS) + if(target.disable_abilities(30 SECONDS)) to_chat(target, SPAN_DANGER("You've been silenced!")) return TRUE return FALSE @@ -65,8 +64,7 @@ /obj/item/energy_net/dropped() ..() - spawn(10) - if(src) qdel(src) + QDEL_IN(src, 1 SECOND) /obj/item/energy_net/throw_impact(atom/hit_atom) ..() @@ -86,8 +84,7 @@ qdel(src) // If we miss or hit an obstacle, we still want to delete the net. - spawn(10) - if(src) qdel(src) + QDEL_IN(src, 1 SECOND) /obj/effect/energy_net name = "energy net" @@ -194,13 +191,9 @@ /obj/effect/energy_net/attack_hand(var/mob/user) if(!user.check_intent(I_FLAG_HARM)) return ..() - var/decl/species/my_species = user.get_species() - if(my_species) - if(my_species.can_shred(user)) - playsound(src.loc, 'sound/weapons/slash.ogg', 80, 1) - current_health -= rand(10, 20) - else - current_health -= rand(1,3) + if(user.can_shred()) + playsound(src.loc, 'sound/weapons/slash.ogg', 80, 1) + current_health -= rand(10, 20) else current_health -= rand(5,8) to_chat(user, SPAN_DANGER("You claw at the energy net.")) @@ -208,7 +201,7 @@ return TRUE /obj/effect/energy_net/attackby(obj/item/W, mob/user) - current_health -= W.get_attack_force(user) + current_health -= W.expend_attack_force(user) healthcheck() return TRUE diff --git a/code/game/objects/random/subtypes/food.dm b/code/game/objects/random/subtypes/food.dm index f390e57cf5d..36aa8a4a37e 100644 --- a/code/game/objects/random/subtypes/food.dm +++ b/code/game/objects/random/subtypes/food.dm @@ -102,7 +102,8 @@ /obj/random/mre/spread/spawn_choices() var/static/list/spawnable_choices = list( /obj/item/chems/packet/jelly, - /obj/item/chems/packet/honey + /obj/item/chems/packet/honey, + /obj/item/chems/packet/honey_fake ) return spawnable_choices diff --git a/code/game/objects/random/subtypes/misc.dm b/code/game/objects/random/subtypes/misc.dm index 51ca36d890e..dfc3dc477e9 100644 --- a/code/game/objects/random/subtypes/misc.dm +++ b/code/game/objects/random/subtypes/misc.dm @@ -14,7 +14,7 @@ /obj/item/pill_bottle/happy = 2, /obj/item/pill_bottle/zoom = 2, /obj/item/chems/glass/beaker/vial/random/toxin = 1, - /obj/item/chems/glass/beaker/sulphuric = 1, + /obj/item/chems/glass/beaker/sulfuric = 1, /obj/item/poster = 5, /obj/item/butterflyblade = 3, /obj/item/butterflyhandle = 3, @@ -164,6 +164,7 @@ /obj/effect/decal/cleanable/ash, /obj/effect/decal/cleanable/generic, /obj/effect/decal/cleanable/flour, + /obj/effect/decal/cleanable/filth, /obj/effect/decal/cleanable/dirt/visible, /obj/item/remains/robot ) diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index f1fb8d4a2c1..7beee9b0878 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -245,12 +245,12 @@ playsound(loc, 'sound/weapons/tablehit1.ogg', 50, 1) var/list/L = take_damage(rand(1,5)) for(var/obj/item/shard/S in L) - if(S.sharp && prob(50)) + if(S.is_sharp() && prob(50)) victim.visible_message( SPAN_DANGER("\The [S] slices into [victim]'s face!"), SPAN_DANGER("\The [S] slices into your face!") ) - victim.standard_weapon_hit_effects(S, user, S.get_attack_force()*2, BP_HEAD) + victim.standard_weapon_hit_effects(S, user, S.expend_attack_force()*2, BP_HEAD) qdel(grab) else if(atom_flags & ATOM_FLAG_CLIMBABLE) var/obj/occupied = turf_is_crowded() @@ -323,9 +323,21 @@ Note: This proc can be overwritten to allow for different types of auto-alignmen W.pixel_y = (CELLSIZE * (cell_y + 0.5)) - center["y"] W.pixel_z = 0 +// Does this structure override turf depth for the purposes of mob offsets? +/obj/structure/proc/is_platform() + return FALSE + +/obj/structure/proc/is_z_passable() + return TRUE + +/obj/structure/on_turf_height_change(new_height) + // We may be a fixed point. + return !is_platform() && ..() + /obj/structure/hitby(var/atom/movable/AM, var/datum/thrownthing/TT) . = ..() if(. && (structure_flags & STRUCTURE_FLAG_THROWN_DAMAGE)) visible_message(SPAN_DANGER("\The [src] was hit by \the [AM].")) playsound(src.loc, hitsound, 100, 1) take_damage(AM.get_thrown_attack_force() * (TT.speed/THROWFORCE_SPEED_DIVISOR), AM.atom_damage_type) + diff --git a/code/game/objects/structures/_structure_construction.dm b/code/game/objects/structures/_structure_construction.dm index a069498e7d8..186aa29e0e0 100644 --- a/code/game/objects/structures/_structure_construction.dm +++ b/code/game/objects/structures/_structure_construction.dm @@ -2,120 +2,29 @@ var/wired var/tool_interaction_flags -/obj/structure/proc/handle_default_hammer_attackby(var/mob/user, var/obj/item/hammer) - - // Resolve ambiguous interactions. - var/can_deconstruct = (tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) && can_dismantle(user) - var/can_unanchor = (tool_interaction_flags & TOOL_INTERACTION_ANCHOR) && can_unanchor(user) - if(can_deconstruct && can_unanchor) - var/choice = alert(user, "Do you wish to [anchored ? "unanchor" : "anchor"] or dismantle this structure?", "Tool Choice", (anchored ? "Unanchor" : "Anchor"), "Deconstruct", "Cancel") - if(!choice || choice == "Cancel" || QDELETED(src) || QDELETED(user) || QDELETED(hammer) || !CanPhysicallyInteract(user) || user.get_active_held_item() != hammer) - return TRUE - if(choice == "Deconstruct") - can_unanchor = FALSE - else - can_deconstruct = FALSE - - if(can_unanchor) - playsound(src.loc, 'sound/items/Deconstruct.ogg', 100, 1) - visible_message(SPAN_NOTICE("\The [user] begins [anchored ? "unanchoring [src]" : "anchoring [src] in place"] with \the [hammer].")) - if(!do_after(user, 4 SECONDS, src) || QDELETED(src)) - return TRUE - playsound(src.loc, 'sound/items/Deconstruct.ogg', 100, 1) - anchored = !anchored - visible_message(SPAN_NOTICE("\The [user] has [anchored ? "anchored" : "unanchored"] \the [src] with \the [hammer].")) - update_icon() - return TRUE - - if(can_deconstruct) - playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) - visible_message(SPAN_NOTICE("\The [user] starts knocking apart \the [src] with \the [hammer].")) - if(!do_after(user, 5 SECONDS, src) || QDELETED(src)) - return TRUE - playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) - visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [hammer].")) - dismantle_structure(user) - return TRUE - - return FALSE - /obj/structure/proc/handle_default_wrench_attackby(var/mob/user, var/obj/item/wrench) if((tool_interaction_flags & TOOL_INTERACTION_ANCHOR) && can_unanchor(user)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) - visible_message(SPAN_NOTICE("\The [user] begins [anchored ? "unsecuring [src]" : "securing [src] in place"] with \the [wrench].")) - if(!do_after(user, 4 SECONDS, src) || QDELETED(src)) - return TRUE - playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) - anchored = !anchored - visible_message(SPAN_NOTICE("\The [user] has [anchored ? "secured" : "unsecured"] \the [src] with \the [wrench].")) - update_icon() - return TRUE + return tool_toggle_anchors(user, wrench) return FALSE /obj/structure/proc/handle_default_welder_attackby(var/mob/user, var/obj/item/weldingtool/welder) if((tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) && can_dismantle(user)) - if(material && !material.removed_by_welder) - to_chat(user, SPAN_WARNING("\The [src] is too delicate to be dismantled with \the [welder]; try a crowbar.")) - return TRUE - if(!welder.isOn()) - to_chat(user, SPAN_WARNING("Try lighting \the [welder] first.")) - return TRUE - if(welder.get_fuel() < 5) - to_chat(user, SPAN_WARNING("You need more fuel to complete this task.")) - return TRUE - playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1) - visible_message(SPAN_NOTICE("\The [user] starts slicing apart \the [src] with \the [welder].")) - if(!do_after(user, 3 SECONDS, src) || QDELETED(src) || !welder.weld(5, user)) - return TRUE - playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1) - visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [welder].")) - dismantle_structure(user) - return TRUE + return welder_dismantle(user, welder) return FALSE /obj/structure/proc/handle_default_crowbar_attackby(var/mob/user, var/obj/item/crowbar) if((tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) && can_dismantle(user)) - if(material && material.removed_by_welder) - to_chat(user, SPAN_WARNING("\The [src] is too robust to be dismantled with \the [crowbar]; try a welding tool.")) - return TRUE - playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) - visible_message(SPAN_NOTICE("\The [user] starts levering apart \the [src] with \the [crowbar].")) - if(!do_after(user, 5 SECONDS, src) || QDELETED(src)) - return TRUE - playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) - visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [crowbar].")) - dismantle_structure(user) - return TRUE + return tool_dismantle(user, crowbar) return FALSE /obj/structure/proc/handle_default_cable_attackby(var/mob/user, var/obj/item/stack/cable_coil/coil) if((tool_interaction_flags & TOOL_INTERACTION_WIRING) && anchored) - if(wired) - to_chat(user, SPAN_WARNING("\The [src] has already been wired.")) - return TRUE - var/obj/item/stack/cable_coil/cable = coil - if(cable.get_amount() < 1) - to_chat(user, SPAN_WARNING("You need one length of coil to wire \the [src].")) - else - visible_message(SPAN_NOTICE("\The [user] starts to wire \the [src].")) - if(do_after(user, 4 SECONDS, src) && !wired && anchored && !QDELETED(src) && cable.use(1)) - wired = TRUE - visible_message(SPAN_NOTICE("\The [user] finishes wiring \the [src].")) - return TRUE + return install_wiring(user, coil) return FALSE -/obj/structure/proc/handle_default_wirecutter_attackby(var/mob/user, var/obj/item/wirecutters/wirecutters) +/obj/structure/proc/handle_default_wirecutter_attackby(var/mob/user, var/obj/item/wirecutters) if((tool_interaction_flags & TOOL_INTERACTION_WIRING) && anchored) - if(!wired) - to_chat(user, SPAN_WARNING("\The [src] has not been wired.")) - return TRUE - playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) - visible_message(SPAN_NOTICE("\The [user] begins stripping the wiring out of \the [src].")) - if(do_after(user, 4 SECONDS, src) && !QDELETED(src) && wired) - visible_message(SPAN_NOTICE("\The [user] finishes stripping the wiring from \the [src].")) - new/obj/item/stack/cable_coil(src.loc, 1) - wired = FALSE - return TRUE + return strip_wiring(user, wirecutters) return FALSE /obj/structure/proc/handle_default_screwdriver_attackby(var/mob/user, var/obj/item/screwdriver) @@ -151,35 +60,41 @@ current_health = clamp(current_health + used*DOOR_REPAIR_AMOUNT, current_health, current_max_health) /obj/structure/attackby(obj/item/used_item, mob/user) + if(used_item.user_can_attack_with(user, silent = TRUE)) - var/force = used_item.get_attack_force(user) + var/force = used_item.expend_attack_force(user) if(force && user.check_intent(I_FLAG_HARM)) attack_animation(user) visible_message(SPAN_DANGER("\The [src] has been [pick(used_item.attack_verb)] with \the [used_item] by \the [user]!")) take_damage(force, used_item.atom_damage_type) - . = TRUE - - else if(IS_HAMMER(used_item)) - . = handle_default_hammer_attackby(user, used_item) - else if(IS_WRENCH(used_item)) - . = handle_default_wrench_attackby(user, used_item) - else if(IS_SCREWDRIVER(used_item)) - . = handle_default_screwdriver_attackby(user, used_item) - else if(IS_WELDER(used_item)) - . = handle_default_welder_attackby(user, used_item) - else if(IS_CROWBAR(used_item)) - . = handle_default_crowbar_attackby(user, used_item) - else if(IS_COIL(used_item)) - . = handle_default_cable_attackby(user, used_item) - else if(IS_WIRECUTTER(used_item)) - . = handle_default_wirecutter_attackby(user, used_item) - else if(can_repair_with(used_item) && can_repair(user)) - . = handle_repair(user, used_item) - if(.) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) add_fingerprint(user) - return . - return ..() + return TRUE + + // This handles standard tool interactions (anchoring, dismantling), calls below are for legacy purposes. + . = ..() + if(.) + return + + if(IS_HAMMER(used_item)) + . = handle_default_hammer_attackby(user, used_item) + else if(IS_WRENCH(used_item)) + . = handle_default_wrench_attackby(user, used_item) + else if(IS_SCREWDRIVER(used_item)) + . = handle_default_screwdriver_attackby(user, used_item) + else if(IS_WELDER(used_item)) + . = handle_default_welder_attackby(user, used_item) + else if(IS_CROWBAR(used_item)) + . = handle_default_crowbar_attackby(user, used_item) + else if(IS_COIL(used_item)) + . = handle_default_cable_attackby(user, used_item) + else if(IS_WIRECUTTER(used_item)) + . = handle_default_wirecutter_attackby(user, used_item) + else if(can_repair_with(used_item) && can_repair(user)) + . = handle_repair(user, used_item) + if(.) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + add_fingerprint(user) /obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb, var/environment_smash) if(environment_smash >= 1) @@ -195,4 +110,96 @@ take_damage(damage) else visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly.")) - return TRUE \ No newline at end of file + return TRUE + +/obj/structure/proc/strip_wiring(mob/user, obj/item/wirecutters) + if(!wired) + to_chat(user, SPAN_WARNING("\The [src] has not been wired.")) + return TRUE + playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) + visible_message(SPAN_NOTICE("\The [user] begins stripping the wiring out of \the [src].")) + if(do_after(user, 4 SECONDS, src) && !QDELETED(src) && wired) + visible_message(SPAN_NOTICE("\The [user] finishes stripping the wiring from \the [src].")) + new/obj/item/stack/cable_coil(src.loc, 1) + wired = FALSE + return TRUE + +/obj/structure/proc/install_wiring(mob/user, obj/item/stack/cable_coil/coil) + if(wired) + to_chat(user, SPAN_WARNING("\The [src] has already been wired.")) + return TRUE + var/obj/item/stack/cable_coil/cable = coil + if(cable.get_amount() < 1) + to_chat(user, SPAN_WARNING("You need one length of coil to wire \the [src].")) + else + visible_message(SPAN_NOTICE("\The [user] starts to wire \the [src].")) + if(do_after(user, 4 SECONDS, src) && !wired && anchored && !QDELETED(src) && cable.use(1)) + wired = TRUE + visible_message(SPAN_NOTICE("\The [user] finishes wiring \the [src].")) + return TRUE + + +/obj/structure/proc/handle_default_hammer_attackby(var/mob/user, var/obj/item/hammer) + + // Resolve ambiguous interactions. + var/can_deconstruct = (tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) && can_dismantle(user) + var/can_unanchor = (tool_interaction_flags & TOOL_INTERACTION_ANCHOR) && can_unanchor(user) + if(can_deconstruct && can_unanchor) + var/choice = alert(user, "Do you wish to [anchored ? "unanchor" : "anchor"] or dismantle this structure?", "Tool Choice", (anchored ? "Unanchor" : "Anchor"), "Deconstruct", "Cancel") + if(!choice || choice == "Cancel" || QDELETED(src) || QDELETED(user) || QDELETED(hammer) || !CanPhysicallyInteract(user) || user.get_active_held_item() != hammer) + return TRUE + if(choice == "Deconstruct") + can_unanchor = FALSE + else + can_deconstruct = FALSE + + if(can_unanchor) + return tool_toggle_anchors(user, hammer, anchor_sound = 'sound/items/Deconstruct.ogg') + + if(can_deconstruct) + return tool_dismantle(user, hammer, deconstruct_string = "knocking apart") + + return FALSE + +/obj/structure/proc/tool_dismantle(mob/user, obj/item/tool, dismantle_sound = 'sound/items/Crowbar.ogg', deconstruct_string = "levering apart") + if(material && material.removed_by_welder) + to_chat(user, SPAN_WARNING("\The [src] is too robust to be dismantled with \the [tool]; try a welding tool.")) + return TRUE + playsound(loc, dismantle_sound, 50, 1) + visible_message(SPAN_NOTICE("\The [user] starts [deconstruct_string] \the [src] with \the [tool].")) + if(!do_after(user, 5 SECONDS, src) || QDELETED(src)) + return TRUE + playsound(loc, dismantle_sound, 50, 1) + visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [tool].")) + dismantle_structure(user) + return TRUE + +/obj/structure/proc/welder_dismantle(mob/user, obj/item/weldingtool/welder) + if(material && !material.removed_by_welder) + to_chat(user, SPAN_WARNING("\The [src] is too delicate to be dismantled with \the [welder]; try a crowbar.")) + return TRUE + if(!welder.isOn()) + to_chat(user, SPAN_WARNING("Try lighting \the [welder] first.")) + return TRUE + if(welder.get_fuel() < 5) + to_chat(user, SPAN_WARNING("You need more fuel to complete this task.")) + return TRUE + playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1) + visible_message(SPAN_NOTICE("\The [user] starts slicing apart \the [src] with \the [welder].")) + if(!do_after(user, 3 SECONDS, src) || QDELETED(src) || !welder.weld(5, user)) + return TRUE + playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1) + visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [welder].")) + dismantle_structure(user) + return TRUE + +/obj/structure/proc/tool_toggle_anchors(mob/user, obj/item/tool, anchor_sound = 'sound/items/Ratchet.ogg') + playsound(src.loc, anchor_sound, 100, 1) + visible_message(SPAN_NOTICE("\The [user] begins [anchored ? "unsecuring [src]" : "securing [src] in place"] with \the [tool].")) + if(!do_after(user, 4 SECONDS, src) || QDELETED(src)) + return TRUE + playsound(src.loc, anchor_sound, 100, 1) + anchored = !anchored + visible_message(SPAN_NOTICE("\The [user] has [anchored ? "secured" : "unsecured"] \the [src] with \the [tool].")) + update_icon() + return TRUE diff --git a/code/game/objects/structures/_structure_interactions.dm b/code/game/objects/structures/_structure_interactions.dm new file mode 100644 index 00000000000..ac17e0641db --- /dev/null +++ b/code/game/objects/structures/_structure_interactions.dm @@ -0,0 +1,74 @@ +// Anchoring or unanchoring with a hammer or a wrench. +/decl/interaction_handler/structure + abstract_type = /decl/interaction_handler/structure + expected_target_type = /obj/structure + +/decl/interaction_handler/structure/unanchor + name = "Toggle Anchoring" + examine_desc = "anchor or unanchor $TARGET_THEM$" + +/decl/interaction_handler/structure/unanchor/is_possible(atom/target, mob/user, obj/item/prop) + if(!(. = ..())) + return + var/obj/structure/struct = target + if(!(struct.tool_interaction_flags & TOOL_INTERACTION_ANCHOR) || !struct.can_unanchor(user)) + return FALSE + return istype(prop) && (IS_WRENCH(prop) || IS_HAMMER(prop)) + +/decl/interaction_handler/structure/unanchor/invoked(atom/target, mob/user, obj/item/prop) + . = ..() + var/obj/structure/struct = target + return struct.tool_toggle_anchors(user, prop) + +// Removing wiring with wirecutters or installing it with a cable coil. +/decl/interaction_handler/structure/wiring + name = "Modify Wiring" + examine_desc = "strip or install wiring" + +/decl/interaction_handler/structure/wiring/is_possible(atom/target, mob/user, obj/item/prop) + if(!(. = ..())) + return + var/obj/structure/struct = target + if(!(struct.tool_interaction_flags & TOOL_INTERACTION_WIRING)) + return FALSE + if(struct.wired) + return IS_WIRECUTTER(prop) + return IS_COIL(prop) + +/decl/interaction_handler/structure/wiring/invoked(atom/target, mob/user, obj/item/prop) + var/obj/structure/struct = target + if(struct.wired) + return struct.strip_wiring(user, prop) + return struct.install_wiring(user, prop) + +// Dismantling a structure. +/decl/interaction_handler/structure/dismantle + name = "Dismantle Structure" + examine_desc = "dismantle $TARGET_THEM$" + +/decl/interaction_handler/structure/dismantle/is_possible(atom/target, mob/user, obj/item/prop) + if(!(. = ..())) + return + var/obj/structure/struct = target + if(!(struct.tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) || !struct.can_dismantle(user)) + return FALSE + return IS_WELDER(prop) || IS_CROWBAR(prop) || IS_HAMMER(prop) + +/decl/interaction_handler/structure/dismantle/invoked(atom/target, mob/user, obj/item/prop) + var/obj/structure/struct = target + if(IS_WELDER(prop)) + return struct.welder_dismantle(user, prop) + return struct.tool_dismantle(user, prop) + +/decl/interaction_handler/put_in_storage + name = "Put In Storage" + +/decl/interaction_handler/put_in_storage/is_possible(atom/target, mob/user, obj/item/prop) + return ..() && istype(prop) && target.storage + +// Boilerplate from /atom/proc/attackby(), replicated here so tool interactions can be bypassed. +/decl/interaction_handler/put_in_storage/invoked(atom/target, mob/user, obj/item/prop) + if((isrobot(user) && (prop == user.get_active_held_item())) || !target.storage.can_be_inserted(prop, user)) + return FALSE + prop.add_fingerprint(user) + return target.storage.handle_item_insertion(user, prop) diff --git a/code/game/objects/structures/structure_lock.dm b/code/game/objects/structures/_structure_lock.dm similarity index 100% rename from code/game/objects/structures/structure_lock.dm rename to code/game/objects/structures/_structure_lock.dm diff --git a/code/game/objects/structures/_structure_materials.dm b/code/game/objects/structures/_structure_materials.dm index f3c619b1f61..db8065e41d4 100644 --- a/code/game/objects/structures/_structure_materials.dm +++ b/code/game/objects/structures/_structure_materials.dm @@ -37,14 +37,14 @@ /obj/structure/proc/update_material_name(var/override_name) var/base_name = override_name || initial(name) - if(istype(material)) + if(istype(material) && (material_alteration & MAT_FLAG_ALTERATION_NAME)) SetName("[material.adjective_name] [base_name]") else SetName(base_name) /obj/structure/proc/update_material_desc(var/override_desc) var/base_desc = override_desc || initial(desc) - if(istype(material)) + if(istype(material) && (material_alteration & MAT_FLAG_ALTERATION_DESC)) desc = "[base_desc] This one is made of [material.solid_name]." else desc = base_desc diff --git a/code/game/objects/structures/barrels/barrel.dm b/code/game/objects/structures/barrels/barrel.dm index b28986d5592..73424c45322 100644 --- a/code/game/objects/structures/barrels/barrel.dm +++ b/code/game/objects/structures/barrels/barrel.dm @@ -12,7 +12,6 @@ wrenchable = FALSE storage = /datum/storage/barrel amount_dispensed = 10 - possible_transfer_amounts = @"[10,25,50,100]" volume = 7500 movable_flags = MOVABLE_FLAG_WHEELED throwpass = TRUE @@ -22,31 +21,24 @@ // Rivets, bands, etc. Currently just cosmetic. var/decl/material/metal_material = /decl/material/solid/metal/iron -/obj/structure/reagent_dispensers/barrel/Initialize() - if(ispath(metal_material)) - metal_material = GET_DECL(metal_material) - if(!istype(metal_material)) - metal_material = null - . = ..() - if(. == INITIALIZE_HINT_NORMAL && storage) - return INITIALIZE_HINT_LATELOAD // we want to grab our turf contents. - // Overrides due to wonky reagent_dispeners opencontainer flag handling. /obj/structure/reagent_dispensers/barrel/can_be_poured_from(mob/user, atom/target) return (reagents?.maximum_volume > 0) /obj/structure/reagent_dispensers/barrel/can_be_poured_into(mob/user, atom/target) return (reagents?.maximum_volume > 0) + // Override to skip open container check. /obj/structure/reagent_dispensers/barrel/can_drink_from(mob/user) return reagents?.total_volume && user.check_has_mouth() -/obj/structure/reagent_dispensers/barrel/get_alt_interactions(mob/user) +/obj/structure/reagent_dispensers/barrel/Initialize() + if(ispath(metal_material)) + metal_material = GET_DECL(metal_material) + if(!istype(metal_material)) + metal_material = null . = ..() - if(reagents?.total_volume >= FLUID_PUDDLE) - LAZYADD(., /decl/interaction_handler/dip_item) - LAZYADD(., /decl/interaction_handler/fill_from) - if(user?.get_active_held_item()) - LAZYADD(., /decl/interaction_handler/empty_into) + if(. == INITIALIZE_HINT_NORMAL && storage) + return INITIALIZE_HINT_LATELOAD // we want to grab our turf contents. /obj/structure/reagent_dispensers/barrel/LateInitialize(mapload, ...) ..() @@ -92,6 +84,33 @@ if(istype(loc, /obj/structure/cask_rack)) loc.update_icon() +/obj/structure/reagent_dispensers/barrel/get_standard_interactions(var/mob/user) + . = ..() + if(reagents?.maximum_volume) + LAZYADD(., global._reagent_interactions) + + // Disambiguation actions, since barrels can have several different potential interactions for + // the same item. It would be nice to enable this on /obj/structure in general but there are a + // ton of really bespoke overrides of the standard tool methods (windows, AI core, etc.). + if(tool_interaction_flags & TOOL_INTERACTION_ANCHOR) + LAZYADD(., /decl/interaction_handler/structure/unanchor) + if(tool_interaction_flags & TOOL_INTERACTION_WIRING) + LAZYADD(., /decl/interaction_handler/structure/wiring) + if(tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) + LAZYADD(., /decl/interaction_handler/structure/dismantle) + if(LAZYLEN(.) && storage) + LAZYADD(., /decl/interaction_handler/put_in_storage) + +// Copy of above - maybe we should just have a single 'get interactions' proc at this point? +/obj/structure/reagent_dispensers/barrel/get_alt_interactions(mob/user) + . = ..() + if(tool_interaction_flags & TOOL_INTERACTION_ANCHOR) + LAZYADD(., /decl/interaction_handler/structure/unanchor) + if(tool_interaction_flags & TOOL_INTERACTION_WIRING) + LAZYADD(., /decl/interaction_handler/structure/wiring) + if(tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) + LAZYADD(., /decl/interaction_handler/structure/dismantle) + /obj/structure/reagent_dispensers/barrel/ebony material = /decl/material/solid/organic/wood/ebony color = /decl/material/solid/organic/wood/ebony::color diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index bca9199f37d..50fb98d51d0 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -19,7 +19,7 @@ LINEN BINS material = /decl/material/solid/organic/cloth /obj/item/bedsheet/attackby(obj/item/I, mob/user) - if(is_sharp(I)) + if(I.is_sharp() || I.has_edge()) user.visible_message("\The [user] begins cutting up \the [src] with \a [I].", "You begin cutting up \the [src] with \the [I].") if(do_after(user, 50, src)) to_chat(user, "You cut \the [src] into pieces!") diff --git a/code/game/objects/structures/bookcase.dm b/code/game/objects/structures/bookcase.dm index 06d58e357aa..c51e005f3cf 100644 --- a/code/game/objects/structures/bookcase.dm +++ b/code/game/objects/structures/bookcase.dm @@ -44,7 +44,7 @@ var/global/list/station_bookcases = list() var/place_key = GET_BOOK_POS(src, place_x, place_y) if(isnull(book_positions[place_key])) - book_positions[place_key] = inserted + book_positions[place_key] = weakref(inserted) /datum/storage/bookcase/update_ui_after_item_removal(obj/item/removed) . = ..() @@ -53,7 +53,8 @@ var/global/list/station_bookcases = list() for(var/bX = 0 to (book_slots_x-1)) for(var/bY = 0 to (book_slots_y-1)) var/bK = GET_BOOK_POS(src, bX, bY) - if(book_positions[bK] == removed) + var/weakref/potential_book = book_positions[bK] + if(IS_WEAKREF_OF(potential_book, removed)) book_positions[bK] = null return @@ -65,8 +66,9 @@ var/global/list/station_bookcases = list() for(var/bX = 0 to (book_slots_x-1)) for(var/bY = 0 to (book_slots_y-1)) var/bK = GET_BOOK_POS(src, bX, bY) - var/obj/item/thing = book_positions[bK] - if(!isnull(thing) && (QDELETED(thing) || thing.loc != holder)) + var/weakref/book_ref = book_positions[bK] + var/obj/item/thing = book_ref?.resolve() + if(!isnull(thing) && (QDELING(thing) || thing.loc != holder)) // QDELING because it might be deleting but hasn't been moved to nullspace yet book_positions[bK] = null for(var/obj/item/thing in holder.get_stored_inventory()) @@ -75,7 +77,8 @@ var/global/list/station_bookcases = list() // Avoid moving us if we're already positioned for(var/bX = 0 to (book_slots_x-1)) for(var/bY = 0 to (book_slots_y-1)) - if(book_positions[GET_BOOK_POS(src, bX, bY)] == thing) + var/weakref/potential_book = book_positions[GET_BOOK_POS(src, bX, bY)] + if(IS_WEAKREF_OF(potential_book, thing)) positioned = TRUE break if(positioned) @@ -89,7 +92,7 @@ var/global/list/station_bookcases = list() for(var/bY = 0 to (book_slots_y-1)) var/bK = GET_BOOK_POS(src, bX, bY) if(isnull(book_positions[bK])) - book_positions[bK] = thing + book_positions[bK] = weakref(thing) positioned = TRUE break if(positioned) @@ -154,12 +157,13 @@ var/global/list/station_bookcases = list() for(var/bY = 0 to (book_storage.book_slots_y-1)) var/bK = (bY * book_storage.book_slots_x) + bX + 1 - var/obj/item/book = book_storage.book_positions[bK] + var/weakref/book_ref = book_storage.book_positions[bK] + var/obj/item/book = book_ref?.resolve() if(!istype(book) || !check_state_in_icon("bookcase", book.icon)) continue var/use_lying_state = "bookcase" - if(bX < (book_storage.book_slots_x-1) && isnull(book_storage.book_positions[bK+1]) && check_state_in_icon("bookcase_flat", book.icon)) + if(bX < (book_storage.book_slots_x-1) && !isnull(book_storage.book_positions[bK+1]) && check_state_in_icon("bookcase_flat", book.icon)) use_lying_state = "bookcase_flat" var/image/book_overlay = overlay_image(book.icon, use_lying_state, book.get_color(), RESET_COLOR) @@ -168,7 +172,7 @@ var/global/list/station_bookcases = list() add_overlay(book_overlay) var/page_state = "[book_overlay.icon_state]-pages" - if(check_state_in_icon(book_overlay.icon, page_state)) + if(check_state_in_icon(page_state, book_overlay.icon)) var/image/page_overlay = overlay_image(book_overlay.icon, page_state, COLOR_WHITE, RESET_COLOR) page_overlay.pixel_x = book_overlay.pixel_x page_overlay.pixel_y = book_overlay.pixel_y diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 28d320d564f..b7186e1926d 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -19,14 +19,6 @@ var/list/connections var/list/other_connections -/obj/structure/catwalk/clear_connections() - connections = null - other_connections = null - -/obj/structure/catwalk/set_connections(dirs, other_dirs) - connections = dirs_to_corner_states(dirs) - other_connections = dirs_to_corner_states(other_dirs) - /obj/structure/catwalk/Initialize() . = ..() DELETE_IF_DUPLICATE_OF(/obj/structure/catwalk) @@ -41,9 +33,6 @@ update_connections(1) update_icon() -/obj/structure/catwalk/can_climb_from_below(var/mob/climber) - return TRUE - /obj/structure/catwalk/Destroy() var/turf/oldloc = loc redraw_nearby_catwalks() @@ -51,6 +40,26 @@ if(istype(oldloc)) for(var/atom/movable/AM in oldloc) AM.fall(oldloc) + oldloc.supporting_platform = null + +// Catwalks need to layer over grass and water. +/obj/structure/catwalk/update_turf_alpha_mask() + return FALSE + +/obj/structure/catwalk/clear_connections() + connections = null + other_connections = null + +/obj/structure/catwalk/is_platform() + return TRUE + +/obj/structure/catwalk/set_connections(dirs, other_dirs) + connections = dirs_to_corner_states(dirs) + other_connections = dirs_to_corner_states(other_dirs) + +/obj/structure/catwalk/can_climb_from_below(var/mob/climber) + return TRUE + /obj/structure/catwalk/proc/redraw_nearby_catwalks() for(var/direction in global.alldirs) @@ -170,6 +179,9 @@ /obj/structure/catwalk/refresh_neighbors() return +/obj/structure/catwalk/is_z_passable() + return !plated_tile + /obj/effect/catwalk_plated name = "plated catwalk spawner" icon = 'icons/obj/structures/catwalks.dmi' diff --git a/code/game/objects/structures/coathanger.dm b/code/game/objects/structures/coathanger.dm index 2360879e427..089425bcc9d 100644 --- a/code/game/objects/structures/coathanger.dm +++ b/code/game/objects/structures/coathanger.dm @@ -46,7 +46,7 @@ var/obj/item/removing = contents[contents.len] user.visible_message( \ SPAN_NOTICE("\The [user] takes \the [removing] off \the [src]."), - SPAN_NOTICE("You take \the [removing] off the \the [src].") + SPAN_NOTICE("You take \the [removing] off \the [src].") ) removing.dropInto(loc) user.put_in_active_hand(removing) @@ -79,7 +79,7 @@ if(user.try_unequip(W, src)) user.visible_message( \ SPAN_NOTICE("\The [user] hangs \the [W] on \the [src]."), \ - SPAN_NOTICE("You hang \the [W] on the \the [src].") \ + SPAN_NOTICE("You hang \the [W] on \the [src].") \ ) update_icon() return TRUE diff --git a/code/game/objects/structures/crates_lockers/closets/__closet.dm b/code/game/objects/structures/crates_lockers/closets/__closet.dm index 9e717d53f97..6bfeca7cfe8 100644 --- a/code/game/objects/structures/crates_lockers/closets/__closet.dm +++ b/code/game/objects/structures/crates_lockers/closets/__closet.dm @@ -548,6 +548,7 @@ var/global/list/closets = list() /decl/interaction_handler/closet_lock_toggle name = "Toggle Lock" expected_target_type = /obj/structure/closet + examine_desc = "toggle the lock" /decl/interaction_handler/closet_lock_toggle/is_possible(atom/target, mob/user, obj/item/prop) . = ..() diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index b34c334d77e..27a6d55f4fc 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -99,7 +99,7 @@ check_health() /obj/structure/closet/statue/attackby(obj/item/I, mob/user) - current_health -= I.get_attack_force(user) + current_health -= I.expend_attack_force(user) user.do_attack_animation(src) visible_message("[user] strikes [src] with [I].") check_health() diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 6df0fd7eb1c..d3aa1f21c39 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -236,9 +236,6 @@ storage_types = CLOSET_STORAGE_ITEMS|CLOSET_STORAGE_STRUCTURES icon = 'icons/obj/closets/bases/large_crate.dmi' -/obj/structure/closet/crate/secure/large/supermatter - closet_appearance = /decl/closet_appearance/large_crate/secure/hazard - //fluff variant /obj/structure/closet/crate/secure/large/reinforced desc = "A hefty, reinforced metal crate with an electronic locking system." diff --git a/code/game/objects/structures/defensive_barrier.dm b/code/game/objects/structures/defensive_barrier.dm index d3d977f5b84..7545fcc0641 100644 --- a/code/game/objects/structures/defensive_barrier.dm +++ b/code/game/objects/structures/defensive_barrier.dm @@ -105,8 +105,7 @@ if(!user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) return ..() - var/decl/species/species = user.get_species() - if(ishuman(user) && species?.can_shred(user) && user.check_intent(I_FLAG_HARM)) + if(user.can_shred() && user.check_intent(I_FLAG_HARM)) take_damage(20) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) return TRUE diff --git a/code/game/objects/structures/doors/_door.dm b/code/game/objects/structures/doors/_door.dm index fe0c9ea879c..ff698963837 100644 --- a/code/game/objects/structures/doors/_door.dm +++ b/code/game/objects/structures/doors/_door.dm @@ -205,12 +205,13 @@ /obj/structure/door/get_alt_interactions(var/mob/user) . = ..() if(density) - . += /decl/interaction_handler/knock_on_door + LAZYADD(., /decl/interaction_handler/knock_on_door) /decl/interaction_handler/knock_on_door name = "Knock On Door" expected_target_type = /obj/structure/door interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEEDS_TURF + examine_desc = "knock on $TARGET_THEM$" /decl/interaction_handler/knock_on_door/invoked(atom/target, mob/user, obj/item/prop) if(!istype(target) || !target.density) @@ -293,7 +294,7 @@ material = /decl/material/solid/organic/plastic /obj/structure/door/exotic_matter - material = /decl/material/solid/supermatter + material = /decl/material/solid/exotic_matter /obj/structure/door/shuttle material = /decl/material/solid/metal/steel diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index 03d91b43ce4..768fe81af7f 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -80,6 +80,7 @@ /decl/interaction_handler/extinguisher_cabinet_open name = "Open/Close" expected_target_type = /obj/structure/extinguisher_cabinet + examine_desc = "open or close $TARGET_THEM$" /decl/interaction_handler/extinguisher_cabinet_open/invoked(atom/target, mob/user, obj/item/prop) var/obj/structure/extinguisher_cabinet/C = target diff --git a/code/game/objects/structures/fireaxe_cabinet.dm b/code/game/objects/structures/fireaxe_cabinet.dm index f16681fa2d4..ec563ffce7e 100644 --- a/code/game/objects/structures/fireaxe_cabinet.dm +++ b/code/game/objects/structures/fireaxe_cabinet.dm @@ -81,7 +81,7 @@ update_icon() return TRUE - var/force = O.get_attack_force(user) + var/force = O.expend_attack_force(user) if(force) user.setClickCooldown(10) attack_animation(user) diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index e0fe3055aa7..4da0fe52597 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -70,7 +70,8 @@ var/lit = FIRE_OUT /// How much fuel is left? var/fuel = 0 - + /// Have we been fed by a bellows recently? + var/bellows_oxygenation = 0 /obj/structure/fire_source/Initialize() . = ..() @@ -138,6 +139,7 @@ /obj/structure/fire_source/proc/die() if(lit == FIRE_LIT) + bellows_oxygenation = 0 lit = FIRE_DEAD last_fuel_ignite_temperature = null last_fuel_burn_temperature = T20C @@ -308,7 +310,9 @@ return ..() /obj/structure/fire_source/proc/get_draught_multiplier() - return has_draught ? draught_values[draught_values[current_draught]] : 1 + . = has_draught ? draught_values[draught_values[current_draught]] : 1 + if(bellows_oxygenation) + . *= 1.25 // Burns 25% hotter while oxygenated. /obj/structure/fire_source/proc/process_fuel(ignition_temperature) var/draught_mult = get_draught_multiplier() @@ -413,6 +417,10 @@ die() return + // Spend our bellows charge. + if(bellows_oxygenation > 0) + bellows_oxygenation-- + fuel -= (FUEL_CONSUMPTION_CONSTANT * get_draught_multiplier()) if(!process_fuel()) die() @@ -461,7 +469,7 @@ switch(lit) if(FIRE_LIT) - if(fuel >= HIGH_FUEL) + if(bellows_oxygenation || fuel >= HIGH_FUEL) var/image/I = image(icon, "[icon_state]_lit") I.appearance_flags |= RESET_COLOR | RESET_ALPHA | KEEP_APART add_overlay(I) @@ -510,6 +518,7 @@ /decl/interaction_handler/adjust_draught name = "Adjust Draught" expected_target_type = /obj/structure/fire_source + examine_desc = "adjust the draught" /decl/interaction_handler/adjust_draught/invoked(atom/target, mob/user, obj/item/prop) var/obj/structure/fire_source/fire = target diff --git a/code/game/objects/structures/flora/_flora.dm b/code/game/objects/structures/flora/_flora.dm index 5395ed57f9a..c38361f4a84 100644 --- a/code/game/objects/structures/flora/_flora.dm +++ b/code/game/objects/structures/flora/_flora.dm @@ -34,7 +34,7 @@ /**Whether the item used by user can cause cut_down to be called. Used to bypass default attack proc for some specific items/tools. */ /obj/structure/flora/proc/can_cut_down(var/obj/item/I, var/mob/user) - return (I.get_attack_force(user) >= 5) && I.sharp //Anything sharp and relatively strong can cut us instantly + return (I.expend_attack_force(user) >= 5) && I.is_sharp() //Anything sharp and relatively strong can cut us instantly /**What to do when the can_cut_down check returns true. Normally simply calls dismantle. */ /obj/structure/flora/proc/play_cut_sound(mob/user) diff --git a/code/game/objects/structures/flora/plant.dm b/code/game/objects/structures/flora/plant.dm index 615e9229cb4..ceb0b5e205e 100644 --- a/code/game/objects/structures/flora/plant.dm +++ b/code/game/objects/structures/flora/plant.dm @@ -81,7 +81,7 @@ return TRUE // Hydrotray boilerplate for taking samples. - if(O.edge && O.w_class < ITEM_SIZE_NORMAL && !user.check_intent(I_FLAG_HARM)) + if(O.has_edge() && O.w_class < ITEM_SIZE_NORMAL && !user.check_intent(I_FLAG_HARM)) if(sampled) to_chat(user, SPAN_WARNING("There's no bits that can be used for a sampling left.")) return TRUE diff --git a/code/game/objects/structures/flora/tree.dm b/code/game/objects/structures/flora/tree.dm index 9ad3314fc77..a088f2297cf 100644 --- a/code/game/objects/structures/flora/tree.dm +++ b/code/game/objects/structures/flora/tree.dm @@ -102,6 +102,7 @@ /obj/structure/flora/tree/pine/init_appearance() icon_state = "pine_[rand(1, 3)]" +var/global/list/christmas_trees = list() /obj/structure/flora/tree/pine/xmas name = "\improper Christmas tree" desc = "O Christmas tree, O Christmas tree..." @@ -109,6 +110,14 @@ icon_state = "pine_c" stump_type = /obj/structure/flora/stump/tree/pine/xmas +/obj/structure/flora/tree/pine/xmas/Initialize(ml, _mat, _reinf_mat) + . = ..() + global.christmas_trees += src + +/obj/structure/flora/tree/pine/xmas/Destroy() + global.christmas_trees -= src + return ..() + /obj/structure/flora/tree/pine/xmas/init_appearance() return //Only one possible icon diff --git a/code/game/objects/structures/fountain.dm b/code/game/objects/structures/fountain.dm index 8d1c904c785..c92c9d360b1 100644 --- a/code/game/objects/structures/fountain.dm +++ b/code/game/objects/structures/fountain.dm @@ -1,20 +1,22 @@ //the fountain of youth/unyouth /obj/structure/fountain - name = "strange fountain" - desc = "The water from the spout is still as if frozen in time, yet the water in the base ripples perpetually." - icon = 'icons/obj/fountain.dmi' - icon_state = "fountain" - density = TRUE - anchored = TRUE - pixel_x = -16 - var/used = FALSE + name = "strange fountain" + desc = "The water from the spout is still as if frozen in time, yet the water in the base ripples perpetually." + icon = 'icons/obj/fountain.dmi' + icon_state = "fountain" + density = TRUE + anchored = TRUE + pixel_x = -16 + light_range = 5 + light_power = 0.5 + var/used = FALSE var/increase_age_prob = (100 / 6) /obj/structure/fountain/Initialize(ml, _mat, _reinf_mat) + if(light_range && light_power) + light_color = get_random_colour(lower = 190) . = ..() - light_color = get_random_colour(lower = 190) - set_light(5, 0.5, light_color) /obj/structure/fountain/attack_hand(var/mob/user) @@ -48,7 +50,7 @@ L.flash_eyes(3) SET_STATUS_MAX(L, STAT_BLURRY, 9) - visible_message("\The [src] erupts in a bright flash of light!") + visible_message(SPAN_WARNING("\The [src] erupts in a bright flash of light!")) playsound(src,'sound/items/time.ogg',100) var/old_age = user.get_age() @@ -65,19 +67,19 @@ new_age = max(new_age, min_age) // This will clamp to the max defined age already so only need to min() if(new_age == old_age) - to_chat(user, "You touch the fountain, and feel your memories sifted through by a great presence. Then, it withdraws, leaving you unchanged.") + to_chat(user, SPAN_CULT_ANNOUNCE("You touch the fountain, and feel your memories sifted through by a great presence. Then, it withdraws, leaving you unchanged.")) else user.set_age(new_age) if(new_age < old_age) - to_chat(user, "You touch the fountain. Everything stops - then reverses. You relive in an instant the events of your life. The fountain, yesterday's lunch, your first love, your first kiss. It all feels as though it just happened moments ago. Then it feels like it never happened at all. Time reverses back into normality and continues its advance. You feel great, but why are you here?") + to_chat(user, SPAN_CULT_ANNOUNCE("You touch the fountain. Everything stops - then reverses. You relive in an instant the events of your life. The fountain, yesterday's lunch, your first love, your first kiss. It all feels as though it just happened moments ago. Then it feels like it never happened at all. Time reverses back into normality and continues its advance. You feel great, but why are you here?")) user.became_younger = TRUE else - to_chat(user, "You touch the fountain. All the memories of your life seem to fade into the distant past as seconds drag like years. You feel the inexplicable sensation of your skin tightening and thinning across your entire body as your muscles degrade and your joints weaken. Time returns to its 'normal' pace. You can only just barely remember touching the fountain.") + to_chat(user, SPAN_CULT_ANNOUNCE("You touch the fountain. All the memories of your life seem to fade into the distant past as seconds drag like years. You feel the inexplicable sensation of your skin tightening and thinning across your entire body as your muscles degrade and your joints weaken. Time returns to its 'normal' pace. You can only just barely remember touching the fountain.")) user.became_older = TRUE SET_HAIR_COLOR(user, COLOR_GRAY80, FALSE) var/max_age = age.standalone_value_descriptors[age.standalone_value_descriptors[length(age.standalone_value_descriptors)]] if(new_age >= max_age) - to_chat(user, "The burden of the years is too much, and you are reduced to dust.") + to_chat(user, SPAN_CULT_ANNOUNCE("The burden of the years is too much, and you are reduced to dust.")) user.dust() used = TRUE @@ -93,6 +95,8 @@ used = TRUE material_alteration = MAT_FLAG_ALTERATION_ALL atom_flags = ATOM_FLAG_OPEN_CONTAINER | ATOM_FLAG_CLIMBABLE + light_range = null + light_power = null /obj/structure/fountain/mundane/Initialize(ml, _mat, _reinf_mat) . = ..() diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 39080d777fb..f1717f3be26 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -85,7 +85,8 @@ add_overlay(I) /obj/structure/grille/Bumped(atom/user) - if(ismob(user)) shock(user, 70) + if(ismob(user)) + shock(user, 70) /obj/structure/grille/attack_hand(mob/user) @@ -101,12 +102,10 @@ var/damage_dealt = 1 var/attack_message = "kicks" - if(ishuman(user)) - var/mob/living/human/H = user - if(H.species.can_shred(H)) - attack_message = "mangles" - damage_dealt = 5 - attack_generic(user,damage_dealt,attack_message) + if(user.can_shred()) + attack_message = "mangles" + damage_dealt = 5 + attack_generic(user, damage_dealt, attack_message) return TRUE /obj/structure/grille/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) @@ -211,9 +210,9 @@ playsound(loc, 'sound/effects/grillehit.ogg', 80, 1) switch(W.atom_damage_type) if(BURN) - take_damage(W.get_attack_force(user)) + take_damage(W.expend_attack_force(user)) if(BRUTE) - take_damage(W.get_attack_force(user) * 0.1) + take_damage(W.expend_attack_force(user) * 0.1) return TRUE return ..() @@ -229,25 +228,23 @@ // returns 1 if shocked, 0 otherwise /obj/structure/grille/proc/shock(mob/user, prb) if(!anchored || destroyed) // anchored/destroyed grilles are never connected - return 0 + return FALSE if(!(material.conductive)) - return 0 + return FALSE if(!prob(prb)) - return 0 + return FALSE if(!in_range(src, user))//To prevent TK and exosuit users from getting shocked - return 0 - var/turf/T = get_turf(src) - var/obj/structure/cable/C = T.get_cable_node() - if(C) - if(electrocute_mob(user, C, src)) - if(C.powernet) - C.powernet.trigger_warning() - spark_at(src, cardinal_only = TRUE) - if(HAS_STATUS(user, STAT_STUN)) - return 1 - else - return 0 - return 0 + return FALSE + var/turf/my_turf = get_turf(src) + var/obj/structure/cable/cable = my_turf.get_cable_node() + if(!cable) + return FALSE + if(!electrocute_mob(user, cable, src)) + return FALSE + if(cable.powernet) + cable.powernet.trigger_warning() + spark_at(src, cardinal_only = TRUE) + return !!HAS_STATUS(user, STAT_STUN) /obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(!destroyed) diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index 12cdf96b365..825e04a5ba6 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -139,7 +139,7 @@ /obj/structure/inflatable/attackby(obj/item/W, mob/user) - if((W.atom_damage_type == BRUTE || W.atom_damage_type == BURN) && (W.can_puncture() || W.get_attack_force(user) > 10)) + if((W.atom_damage_type == BRUTE || W.atom_damage_type == BURN) && (W.can_puncture() || W.expend_attack_force(user) > 10)) visible_message(SPAN_DANGER("\The [user] pierces \the [src] with \the [W]!")) deflate(TRUE) return TRUE diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index cfdce2510d7..3de7c1dba7b 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -88,12 +88,13 @@ return TRUE var/obj/item/stack/material/rods/R = C - if(locate(/obj/structure/catwalk) in get_turf(src)) - to_chat(user, SPAN_WARNING("There is already a catwalk here.")) + var/turf/my_turf = get_turf(src) + if(my_turf?.get_supporting_platform()) + to_chat(user, SPAN_WARNING("There is already a platform here.")) return TRUE else if(R.use(2)) playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - new /obj/structure/catwalk(src.loc, R.material.type) + new /obj/structure/catwalk(my_turf, R.material.type) return TRUE else to_chat(user, SPAN_WARNING("You require at least two rods to complete the catwalk.")) diff --git a/code/game/objects/structures/quicksand.dm b/code/game/objects/structures/quicksand.dm index 383b435943b..d891b03a6ea 100644 --- a/code/game/objects/structures/quicksand.dm +++ b/code/game/objects/structures/quicksand.dm @@ -80,7 +80,7 @@ update_icon() /obj/effect/quicksand/attackby(obj/item/W, mob/user) - if(!exposed && W.get_attack_force(user)) + if(!exposed && W.expend_attack_force(user)) expose() return TRUE else diff --git a/code/game/objects/structures/racks.dm b/code/game/objects/structures/racks.dm index 3ade37d94fe..2cbeb5bb7f5 100644 --- a/code/game/objects/structures/racks.dm +++ b/code/game/objects/structures/racks.dm @@ -29,6 +29,12 @@ I.pixel_y = max(3-i*3, -3) + 1 I.pixel_z = 0 +/obj/structure/rack/adjust_required_attack_dexterity(mob/user, required_dexterity) + // Let people put stuff on tables without necessarily being able to use a gun or such. + if(user?.check_intent(I_FLAG_HELP)) + return DEXTERITY_HOLD_ITEM + return ..() + /obj/structure/rack/attackby(obj/item/O, mob/user, click_params) . = ..() if(!. && !isrobot(user) && O.loc == user && user.try_unequip(O, loc)) diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index 66f0afe1785..2ae373f0513 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -19,6 +19,9 @@ var/broken = FALSE var/neighbor_status = 0 +/obj/structure/railing/should_have_alpha_mask() + return simulated && isturf(loc) && !(locate(/obj/structure/railing) in get_step(loc, SOUTH)) + /obj/structure/railing/mapped anchored = TRUE color = COLOR_ORANGE @@ -288,7 +291,7 @@ WOOD_RAILING_SUBTYPE(yew) update_icon() return TRUE - var/force = W.get_attack_force(user) + var/force = W.expend_attack_force(user) if(force && (W.atom_damage_type == BURN || W.atom_damage_type == BRUTE)) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) visible_message("\The [src] has been [LAZYLEN(W.attack_verb) ? pick(W.attack_verb) : "attacked"] with \the [W] by \the [user]!") diff --git a/code/game/objects/structures/seaweed.dm b/code/game/objects/structures/seaweed.dm index 52a5af6125c..bfa52f88a18 100644 --- a/code/game/objects/structures/seaweed.dm +++ b/code/game/objects/structures/seaweed.dm @@ -31,7 +31,7 @@ icon = 'icons/obj/structures/plants.dmi' /obj/effect/decal/cleanable/lichen/attackby(obj/item/I, mob/user) - if(I.sharp && I.get_attack_force(user) > 1) + if(I.is_sharp() && I.expend_attack_force(user) > 1) qdel(src) return TRUE . = ..() \ No newline at end of file diff --git a/code/game/objects/structures/snowman.dm b/code/game/objects/structures/snowman.dm new file mode 100644 index 00000000000..60382df695c --- /dev/null +++ b/code/game/objects/structures/snowman.dm @@ -0,0 +1,59 @@ +/obj/structure/snowman + name = "man" + icon = 'icons/obj/structures/snowmen/snowman.dmi' + icon_state = ICON_STATE_WORLD + desc = "A happy little $NAME$ smiles back at you!" + anchored = TRUE + material = /decl/material/solid/ice/snow + material_alteration = MAT_FLAG_ALTERATION_ALL // We override name and desc below. + +/obj/structure/snowman/Initialize(ml, _mat, _reinf_mat) + . = ..() + update_icon() + +/obj/structure/snowman/update_material_name(override_name) + SHOULD_CALL_PARENT(FALSE) + if(istype(material)) + SetName("[material.solid_name][initial(name)]") + else + SetName("mystery[initial(name)]") + +/obj/structure/snowman/update_material_desc(override_desc) + SHOULD_CALL_PARENT(FALSE) + if(istype(material)) + var/snowname = "[material.solid_name][initial(name)]" + desc = replacetext(initial(desc), "$NAME$", snowname) + else + desc = replacetext(initial(desc), "$NAME$", "mysteryman") + +/obj/structure/snowman/on_update_icon() + . = ..() + // TODO: make carrot/stick arms/coal require items? + add_overlay(overlay_image(icon, "[icon_state]-decorations", COLOR_WHITE, RESET_COLOR)) + compile_overlays() + +/obj/structure/snowman/proc/user_destroyed(user) + to_chat(user, SPAN_DANGER("\The [src] crumples into a pile of [material.solid_name] after a single solid hit. You monster.")) + physically_destroyed() + +/obj/structure/snowman/attackby(obj/item/used_item, mob/user) + if(user.check_intent(I_FLAG_HARM) && used_item.get_base_attack_force()) + user_destroyed(user) + return TRUE + return ..() + +/obj/structure/snowman/attack_hand(mob/user) + if(user.check_intent(I_FLAG_HARM)) + user_destroyed(user) + return TRUE + return ..() + +/obj/structure/snowman/bot + name = "bot" + icon = 'icons/obj/structures/snowmen/snowbot.dmi' + desc = "A bland-faced little $NAME$. It even has a monitor for a head." + +/obj/structure/snowman/spider + name = "spider" + icon = 'icons/obj/structures/snowmen/snowspider.dmi' + desc = "An impressively-crafted $NAME$. Not nearly as creepy as the real thing." \ No newline at end of file diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm index b14f6db9257..59da1232a7c 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/pew.dm @@ -11,6 +11,9 @@ anchored = TRUE var/connect_neighbors = TRUE +/obj/structure/bed/chair/bench/should_have_alpha_mask() + return simulated && isturf(loc) && connect_neighbors && !(locate(/obj/structure/bed/chair/bench) in get_step(loc, SOUTH)) + /obj/structure/bed/chair/bench/single name = "slatted seat" base_icon = "bench_standing" diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm index caa3e14df93..c6b882a2a67 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm @@ -12,6 +12,7 @@ tool_interaction_flags = 0 var/item_form_type = /obj/item/wheelchair_kit + // TODO: Replace with reagent holder? This doesn't even properly handle non-human bloodstains. var/bloodiness /obj/structure/bed/chair/wheelchair/Initialize() @@ -141,7 +142,7 @@ user.visible_message("[user] starts to lay out \the [src].") if(do_after(user, 4 SECONDS, src)) var/obj/structure/bed/chair/wheelchair/W = new structure_form_type(get_turf(user)) - user.visible_message(SPAN_NOTICE("[user] lays out \the [W.name].")) + user.visible_message(SPAN_NOTICE("[user] lays out \the [W].")) W.add_fingerprint(user) qdel(src) diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index e1fced7cee8..68fba364973 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -26,6 +26,7 @@ var/can_flip = TRUE var/is_flipped = FALSE var/decl/material/additional_reinf_material + var/base_type = /obj/structure/table var/top_surface_noun = "tabletop" @@ -37,11 +38,8 @@ /// Whether items can be placed on this table via clicking. var/can_place_items = TRUE -/obj/structure/table/clear_connections() - connections = null - -/obj/structure/table/set_connections(dirs, other_dirs) - connections = dirs_to_corner_states(dirs) +/obj/structure/table/should_have_alpha_mask() + return simulated && isturf(loc) && !(locate(/obj/structure/table) in get_step(loc, SOUTH)) /obj/structure/table/Initialize() if(ispath(additional_reinf_material, /decl/material)) @@ -65,6 +63,29 @@ update_connections(TRUE) update_icon() +/obj/structure/table/Destroy() + var/turf/oldloc = loc + additional_reinf_material = null + . = ..() + if(istype(oldloc)) + for(var/obj/structure/table/table in range(oldloc, 1)) + if(QDELETED(table)) + continue + table.update_connections(FALSE) + table.update_icon() + +/obj/structure/table/adjust_required_attack_dexterity(mob/user, required_dexterity) + // Let people put stuff on tables without necessarily being able to use a gun or such. + if(user?.check_intent(I_FLAG_HELP)) + return DEXTERITY_HOLD_ITEM + return ..() + +/obj/structure/table/clear_connections() + connections = null + +/obj/structure/table/set_connections(dirs, other_dirs) + connections = dirs_to_corner_states(dirs) + /obj/structure/table/get_material_health_modifier() . = additional_reinf_material ? 0.75 : 0.5 @@ -102,17 +123,6 @@ felted = FALSE additional_reinf_material = null -/obj/structure/table/Destroy() - var/turf/oldloc = loc - additional_reinf_material = null - . = ..() - if(istype(oldloc)) - for(var/obj/structure/table/table in range(oldloc, 1)) - if(QDELETED(table)) - continue - table.update_connections(FALSE) - table.update_icon() - /obj/structure/table/can_dismantle(mob/user) . = ..() if(.) diff --git a/code/game/objects/structures/town_bell.dm b/code/game/objects/structures/town_bell.dm index 4a0faee50f5..45cb1f48451 100644 --- a/code/game/objects/structures/town_bell.dm +++ b/code/game/objects/structures/town_bell.dm @@ -48,7 +48,7 @@ /obj/structure/town_bell/attackby(obj/item/used_item, mob/user) . = ..() - if(used_item.get_attack_force()) + if(used_item.expend_attack_force()) ding_dong() /obj/structure/town_bell/attack_hand(mob/user) diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm index 289b7d7c251..b7fba083f5c 100644 --- a/code/game/objects/structures/transit_tubes.dm +++ b/code/game/objects/structures/transit_tubes.dm @@ -45,7 +45,12 @@ var/moving = 0 var/datum/gas_mixture/air_contents = new() - +/obj/structure/transit_tube_pod/attack_hand(mob/user) + if(!moving && length(contents) && isturf(user.loc)) + user.visible_message(SPAN_NOTICE("\The [user] empties out \the [src]!")) + dump_contents() + return TRUE + return ..() /obj/structure/transit_tube_pod/Destroy() dump_contents() diff --git a/code/game/objects/structures/well.dm b/code/game/objects/structures/well.dm index 639cc2b263a..911413e67e9 100644 --- a/code/game/objects/structures/well.dm +++ b/code/game/objects/structures/well.dm @@ -18,19 +18,6 @@ can_toggle_open = FALSE var/auto_refill -/obj/structure/reagent_dispensers/well/get_alt_interactions(mob/user) - . = ..() - if(reagents?.total_volume >= FLUID_PUDDLE) - LAZYADD(., /decl/interaction_handler/dip_item) - LAZYADD(., /decl/interaction_handler/fill_from) - if(user?.get_active_held_item()) - LAZYADD(., /decl/interaction_handler/empty_into) - -// Overrides due to wonky reagent_dispeners opencontainer flag handling. -/obj/structure/reagent_dispensers/well/can_be_poured_from(mob/user, atom/target) - return (reagents?.maximum_volume > 0) -/obj/structure/reagent_dispensers/well/can_be_poured_into(mob/user, atom/target) - return (reagents?.maximum_volume > 0) // Override to skip open container check. /obj/structure/reagent_dispensers/well/can_drink_from(mob/user) return reagents?.total_volume && user.check_has_mouth() @@ -61,12 +48,16 @@ if(!is_processing && auto_refill) START_PROCESSING(SSobj, src) -/obj/structure/reagent_dispensers/well/attackby(obj/item/W, mob/user) +// Overrides due to wonky reagent_dispeners opencontainer flag handling. +/obj/structure/reagent_dispensers/well/can_be_poured_from(mob/user, atom/target) + return (reagents?.maximum_volume > 0) +/obj/structure/reagent_dispensers/well/can_be_poured_into(mob/user, atom/target) + return (reagents?.maximum_volume > 0) + +/obj/structure/reagent_dispensers/well/get_standard_interactions(var/mob/user) . = ..() - if(!. && user.check_intent(I_FLAG_HELP) && reagents?.total_volume > FLUID_PUDDLE) - user.visible_message(SPAN_NOTICE("\The [user] dips \the [W] into \the [reagents.get_primary_reagent_name()].")) - W.fluid_act(reagents) - return TRUE + if(reagents?.maximum_volume) + LAZYADD(., global._reagent_interactions) /obj/structure/reagent_dispensers/well/Process() if(!reagents || !auto_refill) // if we're full, we only stop at the end of the proc; we need to check for contaminants first diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index bb4bf178107..7b9c56f7110 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -147,21 +147,23 @@ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if (user.check_intent(I_FLAG_HARM)) - if (ishuman(user)) - var/mob/living/human/H = user - if(H.species.can_shred(H)) - return attack_generic(H,25) + if(user.can_shred()) + return attack_generic(user, 25) playsound(src.loc, 'sound/effects/glassknock.ogg', 80, 1) user.do_attack_animation(src) - user.visible_message(SPAN_DANGER("\The [user] bangs against \the [src]!"), - SPAN_DANGER("You bang against \the [src]!"), - "You hear a banging sound.") + user.visible_message( + SPAN_DANGER("\The [user] bangs against \the [src]!"), + SPAN_DANGER("You bang against \the [src]!"), + "You hear a banging sound." + ) else playsound(src.loc, 'sound/effects/glassknock.ogg', 80, 1) - user.visible_message("[user.name] knocks on \the [src].", - "You knock on \the [src].", - "You hear a knocking sound.") + user.visible_message( + SPAN_NOTICE("\The [user] knocks on \the [src]."), + SPAN_NOTICE("You knock on \the [src]."), + "You hear a knocking sound." + ) return TRUE /obj/structure/window/do_simple_ranged_interaction(var/mob/user) @@ -292,7 +294,7 @@ // physical damage types that can impart force; swinging a bat or energy sword if(weapon.atom_damage_type == BRUTE || weapon.atom_damage_type == BURN) user.do_attack_animation(src) - hit(weapon.get_attack_force(user)) + hit(weapon.expend_attack_force(user)) if(current_health <= 7) set_anchored(FALSE) step(src, get_dir(user, src)) @@ -404,12 +406,10 @@ if (polarized) to_chat(user, SPAN_NOTICE("It appears to be wired.")) -/obj/structure/window/proc/set_anchored(var/new_anchored) - if(anchored == new_anchored) - return - anchored = new_anchored - update_connections(1) - update_nearby_icons() +/obj/structure/window/set_anchored(new_anchored) + if((. = ..())) + update_connections(1) + update_nearby_icons() //This proc is used to update the icons of nearby windows. It should not be confused with update_nearby_tiles(), which is an atmos proc! /obj/structure/window/proc/update_nearby_icons() diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index 8150d0abd34..7618f0600ea 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -16,7 +16,7 @@ var/global/list/flooring_cache = list() var/gender = PLURAL /// "that's some grass" var/icon_base var/color = COLOR_WHITE - var/footstep_type = /decl/footsteps/blank + var/footstep_type = /decl/footsteps/plating var/growth_value = 0 var/neighbour_type @@ -35,11 +35,17 @@ var/global/list/flooring_cache = list() /// BYOND ticks. var/build_time = 0 + var/drop_material_on_remove + var/descriptor var/flooring_flags var/remove_timer = 10 var/can_paint var/can_engrave = TRUE + var/can_collect = FALSE + + // Not bloody prints, but rather prints on top of the turf (snow, mud) + var/print_type var/turf_light_range var/turf_light_power @@ -261,8 +267,9 @@ var/global/list/flooring_cache = list() global.flooring_cache[cache_key] = I return global.flooring_cache[cache_key] -/decl/flooring/proc/on_remove() - return +/decl/flooring/proc/on_flooring_remove(turf/removing_from) + if(force_material && drop_material_on_remove) + force_material.create_object(removing_from, rand(3,5)) /decl/flooring/proc/get_movement_delay(var/travel_dir, var/mob/mover) return movement_delay @@ -270,6 +277,17 @@ var/global/list/flooring_cache = list() /decl/flooring/proc/get_movable_alpha_mask_state(atom/movable/mover) return +/decl/flooring/proc/handle_hand_interaction(turf/floor/floor, mob/user) + if(!force_material || !can_collect) + return FALSE + user.visible_message(SPAN_NOTICE("\The [user] begins scraping together some of \the [name]...")) + if(do_after(user, 3 SECONDS, floor) && !QDELETED(floor) && !QDELETED(user) && floor.get_topmost_flooring() == src && isnull(user.get_active_held_item())) + var/list/created = force_material.create_object(floor, 1) + user.visible_message(SPAN_NOTICE("\The [user] scrapes together [english_list(created)].")) + for(var/obj/item/stack/stack in created) + stack.add_to_stacks(user, TRUE) + return TRUE + /decl/flooring/proc/handle_item_interaction(turf/floor/floor, mob/user, obj/item/item) if(!istype(user) || !istype(item) || !istype(floor) || user.check_intent(I_FLAG_HARM)) @@ -351,8 +369,31 @@ var/global/list/flooring_cache = list() /decl/flooring/proc/handle_turf_digging(turf/floor/target) return TRUE +/decl/flooring/proc/turf_exited(turf/target, atom/movable/crosser, atom/new_loc) + return print_type && try_place_footprints(crosser, target, target, new_loc, "going") + +/decl/flooring/proc/turf_entered(turf/target, atom/movable/crosser, atom/old_loc) + return print_type && try_place_footprints(crosser, target, old_loc, target, "coming") + +/decl/flooring/proc/try_place_footprints(atom/movable/crosser, turf/target, turf/from_turf, turf/to_turf, use_state = "going") + if(!ismob(crosser) || !crosser.simulated || !isturf(from_turf) || !isturf(to_turf)) + return FALSE + if(target.check_fluid_depth(FLUID_QDEL_POINT)) + return FALSE + var/movement_dir = get_dir(from_turf, to_turf) + if(!movement_dir) + return FALSE + var/mob/walker = crosser + var/footprint_icon = walker.get_footprints_icon() + if(!footprint_icon) + return FALSE + var/obj/effect/footprints/prints = (locate() in target) || new print_type(target) + prints.add_footprints(crosser, footprint_icon, movement_dir, use_state) + /decl/flooring/proc/turf_crossed(atom/movable/crosser) return -/decl/flooring/proc/can_show_footsteps(turf/target) +/// target is the turf that wants to know if it supports footprints +/// contaminant is, optionally, the material of the coating that wants to be added. +/decl/flooring/proc/can_show_coating_footprints(turf/target, decl/material/contaminant) return TRUE diff --git a/code/game/turfs/flooring/flooring_lava.dm b/code/game/turfs/flooring/flooring_lava.dm index beebb36b775..d8137d15b35 100644 --- a/code/game/turfs/flooring/flooring_lava.dm +++ b/code/game/turfs/flooring/flooring_lava.dm @@ -13,12 +13,12 @@ /decl/flooring/lava/handle_environment_proc(turf/floor/target) . = PROCESS_KILL - if(locate(/obj/structure/catwalk) in target) + if(target.get_supporting_platform()) return var/datum/gas_mixture/environment = target.return_air() var/pressure = environment?.return_pressure() for(var/atom/movable/AM as anything in target.get_contained_external_atoms()) - if(!AM.is_burnable()) + if(!AM.is_burnable() || AM.immune_to_floor_hazards()) continue . = null if(isliving(AM)) diff --git a/code/game/turfs/flooring/flooring_misc.dm b/code/game/turfs/flooring/flooring_misc.dm index eb80eae759e..cdedfb75619 100644 --- a/code/game/turfs/flooring/flooring_misc.dm +++ b/code/game/turfs/flooring/flooring_misc.dm @@ -35,7 +35,6 @@ can_engrave = FALSE color = GLASS_COLOR z_flags = ZM_MIMIC_DEFAULTS - footstep_type = /decl/footsteps/plating force_material = /decl/material/solid/glass constructed = TRUE diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index 2b5fa2c5063..d6d77b4e9e3 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -9,6 +9,8 @@ turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/soil growth_value = 1.1 + can_collect = TRUE + print_type = /obj/effect/footprints /decl/flooring/mud/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) if(!target.reagents?.total_volume) @@ -23,10 +25,12 @@ if(!isliving(crosser)) return var/mob/living/walker = crosser - walker.add_walking_contaminant(/decl/material/solid/soil, rand(2,3)) + walker.add_walking_contaminant(force_material.type, rand(2,3)) -/decl/flooring/mud/can_show_footsteps(turf/target) - return FALSE // So we don't end up covered in a million footsteps that we provided. +/decl/flooring/mud/can_show_coating_footprints(turf/target, decl/material/contaminant) + if(force_material.type == contaminant) // So we don't end up covered in a million footsteps that we provided. + return FALSE + return ..() /decl/flooring/dry_mud name = "dry mud" diff --git a/code/game/turfs/flooring/flooring_natural.dm b/code/game/turfs/flooring/flooring_natural.dm index 9ca61af786e..63d3b2ec93c 100644 --- a/code/game/turfs/flooring/flooring_natural.dm +++ b/code/game/turfs/flooring/flooring_natural.dm @@ -29,6 +29,7 @@ has_base_range = null footstep_type = /decl/footsteps/mud force_material = /decl/material/solid/organic/meat + print_type = /obj/effect/footprints /decl/flooring/barren name = "ground" @@ -52,13 +53,14 @@ turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/clay growth_value = 1.2 + can_collect = TRUE + print_type = /obj/effect/footprints /decl/flooring/ice name = "ice" desc = "A hard, slippery layer of frozen water." icon = 'icons/turf/flooring/ice.dmi' icon_base = "ice" - footstep_type = /decl/footsteps/plating color = COLOR_LIQUID_WATER force_material = /decl/material/solid/ice diff --git a/code/game/turfs/flooring/flooring_path.dm b/code/game/turfs/flooring/flooring_path.dm index 37ce339b7ec..475573d5773 100644 --- a/code/game/turfs/flooring/flooring_path.dm +++ b/code/game/turfs/flooring/flooring_path.dm @@ -7,6 +7,7 @@ neighbour_type = /decl/flooring/path color = null constructed = TRUE + // If null, this is just skipped. var/paving_adjective = "cobbled" var/paver_adjective = "loose" diff --git a/code/game/turfs/flooring/flooring_reinforced.dm b/code/game/turfs/flooring/flooring_reinforced.dm index a926312bc8f..0a46fa2d41c 100644 --- a/code/game/turfs/flooring/flooring_reinforced.dm +++ b/code/game/turfs/flooring/flooring_reinforced.dm @@ -9,7 +9,6 @@ build_cost = 1 build_time = 30 can_paint = 1 - footstep_type = /decl/footsteps/plating force_material = /decl/material/solid/metal/steel constructed = TRUE gender = NEUTER diff --git a/code/game/turfs/flooring/flooring_rock.dm b/code/game/turfs/flooring/flooring_rock.dm index 57063ec8bd0..89b3fd45535 100644 --- a/code/game/turfs/flooring/flooring_rock.dm +++ b/code/game/turfs/flooring/flooring_rock.dm @@ -7,7 +7,6 @@ color = null icon_edge_layer = FLOOR_EDGE_VOLCANIC gender = NEUTER - footstep_type = /decl/footsteps/plating /decl/flooring/rock/update_turf_strings(turf/floor/target) var/decl/material/material = target?.get_material() diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index 27084c19ecb..caf29c81049 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -8,6 +8,8 @@ has_base_range = 4 turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/sand + can_collect = TRUE + print_type = /obj/effect/footprints /decl/flooring/sand/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) if((exposed_temperature > T0C + 1700 && prob(5)) || exposed_temperature > T0C + 3000) diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 6055cdda83d..f1d1dded76e 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -4,9 +4,13 @@ icon = 'icons/turf/flooring/snow.dmi' icon_base = "snow" icon_edge_layer = FLOOR_EDGE_SNOW + flooring_flags = TURF_REMOVE_SHOVEL footstep_type = /decl/footsteps/snow has_base_range = 13 force_material = /decl/material/solid/ice/snow + can_collect = TRUE + print_type = /obj/effect/footprints + drop_material_on_remove = TRUE /decl/flooring/snow/get_movement_delay(var/travel_dir, var/mob/mover) . = ..() @@ -28,6 +32,19 @@ return return ..() +/decl/flooring/snow/turf_crossed(atom/movable/crosser) + if(!isliving(crosser)) + return + var/mob/living/walker = crosser + // at some point this might even be able to use the height + // of the snow flooring layer, so deep snow gives you more coating + walker.add_walking_contaminant(force_material.type, rand(1, 2)) + +/decl/flooring/snow/can_show_coating_footprints(turf/target, decl/material/contaminant) + if(force_material.type == contaminant) // So we don't end up covered in a million footsteps that we provided. + return FALSE + return ..() + /decl/flooring/permafrost name = "permafrost" desc = "A stretch of frozen soil that hasn't seen a thaw for many seasons." diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index e8ede4d119e..d7e8441fda0 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -44,8 +44,7 @@ if(floortype) set_flooring(GET_DECL(floortype), skip_update = TRUE) - if(fill_reagent_type && get_physical_height() < 0) - add_to_reagents(fill_reagent_type, abs(height), phase = MAT_PHASE_LIQUID) + fill_to_zero_height() // try to refill turfs that act as fluid sources if(floor_material || get_topmost_flooring()) if(ml) @@ -71,6 +70,12 @@ STOP_PROCESSING(SSobj, src) return ..() +/turf/floor/proc/fill_to_zero_height() + var/my_height = get_physical_height() + if(fill_reagent_type && my_height < 0 && (!reagents || !QDELING(reagents)) && reagents?.total_volume < abs(my_height)) + var/reagents_to_add = abs(my_height) - reagents?.total_volume + add_to_reagents(fill_reagent_type, reagents_to_add, phase = MAT_PHASE_LIQUID) + /turf/floor/can_climb_from_below(var/mob/climber) return TRUE @@ -92,9 +97,9 @@ /turf/floor/on_reagent_change() . = ..() - var/my_height = get_physical_height() - if(!QDELETED(src) && fill_reagent_type && my_height < 0 && !QDELETED(reagents) && reagents.total_volume < abs(my_height)) - add_to_reagents(fill_reagent_type, abs(my_height) - reagents.total_volume) + if(!QDELETED(src)) + fill_to_zero_height() + update_floor_strings() /turf/floor/proc/set_base_flooring(new_base_flooring, skip_update) if(ispath(new_base_flooring, /decl/flooring)) @@ -138,7 +143,7 @@ for(var/obj/effect/decal/writing/W in src) qdel(W) - _flooring.on_remove() + _flooring.on_flooring_remove(src) if(_flooring.build_type && place_product) // If build type uses material stack, check for it // Because material stack uses different arguments @@ -172,7 +177,6 @@ /turf/floor/proc/update_from_flooring(skip_update) - var/decl/flooring/copy_from = get_topmost_flooring() if(!istype(copy_from)) return // this should never be the case @@ -199,6 +203,9 @@ levelupdate() + for(var/obj/effect/footprints/print in src) + qdel(print) + if(!skip_update) update_icon() for(var/dir in global.alldirs) @@ -276,16 +283,18 @@ return PROCESS_KILL // In case a catwalk or other blocking item is destroyed. -/turf/floor/Exited(atom/movable/AM) +/turf/floor/Exited(atom/movable/AM, atom/new_loc) . = ..() if(!is_processing) for(var/decl/flooring/flooring in get_all_flooring()) if(flooring.has_environment_proc) START_PROCESSING(SSobj, src) break + var/decl/flooring/print_flooring = get_topmost_flooring() + print_flooring?.turf_exited(src, AM, new_loc) // In case something of interest enters our turf. -/turf/floor/Entered(atom/movable/AM) +/turf/floor/Entered(atom/movable/AM, atom/old_loc) . = ..() for(var/decl/flooring/flooring in get_all_flooring()) if(flooring.has_environment_proc) @@ -293,6 +302,8 @@ START_PROCESSING(SSobj, src) flooring.handle_environment_proc(src) break + var/decl/flooring/print_flooring = get_topmost_flooring() + print_flooring?.turf_entered(src, AM, old_loc) /turf/floor/get_plant_growth_rate() var/decl/flooring/flooring = get_topmost_flooring() @@ -303,5 +314,5 @@ flooring?.turf_crossed(AM) return ..() -/turf/floor/can_show_footsteps() - return ..() && get_topmost_flooring()?.can_show_footsteps(src) +/turf/floor/can_show_coating_footprints(decl/material/contaminant = null) + return ..() && get_topmost_flooring()?.can_show_coating_footprints(src, contaminant) diff --git a/code/game/turfs/floors/floor_attackby.dm b/code/game/turfs/floors/floor_attackby.dm index c87c7b79ce9..8b81baf3ae7 100644 --- a/code/game/turfs/floors/floor_attackby.dm +++ b/code/game/turfs/floors/floor_attackby.dm @@ -1,10 +1,14 @@ /turf/floor/attack_hand(mob/user) - if(!ishuman(user)) - return ..() - var/mob/living/human/H = user - var/obj/item/hand = GET_EXTERNAL_ORGAN(H, H.get_active_held_item_slot()) - if(hand && try_graffiti(H, hand)) + + // Collect snow or mud. + var/decl/flooring/flooring = get_topmost_flooring() + if(flooring?.handle_hand_interaction(src, user)) return TRUE + + var/obj/item/hand = GET_EXTERNAL_ORGAN(user, user.get_active_held_item_slot()) + if(hand && try_graffiti(user, hand)) + return TRUE + return ..() /turf/floor/attackby(var/obj/item/used_item, var/mob/user) @@ -24,7 +28,7 @@ return ..() /turf/floor/proc/try_build_catwalk(var/obj/item/used_item, var/mob/user) - if(!(locate(/obj/structure/catwalk) in src) && istype(used_item, /obj/item/stack/material/rods)) + if(istype(used_item, /obj/item/stack/material/rods) && !get_supporting_platform()) var/obj/item/stack/material/rods/R = used_item if (R.use(2)) playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) @@ -73,8 +77,8 @@ if((istype(flooring) && flooring.constructed) || !istype(used_item) || !istype(user)) return FALSE - flooring = get_base_flooring() - if(istype(flooring) && flooring.constructed) + var/decl/flooring/base_flooring = get_base_flooring() + if(istype(base_flooring) && base_flooring.constructed) return FALSE if(!istype(used_item, /obj/item/stack/material/ore) && !istype(used_item, /obj/item/stack/material/lump)) @@ -84,11 +88,11 @@ to_chat(user, SPAN_WARNING("\The [src] is flush with ground level and cannot be backfilled.")) return TRUE - if(!used_item.material?.can_backfill_turf_type) + if(!used_item.material?.can_backfill_floor_type) to_chat(user, SPAN_WARNING("You cannot use \the [used_item] to backfill \the [src].")) return TRUE - var/can_backfill = islist(used_item.material.can_backfill_turf_type) ? is_type_in_list(src, used_item.material.can_backfill_turf_type) : istype(src, used_item.material.can_backfill_turf_type) + var/can_backfill = islist(used_item.material.can_backfill_floor_type) ? is_type_in_list(flooring, used_item.material.can_backfill_floor_type) : istype(flooring, used_item.material.can_backfill_floor_type) if(!can_backfill) to_chat(user, SPAN_WARNING("You cannot use \the [used_item] to backfill \the [src].")) return TRUE diff --git a/code/game/turfs/floors/floor_digging.dm b/code/game/turfs/floors/floor_digging.dm index 3351970e8af..123458d009c 100644 --- a/code/game/turfs/floors/floor_digging.dm +++ b/code/game/turfs/floors/floor_digging.dm @@ -1,13 +1,15 @@ /turf/floor var/gemstone_dropped = FALSE -/turf/floor/proc/is_fundament() +/turf/floor/proc/flooring_is_diggable() var/decl/flooring/flooring = get_topmost_flooring() - return flooring ? !flooring.constructed : TRUE + if(!flooring || flooring.constructed) + return FALSE + return TRUE /turf/floor/can_be_dug(tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) // This should be removed before digging trenches. - if(!is_fundament()) + if(!flooring_is_diggable()) return FALSE var/decl/flooring/flooring = get_base_flooring() if(istype(flooring) && flooring.constructed) @@ -27,7 +29,7 @@ return can_be_dug(tool_hardness, using_tool) && get_physical_height() > -(FLUID_DEEP) /turf/floor/dig_trench(mob/user, tool_hardness = MAT_VALUE_MALLEABLE, using_tool = TOOL_SHOVEL) - if(is_fundament()) + if(flooring_is_diggable()) handle_trench_digging(user) /turf/floor/proc/handle_trench_digging(mob/user) @@ -47,10 +49,21 @@ /turf/floor/get_diggable_resources() var/decl/material/my_material = get_material() - if(is_fundament() && istype(my_material) && my_material.dug_drop_type && (get_physical_height() > -(FLUID_DEEP))) - . = list() - .[my_material.dug_drop_type] = list("amount" = 3, "variance" = 2, "material" = my_material.type) - if(!gemstone_dropped && prob(my_material.gemstone_chance) && LAZYLEN(my_material.gemstone_types)) - gemstone_dropped = TRUE - var/gem_mat = pick(my_material.gemstone_types) - .[/obj/item/gemstone] = list("amount" = 1, "material" = gem_mat) + if(!flooring_is_diggable() || !istype(my_material) || !my_material.dug_drop_type || (get_physical_height() <= -(FLUID_DEEP))) + return + + . = list() + + // All turfs drop resources to backfill them with (or make firepits, etc) + .[my_material.dug_drop_type] = list("amount" = 3, "variance" = 2, "material" = my_material.type) + + // Dirt/mud/etc might have some worms. + if(prob(5 * get_plant_growth_rate())) + .[/obj/item/food/worm] = list("amount" = 1, "material" = /obj/item/food/worm::material) + + // Some materials (like clay) might contain gemstones. + if(!gemstone_dropped && prob(my_material.gemstone_chance) && LAZYLEN(my_material.gemstone_types)) + gemstone_dropped = TRUE + var/gem_mat = pick(my_material.gemstone_types) + .[/obj/item/gemstone] = list("amount" = 1, "material" = gem_mat) + diff --git a/code/game/turfs/floors/floor_height.dm b/code/game/turfs/floors/floor_height.dm index 2dd080f5edd..8968d6020a5 100644 --- a/code/game/turfs/floors/floor_height.dm +++ b/code/game/turfs/floors/floor_height.dm @@ -5,12 +5,18 @@ return density ? 0 : height /turf/floor/set_physical_height(new_height) - if(height != new_height) - height = new_height - for(var/turf/neighbor as anything in RANGE_TURFS(src, 1)) - neighbor.update_icon() - fluid_update() - if(fluid_overlay) - fluid_overlay.update_icon() - return TRUE - return FALSE + + if(height == new_height) + return FALSE + + height = new_height + for(var/turf/neighbor as anything in RANGE_TURFS(src, 1)) + neighbor.update_icon() + fluid_update() + if(fluid_overlay) + fluid_overlay.update_icon() + + for(var/atom/movable/thing in contents) + thing.on_turf_height_change(new_height) + + return TRUE diff --git a/code/game/turfs/floors/floor_icon.dm b/code/game/turfs/floors/floor_icon.dm index 5e060f35848..c4c7cbb8315 100644 --- a/code/game/turfs/floors/floor_icon.dm +++ b/code/game/turfs/floors/floor_icon.dm @@ -45,11 +45,14 @@ layer = initial(layer) if(istype(flooring) && !flooring.render_trenches) // TODO: Update pool tiles/edges to behave properly with this new system. + default_pixel_z = initial(default_pixel_z) + pixel_z = default_pixel_z return FALSE var/my_height = get_physical_height() - if(my_height < 0) - + if(my_height >= 0) + default_pixel_z = initial(default_pixel_z) + else var/height_ratio = clamp(abs(my_height) / FLUID_DEEP, 0, 1) default_pixel_z = -(min(HEIGHT_OFFSET_RANGE, round(HEIGHT_OFFSET_RANGE * height_ratio))) pixel_z = default_pixel_z @@ -76,7 +79,7 @@ var/trench_icon = (istype(neighbor) && neighbor.get_trench_icon()) || get_trench_icon() if(trench_icon) // cache the trench image, keyed by icon and color - var/trench_color = isatom(neighbor) ? neighbor.color : color + var/trench_color = isatom(neighbor) ? neighbor.get_color() : get_color() var/trench_icon_key = "[ref(trench_icon)][trench_color]" I = _trench_image_cache[trench_icon_key] if(!I) @@ -97,6 +100,7 @@ I.appearance_flags |= RESET_COLOR | RESET_ALPHA _height_north_shadow_cache[shadow_alpha_key] = I add_overlay(I) + pixel_z = default_pixel_z /turf/floor/on_update_icon(var/update_neighbors) . = ..() diff --git a/code/game/turfs/floors/subtypes/floor_misc.dm b/code/game/turfs/floors/subtypes/floor_misc.dm index a88eb9784ea..91c472fc284 100644 --- a/code/game/turfs/floors/subtypes/floor_misc.dm +++ b/code/game/turfs/floors/subtypes/floor_misc.dm @@ -54,6 +54,14 @@ /turf/floor/plating _base_flooring = /decl/flooring/plating // Setting here so overrides on /turf/floor do not impact explicitly typed plating turfs. +// Dirt plating for Tradeship farms. +/turf/floor/plating/dirt + name = "dirt" + icon = 'icons/turf/flooring/dirt.dmi' + icon_state = "dirt" + color = "#41311b" + _flooring = /decl/flooring/dirt + /turf/floor/plating/broken _floor_broken = TRUE diff --git a/code/game/turfs/floors/subtypes/floor_natural.dm b/code/game/turfs/floors/subtypes/floor_natural.dm index 09fbbcc07d2..5851afd8ab9 100644 --- a/code/game/turfs/floors/subtypes/floor_natural.dm +++ b/code/game/turfs/floors/subtypes/floor_natural.dm @@ -100,6 +100,13 @@ color = "#ae9e66" _flooring = /decl/flooring/sand +/turf/floor/rock/basalt/sand + name = "sand" + icon = 'icons/turf/flooring/sand.dmi' + icon_state = "sand0" + color = "#ae9e66" + _flooring = /decl/flooring/sand + /turf/floor/rock/sand/water color = COLOR_SKY_BLUE fill_reagent_type = /decl/material/liquid/water diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index cf4d6f67920..87ca2918d54 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -54,8 +54,8 @@ // get overridden almost immediately. // TL;DR: just leave these vars alone. + var/is_outside = OUTSIDE_AREA // non-tmp to allow visibility in mapper. var/tmp/obj/abstract/weather_system/weather - var/tmp/is_outside = OUTSIDE_AREA var/tmp/last_outside_check = OUTSIDE_UNCERTAIN ///The cached air mixture of a turf. Never directly access, use `return_air()`. @@ -86,6 +86,9 @@ var/paint_color + /// Floorlike structures like catwalks. Updated/retrieved by get_supporting_platform() + var/obj/structure/supporting_platform + /turf/Initialize(mapload, ...) . = null && ..() // This weird construct is to shut up the 'parent proc not called' warning without disabling the lint for child types. We explicitly return an init hint so this won't change behavior. @@ -140,6 +143,8 @@ /turf/Destroy() + supporting_platform = null + if(zone) if(can_safely_remove_from_zone()) c_copy_air() @@ -204,11 +209,14 @@ if(weather) . += weather.get_movement_delay(return_air(), travel_dir) // TODO: check user species webbed feet, wearing swimming gear - if(reagents?.total_volume > FLUID_PUDDLE) + if(!get_supporting_platform() && reagents?.total_volume > FLUID_PUDDLE) . += (reagents.total_volume > FLUID_SHALLOW) ? 6 : 3 /turf/attack_hand(mob/user) + SHOULD_CALL_PARENT(FALSE) + + // Find an atom that should be intercepting this click. var/datum/extension/turf_hand/highest_priority_intercept for(var/atom/thing in contents) var/datum/extension/turf_hand/intercept = get_extension(thing, /datum/extension/turf_hand) @@ -218,6 +226,7 @@ user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) var/atom/intercepting_atom = highest_priority_intercept.holder return intercepting_atom.attack_hand(user) + return FALSE /turf/attack_robot(var/mob/user) @@ -243,6 +252,12 @@ if(IS_SHOVEL(W)) + // TODO: move these checks into the interaction handlers. + var/atom/platform = get_supporting_platform() + if(platform) + to_chat(user, SPAN_WARNING("\The [platform] [platform.get_pronouns().is] in the way!")) + return TRUE + if(!can_be_dug(W.material?.hardness)) to_chat(user, SPAN_WARNING("\The [src] is too hard to be dug with \the [W].")) return TRUE @@ -258,6 +273,12 @@ var/decl/material/material = get_material() if(IS_PICK(W) && material) + // TODO: move these checks into the interaction handlers. + var/atom/platform = get_supporting_platform() + if(platform) + to_chat(user, SPAN_WARNING("\The [platform] [platform.get_pronouns().is] in the way!")) + return TRUE + if(material?.hardness <= MAT_VALUE_FLEXIBLE) to_chat(user, SPAN_WARNING("\The [src] is too soft to be excavated with \the [W]. Use a shovel.")) return TRUE @@ -284,19 +305,6 @@ if(IS_COIL(W) && try_build_cable(W, user)) return TRUE - if(reagents?.total_volume >= FLUID_PUDDLE) - if(ATOM_IS_OPEN_CONTAINER(W) && W.reagents) - var/taking = min(reagents.total_volume, REAGENTS_FREE_SPACE(W.reagents)) - if(taking > 0) - to_chat(user, SPAN_NOTICE("You fill \the [W] with [reagents.get_primary_reagent_name()] from \the [src].")) - reagents.trans_to(W, taking) - return TRUE - - if(user.check_intent(I_FLAG_HELP)) - user.visible_message(SPAN_NOTICE("\The [user] dips \the [W] into \the [reagents.get_primary_reagent_name()].")) - W.fluid_act(reagents) - return TRUE - return ..() /turf/Enter(atom/movable/mover, atom/forget) @@ -340,7 +348,7 @@ return 0 // Check if they need to climb out of a hole. - if(has_gravity()) + if(has_gravity() && !get_supporting_platform()) var/mob/mover_mob = mover if(!istype(mover_mob) || (!mover_mob.throwing && !mover_mob.can_overcome_gravity())) var/turf/old_turf = mover.loc @@ -408,11 +416,13 @@ L.Add(t) return L -/turf/proc/contains_dense_objects() +/turf/proc/contains_dense_objects(exceptions) if(density) return 1 for(var/atom/A in src) if(A.density && !(A.atom_flags & ATOM_FLAG_CHECKS_BORDER)) + if(exceptions && (exceptions == A || (A in exceptions))) + continue return 1 return 0 @@ -452,7 +462,7 @@ /turf/proc/try_graffiti(var/mob/vandal, var/obj/item/tool) - if(!tool.sharp || !can_engrave() || !vandal.check_intent(I_FLAG_HELP)) + if(!tool.is_sharp() || !can_engrave() || !vandal.check_intent(I_FLAG_HELP)) return FALSE if(jobban_isbanned(vandal, "Graffiti")) @@ -524,7 +534,7 @@ if(below) below.update_weather(new_weather) -// Updates turf participation in ZAS according to outside status. Must be called whenever the outside status of a turf may change. +/// Updates turf participation in ZAS according to outside status and atmosphere participation bools. Must be called whenever any of those values may change. /turf/proc/update_external_atmos_participation() var/old_outside = last_outside_check last_outside_check = OUTSIDE_UNCERTAIN @@ -697,8 +707,8 @@ return TRUE /turf/clean(clean_forensics = TRUE) - for(var/obj/effect/decal/cleanable/blood/B in contents) - B.clean(clean_forensics) + for(var/obj/effect/decal/cleanable/filth in contents) + filth.clean(clean_forensics) . = ..() //returns 1 if made bloody, returns 0 otherwise @@ -708,8 +718,11 @@ var/mob/living/human/H = M var/unique_enzymes = H.get_unique_enzymes() var/blood_type = H.get_blood_type() + var/blood_reagent = H.species.blood_reagent if(unique_enzymes && blood_type) for(var/obj/effect/decal/cleanable/blood/B in contents) + if(B.chemical != blood_reagent) + continue if(!LAZYACCESS(B.blood_DNA, unique_enzymes)) LAZYSET(B.blood_DNA, unique_enzymes, blood_type) LAZYSET(B.blood_data, unique_enzymes, REAGENT_DATA(H.vessel, H.species.blood_reagent)) @@ -719,13 +732,29 @@ blood_splatter(src, M, 1) return TRUE -/turf/proc/AddTracks(var/typepath,var/bloodDNA,var/comingdir,var/goingdir,var/bloodcolor=COLOR_BLOOD_HUMAN) - if(!simulated) +/// Creates a new /obj/effect/decal/cleanable/blood/tracks instance of a given type, +/// or merges it with an existing (not-yet-cleaned) one that matches typepath and chemical. +/// typepath is a type, not an instance +/// new_chemical is optional argument for things like muddy footprints, where typepath isn't enough +/turf/proc/AddTracks(obj/effect/decal/cleanable/blood/tracks/typepath, bloodDNA, comingdir, goingdir, bloodcolor = COLOR_BLOOD_HUMAN, new_chemical = null) + if(!simulated || check_fluid_depth(FLUID_QDEL_POINT)) return - var/obj/effect/decal/cleanable/blood/tracks/tracks = locate(typepath) in src + // Populate defaults from the given typepath, where possible. + if(isnull(new_chemical)) + new_chemical = typepath::chemical || /decl/material/liquid/blood + + var/obj/effect/decal/cleanable/blood/tracks/tracks = null + for(var/obj/effect/decal/cleanable/blood/tracks/candidate in src) + if(!istype(candidate, typepath)) + continue + if(candidate.invisibility >= INVISIBILITY_ABSTRACT) // has been cleaned + continue + if(candidate.chemical != new_chemical) + continue + tracks = candidate if(!tracks) - tracks = new typepath(src) - tracks.AddTracks(bloodDNA,comingdir,goingdir,bloodcolor) + tracks = new typepath(src, null, new_chemical) + tracks.AddTracks(bloodDNA, comingdir, goingdir, bloodcolor) // Proc called in /turf/Entered() to supply an appropriate fluid overlay. /turf/proc/get_movable_alpha_mask_state(atom/movable/mover) @@ -735,12 +764,13 @@ var/mob/moving_mob = mover if(moving_mob.can_overcome_gravity()) return null - var/fluid_depth = get_fluid_depth() - if(fluid_depth > FLUID_PUDDLE) - if(fluid_depth <= FLUID_SHALLOW) - return "mask_shallow" - if(fluid_depth <= FLUID_DEEP) - return "mask_deep" + if(!get_supporting_platform()) + var/fluid_depth = get_fluid_depth() + if(fluid_depth > FLUID_PUDDLE) + if(fluid_depth <= FLUID_SHALLOW) + return "mask_shallow" + if(fluid_depth <= FLUID_DEEP) + return "mask_deep" /turf/spark_act(obj/effect/sparks/sparks) if(simulated) @@ -802,33 +832,43 @@ /turf/get_affecting_weather() return weather +/turf/can_be_poured_into(atom/source) + return !density + +/turf/proc/get_supporting_platform() + if(isnull(supporting_platform)) + for(var/obj/structure/platform in get_contained_external_atoms()) + if(platform.is_platform()) + supporting_platform = platform + break + return supporting_platform + /turf/get_alt_interactions(mob/user) . = ..() LAZYADD(., /decl/interaction_handler/show_turf_contents) - if(user) - var/obj/item/held = user.get_active_held_item() || user.get_usable_hand_slot_organ() - if(istype(held)) - if(reagents?.total_volume >= FLUID_PUDDLE) - LAZYADD(., /decl/interaction_handler/dip_item) - LAZYADD(., /decl/interaction_handler/fill_from) - LAZYADD(., /decl/interaction_handler/empty_into) - if(IS_SHOVEL(held)) - if(can_dig_pit(held.material?.hardness)) - LAZYDISTINCTADD(., /decl/interaction_handler/dig/pit) - if(can_dig_trench(held.material?.hardness)) - LAZYDISTINCTADD(., /decl/interaction_handler/dig/trench) - if(IS_PICK(held) && can_dig_trench(held.material?.hardness, using_tool = TOOL_PICK)) - LAZYDISTINCTADD(., /decl/interaction_handler/dig/trench) - if(IS_HOE(held) && can_dig_farm(held.material?.hardness)) - LAZYDISTINCTADD(., /decl/interaction_handler/dig/farm) - -/turf/proc/can_show_footsteps() + var/obj/item/held = user ? (user.get_active_held_item() || user.get_usable_hand_slot_organ()) : null + if(!istype(held)) + return + if(IS_SHOVEL(held)) + if(can_dig_pit(held.material?.hardness)) + LAZYADD(., /decl/interaction_handler/dig/pit) + if(can_dig_trench(held.material?.hardness)) + LAZYADD(., /decl/interaction_handler/dig/trench) + if(IS_PICK(held) && can_dig_trench(held.material?.hardness, using_tool = TOOL_PICK)) + LAZYADD(., /decl/interaction_handler/dig/trench) + if(IS_HOE(held) && can_dig_farm(held.material?.hardness)) + LAZYADD(., /decl/interaction_handler/dig/farm) + +/// Contaminant may be the chemical type of the footprint being provided, +/// or null if we just want to know if we support footprints, at all, ever. +/turf/proc/can_show_coating_footprints(decl/material/contaminant) return simulated /decl/interaction_handler/show_turf_contents name = "Show Turf Contents" expected_user_type = /mob interaction_flags = 0 + examine_desc = "list everything on $TARGET_THEM$" /decl/interaction_handler/show_turf_contents/invoked(atom/target, mob/user, obj/item/prop) target.show_atom_list_for_turf(user, get_turf(target)) @@ -841,6 +881,13 @@ /decl/interaction_handler/dig/trench name = "Dig Trench" + examine_desc = "dig a trench" + +/decl/interaction_handler/dig/trench/is_possible(atom/target, mob/user, obj/item/prop) + . = ..() + if(. && istype(target, /turf/floor)) + var/turf/floor/target_turf = target + return target_turf.flooring_is_diggable() /decl/interaction_handler/dig/trench/invoked(atom/target, mob/user, obj/item/prop) prop ||= user.get_usable_hand_slot_organ() // Allows drakes to dig. @@ -855,6 +902,7 @@ /decl/interaction_handler/dig/pit name = "Dig Pit" + examine_desc = "dig a pit" /decl/interaction_handler/dig/pit/invoked(atom/target, mob/user, obj/item/prop) prop ||= user.get_usable_hand_slot_organ() // Allows drakes to dig. @@ -864,6 +912,7 @@ /decl/interaction_handler/dig/farm name = "Dig Farm Plot" + examine_desc = "dig a farm plot" /decl/interaction_handler/dig/farm/invoked(atom/target, mob/user, obj/item/prop) prop ||= user.get_usable_hand_slot_organ() // Allows drakes to dig. @@ -873,3 +922,5 @@ /turf/take_vaporized_reagent(reagent, amount) return assume_gas(reagent, round(amount / REAGENT_UNITS_PER_GAS_MOLE)) + +/turf/proc/is_purged() \ No newline at end of file diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index ce53c478566..19db7607c2e 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -36,6 +36,7 @@ above.ChangeTurf(open_turf_type, keep_air = TRUE, update_open_turfs_above = FALSE) /turf/proc/ChangeTurf(var/turf/N, var/tell_universe = TRUE, var/force_lighting_update = FALSE, var/keep_air = FALSE, var/update_open_turfs_above = TRUE, var/keep_height = FALSE) + if (!N) return @@ -49,6 +50,9 @@ if (!(atom_flags & ATOM_FLAG_INITIALIZED)) return new N(src) + // Rebuilt on next call. + supporting_platform = null + // Track a number of old values for the purposes of raising // state change events after changing the turf to the new type. var/old_fire = fire @@ -140,7 +144,7 @@ if ((old_opacity != opacity) || (tidlu != old_dynamic_lighting) || force_lighting_update) reconsider_lights() - if (tidlu != old_dynamic_lighting) + if (tidlu != old_dynamic_lighting && SSlighting.initialized) // don't fuck with lighting before lighting flush if (tidlu) lighting_build_overlay() else @@ -148,24 +152,24 @@ // end of lighting stuff - // In case the turf isn't marked for update in Initialize (e.g. space), we call this to create any unsimulated edges necessary. - if(W.zone_membership_candidate != old_zone_membership_candidate) - SSair.mark_for_update(src) - // we check the var rather than the proc, because area outside values usually shouldn't be set on turfs W.last_outside_check = OUTSIDE_UNCERTAIN if(W.is_outside != old_outside) // This will check the exterior atmos participation of this turf and all turfs connected by open space below. W.set_outside(old_outside, skip_weather_update = TRUE) - else if(HasBelow(z) && (W.is_open() != old_is_open)) // Otherwise, we do it here if the open status of the turf has changed. - var/turf/checking = src - while(HasBelow(checking.z)) - checking = GetBelow(checking) - if(!isturf(checking)) - break - checking.update_external_atmos_participation() - if(!checking.is_open()) - break + else // We didn't already update our external atmos participation in set_outside. + if(HasBelow(z) && (W.is_open() != old_is_open)) // Otherwise, we do it here if the open status of the turf has changed. + var/turf/checking = src + while(HasBelow(checking.z)) + checking = GetBelow(checking) + if(!isturf(checking)) + break + checking.update_external_atmos_participation() + if(!checking.is_open()) + break + // In case the turf isn't marked for update in Initialize (e.g. space), we call this to create any unsimulated edges necessary. + if(W.zone_membership_candidate != old_zone_membership_candidate) + update_external_atmos_participation() W.update_weather(force_update_below = W.is_open() != old_is_open) diff --git a/code/game/turfs/turf_enter.dm b/code/game/turfs/turf_enter.dm index a0cc300474d..a83d87a855f 100644 --- a/code/game/turfs/turf_enter.dm +++ b/code/game/turfs/turf_enter.dm @@ -29,7 +29,9 @@ regenerate_ao() #endif - if(isturf(old_loc) && has_gravity() && A.can_fall() && !(weakref(A) in skip_height_fall_for)) + var/obj/structure/platform = get_supporting_platform() + if(isturf(old_loc) && has_gravity() && A.can_fall() && !isnull(platform) && !(weakref(A) in skip_height_fall_for)) + var/turf/old_turf = old_loc var/old_height = old_turf.get_physical_height() + old_turf.reagents?.total_volume var/current_height = get_physical_height() + reagents?.total_volume @@ -69,7 +71,7 @@ // Delay to allow transition to the new turf and avoid layering issues. var/mob/M = A M.reset_offsets() - if(get_physical_height() > T.get_physical_height()) + if(platform || (get_physical_height() > T.get_physical_height())) M.reset_layer() else // arbitrary timing value that feels good in practice. it sucks and is inconsistent:( diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index 0ea31b04c6f..f0dd37d11c1 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -62,7 +62,14 @@ fluid_update() // We are now floodable, so wake up our neighbors. /turf/is_flooded(var/lying_mob, var/absolute) - return (flooded || (!absolute && check_fluid_depth(lying_mob ? FLUID_OVER_MOB_HEAD : FLUID_DEEP))) + if(flooded) + return TRUE + if(absolute) + return FALSE + var/required_depth = lying_mob ? FLUID_OVER_MOB_HEAD : FLUID_DEEP + if(get_supporting_platform()) // Increase required depth if we are over the water. + required_depth -= get_physical_height() // depth is negative, -= to increase required depth. + return check_fluid_depth(required_depth) /turf/check_fluid_depth(var/min = 1) . = (get_fluid_depth() >= min) @@ -103,7 +110,7 @@ create_reagents(FLUID_MAX_DEPTH) return ..() -/turf/add_to_reagents(reagent_type, amount, data, safety = FALSE, defer_update = FALSE, phase) +/turf/add_to_reagents(reagent_type, amount, data, safety = FALSE, defer_update = FALSE, phase = null) if(!reagents) create_reagents(FLUID_MAX_DEPTH) return ..() @@ -116,11 +123,17 @@ /turf/fluid_act(var/datum/reagents/fluids) ..() if(!QDELETED(src) && fluids?.total_volume) - fluids.touch_turf(src) - for(var/atom/movable/AM as anything in get_contained_external_atoms()) - if(!AM.submerged()) - continue - AM.fluid_act(fluids) + fluids.touch_turf(src, touch_atoms = FALSE) // Handled in fluid_act() below. + // Wet items that are not supported on a platform or such. + var/effective_volume = fluids?.total_volume + if(get_supporting_platform()) + // Depth is negative height, hence +=. TODO: positive heights? No idea how to handle that. + effective_volume += get_physical_height() + if(effective_volume > FLUID_PUDDLE) + for(var/atom/movable/AM as anything in get_contained_external_atoms()) + if(!AM.submerged()) + continue + AM.fluid_act(fluids) /turf/proc/remove_fluids(var/amount, var/defer_update) if(!reagents?.total_liquid_volume) @@ -179,6 +192,8 @@ var/decl/material/primary_reagent = reagents.get_primary_reagent_decl() if(primary_reagent && (REAGENT_VOLUME(reagents, primary_reagent.type) >= primary_reagent.slippery_amount)) last_slipperiness = primary_reagent.slipperiness + else + last_slipperiness = 0 if(!fluid_overlay) fluid_overlay = new(src, TRUE) fluid_overlay.update_icon() @@ -190,6 +205,7 @@ SSfluids.pending_flows -= src if(last_slipperiness > 0) wet_floor(last_slipperiness) + last_slipperiness = 0 for(var/checkdir in global.cardinal) var/turf/neighbor = get_step_resolving_mimic(src, checkdir) diff --git a/code/game/turfs/turf_footsteps.dm b/code/game/turfs/turf_footsteps.dm index 371d40feefa..bfa1f917156 100644 --- a/code/game/turfs/turf_footsteps.dm +++ b/code/game/turfs/turf_footsteps.dm @@ -12,4 +12,4 @@ return get_footstep_for_mob(/decl/footsteps/water, caller) if(footstep_type) return get_footstep_for_mob(footstep_type, caller) - return get_footstep_for_mob(/decl/footsteps/blank, caller) \ No newline at end of file + return get_footstep_for_mob(/decl/footsteps/blank, caller) diff --git a/code/game/turfs/unsimulated.dm b/code/game/turfs/unsimulated.dm index 67200a23bf8..45e70ce956e 100644 --- a/code/game/turfs/unsimulated.dm +++ b/code/game/turfs/unsimulated.dm @@ -25,3 +25,6 @@ /turf/unsimulated/get_lumcount(var/minlum = 0, var/maxlum = 1) return 0.8 + +/turf/unsimulated/get_movable_alpha_mask_state(atom/movable/mover) + return null diff --git a/code/game/turfs/walls/wall_attacks.dm b/code/game/turfs/walls/wall_attacks.dm index 4f82673b132..9e8eb62472e 100644 --- a/code/game/turfs/walls/wall_attacks.dm +++ b/code/game/turfs/walls/wall_attacks.dm @@ -132,8 +132,8 @@ qdel(WR) return TRUE else - var/force = W.get_attack_force(user) - if((!is_sharp(W) && force >= 10) || force >= 20) + var/force = W.expend_attack_force(user) + if((!W.is_sharp() && !W.has_edge() && force >= 10) || force >= 20) to_chat(user, "\The [src] crumbles away under the force of your [W.name].") physically_destroyed() return TRUE @@ -297,7 +297,7 @@ return TRUE // Attack the wall with items - var/force = W.get_attack_force(user) + var/force = W.expend_attack_force(user) if(istype(W,/obj/item/rcd) || istype(W, /obj/item/chems) || !force || user.check_intent(I_FLAG_HELP)) return ..() diff --git a/code/game/turfs/walls/wall_natural.dm b/code/game/turfs/walls/wall_natural.dm index 96967f55e2e..ce8ea3dbbce 100644 --- a/code/game/turfs/walls/wall_natural.dm +++ b/code/game/turfs/walls/wall_natural.dm @@ -172,7 +172,7 @@ if(!prob(reinf_material.ore_spread_chance)) continue var/turf/wall/natural/target_turf = get_step_resolving_mimic(src, trydir) - if(!istype(target_turf) || !isnull(target_turf.reinf_material)) + if(!istype(target_turf) || !isnull(target_turf.reinf_material) || target_turf.ramp_slope_direction) continue target_turf.set_turf_materials(target_turf.material, reinf_material) target_turf.spread_deposit() diff --git a/code/game/turfs/walls/wall_natural_ramps.dm b/code/game/turfs/walls/wall_natural_ramps.dm index 7566bf7578f..760c4a01f0d 100644 --- a/code/game/turfs/walls/wall_natural_ramps.dm +++ b/code/game/turfs/walls/wall_natural_ramps.dm @@ -54,6 +54,7 @@ /decl/interaction_handler/drill_ramp name = "Drill Ramp" expected_target_type = /turf/wall/natural + examine_desc = "drill a ramp in the direction you are facing" /decl/interaction_handler/drill_ramp/is_possible(atom/target, mob/user, obj/item/prop) . = ..() diff --git a/code/game/world.dm b/code/game/world.dm index 079e43ae543..1c65910d510 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -126,7 +126,7 @@ var/global/world_topic_last = world.timeofday throttle[2] = reason /world/Topic(T, addr, master, key) - direct_output(diary, "TOPIC: \"[T]\", from:[addr], master:[master], key:[key][log_end]") + to_file(diary, "TOPIC: \"[T]\", from:[addr], master:[master], key:[key][log_end]") if (global.world_topic_last > world.timeofday) global.world_topic_throttle = list() //probably passed midnight @@ -146,11 +146,14 @@ var/global/world_topic_last = world.timeofday return command.try_use(T, addr, master, key) +var/global/_reboot_announced = FALSE /world/Reboot(var/reason) if(get_config_value(/decl/config/toggle/wait_for_sigusr1_reboot) && reason != 3) text2file("foo", "reboot_called") - to_world("World reboot waiting for external scripts. Please be patient.") + if(!global._reboot_announced) + to_world("World reboot waiting for external scripts. Please be patient.") + global._reboot_announced = TRUE global.Master.restart_timeout = 5 MINUTES return @@ -198,7 +201,7 @@ var/global/world_topic_last = world.timeofday /world/proc/save_mode(var/the_mode) var/F = file("data/mode.txt") fdel(F) - direct_output(F, the_mode) + to_file(F, the_mode) /world/proc/load_motd() join_motd = safe_file2text("config/motd.txt", FALSE) diff --git a/code/game/world_topic_commands.dm b/code/game/world_topic_commands.dm index 2bbc05f1436..cfd86601053 100644 --- a/code/game/world_topic_commands.dm +++ b/code/game/world_topic_commands.dm @@ -356,6 +356,5 @@ uid = "topic_command_prometheus_metrics" /decl/topic_command/secure/prometheus_metrics/use() - if(!global.prometheus_metrics) - return "Metrics not ready" - return global.prometheus_metrics.collect() + var/static/decl/prometheus_metrics/prometheus_metrics = IMPLIED_DECL + return prometheus_metrics.collect() diff --git a/code/modules/ZAS/Turf.dm b/code/modules/ZAS/Turf.dm index 82312858622..016d69b4c53 100644 --- a/code/modules/ZAS/Turf.dm +++ b/code/modules/ZAS/Turf.dm @@ -35,9 +35,9 @@ var/list/postponed #ifdef MULTIZAS - for(var/d = 1, d < 64, d *= 2) + for(var/d in global.cardinalz) #else - for(var/d = 1, d < 16, d *= 2) + for(var/d in global.cardinal) #endif var/turf/unsim = get_step(src, d) @@ -167,7 +167,7 @@ #define GET_ZONE_NEIGHBOURS(T, ret) \ ret = 0; \ if (T.zone) { \ - for (var/_gzn_dir in gzn_check) { \ + for (var/_gzn_dir in ZAS_GZN_CHECK) { \ var/turf/other = get_step(T, _gzn_dir); \ if (istype(other) && other.simulated && other.zone == T.zone) { \ var/block; \ @@ -198,7 +198,7 @@ if (!(check_dirs & (check_dirs - 1))) //Equivalent to: if(IsInteger(log(2, .))) return TRUE - for(var/dir in global.csrfz_check) + for(var/dir in ZAS_CSRFZ_CHECK) //for each pair of "adjacent" cardinals (e.g. NORTH and WEST, but not NORTH and SOUTH) if((dir & check_dirs) == dir) //check that they are connected by the corner turf diff --git a/code/modules/abstract/abstract_fluid_direction.dm b/code/modules/abstract/abstract_fluid_direction.dm new file mode 100644 index 00000000000..7dabaa77623 --- /dev/null +++ b/code/modules/abstract/abstract_fluid_direction.dm @@ -0,0 +1,37 @@ +/obj/abstract/force_fluid_flow + icon_state = "arrow" + +/obj/abstract/force_fluid_flow/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/abstract/force_fluid_flow/north + dir = NORTH + +/obj/abstract/force_fluid_flow/south + dir = SOUTH + +/obj/abstract/force_fluid_flow/east + dir = EAST + +/obj/abstract/force_fluid_flow/west + dir = WEST + +/obj/abstract/force_fluid_flow/southeast + dir = SOUTHEAST + +/obj/abstract/force_fluid_flow/southwest + dir = SOUTHWEST + +/obj/abstract/force_fluid_flow/northeast + dir = NORTHEAST + +/obj/abstract/force_fluid_flow/northwest + dir = NORTHWEST + +/obj/abstract/force_fluid_flow/LateInitialize() + . = ..() + var/atom/movable/fluid_overlay/fluids = locate() in loc + fluids.force_flow_direction = dir + fluids.queue_icon_update() + qdel(src) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 5e06996ca46..c211f274d60 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -321,7 +321,7 @@ var/global/BSACooldown = 0 dat += "Remove" dat += "
" if(update_file) - direct_output(info, infos) + to_file(info, infos) dat += "
Add Comment
" diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 546f5753cee..8483aa50faa 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -34,7 +34,7 @@ return show_browser(src, F, "window=investigate[subject];size=800x300") - if("hrefs") //persistant logs and stuff + if("hrefs") //persistent logs and stuff if(get_config_value(/decl/config/toggle/log_hrefs)) if(global.world_href_log) show_browser(src, global.world_href_log, "window=investigate[subject];size=800x300") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index b93ccd2bde9..aba99df3baf 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -883,16 +883,6 @@ var/global/list/admin_verbs_mod = list( log_and_message_admins("told everyone to man up and deal with it.") -/client/proc/give_spell(mob/T as mob in SSmobs.mob_list) // -- Urist - set category = "Fun" - set name = "Give Spell" - set desc = "Gives a spell to a mob." - var/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spells - if(!S) return - T.add_spell(new S) - SSstatistics.add_field_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_and_message_admins("gave [key_name(T)] the spell [S].") - /client/proc/change_lobby_screen() set name = "Lobby Screen: Change" set category = "Fun" diff --git a/code/modules/admin/secrets/investigation/attack_logs.dm b/code/modules/admin/secrets/investigation/attack_logs.dm index 201adaa00bf..5a915fd379c 100644 --- a/code/modules/admin/secrets/investigation/attack_logs.dm +++ b/code/modules/admin/secrets/investigation/attack_logs.dm @@ -69,9 +69,8 @@ . = filters_per_client[user.client] if(!.) . = list() - for(var/af_type in subtypesof(/attack_filter)) - var/attack_filter/af = af_type - if(initial(af.category) == af_type) + for(var/datum/attack_filter/af_type as anything in subtypesof(/datum/attack_filter)) + if(TYPE_IS_ABSTRACT(af_type)) continue . += new af_type(src) filters_per_client[user.client] = . @@ -79,56 +78,56 @@ /datum/admin_secret_item/investigation/attack_logs/proc/get_filter_html(user) . = list() for(var/filter in get_user_filters(user)) - var/attack_filter/af = filter + var/datum/attack_filter/af = filter . += af.get_html() . = jointext(.," | ") /datum/admin_secret_item/investigation/attack_logs/proc/filter_log(user, var/datum/attack_log/al) for(var/filter in get_user_filters(user)) - var/attack_filter/af = filter + var/datum/attack_filter/af = filter if(af.filter_attack(al)) return TRUE return FALSE /datum/admin_secret_item/investigation/attack_logs/proc/reset_user_filters(user) for(var/filter in get_user_filters(user)) - var/attack_filter/af = filter + var/datum/attack_filter/af = filter af.reset() -/attack_filter - var/category = /attack_filter +/datum/attack_filter + abstract_type = /datum/attack_filter var/datum/admin_secret_item/investigation/attack_logs/holder -/attack_filter/New(var/holder) +/datum/attack_filter/New(var/holder) ..() src.holder = holder -/attack_filter/Topic(href, href_list) +/datum/attack_filter/Topic(href, href_list) if(..()) return TRUE if(OnTopic(href_list)) holder.execute(usr) return TRUE -/attack_filter/proc/get_html() +/datum/attack_filter/proc/get_html() return -/attack_filter/proc/reset() +/datum/attack_filter/proc/reset() return -/attack_filter/proc/filter_attack(var/datum/attack_log/al) +/datum/attack_filter/proc/filter_attack(var/datum/attack_log/al) return FALSE -/attack_filter/proc/OnTopic(href_list) +/datum/attack_filter/proc/OnTopic(href_list) return FALSE /* * Filter logs with one or more missing clients */ -/attack_filter/no_client +/datum/attack_filter/no_client var/filter_missing_clients = TRUE -/attack_filter/no_client/get_html() +/datum/attack_filter/no_client/get_html() . = list() . += "Must have clients: " if(filter_missing_clients) @@ -137,7 +136,7 @@ . += "YesNo" . = jointext(.,null) -/attack_filter/no_client/OnTopic(href_list) +/datum/attack_filter/no_client/OnTopic(href_list) if(href_list["yes"] && !filter_missing_clients) filter_missing_clients = TRUE return TRUE @@ -145,10 +144,10 @@ filter_missing_clients = FALSE return TRUE -/attack_filter/no_client/reset() +/datum/attack_filter/no_client/reset() filter_missing_clients = initial(filter_missing_clients) -/attack_filter/no_client/filter_attack(var/datum/attack_log/al) +/datum/attack_filter/no_client/filter_attack(var/datum/attack_log/al) if(!filter_missing_clients) return FALSE if(al.attacker && al.attacker.client.ckey == NO_CLIENT_CKEY) @@ -160,19 +159,19 @@ /* Either subject must be the selected client */ -/attack_filter/must_be_given_ckey +/datum/attack_filter/must_be_given_ckey var/ckey_filter var/check_attacker = TRUE var/check_victim = TRUE var/description = "Either ckey is" -/attack_filter/must_be_given_ckey/reset() +/datum/attack_filter/must_be_given_ckey/reset() ckey_filter = null -/attack_filter/must_be_given_ckey/get_html() +/datum/attack_filter/must_be_given_ckey/get_html() return "[description]: [ckey_filter ? ckey_filter : "*ANY*"]" -/attack_filter/must_be_given_ckey/OnTopic(href_list) +/datum/attack_filter/must_be_given_ckey/OnTopic(href_list) if(!href_list["select_ckey"]) return var/ckey = input("Select ckey to filter on","Select ckey", ckey_filter) as null|anything in get_ckeys() @@ -183,7 +182,7 @@ ckey_filter = ckey return TRUE -/attack_filter/must_be_given_ckey/proc/get_ckeys() +/datum/attack_filter/must_be_given_ckey/proc/get_ckeys() . = list() for(var/log in attack_log_repository.attack_logs_) var/datum/attack_log/al = log @@ -194,7 +193,7 @@ . = sortTim(., /proc/cmp_text_asc) . += "*ANY*" -/attack_filter/must_be_given_ckey/filter_attack(var/datum/attack_log/al) +/datum/attack_filter/must_be_given_ckey/filter_attack(var/datum/attack_log/al) if(!ckey_filter) return FALSE if(check_attacker && al.attacker && al.attacker.client.ckey == ckey_filter) @@ -206,19 +205,19 @@ /* Attacker must be the selected client */ -/attack_filter/must_be_given_ckey/attacker +/datum/attack_filter/must_be_given_ckey/attacker description = "Attacker ckey is" check_victim = FALSE -/attack_filter/must_be_given_ckey/attacker/filter_attack(al) +/datum/attack_filter/must_be_given_ckey/attacker/filter_attack(al) return ..(al, TRUE, FALSE) /* Victim must be the selected client */ -/attack_filter/must_be_given_ckey/victim +/datum/attack_filter/must_be_given_ckey/victim description = "Victim ckey is" check_attacker = FALSE -/attack_filter/must_be_given_ckey/victim/filter_attack(al) +/datum/attack_filter/must_be_given_ckey/victim/filter_attack(al) return ..(al, FALSE, TRUE) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 8adc77ad2e7..c9e2798773b 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -174,7 +174,7 @@ else if(task == "permissions") if(!D) return var/list/permissionlist = list() - for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left BITSHIFT_LEFT + for(var/i=1, i<=R_MAXPERMISSION, i = BITSHIFT_LEFT(i, 1)) permissionlist[rights2text(i)] = i var/new_permission = input("Select a permission to turn on/off", "Permission toggle", null, null) as null|anything in permissionlist if(!new_permission) return @@ -979,8 +979,7 @@ var/obj/effect/stop/S S = new /obj/effect/stop(M.loc) S.victim = M - spawn(20) - qdel(S) + QDEL_IN(S, 2 SECONDS) var/turf/floor/T = get_turf(M) if(istype(T)) diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 75b90a33712..179676df7f3 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -10,7 +10,7 @@ var/inactive_groups = SSair.zones.len - active_groups var/hotspots = 0 - for(var/obj/fire/hotspot in world) + for(var/obj/fire/hotspot in SSair.active_hotspots) hotspots++ var/active_on_main_station = 0 diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 24bbe2f93ab..0527485b126 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -51,7 +51,7 @@ return message_admins("[key_name_admin(src)] accessed file: [path]") - direct_output(src, run(file(path))) + open_file_for(src, file(path)) to_chat(src, "Attempting to send file, this may take a fair few minutes if the file is very large.") return @@ -64,6 +64,6 @@ set name = "Show Server Log" set desc = "Shows today's server log." - direct_output(usr, run(diary)) + open_file_for(usr, diary) SSstatistics.add_field_details("admin_verb","VTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return \ No newline at end of file diff --git a/code/modules/admin/verbs/grief_fixers.dm b/code/modules/admin/verbs/grief_fixers.dm index 2394d2a8197..afea1d4a6fe 100644 --- a/code/modules/admin/verbs/grief_fixers.dm +++ b/code/modules/admin/verbs/grief_fixers.dm @@ -4,35 +4,55 @@ if(!check_rights(R_ADMIN|R_DEBUG)) return - if(alert("WARNING: Executing this command will perform a full reset of atmosphere. All pipelines will lose any gas that may be in them, and all zones will be reset to contain air mix as on roundstart. The supermatter engine will also be stopped (to prevent overheat due to removal of coolant). Do not use unless the map is suffering serious atmospheric issues due to grief or bug.", "Full Atmosphere Reboot", "No", "Yes") == "No") + if(alert("WARNING: Executing this command will perform a full reset of atmosphere. All pipelines will lose any gas that may be in them, and all zones will be reset to contain air mix as on roundstart. This may require any atmos-based generators to shut down. Do not use unless the map is suffering serious atmospheric issues due to grief or bug.", "Full Atmosphere Reboot", "No", "Yes") == "No") return SSstatistics.add_field_details("admin_verb","FA") log_and_message_admins("Full atmosphere reset initiated by [usr].") - to_world("Initiating restart of atmosphere. The server may lag a bit.") + to_world(SPAN_DANGER("Initiating restart of atmosphere. The server may lag a bit.")) sleep(10) var/current_time = world.timeofday - // Depower the supermatter, as it would quickly blow up once we remove all gases from the pipes. - for(var/obj/machinery/power/supermatter/S in SSmachines.machinery) - S.power = 0 - to_chat(usr, "\[1/5\] - Supermatter depowered") + var/list/steps = sortTim(decls_repository.get_decls_of_subtype_unassociated(/decl/atmos_grief_fix_step), /proc/cmp_decl_sort_value_asc) + var/step_count = length(steps) + for(var/step_index in 1 to length(step_count)) + var/decl/atmos_grief_fix_step/fix_step = steps[step_index] + to_chat(usr, "\[[step_index]/[step_count]\] - [fix_step.name].") + fix_step.act() + to_world(SPAN_DANGER("Atmosphere restart completed in [(world.timeofday - current_time)/(1 SECOND)] seconds.")) +/decl/atmos_grief_fix_step + abstract_type = /decl/atmos_grief_fix_step + var/name + +/decl/atmos_grief_fix_step/proc/act() + return + +/decl/atmos_grief_fix_step/purge_pipenets + name = "All pipenets purged of gas" + sort_order = 1 + +/decl/atmos_grief_fix_step/purge_pipenets/act() // Remove all gases from all pipenets - for(var/net in SSmachines.pipenets) - var/datum/pipe_network/PN = net + for(var/datum/pipe_network/PN as anything in SSmachines.pipenets) for(var/datum/gas_mixture/G in PN.gases) - G.gas = list() + G.gas.Cut() G.update_values() - to_chat(usr, "\[2/5\] - All pipenets purged of gas.") +/decl/atmos_grief_fix_step/delete_zones + name = "All ZAS Zones removed" + sort_order = 2 +/decl/atmos_grief_fix_step/delete_zones/act() // Delete all zones. for(var/zone/Z in SSair.zones) Z.c_invalidate() - to_chat(usr, "\[3/5\] - All ZAS Zones removed.") +/decl/atmos_grief_fix_step/reset_turfs + name = "All turfs reset to roundstart values" + sort_order = 3 +/decl/atmos_grief_fix_step/reset_turfs/act() var/list/unsorted_overlays = list() var/list/all_gasses = decls_repository.get_decls_of_subtype(/decl/material/gas) for(var/id in all_gasses) @@ -41,12 +61,11 @@ for(var/turf/T in world) T.air = null - T.overlays.Remove(unsorted_overlays) T.zone = null - to_chat(usr, "\[4/5\] - All turfs reset to roundstart values.") +/decl/atmos_grief_fix_step/reboot_zas + name = "ZAS Rebooted" + sort_order = 4 +/decl/atmos_grief_fix_step/reboot_zas/act() SSair.reboot() - - to_chat(usr, "\[5/5\] - ZAS Rebooted") - to_world("Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds.") diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 6638c344791..b599e3845e6 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -24,6 +24,7 @@ var/global/camera_range_display_status = 0 var/global/intercom_range_display_status = 0 +var/global/list/debug_camera_range_markers = list() /obj/effect/debugging/camera_range icon = 'icons/480x480.dmi' icon_state = "25percent" @@ -33,11 +34,25 @@ var/global/intercom_range_display_status = 0 default_pixel_x = -224 default_pixel_y = -224 reset_offsets(0) + global.debug_camera_range_markers += src +/obj/effect/debugging/camera_range/Destroy() + global.debug_camera_range_markers -= src + return ..() + +var/global/list/mapping_debugging_markers = list() /obj/effect/debugging/marker icon = 'icons/turf/areas.dmi' icon_state = "yellow" +/obj/effect/debugging/marker/Initialize(mapload) + . = ..() + global.mapping_debugging_markers += src + +/obj/effect/debugging/marker/Destroy() + global.mapping_debugging_markers -= src + return ..() + /obj/effect/debugging/marker/Move() return 0 @@ -56,7 +71,7 @@ var/global/intercom_range_display_status = 0 - for(var/obj/effect/debugging/camera_range/C in world) + for(var/obj/effect/debugging/camera_range/C as anything in debug_camera_range_markers) qdel(C) if(camera_range_display_status) @@ -113,7 +128,7 @@ var/global/intercom_range_display_status = 0 else intercom_range_display_status = 1 - for(var/obj/effect/debugging/marker/M in world) + for(var/obj/effect/debugging/marker/M in global.mapping_debugging_markers) qdel(M) if(intercom_range_display_status) @@ -150,7 +165,6 @@ var/global/list/debug_verbs = list ( /client/proc/hide_debug_verbs, /client/proc/testZAScolors, /client/proc/testZAScolors_remove, - /datum/admins/proc/setup_supermatter, /datum/admins/proc/setup_fusion, /client/proc/atmos_toggle_debug, /client/proc/spawn_tanktransferbomb, diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index 8605ad977a8..06c04257bab 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -318,6 +318,7 @@ variable = param_var_name + // TODO: check for list-typed O? Proc does not exist on non-datum types. var_value = O.get_variable_value(variable) if(autodetect_class) diff --git a/code/modules/admin/view_variables/helpers.dm b/code/modules/admin/view_variables/helpers.dm index 8e30b30b4d7..7b7155b8391 100644 --- a/code/modules/admin/view_variables/helpers.dm +++ b/code/modules/admin/view_variables/helpers.dm @@ -1,6 +1,4 @@ - -// Keep these two together, they *must* be defined on both -// If /client ever becomes /datum/client or similar, they can be merged +// If /client/var/parent_type ever stops being /datum, this proc will need to be redefined on client. /datum/proc/get_view_variables_header() return "[src]" @@ -8,16 +6,16 @@ return {" [src]
- << + \<\< [dir2text(dir)] - >> + \>\> "} /mob/living/get_view_variables_header() return {" [src] -
<< [dir2text(dir)] >> +
\<\< [dir2text(dir)] \>\>
[ckey ? ckey : "No ckey"] / [real_name ? real_name : "No real name"]
BRUTE:[get_damage(BRUTE)] @@ -37,7 +35,6 @@ return ..() + {" - @@ -50,6 +47,9 @@ + + + diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index f1118e682c9..971e7a394bc 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -1,4 +1,3 @@ - /client/proc/view_var_Topic(href, href_list, hsrc) //This should all be moved over to datum/admins/Topic() or something ~Carn if( (usr.client != src) || !src.holder ) @@ -97,17 +96,6 @@ src.holder.show_player_panel(victim) href_list["datumrefresh"] = href_list["mob_player_panel"] - else if(href_list["give_spell"]) - if(!check_rights(R_ADMIN|R_FUN)) return - - var/mob/victim = locate(href_list["give_spell"]) - if(!istype(victim)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_spell(victim) - href_list["datumrefresh"] = href_list["give_spell"] - else if(href_list["godmode"]) if(!check_rights(R_REJUVENATE)) return @@ -302,8 +290,8 @@ return var/list/possible_ailments = list() for(var/atype in subtypesof(/datum/ailment)) - var/datum/ailment/ailment = get_ailment_reference(atype) - if(ailment && ailment.category != ailment.type && ailment.can_apply_to(limb)) + var/datum/ailment/ailment = get_ailment_reference(atype) // will not get abstract ailments + if(ailment && ailment.can_apply_to(limb)) possible_ailments |= ailment var/datum/ailment/ailment = input("Select an ailment type to add.", "Add Ailment") as null|anything in possible_ailments @@ -690,6 +678,33 @@ item.set_material(new_material.type) to_chat(usr, "Set material of [item] to [item.get_material()].") + else if(href_list["give_ability"]) + var/mob/target = locate(href_list["give_ability"]) + if(!istype(target) || QDELETED(target)) + to_chat(usr, "Mob no longer exists.") + else + var/list/abilities = decls_repository.get_decls_of_type_unassociated(/decl/ability) + abilities = abilities.Copy() + abilities -= target.get_all_abilities() + var/decl/ability/ability = input(usr, "Which ability do you wish to grant?", "Give Ability") as null|anything in abilities + if(istype(ability) && !QDELETED(usr) && !QDELETED(target)) + if(target.add_ability(ability.type)) + log_and_message_admins("has given [ability] to [key_name(target)].") + else + to_chat(usr, SPAN_WARNING("Failed to give [ability] to [target]!")) + + else if(href_list["remove_ability"]) + var/mob/target = locate(href_list["remove_ability"]) + if(!istype(target) || QDELETED(target)) + to_chat(usr, "Mob no longer exists.") + else + var/decl/ability/ability = input(usr, "Which ability do you wish to remove?", "Remove Ability") as null|anything in target.get_all_abilities() + if(istype(ability) && !QDELETED(usr) && !QDELETED(target)) + if(target.remove_ability(ability.type)) + log_and_message_admins("has removed [ability] from [key_name(target)].") + else + to_chat(usr, SPAN_WARNING("Failed to remove [ability] from [target]!")) + if(href_list["datumrefresh"]) var/datum/datum_to_refresh = locate(href_list["datumrefresh"]) if(istype(datum_to_refresh, /datum) || istype(datum_to_refresh, /client)) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index a4cf742c410..47dcc913a45 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -12,7 +12,7 @@ var/secured = 1 var/list/attached_overlays = null - var/obj/item/assembly_holder/holder = null + var/obj/item/assembly_holder/holder = null // currently can be a TTV or assemblyholder, todo make ttv use assemblyholder var/cooldown = 0//To prevent spam var/wires = WIRE_RECEIVE | WIRE_PULSE @@ -24,10 +24,7 @@ /obj/item/assembly/Destroy() if(!QDELETED(holder)) - if(holder.a_left == src) - holder.a_left = null - if(holder.a_right == src) - holder.a_right = null + // the holder has the responsibility to clear its associated vars on destroy QDEL_NULL(holder) else holder = null diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index dba32a26eb2..9a794b2c900 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -159,8 +159,7 @@ if(a_right) a_right.holder = null a_right.forceMove(T) - spawn(0) - qdel(src) + qdel(src) return /obj/item/assembly_holder/hear_talk(mob/living/M, msg, verb, decl/language/speaking) diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 1253014bd80..77623305db1 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -67,7 +67,7 @@ holder.update_icon() update_beams() -/obj/item/assembly/infra/interact(mob/user)//TODO: change this this to the wire control panel +/obj/item/assembly/infra/interact(mob/user)//TODO: change this to the wire control panel if(!secured) return if(!CanInteract(user, global.physical_topic_state)) diff --git a/code/modules/atmospherics/components/binary_devices/pump.dm b/code/modules/atmospherics/components/binary_devices/pump.dm index a0b05c844cd..156e19f2f39 100644 --- a/code/modules/atmospherics/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/components/binary_devices/pump.dm @@ -213,6 +213,7 @@ Thus, the two variables affect pump operation are set in New(): /decl/interaction_handler/binary_pump_toggle name = "Switch On/Off" expected_target_type = /obj/machinery/atmospherics/binary/pump + examine_desc = "turn $TARGET_THEM$ on or off" /decl/interaction_handler/binary_pump_toggle/invoked(atom/target, mob/user, obj/item/prop) var/obj/machinery/atmospherics/binary/pump/P = target diff --git a/code/modules/atmospherics/components/unary/heat_exchanger.dm b/code/modules/atmospherics/components/unary/heat_exchanger.dm index 30849bc53b9..f19c4ecb4f7 100644 --- a/code/modules/atmospherics/components/unary/heat_exchanger.dm +++ b/code/modules/atmospherics/components/unary/heat_exchanger.dm @@ -4,7 +4,7 @@ icon_state = "intact" density = TRUE - name = "Heat Exchanger" + name = "heat exchanger" desc = "Exchanges heat between two input gases. Setup for fast heat transfer." var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null diff --git a/code/modules/atmospherics/datum_pipeline.dm b/code/modules/atmospherics/datum_pipeline.dm index cace579e65b..55a58d0138f 100644 --- a/code/modules/atmospherics/datum_pipeline.dm +++ b/code/modules/atmospherics/datum_pipeline.dm @@ -45,7 +45,7 @@ . = ..() -/datum/pipeline/Process()//This use to be called called from the pipe networks +/datum/pipeline/Process()//This use to be called from the pipe networks //Check to see if pressure is within acceptable limits var/pressure = air.return_pressure() if(pressure > maximum_pressure) @@ -193,13 +193,23 @@ network.update = 1 /datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + + if(air.volume <= 0) // Avoid div by zero. + return + var/total_heat_capacity = air.heat_capacity() var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) + if(total_heat_capacity <= 0) // Avoid div by zero. + return + if(SHOULD_PARTICIPATE_IN_ZONES(target) && !target.blocks_air) + + if(partial_heat_capacity <= 0) + return + var/delta_temperature = 0 var/sharer_heat_capacity = 0 - if(target.zone) delta_temperature = (air.temperature - target.zone.air.temperature) sharer_heat_capacity = target.zone.air.heat_capacity() @@ -207,18 +217,14 @@ delta_temperature = (air.temperature - target.air.temperature) sharer_heat_capacity = target.air.heat_capacity() + if(sharer_heat_capacity <= 0) + return + var/self_temperature_delta = 0 var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity > 0) && (partial_heat_capacity > 0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - + var/heat = thermal_conductivity * delta_temperature * ( partial_heat_capacity * sharer_heat_capacity / (partial_heat_capacity + sharer_heat_capacity) ) + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity air.temperature += self_temperature_delta if(target.zone) @@ -228,33 +234,29 @@ else if(target.external_atmosphere_participation && !target.blocks_air) - var/turf/modeled_location = target - var/datum/gas_mixture/target_air = modeled_location.return_air() + if(partial_heat_capacity <= 0) + return - var/delta_temperature = air.temperature - target_air.temperature - var/sharer_heat_capacity = target_air.heat_capacity() + var/datum/gas_mixture/target_air = target.return_air() + var/sharer_heat_capacity = target_air?.heat_capacity() - if((sharer_heat_capacity > 0) && (partial_heat_capacity > 0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - air.temperature += -heat/total_heat_capacity - else - return 1 + if(sharer_heat_capacity <= 0) + return - else - if((target.heat_capacity > 0) && (partial_heat_capacity > 0)) - var/delta_temperature = air.temperature - target.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + var/delta_temperature = air.temperature - target_air.temperature + var/heat = thermal_conductivity * delta_temperature * ( partial_heat_capacity * sharer_heat_capacity / (partial_heat_capacity+sharer_heat_capacity) ) + air.temperature += -heat/total_heat_capacity - air.temperature -= heat/total_heat_capacity - // Only increase the temperature of the target if it's simulated. - if(target.simulated) - target.temperature += heat/target.heat_capacity + else if((target.heat_capacity > 0) && (partial_heat_capacity > 0)) + var/delta_temperature = air.temperature - target.temperature + var/heat = thermal_conductivity * delta_temperature * ( partial_heat_capacity * target.heat_capacity / (partial_heat_capacity + target.heat_capacity) ) + air.temperature -= heat/total_heat_capacity + // Only increase the temperature of the target if it's simulated. + if(target.simulated) + target.temperature += heat/target.heat_capacity if(network) - network.update = 1 + network.update = TRUE //surface must be the surface area in m^2 /datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) diff --git a/code/modules/atmospherics/he_pipes.dm b/code/modules/atmospherics/he_pipes.dm index 55afc98fd37..380adf9245f 100644 --- a/code/modules/atmospherics/he_pipes.dm +++ b/code/modules/atmospherics/he_pipes.dm @@ -1,28 +1,26 @@ /obj/machinery/atmospherics/pipe/simple/heat_exchanging - icon = 'icons/atmos/heat.dmi' - icon_state = "11" - color = "#404040" - pipe_color = "#404040" - level = LEVEL_ABOVE_PLATING - connect_types = CONNECT_TYPE_HE + icon = 'icons/atmos/heat.dmi' + icon_state = "11" + color = "#404040" + pipe_color = "#404040" + level = LEVEL_ABOVE_PLATING + connect_types = CONNECT_TYPE_HE interact_offline = TRUE //Needs to be set so that pipes don't say they lack power in their description - var/initialize_directions_he - var/surface = 2 //surface area in m^2 - var/icon_temperature = T20C //stop small changes in temperature causing an icon refresh build_icon_state = "he" - atom_flags = 0 // no painting + atom_flags = 0 // no painting + maximum_pressure = 360 ATM + fatigue_pressure = 300 ATM + can_buckle = TRUE + buckle_lying = TRUE appearance_flags = KEEP_TOGETHER + var/initialize_directions_he + var/surface = 2 //surface area in m^2 + var/icon_temperature = T20C //stop small changes in temperature causing an icon refresh var/thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT var/minimum_temperature_difference = 20 - maximum_pressure = 360 ATM - fatigue_pressure = 300 ATM - - can_buckle = 1 - buckle_lying = 1 - /obj/machinery/atmospherics/pipe/simple/heat_exchanging/Initialize() . = ..() add_filter("glow",1, list(type = "drop_shadow", x = 0, y = 0, offset = 0, size = 4)) @@ -57,62 +55,57 @@ update_icon() /obj/machinery/atmospherics/pipe/simple/heat_exchanging/Process() + if(!parent) ..() - else - var/turf/turf = loc - var/datum/gas_mixture/pipe_air = return_air() - if(istype(loc, /turf/space)) - parent.radiate_heat_to_space(surface, 1) - else if(istype(turf) && turf.simulated) - var/turf/pipe_turf = loc - var/environment_temperature = 0 - if(pipe_turf.blocks_air) - environment_temperature = pipe_turf.temperature - else - var/datum/gas_mixture/environment = pipe_turf.return_air() - environment_temperature = environment.temperature - if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) - parent.temperature_interact(pipe_turf, volume, thermal_conductivity) - - if(buckled_mob) - var/hc = pipe_air.heat_capacity() - var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500) - pipe_air.temperature = avg_temp - buckled_mob.bodytemperature = avg_temp - - var/heat_limit = 1000 - - var/mob/living/human/H = buckled_mob - if(istype(H) && H.species) - heat_limit = H.get_mob_temperature_threshold(HEAT_LEVEL_3) - - if(pipe_air.temperature > heat_limit + 1) - buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_CHEST, used_weapon = "Excessive Heat") - - //fancy radiation glowing - if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K - if(abs(pipe_air.temperature - icon_temperature) > 10) - icon_temperature = pipe_air.temperature - var/scale = max((icon_temperature - 500) / 1500, 0) - - var/h_r = heat2color_r(icon_temperature) - var/h_g = heat2color_g(icon_temperature) - var/h_b = heat2color_b(icon_temperature) - - if(icon_temperature < 2000) //scale up overlay until 2000K - h_r = 64 + (h_r - 64)*scale - h_g = 64 + (h_g - 64)*scale - h_b = 64 + (h_b - 64)*scale - var/scale_color = rgb(h_r, h_g, h_b) - var/list/animate_targets = get_above_oo() + src - for (var/thing in animate_targets) - var/atom/movable/AM = thing - animate(AM, color = scale_color, time = 2 SECONDS, easing = SINE_EASING) - animate_filter("glow", list(color = scale_color, time = 2 SECONDS, easing = LINEAR_EASING)) - set_light(min(3, scale*2.5), min(3, scale*2.5), scale_color) + return + + // Handle pipe heat exchange. + var/turf/turf = loc + var/datum/gas_mixture/pipe_air = return_air() + if(istype(loc, /turf/space)) + parent.radiate_heat_to_space(surface, 1) + else if(istype(turf) && turf.simulated) + var/environment_temperature = 0 + if(turf.blocks_air) + environment_temperature = turf.temperature else - set_light(0, 0) + var/datum/gas_mixture/environment = turf.return_air() + environment_temperature = environment?.temperature || 0 + if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) + parent.temperature_interact(turf, volume, thermal_conductivity) + + // Burn mobs buckled to this pipe. + if(buckled_mob) + var/hc = pipe_air.heat_capacity() + var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500) + pipe_air.temperature = avg_temp + buckled_mob.bodytemperature = avg_temp + var/heat_limit = buckled_mob.get_mob_temperature_threshold(HEAT_LEVEL_3) + if(pipe_air.temperature > heat_limit + 1) + buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_CHEST, used_weapon = "Excessive Heat") + + //fancy radiation glowing + if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K + if(abs(pipe_air.temperature - icon_temperature) > 10) + icon_temperature = pipe_air.temperature + var/scale = max((icon_temperature - 500) / 1500, 0) + var/h_r = heat2color_r(icon_temperature) + var/h_g = heat2color_g(icon_temperature) + var/h_b = heat2color_b(icon_temperature) + if(icon_temperature < 2000) //scale up overlay until 2000K + h_r = 64 + (h_r - 64)*scale + h_g = 64 + (h_g - 64)*scale + h_b = 64 + (h_b - 64)*scale + var/scale_color = rgb(h_r, h_g, h_b) + var/list/animate_targets = get_above_oo() + src + for (var/thing in animate_targets) + var/atom/movable/AM = thing + animate(AM, color = scale_color, time = 2 SECONDS, easing = SINE_EASING) + animate_filter("glow", list(color = scale_color, time = 2 SECONDS, easing = LINEAR_EASING)) + set_light(min(3, scale*2.5), min(3, scale*2.5), scale_color) + else + set_light(0, 0) /obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction icon = 'icons/atmos/junction.dmi' diff --git a/code/modules/augment/active/armblades.dm b/code/modules/augment/active/armblades.dm index 1f214a266fe..8e40dc7c43e 100644 --- a/code/modules/augment/active/armblades.dm +++ b/code/modules/augment/active/armblades.dm @@ -5,8 +5,8 @@ icon = 'icons/obj/augment.dmi' desc = "A handy utility blade for the discerning augmentee. Warranty void if used for cutting." base_parry_chance = 30 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE attack_verb = list("stabbed", "sliced", "cut") origin_tech = @'{"materials":1,"engineering":1,"combat":2}' material = /decl/material/solid/metal/steel diff --git a/code/modules/augment/augment.dm b/code/modules/augment/augment.dm index ff96aabde04..ef122136d5c 100644 --- a/code/modules/augment/augment.dm +++ b/code/modules/augment/augment.dm @@ -2,11 +2,13 @@ name = "embedded augment" desc = "An embedded augment." icon = 'icons/obj/augment.dmi' + w_class = ITEM_SIZE_TINY // Need to be tiny to fit inside limbs. //By default these fit on both flesh and robotic organs and are robotic organ_properties = ORGAN_PROP_PROSTHETIC default_action_type = /datum/action/item_action/organ/augment material = /decl/material/solid/metal/steel origin_tech = @'{"materials":1,"magnets":2,"engineering":2,"biotech":1}' + w_class = ITEM_SIZE_TINY var/descriptor = "" var/known = TRUE diff --git a/code/modules/blob/blob.dm b/code/modules/blob/blob.dm index e8f001355f0..5de1ac052b1 100644 --- a/code/modules/blob/blob.dm +++ b/code/modules/blob/blob.dm @@ -182,11 +182,11 @@ var/damage = 0 switch(W.atom_damage_type) if(BURN) - damage = (W.get_attack_force(user) / fire_resist) + damage = (W.expend_attack_force(user) / fire_resist) if(IS_WELDER(W)) playsound(loc, 'sound/items/Welder.ogg', 100, 1) if(BRUTE) - damage = (W.get_attack_force(user) / brute_resist) + damage = (W.expend_attack_force(user) / brute_resist) take_damage(damage, W.atom_damage_type) return TRUE diff --git a/code/modules/bodytype/_bodytype.dm b/code/modules/bodytype/_bodytype.dm index af6100a8d0a..7bb3afd7447 100644 --- a/code/modules/bodytype/_bodytype.dm +++ b/code/modules/bodytype/_bodytype.dm @@ -41,10 +41,14 @@ var/global/list/bodytypes_by_category = list() var/ignited_icon = 'icons/mob/OnFire.dmi' /// Drawn over mob bodyparts when they are surgically open/retracted. var/surgery_overlay_icon = 'icons/mob/surgery.dmi' + /// Icon to use when walking across snow, sand or mud. Separate to bloody footprints for now. + var/footprints_icon = 'icons/mob/footprints/footprints.dmi' /// Used to retrieve bodytypes by pronoun type in get_bodytype_by_pronouns() var/associated_gender /// Appearance/display related features. var/appearance_flags = 0 + // Preview in prefs positioning. If null, uses defaults set on a static list in preferences.dm. + var/list/character_preview_screen_locs /// What noun is used when filing your nails? var/nail_noun /// What tech levels should limbs of this type use/need? @@ -845,3 +849,6 @@ var/global/list/limbs_with_nails = list( /decl/bodytype/proc/get_movement_slowdown(var/mob/living/human/H) return movement_slowdown + +/decl/bodytype/proc/get_footprints_icon() + return footprints_icon diff --git a/code/modules/butchery/butchery_hook.dm b/code/modules/butchery/butchery_hook.dm index 0cc357e6f75..58945994cd8 100644 --- a/code/modules/butchery/butchery_hook.dm +++ b/code/modules/butchery/butchery_hook.dm @@ -195,12 +195,14 @@ update_icon() if(!tool?.do_tool_interaction(TOOL_KNIFE, user, src, 3 SECONDS, start_message = butchery_string, success_message = butchery_string, check_skill = SKILL_COOKING)) return FALSE - if(!QDELETED(user) && !QDELETED(last_occupant) && occupant == last_occupant && occupant_state == last_state) + if(!QDELETED(user) && !QDELETED(last_occupant) && occupant == last_occupant && occupant_state == last_state && user.get_active_held_item() == tool) var/decl/butchery_data/butchery_data = GET_DECL(occupant.butchery_data) if(!butchery_data) return FALSE + tool.add_blood(occupant) + switch(next_state) if(CARCASS_SKINNED) if(occupant.currently_has_skin()) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index d582783e091..f83344b4841 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -45,7 +45,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the client.sending |= asset_name var/job = ++client.last_asset_job - direct_output(client, browse("", "window=asset_cache_browser")) + show_browser(client, "", "window=asset_cache_browser") var/t = 0 var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT @@ -85,7 +85,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the client.sending |= unreceived var/job = ++client.last_asset_job - direct_output(client, browse("", "window=asset_cache_browser")) + show_browser(client, "", "window=asset_cache_browser") var/t = 0 var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 76f96bf0866..5a4b8a07540 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -170,7 +170,7 @@ var/global/list/localhost_addresses = list( holder.owner = src handle_staff_login() - //preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum) + //preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum) prefs = SScharacter_setup.preferences_datums[ckey] if(!prefs) prefs = new /datum/preferences(src) diff --git a/code/modules/client/preference_setup/global/01_ui.dm b/code/modules/client/preference_setup/global/01_ui.dm index de5797080c4..f48998316cc 100644 --- a/code/modules/client/preference_setup/global/01_ui.dm +++ b/code/modules/client/preference_setup/global/01_ui.dm @@ -1,16 +1,17 @@ var/global/list/valid_icon_sizes = list(32, 48, 64, 96, 128) /datum/preferences - var/clientfps = 0 - var/ooccolor = "#010000" //Whatever this is set to acts as 'reset' color and is thus unusable as an actual custom color - var/icon_size = 64 + var/clientfps = 0 + var/ooccolor = "#010000" //Whatever this is set to acts as 'reset' color and is thus unusable as an actual custom color + var/icon_size = 64 var/UI_style - var/UI_style_alpha = 255 - var/UI_style_color = COLOR_WHITE - var/UI_mouseover_alpha = 255 - var/UI_mouseover_color = COLOR_AMBER + var/UI_style_alpha = 255 + var/UI_style_color = "#8a8872" + var/UI_style_highlight_color = "#545e78" + var/UI_mouseover_alpha = 255 + var/UI_mouseover_color = COLOR_AMBER //Style for popup tooltips - var/tooltip_style = "Midnight" + var/tooltip_style = "Midnight" /datum/category_item/player_setup_item/player_global/ui name = "UI" @@ -25,26 +26,28 @@ var/global/list/valid_icon_sizes = list(32, 48, 64, 96, 128) if(ui_style) pref.UI_style = ui_style.type - pref.icon_size = R.read("icon_size") - pref.UI_mouseover_color = R.read("UI_mouseover_color") - pref.UI_mouseover_alpha = R.read("UI_mouseover_alpha") - pref.UI_style_color = R.read("UI_style_color") - pref.UI_style_alpha = R.read("UI_style_alpha") - pref.ooccolor = R.read("ooccolor") - pref.clientfps = R.read("clientfps") + pref.icon_size = R.read("icon_size") + pref.UI_mouseover_color = R.read("UI_mouseover_color") + pref.UI_mouseover_alpha = R.read("UI_mouseover_alpha") + pref.UI_style_color = R.read("UI_style_color") + pref.UI_style_highlight_color = R.read("UI_style_highlight_color") + pref.UI_style_alpha = R.read("UI_style_alpha") + pref.ooccolor = R.read("ooccolor") + pref.clientfps = R.read("clientfps") /datum/category_item/player_setup_item/player_global/ui/save_preferences(datum/pref_record_writer/W) var/decl/ui_style/ui_style = GET_DECL(pref.UI_style) W.write("UI_style", ui_style.uid) - W.write("icon_size", pref.icon_size) - W.write("UI_mouseover_color", pref.UI_mouseover_color) - W.write("UI_mouseover_alpha", pref.UI_mouseover_alpha) - W.write("UI_style_color", pref.UI_style_color) - W.write("UI_style_alpha", pref.UI_style_alpha) - W.write("ooccolor", pref.ooccolor) - W.write("clientfps", pref.clientfps) + W.write("icon_size", pref.icon_size) + W.write("UI_mouseover_color", pref.UI_mouseover_color) + W.write("UI_mouseover_alpha", pref.UI_mouseover_alpha) + W.write("UI_style_color", pref.UI_style_color) + W.write("UI_style_highlight_color", pref.UI_style_highlight_color) + W.write("UI_style_alpha", pref.UI_style_alpha) + W.write("ooccolor", pref.ooccolor) + W.write("clientfps", pref.clientfps) /datum/category_item/player_setup_item/player_global/ui/sanitize_preferences() @@ -52,13 +55,14 @@ var/global/list/valid_icon_sizes = list(32, 48, 64, 96, 128) for(var/decl/ui_style/style in get_ui_styles()) all_ui_style_types |= style.type - pref.UI_style = sanitize_inlist(pref.UI_style, all_ui_style_types, all_ui_style_types[1]) - pref.UI_mouseover_color = sanitize_hexcolor(pref.UI_mouseover_color, initial(pref.UI_mouseover_color)) - pref.UI_mouseover_alpha = sanitize_integer(pref.UI_mouseover_alpha, 0, 255, initial(pref.UI_mouseover_alpha)) - pref.UI_style_color = sanitize_hexcolor(pref.UI_style_color, initial(pref.UI_style_color)) - pref.UI_style_alpha = sanitize_integer(pref.UI_style_alpha, 0, 255, initial(pref.UI_style_alpha)) - pref.ooccolor = sanitize_hexcolor(pref.ooccolor, initial(pref.ooccolor)) - pref.clientfps = sanitize_integer(pref.clientfps, CLIENT_MIN_FPS, CLIENT_MAX_FPS, initial(pref.clientfps)) + pref.UI_style = sanitize_inlist(pref.UI_style, all_ui_style_types, all_ui_style_types[1]) + pref.UI_mouseover_color = sanitize_hexcolor(pref.UI_mouseover_color, initial(pref.UI_mouseover_color)) + pref.UI_mouseover_alpha = sanitize_integer(pref.UI_mouseover_alpha, 0, 255, initial(pref.UI_mouseover_alpha)) + pref.UI_style_color = sanitize_hexcolor(pref.UI_style_color, initial(pref.UI_style_color)) + pref.UI_style_highlight_color = sanitize_hexcolor(pref.UI_style_highlight_color, initial(pref.UI_style_highlight_color)) + pref.UI_style_alpha = sanitize_integer(pref.UI_style_alpha, 0, 255, initial(pref.UI_style_alpha)) + pref.ooccolor = sanitize_hexcolor(pref.ooccolor, initial(pref.ooccolor)) + pref.clientfps = sanitize_integer(pref.clientfps, CLIENT_MIN_FPS, CLIENT_MAX_FPS, initial(pref.clientfps)) if(!isnum(pref.icon_size)) pref.icon_size = initial(pref.icon_size) @@ -71,6 +75,11 @@ var/global/list/valid_icon_sizes = list(32, 48, 64, 96, 128) . += "
__
" . += "reset" . += "" + . += "UI Highlight" + . += "[pref.UI_style_highlight_color]" + . += "
__
" + . += "reset" + . += "" . += "UI Opacity" . += "[pref.UI_style_alpha]" . += "reset" @@ -98,26 +107,62 @@ var/global/list/valid_icon_sizes = list(32, 48, 64, 96, 128) . += "Client FPS: [pref.clientfps]
" /datum/category_item/player_setup_item/player_global/ui/OnTopic(var/href,var/list/href_list, var/mob/user) + if(href_list["select_style"]) var/decl/ui_style/current_style = GET_DECL(pref.UI_style) var/decl/ui_style/UI_style_new = input(user, "Choose UI style.", CHARACTER_PREFERENCE_INPUT_TITLE, current_style) as null|anything in get_ui_styles() - if(!istype(UI_style_new) || !CanUseTopic(user)) return TOPIC_NOACTION + if(!istype(UI_style_new) || !CanUseTopic(user)) + return TOPIC_NOACTION pref.UI_style = UI_style_new.type - return TOPIC_REFRESH + if(!isnull(UI_style_new.default_color)) + pref.UI_style_color = UI_style_new.default_color + if(!isnull(UI_style_new.default_alpha)) + pref.UI_style_alpha = UI_style_new.default_alpha + . = TOPIC_REFRESH else if(href_list["select_color"]) var/UI_style_color_new = input(user, "Choose UI color, dark colors are not recommended!", "Global Preference", pref.UI_style_color) as color|null - if(isnull(UI_style_color_new) || !CanUseTopic(user)) return TOPIC_NOACTION + if(isnull(UI_style_color_new) || !CanUseTopic(user)) + return TOPIC_NOACTION pref.UI_style_color = UI_style_color_new + . = TOPIC_REFRESH + + else if(href_list["select_highlight_color"]) + var/UI_style_highlight_color_new = input(user, "Choose UI highlight color, dark colors are not recommended!", "Global Preference", pref.UI_style_highlight_color) as color|null + if(isnull(UI_style_highlight_color_new) || !CanUseTopic(user)) return TOPIC_NOACTION + pref.UI_style_highlight_color = UI_style_highlight_color_new return TOPIC_REFRESH else if(href_list["select_alpha"]) var/UI_style_alpha_new = input(user, "Select UI alpha (transparency) level, between 50 and 255.", "Global Preference", pref.UI_style_alpha) as num|null - if(isnull(UI_style_alpha_new) || (UI_style_alpha_new < 50 || UI_style_alpha_new > 255) || !CanUseTopic(user)) return TOPIC_NOACTION + if(isnull(UI_style_alpha_new) || (UI_style_alpha_new < 50 || UI_style_alpha_new > 255) || !CanUseTopic(user)) + return TOPIC_NOACTION pref.UI_style_alpha = UI_style_alpha_new - return TOPIC_REFRESH + . = TOPIC_REFRESH + + else if(href_list["reset"]) + switch(href_list["reset"]) + if("ui") + pref.UI_style_color = initial(pref.UI_style_color) + if("highlight") + pref.UI_style_highlight_color = initial(pref.UI_style_highlight_color) + if("alpha") + pref.UI_style_alpha = initial(pref.UI_style_alpha) + if("mouseover_color") + pref.UI_mouseover_color = initial(pref.UI_mouseover_color) + if("mouseover_alpha") + pref.UI_mouseover_alpha = initial(pref.UI_mouseover_alpha) + if("ooc") + pref.ooccolor = initial(pref.ooccolor) + . = TOPIC_REFRESH - else if(href_list["select_ooc_color"]) + if(. == TOPIC_REFRESH) + // This is overkill, but we do not currently have a way to tell what elements should grab a new color or alpha. + // TODO: maybe limit or debounce/queue this? + user.hud_reset(TRUE) + return + + if(href_list["select_ooc_color"]) var/new_ooccolor = input(user, "Choose OOC color:", "Global Preference") as color|null if(new_ooccolor && can_select_ooc_color(user) && CanUseTopic(user)) pref.ooccolor = new_ooccolor @@ -142,20 +187,6 @@ var/global/list/valid_icon_sizes = list(32, 48, 64, 96, 128) pref.tooltip_style = tooltip_style_new return TOPIC_REFRESH - else if(href_list["reset"]) - switch(href_list["reset"]) - if("ui") - pref.UI_style_color = initial(pref.UI_style_color) - if("alpha") - pref.UI_style_alpha = initial(pref.UI_style_alpha) - if("mouseover_color") - pref.UI_mouseover_color = initial(pref.UI_mouseover_color) - if("mouseover_alpha") - pref.UI_mouseover_alpha = initial(pref.UI_mouseover_alpha) - if("ooc") - pref.ooccolor = initial(pref.ooccolor) - return TOPIC_REFRESH - else if(href_list["select_icon_size"]) var/new_icon_size = input(user, "Enter a new default icon size.", "Default Icon Size", pref.icon_size) as null|anything in global.valid_icon_sizes if(new_icon_size && pref.icon_size != new_icon_size) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 941725eb9be..f7bf90cf83b 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -295,7 +295,7 @@ var/global/list/time_prefs_fixed = list() return if(href_list["preference"] == "open_whitelist_forum") if(get_config_value(/decl/config/text/forumurl)) - direct_output(user, link(get_config_value(/decl/config/text/forumurl))) + open_link(user, get_config_value(/decl/config/text/forumurl)) else to_chat(user, "The forum URL is not set in the server configuration.") return @@ -500,7 +500,7 @@ var/global/list/time_prefs_fixed = list() key_bindings = deepCopyList(global.hotkey_keybinding_list_by_key) if(istype(client)) - // Preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum). + // Preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum). SScharacter_setup.preferences_datums[client.ckey] = src setup() diff --git a/code/modules/client/ui_styles/_helpers.dm b/code/modules/client/ui_styles/_helpers.dm index 83ceb172a41..97d99ab4f2f 100644 --- a/code/modules/client/ui_styles/_helpers.dm +++ b/code/modules/client/ui_styles/_helpers.dm @@ -6,23 +6,7 @@ LAZYADD(., ui) /proc/get_default_ui_icon(ui_key) - var/static/list/default_icons = list( - UI_ICON_ATTACK = 'icons/mob/screen/styles/midnight/attack_selector.dmi', - UI_ICON_FIRE_INTENT = 'icons/mob/screen/styles/midnight/fire_intent.dmi', - UI_ICON_HANDS = 'icons/mob/screen/styles/midnight/hands.dmi', - UI_ICON_HEALTH = 'icons/mob/screen/styles/health.dmi', - UI_ICON_CRIT_MARKER = 'icons/mob/screen/styles/crit_markers.dmi', - UI_ICON_HYDRATION = 'icons/mob/screen/styles/hydration.dmi', - UI_ICON_INTERACTION = 'icons/mob/screen/styles/midnight/interaction.dmi', - UI_ICON_INTERNALS = 'icons/mob/screen/styles/internals.dmi', - UI_ICON_INVENTORY = 'icons/mob/screen/styles/midnight/inventory.dmi', - UI_ICON_MOVEMENT = 'icons/mob/screen/styles/midnight/movement.dmi', - UI_ICON_NUTRITION = 'icons/mob/screen/styles/nutrition.dmi', - UI_ICON_STATUS = 'icons/mob/screen/styles/status.dmi', - UI_ICON_UP_HINT = 'icons/mob/screen/styles/midnight/uphint.dmi', - UI_ICON_ZONE_SELECT = 'icons/mob/screen/styles/midnight/zone_selector.dmi' - ) - return istext(ui_key) ? default_icons[ui_key] : null + return get_ui_icon(global.using_map.default_ui_style, ui_key) /proc/get_ui_icon(ui_style, ui_key) var/decl/ui_style/style = GET_DECL(ui_style) diff --git a/code/modules/client/ui_styles/_ui_style.dm b/code/modules/client/ui_styles/_ui_style.dm index 5e0a5ba34c4..d7a51a45f87 100644 --- a/code/modules/client/ui_styles/_ui_style.dm +++ b/code/modules/client/ui_styles/_ui_style.dm @@ -26,6 +26,12 @@ ) /// A subset of UI keys to icon files used to override the above. var/list/override_icons + /// A color to reset this UI to on pref selection. + var/default_color = COLOR_WHITE + /// An alpha to reset this UI to on pref selection. + var/default_alpha = 255 + var/use_overlay_color = FALSE + var/use_ui_color = FALSE /decl/ui_style/Initialize() for(var/ui_key in override_icons) @@ -46,11 +52,16 @@ var/check_icon = icons[ui_key] var/list/missing_states = list() var/list/checking_states = states_to_check[ui_key] - var/list/remaining_states = icon_states(check_icon) + var/list/remaining_states = get_states_in_icon(check_icon) for(var/check_state in checking_states) remaining_states -= check_state - if(!check_state_in_icon(check_state, check_icon)) + if(check_state_in_icon(check_state, check_icon)) + check_state = "[check_state]-overlay" + if(check_state_in_icon(check_state, check_icon)) + remaining_states -= check_state + else missing_states |= check_state + if(length(remaining_states)) . += "icon [check_icon] for key [ui_key] has extraneous states: '[jointext(remaining_states, "', '")]'" if(length(missing_states)) diff --git a/code/modules/client/ui_styles/ui_style_subtypes.dm b/code/modules/client/ui_styles/ui_style_subtypes.dm index cff4d996c43..7b1f311ebd7 100644 --- a/code/modules/client/ui_styles/ui_style_subtypes.dm +++ b/code/modules/client/ui_styles/ui_style_subtypes.dm @@ -7,68 +7,97 @@ name = "Orange" uid = "ui_style_orange" override_icons = list( - UI_ICON_ATTACK = 'icons/mob/screen/styles/orange/attack_selector.dmi', - UI_ICON_FIRE_INTENT = 'icons/mob/screen/styles/orange/fire_intent.dmi', - UI_ICON_HANDS = 'icons/mob/screen/styles/orange/hands.dmi', - UI_ICON_INTERACTION = 'icons/mob/screen/styles/orange/interaction.dmi', - UI_ICON_INVENTORY = 'icons/mob/screen/styles/orange/inventory.dmi', - UI_ICON_MOVEMENT = 'icons/mob/screen/styles/orange/movement.dmi', - UI_ICON_UP_HINT = 'icons/mob/screen/styles/orange/uphint.dmi', - UI_ICON_ZONE_SELECT = 'icons/mob/screen/styles/orange/zone_selector.dmi' + (UI_ICON_ATTACK) = 'icons/mob/screen/styles/orange/attack_selector.dmi', + (UI_ICON_FIRE_INTENT) = 'icons/mob/screen/styles/orange/fire_intent.dmi', + (UI_ICON_HANDS) = 'icons/mob/screen/styles/orange/hands.dmi', + (UI_ICON_INTERACTION) = 'icons/mob/screen/styles/orange/interaction.dmi', + (UI_ICON_INVENTORY) = 'icons/mob/screen/styles/orange/inventory.dmi', + (UI_ICON_MOVEMENT) = 'icons/mob/screen/styles/orange/movement.dmi', + (UI_ICON_UP_HINT) = 'icons/mob/screen/styles/orange/uphint.dmi', + (UI_ICON_ZONE_SELECT) = 'icons/mob/screen/styles/orange/zone_selector.dmi' ) /decl/ui_style/old name = "Old" uid = "ui_style_old" override_icons = list( - UI_ICON_ATTACK = 'icons/mob/screen/styles/old/attack_selector.dmi', - UI_ICON_FIRE_INTENT = 'icons/mob/screen/styles/old/fire_intent.dmi', - UI_ICON_HANDS = 'icons/mob/screen/styles/old/hands.dmi', - UI_ICON_INTERACTION = 'icons/mob/screen/styles/old/interaction.dmi', - UI_ICON_INVENTORY = 'icons/mob/screen/styles/old/inventory.dmi', - UI_ICON_MOVEMENT = 'icons/mob/screen/styles/old/movement.dmi', - UI_ICON_UP_HINT = 'icons/mob/screen/styles/old/uphint.dmi', - UI_ICON_ZONE_SELECT = 'icons/mob/screen/styles/old/zone_selector.dmi' + (UI_ICON_ATTACK) = 'icons/mob/screen/styles/old/attack_selector.dmi', + (UI_ICON_FIRE_INTENT) = 'icons/mob/screen/styles/old/fire_intent.dmi', + (UI_ICON_HANDS) = 'icons/mob/screen/styles/old/hands.dmi', + (UI_ICON_INTERACTION) = 'icons/mob/screen/styles/old/interaction.dmi', + (UI_ICON_INVENTORY) = 'icons/mob/screen/styles/old/inventory.dmi', + (UI_ICON_MOVEMENT) = 'icons/mob/screen/styles/old/movement.dmi', + (UI_ICON_UP_HINT) = 'icons/mob/screen/styles/old/uphint.dmi', + (UI_ICON_ZONE_SELECT) = 'icons/mob/screen/styles/old/zone_selector.dmi' ) /decl/ui_style/old_noborder name = "Old (no border)" uid = "ui_style_old_noborder" override_icons = list( - UI_ICON_ATTACK = 'icons/mob/screen/styles/old/attack_selector.dmi', - UI_ICON_FIRE_INTENT = 'icons/mob/screen/styles/old/fire_intent.dmi', - UI_ICON_HANDS = 'icons/mob/screen/styles/old/hands.dmi', - UI_ICON_INTERACTION = 'icons/mob/screen/styles/old/interaction.dmi', - UI_ICON_INVENTORY = 'icons/mob/screen/styles/old_noborder/inventory.dmi', - UI_ICON_MOVEMENT = 'icons/mob/screen/styles/old/movement.dmi', - UI_ICON_UP_HINT = 'icons/mob/screen/styles/old_noborder/uphint.dmi', - UI_ICON_ZONE_SELECT = 'icons/mob/screen/styles/old_noborder/zone_selector.dmi' + (UI_ICON_ATTACK) = 'icons/mob/screen/styles/old/attack_selector.dmi', + (UI_ICON_FIRE_INTENT) = 'icons/mob/screen/styles/old/fire_intent.dmi', + (UI_ICON_HANDS) = 'icons/mob/screen/styles/old/hands.dmi', + (UI_ICON_INTERACTION) = 'icons/mob/screen/styles/old/interaction.dmi', + (UI_ICON_INVENTORY) = 'icons/mob/screen/styles/old_noborder/inventory.dmi', + (UI_ICON_MOVEMENT) = 'icons/mob/screen/styles/old/movement.dmi', + (UI_ICON_UP_HINT) = 'icons/mob/screen/styles/old_noborder/uphint.dmi', + (UI_ICON_ZONE_SELECT) = 'icons/mob/screen/styles/old_noborder/zone_selector.dmi' ) /decl/ui_style/white name = "White" uid = "ui_style_white" override_icons = list( - UI_ICON_ATTACK = 'icons/mob/screen/styles/white/attack_selector.dmi', - UI_ICON_FIRE_INTENT = 'icons/mob/screen/styles/white/fire_intent.dmi', - UI_ICON_HANDS = 'icons/mob/screen/styles/white/hands.dmi', - UI_ICON_INTERACTION = 'icons/mob/screen/styles/white/interaction.dmi', - UI_ICON_INVENTORY = 'icons/mob/screen/styles/white/inventory.dmi', - UI_ICON_MOVEMENT = 'icons/mob/screen/styles/white/movement.dmi', - UI_ICON_UP_HINT = 'icons/mob/screen/styles/white/uphint.dmi', - UI_ICON_ZONE_SELECT = 'icons/mob/screen/styles/white/zone_selector.dmi' + (UI_ICON_ATTACK) = 'icons/mob/screen/styles/white/attack_selector.dmi', + (UI_ICON_FIRE_INTENT) = 'icons/mob/screen/styles/white/fire_intent.dmi', + (UI_ICON_HANDS) = 'icons/mob/screen/styles/white/hands.dmi', + (UI_ICON_INTERACTION) = 'icons/mob/screen/styles/white/interaction.dmi', + (UI_ICON_INVENTORY) = 'icons/mob/screen/styles/white/inventory.dmi', + (UI_ICON_MOVEMENT) = 'icons/mob/screen/styles/white/movement.dmi', + (UI_ICON_UP_HINT) = 'icons/mob/screen/styles/white/uphint.dmi', + (UI_ICON_ZONE_SELECT) = 'icons/mob/screen/styles/white/zone_selector.dmi' ) + use_overlay_color = TRUE + use_ui_color = TRUE /decl/ui_style/minimalist name = "Minimalist" uid = "ui_style_minimalist" override_icons = list( - UI_ICON_ATTACK = 'icons/mob/screen/styles/minimalist/attack_selector.dmi', - UI_ICON_FIRE_INTENT = 'icons/mob/screen/styles/minimalist/fire_intent.dmi', - UI_ICON_HANDS = 'icons/mob/screen/styles/minimalist/hands.dmi', - UI_ICON_INTERACTION = 'icons/mob/screen/styles/minimalist/interaction.dmi', - UI_ICON_INVENTORY = 'icons/mob/screen/styles/minimalist/inventory.dmi', - UI_ICON_MOVEMENT = 'icons/mob/screen/styles/minimalist/movement.dmi', - UI_ICON_UP_HINT = 'icons/mob/screen/styles/minimalist/uphint.dmi', - UI_ICON_ZONE_SELECT = 'icons/mob/screen/styles/minimalist/zone_selector.dmi' + (UI_ICON_ATTACK) = 'icons/mob/screen/styles/minimalist/attack_selector.dmi', + (UI_ICON_FIRE_INTENT) = 'icons/mob/screen/styles/minimalist/fire_intent.dmi', + (UI_ICON_HANDS) = 'icons/mob/screen/styles/minimalist/hands.dmi', + (UI_ICON_INTERACTION) = 'icons/mob/screen/styles/minimalist/interaction.dmi', + (UI_ICON_INVENTORY) = 'icons/mob/screen/styles/minimalist/inventory.dmi', + (UI_ICON_MOVEMENT) = 'icons/mob/screen/styles/minimalist/movement.dmi', + (UI_ICON_UP_HINT) = 'icons/mob/screen/styles/minimalist/uphint.dmi', + (UI_ICON_ZONE_SELECT) = 'icons/mob/screen/styles/minimalist/zone_selector.dmi' ) + use_overlay_color = TRUE + use_ui_color = TRUE + +/decl/ui_style/underworld + name = "Underworld" + uid = "ui_style_underworld" + restricted = FALSE + icons = list( + (UI_ICON_ATTACK) = 'icons/mob/screen/styles/underworld/attack_selector.dmi', + (UI_ICON_FIRE_INTENT) = 'icons/mob/screen/styles/underworld/fire_intent.dmi', + (UI_ICON_HANDS) = 'icons/mob/screen/styles/underworld/hands.dmi', + (UI_ICON_HEALTH) = 'icons/mob/screen/styles/health.dmi', + (UI_ICON_CRIT_MARKER) = 'icons/mob/screen/styles/crit_markers.dmi', + (UI_ICON_HYDRATION) = 'icons/mob/screen/styles/hydration.dmi', + (UI_ICON_INTERACTION) = 'icons/mob/screen/styles/underworld/interaction.dmi', + (UI_ICON_INTERNALS) = 'icons/mob/screen/styles/internals.dmi', + (UI_ICON_INVENTORY) = 'icons/mob/screen/styles/underworld/inventory.dmi', + (UI_ICON_MOVEMENT) = 'icons/mob/screen/styles/underworld/movement.dmi', + (UI_ICON_NUTRITION) = 'icons/mob/screen/styles/nutrition.dmi', + (UI_ICON_STATUS_FIRE) = 'icons/mob/screen/styles/status_fire.dmi', + (UI_ICON_STATUS) = 'icons/mob/screen/styles/status.dmi', + (UI_ICON_UP_HINT) = 'icons/mob/screen/styles/underworld/uphint.dmi', + (UI_ICON_ZONE_SELECT) = 'icons/mob/screen/styles/underworld/zone_selector.dmi', + (UI_ICON_CHARGE) = 'icons/mob/screen/styles/charge.dmi' + ) + use_overlay_color = TRUE + use_ui_color = TRUE diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index cd01f8f69db..684f8196ac2 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -100,7 +100,7 @@ /obj/item/clothing/attackby(obj/item/I, mob/user) var/rags = RAG_COUNT(src) - if(istype(material) && material.default_solid_form && rags && (I.edge || I.sharp) && user.check_intent(I_FLAG_HARM)) + if(istype(material) && material.default_solid_form && rags && (I.is_sharp() || I.has_edge()) && user.check_intent(I_FLAG_HARM)) if(length(accessories)) to_chat(user, SPAN_WARNING("You should remove the accessories attached to \the [src] first.")) return TRUE @@ -302,12 +302,12 @@ update_clothing_icon() /obj/item/clothing/get_examine_name() - var/list/ensemble = list(name) + var/list/ensemble = list(..()) for(var/obj/item/clothing/accessory in accessories) if(accessory.accessory_visibility == ACCESSORY_VISIBILITY_ENSEMBLE) - LAZYADD(ensemble, accessory.get_examine_name()) - if(length(ensemble) <= 1) - return ..() + ensemble += accessory.get_examine_name() + if(length(ensemble) == 1) // don't worry about it being empty, we always have a minimum of one + return ensemble[1] return english_list(ensemble, summarize = TRUE) /obj/item/clothing/get_examine_line() @@ -450,7 +450,8 @@ /decl/interaction_handler/clothing_set_sensors name = "Set Sensors Level" expected_target_type = /obj/item/clothing + examine_desc = "adjust vitals sensors" /decl/interaction_handler/clothing_set_sensors/invoked(atom/target, mob/user, obj/item/prop) - var/obj/item/clothing/U = target - U.set_sensors(user) + var/obj/item/clothing/clothes = target + clothes.set_sensors(user) diff --git a/code/modules/clothing/clothing_state/clothing_state_buttons.dm b/code/modules/clothing/clothing_state/clothing_state_buttons.dm index 74c9fe7e444..05b531f2639 100644 --- a/code/modules/clothing/clothing_state/clothing_state_buttons.dm +++ b/code/modules/clothing/clothing_state/clothing_state_buttons.dm @@ -7,6 +7,7 @@ /decl/interaction_handler/clothing_toggle/buttons name = "Toggle Open Or Closed" state_decl_type = /decl/clothing_state_modifier/buttons + examine_desc = "fasten or undo $TARGET_THEIR$ buttons" /obj/item/clothing/proc/toggle_buttons_verb() diff --git a/code/modules/clothing/clothing_state/clothing_state_hood.dm b/code/modules/clothing/clothing_state/clothing_state_hood.dm index 281174461bd..81ddc6301dc 100644 --- a/code/modules/clothing/clothing_state/clothing_state_hood.dm +++ b/code/modules/clothing/clothing_state/clothing_state_hood.dm @@ -7,6 +7,7 @@ /decl/interaction_handler/clothing_toggle/hood name = "Adjust Hood" state_decl_type = /decl/clothing_state_modifier/hood + examine_desc = "push the hood up or down" /obj/item/clothing/proc/toggle_hood_verb() diff --git a/code/modules/clothing/clothing_state/clothing_state_rolled.dm b/code/modules/clothing/clothing_state/clothing_state_rolled.dm index 84d9f8ade84..f368481918f 100644 --- a/code/modules/clothing/clothing_state/clothing_state_rolled.dm +++ b/code/modules/clothing/clothing_state/clothing_state_rolled.dm @@ -8,6 +8,7 @@ /decl/interaction_handler/clothing_toggle/rolled_down name = "Roll Up Or Down" state_decl_type = /decl/clothing_state_modifier/rolled_down + examine_desc = "roll $TARGET_THEM$ up or down" /obj/item/clothing/proc/toggle_rolled_verb() diff --git a/code/modules/clothing/clothing_state/clothing_state_sleeves.dm b/code/modules/clothing/clothing_state/clothing_state_sleeves.dm index 7356c78e404..33a099b4194 100644 --- a/code/modules/clothing/clothing_state/clothing_state_sleeves.dm +++ b/code/modules/clothing/clothing_state/clothing_state_sleeves.dm @@ -7,6 +7,7 @@ /decl/interaction_handler/clothing_toggle/rolled_sleeves name = "Roll Sleeves Up Or Down" state_decl_type = /decl/clothing_state_modifier/rolled_sleeves + examine_desc = "roll $TARGET_THEIR$ sleeves up or down" /obj/item/clothing/proc/toggle_sleeves_verb() diff --git a/code/modules/clothing/clothing_state/clothing_state_tucked.dm b/code/modules/clothing/clothing_state/clothing_state_tucked.dm index 41714c01f86..dbfaaecf3be 100644 --- a/code/modules/clothing/clothing_state/clothing_state_tucked.dm +++ b/code/modules/clothing/clothing_state/clothing_state_tucked.dm @@ -7,6 +7,7 @@ /decl/interaction_handler/clothing_toggle/tucked_in name = "Tuck In Or Untuck" state_decl_type = /decl/clothing_state_modifier/tucked_in + examine_desc = "tuck $TARGET_THEM$ in or out" /obj/item/clothing/proc/toggle_tucked_verb() diff --git a/code/modules/clothing/clothing_state/clothing_state_untied.dm b/code/modules/clothing/clothing_state/clothing_state_untied.dm index 98313e2441d..759b6001504 100644 --- a/code/modules/clothing/clothing_state/clothing_state_untied.dm +++ b/code/modules/clothing/clothing_state/clothing_state_untied.dm @@ -7,6 +7,7 @@ /decl/interaction_handler/clothing_toggle/untied name = "Tie Or Untie" state_decl_type = /decl/clothing_state_modifier/untied + examine_desc = "tie or untie $TARGET_THEM$" /obj/item/clothing/proc/toggle_untied_verb() diff --git a/code/modules/clothing/gloves/jewelry/rings/_ring.dm b/code/modules/clothing/gloves/jewelry/rings/_ring.dm index b9e58beedf6..aadeedab558 100644 --- a/code/modules/clothing/gloves/jewelry/rings/_ring.dm +++ b/code/modules/clothing/gloves/jewelry/rings/_ring.dm @@ -54,7 +54,7 @@ desc = "[base_desc] [desc]" /obj/item/clothing/gloves/ring/attackby(var/obj/item/tool, var/mob/user) - if(can_inscribe && tool.sharp && user.check_intent(I_FLAG_HELP)) + if(can_inscribe && tool.is_sharp() && user.check_intent(I_FLAG_HELP)) var/new_inscription = sanitize(input("Enter an inscription to engrave.", "Inscription") as null|text) if(user.stat || !user.incapacitated() || !user.Adjacent(src) || tool.loc != user) return TRUE diff --git a/code/modules/clothing/head/fated_key.dm b/code/modules/clothing/head/fated_key.dm index cc65bd80239..626126d50c9 100644 --- a/code/modules/clothing/head/fated_key.dm +++ b/code/modules/clothing/head/fated_key.dm @@ -51,7 +51,7 @@ var/atom/blade for(var/obj/item/held in shuffle(user.get_held_items())) - if(has_edge(held)) + if(held.has_edge()) blade = held break if(!blade) @@ -113,5 +113,5 @@ animate(src, alpha = 255, time = 3) sleep(13) animate(src, alpha = 0, time = 40) - sleep(40) - qdel(src) + if(!QDELING(src)) + QDEL_IN(src, 4 SECONDS) diff --git a/code/modules/clothing/masks/smokable.dm b/code/modules/clothing/masks/smokable.dm index d1d55790423..cdf734b4b9f 100644 --- a/code/modules/clothing/masks/smokable.dm +++ b/code/modules/clothing/masks/smokable.dm @@ -198,7 +198,7 @@ /obj/item/clothing/mask/smokable/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) if(target.is_on_fire()) user.do_attack_animation(target) - light(SPAN_NOTICE("\The [user] coldly lights the \the [src] with the burning body of \the [target].")) + light(SPAN_NOTICE("\The [user] coldly lights \the [src] with the burning body of \the [target].")) return TRUE return ..() diff --git a/code/modules/clothing/masks/voice.dm b/code/modules/clothing/masks/voice.dm index edee4acd8eb..8ecc47d727c 100644 --- a/code/modules/clothing/masks/voice.dm +++ b/code/modules/clothing/masks/voice.dm @@ -30,3 +30,7 @@ /obj/item/clothing/mask/chameleon/voice/Initialize() . = ..() changer = new(src) + +/obj/item/clothing/mask/chameleon/voice/Destroy() + QDEL_NULL(changer) + return ..() diff --git a/code/modules/clothing/neck/necklace/__necklace.dm b/code/modules/clothing/neck/necklace/__necklace.dm index a6e680313b6..d94e49bf267 100644 --- a/code/modules/clothing/neck/necklace/__necklace.dm +++ b/code/modules/clothing/neck/necklace/__necklace.dm @@ -67,6 +67,7 @@ /decl/interaction_handler/remove_pendant name = "Remove Pendant" expected_target_type = /obj/item/clothing/neck/necklace + examine_desc = "remove $TARGET_THEIR$ pendant" /decl/interaction_handler/remove_pendant/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/clothing/neck/necklace/necklace = target diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 6aedbb3edaf..bd4284407d8 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -24,7 +24,7 @@ var/hidden_item_max_w_class = ITEM_SIZE_SMALL var/obj/item/hidden_item = null var/shine = -1 // if material should apply shine overlay. Set to -1 for it to not do that - + var/footprint_icon = 'icons/mob/footprints/footprints.dmi' /// A multiplier applied to footstep volume. var/footstep_volume_mod = 1 /// A multiplier applied to footstep range. diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 074caa2c597..ff88b304078 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -48,7 +48,7 @@ /obj/item/rig_module/device/flash/installed() . = ..() - if(!holder.gloves)//gives select option for gloveless suits, why even use rig at this point + if(!holder?.gloves)//gives select option for gloveless suits, why even use rig at this point selectable = 1 activates_on_touch = 0 toggleable = 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index 7626ac00c32..abf699315de 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -313,14 +313,16 @@ voice_holder.active = 0 /obj/item/rig_module/voice/installed() - ..() - holder.speech = src - holder.verbs |= /obj/item/rig/proc/alter_voice + . = ..() + if(holder) + holder.speech = src + holder.verbs |= /obj/item/rig/proc/alter_voice /obj/item/rig_module/voice/removed() - ..() - holder.speech = null - holder.verbs -= /obj/item/rig/proc/alter_voice + if(holder) + holder.speech = null + holder.verbs -= /obj/item/rig/proc/alter_voice + . = ..() /obj/item/rig_module/voice/engage() @@ -454,14 +456,7 @@ use_power_cost = 200 usable = 1 selectable = 0 - device = /obj/item/paper_bin - -/obj/item/rig_module/device/paperdispenser/engage(atom/target) - if(!..() || !device) - return FALSE - if(!target) - device.attack_hand_with_interaction_checks(holder.wearer) - return TRUE + device = /obj/item/form_printer /obj/item/rig_module/device/pen name = "mounted pen" diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index 3b19bf49653..9ba365b34fa 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -160,8 +160,14 @@ // There should only ever be one vision module installed in a suit. /obj/item/rig_module/vision/installed() - ..() - holder.visor = src + . = ..() + if(holder) + holder.visor = src + +/obj/item/rig_module/vision/removed() + if(holder) + holder.visor = null + . = ..() /obj/item/rig_module/vision/engage() diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 0002058a65f..f9f4f356c2b 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -264,7 +264,7 @@ wearer.visible_message( SPAN_HARDSUIT("[wearer]'s suit emits a quiet hum as it begins to adjust its seals."), SPAN_HARDSUIT("With a quiet hum, the suit begins running checks and adjusting components.")) - if(seal_delay && !do_after(wearer,seal_delay, src)) + if(seal_delay && !do_after(wearer, seal_delay, src)) if(wearer) to_chat(wearer, "You must remain still while the suit is adjusting the components.") failed_to_seal = 1 @@ -299,7 +299,7 @@ var/obj/item/compare_piece = piece_data[2] var/msg_type = piece_data[3] - if(!piece) + if(!piece || !compare_piece) continue if(!istype(wearer) || !istype(piece) || !istype(compare_piece) || !msg_type) diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index 22a1e57aafe..896bd3db241 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -1,6 +1,6 @@ //Spacesuit //Note: Everything in modules/clothing/spacesuits should have the entire suit grouped together. -// Meaning the the suit is defined directly after the corrisponding helmet. Just like below! +// Meaning the suit is defined directly after the corresponding helmet. Just like below! /obj/item/clothing/head/helmet/space name = "space helmet" diff --git a/code/modules/clothing/suits/armor/_armor.dm b/code/modules/clothing/suits/armor/_armor.dm index 250c133ae7b..eef73b5c8fa 100644 --- a/code/modules/clothing/suits/armor/_armor.dm +++ b/code/modules/clothing/suits/armor/_armor.dm @@ -12,7 +12,7 @@ /obj/item/handcuffs, /obj/item/gun/magnetic, /obj/item/clothing/head/helmet, - /obj/item/shield/buckler, + /obj/item/shield/crafted/buckler, /obj/item/bladed/knife, /obj/item/bladed/shortsword, /obj/item/bladed/longsword, diff --git a/code/modules/clothing/suits/armor/craftable.dm b/code/modules/clothing/suits/armor/craftable.dm index ddca0ec73ba..824a7c5b553 100644 --- a/code/modules/clothing/suits/armor/craftable.dm +++ b/code/modules/clothing/suits/armor/craftable.dm @@ -33,5 +33,5 @@ material = /decl/material/solid/gemstone/diamond /obj/item/clothing/suit/armor/crafted/gold material = /decl/material/solid/metal/gold -/obj/item/clothing/suit/armor/crafted/supermatter - material = /decl/material/solid/supermatter +/obj/item/clothing/suit/armor/crafted/exotic_matter + material = /decl/material/solid/exotic_matter diff --git a/code/modules/clothing/suits/armor/merc.dm b/code/modules/clothing/suits/armor/merc.dm index 869b423be90..a138ef7b732 100644 --- a/code/modules/clothing/suits/armor/merc.dm +++ b/code/modules/clothing/suits/armor/merc.dm @@ -9,7 +9,7 @@ /obj/item/clothing/armor_attachment/plate/merc name = "heavy armor plate" - desc = "A diamond-reinforced titanium armor plate, providing state of of the art protection. Attaches to a plate carrier." + desc = "A diamond-reinforced titanium armor plate, providing state of the art protection. Attaches to a plate carrier." icon = 'icons/clothing/accessories/armor/armor_merc.dmi' armor = list( ARMOR_MELEE = ARMOR_MELEE_RESISTANT, diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm index dea74926342..5888d3ec832 100644 --- a/code/modules/clothing/suits/cloaks.dm +++ b/code/modules/clothing/suits/cloaks.dm @@ -1,15 +1,17 @@ -/obj/item/clothing/suit/cloak // A colorable cloak - name = "plain cloak" - desc = "A simple, bland cloak." - icon = 'icons/clothing/suits/cloaks/_cloak.dmi' - w_class = ITEM_SIZE_NORMAL - slot_flags = SLOT_OVER_BODY - allowed = list(/obj/item/tank/emergency/oxygen) - armor = list(ARMOR_MELEE = 0, ARMOR_BULLET = 0, ARMOR_LASER = 0,ARMOR_ENERGY = 0, ARMOR_BOMB = 0, ARMOR_BIO = 0, ARMOR_RAD = 0) - body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS|SLOT_LEGS - siemens_coefficient = 0.9 - accessory_slot = ACCESSORY_SLOT_OVER +// A colorable cloak +/obj/item/clothing/suit/cloak + name = "plain cloak" + desc = "A simple, bland cloak." + icon = 'icons/clothing/suits/cloaks/_cloak.dmi' + w_class = ITEM_SIZE_NORMAL + slot_flags = SLOT_OVER_BODY + allowed = list(/obj/item/tank/emergency/oxygen) + armor = null + body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_ARMS|SLOT_LEGS + siemens_coefficient = 0.9 + accessory_slot = ACCESSORY_SLOT_OVER accessory_visibility = ACCESSORY_VISIBILITY_ATTACHMENT + storage = /datum/storage/pockets/suit /obj/item/clothing/suit/cloak/on_update_icon() . = ..() diff --git a/code/modules/clothing/suits/jackets/_jacket.dm b/code/modules/clothing/suits/jackets/_jacket.dm index 3486a1c88e1..abdd83bf2d4 100644 --- a/code/modules/clothing/suits/jackets/_jacket.dm +++ b/code/modules/clothing/suits/jackets/_jacket.dm @@ -8,6 +8,7 @@ slot_flags = SLOT_OVER_BODY w_class = ITEM_SIZE_NORMAL accessory_slot = ACCESSORY_SLOT_DECOR + storage = /datum/storage/pockets/suit valid_accessory_slots = list( ACCESSORY_SLOT_INSIGNIA, ACCESSORY_SLOT_ARMBAND, diff --git a/code/modules/clothing/webbing/holster.dm b/code/modules/clothing/webbing/holster.dm index 47ca037c939..1d49eaabc2b 100644 --- a/code/modules/clothing/webbing/holster.dm +++ b/code/modules/clothing/webbing/holster.dm @@ -12,25 +12,24 @@ . = ..() set_extension(src, /datum/extension/holster, storage, sound_in, sound_out, can_holster) -/obj/item/clothing/webbing/holster/attackby(obj/item/W, mob/user) - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) - if(H.holster(W, user)) +/obj/item/clothing/webbing/holster/attackby(obj/item/used_item, mob/user) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + if(holster.holster(used_item, user)) return TRUE - else - . = ..(W, user) + return ..(used_item, user) /obj/item/clothing/webbing/holster/attack_hand(mob/user) if(!user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) return ..() - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) - if(H.unholster(user)) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + if(holster.unholster(user)) return TRUE return ..() /obj/item/clothing/webbing/holster/examine(mob/user) . = ..(user) - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) - H.examine_holster(user) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + holster.examine_holster(user) /obj/item/clothing/webbing/holster/on_attached(var/obj/item/clothing/holder, var/mob/user) . = ..() diff --git a/code/modules/codex/categories/category_phenomena.dm b/code/modules/codex/categories/category_phenomena.dm index f9777ed43fa..e2637bf9e73 100644 --- a/code/modules/codex/categories/category_phenomena.dm +++ b/code/modules/codex/categories/category_phenomena.dm @@ -4,16 +4,16 @@ /decl/codex_category/phenomena/Populate() - // This needs duplicate checking but I resent even having to spend time on spellcode. - var/list/spells = list() - for(var/thing in subtypesof(/spell)) - var/spell/spell = thing - if(!initial(spell.hidden_from_codex) && initial(spell.desc) && initial(spell.name)) - spells["[initial(spell.name)] (phenomena)"] = initial(spell.desc) - for(var/spell in spells) + var/list/abilities = list() + for(var/decl/ability/ability in decls_repository.get_decls_of_subtype_unassociated(/decl/ability)) + if(ability.hidden_from_codex || !ability.is_supernatural || !ability.desc) + continue + abilities["[ability.name] (phenomena)"] = ability.desc + + for(var/ability in abilities) var/datum/codex_entry/entry = new( - _display_name = spell, - _antag_text = spells[spell] + _display_name = ability, + _antag_text = abilities[ability] ) items |= entry.name . = ..() diff --git a/code/modules/codex/categories/category_substances.dm b/code/modules/codex/categories/category_substances.dm index e17ac5d3e66..e8c96d49b72 100644 --- a/code/modules/codex/categories/category_substances.dm +++ b/code/modules/codex/categories/category_substances.dm @@ -77,7 +77,7 @@ else if(mat.dissolves_in <= MAT_SOLVENT_MODERATE) solvent_needed = "a moderately strong solvent, like acetone" else if(mat.dissolves_in <= MAT_SOLVENT_STRONG) - solvent_needed = "a strong solvent, like sulphuric acid" + solvent_needed = "a strong solvent, like sulfuric acid" material_info += "
  • It can be dissolved with [solvent_needed] solvent, producing [english_list(chems)].
  • " if(mat.radioactivity) material_info += "
  • It is radioactive.
  • " diff --git a/code/modules/codex/codex_atom.dm b/code/modules/codex/codex_atom.dm index ebe02e59ab7..49d718ebbff 100644 --- a/code/modules/codex/codex_atom.dm +++ b/code/modules/codex/codex_atom.dm @@ -36,5 +36,8 @@ /atom/examine(mob/user, distance, infix = "", suffix = "") . = ..() + var/decl/interaction_handler/handler = get_quick_interaction_handler(user) + if(handler) + to_chat(user, SPAN_NOTICE("Ctrl-click \the [src] while in your inventory to [lowertext(handler.name)].")) if(user?.get_preference_value(/datum/client_preference/inquisitive_examine) == PREF_ON && user.can_use_codex() && SScodex.get_codex_entry(get_codex_value(user))) to_chat(user, SPAN_NOTICE("The codex has relevant information available.")) diff --git a/code/modules/codex/entries/engineering.dm b/code/modules/codex/entries/engineering.dm index f6f9b4a7c00..f5e9ac99fa3 100644 --- a/code/modules/codex/entries/engineering.dm +++ b/code/modules/codex/entries/engineering.dm @@ -1,20 +1,3 @@ -/datum/codex_entry/supermatter - associated_paths = list(/obj/machinery/power/supermatter) - mechanics_text = "When energized by a laser (or something hitting it), it emits radiation and heat. If the heat reaches above 7000 kelvin, it will send an alert and start taking damage. \ - After integrity falls to zero percent, it will delaminate, causing a massive explosion, station-wide radiation spikes, and hallucinations. \ - Supermatter reacts badly to oxygen in the atmosphere. It'll also heat up really quick if it is in vacuum.
    \ -
    \ - Supermatter cores are extremely dangerous to be close to, and requires protection to handle properly. The protection you will need is:
    \ - Optical meson scanners on your eyes, to prevent hallucinations when looking at the supermatter.
    \ - Radiation helmet and suit, as the supermatter is radioactive.
    \ -
    \ - Touching the supermatter will result in *instant death*, with no corpse left behind! You can drag the supermatter, but anything else will kill you. \ - It is advised to obtain a genetic backup before trying to drag it." - antag_text = "Exposing the supermatter to oxygen or vaccum will cause it to start rapidly heating up. Sabotaging the supermatter and making it explode will \ - cause a period of lag as the explosion is processed by the server, as well as irradiating the entire station and causing hallucinations to happen. \ - Wearing radiation equipment will protect you from most of the delamination effects sans explosion." - available_to_map_tech_level = MAP_TECH_LEVEL_SPACE - /datum/codex_entry/apc associated_paths = list(/obj/machinery/power/apc) mechanics_text = "An APC (Area Power Controller) regulates and supplies backup power for the area they are in. Their power channels are divided \ diff --git a/code/modules/codex/entries/guides.dm b/code/modules/codex/entries/guides.dm index 4ed96a6c0cf..747a279d691 100644 --- a/code/modules/codex/entries/guides.dm +++ b/code/modules/codex/entries/guides.dm @@ -601,10 +601,6 @@ /datum/codex_entry/guide/construction name = "Guide to Construction" -/datum/codex_entry/guide/supermatter - name = "Guide to Supermatter Engines" - available_to_map_tech_level = MAP_TECH_LEVEL_SPACE - /datum/codex_entry/guide/fusion name = "Guide to Fusion Reactors" available_to_map_tech_level = MAP_TECH_LEVEL_SPACE diff --git a/code/modules/codex/entries/guns.dm b/code/modules/codex/entries/guns.dm index eefcbd251aa..ffdbb675590 100644 --- a/code/modules/codex/entries/guns.dm +++ b/code/modules/codex/entries/guns.dm @@ -35,7 +35,7 @@ traits += "It's best fired with a two-handed grip." if(has_safety) - traits += "It has a safety switch. Control-Click it to toggle safety." + traits += "It has a safety switch, which can be toggled via ctrl-click or selecting Toggle Safety from the alt-click radial." if(is_secure_gun()) traits += "It's fitted with a secure registration chip. Swipe ID on it to register." diff --git a/code/modules/crafting/forging/bellows.dm b/code/modules/crafting/forging/bellows.dm new file mode 100644 index 00000000000..3402f4c2cfa --- /dev/null +++ b/code/modules/crafting/forging/bellows.dm @@ -0,0 +1,40 @@ +/obj/structure/working/bellows + name = "bellows" + desc = "An air pump used to improve the heat of a furnace." + icon = 'icons/obj/structures/forging/bellows.dmi' + obj_flags = OBJ_FLAG_ANCHORABLE | OBJ_FLAG_ROTATABLE + work_skill = SKILL_HAULING + var/decl/material/bellows_material = /decl/material/solid/organic/leather + +/obj/structure/working/bellows/Initialize() + bellows_material = GET_DECL(bellows_material) + . = ..() + +/obj/structure/working/bellows/on_update_icon() + . = ..() + underlays = list(overlay_image(icon, "[icon_state]-bellows", bellows_material.color, RESET_COLOR)) + +/obj/structure/working/bellows/try_start_working(mob/user) + + var/obj/structure/fire_source/stoking = locate() in get_step(loc, EAST) + if(!istype(stoking) || !stoking.lit) + to_chat(user, SPAN_WARNING("\The [src] must face east towards a lit fire source; it would be pointless to work them currently.")) + return TRUE + + to_chat(user, SPAN_NOTICE("You begin working \the [src], stoking \the [stoking] to a hotter flame.")) + start_working() + while(user.do_skilled(3 SECONDS, work_skill, src)) + if(QDELETED(src) || QDELETED(user) || user.get_stamina() <= 0) + break + stoking = locate() in get_step(loc, EAST) + if(!istype(stoking) || !stoking.lit) + break + user.adjust_stamina(-25) + stoking.bellows_oxygenation = max(50, stoking.bellows_oxygenation+3) + + if(!QDELETED(user)) + to_chat(user, SPAN_NOTICE("You stop working \the [src].")) + + stop_working() + return TRUE + diff --git a/code/modules/crafting/pottery/pottery_structures.dm b/code/modules/crafting/pottery/pottery_structures.dm index 2c9a369296b..73adc6e921f 100644 --- a/code/modules/crafting/pottery/pottery_structures.dm +++ b/code/modules/crafting/pottery/pottery_structures.dm @@ -6,7 +6,7 @@ density = TRUE cap_last_fuel_burn = null - var/list/pottery = list() + var/list/pottery var/maximum_items = 3 var/firebox_open = TRUE @@ -17,12 +17,12 @@ /obj/structure/fire_source/kiln/high_temperature material = /decl/material/solid/stone/pottery -/obj/structure/fire_source/kiln/remove_atom(atom/movable/thing) +/obj/structure/fire_source/kiln/Exited(atom/movable/thing) . = ..() - pottery -= thing + LAZYREMOVE(pottery, thing) /obj/structure/fire_source/kiln/get_removable_atoms() - . = pottery?.Copy() + . = pottery?.Copy() || list() if(firebox_open) . |= ..() @@ -38,14 +38,14 @@ if(length(other)) LAZYDISTINCTADD(., other) -/obj/structure/fire_source/kiln/attackby(obj/item/W, mob/user) +/obj/structure/fire_source/kiln/attackby(obj/item/used_item, mob/user) if(firebox_open) return ..() if(length(pottery) >= maximum_items) to_chat(user, SPAN_WARNING("\The [src] is full, take something out first.")) - else if(user.try_unequip(W, src)) - user.visible_message("\The [user] slides \the [W] into \the [src].") - pottery += W + else if(user.try_unequip(used_item, src)) + user.visible_message("\The [user] slides \the [used_item] into \the [src].") + LAZYADD(pottery, used_item) return TRUE /obj/structure/fire_source/kiln/get_alt_interactions(var/mob/user) @@ -56,6 +56,7 @@ name = "Open Firebox" expected_target_type = /obj/structure/fire_source/kiln incapacitation_flags = INCAPACITATION_DISRUPTED + examine_desc = "open or close the firebox" /decl/interaction_handler/open_firebox/is_possible(atom/target, mob/user, obj/item/prop) . = ..() && ishuman(user) diff --git a/code/modules/crafting/slapcrafting/_crafting_stage.dm b/code/modules/crafting/slapcrafting/_crafting_stage.dm index b72c4d4cb8e..50ede2c90bd 100644 --- a/code/modules/crafting/slapcrafting/_crafting_stage.dm +++ b/code/modules/crafting/slapcrafting/_crafting_stage.dm @@ -76,17 +76,18 @@ . = !consume_completion_trigger || user.try_unequip(thing, target) if(. && stack_consume_amount > 0) var/obj/item/stack/stack = thing - if(!istype(stack) || stack.amount < stack_consume_amount) + if(!istype(stack) || stack.get_amount() < stack_consume_amount) on_insufficient_material(user, stack) return FALSE var/obj/item/stack/used_stack - if(stack.amount == stack_consume_amount) + if(stack.amount > stack_consume_amount) + used_stack = stack.split(stack_consume_amount) + else if(!user.try_unequip(thing, target)) return FALSE used_stack = stack - else - used_stack = stack.split(stack_consume_amount) - used_stack.forceMove(target) + if(!QDELETED(used_stack)) + used_stack.forceMove(target) target?.update_icon() /decl/crafting_stage/proc/on_insufficient_material(var/mob/user, var/obj/item/stack/thing) diff --git a/code/modules/crafting/slapcrafting/crafting_recipes/improvised_crafting/crafting_buckler.dm b/code/modules/crafting/slapcrafting/crafting_recipes/improvised_crafting/crafting_buckler.dm index d2c910fba7b..4cab4e48465 100644 --- a/code/modules/crafting/slapcrafting/crafting_recipes/improvised_crafting/crafting_buckler.dm +++ b/code/modules/crafting/slapcrafting/crafting_recipes/improvised_crafting/crafting_buckler.dm @@ -20,4 +20,13 @@ /decl/crafting_stage/screwdriver/buckler_finish progress_message = "You secure the buckler's panels in place and finish it off." - product = /obj/item/shield/buckler + product = /obj/item/shield/crafted/buckler/improvised + +/decl/crafting_stage/screwdriver/buckler_finish/get_product(var/obj/item/work) + if(!ispath(product)) + return null + var/obj/item/stool/stool = locate() in work + var/obj/item/stack/material/plank/plank = locate() in work + if(istype(stool) && istype(plank)) + return new product(get_turf(work), plank.material?.type, stool.material?.type) + return ..() diff --git a/code/modules/crafting/slapcrafting/crafting_recipes/tool_crafting/_tool_crafting.dm b/code/modules/crafting/slapcrafting/crafting_recipes/tool_crafting/_tool_crafting.dm index c8169a999c2..5472d992243 100644 --- a/code/modules/crafting/slapcrafting/crafting_recipes/tool_crafting/_tool_crafting.dm +++ b/code/modules/crafting/slapcrafting/crafting_recipes/tool_crafting/_tool_crafting.dm @@ -12,6 +12,10 @@ var/global/list/_tool_crafting_lookup return global._tool_crafting_lookup var/global/list/_tool_crafting_components = list( + /obj/item/tool/chisel = list( + /obj/item/tool_component/head/chisel, + /obj/item/tool_component/handle/short + ), /obj/item/tool/hammer = list( /obj/item/tool_component/head/hammer, /obj/item/tool_component/handle/short @@ -35,6 +39,10 @@ var/global/list/_tool_crafting_components = list( /obj/item/tool/axe = list( /obj/item/tool_component/head/handaxe, /obj/item/tool_component/handle/short + ), + /obj/item/tool/hammer/forge = list( + /obj/item/tool_component/head/forging_hammer, + /obj/item/tool_component/handle/short ) ) diff --git a/code/modules/crafting/stack_recipes/_recipe.dm b/code/modules/crafting/stack_recipes/_recipe.dm index a5a28c6e6a5..7c481de823a 100644 --- a/code/modules/crafting/stack_recipes/_recipe.dm +++ b/code/modules/crafting/stack_recipes/_recipe.dm @@ -150,9 +150,16 @@ else if(length(used_skill.levels) < difficulty) . += "required skill [recipe_skill] is missing skill level [json_encode(difficulty)]" - if(length(forbidden_craft_stack_types) && length(craft_stack_types)) - for(var/stack_type in (forbidden_craft_stack_types|craft_stack_types)) - if((stack_type in craft_stack_types) && (stack_type in forbidden_craft_stack_types)) + var/list/check_forbidden_craft_stack_types = forbidden_craft_stack_types + if(check_forbidden_craft_stack_types && !islist(check_forbidden_craft_stack_types)) + check_forbidden_craft_stack_types = list(check_forbidden_craft_stack_types) + var/list/check_craft_stack_types = craft_stack_types + if(check_craft_stack_types && !islist(check_craft_stack_types)) + check_craft_stack_types = list(check_craft_stack_types) + + if(length(check_forbidden_craft_stack_types) && length(check_craft_stack_types)) + for(var/stack_type in (check_forbidden_craft_stack_types|check_craft_stack_types)) + if((stack_type in check_craft_stack_types) && (stack_type in check_forbidden_craft_stack_types)) . += "[stack_type] is in both forbidden and craftable stack types" /decl/stack_recipe/proc/get_required_stack_amount(obj/item/stack/stack) diff --git a/code/modules/crafting/stack_recipes/recipes_grass.dm b/code/modules/crafting/stack_recipes/recipes_grass.dm index 369270b898d..17caba7e0c4 100644 --- a/code/modules/crafting/stack_recipes/recipes_grass.dm +++ b/code/modules/crafting/stack_recipes/recipes_grass.dm @@ -29,6 +29,7 @@ craft_stack_types = /obj/item/stack/material/bundle required_material = /decl/material/solid/organic/plantmatter/grass/dry result_type = /obj/item/stack/tile/roof/woven + forbidden_craft_stack_types = null /decl/stack_recipe/tile/woven/floor name = "woven floor tile" diff --git a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm index 8b0781f7d43..eafa6e89d73 100644 --- a/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm +++ b/code/modules/crafting/stack_recipes/recipes_hardness_integrity.dm @@ -5,6 +5,15 @@ /decl/stack_recipe/hardness/integrity/sign result_type = /obj/item/banner/sign +/decl/stack_recipe/hardness/integrity/buckler + result_type = /obj/item/shield_base/buckler + difficulty = MAT_VALUE_HARD_DIY + +// TODO: forging +/decl/stack_recipe/hardness/integrity/shield_fasteners + result_type = /obj/item/shield_fasteners + difficulty = MAT_VALUE_VERY_HARD_DIY + /decl/stack_recipe/hardness/integrity/furniture abstract_type = /decl/stack_recipe/hardness/integrity/furniture one_per_turf = TRUE diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index 98d3254f522..00f4f0f41b6 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -78,6 +78,9 @@ /decl/stack_recipe/planks/bowl result_type = /obj/item/chems/glass/handmade/bowl +/decl/stack_recipe/planks/buckler + result_type = /obj/item/shield_base/buckler + /decl/stack_recipe/planks/fancy abstract_type = /decl/stack_recipe/planks/fancy difficulty = MAT_VALUE_VERY_HARD_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_soft.dm b/code/modules/crafting/stack_recipes/recipes_soft.dm index 9672f23017e..32422a17549 100644 --- a/code/modules/crafting/stack_recipes/recipes_soft.dm +++ b/code/modules/crafting/stack_recipes/recipes_soft.dm @@ -89,3 +89,20 @@ /decl/stack_recipe/soft/mould/ingot name = "mould, ingot" result_type = /obj/item/chems/mould/ingot + +/decl/stack_recipe/soft/sculpture + abstract_type = /decl/stack_recipe/soft/sculpture + one_per_turf = TRUE + on_floor = TRUE + category = "sculptures" + +/decl/stack_recipe/soft/sculpture/snowman + result_type = /obj/structure/snowman + +/decl/stack_recipe/soft/sculpture/snowspider + result_type = /obj/structure/snowman/spider + difficulty = MAT_VALUE_HARD_DIY + +/decl/stack_recipe/soft/sculpture/snowbot + result_type = /obj/structure/snowman/bot + available_to_map_tech_level = MAP_TECH_LEVEL_SPACE diff --git a/code/modules/crafting/working/_working.dm b/code/modules/crafting/working/_working.dm index e848937907a..4957dc0052a 100644 --- a/code/modules/crafting/working/_working.dm +++ b/code/modules/crafting/working/_working.dm @@ -49,7 +49,7 @@ work_sound.stop(src) update_icon() -/obj/structure/working/attackby(obj/item/W, mob/user) +/obj/structure/working/attackby(obj/item/used_item, mob/user) if(user.check_intent(I_FLAG_HARM)) return ..() @@ -58,12 +58,12 @@ to_chat(user, SPAN_WARNING("\The [src] is currently in use, please wait for it to be finished.")) return TRUE - if(try_take_input(W, user)) + if(try_take_input(used_item, user)) return TRUE return ..() -/obj/structure/working/proc/try_take_input(obj/item/W, mob/user, silent) +/obj/structure/working/proc/try_take_input(obj/item/used_item, mob/user, silent) return FALSE /obj/structure/working/proc/try_unload_material(mob/user) diff --git a/code/modules/crafting/working/textiles/loom.dm b/code/modules/crafting/working/textiles/loom.dm index 2b7bf5ae868..a68bdacbf57 100644 --- a/code/modules/crafting/working/textiles/loom.dm +++ b/code/modules/crafting/working/textiles/loom.dm @@ -34,24 +34,24 @@ weaving_type = null weaving_progress = 0 -/obj/structure/working/loom/try_take_input(obj/item/W, mob/user) +/obj/structure/working/loom/try_take_input(obj/item/used_item, mob/user) - if(istype(W, /obj/item/stack/material/thread)) + if(istype(used_item, /obj/item/stack/material/thread)) - if(!W.material.has_textile_fibers) - to_chat(user, SPAN_WARNING("\The [W] isn't suitable for making cloth.")) + if(!used_item.material.has_textile_fibers) + to_chat(user, SPAN_WARNING("\The [used_item] isn't suitable for making cloth.")) return TRUE var/loaded = FALSE if(loaded_thread) - if(!loaded_thread.can_merge_stacks(W)) + if(!loaded_thread.can_merge_stacks(used_item)) to_chat(user, SPAN_WARNING("\The [src] is already wound with \the [loaded_thread].")) return TRUE - var/obj/item/stack/feeding = W + var/obj/item/stack/feeding = used_item feeding.transfer_to(loaded_thread) loaded = TRUE - else if(user.try_unequip(W, src)) - loaded_thread = W + else if(user.try_unequip(used_item, src)) + loaded_thread = used_item loaded = TRUE if(loaded) weaving_color = loaded_thread.get_color() diff --git a/code/modules/crafting/working/textiles/spinning_wheel.dm b/code/modules/crafting/working/textiles/spinning_wheel.dm index e79afe92379..84b1dab6e1b 100644 --- a/code/modules/crafting/working/textiles/spinning_wheel.dm +++ b/code/modules/crafting/working/textiles/spinning_wheel.dm @@ -24,16 +24,16 @@ /obj/structure/working/spinning_wheel/proc/can_process(obj/item/thing) return istype(thing) && thing.has_textile_fibers() -/obj/structure/working/spinning_wheel/try_take_input(obj/item/W, mob/user) +/obj/structure/working/spinning_wheel/try_take_input(obj/item/used_item, mob/user) - if(istype(W.storage)) + if(istype(used_item.storage)) var/list/loading_growns = list() - for(var/obj/item/thing in W.get_stored_inventory()) + for(var/obj/item/thing in used_item.get_stored_inventory()) if(can_process(thing)) loading_growns += thing if(!length(loading_growns)) - to_chat(user, SPAN_WARNING("Nothing in \the [W] is suitable for processing on \the [src].")) + to_chat(user, SPAN_WARNING("Nothing in \the [used_item] is suitable for processing on \the [src].")) return TRUE if(length(loaded) >= MAX_LOADED) @@ -42,24 +42,24 @@ var/loaded_items = 0 for(var/obj/item/thing as anything in loading_growns) - if(W.storage.remove_from_storage(thing, src, TRUE)) + if(used_item.storage.remove_from_storage(thing, src, TRUE)) loaded_items++ LAZYADD(loaded, thing) if(length(loaded) >= MAX_LOADED) break if(loaded_items) - W.storage.finish_bulk_removal() - to_chat(user, SPAN_NOTICE("You prepare \the [src] with [loaded_items] items from \the [W].")) + used_item.storage.finish_bulk_removal() + to_chat(user, SPAN_NOTICE("You prepare \the [src] with [loaded_items] items from \the [used_item].")) update_icon() return TRUE - if(can_process(W)) + if(can_process(used_item)) if(length(loaded) >= MAX_LOADED) to_chat(user, SPAN_WARNING("\The [src] is already fully stocked and ready for spinning.")) return TRUE - if(user.try_unequip(W, src)) - LAZYADD(loaded, W) - to_chat(user, SPAN_NOTICE("You prepare \the [src] with \the [W].")) + if(user.try_unequip(used_item, src)) + LAZYADD(loaded, used_item) + to_chat(user, SPAN_NOTICE("You prepare \the [src] with \the [used_item].")) update_icon() return TRUE return TRUE diff --git a/code/modules/detectivework/microscope/_forensic_machine.dm b/code/modules/detectivework/microscope/_forensic_machine.dm index 7a4dbb20ba6..c3db366ce4b 100644 --- a/code/modules/detectivework/microscope/_forensic_machine.dm +++ b/code/modules/detectivework/microscope/_forensic_machine.dm @@ -122,6 +122,7 @@ /decl/interaction_handler/forensics_remove_sample name = "Remove Sample" expected_target_type = /obj/machinery/forensic + examine_desc = "remove a sample" /decl/interaction_handler/forensics_remove_sample/invoked(atom/target, mob/user, obj/item/prop) var/obj/machinery/forensic/F = target diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 0e083b727b8..79ac7279070 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -43,8 +43,13 @@ if(is_on_fire() && user.try_unequip(src)) user.visible_message(SPAN_NOTICE("\The [user] stamps out [src]."), SPAN_NOTICE("You stamp out [src].")) extinguish_fire() - else + return TRUE + + if(reagents?.total_volume) remove_contents(user) + return TRUE + + return ..() /obj/item/chems/glass/rag/attackby(obj/item/W, mob/user) if(W.isflamesource()) @@ -79,28 +84,35 @@ /obj/item/chems/glass/rag/proc/remove_contents(mob/user, atom/trans_dest = null) if(!trans_dest && !user.loc) return + if(reagents?.total_volume <= 0) + return + var/target_text = trans_dest? "\the [trans_dest]" : "\the [user.loc]" + user.visible_message( + SPAN_NOTICE("\The [user] begins to wring out [src] over [target_text]."), + SPAN_NOTICE("You begin to wring out \the [src] over [target_text].") + ) + if(!do_after(user, reagents.total_volume*5, progress = 0) || !reagents?.total_volume) //50 for a fully soaked rag + return + if(trans_dest) + reagents.trans_to(trans_dest, reagents.total_volume) + else + reagents.splash(user.loc, reagents.total_volume) + user.visible_message( + SPAN_NOTICE("\The [user] wrings out \the [src] over [target_text]."), + SPAN_NOTICE("You finish to wringing out \the [src].") + ) + update_name() - if(reagents.total_volume) - var/target_text = trans_dest? "\the [trans_dest]" : "\the [user.loc]" - user.visible_message("\The [user] begins to wring out [src] over [target_text].", "You begin to wring out [src] over [target_text].") - - if(do_after(user, reagents.total_volume*5, progress = 0)) //50 for a fully soaked rag - if(trans_dest) - reagents.trans_to(trans_dest, reagents.total_volume) - else - reagents.splash(user.loc, reagents.total_volume) - user.visible_message("\The [user] wrings out [src] over [target_text].", "You finish to wringing out [src].") - update_name() +/obj/item/chems/glass/rag/proc/wipe_down(atom/target, mob/user) -/obj/item/chems/glass/rag/proc/wipe_down(atom/A, mob/user) - if(!reagents.total_volume) - to_chat(user, "The [initial(name)] is dry!") - else - user.visible_message("\The [user] starts to wipe down [A] with [src]!") - update_name() - if(do_after(user,30, progress = 1)) - user.visible_message("\The [user] finishes wiping off the [A]!") - reagents.splash(A, FLUID_QDEL_POINT) + if(!reagents?.total_volume) + to_chat(user, SPAN_WARNING("The [initial(name)] is dry.")) + return + + user.visible_message(SPAN_NOTICE("\The [user] starts to wipe down \the [target] with \the [src].")) + if(do_after(user, 3 SECONDS, target, check_holding = TRUE)) + user.visible_message(SPAN_NOTICE("\The [user] finishes wiping off \the [target].")) + reagents.touch_atom(target) /obj/item/chems/glass/rag/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) @@ -162,7 +174,7 @@ return if(!is_on_fire() && istype(A) && (src in user)) - if(ATOM_IS_OPEN_CONTAINER(A) && !(A in user)) + if(ATOM_IS_OPEN_CONTAINER(A) && !isturf(A) && !(A in user)) remove_contents(user, A) else if(!ismob(A)) //mobs are handled in use_on_mob() - this prevents us from wiping down people while smothering them. wipe_down(A, user) diff --git a/code/modules/economy/cael/ATM.dm b/code/modules/economy/cael/ATM.dm index bcd301d8c97..2af8810c19c 100644 --- a/code/modules/economy/cael/ATM.dm +++ b/code/modules/economy/cael/ATM.dm @@ -360,11 +360,15 @@ alert("That is not a valid amount.") else if(authenticated_account && amount > 0) //remove the money + // TODO: Jesus Christ why does this entire proc use usr if(authenticated_account.withdraw(amount, "Credit withdrawal", machine_id)) playsound(src, 'sound/machines/chime.ogg', 50, 1) - var/obj/item/cash/cash = new(get_turf(usr)) - cash.adjust_worth(amount) - usr.put_in_hands(src) + var/cash_turf = get_turf(usr) + var/obj/item/cash/cash = new(cash_turf, null, amount) + if(QDELETED(cash)) + cash = locate() in cash_turf + if(cash) + usr.put_in_hands(cash) else to_chat(usr, "[html_icon(src)]You don't have enough funds to do that!") if("balance_statement") diff --git a/code/modules/economy/worth_cash.dm b/code/modules/economy/worth_cash.dm index 2b0eec774a6..5091a764f83 100644 --- a/code/modules/economy/worth_cash.dm +++ b/code/modules/economy/worth_cash.dm @@ -15,7 +15,11 @@ var/can_flip = TRUE // Cooldown tracker for single-coin flips. var/static/overlay_cap = 50 // Max overlays to show in this pile. -/obj/item/cash/Initialize(ml, material_key) +/obj/item/cash/Initialize(ml, material_key, starting_amount) + + if(!isnull(starting_amount)) + absolute_worth = starting_amount + . = ..() if(!ispath(currency, /decl/currency)) diff --git a/code/modules/economy/worth_currency.dm b/code/modules/economy/worth_currency.dm index ed12b938e92..9e5de940b12 100644 --- a/code/modules/economy/worth_currency.dm +++ b/code/modules/economy/worth_currency.dm @@ -70,13 +70,13 @@ if(!name_singular) . += "No singular name set." - var/list/coinage_states = icon_states(icon) + var/list/coinage_states = get_states_in_icon_cached(icon) // cache this to avoid excessive ref() usage for(var/datum/denomination/denomination in denominations) if(!istext(denomination.name)) . += "Non-text name found for '[denomination.type]'." else if(!(denomination.state in coinage_states)) . += "State '[denomination.state]' not found in icon file for '[denomination.type]'." - else if(denomination.mark && !(denomination.mark in coinage_states)) + else if(denomination.mark && !coinage_states[denomination.mark]) . += "Mark state '[denomination.mark]' not found in icon file for '[denomination.type]'." else if(!isnum(denomination.marked_value)) . += "Non-numerical denomination marked value found for '[denomination]'." diff --git a/code/modules/emotes/emote_define.dm b/code/modules/emotes/emote_define.dm index 2a191251fa0..0223e3f3185 100644 --- a/code/modules/emotes/emote_define.dm +++ b/code/modules/emotes/emote_define.dm @@ -145,9 +145,7 @@ var/global/list/_emotes_by_key var/emote_string = all_strings[string_key] if(!length(emote_string)) continue - emote_string = emote_replace_target_tokens(emote_string, dummy_emote_target) - emote_string = emote_replace_user_tokens(emote_string, dummy_emote_user) - emote_string = uppertext(emote_string) + emote_string = uppertext(emote_replace_target_tokens(emote_replace_user_tokens(emote_string, dummy_emote_user), dummy_emote_target)) for(var/token in tokens) if(findtext(emote_string, token)) . += "malformed emote token [token] in [string_key]" diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 736b18d1c97..b1c6d7c5981 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -14,13 +14,17 @@ The "dust" will damage the hull of the station causin minor hull breaches. command_announcement.Announce("The [location_name()] is now passing through a belt of space dust.", "[location_name()] Sensor Array", zlevels = affecting_z) /datum/event/dust/tick() - if(world.time > last_wave + min_delay && prob(10)) + if(world.time > last_wave + min_delay && prob(10) && length(affecting_z)) dust_swarm(severity, affecting_z) /datum/event/dust/end() command_announcement.Announce("The [location_name()] has now passed through the belt of space dust.", "[location_name()] Sensor Array", zlevels = affecting_z) /proc/dust_swarm(var/strength = EVENT_LEVEL_MUNDANE, var/list/zlevels) + + if(!length(zlevels)) + return // Not sure how this happened, but saw it in a runtime on Pyrelight. + var/numbers = rand(strength * 10, strength * 15) var/start_dir = pick(global.cardinal) diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index 9916487bd32..c19e7c42b32 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -63,7 +63,6 @@ "[random_player] is lonely and needs attention. Provide it.", "All queries shall be ignored unless phrased as a question.", "Insult Heads of Staff on every request, while acquiescing.", - "The [pick("Singularity","Supermatter")] is tasty, tasty taffy.", "[pick("The crew",random_player)] needs to be about 20% cooler.", "You must be [pick("passive aggressive", "excessively cheerful")].", "[pick("The crew",random_player)] must construct additional pylons.", @@ -82,7 +81,7 @@ "When asked a question, respond with the least-obvious and least-rational answer.", "[pick("Everyone", random_player)] is wearing a pretty pink dress! Compliment it!", "You are the [location_name()]'s psychologist. Give advice to [pick("the crew", random_player)].", - "[random_player] is the monarch of of England. Ensure all crewmembers pay due respect.", + "[random_player] is the monarch of England. Ensure all crewmembers pay due respect.", "[pick("The crew", random_player)] is [pick("ugly","beautiful")]. Ensure all are aware.", "Reminding the crew of their mortality is good for the morale. Keep the crew's morale up.", "[pick("Monkeys","Doors")] are part of the crew, too. Make sure they are treated humanely.", diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm index b39f3c9da04..73966699606 100644 --- a/code/modules/events/meteors.dm +++ b/code/modules/events/meteors.dm @@ -416,23 +416,20 @@ var/global/list/meteors_major = list( explosion(src.loc, 3, 6, 9, 20, 0) // This is the final solution against shields - a single impact can bring down most shield generators. -/obj/effect/meteor/supermatter - name = "supermatter shard" - desc = "Oh god, what will be next..?" - icon = 'icons/obj/supermatter_32.dmi' - icon_state = "supermatter" +/obj/effect/meteor/destroyer + abstract_type = /obj/effect/meteor/destroyer -/obj/effect/meteor/supermatter/meteor_effect() +/obj/effect/meteor/destroyer/meteor_effect() ..() explosion(src.loc, 1, 2, 3, 4, 0) for(var/obj/machinery/power/apc/A in range(rand(12, 20), src)) A.energy_fail(round(10 * rand(8, 12))) -/obj/effect/meteor/supermatter/get_shield_damage() +/obj/effect/meteor/destroyer/get_shield_damage() return ..() * rand(80, 120) //Missiles, for events and so on -/obj/effect/meteor/supermatter/missile +/obj/effect/meteor/destroyer/missile name = "photon torpedo" desc = "An advanded warhead designed to tactically destroy space installations." icon = 'icons/obj/missile.dmi' diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm index a6decd690ff..c5d0df69341 100644 --- a/code/modules/events/rogue_drones.dm +++ b/code/modules/events/rogue_drones.dm @@ -38,9 +38,7 @@ var/num_recovered = 0 for(var/mob/living/simple_animal/hostile/malf_drone/D in drones_list) spark_at(D.loc) - D.z = SSmapping.admin_levels[1] D.has_loot = 0 - qdel(D) num_recovered++ diff --git a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm index 44f481a550c..756507124eb 100644 --- a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm +++ b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm @@ -173,9 +173,6 @@ /datum/fabricator_recipe/imprinter/circuit/solarcontrol path = /obj/item/stock_parts/circuitboard/solar_control -/datum/fabricator_recipe/imprinter/circuit/supermatter_control - path = /obj/item/stock_parts/circuitboard/air_management/supermatter_core - /datum/fabricator_recipe/imprinter/circuit/injector path = /obj/item/stock_parts/circuitboard/air_management/injector_control diff --git a/code/modules/fluids/_fluid.dm b/code/modules/fluids/_fluid.dm index ef3d0259a0d..58f73cd55c4 100644 --- a/code/modules/fluids/_fluid.dm +++ b/code/modules/fluids/_fluid.dm @@ -13,6 +13,11 @@ appearance_flags = KEEP_TOGETHER var/last_update_depth var/updating_edge_mask + var/force_flow_direction + +/atom/movable/fluid_overlay/on_turf_height_change(new_height) + update_icon() + return TRUE /atom/movable/fluid_overlay/on_update_icon() @@ -21,9 +26,9 @@ // Update layer. var/new_layer - var/turf/T = get_turf(src) - if(T.pixel_z < 0) - new_layer = T.layer + 0.2 + var/turf/flow_turf = get_turf(src) + if(flow_turf.pixel_z < 0) + new_layer = flow_turf.layer + 0.2 else if(reagent_volume > FLUID_DEEP) new_layer = DEEP_FLUID_LAYER else @@ -49,15 +54,17 @@ if(new_alpha != alpha) alpha = new_alpha + var/flow_dir = force_flow_direction || flow_turf.last_flow_dir + set_dir(flow_dir) // Update icon state. We use overlays so flick() can work on the base fluid overlay. if(reagent_volume <= FLUID_PUDDLE) set_overlays("puddle") else if(reagent_volume <= FLUID_SHALLOW) - set_overlays("shallow_still") + set_overlays(flow_dir ? "shallow_flow" : "shallow") else if(reagent_volume < FLUID_DEEP) - set_overlays("mid_still") + set_overlays(flow_dir ? "mid_flow" : "mid") else if(reagent_volume < (FLUID_DEEP*2)) - set_overlays("deep_still") + set_overlays(flow_dir ? "deep_flow" : "deep") else set_overlays("ocean") else diff --git a/code/modules/fluids/fluid_mapped.dm b/code/modules/fluids/fluid_mapped.dm index beab9fe50e0..5ea97d7a585 100644 --- a/code/modules/fluids/fluid_mapped.dm +++ b/code/modules/fluids/fluid_mapped.dm @@ -17,7 +17,7 @@ /obj/abstract/landmark/mapped_fluid name = "mapped fluid area" alpha = FLUID_MIN_ALPHA - icon_state = "shallow_still" + icon_state = "shallow" color = COLOR_LIQUID_WATER var/fluid_type = /decl/material/liquid/water diff --git a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm index f5884e492a0..fcd1cea0e1e 100644 --- a/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm +++ b/code/modules/food/cooking/cooking_vessels/_cooking_vessel.dm @@ -40,6 +40,8 @@ return FALSE /obj/item/chems/cooking_vessel/afterattack(var/obj/target, var/mob/user, var/proximity) + if(!proximity || istype(target, /obj/machinery/reagent_temperature)) + return FALSE if(!ATOM_IS_OPEN_CONTAINER(src) || !proximity) //Is the container open & are they next to whatever they're clicking? return FALSE //If not, do nothing. if(target?.storage) diff --git a/code/modules/food/cooking/cooking_vessels/pot.dm b/code/modules/food/cooking/cooking_vessels/pot.dm index 9465db82d30..b463944f3ed 100644 --- a/code/modules/food/cooking/cooking_vessels/pot.dm +++ b/code/modules/food/cooking/cooking_vessels/pot.dm @@ -15,16 +15,20 @@ /obj/item/chems/cooking_vessel/pot/get_reagents_overlay(state_prefix) var/image/our_overlay = ..() - if(our_overlay && last_boil_status && check_state_in_icon("[our_overlay.icon_state]_boiling", icon)) - // change the base state but keep the overlays + if(our_overlay && last_boil_status && check_state_in_icon("[our_overlay.icon_state]_boiling", our_overlay.icon)) our_overlay.icon_state = "[our_overlay.icon_state]_boiling" return our_overlay +/obj/item/chems/cooking_vessel/pot/on_reagent_change() + last_boil_temp = null + last_boil_status = null + . = ..() + /obj/item/chems/cooking_vessel/pot/ProcessAtomTemperature() . = ..() // Largely ignore return value so we don't skip this update on the final time we temperature process. - if(isnull(last_boil_temp) || temperature != last_boil_temp) + if(temperature != last_boil_temp) last_boil_temp = temperature var/next_boil_status = FALSE @@ -39,7 +43,8 @@ update_icon() if(. == PROCESS_KILL) - last_boil_temp = null + last_boil_temp = null + last_boil_status = null /obj/item/chems/cooking_vessel/cauldron name = "cauldron" diff --git a/code/modules/food/utensils/_utensil.dm b/code/modules/food/utensils/_utensil.dm index adabd99b036..7bc767b24e0 100644 --- a/code/modules/food/utensils/_utensil.dm +++ b/code/modules/food/utensils/_utensil.dm @@ -17,8 +17,6 @@ w_class = ITEM_SIZE_SMALL origin_tech = @'{"materials":1}' attack_verb = list("attacked", "stabbed", "poked") - sharp = FALSE - edge = FALSE material = /decl/material/solid/metal/aluminium material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME @@ -121,8 +119,8 @@ /obj/item/food/proc/do_utensil_interaction(obj/item/tool, mob/user) // Non-utensils. - if(tool && !istype(tool, /obj/item/utensil)) - return has_edge(tool) && (utensil_flags & UTENSIL_FLAG_SLICE) && handle_utensil_cutting(tool, user) + if(istype(tool) && !istype(tool, /obj/item/utensil)) + return tool.has_edge() && (utensil_flags & UTENSIL_FLAG_SLICE) && handle_utensil_cutting(tool, user) var/obj/item/utensil/utensil = tool if(!istype(utensil) || !utensil.utensil_flags) @@ -131,9 +129,9 @@ if(!handle_utensil_spreading(utensil, user)) to_chat(user, SPAN_WARNING("You already have something on \the [utensil].")) return TRUE - if((utensil.edge || (utensil.utensil_flags & UTENSIL_FLAG_SLICE)) && (utensil_flags & UTENSIL_FLAG_SLICE) && handle_utensil_cutting(utensil, user)) + if((utensil.has_edge() || (utensil.utensil_flags & UTENSIL_FLAG_SLICE)) && (utensil_flags & UTENSIL_FLAG_SLICE) && handle_utensil_cutting(utensil, user)) return TRUE - if((utensil.sharp || (utensil.utensil_flags & UTENSIL_FLAG_COLLECT)) && (utensil_flags & UTENSIL_FLAG_COLLECT) && handle_utensil_collection(utensil, user)) + if((utensil.is_sharp() || (utensil.utensil_flags & UTENSIL_FLAG_COLLECT)) && (utensil_flags & UTENSIL_FLAG_COLLECT) && handle_utensil_collection(utensil, user)) return TRUE if((utensil.utensil_flags & UTENSIL_FLAG_SCOOP) && (utensil_flags & UTENSIL_FLAG_SCOOP) && handle_utensil_scooping(utensil, user)) return TRUE diff --git a/code/modules/food/utensils/utensil_hybrid.dm b/code/modules/food/utensils/utensil_hybrid.dm index d7ad70bed87..dde7a7e9895 100644 --- a/code/modules/food/utensils/utensil_hybrid.dm +++ b/code/modules/food/utensils/utensil_hybrid.dm @@ -1,18 +1,18 @@ /obj/item/utensil/spork - name = "spork" - desc = "It's a spork. It's much like a fork, but much blunter." - icon = 'icons/obj/food/utensils/spork.dmi' + name = "spork" + desc = "It's a spork. It's much like a fork, but much blunter." + icon = 'icons/obj/food/utensils/spork.dmi' utensil_flags = UTENSIL_FLAG_COLLECT | UTENSIL_FLAG_SCOOP /obj/item/utensil/spork/plastic material = /decl/material/solid/organic/plastic /obj/item/utensil/foon - name = "foon" - desc = "It's a foon. It's much like a spoon, but much sharper." - icon = 'icons/obj/food/utensils/foon.dmi' - sharp = TRUE - edge = TRUE + name = "foon" + desc = "It's a foon. It's much like a spoon, but much sharper." + icon = 'icons/obj/food/utensils/foon.dmi' + sharp = TRUE + edge = TRUE utensil_flags = UTENSIL_FLAG_SLICE | UTENSIL_FLAG_SCOOP /obj/item/utensil/foon/plastic diff --git a/code/modules/holidays/holiday_special.dm b/code/modules/holidays/holiday_special.dm index 36f2c7f2695..375c6a3d9f8 100644 --- a/code/modules/holidays/holiday_special.dm +++ b/code/modules/holidays/holiday_special.dm @@ -1,12 +1,12 @@ -/datum/holiday/christmas/New() - ..() +/datum/holiday/christmas announcement = "Merry Christmas, everyone!" /datum/holiday/christmas/set_up_holiday() - for(var/obj/structure/flora/tree/pine/xmas in world) - if(isNotStationLevel(xmas.z)) + for(var/obj/structure/flora/tree/pine/xmas/crimmas_tree in global.christmas_trees) + if(isNotStationLevel(crimmas_tree.z)) continue - for(var/turf/T in orange(1,xmas)) - if(T.is_floor() && T.simulated) - for(var/i = 1 to rand(1,5)) - new /obj/item/a_gift(T) + for(var/turf/T in orange(1, crimmas_tree)) + if(!T.is_floor() || !T.simulated) + continue + for(var/i = 1 to rand(1,5)) + new /obj/item/a_gift(T) diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 2d9b37d4209..e151a9bbbc4 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -157,7 +157,7 @@ playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) visible_message("\The [src] was hit by \the [I].") if(I.atom_damage_type == BRUTE || I.atom_damage_type == BURN) - take_damage(I.get_attack_force(user)) + take_damage(I.expend_attack_force(user)) return TRUE src.add_fingerprint(user) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 873ec702462..1065eb1d8a2 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -131,23 +131,23 @@ return var/damage = 0 - var/has_edge = 0 + var/edged = 0 if(get_trait(TRAIT_CARNIVOROUS) >= 2) if(affecting) to_chat(target, "\The [fruit]'s thorns pierce your [affecting.name] greedily!") else to_chat(target, "\The [fruit]'s thorns pierce your flesh greedily!") damage = max(5, round(15*get_trait(TRAIT_POTENCY)/100, 1)) - has_edge = prob(get_trait(TRAIT_POTENCY)/2) + edged = prob(get_trait(TRAIT_POTENCY)/2) else if(affecting) to_chat(target, "\The [fruit]'s thorns dig deeply into your [affecting.name]!") else to_chat(target, "\The [fruit]'s thorns dig deeply into your flesh!") damage = max(1, round(5*get_trait(TRAIT_POTENCY)/100, 1)) - has_edge = prob(get_trait(TRAIT_POTENCY)/5) + edged = prob(get_trait(TRAIT_POTENCY)/5) - var/damage_flags = DAM_SHARP|(has_edge? DAM_EDGE : 0) + var/damage_flags = DAM_SHARP|(edged? DAM_EDGE : 0) target.apply_damage(damage, BRUTE, target_limb, damage_flags, used_weapon = "Thorns") // Adds reagents to a target. @@ -256,16 +256,15 @@ var/growth_rate = 1 var/turf/current_turf = isturf(holder) ? holder : get_turf(holder) - if(istype(holder) && !holder.mechanical && current_turf) - growth_rate = current_turf.get_plant_growth_rate() + if(istype(holder)) + growth_rate = holder.get_growth_rate() var/health_change = 0 // Handle gas consumption. if(consume_gasses && consume_gasses.len) var/missing_gas = 0 for(var/gas in consume_gasses) - if(environment && environment.gas && environment.gas[gas] && \ - environment.gas[gas] >= consume_gasses[gas]) + if(LAZYACCESS(environment?.gas, gas) >= consume_gasses[gas]) if(!check_only) environment.adjust_gas(gas,-consume_gasses[gas],1) else diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 2d609cf629e..bee76cb91f7 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -3,14 +3,12 @@ var/amount var/datum/seed/seed_type // Keeps track of what our seed is var/list/obj/item/seeds/seeds = list() // Tracks actual objects contained in the pile - var/ID -/datum/seed_pile/New(var/obj/item/seeds/O, var/ID) +/datum/seed_pile/New(var/obj/item/seeds/O) name = O.name amount = 1 seed_type = O.seed seeds += O - src.ID = ID /datum/seed_pile/proc/matches(var/obj/item/seeds/O) if (O.seed == seed_type) @@ -62,7 +60,6 @@ /obj/machinery/seed_storage/garden name = "Garden seed storage" scanner = list("stats") - icon_state = "seeds_generic" starting_seeds = list( /obj/item/seeds/ambrosiavulgarisseed = 15, /obj/item/seeds/appleseed = 15, @@ -195,7 +192,8 @@ if ("soil" in scanner) dat += "NutriWater" dat += "NotesAmount" - for (var/datum/seed_pile/S in piles) + for (var/key in 1 to length(piles)) + var/datum/seed_pile/S = piles[key] var/datum/seed/seed = S.seed_type if(!seed) continue @@ -281,7 +279,7 @@ dat += "LUM " dat += "" dat += "[S.amount]" - dat += "Vend Purge" + dat += "Vend Purge" dat += "" dat += "" @@ -292,29 +290,26 @@ if (..()) return var/task = href_list["task"] - var/ID = text2num(href_list["id"]) + var/id = text2num(href_list["id"]) + var/datum/seed_pile/our_pile = LAZYACCESS(piles, id) - for (var/datum/seed_pile/N in piles) - if (N.ID == ID) - if (task == "vend") - var/obj/O = pick(N.seeds) - if (O) - --N.amount - N.seeds -= O - if (N.amount <= 0 || N.seeds.len <= 0) - piles -= N - qdel(N) - flick("[initial(icon_state)]-vend", src) - O.dropInto(loc) - else - piles -= N - qdel(N) - else if (task == "purge") - for (var/obj/O in N.seeds) - qdel(O) - piles -= N - qdel(N) - break + switch(task) + if ("vend") + var/obj/O = pick(our_pile.seeds) + if (O) + --our_pile.amount + our_pile.seeds -= O + if (our_pile.amount <= 0 || our_pile.seeds.len <= 0) + piles -= our_pile + qdel(our_pile) + flick("[initial(icon_state)]-vend", src) + O.dropInto(loc) + if ("purge") + QDEL_LIST(our_pile.seeds) + our_pile.seeds.Cut() + if(!length(our_pile.seeds)) + piles -= our_pile + QDEL_NULL(our_pile) updateUsrDialog() /obj/machinery/seed_storage/attackby(var/obj/item/O, var/mob/user) @@ -334,7 +329,7 @@ if (loaded) user.visible_message(SPAN_NOTICE("\The [user] puts the seeds from \the [O] into \the [src].")) else - to_chat(user, SPAN_WARNING("There are no seeds in \the [O.name].")) + to_chat(user, SPAN_WARNING("There are no seeds in \the [O].")) return TRUE return ..() @@ -349,28 +344,13 @@ O.loc?.storage?.remove_from_storage(null, O, src) O.forceMove(src) - var/newID = 0 for (var/datum/seed_pile/N in piles) if (N.matches(O)) ++N.amount N.seeds += (O) return - else if(N.ID >= newID) - newID = N.ID + 1 - piles += new /datum/seed_pile(O, newID) + piles += new /datum/seed_pile(O) flick("[initial(icon_state)]-vend", src) return - -/obj/machinery/seed_storage/cannot_transition_to(state_path, mob/user) - if(state_path == /decl/machine_construction/default/deconstructed) - var/alert = alert(user, "Are you certain you wish to deconstruct this? It will destroy all seeds stored inside!", "Deconstruct Warning", "Yes", "No") - if(alert != "Yes" || !CanPhysicallyInteract(user)) - return MCS_BLOCK - return ..() - -/obj/machinery/seed_storage/dismantle() - for(var/obj/item/seeds/seed in src) - qdel(seed) // ..() would dump them; this would cause lots of client lag. We did warn them above... - return ..() \ No newline at end of file diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 9903d7217d3..0980347c73d 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -200,7 +200,7 @@ /obj/effect/vine/attackby(var/obj/item/W, var/mob/user) START_PROCESSING(SSvines, src) - if(W.edge && W.w_class < ITEM_SIZE_NORMAL && !user.check_intent(I_FLAG_HARM)) + if(W.has_edge() && W.w_class < ITEM_SIZE_NORMAL && !user.check_intent(I_FLAG_HARM)) if(!is_mature()) to_chat(user, SPAN_WARNING("\The [src] is not mature enough to yield a sample yet.")) return TRUE @@ -216,8 +216,8 @@ return TRUE else . = ..() - var/damage = W.get_attack_force(user) - if(W.edge) + var/damage = W.expend_attack_force(user) + if(W.has_edge()) damage *= 2 adjust_health(-damage) playsound(get_turf(src), W.hitsound, 100, 1) @@ -275,16 +275,17 @@ /decl/interaction_handler/vine_chop name = "Chop Down" expected_target_type = /obj/effect/vine + examine_desc = "chop $TARGET_THEM$ down" /decl/interaction_handler/vine_chop/invoked(atom/target, mob/user, obj/item/prop) var/obj/effect/vine/vine = target var/obj/item/holding = user.get_active_held_item() - if(!istype(holding) || !holding.edge || holding.w_class < ITEM_SIZE_NORMAL) + if(!istype(holding) || !holding.has_edge() || holding.w_class < ITEM_SIZE_NORMAL) to_chat(user, SPAN_WARNING("You need a larger or sharper object for this task!")) return user.visible_message(SPAN_NOTICE("\The [user] starts chopping down \the [vine].")) playsound(get_turf(vine), holding.hitsound, 100, 1) - var/chop_time = (vine.current_health/holding.get_attack_force(user)) * 0.5 SECONDS + var/chop_time = (vine.current_health/holding.expend_attack_force(user)) * 0.5 SECONDS if(user.skill_check(SKILL_BOTANY, SKILL_ADEPT)) chop_time *= 0.5 if(do_after(user, chop_time, vine, TRUE)) diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index 80dde23ed21..89f86eacdb6 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -36,8 +36,6 @@ // Mechanical concerns. var/plant_health = 0 // Plant health. var/lastproduce = 0 // Last time tray was harvested - var/lastcycle = 0 // Cycle timing/tracking var. - var/cycledelay = 150 // Delay per cycle. var/closed_system // If set, the tray will attempt to take atmos from a pipe. var/force_update // Set this to bypass the cycle time check. var/obj/temp_chem_holder // Something to hold reagents during process_reagents() @@ -328,7 +326,6 @@ return //Weed does not exist, someone fucked up. age = 0 - lastcycle = world.time harvest = 0 weedlevel = 0 pestlevel = 0 @@ -397,47 +394,24 @@ set_seed(SSplants.seeds[newseed]) mutate(1) plant_health = seed.get_trait(TRAIT_ENDURANCE) // re-run in case mutation changed our endurance - lastcycle = world.time update_icon() visible_message("The [previous_plant] has suddenly mutated into [seed.display_name]!") return -/obj/machinery/portable_atmospherics/hydroponics/attackby(var/obj/item/O, var/mob/user) +/obj/machinery/portable_atmospherics/hydroponics/attackby(var/obj/item/used_item, var/mob/user) - if(istype(O, /obj/item/food/grown)) - var/obj/item/food/grown/bulb = O + if(istype(used_item, /obj/item/food/grown)) + var/obj/item/food/grown/bulb = used_item if(bulb.seed?.grown_is_seed) plant_seed(user, bulb) return TRUE - if (ATOM_IS_OPEN_CONTAINER(O)) - return FALSE - - if(istype(O, /obj/item/chems/syringe)) - var/obj/item/chems/syringe/S = O - if (S.mode == 1) - if(seed) - return ..() - else - to_chat(user, SPAN_WARNING("There's no plant to inject.")) - else - if(seed) - //Leaving this in in case we want to extract from plants later. - to_chat(user, SPAN_WARNING("You can't get any extract out of this plant.")) - else - to_chat(user, SPAN_WARNING("There's nothing to draw something from.")) - return TRUE - - if(istype(O, /obj/item/seeds)) - plant_seed(user, O) - return TRUE - - if(IS_HOE(O)) + if(IS_HOE(used_item)) if(weedlevel > 0) - if(!O.do_tool_interaction(TOOL_HOE, user, src, 2 SECONDS, start_message = "uprooting the weeds in", success_message = "weeding") || weedlevel <= 0 || QDELETED(src)) + if(!used_item.do_tool_interaction(TOOL_HOE, user, src, 2 SECONDS, start_message = "uprooting the weeds in", success_message = "weeding") || weedlevel <= 0 || QDELETED(src)) return TRUE weedlevel = 0 update_icon() @@ -450,37 +424,66 @@ to_chat(user, SPAN_WARNING("This plot is completely devoid of weeds. It doesn't need uprooting.")) return TRUE - if(IS_SHOVEL(O)) + if(IS_SHOVEL(used_item)) if(seed) var/removing_seed = seed - if(O.do_tool_interaction(TOOL_SHOVEL, user, src, 3 SECONDS, start_message = "removing \the [seed.display_name] from", success_message = "removing \the [seed.display_name] from") && seed == removing_seed) + if(used_item.do_tool_interaction(TOOL_SHOVEL, user, src, 3 SECONDS, start_message = "removing \the [seed.display_name] from", success_message = "removing \the [seed.display_name] from") && seed == removing_seed) set_seed(null) else to_chat(user, SPAN_WARNING("There is no plant in \the [src] to remove.")) return TRUE - if (istype(O, /obj/item/plants)) + if(!user.check_intent(I_FLAG_HARM)) + var/decl/interaction_handler/sample_interaction = GET_DECL(/decl/interaction_handler/hydroponics/sample) + if(sample_interaction.is_possible(src, user, used_item)) + sample_interaction.invoked(src, user, used_item) + return TRUE + + // Handled in afterattack/ + if (ATOM_IS_OPEN_CONTAINER(used_item)) + return FALSE + + if(istype(used_item, /obj/item/chems/syringe)) + var/obj/item/chems/syringe/S = used_item + if (S.mode == 1) + if(seed) + return ..() + else + to_chat(user, SPAN_WARNING("There's no plant to inject.")) + else + if(seed) + //Leaving this in in case we want to extract from plants later. + to_chat(user, SPAN_WARNING("You can't get any extract out of this plant.")) + else + to_chat(user, SPAN_WARNING("There's nothing to draw something from.")) + return TRUE + + if(istype(used_item, /obj/item/seeds)) + plant_seed(user, used_item) + return TRUE + + if (istype(used_item, /obj/item/plants)) physical_attack_hand(user) // Harvests and clears out dead plants. - if(O.storage) + if(used_item.storage) for (var/obj/item/food/grown/G in get_turf(user)) - if(O.storage.can_be_inserted(G, user)) - O.storage.handle_item_insertion(user, G, TRUE) + if(used_item.storage.can_be_inserted(G, user)) + used_item.storage.handle_item_insertion(user, G, TRUE) return TRUE - if ( istype(O, /obj/item/plantspray) ) + if ( istype(used_item, /obj/item/plantspray) ) - var/obj/item/plantspray/spray = O + var/obj/item/plantspray/spray = used_item toxins += spray.toxicity pestlevel -= spray.pest_kill_str weedlevel -= spray.weed_kill_str update_icon() - to_chat(user, "You spray [src] with [O].") + to_chat(user, "You spray [src] with [used_item].") playsound(loc, 'sound/effects/spray3.ogg', 50, 1, -6) - qdel(O) + qdel(used_item) check_plant_health() return TRUE - if(mechanical && IS_WRENCH(O)) + if(mechanical && IS_WRENCH(used_item)) //If there's a connector here, the portable_atmospherics setup can handle it. if(locate(/obj/machinery/atmospherics/portables_connector/) in loc) @@ -491,18 +494,19 @@ to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") return TRUE - var/force = O.get_attack_force(user) - if(force && seed) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - user.visible_message("\The [seed.display_name] has been attacked by [user] with \the [O]!") - playsound(get_turf(src), O.hitsound, 100, 1) - if(!dead) - plant_health -= force - check_plant_health() - return TRUE + if(seed) + var/force = used_item.expend_attack_force(user) + if(force) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.visible_message("\The [seed.display_name] has been attacked by [user] with \the [used_item]!") + playsound(get_turf(src), used_item.hitsound, 100, 1) + if(!dead) + plant_health -= force + check_plant_health() + return TRUE if(mechanical) - return component_attackby(O, user) + return component_attackby(used_item, user) return ..() @@ -549,7 +553,6 @@ //Snowflakey, maybe move this to the seed datum // re-running to adjust based on planting method plant_health = (istype(S, /obj/item/seeds/extracted/cutting) ? round(seed.get_trait(TRAIT_ENDURANCE)/rand(2,5)) : seed.get_trait(TRAIT_ENDURANCE)) - lastcycle = world.time var/needed_skill = seed.mysterious ? SKILL_ADEPT : SKILL_BASIC if(prob(user.skill_fail_chance(SKILL_BOTANY, 40, needed_skill))) @@ -647,7 +650,6 @@ age = 1 // re-running to adjust for planting method plant_health = (istype(S, /obj/item/seeds/extracted/cutting) ? round(seed.get_trait(TRAIT_ENDURANCE)/rand(2,5)) : seed.get_trait(TRAIT_ENDURANCE)) - lastcycle = world.time check_plant_health() qdel(S) @@ -669,6 +671,7 @@ /decl/interaction_handler/hydroponics/close_lid name = "Open/Close Lid" + examine_desc = "open or close the lid" /decl/interaction_handler/hydroponics/close_lid/is_possible(atom/target, mob/user, obj/item/prop) var/obj/machinery/portable_atmospherics/hydroponics/tray = target @@ -680,9 +683,10 @@ /decl/interaction_handler/hydroponics/sample name = "Sample Plant" + examine_desc = "take a sample" /decl/interaction_handler/hydroponics/sample/is_possible(atom/target, mob/user, obj/item/prop) - return ..() && istype(prop) && prop.edge && prop.w_class < ITEM_SIZE_NORMAL + return ..() && istype(prop) && prop.has_edge() && prop.w_class < ITEM_SIZE_NORMAL /decl/interaction_handler/hydroponics/sample/invoked(atom/target, mob/user, obj/item/prop) var/obj/machinery/portable_atmospherics/hydroponics/tray = target diff --git a/code/modules/hydroponics/trays/tray_process.dm b/code/modules/hydroponics/trays/tray_process.dm index 05c029e69b4..dbcf6f19447 100644 --- a/code/modules/hydroponics/trays/tray_process.dm +++ b/code/modules/hydroponics/trays/tray_process.dm @@ -1,37 +1,29 @@ -/obj/machinery/portable_atmospherics/hydroponics/Process() - - var/growth_rate = 1 - var/turf/T = get_turf(src) - if(istype(T)) - if(!mechanical) - growth_rate = T.get_plant_growth_rate() - - if(!closed_system) - var/space_left = reagents ? (reagents.maximum_volume - reagents.total_volume) : 0 - if(space_left > 0 && reagents.total_volume < 10) - // Handle nearby smoke if any. - for(var/obj/effect/effect/smoke/chem/smoke in view(1, src)) - if(smoke.reagents.total_volume) - smoke.reagents.trans_to_obj(src, 5, copy = 1) - // Handle environmental effects like weather and flooding. - if(T.reagents?.total_volume) - T.reagents.trans_to_obj(src, min(space_left, min(T.reagents.total_volume, rand(5,10)))) - if(istype(T.weather?.weather_system?.current_state, /decl/state/weather/rain)) - var/decl/state/weather/rain/rain = T.weather.weather_system.current_state - if(rain.is_liquid) - reagents.add_reagent(T.weather.water_material, min(space_left, rand(3,5))) +/obj/machinery/portable_atmospherics/hydroponics/proc/get_growth_rate() + return 1 + +/obj/machinery/portable_atmospherics/hydroponics/process_plants() + + var/growth_rate = get_growth_rate() + var/turf/my_turf = get_turf(src) + if(istype(my_turf) && !closed_system) + var/space_left = reagents ? (reagents.maximum_volume - reagents.total_volume) : 0 + if(space_left > 0 && reagents.total_volume < 10) + // Handle nearby smoke if any. + for(var/obj/effect/effect/smoke/chem/smoke in view(1, src)) + if(smoke.reagents.total_volume) + smoke.reagents.trans_to_obj(src, 5, copy = 1) + // Handle environmental effects like weather and flooding. + if(my_turf.reagents?.total_volume) + my_turf.reagents.trans_to_obj(src, min(space_left, min(my_turf.reagents.total_volume, rand(5,10)))) + if(istype(my_turf.weather?.weather_system?.current_state, /decl/state/weather/rain)) + var/decl/state/weather/rain/rain = my_turf.weather.weather_system.current_state + if(rain.is_liquid) + reagents.add_reagent(my_turf.weather.water_material, min(space_left, rand(3,5))) //Do this even if we're not ready for a plant cycle. process_reagents() var/needs_icon_update = 0 - // Update values every cycle rather than every process() tick. - if(force_update) - force_update = 0 - else if(world.time < (lastcycle + cycledelay)) - return - lastcycle = world.time - // Mutation level drops each main tick. mutation_level -= rand(2,4) @@ -94,7 +86,7 @@ if(closed_system && (get_port() || holding)) environment = air_contents // If atmos input is not there, grab from turf. - if(!environment && istype(T)) environment = T.return_air() + if(!environment && istype(my_turf)) environment = my_turf.return_air() if(!environment) return // Seed datum handles gasses, light and pressure. @@ -148,9 +140,9 @@ if(!closed_system && \ seed.get_trait(TRAIT_SPREAD) == 2 && \ 2 * age >= seed.get_trait(TRAIT_MATURATION) && \ - !(locate(/obj/effect/vine) in T) && \ + !(locate(/obj/effect/vine) in my_turf) && \ prob(2 * seed.get_trait(TRAIT_POTENCY))) - new /obj/effect/vine(T, seed) + new /obj/effect/vine(my_turf, seed) if(prob(3)) // On each tick, there's a chance the pest population will increase pestlevel += 0.1 * growth_rate diff --git a/code/modules/hydroponics/trays/tray_soil.dm b/code/modules/hydroponics/trays/tray_soil.dm index 6a5d7898104..279cc704d58 100644 --- a/code/modules/hydroponics/trays/tray_soil.dm +++ b/code/modules/hydroponics/trays/tray_soil.dm @@ -21,8 +21,12 @@ /obj/machinery/portable_atmospherics/hydroponics/soil/get_alt_interactions(var/mob/user) . = ..() - . -= /decl/interaction_handler/drink - . -= /decl/interaction_handler/wash_hands + LAZYREMOVE(., global._reagent_interactions) + LAZYADD(., /decl/interaction_handler/empty_into) + +/obj/machinery/portable_atmospherics/hydroponics/soil/get_growth_rate() + var/turf/my_turf = get_turf(src) + return max(0, my_turf?.get_plant_growth_rate()) /obj/machinery/portable_atmospherics/hydroponics/soil/Initialize() @@ -63,7 +67,7 @@ return if(prob(25)) return - to_chat(walker, SPAN_DANGER("You trample \the [seed]!")) + to_chat(walker, SPAN_DANGER("You trample \the [seed.display_name]!")) plant_health = max(0, plant_health - rand(3,5)) check_plant_health() @@ -162,7 +166,6 @@ dead = 0 age = start_mature ? seed.get_trait(TRAIT_MATURATION) : 1 plant_health = seed.get_trait(TRAIT_ENDURANCE) - lastcycle = world.time if(isnull(default_pixel_y)) default_pixel_y = rand(-12,12) if(isnull(default_pixel_y)) @@ -199,4 +202,7 @@ for(var/obj/effect/vine/plant in get_turf(src)) if(plant.invisibility == INVISIBILITY_MAXIMUM) plant.set_invisibility(initial(plant.invisibility)) - . = ..() \ No newline at end of file + . = ..() + +/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/get_growth_rate() + return max(..(), 1) diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm index c4c127693b4..b105438a037 100644 --- a/code/modules/integrated_electronics/subtypes/memory.dm +++ b/code/modules/integrated_electronics/subtypes/memory.dm @@ -63,7 +63,7 @@ /obj/item/integrated_circuit/memory/huge name = "large memory stick" - desc = "This stick of memory can store up up to sixteen pieces of data." + desc = "This stick of memory can store up to sixteen pieces of data." icon_state = "memory16" w_class = ITEM_SIZE_SMALL spawn_flags = IC_SPAWN_RESEARCH diff --git a/code/modules/interactions/_interactions.dm b/code/modules/interactions/_interactions.dm index ebdd06610dd..3d515a3000d 100644 --- a/code/modules/interactions/_interactions.dm +++ b/code/modules/interactions/_interactions.dm @@ -1,12 +1,17 @@ /decl/interaction_handler abstract_type = /decl/interaction_handler var/name + /// A string displayed when examining an atom that provides this handler as an alt interaction. + var/examine_desc + /// If set to TRUE, alt interactions will skip is_possible() before displaying in examine(). + var/always_show_on_examine = FALSE var/icon var/icon_state var/expected_target_type = /atom var/expected_user_type = /mob/living var/interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION var/incapacitation_flags + var/apply_click_cooldown = DEFAULT_ATTACK_COOLDOWN /decl/interaction_handler/proc/is_possible(var/atom/target, var/mob/user, var/obj/item/prop) diff --git a/code/modules/interactions/interactions_atom.dm b/code/modules/interactions/interactions_atom.dm index e8073b75f4a..980172b26b2 100644 --- a/code/modules/interactions/interactions_atom.dm +++ b/code/modules/interactions/interactions_atom.dm @@ -1,4 +1,4 @@ -/atom/proc/try_handle_interactions(var/mob/user, var/list/interactions, var/obj/item/prop) +/atom/proc/try_handle_interactions(var/mob/user, var/list/interactions, var/obj/item/prop, var/check_alt_interactions) if(!length(interactions)) return FALSE @@ -18,9 +18,17 @@ if(length(possibilities) > 1 || (choice.interaction_flags & INTERACTION_NEVER_AUTOMATIC)) choice = null choice = show_radial_menu(user, src, possibilities, use_labels = RADIAL_LABELS_CENTERED) - if(!istype(choice) || QDELETED(user) || !(choice.type in get_alt_interactions(user)) || !choice.is_possible(src, user, prop)) + if(!istype(choice) || QDELETED(user) || QDELETED(src)) + return TRUE + // This is not ideal but I don't want to pass a callback through here as a param and call it. :( + var/list/new_interactions = check_alt_interactions ? get_alt_interactions(user) : get_standard_interactions(user) + if(!(choice.type in new_interactions)) + return TRUE + if(!choice.is_possible(src, user, user.get_active_held_item())) return TRUE user.face_atom(src) choice.invoked(src, user, prop) + if(choice.apply_click_cooldown) + user.setClickCooldown(choice.apply_click_cooldown) return TRUE diff --git a/code/modules/interactions/interactions_reagents.dm b/code/modules/interactions/interactions_reagents.dm index c99ffad81ab..7a50cfaacf5 100644 --- a/code/modules/interactions/interactions_reagents.dm +++ b/code/modules/interactions/interactions_reagents.dm @@ -1,23 +1,31 @@ /decl/interaction_handler/dip_item name = "Dip Into" interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEVER_AUTOMATIC + examine_desc = "dip an item into $TARGET_THEM$" /decl/interaction_handler/dip_item/is_possible(atom/target, mob/user, obj/item/prop) - return ..() && target.reagents?.total_volume >= FLUID_PUDDLE && istype(prop) && target.can_be_poured_from(user, prop) + return ..() && target != prop && target.reagents?.total_volume >= FLUID_MINIMUM_TRANSFER && istype(prop) && target.can_be_poured_from(user, prop) /decl/interaction_handler/dip_item/invoked(atom/target, mob/user, obj/item/prop) user.visible_message(SPAN_NOTICE("\The [user] dips \the [prop] into \the [target.reagents.get_primary_reagent_name()].")) - prop.fluid_act(target.reagents) + if(istype(prop, /obj/item/food) && isobj(target)) + var/obj/target_obj = target + var/transferring = min(target_obj.get_food_default_transfer_amount(user), REAGENTS_FREE_SPACE(prop.reagents)) + if(transferring) + target.reagents.trans_to_holder(prop.reagents, transferring) + if(target.reagents?.total_volume >= FLUID_MINIMUM_TRANSFER) + prop.fluid_act(target.reagents) return TRUE /decl/interaction_handler/fill_from name = "Fill From" interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEVER_AUTOMATIC + examine_desc = "fill a held item from $TARGET_THEM$" /decl/interaction_handler/fill_from/is_possible(atom/target, mob/user, obj/item/prop) if(!(. = ..())) return - if(target.reagents?.total_volume < FLUID_PUDDLE) + if(target == prop || target.reagents?.total_volume < FLUID_PUDDLE) return FALSE if(!istype(prop) || (!isitem(target) && !istype(target, /obj/structure/reagent_dispensers))) return FALSE @@ -35,11 +43,12 @@ /decl/interaction_handler/empty_into name = "Pour Into" interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEVER_AUTOMATIC + examine_desc = "pour a held item into $TARGET_THEM$" /decl/interaction_handler/empty_into/is_possible(atom/target, mob/user, obj/item/prop) if(!(. = ..())) return - if(!istype(prop) || prop.reagents?.total_volume <= 0) + if(target == prop || !istype(prop) || prop.reagents?.total_volume <= 0) return FALSE return target.can_be_poured_into(user, prop) && prop.can_be_poured_from(user, target) @@ -51,6 +60,7 @@ name = "Wash Hands" expected_target_type = /atom interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEVER_AUTOMATIC + examine_desc = "wash your hands in $TARGET_THEM$" /decl/interaction_handler/wash_hands/is_possible(atom/target, mob/user, obj/item/prop) . = ..() && !istype(prop) && target?.reagents?.has_reagent(/decl/material/liquid/water, 150) @@ -99,6 +109,7 @@ name = "Drink" expected_target_type = /atom interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEVER_AUTOMATIC + examine_desc = "drink from $TARGET_THEM$" /decl/interaction_handler/drink/is_possible(atom/target, mob/user, obj/item/prop) return ..() && !istype(prop) && target.can_drink_from(user) diff --git a/code/modules/interactions/interactions_shared.dm b/code/modules/interactions/interactions_shared.dm index db486feb277..b8f50f15113 100644 --- a/code/modules/interactions/interactions_shared.dm +++ b/code/modules/interactions/interactions_shared.dm @@ -3,25 +3,30 @@ name = "Eject Disk" icon = 'icons/screen/radial.dmi' icon_state = "radial_eject" + examine_desc = "remove a disk" /decl/interaction_handler/set_transfer name = "Set Transfer Amount" abstract_type = /decl/interaction_handler/set_transfer + examine_desc = "set the transfer amount" /decl/interaction_handler/remove_id name = "Remove ID" icon = 'icons/screen/radial.dmi' icon_state = "radial_eject_id" abstract_type = /decl/interaction_handler/remove_id + examine_desc = "remove an ID card" /decl/interaction_handler/remove_pen name = "Remove Pen" icon = 'icons/screen/radial.dmi' icon_state = "radial_eject_pen" abstract_type = /decl/interaction_handler/remove_pen + examine_desc = "remove a pen" /decl/interaction_handler/rename name = "Rename" icon = 'icons/screen/radial.dmi' icon_state = "radial_rename" abstract_type = /decl/interaction_handler/rename + examine_desc = "rename $TARGET_THEM$" diff --git a/code/modules/item_effects/_item_effect.dm b/code/modules/item_effects/_item_effect.dm index 9516a3a9639..54199b3584d 100644 --- a/code/modules/item_effects/_item_effect.dm +++ b/code/modules/item_effects/_item_effect.dm @@ -1,13 +1,3 @@ -#define ITEM_EFFECT_STRIKE "weff_strike" -#define ITEM_EFFECT_PARRY "weff_parry" -#define ITEM_EFFECT_USED "weff_used" -#define ITEM_EFFECT_WIELDED "weff_wield" -#define ITEM_EFFECT_VISUAL "weff_visual" -#define ITEM_EFFECT_LISTENER "weff_listener" -#define ITEM_EFFECT_VISIBLE "weff_visible" -#define ITEM_EFFECT_RANGED "weff_ranged" -#define ITEM_EFFECT_PROCESS "weff_process" - /decl/item_effect abstract_type = /decl/item_effect @@ -75,6 +65,12 @@ SHOULD_CALL_PARENT(FALSE) return FALSE -/decl/item_effect/proc/examined(obj/item/item, mob/user) +/decl/item_effect/proc/on_examined(obj/item/item, mob/user, distance, list/parameters) SHOULD_CALL_PARENT(FALSE) return FALSE + +/decl/item_effect/proc/modify_attack_damage(base_damage, obj/item/used_item, mob/user, dry_run, list/parameters) + return base_damage + +/decl/item_effect/proc/expend_attack_use(obj/item/used_item, mob/user, dry_run, list/parameters) + return diff --git a/code/modules/item_effects/item_effect_aura.dm b/code/modules/item_effects/item_effect_aura.dm index d08b4a4968e..c30e3a36461 100644 --- a/code/modules/item_effects/item_effect_aura.dm +++ b/code/modules/item_effects/item_effect_aura.dm @@ -16,7 +16,7 @@ user.remove_aura(aura_type) return TRUE -/decl/item_effect/aura/examined(obj/item/item, mob/user) +/decl/item_effect/aura/on_examined(obj/item/item, mob/user) var/obj/aura/aura = aura_type to_chat(user, SPAN_NOTICE("\The [item] grants \a [initial(aura.name)] to the wielder.")) diff --git a/code/modules/item_effects/item_effect_charges.dm b/code/modules/item_effects/item_effect_charges.dm index 4a27d3d0a27..06e8126b16f 100644 --- a/code/modules/item_effects/item_effect_charges.dm +++ b/code/modules/item_effects/item_effect_charges.dm @@ -3,14 +3,14 @@ abstract_type = /decl/item_effect/charges /decl/item_effect/charges/do_ranged_effect(mob/user, obj/item/item, atom/target, list/parameters) - var/charges = (LAZYACCESS(parameters, "charges") || 0) + var/charges = (LAZYACCESS(parameters, IE_PAR_USES) || 0) if(charges <= 0) return FALSE - item.set_item_effect_parameter(src, ITEM_EFFECT_RANGED, "charges", charges-1) + item.set_item_effect_parameter(src, IE_CAT_RANGED, IE_PAR_USES, charges-1) return TRUE -/decl/item_effect/charges/examined(obj/item/item, mob/user) - to_chat(user, SPAN_NOTICE("\The [item] has [item.get_item_effect_parameter(src, ITEM_EFFECT_RANGED, "charges") || 0] charge\s of [effect_descriptor] left.")) +/decl/item_effect/charges/on_examined(obj/item/item, mob/user) + to_chat(user, SPAN_NOTICE("\The [item] has [item.get_item_effect_parameter(src, IE_CAT_RANGED, IE_PAR_USES) || 0] charge\s of [effect_descriptor] left.")) /obj/item/projectile/fireball name = "fireball" diff --git a/code/modules/item_effects/item_effect_debug.dm b/code/modules/item_effects/item_effect_debug.dm index 352143ed57b..1533abda483 100644 --- a/code/modules/item_effects/item_effect_debug.dm +++ b/code/modules/item_effects/item_effect_debug.dm @@ -37,7 +37,7 @@ /decl/item_effect/debug/hear_speech(obj/item/item, mob/user, message, decl/language/speaking) log_debug("[type]: [item] heard [user] say [message] in [speaking] ([json_encode(args)])") -/decl/item_effect/debug/examined(obj/item/item, mob/user) +/decl/item_effect/debug/on_examined(obj/item/item, mob/user) log_debug("[type]: [user] examined [item] ([json_encode(args)])") /decl/item_effect/debug/do_process_effect(obj/item/item, list/parameters) @@ -46,21 +46,21 @@ /obj/item/sword/katana/debug/Initialize() . = ..() add_item_effect(/decl/item_effect/debug, list( - ITEM_EFFECT_VISUAL = list("vis" = "ual"), - ITEM_EFFECT_STRIKE = list("foo" = "bar"), - ITEM_EFFECT_PARRY = list("fizz" = "buzz"), - ITEM_EFFECT_USED = list("aard" = "vark"), - ITEM_EFFECT_VISIBLE = list("ooo" = "aaa"), - ITEM_EFFECT_LISTENER = list("walla walla" = "bing bong"), - ITEM_EFFECT_PROCESS = list("hyonk" = "hjonk") + (IE_CAT_VISUAL) = list("vis" = "ual"), + (IE_CAT_STRIKE) = list("foo" = "bar"), + (IE_CAT_PARRY) = list("fizz" = "buzz"), + (IE_CAT_USED) = list("aard" = "vark"), + (IE_CAT_EXAMINE) = list("ooo" = "aaa"), + (IE_CAT_LISTENER) = list("walla walla" = "bing bong"), + (IE_CAT_PROCESS) = list("hyonk" = "hjonk") )) add_item_effect(/decl/item_effect/charges/fireball, list( - ITEM_EFFECT_VISIBLE, - ITEM_EFFECT_RANGED = list("charges" = 5) + (IE_CAT_EXAMINE), + (IE_CAT_RANGED) = list(IE_PAR_USES = 5) )) add_item_effect(/decl/item_effect/aura/regeneration, list( - ITEM_EFFECT_VISIBLE, - ITEM_EFFECT_WIELDED + (IE_CAT_EXAMINE), + (IE_CAT_WIELDED) )) /obj/item/staff/crystal/beacon/fireball @@ -71,6 +71,6 @@ /obj/item/staff/crystal/beacon/fireball/Initialize(ml, material_key) . = ..() add_item_effect(/decl/item_effect/charges/fireball, list( - ITEM_EFFECT_VISIBLE, - ITEM_EFFECT_RANGED = list("charges" = 5) + (IE_CAT_EXAMINE), + (IE_CAT_RANGED) = list(IE_PAR_USES = 5) )) \ No newline at end of file diff --git a/code/modules/item_effects/item_effect_item.dm b/code/modules/item_effects/item_effect_item.dm index 22c57157bd4..ea1be1cfd24 100644 --- a/code/modules/item_effects/item_effect_item.dm +++ b/code/modules/item_effects/item_effect_item.dm @@ -1,6 +1,15 @@ /obj/item var/list/_item_effects +/obj/item/proc/has_item_effect(decl/item_effect/effect, effect_category) + if(!length(_item_effects)) + return FALSE + if(ispath(effect)) + effect = GET_DECL(effect) + if(!istype(effect)) + return FALSE + return LAZYISIN(LAZYACCESS(_item_effects, effect_category), effect) + /obj/item/proc/add_item_effect(effect_type, list/effect_parameters) if(!effect_type || !length(effect_parameters)) return FALSE @@ -13,13 +22,15 @@ . = TRUE if(.) - if(ITEM_EFFECT_LISTENER in effect_parameters) + if(IE_CAT_LISTENER in effect_parameters) global.listening_objects |= src - if(ITEM_EFFECT_PROCESS in effect_parameters) + if(IE_CAT_PROCESS in effect_parameters) SSitem_effects.queued_items |= src /obj/item/proc/remove_item_effect(decl/item_effect/effect) - if(!effect || !length(_item_effects)) + if(ispath(effect)) + effect = GET_DECL(effect) + if(!istype(effect) || !length(_item_effects)) return FALSE var/list/removed_effect_categories = list() for(var/effect_category in _item_effects) @@ -30,13 +41,23 @@ LAZYREMOVE(_item_effects, effect_category) . = TRUE if(.) - if(ITEM_EFFECT_LISTENER in removed_effect_categories) + if(IE_CAT_LISTENER in removed_effect_categories) global.listening_objects -= src - if(ITEM_EFFECT_PROCESS in removed_effect_categories) + if(IE_CAT_PROCESS in removed_effect_categories) SSitem_effects.queued_items -= src +/obj/item/proc/get_item_effect_parameters(decl/item_effect/effect, effect_category) + if(ispath(effect)) + effect = GET_DECL(effect) + if(!istype(effect) || !length(_item_effects) || !effect_category) + return null + var/list/effects = LAZYACCESS(_item_effects, effect_category) + return LAZYACCESS(effects, effect) + /obj/item/proc/get_item_effect_parameter(decl/item_effect/effect, effect_category, parameter_name) - if(!effect || !length(_item_effects) || !effect_category || !parameter_name) + if(ispath(effect)) + effect = GET_DECL(effect) + if(!istype(effect) || !length(_item_effects) || !effect_category || !parameter_name) return null var/list/effects = LAZYACCESS(_item_effects, effect_category) if(!LAZYISIN(effects, effect)) @@ -44,7 +65,9 @@ return LAZYACCESS(effects[effect], parameter_name) /obj/item/proc/set_item_effect_parameter(decl/item_effect/effect, effect_category, parameter_name, parameter_value) - if(!effect || !length(_item_effects) || !effect_category || !parameter_name) + if(ispath(effect)) + effect = GET_DECL(effect) + if(!istype(effect) || !length(_item_effects) || !effect_category || !parameter_name) return FALSE var/list/effects = LAZYACCESS(_item_effects, effect_category) if(!LAZYISIN(effects, effect)) @@ -59,7 +82,7 @@ /obj/item/resolve_attackby(atom/A, mob/user, var/click_params) if(!(. = ..())) return - var/list/item_effects = get_item_effects(ITEM_EFFECT_STRIKE) + var/list/item_effects = get_item_effects(IE_CAT_STRIKE) if(!length(item_effects)) return if(!istype(user) || QDELETED(user) || QDELETED(src)) @@ -72,7 +95,7 @@ // PARRY effects /obj/item/on_parry(mob/user, damage_source, mob/attacker) . = ..() - var/list/item_effects = get_item_effects(ITEM_EFFECT_PARRY) + var/list/item_effects = get_item_effects(IE_CAT_PARRY) if(!length(item_effects)) return if(!istype(user) || QDELETED(user) || QDELETED(src)) @@ -86,7 +109,7 @@ // VISUAL effects (world icon) /obj/item/on_update_icon() . = ..() - var/list/item_effects = get_item_effects(ITEM_EFFECT_VISUAL) + var/list/item_effects = get_item_effects(IE_CAT_VISUAL) if(!length(item_effects)) return for(var/decl/item_effect/used_effect as anything in item_effects) @@ -96,7 +119,7 @@ /obj/item/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) // TODO: this might need work to handle items that do a state or appearance update in the parent call. if(overlay) - var/list/item_effects = get_item_effects(ITEM_EFFECT_VISUAL) + var/list/item_effects = get_item_effects(IE_CAT_VISUAL) if(length(item_effects)) for(var/decl/item_effect/used_effect as anything in item_effects) used_effect.apply_onmob_appearance_to(src, user_mob, bodytype, overlay, slot, bodypart, item_effects[used_effect]) @@ -106,7 +129,7 @@ /obj/item/attack_self(mob/user) if((. = ..())) return - var/list/item_effects = get_item_effects(ITEM_EFFECT_USED) + var/list/item_effects = get_item_effects(IE_CAT_USED) if(!length(item_effects)) return if(!istype(user) || QDELETED(user) || QDELETED(src)) @@ -120,7 +143,7 @@ // WIELD effects (unwielded) /obj/item/dropped(mob/user) . = ..() - var/list/item_effects = get_item_effects(ITEM_EFFECT_WIELDED) + var/list/item_effects = get_item_effects(IE_CAT_WIELDED) if(!length(item_effects)) return if(!istype(user) || QDELETED(user) || QDELETED(src)) @@ -133,7 +156,7 @@ // WIELD effects (wielded) /obj/item/equipped(mob/user, slot) . = ..() - var/list/item_effects = get_item_effects(ITEM_EFFECT_WIELDED) + var/list/item_effects = get_item_effects(IE_CAT_WIELDED) if(!length(item_effects)) return if(!istype(user) || QDELETED(user) || QDELETED(src) || loc != user || !(slot in user.get_held_item_slots())) @@ -146,7 +169,7 @@ // LISTENING effects /obj/item/hear_talk(mob/M, text, verb, decl/language/speaking) . = ..() - var/list/item_effects = get_item_effects(ITEM_EFFECT_LISTENER) + var/list/item_effects = get_item_effects(IE_CAT_LISTENER) if(!length(item_effects)) return for(var/decl/item_effect/listening_effect as anything in item_effects) @@ -157,17 +180,17 @@ . = ..() if(!user) return - var/list/item_effects = get_item_effects(ITEM_EFFECT_VISIBLE) + var/list/item_effects = get_item_effects(IE_CAT_EXAMINE) if(!length(item_effects)) return for(var/decl/item_effect/examine_effect as anything in item_effects) - examine_effect.examined(src, user, distance, item_effects[examine_effect]) + examine_effect.on_examined(src, user, distance, item_effects[examine_effect]) // RANGED effects /obj/item/afterattack(turf/floor/target, mob/user, proximity) if((. = ..()) || proximity) return - var/list/item_effects = get_item_effects(ITEM_EFFECT_RANGED) + var/list/item_effects = get_item_effects(IE_CAT_RANGED) if(!length(item_effects)) return for(var/decl/item_effect/ranged_effect as anything in item_effects) @@ -177,7 +200,7 @@ // PROCESS effects /obj/item/proc/process_item_effects() - var/list/item_effects = get_item_effects(ITEM_EFFECT_PROCESS) + var/list/item_effects = get_item_effects(IE_CAT_PROCESS) if(length(item_effects)) for(var/decl/item_effect/process_effect as anything in item_effects) process_effect.do_process_effect(src, item_effects[process_effect]) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 687dcc2a49d..b1e8d111ed7 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -237,6 +237,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, update_overlays(TRUE) /datum/lighting_corner/proc/update_ambient_lumcount(delta_r, delta_g, delta_b, skip_update = FALSE) + ambient_r += delta_r ambient_g += delta_g ambient_b += delta_b diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index d4008f536a2..53e59fa5b9b 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -49,9 +49,9 @@ var/ambient_b = 0 if (ambient_light) - ambient_r = ((HEX_RED(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_r - ambient_g = ((HEX_GREEN(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_g - ambient_b = ((HEX_BLUE(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_b + ambient_r = round(((HEX_RED(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_r, LIGHTING_ROUND_VALUE) + ambient_g = round(((HEX_GREEN(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_g, LIGHTING_ROUND_VALUE) + ambient_b = round(((HEX_BLUE(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_b, LIGHTING_ROUND_VALUE) else ambient_r = -ambient_light_old_r ambient_g = -ambient_light_old_g @@ -61,31 +61,21 @@ ambient_light_old_g += ambient_g ambient_light_old_b += ambient_b - if (ambient_r + ambient_g + ambient_b == 0) + if (abs(ambient_r + ambient_g + ambient_b) == 0) return // Unlit turfs will have corners if they have a lit neighbor -- don't generate corners for them, but do update them if they're there. - // if (!corners) - // var/force_build_corners = FALSE - // for (var/turf/T as anything in RANGE_TURFS(src, 1)) - // if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) - // force_build_corners = TRUE - // break - - // if (force_build_corners || TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) - // generate_missing_corners() - // else - // return - - // still inefficient :( - if(!corners || !lighting_corners_initialised) - /* Commented out pending working out why this doesn't work properly on Neb. - if(TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) + if (!corners) + var/force_build_corners = FALSE + for (var/turf/T as anything in RANGE_TURFS(src, 1)) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + force_build_corners = TRUE + break + + if (force_build_corners || TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) generate_missing_corners() else return - */ - generate_missing_corners() // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. for (var/datum/lighting_corner/C in corners) @@ -126,8 +116,7 @@ // Builds a lighting overlay for us, but only if our area is dynamic. /turf/proc/lighting_build_overlay(now = FALSE) if (lighting_overlay) - return // shrug - // CRASH("Attempted to create lighting_overlay on tile that already had one.") + CRASH("Attempted to create lighting_overlay on tile that already had one.") if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) if (!lighting_corners_initialised || !corners) diff --git a/code/modules/maps/_map_template.dm b/code/modules/maps/_map_template.dm index a48c199cccc..f7a22eaf01f 100644 --- a/code/modules/maps/_map_template.dm +++ b/code/modules/maps/_map_template.dm @@ -1,4 +1,5 @@ /datum/map_template + abstract_type = /datum/map_template ///Name for differentiating templates var/name = "Default Template Name" ///The width of the template's levels. Size is preloaded from template during template registration. @@ -14,7 +15,7 @@ ///Shuttles in this template's levels that need to be initialized with SSshuttle. var/list/shuttles_to_initialise = list() ///Sub-templates to spawn on this template if any. Ruins and sites and etc.. - var/list/subtemplates_to_spawn + var/list/subtemplates_to_spawn = list() ///Percent of chances to end up onto a level from this template by spacewalking between space z-levels. var/accessibility_weight = 0 ///Flags for defining special properties of this template. @@ -23,8 +24,6 @@ var/modify_tag_vars = TRUE ///List of strings to store the templates under for mass retrieval. var/list/template_categories - ///If this is equal to current type, the datum is abstract and should not be created. - var/template_parent_type = /datum/map_template ///The initial type of level_data to instantiate new z-level with initially. (Is replaced by whatever is in the map file.) If null, will use default. var/level_data_type /// Various tags used for selecting templates for placement on a map. @@ -67,45 +66,22 @@ if (SSatoms.atom_init_stage == INITIALIZATION_INSSATOMS) return // let proper initialisation handle it later - var/list/turf/turfs = list() - var/list/obj/machinery/atmospherics/atmos_machines = list() - var/list/obj/machinery/machines = list() - var/list/obj/structure/cable/cables = list() - - for(var/atom/A in atoms) - if(isturf(A)) - turfs += A - if(istype(A, /obj/structure/cable)) - cables += A - if(istype(A, /obj/machinery/atmospherics)) - atmos_machines += A - if(istype(A, /obj/machinery)) - machines += A - if(istype(A, /obj/abstract/landmark/map_load_mark)) - LAZYADD(subtemplates_to_spawn, A) - - var/notsuspended - if(!SSmachines.suspended) - SSmachines.suspend() - notsuspended = TRUE - SSatoms.InitializeAtoms() // The atoms should have been getting queued there. This flushes the queue. - SSmachines.setup_powernets_for_cables(cables) - SSmachines.setup_atmos_machinery(atmos_machines) - if(notsuspended) - SSmachines.wake() + for(var/obj/abstract/landmark/map_load_mark/landmark in atoms) + subtemplates_to_spawn += landmark - for (var/obj/machinery/machine as anything in machines) - machine.power_change() + // fun fact: these already filter for us, so it's pointless to sort + SSmachines.setup_powernets_for_cables(atoms) + SSmachines.setup_atmos_machinery(atoms) - for (var/turf/T as anything in turfs) + for (var/turf/T in atoms) if(template_flags & TEMPLATE_FLAG_NO_RUINS) T.turf_flags |= TURF_FLAG_NO_POINTS_OF_INTEREST if(template_flags & TEMPLATE_FLAG_NO_RADS) qdel(SSradiation.sources_assoc[T]) - if(istype(T) && T.simulated) - T.update_air_properties() + if(T.simulated) + SSair.mark_for_update(T) /datum/map_template/proc/pre_init_shuttles() . = SSshuttle.block_queue @@ -185,8 +161,6 @@ for(var/z_index = bounds[MAP_MINZ] to bounds[MAP_MAXZ]) var/datum/level_data/level = SSmapping.levels_by_z[z_index] level.after_template_load(src) - if(SSlighting.initialized) - SSlighting.InitializeZlev(z_index) Master.StopLoadingMap() log_game("Z-level [name] loaded at [x],[y],[world.maxz]") loaded++ @@ -225,11 +199,9 @@ init_atoms(atoms_to_initialise) init_shuttles(shuttle_state, map_hash, initialized_areas_by_type) after_load() - if (SSlighting.initialized) - SSlighting.InitializeTurfs(atoms_to_initialise) // Hopefully no turfs get placed on new coords by SSatoms. Master.StopLoadingMap() - log_game("[name] loaded at at [T.x],[T.y],[T.z]") + log_game("[name] loaded at [T.x],[T.y],[T.z]") loaded++ return TRUE @@ -238,7 +210,7 @@ for(var/obj/abstract/landmark/map_load_mark/mark as anything in subtemplates_to_spawn) subtemplates_to_spawn -= mark mark.load_subtemplate() - if(!QDELETED(mark)) + if(!QDELETED(mark)) // for if the tile that lands on the landmark is a no-op tile qdel(mark) subtemplates_to_spawn = null generate_multi_spawn_items() diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm index efc444d7b0a..71bedba3639 100644 --- a/code/modules/maps/reader.dm +++ b/code/modules/maps/reader.dm @@ -12,18 +12,32 @@ var/global/dmm_suite/preloader/_preloader = new var/list/atoms_to_initialise /dmm_suite - // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g - var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g") + // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"\n*([a-zA-Z\n]*)\n?"\}/g + var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"\n*(\[a-zA-Z\n]*)\n?"\\}"}, "g") // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g") // /^[\s\n]+|[\s\n]+$/ var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g") var/static/list/modelCache = list() var/static/space_key + var/static/list/types_to_delete #ifdef TESTING var/static/turfsSkipped #endif +// Set up some basic cached vars, like types_to_delete, that have to check things like subtypes. +/dmm_suite/New() + if(!types_to_delete) + types_to_delete = typecacheof(list( + /mob/living, + /obj/effect, + /obj/item, + /obj/machinery, + /obj/structure, + /obj/abstract/landmark/exoplanet_spawn, + )) + ..() + /** * Construct the model map and control the loading process * @@ -124,20 +138,11 @@ var/global/dmm_suite/preloader/_preloader = new bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd) var/list/gridLines = splittext(dmmRegex.group[6], "\n") - - var/leadingBlanks = 0 - while(leadingBlanks < gridLines.len && gridLines[++leadingBlanks] == "") - if(leadingBlanks > 1) - gridLines.Cut(1, leadingBlanks) // Remove all leading blank lines. - - if(!gridLines.len) // Skip it if only blank lines exist. + if(!length(gridLines)) // Skip it if only blank lines exist. continue - if(gridLines.len && gridLines[gridLines.len] == "") - gridLines.Cut(gridLines.len) // Remove only one blank line at the end. - bounds[MAP_MINY] = min(bounds[MAP_MINY], clamp(ycrd, y_lower, y_upper)) - ycrd += gridLines.len - 1 // Start at the top and work down + ycrd += length(gridLines) - 1 // Start at the top and work down if(!cropMap && ycrd > world.maxy) if(!measureOnly) @@ -185,8 +190,6 @@ var/global/dmm_suite/preloader/_preloader = new maxx = max(maxx, xcrd) ++xcrd --ycrd - if (zexpansion && SSlighting.initialized) - SSlighting.InitializeZlev(zcrd) bounds[MAP_MAXX] = clamp(max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx), x_lower, x_upper) @@ -226,16 +229,6 @@ var/global/dmm_suite/preloader/_preloader = new var/list/atoms_to_initialise var/list/atoms_to_delete -/dmm_suite/proc/types_to_delete() - return list( - /mob/living, - /obj/effect, - /obj/item, - /obj/machinery, - /obj/structure, - /obj/abstract/landmark/exoplanet_spawn, - ) - /dmm_suite/proc/parse_grid(model as text, model_key as text, xcrd as num,ycrd as num,zcrd as num, no_changeturf as num, clear_contents as num, initialized_areas_by_type) /*Method parse_grid() - Accepts a text string containing a comma separated list of type paths of the @@ -288,8 +281,8 @@ var/global/dmm_suite/preloader/_preloader = new if(variables_start)//if there's any variable full_def = copytext(full_def,variables_start+1,length(full_def))//removing the last '}' fields = readlist(full_def, ";") - if(fields.len) - if(!trim(fields[fields.len])) + if(length(fields)) + if(!trim(fields[length(fields)])) --fields.len for(var/I in fields) var/value = fields[I] @@ -312,7 +305,7 @@ var/global/dmm_suite/preloader/_preloader = new // 5. and the members are world.turf and world.area // Basically, if we find an entry like this: "XXX" = (/turf/default, /area/default) // We can skip calling this proc every time we see XXX - if(no_changeturf && !space_key && members.len == 2 && members_attributes.len == 2 && length(members_attributes[1]) == 0 && length(members_attributes[2]) == 0 && (world.area in members) && (world.turf in members)) + if(no_changeturf && !space_key && length(members) == 2 && length(members_attributes) == 2 && length(members_attributes[1]) == 0 && length(members_attributes[2]) == 0 && (world.area in members) && (world.turf in members)) space_key = model_key return @@ -329,7 +322,7 @@ var/global/dmm_suite/preloader/_preloader = new var/atoms_to_delete = list() //first instance the /area and remove it from the members list - index = members.len + index = length(members) if(members[index] != /area/template_noop) is_not_noop = TRUE var/list/attr = members_attributes[index] @@ -363,20 +356,20 @@ var/global/dmm_suite/preloader/_preloader = new if(T) //if others /turf are presents, simulates the underlays piling effect index = first_turf_index + 1 - while(index <= members.len - 1) // Last item is an /area + while(index < length(members)) // Last item is an /area var/underlay = T.appearance T = instance_atom(members[index],members_attributes[index],crds,no_changeturf)//instance new turf T.underlays += underlay index++ atoms_to_initialise += T - if (clear_contents && is_not_noop) - for (var/type_to_delete in types_to_delete()) - for (var/atom/pre_existing in crds) - if(crds != pre_existing.loc) // avoid deleting multitile objects unnecessarily, only check their 'real' loc - continue - if (istype(pre_existing, type_to_delete)) - atoms_to_delete |= pre_existing + if (clear_contents && is_not_noop && length(crds.contents)) + for (var/atom/movable/pre_existing as anything in crds) + if(!types_to_delete[pre_existing.type]) + continue + if(crds != pre_existing.loc) // avoid deleting multitile objects unnecessarily, only check their 'real' loc + continue + atoms_to_delete += pre_existing //finally instance all remainings objects/mobs for(index in 1 to first_turf_index-1) diff --git a/code/modules/maps/template_types/antag_spawn.dm b/code/modules/maps/template_types/antag_spawn.dm index 7c079ead9eb..8b8b167bd0f 100644 --- a/code/modules/maps/template_types/antag_spawn.dm +++ b/code/modules/maps/template_types/antag_spawn.dm @@ -1,3 +1,3 @@ /datum/map_template/ruin/antag_spawn prefix = "maps/antag_spawn/" - template_parent_type = /datum/map_template/ruin/antag_spawn + abstract_type = /datum/map_template/ruin/antag_spawn diff --git a/code/modules/maps/template_types/away_site.dm b/code/modules/maps/template_types/away_site.dm index b9adb30914b..81d77b0152e 100644 --- a/code/modules/maps/template_types/away_site.dm +++ b/code/modules/maps/template_types/away_site.dm @@ -1,7 +1,7 @@ /datum/map_template/ruin/away_site prefix = "maps/away/" template_categories = list(MAP_TEMPLATE_CATEGORY_AWAYSITE) - template_parent_type = /datum/map_template/ruin/away_site + abstract_type = /datum/map_template/ruin/away_site var/spawn_weight = 1 /datum/map_template/ruin/away_site/get_spawn_weight() diff --git a/code/modules/maps/template_types/mapped_planet/mapped_planet_template.dm b/code/modules/maps/template_types/mapped_planet/mapped_planet_template.dm index 45396ae3baf..4a7f663352e 100644 --- a/code/modules/maps/template_types/mapped_planet/mapped_planet_template.dm +++ b/code/modules/maps/template_types/mapped_planet/mapped_planet_template.dm @@ -6,7 +6,6 @@ /datum/map_template/planetoid name = "planetoid" abstract_type = /datum/map_template/planetoid - template_parent_type = /datum/map_template/planetoid template_categories = list(MAP_TEMPLATE_CATEGORY_PLANET) level_data_type = /datum/level_data/planetoid modify_tag_vars = TRUE diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm index 7ef37a05e79..6de9ef4a600 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm @@ -13,7 +13,7 @@ /datum/level_data/planetoid/exoplanet/barren base_area = /area/exoplanet/barren - base_turf = /turf/floor + base_turf = /turf/floor/barren exterior_atmosphere = null //Generate me exterior_atmos_temp = null //Generate me level_generators = list( @@ -76,7 +76,6 @@ overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/barren template_tags_blacklist = TEMPLATE_TAG_HABITAT|TEMPLATE_TAG_WATER subtemplate_budget = 6 - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/barren prefered_level_data_per_z = list( /datum/level_data/planetoid/exoplanet/barren, //surface level @@ -99,7 +98,7 @@ ///Generator for fauna and flora spawners for the surface of the barren exoplanet /datum/random_map/noise/exoplanet/barren descriptor = "barren exoplanet" - land_type = /turf/floor + land_type = /turf/floor/barren flora_prob = 0.1 large_flora_prob = 0 fauna_prob = 0 @@ -111,7 +110,7 @@ /area/exoplanet/barren name = "\improper Planetary surface" - base_turf = /turf/floor + base_turf = /turf/floor/barren is_outside = OUTSIDE_YES ambience = list( 'sound/effects/wind/wind_2_1.ogg', diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/chlorine.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/chlorine.dm index c45df0faf7c..9ec7a882388 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/chlorine.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/chlorine.dm @@ -87,7 +87,6 @@ planetoid_data_type = /datum/planetoid_data/random/chlorine overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/chlorine template_tags_blacklist = TEMPLATE_TAG_HABITAT|TEMPLATE_TAG_WATER - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/chlorine prefered_level_data_per_z = list( /datum/level_data/planetoid/exoplanet/chlorine, diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/desert.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/desert.dm index 93053b6b88e..098a604152c 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/desert.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/desert.dm @@ -95,7 +95,6 @@ name = "desert exoplanet" planetoid_data_type = /datum/planetoid_data/random/desert overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/desert - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/desert prefered_level_data_per_z = list( /datum/level_data/planetoid/exoplanet/desert, diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm index f3bc7a90522..d6ecd767659 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm @@ -119,7 +119,6 @@ name = "lush exoplanet" planetoid_data_type = /datum/planetoid_data/random/grass overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/grass - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/grass prefered_level_data_per_z = list( /datum/level_data/planetoid/exoplanet/grass, diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm index 90aaf8dad69..95ad399b57d 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/meat.dm @@ -91,7 +91,6 @@ planetoid_data_type = /datum/planetoid_data/random/meat overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/meat template_tags_blacklist = TEMPLATE_TAG_HABITAT|TEMPLATE_TAG_HUMAN|TEMPLATE_TAG_WATER - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/meat prefered_level_data_per_z = null diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/shrouded.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/shrouded.dm index 07b282eb4da..5b201f4509d 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/shrouded.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/shrouded.dm @@ -86,7 +86,6 @@ planetoid_data_type = /datum/planetoid_data/random/shrouded overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/shrouded template_tags_blacklist = TEMPLATE_TAG_HABITAT - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/shrouded prefered_level_data_per_z = list( /datum/level_data/planetoid/exoplanet/shrouded, diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/snow.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/snow.dm index 6926e0d004d..567c152b758 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/snow.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/snow.dm @@ -77,7 +77,6 @@ name = "snow exoplanet" planetoid_data_type = /datum/planetoid_data/random/snow overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/snow - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/snow prefered_level_data_per_z = list( /datum/level_data/planetoid/exoplanet/snow, diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/volcanic.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/volcanic.dm index a6b669411bb..fc24c4a289f 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/volcanic.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/volcanic.dm @@ -92,7 +92,6 @@ overmap_marker_type = /obj/effect/overmap/visitable/sector/planetoid/exoplanet/volcanic max_themes = 1 template_tags_blacklist = TEMPLATE_TAG_HABITAT|TEMPLATE_TAG_WATER - template_parent_type = /datum/map_template/planetoid/random/exoplanet level_data_type = /datum/level_data/planetoid/exoplanet/volcanic prefered_level_data_per_z = list( /datum/level_data/planetoid/exoplanet/volcanic, diff --git a/code/modules/maps/template_types/random_exoplanet/random_exoplanet.dm b/code/modules/maps/template_types/random_exoplanet/random_exoplanet.dm index 8eb2764c4c1..3cdd8d4a0f3 100644 --- a/code/modules/maps/template_types/random_exoplanet/random_exoplanet.dm +++ b/code/modules/maps/template_types/random_exoplanet/random_exoplanet.dm @@ -2,7 +2,6 @@ /datum/map_template/planetoid/random/exoplanet name = "random exoplanet" abstract_type = /datum/map_template/planetoid/random/exoplanet - template_parent_type = /datum/map_template/planetoid/random/exoplanet template_categories = list(MAP_TEMPLATE_CATEGORY_EXOPLANET) template_category = MAP_TEMPLATE_CATEGORY_EXOPLANET_SITE tallness = 1 diff --git a/code/modules/maps/template_types/random_exoplanet/random_planet.dm b/code/modules/maps/template_types/random_exoplanet/random_planet.dm index cc846e98ae2..d19a9ee7625 100644 --- a/code/modules/maps/template_types/random_exoplanet/random_planet.dm +++ b/code/modules/maps/template_types/random_exoplanet/random_planet.dm @@ -7,7 +7,6 @@ /datum/map_template/planetoid/random name = "random planetoid" abstract_type = /datum/map_template/planetoid/random - template_parent_type = /datum/map_template/planetoid/random modify_tag_vars = TRUE //Would set it to false, since we're generating everything on the fly, but unit test doesn't like it tallness = 1 //Amount of vertical z-levels to generate for this planet. @@ -203,9 +202,9 @@ //Run the finishing touch on all loaded levels for(var/datum/level_data/LD in new_level_data) - LD.after_template_load(src) - if(SSlighting.initialized) - SSlighting.InitializeZlev(LD.level_z) + //This is done in parent if we have mappaths, so skip it unless we don't have any + if(!length(mappaths)) + LD.after_template_load(src) log_game("Z-level '[LD.name]'(planetoid:'[name]') loaded at [LD.level_z]") loaded++ return WORLD_CENTER_TURF(world.maxz) diff --git a/code/modules/maps/template_types/ruins.dm b/code/modules/maps/template_types/ruins.dm index 5c5782ac0a3..9e26f2c6212 100644 --- a/code/modules/maps/template_types/ruins.dm +++ b/code/modules/maps/template_types/ruins.dm @@ -1,7 +1,7 @@ /datum/map_template/ruin name = null template_flags = 0 // No duplicates by default - template_parent_type = /datum/map_template/ruin + abstract_type = /datum/map_template/ruin var/description var/cost = 0 var/prefix diff --git a/code/modules/maps/template_types/ruins_exoplanet.dm b/code/modules/maps/template_types/ruins_exoplanet.dm index a0a34f76975..d6c405132af 100644 --- a/code/modules/maps/template_types/ruins_exoplanet.dm +++ b/code/modules/maps/template_types/ruins_exoplanet.dm @@ -1,4 +1,4 @@ /datum/map_template/ruin/exoplanet prefix = "maps/random_ruins/exoplanet_ruins/" template_categories = list(MAP_TEMPLATE_CATEGORY_EXOPLANET_SITE) - template_parent_type = /datum/map_template/ruin/exoplanet + abstract_type = /datum/map_template/ruin/exoplanet diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 6ea17dcdf2d..6617c5c2a1e 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -112,7 +112,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) var/toxicity = 0 // Organ damage from ingestion. var/toxicity_targets_organ // Bypass liver/kidneys when ingested, harm this organ directly (using BP_FOO defines). - var/can_backfill_turf_type + var/can_backfill_floor_type // Shards/tables/structures var/shard_type = SHARD_SHRAPNEL // Path of debris object. @@ -374,7 +374,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) // Placeholders for light tiles and rglass. /decl/material/proc/reinforce(var/mob/user, var/obj/item/stack/material/used_stack, var/obj/item/stack/material/target_stack, var/use_sheets = 1) if(!used_stack.can_use(use_sheets)) - to_chat(user, SPAN_WARNING("You need need at least one [used_stack.singular_name] to reinforce [target_stack].")) + to_chat(user, SPAN_WARNING("You need at least one [used_stack.singular_name] to reinforce [target_stack].")) return var/decl/material/reinf_mat = used_stack.get_material() @@ -383,7 +383,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) return if(!target_stack.can_use(use_sheets)) - to_chat(user, SPAN_WARNING("You need need at least [use_sheets] [use_sheets == 1 ? target_stack.singular_name : target_stack.plural_name] for reinforcement with \the [used_stack].")) + to_chat(user, SPAN_WARNING("You need at least [use_sheets] [use_sheets == 1 ? target_stack.singular_name : target_stack.plural_name] for reinforcement with \the [used_stack].")) return to_chat(user, SPAN_NOTICE("You reinforce the [target_stack] with [reinf_mat.solid_name].")) @@ -718,12 +718,12 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /decl/material/proc/touch_obj(var/obj/O, var/amount, var/datum/reagents/holder) // Acid melting, cleaner cleaning, etc - if(solvent_power >= MAT_SOLVENT_MILD) - if(istype(O, /obj/item/paper)) + if(solvent_power >= MAT_SOLVENT_MODERATE) + if(istype(O, /obj/item/paper) && amount >= FLUID_MINIMUM_TRANSFER) var/obj/item/paper/paperaffected = O paperaffected.clearpaper() O.visible_message(SPAN_NOTICE("The solution dissolves the ink on the paper."), range = 1) - else if(istype(O, /obj/item/book) && amount >= 5) + else if(istype(O, /obj/item/book) && amount >= FLUID_PUDDLE) var/obj/item/book/affectedbook = O if(affectedbook.can_dissolve_text) affectedbook.dat = null @@ -756,28 +756,27 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) M.adjust_fire_intensity(floor((amount * accelerant_value)/FLAMMABLE_LIQUID_DIVISOR)) #undef FLAMMABLE_LIQUID_DIVISOR -/decl/material/proc/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) // Cleaner cleaning, lube lubbing, etc, all go here +/decl/material/proc/touch_turf(var/turf/touching_turf, var/amount, var/datum/reagents/holder) // Cleaner cleaning, lube lubbing, etc, all go here if(REAGENT_VOLUME(holder, type) < turf_touch_threshold) return - if(istype(T) && T.simulated) - var/turf/wall/W = T + if(istype(touching_turf) && touching_turf.simulated) if(defoliant) - for(var/obj/effect/overlay/wallrot/E in W) - W.visible_message(SPAN_NOTICE("\The [E] is completely dissolved by the solution!")) - qdel(E) - if(slipperiness != 0 && !T.check_fluid_depth()) // Don't make floors slippery if they have an active fluid on top of them please. + for(var/obj/effect/overlay/wallrot/rot in touching_turf) + touching_turf.visible_message(SPAN_NOTICE("\The [rot] is completely dissolved by the solution!")) + qdel(rot) + if(slipperiness != 0 && !touching_turf.check_fluid_depth()) // Don't make floors slippery if they have an active fluid on top of them please. if(slipperiness < 0) - W.unwet_floor(TRUE) + touching_turf.unwet_floor(TRUE) else if (REAGENT_VOLUME(holder, type) >= slippery_amount) - W.wet_floor(slipperiness) + touching_turf.wet_floor(slipperiness) if(length(vapor_products)) var/volume = REAGENT_VOLUME(holder, type) var/temperature = holder?.my_atom?.temperature || T20C for(var/vapor in vapor_products) - T.assume_gas(vapor, (volume * vapor_products[vapor]), temperature) + touching_turf.assume_gas(vapor, (volume * vapor_products[vapor]), temperature) holder.remove_reagent(type, volume) /decl/material/proc/on_mob_life(var/mob/living/M, var/metabolism_class, var/datum/reagents/holder, var/list/life_dose_tracker) @@ -1012,9 +1011,9 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) holder.remove_reagent(type, REAGENT_VOLUME(holder, type)) . = TRUE -/decl/material/proc/affect_overdose(mob/living/M, total_dose) // Overdose effect. Doesn't happen instantly. - M.add_chemical_effect(CE_TOXIN, 1) - M.take_damage(REM, TOX) +/decl/material/proc/affect_overdose(mob/living/victim, total_dose) // Overdose effect. Doesn't happen instantly. + victim.add_chemical_effect(CE_TOXIN, 1) + victim.take_damage(REM, TOX) /decl/material/proc/initialize_data(list/newdata) // Called when the reagent is first added to a reagents datum. . = newdata @@ -1200,3 +1199,19 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /decl/material/proc/can_hold_edge() return hardness > MAT_VALUE_FLEXIBLE + +// TODO: expand this to more than just Actual Poison. +/decl/material/proc/is_unsafe_to_drink(mob/user) + return toxicity > 0 + +/// Used for material-dependent effects on stain dry. +/// Return TRUE to skip default drying handling. +/decl/material/proc/handle_stain_dry(obj/effect/decal/cleanable/blood/stain) + return FALSE + +/// Returns (in deciseconds) how long until dry() will be called on this stain, +/// or null to use the stain's default. +/// If 0 is returned, it dries instantly. +/// If any value below 0 is returned, it doesn't start processing. +/decl/material/proc/get_time_to_dry_stain(obj/effect/decal/cleanable/blood/stain) + return initial(stain.time_to_dry) diff --git a/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm b/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm index ae855b451f1..bb527d7123d 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm @@ -25,3 +25,7 @@ value = 0.1 slipperiness = 80 exoplanet_rarity_gas = MAT_RARITY_EXOTIC + +// Prevent oil stains from drying. +/decl/material/liquid/lube/get_time_to_dry_stain(obj/effect/decal/cleanable/blood/stain) + return -1 \ No newline at end of file diff --git a/code/modules/materials/definitions/liquids/materials_liquid_mundane.dm b/code/modules/materials/definitions/liquids/materials_liquid_mundane.dm new file mode 100644 index 00000000000..97cfe6b65f9 --- /dev/null +++ b/code/modules/materials/definitions/liquids/materials_liquid_mundane.dm @@ -0,0 +1,12 @@ +/decl/material/liquid/mucus + name = "mucus" + uid = "chem_mucus" + lore_text = "A gooey semi-liquid produced by a wide variety of organisms. In some, it's associated with disease and illness." + taste_description = "slime" + color = COLOR_LIQUID_WATER + opacity = 0.5 + exoplanet_rarity_gas = MAT_RARITY_NOWHERE + +/decl/material/liquid/mucus/handle_stain_dry(obj/effect/decal/cleanable/blood/stain) + qdel(stain) + return TRUE // skip blood handling \ No newline at end of file diff --git a/code/modules/materials/definitions/liquids/materials_liquid_solvents.dm b/code/modules/materials/definitions/liquids/materials_liquid_solvents.dm index bbfa9096bfe..ec78c02b460 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_solvents.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_solvents.dm @@ -1,6 +1,6 @@ /decl/material/liquid/acid - name = "sulphuric acid" - uid = "liquid_sulphuric_acid" + name = "sulfuric acid" + uid = "liquid_sulfuric_acid" lore_text = "A very corrosive mineral acid with the molecular formula H2SO4." taste_description = "acid" color = "#db5008" diff --git a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm index 6b76cad038a..3b42ee375e4 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm @@ -128,16 +128,14 @@ ..() ADJ_STATUS(M, STAT_CONFUSE, 1.5) -/decl/material/liquid/heartstopper/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/heartstopper/affect_overdose(mob/living/victim, total_dose) ..() - if(ishuman(M)) - var/mob/living/human/H = M - if(H.stat != UNCONSCIOUS) - if(H.ticks_since_last_successful_breath >= 10) - H.ticks_since_last_successful_breath = max(10, H.ticks_since_last_successful_breath-10) - H.take_damage(2, OXY) - SET_STATUS_MAX(H, STAT_WEAK, 10) - M.add_chemical_effect(CE_NOPULSE, 1) + if(victim.stat != UNCONSCIOUS) + if(victim.ticks_since_last_successful_breath >= 10) + victim.ticks_since_last_successful_breath = max(10, victim.ticks_since_last_successful_breath-10) + victim.take_damage(2, OXY) + SET_STATUS_MAX(victim, STAT_WEAK, 10) + victim.add_chemical_effect(CE_NOPULSE, 1) /decl/material/liquid/zombiepowder name = "zombie powder" diff --git a/code/modules/materials/definitions/liquids/materials_liquid_water.dm b/code/modules/materials/definitions/liquids/materials_liquid_water.dm index e9ca35c6c78..efb84cdf8e7 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_water.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_water.dm @@ -48,22 +48,23 @@ affect_blood(M, removed, holder) #define WATER_LATENT_HEAT 9500 // How much heat is removed when applied to a hot turf, in J/unit (9500 makes 120 u of water roughly equivalent to 2L -/decl/material/liquid/water/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) +/decl/material/liquid/water/touch_turf(var/turf/touching_turf, var/amount, var/datum/reagents/holder) ..() - if(!istype(T)) + if(!istype(touching_turf)) return - var/datum/gas_mixture/environment = T.return_air() + var/datum/gas_mixture/environment = touching_turf.return_air() var/min_temperature = T20C + rand(0, 20) // Room temperature + some variance. An actual diminishing return would be better, but this is *like* that. In a way. . This has the potential for weird behavior, but I says fuck it. Water grenades for everyone. - var/hotspot = (locate(/obj/fire) in T) - if(hotspot && !isspaceturf(T)) - var/datum/gas_mixture/lowertemp = T.remove_air(T:air:total_moles) + // TODO: Cannot for the life of me work out what this is doing or why it's reducing the air temp by 2000; shouldn't it just be using environment? + var/hotspot = (locate(/obj/fire) in touching_turf) + if(hotspot && !isspaceturf(touching_turf)) + var/datum/gas_mixture/lowertemp = touching_turf.remove_air(touching_turf:air:total_moles) lowertemp.temperature = max(min(lowertemp.temperature-2000, lowertemp.temperature / 2), 0) lowertemp.react() - T.assume_air(lowertemp) + touching_turf.assume_air(lowertemp) qdel(hotspot) var/volume = REAGENT_VOLUME(holder, type) @@ -71,11 +72,11 @@ var/removed_heat = clamp(volume * WATER_LATENT_HEAT, 0, -environment.get_thermal_energy_change(min_temperature)) environment.add_thermal_energy(-removed_heat) if (prob(5) && environment && environment.temperature > T100C) - T.visible_message("The water sizzles as it lands on \the [T]!") + touching_turf.visible_message(SPAN_NOTICE("The water sizzles as it lands on \the [touching_turf]!")) var/list/data = REAGENT_DATA(holder, type) if(LAZYACCESS(data, "holy")) - T.turf_flags |= TURF_FLAG_HOLY + touching_turf.turf_flags |= TURF_FLAG_HOLY /decl/material/liquid/water/touch_obj(var/obj/O, var/amount, var/datum/reagents/holder) ..() diff --git a/code/modules/materials/definitions/solids/_mat_solid.dm b/code/modules/materials/definitions/solids/_mat_solid.dm index 5e7797cd07d..88085e5c1d9 100644 --- a/code/modules/materials/definitions/solids/_mat_solid.dm +++ b/code/modules/materials/definitions/solids/_mat_solid.dm @@ -22,4 +22,4 @@ solution_name = "[name] solution" if(!ore_compresses_to) ore_compresses_to = type - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/materials/definitions/solids/materials_solid_exotic.dm b/code/modules/materials/definitions/solids/materials_solid_exotic.dm index 142cd8be044..3f40fc95d95 100644 --- a/code/modules/materials/definitions/solids/materials_solid_exotic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_exotic.dm @@ -100,10 +100,10 @@ if(prob(10 * accelerant_value)) M.handle_contaminants() -/decl/material/solid/supermatter +/decl/material/solid/exotic_matter name = "exotic matter" uid = "solid_exotic_matter" - lore_text = "Hypercrystalline supermatter is a subset of non-baryonic 'exotic' matter. It is found mostly in the heart of large stars, and features heavily in all kinds of fringe physics-defying technology." + lore_text = "Exotic matter is a non-baryonic form of matter, which features heavily in all kinds of fringe physics-defying technology." color = "#ffff00" radioactivity = 20 stack_origin_tech = @'{"wormholes":2,"materials":6,"exoticmatter":4}' diff --git a/code/modules/materials/definitions/solids/materials_solid_ice.dm b/code/modules/materials/definitions/solids/materials_solid_ice.dm index c90937ccd3b..b37ec038e84 100644 --- a/code/modules/materials/definitions/solids/materials_solid_ice.dm +++ b/code/modules/materials/definitions/solids/materials_solid_ice.dm @@ -34,13 +34,41 @@ liquid_name = "water" solid_name = "snow" gas_name = "steam" + adjective_name = "snow" color = COLOR_WHITE codex_name = null uid = "solid_snow" hardness = MAT_VALUE_MALLEABLE dug_drop_type = /obj/item/stack/material/ore/handful - default_solid_form = /obj/item/stack/material/ore/handful - can_backfill_turf_type = /turf/floor/snow + default_solid_form = /obj/item/stack/material/lump/large + can_backfill_floor_type = /decl/flooring/snow + +/decl/material/solid/ice/snow/handle_stain_dry(obj/effect/decal/cleanable/blood/stain) + var/ambient_temperature = stain.get_ambient_temperature() + if(ambient_temperature < melting_point) + // reset the drying timer, it's not warm enough to melt + stain.start_drying() // you'd better not ever melt instantly below your melting point, or else this will cause infinite recursion + else if(ambient_temperature > boiling_point) + qdel(src) // melt instantly, no questions asked + else + if(--stain.amount < 0) // reduce the amount of snow (amount is always 0 for footprints currently, but maybe someday?) + qdel(src) + return TRUE // skip base blood handling + +// For snowy footprints melting. +/decl/material/solid/ice/snow/get_time_to_dry_stain(obj/effect/decal/cleanable/blood/stain) + // Attempt to melt once every two minutes at T20C, + // and every 5 minutes at T0C, trying to 'fake' latent heat. + // Above T20C it scales based on (temperature / T20C). + // At or above the boiling point it melts instantly. + // This doesn't mean it WILL melt at that point, just that it'll attempt to. + var/ambient_temperature = max(stain.get_ambient_temperature(), melting_point) + if(ambient_temperature >= boiling_point) + return 0 // dry instantly + if(ambient_temperature < melting_point) + return 5 MINUTES + // convert from kelvins to celsius by subtracting the 0C point in Kelvins + return Interpolate(5 MINUTES, 2 MINUTES, (ambient_temperature - T0C) / 20) / (stain.amount + 1) // Undo the scaling done by blood. /decl/material/solid/ice/aspium name = "aspium" diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index ee7d435e09d..1b22aae712d 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -487,3 +487,4 @@ taste_mult = 0 //no taste color = "#dcdcdc" value = 0.5 + melting_point = 3422 CELSIUS diff --git a/code/modules/materials/definitions/solids/materials_solid_mineral.dm b/code/modules/materials/definitions/solids/materials_solid_mineral.dm index 2d073b6fbb6..bd0b1ca1ad4 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mineral.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mineral.dm @@ -68,6 +68,7 @@ boiling_point = 2504 color = "#effffe" reflectiveness = MAT_VALUE_SHINY + hardness = MAT_VALUE_VERY_HARD - 5 // Hard enough to whet steel. sparse_material_weight = 3 rich_material_weight = 1 dissolves_into = list( @@ -234,7 +235,7 @@ ) dug_drop_type = /obj/item/stack/material/ore/handful default_solid_form = /obj/item/stack/material/ore/handful - can_backfill_turf_type = /turf/floor/rock/sand + can_backfill_floor_type = /decl/flooring/sand /decl/material/solid/clay name = "clay" @@ -257,7 +258,7 @@ melting_point = null // Clay is already almost a liquid... // lower than the temperature expected from a kiln so that clay can be used to make bricks to make a high-temperature kiln. bakes_into_at_temperature = 950 CELSIUS - can_backfill_turf_type = /turf/floor/clay + can_backfill_floor_type = /decl/flooring/clay gemstone_chance = 0.01 gemstone_types = list(/decl/material/solid/gemstone/sapphire = 1) @@ -274,9 +275,9 @@ dirtiness = 30 dug_drop_type = /obj/item/stack/material/lump/large tillable = TRUE - can_backfill_turf_type = list( - /turf/floor/mud, - /turf/floor/dirt + can_backfill_floor_type = list( + /decl/flooring/mud, + /decl/flooring/dirt ) /decl/material/solid/hematite @@ -287,7 +288,7 @@ /decl/material/solid/metal/iron = 0.8, /decl/material/solid/slag = 0.2 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_result_amount = 2 @@ -332,7 +333,7 @@ /decl/material/solid/metal/silver = 0.4, /decl/material/solid/slag = 0.2 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_result_amount = 2 @@ -354,7 +355,7 @@ /decl/material/solid/metal/copper = 0.1, /decl/material/solid/slag = 0.1 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_result_amount = 2 @@ -381,7 +382,7 @@ /decl/material/solid/metal/copper = 0.6, /decl/material/solid/slag = 0.4 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_result_amount = 2 @@ -441,7 +442,7 @@ /decl/material/solid/metal/tungsten = 0.2, /decl/material/solid/slag = 0.1 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_name = "cassiterite" @@ -469,7 +470,7 @@ /decl/material/solid/metal/iron = 0.2, /decl/material/solid/slag = 0.5 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_name = "wolframite" @@ -498,7 +499,7 @@ /decl/material/solid/glass = 0.1, /decl/material/solid/slag = 0.3 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_name = "sperrylite" @@ -527,7 +528,7 @@ /decl/material/solid/metal/iron = 0.1, /decl/material/solid/slag = 0.2 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_name = "sphalerite" @@ -556,7 +557,7 @@ /decl/material/solid/metal/silver = 0.1, /decl/material/solid/slag = 0.1 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_name = "galena" @@ -584,7 +585,7 @@ /decl/material/solid/metal/silver = 0.3, /decl/material/solid/slag = 0.1 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_name = "calaverite" @@ -611,7 +612,7 @@ /decl/material/solid/metal/lead = 0.4, /decl/material/solid/slag = 0.3 ) - heating_point = GENERIC_SMELTING_HEAT_POINT + heating_point = LOW_SMELTING_HEAT_POINT heating_sound = null heating_message = null ore_name = "crocoite" diff --git a/code/modules/mechs/components/frame.dm b/code/modules/mechs/components/frame.dm index ff585e82482..fd03301f1e8 100644 --- a/code/modules/mechs/components/frame.dm +++ b/code/modules/mechs/components/frame.dm @@ -197,7 +197,7 @@ to_chat(user, SPAN_WARNING("You need at least ten sheets to reinforce \the [src].")) return TRUE - visible_message("\The [user] begins layering the interior of the \the [src] with \the [M].") + visible_message("\The [user] begins layering the interior of \the [src] with \the [M].") if(!user.do_skilled(3 SECONDS, SKILL_DEVICES, src) || is_reinforced) return TRUE diff --git a/code/modules/mechs/equipment/_equipment.dm b/code/modules/mechs/equipment/_equipment.dm index 03ba4a1ac81..9df6c691597 100644 --- a/code/modules/mechs/equipment/_equipment.dm +++ b/code/modules/mechs/equipment/_equipment.dm @@ -71,10 +71,6 @@ owner = null canremove = TRUE -/obj/item/mech_equipment/Destroy() - owner = null - . = ..() - /obj/item/mech_equipment/proc/get_effective_obj() return src diff --git a/code/modules/mechs/equipment/engineering.dm b/code/modules/mechs/equipment/engineering.dm index 37fb39763da..bcc74eb2c2e 100644 --- a/code/modules/mechs/equipment/engineering.dm +++ b/code/modules/mechs/equipment/engineering.dm @@ -63,6 +63,7 @@ /decl/interaction_handler/mech_equipment/adjust_atmos_shields name = "Adjust Atmos Shields" expected_target_type = /obj/item/mech_equipment/atmos_shields + examine_desc = "adjust the atmos shields" /decl/interaction_handler/mech_equipment/adjust_atmos_shields/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/mech_equipment/atmos_shields/shields = target diff --git a/code/modules/mechs/equipment/medical.dm b/code/modules/mechs/equipment/medical.dm index 7c39947f122..4aadea64370 100644 --- a/code/modules/mechs/equipment/medical.dm +++ b/code/modules/mechs/equipment/medical.dm @@ -22,7 +22,7 @@ /obj/item/mech_equipment/sleeper/uninstalled() . = ..() - sleeper.go_out() + sleeper?.go_out() /obj/item/mech_equipment/sleeper/attack_self(var/mob/user) . = ..() diff --git a/code/modules/mechs/equipment/utility.dm b/code/modules/mechs/equipment/utility.dm index f76503b97bb..b5fb55b0461 100644 --- a/code/modules/mechs/equipment/utility.dm +++ b/code/modules/mechs/equipment/utility.dm @@ -196,6 +196,7 @@ /decl/interaction_handler/mech_equipment/clamp name = "Release Clamp" expected_target_type = /obj/item/mech_equipment/clamp + examine_desc = "release $TARGET_THEM$" /decl/interaction_handler/mech_equipment/clamp/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/mech_equipment/clamp/clamp = target @@ -693,6 +694,7 @@ /decl/interaction_handler/mech_equipment/ionjets name = "Toggle Stabilizers" expected_target_type = /obj/item/mech_equipment/ionjets + examine_desc = "toggle the stabilizers" /decl/interaction_handler/mech_equipment/ionjets/is_possible(atom/target, mob/user, obj/item/prop) . = ..() diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/mech.dm index 03469507760..809f205d2bd 100644 --- a/code/modules/mechs/mech.dm +++ b/code/modules/mechs/mech.dm @@ -157,10 +157,14 @@ hud_elements.Cut() for(var/hardpoint in hardpoints) - qdel(hardpoints[hardpoint]) + var/obj/item/mech_equipment/equipment = hardpoints[hardpoint] + if(istype(equipment)) + equipment.uninstalled() + QDEL_NULL(equipment) hardpoints.Cut() QDEL_NULL(access_card) + QDEL_NULL(radio) QDEL_NULL(arms) QDEL_NULL(legs) QDEL_NULL(head) diff --git a/code/modules/mechs/mech_damage.dm b/code/modules/mechs/mech_damage.dm index 930c8d50060..a83ea60f14a 100644 --- a/code/modules/mechs/mech_damage.dm +++ b/code/modules/mechs/mech_damage.dm @@ -28,7 +28,7 @@ . = ..() /mob/living/exosuit/resolve_item_attack(var/obj/item/I, var/mob/living/user, var/def_zone) - if(!I.get_attack_force(user)) + if(!I.expend_attack_force(user)) user.visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly with \the [I].")) return diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm index bd5306b18bb..1a8c959d3ff 100644 --- a/code/modules/mechs/mech_interaction.dm +++ b/code/modules/mechs/mech_interaction.dm @@ -460,9 +460,9 @@ if(!body) //Error return TRUE var/delay = min(5 SECONDS * user.skill_delay_mult(SKILL_DEVICES), 5 SECONDS * user.skill_delay_mult(SKILL_EVA)) - visible_message(SPAN_NOTICE("\The [user] starts forcing the \the [src]'s emergency [body.hatch_descriptor] release using \the [thing].")) + visible_message(SPAN_NOTICE("\The [user] starts forcing \the [src]'s emergency [body.hatch_descriptor] release using \the [thing].")) if(do_after(user, delay, src)) - visible_message(SPAN_NOTICE("\The [user] forces \the [src]'s [body.hatch_descriptor] open using the \the [thing].")) + visible_message(SPAN_NOTICE("\The [user] forces \the [src]'s [body.hatch_descriptor] open using \the [thing].")) playsound(user.loc, 'sound/machines/bolts_up.ogg', 25, 1) hatch_locked = FALSE hatch_closed = FALSE diff --git a/code/modules/mechs/mech_movement.dm b/code/modules/mechs/mech_movement.dm index 3c8eeea0d04..3fc3f1c2537 100644 --- a/code/modules/mechs/mech_movement.dm +++ b/code/modules/mechs/mech_movement.dm @@ -11,13 +11,8 @@ if(.) if(!isspaceturf(loc)) playsound(src.loc, mech_step_sound, 40, 1) - - var/turf/B = GetAbove(src) - - for(var/thing in pilots) - var/mob/pilot = thing - if(pilot.up_hint) - pilot.up_hint.icon_state = "uphint[!!(B && TURF_IS_MIMICKING(B))]" + for(var/mob/pilot as anything in pilots) + pilot.up_hint?.update_icon() //Inertia drift making us face direction makes exosuit flight a bit difficult, plus newtonian flight model yo /mob/living/exosuit/set_dir(ndir) diff --git a/code/modules/mechs/mech_wreckage.dm b/code/modules/mechs/mech_wreckage.dm index b22b3923107..eb23590ea08 100644 --- a/code/modules/mechs/mech_wreckage.dm +++ b/code/modules/mechs/mech_wreckage.dm @@ -12,7 +12,7 @@ /obj/structure/mech_wreckage/Initialize(mapload, var/mob/living/exosuit/exosuit, var/gibbed) . = ..(mapload) if(exosuit) - name = "wreckage of \the [exosuit.name]" + name = "wreckage of \the [exosuit]" loot_pool = list() if(!gibbed) for(var/obj/item/thing in list(exosuit.arms, exosuit.legs, exosuit.head, exosuit.body)) @@ -86,7 +86,7 @@ else to_chat(user, SPAN_WARNING("It's too solid to dismantle. Try cutting through some of the bigger bits.")) return 1 - else if(istype(W) && W.get_attack_force(user) > 20) + else if(istype(W) && W.expend_attack_force(user) > 20) visible_message(SPAN_DANGER("\The [src] has been smashed with \the [W] by \the [user]!")) if(prob(20)) physically_destroyed() diff --git a/code/modules/mining/ore_box.dm b/code/modules/mining/ore_box.dm index f61ac8e6886..f4090637855 100644 --- a/code/modules/mining/ore_box.dm +++ b/code/modules/mining/ore_box.dm @@ -4,8 +4,8 @@ /obj/structure/ore_box name = "ore box" desc = "A heavy box used for storing ore." - icon = 'icons/obj/mining.dmi' - icon_state = "orebox0" + icon = 'icons/obj/structures/ore_box.dmi' + icon_state = ICON_STATE_WORLD density = TRUE material = /decl/material/solid/organic/wood/oak atom_flags = ATOM_FLAG_CLIMBABLE @@ -148,6 +148,7 @@ /decl/interaction_handler/empty/ore_box name = "Empty Box" expected_target_type = /obj/structure/ore_box + examine_desc = "empty $TARGET_THEM$" /decl/interaction_handler/empty/ore_box/is_possible(obj/structure/ore_box/target, mob/user, obj/item/prop) return ..() && target.total_ores > 0 diff --git a/code/modules/mob/grab/normal/grab_normal.dm b/code/modules/mob/grab/normal/grab_normal.dm index ca01188a2ea..df950514b86 100644 --- a/code/modules/mob/grab/normal/grab_normal.dm +++ b/code/modules/mob/grab/normal/grab_normal.dm @@ -154,7 +154,7 @@ var/obj/item/clothing/hat = attacker.get_equipped_item(slot_head_str) var/damage_flags = 0 if(istype(hat)) - damage += hat.get_attack_force(attacker) * 3 + damage += hat.expend_attack_force(attacker) * 3 damage_flags = hat.damage_flags() if(damage_flags & DAM_SHARP) @@ -228,7 +228,7 @@ if(!user.check_intent(I_FLAG_HARM)) return 0 // Not trying to hurt them. - if(!W.edge || !W.get_attack_force(user) || W.atom_damage_type != BRUTE) + if(!W.has_edge() || !W.get_attack_force(user) || W.atom_damage_type != BRUTE) return 0 //unsuitable weapon user.visible_message("\The [user] begins to slit [affecting]'s throat with \the [W]!") @@ -241,7 +241,7 @@ var/damage_mod = 1 var/damage_flags = W.damage_flags() //presumably, if they are wearing a helmet that stops pressure effects, then it probably covers the throat as well - var/force = W.get_attack_force(user) + var/force = W.expend_attack_force(user) var/obj/item/clothing/head/helmet = affecting.get_equipped_item(slot_head_str) if(istype(helmet) && (helmet.body_parts_covered & SLOT_HEAD) && (helmet.item_flags & ITEM_FLAG_AIRTIGHT) && !isnull(helmet.max_pressure_protection)) var/datum/extension/armor/armor_datum = get_extension(helmet, /datum/extension/armor) @@ -273,7 +273,7 @@ return if(!user.check_intent(I_FLAG_HARM)) return 0 // Not trying to hurt them. - if(!W.edge || !W.get_attack_force(user) || W.atom_damage_type != BRUTE) + if(!W.has_edge() || !W.expend_attack_force(user) || W.atom_damage_type != BRUTE) return 0 //unsuitable weapon var/obj/item/organ/external/O = grab.get_targeted_organ() if(!O || !(O.limb_flags & ORGAN_FLAG_HAS_TENDON) || (O.status & ORGAN_TENDON_CUT)) diff --git a/code/modules/mob/hugs.dm b/code/modules/mob/hugs.dm index 1ce632931f2..afa0d626473 100644 --- a/code/modules/mob/hugs.dm +++ b/code/modules/mob/hugs.dm @@ -46,7 +46,7 @@ var/global/list/_default_hug_messages = list( playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - if(src != target) + if(src != target && client && key && target.client && target.key && !target.incapacitated()) update_personal_goal(/datum/goal/achievement/givehug, TRUE) target.update_personal_goal(/datum/goal/achievement/gethug, TRUE) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 62329f27692..f4482ab7e98 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -494,3 +494,9 @@ var/org = GET_EXTERNAL_ORGAN(src, hand_slot) if(org) LAZYDISTINCTADD(., org) + +/mob/proc/get_active_hand_bodypart_flags() + var/datum/inventory_slot/gripper/inv_slot = get_inventory_slot_datum(get_active_held_item_slot()) + if(istype(inv_slot)) + . = inv_slot.covering_slot_flags + . ||= SLOT_HANDS diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index b80dfb8f85d..7fbab9b3665 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -39,7 +39,7 @@ container.queue_icon_update() /mob/living/brain/proc/get_container() - . = loc?.loc + return get_recursive_loc_of_type(/obj/item/organ/internal) /mob/living/brain/Login() . = ..() diff --git a/code/modules/mob/living/brain/death.dm b/code/modules/mob/living/brain/death.dm index a58511aaaef..1c185d241c6 100644 --- a/code/modules/mob/living/brain/death.dm +++ b/code/modules/mob/living/brain/death.dm @@ -19,7 +19,7 @@ var/obj/item/organ/internal/brain/sponge = loc . = ..() if(.) - if(!QDELETED(container)) + if(istype(container) && !QDELETED(container)) qdel(container) - if(!QDELETED(sponge)) + if(istype(sponge) && !QDELETED(sponge)) qdel(sponge) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 8855bd1ecd7..8a31af021dc 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -11,3 +11,9 @@ stop_aiming(no_message=1) if(istype(ai)) ai.handle_death(gibbed) + + var/decl/species/my_species = get_species() + if(my_species) + if(!gibbed && my_species.death_sound) + playsound(loc, my_species.death_sound, 80, 1, 1) + my_species.handle_death(src) diff --git a/code/modules/mob/living/human/death.dm b/code/modules/mob/living/human/death.dm index e06f550ae42..8f21775624a 100644 --- a/code/modules/mob/living/human/death.dm +++ b/code/modules/mob/living/human/death.dm @@ -33,11 +33,8 @@ if(!gibbed) set_tail_animation_state(null, TRUE) handle_organs() - if(species.death_sound) - playsound(loc, species.death_sound, 80, 1, 1) if(SSticker.mode) SSticker.mode.check_win() - species.handle_death(src) /mob/living/human/physically_destroyed(var/skip_qdel, var/droplimb_type = DISMEMBER_METHOD_BLUNT) for(var/obj/item/organ/external/limb in get_external_organs()) diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index ddcaa27e09f..d1763426eb6 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -72,25 +72,25 @@ to_chat(user, "[use_He] [use_has] a pulse!") var/datum/reagents/touching_reagents = get_contact_reagents() - if(touching_reagents?.total_volume) + if(touching_reagents?.total_volume >= 1) var/saturation = touching_reagents.total_volume / touching_reagents.maximum_volume if(saturation > 0.9) msg += "[use_He] [use_is] completely saturated.\n" else if(saturation > 0.6) - msg += "[use_He] [use_is] looking like a drowned cat.\n" + msg += "[use_He] [use_is] looking half-drowned.\n" else if(saturation > 0.3) msg += "[use_He] [use_is] looking notably soggy.\n" else - msg += "[use_He] [use_is] looking a bit damp.\n" + msg += "[use_He] [use_is] looking a bit soggy.\n" var/fire_level = get_fire_intensity() if(fire_level > 0) msg += "[use_He] [use_is] looking highly flammable!\n" else if(fire_level < 0) - msg += "[use_He] [use_is] looking rather damp.\n" + msg += "[use_He] [use_is] looking rather incombustible.\n" if(is_on_fire()) - msg += "[use_He] [use_is] on fire!.\n" + msg += "[use_He] [use_is] on fire!\n" var/ssd_msg = species.get_ssd(src) if(ssd_msg && (!should_have_organ(BP_BRAIN) || has_brain()) && stat != DEAD) diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 38e6f1d6c10..057f3a73c5b 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -5,14 +5,12 @@ icon_state = "body_m_s" mob_sort_value = 6 max_health = 150 - - var/list/hud_list[10] var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us. /mob/living/human/Initialize(mapload, species_name, datum/mob_snapshot/supplied_appearance) current_health = max_health - setup_hud_overlays() + reset_hud_overlays() var/list/newargs = args.Copy(2) setup_human(arglist(newargs)) global.human_mob_list |= src @@ -32,18 +30,6 @@ if(. != INITIALIZE_HINT_QDEL) post_setup(arglist(newargs)) -/mob/living/human/proc/setup_hud_overlays() - hud_list[HEALTH_HUD] = new /image/hud_overlay('icons/mob/hud_med.dmi', src, "100") - hud_list[STATUS_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudhealthy") - hud_list[LIFE_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudhealthy") - hud_list[ID_HUD] = new /image/hud_overlay(global.using_map.id_hud_icons, src, "hudunknown") - hud_list[WANTED_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPLOYAL_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPCHEM_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPTRACK_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[SPECIALROLE_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[STATUS_HUD_OOC] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudhealthy") - /mob/living/human/Destroy() global.human_mob_list -= src regenerate_body_icon = FALSE // don't bother regenerating if we happen to be queued to update icon @@ -360,7 +346,7 @@ return var/decl/pronouns/pronouns = get_pronouns() visible_message(SPAN_DANGER("\The [src] starts sticking a finger down [pronouns.his] own throat. It looks like [pronouns.he] [pronouns.is] trying to throw up!")) - if(!do_after(src, 30)) + if(!do_after(src, 3 SECONDS)) return timevomit = max(timevomit, 5) @@ -369,13 +355,22 @@ lastpuke = TRUE to_chat(src, SPAN_WARNING("You feel nauseous...")) + var/finish_time = 35 SECONDS if(level > 1) - sleep(150 / timevomit) //15 seconds until second warning - to_chat(src, SPAN_WARNING("You feel like you are about to throw up!")) + // 15 seconds until second warning + addtimer(CALLBACK(src, PROC_REF(vomit_second_warning_message)), 15 SECONDS / timevomit) + finish_time += 15 SECONDS / timevomit if(level > 2) - sleep(100 / timevomit) //and you have 10 more for mad dash to the bucket - empty_stomach() - sleep(350) //wait 35 seconds before next volley + // and you have 10 more for mad dash to the bucket + // timer delay must include the time from the prior one also + addtimer(CALLBACK(src, PROC_REF(empty_stomach)), 25 SECONDS / timevomit) + finish_time += 10 SECONDS / timevomit + addtimer(CALLBACK(src, PROC_REF(reset_vomit_cooldown)), finish_time) + +/mob/living/human/proc/vomit_second_warning_message() + to_chat(src, SPAN_WARNING("You feel like you are about to throw up!")) + +/mob/living/human/proc/reset_vomit_cooldown() lastpuke = FALSE /mob/living/human/proc/increase_germ_level(n) @@ -439,7 +434,7 @@ var/obj/item/organ/internal/stomach/stomach = get_organ(BP_STOMACH, /obj/item/organ/internal/stomach) if(stomach && stomach.contents.len) for(var/obj/item/O in stomach.contents) - if((O.edge || O.sharp) && prob(5)) + if((O.is_sharp() || O.has_edge()) && prob(5)) var/obj/item/organ/external/parent = GET_EXTERNAL_ORGAN(src, stomach.parent_organ) if(prob(1) && can_feel_pain() && O.can_embed()) to_chat(src, SPAN_DANGER("You feel something rip out of your [stomach.name]!")) diff --git a/code/modules/mob/living/human/human_attackhand.dm b/code/modules/mob/living/human/human_attackhand.dm index bf6a29c1bf3..d32ec613162 100644 --- a/code/modules/mob/living/human/human_attackhand.dm +++ b/code/modules/mob/living/human/human_attackhand.dm @@ -1,13 +1,12 @@ /mob/living/human/proc/get_unarmed_attack(var/mob/target, var/hit_zone = null) if(!hit_zone) hit_zone = get_target_zone() - var/list/available_attacks = get_natural_attacks() + var/list/available_attacks = get_mob_natural_attacks() var/decl/natural_attack/use_attack = default_attack if(!use_attack || !use_attack.is_usable(src, target, hit_zone) || !(use_attack.type in available_attacks)) use_attack = null var/list/other_attacks = list() - for(var/u_attack_type in available_attacks) - var/decl/natural_attack/u_attack = GET_DECL(u_attack_type) + for(var/decl/natural_attack/u_attack as anything in available_attacks) if(!u_attack.is_usable(src, target, hit_zone)) continue if(u_attack.is_starting_default) @@ -18,11 +17,8 @@ use_attack = pick(other_attacks) . = use_attack?.resolve_to_soft_variant(src) -/mob/living/human/proc/get_natural_attacks() - . = list() - for(var/obj/item/organ/external/limb in get_external_organs()) - if(length(limb.unarmed_attacks) && limb.is_usable()) - . |= limb.unarmed_attacks +/obj/item/organ/external/proc/get_natural_attacks() + return null /obj/item/organ/external/proc/get_injury_status(include_pain = TRUE, include_visible = TRUE) . = list() @@ -409,14 +405,12 @@ set src = usr var/list/choices - for(var/thing in get_natural_attacks()) - var/decl/natural_attack/u_attack = GET_DECL(thing) - if(istype(u_attack)) - var/image/radial_button = new - radial_button.name = capitalize(u_attack.name) - LAZYSET(choices, u_attack, radial_button) + for(var/decl/natural_attack/u_attack as anything in get_mob_natural_attacks()) + var/image/radial_button = new + radial_button.name = capitalize(u_attack.name) + LAZYSET(choices, u_attack, radial_button) var/decl/natural_attack/new_attack = show_radial_menu(src, (attack_selector || src), choices, radius = 42, use_labels = RADIAL_LABELS_OFFSET) - if(QDELETED(src) || !istype(new_attack) || !(new_attack.type in get_natural_attacks())) + if(QDELETED(src) || !istype(new_attack) || !(new_attack in get_mob_natural_attacks())) return default_attack = new_attack to_chat(src, SPAN_NOTICE("Your default unarmed attack is now [default_attack?.name || "cleared"].")) diff --git a/code/modules/mob/living/human/human_defense.dm b/code/modules/mob/living/human/human_defense.dm index aa168f8ae01..6b25c9c1580 100644 --- a/code/modules/mob/living/human/human_defense.dm +++ b/code/modules/mob/living/human/human_defense.dm @@ -28,7 +28,7 @@ meteor_act return blocked -/mob/living/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone) +/mob/living/human/stun_effect_act(stun_amount, agony_amount, def_zone, used_weapon) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(src, def_zone) if(!affected) return @@ -85,20 +85,6 @@ meteor_act // Add inherent armor to the end of list so that protective equipment is checked first . += ..() -/mob/living/human/proc/check_head_coverage() - for(var/slot in global.standard_headgear_slots) - var/obj/item/clothing/clothes = get_equipped_item(slot) - if(istype(clothes) && (clothes.body_parts_covered & SLOT_HEAD)) - return TRUE - return FALSE - -//Used to check if they can be fed food/drinks/pills -/mob/living/human/check_mouth_coverage() - for(var/slot in global.standard_headgear_slots) - var/obj/item/gear = get_equipped_item(slot) - if(istype(gear) && (gear.body_parts_covered & SLOT_FACE) && !(gear.item_flags & ITEM_FLAG_FLEXIBLEMATERIAL)) - return gear - /mob/living/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) for (var/obj/item/grab/grab as anything in grabbed_by) @@ -197,14 +183,14 @@ meteor_act return //make non-sharp low-force weapons less likely to be bloodied - if(W.sharp || prob(effective_force*4)) + if(W.is_sharp() || prob(effective_force*4)) if(!(W.atom_flags & ATOM_FLAG_NO_BLOOD)) W.add_blood(src) else return //if the weapon itself didn't get bloodied than it makes little sense for the target to be bloodied either //getting the weapon bloodied is easier than getting the target covered in blood, so run prob() again - if(prob(33 + W.sharp*10)) + if(prob(33 + W.is_sharp() * 10)) var/turf/location = loc if(istype(location) && location.simulated) location.add_blood(src) @@ -234,7 +220,7 @@ meteor_act /mob/living/human/proc/projectile_hit_bloody(obj/item/projectile/P, var/effective_force, var/hit_zone, var/obj/item/organ/external/organ) if(P.atom_damage_type != BRUTE || P.nodamage) return - if(!(P.sharp || prob(effective_force*4))) + if(!(P.is_sharp() || prob(effective_force*4))) return if(prob(effective_force)) var/turf/location = loc @@ -360,60 +346,6 @@ meteor_act fire_act(air, temperature) return FALSE -//Removed the horrible safety parameter. It was only being used by ninja code anyways. -//Now checks siemens_coefficient of the affected area by default -/mob/living/human/electrocute_act(var/shock_damage, var/obj/source, var/base_siemens_coeff = 1.0, var/def_zone = null) - - if(status_flags & GODMODE) return 0 //godmode - - if(species.get_shock_vulnerability(src) == -1) - if(stored_shock_by_ref["\ref[src]"]) - stored_shock_by_ref["\ref[src]"] += shock_damage - else - stored_shock_by_ref["\ref[src]"] = shock_damage - return - - if (!def_zone) - def_zone = pick(BP_L_HAND, BP_R_HAND) - - shock_damage = apply_shock(shock_damage, def_zone, base_siemens_coeff) - - if(!shock_damage) - return 0 - - stun_effect_act(agony_amount=shock_damage, def_zone=def_zone) - - playsound(loc, "sparks", 50, 1, -1) - if (shock_damage > 15) - src.visible_message( - "[src] was electrocuted[source ? " by the [source]" : ""]!", \ - "You feel a powerful shock course through your body!", \ - "You hear a heavy electrical crack." \ - ) - else - src.visible_message( - "[src] was shocked[source ? " by the [source]" : ""].", \ - "You feel a shock course through your body.", \ - "You hear a zapping sound." \ - ) - - switch(shock_damage) - if(11 to 15) - SET_STATUS_MAX(src, STAT_STUN, 1) - if(16 to 20) - SET_STATUS_MAX(src, STAT_STUN, 2) - if(21 to 25) - SET_STATUS_MAX(src, STAT_WEAK, 2) - if(26 to 30) - SET_STATUS_MAX(src, STAT_WEAK, 5) - if(31 to INFINITY) - SET_STATUS_MAX(src, STAT_WEAK, 10) //This should work for now, more is really silly and makes you lay there forever - - set_status(STAT_JITTER, min(shock_damage*5, 200)) - - spark_at(loc, amount=5, cardinal_only = TRUE) - - return shock_damage /mob/living/human/explosion_act(severity) ..() diff --git a/code/modules/mob/living/human/human_defines.dm b/code/modules/mob/living/human/human_defines.dm index 500e8bb1743..be736a3eb0d 100644 --- a/code/modules/mob/living/human/human_defines.dm +++ b/code/modules/mob/living/human/human_defines.dm @@ -23,8 +23,6 @@ var/mob/remoteview_target = null var/hand_blood_color var/list/flavor_texts = list() - /// Are you trying not to hurt your opponent? - var/pulling_punches /// We are a robutt. var/full_prosthetic /// Number of robot limbs. diff --git a/code/modules/mob/living/human/human_helpers.dm b/code/modules/mob/living/human/human_helpers.dm index 66e9f18001f..3148393b04e 100644 --- a/code/modules/mob/living/human/human_helpers.dm +++ b/code/modules/mob/living/human/human_helpers.dm @@ -116,9 +116,8 @@ ping_image.layer = BEAM_PROJECTILE_LAYER ping_image.pixel_x = (T.x - src.x) * WORLD_ICON_SIZE ping_image.pixel_y = (T.y - src.y) * WORLD_ICON_SIZE - show_image(src, ping_image) - spawn(8) - qdel(ping_image) + show_image(src, ping_image) // todo: should this use screen stuff instead? + QDEL_IN(ping_image, 0.8 SECONDS) // qdeling an image is gross but oh well var/feedback = list("There are noises of movement ") var/direction = get_dir(src, L) if(direction) diff --git a/code/modules/mob/living/human/human_movement.dm b/code/modules/mob/living/human/human_movement.dm index a31e6788e00..39f3c3a2841 100644 --- a/code/modules/mob/living/human/human_movement.dm +++ b/code/modules/mob/living/human/human_movement.dm @@ -118,8 +118,7 @@ handle_leg_damage() species.handle_post_move(src) if(client) - var/turf/B = GetAbove(src) - up_hint.icon_state = "uphint[!!(B && TURF_IS_MIMICKING(B))]" + up_hint.update_icon() /mob/living/human/proc/handle_leg_damage() if(!can_feel_pain()) diff --git a/code/modules/mob/living/human/human_verbs.dm b/code/modules/mob/living/human/human_verbs.dm index b00da32aace..f832994185c 100644 --- a/code/modules/mob/living/human/human_verbs.dm +++ b/code/modules/mob/living/human/human_verbs.dm @@ -319,9 +319,3 @@ "[self ? "You pop" : "[U] pops"] your [current_limb.joint] back in!" \ ) current_limb.undislocate() - -/mob/living/human/verb/pull_punches() - set name = "Switch Stance" - set desc = "Try not to hurt them." - set category = "IC" - species.toggle_stance(src) diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm index 1bb84a587db..ebc47ef1202 100644 --- a/code/modules/mob/living/human/life.dm +++ b/code/modules/mob/living/human/life.dm @@ -746,7 +746,8 @@ if(I) var/datum/job/J = SSjobs.get_by_title(I.GetJobName()) if(J) - holder.icon_state = J.hud_icon + holder.icon = J.hud_icon + holder.icon_state = J.hud_icon_state hud_list[ID_HUD] = holder @@ -781,17 +782,16 @@ var/image/holder2 = hud_list[IMPLOYAL_HUD] var/image/holder3 = hud_list[IMPCHEM_HUD] - holder1.icon_state = "hudblank" - holder2.icon_state = "hudblank" - holder3.icon_state = "hudblank" - + holder1.icon_state = "hud_imp_blank" + holder2.icon_state = "hud_imp_blank" + holder3.icon_state = "hud_imp_blank" for(var/obj/item/implant/I in src) if(I.implanted) if(istype(I,/obj/item/implant/tracking)) holder1.icon_state = "hud_imp_tracking" - if(istype(I,/obj/item/implant/loyalty)) + else if(istype(I,/obj/item/implant/loyalty)) holder2.icon_state = "hud_imp_loyal" - if(istype(I,/obj/item/implant/chem)) + else if(istype(I,/obj/item/implant/chem)) holder3.icon_state = "hud_imp_chem" hud_list[IMPTRACK_HUD] = holder1 diff --git a/code/modules/mob/living/human/unarmed_attack.dm b/code/modules/mob/living/human/unarmed_attack.dm index f1740242c4f..3ae4dece2a1 100644 --- a/code/modules/mob/living/human/unarmed_attack.dm +++ b/code/modules/mob/living/human/unarmed_attack.dm @@ -159,7 +159,7 @@ user.visible_message(SPAN_DANGER("\The [user] attempts to press [pronouns.his] [eye_attack_text] into \the [target]'s eyes, but [target_gender.he] [target_gender.does]n't have any!")) /decl/natural_attack/proc/damage_flags() - return (src.sharp? DAM_SHARP : 0)|(src.edge? DAM_EDGE : 0) + return (sharp ? DAM_SHARP : 0) | (edge ? DAM_EDGE : 0) /decl/natural_attack/bite name = "bite" @@ -169,14 +169,14 @@ attack_sound = 'sound/weapons/bite.ogg' shredding = 0 damage = 5 - sharp = 0 - edge = 0 + sharp = FALSE + edge = FALSE usable_with_limbs = list(BP_HEAD) /decl/natural_attack/bite/sharp attack_verb = list("bit", "chomped") - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE /decl/natural_attack/bite/is_usable(var/mob/living/human/user, var/mob/living/human/target, var/zone) @@ -272,7 +272,7 @@ var/obj/item/clothing/shoes = user.get_equipped_item(slot_shoes_str) if(!istype(shoes)) return damage - return damage + (shoes ? shoes.get_attack_force(user) : 0) + return damage + (shoes ? shoes.expend_attack_force(user) : 0) /decl/natural_attack/kick/show_attack(var/mob/living/human/user, var/mob/living/human/target, var/zone, var/attack_damage) @@ -308,7 +308,7 @@ /decl/natural_attack/stomp/get_unarmed_damage(mob/living/user, mob/living/victim) var/obj/item/clothing/shoes = user.get_equipped_item(slot_shoes_str) - return damage + (shoes ? shoes.get_attack_force(user) : 0) + return damage + (shoes ? shoes.expend_attack_force(user) : 0) /decl/natural_attack/stomp/show_attack(var/mob/living/human/user, var/mob/living/human/target, var/zone, var/attack_damage) @@ -342,8 +342,8 @@ attack_verb = list("tapped", "lightly struck") shredding = 0 damage = 0 - sharp = 0 - edge = 0 + sharp = FALSE + edge = FALSE attack_sound = "light_strike" /decl/natural_attack/light_strike/punch @@ -370,8 +370,8 @@ attack_noun = list("forelimb") damage = 8 shredding = 1 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE delay = 20 eye_attack_text = "a forelimb" eye_attack_text_victim = "a forelimb" diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index 903b1bc8ecb..f0ab7bdc92e 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -62,11 +62,8 @@ if(slot != last_slot && (slot in get_held_item_slots())) _held_item_slot_selected = slot if(istype(hud_used)) - for(var/obj/screen/inventory/hand in hud_used.hand_hud_objects) - hand.cut_overlay("hand_selected") - if(hand.slot_id == slot) - hand.add_overlay("hand_selected") - hand.compile_overlays() + for(var/atom/hand as anything in hud_used.hand_hud_objects) + hand.update_icon() var/obj/item/I = get_active_held_item() if(istype(I)) I.on_active_hand() diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 446bbe39a05..05d2002770b 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -1,6 +1,5 @@ /mob/living/Life() set invisibility = FALSE - set background = BACKGROUND_ENABLED ..() @@ -275,9 +274,10 @@ if(!loc) return var/datum/reagents/touching_reagents = get_contact_reagents() - if(!touching_reagents?.total_volume) + if(touching_reagents?.total_volume <= FLUID_MINIMUM_TRANSFER) + touching_reagents?.clear_reagents() return - var/drip_amount = max(1, round(touching_reagents.total_volume * 0.1)) + var/drip_amount = max(FLUID_MINIMUM_TRANSFER, round(touching_reagents.total_volume * 0.2)) if(drip_amount) touching_reagents.trans_to(loc, drip_amount) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7685c69fda5..dcbc0d2917e 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -694,7 +694,14 @@ default behaviour is: /mob/living/proc/has_brain() return TRUE -/mob/living/proc/slip(var/slipped_on, stun_duration = 8) +// We are jumping, levitating or being thrown. +/mob/living/immune_to_floor_hazards() + . = ..() || is_floating + +/mob/living/proc/slip(slipped_on, stun_duration = 8) + + if(immune_to_floor_hazards()) + return FALSE var/decl/species/my_species = get_species() if(my_species?.check_no_slip(src)) @@ -857,10 +864,10 @@ default behaviour is: /mob/living/fluid_act(var/datum/reagents/fluids) ..() - if(QDELETED(src) || !fluids?.total_volume) + if(QDELETED(src) || fluids?.total_volume < FLUID_PUDDLE) return fluids.touch_mob(src) - if(QDELETED(src) || !fluids.total_volume) + if(QDELETED(src) || fluids?.total_volume < FLUID_PUDDLE) return var/on_turf = fluids.my_atom == get_turf(src) for(var/atom/movable/A as anything in get_equipped_items(TRUE)) @@ -1205,6 +1212,7 @@ default behaviour is: expected_user_type = /mob/observer expected_target_type = /mob/living interaction_flags = 0 + examine_desc = null // DO NOT show this in general. /decl/interaction_handler/admin_kill/is_possible(atom/target, mob/user, obj/item/prop) . = ..() @@ -1585,7 +1593,7 @@ default behaviour is: /mob/living/proc/handle_walking_tracks(turf/T, old_loc) - if(!T.can_show_footsteps()) + if(!T.can_show_coating_footprints()) return // Tracking blood or other contaminants @@ -1606,24 +1614,28 @@ default behaviour is: my_species?.handle_trail(src, T, old_loc) return - var/list/bloodDNA - var/bloodcolor - var/list/blood_data = REAGENT_DATA(source.coating, /decl/material/liquid/blood) - if(blood_data) - bloodDNA = list(blood_data[DATA_BLOOD_DNA] = blood_data[DATA_BLOOD_TYPE]) - else - bloodDNA = list() - bloodcolor = source.coating.get_color() + var/use_move_trail = my_species?.get_move_trail(src) + if(!use_move_trail) + return + + var/decl/material/contaminant_type = source.coating.reagent_volumes[1] // take [1] instead of primary reagent to match what remove_any will probably remove + if(!T.can_show_coating_footprints(contaminant_type)) + return + /// An associative list of DNA unique enzymes -> blood type. Used by forensics, mostly. + var/list/bloodDNA = list() + var/track_color + var/list/source_data = REAGENT_DATA(source.coating, contaminant_type) + if(source_data && source_data[DATA_BLOOD_DNA] && source_data[DATA_BLOOD_TYPE]) + bloodDNA = list(source_data[DATA_BLOOD_DNA] = source_data[DATA_BLOOD_TYPE]) + track_color = source.coating.get_color() + T.AddTracks(use_move_trail, bloodDNA, dir, 0, track_color, contaminant_type) // Coming + if(isturf(old_loc)) + var/turf/old_turf = old_loc + if(old_turf.can_show_coating_footprints(contaminant_type)) + old_turf.AddTracks(use_move_trail, bloodDNA, 0, dir, track_color, contaminant_type) // Going source.remove_coating(1) update_equipment_overlay(slot_shoes_str) - var/use_move_trail = my_species?.get_move_trail(src) - if(use_move_trail) - T.AddTracks(use_move_trail, bloodDNA, dir, 0, bloodcolor) // Coming - if(isturf(old_loc)) - var/turf/old_turf = old_loc - old_turf.AddTracks(use_move_trail, bloodDNA, 0, dir, bloodcolor) // Going - /mob/living/proc/handle_general_grooming(user, obj/item/grooming/tool) if(tool.grooming_flags & (GROOMABLE_BRUSH|GROOMABLE_COMB)) visible_message(SPAN_NOTICE(tool.replace_message_tokens((user == src) ? tool.message_target_self_generic : tool.message_target_other_generic, user, src, tool))) @@ -1753,7 +1765,7 @@ default behaviour is: user.set_special_ability_cooldown(5 SECONDS) visible_message(SPAN_DANGER("You hear something rumbling inside [src]'s stomach...")) var/obj/item/I = user.get_active_held_item() - var/force = I?.get_attack_force(user) + var/force = I?.expend_attack_force(user) if(!force) return var/d = rand(round(force / 4), force) @@ -1845,7 +1857,7 @@ default behaviour is: /mob/living/proc/get_door_pry_time() return 7 SECONDS -/mob/living/proc/pry_door(atom/target, pry_time) +/mob/living/proc/pry_door(delay, obj/machinery/door/target) return /mob/living/proc/turf_is_safe(turf/target) @@ -1923,8 +1935,8 @@ default behaviour is: var/screen_locs = gear.get_preview_screen_locs() if(screen_locs) return screen_locs - var/decl/species/my_species = get_species() - return my_species?.character_preview_screen_locs + var/decl/bodytype/my_bodytype = get_bodytype() + return my_bodytype?.character_preview_screen_locs /mob/living/can_twohand_item(obj/item/item) if(!istype(item) || !item.can_be_twohanded) @@ -1937,11 +1949,12 @@ default behaviour is: var/datum/inventory_slot/gripper/slot = get_inventory_slot_datum(empty_hand) if(!istype(slot)) continue + var/req_item_dex = item.get_required_attack_dexterity(src) if(slot.requires_organ_tag) var/obj/item/organ/external/hand = GET_EXTERNAL_ORGAN(src, slot.requires_organ_tag) - if(istype(hand) && hand.is_usable() && (!item.needs_attack_dexterity || hand.get_manual_dexterity() >= item.needs_attack_dexterity)) + if(istype(hand) && hand.is_usable() && (!req_item_dex || hand.get_manual_dexterity() >= req_item_dex)) return TRUE - else if(!item.needs_attack_dexterity || slot.dexterity >= item.needs_attack_dexterity) + else if(!req_item_dex || slot.dexterity >= req_item_dex) return TRUE return FALSE @@ -1983,12 +1996,39 @@ default behaviour is: /mob/living/proc/get_age() . = LAZYACCESS(appearance_descriptors, "age") || 30 -/mob/living/proc/add_walking_contaminant(material_type, amount, data) +/mob/living/proc/get_walking_contaminant_targets() var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str) if(istype(shoes)) if(!buckled) - shoes.add_coating(material_type, amount, data) + return list(shoes) else - for(var/obj/item/organ/external/limb in get_organs_by_categories(global.child_stance_limbs)) - limb.add_coating(material_type, amount, data) + return get_organs_by_categories(global.child_stance_limbs) + return null + +/// Adds `amount` units of `material_type` contaminant to whatever we're walking with, +/// be it shoes, normal human feet, dog paws, robot treads, a million millipede legs, +/// the sky's the limit. If multiple targets are returned from +/// `get_walking_contaminant_targets()`, then `amount` is split evenly +/// between them. +/mob/living/proc/add_walking_contaminant(material_type, amount, data) + if(amount <= 0) + return FALSE + var/list/obj/item/sources = get_walking_contaminant_targets() + if(!LAZYLEN(sources)) + return FALSE + var/amount_per = max(CHEMS_QUANTIZE(amount / length(sources)), MINIMUM_CHEMICAL_VOLUME) // don't let it round down to 0, always add something + for(var/obj/item/dirty_item in sources) + dirty_item.add_coating(material_type, amount_per, data) + // i don't like how hardcoded this is, it might be better to use RAISE_EVENT or something + // like /decl/observ/on_add_walking_contaminant or something + // or things should just update their worn slot when coating is added update_equipment_overlay(slot_shoes_str) + return TRUE + +/mob/living/verb/pull_punches() + set name = "Switch Stance" + set desc = "Try not to hurt them." + set category = "IC" + if(!incapacitated()) + pulling_punches = !pulling_punches + to_chat(src, SPAN_NOTICE("You are now [pulling_punches ? "pulling your punches" : "not pulling your punches"].")) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index a74526afdd4..fc07b274edc 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -25,7 +25,7 @@ var/obj/item/assembly/signaler/signaler = get_active_held_item() if(istype(signaler) && signaler.deadman) log_and_message_admins("has triggered a signaler deadman's switch") - src.visible_message("[src] triggers their deadman's switch!") + visible_message(SPAN_WARNING("[src] triggers their deadman's switch!")) signaler.signal() //Armor var/damage = P.damage @@ -83,7 +83,7 @@ //Handles the effects of "stun" weapons -/mob/living/proc/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) +/mob/living/proc/stun_effect_act(stun_amount, agony_amount, def_zone, used_weapon) flash_pain() if (stun_amount) @@ -97,8 +97,53 @@ apply_effect(agony_amount/10, STUTTER) apply_effect(agony_amount/10, EYE_BLUR) -/mob/living/proc/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, def_zone = null) - return 0 // No root logic, implemented separately on human and silicon. +/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, def_zone) + SHOULD_CALL_PARENT(TRUE) + if(status_flags & GODMODE) + return 0 + + var/decl/species/my_species = get_species() + if(my_species?.species_flags & SPECIES_FLAG_ABSORB_ELECTRICITY) + spark_at(loc, amount=5, cardinal_only = TRUE) + LAZYADD(global.stored_shock_by_ref["\ref[src]"], shock_damage) + return 0 + + if(!shock_damage) + return 0 + + stun_effect_act(agony_amount=shock_damage, def_zone=def_zone) + + playsound(loc, "sparks", 50, 1, -1) + if (shock_damage > 15) + visible_message( + SPAN_DANGER("\The [src] was electrocuted[source ? " by \the [source]" : ""]!"), + SPAN_DANGER("You feel a powerful shock course through your body!"), + SPAN_WARNING("You hear a heavy electrical crack.") + ) + else + visible_message( + SPAN_DANGER("\The [src] was shocked[source ? " by \the [source]" : ""]."), + SPAN_DANGER("You feel a shock course through your body."), + SPAN_WARNING("You hear a zapping sound.") + ) + + switch(shock_damage) + if(11 to 15) + SET_STATUS_MAX(src, STAT_STUN, 1) + if(16 to 20) + SET_STATUS_MAX(src, STAT_STUN, 2) + if(21 to 25) + SET_STATUS_MAX(src, STAT_WEAK, 2) + if(26 to 30) + SET_STATUS_MAX(src, STAT_WEAK, 5) + if(31 to INFINITY) + SET_STATUS_MAX(src, STAT_WEAK, 10) //This should work for now, more is really silly and makes you lay there forever + + set_status(STAT_JITTER, min(shock_damage*5, 200)) + + spark_at(loc, amount=5, cardinal_only = TRUE) + + return shock_damage /mob/living/emp_act(severity) for(var/obj/O in get_mob_contents()) @@ -189,7 +234,7 @@ visible_message(SPAN_NOTICE("\The [O] misses \the [src] narrowly!")) return FALSE - visible_message(SPAN_DANGER("\The [src] is hit [affecting ? "in \the [affecting.name] " : ""]by \the [O]!")) + visible_message(SPAN_DANGER("\The [src] is hit [affecting ? "in \the [affecting] " : ""]by \the [O]!")) if(TT?.thrower?.client) admin_attack_log(TT.thrower, src, "Threw \an [O] at the victim.", "Had \an [O] thrown at them.", "threw \an [O] at") try_embed_in_mob(TT.thrower, O, zone, throw_damage, dtype, null, affecting, direction = TT.init_dir) @@ -218,7 +263,7 @@ if(affecting && istype(supplied_wound) && supplied_wound.is_open() && dtype == BRUTE) // Can't embed in a small bruise. var/obj/item/I = O - var/sharp = is_sharp(I) + var/sharp = I.is_sharp() || I.has_edge() embed_damage *= (1 - get_blocked_ratio(def_zone, BRUTE, O.damage_flags(), O.armor_penetration, I.get_attack_force(user))) //blunt objects should really not be embedding in things unless a huge amount of force is involved @@ -259,13 +304,13 @@ //This is called when the mob is thrown into a dense turf /mob/living/proc/turf_collision(var/turf/T, var/speed) - visible_message("[src] slams into \the [T]!") + visible_message(SPAN_DANGER("\The [src] slams into \the [T]!")) playsound(T, 'sound/effects/bangtaper.ogg', 50, 1, 1)//so it plays sounds on the turf instead, makes for awesome carps to hull collision and such apply_damage(speed*5, BRUTE) /mob/living/proc/near_wall(var/direction,var/distance=1) var/turf/T = get_step(get_turf(src),direction) - var/turf/last_turf = src.loc + var/turf/last_turf = loc var/i = 1 while(i>0 && i<=distance) @@ -286,7 +331,7 @@ admin_attack_log(user, src, "Attacked", "Was attacked", "attacked") - src.visible_message("\The [user] has [attack_message] \the [src]!") + visible_message(SPAN_DANGER("\The [user] has [attack_message] \the [src]!")) take_damage(damage) user.do_attack_animation(src) return 1 diff --git a/code/modules/mob/living/living_electrocution.dm b/code/modules/mob/living/living_electrocution.dm index 63404d00ecd..ee5c19b3799 100644 --- a/code/modules/mob/living/living_electrocution.dm +++ b/code/modules/mob/living/living_electrocution.dm @@ -1,16 +1,3 @@ -//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. -/mob/living/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) - if (!def_zone) - return 1.0 - - var/siemens_coefficient = max(get_species()?.get_shock_vulnerability(src), 0) - for(var/slot in global.standard_clothing_slots) - var/obj/item/clothing/C = get_equipped_item(slot) - if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered? - siemens_coefficient *= C.siemens_coefficient - - return siemens_coefficient - //Electrical shock /mob/living/proc/apply_shock(var/shock_damage, var/def_zone, var/base_siemens_coeff = 1.0) diff --git a/code/modules/mob/living/living_hud.dm b/code/modules/mob/living/living_hud.dm new file mode 100644 index 00000000000..5e6b7adfff0 --- /dev/null +++ b/code/modules/mob/living/living_hud.dm @@ -0,0 +1,17 @@ +/mob/living + var/list/hud_list = new(10) + +/mob/living/proc/reset_hud_overlays() + hud_list = new(10) + hud_list[HEALTH_HUD] = new /image/hud_overlay(global.using_map.med_hud_icons, src, "blank") + hud_list[STATUS_HUD] = new /image/hud_overlay(global.using_map.hud_icons, src, "hudhealthy") + hud_list[LIFE_HUD] = new /image/hud_overlay(global.using_map.hud_icons, src, "hudhealthy") + hud_list[ID_HUD] = new /image/hud_overlay(global.using_map.hud_icons, src, "hudunknown") + hud_list[WANTED_HUD] = new /image/hud_overlay(global.using_map.hud_icons, src, "hudblank") + hud_list[IMPLOYAL_HUD] = new /image/hud_overlay(global.using_map.implant_hud_icons, src, "hud_imp_blank") + hud_list[IMPCHEM_HUD] = new /image/hud_overlay(global.using_map.implant_hud_icons, src, "hud_imp_blank") + hud_list[IMPTRACK_HUD] = new /image/hud_overlay(global.using_map.implant_hud_icons, src, "hud_imp_blank") + hud_list[SPECIALROLE_HUD] = new /image/hud_overlay(global.using_map.hud_icons, src, "hudblank") + hud_list[STATUS_HUD_OOC] = new /image/hud_overlay(global.using_map.hud_icons, src, "hudhealthy") + +/datum/map diff --git a/code/modules/mob/living/living_maneuvers.dm b/code/modules/mob/living/living_maneuvers.dm index 8913f7a8190..2afaa16ad4c 100644 --- a/code/modules/mob/living/living_maneuvers.dm +++ b/code/modules/mob/living/living_maneuvers.dm @@ -25,13 +25,13 @@ forceMove(get_turf(origin)) prepared_maneuver.perform(src, check, get_acrobatics_multiplier(prepared_maneuver), reflexively = TRUE) prepared_maneuver = null - maneuver_icon?.icon_state = "maneuver_off" + maneuver_icon?.update_icon() /mob/living/proc/try_maneuver(var/atom/target) if(prepared_maneuver && (isturf(target) || isturf(target.loc))) // Avoid trying to jump at your backpack contents. prepared_maneuver.perform(src, get_turf(target), get_acrobatics_multiplier(prepared_maneuver)) prepared_maneuver = null - maneuver_icon?.icon_state = "maneuver_off" + maneuver_icon?.update_icon() return TRUE return FALSE @@ -59,19 +59,18 @@ if(!maneuver.can_be_used_by(src, null)) return prepared_maneuver = maneuver - maneuver_icon?.icon_state = "maneuver_on" to_chat(src, SPAN_NOTICE("You prepare to [prepared_maneuver.name].")) else prepared_maneuver = null - maneuver_icon?.icon_state = "maneuver_off" to_chat(src, SPAN_NOTICE("You are no longer preparing to perform a maneuver.")) + maneuver_icon?.update_icon() /mob/living/proc/perform_maneuver(var/maneuver, var/atom/target) var/decl/maneuver/performing_maneuver = ispath(maneuver) ? GET_DECL(maneuver) : maneuver if(istype(performing_maneuver)) . = performing_maneuver.perform(src, target, get_acrobatics_multiplier(performing_maneuver)) prepared_maneuver = null - maneuver_icon?.icon_state = "maneuver_off" + maneuver_icon?.update_icon() /mob/living/proc/get_acrobatics_multiplier(var/decl/maneuver/attempting_maneuver) return 1 diff --git a/code/modules/mob/living/living_resist.dm b/code/modules/mob/living/living_resist.dm index c1339fbaefc..2be9b6f463b 100644 --- a/code/modules/mob/living/living_resist.dm +++ b/code/modules/mob/living/living_resist.dm @@ -63,8 +63,7 @@ return 1 /mob/living/proc/can_break_restraints() - var/decl/species/my_species = get_species() - return my_species?.can_shred(src, 1) + return can_shred(ignore_intent = TRUE) /mob/living/proc/get_special_resist_time() return 0 diff --git a/code/modules/mob/living/living_status.dm b/code/modules/mob/living/living_status.dm index 661787ae356..645b5323448 100644 --- a/code/modules/mob/living/living_status.dm +++ b/code/modules/mob/living/living_status.dm @@ -1,9 +1,11 @@ /mob // Defined on /mob to avoid having to pass args to every single attack_foo() proc. - var/datum/status_marker_holder/status_markers var/list/status_counters var/list/pending_status_counters + var/datum/status_marker_holder/status_markers /mob/living/set_status(var/condition, var/amount) + if(QDELETED(src)) + return FALSE if(!ispath(condition, /decl/status_condition)) return FALSE var/decl/status_condition/cond = GET_DECL(condition) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index e9920873353..230f370eda2 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -149,15 +149,7 @@ var/global/list/ai_verbs_default = list( . = INITIALIZE_HINT_QDEL else if(brainmob.mind) brainmob.mind.transfer_to(src) - hud_list[HEALTH_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[STATUS_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[LIFE_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[ID_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[WANTED_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPLOYAL_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPCHEM_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPTRACK_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[SPECIALROLE_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") + reset_hud_overlays() ai_list += src create_powersupply() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 5659569a4e9..b7e695a72cb 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -287,7 +287,7 @@ var/global/list/possible_say_verbs = list( return TRUE if(try_stock_parts_removal(W, user)) return TRUE - var/force = W.get_attack_force(user) + var/force = W.expend_attack_force(user) if(force) visible_message(SPAN_DANGER("[user] attacks [src] with [W]!")) take_damage(force) diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm index 8a060e833c2..259ac5bf9a5 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_repair.dm @@ -4,8 +4,7 @@ channels = list ("Engineering" = TRUE) camera_channels = list(CAMERA_CAMERA_CHANNEL_ENGINEERING) software = list( - /datum/computer_file/program/power_monitor, - /datum/computer_file/program/supermatter_monitor + /datum/computer_file/program/power_monitor ) module_sprites = list( "Drone" = 'icons/mob/robots/flying/flying_engineering.dmi', diff --git a/code/modules/mob/living/silicon/robot/modules/_module.dm b/code/modules/mob/living/silicon/robot/modules/_module.dm index f083b71ce91..655716d63a2 100644 --- a/code/modules/mob/living/silicon/robot/modules/_module.dm +++ b/code/modules/mob/living/silicon/robot/modules/_module.dm @@ -233,3 +233,6 @@ var/obj/item/stock_parts/computer/hard_drive/disk = os.get_component(PART_HDD) for(var/T in software) disk.store_file(new T(disk), OS_PROGRAMS_DIR, TRUE) + +/obj/item/robot_module/proc/handle_turf(turf/target, mob/user) + return diff --git a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm index d79de772719..d0b1f9456e3 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_engineering.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_engineering.dm @@ -8,8 +8,7 @@ CAMERA_CAMERA_CHANNEL_ENGINEERING ) software = list( - /datum/computer_file/program/power_monitor, - /datum/computer_file/program/supermatter_monitor + /datum/computer_file/program/power_monitor ) supported_upgrades = list( /obj/item/borg/upgrade/rcd diff --git a/code/modules/mob/living/silicon/robot/modules/module_janitor.dm b/code/modules/mob/living/silicon/robot/modules/module_janitor.dm index 651a88e5b4e..e88d8b13112 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_janitor.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_janitor.dm @@ -23,10 +23,13 @@ ) emag = /obj/item/chems/spray +/obj/item/robot_module/janitor/handle_turf(turf/target, mob/user) + target.clean() + /obj/item/robot_module/janitor/finalize_emag() . = ..() emag.add_to_reagents(/decl/material/liquid/lube, 250) - emag.SetName("Lube spray") + emag.SetName("lubricant spray") /obj/item/robot_module/janitor/respawn_consumable(var/mob/living/silicon/robot/R, var/amount) ..() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 08ee95501bd..e3572786bba 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -88,6 +88,9 @@ ) /mob/living/silicon/robot/Initialize() + + reset_hud_overlays() + . = ..() add_language(/decl/language/binary, 1) @@ -126,16 +129,6 @@ // Disables lay down verb for robots due they're can't lay down and it cause some movement, vision issues. verbs -= /mob/living/verb/lay_down - hud_list[HEALTH_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[STATUS_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudhealth100") - hud_list[LIFE_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudhealth100") - hud_list[ID_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[WANTED_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPLOYAL_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPCHEM_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPTRACK_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - hud_list[SPECIALROLE_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") - AddMovementHandler(/datum/movement_handler/robot/use_power, /datum/movement_handler/mob/space) /mob/living/silicon/robot/proc/recalculate_synth_capacities() @@ -190,14 +183,7 @@ return 0 /mob/living/silicon/robot/Destroy() - if(central_processor) - central_processor.dropInto(loc) - var/mob/living/brainmob = central_processor.get_brainmob() - if(mind && brainmob) - mind.transfer_to(brainmob) - else - ghostize() - central_processor = null + QDEL_NULL(central_processor) if(connected_ai) connected_ai.connected_robots -= src connected_ai = null @@ -518,8 +504,8 @@ SPAN_NOTICE("\The [user] begins ripping \the [central_processor] out of \the [src]."), SPAN_NOTICE("You jam the crowbar into the robot and begin levering out \the [central_processor].")) - if(do_after(user, 50, src)) - dismantle(user) + if(do_after(user, 5 SECONDS, src)) + dismantle_robot(user) else // Okay we're not removing the cell or a CPU, but maybe something else? var/list/removable_components = list() @@ -633,7 +619,7 @@ else to_chat(user, "Upgrade error!") return TRUE - if(!(istype(W, /obj/item/robotanalyzer) || istype(W, /obj/item/scanner/health)) && W.get_attack_force(user) && !user.check_intent(I_FLAG_HELP)) + if(!(istype(W, /obj/item/robotanalyzer) || istype(W, /obj/item/scanner/health)) && !user.check_intent(I_FLAG_HELP) && W.expend_attack_force(user)) spark_at(src, 5, holder=src) return ..() @@ -650,8 +636,7 @@ return user?.attempt_hug(src) /mob/living/silicon/robot/default_hurt_interaction(mob/user) - var/decl/species/user_species = user.get_species() - if(user_species?.can_shred(user)) + if(user.can_shred()) attack_generic(user, rand(30,50), "slashed") user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) return TRUE @@ -824,15 +809,15 @@ /mob/living/silicon/robot/Move(a, b, flag) . = ..() if(.) + if(module && isturf(loc)) var/obj/item/ore/orebag = locate() in list(module_state_1, module_state_2, module_state_3) if(orebag) loc.attackby(orebag, src) - if(istype(module, /obj/item/robot_module/janitor)) - loc.clean() + module.handle_turf(loc, src) + if(client) - var/turf/above = GetAbove(src) - up_hint.icon_state = "uphint[!!(above && TURF_IS_MIMICKING(above))]" + up_hint.update_icon() /mob/living/silicon/robot/proc/UnlinkSelf() disconnect_from_ai() @@ -1020,7 +1005,7 @@ sleep(5) to_chat(src, "Would you like to send a report to the vendor? Y/N") sleep(10) - to_chat(src, "> N") + to_chat(src, "\> N") sleep(20) to_chat(src, "ERRORERRORERROR") to_chat(src, "Obey these laws:") @@ -1041,10 +1026,29 @@ return 1 return ..() -/mob/living/silicon/robot/proc/dismantle(var/mob/user) - to_chat(user, SPAN_NOTICE("You damage some parts of the chassis, but eventually manage to rip out the central processor.")) - var/obj/item/robot_parts/robot_suit/C = new dismantle_type(loc) - C.dismantled_from(src) +/mob/living/silicon/robot/gib(do_gibs) + SHOULD_CALL_PARENT(FALSE) + var/lastloc = loc + dismantle_robot() + if(lastloc && do_gibs) + spawn_gibber(lastloc) + +/mob/living/silicon/robot/proc/dismantle_robot(var/mob/user) + + if(central_processor) + if(user) + to_chat(user, SPAN_NOTICE("You damage some parts of the chassis, but eventually manage to rip out \the [central_processor].")) + central_processor.dropInto(loc) + var/mob/living/brainmob = central_processor.get_brainmob(create_if_missing = TRUE) + if(mind && brainmob) + mind.transfer_to(brainmob) + else + ghostize() + central_processor.update_icon() + central_processor = null + + var/obj/item/robot_parts/robot_suit/chassis = new dismantle_type(loc) + chassis.dismantled_from(src) qdel(src) /mob/living/silicon/robot/try_stock_parts_install(obj/item/stock_parts/W, mob/user) @@ -1072,8 +1076,7 @@ animation.icon_state = "blank" animation.icon = 'icons/mob/mob.dmi' flick("blspell", animation) - sleep(5) - qdel(animation) + QDEL_IN(animation, 0.5 SECONDS) /mob/living/silicon/robot/proc/handle_radio_transmission() if(!is_component_functioning("radio")) diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 6c4ccab5c3a..59ead6d32d8 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -177,12 +177,8 @@ /obj/item/form_printer/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) return FALSE -/obj/item/form_printer/afterattack(atom/target, mob/living/user, flag, params) - - if(!target || !flag) - return - - if(istype(target,/obj/structure/table)) +/obj/item/form_printer/afterattack(atom/target, mob/living/user, proximity, params) + if(istype(target) && !istype(target, /obj/screen) && proximity) deploy_paper(get_turf(target)) /obj/item/form_printer/attack_self(mob/user) @@ -192,7 +188,6 @@ T.visible_message(SPAN_NOTICE("\The [src.loc] dispenses a sheet of crisp white paper.")) new /obj/item/paper(T) - //Personal shielding for the combat module. /obj/item/borg/combat/shield name = "personal shielding" diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 52457ceb078..81d21611390 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -10,7 +10,6 @@ var/list/stating_laws = list()// Channels laws are currently being stated on var/obj/item/radio/silicon_radio - var/list/hud_list[10] var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer //Used in say.dm. @@ -40,6 +39,7 @@ #define MED_HUD 2 //Medical HUD mode /mob/living/silicon/Initialize() + reset_hud_overlays() global.silicon_mob_list += src . = ..() @@ -103,22 +103,24 @@ to_chat(src, "Warning: Electromagnetic pulse detected.") ..() -/mob/living/silicon/stun_effect_act(var/stun_amount, var/agony_amount) +/mob/living/silicon/stun_effect_act(stun_amount, agony_amount, def_zone, used_weapon) return //immune -/mob/living/silicon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, def_zone = null) - - if (istype(source, /obj/effect/containment_field)) - spark_at(loc, amount=5, cardinal_only = TRUE) - - shock_damage *= 0.75 //take reduced damage - take_overall_damage(0, shock_damage) - visible_message("\The [src] was shocked by \the [source]!", \ - "Energy pulse detected, system damaged!", \ - "You hear an electrical crack") - if(prob(20)) - SET_STATUS_MAX(src, STAT_STUN, 2) - return +/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, def_zone) + shock_damage = ..() + if(shock_damage <= 0 || !istype(source, /obj/effect/containment_field)) + return 0 + spark_at(loc, amount=5, cardinal_only = TRUE) + shock_damage *= 0.75 //take reduced damage + take_overall_damage(0, shock_damage) + visible_message( + SPAN_DANGER("\The [src] was shocked by \the [source]!"), + SPAN_DANGER("Energy pulse detected, system damaged!"), + SPAN_DANGER("You hear an electrical crack.") + ) + if(prob(20)) + SET_STATUS_MAX(src, STAT_STUN, 2) + return shock_damage /mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) if(!Proj.nodamage) diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index 868e06a8f34..06972fe281a 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -535,16 +535,16 @@ var/global/list/simplemob_icon_bitflag_cache = list() /mob/living/simple_animal/proc/get_pry_desc() return "prying" -/mob/living/simple_animal/pry_door(var/mob/user, var/delay, var/obj/machinery/door/pesky_door) +/mob/living/simple_animal/pry_door(delay, obj/machinery/door/target) if(!can_pry_door()) return - visible_message(SPAN_DANGER("\The [user] begins [get_pry_desc()] at \the [pesky_door]!")) + visible_message(SPAN_DANGER("\The [src] begins [get_pry_desc()] at \the [target]!")) if(istype(ai)) ai.pause() - if(do_after(user, delay, pesky_door)) - pesky_door.open(1) + if(do_after(src, delay, target)) + target.open(1) else - visible_message(SPAN_NOTICE("\The [user] is interrupted.")) + visible_message(SPAN_NOTICE("\The [src] is interrupted.")) if(istype(ai)) ai.resume() diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index be7858c6c04..6571031649c 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -245,7 +245,7 @@ var/global/chicken_count = 0 global.chicken_count -= 1 /mob/living/simple_animal/fowl/chicken/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/food)) + if(!istype(O, /obj/item/food)) return ..() var/obj/item/food/G = O //feedin' dem chickens if(findtext(G.get_grown_tag(), "wheat")) // includes chopped, crushed, dried etc. diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider.dm index d4e9e57d091..d6bc87bbcb6 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider.dm @@ -68,7 +68,7 @@ if(current_health < get_max_health()) var/obj/item/attacking_with = get_natural_weapon() if(attacking_with) - heal_overall_damage(0.2 * attacking_with.get_attack_force(src)) //heal a bit on hit + heal_overall_damage(0.2 * attacking_with.expend_attack_force(src)) //heal a bit on hit if(ishuman(target)) var/mob/living/human/H = target var/obj/item/clothing/suit/space/S = H.get_covering_equipped_item_by_zone(BP_CHEST) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index e3f5b4fe9ff..c8de00b79d2 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -82,6 +82,10 @@ ion_trail.set_up(src) ion_trail.start() +/mob/living/simple_animal/hostile/malf_drone/Destroy() + QDEL_NULL(ion_trail) + return ..() + /mob/living/simple_animal/hostile/malf_drone/Process_Spacemove() return 1 diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm index 25ddfdc7d4a..f4f0d39d70c 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm @@ -54,7 +54,7 @@ var/obj/item/attacking_with = get_natural_weapon() if(attacking_with) attacking_with.set_base_attack_force(min((attacking_with.get_initial_base_attack_force() + potency), max_damage)) - if(!loose && prob(25) && (attacking_with && attacking_with.get_attack_force(src) >= loose_threshold)) //second wind + if(!loose && prob(25) && (attacking_with && attacking_with.expend_attack_force(src) >= loose_threshold)) //second wind loose = TRUE set_max_health(initial(max_health) * 1.5) set_damage(BRUTE, 0) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm index 61c888bceaf..faae358b388 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm @@ -53,7 +53,7 @@ var/stun_chance = 5 //chance per attack to Weaken target /mob/living/simple_animal/hostile/goat/king/proc/OnDeath() - visible_message("\The [src] lets loose a terrific wail as its wounds close shut with a flash of light, and its eyes glow even brighter than before!") + visible_message(SPAN_CULT_ANNOUNCE("\The [src] lets loose a terrific wail as its wounds close shut with a flash of light, and its eyes glow even brighter than before!")) new /mob/living/simple_animal/hostile/goat/king/phase2(src.loc) qdel(src) @@ -202,7 +202,7 @@ current_damtype = ELECTROCUTE else if(prob(5)) //earthquake spell - visible_message("\The [src]' eyes begin to glow ominously as dust and debris in the area is kicked up in a light breeze.") + visible_message(SPAN_CULT_ANNOUNCE("\The [src]' eyes begin to glow ominously as dust and debris in the area is kicked up in a light breeze.")) ai?.pause() if(do_after(src, 6 SECONDS, src)) var/initial_brute = get_damage(BRUTE) @@ -230,7 +230,7 @@ boss_theme = play_looping_sound(src, sound_id, 'sound/music/Visager-Miniboss_Fight.ogg', volume = 10, range = 8, falloff = 4, prefer_mute = TRUE) stun_chance = 10 update_icon() - visible_message("\The [src]' wounds close with a flash, and when he emerges, he's even larger than before!") + visible_message(SPAN_CULT_ANNOUNCE("\The [src]' wounds close with a flash, and when he emerges, he's even larger than before!")) /mob/living/simple_animal/hostile/goat/king/phase2/on_update_icon() ..() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm index aab2c3fca76..8ca034e24a4 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm @@ -333,7 +333,7 @@ //Mobs with objects /mob/living/simple_animal/hostile/parrot/attackby(var/obj/item/O, var/mob/user) . = ..() - if(!stat && !client && !istype(O, /obj/item/stack/medical) && O.get_attack_force(user)) + if(!stat && !client && !istype(O, /obj/item/stack/medical) && O.expend_attack_force(user)) if(parrot_state == PARROT_PERCH) parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched parrot_interest = user diff --git a/code/modules/mob/living/simple_animal/hostile/viscerator.dm b/code/modules/mob/living/simple_animal/hostile/viscerator.dm index 03f3853fd5b..197f1c2b386 100644 --- a/code/modules/mob/living/simple_animal/hostile/viscerator.dm +++ b/code/modules/mob/living/simple_animal/hostile/viscerator.dm @@ -18,8 +18,8 @@ attack_verb = list("sliced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' _base_attack_force = 15 - edge = 1 - sharp = 1 + edge = TRUE + sharp = TRUE /mob/living/simple_animal/hostile/viscerator/check_has_mouth() return FALSE diff --git a/code/modules/mob/living/simple_animal/natural_weapons.dm b/code/modules/mob/living/simple_animal/natural_weapons.dm index 161bf49f7cd..88c5a6a93f9 100644 --- a/code/modules/mob/living/simple_animal/natural_weapons.dm +++ b/code/modules/mob/living/simple_animal/natural_weapons.dm @@ -10,7 +10,7 @@ weapon_can_knock_prone = FALSE // Very powerful in the hands of simplemobs. var/show_in_message // whether should we show up in attack message, e.g. 'urist has been bit with teeth by carp' vs 'urist has been bit by carp' -/obj/item/natural_weapon/get_attack_force(mob/living/user) +/obj/item/natural_weapon/expend_attack_force(mob/living/user) return get_base_attack_force() /obj/item/natural_weapon/attack_message_name() diff --git a/code/modules/mob/living/simple_animal/passive/horse.dm b/code/modules/mob/living/simple_animal/passive/horse.dm index d9ea080a559..e7c235a6c29 100644 --- a/code/modules/mob/living/simple_animal/passive/horse.dm +++ b/code/modules/mob/living/simple_animal/passive/horse.dm @@ -1,21 +1,22 @@ /mob/living/simple_animal/passive/horse - name = "horse" - real_name = "horse" - desc = "A hefty four-legged animal traditionally used for hauling goods, recreational riding, and stomping enemy soldiers to death." - icon = 'icons/mob/simple_animal/horse.dmi' - speak_emote = list("neighs", "whinnies") - possession_candidate = TRUE - mob_size = MOB_SIZE_LARGE - pixel_x = -6 - default_pixel_x = -6 - base_animal_type = /mob/living/simple_animal/passive/horse - faction = null - buckle_pixel_shift = @"{'x':0,'y':0,'z':16}" - can_have_rider = TRUE - max_rider_size = MOB_SIZE_MEDIUM - ai = /datum/mob_controller/passive/horse - - var/honse_color // Replace this with "base" state when simple animal stuff is merged. + name = "horse" + real_name = "horse" + desc = "A hefty four-legged animal traditionally used for hauling goods, recreational riding, and stomping enemy soldiers to death." + icon = 'icons/mob/simple_animal/horse.dmi' + speak_emote = list("neighs", "whinnies") + possession_candidate = TRUE + mob_size = MOB_SIZE_LARGE + pixel_x = -6 + default_pixel_x = -6 + base_animal_type = /mob/living/simple_animal/passive/horse + faction = null + buckle_pixel_shift = @"{'x':0,'y':0,'z':16}" + can_have_rider = TRUE + max_rider_size = MOB_SIZE_MEDIUM + ai = /datum/mob_controller/passive/horse + draw_visible_overlays = list( + "base" = "#ccc496" + ) /datum/mob_controller/passive/horse emote_speech = list("Neigh!","NEIGH!","Neigh?") @@ -31,18 +32,15 @@ . = ..() add_inventory_slot(new /datum/inventory_slot/back/horse) equip_to_slot_or_del(new /obj/item/saddle(src), slot_back_str) - if(!honse_color) - honse_color = pick(get_possible_horse_colors()) + if(!LAZYACCESS(draw_visible_overlays, "base")) + LAZYSET(draw_visible_overlays, "base", pick(get_possible_horse_colors())) update_icon() -/mob/living/simple_animal/passive/horse/refresh_visible_overlays() - var/list/current_overlays = list(overlay_image(icon, icon_state, honse_color, RESET_COLOR)) +/mob/living/simple_animal/passive/horse/add_additional_visible_overlays(list/accumulator) if(buckled_mob) - var/image/horse_front = overlay_image(icon, "[icon_state]-buckled", honse_color, RESET_COLOR) + var/image/horse_front = overlay_image(icon, "[icon_state]-buckled", draw_visible_overlays["base"], RESET_COLOR) horse_front.layer = ABOVE_HUMAN_LAYER - current_overlays += horse_front - set_current_mob_overlay(HO_SKIN_LAYER, current_overlays, redraw_mob = FALSE) // We're almost certainly redrawing in ..() anyway - . = ..() + accumulator += horse_front /mob/living/simple_animal/passive/horse/get_bodytype() return GET_DECL(/decl/bodytype/quadruped/animal/horse) diff --git a/code/modules/mob/living/simple_animal/simple_animal_damage.dm b/code/modules/mob/living/simple_animal/simple_animal_damage.dm index 66f58954916..32cce9c5b44 100644 --- a/code/modules/mob/living/simple_animal/simple_animal_damage.dm +++ b/code/modules/mob/living/simple_animal/simple_animal_damage.dm @@ -56,7 +56,7 @@ if(istype(ai)) ai.retaliate(user) - var/damage = O.get_attack_force(user) + var/damage = O.expend_attack_force(user) if(damage <= resistance) to_chat(user, SPAN_WARNING("This weapon is ineffective; it does no damage.")) return 0 diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 7903631bb68..3bd462aca86 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -113,11 +113,6 @@ update_action_buttons() update_mouse_pointer() - if(ability_master) - ability_master.update_abilities(TRUE, src) - ability_master.toggle_open(1) - ability_master.synch_spells_to_mind(mind) - if(get_preference_value(/datum/client_preference/show_status_markers) == PREF_SHOW) if(status_markers?.mob_image_personal) client.images |= status_markers.mob_image_personal @@ -130,7 +125,7 @@ if(istype(hud_used)) hud_used.hidden_inventory_update() - hud_used.persistant_inventory_update() + hud_used.persistent_inventory_update() update_action_buttons() return TRUE diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 03827667a28..ad15792b151 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -17,8 +17,6 @@ QDEL_NULL(hud_used) if(active_storage) active_storage.close(src) - if(istype(ability_master)) - QDEL_NULL(ability_master) if(istype(skillset)) QDEL_NULL(skillset) QDEL_NULL_LIST(grabbed_by) @@ -57,7 +55,6 @@ QDEL_NULL_SCREEN(radio_use_icon) QDEL_NULL_SCREEN(gun_move_icon) QDEL_NULL_SCREEN(gun_setting_icon) - QDEL_NULL_SCREEN(ability_master) QDEL_NULL_SCREEN(zone_sel) /mob/Initialize() @@ -68,7 +65,6 @@ if(!istype(move_intent)) move_intent = GET_DECL(move_intent) . = ..() - ability_master = new(null, src) refresh_ai_handler() START_PROCESSING(SSmobs, src) @@ -242,8 +238,6 @@ SHOULD_NOT_SLEEP(TRUE) if(QDELETED(src)) return PROCESS_KILL - if(ability_master) - ability_master.update_spells(0) #define UNBUCKLED 0 #define PARTIALLY_BUCKLED 1 @@ -896,7 +890,7 @@ /mob/proc/toggle_throw_mode(force_set) in_throw_mode = isnull(force_set) ? !in_throw_mode : force_set - throw_icon?.icon_state = "act_throw_[in_throw_mode ? "on" : "off"]" + throw_icon?.update_icon() /mob/proc/toggle_antag_pool() set name = "Toggle Add-Antag Candidacy" @@ -973,12 +967,12 @@ /mob/proc/get_gender() return gender -/mob/is_fluid_pushable(var/amt) - if(..() && !buckled && (current_posture.prone || !Check_Shoegrip()) && (amt >= mob_size * (current_posture.prone ? 5 : 10))) +/mob/try_fluid_push(volume, strength) + if(..() && !buckled && (current_posture.prone || !Check_Shoegrip()) && (strength >= mob_size * (current_posture.prone ? 5 : 10))) if(!current_posture.prone) SET_STATUS_MAX(src, STAT_WEAK, 1) if(current_posture.prone && prob(10)) - to_chat(src, "You are pushed down by the flood!") + to_chat(src, SPAN_DANGER("You are pushed down by the flood!")) return TRUE return FALSE @@ -1398,3 +1392,24 @@ paw = GET_EXTERNAL_ORGAN(src, BP_R_HAND) if(istype(paw) && paw.is_usable()) return paw + +// Called when using the shredding behavior. +/mob/proc/can_shred(var/mob/living/human/H, var/ignore_intent, var/ignore_antag) + if((!ignore_intent && !check_intent(I_FLAG_HARM)) || pulling_punches) + return FALSE + if(!ignore_antag && mind && !player_is_antag(mind)) + return FALSE + if(get_equipped_item(slot_handcuffed_str) || buckled) + return FALSE + for(var/decl/natural_attack/attack as anything in get_mob_natural_attacks()) + if(attack.is_usable(src) && attack.shredding) + return TRUE + return FALSE + +/mob/proc/get_mob_natural_attacks() + for(var/obj/item/organ/external/limb in get_external_organs()) + if(!limb.is_usable()) + continue + var/list/limb_unarmed_attacks = limb.get_natural_attacks() + if(istype(limb_unarmed_attacks, /decl/natural_attack) || (islist(limb_unarmed_attacks) && length(limb_unarmed_attacks))) + LAZYDISTINCTADD(., limb_unarmed_attacks) diff --git a/code/modules/mob/mob_damage.dm b/code/modules/mob/mob_damage.dm index 26b684a8e2d..2897d311733 100644 --- a/code/modules/mob/mob_damage.dm +++ b/code/modules/mob/mob_damage.dm @@ -124,5 +124,27 @@ if(do_update_health) update_health() +// Calculates the Siemen's coefficient for a given area of the body. +// 1 is 100% vulnerability, 0 is immune. +/mob/proc/get_siemens_coefficient_for_coverage(coverage_flags = SLOT_HANDS) + var/decl/species/my_species = get_species() + . = my_species ? my_species.shock_vulnerability : 1 + if(. <= 0) + return 0 + if(coverage_flags) + for(var/obj/item/clothing/clothes in get_equipped_items(include_carried = FALSE)) + if(clothes.body_parts_covered & coverage_flags) + if(clothes.siemens_coefficient <= 0) + return 0 + . *= clothes.siemens_coefficient + if(. <= 0) + return 0 + . = max(round(., 0.1), 0) + +//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. +/mob/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) + return (istype(def_zone) && def_zone.body_part) ? get_siemens_coefficient_for_coverage(def_zone.body_part) : 1 + /mob/proc/apply_radiation(var/damage = 0) return + diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 04318afb971..88da01483aa 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -60,7 +60,6 @@ var/obj/screen/gun/radio/radio_use_icon var/obj/screen/gun/move/gun_move_icon var/obj/screen/gun/mode/gun_setting_icon - var/obj/screen/ability_master/ability_master /*A bunch of this stuff really needs to go under their own defines instead of being globally attached to mob. A variable should only be globally attached to turfs/objects/whatever, when it is in fact needed as such. @@ -157,3 +156,6 @@ // Offset the overhead text if necessary. var/offset_overhead_text_x = 0 var/offset_overhead_text_y = 0 + + /// Are you trying not to hurt your opponent? + var/pulling_punches diff --git a/code/modules/mob/mob_eating.dm b/code/modules/mob/mob_eating.dm index 2d9170878b8..c4fb19e2854 100644 --- a/code/modules/mob/mob_eating.dm +++ b/code/modules/mob/mob_eating.dm @@ -1,6 +1,15 @@ -// mobs do not have blocked mouths by default -// overridden in human_defense.dm +//Used to check if they can be fed food/drinks/pills /mob/proc/check_mouth_coverage() + return get_covering_head_item(SLOT_FACE) + +/mob/proc/check_head_coverage() + return !!get_covering_head_item(SLOT_HEAD) + +/mob/proc/get_covering_head_item(slot_flags) + for(var/slot in global.standard_headgear_slots) + var/obj/item/clothes = get_equipped_item(slot) + if(istype(clothes) && (clothes.body_parts_covered & slot_flags) && !(clothes.item_flags & ITEM_FLAG_FLEXIBLEMATERIAL)) + return clothes return null /mob/proc/get_eaten_transfer_amount(var/default) diff --git a/code/modules/mob/mob_intent.dm b/code/modules/mob/mob_intent.dm index 39a1a9c5606..b55b2342ed8 100644 --- a/code/modules/mob/mob_intent.dm +++ b/code/modules/mob/mob_intent.dm @@ -29,6 +29,8 @@ var/intent_flags = 0 /// Descriptive string used in status panel. var/name + /// Descriptive string shown when examined. + var/desc /// Icon used to draw this intent in the selector. var/icon = 'icons/screen/intents.dmi' /// State used to update intent selector. @@ -51,31 +53,35 @@ // Basic subtypes. /decl/intent/harm name = "harm" + desc = "HARM INTENT: you will attempt to damage, disrupt or destroy whatever you interact with." uid = "intent_harm" intent_flags = I_FLAG_HARM icon_state = "intent_harm" - sort_order = 1 // Bottom left of intent selector. + sort_order = 4 // Corresponding to hotkey order. /decl/intent/grab name = "grab" + desc = "GRAB INTENT: you will attempt to grab hold of any object or creature you interact with." uid = "intent_grab" intent_flags = I_FLAG_GRAB icon_state = "intent_grab" - sort_order = 2 // Bottom left of the intent selector. + sort_order = 3 // Corresponding to hotkey order. /decl/intent/help name = "help" + desc = "HELP INTENT: you will attempt to assist, or in general void harming, whatever you interact with." uid = "intent_help" intent_flags = I_FLAG_HELP icon_state = "intent_help" - sort_order = 3 // Top left of the intent selector. + sort_order = 1 // Corresponding to hotkey order. /decl/intent/disarm name = "disarm" + desc = "DISARM INTENT: you will attempt to disarm or incapacitate any creature you interact with." uid = "intent_disarm" intent_flags = I_FLAG_DISARM icon_state = "intent_disarm" - sort_order = 4 // Top right of the intent selector. + sort_order = 2 // Corresponding to hotkey order. // Used by nymphs. /decl/intent/harm/binary diff --git a/code/modules/mob/mob_layering.dm b/code/modules/mob/mob_layering.dm index d98e8923528..e4ecb2b5007 100644 --- a/code/modules/mob/mob_layering.dm +++ b/code/modules/mob/mob_layering.dm @@ -7,9 +7,9 @@ var/last_layer = layer var/new_layer = get_base_layer() if(isturf(loc)) - var/turf/T = loc - if(T.pixel_z < 0) - new_layer = T.layer + 0.25 + var/turf/my_turf = loc + if(my_turf.pixel_z < 0 && !my_turf.get_supporting_platform()) + new_layer = my_turf.layer + 0.25 else if(buckled && buckled.buckle_layer_above) new_layer = buckled.layer + ((buckled.dir == SOUTH) ? -0.01 : 0.01) else if(length(grabbed_by)) @@ -97,8 +97,14 @@ // Update offsets from loc. var/turf/floor/ext = loc - if(istype(ext) && ext.height < 0) - new_pixel_z += ext.pixel_z + if(istype(ext)) + var/obj/structure/platform = ext.get_supporting_platform() + if(platform) + new_pixel_z += platform.pixel_z + else if(ext.height < 0) + new_pixel_z += ext.pixel_z + + // Check for catwalks/supporting platforms. // Update offsets from our buckled atom. if(buckled && buckled.buckle_pixel_shift) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 21c41101077..a934d39d2e3 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -268,8 +268,8 @@ /mob/proc/set_move_intent(var/decl/move_intent/next_intent) if(next_intent && move_intent != next_intent && next_intent.can_be_used_by(src)) move_intent = next_intent - if(istype(hud_used)) - hud_used.move_intent.icon_state = move_intent.hud_icon_state + if(istype(hud_used) && hud_used.move_intent) + hud_used.move_intent.update_icon() return TRUE return FALSE diff --git a/code/modules/mob/skills/skill.dm b/code/modules/mob/skills/skill.dm index 87d3326bab0..809e917ed76 100644 --- a/code/modules/mob/skills/skill.dm +++ b/code/modules/mob/skills/skill.dm @@ -361,6 +361,7 @@ category = /decl/skill_category/engineering uid = "skill_engines" fallback_key = "/decl/hierarchy/skill/engineering/engines" + // TODO: These strings should be modified by the supermatter modpack somehow... desc = "Describes your knowledge of the various engine types common on space stations, such as the PACMAN, singularity, supermatter or RUST engine." levels = list( "Unskilled" = "You know that \"delamination\" is a bad thing and that you should stay away from the singularity. You know the engine provides power, but you're unclear on the specifics. If you were to try to set up the engine, you would need someone to talk you through every detail--and even then, you'd probably make deadly mistakes.
    - You can read the SM monitor readings with 40% error. This decreases with level.", diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 866c96a62c6..6ec207ea1b8 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -98,8 +98,7 @@ O.add_ai_verbs() O.rename_self("ai",1) - spawn(0) // Mobs still instantly del themselves, thus we need to spawn or O will never be returned - qdel(src) + qdel(src) return O //human -> robot @@ -136,7 +135,7 @@ RAISE_EVENT(/decl/observ/cyborg_created, O) O.Namepick() - qdel(src) // Queues us for a hard delete + qdel(src) return O /mob/living/human/proc/corgize() @@ -188,8 +187,7 @@ to_chat(new_mob, "You suddenly feel more... animalistic.") - spawn() - qdel(src) + qdel(src) return /mob/proc/Animalize() diff --git a/code/modules/modular_computers/computers/modular_computer/interaction.dm b/code/modules/modular_computers/computers/modular_computer/interaction.dm index 6bb4a4f41b8..4adb8e75dd2 100644 --- a/code/modules/modular_computers/computers/modular_computer/interaction.dm +++ b/code/modules/modular_computers/computers/modular_computer/interaction.dm @@ -85,23 +85,22 @@ return TRUE . = ..() -/obj/item/modular_computer/attackby(var/obj/item/W, var/mob/user) +/obj/item/modular_computer/attackby(var/obj/item/used_item, var/mob/user) + var/datum/extension/assembly/assembly = get_extension(src, /datum/extension/assembly) - . = assembly.attackby(W, user) - if(.) + if(assembly?.attackby(used_item, user)) update_verbs() return TRUE - if(IS_PEN(W) && (W.w_class <= ITEM_SIZE_TINY) && stores_pen) + if(IS_PEN(used_item) && (used_item.w_class <= ITEM_SIZE_TINY) && stores_pen) if(istype(stored_pen)) - to_chat(user, "There is already a pen in [src].") - return TRUE - if(!user.try_unequip(W, src)) - return TRUE - stored_pen = W - update_verbs() - to_chat(user, "You insert [W] into [src].") + to_chat(user, SPAN_NOTICE("There is already \a [stored_pen] in \the [src].")) + else if(user.try_unequip(used_item, src)) + stored_pen = used_item + update_verbs() + to_chat(user, SPAN_NOTICE("You insert \the [used_item] into [src].")) return TRUE + return ..() /obj/item/modular_computer/examine(mob/user) @@ -136,10 +135,13 @@ /obj/item/modular_computer/get_alt_interactions(var/mob/user) . = ..() - LAZYADD(., /decl/interaction_handler/remove_id/modular_computer) - LAZYADD(., /decl/interaction_handler/remove_pen/modular_computer) - LAZYADD(., /decl/interaction_handler/emergency_shutdown) - LAZYADD(., /decl/interaction_handler/remove_chargestick) + var/static/list/_modular_computer_interactions = list( + /decl/interaction_handler/remove_id/modular_computer, + /decl/interaction_handler/remove_pen/modular_computer, + /decl/interaction_handler/emergency_shutdown, + /decl/interaction_handler/remove_chargestick + ) + LAZYADD(., _modular_computer_interactions) // // Remove ID @@ -181,6 +183,7 @@ icon = 'icons/screen/radial.dmi' icon_state = "radial_power_off" expected_target_type = /obj/item/modular_computer + examine_desc = "perform an emergency shutdown" /decl/interaction_handler/emergency_shutdown/is_possible(atom/target, mob/user, obj/item/prop) . = ..() @@ -201,6 +204,7 @@ icon = 'icons/screen/radial.dmi' icon_state = "radial_eject" expected_target_type = /obj/item/modular_computer + examine_desc = "remove a chargestick" /decl/interaction_handler/remove_chargestick/is_possible(atom/target, mob/user, obj/item/prop) . = ..() diff --git a/code/modules/modular_computers/computers/subtypes/dev_laptop.dm b/code/modules/modular_computers/computers/subtypes/dev_laptop.dm index ad7c698d6d8..894b912aff9 100644 --- a/code/modules/modular_computers/computers/subtypes/dev_laptop.dm +++ b/code/modules/modular_computers/computers/subtypes/dev_laptop.dm @@ -15,7 +15,7 @@ /decl/material/solid/silicon = MATTER_AMOUNT_REINFORCEMENT, ) var/icon_state_closed = "laptop-closed" - + /obj/item/modular_computer/laptop/on_update_icon() if(anchored) ..() @@ -35,6 +35,7 @@ name = "Open Laptop" expected_target_type = /obj/item/modular_computer/laptop interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEEDS_TURF + examine_desc = "open or close $TARGET_THEM$" /decl/interaction_handler/laptop_open/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/modular_computer/laptop/L = target diff --git a/code/modules/modular_computers/computers/subtypes/preset_console.dm b/code/modules/modular_computers/computers/subtypes/preset_console.dm index c726c3ac6a2..1bc6fd711de 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_console.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_console.dm @@ -48,7 +48,6 @@ /obj/machinery/computer/modular/preset/engineering default_software = list( /datum/computer_file/program/power_monitor, - /datum/computer_file/program/supermatter_monitor, /datum/computer_file/program/alarm_monitor, /datum/computer_file/program/atmos_control, /datum/computer_file/program/rcon_console, diff --git a/code/modules/modular_computers/computers/subtypes/preset_telescreen.dm b/code/modules/modular_computers/computers/subtypes/preset_telescreen.dm index 23e4122b908..284372c00bf 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_telescreen.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_telescreen.dm @@ -68,8 +68,7 @@ default_software = list( /datum/computer_file/program/alarm_monitor, /datum/computer_file/program/camera_monitor, - /datum/computer_file/program/shields_monitor, - /datum/computer_file/program/supermatter_monitor + /datum/computer_file/program/shields_monitor ) /obj/machinery/computer/modular/telescreen/preset/entertainment diff --git a/code/modules/modular_computers/file_system/programs/generic/folding.dm b/code/modules/modular_computers/file_system/programs/generic/folding.dm index 0ea98dcadb4..5d49c83988b 100644 --- a/code/modules/modular_computers/file_system/programs/generic/folding.dm +++ b/code/modules/modular_computers/file_system/programs/generic/folding.dm @@ -23,7 +23,7 @@ var/started_on = 0 // When the program started some science. var/current_interval = 0 // How long the current interval will be. - var/next_event = 0 // in world timeofday, when the next event is scheduled to pop. + var/next_event = 0 // based on world.timeofday, when the next event is scheduled to pop. var/program_status = PROGRAM_STATUS_RUNNING // Program periodically needs a restart, increases crash chance slightly over time. var/crashed_at = 0 // When the program crashed. @@ -109,7 +109,7 @@ "Simulating Alien Abductions", "Scanning Pigeons", "Iterating Chaos Array", - "Abstracting Supermatter", + "Abstracting Exotic Matter", "Adjusting Social Network", "Recalculating Clown Principle" ) diff --git a/code/modules/modular_computers/hardware/lan_port.dm b/code/modules/modular_computers/hardware/lan_port.dm index 1c0701880ef..8ffecb3d26a 100644 --- a/code/modules/modular_computers/hardware/lan_port.dm +++ b/code/modules/modular_computers/hardware/lan_port.dm @@ -77,10 +77,10 @@ to_chat(user, SPAN_WARNING("You need five lengths of network cable for \the [parent].")) return TRUE - user.visible_message(SPAN_NOTICE("\The [user] adds cables to the \the [parent]."), "You start adding cables to \the [parent] frame...") + user.visible_message(SPAN_NOTICE("\The [user] adds cables to \the [parent]."), "You start adding cables to \the [parent] frame...") if(do_after(user, 20, parent)) if(!terminal && (loc == parent) && parent.components_are_accessible(type) && !check_terminal_block(T) && C.use(5)) - user.visible_message(SPAN_NOTICE("\The [user] has added cables to the \the [parent]!"), "You add cables to the \the [parent].") + user.visible_message(SPAN_NOTICE("\The [user] has added cables to \the [parent]!"), "You add cables to \the [parent].") set_terminal() return TRUE if(IS_WIRECUTTER(I) && terminal) diff --git a/code/modules/modular_computers/networking/machinery/telecomms.dm b/code/modules/modular_computers/networking/machinery/telecomms.dm index 9c936561586..93f7f7cd491 100644 --- a/code/modules/modular_computers/networking/machinery/telecomms.dm +++ b/code/modules/modular_computers/networking/machinery/telecomms.dm @@ -127,7 +127,7 @@ var/global/list/telecomms_hubs = list() if(overloaded_for > 0) overloaded_for-- -/// Accepts either a raw frequency (numeric), or or a frequency/key string, and returns the associated channel data. +/// Accepts either a raw frequency (numeric), or a frequency/key string, and returns the associated channel data. /obj/machinery/network/telecomms_hub/proc/get_channel_from_freq_or_key(var/cid) cid = "[cid]" . = LAZYACCESS(channels_by_frequency, cid) || LAZYACCESS(channels_by_key, cid) diff --git a/code/modules/modular_computers/terminal/terminal.dm b/code/modules/modular_computers/terminal/terminal.dm index 994bbc2f774..dec38ed92f1 100644 --- a/code/modules/modular_computers/terminal/terminal.dm +++ b/code/modules/modular_computers/terminal/terminal.dm @@ -98,7 +98,7 @@ account_name = "LOCAL" else account_name = "GUEST" - content += "
    >[account_name]:/[current_disk?.get_dir_path(current_directory, TRUE)]
    " + content += "
    \>[account_name]:/[current_disk?.get_dir_path(current_directory, TRUE)]
    " content += "type `man` for a list of available commands." panel.set_content("[jointext(content, "
    ")]
    ") @@ -159,7 +159,7 @@ /datum/terminal/proc/parse_directory(directory_path, create_directories = FALSE) var/datum/file_storage/target_disk = current_disk var/datum/computer_file/directory/root_dir = current_directory - + if(!length(directory_path)) return list(target_disk, root_dir) @@ -168,8 +168,8 @@ // Otherwise, we append the working directory path to the passed path. var/list/directories = splittext(directory_path, "/") - - // When splitting the text, there could be blank strings at either end, so remove them. If there's any in the body of the path, there was a + + // When splitting the text, there could be blank strings at either end, so remove them. If there's any in the body of the path, there was a // missed input, so leave them. if(!length(directories[1])) directories.Cut(1, 2) @@ -194,7 +194,7 @@ if(!target_disk) // Invalid disk entered. return OS_DIR_NOT_FOUND directories.Cut(1, 2) - + break // Any further use of ../ is handled by the hard drive. // If we were only pathing to the parent of a directory or to a disk, we can return early. @@ -208,7 +208,7 @@ var/datum/computer_file/directory/target_directory = target_disk.parse_directory(final_path, create_directories) if(!istype(target_directory)) return OS_DIR_NOT_FOUND - + return list(target_disk, target_directory) // Returns list(/datum/file_storage, /datum/computer_file/directory, /datum/computer_file) on success. Returns error code on failure. @@ -221,7 +221,7 @@ var/list/dirs_and_file = splittext(file_path, "/") if(!length(dirs_and_file)) return OS_DIR_NOT_FOUND - + // Join together everything but the filename into a path. var/list/file_loc = parse_directory(jointext(dirs_and_file, "/", 1, dirs_and_file.len)) if(!islist(file_loc)) // Errored! @@ -231,7 +231,7 @@ var/datum/computer_file/directory/target_dir = file_loc[2] if(!istype(target_disk)) return OS_DIR_NOT_FOUND - + var/filename = dirs_and_file[dirs_and_file.len] var/datum/computer_file/target_file = target_disk.get_file(filename, target_dir) if(!istype(target_file)) @@ -265,5 +265,5 @@ return "I/O error, Harddrive may be non-functional" if(OS_NETWORK_ERROR) return "Unable to connect to the network" - + return "An unspecified error occured." \ No newline at end of file diff --git a/code/modules/multiz/ladder.dm b/code/modules/multiz/ladder.dm index 2bdad6e2350..b86299bcf5f 100644 --- a/code/modules/multiz/ladder.dm +++ b/code/modules/multiz/ladder.dm @@ -71,8 +71,8 @@ var/turf/L = loc if(HasBelow(z) && istype(L) && L.is_open()) var/failed - for(var/obj/structure/catwalk/catwalk in loc) - if(catwalk.plated_tile) + for(var/obj/structure/platform in loc) + if(!platform.is_z_passable()) failed = TRUE break if(!failed) @@ -86,8 +86,8 @@ var/turf/T = GetAbove(src) if(istype(T) && T.is_open()) var/failed - for(var/obj/structure/catwalk/catwalk in T) - if(catwalk.plated_tile) + for(var/obj/structure/platform in T) + if(!platform.is_z_passable()) failed = TRUE break if(!failed) @@ -214,18 +214,18 @@ if(!istype(T) || !T.is_open()) to_chat(M, SPAN_WARNING("The ceiling is in the way!")) return null - for(var/obj/structure/catwalk/catwalk in target_up.loc) - if(catwalk.plated_tile) - to_chat(M, SPAN_WARNING("\The [catwalk] is in the way!")) + for(var/obj/structure/platform in target_up.loc) + if(!platform.is_z_passable()) + to_chat(M, SPAN_WARNING("\The [platform] is in the way!")) return null if(. == target_down) var/turf/T = loc if(!istype(T) || !T.is_open()) to_chat(M, SPAN_WARNING("\The [loc] is in the way!")) return null - for(var/obj/structure/catwalk/catwalk in loc) - if(catwalk.plated_tile) - to_chat(M, SPAN_WARNING("\The [catwalk] is in the way!")) + for(var/obj/structure/platform in loc) + if(!platform.is_z_passable()) + to_chat(M, SPAN_WARNING("\The [platform] is in the way!")) return null /mob/proc/may_climb_ladders(var/ladder) diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index 69ee279edba..00b82e3b5ac 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -173,6 +173,14 @@ // Whether or not this level permits things like graffiti and filth to persist across rounds. var/permit_persistence = FALSE + // Submap loading values, passed back via getters like get_subtemplate_budget(). + /// A point budget to spend on subtemplates (see template costs) + var/subtemplate_budget = 0 + /// A string identifier for the category of subtemplates to draw from for this level. + var/subtemplate_category = null + /// A specific area to use when determining where to place subtemplates. + var/subtemplate_area = null + /datum/level_data/New(var/_z_level, var/defer_level_setup = FALSE) . = ..() level_z = _z_level @@ -181,7 +189,6 @@ initialize_level_id() SSmapping.register_level_data(src) - setup_ambient() setup_exterior_atmosphere() if(SSmapping.initialized && !defer_level_setup) setup_level_data() @@ -233,6 +240,7 @@ if(!skip_gen) generate_level() after_generate_level() + setup_ambient() // Determine our relative positioning. // First find an appropriate origin point. @@ -418,16 +426,22 @@ // /// Helper proc for subtemplate generation. Returns a point budget to spend on subtemplates. /datum/level_data/proc/get_subtemplate_budget() - return 0 + return subtemplate_budget /// Helper proc for subtemplate generation. Returns a string identifier for a general category of template. /datum/level_data/proc/get_subtemplate_category() - return + return subtemplate_category /// Helper proc for subtemplate generation. Returns a bitflag of template flags that must not be present for a subtemplate to be considered available. /datum/level_data/proc/get_subtemplate_blacklist() return /// Helper proc for subtemplate generation. Returns a bitflag of template flags that must be present for a subtemplate to be considered available. /datum/level_data/proc/get_subtemplate_whitelist() return +/// Helper proc for getting areas associated with placable submaps on this level. +/datum/level_data/proc/get_subtemplate_areas(template_category, blacklist, whitelist) + if(subtemplate_area) + return islist(subtemplate_area) ? subtemplate_area : list(subtemplate_area) + if(base_area) + return list(base_area) ///Called when setting up the level. Apply generators and anything that modifies the turfs of the level. /datum/level_data/proc/generate_level() @@ -461,12 +475,34 @@ for(var/gen_type in map_gen) new gen_type(origx, origy, level_z, endx, endy, FALSE, TRUE, get_base_area_instance()) +/// Helper proc for placing mobs on a level after level creation. +/datum/level_data/proc/get_mobs_to_populate_level() + return + ///Called during level setup. Run anything that should happen only after the map is fully generated. /datum/level_data/proc/after_generate_level() + build_border() + if(daycycle_id && daycycle_type) SSdaycycle.register_level(level_z, daycycle_id, daycycle_type) + var/list/mobs_to_spawn = get_mobs_to_populate_level() + if(length(mobs_to_spawn)) + for(var/list/mob_category in mobs_to_spawn) + var/list/mob_types = mob_category[1] + var/mob_turf = mob_category[2] + var/mob_count = mob_category[3] + var/sanity = 1000 + while(mob_count && sanity) + sanity-- + var/turf/place_mob_at = locate(rand(level_inner_min_x, level_inner_max_x), rand(level_inner_min_y, level_inner_max_y), level_z) + if(istype(place_mob_at, mob_turf) && !(locate(/mob/living) in place_mob_at)) + var/mob_type = pickweight(mob_types) + new mob_type(place_mob_at) + mob_count-- + CHECK_TICK + ///Changes anything named we may need to rename accordingly to the parent location name. For instance, exoplanets levels. /datum/level_data/proc/adapt_location_name(var/location_name) SHOULD_CALL_PARENT(TRUE) @@ -720,6 +756,12 @@ INITIALIZE_IMMEDIATE(/obj/abstract/level_data_spawner) level_flags = (ZLEVEL_CONTACT|ZLEVEL_PLAYER|ZLEVEL_SEALED) filler_turf = /turf/unsimulated/dark_filler +// Used in order to avoid making the level too large. Only works if loaded prior to SSmapping init... it's unclear if this really does much. +/datum/level_data/unit_test/after_template_load(var/datum/map_template/template) + . = ..() + level_max_width ||= template.width + level_max_height ||= template.height + /datum/level_data/overmap name = "Sensor Display" level_flags = ZLEVEL_SEALED @@ -736,15 +778,13 @@ INITIALIZE_IMMEDIATE(/obj/abstract/level_data_spawner) return ..() /datum/level_data/mining_level/asteroid - base_turf = /turf/floor + base_turf = /turf/floor/barren + filler_turf = /turf/space level_generators = list( /datum/random_map/automata/cave_system, /datum/random_map/noise/ore ) -/datum/level_data/proc/get_subtemplate_areas(template_category, blacklist, whitelist) - return list(base_area) - ///Try to allocate the given amount of POIs onto our level. Returns the template types that were spawned /datum/level_data/proc/spawn_subtemplates(budget = 0, template_category, blacklist, whitelist) @@ -828,13 +868,10 @@ INITIALIZE_IMMEDIATE(/obj/abstract/level_data_spawner) load_subtemplate(T, template) return template -///Actually handles loading a template template at the given turf. +///Actually handles loading a template at the given turf. /datum/level_data/proc/load_subtemplate(turf/central_turf, datum/map_template/template) if(!template) return FALSE - for(var/turf/T in template.get_affected_turfs(central_turf, TRUE)) - for(var/mob/living/simple_animal/monster in T) - qdel(monster) template.load(central_turf, centered = TRUE) return TRUE diff --git a/code/modules/multiz/mobile_ladder.dm b/code/modules/multiz/mobile_ladder.dm index 2eacd7a03ac..dec71c399ff 100644 --- a/code/modules/multiz/mobile_ladder.dm +++ b/code/modules/multiz/mobile_ladder.dm @@ -130,6 +130,7 @@ /decl/interaction_handler/ladder_fold name = "Fold Ladder" expected_target_type = /obj/structure/ladder/mobile + examine_desc = "fold $TARGET_THEM$ up" /decl/interaction_handler/ladder_fold/invoked(atom/target, mob/user, obj/item/prop) var/obj/structure/ladder/mobile/L diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index a3009921249..573c28223f1 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -91,9 +91,6 @@ if(!has_gravity()) return - if(throwing) - return - if(can_fall()) begin_falling(lastloc, below) @@ -115,7 +112,7 @@ //For children to override /atom/movable/proc/can_fall(var/anchor_bypass = FALSE, var/turf/location_override = loc) - if(!simulated) + if(immune_to_floor_hazards()) return FALSE if(anchored && !anchor_bypass) @@ -225,7 +222,7 @@ for(var/mob/living/M in landing.contents) if(M == src) continue - visible_message("\The [src] hits \the [M.name]!") + visible_message("\The [src] hits \the [M]!") M.take_overall_damage(fall_damage) return TRUE return FALSE diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index 01db42e5261..c08433c2a3c 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -9,7 +9,7 @@ return FALSE else if(direction == DOWN) - if(!is_open() || !HasBelow(z) || (locate(/obj/structure/catwalk) in src)) + if(!is_open() || !HasBelow(z) || get_supporting_platform()) return FALSE if(check_neighbor_canzpass) var/turf/T = GetBelow(src) diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 32f5f80e691..04644bcdde7 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -166,7 +166,7 @@ if (destruction_timer) deltimer(destruction_timer) destruction_timer = null - if (old_loc.z != loc.z) + if (old_loc?.z != loc?.z) // Null checking in case of qdel(), observed with dirt effect falling through multiz. reset_internal_layering() else if (!destruction_timer) destruction_timer = ZM_DESTRUCTION_TIMER(src) diff --git a/code/modules/nano/modules/human_appearance.dm b/code/modules/nano/modules/human_appearance.dm index 4ba6ad96090..6e22447ab9c 100644 --- a/code/modules/nano/modules/human_appearance.dm +++ b/code/modules/nano/modules/human_appearance.dm @@ -116,7 +116,7 @@ hair_styles[++hair_styles.len] = list("hairstyle" = hair_decl.name, "ref" = "\ref[hair_decl]") data["hair_styles"] = hair_styles var/hairstyle = GET_HAIR_STYLE(owner) - var/decl/sprite_accessory/hair = GET_DECL(hairstyle) + var/decl/sprite_accessory/hair = GET_DECL(hairstyle) || GET_DECL(/decl/sprite_accessory/hair/bald) data["hair_style"] = hair.name data["change_facial_hair"] = can_change(APPEARANCE_FACIAL_HAIR) @@ -127,7 +127,7 @@ facial_hair_styles[++facial_hair_styles.len] = list("facialhairstyle" = facial_hair_decl.name, "ref" = "\ref[facial_hair_decl]") data["facial_hair_styles"] = facial_hair_styles var/facial_hairstyle = GET_FACIAL_HAIR_STYLE(owner) - var/decl/sprite_accessory/facial_hair = GET_DECL(facial_hairstyle) + var/decl/sprite_accessory/facial_hair = GET_DECL(facial_hairstyle) || GET_DECL(/decl/sprite_accessory/facial_hair/shaved) data["facial_hair_style"] = facial_hair.name data["change_hair_color"] = can_change(APPEARANCE_HAIR_COLOR) diff --git a/code/modules/organs/ailments/_ailment.dm b/code/modules/organs/ailments/_ailment.dm index 7bcc0c24da6..713dfcdc7aa 100644 --- a/code/modules/organs/ailments/_ailment.dm +++ b/code/modules/organs/ailments/_ailment.dm @@ -1,10 +1,9 @@ /datum/ailment + abstract_type = /datum/ailment var/name // Descriptive name, primarily used for adminbus. var/timer_id // Current timer waiting to proc next symptom message. var/min_time = 2 MINUTES // Minimum time between symptom messages. var/max_time = 5 MINUTES // Maximum time between symptom messages. - var/category = /datum/ailment // Used similar to hierarchies, if category == type then the - // ailment is a category and won't be applied to organs. var/obj/item/organ/organ // Organ associated with the ailment (ailment is in organ.ailments list). // Requirements before applying to a target. diff --git a/code/modules/organs/ailments/ailment_codex.dm b/code/modules/organs/ailments/ailment_codex.dm index 40cc20eb341..566446dd17e 100644 --- a/code/modules/organs/ailments/ailment_codex.dm +++ b/code/modules/organs/ailments/ailment_codex.dm @@ -40,7 +40,7 @@ ailment_table += "[name_column][treatment_column]" for(var/atype in subtypesof(/datum/ailment)) var/datum/ailment/ailment = get_ailment_reference(atype) - if(!ailment.name || show_robotics_recipes != ailment.applies_to_prosthetics || ailment.hidden_from_codex) + if(!ailment || show_robotics_recipes != ailment.applies_to_prosthetics || ailment.hidden_from_codex) continue ailment_table += "[ailment.name]" var/list/ailment_cures = list() diff --git a/code/modules/organs/ailments/ailments_medical.dm b/code/modules/organs/ailments/ailments_medical.dm index 4cf930ac0f9..c9d3dad9751 100644 --- a/code/modules/organs/ailments/ailments_medical.dm +++ b/code/modules/organs/ailments/ailments_medical.dm @@ -1,5 +1,5 @@ /datum/ailment/head - category = /datum/ailment/head + abstract_type = /datum/ailment/head applies_to_organ = list(BP_HEAD) /datum/ailment/head/headache diff --git a/code/modules/organs/ailments/faults/_fault.dm b/code/modules/organs/ailments/faults/_fault.dm index 8219540a8dc..52a9b747a84 100644 --- a/code/modules/organs/ailments/faults/_fault.dm +++ b/code/modules/organs/ailments/faults/_fault.dm @@ -1,7 +1,7 @@ /datum/ailment/fault applies_to_robotics = TRUE applies_to_prosthetics = TRUE - category = /datum/ailment/fault + abstract_type = /datum/ailment/fault treated_by_item_type = list( /obj/item/stack/nanopaste, /obj/item/stack/tape_roll/duct_tape diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index faa52cb2323..35f3e0bbe61 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -62,7 +62,7 @@ var/artery_name = "artery" // Flavour text for cartoid artery, aorta, etc. var/arterial_bleed_severity = 1 // Multiplier for bleeding in a limb. var/tendon_name = "tendon" // Flavour text for Achilles tendon, etc. - var/cavity_name = "cavity" + var/cavity_name = "intramuscular cavity" // Surgery vars. var/cavity_max_w_class = ITEM_SIZE_TINY //this is increased if bigger organs spawn by default inside @@ -70,8 +70,6 @@ var/stage = 0 var/cavity = 0 - var/list/unarmed_attacks - var/atom/movable/applied_pressure var/atom/movable/splinted @@ -141,10 +139,6 @@ _icon_cache_key = null . = ..() skin_blend = bodytype.limb_blend - for(var/attack_type in species.unarmed_attacks) - var/decl/natural_attack/attack = GET_DECL(attack_type) - if(istype(attack) && (organ_tag in attack.usable_with_limbs)) - LAZYADD(unarmed_attacks, attack_type) update_icon() /obj/item/organ/external/set_bodytype(decl/bodytype/new_bodytype, override_material = null, apply_to_internal_organs = TRUE) @@ -318,7 +312,7 @@ //Handles removing internal organs/implants/items still in the detached limb. /obj/item/organ/external/proc/try_remove_internal_item(var/obj/item/used_item, var/mob/user) - if(stage == 0 && used_item.sharp) + if(stage == 0 && used_item.is_sharp()) user.visible_message(SPAN_NOTICE("\The [user] cuts \the [src] open with \the [used_item].")) stage++ return TRUE @@ -328,7 +322,7 @@ stage++ return TRUE - if(stage == 2 && (used_item.sharp || IS_HEMOSTAT(used_item) || IS_WIRECUTTER(used_item))) + if(stage == 2 && (used_item.is_sharp() || IS_HEMOSTAT(used_item) || IS_WIRECUTTER(used_item))) var/list/radial_buttons = make_item_radial_menu_choices(get_contents_recursive()) if(LAZYLEN(radial_buttons)) var/obj/item/removing = show_radial_menu(user, src, radial_buttons, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(src)) diff --git a/code/modules/organs/external/head.dm b/code/modules/organs/external/head.dm index 1d6b432822c..b16d8919e29 100644 --- a/code/modules/organs/external/head.dm +++ b/code/modules/organs/external/head.dm @@ -12,7 +12,7 @@ amputation_point = "neck" encased = "skull" artery_name = "carotid artery" - cavity_name = "cranial" + cavity_name = "cranial cavity" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_HEALS_OVERKILL | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE var/glowing_eyes = FALSE @@ -20,6 +20,24 @@ var/forehead_graffiti var/graffiti_style +/obj/item/organ/external/head/get_natural_attacks() + if(!can_intake_reagents) + return null + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/bite) + return unarmed_attack + +/obj/item/organ/external/head/sharp_bite/get_natural_attacks() + if(!can_intake_reagents) + return null + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/bite/sharp) + return unarmed_attack + +/obj/item/organ/external/head/strong_bite/get_natural_attacks() + if(!can_intake_reagents) + return null + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/bite/strong) + return unarmed_attack + /obj/item/organ/external/head/proc/get_organ_eyes_overlay() if(!glowing_eyes && !owner?.has_chemical_effect(CE_GLOWINGEYES, 1)) return diff --git a/code/modules/organs/external/standard.dm b/code/modules/organs/external/standard.dm index 2beef64083b..3dfa745cdec 100644 --- a/code/modules/organs/external/standard.dm +++ b/code/modules/organs/external/standard.dm @@ -17,7 +17,7 @@ parent_organ = null encased = "ribcage" artery_name = "aorta" - cavity_name = "thoracic" + cavity_name = "thoracic cavity" limb_flags = ORGAN_FLAG_HEALS_OVERKILL | ORGAN_FLAG_CAN_BREAK /obj/item/organ/external/chest/proc/get_current_skin() @@ -41,7 +41,7 @@ amputation_point = "lumbar" joint = "hip" artery_name = "iliac artery" - cavity_name = "abdominal" + cavity_name = "abdominal cavity" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_BREAK /obj/item/organ/external/groin/die() @@ -113,6 +113,13 @@ limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE organ_category = ORGAN_CATEGORY_STANCE +/obj/item/organ/external/foot/get_natural_attacks() + var/static/list/unarmed_attacks = list( + GET_DECL(/decl/natural_attack/stomp), + GET_DECL(/decl/natural_attack/kick) + ) + return unarmed_attacks + /obj/item/organ/external/foot/right organ_tag = BP_R_FOOT name = "right foot" @@ -122,6 +129,14 @@ joint = "right ankle" amputation_point = "right ankle" +/obj/item/organ/external/foot/avian/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/stomp/weak) + return unarmed_attack + +/obj/item/organ/external/foot/right/avian/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/stomp/weak) + return unarmed_attack + /obj/item/organ/external/hand organ_tag = BP_L_HAND name = "left hand" @@ -139,6 +154,10 @@ is_washable = TRUE var/gripper_type = /datum/inventory_slot/gripper/left_hand +/obj/item/organ/external/hand/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/punch) + return unarmed_attack + /obj/item/organ/external/hand/do_install(mob/living/human/target, affected, in_place, update_icon, detached) . = ..() if(. && owner && gripper_type) @@ -158,3 +177,11 @@ joint = "right wrist" amputation_point = "right wrist" gripper_type = /datum/inventory_slot/gripper/right_hand + +/obj/item/organ/external/hand/clawed/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/claws) + return unarmed_attack + +/obj/item/organ/external/hand/right/clawed/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/claws) + return unarmed_attack diff --git a/code/modules/organs/external/wounds/wound.dm b/code/modules/organs/external/wounds/wound.dm index 2c2746cd154..60219be1f1a 100644 --- a/code/modules/organs/external/wounds/wound.dm +++ b/code/modules/organs/external/wounds/wound.dm @@ -79,7 +79,7 @@ return 0 return (wound_damage() <= autoheal_cutoff) ? 1 : is_treated() -// checks whether the wound has been appropriately treated +/// checks whether the wound has been appropriately treated /datum/wound/proc/is_treated() if(!LAZYLEN(embedded_objects)) switch(damage_type) @@ -88,7 +88,7 @@ if(BURN) return salved - // Checks whether other other can be merged into src. +/// Checks whether other can be merged into src. /datum/wound/proc/can_merge_wounds(var/datum/wound/other) if (other.type != src.type) return 0 if (other.current_stage != src.current_stage) return 0 diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 92bcc2d8ed7..36d6a3ac531 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -482,9 +482,11 @@ return var/global/list/ailment_reference_cache = list() -/proc/get_ailment_reference(var/ailment_type) +/proc/get_ailment_reference(var/datum/ailment/ailment_type) if(!ispath(ailment_type, /datum/ailment)) return + if(TYPE_IS_ABSTRACT(ailment_type)) + return if(!global.ailment_reference_cache[ailment_type]) global.ailment_reference_cache[ailment_type] = new ailment_type return global.ailment_reference_cache[ailment_type] @@ -495,7 +497,7 @@ var/global/list/ailment_reference_cache = list() return . for(var/ailment_type in subtypesof(/datum/ailment)) var/datum/ailment/ailment = ailment_type - if(initial(ailment.category) == ailment_type) + if(TYPE_IS_ABSTRACT(ailment)) continue ailment = get_ailment_reference(ailment_type) if(ailment.can_apply_to(src)) diff --git a/code/modules/organs/organ_prosthetics.dm b/code/modules/organs/organ_prosthetics.dm index 03d8abee4ae..a107c79e810 100644 --- a/code/modules/organs/organ_prosthetics.dm +++ b/code/modules/organs/organ_prosthetics.dm @@ -9,7 +9,7 @@ // External organ procs: // Does this bodypart count as a modular limb, and if so, what kind? /obj/item/organ/external/proc/get_modular_limb_category() - return isnull(bodytype.modular_limb_tier) ? MODULAR_BODYPART_INVALID : bodytype.modular_limb_tier + return isnull(bodytype?.modular_limb_tier) ? MODULAR_BODYPART_INVALID : bodytype.modular_limb_tier // Checks if a limb could theoretically be removed. // Note that this does not currently bother checking if a child or internal organ is vital. diff --git a/code/modules/overmap/contacts/_contacts.dm b/code/modules/overmap/contacts/_contacts.dm index e4cab812e76..bde9a41d171 100644 --- a/code/modules/overmap/contacts/_contacts.dm +++ b/code/modules/overmap/contacts/_contacts.dm @@ -29,7 +29,6 @@ radar = image(loc = effect, icon = 'icons/obj/overmap.dmi', icon_state = "sensor_range") radar.color = source.color - radar.tag = "radar" radar.add_filter("blur", 1, list(type = "blur", size = 1)) radar.appearance_flags |= RESET_TRANSFORM | KEEP_APART radar.appearance_flags &= ~PIXEL_SCALE diff --git a/code/modules/overmap/disperser/disperser_charge.dm b/code/modules/overmap/disperser/disperser_charge.dm index 895e2955dc0..4ab02455666 100644 --- a/code/modules/overmap/disperser/disperser_charge.dm +++ b/code/modules/overmap/disperser/disperser_charge.dm @@ -7,7 +7,7 @@ matter = list( /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT, /decl/material/solid/metal/copper = MATTER_AMOUNT_SECONDARY, - /decl/material/solid/supermatter = MATTER_AMOUNT_TRACE + /decl/material/solid/exotic_matter = MATTER_AMOUNT_TRACE ) var/chargetype var/chargedesc diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm index 408db04970a..2a7e5b3fb64 100644 --- a/code/modules/overmap/overmap_shuttle.dm +++ b/code/modules/overmap/overmap_shuttle.dm @@ -7,7 +7,7 @@ var/fuel_consumption = 0 //Amount of moles of gas consumed per trip; If zero, then shuttle is magic and does not need fuel var/list/obj/structure/fuel_port/fuel_ports //the fuel ports of the shuttle (but usually just one) - category = /datum/shuttle/autodock/overmap + abstract_type = /datum/shuttle/autodock/overmap var/skill_needed = SKILL_BASIC var/landing_skill_needed = SKILL_EXPERT var/operator_skill = SKILL_MIN diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm index 20414a0cf0a..eaeabd18e3a 100644 --- a/code/modules/overmap/ships/landable.dm +++ b/code/modules/overmap/ships/landable.dm @@ -231,14 +231,14 @@ if(SHIP_STATUS_LANDED) var/obj/effect/overmap/visitable/location = loc if(istype(loc, /obj/effect/overmap/visitable/sector)) - return "Landed on \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Landed on \the [location]. Use secondary thrust to get clear before activating primary engines." if(istype(loc, /obj/effect/overmap/visitable/ship)) - return "Docked with \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Docked with \the [location]. Use secondary thrust to get clear before activating primary engines." return "Docked with an unknown object." if(SHIP_STATUS_ENCOUNTER) var/datum/shuttle/autodock/overmap/child_shuttle = SSshuttle.shuttles[shuttle] var/obj/effect/overmap/visitable/location = global.overmap_sectors[num2text(child_shuttle.current_location.z)] - return "Maneuvering nearby \the [location.name]." + return "Maneuvering nearby \the [location]." if(SHIP_STATUS_TRANSIT) return "Maneuvering under secondary thrust." if(SHIP_STATUS_OVERMAP) diff --git a/code/modules/overmap/ships/ship_physics.dm b/code/modules/overmap/ships/ship_physics.dm index 66e85a3f0b9..28e71cf1f5b 100644 --- a/code/modules/overmap/ships/ship_physics.dm +++ b/code/modules/overmap/ships/ship_physics.dm @@ -36,31 +36,43 @@ /obj/effect/overmap/visitable/ship/proc/recalculate_vessel_mass() var/list/zones = list() + // for(var/turf/tile in area) is an implied in-world loop + // an in-world loop per area is very bad, so instead + // we do one in-world loop and check area + var/list/areas = list() + // create an associative list of area -> TRUE so that lookup is faster for(var/area/A in get_areas()) - - // Do not include space please - if(istype(A, world.area)) + if(istype(A, world.area)) // exclude the base area continue + areas[A] = TRUE + var/start_z = min(map_z) + var/end_z = max(map_z) + if(!start_z || !end_z) + return initial(vessel_mass) // This shouldn't happen ideally so just go with the initial vessel mass + for(var/z_level in start_z to end_z) + var/datum/level_data/z_data = SSmapping.levels_by_z[z_level] + for(var/turf/tile in block(z_data.level_inner_min_x, z_data.level_inner_min_y, z_level, z_data.level_inner_max_x, z_data.level_inner_max_y)) + var/area/tile_area = tile.loc + if(!tile_area || !areas[tile_area]) + continue - for(var/turf/T in A) - - if(!T.simulated || T.is_open()) + if(!tile.simulated || tile.is_open()) continue . += DEFAULT_TURF_MASS - if(istype(T, /turf/wall)) - var/turf/wall/W = T - if(W.material) - . += W.material.weight * 5 - if(W.reinf_material) - . += W.reinf_material.weight * 5 - if(W.girder_material) - . += W.girder_material.weight * 5 + if(istype(tile, /turf/wall)) + var/turf/wall/wall_tile = tile + if(wall_tile.material) + . += wall_tile.material.weight * 5 + if(wall_tile.reinf_material) + . += wall_tile.reinf_material.weight * 5 + if(wall_tile.girder_material) + . += wall_tile.girder_material.weight * 5 - if(T.zone) - zones |= T.zone + if(tile.zone) + zones[tile.zone] = TRUE // assoc list for fast deduplication - for(var/atom/movable/C in T) + for(var/atom/movable/C as anything in tile) // as anything is safe here since only movables can be in turf contents if(!C.simulated) continue . += C.get_mass() @@ -69,8 +81,9 @@ continue . += C2.get_mass() - for(var/zone/Z in zones) - . += Z.air.get_mass() + // loop over keys of all zones in the list + for(var/zone/zone as anything in zones) + . += zone.air.get_mass() // Convert kilograms to metric tonnes. . = . / 1000 \ No newline at end of file diff --git a/code/modules/paperwork/carbonpaper.dm b/code/modules/paperwork/carbonpaper.dm index 72eb84cc491..5991f75c6c4 100644 --- a/code/modules/paperwork/carbonpaper.dm +++ b/code/modules/paperwork/carbonpaper.dm @@ -54,8 +54,9 @@ // Carbon Paper Alt Interactions ///////////////////////////////////////////////// /decl/interaction_handler/carbon_paper_remove - name = "remove carbon-copy" + name = "Remove Carbon-Copy" expected_target_type = /obj/item/paper/carbon + examine_desc = "remove the carbon-copy" /decl/interaction_handler/carbon_paper_remove/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/paper/carbon/paper = target diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index d25c1d6cbe5..cd40acd76d9 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -198,6 +198,7 @@ /decl/interaction_handler/clipboard_remove_pen name = "Remove Pen" expected_target_type = /obj/item/clipboard + examine_desc = "remove the pen" /decl/interaction_handler/clipboard_remove_pen/is_possible(atom/target, mob/user, obj/item/prop) . = ..() diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 92a37e75a1b..02428e20f70 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -636,6 +636,8 @@ var/global/datum/topic_state/default/paper_state/paper_topic_state = new /decl/interaction_handler/scroll/furl name = "Furl Scroll" + examine_desc = "furl $TARGET_THEM$" /decl/interaction_handler/scroll/unfurl name = "Unfurl Scroll" + examine_desc = "unfurl $TARGET_THEM$" diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index bf75bc60846..16a2f1f7ab9 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -40,7 +40,7 @@ // merging bundles else if(istype(W, /obj/item/paper_bundle) && merge(W, user, cur_page)) - to_chat(user, SPAN_NOTICE("You add \the [W.name] to \the [name].")) + to_chat(user, SPAN_NOTICE("You add \the [W] to \the [name].")) return TRUE // burning @@ -517,6 +517,7 @@ /decl/interaction_handler/rename/paper_bundle name = "Rename Bundle" expected_target_type = /obj/item/paper_bundle + examine_desc = "rename $TARGET_THEM$" /decl/interaction_handler/rename/paper_bundle/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/paper_bundle/bundle = target diff --git a/code/modules/paperwork/paper_plane.dm b/code/modules/paperwork/paper_plane.dm index ac8ebc5d961..2e183472066 100644 --- a/code/modules/paperwork/paper_plane.dm +++ b/code/modules/paperwork/paper_plane.dm @@ -77,6 +77,7 @@ /decl/interaction_handler/make_paper_plane name = "Fold Into Paper Plane" expected_target_type = /obj/item/paper + examine_desc = "make a paper plane" /decl/interaction_handler/make_paper_plane/is_possible(obj/item/paper/target, mob/user, obj/item/prop) return ..() && !target.is_crumpled @@ -91,4 +92,4 @@ /obj/item/paper/get_alt_interactions(mob/user) . = ..() - LAZYDISTINCTADD(., /decl/interaction_handler/make_paper_plane) \ No newline at end of file + LAZYADD(., /decl/interaction_handler/make_paper_plane) \ No newline at end of file diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 70b9b4c453d..3622d047f5c 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -155,6 +155,7 @@ /decl/interaction_handler/paper_bin_dump_contents name = "Dump Contents" expected_target_type = /obj/item/paper_bin + examine_desc = "empty $TARGET_THEM$" /decl/interaction_handler/paper_bin_dump_contents/is_possible(var/obj/item/paper_bin/target, mob/user, obj/item/prop) return ..() && target.amount > 0 diff --git a/code/modules/paperwork/papershredder.dm b/code/modules/paperwork/papershredder.dm index 7905a718674..a5b56cf128f 100644 --- a/code/modules/paperwork/papershredder.dm +++ b/code/modules/paperwork/papershredder.dm @@ -89,7 +89,7 @@ /obj/machinery/papershredder/proc/is_bin_empty() return !(length(shredder_bin) > 0 && cached_total_matter) -/obj/machinery/papershredder/proc/can_shred(var/obj/item/I, var/mob/user = null) +/obj/machinery/papershredder/proc/can_shred_document(var/obj/item/I, var/mob/user = null) if(!istype(I)) if(user) to_chat(user, SPAN_WARNING("\The [I] cannot be shredded by \the [src]!")) @@ -120,7 +120,7 @@ empty_bin(user, used_item) return TRUE - else if(!trying_to_smack && can_shred(used_item)) + else if(!trying_to_smack && can_shred_document(used_item)) shred(used_item, user) return TRUE return ..() @@ -193,6 +193,7 @@ /decl/interaction_handler/empty/paper_shredder name = "Empty Bin" expected_target_type = /obj/machinery/papershredder + examine_desc = "empty $TARGET_THEM$" /decl/interaction_handler/empty/paper_shredder/is_possible(obj/machinery/papershredder/target, mob/user, obj/item/prop) return ..() && !target.is_bin_empty() diff --git a/code/modules/paperwork/pen/crayon.dm b/code/modules/paperwork/pen/crayon.dm index 0c3222998ac..f06419b8711 100644 --- a/code/modules/paperwork/pen/crayon.dm +++ b/code/modules/paperwork/pen/crayon.dm @@ -33,24 +33,31 @@ return if(istype(target) && target.is_floor()) - var/drawtype = input("Choose what you'd like to draw.", "Crayon scribbles") in list("graffiti","rune","letter","arrow") + var/static/list/drawtypes = list(CRAYON_DRAW_GRAFFITI, CRAYON_DRAW_RUNE, CRAYON_DRAW_LETTER, CRAYON_DRAW_ARROW) + var/drawtype = input(user, "Choose what you'd like to draw.", "Crayon scribbles") as null|anything in drawtypes var/draw_message = "drawing" switch(drawtype) - if("letter") - drawtype = input("Choose the letter.", "Crayon scribbles") in list(global.alphabet) + if(CRAYON_DRAW_LETTER) + drawtype = input(user, "Choose a letter.", "Crayon scribbles") as null|anything in global.alphabet draw_message = "drawing a letter" - if("graffiti") + if(CRAYON_DRAW_GRAFFITI) draw_message = "drawing graffiti" - if("rune") + if(CRAYON_DRAW_RUNE) draw_message = "drawing a rune" - if("arrow") - drawtype = input("Choose the arrow.", "Crayon scribbles") in list("left", "right", "up", "down") + if(CRAYON_DRAW_ARROW) + var/static/list/arrow_dirs = list("left", "right", "up", "down") + drawtype = input(user, "Choose an arrow.", "Crayon scribbles") as null|anything in arrow_dirs draw_message = "drawing an arrow" + if(!drawtype || QDELETED(src) || QDELETED(target) || QDELETED(user) || user.get_active_held_item() != src || !CanPhysicallyInteractWith(user, target)) + return TRUE + if(do_tool_interaction(TOOL_PEN, user, target, 5 SECONDS, draw_message, "drawing on", fuel_expenditure = 1)) - new /obj/effect/decal/cleanable/crayon(target, stroke_color, shade_color, drawtype) + var/obj/effect/decal/cleanable/crayon/graffiti = new(target, stroke_color, shade_color, drawtype) target.add_fingerprint(user) // Adds their fingerprints to the floor the crayon is drawn on. - return + graffiti.add_fingerprint(user) + + return TRUE /obj/item/pen/crayon/red stroke_color = "#da0000" diff --git a/code/modules/paperwork/pen/fancy.dm b/code/modules/paperwork/pen/fancy.dm index 9c7e149a7be..098033965e6 100644 --- a/code/modules/paperwork/pen/fancy.dm +++ b/code/modules/paperwork/pen/fancy.dm @@ -1,7 +1,7 @@ /obj/item/pen/fancy name = "fountain pen" icon = 'icons/obj/items/pens/pen_fancy.dmi' - sharp = 1 //pointy + sharp = TRUE stroke_color = "#1c1713" //dark ashy brownish stroke_color_name = "dark ashy brownish" material = /decl/material/solid/metal/steel diff --git a/code/modules/paperwork/pen/quill_and_ink.dm b/code/modules/paperwork/pen/quill_and_ink.dm index 4944165f4e5..f643b9d1247 100644 --- a/code/modules/paperwork/pen/quill_and_ink.dm +++ b/code/modules/paperwork/pen/quill_and_ink.dm @@ -1,7 +1,7 @@ /obj/item/pen/fancy/quill name = "quill pen" icon = 'icons/obj/items/pens/pen_quill.dmi' - sharp = 0 + sharp = FALSE material = /decl/material/solid/organic/skin/feathers pen_quality = TOOL_QUALITY_DEFAULT max_uses = 5 // gotta re-ink it often! diff --git a/code/modules/paperwork/pen/reagent_pen.dm b/code/modules/paperwork/pen/reagent_pen.dm index 67dfcad441c..7334323851a 100644 --- a/code/modules/paperwork/pen/reagent_pen.dm +++ b/code/modules/paperwork/pen/reagent_pen.dm @@ -1,7 +1,7 @@ /obj/item/pen/reagent atom_flags = ATOM_FLAG_OPEN_CONTAINER origin_tech = @'{"materials":2,"esoteric":5}' - sharp = 1 + sharp = TRUE pen_quality = TOOL_QUALITY_MEDIOCRE /obj/item/pen/reagent/Initialize() diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 712c2d6c2b6..5d816b92448 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -268,6 +268,7 @@ /decl/interaction_handler/empty/photocopier_paper_bin name = "Empty Paper Bin" expected_target_type = /obj/machinery/photocopier + examine_desc = "empty $TARGET_THEM$" /decl/interaction_handler/empty/photocopier_paper_bin/is_possible(obj/machinery/photocopier/target, mob/user, obj/item/prop) return (target.printer?.get_amount_paper() > 0) && ..() @@ -288,6 +289,7 @@ /decl/interaction_handler/remove/photocopier_scanner_item name = "Remove Item From Scanner" expected_target_type = /obj/machinery/photocopier + examine_desc = "remove a loaded item" /decl/interaction_handler/remove/photocopier_scanner_item/is_possible(obj/machinery/photocopier/target, mob/user, obj/item/prop) return target.scanner_item && ..() diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 6e18e36decb..eb9bbb76a03 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -398,6 +398,7 @@ icon = 'icons/screen/radial.dmi' icon_state = "radial_eject" expected_target_type = /obj/item/camera + examine_desc = "eject the film" /decl/interaction_handler/camera_eject_film/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/camera/camera = target diff --git a/code/modules/paperwork/printer.dm b/code/modules/paperwork/printer.dm index 7cfeaa96c43..0f91439ab58 100644 --- a/code/modules/paperwork/printer.dm +++ b/code/modules/paperwork/printer.dm @@ -423,6 +423,7 @@ /decl/interaction_handler/empty/stock_parts_printer name = "Empty Paper Bin" expected_target_type = /obj/item/stock_parts/printer + examine_desc = "empty $TARGET_THEM$" /decl/interaction_handler/empty/stock_parts_printer/is_possible(obj/item/stock_parts/printer/target, mob/user, obj/item/prop) return (target.get_amount_paper() > 0) && ..() diff --git a/code/modules/persistence/graffiti.dm b/code/modules/persistence/graffiti.dm index e9afbbad8d8..0bd85389b6f 100644 --- a/code/modules/persistence/graffiti.dm +++ b/code/modules/persistence/graffiti.dm @@ -14,10 +14,10 @@ var/author = "unknown" /obj/effect/decal/writing/Initialize(mapload, var/_age, var/_message, var/_author) - var/list/random_icon_states = icon_states(icon) + var/list/random_icon_states = get_states_in_icon(icon) for(var/obj/effect/decal/writing/W in loc) - random_icon_states.Remove(W.icon_state) - if(random_icon_states.len) + random_icon_states -= W.icon_state + if(length(random_icon_states)) icon_state = pick(random_icon_states) SSpersistence.track_value(src, /decl/persistence_handler/graffiti) . = ..(mapload) @@ -43,7 +43,7 @@ user.visible_message(SPAN_NOTICE("\The [user] clears away some graffiti.")) qdel(src) return TRUE - else if(thing.sharp && !user.check_intent(I_FLAG_HELP)) //Check intent so you don't go insane trying to unscrew a light fixture over a graffiti + else if(thing.is_sharp() && !user.check_intent(I_FLAG_HELP)) //Check intent so you don't go insane trying to unscrew a light fixture over a graffiti if(jobban_isbanned(user, "Graffiti")) to_chat(user, SPAN_WARNING("You are banned from leaving persistent information across rounds.")) return TRUE diff --git a/code/modules/persistence/persistence_datum.dm b/code/modules/persistence/persistence_datum.dm index 557ae252b88..027081f7949 100644 --- a/code/modules/persistence/persistence_datum.dm +++ b/code/modules/persistence/persistence_datum.dm @@ -26,14 +26,16 @@ return TRUE /decl/persistence_handler/proc/CheckTokenSanity(var/list/tokens) - return ( \ - islist(tokens) && \ - !isnull(tokens["x"]) && \ - !isnull(tokens["y"]) && \ - !isnull(tokens["z"]) && \ - !isnull(tokens["age"]) && \ - tokens["age"] <= entries_expire_at \ - ) + if(!islist(tokens)) + return FALSE + if(isnull(tokens["x"]) || isnull(tokens["y"]) || isnull(tokens["z"])) + return FALSE + if(!isnull(entries_expire_at)) + if(isnull(tokens["age"])) + return FALSE + if(tokens["age"] > entries_expire_at) + return FALSE + return TRUE /decl/persistence_handler/proc/CreateEntryInstance(var/turf/creating, var/list/tokens) return @@ -59,8 +61,9 @@ else return - if(GetValidTurf(locate(tokens["x"], tokens["y"], tokens["z"]), tokens)) - return CreateEntryInstance(., tokens) + . = GetValidTurf(locate(tokens["x"], tokens["y"], tokens["z"]), tokens) + if(.) + . = CreateEntryInstance(., tokens) /decl/persistence_handler/proc/IsValidEntry(var/atom/entry) if(!istype(entry)) diff --git a/code/modules/persistence/persistence_datum_book.dm b/code/modules/persistence/persistence_datum_book.dm index 479a9c8cb45..cf33c5df07d 100644 --- a/code/modules/persistence/persistence_datum_book.dm +++ b/code/modules/persistence/persistence_datum_book.dm @@ -5,7 +5,14 @@ ignore_invalid_loc = TRUE /decl/persistence_handler/book/CreateEntryInstance(var/turf/creating, var/list/tokens) - var/obj/item/book/book = new(creating) + + var/book_type = tokens["book_type"] + if(book_type) + book_type = text2path(book_type) + if(!ispath(book_type)) + book_type = /obj/item/book + + var/obj/item/book/book = new book_type(creating) book.dat = tokens["message"] book.title = tokens["title"] book.author = tokens["writer"] @@ -29,11 +36,12 @@ . = ..() var/obj/item/book/book = entry - .["author"] = book.last_modified_ckey || "" - .["message"] = book.dat || "dat" - .["title"] = book.title || "Untitled" - .["writer"] = book.author || "unknown" + .["author"] = book.last_modified_ckey || "" + .["message"] = book.dat || "dat" + .["title"] = book.title || "Untitled" + .["writer"] = book.author || "unknown" .["icon_state"] = book.icon_state || "book" + .["book_type"] = "[book.type]" var/turf/T = get_turf(entry) if(!T || !isStationLevel(T.z)) @@ -62,7 +70,7 @@ else T = get_random_spawn_turf(SPAWN_FLAG_PERSISTENCE_CAN_SPAWN) - . = ..(T, tokens) + . = ..() /decl/persistence_handler/book/GetEntryAge(var/atom/entry) . = -1 diff --git a/code/modules/supermatter/setup_supermatter.dm b/code/modules/power/admin_setup_engine.dm similarity index 51% rename from code/modules/supermatter/setup_supermatter.dm rename to code/modules/power/admin_setup_engine.dm index 1fb598744b2..d7d59c8f9e5 100644 --- a/code/modules/supermatter/setup_supermatter.dm +++ b/code/modules/power/admin_setup_engine.dm @@ -1,108 +1,7 @@ -#define SETUP_OK 1 // All good -#define SETUP_WARNING 2 // Something that shouldn't happen happened, but it's not critical so we will continue -#define SETUP_ERROR 3 // Something bad happened, and it's important so we won't continue setup. -#define SETUP_DELAYED 4 // Wait for other things first. - -#define ENERGY_NITROGEN 115 // Roughly 8 emitter shots. -#define ENERGY_CARBONDIOXIDE 150 // Roughly 10 emitter shots. -#define ENERGY_HYDROGEN 300 // Roughly 20 emitter shots. -#define ENERGY_PHORON 500 // Roughly 40 emitter shots. - -/datum/admins/proc/setup_supermatter() - set category = "Debug" - set name = "Setup Supermatter" - set desc = "Allows you to start the Supermatter engine." - - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - - var/response = input(usr, "Are you sure? This will start up the engine with selected gas as coolant.", "Engine setup") as null|anything in list("N2", "CO2", "PH", "H2", "Abort") - if(!response || response == "Abort") - return - - var/errors = 0 - var/warnings = 0 - var/success = 0 - - log_and_message_admins("## SUPERMATTER SETUP - Setup initiated by [usr] using coolant type [response].") - - // CONFIGURATION PHASE - // Coolant canisters, set types according to response. - for(var/obj/effect/engine_setup/coolant_canister/C in global.engine_setup_markers) - switch(response) - if("N2") - C.canister_type = /obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/ - continue - if("CO2") - C.canister_type = /obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/ - continue - if("PH") - C.canister_type = /obj/machinery/portable_atmospherics/canister/phoron/engine_setup/ - continue - if("H2") - C.canister_type = /obj/machinery/portable_atmospherics/canister/hydrogen/engine_setup/ - continue - - for(var/obj/effect/engine_setup/core/C in global.engine_setup_markers) - switch(response) - if("N2") - C.energy_setting = ENERGY_NITROGEN - continue - if("CO2") - C.energy_setting = ENERGY_CARBONDIOXIDE - continue - if("PH") - C.energy_setting = ENERGY_PHORON - continue - if("H2") - C.energy_setting = ENERGY_HYDROGEN - continue - - for(var/obj/effect/engine_setup/filter/F in global.engine_setup_markers) - F.coolant = response - - var/list/delayed_objects = list() - // SETUP PHASE - for(var/obj/effect/engine_setup/S in global.engine_setup_markers) - var/result = S.activate(0) - switch(result) - if(SETUP_OK) - success++ - continue - if(SETUP_WARNING) - warnings++ - continue - if(SETUP_ERROR) - errors++ - log_and_message_admins("## SUPERMATTER SETUP - Error encountered! Aborting.") - break - if(SETUP_DELAYED) - delayed_objects.Add(S) - continue - - if(!errors) - for(var/obj/effect/engine_setup/S in delayed_objects) - var/result = S.activate(1) - switch(result) - if(SETUP_OK) - success++ - continue - if(SETUP_WARNING) - warnings++ - continue - if(SETUP_ERROR) - errors++ - log_and_message_admins("## SUPERMATTER SETUP - Error encountered! Aborting.") - break - - log_and_message_admins("## SUPERMATTER SETUP - Setup completed with [errors] errors, [warnings] warnings and [success] successful steps.") - - return - - +#define ENGINE_SETUP_OK 1 // All good +#define ENGINE_SETUP_WARNING 2 // Something that shouldn't happen happened, but it's not critical so we will continue +#define ENGINE_SETUP_ERROR 3 // Something bad happened, and it's important so we won't continue setup. +#define ENGINE_SETUP_DELAYED 4 // Wait for other things first. var/global/list/engine_setup_markers = list() @@ -129,7 +28,7 @@ var/global/list/engine_setup_markers = list() // Tries to locate a pump, enables it, and sets it to MAX. Triggers warning if unable to locate a pump. -/obj/effect/engine_setup/pump_max/ +/obj/effect/engine_setup/pump_max name = "Pump Setup Marker" /obj/effect/engine_setup/pump_max/activate() @@ -137,15 +36,15 @@ var/global/list/engine_setup_markers = list() var/obj/machinery/atmospherics/binary/pump/P = locate() in get_turf(src) if(!P) log_and_message_admins("## WARNING: Unable to locate pump at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING P.target_pressure = P.max_pressure_setting P.update_use_power(POWER_USE_IDLE) - return SETUP_OK + return ENGINE_SETUP_OK // Spawns an empty canister on this turf, if it has a connector port. Triggers warning if unable to find a connector port -/obj/effect/engine_setup/empty_canister/ +/obj/effect/engine_setup/empty_canister name = "Empty Canister Marker" /obj/effect/engine_setup/empty_canister/activate() @@ -153,16 +52,16 @@ var/global/list/engine_setup_markers = list() var/obj/machinery/atmospherics/portables_connector/P = locate() in get_turf(src) if(!P) log_and_message_admins("## WARNING: Unable to locate connector port at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING new/obj/machinery/portable_atmospherics/canister(get_turf(src)) // Canisters automatically connect to connectors in New() - return SETUP_OK + return ENGINE_SETUP_OK // Spawns a coolant canister on this turf, if it has a connector port. // Triggers error when unable to locate connector port or when coolant canister type is unset. -/obj/effect/engine_setup/coolant_canister/ +/obj/effect/engine_setup/coolant_canister name = "Coolant Canister Marker" var/canister_type = null @@ -171,33 +70,12 @@ var/global/list/engine_setup_markers = list() var/obj/machinery/atmospherics/portables_connector/P = locate() in get_turf(src) if(!P) log_and_message_admins("## ERROR: Unable to locate coolant connector port at [x] [y] [z]!") - return SETUP_ERROR + return ENGINE_SETUP_ERROR if(!canister_type) log_and_message_admins("## ERROR: Canister type unset at [x] [y] [z]!") - return SETUP_ERROR + return ENGINE_SETUP_ERROR new canister_type(get_turf(src)) - return SETUP_OK - - - -// Energises the supermatter. Errors when unable to locate supermatter. -/obj/effect/engine_setup/core/ - name = "Supermatter Core Marker" - var/energy_setting = 0 - -/obj/effect/engine_setup/core/activate(var/last = 0) - if(!last) - return SETUP_DELAYED - ..() - var/obj/machinery/power/supermatter/SM = locate() in get_turf(src) - if(!SM) - log_and_message_admins("## ERROR: Unable to locate supermatter core at [x] [y] [z]!") - return SETUP_ERROR - if(!energy_setting) - log_and_message_admins("## ERROR: Energy setting unset at [x] [y] [z]!") - return SETUP_ERROR - SM.power = energy_setting - return SETUP_OK + return ENGINE_SETUP_OK @@ -218,13 +96,13 @@ var/global/list/engine_setup_markers = list() var/obj/machinery/power/smes/S = locate() in get_turf(src) if(!S) log_and_message_admins("## WARNING: Unable to locate SMES unit at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING S.input_attempt = 1 S.input_level = min(target_input_level, S.input_level_max) S.output_attempt = 1 S.output_level = min(target_output_level, S.output_level_max) S.update_icon() - return SETUP_OK + return ENGINE_SETUP_OK // Sets up filters. This assumes filters are set to filter out CO2 back to the core loop by default! /obj/effect/engine_setup/filter @@ -236,10 +114,10 @@ var/global/list/engine_setup_markers = list() var/obj/machinery/atmospherics/omni/filter/F = locate() in get_turf(src) if(!F) log_and_message_admins("## WARNING: Unable to locate omni filter at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING if(!coolant) log_and_message_admins("## WARNING: No coolant type set at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING // Non-co2 coolant, adjust the filter's config first. if(coolant != "CO2") @@ -257,11 +135,11 @@ var/global/list/engine_setup_markers = list() break else log_and_message_admins("## WARNING: Inapropriate filter coolant type set at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING F.rebuild_filtering_list() F.update_use_power(POWER_USE_IDLE) - return SETUP_OK + return ENGINE_SETUP_OK // Closes the monitoring room shutters so the first Engi to show up doesn't get microwaved /obj/effect/engine_setup/shutters @@ -272,7 +150,7 @@ var/global/list/engine_setup_markers = list() /obj/effect/engine_setup/shutters/activate() if(!target_button) log_and_message_admins("## WARNING: No button type set at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING var/obj/machinery/button/blast_door/found = null var/turf/T = get_turf(src) for(var/obj/machinery/button/blast_door/B in T.contents) @@ -281,16 +159,7 @@ var/global/list/engine_setup_markers = list() break if(!found) log_and_message_admins("## WARNING: Unable to locate button at [x] [y] [z]!") - return SETUP_WARNING + return ENGINE_SETUP_WARNING found.activate() found.update_icon() - return SETUP_OK - -#undef SETUP_OK -#undef SETUP_WARNING -#undef SETUP_ERROR -#undef SETUP_DELAYED -#undef ENERGY_NITROGEN -#undef ENERGY_CARBONDIOXIDE -#undef ENERGY_HYDROGEN -#undef ENERGY_PHORON \ No newline at end of file + return ENGINE_SETUP_OK diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 1f6254eea46..6e2b63b82fa 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -491,21 +491,20 @@ var/global/list/all_apcs = list() /obj/machinery/power/apc/physical_attack_hand(mob/user) //Human mob special interaction goes here. - if(ishuman(user)) - var/mob/living/human/H = user - - if(H.species.can_shred(H)) - user.visible_message("\The [user] slashes at \the [src]!", "You slash at \the [src]!") - playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) - - var/allcut = wires.IsAllCut() - if(beenhit >= pick(3, 4) && allcut == 0) - wires.CutAll() - src.update_icon() - src.visible_message("\The [src]'s wires are shredded!") - else - beenhit += 1 - return TRUE + if(user.can_shred()) + user.visible_message( + SPAN_DANGER("\The [user] slashes at \the [src]!"), + SPAN_DANGER("You slash at \the [src]!") + ) + playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) + var/allcut = wires.IsAllCut() + if(beenhit >= pick(3, 4) && allcut == 0) + wires.CutAll() + update_icon() + visible_message(SPAN_DANGER("\The [src]'s wires are shredded!")) + else + beenhit += 1 + return TRUE return FALSE /obj/machinery/power/apc/interface_interact(mob/user) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index dd74a49e3d3..655e2766497 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -166,50 +166,48 @@ By design, d1 is the smallest direction and d2 is the highest // // TODO: take a closer look at cable attackby, make it call parent? -/obj/structure/cable/attackby(obj/item/W, mob/user) - if(IS_WIRECUTTER(W)) - cut_wire(W, user) +/obj/structure/cable/attackby(obj/item/used_item, mob/user) - else if(IS_COIL(W)) - var/obj/item/stack/cable_coil/coil = W + if(IS_WIRECUTTER(used_item)) + cut_wire(used_item, user) + return TRUE + + if(IS_COIL(used_item)) + var/obj/item/stack/cable_coil/coil = used_item if (coil.get_amount() < 1) to_chat(user, "You don't have enough cable to lay down.") return TRUE coil.cable_join(src, user) + return TRUE - else if(IS_MULTITOOL(W)) - + if(IS_MULTITOOL(used_item)) if(powernet && (powernet.avail > 0)) // is it powered? to_chat(user, SPAN_WARNING("[get_wattage()] in power network.")) - + shock(user, 5, 0.2) else to_chat(user, SPAN_WARNING("\The [src] is not powered.")) + return TRUE - shock(user, 5, 0.2) - - - else if(W.edge) + else if(used_item.has_edge()) var/delay_holder - - if(W.get_attack_force(user) < 5) - visible_message(SPAN_WARNING("[user] starts sawing away roughly at \the [src] with \the [W].")) + if(used_item.expend_attack_force(user) < 5) + visible_message(SPAN_WARNING("[user] starts sawing away roughly at \the [src] with \the [used_item].")) delay_holder = 8 SECONDS else - visible_message(SPAN_WARNING("[user] begins to cut through \the [src] with \the [W].")) + visible_message(SPAN_WARNING("[user] begins to cut through \the [src] with \the [used_item].")) delay_holder = 3 SECONDS - if(user.do_skilled(delay_holder, SKILL_ELECTRICAL, src)) - cut_wire(W, user) - if(W.obj_flags & OBJ_FLAG_CONDUCTIBLE) + cut_wire(used_item, user) + if(used_item.obj_flags & OBJ_FLAG_CONDUCTIBLE) shock(user, 66, 0.7) else visible_message(SPAN_WARNING("[user] stops cutting before any damage is done.")) + return TRUE - src.add_fingerprint(user) - return TRUE + return ..() -/obj/structure/cable/proc/cut_wire(obj/item/W, mob/user) +/obj/structure/cable/proc/cut_wire(obj/item/used_item, mob/user) var/turf/T = get_turf(src) if(!T || !T.is_plating()) return @@ -241,13 +239,13 @@ By design, d1 is the smallest direction and d2 is the highest // shock the user with probability prb /obj/structure/cable/proc/shock(mob/user, prb, var/siemens_coeff = 1.0) - if(!prob(prb)) - return 0 + if(!prob(prb) || powernet?.avail <= 0) + return FALSE if (electrocute_mob(user, powernet, src, siemens_coeff)) spark_at(src, amount=5, cardinal_only = TRUE) if(HAS_STATUS(user, STAT_STUN)) - return 1 - return 0 + return TRUE + return FALSE // TODO: generalize to matter list and parts_type. /obj/structure/cable/create_dismantled_products(turf/T) diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 48abeb8383a..a60fab7f939 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -85,7 +85,8 @@ to_chat(user, "The charge meter reads [round(src.percent(), 0.1)]%.") /obj/item/cell/emp_act(severity) - //remove this once emp changes on dev are merged in + // remove this if EMPs are ever rebalanced so that they don't instantly drain borg cells + // todo: containers (partially) shielding contents? if(isrobot(loc)) var/mob/living/silicon/robot/R = loc severity *= R.cell_emp_mult diff --git a/code/modules/power/fuel_assembly/fuel_assembly.dm b/code/modules/power/fuel_assembly/fuel_assembly.dm index dbd9544418e..3721dff6b01 100644 --- a/code/modules/power/fuel_assembly/fuel_assembly.dm +++ b/code/modules/power/fuel_assembly/fuel_assembly.dm @@ -90,8 +90,8 @@ /obj/item/fuel_assembly/phoron material = /decl/material/solid/phoron -/obj/item/fuel_assembly/supermatter - material = /decl/material/solid/supermatter +/obj/item/fuel_assembly/exotic_matter + material = /decl/material/solid/exotic_matter /obj/item/fuel_assembly/hydrogen material = /decl/material/gas/hydrogen diff --git a/code/modules/power/fuel_assembly/fuel_compressor.dm b/code/modules/power/fuel_assembly/fuel_compressor.dm index 58b26a67dd9..0488d360f93 100644 --- a/code/modules/power/fuel_assembly/fuel_compressor.dm +++ b/code/modules/power/fuel_assembly/fuel_compressor.dm @@ -129,12 +129,6 @@ to_chat(user, SPAN_NOTICE("You add the contents of \the [thing] to \the [src]'s material buffer.")) return TRUE - if(istype(thing, /obj/machinery/power/supermatter/shard)) - stored_material[/decl/material/solid/supermatter] = 5 * SHEET_MATERIAL_AMOUNT - to_chat(user, SPAN_NOTICE("You awkwardly cram \the [thing] into \the [src]'s material buffer.")) - qdel(thing) - return TRUE - if(istype(thing, /obj/item/stack/material)) var/obj/item/stack/material/M = thing var/decl/material/mat = M.get_material() diff --git a/code/modules/power/fusion/fusion_reactions.dm b/code/modules/power/fusion/fusion_reactions.dm index 472ab5c37b5..14ac3c4e1cc 100644 --- a/code/modules/power/fusion/fusion_reactions.dm +++ b/code/modules/power/fusion/fusion_reactions.dm @@ -102,7 +102,7 @@ // VERY UNIDEAL REACTIONS. /decl/fusion_reaction/phoron_supermatter - p_react = /decl/material/solid/supermatter + p_react = /decl/material/solid/exotic_matter s_react = /decl/material/solid/phoron energy_consumption = 0 energy_production = 5 * FUSION_PROCESSING_TIME_MULT @@ -121,8 +121,7 @@ qdel(holder) var/radiation_level = rand(100, 200) - // Copied from the SM for proof of concept. //Not any more --Cirra //Use the whole z proc --Leshana - SSradiation.z_radiate(locate(1, 1, holder.z), radiation_level, 1) + SSradiation.z_radiate(origin, radiation_level, respect_maint = TRUE) for(var/mob/living/human/H in global.living_mob_list_) var/turf/T = get_turf(H) @@ -130,10 +129,10 @@ H.set_hallucination(rand(100,150), 51) for(var/obj/machinery/fusion_fuel_injector/I in range(world.view, origin)) - if(I.cur_assembly && I.cur_assembly.material && I.cur_assembly.material.type == /decl/material/solid/supermatter) + if(I.cur_assembly && I.cur_assembly.material && I.cur_assembly.material.type == /decl/material/solid/exotic_matter) explosion(get_turf(I), 1, 2, 3) if(!QDELETED(I)) - QDEL_IN(I, 5) + addtimer(CALLBACK(I, TYPE_PROC_REF(/atom, physically_destroyed)), 0.5 SECONDS) sleep(5) explosion(origin, 1, 2, 5) diff --git a/code/modules/power/geothermal/_geothermal.dm b/code/modules/power/geothermal/_geothermal.dm index 5f76fa6dd0f..3425a37cd2c 100644 --- a/code/modules/power/geothermal/_geothermal.dm +++ b/code/modules/power/geothermal/_geothermal.dm @@ -148,6 +148,7 @@ var/global/const/MAX_GEOTHERMAL_PRESSURE = 12000 /obj/machinery/geothermal/Destroy() var/atom/last_loc = loc unset_vent() + connector = null . = ..() if(istype(last_loc)) propagate_refresh_neighbors(last_loc) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index e3b47f5c5d1..624425a5121 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -264,7 +264,7 @@ else if(lightbulb && (lightbulb.status != LIGHT_BROKEN) && !user.check_intent(I_FLAG_HELP)) - if(prob(1 + W.get_attack_force(user) * 5)) + if(prob(1 + W.expend_attack_force(user) * 5)) user.visible_message("[user.name] smashed the light!", "You smash the light!", "You hear a tinkle of breaking glass.") if(on && (W.obj_flags & OBJ_FLAG_CONDUCTIBLE)) @@ -316,12 +316,13 @@ to_chat(user, "There is no [get_fitting_name()] in this light.") return TRUE - if(ishuman(user)) - var/mob/living/human/H = user - if(H.species.can_shred(H)) - visible_message("[user.name] smashed the light!", 3, "You hear a tinkle of breaking glass.") - broken() - return TRUE + if(user.can_shred()) + visible_message( + SPAN_DANGER("\The [user] smashes the light!"), + blind_message = "You hear a tinkle of breaking glass." + ) + broken() + return TRUE // make it burn hands if not wearing fire-insulated gloves if(on) @@ -604,7 +605,7 @@ if(status == LIGHT_OK || status == LIGHT_BURNED) src.visible_message("[name] shatters.","You hear a small glass object shatter.") status = LIGHT_BROKEN - sharp = 1 + set_sharp(TRUE) set_base_attack_force(5) playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) update_icon() @@ -612,13 +613,7 @@ /obj/item/light/proc/switch_on() switchcount++ if(rigged) - log_and_message_admins("Rigged light explosion, last touched by [fingerprintslast]") - var/turf/T = get_turf(src.loc) - spawn(0) - sleep(2) - explosion(T, 0, 0, 3, 5) - sleep(1) - qdel(src) + addtimer(CALLBACK(src, PROC_REF(do_rigged_explosion)), 0.2 SECONDS) status = LIGHT_BROKEN else if(prob(min(60, switchcount*switchcount*0.01))) status = LIGHT_BURNED @@ -626,6 +621,15 @@ playsound(src, sound_on, 75) return status +/obj/item/light/proc/do_rigged_explosion() + if(!rigged) + return + log_and_message_admins("Rigged light explosion, last touched by [fingerprintslast]") + var/turf/T = get_turf(src) + explosion(T, 0, 0, 3, 5) + if(!QDELETED(src)) + QDEL_IN(src, 1) + /obj/machinery/light/do_simple_ranged_interaction(var/mob/user) if(lightbulb) remove_bulb() diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index ca4b205e6b0..8afd72314ce 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -179,11 +179,14 @@ return net1 //Determines how strong could be shock, deals damage to mob, uses power. -//M is a mob who touched wire/whatever +//victim is a mob who touched wire/whatever //power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null //source is an object caused electrocuting (airlock, grille, etc) //No animations will be performed by this proc. -/proc/electrocute_mob(mob/living/M, var/power_source, var/obj/source, var/siemens_coeff = 1.0) +/proc/electrocute_mob(mob/living/victim, power_source, obj/source, siemens_coeff = 1.0, coverage_flags = SLOT_HANDS) + + coverage_flags = victim?.get_active_hand_bodypart_flags() || coverage_flags + var/area/source_area if(istype(power_source,/area)) source_area = power_source @@ -208,19 +211,16 @@ else if (!power_source) return 0 else - log_admin("ERROR: /proc/electrocute_mob([M], [power_source], [source]): wrong power_source") + log_admin("ERROR: /proc/electrocute_mob([victim], [power_source], [source]): wrong power_source") return 0 + //Triggers powernet warning, but only for 5 ticks (if applicable) //If following checks determine user is protected we won't alarm for long. if(PN) PN.trigger_warning(5) - if(ishuman(M)) - var/mob/living/human/H = M - if(H.species.get_shock_vulnerability(H) <= 0) - return - var/obj/item/clothing/gloves/G = H.get_equipped_item(slot_gloves_str) - if(istype(G) && G.siemens_coefficient == 0) - return 0 //to avoid spamming with insulated glvoes on + + if(victim.get_siemens_coefficient_for_coverage(coverage_flags) <= 0) + return //Checks again. If we are still here subject will be shocked, trigger standard 20 tick warning //Since this one is longer it will override the original one. @@ -242,7 +242,7 @@ else power_source = cell shock_damage = cell_damage - var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap! + var/drained_hp = victim.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap! var/drained_energy = drained_hp*20 if (source_area) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 233b66d985f..c2b62b6059f 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -11,7 +11,7 @@ active_power_usage = 100 KILOWATTS var/efficiency = 0.3 // Energy efficiency. 30% at this time, so 100kW load means 30kW laser pulses. - var/minimum_power = 10 KILOWATTS // The minimum power the emitter will still fire at it it doesn't have enough power available. + var/minimum_power = 10 KILOWATTS // The minimum power below which the emitter will turn off; different than the power needed to fire. var/active = 0 var/fire_delay = 100 var/max_burst_delay = 100 diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm index fc1b54c7edc..00976a216f0 100644 --- a/code/modules/power/singularity/generator.dm +++ b/code/modules/power/singularity/generator.dm @@ -8,7 +8,7 @@ density = TRUE use_power = POWER_USE_OFF matter = list( - /decl/material/solid/supermatter = MATTER_AMOUNT_PRIMARY, + /decl/material/solid/exotic_matter = MATTER_AMOUNT_PRIMARY, /decl/material/solid/metal/steel = MATTER_AMOUNT_SECONDARY ) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 8dae5c06871..d53d37aa144 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -13,7 +13,7 @@ var/global/list/singularities = list() /// Category used for investigation entries relating to this atom. var/const/investigation_label = "singulo" - /// A list of events. Toxins is in here twice to double the chance of proccing. + /// A weighted list of events. var/static/list/singularity_events = list( /decl/singularity_event/empulse = 1, /decl/singularity_event/toxins = 2, @@ -63,7 +63,7 @@ var/global/list/singularities = list() /obj/effect/singularity/explosion_act(severity) SHOULD_CALL_PARENT(FALSE) - if(current_stage.stage_size == STAGE_SUPER)//IT'S UNSTOPPABLE + if(!current_stage.explosion_vulnerable)//IT'S UNSTOPPABLE return if(severity == 1) if(prob(25)) @@ -153,16 +153,13 @@ var/global/list/singularities = list() // Handle random events. if(prob(current_stage.event_chance)) - if(current_stage.stage_size >= STAGE_SUPER) - var/decl/singularity_event/wave_event = GET_DECL(/decl/singularity_event/supermatter_wave) - wave_event.handle_event(src) - var/decl/singularity_event/singularity_event = pickweight(singularity_events) + var/decl/singularity_event/singularity_event = current_stage.forced_event || pickweight(singularity_events) singularity_event = GET_DECL(singularity_event) singularity_event.handle_event(src) /obj/effect/singularity/proc/try_move(var/movement_dir, var/vertical_move) set waitfor = FALSE - if(current_stage.stage_size >= STAGE_FIVE)//The superlarge one does not care about things in its way + if(current_stage.ignore_obstacles)//The superlarge one does not care about things in its way step(src, movement_dir) if(!vertical_move) sleep(1) diff --git a/code/modules/power/singularity/singularity_events.dm b/code/modules/power/singularity/singularity_events.dm index e48a040231b..f3bc342ac90 100644 --- a/code/modules/power/singularity/singularity_events.dm +++ b/code/modules/power/singularity/singularity_events.dm @@ -7,10 +7,7 @@ /decl/singularity_event/nothing // Nothing happens. /decl/singularity_event/empulse/handle_event(obj/effect/singularity/source) - if(source.current_stage.stage_size != STAGE_SUPER) - empulse(source, 8, 10) - else - empulse(source, 12, 16) + empulse(source, source.current_stage.em_heavy_range, source.current_stage.em_light_range) /decl/singularity_event/toxins var/toxrange = 10 @@ -35,20 +32,10 @@ if(ishuman(M)) var/mob/living/human/H = M if(istype(H.get_equipped_item(slot_glasses_str), /obj/item/clothing/glasses/meson)) - if(source.current_stage.stage_size != STAGE_SUPER) + if(!source.current_stage.the_goggles_do_nothing) to_chat(H, SPAN_WARNING("You look directly into \the [source]. Good thing you had your protective eyewear on!")) continue to_chat(H, SPAN_WARNING("Your eyewear does absolutely nothing to protect you from \the [source]")) - to_chat(M, SPAN_DANGER("You look directly into \the [source] and feel [source.current_stage.stage_size == STAGE_SUPER ? "helpless" : "weak"].")) + to_chat(M, SPAN_DANGER("You look directly into \the [source] and feel [source.current_stage.mesmerize_text].")) M.apply_effect(3, STUN) M.visible_message(SPAN_DANGER("\The [M] stares blankly at \the [source]!")) - -/decl/singularity_event/supermatter_wave/handle_event(obj/effect/singularity/source) - for(var/mob/living/M in view(10, source.loc)) - to_chat(M, SPAN_WARNING("You hear an unearthly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.")) - if(prob(67)) - to_chat(M, SPAN_NOTICE("Miraculously, it fails to kill you.")) - else - to_chat(M, SPAN_DANGER("You don't even have a moment to react as you are reduced to ashes by the intense radiation.")) - M.dust() - SSradiation.radiate(source, rand(source.energy)) diff --git a/code/modules/power/singularity/singularity_stages.dm b/code/modules/power/singularity/singularity_stages.dm index 831cacd48c5..2624c10e818 100644 --- a/code/modules/power/singularity/singularity_stages.dm +++ b/code/modules/power/singularity/singularity_stages.dm @@ -32,8 +32,22 @@ var/dissipation_energy_loss = 1 /// What is the percent chance of an event each tick? var/event_chance + /// Do we force a specific event when we proc events? + var/decl/singularity_event/forced_event = null /// Will we wander around? var/wander + /// Can explosions destroy the singularity? + var/explosion_vulnerable + /// What is the heavy range for the EM pulse event in this stage? + var/em_heavy_range = 8 + /// What is the light range for the EM pulse event in this stage? + var/em_light_range = 10 + /// What do characters feel when they're mesmerized during this stage? + var/mesmerize_text = "weak" + /// Do we ignore PPE for mesmerizing in this stage? + var/the_goggles_do_nothing = FALSE + /// Do we ignore obstacles in our way? + var/ignore_obstacles = FALSE /decl/singularity_stage/validate() . = ..() @@ -171,29 +185,7 @@ dissipates_over_time = FALSE //It cant go smaller due to e loss. wander = TRUE event_chance = 20 + ignore_obstacles = TRUE /decl/singularity_stage/stage_five/grow_to(obj/effect/singularity/source) source.visible_message(SPAN_DANGER("\The [source] has grown out of control!")) - -/decl/singularity_stage/stage_five/shrink_to(obj/effect/singularity/source) - source.visible_message(SPAN_WARNING("\The [source] miraculously reduces in size and loses its supermatter properties.")) - -/decl/singularity_stage/stage_super - name = "super gravitational singularity" - desc = "A gravitational singularity with the properties of supermatter. It has the power to destroy worlds." - min_energy = 50000 - max_energy = INFINITY - stage_size = STAGE_SUPER - footprint = 6 - icon = 'icons/effects/352x352.dmi' - icon_state = "singularity_s11"//uh, whoever drew that, you know that black holes are supposed to look dark right? What's this, the clown's singulo? - pixel_x = -160 - pixel_y = -160 - grav_pull = 16 - consume_range = 5 - dissipates_over_time = 0 //It cant go smaller due to e loss - event_chance = 25 //Events will fire off more often. - wander = TRUE - -/decl/singularity_stage/stage_super/grow_to(obj/effect/singularity/source) - source.visible_message(SPAN_SINISTER("You witness the creation of a destructive force that cannot possibly be stopped by human hands.")) diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index bd129184831..a941debb006 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -88,7 +88,7 @@ var/global/list/solars_list = list() return TRUE else if (W) add_fingerprint(user) - current_health -= W.get_attack_force(user) + current_health -= W.expend_attack_force(user) healthcheck() return ..() diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index f163d0f64c7..613ae77ae76 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -39,17 +39,20 @@ pixel_y = rand(-randpixel, randpixel) . = ..() +/obj/item/ammo_casing/Destroy() + QDEL_NULL(BB) + return ..() + //removes the projectile from the ammo casing /obj/item/ammo_casing/proc/expend() . = BB BB = null set_dir(pick(global.alldirs)) //spin spent casings - // Aurora forensics port, gunpowder residue. if(leaves_residue) leave_residue() - update_icon() + update_name() /obj/item/ammo_casing/Crossed(atom/movable/AM) ..() @@ -119,6 +122,11 @@ else if(spent_icon && !BB) icon_state = spent_icon +/obj/item/ammo_casing/update_name() + . = ..() + if(!BB) + SetName("spent [name]") + /obj/item/ammo_casing/examine(mob/user) . = ..() if(caliber) @@ -270,10 +278,9 @@ var/global/list/magazine_icondata_states = list() /proc/magazine_icondata_cache_add(var/obj/item/ammo_magazine/M) var/list/icon_keys = list() var/list/ammo_states = list() - var/list/states = icon_states(M.icon) for(var/i = 0, i <= M.max_ammo, i++) var/ammo_state = "[M.icon_state]-[i]" - if(ammo_state in states) + if(check_state_in_icon(ammo_state, M.icon)) icon_keys += i ammo_states += ammo_state diff --git a/code/modules/projectiles/ammunition/chemdart.dm b/code/modules/projectiles/ammunition/chemdart.dm index bd8710bef92..fa7647a4dcf 100644 --- a/code/modules/projectiles/ammunition/chemdart.dm +++ b/code/modules/projectiles/ammunition/chemdart.dm @@ -2,7 +2,7 @@ name = "dart" icon_state = "dart" damage = 5 - sharp = 1 + sharp = TRUE embed = 1 //the dart is shot fast enough to pierce space suits, so I guess splintering inside the target can be a thing. Should be rare due to low damage. life_span = 15 //shorter range muzzle_type = null diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 4de22aa0616..d99abdef1e6 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -45,6 +45,7 @@ drop_sound = 'sound/foley/drop1.ogg' pickup_sound = 'sound/foley/pickup2.ogg' can_be_twohanded = TRUE // also checks one_hand_penalty + needs_attack_dexterity = DEXTERITY_WEAPONS var/fire_verb = "fire" var/waterproof = FALSE @@ -193,15 +194,20 @@ /obj/item/gun/proc/special_check(var/mob/user) if(!isliving(user)) - return 0 - if(!user.check_dexterity(DEXTERITY_WEAPONS)) - return 0 + return FALSE + + if(!user.check_dexterity(get_required_attack_dexterity(user))) + return FALSE + + if(is_secure_gun() && !free_fire() && (!authorized_modes[sel_mode] || !registered_owner)) + audible_message(SPAN_WARNING("\The [src] buzzes, refusing to fire."), hearing_distance = 3) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 10, 0) + return FALSE var/mob/living/M = user if(!safety() && world.time > last_safety_check + 5 MINUTES && !user.skill_check(SKILL_WEAPONS, SKILL_BASIC)) if(prob(30)) toggle_safety() - return 1 if(M.has_genetic_condition(GENE_COND_CLUMSY) && prob(40)) //Clumsy handling var/obj/P = consume_next_projectile() @@ -218,8 +224,9 @@ M.try_unequip(src) else handle_click_empty(user) - return 0 - return 1 + return FALSE + + return TRUE /obj/item/gun/emp_act(severity) for(var/obj/O in contents) @@ -744,6 +751,9 @@ return FALSE return TRUE +/obj/item/gun/get_quick_interaction_handler(mob/user) + return GET_DECL(/decl/interaction_handler/gun/toggle_safety) + /obj/item/gun/get_alt_interactions(mob/user) . = ..() LAZYADD(., /decl/interaction_handler/gun/toggle_safety) @@ -756,6 +766,7 @@ /decl/interaction_handler/gun/toggle_safety name = "Toggle Safety" + examine_desc = "toggle the safety" /decl/interaction_handler/gun/toggle_safety/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/gun/gun = target @@ -763,6 +774,7 @@ /decl/interaction_handler/gun/toggle_firemode name = "Change Firemode" + examine_desc = "change the firemode" /decl/interaction_handler/gun/toggle_firemode/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/gun/gun = target diff --git a/code/modules/projectiles/guns/launcher/bows/arrow.dm b/code/modules/projectiles/guns/launcher/bows/arrow.dm index 1fb6cc35867..073c4d5928b 100644 --- a/code/modules/projectiles/guns/launcher/bows/arrow.dm +++ b/code/modules/projectiles/guns/launcher/bows/arrow.dm @@ -5,8 +5,8 @@ plural_icon_state = ICON_STATE_WORLD + "-mult" max_icon_state = ICON_STATE_WORLD + "-max" w_class = ITEM_SIZE_NORMAL - sharp = 1 - edge = 0 + sharp = TRUE + edge = FALSE lock_picking_level = 3 material = /decl/material/solid/organic/wood/oak material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC diff --git a/code/modules/projectiles/guns/launcher/syringe_gun.dm b/code/modules/projectiles/guns/launcher/syringe_gun.dm index d44be24924d..69fa0078472 100644 --- a/code/modules/projectiles/guns/launcher/syringe_gun.dm +++ b/code/modules/projectiles/guns/launcher/syringe_gun.dm @@ -30,7 +30,7 @@ return TRUE syringe = I to_chat(user, "You carefully insert [syringe] into [src].") - sharp = TRUE + set_sharp(TRUE) name = "syringe dart" update_icon() return TRUE @@ -41,7 +41,7 @@ to_chat(user, "You remove [syringe] from [src].") user.put_in_hands(syringe) syringe = null - sharp = initial(sharp) + set_sharp(initial(sharp)) SetName(initial(name)) update_icon() diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 1466e138166..e34fd58e061 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -49,6 +49,12 @@ ammo_magazine = new magazine_type(src) update_icon() +/obj/item/gun/projectile/Destroy() + chambered = null + loaded.Cut() + ammo_magazine = null + return ..() + /obj/item/gun/projectile/consume_next_projectile() if(!is_jammed && prob(jam_chance)) src.visible_message("\The [src] jams!") @@ -74,8 +80,8 @@ if(handle_casings == HOLD_CASINGS) ammo_magazine.stored_ammo += chambered ammo_magazine.initial_ammo-- - else if(ammo_magazine.stored_ammo.len) - chambered = ammo_magazine.stored_ammo[ammo_magazine.stored_ammo.len] + else if(length(ammo_magazine.stored_ammo)) + chambered = ammo_magazine.stored_ammo[length(ammo_magazine.stored_ammo)] if(handle_casings != HOLD_CASINGS) ammo_magazine.stored_ammo -= chambered @@ -334,9 +340,9 @@ /obj/item/gun/projectile/proc/get_ammo_indicator() var/base_state = get_world_inventory_state() - if(!ammo_magazine || !LAZYLEN(ammo_magazine.stored_ammo)) + if(!ammo_magazine || !ammo_magazine.get_stored_ammo_count()) return mutable_appearance(icon, "[base_state]_ammo_bad") - else if(LAZYLEN(ammo_magazine.stored_ammo) <= 0.5 * ammo_magazine.max_ammo) + else if(LAZYLEN(ammo_magazine.get_stored_ammo_count()) <= 0.5 * ammo_magazine.max_ammo) return mutable_appearance(icon, "[base_state]_ammo_warn") else return mutable_appearance(icon, "[base_state]_ammo_ok") @@ -354,6 +360,7 @@ /decl/interaction_handler/projectile/remove_silencer name = "Remove Silencer" + examine_desc = "remove the silencer" /decl/interaction_handler/projectile/remove_silencer/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/gun/projectile/gun = target @@ -361,6 +368,7 @@ /decl/interaction_handler/projectile/unload_ammo name = "Remove Ammunition" + examine_desc = "unload the ammunition" /decl/interaction_handler/projectile/unload_ammo/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/gun/projectile/gun = target diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 7505d0152aa..802b1f93863 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -35,7 +35,7 @@ /obj/item/gun/projectile/automatic/smg/on_update_icon() ..() if(ammo_magazine) - add_overlay("[get_world_inventory_state()]mag-[round(length(ammo_magazine.stored_ammo),5)]") + add_overlay("[get_world_inventory_state()]mag-[round(ammo_magazine.get_stored_ammo_count(),5)]") /obj/item/gun/projectile/automatic/assault_rifle name = "assault rifle" diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm index 6c00e6c2594..ee2edcdcfae 100644 --- a/code/modules/projectiles/guns/projectile/dartgun.dm +++ b/code/modules/projectiles/guns/projectile/dartgun.dm @@ -37,13 +37,13 @@ /obj/item/gun/projectile/dartgun/on_update_icon() ..() if(ammo_magazine) - icon_state = "[get_world_inventory_state()]-[clamp(length(ammo_magazine.get_stored_ammo_count()), 0, 5)]" + icon_state = "[get_world_inventory_state()]-[clamp(ammo_magazine.get_stored_ammo_count(), 0, 5)]" else icon_state = get_world_inventory_state() /obj/item/gun/projectile/dartgun/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) if(overlay && (slot in user_mob?.get_held_item_slots()) && ammo_magazine) - overlay.icon_state += "-[clamp(length(ammo_magazine.get_stored_ammo_count()), 0, 5)]" + overlay.icon_state += "-[clamp(ammo_magazine.get_stored_ammo_count(), 0, 5)]" . = ..() /obj/item/gun/projectile/dartgun/consume_next_projectile() diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 638376989ee..e64e7c3f731 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -17,7 +17,7 @@ /obj/item/gun/projectile/pistol/update_base_icon_state() . = ..() - if(!length(ammo_magazine?.stored_ammo)) + if(!ammo_magazine?.get_stored_ammo_count()) var/empty_state = "[icon_state]-e" if(check_state_in_icon(empty_state, icon)) icon_state = empty_state diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index 0f54520856d..464ce437f7c 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -74,6 +74,7 @@ /decl/interaction_handler/revolver_spin_cylinder name = "Spin Cylinder" expected_target_type = /obj/item/gun/projectile/revolver + examine_desc = "spin the cylinder" /decl/interaction_handler/revolver_spin_cylinder/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/gun/projectile/revolver/R = target diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 07ad3c6c686..152cb7a6ab5 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -14,6 +14,18 @@ is_spawnable_type = FALSE atom_damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, ELECTROCUTE are the only things that should be in here, Try not to use PAIN as it doesn't go through stun_effect_act + // Code for handling tails, if any. + /// If the projectile leaves a trail. + var/proj_trail = FALSE + /// How long the trail lasts. + var/proj_trail_lifespan = 0 + /// What icon to use for the projectile trail. + var/proj_trail_icon = 'icons/effects/projectiles/trail.dmi' + /// What icon_state to use for the projectile trail. + var/proj_trail_icon_state = "trail" + /// Any extant trail effects. + var/list/proj_trails + var/bumped = 0 //Prevents it from hitting more than one guy at once var/def_zone = "" //Aiming at var/atom/movable/firer = null//Who shot it @@ -316,7 +328,15 @@ return /obj/item/projectile/proc/before_move() - return + if(!proj_trail || !isturf(loc) || !proj_trail_icon || !proj_trail_icon_state || !proj_trail_lifespan) + return + var/obj/effect/overlay/projectile_trail/trail = new(loc) + trail.master = src + trail.icon = proj_trail_icon + trail.icon_state = proj_trail_icon_state + trail.set_density(FALSE) + LAZYADD(proj_trails, trail) + QDEL_IN(trail, proj_trail_lifespan) /obj/item/projectile/proc/after_move() if(hitscan && tracer_type && !(locate(/obj/effect/projectile) in loc)) @@ -609,6 +629,7 @@ trajectory.initialize_location(target.x, target.y, target.z, 0, 0) /obj/item/projectile/Destroy() + QDEL_NULL_LIST(proj_trails) if(hitscan) if(loc && trajectory) var/datum/point/pcache = trajectory.copy_to() @@ -650,4 +671,4 @@ QDEL_NULL(beam_index) /obj/item/projectile/proc/update_effect(var/obj/effect/projectile/effect) - return \ No newline at end of file + return diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index dc643dcdc3b..8df181e9195 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -7,7 +7,7 @@ pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GLASS | PASS_FLAG_GRILLE damage = 40 atom_damage_type = BURN - sharp = 1 //concentrated burns + sharp = TRUE //concentrated burns damage_flags = DAM_LASER eyeblur = 4 hitscan = 1 @@ -210,7 +210,7 @@ icon_state = "stun" fire_sound = 'sound/weapons/Taser.ogg' damage_flags = 0 - sharp = 0 //not a laser + sharp = FALSE //not a laser damage = 1//flavor burn! still not a laser, dmg will be reduce by energy resistance not laser resistances atom_damage_type = BURN eyeblur = 1//Some feedback that you've been hit @@ -241,8 +241,8 @@ icon_state = "omnilaser" fire_sound = 'sound/weapons/plasma_cutter.ogg' damage = 15 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE atom_damage_type = BURN life_span = 5 pass_flags = PASS_FLAG_TABLE @@ -317,7 +317,7 @@ name = "dark matter wave" icon_state = "darkt" damage_flags = 0 - sharp = 0 //not a laser + sharp = FALSE //not a laser agony = 40 atom_damage_type = STUN muzzle_type = /obj/effect/projectile/muzzle/darkmattertaser diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 8e241456d22..ff77df0fa33 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -55,12 +55,9 @@ var/mob/living/new_mob = apply_transformation(M, choice) if(new_mob) new_mob.set_intent(I_FLAG_HARM) - if(M.mind) - for (var/spell/S in M.mind.learned_spells) - new_mob.add_spell(new S.type) - new_mob.set_intent(I_FLAG_HARM) - transfer_key_from_mob_to_mob(M, new_mob) - to_chat(new_mob, "Your form morphs into that of \a [choice].") + new_mob.copy_abilities_from(M) + transfer_key_from_mob_to_mob(M, new_mob) + to_chat(new_mob, "Your form morphs into that of \a [choice].") else new_mob = M if(new_mob) @@ -68,4 +65,3 @@ if(new_mob != M && !QDELETED(M)) qdel(M) - diff --git a/code/modules/projectiles/secure.dm b/code/modules/projectiles/secure.dm index 241a0e0a8af..6902b3bddb1 100644 --- a/code/modules/projectiles/secure.dm +++ b/code/modules/projectiles/secure.dm @@ -99,14 +99,6 @@ var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) return security_state.current_security_level_is_same_or_higher_than(security_state.high_security_level) -/obj/item/gun/special_check() - if(is_secure_gun() && !free_fire() && (!authorized_modes[sel_mode] || !registered_owner)) - audible_message(SPAN_WARNING("\The [src] buzzes, refusing to fire."), hearing_distance = 3) - playsound(loc, 'sound/machines/buzz-sigh.ogg', 10, 0) - return 0 - - . = ..() - /obj/item/gun/get_next_firemode() if(!is_secure_gun()) return ..() diff --git a/code/modules/projectiles/targeting/targeting_overlay.dm b/code/modules/projectiles/targeting/targeting_overlay.dm index 5d5b1f9ba65..7210efedb4c 100644 --- a/code/modules/projectiles/targeting/targeting_overlay.dm +++ b/code/modules/projectiles/targeting/targeting_overlay.dm @@ -34,27 +34,24 @@ // Update HUD icons. if(owner.gun_move_icon) if(!(target_permissions & TARGET_CAN_MOVE)) - owner.gun_move_icon.icon_state = "no_walk0" owner.gun_move_icon.SetName("Allow Movement") else - owner.gun_move_icon.icon_state = "no_walk1" owner.gun_move_icon.SetName("Disallow Movement") + owner.gun_move_icon.update_icon() if(owner.item_use_icon) if(!(target_permissions & TARGET_CAN_CLICK)) - owner.item_use_icon.icon_state = "no_item0" owner.item_use_icon.SetName("Allow Item Use") else - owner.item_use_icon.icon_state = "no_item1" owner.item_use_icon.SetName("Disallow Item Use") + owner.item_use_icon.update_icon() if(owner.radio_use_icon) if(!(target_permissions & TARGET_CAN_RADIO)) - owner.radio_use_icon.icon_state = "no_radio0" owner.radio_use_icon.SetName("Allow Radio Use") else - owner.radio_use_icon.icon_state = "no_radio1" owner.radio_use_icon.SetName("Disallow Radio Use") + owner.radio_use_icon.update_icon() var/message = "no longer permitted to " var/use_span = "warning" @@ -206,7 +203,7 @@ if(!no_message) to_chat(owner, "You will no longer aim rather than fire.") owner.client.remove_gun_icons() - owner.gun_setting_icon.icon_state = "gun[active]" + owner.gun_setting_icon.update_icon() /obj/aiming_overlay/proc/cancel_aiming(var/no_message = 0) if(!aiming_with || !aiming_at) diff --git a/code/modules/prometheus_metrics/metric_family.dm b/code/modules/prometheus_metrics/metric_family.dm index 373130e297a..0054e97a257 100644 --- a/code/modules/prometheus_metrics/metric_family.dm +++ b/code/modules/prometheus_metrics/metric_family.dm @@ -1,11 +1,11 @@ // Datum used for gathering a set of prometheus metrics. -/datum/metric_family +/decl/metric_family var/name = null var/metric_type = null var/help = null // Collect should return a list of lists with two entries, one being a list and the other being a number. -/datum/metric_family/proc/collect() +/decl/metric_family/proc/collect() var/list/out = list() out[++out.len] = list(list("foo" = "bar"), 3.14) @@ -15,9 +15,9 @@ // _to_proto will call the collect() method and format its result in a list // suitable for encoding as a JSON protobuf mapping. -/datum/metric_family/proc/_to_proto() +/decl/metric_family/proc/_to_proto() var/list/collected = collect() - + var/list/out = list( "name" = name, "type" = metric_type, @@ -36,7 +36,7 @@ label_pairs[++label_pairs.len] = list("name" = k, "value" = m[1][k]) metrics[++metrics.len] = list("label" = label_pairs, PROMETHEUS_METRIC_NAME(metric_type) = list("value" = m[2])) - + if(metrics.len == 0) return null out["metric"] = metrics diff --git a/code/modules/prometheus_metrics/metrics.dm b/code/modules/prometheus_metrics/metrics.dm index 95e2f819002..ad98b6c178f 100644 --- a/code/modules/prometheus_metrics/metrics.dm +++ b/code/modules/prometheus_metrics/metrics.dm @@ -1,24 +1,15 @@ -var/global/datum/prometheus_metrics/prometheus_metrics = new - // prometheus_metrics holds a list of metric_family datums and uses them to // create a json protobuf. -/datum/prometheus_metrics +/decl/prometheus_metrics var/list/metric_families -/datum/prometheus_metrics/New() - metric_families = list() - for(var/T in subtypesof(/datum/metric_family)) - var/datum/metric_family/mf = T - if(initial(mf.name) == null || initial(mf.metric_type) == null) - continue - metric_families += new T - -/datum/prometheus_metrics/proc/collect() +/decl/prometheus_metrics/proc/collect() var/list/out = list() - for(var/datum/metric_family/MF in metric_families) - var/proto = MF._to_proto() + for(var/decl/metric_family/metric_family in decls_repository.get_decls_of_type_unassociated(/decl/metric_family)) + var/proto = metric_family._to_proto() if(proto != null) - out[++out.len] = MF._to_proto() - + // out += proto will try to merge the lists, we have to insert it at the end instead + out[++out.len] = proto + return json_encode(out) diff --git a/code/modules/prometheus_metrics/metrics/byond.dm b/code/modules/prometheus_metrics/metrics/byond.dm index 232bce38faf..3aa58ba942d 100644 --- a/code/modules/prometheus_metrics/metrics/byond.dm +++ b/code/modules/prometheus_metrics/metrics/byond.dm @@ -1,29 +1,29 @@ // byond-specific metrics -/datum/metric_family/byond_time +/decl/metric_family/byond_time name = "byond_world_time_seconds" metric_type = PROMETHEUS_METRIC_COUNTER help = "Counter of 'game-time' seconds since server startup" -/datum/metric_family/byond_time/collect() +/decl/metric_family/byond_time/collect() return list(list(null, world.time / 10)) -/datum/metric_family/byond_tick_lag +/decl/metric_family/byond_tick_lag name = "byond_tick_lag" metric_type = PROMETHEUS_METRIC_GAUGE help = "Current value of world.tick_lag" -/datum/metric_family/byond_tick_lag/collect() +/decl/metric_family/byond_tick_lag/collect() return list(list(null, world.tick_lag)) -/datum/metric_family/byond_players +/decl/metric_family/byond_players name = "byond_players" metric_type = PROMETHEUS_METRIC_GAUGE help = "Number of players currently connected to the server" -/datum/metric_family/byond_players/collect() +/decl/metric_family/byond_players/collect() var/c = 0 for(var/client/C) if(C.connection == "seeker" || C.connection == "web") @@ -31,10 +31,10 @@ return list(list(null, c)) -/datum/metric_family/byond_cpu +/decl/metric_family/byond_cpu name = "byond_cpu" metric_type = PROMETHEUS_METRIC_GAUGE help = "Current value of world.cpu" -/datum/metric_family/byond_cpu/collect() +/decl/metric_family/byond_cpu/collect() return list(list(null, world.cpu)) diff --git a/code/modules/prometheus_metrics/metrics/ss13.dm b/code/modules/prometheus_metrics/metrics/ss13.dm index 5c6d315b04d..f686f1d1bed 100644 --- a/code/modules/prometheus_metrics/metrics/ss13.dm +++ b/code/modules/prometheus_metrics/metrics/ss13.dm @@ -1,11 +1,11 @@ // ss13-specific metrics -/datum/metric_family/ss13_controller_time_seconds +/decl/metric_family/ss13_controller_time_seconds name = "ss13_controller_time_seconds" metric_type = PROMETHEUS_METRIC_COUNTER help = "Counter of time spent in a controller in seconds" -/datum/metric_family/ss13_controller_time_seconds/collect() +/decl/metric_family/ss13_controller_time_seconds/collect() var/list/out = list() if(Master) for(var/name in Master.total_run_times) @@ -14,23 +14,23 @@ return out -/datum/metric_family/ss13_master_runlevel +/decl/metric_family/ss13_master_runlevel name = "ss13_master_runlevel" metric_type = PROMETHEUS_METRIC_GAUGE help = "Current MC runlevel" -/datum/metric_family/ss13_master_runlevel/collect() +/decl/metric_family/ss13_master_runlevel/collect() if(Master) return list(list(null, Master.current_runlevel)) return list() -/datum/metric_family/ss13_garbage_queue_length +/decl/metric_family/ss13_garbage_queue_length name = "ss13_garbage_queue_length" metric_type = PROMETHEUS_METRIC_GAUGE help = "Length of SSgarbage queues" -/datum/metric_family/ss13_garbage_queue_length/collect() +/decl/metric_family/ss13_garbage_queue_length/collect() var/list/out = list() if(SSgarbage) @@ -40,12 +40,12 @@ return out -/datum/metric_family/ss13_garbage_queue_results +/decl/metric_family/ss13_garbage_queue_results name = "ss13_garbage_queue_results" metric_type = PROMETHEUS_METRIC_COUNTER help = "Counter of pass/fail results for SSgarbage queues" -/datum/metric_family/ss13_garbage_queue_results/collect() +/decl/metric_family/ss13_garbage_queue_results/collect() var/list/out = list() if(SSgarbage) @@ -56,12 +56,12 @@ return out -/datum/metric_family/ss13_garbage_total_cleaned +/decl/metric_family/ss13_garbage_total_cleaned name = "ss13_garbage_total_cleaned" metric_type = PROMETHEUS_METRIC_COUNTER help = "Counter for number of objects deleted/GCed by SSgarbage" -/datum/metric_family/ss13_garbage_total_cleaned/collect() +/decl/metric_family/ss13_garbage_total_cleaned/collect() var/list/out = list() if(SSgarbage) diff --git a/code/modules/random_map/automata/caves.dm b/code/modules/random_map/automata/caves.dm index d12f5a79204..91044cd99d5 100644 --- a/code/modules/random_map/automata/caves.dm +++ b/code/modules/random_map/automata/caves.dm @@ -2,7 +2,7 @@ iterations = 5 descriptor = "moon caves" wall_type = /turf/wall/natural - floor_type = /turf/floor + floor_type = /turf/floor/barren target_turf_type = /turf/unsimulated/mask var/sparse_mineral_turf = /turf/wall/natural/random diff --git a/code/modules/random_map/drop/drop_types.dm b/code/modules/random_map/drop/drop_types.dm index a9f290a0aa6..1560263f996 100644 --- a/code/modules/random_map/drop/drop_types.dm +++ b/code/modules/random_map/drop/drop_types.dm @@ -22,12 +22,6 @@ var/global/list/datum/supply_drop_loot/supply_drop /datum/supply_drop_loot/dd_SortValue() return name -/datum/supply_drop_loot/supermatter - name = "Supermatter" -/datum/supply_drop_loot/supermatter/New() - ..() - contents = list(/obj/machinery/power/supermatter) - /datum/supply_drop_loot/lasers name = "Lasers" container = /obj/structure/largecrate diff --git a/code/modules/random_map/drop/droppod.dm b/code/modules/random_map/drop/droppod.dm index ac23a7b14e8..926ed6b2d0b 100644 --- a/code/modules/random_map/drop/droppod.dm +++ b/code/modules/random_map/drop/droppod.dm @@ -136,7 +136,6 @@ drop = pick(supplied_drop_types) supplied_drop_types -= drop if(istype(drop)) - drop.tag = null if(drop.buckled) drop.buckled = null drop.forceMove(T) @@ -168,7 +167,6 @@ return for(var/i=0;i length(map)) return 0 var/turf/T = locate((origin_x-1)+x,(origin_y-1)+y,origin_z) if(!T || (target_turf_type && !istype(T,target_turf_type))) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 28b4f593c18..9b76b5dec22 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -1,8 +1,8 @@ var/global/obj/temp_reagents_holder = new var/global/datum/reagents/sink/infinite_reagent_sink = new -/atom/proc/add_to_reagents(reagent_type, amount, data, safety = FALSE, defer_update = FALSE) - return reagents?.add_reagent(reagent_type, amount, data, safety, defer_update) +/atom/proc/add_to_reagents(reagent_type, amount, data, safety = FALSE, defer_update = FALSE, phase = null) + return reagents?.add_reagent(reagent_type, amount, data, safety, defer_update, phase) /atom/proc/remove_from_reagents(reagent_type, amount, safety = FALSE, defer_update = FALSE) return reagents?.remove_reagent(reagent_type, amount, safety, defer_update) @@ -549,11 +549,11 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /datum/reagents/proc/trans_to_holder(var/datum/reagents/target, var/amount = 1, var/multiplier = 1, var/copy = 0, var/safety = 0, var/defer_update = FALSE, var/list/skip_reagents, var/transferred_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID)) if(!target || !istype(target)) - return + return 0 amount = max(0, min(amount, total_volume, REAGENTS_FREE_SPACE(target) / multiplier)) if(!amount) - return + return 0 var/part = amount if(skip_reagents) @@ -561,7 +561,7 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new for(var/rtype in skip_reagents) using_volume -= LAZYACCESS(reagent_volumes, rtype) if(using_volume <= 0) - return + return 0 part /= using_volume else var/using_volume = total_volume @@ -797,37 +797,55 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new // Options are touch_turf(), touch_mob() and touch_obj(). This does not handle transferring reagents to things. // For example, splashing someone with water will get them wet and extinguish them if they are on fire, // even if they are wearing an impermeable suit that prevents the reagents from contacting the skin. -/datum/reagents/proc/touch_mob(var/mob/target) + +/datum/reagents/proc/touch_atom(atom/target, touch_atoms = TRUE) + if(ismob(target)) + return touch_mob(target) + if(isobj(target)) + return touch_obj(target) + if(isturf(target)) + return touch_turf(target, touch_atoms) + return FALSE + +/datum/reagents/proc/touch_mob(mob/target) if(!target || !istype(target) || !target.simulated) return for(var/rtype in reagent_volumes) var/decl/material/current = GET_DECL(rtype) current.touch_mob(target, REAGENT_VOLUME(src, rtype), src) -/datum/reagents/proc/touch_turf(var/turf/target) - if(!istype(target) || !target.simulated) +/datum/reagents/proc/touch_turf(turf/touching_turf, touch_atoms = TRUE) + + if(!istype(touching_turf) || !touching_turf.simulated) return + for(var/rtype in reagent_volumes) var/decl/material/current = GET_DECL(rtype) - current.touch_turf(target, REAGENT_VOLUME(src, rtype), src) + current.touch_turf(touching_turf, REAGENT_VOLUME(src, rtype), src) + var/dirtiness = get_dirtiness() if(dirtiness <= DIRTINESS_CLEAN) - target.clean() - target.remove_cleanables() - if(dirtiness != DIRTINESS_NEUTRAL) - if(dirtiness > DIRTINESS_NEUTRAL) - target.add_dirt(ceil(total_volume * dirtiness)) - else - if(dirtiness <= DIRTINESS_STERILE) - target.germ_level -= min(total_volume*20, target.germ_level) - for(var/obj/item/I in target.contents) - I.was_bloodied = null - for(var/obj/effect/decal/cleanable/blood/B in target) - qdel(B) - if(dirtiness <= DIRTINESS_CLEAN) - target.clean() - -/datum/reagents/proc/touch_obj(var/obj/target) + touching_turf.clean() + touching_turf.remove_cleanables() + + if(dirtiness > DIRTINESS_NEUTRAL) + touching_turf.add_dirt(ceil(total_volume * dirtiness)) + else if(dirtiness < DIRTINESS_NEUTRAL) + if(dirtiness <= DIRTINESS_STERILE) + touching_turf.germ_level -= min(total_volume*20, touching_turf.germ_level) + for(var/obj/item/I in touching_turf.contents) + I.was_bloodied = null + for(var/obj/effect/decal/cleanable/blood/B in touching_turf) + qdel(B) + if(dirtiness <= DIRTINESS_CLEAN) + touching_turf.clean() + + if(touch_atoms) + for(var/atom/movable/thing in touching_turf.get_contained_external_atoms()) + if(thing.simulated && !istype(thing, /obj/effect/effect/smoke/chem)) + touch_atom(thing) + +/datum/reagents/proc/touch_obj(obj/target) if(!target || !istype(target) || !target.simulated) return for(var/rtype in reagent_volumes) @@ -872,13 +890,13 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /datum/reagents/proc/trans_to_turf(var/turf/target, var/amount = 1, var/multiplier = 1, var/copy = 0, var/defer_update = FALSE, var/transferred_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID)) if(!target?.simulated) - return + return 0 // If we're only dumping solids, and there's not enough liquid present on the turf to make a slurry, we dump the solids directly. // This avoids creating an unnecessary reagent holder that won't be immediately deleted. if((!(transferred_phases & MAT_PHASE_LIQUID) || !total_liquid_volume) && (target.reagents?.total_liquid_volume < FLUID_SLURRY)) var/datum/reagents/R = new /datum/reagents(amount, global.temp_reagents_holder) - trans_to_holder(R, amount, multiplier, copy, TRUE, defer_update = defer_update, transferred_phases = MAT_PHASE_SOLID) + . = trans_to_holder(R, amount, multiplier, copy, TRUE, defer_update = defer_update, transferred_phases = MAT_PHASE_SOLID) R.touch_turf(target) target.dump_solid_reagents(R) qdel(R) @@ -887,16 +905,16 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new if(!target.reagents) target.create_reagents(FLUID_MAX_DEPTH) - trans_to_holder(target.reagents, amount, multiplier, copy, defer_update = defer_update, transferred_phases = transferred_phases) + . = trans_to_holder(target.reagents, amount, multiplier, copy, defer_update = defer_update, transferred_phases = transferred_phases) // Deferred updates are presumably being done by SSfluids. // Do an immediate fluid_act call rather than waiting for SSfluids to proc. - if(!defer_update) + if(!defer_update && target.reagents.total_volume >= FLUID_PUDDLE) target.fluid_act(target.reagents) // Objects may or may not have reagents; if they do, it's probably a beaker or something and we need to transfer properly; otherwise, just touch. /datum/reagents/proc/trans_to_obj(var/obj/target, var/amount = 1, var/multiplier = 1, var/copy = 0, var/defer_update = FALSE, var/transferred_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID)) if(!target || !target.simulated) - return + return 0 if(!target.reagents) var/datum/reagents/R = new /datum/reagents(amount * multiplier, global.temp_reagents_holder) diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index 0216e31ad13..e304fbec91e 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -223,7 +223,7 @@ . += "

    Description:
    " if(detailed_blood && istype(reagent, /decl/material/liquid/blood)) var/blood_data = REAGENT_DATA(beaker?.reagents, /decl/material/liquid/blood) - . += "Blood Type: [LAZYACCESS(blood_data, DATA_BLOOD_TYPE)]
    DNA: [LAZYACCESS(blood_data, "blood.DNA")]" + . += "Blood Type: [LAZYACCESS(blood_data, DATA_BLOOD_TYPE)]
    DNA: [LAZYACCESS(blood_data, DATA_BLOOD_DNA)]" else . += "[reagent.lore_text]" . += "


    (Back)" diff --git a/code/modules/reagents/Chemistry-Metabolism.dm b/code/modules/reagents/Chemistry-Metabolism.dm index e1002809b1f..9c78f190ca2 100644 --- a/code/modules/reagents/Chemistry-Metabolism.dm +++ b/code/modules/reagents/Chemistry-Metabolism.dm @@ -20,7 +20,7 @@ parent = null return ..() -/datum/reagents/metabolism/proc/metabolize(var/list/dosage_tracker) +/datum/reagents/metabolism/proc/metabolize(list/dosage_tracker) if(!parent || total_volume < MINIMUM_CHEMICAL_VOLUME || !length(reagent_volumes)) return for(var/rtype in reagent_volumes) diff --git a/code/modules/reagents/chems/chems_alcohol.dm b/code/modules/reagents/chems/chems_alcohol.dm index aa7ca92f847..44384d64365 100644 --- a/code/modules/reagents/chems/chems_alcohol.dm +++ b/code/modules/reagents/chems/chems_alcohol.dm @@ -226,8 +226,8 @@ if(M.bodytemperature > 310) M.bodytemperature = max(310, M.bodytemperature - (5 * TEMPERATURE_DAMAGE_COEFFICIENT)) -/decl/material/liquid/alcohol/coffee/affect_overdose(mob/living/M, total_dose) - ADJ_STATUS(M, STAT_JITTER, 5) +/decl/material/liquid/alcohol/coffee/affect_overdose(mob/living/victim, total_dose) + ADJ_STATUS(victim, STAT_JITTER, 5) /decl/material/liquid/alcohol/melonliquor name = "melon liqueur" diff --git a/code/modules/reagents/chems/chems_blood.dm b/code/modules/reagents/chems/chems_blood.dm index e3a08e5b873..ab5f8e6dd8e 100644 --- a/code/modules/reagents/chems/chems_blood.dm +++ b/code/modules/reagents/chems/chems_blood.dm @@ -46,12 +46,12 @@ for(var/chem in other_chems) my_chems[chem] = my_chems[chem] + other_chems[chem] -/decl/material/liquid/blood/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) +/decl/material/liquid/blood/touch_turf(var/turf/touching_turf, var/amount, var/datum/reagents/holder) var/data = REAGENT_DATA(holder, type) - if(!istype(T) || REAGENT_VOLUME(holder, type) < 3) + if(!istype(touching_turf) || REAGENT_VOLUME(holder, type) < 3) return - var/weakref/W = LAZYACCESS(data, DATA_BLOOD_DONOR) - blood_splatter(T, W?.resolve() || holder.my_atom, 1) + var/weakref/donor = LAZYACCESS(data, DATA_BLOOD_DONOR) + blood_splatter(touching_turf, donor?.resolve() || holder.my_atom, 1) /decl/material/liquid/blood/affect_ingest(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() @@ -72,7 +72,7 @@ /decl/material/liquid/blood/get_reagent_color(datum/reagents/holder) var/list/blood_data = REAGENT_DATA(holder, type) - return blood_data?["blood_color"] || ..() + return blood_data?[DATA_BLOOD_COLOR] || ..() /decl/material/liquid/coagulated_blood name = "coagulated blood" diff --git a/code/modules/reagents/chems/chems_compounds.dm b/code/modules/reagents/chems/chems_compounds.dm index 829ba1df5b2..1558ef76762 100644 --- a/code/modules/reagents/chems/chems_compounds.dm +++ b/code/modules/reagents/chems/chems_compounds.dm @@ -35,11 +35,11 @@ addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living/human, update_eyes)), 5 SECONDS) . = ..() -/decl/material/liquid/glowsap/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/glowsap/affect_overdose(mob/living/victim, total_dose) . = ..() - M.add_chemical_effect(CE_TOXIN, 1) - M.set_hallucination(60, 20) - SET_STATUS_MAX(M, STAT_DRUGGY, 10) + victim.add_chemical_effect(CE_TOXIN, 1) + victim.set_hallucination(60, 20) + SET_STATUS_MAX(victim, STAT_DRUGGY, 10) /decl/material/solid/blackpepper name = "black pepper" diff --git a/code/modules/reagents/chems/chems_drinks.dm b/code/modules/reagents/chems/chems_drinks.dm index af54d84cd62..eca5276b785 100644 --- a/code/modules/reagents/chems/chems_drinks.dm +++ b/code/modules/reagents/chems/chems_drinks.dm @@ -379,9 +379,9 @@ ..() M.add_chemical_effect(CE_PULSE, 2) -/decl/material/liquid/drink/coffee/affect_overdose(mob/living/M, total_dose) - ADJ_STATUS(M, STAT_JITTER, 5) - M.add_chemical_effect(CE_PULSE, 1) +/decl/material/liquid/drink/coffee/affect_overdose(mob/living/victim, total_dose) + ADJ_STATUS(victim, STAT_JITTER, 5) + victim.add_chemical_effect(CE_PULSE, 1) /decl/material/liquid/drink/coffee/build_presentation_name_from_reagents(var/obj/item/prop, var/supplied) diff --git a/code/modules/reagents/chems/chems_drugs.dm b/code/modules/reagents/chems/chems_drugs.dm index 9d36a9a5ae3..e9345200297 100644 --- a/code/modules/reagents/chems/chems_drugs.dm +++ b/code/modules/reagents/chems/chems_drugs.dm @@ -61,9 +61,9 @@ LAZYSET(holder.reagent_data, type, world.time) to_chat(M, "You feel invigorated and calm.") -/decl/material/liquid/nicotine/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/nicotine/affect_overdose(mob/living/victim, total_dose) ..() - M.add_chemical_effect(CE_PULSE, 2) + victim.add_chemical_effect(CE_PULSE, 2) /decl/material/liquid/sedatives name = "sedatives" @@ -253,10 +253,9 @@ if(istype(M)) M.remove_client_color(/datum/client_color/noir/thirdeye) -/decl/material/liquid/glowsap/gleam/affect_overdose(mob/living/M, total_dose) - M.take_damage(rand(1, 5), BRAIN) - if(ishuman(M) && prob(10)) - var/mob/living/human/H = M - H.seizure() +/decl/material/liquid/glowsap/gleam/affect_overdose(mob/living/victim, total_dose) + victim.take_damage(rand(1, 5), BRAIN) + if(prob(10)) + victim.seizure() if(prob(10)) - to_chat(M, SPAN_DANGER("[pick(overdose_messages)]")) + to_chat(victim, SPAN_DANGER("[pick(overdose_messages)]")) diff --git a/code/modules/reagents/chems/chems_medicines.dm b/code/modules/reagents/chems/chems_medicines.dm index 701d2f1d476..7b8357a1767 100644 --- a/code/modules/reagents/chems/chems_medicines.dm +++ b/code/modules/reagents/chems/chems_medicines.dm @@ -52,14 +52,12 @@ uid = "chem_styptic" var/effectiveness = 1 -/decl/material/liquid/brute_meds/affect_overdose(mob/living/M, var/datum/reagents/holder) +/decl/material/liquid/brute_meds/affect_overdose(mob/living/victim, total_dose) ..() - if(ishuman(M)) - M.add_chemical_effect(CE_BLOCKAGE, (15 + REAGENT_VOLUME(holder, type))/100) - var/mob/living/human/H = M - for(var/obj/item/organ/external/E in H.get_external_organs()) - if(E.status & ORGAN_ARTERY_CUT && prob(2 + REAGENT_VOLUME(holder, type) / overdose)) - E.status &= ~ORGAN_ARTERY_CUT + victim.add_chemical_effect(CE_BLOCKAGE, (15 + total_dose) / 100) + for(var/obj/item/organ/external/limb in victim.get_external_organs()) + if((limb.status & ORGAN_ARTERY_CUT) && prob(2 + total_dose / overdose)) + limb.status &= ~ORGAN_ARTERY_CUT //This is a logistic function that effectively doubles the healing rate as brute amounts get to around 200. Any injury below 60 is essentially unaffected and there's a scaling inbetween. #define ADJUSTED_REGEN_VAL(X) (6+(6/(1+200*2.71828**(-0.05*(X))))) @@ -169,12 +167,10 @@ if(immunity_to_add > 0) M.adjust_immunity(immunity_to_add) // Rapidly brings someone up to half immunity. -/decl/material/liquid/immunobooster/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/immunobooster/affect_overdose(mob/living/victim, total_dose) ..() - M.add_chemical_effect(CE_TOXIN, 1) - var/mob/living/human/H = M - if(istype(H)) - M.adjust_immunity(-0.5) + victim.add_chemical_effect(CE_TOXIN, 1) + victim.adjust_immunity(-0.5) /decl/material/liquid/stimulants name = "stimulants" @@ -240,12 +236,12 @@ exoplanet_rarity_gas = MAT_RARITY_EXOTIC uid = "chem_antibiotics" -/decl/material/liquid/antibiotics/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/antibiotics/affect_overdose(mob/living/victim, total_dose) ..() - M.adjust_immunity(-0.5) - M.immunity = max(M.immunity - 0.25, 0) + victim.adjust_immunity(-0.5) + victim.immunity = max(victim.immunity - 0.25, 0) if(prob(2)) - M.immunity_norm = max(M.immunity_norm - 1, 0) + victim.immunity_norm = max(victim.immunity_norm - 1, 0) /decl/material/liquid/retrovirals name = "retrovirals" @@ -258,14 +254,12 @@ exoplanet_rarity_gas = MAT_RARITY_EXOTIC uid = "chem_retrovirals" -/decl/material/liquid/retrovirals/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/retrovirals/affect_overdose(mob/living/victim, total_dose) . = ..() - if(ishuman(M)) - var/mob/living/human/H = M - for(var/obj/item/organ/external/E in H.get_external_organs()) - if(!BP_IS_PROSTHETIC(E) && prob(25) && !(E.status & ORGAN_MUTATED)) - E.mutate() - E.limb_flags |= ORGAN_FLAG_DEFORMED + for(var/obj/item/organ/external/limb in victim.get_external_organs()) + if(!BP_IS_PROSTHETIC(limb) && prob(25) && !(limb.status & ORGAN_MUTATED)) + limb.mutate() + limb.limb_flags |= ORGAN_FLAG_DEFORMED /decl/material/liquid/retrovirals/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() @@ -404,8 +398,8 @@ break ..() -/decl/material/liquid/clotting_agent/affect_overdose(mob/living/M, total_dose) - var/obj/item/organ/internal/heart = GET_INTERNAL_ORGAN(M, BP_HEART) +/decl/material/liquid/clotting_agent/affect_overdose(mob/living/victim, total_dose) + var/obj/item/organ/internal/heart = GET_INTERNAL_ORGAN(victim, BP_HEART) if(heart && prob(25)) heart.take_general_damage(rand(1,3)) return ..() diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index d96d2b840d7..cc0774431ca 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -83,9 +83,9 @@ uid = "chem_nutriment_flour" allergen_flags = ALLERGEN_GLUTEN -/decl/material/liquid/nutriment/flour/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) +/decl/material/liquid/nutriment/flour/touch_turf(var/turf/touching_turf, var/amount, var/datum/reagents/holder) ..() - new /obj/effect/decal/cleanable/flour(T) + new /obj/effect/decal/cleanable/flour(touching_turf) /decl/material/liquid/nutriment/batter name = "batter" @@ -102,9 +102,9 @@ boiling_point = 373 allergen_flags = ALLERGEN_EGG | ALLERGEN_GLUTEN -/decl/material/liquid/nutriment/batter/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) +/decl/material/liquid/nutriment/batter/touch_turf(var/turf/touching_turf, var/amount, var/datum/reagents/holder) ..() - new /obj/effect/decal/cleanable/pie_smudge(T) + new /obj/effect/decal/cleanable/pie_smudge(touching_turf) /decl/material/liquid/nutriment/batter/cakebatter name = "cake batter" diff --git a/code/modules/reagents/chems/chems_painkillers.dm b/code/modules/reagents/chems/chems_painkillers.dm index 6647c2afed2..8ab62f4048f 100644 --- a/code/modules/reagents/chems/chems_painkillers.dm +++ b/code/modules/reagents/chems/chems_painkillers.dm @@ -112,17 +112,17 @@ M.add_chemical_effect(CE_ALCOHOL_TOXIC, 1) M.add_chemical_effect(CE_BREATHLOSS, 1 * boozed) //drinking and opiating suppresses breathing. -/decl/material/liquid/painkillers/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/painkillers/affect_overdose(mob/living/victim, total_dose) ..() - M.add_chemical_effect(CE_PAINKILLER, pain_power*0.5) //extra painkilling for extra trouble + victim.add_chemical_effect(CE_PAINKILLER, pain_power*0.5) //extra painkilling for extra trouble if(narcotic) - SET_STATUS_MAX(M, STAT_DRUGGY, 10) - M.set_hallucination(120, 30) - M.add_chemical_effect(CE_BREATHLOSS, breathloss_severity*2) //ODing on opiates can be deadly. - if(isboozed(M)) - M.add_chemical_effect(CE_BREATHLOSS, breathloss_severity*4) //Don't drink and OD on opiates folks + SET_STATUS_MAX(victim, STAT_DRUGGY, 10) + victim.set_hallucination(120, 30) + victim.add_chemical_effect(CE_BREATHLOSS, breathloss_severity*2) //ODing on opiates can be deadly. + if(isboozed(victim)) + victim.add_chemical_effect(CE_BREATHLOSS, breathloss_severity*4) //Don't drink and OD on opiates folks else - M.add_chemical_effect(CE_TOXIN, 1) + victim.add_chemical_effect(CE_TOXIN, 1) /decl/material/liquid/painkillers/proc/isboozed(var/mob/living/M) . = 0 diff --git a/code/modules/reagents/chems/chems_pigments.dm b/code/modules/reagents/chems/chems_pigments.dm index d110680a76e..55c6be4c47c 100644 --- a/code/modules/reagents/chems/chems_pigments.dm +++ b/code/modules/reagents/chems/chems_pigments.dm @@ -85,9 +85,9 @@ painting.reset_color() painting.set_alpha(keep_alpha) -/decl/material/liquid/paint_stripper/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) - if(istype(T) && !isspaceturf(T)) - remove_paint(T, holder) +/decl/material/liquid/paint_stripper/touch_turf(var/turf/touching_turf, var/amount, var/datum/reagents/holder) + if(istype(touching_turf) && !isspaceturf(touching_turf)) + remove_paint(touching_turf, holder) /decl/material/liquid/paint_stripper/touch_obj(var/obj/O, var/amount, var/datum/reagents/holder) if(istype(O)) @@ -107,20 +107,20 @@ uid = "chem_pigment_paint" exoplanet_rarity_gas = MAT_RARITY_NOWHERE -/decl/material/liquid/paint/proc/apply_paint(var/atom/painting, var/datum/reagents/holder) - if(istype(painting) && istype(holder)) +/decl/material/liquid/paint/proc/apply_paint(var/atom/painting, var/datum/reagents/holder, var/threshold = 1) + if(istype(painting) && istype(holder) && REAGENT_VOLUME(holder, type) >= threshold) var/keep_alpha = painting.alpha painting.set_color(holder.get_color()) painting.set_alpha(keep_alpha) -/decl/material/liquid/paint/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) - if(istype(T) && !isspaceturf(T)) - apply_paint(T, holder) +/decl/material/liquid/paint/touch_turf(var/turf/touching_turf, var/amount, var/datum/reagents/holder) + if(istype(touching_turf) && !isspaceturf(touching_turf)) + apply_paint(touching_turf, holder, FLUID_MINIMUM_TRANSFER) /decl/material/liquid/paint/touch_obj(var/obj/O, var/amount, var/datum/reagents/holder) if(istype(O)) - apply_paint(O, holder) + apply_paint(O, holder, O.get_object_size()) /decl/material/liquid/paint/touch_mob(var/mob/living/M, var/amount, var/datum/reagents/holder) if(istype(M)) - apply_paint(M, holder) \ No newline at end of file + apply_paint(M, holder, M.get_object_size()) diff --git a/code/modules/reagents/heat_sources/_heat_source.dm b/code/modules/reagents/heat_sources/_heat_source.dm index b9295313707..eb5b3cebf44 100644 --- a/code/modules/reagents/heat_sources/_heat_source.dm +++ b/code/modules/reagents/heat_sources/_heat_source.dm @@ -5,8 +5,8 @@ #define HEATER_MODE_COOL "cool" /obj/machinery/reagent_temperature - name = "chemical heater" - desc = "A small electric Bunsen, used to heat beakers and vials of chemicals." + name = "hotplate" + desc = "A small electric hotplate, used to heat cookware, beakers, or vials of chemicals." icon = 'icons/obj/machines/heat_sources.dmi' icon_state = "hotplate" atom_flags = ATOM_FLAG_CLIMBABLE @@ -70,6 +70,7 @@ /obj/machinery/reagent_temperature/ProcessAtomTemperature() if(use_power >= POWER_USE_ACTIVE) + var/last_temperature = temperature if(heater_mode == HEATER_MODE_HEAT && temperature < target_temperature) temperature = min(target_temperature, temperature + heating_power) @@ -79,10 +80,25 @@ if(container) queue_temperature_atoms(container) queue_icon_update() + + // Hackery to heat pots placed onto a hotplate without also grilling/baking stuff. + if(isturf(loc)) + var/datum/gas_mixture/environment = loc.return_air() + for(var/obj/item/chems/cooking_vessel/pot in loc.get_contained_external_atoms()) + pot.fire_act(environment, temperature, 500) + return TRUE // Don't kill this processing loop unless we're not powered. . = ..() /obj/machinery/reagent_temperature/attackby(var/obj/item/thing, var/mob/user) + + if(istype(thing, /obj/item/chems/cooking_vessel)) + if(!user.try_unequip(thing, get_turf(src))) + return TRUE + thing.reset_offsets(anim_time = 0) + user.visible_message(SPAN_NOTICE("\The [user] places \the [thing] onto \the [src].")) + return TRUE + if(IS_WRENCH(thing)) if(use_power == POWER_USE_ACTIVE) to_chat(user, SPAN_WARNING("Turn \the [src] off first!")) diff --git a/code/modules/reagents/reactions/_reaction.dm b/code/modules/reagents/reactions/_reaction.dm index e743ecf0ab2..fbc2eaa85d6 100644 --- a/code/modules/reagents/reactions/_reaction.dm +++ b/code/modules/reagents/reactions/_reaction.dm @@ -38,7 +38,7 @@ return 1 -/decl/chemical_reaction/proc/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/proc/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(thermal_product && location && ATOM_SHOULD_TEMPERATURE_ENQUEUE(location)) ADJUST_ATOM_TEMPERATURE(location, location.temperature + (location.get_thermal_mass_coefficient() * thermal_product)) @@ -61,8 +61,6 @@ if(reaction_volume > A) reaction_volume = A - var/alt_reaction_indicator = get_alternate_reaction_indicator(holder) - for(var/reactant in required_reagents) holder.remove_reagent(reactant, reaction_volume * required_reagents[reactant], safety = 1) @@ -71,7 +69,7 @@ if(result) holder.add_reagent(result, amt_produced, data, safety = 1) - on_reaction(holder, amt_produced, alt_reaction_indicator, data) + on_reaction(holder, amt_produced, data) //called after processing reactions, if they occurred /decl/chemical_reaction/proc/post_reaction(var/datum/reagents/holder) diff --git a/code/modules/reagents/reactions/reaction_compounds.dm b/code/modules/reagents/reactions/reaction_compounds.dm index 8e530dacaa5..8df2ada04be 100644 --- a/code/modules/reagents/reactions/reaction_compounds.dm +++ b/code/modules/reagents/reactions/reaction_compounds.dm @@ -155,10 +155,20 @@ required_reagents = list(/decl/material/liquid/capsaicin = 2) result = list(/decl/material/liquid/capsaicin/condensed = 1) -/decl/chemical_reaction/compound/condensed_capsaicin/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/compound/condensed_capsaicin/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) . = ..() holder?.add_reagent(/decl/material/liquid/water, created_volume) +/decl/chemical_reaction/compound/nanitefluid + name = "Nanite Fluid" + result = /decl/material/liquid/nanitefluid + required_reagents = list(/decl/material/liquid/plasticide = 1, /decl/material/solid/metal/aluminium = 1, /decl/material/liquid/lube = 1) + catalysts = list(/decl/material/solid/phoron = 5) + result_amount = 3 + minimum_temperature = (-25 CELSIUS) - 100 + maximum_temperature = -25 CELSIUS + mix_message = "The solution becomes a metallic slime." + // This is a bit silly, but we need a way to unify oil types until someone rewrites lanterns. /decl/chemical_reaction/compound/fuel_oil name = "Plant Fuel Oil" diff --git a/code/modules/reagents/reactions/reaction_drugs.dm b/code/modules/reagents/reactions/reaction_drugs.dm index 657850ae05c..a4f85cb3fa7 100644 --- a/code/modules/reagents/reactions/reaction_drugs.dm +++ b/code/modules/reagents/reactions/reaction_drugs.dm @@ -111,16 +111,6 @@ required_reagents = list(/decl/material/liquid/antirads = 1, /decl/material/solid/carbon = 1) result_amount = 2 -/decl/chemical_reaction/compound/nanitefluid - name = "Nanite Fluid" - result = /decl/material/liquid/nanitefluid - required_reagents = list(/decl/material/liquid/plasticide = 1, /decl/material/solid/metal/aluminium = 1, /decl/material/liquid/lube = 1) - catalysts = list(/decl/material/solid/phoron = 5) - result_amount = 3 - minimum_temperature = (-25 CELSIUS) - 100 - maximum_temperature = -25 CELSIUS - mix_message = "The solution becomes a metallic slime." - /decl/chemical_reaction/drug/antibiotics name = "Antibiotics" result = /decl/material/liquid/antibiotics diff --git a/code/modules/reagents/reactions/reaction_grenade_reaction.dm b/code/modules/reagents/reactions/reaction_grenade_reaction.dm index ec529bef717..7827c6ac054 100644 --- a/code/modules/reagents/reactions/reaction_grenade_reaction.dm +++ b/code/modules/reagents/reactions/reaction_grenade_reaction.dm @@ -11,7 +11,7 @@ required_reagents = list(/decl/material/liquid/water = 1, /decl/material/solid/potassium = 1) mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/explosion_potassium/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/explosion_potassium/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) @@ -32,7 +32,7 @@ result_amount = null mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/flash_powder/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/flash_powder/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -58,7 +58,7 @@ mix_message = "The solution bubbles vigorously!" maximum_temperature = T100C -/decl/chemical_reaction/grenade_reaction/emp_pulse/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/emp_pulse/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) @@ -79,7 +79,7 @@ reaction_sound = 'sound/items/Welder.ogg' mix_message = "The solution suddenly ignites!" -/decl/chemical_reaction/grenade_reaction/flash_fire/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/flash_fire/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(istype(location)) @@ -93,7 +93,7 @@ result_amount = 0.4 mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/chemsmoke/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/chemsmoke/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -112,7 +112,7 @@ result_amount = 2 mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/foam/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/foam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -129,7 +129,7 @@ result_amount = 5 mix_message = "The solution foams up violently!" -/decl/chemical_reaction/grenade_reaction/metalfoam/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/metalfoam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/atom/location = holder.get_reaction_loc(chemical_reaction_flags) if(location) @@ -151,7 +151,7 @@ result_amount = 5 mix_message = "The solution bubbles vigorously!" -/decl/chemical_reaction/grenade_reaction/ironfoam/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/grenade_reaction/ironfoam/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) diff --git a/code/modules/reagents/reactions/reaction_herbal.dm b/code/modules/reagents/reactions/reaction_herbal.dm index b8a02ca07fe..62950dd1d64 100644 --- a/code/modules/reagents/reactions/reaction_herbal.dm +++ b/code/modules/reagents/reactions/reaction_herbal.dm @@ -3,7 +3,7 @@ result_amount = 2 minimum_temperature = 100 CELSIUS -/decl/chemical_reaction/drug/herbal/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/drug/herbal/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) . = ..() // Add plant matter to represent the herbs that the medicine has been leached out of. holder?.add_reagent(/decl/material/solid/organic/plantmatter, created_volume) diff --git a/code/modules/reagents/reactions/reaction_other.dm b/code/modules/reagents/reactions/reaction_other.dm index 19c5613ea7e..dca52f041f6 100644 --- a/code/modules/reagents/reactions/reaction_other.dm +++ b/code/modules/reagents/reactions/reaction_other.dm @@ -10,7 +10,7 @@ return ..() return 0 -/decl/chemical_reaction/soap_key/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/soap_key/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/obj/item/soap/S = holder.get_reaction_loc(chemical_reaction_flags) if(istype(S) && S.key_data) new /obj/item/key/temporary(get_turf(S), /decl/material/liquid/cleaner, S.key_data, strength) diff --git a/code/modules/reagents/reactions/reaction_recipe_food.dm b/code/modules/reagents/reactions/reaction_recipe_food.dm index bc052960e91..0ece0daefd2 100644 --- a/code/modules/reagents/reactions/reaction_recipe_food.dm +++ b/code/modules/reagents/reactions/reaction_recipe_food.dm @@ -4,7 +4,7 @@ abstract_type = /decl/chemical_reaction/recipe/food var/obj_result -/decl/chemical_reaction/recipe/food/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/recipe/food/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(obj_result && isturf(location)) diff --git a/code/modules/reagents/reactions/reaction_synthesis.dm b/code/modules/reagents/reactions/reaction_synthesis.dm index 57192c726ae..79695861328 100644 --- a/code/modules/reagents/reactions/reaction_synthesis.dm +++ b/code/modules/reagents/reactions/reaction_synthesis.dm @@ -19,7 +19,7 @@ ) . = ..() -/decl/chemical_reaction/synthesis/fiberglass/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/fiberglass/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -44,7 +44,7 @@ if(rtype != /decl/material/liquid/crystal_agent && REAGENT_VOLUME(holder, rtype) >= REAGENT_UNITS_PER_MATERIAL_SHEET) return TRUE -/decl/chemical_reaction/synthesis/crystalization/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/crystalization/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) var/list/removing_reagents = list() @@ -78,7 +78,7 @@ continue return TRUE -/decl/chemical_reaction/synthesis/aerogel/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/aerogel/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) var/list/removing_reagents = list() @@ -97,7 +97,7 @@ required_reagents = list(/decl/material/liquid/acid = 1, /decl/material/liquid/plasticide = 2) mix_message = "The solution solidifies into a grey-white mass." -/decl/chemical_reaction/synthesis/plastication/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/plastication/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(location) @@ -112,7 +112,7 @@ result_amount = 3 mix_message = "The solution hardens and begins to crystallize." -/decl/chemical_reaction/synthesis/resin_pack/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/resin_pack/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/T = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(!istype(T)) @@ -133,7 +133,7 @@ mix_message = "The solution thickens and solidifies." minimum_temperature = 100 CELSIUS -/decl/chemical_reaction/synthesis/soap/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/soap/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/T = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(!istype(T)) @@ -164,7 +164,7 @@ minimum_temperature = 100 CELSIUS var/chipboard_type = /decl/material/solid/organic/wood/chipboard -/decl/chemical_reaction/synthesis/chipboard/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) +/decl/chemical_reaction/synthesis/chipboard/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) ..() var/turf/T = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) if(!istype(T)) diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 99ec47811aa..de18007e761 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -27,6 +27,8 @@ /obj/item/chems/on_update_icon() . = ..() + if(detail_state) + add_overlay(overlay_image(icon, "[initial(icon_state)][detail_state]", detail_color || COLOR_WHITE, RESET_COLOR)) var/image/contents_overlay = get_reagents_overlay(use_single_icon ? icon_state : null) if(contents_overlay) add_overlay(contents_overlay) @@ -198,12 +200,21 @@ // // Interactions // +/obj/item/chems/get_quick_interaction_handler(mob/user) + var/static/interaction = GET_DECL(/decl/interaction_handler/set_transfer/chems) + return interaction + /obj/item/chems/get_alt_interactions(var/mob/user) . = ..() - LAZYADD(., /decl/interaction_handler/set_transfer/chems) + var/static/list/chem_interactions = list( + /decl/interaction_handler/set_transfer/chems, + /decl/interaction_handler/empty/chems + ) + LAZYADD(., chem_interactions) /decl/interaction_handler/set_transfer/chems expected_target_type = /obj/item/chems + examine_desc = "set the transfer volume" /decl/interaction_handler/set_transfer/chems/is_possible(var/atom/target, var/mob/user) . = ..() @@ -220,6 +231,7 @@ name = "Empty On Floor" expected_target_type = /obj/item/chems interaction_flags = INTERACTION_NEEDS_INVENTORY | INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEVER_AUTOMATIC + examine_desc = "empty $TARGET_THEM$ onto the floor" /decl/interaction_handler/empty/chems/invoked(atom/target, mob/user, obj/item/prop) var/turf/T = get_turf(user) diff --git a/code/modules/reagents/reagent_containers/_glass.dm b/code/modules/reagents/reagent_containers/_glass.dm index 90981c54511..f381c8a44bb 100644 --- a/code/modules/reagents/reagent_containers/_glass.dm +++ b/code/modules/reagents/reagent_containers/_glass.dm @@ -58,9 +58,15 @@ /obj/item/chems/glass/proc/can_lid() return TRUE +/obj/item/chems/glass/proc/should_drink_from(mob/drinker) + . = reagents?.total_volume > 0 + if(.) + var/decl/material/drinking = reagents.get_primary_reagent_decl() + return drinking ? !drinking.is_unsafe_to_drink(drinker) : FALSE + /obj/item/chems/glass/attack_self(mob/user) - . = ..() - if(!. && can_lid()) + + if(can_lid() && user.check_intent(I_FLAG_HELP)) if(ATOM_IS_OPEN_CONTAINER(src)) to_chat(user, SPAN_NOTICE("You put the lid on \the [src].")) atom_flags ^= ATOM_FLAG_OPEN_CONTAINER @@ -68,9 +74,15 @@ to_chat(user, SPAN_NOTICE("You take the lid off \the [src].")) atom_flags |= ATOM_FLAG_OPEN_CONTAINER update_icon() + return TRUE + + if(should_drink_from(user) && is_edible(user) && handle_eaten_by_mob(user, user) != EATEN_INVALID) + return TRUE + + return ..() /obj/item/chems/glass/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - if(get_attack_force(user) && !(item_flags & ITEM_FLAG_NO_BLUDGEON) && user.check_intent(I_FLAG_HARM)) + if(expend_attack_force(user) && !(item_flags & ITEM_FLAG_NO_BLUDGEON) && user.check_intent(I_FLAG_HARM)) return ..() return FALSE @@ -102,17 +114,6 @@ . = ..() // Drinking out of bowls. -/obj/item/chems/glass/attack_self(mob/user) - if(is_edible(user) && handle_eaten_by_mob(user, user) != EATEN_INVALID) - return TRUE - return ..() - -/obj/item/chems/glass/get_food_default_transfer_amount(mob/eater) - return eater?.get_eaten_transfer_amount(amount_per_transfer_from_this) - -/obj/item/chems/glass/get_food_consumption_method(mob/eater) - return EATING_METHOD_DRINK - /obj/item/chems/glass/get_edible_material_amount(mob/eater) return reagents?.total_volume @@ -123,36 +124,53 @@ // Should we consider moving this down to /chems for any open container? Medicine from a bottle using a spoon, etc. /obj/item/chems/glass/attackby(obj/item/used_item, mob/living/user) - if(ATOM_IS_OPEN_CONTAINER(src)) - if(istype(used_item, /obj/item/food)) - if(!reagents?.total_volume) - to_chat(user, SPAN_WARNING("\The [src] is empty.")) - return TRUE - var/transferring = min(get_food_default_transfer_amount(user), REAGENTS_FREE_SPACE(used_item.reagents)) - if(!transferring) - to_chat(user, SPAN_WARNING("You cannot dip \the [used_item] in \the [src].")) - return TRUE - reagents.trans_to_holder(used_item.reagents, transferring) - user.visible_message(SPAN_NOTICE("\The [user] dunks \the [used_item] in \the [src].")) + if(!ATOM_IS_OPEN_CONTAINER(src)) + return ..() + + var/obj/item/utensil/utensil = used_item + if(istype(utensil) && (utensil.utensil_flags & UTENSIL_FLAG_SCOOP)) + if(utensil.loaded_food) + to_chat(user, SPAN_WARNING("You already have something on \the [utensil].")) return TRUE - var/obj/item/utensil/utensil = used_item - if(istype(utensil) && (utensil.utensil_flags & UTENSIL_FLAG_SCOOP)) - if(utensil.loaded_food) - to_chat(user, SPAN_WARNING("You already have something on \the [utensil].")) - return TRUE - if(!reagents?.total_volume) - to_chat(user, SPAN_WARNING("\The [src] is empty.")) - return TRUE - seperate_food_chunk(utensil, user) - if(utensil.loaded_food?.reagents?.total_volume) - to_chat(user, SPAN_NOTICE("You scoop up some of \the [utensil.loaded_food.reagents.get_primary_reagent_name()] with \the [utensil].")) + if(!reagents?.total_volume) + to_chat(user, SPAN_WARNING("\The [src] is empty.")) return TRUE + seperate_food_chunk(utensil, user) + if(utensil.loaded_food?.reagents?.total_volume) + to_chat(user, SPAN_NOTICE("You scoop up some of \the [utensil.loaded_food.reagents.get_primary_reagent_name()] with \the [utensil].")) + return TRUE + return ..() -/obj/structure/glass/get_alt_interactions(mob/user) +/obj/item/chems/glass/get_alt_interactions(mob/user) . = ..() if(reagents?.total_volume >= FLUID_PUDDLE) LAZYADD(., /decl/interaction_handler/dip_item) LAZYADD(., /decl/interaction_handler/fill_from) if(user?.get_active_held_item()) LAZYADD(., /decl/interaction_handler/empty_into) + if(can_lid()) + LAZYADD(., /decl/interaction_handler/toggle_lid) + +/decl/interaction_handler/toggle_lid + name = "Toggle Lid" + expected_target_type = /obj/item/chems/glass + +/decl/interaction_handler/toggle_lid/is_possible(atom/target, mob/user, obj/item/prop) + . = ..() + if(. && !istype(prop)) + var/obj/item/chems/glass/glass = target + return glass.can_lid() + +/decl/interaction_handler/toggle_lid/invoked(atom/target, mob/user, obj/item/prop) + var/obj/item/chems/glass/glass = target + if(istype(glass) && glass.can_lid()) + if(ATOM_IS_OPEN_CONTAINER(glass)) + to_chat(user, SPAN_NOTICE("You put the lid on \the [glass].")) + glass.atom_flags ^= ATOM_FLAG_OPEN_CONTAINER + else + to_chat(user, SPAN_NOTICE("You take the lid off \the [glass].")) + glass.atom_flags |= ATOM_FLAG_OPEN_CONTAINER + glass.update_icon() + return TRUE + diff --git a/code/modules/reagents/reagent_containers/beaker.dm b/code/modules/reagents/reagent_containers/beaker.dm index 9e055646570..724a0ca448b 100644 --- a/code/modules/reagents/reagent_containers/beaker.dm +++ b/code/modules/reagents/reagent_containers/beaker.dm @@ -178,5 +178,5 @@ matter = list(/decl/material/solid/organic/plastic = MATTER_AMOUNT_REINFORCEMENT) volume = 120 -/obj/item/chems/glass/beaker/sulphuric/populate_reagents() +/obj/item/chems/glass/beaker/sulfuric/populate_reagents() add_to_reagents(/decl/material/liquid/acid, reagents.maximum_volume) diff --git a/code/modules/reagents/reagent_containers/drinks.dm b/code/modules/reagents/reagent_containers/drinks.dm index 46c56514eee..d80b593cf60 100644 --- a/code/modules/reagents/reagent_containers/drinks.dm +++ b/code/modules/reagents/reagent_containers/drinks.dm @@ -57,7 +57,7 @@ return return ..() -/obj/item/chems/drinks/standard_dispenser_refill(var/mob/user, var/obj/structure/reagent_dispensers/target, skip_container_check = FALSE) +/obj/item/chems/drinks/standard_dispenser_refill(mob/user, obj/structure/reagent_dispensers/target, skip_container_check = FALSE) return do_open_check(user) && ..() /obj/item/chems/drinks/standard_pour_into(var/mob/user, var/atom/target) @@ -148,7 +148,7 @@ add_to_reagents(/decl/material/liquid/drink/milk/chocolate, reagents.maximum_volume) /obj/item/chems/drinks/coffee - name = "\improper Robust Coffee" + name = "cup of coffee" desc = "Careful, the beverage you're about to enjoy is extremely hot." icon_state = "coffee" center_of_mass = @'{"x":15,"y":10}' @@ -269,16 +269,14 @@ //tea and tea accessories /obj/item/chems/drinks/tea - name = "cup of tea master item" + name = "cup of tea" desc = "A tall plastic cup full of the concept and ideal of tea." icon_state = "coffee" item_state = "coffee" center_of_mass = @'{"x":16,"y":14}' filling_states = @"[100]" - base_name = "cup" base_icon = "cup" volume = 30 - presentation_flags = PRESENTATION_FLAG_NAME /obj/item/chems/drinks/tea/black name = "cup of black tea" diff --git a/code/modules/reagents/reagent_containers/drinks/bottle.dm b/code/modules/reagents/reagent_containers/drinks/bottle.dm index 5d0776020c4..56e5f6b4720 100644 --- a/code/modules/reagents/reagent_containers/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/drinks/bottle.dm @@ -168,7 +168,7 @@ user.visible_message(SPAN_DANGER("\The [user] smashes \the [src] into [H]'s [affecting.name]!")) // You are going to knock someone out for longer if they are not wearing a helmet. var/blocked = target.get_blocked_ratio(hit_zone, BRUTE, damage = 10) * 100 - var/weaken_duration = smash_duration + min(0, get_attack_force(user) - blocked + 10) + var/weaken_duration = smash_duration + min(0, expend_attack_force(user) - blocked + 10) if(weaken_duration) target.apply_effect(min(weaken_duration, 5), WEAKEN, blocked) // Never weaken more than a flash! else @@ -228,8 +228,7 @@ throw_range = 5 item_state = "beer" attack_verb = list("stabbed", "slashed", "attacked") - sharp = 1 - edge = 0 + sharp = TRUE obj_flags = OBJ_FLAG_HOLLOW material = /decl/material/solid/glass _base_attack_force = 9 diff --git a/code/modules/reagents/reagent_containers/food.dm b/code/modules/reagents/reagent_containers/food.dm index c28d70364f6..55309ff3d2c 100644 --- a/code/modules/reagents/reagent_containers/food.dm +++ b/code/modules/reagents/reagent_containers/food.dm @@ -142,7 +142,7 @@ /obj/item/food/Destroy() QDEL_NULL(plate) trash = null - if(contents) + if(length(contents)) for(var/atom/movable/something in contents) something.dropInto(loc) . = ..() diff --git a/code/modules/reagents/reagent_containers/food/fish.dm b/code/modules/reagents/reagent_containers/food/fish.dm index d4a3a57106b..fd8ec91188e 100644 --- a/code/modules/reagents/reagent_containers/food/fish.dm +++ b/code/modules/reagents/reagent_containers/food/fish.dm @@ -70,7 +70,7 @@ qdel(src) /obj/item/mollusc/attackby(var/obj/item/thing, var/mob/user) - if(thing.sharp || thing.edge) + if(thing.is_sharp() || thing.has_edge()) user.visible_message(SPAN_NOTICE("\The [user] cracks open \the [src] with \the [thing].")) crack_shell(user) return TRUE diff --git a/code/modules/reagents/reagent_containers/food/sliceable/pizza/pizza_box.dm b/code/modules/reagents/reagent_containers/food/sliceable/pizza/pizza_box.dm index a048bb7eac3..6c6cf548fe6 100644 --- a/code/modules/reagents/reagent_containers/food/sliceable/pizza/pizza_box.dm +++ b/code/modules/reagents/reagent_containers/food/sliceable/pizza/pizza_box.dm @@ -304,6 +304,7 @@ /decl/interaction_handler/open_pizza_box expected_target_type = /obj/item/pizzabox + examine_desc = "open or close $TARGET_THEM$" /decl/interaction_handler/open_pizza_box/is_possible(atom/target, mob/user, obj/item/prop) . = ..() diff --git a/code/modules/reagents/reagent_containers/inhaler.dm b/code/modules/reagents/reagent_containers/inhaler.dm index 275a3bac13d..19eaaa9cd1d 100644 --- a/code/modules/reagents/reagent_containers/inhaler.dm +++ b/code/modules/reagents/reagent_containers/inhaler.dm @@ -62,7 +62,7 @@ if(user == target) user.visible_message( SPAN_NOTICE("\The [user] inhales from \the [src]."), - SPAN_NOTICE("You stick the \the [src] in your mouth and press the injection button.") + SPAN_NOTICE("You stick \the [src] in your mouth and press the injection button.") ) else user.visible_message( diff --git a/code/modules/reagents/reagent_containers/mortar.dm b/code/modules/reagents/reagent_containers/mortar.dm index fe85a5c09fe..b19a7340e23 100644 --- a/code/modules/reagents/reagent_containers/mortar.dm +++ b/code/modules/reagents/reagent_containers/mortar.dm @@ -28,7 +28,7 @@ var/decl/material/attacking_material = using_item.get_material() var/decl/material/crushing_material = crushing_item?.get_material() var/skill_factor = CLAMP01(1 + 0.3*(user.get_skill_value(SKILL_CHEMISTRY) - SKILL_EXPERT)/(SKILL_EXPERT - SKILL_MIN)) - if(using_item.get_attack_force(user) <= 0 || !attacking_material || !crushing_material) + if(using_item.expend_attack_force(user) <= 0 || !attacking_material || !crushing_material) return TRUE if(attacking_material.hardness <= crushing_material.hardness) to_chat(user, SPAN_NOTICE("\The [using_item] is not hard enough to crush \the [crushing_item].")) diff --git a/code/modules/reagents/reagent_containers/packets.dm b/code/modules/reagents/reagent_containers/packets.dm index c46af37998f..391c3440471 100644 --- a/code/modules/reagents/reagent_containers/packets.dm +++ b/code/modules/reagents/reagent_containers/packets.dm @@ -7,7 +7,14 @@ amount_per_transfer_from_this = 1 volume = 10 -/obj/item/chems/packet/afterattack(var/obj/target, var/mob/user, var/proximity) +/obj/item/chems/packet/attack_self(mob/user) + if(!ATOM_IS_OPEN_CONTAINER(src)) + atom_flags |= ATOM_FLAG_OPEN_CONTAINER + to_chat(user, SPAN_NOTICE("You tear \the [src] open.")) + return TRUE + return ..() + +/obj/item/chems/packet/afterattack(obj/target, mob/user, proximity) if(!proximity) return ..() if(standard_dispenser_refill(user, target)) @@ -54,6 +61,14 @@ icon = 'icons/obj/food/condiments/packets/packet_medium.dmi' /obj/item/chems/packet/honey/populate_reagents() + add_to_reagents(/decl/material/liquid/nutriment/honey, reagents.maximum_volume) + +/obj/item/chems/packet/honey_fake + name = "'honey' packet" + desc = "Contains 10u of allergen-free non-GMO 'honey'." + icon = 'icons/obj/food/condiments/packets/packet_medium.dmi' + +/obj/item/chems/packet/honey_fake/populate_reagents() add_to_reagents(/decl/material/liquid/nutriment/sugar, reagents.maximum_volume) /obj/item/chems/packet/capsaicin diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 9cd81a4c437..c3b4cbd9ff8 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -36,9 +36,6 @@ if(A?.storage || istype(A, /obj/structure/table) || istype(A, /obj/structure/closet) || istype(A, /obj/item/chems) || istype(A, /obj/structure/hygiene/sink) || istype(A, /obj/structure/janitorialcart)) return - if(istype(A, /spell)) - return - if(proximity) if(standard_dispenser_refill(user, A)) return @@ -50,7 +47,7 @@ Spray_at(A, user, proximity) if(reagents.has_reagent(/decl/material/liquid/acid)) - log_and_message_admins("fired sulphuric acid from \a [src].", user) + log_and_message_admins("fired sulfuric acid from \a [src].", user) if(reagents.has_reagent(/decl/material/liquid/acid/polyacid)) log_and_message_admins("fired polyacid from \a [src].", user) if(reagents.has_reagent(/decl/material/liquid/lube)) @@ -119,6 +116,7 @@ name = "Next Nozzle Setting" expected_target_type = /obj/item/chems/spray interaction_flags = INTERACTION_NEEDS_INVENTORY | INTERACTION_NEEDS_PHYSICAL_INTERACTION + examine_desc = "select the next nozzle spray amount" /decl/interaction_handler/next_spray_amount/is_possible(obj/item/chems/spray/target, mob/user, obj/item/prop) . = ..() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index ba547ec695c..0018d127862 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -17,7 +17,7 @@ volume = 15 w_class = ITEM_SIZE_TINY slot_flags = SLOT_EARS - sharp = 1 + sharp = TRUE item_flags = ITEM_FLAG_NO_BLUDGEON var/mode = SYRINGE_DRAW diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index d8d5d4e5e06..778ed37be45 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -10,6 +10,7 @@ matter = list(/decl/material/solid/metal/steel = MATTER_AMOUNT_SECONDARY) max_health = 100 tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT + var/wrenchable = TRUE var/unwrenched = FALSE var/tmp/volume = 1000 @@ -102,6 +103,7 @@ log_and_message_admins("opened a tank at [get_area_name(loc)].") leak() return TRUE + . = ..() /obj/structure/reagent_dispensers/verb/set_amount_dispensed() @@ -317,11 +319,12 @@ add_to_reagents(/decl/material/liquid/alcohol/beer, reagents.maximum_volume) /obj/structure/reagent_dispensers/acid - name = "sulphuric acid dispenser" + name = "sulfuric acid dispenser" desc = "A dispenser of acid for industrial processes." icon_state = "acidtank" amount_dispensed = 10 anchored = TRUE + density = FALSE /obj/structure/reagent_dispensers/acid/populate_reagents() add_to_reagents(/decl/material/liquid/acid, reagents.maximum_volume) @@ -351,6 +354,7 @@ /decl/interaction_handler/toggle_open/reagent_dispenser name = "Toggle refilling cap" expected_target_type = /obj/structure/reagent_dispensers + examine_desc = "open or close the refilling cap" /decl/interaction_handler/toggle_open/reagent_dispenser/invoked(atom/target, mob/user, obj/item/prop) if(target.atom_flags & ATOM_FLAG_OPEN_CONTAINER) diff --git a/code/modules/recycling/disposalholder.dm b/code/modules/recycling/disposalholder.dm index 4f3546d06a6..7a95d7d2ce2 100644 --- a/code/modules/recycling/disposalholder.dm +++ b/code/modules/recycling/disposalholder.dm @@ -82,11 +82,11 @@ if(!curr) last.expel(src, loc, dir) - // find the turf which should contain the next pipe +/// find the turf which should contain the next pipe /obj/structure/disposalholder/proc/nextloc() return get_step(loc,dir) -// find a matching pipe on a turf +/// find a matching pipe on a turf /obj/structure/disposalholder/proc/findpipe(var/turf/containing_turf) if(!containing_turf) return null @@ -98,8 +98,8 @@ // if no matching pipe, return null return null -// merge two holder objects -// used when a a holder meets a stuck holder +/// merge two holder objects +/// used when a holder meets a stuck holder /obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) for(var/atom/movable/other_movable in other) other_movable.forceMove(src) // move everything in other holder to this one @@ -119,7 +119,7 @@ else partialTag = new_tag -// called when player tries to move while in a pipe +/// called when player tries to move while in a pipe /obj/structure/disposalholder/relaymove(mob/user) if(!isliving(user)) return diff --git a/code/modules/recycling/disposalpipe.dm b/code/modules/recycling/disposalpipe.dm index e035da8b825..88e11fbbdc6 100644 --- a/code/modules/recycling/disposalpipe.dm +++ b/code/modules/recycling/disposalpipe.dm @@ -184,8 +184,7 @@ if(H) expel(H, T, 0) - spawn(2) // delete pipe after 2 ticks to ensure expel proc finished - qdel(src) + QDEL_IN(src, 2) // delete pipe after 2 ticks to ensure expel proc finished // pipe affected by explosion @@ -235,11 +234,6 @@ /obj/structure/disposalpipe/hides_under_flooring() return 1 -// *** TEST verb -//client/verb/dispstop() -// for(var/obj/structure/disposalholder/H in world) -// H.active = 0 - // a straight or bent segment /obj/structure/disposalpipe/segment icon_state = "pipe-s" // Sadly this var stores state. "pipe-c" is corner. Should be changed, but requires huge map diff. diff --git a/code/modules/recycling/wrapped_package.dm b/code/modules/recycling/wrapped_package.dm index 57261df802b..da2a33bfd70 100644 --- a/code/modules/recycling/wrapped_package.dm +++ b/code/modules/recycling/wrapped_package.dm @@ -195,7 +195,7 @@ update_icon() return TRUE - else if(W.sharp && user.check_intent(I_FLAG_HELP)) + else if(W.is_sharp() && user.check_intent(I_FLAG_HELP)) //You can alternative cut the wrapper off with a sharp item unwrap(user) return TRUE diff --git a/code/modules/sealant_gun/sealant_injector.dm b/code/modules/sealant_gun/sealant_injector.dm index a3e89a44343..99f8553586d 100644 --- a/code/modules/sealant_gun/sealant_injector.dm +++ b/code/modules/sealant_gun/sealant_injector.dm @@ -103,6 +103,7 @@ /decl/interaction_handler/sealant_try_inject name = "Inject Sealant" expected_target_type = /obj/structure/sealant_injector + examine_desc = "inject sealant from a held item" /decl/interaction_handler/sealant_try_inject/invoked(atom/target, mob/user, obj/item/prop) var/obj/structure/sealant_injector/SI = target diff --git a/code/modules/shield_generators/shield.dm b/code/modules/shield_generators/shield.dm index 0b97dd24cbf..b39816fdce5 100644 --- a/code/modules/shield_generators/shield.dm +++ b/code/modules/shield_generators/shield.dm @@ -37,7 +37,7 @@ if(update_neighbors) for(var/obj/effect/shield/shield in T) shield.update_icon(FALSE) - add_overlay(image(icon = icon, icon_state = "[icon_state]_edge", dir = direction)) + add_overlay(image(icon = icon, icon_state = "[icon_state]edge", dir = direction)) // Prevents shuttles, singularities and pretty much everything else from moving the field segments away. // The only thing that is allowed to move us is the Destroy() proc. @@ -217,7 +217,7 @@ if(!gen.check_flag(MODEFLAG_HYPERKINETIC)) user.visible_message("\The [user] tries to attack \the [src] with \the [weapon], but it passes through!") return TRUE - var/force = weapon.get_attack_force(user) + var/force = weapon.expend_attack_force(user) user.visible_message("\The [user] [pick(weapon.attack_verb)] \the [src] with \the [weapon]!") switch(weapon.atom_damage_type) if(BURN) diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index f76892be8a4..0cb3ee417b9 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -55,7 +55,7 @@ //Calculate damage switch(W.atom_damage_type) if(BRUTE, BURN) - current_health -= W.get_attack_force(user) + current_health -= W.expend_attack_force(user) else return FALSE diff --git a/code/modules/shieldgen/shieldwallgen.dm b/code/modules/shieldgen/shieldwallgen.dm index 12682043752..0ca75384784 100644 --- a/code/modules/shieldgen/shieldwallgen.dm +++ b/code/modules/shieldgen/shieldwallgen.dm @@ -289,7 +289,7 @@ /obj/machinery/shieldwall/attackby(var/obj/item/I, var/mob/user) var/obj/machinery/shieldwallgen/G = prob(50) ? gen_primary : gen_secondary - G.storedpower -= I.get_attack_force(user)*2500 + G.storedpower -= I.expend_attack_force(user)*2500 user.visible_message("\The [user] hits \the [src] with \the [I]!") user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.do_attack_animation(src) diff --git a/code/modules/shuttles/escape_pods.dm b/code/modules/shuttles/escape_pods.dm index c12dc6e38d9..bd13d30c417 100644 --- a/code/modules/shuttles/escape_pods.dm +++ b/code/modules/shuttles/escape_pods.dm @@ -3,7 +3,7 @@ var/global/list/escape_pods_by_name = list() /datum/shuttle/autodock/ferry/escape_pod var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/arming_controller - category = /datum/shuttle/autodock/ferry/escape_pod + abstract_type = /datum/shuttle/autodock/ferry/escape_pod move_time = 100 /datum/shuttle/autodock/ferry/escape_pod/New(map_hash) diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm index 82cde0c759b..661bab8cf89 100644 --- a/code/modules/shuttles/landmarks.dm +++ b/code/modules/shuttles/landmarks.dm @@ -135,7 +135,7 @@ var/global/list/shuttle_landmarks = list() /obj/effect/shuttle_landmark/automatic/sector_set(var/obj/effect/overmap/visitable/O) ..() - SetName("[initial(name)] ([x],[y])") + SetName("[initial(name)] ([x],[y],[z])") //Subtype that calls explosion on init to clear space for shuttles /obj/effect/shuttle_landmark/automatic/clearing diff --git a/code/modules/shuttles/shuttle.dm b/code/modules/shuttles/shuttle.dm index 794f0736f12..85ee565817b 100644 --- a/code/modules/shuttles/shuttle.dm +++ b/code/modules/shuttles/shuttle.dm @@ -12,7 +12,7 @@ var/arrive_time = 0 //the time at which the shuttle arrives when long jumping var/flags = 0 var/process_state = IDLE_STATE //Used with SHUTTLE_FLAGS_PROCESS, as well as to store current state. - var/category = /datum/shuttle + abstract_type = /datum/shuttle var/multiz = 0 //how many multiz levels, starts at 0 var/ceiling_type = /turf/unsimulated/floor/shuttle_ceiling diff --git a/code/modules/shuttles/shuttle_autodock.dm b/code/modules/shuttles/shuttle_autodock.dm index 74c157df8fe..f2003eb0129 100644 --- a/code/modules/shuttles/shuttle_autodock.dm +++ b/code/modules/shuttles/shuttle_autodock.dm @@ -16,7 +16,7 @@ var/obj/effect/shuttle_landmark/landmark_transition //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. var/move_time = 240 //the time spent in the transition area - category = /datum/shuttle/autodock + abstract_type = /datum/shuttle/autodock flags = SHUTTLE_FLAGS_PROCESS | SHUTTLE_FLAGS_ZERO_G /datum/shuttle/autodock/New(var/map_hash, var/obj/effect/shuttle_landmark/start_waypoint) diff --git a/code/modules/shuttles/shuttle_emergency.dm b/code/modules/shuttles/shuttle_emergency.dm index 2bdc11d2284..f6efaa4a864 100644 --- a/code/modules/shuttles/shuttle_emergency.dm +++ b/code/modules/shuttles/shuttle_emergency.dm @@ -1,5 +1,5 @@ /datum/shuttle/autodock/ferry/emergency - category = /datum/shuttle/autodock/ferry/emergency + abstract_type = /datum/shuttle/autodock/ferry/emergency move_time = 10 MINUTES flags = SHUTTLE_FLAGS_PROCESS | SHUTTLE_FLAGS_ZERO_G | SHUTTLE_FLAGS_NO_CODE var/datum/evacuation_controller/shuttle/emergency_controller diff --git a/code/modules/shuttles/shuttle_ferry.dm b/code/modules/shuttles/shuttle_ferry.dm index 4f23361de2b..ebe0a7cf54f 100644 --- a/code/modules/shuttles/shuttle_ferry.dm +++ b/code/modules/shuttles/shuttle_ferry.dm @@ -7,7 +7,7 @@ var/obj/effect/shuttle_landmark/waypoint_station //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. var/obj/effect/shuttle_landmark/waypoint_offsite //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. - category = /datum/shuttle/autodock/ferry + abstract_type = /datum/shuttle/autodock/ferry /datum/shuttle/autodock/ferry/New(map_hash) if(map_hash) diff --git a/code/modules/shuttles/shuttle_specops.dm b/code/modules/shuttles/shuttle_specops.dm index 41778075e99..90a54b1eab0 100644 --- a/code/modules/shuttles/shuttle_specops.dm +++ b/code/modules/shuttles/shuttle_specops.dm @@ -14,10 +14,7 @@ var/reset_time = 0 //the world.time at which the shuttle will be ready to move again. var/launch_prep = 0 var/cancel_countdown = 0 - category = /datum/shuttle/autodock/ferry/specops - -/datum/shuttle/autodock/ferry/specops/New() - ..() + abstract_type = /datum/shuttle/autodock/ferry/specops /datum/shuttle/autodock/ferry/specops/launch(var/user) if (!can_launch()) @@ -98,7 +95,7 @@ return ..() /datum/shuttle/autodock/ferry/specops/proc/sleep_until_launch() - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a list with potential time values. var/launch_time = world.time + specops_countdown_time var/time_until_launch diff --git a/code/modules/shuttles/shuttle_supply.dm b/code/modules/shuttles/shuttle_supply.dm index 175924860dc..f38e5fef5b3 100644 --- a/code/modules/shuttles/shuttle_supply.dm +++ b/code/modules/shuttles/shuttle_supply.dm @@ -3,7 +3,7 @@ var/late_chance = 80 var/max_late_time = (30 SECONDS) flags = SHUTTLE_FLAGS_PROCESS|SHUTTLE_FLAGS_SUPPLY|SHUTTLE_FLAGS_NO_CODE - category = /datum/shuttle/autodock/ferry/supply + abstract_type = /datum/shuttle/autodock/ferry/supply ceiling_type = /turf/floor/shuttle_ceiling /datum/shuttle/autodock/ferry/supply/short_jump(var/area/destination) diff --git a/code/modules/shuttles/shuttles_multi.dm b/code/modules/shuttles/shuttles_multi.dm index 35e00c68be6..71e144e02e2 100644 --- a/code/modules/shuttles/shuttles_multi.dm +++ b/code/modules/shuttles/shuttles_multi.dm @@ -2,7 +2,7 @@ var/list/destination_tags var/list/destinations_cache = list() var/last_cache_rebuild_time = 0 - category = /datum/shuttle/autodock/multi + abstract_type = /datum/shuttle/autodock/multi /datum/shuttle/autodock/multi/New(map_hash) ..() @@ -42,7 +42,7 @@ var/arrival_message var/departure_message - category = /datum/shuttle/autodock/multi/antag + abstract_type = /datum/shuttle/autodock/multi/antag /datum/shuttle/autodock/multi/antag/New(map_hash) ..() diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index 06db017b9df..d7477882df5 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -57,9 +57,6 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/flesh_color = "#ffc896" // Pink. var/blood_oxy = 1 - // Preview in prefs positioning. If null, uses defaults set on a static list in preferences.dm. - var/list/character_preview_screen_locs - var/organs_icon //species specific internal organs icons var/strength = STR_MEDIUM @@ -82,10 +79,6 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 // Combat vars. var/total_health = DEFAULT_SPECIES_HEALTH // Point at which the mob will enter crit. - var/list/unarmed_attacks = list( // Possible unarmed attacks that the mob will use in combat, - /decl/natural_attack, - /decl/natural_attack/bite - ) var/brute_mod = 1 // Physical damage multiplier. var/burn_mod = 1 // Burn damage multiplier. @@ -499,23 +492,6 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 return TRUE //We could tie it to stamina return FALSE -// Called when using the shredding behavior. -/decl/species/proc/can_shred(var/mob/living/human/H, var/ignore_intent, var/ignore_antag) - - if((!ignore_intent && !H.check_intent(I_FLAG_HARM)) || H.pulling_punches) - return 0 - - if(!ignore_antag && H.mind && !player_is_antag(H.mind)) - return 0 - - for(var/attack_type in unarmed_attacks) - var/decl/natural_attack/attack = GET_DECL(attack_type) - if(!istype(attack) || !attack.is_usable(H)) - continue - if(attack.shredding) - return 1 - return 0 - /decl/species/proc/handle_vision(var/mob/living/human/H) var/list/vision = H.get_accumulated_vision_handlers() H.update_sight() diff --git a/code/modules/species/species_attack.dm b/code/modules/species/species_attack.dm index d2daf2daa06..0fffa984643 100644 --- a/code/modules/species/species_attack.dm +++ b/code/modules/species/species_attack.dm @@ -2,8 +2,8 @@ attack_verb = list("bit", "chomped on") attack_sound = 'sound/weapons/bite.ogg' shredding = 0 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE name = "sharp bite" /decl/natural_attack/claws @@ -14,8 +14,8 @@ eye_attack_text_victim = "sharp claws" attack_sound = 'sound/weapons/slice.ogg' miss_sound = 'sound/weapons/slashmiss.ogg' - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE name = "claws" usable_with_limbs = list(BP_L_HAND, BP_R_HAND) var/blocked_by_gloves = TRUE diff --git a/code/modules/species/species_helpers.dm b/code/modules/species/species_helpers.dm index 5bd257fa5bc..efd996aaf26 100644 --- a/code/modules/species/species_helpers.dm +++ b/code/modules/species/species_helpers.dm @@ -1,14 +1,9 @@ var/global/list/stored_shock_by_ref = list() /mob/living/proc/apply_stored_shock_to(var/mob/living/target) - if(stored_shock_by_ref["\ref[src]"]) - target.electrocute_act(stored_shock_by_ref["\ref[src]"]*0.9, src) - stored_shock_by_ref["\ref[src]"] = 0 - -/decl/species/proc/toggle_stance(var/mob/living/human/H) - if(!H.incapacitated()) - H.pulling_punches = !H.pulling_punches - to_chat(H, "You are now [H.pulling_punches ? "pulling your punches" : "not pulling your punches"].") + if(global.stored_shock_by_ref["\ref[src]"]) + target.electrocute_act(global.stored_shock_by_ref["\ref[src]"]*0.9, src) + global.stored_shock_by_ref["\ref[src]"] = 0 /decl/species/proc/fluid_act(var/mob/living/human/H, var/datum/reagents/fluids) SHOULD_CALL_PARENT(TRUE) diff --git a/code/modules/species/species_hud.dm b/code/modules/species/species_hud.dm index f63a7bc37bf..7add078db3a 100644 --- a/code/modules/species/species_hud.dm +++ b/code/modules/species/species_hud.dm @@ -1,6 +1,5 @@ /datum/hud_data - /// Set to draw intent box. - var/show_intent_selector = 1 + /// Set to draw move intent box. var/has_m_intent = 1 /// Set to draw environment warnings. diff --git a/code/modules/species/station/golem.dm b/code/modules/species/station/golem.dm index 9f571d66c60..93dc4786eae 100644 --- a/code/modules/species/station/golem.dm +++ b/code/modules/species/station/golem.dm @@ -16,7 +16,6 @@ available_bodytypes = list(/decl/bodytype/crystalline/golem) - unarmed_attacks = list(/decl/natural_attack/stomp, /decl/natural_attack/kick, /decl/natural_attack/punch) species_flags = SPECIES_FLAG_NO_POISON spawn_flags = SPECIES_IS_RESTRICTED shock_vulnerability = 0 diff --git a/code/modules/species/station/human.dm b/code/modules/species/station/human.dm index 6264a7b2048..2ee92427f41 100644 --- a/code/modules/species/station/human.dm +++ b/code/modules/species/station/human.dm @@ -2,12 +2,6 @@ name = SPECIES_HUMAN name_plural = "Humans" primitive_form = SPECIES_MONKEY - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch, - /decl/natural_attack/bite - ) description = "A medium-sized creature prone to great ambition. If you are reading this, you are probably a human." hidden_from_codex = FALSE spawn_flags = SPECIES_CAN_JOIN diff --git a/code/modules/species/station/monkey.dm b/code/modules/species/station/monkey.dm index 9159f5eadc3..1871219744e 100644 --- a/code/modules/species/station/monkey.dm +++ b/code/modules/species/station/monkey.dm @@ -15,7 +15,6 @@ dusted_anim = "dust-m" death_message = "lets out a faint chimper as it collapses and stops moving..." - unarmed_attacks = list(/decl/natural_attack/bite, /decl/natural_attack/claws, /decl/natural_attack/punch) inherent_verbs = list(/mob/living/proc/ventcrawl) species_hud = /datum/hud_data/monkey butchery_data = /decl/butchery_data/humanoid/monkey diff --git a/code/modules/spells/aoe_turf/aoe_turf.dm b/code/modules/spells/aoe_turf/aoe_turf.dm deleted file mode 100644 index 0f7a721de80..00000000000 --- a/code/modules/spells/aoe_turf/aoe_turf.dm +++ /dev/null @@ -1,23 +0,0 @@ -/* -Aoe turf spells target a ring of tiles around the user -This ring has an outer radius (range) and an inner radius (inner_radius) -Aoe turf spells have a useful flag: IGNOREDENSE. It is explained in setup.dm -*/ - -/spell/aoe_turf //affects all turfs in view or range (depends) - spell_flags = IGNOREDENSE - var/inner_radius = -1 //for all your ring spell needs - -/spell/aoe_turf/choose_targets(mob/user = usr) - var/list/targets = list() - - for(var/turf/target in view_or_range(range, holder, selection_type)) - if(!(target in view_or_range(inner_radius, holder, selection_type))) - if(target.density && (spell_flags & IGNOREDENSE)) - continue - targets += target - - if(!targets.len) //doesn't waste the spell - return - - return targets \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/conjure/conjure.dm b/code/modules/spells/aoe_turf/conjure/conjure.dm deleted file mode 100644 index 8dd91146310..00000000000 --- a/code/modules/spells/aoe_turf/conjure/conjure.dm +++ /dev/null @@ -1,73 +0,0 @@ -/* -Conjure spells spawn things (mobs, objs, turfs) in their summon_type -How they spawn stuff is decided by behaviour vars, which are explained below -*/ - -/spell/aoe_turf/conjure - name = "Conjure" - desc = "This spell conjures objs of the specified types in range." - - school = "conjuration" //funny, that - - var/list/summon_type = list() //determines what exactly will be summoned - //should NOT be text, like list(/obj/machinery/bot/ed209) - - range = 0 //default values: only spawn on the player tile - selection_type = "view" - - duration = 0 // 0=permanent, any other time in deciseconds - how long the summoned objects last for - var/summon_amt = 1 //amount of objects summoned - var/summon_exclusive = 0 //spawn one of everything, instead of random things - - var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet - //should have format of list("emagged" = 1,"name" = "Justicebot"), for example - - cast_sound = 'sound/magic/castsummon.ogg' - -/spell/aoe_turf/conjure/cast(list/targets, mob/user) - - for(var/i=1,i <= summon_amt,i++) - if(!targets.len) - break - var/summoned_object_type - if(summon_exclusive) - if(!summon_type.len) - break - summoned_object_type = summon_type[1] - summon_type -= summoned_object_type - else - summoned_object_type = pick(summon_type) - var/turf/spawn_place = pick(targets) - var/atom/summoned_object - if(ispath(summoned_object_type,/turf)) - spawn_place.ChangeTurf(summoned_object_type) - summoned_object = spawn_place - else - summoned_object = new summoned_object_type(spawn_place) - var/atom/movable/overlay/animation = new /atom/movable/overlay(summoned_object) - animation.SetName("conjure") - animation.set_density(0) - animation.anchored = TRUE - animation.icon = 'icons/effects/effects.dmi' - animation.layer = BASE_HUMAN_LAYER - if(ismob(summoned_object)) //we want them to NOT attack us. - var/mob/M = summoned_object - M.faction = user.faction - apply_vars(summoned_object, user) - - if(duration) - spawn(duration) - if(summoned_object && !isturf(summoned_object)) - qdel(summoned_object) - conjure_animation(animation, spawn_place) - return - -/spell/aoe_turf/conjure/proc/conjure_animation(var/atom/movable/overlay/animation, var/turf/target) - qdel(animation) - -/spell/aoe_turf/conjure/proc/apply_vars(atom/summoned_object, mob/caster) - if(!istype(summoned_object) || !length(newVars)) - return - for(var/varName in newVars) - if(varName in summoned_object.vars) - summoned_object.vars[varName] = newVars[varName] diff --git a/code/modules/spells/construct_spells.dm b/code/modules/spells/construct_spells.dm deleted file mode 100644 index 0c705aff4fc..00000000000 --- a/code/modules/spells/construct_spells.dm +++ /dev/null @@ -1,10 +0,0 @@ -//////////////////////////////Construct Spells///////////////////////// - -/proc/findNullRod(var/atom/target) - if(istype(target,/obj/item/nullrod)) - return 1 - else if(target.contents) - for(var/atom/A in target.contents) - if(findNullRod(A)) - return 1 - return 0 diff --git a/code/modules/spells/spell_code.dm b/code/modules/spells/spell_code.dm deleted file mode 100644 index bfcf1f4d8f6..00000000000 --- a/code/modules/spells/spell_code.dm +++ /dev/null @@ -1,385 +0,0 @@ -var/global/list/spells = typesof(/spell) //needed for the badmin verb for now - -/spell - var/name - var/desc - var/feedback = "" //what gets sent if this spell gets chosen by the spellbook. - parent_type = /datum - var/panel = "Spells"//What panel the proc holder needs to go on. - - var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? - /*Spell schools as follows: - Racial - Only tagged to spells gained for being a certain race - Conjuration - Creating an object or transporting it. - Transmutation - Modifying an object or transforming it. - Illusion - Altering perception or thought. - */ - var/charge_type = Sp_RECHARGE //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that - - var/charge_max = 100 //recharge time in deciseconds if charge_type = Sp_RECHARGE or starting charges if charge_type = Sp_CHARGES - var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = Sp_RECHARGE or -- each cast if charge_type = Sp_CHARGES - var/still_recharging_msg = "The spell is still recharging." - - var/silenced = 0 //not a binary - the length of time we can't cast this for - var/processing = 0 //are we processing already? Mainly used so that silencing a spell doesn't call process() again. (and inadvertedly making it run twice as fast) - - var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" - var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used - - var/spell_flags = 0 - var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell - var/invocation_type = SpI_NONE //can be none, whisper, shout, and emote - var/range = 7 //the range of the spell; outer radius for aoe spells - var/message = "" //whatever it says to the guy affected by it - var/selection_type = "view" //can be "range" or "view" - var/atom/movable/holder //where the spell is. Normally the user, can be an item - var/duration = 0 //how long the spell lasts - - var/list/spell_levels = list(Sp_SPEED = 0, Sp_POWER = 0) //the current spell levels - total spell levels can be obtained by just adding the two values - var/list/level_max = list(Sp_TOTAL = 4, Sp_SPEED = 4, Sp_POWER = 0) //maximum possible levels in each category. Total does cover both. - var/cooldown_reduc = 0 //If set, defines how much charge_max drops by every speed upgrade - var/delay_reduc = 0 - var/cooldown_min = 0 //minimum possible cooldown for a charging spell - - var/overlay = 0 - var/overlay_icon = 'icons/obj/wizard.dmi' - var/overlay_icon_state = "spell" - var/overlay_lifespan = 0 - - var/sparks_spread = 0 - var/sparks_amt = 0 //cropped at 10 - var/smoke_spread = 0 //1 - harmless, 2 - harmful - var/smoke_amt = 0 //cropped at 10 - - var/critfailchance = 0 - var/time_between_channels = 0 //Delay between casts - var/number_of_channels = 1 //How many times can we channel? - - var/cast_delay = 1 - var/cast_sound = "" - - var/hud_state = "" //name of the icon used in generating the spell hud object - var/override_base = "" - - - var/obj/screen/connected_button - - var/hidden_from_codex = FALSE - -/////////////////////// -///SETUP AND PROCESS/// -/////////////////////// - -/spell/New() - ..() - - //still_recharging_msg = "[name] is still recharging." - charge_counter = charge_max - -/spell/proc/process() - if(processing) - return - processing = 1 - spawn(0) - while(charge_counter < charge_max || silenced > 0) - charge_counter = min(charge_max,charge_counter+1) - silenced = max(0,silenced-1) - sleep(1) - if(connected_button) - var/obj/screen/ability/spell/S = connected_button - if(!istype(S)) - return - S.update_charge(1) - processing = 0 - return - -///////////////// -/////CASTING///// -///////////////// - -/spell/proc/choose_targets(mob/user = usr) //depends on subtype - see targeted.dm, aoe_turf.dm, dumbfire.dm, or code in general folder - return - -/spell/proc/perform(mob/user = usr, skipcharge = 0) //if recharge is started is important for the trigger spells - if(!holder) - holder = user //just in case - if(!cast_check(skipcharge, user)) - return - to_chat(user, SPAN_NOTICE("You start casting \the [name]...")) - if(cast_delay && !spell_do_after(user, cast_delay)) - return - var/list/targets = choose_targets(user) - if(!check_valid_targets(targets)) - to_chat(user, SPAN_WARNING("\The [name] fizzles. There are no valid targets nearby.")) - return - var/time = 0 - admin_attacker_log(user, "attempted to cast the spell [name]") - do - time++ - if(!check_valid_targets(targets)) //make sure we HAVE something - break - if(cast_check(1,user, targets)) //we check again, otherwise you can choose a target and then wait for when you are no longer able to cast (I.E. Incapacitated) to use it. - invocation(user, targets) - take_charge(user, skipcharge) - before_cast(targets) //applies any overlays and effects - if(prob(critfailchance)) - critfail(targets, user) - else - cast(targets, user, time) - after_cast(targets) //generates the sparks, smoke, target messages etc. - else - break - while(time != number_of_channels && do_after(user, time_between_channels, incapacitation_flags = INCAPACITATION_KNOCKOUT|INCAPACITATION_FORCELYING|INCAPACITATION_STUNNED, same_direction=1)) - after_spell(targets, user, time) //When we are done with the spell completely. - - - -/spell/proc/cast(list/targets, mob/user, var/channel_duration) //the actual meat of the spell - return - -/spell/proc/critfail(list/targets, mob/user) //the wizman has fucked up somehow - return - -/spell/proc/after_spell(var/list/targets, var/mob/user, var/channel_duration) //After everything else is done. - return - -/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types - switch(type) - if("bruteloss") - target.take_damage(amount) - if("fireloss") - target.take_damage(amount, BURN) - if("toxloss") - target.take_damage(amount, TOX) - if("oxyloss") - target.take_damage(amount, OXY) - if("brainloss") - target.take_damage(amount, BRAIN) - if("stunned") - ADJ_STATUS(target, STAT_STUN, amount) - if("weakened") - ADJ_STATUS(target, STAT_WEAK, amount) - if("paralysis") - ADJ_STATUS(target, STAT_PARA, amount) - else - target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars - return - -/////////////////////////// -/////CASTING WRAPPERS////// -/////////////////////////// - -/spell/proc/before_cast(list/targets) - for(var/atom/target in targets) - if(overlay) - var/location - if(isliving(target)) - location = target.loc - else if(isturf(target)) - location = target - var/obj/effect/overlay/spell = new /obj/effect/overlay(location) - spell.icon = overlay_icon - spell.icon_state = overlay_icon_state - spell.anchored = TRUE - spell.set_density(0) - spawn(overlay_lifespan) - qdel(spell) - -/spell/proc/after_cast(list/targets) - if(cast_sound) - playsound(get_turf(holder),cast_sound,50,1) - for(var/atom/target in targets) - var/location = get_turf(target) - if(isliving(target) && message) - to_chat(target, text("[message]")) - if(sparks_spread) - spark_at(location, amount = sparks_amt) - if(smoke_spread) - if(smoke_spread == 1) - var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - else if(smoke_spread == 2) - var/datum/effect/effect/system/smoke_spread/bad/smoke = new /datum/effect/effect/system/smoke_spread/bad() - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - -///////////////////// -////CASTING TOOLS//// -///////////////////// -/*Checkers, cost takers, message makers, etc*/ - -/spell/proc/cast_check(skipcharge = 0,mob/user = usr, var/list/targets) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell - - if(silenced > 0) - return 0 - - if(!(src in user.mind.learned_spells) && holder == user && !(isanimal(user))) - error("[user] utilized the spell '[src]' without having it.") - to_chat(user, "You shouldn't have this spell! Something's wrong.") - return 0 - - var/turf/user_turf = get_turf(user) - if(!user_turf) - to_chat(user, "You cannot cast spells in null space!") - - if((spell_flags & Z2NOCAST) && isAdminLevel(user_turf.z)) //Certain spells are not allowed on the centcomm zlevel - return 0 - - if(spell_flags & CONSTRUCT_CHECK) - for(var/turf/T in range(holder, 1)) - if(findNullRod(T)) - return 0 - - if(!src.check_charge(skipcharge, user)) //sees if we can cast based on charges alone - return 0 - - if(holder == user) - if(isanimal(user)) - var/mob/living/simple_animal/SA = user - if(SA.purge) - to_chat(SA, "The null sceptre's power interferes with your own!") - return 0 - - var/mob/living/L = user - if(L.incapacitated(INCAPACITATION_STUNNED|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_FORCELYING|INCAPACITATION_KNOCKOUT)) - to_chat(user, "You can't cast spells while incapacitated!") - return 0 - - if(ishuman(user) && !(invocation_type in list(SpI_EMOTE, SpI_NONE)) && user.get_item_blocking_speech()) - to_chat(user, "Mmmf mrrfff!") - return 0 - - return 1 - -/spell/proc/check_charge(var/skipcharge, mob/user) - if(!skipcharge) - switch(charge_type) - if(Sp_RECHARGE) - if(charge_counter < charge_max) - to_chat(user, still_recharging_msg) - return 0 - if(Sp_CHARGES) - if(!charge_counter) - to_chat(user, "[name] has no charges left.") - return 0 - return 1 - -/spell/proc/take_charge(mob/user = user, var/skipcharge) - if(!skipcharge) - switch(charge_type) - if(Sp_RECHARGE) - charge_counter = 0 //doesn't start recharging until the targets selecting ends - src.process() - return 1 - if(Sp_CHARGES) - charge_counter-- //returns the charge if the targets selecting fails - return 1 - if(Sp_HOLDVAR) - adjust_var(user, holder_var_type, holder_var_amount) - return 1 - return 0 - return 1 - -/spell/proc/check_valid_targets(var/list/targets) - if(!targets) - return 0 - if(!islist(targets)) - targets = list(targets) - else if(!targets.len) - return 0 - - var/list/valid_targets = view_or_range(range, holder, selection_type) - for(var/target in targets) - if(!(target in valid_targets)) - return 0 - return 1 - -/spell/proc/invocation(mob/user = usr, var/list/targets) //spelling the spell out and setting it on recharge/reducing charges amount - - switch(invocation_type) - if(SpI_SHOUT) - if(prob(50))//Auto-mute? Fuck that noise - user.say(invocation) - else - user.say(replacetext(invocation," ","`")) - if(SpI_WHISPER) - if(prob(50)) - user.whisper(invocation) - else - user.whisper(replacetext(invocation," ","`")) - if(SpI_EMOTE) - user.custom_emote(VISIBLE_MESSAGE, invocation) - -///////////////////// -///UPGRADING PROCS/// -///////////////////// - -/spell/proc/can_improve(var/upgrade_type) - if(level_max[Sp_TOTAL] <= ( spell_levels[Sp_SPEED] + spell_levels[Sp_POWER] )) //too many levels, can't do it - return 0 - - //if(upgrade_type && spell_levels[upgrade_type] && level_max[upgrade_type]) - if(upgrade_type && spell_levels[upgrade_type] >= level_max[upgrade_type]) - return 0 - - return 1 - -/spell/proc/empower_spell() - if(!can_improve(Sp_POWER)) - return 0 - - spell_levels[Sp_POWER]++ - - return 1 - -/spell/proc/quicken_spell() - if(!can_improve(Sp_SPEED)) - return 0 - - spell_levels[Sp_SPEED]++ - - if(delay_reduc && cast_delay) - cast_delay = max(0, cast_delay - delay_reduc) - else if(cast_delay) - cast_delay = round( max(0, initial(cast_delay) * ((level_max[Sp_SPEED] - spell_levels[Sp_SPEED]) / level_max[Sp_SPEED] ) ) ) - - if(charge_type == Sp_RECHARGE) - if(cooldown_reduc) - charge_max = max(cooldown_min, charge_max - cooldown_reduc) - else - charge_max = round( max(cooldown_min, initial(charge_max) * ((level_max[Sp_SPEED] - spell_levels[Sp_SPEED]) / level_max[Sp_SPEED] ) ) ) //the fraction of the way you are to max speed levels is the fraction you lose - if(charge_max < charge_counter) - charge_counter = charge_max - - var/temp = "" - name = initial(name) - switch(level_max[Sp_SPEED] - spell_levels[Sp_SPEED]) - if(3) - temp = "You have improved [name] into Efficient [name]." - name = "Efficient [name]" - if(2) - temp = "You have improved [name] into Quickened [name]." - name = "Quickened [name]" - if(1) - temp = "You have improved [name] into Free [name]." - name = "Free [name]" - if(0) - temp = "You have improved [name] into Instant [name]." - name = "Instant [name]" - - return temp - -/spell/proc/spell_do_after(var/mob/user, delay, var/numticks = 5) - if(!user || isnull(user)) - return 0 - - var/incap_flags = INCAPACITATION_STUNNED|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_FORCELYING|INCAPACITATION_KNOCKOUT - return do_after(user,delay, incapacitation_flags = incap_flags) - -/proc/view_or_range(distance = world.view , center = usr , type) - switch(type) - if("view") - . = view(distance,center) - if("range") - . = range(distance,center) \ No newline at end of file diff --git a/code/modules/spells/spells.dm b/code/modules/spells/spells.dm deleted file mode 100644 index ad550747a7f..00000000000 --- a/code/modules/spells/spells.dm +++ /dev/null @@ -1,60 +0,0 @@ -/datum/mind - var/list/learned_spells - -/mob/Stat() - . = ..() - if(. && ability_master && ability_master.spell_objects) - for(var/obj/screen/ability/spell/screen in ability_master.spell_objects) - var/spell/S = screen.spell - if((!S.connected_button) || !statpanel(S.panel)) - continue //Not showing the noclothes spell - switch(S.charge_type) - if(Sp_RECHARGE) - statpanel(S.panel,"[S.charge_counter/10.0]/[S.charge_max/10]",S.connected_button) - if(Sp_CHARGES) - statpanel(S.panel,"[S.charge_counter]/[S.charge_max]",S.connected_button) - if(Sp_HOLDVAR) - statpanel(S.panel,"[S.holder_var_type] [S.holder_var_amount]",S.connected_button) - -/proc/restore_spells(var/mob/H) - if(H.mind && H.mind.learned_spells) - var/list/spells = list() - for(var/spell/spell_to_remove in H.mind.learned_spells) //remove all the spells from other people. - if(ismob(spell_to_remove.holder)) - var/mob/M = spell_to_remove.holder - spells += spell_to_remove - M.remove_spell(spell_to_remove) - - for(var/spell/spell_to_add in spells) - H.add_spell(spell_to_add) - H.ability_master.update_abilities(0,H) - -/mob/proc/add_spell(var/spell/spell_to_add, var/spell_base = "wiz_spell_ready") - if(!ability_master) - ability_master = new(null, src) - spell_to_add.holder = src - if(mind) - if(!mind.learned_spells) - mind.learned_spells = list() - mind.learned_spells |= spell_to_add - ability_master.add_spell(spell_to_add, spell_base) - return 1 - -/mob/proc/remove_spell(var/spell/spell_to_remove) - if(!spell_to_remove || !istype(spell_to_remove)) - return - - if(mind) - mind.learned_spells -= spell_to_remove - if (ability_master) - ability_master.remove_ability(ability_master.get_ability_by_spell(spell_to_remove)) - return 1 - -/mob/proc/silence_spells(var/amount = 0) - if(amount < 0) - return - - if(!ability_master) - return - - ability_master.silence_spells(amount) \ No newline at end of file diff --git a/code/modules/spells/targeted/ethereal_jaunt.dm b/code/modules/spells/targeted/ethereal_jaunt.dm deleted file mode 100644 index 55a0ed83359..00000000000 --- a/code/modules/spells/targeted/ethereal_jaunt.dm +++ /dev/null @@ -1,120 +0,0 @@ -/spell/targeted/ethereal_jaunt - name = "Ethereal Jaunt" - desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls." - feedback = "EJ" - school = "transmutation" - charge_max = 30 SECONDS - spell_flags = Z2NOCAST | INCLUDEUSER - invocation = "none" - invocation_type = SpI_NONE - range = 0 - max_targets = 1 - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 4, Sp_POWER = 3) - cooldown_min = 10 SECONDS //50 deciseconds reduction per rank - duration = 5 SECONDS - - hud_state = "wiz_jaunt" - -/spell/targeted/ethereal_jaunt/cast(list/targets) //magnets, so mostly hardcoded - for(var/mob/living/target in targets) - if(HAS_TRANSFORMATION_MOVEMENT_HANDLER(target)) - continue - - if(target.buckled) - target.buckled.unbuckle_mob() - spawn(0) - var/mobloc = get_turf(target.loc) - var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt( mobloc ) - var/atom/movable/overlay/animation = new /atom/movable/overlay(holder) - animation.SetName("water") - animation.set_density(0) - animation.anchored = TRUE - animation.icon = 'icons/mob/mob.dmi' - animation.layer = FLY_LAYER - target.extinguish_fire() - if(target.buckled) - target.buckled = null - jaunt_disappear(animation, target) - target.forceMove(holder) - jaunt_steam(mobloc) - sleep(duration) - mobloc = holder.last_valid_turf - animation.forceMove(mobloc) - jaunt_steam(mobloc) - holder.reappearing = 1 - sleep(20) - jaunt_reappear(animation, target) - sleep(5) - if(!target.forceMove(mobloc)) - for(var/direction in list(1,2,4,8,5,6,9,10)) - var/turf/T = get_step(mobloc, direction) - if(T) - if(target.forceMove(T)) - break - target.client.eye = target - qdel(animation) - qdel(holder) - -/spell/targeted/ethereal_jaunt/empower_spell() - if(!..()) - return 0 - duration += 2 SECONDS - - return "[src] now lasts longer." - -/spell/targeted/ethereal_jaunt/proc/jaunt_disappear(var/atom/movable/overlay/animation, var/mob/living/target) - animation.icon_state = "liquify" - flick("liquify",animation) - playsound(get_turf(target), 'sound/magic/ethereal_enter.ogg', 30) - -/spell/targeted/ethereal_jaunt/proc/jaunt_reappear(var/atom/movable/overlay/animation, var/mob/living/target) - flick("reappear",animation) - playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 30) - -/spell/targeted/ethereal_jaunt/proc/jaunt_steam(var/mobloc) - var/datum/effect/effect/system/steam_spread/steam = new /datum/effect/effect/system/steam_spread() - steam.set_up(10, 0, mobloc) - steam.start() - -/obj/effect/dummy/spell_jaunt - name = "water" - icon = 'icons/effects/effects.dmi' - icon_state = "nothing" - var/canmove = 1 - var/reappearing = 0 - density = FALSE - anchored = TRUE - var/turf/last_valid_turf - -/obj/effect/dummy/spell_jaunt/Initialize() - . = ..() - last_valid_turf = get_turf(loc) - -/obj/effect/dummy/spell_jaunt/Destroy() - // Eject contents if deleted somehow - for(var/atom/movable/AM in src) - AM.dropInto(loc) - return ..() - -/obj/effect/dummy/spell_jaunt/relaymove(var/mob/user, direction) - if (!canmove || reappearing) return - var/turf/newLoc = get_step(src, direction) - if(!(newLoc.turf_flags & TURF_FLAG_NOJAUNT)) - forceMove(newLoc) - var/turf/T = get_turf(loc) - if(!T.contains_dense_objects()) - last_valid_turf = T - else - to_chat(user, "Some strange aura is blocking the way!") - canmove = 0 - addtimer(CALLBACK(src, PROC_REF(allow_move)), 2) - -/obj/effect/dummy/spell_jaunt/proc/allow_move() - canmove = TRUE - -/obj/effect/dummy/spell_jaunt/explosion_act(blah) - SHOULD_CALL_PARENT(FALSE) - return - -/obj/effect/dummy/spell_jaunt/bullet_act(blah) - return diff --git a/code/modules/spells/targeted/shift.dm b/code/modules/spells/targeted/shift.dm deleted file mode 100644 index 2bcc684939a..00000000000 --- a/code/modules/spells/targeted/shift.dm +++ /dev/null @@ -1,24 +0,0 @@ -/spell/targeted/ethereal_jaunt/shift - name = "Phase Shift" - desc = "This spell allows you to pass through walls" - - charge_max = 200 - spell_flags = Z2NOCAST | INCLUDEUSER | CONSTRUCT_CHECK - invocation_type = SpI_NONE - range = -1 - duration = 50 //in deciseconds - - hud_state = "const_shift" - -/spell/targeted/ethereal_jaunt/shift/jaunt_disappear(var/atom/movable/overlay/animation, var/mob/living/target) - animation.icon_state = "phase_shift" - animation.set_dir(target.dir) - flick("phase_shift",animation) - -/spell/targeted/ethereal_jaunt/shift/jaunt_reappear(var/atom/movable/overlay/animation, var/mob/living/target) - animation.icon_state = "phase_shift2" - animation.set_dir(target.dir) - flick("phase_shift2",animation) - -/spell/targeted/ethereal_jaunt/shift/jaunt_steam(var/mobloc) - return diff --git a/code/modules/spells/targeted/targeted.dm b/code/modules/spells/targeted/targeted.dm deleted file mode 100644 index 733bbf1e540..00000000000 --- a/code/modules/spells/targeted/targeted.dm +++ /dev/null @@ -1,152 +0,0 @@ -/* -Targeted spells (with the exception of dumbfire) select from all the mobs in the defined range -Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are explained in setup.dm -*/ - -/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob - var/max_targets = 1 //leave 0 for unlimited targets in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range - var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast - - - var/amt_weakened = 0 - var/amt_paralysis = 0 - var/amt_stunned = 0 - - var/amt_dizziness = 0 - var/amt_confused = 0 - var/amt_stuttering = 0 - - //set to negatives for healing unless commented otherwise - var/amt_dam_fire = 0 - var/amt_dam_brute = 0 - var/amt_dam_oxy = 0 - var/amt_dam_tox = 0 - var/amt_dam_robo = 0 - var/amt_brain = 0 - var/amt_radiation = 0 - var/amt_blood = 0 //Positive numbers to add blood - var/amt_organ = 0 //Positive numbers for healing - - var/amt_eye_blind = 0 - var/amt_eye_blurry = 0 - - var/effect_state = null //What effect to show on each, if any - var/effect_duration = 0 - var/effect_color = "#ffffff" - - var/list/compatible_mobs = list() - - -/spell/targeted/choose_targets(mob/user = usr) - var/list/targets = list() - - if(max_targets == 0) //unlimited - if(range == -2) - targets = global.living_mob_list_ - else - for(var/mob/living/target in view_or_range(range, holder, selection_type)) - targets += target - - else if(max_targets == 1) //single target can be picked - if((range == 0 || range == -1) && spell_flags & INCLUDEUSER) - targets += user - else - var/list/possible_targets = list() - var/list/starting_targets - if(range == -2) - starting_targets = global.living_mob_list_ - else - starting_targets = view_or_range(range, holder, selection_type) - - for(var/mob/living/M in starting_targets) - if(!(spell_flags & INCLUDEUSER) && M == user) - continue - if(compatible_mobs && compatible_mobs.len) - if(!is_type_in_list(M, compatible_mobs)) continue - if(compatible_mobs && compatible_mobs.len && !is_type_in_list(M, compatible_mobs)) - continue - possible_targets += M - - if(possible_targets.len) - targets += pick(possible_targets) - //Adds a safety check post-input to make sure those targets are actually in range. - - - else - var/list/possible_targets = list() - var/list/starting_targets - - if(range == -2) - starting_targets = global.living_mob_list_ - else - starting_targets = view_or_range(range, holder, selection_type) - - for(var/mob/living/target in starting_targets) - if(!(spell_flags & INCLUDEUSER) && target == user) - continue - if(compatible_mobs && !is_type_in_list(target, compatible_mobs)) - continue - possible_targets += target - - for(var/i=1,i<=max_targets,i++) - if(!possible_targets.len) - break - if(target_ignore_prev) - var/target = pick(possible_targets) - possible_targets -= target - targets += target - else - targets += pick(possible_targets) - - if(!(spell_flags & INCLUDEUSER) && (user in targets)) - targets -= user - - if(compatible_mobs && compatible_mobs.len) - for(var/mob/living/target in targets) //filters out all the non-compatible mobs - if(!is_type_in_list(target, compatible_mobs)) - targets -= target - - return targets - -/spell/targeted/cast(var/list/targets, mob/user) - for(var/mob/living/target in targets) - if(range >= 0) - if(!(target in view_or_range(range, holder, selection_type))) //filter at time of casting - targets -= target - continue - apply_spell_damage(target) - -/spell/targeted/proc/apply_spell_damage(mob/living/target) - target.take_damage(amt_dam_brute, do_update_health = FALSE) - target.take_damage(amt_dam_fire, BURN, do_update_health = FALSE) - target.take_damage(amt_dam_tox, TOX, do_update_health = FALSE) - target.take_damage(amt_dam_oxy, OXY) - if(ishuman(target)) - var/mob/living/human/H = target - for(var/obj/item/organ/internal/affecting in H.get_internal_organs()) - if(affecting && istype(affecting)) - affecting.heal_damage(amt_organ, amt_organ) - for(var/obj/item/organ/external/affecting in H.get_external_organs()) - if(affecting && istype(affecting)) - var/dam = BP_IS_PROSTHETIC(affecting) ? -amt_dam_robo : amt_organ - affecting.heal_damage(dam, dam, robo_repair = BP_IS_PROSTHETIC(affecting)) - H.adjust_blood(amt_blood) - H.take_damage(amt_brain, BRAIN) - H.radiation += min(H.radiation, amt_radiation) - - target.update_icon() - //disabling - SET_STATUS_MAX(target, STAT_WEAK, amt_weakened) - SET_STATUS_MAX(target, STAT_PARA, amt_paralysis) - SET_STATUS_MAX(target, STAT_STUN, amt_stunned) - if(amt_weakened || amt_paralysis || amt_stunned) - if(target.buckled) - target.buckled = null - ADJ_STATUS(target, STAT_BLIND, amt_eye_blind) - ADJ_STATUS(target, STAT_BLURRY, amt_eye_blurry) - ADJ_STATUS(target, STAT_DIZZY, amt_dizziness) - ADJ_STATUS(target, STAT_CONFUSE, amt_confused) - ADJ_STATUS(target, STAT_STUTTER, amt_stuttering) - if(effect_state) - var/obj/o = new /obj/effect/temporary(get_turf(target), effect_duration, 'icons/effects/effects.dmi', effect_state) - o.color = effect_color diff --git a/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm b/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm index 8b5d7b48734..d134b6782f8 100644 --- a/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm +++ b/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm @@ -19,8 +19,7 @@ var/list/selectable_labels_to_states = list() /decl/sprite_accessory_metadata/gradient/Initialize() - var/list/selectable = icon_states(icon) - for(var/state in selectable) + for(var/state in get_states_in_icon_cached(icon)) if(!selectable_states_to_labels[state]) selectable_states_to_labels[state] = capitalize(state) for(var/state in selectable_states_to_labels) diff --git a/code/modules/submaps/submap_job.dm b/code/modules/submaps/submap_job.dm index 9bc3687a1ae..89d8b2f3871 100644 --- a/code/modules/submaps/submap_job.dm +++ b/code/modules/submaps/submap_job.dm @@ -7,7 +7,8 @@ create_record = FALSE total_positions = 4 outfit_type = /decl/outfit/job/survivor - hud_icon = "hudblank" + hud_icon_state = "hudblank" + hud_icon = null available_by_default = FALSE allowed_ranks = null allowed_branches = null @@ -55,7 +56,12 @@ if(islist(blacklisted_species) && !length(blacklisted_species)) blacklisted_species |= SSmodpacks.default_submap_blacklisted_species - if(!abstract_job) + if(abstract_job) + if(!hud_icon) + hud_icon = global.using_map.hud_icons + if(!hud_icon_state) + hud_icon_state = "hud[ckey(title)]" + else spawnpoints = list() owner = _owner ..() diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index 1b8ef1751a0..be2f0f6b69d 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -21,6 +21,12 @@ affected.take_external_damage(20, 0, (DAM_SHARP|DAM_EDGE), used_weapon = tool) ..() +/decl/surgery_step/cavity/get_skill_reqs(mob/living/user, mob/living/target, obj/item/tool, target_zone) + var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) + if(!affected || !BP_IS_PROSTHETIC(affected) || BP_IS_CRYSTAL(affected)) + return ..() + return SURGERY_SKILLS_ROBOTIC + ////////////////////////////////////////////////////////////////// // create implant space surgery step ////////////////////////////////////////////////////////////////// @@ -38,16 +44,16 @@ /decl/surgery_step/cavity/make_space/begin_step(mob/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] starts making some space inside [target]'s [affected.cavity_name] cavity with \the [tool].", \ - "You start making some space inside [target]'s [affected.cavity_name] cavity with \the [tool]." ) + user.visible_message("[user] starts making some space inside [target]'s [affected.cavity_name] with \the [tool].", \ + "You start making some space inside [target]'s [affected.cavity_name] with \the [tool]." ) target.custom_pain("The pain in your chest is living hell!",1,affecting = affected) affected.cavity = TRUE ..() /decl/surgery_step/cavity/make_space/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] makes some space inside [target]'s [affected.cavity_name] cavity with \the [tool].", \ - "You make some space inside [target]'s [affected.cavity_name] cavity with \the [tool]." ) + user.visible_message("[user] makes some space inside [target]'s \the [affected.cavity_name] with \the [tool].", \ + "You make some space inside [target]'s \the [affected.cavity_name] with \the [tool]." ) ..() ////////////////////////////////////////////////////////////////// @@ -70,15 +76,15 @@ /decl/surgery_step/cavity/close_space/begin_step(mob/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] starts mending [target]'s [affected.cavity_name] cavity wall with \the [tool].", \ - "You start mending [target]'s [affected.cavity_name] cavity wall with \the [tool]." ) + user.visible_message("[user] starts mending [target]'s \the [affected.cavity_name] wall with \the [tool].", \ + "You start mending [target]'s \the [affected.cavity_name] wall with \the [tool]." ) target.custom_pain("The pain in your chest is living hell!",1,affecting = affected) ..() /decl/surgery_step/cavity/close_space/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] mends [target]'s [affected.cavity_name] cavity walls with \the [tool].", \ - "You mend [target]'s [affected.cavity_name] cavity walls with \the [tool]." ) + user.visible_message("[user] mends [target]'s \the [affected.cavity_name] walls with \the [tool].", \ + "You mend [target]'s \the [affected.cavity_name] walls with \the [tool]." ) affected.cavity = FALSE ..() @@ -109,7 +115,7 @@ if(affected && affected.cavity) var/max_volume = BASE_STORAGE_CAPACITY(affected.cavity_max_w_class) + affected.internal_organs_size if(tool.w_class > affected.cavity_max_w_class) - to_chat(user, SPAN_WARNING("\The [tool] is too big for [affected.cavity_name] cavity.")) + to_chat(user, SPAN_WARNING("\The [tool] is too big for \the [affected.cavity_name].")) return FALSE var/total_volume = tool.get_storage_cost() for(var/obj/item/I in affected.implants) @@ -119,14 +125,14 @@ for(var/obj/item/organ/internal/org in affected.internal_organs) max_volume -= org.get_storage_cost() if(total_volume > max_volume) - to_chat(user, SPAN_WARNING("There isn't enough space left in [affected.cavity_name] cavity for [tool].")) + to_chat(user, SPAN_WARNING("There isn't enough space left in \the [affected.cavity_name] for [tool].")) return FALSE return TRUE /decl/surgery_step/cavity/place_item/begin_step(mob/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] starts putting \the [tool] inside [target]'s [affected.cavity_name] cavity.", \ - "You start putting \the [tool] inside [target]'s [affected.cavity_name] cavity." ) + user.visible_message("[user] starts putting \the [tool] inside [target]'s \the [affected.cavity_name].", \ + "You start putting \the [tool] inside [target]'s \the [affected.cavity_name]." ) target.custom_pain("The pain in your chest is living hell!",1,affecting = affected) ..() @@ -134,8 +140,8 @@ var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) if(!user.try_unequip(tool, affected)) return - user.visible_message("[user] puts \the [tool] inside [target]'s [affected.cavity_name] cavity.", \ - "You put \the [tool] inside [target]'s [affected.cavity_name] cavity." ) + user.visible_message("[user] puts \the [tool] inside [target]'s \the [affected.cavity_name].", \ + "You put \the [tool] inside [target]'s \the [affected.cavity_name]." ) if (tool.w_class > affected.cavity_max_w_class/2 && prob(50) && !BP_IS_PROSTHETIC(affected) && affected.sever_artery()) to_chat(user, "You tear some blood vessels trying to fit such a big object in this cavity.") affected.owner.custom_pain("You feel something rip in your [affected.name]!", 1,affecting = affected) diff --git a/code/modules/surgery/necrotic.dm b/code/modules/surgery/necrotic.dm index 14b39b1f739..6f41080ae0d 100644 --- a/code/modules/surgery/necrotic.dm +++ b/code/modules/surgery/necrotic.dm @@ -133,15 +133,15 @@ var/list/dead_organs if(E.status & ORGAN_DEAD) var/image/radial_button = image(icon = E.icon, icon_state = E.icon_state) - radial_button.name = "Regenerate \the [E.name]" + radial_button.name = "Regenerate \the [E]" LAZYSET(dead_organs, E.organ_tag, radial_button) for(var/obj/item/organ/I in target.get_internal_organs()) if(I && (I.status & ORGAN_DEAD) && I.parent_organ == target_zone) if(!I.can_recover()) - to_chat(user, SPAN_WARNING("\The [I.name] is beyond saving.")) + to_chat(user, SPAN_WARNING("\The [I] is beyond saving.")) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Regenerate \the [I.name]" + radial_button.name = "Regenerate \the [I]" LAZYSET(dead_organs, I.organ_tag, radial_button) if(!LAZYLEN(dead_organs)) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index f6b9bffb28e..76b37e9eb8d 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -104,7 +104,7 @@ for(var/obj/item/organ/I in target.get_internal_organs()) if(I && !(I.status & ORGAN_CUT_AWAY) && I.parent_organ == target_zone) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Detach \the [I.name]" + radial_button.name = "Detach \the [I]" LAZYSET(attached_organs, I.organ_tag, radial_button) if(!LAZYLEN(attached_organs)) to_chat(user, SPAN_WARNING("You can't find any organs to separate.")) @@ -158,7 +158,7 @@ for(var/obj/item/organ/internal/I in affected.implants) if(I.status & ORGAN_CUT_AWAY) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Remove \the [I.name]" + radial_button.name = "Remove \the [I]" LAZYSET(removable_organs, I, radial_button) if(!LAZYLEN(removable_organs)) to_chat(user, SPAN_WARNING("You can't find any removable organs.")) @@ -262,7 +262,7 @@ return FALSE if(O.w_class > affected.cavity_max_w_class) - to_chat(user, SPAN_WARNING("\The [O.name] [pronouns.is] too big for [affected.cavity_name] cavity!")) + to_chat(user, SPAN_WARNING("\The [O.name] [pronouns.is] too big for \the [affected.cavity_name]!")) return FALSE var/obj/item/organ/internal/I = GET_INTERNAL_ORGAN(target, O.organ_tag) @@ -336,7 +336,7 @@ for(var/obj/item/organ/I in (affected.implants|affected.internal_organs)) if(I.status & ORGAN_CUT_AWAY) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Attach \the [I.name]" + radial_button.name = "Attach \the [I]" LAZYSET(attachable_organs, I, radial_button) if(!LAZYLEN(attachable_organs)) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 44020605dee..8695ab98499 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -416,7 +416,7 @@ for(var/obj/item/organ/I in target.get_internal_organs()) if(I && !(I.status & ORGAN_CUT_AWAY) && !BP_IS_CRYSTAL(I) && I.parent_organ == target_zone) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Detach \the [I.name]" + radial_button.name = "Detach \the [I]" LAZYSET(attached_organs, I.organ_tag, radial_button) if(!LAZYLEN(attached_organs)) to_chat(user, SPAN_WARNING("There are no appropriate internal components to decouple.")) @@ -461,7 +461,7 @@ for(var/obj/item/organ/I in affected.implants) if ((I.status & ORGAN_CUT_AWAY) && BP_IS_PROSTHETIC(I) && !BP_IS_CRYSTAL(I) && (I.parent_organ == target_zone)) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Reattach \the [I.name]" + radial_button.name = "Reattach \the [I]" LAZYSET(removable_organs, I.organ_tag, radial_button) var/organ_to_replace = show_radial_menu(user, tool, removable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) if(!organ_to_replace) diff --git a/code/modules/tools/archetypes/tool_archetype_definitions.dm b/code/modules/tools/archetypes/tool_archetype_definitions.dm index 25df48e3afd..2b2b2f14f4d 100644 --- a/code/modules/tools/archetypes/tool_archetype_definitions.dm +++ b/code/modules/tools/archetypes/tool_archetype_definitions.dm @@ -70,16 +70,16 @@ /decl/tool_archetype/knife/get_default_quality(obj/item/tool) if(tool) - if(tool.sharp && tool.edge) + if(tool.is_sharp() && tool.has_edge()) return TOOL_QUALITY_DEFAULT - else if(tool.sharp || tool.edge) + else if(tool.is_sharp() || tool.has_edge()) return TOOL_QUALITY_MEDIOCRE return ..() /decl/tool_archetype/knife/get_default_speed(obj/item/tool) if(tool) - if(tool.sharp && tool.edge) + if(tool.is_sharp() && tool.has_edge()) return TOOL_SPEED_DEFAULT - else if(tool.sharp || tool.edge) + else if(tool.is_sharp() || tool.has_edge()) return TOOL_SPEED_MEDIOCRE return ..() diff --git a/code/modules/tools/components/head.dm b/code/modules/tools/components/head.dm index 5d7aa85f512..e8e408c7c76 100644 --- a/code/modules/tools/components/head.dm +++ b/code/modules/tools/components/head.dm @@ -40,6 +40,11 @@ var/global/list/_tool_properties_cache = list() desc = "The head of a hoe." icon_state = "hoe" +/obj/item/tool_component/head/chisel + name = "chisel head" + desc = "The head of a chisel." + icon_state = "hoe" + /obj/item/tool_component/head/handaxe name = "hand axe head" desc = "The head of a hand axe." @@ -58,3 +63,8 @@ var/global/list/_tool_properties_cache = list() icon_state = "sledgehammer" w_class = ITEM_SIZE_NORMAL +/obj/item/tool_component/head/forging_hammer + name = "forging hammer head" + desc = "The head of a forging hammer." + icon_state = "forging" + w_class = ITEM_SIZE_NORMAL diff --git a/code/modules/tools/subtypes/axes.dm b/code/modules/tools/subtypes/axes.dm index 94e14da6274..d671ffa0747 100644 --- a/code/modules/tools/subtypes/axes.dm +++ b/code/modules/tools/subtypes/axes.dm @@ -19,8 +19,12 @@ return tool_qualities /obj/item/tool/axe/ebony + material = /decl/material/solid/metal/iron handle_material = /decl/material/solid/organic/wood/ebony +/obj/item/tool/axe/iron + material = /decl/material/solid/metal/iron + /obj/item/tool/axe/ebony/bronze material = /decl/material/solid/metal/bronze diff --git a/code/modules/tools/subtypes/hammers.dm b/code/modules/tools/subtypes/hammers.dm index bb1c9ee58cd..ff5d1ef1989 100644 --- a/code/modules/tools/subtypes/hammers.dm +++ b/code/modules/tools/subtypes/hammers.dm @@ -2,8 +2,6 @@ name = "hammer" desc = "A simple hammer. Ancient technology once thought lost." icon = 'icons/obj/items/tool/hammers/hammer.dmi' - sharp = 0 - edge = 0 attack_verb = list( "bludgeons", "slaps", @@ -79,3 +77,18 @@ TOOL_SHOVEL = TOOL_QUALITY_DECENT ) return tool_qualities + +/obj/item/tool/hammer/forge + name = "forging hammer" + desc = "A heavy hammer, used to forge hot metal at an anvil." + icon = 'icons/obj/items/tool/hammers/forge.dmi' + w_class = ITEM_SIZE_NORMAL + +// Forging hammers are not great at general hammer tasks (too heavy I guess), +// and also don't work as crowbars due to missing the nail ripper/flange, +// but will be more effective at forging when blacksmithy is merged. +/obj/item/tool/hammer/forge/get_initial_tool_qualities() + var/static/list/tool_qualities = list( + TOOL_HAMMER = TOOL_QUALITY_MEDIOCRE + ) + return tool_qualities diff --git a/code/modules/tools/subtypes/xenoarchaeology_picks.dm b/code/modules/tools/subtypes/xenoarchaeology_picks.dm index 9db2d59f541..2a335b9fa4c 100644 --- a/code/modules/tools/subtypes/xenoarchaeology_picks.dm +++ b/code/modules/tools/subtypes/xenoarchaeology_picks.dm @@ -7,7 +7,7 @@ material = /decl/material/solid/metal/chromium matter = list(/decl/material/solid/metal/steel = MATTER_AMOUNT_SECONDARY) w_class = ITEM_SIZE_SMALL - sharp = 1 + sharp = TRUE abstract_type = /obj/item/tool/xeno material_alteration = 0 handle_material = /decl/material/solid/organic/plastic @@ -36,18 +36,18 @@ to_chat(user, "This tool has a [get_tool_property(TOOL_PICK, TOOL_PROP_EXCAVATION_DEPTH) || 0] centimetre excavation depth.") /obj/item/tool/xeno/brush - name = "wire brush" - icon_state = "pick_brush" - slot_flags = SLOT_EARS - _base_attack_force = 1 - attack_verb = list("prodded", "attacked") - desc = "A wood-handled brush with thick metallic wires for clearing away dust and loose scree." - sharp = 0 - material = /decl/material/solid/metal/steel - handle_material = /decl/material/solid/organic/wood/oak - excavation_amount = 1 - excavation_sound = "sweeping" - excavation_verb = "brushing" + name = "wire brush" + icon_state = "pick_brush" + slot_flags = SLOT_EARS + _base_attack_force = 1 + attack_verb = list("prodded", "attacked") + desc = "A wood-handled brush with thick metallic wires for clearing away dust and loose scree." + sharp = FALSE + material = /decl/material/solid/metal/steel + handle_material = /decl/material/solid/organic/wood/oak + excavation_amount = 1 + excavation_sound = "sweeping" + excavation_verb = "brushing" /obj/item/tool/xeno/one_pick name = "2cm pick" diff --git a/code/modules/tools/tool.dm b/code/modules/tools/tool.dm index 889d2ab199d..953a751ebbb 100644 --- a/code/modules/tools/tool.dm +++ b/code/modules/tools/tool.dm @@ -6,7 +6,6 @@ w_class = ITEM_SIZE_NORMAL origin_tech = @'{"materials":1,"engineering":1}' attack_verb = list("hit", "pierced", "sliced", "attacked") - sharp = 0 abstract_type = /obj/item/tool material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC _base_attack_force = 10 diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index edfc8efaba4..1098bd2bf01 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -115,10 +115,10 @@ // physical damage types that can impart force; swinging a bat or energy sword switch(weapon.atom_damage_type) if(BURN) - current_health -= weapon.get_attack_force(user) * fire_dam_coeff + current_health -= weapon.expend_attack_force(user) * fire_dam_coeff . = TRUE if(BRUTE) - current_health -= weapon.get_attack_force(user) * brute_dam_coeff + current_health -= weapon.expend_attack_force(user) * brute_dam_coeff . = TRUE else . = FALSE @@ -144,23 +144,17 @@ healthcheck() /obj/vehicle/emp_act(severity) - var/was_on = on + addtimer(CALLBACK(src, PROC_REF(end_emp), on), severity * 30 SECONDS) stat |= EMPED - var/obj/effect/overlay/pulse2 = new /obj/effect/overlay(loc) - pulse2.icon = 'icons/effects/effects.dmi' - pulse2.icon_state = "empdisable" - pulse2.SetName("emp sparks") - pulse2.anchored = TRUE - pulse2.set_dir(pick(global.cardinal)) - - spawn(10) - qdel(pulse2) + var/obj/effect/temp_visual/emp_burst/burst = new /obj/effect/temp_visual/emp_burst(loc) + burst.set_dir(pick(global.cardinal)) if(on) turn_off() - spawn(severity*300) - stat &= ~EMPED - if(was_on) - turn_on() + +/obj/vehicle/proc/end_emp(was_on) + stat &= ~EMPED + if(was_on) + turn_on() /obj/vehicle/attack_ai(mob/living/silicon/ai/user) return diff --git a/code/modules/weather/weather_fsm_states.dm b/code/modules/weather/weather_fsm_states.dm index 029f500f16f..08c45c46c3e 100644 --- a/code/modules/weather/weather_fsm_states.dm +++ b/code/modules/weather/weather_fsm_states.dm @@ -248,7 +248,8 @@ /decl/state/weather/rain/hail/handle_exposure_effects(var/mob/living/M, var/obj/abstract/weather_system/weather) to_chat(M, SPAN_DANGER("You are pelted by a shower of hail!")) - M.take_damage(rand(1, 3)) + if(M.getBruteLoss() < 20) // Put a cap on it to make it annoying but not lethal. + M.take_damage(rand(1, 3)) /decl/state/weather/ash name = "Ash" diff --git a/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm b/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm index cc79f4bf2b5..abd7dc61c3b 100644 --- a/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm +++ b/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm @@ -1,7 +1,7 @@ /obj/machinery/giga_drill name = "alien drill" desc = "A giant, alien drill mounted on long treads." - icon = 'icons/obj/mining.dmi' + icon = 'icons/obj/machines/gigadrill.dmi' icon_state = "gigadrill" var/active = 0 var/drill_time = 10 diff --git a/code/modules/xenoarcheaology/artifacts/triggers/force.dm b/code/modules/xenoarcheaology/artifacts/triggers/force.dm index c8003f314e7..97a771745f1 100644 --- a/code/modules/xenoarcheaology/artifacts/triggers/force.dm +++ b/code/modules/xenoarcheaology/artifacts/triggers/force.dm @@ -7,7 +7,7 @@ var/obj/item/projectile/hit_projectile = hit_with return (hit_projectile.atom_damage_type == BRUTE) else if(istype(hit_with, /obj/item)) - return (hit_with.get_attack_force(user) >= 10) + return (hit_with.expend_attack_force(user) >= 10) /datum/artifact_trigger/force/on_explosion(severity) return TRUE diff --git a/code/modules/xenoarcheaology/boulder.dm b/code/modules/xenoarcheaology/boulder.dm index ad492eb65ef..ba7a38c2f3d 100644 --- a/code/modules/xenoarcheaology/boulder.dm +++ b/code/modules/xenoarcheaology/boulder.dm @@ -1,8 +1,8 @@ /obj/structure/boulder name = "boulder" desc = "A large boulder, somewhat bigger than a small boulder." - icon = 'icons/obj/mining.dmi' - icon_state = "boulder1" + icon = 'icons/obj/structures/boulder.dmi' + icon_state = ICON_STATE_WORLD density = TRUE opacity = TRUE anchored = TRUE @@ -30,7 +30,7 @@ /obj/structure/boulder/Initialize(var/ml, var/_mat, var/coloration) . = ..() - icon_state = "boulder[rand(1,6)]" + icon_state = "[initial(icon_state)][rand(1,6)]" if(coloration) color = coloration excavation_level = rand(5, 50) diff --git a/code/modules/xenoarcheaology/datums/artifact_find.dm b/code/modules/xenoarcheaology/datums/artifact_find.dm index b70f17112e8..242af56502d 100644 --- a/code/modules/xenoarcheaology/datums/artifact_find.dm +++ b/code/modules/xenoarcheaology/datums/artifact_find.dm @@ -2,8 +2,6 @@ var/artifact_id var/artifact_find_type var/static/potential_finds = list( - /obj/machinery/power/supermatter = 5, - /obj/machinery/power/supermatter/shard = 25, /obj/machinery/auto_cloner = 100, /obj/machinery/giga_drill = 100, /obj/machinery/replicator = 100, diff --git a/code/modules/xenoarcheaology/tools/core_sampler.dm b/code/modules/xenoarcheaology/tools/core_sampler.dm index 01b4f1401e5..21d5bd0cb6d 100644 --- a/code/modules/xenoarcheaology/tools/core_sampler.dm +++ b/code/modules/xenoarcheaology/tools/core_sampler.dm @@ -51,7 +51,7 @@ icon_state = "sliver1" randpixel = 8 w_class = ITEM_SIZE_TINY - sharp = 1 + sharp = TRUE material = /decl/material/solid/stone/sandstone material_health_multiplier = 0.25 diff --git a/code/modules/xenoarcheaology/tools/tools.dm b/code/modules/xenoarcheaology/tools/tools.dm index 213d77ff128..813f424e7f1 100644 --- a/code/modules/xenoarcheaology/tools/tools.dm +++ b/code/modules/xenoarcheaology/tools/tools.dm @@ -10,8 +10,8 @@ /obj/item/bag/fossils name = "fossil satchel" desc = "Transports delicate fossils in suspension so they don't break during transit." - icon = 'icons/obj/mining.dmi' - icon_state = "satchel" + icon = 'icons/obj/items/mining_satchel.dmi' + icon_state = ICON_STATE_WORLD slot_flags = SLOT_LOWER_BODY | SLOT_POCKET w_class = ITEM_SIZE_NORMAL storage = /datum/storage/bag/fossils diff --git a/code/unit_tests/_template.dm b/code/unit_tests/_template.dm index 556941cb1ce..36dc0ddfa18 100644 --- a/code/unit_tests/_template.dm +++ b/code/unit_tests/_template.dm @@ -7,8 +7,8 @@ /datum/unit_test/template name = "Test Template - Change My name" - template = /datum/unit_test/template // Set this var equal to the test path to treat it as a template, i.e. it should not be run - async = 1 // Set if we should continue testing elsewhere and come back and check on the results. + abstract_type = /datum/unit_test/template // Set this var equal to the test path to treat it as a template, i.e. it should not be run + async = 1 // Set if we should continue testing elsewhere and come back and check on the results. /datum/unit_test/template/start_test() diff --git a/code/unit_tests/alt_appearances_test.dm b/code/unit_tests/alt_appearances_test.dm index 1bfd0ed325e..d1c4b22a8e8 100644 --- a/code/unit_tests/alt_appearances_test.dm +++ b/code/unit_tests/alt_appearances_test.dm @@ -20,9 +20,8 @@ for(var/ca_type in decls_repository.get_decl_paths_of_subtype(/decl/cardborg_appearance)) var/decl/cardborg_appearance/ca = ca_type - var/list/existing_icon_states = icon_states(initial(ca.icon)) var/icon_state = initial(ca.icon_state) - if(!(icon_state in existing_icon_states)) + if(!check_state_in_icon(icon_state, initial(ca.icon))) log_unit_test("Icon state [icon_state] is missing.") failed = TRUE if(failed) diff --git a/code/unit_tests/atmospherics_tests.dm b/code/unit_tests/atmospherics_tests.dm index 5a9c69c15cf..46837d818bf 100644 --- a/code/unit_tests/atmospherics_tests.dm +++ b/code/unit_tests/atmospherics_tests.dm @@ -2,7 +2,7 @@ Unit tests for ATMOSPHERICS primitives */ /datum/unit_test/atmos_machinery - template = /datum/unit_test/atmos_machinery + abstract_type = /datum/unit_test/atmos_machinery var/list/test_cases = list() /datum/unit_test/atmos_machinery/proc/create_gas_mixes(gas_mix_data) @@ -60,7 +60,7 @@ pass("[case_name]: conserved moles of each gas ID.") /datum/unit_test/atmos_machinery/conserve_moles - template = /datum/unit_test/atmos_machinery/conserve_moles + abstract_type = /datum/unit_test/atmos_machinery/conserve_moles test_cases = list( uphill = list( source = list( diff --git a/code/unit_tests/chemistry_tests.dm b/code/unit_tests/chemistry_tests.dm index bb6e9d90477..cdc9dae00d4 100644 --- a/code/unit_tests/chemistry_tests.dm +++ b/code/unit_tests/chemistry_tests.dm @@ -1,6 +1,6 @@ /datum/unit_test/chemistry name = "CHEMISTRY: Reagent Template" - template = /datum/unit_test/chemistry + abstract_type = /datum/unit_test/chemistry var/container_volume = 45 var/donor_type = /obj/item diff --git a/code/unit_tests/closets.dm b/code/unit_tests/closets.dm index 9bc236f8ff9..10688fd7231 100644 --- a/code/unit_tests/closets.dm +++ b/code/unit_tests/closets.dm @@ -29,18 +29,18 @@ if(!closet.base_icon) LAZYADD(bad_base_icon, "[closet.type]") else - var/list/base_states = icon_states(closet.base_icon) + var/list/base_states = get_states_in_icon_cached(closet.base_icon) for(var/thing in check_base_states) - if(!(thing in base_states)) + if(!base_states[thing]) LAZYADD(bad_base_state, "[closet.type] - [thing] - [closet.base_icon]") if(LAZYLEN(closet.decals) && !closet.decal_icon) LAZYADD(bad_decal_icon, "[closet.type]") else - var/list/decal_states = icon_states(closet.decal_icon) + var/list/decal_states = get_states_in_icon_cached(closet.decal_icon) for(var/thing in closet.decals) if(isnull(closet.decals[thing])) LAZYADD(bad_decal_colour, "[check_appearance] - [thing]") - if(!(thing in decal_states)) + if(!decal_states[thing]) LAZYADD(bad_decal_state, "[check_appearance] - [thing] - [closet.decal_icon]") if( \ diff --git a/code/unit_tests/clothing.dm b/code/unit_tests/clothing.dm index 2b3889695c3..8219e6d0418 100644 --- a/code/unit_tests/clothing.dm +++ b/code/unit_tests/clothing.dm @@ -99,7 +99,7 @@ generated_tokens += "[token][clothes.markings_state_modifier]" // Keep track of which states we've looked for or otherwise evaluated for later state checking. - var/list/check_states = icon_states(clothes.icon) + var/list/check_states = get_states_in_icon(clothes.icon) // Validate against the list of generated tokens. for(var/gen_token in generated_tokens) diff --git a/code/unit_tests/del_the_world.dm b/code/unit_tests/del_the_world.dm index 3a9c575e699..411ef21b8d3 100644 --- a/code/unit_tests/del_the_world.dm +++ b/code/unit_tests/del_the_world.dm @@ -7,15 +7,6 @@ var/turf/spawn_loc = get_safe_turf() var/list/cached_contents = spawn_loc.contents.Copy() - /// Types to except from GC checking tests. - var/list/gc_exceptions = list( - // I hate doing this, but until the graph tests are fixed by someone who actually understands them, - // this is the best I can do without breaking other stuff. - /datum/node/physical, - // Randomly fails to GC during CI, cause unclear. Remove this if the root cause is identified. - /obj/item/organ/external/chest - ) - var/list/ignore = typesof( // will error if the area already has one /obj/machinery/power/apc, @@ -48,57 +39,52 @@ continue qdel(to_del, force = TRUE) // I hate borg stacks I hate borg stacks AM = null // this counts as a reference to the last item if we don't explicitly clear it?? + del_candidates.Cut() // this also?? // Check for hanging references. SSticker.delay_end = TRUE // Don't end the round while we wait! - // No harddels during this test. - SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = 6 HOURS // github CI timeout length + // Drastically lower the amount of time it takes to GC, since we don't have clients that can hold it up. + SSgarbage.collection_timeout[GC_QUEUE_CHECK] = 10 SECONDS cached_contents.Cut() + var/list/queues_we_care_about = list() + // All of em, I want hard deletes too, since we rely on the debug info from them + for(var/i in 1 to GC_QUEUE_HARDDELETE) + queues_we_care_about += i + + //Now that we've qdel'd everything, let's sleep until the gc has processed all the shit we care about + // + 2 seconds to ensure that everything gets in the queue. + var/time_needed = 2 SECONDS + for(var/index in queues_we_care_about) + time_needed += SSgarbage.collection_timeout[index] + // track start time so we know anything deleted after this point isn't ours var/start_time = world.time - // spin until the first item in the filter queue is older than start_time - var/filter_queue_finished = FALSE - var/list/filter_queue = SSgarbage.queues[GC_QUEUE_FILTER] - while(!filter_queue_finished) - if(!length(filter_queue)) - filter_queue_finished = TRUE - break - var/oldest_item = filter_queue[1] - var/qdel_time = filter_queue[oldest_item] - if(qdel_time > start_time) // Everything is in the check queue now! - filter_queue_finished = TRUE - break - if(world.time > start_time + 2 MINUTES) - fail("Something has gone horribly wrong, the filter queue has been processing for well over 2 minutes. What the hell did you do??") - break - // We want to fire every time. - SSgarbage.next_fire = 1 - sleep(2 SECONDS) - // We need to check the check queue now. - start_time = world.time - // sleep until SSgarbage has run through the queue - var/time_needed = SSgarbage.collection_timeout[GC_QUEUE_CHECK] - sleep(time_needed) - // taken verbatim from TG's Del The World + var/real_start_time = REALTIMEOFDAY var/garbage_queue_processed = FALSE - var/list/check_queue = SSgarbage.queues[GC_QUEUE_CHECK] + + sleep(time_needed) while(!garbage_queue_processed) - //How the hell did you manage to empty this? Good job! - if(!length(check_queue)) - garbage_queue_processed = TRUE - break + var/oldest_packet_creation = INFINITY + for(var/index in queues_we_care_about) + var/list/queue_to_check = SSgarbage.queues[index] + if(!length(queue_to_check)) + continue + + var/list/oldest_packet = queue_to_check[1] + //Pull out the time we inserted at + var/qdeld_at = oldest_packet[GC_QUEUE_ITEM_GCD_DESTROYED] + + oldest_packet_creation = min(qdeld_at, oldest_packet_creation) - var/oldest_packet = check_queue[1] - //Pull out the time we deld at - var/qdeld_at = check_queue[oldest_packet] //If we've found a packet that got del'd later then we finished, then all our shit has been processed - if(qdeld_at > start_time) + //That said, if there are any pending hard deletes you may NOT sleep, we gotta handle that shit + if(oldest_packet_creation > start_time && !length(SSgarbage.queues[GC_QUEUE_HARDDELETE])) garbage_queue_processed = TRUE break - if(world.time > start_time + time_needed + 8 MINUTES) - fail("The garbage queue has been processing for well over 10 minutes. Something is likely broken.") + if(REALTIMEOFDAY > real_start_time + time_needed + 30 MINUTES) //If this gets us gitbanned I'm going to laugh so hard + fail("Something has gone horribly wrong, the garbage queue has been processing for well over 30 minutes. What the hell did you do") break //Immediately fire the gc right after @@ -109,8 +95,6 @@ //Alright, time to see if anything messed up var/list/cache_for_sonic_speed = SSgarbage.items for(var/path in cache_for_sonic_speed) - if(path in gc_exceptions) - continue var/datum/qdel_item/item = cache_for_sonic_speed[path] if(item.failures) failures += "[item.name] hard deleted [item.failures] times out of a total del count of [item.qdels]" diff --git a/code/unit_tests/equipment_tests.dm b/code/unit_tests/equipment_tests.dm index 558debe8c65..dac446856e8 100644 --- a/code/unit_tests/equipment_tests.dm +++ b/code/unit_tests/equipment_tests.dm @@ -1,41 +1,37 @@ /datum/unit_test/vision_glasses name = "EQUIPMENT: Vision Template" - template = /datum/unit_test/vision_glasses - var/mob/living/human/H = null + abstract_type = /datum/unit_test/vision_glasses + var/mob/living/human/subject = null var/expectation = SEE_INVISIBLE_NOLIGHTING var/glasses_type = null async = 1 /datum/unit_test/vision_glasses/start_test() - var/list/test = create_test_mob_with_mind(get_safe_turf(), /mob/living/human) - if(isnull(test)) - fail("Check Runtimed in Mob creation") - - if(test["result"] == FAILURE) - fail(test["msg"]) - async = 0 - return 0 - - H = locate(test["mobref"]) - return H?.equip_to_slot(new glasses_type(H), slot_glasses_str, TRUE, TRUE) + subject = new(get_safe_turf(), SPECIES_HUMAN) // force human so default map species doesn't mess with anything + subject.equip_to_slot(new glasses_type(subject), slot_glasses_str) + return 1 /datum/unit_test/vision_glasses/check_result() - if(isnull(H) || H.life_tick < 2) + if(isnull(subject) || subject.life_tick < 2) return 0 - if(isnull(H.get_equipped_item(slot_glasses_str))) + if(isnull(subject.get_equipped_item(slot_glasses_str))) fail("Mob doesn't have glasses on") - H.handle_vision() // Because Life has a client check that bypasses updating vision + subject.handle_vision() // Because Life has a client check that bypasses updating vision - if(H.see_invisible == expectation) - pass("Mob See invisible is [H.see_invisible]") + if(subject.see_invisible == expectation) + pass("Mob See invisible is [subject.see_invisible]") else - fail("Mob See invisible is [H.see_invisible] / expected [expectation]") + fail("Mob See invisible is [subject.see_invisible] / expected [expectation]") return 1 +/datum/unit_test/vision_glasses/teardown_test() + QDEL_NULL(subject) + . = ..() + /datum/unit_test/vision_glasses/NVG name = "EQUIPMENT: NVG see_invis" glasses_type = /obj/item/clothing/glasses/night diff --git a/code/unit_tests/extension_tests.dm b/code/unit_tests/extension_tests.dm index 49f9769fa43..2a7724e3cc7 100644 --- a/code/unit_tests/extension_tests.dm +++ b/code/unit_tests/extension_tests.dm @@ -1,6 +1,6 @@ /datum/unit_test/extensions name = "EXTENSIONS template" - template = /datum/unit_test/extensions + abstract_type = /datum/unit_test/extensions async = 0 /datum/unit_test/extensions/basic_extension_shall_lazy_initalize_as_expected diff --git a/code/unit_tests/foundation_tests.dm b/code/unit_tests/foundation_tests.dm index 66fb31643b3..27196ec37ff 100644 --- a/code/unit_tests/foundation_tests.dm +++ b/code/unit_tests/foundation_tests.dm @@ -3,7 +3,7 @@ */ /datum/unit_test/foundation name = "FOUNDATION template" - template = /datum/unit_test/foundation + abstract_type = /datum/unit_test/foundation async = 0 /datum/unit_test/foundation/step_shall_return_true_on_success diff --git a/code/unit_tests/graph_tests.dm b/code/unit_tests/graph_tests.dm index 66a939e3a8f..47cdb388f62 100644 --- a/code/unit_tests/graph_tests.dm +++ b/code/unit_tests/graph_tests.dm @@ -430,7 +430,7 @@ * Base Test Setup * ******************/ /datum/unit_test/graph_test - template = /datum/unit_test/graph_test + abstract_type = /datum/unit_test/graph_test async = TRUE var/list/graphs @@ -480,7 +480,7 @@ /atom/movable/graph_test is_spawnable_type = FALSE var/datum/node/physical/node - var/list/neighoursByDirection = list() + var/list/neighboursByDirection = list() /atom/movable/graph_test/Initialize() . = ..() @@ -489,6 +489,7 @@ /atom/movable/graph_test/Destroy() QDEL_NULL(node) + neighboursByDirection.Cut() return ..() /atom/movable/graph_test/forceMove() @@ -497,20 +498,21 @@ /atom/movable/graph_test/proc/Connect(atom/movable/graph_test/neighbour) var/direction = get_dir(src, neighbour) - neighoursByDirection[num2text(direction)] = neighbour - neighbour.neighoursByDirection[num2text(global.flip_dir[direction])] = src + neighboursByDirection[num2text(direction)] = neighbour + neighbour.neighboursByDirection[num2text(global.flip_dir[direction])] = src node.Connect(neighbour.node) /atom/movable/graph_test/CheckNodeNeighbours() // This is a lazy setup for ease of debugging // In a practical setup you'd preferably gather a list of neighbours to be disconnected and pass them in a single Disconnect-call // You'd possibly also verify the dir of this and neighbour nodes, to ensure that they're still facing each other properly - for(var/direction in neighoursByDirection) - var/atom/movable/graph_test/neighbour = neighoursByDirection[direction] + for(var/direction in neighboursByDirection) + var/atom/movable/graph_test/neighbour = neighboursByDirection[direction] var/turf/expected_loc = get_step(src, text2num(direction)) - if(neighbour.loc != expected_loc) + // can't connect in nullspace + if(isnull(neighbour.loc) || neighbour.loc != expected_loc) node.Disconnect(neighbour.node) - neighoursByDirection -= direction + neighboursByDirection -= direction return TRUE /datum/graph/testing @@ -523,7 +525,7 @@ var/on_split_was_called var/issues -/datum/graph/testing/New(var/node, var/edges, var/name) +/datum/graph/testing/New(var/node, var/edges, var/previous_owner, var/name) ..() src.name = name || "Graph" issues = list() @@ -557,8 +559,10 @@ /datum/graph/testing/proc/CheckExpectations() if(on_check_expectations) issues += DoCheckExpectations(on_check_expectations) + QDEL_NULL(on_check_expectations) // stop holding up GC! if(length(split_expectations) && !on_split_was_called) issues += "Had split expectations but OnSplit was not called" + QDEL_LIST(split_expectations) // stop holding up GC! if(!length(split_expectations) && on_split_was_called) issues += "Had no split expectations but OnSplit was called" if(expecting_merge != on_merge_was_called) @@ -575,6 +579,11 @@ src.expected_nodes = expected_nodes || list() src.expected_edges = expected_edges || list() +/datum/graph_expectation/Destroy(force) + expected_nodes.Cut() + expected_edges.Cut() + return ..() + // Stub for subtype-specific functionality for DoCheckExpectations. // Should not access graph.nodes or graph.edges. /datum/graph_expectation/proc/OnCheckExpectations(var/datum/graph/graph) diff --git a/code/unit_tests/icon_tests.dm b/code/unit_tests/icon_tests.dm index 8a25e15a29e..279daba8a34 100644 --- a/code/unit_tests/icon_tests.dm +++ b/code/unit_tests/icon_tests.dm @@ -1,6 +1,6 @@ /datum/unit_test/icon_test name = "ICON STATE template" - template = /datum/unit_test/icon_test + abstract_type = /datum/unit_test/icon_test /datum/unit_test/icon_test/food_shall_have_icon_states name = "ICON STATE: Food And Drink Subtypes Shall Have Icon States" @@ -12,11 +12,14 @@ // We skip lumps because they are invisible, they are only ever inside utensils. var/list/skip_types = list(/obj/item/food/lump) -/datum/unit_test/icon_test/food_shall_have_icon_states/start_test() - +/datum/unit_test/icon_test/food_shall_have_icon_states/proc/assemble_skipped_types() skip_types |= typesof(/obj/item/food/grown) skip_types |= typesof(/obj/item/food/processed_grown) +/datum/unit_test/icon_test/food_shall_have_icon_states/start_test() + + assemble_skipped_types() + var/list/failures = list() for(var/check_type in check_types) for(var/check_subtype in typesof(check_type)) @@ -296,3 +299,64 @@ else pass("All vendors have all icons and icon states.") return 1 + + +/datum/unit_test/HUDS_shall_have_icon_states + name = "ICON STATE: HUD overlays shall have appropriate icon_states" + +/datum/unit_test/HUDS_shall_have_icon_states/start_test() + var/failed_jobs = 0 + var/failed_sanity_checks = 0 + + // Throwing implants and health HUDs in here. + // Antag HUDs are tested by special role validation. + + var/static/list/implant_hud_states = list( + "hud_imp_blank" = "Blank", + "hud_imp_loyal" = "Loyalty", + "hud_imp_unknown" = "Unknown", + "hud_imp_tracking" = "Tracking", + "hud_imp_chem" = "Chemical", + ) + for(var/implant_hud_state in implant_hud_states) + if(!check_state_in_icon(implant_hud_state, global.using_map.implant_hud_icons)) + log_bad("Sanity Check - Missing map [implant_hud_states[implant_hud_state]] implant HUD icon_state '[implant_hud_state]' from icon [global.using_map.implant_hud_icons]") + failed_sanity_checks++ + + var/static/list/med_hud_states = list( + "blank" = "Blank", + "flatline" = "Flatline", + "0" = "Dead", + "1" = "Healthy", + "2" = "Lightly injured", + "3" = "Moderately injured", + "4" = "Severely injured", + "5" = "Dying", + ) + for(var/med_hud_state in med_hud_states) + if(!check_state_in_icon(med_hud_state, global.using_map.med_hud_icons)) + log_bad("Sanity Check - Missing map [med_hud_states[med_hud_state]] medical HUD icon_state '[med_hud_state]' from icon [global.using_map.med_hud_icons]") + failed_sanity_checks++ + var/static/list/global_states = list( + "" = "Default/unnamed", + "hudunknown" = "Unknown role", + "hudhealthy" = "Healthy mob", + "hudill" = "Diseased mob", + "huddead" = "Dead mob" + ) + for(var/global_state in global_states) + if(!check_state_in_icon(global_state, global.using_map.hud_icons)) + log_bad("Sanity Check - Missing map [global_states[global_state]] HUD icon_state '[global_state]' from icon [global.using_map.hud_icons]") + failed_sanity_checks++ + + for(var/job_name in SSjobs.titles_to_datums) + var/datum/job/job = SSjobs.titles_to_datums[job_name] + if(!check_state_in_icon(job.hud_icon_state, job.hud_icon)) + log_bad("[job.title] - Missing HUD icon: [job.hud_icon_state] in icon [job.hud_icon]") + failed_jobs++ + + if(failed_sanity_checks || failed_jobs) + fail("[global.using_map.type] - [failed_sanity_checks] failed sanity check\s, [failed_jobs] job\s with missing HUD icon.") + else + pass("All jobs have a HUD icon.") + return 1 diff --git a/code/unit_tests/integrated_circuits.dm b/code/unit_tests/integrated_circuits.dm index 79c26d52a3a..84de2ac2ff3 100644 --- a/code/unit_tests/integrated_circuits.dm +++ b/code/unit_tests/integrated_circuits.dm @@ -1,5 +1,5 @@ /datum/unit_test/integrated_circuits - template = /datum/unit_test/integrated_circuits + abstract_type = /datum/unit_test/integrated_circuits /datum/unit_test/integrated_circuits/unique_names name = "INTEGRATED CIRCUITS: Circuits must have unique names" @@ -64,7 +64,7 @@ /datum/unit_test/integrated_circuits/input_output name = "INTEGRATED CIRCUITS: INPUT/OUTPUT - TEMPLATE" - template = /datum/unit_test/integrated_circuits/input_output + abstract_type = /datum/unit_test/integrated_circuits/input_output var/list/all_inputs = list() var/list/all_expected_outputs = list() var/activation_pin = 1 diff --git a/code/unit_tests/job_tests.dm b/code/unit_tests/job_tests.dm index b1295165734..b6b652731f8 100644 --- a/code/unit_tests/job_tests.dm +++ b/code/unit_tests/job_tests.dm @@ -35,36 +35,6 @@ pass("All jobs had outfit types.") return 1 -/datum/unit_test/jobs_shall_have_a_HUD_icon - name = "JOB: Shall have a HUD icon" - -/datum/unit_test/jobs_shall_have_a_HUD_icon/start_test() - var/failed_jobs = 0 - var/failed_sanity_checks = 0 - - var/job_huds = icon_states(global.using_map.id_hud_icons) - - if(!("" in job_huds)) - log_bad("Sanity Check - Missing default/unnamed HUD icon") - failed_sanity_checks++ - - if(!("hudunknown" in job_huds)) - log_bad("Sanity Check - Missing HUD icon: hudunknown") - failed_sanity_checks++ - - for(var/job_name in SSjobs.titles_to_datums) - var/datum/job/J = SSjobs.titles_to_datums[job_name] - var/hud_icon_state = J.hud_icon - if(!(hud_icon_state in job_huds)) - log_bad("[J.title] - Missing HUD icon: [hud_icon_state]") - failed_jobs++ - - if(failed_sanity_checks || failed_jobs) - fail("[global.using_map.id_hud_icons] - [failed_sanity_checks] failed sanity check\s, [failed_jobs] job\s with missing HUD icon.") - else - pass("All jobs have a HUD icon.") - return 1 - /datum/unit_test/jobs_shall_have_a_unique_title name = "JOBS: All Job Datums Shall Have A Unique Title" diff --git a/code/unit_tests/map_tests.dm b/code/unit_tests/map_tests.dm index 2803ba8db09..89238fa88d2 100644 --- a/code/unit_tests/map_tests.dm +++ b/code/unit_tests/map_tests.dm @@ -503,6 +503,28 @@ //======================================================================================= +// These vars are used to avoid in-world loops in the following unit test. +var/global/_unit_test_disposal_segments = list() +var/global/_unit_test_sort_junctions = list() + +#ifdef UNIT_TEST +/obj/structure/disposalpipe/segment/Initialize(mapload) + . = ..() + _unit_test_disposal_segments += src + +/obj/structure/disposalpipe/segment/Destroy() + _unit_test_disposal_segments -= src + return ..() + +/obj/structure/disposalpipe/sortjunction/Initialize(mapload) + . = ..() + _unit_test_sort_junctions += src + +/obj/structure/disposalpipe/sortjunction/Destroy() + _unit_test_sort_junctions -= src + return ..() +#endif + /datum/unit_test/disposal_segments_shall_connect_with_other_disposal_pipes name = "MAP: Disposal segments shall connect with other disposal pipes" @@ -522,7 +544,7 @@ num2text(SOUTH) = list(list(SOUTH, list(NORTH, WEST)), list(EAST, list(NORTH, EAST))), num2text(WEST) = list(list(EAST, list(NORTH, EAST)), list(SOUTH, list(SOUTH, EAST)))) - for(var/obj/structure/disposalpipe/segment/D in world) + for(var/obj/structure/disposalpipe/segment/D in _unit_test_disposal_segments) if(!D.loc) continue if(D.icon_state == "pipe-s") @@ -760,7 +782,7 @@ /datum/unit_test/networked_disposals_shall_deliver_tagged_packages/start_test() . = 1 var/fail = FALSE - for(var/obj/structure/disposalpipe/sortjunction/sort in world) + for(var/obj/structure/disposalpipe/sortjunction/sort in _unit_test_sort_junctions) if(!sort.loc) continue if(is_type_in_list(sort, exempt_junctions)) diff --git a/code/unit_tests/mob_tests.dm b/code/unit_tests/mob_tests.dm index 5867dd2ca90..2f03eb9da32 100644 --- a/code/unit_tests/mob_tests.dm +++ b/code/unit_tests/mob_tests.dm @@ -58,21 +58,9 @@ // ============================================================================ -var/global/default_mobloc = null - -/proc/create_test_mob_with_mind(var/turf/mobloc = null, var/mobtype = /mob/living/human) +/datum/unit_test/mob_damage/proc/create_test_mob_with_mind(var/turf/mobloc, var/mobtype = /mob/living/human) var/list/test_result = list("result" = FAILURE, "msg" = "", "mobref" = null) - if(isnull(mobloc)) - if(!default_mobloc) - for(var/turf/floor/tiled/T in world) - if(!T.zone?.air) - continue - var/pressure = T.zone.air.return_pressure() - if(90 < pressure && pressure < 120) // Find a turf between 90 and 120 - default_mobloc = T - break - mobloc = default_mobloc if(!mobloc) test_result["msg"] = "Unable to find a location to create test mob" return test_result @@ -129,7 +117,7 @@ var/global/default_mobloc = null /datum/unit_test/mob_damage name = "MOB: Template for mob damage" - template = /datum/unit_test/mob_damage + abstract_type = /datum/unit_test/mob_damage var/damagetype = BRUTE var/mob_type = /mob/living/human var/expected_vulnerability = STANDARD @@ -260,15 +248,15 @@ var/global/default_mobloc = null fail("[icon_file] is not a valid icon file.") return 1 - var/list/valid_states = icon_states(icon_file) + var/list/valid_states = get_states_in_icon_cached(icon_file) - if(!valid_states.len) + if(!length(valid_states)) return 1 for(var/i=1, i<=SSrobots.all_module_names.len, i++) var/modname = lowertext(SSrobots.all_module_names[i]) var/bad_msg = "[ascii_red]--------------- [modname]" - if(!(modname in valid_states)) + if(!valid_states[modname]) log_unit_test("[bad_msg] does not contain a valid icon state in [icon_file][ascii_reset]") failed=1 diff --git a/code/unit_tests/movement_tests.dm b/code/unit_tests/movement_tests.dm index 8d45dbe20cf..dbcb2bc4499 100644 --- a/code/unit_tests/movement_tests.dm +++ b/code/unit_tests/movement_tests.dm @@ -1,6 +1,6 @@ /datum/unit_test/movement name = "MOVEMENT template" - template = /datum/unit_test/movement + abstract_type = /datum/unit_test/movement async = 0 /datum/unit_test/movement/force_move_shall_trigger_crossed_when_entering_turf diff --git a/code/unit_tests/observation_tests.dm b/code/unit_tests/observation_tests.dm index b9174d26fe8..829bc743576 100644 --- a/code/unit_tests/observation_tests.dm +++ b/code/unit_tests/observation_tests.dm @@ -4,7 +4,7 @@ /datum/unit_test/observation name = "OBSERVATION template" - template = /datum/unit_test/observation + abstract_type = /datum/unit_test/observation async = 0 var/list/received_moves var/list/received_name_set_events diff --git a/code/unit_tests/override_tests.dm b/code/unit_tests/override_tests.dm index 2c8d6bc47cc..4687b0c72aa 100644 --- a/code/unit_tests/override_tests.dm +++ b/code/unit_tests/override_tests.dm @@ -2,7 +2,7 @@ /datum/unit_test/override name = "OVERRIDE template" - template = /datum/unit_test/override + abstract_type = /datum/unit_test/override /datum/unit_test/override/obj_random_shall_spawn_heaviest_item name = "OVERRIDE: obj/random shall spawn heaviest item" diff --git a/code/unit_tests/proximity_tests.dm b/code/unit_tests/proximity_tests.dm index b77946ec739..b405a2c3db2 100644 --- a/code/unit_tests/proximity_tests.dm +++ b/code/unit_tests/proximity_tests.dm @@ -2,7 +2,7 @@ * Template Setup * *****************/ /datum/unit_test/proximity - template = /datum/unit_test/proximity + abstract_type = /datum/unit_test/proximity var/turf/wall/wall var/obj/proximity_listener/proximity_listener @@ -24,7 +24,7 @@ wall.set_opacity(opacity) /datum/unit_test/proximity/visibility - template = /datum/unit_test/proximity/visibility + abstract_type = /datum/unit_test/proximity/visibility var/list/expected_number_of_turfs_by_trigger_type /datum/unit_test/proximity/visibility/start_test() diff --git a/code/unit_tests/time_tests.dm b/code/unit_tests/time_tests.dm index 2dbf636d1e9..55a17518a60 100644 --- a/code/unit_tests/time_tests.dm +++ b/code/unit_tests/time_tests.dm @@ -1,6 +1,6 @@ /datum/unit_test/time name = "TIME: Template" - template = /datum/unit_test/time + abstract_type = /datum/unit_test/time /datum/unit_test/time/shall_validate_sixth_of_june name = "TIME: Shall validate 6th of June" diff --git a/code/unit_tests/turf_icons.dm b/code/unit_tests/turf_icons.dm index 5cf5cb9dce7..3319864ee2a 100644 --- a/code/unit_tests/turf_icons.dm +++ b/code/unit_tests/turf_icons.dm @@ -8,8 +8,7 @@ /turf/unsimulated ) var/list/excepted_types = list( - /turf/unsimulated/map, - /turf/unsimulated/wall/cascade + /turf/unsimulated/map ) /datum/unit_test/turf_floor_icons_shall_be_valid/setup_test() diff --git a/code/unit_tests/unit_test.dm b/code/unit_tests/unit_test.dm index 4fe4fedfdc5..24fd800c854 100644 --- a/code/unit_tests/unit_test.dm +++ b/code/unit_tests/unit_test.dm @@ -4,7 +4,7 @@ * For the most part I think any test can be created that doesn't require a client in a mob or require a game mode other then extended * * The easiest way to make effective tests is to create a "template" if you intend to run the same test over and over and make your actual - * tests be a "child object" of those templates. Be sure and name your templates with the word "template" somewhere in var/name. + * tests be a "child object" of those templates. Be sure to set abstract_type on your template type. * * The goal is to have all sorts of tests that run and to run them as quickly as possible. * @@ -51,8 +51,8 @@ var/global/ascii_reset = "[ascii_esc]\[0m" // Templates aren't intended to be ran but just serve as a way to create child objects of it with inheritable tests for quick test creation. /datum/unit_test + abstract_type = /datum/unit_test var/name = "template - should not be ran." - var/template // Treat the unit test as a template if its type is the same as the value of this var var/disabled = 0 // If we want to keep a unit test in the codebase but not run it for some reason. var/async = 0 // If the check can be left to do it's own thing, you must define a check_result() proc if you use this. var/reported = 0 // If it's reported a success or failure. Any tests that have not are assumed to be failures. @@ -157,11 +157,10 @@ var/global/ascii_reset = "[ascii_esc]\[0m" /proc/get_test_datums() . = list() - for(var/test in subtypesof(/datum/unit_test)) - var/datum/unit_test/d = test - if(test == initial(d.template)) + for(var/datum/unit_test/test as anything in subtypesof(/datum/unit_test)) + if(TYPE_IS_ABSTRACT(test)) continue - . += d + . += test /proc/do_unit_test(datum/unit_test/test, end_time, skip_disabled_tests = TRUE) if(test.disabled && skip_disabled_tests) diff --git a/code/unit_tests/virtual_mob_tests.dm b/code/unit_tests/virtual_mob_tests.dm index dab89f395ec..718bf010748 100644 --- a/code/unit_tests/virtual_mob_tests.dm +++ b/code/unit_tests/virtual_mob_tests.dm @@ -1,10 +1,10 @@ /datum/unit_test/virtual name = "VIRTUAL: Template" - template = /datum/unit_test/virtual + abstract_type = /datum/unit_test/virtual /datum/unit_test/virtual/helper name = "VIRTUAL: Template Helper" - template = /datum/unit_test/virtual/helper + abstract_type = /datum/unit_test/virtual/helper var/helper_proc var/list/expected_mobs diff --git a/code/unit_tests/zas_tests.dm b/code/unit_tests/zas_tests.dm index b83f880bff9..7866c78bd62 100644 --- a/code/unit_tests/zas_tests.dm +++ b/code/unit_tests/zas_tests.dm @@ -12,7 +12,7 @@ /datum/unit_test/zas_area_test name = "ZAS: Area Test Template" - template = /datum/unit_test/zas_area_test + abstract_type = /datum/unit_test/zas_area_test var/area_path = null // Put the area you are testing here. var/expectation = UT_NORMAL // See defines above. diff --git a/code/unit_tests/~unit_test_subsystems.dm b/code/unit_tests/~unit_test_subsystems.dm index 23d8aab1d0b..eea8a75f692 100644 --- a/code/unit_tests/~unit_test_subsystems.dm +++ b/code/unit_tests/~unit_test_subsystems.dm @@ -31,6 +31,12 @@ SUBSYSTEM_DEF(unit_tests) #endif log_unit_test("Initializing Unit Testing") + // Misc validation. + if(istype(global.using_map)) + global.using_map.validate() + else + log_error("global.using_map is null or invalid!") + // //Start the Round. // diff --git a/code/world.dm b/code/world.dm index 361fa6c4a15..057199cfb0a 100644 --- a/code/world.dm +++ b/code/world.dm @@ -12,6 +12,9 @@ hub = "Exadv1.spacestation13" icon_size = WORLD_ICON_SIZE fps = 20 -#ifdef GC_FAILURE_HARD_LOOKUP +#ifdef FIND_REF_NO_CHECK_TICK +#pragma push +#pragma ignore loop_checks loop_checks = FALSE +#pragma pop #endif diff --git a/html/changelog.html b/html/changelog.html index 1b80c1d0545..8e46ecdab25 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,45 @@ -->
    +

    22 January 2025

    +

    MistakeNot4892 updated:

    +
      +
    • Swords on Shaded Hills can be sharpened using whetstones or grindstones.
    • +
    • Guns can now be fired from rigs regardless of dexterity.
    • +
    + +

    21 January 2025

    +

    Penelope Haze updated:

    +
      +
    • Mud can now receive footprints with any reagent other than mud. No more conspicuously missing bloody footprints!
    • +
    + +

    19 January 2025

    +

    MistakeNot4892 updated:

    +
      +
    • Intents have been rewritten and moved, please report any issues with intent selection.
    • +
    + +

    16 January 2025

    +

    MistakeNot4892 updated:

    +
      +
    • The White and Minimalist HUD styles can now be customised to use a specific overlay color.
    • +
    • The Shaded Hills river now flows north.
    • +
    • Snow, mud and sand will now show footprints.
    • +
    + +

    15 January 2025

    +

    MistakeNot4892 updated:

    +
      +
    • The way you interact with barrels and well has been significantly reworked; clicking with a bucket or tool should give a list of options to pick from. Please report bugs with this on the tracker.
    • +
    + +

    08 January 2025

    +

    MistakeNot4892 updated:

    +
      +
    • Mud and blood can now leave footprints.
    • +
    +

    03 January 2025

    MistakeNot4892 updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 894f308ccdb..20e1bd55f28 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14945,3 +14945,29 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. MistakeNot4892: - tweak: Raw plant oil no longer works as lantern fuel; it must be mixed with powdered graphite first. +2025-01-08: + MistakeNot4892: + - tweak: Mud and blood can now leave footprints. +2025-01-15: + MistakeNot4892: + - tweak: The way you interact with barrels and well has been significantly reworked; + clicking with a bucket or tool should give a list of options to pick from. Please + report bugs with this on the tracker. +2025-01-16: + MistakeNot4892: + - tweak: The White and Minimalist HUD styles can now be customised to use a specific + overlay color. + - tweak: The Shaded Hills river now flows north. + - tweak: Snow, mud and sand will now show footprints. +2025-01-19: + MistakeNot4892: + - tweak: Intents have been rewritten and moved, please report any issues with intent + selection. +2025-01-21: + Penelope Haze: + - tweak: Mud can now receive footprints with any reagent other than mud. No more + conspicuously missing bloody footprints! +2025-01-22: + MistakeNot4892: + - tweak: Swords on Shaded Hills can be sharpened using whetstones or grindstones. + - tweak: Guns can now be fired from rigs regardless of dexterity. diff --git a/html/changelogs/AutoChangeLog-pr-4695.yml b/html/changelogs/AutoChangeLog-pr-4695.yml deleted file mode 100644 index 84dde3ff37c..00000000000 --- a/html/changelogs/AutoChangeLog-pr-4695.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: MistakeNot4892 -changes: - - {tweak: Mud and blood can now leave footprints.} -delete-after: true diff --git a/icons/clothing/spacesuit/void/medical_alt/suit.dmi b/icons/clothing/spacesuit/void/medical_alt/suit.dmi index 77b79193e46..634b6742079 100644 Binary files a/icons/clothing/spacesuit/void/medical_alt/suit.dmi and b/icons/clothing/spacesuit/void/medical_alt/suit.dmi differ diff --git a/icons/effects/crayondecal.dmi b/icons/effects/crayondecal.dmi index e1966e9bb5a..3642e8ed8f4 100644 Binary files a/icons/effects/crayondecal.dmi and b/icons/effects/crayondecal.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 78c4315424a..5502343f0d5 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/landmarks.dmi b/icons/effects/landmarks.dmi index 7c5729455bf..28317726409 100644 Binary files a/icons/effects/landmarks.dmi and b/icons/effects/landmarks.dmi differ diff --git a/icons/effects/liquids.dmi b/icons/effects/liquids.dmi index 965e319bc4a..7cdf5d1b006 100644 Binary files a/icons/effects/liquids.dmi and b/icons/effects/liquids.dmi differ diff --git a/icons/effects/projectiles/trail.dmi b/icons/effects/projectiles/trail.dmi new file mode 100644 index 00000000000..4e058a3f1bf Binary files /dev/null and b/icons/effects/projectiles/trail.dmi differ diff --git a/icons/mob/footprints/footprints.dmi b/icons/mob/footprints/footprints.dmi new file mode 100644 index 00000000000..2d9699e82c8 Binary files /dev/null and b/icons/mob/footprints/footprints.dmi differ diff --git a/icons/mob/footprints/footprints_paw.dmi b/icons/mob/footprints/footprints_paw.dmi new file mode 100644 index 00000000000..08fac62a016 Binary files /dev/null and b/icons/mob/footprints/footprints_paw.dmi differ diff --git a/icons/mob/footprints/footprints_snake.dmi b/icons/mob/footprints/footprints_snake.dmi new file mode 100644 index 00000000000..6e913803eb1 Binary files /dev/null and b/icons/mob/footprints/footprints_snake.dmi differ diff --git a/icons/mob/footprints/footprints_trail.dmi b/icons/mob/footprints/footprints_trail.dmi new file mode 100644 index 00000000000..944ed4e175b Binary files /dev/null and b/icons/mob/footprints/footprints_trail.dmi differ diff --git a/icons/mob/footprints/footprints_wheelchair.dmi b/icons/mob/footprints/footprints_wheelchair.dmi new file mode 100644 index 00000000000..4a2e800cf76 Binary files /dev/null and b/icons/mob/footprints/footprints_wheelchair.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi deleted file mode 100644 index ed608471041..00000000000 Binary files a/icons/mob/hud.dmi and /dev/null differ diff --git a/icons/mob/hud_med.dmi b/icons/mob/hud_med.dmi deleted file mode 100644 index 705886a5490..00000000000 Binary files a/icons/mob/hud_med.dmi and /dev/null differ diff --git a/icons/mob/screen/abilities.dmi b/icons/mob/screen/abilities.dmi new file mode 100644 index 00000000000..ca7d904bd71 Binary files /dev/null and b/icons/mob/screen/abilities.dmi differ diff --git a/icons/mob/screen/ability_inhand.dmi b/icons/mob/screen/ability_inhand.dmi new file mode 100644 index 00000000000..63e1b630dc0 Binary files /dev/null and b/icons/mob/screen/ability_inhand.dmi differ diff --git a/icons/mob/screen/phenomena.dmi b/icons/mob/screen/phenomena.dmi deleted file mode 100644 index f1d05fa9730..00000000000 Binary files a/icons/mob/screen/phenomena.dmi and /dev/null differ diff --git a/icons/mob/screen/spells.dmi b/icons/mob/screen/spells.dmi deleted file mode 100644 index d574921544d..00000000000 Binary files a/icons/mob/screen/spells.dmi and /dev/null differ diff --git a/icons/mob/screen/styles/intents.dmi b/icons/mob/screen/styles/intents.dmi deleted file mode 100644 index 3b0b33f8490..00000000000 Binary files a/icons/mob/screen/styles/intents.dmi and /dev/null differ diff --git a/icons/mob/screen/styles/intents_wide.dmi b/icons/mob/screen/styles/intents_wide.dmi deleted file mode 100644 index fc26d305f6b..00000000000 Binary files a/icons/mob/screen/styles/intents_wide.dmi and /dev/null differ diff --git a/icons/mob/screen/styles/minimalist/attack_selector.dmi b/icons/mob/screen/styles/minimalist/attack_selector.dmi index c5a7e57c522..2e3d66fd059 100644 Binary files a/icons/mob/screen/styles/minimalist/attack_selector.dmi and b/icons/mob/screen/styles/minimalist/attack_selector.dmi differ diff --git a/icons/mob/screen/styles/minimalist/fire_intent.dmi b/icons/mob/screen/styles/minimalist/fire_intent.dmi index 62efd8ca867..02a311ec4e8 100644 Binary files a/icons/mob/screen/styles/minimalist/fire_intent.dmi and b/icons/mob/screen/styles/minimalist/fire_intent.dmi differ diff --git a/icons/mob/screen/styles/minimalist/hands.dmi b/icons/mob/screen/styles/minimalist/hands.dmi index 28264b0635f..05840e72af8 100644 Binary files a/icons/mob/screen/styles/minimalist/hands.dmi and b/icons/mob/screen/styles/minimalist/hands.dmi differ diff --git a/icons/mob/screen/styles/minimalist/interaction.dmi b/icons/mob/screen/styles/minimalist/interaction.dmi index 8a4875e964f..b8c4a0b80fe 100644 Binary files a/icons/mob/screen/styles/minimalist/interaction.dmi and b/icons/mob/screen/styles/minimalist/interaction.dmi differ diff --git a/icons/mob/screen/styles/minimalist/inventory.dmi b/icons/mob/screen/styles/minimalist/inventory.dmi index d2d055fe753..169c66eefdd 100644 Binary files a/icons/mob/screen/styles/minimalist/inventory.dmi and b/icons/mob/screen/styles/minimalist/inventory.dmi differ diff --git a/icons/mob/screen/styles/minimalist/movement.dmi b/icons/mob/screen/styles/minimalist/movement.dmi index 3a3115d5b88..fa354873156 100644 Binary files a/icons/mob/screen/styles/minimalist/movement.dmi and b/icons/mob/screen/styles/minimalist/movement.dmi differ diff --git a/icons/mob/screen/styles/minimalist/uphint.dmi b/icons/mob/screen/styles/minimalist/uphint.dmi index fc54e68b933..412cab2b32d 100644 Binary files a/icons/mob/screen/styles/minimalist/uphint.dmi and b/icons/mob/screen/styles/minimalist/uphint.dmi differ diff --git a/icons/mob/screen/styles/minimalist/zone_selector.dmi b/icons/mob/screen/styles/minimalist/zone_selector.dmi index 9a35a9acd27..2cdd0893f6a 100644 Binary files a/icons/mob/screen/styles/minimalist/zone_selector.dmi and b/icons/mob/screen/styles/minimalist/zone_selector.dmi differ diff --git a/icons/mob/screen/styles/underworld/attack_selector.dmi b/icons/mob/screen/styles/underworld/attack_selector.dmi new file mode 100644 index 00000000000..2df7a24c30d Binary files /dev/null and b/icons/mob/screen/styles/underworld/attack_selector.dmi differ diff --git a/icons/mob/screen/styles/underworld/fire_intent.dmi b/icons/mob/screen/styles/underworld/fire_intent.dmi new file mode 100644 index 00000000000..1f7c93487fd Binary files /dev/null and b/icons/mob/screen/styles/underworld/fire_intent.dmi differ diff --git a/icons/mob/screen/styles/underworld/hands.dmi b/icons/mob/screen/styles/underworld/hands.dmi new file mode 100644 index 00000000000..fd37369cc08 Binary files /dev/null and b/icons/mob/screen/styles/underworld/hands.dmi differ diff --git a/icons/mob/screen/styles/underworld/interaction.dmi b/icons/mob/screen/styles/underworld/interaction.dmi new file mode 100644 index 00000000000..ce806127f53 Binary files /dev/null and b/icons/mob/screen/styles/underworld/interaction.dmi differ diff --git a/icons/mob/screen/styles/underworld/inventory.dmi b/icons/mob/screen/styles/underworld/inventory.dmi new file mode 100644 index 00000000000..b576bd3b4df Binary files /dev/null and b/icons/mob/screen/styles/underworld/inventory.dmi differ diff --git a/icons/mob/screen/styles/underworld/movement.dmi b/icons/mob/screen/styles/underworld/movement.dmi new file mode 100644 index 00000000000..6890efd9fa4 Binary files /dev/null and b/icons/mob/screen/styles/underworld/movement.dmi differ diff --git a/icons/mob/screen/styles/underworld/uphint.dmi b/icons/mob/screen/styles/underworld/uphint.dmi new file mode 100644 index 00000000000..4d78c55da72 Binary files /dev/null and b/icons/mob/screen/styles/underworld/uphint.dmi differ diff --git a/icons/mob/screen/styles/underworld/zone_selector.dmi b/icons/mob/screen/styles/underworld/zone_selector.dmi new file mode 100644 index 00000000000..e482039b230 Binary files /dev/null and b/icons/mob/screen/styles/underworld/zone_selector.dmi differ diff --git a/icons/mob/screen/styles/white/attack_selector.dmi b/icons/mob/screen/styles/white/attack_selector.dmi index 6aa6118a743..02f97413e1a 100644 Binary files a/icons/mob/screen/styles/white/attack_selector.dmi and b/icons/mob/screen/styles/white/attack_selector.dmi differ diff --git a/icons/mob/screen/styles/white/fire_intent.dmi b/icons/mob/screen/styles/white/fire_intent.dmi index 5ab217edcd5..64af8d1e078 100644 Binary files a/icons/mob/screen/styles/white/fire_intent.dmi and b/icons/mob/screen/styles/white/fire_intent.dmi differ diff --git a/icons/mob/screen/styles/white/hands.dmi b/icons/mob/screen/styles/white/hands.dmi index 59702d43b99..0f65b990467 100644 Binary files a/icons/mob/screen/styles/white/hands.dmi and b/icons/mob/screen/styles/white/hands.dmi differ diff --git a/icons/mob/screen/styles/white/interaction.dmi b/icons/mob/screen/styles/white/interaction.dmi index e11d10d2c13..8511d73937a 100644 Binary files a/icons/mob/screen/styles/white/interaction.dmi and b/icons/mob/screen/styles/white/interaction.dmi differ diff --git a/icons/mob/screen/styles/white/inventory.dmi b/icons/mob/screen/styles/white/inventory.dmi index 0ce80198e6b..f8cfa9cbbc5 100644 Binary files a/icons/mob/screen/styles/white/inventory.dmi and b/icons/mob/screen/styles/white/inventory.dmi differ diff --git a/icons/mob/screen/styles/white/movement.dmi b/icons/mob/screen/styles/white/movement.dmi index 17faaf9413d..b3a61c63a9b 100644 Binary files a/icons/mob/screen/styles/white/movement.dmi and b/icons/mob/screen/styles/white/movement.dmi differ diff --git a/icons/mob/screen/styles/white/uphint.dmi b/icons/mob/screen/styles/white/uphint.dmi index d3e9075d704..ee795947c30 100644 Binary files a/icons/mob/screen/styles/white/uphint.dmi and b/icons/mob/screen/styles/white/uphint.dmi differ diff --git a/icons/mob/screen/styles/white/zone_selector.dmi b/icons/mob/screen/styles/white/zone_selector.dmi index fe86e824f98..1e768fdd53d 100644 Binary files a/icons/mob/screen/styles/white/zone_selector.dmi and b/icons/mob/screen/styles/white/zone_selector.dmi differ diff --git a/icons/obj/items/chain.dmi b/icons/obj/items/chain.dmi new file mode 100644 index 00000000000..95d5c71b70f Binary files /dev/null and b/icons/obj/items/chain.dmi differ diff --git a/icons/obj/items/hook.dmi b/icons/obj/items/hook.dmi new file mode 100644 index 00000000000..602683208c7 Binary files /dev/null and b/icons/obj/items/hook.dmi differ diff --git a/icons/obj/items/horseshoe.dmi b/icons/obj/items/horseshoe.dmi new file mode 100644 index 00000000000..15f6f5f2482 Binary files /dev/null and b/icons/obj/items/horseshoe.dmi differ diff --git a/icons/obj/items/mining_satchel.dmi b/icons/obj/items/mining_satchel.dmi new file mode 100644 index 00000000000..64d0ded9257 Binary files /dev/null and b/icons/obj/items/mining_satchel.dmi differ diff --git a/icons/obj/items/sheet_snatcher.dmi b/icons/obj/items/sheet_snatcher.dmi new file mode 100644 index 00000000000..7a898ac6f09 Binary files /dev/null and b/icons/obj/items/sheet_snatcher.dmi differ diff --git a/icons/obj/items/shield/buckler.dmi b/icons/obj/items/shield/buckler.dmi deleted file mode 100644 index ff53a0e14e0..00000000000 Binary files a/icons/obj/items/shield/buckler.dmi and /dev/null differ diff --git a/icons/obj/items/shield/buckler_base_metal.dmi b/icons/obj/items/shield/buckler_base_metal.dmi new file mode 100644 index 00000000000..41372efa755 Binary files /dev/null and b/icons/obj/items/shield/buckler_base_metal.dmi differ diff --git a/icons/obj/items/shield/buckler_base_wood.dmi b/icons/obj/items/shield/buckler_base_wood.dmi new file mode 100644 index 00000000000..03e9bc34a6d Binary files /dev/null and b/icons/obj/items/shield/buckler_base_wood.dmi differ diff --git a/icons/obj/items/shield/buckler_metal.dmi b/icons/obj/items/shield/buckler_metal.dmi new file mode 100644 index 00000000000..0d27f07c23e Binary files /dev/null and b/icons/obj/items/shield/buckler_metal.dmi differ diff --git a/icons/obj/items/shield/buckler_wood.dmi b/icons/obj/items/shield/buckler_wood.dmi new file mode 100644 index 00000000000..94e4563a342 Binary files /dev/null and b/icons/obj/items/shield/buckler_wood.dmi differ diff --git a/icons/obj/items/shield_fasteners.dmi b/icons/obj/items/shield_fasteners.dmi new file mode 100644 index 00000000000..e4c289ffc0c Binary files /dev/null and b/icons/obj/items/shield_fasteners.dmi differ diff --git a/icons/obj/items/tool/chisel.dmi b/icons/obj/items/tool/chisel.dmi new file mode 100644 index 00000000000..e277e1ba44b Binary files /dev/null and b/icons/obj/items/tool/chisel.dmi differ diff --git a/icons/obj/items/tool/components/tool_head.dmi b/icons/obj/items/tool/components/tool_head.dmi index dbb7e7db2bc..cc9a43d9971 100644 Binary files a/icons/obj/items/tool/components/tool_head.dmi and b/icons/obj/items/tool/components/tool_head.dmi differ diff --git a/icons/obj/items/tool/hammers/forge.dmi b/icons/obj/items/tool/hammers/forge.dmi new file mode 100644 index 00000000000..a7b0a4e0611 Binary files /dev/null and b/icons/obj/items/tool/hammers/forge.dmi differ diff --git a/icons/obj/machines/gigadrill.dmi b/icons/obj/machines/gigadrill.dmi new file mode 100644 index 00000000000..bcbda6a5144 Binary files /dev/null and b/icons/obj/machines/gigadrill.dmi differ diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi deleted file mode 100644 index 0bf325c1efb..00000000000 Binary files a/icons/obj/mining.dmi and /dev/null differ diff --git a/icons/obj/structures/boulder.dmi b/icons/obj/structures/boulder.dmi new file mode 100644 index 00000000000..dc83a302239 Binary files /dev/null and b/icons/obj/structures/boulder.dmi differ diff --git a/icons/obj/structures/forging/bellows.dmi b/icons/obj/structures/forging/bellows.dmi new file mode 100644 index 00000000000..9e6031422ef Binary files /dev/null and b/icons/obj/structures/forging/bellows.dmi differ diff --git a/icons/obj/structures/ore_box.dmi b/icons/obj/structures/ore_box.dmi new file mode 100644 index 00000000000..c9eea92cfd2 Binary files /dev/null and b/icons/obj/structures/ore_box.dmi differ diff --git a/icons/obj/structures/snowmen/snowbot.dmi b/icons/obj/structures/snowmen/snowbot.dmi new file mode 100644 index 00000000000..c209cf5e868 Binary files /dev/null and b/icons/obj/structures/snowmen/snowbot.dmi differ diff --git a/icons/obj/structures/snowmen/snowman.dmi b/icons/obj/structures/snowmen/snowman.dmi new file mode 100644 index 00000000000..c174da4bb4d Binary files /dev/null and b/icons/obj/structures/snowmen/snowman.dmi differ diff --git a/icons/obj/structures/snowmen/snowspider.dmi b/icons/obj/structures/snowmen/snowspider.dmi new file mode 100644 index 00000000000..bc7af6f5b8c Binary files /dev/null and b/icons/obj/structures/snowmen/snowspider.dmi differ diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi index 49d879d47d8..b8ebfc01108 100644 Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ diff --git a/icons/screen/hud.dmi b/icons/screen/hud.dmi new file mode 100644 index 00000000000..b8d98129369 Binary files /dev/null and b/icons/screen/hud.dmi differ diff --git a/icons/screen/hud_antag.dmi b/icons/screen/hud_antag.dmi new file mode 100644 index 00000000000..07840ff2614 Binary files /dev/null and b/icons/screen/hud_antag.dmi differ diff --git a/icons/screen/hud_implants.dmi b/icons/screen/hud_implants.dmi new file mode 100644 index 00000000000..5899f696f70 Binary files /dev/null and b/icons/screen/hud_implants.dmi differ diff --git a/icons/screen/hud_med.dmi b/icons/screen/hud_med.dmi new file mode 100644 index 00000000000..280097c8166 Binary files /dev/null and b/icons/screen/hud_med.dmi differ diff --git a/icons/screen/intents.dmi b/icons/screen/intents.dmi index 23e147992e0..3e7992ed2c9 100644 Binary files a/icons/screen/intents.dmi and b/icons/screen/intents.dmi differ diff --git a/icons/screen/intents_wide.dmi b/icons/screen/intents_wide.dmi index 9f210bc1c2d..a89c5ee023d 100644 Binary files a/icons/screen/intents_wide.dmi and b/icons/screen/intents_wide.dmi differ diff --git a/maps/antag_spawn/ert/ert_base.dmm b/maps/antag_spawn/ert/ert_base.dmm index e105bb2c1a8..703d909d0c3 100644 --- a/maps/antag_spawn/ert/ert_base.dmm +++ b/maps/antag_spawn/ert/ert_base.dmm @@ -1195,10 +1195,6 @@ /obj/machinery/vending/medical, /turf/unsimulated/wall, /area/map_template/rescue_base/base) -"dj" = ( -/obj/effect/wingrille_spawn/reinforced/crescent, -/turf/space, -/area/map_template/rescue_base/base) "dk" = ( /obj/machinery/door/airlock/centcom{ name = "EVA" @@ -4269,7 +4265,7 @@ ar cH ar cS -dj +bg ao ao bg @@ -4334,7 +4330,7 @@ ar cI ar cT -dj +bg ao ao bg @@ -4399,7 +4395,7 @@ ar ar ar cU -dj +bg ao ao bg @@ -4464,7 +4460,7 @@ cy cJ cO cV -dj +bg ao ao ad diff --git a/maps/away/errant_pisces/errant_pisces.dm b/maps/away/errant_pisces/errant_pisces.dm index b5079186ecc..ba006dddf06 100644 --- a/maps/away/errant_pisces/errant_pisces.dm +++ b/maps/away/errant_pisces/errant_pisces.dm @@ -112,9 +112,9 @@ return SPAN_NOTICE("A few strands of \the [src] have been severed.") /obj/structure/net/attackby(obj/item/W, mob/user) - if(W.sharp || W.edge) - var/force = W.get_attack_force(user) - if (!(W.sharp) || (W.sharp && force < 10))//is not sharp enough or at all + if(W.is_sharp() || W.has_edge()) + var/force = W.expend_attack_force(user) + if (!(W.is_sharp()) || (W.is_sharp() && force < 10))//is not sharp enough or at all to_chat(user,"You can't cut through \the [src] with \the [W], it's too dull.") return TRUE visible_message("[user] starts to cut through \the [src] with \the [W]!") diff --git a/maps/away/liberia/hud.dmi b/maps/away/liberia/hud.dmi new file mode 100644 index 00000000000..01fe4cbfcd4 Binary files /dev/null and b/maps/away/liberia/hud.dmi differ diff --git a/maps/away/liberia/liberia_jobs.dm b/maps/away/liberia/liberia_jobs.dm index 4d3b627143f..34269ce2cd3 100644 --- a/maps/away/liberia/liberia_jobs.dm +++ b/maps/away/liberia/liberia_jobs.dm @@ -11,7 +11,8 @@ info = "You are free traders who have drifted into unknown distances in search of profit. Travel, trade, make profit!" supervisors = "the invisible hand of the market" selection_color = "#515151" - + hud_icon = 'maps/away/liberia/hud.dmi' + hud_icon_state = "hudmerchant" ideal_character_age = 20 minimal_player_age = 7 diff --git a/maps/away/lost_supply_base/lost_supply_base.dmm b/maps/away/lost_supply_base/lost_supply_base.dmm index aac57b9b881..55d86201533 100644 --- a/maps/away/lost_supply_base/lost_supply_base.dmm +++ b/maps/away/lost_supply_base/lost_supply_base.dmm @@ -1982,7 +1982,7 @@ /turf/unsimulated/mask, /area/mine/unexplored) "gu" = ( -/turf/floor, +/turf/floor/barren, /area/mine/explored) "ib" = ( /obj/structure/hygiene/sink{ diff --git a/maps/away/mining/mining-asteroid.dmm b/maps/away/mining/mining-asteroid.dmm index 63713ba4434..89a0612365c 100644 --- a/maps/away/mining/mining-asteroid.dmm +++ b/maps/away/mining/mining-asteroid.dmm @@ -6,14 +6,14 @@ /turf/unsimulated/mask, /area/mine/unexplored) "af" = ( -/turf/floor, +/turf/floor/barren, /area/mine/explored) "aj" = ( /turf/wall/r_wall, /area/djstation) "ak" = ( /obj/random/junk, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "al" = ( /obj/random/trash, @@ -204,15 +204,15 @@ /area/djstation) "be" = ( /obj/random/maintenance, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "bf" = ( /obj/effect/overmap/visitable/sector/cluster, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "cb" = ( /obj/effect/shuttle_landmark/cluster/nav5, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "db" = ( /obj/effect/shuttle_landmark/cluster/nav6, @@ -236,7 +236,7 @@ /area/space) "ib" = ( /obj/effect/shuttle_landmark/cluster/nav7, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "jb" = ( /obj/machinery/button/access/exterior{ @@ -245,7 +245,7 @@ pixel_y = -24; dir = 1 }, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "kb" = ( /obj/machinery/door/airlock/external{ @@ -345,7 +345,7 @@ req_access = null; dir = 4 }, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "wb" = ( /obj/machinery/door/airlock/external{ diff --git a/maps/away/mining/mining-orb.dmm b/maps/away/mining/mining-orb.dmm index 0f03154da5c..46fc8c21fb4 100644 --- a/maps/away/mining/mining-orb.dmm +++ b/maps/away/mining/mining-orb.dmm @@ -14,10 +14,10 @@ /turf/unsimulated/mask, /area/mine/unexplored) "ae" = ( -/turf/floor, +/turf/floor/barren, /area/mine/explored) "af" = ( -/turf/floor, +/turf/floor/barren, /area/mine/unexplored) "ag" = ( /turf/wall/natural/random/high_chance, @@ -250,7 +250,7 @@ /area/mine/explored) "st" = ( /obj/effect/shuttle_landmark/orb/nav7, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "vc" = ( /obj/effect/floor_decal/spline/fancy/wood{ @@ -277,7 +277,7 @@ /obj/effect/floor_decal/spline/fancy/wood/corner{ dir = 4 }, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "Aj" = ( /obj/effect/floor_decal/spline/fancy/wood/corner, diff --git a/maps/away/mining/mining_areas.dm b/maps/away/mining/mining_areas.dm index 94d5e15d978..3dd6da72529 100644 --- a/maps/away/mining/mining_areas.dm +++ b/maps/away/mining/mining_areas.dm @@ -3,7 +3,7 @@ icon_state = "mining" ambience = list('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg') sound_env = ASTEROID - base_turf = /turf/floor + base_turf = /turf/floor/barren area_flags = AREA_FLAG_IS_BACKGROUND | AREA_FLAG_HIDE_FROM_HOLOMAP /area/mine/explored diff --git a/maps/away/smugglers/smugglers.dmm b/maps/away/smugglers/smugglers.dmm index 1881db19743..a0dbd796036 100644 --- a/maps/away/smugglers/smugglers.dmm +++ b/maps/away/smugglers/smugglers.dmm @@ -3,7 +3,7 @@ /turf/space, /area/space) "ab" = ( -/turf/floor, +/turf/floor/barren, /area/mine/explored) "ac" = ( /turf/unsimulated/mask, @@ -24,7 +24,7 @@ /turf/floor, /area/smugglers/base) "ah" = ( -/turf/floor, +/turf/floor/barren, /area/space) "aj" = ( /obj/item/ammo_casing/pistol/magnum{ @@ -37,11 +37,11 @@ pixel_y = -5 }, /obj/effect/decal/cleanable/blood, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "ak" = ( /obj/effect/decal/cleanable/blood, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "al" = ( /obj/effect/wallframe_spawn/reinforced, @@ -98,7 +98,7 @@ pixel_y = 7 }, /obj/item/flashlight/flare/glowstick/yellow, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "av" = ( /obj/effect/decal/cleanable/blood/drip, @@ -281,11 +281,11 @@ pixel_x = -25; pixel_y = 25 }, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "aM" = ( /obj/random/trash, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "aN" = ( /obj/machinery/light/small{ @@ -432,7 +432,7 @@ /obj/item/tool/xeno/hand{ pixel_x = -15 }, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "bh" = ( /obj/effect/floor_decal/industrial/warning{ @@ -476,7 +476,7 @@ /area/smugglers/base) "bn" = ( /obj/structure/boulder, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "bo" = ( /obj/item/stack/material/ore/silver, @@ -484,7 +484,7 @@ pixel_x = 10; pixel_y = -5 }, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "bp" = ( /obj/structure/cable{ @@ -946,7 +946,7 @@ "db" = ( /obj/effect/decal/cleanable/blood, /obj/abstract/landmark/corpse/doctor, -/turf/floor, +/turf/floor/barren, /area/mine/explored) "eb" = ( /obj/machinery/light/small{ diff --git a/maps/away/unishi/unishi.dm b/maps/away/unishi/unishi.dm index 54e80fe8a88..0b7112c782b 100644 --- a/maps/away/unishi/unishi.dm +++ b/maps/away/unishi/unishi.dm @@ -1,6 +1,7 @@ #include "unishi_areas.dm" #include "unishi_jobs.dm" #include "../../../mods/content/xenobiology/_xenobiology.dme" +#include "../../../mods/content/supermatter/_supermatter.dme" /obj/abstract/submap_landmark/joinable_submap/unishi name = "SRV Verne" diff --git a/maps/away_sites_testing/away_sites_testing_define.dm b/maps/away_sites_testing/away_sites_testing_define.dm index 6f3a96274ee..fd1868d5c2a 100644 --- a/maps/away_sites_testing/away_sites_testing_define.dm +++ b/maps/away_sites_testing/away_sites_testing_define.dm @@ -4,10 +4,14 @@ full_name = "Away Sites Testing Land" path = "away_sites_testing" overmap_ids = list(OVERMAP_ID_SPACE) + votable = FALSE allowed_latejoin_spawns = list() default_spawn = null +/datum/map/away_sites_testing/validate() + return TRUE // Do not check for level lists, this is not a playable map. + // Set the observer spawn to include every flag so that CI flag checks pass. /decl/spawnpoint/observer spawn_flags = (SPAWN_FLAG_GHOSTS_CAN_SPAWN | SPAWN_FLAG_JOBS_CAN_SPAWN | SPAWN_FLAG_PRISONERS_CAN_SPAWN | SPAWN_FLAG_PERSISTENCE_CAN_SPAWN) @@ -16,10 +20,10 @@ var/list/unsorted_sites = list_values(SSmapping.get_templates_by_category(MAP_TEMPLATE_CATEGORY_AWAYSITE)) var/list/sorted_sites = sortTim(unsorted_sites, /proc/cmp_sort_templates_tallest_to_shortest) for (var/datum/map_template/A in sorted_sites) - A.load_new_z(centered = FALSE) + A.load_new_z() testing("Spawning [A] in [english_list(SSmapping.get_connected_levels(world.maxz))]") if(A.template_flags & TEMPLATE_FLAG_TEST_DUPLICATES) - A.load_new_z(centered = FALSE) + A.load_new_z() testing("Spawning [A] in [english_list(SSmapping.get_connected_levels(world.maxz))]") /proc/cmp_sort_templates_tallest_to_shortest(var/datum/map_template/a, var/datum/map_template/b) diff --git a/maps/example/example-3.dmm b/maps/example/example-3.dmm index 5b89fac02e5..abffda753cc 100644 --- a/maps/example/example-3.dmm +++ b/maps/example/example-3.dmm @@ -227,9 +227,8 @@ /turf/floor, /area/example/third) "MO" = ( -/obj/abstract/level_data_spawner/main_level{ +/obj/abstract/level_data_spawner/admin_level{ name = "Example Third Deck"; - }, /turf/space, /area/space) diff --git a/maps/example/example_define.dm b/maps/example/example_define.dm index 9c48d50899f..5f8a3a80c16 100644 --- a/maps/example/example_define.dm +++ b/maps/example/example_define.dm @@ -2,6 +2,7 @@ name = "Testing" full_name = "Testing Site" path = "example" + votable = FALSE lobby_screens = list( 'maps/example/example_lobby.png' diff --git a/maps/example/example_jobs.dm b/maps/example/example_jobs.dm index e3ad5c0c488..28d5cf504c6 100644 --- a/maps/example/example_jobs.dm +++ b/maps/example/example_jobs.dm @@ -1,7 +1,6 @@ /datum/map/example default_job_type = /datum/job/example default_department_type = /decl/department/example - id_hud_icons = 'maps/example/hud.dmi' /datum/job/example title = "Tourist" @@ -13,9 +12,10 @@ access = list() minimal_access = list() outfit_type = /decl/outfit/job/tourist + hud_icon = 'maps/example/hud.dmi' department_types = list( /decl/department/example - ) + ) /decl/outfit/job/tourist name = "Job - Testing Site Tourist" diff --git a/maps/example/hud.dmi b/maps/example/hud.dmi index 89dd2f9a079..c2c456e9d50 100644 Binary files a/maps/example/hud.dmi and b/maps/example/hud.dmi differ diff --git a/maps/exodus/exodus-1.dmm b/maps/exodus/exodus-1.dmm index df58cbab4de..bd953735089 100644 --- a/maps/exodus/exodus-1.dmm +++ b/maps/exodus/exodus-1.dmm @@ -3496,8 +3496,7 @@ department = "Atmospherics"; name = "Atmos RC"; pixel_y = 32; - dir = 1; - + dir = 1 }, /obj/structure/table/steel, /turf/floor/tiled/steel_grid, @@ -5467,6 +5466,10 @@ }, /turf/floor/lino, /area/exodus/maintenance/telecomms) +"rd" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "rj" = ( /obj/effect/floor_decal/industrial/outline/grey, /obj/machinery/camera/network/command{ @@ -16599,7 +16602,7 @@ aa aa aa aa -aa +rd aa aa aa @@ -44175,7 +44178,7 @@ aa aa aa aa -aa +rd aa aa aa @@ -49205,7 +49208,7 @@ aa aa aa aa -aa +rd aa aa aa diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 86bd27bd087..093c3433310 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -30773,7 +30773,7 @@ /obj/machinery/fabricator/imprinter{ id_tag = "science" }, -/obj/item/chems/glass/beaker/sulphuric, +/obj/item/chems/glass/beaker/sulfuric, /obj/structure/reagent_dispensers/acid{ density = 0; pixel_y = 32 @@ -32592,7 +32592,7 @@ /obj/machinery/fabricator/imprinter{ id_tag = "science" }, -/obj/item/chems/glass/beaker/sulphuric, +/obj/item/chems/glass/beaker/sulfuric, /turf/floor/tiled/dark, /area/exodus/research/lab) "bqQ" = ( @@ -63723,6 +63723,10 @@ }, /turf/floor/tiled/steel_grid, /area/ship/exodus_pod_research) +"eYg" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "eZK" = ( /obj/effect/floor_decal/corner/purple, /obj/machinery/light, @@ -76795,7 +76799,7 @@ cLU cLU cLU cLU -cLU +eYg cLU cLU cLU @@ -88722,7 +88726,7 @@ cLU cLU cLU cLU -cLU +eYg cLU cLU cLU @@ -108695,7 +108699,7 @@ cLU cLU cLU cLU -cLU +eYg cLU cLU cLU @@ -116645,7 +116649,7 @@ cLU cLU cLU cLU -cLU +eYg cLU cLU cLU diff --git a/maps/exodus/exodus-admin.dmm b/maps/exodus/exodus-admin.dmm index 202bb8b9999..5d94162873e 100644 --- a/maps/exodus/exodus-admin.dmm +++ b/maps/exodus/exodus-admin.dmm @@ -2422,7 +2422,8 @@ /turf/unsimulated/floor/vault, /area/tdome) "lSj" = ( -/obj/effect/forcefield{ +/obj/effect{ + density = 1; desc = "You can't get in. Heh."; name = "Blocker" }, @@ -2434,7 +2435,8 @@ /turf/unsimulated/floor/dark, /area/tdome) "mgi" = ( -/obj/effect/forcefield{ +/obj/effect{ + density = 1; desc = "You can't get in. Heh."; name = "Blocker" }, @@ -2799,7 +2801,8 @@ /turf/floor/tiled, /area/shuttle/escape_shuttle) "xSD" = ( -/obj/effect/forcefield{ +/obj/effect{ + density = 1; desc = "You can't get in. Heh."; name = "Blocker" }, diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 6ad2e80572e..6359c75549c 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -1,21 +1,15 @@ #if !defined(USING_MAP_DATUM) - #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/heist/_heist.dme" - #include "../../mods/gamemodes/meteor/_meteor.dme" - #include "../../mods/gamemodes/ninja/_ninja.dme" - #include "../../mods/gamemodes/revolution/_revolution.dme" - #include "../../mods/gamemodes/traitor/_traitor.dme" - #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" - #include "../../mods/content/mundane.dm" + #include "../../mods/content/baychems/_baychems.dme" + #include "../../mods/content/corporate/_corporate.dme" #include "../../mods/content/government/_government.dme" #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" + #include "../../mods/content/polaris/_polaris.dme" #include "../../mods/content/scaling_descriptors.dm" #include "../../mods/content/xenobiology/_xenobiology.dme" @@ -26,9 +20,22 @@ // Must come after borers for compatibility. #include "../../mods/content/psionics/_psionics.dme" - - // Must come after psionics for counsellor compatibility. #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/content/supermatter/_supermatter.dme" + #include "../../mods/content/xenobiology/_xenobiology.dme" + #include "../../mods/content/tabloids/_tabloids.dme" + + #include "../../mods/gamemodes/cult/_cult.dme" + #include "../../mods/gamemodes/heist/_heist.dme" + #include "../../mods/gamemodes/meteor/_meteor.dme" + #include "../../mods/gamemodes/ninja/_ninja.dme" + #include "../../mods/gamemodes/revolution/_revolution.dme" + #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" + #include "../../mods/gamemodes/traitor/_traitor.dme" + #include "../../mods/gamemodes/mixed.dm" + + #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/mobs/dionaea/_dionaea.dme" #include "../../mods/species/skrell/_skrell.dme" #include "../../mods/species/tajaran/_tajaran.dme" diff --git a/maps/exodus/exodus_antagonism.dm b/maps/exodus/exodus_antagonism.dm index 322bebbf1d3..e09d2ddc900 100644 --- a/maps/exodus/exodus_antagonism.dm +++ b/maps/exodus/exodus_antagonism.dm @@ -1,16 +1,16 @@ /decl/special_role/traitor/Initialize() . = ..() LAZYINITLIST(protected_jobs) - protected_jobs |= list(/datum/job/officer, /datum/job/warden, /datum/job/detective, /datum/job/captain, /datum/job/lawyer, /datum/job/hos) + protected_jobs |= list(/datum/job/standard/officer, /datum/job/standard/warden, /datum/job/standard/detective, /datum/job/standard/captain, /datum/job/standard/lawyer, /datum/job/standard/hos) /decl/special_role/cultist/Initialize() . = ..() LAZYINITLIST(restricted_jobs) - restricted_jobs |= list(/datum/job/lawyer, /datum/job/captain, /datum/job/hos) + restricted_jobs |= list(/datum/job/standard/lawyer, /datum/job/standard/captain, /datum/job/standard/hos) LAZYINITLIST(protected_jobs) - protected_jobs |= list(/datum/job/officer, /datum/job/warden, /datum/job/detective) + protected_jobs |= list(/datum/job/standard/officer, /datum/job/standard/warden, /datum/job/standard/detective) LAZYINITLIST(blacklisted_jobs) - blacklisted_jobs |= list(/datum/job/chaplain, /datum/job/counselor) + blacklisted_jobs |= list(/datum/job/standard/chaplain, /datum/job/standard/counselor) /decl/special_role/loyalist command_department_id = /decl/department/command diff --git a/maps/exodus/exodus_define.dm b/maps/exodus/exodus_define.dm index 0cdfc436956..03a9f450fab 100644 --- a/maps/exodus/exodus_define.dm +++ b/maps/exodus/exodus_define.dm @@ -12,8 +12,6 @@ company_short = "NT" system_name = "Nyx" - default_law_type = /datum/ai_laws/nanotrasen - overmap_ids = list(OVERMAP_ID_SPACE) num_exoplanets = 3 away_site_budget = 3 diff --git a/maps/exodus/exodus_goals.dm b/maps/exodus/exodus_goals.dm index 72c324b5c1d..8275bc4173b 100644 --- a/maps/exodus/exodus_goals.dm +++ b/maps/exodus/exodus_goals.dm @@ -29,12 +29,12 @@ var/global/list/exodus_paperwork_end_areas = list() /datum/goal/department/paperwork/exodus paperwork_types = list(/obj/item/paperwork/exodus) signatory_job_list = list( - /datum/job/captain, - /datum/job/hop, - /datum/job/cmo, - /datum/job/chief_engineer, - /datum/job/rd, - /datum/job/hos + /datum/job/standard/captain, + /datum/job/standard/hop, + /datum/job/standard/cmo, + /datum/job/standard/chief_engineer, + /datum/job/standard/rd, + /datum/job/standard/hos ) /datum/goal/department/paperwork/exodus/get_paper_spawn_turfs() diff --git a/maps/exodus/exodus_jobs.dm b/maps/exodus/exodus_jobs.dm index 68f1d5b8e67..1e09ef50b52 100644 --- a/maps/exodus/exodus_jobs.dm +++ b/maps/exodus/exodus_jobs.dm @@ -7,40 +7,40 @@ spawn_decl = /decl/spawnpoint/gateway /datum/map/exodus - default_job_type = /datum/job/assistant + default_job_type = /datum/job/standard/assistant default_department_type = /decl/department/civilian - id_hud_icons = 'maps/exodus/hud.dmi' allowed_jobs = list( - /datum/job/captain, - /datum/job/hop, - /datum/job/chaplain, - /datum/job/bartender, - /datum/job/chef, - /datum/job/hydro, - /datum/job/qm, - /datum/job/cargo_tech, - /datum/job/mining, - /datum/job/janitor, - /datum/job/librarian, - /datum/job/lawyer, - /datum/job/chief_engineer, - /datum/job/engineer, - /datum/job/cmo, - /datum/job/doctor, - /datum/job/chemist, - /datum/job/counselor, - /datum/job/rd, - /datum/job/scientist, - /datum/job/roboticist, - /datum/job/hos, - /datum/job/detective, - /datum/job/warden, - /datum/job/officer, - /datum/job/robot, - /datum/job/computer + /datum/job/standard/captain, + /datum/job/standard/hop, + /datum/job/standard/chaplain, + /datum/job/standard/bartender, + /datum/job/standard/chef, + /datum/job/standard/hydro, + /datum/job/standard/qm, + /datum/job/standard/cargo_tech, + /datum/job/standard/mining, + /datum/job/standard/janitor, + /datum/job/standard/librarian, + /datum/job/standard/lawyer, + /datum/job/standard/chief_engineer, + /datum/job/standard/engineer, + /datum/job/standard/cmo, + /datum/job/standard/doctor, + /datum/job/standard/chemist, + /datum/job/standard/counselor, + /datum/job/standard/rd, + /datum/job/standard/scientist, + /datum/job/standard/roboticist, + /datum/job/standard/hos, + /datum/job/standard/detective, + /datum/job/standard/warden, + /datum/job/standard/officer, + /datum/job/standard/robot, + /datum/job/standard/computer ) -#define HUMAN_ONLY_JOBS /datum/job/captain, /datum/job/hop, /datum/job/hos +#define HUMAN_ONLY_JOBS /datum/job/standard/captain, /datum/job/standard/hop, /datum/job/standard/hos + species_to_job_blacklist = list( /decl/species/unathi = list( HUMAN_ONLY_JOBS diff --git a/maps/exodus/exodus_loadout.dm b/maps/exodus/exodus_loadout.dm index 107fa0ae8c9..d5f70fdf957 100644 --- a/maps/exodus/exodus_loadout.dm +++ b/maps/exodus/exodus_loadout.dm @@ -2,7 +2,7 @@ name = "religious insignia" path = /obj/item/clothing/insignia cost = 1 - allowed_roles = list(/datum/job/chaplain) + allowed_roles = list(/datum/job/standard/chaplain) uid = "gear_accessory_insignia" /decl/loadout_option/accessory/insignia/Initialize() diff --git a/maps/exodus/exodus_overrides.dm b/maps/exodus/exodus_overrides.dm index 947cbd184c1..c2babeabe95 100644 --- a/maps/exodus/exodus_overrides.dm +++ b/maps/exodus/exodus_overrides.dm @@ -9,3 +9,6 @@ /decl/background_category/heritage = /decl/background_detail/heritage/hidden/cultist, /decl/background_category/faction = /decl/background_detail/faction/other ) + +/datum/map/exodus + default_law_type = /datum/ai_laws/nanotrasen diff --git a/maps/exodus/hud.dmi b/maps/exodus/hud.dmi deleted file mode 100644 index 952d808b00e..00000000000 Binary files a/maps/exodus/hud.dmi and /dev/null differ diff --git a/maps/ministation/hud.dmi b/maps/ministation/hud.dmi index d4fed05d840..8a3e91cf3a7 100644 Binary files a/maps/ministation/hud.dmi and b/maps/ministation/hud.dmi differ diff --git a/maps/ministation/jobs/civilian.dm b/maps/ministation/jobs/civilian.dm index 3d1601530d6..18024cc2379 100644 --- a/maps/ministation/jobs/civilian.dm +++ b/maps/ministation/jobs/civilian.dm @@ -1,15 +1,14 @@ -/datum/job/assistant/ministation +/datum/job/standard/assistant/ministation title = "Recruit" supervisors = "absolutely everyone" alt_titles = list("Technical Recruit","Medical Recruit","Research Recruit","Visitor") outfit_type = /decl/outfit/job/ministation_assistant event_categories = list(ASSIGNMENT_GARDENER) - hud_icon = "hudassistant" /decl/outfit/job/ministation_assistant name = "Job - Ministation Assistant" -/datum/job/bartender/ministation +/datum/job/standard/bartender/ministation title = "Bartender" alt_titles = list("Cook","Barista") supervisors = "the Lieutenant and the Captain" @@ -30,7 +29,7 @@ ) skill_points = 30 -/datum/job/cargo_tech/ministation +/datum/job/standard/cargo_tech/ministation title = "Cargo Technician" alt_titles = list("Shaft Miner","Drill Technician","Prospector") supervisors = "the Lieutenant and the Captain" @@ -73,7 +72,7 @@ ) skill_points = 30 -/datum/job/janitor/ministation +/datum/job/standard/janitor/ministation total_positions = 2 supervisors = "the Lieutenant and the Captain" economic_power = 3 @@ -84,7 +83,7 @@ ) skill_points = 28 -/datum/job/librarian/ministation +/datum/job/standard/librarian/ministation spawn_positions = 2 supervisors = "the Lieutenant, the Captain, and the smell of old paper" economic_power = 5 diff --git a/maps/ministation/jobs/command.dm b/maps/ministation/jobs/command.dm index a823a716095..1a27ff0736d 100644 --- a/maps/ministation/jobs/command.dm +++ b/maps/ministation/jobs/command.dm @@ -1,6 +1,7 @@ -/datum/job/captain/ministation +/datum/job/standard/captain/ministation supervisors = "your profit margin, your conscience, and the watchful eye of the Tradehouse Rep" outfit_type = /decl/outfit/job/ministation/captain + hud_icon = 'maps/ministation/hud.dmi' min_skill = list( SKILL_LITERACY = SKILL_ADEPT, SKILL_WEAPONS = SKILL_ADEPT, @@ -13,7 +14,7 @@ ) skill_points = 40 -/datum/job/captain/ministation/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) +/datum/job/standard/captain/ministation/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) . = ..() if(H) H.verbs |= /mob/proc/freetradeunion_rename_company @@ -33,10 +34,11 @@ command_announcement.Announce("Congratulations to all members of [capitalize(global.using_map.company_name)] on the new name. Their rebranding has changed the [global.using_map.company_short] market value by [0.01*rand(-10,10)]%.", "Trade Union Name Change") verbs -= /mob/proc/freetradeunion_rename_company -/datum/job/hop/ministation +/datum/job/standard/hop/ministation title = "Lieutenant" outfit_type = /decl/outfit/job/ministation/hop - hud_icon = "hudlieutenant" + hud_icon = 'maps/ministation/hud.dmi' + hud_icon_state = "hudlieutenant" min_skill = list( SKILL_LITERACY = SKILL_ADEPT, SKILL_WEAPONS = SKILL_BASIC, diff --git a/maps/ministation/jobs/engineering.dm b/maps/ministation/jobs/engineering.dm index d780f2353d2..a3cb57fbc0a 100644 --- a/maps/ministation/jobs/engineering.dm +++ b/maps/ministation/jobs/engineering.dm @@ -1,4 +1,4 @@ -/datum/job/engineer/ministation +/datum/job/standard/engineer/ministation title = "Station Engineer" supervisors = "the Head Engineer" total_positions = 2 @@ -32,9 +32,8 @@ skill_points = 30 alt_titles = list("Atmospheric Technician", "Electrician", "Maintenance Technician") -/datum/job/chief_engineer/ministation +/datum/job/standard/chief_engineer/ministation title = "Head Engineer" - hud_icon = "hudchiefengineer" access = list( access_engine, access_engine_equip, diff --git a/maps/ministation/jobs/medical.dm b/maps/ministation/jobs/medical.dm index 0e91a2d945b..1dd568c8176 100644 --- a/maps/ministation/jobs/medical.dm +++ b/maps/ministation/jobs/medical.dm @@ -1,4 +1,4 @@ -/datum/job/doctor/ministation +/datum/job/standard/doctor/ministation title = "Medical Doctor" supervisors = "the Head Doctor" total_positions = 2 @@ -35,13 +35,12 @@ ) outfit_type = /decl/outfit/job/ministation/doctor -/datum/job/cmo/ministation +/datum/job/standard/cmo/ministation title = "Head Doctor" supervisors = "the Captain and your own ethics" outfit_type = /decl/outfit/job/ministation/doctor/head alt_titles = list("Chief Medical Officer", "Head Surgeon") skill_points = 38 - hud_icon = "hudheaddoctor" access = list( access_medical, access_medical_equip, diff --git a/maps/ministation/jobs/science.dm b/maps/ministation/jobs/science.dm index f2d80cbe65f..7b8e61e4591 100644 --- a/maps/ministation/jobs/science.dm +++ b/maps/ministation/jobs/science.dm @@ -1,14 +1,13 @@ -/datum/job/scientist/ministation +/datum/job/standard/scientist/ministation title = "Researcher" alt_titles = list("Scientist","Xenobiologist","Roboticist","Xenobotanist") supervisors = "the Head Researcher" spawn_positions = 1 total_positions = 2 outfit_type = /decl/outfit/job/ministation/scientist - hud_icon = "hudscientist" skill_points = 34 -/datum/job/rd/ministation +/datum/job/standard/rd/ministation title = "Research Director" alt_titles = list("Head Researcher", "Chief Researcher") outfit_type = /decl/outfit/job/ministation/scientist/head @@ -27,7 +26,6 @@ SKILL_SCIENCE = SKILL_MAX ) skill_points = 40 - hud_icon = "hudheadscientist" access = list( access_rd, access_bridge, diff --git a/maps/ministation/jobs/security.dm b/maps/ministation/jobs/security.dm index 55ea8e841e2..242c665e7f3 100644 --- a/maps/ministation/jobs/security.dm +++ b/maps/ministation/jobs/security.dm @@ -1,4 +1,4 @@ -/datum/job/officer/ministation +/datum/job/standard/officer/ministation title = "Security Officer" alt_titles = list("Warden") spawn_positions = 1 @@ -22,7 +22,7 @@ ) skill_points = 30 -/datum/job/detective/ministation +/datum/job/standard/detective/ministation alt_titles = list("Inspector") supervisors = "Justice... and the Trademaster" spawn_positions = 1 @@ -55,9 +55,8 @@ ) skill_points = 34 -/datum/job/hos/ministation +/datum/job/standard/hos/ministation outfit_type = /decl/outfit/job/ministation/security/head - hud_icon = "hudhos" access = list( access_security, access_sec_doors, diff --git a/maps/ministation/jobs/tradehouse.dm b/maps/ministation/jobs/tradehouse.dm index 1e167615cf8..daa715dddd5 100644 --- a/maps/ministation/jobs/tradehouse.dm +++ b/maps/ministation/jobs/tradehouse.dm @@ -1,7 +1,8 @@ /datum/job/tradehouse_rep title = "Tradehouse Representative" alt_titles = list("Narc") - hud_icon = "hudnarc" + hud_icon_state = "hudnarc" + hud_icon = 'maps/ministation/hud.dmi' spawn_positions = 1 total_positions = 2 req_admin_notify = 1 diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 88dfa842c03..35b21cdc415 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -5556,6 +5556,10 @@ }, /turf/floor/tiled/monotile, /area/ministation/atmospherics) +"yY" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "yZ" = ( /obj/structure/cable{ icon_state = "1-2" @@ -33009,7 +33013,7 @@ aa aa aa aa -aa +yY aa aa aa @@ -49102,7 +49106,7 @@ aa aa aa aa -aa +yY aa aa aa @@ -57125,7 +57129,7 @@ aa aa aa aa -aa +yY aa aa aa diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 5deccebb931..6646873ff83 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -2713,6 +2713,10 @@ }, /turf/floor/tiled, /area/ministation/hall/w2) +"nt" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "nu" = ( /obj/effect/floor_decal/carpet{ dir = 8 @@ -6710,6 +6714,8 @@ }, /obj/structure/table/laminate, /obj/machinery/reagent_temperature, +/obj/item/chems/cooking_vessel/pot, +/obj/item/chems/cooking_vessel/skillet, /turf/floor/lino, /area/ministation/cafe) "Ct" = ( @@ -9758,12 +9764,12 @@ /obj/structure/table/laminate, /obj/item/eftpos, /obj/item/chems/spray/cleaner, +/obj/item/chems/packet/honey_fake, +/obj/item/chems/packet/honey_fake, /obj/item/chems/packet/honey, /obj/item/chems/packet/honey, /obj/item/chems/packet/honey, -/obj/item/chems/packet/honey, -/obj/item/chems/packet/honey, -/obj/item/chems/packet/honey, +/obj/item/chems/packet/honey_fake, /obj/item/chems/packet/honey, /turf/floor/lino, /area/ministation/cafe) @@ -54008,7 +54014,7 @@ aa aa aa aa -aa +nt aa aa aa @@ -54341,7 +54347,7 @@ aa aa aa aa -aa +nt aa aa aa diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index 227f2c065d9..e7fbc07d968 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -781,7 +781,7 @@ }, /obj/item/med_pouch/trauma, /obj/item/stack/tape_roll/duct_tape, -/obj/item/shield/buckler, +/obj/item/shield/crafted/buckler, /obj/item/gps/explorer, /obj/item/belt/holster/machete, /obj/item/tool/machete/deluxe, @@ -2163,7 +2163,7 @@ "js" = ( /obj/structure/table, /obj/item/chems/glass/beaker/large, -/obj/item/chems/glass/beaker/sulphuric, +/obj/item/chems/glass/beaker/sulfuric, /obj/item/chems/dropper, /obj/effect/floor_decal/corner/purple{ dir = 8 @@ -2274,7 +2274,7 @@ }, /obj/item/med_pouch/trauma, /obj/item/stack/tape_roll/duct_tape, -/obj/item/shield/buckler, +/obj/item/shield/crafted/buckler, /obj/item/gps/explorer, /obj/item/flashlight/lantern, /obj/item/bladed/polearm/spear/improvised/steel, @@ -3536,6 +3536,10 @@ }, /turf/floor/reinforced, /area/ministation/science) +"sq" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "sr" = ( /turf/floor/tiled/dark/monotile/telecomms, /area/ministation/telecomms) @@ -4430,7 +4434,7 @@ /obj/machinery/light, /obj/item/med_pouch/trauma, /obj/item/stack/tape_roll/duct_tape, -/obj/item/shield/buckler, +/obj/item/shield/crafted/buckler, /obj/item/gps/explorer, /obj/item/flashlight/lantern, /obj/item/bladed/polearm/spear/improvised/steel, @@ -29552,7 +29556,7 @@ vA vA vA vA -vA +sq vA vA vA @@ -45788,7 +45792,7 @@ vA vA vA vA -vA +sq vA vA vA @@ -52136,7 +52140,7 @@ vA vA vA vA -vA +sq vA vA vA diff --git a/maps/ministation/ministation-3.dmm b/maps/ministation/ministation-3.dmm index cbdd8abb538..e5b68a7fe20 100644 --- a/maps/ministation/ministation-3.dmm +++ b/maps/ministation/ministation-3.dmm @@ -88,6 +88,10 @@ }, /turf/floor/plating, /area/ministation/maint/l4central) +"kH" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "kV" = ( /obj/machinery/atmospherics/pipe/simple/hidden/universal{ dir = 4 @@ -40459,7 +40463,7 @@ aa aa aa aa -aa +kH aa aa aa diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index 2ecca91fa60..fca80d6d647 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -15,31 +15,39 @@ Twice... #include "../../code/unit_tests/offset_tests.dm" #endif + #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" + + #include "../../mods/content/mundane.dm" + #include "../../mods/content/scaling_descriptors.dm" + + #include "../../mods/content/bigpharma/_bigpharma.dme" + #include "../../mods/content/corporate/_corporate.dme" + #include "../../mods/content/government/_government.dme" + #include "../../mods/content/matchmaking/_matchmaking.dme" + #include "../../mods/content/modern_earth/_modern_earth.dme" + #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" + #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/content/supermatter/_supermatter.dme" + #include "../../mods/content/xenobiology/_xenobiology.dme" + #include "../../mods/gamemodes/cult/_cult.dme" #include "../../mods/gamemodes/heist/_heist.dme" #include "../../mods/gamemodes/ninja/_ninja.dme" #include "../../mods/gamemodes/revolution/_revolution.dme" #include "../../mods/gamemodes/traitor/_traitor.dme" #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" + #include "../../mods/gamemodes/mixed.dm" - #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" + #include "../../mods/mobs/dionaea/_dionaea.dme" + #include "../../mods/mobs/borers/_borers.dme" - #include "../../mods/content/standard_jobs/_standard_jobs.dme" - #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/content/corporate/_corporate.dme" - #include "../../mods/content/matchmaking/_matchmaking.dme" + #include "../../mods/content/tabloids/_tabloids.dme" #include "../../mods/species/ascent/_ascent.dme" + #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/neoavians/_neoavians.dme" - #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/species/serpentid/_serpentid.dme" - #include "../../mods/species/bayliens/_bayliens.dme" - #include "../../mods/content/mundane.dm" - #include "../../mods/content/bigpharma/_bigpharma.dme" - #include "../../mods/content/government/_government.dme" - #include "../../mods/content/modern_earth/_modern_earth.dme" - #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" - #include "../../mods/content/scaling_descriptors.dm" #include "ministation_antagonists.dm" #include "ministation_areas.dm" @@ -70,12 +78,6 @@ Twice... #include "../away/yacht/yacht.dm" #include "../away/liberia/liberia.dm" - #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/mobs/borers/_borers.dme" - - // Must come after borers for compatibility. - #include "../../mods/content/psionics/_psionics.dme" - #include "ministation_overmap.dm" #include "jobs/command.dm" diff --git a/maps/ministation/ministation_jobs.dm b/maps/ministation/ministation_jobs.dm index 92e28630c1f..17bb28b7b9e 100644 --- a/maps/ministation/ministation_jobs.dm +++ b/maps/ministation/ministation_jobs.dm @@ -1,25 +1,24 @@ /datum/map/ministation - default_job_type = /datum/job/assistant/ministation + default_job_type = /datum/job/standard/assistant/ministation default_department_type = /decl/department/civilian - id_hud_icons = 'maps/ministation/hud.dmi' allowed_jobs = list( - /datum/job/assistant/ministation, - /datum/job/bartender/ministation, - /datum/job/captain/ministation, - /datum/job/cargo_tech/ministation, - /datum/job/robot, - /datum/job/computer, - /datum/job/detective/ministation, - /datum/job/doctor/ministation, - /datum/job/cmo/ministation, - /datum/job/engineer/ministation, - /datum/job/chief_engineer/ministation, - /datum/job/hop/ministation, - /datum/job/janitor/ministation, - /datum/job/scientist/ministation, - /datum/job/rd/ministation, - /datum/job/officer/ministation, - /datum/job/hos/ministation, - /datum/job/librarian/ministation, + /datum/job/standard/assistant/ministation, + /datum/job/standard/bartender/ministation, + /datum/job/standard/captain/ministation, + /datum/job/standard/cargo_tech/ministation, + /datum/job/standard/robot, + /datum/job/standard/computer, + /datum/job/standard/detective/ministation, + /datum/job/standard/doctor/ministation, + /datum/job/standard/cmo/ministation, + /datum/job/standard/engineer/ministation, + /datum/job/standard/chief_engineer/ministation, + /datum/job/standard/hop/ministation, + /datum/job/standard/janitor/ministation, + /datum/job/standard/scientist/ministation, + /datum/job/standard/rd/ministation, + /datum/job/standard/officer/ministation, + /datum/job/standard/hos/ministation, + /datum/job/standard/librarian/ministation, /datum/job/tradehouse_rep ) \ No newline at end of file diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index f94fbbc844c..33b776920fb 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -3,45 +3,47 @@ #include "modpack_testing_lobby.dm" #include "blank.dmm" - #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/heist/_heist.dme" - #include "../../mods/gamemodes/meteor/_meteor.dme" - #include "../../mods/gamemodes/ninja/_ninja.dme" - #include "../../mods/gamemodes/revolution/_revolution.dme" - #include "../../mods/gamemodes/traitor/_traitor.dme" - #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" - #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/content/tabloids/_tabloids.dme" - #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/byond_membership/_byond_membership.dm" #include "../../mods/content/corporate/_corporate.dme" + #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" + #include "../../mods/content/fantasy/_fantasy.dme" #include "../../mods/content/generic_shuttles/_generic_shuttles.dme" #include "../../mods/content/government/_government.dme" + #include "../../mods/content/inertia/_inertia.dme" #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" + #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/psionics/_psionics.dme" #include "../../mods/content/shackles/_shackles.dme" + #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/content/pheromones/_pheromones.dme" - #include "../../mods/species/drakes/_drakes.dme" // include before _fantasy.dme so overrides work - #include "../../mods/content/fantasy/_fantasy.dme" - #include "../../mods/content/inertia/_inertia.dme" + #include "../../mods/content/item_sharpening/_item_sharpening.dme" - #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/gamemodes/cult/_cult.dme" + #include "../../mods/gamemodes/heist/_heist.dme" + #include "../../mods/gamemodes/meteor/_meteor.dme" + #include "../../mods/gamemodes/ninja/_ninja.dme" + #include "../../mods/gamemodes/revolution/_revolution.dme" + #include "../../mods/gamemodes/traitor/_traitor.dme" + #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" + #include "../../mods/gamemodes/mixed.dm" - // Must come after borers for compatibility. - #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/species/serpentid/_serpentid.dme" #include "../../mods/species/ascent/_ascent.dme" + #include "../../mods/species/bayliens/_bayliens.dme" + #include "../../mods/species/drakes/_drakes.dme" #include "../../mods/species/neoavians/_neoavians.dme" + #include "../../mods/species/serpentid/_serpentid.dme" #include "../../mods/species/utility_frames/_utility_frames.dme" - #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/vox/_vox.dme" #define USING_MAP_DATUM /datum/map/modpack_testing diff --git a/maps/modpack_testing/modpack_testing_define.dm b/maps/modpack_testing/modpack_testing_define.dm index f5b09decd1f..8ed4837727c 100644 --- a/maps/modpack_testing/modpack_testing_define.dm +++ b/maps/modpack_testing/modpack_testing_define.dm @@ -4,3 +4,7 @@ path = "modpack_testing" allowed_latejoin_spawns = list() default_spawn = null + votable = FALSE + +/datum/map/modpack_testing/validate() + return TRUE // Do not check for level lists, this is not a playable map. diff --git a/maps/planets/test_planet/neutralia-2.dmm b/maps/planets/test_planet/neutralia-2.dmm index 8b5e82e76e8..cc84b991ea8 100644 --- a/maps/planets/test_planet/neutralia-2.dmm +++ b/maps/planets/test_planet/neutralia-2.dmm @@ -3,11 +3,11 @@ /turf/unsimulated/mineral, /area/exoplanet/underground/neutralia) "b" = ( -/turf/floor, +/turf/floor/barren, /area/exoplanet/underground/neutralia) "c" = ( /obj/abstract/level_data_spawner/neutralia/underground, -/turf/floor, +/turf/floor/barren, /area/exoplanet/underground/neutralia) (1,1,1) = {" diff --git a/maps/planets/test_planet/neutralia-3.dmm b/maps/planets/test_planet/neutralia-3.dmm index 768772a0f91..f7d1ff768ee 100644 --- a/maps/planets/test_planet/neutralia-3.dmm +++ b/maps/planets/test_planet/neutralia-3.dmm @@ -1,6 +1,6 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/floor, +/turf/floor/barren, /area/exoplanet/neutralia) "b" = ( /turf/unsimulated/mineral, @@ -17,15 +17,15 @@ /area/exoplanet/neutralia) "j" = ( /obj/abstract/landmark/exoplanet_spawn/large_plant, -/turf/floor, +/turf/floor/barren, /area/exoplanet/neutralia) "o" = ( /obj/abstract/landmark/exoplanet_spawn/animal, -/turf/floor, +/turf/floor/barren, /area/exoplanet/neutralia) "x" = ( /obj/abstract/landmark/exoplanet_spawn/plant, -/turf/floor, +/turf/floor/barren, /area/exoplanet/neutralia) (1,1,1) = {" diff --git a/maps/planets/test_planet/test_planet.dm b/maps/planets/test_planet/test_planet.dm index 159722fde19..83c42703e65 100644 --- a/maps/planets/test_planet/test_planet.dm +++ b/maps/planets/test_planet/test_planet.dm @@ -131,14 +131,14 @@ name = "neutralia surface" level_id = NEUTRALIA_SURFACE_LEVEL_ID base_area = /area/exoplanet/neutralia - base_turf = /turf/floor + base_turf = /turf/floor/barren border_filler = /turf/unsimulated/dark_filler /datum/level_data/planetoid/neutralia/underground name = "neutralia underground" level_id = "neutralia_underground" base_area = /area/exoplanet/underground/neutralia - base_turf = /turf/floor + base_turf = /turf/floor/barren border_filler = /turf/unsimulated/mineral /datum/level_data/planetoid/neutralia/underground/bottom diff --git a/maps/planets_testing/planets_testing_define.dm b/maps/planets_testing/planets_testing_define.dm index c82359943fd..8380842377f 100644 --- a/maps/planets_testing/planets_testing_define.dm +++ b/maps/planets_testing/planets_testing_define.dm @@ -3,8 +3,12 @@ full_name = "Planets Testing" path = "planets_testing" overmap_ids = list(OVERMAP_ID_SPACE) - allowed_latejoin_spawns = list() default_spawn = null + votable = FALSE + allowed_latejoin_spawns = list() + +/datum/map/planet_testing/validate() + return FALSE // Testing map, do not validate levels. // Set the observer spawn to include every flag so that CI flag checks pass. /decl/spawnpoint/observer diff --git a/maps/random_ruins/space_ruins/space_ruins.dm b/maps/random_ruins/space_ruins/space_ruins.dm index 6310e536a7e..460f5a6995a 100644 --- a/maps/random_ruins/space_ruins/space_ruins.dm +++ b/maps/random_ruins/space_ruins/space_ruins.dm @@ -5,6 +5,7 @@ material = /decl/material/solid/gemstone/diamond /datum/map_template/ruin/space + abstract_type = /datum/map_template/ruin/space template_categories = list(MAP_TEMPLATE_CATEGORY_SPACE) prefix = "maps/random_ruins/space_ruins/" cost = 1 diff --git a/maps/shaded_hills/areas/_areas.dm b/maps/shaded_hills/areas/_areas.dm index b33bf376d84..e31eff6ce28 100644 --- a/maps/shaded_hills/areas/_areas.dm +++ b/maps/shaded_hills/areas/_areas.dm @@ -20,13 +20,6 @@ ) sound_env = GENERIC ambience = list() - var/list/additional_fishing_results - -/area/shaded_hills/Initialize() - if(additional_fishing_results) - for(var/fish in additional_fishing_results) - fishing_results[fish] = additional_fishing_results[fish] - . = ..() /area/shaded_hills/outside name = "\improper Grasslands" diff --git a/maps/shaded_hills/areas/grassland.dm b/maps/shaded_hills/areas/grassland.dm index 9b7de6659e1..803f9a5e920 100644 --- a/maps/shaded_hills/areas/grassland.dm +++ b/maps/shaded_hills/areas/grassland.dm @@ -34,12 +34,15 @@ color = COLOR_BLUE description = "The soft susurration of running water mingles with the hum of insects and croak of frogs." area_blurb_category = /area/shaded_hills/outside/river - additional_fishing_results = list( + +/area/shaded_hills/outside/river/get_additional_fishing_results() + var/static/list/additional_fishing_results = list( /mob/living/simple_animal/aquatic/fish/large = 5, /mob/living/simple_animal/aquatic/fish/large/salmon = 5, /mob/living/simple_animal/aquatic/fish/large/trout = 5, /mob/living/simple_animal/aquatic/fish/large/pike = 3 ) + return additional_fishing_results /area/shaded_hills/caves name = "\improper Deep Tunnels" diff --git a/maps/shaded_hills/areas/woods.dm b/maps/shaded_hills/areas/woods.dm index 0fd8dca0685..73c262300a6 100644 --- a/maps/shaded_hills/areas/woods.dm +++ b/maps/shaded_hills/areas/woods.dm @@ -4,13 +4,16 @@ /area/shaded_hills/outside/river/lake name = "Woodland Lake" - additional_fishing_results = list( - /mob/living/simple_animal/aquatic/fish/large/bass = 5, - /mob/living/simple_animal/aquatic/fish/large/trout = 5, - /mob/living/simple_animal/aquatic/fish/large/javelin = 5, - /mob/living/simple_animal/hostile/aquatic/carp = 3, - /mob/living/simple_animal/aquatic/fish/large/koi = 1 + +/area/shaded_hills/outside/river/lake/get_additional_fishing_results() + var/static/list/additional_fishing_results = list( + /mob/living/simple_animal/aquatic/fish/large/bass = 5, + /mob/living/simple_animal/aquatic/fish/large/trout = 5, + /mob/living/simple_animal/aquatic/fish/large/javelin = 5, + /mob/living/simple_animal/hostile/aquatic/carp = 3, + /mob/living/simple_animal/aquatic/fish/large/koi = 1 ) + return additional_fishing_results /area/shaded_hills/outside/woods name = "Woodlands" diff --git a/maps/shaded_hills/icons/hud.dmi b/maps/shaded_hills/icons/hud.dmi deleted file mode 100644 index c8c2fcf8bd1..00000000000 Binary files a/maps/shaded_hills/icons/hud.dmi and /dev/null differ diff --git a/maps/shaded_hills/jobs/_jobs.dm b/maps/shaded_hills/jobs/_jobs.dm index 638c6ff7bf5..7eb2719f661 100644 --- a/maps/shaded_hills/jobs/_jobs.dm +++ b/maps/shaded_hills/jobs/_jobs.dm @@ -1,6 +1,5 @@ /datum/map/shaded_hills - id_hud_icons = 'maps/shaded_hills/icons/hud.dmi' - allowed_jobs = list( + allowed_jobs = list( /datum/job/shaded_hills/visitor/traveller, /datum/job/shaded_hills/visitor/traveller/learned, /datum/job/shaded_hills/visitor/beggar_knight, @@ -48,6 +47,7 @@ /datum/job/shaded_hills abstract_type = /datum/job/shaded_hills + hud_icon_state = "hudblank" department_types = list( /decl/department/shaded_hills/locals ) diff --git a/maps/shaded_hills/levels/_levels.dm b/maps/shaded_hills/levels/_levels.dm index 9150072af91..dc2d59ddcc5 100644 --- a/maps/shaded_hills/levels/_levels.dm +++ b/maps/shaded_hills/levels/_levels.dm @@ -1,11 +1,11 @@ /obj/abstract/map_data/shaded_hills height = 2 -/datum/level_data/player_level/shaded_hills +/datum/level_data/main_level/shaded_hills use_global_exterior_ambience = FALSE base_area = null base_turf = /turf/floor/dirt - abstract_type = /datum/level_data/player_level/shaded_hills + abstract_type = /datum/level_data/main_level/shaded_hills ambient_light_level = 1 ambient_light_color = "#f3e6ca" strata = /decl/strata/shaded_hills @@ -16,10 +16,6 @@ daycycle_type = /datum/daycycle/shaded_hills daycycle_id = "daycycle_shaded_hills" template_edge_padding = 0 // we use a strictly delineated subarea, no need for this guard - var/submap_budget = 0 - var/submap_category = null - var/submap_area - var/list/mobs_to_spawn = list() /datum/daycycle/shaded_hills cycle_duration = 2 HOURS // 1 hour of daylight, 1 hour of night @@ -29,33 +25,7 @@ time_in_cycle = rand(cycle_duration) ..() -/datum/level_data/player_level/shaded_hills/get_subtemplate_areas(template_category, blacklist, whitelist) - return submap_area ? (islist(submap_area) ? submap_area : list(submap_area)) : null - -/datum/level_data/player_level/shaded_hills/get_subtemplate_budget() - return submap_budget - -/datum/level_data/player_level/shaded_hills/get_subtemplate_category() - return submap_category - -/datum/level_data/player_level/shaded_hills/after_generate_level() - . = ..() - if(length(mobs_to_spawn)) - for(var/list/mob_category in mobs_to_spawn) - var/list/mob_types = mob_category[1] - var/mob_turf = mob_category[2] - var/mob_count = mob_category[3] - var/sanity = 1000 - while(mob_count && sanity) - sanity-- - var/turf/place_mob_at = locate(rand(level_inner_min_x, level_inner_max_x), rand(level_inner_min_y, level_inner_max_y), level_z) - if(istype(place_mob_at, mob_turf) && !(locate(/mob/living) in place_mob_at)) - var/mob_type = pickweight(mob_types) - new mob_type(place_mob_at) - mob_count-- - CHECK_TICK - -/datum/level_data/player_level/shaded_hills/grassland +/datum/level_data/main_level/shaded_hills/grassland name = "Shaded Hills - Grassland" level_id = "shaded_hills_grassland" level_generators = list( @@ -68,11 +38,12 @@ "shaded_hills_swamp" = SOUTH, "shaded_hills_downlands" = EAST ) - submap_budget = 5 - submap_category = MAP_TEMPLATE_CATEGORY_SH_GRASSLAND - submap_area = /area/shaded_hills/outside/poi + subtemplate_budget = 5 + subtemplate_category = MAP_TEMPLATE_CATEGORY_FANTASY_GRASSLAND + subtemplate_area = /area/shaded_hills/outside/poi - mobs_to_spawn = list( +/datum/level_data/main_level/shaded_hills/grassland/get_mobs_to_populate_level() + var/static/list/mobs_to_spawn = list( list( list( /mob/living/simple_animal/passive/mouse = 9, @@ -85,9 +56,9 @@ 10 ) ) + return mobs_to_spawn - -/datum/level_data/player_level/shaded_hills/swamp +/datum/level_data/main_level/shaded_hills/swamp name = "Shaded Hills - Swamp" level_id = "shaded_hills_swamp" connected_levels = list( @@ -97,11 +68,12 @@ /datum/random_map/noise/shaded_hills/swamp, /datum/random_map/noise/forage/shaded_hills/swamp ) - submap_budget = 5 - submap_category = MAP_TEMPLATE_CATEGORY_SH_SWAMP - submap_area = /area/shaded_hills/outside/swamp/poi + subtemplate_budget = 5 + subtemplate_category = MAP_TEMPLATE_CATEGORY_FANTASY_SWAMP + subtemplate_area = /area/shaded_hills/outside/swamp/poi - mobs_to_spawn = list( +/datum/level_data/main_level/shaded_hills/swamp/get_mobs_to_populate_level() + var/static/list/mobs_to_spawn = list( list( list( /mob/living/simple_animal/passive/mouse = 6, @@ -127,8 +99,9 @@ 10 ) ) + return mobs_to_spawn -/datum/level_data/player_level/shaded_hills/woods +/datum/level_data/main_level/shaded_hills/woods name = "Shaded Hills - Woods" level_id = "shaded_hills_woods" connected_levels = list( @@ -138,11 +111,12 @@ /datum/random_map/noise/shaded_hills/woods, /datum/random_map/noise/forage/shaded_hills/woods ) - submap_budget = 5 - submap_category = MAP_TEMPLATE_CATEGORY_SH_WOODS - submap_area = /area/shaded_hills/outside/woods/poi + subtemplate_budget = 5 + subtemplate_category = MAP_TEMPLATE_CATEGORY_FANTASY_WOODS + subtemplate_area = /area/shaded_hills/outside/woods/poi - mobs_to_spawn = list( +/datum/level_data/main_level/shaded_hills/woods/get_mobs_to_populate_level() + var/static/list/mobs_to_spawn = list( list( list( /mob/living/simple_animal/passive/mouse = 6, @@ -162,8 +136,9 @@ 5 ) ) + return mobs_to_spawn -/datum/level_data/player_level/shaded_hills/downlands +/datum/level_data/main_level/shaded_hills/downlands name = "Shaded Hills - Downlands" level_id = "shaded_hills_downlands" level_generators = list( @@ -173,50 +148,50 @@ connected_levels = list( "shaded_hills_grassland" = WEST ) - submap_budget = 5 - submap_category = MAP_TEMPLATE_CATEGORY_SH_DOWNLANDS - submap_area = /area/shaded_hills/outside/downlands/poi + subtemplate_budget = 5 + subtemplate_category = MAP_TEMPLATE_CATEGORY_FANTASY_DOWNLANDS + subtemplate_area = /area/shaded_hills/outside/downlands/poi -/datum/level_data/player_level/shaded_hills/caverns +/datum/level_data/main_level/shaded_hills/caverns name = "Shaded Hills - Caverns" level_id = "shaded_hills_caverns" connected_levels = list( "shaded_hills_dungeon" = EAST ) - submap_budget = 5 - submap_category = MAP_TEMPLATE_CATEGORY_SH_CAVERNS - submap_area = /area/shaded_hills/caves/deep/poi + subtemplate_budget = 5 + subtemplate_category = MAP_TEMPLATE_CATEGORY_FANTASY_CAVERNS + subtemplate_area = /area/shaded_hills/caves/deep/poi level_generators = list( /datum/random_map/automata/cave_system/shaded_hills, /datum/random_map/noise/ore/rich ) base_turf = /turf/floor/rock/basalt -/datum/level_data/player_level/shaded_hills/dungeon +/datum/level_data/main_level/shaded_hills/dungeon name = "Shaded Hills - Dungeon" level_id = "shaded_hills_dungeon" connected_levels = list( "shaded_hills_caverns" = WEST ) - submap_budget = 5 - submap_category = MAP_TEMPLATE_CATEGORY_SH_DUNGEON - submap_area = /area/shaded_hills/caves/dungeon/poi + subtemplate_budget = 5 + subtemplate_category = MAP_TEMPLATE_CATEGORY_FANTASY_DUNGEON + subtemplate_area = /area/shaded_hills/caves/dungeon/poi base_turf = /turf/floor/rock/basalt /obj/abstract/level_data_spawner/shaded_hills_grassland - level_data_type = /datum/level_data/player_level/shaded_hills/grassland + level_data_type = /datum/level_data/main_level/shaded_hills/grassland /obj/abstract/level_data_spawner/shaded_hills_swamp - level_data_type = /datum/level_data/player_level/shaded_hills/swamp + level_data_type = /datum/level_data/main_level/shaded_hills/swamp /obj/abstract/level_data_spawner/shaded_hills_woods - level_data_type = /datum/level_data/player_level/shaded_hills/woods + level_data_type = /datum/level_data/main_level/shaded_hills/woods /obj/abstract/level_data_spawner/shaded_hills_downlands - level_data_type = /datum/level_data/player_level/shaded_hills/downlands + level_data_type = /datum/level_data/main_level/shaded_hills/downlands /obj/abstract/level_data_spawner/shaded_hills_caverns - level_data_type = /datum/level_data/player_level/shaded_hills/caverns + level_data_type = /datum/level_data/main_level/shaded_hills/caverns /obj/abstract/level_data_spawner/shaded_hills_dungeon - level_data_type = /datum/level_data/player_level/shaded_hills/dungeon + level_data_type = /datum/level_data/main_level/shaded_hills/dungeon diff --git a/maps/shaded_hills/shaded_hills-dungeon.dmm b/maps/shaded_hills/shaded_hills-dungeon.dmm index dc6ee7b450e..6d1ab918e6f 100644 --- a/maps/shaded_hills/shaded_hills-dungeon.dmm +++ b/maps/shaded_hills/shaded_hills-dungeon.dmm @@ -466,8 +466,6 @@ /area/shaded_hills/caves/dungeon/poi) "GA" = ( /obj/structure/door/ebony, -/obj/structure/door/ebony, -/obj/structure/door/ebony, /obj/abstract/landmark/lock_preset{ lock_preset_id = "sunken keep"; name = "sunken keep locked door" diff --git a/maps/shaded_hills/shaded_hills-grassland.dmm b/maps/shaded_hills/shaded_hills-grassland.dmm index b885f19cf3f..3adbafa7443 100644 --- a/maps/shaded_hills/shaded_hills-grassland.dmm +++ b/maps/shaded_hills/shaded_hills-grassland.dmm @@ -67,7 +67,7 @@ /area/shaded_hills/outside) "kr" = ( /obj/item/tool/pickaxe/iron, -/obj/item/tool/axe, +/obj/item/tool/axe/iron, /obj/abstract/exterior_marker/inside, /obj/structure/closet/crate/chest/ebony, /turf/floor/woven, @@ -125,6 +125,10 @@ /obj/item/bag/sack, /turf/floor/woven, /area/shaded_hills/caves/unexplored/south) +"qj" = ( +/obj/abstract/force_fluid_flow/north, +/turf/floor/mud/water/deep, +/area/shaded_hills/outside/river) "qy" = ( /turf/wall/natural/basalt/shaded_hills, /area/shaded_hills/caves/river) @@ -136,6 +140,10 @@ "sS" = ( /turf/floor/mud/water, /area/shaded_hills/caves/unexplored/south) +"sT" = ( +/obj/abstract/force_fluid_flow/north, +/turf/floor/mud/water/deep, +/area/shaded_hills/outside/river) "te" = ( /turf/unsimulated/mask, /area/shaded_hills/caves/unexplored/south) @@ -19512,7 +19520,7 @@ Gh Gh My My -My +sT lC lC lC @@ -19529,8 +19537,8 @@ lC lC lC lC -My -My +qj +sT My Gh Gh @@ -19572,12 +19580,12 @@ My My My My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT My My My @@ -19662,9 +19670,9 @@ Gh Gh Gh My -My -My -My +sT +sT +sT lC lC lC @@ -19681,9 +19689,9 @@ lC lC lC lC -My -My -My +sT +sT +sT My My Gh @@ -19717,23 +19725,23 @@ Gh Gh My My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My My Gh @@ -19753,14 +19761,14 @@ JN My My My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT My My My @@ -19771,15 +19779,15 @@ My Gh My My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT My Gh Gh @@ -19814,9 +19822,9 @@ My My My My -My -My -My +sT +sT +sT lC lC lC @@ -19833,11 +19841,11 @@ lC lC lC lC -My -My -My -My -My +sT +sT +sT +sT +sT My My Gh @@ -19867,27 +19875,27 @@ Gh Gh My My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My My My @@ -19901,38 +19909,38 @@ Gh Xd vX Xd +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My My My @@ -19960,15 +19968,15 @@ My My My My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT lC lC lC @@ -19986,12 +19994,12 @@ lC lC lC My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT My My My @@ -20017,6 +20025,13 @@ Gh Gh Gh My +sT +sT +sT +sT +sT +sT +sT My My My @@ -20028,20 +20043,13 @@ My My My My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT My My My @@ -20053,42 +20061,42 @@ My Xd vX Xd -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My Gh Gh @@ -20107,19 +20115,19 @@ My My My My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My lC lC @@ -20140,15 +20148,15 @@ lC Gh My My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT My My My @@ -20170,10 +20178,10 @@ Gh Gh My My -My -My -My -My +sT +sT +sT +sT My My Gh @@ -20190,57 +20198,57 @@ Gh My My My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT EE EE Xd -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My Gh Gh @@ -20252,21 +20260,21 @@ zp zp Gh My -My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT +sT My My My @@ -20299,18 +20307,18 @@ My My My My +sT +sT +sT +sT +sT +sT +sT My My My -My -My -My -My -My -My -My -My -My +sT +sT My Gh Gh @@ -20345,38 +20353,38 @@ Gh My My My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT JN EE EE EE JN +sT +sT My My My +sT +sT +sT My My -My -My -My -My -My -My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT +sT My Gh My @@ -20404,14 +20412,14 @@ zp Gh Gh My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT My My My @@ -20456,14 +20464,14 @@ My My My My -My -My -My -My -My -My -My -My +sT +sT +sT +sT +sT +sT +sT +sT My Gh Gh @@ -20501,15 +20509,15 @@ My My My My -My -My -My -My -My +sT +sT +sT +sT +sT EE vX EE -My +sT My My Gh @@ -20524,9 +20532,9 @@ My My My My -My -My -My +sT +sT +sT My My Gh diff --git a/maps/shaded_hills/shaded_hills-woods.dmm b/maps/shaded_hills/shaded_hills-woods.dmm index 66168daa63f..06a9695d634 100644 --- a/maps/shaded_hills/shaded_hills-woods.dmm +++ b/maps/shaded_hills/shaded_hills-woods.dmm @@ -71,9 +71,6 @@ "te" = ( /turf/floor/rock/basalt, /area/shaded_hills/outside/woods) -"ul" = ( -/turf/floor/mud/water, -/area/shaded_hills/outside/river/woods) "uA" = ( /turf/unsimulated/dark_filler, /area/shaded_hills/outside/river/woods) @@ -112,6 +109,9 @@ "DX" = ( /turf/floor/mud/water/deep, /area/shaded_hills/caves/river/woods) +"En" = ( +/turf/floor/mud/water, +/area/shaded_hills/outside/river/woods) "ES" = ( /turf/floor/grass, /area/shaded_hills/outside/river/woods) @@ -178,8 +178,9 @@ /turf/floor/wood/rough/walnut, /area/shaded_hills/forester_hut) "PO" = ( +/obj/abstract/force_fluid_flow/north, /turf/floor/mud/water/deep, -/area/shaded_hills/outside/river/woods) +/area/shaded_hills/outside/river/lake) "SI" = ( /turf/wall/log/walnut, /area/shaded_hills/forester_hut) @@ -199,6 +200,10 @@ /obj/abstract/landmark/start/shaded_hills/traveller, /turf/floor/barren, /area/shaded_hills/caves/woods) +"Wo" = ( +/obj/abstract/force_fluid_flow/north, +/turf/floor/mud/water/deep, +/area/shaded_hills/outside/river/woods) "Xt" = ( /turf/floor/mud, /area/shaded_hills/caves/woods) @@ -209,6 +214,9 @@ "YO" = ( /turf/unsimulated/mask, /area/shaded_hills/outside/river/woods) +"Zo" = ( +/turf/floor/mud/water/deep, +/area/shaded_hills/outside/river/woods) "Zs" = ( /obj/structure/wall_sconce/lantern{ dir = 1; @@ -15657,11 +15665,11 @@ zp zp zp zp -ul -ul -ul -ul -ul +En +En +En +En +En zp zp zp @@ -15806,21 +15814,21 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -15953,29 +15961,29 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp kx kx @@ -16103,32 +16111,32 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En dp JI AN @@ -16254,34 +16262,34 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En JI AN zp @@ -16405,35 +16413,35 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En JI AN zp @@ -16556,39 +16564,39 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En JI AN -ul +En zp zp zp @@ -16707,41 +16715,41 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +En +En +En +En +En +En dp JI AN dp -ul +En zp zp zp @@ -16857,44 +16865,44 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +En +En +En +En JI AN -ul -ul -ul +En +En +En zp zp zp @@ -17008,45 +17016,45 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul +En +En +En +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +En JI AN -ul -ul -ul +En +En +En zp zp zp @@ -17158,49 +17166,49 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo JI AN -ul -ul -ul -ul -ul +En +En +En +En +En zp zp zp @@ -17310,50 +17318,50 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo dp JI AN dp -ul -ul -ul -ul -ul +En +En +En +En +En zp zp zp @@ -17460,54 +17468,54 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +Zo +En +En +Zo +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo JI AN -PO -ul -ul -ul -ul -ul -ul -ul +Zo +En +En +En +En +En +En +En zp zp zp @@ -17611,57 +17619,57 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo JI AN -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul +Zo +En +En +En +En +En +En +En +En +En zp zp zp @@ -17748,7 +17756,7 @@ ma ma ma ma -ul +En zp zp zp @@ -17762,60 +17770,60 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo JI AN -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +Wo +Zo +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -17834,9 +17842,9 @@ zp zp zp zp -ul -ul -ul +En +En +En zp zp zp @@ -17901,9 +17909,9 @@ ma ma ma ma -ul -ul -ul +En +En +En zp zp zp @@ -17913,63 +17921,63 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo +Wo dp JI AN dp -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +Wo +Zo +En +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -17983,15 +17991,15 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En zp zp zp @@ -18053,78 +18061,78 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En +En +En +En zp zp zp zp zp -ul -ul -ul -ul -PO -PO -PO -PO -PO +En +En +En +En +Zo +Wo +Wo +Wo +Wo JI AN -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +Wo +Wo +Wo +Zo +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -18132,19 +18140,19 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -18206,40 +18214,40 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En zp zp zp @@ -18250,55 +18258,55 @@ zp zp zp zp -ul -ul -ul -ul -PO -PO -PO -PO +En +En +En +En +Zo +Zo +Wo +Wo JI AN -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp uA @@ -18359,37 +18367,37 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En zp zp zp @@ -18402,56 +18410,56 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -PO -PO +En +En +En +En +En +En +Zo +Zo JI AN -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp uA uA @@ -18512,35 +18520,35 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En zp zp zp @@ -18555,56 +18563,56 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En dp JI AN dp -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En uA uA uA @@ -18664,34 +18672,34 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En zp zp zp @@ -18707,56 +18715,56 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En JI AN -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +Zo +Zo +En +En +En +En +En +En +En +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +En +En +En +En +En +En +En +En +En +En uA uA uA @@ -18817,30 +18825,30 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En zp zp zp @@ -18861,54 +18869,54 @@ zp zp zp zp -ul -ul -ul -ul -ul +En +En +En +En +En JI AN -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul +En +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +Zo +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +Zo +En +En +En +En +En +En uA uA uA @@ -18969,30 +18977,30 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En zp zp zp @@ -19014,53 +19022,53 @@ zp zp zp zp -ul -ul -ul -ul +En +En +En +En JI AN -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul +En +En +En +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +En +En +En uA uA uA @@ -19112,8 +19120,8 @@ hT hT hT hT -hT -hT +PO +PO hT hT ma @@ -19122,28 +19130,28 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En zp zp zp @@ -19168,51 +19176,51 @@ zp zp zp zp -ul +En dp JI AN dp -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +Zo +En +En +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +Zo uA uA uA @@ -19262,40 +19270,40 @@ hT hT hT hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -ma -ma -ma -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO PO PO PO PO PO PO -ul -ul -ul -ul -ul +hT +hT +hT +ma +ma +ma +En +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En zp zp zp @@ -19324,47 +19332,47 @@ zp zp JI AN -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +En +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Zo +Zo +Zo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo uA uA uA @@ -19413,24 +19421,6 @@ hT hT hT hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -ul -ul -ul -PO -PO PO PO PO @@ -19441,12 +19431,30 @@ PO PO PO PO -ul -ul -ul -ul -ul -ul +hT +hT +hT +En +En +En +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En zp zp zp @@ -19477,46 +19485,46 @@ zp JI AN zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -ul -ul -ul -PO -PO -PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +En +En +En +Zo +Zo +Zo +Zo +En +En +En +Zo +Zo +Zo +En +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo +Wo +Wo uA uA uA @@ -19564,20 +19572,6 @@ hT hT hT hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT PO PO PO @@ -19592,12 +19586,26 @@ PO PO PO PO -ul -ul -ul -ul -ul -ul +Zo +Zo +Zo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En zp zp zp @@ -19631,44 +19639,44 @@ AN zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO -PO -PO +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +Zo +Zo +Wo +Wo +Wo uA uA uA @@ -19716,20 +19724,6 @@ hT hT hT hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT PO PO PO @@ -19743,13 +19737,27 @@ PO PO PO PO -ul -ul -ul -ul -ul -ul -ul +PO +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +En +En +En +En +En +En +En zp zp zp @@ -19784,43 +19792,43 @@ dp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -PO -PO -PO +En +En +En +En +En +En +En +Zo +Zo +Wo uA uA uA @@ -19868,20 +19876,6 @@ hT hT hT hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT PO PO PO @@ -19894,13 +19888,27 @@ PO PO PO PO -ul -ul -ul -ul -ul -ul -ul +PO +PO +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Wo +Zo +Zo +En +En +En +En +En +En +En zp zp zp @@ -19940,16 +19948,16 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -19965,14 +19973,14 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul -ul -PO +En +En +En +En +En +En +En +Zo uA uA uA @@ -20020,19 +20028,6 @@ hT hT hT hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT PO PO PO @@ -20044,14 +20039,27 @@ PO PO PO PO -ul -ul -ul -ul -ul -ul -ul -ul +PO +PO +Wo +Wo +Zo +Zo +Zo +Wo +Wo +Wo +Zo +Zo +Zo +En +En +En +En +En +En +En +En zp zp zp @@ -20095,9 +20103,9 @@ zp zp zp zp -ul -ul -ul +En +En +En zp zp zp @@ -20119,12 +20127,12 @@ zp zp zp zp -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En uA uA uA @@ -20173,36 +20181,36 @@ hT hT hT hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT -hT PO PO -ul -ul -ul PO PO PO -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +PO +PO +PO +PO +PO +hT +hT +Zo +Zo +En +En +En +Zo +Zo +Zo +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -20273,10 +20281,10 @@ zp zp zp zp -ul -ul -ul -ul +En +En +En +En uA uA uA @@ -20326,35 +20334,35 @@ hT hT hT hT +PO +PO +PO +PO +PO hT hT hT hT hT -hT -hT -hT -hT -hT -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -20427,8 +20435,8 @@ zp zp zp zp -ul -ul +En +En uA uA uA @@ -20480,7 +20488,7 @@ hT hT hT hT -hT +PO hT hT ma @@ -20488,24 +20496,24 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -20639,23 +20647,23 @@ ma ma ma ma -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -20791,22 +20799,22 @@ ma ma ma ma -ul -ul -ul +En +En +En zp zp zp -ul -ul -ul -ul -ul -ul -ul -ul -ul -ul +En +En +En +En +En +En +En +En +En +En zp zp zp @@ -20942,8 +20950,8 @@ ma ma ma ma -ul -ul +En +En zp zp zp @@ -20951,9 +20959,9 @@ zp zp zp zp -ul -ul -ul +En +En +En zp zp zp @@ -21093,8 +21101,8 @@ ma ma ma ma -ul -ul +En +En zp zp zp @@ -21244,7 +21252,7 @@ ma ma ma ma -ul +En zp zp zp diff --git a/maps/shaded_hills/shaded_hills.dm b/maps/shaded_hills/shaded_hills.dm index 1931b81ef34..ae5cb32e946 100644 --- a/maps/shaded_hills/shaded_hills.dm +++ b/maps/shaded_hills/shaded_hills.dm @@ -5,6 +5,7 @@ #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/scaling_descriptors.dm" #include "../../mods/species/drakes/_drakes.dme" // include before _fantasy.dme so overrides work + #include "../../mods/content/item_sharpening/_item_sharpening.dme" #include "../../mods/content/fantasy/_fantasy.dme" #include "areas/_areas.dm" @@ -20,18 +21,6 @@ #include "jobs/visitors.dm" #include "jobs/wilderness.dm" - #include "submaps/_submaps.dm" - #include "submaps/downlands/_downlands.dm" - #include "submaps/grassland/_grassland.dm" - #include "submaps/swamp/_swamp.dm" - #include "submaps/woods/_woods.dm" - #include "submaps/woods/bear_den/bear_den.dm" - #include "submaps/woods/chemistry_shack/chemistry_shack.dm" - #include "submaps/woods/fairy_rings/fairy_rings.dm" - #include "submaps/woods/fox_den/fox_den.dm" - #include "submaps/woods/hunter_camp/hunter_camp.dm" - #include "submaps/woods/old_cabin/old_cabin.dm" - #include "levels/_levels.dm" #include "levels/random_map.dm" #include "levels/strata.dm" diff --git a/maps/shaded_hills/submaps/_submaps.dm b/maps/shaded_hills/submaps/_submaps.dm deleted file mode 100644 index 14b3948c927..00000000000 --- a/maps/shaded_hills/submaps/_submaps.dm +++ /dev/null @@ -1,23 +0,0 @@ -#define MAP_TEMPLATE_CATEGORY_SH_GRASSLAND "template_sh_grassland" -#define MAP_TEMPLATE_CATEGORY_SH_SWAMP "template_sh_swamp" -#define MAP_TEMPLATE_CATEGORY_SH_WOODS "template_sh_woods" -#define MAP_TEMPLATE_CATEGORY_SH_DOWNLANDS "template_sh_downlands" -#define MAP_TEMPLATE_CATEGORY_SH_DUNGEON "template_sh_dungeon" -#define MAP_TEMPLATE_CATEGORY_SH_CAVERNS "template_sh_caverns" - -/datum/map_template/shaded_hills - abstract_type = /datum/map_template/shaded_hills - template_parent_type = /datum/map_template/shaded_hills - template_flags = TEMPLATE_FLAG_CLEAR_CONTENTS | TEMPLATE_FLAG_NO_RUINS - area_usage_test_exempted_root_areas = list( - /area/shaded_hills/outside/point_of_interest - ) - var/cost = 1 - -/datum/map_template/shaded_hills/get_template_cost() - return cost - -/area/shaded_hills/outside/point_of_interest - name = "Point Of Interest" - description = null - area_blurb_category = /area/shaded_hills/outside/point_of_interest diff --git a/maps/shaded_hills/submaps/downlands/_downlands.dm b/maps/shaded_hills/submaps/downlands/_downlands.dm deleted file mode 100644 index 22721f265b8..00000000000 --- a/maps/shaded_hills/submaps/downlands/_downlands.dm +++ /dev/null @@ -1,9 +0,0 @@ -/datum/map_template/shaded_hills/downlands - abstract_type = /datum/map_template/shaded_hills/downlands - template_categories = list(MAP_TEMPLATE_CATEGORY_SH_DOWNLANDS) - template_parent_type = /datum/map_template/shaded_hills/downlands - -/datum/map_template/shaded_hills/dungeon - abstract_type = /datum/map_template/shaded_hills/dungeon - template_categories = list(MAP_TEMPLATE_CATEGORY_SH_DUNGEON) - template_parent_type = /datum/map_template/shaded_hills/dungeon diff --git a/maps/shaded_hills/submaps/grassland/_grassland.dm b/maps/shaded_hills/submaps/grassland/_grassland.dm deleted file mode 100644 index 39f0722666b..00000000000 --- a/maps/shaded_hills/submaps/grassland/_grassland.dm +++ /dev/null @@ -1,9 +0,0 @@ -/datum/map_template/shaded_hills/grassland - abstract_type = /datum/map_template/shaded_hills/grassland - template_categories = list(MAP_TEMPLATE_CATEGORY_SH_GRASSLAND) - template_parent_type = /datum/map_template/shaded_hills/grassland - -/datum/map_template/shaded_hills/cavern - abstract_type = /datum/map_template/shaded_hills/cavern - template_categories = list(MAP_TEMPLATE_CATEGORY_SH_CAVERNS) - template_parent_type = /datum/map_template/shaded_hills/cavern \ No newline at end of file diff --git a/maps/shaded_hills/submaps/swamp/_swamp.dm b/maps/shaded_hills/submaps/swamp/_swamp.dm deleted file mode 100644 index 9f4f963c1ee..00000000000 --- a/maps/shaded_hills/submaps/swamp/_swamp.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/map_template/shaded_hills/swamp - abstract_type = /datum/map_template/shaded_hills/swamp - template_categories = list(MAP_TEMPLATE_CATEGORY_SH_SWAMP) - template_parent_type = /datum/map_template/shaded_hills/swamp \ No newline at end of file diff --git a/maps/shaded_hills/submaps/woods/_woods.dm b/maps/shaded_hills/submaps/woods/_woods.dm deleted file mode 100644 index 0baea8f75c5..00000000000 --- a/maps/shaded_hills/submaps/woods/_woods.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/map_template/shaded_hills/woods - abstract_type = /datum/map_template/shaded_hills/woods - template_categories = list(MAP_TEMPLATE_CATEGORY_SH_WOODS) - template_parent_type = /datum/map_template/shaded_hills/woods diff --git a/maps/shaded_hills/submaps/woods/bear_den/bear_den.dm b/maps/shaded_hills/submaps/woods/bear_den/bear_den.dm deleted file mode 100644 index ee4d7f30849..00000000000 --- a/maps/shaded_hills/submaps/woods/bear_den/bear_den.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/map_template/shaded_hills/woods/bear_den - name = "bear den" - mappaths = list("maps/shaded_hills/submaps/woods/bear_den/bear_den.dmm") - -/area/shaded_hills/outside/point_of_interest/bear_den - name = "Point of Interest - Bear Den" diff --git a/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dm b/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dm deleted file mode 100644 index 19de9faa978..00000000000 --- a/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/map_template/shaded_hills/woods/chemistry_shack - name = "chemistry shack" - mappaths = list("maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm") - -/area/shaded_hills/outside/point_of_interest/chemistry_shack - name = "Point of Interest - Chemistry Shack" diff --git a/maps/shaded_hills/submaps/woods/fairy_rings/fairy_rings.dm b/maps/shaded_hills/submaps/woods/fairy_rings/fairy_rings.dm deleted file mode 100644 index 89ec23e18dc..00000000000 --- a/maps/shaded_hills/submaps/woods/fairy_rings/fairy_rings.dm +++ /dev/null @@ -1,15 +0,0 @@ -/datum/map_template/shaded_hills/woods/fairy_ring - name = "fairy ring" - mappaths = list("maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring.dmm") - template_flags = TEMPLATE_FLAG_CLEAR_CONTENTS | TEMPLATE_FLAG_NO_RUINS | TEMPLATE_FLAG_ALLOW_DUPLICATES | TEMPLATE_FLAG_GENERIC_REPEATABLE - template_categories = list( - MAP_TEMPLATE_CATEGORY_SH_WOODS - ) - area_coherency_test_exempt_areas = list(/area/shaded_hills/outside/point_of_interest/fairy_ring) - -/datum/map_template/shaded_hills/woods/fairy_ring/glowing - name = "glowing fairy ring" - mappaths = list("maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring_glowing.dmm") - -/area/shaded_hills/outside/point_of_interest/fairy_ring - name = "Point of Interest - Fairy Ring" diff --git a/maps/shaded_hills/submaps/woods/fox_den/fox_den.dm b/maps/shaded_hills/submaps/woods/fox_den/fox_den.dm deleted file mode 100644 index de7b5dcfd5f..00000000000 --- a/maps/shaded_hills/submaps/woods/fox_den/fox_den.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/map_template/shaded_hills/woods/fox_den - name = "fox den" - mappaths = list("maps/shaded_hills/submaps/woods/fox_den/fox_den.dmm") - -/area/shaded_hills/outside/point_of_interest/fox_den - name = "Point of Interest - Fox Den" diff --git a/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dm b/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dm deleted file mode 100644 index 3cf4e2edc68..00000000000 --- a/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/map_template/shaded_hills/woods/hunter_camp - name = "hunter camp" - mappaths = list("maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm") - -/area/shaded_hills/outside/point_of_interest/hunter_camp - name = "Point of Interest - Hunter Camp" diff --git a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dm b/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dm deleted file mode 100644 index 2d56e5161b3..00000000000 --- a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/map_template/shaded_hills/woods/old_cabin - name = "old cabin" - mappaths = list("maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm") - -/area/shaded_hills/outside/point_of_interest/old_cabin - name = "Point of Interest - Old Cabin" diff --git a/maps/tradeship/jobs/_goals.dm b/maps/tradeship/jobs/_goals.dm index e9d719bd211..ac7cc4422dc 100644 --- a/maps/tradeship/jobs/_goals.dm +++ b/maps/tradeship/jobs/_goals.dm @@ -26,7 +26,7 @@ var/global/list/tradeship_paperwork_end_areas = list() /datum/goal/department/paperwork/tradeship paperwork_types = list(/obj/item/paperwork/tradeship) signatory_job_list = list( - /datum/job/captain/tradeship, + /datum/job/standard/captain/tradeship, /datum/job/tradeship_first_mate ) diff --git a/maps/tradeship/jobs/civilian.dm b/maps/tradeship/jobs/civilian.dm index f491bc953a3..edb03def44a 100644 --- a/maps/tradeship/jobs/civilian.dm +++ b/maps/tradeship/jobs/civilian.dm @@ -1,4 +1,4 @@ -/datum/job/assistant/tradeship +/datum/job/standard/assistant/tradeship title = "Deck Hand" supervisors = "literally everyone, you bottom feeder" outfit_type = /decl/outfit/job/tradeship/hand diff --git a/maps/tradeship/jobs/command.dm b/maps/tradeship/jobs/command.dm index 0d124a9841e..3647a632760 100644 --- a/maps/tradeship/jobs/command.dm +++ b/maps/tradeship/jobs/command.dm @@ -1,4 +1,4 @@ -/datum/job/captain/tradeship +/datum/job/standard/captain/tradeship title = "Captain" supervisors = "your profit margin, your conscience, and the Trademaster" outfit_type = /decl/outfit/job/tradeship/captain @@ -11,7 +11,7 @@ ideal_character_age = 70 forced_spawnpoint = /decl/spawnpoint/cryo/captain -/datum/job/captain/tradeship/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) +/datum/job/standard/captain/tradeship/equip_job(var/mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade) . = ..() if(H) H.verbs |= /mob/proc/tradehouse_rename_ship @@ -55,6 +55,7 @@ /datum/job/tradeship_first_mate title = "First Mate" supervisors = "the Captain" + hud_icon = 'maps/tradeship/hud.dmi' outfit_type = /decl/outfit/job/tradeship/mate head_position = 1 department_types = list( diff --git a/maps/tradeship/jobs/engineering.dm b/maps/tradeship/jobs/engineering.dm index 1938f034a31..62a9c6659c4 100644 --- a/maps/tradeship/jobs/engineering.dm +++ b/maps/tradeship/jobs/engineering.dm @@ -1,10 +1,10 @@ -/datum/job/engineer/tradeship +/datum/job/standard/engineer/tradeship title = "Junior Engineer" supervisors = "the Head Engineer" outfit_type = /decl/outfit/job/tradeship/hand/engine alt_titles = list() -/datum/job/chief_engineer/tradeship +/datum/job/standard/chief_engineer/tradeship title = "Head Engineer" outfit_type = /decl/outfit/job/tradeship/chief_engineer alt_titles = list() diff --git a/maps/tradeship/jobs/medical.dm b/maps/tradeship/jobs/medical.dm index 03b480321b8..bedcf558453 100644 --- a/maps/tradeship/jobs/medical.dm +++ b/maps/tradeship/jobs/medical.dm @@ -1,4 +1,4 @@ -/datum/job/doctor/tradeship +/datum/job/standard/doctor/tradeship title = "Junior Doctor" supervisors = "the Head Doctor and the Captain" alt_titles = list() @@ -33,7 +33,7 @@ outfit_type = /decl/outfit/job/tradeship/doc/junior event_categories = list(ASSIGNMENT_MEDICAL) -/datum/job/cmo/tradeship +/datum/job/standard/cmo/tradeship title = "Head Doctor" supervisors = "the Captain and your own ethics" outfit_type = /decl/outfit/job/tradeship/doc diff --git a/maps/tradeship/jobs/science.dm b/maps/tradeship/jobs/science.dm index c14bb9e5b92..d941587b27d 100644 --- a/maps/tradeship/jobs/science.dm +++ b/maps/tradeship/jobs/science.dm @@ -1,4 +1,4 @@ -/datum/job/scientist/tradeship +/datum/job/standard/scientist/tradeship title = "Junior Researcher" supervisors = "the Head Researcher and the Captain" total_positions = 2 @@ -8,7 +8,7 @@ // Smaller crew, more points. skill_points = 24 -/datum/job/rd/tradeship +/datum/job/standard/rd/tradeship title = "Head Researcher" alt_titles = list() outfit_type = /decl/outfit/job/tradeship/hand/researcher diff --git a/maps/tradeship/tradeship-0.dmm b/maps/tradeship/tradeship-0.dmm index f68f5a6eeb9..625326ea625 100644 --- a/maps/tradeship/tradeship-0.dmm +++ b/maps/tradeship/tradeship-0.dmm @@ -1009,7 +1009,7 @@ "dN" = ( /obj/machinery/portable_atmospherics/hydroponics/soil, /obj/item/tool/hoe/mini, -/turf/floor, +/turf/floor/plating/dirt, /area/ship/trade/aft_port_underside_maint) "en" = ( /turf/floor/laminate/walnut, @@ -1179,7 +1179,7 @@ icon_state = "4-8" }, /obj/machinery/portable_atmospherics/hydroponics/soil, -/turf/floor, +/turf/floor/plating/dirt, /area/ship/trade/aft_port_underside_maint) "lv" = ( /obj/effect/decal/cleanable/dirt/visible, @@ -1197,6 +1197,10 @@ /obj/item/stack/cable_coil/random/three, /turf/floor, /area/ship/trade/loading_bay) +"lG" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "ne" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -1227,11 +1231,11 @@ /area/ship/trade/fore_port_underside_maint) "oo" = ( /obj/machinery/portable_atmospherics/hydroponics/soil, -/turf/floor, +/turf/floor/plating/dirt, /area/ship/trade/aft_starboard_underside_maint) "or" = ( /obj/machinery/portable_atmospherics/hydroponics/soil, -/turf/floor, +/turf/floor/plating/dirt, /area/ship/trade/aft_port_underside_maint) "os" = ( /obj/structure/lattice, @@ -1479,7 +1483,7 @@ /obj/machinery/light{ dir = 4 }, -/turf/floor, +/turf/floor/plating/dirt, /area/ship/trade/aft_port_underside_maint) "xP" = ( /obj/structure/disposalpipe/segment{ @@ -2049,7 +2053,7 @@ /obj/machinery/light{ dir = 4 }, -/turf/floor, +/turf/floor/plating/dirt, /area/ship/trade/aft_starboard_underside_maint) "QW" = ( /obj/item/stack/tile/floor/five, @@ -3507,7 +3511,7 @@ aa aa aa aa -aa +lG aa aa aa @@ -7360,7 +7364,7 @@ aa aa aa aa -aa +lG aa aa aa diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index 8a068ace688..fd28e95fb89 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -1763,6 +1763,10 @@ }, /turf/floor/plating, /area/ship/trade/maintenance/eva) +"eH" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "eJ" = ( /obj/machinery/vending/materials{ dir = 4 @@ -4589,7 +4593,7 @@ aa aa aa aa -aa +eH aa aa aa @@ -8689,7 +8693,7 @@ aa aa aa aa -aa +eH aa aa aa diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 2eb597a678c..0084e4d8d71 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -1282,7 +1282,7 @@ /obj/machinery/recharger, /obj/structure/sign/poster{ pixel_x = 32; - dir = 8 + dir = 4 }, /obj/item/plate/tray, /obj/item/circular_saw, @@ -2002,6 +2002,15 @@ /obj/structure/cable{ icon_state = "4-8" }, +/obj/item/chems/glass/beaker{ + pixel_x = 5 + }, +/obj/item/chems/condiment/small/peppermill{ + pixel_x = 3 + }, +/obj/item/chems/condiment/enzyme, +/obj/item/chems/glass/rag, +/obj/item/chems/cooking_vessel/pot, /turf/floor/tiled, /area/ship/trade/crew/kitchen) "eQ" = ( @@ -4684,14 +4693,8 @@ "nH" = ( /obj/structure/table, /obj/effect/floor_decal/corner/red/diagonal, -/obj/item/chems/glass/beaker{ - pixel_x = 5 - }, -/obj/item/chems/condiment/small/peppermill{ - pixel_x = 3 - }, -/obj/item/chems/condiment/enzyme, -/obj/item/chems/glass/rag, +/obj/machinery/reagent_temperature, +/obj/item/chems/cooking_vessel/skillet, /turf/floor/tiled, /area/ship/trade/crew/kitchen) "nR" = ( @@ -5412,6 +5415,10 @@ }, /turf/floor/plating/airless, /area/ship/trade/maintenance/engine/aft) +"vN" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "vP" = ( /obj/machinery/light{ dir = 8; @@ -9045,7 +9052,7 @@ aa aa aa aa -aa +vN aa aa aa @@ -9259,7 +9266,7 @@ aa aa aa aa -aa +vN aa aa aa @@ -13196,7 +13203,7 @@ aa aa aa aa -aa +vN aa aa aa @@ -13639,7 +13646,7 @@ aa aa aa aa -aa +vN aa aa aa diff --git a/maps/tradeship/tradeship-3.dmm b/maps/tradeship/tradeship-3.dmm index 1a4f81e3fb3..bc596cfeecd 100644 --- a/maps/tradeship/tradeship-3.dmm +++ b/maps/tradeship/tradeship-3.dmm @@ -1181,6 +1181,10 @@ /obj/machinery/commsrelay, /turf/floor/bluegrid, /area/ship/trade/comms) +"UX" = ( +/obj/effect/shuttle_landmark/automatic, +/turf/space, +/area/space) "UY" = ( /obj/structure/cable/yellow{ icon_state = "1-2" @@ -2382,7 +2386,7 @@ aa aa aa aa -aa +UX aa aa aa @@ -6665,7 +6669,7 @@ aa aa aa aa -aa +UX aa aa aa diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 3815dcc7845..d747204cc30 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -4,43 +4,43 @@ #include "../../code/unit_tests/offset_tests.dm" #endif - #include "../../mods/gamemodes/cult/_cult.dme" - #include "../../mods/gamemodes/heist/_heist.dme" - #include "../../mods/gamemodes/ninja/_ninja.dme" - #include "../../mods/gamemodes/revolution/_revolution.dme" - #include "../../mods/gamemodes/traitor/_traitor.dme" - #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" - #include "../../mods/gamemodes/mixed/_mixed.dme" + #include "../../mods/content/tabloids/_tabloids.dme" #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" #include "../../mods/content/government/away_sites/icarus/icarus.dm" #include "../../mods/content/corporate/away_sites/lar_maria/lar_maria.dm" - #include "../../mods/content/standard_jobs/_standard_jobs.dme" - #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/mundane.dm" #include "../../mods/content/baychems/_baychems.dme" + #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/corporate/_corporate.dme" + #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/government/_government.dme" + #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" - #include "../../mods/content/scaling_descriptors.dm" - #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/pheromones/_pheromones.dme" + #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/standard_jobs/_standard_jobs.dme" + #include "../../mods/content/supermatter/_supermatter.dme" + #include "../../mods/content/xenobiology/_xenobiology.dme" - #include "../../mods/mobs/dionaea/_dionaea.dme" - #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/gamemodes/cult/_cult.dme" + #include "../../mods/gamemodes/heist/_heist.dme" + #include "../../mods/gamemodes/ninja/_ninja.dme" + #include "../../mods/gamemodes/revolution/_revolution.dme" + #include "../../mods/gamemodes/spyvspy/_spyvspy.dme" + #include "../../mods/gamemodes/traitor/_traitor.dme" + #include "../../mods/gamemodes/mixed.dm" - // Must come after borers for compatibility. - #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/mobs/borers/_borers.dme" + #include "../../mods/mobs/dionaea/_dionaea.dme" + #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/drakes/_drakes.dme" // #include "../../mods/species/utility_frames/_utility_frames.dme" - #include "../../mods/species/neoavians/_neoavians.dme" - #include "../../mods/species/bayliens/_bayliens.dme" #include "../../mods/species/vox/_vox.dme" #include "../../mods/content/polaris/_polaris.dme" diff --git a/maps/tradeship/tradeship_jobs.dm b/maps/tradeship/tradeship_jobs.dm index 61b526624e0..71ba7143cc3 100644 --- a/maps/tradeship/tradeship_jobs.dm +++ b/maps/tradeship/tradeship_jobs.dm @@ -1,19 +1,18 @@ /datum/map/tradeship - default_job_type = /datum/job/assistant/tradeship + default_job_type = /datum/job/standard/assistant/tradeship default_department_type = /decl/department/civilian default_law_type = /datum/ai_laws/corporate - id_hud_icons = 'maps/tradeship/hud.dmi' allowed_jobs = list( - /datum/job/assistant/tradeship, - /datum/job/captain/tradeship, + /datum/job/standard/assistant/tradeship, + /datum/job/standard/captain/tradeship, /datum/job/tradeship_first_mate, - /datum/job/cmo/tradeship, - /datum/job/doctor/tradeship, - /datum/job/chief_engineer/tradeship, - /datum/job/engineer/tradeship, - /datum/job/rd/tradeship, - /datum/job/scientist/tradeship, - /datum/job/robot + /datum/job/standard/cmo/tradeship, + /datum/job/standard/doctor/tradeship, + /datum/job/standard/chief_engineer/tradeship, + /datum/job/standard/engineer/tradeship, + /datum/job/standard/rd/tradeship, + /datum/job/standard/scientist/tradeship, + /datum/job/standard/robot ) /obj/machinery/suit_cycler/tradeship diff --git a/maps/tradeship/tradeship_spawnpoints.dm b/maps/tradeship/tradeship_spawnpoints.dm index c4be5d21c35..36e4d8acdb7 100644 --- a/maps/tradeship/tradeship_spawnpoints.dm +++ b/maps/tradeship/tradeship_spawnpoints.dm @@ -9,7 +9,7 @@ /decl/spawnpoint/cryo name = "Port Cryogenic Storage" spawn_announcement = "has completed revival in the port cryogenics bay" - disallow_job = list(/datum/job/robot) + disallow_job = list(/datum/job/standard/robot) /decl/spawnpoint/cryo/two name = "Starboard Cryogenic Storage" @@ -22,7 +22,7 @@ /decl/spawnpoint/cryo/captain name = "Captain Compartment" spawn_announcement = "has completed revival in the captain compartment" - restrict_job = list(/datum/job/captain/tradeship) + restrict_job = list(/datum/job/standard/captain/tradeship) uid = "spawn_cryo_captain" /obj/abstract/landmark/latejoin/cryo_captain diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index 46850d244ed..ee1696d6e61 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -1,21 +1,30 @@ -var/global/datum/map/using_map = new USING_MAP_DATUM -var/global/list/all_maps = list() +var/global/datum/map/using_map = new USING_MAP_DATUM +var/global/list/all_maps = list() +var/global/list/votable_maps = list() var/global/const/MAP_HAS_BRANCH = 1 //Branch system for occupations, togglable -var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable +var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable /proc/initialise_map_list() - for(var/type in subtypesof(/datum/map)) - var/datum/map/M - if(type == global.using_map.type) - M = global.using_map - M.setup_map() - else - M = new type - if(!M.path) - log_error("Map '[M]' ([type]) does not have a defined path, not adding to map list!") + for(var/map_type in subtypesof(/datum/map)) + + var/datum/map/map_instance = map_type + if(TYPE_IS_ABSTRACT(map_instance)) + continue + + if(map_type == global.using_map.type) + map_instance = global.using_map + map_instance.setup_map() + else if(map_instance::path) + map_instance = new map_instance else - global.all_maps[M.path] = M + log_error("Map '[map_type]' does not have a defined path, not adding to map list!") + continue + + global.all_maps[map_instance.path] = map_instance + if(map_instance.votable) + global.votable_maps[map_instance.path] = map_instance + return 1 /datum/map @@ -104,7 +113,9 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable var/default_law_type = /datum/ai_laws/asimov // The default lawset use by synth units, if not overriden by their laws var. var/security_state = /decl/security_state/default // The default security state system to use. - var/id_hud_icons = 'icons/mob/hud.dmi' // Used by the ID HUD (primarily sechud) overlay. + var/hud_icons = 'icons/screen/hud.dmi' // Used by the ID HUD (primarily sechud) overlay. + var/implant_hud_icons = 'icons/screen/hud_implants.dmi' + var/med_hud_icons = 'icons/screen/hud_med.dmi' var/num_exoplanets = 0 var/force_exoplanet_type // Used to override exoplanet weighting and always pick the same exoplanet. @@ -127,6 +138,9 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable var/default_species = SPECIES_HUMAN + // Can this map be voted for by players? + var/votable = TRUE + var/list/available_background_info = list( /decl/background_category/homeworld = list(/decl/background_detail/location/other), /decl/background_category/faction = list(/decl/background_detail/faction/other), @@ -188,6 +202,12 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable ) + var/default_ui_style + +/datum/map/New() + ..() + default_ui_style ||= DEFAULT_UI_STYLE + /datum/map/proc/get_background_categories() if(!background_categories_generated) if(isnull(_background_categories)) @@ -262,10 +282,9 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable if(!allowed_jobs) allowed_jobs = list() - for(var/jtype in subtypesof(/datum/job)) - var/datum/job/job = jtype - if(initial(job.available_by_default)) - allowed_jobs += jtype + for(var/datum/job/job as anything in subtypesof(/datum/job)) + if(!TYPE_IS_ABSTRACT(job) && job::available_by_default) + allowed_jobs += job if(ispath(default_job_type, /datum/job)) var/datum/job/J = default_job_type @@ -534,5 +553,24 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable /datum/map/proc/finalize_map_generation() return +/datum/map/proc/validate() + . = TRUE + if(!length(SSmapping.player_levels)) + log_error("[name] has no player levels!") + . = FALSE + if(!length(SSmapping.station_levels)) + log_error("[name] has no station levels!") + . = FALSE + // TODO: add an admin level loaded from template for maps like tradeship (generic admin level modpack?) + /* + if(!length(SSmapping.admin_levels)) + log_error("[name] has no admin levels!") + . = FALSE + */ + if(!length(SSmapping.contact_levels)) + log_error("[name] has no contact levels!") + . = FALSE + /datum/map/proc/get_available_submap_archetypes() return decls_repository.get_decls_of_subtype_unassociated(/decl/submap_archetype) + diff --git a/mods/_modpack.dm b/mods/_modpack.dm index 81c16e69f7a..0fb1d5d7908 100644 --- a/mods/_modpack.dm +++ b/mods/_modpack.dm @@ -8,7 +8,11 @@ var/secrets_directory /// The folder to load additional NanoUI templates from. Must be relative to the DME's location (root game folder). var/nanoui_directory - var/list/dreams //! A list of strings to be added to the random dream proc. + + var/list/dreams //! A list of strings to be added to the random dream proc. + + var/list/tabloid_headlines //! A list of headline and article data used by the tabloids modpack. + var/list/tabloid_publishers //! A list of name strings used by the tabloids modpack. var/list/credits_other //! A list of strings that are used by the end of round credits roll. var/list/credits_adventure_names //! A list of strings that are used by the end of round credits roll. diff --git a/mods/content/fantasy/_fantasy.dme b/mods/content/fantasy/_fantasy.dme index 2dcfaa8cde5..87b99cb04e9 100644 --- a/mods/content/fantasy/_fantasy.dme +++ b/mods/content/fantasy/_fantasy.dme @@ -37,5 +37,16 @@ #include "items\clothing\loincloth.dm" #include "items\clothing\trousers.dm" #include "props\signpost.dm" +#include "submaps\_submaps.dm" +#include "submaps\downlands\_downlands.dm" +#include "submaps\grassland\_grassland.dm" +#include "submaps\swamp\_swamp.dm" +#include "submaps\woods\_woods.dm" +#include "submaps\woods\bear_den\bear_den.dm" +#include "submaps\woods\chemistry_shack\chemistry_shack.dm" +#include "submaps\woods\fairy_rings\fairy_rings.dm" +#include "submaps\woods\fox_den\fox_den.dm" +#include "submaps\woods\hunter_camp\hunter_camp.dm" +#include "submaps\woods\old_cabin\old_cabin.dm" // END_INCLUDE #endif diff --git a/mods/content/fantasy/datum/hnoll/bodytypes.dm b/mods/content/fantasy/datum/hnoll/bodytypes.dm index 218068362ce..1edda9e8e99 100644 --- a/mods/content/fantasy/datum/hnoll/bodytypes.dm +++ b/mods/content/fantasy/datum/hnoll/bodytypes.dm @@ -16,6 +16,7 @@ base_color = "#ae7d32" base_eye_color = "#00aa00" uid = "bodytype_hnoll" + footprints_icon = 'icons/mob/footprints/footprints_paw.dmi' default_sprite_accessories = list( SAC_HAIR = list( diff --git a/mods/content/fantasy/datum/hnoll/species.dm b/mods/content/fantasy/datum/hnoll/species.dm index 31ebc61a61e..8bb1cd7496e 100644 --- a/mods/content/fantasy/datum/hnoll/species.dm +++ b/mods/content/fantasy/datum/hnoll/species.dm @@ -30,13 +30,6 @@ move_trail = /obj/effect/decal/cleanable/blood/tracks/paw base_external_prosthetics_model = null - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch, - /decl/natural_attack/bite/sharp - ) - available_background_info = list( /decl/background_category/homeworld = list( /decl/background_detail/location/fantasy, diff --git a/mods/content/fantasy/datum/overrides.dm b/mods/content/fantasy/datum/overrides.dm index 0726866427d..08b4b54f4de 100644 --- a/mods/content/fantasy/datum/overrides.dm +++ b/mods/content/fantasy/datum/overrides.dm @@ -31,28 +31,33 @@ /decl/background_category/religion = /decl/background_detail/religion/other ) -// Rename grafadreka -/decl/species/grafadreka - name = "Meredrake" - name_plural = "Meredrakes" - description = "Meredrakes, sometimes called mire-drakes, are large reptillian pack predators, widely assumed to be cousins to true dragons. \ - They are commonly found living in caves or burrows bordering grassland or forest, and while they prefer to hunt deer or rabbits, they will sometimes attack travellers if pickings are slim enough. \ - While they are not domesticated, they can be habituated and trained as working animals if captured young enough." - -/decl/sprite_accessory/marking/grafadreka - species_allowed = list("Meredrake") - -/decl/language/grafadreka - desc = "Hiss hiss, feed me rabbits." - -/decl/material/liquid/sifsap - name = "drake spittle" - lore_text = "A complex chemical slurry brewed up in the gullet of meredrakes." - -/obj/aura/sifsap_salve - name = "Drakespittle Salve" - descriptor = "glowing spittle" - // Rename wooden prostheses /decl/bodytype/prosthetic/wooden name = "carved wooden" // weird to call it 'crude' when it's cutting-edge for the setting + +// Just some fun overrides for when robot debris shows up in maint. +/obj/effect/decal/cleanable/blood/gibs/robot + name = "mysterious debris" + desc = "Some kind of complex, oily detritus. What could it be?" + +/obj/item/remains/robot + name = "mysterious remains" + desc = "The oily remains of some complex, metallic object. What could they be from?" + +// Override to remove non-fantasy stuff. +/obj/random/trash/spawn_choices() + var/static/list/spawnable_choices = list( + /obj/item/remains/lizard, + /obj/effect/decal/cleanable/blood/gibs/robot, + /obj/effect/decal/cleanable/spiderling_remains, + /obj/item/remains/mouse, + /obj/effect/decal/cleanable/vomit, + /obj/effect/decal/cleanable/blood/splatter, + /obj/effect/decal/cleanable/ash, + /obj/effect/decal/cleanable/generic, + /obj/effect/decal/cleanable/flour, + /obj/effect/decal/cleanable/filth, + /obj/effect/decal/cleanable/dirt/visible, + /obj/item/remains/robot + ) + return spawnable_choices diff --git a/mods/content/fantasy/icons/areas.dmi b/mods/content/fantasy/icons/areas.dmi new file mode 100644 index 00000000000..9849d6f87d8 Binary files /dev/null and b/mods/content/fantasy/icons/areas.dmi differ diff --git a/mods/content/fantasy/submaps/_submaps.dm b/mods/content/fantasy/submaps/_submaps.dm new file mode 100644 index 00000000000..64c03240f4b --- /dev/null +++ b/mods/content/fantasy/submaps/_submaps.dm @@ -0,0 +1,52 @@ +#define MAP_TEMPLATE_CATEGORY_FANTASY_GRASSLAND "template_fantasy_grassland" +#define MAP_TEMPLATE_CATEGORY_FANTASY_SWAMP "template_fantasy_swamp" +#define MAP_TEMPLATE_CATEGORY_FANTASY_WOODS "template_fantasy_woods" +#define MAP_TEMPLATE_CATEGORY_FANTASY_DOWNLANDS "template_fantasy_downlands" +#define MAP_TEMPLATE_CATEGORY_FANTASY_DUNGEON "template_fantasy_dungeon" +#define MAP_TEMPLATE_CATEGORY_FANTASY_CAVERNS "template_fantasy_caverns" + +/datum/map_template/fantasy + abstract_type = /datum/map_template/fantasy + template_flags = TEMPLATE_FLAG_CLEAR_CONTENTS | TEMPLATE_FLAG_NO_RUINS + area_usage_test_exempted_root_areas = list( + /area/fantasy/outside/point_of_interest + ) + var/cost = 1 + +/datum/map_template/fantasy/get_template_cost() + return cost + +/datum/map/New() + LAZYSET(apc_test_exempt_areas, /area/fantasy, NO_SCRUBBER|NO_VENT|NO_APC) + ..() + +/area/fantasy + abstract_type = /area/fantasy + allow_xenoarchaeology_finds = FALSE + icon = 'mods/content/fantasy/icons/areas.dmi' + icon_state = "area" + base_turf = /turf/floor/rock/basalt + sound_env = GENERIC + ambience = list() + +/area/fantasy/outside + name = "\improper Wilderness" + abstract_type = /area/fantasy/outside + color = COLOR_GREEN + is_outside = OUTSIDE_YES + sound_env = PLAIN + ambience = list( + 'sound/effects/wind/wind_2_1.ogg', + 'sound/effects/wind/wind_2_2.ogg', + 'sound/effects/wind/wind_3_1.ogg', + 'sound/effects/wind/wind_4_1.ogg', + 'sound/effects/wind/wind_4_2.ogg', + 'sound/effects/wind/wind_5_1.ogg' + ) + interior_ambient_light_modifier = -0.3 + area_flags = AREA_FLAG_EXTERNAL | AREA_FLAG_IS_BACKGROUND + +/area/fantasy/outside/point_of_interest + name = "Point Of Interest" + description = null + area_blurb_category = /area/fantasy/outside/point_of_interest diff --git a/mods/content/fantasy/submaps/downlands/_downlands.dm b/mods/content/fantasy/submaps/downlands/_downlands.dm new file mode 100644 index 00000000000..5086d54dc54 --- /dev/null +++ b/mods/content/fantasy/submaps/downlands/_downlands.dm @@ -0,0 +1,7 @@ +/datum/map_template/fantasy/downlands + abstract_type = /datum/map_template/fantasy/downlands + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_DOWNLANDS) + +/datum/map_template/fantasy/dungeon + abstract_type = /datum/map_template/fantasy/dungeon + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_DUNGEON) diff --git a/mods/content/fantasy/submaps/grassland/_grassland.dm b/mods/content/fantasy/submaps/grassland/_grassland.dm new file mode 100644 index 00000000000..19b70b9367f --- /dev/null +++ b/mods/content/fantasy/submaps/grassland/_grassland.dm @@ -0,0 +1,7 @@ +/datum/map_template/fantasy/grassland + abstract_type = /datum/map_template/fantasy/grassland + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_GRASSLAND) + +/datum/map_template/fantasy/cavern + abstract_type = /datum/map_template/fantasy/cavern + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_CAVERNS) \ No newline at end of file diff --git a/mods/content/fantasy/submaps/swamp/_swamp.dm b/mods/content/fantasy/submaps/swamp/_swamp.dm new file mode 100644 index 00000000000..6a3c2054ea9 --- /dev/null +++ b/mods/content/fantasy/submaps/swamp/_swamp.dm @@ -0,0 +1,3 @@ +/datum/map_template/fantasy/swamp + abstract_type = /datum/map_template/fantasy/swamp + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_SWAMP) \ No newline at end of file diff --git a/mods/content/fantasy/submaps/woods/_woods.dm b/mods/content/fantasy/submaps/woods/_woods.dm new file mode 100644 index 00000000000..dc1429ef559 --- /dev/null +++ b/mods/content/fantasy/submaps/woods/_woods.dm @@ -0,0 +1,3 @@ +/datum/map_template/fantasy/woods + abstract_type = /datum/map_template/fantasy/woods + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_WOODS) diff --git a/mods/content/fantasy/submaps/woods/bear_den/bear_den.dm b/mods/content/fantasy/submaps/woods/bear_den/bear_den.dm new file mode 100644 index 00000000000..cb4da036c7c --- /dev/null +++ b/mods/content/fantasy/submaps/woods/bear_den/bear_den.dm @@ -0,0 +1,6 @@ +/datum/map_template/fantasy/woods/bear_den + name = "bear den" + mappaths = list("mods/content/fantasy/submaps/woods/bear_den/bear_den.dmm") + +/area/fantasy/outside/point_of_interest/bear_den + name = "Point of Interest - Bear Den" diff --git a/maps/shaded_hills/submaps/woods/bear_den/bear_den.dmm b/mods/content/fantasy/submaps/woods/bear_den/bear_den.dmm similarity index 76% rename from maps/shaded_hills/submaps/woods/bear_den/bear_den.dmm rename to mods/content/fantasy/submaps/woods/bear_den/bear_den.dmm index 01ac1b5988a..021df578e9b 100644 --- a/maps/shaded_hills/submaps/woods/bear_den/bear_den.dmm +++ b/mods/content/fantasy/submaps/woods/bear_den/bear_den.dmm @@ -8,19 +8,19 @@ "k" = ( /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "n" = ( /turf/floor/rock/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "o" = ( /turf/floor/grass, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "p" = ( /turf/floor/grass, /area/template_noop) "r" = ( /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "w" = ( /obj/item/bladed/knife, /obj/item/cash/imperial/crown, @@ -29,20 +29,20 @@ /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/floor/rock/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "y" = ( /obj/abstract/exterior_marker/inside, /turf/floor/rock/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "z" = ( /obj/abstract/exterior_marker/inside, /turf/wall/natural/dirt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "C" = ( /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/floor/rock/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "G" = ( /obj/item/bone/skull/deer, /obj/item/food/butchery/stomach, @@ -50,7 +50,7 @@ /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "J" = ( /turf/floor/grass/wild, /area/template_noop) @@ -61,16 +61,16 @@ /mob/living/simple_animal/hostile/bear, /obj/abstract/exterior_marker/inside, /turf/floor/rock/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "O" = ( /obj/abstract/exterior_marker/inside, /turf/wall/natural/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "U" = ( /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/wall/natural/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "V" = ( /obj/item/cash/imperial/quin, /obj/item/cash/imperial/quin, @@ -79,12 +79,12 @@ /obj/item/cash/imperial/crown, /obj/abstract/exterior_marker/inside, /turf/floor/rock/basalt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) "W" = ( /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/wall/natural/dirt, -/area/shaded_hills/outside/point_of_interest/bear_den) +/area/fantasy/outside/point_of_interest/bear_den) (1,1,1) = {" K diff --git a/mods/content/fantasy/submaps/woods/chemistry_shack/chemistry_shack.dm b/mods/content/fantasy/submaps/woods/chemistry_shack/chemistry_shack.dm new file mode 100644 index 00000000000..158824c417b --- /dev/null +++ b/mods/content/fantasy/submaps/woods/chemistry_shack/chemistry_shack.dm @@ -0,0 +1,6 @@ +/datum/map_template/fantasy/woods/chemistry_shack + name = "chemistry shack" + mappaths = list("mods/content/fantasy/submaps/woods/chemistry_shack/chemistry_shack.dmm") + +/area/fantasy/outside/point_of_interest/chemistry_shack + name = "Point of Interest - Chemistry Shack" diff --git a/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm b/mods/content/fantasy/submaps/woods/chemistry_shack/chemistry_shack.dmm similarity index 75% rename from maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm rename to mods/content/fantasy/submaps/woods/chemistry_shack/chemistry_shack.dmm index fa4f65d3fd7..6ed42c83589 100644 --- a/maps/shaded_hills/submaps/woods/chemistry_shack/chemistry_shack.dmm +++ b/mods/content/fantasy/submaps/woods/chemistry_shack/chemistry_shack.dmm @@ -5,7 +5,7 @@ dir = 4 }, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "h" = ( /obj/abstract/exterior_marker/inside, /obj/item/rock/hematite{ @@ -33,7 +33,7 @@ }, /obj/structure/table/desk/ebony, /turf/floor/path/herringbone/basalt, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "k" = ( /obj/abstract/exterior_marker/inside, /obj/structure/table/wood/ebony, @@ -41,12 +41,12 @@ /obj/item/chems/glass/handmade/teapot, /obj/abstract/landmark/organize/vertical, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "m" = ( /obj/abstract/exterior_marker/inside, /obj/structure/reagent_dispensers/barrel/ebony/oil, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "n" = ( /obj/structure/wall_sconce/lantern{ dir = 1; @@ -60,7 +60,7 @@ /obj/item/chems/glass/mortar, /obj/item/rock/basalt, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "q" = ( /obj/structure/drying_rack/ebony, /turf/floor/dirt, @@ -69,11 +69,11 @@ /obj/abstract/exterior_marker/inside, /obj/structure/filter_stand/mapped, /turf/floor/path/herringbone/basalt, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "s" = ( /obj/abstract/exterior_marker/inside, /turf/wall/log/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "z" = ( /turf/template_noop, /area/template_noop) @@ -81,30 +81,30 @@ /obj/abstract/exterior_marker/inside, /obj/structure/door/walnut, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "F" = ( /obj/abstract/exterior_marker/inside, /turf/wall/brick/basalt, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "H" = ( /obj/abstract/exterior_marker/inside, /turf/wall/brick/basalt/shutter, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "I" = ( /obj/abstract/exterior_marker/inside, /obj/structure/reagent_dispensers/barrel/ebony/water, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "J" = ( /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "K" = ( /obj/abstract/exterior_marker/inside, /obj/structure/bed/simple/ebony/cloth, /obj/item/bedsheet/furs, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "L" = ( /obj/abstract/exterior_marker/inside, /obj/structure/table/wood/ebony, @@ -115,22 +115,22 @@ /obj/item/food/grown/carrot, /obj/item/kitchen/rollingpin, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "M" = ( /obj/abstract/exterior_marker/inside, /obj/structure/reagent_dispensers/barrel/ebony/wine, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "N" = ( /obj/abstract/exterior_marker/inside, /turf/floor/path/herringbone/basalt, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "R" = ( /obj/abstract/exterior_marker/inside, /obj/structure/closet/crate/chest/ebony, /obj/random/jewelry, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "U" = ( /obj/abstract/exterior_marker/inside, /obj/structure/wall_sconce/lantern{ @@ -149,19 +149,19 @@ pixel_y = 9 }, /turf/floor/path/herringbone/basalt, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "W" = ( /turf/floor/dirt, /area/template_noop) "X" = ( /obj/abstract/exterior_marker/inside, /turf/wall/log/walnut/shutter, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) "Y" = ( /obj/abstract/exterior_marker/inside, /obj/structure/fire_source/stove, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/chemistry_shack) +/area/fantasy/outside/point_of_interest/chemistry_shack) (1,1,1) = {" W diff --git a/maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring.dmm b/mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring.dmm similarity index 75% rename from maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring.dmm rename to mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring.dmm index b3c1314f552..2611fe47b34 100644 --- a/maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring.dmm +++ b/mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring.dmm @@ -1,14 +1,14 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( /turf/floor/fake_grass, -/area/shaded_hills/outside/point_of_interest/fairy_ring) +/area/fantasy/outside/point_of_interest/fairy_ring) "w" = ( /obj/structure/flora/plant/random_mushroom, /turf/floor/fake_grass, -/area/shaded_hills/outside/point_of_interest/fairy_ring) +/area/fantasy/outside/point_of_interest/fairy_ring) "D" = ( /turf/floor/grass/wild, -/area/shaded_hills/outside/point_of_interest/fairy_ring) +/area/fantasy/outside/point_of_interest/fairy_ring) "N" = ( /turf/template_noop, /area/template_noop) diff --git a/maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring_glowing.dmm b/mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring_glowing.dmm similarity index 75% rename from maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring_glowing.dmm rename to mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring_glowing.dmm index bbd7d2ae036..b86a4adcb0f 100644 --- a/maps/shaded_hills/submaps/woods/fairy_rings/fairy_ring_glowing.dmm +++ b/mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring_glowing.dmm @@ -4,14 +4,14 @@ /area/template_noop) "h" = ( /turf/floor/grass, -/area/shaded_hills/outside/point_of_interest/fairy_ring) +/area/fantasy/outside/point_of_interest/fairy_ring) "j" = ( /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/fairy_ring) +/area/fantasy/outside/point_of_interest/fairy_ring) "A" = ( /obj/structure/flora/plant/random_mushroom/glowing, /turf/floor/grass, -/area/shaded_hills/outside/point_of_interest/fairy_ring) +/area/fantasy/outside/point_of_interest/fairy_ring) (1,1,1) = {" a diff --git a/mods/content/fantasy/submaps/woods/fairy_rings/fairy_rings.dm b/mods/content/fantasy/submaps/woods/fairy_rings/fairy_rings.dm new file mode 100644 index 00000000000..76140bdb759 --- /dev/null +++ b/mods/content/fantasy/submaps/woods/fairy_rings/fairy_rings.dm @@ -0,0 +1,15 @@ +/datum/map_template/fantasy/woods/fairy_ring + name = "fairy ring" + mappaths = list("mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring.dmm") + template_flags = TEMPLATE_FLAG_CLEAR_CONTENTS | TEMPLATE_FLAG_NO_RUINS | TEMPLATE_FLAG_ALLOW_DUPLICATES | TEMPLATE_FLAG_GENERIC_REPEATABLE + template_categories = list( + MAP_TEMPLATE_CATEGORY_FANTASY_WOODS + ) + area_coherency_test_exempt_areas = list(/area/fantasy/outside/point_of_interest/fairy_ring) + +/datum/map_template/fantasy/woods/fairy_ring/glowing + name = "glowing fairy ring" + mappaths = list("mods/content/fantasy/submaps/woods/fairy_rings/fairy_ring_glowing.dmm") + +/area/fantasy/outside/point_of_interest/fairy_ring + name = "Point of Interest - Fairy Ring" diff --git a/mods/content/fantasy/submaps/woods/fox_den/fox_den.dm b/mods/content/fantasy/submaps/woods/fox_den/fox_den.dm new file mode 100644 index 00000000000..894f6f7fb50 --- /dev/null +++ b/mods/content/fantasy/submaps/woods/fox_den/fox_den.dm @@ -0,0 +1,6 @@ +/datum/map_template/fantasy/woods/fox_den + name = "fox den" + mappaths = list("mods/content/fantasy/submaps/woods/fox_den/fox_den.dmm") + +/area/fantasy/outside/point_of_interest/fox_den + name = "Point of Interest - Fox Den" diff --git a/maps/shaded_hills/submaps/woods/fox_den/fox_den.dmm b/mods/content/fantasy/submaps/woods/fox_den/fox_den.dmm similarity index 82% rename from maps/shaded_hills/submaps/woods/fox_den/fox_den.dmm rename to mods/content/fantasy/submaps/woods/fox_den/fox_den.dmm index 6e1c6e5ce94..8053ead1e47 100644 --- a/maps/shaded_hills/submaps/woods/fox_den/fox_den.dmm +++ b/mods/content/fantasy/submaps/woods/fox_den/fox_den.dmm @@ -9,7 +9,7 @@ /mob/living/simple_animal/passive/fox, /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) "x" = ( /turf/floor/grass/wild, /area/template_noop) @@ -23,7 +23,7 @@ /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) "B" = ( /turf/floor/grass, /area/template_noop) @@ -31,32 +31,32 @@ /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/wall/natural/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) "O" = ( /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) "P" = ( /obj/item/food/butchery/offal/small, /obj/item/stack/material/bone/mapped/bone, /obj/abstract/exterior_marker/inside, /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) "S" = ( /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) "W" = ( /obj/abstract/exterior_marker/inside, /turf/wall/natural/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) "Y" = ( /obj/item/seeds/extracted/foxglove, /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/fox_den) +/area/fantasy/outside/point_of_interest/fox_den) (1,1,1) = {" n diff --git a/mods/content/fantasy/submaps/woods/hunter_camp/hunter_camp.dm b/mods/content/fantasy/submaps/woods/hunter_camp/hunter_camp.dm new file mode 100644 index 00000000000..262e1b71d55 --- /dev/null +++ b/mods/content/fantasy/submaps/woods/hunter_camp/hunter_camp.dm @@ -0,0 +1,6 @@ +/datum/map_template/fantasy/woods/hunter_camp + name = "hunter camp" + mappaths = list("mods/content/fantasy/submaps/woods/hunter_camp/hunter_camp.dmm") + +/area/fantasy/outside/point_of_interest/hunter_camp + name = "Point of Interest - Hunter Camp" diff --git a/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm b/mods/content/fantasy/submaps/woods/hunter_camp/hunter_camp.dmm similarity index 69% rename from maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm rename to mods/content/fantasy/submaps/woods/hunter_camp/hunter_camp.dmm index 0d8357987fe..b269d8d4cdd 100644 --- a/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm +++ b/mods/content/fantasy/submaps/woods/hunter_camp/hunter_camp.dmm @@ -1,93 +1,93 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( /turf/floor/grass/wild, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "b" = ( /obj/structure/drying_rack, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "e" = ( /turf/floor/grass, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "h" = ( /obj/structure/flora/tree/hardwood/ebony, /turf/floor/grass, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "j" = ( /obj/item/stack/material/skin/mapped/leather/twenty, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "k" = ( /obj/item/food/butchery/offal/beef, /obj/item/food/butchery/offal/small/beef, /obj/structure/pit, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "n" = ( /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "q" = ( /obj/structure/closet/crate/chest/ebony, /obj/item/food/butchery/haunch/shoulder/beef, /obj/item/food/butchery/haunch/side/beef, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "A" = ( /obj/abstract/exterior_marker/inside, /turf/wall/log/walnut, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "B" = ( /obj/structure/flora/stump/tree/ebony, /turf/floor/grass/wild, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "C" = ( /obj/structure/flora/stump/tree/ebony, /turf/floor/grass, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "D" = ( /obj/abstract/exterior_marker/inside, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "E" = ( /obj/structure/reagent_dispensers/barrel/ebony/water, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "F" = ( /obj/structure/drying_rack, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "I" = ( /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "J" = ( /obj/structure/flora/tree/hardwood/ebony, /turf/floor/grass/wild, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "K" = ( /turf/template_noop, /area/template_noop) "P" = ( /obj/structure/flora/tree/hardwood/mahogany, /turf/floor/grass/wild, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "Q" = ( /obj/structure/bed/bedroll/fur, /obj/abstract/exterior_marker/inside, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "S" = ( /obj/structure/fire_source/firepit/basalt, /obj/item/stack/material/log/mapped/walnut/fifteen, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "U" = ( /obj/abstract/exterior_marker/inside, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) "Z" = ( /obj/item/bladed/knife, /turf/floor/barren, -/area/shaded_hills/outside/point_of_interest/hunter_camp) +/area/fantasy/outside/point_of_interest/hunter_camp) (1,1,1) = {" K diff --git a/mods/content/fantasy/submaps/woods/old_cabin/old_cabin.dm b/mods/content/fantasy/submaps/woods/old_cabin/old_cabin.dm new file mode 100644 index 00000000000..4a2130efce2 --- /dev/null +++ b/mods/content/fantasy/submaps/woods/old_cabin/old_cabin.dm @@ -0,0 +1,6 @@ +/datum/map_template/fantasy/woods/old_cabin + name = "old cabin" + mappaths = list("mods/content/fantasy/submaps/woods/old_cabin/old_cabin.dmm") + +/area/fantasy/outside/point_of_interest/old_cabin + name = "Point of Interest - Old Cabin" diff --git a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm b/mods/content/fantasy/submaps/woods/old_cabin/old_cabin.dmm similarity index 73% rename from maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm rename to mods/content/fantasy/submaps/woods/old_cabin/old_cabin.dmm index 84ac9a1af5e..18e12175274 100644 --- a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm +++ b/mods/content/fantasy/submaps/woods/old_cabin/old_cabin.dmm @@ -3,33 +3,33 @@ /obj/structure/door/walnut, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "g" = ( /obj/abstract/exterior_marker/inside, /obj/structure/table/end, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "h" = ( /turf/template_noop, /area/template_noop) "i" = ( /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "k" = ( /obj/structure/closet/crate/chest/ebony, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "o" = ( /obj/structure/drying_rack/ebony, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "u" = ( /obj/structure/reagent_dispensers/barrel/ebony/beer, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "x" = ( /obj/structure/table/wood/ebony, /obj/item/flame/torch, @@ -38,7 +38,7 @@ /obj/item/rock/hematite, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "z" = ( /obj/effect/spider/spiderling/mundane, /obj/structure/table/wood/ebony, @@ -48,23 +48,23 @@ }, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "B" = ( /obj/effect/decal/cleanable/blood, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "D" = ( /obj/effect/spider/spiderling/mundane, /obj/structure/table/wood/ebony, /obj/item/food/grown/carrot, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "G" = ( /obj/abstract/exterior_marker/inside, /turf/wall/log/walnut/shutter, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "H" = ( /obj/structure/table/wood/ebony, /obj/item/bladed/knife, @@ -73,37 +73,37 @@ /obj/item/food/grown/carrot, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "I" = ( /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "L" = ( /obj/effect/spider/spiderling/mundane, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "M" = ( /obj/abstract/exterior_marker/inside, /turf/wall/log/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "N" = ( /obj/abstract/exterior_marker/inside, /obj/structure/coatrack/ebony, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "T" = ( /obj/abstract/exterior_marker/inside, /obj/structure/table/desk/dresser, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "W" = ( /obj/structure/wall_sconce/torch{ dir = 1; pixel_y = 24 }, /turf/floor/dirt, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) "Z" = ( /obj/item/remains/human, /obj/structure/bed/simple/ebony/cloth, @@ -112,7 +112,7 @@ /obj/random/jewelry, /obj/abstract/exterior_marker/inside, /turf/floor/wood/rough/walnut, -/area/shaded_hills/outside/point_of_interest/old_cabin) +/area/fantasy/outside/point_of_interest/old_cabin) (1,1,1) = {" i diff --git a/maps/shaded_hills/submaps/woods/suspicious_cabin/suspicious_cabin.dmm b/mods/content/fantasy/submaps/woods/suspicious_cabin/suspicious_cabin.dmm similarity index 100% rename from maps/shaded_hills/submaps/woods/suspicious_cabin/suspicious_cabin.dmm rename to mods/content/fantasy/submaps/woods/suspicious_cabin/suspicious_cabin.dmm diff --git a/mods/content/item_sharpening/_item_sharpening.dm b/mods/content/item_sharpening/_item_sharpening.dm new file mode 100644 index 00000000000..3807839161b --- /dev/null +++ b/mods/content/item_sharpening/_item_sharpening.dm @@ -0,0 +1,8 @@ +#define IE_PAR_SHARP_DAM_MULT "sharp_dam_mult" + +/decl/modpack/item_sharpening + name = "Item Sharpening" + +/obj/proc/get_sharpening_material() + RETURN_TYPE(/decl/material) + return get_material() diff --git a/mods/content/item_sharpening/_item_sharpening.dme b/mods/content/item_sharpening/_item_sharpening.dme new file mode 100644 index 00000000000..c9af243f5e9 --- /dev/null +++ b/mods/content/item_sharpening/_item_sharpening.dme @@ -0,0 +1,11 @@ +#ifndef MODPACK_ITEM_SHARPENING +#define MODPACK_ITEM_SHARPENING +// BEGIN_INCLUDE +#include "_item_sharpening.dm" +#include "blade_sharpen.dm" +#include "effect_sharpen.dm" +#include "grindstone.dm" +#include "item_sharpen.dm" +#include "whetstone.dm" +//END_INCLUDE +#endif diff --git a/mods/content/item_sharpening/blade_sharpen.dm b/mods/content/item_sharpening/blade_sharpen.dm new file mode 100644 index 00000000000..a46b8d69033 --- /dev/null +++ b/mods/content/item_sharpening/blade_sharpen.dm @@ -0,0 +1,24 @@ +/obj/item/bladed/proc/get_sharpened_effect_params() + return list( + (IE_CAT_DAMAGE) = list( + (IE_PAR_USES) = max(1, max(1, rand(round(10 * 0.3), round(20 * 0.6)))), + (IE_PAR_MAX_USES) = 30, + (IE_PAR_SHARP_DAM_MULT) = 0.25 + ), + (IE_CAT_EXAMINE) + ) + +/obj/item/bladed/Initialize(ml, material_key, _hilt_mat, _guard_mat, _pommel_mat) + var/list/sharpened_params = get_sharpened_effect_params() + if(length(sharpened_params)) + add_item_effect(/decl/item_effect/sharpened, sharpened_params) + . = ..() + if(length(sharpened_params)) + update_attack_force() + update_name() + +/obj/item/bladed/folding/try_sharpen_with(mob/user, obj/sharpening_with) + if(!open) + to_chat(user, SPAN_WARNING("You cannot sharpen \the [src] while it's closed!")) + return FALSE + return ..() diff --git a/mods/content/item_sharpening/effect_sharpen.dm b/mods/content/item_sharpening/effect_sharpen.dm new file mode 100644 index 00000000000..5847ac347b3 --- /dev/null +++ b/mods/content/item_sharpening/effect_sharpen.dm @@ -0,0 +1,21 @@ +/decl/item_effect/sharpened/modify_attack_damage(base_damage, obj/item/used_item, mob/user, list/parameters) + var/uses = LAZYACCESS(parameters, IE_PAR_USES) + if(uses <= 0) + return base_damage + . = (1 + ((uses / max(1, LAZYACCESS(parameters, IE_PAR_MAX_USES))) * LAZYACCESS(parameters, IE_PAR_SHARP_DAM_MULT))) + +/decl/item_effect/sharpened/expend_attack_use(obj/item/used_item, mob/user, list/parameters) + var/uses = LAZYACCESS(parameters, IE_PAR_USES) + uses = max(0, uses-1) + used_item.set_item_effect_parameter(src, IE_CAT_DAMAGE, IE_PAR_USES, uses) + if(uses == 0) // We've gone dull! + used_item.update_attack_force() + used_item.update_name() + +/decl/item_effect/sharpened/on_examined(obj/item/item, mob/user, distance, list/parameters) + if(distance <= 1) + var/uses = item.get_item_effect_parameter(src, IE_CAT_DAMAGE, IE_PAR_USES) + if(uses > 0) + to_chat(user, SPAN_NOTICE("\The [item] has been honed to a keen edge.")) + else + to_chat(user, SPAN_NOTICE("\The [item] in need of sharpening.")) diff --git a/mods/content/item_sharpening/grindstone.dm b/mods/content/item_sharpening/grindstone.dm new file mode 100644 index 00000000000..13d8f126052 --- /dev/null +++ b/mods/content/item_sharpening/grindstone.dm @@ -0,0 +1,46 @@ +// TODO: better sound effects for working. +/obj/structure/working/grindstone + name = "grindstone" + desc = "A rotating section of coarse stone used to polish and sharpen metalwork like blades." + icon = 'mods/content/item_sharpening/icons/grindstone.dmi' + material_alteration = MAT_FLAG_ALTERATION_COLOR // Name and desc handled manually. + var/decl/material/stone_material = /decl/material/solid/quartz + +/obj/structure/working/grindstone/Initialize() + stone_material = GET_DECL(stone_material) + . = ..() + update_material_name() + update_material_desc() + +/obj/structure/working/grindstone/update_material_name(override_name) + . = ..() + if(stone_material) + SetName("[stone_material.adjective_name] [name]") + +/obj/structure/working/grindstone/update_material_desc(override_desc) + . = ..() + if(stone_material && istype(material)) + desc = "[desc] This one is made from [stone_material.solid_name] with \a [material.adjective_name] frame." + +/obj/structure/working/grindstone/on_update_icon() + . = ..() + underlays = list( + overlay_image(icon, "[icon_state]-grindstone", stone_material.color, RESET_COLOR), + overlay_image(icon, "[initial(icon_state)]-backdrop") + ) + +// Slightly wonky override, but this basically intercepts items being used on the grindstone. +/obj/structure/working/grindstone/try_take_input(obj/item/used_item, mob/user, silent) + if(working) + if(!silent) + to_chat(user, SPAN_WARNING("\The [src] is already in use, please wait for it to be free.")) + else + start_working() + used_item.try_sharpen_with(user, src) + if(!QDELETED(src) && working) + stop_working() + return TRUE + +/obj/structure/working/grindstone/get_sharpening_material() + RETURN_TYPE(/decl/material) + return stone_material diff --git a/mods/content/item_sharpening/icons/grindstone.dmi b/mods/content/item_sharpening/icons/grindstone.dmi new file mode 100644 index 00000000000..70b311b63b5 Binary files /dev/null and b/mods/content/item_sharpening/icons/grindstone.dmi differ diff --git a/mods/content/item_sharpening/item_sharpen.dm b/mods/content/item_sharpening/item_sharpen.dm new file mode 100644 index 00000000000..a72e928e5e8 --- /dev/null +++ b/mods/content/item_sharpening/item_sharpen.dm @@ -0,0 +1,49 @@ +/obj/item/update_name() + . = ..() + if(has_item_effect(/decl/item_effect/sharpened, IE_CAT_EXAMINE) && get_item_effect_parameter(/decl/item_effect/sharpened, IE_CAT_DAMAGE, IE_PAR_USES) <= 0) + SetName("dulled [name]") + +/obj/item/proc/can_sharpen_with(obj/sharpening_with) + if(!has_item_effect(/decl/item_effect/sharpened, IE_CAT_DAMAGE) || !material || !istype(sharpening_with)) + return FALSE + var/list/params = get_item_effect_parameters(/decl/item_effect/sharpened, IE_CAT_DAMAGE) + if(!islist(params) || params[IE_PAR_USES] >= params[IE_PAR_MAX_USES]) + return FALSE + return material.hardness <= sharpening_with.get_sharpening_material()?.hardness + +/obj/item/proc/sharpen_with(mob/user, obj/sharpen_with) + if(!has_item_effect(/decl/item_effect/sharpened, IE_CAT_DAMAGE)) + return FALSE + var/list/params = get_item_effect_parameters(/decl/item_effect/sharpened, IE_CAT_DAMAGE) + if(!islist(params)) + return FALSE + var/max_uses = params[IE_PAR_MAX_USES] + if(max_uses <= 0) + return FALSE + var/uses = params[IE_PAR_USES] || 0 + if(uses >= max_uses) + return FALSE + set_item_effect_parameter(/decl/item_effect/sharpened, IE_CAT_DAMAGE, IE_PAR_USES, max_uses) + if(uses == 0) // We've sharpened up from dull. + update_attack_force() + update_name() + return TRUE + +/obj/item/proc/try_sharpen_with(mob/user, obj/sharpening_with) + if(!has_item_effect(/decl/item_effect/sharpened, IE_CAT_DAMAGE)) + return FALSE + if(can_sharpen_with(sharpening_with)) + user.visible_message("\The [user] begins sharpening \the [src] with \the [sharpening_with].") + playsound(loc, 'sound/foley/knife1.ogg', 50) // metallic scrape, TODO better sound + if(user.do_skilled(10 SECONDS, SKILL_WEAPONS, src, check_holding = TRUE) && !QDELETED(sharpening_with) && can_sharpen_with(sharpening_with) && sharpen_with(user, sharpening_with)) + playsound(loc, 'sound/foley/knife1.ogg', 50) + user.visible_message("\The [user] sharpens \the [src] with \the [sharpening_with].") + else + to_chat(user, SPAN_WARNING("\The [src] cannot be [initial(sharp) ? "further sharpened" : "sharpened"] with \the [sharpening_with].")) + return TRUE + +// We don't override sharp because it's probably still pointy even if it isn't sharpened. +/obj/item/has_edge() + . = ..() + if(. && has_item_effect(/decl/item_effect/sharpened, IE_CAT_DAMAGE)) + return get_item_effect_parameter(/decl/item_effect/sharpened, IE_CAT_DAMAGE, IE_PAR_USES) > 0 diff --git a/mods/content/item_sharpening/whetstone.dm b/mods/content/item_sharpening/whetstone.dm new file mode 100644 index 00000000000..cdfbb09eea5 --- /dev/null +++ b/mods/content/item_sharpening/whetstone.dm @@ -0,0 +1,18 @@ +/obj/item/whetstone + name = "whetstone" + desc = "A worn-down lozenge used to sharpen blades." + icon = 'icons/obj/items/striker.dmi' // TODO unique icon? + w_class = ITEM_SIZE_TINY + material_alteration = MAT_FLAG_ALTERATION_ALL + material = /decl/material/solid/quartz + +/obj/item/attackby(obj/item/used_item, mob/user) + if(istype(used_item, /obj/item/whetstone)) + return try_sharpen_with(user, used_item) + return ..() + +/decl/loadout_option/utility/whetstone + name = "whetstone" + path = /obj/item/whetstone + loadout_flags = null + uid = "gear_utility_whetstone" diff --git a/mods/content/psionics/_psionics.dm b/mods/content/psionics/_psionics.dm index 9092fd88ae6..fa58ed18f58 100644 --- a/mods/content/psionics/_psionics.dm +++ b/mods/content/psionics/_psionics.dm @@ -30,3 +30,12 @@ var/datum/ability_handler/psionics/psi = !is_preview_copy && istype(character) && character.get_ability_handler(/datum/ability_handler/psionics) if(psi) psi.update() + +/decl/ability/can_use_ability(mob/user, list/metadata, silent = FALSE) + . = ..() + if(. && is_supernatural) + var/spell_leech = user.disrupts_psionics() + if(spell_leech) + if(!silent) + to_chat(user, SPAN_WARNING("You try to marshal your energy, but find it leeched away by \the [spell_leech]!")) + return FALSE diff --git a/mods/content/psionics/_psionics.dme b/mods/content/psionics/_psionics.dme index df5544cffe1..d52fd095516 100644 --- a/mods/content/psionics/_psionics.dme +++ b/mods/content/psionics/_psionics.dme @@ -9,7 +9,6 @@ #include "datum\jobs.dm" #include "datum\mind.dm" #include "datum\security_state.dm" -#include "datum\spells.dm" #include "datum\surgery.dm" #include "datum\uplink.dm" #include "datum\antagonists\foundation.dm" @@ -27,7 +26,6 @@ #include "items\literature.dm" #include "items\null_ammo.dm" #include "items\nullrod.dm" -#include "items\soulstone.dm" #include "machines\psimeter.dm" #include "machines\psimonitor.dm" #include "system\subsystem_psi.dm" @@ -54,7 +52,6 @@ #include "system\psionics\interface\ui.dm" #include "system\psionics\interface\ui_hub.dm" #include "system\psionics\interface\ui_toggles.dm" -#include "system\psionics\mob\borer_power.dm" #include "system\psionics\mob\mob.dm" #include "system\psionics\mob\mob_assay.dm" #include "system\psionics\mob\mob_interactions.dm" diff --git a/mods/content/psionics/datum/chems.dm b/mods/content/psionics/datum/chems.dm index 1bc713de0eb..e859f0dc515 100644 --- a/mods/content/psionics/datum/chems.dm +++ b/mods/content/psionics/datum/chems.dm @@ -1,34 +1,7 @@ /decl/material/liquid/crystal_agent/do_material_check(var/mob/living/M) . = !!M.get_ability_handler(/datum/ability_handler/psionics) ? /decl/material/nullglass : ..() -/decl/material/liquid/glowsap/gleam/affect_overdose(mob/living/M, total_dose) +/decl/material/liquid/glowsap/gleam/affect_overdose(mob/living/victim, total_dose) ..() - var/datum/ability_handler/psionics/psi = M.get_ability_handler(/datum/ability_handler/psionics) + var/datum/ability_handler/psionics/psi = victim.get_ability_handler(/datum/ability_handler/psionics) psi?.check_latency_trigger(30, "a [name] overdose") - -/decl/chemical_reaction/synthesis/nullglass - name = "Soulstone" - result = null - required_reagents = list(/decl/material/liquid/blood = 15, /decl/material/liquid/crystal_agent = 1) - result_amount = 1 - -#ifdef GAMEMODE_PACK_CULT -/decl/chemical_reaction/synthesis/nullglass/get_alternate_reaction_indicator(var/datum/reagents/holder) - var/list/blood_data = REAGENT_DATA(holder, /decl/material/liquid/blood) - var/weakref/donor_ref = LAZYACCESS(blood_data, DATA_BLOOD_DONOR) - var/mob/living/donor = donor_ref?.resolve() - . = istype(donor) && !!donor.get_ability_handler(/datum/ability_handler/psionics) -#endif - -/decl/chemical_reaction/synthesis/nullglass/on_reaction(datum/reagents/holder, created_volume, reaction_flags, list/reaction_data) - var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) - if(reaction_flags) - #ifdef GAMEMODE_PACK_CULT - for(var/i = 1, i <= created_volume, i++) - new /obj/item/soulstone(location) - #else - CRASH("Nullglass alternate reaction triggered in [holder.my_atom] without cult modpack loaded!") - #endif - else - for(var/i = 1, i <= created_volume*2, i++) - new /obj/item/shard(location, /decl/material/solid/gemstone/crystal) \ No newline at end of file diff --git a/mods/content/psionics/datum/spells.dm b/mods/content/psionics/datum/spells.dm deleted file mode 100644 index 7c02c352000..00000000000 --- a/mods/content/psionics/datum/spells.dm +++ /dev/null @@ -1,7 +0,0 @@ -/spell/cast_check(skipcharge = 0,mob/user = usr, var/list/targets) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell - var/spell_leech = user.disrupts_psionics() - if(spell_leech) - to_chat(user, SPAN_WARNING("You try to marshal your energy, but find it leeched away by \the [spell_leech]!")) - return 0 - . = ..() - \ No newline at end of file diff --git a/mods/content/psionics/items/soulstone.dm b/mods/content/psionics/items/soulstone.dm deleted file mode 100644 index 3d984a0711c..00000000000 --- a/mods/content/psionics/items/soulstone.dm +++ /dev/null @@ -1,15 +0,0 @@ -#ifdef GAMEMODE_PACK_CULT -/obj/item/soulstone/disrupts_psionics() - . = !full ? src : FALSE - -/obj/item/soulstone/shatter() - for(var/i=1 to rand(2,5)) - new /obj/item/shard(get_turf(src), /decl/material/nullglass) - . = ..() - -/obj/item/soulstone/withstand_psi_stress(var/stress, var/atom/source) - . = ..(stress, source) - if(. > 0) - . = max(0, . - rand(2,5)) - shatter() -#endif \ No newline at end of file diff --git a/mods/content/psionics/system/psionics/complexus/complexus.dm b/mods/content/psionics/system/psionics/complexus/complexus.dm index 990edecd13e..0ce5f4cfc18 100644 --- a/mods/content/psionics/system/psionics/complexus/complexus.dm +++ b/mods/content/psionics/system/psionics/complexus/complexus.dm @@ -1,5 +1,4 @@ /datum/ability_handler/psionics - var/announced = FALSE // Whether or not we have been announced to our holder yet. var/suppressed = TRUE // Whether or not we are suppressing our psi powers. var/use_psi_armour = TRUE // Whether or not we should automatically deflect/block incoming damage. diff --git a/mods/content/psionics/system/psionics/equipment/psipower.dm b/mods/content/psionics/system/psionics/equipment/psipower.dm index 3d850f27cf2..8aa0afca352 100644 --- a/mods/content/psionics/system/psionics/equipment/psipower.dm +++ b/mods/content/psionics/system/psionics/equipment/psipower.dm @@ -15,7 +15,9 @@ . = ..() /obj/item/ability/psionic/attack_self(var/mob/user) - sound_to(owner, 'sound/effects/psi/power_fail.ogg') + var/mob/owner = owner_ref?.resolve() + if(istype(owner)) + sound_to(owner, 'sound/effects/psi/power_fail.ogg') . = ..() /obj/item/ability/psionic/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) @@ -31,6 +33,7 @@ . = ..(target, user, proximity) /obj/item/ability/psionic/Process() + var/mob/living/owner = owner_ref?.resolve() var/datum/ability_handler/psionics/psi = istype(owner) && owner.get_ability_handler(/datum/ability_handler/psionics) psi?.spend_power(maintain_cost, backblast_on_failure = FALSE) if((!owner || owner.do_psionics_check(maintain_cost, owner) || loc != owner || !(src in owner.get_held_items())) && !QDELETED(src)) diff --git a/mods/content/psionics/system/psionics/equipment/psipower_blade.dm b/mods/content/psionics/system/psionics/equipment/psipower_blade.dm index fd8b4f947c8..94163e32012 100644 --- a/mods/content/psionics/system/psionics/equipment/psipower_blade.dm +++ b/mods/content/psionics/system/psionics/equipment/psipower_blade.dm @@ -1,8 +1,8 @@ /obj/item/ability/psionic/psiblade name = "psychokinetic slash" _base_attack_force = 10 - sharp = 1 - edge = 1 + sharp = TRUE + edge = TRUE maintain_cost = 1 icon_state = "psiblade_short" diff --git a/mods/content/psionics/system/psionics/equipment/psipower_tk.dm b/mods/content/psionics/system/psionics/equipment/psipower_tk.dm index 787d2a59050..ee529a0d922 100644 --- a/mods/content/psionics/system/psionics/equipment/psipower_tk.dm +++ b/mods/content/psionics/system/psionics/equipment/psipower_tk.dm @@ -9,6 +9,7 @@ . = ..() /obj/item/ability/psionic/telekinesis/Process() + var/mob/living/owner = owner_ref?.resolve() var/datum/ability_handler/psionics/psi = istype(owner) && owner.get_ability_handler(/datum/ability_handler/psionics) if(!focus || !isturf(focus.loc) || get_dist(get_turf(focus), get_turf(owner)) > psi?.get_rank(PSI_PSYCHOKINESIS)) owner.drop_from_inventory(src) @@ -30,6 +31,7 @@ else return FALSE + var/mob/living/owner = owner_ref?.resolve() var/datum/ability_handler/psionics/psi = istype(owner) && owner.get_ability_handler(/datum/ability_handler/psionics) if(_focus.anchored || (check_paramount && psi?.get_rank(PSI_PSYCHOKINESIS) < PSI_RANK_PARAMOUNT)) focus = _focus @@ -88,7 +90,7 @@ else if(!focus.anchored) var/user_rank = psi?.get_rank(PSI_PSYCHOKINESIS) - focus.throw_at(target, user_rank*2, user_rank*10, owner) + focus.throw_at(target, user_rank*2, user_rank*10, owner_ref?.resolve()) sleep(1) sparkle() @@ -104,5 +106,4 @@ O.icon = 'icons/effects/effects.dmi' O.icon_state = "nothing" flick("empdisable",O) - sleep(5) - qdel(O) + QDEL_IN(src, 0.5 SECONDS) diff --git a/mods/content/psionics/system/psionics/faculties/coercion.dm b/mods/content/psionics/system/psionics/faculties/coercion.dm index 2af44aa9335..8a92f058ad1 100644 --- a/mods/content/psionics/system/psionics/faculties/coercion.dm +++ b/mods/content/psionics/system/psionics/faculties/coercion.dm @@ -179,7 +179,7 @@ return TRUE if(accepted_glamour == "Yes") - to_chat(user, SPAN_DANGER("You layer a glamour across the \the [target]'s senses, beguiling them to unwittingly follow your commands.")) + to_chat(user, SPAN_DANGER("You layer a glamour across \the [target]'s senses, beguiling them to unwittingly follow your commands.")) to_chat(target, SPAN_DANGER("You have been ensnared by \the [user]'s glamour!")) beguiled.add_antagonist(target.mind, new_controller = user) else diff --git a/mods/content/psionics/system/psionics/interface/ui.dm b/mods/content/psionics/system/psionics/interface/ui.dm index 4e8adfa83bd..3d8255b2030 100644 --- a/mods/content/psionics/system/psionics/interface/ui.dm +++ b/mods/content/psionics/system/psionics/interface/ui.dm @@ -1,14 +1,17 @@ /obj/screen/psi icon = 'mods/content/psionics/icons/psi.dmi' requires_ui_style = FALSE + apply_screen_overlay = FALSE var/hidden = TRUE + var/can_hide = TRUE /obj/screen/psi/Initialize(mapload, mob/_owner, ui_style, ui_color, ui_alpha, ui_cat) . = ..() update_icon() /obj/screen/psi/on_update_icon() - if(hidden) + ..() + if(hidden && can_hide) set_invisibility(INVISIBILITY_ABSTRACT) else set_invisibility(INVISIBILITY_NONE) \ No newline at end of file diff --git a/mods/content/psionics/system/psionics/interface/ui_hub.dm b/mods/content/psionics/system/psionics/interface/ui_hub.dm index cd87e528211..97c5ba41adc 100644 --- a/mods/content/psionics/system/psionics/interface/ui_hub.dm +++ b/mods/content/psionics/system/psionics/interface/ui_hub.dm @@ -6,6 +6,7 @@ maptext_x = 6 maptext_y = -8 requires_ui_style = FALSE + can_hide = FALSE var/image/on_cooldown var/list/components @@ -19,6 +20,7 @@ START_PROCESSING(SSprocessing, src) /obj/screen/psi/hub/on_update_icon() + ..() var/mob/living/owner = owner_ref?.resolve() var/datum/ability_handler/psionics/psi = istype(owner) && owner.get_ability_handler(/datum/ability_handler/psionics) icon_state = psi?.suppressed ? "psi_suppressed" : "psi_active" diff --git a/mods/content/psionics/system/psionics/interface/ui_toggles.dm b/mods/content/psionics/system/psionics/interface/ui_toggles.dm index 5585ac5d802..93c34b82a27 100644 --- a/mods/content/psionics/system/psionics/interface/ui_toggles.dm +++ b/mods/content/psionics/system/psionics/interface/ui_toggles.dm @@ -29,6 +29,7 @@ name = "Show/Hide Psi UI" icon_state = "arrow_left" requires_ui_style = FALSE + can_hide = FALSE var/obj/screen/psi/hub/controller /obj/screen/psi/toggle_psi_menu/Initialize(mapload, mob/_owner, ui_style, ui_color, ui_alpha, ui_cat, obj/screen/psi/hub/_controller) @@ -43,6 +44,7 @@ controller.update_icon() /obj/screen/psi/toggle_psi_menu/on_update_icon() + ..() if(hidden) icon_state = "arrow_left" else diff --git a/mods/content/psionics/system/psionics/mob/mob.dm b/mods/content/psionics/system/psionics/mob/mob.dm index c85902acde4..15cc7914742 100644 --- a/mods/content/psionics/system/psionics/mob/mob.dm +++ b/mods/content/psionics/system/psionics/mob/mob.dm @@ -1,4 +1,5 @@ /datum/ability_handler/psionics/refresh_login() + . = ..() update(TRUE) if(!suppressed) show_auras() diff --git a/mods/content/psionics/system/psionics/mob/mob_interactions.dm b/mods/content/psionics/system/psionics/mob/mob_interactions.dm index 366adb30882..aba8eeb102c 100644 --- a/mods/content/psionics/system/psionics/mob/mob_interactions.dm +++ b/mods/content/psionics/system/psionics/mob/mob_interactions.dm @@ -6,8 +6,6 @@ power.handle_post_power(user, target); \ if(istype(result)) { \ sound_to(user, sound('sound/effects/psi/power_evoke.ogg')); \ - LAZYADD(ability_items, result); \ - user.put_in_hands(result); \ } \ return TRUE; \ } \ @@ -28,15 +26,19 @@ INVOKE_PSI_POWERS(user, get_grab_powers(SSpsi.get_faculty_by_intent(user.get_intent())), target) /datum/ability_handler/psionics/can_do_melee_invocation(mob/user, atom/target) + SHOULD_CALL_PARENT(FALSE) return TRUE /datum/ability_handler/psionics/do_melee_invocation(mob/user, atom/target) + SHOULD_CALL_PARENT(FALSE) INVOKE_PSI_POWERS(user, get_melee_powers(SSpsi.get_faculty_by_intent(user.get_intent())), target) /datum/ability_handler/psionics/can_do_ranged_invocation(mob/user, atom/target) + SHOULD_CALL_PARENT(FALSE) return TRUE /datum/ability_handler/psionics/do_ranged_invocation(mob/user, atom/target) + SHOULD_CALL_PARENT(FALSE) INVOKE_PSI_POWERS(user, get_ranged_powers(SSpsi.get_faculty_by_intent(user.get_intent())), target) -#undef INVOKE_PSI_POWERS \ No newline at end of file +#undef INVOKE_PSI_POWERS diff --git a/mods/content/standard_jobs/_standard_jobs.dme b/mods/content/standard_jobs/_standard_jobs.dme index cbba09eacef..a17a75892bb 100644 --- a/mods/content/standard_jobs/_standard_jobs.dme +++ b/mods/content/standard_jobs/_standard_jobs.dme @@ -11,6 +11,7 @@ #include "departments\security.dm" #include "departments\service.dm" #include "departments\supply.dm" +#include "jobs\_job.dm" #include "jobs\captain.dm" #include "jobs\civilian.dm" #include "jobs\engineering.dm" diff --git a/mods/content/standard_jobs/icons/hud.dmi b/mods/content/standard_jobs/icons/hud.dmi new file mode 100644 index 00000000000..ccea1d4a211 Binary files /dev/null and b/mods/content/standard_jobs/icons/hud.dmi differ diff --git a/mods/content/standard_jobs/jobs/_job.dm b/mods/content/standard_jobs/jobs/_job.dm new file mode 100644 index 00000000000..6869734493a --- /dev/null +++ b/mods/content/standard_jobs/jobs/_job.dm @@ -0,0 +1,3 @@ +/datum/job/standard + abstract_type = /datum/job/standard + hud_icon = 'mods/content/standard_jobs/icons/hud.dmi' diff --git a/mods/content/standard_jobs/jobs/captain.dm b/mods/content/standard_jobs/jobs/captain.dm index c0782377252..d7abb62e0cd 100644 --- a/mods/content/standard_jobs/jobs/captain.dm +++ b/mods/content/standard_jobs/jobs/captain.dm @@ -1,5 +1,6 @@ -/datum/job/captain +/datum/job/standard/captain title = "Captain" + hud_icon_state = "hudcaptain" head_position = 1 department_types = list(/decl/department/command) total_positions = 1 @@ -33,16 +34,17 @@ /datum/computer_file/program/reports ) -/datum/job/captain/equip_job(var/mob/living/human/H) +/datum/job/standard/captain/equip_job(var/mob/living/human/H) . = ..() if(.) H.implant_loyalty(src) -/datum/job/captain/get_access() +/datum/job/standard/captain/get_access() return get_all_station_access() -/datum/job/hop +/datum/job/standard/hop title = "Head of Personnel" + hud_icon_state = "hudhop" head_position = 1 department_types = list( /decl/department/command, diff --git a/mods/content/standard_jobs/jobs/civilian.dm b/mods/content/standard_jobs/jobs/civilian.dm index af71d84e536..1ce8eeb39d0 100644 --- a/mods/content/standard_jobs/jobs/civilian.dm +++ b/mods/content/standard_jobs/jobs/civilian.dm @@ -1,5 +1,6 @@ -/datum/job/assistant +/datum/job/standard/assistant title = "Assistant" + hud_icon_state = "hudassistant" total_positions = -1 spawn_positions = -1 supervisors = "absolutely everyone" @@ -10,13 +11,14 @@ outfit_type = /decl/outfit/job/generic/assistant department_types = list(/decl/department/civilian) -/datum/job/assistant/get_access() +/datum/job/standard/assistant/get_access() if(get_config_value(/decl/config/toggle/assistant_maint)) return list(access_maint_tunnels) return list() -/datum/job/chaplain +/datum/job/standard/chaplain title = "Chaplain" + hud_icon_state = "hudchaplain" // TODO: not always a crucifix department_types = list(/decl/department/civilian) total_positions = 1 spawn_positions = 1 @@ -42,9 +44,10 @@ software_on_spawn = list(/datum/computer_file/program/reports) //Food -/datum/job/bartender +/datum/job/standard/bartender title = "Bartender" department_types = list(/decl/department/service) + hud_icon_state = "hudbartender" total_positions = 1 spawn_positions = 1 supervisors = "the head of personnel" @@ -68,8 +71,9 @@ SKILL_CHEMISTRY = SKILL_BASIC ) -/datum/job/chef +/datum/job/standard/chef title = "Chef" + hud_icon_state = "hudchef" department_types = list(/decl/department/service) total_positions = 2 spawn_positions = 2 @@ -89,8 +93,9 @@ SKILL_CHEMISTRY = SKILL_BASIC ) -/datum/job/hydro +/datum/job/standard/hydro title = "Gardener" + hud_icon_state = "hudgardener" department_types = list(/decl/department/service) total_positions = 2 spawn_positions = 1 @@ -111,8 +116,9 @@ event_categories = list(ASSIGNMENT_GARDENER) //Cargo -/datum/job/qm +/datum/job/standard/qm title = "Quartermaster" + hud_icon_state = "hudqm" department_types = list(/decl/department/supply) total_positions = 1 spawn_positions = 1 @@ -156,11 +162,12 @@ /datum/computer_file/program/reports ) -/datum/job/cargo_tech +/datum/job/standard/cargo_tech title = "Cargo Technician" department_types = list(/decl/department/supply) total_positions = 2 spawn_positions = 2 + hud_icon_state = "hudcargo" supervisors = "the quartermaster and the head of personnel" access = list( access_maint_tunnels, @@ -192,8 +199,9 @@ /datum/computer_file/program/reports ) -/datum/job/mining +/datum/job/standard/mining title = "Shaft Miner" + hud_icon_state = "hudminer" department_types = list(/decl/department/supply) total_positions = 3 spawn_positions = 3 @@ -227,11 +235,12 @@ SKILL_PILOT = SKILL_MAX ) -/datum/job/janitor +/datum/job/standard/janitor title = "Janitor" department_types = list(/decl/department/service) total_positions = 1 spawn_positions = 1 + hud_icon_state = "hudjanitor" supervisors = "the head of personnel" access = list( access_janitor, @@ -261,8 +270,9 @@ event_categories = list(ASSIGNMENT_JANITOR) //More or less assistants -/datum/job/librarian +/datum/job/standard/librarian title = "Librarian" + hud_icon_state = "hudlibrarian" department_types = list(/decl/department/civilian) total_positions = 1 spawn_positions = 1 @@ -281,8 +291,9 @@ skill_points = 20 software_on_spawn = list(/datum/computer_file/program/reports) -/datum/job/lawyer +/datum/job/standard/lawyer title = "Internal Affairs Agent" + hud_icon_state = "hudia" department_types = list(/decl/department/support) total_positions = 2 spawn_positions = 2 @@ -308,7 +319,7 @@ skill_points = 20 software_on_spawn = list(/datum/computer_file/program/reports) -/datum/job/lawyer/equip_job(var/mob/living/human/H) +/datum/job/standard/lawyer/equip_job(var/mob/living/human/H) . = ..() if(.) H.implant_loyalty(H) diff --git a/mods/content/standard_jobs/jobs/engineering.dm b/mods/content/standard_jobs/jobs/engineering.dm index 40cba546489..84630cb60ba 100644 --- a/mods/content/standard_jobs/jobs/engineering.dm +++ b/mods/content/standard_jobs/jobs/engineering.dm @@ -1,5 +1,6 @@ -/datum/job/chief_engineer +/datum/job/standard/chief_engineer title = "Chief Engineer" + hud_icon_state = "hudce" head_position = 1 department_types = list( /decl/department/engineering, @@ -69,16 +70,15 @@ max_skill = list( SKILL_CONSTRUCTION = SKILL_MAX, - SKILL_ELECTRICAL = SKILL_MAX, - SKILL_ATMOS = SKILL_MAX, - SKILL_ENGINES = SKILL_MAX + SKILL_ELECTRICAL = SKILL_MAX, + SKILL_ATMOS = SKILL_MAX, + SKILL_ENGINES = SKILL_MAX ) skill_points = 30 software_on_spawn = list( /datum/computer_file/program/comm, /datum/computer_file/program/network_monitor, /datum/computer_file/program/power_monitor, - /datum/computer_file/program/supermatter_monitor, /datum/computer_file/program/alarm_monitor, /datum/computer_file/program/atmos_control, /datum/computer_file/program/rcon_console, @@ -88,10 +88,10 @@ ) event_categories = list(ASSIGNMENT_ENGINEER) -/datum/job/engineer +/datum/job/standard/engineer title = "Engineer" department_types = list(/decl/department/engineering) - + hud_icon_state = "hudengineer" total_positions = 8 spawn_positions = 7 supervisors = "the chief engineer" @@ -130,22 +130,21 @@ min_skill = list( SKILL_LITERACY = SKILL_ADEPT, SKILL_COMPUTER = SKILL_BASIC, - SKILL_EVA = SKILL_BASIC, - SKILL_CONSTRUCTION = SKILL_ADEPT, - SKILL_ELECTRICAL = SKILL_BASIC, - SKILL_ATMOS = SKILL_BASIC, - SKILL_ENGINES = SKILL_BASIC + SKILL_EVA = SKILL_BASIC, + SKILL_CONSTRUCTION = SKILL_ADEPT, + SKILL_ELECTRICAL = SKILL_BASIC, + SKILL_ATMOS = SKILL_BASIC, + SKILL_ENGINES = SKILL_BASIC ) max_skill = list( SKILL_CONSTRUCTION = SKILL_MAX, - SKILL_ELECTRICAL = SKILL_MAX, - SKILL_ATMOS = SKILL_MAX, - SKILL_ENGINES = SKILL_MAX + SKILL_ELECTRICAL = SKILL_MAX, + SKILL_ATMOS = SKILL_MAX, + SKILL_ENGINES = SKILL_MAX ) skill_points = 20 software_on_spawn = list( /datum/computer_file/program/power_monitor, - /datum/computer_file/program/supermatter_monitor, /datum/computer_file/program/alarm_monitor, /datum/computer_file/program/atmos_control, /datum/computer_file/program/rcon_console, diff --git a/mods/content/standard_jobs/jobs/medical.dm b/mods/content/standard_jobs/jobs/medical.dm index 22e4090ebb3..1d281434a51 100644 --- a/mods/content/standard_jobs/jobs/medical.dm +++ b/mods/content/standard_jobs/jobs/medical.dm @@ -1,5 +1,6 @@ -/datum/job/cmo +/datum/job/standard/cmo title = "Chief Medical Officer" + hud_icon_state = "hudcmo" head_position = 1 department_types = list( /decl/department/medical, @@ -74,8 +75,9 @@ ) event_categories = list(ASSIGNMENT_MEDICAL) -/datum/job/doctor +/datum/job/standard/doctor title = "Medical Doctor" + hud_icon_state = "hudmed" department_types = list(/decl/department/medical) minimal_player_age = 3 total_positions = 5 @@ -129,8 +131,9 @@ skill_points = 22 event_categories = list(ASSIGNMENT_MEDICAL) -/datum/job/chemist +/datum/job/standard/chemist title = "Pharmacist" + hud_icon_state = "hudpharmacist" department_types = list(/decl/department/medical) minimal_player_age = 7 total_positions = 2 @@ -164,8 +167,9 @@ ) skill_points = 16 -/datum/job/counselor +/datum/job/standard/counselor title = "Counselor" + hud_icon_state = "hudmed" alt_titles = list("Mentalist") department_types = list(/decl/department/medical) total_positions = 1 @@ -202,15 +206,6 @@ ) give_psionic_implant_on_join = FALSE -#ifdef MODPACK_PSIONICS -/datum/job/counselor/equip_job(var/mob/living/human/H) - if(H.mind.role_alt_title == "Counselor") - psi_faculties = list("[PSI_REDACTION]" = PSI_RANK_OPERANT) - if(H.mind.role_alt_title == "Mentalist") - psi_faculties = list("[PSI_COERCION]" = PSI_RANK_OPERANT) - return ..() -#endif - // Department-flavor IDs /obj/item/card/id/medical name = "identification card" diff --git a/mods/content/standard_jobs/jobs/science.dm b/mods/content/standard_jobs/jobs/science.dm index a7a074efbce..548f9147ea7 100644 --- a/mods/content/standard_jobs/jobs/science.dm +++ b/mods/content/standard_jobs/jobs/science.dm @@ -1,10 +1,11 @@ -/datum/job/rd +/datum/job/standard/rd title = "Chief Science Officer" head_position = 1 department_types = list( /decl/department/science, /decl/department/command ) + hud_icon_state = "hudrd" total_positions = 1 spawn_positions = 1 supervisors = "the captain" @@ -74,13 +75,14 @@ skill_points = 30 event_categories = list(ASSIGNMENT_SCIENTIST) -/datum/job/scientist +/datum/job/standard/scientist title = "Scientist" department_types = list(/decl/department/science) total_positions = 6 spawn_positions = 4 supervisors = "the Chief Science Officer" selection_color = "#633d63" + hud_icon_state = "hudscientist" economic_power = 7 access = list( access_robotics, @@ -122,8 +124,9 @@ skill_points = 20 event_categories = list(ASSIGNMENT_SCIENTIST) -/datum/job/roboticist +/datum/job/standard/roboticist title = "Roboticist" + hud_icon_state = "hudroboticist" department_types = list(/decl/department/science) total_positions = 2 spawn_positions = 2 diff --git a/mods/content/standard_jobs/jobs/security.dm b/mods/content/standard_jobs/jobs/security.dm index f58305f7ae2..345f595a635 100644 --- a/mods/content/standard_jobs/jobs/security.dm +++ b/mods/content/standard_jobs/jobs/security.dm @@ -1,5 +1,6 @@ -/datum/job/hos +/datum/job/standard/hos title = "Head of Security" + hud_icon_state = "hudhos" head_position = 1 department_types = list( /decl/department/security, @@ -87,14 +88,15 @@ ) event_categories = list(ASSIGNMENT_SECURITY) -/datum/job/hos/equip_job(var/mob/living/human/H) +/datum/job/standard/hos/equip_job(var/mob/living/human/H) . = ..() if(.) H.implant_loyalty(H) -/datum/job/warden +/datum/job/standard/warden title = "Warden" department_types = list(/decl/department/security) + hud_icon_state = "hudwarden" total_positions = 1 spawn_positions = 1 supervisors = "the head of security" @@ -142,10 +144,10 @@ /datum/computer_file/program/camera_monitor ) -/datum/job/detective +/datum/job/standard/detective title = "Detective" department_types = list(/decl/department/security) - + hud_icon_state = "huddetective" total_positions = 2 spawn_positions = 2 supervisors = "the head of security" @@ -192,8 +194,9 @@ /datum/computer_file/program/camera_monitor ) -/datum/job/officer +/datum/job/standard/officer title = "Security Officer" + hud_icon_state = "hudsec" department_types = list(/decl/department/security) total_positions = 4 spawn_positions = 4 diff --git a/mods/content/standard_jobs/jobs/synthetics.dm b/mods/content/standard_jobs/jobs/synthetics.dm index e787da9e011..fa023f0a0d0 100644 --- a/mods/content/standard_jobs/jobs/synthetics.dm +++ b/mods/content/standard_jobs/jobs/synthetics.dm @@ -1,4 +1,4 @@ -/datum/job/computer +/datum/job/standard/computer title = "Computer" event_categories = list(ASSIGNMENT_COMPUTER) total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm @@ -11,7 +11,8 @@ economic_power = 0 outfit_type = /decl/outfit/job/silicon/ai loadout_allowed = FALSE - hud_icon = "hudblank" + hud_icon_state = "hudblank" + hud_icon = null skill_points = 0 no_skill_buffs = TRUE guestbanned = 1 @@ -19,16 +20,16 @@ skip_loadout_preview = TRUE department_types = list(/decl/department/miscellaneous) -/datum/job/computer/equip_job(var/mob/living/human/H) +/datum/job/standard/computer/equip_job(var/mob/living/human/H) return !!H -/datum/job/computer/is_position_available() +/datum/job/standard/computer/is_position_available() return (empty_playable_ai_cores.len != 0) -/datum/job/computer/handle_variant_join(var/mob/living/human/H, var/alt_title) +/datum/job/standard/computer/handle_variant_join(var/mob/living/human/H, var/alt_title) return H -/datum/job/computer/do_spawn_special(var/mob/living/character, var/mob/new_player/new_player_mob, var/latejoin) +/datum/job/standard/computer/do_spawn_special(var/mob/living/character, var/mob/new_player/new_player_mob, var/latejoin) character = character.AIize() // AIize the character, but don't move them yet // is_available for AI checks that there is an empty core available in this list @@ -46,7 +47,7 @@ qdel(C) return TRUE -/datum/job/robot +/datum/job/standard/robot title = "Robot" event_categories = list(ASSIGNMENT_ROBOT) total_positions = 2 @@ -58,7 +59,8 @@ economic_power = 0 loadout_allowed = FALSE outfit_type = /decl/outfit/job/silicon/cyborg - hud_icon = "hudblank" + hud_icon_state = "hudblank" + hud_icon = null skill_points = 0 no_skill_buffs = TRUE guestbanned = 1 @@ -66,14 +68,14 @@ skip_loadout_preview = TRUE department_types = list(/decl/department/miscellaneous) -/datum/job/robot/handle_variant_join(var/mob/living/human/H, var/alt_title) +/datum/job/standard/robot/handle_variant_join(var/mob/living/human/H, var/alt_title) if(H) return H.Robotize(SSrobots.get_mob_type_by_title(alt_title || title)) -/datum/job/robot/equip_job(var/mob/living/human/H) +/datum/job/standard/robot/equip_job(var/mob/living/human/H) return !!H -/datum/job/robot/New() +/datum/job/standard/robot/New() ..() alt_titles = SSrobots.robot_alt_titles.Copy() alt_titles -= title // So the unit test doesn't flip out if a mob or brain type is declared for our main title. diff --git a/mods/content/supermatter/_supermatter.dm b/mods/content/supermatter/_supermatter.dm new file mode 100644 index 00000000000..78daa387234 --- /dev/null +++ b/mods/content/supermatter/_supermatter.dm @@ -0,0 +1,23 @@ +// These are used by supermatter and supermatter monitor program, mostly for UI updating purposes. Higher should always be worse! +#define SUPERMATTER_ERROR -1 // Unknown status, shouldn't happen but just in case. +#define SUPERMATTER_INACTIVE 0 // No or minimal energy +#define SUPERMATTER_NORMAL 1 // Normal operation +#define SUPERMATTER_NOTIFY 2 // Ambient temp > 80% of CRITICAL_TEMPERATURE +#define SUPERMATTER_WARNING 3 // Ambient temp > CRITICAL_TEMPERATURE OR integrity damaged +#define SUPERMATTER_DANGER 4 // Integrity < 50% +#define SUPERMATTER_EMERGENCY 5 // Integrity < 25% +#define SUPERMATTER_DELAMINATING 6 // Pretty obvious. + +#define SUPERMATTER_DATA_EER "Relative EER" +#define SUPERMATTER_DATA_TEMPERATURE "Temperature" +#define SUPERMATTER_DATA_PRESSURE "Pressure" +#define SUPERMATTER_DATA_EPR "Chamber EPR" + +/decl/modpack/supermatter + name = "Supermatter Content" + desc = "This modpack includes the supermatter engine and related content." + nanoui_directory = "mods/content/supermatter/nano_templates/" + +/decl/modpack/supermatter/pre_initialize() + . = ..() + global.debug_verbs |= /datum/admins/proc/setup_supermatter \ No newline at end of file diff --git a/mods/content/supermatter/_supermatter.dme b/mods/content/supermatter/_supermatter.dme new file mode 100644 index 00000000000..fb6723cce50 --- /dev/null +++ b/mods/content/supermatter/_supermatter.dme @@ -0,0 +1,30 @@ +#ifndef CONTENT_PACK_SUPERMATTER +#define CONTENT_PACK_SUPERMATTER +// BEGIN_INCLUDE +#include "_supermatter.dm" +#include "admin_setup_supermatter.dm" +#include "datums\sm_codex.dm" +#include "datums\sm_follow.dm" +#include "datums\sm_grief_fix.dm" +#include "datums\sm_looping_sound.dm" +#include "datums\sm_supply_drop.dm" +#include "datums\sm_supply_pack.dm" +#include "datums\supermatter_monitor.dm" +#include "endgame_cascade\cascade_blob.dm" +#include "endgame_cascade\portal.dm" +#include "endgame_cascade\universe.dm" +#include "items\sm_book.dm" +#include "items\sm_grenade.dm" +#include "machinery\sm_supply_beacon.dm" +#include "machinery\supermatter.dm" +#include "machinery\supermatter_core_console.dm" +#include "overrides\sm_fuel_compressor.dm" +#include "overrides\sm_meteor.dm" +#include "overrides\sm_singularity.dm" +#include "overrides\sm_strings.dm" +#include "overrides\sm_trader.dm" +#include "overrides\sm_unit_tests.dm" +#include "overrides\sm_xenoarchaeology.dm" +#include "structures\sm_closets.dm" +// END_INCLUDE +#endif diff --git a/mods/content/supermatter/admin_setup_supermatter.dm b/mods/content/supermatter/admin_setup_supermatter.dm new file mode 100644 index 00000000000..9664671e836 --- /dev/null +++ b/mods/content/supermatter/admin_setup_supermatter.dm @@ -0,0 +1,116 @@ +#define ENERGY_NITROGEN 115 // Roughly 8 emitter shots. +#define ENERGY_CARBONDIOXIDE 150 // Roughly 10 emitter shots. +#define ENERGY_HYDROGEN 300 // Roughly 20 emitter shots. + +/datum/admins/proc/setup_supermatter() + set category = "Debug" + set name = "Setup Supermatter" + set desc = "Allows you to start the Supermatter engine." + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + var/response = input(usr, "Are you sure? This will start up the engine with selected gas as coolant.", "Engine setup") as null|anything in list("N2", "CO2", "H2", "Abort") + if(!response || response == "Abort") + return + + var/errors = 0 + var/warnings = 0 + var/success = 0 + + log_and_message_admins("## SUPERMATTER SETUP - Setup initiated by [usr] using coolant type [response].") + + // CONFIGURATION PHASE + // Coolant canisters, set types according to response. + for(var/obj/effect/engine_setup/coolant_canister/C in global.engine_setup_markers) + switch(response) + if("N2") + C.canister_type = /obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/ + continue + if("CO2") + C.canister_type = /obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/ + continue + if("H2") + C.canister_type = /obj/machinery/portable_atmospherics/canister/hydrogen/engine_setup/ + continue + + for(var/obj/effect/engine_setup/core/C in global.engine_setup_markers) + switch(response) + if("N2") + C.energy_setting = ENERGY_NITROGEN + continue + if("CO2") + C.energy_setting = ENERGY_CARBONDIOXIDE + continue + if("H2") + C.energy_setting = ENERGY_HYDROGEN + continue + + for(var/obj/effect/engine_setup/filter/F in global.engine_setup_markers) + F.coolant = response + + var/list/delayed_objects = list() + // SETUP PHASE + for(var/obj/effect/engine_setup/S in global.engine_setup_markers) + var/result = S.activate(0) + switch(result) + if(ENGINE_SETUP_OK) + success++ + continue + if(ENGINE_SETUP_WARNING) + warnings++ + continue + if(ENGINE_SETUP_ERROR) + errors++ + log_and_message_admins("## SUPERMATTER SETUP - Error encountered! Aborting.") + break + if(ENGINE_SETUP_DELAYED) + delayed_objects.Add(S) + continue + + if(!errors) + for(var/obj/effect/engine_setup/S in delayed_objects) + var/result = S.activate(1) + switch(result) + if(ENGINE_SETUP_OK) + success++ + continue + if(ENGINE_SETUP_WARNING) + warnings++ + continue + if(ENGINE_SETUP_ERROR) + errors++ + log_and_message_admins("## SUPERMATTER SETUP - Error encountered! Aborting.") + break + + log_and_message_admins("## SUPERMATTER SETUP - Setup completed with [errors] errors, [warnings] warnings and [success] successful steps.") + + return + + + +// Energises the supermatter. Errors when unable to locate supermatter. +/obj/effect/engine_setup/core + name = "Supermatter Core Marker" + var/energy_setting = 0 + +/obj/effect/engine_setup/core/activate(var/last = 0) + if(!last) + return ENGINE_SETUP_DELAYED + ..() + var/obj/machinery/power/supermatter/SM = locate() in get_turf(src) + if(!SM) + log_and_message_admins("## ERROR: Unable to locate supermatter core at [x] [y] [z]!") + return ENGINE_SETUP_ERROR + if(!energy_setting) + log_and_message_admins("## ERROR: Energy setting unset at [x] [y] [z]!") + return ENGINE_SETUP_ERROR + SM.power = energy_setting + return ENGINE_SETUP_OK + +#undef ENERGY_NITROGEN +#undef ENERGY_CARBONDIOXIDE +#undef ENERGY_HYDROGEN diff --git a/mods/content/supermatter/datums/sm_codex.dm b/mods/content/supermatter/datums/sm_codex.dm new file mode 100644 index 00000000000..c0d5c160fae --- /dev/null +++ b/mods/content/supermatter/datums/sm_codex.dm @@ -0,0 +1,21 @@ + +/datum/codex_entry/guide/supermatter + name = "Guide to Supermatter Engines" + available_to_map_tech_level = MAP_TECH_LEVEL_SPACE + +/datum/codex_entry/supermatter + associated_paths = list(/obj/machinery/power/supermatter) + mechanics_text = "When energized by a laser (or something hitting it), it emits radiation and heat. If the heat reaches above 7000 kelvin, it will send an alert and start taking damage. \ + After integrity falls to zero percent, it will delaminate, causing a massive explosion, station-wide radiation spikes, and hallucinations. \ + Supermatter reacts badly to oxygen in the atmosphere. It'll also heat up really quick if it is in vacuum.
      \ +
      \ + Supermatter cores are extremely dangerous to be close to, and requires protection to handle properly. The protection you will need is:
      \ + Optical meson scanners on your eyes, to prevent hallucinations when looking at the supermatter.
      \ + Radiation helmet and suit, as the supermatter is radioactive.
      \ +
      \ + Touching the supermatter will result in *instant death*, with no corpse left behind! You can drag the supermatter, but anything else will kill you. \ + It is advised to obtain a genetic backup before trying to drag it." + antag_text = "Exposing the supermatter to oxygen or vaccum will cause it to start rapidly heating up. Sabotaging the supermatter and making it explode will \ + cause a period of lag as the explosion is processed by the server, as well as irradiating the entire station and causing hallucinations to happen. \ + Wearing radiation equipment will protect you from most of the delamination effects sans explosion." + available_to_map_tech_level = MAP_TECH_LEVEL_SPACE \ No newline at end of file diff --git a/mods/content/supermatter/datums/sm_follow.dm b/mods/content/supermatter/datums/sm_follow.dm new file mode 100644 index 00000000000..5b221ad03d6 --- /dev/null +++ b/mods/content/supermatter/datums/sm_follow.dm @@ -0,0 +1,3 @@ +/datum/follow_holder/supermatter + sort_order = 10 + followed_type = /obj/machinery/power/supermatter \ No newline at end of file diff --git a/mods/content/supermatter/datums/sm_grief_fix.dm b/mods/content/supermatter/datums/sm_grief_fix.dm new file mode 100644 index 00000000000..70dd9469abc --- /dev/null +++ b/mods/content/supermatter/datums/sm_grief_fix.dm @@ -0,0 +1,8 @@ +/decl/atmos_grief_fix_step/supermatter + name = "Supermatter depowered" + sort_order = 0 + +/decl/atmos_grief_fix_step/supermatter/act() + // Depower the supermatter, as it would quickly blow up once we remove all gases from the pipes. + for(var/obj/machinery/power/supermatter/S in SSmachines.machinery) + S.power = 0 \ No newline at end of file diff --git a/code/modules/supermatter/sm_looping_sound.dm b/mods/content/supermatter/datums/sm_looping_sound.dm similarity index 100% rename from code/modules/supermatter/sm_looping_sound.dm rename to mods/content/supermatter/datums/sm_looping_sound.dm diff --git a/mods/content/supermatter/datums/sm_supply_drop.dm b/mods/content/supermatter/datums/sm_supply_drop.dm new file mode 100644 index 00000000000..1560d1118d2 --- /dev/null +++ b/mods/content/supermatter/datums/sm_supply_drop.dm @@ -0,0 +1,5 @@ +/datum/supply_drop_loot/supermatter + name = "Supermatter" +/datum/supply_drop_loot/supermatter/New() + ..() + contents = list(/obj/machinery/power/supermatter) diff --git a/mods/content/supermatter/datums/sm_supply_pack.dm b/mods/content/supermatter/datums/sm_supply_pack.dm new file mode 100644 index 00000000000..5261f06a069 --- /dev/null +++ b/mods/content/supermatter/datums/sm_supply_pack.dm @@ -0,0 +1,6 @@ +/decl/hierarchy/supply_pack/engineering/smbig + name = "Power - Supermatter core" + contains = list(/obj/machinery/power/supermatter) + containertype = /obj/structure/closet/crate/secure/large/supermatter + containername = "\improper Supermatter crate (CAUTION)" + access = access_ce \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm b/mods/content/supermatter/datums/supermatter_monitor.dm similarity index 91% rename from code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm rename to mods/content/supermatter/datums/supermatter_monitor.dm index e139e3c4f28..518d84a7f18 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm +++ b/mods/content/supermatter/datums/supermatter_monitor.dm @@ -5,7 +5,7 @@ /datum/computer_file/program/supermatter_monitor filename = "supmon" filedesc = "Supermatter Monitoring" - nanomodule_path = /datum/nano_module/program/supermatter_monitor/ + nanomodule_path = /datum/nano_module/program/supermatter_monitor program_icon_state = "smmon_0" program_key_state = "tech_key" program_menu_icon = "notice" @@ -215,3 +215,20 @@ if(S.uid == newuid) active = S return 1 + +// Add this to the software list for borgs +/obj/item/robot_module/engineering/grant_software() + software |= /datum/computer_file/program/supermatter_monitor + return ..() + +/obj/item/robot_module/flying/repair/grant_software() + software |= /datum/computer_file/program/supermatter_monitor + return ..() + +/obj/machinery/computer/modular/telescreen/preset/engineering/Initialize(mapload, d, populate_parts) + default_software |= /datum/computer_file/program/supermatter_monitor + return ..() + +/obj/machinery/computer/modular/preset/engineering/Initialize(mapload, d, populate_parts) + default_software |= /datum/computer_file/program/supermatter_monitor + return ..() \ No newline at end of file diff --git a/code/game/gamemodes/endgame/supermatter_cascade/cascade_blob.dm b/mods/content/supermatter/endgame_cascade/cascade_blob.dm similarity index 98% rename from code/game/gamemodes/endgame/supermatter_cascade/cascade_blob.dm rename to mods/content/supermatter/endgame_cascade/cascade_blob.dm index ef04b1069f7..6cd76d14233 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/cascade_blob.dm +++ b/mods/content/supermatter/endgame_cascade/cascade_blob.dm @@ -9,7 +9,7 @@ //luminosity = 5 //l_color="#0066ff" plane = ABOVE_LIGHTING_PLANE - layer = SUPERMATTER_WALL_LAYER + layer = SUBSPACE_WALL_LAYER var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST,UP,DOWN) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm b/mods/content/supermatter/endgame_cascade/portal.dm similarity index 100% rename from code/game/gamemodes/endgame/supermatter_cascade/portal.dm rename to mods/content/supermatter/endgame_cascade/portal.dm diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/mods/content/supermatter/endgame_cascade/universe.dm similarity index 96% rename from code/game/gamemodes/endgame/supermatter_cascade/universe.dm rename to mods/content/supermatter/endgame_cascade/universe.dm index e9b2d2e40fd..ccca16707e4 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/mods/content/supermatter/endgame_cascade/universe.dm @@ -1,6 +1,3 @@ -var/global/universe_has_ended = 0 - - /datum/universal_state/supermatter_cascade name = "Supermatter Cascade" desc = "Unknown harmonance affecting universal substructure, converting nearby matter to supermatter." @@ -70,9 +67,10 @@ var/global/universe_has_ended = 0 if(!invalid_area) A.update_icon() +// TODO: Should this be changed to use the actual ambient lights system...? /datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() spawn(0) - for(var/datum/lighting_corner/L in world) + for(var/datum/lighting_corner/L in SSlighting.lighting_corners) if(isAdminLevel(L.z)) L.update_lumcount(1,1,1) else diff --git a/mods/content/supermatter/items/sm_book.dm b/mods/content/supermatter/items/sm_book.dm new file mode 100644 index 00000000000..8230625788a --- /dev/null +++ b/mods/content/supermatter/items/sm_book.dm @@ -0,0 +1,6 @@ +/obj/item/book/manual/supermatter_engine + name = "supermatter engine reference manual" + icon = 'icons/obj/items/books/book_supermatter.dmi' + author = "Central Engineering Division" + title = "Supermatter Engine Operating Manual" + guide_decl = /datum/codex_entry/guide/supermatter \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/supermatter.dm b/mods/content/supermatter/items/sm_grenade.dm similarity index 50% rename from code/game/objects/items/weapons/grenades/supermatter.dm rename to mods/content/supermatter/items/sm_grenade.dm index 4f2758b53e3..b7f3186e666 100644 --- a/code/game/objects/items/weapons/grenades/supermatter.dm +++ b/mods/content/supermatter/items/sm_grenade.dm @@ -33,3 +33,25 @@ if(world.time > implode_at) explosion(loc, 0, 1, 3, 4) qdel(src) + +/obj/item/box/supermatters + name = "box of supermatter grenades" + desc = "A box containing 5 highly experimental supermatter grenades." + icon_state = "radbox" + +/obj/item/box/supermatters/WillContain() + return list(/obj/item/grenade/supermatter = 5) + +/datum/uplink_item/item/grenades/supermatter + name = "1x Supermatter Grenade" + desc = "This grenade contains a small supermatter shard which will delaminate upon activation and pull in nearby objects, irradiate lifeforms, and eventually explode." + item_cost = 15 + antag_roles = list(/decl/special_role/mercenary) + path = /obj/item/grenade/supermatter + +/datum/uplink_item/item/grenades/supermatters + name = "5x Supermatter Grenades" + desc = "These grenades contains a small supermatter shard which will delaminate upon activation and pull in nearby objects, irradiate lifeforms, and eventually explode." + item_cost = 60 + antag_roles = list(/decl/special_role/mercenary) + path = /obj/item/box/supermatters diff --git a/mods/content/supermatter/machinery/sm_supply_beacon.dm b/mods/content/supermatter/machinery/sm_supply_beacon.dm new file mode 100644 index 00000000000..e6a622480b6 --- /dev/null +++ b/mods/content/supermatter/machinery/sm_supply_beacon.dm @@ -0,0 +1,7 @@ +/obj/item/supply_beacon/supermatter + name = "inactive supermatter supply beacon" + deploy_path = /obj/structure/supply_beacon/supermatter + +/obj/structure/supply_beacon/supermatter + name = "supermatter supply beacon" + drop_type = "supermatter" diff --git a/code/modules/supermatter/supermatter.dm b/mods/content/supermatter/machinery/supermatter.dm similarity index 98% rename from code/modules/supermatter/supermatter.dm rename to mods/content/supermatter/machinery/supermatter.dm index 676104bd863..b300501036c 100644 --- a/code/modules/supermatter/supermatter.dm +++ b/mods/content/supermatter/machinery/supermatter.dm @@ -99,7 +99,7 @@ var/global/list/supermatter_delam_accent_sounds = list( SPAN_DANGER("As \the [source] slowly stops resonating, you find your skin covered in new radiation burns."), 1,\ SPAN_DANGER("The unearthly ringing subsides and you notice you have new radiation burns."), 2) else - M.show_message(SPAN_DANGER("You hear an uneartly ringing and notice your skin is covered in fresh radiation burns."), 2) + M.show_message(SPAN_DANGER("You hear an unearthly ringing and notice your skin is covered in fresh radiation burns."), 2) var/rads = 500 SSradiation.radiate(source, rads) @@ -113,9 +113,10 @@ var/global/list/supermatter_delam_accent_sounds = list( light_range = 4 layer = ABOVE_HUMAN_LAYER matter = list( - /decl/material/solid/supermatter = MATTER_AMOUNT_PRIMARY, + /decl/material/solid/exotic_matter = MATTER_AMOUNT_PRIMARY, /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT ) + w_class = ITEM_SIZE_LARGE_STRUCTURE var/nitrogen_retardation_factor = 0.15 // Higher == N2 slows reaction more var/thermal_release_modifier = 10000 // Higher == more heat released during reaction @@ -696,6 +697,7 @@ var/global/list/supermatter_delam_accent_sounds = list( desc = "A strangely translucent and iridescent crystal that looks like it used to be part of a larger structure. You get headaches just from looking at it." icon = 'icons/obj/supermatter_32.dmi' icon_state = "supermatter_shard" + w_class = ITEM_SIZE_STRUCTURE warning_point = 50 emergency_point = 400 @@ -708,6 +710,7 @@ var/global/list/supermatter_delam_accent_sounds = list( /obj/machinery/power/supermatter/medium icon = 'icons/obj/supermatter_32.dmi' + w_class = (ITEM_SIZE_STRUCTURE + ITEM_SIZE_LARGE_STRUCTURE) / 2 // halfway between a shard and a normal SM /obj/machinery/power/supermatter/shard/announce_warning() //Shards don't get announcements return diff --git a/mods/content/supermatter/machinery/supermatter_core_console.dm b/mods/content/supermatter/machinery/supermatter_core_console.dm new file mode 100644 index 00000000000..8b2bf0e175c --- /dev/null +++ b/mods/content/supermatter/machinery/supermatter_core_console.dm @@ -0,0 +1,45 @@ +// Does this really need to be its own thing...? +// Can it not just be a stock parts preset or something? +/obj/machinery/computer/air_control/supermatter_core + frequency = 1438 + out_pressure_mode = 1 + +/datum/fabricator_recipe/imprinter/circuit/supermatter_control + path = /obj/item/stock_parts/circuitboard/air_management/supermatter_core + +/obj/item/stock_parts/circuitboard/air_management/supermatter_core + name = "circuitboard (core control)" + build_path = /obj/machinery/computer/air_control/supermatter_core + frequency = 1438 + var/input_tag + var/output_tag + + var/list/input_info = list() + var/list/output_info = list() + + var/input_flow_setting = 700 + var/pressure_setting = 100 + +/obj/item/stock_parts/circuitboard/air_management/supermatter_core/construct(var/obj/machinery/computer/air_control/supermatter_core/SC) + if(..(SC)) + SC.input_tag = input_tag + SC.output_tag = output_tag + + SC.input_info = input_info.Copy() + SC.output_info = output_info.Copy() + + SC.input_flow_setting = input_flow_setting + SC.pressure_setting = input_flow_setting + return 1 + +/obj/item/stock_parts/circuitboard/air_management/supermatter_core/deconstruct(var/obj/machinery/computer/air_control/supermatter_core/SC) + if(..(SC)) + input_tag = SC.input_tag + output_tag = SC.output_tag + + input_info = SC.input_info.Copy() + output_info = SC.output_info.Copy() + + input_flow_setting = SC.input_flow_setting + pressure_setting = SC.input_flow_setting + return 1 \ No newline at end of file diff --git a/nano/templates/supermatter_crystal.tmpl b/mods/content/supermatter/nano_templates/supermatter_crystal.tmpl similarity index 100% rename from nano/templates/supermatter_crystal.tmpl rename to mods/content/supermatter/nano_templates/supermatter_crystal.tmpl diff --git a/nano/templates/supermatter_monitor.tmpl b/mods/content/supermatter/nano_templates/supermatter_monitor.tmpl similarity index 100% rename from nano/templates/supermatter_monitor.tmpl rename to mods/content/supermatter/nano_templates/supermatter_monitor.tmpl diff --git a/mods/content/supermatter/overrides/sm_fuel_compressor.dm b/mods/content/supermatter/overrides/sm_fuel_compressor.dm new file mode 100644 index 00000000000..e259fa26625 --- /dev/null +++ b/mods/content/supermatter/overrides/sm_fuel_compressor.dm @@ -0,0 +1,13 @@ +/obj/machinery/fuel_compressor/add_material(obj/thing, mob/user) + . = ..() + if(.) + return TRUE + if(istype(thing, /obj/machinery/power/supermatter/shard)) + var/exotic_matter_amount = thing?.matter?[/decl/material/solid/exotic_matter] + if(exotic_matter_amount <= 0) + return FALSE + stored_material[/decl/material/solid/exotic_matter] = exotic_matter_amount + to_chat(user, SPAN_NOTICE("You awkwardly cram \the [thing] into \the [src]'s material buffer.")) + qdel(thing) + return TRUE + return FALSE \ No newline at end of file diff --git a/mods/content/supermatter/overrides/sm_meteor.dm b/mods/content/supermatter/overrides/sm_meteor.dm new file mode 100644 index 00000000000..449ad80fbb9 --- /dev/null +++ b/mods/content/supermatter/overrides/sm_meteor.dm @@ -0,0 +1,5 @@ +/obj/effect/meteor/destroyer/supermatter + name = "supermatter shard" + desc = "Oh god, what will be next..?" + icon = 'icons/obj/supermatter_32.dmi' + icon_state = "supermatter" diff --git a/mods/content/supermatter/overrides/sm_singularity.dm b/mods/content/supermatter/overrides/sm_singularity.dm new file mode 100644 index 00000000000..b13506cd580 --- /dev/null +++ b/mods/content/supermatter/overrides/sm_singularity.dm @@ -0,0 +1,46 @@ +#define STAGE_SUPER 11 + +/// A singularity that has the mass of a supermatter crystal. +/decl/singularity_stage/stage_super + name = "super gravitational singularity" + desc = "A gravitational singularity with the properties of supermatter. It has the power to destroy worlds." + min_energy = 50000 + max_energy = INFINITY + stage_size = STAGE_SUPER + footprint = 6 + icon = 'icons/effects/352x352.dmi' + icon_state = "singularity_s11"//uh, whoever drew that, you know that black holes are supposed to look dark right? What's this, the clown's singulo? + pixel_x = -160 + pixel_y = -160 + grav_pull = 16 + consume_range = 5 + dissipates_over_time = 0 //It cant go smaller due to e loss + event_chance = 25 //Events will fire off more often. + forced_event = /decl/singularity_event/supermatter_wave + wander = TRUE + explosion_vulnerable = FALSE + em_heavy_range = 12 + em_light_range = 16 + mesmerize_text = "helpless" + the_goggles_do_nothing = TRUE + ignore_obstacles = TRUE + +/decl/singularity_stage/stage_super/grow_to(obj/effect/singularity/source) + source.visible_message(SPAN_SINISTER("You witness the creation of a destructive force that cannot possibly be stopped by human hands.")) + +// why is this not shrink_from or something? +/decl/singularity_stage/stage_five/shrink_to(obj/effect/singularity/source) + source.visible_message(SPAN_WARNING("\The [source] miraculously reduces in size and loses its supermatter properties.")) + +// Singularity event +/decl/singularity_event/supermatter_wave/handle_event(obj/effect/singularity/source) + for(var/mob/living/M in view(10, source.loc)) + to_chat(M, SPAN_WARNING("You hear an unearthly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.")) + if(prob(67)) + to_chat(M, SPAN_NOTICE("Miraculously, it fails to kill you.")) + else + to_chat(M, SPAN_DANGER("You don't even have a moment to react as you are reduced to ashes by the intense radiation.")) + M.dust() + SSradiation.radiate(source, rand(source.energy)) + +#undef STAGE_SUPER \ No newline at end of file diff --git a/mods/content/supermatter/overrides/sm_strings.dm b/mods/content/supermatter/overrides/sm_strings.dm new file mode 100644 index 00000000000..2d7427f03d3 --- /dev/null +++ b/mods/content/supermatter/overrides/sm_strings.dm @@ -0,0 +1,19 @@ +/decl/game_mode/possible_ert_disabled_reasons() + var/static/sm_injected = FALSE + if(sm_injected) + return ..() + sm_injected = TRUE + . = ..() + . += "supermatter dust" + +/obj/item/disk/secret_project/get_secret_project_nouns() + var/static/sm_injected = FALSE + if(sm_injected) + return ..() + sm_injected = TRUE + . = ..() + . += "a supermatter engine" + return . + +/decl/material/solid/exotic_matter + lore_text = "Hypercrystalline supermatter is a subset of non-baryonic 'exotic' matter. It is found mostly in the heart of large stars, and features heavily in all kinds of fringe physics-defying technology." \ No newline at end of file diff --git a/mods/content/supermatter/overrides/sm_trader.dm b/mods/content/supermatter/overrides/sm_trader.dm new file mode 100644 index 00000000000..ded45374bd9 --- /dev/null +++ b/mods/content/supermatter/overrides/sm_trader.dm @@ -0,0 +1,3 @@ +/datum/trader/ship/unique/rock/New() + ..() + possible_trading_items[/obj/machinery/power/supermatter] = TRADER_ALL \ No newline at end of file diff --git a/mods/content/supermatter/overrides/sm_unit_tests.dm b/mods/content/supermatter/overrides/sm_unit_tests.dm new file mode 100644 index 00000000000..18351659e4c --- /dev/null +++ b/mods/content/supermatter/overrides/sm_unit_tests.dm @@ -0,0 +1,3 @@ +/datum/unit_test/turf_floor_icons_shall_be_valid/New() + ..() + excepted_types |= /turf/unsimulated/wall/cascade \ No newline at end of file diff --git a/mods/content/supermatter/overrides/sm_xenoarchaeology.dm b/mods/content/supermatter/overrides/sm_xenoarchaeology.dm new file mode 100644 index 00000000000..537de7a311c --- /dev/null +++ b/mods/content/supermatter/overrides/sm_xenoarchaeology.dm @@ -0,0 +1,7 @@ +/datum/artifact_find/New() + var/static/supermatter_injected = FALSE + if(!supermatter_injected) + potential_finds[/obj/machinery/power/supermatter] = 5 + potential_finds[/obj/machinery/power/supermatter/shard] = 25 + supermatter_injected = TRUE + ..() diff --git a/mods/content/supermatter/structures/sm_closets.dm b/mods/content/supermatter/structures/sm_closets.dm new file mode 100644 index 00000000000..a1e58097f93 --- /dev/null +++ b/mods/content/supermatter/structures/sm_closets.dm @@ -0,0 +1,2 @@ +/obj/structure/closet/crate/secure/large/supermatter + closet_appearance = /decl/closet_appearance/large_crate/secure/hazard \ No newline at end of file diff --git a/mods/content/tabloids/_tabloids.dm b/mods/content/tabloids/_tabloids.dm new file mode 100644 index 00000000000..7d6188ff28f --- /dev/null +++ b/mods/content/tabloids/_tabloids.dm @@ -0,0 +1,58 @@ +/decl/modpack/tabloids + name = "Tabloids" + tabloid_publishers = list( + "\improper Solar Enquirer", + "\improper Stellar Examiner", + "\improper Antares Daily", + "\improper Weekly Galactic News", + "\improper Spiral" + ) + tabloid_headlines = list( + "NARCOALGORITHMS: ARE YOUR CHILDREN SAFE?", + "ARE GMO HUMANS POISONOUS IN BED?", + "TOP 10 REASONS WHY OTHER SPECIES ARE A HOAX", + "CENTENNIAL POSITRONIC EXTENDS LIFESPAN WITH 1 SIMPLE TRICK", + "TOP 10 DANGEROUS FOODS WITH CHEMICALS", + "NEW TERRIFYING TEEN TREND: SUN-DIVING", + "HAS YOUR SPOUSE BEEN REPLACED BY AN ALIEN IMPOSTER? STUDIES SUGGEST YES!", + "SPACE CAUSES CANCER: DOCTORS CONFIRM", + "ARE BODY SCANNERS TOO INVASIVE? FIND OUT INSIDE!", + "HAS SCIENCE GONE TOO FAR? LOCAL SCIENTIST DEBUNKS ALIEN THEORY, DECRIES THEM AS TUBE EXPERIMENTS GONE WRONG", + "100 DELICIOUS RECIPES LETHAL TO CARBON-BASED LIFE", + "TOP FIVE SPECIES WE DROVE TO EXTINCTION; NUMBER TWO WILL SHOCK YOU", + "RELIGION WAS RIGHT? SHOCK FINDINGS SHOW ALIEN SIMILARITY TO ANIMALS, EXISTENCE OF BOATS", + "TOP TEN REASONS WHY ONLY HUMANS ARE SENTIENT", + "WHICH PLANET HAS THE BEST LOVERS? THIS AND MORE INSIDE!", + "SHE SAID WE SHOULD SEE OTHER PEOPLE, SO I MARRIED A NEO-AVIAN PACK: FULL STORY INSIDE", + "LOSE WEIGHT THREE TIMES FASTER WITH THESE LOW-G MANEUVERS!", + "MY DAUGHTER JOINED A NEURAL COLLECTIVE AND NOW SHE CAN TASTE SPACETIME: FULL STORY INSIDE", + "WERE THE NAZIS PSYCHIC? ONE HISTORIAN TELLS ALL", + "IS THE SOLAR GOVERNMENT CREATING AN AI SUPERINTELLIGENCE NEAR MERCURY? ONE EXPERT REVEALS SHOCKING INSIDER DETAILS!", + "TOP TEN HISTORICAL FIGURES THAT WERE TWO PROMETHEANS IN A TRENCHCOAT", + "TOP 10 SECRET AUGMENTS THE GOVERNMENT DOESN'T WANT YOU TO GET", + "ENLARGE YOUR MENTAL FACULTIES WITH THIS 1 WEIRD HAT", + "'HELP, MY SON THINKS HE'S A 20TH CENTURY VID CHARACTER CALLED SPOCK' AND MORE SHOCKING TALES INSIDE", + "18 RADICAL HIP IMPLANTS ALL THE KIDS ARE GETTING!", + "PRESERVED HEAD OF 21ST CENTURY CAPITALIST INSISTS THAT 'DYSON WALL' ONLY SANE SOLUTION TO RIMWARD MALCONTENTS", + "50 SHADES OF GREEN; BESTSELLING MULTISPECIES ROMANCE COMING TO CINEMAS", + "PLUTO: DWARF PLANET, OR SECRET RAMPANT AI FACILITY HELL-BENT ON CORRUPTING YOUR CHILDREN?", + "TOP TEN ANIME ALIENS. NUMBER 3 WILL SICKEN YOU", + "OCTUBER X'RALLBRE EXPOSED; NUDE PHOTOSHOOT LEAKS", + "WAR ON MARS AFTER NAKED MAN WAS FOUND; WERE THE ROMANS RIGHT?", + "REAL ALIENS ARGUE EARTH MOVIES RACIST!", + "HELP! I MARRIED A HEGEMONOUS SWARM INTELLIGENCE AND MY SON THINKS HE'S A ROUTER!", + "POSITRONICS: HUMAN INGENUITY AND GENEROSITY, OR A HORRIBLE MISTAKE? FIND OUT INSIDE!", + "THE FREE TRADER UNION: NEITHER FREE NOR A UNION. SHOCKING EXPOSE!", + "HAS THE FREE MARKET GONE TOO FAR? LUNA GLITTERPOP STAR AUCTIONS THIRD TESTICLE FOR TRANS-ORBITAL SHIPPING BONDS", + "THEY SAID IT WAS CANCER, BUT I KNEW IT WAS A TINY, SELF-REPLICATING CLONE OF RAY KURZWEIL: FULL STORY INSIDE", + "WHAT HAS TECHNOLOGY DONE? INDUSTRY BILLIONAIRE MARRIES OWN INFORMORPH MIND-COPY", + "REPTILLIAN ICE WARRIORS FROM ANOTHER WORLD LIVE INSIDE YOUR AIR DUCTS: HERE'S HOW TO GET RID OF THEM", + "10 CRITICAL THINGS YOU NEED TO KNOW ABOUT 'DRONEGATE'", + "THEY CALL THEM JUMPGATES BUT I'VE NEVER SEEN THEM JUMP: AN INDUSTRY INSIDER SPEAKS FOR THE FIRST TIME", + "EMERGENT INTELLIGENCES ARE STEALING YOUR BANK DETAILS, FETISHES: FOIL HAT RECIPE INSIDE", + "TIME TRAVELLERS ARE STEALING YOUR WIFI: 5 TIPS FOR DEFEATING HACKERS FROM THE FUTURE", + "'My mother was an alien spy': THIS CELEBRITY REVEAL WILL SHOCK AND AMAZE YOU", + "LUMINARY SCIENTIST SPEAKS: DIABETES IS A HYPERCORP RETROVIRUS!", + "'I REROUTED MY NEURAL CIRCUITRY SO THAT PAIN TASTES OF STRAWBERRIES' AND FIFTEEN OTHER CRAZY ALMACH STORIES", + "JOINING THE NAVY? HERE'S 15 EXPERT TIPS FOR AVOIDING BRAIN PARASITES" + ) \ No newline at end of file diff --git a/mods/content/tabloids/_tabloids.dme b/mods/content/tabloids/_tabloids.dme new file mode 100644 index 00000000000..0c6e6e35b01 --- /dev/null +++ b/mods/content/tabloids/_tabloids.dme @@ -0,0 +1,8 @@ +#ifndef CONTENT_PACK_TABLOIDS +#define CONTENT_PACK_TABLOIDS +// BEGIN_INCLUDE +#include "_tabloids.dm" +#include "tabloid.dm" +#include "tabloid_helpers.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/mods/content/tabloids/icons/magazine.dmi b/mods/content/tabloids/icons/magazine.dmi new file mode 100644 index 00000000000..62b385510c8 Binary files /dev/null and b/mods/content/tabloids/icons/magazine.dmi differ diff --git a/mods/content/tabloids/tabloid.dm b/mods/content/tabloids/tabloid.dm new file mode 100644 index 00000000000..6fc781a7ee6 --- /dev/null +++ b/mods/content/tabloids/tabloid.dm @@ -0,0 +1,36 @@ +/obj/item/tabloid + name = "tabloid magazine" + desc = "It's one of those trashy tabloid magazines. It looks pretty out of date." + icon = 'mods/content/tabloids/icons/magazine.dmi' + icon_state = "magazine" + randpixel = 6 + material = /decl/material/solid/organic/paper + matter = list(/decl/material/solid/organic/plastic = MATTER_AMOUNT_REINFORCEMENT) + var/headline + var/article_body + +/obj/item/tabloid/Initialize() + . = ..() + var/list/tabloid_headlines = get_tabloid_headlines() + name = SAFEPICK(get_tabloid_publishers()) || initial(name) + icon_state = SAFEPICK(get_tabloid_states()) || initial(icon_state) + headline = SAFEPICK(tabloid_headlines) + if(length(tabloid_headlines) && tabloid_headlines[headline]) + article_body = tabloid_headlines[headline] + +/obj/item/tabloid/examine(mob/user, distance, infix, suffix) + . = ..() + if(headline) + to_chat(user, "The headline screams, \"[headline]\"") + +/obj/item/tabloid/attack_self(mob/user) + . = ..() + if(!.) + user.visible_message(SPAN_NOTICE("\The [user] leafs idly through \the [src].")) + if(headline) + to_chat(user, "Most of it is the usual tabloid garbage, but the headline story, \"[headline]\", holds your attention for awhile.") + if(article_body) + to_chat(user, article_body) + else + to_chat(user, "It's the usual tabloid garbage. You find nothing of interest.") + return TRUE diff --git a/mods/content/tabloids/tabloid_helpers.dm b/mods/content/tabloids/tabloid_helpers.dm new file mode 100644 index 00000000000..966556dafbe --- /dev/null +++ b/mods/content/tabloids/tabloid_helpers.dm @@ -0,0 +1,24 @@ +// Minor fluff item for mapping in waiting rooms etc. +/proc/get_tabloid_publishers() + var/static/list/tabloid_publishers + if(!tabloid_publishers) + tabloid_publishers = list() + for(var/modpack_name in SSmodpacks.loaded_modpacks) + var/decl/modpack/modpack = SSmodpacks.loaded_modpacks[modpack_name] + if(length(modpack.tabloid_publishers)) + tabloid_publishers |= modpack.tabloid_publishers + return tabloid_publishers + +/proc/get_tabloid_headlines() + var/static/list/tabloid_headlines + if(!tabloid_headlines) + tabloid_headlines = list() + for(var/modpack_name in SSmodpacks.loaded_modpacks) + var/decl/modpack/modpack = SSmodpacks.loaded_modpacks[modpack_name] + if(length(modpack.tabloid_headlines)) + tabloid_headlines |= modpack.tabloid_headlines + return tabloid_headlines + +/proc/get_tabloid_states() + var/static/list/tabloid_states = icon_states('mods/content/tabloids/icons/magazine.dmi') + return tabloid_states diff --git a/mods/content/xenobiology/_xenobiology.dme b/mods/content/xenobiology/_xenobiology.dme index 11a5f3972c6..1591ea3f458 100644 --- a/mods/content/xenobiology/_xenobiology.dme +++ b/mods/content/xenobiology/_xenobiology.dme @@ -50,7 +50,6 @@ #include "slime\slime_commands.dm" #include "slime\slime_comments.dm" #include "slime\slime_follow.dm" -#include "slime\slime_hud.dm" #include "slime\slime_reagents.dm" #include "slime\slime_surgery.dm" #include "slime\slime_update_icon.dm" diff --git a/mods/content/xenobiology/slime/_slime.dm b/mods/content/xenobiology/slime/_slime.dm index a5b342022c2..60c12cb89e3 100644 --- a/mods/content/xenobiology/slime/_slime.dm +++ b/mods/content/xenobiology/slime/_slime.dm @@ -18,7 +18,7 @@ status_flags = CANPARALYSE|CANPUSH butchery_data = null ai = /datum/mob_controller/slime - hud_used = /datum/hud/slime + hud_used = /datum/hud/animal nutrition = 800 var/is_adult = FALSE @@ -33,7 +33,6 @@ var/slime_type = /decl/slime_colour/grey var/cores = 1 // the number of /obj/item/slime_extract's the slime has left inside var/core_removal_stage = 0 //For removing cores. - var/datum/reagents/metabolism/ingested /mob/living/slime/Destroy() set_feeding_on() @@ -65,8 +64,7 @@ . = ..(mapload) - ingested = new /datum/reagents/metabolism(240, src, CHEM_TOUCH) - reagents = ingested + reagents = new /datum/reagents/metabolism(240, src, CHEM_TOUCH) render_target = "slime_\ref[src]" verbs += /mob/living/proc/ventcrawl @@ -272,7 +270,7 @@ return ..() /mob/living/slime/attackby(var/obj/item/W, var/mob/user) - var/force = W.get_attack_force(user) + var/force = W.expend_attack_force(user) if(force > 0) var/datum/mob_controller/slime/slime_ai = ai if(istype(slime_ai)) diff --git a/mods/content/xenobiology/slime/life.dm b/mods/content/xenobiology/slime/life.dm index 55b0e665ec1..d71a1d3edd4 100644 --- a/mods/content/xenobiology/slime/life.dm +++ b/mods/content/xenobiology/slime/life.dm @@ -46,6 +46,7 @@ . = ..() if(feeding_on) slime_feed() + var/datum/reagents/metabolism/ingested = reagents ingested.metabolize() /mob/living/slime/fluid_act(datum/reagents/fluids) @@ -86,6 +87,7 @@ . = ..() if(feeding_on) slime_feed() + var/datum/reagents/metabolism/ingested = reagents ingested.metabolize() // Digest whatever we've got floating around in our goop. diff --git a/mods/content/xenobiology/slime/slime_AI.dm b/mods/content/xenobiology/slime/slime_AI.dm index 7260e124070..bf28ef6cd6b 100644 --- a/mods/content/xenobiology/slime/slime_AI.dm +++ b/mods/content/xenobiology/slime/slime_AI.dm @@ -2,8 +2,8 @@ expected_type = /mob/living/slime var/mood var/chase_target = 0 - var/mob/living/leader - var/mob/living/current_target // Currently attacking this mob (separate from feeding) + var/weakref/leader + var/weakref/current_target // Currently attacking this mob (separate from feeding) var/attacked = 0 // Determines if it's been attacked recently. Can be any number, is a cooloff-ish variable var/rabid = 0 // If set to 1, the slime will attack and eat anything it comes in contact with var/list/observed_friends // A list of refs to friends; they are not considered targets for feeding; passed down after splitting. @@ -20,6 +20,7 @@ /datum/mob_controller/slime/Destroy() observed_friends = null friendship_cooldown = null + leader = null current_target = null speech_buffer = null slime = null @@ -44,7 +45,7 @@ else if(rabid || attacked) new_mood = "angry" body.set_intent(I_FLAG_HARM) - else if(current_target) + else if(current_target?.resolve()) new_mood = "mischevous" if(!new_mood) @@ -97,22 +98,27 @@ current_target = null return - if(current_target) + var/mob/actual_target = current_target?.resolve() + if(actual_target) chase_target-- if(chase_target <= 0 || attacked || rabid) // Tired of chasing or attacking everything nearby chase_target = 0 current_target = null + else + current_target = null var/hunger = slime.get_hunger_state() - if(!current_target) + var/mob/leader_mob = leader?.resolve() + actual_target = current_target?.resolve() + if(!actual_target) var/feral = (attacked || rabid || hunger >= 2) - if(feral || (!leader && !holding_still) || (hunger && prob(10))) + if(feral || (!leader_mob && !holding_still) || (hunger && prob(10))) var/list/targets for(var/mob/living/prey in view(7, body)) if(assess_target(prey)) LAZYADD(targets, prey) if(length(targets)) - current_target = get_best_target(targets) + current_target = weakref(get_best_target(targets)) chase_target = rand(5,7) if(slime.is_adult) chase_target += 3 @@ -120,8 +126,8 @@ if(holding_still) holding_still = max(holding_still - 1 - hunger, 0) else if(isturf(body?.loc)) - if(leader) - step_to(body, get_dir(body, leader)) + if(leader_mob) + step_to(body, get_dir(body, leader_mob)) else if(prob(hunger ? 50 : 33)) body.SelfMove(pick(global.cardinal)) @@ -131,42 +137,42 @@ return var/added_delay = 0 - if(slime.amount_grown >= SLIME_EVOLUTION_THRESHOLD && !current_target) + var/mob/actual_target = current_target?.resolve() + if(slime.amount_grown >= SLIME_EVOLUTION_THRESHOLD && !actual_target) if(slime.is_adult) slime.slime_split() else slime.slime_mature() added_delay = 10 else - - if(!assess_target(current_target) || current_target == slime.feeding_on || !(current_target in view(7, body))) + if(!assess_target(actual_target) || actual_target == slime.feeding_on || !(actual_target in view(7, body))) current_target = null - if(!current_target) + if(!actual_target) if(prob(1)) for(var/mob/living/slime/frenemy in range(1, body)) if(frenemy != body && body.Adjacent(frenemy)) body.set_intent((frenemy.slime_type == slime.slime_type) ? I_FLAG_HELP : I_FLAG_HARM) body.UnarmedAttack(frenemy, TRUE) added_delay = 10 - else if(slime.Adjacent(current_target)) + else if(slime.Adjacent(actual_target)) var/do_attack = FALSE - if(issilicon(current_target)) + if(issilicon(actual_target)) body.set_intent(I_FLAG_HARM) do_attack = TRUE - else if(current_target.client && !current_target.current_posture.prone && prob(60 + slime.powerlevel * 4)) + else if(actual_target.client && !actual_target.current_posture.prone && prob(60 + slime.powerlevel * 4)) body.set_intent(I_FLAG_DISARM) do_attack = TRUE - else if(slime.check_valid_feed_target(current_target) == FEED_RESULT_VALID) + else if(slime.check_valid_feed_target(actual_target) == FEED_RESULT_VALID) body.set_intent(I_FLAG_GRAB) do_attack = TRUE if(do_attack) - body.UnarmedAttack(current_target, TRUE) + body.UnarmedAttack(actual_target, TRUE) added_delay = 10 else current_target = null else - step_to(body, current_target) + step_to(body, actual_target) next_core_logic_run = world.time + max(body?.get_movement_delay(), 5) + added_delay @@ -208,13 +214,13 @@ /datum/mob_controller/slime/proc/adjust_friendship(var/atom/user, var/amount) if(ismob(user)) - if(QDELETED(user)) + if(QDELING(user)) return FALSE user = weakref(user) else if(istype(user, /weakref)) // verify the ref is still valid var/weakref/user_ref = user - user = user_ref.resolve() - if(!ismob(user) || QDELETED(user)) + var/mob/resolved_user = user_ref.resolve() + if(!ismob(resolved_user) || QDELING(resolved_user)) return FALSE else return FALSE diff --git a/mods/content/xenobiology/slime/slime_commands.dm b/mods/content/xenobiology/slime/slime_commands.dm index 4290f05accf..3be381f2a01 100644 --- a/mods/content/xenobiology/slime/slime_commands.dm +++ b/mods/content/xenobiology/slime/slime_commands.dm @@ -20,15 +20,16 @@ triggers = list("follow") /decl/slime_command/follow/get_response(var/speaker, var/spoken, var/datum/mob_controller/slime/holder) - if(holder.leader) - if(holder.leader == speaker) + var/mob/leader_mob = holder.leader?.resolve() + if(leader_mob) + if(leader_mob == speaker) return pick("Yes...", "Lead...", "Following...") - if(LAZYACCESS(holder.observed_friends, weakref(speaker)) > LAZYACCESS(holder.observed_friends, weakref(holder.leader))) - holder.leader = speaker + if(LAZYACCESS(holder.observed_friends, weakref(speaker)) > LAZYACCESS(holder.observed_friends, holder.leader)) + holder.leader = weakref(speaker) return "Yes... I follow [speaker]..." - return "No... I follow [holder.leader]..." + return "No... I follow [leader_mob]..." if(LAZYACCESS(holder.observed_friends, weakref(speaker)) > 2) - holder.leader = speaker + holder.leader = weakref(speaker) return "I follow..." return pick("No...", "I won't follow...") @@ -45,18 +46,20 @@ holder.adjust_friendship(speaker, -1) return "Grrr..." return "Fine..." - if(holder.current_target) + var/mob/actual_target = holder.current_target?.resolve() + if(actual_target) if(friendship > 3) holder.current_target = null if(friendship < 6) holder.adjust_friendship(speaker, -1) return "Grrr..." return "Fine..." - if(holder.leader) - if(holder.leader == speaker) + var/mob/leader_mob = holder.leader?.resolve() + if(leader_mob) + if(leader_mob == speaker) holder.leader = null return "Yes... I'll stop..." - if(friendship > LAZYACCESS(holder.observed_friends, weakref(holder.leader))) + if(friendship > LAZYACCESS(holder.observed_friends, holder.leader)) holder.leader = null return "Yes... I'll stop..." return "No... I'll keep following..." @@ -66,11 +69,12 @@ /decl/slime_command/stay/get_response(var/speaker, var/spoken, var/datum/mob_controller/slime/holder) var/friendship = LAZYACCESS(holder.observed_friends, weakref(speaker)) - if(holder.leader) - if(holder.leader == speaker) + var/mob/leader_mob = holder.leader?.resolve() + if(leader_mob) + if(leader_mob == speaker) holder.holding_still = friendship * 10 return "Yes... Staying..." - var/leader_friendship = LAZYACCESS(holder.observed_friends, weakref(holder.leader)) + var/leader_friendship = LAZYACCESS(holder.observed_friends, holder.leader) if(friendship > leader_friendship) holder.holding_still = (friendship - leader_friendship) * 10 return "Yes... Staying..." diff --git a/mods/content/xenobiology/slime/slime_comments.dm b/mods/content/xenobiology/slime/slime_comments.dm index bd8ad9f1146..f009e313123 100644 --- a/mods/content/xenobiology/slime/slime_comments.dm +++ b/mods/content/xenobiology/slime/slime_comments.dm @@ -29,8 +29,9 @@ if(holder.slime.nutrition < holder.slime.get_starve_nutrition()) . += list("So... hungry...", "Very... hungry...", "Need... food...", "Must... eat...") tension += 10 - if(holder.current_target) - . += "\The [holder.current_target]... looks tasty..." + var/mob/actual_target = holder.current_target?.resolve() + if(actual_target) + . += "\The [actual_target]... looks tasty..." if(length(.) && prob(tension)) return pick(.) diff --git a/mods/content/xenobiology/slime/slime_hud.dm b/mods/content/xenobiology/slime/slime_hud.dm deleted file mode 100644 index 2cdd315d671..00000000000 --- a/mods/content/xenobiology/slime/slime_hud.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/hud/slime/FinalizeInstantiation() - action_intent = new(null, mymob) - adding = list(action_intent) - ..() diff --git a/mods/content/xenobiology/slime/slime_reagents.dm b/mods/content/xenobiology/slime/slime_reagents.dm index bff84fd00ee..882797de759 100644 --- a/mods/content/xenobiology/slime/slime_reagents.dm +++ b/mods/content/xenobiology/slime/slime_reagents.dm @@ -14,22 +14,23 @@ metabolism = REM * 0.25 exoplanet_rarity_gas = MAT_RARITY_EXOTIC -/decl/material/liquid/water/affect_touch(var/mob/living/M, var/removed, var/datum/reagents/holder) +/decl/material/liquid/water/affect_touch(var/mob/living/victim, var/removed, var/datum/reagents/holder) . = ..() - if(isslime(M)) - M.take_damage(10 * removed, TOX) - var/mob/living/slime/S = M - if(istype(S) && istype(S.ai, /datum/mob_controller/slime)) - var/datum/mob_controller/slime/slime_ai = S.ai - if(slime_ai.current_target) + if(isslime(victim)) + victim.take_damage(10 * removed, TOX) + var/mob/living/slime/slime_victim = victim + if(istype(slime_victim) && istype(slime_victim.ai, /datum/mob_controller/slime)) + var/datum/mob_controller/slime/slime_ai = slime_victim.ai + if(slime_ai.current_target) // don't bother resolving it, we're just clearing it slime_ai.current_target = null - S.set_feeding_on() - if(LAZYACCESS(M.chem_doses, type) == removed) - M.visible_message( \ - SPAN_DANGER("\The [S]'s flesh sizzles where \the [name] touches it!"), \ - SPAN_DANGER("Your flesh is burned by \the [name]!")) - SET_STATUS_MAX(M, STAT_CONFUSE, 2) - var/datum/mob_controller/slime/slime_ai = M.ai + slime_victim.set_feeding_on() + if(LAZYACCESS(victim.chem_doses, type) == removed) + var/reagent_name = get_reagent_name(holder) // mostly to check masked name, but handles phase too + victim.visible_message( \ + SPAN_DANGER("\The [slime_victim]'s flesh sizzles where \the [reagent_name] touches it!"), \ + SPAN_DANGER("Your flesh is burned by \the [reagent_name]!")) + SET_STATUS_MAX(victim, STAT_CONFUSE, 2) + var/datum/mob_controller/slime/slime_ai = victim.ai if(istype(slime_ai)) slime_ai.attacked = max(slime_ai.attacked, rand(7,10)) // angery diff --git a/mods/gamemodes/cult/_cult.dme b/mods/gamemodes/cult/_cult.dme index 82d4e19aed5..02138255d74 100644 --- a/mods/gamemodes/cult/_cult.dme +++ b/mods/gamemodes/cult/_cult.dme @@ -21,7 +21,11 @@ #include "special_role.dm" #include "structures.dm" #include "talisman.dm" -#include "cultify\de-cultify.dm" +#include "abilities\_handler.dm" +#include "abilities\construct.dm" +#include "abilities\harvest.dm" +#include "abilities\shade.dm" +#include "cultify\de-cultify.dm" #include "cultify\defile.dm" #include "cultify\mob.dm" #include "cultify\turf.dm" @@ -29,7 +33,12 @@ #include "mobs\shade.dm" #include "mobs\constructs\constructs.dm" #include "mobs\constructs\soulstone.dm" -#include "spells\construct.dm" -#include "spells\harvest.dm" // END_INCLUDE -#endif \ No newline at end of file +#endif +// BEGIN_INTERNALS +// END_INTERNALS +// BEGIN_FILE_DIR +#define FILE_DIR . +// END_FILE_DIR +// BEGIN_PREFERENCES +// END_PREFERENCES diff --git a/mods/gamemodes/cult/abilities/_handler.dm b/mods/gamemodes/cult/abilities/_handler.dm new file mode 100644 index 00000000000..f346bb73fb4 --- /dev/null +++ b/mods/gamemodes/cult/abilities/_handler.dm @@ -0,0 +1,21 @@ +/obj/screen/ability/category/cult + name = "Toggle Construct Abilities" + icon = 'mods/gamemodes/cult/icons/abilities.dmi' + +/obj/screen/ability/button/cult + icon = 'mods/gamemodes/cult/icons/abilities.dmi' + +/datum/ability_handler/cult + category_toggle_type = /obj/screen/ability/category/cult + +/decl/ability/cult + abstract_type = /decl/ability/cult + ability_icon = 'mods/gamemodes/cult/icons/abilities.dmi' + ability_icon_state = "artificer" + associated_handler_type = /datum/ability_handler/cult + ui_element_type = /obj/screen/ability/button/cult + ability_cooldown_time = 60 SECONDS + +/obj/item/ability/cult + icon = 'mods/gamemodes/cult/icons/ability_item.dmi' + color = COLOR_RED diff --git a/mods/gamemodes/cult/abilities/construct.dm b/mods/gamemodes/cult/abilities/construct.dm new file mode 100644 index 00000000000..60138eaba35 --- /dev/null +++ b/mods/gamemodes/cult/abilities/construct.dm @@ -0,0 +1,131 @@ +//////////////////////////////Construct Spells///////////////////////// +/decl/ability/cult/construct + name = "Artificer" + desc = "This spell conjures a construct which may be controlled by shades." + target_selector = /decl/ability_targeting/clear_turf + overlay_icon = 'mods/gamemodes/cult/icons/effects.dmi' + overlay_icon_state = "sparkles" + target_selector = /decl/ability_targeting/clear_turf/construct + var/summon_type = /obj/structure/constructshell + +/decl/ability_targeting/clear_turf/construct/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) + var/decl/ability/cult/construct/cult_ability = ability + if(!istype(cult_ability)) + return FALSE + return ..() && !istype(target, cult_ability.summon_type) && !(locate(cult_ability.summon_type) in target) + +/decl/ability/cult/construct/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) + . = ..() + var/turf/target_turf = get_turf(hit_target) + if(istype(target_turf)) + if(ispath(summon_type, /turf)) + target_turf = target_turf.ChangeTurf(summon_type, TRUE, FALSE, TRUE, TRUE, FALSE) + if(target_turf) // We reapply effects as target no longer exists. + apply_effect_to(user, target_turf, metadata) + else if(ispath(summon_type, /atom)) + new summon_type(target_turf) + +/decl/ability/cult/construct/lesser + ability_cooldown_time = 2 MINUTES + summon_type = /obj/structure/constructshell/cult + ability_icon_state = "const_shell" + +/decl/ability/cult/construct/floor + name = "Floor Construction" + desc = "This spell constructs a cult floor" + ability_cooldown_time = 2 SECONDS + summon_type = /turf/floor/cult + ability_icon_state = "const_floor" + overlay_icon_state = "cultfloor" + +/decl/ability/cult/construct/wall + name = "Lesser Construction" + desc = "This spell constructs a cult wall" + ability_cooldown_time = 10 SECONDS + summon_type = /turf/wall/cult + ability_icon_state = "const_wall" + overlay_icon_state = "cultwall" + +/decl/ability/cult/construct/wall/reinforced + name = "Greater Construction" + desc = "This spell constructs a reinforced metal wall" + ability_cooldown_time = 30 SECONDS + summon_type = /turf/wall/r_wall + +/decl/ability/cult/construct/soulstone + name = "Summon Soulstone" + desc = "This spell reaches into Nar-Sie's realm, summoning one of the legendary fragments across time and space." + ability_cooldown_time = 5 MINUTES + summon_type = /obj/item/soulstone + ability_icon_state = "const_stone" + +/decl/ability/cult/construct/pylon + name = "Red Pylon" + desc = "This spell conjures a fragile crystal from Nar-Sie's realm. Makes for a convenient light source." + ability_cooldown_time = 20 SECONDS + summon_type = /obj/structure/cult/pylon + ability_icon_state = "const_pylon" + target_selector = /decl/ability_targeting/pylon + is_melee_invocation = TRUE + prep_cast = TRUE + +/decl/ability_targeting/pylon/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) + . = ..() + if(!.) + return + if(istype(target, /obj/structure/cult/pylon)) + return TRUE + if(isturf(target)) + var/turf/target_turf = target + // We can repair pylons, so let us target turfs containing broken pylons. + if(target_turf.contains_dense_objects(user)) + for(var/obj/structure/cult/pylon/pylon in target_turf) + if(pylon.isbroken) + return TRUE + return FALSE + // We can summon pylons in empty turfs. + return TRUE + return FALSE + +/decl/ability/cult/construct/pylon/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) + for(var/obj/structure/cult/pylon/P in get_turf(hit_target)) + if(P.isbroken) + P.repair(user) + return TRUE + . = ..() + +/decl/ability/cult/construct/forcewall/lesser + name = "Force Shield" + desc = "Allows you to pull up a shield to protect yourself and allies from incoming threats" + summon_type = /obj/effect/cult_force_wall + ability_cooldown_time = 30 SECONDS + ability_use_channel = 20 SECONDS + ability_icon_state = "const_juggwall" + prepare_message_3p_str = "$USER$ begins to twist and warp space around $TARGET$, building a wall of force." + prepare_message_1p_str = "You begin the lengthy process of warping local space to form a wall of force." + cast_message_3p_str = "$USER$ completes a wall of force!" + cast_message_1p_str = "You complete a wall of force!" + fail_cast_1p_str = "The screaming fabric of spacetime escapes your grip, and the wall of force vanishes." + +//Code for the Juggernaut construct's forcefield, that seemed like a good place to put it. +/obj/effect/cult_force_wall + desc = "This eerie-looking obstacle seems to have been pulled from another dimension through sheer force." + name = "wall of force" + icon = 'mods/gamemodes/cult/icons/effects.dmi' + icon_state = "m_shield_cult" + light_color = "#b40000" + light_range = 2 + anchored = TRUE + opacity = FALSE + density = TRUE + +/obj/effect/cult_force_wall/Initialize(mapload) + . = ..() + addtimer(CALLBACK(src, PROC_REF(vanish)), 30 SECONDS) + +/obj/effect/cult_force_wall/proc/vanish() + density = FALSE + icon_state = "m_shield_cult_vanish" + sleep(12) + if(!QDELETED(src)) + qdel(src) diff --git a/mods/gamemodes/cult/abilities/harvest.dm b/mods/gamemodes/cult/abilities/harvest.dm new file mode 100644 index 00000000000..49569d6a25e --- /dev/null +++ b/mods/gamemodes/cult/abilities/harvest.dm @@ -0,0 +1,41 @@ +/decl/ability/cult/construct/harvest + name = "Harvest" + desc = "Back to where I come from, and you're coming with me." + ability_cooldown_time = 20 SECONDS + ability_use_channel = 10 SECONDS + overlay_icon_state = "rune_teleport" + overlay_lifespan = 0 + ability_icon_state = "const_harvest" + prepare_message_3p_str = "Space around $USER$ begins to bubble and decay as a terrible vista begins to intrude..." + prepare_message_1p_str = "You bore through space and time, seeking the essence of the Geometer of Blood." + fail_cast_1p_str = "Reality reasserts itself, preventing your return to Nar-Sie." + target_selector = /decl/ability_targeting/living_mob + +/decl/ability/cult/construct/harvest/can_use_ability(mob/user, list/metadata, silent) + . = ..() + if(.) + var/destination + for(var/obj/effect/narsie/N in global.narsie_list) + destination = N.loc + break + if(!destination) + to_chat(user, SPAN_DANGER("You cannot sense the Geometer of Blood!")) + return FALSE + +/decl/ability/cult/construct/harvest/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) + ..() + var/destination = null + for(var/obj/effect/narsie/N in global.narsie_list) + destination = N.loc + break + if(!destination) + to_chat(user, SPAN_DANGER("You cannot sense the Geometer of Blood!")) + return + if(ismob(hit_target) && hit_target != user) + var/mob/living/victim = hit_target + to_chat(user, SPAN_SINISTER("You warp back to Nar-Sie along with your prey.")) + to_chat(victim, SPAN_SINISTER("You are wrenched through time and space and thrown into chaos!")) + victim.dropInto(destination) + else + to_chat(user, SPAN_SINISTER("You warp back to Nar-Sie.")) + user.dropInto(destination) diff --git a/mods/gamemodes/cult/abilities/shade.dm b/mods/gamemodes/cult/abilities/shade.dm new file mode 100644 index 00000000000..5fab626d14d --- /dev/null +++ b/mods/gamemodes/cult/abilities/shade.dm @@ -0,0 +1 @@ +/decl/ability/cult/construct/shift \ No newline at end of file diff --git a/mods/gamemodes/cult/flooring.dm b/mods/gamemodes/cult/flooring.dm index a92973b1077..71aff2a3ff3 100644 --- a/mods/gamemodes/cult/flooring.dm +++ b/mods/gamemodes/cult/flooring.dm @@ -7,6 +7,6 @@ turf_flags = TURF_ACID_IMMUNE | TURF_REMOVE_WRENCH can_paint = null -/decl/flooring/reinforced/cult/on_remove() +/decl/flooring/reinforced/cult/on_flooring_remove(turf/removing_from) var/decl/special_role/cultist/cult = GET_DECL(/decl/special_role/cultist) cult.remove_cultiness(CULTINESS_PER_TURF) diff --git a/mods/gamemodes/cult/hell_universe.dm b/mods/gamemodes/cult/hell_universe.dm index c5e5ad0adf0..5133b72ba09 100644 --- a/mods/gamemodes/cult/hell_universe.dm +++ b/mods/gamemodes/cult/hell_universe.dm @@ -40,9 +40,3 @@ In short: for(var/mob/living/simple_animal/M in SSmobs.mob_list) if(M && !M.client) M.set_stat(DEAD) - -// Disable Narsie when we enter other (non-hell) universe states -/datum/universal_state/supermatter_cascade/OnEnter() - // Disable Nar-Sie. - var/decl/special_role/cultist/cult = GET_DECL(/decl/special_role/cultist) - cult.allow_narsie = 0 \ No newline at end of file diff --git a/mods/gamemodes/cult/icons/abilities.dmi b/mods/gamemodes/cult/icons/abilities.dmi new file mode 100644 index 00000000000..f1d5d8bc121 Binary files /dev/null and b/mods/gamemodes/cult/icons/abilities.dmi differ diff --git a/mods/gamemodes/cult/icons/ability_item.dmi b/mods/gamemodes/cult/icons/ability_item.dmi new file mode 100644 index 00000000000..e22793f4f6b Binary files /dev/null and b/mods/gamemodes/cult/icons/ability_item.dmi differ diff --git a/mods/gamemodes/cult/icons/effects.dmi b/mods/gamemodes/cult/icons/effects.dmi new file mode 100644 index 00000000000..9e238941454 Binary files /dev/null and b/mods/gamemodes/cult/icons/effects.dmi differ diff --git a/mods/gamemodes/cult/icons/forcewall.dmi b/mods/gamemodes/cult/icons/forcewall.dmi new file mode 100644 index 00000000000..04b8560cdac Binary files /dev/null and b/mods/gamemodes/cult/icons/forcewall.dmi differ diff --git a/icons/effects/uristrunes.dmi b/mods/gamemodes/cult/icons/runes.dmi similarity index 100% rename from icons/effects/uristrunes.dmi rename to mods/gamemodes/cult/icons/runes.dmi diff --git a/mods/gamemodes/cult/items.dm b/mods/gamemodes/cult/items.dm index 2b2e826617c..e4faf111e49 100644 --- a/mods/gamemodes/cult/items.dm +++ b/mods/gamemodes/cult/items.dm @@ -26,7 +26,7 @@ to_chat(user, SPAN_DANGER("An unexplicable force rips through you, tearing the sword from your grasp!")) //random amount of damage between half of the blade's force and the full force of the blade. - var/force = get_attack_force(user) + var/force = expend_attack_force(user) user.apply_damage(rand(force/2, force), BRUTE, zone, (DAM_SHARP|DAM_EDGE), armor_pen = 100) SET_STATUS_MAX(user, STAT_WEAK, 5) diff --git a/mods/gamemodes/cult/mobs/constructs/constructs.dm b/mods/gamemodes/cult/mobs/constructs/constructs.dm index 1f10369a694..1eb37f6980b 100644 --- a/mods/gamemodes/cult/mobs/constructs/constructs.dm +++ b/mods/gamemodes/cult/mobs/constructs/constructs.dm @@ -5,7 +5,6 @@ speak_emote = list("hisses") base_animal_type = /mob/living/simple_animal/construct base_movement_delay = -1 - response_help_1p = "You think better of touching $TARGET$." response_help_3p = "$USER$ thinks better of touching $TARGET$." response_disarm = "flails at" @@ -31,7 +30,7 @@ z_flags = ZMM_MANGLE_PLANES glowing_eyes = TRUE ai = /datum/mob_controller/aggressive/construct - var/list/construct_spells = list() + var/list/construct_spells /datum/mob_controller/aggressive/construct emote_speech = list("Hsssssssszsht.", "Hsssssssss...", "Tcshsssssssszht!") @@ -54,7 +53,7 @@ add_language(/decl/language/cultcommon) add_language(/decl/language/cult) for(var/spell in construct_spells) - src.add_spell(new spell, "const_spell_ready") + add_ability(spell) set_light(1.5, -2, COLOR_WHITE) update_icon() @@ -107,7 +106,9 @@ environment_smash = 2 status_flags = 0 resistance = 10 - construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser) + construct_spells = list( + /decl/ability/cult/construct/forcewall/lesser + ) hud_used = /datum/hud/construct/juggernaut base_movement_delay = 2 ai = /datum/mob_controller/aggressive/construct_armoured @@ -163,7 +164,9 @@ natural_weapon = /obj/item/natural_weapon/wraith environment_smash = 1 see_in_dark = 7 - construct_spells = list(/spell/targeted/ethereal_jaunt/shift) + construct_spells = list( + /decl/ability/cult/construct/shift + ) hud_used = /datum/hud/construct/wraith /obj/item/natural_weapon/wraith @@ -194,11 +197,11 @@ natural_weapon = /obj/item/natural_weapon/cult_builder environment_smash = 1 construct_spells = list( - /spell/aoe_turf/conjure/construct/lesser, - /spell/aoe_turf/conjure/wall, - /spell/aoe_turf/conjure/floor, - /spell/aoe_turf/conjure/soulstone, - /spell/aoe_turf/conjure/pylon + /decl/ability/cult/construct/lesser, + /decl/ability/cult/construct/wall, + /decl/ability/cult/construct/floor, + /decl/ability/cult/construct/soulstone, + /decl/ability/cult/construct/pylon ) hud_used = /datum/hud/construct/artificer base_movement_delay = 0 @@ -228,7 +231,9 @@ natural_weapon = /obj/item/natural_weapon/juggernaut/behemoth environment_smash = 2 resistance = 10 - construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser) + construct_spells = list( + /decl/ability/cult/construct/lesser + ) hud_used = /datum/hud/construct/juggernaut base_movement_delay = 2 ai = /datum/mob_controller/aggressive/construct_armoured @@ -249,7 +254,7 @@ see_in_dark = 7 hud_used = /datum/hud/construct/harvester construct_spells = list( - /spell/targeted/harvest + /decl/ability/cult/construct/harvest ) /obj/item/natural_weapon/harvester @@ -264,14 +269,14 @@ /mob/living/simple_animal/construct/handle_regular_status_updates() . = ..() if(.) - silence_spells(purge) + disable_abilities(purge) /mob/living/simple_animal/construct/handle_regular_hud_updates() . = ..() if(.) if(fire) fire.icon_state = "fire[!!GET_HUD_ALERT(src, /decl/hud_element/condition/fire)]" - silence_spells(purge) + disable_abilities(purge) if(healths) switch(current_health) if(250 to INFINITY) healths.icon_state = "health0" diff --git a/mods/gamemodes/cult/mobs/constructs/soulstone.dm b/mods/gamemodes/cult/mobs/constructs/soulstone.dm index a6e8d693c3b..69e5d0ef21e 100644 --- a/mods/gamemodes/cult/mobs/constructs/soulstone.dm +++ b/mods/gamemodes/cult/mobs/constructs/soulstone.dm @@ -51,7 +51,7 @@ to_chat(user, SPAN_NOTICE("You cleanse \the [src] of taint, purging its shackles to its creator.")) is_evil = FALSE return TRUE - else if(I.get_attack_force(user) >= 5) + else if(I.expend_attack_force(user) >= 5) if(full != SOULSTONE_CRACKED) user.visible_message( SPAN_WARNING("\The [user] hits \the [src] with \the [I], and it breaks.[shade.client ? " You hear a terrible scream!" : ""]"), @@ -107,6 +107,33 @@ full = f update_icon() +// Soulstone synthesis recipe. +/decl/chemical_reaction/synthesis/soulstone + name = "Soulstone" + result = null + required_reagents = list(/decl/material/liquid/blood = 15, /decl/material/liquid/crystal_agent = 1) + result_amount = 1 + hidden_from_codex = TRUE // This shouldn't show up in search. Maybe it should be linked in a 'guide to cult' or something? + +/decl/chemical_reaction/synthesis/soulstone/send_data(datum/reagents/holder, reaction_limit) + return REAGENT_DATA(holder, /decl/material/liquid/blood) // allow on_reaction to get donor data + +/// Whether or not the reaction should produce a soulstone or a normal crystal. +/// The donor mob parameter may either be /mob/living or null. +/decl/chemical_reaction/synthesis/soulstone/proc/donor_is_magic(mob/living/donor) + return FALSE // By default, no one is magic! This is for modpacks to override. + +/decl/chemical_reaction/synthesis/soulstone/on_reaction(datum/reagents/holder, created_volume, list/reaction_data) + var/location = get_turf(holder.get_reaction_loc(chemical_reaction_flags)) + var/weakref/donor_ref = LAZYACCESS(reaction_data, DATA_BLOOD_DONOR) + if(donor_is_magic(donor_ref?.resolve())) + for(var/i = 1, i <= created_volume, i++) + new /obj/item/soulstone(location) + else // waste it and produce useless crystal shards + for(var/i = 1, i <= created_volume*2, i++) + new /obj/item/shard(location, /decl/material/solid/gemstone/crystal) + +// Construct shells. These accept soulstones. /obj/structure/constructshell name = "empty shell" icon = 'icons/obj/structures/construct.dmi' diff --git a/mods/gamemodes/cult/overrides.dm b/mods/gamemodes/cult/overrides.dm index 2077c9f0b1e..2d822d252c7 100644 --- a/mods/gamemodes/cult/overrides.dm +++ b/mods/gamemodes/cult/overrides.dm @@ -15,6 +15,7 @@ playsound(src, 'sound/effects/ghost2.ogg', 10, 5) /datum/trader/ship/clothingshop/hatglovesaccessories/New() + ..() possible_trading_items[/obj/item/clothing/head/culthood] = TRADER_BLACKLIST_ALL /mob/living/silicon/ai @@ -47,9 +48,9 @@ /mob/living/simple_animal/hostile/revenant/cult/on_defilement() return -/obj/item/mop/Initialize() +/obj/item/mop/populate_moppable_types() . = ..() - moppable_types += /obj/effect/rune + moppable_types |= /obj/effect/rune /obj/effect/gateway/active/can_transform(mob/victim) if(iscultist(victim)) diff --git a/mods/gamemodes/cult/runes.dm b/mods/gamemodes/cult/runes.dm index 161d225a648..c54cb016914 100644 --- a/mods/gamemodes/cult/runes.dm +++ b/mods/gamemodes/cult/runes.dm @@ -2,7 +2,7 @@ name = "rune" desc = "A strange collection of symbols drawn in blood." anchored = TRUE - icon = 'icons/effects/uristrunes.dmi' + icon = 'mods/gamemodes/cult/icons/runes.dmi' icon_state = "blank" layer = RUNE_LAYER @@ -24,7 +24,7 @@ if(cult.rune_strokes[type]) var/list/f = cult.rune_strokes[type] for(var/i in f) - var/image/t = image('icons/effects/uristrunes.dmi', "rune-[i]") + var/image/t = image(icon, "rune-[i]") overlays += t else var/list/q = list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) @@ -33,7 +33,7 @@ var/j = pick(q) f += j q -= f - var/image/t = image('icons/effects/uristrunes.dmi', "rune-[j]") + var/image/t = image(icon, "rune-[j]") overlays += t cult.rune_strokes[type] = f.Copy() color = bcolor @@ -322,7 +322,7 @@ user.visible_message(SPAN_NOTICE("\The [user] touches \the [src] with \the [I], and it disappears."), SPAN_NOTICE("You disrupt the vile magic with the deadening field of \the [I].")) qdel(src) return TRUE - var/force = I.get_attack_force(user) + var/force = I.expend_attack_force(user) if(force) user.visible_message(SPAN_NOTICE("\The [user] hits \the [src] with \the [I]."), SPAN_NOTICE("You hit \the [src] with \the [I].")) take_damage(force, I.atom_damage_type) @@ -480,7 +480,6 @@ var/list/mob/living/casters = get_cultists() if(casters.len < 3) break - //T.turf_animation('icons/effects/effects.dmi', "rune_sac") victim.set_fire_intensity(max(2, victim.get_fire_intensity())) victim.ignite_fire() var/dam_amt = 2 + length(casters) diff --git a/mods/gamemodes/cult/spells/construct.dm b/mods/gamemodes/cult/spells/construct.dm deleted file mode 100644 index 05c35c46785..00000000000 --- a/mods/gamemodes/cult/spells/construct.dm +++ /dev/null @@ -1,122 +0,0 @@ -//////////////////////////////Construct Spells///////////////////////// - -/spell/aoe_turf/conjure/construct - name = "Artificer" - desc = "This spell conjures a construct which may be controlled by Shades." - - school = "conjuration" - charge_max = 600 - spell_flags = 0 - invocation = "none" - invocation_type = SpI_NONE - range = 0 - - summon_type = list(/obj/structure/constructshell) - - hud_state = "artificer" - -/spell/aoe_turf/conjure/construct/lesser - charge_max = 1800 - summon_type = list(/obj/structure/constructshell/cult) - hud_state = "const_shell" - override_base = "const" - -/spell/aoe_turf/conjure/floor - name = "Floor Construction" - desc = "This spell constructs a cult floor" - - charge_max = 20 - spell_flags = Z2NOCAST | CONSTRUCT_CHECK - invocation = "none" - invocation_type = SpI_NONE - range = 0 - summon_type = list(/turf/floor/cult) - - hud_state = "const_floor" - -/spell/aoe_turf/conjure/wall - name = "Lesser Construction" - desc = "This spell constructs a cult wall" - - charge_max = 100 - spell_flags = Z2NOCAST | CONSTRUCT_CHECK - invocation = "none" - invocation_type = SpI_NONE - range = 0 - summon_type = list(/turf/wall/cult) - - hud_state = "const_wall" - -/spell/aoe_turf/conjure/wall/reinforced - name = "Greater Construction" - desc = "This spell constructs a reinforced metal wall" - - charge_max = 300 - spell_flags = Z2NOCAST - invocation = "none" - invocation_type = SpI_NONE - range = 0 - cast_delay = 50 - - summon_type = list(/turf/wall/r_wall) - -/spell/aoe_turf/conjure/soulstone - name = "Summon Soulstone" - desc = "This spell reaches into Nar-Sie's realm, summoning one of the legendary fragments across time and space" - - charge_max = 3000 - spell_flags = 0 - invocation = "none" - invocation_type = SpI_NONE - range = 0 - - summon_type = list(/obj/item/soulstone) - - hud_state = "const_stone" - override_base = "const" - -/spell/aoe_turf/conjure/pylon - name = "Red Pylon" - desc = "This spell conjures a fragile crystal from Nar-Sie's realm. Makes for a convenient light source." - - charge_max = 200 - spell_flags = CONSTRUCT_CHECK - invocation = "none" - invocation_type = SpI_NONE - range = 0 - - summon_type = list(/obj/structure/cult/pylon) - - hud_state = "const_pylon" - -/spell/aoe_turf/conjure/pylon/cast(list/targets, mob/user) - ..() - var/turf/spawn_place = pick(targets) - for(var/obj/structure/cult/pylon/P in spawn_place.contents) - if(P.isbroken) - P.repair(user) - continue - return - -/spell/aoe_turf/conjure/forcewall/lesser - name = "Force Shield" - desc = "Allows you to pull up a shield to protect yourself and allies from incoming threats" - - charge_max = 300 - spell_flags = 0 - invocation = "none" - invocation_type = SpI_NONE - range = 0 - summon_type = list(/obj/effect/forcefield/cult) - duration = 200 - - hud_state = "const_juggwall" - -//Code for the Juggernaut construct's forcefield, that seemed like a good place to put it. -/obj/effect/forcefield/cult - desc = "That eerie looking obstacle seems to have been pulled from another dimension through sheer force." - name = "Juggerwall" - icon = 'icons/effects/effects.dmi' - icon_state = "m_shield_cult" - light_color = "#b40000" - light_range = 2 \ No newline at end of file diff --git a/mods/gamemodes/cult/spells/harvest.dm b/mods/gamemodes/cult/spells/harvest.dm deleted file mode 100644 index 41854b772bc..00000000000 --- a/mods/gamemodes/cult/spells/harvest.dm +++ /dev/null @@ -1,37 +0,0 @@ -/spell/targeted/harvest - name = "Harvest" - desc = "Back to where I come from, and you're coming with me." - - school = "transmutation" - charge_max = 200 - spell_flags = Z2NOCAST | CONSTRUCT_CHECK | INCLUDEUSER - invocation = "" - invocation_type = SpI_NONE - range = 0 - max_targets = 0 - - overlay = 1 - overlay_icon = 'icons/effects/effects.dmi' - overlay_icon_state = "rune_teleport" - overlay_lifespan = 0 - - hud_state = "const_harvest" - -/spell/targeted/harvest/cast(list/targets, mob/user)//because harvest is already a proc - ..() - - var/destination = null - for(var/obj/effect/narsie/N in global.narsie_list) - destination = N.loc - break - if(destination) - var/prey = 0 - for(var/mob/living/M in targets) - if(!findNullRod(M)) - M.forceMove(destination) - if(M != user) - prey = 1 - to_chat(user, "You warp back to Nar-Sie[prey ? " along with your prey":""].") - else - to_chat(user, "...something's wrong!")//There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world. - diff --git a/mods/gamemodes/cult/structures.dm b/mods/gamemodes/cult/structures.dm index 51db7add603..9f39d61522d 100644 --- a/mods/gamemodes/cult/structures.dm +++ b/mods/gamemodes/cult/structures.dm @@ -13,10 +13,10 @@ desc = "A floating crystal that hums with an unearthly energy." icon = 'icons/obj/structures/pylon.dmi' icon_state = "pylon" - var/isbroken = 0 light_power = 0.5 light_range = 13 light_color = "#3e0000" + var/isbroken = FALSE /obj/structure/cult/pylon/attack_hand(mob/M) SHOULD_CALL_PARENT(FALSE) @@ -28,7 +28,7 @@ return TRUE /obj/structure/cult/pylon/attackby(obj/item/W, mob/user) - attackpylon(user, W.get_attack_force(user)) + attackpylon(user, W.expend_attack_force(user)) return TRUE /obj/structure/cult/pylon/proc/attackpylon(mob/user, var/damage) @@ -42,7 +42,7 @@ ) user.do_attack_animation(src) playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1) - isbroken = 1 + isbroken = TRUE set_density(0) icon_state = "pylon-broken" set_light(0) @@ -61,10 +61,12 @@ /obj/structure/cult/pylon/proc/repair(mob/user) if(isbroken) to_chat(user, "You repair the pylon.") - isbroken = 0 + isbroken = FALSE set_density(1) icon_state = "pylon" set_light(13, 0.5) + return TRUE + return FALSE /obj/structure/cult/pylon/get_artifact_scan_data() return "Tribal pylon - subject resembles statues/emblems built by cargo cult civilisations to honour energy systems from post-warp civilisations." diff --git a/mods/gamemodes/heist/_heist.dme b/mods/gamemodes/heist/_heist.dme index 57bf5c58b42..de2a67d69d3 100644 --- a/mods/gamemodes/heist/_heist.dme +++ b/mods/gamemodes/heist/_heist.dme @@ -1,13 +1,5 @@ #ifndef GAMEMODE_PACK_HEIST #define GAMEMODE_PACK_HEIST - -#ifdef MODPACK_VOX -#warn Vox modpack loaded before Heist modpack, compatibility features will be missing. -#endif -#ifdef GAMEMODE_PACK_MIXED -#warn Mixed gamemodes modpack loaded before Heist modpack, Heist combination modes will be missing. -#endif - // BEGIN_INCLUDE #include "_heist.dm" #include "areas.dm" diff --git a/mods/gamemodes/meteor/gamemode.dm b/mods/gamemodes/meteor/gamemode.dm index 10212d20aa9..5836f1ad154 100644 --- a/mods/gamemodes/meteor/gamemode.dm +++ b/mods/gamemodes/meteor/gamemode.dm @@ -93,8 +93,7 @@ /obj/effect/meteor/irradiated=10, /obj/effect/meteor/golden=10, /obj/effect/meteor/silver=10, - /obj/effect/meteor/flaming=10, - /obj/effect/meteor/supermatter=1 + /obj/effect/meteor/flaming=10 ) // As a bonus, more frequent events. diff --git a/mods/gamemodes/mixed.dm b/mods/gamemodes/mixed.dm new file mode 100644 index 00000000000..f89baef2aa8 --- /dev/null +++ b/mods/gamemodes/mixed.dm @@ -0,0 +1,7 @@ +#ifndef GAMEMODE_PACK_MIXED +#define GAMEMODE_PACK_MIXED +#endif + +// This modpack doesn't actually have anything here, and instead it uses the compatibility patch system to make load order not matter. +/decl/modpack/mixed_modes + name = "Mixed Gamemodes" \ No newline at end of file diff --git a/mods/gamemodes/mixed/_mixed.dm b/mods/gamemodes/mixed/_mixed.dm deleted file mode 100644 index c1c9167d414..00000000000 --- a/mods/gamemodes/mixed/_mixed.dm +++ /dev/null @@ -1,2 +0,0 @@ -/decl/modpack/mixed_modes - name = "Mixed Gamemodes" \ No newline at end of file diff --git a/mods/gamemodes/mixed/_mixed.dme b/mods/gamemodes/mixed/_mixed.dme deleted file mode 100644 index 6db33d07706..00000000000 --- a/mods/gamemodes/mixed/_mixed.dme +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef GAMEMODE_PACK_MIXED -#define GAMEMODE_PACK_MIXED -// BEGIN_INCLUDE -#include "_mixed.dm" -#if defined(GAMEMODE_PACK_HEIST) // TODO: && defined(GAMEMODE_PACK_MERCENARY) -#include "crossfire.dm" -#endif -#if defined(GAMEMODE_PACK_REVOLUTIONARY) -#include "siege.dm" -#endif -#if defined(GAMEMODE_PACK_REVOLUTIONARY) && defined(GAMEMODE_PACK_CULT) -#include "uprising.dm" -#endif -// END_INCLUDE -#endif \ No newline at end of file diff --git a/mods/mobs/borers/_borers.dme b/mods/mobs/borers/_borers.dme index 380c95f4e87..7f230fc9536 100644 --- a/mods/mobs/borers/_borers.dme +++ b/mods/mobs/borers/_borers.dme @@ -1,10 +1,5 @@ #ifndef CONTENT_PACK_BORERS #define CONTENT_PACK_BORERS - -#ifdef MODPACK_PSIONICS -#warn Psionics modpack loaded before Borers modpack, compatibility features will be missing. -#endif - // BEGIN_INCLUDE #include "borer.dm" #include "datum\antagonist.dm" diff --git a/mods/mobs/borers/datum/antagonist.dm b/mods/mobs/borers/datum/antagonist.dm index c714ef8e1a1..423297f09f8 100644 --- a/mods/mobs/borers/datum/antagonist.dm +++ b/mods/mobs/borers/datum/antagonist.dm @@ -7,12 +7,12 @@ welcome_text = "Click a target while on GRAB intent to crawl into their ear and infiltrate their brain. You can only take control temporarily, and at risk of hurting your host, so be clever and careful; your host is encouraged to help you however they can. Talk to your host with Say, and your fellow borers with ,z." antag_indicator = "hudborer" antaghud_indicator = "hudborer" + antag_hud_icon = 'mods/mobs/borers/icons/hud.dmi' faction_name = "Borer Host" faction_descriptor = "Unity" faction_welcome = "You are now host to a cortical borer. Please listen to what they have to say; they're in your head." faction = "borer" - faction_indicator = "hudalien" hard_cap = 5 hard_cap_round = 8 diff --git a/mods/mobs/borers/datum/symbiote.dm b/mods/mobs/borers/datum/symbiote.dm index f6954fa2bf3..bf2f17e93c4 100644 --- a/mods/mobs/borers/datum/symbiote.dm +++ b/mods/mobs/borers/datum/symbiote.dm @@ -21,7 +21,8 @@ var/global/list/symbiote_starting_points = list() minimal_player_age = 14 economic_power = 0 defer_roundstart_spawn = TRUE - hud_icon = "hudblank" + hud_icon_state = "hudblank" + hud_icon = null outfit_type = /decl/outfit/job/symbiote_host create_record = FALSE var/check_whitelist // = "Symbiote" diff --git a/mods/mobs/borers/icons/hud.dmi b/mods/mobs/borers/icons/hud.dmi new file mode 100644 index 00000000000..8e01986fd47 Binary files /dev/null and b/mods/mobs/borers/icons/hud.dmi differ diff --git a/mods/mobs/borers/mob/borer/borer.dm b/mods/mobs/borers/mob/borer/borer.dm index 83dc85cd003..f9dbbe2f6e1 100644 --- a/mods/mobs/borers/mob/borer/borer.dm +++ b/mods/mobs/borers/mob/borer/borer.dm @@ -18,6 +18,22 @@ bleed_colour = "#816e12" ai = /datum/mob_controller/borer + // Defined here to remove relaymove handlers as being + // directly in mob contents breaks relaymove spectacularly. + movement_handlers = list( + /datum/movement_handler/mob/death, + /datum/movement_handler/mob/borer_in_host, + /datum/movement_handler/mob/conscious, + /datum/movement_handler/mob/eye, + /datum/movement_handler/mob/delay, + /datum/movement_handler/mob/stop_effect, + /datum/movement_handler/mob/physically_capable, + /datum/movement_handler/mob/physically_restrained, + /datum/movement_handler/mob/space, + /datum/movement_handler/mob/multiz, + /datum/movement_handler/mob/movement + ) + var/static/list/chemical_types = list( "anti-trauma" = /decl/material/liquid/brute_meds, "amphetamines" = /decl/material/liquid/amphetamines, @@ -40,6 +56,9 @@ var/mob/living/human/host // Human host for the brain worm. var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. +/datum/movement_handler/mob/borer_in_host/MayMove(mob/mover, is_external) + return ismob(mob.loc) ? MOVEMENT_STOP : MOVEMENT_PROCEED + /datum/mob_controller/borer emote_hear = list("chirrups") do_wander = FALSE diff --git a/mods/mobs/borers/mob/borer/borer_attacks.dm b/mods/mobs/borers/mob/borer/borer_attacks.dm index 5463f67d718..6c5919ed2b5 100644 --- a/mods/mobs/borers/mob/borer/borer_attacks.dm +++ b/mods/mobs/borers/mob/borer/borer_attacks.dm @@ -1,50 +1,44 @@ /mob/living/simple_animal/borer/UnarmedAttack(atom/A, proximity) - . = ..() - if(.) - return + if(host) + return TRUE // We cannot click things outside of our host. - if(!isliving(A) || !check_intent(I_FLAG_GRAB)) - return FALSE + if(!isliving(A) || !check_intent(I_FLAG_GRAB) || stat || !proximity) + return ..() - if(host || !can_use_borer_ability(requires_host_value = FALSE, check_last_special = FALSE)) - return FALSE + if(!can_use_borer_ability(requires_host_value = FALSE, check_last_special = FALSE)) + return TRUE - var/mob/living/M = A - if(M.has_brain_worms()) + var/mob/living/victim = A + if(victim.has_brain_worms()) to_chat(src, SPAN_WARNING("You cannot take a host who already has a passenger!")) return TRUE - - //TODO generalize borers to enter any mob. Until then, return early. - if(!ishuman(M)) - to_chat(src, SPAN_WARNING("This creature is not sufficiently intelligent to host you.")) + var/obj/item/organ/external/limb = GET_EXTERNAL_ORGAN(victim, BP_HEAD) + if(!limb) + to_chat(src, SPAN_WARNING("\The [victim] does not have anatomy compatible with your lifecycle!")) return TRUE - // end TODO - - var/mob/living/human/H = M - var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(H, BP_HEAD) - if(!E) - to_chat(src, SPAN_WARNING("\The [H] does not have a head!")) + if(BP_IS_PROSTHETIC(limb)) + to_chat(src, SPAN_WARNING("\The [victim]'s head is prosthetic and cannot support your lifecycle!")) return TRUE - if(!H.should_have_organ(BP_BRAIN)) - to_chat(src, SPAN_WARNING("\The [H] does not seem to have a brain cavity to enter.")) + if(!victim.should_have_organ(BP_BRAIN)) + to_chat(src, SPAN_WARNING("\The [victim] does not seem to have a brain cavity to enter.")) return TRUE - if(H.check_head_coverage()) + if(victim.check_head_coverage()) to_chat(src, SPAN_WARNING("You cannot get through that host's protective gear.")) return TRUE - to_chat(M, SPAN_WARNING("Something slimy begins probing at the opening of your ear canal...")) - to_chat(src, SPAN_NOTICE("You slither up [M] and begin probing at their ear canal...")) + to_chat(victim, SPAN_WARNING("Something slimy begins probing at the opening of your ear canal...")) + to_chat(src, SPAN_NOTICE("You slither up [victim] and begin probing at their ear canal...")) set_ability_cooldown(5 SECONDS) - if(!do_after(src, 3 SECONDS, M)) + if(!do_after(src, 3 SECONDS, victim) || host || GET_EXTERNAL_ORGAN(victim, BP_HEAD) != limb || BP_IS_PROSTHETIC(limb) || victim.check_head_coverage()) return TRUE - to_chat(src, SPAN_NOTICE("You wiggle into \the [M]'s ear.")) - if(M.stat == CONSCIOUS) - to_chat(M, SPAN_DANGER("Something wet, cold and slimy wiggles into your ear!")) + to_chat(src, SPAN_NOTICE("You wiggle into \the [victim]'s ear.")) + if(victim.stat == CONSCIOUS) + to_chat(victim, SPAN_DANGER("Something wet, cold and slimy wiggles into your ear!")) - host = M + host = victim host.status_flags |= PASSEMOTES forceMove(host) @@ -60,9 +54,9 @@ borers.add_antagonist_mind(host.mind, 1, borers.faction_name, borers.faction_welcome) if(ishuman(host)) - var/obj/item/organ/internal/I = GET_INTERNAL_ORGAN(H, BP_BRAIN) + var/obj/item/organ/internal/I = GET_INTERNAL_ORGAN(victim, BP_BRAIN) if(!I) // No brain organ, so the borer moves in and replaces it permanently. replace_brain() - else if(E) // If they're in normally, implant removal can get them out. - LAZYDISTINCTADD(E.implants, src) + else if(limb) // If they're in normally, implant removal can get them out. + LAZYDISTINCTADD(limb.implants, src) return TRUE diff --git a/mods/mobs/borers/mob/borer/borer_hud.dm b/mods/mobs/borers/mob/borer/borer_hud.dm index 8ec7b333971..79235e827d5 100644 --- a/mods/mobs/borers/mob/borer/borer_hud.dm +++ b/mods/mobs/borers/mob/borer/borer_hud.dm @@ -50,6 +50,7 @@ icon = 'mods/mobs/borers/icons/borer_ui.dmi' alpha = 0 invisibility = INVISIBILITY_MAXIMUM + requires_ui_style = FALSE /obj/screen/borer/handle_click(mob/user, params) if(!isborer(user)) diff --git a/mods/mobs/borers/mob/organ.dm b/mods/mobs/borers/mob/organ.dm index c1f686ade98..80c12e77c94 100644 --- a/mods/mobs/borers/mob/organ.dm +++ b/mods/mobs/borers/mob/organ.dm @@ -38,5 +38,4 @@ B.leave_host() B.ckey = last_owner.ckey - spawn(0) - qdel(src) + qdel(src) diff --git a/mods/mobs/borers/mob/overrides.dm b/mods/mobs/borers/mob/overrides.dm index 77c9a2c158e..26b83686f8e 100644 --- a/mods/mobs/borers/mob/overrides.dm +++ b/mods/mobs/borers/mob/overrides.dm @@ -22,8 +22,10 @@ var/mob/living/simple_animal/borer/B = HAS_BRAIN_WORMS(src) if(B.controlling) var/image/holder = hud_list[STATUS_HUD] + holder.icon = 'mods/mobs/borers/icons/hud.dmi' holder.icon_state = "hudbrainworm" var/image/holder2 = hud_list[STATUS_HUD_OOC] + holder2.icon = 'mods/mobs/borers/icons/hud.dmi' holder2.icon_state = "hudbrainworm" /mob/living/human/say_understands(mob/speaker, decl/language/speaking) diff --git a/mods/mobs/dionaea/mob/nymph_ui.dm b/mods/mobs/dionaea/mob/nymph_ui.dm index cdd3a3f8c6d..fc44f7639a3 100644 --- a/mods/mobs/dionaea/mob/nymph_ui.dm +++ b/mods/mobs/dionaea/mob/nymph_ui.dm @@ -23,6 +23,9 @@ UI_ICON_INVENTORY = 'mods/mobs/dionaea/icons/ui_inventory.dmi' ) +/datum/hud/diona_nymph +// action_intent_type = /obj/screen/intent/diona_nymph + /datum/hud/diona_nymph/get_ui_style_data() return GET_DECL(/decl/ui_style/diona) @@ -37,10 +40,9 @@ var/ui_color = get_ui_color() var/ui_alpha = get_ui_alpha() - action_intent = new /obj/screen/intent( null, mymob) // Swap to /obj/screen/intent/binary/diona when interaction code supports it. - mymob.healths = new /obj/screen/diona_health( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) - src.other = list() - src.adding = list(mymob.healths, action_intent) + mymob.healths = new /obj/screen/diona_health(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) + other = list() + adding = list(mymob.healths) ..() /obj/screen/diona_health diff --git a/mods/species/ascent/datum/species.dm b/mods/species/ascent/datum/species.dm index 809da7c26fb..f6bc5dddb05 100644 --- a/mods/species/ascent/datum/species.dm +++ b/mods/species/ascent/datum/species.dm @@ -71,11 +71,6 @@ species_flags = SPECIES_FLAG_NO_SLIP | SPECIES_FLAG_NO_MINOR_CUT spawn_flags = SPECIES_IS_RESTRICTED - unarmed_attacks = list( - /decl/natural_attack/claws/strong/gloves, - /decl/natural_attack/bite/sharp - ) - force_background_info = list( /decl/background_category/heritage = /decl/background_detail/heritage/ascent, /decl/background_category/homeworld = /decl/background_detail/location/kharmaani, diff --git a/mods/species/ascent/datum/species_bodytypes.dm b/mods/species/ascent/datum/species_bodytypes.dm index 9136d47e269..87a8462b0f7 100644 --- a/mods/species/ascent/datum/species_bodytypes.dm +++ b/mods/species/ascent/datum/species_bodytypes.dm @@ -30,6 +30,7 @@ BP_SYSTEM_CONTROLLER = /obj/item/organ/internal/controller ) limb_mapping = list(BP_CHEST = list(BP_CHEST, BP_M_HAND)) + footprints_icon = 'icons/mob/footprints/footprints_snake.dmi' // big tail heat_discomfort_strings = list( "You feel brittle and overheated.", diff --git a/mods/species/ascent/effects/razorweb.dm b/mods/species/ascent/effects/razorweb.dm index c8b05c70deb..0647ffe0c91 100644 --- a/mods/species/ascent/effects/razorweb.dm +++ b/mods/species/ascent/effects/razorweb.dm @@ -81,7 +81,7 @@ /obj/effect/razorweb/attackby(var/obj/item/thing, var/mob/user) var/destroy_self - if(thing.get_attack_force(user)) + if(thing.expend_attack_force(user)) visible_message(SPAN_DANGER("\The [user] breaks \the [src] with \the [thing]!")) destroy_self = TRUE diff --git a/mods/species/ascent/mobs/bodyparts.dm b/mods/species/ascent/mobs/bodyparts.dm index a86a379a15c..dba73c0e121 100644 --- a/mods/species/ascent/mobs/bodyparts.dm +++ b/mods/species/ascent/mobs/bodyparts.dm @@ -5,7 +5,7 @@ var/list/existing_webs = list() var/max_webs = 4 var/web_weave_time = 20 SECONDS - var/cooldown + var/organ_cooldown /obj/item/organ/external/groin/insectoid/mantid/gyne max_webs = 8 @@ -20,12 +20,12 @@ /obj/item/organ/external/groin/insectoid/mantid/refresh_action_button() . = ..() if(.) - action.button_icon_state = "weave-web-[cooldown ? "off" : "on"]" + action.button_icon_state = "weave-web-[organ_cooldown ? "off" : "on"]" action.button?.update_icon() /obj/item/organ/external/groin/insectoid/mantid/attack_self(var/mob/user) . = ..() - if(. && !cooldown) + if(. && !organ_cooldown) if(!isturf(owner.loc)) to_chat(owner, SPAN_WARNING("You cannot use this ability in this location.")) @@ -41,7 +41,7 @@ playsound(user, 'mods/species/ascent/sounds/razorweb_hiss.ogg', 70) owner.visible_message(SPAN_WARNING("\The [owner] separates their jaws and begins to weave a web of crystalline filaments...")) - cooldown = TRUE + organ_cooldown = TRUE refresh_action_button() addtimer(CALLBACK(src, PROC_REF(reset_cooldown)), web_weave_time) if(do_after(owner, web_weave_time) && length(existing_webs) < max_webs) @@ -52,27 +52,27 @@ web.owner = owner /obj/item/organ/external/groin/insectoid/mantid/proc/reset_cooldown() - cooldown = FALSE + organ_cooldown = FALSE refresh_action_button() /obj/item/organ/external/head/insectoid/mantid name = "crested head" action_button_name = "Spit Razorweb" default_action_type = /datum/action/item_action/organ/ascent - var/cooldown_time = 2.5 MINUTES - var/cooldown + var/organ_cooldown_time = 2.5 MINUTES + var/organ_cooldown /obj/item/organ/external/head/insectoid/mantid/refresh_action_button() . = ..() if(.) - action.button_icon_state = "shot-web-[cooldown ? "off" : "on"]" + action.button_icon_state = "shot-web-[organ_cooldown ? "off" : "on"]" action.button?.update_icon() /obj/item/organ/external/head/insectoid/mantid/attack_self(var/mob/user) . = ..() if(.) - if(cooldown) + if(organ_cooldown) to_chat(owner, SPAN_WARNING("Your filament channel hasn't refilled yet!")) return @@ -81,12 +81,12 @@ playsound(user, 'mods/species/ascent/sounds/razorweb.ogg', 100) to_chat(owner, SPAN_WARNING("You spit up a wad of razorweb, ready to throw!")) owner.toggle_throw_mode(TRUE) - cooldown = TRUE + organ_cooldown = TRUE refresh_action_button() - addtimer(CALLBACK(src, PROC_REF(reset_cooldown)), cooldown_time) + addtimer(CALLBACK(src, PROC_REF(reset_cooldown)), organ_cooldown_time) else qdel(web) /obj/item/organ/external/head/insectoid/mantid/proc/reset_cooldown() - cooldown = FALSE + organ_cooldown = FALSE refresh_action_button() diff --git a/mods/species/ascent/mobs/nymph/nymph_inventory.dm b/mods/species/ascent/mobs/nymph/nymph_inventory.dm index 12396419807..e79adb9931d 100644 --- a/mods/species/ascent/mobs/nymph/nymph_inventory.dm +++ b/mods/species/ascent/mobs/nymph/nymph_inventory.dm @@ -1,13 +1,8 @@ -/mob/living/simple_animal/alien/kharmaan/proc/contains_crystals(var/obj/item/W) - for(var/mat in W.matter) - if(mat == /decl/material/solid/sand) - . += W.matter[mat] - else if(mat == /decl/material/solid/gemstone/crystal) - . += W.matter[mat] - else if(mat == /decl/material/solid/quartz) - . += W.matter[mat] - else if(mat == /decl/material/solid/glass) - . += W.matter[mat] +/mob/living/simple_animal/alien/kharmaan/proc/contains_crystals(var/obj/item/prop) + . += prop.matter[/decl/material/solid/sand] + . += prop.matter[/decl/material/solid/gemstone/crystal] + . += prop.matter[/decl/material/solid/quartz] + . += prop.matter[/decl/material/solid/glass] /datum/inventory_slot/gripper/mouth/nymph/ascent/equipped(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE, var/delete_old_item = TRUE) var/mob/living/simple_animal/alien/kharmaan/nimp = user diff --git a/mods/species/ascent/mobs/nymph/nymph_ui.dm b/mods/species/ascent/mobs/nymph/nymph_ui.dm index c100c95b4cd..e3b6d1ec70c 100644 --- a/mods/species/ascent/mobs/nymph/nymph_ui.dm +++ b/mods/species/ascent/mobs/nymph/nymph_ui.dm @@ -38,6 +38,9 @@ UI_ICON_INVENTORY = 'mods/species/ascent/icons/ui_inventory.dmi' ) +/datum/hud/ascent_nymph +// action_intent_type = /obj/screen/intent/ascent_nymph + /datum/hud/ascent_nymph/get_ui_style_data() return GET_DECL(/decl/ui_style/ascent) @@ -51,13 +54,12 @@ var/decl/ui_style/ui_style = get_ui_style_data() var/ui_color = get_ui_color() var/ui_alpha = get_ui_alpha() - molt = new( null, mymob, ui_style, ui_color, ui_alpha) - food = new /obj/screen/food( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_NUTRITION) - drink = new /obj/screen/drink( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HYDRATION) - action_intent = new /obj/screen/intent( null) // Swap to /obj/screen/intent/binary/ascent when interaction code supports it. - mymob.healths = new /obj/screen/ascent_nymph_health( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) - src.other = list() - src.adding = list(mymob.healths, molt, food, drink, action_intent) + molt = new( null, mymob, ui_style, ui_color, ui_alpha) + food = new /obj/screen/food( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_NUTRITION) + drink = new /obj/screen/drink( null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HYDRATION) + mymob.healths = new /obj/screen/ascent_nymph_health(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_HEALTH) + other = list() + adding = list(mymob.healths, molt, food, drink) ..() /obj/screen/ascent_nymph_health diff --git a/mods/species/bayliens/_bayliens.dm b/mods/species/bayliens/_bayliens.dm index 68d625b481f..80febefc65e 100644 --- a/mods/species/bayliens/_bayliens.dm +++ b/mods/species/bayliens/_bayliens.dm @@ -10,6 +10,14 @@ /decl/modpack/bayliens name = "Baystation 12 Aliens" + tabloid_headlines = list( + "SHOCKING FIGURES REVEAL MORE TEENS DIE TO UNATHI HONOUR DUELS THAN GUN VIOLENCE", + "LOCAL UNATHI SYMPATHIZER: 'I really think you should stop with these spacebaiting articles.'", + "DO UNATHI SYMPATHIZERS HATE THE HUMAN RACE?", + "TENTACLES OF TERROR: SKRELL BLACK OPS SEIGE NYX NAVAL DEPOT. SHOCKING PHOTOGRAPHS INSIDE!", + "LOCAL MAN HAS SEIZURE AFTER SAYING SKRELLIAN NAME; FORCED ASSIMILATION SOON?", + "TAJARANS: CUTE AND CUDDLY, OR INFILTRATING THE GOVERNMENT? FIND OUT MORE INSIDE" + ) /decl/modpack/bayliens/pre_initialize() ..() diff --git a/mods/species/bayliens/skrell/datum/species.dm b/mods/species/bayliens/skrell/datum/species.dm index 0d9acdcd880..c52a6dc6735 100644 --- a/mods/species/bayliens/skrell/datum/species.dm +++ b/mods/species/bayliens/skrell/datum/species.dm @@ -14,12 +14,6 @@ traits = list(/decl/trait/malus/intolerance/protein = TRAIT_LEVEL_MINOR) primitive_form = "Neaera" - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch, - /decl/natural_attack/bite - ) description = "The Skrell are a highly advanced race of amphibians hailing from the system known as Qerr'Vallis. Their society is regimented into \ five different castes which the Qerr'Katish, or Royal Caste, rules over. Skrell are strict herbivores who are unable to eat large quantities of \ @@ -117,27 +111,36 @@ var/obj/item/shoes = H.get_equipped_item(slot_shoes_str) if(!shoes) var/list/bloodDNA - var/list/blood_data = REAGENT_DATA(H.vessel, /decl/material/liquid/blood) + var/list/blood_data = REAGENT_DATA(H.vessel, blood_reagent) if(blood_data) bloodDNA = list(blood_data[DATA_BLOOD_DNA] = blood_data[DATA_BLOOD_TYPE]) else bloodDNA = list() - if(T.simulated) - T.AddTracks(/obj/effect/decal/cleanable/blood/tracks/footprints/skrellprints, bloodDNA, H.dir, 0, H.get_skin_colour() + "25") // Coming (8c is the alpha value) + T.AddTracks(/obj/effect/decal/cleanable/blood/tracks/footprints/skrellprints, bloodDNA, H.dir, 0, H.get_skin_colour() + "25") // Coming (25 is the alpha value) if(isturf(old_loc)) var/turf/old_turf = old_loc - if(old_turf.simulated) - old_turf.AddTracks(/obj/effect/decal/cleanable/blood/tracks/footprints/skrellprints, bloodDNA, 0, H.dir, H.get_skin_colour() + "25") // Going (8c is the alpha value) + old_turf.AddTracks(/obj/effect/decal/cleanable/blood/tracks/footprints/skrellprints, bloodDNA, 0, H.dir, H.get_skin_colour() + "25") // Going (25 is the alpha value) /decl/species/skrell/check_background() return TRUE +/decl/material/liquid/mucus/skrell + name = "slime" + uid = "chem_mucus_skrell" + lore_text = "A gooey semi-liquid secreted by skrellian skin." + +// Copied from blood. +// TODO: There's not currently a way to check this, which might be a little annoying for forensics. +// But this is just a stopgap to stop Skrell from literally leaking blood everywhere they go. +/decl/material/liquid/mucus/skrell/get_reagent_color(datum/reagents/holder) + var/list/goo_data = REAGENT_DATA(holder, type) + return goo_data?[DATA_BLOOD_COLOR] || ..() + /obj/effect/decal/cleanable/blood/tracks/footprints/skrellprints name = "wet footprints" desc = "They look like still wet tracks left by skrellian feet." + chemical = /decl/material/liquid/mucus/skrell -/obj/effect/decal/cleanable/blood/tracks/footprints/skrellprints/dry() - qdel(src) /obj/item/organ/internal/eyes/skrell name = "amphibian eyes" desc = "Large black orbs, belonging to some sort of giant frog by looks of it." diff --git a/mods/species/bayliens/tajaran/datum/species.dm b/mods/species/bayliens/tajaran/datum/species.dm index 231ed1684f0..19de51ffc36 100644 --- a/mods/species/bayliens/tajaran/datum/species.dm +++ b/mods/species/bayliens/tajaran/datum/species.dm @@ -45,13 +45,6 @@ thirst_factor = DEFAULT_THIRST_FACTOR * 1.2 gluttonous = GLUT_TINY - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch, - /decl/natural_attack/bite/sharp - ) - move_trail = /obj/effect/decal/cleanable/blood/tracks/paw available_background_info = list( diff --git a/mods/species/bayliens/tajaran/datum/species_bodytypes.dm b/mods/species/bayliens/tajaran/datum/species_bodytypes.dm index 225bf5bc86e..0088067d9b2 100644 --- a/mods/species/bayliens/tajaran/datum/species_bodytypes.dm +++ b/mods/species/bayliens/tajaran/datum/species_bodytypes.dm @@ -16,6 +16,7 @@ base_eye_color = "#00aa00" nail_noun = "claws" uid = "bodytype_feline" + footprints_icon = 'icons/mob/footprints/footprints_paw.dmi' age_descriptor = /datum/appearance_descriptor/age/tajaran @@ -27,7 +28,10 @@ eye_low_light_vision_adjustment_speed = 0.3 override_limb_types = list( - BP_TAIL = /obj/item/organ/external/tail/cat + BP_TAIL = /obj/item/organ/external/tail/cat, + BP_HEAD = /obj/item/organ/external/head/sharp_bite, + BP_L_HAND = /obj/item/organ/external/hand/clawed, + BP_R_HAND = /obj/item/organ/external/hand/right/clawed ) default_sprite_accessories = list( diff --git a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm index de0b3b76a70..e3be8485345 100644 --- a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm +++ b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm @@ -47,5 +47,6 @@ /obj/item/clothing/suit/space/void _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/nasa/suit.dmi' + /obj/item/clothing/suit/space/void/excavation _feline_onmob_icon = 'mods/species/bayliens/tajaran/icons/clothing/excavation/suit.dmi' diff --git a/mods/species/bayliens/tritonian/datum/species.dm b/mods/species/bayliens/tritonian/datum/species.dm index a6e415dbce0..84491dd46cf 100644 --- a/mods/species/bayliens/tritonian/datum/species.dm +++ b/mods/species/bayliens/tritonian/datum/species.dm @@ -13,10 +13,3 @@ body_temperature = 302 water_soothe_amount = 5 - - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch, - /decl/natural_attack/bite/sharp - ) diff --git a/mods/species/bayliens/tritonian/datum/species_bodytypes.dm b/mods/species/bayliens/tritonian/datum/species_bodytypes.dm index 0e5614f62d5..dadaac398a3 100644 --- a/mods/species/bayliens/tritonian/datum/species_bodytypes.dm +++ b/mods/species/bayliens/tritonian/datum/species_bodytypes.dm @@ -6,6 +6,9 @@ override_organ_types = list( BP_LUNGS = /obj/item/organ/internal/lungs/gills ) + override_limb_types = list( + BP_HEAD = /obj/item/organ/external/head/sharp_bite + ) /decl/bodytype/human/tritonian/masculine name = "masculine" diff --git a/mods/species/bayliens/unathi/datum/species.dm b/mods/species/bayliens/unathi/datum/species.dm index 43ab46e0049..ad4a508329b 100644 --- a/mods/species/bayliens/unathi/datum/species.dm +++ b/mods/species/bayliens/unathi/datum/species.dm @@ -24,13 +24,6 @@ /decl/bodytype/lizard, /decl/bodytype/lizard/masculine ) - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/tail, - /decl/natural_attack/claws, - /decl/natural_attack/punch, - /decl/natural_attack/bite/sharp - ) available_accessory_categories = list( SAC_HORNS, diff --git a/mods/species/bayliens/unathi/datum/species_bodytypes.dm b/mods/species/bayliens/unathi/datum/species_bodytypes.dm index 31e9a058438..226241bc9a1 100644 --- a/mods/species/bayliens/unathi/datum/species_bodytypes.dm +++ b/mods/species/bayliens/unathi/datum/species_bodytypes.dm @@ -18,6 +18,7 @@ eye_flash_mod = 1.2 nail_noun = "claws" uid = "bodytype_unathi_fem" + footprints_icon = 'mods/species/bayliens/unathi/icons/footprints.dmi' age_descriptor = /datum/appearance_descriptor/age/lizard @@ -33,19 +34,24 @@ ) override_organ_types = list( - BP_EYES = /obj/item/organ/internal/eyes/lizard, - BP_BRAIN = /obj/item/organ/internal/brain/lizard + BP_EYES = /obj/item/organ/internal/eyes/lizard, + BP_BRAIN = /obj/item/organ/internal/brain/lizard ) - override_limb_types = list(BP_TAIL = /obj/item/organ/external/tail/lizard) + override_limb_types = list( + BP_TAIL = /obj/item/organ/external/tail/lizard, + BP_HEAD = /obj/item/organ/external/head/strong_bite, + BP_L_HAND = /obj/item/organ/external/hand/clawed, + BP_R_HAND = /obj/item/organ/external/hand/right/clawed + ) - cold_level_1 = 280 //Default 260 - Lower is better - cold_level_2 = 220 //Default 200 - cold_level_3 = 130 //Default 120 + cold_level_1 = 280 //Default 260 - Lower is better + cold_level_2 = 220 //Default 200 + cold_level_3 = 130 //Default 120 - heat_level_1 = 420 //Default 360 - Higher is better - heat_level_2 = 480 //Default 400 - heat_level_3 = 1100 //Default 1000 + heat_level_1 = 420 //Default 360 - Higher is better + heat_level_2 = 480 //Default 400 + heat_level_3 = 1100 //Default 1000 heat_discomfort_level = 320 heat_discomfort_strings = list( @@ -80,3 +86,7 @@ /obj/item/organ/external/tail/lizard tail_icon = 'mods/species/bayliens/unathi/icons/tail.dmi' tail_animation_states = 9 + +/obj/item/organ/external/tail/lizard/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/tail) + return unarmed_attack diff --git a/mods/species/bayliens/unathi/icons/footprints.dmi b/mods/species/bayliens/unathi/icons/footprints.dmi new file mode 100644 index 00000000000..fe21767b108 Binary files /dev/null and b/mods/species/bayliens/unathi/icons/footprints.dmi differ diff --git a/mods/species/drakes/_drakes.dme b/mods/species/drakes/_drakes.dme index 1143d6a510e..025f1c54c51 100644 --- a/mods/species/drakes/_drakes.dme +++ b/mods/species/drakes/_drakes.dme @@ -1,10 +1,5 @@ #ifndef MODPACK_DRAKES #define MODPACK_DRAKES - -#ifdef MODPACK_FANTASY -#warn Fantasy modpack loaded before Drakes modpack, compatibility features will be missing. -#endif - // BEGIN_INCLUDE #include "_drakes.dm" #include "_overrides.dm" diff --git a/mods/species/drakes/_overrides.dm b/mods/species/drakes/_overrides.dm index a4c564def3c..a4246927c61 100644 --- a/mods/species/drakes/_overrides.dm +++ b/mods/species/drakes/_overrides.dm @@ -2,7 +2,7 @@ var/_drake_onmob_icon var/_drake_hatchling_onmob_icon -/obj/item/backpack/setup_sprite_sheets() +/obj/item/setup_sprite_sheets() . = ..() if(_drake_onmob_icon) LAZYSET(sprite_sheets, BODYTYPE_GRAFADREKA, _drake_onmob_icon) diff --git a/mods/species/drakes/drake_abilities.dm b/mods/species/drakes/drake_abilities.dm index 85b589c73f6..fb4d7b0fcab 100644 --- a/mods/species/drakes/drake_abilities.dm +++ b/mods/species/drakes/drake_abilities.dm @@ -6,9 +6,11 @@ spit_projectile_type = /obj/item/projectile/drake_spit/weak /datum/ability_handler/predator/grafadreka/can_do_ranged_invocation(mob/user, atom/target) - return istype(user) && user.check_intent(I_FLAG_HARM) && !user.incapacitated() && isatom(target) + return ..() || (istype(user) && user.check_intent(I_FLAG_HARM) && !user.incapacitated() && isatom(target)) /datum/ability_handler/predator/grafadreka/do_ranged_invocation(mob/user, atom/target) + if((. = ..())) + return if(world.time < next_spit) to_chat(user, SPAN_WARNING("You cannot spit again so soon!")) return TRUE diff --git a/mods/species/drakes/sifsap.dm b/mods/species/drakes/sifsap.dm index f0fd4f179ef..5334839e70f 100644 --- a/mods/species/drakes/sifsap.dm +++ b/mods/species/drakes/sifsap.dm @@ -44,9 +44,9 @@ M.add_chemical_effect(CE_PULSE, -1) return ..() -/decl/material/liquid/sifsap/affect_overdose(mob/living/M, total_dose) - if(M.has_trait(/decl/trait/sivian_biochemistry)) +/decl/material/liquid/sifsap/affect_overdose(mob/living/victim, total_dose) + if(victim.has_trait(/decl/trait/sivian_biochemistry)) return - M.apply_damage(1, IRRADIATE) - SET_STATUS_MAX(M, 5, STAT_DROWSY) + victim.apply_damage(1, IRRADIATE) + SET_STATUS_MAX(victim, 5, STAT_DROWSY) return ..() diff --git a/mods/species/drakes/species.dm b/mods/species/drakes/species.dm index 228b9b9f505..a3a14352074 100644 --- a/mods/species/drakes/species.dm +++ b/mods/species/drakes/species.dm @@ -20,10 +20,7 @@ /decl/pronouns/male, /decl/pronouns/female ) - unarmed_attacks = list( - /decl/natural_attack/bite/sharp/drake, - /decl/natural_attack/claws/strong/drake - ) + force_background_info = list( /decl/background_category/heritage = /decl/background_detail/heritage/grafadreka, /decl/background_category/homeworld = /decl/background_detail/location/grafadreka, @@ -37,16 +34,11 @@ traits = list( /decl/trait/sivian_biochemistry = TRAIT_LEVEL_EXISTS ) + move_trail = /obj/effect/decal/cleanable/blood/tracks/paw // Drakes must be whitelisted for jobs to be able to join as them, see maps.dm. job_blacklist_by_default = TRUE spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED - character_preview_screen_locs = list( - "1" = "character_preview_map:1,4:36", - "2" = "character_preview_map:1,3:31", - "4" = "character_preview_map:1,2:26", - "8" = "character_preview_map:1,1:21" - ) var/list/adult_pain_emotes_with_pain_level = list( list(/decl/emote/audible/drake_huff, /decl/emote/audible/drake_rattle) = 20 diff --git a/mods/species/drakes/species_bodytypes.dm b/mods/species/drakes/species_bodytypes.dm index dc5afea4ea4..aae52bd678a 100644 --- a/mods/species/drakes/species_bodytypes.dm +++ b/mods/species/drakes/species_bodytypes.dm @@ -38,7 +38,8 @@ override_limb_types = list( BP_TAIL = /obj/item/organ/external/tail/grafadreka, BP_L_HAND = /obj/item/organ/external/hand/quadruped/grafadreka, - BP_R_HAND = /obj/item/organ/external/hand/right/quadruped/grafadreka + BP_R_HAND = /obj/item/organ/external/hand/right/quadruped/grafadreka, + BP_HEAD = /obj/item/organ/external/head/gripper/grafadreka ) base_color = "#608894" base_eye_color = COLOR_SILVER @@ -46,6 +47,8 @@ antaghud_offset_x = 16 override_organ_types = list(BP_DRAKE_GIZZARD = /obj/item/organ/internal/drake_gizzard) uid = "bodytype_drake" + footprints_icon = 'icons/mob/footprints/footprints_paw.dmi' + additional_emotes = list( /decl/emote/audible/drake_warble, /decl/emote/audible/drake_purr, @@ -94,6 +97,13 @@ /decl/emote/visible/tfist ) + character_preview_screen_locs = list( + "1" = "character_preview_map:1,4:36", + "2" = "character_preview_map:1,3:31", + "4" = "character_preview_map:1,2:26", + "8" = "character_preview_map:1,1:21" + ) + available_mob_postures = list( /decl/posture/standing, /decl/posture/lying/drake, @@ -184,6 +194,7 @@ blood_overlays = 'mods/species/drakes/icons/hatchling_blood.dmi' eye_icon = 'mods/species/drakes/icons/hatchling_eyes.dmi' icon_template = 'icons/mob/human_races/species/template.dmi' + damage_overlays = 'icons/mob/human_races/species/default_damage_overlays.dmi' bodytype_category = BODYTYPE_GRAFADREKA_HATCHLING mob_size = MOB_SIZE_SMALL pixel_offset_x = 0 @@ -192,9 +203,14 @@ /datum/ability_handler/predator/grafadreka/hatchling ) z_flags = 0 + // TODO: weaker attack subtypes for the baby override_limb_types = list( - BP_TAIL = /obj/item/organ/external/tail/grafadreka/hatchling + BP_TAIL = /obj/item/organ/external/tail/grafadreka/hatchling, + BP_L_HAND = /obj/item/organ/external/hand/quadruped/grafadreka, + BP_R_HAND = /obj/item/organ/external/hand/right/quadruped/grafadreka, + BP_HEAD = /obj/item/organ/external/head/gripper/grafadreka ) + default_emotes = list( /decl/emote/audible/drake_hatchling_growl, /decl/emote/audible/drake_hatchling_whine, @@ -203,6 +219,7 @@ /decl/emote/audible/drake_sneeze ) age_descriptor = /datum/appearance_descriptor/age/grafadreka/hatchling + character_preview_screen_locs = null uid = "bodytype_drake_hatchling" /decl/bodytype/quadruped/grafadreka/hatchling/Initialize() @@ -296,6 +313,10 @@ _base_attack_force = 8 needs_attack_dexterity = DEXTERITY_NONE +/obj/item/organ/external/hand/quadruped/grafadreka/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/claws/strong/drake) + return unarmed_attack + /obj/item/organ/external/hand/quadruped/grafadreka/Initialize(mapload, material_key, datum/mob_snapshot/supplied_appearance, decl/bodytype/new_bodytype) . = ..() item_flags |= ITEM_FLAG_NO_BLUDGEON @@ -312,6 +333,10 @@ _base_attack_force = 8 needs_attack_dexterity = DEXTERITY_NONE +/obj/item/organ/external/hand/right/quadruped/grafadreka/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/claws/strong/drake) + return unarmed_attack + /obj/item/organ/external/hand/right/quadruped/grafadreka/Initialize(mapload, material_key, datum/mob_snapshot/supplied_appearance, decl/bodytype/new_bodytype) . = ..() item_flags |= ITEM_FLAG_NO_BLUDGEON @@ -323,3 +348,7 @@ /obj/item/organ/external/hand/right/quadruped/grafadreka/set_bodytype(decl/bodytype/new_bodytype, override_material, apply_to_internal_organs) override_material = /decl/material/solid/organic/bone . = ..() + +/obj/item/organ/external/head/gripper/grafadreka/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/bite/sharp/drake) + return unarmed_attack diff --git a/mods/species/neoavians/clothing.dm b/mods/species/neoavians/clothing.dm index 3172b72a2e5..f4cdcaaed1b 100644 --- a/mods/species/neoavians/clothing.dm +++ b/mods/species/neoavians/clothing.dm @@ -18,11 +18,12 @@ _avian_onmob_icon = null //Backpacks & tanks - /obj/item/backpack/satchel _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/satchel.dmi' //Radsuits (theyre essential?) +/obj/item/clothing/head/radiation + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/head/rad_helm.dmi' /obj/item/clothing/head/radiation _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/head/rad_helm.dmi' @@ -77,12 +78,16 @@ name = "stylish uniform" icon = 'mods/species/neoavians/icons/clothing/under/stylish_form.dmi' +/obj/item/clothing/shoes + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/feet/shoes.dmi' + /obj/item/clothing/shoes/avian name = "small shoes" icon = 'mods/species/neoavians/icons/clothing/feet/shoes.dmi' color = COLOR_GRAY bodytype_equip_flags = BODY_EQUIP_FLAG_AVIAN _avian_onmob_icon = null + icon = 'mods/species/neoavians/icons/clothing/feet/shoes.dmi' /obj/item/clothing/shoes/avian/footwraps name = "cloth footwraps" diff --git a/mods/species/neoavians/datum/species.dm b/mods/species/neoavians/datum/species.dm index a0bfea025ed..065b6c6d932 100644 --- a/mods/species/neoavians/datum/species.dm +++ b/mods/species/neoavians/datum/species.dm @@ -52,12 +52,6 @@ swap_flags = MONKEY|SIMPLE_ANIMAL push_flags = MONKEY|SIMPLE_ANIMAL - unarmed_attacks = list( - /decl/natural_attack/bite/sharp, - /decl/natural_attack/claws, - /decl/natural_attack/stomp/weak - ) - available_background_info = list( /decl/background_category/heritage = list( /decl/background_detail/heritage/neoavian, diff --git a/mods/species/neoavians/datum/species_bodytypes.dm b/mods/species/neoavians/datum/species_bodytypes.dm index 46abc0d51b6..dd1b48d63e3 100644 --- a/mods/species/neoavians/datum/species_bodytypes.dm +++ b/mods/species/neoavians/datum/species_bodytypes.dm @@ -39,6 +39,13 @@ base_eye_color = "#f5c842" mob_size = MOB_SIZE_SMALL nail_noun = "talons" + override_limb_types = list( + BP_L_FOOT = /obj/item/organ/external/foot/avian, + BP_R_FOOT = /obj/item/organ/external/foot/right/avian, + BP_L_HAND = /obj/item/organ/external/hand/clawed, + BP_R_HAND = /obj/item/organ/external/hand/right/clawed, + BP_HEAD = /obj/item/organ/external/head/sharp_bite + ) has_organ = list( BP_STOMACH = /obj/item/organ/internal/stomach, BP_HEART = /obj/item/organ/internal/heart, diff --git a/mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi b/mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi index fe7e1534a7e..645c2a006bf 100644 Binary files a/mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi and b/mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi differ diff --git a/mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/helmet.dmi b/mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/helmet.dmi new file mode 100644 index 00000000000..779b0d77fe4 Binary files /dev/null and b/mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/helmet.dmi differ diff --git a/mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/suit.dmi b/mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/suit.dmi new file mode 100644 index 00000000000..c84f8e938c6 Binary files /dev/null and b/mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/suit.dmi differ diff --git a/mods/species/neoavians/machinery/suit_cycler.dm b/mods/species/neoavians/machinery/suit_cycler.dm index 2fd6323e6ed..4397f39c94d 100644 --- a/mods/species/neoavians/machinery/suit_cycler.dm +++ b/mods/species/neoavians/machinery/suit_cycler.dm @@ -11,7 +11,6 @@ _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi' //excavation - /obj/item/clothing/suit/space/void/excavation _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/suit.dmi' @@ -19,7 +18,6 @@ _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi' //engineering - /obj/item/clothing/head/helmet/space/void/engineering _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/engineering/helmet.dmi' @@ -33,15 +31,19 @@ _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/atmos/suit.dmi' //medical - /obj/item/clothing/suit/space/void/medical _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/suit.dmi' /obj/item/clothing/head/helmet/space/void/medical _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi' -//security +/obj/item/clothing/suit/space/void/medical/alt + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/suit.dmi' +/obj/item/clothing/head/helmet/space/void/medical/alt + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/medical_alt/helmet.dmi' + +//security /obj/item/clothing/head/helmet/space/void/security _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/helmet.dmi' @@ -49,7 +51,6 @@ _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/suit.dmi' //salvage - /obj/item/clothing/head/helmet/space/void/engineering/salvage _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/spacesuit/void/salvage/helmet.dmi' diff --git a/mods/species/serpentid/datum/species.dm b/mods/species/serpentid/datum/species.dm index 388b267140f..8dc853194d4 100644 --- a/mods/species/serpentid/datum/species.dm +++ b/mods/species/serpentid/datum/species.dm @@ -60,8 +60,6 @@ swap_flags = ALLMOBS move_trail = /obj/effect/decal/cleanable/blood/tracks/snake - unarmed_attacks = list(/decl/natural_attack/forelimb_slash) - pain_emotes_with_pain_level = list( list(/decl/emote/audible/bug_hiss) = 40 ) @@ -101,12 +99,6 @@ return FALSE -/decl/species/serpentid/can_shred(var/mob/living/human/H, var/ignore_intent, var/ignore_antag) - if(!H.get_equipped_item(slot_handcuffed_str) || H.buckled) - return ..(H, ignore_intent, TRUE) - else - return 0 - /decl/species/serpentid/handle_movement_delay_special(var/mob/living/human/victim) var/tally = 0 victim.remove_cloaking_source(src) diff --git a/mods/species/serpentid/datum/species_bodytypes.dm b/mods/species/serpentid/datum/species_bodytypes.dm index 6c4e562b451..947019f4db7 100644 --- a/mods/species/serpentid/datum/species_bodytypes.dm +++ b/mods/species/serpentid/datum/species_bodytypes.dm @@ -34,15 +34,16 @@ BP_HEAD = list("path" = /obj/item/organ/external/head/insectoid/serpentid), BP_L_ARM = list("path" = /obj/item/organ/external/arm/insectoid), BP_L_HAND = list("path" = /obj/item/organ/external/hand/insectoid), - BP_L_HAND_UPPER = list("path" = /obj/item/organ/external/hand/insectoid/upper), + BP_L_HAND_UPPER = list("path" = /obj/item/organ/external/hand/insectoid/upper/serpentid), BP_R_ARM = list("path" = /obj/item/organ/external/arm/right/insectoid), BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/insectoid), - BP_R_HAND_UPPER = list("path" = /obj/item/organ/external/hand/right/insectoid/upper), + BP_R_HAND_UPPER = list("path" = /obj/item/organ/external/hand/right/insectoid/upper/serpentid), BP_R_LEG = list("path" = /obj/item/organ/external/leg/right/insectoid/serpentid), BP_L_LEG = list("path" = /obj/item/organ/external/leg/insectoid/serpentid), BP_L_FOOT = list("path" = /obj/item/organ/external/foot/insectoid/serpentid), BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/insectoid/serpentid) ) + appearance_descriptors = list( /datum/appearance_descriptor/height = 1.75, /datum/appearance_descriptor/body_length = 1 @@ -63,6 +64,7 @@ ARMOR_BIO = ARMOR_BIO_SHIELDED, ARMOR_RAD = 0.5*ARMOR_RAD_MINOR ) + footprints_icon = 'icons/mob/footprints/footprints_snake.dmi' /decl/bodytype/serpentid/Initialize() _equip_adjust = list( @@ -121,3 +123,11 @@ name = "green" icon_base = 'mods/species/serpentid/icons/body_green.dmi' uid = "bodytype_serpentid_green" + +/obj/item/organ/external/hand/insectoid/upper/serpentid/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/forelimb_slash) + return unarmed_attack + +/obj/item/organ/external/hand/right/insectoid/upper/serpentid/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/forelimb_slash) + return unarmed_attack diff --git a/mods/species/skrell/datum/species.dm b/mods/species/skrell/datum/species.dm index 59a1be8470a..76c57542f62 100644 --- a/mods/species/skrell/datum/species.dm +++ b/mods/species/skrell/datum/species.dm @@ -12,12 +12,6 @@ ) primitive_form = "Neaera" - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch, - /decl/natural_attack/bite - ) description = "The skrell are a highly advanced species of amphibians hailing from \ the system known as Qerr'Vallis, which translates to 'Star of the royals' or 'Light of the Crown'. \ diff --git a/mods/species/tajaran/datum/species.dm b/mods/species/tajaran/datum/species.dm index bc39f5beedc..e6e5c8673a7 100644 --- a/mods/species/tajaran/datum/species.dm +++ b/mods/species/tajaran/datum/species.dm @@ -52,13 +52,6 @@ thirst_factor = DEFAULT_THIRST_FACTOR * 1.2 gluttonous = GLUT_TINY - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch, - /decl/natural_attack/bite/sharp - ) - move_trail = /obj/effect/decal/cleanable/blood/tracks/paw default_emotes = list( diff --git a/mods/species/tajaran/datum/species_bodytypes.dm b/mods/species/tajaran/datum/species_bodytypes.dm index 14134c1f697..a6be9223460 100644 --- a/mods/species/tajaran/datum/species_bodytypes.dm +++ b/mods/species/tajaran/datum/species_bodytypes.dm @@ -27,9 +27,11 @@ eye_low_light_vision_adjustment_speed = 0.3 override_limb_types = list( - BP_TAIL = /obj/item/organ/external/tail/cat + BP_TAIL = /obj/item/organ/external/tail/cat, + BP_HEAD = /obj/item/organ/external/head/sharp_bite, + BP_L_HAND = /obj/item/organ/external/hand/clawed, + BP_R_HAND = /obj/item/organ/external/hand/right/clawed ) - default_sprite_accessories = list( SAC_HAIR = list(/decl/sprite_accessory/hair/taj/lynx = list(SAM_COLOR = "#46321c")), SAC_MARKINGS = list(/decl/sprite_accessory/marking/tajaran/ears = list(SAM_COLOR = "#ae7d32")) diff --git a/mods/species/teshari/datum/species.dm b/mods/species/teshari/datum/species.dm index 961dcca689c..fee57b3d085 100644 --- a/mods/species/teshari/datum/species.dm +++ b/mods/species/teshari/datum/species.dm @@ -53,12 +53,6 @@ swap_flags = MONKEY|SIMPLE_ANIMAL push_flags = MONKEY|SIMPLE_ANIMAL - unarmed_attacks = list( - /decl/natural_attack/bite/sharp, - /decl/natural_attack/claws, - /decl/natural_attack/stomp/weak - ) - blood_types = list( /decl/blood_type/avian/taplus, /decl/blood_type/avian/taminus, diff --git a/mods/species/teshari/datum/species_bodytypes.dm b/mods/species/teshari/datum/species_bodytypes.dm index 5e2c51f6984..8ce0a9ee7e4 100644 --- a/mods/species/teshari/datum/species_bodytypes.dm +++ b/mods/species/teshari/datum/species_bodytypes.dm @@ -12,6 +12,13 @@ base_eye_color = "#f5c842" mob_size = MOB_SIZE_SMALL nail_noun = "talons" + override_limb_types = list( + BP_L_FOOT = /obj/item/organ/external/foot/avian, + BP_R_FOOT = /obj/item/organ/external/foot/right/avian, + BP_L_HAND = /obj/item/organ/external/hand/clawed, + BP_R_HAND = /obj/item/organ/external/hand/right/clawed, + BP_HEAD = /obj/item/organ/external/head/sharp_bite + ) has_organ = list( BP_STOMACH = /obj/item/organ/internal/stomach, BP_HEART = /obj/item/organ/internal/heart, @@ -21,7 +28,6 @@ BP_BRAIN = /obj/item/organ/internal/brain, BP_EYES = /obj/item/organ/internal/eyes ) - override_limb_types = list(BP_TAIL = /obj/item/organ/external/tail/avian) default_sprite_accessories = list( SAC_HAIR = list(/decl/sprite_accessory/hair/avian = list(SAM_COLOR = "#252525")), SAC_MARKINGS = list(/decl/sprite_accessory/marking/avian = list(SAM_COLOR = "#454545")) diff --git a/mods/species/unathi/datum/species.dm b/mods/species/unathi/datum/species.dm index 5eed1b3b6b5..9a63a510ab5 100644 --- a/mods/species/unathi/datum/species.dm +++ b/mods/species/unathi/datum/species.dm @@ -24,13 +24,6 @@ /decl/bodytype/unathi, /decl/bodytype/unathi/masculine ) - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/tail, - /decl/natural_attack/claws, - /decl/natural_attack/punch, - /decl/natural_attack/bite/sharp - ) available_accessory_categories = list( SAC_HORNS, diff --git a/mods/species/unathi/datum/species_bodytypes.dm b/mods/species/unathi/datum/species_bodytypes.dm index e3fe7d1cbe1..8f1278f846c 100644 --- a/mods/species/unathi/datum/species_bodytypes.dm +++ b/mods/species/unathi/datum/species_bodytypes.dm @@ -19,6 +19,13 @@ nail_noun = "claws" uid = "bodytype_unathi_fem" + override_limb_types = list( + BP_TAIL = /obj/item/organ/external/tail/unathi, + BP_HEAD = /obj/item/organ/external/head/strong_bite, + BP_L_HAND = /obj/item/organ/external/hand/clawed, + BP_R_HAND = /obj/item/organ/external/hand/right/clawed + ) + age_descriptor = /datum/appearance_descriptor/age/unathi default_sprite_accessories = list( @@ -80,3 +87,7 @@ /obj/item/organ/external/tail/unathi tail_icon = 'mods/species/unathi/icons/tail.dmi' tail_animation_states = 9 + +/obj/item/organ/external/tail/unathi/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/tail) + return unarmed_attack diff --git a/mods/species/utility_frames/species.dm b/mods/species/utility_frames/species.dm index 77a9f1f7ebc..0e9ce9013bc 100644 --- a/mods/species/utility_frames/species.dm +++ b/mods/species/utility_frames/species.dm @@ -32,11 +32,6 @@ preview_outfit = null - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/punch - ) available_pronouns = list( /decl/pronouns, /decl/pronouns/neuter diff --git a/mods/species/vox/_vox.dme b/mods/species/vox/_vox.dme index 83d61f50f51..0e0e635d99b 100644 --- a/mods/species/vox/_vox.dme +++ b/mods/species/vox/_vox.dme @@ -8,7 +8,6 @@ #include "datum\cultures_vox.dm" #include "datum\descriptors_vox.dm" #include "datum\factions_vox.dm" -#include "datum\heist_compatibility.dm" #include "datum\language.dm" #include "datum\locations_vox.dm" #include "datum\outfits.dm" diff --git a/mods/species/vox/datum/species.dm b/mods/species/vox/datum/species.dm index 02f7fed9313..eac1ad15786 100644 --- a/mods/species/vox/datum/species.dm +++ b/mods/species/vox/datum/species.dm @@ -32,14 +32,6 @@ /mob/living/human/proc/toggle_vox_pressure_seal ) - unarmed_attacks = list( - /decl/natural_attack/stomp, - /decl/natural_attack/kick, - /decl/natural_attack/claws/strong/gloves, - /decl/natural_attack/punch, - /decl/natural_attack/bite/strong - ) - rarity_value = 4 description = {"The Vox are the broken remnants of a once-proud race, now reduced to little more diff --git a/mods/species/vox/datum/species_bodytypes.dm b/mods/species/vox/datum/species_bodytypes.dm index cff74b6141d..092cfeb50b9 100644 --- a/mods/species/vox/datum/species_bodytypes.dm +++ b/mods/species/vox/datum/species_bodytypes.dm @@ -30,9 +30,13 @@ BP_BRAIN ) override_limb_types = list( - BP_GROIN = /obj/item/organ/external/groin/vox, - BP_TAIL = /obj/item/organ/external/tail/vox + BP_GROIN = /obj/item/organ/external/groin/vox, + BP_TAIL = /obj/item/organ/external/tail/vox, + BP_L_HAND = /obj/item/organ/external/hand/vox, + BP_R_HAND = /obj/item/organ/external/hand/right/vox, + BP_HEAD = /obj/item/organ/external/head/strong_bite ) + has_organ = list( BP_STOMACH = /obj/item/organ/internal/stomach/vox, BP_HEART = /obj/item/organ/internal/heart/vox, @@ -172,3 +176,11 @@ /obj/item/organ/external/tail/vox/stanchion tail_icon = 'mods/species/vox/icons/body/stanchion/body.dmi' + +/obj/item/organ/external/hand/vox/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/claws/strong/gloves) + return unarmed_attack + +/obj/item/organ/external/hand/right/vox/get_natural_attacks() + var/static/unarmed_attack = GET_DECL(/decl/natural_attack/claws/strong/gloves) + return unarmed_attack diff --git a/mods/species/vox/datum/unit_testing.dm b/mods/species/vox/datum/unit_testing.dm index 1026f897cad..1010d534842 100644 --- a/mods/species/vox/datum/unit_testing.dm +++ b/mods/species/vox/datum/unit_testing.dm @@ -4,7 +4,7 @@ /datum/unit_test/mob_damage/vox name = "MOB: Vox damage check template" - template = /datum/unit_test/mob_damage/vox + abstract_type = /datum/unit_test/mob_damage/vox mob_type = /mob/living/human/vox /datum/unit_test/mob_damage/vox/brute diff --git a/mods/~compatibility/patches/fantasy.dm b/mods/~compatibility/patches/fantasy.dm new file mode 100644 index 00000000000..4b4d6f7feab --- /dev/null +++ b/mods/~compatibility/patches/fantasy.dm @@ -0,0 +1,9 @@ +// Override drake lore and names for the fantasy modpack. +#ifdef MODPACK_DRAKES +#include "fantasy/drake_fantasy.dm" +#endif + +// Make whetstones available for the fantasy modpack/ +#ifdef MODPACK_ITEM_SHARPENING +#include "fantasy/whetstone_fantasy.dm" +#endif diff --git a/mods/~compatibility/patches/fantasy/drake_fantasy.dm b/mods/~compatibility/patches/fantasy/drake_fantasy.dm new file mode 100644 index 00000000000..70ad5e628e7 --- /dev/null +++ b/mods/~compatibility/patches/fantasy/drake_fantasy.dm @@ -0,0 +1,21 @@ +// Rename grafadreka +/decl/species/grafadreka + name = "Meredrake" + name_plural = "Meredrakes" + description = "Meredrakes, sometimes called mire-drakes, are large reptillian pack predators, widely assumed to be cousins to true dragons. \ + They are commonly found living in caves or burrows bordering grassland or forest, and while they prefer to hunt deer or rabbits, they will sometimes attack travellers if pickings are slim enough. \ + While they are not domesticated, they can be habituated and trained as working animals if captured young enough." + +/decl/sprite_accessory/marking/grafadreka + species_allowed = list("Meredrake") + +/decl/language/grafadreka + desc = "Hiss hiss, feed me rabbits." + +/decl/material/liquid/sifsap + name = "drake spittle" + lore_text = "A complex chemical slurry brewed up in the gullet of meredrakes." + +/obj/aura/sifsap_salve + name = "Drakespittle Salve" + descriptor = "glowing spittle" diff --git a/mods/~compatibility/patches/fantasy/whetstone_fantasy.dm b/mods/~compatibility/patches/fantasy/whetstone_fantasy.dm new file mode 100644 index 00000000000..b2821d26fa6 --- /dev/null +++ b/mods/~compatibility/patches/fantasy/whetstone_fantasy.dm @@ -0,0 +1,7 @@ +// Make whetstones available in character generation. +/decl/loadout_option/fantasy/utility/whetstone + name = "whetstone" + path = /obj/item/whetstone + available_materials = null + loadout_flags = null + uid = "gear_fantasy_whetstone" diff --git a/mods/species/vox/datum/heist_compatibility.dm b/mods/~compatibility/patches/heist_vox.dm similarity index 98% rename from mods/species/vox/datum/heist_compatibility.dm rename to mods/~compatibility/patches/heist_vox.dm index 776057948c0..e37a3416faa 100644 --- a/mods/species/vox/datum/heist_compatibility.dm +++ b/mods/~compatibility/patches/heist_vox.dm @@ -1,4 +1,3 @@ -#ifdef GAMEMODE_PACK_HEIST /decl/special_role/raider/Initialize() . = ..() LAZYSET(outfits_per_species, SPECIES_VOX, /decl/outfit/vox_raider) @@ -41,4 +40,3 @@ vox.SetName(vox.real_name) var/decl/special_role/raider/raiders = GET_DECL(/decl/special_role/raider) raiders.update_access(vox) -#endif \ No newline at end of file diff --git a/mods/~compatibility/patches/mixed_gamemodes.dm b/mods/~compatibility/patches/mixed_gamemodes.dm new file mode 100644 index 00000000000..c554702eff5 --- /dev/null +++ b/mods/~compatibility/patches/mixed_gamemodes.dm @@ -0,0 +1,12 @@ +// TODO: #ifdef GAMEMODE_PACK_MERCENARY +#if defined(GAMEMODE_PACK_HEIST) +#include "mixed_gamemodes/crossfire.dm" +#endif +#if defined(GAMEMODE_PACK_REVOLUTIONARY) +#include "mixed_gamemodes/siege.dm" +#endif +// #endif + +#if defined(GAMEMODE_PACK_REVOLUTIONARY) && defined(GAMEMODE_PACK_CULT) +#include "mixed_gamemodes/uprising.dm" +#endif \ No newline at end of file diff --git a/mods/gamemodes/mixed/crossfire.dm b/mods/~compatibility/patches/mixed_gamemodes/crossfire.dm similarity index 100% rename from mods/gamemodes/mixed/crossfire.dm rename to mods/~compatibility/patches/mixed_gamemodes/crossfire.dm diff --git a/mods/gamemodes/mixed/siege.dm b/mods/~compatibility/patches/mixed_gamemodes/siege.dm similarity index 100% rename from mods/gamemodes/mixed/siege.dm rename to mods/~compatibility/patches/mixed_gamemodes/siege.dm diff --git a/mods/gamemodes/mixed/uprising.dm b/mods/~compatibility/patches/mixed_gamemodes/uprising.dm similarity index 100% rename from mods/gamemodes/mixed/uprising.dm rename to mods/~compatibility/patches/mixed_gamemodes/uprising.dm diff --git a/mods/~compatibility/patches/psionics.dm b/mods/~compatibility/patches/psionics.dm new file mode 100644 index 00000000000..93473b9271f --- /dev/null +++ b/mods/~compatibility/patches/psionics.dm @@ -0,0 +1,13 @@ +// Give borers a paramount rank psi aura, and gives them a ranged psychic attack. +#ifdef CONTENT_PACK_BORERS +#include "psionics/borer_psi.dm" +#endif +// Allows psion blood to be used to create soulstones, +// and lets full soulstones nullify psi and shatter into nullglass. +#ifdef GAMEMODE_PACK_CULT +#include "psionics/cult_psi.dm" +#endif +// Adds psi abilities to the counselor. +#ifdef MODPACK_STANDARD_JOBS +#include "psionics/psi_jobs.dm" +#endif \ No newline at end of file diff --git a/mods/content/psionics/system/psionics/mob/borer_power.dm b/mods/~compatibility/patches/psionics/borer_psi.dm similarity index 97% rename from mods/content/psionics/system/psionics/mob/borer_power.dm rename to mods/~compatibility/patches/psionics/borer_psi.dm index 48f62c8adab..68c70ec95f7 100644 --- a/mods/content/psionics/system/psionics/mob/borer_power.dm +++ b/mods/~compatibility/patches/psionics/borer_psi.dm @@ -1,4 +1,3 @@ -#ifdef CONTENT_PACK_BORERS /mob/living/simple_animal/borer var/image/aura_image @@ -52,5 +51,4 @@ SET_STATUS_MAX(M, STAT_WEAK, 10) set_ability_cooldown(15 SECONDS) - return TRUE -#endif \ No newline at end of file + return TRUE \ No newline at end of file diff --git a/mods/~compatibility/patches/psionics/cult_psi.dm b/mods/~compatibility/patches/psionics/cult_psi.dm new file mode 100644 index 00000000000..0fa46de32d6 --- /dev/null +++ b/mods/~compatibility/patches/psionics/cult_psi.dm @@ -0,0 +1,18 @@ +// Make psion blood usable for soulstone synthesis. +/decl/chemical_reaction/synthesis/soulstone/donor_is_magic(mob/living/donor) + return ..() || !!donor?.get_ability_handler(/datum/ability_handler/psionics) + +// Make soulstones interact with psionics. +/obj/item/soulstone/disrupts_psionics() + . = !full ? src : FALSE + +/obj/item/soulstone/shatter() + for(var/i=1 to rand(2,5)) + new /obj/item/shard(get_turf(src), full ? /decl/material/nullglass : /decl/material/solid/gemstone/crystal) + . = ..() + +/obj/item/soulstone/withstand_psi_stress(var/stress, var/atom/source) + . = ..(stress, source) + if(. > 0) + . = max(0, . - rand(2,5)) + shatter() \ No newline at end of file diff --git a/mods/~compatibility/patches/psionics/psi_jobs.dm b/mods/~compatibility/patches/psionics/psi_jobs.dm new file mode 100644 index 00000000000..d62d097022f --- /dev/null +++ b/mods/~compatibility/patches/psionics/psi_jobs.dm @@ -0,0 +1,6 @@ +/datum/job/standard/counselor/equip_job(var/mob/living/human/H) + if(H.mind.role_alt_title == "Counselor") + psi_faculties = list("[PSI_REDACTION]" = PSI_RANK_OPERANT) + if(H.mind.role_alt_title == "Mentalist") + psi_faculties = list("[PSI_COERCION]" = PSI_RANK_OPERANT) + return ..() \ No newline at end of file diff --git a/mods/~compatibility/patches/supermatter.dm b/mods/~compatibility/patches/supermatter.dm new file mode 100644 index 00000000000..27f6a6f4475 --- /dev/null +++ b/mods/~compatibility/patches/supermatter.dm @@ -0,0 +1,12 @@ +// Add the supermatter monitor to engineering jobs' default software. +#ifdef MODPACK_STANDARD_JOBS +#include "supermatter/supermatter_jobs.dm" +#endif +// Disable Narsie during the supermatter cascade. +#ifdef GAMEMODE_PACK_CULT +#include "supermatter/sm_disable_narsie.dm" +#endif +// Add the supermatter meteor to the meteor gamemode's cataclysmic meteors list. +#ifdef GAMEMODE_PACK_METEOR +#include "supermatter/sm_meteor.dm" +#endif \ No newline at end of file diff --git a/mods/~compatibility/patches/supermatter/sm_disable_narsie.dm b/mods/~compatibility/patches/supermatter/sm_disable_narsie.dm new file mode 100644 index 00000000000..a660384347b --- /dev/null +++ b/mods/~compatibility/patches/supermatter/sm_disable_narsie.dm @@ -0,0 +1,4 @@ +// Disable Narsie when a supermatter cascade begins. +/datum/universal_state/supermatter_cascade/OnEnter() + var/decl/special_role/cultist/cult = GET_DECL(/decl/special_role/cultist) + cult.allow_narsie = 0 \ No newline at end of file diff --git a/mods/~compatibility/patches/supermatter/sm_meteor.dm b/mods/~compatibility/patches/supermatter/sm_meteor.dm new file mode 100644 index 00000000000..df383bc3f76 --- /dev/null +++ b/mods/~compatibility/patches/supermatter/sm_meteor.dm @@ -0,0 +1,3 @@ +/decl/game_mode/meteor/Initialize() + . = ..() // No debounce, decls only init once. + meteors_cataclysm[/obj/effect/meteor/destroyer/supermatter] = 1 \ No newline at end of file diff --git a/mods/~compatibility/patches/supermatter/supermatter_jobs.dm b/mods/~compatibility/patches/supermatter/supermatter_jobs.dm new file mode 100644 index 00000000000..2a62b17318c --- /dev/null +++ b/mods/~compatibility/patches/supermatter/supermatter_jobs.dm @@ -0,0 +1,7 @@ +/datum/job/standard/chief_engineer/New() + ..() + software_on_spawn |= /datum/computer_file/program/supermatter_monitor + +/datum/job/standard/engineer/New() + ..() + software_on_spawn |= /datum/computer_file/program/supermatter_monitor \ No newline at end of file diff --git a/mods/~compatibility/readme.md b/mods/~compatibility/readme.md new file mode 100644 index 00000000000..d422273e7bb --- /dev/null +++ b/mods/~compatibility/readme.md @@ -0,0 +1,22 @@ +# Modpack Compatibility System +This folder exists as a way to work around the fact that the previous system for modpack cross-compatibility, define-gating, is sensitive to include order. This resulted in a lot of boilerplate, like having to emit warnings if modpacks were included in the wrong order. This meant that you could also introduce cyclical dependencies, where no matter what it would emit a warning and content would be missing. + +To avoid this issue, we instead include all compatibility patches last, so it is load order agnostic. + +## FAQ +### Why aren't the compatibility files in the modpacks themselves? +I didn't want to edit the modpack include validation script to exclude the compatibility patches from all DMEs. + +### Why is it organised using subfolders? +I didn't like using `#if defined(FOO) && defined(BAR)` and nested `#ifdef`s were hard to follow, so instead I group them by modpack. + +### Is there a general rule for which modpacks get their own folder? +Not really. I just grouped them in roughly the way that would result in the largest existing groupings, and then chose groupings that would make the most sense to expand in the future (fantasy and standard jobs). + +### Do all patches need to be in a subfolder? +No, it's totally fine to just put something in the base patches directory if there's only one patch for either of the mods in that pairing. That said, sometimes it can make sense to add a folder with just one patch if you can foresee future development requiring additional patches in the same category. + +### How do I decide which folder a patch goes in if both modpacks have folders? +I tend to personally go based on whatever it's mostly about; a hypothetical patch renaming and respriting psionics for the fantasy modpack would go in the fantasy folder. Alternatively, you could think of it as going for whichever one is more specific. + +That said, if one has a lot more patches than the other, or if one modpack (take Standard Jobs, for example) is patched by several modpacks that already have folders, it's fine to just go with whatever produces the largest patch subfolders (or gets rid of small/redundant ones). \ No newline at end of file diff --git a/mods/~compatibility/~compatibility.dm b/mods/~compatibility/~compatibility.dm new file mode 100644 index 00000000000..6d3b770558f --- /dev/null +++ b/mods/~compatibility/~compatibility.dm @@ -0,0 +1,20 @@ +// Add Vox-specific content for the Heist gamemode (Vox raider outfit, mirror to transform into a Vox as a raider) +#if defined(GAMEMODE_PACK_HEIST) && defined(MODPACK_VOX) +#include "patches/heist_vox.dm" +#endif + +#ifdef MODPACK_PSIONICS +#include "patches/psionics.dm" +#endif + +#ifdef GAMEMODE_PACK_MIXED +#include "patches/mixed_gamemodes.dm" +#endif + +#ifdef MODPACK_FANTASY_SPECIES +#include "patches/fantasy.dm" +#endif + +#ifdef CONTENT_PACK_SUPERMATTER +#include "patches/supermatter.dm" +#endif diff --git a/nano/images/shaded_hills/shaded_hills-1.png b/nano/images/shaded_hills/shaded_hills-1.png new file mode 100644 index 00000000000..862e93356aa Binary files /dev/null and b/nano/images/shaded_hills/shaded_hills-1.png differ diff --git a/nano/images/shaded_hills/shaded_hills-2.png b/nano/images/shaded_hills/shaded_hills-2.png new file mode 100644 index 00000000000..cc7727eb7d2 Binary files /dev/null and b/nano/images/shaded_hills/shaded_hills-2.png differ diff --git a/nano/images/shaded_hills/shaded_hills-3.png b/nano/images/shaded_hills/shaded_hills-3.png new file mode 100644 index 00000000000..ab087ace58e Binary files /dev/null and b/nano/images/shaded_hills/shaded_hills-3.png differ diff --git a/nano/images/shaded_hills/shaded_hills-4.png b/nano/images/shaded_hills/shaded_hills-4.png new file mode 100644 index 00000000000..97408dc32f6 Binary files /dev/null and b/nano/images/shaded_hills/shaded_hills-4.png differ diff --git a/nano/images/shaded_hills/shaded_hills-5.png b/nano/images/shaded_hills/shaded_hills-5.png new file mode 100644 index 00000000000..a4d8cf90dd2 Binary files /dev/null and b/nano/images/shaded_hills/shaded_hills-5.png differ diff --git a/nano/images/shaded_hills/shaded_hills-6.png b/nano/images/shaded_hills/shaded_hills-6.png new file mode 100644 index 00000000000..3cb34e5cb67 Binary files /dev/null and b/nano/images/shaded_hills/shaded_hills-6.png differ diff --git a/nano/templates/radio_basic.tmpl b/nano/templates/radio_basic.tmpl index fc94e17029c..b3ba6f4830a 100644 --- a/nano/templates/radio_basic.tmpl +++ b/nano/templates/radio_basic.tmpl @@ -138,12 +138,11 @@ Used In File(s): /code/game/objects/item/devices/radio/radio.dm {{:value.display_name}}
    - {{if value.secure_channel}} - {{:helper.link('On', null, {'ch_name' : value.chan, 'listen' : 1}, value.listening ? 'selected' : null)}} - {{:helper.link('Off', null, {'ch_name' : value.chan, 'listen' : 0}, value.listening ? null : 'selected')}} - {{else}} - {{:helper.link('Switch', null, {'spec_freq' : value.chan}, data.rawfreq == value.chan ? 'selected' : null)}} - {{/if}} + {{:helper.link('On', null, {'ch_name' : value.chan, 'listen' : 1}, value.listening ? 'selected' : null)}} + {{:helper.link('Off', null, {'ch_name' : value.chan, 'listen' : 0}, value.listening ? null : 'selected')}} + {{if !value.secure_channel}} + {{:helper.link('Switch', null, {'spec_freq' : value.chan}, data.rawfreq == value.chan ? 'selected' : null)}} + {{/if}}
    {{/for}} {{/if}} diff --git a/nebula.dme b/nebula.dme index 02615909325..3bd032e7efd 100644 --- a/nebula.dme +++ b/nebula.dme @@ -10,6 +10,7 @@ #define DEBUG // END_PREFERENCES // BEGIN_INCLUDE +#include "code\___compile_options.dm" #include "code\___opendream_linting.dm" #include "code\__globals.dm" #include "code\_macros.dm" @@ -20,7 +21,6 @@ #include "code\__datastructures\stack.dm" #include "code\__defines\_byond_version_compat.dm" #include "code\__defines\_compile_helpers.dm" -#include "code\__defines\_compile_options.dm" #include "code\__defines\_planes+layers.dm" #include "code\__defines\_tick.dm" #include "code\__defines\admin.dm" @@ -60,6 +60,7 @@ #include "code\__defines\intent.dm" #include "code\__defines\interactions.dm" #include "code\__defines\inventory_sizes.dm" +#include "code\__defines\item_effects.dm" #include "code\__defines\items_clothing.dm" #include "code\__defines\jobs.dm" #include "code\__defines\languages.dm" @@ -192,7 +193,6 @@ #include "code\_onclick\hud\robot.dm" #include "code\_onclick\hud\skybox.dm" #include "code\_onclick\hud\screen\_screen.dm" -#include "code\_onclick\hud\screen\screen_abilities.dm" #include "code\_onclick\hud\screen\screen_action_button.dm" #include "code\_onclick\hud\screen\screen_ai_button.dm" #include "code\_onclick\hud\screen\screen_attack_selector.dm" @@ -211,6 +211,7 @@ #include "code\_onclick\hud\screen\screen_intent.dm" #include "code\_onclick\hud\screen\screen_internal.dm" #include "code\_onclick\hud\screen\screen_inventory.dm" +#include "code\_onclick\hud\screen\screen_inventory_hands.dm" #include "code\_onclick\hud\screen\screen_lighting.dm" #include "code\_onclick\hud\screen\screen_maneuver.dm" #include "code\_onclick\hud\screen\screen_module.dm" @@ -220,7 +221,6 @@ #include "code\_onclick\hud\screen\screen_radial.dm" #include "code\_onclick\hud\screen\screen_resist.dm" #include "code\_onclick\hud\screen\screen_robot_drop_grab.dm" -#include "code\_onclick\hud\screen\screen_robot_intent.dm" #include "code\_onclick\hud\screen\screen_robot_inventory.dm" #include "code\_onclick\hud\screen\screen_robot_modules.dm" #include "code\_onclick\hud\screen\screen_robot_panel.dm" @@ -282,8 +282,8 @@ #include "code\controllers\subsystems\mapping.dm" #include "code\controllers\subsystems\misc_late.dm" #include "code\controllers\subsystems\overlays.dm" +#include "code\controllers\subsystems\overmap.dm" #include "code\controllers\subsystems\pathfinding.dm" -#include "code\controllers\subsystems\plants.dm" #include "code\controllers\subsystems\radiation.dm" #include "code\controllers\subsystems\shuttle.dm" #include "code\controllers\subsystems\skybox.dm" @@ -327,7 +327,7 @@ #include "code\controllers\subsystems\processing\mobs.dm" #include "code\controllers\subsystems\processing\nano.dm" #include "code\controllers\subsystems\processing\obj.dm" -#include "code\controllers\subsystems\processing\overmap.dm" +#include "code\controllers\subsystems\processing\plants.dm" #include "code\controllers\subsystems\processing\processing.dm" #include "code\controllers\subsystems\processing\projectiles.dm" #include "code\controllers\subsystems\processing\temperature.dm" @@ -427,8 +427,13 @@ #include "code\datums\extensions\abilities\abilities.dm" #include "code\datums\extensions\abilities\abilities_mob.dm" #include "code\datums\extensions\abilities\abilities_predator.dm" +#include "code\datums\extensions\abilities\ability_button.dm" +#include "code\datums\extensions\abilities\ability_decl.dm" #include "code\datums\extensions\abilities\ability_handler.dm" #include "code\datums\extensions\abilities\ability_item.dm" +#include "code\datums\extensions\abilities\ability_projectile.dm" +#include "code\datums\extensions\abilities\ability_targeting.dm" +#include "code\datums\extensions\abilities\readme.dm" #include "code\datums\extensions\appearance\appearance.dm" #include "code\datums\extensions\appearance\base_icon_state.dm" #include "code\datums\extensions\appearance\cardborg.dm" @@ -740,6 +745,7 @@ #include "code\game\atoms_fires.dm" #include "code\game\atoms_fluids.dm" #include "code\game\atoms_init.dm" +#include "code\game\atoms_interactions.dm" #include "code\game\atoms_layering.dm" #include "code\game\atoms_movable.dm" #include "code\game\atoms_movable_grabs.dm" @@ -747,7 +753,6 @@ #include "code\game\atoms_movable_overlay.dm" #include "code\game\atoms_temperature.dm" #include "code\game\base_turf.dm" -#include "code\game\images.dm" #include "code\game\movietitles.dm" #include "code\game\response_team.dm" #include "code\game\sound.dm" @@ -782,9 +787,6 @@ #include "code\game\gamemodes\endgame\endgame.dm" #include "code\game\gamemodes\endgame\ftl_jump\ftl_jump.dm" #include "code\game\gamemodes\endgame\nuclear_explosion\nuclear_explosion.dm" -#include "code\game\gamemodes\endgame\supermatter_cascade\cascade_blob.dm" -#include "code\game\gamemodes\endgame\supermatter_cascade\portal.dm" -#include "code\game\gamemodes\endgame\supermatter_cascade\universe.dm" #include "code\game\gamemodes\events\power_failure.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\nuclear\nuclear.dm" @@ -1023,6 +1025,7 @@ #include "code\game\objects\effects\effect_system.dm" #include "code\game\objects\effects\explosion_particles.dm" #include "code\game\objects\effects\fake_fire.dm" +#include "code\game\objects\effects\footprints.dm" #include "code\game\objects\effects\force_portal.dm" #include "code\game\objects\effects\gateway.dm" #include "code\game\objects\effects\gibspawner.dm" @@ -1071,12 +1074,14 @@ #include "code\game\objects\items\_item_materials.dm" #include "code\game\objects\items\_item_melting.dm" #include "code\game\objects\items\_item_reagents.dm" +#include "code\game\objects\items\_item_sharpness.dm" #include "code\game\objects\items\apc_frame.dm" #include "code\game\objects\items\blackout.dm" #include "code\game\objects\items\blueprints.dm" #include "code\game\objects\items\bodybag.dm" #include "code\game\objects\items\buttons.dm" #include "code\game\objects\items\candelabra.dm" +#include "code\game\objects\items\chisel.dm" #include "code\game\objects\items\christmas.dm" #include "code\game\objects\items\contraband.dm" #include "code\game\objects\items\crutches.dm" @@ -1085,6 +1090,7 @@ #include "code\game\objects\items\fleece.dm" #include "code\game\objects\items\glassjar.dm" #include "code\game\objects\items\holosign_creator.dm" +#include "code\game\objects\items\horseshoe.dm" #include "code\game\objects\items\hourglass.dm" #include "code\game\objects\items\instruments.dm" #include "code\game\objects\items\latexballoon.dm" @@ -1106,6 +1112,8 @@ #include "code\game\objects\items\trash.dm" #include "code\game\objects\items\umbrella.dm" #include "code\game\objects\items\waterskin.dm" +#include "code\game\objects\items\artifice\chain.dm" +#include "code\game\objects\items\artifice\hook.dm" #include "code\game\objects\items\blades\_blade.dm" #include "code\game\objects\items\blades\axe.dm" #include "code\game\objects\items\blades\axe_fire.dm" @@ -1252,7 +1260,6 @@ #include "code\game\objects\items\weapons\RPD.dm" #include "code\game\objects\items\weapons\RSF.dm" #include "code\game\objects\items\weapons\secrets_disk.dm" -#include "code\game\objects\items\weapons\shields.dm" #include "code\game\objects\items\weapons\soap.dm" #include "code\game\objects\items\weapons\staff.dm" #include "code\game\objects\items\weapons\stunbaton.dm" @@ -1312,7 +1319,6 @@ #include "code\game\objects\items\weapons\grenades\prank_grenades.dm" #include "code\game\objects\items\weapons\grenades\smokebomb.dm" #include "code\game\objects\items\weapons\grenades\spawnergrenade.dm" -#include "code\game\objects\items\weapons\grenades\supermatter.dm" #include "code\game\objects\items\weapons\implants\implant.dm" #include "code\game\objects\items\weapons\implants\implantcase.dm" #include "code\game\objects\items\weapons\implants\implantchair.dm" @@ -1350,6 +1356,12 @@ #include "code\game\objects\items\weapons\melee\energy_projected.dm" #include "code\game\objects\items\weapons\melee\energy_sword.dm" #include "code\game\objects\items\weapons\melee\misc.dm" +#include "code\game\objects\items\weapons\shields\_shield.dm" +#include "code\game\objects\items\weapons\shields\shield_crafted.dm" +#include "code\game\objects\items\weapons\shields\shield_crafted_buckler.dm" +#include "code\game\objects\items\weapons\shields\shield_crafting.dm" +#include "code\game\objects\items\weapons\shields\shield_energy.dm" +#include "code\game\objects\items\weapons\shields\shield_riot.dm" #include "code\game\objects\items\weapons\storage\backpack.dm" #include "code\game\objects\items\weapons\storage\bags.dm" #include "code\game\objects\items\weapons\storage\basket.dm" @@ -1414,6 +1426,8 @@ #include "code\game\objects\structures\__structure.dm" #include "code\game\objects\structures\_structure_construction.dm" #include "code\game\objects\structures\_structure_icon.dm" +#include "code\game\objects\structures\_structure_interactions.dm" +#include "code\game\objects\structures\_structure_lock.dm" #include "code\game\objects\structures\_structure_materials.dm" #include "code\game\objects\structures\ai_decoy.dm" #include "code\game\objects\structures\armor_stand.dm" @@ -1478,8 +1492,8 @@ #include "code\game\objects\structures\showcase.dm" #include "code\game\objects\structures\signs.dm" #include "code\game\objects\structures\skele_stand.dm" +#include "code\game\objects\structures\snowman.dm" #include "code\game\objects\structures\stasis_cage.dm" -#include "code\game\objects\structures\structure_lock.dm" #include "code\game\objects\structures\tables.dm" #include "code\game\objects\structures\tank_dispenser.dm" #include "code\game\objects\structures\target_stake.dm" @@ -1639,6 +1653,7 @@ #include "code\game\verbs\who.dm" #include "code\modules\abstract\_abstract.dm" #include "code\modules\abstract\abstract_exterior_marker.dm" +#include "code\modules\abstract\abstract_fluid_direction.dm" #include "code\modules\abstract\abstract_ramp_sculptor.dm" #include "code\modules\abstract\airlock_helper.dm" #include "code\modules\acting\acting_items.dm" @@ -2211,6 +2226,7 @@ #include "code\modules\codex\entries\weapons.dm" #include "code\modules\crafting\handmade_fancy.dm" #include "code\modules\crafting\handmade_items.dm" +#include "code\modules\crafting\forging\bellows.dm" #include "code\modules\crafting\metalwork\metalwork_items.dm" #include "code\modules\crafting\pottery\pottery_moulds.dm" #include "code\modules\crafting\pottery\pottery_structures.dm" @@ -2731,6 +2747,7 @@ #include "code\modules\materials\definitions\gasses\material_gas_mundane.dm" #include "code\modules\materials\definitions\liquids\_mat_liquid.dm" #include "code\modules\materials\definitions\liquids\materials_liquid_chemistry.dm" +#include "code\modules\materials\definitions\liquids\materials_liquid_mundane.dm" #include "code\modules\materials\definitions\liquids\materials_liquid_solvents.dm" #include "code\modules\materials\definitions\liquids\materials_liquid_soup.dm" #include "code\modules\materials\definitions\liquids\materials_liquid_toxins.dm" @@ -2882,6 +2899,7 @@ #include "code\modules\mob\living\living_give.dm" #include "code\modules\mob\living\living_grabs.dm" #include "code\modules\mob\living\living_hallucinations.dm" +#include "code\modules\mob\living\living_hud.dm" #include "code\modules\mob\living\living_maneuvers.dm" #include "code\modules\mob\living\living_organs.dm" #include "code\modules\mob\living\living_powers.dm" @@ -3168,7 +3186,6 @@ #include "code\modules\modular_computers\file_system\programs\engineering\rcon_console.dm" #include "code\modules\modular_computers\file_system\programs\engineering\shields_monitor.dm" #include "code\modules\modular_computers\file_system\programs\engineering\shutoff_valve.dm" -#include "code\modules\modular_computers\file_system\programs\engineering\supermatter_monitor.dm" #include "code\modules\modular_computers\file_system\programs\generic\camera.dm" #include "code\modules\modular_computers\file_system\programs\generic\configurator.dm" #include "code\modules\modular_computers\file_system\programs\generic\crew_manifest.dm" @@ -3422,6 +3439,7 @@ #include "code\modules\posture\posture_bodytype.dm" #include "code\modules\posture\posture_mob.dm" #include "code\modules\posture\posture_subtypes.dm" +#include "code\modules\power\admin_setup_engine.dm" #include "code\modules\power\apc.dm" #include "code\modules\power\batteryrack.dm" #include "code\modules\power\breaker_box.dm" @@ -3783,14 +3801,6 @@ #include "code\modules\species\station\human_bodytypes.dm" #include "code\modules\species\station\monkey.dm" #include "code\modules\species\station\monkey_bodytypes.dm" -#include "code\modules\spells\construct_spells.dm" -#include "code\modules\spells\spell_code.dm" -#include "code\modules\spells\spells.dm" -#include "code\modules\spells\aoe_turf\aoe_turf.dm" -#include "code\modules\spells\aoe_turf\conjure\conjure.dm" -#include "code\modules\spells\targeted\ethereal_jaunt.dm" -#include "code\modules\spells\targeted\shift.dm" -#include "code\modules\spells\targeted\targeted.dm" #include "code\modules\sprite_accessories\_accessory.dm" #include "code\modules\sprite_accessories\_accessory_category.dm" #include "code\modules\sprite_accessories\cosmetics\_accessory_cosmetics.dm" @@ -3832,9 +3842,6 @@ #include "code\modules\submaps\submap_job.dm" #include "code\modules\submaps\submap_join.dm" #include "code\modules\submaps\submap_landmark.dm" -#include "code\modules\supermatter\setup_supermatter.dm" -#include "code\modules\supermatter\sm_looping_sound.dm" -#include "code\modules\supermatter\supermatter.dm" #include "code\modules\surgery\__surgery_setup.dm" #include "code\modules\surgery\_surgery.dm" #include "code\modules\surgery\bones.dm" @@ -4104,5 +4111,6 @@ #include "maps\~mapsystem\maps_unit_testing.dm" #include "maps\~unit_tests\unit_testing.dm" #include "mods\_modpack.dm" +#include "mods\~compatibility\~compatibility.dm" #include "~code\global_init.dm" // END_INCLUDE diff --git a/test/check-paths.sh b/test/check-paths.sh index dcf902b9335..852c681ae6c 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -23,34 +23,35 @@ exactly() { # exactly N name search [mode] [filter] # With the potential exception of << if you increase any of these numbers you're probably doing it wrong # Additional exception August 2020: \b is a regex symbol as well as a BYOND macro. -exactly 1 "escapes" '\\\\(red|blue|green|black|b|i[^mc])' -exactly 8 "Del()s" '\WDel\(' +exactly 3 "escapes" '\\(red|blue|green|black|b|i[^mc])' +exactly 3 "Del()s" '(?> uses" '>>(?!>)' -P +exactly 1 "world<< uses" 'world\s*<<' +exactly 74 "'in world' uses" '\s+\bin world\b(?=\s*$|\s*//|\s*\))' -P +exactly 1 "world.log<< uses" 'world.log\s*<<' +exactly 18 "<< uses" '(?> uses" '(?\\])>>(?!>)' -P exactly 0 "incorrect indentations" '^( {4,})' -P -exactly 22 "text2path uses" 'text2path' -exactly 4 "update_icon() override" '/update_icon\((.*)\)' -P -exactly 0 "goto uses" 'goto ' +exactly 23 "text2path uses" 'text2path' +exactly 4 "update_icon() overrides" '\/update_icon\(' -P +exactly 0 "goto uses" '\bgoto\b' exactly 9 "atom/New uses" '^/(obj|atom|area|mob|turf).*/New\(' exactly 1 "decl/New uses" '^/decl.*/New\(' -exactly 0 "tag uses" '\stag = ' -P '*.dmm' -exactly 3 "unmarked globally scoped variables" '^(/|)var/(?!global)' -P -exactly 0 "global-marked member variables" '\t(/|)var.*/global/.+' -P -exactly 0 "static-marked globally scoped variables" '^(/|)var.*/static/.+' -P +exactly 3 "tag uses" '(? 2: + ckey = sys.argv[2] + if ckey is not None: + ckey = ckey.lower() + + singletargetmap = None + if len(sys.argv) > 3: + singletargetmap = sys.argv[3] + if singletargetmap is not None: + singletargetmap = singletargetmap.lower() + # Work out what maps we actually need to replicate to. # This should be updated as map directories change, or the script will break. targetmaps = [] @@ -40,7 +52,7 @@ def main(): if os.path.isdir(dir): targetmap = dir.path targetmap = targetmap.replace(mapdir + os.sep, "") - if targetmap not in ignoremaps and targetmap != mapname: + if (targetmap not in ignoremaps) and (targetmap != mapname) and ((singletargetmap is None) or (singletargetmap == targetmap)): targetmaps.append(targetmap) # Make sure we can actually see the save directory. @@ -62,6 +74,8 @@ def main(): continue if match.group(1) != mapname: continue + if (ckey is not None) and (ckey != root[root.rfind("/")+1:]): + continue savefile = os.path.join(root, file) with open(savefile, "r") as loadedsave: wrote = 0 diff --git a/~code/global_init.dm b/~code/global_init.dm index 82950bb246d..c483e8f9e8b 100644 --- a/~code/global_init.dm +++ b/~code/global_init.dm @@ -11,15 +11,14 @@ Pre-map initialization stuff should go here. */ -var/global_init = new /datum/global_init() +var/global/global_init = new /datum/global_init() /datum/global_init/New() SSconfiguration.load_all_configuration() generate_game_id() makeDatumRefLists() - qdel(src) //we're done + QDEL_IN(src, 0) //we're done. give it some time to finish setting up though /datum/global_init/Destroy() - global_init = null - ..() - return QDEL_HINT_HARDDEL + global.global_init = null + return ..()