diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index 4af20415323..ce8b10b2b39 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: env: - SPACEMAN_DMM_VERSION: suite-1.7.2 + SPACEMAN_DMM_VERSION: suite-1.9 jobs: generate_documentation: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7e9c03f55c..05f6b0e3a55 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,12 +17,12 @@ on: env: BYOND_MAJOR: "515" - BYOND_MINOR: "1633" + BYOND_MINOR: "1643" SPACEMAN_DMM_VERSION: suite-1.8 jobs: DreamChecker: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Setup Cache @@ -53,7 +53,7 @@ jobs: chmod +x send.sh ./send.sh failure $WEBHOOK_URL OpenDream: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Python setup @@ -75,7 +75,7 @@ jobs: - name: Run OpenDream run: ./DMCompiler_linux-x64/DMCompiler nebula.dme --suppress-unimplemented --skip-anything-typecheck --version=${BYOND_MAJOR}.${BYOND_MINOR} | python tools/od_annotator/__main__.py "$@" Code: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Setup Cache @@ -101,7 +101,7 @@ jobs: chmod +x send.sh ./send.sh failure $WEBHOOK_URL Maps: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: map_path: [example, tether, tradeship, exodus, ministation, shaded_hills, away_sites_testing, modpack_testing, planets_testing] 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/SpacemanDMM.toml b/SpacemanDMM.toml index 2df2dcf2b92..f2359843c4b 100644 --- a/SpacemanDMM.toml +++ b/SpacemanDMM.toml @@ -56,6 +56,9 @@ override_precedes_definition = "error" hide_invisible = [ "/obj/effect/step_trigger", "/obj/abstract", + "/turf/unsimulated/mask", + "/obj/effect/shuttle_landmark", + "/obj/effect/overmap", ] [map_renderer.render_passes] diff --git a/code/___opendream_linting.dm b/code/___opendream_linting.dm index 889c19af33d..446ad58ad57 100644 --- a/code/___opendream_linting.dm +++ b/code/___opendream_linting.dm @@ -31,4 +31,5 @@ #pragma UnsafeClientAccess disabled #pragma SuspiciousSwitchCase error #pragma AssignmentInConditional error +#pragma AmbiguousInOrder error #endif \ No newline at end of file diff --git a/code/__datastructures/priority_queue.dm b/code/__datastructures/priority_queue.dm index 8625b858015..1c29024a1ca 100644 --- a/code/__datastructures/priority_queue.dm +++ b/code/__datastructures/priority_queue.dm @@ -54,7 +54,7 @@ /datum/priority_queue/proc/Length() . = L.len -/// Resorts the `item` to it's correct position in the queue. +/// Resorts the `item` to its correct position in the queue. /// * For example: The queue is sorted based on weight and atom A changes weight after being added /datum/priority_queue/proc/ReSort(item) var/i = Seek(item) diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm index d11cc95810b..ddc5a1c7516 100644 --- a/code/__defines/MC.dm +++ b/code/__defines/MC.dm @@ -96,6 +96,15 @@ if(Datum.is_processing) {\ #define TIMER_NO_HASH_WAIT BITFLAG(4) // For unique timers: don't distinguish timers by wait. #define TIMER_LOOP BITFLAG(5) // Repeat the timer until it's deleted or the parent is destroyed. +// TIMER_OVERRIDE is impossible to support because we don't track that for DPC queued calls, and adding a third list for that would be a lot of overhead for no real benefit +// TIMER_STOPPABLE can't work because it uses timer IDs instead of hashes, and DPC queued calls don't have IDs. +// TIMER_LOOP doesn't work because it needs to be a timer that can re-insert in the list, and a zero-wait looping timer should really be a ticker subsystem instead. +// Update these defines if any of those change. +/// These are the flags forbidden when putting zero-wait timers on SSdpc instead of SStimer. +#define DPC_FORBID_FLAGS TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE | TIMER_LOOP +/// These are the flags forbidden when putting zero-wait TIMER_UNIQUE timers on SSdpc instead of SStimer. +#define UDPC_FORBID_FLAGS TIMER_OVERRIDE | TIMER_STOPPABLE | TIMER_LOOP + #define TIMER_ID_NULL -1 /** diff --git a/code/__defines/ZAS.dm b/code/__defines/ZAS.dm index e80560b9fea..59466d88b53 100644 --- a/code/__defines/ZAS.dm +++ b/code/__defines/ZAS.dm @@ -110,3 +110,5 @@ var/global/list/gzn_check = list(NORTH, SOUTH, EAST, WEST) } #endif + +#define GAS_STANDARD_AIRMIX "STANDARD_AIRMIX" \ No newline at end of file diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index d2f61e13dca..6c9bc2a96df 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -1,3 +1,4 @@ +#define UNIT_TEST // 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 b2722d41224..656070dd108 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -91,31 +91,32 @@ What is the naming convention for planes or layers? #define ABOVE_WIRE_LAYER 1.58 //TURF PLANE //TURF_LAYER = 2 - #define TURF_DETAIL_LAYER 2.01 - #define TURF_SHADOW_LAYER 2.02 + #define TURF_OVER_EDGE_LAYER TURF_LAYER + (FLOOR_LAYER_CONSTANT*100) + #define TURF_DETAIL_LAYER TURF_OVER_EDGE_LAYER + 0.01 + #define TURF_SHADOW_LAYER TURF_OVER_EDGE_LAYER + 0.02 //ABOVE TURF - #define DECAL_LAYER 2.03 - #define RUNE_LAYER 2.04 - #define AO_LAYER 2.045 - #define ABOVE_TILE_LAYER 2.05 - #define EXPOSED_PIPE_LAYER 2.06 - #define EXPOSED_WIRE_LAYER 2.07 - #define EXPOSED_WIRE_TERMINAL_LAYER 2.08 - #define CATWALK_LAYER 2.09 - #define BLOOD_LAYER 2.10 - #define MOUSETRAP_LAYER 2.11 - #define PLANT_LAYER 2.12 + #define DECAL_LAYER TURF_OVER_EDGE_LAYER + 0.03 + #define RUNE_LAYER TURF_OVER_EDGE_LAYER + 0.04 + #define AO_LAYER TURF_OVER_EDGE_LAYER + 0.045 + #define ABOVE_TILE_LAYER TURF_OVER_EDGE_LAYER + 0.05 + #define EXPOSED_PIPE_LAYER TURF_OVER_EDGE_LAYER + 0.06 + #define EXPOSED_WIRE_LAYER TURF_OVER_EDGE_LAYER + 0.07 + #define EXPOSED_WIRE_TERMINAL_LAYER TURF_OVER_EDGE_LAYER + 0.08 + #define CATWALK_LAYER TURF_OVER_EDGE_LAYER + 0.09 + #define BLOOD_LAYER TURF_OVER_EDGE_LAYER + 0.10 + #define MOUSETRAP_LAYER TURF_OVER_EDGE_LAYER + 0.11 + #define PLANT_LAYER TURF_OVER_EDGE_LAYER + 0.12 //HIDING MOB - #define HIDING_MOB_LAYER 2.14 - #define SHALLOW_FLUID_LAYER 2.15 - #define MOB_SHADOW_LAYER 2.16 + #define HIDING_MOB_LAYER TURF_OVER_EDGE_LAYER + 0.14 + #define SHALLOW_FLUID_LAYER TURF_OVER_EDGE_LAYER + 0.15 + #define MOB_SHADOW_LAYER TURF_OVER_EDGE_LAYER + 0.16 //OBJ - #define BELOW_DOOR_LAYER 2.17 - #define OPEN_DOOR_LAYER 2.18 - #define BELOW_TABLE_LAYER 2.19 - #define TABLE_LAYER 2.20 - #define BELOW_OBJ_LAYER 2.21 - #define STRUCTURE_LAYER 2.22 + #define BELOW_DOOR_LAYER TURF_OVER_EDGE_LAYER + 0.17 + #define OPEN_DOOR_LAYER TURF_OVER_EDGE_LAYER + 0.18 + #define BELOW_TABLE_LAYER TURF_OVER_EDGE_LAYER + 0.19 + #define TABLE_LAYER TURF_OVER_EDGE_LAYER + 0.20 + #define BELOW_OBJ_LAYER TURF_OVER_EDGE_LAYER + 0.21 + #define STRUCTURE_LAYER TURF_OVER_EDGE_LAYER + 0.22 // OBJ_LAYER 3 #define ABOVE_OBJ_LAYER 3.01 #define CLOSED_DOOR_LAYER 3.02 @@ -124,6 +125,7 @@ What is the naming convention for planes or layers? #define FULL_WINDOW_LAYER 3.05 #define ABOVE_WINDOW_LAYER 3.06 //LYING MOB AND HUMAN + #define UNDER_MOB_LAYER 3.065 #define LYING_MOB_LAYER 3.07 #define LYING_HUMAN_LAYER 3.08 #define BASE_ABOVE_OBJ_LAYER 3.09 diff --git a/code/__defines/ambience.dm b/code/__defines/ambience.dm new file mode 100644 index 00000000000..c3b7c14d570 --- /dev/null +++ b/code/__defines/ambience.dm @@ -0,0 +1,11 @@ +#define AMBIENCE_QUEUE_TURF(T) \ + if(!T.ambience_queued) { \ + T.ambience_queued = TRUE; \ + SSambience.queued += T; \ + } + +#define AMBIENCE_DEQUEUE_TURF(T) \ + if(T.ambience_queued) { \ + T.ambience_queued = FALSE; \ + SSambience.queued -= T; \ + } \ No newline at end of file diff --git a/code/__defines/backgrounds.dm b/code/__defines/backgrounds.dm new file mode 100644 index 00000000000..f9fbe1bb6ef --- /dev/null +++ b/code/__defines/backgrounds.dm @@ -0,0 +1,7 @@ +#define BACKGROUND_FLAG_NAMING BITFLAG(0) +#define BACKGROUND_FLAG_CITIZENSHIP BITFLAG(1) +#define BACKGROUND_FLAG_IDEOLOGY BITFLAG(2) +#define BACKGROUND_FLAG_RELIGION BITFLAG(3) +#define BACKGROUND_FLAG_LOCATION BITFLAG(4) +#define BACKGROUND_FLAG_RESIDENCE BITFLAG(5) +#define BACKGROUND_FLAG_HOMEWORLD BITFLAG(6) diff --git a/code/__defines/bodytype.dm b/code/__defines/bodytype.dm index 5a720df0564..a44b77cc47f 100644 --- a/code/__defines/bodytype.dm +++ b/code/__defines/bodytype.dm @@ -1,7 +1,3 @@ -#define BODY_FLAG_EXCLUDE BITFLAG(0) -#define BODY_FLAG_HUMANOID BITFLAG(1) -#define BODY_FLAG_MONKEY BITFLAG(2) -#define BODY_FLAG_QUADRUPED BITFLAG(3) #define BODYTYPE_HUMANOID "humanoid body" #define BODYTYPE_QUADRUPED "quadruped body" @@ -10,9 +6,9 @@ // Bodytype appearance flags #define HAS_SKIN_TONE_NORMAL BITFLAG(0) // Skin tone selectable in chargen for baseline humans (0-220) -#define HAS_SKIN_COLOR BITFLAG(1) // Skin colour selectable in chargen. (RGB) +#define HAS_SKIN_COLOR BITFLAG(1) // Skin color selectable in chargen. (RGB) #define HAS_UNDERWEAR BITFLAG(3) // Underwear is drawn onto the mob icon. -#define HAS_EYE_COLOR BITFLAG(4) // Eye colour selectable in chargen. (RGB) +#define HAS_EYE_COLOR BITFLAG(4) // Eye color selectable in chargen. (RGB) #define RADIATION_GLOWS BITFLAG(6) // Radiation causes this character to glow. #define HAS_SKIN_TONE_GRAV BITFLAG(7) // Skin tone selectable in chargen for grav-adapted humans (0-100) #define HAS_SKIN_TONE_SPCR BITFLAG(8) // Skin tone selectable in chargen for spacer humans (0-165) @@ -20,9 +16,21 @@ #define HAS_A_SKIN_TONE (HAS_SKIN_TONE_NORMAL | HAS_SKIN_TONE_GRAV | HAS_SKIN_TONE_SPCR | HAS_SKIN_TONE_TRITON) // Bodytype has a numeric skintone // Bodytype feature flags -#define BODY_FLAG_NO_DNA BITFLAG(0) // Does not create DNA. Replaces SPECIES_FLAG_NO_SCAN. -#define BODY_FLAG_NO_PAIN BITFLAG(1) // Cannot suffer halloss/recieves deceptive health indicator. -#define BODY_FLAG_NO_EAT BITFLAG(2) // Cannot eat food/drink drinks even if a stomach organ is present. -#define BODY_FLAG_CRYSTAL_REFORM BITFLAG(3) // Can regenerate missing limbs from mineral baths. -#define BODY_FLAG_NO_STASIS BITFLAG(4) // Does not experience stasis effects (sleeper, cryo) -#define BODY_FLAG_NO_DEFIB BITFLAG(5) // Cannot be revived with a defibrilator. +/// Does not create DNA. Replaces SPECIES_FLAG_NO_SCAN. +#define BODY_FLAG_NO_DNA BITFLAG(0) +/// Cannot suffer halloss/recieves deceptive health indicator. +#define BODY_FLAG_NO_PAIN BITFLAG(1) +/// Cannot eat food/drink drinks even if a stomach organ is present. +#define BODY_FLAG_NO_EAT BITFLAG(2) +/// Can regenerate missing limbs from mineral baths. +#define BODY_FLAG_CRYSTAL_REFORM BITFLAG(3) +/// Does not experience stasis effects (sleeper, cryo) +#define BODY_FLAG_NO_STASIS BITFLAG(4) +/// Cannot be revived with a defibrilator. +#define BODY_FLAG_NO_DEFIB BITFLAG(5) + +// Equipment flags for gear and accessory restrictions +#define BODY_EQUIP_FLAG_EXCLUDE BITFLAG(0) +#define BODY_EQUIP_FLAG_HUMANOID BITFLAG(1) +#define BODY_EQUIP_FLAG_MONKEY BITFLAG(2) +#define BODY_EQUIP_FLAG_QUADRUPED BITFLAG(3) diff --git a/code/__defines/chemistry.dm b/code/__defines/chemistry.dm index 69ff2142d55..670021178c3 100644 --- a/code/__defines/chemistry.dm +++ b/code/__defines/chemistry.dm @@ -14,33 +14,60 @@ #define CHEM_SYNTH_ENERGY 500 // How much energy does it take to synthesize 1 unit of chemical, in Joules. -#define CE_STABLE "stable" // Stabilizing brain, pulse and breathing -#define CE_ANTIBIOTIC "antibiotic" // Spaceacilin -#define CE_BLOODRESTORE "bloodrestore" // Iron/nutriment -#define CE_PAINKILLER "painkiller" // Reduces the impact of shock/pain -#define CE_ALCOHOL "alcohol" // Liver filtering -#define CE_ALCOHOL_TOXIC "alcotoxic" // Liver damage -#define CE_SPEEDBOOST "gofast" // Stimulants -#define CE_SLOWDOWN "goslow" // Slowdown -#define CE_PULSE "xcardic" // increases or decreases heart rate -#define CE_NOPULSE "heartstop" // stops heartbeat -#define CE_ANTITOX "antitox" // Removes toxins -#define CE_OXYGENATED "oxygen" // Helps oxygenate the brain. -#define CE_BRAIN_REGEN "brainfix" // Allows the brain to recover after injury -#define CE_TOXIN "toxins" // Generic toxins, stops autoheal. -#define CE_BREATHLOSS "breathloss" // Breathing depression, makes you need more air -#define CE_MIND "mindbending" // Stabilizes or wrecks mind. Used for hallucinations -#define CE_CRYO "cryogenic" // Prevents damage from being frozen -#define CE_BLOCKAGE "blockage" // Gets in the way of blood circulation, higher the worse -#define CE_SQUEAKY "squeaky" // Helium voice. Squeak squeak. -#define CE_THIRDEYE "thirdeye" // Gives xray vision. -#define CE_SEDATE "sedate" // Applies sedation effects, i.e. paralysis, inability to use items, etc. -#define CE_ENERGETIC "energetic" // Speeds up stamina recovery. -#define CE_VOICELOSS "whispers" // Lowers the subject's voice to a whisper -#define CE_GLOWINGEYES "eyeglow" // Causes eyes to glow. - -#define CE_REGEN_BRUTE "bruteheal" // Causes brute damage to regenerate. -#define CE_REGEN_BURN "burnheal" // Causes burn damage to regenerate. +/// Stabilizing brain, pulse and breathing +#define CE_STABLE "stable" +/// Spaceacilin +#define CE_ANTIBIOTIC "antibiotic" +/// Iron/nutriment +#define CE_BLOODRESTORE "bloodrestore" +/// Reduces the impact of shock/pain +#define CE_PAINKILLER "painkiller" +/// Liver filtering +#define CE_ALCOHOL "alcohol" +/// Liver damage +#define CE_ALCOHOL_TOXIC "alcotoxic" +/// Stimulants +#define CE_SPEEDBOOST "gofast" +/// Slowdown +#define CE_SLOWDOWN "goslow" +/// increases or decreases heart rate +#define CE_PULSE "xcardic" +/// stops heartbeat +#define CE_NOPULSE "heartstop" +/// Removes toxins +#define CE_ANTITOX "antitox" +/// Helps oxygenate the brain. +#define CE_OXYGENATED "oxygen" +/// Allows the brain to recover after injury +#define CE_BRAIN_REGEN "brainfix" +/// Generic toxins, stops autoheal. +#define CE_TOXIN "toxins" +/// Breathing depression, makes you need more air +#define CE_BREATHLOSS "breathloss" +/// Stabilizes or wrecks mind. Used for hallucinations +#define CE_MIND "mindbending" +/// Prevents damage from being frozen +#define CE_CRYO "cryogenic" +/// Gets in the way of blood circulation, higher the worse +#define CE_BLOCKAGE "blockage" +/// Helium voice. Squeak squeak. +#define CE_SQUEAKY "squeaky" +/// Gives xray vision. +#define CE_THIRDEYE "thirdeye" +/// Applies sedation effects, i.e. paralysis, inability to use items, etc. +#define CE_SEDATE "sedate" +/// Speeds up stamina recovery. +#define CE_ENERGETIC "energetic" +/// Lowers the subject's voice to a whisper +#define CE_VOICELOSS "whispers" +/// Causes eyes to glow. +#define CE_GLOWINGEYES "eyeglow" +/// Causes brute damage to regenerate. +#define CE_REGEN_BRUTE "bruteheal" +/// Causes burn damage to regenerate. +#define CE_REGEN_BURN "burnheal" +/// Anaphylaxis etc. +#define CE_ALLERGEN "allergyreaction" #define GET_CHEMICAL_EFFECT(X, C) (LAZYACCESS(X.chem_effects, C) || 0) @@ -55,6 +82,10 @@ #define REAGENTS_FREE_SPACE(R) (R?.maximum_volume - R?.total_volume) #define REAGENT_VOLUME(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.reagent_volumes && REAGENT_HOLDER.reagent_volumes[REAGENT_TYPE]) + +#define LIQUID_VOLUME(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.liquid_volumes && REAGENT_HOLDER.liquid_volumes[REAGENT_TYPE]) +#define SOLID_VOLUME(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.solid_volumes && REAGENT_HOLDER.solid_volumes[REAGENT_TYPE]) + #define REAGENT_DATA(REAGENT_HOLDER, REAGENT_TYPE) (REAGENT_HOLDER?.reagent_data && REAGENT_HOLDER.reagent_data[REAGENT_TYPE]) #define MAT_SOLVENT_NONE 0 @@ -74,3 +105,5 @@ #define DEFAULT_GAS_OXIDIZER /decl/material/gas/oxygen #define CHEM_REACTION_FLAG_OVERFLOW_CONTAINER BITFLAG(0) + +#define MAX_SCRAP_MATTER (SHEET_MATERIAL_AMOUNT * 5) // Maximum amount of matter in chemical scraps \ No newline at end of file diff --git a/code/__defines/colors.dm b/code/__defines/colors.dm index 45348be8eeb..7477331b43e 100644 --- a/code/__defines/colors.dm +++ b/code/__defines/colors.dm @@ -1,7 +1,7 @@ -#define HEX_RED(COLOUR) hex2num(copytext(COLOUR,2,4)) -#define HEX_GREEN(COLOUR) hex2num(copytext(COLOUR,4,6)) -#define HEX_BLUE(COLOUR) hex2num(copytext(COLOUR,6,8)) -#define HEX_ALPHA(COLOUR) hex2num(copytext(COLOUR,8,10)) +#define HEX_RED(COLOR) hex2num(copytext(COLOR,2,4)) +#define HEX_GREEN(COLOR) hex2num(copytext(COLOR,4,6)) +#define HEX_BLUE(COLOR) hex2num(copytext(COLOR,6,8)) +#define HEX_ALPHA(COLOR) hex2num(copytext(COLOR,8,10)) // BYOND lower-cases color values, and thus we do so as well to ensure atom.color == COLOR_X will work correctly #define COLOR_BLACK "#000000" @@ -99,6 +99,7 @@ #define COLOR_VERDANT_GREEN "#287d00" #define COLOR_SCIENCE_PURPLE "#6633cc" #define COLOR_DAYLIGHT "#f3e6ca" +#define COLOR_CHERRY_RED "#902630" #define PIPE_COLOR_GREY "#808080" #define PIPE_COLOR_RED "#ff0000" diff --git a/code/__defines/cooking.dm b/code/__defines/cooking.dm index cb24c0e6e9f..820a6b98144 100644 --- a/code/__defines/cooking.dm +++ b/code/__defines/cooking.dm @@ -6,9 +6,20 @@ #define FOOD_PREPARED 0 #define FOOD_COOKED 1 -#define INGREDIENT_FLAG_PLAIN 0 -#define INGREDIENT_FLAG_MEAT BITFLAG(0) -#define INGREDIENT_FLAG_FISH BITFLAG(1) -#define INGREDIENT_FLAG_VEGETABLE BITFLAG(2) -#define INGREDIENT_FLAG_DAIRY BITFLAG(3) -#define INGREDIENT_FLAG_EGG BITFLAG(4) +#define ALLERGEN_NONE 0 +#define ALLERGEN_MEAT BITFLAG(0) +#define ALLERGEN_FISH BITFLAG(1) +#define ALLERGEN_VEGETABLE BITFLAG(2) +#define ALLERGEN_DAIRY BITFLAG(3) +#define ALLERGEN_CHEESE BITFLAG(4) +#define ALLERGEN_EGG BITFLAG(5) +#define ALLERGEN_FRUIT BITFLAG(6) +#define ALLERGEN_GLUTEN BITFLAG(7) +#define ALLERGEN_SOY BITFLAG(8) +#define ALLERGEN_CAFFEINE BITFLAG(9) +#define ALLERGEN_FUNGI BITFLAG(10) +#define ALLERGEN_NUTS BITFLAG(11) +#define ALLERGEN_ALLIUM BITFLAG(12) +#define ALLERGEN_STIMULANT BITFLAG(13) + +#define ALLERGENS_ALL (ALLERGEN_MEAT|ALLERGEN_FISH|ALLERGEN_VEGETABLE|ALLERGEN_DAIRY|ALLERGEN_CHEESE|ALLERGEN_EGG|ALLERGEN_FRUIT|ALLERGEN_GLUTEN|ALLERGEN_SOY|ALLERGEN_CAFFEINE|ALLERGEN_NUTS|ALLERGEN_ALLIUM|ALLERGEN_STIMULANT) diff --git a/code/__defines/culture.dm b/code/__defines/culture.dm deleted file mode 100644 index 6c83c3c792e..00000000000 --- a/code/__defines/culture.dm +++ /dev/null @@ -1,11 +0,0 @@ -#define TAG_CULTURE "culture" -#define TAG_HOMEWORLD "home_system" -#define TAG_FACTION "faction" -#define TAG_RELIGION "religion" - -var/global/list/ALL_CULTURAL_TAGS = list( - TAG_CULTURE = "Culture", - TAG_HOMEWORLD = "Residence", - TAG_FACTION = "Faction", - TAG_RELIGION = "Beliefs" -) diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm index 24a1bf4e5e3..5e04e949963 100644 --- a/code/__defines/flags.dm +++ b/code/__defines/flags.dm @@ -67,6 +67,7 @@ The latter will result in a linter warning and will not work correctly. #define OBJ_FLAG_HOLLOW BITFLAG(5) // Modifies initial matter values to be lower than w_class normally sets. #define OBJ_FLAG_SUPPORT_MOB BITFLAG(6) // Object can be used to prop up a mob with stance damage (broken legs) #define OBJ_FLAG_INSULATED_HANDLE BITFLAG(7) // Object skips burn checks when held by unprotected hands. +#define OBJ_FLAG_NO_STORAGE BITFLAG(8) // Object cannot be placed into storage. // Item-level flags (/obj/item/item_flags) #define ITEM_FLAG_NO_BLUDGEON BITFLAG(0) // When an item has this it produces no "X has been hit by Y with Z" message with the default handler. @@ -83,8 +84,9 @@ 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_IS_WEAPON BITFLAG(15) // Item is considered a weapon. Currently only used for force-based worth calculation. +#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. +#define ITEM_FLAG_MAGNETISED BITFLAG(16) // When worn on feet and standing on an appropriate spot, will prevent slipping. // Flags for pass_flags (/atom/var/pass_flags) #define PASS_FLAG_TABLE BITFLAG(0) diff --git a/code/__defines/fluids.dm b/code/__defines/fluids.dm index bae8f40cde6..96ebc2a9339 100644 --- a/code/__defines/fluids.dm +++ b/code/__defines/fluids.dm @@ -1,6 +1,7 @@ #define FLUID_QDEL_POINT 1 // Depth a fluid begins self-deleting #define FLUID_MINIMUM_TRANSFER 5 // Minimum amount that a flowing fluid will transfer from one turf to another. #define FLUID_PUDDLE 25 // Minimum total depth that a fluid needs before it will start spreading. +#define FLUID_SLURRY 50 // Mimimum depth before fluids will move solids as reagents. #define FLUID_SHALLOW 200 // Depth shallow icon is used #define FLUID_OVER_MOB_HEAD 300 // Depth icon layers over mobs. #define FLUID_DEEP 800 // Depth deep icon is used diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 9493ae7aa12..b3c749c4a54 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 diff --git a/code/__defines/interactions.dm b/code/__defines/interactions.dm index d624b0bee87..e51fa7ac213 100644 --- a/code/__defines/interactions.dm +++ b/code/__defines/interactions.dm @@ -1,4 +1,10 @@ +/// This interaction requires the user to be adjacent to the target. #define INTERACTION_NEEDS_ADJACENCY BITFLAG(0) +/// This interaction requires the user to pass a physical interaction check. #define INTERACTION_NEEDS_PHYSICAL_INTERACTION BITFLAG(1) +/// This interaction requires the target to be on a turf #define INTERACTION_NEEDS_TURF BITFLAG(2) +/// This interaction requires the target to be in the user's inventory. #define INTERACTION_NEEDS_INVENTORY BITFLAG(3) +/// This interaction will always prompt for a selection from the user, even if it is the only available interaction. +#define INTERACTION_NEVER_AUTOMATIC BITFLAG(4) \ No newline at end of file diff --git a/code/__defines/inventory_sizes.dm b/code/__defines/inventory_sizes.dm index dbbdd6f31df..39842fd9099 100644 --- a/code/__defines/inventory_sizes.dm +++ b/code/__defines/inventory_sizes.dm @@ -22,7 +22,6 @@ #define ITEM_SIZE_LARGE 4 #define ITEM_SIZE_HUGE 5 #define ITEM_SIZE_GARGANTUAN 6 -#define ITEM_SIZE_NO_CONTAINER 10 // Use this to forbid item from being placed in a container. #define ITEM_SIZE_STRUCTURE 20 #define ITEM_SIZE_MIN ITEM_SIZE_TINY diff --git a/code/__defines/items_clothing.dm b/code/__defines/items_clothing.dm index b9d70eeca7a..b2a5c347ddf 100644 --- a/code/__defines/items_clothing.dm +++ b/code/__defines/items_clothing.dm @@ -2,23 +2,26 @@ #define CANDLE_LUM 3 // For how bright candles are. -#define ACCESSORY_SLOT_UTILITY "Utility" -#define ACCESSORY_SLOT_HOLSTER "Holster" -#define ACCESSORY_SLOT_ARMBAND "Armband" -#define ACCESSORY_SLOT_RANK "Rank" -#define ACCESSORY_SLOT_DEPT "Department" -#define ACCESSORY_SLOT_DECOR "Decor" -#define ACCESSORY_SLOT_NECK "Neck" -#define ACCESSORY_SLOT_MEDAL "Medal" -#define ACCESSORY_SLOT_INSIGNIA "Insignia" -#define ACCESSORY_SLOT_ARMOR_C "Chest armor" -#define ACCESSORY_SLOT_ARMOR_A "Arm armor" -#define ACCESSORY_SLOT_ARMOR_L "Leg armor" -#define ACCESSORY_SLOT_ARMOR_S "Armor storage" -#define ACCESSORY_SLOT_ARMOR_M "Misc armor" -#define ACCESSORY_SLOT_HELM_C "Helmet cover" -#define ACCESSORY_SLOT_OVER "Over" -#define ACCESSORY_SLOT_SENSORS "Suit Sensors" +#define ACCESSORY_SLOT_UTILITY "Utility" +#define ACCESSORY_SLOT_HOLSTER "Holster" +#define ACCESSORY_SLOT_ARMBAND "Armband" +#define ACCESSORY_SLOT_RANK "Rank" +#define ACCESSORY_SLOT_DEPT "Department" +#define ACCESSORY_SLOT_DECOR "Decor" +#define ACCESSORY_SLOT_NECK "Neck" +#define ACCESSORY_SLOT_MEDAL "Medal" +#define ACCESSORY_SLOT_INSIGNIA "Insignia" +#define ACCESSORY_SLOT_ARMOR_C "Chest armor" +#define ACCESSORY_SLOT_ARMOR_A "Arm armor" +#define ACCESSORY_SLOT_ARMOR_L "Leg armor" +#define ACCESSORY_SLOT_ARMOR_S "Armor storage" +#define ACCESSORY_SLOT_ARMOR_M "Misc armor" +#define ACCESSORY_SLOT_HELM_C "Helmet cover" +#define ACCESSORY_SLOT_OVER_HELMET "Hat" +#define ACCESSORY_SLOT_OVER "Over" +#define ACCESSORY_SLOT_SENSORS "Suit Sensors" +#define ACCESSORY_SLOT_GREAVES "Greaves" +#define ACCESSORY_SLOT_GAUNTLETS "Gauntlets" // Accessory will be shown as part of the name of the item when examined. #define ACCESSORY_VISIBILITY_ENSEMBLE 0 @@ -173,7 +176,7 @@ #define FIRE_MAX_FIRESUIT_STACKS 20 // If the number of stacks goes above this firesuits won't protect you anymore. If not, you can walk around while on fire like a badass. #define THROWFORCE_GIBS 3 // Throw speed for gibbed or dismembered organs. -#define THROWFORCE_SPEED_DIVISOR 12 // The throwing speed value at which the throwforce multiplier is exactly 1. +#define THROWFORCE_SPEED_DIVISOR 12 // The throwing speed value at which the thrown force multiplier is exactly 1. #define THROWNOBJ_KNOCKBACK_SPEED 15 // The minumum speed of a w_class 2 thrown object that will cause living mobs it hits to be knocked back. Heavier objects can cause knockback at lower speeds. #define THROWNOBJ_KNOCKBACK_DIVISOR 2 // Affects how much speed the mob is knocked back with. @@ -184,7 +187,7 @@ #define VITALS_SENSOR_TRACKING 3 // Hair Flags -#define VERY_SHORT BITFLAG(0) +#define HAIR_VERY_SHORT BITFLAG(0) #define HAIR_TIEABLE BITFLAG(1) #define HAIR_BALD BITFLAG(2) #define HAIR_LOSS_VULNERABLE BITFLAG(3) diff --git a/code/__defines/materials.dm b/code/__defines/materials.dm index f45f68e16dc..fead13bf7dc 100644 --- a/code/__defines/materials.dm +++ b/code/__defines/materials.dm @@ -29,10 +29,12 @@ #define MAT_VALUE_VERY_HEAVY 80 // uranium tier //Construction difficulty -#define MAT_VALUE_EASY_DIY 0 -#define MAT_VALUE_NORMAL_DIY 1 -#define MAT_VALUE_HARD_DIY 2 -#define MAT_VALUE_VERY_HARD_DIY 3 +#define MAT_VALUE_TRIVIAL_DIY 0 // Does not have any difficulty component at all +#define MAT_VALUE_EASY_DIY 1 // SKILL_NONE (Unskilled) +#define MAT_VALUE_NORMAL_DIY 2 // SKILL_BASIC (Basic) +#define MAT_VALUE_HARD_DIY 3 // SKILL_ADEPT (Trained) +#define MAT_VALUE_VERY_HARD_DIY 4 // SKILL_EXPERT (Experienced) +#define MAT_VALUE_EXTREME_DIY 5 // SKILL_PROF (Master) //Arbitrary hardness thresholds #define MAT_VALUE_MALLEABLE 0 @@ -64,10 +66,10 @@ #define ORE_EXOTIC "exotic matter" //Phase of matter placeholders -#define MAT_PHASE_SOLID "solid" -#define MAT_PHASE_LIQUID "liquid" -#define MAT_PHASE_GAS "gas" -#define MAT_PHASE_PLASMA "plasma" +#define MAT_PHASE_SOLID BITFLAG(0) +#define MAT_PHASE_LIQUID BITFLAG(1) +#define MAT_PHASE_GAS BITFLAG(2) +#define MAT_PHASE_PLASMA BITFLAG(3) // Fission interactions. // For these, associated value is ideal neutron energy for reaction. diff --git a/code/__defines/math_physics.dm b/code/__defines/math_physics.dm index 5560282337e..4edf99ad88a 100644 --- a/code/__defines/math_physics.dm +++ b/code/__defines/math_physics.dm @@ -34,5 +34,8 @@ #define TICKS_IN_DAY 24*60*60*10 #define TICKS_IN_SECOND 10 -#define SIMPLE_SIGN(X) ((X) < 0 ? -1 : 1) -#define SIGN(X) ((X) ? SIMPLE_SIGN(X) : 0) +#if DM_VERSION < 516 +#define SIGN(X) ( (X) ? ( (X) < 0 ? -1 : 1 ) : 0 ) +#else +#define SIGN(X) sign(X) +#endif diff --git a/code/__defines/maths.dm b/code/__defines/maths.dm index 17ad6e2a91c..0307b295219 100644 --- a/code/__defines/maths.dm +++ b/code/__defines/maths.dm @@ -1,15 +1,15 @@ // Macro functions. #define RAND_F(LOW, HIGH) (rand() * (HIGH - LOW) + LOW) -#define CEILING(x) (-round(-(x))) // Float-aware floor and ceiling since round() will round upwards when given a second arg. -#define NONUNIT_FLOOR(x, y) (round( (x) / (y)) * (y)) -#define NONUNIT_CEILING(x, y) (-round(-(x) / (y)) * (y)) +#define NONUNIT_FLOOR(x, y) (floor((x) / (y)) * (y)) +#define NONUNIT_CEILING(x, y) (ceil((x) / (y)) * (y)) + +// Special two-step rounding for reagents, to avoid floating point errors. +#define CHEMS_QUANTIZE(x) NONUNIT_FLOOR(round(x, MINIMUM_CHEMICAL_VOLUME * 0.1), MINIMUM_CHEMICAL_VOLUME) #define MULT_BY_RANDOM_COEF(VAR,LO,HI) VAR = round((VAR * rand(LO * 100, HI * 100))/100, 0.1) -#define ROUND(x) (((x) >= 0) ? round((x)) : -round(-(x))) -#define FLOOR(x) (round(x)) #define EULER 2.7182818285 #define MODULUS_FLOAT(X, Y) ( (X) - (Y) * round((X) / (Y)) ) @@ -19,4 +19,7 @@ #define SIMPLIFY_DEGREES(degrees) (MODULUS_FLOAT((degrees), 360)) #define IS_POWER_OF_TWO(VAL) ((VAL & (VAL-1)) == 0) -#define ROUND_UP_TO_POWER_OF_TWO(VAL) (2 ** CEILING(log(2,VAL))) +#define ROUND_UP_TO_POWER_OF_TWO(VAL) (2 ** ceil(log(2,VAL))) + +// turn(0, angle) returns a random dir. This macro will instead do nothing if dir is already 0. +#define SAFE_TURN(DIR, ANGLE) (DIR && turn(DIR, ANGLE)) diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 57b17ee6df6..428cfcb24c7 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -131,11 +131,20 @@ #define PROJECTILE_CONTINUE -1 //if the projectile should continue flying after calling bullet_act() #define PROJECTILE_FORCE_MISS -2 //if the projectile should treat the attack as a miss (suppresses attack and admin logs) - only applies to mobs. -//objectives +// Objective config enum values. #define CONFIG_OBJECTIVE_NONE 2 #define CONFIG_OBJECTIVE_VERB 1 #define CONFIG_OBJECTIVE_ALL 0 +// Server whitelist config enums. +#define CONFIG_SERVER_NO_WHITELIST 1 +#define CONFIG_SERVER_JOBS_WHITELIST 2 +#define CONFIG_SERVER_JOIN_WHITELIST 3 +#define CONFIG_SERVER_CONNECT_WHITELIST 4 + +// Location for server whitelist file to load from. +#define CONFIG_SERVER_WHITELIST_FILE "config/server_whitelist.txt" + // How many times an AI tries to connect to APC before switching to low power mode. #define AI_POWER_RESTORE_MAX_ATTEMPTS 3 @@ -261,8 +270,6 @@ /// Returns the hex value of a number given a value assumed to be a base-ten value, padded to a supplied minimum length. #define num2hex_padded(num, len) num2text(num, len, 16) -#define Z_ALL_TURFS(Z) block(locate(1, 1, Z), locate(world.maxx, world.maxy, Z)) - //NOTE: INTENT_HOTKEY_* defines are not actual intents! //they are here to support hotkeys #define INTENT_HOTKEY_LEFT "left" @@ -376,4 +383,18 @@ #define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X)) /// Call by name proc reference, checks if the proc is an existing global proc -#define GLOBAL_PROC_REF(X) (/proc/##X) \ No newline at end of file +#define GLOBAL_PROC_REF(X) (/proc/##X) + +#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" + +// Enum for results of is_space_movement_permitted() +#define SPACE_MOVE_SUPPORTED (-1) +#define SPACE_MOVE_FORBIDDEN 0 +#define SPACE_MOVE_PERMITTED 1 diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 88afc006148..9d48c45354b 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -90,11 +90,11 @@ #define COMPANY_ALIGNMENTS list(COMPANY_LOYAL,COMPANY_SUPPORTATIVE,COMPANY_NEUTRAL,COMPANY_SKEPTICAL,COMPANY_OPPOSED) // Defines mob sizes, used by lockers and to determine what is considered a small sized mob, etc. -#define MOB_SIZE_LARGE 40 -#define MOB_SIZE_MEDIUM 20 -#define MOB_SIZE_SMALL 10 -#define MOB_SIZE_TINY 5 -#define MOB_SIZE_MINISCULE 1 +#define MOB_SIZE_LARGE ITEM_SIZE_STRUCTURE * 2 +#define MOB_SIZE_MEDIUM ITEM_SIZE_STRUCTURE +#define MOB_SIZE_SMALL ITEM_SIZE_NORMAL +#define MOB_SIZE_TINY ITEM_SIZE_SMALL +#define MOB_SIZE_MINISCULE ITEM_SIZE_TINY #define MOB_SIZE_MIN MOB_SIZE_MINISCULE #define MOB_SIZE_MAX MOB_SIZE_LARGE @@ -227,6 +227,31 @@ // One 'unit' of taste sensitivity probability, used in mob/living/proc/ingest #define TASTE_DEGREE_PROB 15 +// General food data flags +#define DATA_TASTE /decl/reagent_data_field/taste +#define DATA_INGREDIENT_LIST /decl/reagent_data_field/ingredient_list +#define DATA_INGREDIENT_FLAGS /decl/reagent_data_field/ingredient_flags +#define DATA_MASK_COLOR /decl/reagent_data_field/mask_color +#define DATA_MASK_NAME /decl/reagent_data_field/mask_name +#define DATA_EXTRA_COLOR /decl/reagent_data_field/extra_color + +// Milk and chees data flags +#define DATA_MILK_DONOR /decl/reagent_data_field/milk_donor +#define DATA_MILK_NAME /decl/reagent_data_field/milk_name +#define DATA_MILK_COLOR /decl/reagent_data_field/milk_color +#define DATA_CHEESE_NAME /decl/reagent_data_field/cheese_name +#define DATA_CHEESE_COLOR /decl/reagent_data_field/cheese_color + +// Blood data flags +#define DATA_BLOOD_DNA /decl/reagent_data_field/blood_dna +#define DATA_BLOOD_DONOR /decl/reagent_data_field/blood_donor +#define DATA_BLOOD_SPECIES /decl/reagent_data_field/blood_species +#define DATA_BLOOD_COLOR /decl/reagent_data_field/blood_color +#define DATA_BLOOD_TYPE /decl/reagent_data_field/blood_type +#define DATA_BLOOD_TRACE_CHEM /decl/reagent_data_field/blood_trace_chem +#define DATA_BLOOD_DOSE_CHEM /decl/reagent_data_field/blood_dose_chem +#define DATA_BLOOD_HAS_OXY /decl/reagent_data_field/blood_has_oxy + //Used by show_message() and emotes #define VISIBLE_MESSAGE 1 #define AUDIBLE_MESSAGE 2 @@ -247,8 +272,9 @@ #define SYNTH_HEAT_LEVEL_2 1000 #define SYNTH_HEAT_LEVEL_3 2000 -#define CORPSE_CAN_REENTER 1 -#define CORPSE_CAN_REENTER_AND_RESPAWN 2 +#define CORPSE_CANNOT_REENTER 0 +#define CORPSE_CAN_REENTER BITFLAG(0) +#define CORPSE_CAN_RESPAWN BITFLAG(1) #define SPECIES_HUMAN "Human" #define SPECIES_MONKEY "Monkey" @@ -334,10 +360,11 @@ var/global/list/dexterity_levels = list( #define MOB_ICON_HAS_LIVING_STATE BITFLAG(0) #define MOB_ICON_HAS_DEAD_STATE BITFLAG(1) #define MOB_ICON_HAS_REST_STATE BITFLAG(2) -#define MOB_ICON_HAS_SLEEP_STATE BITFLAG(3) -#define MOB_ICON_HAS_GIB_STATE BITFLAG(4) -#define MOB_ICON_HAS_DUST_STATE BITFLAG(5) -#define MOB_ICON_HAS_PARALYZED_STATE BITFLAG(6) +#define MOB_ICON_HAS_SITTING_STATE BITFLAG(3) +#define MOB_ICON_HAS_SLEEP_STATE BITFLAG(4) +#define MOB_ICON_HAS_GIB_STATE BITFLAG(5) +#define MOB_ICON_HAS_DUST_STATE BITFLAG(6) +#define MOB_ICON_HAS_PARALYZED_STATE BITFLAG(7) #define NEUTER_ANIMATE "animate singular neutral" // Equipment Overlays Indices // @@ -345,29 +372,30 @@ var/global/list/dexterity_levels = list( #define HO_SKIN_LAYER 2 #define HO_DAMAGE_LAYER 3 #define HO_SURGERY_LAYER 4 //bs12 specific. -#define HO_UNDERWEAR_LAYER 5 -#define HO_UNIFORM_LAYER 6 -#define HO_ID_LAYER 7 -#define HO_SHOES_LAYER 8 -#define HO_GLOVES_LAYER 9 -#define HO_BELT_LAYER 10 -#define HO_SUIT_LAYER 11 -#define HO_GLASSES_LAYER 12 -#define HO_BELT_LAYER_ALT 13 -#define HO_SUIT_STORE_LAYER 14 -#define HO_BACK_LAYER 15 -#define HO_TAIL_LAYER 16 //bs12 specific. this hack is probably gonna come back to haunt me -#define HO_HAIR_LAYER 17 //TODO: make part of head layer? -#define HO_GOGGLES_LAYER 18 -#define HO_L_EAR_LAYER 19 -#define HO_R_EAR_LAYER 20 -#define HO_FACEMASK_LAYER 21 -#define HO_HEAD_LAYER 22 -#define HO_COLLAR_LAYER 23 -#define HO_HANDCUFF_LAYER 24 -#define HO_INHAND_LAYER 25 -#define HO_FIRE_LAYER 26 //If you're on fire -#define TOTAL_OVER_LAYERS 26 +#define HO_BANDAGE_LAYER 5 +#define HO_UNDERWEAR_LAYER 6 +#define HO_UNIFORM_LAYER 7 +#define HO_ID_LAYER 8 +#define HO_SHOES_LAYER 9 +#define HO_GLOVES_LAYER 10 +#define HO_BELT_LAYER 11 +#define HO_SUIT_LAYER 12 +#define HO_GLASSES_LAYER 13 +#define HO_BELT_LAYER_ALT 14 +#define HO_SUIT_STORE_LAYER 15 +#define HO_BACK_LAYER 16 +#define HO_TAIL_LAYER 17 //bs12 specific. this hack is probably gonna come back to haunt me +#define HO_HAIR_LAYER 18 //TODO: make part of head layer? +#define HO_GOGGLES_LAYER 19 +#define HO_L_EAR_LAYER 20 +#define HO_R_EAR_LAYER 21 +#define HO_FACEMASK_LAYER 22 +#define HO_HEAD_LAYER 23 +#define HO_COLLAR_LAYER 24 +#define HO_HANDCUFF_LAYER 25 +#define HO_INHAND_LAYER 26 +#define HO_FIRE_LAYER 27 //If you're on fire +#define TOTAL_OVER_LAYERS 27 ////////////////////////////////// // Underlay defines; vestigal implementation currently. @@ -383,24 +411,49 @@ var/global/list/dexterity_levels = list( #define EATING_METHOD_EAT 0 #define EATING_METHOD_DRINK 1 +// Sprite accessory categories for shorter reference. #define SAC_HAIR /decl/sprite_accessory_category/hair #define SAC_FACIAL_HAIR /decl/sprite_accessory_category/facial_hair #define SAC_COSMETICS /decl/sprite_accessory_category/cosmetics #define SAC_MARKINGS /decl/sprite_accessory_category/markings +#define SAC_EARS /decl/sprite_accessory_category/ears #define SAC_HORNS /decl/sprite_accessory_category/horns #define SAC_FRILLS /decl/sprite_accessory_category/frills +#define SAC_TAIL /decl/sprite_accessory_category/tail + +// Sprite accessory metadata types for shorter reference. +#define SAM_COLOR /decl/sprite_accessory_metadata/color +#define SAM_COLOR_INNER /decl/sprite_accessory_metadata/color/alt +#define SAM_GRADIENT /decl/sprite_accessory_metadata/gradient // Helpers for setting mob appearance. They are extremely ugly, hence the helpers. -#define SET_HAIR_STYLE(TARGET, STYLE, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category((STYLE), SAC_HAIR, null, TRUE, FALSE, BP_HEAD, SKIP_UPDATE)) -#define GET_HAIR_STYLE(TARGET) (TARGET.get_organ_sprite_accessory_by_category(SAC_HAIR, BP_HEAD)) -#define SET_HAIR_COLOUR(TARGET, COLOUR, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category(null, SAC_HAIR, (COLOUR), FALSE, TRUE, BP_HEAD, SKIP_UPDATE)) -#define GET_HAIR_COLOUR(TARGET) (TARGET.get_organ_sprite_accessory(GET_HAIR_STYLE(TARGET), BP_HEAD)) +#define SET_HAIR_STYLE(TARGET, STYLE, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category((STYLE), SAC_HAIR, null, TRUE, FALSE, BP_HEAD, SKIP_UPDATE)) +#define GET_HAIR_STYLE(TARGET) (TARGET.get_organ_sprite_accessory_by_category(SAC_HAIR, BP_HEAD)) +#define SET_HAIR_COLOR(TARGET, COLOR, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category(null, SAC_HAIR, list(SAM_COLOR = (COLOR)), FALSE, TRUE, BP_HEAD, SKIP_UPDATE)) +#define GET_HAIR_COLOR(TARGET) (TARGET.get_organ_sprite_accessory_metadata(GET_HAIR_STYLE(TARGET), BP_HEAD, SAM_COLOR)) -#define SET_FACIAL_HAIR_STYLE(TARGET, STYLE, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category((STYLE), SAC_FACIAL_HAIR, null, TRUE, FALSE, BP_HEAD, SKIP_UPDATE)) -#define GET_FACIAL_HAIR_STYLE(TARGET) (TARGET.get_organ_sprite_accessory_by_category(SAC_FACIAL_HAIR, BP_HEAD)) -#define SET_FACIAL_HAIR_COLOUR(TARGET, COLOUR, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category(null, SAC_FACIAL_HAIR, (COLOUR), FALSE, TRUE, BP_HEAD, SKIP_UPDATE)) -#define GET_FACIAL_HAIR_COLOUR(TARGET) (TARGET.get_organ_sprite_accessory(GET_FACIAL_HAIR_STYLE(TARGET), BP_HEAD)) +#define SET_FACIAL_HAIR_STYLE(TARGET, STYLE, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category((STYLE), SAC_FACIAL_HAIR, null, TRUE, FALSE, BP_HEAD, SKIP_UPDATE)) +#define GET_FACIAL_HAIR_STYLE(TARGET) (TARGET.get_organ_sprite_accessory_by_category(SAC_FACIAL_HAIR, BP_HEAD)) +#define SET_FACIAL_HAIR_COLOR(TARGET, COLOR, SKIP_UPDATE) (TARGET.set_organ_sprite_accessory_by_category(null, SAC_FACIAL_HAIR, list(SAM_COLOR = (COLOR)), FALSE, TRUE, BP_HEAD, SKIP_UPDATE)) +#define GET_FACIAL_HAIR_COLOR(TARGET) (TARGET.get_organ_sprite_accessory_metadata(GET_FACIAL_HAIR_STYLE(TARGET), BP_HEAD, SAM_COLOR)) // Used in death() to skip message broadcast. #define SKIP_DEATH_MESSAGE "no message" +// Used in organ stance calc. +#define LIMB_UNUSABLE 2 +#define LIMB_DAMAGED 1 +#define LIMB_IMPAIRED 0.5 + +// Used by allergy effects. +#define ALLERGEN_REACTION_NONE 0 +#define ALLERGEN_REACTION_PHYS_DMG BITFLAG(0) +#define ALLERGEN_REACTION_BURN_DMG BITFLAG(1) +#define ALLERGEN_REACTION_TOX_DMG BITFLAG(2) +#define ALLERGEN_REACTION_OXY_DMG BITFLAG(3) +#define ALLERGEN_REACTION_EMOTE BITFLAG(4) +#define ALLERGEN_REACTION_PAIN BITFLAG(5) +#define ALLERGEN_REACTION_WEAKEN BITFLAG(6) +#define ALLERGEN_REACTION_BLURRY BITFLAG(7) +#define ALLERGEN_REACTION_SLEEPY BITFLAG(8) +#define ALLERGEN_REACTION_CONFUSE BITFLAG(9) diff --git a/code/__defines/reagent_data_fields.dm b/code/__defines/reagent_data_fields.dm new file mode 100644 index 00000000000..0caef07d225 --- /dev/null +++ b/code/__defines/reagent_data_fields.dm @@ -0,0 +1,64 @@ +// Currently just used for indexing reagent data without +// having to use strings. UID is provided for serde. + +/decl/reagent_data_field + abstract_type = /decl/reagent_data_field + decl_flags = DECL_FLAG_MANDATORY_UID + +/decl/reagent_data_field/taste + uid = "rdf_taste" + +/decl/reagent_data_field/ingredient_list + uid = "rdf_ingredient_list" + +/decl/reagent_data_field/ingredient_flags + uid = "rdf_ingredient_flags" + +/decl/reagent_data_field/mask_color + uid = "rdf_mask_color" + +/// An extra colour used for things like additional reagent overlays on soups, so that you can have noodle soup with veggie bits a different colour than the main soup. +/decl/reagent_data_field/extra_color + uid = "rdf_extra_color" + +/decl/reagent_data_field/mask_name + uid = "rdf_mask_name" + +/decl/reagent_data_field/milk_donor + uid = "rdf_milk_donor" + +/decl/reagent_data_field/milk_name + uid = "rdf_milk_name" + +/decl/reagent_data_field/milk_color + uid = "rdf_milk_color" + +/decl/reagent_data_field/cheese_name + uid = "rdf_cheese_name" + +/decl/reagent_data_field/cheese_color + uid = "rdf_cheese_color" + +/decl/reagent_data_field/blood_dna + uid = "rdf_blood_dna" + +/decl/reagent_data_field/blood_donor + uid = "rdf_blood_donor" + +/decl/reagent_data_field/blood_species + uid = "rdf_blood_species" + +/decl/reagent_data_field/blood_color + uid = "rdf_blood_color" + +/decl/reagent_data_field/blood_type + uid = "rdf_blood_type" + +/decl/reagent_data_field/blood_trace_chem + uid = "rdf_blood_trace_chem" + +/decl/reagent_data_field/blood_dose_chem + uid = "rdf_blood_dose_chem" + +/decl/reagent_data_field/blood_has_oxy + uid = "rdf_blood_has_oxy" diff --git a/code/__defines/research.dm b/code/__defines/research.dm index 660fb76bb1c..3898d8dc295 100644 --- a/code/__defines/research.dm +++ b/code/__defines/research.dm @@ -19,8 +19,10 @@ #define HOLLOW_OBJECT_MATTER_MULTIPLIER 0.05 #define BASE_OBJECT_MATTER_MULTPLIER 0.25 -#define GENERIC_SMELTING_HEAT_POINT 1000 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" #define TECH_ENGINEERING "engineering" #define TECH_EXOTIC_MATTER "exoticmatter" diff --git a/code/__defines/shuttle.dm b/code/__defines/shuttle.dm index e9f48230b06..8b28b098294 100644 --- a/code/__defines/shuttle.dm +++ b/code/__defines/shuttle.dm @@ -1,16 +1,17 @@ #define SHUTTLE_FLAGS_NONE 0 -#define SHUTTLE_FLAGS_PROCESS 1 -#define SHUTTLE_FLAGS_SUPPLY 2 -#define SHUTTLE_FLAGS_ZERO_G 4 -#define SHUTTLE_FLAGS_NO_CODE 8 // Essentially bypasses docking codes by extracting them from the target docking controller. Only relevant on /autodock. +#define SHUTTLE_FLAGS_PROCESS BITFLAG(0) +#define SHUTTLE_FLAGS_SUPPLY BITFLAG(1) +#define SHUTTLE_FLAGS_ZERO_G BITFLAG(2) +#define SHUTTLE_FLAGS_NO_CODE BITFLAG(3) // Essentially bypasses docking codes by extracting them from the target docking controller. Only relevant on /autodock. #define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE) -#define SLANDMARK_FLAG_AUTOSET 1 // If set, will set base area and turf type to same as where it was spawned at. -#define SLANDMARK_FLAG_ZERO_G 2 // Zero-G shuttles moved here will lose gravity unless the area has ambient gravity. -#define SLANDMARK_FLAG_DISCONNECTED 4 // Landable ships that land here will be forceably removed if the sector moves out of range. +#define SLANDMARK_FLAG_AUTOSET BITFLAG(0) // If set, will set base area and turf type to same as where it was spawned at. +#define SLANDMARK_FLAG_ZERO_G BITFLAG(1) // Zero-G shuttles moved here will lose gravity unless the area has ambient gravity. +#define SLANDMARK_FLAG_DISCONNECTED BITFLAG(2) // Landable ships that land here will be forceably removed if the sector moves out of range. +#define SLANDMARK_FLAG_REORIENT BITFLAG(3) // Shuttles that land here will be reoriented to face the correct dir for docking. //Overmap landable shuttles -#define SHIP_STATUS_LANDED 1 -#define SHIP_STATUS_TRANSIT 2 -#define SHIP_STATUS_OVERMAP 3 -#define SHIP_STATUS_ENCOUNTER 4 \ No newline at end of file +#define SHIP_STATUS_LANDED BITFLAG(0) +#define SHIP_STATUS_TRANSIT BITFLAG(1) +#define SHIP_STATUS_OVERMAP BITFLAG(2) +#define SHIP_STATUS_ENCOUNTER BITFLAG(3) \ No newline at end of file diff --git a/code/__defines/skills.dm b/code/__defines/skills.dm index 98d8abec4a9..ef7fcdd8ea4 100644 --- a/code/__defines/skills.dm +++ b/code/__defines/skills.dm @@ -15,24 +15,24 @@ #define SKILL_AVERAGE 2 #define SKILL_HARD 4 -#define SKILL_LITERACY /decl/hierarchy/skill/organizational/literacy -#define SKILL_FINANCE /decl/hierarchy/skill/organizational/finance -#define SKILL_EVA /decl/hierarchy/skill/general/eva -#define SKILL_MECH /decl/hierarchy/skill/general/eva/mech -#define SKILL_PILOT /decl/hierarchy/skill/general/pilot -#define SKILL_HAULING /decl/hierarchy/skill/general/hauling -#define SKILL_COMPUTER /decl/hierarchy/skill/general/computer -#define SKILL_BOTANY /decl/hierarchy/skill/service/botany -#define SKILL_COOKING /decl/hierarchy/skill/service/cooking -#define SKILL_COMBAT /decl/hierarchy/skill/security/combat -#define SKILL_WEAPONS /decl/hierarchy/skill/security/weapons -#define SKILL_FORENSICS /decl/hierarchy/skill/security/forensics -#define SKILL_CONSTRUCTION /decl/hierarchy/skill/engineering/construction -#define SKILL_ELECTRICAL /decl/hierarchy/skill/engineering/electrical -#define SKILL_ATMOS /decl/hierarchy/skill/engineering/atmos -#define SKILL_ENGINES /decl/hierarchy/skill/engineering/engines -#define SKILL_DEVICES /decl/hierarchy/skill/research/devices -#define SKILL_SCIENCE /decl/hierarchy/skill/research/science -#define SKILL_MEDICAL /decl/hierarchy/skill/medical/medical -#define SKILL_ANATOMY /decl/hierarchy/skill/medical/anatomy -#define SKILL_CHEMISTRY /decl/hierarchy/skill/medical/chemistry \ No newline at end of file +#define SKILL_LITERACY /decl/skill/literacy +#define SKILL_FINANCE /decl/skill/finance +#define SKILL_EVA /decl/skill/general/eva +#define SKILL_MECH /decl/skill/general/mech +#define SKILL_PILOT /decl/skill/general/pilot +#define SKILL_HAULING /decl/skill/general/hauling +#define SKILL_COMPUTER /decl/skill/general/computer +#define SKILL_BOTANY /decl/skill/service/botany +#define SKILL_COOKING /decl/skill/service/cooking +#define SKILL_COMBAT /decl/skill/combat +#define SKILL_WEAPONS /decl/skill/weapons +#define SKILL_FORENSICS /decl/skill/forensics +#define SKILL_CONSTRUCTION /decl/skill/construction +#define SKILL_ELECTRICAL /decl/skill/electrical +#define SKILL_ATMOS /decl/skill/atmos +#define SKILL_ENGINES /decl/skill/engines +#define SKILL_DEVICES /decl/skill/devices +#define SKILL_SCIENCE /decl/skill/science +#define SKILL_MEDICAL /decl/skill/medicine/medical +#define SKILL_ANATOMY /decl/skill/medicine/anatomy +#define SKILL_CHEMISTRY /decl/skill/medicine/chemistry \ No newline at end of file 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/temperature.dm b/code/__defines/temperature.dm index fd2a6698060..8258b1001f6 100644 --- a/code/__defines/temperature.dm +++ b/code/__defines/temperature.dm @@ -1,4 +1,4 @@ -#define ATOM_IS_TEMPERATURE_SENSITIVE(A) (A?.simulated) +#define ATOM_IS_TEMPERATURE_SENSITIVE(A) (istype(A) && A.simulated && A.temperature_sensitive) #define ATOM_SHOULD_TEMPERATURE_ENQUEUE(A) (ATOM_IS_TEMPERATURE_SENSITIVE(A) && !QDELETED(A)) #define ATOM_TEMPERATURE_EQUILIBRIUM_THRESHOLD 5 #define MIN_TEMPERATURE_COEFFICIENT 1 diff --git a/code/__defines/tools.dm b/code/__defines/tools.dm index 30acb8f6422..3caa322b023 100644 --- a/code/__defines/tools.dm +++ b/code/__defines/tools.dm @@ -15,6 +15,8 @@ // Misc tools. #define TOOL_PEN /decl/tool_archetype/pen +#define TOOL_STAMP /decl/tool_archetype/stamp +#define TOOL_SHEARS /decl/tool_archetype/shears // Surgical tools. #define TOOL_SCALPEL /decl/tool_archetype/scalpel @@ -85,6 +87,7 @@ #define TOOL_PROP_COLOR_NAME "color_name" //Property containing a color name for some tools. Namely the pen tool. #define TOOL_PROP_COLOR "color" //Property for specifying a color, for something like a pen. #define TOOL_PROP_USES "uses_left" //Property for things that have a fixed amount of uses. -1 is unlimited. +#define TOOL_PROP_EMPTY_MESSAGE "empty_msg" //The message given on depletion when a tool runs out of charges. //Pen specific stuff #define TOOL_PROP_PEN_FLAG "pen_flag" //Property for pens to specify additional properties about themselves diff --git a/code/__defines/traits.dm b/code/__defines/traits.dm index f55c54f73c9..2404c521bc1 100644 --- a/code/__defines/traits.dm +++ b/code/__defines/traits.dm @@ -3,59 +3,70 @@ #define TRAIT_LEVEL_MODERATE 2 #define TRAIT_LEVEL_MAJOR 3 -#define DEFINE_ROBOLIMB_MODEL_TRAITS(MODEL_PATH, MODEL_ID, COST) \ -/decl/trait/prosthetic_limb/left_hand/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/left_hand; \ - trait_cost = COST * 0.5; \ -} \ -/decl/trait/prosthetic_limb/left_arm/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/left_arm; \ - trait_cost = COST; \ -} \ -/decl/trait/prosthetic_limb/right_hand/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/right_hand; \ - trait_cost = COST * 0.5; \ -} \ -/decl/trait/prosthetic_limb/right_arm/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/right_arm; \ - trait_cost = COST; \ -} \ -/decl/trait/prosthetic_limb/left_foot/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/left_foot; \ - trait_cost = COST * 0.5; \ -} \ -/decl/trait/prosthetic_limb/left_leg/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/left_leg; \ - trait_cost = COST; \ -} \ -/decl/trait/prosthetic_limb/right_foot/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/right_foot; \ - trait_cost = COST * 0.5; \ -} \ -/decl/trait/prosthetic_limb/right_leg/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/right_leg; \ - trait_cost = COST; \ -} \ -/decl/trait/prosthetic_limb/head/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/head; \ - trait_cost = COST * 0.5; \ -} \ -/decl/trait/prosthetic_limb/chest/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/chest; \ - trait_cost = COST * 0.5; \ -} \ -/decl/trait/prosthetic_limb/groin/##MODEL_ID { \ - model = MODEL_PATH; \ - parent = /decl/trait/prosthetic_limb/groin; \ - trait_cost = COST * 0.5; \ +#define DEFINE_ROBOLIMB_MODEL_TRAITS(MODEL_PATH, MODEL_ID, COST, MODEL_STRING) \ +/decl/trait/prosthetic_limb/left_hand/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/left_hand; \ + trait_cost = COST * 0.5; \ + uid = "trait_prosthetic_left_hand_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/left_arm/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/left_arm; \ + trait_cost = COST; \ + uid = "trait_prosthetic_left_arm_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/right_hand/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/right_hand; \ + trait_cost = COST * 0.5; \ + uid = "trait_prosthetic_right_hand_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/right_arm/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/right_arm; \ + trait_cost = COST; \ + uid = "trait_prosthetic_right_arm_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/left_foot/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/left_foot; \ + trait_cost = COST * 0.5; \ + uid = "trait_prosthetic_left_foot_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/left_leg/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/left_leg; \ + trait_cost = COST; \ + uid = "trait_prosthetic_left_leg_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/right_foot/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/right_foot; \ + trait_cost = COST * 0.5; \ + uid = "trait_prosthetic_right_foot_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/right_leg/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/right_leg; \ + trait_cost = COST; \ + uid = "trait_prosthetic_right_leg_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/head/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/head; \ + trait_cost = COST * 0.5; \ + uid = "trait_prosthetic_head_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/chest/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/chest; \ + trait_cost = COST * 0.5; \ + uid = "trait_prosthetic_chest_" + MODEL_STRING; \ +} \ +/decl/trait/prosthetic_limb/groin/##MODEL_ID { \ + model = MODEL_PATH; \ + parent = /decl/trait/prosthetic_limb/groin; \ + trait_cost = COST * 0.5; \ + uid = "trait_prosthetic_groin_" + MODEL_STRING; \ } diff --git a/code/__defines/turfs.dm b/code/__defines/turfs.dm index 2471b56efbd..6c46f5f1a9f 100644 --- a/code/__defines/turfs.dm +++ b/code/__defines/turfs.dm @@ -2,12 +2,10 @@ #define TURF_REMOVE_SCREWDRIVER BITFLAG(1) #define TURF_REMOVE_SHOVEL BITFLAG(2) #define TURF_REMOVE_WRENCH BITFLAG(3) -#define TURF_CAN_BREAK BITFLAG(4) -#define TURF_CAN_BURN BITFLAG(5) -#define TURF_IS_FRAGILE BITFLAG(6) -#define TURF_ACID_IMMUNE BITFLAG(7) -#define TURF_IS_WET BITFLAG(8) -#define TURF_HAS_RANDOM_BORDER BITFLAG(9) +#define TURF_IS_FRAGILE BITFLAG(4) +#define TURF_ACID_IMMUNE BITFLAG(5) +#define TURF_IS_WET BITFLAG(6) +#define TURF_HAS_RANDOM_BORDER BITFLAG(7) //Used for floor/wall smoothing #define SMOOTH_NONE 0 //Smooth only with itself @@ -15,14 +13,14 @@ #define SMOOTH_WHITELIST 2 //Smooth with a whitelist of subtypes #define SMOOTH_BLACKLIST 3 //Smooth with all but a blacklist of subtypes -#define RANGE_TURFS(CENTER, RADIUS) block(locate(max(CENTER.x-(RADIUS), 1), max(CENTER.y-(RADIUS),1), CENTER.z), locate(min(CENTER.x+(RADIUS), world.maxx), min(CENTER.y+(RADIUS), world.maxy), CENTER.z)) -#define BLOCK_TURFS(X1, Y1, X2, Y2, Z) block(locate(X1, Y1, Z), locate(X2, Y2, Z)) +#define RANGE_TURFS(CENTER, RADIUS) block(max(CENTER.x-(RADIUS), 1), max(CENTER.y-(RADIUS),1), CENTER.z, min(CENTER.x+(RADIUS), world.maxx), min(CENTER.y+(RADIUS), world.maxy), CENTER.z) +#define Z_ALL_TURFS(Z) block(1, 1, Z, world.maxx, world.maxy) //Here are a few macros to help with people always forgetting to round the coordinates somewhere, and forgetting that not everything automatically rounds decimals. ///Helper macro for the x coordinate of the turf at the center of the world. Handles rounding. -#define WORLD_CENTER_X CEILING((1 + world.maxx) / 2) +#define WORLD_CENTER_X ceil((1 + world.maxx) / 2) ///Helper macro for the y coordinate of the turf at the center of the world. Handles rounding. -#define WORLD_CENTER_Y CEILING((1 + world.maxy) / 2) +#define WORLD_CENTER_Y ceil((1 + world.maxy) / 2) ///Helper macro for getting the center turf on a given z-level. Handles rounding. #define WORLD_CENTER_TURF(Z) locate(WORLD_CENTER_X, WORLD_CENTER_Y, Z) ///Helper macro to check if a position is within the world's bounds. @@ -30,18 +28,20 @@ ///Helper macro for printing to text the world's x,y,z size to a string. #define WORLD_SIZE_TO_STRING "[world.maxx]x[world.maxy]x[world.maxz]" -#define EXT_LAYER_CONSTANT 0.001 -#define EXT_EDGE_OCEAN (10 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_SEAFLOOR (11 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_VOLCANIC (12 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_DIRT (20 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_BARREN (21 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_CLAY (21 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_MUD (22 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_SAND (30 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_CHLORINE_SAND (31 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_WATER (40 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_GRASS (51 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_PATH (52 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_GRASS_WILD (53 * EXT_LAYER_CONSTANT) -#define EXT_EDGE_SNOW (60 * EXT_LAYER_CONSTANT) +#define FLOOR_EDGE_NONE -1 +#define FLOOR_LAYER_CONSTANT 0.001 +#define FLOOR_LAYER_OCEAN (10 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_SEAFLOOR (11 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_VOLCANIC (12 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_DIRT (20 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_BARREN (21 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_CLAY (21 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_MUD (22 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_SAND (30 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_CHLORINE_SAND (50 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_WATER (50 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_GRASS (55 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_PATH (60 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_GRASS_WILD (65 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_SNOW (70 * FLOOR_LAYER_CONSTANT) +#define FLOOR_EDGE_CARPET (75 * FLOOR_LAYER_CONSTANT) diff --git a/code/__defines/zmimic.dm b/code/__defines/zmimic.dm index 71982547c3e..a9af1b126f5 100644 --- a/code/__defines/zmimic.dm +++ b/code/__defines/zmimic.dm @@ -52,7 +52,7 @@ var/global/list/mimic_defines = list( ) // Movable flags. -#define ZMM_IGNORE 1 //! Do not copy this movable. -#define ZMM_MANGLE_PLANES 2 //! Check this movable's overlays/underlays for explicit plane use and mangle for compatibility with Z-Mimic. If you're using emissive overlays, you probably should be using this flag. Expensive, only use if necessary. -#define ZMM_LOOKAHEAD 3 //! Look one turf ahead and one turf back when considering z-turfs that might be seeing this atom. Cheap, but not free. -#define ZMM_LOOKBESIDE 4 //! Look one turf beside (left/right) when considering z-turfs that might be seeing this atom. Cheap, but not free. +#define ZMM_IGNORE BITFLAG(0) //! Do not copy this movable. +#define ZMM_MANGLE_PLANES BITFLAG(1) //! Check this movable's overlays/underlays for explicit plane use and mangle for compatibility with Z-Mimic. If you're using emissive overlays, you probably should be using this flag. Expensive, only use if necessary. +#define ZMM_LOOKAHEAD BITFLAG(2) //! Look one turf ahead and one turf back when considering z-turfs that might be seeing this atom. Cheap, but not free. +#define ZMM_LOOKBESIDE BITFLAG(3) //! Look one turf beside (left/right) when considering z-turfs that might be seeing this atom. Cheap, but not free. diff --git a/code/_global_vars/lists/flavor.dm b/code/_global_vars/lists/flavor.dm index 2b77650f6ba..b2096edc0a4 100644 --- a/code/_global_vars/lists/flavor.dm +++ b/code/_global_vars/lists/flavor.dm @@ -130,6 +130,6 @@ GLOBAL_GETTER(cable_colors, /list, SetupCableColors()) var/obj/item/stack/cable_coil/C = coil_type if(!initial(C.can_have_color)) continue - var/color = initial(C.color) + var/color = initial(C.paint_color) || initial(C.color) .[name] = color . = sortTim(., /proc/cmp_text_asc) diff --git a/code/_global_vars/sound.dm b/code/_global_vars/sound.dm index 4b42260aa53..9525f1b0b2b 100644 --- a/code/_global_vars/sound.dm +++ b/code/_global_vars/sound.dm @@ -113,8 +113,18 @@ var/global/list/light_strike_sound = list( 'sound/effects/hit_kick.ogg', 'sound/effects/hit_punch.ogg' ) - + var/global/list/tray_hit_sound = list( 'sound/items/trayhit1.ogg', 'sound/items/trayhit2.ogg' +) + +var/global/list/sweeping_sound = list( + 'sound/foley/sweeping1.ogg', + 'sound/foley/sweeping2.ogg', + 'sound/foley/sweeping3.ogg', + 'sound/foley/sweeping4.ogg', + 'sound/foley/sweeping5.ogg', + 'sound/foley/sweeping6.ogg', + 'sound/foley/sweeping7.ogg', ) \ No newline at end of file diff --git a/code/_helpers/_global_objects.dm b/code/_helpers/_global_objects.dm index 8d6c51ec88a..705aab49b27 100644 --- a/code/_helpers/_global_objects.dm +++ b/code/_helpers/_global_objects.dm @@ -2,3 +2,8 @@ var/global/datum/gear_tweak/color/gear_tweak_free_color_choice_ /proc/gear_tweak_free_color_choice() if(!gear_tweak_free_color_choice_) gear_tweak_free_color_choice_ = new() return gear_tweak_free_color_choice_ + +var/global/datum/gear_tweak/color/markings/gear_tweek_free_markings_color_choice_ +/proc/gear_tweak_free_markings_color_choice() + if(!gear_tweek_free_markings_color_choice_) gear_tweek_free_markings_color_choice_ = new() + return gear_tweek_free_markings_color_choice_ \ No newline at end of file diff --git a/code/_helpers/animations.dm b/code/_helpers/animations.dm index b6fcccda02d..be0d2188f5a 100644 --- a/code/_helpers/animations.dm +++ b/code/_helpers/animations.dm @@ -81,22 +81,11 @@ var/segment = 360/segments if(!clockwise) segment = -segment - var/list/matrices = list() - for(var/i in 1 to segments-1) - var/matrix/M = matrix(transform) - M.Turn(segment*i) - matrices += M - var/matrix/last = matrix(transform) - matrices += last - speed /= segments - if(parallel) - animate(src, transform = matrices[1], time = speed, loops , flags = ANIMATION_PARALLEL) - else - animate(src, transform = matrices[1], time = speed, loops) + animate(src, transform = matrix().Turn(segment), time = speed, loops, flags = parallel ? (ANIMATION_PARALLEL | ANIMATION_RELATIVE) : ANIMATION_RELATIVE) for(var/i in 2 to segments) //2 because 1 is covered above - animate(transform = matrices[i], time = speed) + animate(transform = matrix().Turn(segment), time = speed, loops, flags = ANIMATION_RELATIVE) //doesn't have an object argument because this is "Stacking" with the animate call above //3 billion% intentional diff --git a/code/_helpers/cmp.dm b/code/_helpers/cmp.dm index a02abae6309..e84ecc341fc 100644 --- a/code/_helpers/cmp.dm +++ b/code/_helpers/cmp.dm @@ -139,3 +139,19 @@ /proc/cmp_inventory_slot_desc(datum/inventory_slot/a, datum/inventory_slot/b) return b.quick_equip_priority - a.quick_equip_priority + +/proc/cmp_skill_category_asc(decl/skill_category/a, decl/skill_category/b) + return a.sort_priority - b.sort_priority + +/proc/cmp_skill_asc(decl/skill/a, decl/skill/b) + if(length(a.prerequisites)) + var/decl/skill/prerequisite = GET_DECL(a.prerequisites[1]) + if(b == prerequisite) + return 1 // goes before + return cmp_skill_asc(GET_DECL(a.prerequisites[1]), b) + if(length(b.prerequisites)) + var/decl/skill/prerequisite = GET_DECL(b.prerequisites[1]) + if(a == prerequisite) + return -1 // goes after + return cmp_skill_asc(a, GET_DECL(b.prerequisites[1])) + return cmp_name_or_type_asc(a, b) \ No newline at end of file diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 4f56b508d6c..c33ecb8a33a 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -108,7 +108,7 @@ return dist /proc/get_dist_bounds(var/target, var/source) // Alternative to get_dist for multi-turf objects - return CEILING(bounds_dist(target, source)/world.icon_size) + 1 + return ceil(bounds_dist(target, source)/world.icon_size) + 1 /proc/circlerangeturfs(center=usr,radius=3) var/turf/centerturf = get_turf(center) @@ -172,7 +172,6 @@ return L // Returns a list of mobs and/or objects in range of R from source. Used in radio and say code. - /proc/get_mobs_or_objects_in_view(var/R, var/atom/source, var/include_mobs = 1, var/include_objects = 1) var/turf/T = get_turf(source) @@ -195,28 +194,24 @@ hear += I return hear -/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/list/mobs, var/list/objs, var/check_ghosts = null) - var/list/hear = list() - DVIEW(hear, range, T, INVISIBILITY_MAXIMUM) +// Alternative to get_mobs_or_objects_in_view which only considers mobs and "listening" objects. +/proc/get_listeners_in_range(turf/center, range, list/mobs, list/objs, check_ghosts=FALSE) var/list/hearturfs = list() - - for(var/atom/movable/AM in hear) - if(ismob(AM)) - mobs += AM - hearturfs += get_turf(AM) - else if(isobj(AM)) - objs += AM - hearturfs += get_turf(AM) + FOR_DVIEW(var/turf/T, range, center, INVISIBILITY_MAXIMUM) + hearturfs[T] = TRUE + for(var/mob/M in T) + mobs += M + END_FOR_DVIEW for(var/mob/M in global.player_list) if(check_ghosts && M.stat == DEAD && M.get_preference_value(check_ghosts) != PREF_NEARBY) mobs |= M - else if(get_turf(M) in hearturfs) + else if(hearturfs[get_turf(M)]) mobs |= M for(var/obj/O in global.listening_objects) - if(get_turf(O) in hearturfs) - objs |= O + if(hearturfs[get_turf(O)]) + objs += O @@ -228,7 +223,7 @@ if(Y1==Y2) return 1 //Light cannot be blocked on same tile else - var/s = SIMPLE_SIGN(Y2-Y1) + var/s = SIGN(Y2-Y1) Y1+=s while(Y1!=Y2) T=locate(X1,Y1,Z) @@ -238,8 +233,8 @@ else var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles - var/signX = SIMPLE_SIGN(X2-X1) - var/signY = SIMPLE_SIGN(Y2-Y1) + var/signX = SIGN(X2-X1) + var/signY = SIGN(Y2-Y1) if(X1= min) && (val <= max) /proc/IsInteger(x) - return FLOOR(x) == x + return floor(x) == x /proc/IsMultiple(x, y) return x % y == 0 @@ -91,7 +91,7 @@ /proc/polar2turf(x, y, z, angle, distance) var/x_offset = POLAR_TO_BYOND_X(distance, angle) var/y_offset = POLAR_TO_BYOND_Y(distance, angle) - return locate(CEILING(x + x_offset), CEILING(y + y_offset), z) + return locate(ceil(x + x_offset), ceil(y + y_offset), z) /proc/get_turf_from_angle(x, y, z, angle, ideal_distance) do diff --git a/code/_helpers/medical_scans.dm b/code/_helpers/medical_scans.dm index c48ff1b6028..99468c41c8c 100644 --- a/code/_helpers/medical_scans.dm +++ b/code/_helpers/medical_scans.dm @@ -1,64 +1,66 @@ -/mob/living/human/proc/get_raw_medical_data(var/tag = FALSE) - var/mob/living/human/H = src - var/list/scan = list() - - scan["name"] = H.name - scan["time"] = stationtime2text() +/mob/living/proc/get_raw_medical_data(var/tag = FALSE) + . = list() + .["name"] = name + .["time"] = stationtime2text() var/brain_result - if(H.should_have_organ(BP_BRAIN)) - var/obj/item/organ/internal/brain = GET_INTERNAL_ORGAN(H, BP_BRAIN) - if(!brain || H.stat == DEAD || (H.status_flags & FAKEDEATH)) + if(should_have_organ(BP_BRAIN)) + var/obj/item/organ/internal/brain = GET_INTERNAL_ORGAN(src, BP_BRAIN) + if(!brain || stat == DEAD || (status_flags & FAKEDEATH)) brain_result = 0 - else if(H.stat != DEAD) + else if(stat != DEAD) brain_result = round(max(0,(1 - brain.damage/brain.max_damage)*100)) else brain_result = -1 - scan["brain_activity"] = brain_result + .["brain_activity"] = brain_result var/pulse_result - if(H.should_have_organ(BP_HEART)) - var/obj/item/organ/internal/heart/heart = H.get_organ(BP_HEART, /obj/item/organ/internal/heart) + if(should_have_organ(BP_HEART)) + var/obj/item/organ/internal/heart/heart = get_organ(BP_HEART, /obj/item/organ/internal/heart) if(!heart) pulse_result = 0 else if(BP_IS_PROSTHETIC(heart)) pulse_result = -2 - else if(H.status_flags & FAKEDEATH) + else if(status_flags & FAKEDEATH) pulse_result = 0 else - pulse_result = H.get_pulse_as_string(GETPULSE_TOOL) + pulse_result = get_pulse_as_string(GETPULSE_TOOL) else pulse_result = -1 if(pulse_result == ">250") pulse_result = -3 - scan["pulse"] = text2num(pulse_result) - - scan["blood_pressure"] = H.get_blood_pressure() - scan["blood_o2"] = H.get_blood_oxygenation() - scan["blood_volume"] = H.vessel.total_volume - scan["blood_volume_max"] = H.vessel.maximum_volume - scan["temperature"] = H.bodytemperature - scan["trauma"] = H.get_damage(BRUTE) - scan["burn"] = H.get_damage(BURN) - scan["toxin"] = H.get_damage(TOX) - scan["oxygen"] = H.get_damage(OXY) - scan["radiation"] = H.radiation - scan["genetic"] = H.get_damage(CLONE) - scan["paralysis"] = GET_STATUS(H, STAT_PARA) - scan["immune_system"] = H.get_immunity() - scan["reagents"] = list() - - if(H.reagents?.total_volume) - for(var/reagent_type in H.reagents.reagent_volumes) - var/decl/material/R = GET_DECL(reagent_type) + .["pulse"] = text2num(pulse_result) + + .["temperature"] = bodytemperature + .["trauma"] = get_damage(BRUTE) + .["burn"] = get_damage(BURN) + .["toxin"] = get_damage(TOX) + .["oxygen"] = get_damage(OXY) + .["radiation"] = radiation + .["genetic"] = get_damage(CLONE) + .["paralysis"] = GET_STATUS(src, STAT_PARA) + .["immune_system"] = get_immunity() + .["reagents"] = list() + + if(reagents?.total_volume) + for(var/liquid_type in reagents.liquid_volumes) + var/decl/material/R = GET_DECL(liquid_type) + var/list/reagent = list() + reagent["name"]= R.get_reagent_name(reagents, MAT_PHASE_LIQUID) + reagent["quantity"] = round(REAGENT_VOLUME(reagents, R.type),1) + reagent["scannable"] = R.scannable + .["reagents"] += list(reagent) + + for(var/solid_type in reagents.solid_volumes) + var/decl/material/R = GET_DECL(solid_type) var/list/reagent = list() - reagent["name"]= R.get_reagent_name(H.reagents) - reagent["quantity"] = round(REAGENT_VOLUME(H.reagents, R.type),1) + reagent["name"]= R.get_reagent_name(reagents, MAT_PHASE_SOLID) + reagent["quantity"] = round(REAGENT_VOLUME(reagents, R.type),1) reagent["scannable"] = R.scannable - scan["reagents"] += list(reagent) + .["reagents"] += list(reagent) - scan["external_organs"] = list() - for(var/obj/item/organ/external/E in H.get_external_organs()) + .["external_organs"] = list() + for(var/obj/item/organ/external/E in get_external_organs()) var/list/O = list() O["name"] = E.name O["brute_ratio"] = E.brute_ratio @@ -69,10 +71,10 @@ O["scan_results"] = E.get_scan_results(tag) O["tumors"] = E.has_growths() O["ailments"] = E.has_diagnosable_ailments(scanner = TRUE) - scan["external_organs"] += list(O) + .["external_organs"] += list(O) - scan["internal_organs"] = list() - var/list/internal_organs = H.get_internal_organs() + .["internal_organs"] = list() + var/list/internal_organs = get_internal_organs() for(var/obj/item/organ/internal/I in internal_organs) var/list/O = list() O["name"] = I.name @@ -81,18 +83,25 @@ O["is_damaged"] = I.damage > 0 O["scan_results"] = I.get_scan_results(tag) O["ailments"] = I.has_diagnosable_ailments(scanner = TRUE) - scan["internal_organs"] += list(O) + .["internal_organs"] += list(O) - scan["missing_organs"] = list() + .["missing_organs"] = list() var/decl/bodytype/root_bodytype = get_bodytype() for(var/organ_name in root_bodytype.has_organ) - if(!GET_INTERNAL_ORGAN(H, organ_name)) - scan["missing_organs"] += organ_name - if(H.has_genetic_condition(GENE_COND_BLINDED)) - scan["blind"] = TRUE - if(H.has_genetic_condition(GENE_COND_NEARSIGHTED)) - scan["nearsight"] = TRUE - return scan + if(!GET_INTERNAL_ORGAN(src, organ_name)) + .["missing_organs"] += organ_name + if(has_genetic_condition(GENE_COND_BLINDED)) + .["blind"] = TRUE + if(has_genetic_condition(GENE_COND_NEARSIGHTED)) + .["nearsight"] = TRUE + +/mob/living/human/get_raw_medical_data(var/tag = FALSE) + . = ..() + .["blood_pressure"] = get_blood_pressure() + .["blood_o2"] = get_blood_oxygenation() + if(vessel) + .["blood_volume"] = vessel.total_volume + .["blood_volume_max"] = vessel.maximum_volume /proc/display_medical_data_header(var/list/scan, skill_level = SKILL_DEFAULT) //In case of problems, abort. diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 2be61d45948..6c040f5a57c 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -11,9 +11,9 @@ if(species) var/decl/species/current_species = get_species_by_key(species) if(current_species) - var/decl/cultural_info/current_culture = GET_DECL(current_species.default_cultural_info[TAG_CULTURE]) - if(current_culture) - return current_culture.get_random_name(null, gender) + var/decl/background_detail/background = current_species.get_default_background_datum_by_flag(BACKGROUND_FLAG_NAMING) + if(background) + return background.get_random_name(null, gender) return capitalize(pick(gender == FEMALE ? global.using_map.first_names_female : global.using_map.first_names_male)) + " " + capitalize(pick(global.using_map.last_names)) /proc/random_skin_tone(var/decl/bodytype/current_bodytype) @@ -40,7 +40,7 @@ var/user_loc = user.loc var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) + if(user.is_space_movement_permitted() == SPACE_MOVE_FORBIDDEN && user.inertia_dir) drifting = 1 var/target_loc = target.loc @@ -101,7 +101,7 @@ var/atom/original_loc = user.loc var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) + if(user.is_space_movement_permitted() == SPACE_MOVE_FORBIDDEN && user.inertia_dir) drifting = 1 var/holding = user.get_active_held_item() @@ -241,7 +241,7 @@ return val if(istext(val)) var/list/vals = splittext(val, "x") - return FLOOR(max(text2num(vals[1]), text2num(vals[2]))/2) + return floor(max(text2num(vals[1]), text2num(vals[2]))/2) return 0 // If all of these flags are present, it should come out at exactly 1. Yes, this diff --git a/code/_helpers/storage.dm b/code/_helpers/storage.dm index 2d415a55ac5..5437f13c60d 100644 --- a/code/_helpers/storage.dm +++ b/code/_helpers/storage.dm @@ -20,7 +20,7 @@ /datum/atom_creator/simple var/path var/probability - var/prob_method = /proc/prob_call + var/prob_method = GLOBAL_PROC_REF(prob_call) /datum/atom_creator/simple/New(var/path, var/probability) if(args.len != 2) @@ -36,7 +36,7 @@ /datum/atom_creator/weighted var/list/paths - var/selection_method = /proc/pickweight + var/selection_method = GLOBAL_PROC_REF(pickweight) /datum/atom_creator/weighted/New(var/list/paths) if(args.len != 1) diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index cc444597aa2..e25429b8fe5 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -58,6 +58,14 @@ // https://en.wikipedia.org/wiki/Whitespace_character#Unicode var/static/regex/unicode_control_chars = regex(@"[\u0001-\u0009\u000B\u000C\u000E-\u001F\u007F\u0080-\u009F\u00A0\u1680\u180E\u2000-\u200D\u2028\u2029\u202F\u205F\u2060\u3000\uFEFF]", "g") input = unicode_control_chars.Replace(input, "") + // Allows at most one Unicode combining diacritical mark in a row + // Codes acquired from https://en.wikipedia.org/wiki/Combining_Diacritical_Marks + // https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_Extended + // https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_Supplement + // https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols + // https://en.wikipedia.org/wiki/Combining_Half_Marks + var/static/regex/unicode_diacritical_marks = regex(@"([\u0300-\u036F\u1AB0-\u1ACE\u1DC0-\u1DFF\u20D0-\u20F0\uFE20-\uFE2F]){2,}", "g") + input = unicode_diacritical_marks.Replace(input, "$1") if(encode) // In addition to processing html, html_encode removes byond formatting codes like "\red", "\i" and other. diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index eea2b9b3833..464860a2392 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -89,7 +89,7 @@ var/global/round_start_time = 0 out += "[seconds] second\s" if(length(out)) return english_list(out) - return null + return "less than a second" /proc/roundduration2text() if(!round_start_time) diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index 6f3388b912b..d74b742f97a 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -81,22 +81,38 @@ //Returns an assoc list that describes how turfs would be changed if the //turfs in turfs_src were translated by shifting the src_origin to the dst_origin -/proc/get_turf_translation(turf/src_origin, turf/dst_origin, list/turfs_src) +/proc/get_turf_translation(turf/src_origin, turf/dst_origin, list/turfs_src, angle = 0) + angle = round(SIMPLIFY_DEGREES(angle), 90) // can only turn at right angles + var/adj = 1 + var/opp = 0 + switch(angle) + if(90) + adj = 0 + opp = 1 // swap the X and Y axes + if(180) + adj = -1 // flip across the axes + opp = 0 + if(270) + adj = 0 + opp = -1 // swap the X and Y axes and then flip var/list/turf_map = list() for(var/turf/source in turfs_src) - var/x_pos = (source.x - src_origin.x) - var/y_pos = (source.y - src_origin.y) - var/z_pos = (source.z - src_origin.z) + var/dx = (source.x - src_origin.x) + var/dy = (source.y - src_origin.y) + var/dz = (source.z - src_origin.z) + var/x_pos = dst_origin.x + dx * adj + dy * opp + var/y_pos = dst_origin.y + dy * adj - dx * opp // y-axis is flipped in BYOND :( + var/z_pos = dst_origin.z + dz - var/turf/target = locate(dst_origin.x + x_pos, dst_origin.y + y_pos, dst_origin.z + z_pos) + var/turf/target = locate(x_pos, y_pos, z_pos) if(!target) - error("Null turf in translation @ ([dst_origin.x + x_pos], [dst_origin.y + y_pos], [dst_origin.z + z_pos])") + error("Null turf in translation @ ([x_pos], [y_pos], [z_pos])") turf_map[source] = target //if target is null, preserve that information in the turf map return turf_map -/proc/translate_turfs(var/list/translation, var/area/base_area = null, var/turf/base_turf, var/ignore_background, var/translate_air) +/proc/translate_turfs(list/translation, area/base_area = null, turf/base_turf, ignore_background, translate_air, angle = 0) . = list() for(var/turf/source in translation) @@ -105,10 +121,10 @@ if(target) if(base_area) ChangeArea(target, get_area(source)) - . += transport_turf_contents(source, target, ignore_background, translate_air) + . += transport_turf_contents(source, target, ignore_background, translate_air, angle = angle) ChangeArea(source, base_area) else - . += transport_turf_contents(source, target, ignore_background, translate_air) + . += transport_turf_contents(source, target, ignore_background, translate_air, angle = angle) //change the old turfs for(var/turf/source in translation) if(ignore_background && (source.turf_flags & TURF_FLAG_BACKGROUND)) @@ -117,12 +133,51 @@ var/turf/changed = source.ChangeTurf(old_turf, keep_air = !translate_air) changed.prev_type = null +// Currently used only for shuttles. If it gets generalized rename it. +/atom/proc/shuttle_rotate(angle) + if(angle) + set_dir(SAFE_TURN(dir, angle)) + addtimer(CALLBACK(src, PROC_REF(queue_icon_update)), 1) + return TRUE + +// Adjust pixel_x, pixel_y, etc. for things without directional_offset. +// This may cause issues with things that shouldn't rotate. Those should be using pixel_w and pixel_z, preferably! +/obj/shuttle_rotate(angle) + . = ..() + if(. && isnull(directional_offset) && (pixel_x || pixel_y)) + var/adj = cos(angle) + var/opp = sin(angle) + var/old_pixel_x = pixel_x + var/old_pixel_y = pixel_y + pixel_x = adj * old_pixel_x + opp * old_pixel_y + pixel_y = adj * old_pixel_y + opp * old_pixel_x + +/obj/structure/shuttle_rotate(angle) + . = ..() + if(.) + addtimer(CALLBACK(src, PROC_REF(update_connections), TRUE), 1) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, queue_icon_update)), 1) + +/obj/machinery/network/requests_console/shuttle_rotate(angle) + . = ..(-angle) // for some reason directions are switched for these + +/obj/machinery/firealarm/shuttle_rotate(angle) + . = ..(-angle) + +/obj/machinery/door/shuttle_rotate(angle) + . = ..() + if(.) + addtimer(CALLBACK(src, PROC_REF(update_connections), TRUE), 1) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, queue_icon_update)), 1) + //Transports a turf from a source turf to a target turf, moving all of the turf's contents and making the target a copy of the source. //If ignore_background is set to true, turfs with TURF_FLAG_BACKGROUND set will only translate anchored contents. //Returns the new turf, or list(new turf, source) if a background turf was ignored and things may have been left behind. -/proc/transport_turf_contents(turf/source, turf/target, ignore_background, translate_air) +/proc/transport_turf_contents(turf/source, turf/target, ignore_background, translate_air, angle = 0) var/target_type = target.type var/turf/new_turf + // byond's coordinate system is weird so we have to invert the angle + angle = -angle var/is_background = ignore_background && (source.turf_flags & TURF_FLAG_BACKGROUND) var/supported = FALSE // Whether or not there's an object in the turf which can support other objects. @@ -131,6 +186,7 @@ else new_turf = target.ChangeTurf(source.type, 1, 1, !translate_air) new_turf.transport_properties_from(source, translate_air) + new_turf.shuttle_rotate(angle) new_turf.prev_type = target_type for(var/obj/O in source) @@ -141,6 +197,7 @@ for(var/obj/O in source) if((O.movable_flags & MOVABLE_FLAG_ALWAYS_SHUTTLEMOVE) || (O.simulated && (!is_background || supported || (O.obj_flags & OBJ_FLAG_MOVES_UNSUPPORTED)))) O.forceMove(new_turf) + O.shuttle_rotate(angle) for(var/mob/M in source) if(is_background && !supported) @@ -148,8 +205,54 @@ if(isEye(M)) continue // If we need to check for more mobs, I'll add a variable M.forceMove(new_turf) + M.shuttle_rotate(angle) if(is_background) return list(new_turf, source) return new_turf + +/proc/get_dir_z_text(turf/origin, turf/target) + + origin = get_turf(origin) + target = get_turf(target) + + if(!istype(origin) || !istype(target) || !(origin.z in SSmapping.get_connected_levels(target.z, include_lateral = TRUE))) + return "somewhere" + if(origin == target) + return "right next to you" + + var/datum/level_data/origin_level = SSmapping.levels_by_z[origin.z] + var/datum/level_data/target_level = SSmapping.levels_by_z[target.z] + + if(origin_level.z_volume_level_z < target_level.z_volume_level_z) + . += "above and to" + else if(origin_level.z_volume_level_z > target_level.z_volume_level_z) + . += "below and to" + + var/origin_x = origin.x + origin_level.z_volume_level_x + var/origin_y = origin.y + origin_level.z_volume_level_y + var/target_x = target.x + target_level.z_volume_level_x + var/target_y = target.y + target_level.z_volume_level_y + + if(origin_x == target_x) + if(origin_y > target_y) + . += "the south" + else + . += "the north" + else if(origin_y == target_y) + if(origin_x > target_x) + . += "the west" + else + . += "the east" + else + if(origin_x > target_x) + if(origin_y > target_y) + . += "the southwest" + else + . += "the northwest" + else + if(origin_y > target_y) + . += "the southeast" + else + . += "the northeast" diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm index ac224c1e62d..2cd597169bd 100644 --- a/code/_helpers/type2type.dm +++ b/code/_helpers/type2type.dm @@ -12,16 +12,6 @@ /proc/file2list(filename, seperator = "\n") return splittext(safe_file2text(filename), seperator) -// Turns a direction into text -/proc/num2dir(direction) - switch (direction) - if (1.0) return NORTH - if (2.0) return SOUTH - if (4.0) return EAST - if (8.0) return WEST - else - to_world_log("UNKNOWN DIRECTION: [direction]") - // Turns a direction into text /proc/dir2text(direction) switch (direction) @@ -40,14 +30,14 @@ // Turns text into proper directions /proc/text2dir(direction) switch (uppertext(direction)) - if ("NORTH") return 1 - if ("SOUTH") return 2 - if ("EAST") return 4 - if ("WEST") return 8 - if ("NORTHEAST") return 5 - if ("NORTHWEST") return 9 - if ("SOUTHEAST") return 6 - if ("SOUTHWEST") return 10 + if ("NORTH") return NORTH + if ("SOUTH") return SOUTH + if ("EAST") return EAST + if ("WEST") return WEST + if ("NORTHEAST") return NORTHEAST + if ("NORTHWEST") return NORTHWEST + if ("SOUTHEAST") return SOUTHEAST + if ("SOUTHWEST") return SOUTHWEST // Converts an angle (degrees) into an ss13 direction /proc/angle2dir(var/degree) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index fff0be5f4a9..8e19c11164f 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -113,7 +113,7 @@ Turf and target are seperate in case you want to teleport some distance from a t var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. //Now to find a box from center location and make that our destination. - for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) + for(var/turf/T as anything in block(center.x+b1xerror, center.y+b1yerror, location.z, center.x+b2xerror, center.y+b2yerror, location.z)) if(density && T.contains_dense_objects()) continue//If density was specified. if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. if(T.y>world.maxy || T.y<1) continue @@ -169,9 +169,6 @@ Turf and target are seperate in case you want to teleport some distance from a t return 1 return 0 -/proc/sign(x) - return x!=0?x/abs(x):0 - /proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm var/px=M.x //starting x var/py=M.y @@ -180,8 +177,8 @@ Turf and target are seperate in case you want to teleport some distance from a t var/dy=N.y-py var/dxabs=abs(dx)//Absolute value of x distance var/dyabs=abs(dy) - var/sdx=sign(dx) //Sign of x distance (+ or -) - var/sdy=sign(dy) + var/sdx=SIGN(dx) //Sign of x distance (+ or -) + var/sdy=SIGN(dy) var/x=BITSHIFT_RIGHT(dxabs,1) //Counters for steps taken, setting to distance/2 var/y=BITSHIFT_RIGHT(dyabs,1) //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. var/j //Generic integer for counting diff --git a/code/_helpers/visual_filters.dm b/code/_helpers/visual_filters.dm index deb7162f196..13bdaa2d6be 100644 --- a/code/_helpers/visual_filters.dm +++ b/code/_helpers/visual_filters.dm @@ -55,8 +55,8 @@ LAZYREMOVE(filter_data, filter_name) filters -= thing update_filters() - return FALSE - return TRUE + return TRUE + return FALSE /// Animate a given filter on this atom. All params after the first are passed to animate(). /atom/movable/proc/animate_filter(filter_name, list/params) diff --git a/code/_macros.dm b/code/_macros.dm index 24c978ba2ad..f5a7ba925a2 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -147,36 +147,36 @@ #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_BOLDANNOUNCE(X) SPAN_CLASS("boldannounce", 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_BOLDANNOUNCE(X) SPAN_CLASS("boldannounce", 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" diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index abb55bfe116..e35ab0ceb7e 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -63,12 +63,6 @@ silicon_camera.captureimage(A, usr) return - /* - AI restrained() currently does nothing - if(restrained()) - RestrainedClickOn(A) - else - */ A.add_hiddenprint(src) A.attack_ai(src) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index b6110f8ff15..42298a21d64 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -116,18 +116,13 @@ var/sdepth = A.storage_depth(src) if((!isturf(A) && A == loc) || (sdepth != -1 && sdepth <= 1)) if(holding) - - // AI driven mobs have a melee telegraph that needs to be handled here. - if(a_intent == I_HURT && istype(A) && (!do_attack_windup_checking(A) || holding != get_active_held_item())) - return TRUE - var/resolved = holding.resolve_attackby(A, src, params) if(!resolved && A && holding) holding.afterattack(A, src, 1, params) // 1 indicates adjacency - setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + setClickCooldown(DEFAULT_QUICK_COOLDOWN) else if(ismob(A)) // No instant mob attacking - setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + setClickCooldown(DEFAULT_QUICK_COOLDOWN) UnarmedAttack(A, TRUE) trigger_aiming(TARGET_CAN_CLICK) @@ -143,18 +138,14 @@ if(A.Adjacent(src)) // see adjacent.dm if(holding) - // AI driven mobs have a melee telegraph that needs to be handled here. - if(a_intent == I_HURT && istype(A) && (!do_attack_windup_checking(A) || holding != get_active_held_item())) - return TRUE - // Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example) - var/resolved = holding.resolve_attackby(A,src, params) + var/resolved = holding.resolve_attackby(A, src, params) if(!resolved && A && holding) holding.afterattack(A, src, 1, params) // 1: clicking something Adjacent - setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + setClickCooldown(DEFAULT_QUICK_COOLDOWN) else if(ismob(A)) // No instant mob attacking - setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + setClickCooldown(DEFAULT_QUICK_COOLDOWN) UnarmedAttack(A, TRUE) trigger_aiming(TARGET_CAN_CLICK) @@ -217,15 +208,7 @@ if(istype(G) && G.Touch(A,1)) return TRUE - // Pick up items. - if(check_dexterity(DEXTERITY_HOLD_ITEM, silent = TRUE)) - return A.attack_hand(src) - - // AI driven mobs have a melee telegraph that needs to be handled here. - if(a_intent == I_HURT && istype(A) && !do_attack_windup_checking(A)) - return TRUE - - return FALSE + return A.attack_hand(src) /* Ranged unarmed attack: @@ -247,6 +230,9 @@ if(abilities?.do_ranged_invocation(A)) return TRUE + if(A.attack_hand_ranged(src)) + return TRUE + return FALSE /* @@ -296,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 diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 6c3101a965a..45b5e45bf42 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -44,16 +44,9 @@ if(is_component_functioning("camera")) silicon_camera.captureimage(A, usr) else - to_chat(src, "Your camera isn't functional.") + to_chat(src, "Your camera isn't functional.") return - /* - cyborg restrained() currently does nothing - if(restrained()) - RestrainedClickOn(A) - return - */ - var/obj/item/holding = get_active_held_item() // Cyborgs have no range-checking unless there is item use @@ -76,7 +69,7 @@ var/resolved = holding.resolve_attackby(A, src, params) if(!resolved && A && holding) holding.afterattack(A, src, 1, params) // 1 indicates adjacency - setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + setClickCooldown(DEFAULT_QUICK_COOLDOWN) return if(!isturf(loc)) @@ -88,7 +81,7 @@ var/resolved = holding.resolve_attackby(A, src, params) if(!resolved && A && holding) holding.afterattack(A, src, 1, params) // 1 indicates adjacency - setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + setClickCooldown(DEFAULT_QUICK_COOLDOWN) else holding.afterattack(A, src, 0, params) return diff --git a/code/_onclick/ghost.dm b/code/_onclick/ghost.dm index da5d187674d..7bee520bdb5 100644 --- a/code/_onclick/ghost.dm +++ b/code/_onclick/ghost.dm @@ -45,6 +45,9 @@ return if(user.client && user.client.inquisitive_ghost) user.examinate(src) + return + if(user.client?.holder || user.antagHUD) + storage?.show_to(user) return // --------------------------------------- diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index f13491832f4..c493f34e7bf 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -16,10 +16,10 @@ #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" @@ -48,7 +48,7 @@ #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" diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm index 0512b8ce1e7..dbd392509b6 100644 --- a/code/_onclick/hud/action.dm +++ b/code/_onclick/hud/action.dm @@ -22,11 +22,14 @@ var/obj/screen/action_button/button = null var/button_icon = 'icons/obj/action_buttons/actions.dmi' var/button_icon_state = "default" + /// The icon to use for the background icon state. Defaults to button_icon if unset. + var/background_icon = 'icons/obj/action_buttons/actions.dmi' var/background_icon_state = "bg_default" var/mob/living/owner /datum/action/New(var/Target) target = Target + background_icon ||= button_icon /datum/action/Destroy() if(owner) 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 5c1d9173a3a..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, UI_ICON_INTENT) - adding += action_intent ..() diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 9efc84cdfd6..cc1728a65fe 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -66,7 +66,7 @@ screen.transform = null if(screen.screen_loc != ui_entire_screen && largest_bound > 7) var/matrix/M = matrix() - M.Scale(CEILING(client.last_view_x_dim/7), CEILING(client.last_view_y_dim/7)) + M.Scale(ceil(client.last_view_x_dim/7), ceil(client.last_view_y_dim/7)) screen.transform = M client.screen |= screen diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index fec45f64dc1..85f3e763a57 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -28,10 +28,12 @@ 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() @@ -63,24 +65,24 @@ var/stamina = mymob.get_stamina() if(stamina < 100) stamina_bar.set_invisibility(INVISIBILITY_NONE) - stamina_bar.icon_state = "prog_bar_[FLOOR(stamina/5)*5][(stamina >= 5) && (stamina <= 25) ? "_fail" : null]" + stamina_bar.icon_state = "prog_bar_[floor(stamina/5)*5][(stamina >= 5) && (stamina <= 25) ? "_fail" : null]" /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 +114,21 @@ return FALSE /datum/hud/proc/FinalizeInstantiation() + SHOULD_CALL_PARENT(TRUE) + + var/ui_style = get_ui_style_data() + var/ui_color = get_ui_color() + var/ui_alpha = get_ui_alpha() + + if(!action_intent && action_intent_type) // Everyone needs an intent selector. + action_intent = new action_intent_type(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_INTENT) + adding |= action_intent + hud_elements |= action_intent + BuildInventoryUI() BuildHandsUI() + if(mymob.client) mymob.client.screen = list() if(length(hand_hud_objects)) @@ -127,6 +141,7 @@ mymob.client.screen |= adding if(length(hotkeybuttons)) mymob.client.screen |= hotkeybuttons + hide_inventory() /datum/hud/proc/get_ui_style_data() @@ -217,7 +232,8 @@ inv_box = sublist[2] inv_box.screen_loc = "CENTER:[world.icon_size/2],BOTTOM:[hand_y_offset]" hand_y_offset += world.icon_size - if(mymob.client && length(hand_hud_objects)) + + if(mymob.client && islist(hand_hud_objects) && length(hand_hud_objects)) mymob.client.screen |= hand_hud_objects // Make sure all held items are on the screen and set to the correct screen loc. @@ -362,7 +378,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. @@ -399,7 +415,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 a4fb7dab7ff..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.has_a_intent) - action_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_INTENT) - 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/radial.dm b/code/_onclick/hud/radial.dm index 5a9d6fcde2a..33ba6c229f8 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -173,7 +173,7 @@ var/global/list/radial_menus = list() /datum/radial_menu/proc/get_next_id() return "c_[choices.len]" -/datum/radial_menu/proc/set_choices(list/new_choices, use_tooltips, use_labels) +/datum/radial_menu/proc/set_choices(list/new_choices, use_tooltips, use_labels = RADIAL_LABELS_NONE) if(choices.len) Reset() for(var/E in new_choices) @@ -186,8 +186,7 @@ var/global/list/radial_menus = list() choices_icons[id] = I setup_menu(use_tooltips) - -/datum/radial_menu/proc/extract_image(image/E, var/use_labels) +/datum/radial_menu/proc/extract_image(image/E, var/use_labels = RADIAL_LABELS_NONE) var/mutable_appearance/MA = new /mutable_appearance(E) if(MA) MA.layer = HUD_ABOVE_HUD_LAYER @@ -196,9 +195,15 @@ var/global/list/radial_menus = list() MA.maptext_width = 64 MA.maptext_height = 64 MA.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - MA.maptext_x = -round(MA.maptext_width/2) + 16 - MA.maptext_y = -round(MA.maptext_height/2) + 16 - MA.maptext = STYLE_SMALLFONTS_OUTLINE("
[E.name]
", 7, COLOR_WHITE, COLOR_BLACK) + switch(use_labels) + if(RADIAL_LABELS_OFFSET) + MA.maptext_x = -16 + MA.maptext_y = -8 + MA.maptext = STYLE_SMALLFONTS_OUTLINE("
[E.name]
", 7, COLOR_WHITE, COLOR_BLACK) + if(RADIAL_LABELS_CENTERED) + MA.maptext_x = -16 + MA.maptext_y = -16 + MA.maptext = "[E.name]" return MA @@ -253,7 +258,7 @@ var/global/list/radial_menus = list() Choices should be a list where list keys are movables or text used for element names and return value and list values are movables/icons/images used for element icons */ -/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE, list/check_locs, use_labels = FALSE) +/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE, list/check_locs, use_labels = RADIAL_LABELS_NONE) if(!user || !anchor || !length(choices)) return if(!uniqueid) diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index b7b7dce280e..ab88cc0b2be 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, ui_style, ui_color, ui_alpha, UI_ICON_INTENT) - action_intent.icon_state = R.a_intent - - adding += action_intent adding += new /obj/screen/robot_panel(null, mymob) adding += new /obj/screen/robot_store(null, mymob) @@ -96,17 +91,17 @@ var/global/obj/screen/robot_inventory R.active_storage.close(R) //Closes the inventory ui. if(!R.module) - to_chat(usr, "No module selected") + to_chat(usr, SPAN_WARNING("No module selected.")) return if(!R.module.equipment) - to_chat(usr, "Selected module has no modules to select") + to_chat(usr, SPAN_WARNING("Selected module has no equipment available.")) return if(!R.robot_modules_background) return - var/display_rows = -round(-(R.module.equipment.len) / 8) + var/display_rows = ceil(R.module.equipment.len / 8) R.robot_modules_background.screen_loc = "CENTER-4:16,BOTTOM+1:7 to CENTER+3:16,BOTTOM+[display_rows]:7" R.client.screen += R.robot_modules_background diff --git a/code/_onclick/hud/screen/screen_action_button.dm b/code/_onclick/hud/screen/screen_action_button.dm index 2681beeabc8..33bf849cb9c 100644 --- a/code/_onclick/hud/screen/screen_action_button.dm +++ b/code/_onclick/hud/screen/screen_action_button.dm @@ -16,7 +16,7 @@ /obj/screen/action_button/on_update_icon() if(!action) return - icon = action.button_icon + icon = action.background_icon icon_state = action.background_icon_state cut_overlays() diff --git a/code/_onclick/hud/screen/screen_ai_button.dm b/code/_onclick/hud/screen/screen_ai_button.dm index 03371223da7..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(usr)) + + var/mob/living/silicon/ai/A = user + if(!istype(A)) return TRUE - var/mob/living/silicon/ai/A = usr + 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_maneuver.dm b/code/_onclick/hud/screen/screen_maneuver.dm index 76c524467e5..203bffc6943 100644 --- a/code/_onclick/hud/screen/screen_maneuver.dm +++ b/code/_onclick/hud/screen/screen_maneuver.dm @@ -8,7 +8,7 @@ var/mob/living/user_living = user user_living.prepare_maneuver() -/obj/screen/maneuver/examine(mob/user) +/obj/screen/maneuver/examine(mob/user, distance) . = ..() if(!isliving(user)) return diff --git a/code/_onclick/hud/screen/screen_pai.dm b/code/_onclick/hud/screen/screen_pai.dm index e14096b5a34..7059432c1a7 100644 --- a/code/_onclick/hud/screen/screen_pai.dm +++ b/code/_onclick/hud/screen/screen_pai.dm @@ -2,6 +2,7 @@ icon = 'icons/mob/screen/pai.dmi' abstract_type = /obj/screen/pai requires_ui_style = FALSE + user_incapacitation_flags = INCAPACITATION_KNOCKOUT /obj/screen/pai/shell name = "Toggle Chassis" diff --git a/code/_onclick/hud/screen/screen_robot_intent.dm b/code/_onclick/hud/screen/screen_robot_intent.dm index bbc6501941e..e69de29bb2d 100644 --- a/code/_onclick/hud/screen/screen_robot_intent.dm +++ b/code/_onclick/hud/screen/screen_robot_intent.dm @@ -1,7 +0,0 @@ -/obj/screen/intent/robot - name = "act_intent" - dir = SOUTHWEST - screen_loc = ui_acti - -/obj/screen/intent/robot/handle_click(mob/user, params) - user.a_intent_change("right") diff --git a/code/_onclick/hud/screen/screen_setup.dm b/code/_onclick/hud/screen/screen_setup.dm index d0159e2aff2..eaa68e8feb9 100644 --- a/code/_onclick/hud/screen/screen_setup.dm +++ b/code/_onclick/hud/screen/screen_setup.dm @@ -1,6 +1,6 @@ // Character setup stuff /obj/screen/setup_preview - icon = 'icons/effects/32x32.dmi' + icon = 'icons/effects/64x48.dmi' plane = DEFAULT_PLANE layer = MOB_LAYER requires_owner = FALSE @@ -15,11 +15,12 @@ /obj/screen/setup_preview/bg layer = TURF_LAYER mouse_over_pointer = MOUSE_HAND_POINTER + screen_loc = "character_preview_map:1,1 to 1,5" // Uses Click() instead of handle_click() due to being accessed by new_player mobs. /obj/screen/setup_preview/bg/Click(location, control, params) if(pref) - pref.bgstate = next_in_list(pref.bgstate, pref.bgstate_options) + pref.bgstate = next_in_list(pref.bgstate, global.using_map.char_preview_bgstate_options) var/mob/living/human/dummy/mannequin/mannequin = get_mannequin(pref.client_ckey) if(mannequin) pref.update_character_previews(mannequin) diff --git a/code/_onclick/hud/screen/screen_warnings.dm b/code/_onclick/hud/screen/screen_warnings.dm index 842f93677d1..d89eb94e10b 100644 --- a/code/_onclick/hud/screen/screen_warnings.dm +++ b/code/_onclick/hud/screen/screen_warnings.dm @@ -8,6 +8,12 @@ icon_state = "health0" screen_loc = ui_health +/obj/screen/health_warning/handle_click(mob/user, params) + if(ishuman(user)) + var/mob/living/human/human_user = user + human_user.check_self_injuries() + return TRUE + /obj/screen/warning_cells name = "cell" icon_state = "charge-empty" diff --git a/code/_onclick/hud/skybox.dm b/code/_onclick/hud/skybox.dm index 3de900748eb..03cdfaf1dab 100644 --- a/code/_onclick/hud/skybox.dm +++ b/code/_onclick/hud/skybox.dm @@ -13,7 +13,7 @@ var/global/const/SKYBOX_DIMENSION = 736 // Largest measurement for icon sides, u /obj/skybox/Initialize() if(!max_view_dim) - max_view_dim = CEILING(SKYBOX_DIMENSION / world.icon_size) + max_view_dim = ceil(SKYBOX_DIMENSION / world.icon_size) . = ..() /client diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index a5c912e0263..3bb7bf3b1a7 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -24,19 +24,21 @@ avoid code duplication. This includes items that may sometimes act as a standard var/datum/extension/tool/tool = get_extension(src, /datum/extension/tool) return (tool?.handle_physical_manipulation(user)) || FALSE +// If TRUE, prevent afterattack from running. /obj/item/proc/resolve_attackby(atom/A, mob/user, var/click_params) if(!(item_flags & ITEM_FLAG_NO_PRINT)) add_fingerprint(user) return A.attackby(src, user, click_params) -/atom/proc/attackby(obj/item/W, mob/user, var/click_params) +// If TRUE, prevent afterattack from running. +/atom/proc/attackby(obj/item/used_item, mob/user, var/click_params) if(storage) - if(isrobot(user) && (W == user.get_active_held_item())) - return //Robots can't store their modules. - if(!storage.can_be_inserted(W, user)) - return - W.add_fingerprint(user) - return storage.handle_item_insertion(user, W) + if(isrobot(user) && (used_item == user.get_active_held_item())) + return FALSE //Robots can't store their modules. + if(!storage.can_be_inserted(used_item, user, click_params = click_params)) + 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) @@ -44,10 +46,11 @@ avoid code duplication. This includes items that may sometimes act as a standard if(!.) return bash(W,user) +// Return TRUE if further actions (afterattack, etc) should be prevented, FALSE if they can proceed. /atom/movable/proc/bash(obj/item/weapon, mob/user) if(isliving(user) && user.a_intent == I_HELP) return FALSE - if(!weapon.user_can_wield(user)) + if(!weapon.user_can_attack_with(user)) return FALSE if(weapon.item_flags & ITEM_FLAG_NO_BLUDGEON) return FALSE @@ -57,39 +60,44 @@ avoid code duplication. This includes items that may sometimes act as a standard /mob/living/attackby(obj/item/used_item, mob/user) if(!ismob(user)) return TRUE + + if(!QDELETED(used_item) && user.a_intent == I_HELP) + var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, user.get_target_zone()) + if(length(E?.ailments)) + for(var/datum/ailment/ailment in E.ailments) + if(ailment.treated_by_item(used_item)) + ailment.was_treated_by_item(used_item, user, src) + return TRUE + if(user.a_intent != I_HURT) if(can_operate(src, user) != OPERATE_DENY && used_item.do_surgery(src,user)) //Surgery return TRUE if(try_butcher_in_place(user, used_item)) return TRUE + + if(istype(used_item, /obj/item/chems) && ATOM_IS_OPEN_CONTAINER(used_item) && has_extension(src, /datum/extension/milkable)) + var/datum/extension/milkable/milkable = get_extension(src, /datum/extension/milkable) + if(milkable.handle_milked(used_item, user)) + return TRUE + + if(used_item.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 + var/oldhealth = current_health . = used_item.use_on_mob(src, user) - if(used_item.force && istype(ai) && current_health < oldhealth) + if(used_item.get_attack_force(user) && istype(ai) && current_health < oldhealth) ai.retaliate(user) -/mob/living/human/attackby(obj/item/I, mob/user) - - . = ..() - if(.) - if(user.a_intent != I_HELP) - return - var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, user.get_target_zone()) - if(!E) - return - for(var/datum/ailment/ailment in E.ailments) - if(ailment.treated_by_item(I)) - ailment.was_treated_by_item(I, user, src) - return - - else if(user == src && user.get_target_zone() == BP_MOUTH && can_devour(I, silent = TRUE)) + if(!. && user == src && user.get_target_zone() == BP_MOUTH && can_devour(used_item, silent = TRUE)) var/obj/item/blocked = src.check_mouth_coverage() if(blocked) to_chat(user, SPAN_WARNING("\The [blocked] is in the way!")) else - devour(I) + devour(used_item) return TRUE - // Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person. // Click parameters is the params string from byond Click() code, see that documentation. /obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters) @@ -100,11 +108,12 @@ avoid code duplication. This includes items that may sometimes act as a standard var/mob/living/attackee = null //I would prefer to rename this attack_as_weapon(), but that would involve touching hundreds of files. +// If this returns TRUE, the interaction has been handled and other interactions like afterattack should be skipped. /obj/item/proc/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) // TODO: revisit if this should be a silent failure/parent call instead, for mob-level storage interactions? // like a horse with a saddlebag or something - if(!user_can_wield(user)) + if(!user_can_attack_with(user)) return TRUE // skip other interactions if(squash_item()) @@ -161,7 +170,7 @@ avoid code duplication. This includes items that may sometimes act as a standard else use_hitsound = "swing_hit" playsound(loc, use_hitsound, 50, 1, -1) - return target.hit_with_weapon(src, user, force, hit_zone) + return target.hit_with_weapon(src, user, get_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 d25017d2426..49bf7f7047f 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -8,9 +8,15 @@ /atom/proc/can_interact_with_storage(user, strict = FALSE) return isliving(user) +/atom/proc/get_required_interaction_dexterity() + return DEXTERITY_NONE + /atom/proc/attack_hand(mob/user) SHOULD_CALL_PARENT(TRUE) + if(!user.check_dexterity(get_required_interaction_dexterity(), silent = TRUE)) + return FALSE + if(can_interact_with_storage(user, strict = TRUE) && storage && user.check_dexterity((DEXTERITY_HOLD_ITEM|DEXTERITY_EQUIP_ITEM), TRUE)) add_fingerprint(user) storage.open(user) @@ -39,10 +45,6 @@ return TRUE return FALSE - -/mob/living/human/RestrainedClickOn(var/atom/A) - return - /mob/living/human/RangedAttack(var/atom/A, var/params) //Climbing up open spaces if(isturf(loc) && bound_overlay && !is_physically_disabled() && istype(A) && A.can_climb_from_below(src)) @@ -54,8 +56,31 @@ . = ..() +/atom/proc/attack_hand_ranged(mob/user) + SHOULD_CALL_PARENT(TRUE) + return FALSE + /mob/living/RestrainedClickOn(var/atom/A) - return + if (A != src) + return ..() + if(world.time < next_restraint_chew || !get_equipped_item(slot_handcuffed_str) || a_intent != I_HURT || get_target_zone() != BP_MOUTH) + return FALSE + // Cannot chew with a mask or a full body restraint. + if (get_equipped_item(slot_wear_mask_str) || istype(get_equipped_item(slot_wear_suit_str), /obj/item/clothing/suit/straight_jacket)) + return FALSE + // Type to hand so drakes don't chew off their own head. + var/obj/item/organ/external/hand/O = GET_EXTERNAL_ORGAN(src, get_active_held_item_slot()) + if(!istype(O)) + return FALSE + var/decl/pronouns/pronouns = get_pronouns() + visible_message( + SPAN_DANGER("\The [src] chews on [pronouns.his] [O.name]"), + SPAN_DANGER("You chew on your [O.name]!") + ) + admin_attacker_log(src, "chewed on their [O.name]!") + O.take_external_damage(3,0, DAM_SHARP|DAM_EDGE ,"teeth marks") + next_restraint_chew = world.time + (2.5 SECONDS) + return TRUE /* New Players: @@ -80,10 +105,9 @@ a_intent = I_HURT . = A.attackby(attacking_with, src) + // attack effects are handled in natural_weapon's apply_hit_effect() instead of here if(!.) reset_offsets(anim_time = 2) - else if(isliving(A)) - apply_attack_effects(A) // Attack hand but for simple animals /atom/proc/attack_animal(mob/user) diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm index 9c1802edd7a..3b7d5f0d8ac 100644 --- a/code/_onclick/rig.dm +++ b/code/_onclick/rig.dm @@ -46,6 +46,6 @@ return 0 rig.selected_module.engage(A, alert_ai) if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns - setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + setClickCooldown(DEFAULT_QUICK_COOLDOWN) return 1 return 0 \ No newline at end of file diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 47cd2687af3..4042baed6b1 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -18,7 +18,7 @@ var/global/datum/controller/failsafe/Failsafe // (Real friends look out for *eachother*) var/lasttick = 0 - // Track the MC iteration to make sure its still on track. + // Track the MC iteration to make sure it's still on track. var/master_iteration = 0 var/running = TRUE diff --git a/code/controllers/master.dm b/code/controllers/master.dm index fc55af0e232..2c38fd4c194 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -157,7 +157,7 @@ var/global/datum/controller/master/Master = new // Please don't stuff random bullshit here, -// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize() +// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in its Initialize() /datum/controller/master/Initialize(delay, init_sss) set waitfor = 0 @@ -223,12 +223,17 @@ var/global/datum/controller/master/Master = new CRASH("Attempted to set invalid runlevel: [new_runlevel]") // Starts the mc, and sticks around to restart it if the loop ever ends. +var/global/_announced_start = FALSE /datum/controller/master/proc/StartProcessing(delay) set waitfor = 0 if(delay) sleep(delay) report_progress("Master starting processing") - SSwebhooks.send(WEBHOOK_ROUNDPREP, list("map" = station_name(), "url" = get_world_url())) + + if(!global._announced_start) // Only announce roundstart once. + SSwebhooks.send(WEBHOOK_ROUNDPREP, list("map" = station_name(), "url" = get_world_url())) + global._announced_start = TRUE + var/rtn = Loop() if (rtn > 0 || processing < 0) return //this was suppose to happen. diff --git a/code/controllers/subsystems/DPC.dm b/code/controllers/subsystems/DPC.dm index ab23225d314..1adde54dfb1 100644 --- a/code/controllers/subsystems/DPC.dm +++ b/code/controllers/subsystems/DPC.dm @@ -1,7 +1,7 @@ /* This is pretty much just an optimization for wait=0 timers. They're relatively common, but generally don't actually need the more complex features of SStimer. SSdpc can handle these timers instead (and it's a lot simpler than SStimer is), but it can't handle - complex timers with flags. This doesn't need to be explicitly used, eligible timers are automatically converted. + timers with certain flags (check MC.dm). This doesn't need to be explicitly used, eligible timers are automatically converted. */ SUBSYSTEM_DEF(dpc) @@ -12,15 +12,19 @@ SUBSYSTEM_DEF(dpc) flags = SS_TICKER | SS_NO_INIT var/list/queued_calls = list() - var/list/avg = 0 + var/avg = 0 + var/list/unique_queued_calls = list() + var/unique_avg = 0 /datum/controller/subsystem/dpc/stat_entry() - return ..() + " Q: [queued_calls.len], AQ: ~[round(avg)]" + return ..() + " Q: [queued_calls.len], AQ: ~[round(avg)], UQ: [unique_queued_calls.len], UAQ: ~[round(unique_avg)]" /datum/controller/subsystem/dpc/fire(resumed = FALSE) var/list/qc = queued_calls + var/list/uqc = unique_queued_calls if (!resumed) avg = MC_AVERAGE_FAST(avg, qc.len) + unique_avg = MC_AVERAGE_FAST(unique_avg, uqc.len) var/q_idex = 1 @@ -35,3 +39,17 @@ SUBSYSTEM_DEF(dpc) if (q_idex > 1) queued_calls.Cut(1, q_idex) + + q_idex = 1 // Reuse this variable so we don't waste time allocating two + while (q_idex <= uqc.len) + var/hash = uqc[q_idex] + var/datum/callback/CB = uqc[hash] + q_idex += 1 + + CB.InvokeAsync() + + if (MC_TICK_CHECK) + break + + if (q_idex > 1) + unique_queued_calls.Cut(1, q_idex) diff --git a/code/controllers/subsystems/ambience.dm b/code/controllers/subsystems/ambience.dm index eb134f0ebf4..d0a77c628f6 100644 --- a/code/controllers/subsystems/ambience.dm +++ b/code/controllers/subsystems/ambience.dm @@ -14,6 +14,7 @@ SUBSYSTEM_DEF(ambience) var/list/curr = queued while (curr.len) var/turf/target = curr[curr.len] + target.ambience_queued = FALSE curr.len-- if(!QDELETED(target)) target.update_ambient_light_from_z_or_area() @@ -22,6 +23,16 @@ SUBSYSTEM_DEF(ambience) else if (MC_TICK_CHECK) return +/datum/controller/subsystem/ambience/StartLoadingMap() + suspend() + +/datum/controller/subsystem/ambience/StopLoadingMap() + wake() + +/turf + /// Whether this turf has been queued for an ambient lighting update. + var/ambience_queued = FALSE + /turf/proc/update_ambient_light_from_z_or_area() // If we're not outside, we don't show ambient light. @@ -42,7 +53,7 @@ SUBSYSTEM_DEF(ambience) var/lit = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) // If we're not, we want ambient light if one of our neighbors needs to show spillover from corners. if(!lit) - for(var/turf/T in RANGE_TURFS(src, 1)) + for(var/turf/T as anything in RANGE_TURFS(src, 1)) // Fuck if I know how these turfs are located in an area that is not an area. if(isloc(T.loc) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) lit = TRUE diff --git a/code/controllers/subsystems/atoms.dm b/code/controllers/subsystems/atoms.dm index 8a7ad0bedd7..6e45e5e274b 100644 --- a/code/controllers/subsystems/atoms.dm +++ b/code/controllers/subsystems/atoms.dm @@ -72,28 +72,34 @@ SUBSYSTEM_DEF(atoms) BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE return TRUE + // This is handled and battle tested by dreamchecker. Limit to UNIT_TEST just in case that ever fails. + #ifdef UNIT_TEST var/start_tick = world.time + #endif var/result = A.Initialize(arglist(arguments)) + #ifdef UNIT_TEST if(start_tick != world.time) BadInitializeCalls[the_type] |= BAD_INIT_SLEPT + #endif var/qdeleted = FALSE - if(result != INITIALIZE_HINT_NORMAL) - switch(result) - if(INITIALIZE_HINT_LATELOAD) - if(arguments[1]) //mapload - late_loaders[A] = arguments - else - A.LateInitialize(arglist(arguments)) - if(INITIALIZE_HINT_QDEL) - A.atom_flags |= ATOM_FLAG_INITIALIZED // never call EarlyDestroy if we return this hint - qdel(A) - qdeleted = TRUE + switch(result) + if(INITIALIZE_HINT_NORMAL) + EMPTY_BLOCK_GUARD + if(INITIALIZE_HINT_LATELOAD) + if(arguments[1]) //mapload + late_loaders[A] = arguments else - BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT + A.LateInitialize(arglist(arguments)) + if(INITIALIZE_HINT_QDEL) + A.atom_flags |= ATOM_FLAG_INITIALIZED // never call EarlyDestroy if we return this hint + qdel(A) + qdeleted = TRUE + else + BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT if(!A) //possible harddel qdeleted = TRUE diff --git a/code/controllers/subsystems/event.dm b/code/controllers/subsystems/event.dm index 1e9f449a1f0..e0abcf5b02c 100644 --- a/code/controllers/subsystems/event.dm +++ b/code/controllers/subsystems/event.dm @@ -126,7 +126,7 @@ SUBSYSTEM_DEF(event) //Event manager UI /datum/controller/subsystem/event/proc/GetInteractWindow() - var/allow_random_events = get_config_value(/decl/config/toggle/allow_random_events) + var/allow_random_events = get_config_value(/decl/config/toggle/on/allow_random_events) var/html = "Refresh" html += "Pause All - [allow_random_events ? "Pause" : "Resume"]" @@ -262,8 +262,8 @@ SUBSYSTEM_DEF(event) EC.delayed = !EC.delayed log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") else if(href_list["pause_all"]) - set_config_value(/decl/config/toggle/allow_random_events, text2num(href_list["pause_all"])) - log_and_message_admins("has [get_config_value(/decl/config/toggle/allow_random_events) ? "resumed" : "paused"] countdown for all events.") + set_config_value(/decl/config/toggle/on/allow_random_events, text2num(href_list["pause_all"])) + log_and_message_admins("has [get_config_value(/decl/config/toggle/on/allow_random_events) ? "resumed" : "paused"] countdown for all events.") else if(href_list["interval"]) var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null if(delay && delay > 0) diff --git a/code/controllers/subsystems/fluids.dm b/code/controllers/subsystems/fluids.dm index 43d72c04b60..0b9faea243e 100644 --- a/code/controllers/subsystems/fluids.dm +++ b/code/controllers/subsystems/fluids.dm @@ -121,6 +121,12 @@ SUBSYSTEM_DEF(fluids) if(current_depth <= FLUID_PUDDLE && prob(60)) current_fluid_holder.remove_fluids(min(current_depth, 1), defer_update = TRUE) current_depth = current_fluid_holder.get_fluid_depth() + + // Mimimum liquid depth for creation of slurries. Do this after evaporation since it may change the total depth. + if(reagent_holder?.total_liquid_volume < FLUID_SLURRY) + current_fluid_holder.dump_solid_reagents() + current_depth = current_fluid_holder.get_fluid_depth() + if(current_depth <= FLUID_QDEL_POINT) current_fluid_holder.reagents?.clear_reagents() REMOVE_ACTIVE_FLUID(current_fluid_holder) @@ -134,6 +140,8 @@ SUBSYSTEM_DEF(fluids) if(removing > 0) current_fluid_holder.remove_fluids(removing, defer_update = TRUE) else + // Dump any solids in case there were any in slurry. + current_fluid_holder.dump_solid_reagents() reagent_holder.clear_reagents() current_depth = current_fluid_holder.get_fluid_depth() if(current_depth <= FLUID_QDEL_POINT) @@ -147,7 +155,7 @@ SUBSYSTEM_DEF(fluids) UPDATE_FLUID_BLOCKED_DIRS(other_fluid_holder) if(!(other_fluid_holder.fluid_blocked_dirs & UP) && other_fluid_holder.CanFluidPass(UP)) if(!QDELETED(other_fluid_holder) && other_fluid_holder.reagents?.total_volume < FLUID_MAX_DEPTH) - current_fluid_holder.transfer_fluids_to(other_fluid_holder, min(FLOOR(current_depth*0.5), FLUID_MAX_DEPTH - other_fluid_holder.reagents?.total_volume)) + current_fluid_holder.transfer_fluids_to(other_fluid_holder, min(floor(current_depth*0.5), FLUID_MAX_DEPTH - other_fluid_holder.reagents?.total_volume)) current_depth = current_fluid_holder.get_fluid_depth() // Flow into the lowest level neighbor. @@ -223,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 8f71c4f3a8d..47c1be9e245 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -278,7 +278,7 @@ SUBSYSTEM_DEF(garbage) // 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. -/proc/qdel(datum/D, force=FALSE, ...) +/proc/qdel(datum/D, force=FALSE) if(isnull(D)) return if(!istype(D)) @@ -299,10 +299,10 @@ SUBSYSTEM_DEF(garbage) // Let our friend know they're about to get fucked up. if (isloc(D) && !(D:atom_flags & ATOM_FLAG_INITIALIZED)) - hint = D:EarlyDestroy(arglist(args.Copy(2))) + hint = D:EarlyDestroy(force) I.early_destroy += 1 else - hint = D.Destroy(arglist(args.Copy(2))) + hint = D.Destroy(force) if(world.time != start_time) I.slept_destroy++ @@ -312,6 +312,9 @@ SUBSYSTEM_DEF(garbage) if(isnull(D)) return + if(D.is_processing) + get_stack_trace("[D] ([D.type]) was qdeleted while still processing on [D.is_processing]!") + switch(hint) if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion. GC_CHECK_AM_NULLSPACE(D, "QDEL_HINT_QUEUE") diff --git a/code/controllers/subsystems/holomap.dm b/code/controllers/subsystems/holomap.dm index 25785fec3b1..ac6662184f6 100644 --- a/code/controllers/subsystems/holomap.dm +++ b/code/controllers/subsystems/holomap.dm @@ -74,13 +74,14 @@ SUBSYSTEM_DEF(minimap) if(world.maxy + offset_y > canvas.Height()) CRASH("Minimap for z=[zlevel] : world.maxy ([world.maxy]) + holomap_offset_y ([offset_y]) must be <= [canvas.Height()]") - for(var/turf/tile as anything in BLOCK_TURFS(1,1,world.maxx,world.maxy,zlevel)) + var/datum/level_data/level = SSmapping.levels_by_z[zlevel] + for(var/turf/tile as anything in block(level.level_inner_min_x, level.level_inner_min_y, zlevel, level.level_inner_max_x, level.level_inner_max_y, zlevel)) var/area/A = get_area(tile) if ((A && (A.area_flags & AREA_FLAG_HIDE_FROM_HOLOMAP)) || (tile.turf_flags & TURF_IS_HOLOMAP_ROCK)) 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 @@ -92,8 +93,9 @@ SUBSYSTEM_DEF(minimap) var/offset_x = HOLOMAP_PIXEL_OFFSET_X(zlevel) var/offset_y = HOLOMAP_PIXEL_OFFSET_Y(zlevel) - for(var/x = 1 to world.maxx) - for(var/y = 1 to world.maxy) + var/datum/level_data/level = SSmapping.levels_by_z[zlevel] + for(var/x = level.level_inner_min_x to level.level_inner_max_x) + for(var/y = level.level_inner_min_y to level.level_inner_max_y) var/turf/tile = locate(x, y, zlevel) if(tile && tile.loc) var/area/areaToPaint = tile.loc diff --git a/code/controllers/subsystems/icon_updates.dm b/code/controllers/subsystems/icon_updates.dm index 53b6eaa68c7..7ad1540578b 100644 --- a/code/controllers/subsystems/icon_updates.dm +++ b/code/controllers/subsystems/icon_updates.dm @@ -21,11 +21,11 @@ SUBSYSTEM_DEF(icon_update) return while (queue_refs.len) - + if(Master.map_loading) return - // Pops the atom and it's args + // Pops the atom and its args var/atom/A = queue_refs[queue_refs.len] var/myArgs = queue_args[queue_args.len] @@ -61,10 +61,11 @@ SUBSYSTEM_DEF(icon_update) var/length = length(SSicon_update.queue_refs) SSicon_update.queue_args.len = length SSicon_update.queue_args[length] = args.len ? args : null - + // SSicon_update sleeps when it runs out of things in its // queue, so wake it up. - SSicon_update.wake() + if(!Master.map_loading) // Don't wake early if we're loading a map, it'll get woken up when the map loads. + SSicon_update.wake() /datum/controller/subsystem/icon_update/StartLoadingMap() suspend() diff --git a/code/controllers/subsystems/initialization/character_info.dm b/code/controllers/subsystems/initialization/character_info.dm index 813f1fda5d8..cb09b69f361 100644 --- a/code/controllers/subsystems/initialization/character_info.dm +++ b/code/controllers/subsystems/initialization/character_info.dm @@ -121,10 +121,11 @@ SUBSYSTEM_DEF(character_info) search_for = ckey(lowertext(trim(search_for))) for(var/id in _comment_holders_by_id) var/datum/character_information/comments = _comment_holders_by_id[id] - for(var/checkstring in list(comments.name, comments.ckey, comments.record_id)) - if(findtext(ckey(lowertext(trim(checkstring))), search_for) > 0) - LAZYADD(., comments) - break + if(istype(comments)) + for(var/checkstring in list(comments.name, comments.ckey, comments.record_id)) + if(findtext(ckey(lowertext(trim(checkstring))), search_for) > 0) + LAZYADD(., comments) + break /datum/controller/subsystem/character_info/Topic(href, href_list) . = ..() diff --git a/code/controllers/subsystems/initialization/fabrication.dm b/code/controllers/subsystems/initialization/fabrication.dm index e383f77a1c4..37a6d57dfae 100644 --- a/code/controllers/subsystems/initialization/fabrication.dm +++ b/code/controllers/subsystems/initialization/fabrication.dm @@ -33,6 +33,9 @@ SUBSYSTEM_DEF(fabrication) var/list/all_crafting_handlers = decls_repository.get_decls_of_subtype(/decl/crafting_stage) for(var/hid in all_crafting_handlers) var/decl/crafting_stage/handler = all_crafting_handlers[hid] + // TODO: revisit this if map tech level can be mutated at runtime + if(global.using_map.map_tech_level < handler.available_to_map_tech_level) + continue if(ispath(handler.begins_with_object_type)) LAZYDISTINCTADD(crafting_procedures_by_type[handler.begins_with_object_type], handler) diff --git a/code/controllers/subsystems/initialization/lore.dm b/code/controllers/subsystems/initialization/lore.dm index 068c0dee2bc..96b7a9850e0 100644 --- a/code/controllers/subsystems/initialization/lore.dm +++ b/code/controllers/subsystems/initialization/lore.dm @@ -29,14 +29,14 @@ SUBSYSTEM_DEF(lore) /datum/controller/subsystem/lore/Initialize() - var/list/all_cultural_decls = decls_repository.get_decls_of_subtype(/decl/cultural_info) - for(var/ftype in all_cultural_decls) - var/decl/cultural_info/culture = all_cultural_decls[ftype] - if(culture.name && culture.category && !culture.hidden) - if(!tagged_info[culture.category]) - tagged_info[culture.category] = list() - var/list/tag_list = tagged_info[culture.category] - tag_list[culture.name] = culture + var/list/all_backgrounds = decls_repository.get_decls_of_subtype(/decl/background_detail) + for(var/ftype in all_backgrounds) + var/decl/background_detail/background = all_backgrounds[ftype] + if(background.name && background.category && !background.hidden) + if(!tagged_info[background.category]) + tagged_info[background.category] = list() + var/list/tag_list = tagged_info[background.category] + tag_list[background.name] = background for(var/jobtype in subtypesof(/datum/job)) var/datum/job/job = jobtype diff --git a/code/controllers/subsystems/initialization/modpacks.dm b/code/controllers/subsystems/initialization/modpacks.dm index 2d2a2ddc711..4e3819c4b56 100644 --- a/code/controllers/subsystems/initialization/modpacks.dm +++ b/code/controllers/subsystems/initialization/modpacks.dm @@ -38,7 +38,12 @@ SUBSYSTEM_DEF(modpacks) if(fail_msg) PRINT_STACK_TRACE("Modpack [(istype(manifest) && manifest.name) || "Unknown"] failed to post-initialize: [fail_msg]") - // Update compiled infolists. + // Update compiled infolists and apply. default_submap_whitelisted_species |= global.using_map.default_species + for(var/decl/submap_archetype/submap in decls_repository.get_decls_of_type_unassociated(/decl/submap_archetype)) + if(islist(submap.whitelisted_species) && !length(submap.whitelisted_species)) + submap.whitelisted_species |= SSmodpacks.default_submap_whitelisted_species + if(islist(submap.blacklisted_species) && !length(submap.blacklisted_species)) + submap.blacklisted_species |= SSmodpacks.default_submap_blacklisted_species . = ..() diff --git a/code/controllers/subsystems/initialization/persistence.dm b/code/controllers/subsystems/initialization/persistence.dm index ffad9f38179..c1119f8ca85 100644 --- a/code/controllers/subsystems/initialization/persistence.dm +++ b/code/controllers/subsystems/initialization/persistence.dm @@ -44,7 +44,8 @@ SUBSYSTEM_DEF(persistence) if(!A || (A.area_flags & AREA_FLAG_IS_NOT_PERSISTENT)) return - if(!isStationLevel(T.z)) + var/datum/level_data/level = SSmapping.levels_by_z[T.z] + if(!istype(level) || !level.permit_persistence) return if(!tracking_values[track_type]) diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index a3ad71126c4..5faeabfefd1 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -330,6 +330,8 @@ SUBSYSTEM_DEF(jobs) //Get the players who are ready for(var/mob/new_player/player in global.player_list) if(player.ready && player.mind && !player.mind.assigned_role) + if(get_config_value(/decl/config/enum/server_whitelist) == CONFIG_SERVER_JOIN_WHITELIST && !check_server_whitelist(player)) + continue unassigned_roundstart += player if(unassigned_roundstart.len == 0) return 0 //Shuffle players and jobs @@ -411,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)) @@ -436,13 +438,13 @@ SUBSYSTEM_DEF(jobs) var/list/spawn_in_storage = list() if(H.client.prefs.Gear() && job.loadout_allowed) for(var/thing in H.client.prefs.Gear()) - var/decl/loadout_option/G = global.gear_datums[thing] + 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.name])) + if(!G.slot || !G.spawn_on_mob(H, H.client.prefs.Gear()[G.uid])) spawn_in_storage.Add(G) // do accessories last so they don't attach to a suit that will be replaced @@ -536,7 +538,7 @@ SUBSYSTEM_DEF(jobs) if(spawn_in_storage) for(var/decl/loadout_option/G in spawn_in_storage) - G.spawn_in_storage_or_drop(H, H.client.prefs.Gear()[G.name]) + G.spawn_in_storage_or_drop(H, H.client.prefs.Gear()[G.uid]) var/article = job.total_positions == 1 ? "the" : "a" to_chat(H, "You are [article] [alt_title || job_title].") diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 9872df6741e..6e8cf042227 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -42,7 +42,7 @@ SUBSYSTEM_DEF(mapping) /// Z-levels available to various consoles, such as the crew monitor. Defaults to station_levels if unset. var/list/map_levels /// The turf type used when generating floors between Z-levels at startup. - var/base_floor_type = /turf/floor/airless + var/base_floor_type = /turf/floor/plating/airless /// Replacement area, if a base_floor_type is generated. Leave blank to skip. var/base_floor_area /// A list of connected z-levels to avoid repeatedly rebuilding connections diff --git a/code/controllers/subsystems/overlays.dm b/code/controllers/subsystems/overlays.dm index 689c5f1bd68..88650811392 100644 --- a/code/controllers/subsystems/overlays.dm +++ b/code/controllers/subsystems/overlays.dm @@ -113,12 +113,13 @@ SUBSYSTEM_DEF(overlays) /atom/proc/build_appearance_list(atom/new_overlays) var/static/image/appearance_bro = new if(islist(new_overlays)) - if(null in new_overlays) - new_overlays -= new /list(length(new_overlays)) // Clears nulls from the list prior to appearancifying. - for(var/i in 1 to length(new_overlays)) - var/image/cached_overlay = new_overlays[i] - APPEARANCEIFY(cached_overlay, new_overlays[i]) - return new_overlays + var/list/new_overlays_list = new_overlays + if(null in new_overlays_list) + new_overlays_list -= new /list(length(new_overlays_list)) // Clears nulls from the list prior to appearancifying. + for(var/i in 1 to length(new_overlays_list)) + var/image/cached_overlay = new_overlays_list[i] + APPEARANCEIFY(cached_overlay, new_overlays_list[i]) + return new_overlays_list else APPEARANCEIFY(new_overlays, .) @@ -208,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 a7d531e8679..dfaae9e7999 100644 --- a/code/controllers/subsystems/processing/nano.dm +++ b/code/controllers/subsystems/processing/nano.dm @@ -96,14 +96,14 @@ PROCESSING_SUBSYSTEM_DEF(nano) * * @return int The number of uis updated */ -/datum/controller/subsystem/processing/nano/proc/update_user_uis(mob/user, src_object, ui_key) +/datum/controller/subsystem/processing/nano/proc/update_user_uis(mob/user, src_object, ui_key = null, force_open = FALSE) . = 0 if (!length(user.open_uis)) return // has no open uis for (var/datum/nanoui/ui in user.open_uis) if ((isnull(src_object) || ui.src_object == src_object) && (isnull(ui_key) || ui.ui_key == ui_key)) - ui.try_update(1) + ui.try_update(update = TRUE, force_open = force_open) .++ /** 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/radiation.dm b/code/controllers/subsystems/radiation.dm index 4d0d4e98883..2728e47cec4 100644 --- a/code/controllers/subsystems/radiation.dm +++ b/code/controllers/subsystems/radiation.dm @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(radiation) if(QDELETED(T)) resistance_cache -= T else if((length(T.contents) + 1) != resistance_cache[T]) - resistance_cache -= T // If its stale REMOVE it! It will get added if its needed. + resistance_cache -= T // If it's stale REMOVE it! It will get added if it's needed. if (MC_TICK_CHECK) return diff --git a/code/controllers/subsystems/shuttle.dm b/code/controllers/subsystems/shuttle.dm index f8e5f2063e8..7c6b1a07711 100644 --- a/code/controllers/subsystems/shuttle.dm +++ b/code/controllers/subsystems/shuttle.dm @@ -161,6 +161,11 @@ SUBSYSTEM_DEF(shuttle) if (ship.type == type) return ship +/datum/controller/subsystem/shuttle/proc/ship_by_shuttle(shuttle) + for(var/obj/effect/overmap/visitable/ship/landable/landable in SSshuttle.ships) + if(landable.shuttle == shuttle) + return landable + /datum/controller/subsystem/shuttle/proc/docking_beacons_by_z(z_levels) . = list() if(!islist(z_levels)) diff --git a/code/controllers/subsystems/spacedrift.dm b/code/controllers/subsystems/spacedrift.dm index 31b4d0fae59..331e856cdf4 100644 --- a/code/controllers/subsystems/spacedrift.dm +++ b/code/controllers/subsystems/spacedrift.dm @@ -34,9 +34,9 @@ SUBSYSTEM_DEF(spacedrift) return continue - if (!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0)) + if (!AM.loc || AM.loc != AM.inertia_last_loc || AM.is_space_movement_permitted() != SPACE_MOVE_FORBIDDEN) AM.inertia_dir = 0 - + AM.inertia_ignore = null if (!AM.inertia_dir) diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 60706a8ab19..aba3e4aaa0d 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -103,6 +103,7 @@ SUBSYSTEM_DEF(ticker) if(!length(global.admins)) send2adminirc("Round has started with no admins online.") + SSwebhooks.send(WEBHOOK_AHELP_SENT, list("name" = "Round Started (Game ID: [game_id])", "body" = "Round has started with no admins online.")) /datum/controller/subsystem/ticker/proc/playing_tick() mode.process() @@ -112,7 +113,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/controllers/subsystems/timer.dm b/code/controllers/subsystems/timer.dm index 57f55e74d89..10195799ea2 100644 --- a/code/controllers/subsystems/timer.dm +++ b/code/controllers/subsystems/timer.dm @@ -1,7 +1,7 @@ /// Controls how many buckets should be kept, each representing a tick. (1 minutes worth) #define BUCKET_LEN (world.fps*1*60) /// Helper for getting the correct bucket for a given timer -#define BUCKET_POS(timer) (((CEILING((timer.timeToRun - timer.timer_subsystem.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN) +#define BUCKET_POS(timer) (((ceil((timer.timeToRun - timer.timer_subsystem.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN) /// Gets the maximum time at which timers will be invoked from buckets, used for deferring to secondary queue #define TIMER_MAX(timer_ss) (timer_ss.head_offset + TICKS2DS(BUCKET_LEN + timer_ss.practical_offset - 1)) /// Max float with integer precision @@ -569,7 +569,7 @@ SUBSYSTEM_DEF(timer) PRINT_STACK_TRACE("addtimer called with a callback assigned to a qdeleted object. In the future such timers will not \ be supported and may refuse to run or run with a 0 wait") - if (wait == 0 && !flags) + if (wait == 0 && !(flags & DPC_FORBID_FLAGS)) SSdpc.queued_calls += callback return @@ -601,6 +601,22 @@ SUBSYSTEM_DEF(timer) if (hash_timer.flags & TIMER_STOPPABLE) . = hash_timer.id return + else + if(wait == 0 || flags & TIMER_NO_HASH_WAIT) + // Check if a unique DPC queued call exists with the same hash + var/datum/callback/dpc_callback = SSdpc.unique_queued_calls[hash] + if(dpc_callback) + if(flags & TIMER_OVERRIDE) + // if this turns out to have too much overhead, could try setting + // the hash's callback to null and then just having SSdpc skip nulls? + SSdpc.unique_queued_calls -= hash + else + return // don't create a timer + // No timer with this hash exists, so we can use the fast unique-DPC path + // Have to make sure it doesn't have illegal flags for unique DPC though + if(wait == 0 && !(flags & UDPC_FORBID_FLAGS)) + SSdpc.unique_queued_calls[hash] = callback + return else if(flags & TIMER_OVERRIDE) PRINT_STACK_TRACE("TIMER_OVERRIDE used without TIMER_UNIQUE") diff --git a/code/controllers/subsystems/vis_contents.dm b/code/controllers/subsystems/vis_contents.dm index bec6b36215c..b18e803da68 100644 --- a/code/controllers/subsystems/vis_contents.dm +++ b/code/controllers/subsystems/vis_contents.dm @@ -50,7 +50,8 @@ SUBSYSTEM_DEF(vis_contents_update) return vis_update_queued = TRUE SSvis_contents_update.queue_refs.Add(src) - SSvis_contents_update.wake() + if(!Master.map_loading) // Don't wake early if we're loading a map, it'll get woken up when the map loads. + SSvis_contents_update.wake() // Horrible colon syntax below is because vis_contents // exists in /atom.vars, but will not compile. No idea why. diff --git a/code/controllers/subsystems/vote.dm b/code/controllers/subsystems/vote.dm index 4e6acafd3f0..fb2a258791c 100644 --- a/code/controllers/subsystems/vote.dm +++ b/code/controllers/subsystems/vote.dm @@ -64,6 +64,7 @@ SUBSYSTEM_DEF(vote) /datum/controller/subsystem/vote/proc/initiate_vote(vote_type, mob/creator, automatic = 0) set waitfor = FALSE if(active_vote) + to_chat(creator, SPAN_WARNING("There is already a vote in progress.")) return FALSE if(!automatic && (!istype(creator) || !creator.client)) return FALSE @@ -71,10 +72,12 @@ SUBSYSTEM_DEF(vote) if(last_started_time != null && !(is_admin(creator) || automatic)) var/next_allowed_time = (last_started_time + get_config_value(/decl/config/num/vote_delay)) if(next_allowed_time > world.time) + to_chat(creator, SPAN_WARNING("Another vote cannot be run so soon.")) return FALSE var/datum/vote/new_vote = new vote_type if(!new_vote.setup(creator, automatic)) + to_chat(creator, SPAN_WARNING("The selected vote could not be set up or run.")) return FALSE active_vote = new_vote diff --git a/code/controllers/subsystems/weather_atoms.dm b/code/controllers/subsystems/weather_atoms.dm new file mode 100644 index 00000000000..d03ad6c63e2 --- /dev/null +++ b/code/controllers/subsystems/weather_atoms.dm @@ -0,0 +1,55 @@ +SUBSYSTEM_DEF(weather_atoms) + name = "Weather Atoms" + wait = 2 SECONDS + priority = SS_PRIORITY_WEATHER + flags = SS_NO_INIT + var/list/weather_atoms = list() + var/list/processing_atoms + + // Predeclared vars for processing. + var/atom/atom + var/obj/abstract/weather_system/weather + var/decl/state/weather/weather_state + +/datum/controller/subsystem/weather_atoms/stat_entry() + ..("A:[weather_atoms.len]") + +/datum/controller/subsystem/weather_atoms/fire(resumed = 0) + if(!resumed) + processing_atoms = weather_atoms.Copy() + + atom = null + weather = null + weather_state = null + + var/i = 0 + while(i < processing_atoms.len) + i++ + atom = processing_atoms[i] + + // Atom is null or doesn't exist, remove it from processing. + if(QDELETED(atom)) + weather_atoms -= atom + continue + + // Not outside, or not on a turf with a Z- weather is not relevant. + if(!atom.z || !atom.is_outside()) + continue + + // If weather does not exist, we don't care. + weather = atom.get_affecting_weather() + weather_state = weather?.weather_system?.current_state + if(!istype(weather_state)) + continue + + // Process the atom and return early if needed. + if(atom.process_weather(weather, weather_state) == PROCESS_KILL) + weather_atoms -= atom + if (MC_TICK_CHECK) + processing_atoms.Cut(1, i+1) + return + + processing_atoms.Cut() + +/atom/proc/process_weather(obj/abstract/weather_system/weather, decl/state/weather/weather_state) + return PROCESS_KILL diff --git a/code/controllers/subsystems/zcopy.dm b/code/controllers/subsystems/zcopy.dm index 5c9ccfbfcbb..1f550d0aeeb 100644 --- a/code/controllers/subsystems/zcopy.dm +++ b/code/controllers/subsystems/zcopy.dm @@ -198,7 +198,8 @@ SUBSYSTEM_DEF(zcopy) calculate_zstack_limits() for (var/zlev in 1 to world.maxz) - for (var/turf/T in block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev))) + var/datum/level_data/level = SSmapping.levels_by_z[zlev] + for (var/turf/T as anything in block(level.level_inner_min_x, level.level_inner_min_y, zlev, level.level_inner_max_x, level.level_inner_max_y)) if (T.z_flags & ZM_MIMIC_BELOW) flush_z_state(T) T.below = GetAbove(T) @@ -495,10 +496,10 @@ SUBSYSTEM_DEF(zcopy) ASSERT(!QDELETED(object)) var/turf/Tloc = object.loc - if (!isturf(Tloc) || !Tloc.above) + if (!isturf(Tloc) || !MOVABLE_SHALL_MIMIC(object)) return TRUE - var/turf/T = Tloc.above + var/turf/T = GetAbove(Tloc) ZM_RECORD_START diff --git a/code/datums/ai/_ai.dm b/code/datums/ai/_ai.dm index 22ff8d65539..6e40fd7ab35 100644 --- a/code/datums/ai/_ai.dm +++ b/code/datums/ai/_ai.dm @@ -44,6 +44,8 @@ /// What directions can we wander in? Uses global.cardinal if unset. var/list/wander_directions + /// Should we retaliate/startle when grabbed or buckled? + var/spooked_by_grab = TRUE /// Can we automatically escape from buckling? var/can_escape_buckles = FALSE @@ -109,12 +111,15 @@ // This is the place to actually do work in the AI. /datum/mob_controller/proc/do_process() SHOULD_CALL_PARENT(TRUE) - if(!body || QDELETED(body)) - return - if(!body.stat) - try_unbuckle() - try_wander() - try_bark() + if(!QDELETED(body) && !QDELETED(src)) + if(!body.stat) + try_unbuckle() + try_wander() + try_bark() + // Recheck in case we walked into lava or something during wandering. + return !QDELETED(body) && !QDELETED(src) + return TRUE + return FALSE // The mob will try to unbuckle itself from nets, beds, chairs, etc. /datum/mob_controller/proc/try_unbuckle() @@ -140,18 +145,19 @@ // The mob will periodically sit up or step 1 tile in a random direction. /datum/mob_controller/proc/try_wander() //Movement + if(stop_wander || body.buckled_mob || !do_wander || body.anchored) + return if(body.current_posture?.prone) if(!body.incapacitated()) body.set_posture(/decl/posture/standing) - else if(!stop_wander && !body.buckled_mob && do_wander && !body.anchored) - if(isturf(body.loc) && !body.current_posture?.prone) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_wander++ - if(turns_since_wander >= turns_per_wander && (!(stop_wander_when_pulled) || !LAZYLEN(body.grabbed_by))) //Some animals don't move when pulled - var/direction = pick(wander_directions || global.cardinal) - var/turf/move_to = get_step(body.loc, direction) - if(body.turf_is_safe(move_to)) - body.SelfMove(direction) - turns_since_wander = 0 + else if(isturf(body.loc)) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_wander++ + if(turns_since_wander >= turns_per_wander && (!(stop_wander_when_pulled) || !LAZYLEN(body.grabbed_by))) //Some animals don't move when pulled + var/direction = pick(wander_directions || global.cardinal) + var/turf/move_to = get_step(body.loc, direction) + if(body.turf_is_safe(move_to)) + body.SelfMove(direction) + turns_since_wander = 0 // The mob will periodically make a noise or perform an emote. /datum/mob_controller/proc/try_bark() @@ -215,10 +221,18 @@ /datum/mob_controller/proc/open_fire() return +/datum/mob_controller/proc/startle() + if(QDELETED(body) || body.stat != UNCONSCIOUS) + return + body.set_stat(CONSCIOUS) + if(body.current_posture?.prone) + body.set_posture(/decl/posture/standing) + /datum/mob_controller/proc/retaliate(atom/source) SHOULD_CALL_PARENT(TRUE) - if(!istype(body) || body.stat) + if(!istype(body) || body.stat == DEAD) return FALSE + startle() if(isliving(source)) remove_friend(source) return TRUE @@ -247,6 +261,11 @@ /datum/mob_controller/proc/check_memory(mob/speaker, message) return FALSE +/// General-purpose scooping reaction proc, used by /passive. +/// Returns TRUE if the scoop should proceed, FALSE if it should be canceled. +/datum/mob_controller/proc/scooped_by(mob/initiator) + return TRUE + // Enemy tracking - used on /aggressive /datum/mob_controller/proc/get_enemies() return _enemies @@ -292,3 +311,22 @@ /datum/mob_controller/proc/is_friend(mob/friend) . = istype(friend) && LAZYLEN(_friends) && (weakref(friend) in _friends) + +// By default, randomize the target area a bit to make armor/combat +// a bit more dynamic (and avoid constant organ damage to the chest) +/datum/mob_controller/proc/update_target_zone() + if(body) + return body.set_target_zone(ran_zone()) + return FALSE + +/datum/mob_controller/proc/on_buckled(mob/scary_grabber) + if(!scary_grabber || body.buckled_mob != scary_grabber) // the buckle got cancelled somehow? + return + if(spooked_by_grab && !is_friend(scary_grabber)) + retaliate(scary_grabber) + +/datum/mob_controller/proc/on_grabbed(mob/scary_grabber) + if(!scary_grabber) + return + if(spooked_by_grab && !is_friend(scary_grabber)) + retaliate(scary_grabber) \ No newline at end of file diff --git a/code/datums/ai/aggressive.dm b/code/datums/ai/aggressive.dm index 7dcc9280f9f..cb90d87d4f6 100644 --- a/code/datums/ai/aggressive.dm +++ b/code/datums/ai/aggressive.dm @@ -28,8 +28,8 @@ return ..() /datum/mob_controller/aggressive/do_process() - . = ..() - if(QDELETED(body) || body.stat) + + if(!(. = ..())) return if(!body.can_act()) @@ -92,10 +92,14 @@ return TRUE /datum/mob_controller/aggressive/proc/attack_target() + + set waitfor = FALSE + var/atom/target = get_target() if(!istype(target)) lose_target() return + if(isliving(target) && body.buckled_mob == target && (!body.faction || body.buckled_mob.faction != body.faction)) body.visible_message(SPAN_DANGER("\The [body] attempts to unseat \the [body.buckled_mob]!")) body.set_dir(pick(global.cardinal)) @@ -107,11 +111,21 @@ var/mob/living/victim = target SET_STATUS_MAX(victim, STAT_WEAK, 3) return target - if(body.Adjacent(target)) - body.a_intent = I_HURT - body.ClickOn(target) + + if(!body.Adjacent(target)) + return target + + // AI-driven mobs have a melee telegraph that needs to be handled here. + if(!body.do_attack_windup_checking(target)) return target + if(QDELETED(body) || body.incapacitated() || QDELETED(target)) + return target + + body.a_intent = I_HURT + body.ClickOn(target) + return target + /datum/mob_controller/aggressive/destroy_surroundings() if(!body.can_act()) @@ -166,7 +180,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) @@ -174,27 +188,25 @@ if(!(. = ..())) return - if(!only_attack_enemies) - if(source) - set_target(source) - move_to_target(move_only = TRUE) - return - - var/list/allies - var/list/around = view(body, 7) - for(var/atom/movable/A in around) - if(A == body || !isliving(A)) - continue - var/mob/living/M = A - if(attack_same_faction || M.faction != body.faction) - add_enemy(M) - else if(istype(M.ai)) - LAZYADD(allies, M.ai) - - var/list/enemies = get_enemies() - if(LAZYLEN(enemies) && LAZYLEN(allies)) - for(var/datum/mob_controller/ally as anything in allies) - ally.add_enemies(enemies) + if(only_attack_enemies) + var/list/allies + var/list/around = view(body, 7) + for(var/atom/movable/A in around) + if(A == body || !isliving(A)) + continue + var/mob/living/M = A + if(attack_same_faction || M.faction != body.faction) + add_enemy(M) + else if(istype(M.ai)) + LAZYADD(allies, M.ai) + var/list/enemies = get_enemies() + if(LAZYLEN(enemies) && LAZYLEN(allies)) + for(var/datum/mob_controller/ally as anything in allies) + ally.add_enemies(enemies) + + if(source) + set_target(source) + move_to_target(move_only = TRUE) /datum/mob_controller/aggressive/move_to_target(var/move_only = FALSE) if(!body.can_act()) diff --git a/code/datums/ai/beast.dm b/code/datums/ai/beast.dm index f0c081734ef..91ad1fec7a5 100644 --- a/code/datums/ai/beast.dm +++ b/code/datums/ai/beast.dm @@ -6,7 +6,8 @@ /datum/mob_controller/aggressive/beast/do_process(time_elapsed) - . = ..() + if(!(. = ..())) + return var/mob/living/simple_animal/hostile/beast/beast = body if(!istype(beast)) diff --git a/code/datums/ai/commanded.dm b/code/datums/ai/commanded.dm index 8937a654565..e4452782a84 100644 --- a/code/datums/ai/commanded.dm +++ b/code/datums/ai/commanded.dm @@ -10,7 +10,10 @@ var/retribution = TRUE /datum/mob_controller/aggressive/commanded/do_process(time_elapsed) - ..() + + if(!(. = ..()) || body.stat) + return + while(command_buffer.len > 1) var/mob/speaker = command_buffer[1] var/message = command_buffer[2] @@ -19,6 +22,7 @@ var/substring = copytext(message,length(filtered_name)+1) //get rid of the name. listen(speaker,substring) command_buffer.Remove(command_buffer[1],command_buffer[2]) + switch(stance) if(STANCE_COMMANDED_FOLLOW) follow_target() @@ -49,7 +53,7 @@ set_target(null) //want me to attack something? Well I better forget my old target. set_stance(STANCE_IDLE) body.stop_automove() - if(message == "attack" || findtext(message,"everyone") || findtext(message,"anybody") || findtext(message, "somebody") || findtext(message, "someone")) //if its just 'attack' then just attack anybody, same for if they say 'everyone', somebody, anybody. Assuming non-pickiness. + if(message == "attack" || findtext(message,"everyone") || findtext(message,"anybody") || findtext(message, "somebody") || findtext(message, "someone")) //if it's just 'attack' then just attack anybody, same for if they say 'everyone', somebody, anybody. Assuming non-pickiness. _allowed_targets = list("everyone")//everyone? EVERYONE return 1 var/list/targets = get_targets_by_name(message) diff --git a/code/datums/ai/human.dm b/code/datums/ai/human.dm index 7e34363d9a3..59d25d1d34e 100644 --- a/code/datums/ai/human.dm +++ b/code/datums/ai/human.dm @@ -8,7 +8,8 @@ if(H.stat != CONSCIOUS) return - . = ..() + if(!(. = ..())) + return if(H.get_shock() && H.shock_stage < 40 && prob(1.5)) H.emote(pick(/decl/emote/audible/moan, /decl/emote/audible/groan)) @@ -33,20 +34,20 @@ if(dam > maxdam && (maxdam == 0 || prob(50)) ) damaged_organ = E maxdam = dam - var/decl/pronouns/G = H.get_pronouns() + var/decl/pronouns/pronouns = H.get_pronouns() if(damaged_organ) if(damaged_organ.status & ORGAN_BLEEDING) - H.custom_emote("clutches [G.his] [damaged_organ.name], trying to stop the blood.") + H.custom_emote("clutches [pronouns.his] [damaged_organ.name], trying to stop the blood.") else if(damaged_organ.status & ORGAN_BROKEN) - H.custom_emote("holds [G.his] [damaged_organ.name] carefully.") + H.custom_emote("holds [pronouns.his] [damaged_organ.name] carefully.") else if(damaged_organ.burn_dam > damaged_organ.brute_dam && damaged_organ.organ_tag != BP_HEAD) - H.custom_emote("blows on [G.his] [damaged_organ.name] carefully.") + H.custom_emote("blows on [pronouns.his] [damaged_organ.name] carefully.") else - H.custom_emote("rubs [G.his] [damaged_organ.name] carefully.") + H.custom_emote("rubs [pronouns.his] [damaged_organ.name] carefully.") for(var/obj/item/organ/I in H.get_internal_organs()) if((I.status & ORGAN_DEAD) || BP_IS_PROSTHETIC(I)) continue if(I.damage > 2 && prob(1)) var/obj/item/organ/external/parent = GET_EXTERNAL_ORGAN(H, I.parent_organ) - H.custom_emote("clutches [G.his] [parent.name]!") + H.custom_emote("clutches [pronouns.his] [parent.name]!") diff --git a/code/datums/ai/hunter.dm b/code/datums/ai/hunter.dm index 35c7236eb59..b7715277783 100644 --- a/code/datums/ai/hunter.dm +++ b/code/datums/ai/hunter.dm @@ -50,9 +50,10 @@ /datum/mob_controller/passive/hunter/do_process(time_elapsed) - ..() + if(!(. = ..())) + return - if(!body || body.incapacitated() || body.current_posture?.prone || body.buckled || flee_target || !get_target()) + if(body.incapacitated() || body.current_posture?.prone || body.buckled || flee_target || !get_target()) return var/mob/living/target = get_target() diff --git a/code/datums/ai/monkey.dm b/code/datums/ai/monkey.dm index 5023e590d81..706baf66002 100644 --- a/code/datums/ai/monkey.dm +++ b/code/datums/ai/monkey.dm @@ -7,7 +7,9 @@ /datum/mob_controller/monkey/do_process(var/time_elapsed) - . = ..() + if(!(. = ..())) + return + if(body.incapacitated()) return diff --git a/code/datums/ai/passive.dm b/code/datums/ai/passive.dm index b3320d0bc00..ae2bc1e62e4 100644 --- a/code/datums/ai/passive.dm +++ b/code/datums/ai/passive.dm @@ -8,37 +8,70 @@ //see if we should stop fleeing var/atom/flee_target_atom = flee_target?.resolve() if(istype(flee_target_atom) && (flee_target_atom.loc in view(body))) - if(body.MayMove()) - walk_away(body, flee_target_atom, 7, 2) + startle() stop_wandering() - else - flee_target = null - resume_wandering() - return !isnull(flee_target) + if(body.MayMove()) + var/static/datum/automove_metadata/_passive_flee_metadata = new( + _avoid_target = TRUE, + _acceptable_distance = 6 + ) + body.set_moving_quickly() + body.start_automove(flee_target_atom, metadata = _passive_flee_metadata) + return TRUE + + flee_target = null + body.set_moving_slowly() + body.stop_automove() + resume_wandering() + return FALSE /datum/mob_controller/passive/do_process(time_elapsed) - ..() + + if(!(. = ..())) + return // Handle fleeing from aggressors. - turns_since_scan++ - if (turns_since_scan > 10) - body.stop_automove() - turns_since_scan = 0 - if(update_targets()) - return + if(body.stat == CONSCIOUS) + turns_since_scan++ + if (turns_since_scan > 10) + turns_since_scan = 0 + if(update_targets()) + return // Handle sleeping or wandering. - if(body.stat == CONSCIOUS && prob(0.25)) - body.set_stat(UNCONSCIOUS) - do_wander = FALSE - speak_chance = 0 - else if(body.stat == UNCONSCIOUS && prob(0.5)) - body.set_stat(CONSCIOUS) - do_wander = TRUE + if(isnull(flee_target) && prob(0.5)) + if(prob(50) && body.stat == CONSCIOUS) + body.set_stat(UNCONSCIOUS) + stop_wander = TRUE + speak_chance = 0 + else if(body.stat == UNCONSCIOUS) + body.set_stat(CONSCIOUS) + stop_wander = FALSE + speak_chance = initial(speak_chance) + body.update_posture() /datum/mob_controller/passive/retaliate(atom/source) if((. = ..())) source = source || get_turf(body) if(istype(source)) flee_target = weakref(source) - turns_since_scan = 10 + update_targets() + +/datum/mob_controller/passive + var/decl/skill/scooping_skill // If overridden (on a subtype, on a map/downstream, etc) check this skill to see if scooping should succeed uncontested. + var/scooping_skill_req = SKILL_ADEPT + +/datum/mob_controller/passive/scooped_by(mob/living/initiator) + if(body.stat != CONSCIOUS) + return TRUE + if(is_friend(initiator)) + return TRUE + if(is_enemy(initiator) || (scooping_skill && initiator.skill_fail_prob(scooping_skill, 50, scooping_skill_req))) // scary, try to wriggle away + retaliate(initiator) // run! run like the wind! + if(!initiator.skill_fail_prob(SKILL_HAULING, 100, SKILL_EXPERT)) + to_chat(initiator, SPAN_WARNING("\The [body] tries to wriggle out of your grasp, but you hold on tight!")) + return TRUE + to_chat(initiator, SPAN_WARNING("\The [body] wriggles out of your grasp!")) + initiator.drop_from_inventory(body) + return FALSE + return TRUE \ No newline at end of file diff --git a/code/datums/beam.dm b/code/datums/beam.dm index c2cc3738f24..e636bc092c8 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -120,11 +120,11 @@ var/global/list/beam_icon_cache = list() // shut up chinsky I do not have a cach //Position the effect so the beam is one continous line var/a if(abs(Pixel_x)>32) - a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32) + a = Pixel_x > 0 ? round(Pixel_x/32) : ceil(Pixel_x/32) X.x += a Pixel_x %= 32 if(abs(Pixel_y)>32) - a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32) + a = Pixel_y > 0 ? round(Pixel_y/32) : ceil(Pixel_y/32) X.y += a Pixel_y %= 32 diff --git a/code/datums/callbacks.dm b/code/datums/callbacks.dm index 869a5831bed..cc2ebd14313 100644 --- a/code/datums/callbacks.dm +++ b/code/datums/callbacks.dm @@ -26,7 +26,7 @@ * ## PROC TYPEPATH SHORTCUTS * (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...) * - * ### proc defined on current(src) object OR overridden at src or any of it's parents: + * ### proc defined on current(src) object OR overridden at src or any of its parents: * PROC_REF(procname) * * `CALLBACK(src, PROC_REF(some_proc_here))` diff --git a/code/datums/cinematic.dm b/code/datums/cinematic.dm index 2d56bc1db45..86ddc55b91f 100644 --- a/code/datums/cinematic.dm +++ b/code/datums/cinematic.dm @@ -30,8 +30,8 @@ var/global/datum/cinematic/cinematic = new M.set_status(STAT_STUN, 8000) override.nuke_act(cinematic_screen, station_missed) //cinematic happens here, as does mob death. - //If its actually the end of the round, wait for it to end. - //Otherwise if its a verb it will continue on afterwards. + //If it's actually the end of the round, wait for it to end. + //Otherwise if it's a verb it will continue on afterwards. sleep(30 SECONDS) for(var/client/C in viewers) diff --git a/code/datums/config/config_toggle_on.dm b/code/datums/config/config_toggle_on.dm index 1f1910a1308..873c917f620 100644 --- a/code/datums/config/config_toggle_on.dm +++ b/code/datums/config/config_toggle_on.dm @@ -1,4 +1,3 @@ - /decl/config/toggle/on abstract_type = /decl/config/toggle/on default_value = TRUE diff --git a/code/datums/config/config_types/config_events.dm b/code/datums/config/config_types/config_events.dm index d8a103ac66b..3930a216c81 100644 --- a/code/datums/config/config_types/config_events.dm +++ b/code/datums/config/config_types/config_events.dm @@ -6,7 +6,7 @@ /decl/config/lists/event_first_run, /decl/config/lists/event_delay_lower, /decl/config/lists/event_delay_upper, - /decl/config/toggle/allow_random_events + /decl/config/toggle/on/allow_random_events ) //if objectives are disabled or not @@ -42,6 +42,6 @@ "Affect mundane, moderate, and major events respectively." ) -/decl/config/toggle/allow_random_events +/decl/config/toggle/on/allow_random_events uid = "allow_random_events" desc = "Hash out to disable random events during the round." diff --git a/code/datums/config/config_types/config_game_option.dm b/code/datums/config/config_types/config_game_option.dm index d6bc64033e7..330f993c1a0 100644 --- a/code/datums/config/config_types/config_game_option.dm +++ b/code/datums/config/config_types/config_game_option.dm @@ -36,30 +36,31 @@ /decl/config/num/movement_human uid = "human_delay" - desc = "These modify the run/walk speed of all mobs before the mob-specific modifiers are applied." + desc = "A modifier applied to the run/walk delay of human-type mobs. A higher value will result in slower movement." + default_value = -1 /decl/config/num/movement_robot uid = "robot_delay" - desc = "These modify the run/walk speed of all mobs before the mob-specific modifiers are applied." + desc = "A modifier applied to the run/walk delay of robots (cyborgs and drones). A higher value will result in slower movement." /decl/config/num/movement_animal uid = "animal_delay" - desc = "These modify the run/walk speed of all mobs before the mob-specific modifiers are applied." + desc = "A modifier applied to the run/walk delay of simple_animal type mobs (including combat drones and other simple hostile mobs). A higher value will result in slower movement." /decl/config/num/movement_run uid = "run_delay" default_value = 2 - desc = "These modify the run/walk speed of all mobs before the mob-specific modifiers are applied." + desc = "A modifier applied to the run delay of all mobs, prior to the mob-specific modifiers being applied. A higher value will result in slower movement." /decl/config/num/movement_walk uid = "walk_delay" default_value = 4 - desc = "These modify the run/walk speed of all mobs before the mob-specific modifiers are applied." + desc = "A modifier applied to the walk delay of all mobs, prior to the mob-specific modifiers being applied. A higher value will result in slower movement." /decl/config/num/movement_creep uid = "creep_delay" default_value = 6 - desc = "These modify the run/walk speed of all mobs before the mob-specific modifiers are applied." + desc = "A modifier applied to the creep delay of all mobs, prior to the mob-specific modifiers being applied. A higher value will result in slower movement." /decl/config/num/movement_glide_size uid = "glide_size_delay" diff --git a/code/datums/config/config_types/config_server.dm b/code/datums/config/config_types/config_server.dm index b2f5b2a0fcd..47239568235 100644 --- a/code/datums/config/config_types/config_server.dm +++ b/code/datums/config/config_types/config_server.dm @@ -15,6 +15,7 @@ /decl/config/num/max_maint_drones, /decl/config/num/drone_build_time, /decl/config/num/max_character_traits, + /decl/config/num/max_alternate_languages, /decl/config/text/irc_bot_host, /decl/config/text/main_irc, /decl/config/text/admin_irc, @@ -42,7 +43,6 @@ /decl/config/toggle/on/allow_ai, /decl/config/toggle/on/allow_drone_spawn, /decl/config/toggle/hub_visibility, - /decl/config/toggle/usewhitelist, /decl/config/toggle/load_jobs_from_txt, /decl/config/toggle/disable_player_mice, /decl/config/toggle/uneducated_mice, @@ -60,7 +60,8 @@ /decl/config/toggle/guests_allowed, /decl/config/toggle/on/jobs_have_minimal_access, /decl/config/toggle/on/admin_legacy_system, - /decl/config/toggle/on/ban_legacy_system + /decl/config/toggle/on/ban_legacy_system, + /decl/config/enum/server_whitelist ) /decl/config/num/kick_inactive @@ -164,6 +165,11 @@ default_value = 5 desc = "Remove the # to define a different cap for trait points in chargen." +/decl/config/num/max_alternate_languages + uid = "max_alternate_languages" + default_value = 3 + desc = "Remove the # to define a different maximum for alternate language selection in chargen." + /decl/config/text/irc_bot_host uid = "irc_bot_host" default_value = "localhost" @@ -287,13 +293,6 @@ . = ..() world.update_hub_visibility() -/decl/config/toggle/usewhitelist - uid = "usewhitelist" - desc = list( - "Set to jobban everyone who's key is not listed in data/whitelist.txt from Captain, HoS, HoP, CE, RD, CMO, Warden, Security, Detective, and AI positions.", - "Uncomment to 1 to jobban, leave commented out to allow these positions for everyone (but see GUEST_JOBBAN above and regular jobbans)." - ) - /decl/config/toggle/load_jobs_from_txt uid = "load_jobs_from_txt" desc = "Toggle for having jobs load up from the .txt" @@ -363,3 +362,14 @@ /decl/config/toggle/on/jobs_have_minimal_access uid = "jobs_have_minimal_access" desc = "Add a # here if you wish to use the setup where jobs have more access. This is intended for servers with low populations - where there are not enough players to fill all roles, so players need to do more than just one job. Also for servers where they don't want people to hide in their own departments." + +/decl/config/enum/server_whitelist + uid = "server_whitelist" + desc = "Determines how the server should handle whitelisting for ckeys. Whitelisted ckeys are found in '" + CONFIG_SERVER_WHITELIST_FILE + "'. Set to 'none' for no whitelisting, 'jobs' to whitelist sensitive jobs, 'join' to whitelist joining the round (observing and OOC are still available, or 'connect' to whitelist access to the server." + default_value = CONFIG_SERVER_NO_WHITELIST + enum_map = list( + "none" = CONFIG_SERVER_NO_WHITELIST, + "jobs" = CONFIG_SERVER_JOBS_WHITELIST, + "join" = CONFIG_SERVER_JOIN_WHITELIST, + "connect" = CONFIG_SERVER_CONNECT_WHITELIST + ) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index a1114baa8b9..0fbfeaf0232 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -19,6 +19,7 @@ // This should be overridden to remove all references pointing to the object being destroyed. // Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE. /datum/proc/Destroy(force=FALSE) + SHOULD_NOT_SLEEP(TRUE) SHOULD_CALL_PARENT(TRUE) tag = null weakref = null // Clear this reference to ensure it's kept for as brief duration as possible. @@ -26,13 +27,14 @@ if(istype(SSnano)) SSnano.close_uis(src) - var/list/timers = active_timers - active_timers = null - for(var/thing in timers) - var/datum/timedevent/timer = thing - if (timer.spent && !(timer.flags & TIMER_LOOP)) - continue - qdel(timer) + if(active_timers) + var/list/timers = active_timers + active_timers = null + for(var/thing in timers) + var/datum/timedevent/timer = thing + if (timer.spent && !(timer.flags & TIMER_LOOP)) + continue + qdel(timer) if(extensions) for(var/expansion_key in extensions) @@ -45,7 +47,7 @@ var/decl/observ/destroyed/destroyed_event = GET_DECL(/decl/observ/destroyed) // Typecheck is needed (rather than nullchecking) due to oddness with new() ordering during world creation. - if(istype(events_repository) && destroyed_event.global_listeners.len || destroyed_event.event_sources[src]) + if(istype(events_repository) && destroyed_event.event_sources[src]) RAISE_EVENT(/decl/observ/destroyed, src) if (!isturf(src)) // Not great, but the 'correct' way to do it would add overhead for little benefit. diff --git a/code/datums/extensions/abilities/abilities.dm b/code/datums/extensions/abilities/abilities.dm index 7d7ff63a6f7..3995c524dc4 100644 --- a/code/datums/extensions/abilities/abilities.dm +++ b/code/datums/extensions/abilities/abilities.dm @@ -20,7 +20,7 @@ /datum/extension/abilities/proc/do_self_invocation() if(isliving(holder) && LAZYLEN(ability_handlers)) for(var/datum/ability_handler/handler in ability_handlers) - if(handler.do_self_invocation(holder)) + if(handler.can_do_self_invocation(holder) && handler.do_self_invocation(holder)) return TRUE return FALSE @@ -28,7 +28,7 @@ /datum/extension/abilities/proc/do_grabbed_invocation(atom/target) if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers)) for(var/datum/ability_handler/handler in ability_handlers) - if(handler.do_grabbed_invocation(holder, target)) + if(handler.can_do_grabbed_invocation(holder, target) && handler.do_grabbed_invocation(holder, target)) return TRUE return FALSE @@ -36,7 +36,7 @@ /datum/extension/abilities/proc/do_melee_invocation(atom/target) if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers)) for(var/datum/ability_handler/handler in ability_handlers) - if(handler.do_melee_invocation(holder, target)) + if(handler.can_do_melee_invocation(holder, target) && handler.do_melee_invocation(holder, target)) return TRUE return FALSE @@ -44,7 +44,7 @@ /datum/extension/abilities/proc/do_ranged_invocation(atom/target) if(isliving(holder) && istype(target) && LAZYLEN(ability_handlers)) for(var/datum/ability_handler/handler in ability_handlers) - if(handler.do_ranged_invocation(holder, target)) + if(handler.can_do_ranged_invocation(holder, target) && handler.do_ranged_invocation(holder, target)) return TRUE return FALSE diff --git a/code/datums/extensions/abilities/abilities_predator.dm b/code/datums/extensions/abilities/abilities_predator.dm new file mode 100644 index 00000000000..739909b81c2 --- /dev/null +++ b/code/datums/extensions/abilities/abilities_predator.dm @@ -0,0 +1,73 @@ +/datum/ability_handler/predator + 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) + +/datum/ability_handler/predator/do_melee_invocation(mob/user, atom/target) + + // Nibbles! + if(user.a_intent == I_HURT) + if(isliving(target)) + return handle_dismemberment(user, target) + if(istype(target, /obj/item/organ)) + return handle_organ_destruction(user, target) + + // Digging! + var/static/list/diggable_types = list( + /turf/floor, + /turf/wall, + /obj/structure/pit, + /obj/machinery/portable_atmospherics/hydroponics/soil + ) + if(is_type_in_list(target, diggable_types)) + var/obj/item/organ/external/paw = user.get_usable_hand_slot_organ() + if(paw) + return target.attackby(paw, user) + + return FALSE + +/datum/ability_handler/predator/proc/handle_organ_destruction(mob/user, obj/item/organ/chewtoy) + if(!chewtoy.is_internal()) + user.visible_message(SPAN_DANGER("\The [user] tears apart \the [chewtoy].")) + chewtoy.physically_destroyed() + else if(BP_IS_PROSTHETIC(chewtoy)) + to_chat(user, SPAN_WARNING("\The [chewtoy] seems to be inedible.")) + else + user.visible_message(SPAN_DANGER("\The [user] nibbles on \the [chewtoy].")) + chewtoy.convert_to_food(user) + return TRUE + +/datum/ability_handler/predator/proc/handle_dismemberment(mob/user, mob/living/victim) + + if(victim.stat != DEAD || !victim.current_posture?.prone) + return FALSE + + if(!victim.butchery_data) + to_chat(user, SPAN_WARNING("\The [victim] appears to be inedible.")) + return TRUE + + if(victim.get_object_size() > max_dismember_size) + to_chat(user, SPAN_WARNING("\The [victim] is too big for you to dismember.")) + return TRUE + + to_chat(user, SPAN_NOTICE("You dig into \the [victim], hunting for something edible.")) + if(!do_after(user, max(2 SECONDS, victim.get_object_size() * 5), victim) || QDELETED(victim) || !victim.butchery_data || victim.stat != DEAD) + return TRUE + + var/list/external_organs = victim.get_external_organs() + if(length(external_organs) <= 1) + user.visible_message(SPAN_DANGER("\The [user] tears \the [victim] apart!")) + victim.gib() + return TRUE + + var/obj/item/organ/external/limb = victim.get_organ(user.get_target_zone()) + if(!limb) + to_chat(user, SPAN_WARNING("\The [victim] is missing that limb!")) + return TRUE + + user.visible_message(SPAN_DANGER("\The [user] tears \the [limb] from \the [victim]!")) + limb.dismember(FALSE, DISMEMBER_METHOD_EDGE, silent = TRUE) + if(!QDELETED(limb)) + user.put_in_hands(limb) + return TRUE diff --git a/code/datums/extensions/abilities/ability_handler.dm b/code/datums/extensions/abilities/ability_handler.dm index 05ad274748d..8dc7ee1e5de 100644 --- a/code/datums/extensions/abilities/ability_handler.dm +++ b/code/datums/extensions/abilities/ability_handler.dm @@ -33,14 +33,26 @@ /datum/ability_handler/proc/refresh_login() return +/datum/ability_handler/proc/can_do_self_invocation(mob/user) + return FALSE + /datum/ability_handler/proc/do_self_invocation(mob/user) return FALSE +/datum/ability_handler/proc/can_do_grabbed_invocation(mob/user, atom/target) + return FALSE + /datum/ability_handler/proc/do_grabbed_invocation(mob/user, atom/target) return FALSE +/datum/ability_handler/proc/can_do_melee_invocation(mob/user, atom/target) + return FALSE + /datum/ability_handler/proc/do_melee_invocation(mob/user, atom/target) return FALSE +/datum/ability_handler/proc/can_do_ranged_invocation(mob/user, atom/target) + return FALSE + /datum/ability_handler/proc/do_ranged_invocation(mob/user, atom/target) return FALSE diff --git a/code/datums/extensions/abilities/ability_item.dm b/code/datums/extensions/abilities/ability_item.dm index 59baf3c7a27..98bb2b8d43b 100644 --- a/code/datums/extensions/abilities/ability_item.dm +++ b/code/datums/extensions/abilities/ability_item.dm @@ -1,6 +1,6 @@ /obj/item/ability - atom_flags = 0 simulated = 1 + obj_flags = OBJ_FLAG_NO_STORAGE anchored = TRUE pickup_sound = null drop_sound = null @@ -26,9 +26,6 @@ ..() qdel(src) -/obj/item/ability/get_storage_cost() - return ITEM_SIZE_NO_CONTAINER - /obj/item/ability/attack_self(var/mob/user) user?.drop_from_inventory(src) return TRUE diff --git a/code/datums/extensions/assembly/assembly_interaction.dm b/code/datums/extensions/assembly/assembly_interaction.dm index e59c7d531da..9e250bb5d7b 100644 --- a/code/datums/extensions/assembly/assembly_interaction.dm +++ b/code/datums/extensions/assembly/assembly_interaction.dm @@ -102,4 +102,5 @@ return TRUE if(istype(W, /obj/item/stock_parts)) - return try_install_component(user, W) \ No newline at end of file + return try_install_component(user, W) + return FALSE \ No newline at end of file diff --git a/code/datums/extensions/demolisher/_demolisher.dm b/code/datums/extensions/demolisher/_demolisher.dm new file mode 100644 index 00000000000..0b5f4bc8db5 --- /dev/null +++ b/code/datums/extensions/demolisher/_demolisher.dm @@ -0,0 +1,38 @@ +// A subtype for items that can dismantle/demolish non-reinforced non-natural walls. +/datum/extension/demolisher + base_type = /datum/extension/demolisher + expected_type = /obj/item + var/demolish_delay = 6 SECONDS + var/demolish_verb + var/demolish_sound + +/datum/extension/demolisher/New(datum/holder, _delay, _verb, _sound) + . = ..() + if(_delay) + demolish_delay = _delay + if(_verb) + demolish_verb = _verb + if(_sound) + demolish_sound = _sound + +/datum/extension/demolisher/proc/get_demolish_delay(turf/wall/wall) + return demolish_delay + wall?.get_material()?.cut_delay + +/datum/extension/demolisher/proc/get_demolish_verb() + return demolish_verb + +/datum/extension/demolisher/proc/get_demolish_sound() + return demolish_sound + +/datum/extension/demolisher/proc/try_demolish(mob/user, turf/wall/wall) + var/dismantle_verb = get_demolish_verb() + var/dismantle_sound = get_demolish_sound() + to_chat(user, SPAN_NOTICE("You begin [dismantle_verb] \the [wall].")) + if(dismantle_sound) + playsound(wall, dismantle_sound, 100, 1) + var/cut_delay = max(0, get_demolish_delay(wall)) + if(do_after(user, cut_delay, wall)) + to_chat(user, SPAN_NOTICE("You finish [dismantle_verb] \the [wall].")) + user.visible_message(SPAN_DANGER("\The [user] finishes [dismantle_verb] \the [wall]!")) + wall.dismantle_turf() + return TRUE diff --git a/code/datums/extensions/demolisher/delicate.dm b/code/datums/extensions/demolisher/delicate.dm new file mode 100644 index 00000000000..a9f6dbdb543 --- /dev/null +++ b/code/datums/extensions/demolisher/delicate.dm @@ -0,0 +1,13 @@ +/datum/extension/demolisher/delicate + demolish_verb = "dismantling" + demolish_sound = 'sound/items/Crowbar.ogg' + var/alternative_tools = "a sledgehammer or welding torch" // For overriding on medieval maps. + +/datum/extension/demolisher/delicate/try_demolish(mob/user, turf/wall/wall) + if(!(wall.get_material()?.removed_by_welder)) + return ..() + to_chat(user, SPAN_WARNING("\The [wall] is too robust to be dismantled with \the [holder]; try [alternative_tools].")) + return TRUE + +/datum/extension/demolisher/delicate/get_demolish_delay(turf/wall/wall) + return ..() * 1.2 diff --git a/code/datums/extensions/demolisher/energy.dm b/code/datums/extensions/demolisher/energy.dm new file mode 100644 index 00000000000..ff4a7282de6 --- /dev/null +++ b/code/datums/extensions/demolisher/energy.dm @@ -0,0 +1,16 @@ +/datum/extension/demolisher/energy + demolish_sound = "sparks" + demolish_verb = "slicing through" + +/datum/extension/demolisher/energy/try_demolish(mob/user, turf/wall/wall) + var/obj/item/tool = holder + if(!tool.is_special_cutting_tool()) + return FALSE + if(istype(tool, /obj/item/gun/energy/plasmacutter)) + var/obj/item/gun/energy/plasmacutter/cutter = tool + if(!cutter.slice(user)) + return TRUE + return ..() + +/datum/extension/demolisher/energy/get_demolish_delay(turf/wall/wall) + return ..() * 0.5 diff --git a/code/datums/extensions/demolisher/pick.dm b/code/datums/extensions/demolisher/pick.dm new file mode 100644 index 00000000000..5b48b14b6ef --- /dev/null +++ b/code/datums/extensions/demolisher/pick.dm @@ -0,0 +1,22 @@ +/datum/extension/demolisher/pick + demolish_verb = "dismantling" + demolish_sound = 'sound/items/Crowbar.ogg' + +/datum/extension/demolisher/pick/try_demolish(mob/user, turf/wall/wall) + var/obj/item/pick = holder + if(pick.material?.hardness < wall.get_material()?.hardness) + to_chat(user, SPAN_WARNING("\The [holder] is not hard enough to cut through [wall.get_material().solid_name].")) + return TRUE + return ..() + +/datum/extension/demolisher/pick/get_demolish_delay(turf/wall/wall) + var/obj/item/pick = holder + return pick.get_expected_tool_use_delay(TOOL_PICK, ..()) + +/datum/extension/demolisher/pick/get_demolish_verb() + var/obj/item/pick = holder + return pick.get_tool_message(TOOL_PICK) + +/datum/extension/demolisher/pick/get_demolish_sound() + var/obj/item/pick = holder + return pick.get_tool_sound(TOOL_PICK) diff --git a/code/datums/extensions/demolisher/welder.dm b/code/datums/extensions/demolisher/welder.dm new file mode 100644 index 00000000000..b6a43fc85b0 --- /dev/null +++ b/code/datums/extensions/demolisher/welder.dm @@ -0,0 +1,16 @@ +/datum/extension/demolisher/welder + demolish_verb = "cutting through" + demolish_sound = 'sound/items/Welder.ogg' + expected_type = /obj/item/weldingtool + +/datum/extension/demolisher/welder/try_demolish(mob/user, turf/wall/wall) + if(!(wall.get_material()?.removed_by_welder)) + to_chat(user, SPAN_WARNING("\The [wall] is too delicate to be dismantled with \the [holder]; try a hammer or crowbar.")) + return TRUE + var/obj/item/weldingtool/welder = holder + if(welder.weld(0,user)) + return ..() + return TRUE + +/datum/extension/demolisher/welder/get_demolish_delay(turf/wall/wall) + return ..() * 0.7 diff --git a/code/datums/extensions/extensions.dm b/code/datums/extensions/extensions.dm index faad1fff468..196d396e30a 100644 --- a/code/datums/extensions/extensions.dm +++ b/code/datums/extensions/extensions.dm @@ -73,7 +73,16 @@ return get_extension(source, base_type) /proc/get_extension(var/datum/source, var/base_type) - if(!istype(source) || !source.extensions || isnull(base_type)) + // Invalid holder datum. + if(!istype(source)) + PRINT_STACK_TRACE("Invalid source provided to get_extension(): [source || "null"]") + return + // Invalid extension type. + if(!ispath(base_type, /datum/extension)) + PRINT_STACK_TRACE("Invalid base_type provided to get_extension(): [base_type || "null"]") + return + // Return early if we have no extensions whatsoever. + if(!source.extensions) return . = source.extensions[base_type] if(!.) diff --git a/code/datums/extensions/eye/landing.dm b/code/datums/extensions/eye/landing.dm index 435124688d0..b54a722cb4f 100644 --- a/code/datums/extensions/eye/landing.dm +++ b/code/datums/extensions/eye/landing.dm @@ -23,4 +23,16 @@ name = "Offset landing location" procname = TYPE_PROC_REF(/mob/observer/eye/landing, toggle_offsetting) button_icon_state = "shuttle_offset" + target_type = EYE_TARGET + +/datum/action/eye/landing/rotate_cw + name = "Rotate clockwise" + procname ="turn_shuttle_cw" + button_icon_state = "shuttle_rotate_cw" + target_type = EYE_TARGET + +/datum/action/eye/landing/rotate_ccw + name = "Rotate counterclockwise" + procname ="turn_shuttle_ccw" + button_icon_state = "shuttle_rotate_ccw" target_type = EYE_TARGET \ No newline at end of file diff --git a/code/datums/extensions/holster/holster.dm b/code/datums/extensions/holster/holster.dm index f83a5ac67a9..88ddb7399b9 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/lockable.dm b/code/datums/extensions/lockable.dm index d3c9aee3cb2..095522d2f9b 100644 --- a/code/datums/extensions/lockable.dm +++ b/code/datums/extensions/lockable.dm @@ -132,8 +132,8 @@ Returns null if there are no problems. Or a text string describing the problem otherwise. */ /datum/extension/lockable/proc/keycode_issues_text(code) - if(length(code) < FLOOR(max_code_length * 0.5) || length(code) > max_code_length) - return "Keycode must be between [FLOOR(max_code_length * 0.5)] and [max_code_length] numbers long." + if(length(code) < floor(max_code_length * 0.5) || length(code) > max_code_length) + return "Keycode must be between [floor(max_code_length * 0.5)] and [max_code_length] numbers long." return null //Return null, since we have no issues /** @@ -324,7 +324,7 @@ if(!locked) return FALSE - if(!used_item.user_can_wield(user)) + if(!used_item.user_can_attack_with(user)) return TRUE //TODO: This probably should be handled in a better way. diff --git a/code/datums/extensions/milkable/milkable.dm b/code/datums/extensions/milkable/milkable.dm new file mode 100644 index 00000000000..debc42f4853 --- /dev/null +++ b/code/datums/extensions/milkable/milkable.dm @@ -0,0 +1,139 @@ +/datum/extension/milkable + base_type = /datum/extension/milkable + expected_type = /mob/living + flags = EXTENSION_FLAG_IMMEDIATE + + var/milk_type = /decl/material/liquid/drink/milk + var/milk_prob = 5 + var/milk_min = 5 + var/milk_max = 10 + + var/impatience = 0 + var/decl/skill/milking_skill = SKILL_BOTANY + var/milking_skill_req = SKILL_BASIC + + var/datum/reagents/udder + +/datum/extension/milkable/New(datum/holder, _milk_type) + ..() + if(ispath(_milk_type, /decl/material)) + milk_type = _milk_type + if(isatom(holder)) + udder = new /datum/reagents(50, holder) + START_PROCESSING(SSprocessing, src) + +/datum/extension/milkable/Destroy() + STOP_PROCESSING(SSprocessing, src) + QDEL_NULL(udder) + return ..() + +/datum/extension/milkable/Process() + if(!isatom(holder) || QDELETED(holder) || QDELETED(src) || QDELETED(udder)) + return PROCESS_KILL + + var/mob/living/critter = holder + if(critter.stat == DEAD) + return PROCESS_KILL + + if(critter.stat == CONSCIOUS && !critter.get_automove_target() && impatience > 0 && prob(10)) // if not fleeing, 10% chance to regain patience + impatience-- + + if(prob(milk_prob)) + create_milk() + +/datum/extension/milkable/proc/create_milk() + var/create_milk = min(rand(milk_min, milk_max), REAGENTS_FREE_SPACE(udder)) + if(create_milk > 0) + udder.add_reagent(milk_type, create_milk, get_milk_data()) + +/datum/extension/milkable/proc/get_milk_data() + var/static/list/milk_data = list( + DATA_MILK_DONOR = "cow" + ) + return milk_data.Copy() + +// Return TRUE if attackby() should halt at this call. +/datum/extension/milkable/proc/handle_milked(obj/item/chems/container, mob/user) + + if(!istype(container) || !ATOM_IS_OPEN_CONTAINER(container)) + return FALSE + + var/mob/living/critter = holder + if(critter.stat == DEAD) + return FALSE + + if(udder?.total_volume <= 0) + to_chat(user, SPAN_WARNING("\The [critter]'s udder is dry. Wait a little longer.")) + return TRUE + + if(critter.get_automove_target()) + if(user.skill_check(milking_skill, SKILL_PROF)) + to_chat(user, SPAN_NOTICE("\The [critter] goes still at your touch.")) + critter.stop_automove() + else + to_chat(user, SPAN_WARNING("Wait for \the [critter] to stop moving before you try milking it.")) + return TRUE + + if(container.reagents.total_volume >= container.volume) + to_chat(user, SPAN_WARNING("\The [container] is full.")) + return TRUE + + // Cows don't like being milked if you're unskilled. + if(user.skill_fail_prob(milking_skill, 40, milking_skill_req)) + handle_milking_failure(user, critter) + return TRUE + + user.visible_message( + SPAN_NOTICE("\The [user] starts milking \the [critter] into \the [container]."), + SPAN_NOTICE("You start milking \the [critter] into \the [container].") + ) + if(!user.do_skilled(4 SECONDS, milking_skill, target = critter, check_holding = TRUE)) + user.visible_message( + SPAN_NOTICE("\The [user] stops milking \the [critter]."), + SPAN_NOTICE("You stop milking \the [critter].") + ) + return TRUE + + if(critter.stat == DEAD) + return FALSE + + if(udder?.total_volume <= 0) + to_chat(user, SPAN_WARNING("\The [critter]'s udder is dry. Wait a little longer.")) + return TRUE + + if(container.reagents.total_volume >= container.volume) + to_chat(user, SPAN_NOTICE("\The [container] is full.")) + return TRUE + + if(critter.get_automove_target()) + to_chat(user, SPAN_WARNING("Wait for \the [critter] to stop moving before you try milking it.")) + return TRUE + + user.visible_message( + SPAN_NOTICE("\The [user] milks \the [critter] into \the [container]."), + SPAN_NOTICE("You milk \the [critter] into \the [container].") + ) + udder.trans_type_to(container, milk_type, min(REAGENTS_FREE_SPACE(container.reagents), rand(15, 20))) + return TRUE + +/datum/extension/milkable/proc/handle_milking_failure(mob/user, mob/living/critter) + if(impatience > 3) + critter.visible_message(SPAN_WARNING("\The [critter] bellows and flees from \the [user]!")) + critter.flee(user, upset = TRUE) + else + critter.visible_message(SPAN_WARNING("\The [critter] huffs and moves away from \the [user].")) + critter.flee(user, upset = FALSE) + impatience++ + +/datum/extension/milkable/goat/handle_milking_failure(mob/user, mob/living/critter) + critter?.ai?.retaliate() + +/datum/extension/milkable/goat/get_milk_data() + var/static/list/milk_data = list( + DATA_MILK_DONOR = "goat", + DATA_MILK_NAME = "goat", + DATA_CHEESE_NAME = "feta", + DATA_CHEESE_COLOR = "#f3f2be", + DATA_MASK_NAME = "goat's milk", + ) + return milk_data.Copy() diff --git a/code/datums/extensions/resistable/resistable.dm b/code/datums/extensions/resistable/resistable.dm new file mode 100644 index 00000000000..9e6fa45299a --- /dev/null +++ b/code/datums/extensions/resistable/resistable.dm @@ -0,0 +1,85 @@ +// TODO: extend this to cover buckled structures +/datum/extension/resistable + base_type = /datum/extension/resistable + expected_type = /obj/item + +/datum/extension/resistable/proc/get_restraint_escape_time(mob/living/user) + return 2 MINUTES + +/datum/extension/resistable/proc/user_try_escape(mob/living/user, slot) + + set waitfor = FALSE + + var/obj/item/restraint = holder + if(!istype(user) || QDELETED(user) || QDELETED(restraint) || restraint.loc != user || user.buckled) + return FALSE + + // Don't want to do a lot of logic gating here. + if(user.can_break_restraints()) + return user.break_restraints(restraint) + + var/breakouttime = get_restraint_escape_time(user) + breakouttime = max(5, breakouttime * user.get_restraint_breakout_mod()) + user.setClickCooldown(breakouttime) + + user.visible_message( + SPAN_DANGER("\The [user] attempts to remove \the [restraint]!"), + SPAN_DANGER("You attempt to remove \the [restraint] (This will take around [ceil(breakouttime / (1 SECOND))] second\s and you need to stand still)."), + range = 2 + ) + + var/static/resist_stages = 4 + for(var/i = 1 to resist_stages) + if(!do_after(user, breakouttime*0.25, incapacitation_flags = (INCAPACITATION_DEFAULT & ~INCAPACITATION_RESTRAINED))) + user.visible_message( + SPAN_WARNING("\The [user] stops fiddling with \the [restraint]."), + SPAN_WARNING("You stop trying to slip free of \the [restraint]."), + range = 2 + ) + return FALSE + + var/new_restraint = user.get_equipped_item(slot) + if((restraint != new_restraint) || user.buckled) + return FALSE + user.visible_message( + SPAN_WARNING("\The [user] fiddles with \the [restraint]."), + SPAN_WARNING("You try to slip free of \the [restraint] ([i*100/resist_stages]% done)."), + range = 2 + ) + + if (restraint.can_take_damage() && restraint.current_health > 0) // Improvised restraint can break because their health is > 0 + restraint.take_damage(restraint.get_max_health() / 2) + if (QDELETED(restraint) || restraint.current_health < 1) + var/decl/pronouns/pronouns = restraint.get_pronouns() + user.visible_message( + SPAN_DANGER("\The [user] manages to remove \the [restraint], breaking [pronouns.him]!"), + SPAN_NOTICE("You successfully remove \the [restraint], breaking [pronouns.him]!"), + range = 2 + ) + QDEL_NULL(restraint) + if(user.buckled && user.buckled.buckle_require_restraints) + user.buckled.unbuckle_mob() + user.update_equipment_overlay(slot) + return + user.visible_message( + SPAN_WARNING("\The [user] manages to remove \the [restraint]!"), + SPAN_NOTICE("You successfully remove \the [restraint]!"), + range = 2 + ) + user.drop_from_inventory(restraint) + +// Specific item subtypes/logic below. +/datum/extension/resistable/handcuffs + expected_type = /obj/item/handcuffs + +/datum/extension/resistable/handcuffs/get_restraint_escape_time(mob/living/user) + var/obj/item/handcuffs/cuffs = holder + . = cuffs.breakouttime + if(istype(user?.get_equipped_item(slot_gloves_str), /obj/item/clothing/gloves/rig)) + . = round(.*0.5) + +/datum/extension/resistable/straightjacket + expected_type = /obj/item/clothing/suit/straight_jacket + +/datum/extension/resistable/straightjacket/get_restraint_escape_time(mob/living/user) + return 5 MINUTES diff --git a/code/datums/extensions/scent/_scent.dm b/code/datums/extensions/scent/_scent.dm index 4fe2a1064cd..cdf60ec83e0 100644 --- a/code/datums/extensions/scent/_scent.dm +++ b/code/datums/extensions/scent/_scent.dm @@ -128,7 +128,7 @@ To add a scent extension to an atom using a reagent's info, where R. is the reag // Returns the smelliest reagent of a reagent holder. /proc/get_smelliest_reagent(var/datum/reagents/holder) var/decl/material/smelliest - var/decl/material/scent_intensity + var/scent_intensity if(!holder || !holder.total_volume) return for(var/reagent_type in holder.reagent_volumes) diff --git a/code/datums/extensions/shearable/shearable.dm b/code/datums/extensions/shearable/shearable.dm new file mode 100644 index 00000000000..c94d40a8669 --- /dev/null +++ b/code/datums/extensions/shearable/shearable.dm @@ -0,0 +1,108 @@ +/datum/extension/shearable + base_type = /datum/extension/shearable + expected_type = /mob/living + flags = EXTENSION_FLAG_IMMEDIATE + + var/has_fleece = FALSE + var/next_fleece = 0 + var/fleece_time = 5 MINUTES + var/fleece_type = /obj/item/fleece + var/decl/material/fleece_material = /decl/material/solid/organic/cloth/wool + + var/decl/skill/shearing_skill = SKILL_BOTANY + var/shearing_skill_req = SKILL_BASIC + +/datum/extension/shearable/New(datum/holder, _fleece_material) + . = ..() + if(ispath(_fleece_material, /decl/material)) + fleece_material = _fleece_material + if(ispath(fleece_material, /decl/material)) + fleece_material = GET_DECL(fleece_material) + START_PROCESSING(SSprocessing, src) + +/datum/extension/shearable/Destroy() + STOP_PROCESSING(SSprocessing, src) + return ..() + +/datum/extension/shearable/Process() + if(has_fleece) + return PROCESS_KILL + if(world.time >= next_fleece) + + has_fleece = TRUE + var/mob/living/critter = holder + + // Update fleeced simple animals with overlay. + if(istype(holder, /mob/living/simple_animal)) + var/fleece_state = "[critter.icon_state]-fleece" + if(check_state_in_icon(fleece_state, critter.icon)) + var/mob/living/simple_animal/animal = critter + LAZYSET(animal.draw_visible_overlays, "fleece", fleece_material.color) + + critter.try_refresh_visible_overlays() + return PROCESS_KILL + +/datum/extension/shearable/proc/handle_sheared(obj/item/shears, mob/user) + + var/mob/living/critter = holder + if(!has_fleece) + to_chat(user, SPAN_WARNING("\The [critter] is not ready to be shorn again yet.")) + return TRUE + + if(critter.get_automove_target()) + if(user.skill_check(shearing_skill, SKILL_PROF)) + to_chat(user, SPAN_NOTICE("\The [critter] goes still at your touch.")) + critter.stop_automove() + else + to_chat(user, SPAN_WARNING("Wait for \the [critter] to stop moving before you try shearing it.")) + return TRUE + + // Cows don't like being milked if you're unskilled. + if(user.skill_fail_prob(shearing_skill, 40, shearing_skill_req)) + handle_shearing_failure(user, critter) + return TRUE + + user.visible_message( + SPAN_NOTICE("\The [user] starts shearing \the [critter]."), + SPAN_NOTICE("You start shearing \the [critter].") + ) + if(!user.do_skilled(4 SECONDS, shearing_skill)) + user.visible_message( + SPAN_NOTICE("\The [user] stops shearing \the [critter]."), + SPAN_NOTICE("You stop shearing \the [critter].") + ) + return TRUE + + if(QDELETED(user) || QDELETED(critter) || QDELETED(shears) || user.get_active_held_item() != shears) + return TRUE + + if(critter.get_automove_target()) + to_chat(user, SPAN_WARNING("Wait for \the [critter] to stop moving before you try shearing it.")) + return TRUE + + if(!has_fleece) + to_chat(user, SPAN_WARNING("\The [critter] is not ready to be shorn again yet.")) + return TRUE + + user.visible_message( + SPAN_NOTICE("\The [user] finishes shearing \the [critter]."), + SPAN_NOTICE("You finish shearing \the [critter].") + ) + + new fleece_type(get_turf(critter), fleece_material.type, critter) + + has_fleece = FALSE + next_fleece = world.time + fleece_time + + // Update fleeced simple animals with overlay. + if(istype(holder, /mob/living/simple_animal)) + var/mob/living/simple_animal/animal = holder + LAZYREMOVE(animal.draw_visible_overlays, "fleece") + + if(!is_processing) + START_PROCESSING(SSprocessing, src) + critter.try_refresh_visible_overlays() + return TRUE + +/datum/extension/shearable/proc/handle_shearing_failure(mob/user, mob/living/critter) + critter?.ai?.retaliate() diff --git a/code/datums/extensions/state_machine.dm b/code/datums/extensions/state_machine.dm index 32b8d6cd948..63e58f0dd67 100644 --- a/code/datums/extensions/state_machine.dm +++ b/code/datums/extensions/state_machine.dm @@ -6,16 +6,15 @@ var/global/list/state_machines = list() var/list/machines = global.state_machines["\ref[holder]"] return islist(machines) && machines[base_type] -/proc/add_state_machine(var/datum/holder, var/base_type, var/fsm_type) - if(istype(holder) && 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] if(!islist(machines)) machines = list() global.state_machines[holder_ref] = machines + var/base_type = fsm_type::base_type if(!machines[base_type]) - if(!fsm_type) - fsm_type = base_type var/datum/state_machine/machine = new fsm_type(holder) machines[base_type] = machine holder.has_state_machine = TRUE @@ -43,8 +42,8 @@ var/global/list/state_machines = list() /datum/state_machine/New(var/datum/_holder) ..() - if(!istype(_holder)) - PRINT_STACK_TRACE("Non-datum holder supplied to [type] New().") + if(!istype(_holder, expected_type)) + PRINT_STACK_TRACE("Non-[expected_type] holder supplied to [type] New().") else holder_ref = weakref(_holder) set_state(current_state) diff --git a/code/datums/extensions/storage/_storage.dm b/code/datums/extensions/storage/_storage.dm index 36923527cb8..e6200fdaf08 100644 --- a/code/datums/extensions/storage/_storage.dm +++ b/code/datums/extensions/storage/_storage.dm @@ -42,8 +42,8 @@ var/global/list/_test_storage_items = list() global._test_storage_items += src #endif - if(!istype(_holder)) - PRINT_STACK_TRACE("Storage datum initialized with non-atom holder '[_holder || "NULL"].") + if(!istype(_holder, expected_type)) + PRINT_STACK_TRACE("Storage datum initialized with non-[expected_type] holder '[_holder || "NULL"].") qdel(src) return @@ -124,7 +124,7 @@ var/global/list/_test_storage_items = list() //This proc return 1 if the item can be picked up and 0 if it can't. //Set the stop_messages to stop it from printing messages -/datum/storage/proc/can_be_inserted(obj/item/W, mob/user, stop_messages = 0) +/datum/storage/proc/can_be_inserted(obj/item/W, mob/user, stop_messages = 0, click_params = null) if(!istype(W)) return //Not an item if(user && !user.canUnEquip(W)) @@ -177,13 +177,12 @@ var/global/list/_test_storage_items = list() to_chat(user, SPAN_WARNING("\The [W] is too big for \the [holder].")) return 0 - var/total_storage_space = W.get_storage_cost() - if(total_storage_space >= ITEM_SIZE_NO_CONTAINER) + if(W.obj_flags & OBJ_FLAG_NO_STORAGE) if(!stop_messages) to_chat(user, SPAN_WARNING("\The [W] cannot be placed in \the [holder].")) return 0 - total_storage_space += storage_space_used() //Adds up the combined w_classes which will be in the storage item if the item is added to it. + var/total_storage_space = W.get_storage_cost() + storage_space_used() //Adds up the combined w_classes which will be in the storage item if the item is added to it. if(total_storage_space > max_storage_space) if(!stop_messages) to_chat(user, SPAN_WARNING("\The [holder] is too full, make some space.")) @@ -194,18 +193,18 @@ var/global/list/_test_storage_items = list() //This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted() //The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once, //such as when picking up all the items on a tile with one click. -/datum/storage/proc/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update) +/datum/storage/proc/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update, click_params) if(!istype(W)) - return 0 + return FALSE if(ismob(W.loc)) var/mob/M = W.loc if(!M.try_unequip(W)) - return + return FALSE if(holder.reagents?.total_volume) W.fluid_act(holder.reagents) if(QDELETED(W)) - return + return FALSE W.forceMove(holder) W.on_enter_storage(src) @@ -222,10 +221,11 @@ var/global/list/_test_storage_items = list() // Run this regardless of update flag, as it impacts our remaining storage space. consolidate_stacks() if(!skip_update) - update_ui_after_item_insertion() + update_ui_after_item_insertion(W, click_params) holder.storage_inserted() - holder.update_icon() - return 1 + if(!skip_update) + holder.update_icon() + return TRUE /datum/storage/proc/consolidate_stacks() @@ -250,11 +250,11 @@ var/global/list/_test_storage_items = list() if(!stack.amount || QDELETED(stack)) stacks -= stack -/datum/storage/proc/update_ui_after_item_insertion() +/datum/storage/proc/update_ui_after_item_insertion(obj/item/inserted, click_params) prepare_ui() storage_ui?.on_insertion() -/datum/storage/proc/update_ui_after_item_removal() +/datum/storage/proc/update_ui_after_item_removal(obj/item/removed) prepare_ui() storage_ui?.on_post_remove() @@ -272,7 +272,7 @@ var/global/list/_test_storage_items = list() W.reset_plane_and_layer() W.forceMove(new_location) if(!skip_update) - update_ui_after_item_removal() + update_ui_after_item_removal(W) if(W.maptext) W.maptext = "" W.on_exit_storage(src) @@ -286,7 +286,7 @@ var/global/list/_test_storage_items = list() // Only do ui functions for now; the obj is responsible for anything else. /datum/storage/proc/on_item_post_deletion(obj/item/W) - update_ui_after_item_removal() + update_ui_after_item_removal(W) holder?.queue_icon_update() //Run once after using remove_from_storage with skip_update = 1 @@ -294,6 +294,11 @@ var/global/list/_test_storage_items = list() update_ui_after_item_removal() holder?.queue_icon_update() +//Run once after using handle_item_insertion with skip_update = 1 +/datum/storage/proc/finish_bulk_insertion() + update_ui_after_item_insertion() + holder?.queue_icon_update() + /datum/storage/proc/gather_all(var/turf/T, var/mob/user) var/success = 0 var/failure = 0 @@ -303,12 +308,12 @@ var/global/list/_test_storage_items = list() continue success = 1 handle_item_insertion(user, I, TRUE, TRUE) // First 1 is no messages, second 1 is no ui updates - if(success && !failure) - to_chat(user, SPAN_NOTICE("You put everything into \the [holder].")) - update_ui_after_item_insertion() - else if(success) - to_chat(user, SPAN_NOTICE("You put some things into \the [holder].")) - update_ui_after_item_insertion() + if(success) + if(failure) + to_chat(user, SPAN_NOTICE("You put some things into \the [holder].")) + else + to_chat(user, SPAN_NOTICE("You put everything into \the [holder].")) + finish_bulk_insertion() else to_chat(user, SPAN_NOTICE("You fail to pick anything up with \the [holder].")) diff --git a/code/datums/extensions/storage/_storage_ui.dm b/code/datums/extensions/storage/_storage_ui.dm index cbdaae19700..6f4db924f32 100644 --- a/code/datums/extensions/storage/_storage_ui.dm +++ b/code/datums/extensions/storage/_storage_ui.dm @@ -271,6 +271,69 @@ arrange_item_slots(row_num, col_count) refresh_viewers() +// produce bin +// numbered maptext display + deduplication by seed name +/datum/storage_ui/default/produce_bin/prepare_ui(mob/user) + slot_orient_objs() + refresh_viewers() + +/datum/storage_ui/default/produce_bin/show_to(mob/user) + . = ..() + if(user.client) + user.client.screen -= storage_start + user.client.screen -= storage_continue + user.client.screen -= storage_end + user.client.screen += boxes + +/// Returns a key (not necessarily a string) to group objects by. +/datum/storage_ui/default/produce_bin/proc/get_key_for_object(obj/item/food/grown/produce) + if(!istype(produce) || !produce.seed) + return produce // should never happen, but it's a fallback nonetheless + return produce.seed.product_name || produce.seed.name + +/datum/storage_ui/default/produce_bin/proc/get_seed_counts() + var/list/counts = list() + for(var/obj/item/food/grown/produce in _storage.get_contents()) + counts[get_key_for_object(produce)]++ + return counts + +//This proc determins the size of the inventory to be displayed. Please touch it only if you know what you're doing. +/datum/storage_ui/default/produce_bin/slot_orient_objs() + var/adjusted_contents = length(get_seed_counts()) + var/row_num = 0 + if (adjusted_contents > 7) + row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. + arrange_item_slots(row_num, clamp(adjusted_contents - 1, 0, 6)) + +//This proc draws out the inventory and places the items on it. It uses the standard position. +/datum/storage_ui/default/produce_bin/arrange_item_slots(rows, cols) + var/cx = SCREEN_LOC_MOD_FIRST + var/cy = SCREEN_LOC_MOD_SECOND + rows + boxes.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED] to LEFT+[SCREEN_LOC_MOD_FIRST + cols]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND + rows]:[SCREEN_LOC_MOD_DIVIDED]" + + var/list/counts = get_seed_counts() + var/list/displayed = list() + for(var/obj/item/food/grown/produce in _storage.get_contents()) + var/produce_key = get_key_for_object(produce) + if(displayed[produce_key]) + continue + displayed[produce_key] = TRUE + produce.screen_loc = "LEFT+[cx]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[cy]:[SCREEN_LOC_MOD_DIVIDED]" + produce.maptext_x = 2 + produce.maptext_y = 2 + produce.maptext = STYLE_SMALLFONTS_OUTLINE(counts[produce_key], 6, COLOR_WHITE, COLOR_BLACK) + produce.hud_layerise() + cx++ + if (cx > (SCREEN_LOC_MOD_FIRST + cols)) + cx = SCREEN_LOC_MOD_FIRST + cy-- + + 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/produce_bin/on_pre_remove(obj/item/food/grown/produce) + produce.maptext = "" + ..() + #undef SCREEN_LOC_MOD_FIRST #undef SCREEN_LOC_MOD_SECOND #undef SCREEN_LOC_MOD_DIVIDED diff --git a/code/datums/extensions/storage/subtypes_backpack.dm b/code/datums/extensions/storage/subtypes_backpack.dm index aee6fd622ee..3523c496826 100644 --- a/code/datums/extensions/storage/subtypes_backpack.dm +++ b/code/datums/extensions/storage/subtypes_backpack.dm @@ -7,7 +7,7 @@ max_w_class = ITEM_SIZE_NORMAL max_storage_space = 56 -/datum/storage/backpack/holding/can_be_inserted(obj/item/W, stop_messages = 0) +/datum/storage/backpack/holding/can_be_inserted(obj/item/W, mob/user, stop_messages = 0, click_params) if(istype(W, /obj/item/backpack/holding)) return 1 return ..() diff --git a/code/datums/extensions/storage/subtypes_bag.dm b/code/datums/extensions/storage/subtypes_bag.dm index 0777f3e406a..07cdaf12cd2 100644 --- a/code/datums/extensions/storage/subtypes_bag.dm +++ b/code/datums/extensions/storage/subtypes_bag.dm @@ -3,7 +3,7 @@ allow_quick_empty = 1 use_to_pickup = 1 -/datum/storage/bag/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update) +/datum/storage/bag/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update, click_params) . = ..() if(. && istype(holder, /obj/item/bag)) var/obj/item/bag/bag = holder @@ -15,7 +15,7 @@ var/obj/item/bag/bag = holder bag.update_w_class() -/datum/storage/bag/can_be_inserted(obj/item/W, mob/user, stop_messages = 0) +/datum/storage/bag/can_be_inserted(obj/item/W, mob/user, stop_messages = 0, click_params = null) var/mob/living/human/H = ishuman(user) ? user : null // if we're human, then we need to check if bag in a pocket if(holder.loc?.storage || H?.is_in_pocket(holder)) if(!stop_messages) diff --git a/code/datums/extensions/storage/subtypes_belt.dm b/code/datums/extensions/storage/subtypes_belt.dm index 09b0d1a8ccc..e730ffbdd53 100644 --- a/code/datums/extensions/storage/subtypes_belt.dm +++ b/code/datums/extensions/storage/subtypes_belt.dm @@ -67,7 +67,7 @@ /obj/item/clothing/glasses, /obj/item/ammo_casing/shotgun, /obj/item/ammo_magazine, - /obj/item/chems/food/donut, + /obj/item/food/donut, /obj/item/baton, /obj/item/telebaton, /obj/item/flame/fuelled/lighter, diff --git a/code/datums/extensions/storage/subtypes_box.dm b/code/datums/extensions/storage/subtypes_box.dm index 2869e775588..dd94c30e1f3 100644 --- a/code/datums/extensions/storage/subtypes_box.dm +++ b/code/datums/extensions/storage/subtypes_box.dm @@ -15,8 +15,8 @@ max_w_class = ITEM_SIZE_NORMAL max_storage_space = DEFAULT_LARGEBOX_STORAGE -/datum/storage/box/monkey - can_hold = list(/obj/item/chems/food/monkeycube) +/datum/storage/box/animal_cube + can_hold = list(/obj/item/food/animal_cube) /datum/storage/box/snappop can_hold = list(/obj/item/toy/snappop) @@ -35,7 +35,7 @@ max_w_class = ITEM_SIZE_NORMAL can_hold = list( /obj/item/organ, - /obj/item/chems/food, + /obj/item/food, /obj/item/chems/drinks, /obj/item/chems/condiment, /obj/item/chems/glass @@ -70,11 +70,11 @@ max_storage_space = 5 /datum/storage/box/parts_pack - max_storage_space = BASE_STORAGE_CAPACITY(ITEM_SIZE_SMALL) + max_storage_space = BASE_STORAGE_CAPACITY(ITEM_SIZE_NORMAL) /datum/storage/box/donut max_storage_space = ITEM_SIZE_SMALL * 6 - can_hold = list(/obj/item/chems/food/donut) + can_hold = list(/obj/item/food/donut) /datum/storage/box/glasses can_hold = list(/obj/item/chems/drinks/glass2) @@ -94,26 +94,28 @@ max_w_class = ITEM_SIZE_SMALL max_storage_space = ITEM_SIZE_SMALL * 12 can_hold = list( - /obj/item/chems/food/egg, - /obj/item/chems/food/boiledegg + /obj/item/food/egg, + /obj/item/food/boiledegg ) /datum/storage/box/crackers storage_slots = 6 max_w_class = ITEM_SIZE_TINY max_storage_space = ITEM_SIZE_TINY * 6 - can_hold = list(/obj/item/chems/food/cracker) + can_hold = list(/obj/item/food/cracker) /datum/storage/box/crayons max_w_class = ITEM_SIZE_TINY max_storage_space = 6 +/datum/storage/box/tapes + max_storage_space = 28 + /datum/storage/box/candles - max_w_class = ITEM_SIZE_TINY - max_storage_space = 7 + max_storage_space = DEFAULT_LARGEBOX_STORAGE /datum/storage/box/candles/scented - max_storage_space = 5 + max_storage_space = 10 /datum/storage/box/candles/incense - max_storage_space = 9 + max_storage_space = 18 diff --git a/code/datums/extensions/storage/subtypes_excavation.dm b/code/datums/extensions/storage/subtypes_excavation.dm index e87d3ae1723..dacbdf2d244 100644 --- a/code/datums/extensions/storage/subtypes_excavation.dm +++ b/code/datums/extensions/storage/subtypes_excavation.dm @@ -5,7 +5,7 @@ max_w_class = ITEM_SIZE_NORMAL use_to_pickup = 1 -/datum/storage/excavation/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update) +/datum/storage/excavation/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update, click_params) . = ..() var/obj/item/excavation/picks = holder if(istype(picks)) diff --git a/code/datums/extensions/storage/subtypes_firstaid.dm b/code/datums/extensions/storage/subtypes_firstaid.dm index 9896ba4ec5b..01ed7512759 100644 --- a/code/datums/extensions/storage/subtypes_firstaid.dm +++ b/code/datums/extensions/storage/subtypes_firstaid.dm @@ -1,6 +1,6 @@ /datum/storage/firstaid max_w_class = ITEM_SIZE_SMALL - max_storage_space = DEFAULT_BOX_STORAGE + max_storage_space = DEFAULT_LARGEBOX_STORAGE use_sound = 'sound/effects/storage/box.ogg' /datum/storage/firstaid/surgery @@ -18,7 +18,7 @@ /obj/item/surgicaldrill, /obj/item/bonegel, /obj/item/sutures, - /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/medical/bandage/advanced, /obj/item/stack/nanopaste ) diff --git a/code/datums/extensions/storage/subtypes_holster.dm b/code/datums/extensions/storage/subtypes_holster.dm index 1f886b89a9b..7ab6a47b4a7 100644 --- a/code/datums/extensions/storage/subtypes_holster.dm +++ b/code/datums/extensions/storage/subtypes_holster.dm @@ -20,7 +20,7 @@ /obj/item/clothing/glasses, /obj/item/ammo_casing/shotgun, /obj/item/ammo_magazine, - /obj/item/chems/food/donut/, + /obj/item/food/donut, /obj/item/baton, /obj/item/telebaton, /obj/item/flame/fuelled/lighter, diff --git a/code/datums/extensions/storage/subtypes_misc.dm b/code/datums/extensions/storage/subtypes_misc.dm index 511b740f901..fec9cd65cdf 100644 --- a/code/datums/extensions/storage/subtypes_misc.dm +++ b/code/datums/extensions/storage/subtypes_misc.dm @@ -1,3 +1,7 @@ +/datum/storage/book + max_w_class = ITEM_SIZE_SMALL + storage_slots = 1 + /datum/storage/bible max_w_class = ITEM_SIZE_SMALL max_storage_space = 4 @@ -102,17 +106,26 @@ can_hold = list(/obj/item) expected_type = /obj/structure/reagent_dispensers/compost_bin -/datum/storage/hopper/industrial/compost/can_be_inserted(obj/item/W, mob/user, stop_messages = 0) +/datum/storage/hopper/industrial/compost/can_be_inserted(obj/item/W, mob/user, stop_messages = 0, click_params = null) . = ..() if(!.) return - if(istype(W, /obj/item/chems/food/worm) && istype(holder, /obj/structure/reagent_dispensers/compost_bin)) + if(istype(W, /obj/item/food/worm) && istype(holder, /obj/structure/reagent_dispensers/compost_bin)) var/worms = 0 - for(var/obj/item/chems/food/worm/worm in get_contents()) + for(var/obj/item/food/worm/worm in get_contents()) worms++ return worms < COMPOST_MAX_WORMS return W.is_compostable() +/datum/storage/hopper/mortar + max_w_class = ITEM_SIZE_NORMAL * 2 + +/datum/storage/hopper/mortar/can_be_inserted(obj/item/inserting_item, mob/user, stop_messages, click_params = null) + . = ..() + if(!.) + return + return inserting_item.reagents?.total_volume > 0 + /datum/storage/photo_album storage_slots = DEFAULT_BOX_STORAGE //yes, that's storage_slots. Photos are w_class 1 so this has as many slots equal to the number of photos you could put in a box can_hold = list(/obj/item/photo) @@ -130,3 +143,8 @@ max_w_class = ITEM_SIZE_LARGE storage_slots = 4 use_sound = 'sound/effects/storage/toolbox.ogg' + +/datum/storage/produce_bin + can_hold = list(/obj/item/food/grown) + max_storage_space = BASE_STORAGE_CAPACITY(ITEM_SIZE_STRUCTURE) + storage_ui = /datum/storage_ui/default/produce_bin \ No newline at end of file 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/extensions/storage/subtypes_pills.dm b/code/datums/extensions/storage/subtypes_pills.dm index 4c6c3e90272..ca8287f168c 100644 --- a/code/datums/extensions/storage/subtypes_pills.dm +++ b/code/datums/extensions/storage/subtypes_pills.dm @@ -17,7 +17,7 @@ if(pop.pop_sound) playsound(get_turf(pop), pop.pop_sound, 50) -/datum/storage/pillbottle/foil/can_be_inserted(obj/item/W, mob/user, stop_messages = 0) +/datum/storage/pillbottle/foil/can_be_inserted(obj/item/W, mob/user, stop_messages = 0, click_params = null) return FALSE /datum/storage/pillbottle/foil/remove_from_storage(mob/user, obj/item/W, atom/new_location, skip_update) diff --git a/code/datums/extensions/storage/subtypes_secure.dm b/code/datums/extensions/storage/subtypes_secure.dm index 09fe2b16c12..bf501d186be 100644 --- a/code/datums/extensions/storage/subtypes_secure.dm +++ b/code/datums/extensions/storage/subtypes_secure.dm @@ -16,7 +16,7 @@ . = ..() //Must be overriden to prevent gathering from tile and using on items when locked! -/datum/storage/secure/can_be_inserted(obj/item/W, mob/user, stop_messages) +/datum/storage/secure/can_be_inserted(obj/item/W, mob/user, stop_messages, click_params = null) if(is_locked()) if(!stop_messages) to_chat(user, SPAN_WARNING("\The [holder] is locked, you cannot put anything inside.")) diff --git a/code/datums/extensions/storage/subtypes_sheets.dm b/code/datums/extensions/storage/subtypes_sheets.dm index 67af8a8f771..1db2042cb97 100644 --- a/code/datums/extensions/storage/subtypes_sheets.dm +++ b/code/datums/extensions/storage/subtypes_sheets.dm @@ -9,7 +9,7 @@ /datum/storage/sheets/robot capacity = 500 //Borgs get more because >specialization -/datum/storage/sheets/can_be_inserted(obj/item/W, mob/user, stop_messages = 0) +/datum/storage/sheets/can_be_inserted(obj/item/W, mob/user, stop_messages = 0, click_params = null) if(!istype(W,/obj/item/stack/material)) if(!stop_messages) to_chat(user, "\The [holder] does not accept [W].") @@ -24,7 +24,7 @@ return TRUE // Modified handle_item_insertion. Would prefer not to, but... -/datum/storage/sheets/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update) +/datum/storage/sheets/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update, click_params) var/obj/item/stack/material/S = W if(!istype(S)) return FALSE diff --git a/code/datums/extensions/storage/subtypes_slides.dm b/code/datums/extensions/storage/subtypes_slides.dm index 77be82f89ba..1f1e76f069a 100644 --- a/code/datums/extensions/storage/subtypes_slides.dm +++ b/code/datums/extensions/storage/subtypes_slides.dm @@ -10,7 +10,7 @@ var/list/contents = get_contents() projector.set_slide(length(contents) ? contents[1] : null) -/datum/storage/slide_projector/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update) +/datum/storage/slide_projector/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update, click_params) . = ..() var/obj/item/slide_projector/projector = holder if(. && istype(projector) && !projector.current_slide) diff --git a/code/datums/extensions/storage/subtypes_specialized.dm b/code/datums/extensions/storage/subtypes_specialized.dm index 812315fc6e2..aec59959061 100644 --- a/code/datums/extensions/storage/subtypes_specialized.dm +++ b/code/datums/extensions/storage/subtypes_specialized.dm @@ -25,7 +25,7 @@ max_storage_space = 100 max_w_class = ITEM_SIZE_SMALL can_hold = list( - /obj/item/chems/food/grown, + /obj/item/food/grown, /obj/item/seeds ) allow_quick_gather = TRUE diff --git a/code/datums/extensions/storage/subtypes_wallet.dm b/code/datums/extensions/storage/subtypes_wallet.dm index dd430d40815..c9318f0146d 100644 --- a/code/datums/extensions/storage/subtypes_wallet.dm +++ b/code/datums/extensions/storage/subtypes_wallet.dm @@ -3,6 +3,7 @@ max_storage_space = 8 can_hold = list( /obj/item/cash, + /obj/item/charge_stick, /obj/item/card, /obj/item/clothing/mask/smokable, /obj/item/cosmetics, @@ -49,8 +50,8 @@ if(W == wallet.front_stick) wallet.front_stick = null -/datum/storage/wallet/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update) - . = ..(W, prevent_warning) +/datum/storage/wallet/handle_item_insertion(mob/user, obj/item/W, prevent_warning, skip_update, click_params) + . = ..() if(. && istype(holder, /obj/item/wallet)) var/obj/item/wallet/wallet = holder if(!wallet.front_id && istype(W, /obj/item/card/id)) diff --git a/code/datums/hostility/hostility.dm b/code/datums/hostility/hostility.dm index b17ad114bd7..12106d4250a 100644 --- a/code/datums/hostility/hostility.dm +++ b/code/datums/hostility/hostility.dm @@ -59,5 +59,5 @@ var/mob/living/L = target if(iscuffed(L)) return FALSE - return L.assess_perp(holder, access_security, check_weapons, check_no_record, check_wanted) < threat_level_threshold + return L.assess_perp(holder, access_check, check_weapons, check_no_record, check_wanted) < threat_level_threshold return FALSE \ No newline at end of file diff --git a/code/datums/inventory_slots/_inventory_slot.dm b/code/datums/inventory_slots/_inventory_slot.dm index 5f16030d362..204284e6f2c 100644 --- a/code/datums/inventory_slots/_inventory_slot.dm +++ b/code/datums/inventory_slots/_inventory_slot.dm @@ -39,6 +39,7 @@ prop.forceMove(user) prop.hud_layerise() prop.equipped(user, slot_id) + prop.compile_overlays() // avoid world overlays on inventory state and vice versa // Clean up the preexisting item. if(held) diff --git a/code/datums/inventory_slots/inventory_gripper.dm b/code/datums/inventory_slots/inventory_gripper.dm index f6ef59f0acc..c11a09eed72 100644 --- a/code/datums/inventory_slots/inventory_gripper.dm +++ b/code/datums/inventory_slots/inventory_gripper.dm @@ -1,6 +1,5 @@ /datum/inventory_slot/gripper var/hand_sort_priority = 1 - var/can_use_held_item = TRUE var/dexterity = DEXTERITY_FULL var/covering_slot_flags /// If set, use this icon_state for the hand slot overlay; otherwise, use slot_id. diff --git a/code/datums/inventory_slots/inventory_gripper_subtypes.dm b/code/datums/inventory_slots/inventory_gripper_subtypes.dm index 3e72420e047..bced228d5d5 100644 --- a/code/datums/inventory_slots/inventory_gripper_subtypes.dm +++ b/code/datums/inventory_slots/inventory_gripper_subtypes.dm @@ -2,7 +2,6 @@ slot_name = "Mouth" slot_id = BP_MOUTH requires_organ_tag = BP_HEAD - can_use_held_item = FALSE overlay_slot = BP_MOUTH ui_label = "M" hand_sort_priority = 3 @@ -29,8 +28,8 @@ // It also means they can do the old school cartoon schtick of eating // an entire sandwich and spitting up an empty plate. Ptooie. - if(istype(prop, /obj/item/chems/food)) - var/obj/item/chems/food/food = prop + if(istype(prop, /obj/item/food)) + var/obj/item/food/food = prop var/trash = food.trash _holding = null qdel(prop) diff --git a/code/datums/inventory_slots/slots/slot_ears.dm b/code/datums/inventory_slots/slots/slot_ears.dm index b6dd92dd37f..295c24a5a89 100644 --- a/code/datums/inventory_slots/slots/slot_ears.dm +++ b/code/datums/inventory_slots/slots/slot_ears.dm @@ -36,7 +36,7 @@ inv_slot.clear_slot() /datum/inventory_slot/ear/prop_can_fit_in_slot(var/obj/item/prop) - return ..() || prop.w_class <= ITEM_SIZE_TINY + return ..() || (prop.w_class <= ITEM_SIZE_TINY && !(prop.obj_flags & OBJ_FLAG_NO_STORAGE)) /datum/inventory_slot/ear/right slot_name = "Right Ear" diff --git a/code/datums/inventory_slots/slots/slot_mask.dm b/code/datums/inventory_slots/slots/slot_mask.dm index 7772cc4b5a4..33f44fa9044 100644 --- a/code/datums/inventory_slots/slots/slot_mask.dm +++ b/code/datums/inventory_slots/slots/slot_mask.dm @@ -37,6 +37,13 @@ /datum/inventory_slot/mask/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) if(_holding && !(hideflags & HIDEMASK)) - if(user == owner) - return "You are wearing [_holding.get_examine_line()] on your face." - return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] on [pronouns.his] face." + if(_holding.body_parts_covered & SLOT_FACE) + if(user == owner) + return "You are wearing [_holding.get_examine_line()] on your face." + else + return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] on [pronouns.his] face." + else + if(user == owner) + return "You are wearing [_holding.get_examine_line()] around your neck." + else + return "[pronouns.He] [pronouns.is] wearing [_holding.get_examine_line()] around [pronouns.his] neck." diff --git a/code/datums/inventory_slots/slots/slot_pockets.dm b/code/datums/inventory_slots/slots/slot_pockets.dm index 9a075cd7e85..8357af8834d 100644 --- a/code/datums/inventory_slots/slots/slot_pockets.dm +++ b/code/datums/inventory_slots/slots/slot_pockets.dm @@ -24,7 +24,7 @@ if(!disable_warning) to_chat(user, SPAN_WARNING("You need to be wearing something before you can put \the [prop] in your pocket.")) return FALSE - return prop.get_storage_cost() < ITEM_SIZE_NO_CONTAINER + return !(prop.obj_flags & OBJ_FLAG_NO_STORAGE) /datum/inventory_slot/pocket/get_examined_string(mob/owner, mob/user, distance, hideflags, decl/pronouns/pronouns) return diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 6102b20d187..355cd25efdc 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -283,7 +283,7 @@ if(!def_value)//If it's a custom objective, it will be an empty string. def_value = "custom" - var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "custom") + var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "custom") if (!new_obj_type) return var/datum/objective/new_objective = null @@ -341,7 +341,7 @@ new_objective = new /datum/objective/steal new_objective.owner = src - if("download","capture") + if("download") var/def_num if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) def_num = objective.target_amount @@ -354,9 +354,6 @@ if("download") new_objective = new /datum/objective/download new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Accumulate [target_number] capture points." new_objective.owner = src new_objective.target_amount = target_number @@ -366,6 +363,8 @@ new_objective = new /datum/objective new_objective.owner = src new_objective.explanation_text = expl + else + PRINT_STACK_TRACE("ERROR: Unrecognized objective type [new_obj_type]") if (!new_objective) return diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm index 8736fead6f9..996c6215085 100644 --- a/code/datums/movement/mob.dm +++ b/code/datums/movement/mob.dm @@ -62,21 +62,28 @@ return (MOVEMENT_PROCEED|MOVEMENT_HANDLED) /datum/movement_handler/mob/space - var/allow_move + var/last_space_move_result + +// Notes on space movement chain: +// - owning mob calls MayMove() via normal movement handler chain +// - MayMove() sets last_space_move_result based on is_space_movement_permitted() (checks for footing, magboots, etc) +// - last_space_move_result is checked in DoMove() and passed to try_space_move() as a param, which returns TRUE/FALSE +// - if the original move result was forbidden, or try_space_move() fails, the handler prevents movement. +// - Otherwise it goes ahead and lets the mob move. // Space movement /datum/movement_handler/mob/space/DoMove(direction, mob/mover, is_external) if(mob.has_gravity() || (IS_NOT_SELF(mover) && is_external)) return - if(!allow_move || !mob.space_do_move(allow_move, direction)) + if(last_space_move_result == SPACE_MOVE_FORBIDDEN || !mob.try_space_move(last_space_move_result, direction)) return MOVEMENT_HANDLED /datum/movement_handler/mob/space/MayMove(mob/mover, is_external) if(IS_NOT_SELF(mover) && is_external) return MOVEMENT_PROCEED if(!mob.has_gravity()) - allow_move = mob.Process_Spacemove(1) - if(!allow_move) + last_space_move_result = mob.is_space_movement_permitted(allow_movement = TRUE) + if(last_space_move_result == SPACE_MOVE_FORBIDDEN) return MOVEMENT_STOP return MOVEMENT_PROCEED @@ -157,8 +164,8 @@ to_chat(mob, SPAN_WARNING("You're pinned down by \a [mob.pinned[1]]!")) return MOVEMENT_STOP - for(var/obj/item/grab/G as anything in mob.grabbed_by) - if(G.assailant != mob && G.assailant != mover && (mob.restrained() || G.stop_move())) + for(var/obj/item/grab/grab as anything in mob.grabbed_by) + if(grab.assailant != mob && grab.assailant != mover && (mob.restrained() || grab.stop_move())) if(mover == mob) to_chat(mob, SPAN_WARNING("You're restrained and cannot move!")) mob.ProcessGrabs() @@ -191,6 +198,8 @@ mob.moving = FALSE return + mob.handle_footsteps() + // Sprinting uses up stamina and causes exertion effects. if(MOVING_QUICKLY(mob)) mob.last_quick_move_time = world.time @@ -205,7 +214,7 @@ mob.moving = FALSE /datum/movement_handler/mob/movement/MayMove(mob/mover, is_external) - return IS_SELF(mover) && mob.moving ? MOVEMENT_STOP : MOVEMENT_PROCEED + return IS_SELF(mover) && mob.moving ? MOVEMENT_STOP : MOVEMENT_PROCEED /mob/proc/get_stamina_used_per_step() return 1 diff --git a/code/datums/movement/robot.dm b/code/datums/movement/robot.dm index 258c5e840b4..2674938b3cc 100644 --- a/code/datums/movement/robot.dm +++ b/code/datums/movement/robot.dm @@ -16,8 +16,11 @@ // Use power while moving. /datum/movement_handler/robot/use_power/DoMove(direction, mob/mover, is_external) var/datum/robot_component/actuator/A = robot.get_component("actuator") - if(!robot.cell_use_power(A.active_usage * robot.power_efficiency)) + if(!is_external && !robot.cell_use_power(A.active_usage * robot.power_efficiency)) return MOVEMENT_HANDLED + return MOVEMENT_PROCEED /datum/movement_handler/robot/use_power/MayMove(mob/mover, is_external) - return (!robot.lockcharge && robot.is_component_functioning("actuator")) ? MOVEMENT_PROCEED : MOVEMENT_STOP + if(is_external || (!robot.incapacitated() && !robot.lockcharge && robot.is_component_functioning("actuator"))) + return MOVEMENT_PROCEED + return MOVEMENT_STOP diff --git a/code/datums/music_tracks/fantasy.dm b/code/datums/music_tracks/fantasy.dm index 5fc7234da26..6f1f6e05f27 100644 --- a/code/datums/music_tracks/fantasy.dm +++ b/code/datums/music_tracks/fantasy.dm @@ -19,3 +19,9 @@ license = /decl/license/cc_by_3_0 url = "https://incompetech.com/music/royalty-free/index.html?isrc=USUAN1400023" +/decl/music_track/magic_dance + artist = "Kevin Macleod" + title = "Miri's Magic Dance" + song = 'sound/music/Miris-Magic-Dance.ogg' + license = /decl/license/cc_by_3_0 + url = "https://incompetech.com/music/royalty-free/index.html?isrc=USUAN1100157" diff --git a/code/datums/observation/destroyed.dm b/code/datums/observation/destroyed.dm index 7678c52dfe0..e3cd1c44905 100644 --- a/code/datums/observation/destroyed.dm +++ b/code/datums/observation/destroyed.dm @@ -8,3 +8,4 @@ /decl/observ/destroyed name = "Destroyed" + flags = OBSERVATION_NO_GLOBAL_REGISTRATIONS diff --git a/code/datums/observation/moved.dm b/code/datums/observation/moved.dm index 19557c43c5e..101dd5b756f 100644 --- a/code/datums/observation/moved.dm +++ b/code/datums/observation/moved.dm @@ -20,6 +20,13 @@ if(. && istype(mover.loc, expected_type)) register(mover.loc, mover, TYPE_PROC_REF(/atom/movable, recursive_move)) +/decl/observ/moved/unregister(var/atom/movable/mover, var/datum/listener, var/proc_call) + . = ..() + + // Unregister from the parent if possible. + if(. && istype(mover.loc, expected_type)) + unregister(mover.loc, mover, TYPE_PROC_REF(/atom/movable, recursive_move)) + /******************** * Movement Handling * ********************/ diff --git a/code/datums/outfits/horror_killers.dm b/code/datums/outfits/horror_killers.dm index 09b462349cc..42538ab43b0 100644 --- a/code/datums/outfits/horror_killers.dm +++ b/code/datums/outfits/horror_killers.dm @@ -1,4 +1,4 @@ -/decl/hierarchy/outfit/tunnel_clown +/decl/outfit/tunnel_clown name = "Tunnel clown" uniform = /obj/item/clothing/costume/clown shoes = /obj/item/clothing/shoes/clown_shoes @@ -9,13 +9,13 @@ glasses = /obj/item/clothing/glasses/thermal/plain/monocle suit = /obj/item/clothing/suit/chaplain_hoodie r_pocket = /obj/item/bikehorn - hands = list(/obj/item/twohanded/fireaxe) + hands = list(/obj/item/bladed/axe/fire) id_slot = slot_wear_id_str id_type = /obj/item/card/id/centcom/station id_pda_assignment = "Tunnel Clown!" -/decl/hierarchy/outfit/masked_killer +/decl/outfit/masked_killer name = "Masked killer" uniform = /obj/item/clothing/pants/mustard/overalls shoes = /obj/item/clothing/shoes/color/white @@ -27,16 +27,16 @@ suit = /obj/item/clothing/suit/apron l_pocket = /obj/item/knife/combat r_pocket = /obj/item/scalpel - hands = list(/obj/item/twohanded/fireaxe) + hands = list(/obj/item/bladed/axe/fire) -/decl/hierarchy/outfit/masked_killer/post_equip(var/mob/living/human/H) +/decl/outfit/masked_killer/post_equip(var/mob/living/wearer) ..() - var/victim = get_mannequin(H.ckey) + var/victim = get_mannequin(wearer.ckey) if(victim) - for(var/obj/item/carried_item in H.get_equipped_items(TRUE)) + for(var/obj/item/carried_item in wearer.get_equipped_items(TRUE)) carried_item.add_blood(victim) //Oh yes, there will be blood... just not blood from the killer because that's odd -/decl/hierarchy/outfit/reaper +/decl/outfit/reaper name = "Reaper" uniform = /obj/item/clothing/pants/slacks/outfit shoes = /obj/item/clothing/shoes/color/black @@ -50,9 +50,9 @@ pda_slot = slot_belt_str pda_type = /obj/item/modular_computer/pda/heads -/decl/hierarchy/outfit/reaper/post_equip(var/mob/living/human/H) +/decl/outfit/reaper/post_equip(var/mob/living/wearer) ..() - var/obj/item/secure_storage/briefcase/sec_briefcase = new(H) + var/obj/item/secure_storage/briefcase/sec_briefcase = new(wearer) for(var/obj/item/briefcase_item in sec_briefcase) qdel(briefcase_item) for(var/i=3, i>0, i--) @@ -61,4 +61,4 @@ new /obj/item/gun/projectile/revolver(sec_briefcase) new /obj/item/ammo_magazine/speedloader(sec_briefcase) new /obj/item/plastique(sec_briefcase) - H.put_in_hands_or_del(sec_briefcase) + wearer.put_in_hands_or_del(sec_briefcase) diff --git a/code/datums/outfits/jobs/generic.dm b/code/datums/outfits/jobs/generic.dm index 4fb0e6e39c9..67ebfa0d20a 100644 --- a/code/datums/outfits/jobs/generic.dm +++ b/code/datums/outfits/jobs/generic.dm @@ -1,11 +1,11 @@ -/decl/hierarchy/outfit/job/generic - abstract_type = /decl/hierarchy/outfit/job/generic +/decl/outfit/job/generic + abstract_type = /decl/outfit/job/generic id_type = /obj/item/card/id/civilian -/decl/hierarchy/outfit/job/generic/assistant +/decl/outfit/job/generic/assistant name = "Job - Assistant" -/decl/hierarchy/outfit/job/generic/scientist +/decl/outfit/job/generic/scientist name = "Job - Default Scientist" l_ear = /obj/item/radio/headset/headset_sci suit = /obj/item/clothing/suit/toggle/labcoat/science @@ -13,7 +13,7 @@ pda_type = /obj/item/modular_computer/pda/science uniform = /obj/item/clothing/jumpsuit/white -/decl/hierarchy/outfit/job/generic/engineer +/decl/outfit/job/generic/engineer name = "Job - Default Engineer" head = /obj/item/clothing/head/hardhat uniform = /obj/item/clothing/jumpsuit/engineer @@ -25,11 +25,11 @@ pda_slot = slot_l_store_str outfit_flags = OUTFIT_HAS_BACKPACK | OUTFIT_EXTENDED_SURVIVAL | OUTFIT_HAS_VITALS_SENSOR -/decl/hierarchy/outfit/job/generic/engineer/Initialize() +/decl/outfit/job/generic/engineer/Initialize() . = ..() BACKPACK_OVERRIDE_ENGINEERING -/decl/hierarchy/outfit/job/generic/doctor +/decl/outfit/job/generic/doctor name = "Job - Default Doctor" uniform = /obj/item/clothing/jumpsuit/medical suit = /obj/item/clothing/suit/toggle/labcoat @@ -40,11 +40,11 @@ pda_type = /obj/item/modular_computer/pda/medical pda_slot = slot_l_store_str -/decl/hierarchy/outfit/job/generic/doctor/Initialize() +/decl/outfit/job/generic/doctor/Initialize() . = ..() BACKPACK_OVERRIDE_MEDICAL -/decl/hierarchy/outfit/job/generic/chef +/decl/outfit/job/generic/chef name = "Job - Default Chef" l_ear = /obj/item/radio/headset/headset_service uniform = /obj/item/clothing/pants/slacks/outfit_chef diff --git a/code/datums/outfits/jobs/job.dm b/code/datums/outfits/jobs/job.dm index 0120a3f5b6e..717f6127dcd 100644 --- a/code/datums/outfits/jobs/job.dm +++ b/code/datums/outfits/jobs/job.dm @@ -1,6 +1,6 @@ -/decl/hierarchy/outfit/job +/decl/outfit/job name = "Standard Gear" - abstract_type = /decl/hierarchy/outfit/job + abstract_type = /decl/outfit/job uniform = /obj/item/clothing/jumpsuit/grey l_ear = /obj/item/radio/headset diff --git a/code/datums/outfits/jobs/misc.dm b/code/datums/outfits/jobs/misc.dm index b87b03f5121..feb531208ff 100644 --- a/code/datums/outfits/jobs/misc.dm +++ b/code/datums/outfits/jobs/misc.dm @@ -1,11 +1,11 @@ -/decl/hierarchy/outfit/job/silicon +/decl/outfit/job/silicon head = /obj/item/clothing/head/cardborg - abstract_type = /decl/hierarchy/outfit/job/silicon + abstract_type = /decl/outfit/job/silicon -/decl/hierarchy/outfit/job/silicon/ai +/decl/outfit/job/silicon/ai name = "Job - AI" suit = /obj/item/clothing/suit/straight_jacket -/decl/hierarchy/outfit/job/silicon/cyborg +/decl/outfit/job/silicon/cyborg name = "Job - Cyborg" suit = /obj/item/clothing/suit/cardborg diff --git a/code/datums/outfits/misc.dm b/code/datums/outfits/misc.dm index af13c63d40c..aaf3e6d7b36 100644 --- a/code/datums/outfits/misc.dm +++ b/code/datums/outfits/misc.dm @@ -1,4 +1,4 @@ -/decl/hierarchy/outfit/standard_space_gear +/decl/outfit/standard_space_gear name = "Standard space gear" shoes = /obj/item/clothing/shoes/color/black head = /obj/item/clothing/head/helmet/space @@ -8,7 +8,7 @@ mask = /obj/item/clothing/mask/breath outfit_flags = OUTFIT_HAS_JETPACK|OUTFIT_RESET_EQUIPMENT -/decl/hierarchy/outfit/soviet_soldier +/decl/outfit/soviet_soldier name = "Soviet soldier" uniform = /obj/item/clothing/costume/soviet shoes = /obj/item/clothing/shoes/jackboots/swat/combat @@ -17,7 +17,7 @@ back = /obj/item/backpack/satchel belt = /obj/item/gun/projectile/revolver -/decl/hierarchy/outfit/soviet_soldier/admiral +/decl/outfit/soviet_soldier/admiral name = "Soviet admiral" head = /obj/item/clothing/head/hgpiratecap l_ear = /obj/item/radio/headset/heads/captain @@ -28,7 +28,7 @@ id_type = /obj/item/card/id/centcom/station id_pda_assignment = "Admiral" -/decl/hierarchy/outfit/clown +/decl/outfit/clown name = "Clown" shoes = /obj/item/clothing/shoes/clown_shoes mask = /obj/item/clothing/mask/gas/clown_hat @@ -37,6 +37,6 @@ l_pocket = /obj/item/bikehorn outfit_flags = OUTFIT_HAS_BACKPACK | OUTFIT_RESET_EQUIPMENT | OUTFIT_HAS_VITALS_SENSOR -/decl/hierarchy/outfit/clown/Initialize() +/decl/outfit/clown/Initialize() . = ..() backpack_overrides[/decl/backpack_outfit/backpack] = /obj/item/backpack/clown diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm index e69dfd4aaa9..572306145f5 100644 --- a/code/datums/outfits/outfit.dm +++ b/code/datums/outfits/outfit.dm @@ -1,29 +1,6 @@ -var/global/list/outfits_decls_ -var/global/list/outfits_decls_root_ -var/global/list/outfits_decls_by_type_ - -/proc/outfit_by_type(var/outfit_type) - if(!outfits_decls_root_) - init_outfit_decls() - return outfits_decls_by_type_[outfit_type] - -/proc/outfits() - if(!outfits_decls_root_) - init_outfit_decls() - return outfits_decls_ - -/proc/init_outfit_decls() - if(outfits_decls_root_) - return - outfits_decls_ = list() - outfits_decls_by_type_ = list() - outfits_decls_root_ = GET_DECL(/decl/hierarchy/outfit) - -/decl/hierarchy/outfit - name = "Naked" - abstract_type = /decl/hierarchy/outfit - expected_type = /decl/hierarchy/outfit - +/decl/outfit + abstract_type = /decl/outfit + var/name = "Naked" var/uniform = null var/suit = null var/back = null @@ -55,16 +32,35 @@ var/global/list/outfits_decls_by_type_ var/list/backpack_overrides var/outfit_flags = OUTFIT_RESET_EQUIPMENT -/decl/hierarchy/outfit/Initialize() +/decl/outfit/Initialize() . = ..() backpack_overrides = backpack_overrides || list() - if(!INSTANCE_IS_ABSTRACT(src)) - outfits_decls_by_type_[type] = src - dd_insertObjectList(outfits_decls_, src) // This proc is structured slightly strangely because I will be adding pants to it. -/decl/hierarchy/outfit/validate() +/decl/outfit/validate() . = ..() + + for(var/check_type in list(uniform, suit, back, belt, gloves, shoes, head, mask, l_ear, r_ear, glasses, id, l_pocket, r_pocket, suit_store, pda_type, id_type)) + var/obj/item/thing = check_type + if(isnull(thing)) + continue + if(TYPE_IS_ABSTRACT(thing)) + . += "equipment includes abstract type '[thing]'" + + for(var/check_type in hands) + var/obj/item/thing = check_type + if(isnull(thing)) + continue + if(TYPE_IS_ABSTRACT(thing)) + . += "hands includes abstract type '[thing]'" + + for(var/check_type in backpack_contents) + var/obj/item/thing = check_type + if(isnull(thing)) + continue + if(TYPE_IS_ABSTRACT(thing)) + . += "backpack includes abstract type '[thing]'" + if(uniform && (outfit_flags & OUTFIT_HAS_VITALS_SENSOR)) if(!ispath(uniform, /obj/item/clothing)) . += "outfit is flagged for sensors, but uniform cannot take accessories" @@ -79,128 +75,138 @@ var/global/list/outfits_decls_by_type_ . += "outfit is flagged for sensors, but uniform does not accept sensors" qdel(sensor) -/decl/hierarchy/outfit/proc/pre_equip(mob/living/human/H) +/decl/outfit/proc/pre_equip(mob/living/wearer) if(outfit_flags & OUTFIT_RESET_EQUIPMENT) - H.delete_inventory(TRUE) + wearer.delete_inventory(TRUE) -/decl/hierarchy/outfit/proc/post_equip(mob/living/human/H) +/decl/outfit/proc/post_equip(mob/living/wearer) if(outfit_flags & OUTFIT_HAS_JETPACK) - var/obj/item/tank/jetpack/J = locate(/obj/item/tank/jetpack) in H + var/obj/item/tank/jetpack/J = locate(/obj/item/tank/jetpack) in wearer if(!J) return J.toggle() J.toggle_valve() -/decl/hierarchy/outfit/proc/equip_outfit(mob/living/human/H, assignment, equip_adjustments, datum/job/job, datum/mil_rank/rank) - equip_base(H, equip_adjustments) - equip_id(H, assignment, equip_adjustments, job, rank) +/decl/outfit/proc/equip_outfit(mob/living/wearer, assignment, equip_adjustments, datum/job/job, datum/mil_rank/rank) + equip_base(wearer, equip_adjustments) + equip_id(wearer, assignment, equip_adjustments, job, rank) for(var/path in backpack_contents) var/number = backpack_contents[path] for(var/i=0,i= available_at_map_tech + /decl/trait/proc/validate_level(level) SHOULD_NOT_OVERRIDE(TRUE) SHOULD_NOT_SLEEP(TRUE) @@ -160,7 +176,7 @@ trait_category = new(category) global.trait_categories[category] = trait_category trait_category.items += src - if(trait_category.hide_from_chargen && available_at_chargen) + if(trait_category.hide_from_chargen && is_available_at_chargen()) trait_category.hide_from_chargen = FALSE if(istype(parent)) LAZYDISTINCTADD(parent.children, src) @@ -168,7 +184,9 @@ /decl/trait/proc/applies_to_organ(var/organ) return FALSE -/decl/trait/proc/is_available_to(var/datum/preferences/pref) +/decl/trait/proc/is_available_to_select(var/datum/preferences/pref) + if(!is_available_at_chargen()) + return FALSE for(var/blacklisted_type in incompatible_with) if(blacklisted_type in pref.traits) return FALSE @@ -185,7 +203,7 @@ /decl/trait/proc/get_trait_selection_data(var/datum/category_item/player_setup_item/traits/caller, var/list/ticked_traits = list(), var/recurse_level = 0, var/ignore_children_if_unticked = 1, var/ignore_unticked) var/ticked = (type in ticked_traits) - if((ignore_unticked && !ticked) || (caller && !is_available_to(caller.pref))) + if((ignore_unticked && !ticked) || (caller && !is_available_to_select(caller.pref))) return "" var/result = "" @@ -199,16 +217,18 @@ incompatible_trait_taken = TRUE break + var/chargen_name = get_chargen_name(caller.pref) + var/chargen_desc = get_chargen_desc(caller.pref) if(istype(caller) && (ticked || caller.get_trait_total() + trait_cost <= get_config_value(/decl/config/num/max_character_traits)) && !incompatible_trait_taken) - result += "[ticked ? "[name]" : "[name]"] ([trait_cost])" + result += "[ticked ? "[chargen_name]" : "[chargen_name]"] ([trait_cost])" else - result += ticked ? "[name]" : "[name]" + result += ticked ? "[chargen_name]" : "[chargen_name]" result += "" if(ticked) - result += "[description]" + result += "[chargen_desc]" else - result += "[description]" + result += "[chargen_desc]" result += "" if(LAZYLEN(children) && !(ignore_children_if_unticked && !ticked)) diff --git a/code/datums/traits/maluses/amputations.dm b/code/datums/traits/maluses/amputations.dm index 4de7eb782a2..5cbd3ea7c77 100644 --- a/code/datums/traits/maluses/amputations.dm +++ b/code/datums/traits/maluses/amputations.dm @@ -38,7 +38,7 @@ /decl/trait/malus/amputation/applies_to_organ(var/organ) return (organ in apply_to_limbs) -/decl/trait/malus/amputation/is_available_to(datum/preferences/pref) +/decl/trait/malus/amputation/is_available_to_select(datum/preferences/pref) . = ..() if(. && pref.bodytype) var/decl/bodytype/mob_bodytype = pref.get_bodytype_decl() @@ -63,6 +63,7 @@ description = "You are missing your left hand." apply_to_limbs = list(BP_L_HAND) ban_traits_relating_to_limbs = list(BP_L_HAND, BP_L_ARM) + uid = "trait_amputated_left_hand" /decl/trait/malus/amputation/left_arm name = "Amputated Left Arm" @@ -70,12 +71,14 @@ apply_to_limbs = list(BP_L_ARM, BP_L_HAND) ban_traits_relating_to_limbs = list(BP_L_ARM, BP_L_HAND) trait_cost = -2 + uid = "trait_amputated_left_arm" /decl/trait/malus/amputation/right_hand name = "Amputated Right Hand" description = "You are missing your right hand." apply_to_limbs = list(BP_R_HAND) ban_traits_relating_to_limbs = list(BP_R_HAND, BP_R_ARM) + uid = "trait_amputated_right_hand" /decl/trait/malus/amputation/right_arm name = "Amputated Right Arm" @@ -83,12 +86,14 @@ apply_to_limbs = list(BP_R_ARM, BP_R_HAND) ban_traits_relating_to_limbs = list(BP_R_ARM, BP_R_HAND) trait_cost = -2 + uid = "trait_amputated_right_arm" /decl/trait/malus/amputation/left_foot name = "Amputated Left Foot" description = "You are missing your left foot." apply_to_limbs = list(BP_L_FOOT) ban_traits_relating_to_limbs = list(BP_L_LEG, BP_L_FOOT) + uid = "trait_amputated_left_foot" /decl/trait/malus/amputation/left_leg name = "Amputated Left Leg" @@ -96,12 +101,14 @@ apply_to_limbs = list(BP_L_LEG, BP_L_FOOT) ban_traits_relating_to_limbs = list(BP_L_LEG, BP_L_FOOT) trait_cost = -2 + uid = "trait_amputated_left_leg" /decl/trait/malus/amputation/right_foot name = "Amputated Right Foot" description = "You are missing your right foot." apply_to_limbs = list(BP_R_FOOT) ban_traits_relating_to_limbs = list(BP_R_LEG, BP_R_FOOT) + uid = "trait_amputated_right_foot" /decl/trait/malus/amputation/right_leg name = "Amputated Right Leg" @@ -109,3 +116,4 @@ apply_to_limbs = list(BP_R_LEG, BP_R_FOOT) ban_traits_relating_to_limbs = list(BP_R_LEG, BP_R_FOOT) trait_cost = -2 + uid = "trait_amputated_right_leg" diff --git a/code/datums/traits/maluses/animal_protein.dm b/code/datums/traits/maluses/animal_protein.dm deleted file mode 100644 index 0963b91f5f5..00000000000 --- a/code/datums/traits/maluses/animal_protein.dm +++ /dev/null @@ -1,3 +0,0 @@ -/decl/trait/malus/animal_protein - name = "Animal Protein Allergy" - levels = list(TRAIT_LEVEL_MINOR, TRAIT_LEVEL_MAJOR) diff --git a/code/datums/traits/maluses/ethanol.dm b/code/datums/traits/maluses/ethanol.dm index f4a22e7f446..14943bc4aeb 100644 --- a/code/datums/traits/maluses/ethanol.dm +++ b/code/datums/traits/maluses/ethanol.dm @@ -1,3 +1,5 @@ /decl/trait/malus/ethanol - name = "Ethanol Allergy" + name = "Alcohol Intolerance" levels = list(TRAIT_LEVEL_MINOR, TRAIT_LEVEL_MODERATE, TRAIT_LEVEL_MAJOR) + description = "You cannot handle alcohol at all, even in limited quantities, and will become seriously ill if you drink it." + uid = "trait_allergy_alcohol" diff --git a/code/datums/traits/maluses/intolerances.dm b/code/datums/traits/maluses/intolerances.dm new file mode 100644 index 00000000000..4c4b096dbd6 --- /dev/null +++ b/code/datums/traits/maluses/intolerances.dm @@ -0,0 +1,106 @@ +var/global/list/_intolerances_by_flag = list() +/proc/get_intolerances_by_flag(allergen_flags, ingestion_method) + + var/flag_key + if(ingestion_method) + flag_key = "[allergen_flags]-[ingestion_method]" + else + flag_key = num2text(allergen_flags) + + . = global._intolerances_by_flag[flag_key] + if(isnull(.)) + . = list() + for(var/decl/trait/malus/intolerance/allergy in decls_repository.get_decls_of_type_unassociated(/decl/trait/malus/intolerance)) + if((!ingestion_method || (ingestion_method in allergy.ingested_types)) && (allergy.allergen_flags & allergen_flags)) + . += allergy + global._intolerances_by_flag[flag_key] = . + +/decl/trait/malus/intolerance + abstract_type = /decl/trait/malus/intolerance + levels = list(TRAIT_LEVEL_MINOR, TRAIT_LEVEL_MAJOR) + var/list/ingested_types = list(CHEM_INGEST) + var/allergen_flags = ALLERGEN_NONE + +/decl/trait/malus/intolerance/protein + name = "Protein Intolerance" + description = "You are allergic to or intolerant of animal protein in any form, and can become very ill if you ingest it." + allergen_flags = (ALLERGEN_MEAT | ALLERGEN_FISH | ALLERGEN_DAIRY | ALLERGEN_EGG) + uid = "trait_allergy_protein" + +/decl/trait/malus/intolerance/meat + name = "Meat Intolerance" + description = "You are allergic or intolerant of red and white meat, and can become very ill if you ingest it." + allergen_flags = ALLERGEN_MEAT | ALLERGEN_FISH + uid = "trait_allergy_meat" + +/decl/trait/malus/intolerance/fish + name = "Fish Intolerance" + description = "You are allergic to or intolerant of fish meat, and can become very ill if you ingest it." + allergen_flags = ALLERGEN_FISH + uid = "trait_allergy_fish" + +/decl/trait/malus/intolerance/vegetable + name = "Vegetable Intolerance" + description = "You are allergic to or intolerant of vegetables, and can become very ill if you ingest them." + allergen_flags = ALLERGEN_VEGETABLE + uid = "trait_allergy_vegetable" + +/decl/trait/malus/intolerance/dairy + name = "Dairy Intolerance" + description = "You are allergic to or intolerant of milk solids or lactose - essentially dairy in any form - and can become very ill if you ingest it." + allergen_flags = ALLERGEN_DAIRY + uid = "trait_allergy_dairy" + +/decl/trait/malus/intolerance/egg + name = "Egg Intolerance" + description = "You are allergic to or intolerant of eggs, and can become very ill if you ingest them." + allergen_flags = ALLERGEN_EGG + uid = "trait_allergy_egg" + +/decl/trait/malus/intolerance/fruit + name = "Fruit Intolerance" + description = "You are allergic to or intolerant of fruit, and can become very ill if you ingest it." + allergen_flags = ALLERGEN_FRUIT + uid = "trait_allergy_fruit" + +/decl/trait/malus/intolerance/gluten + name = "Gluten Intolerance" + description = "You are allergic to or intolerant of gluten, and can become very ill if you ingest it." + allergen_flags = ALLERGEN_GLUTEN + uid = "trait_allergy_gluten" + +/decl/trait/malus/intolerance/soy + name = "Soy Intolerance" + description = "You are allergic to or intolerant of soy protein, and can become very ill if you ingest it." + allergen_flags = ALLERGEN_SOY + uid = "trait_allergy_soy" + +/decl/trait/malus/intolerance/caffeine + name = "Caffeine Intolerance" + description = "You are allergic to or intolerant of caffeine, and can become very ill if you ingest it." + allergen_flags = ALLERGEN_CAFFEINE + uid = "trait_allergy_caffeine" + +/decl/trait/malus/intolerance/fungi + name = "Mushroom Intolerance" + description = "You are allergic to or intolerant of mushrooms and fungi, and can become very ill if you ingest them." + allergen_flags = ALLERGEN_FUNGI + uid = "trait_allergy_fungi" + +/decl/trait/malus/intolerance/nuts + name = "Nut Intolerance" + description = "You are allergic to or intolerant of nuts, and can become very ill if you ingest them." + allergen_flags = ALLERGEN_NUTS + uid = "trait_allergy_nuts" + +/decl/trait/malus/intolerance/stimulant + name = "Caffeine Intolerance" + description = "You are allergic to or intolerant of caffeine, and can become very ill if you ingest it." + allergen_flags = ALLERGEN_CAFFEINE + uid = "trait_allergy_caffeine" + +/decl/trait/malus/intolerance/stimulant + name = "Stimulant Intolerance" + description = "You are allergic to or intolerant of stimulants, and can become very ill if you ingest them." + allergen_flags = ALLERGEN_STIMULANT | ALLERGEN_CAFFEINE + uid = "trait_allergy_stimulant" diff --git a/code/datums/traits/maluses/vision.dm b/code/datums/traits/maluses/vision.dm index 928f9be9070..b3402c2328f 100644 --- a/code/datums/traits/maluses/vision.dm +++ b/code/datums/traits/maluses/vision.dm @@ -2,12 +2,15 @@ name = "Poor Eyesight" description = "Your vision is somewhat impaired, and you need prescription glasses to see clearly." incompatible_with = list(/decl/trait/prosthetic_organ/eyes) + uid = "trait_vision_poor" + /// The typepath of the glasses to give the holder. + var/glasses_type = /obj/item/clothing/glasses/prescription /decl/trait/malus/impaired_vision/apply_trait(mob/living/holder) . = ..() if(.) holder.add_genetic_condition(GENE_COND_NEARSIGHTED) - var/equipped = holder.equip_to_slot_or_del(new /obj/item/clothing/glasses/prescription(holder), slot_glasses_str) + var/equipped = holder.equip_to_slot_or_del(new glasses_type(holder), slot_glasses_str) if(equipped) var/obj/item/clothing/glasses/G = holder.get_equipped_item(slot_glasses_str) if(istype(G)) @@ -22,6 +25,7 @@ /decl/trait/malus/colourblind/tritanopia, /decl/trait/malus/colourblind/achromatopsia, ) + uid = "trait_vision_deuteranopia" var/client_color = /datum/client_color/deuteranopia /decl/trait/malus/colourblind/apply_trait(mob/living/holder) @@ -39,6 +43,7 @@ /decl/trait/malus/colourblind ) client_color = /datum/client_color/protanopia + uid = "trait_vision_protanopia" /decl/trait/malus/colourblind/tritanopia name = "Tritanopia" @@ -50,6 +55,7 @@ /decl/trait/malus/colourblind ) client_color = /datum/client_color/tritanopia + uid = "trait_vision_trianopia" /decl/trait/malus/colourblind/achromatopsia name = "Achromatopsia" @@ -61,3 +67,4 @@ /decl/trait/malus/colourblind ) client_color = /datum/client_color/achromatopsia + uid = "trait_vision_achromatopsia" diff --git a/code/datums/traits/prosthetics/prosthetic_limbs.dm b/code/datums/traits/prosthetics/prosthetic_limbs.dm index 44a21b9c5aa..0ea2e8c0658 100644 --- a/code/datums/traits/prosthetics/prosthetic_limbs.dm +++ b/code/datums/traits/prosthetics/prosthetic_limbs.dm @@ -3,6 +3,7 @@ trait_cost = 1 category = "Prosthetic Limbs" available_at_chargen = TRUE + available_at_map_tech = MAP_TECH_LEVEL_ANY // the base trait must be available so that wooden prostheses are available abstract_type = /decl/trait/prosthetic_limb reapply_on_rejuvenation = TRUE var/fullbody_synthetic_only = FALSE @@ -19,16 +20,29 @@ var/decl/species/species = species_name ? get_species_by_key(species_name) : global.using_map.default_species return species?.base_external_prosthetics_model +/decl/trait/prosthetic_limb/get_chargen_name(datum/preferences/pref) + var/decl/bodytype/prosthetic/our_model = get_base_model(pref.species) + if(!model && our_model.name) + return "[capitalize_words(our_model.name)] [bodypart_name]" + return ..() + +/decl/trait/prosthetic_limb/get_chargen_desc(datum/preferences/pref) + var/decl/bodytype/prosthetic/our_model = get_base_model(pref.species) + if(!model && our_model.name) + return "You have been fitted with [ADD_ARTICLE(our_model.name)] [lowertext(bodypart_name)] prosthesis." + return ..() + /decl/trait/prosthetic_limb/Initialize() . = ..() // Macro can generate float costs, round to closest point value. if(trait_cost) - trait_cost = CEILING(trait_cost) + trait_cost = ceil(trait_cost) if(bodypart_name) if(model) var/decl/bodytype/prosthetic/model_manufacturer = GET_DECL(model) - name = "[capitalize(model_manufacturer.name)] [bodypart_name]" + name = "[capitalize_words(model_manufacturer.name)] [bodypart_name]" description = "You have been fitted with [ADD_ARTICLE(model_manufacturer.name)] [lowertext(bodypart_name)] prosthesis." + available_at_map_tech = model_manufacturer.required_map_tech else name = "Prosthetic [bodypart_name]" description = "You have been fitted with a basic [lowertext(bodypart_name)] prosthesis." @@ -56,13 +70,13 @@ // We will also exclude from relevant amputations, but they will be handled by amputation trait build_references() // If our model has any additional trait handling, do it here. - // Without a model, we will rely on is_available_to() to check get_base_model() for the user species. + // Without a model, we will rely on is_available_to_select() to check get_base_model() for the user species. blocked_species = null /decl/trait/prosthetic_limb/applies_to_organ(var/organ) return apply_to_limb && organ == apply_to_limb -/decl/trait/prosthetic_limb/is_available_to(datum/preferences/pref) +/decl/trait/prosthetic_limb/is_available_to_select(datum/preferences/pref) . = ..() if(.) if(fullbody_synthetic_only) @@ -77,6 +91,10 @@ var/decl/bodytype/B = S.get_bodytype_by_name(pref.bodytype) if(!R.check_can_install(apply_to_limb, target_bodytype = (check_bodytype || B.bodytype_category))) return FALSE + // If we're a duplicate of our parent due to get_base_model(), hide this one. + var/decl/trait/prosthetic_limb/parent_limb = parent + if(parent && !parent_limb.model && parent_limb.get_base_model(pref.species) == model && parent_limb.bodypart_name == bodypart_name) + return FALSE // duplicate else if(!get_base_model(pref.species)) return FALSE @@ -104,7 +122,7 @@ if("path" in organ_data) E = new limb_path(holder, null, use_model) if(istype(E) && E.bodytype != model) // sometimes in the last line we save ourselves some work here - // this should be pre-validated by is_available_to() + // this should be pre-validated by is_available_to_select() if(replace_children) E.set_bodytype_with_children(use_model) else @@ -114,45 +132,53 @@ bodypart_name = "Left Hand" apply_to_limb = BP_L_HAND incompatible_with_limbs = list(BP_L_HAND, BP_L_ARM) + uid = "trait_prosthetic_left_hand" /decl/trait/prosthetic_limb/left_arm bodypart_name = "Left Arm" trait_cost = 2 apply_to_limb = BP_L_ARM incompatible_with_limbs = list(BP_L_HAND, BP_L_ARM) + uid = "trait_prosthetic_left_arm" /decl/trait/prosthetic_limb/right_hand bodypart_name = "Right Hand" apply_to_limb = BP_R_HAND incompatible_with_limbs = list(BP_R_HAND, BP_R_ARM) + uid = "trait_prosthetic_right_hand" /decl/trait/prosthetic_limb/right_arm bodypart_name = "Right Arm" trait_cost = 2 apply_to_limb = BP_R_ARM incompatible_with_limbs = list(BP_R_HAND, BP_R_ARM) + uid = "trait_prosthetic_right_arm" /decl/trait/prosthetic_limb/left_foot bodypart_name = "Left Foot" apply_to_limb = BP_L_FOOT incompatible_with_limbs = list(BP_L_FOOT, BP_L_LEG) + uid = "trait_prosthetic_left_foot" /decl/trait/prosthetic_limb/left_leg bodypart_name = "Left Leg" trait_cost = 2 apply_to_limb = BP_L_LEG incompatible_with_limbs = list(BP_L_FOOT, BP_L_LEG) + uid = "trait_prosthetic_left_leg" /decl/trait/prosthetic_limb/right_foot bodypart_name = "Right Foot" apply_to_limb = BP_R_FOOT incompatible_with_limbs = list(BP_R_FOOT, BP_R_LEG) + uid = "trait_prosthetic_right_foot" /decl/trait/prosthetic_limb/right_leg bodypart_name = "Right Leg" trait_cost = 2 apply_to_limb = BP_R_LEG incompatible_with_limbs = list(BP_R_FOOT, BP_R_LEG) + uid = "trait_prosthetic_right_leg" /decl/trait/prosthetic_limb/head bodypart_name = "Head" @@ -161,6 +187,7 @@ incompatible_with_limbs = list(BP_HEAD) fullbody_synthetic_only = TRUE replace_children = FALSE + uid = "trait_prosthetic_head" /decl/trait/prosthetic_limb/chest bodypart_name = "Upper Body" @@ -169,6 +196,7 @@ incompatible_with_limbs = list(BP_CHEST) fullbody_synthetic_only = TRUE replace_children = FALSE + uid = "trait_prosthetic_upper_body" /decl/trait/prosthetic_limb/groin bodypart_name = "Lower Body" @@ -177,3 +205,4 @@ incompatible_with_limbs = list(BP_GROIN) fullbody_synthetic_only = TRUE replace_children = FALSE + uid = "trait_prosthetic_lower_body" diff --git a/code/datums/traits/prosthetics/prosthetic_organs.dm b/code/datums/traits/prosthetics/prosthetic_organs.dm index a9d6c093de2..0ae6ce94956 100644 --- a/code/datums/traits/prosthetics/prosthetic_organs.dm +++ b/code/datums/traits/prosthetics/prosthetic_organs.dm @@ -1,18 +1,21 @@ /decl/trait/prosthetic_organ - name = "Prosthetic Heart" - description = "You have a synthetic heart." + abstract_type = /decl/trait/prosthetic_organ trait_cost = 1 available_at_chargen = TRUE + available_at_map_tech = MAP_TECH_LEVEL_SPACE category = "Prosthetic Organs" reapply_on_rejuvenation = TRUE var/synthetic_bodytype_restricted = FALSE - var/apply_to_organ = BP_HEART + var/apply_to_organ -/decl/trait/prosthetic_organ/is_available_to(datum/preferences/pref) +/decl/trait/prosthetic_organ/is_available_to_select(datum/preferences/pref) . = ..() if(. && pref.species && pref.bodytype) var/decl/species/mob_species = pref.get_species_decl() + if(!istype(mob_species) || isnull(mob_species.base_internal_prosthetics_model)) + return FALSE + var/decl/bodytype/mob_bodytype = pref.get_bodytype_decl() if(!istype(mob_bodytype)) @@ -44,6 +47,12 @@ if(I) I.set_bodytype(I.species.base_internal_prosthetics_model) +/decl/trait/prosthetic_organ/heart + name = "Prosthetic Heart" + description = "You have a synthetic heart." + uid = "trait_prosthetic_heart" + apply_to_organ = BP_HEART + /decl/trait/prosthetic_organ/eyes name = "Prosthetic Eyes" description = "Your vision is augmented." @@ -55,32 +64,38 @@ /decl/trait/malus/colourblind/tritanopia, /decl/trait/malus/colourblind/achromatopsia ) + uid = "trait_prosthetic_eyes" /decl/trait/prosthetic_organ/kidneys name = "Prosthetic Kidneys" description = "You have synthetic kidneys." apply_to_organ = BP_KIDNEYS + uid = "trait_prosthetic_kidneys" /decl/trait/prosthetic_organ/liver name = "Prosthetic Liver" description = "You have a literal iron liver." apply_to_organ = BP_LIVER + uid = "trait_prosthetic_liver" /decl/trait/prosthetic_organ/lungs name = "Prosthetic Lungs" description = "You have synthetic lungs." apply_to_organ = BP_LUNGS + uid = "trait_prosthetic_lungs" /decl/trait/prosthetic_organ/stomach name = "Prosthetic Stomach" description = "You have a literal iron stomach." apply_to_organ = BP_STOMACH + uid = "trait_prosthetic_stomach" /decl/trait/prosthetic_organ/brain name = "Synthetic Brain" description = "You are an artificial lifeform, with a mind made of steel and light." apply_to_organ = BP_BRAIN synthetic_bodytype_restricted = TRUE + uid = "trait_prosthetic_brain" var/new_brain_type = /obj/item/organ/internal/brain/robotic /decl/trait/prosthetic_organ/brain/apply_trait(mob/living/holder) diff --git a/code/datums/uplink/services.dm b/code/datums/uplink/services.dm index f7f37fc49af..03d4b72eebe 100644 --- a/code/datums/uplink/services.dm +++ b/code/datums/uplink/services.dm @@ -234,7 +234,7 @@ var/datum/job/job = SSjobs.get_by_title(new_record.get_job()) if(job) var/skills = list() - for(var/decl/hierarchy/skill/S in global.using_map.get_available_skills()) + for(var/decl/skill/S in global.using_map.get_available_skills()) var/level = job.min_skill[S.type] if(prob(10)) level = min(rand(1,3), job.max_skill[S.type]) diff --git a/code/datums/uplink/stealthy_and_inconspicuous_weapons.dm b/code/datums/uplink/stealthy_and_inconspicuous_weapons.dm index f57942a23d7..d085b9438d9 100644 --- a/code/datums/uplink/stealthy_and_inconspicuous_weapons.dm +++ b/code/datums/uplink/stealthy_and_inconspicuous_weapons.dm @@ -19,7 +19,7 @@ name = "Concealed Cane Sword" desc = "A cane used by a true gentlemen, especially ones with sharp intentions." item_cost = 8 - path = /obj/item/cane/concealed + path = /obj/item/cane/fancy/sword /datum/uplink_item/item/stealthy_weapons/wcoat_armored name = "Armoured Waistcoat" 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 4162b0eb3f8..05b23f45eaa 100644 --- a/code/game/alpha_masks.dm +++ b/code/game/alpha_masks.dm @@ -27,6 +27,8 @@ var/global/list/_alpha_masks = list() /atom/movable/alpha_mask/Destroy() if(owner) global._alpha_masks -= owner + events_repository.unregister(/decl/observ/moved, owner, src) + events_repository.unregister(/decl/observ/destroyed, owner, src) owner = null return ..() diff --git a/code/game/antagonist/antagonist_equip.dm b/code/game/antagonist/antagonist_equip.dm index 053e1552751..358f49da1a4 100644 --- a/code/game/antagonist/antagonist_equip.dm +++ b/code/game/antagonist/antagonist_equip.dm @@ -18,7 +18,7 @@ player.species.equip_survival_gear(player) if(default_outfit) - var/decl/hierarchy/outfit/outfit = GET_DECL(default_outfit) + var/decl/outfit/outfit = GET_DECL(default_outfit) outfit.equip_outfit(player) if(default_access) diff --git a/code/game/antagonist/outsider/actors.dm b/code/game/antagonist/outsider/actors.dm index cbc2f72fc8b..c18e449a882 100644 --- a/code/game/antagonist/outsider/actors.dm +++ b/code/game/antagonist/outsider/actors.dm @@ -10,11 +10,11 @@ initial_spawn_target = 1 show_objectives_on_creation = 0 //actors are not antagonists and do not need the antagonist greet text required_language = /decl/language/human/common - default_outfit = /decl/hierarchy/outfit/actor + default_outfit = /decl/outfit/actor default_access = list() id_title = "Actor" -/decl/hierarchy/outfit/actor +/decl/outfit/actor name = "Special Role - Actor" uniform = /obj/item/clothing/jumpsuit/chameleon shoes = /obj/item/clothing/shoes/chameleon diff --git a/code/game/antagonist/outsider/ert.dm b/code/game/antagonist/outsider/ert.dm index 0e6db1cebaf..439d9f7f23b 100644 --- a/code/game/antagonist/outsider/ert.dm +++ b/code/game/antagonist/outsider/ert.dm @@ -20,7 +20,7 @@ initial_spawn_req = 5 initial_spawn_target = 7 show_objectives_on_creation = 0 //we are not antagonists, we do not need the antagonist shpiel/objectives - default_outfit = /decl/hierarchy/outfit/ert + default_outfit = /decl/outfit/ert base_to_load = "ERT Base" diff --git a/code/game/antagonist/outsider/mercenary.dm b/code/game/antagonist/outsider/mercenary.dm index a45f958df65..7b9bbc83e96 100644 --- a/code/game/antagonist/outsider/mercenary.dm +++ b/code/game/antagonist/outsider/mercenary.dm @@ -18,7 +18,7 @@ faction = "mercenary" base_to_load = "Mercenary Base" - default_outfit = /decl/hierarchy/outfit/mercenary + default_outfit = /decl/outfit/mercenary /decl/special_role/mercenary/create_global_objectives() if(!..()) diff --git a/code/game/antagonist/outsider/wizard.dm b/code/game/antagonist/outsider/wizard.dm index d63f249ce37..9aa819de843 100644 --- a/code/game/antagonist/outsider/wizard.dm +++ b/code/game/antagonist/outsider/wizard.dm @@ -65,7 +65,7 @@ wizard.current.SetName(wizard.current.real_name) /decl/special_role/wizard/equip_role(var/mob/living/human/wizard_mob) - default_outfit = pick(decls_repository.get_decl_paths_of_subtype(/decl/hierarchy/outfit/wizard)) + default_outfit = pick(decls_repository.get_decl_paths_of_subtype(/decl/outfit/wizard)) . = ..() /decl/special_role/wizard/print_player_summary() diff --git a/code/game/area/area_fishing.dm b/code/game/area/area_fishing.dm index 7564c711f38..8370e39c449 100644 --- a/code/game/area/area_fishing.dm +++ b/code/game/area/area_fishing.dm @@ -10,7 +10,7 @@ /obj/item/hand/missing_card = 1 ) -/area/proc/get_fishing_result(turf/origin, obj/item/chems/food/bait) +/area/proc/get_fishing_result(turf/origin, obj/item/food/bait) if(!length(fishing_results) || prob(fishing_failure_prob)) return null return pickweight(fishing_results) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index ba392ffe5c9..8989d55d2ad 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -46,7 +46,20 @@ var/global/list/areas = list() var/obj/machinery/power/apc/apc var/list/all_doors //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area - var/list/ambience = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') + var/list/ambience = list( + 'sound/ambience/ambigen1.ogg', + 'sound/ambience/ambigen3.ogg', + 'sound/ambience/ambigen4.ogg', + 'sound/ambience/ambigen5.ogg', + 'sound/ambience/ambigen6.ogg', + 'sound/ambience/ambigen7.ogg', + 'sound/ambience/ambigen8.ogg', + 'sound/ambience/ambigen9.ogg', + 'sound/ambience/ambigen10.ogg', + 'sound/ambience/ambigen11.ogg', + 'sound/ambience/ambigen12.ogg', + 'sound/ambience/ambigen14.ogg' + ) var/list/forced_ambience var/sound_env = STANDARD_STATION var/description //A text-based description of what this area is for. @@ -125,10 +138,10 @@ var/global/list/areas = list() A.contents.Add(T) if(old_area) old_area.Exited(T, A) - for(var/atom/movable/AM in T) + for(var/atom/movable/AM as anything in T) old_area.Exited(AM, A) // Note: this _will_ raise exited events. A.Entered(T, old_area) - for(var/atom/movable/AM in T) + for(var/atom/movable/AM as anything in T) A.Entered(AM, old_area) // Note: this will _not_ raise moved or entered events. If you change this, you must also change everything which uses them. for(var/obj/machinery/M in T) @@ -146,9 +159,9 @@ var/global/list/areas = list() if(T.is_outside() != old_outside) T.update_weather() - SSambience.queued |= T + AMBIENCE_QUEUE_TURF(T) else if(A.interior_ambient_light_modifier != old_area_ambience) - SSambience.queued |= T + AMBIENCE_QUEUE_TURF(T) /turf/proc/update_registrations_on_adjacent_area_change() for(var/obj/machinery/door/firedoor/door in src) @@ -401,7 +414,7 @@ var/global/list/mob/living/forced_ambiance_list = new if(isspaceturf(get_turf(mob))) // Can't fall onto nothing. return - if(mob.Check_Shoegrip()) + if(!mob.can_slip(magboots_only = TRUE)) return if(ishuman(mob)) @@ -421,6 +434,8 @@ var/global/list/mob/living/forced_ambiance_list = new /area/proc/throw_unbuckled_occupant(var/mob/M, var/maxrange, var/speed, var/direction) if(isliving(M)) + if(M.anchored) // So mechs don't get tossed around. + return if(M.buckled) to_chat(M, SPAN_WARNING("Sudden acceleration presses you into your chair!")) shake_camera(M, 3, 1) diff --git a/code/game/atom_edibility.dm b/code/game/atom_edibility.dm new file mode 100644 index 00000000000..5d4791ae614 --- /dev/null +++ b/code/game/atom_edibility.dm @@ -0,0 +1,10 @@ +/atom/proc/show_food_empty_message(mob/user, consumption_method = EATING_METHOD_EAT) + to_chat(user, SPAN_NOTICE("\The [src] is empty of anyting [consumption_method == EATING_METHOD_EAT ? "edible" : "potable"].")) + +/atom/proc/show_food_no_mouth_message(mob/user, mob/target) + target = target || user + if(user) + if(user == target) + to_chat(user, SPAN_WARNING("Where do you intend to put \the [src]? You don't have a mouth!")) + else + to_chat(user, SPAN_WARNING("Where do you intend to put \the [src]? \The [target] doesn't have a mouth!")) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 1ea0a94c89f..ab3ed74209c 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -57,6 +57,8 @@ /// (FLOAT) Theoretical maximum health value. var/max_health + /// (BOOL) Does this atom respond to changes in local temperature via the `temperature` var? + var/temperature_sensitive = FALSE /// (DATUM) /datum/storage instance to use for this obj. Set to a type for instantiation on init. var/datum/storage/storage /// (FLOAT) world.time of last on_reagent_update call, used to prevent recursion due to reagents updating reagents @@ -340,6 +342,14 @@ RAISE_EVENT(/decl/observ/dir_set, src, old_dir, new_dir) + +/// Set the icon to `new_icon` +/atom/proc/set_icon(new_icon) + if(icon != new_icon) + icon = new_icon + return TRUE + return FALSE + /// Set the icon_state to `new_icon_state` /atom/proc/set_icon_state(var/new_icon_state) SHOULD_CALL_PARENT(TRUE) @@ -378,7 +388,7 @@ if(length(reagents?.reagent_volumes)) LAZYINITLIST(.) for(var/R in reagents.reagent_volumes) - .[R] += FLOOR(REAGENT_VOLUME(reagents, R) / REAGENT_UNITS_PER_MATERIAL_UNIT) + .[R] += floor(REAGENT_VOLUME(reagents, R) / REAGENT_UNITS_PER_MATERIAL_UNIT) for(var/atom/contained_obj as anything in get_contained_external_atoms()) // machines handle component parts separately . = MERGE_ASSOCS_WITH_NUM_VALUES(., contained_obj.get_contained_matter()) @@ -415,7 +425,7 @@ M.client.perspective = MOB_PERSPECTIVE /** - Handle the destruction of this atom, spilling it's contents by default + Handle the destruction of this atom, spilling its contents by default - `skip_qdel`: If calling qdel() on this atom should be skipped. - Return: Unknown, feel free to change this @@ -490,10 +500,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 @@ -574,7 +586,7 @@ var/turf/T = get_turf(src) var/list/mobs = list() var/list/objs = list() - get_mobs_and_objs_in_view_fast(T,range, mobs, objs, check_ghosts) + get_listeners_in_range(T,range, mobs, objs, check_ghosts) for(var/o in objs) var/obj/O = o @@ -602,7 +614,7 @@ var/turf/T = get_turf(src) var/list/mobs = list() var/list/objs = list() - get_mobs_and_objs_in_view_fast(T, hearing_distance, mobs, objs, check_ghosts) + get_listeners_in_range(T, hearing_distance, mobs, objs, check_ghosts) for(var/m in mobs) var/mob/M = m @@ -651,7 +663,7 @@ - `G`: The grab hitting this atom - Return: `TRUE` to skip attackby() and afterattack() or `FALSE` */ -/atom/proc/grab_attack(var/obj/item/grab/G) +/atom/proc/grab_attack(obj/item/grab/grab, mob/user) return FALSE /atom/proc/climb_on() @@ -727,7 +739,10 @@ LAZYREMOVE(climbers,user) return FALSE - var/target_turf = get_turf(src) + // handle multitile objects + // this should also be fine for non-multitile objects + // and ensures we don't ever move more than 1 tile + var/target_turf = get_step(user, get_dir(user, src)) //climbing over border objects like railings if((atom_flags & ATOM_FLAG_CHECKS_BORDER) && get_turf(user) == target_turf) @@ -740,7 +755,7 @@ LAZYREMOVE(climbers,user) return TRUE -/// Shake this atom and all it's climbers. +/// Shake this atom and all its climbers. /atom/proc/object_shaken() for(var/mob/living/M in climbers) SET_STATUS_MAX(M, STAT_WEAK, 1) @@ -879,6 +894,15 @@ return check_loc check_loc = check_loc.loc +/** + 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 for a user from this atom. @@ -891,6 +915,9 @@ . = 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 @@ -956,3 +983,31 @@ /atom/proc/spark_act(obj/effect/sparks/sparks) return + +/atom/proc/get_affecting_weather() + return + +/atom/proc/is_outside() + var/turf/turf = get_turf(src) + return istype(turf) ? turf.is_outside() : OUTSIDE_UNCERTAIN + +/atom/proc/can_be_poured_into(atom/source) + return (reagents?.maximum_volume > 0) && ATOM_IS_OPEN_CONTAINER(src) + +/// This is whether it's physically possible to pour from this atom to the target atom, based on context like user intent and src being open, etc. +/// This should not check things like whether there is actually anything in src to pour. +/// It should also not check anything controlled by the target atom, because can_be_poured_into() already exists. +/atom/proc/can_be_poured_from(mob/user, atom/target) + return (reagents?.maximum_volume > 0) && ATOM_IS_OPEN_CONTAINER(src) + +/atom/proc/take_vaporized_reagent(reagent, amount) + return + +/atom/proc/is_watertight() + 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/immune_to_floor_hazards() + return !simulated || !has_gravity() diff --git a/code/game/atoms_fluids.dm b/code/game/atoms_fluids.dm index 450cbc6f52d..8f3f7def788 100644 --- a/code/game/atoms_fluids.dm +++ b/code/game/atoms_fluids.dm @@ -3,7 +3,7 @@ /atom/proc/fluid_act(var/datum/reagents/fluids) SHOULD_CALL_PARENT(TRUE) - if(reagents && reagents != fluids && fluids?.total_volume >= FLUID_SHALLOW && ATOM_IS_OPEN_CONTAINER(src)) + if(reagents && reagents != fluids && fluids?.total_volume >= FLUID_SHALLOW && !is_watertight()) reagents.trans_to_holder(fluids, reagents.total_volume) fluids.trans_to_holder(reagents, min(fluids.total_volume, reagents.maximum_volume)) @@ -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) diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm index dcbd9a7ba2c..3a2f3b619c1 100644 --- a/code/game/atoms_init.dm +++ b/code/game/atoms_init.dm @@ -9,6 +9,7 @@ if(!istype(storage)) storage = null + // This preloader code is also duplicated in /turf/unsimulated/New(). If you change this, be sure to change it there, too. //atom creation method that preloads variables at creation if(global.use_preloader && (src.type == global._preloader.target_path))//in case the instanciated atom is creating other atoms in New() global._preloader.load(src) @@ -65,7 +66,7 @@ if(light_power && light_range) update_light() - if(opacity) + if(simulated && opacity) updateVisibility(src) var/turf/T = loc if(istype(T)) @@ -87,7 +88,7 @@ LAZYCLEARLIST(priority_overlays) LAZYCLEARLIST(climbers) QDEL_NULL(light) - if(opacity) + if(simulated && opacity) updateVisibility(src) if(atom_codex_ref && atom_codex_ref != TRUE) // may be null, TRUE or a datum instance QDEL_NULL(atom_codex_ref) @@ -117,6 +118,8 @@ // Changing this behavior will almost certainly break power; update accordingly. if (!ml && loc) loc.Entered(src, null) + if(loc && (z_flags & ZMM_WIDE_LOAD)) + SSzcopy.discover_movable(src) /atom/movable/EarlyDestroy(force = FALSE) loc = null // should NOT use forceMove, in order to avoid events @@ -127,17 +130,14 @@ if(isatom(virtual_mob)) QDEL_NULL(virtual_mob) + unregister_all_movement(loc, src) // unregister events before destroy to avoid expensive checking + // If you want to keep any of these atoms, handle them before ..() - for(var/thing in contents) // proooobably safe to assume they're never going to have non-movables in contents? + for(var/atom/movable/thing as anything in src) // safe to assume they're never going to have non-movables in contents qdel(thing) - unregister_all_movement(loc, src) // unregister events before destroy to avoid expensive checking - . = ..() - for(var/A in src) - qdel(A) - forceMove(null) if(LAZYLEN(movement_handlers) && !ispath(movement_handlers[1])) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 4498d6d993e..bb82fc70618 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -46,7 +46,7 @@ //call this proc to start space drifting /atom/movable/proc/space_drift(direction)//move this down - if(!loc || direction & (UP|DOWN) || Process_Spacemove(0)) + if(!loc || direction & (UP|DOWN) || is_space_movement_permitted() != SPACE_MOVE_FORBIDDEN) inertia_dir = 0 inertia_ignore = null return 0 @@ -59,29 +59,22 @@ return 1 //return 0 to space drift, 1 to stop, -1 for mobs to handle space slips -/atom/movable/proc/Process_Spacemove(var/allow_movement) +/atom/movable/proc/is_space_movement_permitted(allow_movement = FALSE) if(!simulated) - return 1 - + return SPACE_MOVE_PERMITTED if(has_gravity()) - return 1 - + return SPACE_MOVE_PERMITTED if(length(grabbed_by)) - return 1 - + return SPACE_MOVE_PERMITTED if(throwing) - return 1 - + return SPACE_MOVE_PERMITTED if(anchored) - return 1 - + return SPACE_MOVE_PERMITTED if(!isturf(loc)) - return 1 - + return SPACE_MOVE_PERMITTED if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier - return -1 - - return 0 + return SPACE_MOVE_SUPPORTED + return SPACE_MOVE_FORBIDDEN /atom/movable/attack_hand(mob/user) // Unbuckle anything buckled to us. @@ -193,7 +186,7 @@ RAISE_EVENT(/decl/observ/moved, src, origin, null) // freelook - if(opacity) + if(simulated && opacity) updateVisibility(src) // lighting @@ -245,7 +238,7 @@ RAISE_EVENT(/decl/observ/moved, src, old_loc, null) // freelook - if(opacity) + if(simulated && opacity) updateVisibility(src) // lighting @@ -432,9 +425,9 @@ /atom/movable/proc/show_buckle_message(var/mob/buckled, var/mob/buckling) if(buckled == buckling) - var/decl/pronouns/G = buckled.get_pronouns() + var/decl/pronouns/pronouns = buckled.get_pronouns() visible_message( - SPAN_NOTICE("\The [buckled] buckles [G.self] to \the [src]."), + SPAN_NOTICE("\The [buckled] buckles [pronouns.self] to \the [src]."), SPAN_NOTICE("You buckle yourself to \the [src]."), SPAN_NOTICE("You hear metal clanking.") ) @@ -449,16 +442,16 @@ var/mob/living/M = unbuckle_mob() if(M) show_unbuckle_message(M, user) - for(var/obj/item/grab/G as anything in (M.grabbed_by|grabbed_by)) - qdel(G) + for(var/obj/item/grab/grab as anything in (M.grabbed_by|grabbed_by)) + qdel(grab) add_fingerprint(user) return M /atom/movable/proc/show_unbuckle_message(var/mob/buckled, var/mob/buckling) if(buckled == buckling) - var/decl/pronouns/G = buckled.get_pronouns() + var/decl/pronouns/pronouns = buckled.get_pronouns() visible_message( - SPAN_NOTICE("\The [buckled] unbuckled [G.self] from \the [src]!"), + SPAN_NOTICE("\The [buckled] unbuckled [pronouns.self] from \the [src]!"), SPAN_NOTICE("You unbuckle yourself from \the [src]."), SPAN_NOTICE("You hear metal clanking.") ) @@ -570,3 +563,28 @@ var/datum/movement_handler/delay/delay = locate() in movement_handlers if(istype(delay)) delay.next_move = world.time + +/atom/movable/get_affecting_weather() + var/turf/my_turf = get_turf(src) + if(!istype(my_turf)) + return + var/turf/actual_loc = loc + // If we're standing in the rain, use the turf weather. + . = istype(actual_loc) && actual_loc.weather + if(!.) // If we're under or inside shelter, use the z-level rain (for ambience) + . = SSweather.weather_by_z[my_turf.z] + +/atom/movable/take_vaporized_reagent(reagent, amount) + if(ATOM_IS_OPEN_CONTAINER(src)) + return loc?.take_vaporized_reagent(reagent, amount) + return null + +/atom/movable/immune_to_floor_hazards() + return ..() || throwing + +// 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_overlay.dm b/code/game/atoms_movable_overlay.dm index 9d286c7913f..c6beb4d1bec 100644 --- a/code/game/atoms_movable_overlay.dm +++ b/code/game/atoms_movable_overlay.dm @@ -42,6 +42,7 @@ /atom/movable/overlay/attackby(obj/item/I, mob/user) if (master) return master.attackby(I, user) + return TRUE /atom/movable/overlay/attack_hand(mob/user) SHOULD_CALL_PARENT(FALSE) diff --git a/code/game/atoms_temperature.dm b/code/game/atoms_temperature.dm index b30d2576a66..2beb585761a 100644 --- a/code/game/atoms_temperature.dm +++ b/code/game/atoms_temperature.dm @@ -28,22 +28,30 @@ /atom/proc/get_manual_heat_source_coefficient() return 1 +/// Returns the 'ambient temperature' used for temperature equalisation. +/atom/proc/get_ambient_temperature() + if(isturf(loc)) + return loc.return_air().temperature + else if(loc) + return loc.temperature + // Nullspace is room temperature, clearly. + return T20C + +/// Returns the coefficient used for ambient temperature equalisation. +/// Mainly used to prevent vacuum from cooling down objects. +/atom/proc/get_ambient_temperature_coefficient() + if(isturf(loc)) + //scale the thermal mass coefficient so that 1atm = 1x, 0atm = 0x, 10atm = 10x + return loc.return_air().return_pressure() / ONE_ATMOSPHERE + return 1 + // TODO: move mob bodytemperature onto this proc. /atom/proc/ProcessAtomTemperature() SHOULD_NOT_SLEEP(TRUE) - // Get our location temperature if possible. - // Nullspace is room temperature, clearly. - var/adjust_temp = T20C - var/thermal_mass_coefficient = get_thermal_mass_coefficient() - if(isturf(loc)) - var/turf/T = loc - var/datum/gas_mixture/environment = T.return_air() - adjust_temp = environment.temperature - //scale the thermal mass coefficient so that 1atm = 1x, 0atm = 0x, 10atm = 10x - thermal_mass_coefficient *= (environment.return_pressure() / ONE_ATMOSPHERE) - else if(loc) - adjust_temp = loc.temperature + // Get our ambient temperature if possible. + var/adjust_temp = get_ambient_temperature() + var/thermal_mass_coefficient = get_thermal_mass_coefficient() * get_ambient_temperature_coefficient() // Determine if our temperature needs to change. var/old_temp = temperature diff --git a/code/game/gamemodes/endgame/ftl_jump/ftl_jump.dm b/code/game/gamemodes/endgame/ftl_jump/ftl_jump.dm index b95a69b21c6..aed8aa4e95e 100644 --- a/code/game/gamemodes/endgame/ftl_jump/ftl_jump.dm +++ b/code/game/gamemodes/endgame/ftl_jump/ftl_jump.dm @@ -104,7 +104,7 @@ if(nloc == new_loc) reality++ if(reality > 5) - to_chat(daddy, SPAN_NOTICE("Yep, it's certainly the other one. Your existance was a glitch, and it's finally being mended...")) + to_chat(daddy, SPAN_NOTICE("Yep, it's certainly the other one. Your existence was a glitch, and it's finally being mended...")) blueswitch() else if(reality > 3) to_chat(daddy, SPAN_DANGER("Something is definitely wrong. Why do you think YOU are the original?")) @@ -121,7 +121,7 @@ /obj/effect/bluegoast/proc/blueswitch() var/mob/living/human/H if(ishuman(daddy)) - H = new(get_turf(src), daddy.species.name, daddy.get_mob_snapshot(force = TRUE), daddy.get_bodytype()) + H = new(get_turf(src), daddy.species.name, daddy.get_mob_snapshot(), daddy.get_bodytype()) for(var/obj/item/entry in daddy.get_equipped_items(TRUE)) daddy.remove_from_mob(entry) //steals instead of copies so we don't end up with duplicates H.equip_to_appropriate_slot(entry) diff --git a/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm b/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm index 6da5185fc4c..e01a6995e1b 100644 --- a/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm +++ b/code/game/gamemodes/endgame/nuclear_explosion/nuclear_explosion.dm @@ -49,7 +49,7 @@ var/turf/T = get_turf(L) if(T && (T.z in affected_z_levels)) //this is needed because dusting resets client screen 1.5 seconds after being called (delayed due to the dusting animation) - var/mob/ghost = L.ghostize(0) //So we ghostize them right beforehand instead + var/mob/ghost = L.ghostize(CORPSE_CANNOT_REENTER) //So we ghostize them right beforehand instead if(ghost && ghost.client) ghost.client.screen += cinematic L.dust() //then dust the body diff --git a/code/game/gamemodes/endgame/supermatter_cascade/cascade_blob.dm b/code/game/gamemodes/endgame/supermatter_cascade/cascade_blob.dm index 92a0e442676..ef04b1069f7 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/cascade_blob.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/cascade_blob.dm @@ -13,7 +13,7 @@ var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST,UP,DOWN) -/turf/unsimulated/wall/cascade/Initialize(mapload, ...) +/turf/unsimulated/wall/cascade/New() . = ..() START_PROCESSING(SSturf, src) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index 8de40b0d123..e9b2d2e40fd 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -15,9 +15,9 @@ var/global/universe_has_ended = 0 /datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T) var/turf/space/S = T if(istype(S)) - S.color = "#0066ff" + S.set_color("#0066ff") else - S.color = initial(S.color) + S.set_color(initial(S.color)) /datum/universal_state/supermatter_cascade/DecayTurf(var/turf/T) T.handle_universal_decay() diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 8fca9e3f5cf..9a95ba9802f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -543,7 +543,7 @@ var/global/list/additional_antag_types = list() continue //Admin paralyzed if(L.stat) if(L.stat == UNCONSCIOUS) - msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" + msg += "[L.name] ([L.ckey]), the [L.job] (Unconscious)\n" continue //Unconscious if(L.stat == DEAD) msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 410e50873a8..bd4b8000d88 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -22,13 +22,6 @@ var/global/list/nuke_disks = list() var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level -//checks if L has a nuke disk on their person -/decl/game_mode/nuclear/proc/check_mob(mob/living/L) - for(var/obj/item/disk/nuclear/N in nuke_disks) - if(N.storage_depth(L) >= 0) - return TRUE - return FALSE - /decl/game_mode/nuclear/declare_completion() var/decl/special_role/merc = GET_DECL(/decl/special_role/mercenary) if(get_config_value(/decl/config/enum/objectives_disabled) == CONFIG_OBJECTIVE_NONE || (merc && !merc.global_objectives.len)) diff --git a/code/game/gamemodes/objectives/objective_capture.dm b/code/game/gamemodes/objectives/objective_capture.dm deleted file mode 100644 index 3f45d46c93e..00000000000 --- a/code/game/gamemodes/objectives/objective_capture.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/objective/capture/proc/gen_amount_goal() - target_amount = rand(5,10) - explanation_text = "Accumulate [target_amount] capture points." - return target_amount diff --git a/code/game/gamemodes/objectives/objective_demote.dm b/code/game/gamemodes/objectives/objective_demote.dm index 0edfc3ce986..de907a39a73 100644 --- a/code/game/gamemodes/objectives/objective_demote.dm +++ b/code/game/gamemodes/objectives/objective_demote.dm @@ -1,8 +1,8 @@ /datum/objective/anti_revolution/demote/find_target() ..() if(target && target.current) - var/decl/pronouns/G = target.current.get_pronouns(ignore_coverings = TRUE) - explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [global.using_map.company_name]'s goals. Demote [G.him] to assistant." + var/decl/pronouns/pronouns = target.current.get_pronouns(ignore_coverings = TRUE) + explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [global.using_map.company_name]'s goals. Demote [pronouns.him] to assistant." else explanation_text = "Free Objective" return target diff --git a/code/game/gamemodes/objectives/objective_execute.dm b/code/game/gamemodes/objectives/objective_execute.dm index ea740533807..312df8d501a 100644 --- a/code/game/gamemodes/objectives/objective_execute.dm +++ b/code/game/gamemodes/objectives/objective_execute.dm @@ -1,8 +1,8 @@ /datum/objective/anti_revolution/execute/find_target() ..() if(target && target.current) - var/decl/pronouns/G = target.current.get_pronouns(ignore_coverings = TRUE) - explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute [G.him]." + var/decl/pronouns/pronouns = target.current.get_pronouns(ignore_coverings = TRUE) + explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute [pronouns.him]." else explanation_text = "Free Objective" return target diff --git a/code/game/gamemodes/objectives/objective_heist.dm b/code/game/gamemodes/objectives/objective_heist.dm index 26fa59d055e..ef72208917a 100644 --- a/code/game/gamemodes/objectives/objective_heist.dm +++ b/code/game/gamemodes/objectives/objective_heist.dm @@ -25,44 +25,47 @@ explanation_text = "Free Objective" return target +/datum/objective/loot + var/target_obj + /datum/objective/loot/find_target() var/loot = "an object" switch(rand(1,8)) if(1) - target = /obj/structure/particle_accelerator + target_obj = /obj/structure/particle_accelerator target_amount = 6 loot = "a complete particle accelerator" if(2) - target = /obj/machinery/singularity_generator + target_obj = /obj/machinery/singularity_generator target_amount = 1 loot = "a gravitational generator" if(3) - target = /obj/machinery/emitter + target_obj = /obj/machinery/emitter target_amount = 4 loot = "four emitters" if(4) - target = /obj/machinery/nuclearbomb + target_obj = /obj/machinery/nuclearbomb target_amount = 1 loot = "a nuclear bomb" if(5) - target = /obj/item/gun + target_obj = /obj/item/gun target_amount = 6 loot = "six guns" if(6) - target = /obj/item/gun/energy + target_obj = /obj/item/gun/energy target_amount = 4 loot = "four energy guns" if(7) - target = /obj/item/gun/energy/laser + target_obj = /obj/item/gun/energy/laser target_amount = 2 loot = "two laser guns" if(8) - target = /obj/item/gun/energy/ionrifle + target_obj = /obj/item/gun/energy/ionrifle target_amount = 1 loot = "an ion gun" explanation_text = "It's a buyer's market out here. Steal [loot] for resale." - return target + return target_obj /datum/objective/salvage/find_target() var/list/loot = list( diff --git a/code/game/gamemodes/wizard/servant_items/caretaker.dm b/code/game/gamemodes/wizard/servant_items/caretaker.dm index 60707257859..c5adb272efe 100644 --- a/code/game/gamemodes/wizard/servant_items/caretaker.dm +++ b/code/game/gamemodes/wizard/servant_items/caretaker.dm @@ -9,7 +9,7 @@ ARMOR_ENERGY = ARMOR_ENERGY_SMALL, ARMOR_RAD = ARMOR_RAD_SHIELDED ) - bodytype_equip_flags = BODY_FLAG_HUMANOID + bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID flags_inv = HIDEEARS | BLOCK_HEAD_HAIR /obj/item/clothing/suit/caretakercloak diff --git a/code/game/gamemodes/wizard/servant_items/champion.dm b/code/game/gamemodes/wizard/servant_items/champion.dm index bb5e7504f49..80e04ee1bfd 100644 --- a/code/game/gamemodes/wizard/servant_items/champion.dm +++ b/code/game/gamemodes/wizard/servant_items/champion.dm @@ -11,7 +11,7 @@ ARMOR_BOMB = ARMOR_BOMB_RESISTANT, ARMOR_BIO = ARMOR_BIO_MINOR ) - bodytype_equip_flags = BODY_FLAG_HUMANOID + bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID /obj/item/clothing/suit/champarmor name = "champion's armor" @@ -44,7 +44,6 @@ name = "leather boots" desc = "Old-fashioned leather boots. Probably not something you want to get kicked with." material = /decl/material/solid/organic/leather - force = 5 armor = list( ARMOR_MELEE = ARMOR_MELEE_RESISTANT, ARMOR_BULLET = ARMOR_BALLISTIC_MINOR, diff --git a/code/game/gamemodes/wizard/servant_items/fiend.dm b/code/game/gamemodes/wizard/servant_items/fiend.dm index f4c52f75866..e52606eb62f 100644 --- a/code/game/gamemodes/wizard/servant_items/fiend.dm +++ b/code/game/gamemodes/wizard/servant_items/fiend.dm @@ -9,7 +9,7 @@ ARMOR_ENERGY = ARMOR_ENERGY_SMALL, ARMOR_RAD = ARMOR_RAD_SHIELDED ) - bodytype_equip_flags = BODY_FLAG_HUMANOID + bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID flags_inv = HIDEEARS | BLOCK_HEAD_HAIR /obj/item/clothing/suit/fiendcowl @@ -29,7 +29,7 @@ name = "black suit" desc = "A snappy black suit with red trim. The undershirt's stained with something, though..." icon = 'icons/clothing/suits/suit_fiend.dmi' - bodytype_equip_flags = BODY_FLAG_HUMANOID + bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID /obj/item/clothing/shoes/dress/devilshoes desc = "Off-colour leather dress shoes. Their footsteps are silent." diff --git a/code/game/gamemodes/wizard/servant_items/infiltrator.dm b/code/game/gamemodes/wizard/servant_items/infiltrator.dm index 0efb01274eb..846addc5b7c 100644 --- a/code/game/gamemodes/wizard/servant_items/infiltrator.dm +++ b/code/game/gamemodes/wizard/servant_items/infiltrator.dm @@ -11,7 +11,7 @@ ARMOR_LASER = ARMOR_LASER_MINOR, ARMOR_ENERGY = ARMOR_ENERGY_MINOR ) - bodytype_equip_flags = BODY_FLAG_HUMANOID + bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID /obj/item/clothing/suit/infilsuit name = "immaculate suit" diff --git a/code/game/gamemodes/wizard/servant_items/overseer.dm b/code/game/gamemodes/wizard/servant_items/overseer.dm index 9274cb20574..f8676e0440d 100644 --- a/code/game/gamemodes/wizard/servant_items/overseer.dm +++ b/code/game/gamemodes/wizard/servant_items/overseer.dm @@ -12,7 +12,7 @@ item_flags = ITEM_FLAG_AIRTIGHT max_pressure_protection = FIRESUIT_MAX_PRESSURE min_pressure_protection = 0 - bodytype_equip_flags = BODY_FLAG_HUMANOID + bodytype_equip_flags = BODY_EQUIP_FLAG_HUMANOID flags_inv = HIDEEARS | BLOCK_HEAD_HAIR /obj/item/clothing/suit/straight_jacket/overseercloak diff --git a/code/game/jobs/job/_job.dm b/code/game/jobs/job/_job.dm index 62125676c80..93255d8d5b4 100644 --- a/code/game/jobs/job/_job.dm +++ b/code/game/jobs/job/_job.dm @@ -43,7 +43,7 @@ var/list/minimal_access = list() // Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) var/list/access = list() // Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) - //Minimum skills allowed for the job. List should contain skill (as in /decl/hierarchy/skill path), with values which are numbers. + //Minimum skills allowed for the job. List should contain skill (as in /decl/skill path), with values which are numbers. var/min_skill = list( SKILL_LITERACY = SKILL_ADEPT ) @@ -51,6 +51,8 @@ var/skill_points = 16 //The number of unassigned skill points the job comes with (on top of the minimum skills). var/no_skill_buffs = FALSE //Whether skills can be buffed by age/species modifiers. var/available_by_default = TRUE + /// If TRUE, 'Not available at roundstart.' won't be shown for this job if available_by_default is FALSE. + var/suppress_no_roundstart_warning = FALSE var/list/possible_goals var/min_goals = 1 @@ -68,7 +70,7 @@ if(type == /datum/job && global.using_map.default_job_type == type) title = "Debug Job" hud_icon = "hudblank" - outfit_type = /decl/hierarchy/outfit/job/generic/scientist + outfit_type = /decl/outfit/job/generic/scientist autoset_department = TRUE if(!length(department_types) && autoset_department) @@ -96,7 +98,7 @@ else H.set_default_language(/decl/language/human/common) - var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title, branch, grade) + var/decl/outfit/outfit = get_outfit(H, alt_title, branch, grade) if(outfit) . = outfit.equip_outfit(H, alt_title || title, job = src, rank = grade) @@ -120,64 +122,69 @@ . = allowed_branches[branch.type] || . if(allowed_ranks && grade) . = allowed_ranks[grade.type] || . - . = . || outfit_type - . = outfit_by_type(.) + . ||= outfit_type + return GET_DECL(.) -/datum/job/proc/create_cash_on_hand(var/mob/living/human/H, var/datum/money_account/M) - if(!istype(M) || !H.client?.prefs?.starting_cash_choice) +/datum/job/proc/create_cash_on_hand(var/mob/living/human/worker, var/datum/money_account/account) + if(!istype(account) || !worker.client?.prefs?.starting_cash_choice) return 0 - for(var/obj/item/thing in H.client.prefs.starting_cash_choice.get_cash_objects(H, M)) + for(var/obj/item/thing in worker.client.prefs.starting_cash_choice.get_cash_objects(worker, account)) . += thing.get_base_value() - H.equip_to_storage_or_put_in_hands(thing) + worker.equip_to_storage_or_put_in_hands(thing) -/datum/job/proc/get_total_starting_money(var/mob/living/human/H) +/datum/job/proc/get_total_starting_money(var/mob/living/human/worker) . = 4 * rand(75, 100) * economic_power - // Get an average economic power for our cultures. - var/culture_mod = 0 - var/culture_count = 0 - for(var/token in H.cultural_info) - var/decl/cultural_info/culture = H.get_cultural_value(token) - if(culture && !isnull(culture.economic_power)) - culture_count++ - culture_mod += culture.economic_power - if(culture_count) - culture_mod /= culture_count - . *= culture_mod + // Get an average economic power for our background. + var/background_mod = 0 + var/background_count = 0 + for(var/token in worker.background_info) + var/decl/background_detail/background = worker.get_background_datum(token) + if(!isnull(background?.economic_power)) + background_count++ + background_mod += background.economic_power + if(background_count) + background_mod /= background_count + . *= background_mod // Apply other mods. . *= global.using_map.salary_modifier - . *= 1 + 2 * H.get_skill_value(SKILL_FINANCE)/(SKILL_MAX - SKILL_MIN) + // Apply a 50% bonus per skill level above minimum. + . *= 1 + 2 * (worker.get_skill_value(SKILL_FINANCE) - SKILL_MIN)/(SKILL_MAX - SKILL_MIN) . = round(.) -/datum/job/proc/setup_account(var/mob/living/human/H) - if(!account_allowed || (H.mind && H.mind.initial_account)) +/datum/job/proc/setup_account(var/mob/living/human/worker) + if(!account_allowed || worker.mind?.initial_account) return // Calculate our pay and apply all relevant modifiers. - var/money_amount = get_total_starting_money(H) + var/money_amount = get_total_starting_money(worker) if(money_amount <= 0) return // You are too poor for an account. //give them an account in the station database - var/datum/money_account/M = create_account("[H.real_name]'s account", H.real_name, money_amount) - var/cash_on_hand = create_cash_on_hand(H, M) + var/datum/money_account/account = create_account("[worker.real_name]'s account", worker.real_name, money_amount) + var/cash_on_hand = create_cash_on_hand(worker, account) // Store their financial info. - if(H.mind) + if(worker.mind) var/remembered_info = "" - remembered_info += "Your account number is: #[M.account_number]
" - remembered_info += "Your account pin is: [M.remote_access_pin]
" - remembered_info += "Your account funds are: [M.format_value_by_currency(M.money)]
" - if(M.transaction_log.len) - var/datum/transaction/T = M.transaction_log[1] + remembered_info += "Your account number is: #[account.account_number]
" + remembered_info += "Your account pin is: [account.remote_access_pin]
" + remembered_info += "Your account funds are: [account.format_value_by_currency(account.money)]
" + if(account.transaction_log.len) + var/datum/transaction/T = account.transaction_log[1] remembered_info += "Your account was created: [T.time], [T.date] at [T.get_source_name()]
" if(cash_on_hand > 0) var/decl/currency/cur = GET_DECL(global.using_map.default_currency) remembered_info += "Your cash on hand is: [cur.format_value(cash_on_hand)]
" - H.StoreMemory(remembered_info, /decl/memory_options/system) - H.mind.initial_account = M + worker.StoreMemory(remembered_info, /decl/memory_options/system) + worker.mind.initial_account = account + for(var/obj/item/card/id/I in worker.GetIdCards()) + if(!I.associated_account_number) + I.associated_account_number = account.account_number + break // overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/qdel() /datum/job/proc/equip_preview(mob/living/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade, var/additional_skips) - var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title, branch, grade) + var/decl/outfit/outfit = get_outfit(H, alt_title, branch, grade) if(!outfit) return FALSE . = outfit.equip_outfit(H, alt_title || title, equip_adjustments = (OUTFIT_ADJUSTMENT_SKIP_POST_EQUIP|OUTFIT_ADJUSTMENT_SKIP_ID_PDA|additional_skips), job = src, rank = grade) diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/server_whitelist.dm similarity index 57% rename from code/game/jobs/whitelist.dm rename to code/game/jobs/server_whitelist.dm index e8e49a0f68a..083876ecad3 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/server_whitelist.dm @@ -1,24 +1,36 @@ -#define WHITELISTFILE "data/whitelist.txt" +var/global/list/server_whitelist -var/global/list/whitelist = list() - -/hook/startup/proc/loadWhitelist() - if(get_config_value(/decl/config/toggle/usewhitelist)) - load_whitelist() - return 1 - -/proc/load_whitelist() - whitelist = file2list(WHITELISTFILE) - if(!length(whitelist)) - whitelist = null - -/proc/check_whitelist(mob/M /*, var/rank*/) - if(!whitelist) - return 0 - return ("[M.ckey]" in whitelist) +/proc/check_server_whitelist(ckey) + if(get_config_value(/decl/config/enum/server_whitelist) == CONFIG_SERVER_NO_WHITELIST) + return TRUE + if(ismob(ckey)) + var/mob/checking = ckey + ckey = checking.ckey + if(!istext(ckey)) + return FALSE + if(!global.server_whitelist) + global.server_whitelist = file2list(CONFIG_SERVER_WHITELIST_FILE) || list() + return (ckey in global.server_whitelist) + +/proc/save_server_whitelist() + // Ensure we have the server whitelist loaded regardless of config or prior call. + if(!global.server_whitelist) + global.server_whitelist = file2list(CONFIG_SERVER_WHITELIST_FILE) || list() + + // Clear blank rows. + while(null in global.server_whitelist) + global.server_whitelist -= null + while("" in global.server_whitelist) + global.server_whitelist -= "" + + // Remove old list rather than append. + if(fexists(CONFIG_SERVER_WHITELIST_FILE)) + fdel(CONFIG_SERVER_WHITELIST_FILE) + // Write our list out. + var/write_file = file(CONFIG_SERVER_WHITELIST_FILE) + to_file(write_file, jointext(global.server_whitelist, "\n")) var/global/list/alien_whitelist = list() - /hook/startup/proc/loadAlienWhitelist() if(get_config_value(/decl/config/toggle/use_alien_whitelist)) if(get_config_value(/decl/config/toggle/use_alien_whitelist_sql)) @@ -27,35 +39,34 @@ var/global/list/alien_whitelist = list() else load_alienwhitelist() return 1 + /proc/load_alienwhitelist() var/text = safe_file2text("config/alienwhitelist.txt", FALSE) if (!text) log_misc("Failed to load config/alienwhitelist.txt") - return 0 - else - alien_whitelist = splittext(text, "\n") - return 1 + return FALSE + alien_whitelist = splittext(text, "\n") + to_world_log("Loaded [length(alien_whitelist)] whitelist [length(alien_whitelist) == 1 ? "entry" : "entries"] from text file.") + return TRUE /proc/load_alienwhitelistSQL() var/DBQuery/query = dbcon.NewQuery("SELECT * FROM `whitelist`") if(!query.Execute()) to_world_log(dbcon.ErrorMsg()) - return 0 - else - while(query.NextRow()) - var/list/row = query.GetRowData() - if(alien_whitelist[row["ckey"]]) - var/list/A = alien_whitelist[row["ckey"]] - A.Add(row["race"]) - else - alien_whitelist[row["ckey"]] = list(row["race"]) - return 1 + return FALSE + while(query.NextRow()) + var/list/row = query.GetRowData() + if(alien_whitelist[row["ckey"]]) + var/list/A = alien_whitelist[row["ckey"]] + A.Add(row["race"]) + else + alien_whitelist[row["ckey"]] = list(row["race"]) + return TRUE /proc/is_species_whitelisted(mob/M, var/species_name) var/decl/species/S = get_species_by_key(species_name) return is_alien_whitelisted(M, S) -//todo: admin aliens /proc/is_alien_whitelisted(mob/M, var/species) if(!M || !species) @@ -86,7 +97,8 @@ var/global/list/alien_whitelist = list() return TRUE return whitelist_lookup(S.get_root_species_name(M), M.ckey) - return FALSE + // Check for arbitrary text whitelisting. + return istext(species) ? whitelist_lookup(species, M.ckey) : FALSE /proc/whitelist_lookup(var/item, var/ckey) if(!alien_whitelist) @@ -107,5 +119,3 @@ var/global/list/alien_whitelist = list() if(findtext(s,"[ckey] - All")) return TRUE return FALSE - -#undef WHITELISTFILE diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm index 0501f7f3a67..16353f9bf47 100644 --- a/code/game/machinery/CableLayer.dm +++ b/code/game/machinery/CableLayer.dm @@ -34,7 +34,7 @@ to_chat(user, "\The [src]'s cable reel is full.") else to_chat(user, "You load [result] lengths of cable into [src].") - return + return TRUE if(IS_WIRECUTTER(O)) if(cable && cable.amount) @@ -48,6 +48,8 @@ CC.amount = m else to_chat(usr, "There's no more cable on the reel.") + return TRUE + return ..() /obj/machinery/cablelayer/examine(mob/user) . = ..() @@ -85,7 +87,7 @@ if(istype(new_turf, /turf/floor)) var/turf/floor/T = new_turf if(!T.is_plating()) - T.make_plating(!T.is_floor_damaged()) + T.set_flooring(null, place_product = !T.is_floor_damaged()) return new_turf.is_plating() /obj/machinery/cablelayer/proc/layCable(var/turf/new_turf,var/M_Dir) diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 2388d201d9d..0c9c319aacf 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -28,13 +28,11 @@ . = ..() to_chat(user, SPAN_NOTICE("The neural suppressors are switched [suppressing ? "on" : "off"].")) -/obj/machinery/optable/attackby(var/obj/item/O, var/mob/user) - if (istype(O, /obj/item/grab)) - var/obj/item/grab/G = O - if(isliving(G.affecting) && check_table(G.affecting)) - take_victim(G.affecting,usr) - qdel(O) - return +/obj/machinery/optable/grab_attack(obj/item/grab/grab, mob/user) + if(isliving(grab.affecting) && check_table(grab.affecting)) + take_victim(grab.affecting, user) + qdel(grab) + return TRUE return ..() /obj/machinery/optable/state_transition(var/decl/machine_construction/default/new_state) @@ -50,7 +48,7 @@ if(stat & (NOPOWER|BROKEN)) to_chat(user, "You try to switch on the suppressor, yet nothing happens.") - return + return TRUE if(user != victim && !suppressing) // Skip checks if you're doing it to yourself or turning it off, this is an anti-griefing mechanic more than anything. user.visible_message("\The [user] begins switching on \the [src]'s neural suppressor.") @@ -107,8 +105,8 @@ SPAN_NOTICE("You climb on \the [src].")) else visible_message(SPAN_NOTICE("\The [target] has been laid on \the [src] by \the [user].")) - target.set_posture(/decl/posture/lying/deliberate) target.dropInto(loc) + target.set_posture(/decl/posture/lying/deliberate) add_fingerprint(user) update_icon() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index ff647bdbbf5..dd697812746 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -308,7 +308,7 @@ add_fingerprint(user) if(!beaker) if(!user.try_unequip(I, src)) - return + return TRUE beaker = I user.visible_message(SPAN_NOTICE("\The [user] adds \a [I] to \the [src]."), SPAN_NOTICE("You add \a [I] to \the [src].")) else diff --git a/code/game/machinery/_machines_base/machine_construction/_construction.dm b/code/game/machinery/_machines_base/machine_construction/_construction.dm index 26a364d9d8f..fa819a58c1a 100644 --- a/code/game/machinery/_machines_base/machine_construction/_construction.dm +++ b/code/game/machinery/_machines_base/machine_construction/_construction.dm @@ -88,8 +88,8 @@ /decl/machine_construction/proc/attackby(obj/item/I, mob/user, obj/machinery/machine) if(!validate_state(machine)) PRINT_STACK_TRACE("Machine [log_info_line(machine)] violated the state assumptions of the construction state [type]!") - machine.attackby(I, user) - return TRUE + return machine.attackby(I, user) + return FALSE /decl/machine_construction/proc/mechanics_info() diff --git a/code/game/machinery/_machines_base/machine_construction/airlock.dm b/code/game/machinery/_machines_base/machine_construction/airlock.dm index 52c25f29a99..835402c5484 100644 --- a/code/game/machinery/_machines_base/machine_construction/airlock.dm +++ b/code/game/machinery/_machines_base/machine_construction/airlock.dm @@ -9,7 +9,7 @@ playsound(get_turf(machine), 'sound/items/Screwdriver.ogg', 50, 1) to_chat(user, SPAN_NOTICE("You release some of the logic wiring on \the [machine]. The cover panel remains closed.")) machine.update_icon() - return + return TRUE if(IS_WRENCH(I)) TRANSFER_STATE(down_state) playsound(get_turf(machine), 'sound/items/Crowbar.ogg', 50, 1) @@ -23,6 +23,7 @@ machine.part_replacement(user, replacer) machine.display_parts(user) return TRUE + return FALSE /decl/machine_construction/default/panel_closed/door/mechanics_info() . = list() @@ -40,6 +41,7 @@ to_chat(user, SPAN_NOTICE("You tuck the exposed wiring back into \the [machine] and screw the hatch back into place.")) machine.queue_icon_update() return TRUE + return FALSE /decl/machine_construction/default/panel_closed/door/hacking/mechanics_info() . = list() diff --git a/code/game/machinery/_machines_base/machine_construction/default.dm b/code/game/machinery/_machines_base/machine_construction/default.dm index b2cd2e49d11..c178a598eb4 100644 --- a/code/game/machinery/_machines_base/machine_construction/default.dm +++ b/code/game/machinery/_machines_base/machine_construction/default.dm @@ -30,13 +30,14 @@ machine.panel_open = TRUE to_chat(user, SPAN_NOTICE("You open the maintenance hatch of \the [machine].")) machine.update_icon() - return + return TRUE if(istype(I, /obj/item/part_replacer)) var/obj/item/part_replacer/replacer = I if(replacer.remote_interaction) machine.part_replacement(user, replacer) machine.display_parts(user) return TRUE + return FALSE /decl/machine_construction/default/panel_closed/post_construct(obj/machinery/machine) try_change_state(machine, down_state) @@ -75,16 +76,14 @@ machine.panel_open = FALSE to_chat(user, SPAN_NOTICE("You close the maintenance hatch of \the [machine].")) machine.update_icon() - return - + return TRUE if(istype(I, /obj/item/part_replacer)) return machine.part_replacement(user, I) - if(IS_WRENCH(I)) return machine.part_removal(user) - if(istype(I)) return machine.part_insertion(user, I) + return FALSE /decl/machine_construction/default/panel_open/mechanics_info() . = list() diff --git a/code/game/machinery/_machines_base/machine_construction/frame.dm b/code/game/machinery/_machines_base/machine_construction/frame.dm index 459f90a8fb2..1913c959fbe 100644 --- a/code/game/machinery/_machines_base/machine_construction/frame.dm +++ b/code/game/machinery/_machines_base/machine_construction/frame.dm @@ -30,7 +30,7 @@ TRANSFER_STATE(/decl/machine_construction/default/deconstructed) to_chat(user, "You deconstruct \the [machine].") machine.dismantle() - + return FALSE /decl/machine_construction/frame/unwrenched/mechanics_info() . = list() @@ -49,15 +49,13 @@ try_change_state(machine, /decl/machine_construction/frame/unwrenched) /decl/machine_construction/frame/wrenched/attackby(obj/item/I, mob/user, obj/machinery/machine) - if(IS_WRENCH(I)) playsound(machine.loc, 'sound/items/Ratchet.ogg', 50, 1) if(do_after(user, 20, machine)) TRANSFER_STATE(/decl/machine_construction/frame/unwrenched) to_chat(user, "You unfasten \the [machine].") machine.anchored = FALSE - return - + return TRUE if(IS_COIL(I)) var/obj/item/stack/cable_coil/C = I if(C.get_amount() < 5) @@ -69,7 +67,7 @@ TRANSFER_STATE(/decl/machine_construction/frame/awaiting_circuit) to_chat(user, "You add cables to the frame.") return TRUE - + return FALSE /decl/machine_construction/frame/wrenched/mechanics_info() . = list() @@ -91,22 +89,22 @@ if(istype(I, /obj/item/stock_parts/circuitboard)) var/obj/item/stock_parts/circuitboard/circuit = I if(circuit.board_type == machine.expected_machine_type) - if(!user.canUnEquip(I)) - return FALSE - TRANSFER_STATE(/decl/machine_construction/frame/awaiting_parts) - user.try_unequip(I, machine) - playsound(machine.loc, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You add the circuit board to \the [machine].") - machine.circuit = I - return + if(user.canUnEquip(I)) + TRANSFER_STATE(/decl/machine_construction/frame/awaiting_parts) + user.try_unequip(I, machine) + playsound(machine.loc, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You add the circuit board to \the [machine].") + machine.circuit = I else to_chat(user, "This frame does not accept circuit boards of this type!") - return TRUE + return TRUE if(IS_WIRECUTTER(I)) TRANSFER_STATE(/decl/machine_construction/frame/wrenched) playsound(machine.loc, 'sound/items/Wirecutter.ogg', 50, 1) to_chat(user, "You remove the cables.") new /obj/item/stack/cable_coil(machine.loc, 5) + return TRUE + return FALSE /decl/machine_construction/frame/awaiting_circuit/mechanics_info() . = list() @@ -131,7 +129,7 @@ machine.circuit.dropInto(machine.loc) machine.circuit = null to_chat(user, "You remove the circuit board.") - return + return TRUE if(IS_SCREWDRIVER(I)) playsound(machine.loc, 'sound/items/Screwdriver.ogg', 50, 1) var/obj/machinery/new_machine = new machine.circuit.build_path(machine.loc, machine.dir, FALSE) @@ -145,6 +143,7 @@ PRINT_STACK_TRACE("Machine of type [new_machine.type] was built from a circuit and frame, but had no construct state set.") qdel(machine) return TRUE + return FALSE /decl/machine_construction/frame/awaiting_parts/mechanics_info() . = list() diff --git a/code/game/machinery/_machines_base/machine_construction/item_chassis.dm b/code/game/machinery/_machines_base/machine_construction/item_chassis.dm index f395d35b1b9..234319a1bca 100644 --- a/code/game/machinery/_machines_base/machine_construction/item_chassis.dm +++ b/code/game/machinery/_machines_base/machine_construction/item_chassis.dm @@ -11,7 +11,7 @@ playsound(get_turf(machine), 'sound/items/Ratchet.ogg', 50, 1) machine.visible_message(SPAN_NOTICE("\The [user] deconstructs \the [machine].")) machine.dismantle() - return + return TRUE return ..() /decl/machine_construction/default/panel_closed/item_chassis/mechanics_info() diff --git a/code/game/machinery/_machines_base/machine_construction/wall_frame.dm b/code/game/machinery/_machines_base/machine_construction/wall_frame.dm index 023bbbc699f..e050c23ff8f 100644 --- a/code/game/machinery/_machines_base/machine_construction/wall_frame.dm +++ b/code/game/machinery/_machines_base/machine_construction/wall_frame.dm @@ -78,7 +78,7 @@ new /obj/item/stack/cable_coil(get_turf(machine), 5) machine.set_broken(TRUE, MACHINE_BROKEN_CONSTRUCT) machine.queue_icon_update() - return + return TRUE if((. = up_interaction(I, user, machine))) return @@ -135,10 +135,9 @@ to_chat(user, SPAN_NOTICE("You wire the [machine].")) machine.set_broken(FALSE, MACHINE_BROKEN_CONSTRUCT) machine.queue_icon_update() - return else to_chat(user, SPAN_WARNING("You need five pieces of cable to wire \the [machine].")) - return TRUE + return TRUE if((. = down_interaction(I, user, machine))) return @@ -200,14 +199,16 @@ machine.install_component(board) user.visible_message(SPAN_NOTICE("\The [user] inserts \the [board] into \the [machine]!"), SPAN_NOTICE("You insert \the [board] into \the [machine]!")) machine.queue_icon_update() - return + return TRUE if(IS_WRENCH(I)) TRANSFER_STATE(/decl/machine_construction/default/deconstructed) playsound(get_turf(machine), 'sound/items/Ratchet.ogg', 50, 1) machine.visible_message(SPAN_NOTICE("\The [user] deconstructs \the [machine].")) machine.dismantle() - return + return TRUE + + return FALSE /decl/machine_construction/wall_frame/no_circuit/mechanics_info() . = list() diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index 135c4a4f381..290540f78f5 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -85,6 +85,7 @@ Class Procs: matter = list( /decl/material/solid/metal/steel = MATTER_AMOUNT_PRIMARY ) + temperature_sensitive = TRUE abstract_type = /obj/machinery var/stat = 0 diff --git a/code/game/machinery/_machines_base/machinery_components.dm b/code/game/machinery/_machines_base/machinery_components.dm index 8becd763ea1..cdc1dc9e71d 100644 --- a/code/game/machinery/_machines_base/machinery_components.dm +++ b/code/game/machinery/_machines_base/machinery_components.dm @@ -43,6 +43,10 @@ var/global/list/machine_path_to_circuit_type for(var/component_path in uncreated_component_parts) var/number = uncreated_component_parts[component_path] || 1 LAZYREMOVE(uncreated_component_parts, component_path) + // Stacks are created differently to avoid qdel churn. + if(ispath(component_path, /obj/item/stack)) + install_component(new component_path(src, number), refresh_parts = FALSE) + continue for(var/i in 1 to number) install_component(component_path, refresh_parts = FALSE) @@ -251,8 +255,8 @@ var/global/list/machine_path_to_circuit_type /obj/machinery/proc/component_stat_change(var/obj/item/stock_parts/part, old_stat, flag) /obj/machinery/attackby(obj/item/I, mob/user) - if(component_attackby(I, user)) - return TRUE + if((. = component_attackby(I, user))) + return return ..() /obj/machinery/proc/component_attackby(obj/item/I, mob/user) @@ -261,7 +265,7 @@ var/global/list/machine_path_to_circuit_type continue if((. = part.attackby(I, user))) return - return construct_state && construct_state.attackby(I, user, src) + return construct_state?.attackby(I, user, src) /obj/machinery/proc/component_attack_hand(mob/user) for(var/obj/item/stock_parts/part in component_parts) diff --git a/code/game/machinery/_machines_base/machinery_damage.dm b/code/game/machinery/_machines_base/machinery_damage.dm index 2ecf6cd43e8..a434776abd8 100644 --- a/code/game/machinery/_machines_base/machinery_damage.dm +++ b/code/game/machinery/_machines_base/machinery_damage.dm @@ -83,9 +83,12 @@ take_damage(P.damage, P.atom_damage_type) /obj/machinery/bash(obj/item/W, mob/user) - if(!istype(W) || W.force <= 5) + if(!istype(W)) + return FALSE + var/force = W.get_attack_force(user) + if(force <= 5) return FALSE . = ..() if(.) user.setClickCooldown(W.attack_cooldown + W.w_class) - take_damage(W.force, W.atom_damage_type) \ No newline at end of file + take_damage(force, W.atom_damage_type) \ No newline at end of file diff --git a/code/game/machinery/_machines_base/machinery_public_vars_common.dm b/code/game/machinery/_machines_base/machinery_public_vars_common.dm index 63e856c7588..30de5ce9da6 100644 --- a/code/game/machinery/_machines_base/machinery_public_vars_common.dm +++ b/code/game/machinery/_machines_base/machinery_public_vars_common.dm @@ -24,7 +24,7 @@ Public vars at /obj/machinery level. Just because they are here does not mean th /decl/public_access/public_method/toggle_input_toggle name = "toggle input" desc = "Toggles the input toggle variable." - call_proc = /obj/machinery/proc/toggle_input_toggle + call_proc = TYPE_PROC_REF(/obj/machinery, toggle_input_toggle) /obj/machinery/proc/toggle_input_toggle() var/decl/public_access/public_variable/variable = GET_DECL(/decl/public_access/public_variable/input_toggle) @@ -141,7 +141,7 @@ Public vars at /obj/machinery level. Just because they are here does not mean th /decl/public_access/public_method/toggle_power name = "toggle power" desc = "Turns the machine on or off." - call_proc = /obj/machinery/proc/toggle_power + call_proc = TYPE_PROC_REF(/obj/machinery, toggle_power) /obj/machinery/proc/toggle_power() update_use_power(!use_power) @@ -149,7 +149,7 @@ Public vars at /obj/machinery level. Just because they are here does not mean th /decl/public_access/public_method/refresh name = "refresh machine" desc = "Attempts to refresh the machine's status. Implementation may vary." - call_proc = /obj/machinery/proc/refresh + call_proc = TYPE_PROC_REF(/obj/machinery, refresh) /obj/machinery/proc/refresh() queue_icon_update() \ No newline at end of file diff --git a/code/game/machinery/_machines_base/stock_parts/access_lock.dm b/code/game/machinery/_machines_base/stock_parts/access_lock.dm index c69c206ed6e..f5ae7df7558 100644 --- a/code/game/machinery/_machines_base/stock_parts/access_lock.dm +++ b/code/game/machinery/_machines_base/stock_parts/access_lock.dm @@ -62,7 +62,6 @@ locked = !locked visible_message(SPAN_NOTICE("\The [src] beeps and flashes green twice: it is now [locked ? "" : "un"]locked.")) return TRUE - return return ..() /obj/item/stock_parts/access_lock/attack_self(mob/user) 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 fb8b1fe8d34..f78430de17d 100644 --- a/code/game/machinery/_machines_base/stock_parts/card_reader.dm +++ b/code/game/machinery/_machines_base/stock_parts/card_reader.dm @@ -48,7 +48,7 @@ /obj/item/stock_parts/item_holder/card_reader/attackby(obj/item/W, mob/user) if(IS_SCREWDRIVER(W) && !istype(loc, /obj/machinery)) //Only if not in the machine, to prevent hijacking tool interactions with the machine should_swipe = !should_swipe - to_chat(user, SPAN_NOTICE("You toggle \the [src] into [should_swipe? "swipe" : "insert"] card mode")) + to_chat(user, SPAN_NOTICE("You toggle \the [src] into [should_swipe? "swipe" : "insert"] card mode.")) return TRUE . = ..() diff --git a/code/game/machinery/_machines_base/stock_parts/legacy_parts.dm b/code/game/machinery/_machines_base/stock_parts/legacy_parts.dm index 7eb99203b77..15cc987b406 100644 --- a/code/game/machinery/_machines_base/stock_parts/legacy_parts.dm +++ b/code/game/machinery/_machines_base/stock_parts/legacy_parts.dm @@ -8,7 +8,6 @@ material = /decl/material/solid/metal/steel matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) base_type = /obj/item/stock_parts/scanning_module - w_class = ITEM_SIZE_TINY /obj/item/stock_parts/manipulator name = "micro-manipulator" @@ -17,7 +16,6 @@ origin_tech = @'{"materials":1,"programming":1}' material = /decl/material/solid/metal/steel base_type = /obj/item/stock_parts/manipulator - w_class = ITEM_SIZE_TINY /obj/item/stock_parts/micro_laser name = "micro-laser" @@ -27,7 +25,6 @@ material = /decl/material/solid/metal/steel matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) base_type = /obj/item/stock_parts/micro_laser - w_class = ITEM_SIZE_TINY /obj/item/stock_parts/matter_bin name = "matter bin" @@ -44,10 +41,9 @@ origin_tech = @'{"powerstorage":1}' material = /decl/material/solid/metal/steel matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) + base_type = /obj/item/stock_parts/capacitor var/charge = 0 var/max_charge = 1000 - base_type = /obj/item/stock_parts/capacitor - w_class = ITEM_SIZE_TINY /obj/item/stock_parts/capacitor/Initialize() . = ..() diff --git a/code/game/machinery/_machines_base/stock_parts/power/battery.dm b/code/game/machinery/_machines_base/stock_parts/power/battery.dm index f883e307f14..c78b358aea6 100644 --- a/code/game/machinery/_machines_base/stock_parts/power/battery.dm +++ b/code/game/machinery/_machines_base/stock_parts/power/battery.dm @@ -163,7 +163,7 @@ return TRUE if(!user.try_unequip(I, src)) - return + return TRUE add_cell(machine, I) user.visible_message(\ SPAN_WARNING("\The [user] has inserted the power cell to \the [src]!"),\ @@ -173,6 +173,7 @@ // Interactions without machine if(!istype(machine)) return ..() + return FALSE /obj/item/stock_parts/power/battery/attack_self(mob/user) if(cell) 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 d267e96446e..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,10 +147,11 @@ 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 /obj/item/stock_parts/power/terminal/get_source_info() . = "The machine can receive power by direct connection to the powernet. " diff --git a/code/game/machinery/_machines_base/stock_parts/radio/transmitter.dm b/code/game/machinery/_machines_base/stock_parts/radio/transmitter.dm index db0dba24ece..e4e547aa54a 100644 --- a/code/game/machinery/_machines_base/stock_parts/radio/transmitter.dm +++ b/code/game/machinery/_machines_base/stock_parts/radio/transmitter.dm @@ -14,9 +14,9 @@ return if(!buffer) buffer = data - addtimer(CALLBACK(src, PROC_REF(transmit)), latency) + addtimer(CALLBACK(src, PROC_REF(transmit)), latency, TIMER_UNIQUE) else - buffer |= data + buffer = (data |= buffer) // this avoids list copies. basically, give entries already in data priority and add buffered data after /obj/item/stock_parts/radio/transmitter/proc/transmit() if(!LAZYLEN(buffer)) diff --git a/code/game/machinery/_machines_base/stock_parts/stock_parts_interface.dm b/code/game/machinery/_machines_base/stock_parts/stock_parts_interface.dm index b1aa3c32f90..e56703abc15 100644 --- a/code/game/machinery/_machines_base/stock_parts/stock_parts_interface.dm +++ b/code/game/machinery/_machines_base/stock_parts/stock_parts_interface.dm @@ -26,7 +26,7 @@ material = /decl/material/solid/organic/plastic base_type = /obj/item/stock_parts/keyboard part_flags = PART_FLAG_HAND_REMOVE - w_class = ITEM_SIZE_TINY + w_class = ITEM_SIZE_SMALL ignore_damage_types = list(ELECTROCUTE) // emp damage is annoying enough without destroying purely physical or mechanical components /obj/item/stock_parts/keyboard/on_refresh(obj/machinery/machine) diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 78f73e8df9c..dc389cb9dec 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -25,7 +25,7 @@ /obj/machinery/ai_slipper/attackby(obj/item/W, mob/user) if(stat & (NOPOWER|BROKEN)) - return + return FALSE if (issilicon(user)) return attack_ai(user) else // trying to unlock the interface @@ -41,6 +41,7 @@ interact(user) else to_chat(user, "Access denied.") + return TRUE /obj/machinery/ai_slipper/interface_interact(mob/user) interact(user) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 5ec349be144..6a33a9cd274 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -784,7 +784,7 @@ /obj/machinery/alarm/attackby(obj/item/used_item, mob/user) if(!(stat & (BROKEN|NOPOWER))) if (istype(used_item, /obj/item/card/id) || istype(used_item, /obj/item/modular_computer))// trying to unlock the interface with an ID card - if(!used_item.user_can_wield(user)) + if(!used_item.user_can_attack_with(user)) return TRUE if(allowed(user) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) locked = !locked diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 983410a5a70..948ddf5babc 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -87,35 +87,21 @@ /obj/machinery/portable_atmospherics/canister/empty start_pressure = 0 can_label = TRUE - var/obj/machinery/portable_atmospherics/canister/canister_type = /obj/machinery/portable_atmospherics/canister -/obj/machinery/portable_atmospherics/canister/empty/Initialize() - . = ..() - name = initial(canister_type.name) - icon_state = initial(canister_type.icon_state) - canister_color = initial(canister_type.canister_color) - -/obj/machinery/portable_atmospherics/canister/empty/air - icon_state = "grey" - canister_type = /obj/machinery/portable_atmospherics/canister/air -/obj/machinery/portable_atmospherics/canister/empty/oxygen - icon_state = "blue" - canister_type = /obj/machinery/portable_atmospherics/canister/oxygen -/obj/machinery/portable_atmospherics/canister/empty/phoron - icon_state = "orange" - canister_type = /obj/machinery/portable_atmospherics/canister/phoron -/obj/machinery/portable_atmospherics/canister/empty/nitrogen - icon_state = "red" - canister_type = /obj/machinery/portable_atmospherics/canister/nitrogen -/obj/machinery/portable_atmospherics/canister/empty/carbon_dioxide - icon_state = "black" - canister_type = /obj/machinery/portable_atmospherics/canister/carbon_dioxide -/obj/machinery/portable_atmospherics/canister/empty/sleeping_agent - icon_state = "redws" - canister_type = /obj/machinery/portable_atmospherics/canister/sleeping_agent -/obj/machinery/portable_atmospherics/canister/empty/hydrogen - icon_state = "purple" - canister_type = /obj/machinery/portable_atmospherics/canister/hydrogen +#define EMPTY_CANISTER(TYPE, CTYPE) \ +/obj/machinery/portable_atmospherics/canister/empty/##TYPE{\ + name = CTYPE::name; \ + icon_state = CTYPE::icon_state; \ + canister_color = CTYPE::canister_color; \ +} + +EMPTY_CANISTER(air, /obj/machinery/portable_atmospherics/canister/air) +EMPTY_CANISTER(oxygen, /obj/machinery/portable_atmospherics/canister/oxygen) +EMPTY_CANISTER(phoron, /obj/machinery/portable_atmospherics/canister/phoron) +EMPTY_CANISTER(nitrogen, /obj/machinery/portable_atmospherics/canister/nitrogen) +EMPTY_CANISTER(carbon_dioxide, /obj/machinery/portable_atmospherics/canister/carbon_dioxide) +EMPTY_CANISTER(sleeping_agent, /obj/machinery/portable_atmospherics/canister/sleeping_agent) +EMPTY_CANISTER(hydrogen, /obj/machinery/portable_atmospherics/canister/hydrogen) /obj/machinery/portable_atmospherics/canister/on_update_icon() @@ -221,7 +207,7 @@ /obj/machinery/portable_atmospherics/canister/bash(var/obj/item/W, var/mob/user) . = ..() if(.) - current_health -= W.force + current_health -= W.get_attack_force(user) healthcheck() /obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/W, var/mob/user) diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 025e9281756..b31aee68944 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -186,16 +186,16 @@ if(IS_WRENCH(I)) if(use_power == POWER_USE_ACTIVE) to_chat(user, "Turn \the [src] off first!") - return + return TRUE anchored = !anchored playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - return + return TRUE //doesn't hold tanks if(istype(I, /obj/item/tank)) - return + return FALSE return ..() @@ -207,6 +207,6 @@ /obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/attackby(var/obj/item/I, var/mob/user) if(IS_WRENCH(I)) to_chat(user, "The bolts are too tight for you to unscrew!") - return + return TRUE return ..() \ No newline at end of file diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index b827de44877..710ca1c1e53 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -28,7 +28,7 @@ "Food" = list( /obj/item/chems/drinks/milk/smallcarton = 30, /obj/item/chems/drinks/milk = 50, - /obj/item/chems/food/butchery/meat/syntiflesh = 50, + /obj/item/food/butchery/meat/syntiflesh = 50, /obj/item/box/fancy/egg_box = 300), "Nutrients" = list( /obj/item/chems/glass/bottle/eznutrient = 60, @@ -79,45 +79,53 @@ return SPAN_NOTICE("You must turn \the [src] off first.") return ..() -/obj/machinery/biogenerator/attackby(var/obj/item/O, var/mob/user) - if((. = component_attackby(O, user))) - return +/obj/machinery/biogenerator/attackby(var/obj/item/used_item, var/mob/user) + if(processing) - to_chat(user, "\The [src] is currently processing.") - if(istype(O, /obj/item/chems/glass)) + if((. = component_attackby(used_item, user))) + return + to_chat(user, SPAN_WARNING("\The [src] is currently processing.")) + return TRUE + + if(istype(used_item, /obj/item/chems/glass)) if(beaker) - to_chat(user, "]The [src] is already loaded.") - return TRUE - else if(user.try_unequip(O, src)) - beaker = O + to_chat(user, SPAN_NOTICE("\The [src] is already loaded.")) + else if(user.try_unequip(used_item, src)) + beaker = used_item state = BG_READY updateUsrDialog() - return TRUE + return TRUE if(ingredients >= capacity) - to_chat(user, "\The [src] is already full! Activate it.") - else if(isobj(O)) - if(O.storage) - var/hadPlants = 0 - for(var/obj/item/chems/food/grown/G in O.storage.get_contents()) - hadPlants = 1 - O.storage.remove_from_storage(user, G, src, TRUE) - ingredients++ - if(ingredients >= capacity) - to_chat(user, "You fill \the [src] to its capacity.") - break - O.storage.finish_bulk_removal() //Now do the UI stuff once. - if(!hadPlants) - to_chat(user, "\The [O] has no produce inside.") - else if(ingredients < capacity) - to_chat(user, "You empty \the [O] into \the [src].") - - else if(!istype(O, /obj/item/chems/food/grown)) - to_chat(user, "You cannot put this in \the [src].") - else if(user.try_unequip(O, src)) + to_chat(user, SPAN_NOTICE("\The [src] is already full! Activate it.")) + return TRUE + + if(used_item.storage) + var/added_plants = FALSE + for(var/obj/item/food/grown/G in used_item.storage.get_contents()) + added_plants = TRUE + used_item.storage.remove_from_storage(user, G, src, TRUE) + ingredients++ + if(ingredients >= capacity) + to_chat(user, SPAN_NOTICE("You fill \the [src] to its capacity.")) + break + used_item.storage.finish_bulk_removal() //Now do the UI stuff once. + if(!added_plants) + to_chat(user, SPAN_WARNING("\The [used_item] has no produce inside.")) + else if(ingredients < capacity) + to_chat(user, SPAN_NOTICE("You empty \the [used_item] into \the [src].")) + return TRUE + + if(!istype(used_item, /obj/item/food/grown)) + to_chat(user, SPAN_WARNING("You cannot put this in \the [src].")) + return TRUE + + if(user.try_unequip(used_item, src)) ingredients++ - to_chat(user, "You put \the [O] in \the [src]") - update_icon() + to_chat(user, SPAN_NOTICE("You put \the [used_item] in \the [src]")) + return TRUE + + return ..() /** * Display the NanoUI window for the vending machine. @@ -193,7 +201,7 @@ return var/S = 0 - for(var/obj/item/chems/food/grown/I in contents) + for(var/obj/item/food/grown/I in contents) S += 5 ingredients-- var/amt = REAGENT_VOLUME(I.reagents, /decl/material/liquid/nutriment) diff --git a/code/game/machinery/bodyscanner.dm b/code/game/machinery/bodyscanner.dm index 067ca08ef6d..5658432aa1f 100644 --- a/code/game/machinery/bodyscanner.dm +++ b/code/game/machinery/bodyscanner.dm @@ -68,12 +68,10 @@ if(istype(new_state)) updateUsrDialog() -/obj/machinery/bodyscanner/attackby(obj/item/grab/G, user) - if(istype(G)) - var/mob/M = G.get_affecting_mob() - if(!M || !user_can_move_target_inside(M, user)) - return - qdel(G) +/obj/machinery/bodyscanner/grab_attack(obj/item/grab/grab, mob/user) + var/mob/living/victim = grab.get_affecting_mob() + if(istype(victim) && user_can_move_target_inside(victim, user)) + qdel(grab) return TRUE return ..() diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 2565c5f3e16..ee9a28eee31 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -177,15 +177,16 @@ /obj/machinery/camera/hitby(var/atom/movable/AM) . = ..() - if(. && istype(AM, /obj)) - var/obj/O = AM - if (O.throwforce >= src.toughness) - visible_message(SPAN_WARNING("[src] was hit by [O]!")) - take_damage(O.throwforce, O.atom_damage_type) + if(. && isobj(AM)) + var/thrown_force = AM.get_thrown_attack_force() + if (thrown_force >= toughness) + visible_message(SPAN_DANGER("\The [src] was hit by \the [AM]!")) + var/obj/O = AM + take_damage(thrown_force, O.atom_damage_type) /obj/machinery/camera/physical_attack_hand(mob/living/human/user) if(!istype(user)) - return + return TRUE if(user.species.can_shred(user)) user.do_attack_animation(src) visible_message(SPAN_WARNING("\The [user] slashes at [src]!")) @@ -193,6 +194,7 @@ add_hiddenprint(user) take_damage(25) return TRUE + return FALSE /obj/machinery/camera/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/paper)) @@ -340,7 +342,7 @@ /decl/public_access/public_method/toggle_camera name = "toggle camera" desc = "Toggles camera on or off." - call_proc = /obj/machinery/camera/proc/toggle_status + call_proc = TYPE_PROC_REF(/obj/machinery/camera, toggle_status) /decl/public_access/public_variable/camera_state expected_type = /obj/machinery/camera diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 91976e992ed..0042bac3abb 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -43,20 +43,20 @@ camera_loc = sanitize(camera_loc) if(!camera_loc) - to_chat(src, "Must supply a location name") + to_chat(src, SPAN_WARNING("Must supply a location name.")) return if(stored_locations.len >= max_locations) - to_chat(src, "Cannot store additional locations. Remove one first") + to_chat(src, SPAN_WARNING("Cannot store additional locations, remove one first.")) return if(camera_loc in stored_locations) - to_chat(src, "There is already a stored location by this name") + to_chat(src, SPAN_WARNING("There is already a stored location by that name.")) return var/L = src.eyeobj.getLoc() if (InvalidPlayerTurf(get_turf(L))) - to_chat(src, "Unable to store this location") + to_chat(src, SPAN_WARNING("Unable to store this location.")) return stored_locations[camera_loc] = L @@ -71,7 +71,7 @@ set desc = "Returns to the selected camera location" if (!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") + to_chat(src, SPAN_WARNING("Location [loc] not found.")) return var/L = stored_locations[loc] @@ -83,7 +83,7 @@ set desc = "Deletes the selected camera location" if (!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") + to_chat(src, SPAN_WARNING("Location [loc] not found.")) return stored_locations.Remove(loc) @@ -240,14 +240,14 @@ /mob/living/silicon/robot/tracking_initiated() tracking_entities++ if(tracking_entities == 1 && has_zeroth_law()) - to_chat(src, "Internal camera is currently being accessed.") + to_chat(src, SPAN_WARNING("Internal camera is currently being accessed.")) /mob/living/proc/tracking_cancelled() /mob/living/silicon/robot/tracking_cancelled() tracking_entities-- if(!tracking_entities && has_zeroth_law()) - to_chat(src, "Internal camera is no longer being accessed.") + to_chat(src, SPAN_NOTICE("Internal camera is no longer being accessed.")) #undef TRACKING_POSSIBLE diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index ed265488c05..dac2d458472 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -60,7 +60,7 @@ return anchored = !anchored - to_chat(user, "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground") + to_chat(user, "You [anchored ? "attach" : "detach"] \the [src] [anchored ? "to" : "from"] the ground.") playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) return diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index efc4136a173..c76905972bc 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -225,9 +225,7 @@ var/global/list/deactivated_ai_cores = list() qdel(src) /obj/structure/aicore/deactivated/attackby(var/obj/item/W, var/mob/user) - if(IS_WRENCH(W) || IS_WELDER(W)) - . = ..() - else if(istype(W, /obj/item/aicard)) + if(istype(W, /obj/item/aicard)) var/obj/item/aicard/card = W var/mob/living/silicon/ai/transfer = locate() in card if(transfer) @@ -235,6 +233,7 @@ var/global/list/deactivated_ai_cores = list() else to_chat(user, SPAN_DANGER("ERROR: Unable to locate artificial intelligence.")) return TRUE + return ..() /client/proc/empty_ai_core_toggle_latejoin() set name = "Toggle AI Core Latejoin" diff --git a/code/game/machinery/computer/guestpass.dm b/code/game/machinery/computer/guestpass.dm index 089a92f8471..af53c701163 100644 --- a/code/game/machinery/computer/guestpass.dm +++ b/code/game/machinery/computer/guestpass.dm @@ -34,7 +34,7 @@ to_chat(usr, SPAN_NOTICE("Issuing reason: [reason].")) /obj/item/card/id/guest/proc/expire() - color = COLOR_BLACK + set_color(COLOR_BLACK) detail_color = COLOR_BLACK update_icon() @@ -74,8 +74,8 @@ updateUsrDialog() else if(giver) to_chat(user, SPAN_WARNING("There is already ID card inside.")) - return - ..() + return TRUE + return ..() /obj/machinery/computer/guestpass/interface_interact(var/mob/user) ui_interact(user) diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index a186ccd2320..992d2b211b5 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -8,8 +8,9 @@ if(istype(O, /obj/item/aiModule)) var/obj/item/aiModule/M = O M.install(src, user) + return TRUE else - ..() + return ..() /obj/machinery/computer/upload/ai name = "\improper AI upload console" diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index e81ec84863a..16503b09fe9 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -40,16 +40,14 @@ /obj/machinery/computer/message_monitor/attackby(obj/item/O, mob/user) if(stat & (NOPOWER|BROKEN)) - ..() - return + return ..() if(!istype(user)) - return + return TRUE if(IS_SCREWDRIVER(O) && emag) //Stops people from just unscrewing the monitor and putting it back to get the console working again. to_chat(user, "It is too hot to mess with!") - return - ..() - return + return TRUE + return ..() /obj/machinery/computer/message_monitor/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm index f43c392a16c..dad8eb391e9 100644 --- a/code/game/machinery/computer/shuttle.dm +++ b/code/game/machinery/computer/shuttle.dm @@ -10,37 +10,28 @@ /obj/machinery/computer/shuttle/attackby(var/obj/item/card as obj, var/mob/user as mob) - if(stat & (BROKEN|NOPOWER)) return + if(stat & (BROKEN|NOPOWER)) return TRUE var/datum/evacuation_controller/shuttle/evac_control = SSevac.evacuation_controller if(!istype(evac_control)) to_chat(user, "This console should not be in use on this map. Please report this to a developer.") - return + return TRUE if(!istype(card, /obj/item/card)) // don't try to get an ID card if we're an emag card = card.GetIdCard() // handles stored IDs in modcomps and similar if ((!istype(card, /obj/item/card) || evac_control.has_evacuated() || !user)) - return + return FALSE if (istype(card, /obj/item/card/id)) var/obj/item/card/id/id_card = card - if (!id_card.access) //no access + if(!LAZYISIN(id_card.access, access_bridge)) //doesn't have this access to_chat(user, "The access level of [id_card.registered_name]\'s card is not high enough.") - return - - var/list/cardaccess = id_card.access - if(!istype(cardaccess, /list) || !cardaccess.len) //no access - to_chat(user, "The access level of [id_card.registered_name]\'s card is not high enough.") - return - - if(!(access_bridge in id_card.access)) //doesn't have this access - to_chat(user, "The access level of [id_card.registered_name]\'s card is not high enough.") - return 0 + return TRUE var/choice = alert(user, "Would you like to (un)authorize a shortened launch time? [auth_need - authorized.len] authorization\s are still needed. Use abort to cancel all authorizations.", "Shuttle Launch", "Authorize", "Repeal", "Abort") if(evac_control.is_prepared() && user.get_active_held_item() != card) - return 0 + return TRUE switch(choice) if("Authorize") src.authorized -= id_card.registered_name @@ -66,16 +57,14 @@ to_world("All authorizations to shortening time for shuttle launch have been revoked!") src.authorized.len = 0 src.authorized = list( ) + return TRUE else if (istype(card, /obj/item/card/emag) && !emagged) var/choice = alert(user, "Would you like to launch the shuttle?","Shuttle control", "Launch", "Cancel") - if(!emagged && !evac_control.is_prepared() && user.get_active_held_item() == card) - switch(choice) - if("Launch") - to_world("Alert: Shuttle launch time shortened to 10 seconds!") - evac_control.set_launch_time(world.time+100) - emagged = 1 - if("Cancel") - return - return + if(!emagged && !evac_control.is_prepared() && user.get_active_held_item() == card && choice == "Launch") + to_world("Alert: Shuttle launch time shortened to 10 seconds!") + evac_control.set_launch_time(world.time+100) + emagged = 1 + return TRUE + return FALSE diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index f76288b60c9..281fd6c42f8 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -1,3 +1,11 @@ +/proc/despawn_character(mob/living/character) + if(character.client) + character.ghostize() + character.prepare_for_despawn() + character.key = null + character.ckey = null + qdel(character) + /* * Cryogenic refrigeration unit. Basically a despawner. * Stealing a lot of concepts/code from sleepers due to massive laziness. @@ -5,7 +13,6 @@ * since time_entered, which is world.time when the occupant moves in. */ - //Main cryopod console. /obj/machinery/computer/cryopod @@ -36,6 +43,7 @@ desc = "An interface between crew and the robotic storage systems." icon = 'icons/obj/robot_storage.dmi' icon_state = "console" + base_type = /obj/machinery/computer/cryopod/robot storage_desc = "cyborgs" storage_name = "Robotic Storage Control" @@ -310,21 +318,6 @@ despawn_occupant() -// This function can not be undone; do not call this unless you are sure -// Also make sure there is a valid control computer -/obj/machinery/cryopod/robot/despawn_occupant() - var/mob/living/silicon/robot/R = occupant - if(istype(R)) - R.clear_brain() - if(R.module) - for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc - for(var/obj/item/O in I.get_contained_external_atoms()) // the things inside the tools, if anything; mainly for janiborg trash bags - O.forceMove(R) - qdel(I) - qdel(R.module) - - . = ..() - // This function can not be undone; do not call this unless you are sure // Also make sure there is a valid control computer /obj/machinery/cryopod/proc/despawn_occupant() @@ -352,38 +345,9 @@ else frozen_item.forceMove(get_turf(src)) - //Update any existing objectives involving this mob. - for(var/datum/objective/objective in global.all_objectives) - // We don't want revs to get objectives that aren't for heads of staff. Letting - // them win or lose based on cryo is silly so we remove the objective. - if(objective.target == occupant.mind) - if(objective.owner?.current) - to_chat(objective.owner.current, SPAN_DANGER("You get the feeling your target, [occupant.real_name], is no longer within your reach...")) - qdel(objective) - - //Handle job slot/tater cleanup. - if(occupant.mind) - if(occupant.mind.assigned_job) - occupant.mind.assigned_job.clear_slot() - - if(occupant.mind.objectives.len) - occupant.mind.objectives = null - occupant.mind.assigned_special_role = null - - // Delete them from datacore. - var/sanitized_name = occupant.real_name - sanitized_name = sanitize(sanitized_name) - var/datum/computer_file/report/crew_record/record = get_crewmember_record(sanitized_name) - if(record) - qdel(record) - icon_state = base_icon_state - //TODO: Check objectives/mode, update new targets if this mob is the target, spawn new antags? - - //Make an announcement and log the person entering storage. - // Titles should really be fetched from data records // and records should not be fetched by name as there is no guarantee names are unique var/role_alt_title = occupant.mind ? occupant.mind.role_alt_title : "Unknown" @@ -394,12 +358,7 @@ log_and_message_admins("[key_name(occupant)] ([role_alt_title]) entered cryostorage.") do_telecomms_announcement(src, "[occupant.real_name], [role_alt_title], [on_store_message]", "[on_store_name]") - - //This should guarantee that ghosts don't spawn. - occupant.ckey = null - - // Delete the mob. - qdel(occupant) + despawn_character(occupant) set_occupant(null) /obj/machinery/cryopod/proc/attempt_enter(var/mob/target, var/mob/user) @@ -434,21 +393,16 @@ attempt_enter(dropping, user) return TRUE -/obj/machinery/cryopod/attackby(var/obj/item/G, var/mob/user) - - if(istype(G, /obj/item/grab)) - var/obj/item/grab/grab = G +/obj/machinery/cryopod/grab_attack(obj/item/grab/grab, mob/user) + var/mob/living/victim = grab.get_affecting_mob() + if(istype(victim) && istype(user)) if(occupant) to_chat(user, SPAN_NOTICE("\The [src] is in use.")) - return - - if(!ismob(grab.affecting)) - return - - if(!check_occupant_allowed(grab.affecting)) - return - - attempt_enter(grab.affecting, user) + return TRUE + if(!check_occupant_allowed(victim)) + return TRUE + attempt_enter(victim, user) + return TRUE return ..() /obj/machinery/cryopod/verb/eject() @@ -570,22 +524,23 @@ /obj/structure/broken_cryo/attackby(obj/item/W, mob/user) if (busy) to_chat(user, SPAN_NOTICE("Someone else is attempting to open this.")) - return - if (closed) - if (IS_CROWBAR(W)) - busy = 1 - visible_message("[user] starts to pry the glass cover off of \the [src].") - if (!do_after(user, 50, src)) - visible_message("[user] stops trying to pry the glass off of \the [src].") - busy = 0 - return - closed = 0 - busy = 0 - icon_state = "broken_cryo_open" - var/obj/dead = new remains_type(loc) - dead.set_dir(dir) //skeleton is oriented as cryo - else + return TRUE + if (!closed) to_chat(user, SPAN_NOTICE("The glass cover is already open.")) + return TRUE + if (IS_CROWBAR(W)) + busy = 1 + visible_message("[user] starts to pry the glass cover off of \the [src].") + if (!do_after(user, 50, src)) + visible_message("[user] stops trying to pry the glass off of \the [src].") + busy = 0 + return TRUE + closed = 0 + busy = 0 + icon_state = "broken_cryo_open" + var/obj/dead = new remains_type(loc) + dead.set_dir(dir) //skeleton is oriented as cryo + return TRUE /obj/machinery/cryopod/proc/on_mob_spawn() playsound(src, 'sound/machines/ding.ogg', 30, 1) \ No newline at end of file diff --git a/code/game/machinery/dehumidifier.dm b/code/game/machinery/dehumidifier.dm index 47ac27be620..eaef14277b2 100644 --- a/code/game/machinery/dehumidifier.dm +++ b/code/game/machinery/dehumidifier.dm @@ -59,9 +59,10 @@ return TRUE set_active(!active) user.visible_message( - SPAN_NOTICE("[user] switches [active ? "on" : "off"] the [src]."), - SPAN_NOTICE("You switch [active ? "on" : "off"] the [src].")) + SPAN_NOTICE("[user] switches [active ? "on" : "off"] \the [src]."), + SPAN_NOTICE("You switch [active ? "on" : "off"] \the [src].")) return TRUE + return FALSE /obj/machinery/dehumidifier/Process() if(!active) @@ -76,7 +77,7 @@ use_power_oneoff(drainage * 1000) return - T.remove_fluid(CEILING(fluid_here * drainage)) + T.remove_fluid(ceil(fluid_here * drainage)) T.show_bubbles() use_power_oneoff(drainage * 5000) diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 6f04c4a12dd..5b319d56bcb 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -28,15 +28,14 @@ src.icon_state = "barrier[src.locked]" if ((src.locked == 1.0) && (src.emagged < 2.0)) to_chat(user, "Barrier lock toggled on.") - return + return TRUE else if ((src.locked == 0.0) && (src.emagged < 2.0)) to_chat(user, "Barrier lock toggled off.") - return + return TRUE else spark_at(src, amount=2, cardinal_only = TRUE) visible_message("BZZzZZzZZzZT") - return - return + return TRUE else if(IS_WRENCH(W)) var/current_max_health = get_max_health() if (current_health < current_max_health) @@ -44,22 +43,22 @@ emagged = 0 req_access = list(access_security) visible_message("[user] repairs \the [src]!") - return + return TRUE else if (src.emagged > 0) src.emagged = 0 src.req_access = list(access_security) visible_message("[user] repairs \the [src]!") - return - return + return TRUE else switch(W.atom_damage_type) if(BURN) - current_health -= W.force * 0.75 + current_health -= W.get_attack_force(user) * 0.75 if(BRUTE) - current_health -= W.force * 0.5 + current_health -= W.get_attack_force(user) * 0.5 if (current_health <= 0) explode() - ..() + return TRUE + return ..() /obj/machinery/deployable/barrier/explosion_act(severity) . = ..() diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm index 7d26dbd2645..356db05a3fa 100644 --- a/code/game/machinery/doors/_door.dm +++ b/code/game/machinery/doors/_door.dm @@ -136,7 +136,7 @@ if(close_door_at && world.time >= close_door_at) if(autoclose) close_door_at = next_close_time() - INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/machinery/door, close)) + INVOKE_ASYNC(src, PROC_REF(close)) else close_door_at = 0 @@ -235,14 +235,8 @@ . = ..() if(.) visible_message(SPAN_DANGER("\The [src] was hit by \the [AM].")) - var/tforce = 0 - if(ismob(AM)) - tforce = 3 * TT.speed - else if(isobj(AM)) - var/obj/hitter_obj = AM - tforce = hitter_obj.throwforce * (TT.speed/THROWFORCE_SPEED_DIVISOR) playsound(src.loc, hitsound, 100, 1) - take_damage(tforce) + take_damage(AM.get_thrown_attack_force() * (TT.speed/THROWFORCE_SPEED_DIVISOR)) // This is legacy code that should be revisited, probably by moving the bulk of the logic into here. /obj/machinery/door/physical_attack_hand(user) @@ -272,7 +266,7 @@ //figure out how much metal we need var/amount_needed = (current_max_health - current_health) / DOOR_REPAIR_AMOUNT - amount_needed = CEILING(amount_needed) + amount_needed = ceil(amount_needed) var/obj/item/stack/stack = I var/transfer @@ -333,7 +327,7 @@ /obj/machinery/door/bash(obj/item/weapon, mob/user) if(isliving(user) && user.a_intent != I_HURT) return FALSE - if(!weapon.user_can_wield(user)) + if(!weapon.user_can_attack_with(user)) return FALSE if(weapon.item_flags & ITEM_FLAG_NO_BLUDGEON) return FALSE @@ -341,12 +335,13 @@ return FALSE user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.do_attack_animation(src) - if(weapon.force < min_force) + var/force = weapon.get_attack_force(user) + if(force < min_force) user.visible_message("\The [user] hits \the [src] with \the [weapon] with no visible effect.") else user.visible_message("\The [user] forcefully strikes \the [src] with \the [weapon]!") playsound(src.loc, hitsound, 100, 1) - take_damage(weapon.force, weapon.atom_damage_type) + take_damage(force, weapon.atom_damage_type) return TRUE /obj/machinery/door/take_damage(damage, damage_type = BRUTE, damage_flags, inflicter, armor_pen = 0, silent, do_update_health) @@ -436,7 +431,7 @@ /obj/machinery/door/proc/open(forced = FALSE) if(!can_open(forced)) - return + return FALSE operating = 1 @@ -464,7 +459,7 @@ /obj/machinery/door/proc/close(forced = FALSE) if(!can_close(forced)) - return + return FALSE operating = 1 @@ -485,6 +480,7 @@ //I shall not add a check every x ticks if a door has closed over some fire. var/obj/fire/fire = locate() in loc qdel(fire) + return TRUE /obj/machinery/door/proc/toggle(to_open = density) if(to_open) @@ -608,20 +604,20 @@ /decl/public_access/public_method/open_door name = "open door" desc = "Opens the door if possible." - call_proc = /obj/machinery/door/proc/open + call_proc = TYPE_PROC_REF(/obj/machinery/door, open) /decl/public_access/public_method/toggle_door name = "toggle door" desc = "Toggles whether the door is open or not, if possible." - call_proc = /obj/machinery/door/proc/toggle + call_proc = TYPE_PROC_REF(/obj/machinery/door, toggle) /decl/public_access/public_method/toggle_door_to name = "toggle door to" desc = "Toggles the door, depending on the supplied argument, to open (if 1) or closed (if 0)." - call_proc = /obj/machinery/door/proc/toggle + call_proc = TYPE_PROC_REF(/obj/machinery/door, toggle) forward_args = TRUE /decl/public_access/public_method/close_door name = "close door" desc = "Closes the door if possible." - call_proc = /obj/machinery/door/proc/close + call_proc = TYPE_PROC_REF(/obj/machinery/door, close) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 891c8b3c96b..65f6f87a176 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -33,6 +33,10 @@ var/shockedby = list() //Some sort of admin logging var var/welded = null var/locked = FALSE + /// If TRUE, when operating goes from TRUE to FALSE (i.e. door finishes closing/opening), the door will lock itself. + var/locking = FALSE + /// If TRUE, when operating goes from TRUE to FALSE (i.e. door finishes closing/opening), the door will unlock itself. + var/unlocking = FALSE var/lock_cut_state = BOLTS_FINE var/lights = 1 // Lights show by default var/aiDisabledIdScanner = 0 @@ -87,6 +91,7 @@ var/emag_file = 'icons/obj/doors/station/emag.dmi' /obj/machinery/door/airlock/get_material() + RETURN_TYPE(/decl/material) return GET_DECL(mineral ? mineral : /decl/material/solid/metal/steel) /obj/machinery/door/airlock/proc/get_window_material() @@ -291,9 +296,9 @@ About the new airlock wires panel: /obj/machinery/door/airlock/on_update_icon(state=0, override=0) if(set_dir_on_update) - if(connections & (NORTH|SOUTH) == (NORTH|SOUTH)) + if((connections & (NORTH|SOUTH)) == (NORTH|SOUTH)) set_dir(EAST) - else if (connections & (EAST|WEST) == (EAST|WEST)) + else if ((connections & (EAST|WEST)) == (EAST|WEST)) set_dir(SOUTH) switch(state) @@ -653,12 +658,12 @@ About the new airlock wires panel: cut_sound = 'sound/weapons/circsawhit.ogg' cut_delay *= 1.5 - else if(istype(item,/obj/item/twohanded/fireaxe)) + else if(istype(item,/obj/item/bladed/axe/fire)) //special case - zero delay, different message if (src.lock_cut_state == BOLTS_EXPOSED) return FALSE //can't actually cut the bolts, go back to regular smashing - var/obj/item/twohanded/fireaxe/F = item - if (!F.wielded) + var/obj/item/bladed/axe/fire/F = item + if (!F.is_held_twohanded()) return FALSE user.visible_message( SPAN_DANGER("\The [user] smashes the bolt cover open!"), @@ -778,8 +783,8 @@ About the new airlock wires panel: close(1) return TRUE - if(istype(C, /obj/item/twohanded/fireaxe) && !arePowerSystemsOn() && !(user.a_intent == I_HURT)) - var/obj/item/twohanded/fireaxe/F = C + if(istype(C, /obj/item/bladed/axe/fire) && !arePowerSystemsOn() && !(user.a_intent == I_HURT)) + var/obj/item/bladed/axe/fire/F = C if(F.is_held_twohanded(user)) if(locked) to_chat(user, SPAN_WARNING("The airlock's bolts prevent it from being forced.")) @@ -790,7 +795,7 @@ About the new airlock wires panel: else close(1) else - if(user.can_wield_item(F)) + if(user.can_twohand_item(F)) to_chat(user, SPAN_WARNING("You need to be holding \the [C] in both hands to do that!")) else to_chat(user, SPAN_WARNING("You are too small to lever \the [src] open with \the [C]!")) @@ -800,7 +805,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?.force >= 10) + if(I?.get_attack_force(user) >= 10) if(density) visible_message(SPAN_DANGER("\The [A] forces \the [src] open!")) open(1) @@ -815,12 +820,12 @@ About the new airlock wires panel: /obj/machinery/door/airlock/bash(obj/item/weapon, mob/user) //if door is unbroken, hit with fire axe using harm intent - if (istype(weapon, /obj/item/twohanded/fireaxe) && !(stat & BROKEN) && user.a_intent == I_HURT && weapon.user_can_wield(user)) + if (istype(weapon, /obj/item/bladed/axe/fire) && !(stat & BROKEN) && user.a_intent == I_HURT && weapon.user_can_attack_with(user)) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - var/obj/item/twohanded/fireaxe/F = weapon - if (F.wielded) + var/obj/item/bladed/axe/fire/F = weapon + if (F.is_held_twohanded()) playsound(src, 'sound/weapons/smash.ogg', 100, 1) - current_health -= F.force_wielded * 2 + current_health -= F.get_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 @@ -885,7 +890,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/open(var/forced=0) if(!can_open(forced)) - return 0 + return FALSE use_power_oneoff(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people //if the door is unpowered then it doesn't make sense to hear the woosh of a pneumatic actuator @@ -894,7 +899,15 @@ About the new airlock wires panel: else playsound(src.loc, pick(open_sound_unpowered), 100, 1) - return ..() + . = ..() + if(.) + // lock and unlock handle updating for us, we don't want duplicate updates + if(locking) + lock() + else if(unlocking) + unlock() + else + toggle_input_toggle() // this sends an update to anything listening for our status, like docking/airlock controllers /obj/machinery/door/airlock/can_open(var/forced=0) if(QDELETED(src) || brace) @@ -921,7 +934,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/close(var/forced=0) if(!can_close(forced)) - return 0 + return FALSE if(safe) for(var/turf/turf in locs) @@ -931,7 +944,7 @@ About the new airlock wires panel: playsound(src.loc, close_failure_blocked, 30, 0, -3) next_beep_at = world.time + SecondsToTicks(10) close_door_at = world.time + 6 - return + return FALSE for(var/turf/turf in locs) for(var/atom/movable/AM in turf) @@ -945,35 +958,54 @@ About the new airlock wires panel: else playsound(src.loc, pick(close_sound_unpowered), 100, 1) - ..() + . = ..() + if(.) + // lock and unlock handle updating for us, we don't want duplicate updates + if(locking) + lock() + else if(unlocking) + unlock() + else + toggle_input_toggle() // this sends an update to anything listening for our status, like docking/airlock controllers /obj/machinery/door/airlock/proc/lock(var/forced=0) if(locked) - return 0 + return FALSE - if (operating && !forced) return 0 + if (operating && !forced) + unlocking = FALSE + locking = TRUE + return FALSE - if (lock_cut_state == BOLTS_CUT) return 0 //what bolts? + if (lock_cut_state == BOLTS_CUT) return FALSE //what bolts? src.locked = TRUE playsound(src, bolts_dropping, 30, 0, -6) audible_message("You hear a click from the bottom of the door.", hearing_distance = 1) update_icon() - return 1 + toggle_input_toggle() // this sends an update to anything listening for our status, like docking/airlock controllers + return TRUE /obj/machinery/door/airlock/proc/unlock(var/forced=0) if(!src.locked) - return + return FALSE if (!forced) + if(!src.arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) + return FALSE + if(operating) + locking = FALSE + unlocking = TRUE + return FALSE if(operating || !src.arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - return + return FALSE src.locked = FALSE playsound(src, bolts_rising, 30, 0, -6) audible_message("You hear a click from the bottom of the door.", hearing_distance = 1) update_icon() - return 1 + toggle_input_toggle() // this sends an update to anything listening for our status, like docking/airlock controllers + return TRUE /obj/machinery/door/airlock/proc/toggle_lock(var/forced = 0) return locked ? unlock() : lock() diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm index 720b6626025..afaf24809e8 100644 --- a/code/game/machinery/doors/airlock_control.dm +++ b/code/game/machinery/doors/airlock_control.dm @@ -56,17 +56,17 @@ /decl/public_access/public_method/airlock_lock name = "engage bolts" desc = "Bolts the airlock, if possible." - call_proc = /obj/machinery/door/airlock/proc/lock + call_proc = TYPE_PROC_REF(/obj/machinery/door/airlock, lock) /decl/public_access/public_method/airlock_unlock name = "disengage bolts" desc = "Unbolts the airlock, if possible." - call_proc = /obj/machinery/door/airlock/proc/unlock + call_proc = TYPE_PROC_REF(/obj/machinery/door/airlock, unlock) /decl/public_access/public_method/airlock_toggle_bolts name = "toggle bolts" desc = "Toggles whether the airlock is bolted or not, if possible." - call_proc = /obj/machinery/door/airlock/proc/toggle_lock + call_proc = TYPE_PROC_REF(/obj/machinery/door/airlock, toggle_lock) /decl/public_access/public_variable/airlock_door_state expected_type = /obj/machinery/door/airlock diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index e7ce3e4b90c..8b1112a39ed 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -138,6 +138,7 @@ force_close() /obj/machinery/door/blast/get_material() + RETURN_TYPE(/decl/material) return implicit_material // Proc: attackby() @@ -147,8 +148,7 @@ /obj/machinery/door/blast/attackby(obj/item/C, mob/user) add_fingerprint(user, 0, C) if(!panel_open) //Do this here so the door won't change state while prying out the circuit - var/obj/item/twohanded/zweihander = C - if(IS_CROWBAR(C) || (istype(C, /obj/item/twohanded/fireaxe) && zweihander.wielded)) + if(IS_CROWBAR(C) || (istype(C, /obj/item/bladed/axe/fire) && C.is_held_twohanded())) if(((stat & NOPOWER) || (stat & BROKEN)) && !( operating )) to_chat(user, "You begin prying at \the [src]...") if(do_after(user, 2 SECONDS, src)) @@ -157,16 +157,16 @@ to_chat(user, "You must remain still while working on \the [src].") else to_chat(user, "[src]'s motors resist your effort.") - return + return TRUE if(istype(C, /obj/item/stack/material) && C.get_material_type() == /decl/material/solid/metal/plasteel) - var/amt = CEILING((get_max_health() - current_health)/150) + var/amt = ceil((get_max_health() - current_health)/150) if(!amt) to_chat(user, "\The [src] is already fully functional.") - return + return TRUE var/obj/item/stack/P = C if(!P.can_use(amt)) to_chat(user, "You don't have enough sheets to repair this! You need at least [amt] sheets.") - return + return TRUE to_chat(user, "You begin repairing \the [src]...") if(do_after(user, 5 SECONDS, src)) if(P.use(amt)) @@ -176,6 +176,7 @@ to_chat(user, "You don't have enough sheets to repair this! You need at least [amt] sheets.") else to_chat(user, "You must remain still while working on \the [src].") + return TRUE return ..() // Proc: open() @@ -236,7 +237,7 @@ /decl/public_access/public_method/close_door_delayed name = "delayed close door" desc = "Closes the door if possible, after a short delay." - call_proc = /obj/machinery/door/blast/proc/delayed_close + call_proc = TYPE_PROC_REF(/obj/machinery/door/blast, delayed_close) /decl/stock_part_preset/radio/receiver/blast_door frequency = BLAST_DOORS_FREQ diff --git a/code/game/machinery/doors/braces.dm b/code/game/machinery/doors/braces.dm index 39b91a4f4b6..b55afe45495 100644 --- a/code/game/machinery/doors/braces.dm +++ b/code/game/machinery/doors/braces.dm @@ -5,7 +5,7 @@ w_class = ITEM_SIZE_NORMAL icon = 'icons/obj/items/tool/maintenance_jack.dmi' icon_state = ICON_STATE_WORLD - force = 17.5 //It has a hammer head, should probably do some more damage. - Cirra + _base_attack_force = 17 //It has a hammer head, should probably do some more damage. - Cirra attack_cooldown = 2.5*DEFAULT_WEAPON_COOLDOWN melee_accuracy_bonus = -25 material = /decl/material/solid/metal/steel @@ -69,11 +69,9 @@ electronics.attack_self(user) /obj/item/airlock_brace/attackby(obj/item/W, mob/user) - ..() if (istype(W.GetIdCard(), /obj/item/card/id)) if(!airlock) - attack_self(user) - return + return attack_self(user) else var/obj/item/card/id/C = W.GetIdCard() if(check_access(C)) @@ -83,23 +81,23 @@ unlock_brace(usr) else to_chat(user, "You swipe \the [C] through \the [src], but it does not react.") - return + return TRUE if (istype(W, /obj/item/crowbar/brace_jack)) if(!airlock) - return + return FALSE var/obj/item/crowbar/brace_jack/C = W to_chat(user, "You begin forcibly removing \the [src] with \the [C].") if(do_after(user, rand(150,300), airlock)) to_chat(user, "You finish removing \the [src].") unlock_brace(user) - return + return TRUE if(IS_WELDER(W)) var/obj/item/weldingtool/C = W if(!is_damaged()) to_chat(user, "\The [src] does not require repairs.") - return + return TRUE if(C.weld(0,user)) playsound(src, 'sound/items/Welder.ogg', 100, 1) current_health = min(current_health + rand(20,30), get_max_health()) @@ -107,6 +105,8 @@ to_chat(user, "You repair some dents on \the [src]. It is in perfect condition now.") else to_chat(user, "You repair some dents on \the [src].") + return TRUE + return ..() /obj/item/airlock_brace/physically_destroyed(skip_qdel) diff --git a/code/game/machinery/doors/double.dm b/code/game/machinery/doors/double.dm index 290c5c5ee17..1cd2c99b266 100644 --- a/code/game/machinery/doors/double.dm +++ b/code/game/machinery/doors/double.dm @@ -20,6 +20,31 @@ opacity = TRUE width = 2 +// TODO: Find a better way to do this. +/obj/machinery/door/airlock/double/shuttle_rotate(angle) + . = ..() + if(.) + var/turf/obstacle + switch(dir) + if(WEST) + obstacle = get_step(get_step(src, SOUTH), SOUTH) // two turfs south + if(!obstacle.density) + y-- + return + obstacle = get_step(src, NORTH) + if(!obstacle.density) + y++ + return + if(SOUTH) + obstacle = get_step(get_step(src, EAST), EAST) // two turfs east + if(!obstacle.density) + x++ + return + obstacle = get_step(src, WEST) + if(!obstacle.density) + x-- + return + /obj/machinery/door/airlock/double/update_connections(var/propagate = 0) var/dirs = 0 diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index afaddfe4bd7..fab7e5836fe 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -15,6 +15,9 @@ var/panel_file = 'icons/obj/doors/hazard/panel.dmi' var/welded_file = 'icons/obj/doors/hazard/welded.dmi' icon_state = "open" + icon_state_open = "open" + icon_state_closed = "closed" + begins_closed = FALSE initial_access = list(list(access_atmospherics, access_engine_equip)) autoset_access = FALSE opacity = FALSE @@ -111,6 +114,7 @@ register_area(area) /obj/machinery/door/firedoor/get_material() + RETURN_TYPE(/decl/material) return GET_DECL(/decl/material/solid/metal/steel) /obj/machinery/door/firedoor/examine(mob/user, distance) @@ -215,23 +219,22 @@ /obj/machinery/door/firedoor/attackby(obj/item/C, mob/user) add_fingerprint(user, 0, C) if(operating) - return//Already doing something. + return TRUE //Already doing something. if(IS_WELDER(C) && !repairing) var/obj/item/weldingtool/W = C if(W.weld(0, user)) playsound(src, 'sound/items/Welder.ogg', 100, 1) if(do_after(user, 2 SECONDS, src)) - if(!W.isOn()) return + if(!W.isOn()) return TRUE blocked = !blocked user.visible_message("\The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\ "You [blocked ? "weld" : "unweld"] \the [src] with \the [W].",\ "You hear something being welded.") playsound(src, 'sound/items/Welder.ogg', 100, 1) update_icon() - return TRUE else to_chat(user, SPAN_WARNING("You must remain still to complete this task.")) - return TRUE + return TRUE if(blocked && IS_CROWBAR(C)) user.visible_message("\The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\ @@ -239,19 +242,19 @@ "You hear someone struggle and metal straining.") return TRUE - if(!blocked && (IS_CROWBAR(C) || istype(C,/obj/item/twohanded/fireaxe))) + if(!blocked && (IS_CROWBAR(C) || istype(C,/obj/item/bladed/axe/fire))) if(operating) return ..() - if(istype(C,/obj/item/twohanded/fireaxe)) - var/obj/item/twohanded/fireaxe/F = C - if(!F.wielded) + if(istype(C,/obj/item/bladed/axe/fire)) + var/obj/item/bladed/axe/fire/F = C + if(!F.is_held_twohanded()) return ..() user.visible_message("\The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\ "You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\ "You hear metal strain.") - if(do_after(user,30,src)) + if(do_after(user, 3 SECONDS, src)) if(IS_CROWBAR(C)) if(stat & (BROKEN|NOPOWER) || !density) user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\ diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 722d253c526..6ec853d2eb8 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -5,7 +5,7 @@ icon_state = "left" min_force = 4 hitsound = 'sound/effects/Glasshit.ogg' - max_health = 150 //If you change this, consiter changing ../door/window/brigdoor/ health at the bottom of this .dm file + max_health = 150 //If you change this, consider changing ../door/window/brigdoor/ health at the bottom of this .dm file current_health = 150 visible = 0.0 use_power = POWER_USE_OFF @@ -176,7 +176,7 @@ /obj/machinery/door/window/attackby(obj/item/I, mob/user) //If it's in the process of opening/closing, ignore the click if(operating) - return + return TRUE if(bash(I, user)) return TRUE @@ -191,15 +191,17 @@ else if (emagged) to_chat(user, SPAN_WARNING("\The [src] seems to be stuck and refuses to close!")) - return + return TRUE close() + return TRUE else if (density) flick("[base_state]deny", src) + return TRUE /obj/machinery/door/window/bash(obj/item/weapon, mob/user) //Emags and energy swords? You may pass. - if (weapon.user_can_wield(user) && istype(weapon, /obj/item/energy_blade)) + if (weapon.user_can_attack_with(user) && istype(weapon, /obj/item/energy_blade)) var/obj/item/energy_blade/blade = weapon if(blade.is_special_cutting_tool() && emag_act(10, user)) spark_at(loc, amount=5) diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller.dm b/code/game/machinery/embedded_controller/airlock_docking_controller.dm index 99141afeb5d..87696262c88 100644 --- a/code/game/machinery/embedded_controller/airlock_docking_controller.dm +++ b/code/game/machinery/embedded_controller/airlock_docking_controller.dm @@ -21,8 +21,9 @@ else code = stars(code) to_chat(user,"[W]'s screen displays '[code]'") + return TRUE else - ..() + return ..() /obj/machinery/embedded_controller/radio/airlock/docking_port/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nanoui/master_ui = null, var/datum/topic_state/state = global.default_topic_state) var/data[0] diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index af526f2e0d0..6d608fbe586 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -415,7 +415,7 @@ toggleDoor() Sends a radio command to a door to either open or close. If the command is 'toggle' the door will be sent a command that -reverses it's current state. +reverses its current state. Can also toggle whether the door bolts are locked or not, depending on the state of the 'secure' flag. Only sends a command if it is needed, i.e. if the door is @@ -428,6 +428,8 @@ send an additional command to open the door again. if(command == "toggle") command = doorStatus["state"] == "open" ? "close" : "open" + // the close command is 'close' but the closed state is 'closed' + // so we have to check if we aren't the open state, because the command and status strings match for it var/toggle = command && ((doorStatus["state"] == "open") ^ (command == "open")) var/locked = (doorStatus["lock"] == "locked") if(toggle) diff --git a/code/game/machinery/embedded_controller/tin_can.dm b/code/game/machinery/embedded_controller/tin_can.dm index 0f0fc85f343..d2cada82a3d 100644 --- a/code/game/machinery/embedded_controller/tin_can.dm +++ b/code/game/machinery/embedded_controller/tin_can.dm @@ -54,15 +54,15 @@ return state = STATE_EVACUATE toggleDoor(memory["exterior_status"], tag_exterior_door, door_safety, "close") - signalPump(tag_pump_out_internal, 0) - signalPump(tag_pump_out_external, 1) + signalPump(tag_pump_out_internal, 1, 0, 0) // Interior pump, target is a vaccum + signalPump(tag_pump_out_external, 1, 1, 10000) // Exterior pump, target is infinite if("fill_atmos") if(state == STATE_FILL) return state = STATE_FILL toggleDoor(memory["exterior_status"], tag_exterior_door, door_safety, "close") - signalPump(tag_pump_out_internal, 1) - signalPump(tag_pump_out_external, 0) + signalPump(tag_pump_out_internal, 1, 1, memory["external_sensor_pressure"]) // Interior pump, target is exterior pressure + signalPump(tag_pump_out_external, 1, 0, 0) // Exterior pump, target is zero, to intake if("seal") if(state == STATE_SEALED) return diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 259a5461286..b70c00dbe1a 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -40,11 +40,12 @@ add_fingerprint(user, 0, W) src.disable = !src.disable if (src.disable) - user.visible_message("[user] has disconnected the [src]'s flashbulb!", "You disconnect the [src]'s flashbulb!") + user.visible_message("[user] has disconnected \the [src]'s flashbulb!", "You disconnect \the [src]'s flashbulb!") if (!src.disable) - user.visible_message("[user] has connected the [src]'s flashbulb!", "You connect the [src]'s flashbulb!") + user.visible_message("[user] has connected \the [src]'s flashbulb!", "You connect \the [src]'s flashbulb!") + return TRUE else - ..() + return ..() //Let the AI trigger them directly. /obj/machinery/flasher/attack_ai() @@ -134,6 +135,8 @@ else if (src.anchored) user.show_message(text("[src] is now secured.")) src.overlays += "[base_state]-s" + return TRUE + return ..() /obj/machinery/button/flasher name = "flasher button" @@ -143,7 +146,7 @@ /decl/public_access/public_method/flasher_flash name = "flash" desc = "Performs a flash, if possible." - call_proc = /obj/machinery/flasher/proc/flash + call_proc = TYPE_PROC_REF(/obj/machinery/flasher, flash) /decl/stock_part_preset/radio/receiver/flasher frequency = BUTTON_FREQ diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index ae9bc63c9a6..65fef9d81f1 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -58,7 +58,7 @@ var/global/list/floor_light_cache = list() qdel(src) return TRUE - if(W.force && user.a_intent == I_HURT) + if(W.get_attack_force(user) && user.a_intent == I_HURT) return physical_attack_hand(user) return ..() @@ -75,6 +75,7 @@ var/global/list/floor_light_cache = list() if(isnull(damaged)) damaged = 0 return TRUE + return FALSE /obj/machinery/floor_light/interface_interact(var/mob/user) if(!CanInteract(user, DefaultTopicState())) diff --git a/code/game/machinery/floorlayer.dm b/code/game/machinery/floorlayer.dm index 9c61e415d49..6d804caa374 100644 --- a/code/game/machinery/floorlayer.dm +++ b/code/game/machinery/floorlayer.dm @@ -42,14 +42,14 @@ mode[m] = !mode[m] var/O = mode[m] user.visible_message("[usr] has set \the [src] [m] mode [!O?"off":"on"].", "You set \the [src] [m] mode [!O?"off":"on"].") - return + return TRUE if(istype(W, /obj/item/stack/tile)) if(!user.try_unequip(W, T)) - return + return TRUE to_chat(user, "\The [W] successfully loaded.") TakeTile(T) - return + return TRUE if(IS_CROWBAR(W)) if(!length(contents)) @@ -57,15 +57,15 @@ else var/obj/item/stack/tile/E = input("Choose remove tile type.", "Tiles") as null|anything in contents if(E) - to_chat(user, "You remove the [E] from /the [src].") + to_chat(user, "You remove \the [E] from \the [src].") E.dropInto(loc) T = null - return + return TRUE if(IS_SCREWDRIVER(W)) T = input("Choose tile type.", "Tiles") as null|anything in contents - return - ..() + return TRUE + return ..() /obj/machinery/floorlayer/examine(mob/user) . = ..() @@ -82,7 +82,7 @@ if(istype(new_turf, /turf/floor)) var/turf/floor/T = new_turf if(!T.is_plating()) - T.make_plating(!T.is_floor_damaged()) + T.set_flooring(null, place_product = !T.is_floor_damaged()) return new_turf.is_plating() /obj/machinery/floorlayer/proc/TakeNewStack() diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index a3a7e7e34ca..7f1717f7d37 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -63,8 +63,9 @@ var/global/list/holopads = list() /obj/machinery/hologram/holopad/Initialize() . = ..() - // Null ID means we want to use our area name. global.holopads += src + global.listening_objects += src + // Null ID means we want to use our area name. if(isnull(holopad_id)) var/area/A = get_area(src) holopad_id = A?.proper_name || "Unknown" @@ -77,6 +78,10 @@ var/global/list/holopads = list() // Update our desc. desc = "It's a floor-mounted device for projecting holographic images. Its ID is '[holopad_id]'" +/obj/machinery/hologram/holopad/Destroy() + global.listening_objects -= src + return ..() + /obj/machinery/hologram/holopad/interface_interact(var/mob/living/human/user) //Carn: Hologram requests. if(!CanInteract(user, DefaultTopicState())) return FALSE @@ -340,7 +345,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ end_call() if (caller_id&&sourcepad) if(caller_id.loc!=sourcepad.loc) - sourcepad.to_chat(caller_id, "Severing connection to distant holopad.") + to_chat(sourcepad.caller_id, "Severing connection to distant holopad.") end_call() audible_message("The connection has been terminated by the caller.") return 1 diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 3faf3c31f77..81ea2fc50ed 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -53,7 +53,7 @@ /decl/public_access/public_method/holosign_toggle name = "holosign toggle" desc = "Toggle the holosign's active state." - call_proc = /obj/machinery/holosign/proc/toggle + call_proc = TYPE_PROC_REF(/obj/machinery/holosign, toggle) /decl/stock_part_preset/radio/receiver/holosign frequency = BUTTON_FREQ diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 3963ca07fef..f086a33332b 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -73,7 +73,7 @@ /decl/public_access/public_method/igniter_toggle name = "igniter toggle" desc = "Toggle the igniter on or off." - call_proc = /obj/machinery/igniter/proc/ignite + call_proc = TYPE_PROC_REF(/obj/machinery/igniter, ignite) /decl/stock_part_preset/radio/receiver/igniter frequency = BUTTON_FREQ @@ -124,12 +124,13 @@ add_fingerprint(user) disable = !disable if(disable) - user.visible_message("[user] has disabled the [src]!", "You disable the connection to the [src].") + user.visible_message("[user] has disabled \the [src]!", "You disable the connection to \the [src].") else if(!disable) - user.visible_message("[user] has reconnected the [src]!", "You fix the connection to the [src].") + user.visible_message("[user] has reconnected \the [src]!", "You fix the connection to \the [src].") update_icon() + return TRUE else - ..() + return ..() /obj/machinery/sparker/attack_ai() if (anchored) @@ -164,7 +165,7 @@ /decl/public_access/public_method/sparker_spark name = "spark" desc = "Creates sparks to ignite nearby gases." - call_proc = /obj/machinery/sparker/proc/ignite + call_proc = TYPE_PROC_REF(/obj/machinery/sparker, ignite) /decl/stock_part_preset/radio/receiver/sparker frequency = BUTTON_FREQ diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index d6ef1ab8daa..6351cca16c7 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -157,7 +157,7 @@ add_fingerprint(user) wrench_floor_bolts(user, 0, W) power_change() - return + return TRUE return ..() /obj/machinery/media/jukebox/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/game/machinery/kitchen/drying_oven.dm b/code/game/machinery/kitchen/drying_oven.dm index 7c2a310b112..e69de29bb2d 100644 --- a/code/game/machinery/kitchen/drying_oven.dm +++ b/code/game/machinery/kitchen/drying_oven.dm @@ -1,45 +0,0 @@ -/obj/machinery/smartfridge/drying_oven - name = "drying oven" - desc = "A machine for drying plants." - icon_state = "drying_rack" - icon_base = "drying_rack" - obj_flags = OBJ_FLAG_ANCHORABLE - atom_flags = ATOM_FLAG_CLIMBABLE - -/obj/machinery/smartfridge/drying_oven/accept_check(var/obj/item/O) - return istype(O) && O.is_dryable() - -/obj/machinery/smartfridge/drying_oven/Process() - ..() - if(inoperable()) - return - var/do_update = FALSE - for(var/obj/item/thing in get_contained_external_atoms()) - var/obj/item/product = thing.dry_out(src, silent = TRUE) - if(product) - product.dropInto(loc) - do_update = TRUE - if(QDELETED(thing) || !(thing in contents)) - for(var/datum/stored_items/I in item_records) - I.instances -= thing - if(do_update) - update_icon() - -/obj/machinery/smartfridge/drying_oven/on_update_icon() - ..() - var/has_items = FALSE - for(var/datum/stored_items/I in item_records) - if(I.get_amount()) - has_items = TRUE - break - if(inoperable()) - if(has_items) - icon_state = "[icon_base]-plant-off" - else - icon_state = "[icon_base]-off" - else if(has_items) - icon_state = "[icon_base]-plant" - if(!inoperable()) - icon_state = "[icon_base]-close" - else - icon_state = icon_base diff --git a/code/game/machinery/kitchen/gibber.dm b/code/game/machinery/kitchen/gibber.dm index 325eb6ac96d..ce3f44d1938 100644 --- a/code/game/machinery/kitchen/gibber.dm +++ b/code/game/machinery/kitchen/gibber.dm @@ -56,7 +56,7 @@ /obj/machinery/gibber/examine(mob/user) . = ..() - to_chat(user, "The safety guard is [emagged ? "disabled" : "enabled"].") + to_chat(user, "The safety guard is [emagged ? SPAN_DANGER("disabled") : "enabled"].") /obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) emagged = !emagged @@ -71,23 +71,21 @@ return SPAN_NOTICE("You must wait for \the [src] to finish operating first!") return ..() -/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) - if(!operating) - return - if(istype(W, /obj/item/grab)) - var/obj/item/grab/G = W - if(!G.force_danger()) - to_chat(user, "You need a better grip to do that!") - return - qdel(G) - move_into_gibber(user,G.affecting) - else if(istype(W, /obj/item/organ)) - if(!user.try_unequip(W)) - return - qdel(W) - user.visible_message("\The [user] feeds \the [W] into \the [src], obliterating it.") +/obj/machinery/gibber/grab_attack(obj/item/grab/grab, mob/user) + if(grab.force_danger()) + move_into_gibber(user, grab.affecting) + qdel(grab) else - return ..() + to_chat(user, SPAN_DANGER("You need a better grip to do that!")) + return TRUE + +/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) + if(!operating && istype(W, /obj/item/organ)) + if(user.try_unequip(W)) + qdel(W) + user.visible_message(SPAN_DANGER("\The [user] feeds \the [W] into \the [src], obliterating it.")) + return TRUE + return ..() /obj/machinery/gibber/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() @@ -187,15 +185,15 @@ slab_nutrition /= gib_products.len - var/drop_products = FLOOR(gib_products.len * 0.35) + var/drop_products = floor(gib_products.len * 0.35) for(var/atom/movable/thing in gib_products) if(drop_products) drop_products-- qdel(thing) else thing.forceMove(src) - if(istype(thing, /obj/item/chems/food/butchery/meat)) - var/obj/item/chems/food/butchery/meat/slab = thing + if(istype(thing, /obj/item/food/butchery/meat)) + var/obj/item/food/butchery/meat/slab = thing slab.SetName("[slab_name] [slab.name]") slab.add_to_reagents(/decl/material/liquid/nutriment,slab_nutrition) diff --git a/code/game/machinery/kitchen/icecream.dm b/code/game/machinery/kitchen/icecream.dm index 6f534e3bf51..cd751f4d64a 100644 --- a/code/game/machinery/kitchen/icecream.dm +++ b/code/game/machinery/kitchen/icecream.dm @@ -33,7 +33,7 @@ if(ICECREAM_STRAWBERRY) return list(/decl/material/liquid/drink/milk, /decl/material/solid/ice, /decl/material/liquid/drink/juice/berry) if(ICECREAM_BLUE) - return list(/decl/material/liquid/drink/milk, /decl/material/solid/ice, /decl/material/liquid/ethanol/bluecuracao) + return list(/decl/material/liquid/drink/milk, /decl/material/solid/ice, /decl/material/liquid/alcohol/bluecuracao) if(ICECREAM_CHERRY) return list(/decl/material/liquid/drink/milk, /decl/material/solid/ice, /decl/material/liquid/nutriment/cherryjelly) if(ICECREAM_BANANA) @@ -100,10 +100,16 @@ dat += "Chocolate cones: Dispense Make x5 [product_types[CONE_CHOC]] cones left. (Ingredients: flour, sugar, coco powder)
" dat += "
" dat += "VAT CONTENT
" - for(var/reagent_type in reagents?.reagent_volumes) - var/decl/material/R = GET_DECL(reagent_type) - dat += "[R.get_reagent_name(reagents)]: [REAGENT_VOLUME(reagents, reagent_type)]" + for(var/liquid_type in reagents?.liquid_volumes) + var/decl/material/R = GET_DECL(liquid_type) + dat += "[R.get_reagent_name(reagents, MAT_PHASE_LIQUID)]: [LIQUID_VOLUME(reagents, liquid_type)]" dat += "Purge
" + + for(var/solid_type in reagents?.solid_volumes) + var/decl/material/R = GET_DECL(solid_type) + dat += "[R.get_reagent_name(reagents, MAT_PHASE_SOLID)]: [SOLID_VOLUME(reagents, solid_type)]" + dat += "Purge
" + dat += "Refresh Close" var/datum/browser/popup = new(user, "icecreamvat","Icecream Vat", 700, 500, src) @@ -111,8 +117,8 @@ popup.open() /obj/machinery/icecream_vat/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/chems/food/icecream)) - var/obj/item/chems/food/icecream/I = O + if(istype(O, /obj/item/food/icecream)) + var/obj/item/food/icecream/I = O if(!I.ice_creamed) if(product_types[dispense_flavour] > 0) src.visible_message("[html_icon(src)] [user] scoops delicious [flavour_name] icecream into [I].") @@ -126,11 +132,11 @@ to_chat(user, "There is not enough icecream left!") else to_chat(user, "[O] already has icecream in it.") - return 1 + return TRUE else if(ATOM_IS_OPEN_CONTAINER(O)) - return + return TRUE else - ..() + return ..() /obj/machinery/icecream_vat/proc/make(var/mob/user, var/make_type, var/amount) for(var/R in get_ingredient_list(make_type)) @@ -166,7 +172,7 @@ var/cone_name = get_flavour_name(dispense_cone) if(product_types[dispense_cone] >= 1) product_types[dispense_cone] -= 1 - var/obj/item/chems/food/icecream/I = new(src.loc) + var/obj/item/food/icecream/I = new(src.loc) I.cone_type = cone_name I.icon_state = "icecream_cone_[cone_name]" I.desc = "Delicious [cone_name] cone, but no ice cream." @@ -190,7 +196,7 @@ if(href_list["refresh"]) . = TOPIC_REFRESH -/obj/item/chems/food/icecream +/obj/item/food/icecream name = "ice cream cone" desc = "Delicious waffle cone, but no ice cream." icon = 'icons/obj/icecream.dmi' @@ -198,20 +204,22 @@ layer = ABOVE_OBJ_LAYER bitesize = 3 volume = 20 + nutriment_amt = 5 + nutriment_type = /decl/material/liquid/nutriment + nutriment_desc = list("crunchy waffle cone" = 1) var/ice_creamed var/cone_type -/obj/item/chems/food/icecream/Initialize() +/obj/item/food/icecream/Initialize(mapload, material_key, skip_plate = FALSE) . = ..() - add_to_reagents(/decl/material/liquid/nutriment, 5) update_icon() -/obj/item/chems/food/icecream/on_update_icon() +/obj/item/food/icecream/on_update_icon() . = ..() if(ice_creamed) add_overlay("icecream_[ice_creamed]") -/obj/item/chems/food/icecream/proc/add_ice_cream(var/flavour_name) +/obj/item/food/icecream/proc/add_ice_cream(var/flavour_name) name = "[flavour_name] icecream" desc = "Delicious [cone_type] cone with a dollop of [flavour_name] ice cream." ice_creamed = flavour_name diff --git a/code/game/machinery/kitchen/microwave.dm b/code/game/machinery/kitchen/microwave.dm index d56d2ffc896..1ab29ca7377 100644 --- a/code/game/machinery/kitchen/microwave.dm +++ b/code/game/machinery/kitchen/microwave.dm @@ -50,6 +50,10 @@ * Item Adding ********************/ +/obj/machinery/microwave/grab_attack(obj/item/grab/grab, mob/user) + to_chat(user, SPAN_WARNING("This is ridiculous. You can not fit \the [grab.affecting] into \the [src].")) + return TRUE + /obj/machinery/microwave/attackby(var/obj/item/O, var/mob/user) if(broken > 0) if(broken == 2 && IS_SCREWDRIVER(O)) // If it's broken and they're using a screwdriver @@ -101,14 +105,10 @@ else //Otherwise bad luck!! to_chat(user, SPAN_WARNING("It's dirty!")) return 1 - else if(istype(O,/obj/item/chems/glass) || istype(O,/obj/item/chems/drinks) || istype(O,/obj/item/chems/condiment) ) + else if(!istype(O, /obj/item/chems/glass/bowl) && (istype(O,/obj/item/chems/glass) || istype(O,/obj/item/chems/drinks) || istype(O,/obj/item/chems/condiment) )) if (!O.reagents) return 1 return // transfer is handled in afterattack - else if(istype(O,/obj/item/grab)) - var/obj/item/grab/G = O - to_chat(user, SPAN_WARNING("This is ridiculous. You can not fit \the [G.affecting] in this [src].")) - return 1 else if(IS_WRENCH(O)) user.visible_message( SPAN_NOTICE("\The [user] begins [anchored ? "securing" : "unsecuring"] [src]."), @@ -215,7 +215,7 @@ return if (reagents.total_volume && prob(50)) // 50% chance a liquid recipe gets messy - dirty += CEILING(reagents.total_volume / 10) + dirty += ceil(reagents.total_volume / 10) var/decl/recipe/recipe = select_recipe(RECIPE_CATEGORY_MICROWAVE, src, cooking_temperature) if (!recipe) @@ -280,7 +280,7 @@ /obj/machinery/microwave/proc/has_extra_item() for(var/obj/O in get_contained_external_atoms()) - if(!istype(O,/obj/item/chems/food)) + if(!istype(O,/obj/item/food)) return TRUE return FALSE @@ -411,7 +411,7 @@ qdel(O) reagents.clear_reagents() SSnano.update_uis(src) - var/obj/item/chems/food/badrecipe/ffuu = new(src) + var/obj/item/food/badrecipe/ffuu = new(src) ffuu.add_to_reagents(/decl/material/solid/carbon, amount) ffuu.add_to_reagents(/decl/material/liquid/acrylamide, amount/10) return ffuu @@ -457,5 +457,5 @@ las_rating = total_component_rating_of_type(/obj/item/stock_parts/micro_laser) change_power_consumption(initial(active_power_usage) - (cap_rating * 25), POWER_USE_ACTIVE) - max_n_of_items = initial(max_n_of_items) + FLOOR(bin_rating) + max_n_of_items = initial(max_n_of_items) + floor(bin_rating) cooking_power = initial(cooking_power) + (las_rating / 3) diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 6c3161ce09b..cc4014b6eaa 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -55,12 +55,12 @@ /decl/public_access/public_method/driver_drive name = "launch" desc = "Makes the mass driver launch immediately" - call_proc = /obj/machinery/mass_driver/proc/drive + call_proc = TYPE_PROC_REF(/obj/machinery/mass_driver, drive) /decl/public_access/public_method/driver_drive_delayed name = "delayed launch" desc = "Makes the mass driver launch after a short delay" - call_proc = /obj/machinery/mass_driver/proc/delayed_drive + call_proc = TYPE_PROC_REF(/obj/machinery/mass_driver, delayed_drive) /decl/stock_part_preset/radio/receiver/driver frequency = BLAST_DOORS_FREQ diff --git a/code/game/machinery/message_server.dm b/code/game/machinery/message_server.dm index ba19c1bb7b9..fdec0c2b48e 100644 --- a/code/game/machinery/message_server.dm +++ b/code/game/machinery/message_server.dm @@ -140,7 +140,8 @@ var/global/list/message_servers = list() istype(O,/obj/item/stock_parts/circuitboard/message_monitor)) spamfilter_limit += round(MESSAGE_SERVER_DEFAULT_SPAM_LIMIT / 2) qdel(O) - to_chat(user, "You install additional memory and processors into message server. Its filtering capabilities been enhanced.") + to_chat(user, "You install additional memory and processors into \the [src]. Its filtering capabilities been enhanced.") + return TRUE else return ..() diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 0fedef9c7c8..2e01d126217 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -44,14 +44,13 @@ var/global/list/navbeacons = list() /obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user) var/turf/T = loc if(!T.is_plating()) - return // prevent intraction when T-scanner revealed + return TRUE // prevent intraction when T-scanner revealed if(IS_SCREWDRIVER(I)) open = !open - user.visible_message("\The [user] [open ? "opens" : "closes"] cover of \the [src].", "You [open ? "open" : "close"] cover of \the [src].") - update_icon() + return TRUE else if(I.GetIdCard()) if(open) @@ -63,7 +62,8 @@ var/global/list/navbeacons = list() updateDialog() else to_chat(user, "You must open the cover first!") - return + return TRUE + return FALSE /obj/machinery/navbeacon/interface_interact(var/mob/user) interact(user) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index e23f3e5d4fc..75221d30278 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -448,7 +448,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to else dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - var/processed_dat = human_or_robot_user.handle_reading_literacy(human_or_robot_user, dat) + var/processed_dat = human_or_robot_user.handle_reading_literacy(human_or_robot_user, dat, digital = TRUE) if(processed_dat) show_browser(human_or_robot_user, processed_dat, "window=newscaster_main;size=400x600") onclose(human_or_robot_user, "newscaster_main") @@ -744,7 +744,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to icon_state = "newspaper" w_class = ITEM_SIZE_SMALL //Let's make it fit in trashbags! attack_verb = list("bapped","thwapped","smacked") - force = 0 + _base_attack_force = 0 material = /decl/material/solid/organic/paper var/screen = 0 @@ -829,7 +829,7 @@ var/global/list/allCasters = list() //Global list that will contain reference to dat+= "
Previous Page
" dat+="

[src.curr_page+1]
" - var/processed_dat = human_user.handle_reading_literacy(human_user, dat) + var/processed_dat = human_user.handle_reading_literacy(human_user, dat, digital = TRUE) if(processed_dat) show_browser(human_user, processed_dat, "window=newspaper_main;size=300x400") onclose(human_user, "newspaper_main") @@ -874,21 +874,19 @@ var/global/list/allCasters = list() //Global list that will contain reference to if(src.scribble_page == src.curr_page) to_chat(user, SPAN_WARNING("There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?")) return TRUE - else - var/s = input(user, "Write something", "Newspaper") as null | message - if(!length(s)) - return - if(!CanPhysicallyInteractWith(user, src)) - to_chat(user, SPAN_WARNING("You must stay close to \the [src]!")) - return - if(W.do_tool_interaction(TOOL_PEN, user, src, 0, fuel_expenditure = 1) && !QDELETED(src)) //Make it instant, since handle_writing_literacy does the waiting - s = sanitize(s) - s = user.handle_writing_literacy(user, s) - src.scribble_page = src.curr_page - src.scribble = s - src.attack_self(user) - return TRUE - return + var/s = input(user, "Write something", "Newspaper") as null | message + if(!length(s)) + return TRUE + if(!CanPhysicallyInteractWith(user, src)) + to_chat(user, SPAN_WARNING("You must stay close to \the [src]!")) + return TRUE + if(W.do_tool_interaction(TOOL_PEN, user, src, 0, fuel_expenditure = 1) && !QDELETED(src)) //Make it instant, since handle_writing_literacy does the waiting + s = sanitize(s) + s = user.handle_writing_literacy(user, s) + src.scribble_page = src.curr_page + src.scribble = s + src.attack_self(user) + return TRUE return ..() ////////////////////////////////////helper procs diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index 5bcc0785145..27e7d81ea03 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -67,7 +67,7 @@ var/global/bomb_set to_chat(user, "You screw the control panel of \the [src] back on.") playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) flick("lock", src) - return + return TRUE if(panel_open && (IS_MULTITOOL(O) || IS_WIRECUTTER(O))) return attack_hand_with_interaction_checks(user) @@ -75,7 +75,7 @@ var/global/bomb_set if(extended) if(istype(O, /obj/item/disk/nuclear)) if(!user.try_unequip(O, src)) - return + return TRUE auth = O add_fingerprint(user) return attack_hand_with_interaction_checks(user) @@ -85,66 +85,67 @@ var/global/bomb_set if(0) if(IS_WELDER(O)) var/obj/item/weldingtool/WT = O - if(!WT.isOn()) return + if(!WT.isOn()) return TRUE if(WT.get_fuel() < 5) // uses up 5 fuel. to_chat(user, "You need more fuel to complete this task.") - return + return TRUE user.visible_message("[user] starts cutting loose the anchoring bolt covers on [src].", "You start cutting loose the anchoring bolt covers with [O]...") - if(do_after(user,40, src)) - if(!src || !user || !WT.weld(5, user)) return + if(do_after(user, 4 SECONDS, src)) + if(QDELETED(src) || QDELETED(user) || !WT.weld(5, user)) return TRUE user.visible_message("\The [user] cuts through the bolt covers on \the [src].", "You cut through the bolt cover.") removal_stage = 1 - return + return TRUE if(1) if(IS_CROWBAR(O)) user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [O]...") - if(do_after(user, 15, src)) - if(!src || !user) return + if(do_after(user, 1.5 SECONDS, src)) + if(QDELETED(src) || QDELETED(user)) return TRUE user.visible_message("\The [user] forces open the bolt covers on \the [src].", "You force open the bolt covers.") removal_stage = 2 - return + return TRUE if(2) if(IS_WELDER(O)) var/obj/item/weldingtool/WT = O - if(!WT.isOn()) return + if(!WT.isOn()) return TRUE if (WT.get_fuel() < 5) // uses up 5 fuel. to_chat(user, "You need more fuel to complete this task.") - return + return TRUE user.visible_message("[user] starts cutting apart the anchoring system sealant on [src].", "You start cutting apart the anchoring system's sealant with [O]...") - if(do_after(user, 40, src)) - if(!src || !user || !WT.weld(5, user)) return + if(do_after(user, 4 SECONDS, src)) + if(QDELETED(src) || QDELETED(user) || !WT.weld(5, user)) return TRUE user.visible_message("\The [user] cuts apart the anchoring system sealant on \the [src].", "You cut apart the anchoring system's sealant.") removal_stage = 3 - return + return TRUE if(3) if(IS_WRENCH(O)) user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") - if(do_after(user, 50, src)) - if(!src || !user) return + if(do_after(user, 5 SECONDS, src)) + if(QDELETED(src) || QDELETED(user)) return TRUE user.visible_message("[user] unwrenches the anchoring bolts on [src].", "You unwrench the anchoring bolts.") removal_stage = 4 - return + return TRUE if(4) if(IS_CROWBAR(O)) user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") - if(do_after(user, 80, src)) - if(!src || !user) return + if(do_after(user, 8 SECONDS, src)) + if(QDELETED(src) || QDELETED(user)) return TRUE user.visible_message("\The [user] crowbars \the [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!") anchored = FALSE removal_stage = 5 - return - ..() + return TRUE + return ..() /obj/machinery/nuclearbomb/physical_attack_hand(mob/user) + . = FALSE if(!extended && deployable) . = TRUE if(removal_stage < 5) @@ -435,7 +436,7 @@ var/global/bomb_set "vessel self-destruct instructions") //stamp the paper - R.apply_custom_stamp(overlay_image('icons/obj/bureaucracy.dmi', icon_state = "paper_stamp-hos", flags = RESET_COLOR), "'Top Secret'") + R.apply_custom_stamp('icons/obj/items/stamps/stamp_cos.dmi', "'Top Secret'") //====vessel self-destruct system==== /obj/machinery/nuclearbomb/station @@ -462,15 +463,14 @@ var/global/bomb_set . = ..() verbs -= /obj/machinery/nuclearbomb/verb/toggle_deployable for(var/turf/floor/T in get_area(src)) - if(istype(T.flooring, /decl/flooring/reinforced/circuit/red)) + if(istype(T.get_topmost_flooring(), /decl/flooring/reinforced/circuit/red)) flash_tiles += T update_icon() for(var/obj/machinery/self_destruct/ch in get_area(src)) inserters += ch /obj/machinery/nuclearbomb/station/attackby(obj/item/O, mob/user) - if(IS_WRENCH(O)) - return + return TRUE // cannot be moved /obj/machinery/nuclearbomb/station/Topic(href, href_list) if((. = ..())) @@ -547,7 +547,7 @@ var/global/bomb_set if(!last_turf_state || target_icon_state != last_turf_state) for(var/thing in flash_tiles) var/turf/floor/T = thing - if(!istype(T.flooring, /decl/flooring/reinforced/circuit/red)) + if(!istype(T.get_topmost_flooring(), /decl/flooring/reinforced/circuit/red)) flash_tiles -= T continue T.icon_state = target_icon_state diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index b72f83b4267..27a13a93063 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -62,6 +62,7 @@ if(breather) detach_mask(user) return TRUE + return FALSE /obj/machinery/oxygen_pump/interface_interact(mob/user) ui_interact(user) @@ -143,18 +144,21 @@ icon_state = icon_state_open if(!stat) icon_state = icon_state_closed - //TO-DO: Open icon + return TRUE if(istype(W, /obj/item/tank) && (stat & MAINT)) if(tank) to_chat(user, SPAN_WARNING("\The [src] already has a tank installed!")) - else - if(!user.try_unequip(W, src)) - return - tank = W - user.visible_message(SPAN_NOTICE("\The [user] installs \the [tank] into \the [src]."), SPAN_NOTICE("You install \the [tank] into \the [src].")) - src.add_fingerprint(user) + return TRUE + if(!user.try_unequip(W, src)) + return TRUE + tank = W + user.visible_message(SPAN_NOTICE("\The [user] installs \the [tank] into \the [src]."), SPAN_NOTICE("You install \the [tank] into \the [src].")) + src.add_fingerprint(user) + return TRUE if(istype(W, /obj/item/tank) && !stat) to_chat(user, SPAN_WARNING("Please open the maintenance hatch first.")) + return TRUE + return FALSE // TODO: should this be a parent call? do we want this to be (de)constructable? /obj/machinery/oxygen_pump/examine(mob/user) . = ..() diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 0b2d29371b0..57fe940fe0b 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -6,8 +6,7 @@ Buildable meters /obj/item/pipe name = "pipe" desc = "A pipe." - var/connect_types = CONNECT_TYPE_REGULAR - force = 7 + _base_attack_force = 7 icon = 'icons/obj/pipe-item.dmi' icon_state = "simple" randpixel = 5 @@ -17,6 +16,7 @@ Buildable meters obj_flags = OBJ_FLAG_ROTATABLE dir = SOUTH material = /decl/material/solid/metal/steel + var/connect_types = CONNECT_TYPE_REGULAR var/constructed_path = /obj/machinery/atmospherics/pipe/simple/hidden var/pipe_class = PIPE_CLASS_BINARY var/rotate_class = PIPE_ROTATE_STANDARD @@ -104,8 +104,8 @@ Buildable meters playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) if(user) user.visible_message( \ - "[user] fastens the [src].", \ - "You have fastened the [src].", \ + "[user] fastens \the [src].", \ + "You have fastened \the [src].", \ "You hear ratchet.") qdel(src) // remove the pipe item @@ -132,8 +132,9 @@ Buildable meters if(machine.construct_state) machine.construct_state.post_construct(machine) playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) - to_chat(user, "You have fastened the [src].") + to_chat(user, "You have fastened \the [src].") qdel(src) + return TRUE /obj/item/machine_chassis/air_sensor name = "gas sensor" diff --git a/code/game/machinery/pipe/pipelayer.dm b/code/game/machinery/pipe/pipelayer.dm index fcf03128bd6..fdc1b122ef3 100644 --- a/code/game/machinery/pipe/pipelayer.dm +++ b/code/game/machinery/pipe/pipelayer.dm @@ -45,12 +45,12 @@ P_type_t = input("Choose pipe type", "Pipe type") as null|anything in Pipes P_type = Pipes[P_type_t] user.visible_message("[user] has set \the [src] to manufacture [P_type_t].", "You set \the [src] to manufacture [P_type_t].") - return + return TRUE if(IS_CROWBAR(W)) a_dis=!a_dis user.visible_message("[user] has [!a_dis?"de":""]activated auto-dismantling.", "You [!a_dis?"de":""]activate auto-dismantling.") - return + return TRUE if(istype(W, /obj/item/stack/material) && W.get_material_type() == /decl/material/solid/metal/steel) @@ -62,7 +62,7 @@ else user.visible_message("[user] has loaded metal into \the [src].", "You load metal into \the [src]") - return + return TRUE if(IS_SCREWDRIVER(W)) if(metal) @@ -73,11 +73,11 @@ 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 - ..() + return TRUE + return ..() /obj/machinery/pipelayer/examine(mob/user) . = ..() @@ -111,7 +111,7 @@ if(istype(new_turf, /turf/floor)) var/turf/floor/T = new_turf if(!T.is_plating()) - T.make_plating(!T.is_floor_damaged()) + T.set_flooring(null, place_product = !T.is_floor_damaged()) return new_turf.is_plating() /obj/machinery/pipelayer/proc/layPipe(var/turf/w_turf,var/M_Dir,var/old_dir) diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index ba7478c7439..e7f5b123b93 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -236,6 +236,7 @@ var/global/list/turret_icons new /obj/item/assembly/prox_sensor(loc) . = ..() +// TODO: remove these or refactor to use construct states /obj/machinery/porta_turret/attackby(obj/item/I, mob/user) if(stat & BROKEN) if(IS_CROWBAR(I)) @@ -248,17 +249,19 @@ var/global/list/turret_icons physically_destroyed() else to_chat(user, "You remove the turret but did not manage to salvage anything.") + return TRUE + return FALSE else if(IS_WRENCH(I)) if(enabled || raised) to_chat(user, "You cannot unsecure an active turret!") - return + return TRUE if(wrenching) to_chat(user, "Someone is already [anchored ? "un" : ""]securing the turret!") - return + return TRUE if(!anchored && isspaceturf(get_turf(src))) to_chat(user, "Cannot secure turrets in space!") - return + return TRUE user.visible_message( \ "[user] begins [anchored ? "un" : ""]securing the turret.", \ @@ -266,19 +269,14 @@ var/global/list/turret_icons ) wrenching = 1 - if(do_after(user, 50, src)) + if(do_after(user, 5 SECONDS, src)) //This code handles moving the turret around. After all, it's a portable turret! - if(!anchored) - playsound(loc, 'sound/items/Ratchet.ogg', 100, 1) - anchored = TRUE - update_icon() - to_chat(user, "You secure the exterior bolts on the turret.") - else if(anchored) - playsound(loc, 'sound/items/Ratchet.ogg', 100, 1) - anchored = FALSE - to_chat(user, "You unsecure the exterior bolts on the turret.") - update_icon() + playsound(loc, 'sound/items/Ratchet.ogg', 100, TRUE) + anchored = !anchored + update_icon() + to_chat(user, "You [anchored ? "secure" : "unsecure"] the exterior bolts on [src].") wrenching = 0 + return TRUE else if(istype(I, /obj/item/card/id)||istype(I, /obj/item/modular_computer)) //Behavior lock/unlock mangement @@ -288,18 +286,20 @@ var/global/list/turret_icons updateUsrDialog() else to_chat(user, "Access denied.") + return TRUE else //if the turret was attacked with the intention of harming it: + var/force = I.get_attack_force(user) * 0.5 user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - take_damage(I.force * 0.5, I.atom_damage_type) - if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off + 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 if(!attacked && !emagged) attacked = 1 spawn() - sleep(60) + sleep(6 SECONDS) attacked = 0 - ..() + return TRUE /obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user) if(!emagged) @@ -383,6 +383,7 @@ var/global/list/turret_icons atom_flags |= ATOM_FLAG_CLIMBABLE // they're now climbable /obj/machinery/porta_turret/Process() + if(stat & (NOPOWER|BROKEN)) //if the turret has no power or is broken, make the turret pop down if it hasn't already popDown() @@ -399,9 +400,8 @@ var/global/list/turret_icons for(var/mob/M in mobs_in_view(world.view, src)) assess_and_assign(M, targets, secondarytargets) - if(!tryToShootAt(targets)) - if(!tryToShootAt(secondarytargets)) // if no valid targets, go for secondary targets - popDown() // no valid targets, close the cover + if(!tryToShootAt(targets) && !tryToShootAt(secondarytargets)) // if no valid targets, go for secondary targets + popDown() // no valid targets, close the cover var/current_max_health = get_max_health() if(auto_repair && (current_health < current_max_health)) @@ -416,15 +416,12 @@ var/global/list/turret_icons secondarytargets += L /obj/machinery/porta_turret/proc/assess_living(var/mob/living/L) - if(!istype(L)) + if(!istype(L) || !L.simulated) return TURRET_NOT_TARGET if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var return TURRET_NOT_TARGET - if(!L) - return TURRET_NOT_TARGET - if(!emagged && issilicon(L)) // Don't target silica return TURRET_NOT_TARGET @@ -454,34 +451,29 @@ var/global/list/turret_icons if(isanimal(L) || issmall(L)) // Animals are not so dangerous return check_anomalies ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - if(ishuman(L)) //if the target is a human, analyze threat level - if(assess_perp(L) < 4) - return TURRET_NOT_TARGET //if threat level < 4, keep going + if(assess_perp(L) < 4) //if the target is a human, analyze threat level + return TURRET_NOT_TARGET //if threat level < 4, keep going if(L.current_posture.prone) //if the perp is lying down, it's still a target but a less-important target return lethal ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee -/obj/machinery/porta_turret/proc/assess_perp(var/mob/living/human/H) - if(!H || !istype(H)) +/obj/machinery/porta_turret/proc/assess_perp(var/mob/living/perp) + if(!istype(perp)) return 0 - if(emagged) return 10 - - return H.assess_perp(src, check_access, check_weapons, check_records, check_arrest) + return perp.assess_perp(src, check_access, check_weapons, check_records, check_arrest) /obj/machinery/porta_turret/proc/tryToShootAt(var/list/mob/living/targets) - if(targets.len && last_target && (last_target in targets) && target(last_target)) + if(length(targets) && last_target && (last_target in targets) && target(last_target)) return 1 - while(targets.len > 0) - var/mob/living/M = pick(targets) - targets -= M + while(length(targets)) + var/mob/living/M = pick_n_take(targets) if(target(M)) - return 1 - + return TRUE /obj/machinery/porta_turret/proc/popUp() //pops the turret up if(disabled) diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 1326de26f89..fc240f89e81 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -33,9 +33,9 @@ if(charging) to_chat(user, "\A [charging] is already charging here.") return - // Checks to make sure he's not in space doing it, and that the area got proper power. + // Checks to make sure the recharger is powered. if(stat & NOPOWER) - to_chat(user, "The [name] blinks red as you try to insert the item!") + to_chat(user, "\The [src] blinks red as you try to insert \the [G]!") return if (istype(G, /obj/item/gun/energy/)) var/obj/item/gun/energy/E = G @@ -71,6 +71,7 @@ charging = null update_icon() return TRUE + return FALSE /obj/machinery/recharger/Process() if(stat & (NOPOWER|BROKEN) || !anchored) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index f8ee5e4f63b..2fe46fa3e1d 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -208,26 +208,29 @@ var/global/req_console_information = list() /obj/machinery/network/requests_console/attackby(var/obj/item/O, var/mob/user) if (istype(O, /obj/item/card/id)) - if(inoperable(MAINT)) return - if(screen == RCS_MESSAUTH) - var/obj/item/card/id/T = O - msgVerified = text("Verified by [T.registered_name] ([T.assignment])") - SSnano.update_uis(src) - if(screen == RCS_ANNOUNCE) - var/obj/item/card/id/ID = O - if (access_RC_announce in ID.GetAccess()) - announceAuth = 1 - announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name - else - reset_message() - to_chat(user, "You are not authorized to send announcements.") - SSnano.update_uis(src) + if(inoperable(MAINT)) return TRUE + switch(screen) + if(RCS_MESSAUTH) + var/obj/item/card/id/T = O + msgVerified = text("Verified by [T.registered_name] ([T.assignment])") + SSnano.update_uis(src) + if(RCS_ANNOUNCE) + var/obj/item/card/id/ID = O + if (access_RC_announce in ID.GetAccess()) + announceAuth = 1 + announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name + else + reset_message() + to_chat(user, "You are not authorized to send announcements.") + SSnano.update_uis(src) + return TRUE if (istype(O, /obj/item/stamp)) - if(inoperable(MAINT)) return + if(inoperable(MAINT)) return TRUE if(screen == RCS_MESSAUTH) var/obj/item/stamp/T = O msgStamped = "Stamped with the [T.name]" SSnano.update_uis(src) + return TRUE return ..() /obj/machinery/network/requests_console/proc/reset_message(var/mainmenu = 0) diff --git a/code/game/machinery/seed_extractor.dm b/code/game/machinery/seed_extractor.dm index 9dc11989ea3..28bcefc7158 100644 --- a/code/game/machinery/seed_extractor.dm +++ b/code/game/machinery/seed_extractor.dm @@ -14,14 +14,11 @@ /obj/machinery/seed_extractor/attackby(var/obj/item/O, var/mob/user) - if((. = component_attackby(O, user))) - return - // Fruits and vegetables. - if(istype(O, /obj/item/chems/food/grown)) + if(istype(O, /obj/item/food/grown)) if(!user.try_unequip(O)) return TRUE - var/obj/item/chems/food/grown/F = O + var/obj/item/food/grown/F = O if(!F.seed) to_chat(user, SPAN_WARNING("\The [O] doesn't seem to have any usable seeds inside it.")) return TRUE diff --git a/code/game/machinery/self_destruct.dm b/code/game/machinery/self_destruct.dm index 6b516eb954f..ee842c32e23 100644 --- a/code/game/machinery/self_destruct.dm +++ b/code/game/machinery/self_destruct.dm @@ -11,33 +11,35 @@ /obj/machinery/self_destruct/attackby(obj/item/W, mob/user) if(IS_WELDER(W)) - if(damaged) - user.visible_message("[user] begins to repair [src].", "You begin repairing [src].") - if(do_after(usr, 100, src)) - var/obj/item/weldingtool/w = W - if(w.weld(10)) - damaged = 0 - user.visible_message("[user] repairs [src].", "You repair [src].") - else - to_chat(user, "There is not enough fuel to repair [src].") - return + if(!damaged) + return FALSE + user.visible_message("[user] begins to repair [src].", "You begin repairing [src].") + if(do_after(usr, 100, src)) + var/obj/item/weldingtool/w = W + if(w.weld(10)) + damaged = 0 + user.visible_message("[user] repairs [src].", "You repair [src].") + else + to_chat(user, "There is not enough fuel to repair [src].") + return TRUE if(istype(W, /obj/item/nuclear_cylinder)) if(damaged) to_chat(user, "[src] is damaged, you cannot place the cylinder.") - return + return TRUE if(cylinder) to_chat(user, "There is already a cylinder here.") - return - user.visible_message("[user] begins to carefully place [W] onto the Inserter.", "You begin to carefully place [W] onto the Inserter.") + return TRUE + user.visible_message("[user] begins to carefully place [W] onto [src].", "You begin to carefully place [W] onto [src].") if(do_after(user, 80, src) && user.try_unequip(W, src)) cylinder = W density = TRUE - user.visible_message("[user] places [W] onto the Inserter.", "You place [W] onto the Inserter.") + user.visible_message("[user] places [W] onto [src].", "You place [W] onto [src].") update_icon() - return - ..() + return TRUE + return ..() /obj/machinery/self_destruct/physical_attack_hand(mob/user) + . = FALSE if(cylinder) . = TRUE if(armed) diff --git a/code/game/machinery/self_destruct_storage.dm b/code/game/machinery/self_destruct_storage.dm index 09f2e613213..d8cd3e9d3a0 100644 --- a/code/game/machinery/self_destruct_storage.dm +++ b/code/game/machinery/self_destruct_storage.dm @@ -65,15 +65,6 @@ /obj/machinery/nuclear_cylinder_storage/physical_attack_hand(mob/user) if(!panel_open) - if(operable() && locked && check_access(user)) - locked = FALSE - user.visible_message( - "\The [user] unlocks \the [src].", - "You unlock \the [src]." - ) - update_icon() - return TRUE - if(!locked) open = !open user.visible_message( @@ -82,14 +73,24 @@ ) update_icon() return TRUE + if(operable() && check_access(user)) + locked = FALSE + user.visible_message( + "\The [user] unlocks \the [src].", + "You unlock \the [src]." + ) + update_icon() + return TRUE else to_chat(user, SPAN_WARNING("\The [src] is currently in maintenance mode!")) + return TRUE + return FALSE /obj/machinery/nuclear_cylinder_storage/attackby(obj/item/O, mob/user) if(!open && operable() && istype(O, /obj/item/card/id)) if(panel_open) to_chat(user, SPAN_WARNING("\The [src] is currently in maintenance mode!")) - return + return TRUE var/obj/item/card/id/id = O if(check_access(id)) @@ -99,12 +100,12 @@ "You [locked ? "lock" : "unlock"] \the [src]." ) update_icon() - return + return TRUE if(open && istype(O, /obj/item/nuclear_cylinder) && (length(cylinders) < max_cylinders)) if(panel_open) to_chat(user, SPAN_WARNING("\The [src] is currently in maintenance mode!")) - return + return TRUE user.visible_message( "\The [user] begins inserting \the [O] into storage.", @@ -117,8 +118,9 @@ ) cylinders.Add(O) update_icon() + return TRUE - ..() + return ..() /obj/machinery/nuclear_cylinder_storage/handle_mouse_drop(atom/over, mob/user, params) if(over == user && open && !panel_open && length(cylinders)) diff --git a/code/game/machinery/singularitybeacon.dm b/code/game/machinery/singularitybeacon.dm index b47720adbfe..5d8690d5676 100644 --- a/code/game/machinery/singularitybeacon.dm +++ b/code/game/machinery/singularitybeacon.dm @@ -58,18 +58,15 @@ var/global/list/singularity_beacons = list() if(IS_SCREWDRIVER(W)) if(use_power) to_chat(user, SPAN_DANGER("You need to deactivate the beacon first!")) - return + return TRUE + anchored = !anchored if(anchored) - anchored = FALSE - to_chat(user, SPAN_NOTICE("You unscrew the beacon from the floor.")) - return - else - anchored = TRUE to_chat(user, SPAN_NOTICE("You screw the beacon to the floor.")) - return - ..() - return + else + to_chat(user, SPAN_NOTICE("You unscrew the beacon from the floor.")) + return TRUE + return ..() // Ensure the terminal is always accessible to be plugged in. /obj/machinery/singularity_beacon/components_are_accessible(var/path) diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/smartfridge/_smartfridge.dm similarity index 58% rename from code/game/machinery/kitchen/smartfridge.dm rename to code/game/machinery/smartfridge/_smartfridge.dm index d3848036e9f..28f51fab493 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/smartfridge/_smartfridge.dm @@ -3,8 +3,8 @@ */ /obj/machinery/smartfridge name = "\improper SmartFridge" - icon = 'icons/obj/vending.dmi' - icon_state = "fridge_sci" + icon = 'icons/obj/machines/smartfridges/science.dmi' + icon_state = ICON_STATE_WORLD layer = BELOW_OBJ_LAYER density = TRUE anchored = TRUE @@ -14,9 +14,11 @@ obj_flags = OBJ_FLAG_ANCHORABLE | OBJ_FLAG_ROTATABLE atmos_canpass = CANPASS_NEVER required_interaction_dexterity = DEXTERITY_SIMPLE_MACHINES + construct_state = /decl/machine_construction/default/panel_closed + uncreated_component_parts = null + stat_immune = 0 - var/icon_base = "fridge_sci" - var/icon_contents = "chem" + var/overlay_contents_icon = 'icons/obj/machines/smartfridges/contents_chem.dmi' var/list/item_records = list() var/seconds_electrified = 0; var/shoot_inventory = 0 @@ -24,13 +26,6 @@ var/scan_id = 1 var/is_secure = 0 - construct_state = /decl/machine_construction/default/panel_closed - uncreated_component_parts = null - stat_immune = 0 - -/obj/machinery/smartfridge/secure - is_secure = 1 - /obj/machinery/smartfridge/Initialize() if(is_secure) wires = new/datum/wires/smartfridge/secure(src) @@ -51,79 +46,10 @@ return ..() /obj/machinery/smartfridge/proc/accept_check(var/obj/item/O) - if(istype(O,/obj/item/chems/food/grown/) || istype(O,/obj/item/seeds/)) - return 1 - return 0 - -/obj/machinery/smartfridge/seeds - name = "\improper MegaSeed Servitor" - desc = "When you need seeds fast!" - -/obj/machinery/smartfridge/seeds/accept_check(var/obj/item/O) - if(istype(O,/obj/item/seeds/)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/medbay - name = "\improper Refrigerated Medicine Storage" - desc = "A refrigerated storage unit for storing medicine and chemicals." - icon_contents = "chem" - initial_access = list(list(access_medical, access_chemistry)) - -/obj/machinery/smartfridge/secure/medbay/accept_check(var/obj/item/O) - if(istype(O,/obj/item/chems/glass)) - return 1 - if(istype(O,/obj/item/pill_bottle)) - return 1 - if(istype(O,/obj/item/chems/pill)) - return 1 - return 0 - -/obj/machinery/smartfridge/produce - name = "produce smartfridge" - desc = "A refrigerated storage unit for fruits and vegetables." - -/obj/machinery/smartfridge/produce/accept_check(var/obj/item/O) - return istype(O, /obj/item/chems/food/grown) - -/obj/machinery/smartfridge/sheets - name = "raw material storage" - desc = "A storage unit for bundles of material sheets, ingots and other shapes." - -/obj/machinery/smartfridge/sheets/accept_check(var/obj/item/O) - return istype(O, /obj/item/stack/material) - -/obj/machinery/smartfridge/chemistry - name = "\improper Smart Chemical Storage" - desc = "A refrigerated storage unit for medicine and chemical storage." - icon_contents = "chem" - -/obj/machinery/smartfridge/chemistry/accept_check(var/obj/item/O) - if(istype(O,/obj/item/pill_bottle) || istype(O,/obj/item/chems)) + if(istype(O,/obj/item/food/grown/) || istype(O,/obj/item/seeds/)) return 1 return 0 -/obj/machinery/smartfridge/drinks - name = "\improper Drink Showcase" - desc = "A refrigerated storage unit for tasty tasty alcohol." - icon_state = "fridge_dark" - icon_base = "fridge_dark" - icon_contents = "drink" - -/obj/machinery/smartfridge/drinks/accept_check(var/obj/item/O) - if(istype(O,/obj/item/chems/glass) || istype(O,/obj/item/chems/drinks) || istype(O,/obj/item/chems/condiment)) - return 1 - -/obj/machinery/smartfridge/foods - name = "\improper Hot Foods Display" - desc = "A heated storage unit for piping hot meals." - icon_state = "fridge_food" - icon_contents = "food" - -/obj/machinery/smartfridge/foods/accept_check(var/obj/item/O) - if(istype(O,/obj/item/chems/food) || istype(O,/obj/item/utensil)) - return 1 - /obj/machinery/smartfridge/Process() if(stat & (BROKEN|NOPOWER)) return @@ -133,42 +59,61 @@ src.throw_item() /obj/machinery/smartfridge/on_update_icon() - overlays.Cut() - if(stat & (BROKEN|NOPOWER)) - icon_state = "[icon_base]-off" - else - icon_state = icon_base + // Reset our icon_state and overlays. + icon_state = initial(icon_state) + cut_overlays() // Does not appear to be called lower down the chain, sadly. + + // Draw our side panel overlay (for access checking) + var/draw_state if(is_secure) - overlays += image(icon, "[icon_base]-sidepanel") + if(stat & BROKEN) + draw_state = "[icon_state]-sidepanel-broken" + else + draw_state = "[icon_state]-sidepanel" + if(check_state_in_icon(draw_state, icon)) + add_overlay(draw_state) + // Draw our panel overlay. if(panel_open) - overlays += image(icon, "[icon_base]-panel") - - var/image/I - var/is_off = "" - if(inoperable()) - is_off = "-off" + draw_state = "[icon_state]-panel" + if(check_state_in_icon(draw_state, icon)) + add_overlay(draw_state) // Fridge contents - switch(contents.len - LAZYLEN(component_parts)) - if(0) - I = image(icon, "empty[is_off]") - if(1 to 2) - I = image(icon, "[icon_contents]-1[is_off]") - if(3 to 5) - I = image(icon, "[icon_contents]-2[is_off]") - if(6 to 8) - I = image(icon, "[icon_contents]-3[is_off]") - else - I = image(icon, "[icon_contents]-4[is_off]") - overlays += I + if(overlay_contents_icon) + var/is_off = inoperable() ? "-off" : "" + switch(contents.len - LAZYLEN(component_parts)) + if(0) + draw_state = "empty[is_off]" + if(1 to 2) + draw_state = "1[is_off]" + if(3 to 5) + draw_state = "2[is_off]" + if(6 to 8) + draw_state = "3[is_off]" + else + draw_state = "4[is_off]" + if(draw_state && check_state_in_icon(draw_state, icon)) + add_overlay(image(overlay_contents_icon, draw_state)) // Fridge top - I = image(icon, "[icon_base]-top") - I.pixel_z = 32 - I.layer = ABOVE_WINDOW_LAYER - overlays += I + if(stat & BROKEN) + draw_state = "[draw_state]-top-broken" + else + draw_state = "[icon_state]-top" + + if(check_state_in_icon(draw_state, icon)) + var/image/I = image(icon, draw_state) + I.pixel_z = 32 + I.layer = ABOVE_WINDOW_LAYER + add_overlay(I) + + // Append our off state if needed. + if(stat & BROKEN) + icon_state = "[icon_state]-broken" + else if(stat & NOPOWER) + icon_state = "[icon_state]-off" /obj/machinery/smartfridge/dismantle() for(var/datum/stored_items/I in item_records) @@ -187,7 +132,7 @@ /obj/machinery/smartfridge/attackby(var/obj/item/O, var/mob/user) if(accept_check(O)) if(!user.try_unequip(O)) - return + return TRUE stock_item(O) user.visible_message("\The [user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") update_icon() @@ -208,14 +153,6 @@ return TRUE return ..() -/obj/machinery/smartfridge/secure/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - emagged = 1 - locked = -1 - req_access.Cut() - to_chat(user, "You short out the product lock on [src].") - return 1 - /obj/machinery/smartfridge/proc/stock_item(var/obj/item/O) for(var/datum/stored_items/I in item_records) if(istype(O, I.item_path) && O.name == I.item_name) @@ -288,7 +225,9 @@ for(var/i = 1 to amount) I.get_product(get_turf(src)) update_icon() - + var/vend_state = "[icon_state]-vend" + if (check_state_in_icon(vend_state, icon)) //Show the vending animation if needed + flick(vend_state, src) return 1 return 0 @@ -311,12 +250,3 @@ update_icon() return 1 -/************************ -* Secure SmartFridges -*************************/ - -/obj/machinery/smartfridge/secure/CanUseTopic(mob/user, datum/topic_state/state, href_list) - if(!allowed(user) && !emagged && locked != -1 && href_list && href_list["vend"] && scan_id) - to_chat(user, "Access denied.") - return STATUS_CLOSE - return ..() \ No newline at end of file diff --git a/code/game/machinery/smartfridge/_smartfridge_secure.dm b/code/game/machinery/smartfridge/_smartfridge_secure.dm new file mode 100644 index 00000000000..25ba47725b7 --- /dev/null +++ b/code/game/machinery/smartfridge/_smartfridge_secure.dm @@ -0,0 +1,22 @@ +/************************ +* Secure SmartFridges +*************************/ +/obj/machinery/smartfridge/secure + is_secure = 1 + +/obj/machinery/smartfridge/secure/CanUseTopic(mob/user, datum/topic_state/state, href_list) + if(!allowed(user) && !emagged && locked != -1 && href_list && href_list["vend"] && scan_id) + to_chat(user, SPAN_WARNING("Access denied.")) + var/vend_state = "[icon_state]-deny" + if (check_state_in_icon(vend_state, icon)) //Show the vending animation if needed + flick(vend_state, src) + return STATUS_CLOSE + return ..() + +/obj/machinery/smartfridge/secure/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + emagged = 1 + locked = -1 + req_access.Cut() + to_chat(user, SPAN_NOTICE("You short out the product lock on \the [src].")) + return 1 diff --git a/code/game/machinery/smartfridge/chemistry.dm b/code/game/machinery/smartfridge/chemistry.dm new file mode 100644 index 00000000000..486a812d281 --- /dev/null +++ b/code/game/machinery/smartfridge/chemistry.dm @@ -0,0 +1,9 @@ +/obj/machinery/smartfridge/chemistry + name = "\improper Smart Chemical Storage" + desc = "A refrigerated storage unit for medicine and chemical storage." + overlay_contents_icon = 'icons/obj/machines/smartfridges/contents_chem.dmi' + +/obj/machinery/smartfridge/chemistry/accept_check(var/obj/item/O) + if(istype(O,/obj/item/pill_bottle) || istype(O,/obj/item/chems)) + return 1 + return 0 diff --git a/code/game/machinery/smartfridge/drinks.dm b/code/game/machinery/smartfridge/drinks.dm new file mode 100644 index 00000000000..f90f23e2d4b --- /dev/null +++ b/code/game/machinery/smartfridge/drinks.dm @@ -0,0 +1,9 @@ +/obj/machinery/smartfridge/drinks + name = "\improper Drink Showcase" + desc = "A refrigerated storage unit for tasty tasty alcohol." + icon = 'icons/obj/machines/smartfridges/dark.dmi' + overlay_contents_icon = 'icons/obj/machines/smartfridges/contents_drink.dmi' + +/obj/machinery/smartfridge/drinks/accept_check(var/obj/item/O) + if(istype(O,/obj/item/chems/glass) || istype(O,/obj/item/chems/drinks) || istype(O,/obj/item/chems/condiment)) + return 1 diff --git a/code/game/machinery/smartfridge/drying_oven.dm b/code/game/machinery/smartfridge/drying_oven.dm new file mode 100644 index 00000000000..ece54b6c8c3 --- /dev/null +++ b/code/game/machinery/smartfridge/drying_oven.dm @@ -0,0 +1,35 @@ +/obj/machinery/smartfridge/drying_oven + name = "drying oven" + desc = "A machine for drying plants." + icon = 'icons/obj/machines/smartfridges/drying_oven.dmi' + overlay_contents_icon = 'icons/obj/machines/smartfridges/contents_plants.dmi' + obj_flags = OBJ_FLAG_ANCHORABLE + atom_flags = ATOM_FLAG_CLIMBABLE + +/obj/machinery/smartfridge/drying_oven/accept_check(var/obj/item/O) + return istype(O) && O.is_dryable() + +/obj/machinery/smartfridge/drying_oven/Process() + ..() + if(inoperable()) + return + var/do_update = FALSE + for(var/obj/item/thing in get_contained_external_atoms()) + var/obj/item/product = thing.dry_out(src, silent = TRUE) + if(product) + product.dropInto(loc) + do_update = TRUE + if(QDELETED(thing) || !(thing in contents)) + for(var/datum/stored_items/I in item_records) + I.instances -= thing + if(do_update) + update_icon() + +/obj/machinery/smartfridge/drying_oven/on_update_icon() + if(!inoperable()) + for(var/datum/stored_items/I in item_records) + if(I.get_amount()) + cut_overlays() + icon_state = "[initial(icon_state)]-closed" + return + return ..() diff --git a/code/game/machinery/smartfridge/foods.dm b/code/game/machinery/smartfridge/foods.dm new file mode 100644 index 00000000000..1d3cf0d5b89 --- /dev/null +++ b/code/game/machinery/smartfridge/foods.dm @@ -0,0 +1,14 @@ +/obj/machinery/smartfridge/foods + name = "\improper Hot Foods Display" + desc = "A heated storage unit for piping hot meals." + icon = 'icons/obj/machines/smartfridges/food.dmi' + overlay_contents_icon = 'icons/obj/machines/smartfridges/contents_food.dmi' + +/obj/machinery/smartfridge/foods/accept_check(var/obj/item/O) + var/static/list/_food_types = list( + /obj/item/food, + /obj/item/utensil, + /obj/item/chems/glass/bowl, + /obj/item/chems/glass/handmade/bowl + ) + return istype(O) && O.reagents?.total_volume && is_type_in_list(O, _food_types) diff --git a/code/game/machinery/smartfridge/medbay.dm b/code/game/machinery/smartfridge/medbay.dm new file mode 100644 index 00000000000..c1361990e87 --- /dev/null +++ b/code/game/machinery/smartfridge/medbay.dm @@ -0,0 +1,15 @@ + +/obj/machinery/smartfridge/secure/medbay + name = "\improper Refrigerated Medicine Storage" + desc = "A refrigerated storage unit for storing medicine and chemicals." + overlay_contents_icon = 'icons/obj/machines/smartfridges/contents_chem.dmi' + initial_access = list(list(access_medical, access_chemistry)) + +/obj/machinery/smartfridge/secure/medbay/accept_check(var/obj/item/O) + if(istype(O,/obj/item/chems/glass)) + return 1 + if(istype(O,/obj/item/pill_bottle)) + return 1 + if(istype(O,/obj/item/chems/pill)) + return 1 + return 0 diff --git a/code/game/machinery/smartfridge/produce.dm b/code/game/machinery/smartfridge/produce.dm new file mode 100644 index 00000000000..b7ef607b1b5 --- /dev/null +++ b/code/game/machinery/smartfridge/produce.dm @@ -0,0 +1,6 @@ +/obj/machinery/smartfridge/produce + name = "produce smartfridge" + desc = "A refrigerated storage unit for fruits and vegetables." + +/obj/machinery/smartfridge/produce/accept_check(var/obj/item/O) + return istype(O, /obj/item/food/grown) diff --git a/code/game/machinery/smartfridge/seeds.dm b/code/game/machinery/smartfridge/seeds.dm new file mode 100644 index 00000000000..9aa1259e528 --- /dev/null +++ b/code/game/machinery/smartfridge/seeds.dm @@ -0,0 +1,8 @@ +/obj/machinery/smartfridge/seeds + name = "\improper MegaSeed Servitor" + desc = "When you need seeds fast!" + +/obj/machinery/smartfridge/seeds/accept_check(var/obj/item/O) + if(istype(O,/obj/item/seeds/)) + return 1 + return 0 diff --git a/code/game/machinery/smartfridge/sheets.dm b/code/game/machinery/smartfridge/sheets.dm new file mode 100644 index 00000000000..a1e9c3be5f8 --- /dev/null +++ b/code/game/machinery/smartfridge/sheets.dm @@ -0,0 +1,6 @@ +/obj/machinery/smartfridge/sheets + name = "raw material storage" + desc = "A storage unit for bundles of material sheets, ingots and other shapes." + +/obj/machinery/smartfridge/sheets/accept_check(var/obj/item/O) + return istype(O, /obj/item/stack/material) diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 67c1d4baafd..e59df75a663 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -88,9 +88,10 @@ /obj/machinery/space_heater/physical_attack_hand(mob/user) if(!panel_open) on = !on - user.visible_message("[user] switches [on ? "on" : "off"] the [src].","You switch [on ? "on" : "off"] the [src].") + user.visible_message("[user] switches [on ? "on" : "off"] \the [src].","You switch [on ? "on" : "off"] \the [src].") update_icon() return TRUE + return FALSE /obj/machinery/space_heater/Topic(href, href_list, state = global.physical_topic_state) if (..()) diff --git a/code/game/machinery/suit_cycler.dm b/code/game/machinery/suit_cycler.dm index 38847883dfa..7f899870a1c 100644 --- a/code/game/machinery/suit_cycler.dm +++ b/code/game/machinery/suit_cycler.dm @@ -187,77 +187,73 @@ return TRUE return FALSE +/obj/machinery/suit_cycler/grab_attack(obj/item/grab/grab, mob/user) + var/mob/living/victim = grab.get_affecting_mob() + if(istype(victim) && try_move_inside(victim, user)) + qdel(grab) + updateUsrDialog() + return TRUE + return ..() + /obj/machinery/suit_cycler/attackby(obj/item/I, mob/user) - if(electrified != 0) - if(shock(user, 100)) - return + if(electrified != 0 && shock(user, 100)) + return TRUE //Hacking init. if(IS_MULTITOOL(I) || IS_WIRECUTTER(I)) if(panel_open) physical_attack_hand(user) - return - //Other interface stuff. - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - - if(!(ismob(G.affecting))) - return - - if(try_move_inside(G.affecting, user)) - qdel(G) - updateUsrDialog() - return + return TRUE - else if(istype(I, /obj/item/clothing/shoes/magboots)) + if(istype(I, /obj/item/clothing/shoes/magboots)) if(locked) to_chat(user, SPAN_WARNING("The suit cycler is locked.")) - return + return TRUE if(boots) to_chat(user, SPAN_WARNING("The cycler already contains some boots.")) - return + return TRUE if(!user.try_unequip(I, src)) - return + return TRUE to_chat(user, "You fit \the [I] into the suit cycler.") set_boots(I) update_icon() updateUsrDialog() + return TRUE - else if(istype(I,/obj/item/clothing/head/helmet/space) && !istype(I, /obj/item/clothing/head/helmet/space/rig)) + if(istype(I,/obj/item/clothing/head/helmet/space) && !istype(I, /obj/item/clothing/head/helmet/space/rig)) if(locked) to_chat(user, SPAN_WARNING("The suit cycler is locked.")) - return + return TRUE if(helmet) to_chat(user, SPAN_WARNING("The cycler already contains a helmet.")) - return - if(!user.try_unequip(I, src)) - return - to_chat(user, "You fit \the [I] into the suit cycler.") - set_helmet(I) - update_icon() - updateUsrDialog() - return + return TRUE - else if(istype(I,/obj/item/clothing/suit/space/void)) + if(user.try_unequip(I, src)) + to_chat(user, "You fit \the [I] into the suit cycler.") + set_helmet(I) + update_icon() + updateUsrDialog() + return TRUE + + if(istype(I,/obj/item/clothing/suit/space/void)) if(locked) to_chat(user, SPAN_WARNING("The suit cycler is locked.")) - return + return TRUE if(suit) to_chat(user, SPAN_WARNING("The cycler already contains a voidsuit.")) - return + return TRUE - if(!user.try_unequip(I, src)) - return - to_chat(user, "You fit \the [I] into the suit cycler.") - set_suit(I) - update_icon() - updateUsrDialog() - return + if(user.try_unequip(I, src)) + to_chat(user, "You fit \the [I] into the suit cycler.") + set_suit(I) + update_icon() + updateUsrDialog() + return TRUE return ..() diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index b9a53e07e6e..550e474d0ef 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -5,7 +5,8 @@ desc = "An inactive, hacked supply beacon stamped with the Nyx Rapid Fabrication logo. Good for one (1) ballistic supply pod shipment." icon_state = "beacon" material = /decl/material/solid/metal/steel - w_class = ITEM_SIZE_NO_CONTAINER + w_class = ITEM_SIZE_STRUCTURE + obj_flags = OBJ_FLAG_NO_STORAGE var/deploy_path = /obj/structure/supply_beacon var/deploy_time = 30 diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm index 51d662e9d63..141619bd14f 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -15,6 +15,7 @@ density = FALSE obj_flags = OBJ_FLAG_MOVES_UNSUPPORTED directional_offset = @'{"NORTH":{"y":-32}, "SOUTH":{"y":32}, "EAST":{"x":-32}, "WEST":{"x":32}}' + var/enabled = 0 var/lethal = 0 var/locked = 1 @@ -85,7 +86,7 @@ /obj/machinery/turretid/attackby(obj/item/W, mob/user) if(stat & BROKEN) - return + return FALSE if(istype(W, /obj/item/card/id)||istype(W, /obj/item/modular_computer)) if(src.allowed(usr)) @@ -94,7 +95,7 @@ else locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] the panel.") - return + return TRUE return ..() /obj/machinery/turretid/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/game/machinery/turrets/_turrets.dm b/code/game/machinery/turrets/_turrets.dm index 8e95c16fe0f..957c3407889 100644 --- a/code/game/machinery/turrets/_turrets.dm +++ b/code/game/machinery/turrets/_turrets.dm @@ -12,14 +12,14 @@ idle_power_usage = 500 active_power_usage = 10 KILOWATTS interact_offline = TRUE - transform_animate_time = 0.2 SECONDS uncreated_component_parts = list(/obj/item/stock_parts/power/apc) + abstract_type = /obj/machinery/turret + // Visuals. var/image/turret_stand = null var/image/turret_ray = null var/ray_color = "#ffffffff" // Color of the ray, changed by the FSM when switching states. - var/image/transverse_left // Images for displaying the range of the turret's transverse var/image/transverse_right @@ -38,7 +38,7 @@ // Angles // Remember that in BYOND, NORTH equals 0 absolute degrees, and not 90. - var/traverse = 180 // Determines how wide the turret can turn to shoot things, in degrees. The 'front' of the turret is determined by it's dir variable. + var/traverse = 180 // Determines how wide the turret can turn to shoot things, in degrees. The 'front' of the turret is determined by its dir variable. var/leftmost_traverse = null // How far left or right the turret can turn. Set automatically using the above variable and the inital dir value. var/rightmost_traverse = null var/current_bearing = 0 // Current absolute angle the turret has, used to calculate if it needs to turn to try to shoot the target. @@ -133,20 +133,18 @@ update_use_power(POWER_USE_IDLE) /obj/machinery/turret/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/gun)) - if(!installed_gun) - if(!user.try_unequip(I, src)) - return - to_chat(user, SPAN_NOTICE("You install \the [I] into \the [src]!")) - installed_gun = I - setup_gun() - return + if(istype(I, /obj/item/gun) && !installed_gun) + if(!user.try_unequip(I, src)) + return TRUE + to_chat(user, SPAN_NOTICE("You install \the [I] into \the [src]!")) + installed_gun = I + setup_gun() + return TRUE if(istype(I, /obj/item/ammo_magazine) || istype(I, /obj/item/ammo_casing)) var/obj/item/stock_parts/ammo_box/ammo_box = get_component_of_type(/obj/item/stock_parts/ammo_box) if(istype(ammo_box)) - ammo_box.attackby(I, user) - return + return ammo_box.attackby(I, user) . = ..() // This is called after the gun gets instantiated or slotted in. @@ -190,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) @@ -368,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 7054e5ecf23..2ae59092307 100644 --- a/code/game/machinery/turrets/turret_ammo.dm +++ b/code/game/machinery/turrets/turret_ammo.dm @@ -32,10 +32,10 @@ var/obj/item/ammo_casing/casing = W if(stored_caliber && casing.caliber != stored_caliber) to_chat(user, SPAN_WARNING("The caliber of \the [casing] does not match the caliber stored in \the [src]!")) - return FALSE + return TRUE if(length(stored_ammo) >= max_ammo) to_chat(user, SPAN_WARNING("\The [src] is full!")) - return FALSE + return TRUE casing.forceMove(src) stored_ammo += casing @@ -49,16 +49,16 @@ var/obj/item/ammo_magazine/magazine = W 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 FALSE - if(!length(magazine.stored_ammo)) + return TRUE + if(!magazine.get_stored_ammo_count()) to_chat(user, SPAN_WARNING("\The [magazine] is empty!")) - return FALSE + return TRUE if(length(stored_ammo) >= max_ammo) to_chat(user, SPAN_WARNING("\The [src] is full!")) - return FALSE + 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/turrets/turret_fsm.dm b/code/game/machinery/turrets/turret_fsm.dm index d545440d881..2888df30ea0 100644 --- a/code/game/machinery/turrets/turret_fsm.dm +++ b/code/game/machinery/turrets/turret_fsm.dm @@ -1,6 +1,7 @@ /datum/state_machine/turret current_state = /decl/state/turret/idle expected_type = /obj/machinery/turret + base_type = /datum/state_machine/turret /decl/state/turret var/ray_color = "#ffffffff" // Turrets have a visual indicator of their current state. diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index dcd978ded68..8e0f352a8e1 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -4,8 +4,8 @@ /obj/machinery/vending name = "Vendomat" desc = "A generic vending machine." - icon = 'icons/obj/vending.dmi' - icon_state = "generic" + icon = 'icons/obj/machines/vending/generic.dmi' + icon_state = ICON_STATE_WORLD layer = BELOW_OBJ_LAYER anchored = TRUE density = TRUE @@ -20,9 +20,6 @@ wires = /datum/wires/vending required_interaction_dexterity = DEXTERITY_SIMPLE_MACHINES - var/icon_vend //Icon_state when vending - var/icon_deny //Icon_state when denying access - // Power var/vend_power_usage = 150 //actuators and stuff @@ -110,7 +107,7 @@ var/category = current_list[2] for(var/entry in current_list[1]) var/datum/stored_items/vending_products/product = new(src, entry) - product.price = atom_info_repository.get_combined_worth_for(entry) * markup + product.price = ceil(atom_info_repository.get_combined_worth_for(entry) * markup) product.category = category if(product && populate_parts) product.amount = (current_list[1][entry]) ? current_list[1][entry] : 1 @@ -143,6 +140,10 @@ to_chat(user, "You short out the product lock on \the [src].") return 1 +/obj/machinery/vending/receive_mouse_drop(atom/dropping, mob/user, params) + if(!(. = ..()) && isitem(dropping) && istype(user) && user.a_intent == I_HELP && CanPhysicallyInteract(user)) + return attempt_to_stock(dropping, user) + /obj/machinery/vending/attackby(obj/item/W, mob/user) var/obj/item/charge_stick/CS = W.GetChargeStick() @@ -173,14 +174,18 @@ if (istype(W, /obj/item/cash)) attack_hand_with_interaction_checks(user) return TRUE + if(IS_MULTITOOL(W) || IS_WIRECUTTER(W)) if(panel_open) attack_hand_with_interaction_checks(user) return TRUE - if((user.a_intent == I_HELP) && attempt_to_stock(W, user)) - return TRUE + if((. = component_attackby(W, user))) return + + if((user.a_intent == I_HELP) && attempt_to_stock(W, user)) + return TRUE + if((obj_flags & OBJ_FLAG_ANCHORABLE) && (IS_WRENCH(W) || IS_HAMMER(W))) wrench_floor_bolts(user, null, W) power_change() @@ -208,9 +213,9 @@ if(currently_vending.price > cashmoney.absolute_worth) // This is not a status display message, since it's something the character // themselves is meant to see BEFORE putting the money in - to_chat(usr, "[html_icon(cashmoney)] That is not enough money.") + to_chat(usr, SPAN_WARNING("[html_icon(cashmoney)] That is not enough money.")) return 0 - visible_message("\The [usr] inserts some cash into \the [src].") + visible_message(SPAN_INFO("\The [usr] inserts some cash into \the [src].")) cashmoney.adjust_worth(-(currently_vending.price)) // Vending machines have no idea who paid with cash credit_purchase("(cash)") @@ -223,7 +228,7 @@ * successful, 0 if failed. */ /obj/machinery/vending/proc/pay_with_charge_card(var/obj/item/charge_stick/wallet) - visible_message("\The [usr] plugs \the [wallet] into \the [src].") + visible_message(SPAN_INFO("\The [usr] plugs \the [wallet] into \the [src].")) if(wallet.is_locked()) status_message = "Unlock \the [wallet] before using it." status_error = TRUE @@ -251,6 +256,7 @@ if(seconds_electrified != 0) if(shock(user, 100)) return TRUE + return FALSE /obj/machinery/vending/interface_interact(mob/user) ui_interact(user) @@ -269,7 +275,7 @@ data["mode"] = 1 data["product"] = currently_vending.item_name data["price"] = cur.format_value(currently_vending.price) - data["price_num"] = FLOOR(currently_vending.price / cur.absolute_value) + data["price_num"] = floor(currently_vending.price / cur.absolute_value) data["message"] = status_message data["message_err"] = status_error else @@ -286,7 +292,7 @@ "key" = key, "name" = I.item_name, "price" = cur.format_value(I.price), - "price_num" = FLOOR(I.price / cur.absolute_value), + "price_num" = floor(I.price / cur.absolute_value), "color" = I.display_color, "amount" = I.get_amount()))) @@ -321,7 +327,7 @@ if(R.price <= 0) vend(R, user) else if(issilicon(user)) //If the item is not free, provide feedback if a synth is trying to buy something. - to_chat(user, "Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled.") + to_chat(user, SPAN_DANGER("Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled.")) else currently_vending = R if(!vendor_account || vendor_account.suspended) @@ -349,9 +355,12 @@ if(!vend_ready) return if((!allowed(user)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH - to_chat(user, "Access denied.")//Unless emagged of course - flick(icon_deny,src) + to_chat(user, SPAN_WARNING("Access denied."))//Unless emagged of course + var/deny_state = "[icon_state]-deny" + if(check_state_in_icon(deny_state, icon)) + flick(deny_state, src) return + vend_ready = 0 //One thing at a time!! status_message = "Vending..." status_error = 0 @@ -360,8 +369,9 @@ do_vending_reply() use_power_oneoff(vend_power_usage) //actuators and stuff - if (icon_vend) //Show the vending animation if needed - flick(icon_vend,src) + var/vend_state = "[icon_state]-vend" + if (check_state_in_icon(vend_state, icon)) //Show the vending animation if needed + flick(vend_state, src) addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/vending, finish_vending), R), vend_delay) /obj/machinery/vending/proc/do_vending_reply() @@ -380,7 +390,7 @@ if(prob(1)) //The vending gods look favorably upon you sleep(3) if(product.get_product(get_turf(src))) - visible_message("\The [src] clunks as it vends an additional [product.item_name].") + visible_message(SPAN_NOTICE("\The [src] clunks as it vends an additional [product.item_name].")) status_message = "" status_error = 0 vend_ready = 1 @@ -398,7 +408,7 @@ return if(R.add_product(W)) - to_chat(user, "You insert \the [W] in the product receptor.") + to_chat(user, SPAN_NOTICE("You insert \the [W] in the product receptor.")) SSnano.update_uis(src) return 1 @@ -474,5 +484,5 @@ return 0 spawn(0) throw_item.throw_at(target, rand(1,2), 3) - visible_message("\The [src] launches \a [throw_item] at \the [target]!") + visible_message(SPAN_WARNING("\The [src] launches \a [throw_item] at \the [target]!")) return 1 diff --git a/code/game/machinery/vending/actors.dm b/code/game/machinery/vending/actors.dm index 4808085a60c..0dac1d09962 100644 --- a/code/game/machinery/vending/actors.dm +++ b/code/game/machinery/vending/actors.dm @@ -3,14 +3,11 @@ /obj/machinery/vending/props name = "prop dispenser" desc = "All the props an actor could need. Probably." - icon_state = "theater" - icon_vend = "theater-vend" - icon_deny = "theater-deny" + icon = 'icons/obj/machines/vending/theater.dmi' products = list( /obj/structure/flora/pottedplant = 2, /obj/item/flashlight/lamp = 2, /obj/item/flashlight/lamp/green = 2, - /obj/item/chems/drinks/jar = 1, /obj/item/nullrod = 1, /obj/item/sword/cult_toy = 4, /obj/item/sword/katana/toy = 2 @@ -25,7 +22,7 @@ /obj/machinery/vending/containers name = "container dispenser" desc = "A container that dispenses containers." - icon_state = "robotics" + icon = 'icons/obj/machines/vending/robotics.dmi' base_type = /obj/machinery/vending/containers products = list( /obj/structure/closet/crate/freezer = 2, diff --git a/code/game/machinery/vending/botany.dm b/code/game/machinery/vending/botany.dm index 9f29ac496ed..b1042ee71ea 100644 --- a/code/game/machinery/vending/botany.dm +++ b/code/game/machinery/vending/botany.dm @@ -4,9 +4,7 @@ desc = "A plant nutrients vendor." product_slogans = "Aren't you glad you don't have to fertilize the natural way?;Now with 50% less stink!;Plants are people too!" product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..." - icon_state = "nutri" - icon_deny = "nutri-deny" - icon_vend = "nutri-vend" + icon = 'icons/obj/machines/vending/nutrimat.dmi' vend_delay = 26 base_type = /obj/machinery/vending/hydronutrients products = list( @@ -22,18 +20,14 @@ markup = 0 /obj/machinery/vending/hydronutrients/generic - icon_state = "nutri_generic" - icon_vend = "nutri_generic-vend" - icon_deny = "nutri_generic-deny" + icon = 'icons/obj/machines/vending/nutri_green.dmi' /obj/machinery/vending/hydroseeds name = "MegaSeed Servitor" desc = "When you need seeds fast!" product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection this half of the galaxy!;Also certain mushroom varieties available, more for experts! Get certified today!" product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!" - icon_state = "seeds" - icon_vend = "seeds-vend" - icon_deny = "seeds-deny" + icon = 'icons/obj/machines/vending/seeds_green.dmi' vend_delay = 13 base_type = /obj/machinery/vending/hydroseeds markup = 0 @@ -85,6 +79,4 @@ ) /obj/machinery/vending/hydroseeds/generic - icon_state = "seeds_generic" - icon_vend = "seeds_generic-vend" - icon_deny = "seeds_generic-deny" + icon = 'icons/obj/machines/vending/seeds_grey.dmi' diff --git a/code/game/machinery/vending/cigs.dm b/code/game/machinery/vending/cigs.dm index b54fa328b69..e5cb5a43b02 100644 --- a/code/game/machinery/vending/cigs.dm +++ b/code/game/machinery/vending/cigs.dm @@ -21,9 +21,7 @@ We understand the depressed, alcoholic cowboy in you. That's why we also smoke Jericho.;\ Professionals. Better cigarettes for better people. Yes, better people." vend_delay = 21 - icon_state = "cigs" - icon_vend = "cigs-vend" - icon_deny = "cigs-deny" + icon = 'icons/obj/machines/vending/cigarettes.dmi' base_type = /obj/machinery/vending/cigarette products = list( /obj/item/cigpaper/filters = 5, diff --git a/code/game/machinery/vending/engineering.dm b/code/game/machinery/vending/engineering.dm index d23ee004bdb..6eda475db41 100644 --- a/code/game/machinery/vending/engineering.dm +++ b/code/game/machinery/vending/engineering.dm @@ -2,9 +2,7 @@ name = "YouTool" desc = "Tools for tools." markup = 0 - icon_state = "tool" - icon_deny = "tool-deny" - icon_vend = "tool-vend" + icon = 'icons/obj/machines/vending/tool.dmi' vend_delay = 11 base_type = /obj/machinery/vending/tool products = list( @@ -29,9 +27,7 @@ /obj/machinery/vending/engivend name = "Engi-Vend" desc = "Spare tool vending. What? Did you expect some witty description?" - icon_state = "engivend" - icon_deny = "engivend-deny" - icon_vend = "engivend-vend" + icon = 'icons/obj/machines/vending/engivend.dmi' markup = 0 vend_delay = 21 base_type = /obj/machinery/vending/engivend @@ -52,9 +48,7 @@ /obj/machinery/vending/engineering name = "Robco Tool Maker" desc = "Everything you need for do-it-yourself repair." - icon_state = "engi" - icon_deny = "engi-deny" - icon_vend = "engi-vend" + icon = 'icons/obj/machines/vending/engivend.dmi' base_type = /obj/machinery/vending/engineering markup = 0 initial_access = list(access_atmospherics, access_engine_equip) @@ -87,9 +81,7 @@ /obj/machinery/vending/robotics name = "Robotech Deluxe" desc = "All the tools you need to create your own robot army." - icon_state = "robotics" - icon_deny = "robotics-deny" - icon_vend = "robotics-vend" + icon = 'icons/obj/machines/vending/robotics.dmi' initial_access = list(access_robotics) base_type = /obj/machinery/vending/robotics products = list( @@ -108,9 +100,7 @@ /obj/machinery/vending/materials name = "MatterVend" desc = "Provides access to baryonic matter in easy to handle sheet form." - icon_state = "engivend" - icon_deny = "engivend-deny" - icon_vend = "engivend-vend" + icon = 'icons/obj/machines/vending/engivend.dmi' markup = 0 vend_delay = 21 base_type = /obj/machinery/vending/materials diff --git a/code/game/machinery/vending/food.dm b/code/game/machinery/vending/food.dm index 65f1bc4428e..8abc07f5536 100644 --- a/code/game/machinery/vending/food.dm +++ b/code/game/machinery/vending/food.dm @@ -4,26 +4,24 @@ desc = "A snack machine courtesy of the Getmore Chocolate Corporation, based out of Mars." product_slogans = "Try our new nougat bar!;Twice the calories for half the price!" product_ads = "The healthiest!;Award-winning chocolate bars!;Mmm! So good!;Oh my god it's so juicy!;Have a snack.;Snacks are good for you!;Have some more Getmore!;Best quality snacks straight from mars.;We love chocolate!;Try our new jerky!" - icon_state = "snack" - icon_vend = "snack-vend" - icon_deny = "snack-deny" + icon = 'icons/obj/machines/vending/snacks.dmi' vend_delay = 25 base_type = /obj/machinery/vending/snack products = list( /obj/item/clothing/mask/chewable/candy/lolli = 8, /obj/item/chewables/candy/gum = 4, /obj/item/chewables/candy/cookies = 4, - /obj/item/chems/food/candy = 6, + /obj/item/food/junk/candy = 6, /obj/item/chems/drinks/dry_ramen = 6, - /obj/item/chems/food/chips = 6, - /obj/item/chems/food/sosjerky = 6, - /obj/item/chems/food/no_raisin = 6, - /obj/item/chems/food/spacetwinkie = 6, - /obj/item/chems/food/cheesiehonkers = 6, - /obj/item/chems/food/tastybread = 6 + /obj/item/food/junk/chips = 6, + /obj/item/food/junk/sosjerky = 6, + /obj/item/food/junk/no_raisin = 6, + /obj/item/food/junk/spacetwinkie = 6, + /obj/item/food/junk/cheesiehonkers = 6, + /obj/item/food/junk/tastybread = 6 ) contraband = list( - /obj/item/chems/food/syndicake = 6 + /obj/item/food/junk/syndicake = 6 ) //a food variant of the boda machine - It carries slavic themed foods. Mostly beer snacks @@ -33,20 +31,17 @@ vend_delay = 30 base_type = /obj/machinery/vending/snix product_slogans = "Snix!" - - icon_state = "snix" - icon_vend = "snix-vend" - icon_deny = "snix-deny" - products = list(/obj/item/chems/food/semki = 7, - /obj/item/chems/food/can/caviar = 7, - /obj/item/chems/food/squid = 7, - /obj/item/chems/food/croutons = 7, - /obj/item/chems/food/salo = 7, - /obj/item/chems/food/driedfish = 7, - /obj/item/chems/food/pistachios = 7, + icon = 'icons/obj/machines/vending/snix.dmi' + products = list(/obj/item/food/junk/semki = 7, + /obj/item/food/can/caviar = 7, + /obj/item/food/junk/squid = 7, + /obj/item/food/junk/croutons = 7, + /obj/item/food/junk/salo = 7, + /obj/item/food/junk/driedfish = 7, + /obj/item/food/junk/pistachios = 7, ) - contraband = list(/obj/item/chems/food/can/caviar/true = 1) + contraband = list(/obj/item/food/can/caviar/true = 1) /obj/machinery/vending/snix/on_update_icon() ..() @@ -58,20 +53,18 @@ desc = "A SolCentric vending machine dispensing treats from home." vend_delay = 30 product_slogans = "A taste of home!" - icon_state = "solsnack" - icon_vend = "solsnack-vend" - icon_deny = "solsnack-deny" + icon = 'icons/obj/machines/vending/solsnacks.dmi' products = list( - /obj/item/chems/food/lunacake = 8, - /obj/item/chems/food/lunacake/mochicake = 8, - /obj/item/chems/food/lunacake/mooncake = 8, - /obj/item/chems/food/pluto = 8, - /obj/item/chems/food/triton = 8, - /obj/item/chems/food/saturn = 8, - /obj/item/chems/food/jupiter = 8, - /obj/item/chems/food/mars = 8, - /obj/item/chems/food/venus = 8, - /obj/item/chems/food/oort = 8 + /obj/item/food/junk/lunacake = 8, + /obj/item/food/junk/lunacake/mochicake = 8, + /obj/item/food/junk/lunacake/mooncake = 8, + /obj/item/food/junk/pluto = 8, + /obj/item/food/junk/triton = 8, + /obj/item/food/junk/saturn = 8, + /obj/item/food/junk/jupiter = 8, + /obj/item/food/junk/mars = 8, + /obj/item/food/junk/venus = 8, + /obj/item/food/junk/oort = 8 ) /obj/machinery/vending/weeb @@ -79,15 +72,13 @@ desc = "A distressingly ethnic vending machine loaded with high sucrose low calorie for lack of better words snacks." vend_delay = 30 product_slogans = "Konnichiwa gaijin senpai! ;Notice me senpai!; Kawaii-desu!" - icon_state = "weeb" - icon_vend = "weeb-vend" - icon_deny = "weeb-deny" + icon = 'icons/obj/machines/vending/weeb.dmi' products = list( - /obj/item/chems/food/weebonuts = 8, - /obj/item/chems/food/ricecake = 8, - /obj/item/chems/food/dango = 8, - /obj/item/chems/food/pokey = 8, - /obj/item/chems/food/chocobanana = 8 + /obj/item/food/junk/weebonuts = 8, + /obj/item/food/junk/ricecake = 8, + /obj/item/food/junk/dango = 8, + /obj/item/food/junk/pokey = 8, + /obj/item/food/junk/chocobanana = 8 ) /obj/machinery/vending/weeb/on_update_icon() @@ -100,16 +91,13 @@ desc = "An old vending machine promising 'hot foods'. You doubt any of its contents are still edible." vend_delay = 40 base_type = /obj/machinery/vending/hotfood - - icon_state = "hotfood" - icon_deny = "hotfood-deny" - icon_vend = "hotfood-vend" - products = list(/obj/item/chems/food/old/pizza = 1, - /obj/item/chems/food/old/burger = 1, - /obj/item/chems/food/old/hamburger = 1, - /obj/item/chems/food/old/fries = 1, - /obj/item/chems/food/old/hotdog = 1, - /obj/item/chems/food/old/taco = 1 + icon = 'icons/obj/machines/vending/hotfood.dmi' + products = list(/obj/item/food/old/pizza = 1, + /obj/item/food/old/burger = 1, + /obj/item/food/old/hamburger = 1, + /obj/item/food/old/fries = 1, + /obj/item/food/old/hotdog = 1, + /obj/item/food/old/taco = 1 ) /obj/machinery/vending/hotfood/on_update_icon() @@ -120,8 +108,7 @@ /obj/machinery/vending/boozeomat name = "Booze-O-Mat" desc = "A refrigerated vending unit for alcoholic beverages and alcoholic beverage accessories." - icon_state = "fridge_dark" - icon_deny = "fridge_dark-deny" + icon = 'icons/obj/machines/vending/bar.dmi' markup = 0 products = list( /obj/item/chems/drinks/glass2/square = 10, @@ -183,9 +170,7 @@ name = "Hot Drinks machine" desc = "A vending machine which dispenses hot drinks and hot drinks accessories." product_ads = "Have a drink!;Drink up!;It's good for you!;Would you like a hot joe?;I'd kill for some coffee!;The best beans in the galaxy.;Only the finest brew for you.;Mmmm. Nothing like a coffee.;I like coffee, don't you?;Coffee helps you work!;Try some tea.;We hope you like the best!;Try our new chocolate!;Admin conspiracies" - icon_state = "coffee" - icon_vend = "coffee-vend" - icon_deny = "coffee-deny" + icon = 'icons/obj/machines/vending/coffee.dmi' vend_delay = 34 base_type = /obj/machinery/vending/coffee idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan. @@ -196,7 +181,7 @@ /obj/item/chems/drinks/tea/green = 15, /obj/item/chems/drinks/tea/chai = 15, /obj/item/chems/drinks/h_chocolate = 10, - /obj/item/chems/condiment/small/packet/sugar = 25, + /obj/item/chems/packet/sugar = 25, /obj/item/chems/pill/pod/cream = 25, /obj/item/chems/pill/pod/cream_soy = 25, /obj/item/chems/pill/pod/orange = 10, @@ -206,7 +191,7 @@ /obj/machinery/vending/coffee/on_update_icon() ..() - if(stat & BROKEN && prob(20)) + if((stat & BROKEN) && prob(20)) icon_state = "[initial(icon_state)]-hellfire" else if(!(stat & NOPOWER)) add_overlay("[initial(icon_state)]-screen") @@ -214,9 +199,7 @@ /obj/machinery/vending/cola name = "Robust Softdrinks" desc = "A softdrink vendor provided by Robust Industries, LLC." - icon_state = "Cola_Machine" - icon_vend = "Cola_Machine-vend" - icon_deny = "Cola_Machine-deny" + icon = 'icons/obj/machines/vending/drinks.dmi' vend_delay = 11 base_type = /obj/machinery/vending/cola product_slogans = "Robust Softdrinks: More robust than a toolbox to the head!" @@ -236,7 +219,7 @@ ) contraband = list( /obj/item/chems/drinks/cans/thirteenloko = 5, - /obj/item/chems/food/liquidfood = 6 + /obj/item/food/junk/liquidfood = 6 ) idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan. @@ -245,9 +228,7 @@ desc = "An exercise aid and nutrition supplement vendor that preys on your inadequacy." product_slogans = "SweatMAX, get robust!" product_ads = "Pain is just weakness leaving the body!;Run! Your fat is catching up to you;Never forget leg day!;Push out!;This is the only break you get today.;Don't cry, sweat!;Healthy is an outfit that looks good on everybody." - icon_state = "fitness" - icon_vend = "fitness-vend" - icon_deny = "fitness-deny" + icon = 'icons/obj/machines/vending/fitness.dmi' vend_delay = 6 base_type = /obj/machinery/vending/fitness products = list( @@ -256,7 +237,7 @@ /obj/item/chems/drinks/cans/waterbottle = 8, /obj/item/chems/drinks/glass2/fitnessflask/proteinshake = 8, /obj/item/chems/drinks/glass2/fitnessflask = 8, - /obj/item/chems/food/candy/proteinbar = 8, + /obj/item/food/junk/candy/proteinbar = 8, /obj/item/mre/random = 8, /obj/item/mre/menu9 = 4, /obj/item/mre/menu10 = 4, @@ -272,9 +253,7 @@ /obj/machinery/vending/sovietsoda name = "BODA" desc = "An old soda vending machine. How could this have got here?" - icon_state = "sovietsoda" - icon_vend = "sovietsoda-vend" - icon_deny = "sovietsoda-deny" + icon = 'icons/obj/machines/vending/soviet.dmi' base_type = /obj/machinery/vending/sovietsoda product_ads = "For Tsar and Country.;Have you fulfilled your nutrition quota today?;Very nice!;We are simple people, for this is all we eat.;If there is a person, there is a problem. If there is no person, then there is no problem." products = list( diff --git a/code/game/machinery/vending/medical.dm b/code/game/machinery/vending/medical.dm index 7288dabecb3..4796499cbe3 100644 --- a/code/game/machinery/vending/medical.dm +++ b/code/game/machinery/vending/medical.dm @@ -2,9 +2,7 @@ /obj/machinery/vending/medical name = "NanoMed Plus" desc = "Medical drug dispenser." - icon_state = "med" - icon_deny = "med-deny" - icon_vend = "med-vend" + icon = 'icons/obj/machines/vending/medical.dmi' vend_delay = 18 markup = 0 base_type = /obj/machinery/vending/medical @@ -21,8 +19,8 @@ /obj/item/scanner/breath = 5, /obj/item/chems/glass/beaker = 4, /obj/item/chems/dropper = 2, - /obj/item/stack/medical/advanced/bruise_pack = 3, - /obj/item/stack/medical/advanced/ointment = 3, + /obj/item/stack/medical/bandage/advanced = 3, + /obj/item/stack/medical/ointment/advanced = 3, /obj/item/stack/medical/splint = 2, /obj/item/chems/hypospray/autoinjector/pain = 4 ) @@ -38,13 +36,11 @@ name = "NanoMed" desc = "A wall-mounted version of the NanoMed." product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?" - icon_state = "wallmed" - icon_deny = "wallmed-deny" - icon_vend = "wallmed-vend" + icon = 'icons/obj/machines/vending/wallmed.dmi' base_type = /obj/machinery/vending/wallmed1 density = FALSE //It is wall-mounted, and thus, not dense. --Superxpdude products = list( - /obj/item/stack/medical/bruise_pack = 3, + /obj/item/stack/medical/bandage = 3, /obj/item/stack/medical/ointment = 3, /obj/item/chems/pill/painkillers = 2, /obj/item/chems/pill/strong_painkillers = 2, @@ -61,14 +57,12 @@ name = "NanoMed Mini" desc = "A wall-mounted version of the NanoMed, containing only vital first aid equipment." product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?" - icon_state = "wallmed" - icon_deny = "wallmed-deny" - icon_vend = "wallmed-vend" + icon = 'icons/obj/machines/vending/wallmed.dmi' density = FALSE //It is wall-mounted, and thus, not dense. --Superxpdude base_type = /obj/machinery/vending/wallmed2 products = list( /obj/item/chems/hypospray/autoinjector/stabilizer = 5, - /obj/item/stack/medical/bruise_pack = 4, + /obj/item/stack/medical/bandage = 4, /obj/item/stack/medical/ointment = 4, /obj/item/med_pouch/trauma, /obj/item/med_pouch/burn, @@ -77,7 +71,7 @@ /obj/item/med_pouch/radiation ) contraband = list( - /obj/item/chems/pill/bromide = 3, + /obj/item/chems/pill/bromide = 3, /obj/item/chems/hypospray/autoinjector/pain = 2 ) directional_offset = @'{"NORTH":{"y":-24}, "SOUTH":{"y":24}, "EAST":{"x":-24}, "WEST":{"x":24}}' diff --git a/code/game/machinery/vending/misc.dm b/code/game/machinery/vending/misc.dm index fc635b2c888..2bc8d846aa2 100644 --- a/code/game/machinery/vending/misc.dm +++ b/code/game/machinery/vending/misc.dm @@ -1,9 +1,7 @@ /obj/machinery/vending/magivend name = "MagiVend" desc = "A magic vending machine." - icon_state = "MagiVend" - icon_deny = "MagiVend-deny" - icon_vend = "MagiVend-vend" + icon = 'icons/obj/machines/vending/magic.dmi' product_slogans = "Sling spells the proper way with MagiVend!;Be your own Houdini! Use MagiVend!" vend_delay = 15 vend_reply = "Have an enchanted evening!" @@ -14,15 +12,13 @@ /obj/item/clothing/head/wizard/red = 1, /obj/item/clothing/suit/wizrobe/red = 1, /obj/item/clothing/shoes/sandal = 1, - /obj/item/staff = 2) + /obj/item/staff/crystal = 2) /obj/machinery/vending/dinnerware name = "Dinnerware" desc = "A kitchen and restaurant equipment vendor." product_ads = "Mm, food stuffs!;Food and food accessories.;Get your plates!;You like forks?;I like forks.;Woo, utensils.;You don't really need these..." - icon_state = "dinnerware" - icon_vend = "dinnerware-vend" - icon_deny = "dinnerware-deny" + icon = 'icons/obj/machines/vending/dinnerware.dmi' markup = 0 base_type = /obj/machinery/vending/dinnerware products = list( @@ -60,7 +56,7 @@ name = "Smashing Fashions" desc = "For all your cheap knockoff needs." product_slogans = "Look smashing for your darling!;Be rich! Dress rich!" - icon_state = "theater" + icon = 'icons/obj/machines/vending/theater.dmi' vend_delay = 15 base_type = /obj/machinery/vending/fashionvend vend_reply = "Absolutely smashing!" @@ -86,9 +82,7 @@ vend_delay = 15 product_slogans = "Escape to a fantasy world!;Fuel your gambling addiction!;Ruin your friendships!" product_ads = "Elves and dwarves!;Totally not satanic!;Fun times forever!" - icon_state = "games" - icon_deny = "games-deny" - icon_vend = "games-vend" + icon = 'icons/obj/machines/vending/games.dmi' base_type = /obj/machinery/vending/games products = list( /obj/item/toy/blink = 5, @@ -121,10 +115,7 @@ desc = "Vends things that make you less reviled in the work-place!" vend_delay = 15 product_slogans = "Take a shower you hippie.;Get a haircut, hippie!;Reeking of scale taint? Take a shower!" - - icon_state = "lavatory" - icon_deny = "lavatory-deny" - icon_vend = "lavatory-vend" + icon = 'icons/obj/machines/vending/lavatory.dmi' base_type = /obj/machinery/vending/lavatory products = list( /obj/item/soap = 12, diff --git a/code/game/machinery/vending/security.dm b/code/game/machinery/vending/security.dm index 524f5d211ab..17b2a6cc88d 100644 --- a/code/game/machinery/vending/security.dm +++ b/code/game/machinery/vending/security.dm @@ -3,9 +3,7 @@ name = "SecTech" desc = "A security equipment vendor." product_ads = "Crack capitalist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?" - icon_state = "sec" - icon_deny = "sec-deny" - icon_vend = "sec-vend" + icon = 'icons/obj/machines/vending/security.dmi' vend_delay = 14 markup = 0 base_type = /obj/machinery/vending/security @@ -15,7 +13,7 @@ /obj/item/grenade/flashbang = 4, /obj/item/grenade/chem_grenade/teargas = 4, /obj/item/flash = 5, - /obj/item/chems/food/donut = 12, + /obj/item/food/donut = 12, /obj/item/box/evidence = 6 ) contraband = list(/obj/item/clothing/glasses/sunglasses = 2,/obj/item/box/fancy/donut = 2) diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm index f3f7fb0ed89..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 @@ -292,7 +292,8 @@ if(.) M = build_machine_type to_chat(user, SPAN_NOTICE("You setup \the [src]'s software to work as a '[initial(M.name)]', using \the [W].")) - return . + return TRUE + return FALSE /obj/item/frame/button/airlock_controller/kit fully_construct = TRUE @@ -321,7 +322,7 @@ var/choice = input(user, "Chose the type of controller to build:", "Select Controller Type") as null|anything in possible_kit_type_names if(!choice || !CanPhysicallyInteract(user)) build_machine_type = initial(build_machine_type) - return + return TRUE build_machine_type = possible_kit_type_names[choice] M = build_machine_type to_chat(user, SPAN_NOTICE("You set the kit type to '[initial(M.name)]'!")) diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index 8c8f6637d12..a82e8082510 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -69,7 +69,7 @@ /obj/machinery/washing_machine/proc/wash() if(operable()) var/list/washing_atoms = get_contained_external_atoms() - var/amount_per_atom = FLOOR(reagents.total_volume / length(washing_atoms)) + var/amount_per_atom = floor(reagents.total_volume / length(washing_atoms)) if(amount_per_atom > 0) var/decl/material/smelliest = get_smelliest_reagent(reagents) @@ -97,12 +97,12 @@ if(istype(W, /obj/item/chems/pill/detergent)) if(!(atom_flags & ATOM_FLAG_OPEN_CONTAINER)) to_chat(user, SPAN_WARNING("Open the detergent port first!")) - return + return TRUE if(reagents.total_volume >= reagents.maximum_volume) to_chat(user, SPAN_WARNING("The detergent port is full!")) - return + return TRUE if(!user.try_unequip(W)) - return + return TRUE // Directly transfer to the holder to avoid touch reactions. W.reagents?.trans_to_holder(reagents, W.reagents.total_volume) to_chat(user, SPAN_NOTICE("You dissolve \the [W] in the detergent port.")) @@ -131,14 +131,14 @@ else if((!length(wash_whitelist) || is_type_in_list(W, wash_whitelist)) && !is_type_in_list(W, wash_blacklist)) if(W.w_class > max_item_size) to_chat(user, SPAN_WARNING("\The [W] is too large for \the [src]!")) - return + return TRUE if(!user.try_unequip(W, src)) - return + return TRUE state |= WASHER_STATE_LOADED update_icon() else to_chat(user, SPAN_WARNING("You can't put \the [W] in \the [src].")) - return + return TRUE else to_chat(user, SPAN_NOTICE("\The [src] is full.")) return TRUE @@ -256,14 +256,16 @@ if(.) return washer.operable() && !(washer.state & WASHER_STATE_RUNNING) -/decl/interaction_handler/start_washer/invoked(obj/machinery/washing_machine/washer, mob/user) +/decl/interaction_handler/start_washer/invoked(atom/target, mob/user, obj/item/prop) + var/obj/machinery/washing_machine/washer = target return washer.start_washing(user) /decl/interaction_handler/toggle_open/washing_machine name = "Toggle detergent port" expected_target_type = /obj/machinery/washing_machine -/decl/interaction_handler/toggle_open/washing_machine/invoked(obj/machinery/washing_machine/washer, mob/user) +/decl/interaction_handler/toggle_open/washing_machine/invoked(atom/target, mob/user, obj/item/prop) + var/obj/machinery/washing_machine/washer = target return washer.toggle_detergent_port(user) /obj/machinery/washing_machine/on_update_icon() diff --git a/code/game/movietitles.dm b/code/game/movietitles.dm index 1a685073daa..5361529c159 100644 --- a/code/game/movietitles.dm +++ b/code/game/movietitles.dm @@ -75,17 +75,17 @@ var/global/list/end_titles if(H.ckey && H.client) if(H.client.get_preference_value(/datum/client_preference/show_ckey_credits) == PREF_SHOW) showckey = 1 - var/decl/cultural_info/actor_culture = GET_DECL(H.get_cultural_value(TAG_CULTURE)) - if(!actor_culture || !(H.species.spawn_flags & SPECIES_CAN_JOIN) || prob(10)) - actor_culture = GET_DECL(/decl/cultural_info/culture/human) + var/decl/background_detail/background = H.get_background_datum_by_flag(BACKGROUND_FLAG_NAMING) + if(!background || !(H.species.spawn_flags & SPECIES_CAN_JOIN) || prob(10)) + background = GET_DECL(/decl/background_detail/heritage/human) if(!showckey) if(prob(90)) - chunk += "[actor_culture.get_random_name(H, H.gender)]\t \t \t \t[uppertext(used_name)][job]" + chunk += "[background.get_random_name(H, H.gender)]\t \t \t \t[uppertext(used_name)][job]" else - var/decl/pronouns/G = H.get_pronouns() - chunk += "[used_name]\t \t \t \t[uppertext(G.him)]SELF" + var/decl/pronouns/pronouns = H.get_pronouns() + chunk += "[used_name]\t \t \t \t[uppertext(pronouns.him)]SELF" else - chunk += "[uppertext(actor_culture.get_random_name(H, H.gender))] a.k.a. '[uppertext(H.ckey)]'\t \t \t \t[uppertext(used_name)][job]" + chunk += "[uppertext(background.get_random_name(H, H.gender))] a.k.a. '[uppertext(H.ckey)]'\t \t \t \t[uppertext(used_name)][job]" chunksize++ if(chunksize > 2) cast += "
[jointext(chunk,"
")]
" @@ -118,8 +118,8 @@ var/global/list/end_titles if(!C.holder) continue if(C.holder.rights & (R_DEBUG|R_ADMIN)) - var/list/all_cultures = decls_repository.get_decls_of_subtype(/decl/cultural_info/culture) - var/decl/cultural_info/cult = all_cultures[pick(all_cultures)] + var/list/all_backgrounds = decls_repository.get_decls_of_subtype(/decl/background_detail/heritage) + var/decl/background_detail/cult = all_backgrounds[pick(all_backgrounds)] staff += "[uppertext(pick(staffjobs))] - [cult.get_random_name(pick(MALE, FEMALE))] a.k.a. '[C.key]'" else if(C.holder.rights & R_MOD) goodboys += "[C.key]" diff --git a/code/game/objects/objs.dm b/code/game/objects/__objs.dm similarity index 91% rename from code/game/objects/objs.dm rename to code/game/objects/__objs.dm index 8ae8315d01e..5f71fecad44 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/__objs.dm @@ -14,7 +14,6 @@ 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/throwforce = 1 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! @@ -97,7 +96,7 @@ ..() /obj/proc/interact(mob/user) - return + return FALSE /obj/proc/hide(var/hide) set_invisibility(hide ? INVISIBILITY_MAXIMUM : initial(invisibility)) @@ -130,8 +129,8 @@ /obj/attackby(obj/item/used_item, mob/user) // We need to call parent even if we lack dexterity, so that storage can work. - if(used_item.user_can_wield(user)) - if((obj_flags & OBJ_FLAG_ANCHORABLE) && (IS_WRENCH(used_item) || IS_HAMMER(used_item))) + if((obj_flags & OBJ_FLAG_ANCHORABLE) && (IS_WRENCH(used_item) || IS_HAMMER(used_item))) + if(used_item.user_can_attack_with(user)) wrench_floor_bolts(user, null, used_item) update_icon() return TRUE @@ -149,7 +148,7 @@ user.visible_message("\The [user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.") if(do_after(user, delay, src)) if(!src) return - to_chat(user, "You [anchored? "un" : ""]secured \the [src]!") + to_chat(user, SPAN_NOTICE("You [anchored? "un" : ""]secured \the [src]!")) anchored = !anchored return 1 @@ -158,8 +157,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 @@ -245,7 +244,7 @@ var/turf/reverse = get_step(get_turf(src), global.reverse_dir[dir]) //If we're wall mounted and don't have a wall either facing us, or in the opposite direction, don't apply the offset. // This is mainly for things that can be both wall mounted and floor mounted. Like buttons, which mappers seem to really like putting on tables. - // Its sort of a hack for now. But objects don't handle being on a wall or not. (They don't change their flags, layer, etc when on a wall or anything) + // It's sort of a hack for now. But objects don't handle being on a wall or not. (They don't change their flags, layer, etc when on a wall or anything) if(!forward?.is_wall() && !reverse?.is_wall()) return return TRUE @@ -282,7 +281,7 @@ /** * Returns a list with the contents that may be spawned in this object. * This shouldn't include things that are necessary for the object to operate, like machine components. - * Its mainly for populating storage and the like. + * It's mainly for populating storage and the like. */ /obj/proc/WillContain() return @@ -401,3 +400,20 @@ if(obj_flags & OBJ_FLAG_INSULATED_HANDLE) return return ..() + +// Stub, used by /item and /structure +/obj/proc/refresh_color() + return + +// Slightly convoluted reagent logic to avoid fluid_act() putting reagents straight back into the destroyed /obj. +/obj/physically_destroyed(skip_qdel) + var/dumped_reagents = FALSE + var/atom/last_loc = loc + if(last_loc && reagents?.total_volume) + reagents.trans_to(loc, reagents.total_volume, defer_update = TRUE) + dumped_reagents = TRUE + reagents.clear_reagents() // We are qdeling, don't bother with a more nuanced update. + . = ..() + if(dumped_reagents && last_loc && !QDELETED(last_loc) && last_loc.reagents?.total_volume) + last_loc.reagents.handle_update() + HANDLE_REACTIONS(last_loc.reagents) diff --git a/code/game/objects/obj_edibility.dm b/code/game/objects/_obj_edibility.dm similarity index 92% rename from code/game/objects/obj_edibility.dm rename to code/game/objects/_obj_edibility.dm index 4f320837ef2..9fa4ec70827 100644 --- a/code/game/objects/obj_edibility.dm +++ b/code/game/objects/_obj_edibility.dm @@ -92,14 +92,6 @@ else to_chat(user, SPAN_WARNING("There is nothing in \the [src] that \the [target] can consume.")) -/obj/proc/show_food_no_mouth_message(mob/user, mob/target) - target = target || user - if(user) - if(user == target) - to_chat(user, SPAN_WARNING("Where do you intend to put \the [src]? You don't have a mouth!")) - else - to_chat(user, SPAN_WARNING("Where do you intend to put \the [src]? \The [target] doesn't have a mouth!")) - /obj/proc/play_feed_sound(var/mob/user, consumption_method = EATING_METHOD_EAT) var/turf/play_turf = get_turf(user) if(!play_turf) @@ -110,9 +102,6 @@ if(EATING_METHOD_DRINK) playsound(user.loc, 'sound/items/drink.ogg', rand(10, 50), 1) -/obj/proc/show_food_empty_message(mob/user, mob/target) - to_chat(user, SPAN_NOTICE("\The [src] is empty.")) - /obj/proc/is_food_empty(mob/eater) return get_edible_material_amount(eater) <= 0 @@ -135,15 +124,16 @@ show_food_inedible_message(user, target) return EATEN_UNABLE + var/consumption_method = get_food_consumption_method(target) if(is_food_empty(target)) - show_food_empty_message(user, target) + show_food_empty_message(user, consumption_method) return EATEN_UNABLE if(!target.check_has_mouth()) show_food_no_mouth_message(user, target) return EATEN_UNABLE - if(!target.can_eat_food_currently(src, user)) + if(!target.can_eat_food_currently(src, user, consumption_method)) return EATEN_UNABLE var/blocked = target.check_mouth_coverage() @@ -154,7 +144,6 @@ if(user != target && !user.can_force_feed(target, src)) return EATEN_UNABLE - var/consumption_method = get_food_consumption_method(target) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(user != target) if(!user.can_force_feed(target, src)) diff --git a/code/game/objects/objs_damage.dm b/code/game/objects/_objs_damage.dm similarity index 100% rename from code/game/objects/objs_damage.dm rename to code/game/objects/_objs_damage.dm diff --git a/code/game/objects/objs_interactions.dm b/code/game/objects/_objs_interactions.dm similarity index 100% rename from code/game/objects/objs_interactions.dm rename to code/game/objects/_objs_interactions.dm diff --git a/code/game/objects/alien_props.dm b/code/game/objects/alien_props.dm index b6dca18e783..6297ef140f0 100644 --- a/code/game/objects/alien_props.dm +++ b/code/game/objects/alien_props.dm @@ -65,8 +65,7 @@ . = ..() if(!random_light_color) random_light_color = get_random_colour(FALSE, 100, 255) - b_color = random_light_color - color = random_light_color + set_color(random_light_color) // if stuff starts exploding due to too-early update_icon calls it's this thing's fault //Airlock /obj/machinery/door/airlock/alien diff --git a/code/game/objects/auras/starlight.dm b/code/game/objects/auras/starlight.dm index 4fb7179b4e7..5e6253cbd93 100644 --- a/code/game/objects/auras/starlight.dm +++ b/code/game/objects/auras/starlight.dm @@ -15,6 +15,6 @@ /obj/aura/starborn/attackby(var/obj/item/I, var/mob/i_user) if(I.atom_damage_type == BURN) to_chat(i_user, "\The [I] seems to only feed into \the [user]'s flames.") - user.heal_damage(BRUTE, I.force) + user.heal_damage(BRUTE, I.get_attack_force(user)) return AURA_FALSE return 0 \ No newline at end of file diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index 27bffa2de76..ccfd9b93b6e 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -60,7 +60,7 @@ if(!istype(AM, /obj/effect/effect/smoke/chem)) reagents.splash(AM, splash_amount, copy = 1) -// Fades out the smoke smoothly using it's alpha variable. +// Fades out the smoke smoothly using its alpha variable. /obj/effect/effect/smoke/chem/end_of_life() if(QDELETED(src)) return @@ -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 2dcb2c0123d..1e7a3685c90 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) @@ -164,17 +162,21 @@ to_chat(user, "You hit the metal foam but bounce off it.") return TRUE -/obj/structure/foamedmetal/attackby(var/obj/item/I, var/mob/user) - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - G.affecting.forceMove(loc) - visible_message("[G.assailant] smashes [G.affecting] through the foamed metal wall.") - qdel(I) - qdel(src) - return - if(prob(I.force * 20 - metal * 25)) - user.visible_message("[user] smashes through the foamed metal.", "You smash through the foamed metal with \the [I].") - qdel(src) +/obj/structure/foamedmetal/grab_attack(obj/item/grab/grab, mob/user) + grab.affecting.forceMove(loc) + visible_message(SPAN_DANGER("\The [user] smashes \the [grab.affecting] through the foamed metal wall!")) + qdel(grab) + physically_destroyed() + return TRUE + +/obj/structure/foamedmetal/attackby(var/obj/item/I, var/mob/user) + if(prob(I.get_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].") + ) + physically_destroyed() else - to_chat(user, "You hit the metal foam to no effect.") + to_chat(user, SPAN_WARNING("You hit \the [src] to no effect.")) + return TRUE diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 9a1dfda3656..3cc2dd4bf5e 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -59,13 +59,15 @@ // Returns true if overriden and needs deletion. If the argument is false, we will merge into any existing blood. /obj/effect/decal/cleanable/blood/proc/merge_with_blood() if(!isturf(loc) || blood_size == BLOOD_SIZE_NO_MERGE) - return + 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 + return FALSE /obj/effect/decal/cleanable/blood/proc/start_drying() drytime = world.time + DRYING_TIME * (amount+1) diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 3dc7f727a91..2a22a171886 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -1,16 +1,20 @@ /obj/effect/decal/cleanable/generic - name = "clutter" - desc = "Someone should clean that up." - gender = PLURAL - icon = 'icons/obj/objects.dmi' - icon_state = "shards" + name = "clutter" + desc = "Someone should clean that up." + gender = PLURAL + icon = 'icons/obj/objects.dmi' + icon_state = "shards" + sweepable = TRUE /obj/effect/decal/cleanable/ash - name = "ashes" - desc = "Ashes to ashes, dust to dust, and into space." - gender = PLURAL - icon = 'icons/obj/objects.dmi' - icon_state = "ash" + name = "ashes" + desc = "Ashes to ashes, dust to dust, and into space." + gender = PLURAL + icon = 'icons/obj/objects.dmi' + icon_state = "ash" + weather_sensitive = FALSE + sweepable = TRUE + burnable = FALSE /obj/effect/decal/cleanable/ash/attackby(obj/item/I, mob/user) if(ATOM_IS_OPEN_CONTAINER(I)) @@ -33,44 +37,50 @@ return TRUE /obj/effect/decal/cleanable/flour - name = "flour" - desc = "It's still good. Four second rule!" - gender = PLURAL - icon = 'icons/effects/effects.dmi' - icon_state = "flour" - persistent = TRUE + name = "flour" + desc = "It's still good. Four second rule!" + gender = PLURAL + icon = 'icons/effects/effects.dmi' + icon_state = "flour" + persistent = TRUE + sweepable = TRUE /obj/effect/decal/cleanable/cobweb - name = "cobweb" - desc = "Somebody should remove that." - layer = ABOVE_HUMAN_LAYER - icon = 'icons/effects/effects.dmi' - icon_state = "cobweb1" + name = "cobweb" + desc = "Somebody should remove that." + layer = ABOVE_HUMAN_LAYER + icon = 'icons/effects/effects.dmi' + icon_state = "cobweb1" + weather_sensitive = FALSE + sweepable = TRUE /obj/effect/decal/cleanable/molten_item - name = "gooey grey mass" - desc = "It looks like a melted... something." - icon = 'icons/effects/molten_item.dmi' - icon_state = "molten" - persistent = TRUE - generic_filth = TRUE + name = "gooey grey mass" + desc = "It looks like a melted... something." + icon = 'icons/effects/molten_item.dmi' + icon_state = "molten" + persistent = TRUE + generic_filth = TRUE + weather_sensitive = FALSE /obj/effect/decal/cleanable/cobweb2 - name = "cobweb" - desc = "Somebody should remove that." - layer = ABOVE_HUMAN_LAYER - icon = 'icons/effects/effects.dmi' - icon_state = "cobweb2" + name = "cobweb" + desc = "Somebody should remove that." + layer = ABOVE_HUMAN_LAYER + icon = 'icons/effects/effects.dmi' + icon_state = "cobweb2" + weather_sensitive = FALSE + sweepable = TRUE //Vomit (sorry) /obj/effect/decal/cleanable/vomit - name = "vomit" - desc = "Gosh, how unpleasant." - gender = PLURAL - icon = 'icons/effects/vomit.dmi' - icon_state = "vomit_1" - persistent = TRUE - generic_filth = TRUE + name = "vomit" + desc = "Gosh, how unpleasant." + gender = PLURAL + icon = 'icons/effects/vomit.dmi' + icon_state = "vomit_1" + persistent = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/vomit/Initialize(ml, _age) random_icon_states = icon_states(icon) @@ -85,37 +95,46 @@ color = reagents.get_color() /obj/effect/decal/cleanable/tomato_smudge - name = "tomato smudge" - desc = "It's red." - icon = 'icons/effects/tomatodecal.dmi' - icon_state = "tomato_floor1" + name = "tomato smudge" + desc = "It's red." + icon = 'icons/effects/tomatodecal.dmi' + icon_state = "tomato_floor1" random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3") - persistent = TRUE - generic_filth = TRUE + persistent = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/egg_smudge - name = "smashed egg" - desc = "Seems like this one won't hatch." - icon = 'icons/effects/tomatodecal.dmi' - icon_state = "smashed_egg1" + name = "smashed egg" + desc = "Seems like this one won't hatch." + icon = 'icons/effects/tomatodecal.dmi' + icon_state = "smashed_egg1" random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3") - persistent = TRUE - generic_filth = TRUE + persistent = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/pie_smudge //honk - name = "smashed pie" - desc = "It's pie cream from a cream pie." - icon = 'icons/effects/tomatodecal.dmi' - icon_state = "smashed_pie" + name = "smashed pie" + desc = "It's pie cream from a cream pie." + icon = 'icons/effects/tomatodecal.dmi' + icon_state = "smashed_pie" random_icon_states = list("smashed_pie") - persistent = TRUE - generic_filth = TRUE + persistent = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/fruit_smudge - name = "smudge" - desc = "Some kind of fruit smear." - icon = 'icons/effects/blood.dmi' - icon_state = "mfloor1" + name = "smudge" + desc = "Some kind of fruit smear." + icon = 'icons/effects/blood.dmi' + icon_state = "mfloor1" random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") - persistent = TRUE - generic_filth = TRUE + persistent = TRUE + generic_filth = TRUE + +/obj/effect/decal/cleanable/champagne + name = "champagne" + desc = "Someone got a bit too excited." + gender = PLURAL + icon = 'icons/effects/effects.dmi' + icon_state = "fchampagne1" + color = COLOR_BRASS + random_icon_states = list("fchampagne1", "fchampagne2", "fchampagne3", "fchampagne4") diff --git a/code/game/objects/effects/decals/Cleanable/robots.dm b/code/game/objects/effects/decals/Cleanable/robots.dm index 330acc9f3ce..e7fa4729e48 100644 --- a/code/game/objects/effects/decals/Cleanable/robots.dm +++ b/code/game/objects/effects/decals/Cleanable/robots.dm @@ -44,6 +44,10 @@ chemical = /decl/material/liquid/lube cleanable_scent = "industrial lubricant" +/obj/effect/decal/cleanable/blood/oil/Initialize(mapload) + . = ..() + update_icon() + /obj/effect/decal/cleanable/blood/oil/dry() return diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index 6768d4963fa..870df8fbbf1 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -139,7 +139,7 @@ if(track.overlay) track.overlay=null - var/image/I = image(icon, icon_state=state, dir=num2dir(truedir)) + var/image/I = image(icon, icon_state=state, dir=FIRST_DIR(truedir)) I.color = track.basecolor track.fresh=0 diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index 757ed59db52..66319d3ee89 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -1,7 +1,11 @@ /obj/effect/decal/cleanable density = FALSE anchored = TRUE + abstract_type = /obj/effect/decal/cleanable + var/burnable = TRUE + var/sweepable = FALSE + var/weather_sensitive = TRUE var/persistent = FALSE var/generic_filth = FALSE var/age = 0 @@ -33,11 +37,29 @@ animate(src, alpha = 0, time = 5 SECONDS) QDEL_IN(src, 5 SECONDS) + if(weather_sensitive) + SSweather_atoms.weather_atoms += src + /obj/effect/decal/cleanable/Destroy() + if(weather_sensitive) + SSweather_atoms.weather_atoms -= src if(persistent) SSpersistence.forget_value(src, /decl/persistence_handler/filth) . = ..() +/obj/effect/decal/cleanable/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + . = ..() + if(burnable && !QDELETED(src)) + qdel(src) + +/obj/effect/decal/cleanable/process_weather(obj/abstract/weather_system/weather, decl/state/weather/weather_state) + if(!weather_sensitive) + return PROCESS_KILL + if(weather_state.is_liquid) + alpha -= 15 + if(alpha <= 0) + clean(TRUE) + /obj/effect/decal/cleanable/clean(clean_forensics = TRUE) if(clean_forensics) qdel(src) @@ -50,7 +72,7 @@ /obj/effect/decal/cleanable/fluid_act(var/datum/reagents/fluid) SHOULD_CALL_PARENT(FALSE) - if(fluid?.total_volume && !QDELETED(src)) + if(fluid?.total_liquid_volume && !QDELETED(src)) if(reagents?.total_volume) reagents.trans_to(fluid, reagents.total_volume) qdel(src) diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index fcdd720754a..664d7af8c8f 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -1,26 +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 303e5dbbb7b..6f0e3ab5cf2 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 @@ -127,10 +127,8 @@ desc = "The poster comes with its own automatic adhesive mechanism, for easy pinning to any vertical surface." icon = 'icons/obj/items/posters.dmi' icon_state = "rolled_poster" - force = 0 + _base_attack_force = 0 material = /decl/material/solid/organic/paper - ///The name of the medium, excluding any reference to the design - var/base_name = "rolled-up poster" ///The description for the item/medium without any reference to the design. var/base_desc = "The poster comes with its own automatic adhesive mechanism, for easy pinning to any vertical surface." ///Type path to the /decl for the design on this poster. At runtime is changed for a reference to the decl @@ -138,14 +136,15 @@ /obj/item/poster/Initialize(ml, material_key, var/given_poster_type = null) //Init design + base_name ||= name set_design(given_poster_type || poster_design || pick(decls_repository.get_decl_paths_of_subtype(/decl/poster_design))) return ..(ml, material_key) /obj/item/poster/proc/set_design(var/decl/poster_design/_design_path) - if(_design_path == poster_design) - return TRUE if(ispath(_design_path, /decl)) _design_path = GET_DECL(_design_path) + if(_design_path == poster_design) + return TRUE if(!istype(_design_path)) CRASH("Invalid poster type: [log_info_line(_design_path)]") diff --git a/code/game/objects/effects/dirty_floor.dm b/code/game/objects/effects/dirty_floor.dm index 798d07d738c..8c2df8b9f8b 100644 --- a/code/game/objects/effects/dirty_floor.dm +++ b/code/game/objects/effects/dirty_floor.dm @@ -9,10 +9,24 @@ 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. + /obj/effect/decal/cleanable/dirt/Initialize() . = ..() - name = "" verbs.Cut() + update_icon() /obj/effect/decal/cleanable/dirt/on_update_icon() . = ..() diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index b793a5fae03..ef312635529 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -100,14 +100,20 @@ steam.start() -- spawns the effect mouse_opacity = MOUSE_OPACITY_UNCLICKABLE pass_flags = PASS_FLAG_TABLE var/spark_sound = "sparks" + var/lit_light_range = 1 + var/lit_light_power = 0.5 + var/lit_light_color = COLOR_MUZZLE_FLASH /obj/effect/sparks/struck spark_sound = "light_bic" /obj/effect/sparks/Initialize() . = ..() - QDEL_IN(src, 5 SECONDS) + // this is 2 seconds so that it doesn't appear to freeze after its last move, which ends up making it look like timers are broken + // if you change the number of or delay between moves in spread(), this may need to be changed + QDEL_IN(src, 2 SECONDS) playsound(loc, spark_sound, 100, 1) + set_light(lit_light_range, lit_light_power, lit_light_color) if(isturf(loc)) var/turf/T = loc T.spark_act() @@ -436,7 +442,7 @@ steam.start() -- spawns the effect ///////////////////////////////////////////// //////// Attach a steam trail to an object (eg. a reacting beaker) that will follow it -// even if it's carried of thrown. +// even if it's carried or thrown. ///////////////////////////////////////////// /datum/effect/effect/system/trail/steam diff --git a/code/game/objects/effects/fake_fire.dm b/code/game/objects/effects/fake_fire.dm index 783ecd3ac62..5993628c76b 100644 --- a/code/game/objects/effects/fake_fire.dm +++ b/code/game/objects/effects/fake_fire.dm @@ -33,17 +33,25 @@ if(lifetime) QDEL_IN(src,lifetime) +/obj/effect/fake_fire/proc/can_affect_atom(atom/target) + if(target == src) + return FALSE + return target.simulated + +/obj/effect/fake_fire/proc/can_affect_mob(mob/living/victim) + return can_affect_atom(victim) + /obj/effect/fake_fire/Process() if(!loc) qdel(src) return PROCESS_KILL for(var/mob/living/victim in loc) - if(!victim.simulated) + if(!can_affect_mob(victim)) continue victim.FireBurn(firelevel, last_temperature, pressure) loc.fire_act(firelevel, last_temperature, pressure) for(var/atom/burned in loc) - if(!burned.simulated || burned == src) + if(!can_affect_atom(burned)) continue burned.fire_act(firelevel, last_temperature, pressure) @@ -53,4 +61,17 @@ /obj/effect/fake_fire/Destroy() STOP_PROCESSING(SSobj,src) - . = ..() \ No newline at end of file + return ..() + +// A subtype of fake_fire used for spells that shouldn't affect the caster. +/obj/effect/fake_fire/variable/owned + var/mob/living/owner + +/obj/effect/fake_fire/variable/owned/can_affect_atom(atom/target) + if(target == owner) + return FALSE + return ..() + +/obj/effect/fake_fire/variable/owned/Destroy() + owner = null + return ..() \ No newline at end of file diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 55aaddcf1bd..de6f34d85bb 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -5,7 +5,7 @@ icon_state = "portal" density = TRUE anchored = TRUE - var/obj/item/target = null + var/atom/target = null var/dangerous = FALSE var/failchance = 0 diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 4c2a64e69f9..a1a3fd87bd3 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -41,7 +41,7 @@ else visible_message("\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]") - var/damage = W.force / 4.0 + var/damage = W.get_attack_force(user) / 4 if(W.edge) damage += 5 @@ -55,6 +55,7 @@ current_health -= damage healthcheck() + return TRUE /obj/effect/spider/bullet_act(var/obj/item/projectile/Proj) ..() @@ -194,7 +195,7 @@ . = ..() /obj/effect/spider/spiderling/attackby(var/obj/item/W, var/mob/user) - ..() + . = ..() if(current_health > 0) disturbed() diff --git a/code/game/objects/effects/temporaray.dm b/code/game/objects/effects/temporary.dm similarity index 100% rename from code/game/objects/effects/temporaray.dm rename to code/game/objects/effects/temporary.dm diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index e99d8b05f67..638b704a188 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -64,7 +64,7 @@ var/x0 = epicenter.x var/y0 = epicenter.y var/z0 = epicenter.z - for(var/turf/T in RANGE_TURFS(epicenter, max_range)) + for(var/turf/T as anything in RANGE_TURFS(epicenter, max_range)) var/dist = sqrt((T.x - x0)**2 + (T.y - y0)**2) if(dist < devastation_range) dist = 1 @@ -85,7 +85,7 @@ var/atom/movable/AM = atom_movable if(AM && AM.simulated && !T.protects_atom(AM)) AM.explosion_act(dist) - if(!QDELETED(AM) && !AM.anchored) + if(!QDELETED(AM) && !AM.anchored && isturf(AM.loc)) addtimer(CALLBACK(AM, TYPE_PROC_REF(/atom/movable, throw_at), throw_target, throw_dist, throw_dist), 0) var/took = (REALTIMEOFDAY-start_time)/10 @@ -93,10 +93,10 @@ to_world_log("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") return 1 -#define EXPLFX_BOTH 3 -#define EXPLFX_SOUND 2 -#define EXPLFX_SHAKE 1 -#define EXPLFX_NONE 0 +#define EXPLFX_NONE 0 +#define EXPLFX_SOUND BITFLAG(0) +#define EXPLFX_SHAKE BITFLAG(1) +#define EXPLFX_BOTH (EXPLFX_SOUND|EXPLFX_SHAKE) // All the vars used on the turf should be on unsimulated turfs too, we just don't care about those generally. #define SEARCH_DIR(dir) \ @@ -206,9 +206,10 @@ if (T.type == /turf/space) // Equality is faster than istype. reception = EXPLFX_NONE - for (var/turf/THING in RANGE_TURFS(M, 1)) - reception |= EXPLFX_SHAKE - break + for (var/turf/THING as anything in RANGE_TURFS(M, 1)) + if(THING.simulated) + reception |= EXPLFX_SHAKE + break if (!reception) CHECK_TICK diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm index 325f81299ce..5827dbba478 100644 --- a/code/game/objects/item_mob_overlay.dm +++ b/code/game/objects/item_mob_overlay.dm @@ -145,19 +145,32 @@ var/global/list/icon_state_cache = list() if(!draw_on_mob_when_equipped && !(slot in global.all_hand_slots)) return overlay - var/decl/bodytype/root_bodytype = user_mob?.get_bodytype() - if(root_bodytype && root_bodytype.bodytype_category != bodytype) - var/list/overlays_to_offset = overlay.overlays - overlay = root_bodytype.get_offset_overlay_image(user_mob, overlay.icon, overlay.icon_state, color, (bodypart || slot)) - for(var/thing in overlays_to_offset) - var/image/I = thing // Technically an appearance but don't think we can cast to those - var/image/adjusted_overlay = root_bodytype.get_offset_overlay_image(user_mob, I.icon, I.icon_state, I.color, (bodypart || slot)) - adjusted_overlay.appearance_flags = I.appearance_flags - adjusted_overlay.plane = I.plane - adjusted_overlay.layer = I.layer - overlay.overlays += adjusted_overlay + if(overlay) + + if(is_held_twohanded()) + var/wielded_state = "[overlay.icon_state]-wielded" + if(check_state_in_icon(wielded_state, overlay.icon)) + overlay.icon_state = wielded_state + apply_additional_mob_overlays(user_mob, bodytype, overlay, slot, bodypart, use_fallback_if_icon_missing) + + var/decl/bodytype/root_bodytype = user_mob?.get_bodytype() + if(root_bodytype && root_bodytype.bodytype_category != bodytype) + var/list/overlays_to_offset = overlay.overlays + overlay = root_bodytype.get_offset_overlay_image(user_mob, overlay.icon, overlay.icon_state, color, (bodypart || slot)) + if(overlay) + for(var/thing in overlays_to_offset) + var/image/I = thing // Technically an appearance but don't think we can cast to those + var/image/adjusted_overlay = root_bodytype.get_offset_overlay_image(user_mob, I.icon, I.icon_state, I.color, (bodypart || slot)) + adjusted_overlay.appearance_flags = I.appearance_flags + adjusted_overlay.plane = I.plane + adjusted_overlay.layer = I.layer + overlay.overlays += adjusted_overlay + return overlay +/obj/item/proc/apply_additional_mob_overlays(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) + return + //Special proc belts use to compose their icon /obj/item/proc/get_on_belt_overlay() if(check_state_in_icon("on_belt", icon)) diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 247d1f1c45b..fc7e6821e1c 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -4,6 +4,7 @@ mouse_drag_pointer = MOUSE_ACTIVE_POINTER pass_flags = PASS_FLAG_TABLE abstract_type = /obj/item + temperature_sensitive = TRUE /// Set to false to skip state checking and never draw an icon on the mob (except when held) var/draw_on_mob_when_equipped = TRUE @@ -20,7 +21,6 @@ var/origin_tech //Used by R&D to determine what research bonuses it grants. var/list/attack_verb = list("hit") //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]" var/lock_picking_level = 0 //used to determine whether something can pick a lock, and how well. - var/force = 0 var/attack_cooldown = DEFAULT_WEAPON_COOLDOWN var/melee_accuracy_bonus = 0 @@ -50,7 +50,7 @@ var/gas_transfer_coefficient = 1 // for leaking gas from turf to mask and vice-versa (for masks right now, but at some point, i'd like to include space helmets) var/permeability_coefficient = 1 // for chemicals/diseases var/siemens_coefficient = 1 // for electrical admittance/conductance (electrocution checks and shit) - var/slowdown_general = 0 // How much clothing is slowing you down. Negative values speeds you up. This is a genera##l slowdown, no matter equipment slot. + var/slowdown_general = 0 // How much clothing is slowing you down. Negative values speeds you up. This is a general slowdown, no matter equipment slot. var/slowdown_per_slot // How much clothing is slowing you down. This is an associative list: item slot - slowdown var/slowdown_accessory // How much an accessory will slow you down when attached to a worn article of clothing. var/canremove = 1 //Mostly for Ninja code at this point but basically will not allow the item to be removed if set to 0. /N @@ -64,6 +64,7 @@ var/zoom = 0 //1 if item is actively being used to zoom. For scoped guns and binoculars. var/base_parry_chance // Will allow weapon to parry melee attacks if non-zero + var/wielded_parry_bonus = 15 var/use_alt_layer = FALSE // Use the slot's alternative layer when rendering on a mob @@ -73,9 +74,6 @@ var/decl/material/material // Reference to material decl. If set to a string corresponding to a material ID, will init the item with that material. ///Will apply the flagged modifications to the object var/material_alteration = MAT_FLAG_ALTERATION_NONE - var/max_force // any damage above this is added to armor penetration value. If unset, autogenerated based on w_class - var/material_force_multiplier = 0.1 // Multiplier to material's generic damage value for this specific type of weapon - var/thrown_material_force_multiplier = 0.1 // As above, but for throwing the weapon. var/anomaly_shielding // 0..1 value of how well it shields against xenoarch anomalies ///Sound used when equipping the item into a valid slot @@ -95,10 +93,28 @@ var/replaced_in_loadout = TRUE var/paint_color - var/paint_verb = "painted" + var/paint_verb /// What dexterity is required to attack with this item? - var/needs_attack_dexterity = DEXTERITY_WIELD_ITEM + var/needs_attack_dexterity = DEXTERITY_WIELD_ITEM + var/needs_interaction_dexterity = DEXTERITY_HOLD_ITEM + + /// Vars relating to wielding the item with two or more hands. + var/can_be_twohanded = FALSE + var/minimum_size_to_twohand = MOB_SIZE_MEDIUM + var/maximum_size_to_twohand = MOB_SIZE_LARGE + var/last_twohanded_state = FALSE + + var/wieldsound = 'sound/foley/scrape1.ogg' + var/unwieldsound = 'sound/foley/tooldrop1.ogg' + + var/base_name + + /// Can this object leak into water sources? + var/watertight = FALSE + + /// 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 /obj/item/get_color() if(paint_color) @@ -112,13 +128,17 @@ new_color = null if(paint_color != new_color) paint_color = new_color + . = TRUE + refresh_color() + +/obj/item/refresh_color() if(paint_color) color = paint_color else if(material && (material_alteration & MAT_FLAG_ALTERATION_COLOR)) color = material.color else - color = new_color - return FALSE + color = null + /obj/item/proc/can_contaminate() return !(obj_flags & ITEM_FLAG_NO_CONTAMINATION) @@ -141,12 +161,15 @@ /obj/item/Initialize(var/ml, var/material_key) + base_name ||= name + if(isnull(current_health)) current_health = max_health //Make sure to propagate max_health to health var before material setup, for consistency if(!ispath(material_key, /decl/material)) material_key = material if(material_key) set_material(material_key) + paint_verb ||= "painted" // fallback for the case of no material . = ..() @@ -222,33 +245,42 @@ clone.update_held_icon() return clone -//Checks if the item is being held by a mob, and if so, updates the held icons /obj/item/proc/update_twohanding() + + var/next_wielded_state = is_held_twohanded() + if(last_twohanded_state == next_wielded_state) + return + + if(next_wielded_state) + if(wieldsound) + playsound(src, wieldsound, 50) + else + if(unwieldsound) + playsound(src, unwieldsound, 50) + + update_icon() update_held_icon() + update_attack_force() + last_twohanded_state = next_wielded_state +//Checks if the item is being held by a mob, and if so, updates the held icons /obj/item/proc/update_held_icon() if(ismob(src.loc)) var/mob/M = src.loc M.update_inhand_overlays() -/obj/item/proc/is_held_twohanded(mob/living/M) - if(!M) - M = loc - if(!istype(M)) - return +/obj/item/proc/is_held_twohanded(mob/living/wielder) + if(!can_be_twohanded) + return FALSE if(istype(loc, /obj/item/rig_module) || istype(loc, /obj/item/rig)) return TRUE - if(!(src in M.get_held_items())) + if(!wielder) + wielder = loc + if(!istype(wielder)) return FALSE - - //would check is_broken() and is_malfunctioning() here too but is_malfunctioning() - //is probabilistic so we can't do that and it would be unfair to just check one. - if(ishuman(M)) - var/mob/living/human/H = M - var/obj/item/organ/external/hand = GET_EXTERNAL_ORGAN(H, M.get_empty_hand_slot()) - if(istype(hand) && hand.is_usable()) - return TRUE - return FALSE + if(!(src in wielder.get_held_items())) + return FALSE + return wielder.can_twohand_item(src) /obj/item/examine(mob/user, distance) @@ -260,7 +292,8 @@ desc_comp += "[desc_damage]" if(paint_color) - desc_comp += "\The [src] has been [paint_verb]." + var/decl/pronouns/obj_pronouns = get_pronouns() // so we can do 'have' for plural objects like sheets + desc_comp += "\The [src] [obj_pronouns.has] been [paint_verb]." var/added_header = FALSE if(user?.get_preference_value(/datum/client_preference/inquisitive_examine) == PREF_ON) @@ -268,11 +301,7 @@ var/list/available_recipes = list() for(var/decl/crafting_stage/initial_stage in SSfabrication.find_crafting_recipes(type)) if(initial_stage.can_begin_with(src) && ispath(initial_stage.completion_trigger_type)) - var/atom/movable/prop = initial_stage.completion_trigger_type - if(initial_stage.stack_consume_amount > 1) - available_recipes[initial_stage] = "[initial_stage.stack_consume_amount] [initial(prop.name)]\s" - else - available_recipes[initial_stage] = "\a [initial(prop.name)]" + available_recipes[initial_stage] = initial_stage.generate_completion_string() if(length(available_recipes)) @@ -521,19 +550,19 @@ return TRUE return FALSE -/obj/item/proc/user_can_wield(mob/user, silent = 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/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_wield(user, silent = TRUE) + var/can_wield = user_can_attack_with(user, silent = TRUE) if(can_wield && try_slapcrafting(used_item, user)) return TRUE - if(used_item.storage?.use_to_pickup) + if(used_item.storage?.use_to_pickup && isturf(src.loc)) //Mode is set to collect all items - if(used_item.storage.collection_mode && isturf(loc)) + if(used_item.storage.collection_mode) used_item.storage.gather_all(loc, user) return TRUE if(used_item.storage.can_be_inserted(src, user)) @@ -549,11 +578,6 @@ return ..() -/obj/item/attack_ghost(mob/user) - var/mob/observer/ghost/G = user - if(G.client?.holder || G.antagHUD) - storage?.show_to(user) - /obj/item/proc/talk_into(mob/living/M, message, message_mode, var/verb = "says", var/decl/language/speaking = null) return @@ -696,7 +720,6 @@ return usr.UnarmedAttack(src, usr.Adjacent(src)) - //This proc is executed when someone clicks the on-screen UI button. To make the UI button show, set the 'icon_action_button' to the icon_state of the image of the button in screen1_action.dmi //The default action is attack_self(). //Checks before we get to here are: mob is alive, mob is not restrained, paralyzed, asleep, resting, laying, item is on the mob. @@ -728,15 +751,18 @@ if(user) if(base_parry_chance || user.skill_check(SKILL_COMBAT, SKILL_ADEPT)) . += 10 * (user.get_skill_value(SKILL_COMBAT) - SKILL_BASIC) + if(is_held_twohanded()) + . += wielded_parry_bonus /obj/item/proc/on_disarm_attempt(mob/target, mob/living/attacker) + var/force = get_attack_force(attacker) if(force < 1) return 0 if(!istype(attacker)) return 0 - var/decl/pronouns/G = attacker.get_pronouns() + var/decl/pronouns/pronouns = attacker.get_pronouns() attacker.apply_damage(force, atom_damage_type, attacker.get_active_held_item_slot(), used_weapon = src) - attacker.visible_message(SPAN_DANGER("\The [attacker] hurts [G.his] hand on \the [src]!")) + attacker.visible_message(SPAN_DANGER("\The [attacker] hurts [pronouns.his] hand on \the [src]!")) playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) playsound(target, hitsound, 50, 1, -1) return 1 @@ -758,7 +784,7 @@ if(!blood_data && ishuman(M)) var/mob/living/human/H = M blood_data = REAGENT_DATA(H.vessel, /decl/material/liquid/blood) - var/sample_dna = LAZYACCESS(blood_data, "blood_DNA") + var/sample_dna = LAZYACCESS(blood_data, DATA_BLOOD_DNA) if(sample_dna) var/datum/extension/forensic_evidence/forensics = get_or_create_extension(src, /datum/extension/forensic_evidence) forensics.add_data(/datum/forensics/blood_dna, sample_dna) @@ -770,7 +796,7 @@ return TRUE var/global/list/_blood_overlay_cache = list() -var/global/list/_item_blood_mask = icon('icons/effects/blood.dmi', "itemblood") +var/global/icon/_item_blood_mask = icon('icons/effects/blood.dmi', "itemblood") /obj/item/proc/generate_blood_overlay(force = FALSE) if(blood_overlay && !force) return @@ -932,6 +958,11 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. if(citem.item_state) set_icon_state(citem.item_state) +/obj/item/clothing/inherit_custom_item_data(var/datum/custom_item/citem) + . = ..() + base_clothing_icon = icon + base_clothing_state = icon_state + /obj/item/proc/is_special_cutting_tool(var/high_power) return FALSE @@ -957,7 +988,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. descriptors += "sharp" if(edge) descriptors += "edged" - if(force >= 10 && !sharp && !edge) + if(get_attack_force() >= 10 && !sharp && !edge) descriptors += "heavy" if(material) descriptors += "made of [material.solid_name]" @@ -1003,17 +1034,40 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. if(item_flags & ITEM_FLAG_IS_BELT) LAZYADD(., slot_belt_str) + // Where are we usually worn? + var/default_slot = get_fallback_slot() + if(default_slot) + LAZYDISTINCTADD(., default_slot) + // Uniforms can show or hide ID. + if(default_slot == slot_w_uniform_str) + LAZYDISTINCTADD(., slot_wear_id_str) + + // Currently this proc is used for clothing updates, so we + // need to care what slot we are being worn in, if any. + if(ismob(loc)) + var/mob/wearer = loc + var/equipped_slot = wearer.get_equipped_slot_for_item(src) + if(equipped_slot) + LAZYDISTINCTADD(., equipped_slot) + // Updates the icons of the mob wearing the clothing item, if any. -/obj/item/proc/update_clothing_icon() - var/mob/wearer = loc - if(!istype(wearer)) - return FALSE - var/equip_slots = get_associated_equipment_slots() - if(!islist(equip_slots)) +/obj/item/proc/update_clothing_icon(do_update_icon = TRUE) + + // Accessories should pass this back to their holder. + if(isitem(loc)) + var/obj/item/holder = loc + return holder.update_clothing_icon(do_update_icon) + + // If we're not on a mob, we do not care. + if(!ismob(loc)) return FALSE - for(var/slot in equip_slots) - wearer.update_equipment_overlay(slot, FALSE) - wearer.update_icon() + + // We refresh our equipped slot and any associated slots that might depend on the state of this slot. + var/mob/wearer = loc + for(var/equipped_slot in get_associated_equipment_slots()) + wearer.update_equipment_overlay(equipped_slot, FALSE) + if(do_update_icon) + wearer.update_icon() return TRUE /obj/item/proc/reconsider_client_screen_presence(var/client/client, var/slot) @@ -1048,7 +1102,9 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. // Supplied during loadout gear tweaking. /obj/item/proc/set_custom_name(var/new_name) - SetName(new_name) + base_name = new_name + material_alteration &= ~MAT_FLAG_ALTERATION_NAME + update_name() // Supplied during loadout gear tweaking. /obj/item/proc/set_custom_desc(var/new_desc) @@ -1073,7 +1129,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. try_burn_wearer(user, slot, 1) /obj/item/can_embed() - . = !anchored && !is_robot_module(src) + . = !anchored && (!ismob(loc) || canremove) && (!loc || isturf(loc) || ismob(loc)) && !is_robot_module(src) if(. && isliving(loc)) var/mob/living/holder = loc // Terrible check for if the mob is being driven by an AI or not. @@ -1115,7 +1171,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. set name = "Switch Gathering Method" set category = "Object" if(!storage) - verbs = /obj/item/proc/toggle_gathering_mode + verbs -= /obj/item/proc/toggle_gathering_mode return storage.collection_mode = !storage.collection_mode switch (storage.collection_mode) @@ -1128,7 +1184,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. set name = "Empty Contents" set category = "Object" if(!storage) - verbs = /obj/item/proc/quick_empty + verbs -= /obj/item/proc/quick_empty return if((!ishuman(usr) && (loc != usr)) || usr.stat || usr.restrained()) return @@ -1149,3 +1205,52 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /obj/item/proc/get_equipment_tint() return TINT_NONE + +/obj/item/proc/get_preview_screen_locs() + return + +/obj/item/proc/get_stance_support_value() + return 0 + +/obj/item/is_watertight() + return watertight || ..() + +// TODO: merge beakers etc down into this proc. +/// @params: +/// - state_prefix as text: if non-null, this string is prepended to the reagent overlay state, typically world/inventory/etc +/// @returns: +/// - reagent_overlay as /image|null - the overlay image representing the reagents in this object +/obj/item/proc/get_reagents_overlay(state_prefix) + if(reagents?.total_volume <= 0) + return + var/decl/material/primary_reagent = reagents.get_primary_reagent_decl() + if(!primary_reagent) + return + var/reagents_state + if(primary_reagent.reagent_overlay_base) + reagents_state = primary_reagent.reagent_overlay_base + else + reagents_state = "reagent_base" + if(state_prefix) + reagents_state = "[state_prefix]_[reagents_state]" // prepend world, inventory, or slot + if(!reagents_state || !check_state_in_icon(reagents_state, icon)) + return + var/image/reagent_overlay = overlay_image(icon, reagents_state, reagents.get_color(), RESET_COLOR | RESET_ALPHA) + for(var/reagent_type in reagents.reagent_volumes) + var/decl/material/reagent = GET_DECL(reagent_type) + if(!reagent.reagent_overlay) + continue + var/modified_reagent_overlay = state_prefix ? "[state_prefix]_[reagent.reagent_overlay]" : reagent.reagent_overlay + if(!check_state_in_icon(modified_reagent_overlay, icon)) + continue + reagent_overlay.overlays += overlay_image(icon, modified_reagent_overlay, reagent.get_reagent_overlay_color(reagents), RESET_COLOR | RESET_ALPHA) + return reagent_overlay + +/obj/item/on_reagent_change() + . = ..() + // You can't put liquids in clay/sand/dirt vessels, sorry. + if(reagents?.total_liquid_volume > 0 && material && material.hardness <= MAT_VALUE_MALLEABLE && !QDELETED(src)) + visible_message(SPAN_DANGER("\The [src] falls apart!")) + squash_item() + if(!QDELETED(src)) + physically_destroyed() diff --git a/code/game/objects/items/_item_damage.dm b/code/game/objects/items/_item_damage.dm index 71afac1af8d..6d8803c33d3 100644 --- a/code/game/objects/items/_item_damage.dm +++ b/code/game/objects/items/_item_damage.dm @@ -48,19 +48,27 @@ . = ..() take_damage(explosion_severity_damage(severity), BURN, DAM_EXPLODE | DAM_DISPERSED, "explosion") +/obj/item/bash(obj/item/weapon, mob/user) + . = ..() + var/force = weapon.get_attack_force(user) + if(force >= 3 && .) + user.setClickCooldown(weapon.attack_cooldown + weapon.w_class) + take_damage(force, weapon.atom_damage_type) + /obj/item/proc/explosion_severity_damage(var/severity) var/mult = explosion_severity_damage_multiplier() return (mult * (4 - severity)) + (severity != 1? rand(-(mult / severity), (mult / severity)) : 0 ) /obj/item/proc/explosion_severity_damage_multiplier() - return CEILING(get_max_health() / 3) + return ceil(get_max_health() / 3) /obj/item/is_burnable() return simulated /obj/item/proc/get_volume_by_throwforce_and_or_w_class() - if(throwforce && w_class) - return clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100 + var/thrown_force = get_thrown_attack_force() + if(thrown_force && w_class) + return clamp((thrown_force + w_class) * 5, 30, 100)// Add the item's thrown force to its weight class and multiply by 5, then clamp the value between 30 and 100 else if(w_class) return clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100 else @@ -70,7 +78,7 @@ . = ..() if(isliving(hit_atom)) //Living mobs handle hit sounds differently. var/volume = get_volume_by_throwforce_and_or_w_class() - if (throwforce > 0) + if (get_thrown_attack_force() > 0) if(hitsound) playsound(hit_atom, hitsound, volume, TRUE, -1) else diff --git a/code/game/objects/items/_item_drying.dm b/code/game/objects/items/_item_drying.dm index e8a15ed1225..f9575e2dc96 100644 --- a/code/game/objects/items/_item_drying.dm +++ b/code/game/objects/items/_item_drying.dm @@ -71,16 +71,27 @@ /obj/item/proc/get_drying_state(var/obj/rack) return "generic" +/obj/item/proc/should_take_heat_damage(datum/gas_mixture/air, exposed_temperature) + if(!can_take_damage()) + return FALSE + if(!length(matter)) + return FALSE + for(var/mat in matter) + var/decl/material/material = GET_DECL(mat) + // We should burn if we're above the temperature damage threshold. + if(!isnull(material.temperature_damage_threshold) && exposed_temperature >= material.temperature_damage_threshold) + return TRUE + return FALSE + /obj/item/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) ..() - // Take fire damage if appropriate. - if(get_max_health() != ITEM_HEALTH_NO_DAMAGE && length(matter)) - for(var/mat in matter) - var/decl/material/material = GET_DECL(mat) - if(!isnull(material.temperature_damage_threshold) && exposed_temperature >= material.temperature_damage_threshold) - take_damage(rand(3,5), BURN) - break + var/should_burn = should_take_heat_damage(air, exposed_temperature) if(exposed_temperature >= drying_threshold_temperature) dry_out(drying_power = rand(2, 4), fire_exposed = TRUE, silent = TRUE) + should_burn &&= !is_dryable() // no fire damage if we're drying + + // Take fire damage if appropriate. + if(should_burn) + take_damage(rand(3,5), BURN) diff --git a/code/game/objects/items/_item_edibility.dm b/code/game/objects/items/_item_edibility.dm index d5d0cd948ec..c93934b6f52 100644 --- a/code/game/objects/items/_item_edibility.dm +++ b/code/game/objects/items/_item_edibility.dm @@ -2,3 +2,42 @@ . = ..() if(. == EATEN_SUCCESS && !QDELETED(src)) add_trace_DNA(target) + +// Used to get a piece of food from an item. +/obj/item/proc/seperate_food_chunk(obj/item/utensil/utensil, mob/user) + var/utensil_food_type = get_utensil_food_type() + if(!istype(utensil) || !utensil_food_type) + return + var/remove_amt = min(reagents?.total_volume, get_food_default_transfer_amount(user)) + if(remove_amt) + + // Create a dummy copy of the target food item. + // This ensures we keep all food behavior, strings, sounds, etc. + utensil.loaded_food = new utensil_food_type(utensil, material?.type, TRUE) + QDEL_NULL(utensil.loaded_food.trash) + QDEL_NULL(utensil.loaded_food.plate) + utensil.loaded_food.color = color + utensil.loaded_food.filling_color = get_food_filling_color() + utensil.loaded_food.SetName("\proper some [utensil.loaded_food.name]") + + // Pass over a portion of our reagents. + utensil.loaded_food.reagents.clear_reagents() + reagents.trans_to(utensil.loaded_food, remove_amt) + handle_chunk_separated() + if(!reagents.total_volume) + handle_consumed() + utensil.update_icon() + + else // This shouldn't happen, but who knows. + to_chat(user, SPAN_WARNING("None of \the [src] is left!")) + handle_consumed() + return TRUE + +/obj/item/proc/get_food_filling_color() + return color + +/obj/item/proc/get_utensil_food_type() + return + +/obj/item/proc/handle_chunk_separated() + return diff --git a/code/game/objects/items/_item_force.dm b/code/game/objects/items/_item_force.dm new file mode 100644 index 00000000000..3ff63de0cc7 --- /dev/null +++ b/code/game/objects/items/_item_force.dm @@ -0,0 +1,165 @@ +/obj/item + /// Tracking var for base attack value with material modifiers, to save recalculating 200 times. + VAR_PRIVATE/_cached_attack_force + /// Base force value; generally, the damage output if used one-handed by a medium mob (human) and made of steel. + VAR_PROTECTED/_base_attack_force = 5 + /// Multiplier to the base thrown force based on the material per above. + VAR_PROTECTED/_thrown_force_multiplier = 0.3 + /// Multiplier to total base damage from weapon and material if the weapon is wieldable and held in two hands. + VAR_PROTECTED/_wielded_force_multiplier = 1.25 + /// How much of our overall damage is influenced by material weight? + VAR_PROTECTED/_weight_force_factor = 0.25 + /// How much of our overall damage is influenced by material hardness? + VAR_PROTECTED/_hardness_force_factor = 0.25 + +/obj/item/proc/get_max_weapon_force() + . = get_attack_force() + if(can_be_twohanded) + . = round(. * _wielded_force_multiplier) + +/obj/item/proc/get_attack_force(mob/living/user) + if(_base_attack_force <= 0 || (item_flags & ITEM_FLAG_NO_BLUDGEON)) + return 0 + if(isnull(_cached_attack_force)) + update_attack_force() + if(_cached_attack_force <= 0) + return 0 + return istype(user) ? user.modify_attack_force(src, _cached_attack_force, _wielded_force_multiplier) : _cached_attack_force + +// Existing hitby() code expects mobs, structures and machines to be thrown, it seems. +/atom/movable/proc/get_thrown_attack_force() + return get_object_size() + +/obj/item/get_thrown_attack_force() + return round(get_attack_force() * _thrown_force_multiplier) + +/obj/item/proc/get_base_attack_force() + return _base_attack_force + +/obj/item/proc/get_initial_base_attack_force() + return initial(_base_attack_force) + +/obj/item/proc/set_base_attack_force(new_force) + _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. + _cached_attack_force = get_base_attack_force() + if(_cached_attack_force <= 0 || !istype(material)) + _cached_attack_force = 0 + return _cached_attack_force + + // Check if this material is hard enough to hold an edge. + if(!material.can_hold_edge()) + set_edge(FALSE) + else if(!edge) + set_edge(initial(edge)) + + // Check if this material can hold a point. + if(!material.can_hold_sharpness()) + set_sharp(FALSE) + else if(!sharp) + set_sharp(initial(sharp)) + + // Work out where we're going to cap our calculated force. + // Any additional force resulting from hardness or weight turn into armour penetration. + var/accumulated_force = _cached_attack_force + var/weight_force_component = (_cached_attack_force * _weight_force_factor) + var/hardness_force_component = (_cached_attack_force * _hardness_force_factor) + var/expected_material_mod = (weight_force_component+hardness_force_component)/2 + var/expected_min_force = _cached_attack_force - expected_material_mod + var/expected_max_force = _cached_attack_force + expected_material_mod + + // Heavier weapons are generally going to be more effective, regardless of edge or sharpness. + // We don't factor in w_class or matter at this point, because we are assuming the base attack + // force was set already taking into account how effective this weapon should be. + var/relative_weight = (material.weight / MAT_VALUE_VERY_HEAVY) + if(obj_flags & OBJ_FLAG_HOLLOW) + relative_weight *= HOLLOW_OBJECT_MATTER_MULTIPLIER + accumulated_force += (weight_force_component * relative_weight) - (weight_force_component/2) + + // Apply hardness in the same way. + accumulated_force += (hardness_force_component * (material.hardness / MAT_VALUE_VERY_HARD)) - (hardness_force_component/2) + + // Round off our calculated attack force and turn any overflow into armour penetration. + _cached_attack_force = round(clamp(accumulated_force, expected_min_force, expected_max_force)) + + // Update our armor penetration. + armor_penetration = initial(armor_penetration) + if(accumulated_force > expected_max_force) + armor_penetration = armor_penetration+(accumulated_force-expected_max_force) + armor_penetration += 2 * max(0, material.brute_armor - 2) + armor_penetration = min(100, round(armor_penetration)) + + return _cached_attack_force + +// TODO: consider strength, athletics, mob size +/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) + +// Debug proc - leaving in for future work. Linter hates protected var access so leave commented. +/* +/client/verb/debug_dump_weapon_values() + set name = "Dump Weapon Values" + set category = "Debug" + set src = usr + + var/list/rows = list() + rows += jointext(list( + "Name", + "Type", + "Base Force", + "Calculated Force", + "Thrown Force", + "Min Force", + "Max Force", + "Wield force", + "Armour Pen", + "Sharp/Edge" + ), "|") + + for(var/thing in subtypesof(/obj/item)) + + var/obj/item/item = thing + if(!TYPE_IS_SPAWNABLE(item)) + continue + + item = new item + + var/attk_force = item.get_attack_force() + var/expected_material_mod = ((attk_force * item._weight_force_factor) + (attk_force * item._hardness_force_factor))/2 + + rows += jointext(list( + item.name, + item.type, + item.get_base_attack_force(), + attk_force, + item.get_thrown_attack_force(), + (attk_force - expected_material_mod), + (attk_force + expected_material_mod), + (attk_force * item._wielded_force_multiplier), + item.armor_penetration, + (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")) + to_chat(usr, "Done.") +*/ diff --git a/code/game/objects/items/_item_materials.dm b/code/game/objects/items/_item_materials.dm index ac8d20f8386..1a80c8c3bd5 100644 --- a/code/game/objects/items/_item_materials.dm +++ b/code/game/objects/items/_item_materials.dm @@ -12,7 +12,7 @@ /obj/item/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) . = ..() - if(material && (material.is_brittle() || target.get_blocked_ratio(hit_zone, BRUTE, damage_flags(), armor_penetration, force) * 100 >= material.hardness/5)) + if(material && (material.is_brittle() || target.get_blocked_ratio(hit_zone, BRUTE, damage_flags(), armor_penetration, get_attack_force(user)) * 100 >= material.hardness/5)) apply_wear() /obj/item/on_parry(mob/user, damage_source, mob/attacker) @@ -47,14 +47,15 @@ /obj/item/proc/shatter(var/consumed) var/turf/T = get_turf(src) - T?.visible_message(SPAN_DANGER("\The [src] [material ? material.destruction_desc : "shatters"]!")) - playsound(src, "shatter", 70, 1) + T?.visible_message(SPAN_DANGER("\The [src] [material?.destruction_desc || "shatters"]!")) + playsound(src, material?.destruction_sound || "shatter", 70, 1) if(!consumed && material && w_class > ITEM_SIZE_SMALL && T) material.place_shards(T) qdel(src) /obj/item/get_material() - . = material + RETURN_TYPE(/decl/material) + return material // TODO: Refactor more code to use this where necessary, and then make this use // some sort of generalized system for hitting with different parts of an item @@ -66,37 +67,12 @@ /obj/item/proc/get_striking_material(mob/user, atom/target) return get_material() -/obj/item/proc/update_force() - var/new_force - if(!max_force) - max_force = 5 * min(w_class, ITEM_SIZE_GARGANTUAN) - if(material) - if(edge || sharp) - new_force = material.get_edge_damage() - else - new_force = material.get_blunt_damage() - if(obj_flags & OBJ_FLAG_HOLLOW) - new_force *= HOLLOW_OBJECT_MATTER_MULTIPLIER - - new_force = round(new_force*material_force_multiplier) - force = min(new_force, max_force) - - if(new_force > max_force) - armor_penetration = initial(armor_penetration) + new_force - max_force - - attack_cooldown = initial(attack_cooldown) - if(material) - armor_penetration += 2*max(0, material.brute_armor - 2) - throwforce = material.get_blunt_damage() * thrown_material_force_multiplier - if(obj_flags & OBJ_FLAG_HOLLOW) - throwforce *= HOLLOW_OBJECT_MATTER_MULTIPLIER - throwforce = round(throwforce) - attack_cooldown += material.get_attack_cooldown() - /obj/item/proc/set_material(var/new_material) if(new_material) material = GET_DECL(new_material) + attack_cooldown = initial(attack_cooldown) if(istype(material)) + attack_cooldown += material.get_attack_cooldown() //Only set the current_health if health is null. Some things define their own health value. if(isnull(max_health)) max_health = round(material_health_multiplier * material.integrity, 0.01) @@ -113,9 +89,10 @@ obj_flags |= OBJ_FLAG_CONDUCTIBLE else obj_flags &= (~OBJ_FLAG_CONDUCTIBLE) - update_force() - if(material_alteration & MAT_FLAG_ALTERATION_NAME) - SetName("[material.solid_name] [initial(name)]") + if(isnull(initial(paint_verb))) + paint_verb = material.paint_verb + update_attack_force() + update_name() if(material_armor_multiplier) armor = material.get_armor(material_armor_multiplier) armor_degradation_speed = material.armor_degradation_speed @@ -123,8 +100,15 @@ set_extension(src, armor_type, armor, armor_degradation_speed) else remove_extension(src, armor_type) + queue_icon_update() +/obj/item/proc/update_name() + if(istype(material) && (material_alteration & MAT_FLAG_ALTERATION_NAME)) + SetName("[material.adjective_name] [base_name || initial(name)]") + else + SetName(base_name || initial(name)) + /obj/item/get_matter_amount_modifier() . = ..() if(obj_flags & OBJ_FLAG_HOLLOW) diff --git a/code/game/objects/items/_item_melting.dm b/code/game/objects/items/_item_melting.dm index d835d7d1c7d..318d8f28823 100644 --- a/code/game/objects/items/_item_melting.dm +++ b/code/game/objects/items/_item_melting.dm @@ -16,16 +16,22 @@ try_burn_wearer(holder, holder.get_equipped_slot_for_item(src)) // Temp gate until generalized temperature-based melting works properly. - if(istype(loc, /obj/item/chems/crucible)) - // Check if this is meltable at all. - var/list/meltable_materials - for(var/mat in matter) - var/decl/material/melt_material = GET_DECL(mat) - if(!isnull(melt_material.melting_point) && temperature >= melt_material.melting_point) - LAZYDISTINCTADD(meltable_materials, melt_material) - if(length(meltable_materials)) - . = null // Don't return PROCESS_KILL here. - handle_melting(meltable_materials) + var/static/list/_melting_containers = list( + /obj/item/chems/crucible, + /obj/item/organ/internal/stomach + ) + if(!is_type_in_list(loc, _melting_containers)) + return + + // Check if this is meltable at all. + var/list/meltable_materials + for(var/mat in matter) + var/decl/material/melt_material = GET_DECL(mat) + if(!isnull(melt_material.melting_point) && temperature >= melt_material.melting_point) + LAZYDISTINCTADD(meltable_materials, melt_material) + if(length(meltable_materials)) + . = null // Don't return PROCESS_KILL here. + handle_melting(meltable_materials) /obj/item/place_melted_product(list/meltable_materials) diff --git a/code/game/objects/items/_item_reagents.dm b/code/game/objects/items/_item_reagents.dm index d3220b1a5bc..c9237fe4859 100644 --- a/code/game/objects/items/_item_reagents.dm +++ b/code/game/objects/items/_item_reagents.dm @@ -1,34 +1,34 @@ -/obj/item/proc/standard_dispenser_refill(var/mob/user, var/obj/structure/reagent_dispensers/target) // This goes into afterattack - if(!istype(target) || (target.atom_flags & ATOM_FLAG_OPEN_CONTAINER)) - return 0 +/obj/item/proc/standard_dispenser_refill(var/mob/user, var/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 if(!target.reagents || !target.reagents.total_volume) - to_chat(user, SPAN_NOTICE("[target] is empty.")) - return 1 + to_chat(user, SPAN_NOTICE("[target] is empty of reagents.")) + return TRUE if(reagents && !REAGENTS_FREE_SPACE(reagents)) - to_chat(user, SPAN_NOTICE("[src] is full.")) - return 1 + to_chat(user, SPAN_NOTICE("[src] is full of reagents.")) + return TRUE var/trans = target.reagents.trans_to_obj(src, target.amount_dispensed) to_chat(user, SPAN_NOTICE("You fill [src] with [trans] units of the contents of [target].")) - return 1 + return TRUE /obj/item/proc/standard_splash_mob(var/mob/user, var/mob/target) // This goes into afterattack if(!istype(target)) - return + return FALSE if(user.a_intent == I_HELP) to_chat(user, SPAN_NOTICE("You can't splash people on help intent.")) - return 1 + return TRUE if(!reagents || !reagents.total_volume) - to_chat(user, SPAN_NOTICE("[src] is empty.")) - return 1 + to_chat(user, SPAN_NOTICE("[src] is empty of reagents.")) + return TRUE if(target.reagents && !REAGENTS_FREE_SPACE(target.reagents)) - to_chat(user, SPAN_NOTICE("[target] is full.")) - return 1 + to_chat(user, SPAN_NOTICE("[target] is full of reagents.")) + return TRUE var/contained = REAGENT_LIST(src) @@ -38,30 +38,38 @@ SPAN_DANGER("You splash \the [target] with the contents of \the [src].")) reagents.splash(target, reagents.total_volume) - return 1 - + return TRUE /obj/item/proc/standard_pour_into(mob/user, atom/target, amount = 5) // This goes into afterattack and yes, it's atom-level if(!target.reagents) - return 0 + return FALSE + + if(!target.can_be_poured_into(src)) + // Ensure we don't splash beakers and similar containers. + if(istype(target, /obj/item/chems)) + to_chat(user, SPAN_NOTICE("\The [target] is closed.")) + return TRUE + // Otherwise don't care about splashing. + return FALSE - // Ensure we don't splash beakers and similar containers. - if(!ATOM_IS_OPEN_CONTAINER(target) && istype(target, /obj/item/chems)) - to_chat(user, SPAN_NOTICE("\The [target] is closed.")) - return 1 - // Otherwise don't care about splashing. - else if(!ATOM_IS_OPEN_CONTAINER(target)) - return 0 + if(!can_be_poured_from(user, target)) + return TRUE // don't splash if we can't pour if(!reagents || !reagents.total_volume) - to_chat(user, SPAN_NOTICE("[src] is empty.")) - return 1 + to_chat(user, SPAN_NOTICE("[src] is empty of reagents.")) + return TRUE if(!REAGENTS_FREE_SPACE(target.reagents)) - to_chat(user, SPAN_NOTICE("[target] is full.")) - return 1 - - var/trans = reagents.trans_to(target, amount) - playsound(src, 'sound/effects/pour.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.")) - return 1 \ No newline at end of file + to_chat(user, SPAN_NOTICE("[target] is full of reagents.")) + return TRUE + + var/had_liquids = length(reagents.liquid_volumes) + 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 [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/blades/_blade.dm b/code/game/objects/items/blades/_blade.dm index ccde6d9ef77..775b5a0f09b 100644 --- a/code/game/objects/items/blades/_blade.dm +++ b/code/game/objects/items/blades/_blade.dm @@ -2,8 +2,6 @@ /obj/item/bladed icon_state = "preview" abstract_type = /obj/item/bladed - material = /decl/material/solid/metal/steel - material_force_multiplier = 0.3 material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME origin_tech = @'{"materials":1,"combat":1}' attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") @@ -14,8 +12,9 @@ pickup_sound = 'sound/foley/knife1.ogg' drop_sound = 'sound/foley/knifedrop3.ogg' hitsound = 'sound/weapons/bladeslice.ogg' - thrown_material_force_multiplier = 0.16 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 var/decl/material/guard_material = /decl/material/solid/organic/wood var/decl/material/pommel_material = /decl/material/solid/organic/wood @@ -81,21 +80,30 @@ /obj/item/bladed/proc/update_base_icon_state() icon_state = get_world_inventory_state() +/obj/item/bladed/proc/get_hilt_color() + return istype(hilt_material) ? hilt_material.color : COLOR_WHITE + +/obj/item/bladed/proc/get_guard_color() + return istype(guard_material) ? guard_material.color : COLOR_WHITE + +/obj/item/bladed/proc/get_pommel_color() + return istype(pommel_material) ? pommel_material.color : COLOR_WHITE + /obj/item/bladed/on_update_icon() . = ..() update_base_icon_state() if(istype(hilt_material)) var/check_state = "[icon_state]-hilt" if(check_state_in_icon(check_state, icon)) - add_overlay(overlay_image(icon, check_state, hilt_material.color, RESET_COLOR)) + add_overlay(overlay_image(icon, check_state, get_hilt_color(), RESET_COLOR)) if(istype(guard_material)) var/check_state = "[icon_state]-guard" if(check_state_in_icon(check_state, icon)) - add_overlay(overlay_image(icon, check_state, guard_material.color, RESET_COLOR)) + add_overlay(overlay_image(icon, check_state, get_guard_color(), RESET_COLOR)) if(istype(pommel_material)) var/check_state = "[icon_state]-pommel" if(check_state_in_icon(check_state, icon)) - add_overlay(overlay_image(icon, check_state, pommel_material.color, RESET_COLOR)) + add_overlay(overlay_image(icon, check_state, get_pommel_color(), RESET_COLOR)) if(shine) var/check_state = "[icon_state]-shine" if(check_state_in_icon(check_state, icon)) @@ -104,20 +112,21 @@ add_overlay(I) update_held_icon() -/obj/item/bladed/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) +// We handle this in the post-proc so wielding status is updated for polearms/broadswords/etc +/obj/item/bladed/apply_additional_mob_overlays(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) if(overlay) if(istype(hilt_material)) var/check_state = "[overlay.icon_state]-hilt" if(check_state_in_icon(check_state, overlay.icon)) - overlay.overlays += overlay_image(overlay.icon, check_state, hilt_material.color, RESET_COLOR) + overlay.overlays += overlay_image(overlay.icon, check_state, get_hilt_color(), RESET_COLOR) if(istype(guard_material)) var/check_state = "[overlay.icon_state]-guard" if(check_state_in_icon(check_state, overlay.icon)) - overlay.overlays += overlay_image(overlay.icon, check_state, guard_material.color, RESET_COLOR) + overlay.overlays += overlay_image(overlay.icon, check_state, get_guard_color(), RESET_COLOR) if(istype(pommel_material)) var/check_state = "[overlay.icon_state]-pommel" if(check_state_in_icon(check_state, overlay.icon)) - overlay.overlays += overlay_image(overlay.icon, check_state, pommel_material.color, RESET_COLOR) + overlay.overlays += overlay_image(overlay.icon, check_state, get_pommel_color(), RESET_COLOR) if(shine) var/check_state = "[overlay.icon_state]-shine" if(check_state_in_icon(check_state, overlay.icon)) diff --git a/code/game/objects/items/blades/axe.dm b/code/game/objects/items/blades/axe.dm new file mode 100644 index 00000000000..c136fb4e34d --- /dev/null +++ b/code/game/objects/items/blades/axe.dm @@ -0,0 +1,28 @@ +/obj/item/bladed/axe + abstract_type = /obj/item/bladed/axe + can_be_twohanded = TRUE + pickup_sound = 'sound/foley/scrape1.ogg' + drop_sound = 'sound/foley/tooldrop1.ogg' + w_class = ITEM_SIZE_HUGE + slot_flags = SLOT_BACK + hilt_material = /decl/material/solid/organic/wood + guard_material = /decl/material/solid/organic/leather/gut + pommel_material = null + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + _base_attack_force = 22 + +// Discard pommel material. +/obj/item/bladed/axe/Initialize(ml, material_key, _hilt_mat, _guard_mat, _pommel_mat) + return ..(ml, material_key, _hilt_mat, _guard_mat) + +/obj/item/bladed/axe/afterattack(atom/A, mob/user, proximity) + . = ..() + if(proximity && A && is_held_twohanded()) + if(istype(A,/obj/structure/window)) + var/obj/structure/window/W = A + W.shatter() + else if(istype(A,/obj/structure/grille)) + qdel(A) + else if(istype(A,/obj/effect/vine)) + var/obj/effect/vine/P = A + P.die_off() diff --git a/code/game/objects/items/blades/axe_fire.dm b/code/game/objects/items/blades/axe_fire.dm new file mode 100644 index 00000000000..61cc07e683f --- /dev/null +++ b/code/game/objects/items/blades/axe_fire.dm @@ -0,0 +1,6 @@ +/obj/item/bladed/axe/fire + name = "fire axe" + icon = 'icons/obj/items/bladed/fireaxe.dmi' + desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" + material_alteration = MAT_FLAG_ALTERATION_NAME + guard_material = /decl/material/solid/organic/plastic diff --git a/code/game/objects/items/blades/folding.dm b/code/game/objects/items/blades/folding.dm index d774df06151..69f2de5f8fa 100644 --- a/code/game/objects/items/blades/folding.dm +++ b/code/game/objects/items/blades/folding.dm @@ -2,7 +2,6 @@ name = "folding knife" desc = "A small folding knife." icon = 'icons/obj/items/bladed/folding.dmi' - material_force_multiplier = 0.2 w_class = ITEM_SIZE_SMALL sharp = FALSE pommel_material = null @@ -10,6 +9,7 @@ slot_flags = null material = /decl/material/solid/metal/bronze hilt_material = /decl/material/solid/organic/wood + _base_attack_force = 5 var/open = FALSE var/closed_item_size = ITEM_SIZE_SMALL @@ -17,9 +17,12 @@ var/open_attack_verbs = list("slashed", "stabbed") var/closed_attack_verbs = list("prodded", "tapped") +/obj/item/bladed/folding/iron + material = /decl/material/solid/metal/iron + /obj/item/bladed/folding/Initialize() . = ..() - update_force() + update_attack_force() /obj/item/bladed/folding/attack_self(mob/user) if(user.a_intent != I_HELP) @@ -28,12 +31,12 @@ var/decl/interaction_handler/folding_knife/interaction = GET_DECL(/decl/interaction_handler/folding_knife) if(!interaction.is_possible(src, user)) return FALSE - interaction.invoked(src, user) + interaction.invoked(src, user, user.get_active_held_item()) return TRUE /obj/item/bladed/folding/proc/set_open(new_state, mob/user) open = new_state - update_force() + update_attack_force() update_icon() if(user) if(open) @@ -48,7 +51,9 @@ if(!open) icon_state = "[icon_state]-closed" -/obj/item/bladed/folding/update_force() +/obj/item/bladed/folding/update_attack_force() + ..() + // TODO: check sharp/edge. edge = open sharp = open if(open) @@ -57,7 +62,6 @@ else w_class = closed_item_size attack_verb = closed_attack_verbs - ..() // Only show the inhand sprite when open. /obj/item/bladed/folding/get_mob_overlay(mob/user_mob, slot, bodypart, use_fallback_if_icon_missing = TRUE, skip_adjustment = FALSE) @@ -141,9 +145,9 @@ I.name = "Use as [tool_name]" .[tool_mode] = I -/decl/interaction_handler/folding_knife/invoked(atom/target, mob/user) +/decl/interaction_handler/folding_knife/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/bladed/folding/folding_knife = target - var/chosen_option = show_radial_menu(user, user, get_radial_choices(folding_knife), radius = 42, use_labels = TRUE) + var/chosen_option = show_radial_menu(user, user, get_radial_choices(folding_knife), radius = 42, use_labels = RADIAL_LABELS_OFFSET) if(!chosen_option) return if(chosen_option == "Toggle") diff --git a/code/game/objects/items/blades/knife.dm b/code/game/objects/items/blades/knife.dm index 82b25262cef..8362cbbfb37 100644 --- a/code/game/objects/items/blades/knife.dm +++ b/code/game/objects/items/blades/knife.dm @@ -5,4 +5,3 @@ item_flags = ITEM_FLAG_CAN_HIDE_IN_SHOES | ITEM_FLAG_IS_WEAPON w_class = ITEM_SIZE_SMALL base_parry_chance = 20 - material_force_multiplier = 0.3 diff --git a/code/game/objects/items/blades/one_handed.dm b/code/game/objects/items/blades/one_handed.dm deleted file mode 100644 index c9f8b5272b4..00000000000 --- a/code/game/objects/items/blades/one_handed.dm +++ /dev/null @@ -1,35 +0,0 @@ -/obj/item/bladed/poignard - name = "poignard" - desc = "A short stabbing blade, usually used in the off-hand." - icon = 'icons/obj/items/bladed/poignard.dmi' - hilt_material = /decl/material/solid/metal/brass - guard_material = /decl/material/solid/metal/brass - pommel_material = /decl/material/solid/metal/brass - w_class = ITEM_SIZE_NORMAL - base_parry_chance = 30 - material_force_multiplier = 0.4 - melee_accuracy_bonus = 20 - -/obj/item/bladed/shortsword - name = "shortsword" - desc = "A one-handed shortsword, commonly issued to infantry." - icon = 'icons/obj/items/bladed/shortsword.dmi' - hilt_material = /decl/material/solid/metal/brass - guard_material = /decl/material/solid/metal/brass - pommel_material = /decl/material/solid/metal/brass - w_class = ITEM_SIZE_NORMAL - base_parry_chance = 50 - material_force_multiplier = 0.5 - armor_penetration = 10 - -/obj/item/bladed/rapier - name = "rapier" - desc = "A long, slender, elegant, one-handed blade with a reputation for being used by duelists." - icon = 'icons/obj/items/bladed/rapier.dmi' - hilt_material = /decl/material/solid/metal/brass - guard_material = /decl/material/solid/metal/brass - pommel_material = /decl/material/solid/metal/brass - base_parry_chance = 60 - material_force_multiplier = 0.4 - armor_penetration = 25 - melee_accuracy_bonus = 25 diff --git a/code/game/objects/items/blades/polearm.dm b/code/game/objects/items/blades/polearm.dm new file mode 100644 index 00000000000..1761b1a7942 --- /dev/null +++ b/code/game/objects/items/blades/polearm.dm @@ -0,0 +1,15 @@ +/obj/item/bladed/polearm + abstract_type = /obj/item/bladed/polearm + can_be_twohanded = TRUE + pickup_sound = 'sound/foley/scrape1.ogg' + drop_sound = 'sound/foley/tooldrop1.ogg' + w_class = ITEM_SIZE_HUGE + slot_flags = SLOT_BACK + hilt_material = /decl/material/solid/organic/wood + guard_material = /decl/material/solid/organic/leather/gut + pommel_material = null + _base_attack_force = 20 + +// Discard pommel material. +/obj/item/bladed/polearm/Initialize(ml, material_key, _hilt_mat, _guard_mat, _pommel_mat) + return ..(ml, material_key, _hilt_mat, _guard_mat, _pommel_mat) diff --git a/code/game/objects/items/blades/spear.dm b/code/game/objects/items/blades/spear.dm new file mode 100644 index 00000000000..8659642ba91 --- /dev/null +++ b/code/game/objects/items/blades/spear.dm @@ -0,0 +1,10 @@ +/obj/item/bladed/polearm/spear + name = "spear" + 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 + 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 new file mode 100644 index 00000000000..ad575292627 --- /dev/null +++ b/code/game/objects/items/blades/spear_improvised.dm @@ -0,0 +1,43 @@ +/obj/item/bladed/polearm/spear/improvised + material = /decl/material/solid/glass + hilt_material = /decl/material/solid/metal/steel + guard_material = /decl/material/solid/metal/copper + var/force_binding_color + +/obj/item/bladed/polearm/spear/improvised/Initialize(ml, material_key, _hilt_mat, _guard_mat, _binding_color) + if(_binding_color) + force_binding_color = _binding_color + if(!force_binding_color) + force_binding_color = pick(global.cable_colors) + . = ..(ml, material_key, _hilt_mat, _guard_mat) + +/obj/item/bladed/polearm/spear/improvised/update_name() + . = ..() + SetName("improvised [name]") + +/obj/item/bladed/polearm/spear/improvised/get_guard_color() + return force_binding_color || ..() + +/obj/item/bladed/polearm/spear/improvised/shatter(var/consumed) + if(!consumed) + if(istype(hilt_material)) + SSmaterials.create_object(hilt_material, get_turf(src), 1, /obj/item/stack/material/rods) + if(istype(guard_material)) + new /obj/item/stack/cable_coil(get_turf(src), 3, force_binding_color) + ..() + +// Subtypes for mapping/spawning +/obj/item/bladed/polearm/spear/improvised/diamond + material = /decl/material/solid/gemstone/diamond + hilt_material = /decl/material/solid/metal/gold + force_binding_color = COLOR_PURPLE + +/obj/item/bladed/polearm/spear/improvised/steel + material = /decl/material/solid/metal/steel + hilt_material = /decl/material/solid/organic/wood + force_binding_color = COLOR_GREEN + +/obj/item/bladed/polearm/spear/improvised/supermatter + material = /decl/material/solid/supermatter + hilt_material = /decl/material/solid/organic/wood/ebony + force_binding_color = COLOR_INDIGO diff --git a/code/game/objects/items/blades/swords_one_handed.dm b/code/game/objects/items/blades/swords_one_handed.dm new file mode 100644 index 00000000000..a684d9e4d41 --- /dev/null +++ b/code/game/objects/items/blades/swords_one_handed.dm @@ -0,0 +1,35 @@ +/obj/item/bladed/poignard + name = "poignard" + desc = "A short stabbing blade, usually used in the off-hand." + icon = 'icons/obj/items/bladed/poignard.dmi' + hilt_material = /decl/material/solid/metal/brass + guard_material = /decl/material/solid/metal/brass + pommel_material = /decl/material/solid/metal/brass + w_class = ITEM_SIZE_NORMAL + base_parry_chance = 30 + melee_accuracy_bonus = 20 + _base_attack_force = 15 + +/obj/item/bladed/shortsword + name = "shortsword" + desc = "A one-handed shortsword, commonly issued to infantry." + icon = 'icons/obj/items/bladed/shortsword.dmi' + hilt_material = /decl/material/solid/metal/brass + guard_material = /decl/material/solid/metal/brass + pommel_material = /decl/material/solid/metal/brass + w_class = ITEM_SIZE_NORMAL + base_parry_chance = 50 + armor_penetration = 10 + _base_attack_force = 15 + +/obj/item/bladed/rapier + name = "rapier" + desc = "A long, slender, elegant, one-handed blade with a reputation for being used by duelists." + icon = 'icons/obj/items/bladed/rapier.dmi' + hilt_material = /decl/material/solid/metal/brass + guard_material = /decl/material/solid/metal/brass + pommel_material = /decl/material/solid/metal/brass + base_parry_chance = 60 + armor_penetration = 25 + melee_accuracy_bonus = 25 + _base_attack_force = 15 diff --git a/code/game/objects/items/blades/swords_two_handed.dm b/code/game/objects/items/blades/swords_two_handed.dm new file mode 100644 index 00000000000..8231a87e175 --- /dev/null +++ b/code/game/objects/items/blades/swords_two_handed.dm @@ -0,0 +1,31 @@ +/obj/item/bladed/longsword + name = "longsword" + desc = "A long and heavy two-handed blade with a cruciform hilt and guard." + icon = 'icons/obj/items/bladed/longsword.dmi' + hilt_material = /decl/material/solid/metal/brass + guard_material = /decl/material/solid/metal/brass + pommel_material = /decl/material/solid/metal/brass + slot_flags = SLOT_LOWER_BODY | SLOT_BACK + w_class = ITEM_SIZE_LARGE + armor_penetration = 10 + base_parry_chance = 50 + melee_accuracy_bonus = 10 + can_be_twohanded = TRUE + pickup_sound = 'sound/foley/scrape1.ogg' + drop_sound = 'sound/foley/tooldrop1.ogg' + _base_attack_force = 20 + +/obj/item/bladed/broadsword + name = "broadsword" + desc = "A massive two-handed blade, almost too large to be called a sword." + icon = 'icons/obj/items/bladed/broadsword.dmi' + hilt_material = /decl/material/solid/metal/brass + guard_material = /decl/material/solid/metal/brass + pommel_material = /decl/material/solid/metal/brass + slot_flags = SLOT_BACK + base_parry_chance = 35 + armor_penetration = 20 + can_be_twohanded = TRUE + pickup_sound = 'sound/foley/scrape1.ogg' + drop_sound = 'sound/foley/tooldrop1.ogg' + _base_attack_force = 22 diff --git a/code/game/objects/items/blades/two_handed.dm b/code/game/objects/items/blades/two_handed.dm deleted file mode 100644 index a0350214f7e..00000000000 --- a/code/game/objects/items/blades/two_handed.dm +++ /dev/null @@ -1,25 +0,0 @@ -/obj/item/bladed/longsword - name = "longsword" - desc = "A long and heavy two-handed blade with a cruciform hilt and guard." - icon = 'icons/obj/items/bladed/longsword.dmi' - hilt_material = /decl/material/solid/metal/brass - guard_material = /decl/material/solid/metal/brass - pommel_material = /decl/material/solid/metal/brass - slot_flags = SLOT_LOWER_BODY | SLOT_BACK - w_class = ITEM_SIZE_LARGE - material_force_multiplier = 0.5 - armor_penetration = 10 - base_parry_chance = 50 - melee_accuracy_bonus = 10 - -/obj/item/bladed/broadsword - name = "broadsword" - desc = "A massive two-handed blade, almost too large to be called a sword." - icon = 'icons/obj/items/bladed/broadsword.dmi' - hilt_material = /decl/material/solid/metal/brass - guard_material = /decl/material/solid/metal/brass - pommel_material = /decl/material/solid/metal/brass - material_force_multiplier = 0.6 - slot_flags = SLOT_BACK - base_parry_chance = 35 - armor_penetration = 20 diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index 19ecf0b86cc..4313657b12d 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -58,7 +58,7 @@ /obj/structure/closet/body_bag/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/hand_labeler)) - return //Prevent the labeler from opening the bag when trying to apply a label + return FALSE //Prevent the labeler from opening the bag when trying to apply a label . = ..() /obj/structure/closet/body_bag/store_mobs(var/stored_units) @@ -76,7 +76,7 @@ if(!(ishuman(user) || isrobot(user))) return 0 if(opened) return 0 if(contents.len) return 0 - visible_message("[user] folds up the [name]") + visible_message("[user] folds up \the [src]") . = new item_path(get_turf(src)) qdel(src) diff --git a/code/game/objects/items/books/_book.dm b/code/game/objects/items/books/_book.dm index 6ccc01a0d78..341f3f0bc66 100644 --- a/code/game/objects/items/books/_book.dm +++ b/code/game/objects/items/books/_book.dm @@ -1,7 +1,7 @@ /obj/item/book name = "book" - icon = 'icons/obj/library.dmi' - icon_state = "book" + icon = 'icons/obj/items/books/book.dmi' + icon_state = ICON_STATE_WORLD throw_speed = 1 throw_range = 5 w_class = ITEM_SIZE_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) @@ -9,13 +9,17 @@ material = /decl/material/solid/organic/plastic matter = list(/decl/material/solid/organic/paper = MATTER_AMOUNT_REINFORCEMENT) - var/dat // Actual page content - var/pencode_dat // Cache pencode if input, so it can be edited later. - var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified - var/title // The real name of the book. - var/carved = 0 // Has the book been hollowed out for use as a secret storage item? - var/obj/item/store //What's in the book? + /// Actual page content + var/dat + /// Cache pencode if input, so it can be edited later. + var/pencode_dat + /// Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + var/author + /// 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified + var/unique = FALSE + /// The real name of the book. + var/title + /// Who modified the book last? var/last_modified_ckey /// If TRUE, mild solvents can dissolve ink off the page. /// If FALSE, the user instead receives a message about how the text doesn't seem to be normal ink. @@ -37,6 +41,20 @@ if(SSpersistence.is_tracking(src, /decl/persistence_handler/book)) . = QDEL_HINT_LETMELIVE +/// Clears the text written in the book. Used for acetone removing ink. Returns TRUE if successful, FALSE if it can't be dissolved. +/obj/item/book/proc/clear_text() + if(can_dissolve_text) + dat = null + return TRUE + return FALSE + +/obj/item/book/on_update_icon() + . = ..() + icon_state = get_world_inventory_state() + var/page_state = "[icon_state]-pages" + if(check_state_in_icon(page_state, icon)) + add_overlay(overlay_image(icon, page_state, COLOR_WHITE, RESET_COLOR)) + /obj/item/book/proc/get_style_css() return {"