diff --git a/.editorconfig b/.editorconfig index 13dcd5e029..c44572fbf9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,11 +1,14 @@ [*] -insert_final_newline = true indent_style = tab indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +#end_of_line = lf [*.yml] indent_style = space indent_size = 2 -[*.txt] -insert_final_newline = false +[*.py] +indent_style = space diff --git a/.gitattributes b/.gitattributes index a0a624af04..c447869d3e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,42 @@ -# merger hooks, run tools/hooks/install.bat or install.sh to set up -*.dmm merge=dmm -*.dmi merge=dmi - -# force changelog merging to use union -html/changelog.html merge=union - -# force all files to be LF * text=auto + +## Enforce text mode and LF line breaks +## porter note: not yet LFing dm. +# *.bat text eol=lf +*.css text eol=lf +# *.dm text eol=lf +# *.dme text eol=lf +# *.dmf text eol=lf +*.htm text eol=lf +*.html text eol=lf +*.js text eol=lf +*.json text eol=lf +*.jsx text eol=lf +# *.md text eol=lf +*.py text eol=lf +*.scss text eol=lf +# *.sh text eol=lf +# *.sql text eol=lf +*.svg text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +# *.txt text eol=lf +*.yaml text eol=lf +*.yml text eol=lf + +## Enforce binary mode +*.bmp binary +*.dll binary +*.dmb binary +*.exe binary +*.gif binary +*.jpg binary +*.png binary +*.so binary + +## Merger hooks, run tools/hooks/install.bat or install.sh to set up +*.dmm text eol=lf merge=dmm +*.dmi binary merge=dmi + +## Force changelog merging to use union +html/changelog.html text eol=lf merge=union diff --git a/.github/workflows/autobuild_tgui.yml b/.github/workflows/autobuild_tgui.yml deleted file mode 100644 index 226ea2b7ce..0000000000 --- a/.github/workflows/autobuild_tgui.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Autobuild TGUI - -on: - push: - branches: - - 'master' - paths: - - 'tgui/**.js' - - 'tgui/**.scss' - -jobs: - build: - name: Rebuild TGUI - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v1 - with: - fetch-depth: 25 - - name: Setup Node - uses: actions/setup-node@v1 - with: - node-version: '>=12.13' - - name: Build TGUI - run: bin/tgui --ci - working-directory: ./tgui - - name: Commit Artifacts - run: | - git config --local user.email "action@github.com" - git config --local user.name "TGUI" - git pull origin master - git commit -m "Automatic TGUI Rebuild [ci skip]" -a || true - - name: Push Artifacts - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_MASTER_KEY }} diff --git a/.github/workflows/round_id_linker.yml b/.github/workflows/round_id_linker.yml new file mode 100644 index 0000000000..37998a93eb --- /dev/null +++ b/.github/workflows/round_id_linker.yml @@ -0,0 +1,12 @@ +name: "Round ID Linker" +on: + issues: + types: [opened] + +jobs: + link_rounds: + runs-on: ubuntu-latest + steps: + - uses: Cidatel-Station-13/round_linker@master #notice: fork the round linkies from tg!! + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index f65245c021..e885761104 100644 --- a/.gitignore +++ b/.gitignore @@ -198,13 +198,36 @@ Temporary Items !/.vscode/extensions.json tools/MapAtmosFixer/MapAtmosFixer/obj/* tools/MapAtmosFixer/MapAtmosFixer/bin/* +tools/CreditsTool/bin/* +tools/CreditsTool/obj/* #GitHub Atom .atom-build.json -#Jukebox audio files -/config/jukebox_music/**/* +#KDevelop and Kate +*.kdev4* +*.kate-swp #extra map stuff /_maps/**/backup/ /_maps/templates.dm + +#dmdoc default folder +/dmdoc + +# Ignore custom music and title screens (amend as appropriate) +/config/jukebox_music/sounds/* +!/config/jukebox_music/sounds/exclude +/config/title_music/sounds/* +!/config/title_music/sounds/exclude +/config/title_screens/images/* +!/config/title_screens/images/exclude + +#Linux docker +tools/LinuxOneShot/SetupProgram/obj/* +tools/LinuxOneShot/SetupProgram/bin/* +tools/LinuxOneShot/SetupProgram/.vs +tools/LinuxOneShot/Database +tools/LinuxOneShot/TGS_Config +tools/LinuxOneShot/TGS_Instances +tools/LinuxOneShot/TGS_Logs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..11d27b55d3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,11 @@ +build: + stage: build + rules: + - if: $CI_MERGE_REQUEST_IID || $CI_COMMIT_REF_NAME == "master" + changes: + - tgui/**/*.js + - tgui/**/*.scss + when: always + image: node:lts + script: + - tgui/bin/tgui --ci diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/.travis.yml b/.travis.yml index 2214df3aee..83ef6fa8ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,13 @@ language: generic +os: linux dist: xenial -sudo: false branches: except: - ___TGS3TempBranch - ___TGSTempBranch -matrix: +jobs: include: - name: "Run Linters" addons: @@ -16,9 +16,15 @@ matrix: - python3 - python3-pip - python3-setuptools + - pcregrep + - rustc + - cargo + cache: + directories: + - $HOME/SpacemanDMM install: - tools/travis/install_build_tools.sh - - tools/travis/install_dreamchecker.sh + - tools/travis/install_spaceman_dmm.sh dreamchecker script: - tools/travis/check_filedirs.sh tgstation.dme - tools/travis/check_changelogs.sh @@ -76,3 +82,21 @@ matrix: script: - tools/travis/dm.sh -DTRAVISBUILDING tgstation.dme || travis_terminate 1 - tools/travis/run_server.sh + + # - name: "Generate Documentation" + # # Only run for non-PR commits to the real master branch. + # if: branch = master AND head_branch IS blank + # install: + # - tools/travis/install_spaceman_dmm.sh dmdoc + # before_script: + # # Travis checks out a hash, try to get back on a branch. + # - git checkout $TRAVIS_BRANCH || true + # script: + # - ~/dmdoc + # - touch dmdoc/.nojekyll + # deploy: + # provider: pages + # skip_cleanup: true + # local_dir: dmdoc + # token: $DMDOC_GITHUB_TOKEN + # fqdn: codedocs.tgstation13.org diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 17253e2665..943aa85efd 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -12556,15 +12556,11 @@ /area/engine/atmospherics_engine) "aAb" = ( /turf/closed/wall, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aAc" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aAd" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/neutral{ @@ -13138,9 +13134,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBh" = ( /obj/item/kirbyplants/random, /obj/machinery/light/small{ @@ -13149,25 +13143,19 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBi" = ( /obj/structure/table, /obj/item/clothing/suit/apron/overalls, /obj/item/cultivator, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBj" = ( /obj/machinery/biogenerator, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBk" = ( /obj/structure/table, /obj/item/reagent_containers/food/snacks/grown/wheat, @@ -13182,18 +13170,14 @@ pixel_y = 4 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBl" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/tea, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBm" = ( /obj/item/kirbyplants/random, /obj/machinery/status_display/evac{ @@ -13201,9 +13185,7 @@ }, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBn" = ( /obj/structure/table, /obj/item/stack/packageWrap, @@ -13213,9 +13195,7 @@ /obj/item/reagent_containers/food/snacks/grown/cherries, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBo" = ( /obj/item/kirbyplants/random, /obj/machinery/light/small{ @@ -13225,18 +13205,14 @@ /obj/effect/turf_decal/delivery, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBp" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/poppy, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aBq" = ( /obj/structure/cable/white{ icon_state = "2-4" @@ -13817,33 +13793,25 @@ /area/engine/atmospherics_engine) "aCv" = ( /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aCw" = ( /obj/effect/turf_decal/delivery, /obj/machinery/hydroponics/soil, /obj/item/seeds/watermelon, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aCx" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 4 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aCy" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ dir = 4 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aCz" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -14459,21 +14427,15 @@ }, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aDy" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aDz" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aDA" = ( /obj/structure/sink{ dir = 4; @@ -14484,9 +14446,7 @@ }, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aDB" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -14968,34 +14928,26 @@ icon_state = "2-4" }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aEC" = ( /obj/structure/cable/white{ icon_state = "4-8" }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aED" = ( /obj/structure/cable/white{ icon_state = "4-8" }, /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aEE" = ( /obj/structure/cable/white{ icon_state = "2-8" }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aEG" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ @@ -15628,18 +15580,14 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFK" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/harebell, /obj/machinery/light/small, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFL" = ( /obj/structure/table, /obj/item/shovel/spade, @@ -15658,9 +15606,7 @@ }, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFM" = ( /obj/machinery/status_display/evac{ pixel_y = -32 @@ -15669,34 +15615,26 @@ /obj/machinery/hydroponics/soil, /obj/item/seeds/poppy/geranium, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFN" = ( /obj/structure/table, /obj/item/plant_analyzer, /obj/item/hatchet, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFO" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFP" = ( /obj/structure/cable/white{ icon_state = "1-2" }, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFQ" = ( /obj/structure/table, /obj/item/crowbar, @@ -15704,9 +15642,7 @@ /obj/item/reagent_containers/glass/bucket, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFR" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/tower, @@ -15715,9 +15651,7 @@ /obj/effect/turf_decal/delivery, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aFS" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -16707,9 +16641,7 @@ "aHl" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aHm" = ( /obj/machinery/door/airlock/maintenance_hatch{ name = "Maintenance Hatch"; @@ -16721,9 +16653,7 @@ dir = 1 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aHn" = ( /obj/machinery/door/airlock/maintenance_hatch{ name = "Maintenance Hatch"; @@ -16737,16 +16667,12 @@ dir = 1 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aHo" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plating, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "aHp" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ @@ -125347,9 +125273,7 @@ /obj/item/reagent_containers/glass/bucket, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "etO" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 @@ -125474,9 +125398,7 @@ /obj/machinery/hydroponics/soil, /obj/item/seeds/grape, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "faI" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -126395,9 +126317,7 @@ pixel_x = 6 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned{ - name = "Maintenance Garden" - }) +/area/hydroponics/garden/abandoned) "loH" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index f426ad8e97..51bfe5038e 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -75352,9 +75352,7 @@ /obj/machinery/atmospherics/pipe/simple/orange/visible{ dir = 4 }, -/obj/machinery/airalarm{ - pixel_z = 24 - }, +/obj/machinery/airalarm, /obj/machinery/light/small, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating{ diff --git a/_maps/map_files/PubbyStation/job_changes.dm b/_maps/map_files/PubbyStation/job_changes.dm deleted file mode 100644 index 726601725b..0000000000 --- a/_maps/map_files/PubbyStation/job_changes.dm +++ /dev/null @@ -1,20 +0,0 @@ -#define JOB_MODIFICATION_MAP_NAME "PubbyStation" - -/datum/job/hos/New() - ..() - MAP_JOB_CHECK - access += ACCESS_CREMATORIUM - minimal_access += ACCESS_CREMATORIUM - -/datum/job/warden/New() - ..() - MAP_JOB_CHECK - access += ACCESS_CREMATORIUM - minimal_access += ACCESS_CREMATORIUM - -/datum/job/officer/New() - ..() - MAP_JOB_CHECK - access += ACCESS_CREMATORIUM - minimal_access += ACCESS_CREMATORIUM - diff --git a/_maps/pubbystation.json b/_maps/pubbystation.json index f99cca57c5..acf49b773b 100644 --- a/_maps/pubbystation.json +++ b/_maps/pubbystation.json @@ -7,5 +7,10 @@ "whiteship": "whiteship_pubby", "ferry": "ferry_fancy", "cargo": "cargo_box" - } + }, + "job_access_add": { + "/datum/job/hos": [27], + "/datum/job/officer": [27], + "/datum/job/detective": [27] + } } diff --git a/_maps/templates/hilbertshotel.dmm b/_maps/templates/hilbertshotel.dmm index 4e4d8f8267..da5d13d93e 100644 --- a/_maps/templates/hilbertshotel.dmm +++ b/_maps/templates/hilbertshotel.dmm @@ -7,7 +7,7 @@ /area/hilbertshotel) "c" = ( /obj/machinery/jukebox{ - req_access = null + req_one_access = null }, /turf/open/indestructible/hotelwood, /area/hilbertshotel) diff --git a/code/__DEFINES/_flags/item_flags.dm b/code/__DEFINES/_flags/item_flags.dm index 26e764c45b..7da71e22cb 100644 --- a/code/__DEFINES/_flags/item_flags.dm +++ b/code/__DEFINES/_flags/item_flags.dm @@ -22,12 +22,10 @@ #define SURGICAL_TOOL (1<<10) ///Can be worn on certain slots (currently belt and id) that would otherwise require an uniform. #define NO_UNIFORM_REQUIRED (1<<11) -///Damage when attacking people is not affected by combat mode. -#define NO_COMBAT_MODE_FORCE_MODIFIER (1<<12) /// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all. -#define ITEM_CAN_PARRY (1<<13) +#define ITEM_CAN_PARRY (1<<12) /// This item can be used in the directional blocking system. Only a basic check used to determine if we should proceed with directional block handling at all. -#define ITEM_CAN_BLOCK (1<<14) +#define ITEM_CAN_BLOCK (1<<13) // Flags for the clothing_flags var on /obj/item/clothing diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 2273d34c82..fb461acfa4 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -1,54 +1,54 @@ // This is eventually for wjohn to add more color standardization stuff like I keep asking him >:( -#define COLOR_INPUT_DISABLED "#F0F0F0" -#define COLOR_INPUT_ENABLED "#D3B5B5" -#define COLOR_FLOORTILE_GRAY "#8D8B8B" -#define COLOR_ALMOST_BLACK "#333333" -#define COLOR_BLACK "#000000" -#define COLOR_RED "#FF0000" -#define COLOR_RED_LIGHT "#FF3333" -#define COLOR_MAROON "#800000" -#define COLOR_YELLOW "#FFFF00" -#define COLOR_OLIVE "#808000" -#define COLOR_LIME "#32CD32" -#define COLOR_GREEN "#008000" -#define COLOR_CYAN "#00FFFF" -#define COLOR_TEAL "#008080" -#define COLOR_BLUE "#0000FF" -#define COLOR_BLUE_LIGHT "#33CCFF" -#define COLOR_NAVY "#000080" -#define COLOR_PINK "#FFC0CB" -#define COLOR_MAGENTA "#FF00FF" -#define COLOR_PURPLE "#800080" -#define COLOR_ORANGE "#FF9900" -#define COLOR_BEIGE "#CEB689" -#define COLOR_BLUE_GRAY "#75A2BB" -#define COLOR_BROWN "#BA9F6D" -#define COLOR_DARK_BROWN "#997C4F" -#define COLOR_DARK_ORANGE "#C3630C" -#define COLOR_GREEN_GRAY "#99BB76" -#define COLOR_RED_GRAY "#B4696A" -#define COLOR_PALE_BLUE_GRAY "#98C5DF" -#define COLOR_PALE_GREEN_GRAY "#B7D993" -#define COLOR_PALE_ORANGE "#FFC066" -#define COLOR_PALE_RED_GRAY "#D59998" -#define COLOR_PALE_PURPLE_GRAY "#CBB1CA" -#define COLOR_PURPLE_GRAY "#AE8CA8" +#define COLOR_INPUT_DISABLED "#F0F0F0" +#define COLOR_INPUT_ENABLED "#D3B5B5" +#define COLOR_FLOORTILE_GRAY "#8D8B8B" +#define COLOR_ALMOST_BLACK "#333333" +#define COLOR_BLACK "#000000" +#define COLOR_RED "#FF0000" +#define COLOR_RED_LIGHT "#FF3333" +#define COLOR_MAROON "#800000" +#define COLOR_YELLOW "#FFFF00" +#define COLOR_OLIVE "#808000" +#define COLOR_LIME "#32CD32" +#define COLOR_GREEN "#008000" +#define COLOR_CYAN "#00FFFF" +#define COLOR_TEAL "#008080" +#define COLOR_BLUE "#0000FF" +#define COLOR_BLUE_LIGHT "#33CCFF" +#define COLOR_NAVY "#000080" +#define COLOR_PINK "#FFC0CB" +#define COLOR_MAGENTA "#FF00FF" +#define COLOR_PURPLE "#800080" +#define COLOR_ORANGE "#FF9900" +#define COLOR_BEIGE "#CEB689" +#define COLOR_BLUE_GRAY "#75A2BB" +#define COLOR_BROWN "#BA9F6D" +#define COLOR_DARK_BROWN "#997C4F" +#define COLOR_DARK_ORANGE "#C3630C" +#define COLOR_GREEN_GRAY "#99BB76" +#define COLOR_RED_GRAY "#B4696A" +#define COLOR_PALE_BLUE_GRAY "#98C5DF" +#define COLOR_PALE_GREEN_GRAY "#B7D993" +#define COLOR_PALE_ORANGE "#FFC066" +#define COLOR_PALE_RED_GRAY "#D59998" +#define COLOR_PALE_PURPLE_GRAY "#CBB1CA" +#define COLOR_PURPLE_GRAY "#AE8CA8" -//Color defines used by the assembly detailer. -#define COLOR_ASSEMBLY_BLACK "#545454" -#define COLOR_ASSEMBLY_BGRAY "#9497AB" -#define COLOR_ASSEMBLY_WHITE "#E2E2E2" -#define COLOR_ASSEMBLY_RED "#CC4242" -#define COLOR_ASSEMBLY_ORANGE "#E39751" -#define COLOR_ASSEMBLY_BEIGE "#AF9366" -#define COLOR_ASSEMBLY_BROWN "#97670E" -#define COLOR_ASSEMBLY_GOLD "#AA9100" -#define COLOR_ASSEMBLY_YELLOW "#CECA2B" -#define COLOR_ASSEMBLY_GURKHA "#999875" -#define COLOR_ASSEMBLY_LGREEN "#789876" -#define COLOR_ASSEMBLY_GREEN "#44843C" -#define COLOR_ASSEMBLY_LBLUE "#5D99BE" -#define COLOR_ASSEMBLY_BLUE "#38559E" -#define COLOR_ASSEMBLY_PURPLE "#6F6192" -#define COLOR_ASSEMBLY_PINK "#ff4adc" +// Color defines used by the assembly detailer. +#define COLOR_ASSEMBLY_BLACK "#545454" +#define COLOR_ASSEMBLY_BGRAY "#9497AB" +#define COLOR_ASSEMBLY_WHITE "#E2E2E2" +#define COLOR_ASSEMBLY_RED "#CC4242" +#define COLOR_ASSEMBLY_ORANGE "#E39751" +#define COLOR_ASSEMBLY_BEIGE "#AF9366" +#define COLOR_ASSEMBLY_BROWN "#97670E" +#define COLOR_ASSEMBLY_GOLD "#AA9100" +#define COLOR_ASSEMBLY_YELLOW "#CECA2B" +#define COLOR_ASSEMBLY_GURKHA "#999875" +#define COLOR_ASSEMBLY_LGREEN "#789876" +#define COLOR_ASSEMBLY_GREEN "#44843C" +#define COLOR_ASSEMBLY_LBLUE "#5D99BE" +#define COLOR_ASSEMBLY_BLUE "#38559E" +#define COLOR_ASSEMBLY_PURPLE "#6F6192" +#define COLOR_ASSEMBLY_PINK "#ff4adc" diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index eff5d9eca9..5f5a76b58d 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -34,6 +34,8 @@ #define COMBAT_FLAGS_DEFAULT (COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE) /// Default combat flags for everyone else (so literally everyone but humans). #define COMBAT_FLAGS_SPRINT_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_SPRINT_FORCED | COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE) +/// Default combat flags for those in stamina combat system +#define COMBAT_FLAGS_STAMINA_COMBAT (COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE | COMBAT_FLAG_STAMINA_BUFFER) /// The user wants sprint mode on #define COMBAT_FLAG_SPRINT_TOGGLED (1<<0) @@ -47,8 +49,8 @@ #define COMBAT_FLAG_RESISTING_REST (1<<4) /// Intentionally resting #define COMBAT_FLAG_INTENTIONALLY_RESTING (1<<5) -/// Currently stamcritted but not as violently -#define COMBAT_FLAG_SOFT_STAMCRIT (1<<6) +/// This mob requires stamina buffer to do things that require stamina buffer. Not having this exempts the mob from stamina combat. +#define COMBAT_FLAG_STAMINA_BUFFER (1<<6) /// Force sprint mode on at all times, overrides everything including sprint disable traits. #define COMBAT_FLAG_SPRINT_FORCED (1<<7) /// This mob is capable of using the active parrying system. @@ -64,26 +66,17 @@ // Helpers for getting someone's stamcrit state. Cast to living. #define NOT_STAMCRIT 0 -#define SOFT_STAMCRIT 1 -#define HARD_STAMCRIT 2 +#define HARD_STAMCRIT 1 // Stamcrit check helpers #define IS_STAMCRIT(mob) (CHECK_STAMCRIT(mob) != NOT_STAMCRIT) -#define CHECK_STAMCRIT(mob) ((mob.combat_flags & COMBAT_FLAG_HARD_STAMCRIT)? HARD_STAMCRIT : ((mob.combat_flags & COMBAT_FLAG_SOFT_STAMCRIT)? SOFT_STAMCRIT : NOT_STAMCRIT)) +#define CHECK_STAMCRIT(mob) (mob.combat_flags & COMBAT_FLAG_HARD_STAMCRIT) //stamina stuff -///Threshold over which attacks start being hindered. -#define STAMINA_NEAR_SOFTCRIT 90 -///softcrit for stamina damage. prevents standing up, some actions that cost stamina, etc, but doesn't force a rest or stop movement -#define STAMINA_SOFTCRIT 100 -///sanity cap to prevent stamina actions (that are still performable) from sending you into crit. -#define STAMINA_NEAR_CRIT 130 -///crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit +/// crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit #define STAMINA_CRIT 140 -///same as STAMINA_SOFTCRIT except for the more traditional health calculations -#define STAMINA_SOFTCRIT_TRADITIONAL 0 -///ditto, but for STAMINA_CRIT -#define STAMINA_CRIT_TRADITIONAL -40 +/// Threshold under for which you are unable to draw from stamina health to replace stamina buffer +#define STAMINA_NO_OVERDRAW_THRESHOLD 100 #define CRAWLUNDER_DELAY 30 //Delay for crawling under a standing mob @@ -252,36 +245,6 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( //We will round to this value in damage calculations. #define DAMAGE_PRECISION 0.01 -//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25) -#define TOTAL_MASS_TINY_ITEM 1.25 -#define TOTAL_MASS_SMALL_ITEM 2.5 -#define TOTAL_MASS_NORMAL_ITEM 3.75 -#define TOTAL_MASS_BULKY_ITEM 5 -#define TOTAL_MASS_HUGE_ITEM 6.25 -#define TOTAL_MASS_GIGANTIC_ITEM 7.5 - -#define TOTAL_MASS_HAND_REPLACEMENT 5 //standard punching stamina cost. most hand replacements are huge items anyway. -#define TOTAL_MASS_MEDIEVAL_WEAPON 3.6 //very, very generic average sword/warpick/etc. weight in pounds. -#define TOTAL_MASS_TOY_SWORD 1.5 - -//stamina cost defines. -#define STAM_COST_ATTACK_OBJ_MULT 1.2 -#define STAM_COST_ATTACK_MOB_MULT 1 -#define STAM_COST_BATON_MOB_MULT 1 -#define STAM_COST_NO_COMBAT_MULT 1.25 -#define STAM_COST_W_CLASS_MULT 1.25 -#define STAM_COST_THROW_MULT 2 -#define STAM_COST_THROW_MOB 2.5 //multiplied by (mob size + 1)^2. - -///Multiplier of the (STAMINA_NEAR_CRIT - user current stamina loss) : (STAMINA_NEAR_CRIT - STAMINA_SOFTCRIT) ratio used in damage penalties when stam soft-critted. -#define STAM_CRIT_ITEM_ATTACK_PENALTY 0.66 -/// changeNext_move penalty multiplier of the above. -#define STAM_CRIT_ITEM_ATTACK_DELAY 1.75 -/// Damage penalty when fighting prone. -#define LYING_DAMAGE_PENALTY 0.7 -/// Added delay when firing guns stam-softcritted. Summed with a hardset CLICK_CD_RANGE delay, similar to STAM_CRIT_DAMAGE_DELAY otherwise. -#define STAM_CRIT_GUN_DELAY 2.75 - //stamina recovery defines. Blocked if combat mode is on. #define STAM_RECOVERY_STAM_CRIT -7.5 #define STAM_RECOVERY_RESTING -6 diff --git a/code/__DEFINES/combat/stamina_combat.dm b/code/__DEFINES/combat/stamina_combat.dm new file mode 100644 index 0000000000..1f781ca3e0 --- /dev/null +++ b/code/__DEFINES/combat/stamina_combat.dm @@ -0,0 +1,35 @@ +// Stamina Buffer + +// Standard amounts for stamina usage + +// Multipliers +/// Base stamina cost for an item of a certain w_class without total_mass set. +#define STAM_COST_W_CLASS_MULT 1.25 + +// Flat amounts +/// Usage for eyestabbing with a screwdriver +#define STAMINA_COST_ITEM_EYESTAB 7.5 +/// Usage for shoving yourself off the ground instantly +#define STAMINA_COST_SHOVE_UP 10 + +//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25) +#define TOTAL_MASS_TINY_ITEM 1.25 +#define TOTAL_MASS_SMALL_ITEM 2.5 +#define TOTAL_MASS_NORMAL_ITEM 3.75 +#define TOTAL_MASS_BULKY_ITEM 5 +#define TOTAL_MASS_HUGE_ITEM 6.25 +#define TOTAL_MASS_GIGANTIC_ITEM 7.5 + +#define TOTAL_MASS_HAND_REPLACEMENT 5 //standard punching stamina cost. most hand replacements are huge items anyway. +#define TOTAL_MASS_MEDIEVAL_WEAPON 3.6 //very, very generic average sword/warpick/etc. weight in pounds. +#define TOTAL_MASS_TOY_SWORD 1.5 + +//stamina cost defines. +#define STAM_COST_ATTACK_OBJ_MULT 0.75 +#define STAM_COST_ATTACK_MOB_MULT 1 +#define STAM_COST_BATON_MOB_MULT 0.85 +#define STAM_COST_THROW_MULT 0.75 +#define STAM_COST_THROW_MOB 1.25 //multiplied by (mob size + 1)^2. + +/// Damage penalty when fighting prone. +#define LYING_DAMAGE_PENALTY 0.7 diff --git a/code/__DEFINES/configuration.dm b/code/__DEFINES/configuration.dm index 20c1df2ec7..0428a16828 100644 --- a/code/__DEFINES/configuration.dm +++ b/code/__DEFINES/configuration.dm @@ -3,6 +3,8 @@ #define CONFIG_SET(X, Y) global.config.Set(/datum/config_entry/##X, ##Y) /// Gets the datum of the object, for when editing a const define. #define CONFIG_GET_ENTRY(X) global.config.GetEntryDatum(/datum/config_entry/##X) +/// Caches an entry in the proc +#define CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(X, varname) var/static/datum/config_entry/##X/entrycache_##varname;if(!entrycache_##varname){entrycache_##varname=CONFIG_GET_ENTRY(##X);};var/##varname=entrycache_##varname.config_entry_value #define CONFIG_MAPS_FILE "maps.txt" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 21b66f5e37..3407b3342b 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -86,6 +86,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isinsect(A) (is_species(A, /datum/species/insect)) #define isxenoperson(A) (is_species(A, /datum/species/xeno)) #define isstartjelly(A) (is_species(A, /datum/species/jelly/roundstartslime)) +#define isarachnid(A) (is_species(A, /datum/species/arachnid)) //more carbon mobs #define ismonkey(A) (istype(A, /mob/living/carbon/monkey)) diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm index c5c2dd58c2..854b6c9dcf 100644 --- a/code/__DEFINES/maps.dm +++ b/code/__DEFINES/maps.dm @@ -20,11 +20,6 @@ Multi-Z stations are supported and multi-Z mining and away missions would require only minor tweaks. */ -// helpers for modifying jobs, used in various job_changes.dm files -#define MAP_JOB_CHECK if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return; } -#define MAP_JOB_CHECK_BASE if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return ..(); } -#define MAP_REMOVE_JOB(jobpath) /datum/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() } - #define SPACERUIN_MAP_EDGE_PAD 15 // traits diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 586fce3e0c..e8849d04c8 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -1,17 +1,17 @@ /*ALL MOB-RELATED DEFINES THAT DON'T BELONG IN ANOTHER FILE GO HERE*/ -//Misc mob defines +// Misc mob defines -//Ready states at roundstart for mob/dead/new_player +// Ready states at roundstart for mob/dead/new_player #define PLAYER_NOT_READY 0 #define PLAYER_READY_TO_PLAY 1 #define PLAYER_READY_TO_OBSERVE 2 -//movement intent defines for the m_intent var +// movement intent defines for the m_intent var #define MOVE_INTENT_WALK "walk" #define MOVE_INTENT_RUN "run" -//Blood levels +// Blood levels #define BLOOD_VOLUME_MAX_LETHAL 2150 #define BLOOD_VOLUME_EXCESS 2100 #define BLOOD_VOLUME_MAXIMUM 2000 @@ -22,39 +22,43 @@ #define BLOOD_VOLUME_BAD 224 #define BLOOD_VOLUME_SURVIVE 122 -//Sizes of mobs, used by mob/living/var/mob_size +// Sizes of mobs, used by mob/living/var/mob_size #define MOB_SIZE_TINY 0 #define MOB_SIZE_SMALL 1 #define MOB_SIZE_HUMAN 2 #define MOB_SIZE_LARGE 3 -//Ventcrawling defines +// Ventcrawling defines #define VENTCRAWLER_NONE 0 #define VENTCRAWLER_NUDE 1 #define VENTCRAWLER_ALWAYS 2 -//Bloodcrawling defines +// Bloodcrawling defines #define BLOODCRAWL 1 #define BLOODCRAWL_EAT 2 -//Mob bio-types flags -#define MOB_ORGANIC (1 << 0) +// Mob bio-types flags +#define MOB_ORGANIC (1 << 0) #define MOB_MINERAL (1 << 1) -#define MOB_ROBOTIC (1 << 2) +#define MOB_ROBOTIC (1 << 2) #define MOB_UNDEAD (1 << 3) -#define MOB_HUMANOID (1 << 4) -#define MOB_BUG (1 << 5) +#define MOB_HUMANOID (1 << 4) +#define MOB_BUG (1 << 5) #define MOB_BEAST (1 << 6) -#define MOB_EPIC (1 << 7) //megafauna +#define MOB_EPIC (1 << 7) // Megafauna #define MOB_REPTILE (1 << 8) #define MOB_SPIRIT (1 << 9) -//Organ defines for carbon mobs +// Organ defines for carbon mobs #define ORGAN_ORGANIC 1 #define ORGAN_ROBOTIC 2 #define BODYPART_ORGANIC 1 #define BODYPART_ROBOTIC 2 +#define BODYPART_HYBRID 3 + +#define HYBRID_BODYPART_DAMAGE_THRESHHOLD 25 //How much damage has to be suffered until the damage threshhold counts as passed +#define HYBRID_BODYPART_THESHHOLD_MINDAMAGE 15 //Which damage value this limb cannot be healed out of via easy nonsurgical means if the threshhold has been passed, state resets if damage value goes below mindamage. #define BODYPART_NOT_DISABLED 0 #define BODYPART_DISABLED_DAMAGE 1 @@ -66,194 +70,194 @@ #define DEFAULT_BODYPART_ICON_ROBOTIC 'icons/mob/augmentation/augments.dmi' #define MONKEY_BODYPART "monkey" -#define ALIEN_BODYPART "alien" -#define LARVA_BODYPART "larva" -#define DEVIL_BODYPART "devil" +#define ALIEN_BODYPART "alien" +#define LARVA_BODYPART "larva" +#define DEVIL_BODYPART "devil" /*see __DEFINES/inventory.dm for bodypart bitflag defines*/ // Health/damage defines for carbon mobs #define HUMAN_MAX_OXYLOSS 3 #define HUMAN_CRIT_MAX_OXYLOSS (SSmobs.wait/30) -#define HEAT_DAMAGE_LEVEL_1 2 //Amount of damage applied when your body temperature just passes the 360.15k safety point -#define HEAT_DAMAGE_LEVEL_2 3 //Amount of damage applied when your body temperature passes the 400K point -#define HEAT_DAMAGE_LEVEL_3 8 //Amount of damage applied when your body temperature passes the 460K point and you are on fire +#define HEAT_DAMAGE_LEVEL_1 2 // Amount of damage applied when your body temperature just passes the 360.15k safety point +#define HEAT_DAMAGE_LEVEL_2 3 // Amount of damage applied when your body temperature passes the 400K point +#define HEAT_DAMAGE_LEVEL_3 8 // Amount of damage applied when your body temperature passes the 460K point and you are on fire -#define COLD_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when your body temperature just passes the 260.15k safety point -#define COLD_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when your body temperature passes the 200K point -#define COLD_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 120K point +#define COLD_DAMAGE_LEVEL_1 0.5 // Amount of damage applied when your body temperature just passes the 260.15k safety point +#define COLD_DAMAGE_LEVEL_2 1.5 // Amount of damage applied when your body temperature passes the 200K point +#define COLD_DAMAGE_LEVEL_3 3 // Amount of damage applied when your body temperature passes the 120K point -//Note that gas heat damage is only applied once every FOUR ticks. -#define HEAT_GAS_DAMAGE_LEVEL_1 2 //Amount of damage applied when the current breath's temperature just passes the 360.15k safety point -#define HEAT_GAS_DAMAGE_LEVEL_2 4 //Amount of damage applied when the current breath's temperature passes the 400K point -#define HEAT_GAS_DAMAGE_LEVEL_3 8 //Amount of damage applied when the current breath's temperature passes the 1000K point +// Note that gas heat damage is only applied once every FOUR ticks. +#define HEAT_GAS_DAMAGE_LEVEL_1 2 // Amount of damage applied when the current breath's temperature just passes the 360.15k safety point +#define HEAT_GAS_DAMAGE_LEVEL_2 4 // Amount of damage applied when the current breath's temperature passes the 400K point +#define HEAT_GAS_DAMAGE_LEVEL_3 8 // Amount of damage applied when the current breath's temperature passes the 1000K point -#define COLD_GAS_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when the current breath's temperature just passes the 260.15k safety point -#define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point -#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point +#define COLD_GAS_DAMAGE_LEVEL_1 0.5 // Amount of damage applied when the current breath's temperature just passes the 260.15k safety point +#define COLD_GAS_DAMAGE_LEVEL_2 1.5 // Amount of damage applied when the current breath's temperature passes the 200K point +#define COLD_GAS_DAMAGE_LEVEL_3 3 // Amount of damage applied when the current breath's temperature passes the 120K point -//Brain Damage defines -#define BRAIN_DAMAGE_MILD 20 +// Brain Damage defines +#define BRAIN_DAMAGE_MILD 20 #define BRAIN_DAMAGE_SEVERE 100 -#define BRAIN_DAMAGE_DEATH 200 +#define BRAIN_DAMAGE_DEATH 200 #define BRAIN_TRAUMA_MILD /datum/brain_trauma/mild #define BRAIN_TRAUMA_SEVERE /datum/brain_trauma/severe #define BRAIN_TRAUMA_SPECIAL /datum/brain_trauma/special #define BRAIN_TRAUMA_MAGIC /datum/brain_trauma/magic -#define TRAUMA_RESILIENCE_BASIC 1 //Curable with chems -#define TRAUMA_RESILIENCE_SURGERY 2 //Curable with brain surgery -#define TRAUMA_RESILIENCE_LOBOTOMY 3 //Curable with lobotomy -#define TRAUMA_RESILIENCE_WOUND 4 //Curable by healing the head wound -#define TRAUMA_RESILIENCE_MAGIC 5 //Curable only with magic -#define TRAUMA_RESILIENCE_ABSOLUTE 6 //This is here to stay +#define TRAUMA_RESILIENCE_BASIC 1 // Curable with chems +#define TRAUMA_RESILIENCE_SURGERY 2 // Curable with brain surgery +#define TRAUMA_RESILIENCE_LOBOTOMY 3 // Curable with lobotomy +#define TRAUMA_RESILIENCE_WOUND 4 // Curable by healing the head wound +#define TRAUMA_RESILIENCE_MAGIC 5 // Curable only with magic +#define TRAUMA_RESILIENCE_ABSOLUTE 6 // This is here to stay -//Limit of traumas for each resilience tier -#define TRAUMA_LIMIT_BASIC 3 -#define TRAUMA_LIMIT_SURGERY 2 -#define TRAUMA_LIMIT_WOUND 2 +// Limit of traumas for each resilience tier +#define TRAUMA_LIMIT_BASIC 3 +#define TRAUMA_LIMIT_SURGERY 2 +#define TRAUMA_LIMIT_WOUND 2 #define TRAUMA_LIMIT_LOBOTOMY 3 -#define TRAUMA_LIMIT_MAGIC 3 +#define TRAUMA_LIMIT_MAGIC 3 #define TRAUMA_LIMIT_ABSOLUTE INFINITY #define BRAIN_DAMAGE_INTEGRITY_MULTIPLIER 0.5 -//Surgery Defines +// Surgery Defines #define BIOWARE_GENERIC "generic" #define BIOWARE_NERVES "nerves" #define BIOWARE_CIRCULATION "circulation" #define BIOWARE_LIGAMENTS "ligaments" -//Health hud screws for carbon mobs -#define SCREWYHUD_NONE 0 -#define SCREWYHUD_CRIT 1 -#define SCREWYHUD_DEAD 2 +// Health hud screws for carbon mobs +#define SCREWYHUD_NONE 0 +#define SCREWYHUD_CRIT 1 +#define SCREWYHUD_DEAD 2 #define SCREWYHUD_HEALTHY 3 -//Threshold levels for beauty for humans +// Threshold levels for beauty for humans #define BEAUTY_LEVEL_HORRID -66 -#define BEAUTY_LEVEL_BAD -33 -#define BEAUTY_LEVEL_DECENT 33 -#define BEAUTY_LEVEL_GOOD 66 -#define BEAUTY_LEVEL_GREAT 100 +#define BEAUTY_LEVEL_BAD -33 +#define BEAUTY_LEVEL_DECENT 33 +#define BEAUTY_LEVEL_GOOD 66 +#define BEAUTY_LEVEL_GREAT 100 -//Moods levels for humans -#define MOOD_LEVEL_HAPPY4 15 -#define MOOD_LEVEL_HAPPY3 10 -#define MOOD_LEVEL_HAPPY2 6 -#define MOOD_LEVEL_HAPPY1 2 -#define MOOD_LEVEL_NEUTRAL 0 -#define MOOD_LEVEL_SAD1 -3 -#define MOOD_LEVEL_SAD2 -12 -#define MOOD_LEVEL_SAD3 -18 -#define MOOD_LEVEL_SAD4 -25 +// Moods levels for humans +#define MOOD_LEVEL_HAPPY4 15 +#define MOOD_LEVEL_HAPPY3 10 +#define MOOD_LEVEL_HAPPY2 6 +#define MOOD_LEVEL_HAPPY1 2 +#define MOOD_LEVEL_NEUTRAL 0 +#define MOOD_LEVEL_SAD1 -3 +#define MOOD_LEVEL_SAD2 -12 +#define MOOD_LEVEL_SAD3 -18 +#define MOOD_LEVEL_SAD4 -25 -//Sanity levels for humans -#define SANITY_AMAZING 150 -#define SANITY_GREAT 125 -#define SANITY_NEUTRAL 100 +// Sanity levels for humans +#define SANITY_AMAZING 150 +#define SANITY_GREAT 125 +#define SANITY_NEUTRAL 100 #define SANITY_DISTURBED 75 -#define SANITY_UNSTABLE 50 -#define SANITY_CRAZY 25 -#define SANITY_INSANE 0 +#define SANITY_UNSTABLE 50 +#define SANITY_CRAZY 25 +#define SANITY_INSANE 0 -//Nutrition levels for humans -#define NUTRITION_LEVEL_FAT 600 -#define NUTRITION_LEVEL_FULL 550 +// Nutrition levels for humans +#define NUTRITION_LEVEL_FAT 600 +#define NUTRITION_LEVEL_FULL 550 #define NUTRITION_LEVEL_WELL_FED 450 -#define NUTRITION_LEVEL_FED 350 -#define NUTRITION_LEVEL_HUNGRY 250 +#define NUTRITION_LEVEL_FED 350 +#define NUTRITION_LEVEL_HUNGRY 250 #define NUTRITION_LEVEL_STARVING 150 #define NUTRITION_LEVEL_START_MIN 250 #define NUTRITION_LEVEL_START_MAX 400 -//Disgust levels for humans -#define DISGUST_LEVEL_MAXEDOUT 150 -#define DISGUST_LEVEL_DISGUSTED 75 -#define DISGUST_LEVEL_VERYGROSS 50 -#define DISGUST_LEVEL_GROSS 25 +// Disgust levels for humans +#define DISGUST_LEVEL_MAXEDOUT 150 +#define DISGUST_LEVEL_DISGUSTED 75 +#define DISGUST_LEVEL_VERYGROSS 50 +#define DISGUST_LEVEL_GROSS 25 -//Charge levels for Ethereals -#define ETHEREAL_CHARGE_NONE 0 -#define ETHEREAL_CHARGE_LOWPOWER 20 -#define ETHEREAL_CHARGE_NORMAL 50 +// Charge levels for Ethereals +#define ETHEREAL_CHARGE_NONE 0 +#define ETHEREAL_CHARGE_LOWPOWER 20 +#define ETHEREAL_CHARGE_NORMAL 50 #define ETHEREAL_CHARGE_ALMOSTFULL 75 -#define ETHEREAL_CHARGE_FULL 100 -#define ETHEREAL_CHARGE_OVERLOAD 125 +#define ETHEREAL_CHARGE_FULL 100 +#define ETHEREAL_CHARGE_OVERLOAD 125 #define ETHEREAL_CHARGE_DANGEROUS 150 -//Slime evolution threshold. Controls how fast slimes can split/grow +// Slime evolution threshold. Controls how fast slimes can split/grow #define SLIME_EVOLUTION_THRESHOLD 10 -//Slime extract crossing. Controls how many extracts is required to feed to a slime to core-cross. +// Slime extract crossing. Controls how many extracts is required to feed to a slime to core-cross. #define SLIME_EXTRACT_CROSSING_REQUIRED 10 -//Slime commands defines -#define SLIME_FRIENDSHIP_FOLLOW 3 //Min friendship to order it to follow -#define SLIME_FRIENDSHIP_STOPEAT 5 //Min friendship to order it to stop eating someone -#define SLIME_FRIENDSHIP_STOPEAT_NOANGRY 7 //Min friendship to order it to stop eating someone without it losing friendship -#define SLIME_FRIENDSHIP_STOPCHASE 4 //Min friendship to order it to stop chasing someone (their target) -#define SLIME_FRIENDSHIP_STOPCHASE_NOANGRY 6 //Min friendship to order it to stop chasing someone (their target) without it losing friendship -#define SLIME_FRIENDSHIP_STAY 3 //Min friendship to order it to stay -#define SLIME_FRIENDSHIP_ATTACK 8 //Min friendship to order it to attack +// Slime commands defines +#define SLIME_FRIENDSHIP_FOLLOW 3 // Min friendship to order it to follow +#define SLIME_FRIENDSHIP_STOPEAT 5 // Min friendship to order it to stop eating someone +#define SLIME_FRIENDSHIP_STOPEAT_NOANGRY 7 // Min friendship to order it to stop eating someone without it losing friendship +#define SLIME_FRIENDSHIP_STOPCHASE 4 // Min friendship to order it to stop chasing someone (their target) +#define SLIME_FRIENDSHIP_STOPCHASE_NOANGRY 6 // Min friendship to order it to stop chasing someone (their target) without it losing friendship +#define SLIME_FRIENDSHIP_STAY 3 // Min friendship to order it to stay +#define SLIME_FRIENDSHIP_ATTACK 8 // Min friendship to order it to attack -//Sentience types, to prevent things like sentience potions from giving bosses sentience -#define SENTIENCE_ORGANIC 1 +// Sentience types, to prevent things like sentience potions from giving bosses sentience +#define SENTIENCE_ORGANIC 1 #define SENTIENCE_ARTIFICIAL 2 -// #define SENTIENCE_OTHER 3 unused -#define SENTIENCE_MINEBOT 4 -#define SENTIENCE_BOSS 5 +//#define SENTIENCE_OTHER 3 // Unused +#define SENTIENCE_MINEBOT 4 +#define SENTIENCE_BOSS 5 -//Mob AI Status +// Mob AI Status -//Hostile simple animals -//If you add a new status, be sure to add a list for it to the simple_animals global in _globalvars/lists/mobs.dm +// Hostile simple animals +// If you add a new status, be sure to add a list for it to the simple_animals global in _globalvars/lists/mobs.dm #define AI_ON 1 #define AI_IDLE 2 #define AI_OFF 3 #define AI_Z_OFF 4 -//determines if a mob can smash through it -#define ENVIRONMENT_SMASH_NONE 0 -#define ENVIRONMENT_SMASH_STRUCTURES (1<<0) //crates, lockers, ect -#define ENVIRONMENT_SMASH_WALLS (1<<1) //walls -#define ENVIRONMENT_SMASH_RWALLS (1<<2) //rwalls +// determines if a mob can smash through it +#define ENVIRONMENT_SMASH_NONE 0 +#define ENVIRONMENT_SMASH_STRUCTURES (1<<0) // crates, lockers, ect +#define ENVIRONMENT_SMASH_WALLS (1<<1) // walls +#define ENVIRONMENT_SMASH_RWALLS (1<<2) // rwalls -#define NO_SLIP_WHEN_WALKING (1<<0) -#define SLIDE (1<<1) -#define GALOSHES_DONT_HELP (1<<2) -#define FLYING_DOESNT_HELP (1<<3) -#define SLIDE_ICE (1<<4) -#define SLIP_WHEN_CRAWLING (1<<5) //clown planet ruin amongst others -#define SLIP_WHEN_JOGGING (1<<6) //slips prevented by walking are also dodged if the mob is nor sprinting or fatigued... unless this flag is on. +#define NO_SLIP_WHEN_WALKING (1<<0) +#define SLIDE (1<<1) +#define GALOSHES_DONT_HELP (1<<2) +#define FLYING_DOESNT_HELP (1<<3) +#define SLIDE_ICE (1<<4) +#define SLIP_WHEN_CRAWLING (1<<5) // clown planet ruin amongst others +#define SLIP_WHEN_JOGGING (1<<6) // slips prevented by walking are also dodged if the mob is nor sprinting or fatigued... unless this flag is on. #define MAX_CHICKENS 50 -///Flags used by the flags parameter of electrocute act. +// /Flags used by the flags parameter of electrocute act. -///Makes it so that the shock doesn't take gloves into account. -#define SHOCK_NOGLOVES (1 << 0) -///Used when the shock is from a tesla bolt. -#define SHOCK_TESLA (1 << 1) -///Used when an illusion shocks something. Makes the shock deal stamina damage and not trigger certain secondary effects. -#define SHOCK_ILLUSION (1 << 2) -///The shock doesn't stun. -#define SHOCK_NOSTUN (1 << 3) +// /Makes it so that the shock doesn't take gloves into account. +#define SHOCK_NOGLOVES (1 << 0) +// /Used when the shock is from a tesla bolt. +#define SHOCK_TESLA (1 << 1) +// /Used when an illusion shocks something. Makes the shock deal stamina damage and not trigger certain secondary effects. +#define SHOCK_ILLUSION (1 << 2) +// /The shock doesn't stun. +#define SHOCK_NOSTUN (1 << 3) -#define INCORPOREAL_MOVE_BASIC 1 -#define INCORPOREAL_MOVE_SHADOW 2 // leaves a trail of shadows -#define INCORPOREAL_MOVE_JAUNT 3 // is blocked by holy water/salt +#define INCORPOREAL_MOVE_BASIC 1 +#define INCORPOREAL_MOVE_SHADOW 2 // leaves a trail of shadows +#define INCORPOREAL_MOVE_JAUNT 3 // is blocked by holy water/salt -//Secbot and ED209 judgement criteria bitflag values +// Secbot and ED209 judgement criteria bitflag values #define JUDGE_EMAGGED (1<<0) #define JUDGE_IDCHECK (1<<1) #define JUDGE_WEAPONCHECK (1<<2) #define JUDGE_RECORDCHECK (1<<3) -//ED209's ignore monkeys +// ED209's ignore monkeys #define JUDGE_IGNOREMONKEYS (1<<4) #define MEGAFAUNA_DEFAULT_RECOVERY_TIME 5 @@ -262,45 +266,45 @@ // Offsets defines -#define OFFSET_UNIFORM "uniform" -#define OFFSET_ID "id" -#define OFFSET_GLOVES "gloves" -#define OFFSET_GLASSES "glasses" -#define OFFSET_EARS "ears" -#define OFFSET_SHOES "shoes" -#define OFFSET_S_STORE "s_store" -#define OFFSET_FACEMASK "mask" -#define OFFSET_HEAD "head" -#define OFFSET_EYES "eyes" -#define OFFSET_LIPS "lips" -#define OFFSET_BELT "belt" -#define OFFSET_BACK "back" -#define OFFSET_SUIT "suit" -#define OFFSET_NECK "neck" -#define OFFSET_HAIR "hair" -#define OFFSET_FHAIR "fhair" -#define OFFSET_MUTPARTS "mutantparts" +#define OFFSET_UNIFORM "uniform" +#define OFFSET_ID "id" +#define OFFSET_GLOVES "gloves" +#define OFFSET_GLASSES "glasses" +#define OFFSET_EARS "ears" +#define OFFSET_SHOES "shoes" +#define OFFSET_S_STORE "s_store" +#define OFFSET_FACEMASK "mask" +#define OFFSET_HEAD "head" +#define OFFSET_EYES "eyes" +#define OFFSET_LIPS "lips" +#define OFFSET_BELT "belt" +#define OFFSET_BACK "back" +#define OFFSET_SUIT "suit" +#define OFFSET_NECK "neck" +#define OFFSET_HAIR "hair" +#define OFFSET_FHAIR "fhair" +#define OFFSET_MUTPARTS "mutantparts" -//MINOR TWEAKS/MISC -#define AGE_MIN 18 //youngest a character can be //CITADEL EDIT - 17 --> 18 -#define AGE_MAX 85 //oldest a character can be -#define WIZARD_AGE_MIN 30 //youngest a wizard can be -#define APPRENTICE_AGE_MIN 29 //youngest an apprentice can be -#define SHOES_SLOWDOWN 0 //How much shoes slow you down by default. Negative values speed you up -#define SHOES_SPEED_SLIGHT SHOES_SLOWDOWN - 1 // slightest speed boost to movement -#define POCKET_STRIP_DELAY 40 //time taken (in deciseconds) to search somebody's pockets -#define DOOR_CRUSH_DAMAGE 15 //the amount of damage that airlocks deal when they crush you +// MINOR TWEAKS/MISC +#define AGE_MIN 18 // youngest a character can be // CITADEL EDIT - 17 --> 18 +#define AGE_MAX 85 // oldest a character can be +#define WIZARD_AGE_MIN 30 // youngest a wizard can be +#define APPRENTICE_AGE_MIN 29 // youngest an apprentice can be +#define SHOES_SLOWDOWN 0 // How much shoes slow you down by default. Negative values speed you up +#define SHOES_SPEED_SLIGHT SHOES_SLOWDOWN - 1 // slightest speed boost to movement +#define POCKET_STRIP_DELAY 40 // time taken (in deciseconds) to search somebody's pockets +#define DOOR_CRUSH_DAMAGE 15 // the amount of damage that airlocks deal when they crush you -#define HUNGER_FACTOR 0.1 //factor at which mob nutrition decreases -#define ETHEREAL_CHARGE_FACTOR 0.08 //factor at which ethereal's charge decreases -#define REAGENTS_METABOLISM 0.4 //How many units of reagent are consumed per tick, by default. +#define HUNGER_FACTOR 0.1 // factor at which mob nutrition decreases +#define ETHEREAL_CHARGE_FACTOR 0.08 // factor at which ethereal's charge decreases +#define REAGENTS_METABOLISM 0.4 // How many units of reagent are consumed per tick, by default. #define REAGENTS_EFFECT_MULTIPLIER (REAGENTS_METABOLISM / 0.4) // By defining the effect multiplier this way, it'll exactly adjust all effects according to how they originally were with the 0.4 metabolism // Roundstart trait system -#define MAX_QUIRKS 6 //The maximum amount of quirks one character can have at roundstart +#define MAX_QUIRKS 6 // The maximum amount of quirks one character can have at roundstart -#define MAX_REVIVE_FIRE_DAMAGE 180 +#define MAX_REVIVE_FIRE_DAMAGE 180 #define MAX_REVIVE_BRUTE_DAMAGE 180 // AI Toggles @@ -308,28 +312,28 @@ #define AI_VOX // Comment out if you don't want VOX to be enabled and have players download the voice sounds. // /obj/item/bodypart on_mob_life() retval flag -#define BODYPART_LIFE_UPDATE_HEALTH (1<<0) +#define BODYPART_LIFE_UPDATE_HEALTH (1<<0) #define HUMAN_FIRE_STACK_ICON_NUM 3 #define TYPING_INDICATOR_TIMEOUT 20 MINUTES -#define GRAB_PIXEL_SHIFT_PASSIVE 6 -#define GRAB_PIXEL_SHIFT_AGGRESSIVE 12 -#define GRAB_PIXEL_SHIFT_NECK 16 +#define GRAB_PIXEL_SHIFT_PASSIVE 6 +#define GRAB_PIXEL_SHIFT_AGGRESSIVE 12 +#define GRAB_PIXEL_SHIFT_NECK 16 #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; #define INTERACTING_WITH(X, Y) (Y in X.do_afters) -/// Field of vision defines. +// / Field of vision defines. #define FOV_90_DEGREES 90 #define FOV_180_DEGREES 180 #define FOV_270_DEGREES 270 -/// How far away you can be to make eye contact with someone while examining +// / How far away you can be to make eye contact with someone while examining #define EYE_CONTACT_RANGE 5 -/// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine() +// / If you examine the same atom twice in this timeframe, we call examine_more() instead of examine() #define EXAMINE_MORE_TIME 1 SECONDS -#define SILENCE_RANGED_MESSAGE (1<<0) +#define SILENCE_RANGED_MESSAGE (1<<0) diff --git a/code/__DEFINES/skills/defines.dm b/code/__DEFINES/skills/defines.dm index 47aaeeb1dc..a0b34895df 100644 --- a/code/__DEFINES/skills/defines.dm +++ b/code/__DEFINES/skills/defines.dm @@ -40,7 +40,6 @@ #define SKILL_TRAIN_ATTACK_OBJ "train_attack_obj" #define SKILL_STAMINA_COST "stamina_cost" //Influences the stamina cost from weapon usage. #define SKILL_THROW_STAM_COST "throw_stam_cost" -#define SKILL_COMBAT_MODE "combat_mode" //The user must have combat mode on. #define SKILL_SANITY "sanity" //Is the skill affected by (in)sanity. #define SKILL_INTELLIGENCE "intelligence" //Is the skill affected by brain damage? diff --git a/code/__DEFINES/species.dm b/code/__DEFINES/species.dm new file mode 100644 index 0000000000..c6cc6a5037 --- /dev/null +++ b/code/__DEFINES/species.dm @@ -0,0 +1,50 @@ + +// Species ID Defines --------------------------- + +#define SPECIES_ABDUCTOR "abductor" +#define SPECIES_ANDROID "android" +#define SPECIES_ANGEL "angel" +#define SPECIES_MAMMAL "mammal" +#define SPECIES_INSECT "insect" +#define SPECIES_DULLAHAN "dullahan" +#define SPECIES_DWARF "dwarf" +#define SPECIES_ETHEREAL "ethereal" +#define SPECIES_FELINID "felinid" +#define SPECIES_FLY "fly" +#define SPECIES_GOLEM "golem" +#define SPECIES_HUMAN "human" +#define SPECIES_IPC "ipc" +#define SPECIES_JELLY "jelly" + #define SPECIES_SLIME "slime" + #define SPECIES_SLIME_HYBRID "slimeperson" + #define SPECIES_SLIME_LUMI "slimelumi" + #define SPECIES_STARGAZER "stargazer" +#define SPECIES_LIZARD "lizard" + #define SPECIES_ASHWALKER "ashlizard" +#define SPECIES_MUSHROOM "mush" +#define SPECIES_PLASMAMAN "plasmaman" +#define SPECIES_POD "pod" + #define SPECIES_POD_WEAK "podweak" +#define SPECIES_SHADOW "shadow" + #define SPECIES_NIGHTMARE "nightmare" +#define SPECIES_SKELETON "skeleton" + #define SPECIES_SKELETON_SPACE "spaceskeleton" +#define SPECIES_SYNTH_LIZARD "synthliz" +#define SPECIES_SYNTH "synth" + #define SPECIES_SYNTH_MIL "military_synth" +#define SPECIES_VAMPIRE "vampire" +#define SPECIES_XENOHYBRID "xeno" + +// Species Category Defines --------------------- + +#define SPECIES_CATEGORY_ALIEN "alien" +#define SPECIES_CATEGORY_BASIC "human" +#define SPECIES_CATEGORY_FURRY "furry" // Weird Category, but okay. +#define SPECIES_CATEGORY_GOLEM "golem" +#define SPECIES_CATEGORY_INSECT "insect" +#define SPECIES_CATEGORY_PLANT "plant" +#define SPECIES_CATEGORY_ROBOT "robotic" +#define SPECIES_CATEGORY_JELLY "jelly" +#define SPECIES_CATEGORY_SHADOW "shadow" +#define SPECIES_CATEGORY_SKELETON "skeleton" +#define SPECIES_CATEGORY_UNDEAD "undead" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 80633148e9..520f205381 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -164,6 +164,7 @@ #define TRAIT_NO_MIDROUND_ANTAG "no-midround-antag" //can't be turned into an antag by random events #define TRAIT_PUGILIST "pugilist" //This guy punches people for a living #define TRAIT_KI_VAMPIRE "ki-vampire" //when someone with this trait rolls maximum damage on a punch and stuns the target, they regain some stamina and do clone damage +#define TRAIT_MAULER "mauler" // this guy punches the shit out of people to hurt them, not to drain their stamina #define TRAIT_PASSTABLE "passtable" #define TRAIT_GIANT "giant" #define TRAIT_DWARF "dwarf" @@ -207,6 +208,10 @@ #define TRAIT_FAST_PUMP "fast_pump" #define TRAIT_NO_PROCESS_FOOD "no-process-food" // You don't get benefits from nutriment, nor nutrition from reagent consumables #define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good... +/// Prevents stamina buffer regeneration +#define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen" +/// Prevents stamina regeneration +#define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen" // mobility flag traits // IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 1119ef44ed..b8c009ff4f 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -32,6 +32,9 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/caps, GLOB.caps_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_wings, GLOB.insect_wings_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_fluff, GLOB.insect_fluffs_list) + init_sprite_accessory_subtypes(/datum/sprite_accessory/arachnid_legs, GLOB.arachnid_legs_list) + init_sprite_accessory_subtypes(/datum/sprite_accessory/arachnid_spinneret, GLOB.arachnid_spinneret_list) + init_sprite_accessory_subtypes(/datum/sprite_accessory/arachnid_mandibles, GLOB.arachnid_mandibles_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/deco_wings, GLOB.deco_wings_list) //CIT CHANGES START HERE, ADDS SNOWFLAKE BODYPARTS AND MORE diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 52e26b67e7..513437e57e 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -76,6 +76,12 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_fluff, GLOB.insect_fluffs_list) if(!GLOB.insect_markings_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_markings, GLOB.insect_markings_list) + if(!GLOB.arachnid_legs_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/arachnid_legs, GLOB.arachnid_legs_list) + if(!GLOB.arachnid_spinneret_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/arachnid_spinneret, GLOB.arachnid_spinneret_list) + if(!GLOB.arachnid_mandibles_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/arachnid_mandibles, GLOB.arachnid_mandibles_list) //CIT CHANGES - genitals and such if(!GLOB.cock_shapes_list.len) @@ -177,6 +183,9 @@ "insect_wings" = pick(GLOB.insect_wings_list), "insect_fluff" = "None", "insect_markings" = pick(GLOB.insect_markings_list), + "arachnid_legs" = pick(GLOB.arachnid_legs_list), + "arachnid_spinneret" = pick(GLOB.arachnid_spinneret_list), + "arachnid_mandibles" = pick(GLOB.arachnid_mandibles_list), "taur" = "None", "mam_body_markings" = snowflake_markings_list.len ? pick(snowflake_markings_list) : "None", "mam_ears" = snowflake_ears_list ? pick(snowflake_ears_list) : "None", @@ -278,6 +287,13 @@ if(!findname(.)) break +/proc/random_unique_arachnid_name(attempts_to_find_unique_name=10) + for(var/i in 1 to attempts_to_find_unique_name) + . = capitalize(pick(GLOB.arachnid_first)) + " " + capitalize(pick(GLOB.arachnid_last)) + + if(!findname(.)) + break + #define SKINTONE2HEX(skin_tone) GLOB.skin_tones[skin_tone] || skin_tone /proc/random_skin_tone() diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index e1848b21a2..a61e3a6492 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -16,6 +16,9 @@ /proc/moth_name() return "[pick(GLOB.moth_first)] [pick(GLOB.moth_last)]" +/proc/arachnid_name() + return "[pick(GLOB.arachnid_first)] [pick(GLOB.arachnid_last)]" + /proc/church_name() var/static/church_name if (church_name) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 85f5e4fd9b..b558b28cf2 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -252,7 +252,6 @@ GLOBAL_LIST_INIT(bitfields, list( "COMBAT_FLAG_SPRINT_ACTIVE" = COMBAT_FLAG_SPRINT_ACTIVE, "COMBAT_FLAG_ATTEMPTING_CRAWL" = COMBAT_FLAG_ATTEMPTING_CRAWL, "COMBAT_FLAG_HARD_STAMCRIT" = COMBAT_FLAG_HARD_STAMCRIT, - "COMBAT_FLAG_SOFT_STAMCRIT" = COMBAT_FLAG_SOFT_STAMCRIT, "COMBAT_FLAG_INTENTIONALLY_RESTING" = COMBAT_FLAG_INTENTIONALLY_RESTING, "COMBAT_FLAG_RESISTING_REST" = COMBAT_FLAG_RESISTING_REST, "COMBAT_FLAG_SPRINT_FORCED" = COMBAT_FLAG_SPRINT_FORCED diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 9d8da6c145..0444e42e91 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -38,6 +38,9 @@ GLOBAL_LIST_EMPTY(r_wings_list) GLOBAL_LIST_EMPTY(insect_wings_list) GLOBAL_LIST_EMPTY(insect_fluffs_list) GLOBAL_LIST_EMPTY(insect_markings_list) +GLOBAL_LIST_EMPTY(arachnid_legs_list) +GLOBAL_LIST_EMPTY(arachnid_spinneret_list) +GLOBAL_LIST_EMPTY(arachnid_mandibles_list) GLOBAL_LIST_EMPTY(caps_list) //a way to index the right bodypart list given the type of bodypart @@ -60,6 +63,9 @@ GLOBAL_LIST_INIT(mutant_reference_list, list( "insect_wings" = GLOB.insect_wings_list, "insect_fluff" = GLOB.insect_fluffs_list, "insect_markings" = GLOB.insect_markings_list, + "arachnid_legs" = GLOB.arachnid_legs_list, + "arachnid_spinneret" = GLOB.arachnid_spinneret_list, + "arachnid_mandibles" = GLOB.arachnid_mandibles_list, "caps" = GLOB.caps_list, "ipc_screen" = GLOB.ipc_screens_list, "ipc_antenna" = GLOB.ipc_antennas_list, @@ -275,14 +281,14 @@ GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/o //locked parts are those that your picked species requires to have //unlocked parts are those that anyone can choose on customisation regardless //parts not in unlocked, but in all, are thus locked -GLOBAL_LIST_INIT(all_mutant_parts, list("tail_lizard" = "Tail", "mam_tail" = "Tail", "tail_human" = "Tail", "snout" = "Snout", "frills" = "Frills", "spines" = "Spines", "body_markings" = "Body Markings", "mam_body_markings" = "Species Markings" , "mam_ears" = "Ears", "ears" = "Ears", "mam_snouts" = "Snout", "legs" = "Legs", "deco_wings" = "Decorative Wings", "insect_wings" = "Insect Wings", "insect_fluff" = "Insect Fluff", "taur" = "Tauric Body", "insect_markings" = "Insect Markings", "wings" = "Wings", "xenohead" = "Caste Head", "xenotail" = "Tail", "xenodorsal" = "Dorsal Spines", "ipc_screen" = "Screen", "ipc_antenna" = "Antenna", "meat_type" = "Meat Type", "horns" = "Horns")) +GLOBAL_LIST_INIT(all_mutant_parts, list("tail_lizard" = "Tail", "mam_tail" = "Tail", "tail_human" = "Tail", "snout" = "Snout", "frills" = "Frills", "spines" = "Spines", "body_markings" = "Body Markings", "mam_body_markings" = "Species Markings" , "mam_ears" = "Ears", "ears" = "Ears", "mam_snouts" = "Snout", "legs" = "Legs", "deco_wings" = "Decorative Wings", "insect_wings" = "Insect Wings", "insect_fluff" = "Insect Fluff", "taur" = "Tauric Body", "insect_markings" = "Insect Markings", "wings" = "Wings", "arachnid_legs" = "Arachnid Legs", "arachnid_spinneret" = "Spinneret", "arachnid_mandibles" = "Mandibles", "xenohead" = "Caste Head", "xenotail" = "Tail", "xenodorsal" = "Dorsal Spines", "ipc_screen" = "Screen", "ipc_antenna" = "Antenna", "meat_type" = "Meat Type", "horns" = "Horns")) GLOBAL_LIST_INIT(unlocked_mutant_parts, list("horns", "insect_fluff")) //parts in either of the above two lists that require a second option that allows them to be coloured GLOBAL_LIST_INIT(colored_mutant_parts, list("insect_wings" = "wings_color", "deco_wings" = "wings_color", "horns" = "horns_color")) //body ids that have greyscale sprites -GLOBAL_LIST_INIT(greyscale_limb_types, list("human","moth","lizard","pod","plant","jelly","slime","golem","lum","stargazer","mush","ethereal","snail","c_golem","b_golem","mammal","xeno","ipc","insect","synthliz","avian","aquatic")) +GLOBAL_LIST_INIT(greyscale_limb_types, list("human","moth","lizard","pod","plant","jelly","slime","golem","slimelumi","stargazer","mush","ethereal","snail","c_golem","b_golem","mammal","xeno","ipc","insect","synthliz","avian","aquatic")) //body ids that have prosthetic sprites GLOBAL_LIST_INIT(prosthetic_limb_types, list("xion","bishop","cybersolutions","grayson","hephaestus","nanotrasen","talon")) diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 134f9d9cbe..64bac1f9af 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -40,6 +40,7 @@ GLOBAL_LIST_EMPTY(sentient_disease_instances) GLOBAL_LIST_EMPTY(latejoin_ai_cores) GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup) +GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup_floating) GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traitor-target purposes. @@ -47,14 +48,22 @@ GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traito // NOTE: This is entirely based on the fact that byond typesof/subtypesof gets longer/deeper paths before shallower ones. // If that ever breaks this entire proc breaks. var/list/mob_types = typesof(/mob) - var/list/entry_value = CONFIG_GET(keyed_list/multiplicative_movespeed) + var/list/mob_types_floating = typesof(/mob) + var/list/entry_value = CONFIG_GET(keyed_list/multiplicative_movespeed/normal) + var/list/entry_value_floating = CONFIG_GET(keyed_list/multiplicative_movespeed/floating) var/list/configured_types = list() + var/list/configured_types_floating = list() for(var/path in entry_value) var/value = entry_value[path] if(isnull(value)) continue // associative list sets for elements that already exist preserve order mob_types[path] = value + for(var/path in entry_value_floating) + var/value = entry_value_floating[path] + if(isnull(value)) + continue + mob_types_floating[path] = value // now go back up through it to set everything, making absolute sure that base paths are overridden by child paths all the way down the path tree. for(var/i in length(mob_types) to 1 step -1) var/path = mob_types[i] @@ -63,7 +72,14 @@ GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traito // we're going from bottom to top so it should be safe to do this without further checks.. for(var/subpath in typesof(path)) configured_types[subpath] = mob_types[path] + for(var/i in length(mob_types_floating) to 1 step -1) + var/path = mob_types_floating[i] + if(isnull(mob_types_floating[path])) + continue + for(var/subpath in typesof(path)) + configured_types_floating[subpath] = mob_types_floating[path] GLOB.mob_config_movespeed_type_lookup = configured_types + GLOB.mob_config_movespeed_type_lookup_floating = configured_types_floating if(update_mobs) update_mob_config_movespeeds() diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index b80fcf0bbf..1148e92dd5 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -16,6 +16,8 @@ GLOBAL_LIST_INIT(carp_names, world.file2list("strings/names/carp.txt")) GLOBAL_LIST_INIT(golem_names, world.file2list("strings/names/golem.txt")) GLOBAL_LIST_INIT(moth_first, world.file2list("strings/names/moth_first.txt")) GLOBAL_LIST_INIT(moth_last, world.file2list("strings/names/moth_last.txt")) +GLOBAL_LIST_INIT(arachnid_first, world.file2list("strings/names/arachnid_first.txt")) +GLOBAL_LIST_INIT(arachnid_last, world.file2list("strings/names/arachnid_last.txt")) GLOBAL_LIST_INIT(plasmaman_names, world.file2list("strings/names/plasmaman.txt")) GLOBAL_LIST_INIT(ethereal_names, world.file2list("strings/names/ethereal.txt")) GLOBAL_LIST_INIT(posibrain_names, world.file2list("strings/names/posibrain.txt")) diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 1ead41d513..929e01c6d9 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -318,4 +318,4 @@ GLOBAL_LIST_EMPTY(radial_menus) if(istype(custom_check)) if(!custom_check.Invoke()) return - return answer \ No newline at end of file + return answer diff --git a/code/_onclick/hud/screen_objects/stamina.dm b/code/_onclick/hud/screen_objects/stamina.dm index 5484014f8f..98a387d32d 100644 --- a/code/_onclick/hud/screen_objects/stamina.dm +++ b/code/_onclick/hud/screen_objects/stamina.dm @@ -11,7 +11,9 @@ /obj/screen/staminas/Click(location,control,params) if(isliving(usr)) var/mob/living/L = usr - to_chat(L, "You have [L.getStaminaLoss()] stamina loss.
Your stamina buffer can take [L.stambuffer] stamina loss, and recharges at no cost.
Your stamina buffer is [(L.stambuffer*(100/L.stambuffer))-(L.bufferedstam*(100/L.stambuffer))]% full.
") + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/buffer_max, buffer_max) + to_chat(L, "You have [L.getStaminaLoss()] stamina loss.
\ +
Your stamina buffer is [round((L.stamina_buffer / buffer_max) * 100, 0.1)]% full.
") /obj/screen/staminas/update_icon_state() var/mob/living/carbon/user = hud?.mymob @@ -24,6 +26,19 @@ else icon_state = "stamina[clamp(FLOOR(user.getStaminaLoss() /20, 1), 0, 6)]" +/obj/screen/staminas/update_overlays() + . = ..() + var/mob/living/carbon/user = hud?.mymob + if(!user) + return + var/percent = user.getStaminaLoss() / STAMINA_CRIT + if((user.stat == DEAD) || (user.combat_flags & COMBAT_FLAG_HARD_STAMCRIT) || (user.hal_screwyhud in 1 to 2)) + . += list("stamina_alert3") + else if(percent >= 0.85) + . += list("stamina_alert2") + else if(percent >= 0.7) + . += list("stamina_alert1") + //stam buffer /obj/screen/staminabuffer icon = 'modular_citadel/icons/ui/screen_gen.dmi' @@ -33,29 +48,33 @@ layer = ABOVE_HUD_LAYER + 0.1 mouse_opacity = 0 -/obj/screen/staminabuffer/update_icon_state() +/obj/screen/staminabuffer/proc/mark_dirty() + if(update_to_mob()) + START_PROCESSING(SShuds, src) + +/obj/screen/staminabuffer/process() + if(!update_to_mob()) + return PROCESS_KILL + +/obj/screen/staminabuffer/Destroy() + STOP_PROCESSING(SShuds, src) + return ..() + +/obj/screen/staminabuffer/proc/update_to_mob() var/mob/living/carbon/user = hud?.mymob - if(!user) - return + user.UpdateStaminaBuffer(FALSE) + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/buffer_max, buffer_max) + if(!user?.client) + return FALSE if(user.stat == DEAD || (user.combat_flags & COMBAT_FLAG_HARD_STAMCRIT) || (user.hal_screwyhud in 1 to 2)) - icon_state = "stambuffer7" - else if(user.hal_screwyhud == 5) icon_state = "stambuffer0" + return FALSE + else if(user.hal_screwyhud == 5) + icon_state = "stambuffer29" + return FALSE + else if(user.stamina_buffer >= buffer_max) + icon_state = "stambuffer29" + return FALSE else - switch(user.bufferedstam / user.stambuffer) - if(0.95 to INFINITY) - icon_state = "stambuffer7" - if(0.9 to 0.95) - icon_state = "stambuffer6" - if(0.8 to 0.9) - icon_state = "stambuffer5" - if(0.6 to 0.8) - icon_state = "stambuffer4" - if(0.4 to 0.6) - icon_state = "stambuffer3" - if(0.2 to 0.4) - icon_state = "stambuffer2" - if(0.05 to 0.2) - icon_state = "stambuffer1" - else - icon_state = "stambuffer0" + icon_state = "stambuffer[FLOOR((user.stamina_buffer / buffer_max) * 29, 1)]" + return TRUE diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 9e88476513..cd1ee56848 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -85,6 +85,9 @@ if(force && damtype != STAMINA && HAS_TRAIT(user, TRAIT_PACIFISM)) to_chat(user, "You don't want to harm other living beings!") return + + if(!UseStaminaBufferStandard(user, STAM_COST_ATTACK_MOB_MULT, null, TRUE)) + return DISCARD_LAST_ACTION if(!force) playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), 1, -1) @@ -100,10 +103,6 @@ log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") add_fingerprint(user) - var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT) //CIT CHANGE - makes attacking things cause stamina loss - if(weight) - user.adjustStaminaLossBuffered(weight) - // CIT SCREENSHAKE if(force >= 15) shake_camera(user, ((force - 10) * 0.01 + 1), ((force - 10) * 0.01)) @@ -120,31 +119,21 @@ if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_OBJ, O, user) & COMPONENT_NO_ATTACK_OBJ) return if(item_flags & NOBLUDGEON) - return + return DISCARD_LAST_ACTION + if(!UseStaminaBufferStandard(user, STAM_COST_ATTACK_OBJ_MULT, warn = TRUE)) + return DISCARD_LAST_ACTION user.do_attack_animation(O) O.attacked_by(src, user) - var/weight = getweight(user, STAM_COST_ATTACK_OBJ_MULT) - if(weight) - user.adjustStaminaLossBuffered(weight)//CIT CHANGE - makes attacking things cause stamina loss /atom/movable/proc/attacked_by() return /obj/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) var/totitemdamage = I.force * damage_multiplier - var/bad_trait - - var/stamloss = user.getStaminaLoss() - if(stamloss > STAMINA_NEAR_SOFTCRIT) //The more tired you are, the less damage you do. - var/penalty = (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*STAM_CRIT_ITEM_ATTACK_PENALTY - totitemdamage *= 1 - penalty - - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - bad_trait = SKILL_COMBAT_MODE //blacklist combat skills. if(I.used_skills && user.mind) if(totitemdamage) - totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_trait) + totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ) for(var/skill in I.used_skills) if(!(SKILL_TRAIN_ATTACK_OBJ in I.used_skills[skill])) continue @@ -187,28 +176,15 @@ if(!.) return - var/stamloss = user.getStaminaLoss() var/stam_mobility_mult = 1 - if(stamloss > STAMINA_NEAR_SOFTCRIT) //The more tired you are, the less damage you do. - var/penalty = (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*STAM_CRIT_ITEM_ATTACK_PENALTY - stam_mobility_mult -= penalty if(stam_mobility_mult > LYING_DAMAGE_PENALTY && !CHECK_MOBILITY(user, MOBILITY_STAND)) //damage penalty for fighting prone, doesn't stack with the above. stam_mobility_mult = LYING_DAMAGE_PENALTY . *= stam_mobility_mult - var/bad_trait - if(!(I.item_flags & NO_COMBAT_MODE_FORCE_MODIFIER)) - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - bad_trait = SKILL_COMBAT_MODE //blacklist combat skills. - if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) - . *= 0.8 - else if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - . *= 1.2 - if(!user.mind || !I.used_skills) return if(.) - . = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB, bad_trait) + . = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB) for(var/skill in I.used_skills) if(!(SKILL_TRAIN_ATTACK_MOB in I.used_skills[skill])) continue @@ -263,21 +239,23 @@ . = (total_mass || w_class * STAM_COST_W_CLASS_MULT) * multiplier if(!user) return - var/bad_trait - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - . *= STAM_COST_NO_COMBAT_MULT - bad_trait = SKILL_COMBAT_MODE if(used_skills && user.mind) - . = user.mind.item_action_skills_mod(src, ., skill_difficulty, trait, bad_trait, FALSE) - var/total_health = user.getStaminaLoss() - . = clamp(., 0, STAMINA_NEAR_CRIT - total_health) + . = user.mind.item_action_skills_mod(src, ., skill_difficulty, trait, null, FALSE) + +/** + * Uses the amount of stamina required for a standard hit + */ +/obj/item/proc/UseStaminaBufferStandard(mob/living/user, multiplier = 1, trait = SKILL_STAMINA_COST, warn = TRUE) + ASSERT(user) + var/cost = getweight(user, multiplier, trait) + return user.UseStaminaBuffer(cost, warn) /// How long this staggers for. 0 and negatives supported. /obj/item/proc/melee_stagger_duration(force_override) if(!isnull(stagger_force)) return stagger_force /// totally not an untested, arbitrary equation. - return clamp((1.5 + (w_class/7.5)) * ((force_override || force) / 2), 0, 10 SECONDS) + return clamp((1.5 + (w_class/5)) * ((force_override || force) / 1.5), 0, 10 SECONDS) /obj/item/proc/do_stagger_action(mob/living/target, mob/living/user, force_override) if(!CHECK_BITFIELD(target.status_flags, CANSTAGGER)) diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm index 259082a6ba..ea5c861cd4 100644 --- a/code/controllers/configuration/config_entry.dm +++ b/code/controllers/configuration/config_entry.dm @@ -27,7 +27,10 @@ /datum/config_entry/New() if(type == abstract_type) CRASH("Abstract config entry [type] instatiated!") - name = lowertext(type2top(type)) + if(!name) + name = lowertext(type2top(type)) + else + name = lowertext(name) if(islist(config_entry_value)) var/list/L = config_entry_value default = L.Copy() diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 8f55293820..fdf7ffd663 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -230,14 +230,7 @@ /datum/config_entry/keyed_list/multiplicative_movespeed key_mode = KEY_MODE_TYPE value_mode = VALUE_MODE_NUM - config_entry_value = list( //DEFAULTS - /mob/living/simple_animal = 1, - /mob/living/silicon/pai = 1, - /mob/living/carbon/alien/humanoid/sentinel = 0.25, - /mob/living/carbon/alien/humanoid/drone = 0.5, - /mob/living/carbon/alien/humanoid/royal/praetorian = 1, - /mob/living/carbon/alien/humanoid/royal/queen = 3 - ) + abstract_type = /datum/config_entry/keyed_list/multiplicative_movespeed /datum/config_entry/keyed_list/multiplicative_movespeed/ValidateAndSet() . = ..() @@ -249,6 +242,26 @@ if(. && (var_name == NAMEOF(src, config_entry_value))) update_config_movespeed_type_lookup(TRUE) +/datum/config_entry/keyed_list/multiplicative_movespeed/normal + name = "multiplicative_movespeed" + config_entry_value = list( //DEFAULTS + /mob/living/simple_animal = 1, + /mob/living/silicon/pai = 1, + /mob/living/carbon/alien/humanoid/sentinel = 0.25, + /mob/living/carbon/alien/humanoid/drone = 0.5, + /mob/living/carbon/alien/humanoid/royal/praetorian = 1, + /mob/living/carbon/alien/humanoid/royal/queen = 3 + ) + +/datum/config_entry/keyed_list/multiplicative_movespeed/floating + name = "multiplicative_movespeed_floating" + config_entry_value = list( + /mob/living = 0, + /mob/living/carbon/alien/humanoid = 0, + /mob/living/carbon/alien/humanoid/royal/praetorian = 0, + /mob/living/carbon/alien/humanoid/royal/queen = 2 + ) + /datum/config_entry/number/movedelay //Used for modifying movement speed for mobs. abstract_type = /datum/config_entry/number/movedelay integer = FALSE @@ -280,6 +293,12 @@ /datum/config_entry/number/movedelay/sprint_speed_increase config_entry_value = 1 +/datum/config_entry/number/movedelay/sprint_max_tiles_increase + config_entry_value = 5 + +/datum/config_entry/number/movedelay/sprint_absolute_max_tiles + config_entry_value = 13 + /datum/config_entry/number/movedelay/sprint_buffer_max config_entry_value = 24 @@ -291,7 +310,7 @@ /////////////////////////////////////////////////Outdated move delay /datum/config_entry/number/outdated_movedelay - deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed + deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed/normal abstract_type = /datum/config_entry/number/outdated_movedelay var/movedelay_type diff --git a/code/controllers/configuration/entries/stamina_combat.dm b/code/controllers/configuration/entries/stamina_combat.dm new file mode 100644 index 0000000000..65580899f1 --- /dev/null +++ b/code/controllers/configuration/entries/stamina_combat.dm @@ -0,0 +1,35 @@ +/datum/config_entry/number/stamina_combat + integer = FALSE + abstract_type = /datum/config_entry/number/stamina_combat + +/// Maximum stamina buffer +/datum/config_entry/number/stamina_combat/buffer_max + config_entry_value = 25 + +/// Seconds until percent_regeneration_out_of_combat kicks in +/datum/config_entry/number/stamina_combat/out_of_combat_timer + config_entry_value = 15 + +/// Base regeneration per second +/datum/config_entry/number/stamina_combat/base_regeneration + config_entry_value = 0.5 + +/// Combat mode regeneration per second +/datum/config_entry/number/stamina_combat/combat_regeneration + config_entry_value = 5 + +/// After out_of_combat_timer elapses, additionally regenerate this percent of total stamina per second. Unaffected by combat mode. +/datum/config_entry/number/stamina_combat/percent_regeneration_out_of_combat + config_entry_value = 30 + +/// Seconds after an action for which your regeneration is penalized +/datum/config_entry/number/stamina_combat/post_action_penalty_delay + config_entry_value = 5 + +/// Factor to multiply by for penalizing post-action-stamina-regen +/datum/config_entry/number/stamina_combat/post_action_penalty_factor + config_entry_value = 0.25 + +/// Factor to multiply by for stamina usage past buffer into health +/datum/config_entry/number/stamina_combat/overdraw_penalty_factor + config_entry_value = 1.5 diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index 32936af9e9..bc3f6cf51b 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -64,10 +64,10 @@ SUBSYSTEM_DEF(input) // Misc macroset_classic_input["Tab"] = "\".winset \\\"mainwindow.macro=[SKIN_MACROSET_CLASSIC_HOTKEYS] map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\"" macroset_classic_input["Escape"] = "\".winset \\\"input.text=\\\"\\\"\\\"\"" - + // FINALLY, WE CAN DO SOMETHING MORE NORMAL FOR THE SNOWFLAKE-BUT-LESS KEYSET. - - // HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when + + // HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when // conflicts occur with something like say indicator vs say), we're going to snowflake this anyways var/list/hard_binds = list( "O" = "ooc", @@ -80,7 +80,7 @@ SUBSYSTEM_DEF(input) for(var/key in hard_binds) for(var/modifier in anti_collision_modifiers) hard_bind_anti_collision["[modifier]+[key]"] = ".NONSENSICAL_VERB_THAT_DOES_NOTHING" - + macroset_classic_hotkey = list( "Any" = "\"KeyDown \[\[*\]\]\"", "Any+UP" = "\"KeyUp \[\[*\]\]\"", @@ -88,7 +88,7 @@ SUBSYSTEM_DEF(input) "Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", ) - + macroset_classic_hotkey |= hard_binds macroset_classic_hotkey |= hard_bind_anti_collision @@ -100,7 +100,7 @@ SUBSYSTEM_DEF(input) "Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", ) - + macroset_hotkey |= hard_binds macroset_hotkey |= hard_bind_anti_collision diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index e61a3b3854..0f936bdeff 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -55,9 +55,10 @@ SUBSYSTEM_DEF(job) continue if(!job.config_check()) continue - if(!job.map_check()) //Even though we initialize before mapping, this is fine because the config is loaded at new + if(!job.map_check(SSmapping.config)) //Even though we initialize before mapping, this is fine because the config is loaded at new testing("Removed [job.type] due to map config"); continue + job.process_map_overrides(SSmapping.config) occupations += job name_occupations[job.title] = job type_occupations[J] = job diff --git a/code/controllers/subsystem/minimum_spawns.dm b/code/controllers/subsystem/minimum_spawns.dm new file mode 100644 index 0000000000..caab2b1949 --- /dev/null +++ b/code/controllers/subsystem/minimum_spawns.dm @@ -0,0 +1,144 @@ +SUBSYSTEM_DEF(min_spawns) + name = "Minimum Spawns" /// this hot steaming pile of garbage makes sure theres a minimum of tendrils scattered around + init_order = INIT_ORDER_DEFAULT + flags = SS_BACKGROUND | SS_NO_FIRE | SS_ALWAYS_SHOW_STAT + wait = 2 + var/where_we_droppin_boys_iterations = 0 + var/snaxi_snowflake_check = FALSE + var/list/active_spawns = list() // lavaland, snaxi, etc. primary spawn list + var/list/active_spawns_2 = list() // snaxi underground, etc. secondary spawn list + var/list/valid_mining_turfs = list() // lavaland/snaxi turfs + var/list/valid_mining_turfs_2 = list() // snaxi underground turfs + +GLOBAL_LIST_INIT(minimum_lavaland_spawns, list( + /obj/structure/spawner/lavaland, + /obj/structure/spawner/lavaland/goliath, + /obj/structure/spawner/lavaland/legion, + /mob/living/simple_animal/hostile/megafauna/dragon, + /mob/living/simple_animal/hostile/megafauna/colossus, + /mob/living/simple_animal/hostile/megafauna/bubblegum +)) + +GLOBAL_LIST_INIT(minimum_snow_surface_spawns, list( + /obj/structure/spawner/ice_moon, + /obj/structure/spawner/ice_moon/polarbear +)) +GLOBAL_LIST_INIT(minimum_snow_under_spawns, list( + /obj/structure/spawner/ice_moon/demonic_portal, + /obj/structure/spawner/ice_moon/demonic_portal/ice_whelp, + /obj/structure/spawner/ice_moon/demonic_portal/snowlegion +)) + +// step 1: check for which list(s) to use - done +// step 2: check for caves - done +// step 3: check for mobs - done +// step 4: start throwing shit down - done +// step 5: snaxi support - done? + +/datum/controller/subsystem/min_spawns/Initialize(start_timeofday) + var/list/snaxi_zs_list = SSmapping.levels_by_trait(ZTRAIT_ICE_RUINS) // boy if these things arent mutually exclusive + var/list/lavaland_zs_list = SSmapping.levels_by_trait(ZTRAIT_LAVA_RUINS) // i'm gonna fuckin scream + if(snaxi_zs_list.len) + active_spawns = GLOB.minimum_snow_surface_spawns + active_spawns_2 = GLOB.minimum_snow_under_spawns + snaxi_snowflake_check = TRUE + else if(lavaland_zs_list.len) //todo: recognizing maps that aren't lavaland mining but are also not snaxi + active_spawns = GLOB.minimum_lavaland_spawns + else + return ..() // call it a day i guess + // borrowing this from auxbase code - see code\modules\mining\aux_base.dm + if(snaxi_snowflake_check) + for(var/z_level in SSmapping.levels_by_trait(ZTRAIT_ICE_RUINS)) + for(var/turf/TT in Z_TURFS(z_level)) + if(!isarea(TT.loc)) + continue + var/area/A = TT.loc + if(!A.mob_spawn_allowed) + continue + if(!istype(TT, /turf/open/floor/plating/asteroid)) + continue + if(typesof(/turf/open/lava) in orange(9, TT)) + continue + valid_mining_turfs.Add(TT) + for(var/z_level in SSmapping.levels_by_trait(ZTRAIT_ICE_RUINS_UNDERGROUND)) + for(var/turf/TT in Z_TURFS(z_level)) + if(!isarea(TT.loc)) + continue + var/area/A = TT.loc + if(!A.mob_spawn_allowed) + continue + if(!istype(TT, /turf/open/floor/plating/asteroid)) + continue + if(typesof(/turf/open/lava) in orange(9, TT)) + continue + valid_mining_turfs_2.Add(TT) + else + for(var/z_level in SSmapping.levels_by_trait(ZTRAIT_LAVA_RUINS)) + for(var/turf/TT in Z_TURFS(z_level)) + if(!isarea(TT.loc)) + continue + var/area/A = TT.loc + if(!A.mob_spawn_allowed) + continue + if(!istype(TT, /turf/open/floor/plating/asteroid)) + continue + if(typesof(/turf/open/lava) in orange(9, TT)) + continue + valid_mining_turfs.Add(TT) + if(!valid_mining_turfs.len) + return ..() // call it a day i guess + // if we're at this point we might as well fucking hit it + where_we_droppin_boys() + return ..() + +/datum/controller/subsystem/min_spawns/proc/where_we_droppin_boys() + while(active_spawns.len) + where_we_droppin_boys_iterations++ + CHECK_TICK + if(where_we_droppin_boys_iterations >= 1250) + INIT_ANNOUNCE("Minimum Spawns subsystem stopped early on spawns list 1 - too many iterations!") + break + var/turf/RT = pick_n_take(valid_mining_turfs) //Pick a random mining Z-level turf + var/MS_tospawn = pick_n_take(active_spawns) + for(var/mob/living/simple_animal/hostile/megafauna/H in urange(70,RT)) //prevents mob clumps + if((istype(MS_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT, H) <= 70) + active_spawns.Add(MS_tospawn) + continue //let's try not to dump megas too close to each other? + if((istype(MS_tospawn, /obj/structure/spawner)) && get_dist(RT, H) <= 40) + active_spawns.Add(MS_tospawn) + continue //let's at least /try/ to space these out? + for(var/obj/structure/spawner/LT in urange(70,RT)) //prevents tendril/mega clumps + if((istype(MS_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT, LT) <= 70) + active_spawns.Add(MS_tospawn) + continue //let's try not to dump megas too close to each other? + if((istype(MS_tospawn, /obj/structure/spawner)) && get_dist(RT, LT) <= 40) + active_spawns.Add(MS_tospawn) + continue //let's at least /try/ to space these out? + // man the overhead on this is gonna SUCK + new MS_tospawn(RT) + while(active_spawns_2.len) + where_we_droppin_boys_iterations++ + CHECK_TICK + if(where_we_droppin_boys_iterations >= 1250) + INIT_ANNOUNCE("Minimum Spawns subsystem stopped early on active list 2 - too many iterations!") + break + var/turf/RT2 = pick_n_take(valid_mining_turfs_2) //Pick a random mining Z-level turf + var/MS2_tospawn = pick_n_take(active_spawns_2) + for(var/mob/living/simple_animal/hostile/H in urange(70,RT2)) //prevents mob clumps + if((istype(MS2_tospawn, /mob/living/simple_animal/hostile/megafauna) || ismegafauna(H)) && get_dist(RT2, H) <= 70) + active_spawns_2.Add(MS2_tospawn) + continue //let's try not to dump megas too close to each other? + if((istype(MS2_tospawn, /obj/structure/spawner)) && get_dist(RT2, H) <= 40) + active_spawns_2.Add(MS2_tospawn) + continue //let's at least /try/ to space these out? + for(var/obj/structure/spawner/LT in urange(70,RT2)) //prevents tendril/mega clumps + if((istype(MS2_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT2, LT) <= 70) + active_spawns_2.Add(MS2_tospawn) + continue //let's try not to dump megas too close to each other? + if((istype(MS2_tospawn, /obj/structure/spawner)) && get_dist(RT2, LT) <= 40) + active_spawns_2.Add(MS2_tospawn) + continue //let's at least /try/ to space these out? + // man the overhead on this is gonna SUCK + new MS2_tospawn(RT2) + if(!active_spawns.len && !active_spawns_2.len) + return // we're done here diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm index c67deaede1..c20820c092 100644 --- a/code/controllers/subsystem/npcpool.dm +++ b/code/controllers/subsystem/npcpool.dm @@ -5,6 +5,10 @@ SUBSYSTEM_DEF(npcpool) runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME var/list/currentrun = list() + /// catches sleeping + var/invoking = FALSE + /// Invoke start time + var/invoke_start = 0 /datum/controller/subsystem/npcpool/stat_entry(msg) var/list/activelist = GLOB.simple_animals[AI_ON] @@ -12,7 +16,6 @@ SUBSYSTEM_DEF(npcpool) return ..() /datum/controller/subsystem/npcpool/fire(resumed = FALSE) - if (!resumed) var/list/activelist = GLOB.simple_animals[AI_ON] src.currentrun = activelist.Copy() @@ -24,12 +27,22 @@ SUBSYSTEM_DEF(npcpool) var/mob/living/simple_animal/SA = currentrun[currentrun.len] --currentrun.len - if(!SA.ckey && !SA.mob_transforming) - if(SA.stat != DEAD) - SA.handle_automated_movement() - if(SA.stat != DEAD) - SA.handle_automated_action() - if(SA.stat != DEAD) - SA.handle_automated_speech() + invoking = TRUE + invoke_start = world.time + INVOKE_ASYNC(src, .proc/invoke_process, SA) + if(invoking) + stack_trace("WARNING: [SA] ([SA.type]) slept during NPCPool processing.") + invoking = FALSE + if (MC_TICK_CHECK) return + +/datum/controller/subsystem/npcpool/proc/invoke_process(mob/living/simple_animal/SA) + if(!SA.ckey && !SA.mob_transforming) + if(SA.stat != DEAD) + SA.handle_automated_movement() + if(SA.stat != DEAD) + SA.handle_automated_action() + if(SA.stat != DEAD) + SA.handle_automated_speech() + invoking = FALSE diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm index f556f7dee7..1dea40a802 100644 --- a/code/controllers/subsystem/traumas.dm +++ b/code/controllers/subsystem/traumas.dm @@ -198,7 +198,8 @@ SUBSYSTEM_DEF(traumas) "aliens" = typecacheof(list(/datum/species/abductor, /datum/species/jelly, /datum/species/pod, /datum/species/shadow)), "anime" = typecacheof(list(/datum/species/human/felinid)), "cats" = typecacheof(list(/datum/species/human/felinid)), - "syndicate" = typecacheof(list(/datum/species/corporate, /datum/species/zombie/infectious)) + "syndicate" = typecacheof(list(/datum/species/corporate, /datum/species/zombie/infectious)), + "arachnid" = typecacheof(list(/datum/species/arachnid)) ) return ..() diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm index d138cf1971..72cf71496b 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -34,7 +34,7 @@ var/obj/item/bodypart/O = H.get_bodypart(picked_def_zone) if(!istype(O)) return - if(O.status == BODYPART_ROBOTIC) + if(O.is_robotic_limb()) return var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET) || (H.shoes && (H.shoes.body_parts_covered & FEET))) diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index c0c312cbc2..6835cb13d4 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -49,7 +49,7 @@ BONUS var/mob/living/carbon/M = A.affected_mob var/picked_bodypart = pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) var/obj/item/bodypart/bodypart = M.get_bodypart(picked_bodypart) - if(bodypart && bodypart.status == BODYPART_ORGANIC && !bodypart.is_pseudopart) //robotic limbs will mean less scratching overall + if(bodypart && bodypart.is_organic_limb() && !bodypart.is_pseudopart) //robotic limbs will mean less scratching overall var/can_scratch = scratch && !M.incapacitated() && get_location_accessible(M, picked_bodypart) M.visible_message("[can_scratch ? "[M] scratches [M.p_their()] [bodypart.name]." : ""]", "Your [bodypart.name] itches. [can_scratch ? " You scratch it." : ""]") if(can_scratch) diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index e90eededd9..f952307685 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -236,12 +236,12 @@ switch(stage) if(1) if(ishuman(affected_mob) && affected_mob.dna) - if(affected_mob.dna.species.id == "slime" || affected_mob.dna.species.id == "stargazer" || affected_mob.dna.species.id == "lum") + if(affected_mob.dna.species.id == "slime" || affected_mob.dna.species.id == "stargazer" || affected_mob.dna.species.id == "slimelumi") stage = 5 if(3) if(ishuman(affected_mob)) var/mob/living/carbon/human/human = affected_mob - if(human.dna.species.id != "slime" && affected_mob.dna.species.id != "stargazer" && affected_mob.dna.species.id != "lum") + if(human.dna.species.id != "slime" && affected_mob.dna.species.id != "stargazer" && affected_mob.dna.species.id != "slimelumi") human.set_species(/datum/species/jelly/slime) /datum/disease/transformation/corgi diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 843ea06f74..cd90b554ac 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -76,6 +76,9 @@ new_dna.features = features.Copy() new_dna.species = new species.type new_dna.species.say_mod = species.say_mod + new_dna.species.exotic_blood_color = species.exotic_blood_color //it can change from the default value + new_dna.species.eye_type = species.eye_type + new_dna.species.limbs_id = species.limbs_id || species.id new_dna.real_name = real_name new_dna.nameless = nameless new_dna.custom_species = custom_species diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index 7d884344f1..81b7ea0628 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -130,7 +130,7 @@ playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE) P.firer = A P.setAngle(rand(0, 360))//SHING - A.adjustStaminaLossBuffered (3) //Citadel change to stop infinite bullet sponging as you run away, but it is buffered! + A.adjustStaminaLoss(3) return BULLET_ACT_FORCE_PIERCE return BULLET_ACT_HIT diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index bd9ecaeeaa..c859d38587 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -342,8 +342,9 @@ to_chat(user, "You can't lay webs here!") failed = TRUE var/turf/T = get_turf(user) - var/obj/structure/spider/stickyweb/genetic/W = locate() in T - if(W) + var/obj/structure/spider/stickyweb/W = locate() in T + var/obj/structure/arachnid/W2 = locate() in T + if(W || W2) to_chat(user, "There's already a web here!") failed = TRUE if(failed) diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index 933eaf082e..a08b535aff 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -114,14 +114,12 @@ description = "..." suffix = "lavaland_surface_sloth.dmm" // Generates nothing but atmos runtimes and salt - cost = 0 /datum/map_template/ruin/lavaland/ratvar name = "Dead God" id = "ratvar" - description = "Ratvars final resting place." + description = "Ratvar's final resting place." suffix = "lavaland_surface_dead_ratvar.dmm" - cost = 0 allow_duplicates = FALSE /datum/map_template/ruin/lavaland/hierophant @@ -137,7 +135,7 @@ id = "blooddrunk" description = "A strange arrangement of stone tiles and an insane, beastly miner contemplating them." suffix = "lavaland_surface_blooddrunk1.dmm" - cost = 0 + always_place = TRUE allow_duplicates = FALSE //will only spawn one variant of the ruin /datum/map_template/ruin/lavaland/blood_drunk_miner/guidance diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm index 43190ef50c..6248be0de5 100644 --- a/code/datums/weather/weather_types/ash_storm.dm +++ b/code/datums/weather/weather_types/ash_storm.dm @@ -95,8 +95,8 @@ if(is_ash_immune(L)) return if(is_species(L, /datum/species/lizard/ashwalker)) - if(!IS_STAMCRIT(L)) - L.adjustStaminaLossBuffered(4) + if(L.getStaminaLoss() < (STAMINA_CRIT - 40)) + L.adjustStaminaLoss(4) return L.adjustFireLoss(4) diff --git a/code/datums/wounds/_scars.dm b/code/datums/wounds/_scars.dm index 85589976e6..8cd0d8a047 100644 --- a/code/datums/wounds/_scars.dm +++ b/code/datums/wounds/_scars.dm @@ -79,7 +79,7 @@ /// Used to "load" a persistent scar /datum/scar/proc/load(obj/item/bodypart/BP, version, description, specific_location, severity=WOUND_SEVERITY_SEVERE) - if(!(BP.body_zone in applicable_zones) || !(BP.is_organic_limb() || BP.render_like_organic)) + if(!(BP.body_zone in applicable_zones) || !BP.is_organic_limb()) qdel(src) return diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm index 1ed0d98543..29c87b32d4 100644 --- a/code/datums/wounds/_wounds.dm +++ b/code/datums/wounds/_wounds.dm @@ -110,7 +110,7 @@ * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented) */ /datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE) - if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner) || !(L.is_organic_limb() || L.render_like_organic)) + if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner) || !L.is_organic_limb()) qdel(src) return diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 0a6c2b9eca..4715c3bed3 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -36,6 +36,7 @@ var/grab_state = 0 var/throwforce = 0 var/datum/component/orbiter/orbiting + /// Used for space ztransit stuff var/can_be_z_moved = TRUE ///If we were without gravity and another animation happened, the bouncing will stop, and we need to restart it in next life(). var/floating_need_update = FALSE diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 10b92d9655..a8ed4611eb 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -76,6 +76,13 @@ GLOBAL_LIST_EMPTY(objectives) /datum/objective/proc/check_completion() return completed +/* +Used during the round to check if an objective has already been completed, generally should have harsher requirements that the default objective (no true because of short afk, etc) +If not set, defaults to check_completion instead. Set it. It's used by cryo. +*/ +/datum/objective/proc/check_midround_completion() + return check_completion() + /datum/objective/proc/is_unique_objective(possible_target) var/list/datum/mind/owners = get_owners() for(var/datum/mind/M in owners) @@ -175,6 +182,9 @@ GLOBAL_LIST_EMPTY(objectives) /datum/objective/assassinate/check_completion() return !considered_alive(target) || considered_afk(target) +/datum/objective/assassinate/check_midround_completion() + return FALSE //They need to be dead at the end of the round, silly! + /datum/objective/assassinate/update_explanation_text() ..() if(target && target.current) @@ -201,13 +211,16 @@ GLOBAL_LIST_EMPTY(objectives) return won || ..() /datum/objective/assassinate/once/process() - won = check_midround_completion() + won = tick_check_completion() if(won) STOP_PROCESSING(SSprocessing,src) -/datum/objective/assassinate/once/proc/check_midround_completion() +/datum/objective/assassinate/once/proc/tick_check_completion() return won || !considered_alive(target) //The target afking / logging off for a bit during the round doesn't complete it, but them being afk at roundend does. +/datum/objective/assassinate/once/check_midround_completion() + return won //If they cryoed, only keep it if we already won + /datum/objective/assassinate/internal var/stolen = 0 //Have we already eliminated this target? @@ -233,6 +246,9 @@ GLOBAL_LIST_EMPTY(objectives) var/turf/T = get_turf(target.current) return !T || !is_station_level(T.z) +/datum/objective/mutiny/check_midround_completion() + return FALSE + /datum/objective/mutiny/update_explanation_text() ..() if(target && target.current) @@ -252,7 +268,10 @@ GLOBAL_LIST_EMPTY(objectives) return target /datum/objective/maroon/check_completion() - return !target || !considered_alive(target) || (!target.current.onCentCom() && !target.current.onSyndieBase()) + return !target || !considered_alive(target) || (!target.current?.onCentCom() && !target.current?.onSyndieBase()) + +/datum/objective/maroon/check_midround_completion() + return FALSE /datum/objective/maroon/update_explanation_text() if(target && target.current) @@ -313,6 +332,9 @@ GLOBAL_LIST_EMPTY(objectives) /datum/objective/protect/check_completion() return !target || considered_alive(target, enforce_human = human_check) +/datum/objective/protect/check_midround_completion() + return FALSE //Nuh uh, you get a new objective + /datum/objective/protect/update_explanation_text() ..() if(target && target.current) @@ -412,6 +434,9 @@ GLOBAL_LIST_EMPTY(objectives) /datum/objective/breakout/check_completion() return !target || considered_escaped(target) +/datum/objective/breakout/check_midround_completion() + return FALSE + /datum/objective/breakout/find_target_by_role(role, role_type=0, invert=0) if(!invert) target_role_type = role_type @@ -462,6 +487,9 @@ GLOBAL_LIST_EMPTY(objectives) return TRUE return FALSE +/datum/objective/escape/escape_with_identity/check_midround_completion() + return FALSE + /datum/objective/escape/escape_with_identity/admin_edit(mob/admin) admin_simple_target_pick(admin) @@ -622,6 +650,8 @@ GLOBAL_LIST_EMPTY(possible_items_special) explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" steal_target = targetinfo.targetitem +/datum/objective/steal/exchange/check_midround_completion() + return FALSE /datum/objective/steal/exchange/update_explanation_text() ..() @@ -826,6 +856,9 @@ GLOBAL_LIST_EMPTY(possible_items_special) return target.current.stat == DEAD || target.current.z > 6 || !target.current.ckey //Borgs/brains/AIs count as dead for traitor objectives. return TRUE +/datum/objective/destroy/check_midround_completion() + return FALSE + /datum/objective/destroy/update_explanation_text() ..() if(target && target.current) diff --git a/code/game/machinery/aug_manipulator.dm b/code/game/machinery/aug_manipulator.dm index 28733a6b6c..50b6180d62 100644 --- a/code/game/machinery/aug_manipulator.dm +++ b/code/game/machinery/aug_manipulator.dm @@ -59,7 +59,7 @@ else if(istype(O, /obj/item/bodypart)) var/obj/item/bodypart/B = O - if(B.status != BODYPART_ROBOTIC) + if(!B.is_robotic_limb(FALSE)) to_chat(user, "The machine only accepts cybernetics!") return if(storedpart) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 2a05b359d8..7057b8b02d 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -12,6 +12,7 @@ var/list/pods //Linked cloning pods var/temp = "Inactive" var/scantemp_ckey + var/scantemp_name var/scantemp = "Ready to Scan" var/menu = 1 //Which menu screen to display var/datum/data/record/active_record = null @@ -195,9 +196,10 @@ dat += "[scanner_occupant] => Scanning..." else if(use_records) - if(scanner_occupant.ckey != scantemp_ckey) + if(scanner_occupant.ckey != scantemp_ckey || scanner_occupant.name != scantemp_name) scantemp = "Ready to Scan" scantemp_ckey = scanner_occupant.ckey + scantemp_name = scanner_occupant.name else scantemp = "Ready to Clone" dat += "[scanner_occupant] => [scantemp]" @@ -296,17 +298,18 @@ autoprocess = FALSE STOP_PROCESSING(SSmachines, src) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + src.updateUsrDialog() . = TRUE else if ((href_list["scan"]) && !isnull(scanner) && scanner.is_operational()) scantemp = "" loading = TRUE - src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) say("Initiating scan...") var/prev_locked = scanner.locked scanner.locked = TRUE + src.updateUsrDialog() addtimer(CALLBACK(src, .proc/finish_scan, scanner.occupant, prev_locked), 2 SECONDS) . = TRUE @@ -318,6 +321,7 @@ else scanner.locked = FALSE playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + src.updateUsrDialog() . = TRUE @@ -340,6 +344,7 @@ src.menu = 3 else src.temp = "Record missing." + src.updateUsrDialog() . = TRUE else if (href_list["del_rec"]) @@ -348,6 +353,7 @@ if (src.menu == 3) //If we are viewing a record, confirm deletion src.temp = "Delete record?" src.menu = 4 + src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) else if (src.menu == 4) @@ -357,12 +363,14 @@ src.temp = "[src.active_record.fields["name"]] => Record deleted." src.records.Remove(active_record) active_record = null + src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) src.menu = 2 var/obj/item/circuitboard/computer/cloning/board = circuit board.records = records else src.temp = "Access Denied." + src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) . = TRUE @@ -384,6 +392,7 @@ for(var/key in diskette.fields) src.active_record.fields[key] = diskette.fields[key] src.temp = "Load successful." + src.updateUsrDialog() var/obj/item/circuitboard/computer/cloning/board = circuit board.records = records playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) @@ -403,6 +412,7 @@ diskette.fields = active_record.fields.Copy() diskette.name = "data disk - '[src.diskette.fields["name"]]'" src.temp = "Save successful." + src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) . = TRUE @@ -431,17 +441,21 @@ if(active_record == C) active_record = null menu = 1 + src.updateUsrDialog() else temp = "[C.fields["name"]] => Initialisation failure." + src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else temp = "Data corruption." + src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) . = TRUE else if (href_list["menu"] && use_records) menu = text2num(href_list["menu"]) + src.updateUsrDialog() playsound(src, "terminal_type", 25, 0) . = TRUE @@ -449,7 +463,6 @@ if(!scanner || !L) return src.add_fingerprint(usr) - src.updateUsrDialog() if(use_records) scan_occupant(L) @@ -457,9 +470,10 @@ clone_occupant(L) loading = FALSE + scanner.locked = prev_locked src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - scanner.locked = prev_locked + /obj/machinery/computer/cloning/proc/scan_occupant(occupant) var/mob/living/mob_occupant = get_mob_or_brainmob(occupant) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index b48742d885..a2a395c51c 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -361,7 +361,7 @@ // them win or lose based on cryo is silly so we remove the objective. if(istype(O,/datum/objective/mutiny) && O.target == mob_occupant.mind) qdel(O) - else if(O.target && istype(O.target, /datum/mind) && !O.check_completion()) + else if(O.target && istype(O.target, /datum/mind) && !O.check_midround_completion()) if(O.target == mob_occupant.mind && O.owner?.current) to_chat(O.owner.current, "
You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!") O.target = null diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm index 12fdc2193b..6aeef4b6ac 100644 --- a/code/game/machinery/wishgranter.dm +++ b/code/game/machinery/wishgranter.dm @@ -84,7 +84,7 @@ if(is_station_level(T.z)) destinations += B var/chosen_beacon = pick(destinations) - var/obj/effect/portal/jaunt_tunnel/J = new (get_turf(src), src, 100, null, FALSE, get_turf(chosen_beacon)) + var/obj/effect/portal/jaunt_tunnel/J = new (get_turf(src), 100, null, FALSE, get_turf(chosen_beacon)) try_move_adjacent(J) playsound(src,'sound/effects/sparks4.ogg',50,1) charges-- diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm index 8039aabd1f..4dbb57479f 100644 --- a/code/game/mecha/equipment/tools/other_tools.dm +++ b/code/game/mecha/equipment/tools/other_tools.dm @@ -56,7 +56,7 @@ var/turf/target_turf = pick(L) if(!target_turf) return - var/list/obj/effect/portal/created = create_portal_pair(get_turf(src), target_turf, src, 300, 1, /obj/effect/portal/anom) + var/list/obj/effect/portal/created = create_portal_pair(get_turf(src), target_turf, 300, 1, /obj/effect/portal/anom) var/turf/T = get_turf(target) message_admins("[ADMIN_LOOKUPFLW(chassis.occupant)] used a Wormhole Generator in [ADMIN_VERBOSEJMP(T)]") log_game("[key_name(chassis.occupant)] used a Wormhole Generator in [AREACOORD(T)]") diff --git a/code/game/objects/effects/arachnid_web.dm b/code/game/objects/effects/arachnid_web.dm new file mode 100644 index 0000000000..2adaa12dc9 --- /dev/null +++ b/code/game/objects/effects/arachnid_web.dm @@ -0,0 +1,70 @@ +/obj/structure/arachnid + name = "large web" + icon = 'icons/effects/effects.dmi' + desc = "It's stringy and sticky, but the threads are larger than what spiderlings could produce." + anchored = TRUE + density = FALSE + max_integrity = 20 + +/obj/structure/arachnid/New() + ..() + icon_state = pick(list("stickyweb1", "stickyweb2")) + + +/obj/structure/arachnid/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + if(damage_type == BURN)//the stickiness of the web mutes all attack sounds except fire damage type + playsound(loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/arachnid/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(damage_flag == "melee") + switch(damage_type) + if(BURN) + damage_amount *= 2 + if(BRUTE) + damage_amount *= 0.5 + . = ..() + +/obj/structure/arachnid/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > 300) + take_damage(5, BURN, 0, 0) + +/obj/structure/arachnid/CanPass(atom/movable/mover, turf/target) + . = ..() + if(isarachnid(mover)) + + return TRUE + else if(isliving(mover)) + if(isarachnid(mover.pulledby)) + + return TRUE + if(prob(20)) + to_chat(mover, "You get stuck in \the [src] for a moment.") + return FALSE + +/obj/structure/arachnid/cocoon + name = "cocoon" + desc = "Something wrapped in silky spider web." + icon_state = "cocoon1" + anchored = FALSE + density = FALSE + max_integrity = 60 + +/obj/structure/arachnid/cocoon/Initialize() + icon_state = pick("cocoon1","cocoon2","cocoon3") + . = ..() + +/obj/structure/arachnid/cocoon/container_resist(mob/living/user) + var/breakout_time = 150 // DECI not DECA ffs + to_chat(user, "You struggle against the tight bonds... (This will take about [DisplayTimeText(breakout_time)].)") + visible_message("You see something struggling and writhing in \the [src]!") + if(do_after(user,(breakout_time), target = src)) + if(!user || user.stat != CONSCIOUS || user.loc != src) + return + qdel(src) + +/obj/structure/arachnid/cocoon/Destroy() + var/turf/T = get_turf(src) + src.visible_message("\The [src] splits open.") + for(var/atom/movable/A in contents) + A.forceMove(T) + return ..() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index b4c114eb6a..1c75f1e533 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -315,7 +315,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb return if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS) if(current_equipped_slot in user.check_obscured_slots()) - to_chat(src, "You are unable to unequip that while wearing other garments over it!") + to_chat(user, "You are unable to unequip that while wearing other garments over it!") return FALSE if(resistance_flags & ON_FIRE) @@ -383,7 +383,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb return if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS) if(current_equipped_slot in user.check_obscured_slots()) - to_chat(src, "You are unable to unequip that while wearing other garments over it!") + to_chat(user, "You are unable to unequip that while wearing other garments over it!") return FALSE @@ -565,7 +565,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb to_chat(user, "You cannot locate any organic eyes on this brain!") return - if(IS_STAMCRIT(user))//CIT CHANGE - makes eyestabbing impossible if you're in stamina softcrit + if(IS_STAMCRIT(user) || !user.UseStaminaBuffer(STAMINA_COST_ITEM_EYESTAB, warn = TRUE))//CIT CHANGE - makes eyestabbing impossible if you're in stamina softcrit to_chat(user, "You're too exhausted for that.")//CIT CHANGE - ditto return //CIT CHANGE - ditto @@ -575,8 +575,6 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb user.do_attack_animation(M) - user.adjustStaminaLossBuffered(10)//CIT CHANGE - makes eyestabbing cost stamina - if(M != user) M.visible_message("[user] has stabbed [M] in the eye with [src]!", \ "[user] stabs you in the eye with [src]!") diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm index 484901990a..4777796331 100644 --- a/code/game/objects/items/defib.dm +++ b/code/game/objects/items/defib.dm @@ -4,7 +4,7 @@ /obj/item/defibrillator name = "defibrillator" desc = "A device that delivers powerful shocks to detachable paddles that resuscitate incapacitated patients." - icon = 'icons/obj/items_and_weapons.dmi' + icon = 'icons/obj/defibrillators.dmi' icon_state = "defibunit" item_state = "defibunit" lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' @@ -23,8 +23,8 @@ var/obj/item/stock_parts/cell/cell var/combat = FALSE //can we revive through space suits? var/grab_ghost = FALSE // Do we pull the ghost back into their body? - var/healdisk = FALSE // Will we shock people dragging the body? - var/pullshocksafely = FALSE //Dose the unit have the healdisk upgrade? + var/healdisk = FALSE // Does the unit have the healdisk upgrade? + var/pullshocksafely = FALSE // Will we shock people dragging the body? var/primetime = 0 // is the defib faster var/timedeath = 10 var/disarm_shock_time = 10 @@ -261,7 +261,7 @@ /obj/item/shockpaddles name = "defibrillator paddles" desc = "A pair of plastic-gripped paddles with flat metal surfaces that are used to deliver powerful electric shocks." - icon = 'icons/obj/items_and_weapons.dmi' + icon = 'icons/obj/defibrillators.dmi' icon_state = "defibpaddles0" item_state = "defibpaddles0" lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' @@ -682,7 +682,7 @@ /obj/item/shockpaddles/cyborg name = "cyborg defibrillator paddles" - icon = 'icons/obj/items_and_weapons.dmi' + icon = 'icons/obj/defibrillators.dmi' icon_state = "defibpaddles0" item_state = "defibpaddles0" req_defib = FALSE @@ -703,7 +703,7 @@ name = "syndicate defibrillator paddles" desc = "A pair of paddles used to revive deceased operatives. It possesses both the ability to penetrate armor and to deliver powerful shocks offensively." combat = TRUE - icon = 'icons/obj/items_and_weapons.dmi' + icon = 'icons/obj/defibrillators.dmi' icon_state = "defibpaddles0" item_state = "defibpaddles0" req_defib = FALSE diff --git a/code/game/objects/items/electrostaff.dm b/code/game/objects/items/electrostaff.dm index 65d2fdd699..31aaff12b5 100644 --- a/code/game/objects/items/electrostaff.dm +++ b/code/game/objects/items/electrostaff.dm @@ -21,12 +21,11 @@ var/can_block_projectiles = FALSE //can't block guns var/lethal_cost = 400 //10000/400*20 = 500. decent enough? var/lethal_damage = 20 - var/lethal_stam_cost = 4 var/stun_cost = 333 //10000/333*25 = 750. stunbatons are at time of writing 10000/1000*49 = 490. var/stun_status_effect = STATUS_EFFECT_ELECTROSTAFF //a small slowdown effect var/stun_stamdmg = 40 var/stun_status_duration = 25 - var/stun_stam_cost = 3.5 + var/stam_cost = 3.5 var/wielded = FALSE // track wielded status on item // haha security desword time /s @@ -171,7 +170,7 @@ turn_off() /obj/item/electrostaff/attack(mob/living/target, mob/living/user) - if(IS_STAMCRIT(user))//CIT CHANGE - makes it impossible to baton in stamina softcrit + if(IS_STAMCRIT(user) || !user.UseStaminaBuffer(stam_cost))//CIT CHANGE - makes it impossible to baton in stamina softcrit to_chat(user, "You're too exhausted to use [src] properly.")//CIT CHANGE - ditto return //CIT CHANGE - ditto if(on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) @@ -186,13 +185,11 @@ if(user.a_intent != INTENT_HARM) if(stun_act(target, user, null, return_list)) user.do_attack_animation(target) - user.adjustStaminaLossBuffered(stun_stam_cost) return else if(!harm_act(target, user, null, return_list)) return ..() //if you can't fry them just beat them with it else //we did harm act them user.do_attack_animation(target) - user.adjustStaminaLossBuffered(lethal_stam_cost) /obj/item/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list()) var/stunforce = block_calculate_resultant_damage(stun_stamdmg, block_return) diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index 679491aeb6..7d95b8e736 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -190,12 +190,53 @@ /obj/item/melee/transforming/energy/sword/saber possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER) + unique_reskin = list("Sword" = "sword0", "saber" = "esaber0") var/hacked = FALSE + var/saber = FALSE -/obj/item/melee/transforming/energy/sword/saber/set_sword_color() - if(LAZYLEN(possible_colors)) +/obj/item/melee/transforming/energy/sword/saber/transform_weapon(mob/living/user, supress_message_text) + . = ..() + if(.) + if(active) + if(sword_color) + if(saber) + icon_state = "esaber[sword_color]" + else + icon_state = "sword[sword_color]" + else + if(saber) + icon_state = "esaber0" + else + icon_state = "sword0" + +/obj/item/melee/transforming/energy/sword/saber/reskin_obj(mob/M) + . = ..() + if(icon_state == "esaber0") + saber = TRUE + if(active) + if(saber) + icon_state = "esaber[sword_color]" + else + icon_state = "sword[sword_color]" + +/obj/item/melee/transforming/energy/sword/saber/set_sword_color(var/color_forced) + if(color_forced) // wow i really do not like this at fucking all holy SHIT + if(color_forced == "red") + sword_color = "red" + light_color = LIGHT_COLOR_RED + else if(color_forced == "blue") + sword_color = "blue" + light_color = LIGHT_COLOR_LIGHT_CYAN + else if(color_forced == "green") + sword_color = "green" + light_color = LIGHT_COLOR_GREEN + else if(color_forced == "purple") + sword_color = "purple" + light_color = LIGHT_COLOR_LAVENDER + else if(LAZYLEN(possible_colors)) sword_color = pick(possible_colors) light_color = possible_colors[sword_color] + return /obj/item/melee/transforming/energy/sword/saber/process() . = ..() @@ -204,30 +245,59 @@ light_color = possible_colors[set_color] update_light() -/obj/item/melee/transforming/energy/sword/saber/red - possible_colors = list("red" = LIGHT_COLOR_RED) +/obj/item/melee/transforming/energy/sword/saber/red/Initialize(mapload) + . = ..() + set_sword_color("red") -/obj/item/melee/transforming/energy/sword/saber/blue - possible_colors = list("blue" = LIGHT_COLOR_LIGHT_CYAN) +/obj/item/melee/transforming/energy/sword/saber/blue/Initialize(mapload) + . = ..() + set_sword_color("blue") -/obj/item/melee/transforming/energy/sword/saber/green - possible_colors = list("green" = LIGHT_COLOR_GREEN) +/obj/item/melee/transforming/energy/sword/saber/green/Initialize(mapload) + . = ..() + set_sword_color("green") -/obj/item/melee/transforming/energy/sword/saber/purple - possible_colors = list("purple" = LIGHT_COLOR_LAVENDER) +/obj/item/melee/transforming/energy/sword/saber/purple/Initialize(mapload) + . = ..() + set_sword_color("purple") + +/obj/item/melee/transforming/energy/sword/saber/proc/select_sword_color(mob/user) /// this is for the radial + if(!istype(user) || user.incapacitated()) + return + + var/static/list/options = list( + "red" = image(icon = 'icons/obj/items_and_weapons.dmi', icon_state = "swordred-blade"), + "blue" = image(icon = 'icons/obj/items_and_weapons.dmi', icon_state = "swordblue-blade"), + "green" = image(icon = 'icons/obj/items_and_weapons.dmi', icon_state = "swordgreen-blade"), + "purple" = image(icon = 'icons/obj/items_and_weapons.dmi', icon_state = "swordpurple-blade") + ) + + var/choice = show_radial_menu(user, src, options, custom_check = FALSE, radius = 36, require_near = TRUE) + + if(src && choice && !user.incapacitated() && in_range(user,src)) + set_sword_color(choice) + to_chat(user, "[src] is now [choice].") /obj/item/melee/transforming/energy/sword/saber/attackby(obj/item/W, mob/living/user, params) if(istype(W, /obj/item/multitool)) + if(user.a_intent == INTENT_DISARM) + if(!active) + to_chat(user, "COLOR_SET") + hacked = FALSE + select_sword_color(user) + return + else + to_chat(user, "Turn it off first - getting that close to an active sword is not a great idea.") + return if(!hacked) hacked = TRUE sword_color = "rainbow" to_chat(user, "RNBW_ENGAGE") - if(active) icon_state = "swordrainbow" user.update_inv_hands() else - to_chat(user, "It's already fabulous!") + to_chat(user, "It's already fabulous! If you wanted to reset the color, though, try a disarming intent while it's off.") else return ..() diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 52ff0f740e..15d10c4d11 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -352,6 +352,8 @@ return else if(cooldown_check < world.time) + if(!UseStaminaBufferStandard(user, STAM_COST_BATON_MOB_MULT, warn = TRUE)) + return DISCARD_LAST_ACTION if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) playsound(target, 'sound/weapons/genhit.ogg', 50, 1) return @@ -373,7 +375,6 @@ else target.LAssailant = WEAKREF(user) cooldown_check = world.time + cooldown - user.adjustStaminaLossBuffered(getweight(user, STAM_COST_BATON_MOB_MULT)) else var/wait_desc = get_wait_description() if(wait_desc) diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm index b420bfc002..01ef96b7e8 100644 --- a/code/game/objects/items/mop.dm +++ b/code/game/objects/items/mop.dm @@ -56,22 +56,20 @@ return if(T) + if(!L.UseStaminaBuffer(stamusage, warn = TRUE)) + return user.visible_message("[user] cleans \the [T] with [src].", "You clean \the [T] with [src].") clean(T) user.DelayNextAction(CLICK_CD_MELEE) user.do_attack_animation(T, used_item = src) - if(istype(L)) - L.adjustStaminaLossBuffered(stamusage) playsound(T, "slosh", 50, 1) - /obj/effect/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/mop) || istype(I, /obj/item/soap)) return else return ..() - /obj/item/mop/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) if(insertable) J.put_in_cart(src, user) diff --git a/code/game/objects/items/powerfist.dm b/code/game/objects/items/powerfist.dm index 2834b3b758..bd83404356 100644 --- a/code/game/objects/items/powerfist.dm +++ b/code/game/objects/items/powerfist.dm @@ -6,7 +6,7 @@ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' flags_1 = CONDUCT_1 - item_flags = NEEDS_PERMIT | NO_COMBAT_MODE_FORCE_MODIFIER //To avoid ambushing and oneshotting healthy crewmembers on force setting 3. + item_flags = NEEDS_PERMIT attack_verb = list("whacked", "fisted", "power-punched") force = 20 throwforce = 10 @@ -76,6 +76,9 @@ if(!tank) to_chat(user, "\The [src] can't operate without a source of gas!") return FALSE + var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT) + if(!user.UseStaminaBuffer(weight, warn = TRUE)) + return FALSE var/datum/gas_mixture/gasused = tank.air_contents.remove(gasperfist * fisto_setting) var/turf/T = get_turf(src) if(!T) @@ -108,8 +111,4 @@ target.throw_at(throw_target, 5 * fisto_setting, 0.5 + (fisto_setting / 2)) log_combat(user, target, "power fisted", src) - - var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT) - if(weight) - user.adjustStaminaLossBuffered(weight) return TRUE diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 4952c93928..d1d57fe375 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -136,15 +136,17 @@ if(!(shield_flags & SHIELD_BASH_GROUND_SLAM)) to_chat(user, "You can't ground slam with [src]!") return FALSE + if(!user.UseStaminaBuffer(shieldbash_stamcost, warn = TRUE)) + return FALSE bash_target(user, target, NONE, harmful) user.do_attack_animation(target, used_item = src) playsound(src, harmful? "swing_hit" : 'sound/weapons/thudswoosh.ogg', 75, 1) last_shieldbash = world.time - user.adjustStaminaLossBuffered(shieldbash_stamcost) return TRUE // Directional sweep! last_shieldbash = world.time - user.adjustStaminaLossBuffered(shieldbash_stamcost) + if(!user.UseStaminaBuffer(shieldbash_stamcost, warn = TRUE)) + return FALSE // Since we are in combat mode, we can probably safely use the user's dir instead of getting their mouse pointing cardinal dir. var/bashdir = user.dir do_shieldbash_effect(user, bashdir, harmful) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index a96b0104f1..01d684adad 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -59,7 +59,7 @@ if(!affecting) //Missing limb? to_chat(user, "[C] doesn't have \a [parse_zone(user.zone_selected)]!") return - if(affecting.status == BODYPART_ORGANIC) //Limb must be organic to be healed - RR + if(affecting.is_organic_limb(FALSE)) //Limb must be organic to be healed - RR if(affecting.brute_dam && brute || affecting.burn_dam && burn) user.visible_message("[user] applies \the [src] on [C]'s [affecting.name].", "You apply \the [src] on [C]'s [affecting.name].") if(affecting.heal_damage(brute, burn)) diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index b64aa60cac..ab1aab32a0 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -234,7 +234,7 @@ STR.max_w_class = WEIGHT_CLASS_NORMAL STR.max_combined_w_class = 100 STR.max_items = 100 - STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/grown, /obj/item/reagent_containers/honeycomb)) + STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/grown, /obj/item/reagent_containers/honeycomb, /obj/item/disk/plantgene)) //////// diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 590df34cde..2a1844d218 100755 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -816,6 +816,40 @@ fitting_swords = list(/obj/item/melee/rapier) starting_sword = /obj/item/melee/rapier +/obj/item/storage/belt/sabre/secbelt + name = "security sheath" + desc = "A statement on modern practical fashion; this limber black sheath is fitted to a lightened security belt, allowing one to look fashionable with their sword-shaped stun-baton, while of course carrying less things." + icon_state = "secsheath" + item_state = "secsheath" + w_class = WEIGHT_CLASS_BULKY + starting_sword = /obj/item/melee/baton/stunsword + content_overlays = TRUE + +/obj/item/storage/belt/sabre/secbelt/ComponentInitialize() + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_items = 5 + STR.max_w_class = WEIGHT_CLASS_NORMAL + STR.rustle_sound = TRUE + STR.quickdraw = FALSE + STR.can_hold = typecacheof(list( // cannot carry other batons + /obj/item/melee/baton/stunsword, + /obj/item/grenade, + /obj/item/reagent_containers/spray/pepper, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash/handheld, + /obj/item/clothing/glasses, + /obj/item/reagent_containers/food/snacks/donut, + /obj/item/flashlight/seclite, + /obj/item/radio, + /obj/item/clothing/gloves, + /obj/item/restraints/legcuffs/bola + )) + +/obj/item/storage/belt/sabre/secbelt/PopulateContents() + new /obj/item/melee/baton/stunsword(src) + update_icon() + /obj/item/storage/belt/sabre/twin name = "twin sheath" desc = "Two sheaths. One is capable of holding a katana (or bokken) and the other a wakizashi. You could put two wakizashis in if you really wanted to. Now you can really roleplay as a samurai." @@ -830,3 +864,28 @@ var/datum/component/storage/STR = GetComponent(/datum/component/storage) STR.max_items = 2 STR.max_w_class = WEIGHT_CLASS_BULKY + WEIGHT_CLASS_NORMAL //katana and waki. + +/obj/item/storage/belt/plant + name = "botanical belt" + desc = "A belt used to hold most hydroponics supplies. Suprisingly, not green." + icon_state = "plantbelt" + item_state = "plantbelt" + content_overlays = TRUE + +/obj/item/storage/belt/plant/ComponentInitialize() + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_items = 6 + STR.max_w_class = WEIGHT_CLASS_NORMAL + STR.can_hold = typecacheof(list( + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/plant_analyzer, + /obj/item/seeds, + /obj/item/reagent_containers/glass/bottle, + /obj/item/reagent_containers/glass/beaker, + /obj/item/cultivator, + /obj/item/reagent_containers/spray/pestspray, + /obj/item/hatchet, + /obj/item/shovel/spade, + /obj/item/gun/energy/floragun + )) diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm index 28850e79a2..244b8c804b 100644 --- a/code/game/objects/items/storage/book.dm +++ b/code/game/objects/items/storage/book.dm @@ -103,7 +103,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible", /obj/item/storage/book/bible/proc/bless(mob/living/carbon/human/H, mob/living/user) for(var/X in H.bodyparts) var/obj/item/bodypart/BP = X - if(BP.status == BODYPART_ROBOTIC) + if(BP.is_robotic_limb()) to_chat(user, "[src.deity_name] refuses to heal this metallic taint!") return 0 diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index 5e76d506ba..16efecec7c 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -166,13 +166,12 @@ if(turned_on) if(baton_stun(M, user, disarming)) user.do_attack_animation(M) - user.adjustStaminaLossBuffered(getweight(user, STAM_COST_BATON_MOB_MULT)) else if(user.a_intent != INTENT_HARM) //they'll try to bash in the last proc. M.visible_message("[user] has prodded [M] with [src]. Luckily it was off.", \ "[user] has prodded you with [src]. Luckily it was off") return disarming || (user.a_intent != INTENT_HARM) -/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE) +/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/living/user, disarming = FALSE) var/list/return_list = list() if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; check_shields() handles that playsound(L, 'sound/weapons/genhit.ogg', 50, 1) @@ -194,6 +193,9 @@ return FALSE stunpwr *= round(stuncharge/hitcost, 0.1) + if(!user.UseStaminaBuffer(getweight(user, STAM_COST_BATON_MOB_MULT), warn = TRUE)) + return FALSE + if(!disarming) if(knockdown) L.DefaultCombatKnockdown(50, override_stamdmg = 0) //knockdown @@ -236,7 +238,7 @@ /obj/item/melee/baton/stunsword name = "stunsword" - desc = "not actually sharp, this sword is functionally identical to a stunbaton" + desc = "Not actually sharp, this sword is functionally identical to its baton counterpart." icon_state = "stunsword" item_state = "sword" @@ -248,6 +250,18 @@ /obj/item/melee/baton/stunsword/get_worn_belt_overlay(icon_file) return mutable_appearance(icon_file, "-stunsword") +/obj/item/melee/baton/stunsword/on_exit_storage(datum/component/storage/S) + var/obj/item/storage/belt/sabre/secbelt/B = S.parent + if(istype(B)) + playsound(B, 'sound/items/unsheath.ogg', 25, 1) + ..() + +/obj/item/melee/baton/stunsword/on_enter_storage(datum/component/storage/S) + var/obj/item/storage/belt/sabre/secbelt/B = S.parent + if(istype(B)) + playsound(B, 'sound/items/sheath.ogg', 25, 1) + ..() + /obj/item/ssword_kit name = "stunsword kit" desc = "a modkit for making a stunbaton into a stunsword" diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm index 7cf25098e1..29961d12b4 100644 --- a/code/game/objects/items/tanks/jetpack.dm +++ b/code/game/objects/items/tanks/jetpack.dm @@ -55,6 +55,8 @@ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/move_react) if(full_speed) user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + else + user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack) /obj/item/tank/jetpack/proc/turn_off(mob/user) on = FALSE @@ -63,6 +65,7 @@ ion_trail.stop() UnregisterSignal(user, COMSIG_MOVABLE_MOVED) user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack) /obj/item/tank/jetpack/proc/move_react(mob/user) allow_thrust(0.01, user) diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index bb507e5725..bf767af2ed 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -161,4 +161,4 @@ item_state = "screwdriver_nuke" usesound = 'sound/items/pshoom.ogg' toolspeed = 0.2 - random_color = FALSE \ No newline at end of file + random_color = FALSE diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 4cada03307..d35dad9f08 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -109,18 +109,25 @@ var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected)) - if(affecting && affecting.status == BODYPART_ROBOTIC && user.a_intent != INTENT_HARM) - //only heal to 25 if limb is damaged to or past 25 brute, otherwise heal normally - var/difference = affecting.brute_dam - 25 + if(affecting && affecting.is_robotic_limb() && user.a_intent != INTENT_HARM) + //only heal to threshhold_passed_mindamage if limb is damaged to or past threshhold, otherwise heal normally + var/damage var/heal_amount = 15 - if(difference >= 0) - heal_amount = difference + if(src.use_tool(H, user, 0, volume=50, amount=1)) if(user == H) user.visible_message("[user] starts to fix some of the dents on [H]'s [affecting.name].", "You start fixing some of the dents on [H]'s [affecting.name].") if(!do_mob(user, H, 50)) return + damage = affecting.brute_dam + affecting.update_threshhold_state(burn = FALSE) + if(affecting.threshhold_brute_passed) + heal_amount = min(heal_amount, damage - affecting.threshhold_passed_mindamage) + + if(!heal_amount) + to_chat(user, "[user == H ? "Your" : "[H]'s"] [affecting.name] appears to have suffered severe internal damage and requires surgery to repair further.") + return item_heal_robotic(H, user, heal_amount, 0) else return ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 3abee4db33..8a14d99776 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -304,18 +304,36 @@ /obj/proc/reskin_obj(mob/M) if(!LAZYLEN(unique_reskin)) return - var/list/skins = list() - for(var/S in unique_reskin) - skins[S] = image(icon = icon, icon_state = unique_reskin[S]) - var/choice = show_radial_menu(M, src, skins, custom_check = CALLBACK(src, .proc/check_skinnable, M), radius = 40, require_near = TRUE) - if(!choice) - return FALSE - icon_state = unique_reskin[choice] - current_skin = choice - return -/obj/proc/check_skinnable(/mob/M) - if(current_skin || !always_reskinnable) + var/list/items = list() + for(var/reskin_option in unique_reskin) + var/image/item_image = image(icon = src.icon, icon_state = unique_reskin[reskin_option]) + items += list("[reskin_option]" = item_image) + sortList(items) + + var/pick = show_radial_menu(M, src, items, custom_check = CALLBACK(src, .proc/check_reskin_menu, M), radius = 38, require_near = TRUE) + if(!pick) + return + if(!unique_reskin[pick]) + return + current_skin = pick + icon_state = unique_reskin[pick] + to_chat(M, "[src] is now skinned as '[pick].'") + +/** + * Checks if we are allowed to interact with a radial menu for reskins + * + * Arguments: + * * user The mob interacting with the menu + */ +/obj/proc/check_reskin_menu(mob/user) + if(QDELETED(src)) + return FALSE + if(current_skin) + return FALSE + if(!istype(user)) + return FALSE + if(user.incapacitated()) return FALSE return TRUE diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 9f91d2c5e0..31f11a199c 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -73,7 +73,7 @@ if(user.grab_state < GRAB_AGGRESSIVE) to_chat(user, "You need a better grip to do that!") return - if(user.grab_state >= GRAB_NECK) + if(user.grab_state >= GRAB_NECK || HAS_TRAIT(user, TRAIT_MAULER)) tablelimbsmash(user, pushed_mob) else tablepush(user, pushed_mob) @@ -147,7 +147,7 @@ pushed_mob.Knockdown(30) var/obj/item/bodypart/banged_limb = pushed_mob.get_bodypart(user.zone_selected) || pushed_mob.get_bodypart(BODY_ZONE_HEAD) var/extra_wound = 0 - if(HAS_TRAIT(user, TRAIT_HULK)) + if(HAS_TRAIT(user, TRAIT_HULK) || HAS_TRAIT(user, TRAIT_MAULER)) extra_wound = 20 banged_limb.receive_damage(30, wound_bonus = extra_wound) pushed_mob.apply_damage(60, STAMINA) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 3df64550b1..b150d4930e 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -133,13 +133,10 @@ else to_chat(user, "The plating is going to need some support! Place metal rods first.") -/turf/open/space/Entered(atom/movable/A, atom/OldLoc) - . = ..() - - var/turf/old = get_turf(OldLoc) - if(!isspaceturf(old) && ismob(A)) - var/mob/M = A - M.update_gravity(M.mob_has_gravity()) +/turf/open/space/Entered(atom/movable/A) + ..() + if ((!(A) || src != A.loc)) + return if(destination_z && destination_x && destination_y && !(A.pulledby || !A.can_be_z_moved)) var/tx = destination_x @@ -160,18 +157,27 @@ ty-- DT = locate(tx, ty, destination_z) - var/atom/movable/AM = A.pulling + var/atom/movable/pulling = A.pulling + var/atom/movable/puller = A A.forceMove(DT) - if(AM) - var/turf/T = get_step(A.loc,turn(A.dir, 180)) - AM.can_be_z_moved = FALSE - AM.forceMove(T) - A.start_pulling(AM) - AM.can_be_z_moved = TRUE + + while (pulling != null) + var/next_pulling = pulling.pulling + + var/turf/T = get_step(puller.loc, turn(puller.dir, 180)) + pulling.can_be_z_moved = FALSE + pulling.forceMove(T) + puller.start_pulling(pulling) + pulling.can_be_z_moved = TRUE + + puller = pulling + pulling = next_pulling //now we're on the new z_level, proceed the space drifting stoplag()//Let a diagonal move finish, if necessary A.newtonian_move(A.inertia_dir) + A.inertia_moving = TRUE + /turf/open/space/Exited(atom/movable/AM, atom/OldLoc) . = ..() diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index e287d9b1b3..6aab698a4a 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -43,6 +43,9 @@ H.dna.features["insect_wings"] = pick(GLOB.insect_wings_list) H.dna.features["deco_wings"] = pick(GLOB.deco_wings_list) H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list) + H.dna.features["arachnid_legs"] = pick(GLOB.arachnid_legs_list) + H.dna.features["arachnid_spinneret"] = pick(GLOB.arachnid_spinneret_list) + H.dna.features["arachnid_mandibles"] = pick(GLOB.arachnid_mandibles_list) H.dna.features["flavor_text"] = "" //Oh no. H.dna.features["body_model"] = H.gender diff --git a/code/modules/antagonists/abductor/equipment/glands/heal.dm b/code/modules/antagonists/abductor/equipment/glands/heal.dm index c60ec90480..0fcd1169d8 100644 --- a/code/modules/antagonists/abductor/equipment/glands/heal.dm +++ b/code/modules/antagonists/abductor/equipment/glands/heal.dm @@ -39,7 +39,7 @@ if(!limb) replace_limb(zone) return - if((limb.get_damage() >= (limb.max_damage / 2)) || (limb.status == BODYPART_ROBOTIC)) + if((limb.get_damage() >= (limb.max_damage / 2)) || limb.is_robotic_limb(FALSE)) replace_limb(zone, limb) return @@ -58,7 +58,7 @@ return var/obj/item/bodypart/chest/chest = owner.get_bodypart(BODY_ZONE_CHEST) - if((chest.get_damage() >= (chest.max_damage / 4)) || (chest.status == BODYPART_ROBOTIC)) + if((chest.get_damage() >= (chest.max_damage / 4)) || chest.is_robotic_limb(FALSE)) replace_chest(chest) return @@ -158,7 +158,7 @@ addtimer(CALLBACK(src, .proc/keep_replacing_blood), 30) /obj/item/organ/heart/gland/heal/proc/replace_chest(obj/item/bodypart/chest/chest) - if(chest.status == BODYPART_ROBOTIC) + if(chest.is_robotic_limb(FALSE)) owner.visible_message("[owner]'s [chest.name] rapidly expels its mechanical components, replacing them with flesh!", "Your [chest.name] rapidly expels its mechanical components, replacing them with flesh!") playsound(owner, 'sound/magic/clockwork/anima_fragment_attack.ogg', 50, TRUE) var/list/dirs = GLOB.alldirs.Copy() diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 24288be078..05c265bc15 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -6,6 +6,10 @@ Shield Armor Tentacles + Hatterhat Additions: + claws! (glove slot) + jagged (thieves') claws + bone gauntlets for punching dudes */ @@ -89,7 +93,7 @@ return 1 var/mob/living/carbon/human/H = user if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type)) - H.visible_message("[H] casts off [H.p_their()] [suit_name_simple]!", "We cast off our [suit_name_simple].", "You hear the organic matter ripping and tearing!") + H.visible_message("[H] casts off [H.p_their()] [suit_name_simple]!", "We cast off our [suit_name_simple].", "You hear organic matter ripping and tearing!") H.temporarilyRemoveItemFromInventory(H.head, TRUE) //The qdel on dropped() takes care of it H.temporarilyRemoveItemFromInventory(H.wear_suit, TRUE) H.update_inv_wear_suit() @@ -461,7 +465,7 @@ if(--remaining_uses < 1) if(ishuman(loc)) var/mob/living/carbon/human/H = loc - H.visible_message("With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!", "We assimilate our shield into our body", "With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!", "We assimilate our shield into our body.", "With a sickening crunch, [H] reforms [H.p_their()] [glove_name_simple] into hands!", "We assimilate our [glove_name_simple].", "You hear organic matter ripping and tearing!") + H.temporarilyRemoveItemFromInventory(H.gloves, TRUE) //The qdel on dropped() takes care of it + H.update_inv_gloves() + playsound(H.loc, 'sound/effects/blobattack.ogg', 30, 1) + if(blood_on_castoff) + H.add_splatter_floor() + playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds + + changeling.chem_recharge_slowdown -= recharge_slowdown + return 1 + +/obj/effect/proc_holder/changeling/gloves/on_refund(mob/user) + if(!ishuman(user)) + return + action.Remove(user) + var/mob/living/carbon/human/H = user + check_gloves(H) + +/obj/effect/proc_holder/changeling/gloves/sting_action(mob/living/carbon/human/user) + if(!user.canUnEquip(user.gloves)) + to_chat(user, "\the [user.gloves] is stuck to your body, you cannot grow [glove_name_simple] over it!") + return + + user.dropItemToGround(user.gloves) + + user.equip_to_slot_if_possible(new glove_type(user), SLOT_GLOVES, 1, 1, 1) + playsound(user, 'sound/effects/blobattack.ogg', 30, 1) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + changeling.chem_recharge_slowdown += recharge_slowdown + return TRUE + +/obj/item/clothing/gloves/claws + name = "claws of doing nothing" + desc = "These shouldn't be here." + icon_state = "bracers" + item_state = "bracers" + transfer_prints = TRUE + body_parts_covered = HANDS + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0) + +/obj/item/clothing/gloves/claws/Initialize() + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) + +/obj/item/clothing/gloves/claws/vulture // prying shit off dead/soon to be dead dudes! + name = "jagged claws" + desc = "Good for prying things off of people and looking incredibly creepy." + strip_mod = 2 + +/obj/effect/proc_holder/changeling/gloves/gauntlets + name = "Bone Gauntlets" + desc = "We turn our hands into solid bone and chitin, sacrificing dexterity for raw strength." + helptext = "These grotesque, bone-and-chitin gauntlets are remarkably good at beating victims senseless, and cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat." + chemical_cost = 10 // same cost as armblade because its a sidegrade (sacrifice utility for punching people violently) + dna_cost = 2 + loudness = 2 + req_human = 1 + action_icon = 'icons/mob/actions/actions_changeling.dmi' + action_icon_state = "ling_gauntlets" + action_background_icon_state = "bg_ling" + + glove_type = /obj/item/clothing/gloves/fingerless/pugilist/cling // just punch his head off dude + glove_name_simple = "bone gauntlets" + +/obj/item/clothing/gloves/fingerless/pugilist/cling // switches between lesser GotNS and Big Punchy Rib Breaky Hands + name = "hewn bone gauntlets" + icon_state = "ling_gauntlets" + item_state = "ling_gauntlets" + desc = "Rough bone and chitin, pulsing with an abomination barely called \"life\". Good for punching people, not so much for firearms." + transfer_prints = TRUE + body_parts_covered = ARMS|HANDS + cold_protection = ARMS|HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0) + enhancement = 9 // first, do harm. all of it. all of the harm. just fuck em up. + wound_enhancement = 9 + var/fast_enhancement = 9 + var/fast_wound_enhancement = 9 + var/slow_enhancement = 20 + var/slow_wound_enhancement = 20 + silent = TRUE + inherited_trait = TRAIT_CHUNKYFINGERS // dummy thicc bone hands + secondary_trait = TRAIT_MAULER // its only violence from here, bucko + var/fasthands = TRUE + +/obj/item/clothing/gloves/fingerless/pugilist/cling/examine(mob/user) + . = ..() + . += "[src] are formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"]." + . += "Alt-click them to change between rapid strikes and strong blows." + +/obj/item/clothing/gloves/fingerless/pugilist/cling/AltClick(mob/user) + . = ..() + use_buffs(user, FALSE) // reset + fasthands = !fasthands + if(fasthands) + enhancement = fast_enhancement + wound_enhancement = fast_wound_enhancement + else + enhancement = slow_enhancement // fuck em up kiddo + wound_enhancement = slow_wound_enhancement // really. fuck em up. + to_chat(user, "[src] are now formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"].") + addtimer(CALLBACK(src, .proc/use_buffs, user, TRUE), 0.1) // go fuckin get em + +/obj/item/clothing/gloves/fingerless/pugilist/cling/Initialize() + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) + +/obj/item/clothing/gloves/fingerless/pugilist/cling/equipped(mob/user, slot) + . = ..() + if(current_equipped_slot == SLOT_GLOVES) + to_chat(user, "With [src] formed around our arms, we are ready to fight.") + +/obj/item/clothing/gloves/fingerless/pugilist/cling/dropped(mob/user) + . = ..() + if(wornonce) + to_chat(user, "With [src] assimilated, we feel less ready to punch things.") + +/obj/item/clothing/gloves/fingerless/pugilist/cling/Touch(atom/target, proximity = TRUE) + if(!isliving(target)) + return + var/mob/living/M = loc + if(fasthands) + M.SetNextAction(CLICK_CD_RANGE) // fast punches + else + M.SetNextAction(CLICK_CD_GRABBING) // strong punches + return NO_AUTO_CLICKDELAY_HANDLING | ATTACK_IGNORE_ACTION diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm index 0c6ca13e86..eee8859485 100644 --- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm +++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm @@ -312,6 +312,8 @@ return TRUE /turf/open/consider_superconductivity(starting) + if(planetary_atmos) + return FALSE if(air.return_temperature() < (starting?MINIMUM_TEMPERATURE_START_SUPERCONDUCTION:MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION)) return FALSE if(air.heat_capacity() < M_CELL_WITH_RATIO) // Was: MOLES_CELLSTANDARD*0.1*0.05 Since there are no variables here we can make this a constant. diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index c7e2609436..d873b42d33 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -228,9 +228,9 @@ for(var/BP in PP.bodyparts) var/obj/item/bodypart/NN = BP - if(NN.status == BODYPART_ORGANIC && NN.species_id != "plasmaman") //getting every organic, non-plasmaman limb (augments/androids are immune to this) + if(NN.is_organic_limb() && NN.species_id != "plasmaman") //getting every organic, non-plasmaman limb (augments/androids are immune to this) plasma_parts += NN - if(NN.status == BODYPART_ROBOTIC) + if(NN.is_robotic_limb(FALSE)) robo_parts += NN if(prob(35)) //checking if the delay is over & if the victim actually has any parts to nom diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 7133e3d6c5..d817cc3a53 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -120,6 +120,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", + "arachnid_legs" = "Plain", + "arachnid_spinneret" = "Plain", + "arachnid_mandibles" = "Plain", "mam_body_markings" = "Plain", "mam_ears" = "None", "mam_snouts" = "None", @@ -1715,6 +1718,24 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(new_ipc_antenna) features["ipc_antenna"] = new_ipc_antenna + if("arachnid_legs") + var/new_arachnid_legs + new_arachnid_legs = input(user, "Choose your character's variant of arachnid legs:", "Character Preference") as null|anything in GLOB.arachnid_legs_list + if(new_arachnid_legs) + features["arachnid_legs"] = new_arachnid_legs + + if("arachnid_spinneret") + var/new_arachnid_spinneret + new_arachnid_spinneret = input(user, "Choose your character's spinneret markings:", "Character Preference") as null|anything in GLOB.arachnid_spinneret_list + if(new_arachnid_spinneret) + features["arachnid_spinneret"] = new_arachnid_spinneret + + if("arachnid_mandibles") + var/new_arachnid_mandibles + new_arachnid_mandibles = input(user, "Choose your character's variant of mandibles:", "Character Preference") as null|anything in GLOB.arachnid_mandibles_list + if (new_arachnid_mandibles) + features["arachnid_mandibles"] = new_arachnid_mandibles + if("tail_lizard") var/new_tail new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard @@ -1880,6 +1901,24 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(new_insect_markings) features["insect_markings"] = new_insect_markings + if("arachnid_legs") + var/new_arachnid_legs + new_arachnid_legs = input(user, "Choose your character's variant of arachnid legs:", "Character Preference") as null|anything in GLOB.arachnid_legs_list + if(new_arachnid_legs) + features["arachnid_legs"] = new_arachnid_legs + + if("arachnid_spinneret") + var/new_arachnid_spinneret + new_arachnid_spinneret = input(user, "Choose your character's spinneret markings:", "Character Preference") as null|anything in GLOB.arachnid_spinneret_list + if(new_arachnid_spinneret) + features["arachnid_spinneret"] = new_arachnid_spinneret + + if("arachnid_mandibles") + var/new_arachnid_mandibles + new_arachnid_mandibles = input(user, "Choose your character's variant of mandibles:", "Character Preference") as null|anything in GLOB.arachnid_mandibles_list + if (new_arachnid_mandibles) + features["arachnid_mandibles"] = new_arachnid_mandibles + if("s_tone") var/list/choices = GLOB.skin_tones - GLOB.nonstandard_skin_tones if(CONFIG_GET(flag/allow_custom_skintones)) @@ -1919,6 +1958,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["xenotail"] = "None" features["tail_human"] = "None" features["tail_lizard"] = "None" + features["arachnid_spinneret"] = "None" if("ears") var/list/snowflake_ears_list = list() @@ -2700,7 +2740,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(initial_spawn) //delete any existing prosthetic limbs to make sure no remnant prosthetics are left over - But DO NOT delete those that are species-related for(var/obj/item/bodypart/part in character.bodyparts) - if(part.status == BODYPART_ROBOTIC && !part.render_like_organic) + if(part.is_robotic_limb(FALSE)) qdel(part) character.regenerate_limbs() //regenerate limbs so now you only have normal limbs for(var/modified_limb in modified_limbs) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index ab30e48b67..3bad31a919 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 37 +#define SAVEFILE_VERSION_MAX 38 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -217,6 +217,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car else eye_type = "insect" + if(current_version < 38) //further eye sprite changes + if(S["species"] == "plasmaman") + left_eye_color = "#FFC90E" + right_eye_color = "#FFC90E" + else + if(S["species"] == "skeleton") + left_eye_color = "#BAB99E" + right_eye_color = "#BAB99E" + /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) return @@ -554,6 +563,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["feature_insect_wings"] >> features["insect_wings"] S["feature_insect_fluff"] >> features["insect_fluff"] S["feature_insect_markings"] >> features["insect_markings"] + S["feature_arachnid_legs"] >> features["arachnid_legs"] + S["feature_arachnid_spinneret"] >> features["arachnid_spinneret"] + S["feature_arachnid_mandibles"] >> features["arachnid_mandibles"] S["feature_horns_color"] >> features["horns_color"] S["feature_wings_color"] >> features["wings_color"] S["persistent_scars"] >> persistent_scars @@ -738,6 +750,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car features["insect_fluff"] = sanitize_inlist(features["insect_fluff"], GLOB.insect_fluffs_list) features["insect_markings"] = sanitize_inlist(features["insect_markings"], GLOB.insect_markings_list, "None") features["insect_wings"] = sanitize_inlist(features["insect_wings"], GLOB.insect_wings_list) + features["arachnid_legs"] = sanitize_inlist(features["arachnid_legs"], GLOB.arachnid_legs_list, "Plain") + features["arachnid_spinneret"] = sanitize_inlist(features["arachnid_spinneret"], GLOB.arachnid_spinneret_list, "Plain") + features["arachnid_mandibles"] = sanitize_inlist(features["arachnid_mandibles"], GLOB.arachnid_mandibles_list, "Plain") var/static/size_min if(!size_min) @@ -877,6 +892,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["feature_insect_wings"] , features["insect_wings"]) WRITE_FILE(S["feature_insect_fluff"] , features["insect_fluff"]) WRITE_FILE(S["feature_insect_markings"] , features["insect_markings"]) + WRITE_FILE(S["feature_arachnid_legs"] , features["arachnid_legs"]) + WRITE_FILE(S["feature_arachnid_spinneret"] , features["arachnid_spinneret"]) + WRITE_FILE(S["feature_arachnid_mandibles"] , features["arachnid_mandibles"]) WRITE_FILE(S["feature_meat"] , features["meat_type"]) WRITE_FILE(S["feature_has_cock"], features["has_cock"]) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 2fbe738acb..ea01b0c0ed 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -45,13 +45,12 @@ var/dynamic_fhair_suffix = ""//mask > head for facial hair //basically a restriction list. - var/list/species_restricted = null + var/list/species_restricted //Basically syntax is species_restricted = list("Species Name","Species Name") //Add a "exclude" string to do the opposite, making it only only species listed that can't wear it. //You append this to clothing objects - // How much clothing damage has been dealt to each of the limbs of the clothing, assuming it covers more than one limb var/list/damage_by_parts // How much integrity is in a specific limb before that limb is disabled (for use in [/obj/item/clothing/proc/take_damage_zone], and only if we cover multiple zones.) Set to 0 to disable shredding. @@ -477,7 +476,6 @@ BLIND // can't see anything return TRUE - /// If we're a clothing with at least 1 shredded/disabled zone, give the wearer a periodic heads up letting them know their clothes are damaged /obj/item/clothing/proc/bristle(mob/living/L) if(!istype(L)) diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index b5d92fb780..f9f587ae5b 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -20,8 +20,14 @@ body_parts_covered = ARMS cold_protection = ARMS strip_delay = 300 //you can't just yank them off + /// did you ever get around to wearing these or no + var/wornonce = FALSE ///Extra damage through the punch. var/enhancement = 0 //it's a +0 to your punches because it isn't magical + ///extra wound bonus through the punch (MAYBE DON'T BE GENEROUS WITH THIS) + var/wound_enhancement = 0 + /// do we give the flavortext for wearing them + var/silent = FALSE ///Main trait added by the gloves to the user on wear. var/inherited_trait = TRAIT_NOGUNS //what are you, dishonoroable? ///Secondary trait added by the gloves to the user on wear. @@ -29,7 +35,18 @@ /obj/item/clothing/gloves/fingerless/pugilist/equipped(mob/user, slot) . = ..() - if(slot == SLOT_GLOVES) + if(current_equipped_slot == SLOT_GLOVES) + use_buffs(user, TRUE) + wornonce = TRUE + +/obj/item/clothing/gloves/fingerless/pugilist/dropped(mob/user) + . = ..() + if(wornonce) + use_buffs(user, FALSE) + wornonce = FALSE + +/obj/item/clothing/gloves/fingerless/pugilist/proc/use_buffs(mob/user, buff) + if(buff) // tarukaja if(ishuman(user)) var/mob/living/carbon/human/H = user ADD_TRAIT(H, TRAIT_PUGILIST, GLOVE_TRAIT) @@ -37,17 +54,20 @@ ADD_TRAIT(H, secondary_trait, GLOVE_TRAIT) H.dna.species.punchdamagehigh += enhancement H.dna.species.punchdamagelow += enhancement - -/obj/item/clothing/gloves/fingerless/pugilist/dropped(mob/user) - - REMOVE_TRAIT(user, TRAIT_PUGILIST, GLOVE_TRAIT) - REMOVE_TRAIT(user, inherited_trait, GLOVE_TRAIT) - REMOVE_TRAIT(user, secondary_trait, GLOVE_TRAIT) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.dna.species.punchdamagehigh -= enhancement - H.dna.species.punchdamagelow -= enhancement - return ..() + H.dna.species.punchwoundbonus += wound_enhancement + if(!silent) + to_chat(H, "With [src] on your arms, you feel ready to punch things.") + else // dekaja + REMOVE_TRAIT(user, TRAIT_PUGILIST, GLOVE_TRAIT) + REMOVE_TRAIT(user, inherited_trait, GLOVE_TRAIT) + REMOVE_TRAIT(user, secondary_trait, GLOVE_TRAIT) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.dna.species.punchdamagehigh -= enhancement + H.dna.species.punchdamagelow -= enhancement + H.dna.species.punchwoundbonus -= wound_enhancement + if(!silent) + to_chat(user, "With [src] off of your arms, you feel less ready to punch things.") /obj/item/clothing/gloves/fingerless/pugilist/chaplain name = "armwraps of unyielding resolve" @@ -93,6 +113,7 @@ icon_state = "rapid" item_state = "rapid" enhancement = 10 //omae wa mou shindeiru + wound_enhancement = 10 var/warcry = "AT" secondary_trait = TRAIT_NOSOFTCRIT //basically extra health diff --git a/code/modules/clothing/gloves/tacklers.dm b/code/modules/clothing/gloves/tacklers.dm index f4b4140a1a..eda36ea12f 100644 --- a/code/modules/clothing/gloves/tacklers.dm +++ b/code/modules/clothing/gloves/tacklers.dm @@ -65,12 +65,14 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE + strip_mod = 1.2 // because apparently black gloves had this /obj/item/clothing/gloves/tackler/combat/insulated name = "guerrilla gloves" desc = "Superior quality combative gloves, good for performing tackle takedowns as well as absorbing electrical shocks." siemens_coefficient = 0 permeability_coefficient = 0.05 + strip_mod = 1.5 // and combat gloves had this?? /obj/item/clothing/gloves/tackler/combat/insulated/infiltrator name = "insidious guerrilla gloves" diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 11df38cfdd..5dd35788f1 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -355,7 +355,7 @@ /obj/item/clothing/mask/paper/Initialize(mapload) - .=..() + . = ..() papermask_designs = list( "Blank" = image(icon = src.icon, icon_state = "plainmask"), "Neutral" = image(icon = src.icon, icon_state = "neutralmask"), @@ -384,7 +384,7 @@ "Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask", "Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask") - var/choice = show_radial_menu(user,src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + var/choice = show_radial_menu(user, src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) if(src && choice && !user.incapacitated() && in_range(user,src)) icon_state = options[choice] diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 0920c9ccb6..e723ddd79b 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -29,7 +29,7 @@ for(var/i = 1, i <= number_of_wormholes, i++) var/turf/T = pick(pick_turfs) - wormholes += new /obj/effect/portal/wormhole(T, null, 0, null, FALSE) + wormholes += new /obj/effect/portal/wormhole(T, 0, null, FALSE) /datum/round_event/wormholes/announce(fake) priority_announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", "spanomalies") diff --git a/code/modules/food_and_drinks/food/snacks_soup.dm b/code/modules/food_and_drinks/food/snacks_soup.dm index eea7cdb87d..38a68ad364 100644 --- a/code/modules/food_and_drinks/food/snacks_soup.dm +++ b/code/modules/food_and_drinks/food/snacks_soup.dm @@ -291,3 +291,12 @@ tastes = list("creamy peas"= 2, "parsnip" = 1) filling_color = "#9dc530" foodtype = VEGETABLES + +/obj/item/reagent_containers/food/snacks/soup/facehuggerpot + name = "pot of face hugger jambalaya" + desc = "An entire pot of an extremely spicy dish made using extremely exotic ingredients. Highly recommend by an interdimensional businessman." + icon_state = "facehuggerpot" + bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 2) + list_reagents = list(/datum/reagent/consumable/nutriment = 10, /datum/reagent/consumable/nutriment/vitamin = 4) + tastes = list("face huggers" = 1) + foodtype = MEAT diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm index 6372137edc..c52218c913 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm @@ -291,3 +291,18 @@ ) result = /obj/item/reagent_containers/food/snacks/soup/peasoup subcategory = CAT_SOUP + +/datum/crafting_recipe/food/facehuggerjambalayapot + name = "Facehugger jambalaya" + reqs = list( + /datum/reagent/bluespace = 10, + /datum/reagent/consumable/ethanol/booger =10, + /obj/item/organ/heart= 3, + /obj/item/clothing/mask/facehugger = 1, + /obj/item/reagent_containers/food/snacks/egg = 1, + /datum/reagent/consumable/blackpepper = 5, + /datum/reagent/toxin/acid = 5 + + ) + result = /obj/item/reagent_containers/food/snacks/soup/facehuggerpot + subcategory = CAT_SOUP diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index 83bb67b879..f6e4684e21 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -351,7 +351,7 @@ /datum/holiday/halloween name = HALLOWEEN - begin_day = 28 + begin_day = 1 begin_month = OCTOBER end_day = 2 end_month = NOVEMBER diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index aa8a7b60c2..a1a177c028 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -42,6 +42,14 @@ icon = 'icons/obj/hydroponics/equipment.dmi' icon_state = "hydrotray3" +/obj/machinery/hydroponics/constructable/ComponentInitialize() + . = ..() + AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated)) + AddComponent(/datum/component/plumbing/simple_demand) + +/obj/machinery/hydroponics/constructable/proc/can_be_rotated(mob/user, rotation_type) + return !anchored + /obj/machinery/hydroponics/constructable/RefreshParts() var/tmp_capacity = 0 for (var/obj/item/stock_parts/matter_bin/M in component_parts) diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index efd98c4d50..833b2963ad 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -44,18 +44,18 @@ /obj/item/integrated_circuit/output/screen/large/do_work() ..() - if(isliving(assembly.loc))//this whole block just returns if the assembly is neither in a mobs hands or on the ground - var/mob/living/H = assembly.loc - if(H.get_active_held_item() != assembly && H.get_inactive_held_item() != assembly) - return - else - if(!isturf(assembly.loc)) - return - - var/atom/host = assembly || src var/list/mobs = list() - for(var/mob/M in range(0, get_turf(src))) - mobs += M + if(isliving(assembly.loc)) + mobs += assembly.loc + var/mob/living/L = assembly.loc + if(L.is_holding(src)) + for(var/mob/M in range(1, get_turf(src))) + mobs += M + else + for(var/mob/M in range(2, get_turf(src))) + mobs += M + + var/atom/host = assembly || src to_chat(mobs, "[icon2html(host.icon, world, host.icon_state)] flashes a message: [stuff_to_display]") host.investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 0feceaf1a8..b0ac5248c1 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -73,6 +73,31 @@ /// Starting skill modifiers. var/list/starting_modifiers +/** + * Checks if we should be created on a certain map + */ +/datum/job/proc/map_check(datum/map_config/C) + return (length(C.job_whitelist)? (type in C.job_whitelist) : !(type in C.job_blacklist)) + +/** + * Processes map specific overrides + */ +/datum/job/proc/process_map_overrides(datum/map_config/C) + if(type in C.job_override_spawn_positions) + spawn_positions = C.job_override_spawn_positions[type] + if(type in C.job_override_total_positions) + total_positions = C.job_override_total_positions[type] + if(type in C.job_access_override) + access = C.job_access_override[type] + minimal_access = access + else + if(type in C.job_access_add) + access += C.job_access_add[type] + minimal_access += C.job_access_add[type] + if(type in C.job_access_remove) + access -= C.job_access_add[type] + minimal_access -= C.job_access_remove[type] + //Only override this proc //H is usually a human unless an /equip override transformed it /datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE) @@ -175,9 +200,6 @@ /datum/job/proc/config_check() return TRUE -/datum/job/proc/map_check() - return TRUE - /datum/job/proc/radio_help_message(mob/M) to_chat(M, "Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.") diff --git a/code/modules/jobs/map_changes/map_changes.dm b/code/modules/jobs/map_changes/map_changes.dm deleted file mode 100644 index 575075037b..0000000000 --- a/code/modules/jobs/map_changes/map_changes.dm +++ /dev/null @@ -1,4 +0,0 @@ -//this needs to come after the job_types subfolder to keep the correct ordering - -#include "..\..\..\..\_maps\map_files\PubbyStation\job_changes.dm" -#undef JOB_MODIFICATION_MAP_NAME \ No newline at end of file diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm index 3a47cd2315..3d12f89cfe 100644 --- a/code/modules/keybindings/bindings_client.dm +++ b/code/modules/keybindings/bindings_client.dm @@ -1,6 +1,7 @@ // Clients aren't datums so we have to define these procs indpendently. // These verbs are called for all key press and release events /client/verb/keyDown(_key as text) + SHOULD_NOT_SLEEP(TRUE) set instant = TRUE set hidden = TRUE @@ -83,6 +84,7 @@ keyUp("[key]") /client/verb/keyUp(_key as text) + SHOULD_NOT_SLEEP(TRUE) set instant = TRUE set hidden = TRUE diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index a53820b5d2..47dbfd855f 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -1,12 +1,11 @@ /datum/proc/key_down(key, client/user) // Called when a key is pressed down initially - return + SHOULD_NOT_SLEEP(TRUE) /datum/proc/key_up(key, client/user) // Called when a key is released - return + SHOULD_NOT_SLEEP(TRUE) /datum/proc/keyLoop(client/user) // Called once every frame - set waitfor = FALSE - return + SHOULD_NOT_SLEEP(TRUE) // removes all the existing macros /client/proc/erase_all_macros() diff --git a/code/modules/language/arachnid.dm b/code/modules/language/arachnid.dm new file mode 100644 index 0000000000..92ea47781e --- /dev/null +++ b/code/modules/language/arachnid.dm @@ -0,0 +1,15 @@ +/datum/language/arachnid + name = "Rachnidian" + desc = "A language that exploits the multiple limbs of arachnids to do subtle dance like movements to communicate.\ + A proper speaker's movements are quick and sharp enough to make audible whiffs and thumps however, which are intelligible over the radio." + speech_verb = "chitter" + ask_verb = "chitter" + exclaim_verb = "chitter" + key = "r" + flags = NO_STUTTER | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD + + icon_state = "arachnid" + +/datum/language/arachnid/scramble(input) + . = prob(65) ? "wiff" : "thump" + . += (copytext(input, length(input)) == "?") ? "?" : "!" diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm index c1677117e9..219d8dc184 100644 --- a/code/modules/language/language_holder.dm +++ b/code/modules/language/language_holder.dm @@ -330,6 +330,12 @@ Key procs spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM), /datum/language/voltaic = list(LANGUAGE_ATOM)) +/datum/language_holder/arachnid + understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM), + /datum/language/arachnid = list(LANGUAGE_ATOM)) + spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM), + /datum/language/arachnid = list(LANGUAGE_ATOM)) + /datum/language_holder/empty understood_languages = list() spoken_languages = list() diff --git a/code/modules/mapping/map_config.dm b/code/modules/mapping/map_config.dm index c03ef65f43..d18ca1e1e3 100644 --- a/code/modules/mapping/map_config.dm +++ b/code/modules/mapping/map_config.dm @@ -43,6 +43,21 @@ /// Orientation to load in by default. var/orientation = SOUTH //byond defaults to placing everyting SOUTH. + /// Jobs whitelist - if this is not empty, ONLY these jobs are allowed. Overrides blacklist. + var/list/job_whitelist + /// Jobs blacklist - if this is not empty, jobs in this aren't allowed. + var/list/job_blacklist + /// Job spawn position mod - type = number + var/list/job_override_spawn_positions + /// Job total position mod - type = number + var/list/job_override_total_positions + /// Add these accesses to jobs - type = list() + var/list/job_access_add + /// Remove these accesses from jobs - type = list() + var/list/job_access_remove + /// Override job accesses - type = list() - overrides everything else + var/list/job_access_override + /proc/load_map_config(filename = "data/next_map.json", default_to_box, delete_after, error_if_missing = TRUE) var/datum/map_config/config = new if (default_to_box) @@ -161,6 +176,69 @@ allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE + if("job_whitelist" in json) + job_whitelist = list() + for(var/path in json["job_whitelist"]) + var/type = text2path(path) + if(!path) + log_config("map datum [config_filename] failed to validate path [path] in job overrides.") + continue + job_whitelist += type + + if("job_blacklist" in json) + job_blacklist = list() + for(var/path in json["job_blacklist"]) + var/type = text2path(path) + if(!path) + log_config("map datum [config_filename] failed to validate path [path] in job overrides.") + continue + job_blacklist += type + + if("job_override_spawn_positions" in json) + job_override_spawn_positions = list() + for(var/path in json["job_override_spawn_positions"]) + var/type = text2path(path) + if(!path) + log_config("map datum [config_filename] failed to validate path [path] in job overrides.") + continue + job_override_spawn_positions += type + + if("job_override_total_positions" in json) + job_override_total_positions = list() + for(var/path in json["job_override_total_positions"]) + var/type = text2path(path) + if(!path) + log_config("map datum [config_filename] failed to validate path [path] in job overrides.") + continue + job_override_total_positions += type + + if("job_access_add" in json) + job_access_add = list() + for(var/path in json["job_acces_add"]) + var/type = text2path(path) + if(!path) + log_config("map datum [config_filename] failed to validate path [path] in job overrides.") + continue + job_access_add[type] = json["job_access_add"] + + if("job_access_remove" in json) + job_access_remove = list() + for(var/path in json["job_acces_add"]) + var/type = text2path(path) + if(!path) + log_config("map datum [config_filename] failed to validate path [path] in job overrides.") + continue + job_access_remove[type] = json["job_access_remove"] + + if("job_access_override" in json) + job_access_override = list() + for(var/path in json["job_acces_add"]) + var/type = text2path(path) + if(!path) + log_config("map datum [config_filename] failed to validate path [path] in job overrides.") + continue + job_access_override[type] = json["job_access_override"] + defaulted = FALSE return TRUE #undef CHECK_EXISTS @@ -190,6 +268,13 @@ jsonlist["announcertype"] = announcertype jsonlist["orientation"] = orientation jsonlist["allow_custom_shuttles"] = allow_custom_shuttles + jsonlist["job_whitelist"] = job_whitelist + jsonlist["job_blacklist"] = job_blacklist + jsonlist["job_override_spawn_positions"] = job_override_spawn_positions + jsonlist["job_override_total_positions"] = job_override_total_positions + jsonlist["job_access_add"] = job_access_add + jsonlist["job_access_remove"] = job_access_remove + jsonlist["job_access_override"] = job_access_override if(fexists("data/next_map.json")) fdel("data/next_map.json") var/F = file("data/next_map.json") diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm index 193106c294..b681c348e0 100644 --- a/code/modules/mapping/ruins.dm +++ b/code/modules/mapping/ruins.dm @@ -135,7 +135,12 @@ //That's done remove from priority even if it failed if(forced) //TODO : handle forced ruins with multiple variants + // this might work? forced_ruins -= current_pick + if(!current_pick.allow_duplicates) + for(var/datum/map_template/ruin/R in forced_ruins) + if(R.id == current_pick.id) + forced_ruins -= R forced = FALSE if(failed_to_place) diff --git a/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm index 4faef230df..3972bdc2c3 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm @@ -51,7 +51,7 @@ /datum/sprite_accessory/xeno_head/is_not_visible(var/mob/living/carbon/human/H, var/tauric) var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) - return (!H.dna.features["xenohead"] || H.dna.features["xenohead"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic)) + return (!H.dna.features["xenohead"] || H.dna.features["xenohead"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.is_robotic_limb(FALSE)) /datum/sprite_accessory/xeno_head/standard name = "Standard" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/arachnid.dm b/code/modules/mob/dead/new_player/sprite_accessories/arachnid.dm new file mode 100644 index 0000000000..f75c6208f8 --- /dev/null +++ b/code/modules/mob/dead/new_player/sprite_accessories/arachnid.dm @@ -0,0 +1,68 @@ +/datum/sprite_accessory/arachnid_legs + icon = 'icons/mob/arachnid_legs.dmi' + relevant_layers = list(BODY_BEHIND_LAYER, BODY_ADJ_LAYER, BODY_FRONT_LAYER) + mutant_part_string = "arachnid_legs" + color_src = 0 + +/datum/sprite_accessory/arachnid_legs/none + name = "None" + icon_state = null + relevant_layers = null + +/datum/sprite_accessory/arachnid_legs/plain + name = "Plain" + icon_state = "plain" + +/datum/sprite_accessory/arachnid_legs/fuzzy + name = "Fuzzy" + icon_state = "fuzzy" + +/datum/sprite_accessory/arachnid_legs/spiky + name = "Spiky" + icon_state = "spiky" + +/datum/sprite_accessory/arachnid_spinneret + icon = 'icons/mob/arachnid_spinneret.dmi' + relevant_layers = list(BODY_BEHIND_LAYER, BODY_ADJ_LAYER, BODY_FRONT_LAYER) + mutant_part_string = "spinneret" + color_src = 0 + +/datum/sprite_accessory/arachnid_spinneret/none + name = "None" + icon_state = null + relevant_layers = null + +/datum/sprite_accessory/arachnid_spinneret/plain + name = "Plain" + icon_state = "plain" + +/datum/sprite_accessory/arachnid_spinneret/fuzzy + name = "Fuzzy" + icon_state = "fuzzy" + +/datum/sprite_accessory/arachnid_spinneret/black_widow + name = "Black Widow" + icon_state = "blackwidow" + +/datum/sprite_accessory/arachnid_mandibles + icon = 'icons/mob/arachnid_mandibles.dmi' + relevant_layers = list(BODY_ADJ_LAYER, BODY_FRONT_LAYER) + mutant_part_string = "mandibles" + color_src = 0 + +/datum/sprite_accessory/arachnid_mandibles/none + name = "None" + icon_state = null + relevant_layers = null + +/datum/sprite_accessory/arachnid_mandibles/plain + name = "Plain" + icon_state = "plain" + +/datum/sprite_accessory/arachnid_mandibles/fuzzy + name = "Fuzzy" + icon_state = "fuzzy" + +/datum/sprite_accessory/arachnid_mandibles/spiky + name = "Spiky" + icon_state = "spiky" \ No newline at end of file diff --git a/code/modules/mob/dead/new_player/sprite_accessories/ears.dm b/code/modules/mob/dead/new_player/sprite_accessories/ears.dm index 1d9138465f..cae1b2e482 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/ears.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/ears.dm @@ -5,7 +5,7 @@ /datum/sprite_accessory/ears/is_not_visible(var/mob/living/carbon/human/H, var/tauric) var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) - return (!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic)) + return (!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || HD.is_robotic_limb(FALSE)) /datum/sprite_accessory/ears/none name = "None" @@ -187,7 +187,7 @@ /datum/sprite_accessory/ears/mam_ears/is_not_visible(var/mob/living/carbon/human/H, var/tauric) var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) - return (!H.dna.features["mam_ears"] || H.dna.features["mam_ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic)) + return (!H.dna.features["mam_ears"] || H.dna.features["mam_ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || HD.is_robotic_limb(FALSE)) /datum/sprite_accessory/ears/mam_ears/none name = "None" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/frills.dm b/code/modules/mob/dead/new_player/sprite_accessories/frills.dm index 49013161a9..c7a59da028 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/frills.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/frills.dm @@ -4,7 +4,7 @@ /datum/sprite_accessory/frills/is_not_visible(var/mob/living/carbon/human/H, var/tauric) var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) - return (!H.dna.features["frills"] || H.dna.features["frills"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || !HD || HD.status == BODYPART_ROBOTIC) + return (!H.dna.features["frills"] || H.dna.features["frills"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || !HD || HD.is_robotic_limb(FALSE)) /datum/sprite_accessory/frills/none name = "None" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/horns.dm b/code/modules/mob/dead/new_player/sprite_accessories/horns.dm index accbc4ee36..0d097b24f0 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/horns.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/horns.dm @@ -5,7 +5,7 @@ /datum/sprite_accessory/horns/is_not_visible(var/mob/living/carbon/human/H, var/tauric) var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) - return (!H.dna.features["horns"] || H.dna.features["horns"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic)) + return (!H.dna.features["horns"] || H.dna.features["horns"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.is_robotic_limb(FALSE)) /datum/sprite_accessory/horns/none name = "None" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm index 0cd7d301db..69016d2890 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm @@ -5,7 +5,7 @@ /datum/sprite_accessory/snouts/is_not_visible(var/mob/living/carbon/human/H, var/tauric) var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) - return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic)) + return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.is_robotic_limb(FALSE)) /datum/sprite_accessory/snout/guilmon name = "Guilmon" @@ -163,7 +163,7 @@ /datum/sprite_accessory/snouts/mam_snouts/is_not_visible(var/mob/living/carbon/human/H, var/tauric) var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) - return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic)) + return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.is_robotic_limb(FALSE)) /datum/sprite_accessory/snouts/mam_snouts/none name = "None" diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index ad8828572c..5392e7b2d5 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -212,7 +212,7 @@ icon_state = "[initial(icon_state)]_impregnated" var/obj/item/bodypart/chest/LC = target.get_bodypart(BODY_ZONE_CHEST) - if((!LC || LC.status != BODYPART_ROBOTIC) && !target.getorgan(/obj/item/organ/body_egg/alien_embryo)) + if((!LC || !LC.is_robotic_limb(FALSE)) && !target.getorgan(/obj/item/organ/body_egg/alien_embryo)) new /obj/item/organ/body_egg/alien_embryo(target) else diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 11b133f5e7..26486f255a 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -191,8 +191,8 @@ if(HAS_TRAIT(src, TRAIT_PACIFISM)) to_chat(src, "You gently let go of [throwable_mob].") return - - adjustStaminaLossBuffered(STAM_COST_THROW_MOB * ((throwable_mob.mob_size+1)**2))// throwing an entire person shall be very tiring + if(!UseStaminaBuffer(STAM_COST_THROW_MOB * ((throwable_mob.mob_size+1)**2), TRUE)) + return var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors var/turf/end_T = get_turf(target) if(start_T && end_T) @@ -206,7 +206,8 @@ to_chat(src, "You set [I] down gently on the ground.") return - adjustStaminaLossBuffered(I.getweight(src, STAM_COST_THROW_MULT, SKILL_THROW_STAM_COST)) + if(!UseStaminaBuffer(I.getweight(src, STAM_COST_THROW_MULT, SKILL_THROW_STAM_COST), warn = TRUE)) + return if(thrown_thing) var/power_throw = 0 @@ -594,15 +595,23 @@ remove_movespeed_modifier(/datum/movespeed_modifier/carbon_softcrit) /mob/living/carbon/update_stamina() - var/stam = getStaminaLoss() - if(stam > DAMAGE_PRECISION) - var/total_health = (maxHealth - stam) - if(total_health <= crit_threshold && !stat) - if(CHECK_MOBILITY(src, MOBILITY_STAND)) - to_chat(src, "You're too exhausted to keep going...") - KnockToFloor(TRUE) - update_health_hud() - + var/total_health = getStaminaLoss() + if(total_health) + if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health >= STAMINA_CRIT && !stat) + to_chat(src, "You're too exhausted to keep going...") + set_resting(TRUE, FALSE, FALSE) + SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE) + ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT) + filters += CIT_FILTER_STAMINACRIT + update_mobility() + if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_CRIT) + to_chat(src, "You don't feel nearly as exhausted anymore.") + DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT) + filters -= CIT_FILTER_STAMINACRIT + update_mobility() + UpdateStaminaBuffer() + update_health_hud() + /mob/living/carbon/update_sight() if(!client) return diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 566c004142..eefd422727 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -80,7 +80,7 @@ I.do_stagger_action(src, user, totitemdamage) if(I.force) apply_damage(totitemdamage, I.damtype, affecting, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness()) //CIT CHANGE - replaces I.force with totitemdamage - if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC) + if(I.damtype == BRUTE && affecting.is_organic_limb(FALSE)) var/basebloodychance = affecting.brute_dam + totitemdamage if(prob(basebloodychance)) I.add_mob_blood(src) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 5c5a1d6d52..aad647e1f3 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -169,11 +169,11 @@ //////////////////////////////////////////// //Returns a list of damaged bodyparts -/mob/living/carbon/proc/get_damaged_bodyparts(brute = FALSE, burn = FALSE, stamina = FALSE, status) +/mob/living/carbon/proc/get_damaged_bodyparts(brute = FALSE, burn = FALSE, stamina = FALSE, list/status) var/list/obj/item/bodypart/parts = list() for(var/X in bodyparts) var/obj/item/bodypart/BP = X - if(status && BP.status != status) + if(status && !status[BP.status]) continue if((brute && BP.brute_dam) || (burn && BP.burn_dam) || (stamina && BP.stamina_dam)) parts += BP diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 904753f9b1..710d49c075 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -254,10 +254,10 @@ if(href_list["pockets"]) var/strip_mod = 1 var/strip_silence = FALSE - var/obj/item/clothing/gloves/g = gloves - if (istype(g)) - strip_mod = g.strip_mod - strip_silence = g.strip_silence + var/obj/item/clothing/gloves/G = gloves + if(istype(G)) + strip_mod = G.strip_mod + strip_silence = G.strip_silence var/pocket_side = href_list["pockets"] var/pocket_id = (pocket_side == "right" ? SLOT_R_STORE : SLOT_L_STORE) var/obj/item/pocket_item = (pocket_id == SLOT_R_STORE ? r_store : l_store) @@ -805,7 +805,7 @@ hud_used.healthdoll.icon_state = "healthdoll_DEAD" hud_used.staminas?.update_icon_state() - hud_used.staminabuffer?.update_icon_state() + hud_used.staminabuffer?.mark_dirty() /mob/living/carbon/human/fully_heal(admin_revive = FALSE) if(admin_revive) @@ -1044,10 +1044,9 @@ remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying) return - var/stambufferinfluence = (bufferedstam*(100/stambuffer))*0.2 //CIT CHANGE - makes stamina buffer influence movedelay if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment var/scaling = maxHealth / 100 - var/health_deficiency = (((maxHealth / scaling) + stambufferinfluence) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it + var/health_deficiency = ((maxHealth / scaling) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it if(health_deficiency >= 40) add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency-39) / 75) add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency-39) / 25) @@ -1251,3 +1250,6 @@ /mob/living/carbon/human/species/roundstartslime race = /datum/species/jelly/roundstartslime + +/mob/living/carbon/human/species/arachnid + race = /datum/species/arachnid diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 96fda36370..76a9c00ed1 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -411,7 +411,7 @@ apply_status_effect(/datum/status_effect/no_combat_mode/robotic_emp, severity / 20) severity *= 0.5 for(var/obj/item/bodypart/L in src.bodyparts) - if(L.status == BODYPART_ROBOTIC) + if(L.is_robotic_limb()) if(!informed) to_chat(src, "You feel a sharp pain as your robotic limbs overload.") informed = TRUE diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index a6a3f408dc..89a4570e8d 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -7,7 +7,7 @@ buckle_lying = FALSE mob_biotypes = MOB_ORGANIC|MOB_HUMANOID /// Enable stamina combat - combat_flags = COMBAT_FLAGS_DEFAULT | COMBAT_FLAG_UNARMED_PARRY + combat_flags = COMBAT_FLAGS_STAMINA_COMBAT | COMBAT_FLAG_UNARMED_PARRY status_flags = CANSTUN|CANKNOCKDOWN|CANUNCONSCIOUS|CANPUSH|CANSTAGGER has_field_of_vision = FALSE //Handled by species. diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 5ced11e35a..97c40c96d7 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -11,11 +11,6 @@ /mob/living/carbon/human/movement_delay() . = ..() - if(CHECK_MOBILITY(src, MOBILITY_STAND) && m_intent == MOVE_INTENT_RUN && (combat_flags & COMBAT_FLAG_SPRINT_ACTIVE)) - var/static/datum/config_entry/number/movedelay/sprint_speed_increase/SSI - if(!SSI) - SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase) - . -= SSI.config_entry_value if (m_intent == MOVE_INTENT_WALK && HAS_TRAIT(src, TRAIT_SPEEDY_STEP)) . -= 1.5 @@ -61,7 +56,11 @@ HM.on_move(NewLoc) if(. && (combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) && !(movement_type & FLYING) && CHECK_ALL_MOBILITY(src, MOBILITY_MOVE|MOBILITY_STAND) && m_intent == MOVE_INTENT_RUN && has_gravity(loc) && (!pulledby || (pulledby.pulledby == src))) if(!HAS_TRAIT(src, TRAIT_FREESPRINT)) - doSprintLossTiles(1) + var/datum/movespeed_modifier/equipment_speedmod/MM = get_movespeed_modifier_datum(/datum/movespeed_modifier/equipment_speedmod) + var/amount = 1 + if(MM?.multiplicative_slowdown >= 1) + amount *= (1 + (6 - (3 / MM.multiplicative_slowdown))) + doSprintLossTiles(amount) if((oldpseudoheight - pseudo_z_axis) >= 8) to_chat(src, "You trip off of the elevated surface!") for(var/obj/item/I in held_items) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index b08d4a64aa..126d4401e0 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -64,7 +64,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/stunmod = 1 // multiplier for stun duration var/punchdamagelow = 1 //lowest possible punch damage. if this is set to 0, punches will always miss var/punchdamagehigh = 10 //highest possible punch damage - var/punchstunthreshold = 10//damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical + var/punchstunthreshold = 10 //damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical + var/punchwoundbonus = 0 // additional wound bonus. generally zero. var/siemens_coeff = 1 //base electrocution coefficient var/damage_overlay_type = "human" //what kind of damage overlays (if any) appear on our species when wounded? var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"] @@ -106,7 +107,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/whitelisted = 0 //Is this species restricted to certain players? var/whitelist = list() //List the ckeys that can use this species, if it's whitelisted.: list("John Doe", "poopface666", "SeeALiggerPullTheTrigger") Spaces & capitalization can be included or ignored entirely for each key as it checks for both. var/icon_limbs //Overrides the icon used for the limbs of this species. Mainly for downstream, and also because hardcoded icons disgust me. Implemented and maintained as a favor in return for a downstream's implementation of synths. - var/species_type + var/species_category var/tail_type //type of tail i.e. mam_tail var/wagging_type //type of wagging i.e. waggingtail_lizard @@ -117,7 +118,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) //the ids you can use for your species, if empty, it means default only and not changeable var/list/allowed_limb_ids - //the type of eyes this species has + //override for the icon path used when setting bodypart overlays. Intended for species that don't fit in the standard 32x32 files. + var/override_bp_icon + //the icon state of the eyes this species has var/eye_type = "normal" /////////// @@ -369,8 +372,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(ROBOTIC_LIMBS in species_traits) for(var/obj/item/bodypart/B in C.bodyparts) - B.change_bodypart_status(BODYPART_ROBOTIC, FALSE, TRUE) // Makes all Bodyparts robotic. - B.render_like_organic = TRUE + B.change_bodypart_status(BODYPART_HYBRID, FALSE, TRUE) // Makes all Bodyparts 'robotic'. SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species) @@ -415,8 +417,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(ROBOTIC_LIMBS in species_traits) for(var/obj/item/bodypart/B in C.bodyparts) - B.change_bodypart_status(BODYPART_ORGANIC, FALSE, TRUE) - B.render_like_organic = FALSE + B.change_bodypart_status(initial(B.status), FALSE, TRUE) SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src) @@ -438,7 +439,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/dynamic_fhair_suffix = "" //for augmented heads - if(HD.status == BODYPART_ROBOTIC && !HD.render_like_organic) + if(HD.is_robotic_limb(FALSE)) return //we check if our hat or helmet hides our facial hair. @@ -1031,7 +1032,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) return FALSE if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED)) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST) - if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) + if(!H.w_uniform && !nojumpsuit && (!O || !O.is_robotic_limb())) if(return_warning) return_warning[1] = "You need a jumpsuit before you can attach this [I.name]!" return FALSE @@ -1073,7 +1074,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) return FALSE if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED)) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST) - if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) + if(!H.w_uniform && !nojumpsuit && (!O || !O.is_robotic_limb())) if(return_warning) return_warning[1] = "You need a jumpsuit before you can attach this [I.name]!" return FALSE @@ -1088,7 +1089,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_L_LEG) - if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) + if(!H.w_uniform && !nojumpsuit && (!O || !O.is_robotic_limb())) if(return_warning) return_warning[1] = "You need a jumpsuit before you can attach this [I.name]!" return FALSE @@ -1104,7 +1105,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_R_LEG) - if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) + if(!H.w_uniform && !nojumpsuit && (!O || !O.is_robotic_limb())) if(return_warning) return_warning[1] = "You need a jumpsuit before you can attach this [I.name]!" return FALSE @@ -1363,9 +1364,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(!(attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK)) if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes punching cause staminaloss but funny martial artist types get a discount - user.adjustStaminaLossBuffered(1.5) - else - user.adjustStaminaLossBuffered(3.5) + if(!user.UseStaminaBuffer(1.5, warn = TRUE)) + return + else if(!user.UseStaminaBuffer(3.5, warn = TRUE)) + return if(attacker_style && attacker_style.harm_act(user,target)) return TRUE @@ -1386,6 +1388,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh) + var/punchwoundbonus = user.dna.species.punchwoundbonus var/puncherstam = user.getStaminaLoss() var/puncherbrute = user.getBruteLoss() var/punchedstam = target.getStaminaLoss() @@ -1401,6 +1404,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) //END OF CITADEL CHANGES var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected)) + if(HAS_TRAIT(user, TRAIT_PUGILIST)) + affecting = target.get_bodypart(check_zone(user.zone_selected)) // if you're going the based unarmed route you won't miss if(!affecting) //Maybe the bodypart is missing? Or things just went wrong.. affecting = target.get_bodypart(BODY_ZONE_CHEST) //target chest instead, as failsafe. Or hugbox? You decide. @@ -1412,8 +1417,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(user.dna.species.punchdamagelow) if(atk_verb == ATTACK_EFFECT_KICK) //kicks never miss (provided your species deals more than 0 damage) miss_chance = 0 - else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists have a flat 10% miss chance - miss_chance = 10 + else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists, being good at Punching People, also never miss + miss_chance = 0 else miss_chance = min(10 + max(puncherstam * 0.5, puncherbrute * 0.5), 100) //probability of miss has a base of 10, and modified based on half brute total. Capped at max 100 to prevent weirdness in prob() @@ -1427,12 +1432,13 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/armor_block = target.run_armor_check(affecting, "melee") + if(HAS_TRAIT(user, TRAIT_MAULER)) // maulers get 15 armorpierce because if you're going to punch someone you might as well do a good job of it + armor_block = target.run_armor_check(affecting, "melee", armour_penetration = 15) // lot of good that sec jumpsuit did you playsound(target.loc, user.dna.species.attack_sound, 25, 1, -1) - - target.visible_message("[user] [atk_verb]s [target]!", \ - "[user] [atk_verb]s you!", null, COMBAT_MESSAGE_RANGE, null, \ - user, "You [atk_verb] [target]!") + target.visible_message("[user] [atk_verb]ed [target]!", \ + "[user] [atk_verb]ed you!", null, COMBAT_MESSAGE_RANGE, null, \ + user, "You [atk_verb]ed [target]!") target.lastattacker = user.real_name target.lastattackerckey = user.ckey @@ -1442,11 +1448,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) target.dismembering_strike(user, affecting.body_zone) if(atk_verb == ATTACK_EFFECT_KICK)//kicks deal 1.5x raw damage + 0.5x stamina damage - target.apply_damage(damage*1.5, attack_type, affecting, armor_block) + target.apply_damage(damage*1.5, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus) target.apply_damage(damage*0.5, STAMINA, affecting, armor_block) log_combat(user, target, "kicked") - else//other attacks deal full raw damage + 2x in stamina damage - target.apply_damage(damage, attack_type, affecting, armor_block) + else if(HAS_TRAIT(user, TRAIT_MAULER)) // mauler punches deal 1.3x raw damage + 1x stam damage, and have some armor pierce + target.apply_damage(damage*1.3, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus) + target.apply_damage(damage, STAMINA, affecting, armor_block) + log_combat(user, target, "punched (mauler)") + else //other attacks deal full raw damage + 2x in stamina damage + target.apply_damage(damage, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus) target.apply_damage(damage*2, STAMINA, affecting, armor_block) log_combat(user, target, "punched") @@ -1496,6 +1506,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) return FALSE else if(aim_for_mouth && ( target_on_help || target_restrained || target_aiming_for_mouth)) + if(!user.UseStaminaBuffer(3, warn = TRUE)) + return playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1) target.visible_message(\ @@ -1503,7 +1515,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) "[user] slaps you in the face! ",\ "You hear a slap.", target = user, target_message = "You slap [user == target ? "yourself" : "\the [target]"] in the face! ") user.do_attack_animation(target, ATTACK_EFFECT_FACE_SLAP) - user.adjustStaminaLossBuffered(3) if (!HAS_TRAIT(target, TRAIT_PERMABONER)) stop_wagging_tail(target) return FALSE @@ -1511,8 +1522,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(target.client?.prefs.cit_toggles & NO_ASS_SLAP) to_chat(user,"A force stays your hand, preventing you from slapping \the [target]'s ass!") return FALSE + if(!user.UseStaminaBuffer(3, warn = TRUE)) + return FALSE user.do_attack_animation(target, ATTACK_EFFECT_ASS_SLAP) - user.adjustStaminaLossBuffered(3) target.adjust_arousal(20,maso = TRUE) if (ishuman(target) && HAS_TRAIT(target, TRAIT_MASO) && target.has_dna() && prob(10)) target.mob_climax(forced_climax=TRUE) @@ -1530,9 +1542,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) user.do_attack_animation(target, ATTACK_EFFECT_DISARM) if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes disarmspam cause staminaloss, pugilists can do it almost effortlessly - user.adjustStaminaLossBuffered(1) + if(!user.UseStaminaBuffer(1, warn = TRUE)) + return else - user.adjustStaminaLossBuffered(3) + if(!user.UseStaminaBuffer(1, warn = TRUE)) + return if(attacker_style && attacker_style.disarm_act(user,target)) return TRUE @@ -1661,7 +1675,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/bloody = 0 if(((I.damtype == BRUTE) && I.force && prob(25 + (I.force * 2)))) - if(affecting.status == BODYPART_ORGANIC) + if(affecting.is_organic_limb(FALSE)) I.add_mob_blood(H) //Make the weapon bloody, not the person. if(prob(I.force * 2)) //blood spatter! bloody = 1 @@ -1768,9 +1782,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(CHECK_MOBILITY(user, MOBILITY_STAND)) to_chat(user, "You can only force yourself up if you're on the ground.") return + if(!user.UseStaminaBuffer(STAMINA_COST_SHOVE_UP, TRUE)) + return user.visible_message("[user] forces [p_them()]self up to [p_their()] feet!", "You force yourself up to your feet!") user.set_resting(FALSE, TRUE) - user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) /datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) @@ -1789,8 +1804,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) else if(user == target) return + if(!user.UseStaminaBuffer(4, warn = TRUE)) + return user.do_attack_animation(target, ATTACK_EFFECT_DISARM) - user.adjustStaminaLossBuffered(4) playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) if(target.w_uniform) diff --git a/code/modules/mob/living/carbon/human/species_types/abductors.dm b/code/modules/mob/living/carbon/human/species_types/abductor.dm similarity index 90% rename from code/modules/mob/living/carbon/human/species_types/abductors.dm rename to code/modules/mob/living/carbon/human/species_types/abductor.dm index 38e10e8662..004ba267e8 100644 --- a/code/modules/mob/living/carbon/human/species_types/abductors.dm +++ b/code/modules/mob/living/carbon/human/species_types/abductor.dm @@ -1,12 +1,12 @@ /datum/species/abductor name = "Abductor" - id = "abductor" + id = SPECIES_ABDUCTOR say_mod = "gibbers" sexes = FALSE species_traits = list(NOBLOOD,NOEYES,NOGENITALS,NOAROUSAL,HAS_FLESH,HAS_BONE) inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_CHUNKYFINGERS,TRAIT_NOHUNGER,TRAIT_NOBREATH) mutanttongue = /obj/item/organ/tongue/abductor - species_type = "alien" + species_category = SPECIES_CATEGORY_ALIEN /datum/species/abductor/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() diff --git a/code/modules/mob/living/carbon/human/species_types/android.dm b/code/modules/mob/living/carbon/human/species_types/android.dm index 5d43d18429..492f3d58e0 100644 --- a/code/modules/mob/living/carbon/human/species_types/android.dm +++ b/code/modules/mob/living/carbon/human/species_types/android.dm @@ -1,6 +1,6 @@ /datum/species/android name = "Android" - id = "android" + id = SPECIES_ANDROID say_mod = "states" species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,ROBOTIC_LIMBS) inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_NOFIRE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_LIMBATTACHMENT) @@ -11,16 +11,4 @@ mutanttongue = /obj/item/organ/tongue/robot species_language_holder = /datum/language_holder/synthetic limbs_id = "synth" - species_type = "robotic" - -/datum/species/android/on_species_gain(mob/living/carbon/C) - . = ..() - for(var/X in C.bodyparts) - var/obj/item/bodypart/O = X - O.change_bodypart_status(BODYPART_ROBOTIC, FALSE, TRUE) - -/datum/species/android/on_species_loss(mob/living/carbon/C) - . = ..() - for(var/X in C.bodyparts) - var/obj/item/bodypart/O = X - O.change_bodypart_status(BODYPART_ORGANIC,FALSE, TRUE) + species_category = SPECIES_CATEGORY_ROBOT diff --git a/code/modules/mob/living/carbon/human/species_types/angel.dm b/code/modules/mob/living/carbon/human/species_types/angel.dm index 3cae574a90..9c87f3ad5d 100644 --- a/code/modules/mob/living/carbon/human/species_types/angel.dm +++ b/code/modules/mob/living/carbon/human/species_types/angel.dm @@ -1,6 +1,6 @@ /datum/species/angel name = "Angel" - id = "angel" + id = SPECIES_ANGEL default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE) mutant_bodyparts = list("tail_human" = "None", "ears" = "None", "wings" = "Angel") @@ -9,7 +9,7 @@ blacklisted = 1 limbs_id = "human" skinned_type = /obj/item/stack/sheet/animalhide/human - species_type = "human" //they're a kind of human + species_category = SPECIES_CATEGORY_BASIC //they're a kind of human var/datum/action/innate/flight/fly diff --git a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/code/modules/mob/living/carbon/human/species_types/anthromorph.dm similarity index 87% rename from code/modules/mob/living/carbon/human/species_types/furrypeople.dm rename to code/modules/mob/living/carbon/human/species_types/anthromorph.dm index 534536d6e7..95aec26d9f 100644 --- a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/anthromorph.dm @@ -1,6 +1,6 @@ /datum/species/mammal name = "Anthromorph" - id = "mammal" + id = SPECIES_MAMMAL default_color = "4B4B4B" species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE) inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BEAST @@ -15,6 +15,6 @@ tail_type = "mam_tail" wagging_type = "mam_waggingtail" - species_type = "furry" + species_category = SPECIES_CATEGORY_FURRY - allowed_limb_ids = list("mammal","aquatic","avian") \ No newline at end of file + allowed_limb_ids = list("mammal","aquatic","avian") diff --git a/code/modules/mob/living/carbon/human/species_types/arachnid.dm b/code/modules/mob/living/carbon/human/species_types/arachnid.dm new file mode 100644 index 0000000000..2072883c54 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types/arachnid.dm @@ -0,0 +1,155 @@ +/datum/species/arachnid + name = "Arachnid" + id = "arachnid" + override_bp_icon = 'icons/mob/arachnid_parts.dmi' + say_mod = "chitters" + default_color = "00FF00" + species_traits = list(LIPS, NOEYES, NO_UNDERWEAR) + inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG + mutant_bodyparts = list("arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain") + attack_verb = "slash" + attack_sound = 'sound/weapons/slash.ogg' + miss_sound = 'sound/weapons/slashmiss.ogg' + meat = /obj/item/reagent_containers/food/snacks/meat/slab/spider + liked_food = MEAT | RAW + disliked_food = FRUIT | GROSS + toxic_food = VEGETABLES | DAIRY + mutanteyes = /obj/item/organ/eyes/night_vision/arachnid + mutanttongue = /obj/item/organ/tongue/arachnid + species_language_holder = /datum/language_holder/arachnid + brutemod = 1.1 + var/web_cooldown = 200 + var/web_ready = TRUE + var/spinner_rate = 25 + +/datum/species/arachnid/random_name(gender,unique,lastname) + if(unique) + return random_unique_arachnid_name() + + var/randname = arachnid_name() + + if(lastname) + randname += " [lastname]" + + return randname + +/datum/species/arachnid/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + . = ..() + if(chem.type == /datum/reagent/toxin/pestkiller) + H.adjustToxLoss(3) + H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) + +/datum/species/arachnid/check_weakness(obj/item/weapon, mob/living/attacker) + if(istype(weapon, /obj/item/melee/flyswatter)) + return 9 //flyswatters deal 10x damage to arachnids + return 0 + +/datum/species/arachnid/on_species_gain(mob/living/carbon/human/H, datum/species/old_species) + . = ..() + var/datum/action/innate/spin_web/SW = new + var/datum/action/innate/spin_cocoon/SC = new + SC.Grant(H) + SW.Grant(H) + +/datum/species/arachnid/on_species_loss(mob/living/carbon/human/H) + . = ..() + var/datum/action/innate/spin_web/SW = locate(/datum/action/innate/spin_web) in H.actions + var/datum/action/innate/spin_cocoon/SC = locate(/datum/action/innate/spin_cocoon) in H.actions + SC?.Remove(H) + SW?.Remove(H) + +/datum/action/innate/spin_web + name = "Spin Web" + check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS + icon_icon = 'icons/mob/actions/actions_animal.dmi' + button_icon_state = "lay_web" + +/datum/action/innate/spin_cocoon + name = "Spin Cocoon" + check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS + icon_icon = 'icons/mob/actions/actions_animal.dmi' + button_icon_state = "wrap_0" + +/datum/action/innate/spin_web/Activate() + var/mob/living/carbon/human/species/arachnid/H = owner + var/datum/species/arachnid/E = H.dna.species + if(H.stat == "DEAD") + return + if(E.web_ready == FALSE) + to_chat(H, "You need to wait a while to regenerate web fluid.") + return + var/turf/T = get_turf(H) + if(!T) + to_chat(H, "There's no room to spin your web here!") + return + var/obj/structure/spider/stickyweb/W = locate() in T + var/obj/structure/arachnid/W2 = locate() in T + if(W || W2) + to_chat(H, "There's already a web here!") + return + // Should have some minimum amount of food before trying to activate + var/nutrition_threshold = NUTRITION_LEVEL_FED + if (H.nutrition >= nutrition_threshold) + to_chat(H, "You begin spinning some web...") + if(!do_after(H, 10 SECONDS, 1, T)) + to_chat(H, "Your web spinning was interrupted!") + return + H.adjust_nutrition(-E.spinner_rate) + addtimer(VARSET_CALLBACK(E, web_ready, TRUE), E.web_cooldown) + to_chat(H, "You use up a fair amount of energy weaving a web on the ground with your spinneret!") + new /obj/structure/arachnid(T, owner) + + else + to_chat(H, "You're too hungry to spin web right now, eat something first!") + return +/* + This took me far too long to figure out so I'm gonna document it here. + 1) Create an innate action for the species + 2) Have that action trigger a RegisterSignal for mob clicking + 3) Trigger the cocoonAtom proc on that signal + 4) Validate the target then start spinning + 5) if you're not interrupted, force move the target to the cocoon created at their location. +*/ +/datum/action/innate/spin_cocoon/Activate() + var/mob/living/carbon/human/species/arachnid/H = owner + var/datum/species/arachnid/E = H.dna.species + if(H.stat == "DEAD") + return + if(E.web_ready == FALSE) + to_chat(H, "You need to wait awhile to regenerate web fluid.") + return + var/nutrition_threshold = NUTRITION_LEVEL_FED + if (H.nutrition >= nutrition_threshold) + to_chat(H, "You pull out a strand from your spinneret, ready to wrap a target.
\ + (Press ALT+CLICK on the target to start wrapping.)
") + H.adjust_nutrition(E.spinner_rate * -0.5) + addtimer(VARSET_CALLBACK(E, web_ready, TRUE), E.web_cooldown) + RegisterSignal(H, list(COMSIG_MOB_ALTCLICKON), .proc/cocoonAtom) + return + else + to_chat(H, "You're too hungry to spin web right now, eat something first!") + return + +/datum/action/innate/spin_cocoon/proc/cocoonAtom(mob/living/carbon/human/species/arachnid/H, atom/movable/A) + UnregisterSignal(H, list(COMSIG_MOB_ALTCLICKON)) + var/datum/species/arachnid/E = H.dna.species + if (!H || !isarachnid(H)) + return COMSIG_MOB_CANCEL_CLICKON + else + if(E.web_ready == FALSE) + to_chat(H, "You need to wait awhile to regenerate web fluid.") + return + if(!do_after(H, 10 SECONDS, 1, A)) + to_chat(H, "Your web spinning was interrupted!") + return + H.adjust_nutrition(E.spinner_rate * -3) + var/obj/structure/arachnid/cocoon/C = new(A.loc) + if(isliving(A)) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + A.forceMove(C) + H.visible_message("[H] wraps [A] into a large cocoon!") + return + else + A.forceMove(C) + H.visible_message("[H] wraps [A] into a cocoon!") + return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species_types/bugmen.dm b/code/modules/mob/living/carbon/human/species_types/bugmen.dm index 6eccde61a9..773651d464 100644 --- a/code/modules/mob/living/carbon/human/species_types/bugmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/bugmen.dm @@ -1,6 +1,6 @@ /datum/species/insect name = "Anthromorphic Insect" - id = "insect" + id = SPECIES_INSECT say_mod = "chitters" default_color = "00FF00" species_traits = list(LIPS,EYECOLOR,HAIR,FACEHAIR,MUTCOLORS,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE) @@ -18,7 +18,7 @@ tail_type = "mam_tail" wagging_type = "mam_waggingtail" - species_type = "insect" + species_category = SPECIES_CATEGORY_INSECT allowed_limb_ids = list("insect","apid","moth","moth_not_greyscale") diff --git a/code/modules/mob/living/carbon/human/species_types/corporate.dm b/code/modules/mob/living/carbon/human/species_types/corporate.dm index a2597ed286..e9baa786da 100644 --- a/code/modules/mob/living/carbon/human/species_types/corporate.dm +++ b/code/modules/mob/living/carbon/human/species_types/corporate.dm @@ -18,4 +18,4 @@ inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOLIMBDISABLE,TRAIT_NOHUNGER) sexes = 0 gib_types = /obj/effect/gibspawner/robot - species_type = "robotic" \ No newline at end of file + species_category = SPECIES_CATEGORY_ROBOT diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index f3b499093b..f756fd8816 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -1,6 +1,6 @@ /datum/species/dullahan name = "Dullahan" - id = "dullahan" + id = SPECIES_DULLAHAN default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE) inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH) @@ -14,7 +14,7 @@ limbs_id = "human" skinned_type = /obj/item/stack/sheet/animalhide/human has_field_of_vision = FALSE //Too much of a trouble, their vision is already bound to their severed head. - species_type = "undead" + species_category = SPECIES_CATEGORY_UNDEAD var/pumpkin = FALSE var/obj/item/dullahan_relay/myhead diff --git a/code/modules/mob/living/carbon/human/species_types/dwarves.dm b/code/modules/mob/living/carbon/human/species_types/dwarves.dm index a465e7c3a9..7c35ade4f2 100644 --- a/code/modules/mob/living/carbon/human/species_types/dwarves.dm +++ b/code/modules/mob/living/carbon/human/species_types/dwarves.dm @@ -4,7 +4,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) // /datum/species/dwarf //not to be confused with the genetic manlets name = "Dwarf" - id = "dwarf" //Also called Homo sapiens pumilionis + id = SPECIES_DWARF //Also called Homo sapiens pumilionis default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE) inherent_traits = list(TRAIT_DWARF,TRAIT_SNOB) @@ -18,7 +18,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) // mutant_organs = list(/obj/item/organ/dwarfgland) //Dwarven alcohol gland, literal gland warrior mutantliver = /obj/item/organ/liver/dwarf //Dwarven super liver (Otherwise they r doomed) species_language_holder = /datum/language_holder/dwarf - species_type = "human" //a kind of human + species_category = SPECIES_CATEGORY_BASIC //a kind of human /mob/living/carbon/human/species/dwarf //species admin spawn path race = /datum/species/dwarf //and the race the path is set to. @@ -51,17 +51,18 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) // //Dwarf Speech handling - Basically a filter/forces them to say things. The IC helper /datum/species/dwarf/proc/handle_speech(datum/source, list/speech_args) var/message = speech_args[SPEECH_MESSAGE] - if(speech_args[SPEECH_LANGUAGE] != /datum/language/dwarf) // No accent if they speak their language - if(message[1] != "*") - message = " [message]" //Credits to goonstation for the strings list. - var/list/dwarf_words = strings("dwarf_replacement.json", "dwarf") //thanks to regex too. - for(var/key in dwarf_words) //Theres like 1459 words or something man. - var/value = dwarf_words[key] //Thus they will always be in character. - if(islist(value)) //Whether they like it or not. - value = pick(value) //This could be drastically reduced if needed though. - message = replacetextEx(message, " [uppertext(key)]", " [uppertext(value)]") - message = replacetextEx(message, " [capitalize(key)]", " [capitalize(value)]") - message = replacetextEx(message, " [key]", " [value]") //Also its scottish. + if(speech_args[SPEECH_LANGUAGE] != /datum/language/dwarf && message[1] != "*") // No accent if they speak their language + message = " [message]" //Credits to goonstation for the strings list. + var/list/dwarf_words = strings("dwarf_replacement.json", "dwarf") //thanks to regex too. + for(var/word in splittext(message," ")) + var/value = dwarf_words[word] //Thus they will always be in character. + if(!value) + continue + if(islist(value)) //Whether they like it or not. + value = pick(value) //This could be drastically reduced if needed though. + message = replacetextEx(message, " [uppertext(word)]", " [uppertext(value)]") + message = replacetextEx(message, " [capitalize(word)]", " [capitalize(value)]") + message = replacetextEx(message, " [word]", " [value]") //Also its scottish. if(prob(3)) message += " By Armok!" diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index c182defda4..d92f4ab14a 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -2,7 +2,7 @@ /datum/species/ethereal name = "Ethereal" - id = "ethereal" + id = SPECIES_ETHEREAL attack_verb = "burn" attack_sound = 'sound/weapons/etherealhit.ogg' miss_sound = 'sound/weapons/etherealmiss.ogg' diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index b760fd0aee..011b6b7642 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -1,7 +1,7 @@ //Subtype of human /datum/species/human/felinid name = "Felinid" - id = "felinid" + id = SPECIES_FELINID limbs_id = "human" mutant_bodyparts = list("mam_tail" = "Cat", "mam_ears" = "Cat", "deco_wings" = "None") @@ -11,7 +11,7 @@ tail_type = "mam_tail" wagging_type = "mam_waggingtail" - species_type = "furry" + species_category = SPECIES_CATEGORY_FURRY /datum/species/human/felinid/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) if(ishuman(C)) diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm index c56adaf1b0..c3e406244b 100644 --- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm @@ -1,6 +1,6 @@ /datum/species/fly name = "Anthromorphic Fly" - id = "fly" + id = SPECIES_FLY say_mod = "buzzes" species_traits = list(NOEYES,HAS_FLESH,HAS_BONE) inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG @@ -12,7 +12,7 @@ liked_food = GROSS exotic_bloodtype = "BUG" exotic_blood_color = BLOOD_COLOR_BUG - species_type = "insect" + species_category = SPECIES_CATEGORY_INSECT /datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) if(istype(chem, /datum/reagent/toxin/pestkiller)) diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 438eaf1eea..ff0287d7b9 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -1,7 +1,7 @@ /datum/species/golem // Animated beings of stone. They have increased defenses, and do not need to breathe. They're also slow as fuuuck. name = "Golem" - id = "iron golem" + id = SPECIES_GOLEM species_traits = list(NOBLOOD,MUTCOLORS,NO_UNDERWEAR,NOGENITALS,NOAROUSAL) inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_CHUNKYFINGERS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) inherent_biotypes = MOB_HUMANOID|MOB_MINERAL @@ -32,7 +32,7 @@ var/special_name_chance = 5 var/owner //dobby is a free golem - species_type = "golem" + species_category = SPECIES_CATEGORY_GOLEM /datum/species/golem/random_name(gender,unique,lastname) var/golem_surname = pick(GLOB.golem_names) diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index f175ddb921..e8e0ff1a78 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -1,6 +1,6 @@ /datum/species/human name = "Human" - id = "human" + id = SPECIES_HUMAN default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR,HAS_FLESH,HAS_BONE) @@ -12,7 +12,7 @@ tail_type = "tail_human" wagging_type = "waggingtail_human" - species_type = "human" + species_category = SPECIES_CATEGORY_BASIC /datum/species/human/spec_death(gibbed, mob/living/carbon/human/H) if(H) diff --git a/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm index 9bfe661f47..6353fd5184 100644 --- a/code/modules/mob/living/carbon/human/species_types/ipc.dm +++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm @@ -1,6 +1,6 @@ /datum/species/ipc name = "I.P.C." - id = "ipc" + id = SPECIES_IPC say_mod = "beeps" default_color = "00FF00" blacklisted = 0 @@ -28,7 +28,7 @@ exotic_bloodtype = "HF" exotic_blood_color = BLOOD_COLOR_OIL - species_type = "robotic" + species_category = SPECIES_CATEGORY_ROBOT var/datum/action/innate/monitor_change/screen diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 6193172f06..58a10fb08e 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -1,7 +1,7 @@ /datum/species/jelly // Entirely alien beings that seem to be made entirely out of gel. They have three eyes and a skeleton visible within them. name = "Xenobiological Jelly Entity" - id = "jelly" + id = SPECIES_JELLY default_color = "00FF90" say_mod = "chirps" species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH) @@ -29,7 +29,7 @@ tail_type = "mam_tail" wagging_type = "mam_waggingtail" - species_type = "jelly" + species_category = SPECIES_CATEGORY_JELLY /obj/item/organ/brain/jelly name = "slime nucleus" @@ -158,7 +158,7 @@ /datum/species/jelly/slime name = "Xenobiological Slime Entity" - id = "slime" + id = SPECIES_SLIME default_color = "00FFFF" species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR) say_mod = "says" @@ -467,7 +467,7 @@ /datum/species/jelly/roundstartslime name = "Xenobiological Slime Hybrid" - id = "slimeperson" + id = SPECIES_SLIME_HYBRID limbs_id = "slime" default_color = "00FFFF" species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR) @@ -777,7 +777,7 @@ /datum/species/jelly/luminescent name = "Luminescent Slime Entity" - id = "lum" + id = SPECIES_SLIME_LUMI say_mod = "says" var/glow_intensity = LUMINESCENT_DEFAULT_GLOW var/obj/effect/dummy/luminescent_glow/glow @@ -944,7 +944,7 @@ /datum/species/jelly/stargazer name = "Stargazer Slime Entity" - id = "stargazer" + id = SPECIES_STARGAZER var/datum/action/innate/project_thought/project_thought var/datum/action/innate/link_minds/link_minds var/list/mob/living/linked_mobs = list() diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index a8e3f89957..c37a24a93a 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -1,7 +1,7 @@ /datum/species/lizard // Reptilian humanoids with scaled skin and tails. name = "Anthromorphic Lizard" - id = "lizard" + id = SPECIES_LIZARD say_mod = "hisses" default_color = "00FF00" species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE) @@ -28,7 +28,7 @@ tail_type = "tail_lizard" wagging_type = "waggingtail_lizard" - species_type = "lizard" + species_category = "lizard" /datum/species/lizard/random_name(gender,unique,lastname) if(unique) @@ -46,7 +46,7 @@ */ /datum/species/lizard/ashwalker name = "Ash Walker" - id = "ashlizard" + id = SPECIES_ASHWALKER limbs_id = "lizard" species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE) inherent_traits = list(TRAIT_CHUNKYFINGERS) diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm index f91c3518a3..dcbdf91485 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -1,6 +1,6 @@ /datum/species/mush //mush mush codecuck name = "Anthromorphic Mushroom" - id = "mush" + id = SPECIES_MUSHROOM mutant_bodyparts = list("caps" = "Round") fixed_mut_color = "DBBF92" @@ -21,7 +21,7 @@ burnmod = 1.25 heatmod = 1.5 - species_type = "plant" + species_category = SPECIES_CATEGORY_PLANT mutanteyes = /obj/item/organ/eyes/night_vision/mushroom var/datum/martial_art/mushpunch/mush diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index 8c30adb8f9..12a520b463 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -1,6 +1,6 @@ /datum/species/plasmaman name = "Plasmaman" - id = "plasmaman" + id = SPECIES_PLASMAMAN say_mod = "rattles" sexes = 0 meat = /obj/item/stack/sheet/mineral/plasma @@ -22,7 +22,7 @@ liked_food = VEGETABLES outfit_important_for_life = /datum/outfit/plasmaman - species_type = "skeleton" + species_category = SPECIES_CATEGORY_SKELETON /datum/species/plasmaman/spec_life(mob/living/carbon/human/H) var/datum/gas_mixture/environment = H.loc.return_air() diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index 0f62953e6c..fae8ebfb0d 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -1,7 +1,7 @@ /datum/species/pod // A mutation caused by a human being ressurected in a revival pod. These regain health in light, and begin to wither in darkness. name = "Anthromorphic Plant" - id = "pod" + id = SPECIES_POD default_color = "59CE00" species_traits = list(MUTCOLORS,EYECOLOR,CAN_SCAR,HAS_FLESH,HAS_BONE) attack_verb = "slash" @@ -19,7 +19,7 @@ var/light_burnheal = -1 var/light_bruteheal = -1 - species_type = "plant" + species_category = SPECIES_CATEGORY_PLANT allowed_limb_ids = list("pod","mush") @@ -66,7 +66,7 @@ /datum/species/pod/pseudo_weak name = "Anthromorphic Plant" - id = "podweak" + id = SPECIES_POD_WEAK species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS) mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "mam_body_markings" = "Husky", "taur" = "None", "legs" = "Normal Legs") limbs_id = "pod" diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index a7d9ce4473..7dc8083264 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -4,7 +4,7 @@ /datum/species/shadow // Humans cursed to stay in the darkness, lest their life forces drain. They regain health in shadow and die in light. name = "???" - id = "shadow" + id = SPECIES_SHADOW sexes = 0 blacklisted = 1 ignored_by = list(/mob/living/simple_animal/hostile/faithless) @@ -15,7 +15,7 @@ dangerous_existence = 1 mutanteyes = /obj/item/organ/eyes/night_vision - species_type = "shadow" + species_category = SPECIES_CATEGORY_SHADOW /datum/species/shadow/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() @@ -32,7 +32,7 @@ /datum/species/shadow/nightmare name = "Nightmare" - id = "nightmare" + id = SPECIES_NIGHTMARE limbs_id = "shadow" burnmod = 1.5 blacklisted = TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index 1b6bef9dc6..b0673a7df4 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -1,6 +1,6 @@ /datum/species/skeleton name = "Skeleton" - id = "skeleton" + id = SPECIES_SKELETON say_mod = "rattles" blacklisted = 0 sexes = 0 @@ -15,7 +15,7 @@ brutemod = 1.25 burnmod = 1.25 - species_type = "skeleton" //they have their own category that's disassociated from undead, paired with plasmapeople + species_category = SPECIES_CATEGORY_SKELETON //they have their own category that's disassociated from undead, paired with plasmapeople /datum/species/skeleton/New() if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) //skeletons are stronger during the spooky season! @@ -31,7 +31,7 @@ /datum/species/skeleton/space name = "Spooky Spacey Skeleton" - id = "spaceskeleton" + id = SPECIES_SKELETON_SPACE limbs_id = "skeleton" blacklisted = 1 inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT, TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER) diff --git a/code/modules/mob/living/carbon/human/species_types/synthliz.dm b/code/modules/mob/living/carbon/human/species_types/synthliz.dm index b446e3796e..88781532d3 100644 --- a/code/modules/mob/living/carbon/human/species_types/synthliz.dm +++ b/code/modules/mob/living/carbon/human/species_types/synthliz.dm @@ -1,6 +1,6 @@ /datum/species/synthliz name = "Synthetic Lizardperson" - id = "synthliz" + id = SPECIES_SYNTH_LIZARD say_mod = "beeps" default_color = "00FF00" species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE) @@ -27,4 +27,4 @@ tail_type = "mam_tail" wagging_type = "mam_waggingtail" - species_type = "robotic" + species_category = SPECIES_CATEGORY_ROBOT diff --git a/code/modules/mob/living/carbon/human/species_types/synths.dm b/code/modules/mob/living/carbon/human/species_types/synths.dm index 3d55ce1027..5d2b6f4c77 100644 --- a/code/modules/mob/living/carbon/human/species_types/synths.dm +++ b/code/modules/mob/living/carbon/human/species_types/synths.dm @@ -1,6 +1,6 @@ /datum/species/synth name = "Synthetic" //inherited from the real species, for health scanners and things - id = "synth" + id = SPECIES_SYNTH say_mod = "beep boops" //inherited from a user's real species sexes = 0 species_traits = list(NOTRANSSTING,NOGENITALS,NOAROUSAL) //all of these + whatever we inherit from the real species @@ -17,11 +17,11 @@ var/disguise_fail_health = 75 //When their health gets to this level their synthflesh partially falls off var/datum/species/fake_species = null //a species to do most of our work for us, unless we're damaged species_language_holder = /datum/language_holder/synthetic - species_type = "robotic" + species_category = SPECIES_CATEGORY_ROBOT /datum/species/synth/military name = "Military Synth" - id = "military_synth" + id = SPECIES_SYNTH_MIL armor = 25 punchdamagelow = 10 punchdamagehigh = 19 diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 857fb6d8ed..4125d3beab 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -1,6 +1,6 @@ /datum/species/vampire name = "Vampire" - id = "vampire" + id = SPECIES_VAMPIRE default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD,HAS_FLESH,HAS_BONE) inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH) @@ -14,7 +14,7 @@ limbs_id = "human" skinned_type = /obj/item/stack/sheet/animalhide/human var/info_text = "You are a Vampire. You will slowly but constantly lose blood if outside of a coffin. If inside a coffin, you will slowly heal. You may gain more blood by grabbing a live victim and using your drain ability." - species_type = "undead" + species_category = SPECIES_CATEGORY_UNDEAD /datum/species/vampire/check_roundstart_eligible() if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) diff --git a/code/modules/mob/living/carbon/human/species_types/xeno.dm b/code/modules/mob/living/carbon/human/species_types/xeno.dm index ddd1c86f0a..2757c624e7 100644 --- a/code/modules/mob/living/carbon/human/species_types/xeno.dm +++ b/code/modules/mob/living/carbon/human/species_types/xeno.dm @@ -1,7 +1,7 @@ /datum/species/xeno // A cloning mistake, crossing human and xenomorph DNA name = "Xenomorph Hybrid" - id = "xeno" + id = SPECIES_XENOHYBRID say_mod = "hisses" default_color = "00FF00" species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR) @@ -15,4 +15,4 @@ exotic_bloodtype = "X*" damage_overlay_type = "xeno" liked_food = MEAT - species_type = "alien" + species_category = SPECIES_CATEGORY_ALIEN diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 8d86ad2fa9..f1c0b8e7c0 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -15,7 +15,7 @@ var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg') disliked_food = NONE liked_food = GROSS | MEAT | RAW - species_type = "undead" + species_category = SPECIES_CATEGORY_UNDEAD /datum/species/zombie/notspaceproof id = "notspaceproofzombie" @@ -98,7 +98,7 @@ // Your skin falls off /datum/species/krokodil_addict - name = "Human" + name = SPECIES_HUMAN id = "goofzombies" limbs_id = "zombie" //They look like zombies sexes = 0 diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 71833d22a3..7d7d420128 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -696,7 +696,7 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if continue . += "-[BP.body_zone]" - if(BP.status == BODYPART_ORGANIC) + if(BP.is_organic_limb(FALSE)) . += "-organic" else . += "-robotic" diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 8f181d47fd..30785a0103 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -502,19 +502,14 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put //this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc.. /mob/living/carbon/handle_status_effects() ..() - if(getStaminaLoss() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active - adjustStaminaLoss(!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL) + var/combat_mode = SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) + if(getStaminaLoss() && !HAS_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION)) + adjustStaminaLoss((!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL) * (combat_mode? 0.25 : 1)) if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1) incomingstammult = max(0.01, incomingstammult) incomingstammult = min(1, incomingstammult*2) - //CIT CHANGES START HERE. STAMINA BUFFER STUFF - if(bufferedstam && world.time > stambufferregentime) - var/drainrate = max((bufferedstam*(bufferedstam/(5)))*0.1,1) - bufferedstam = max(bufferedstam - drainrate, 0) - //END OF CIT CHANGES - var/restingpwr = 1 + 4 * !CHECK_MOBILITY(src, MOBILITY_STAND) //Dizziness diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index 1a796fb2bc..a1ea18a8b9 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -259,7 +259,7 @@ . += "-digitigrade[BP.use_digitigrade]" if(BP.animal_origin) . += "-[BP.animal_origin]" - if(BP.status == BODYPART_ORGANIC) + if(BP.is_organic_limb(FALSE)) . += "-organic" else . += "-robotic" diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 1d78979cbd..4ee52a08a6 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -562,7 +562,7 @@ if(. && iscarbon(user)) var/mob/living/carbon/C = user if(isjellyperson(C)) - pick(playsound(C, 'sound/effects/attackblob.ogg', 50, 1),playsound(C, 'sound/effects/blobattack.ogg', 50, 1)) + playsound(C, 'sound/effects/attackblob.ogg', 50, 1) /datum/emote/living/audio_emote/blurp key = "blurp" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 8dac56a8c9..da03c4b533 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -8,6 +8,8 @@ for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) faction += "[REF(src)]" + stamina_buffer = INFINITY + UpdateStaminaBuffer() GLOB.mob_living_list += src /mob/living/prepare_huds() @@ -842,12 +844,12 @@ return var/strip_mod = 1 var/strip_silence = FALSE - if (ishuman(src)) //carbon doesn't actually wear gloves + if(ishuman(src)) //carbon doesn't actually wear gloves var/mob/living/carbon/C = src - var/obj/item/clothing/gloves/g = C.gloves - if (istype(g)) - strip_mod = g.strip_mod - strip_silence = g.strip_silence + var/obj/item/clothing/gloves/G = C.gloves + if(istype(G)) + strip_mod = G.strip_mod + strip_silence = G.strip_silence if (!strip_silence) who.visible_message("[src] tries to remove [who]'s [what.name].", \ "[src] tries to remove your [what.name].", target = src, @@ -1030,7 +1032,7 @@ return TRUE return FALSE -/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback) +/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force) stop_pulling() . = ..() @@ -1257,7 +1259,7 @@ SetUnconscious(clamp_unconscious_to) HealAllImmobilityUpTo(clamp_immobility_to) adjustStaminaLoss(min(0, -stamina_boost)) - adjustStaminaLossBuffered(min(0, -stamina_buffer_boost)) + RechargeStaminaBuffer(stamina_buffer_boost) // this MUST GO AFTER ADJUSTSTAMINALOSS. if(scale_stamina_loss_recovery) adjustStaminaLoss(min(-((getStaminaLoss() - stamina_loss_recovery_bypass) * scale_stamina_loss_recovery), 0)) if(put_on_feet) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 8e8b94463b..db472df388 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -8,6 +8,8 @@ active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT) + REMOVE_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION, ACTIVE_BLOCK_TRAIT) + REMOVE_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION, ACTIVE_BLOCK_TRAIT) remove_movespeed_modifier(/datum/movespeed_modifier/active_block) var/datum/block_parry_data/data = I.get_block_parry_data() DelayNextAction(data.block_end_click_cd_add) @@ -27,6 +29,10 @@ ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point if(data.block_lock_sprinting) ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT) + if(data.block_no_stamina_regeneration) + ADD_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION, ACTIVE_BLOCK_TRAIT) + if(data.block_no_stambuffer_regeneration) + ADD_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION, ACTIVE_BLOCK_TRAIT) add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown) active_block_effect_start() return TRUE @@ -192,7 +198,7 @@ var/held_index = C.get_held_index_of_item(src) var/obj/item/bodypart/BP = C.hand_bodyparts[held_index] if(!BP?.body_zone) - return C.adjustStaminaLossBuffered(stamina_amount) //nah + return C.adjustStaminaLoss(stamina_amount) //nah var/zone = BP.body_zone var/stamina_to_zone = data.block_stamina_limb_ratio * stamina_amount var/stamina_to_chest = stamina_amount - stamina_to_zone @@ -200,9 +206,9 @@ stamina_to_chest -= stamina_buffered C.apply_damage(stamina_to_zone, STAMINA, zone) C.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST) - C.adjustStaminaLossBuffered(stamina_buffered) + C.adjustStaminaLoss(stamina_buffered) else - owner.adjustStaminaLossBuffered(stamina_amount) + owner.adjustStaminaLoss(stamina_amount) /obj/item/proc/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) return diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 10dece1ffc..7353add745 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -68,12 +68,13 @@ // can always implement it later, whatever. if((data.parry_respect_clickdelay && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time)) to_chat(src, "You are not ready to parry (again)!") - return + return FALSE // Point of no return, make sure everything is set. parrying = method if(method == ITEM_PARRY) active_parry_item = using_item - adjustStaminaLossBuffered(data.parry_stamina_cost) + if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE)) + return FALSE parry_start_time = world.time successful_parries = list() addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 47dae8849d..08ef674b33 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -74,8 +74,12 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Ratio of stamina incurred by chest (so after [block_stamina_limb_ratio] runs) that is buffered. var/block_stamina_buffer_ratio = 1 - /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block. + /// Stamina dealt directly via UseStaminaBuffer() per SECOND of block. var/block_stamina_cost_per_second = 1.5 + /// Prevent stamina buffer regeneration while block? + var/block_no_stambuffer_regeneration = TRUE + /// Prevent stamina regeneration while block? + var/block_no_stamina_regeneration = FALSE /// Bitfield for attack types that we can block while down. This will work in any direction. var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE @@ -307,7 +311,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /mob/living/proc/handle_block_parry(seconds = 1) if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING) var/datum/block_parry_data/data = return_block_parry_datum(active_block_item.block_parry_data) - adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds) + UseStaminaBuffer(data.block_stamina_cost_per_second * seconds) /mob/living/on_item_dropped(obj/item/I) if(I == active_block_item) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 5495d37297..5621fab9c1 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -53,6 +53,7 @@ var/hallucination = 0 //Directly affects how long a mob will hallucinate for + var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. var/timeofdeath = 0 //Allows mobs to move through dense areas without restriction. For instance, in space or out of holder objects. @@ -148,9 +149,6 @@ var/combatmessagecooldown = 0 var/incomingstammult = 1 - var/bufferedstam = 0 - var/stambuffer = 20 - var/stambufferregentime //Sprint buffer--- var/sprint_buffer = 42 //Tiles @@ -159,3 +157,13 @@ var/sprint_buffer_regen_last = 0 //last world.time this was regen'd for math. var/sprint_stamina_cost = 0.70 //stamina loss per tile while insufficient sprint buffer. //---End + + // Stamina Buffer--- + /// Our stamina buffer + var/stamina_buffer + /// Stamina buffer regen modifier + var/stamina_buffer_regen_mod = 1 + /// Last time stamina buffer regen was done + var/stamina_buffer_regen_last = 0 + /// Last time we used stamina buffer + var/stamina_buffer_last_use = 0 diff --git a/code/modules/mob/living/living_mobility.dm b/code/modules/mob/living/living_mobility.dm index 92de711c27..8c6cf9d207 100644 --- a/code/modules/mob/living/living_mobility.dm +++ b/code/modules/mob/living/living_mobility.dm @@ -176,4 +176,6 @@ else remove_movespeed_modifier(/datum/movespeed_modifier/limbless) + update_movespeed() + return mobility_flags diff --git a/code/modules/mob/living/living_sprint.dm b/code/modules/mob/living/living_sprint.dm index dc0f6d2767..3ef67c9edd 100644 --- a/code/modules/mob/living/living_sprint.dm +++ b/code/modules/mob/living/living_sprint.dm @@ -29,6 +29,7 @@ if(combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) return ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_SPRINT_ACTIVE) + add_movespeed_modifier(/datum/movespeed_modifier/sprinting) if(update_icon) update_sprint_icon() @@ -36,6 +37,7 @@ if(!(combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) || (combat_flags & COMBAT_FLAG_SPRINT_FORCED)) return DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_SPRINT_ACTIVE) + remove_movespeed_modifier(/datum/movespeed_modifier/sprinting) if(update_icon) update_sprint_icon() diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 66c2cd96c7..4c03e74d70 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -82,6 +82,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list( return new_msg /mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) + set waitfor = FALSE var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE) var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE) var/talk_key = get_key(message) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 1eb308af00..83df3ffba5 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1132,10 +1132,6 @@ bellyup = 1 update_icons() -/mob/living/silicon/robot/adjustStaminaLossBuffered(amount, updating_health = 1) - if(istype(cell)) - cell.charge -= amount * 5 - /mob/living/silicon/robot/verb/viewmanifest() set category = "Robot Commands" set name = "View Crew Manifest" diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 3f28fd6e01..8375d621d4 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -236,7 +236,7 @@ Auto Patrol[]"}, if(targets.len>0) var/mob/living/carbon/t = pick(targets) if((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down. - shootAt(t) + INVOKE_ASYNC(src, .proc/shootAt, t) switch(mode) if(BOT_IDLE) // idle @@ -254,7 +254,7 @@ Auto Patrol[]"}, if(target) // make sure target exists if(Adjacent(target) && isturf(target.loc)) // if right next to perp - stun_attack(target) + INVOKE_ASYNC(src, .proc/stun_attack, target) mode = BOT_PREP_ARREST anchored = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index a8799f4e8c..bde5a19518 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -368,7 +368,8 @@ var/turf/T = get_turf(S) var/obj/structure/spider/stickyweb/W = locate() in T - if(W) + var/obj/structure/arachnid/W2 = locate() in T + if(W || W2) to_chat(S, "There's already a web here!") return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index debd7f2e7b..fa3a59a6b9 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -127,6 +127,8 @@ Difficulty: Very Hard if(H.mind) if(istype(H.mind.martial_art, /datum/martial_art/the_sleeping_carp) & istype(H.mind.martial_art, /datum/martial_art/the_rising_bass)) . = TRUE + if (is_species(H, /datum/species/golem/sand)) + .= TRUE /mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() dir_shots(GLOB.diagonals) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 3de4c8b41b..3c6857b54e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -3,28 +3,31 @@ LEGION Legion spawns from the necropolis gate in the far north of lavaland. It is the guardian of the Necropolis and emerges from within whenever an intruder tries to enter through its gate. -Whenever Legion emerges, everything in lavaland will receive a notice via color, audio, and text. This is because Legion is powerful enough to slaughter the entirety of lavaland with little effort. +Whenever Legion emerges, everything in lavaland will receive a notice via color, audio, and text. This is because Legion is powerful enough to slaughter the entirety of lavaland with little effort. LOL -It has two attack modes that it constantly rotates between. +It has three attacks. +Spawn Skull. Most of the time it will use this attack. Spawns a single legion skull. +Spawn Sentinel. The legion will spawn up to three sentinels, depending on its size. +CHARGE! The legion starts spinning and tries to melee the player. It will try to flick itself towards the player, dealing some damage if it hits. -In ranged mode, it will behave like a normal legion - retreating when possible and firing legion skulls at the target. -In charge mode, it will spin and rush its target, attacking with melee whenever possible. - -When Legion dies, it drops a staff of storms, which allows its wielder to call and disperse ash storms at will and functions as a powerful melee weapon. +When Legion dies, it will split into three smaller skulls up to three times. +If you kill all of the smaller ones it drops a staff of storms, which allows its wielder to call and disperse ash storms at will and functions as a powerful melee weapon. Difficulty: Medium +SHITCODE AHEAD. BE ADVISED. Also comment extravaganza + */ /mob/living/simple_animal/hostile/megafauna/legion name = "Legion" - health = 800 - maxHealth = 800 + health = 700 + maxHealth = 700 spacewalk = TRUE - icon_state = "legion" - icon_living = "legion" + icon_state = "mega_legion" + icon_living = "mega_legion" desc = "One of many." - icon = 'icons/mob/lavaland/legion.dmi' + icon = 'icons/mob/lavaland/96x96megafauna.dmi' attack_verb_continuous = "chomps" attack_verb_simple = "chomp" attack_sound = 'sound/magic/demon_attack1.ogg' @@ -39,12 +42,10 @@ Difficulty: Medium retreat_distance = 5 minimum_distance = 5 ranged_cooldown_time = 10 - var/size = 5 - var/charging = 0 medal_type = BOSS_MEDAL_LEGION score_type = LEGION_SCORE - pixel_y = -90 - pixel_x = -75 + pixel_y = -16 + pixel_x = -32 loot = list(/obj/item/stack/sheet/bone = 3) vision_range = 10 wander = FALSE @@ -53,6 +54,8 @@ Difficulty: Medium mouse_opacity = MOUSE_OPACITY_ICON wound_bonus = -40 bare_wound_bonus = 20 + var/size = 3 + var/charging = FALSE /mob/living/simple_animal/hostile/megafauna/legion/Initialize() . = ..() @@ -77,72 +80,176 @@ Difficulty: Medium A.infest(L) /mob/living/simple_animal/hostile/megafauna/legion/OpenFire(the_target) - if(world.time >= ranged_cooldown && !charging) - if(prob(75)) - var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc) - A.GiveTarget(target) - A.friends = friends - A.faction = faction - ranged_cooldown = world.time + ranged_cooldown_time - else - visible_message("[src] charges!") - SpinAnimation(speed = 20, loops = 5) - ranged = 0 - retreat_distance = 0 - minimum_distance = 0 - speed = 0 - move_to_delay = 1 - charging = 1 - addtimer(CALLBACK(src, .proc/reset_charge), 50) + if(charging) + return + ranged_cooldown = world.time + ranged_cooldown_time + if(client) + switch(chosen_attack) + if(1) + create_legion_skull() + if(2) + charge_target() + if(3) + create_legion_turrets() + return + + switch(rand(4)) //Larger skulls use more attacks. + if(0 to 2) + create_legion_skull() + if(3) + charge_target() + if(4) + create_legion_turrets() + +//SKULLS + +///Attack proc. Spawns a singular legion skull. +/mob/living/simple_animal/hostile/megafauna/legion/proc/create_legion_skull() + var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc) + A.GiveTarget(target) + A.friends = friends + A.faction = faction + +//CHARGE + +///Attack proc. Gives legion some movespeed buffs and switches the AI to melee. At lower sizes, this also throws the skull at the player. +/mob/living/simple_animal/hostile/megafauna/legion/proc/charge_target() + visible_message("[src] charges!") + SpinAnimation(speed = 20, loops = 3, parallel = FALSE) + ranged = FALSE + retreat_distance = 0 + minimum_distance = 0 + set_varspeed(0) + charging = TRUE + addtimer(CALLBACK(src, .proc/reset_charge), 60) + var/mob/living/L = target + if(!istype(L) || L.stat != DEAD) //I know, weird syntax, but it just works. + addtimer(CALLBACK(src, .proc/throw_thyself), 20) + +///This is the proc that actually does the throwing. Charge only adds a timer for this. +/mob/living/simple_animal/hostile/megafauna/legion/proc/throw_thyself() + playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, TRUE) + throw_at(target, 7, 1.1, src, FALSE, FALSE, CALLBACK(GLOBAL_PROC, .proc/playsound, src, 'sound/effects/meteorimpact.ogg', 50 * size, TRUE, 2), INFINITY) + +///Deals some extra damage on throw impact. +/mob/living/simple_animal/hostile/megafauna/legion/throw_impact(mob/living/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if(istype(hit_atom)) + playsound(src, attack_sound, 100, TRUE) + hit_atom.apply_damage(22 * size / 2) //It gets pretty hard to dodge the skulls when there are a lot of them. Scales down with size + hit_atom.safe_throw_at(get_step(src, get_dir(src, hit_atom)), 2) //Some knockback. Prevent the legion from melee directly after the throw. + +//TURRETS + +///Attack proc. Creates up to three legion turrets on suitable turfs nearby. +/mob/living/simple_animal/hostile/megafauna/legion/proc/create_legion_turrets(minimum = 2, maximum = size * 2) + playsound(src, 'sound/magic/RATTLEMEBONES.ogg', 100, TRUE) + var/list/possiblelocations = list() + for(var/turf/T in oview(src, 4)) //Only place the turrets on open turfs + if(is_blocked_turf(T)) + continue + possiblelocations += T + for(var/i in 1 to min(rand(minimum, maximum), LAZYLEN(possiblelocations))) //Makes sure aren't spawning in nullspace. + var/chosen = pick(possiblelocations) + new /obj/structure/legionturret(chosen) + possiblelocations -= chosen + +/mob/living/simple_animal/hostile/megafauna/legion/GiveTarget(new_target) + . = ..() + if(target) + wander = TRUE + +///This makes sure that the legion door opens on taking damage, so you can't cheese this boss. +/mob/living/simple_animal/hostile/megafauna/legion/adjustHealth(amount, updating_health = TRUE, forced = FALSE) + if(GLOB.necropolis_gate && true_spawn) + GLOB.necropolis_gate.toggle_the_gate(null, TRUE) //very clever. + return ..() + +///In addition to parent functionality, this will also turn the target into a small legion if they are unconcious. +/mob/living/simple_animal/hostile/megafauna/legion/AttackingTarget() + . = ..() + if(. && ishuman(target)) + var/mob/living/L = target + if(L.stat == UNCONSCIOUS) + var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc) + A.infest(L) + +///Resets the charge buffs. /mob/living/simple_animal/hostile/megafauna/legion/proc/reset_charge() - ranged = 1 + ranged = TRUE retreat_distance = 5 minimum_distance = 5 - speed = 1 - move_to_delay = 2 - charging = 0 + set_varspeed(2) + charging = FALSE +///Special snowflake death() here. Can only die if size is 1 or lower and HP is 0 or below. /mob/living/simple_animal/hostile/megafauna/legion/death() + //Make sure we didn't get cheesed if(health > 0) return - if(size > 1) - adjustHealth(-maxHealth) //heal ourself to full in prep for splitting + if(Split()) + return + //We check what loot we should drop. + var/last_legion = TRUE + for(var/mob/living/simple_animal/hostile/megafauna/legion/other in GLOB.mob_living_list) + if(other != src) + last_legion = FALSE + break + if(last_legion) + loot = list(/obj/item/staff/storm) + elimination = FALSE + else if(prob(20)) //20% chance for sick lootz. + loot = list(/obj/structure/closet/crate/necropolis/tendril/all) + if(!true_spawn) + loot = null + return ..() + +///Splits legion into smaller skulls. +/mob/living/simple_animal/hostile/megafauna/legion/proc/Split() + size-- + if(size < 1) + return FALSE + adjustHealth(-maxHealth) //We heal in preparation of the split + switch(size) //Yay, switches + if(3 to INFINITY) + icon = initial(icon) + pixel_x = initial(pixel_x) + pixel_y = initial(pixel_y) + maxHealth = initial(maxHealth) + if(2) + icon = 'icons/mob/lavaland/64x64megafauna.dmi' + pixel_x = -16 + pixel_y = -8 + maxHealth = 350 + if(1) + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + pixel_x = 0 + pixel_y = 0 + maxHealth = 200 + adjustHealth(0) //Make the health HUD look correct. + visible_message("This is getting out of hands. Now there are three of them!") + for(var/i in 1 to 2) //Create three skulls in total var/mob/living/simple_animal/hostile/megafauna/legion/L = new(loc) + L.setVarsAfterSplit(src) + return TRUE - L.maxHealth = round(maxHealth * 0.6,DAMAGE_PRECISION) - maxHealth = L.maxHealth +///Sets the variables for new legion skulls. Usually called after splitting. +/mob/living/simple_animal/hostile/megafauna/legion/proc/setVarsAfterSplit(var/mob/living/simple_animal/hostile/megafauna/legion/L) + maxHealth = L.maxHealth + updatehealth() + size = L.size + icon = L.icon + pixel_x = L.pixel_x + pixel_y = L.pixel_y + faction = L.faction.Copy() + GiveTarget(L.target) - L.health = L.maxHealth - health = maxHealth - - size-- - L.size = size - - L.resize = L.size * 0.2 - transform = initial(transform) - resize = size * 0.2 - - L.update_transform() - update_transform() - - L.faction = faction.Copy() - - L.GiveTarget(target) - - visible_message("[src] splits in twain!") - else - var/last_legion = TRUE - for(var/mob/living/simple_animal/hostile/megafauna/legion/other in GLOB.mob_living_list) - if(other != src) - last_legion = FALSE - break - if(last_legion) - loot = list(/obj/item/staff/storm) - elimination = 0 - else if(prob(20)) - loot = list(/obj/structure/closet/crate/necropolis/tendril/random) //This one spawns a chest that could be any of the three types - ..() +/obj/item/gps/internal/legion + icon_state = null + gpstag = "Echoing Signal" + desc = "The message repeats." + invisibility = 100 /obj/item/gps/internal/legion icon_state = null @@ -211,3 +318,87 @@ Difficulty: Medium playsound(user, 'sound/magic/staff_change.ogg', 200, 0) A.telegraph() storm_cooldown = world.time + 200 + +///A basic turret that shoots at nearby mobs. Intended to be used for the legion megafauna. +/obj/structure/legionturret + name = "\improper Legion sentinel" + desc = "The eye pierces your soul." + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + icon_state = "legion_turret" + light_power = 0.5 + light_range = 2 + max_integrity = 80 + luminosity = 6 + anchored = TRUE + density = TRUE + layer = ABOVE_OBJ_LAYER + armor = list("melee" = 0, "bullet" = 0, "laser" = 100,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + ///What kind of projectile the actual damaging part should be. + var/projectile_type = /obj/item/projectile/beam/legion + ///Time until the tracer gets shot + var/initial_firing_time = 18 + ///How long it takes between shooting the tracer and the projectile. + var/shot_delay = 8 + ///Compared with the targeted mobs. If they have the faction, turret won't shoot. + var/faction = list("mining") + +/obj/structure/legionturret/Initialize() + . = ..() + addtimer(CALLBACK(src, .proc/set_up_shot), initial_firing_time) + +///Handles an extremely basic AI +/obj/structure/legionturret/proc/set_up_shot() + for(var/mob/living/L in oview(9, src)) + if(L.stat == DEAD || L.stat == UNCONSCIOUS) + continue + if(faction_check(faction, L.faction)) + continue + fire(L) + return + fire(get_edge_target_turf(src, pick(GLOB.cardinals))) + +///Called when attacking a target. Shoots a projectile at the turf underneath the target. +/obj/structure/legionturret/proc/fire(atom/target) + var/turf/T = get_turf(target) + var/turf/T1 = get_turf(src) + if(!T || !T1) + return + //Now we generate the tracer. + var/angle = Get_Angle(T1, T) + var/datum/point/vector/V = new(T1.x, T1.y, T1.z, 0, 0, angle) + generate_tracer_between_points(V, V.return_vector_after_increments(6), /obj/effect/projectile/tracer/legion/tracer, 0, shot_delay, 0, 0, 0, null) + playsound(src, 'sound/machines/airlockopen.ogg', 100, TRUE) + addtimer(CALLBACK(src, .proc/fire_beam, angle), shot_delay) + +///Called shot_delay after the turret shot the tracer. Shoots a projectile into the same direction. +/obj/structure/legionturret/proc/fire_beam(angle) + var/obj/item/projectile/ouchie = new projectile_type(loc) + ouchie.firer = src + ouchie.fire(angle) + playsound(src, 'sound/effects/bin_close.ogg', 100, TRUE) + QDEL_IN(src, 5) + +///Used for the legion turret. +/obj/item/projectile/beam/legion + name = "blood pulse" + hitsound = 'sound/magic/magic_missile.ogg' + damage = 19 + range = 6 + eyeblur = 0 + light_color = LIGHT_COLOR_RED + impact_effect_type = /obj/effect/temp_visual/kinetic_blast + tracer_type = /obj/effect/projectile/tracer/legion + muzzle_type = /obj/effect/projectile/tracer/legion + impact_type = /obj/effect/projectile/tracer/legion + hitscan = TRUE + movement_type = UNSTOPPABLE + +///Used for the legion turret tracer. +/obj/effect/projectile/tracer/legion/tracer + icon = 'icons/effects/beam.dmi' + icon_state = "blood_light" + +///Used for the legion turret beam. +/obj/effect/projectile/tracer/legion + icon = 'icons/effects/beam.dmi' + icon_state = "blood" diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm index b3523fc42c..59bd67e42a 100644 --- a/code/modules/mob/living/simple_animal/hostile/wizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm @@ -62,6 +62,9 @@ /mob/living/simple_animal/hostile/wizard/handle_automated_action() . = ..() + INVOKE_ASYNC(src, .proc/AutomatedCast) + +/mob/living/simple_animal/hostile/wizard/proc/AutomatedCast() if(target && next_cast < world.time) if((get_dir(src,target) in list(SOUTH,EAST,WEST,NORTH)) && fireball.cast_check(0,src)) //Lined up for fireball src.setDir(get_dir(src,target)) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 3ca29b6746..1ff7e2f799 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -436,12 +436,7 @@ newspeak.Add(possible_phrase) speak = newspeak - //Search for item to steal - parrot_interest = search_for_item() - if(parrot_interest) - emote("me", EMOTE_VISIBLE, "looks in [parrot_interest]'s direction and takes flight.") - parrot_state = PARROT_SWOOP | PARROT_STEAL - icon_state = icon_living + INVOKE_ASYNC(src, .proc/attempt_item_theft) return //-----WANDERING - This is basically a 'I dont know what to do yet' state @@ -620,6 +615,14 @@ parrot_lastmove = src.loc return 0 +/mob/living/simple_animal/parrot/proc/attempt_item_theft() + //Search for item to steal + search_for_item() + if(parrot_interest) + emote("me", EMOTE_VISIBLE, "looks in [parrot_interest]'s direction and takes flight.") + parrot_state = PARROT_SWOOP | PARROT_STEAL + icon_state = icon_living + /mob/living/simple_animal/parrot/proc/search_for_item() var/item for(var/atom/movable/AM in view(src)) diff --git a/code/modules/mob/living/stamina_buffer.dm b/code/modules/mob/living/stamina_buffer.dm new file mode 100644 index 0000000000..a410df582b --- /dev/null +++ b/code/modules/mob/living/stamina_buffer.dm @@ -0,0 +1,56 @@ +/** + * Attempts to use an amount of stamina from our stamina buffer. + * Does not use anything if we don't have enough. + * + * Returns TRUE or FALSE based on if we have it. + */ +/mob/living/proc/UseStaminaBuffer(amount, warn = FALSE, considered_action = TRUE) + if(!(combat_flags & COMBAT_FLAG_STAMINA_BUFFER)) + return TRUE + if(stamina_buffer < amount) + var/stamina_health = getStaminaLoss() + if(stamina_health > STAMINA_NO_OVERDRAW_THRESHOLD) + if(warn) + to_chat(src, "You do not have enough action stamina to do that!") + return FALSE + adjustStaminaLoss(amount * CONFIG_GET(number/stamina_combat/overdraw_penalty_factor)) + else + stamina_buffer -= amount + if(considered_action) + stamina_buffer_last_use = world.time + UpdateStaminaBuffer() + return TRUE + +/** + * Updates our stamina buffer amount. + */ +/mob/living/proc/UpdateStaminaBuffer(updating_hud = TRUE) + var/time = world.time - stamina_buffer_regen_last + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/buffer_max, buffer_max) + stamina_buffer_regen_last = world.time + if((stamina_buffer >= buffer_max) || !(combat_flags & COMBAT_FLAG_STAMINA_BUFFER)) + stamina_buffer = buffer_max + return + else if(!time || HAS_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION)) + return + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/out_of_combat_timer, out_of_combat_timer) + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/base_regeneration, base_regeneration) + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/combat_regeneration, combat_regeneration) + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/percent_regeneration_out_of_combat, percent_regeneration_out_of_combat) + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_delay, post_action_penalty_delay) + CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_factor, post_action_penalty_factor) + var/base_regen = (SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))? base_regeneration : combat_regeneration + var/time_since_last_action = world.time - stamina_buffer_last_use + var/action_penalty = ((time_since_last_action) < (post_action_penalty_delay * 10))? post_action_penalty_factor : 1 + var/out_of_combat_bonus = (time_since_last_action < (out_of_combat_timer * 10))? 0 : ((buffer_max * percent_regeneration_out_of_combat * 0.01)) + var/regen = ((base_regen * action_penalty) + out_of_combat_bonus) * time * 0.1 * stamina_buffer_regen_mod + stamina_buffer += min((buffer_max - stamina_buffer), regen) + if(updating_hud) + hud_used?.staminabuffer?.mark_dirty() + +/** + * Boosts our stamina buffer by this much. + */ +/mob/living/proc/RechargeStaminaBuffer(amount) + stamina_buffer += abs(amount) + UpdateStaminaBuffer() diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index aa6635f73d..d4edb78e2d 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -379,7 +379,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp /proc/item_heal_robotic(mob/living/carbon/human/H, mob/user, brute_heal, burn_heal) var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected)) - if(affecting && affecting.status == BODYPART_ROBOTIC) + if(affecting && affecting.is_robotic_limb()) var/dam //changes repair text based on how much brute/burn was supplied if(brute_heal > burn_heal) dam = 1 diff --git a/code/modules/movespeed/_movespeed_modifier.dm b/code/modules/movespeed/_movespeed_modifier.dm index 3bc4463531..9c8036bd55 100644 --- a/code/modules/movespeed/_movespeed_modifier.dm +++ b/code/modules/movespeed/_movespeed_modifier.dm @@ -38,6 +38,12 @@ Key procs /// Multiplicative slowdown var/multiplicative_slowdown = 0 + /// Next two variables depend on this: Should we do advanced calculations? + var/complex_calculation = FALSE + /// Absolute max tiles we can boost to + var/absolute_max_tiles_per_second + /// Max tiles per second we can boost + var/max_tiles_per_second_boost /// Movetypes this applies to var/movetypes = ALL @@ -53,6 +59,16 @@ Key procs if(!id) id = "[type]" //We turn the path into a string. +/** + * Returns new multiplicative movespeed after modification. + */ +/datum/movespeed_modifier/proc/apply_multiplicative(existing, mob/target) + if(!complex_calculation || (multiplicative_slowdown > 0)) // we aren't limiting how much things can slowdown.. yet. + return existing + multiplicative_slowdown + var/current_tiles = 10 / max(existing, world.tick_lag) + var/minimum_speed = 10 / min(current_tiles + max_tiles_per_second_boost, max(current_tiles, absolute_max_tiles_per_second)) + return max(minimum_speed, existing + multiplicative_slowdown) + GLOBAL_LIST_EMPTY(movespeed_modification_cache) /// Grabs a STATIC MODIFIER datum from cache. YOU MUST NEVER EDIT THESE DATUMS, OR IT WILL AFFECT ANYTHING ELSE USING IT TOO! @@ -171,13 +187,15 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache) /// Set or update the global movespeed config on a mob /mob/proc/update_config_movespeed() add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/mob_config_speedmod, multiplicative_slowdown = get_config_multiplicative_speed()) + add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/mob_config_speedmod_floating, multiplicative_slowdown = get_config_multiplicative_speed(TRUE)) /// Get the global config movespeed of a mob by type -/mob/proc/get_config_multiplicative_speed() - if(!islist(GLOB.mob_config_movespeed_type_lookup) || !GLOB.mob_config_movespeed_type_lookup[type]) +/mob/proc/get_config_multiplicative_speed(floating = FALSE) + var/list/read = floating? GLOB.mob_config_movespeed_type_lookup_floating : GLOB.mob_config_movespeed_type_lookup + if(!islist(read) || !read[type]) return 0 else - return GLOB.mob_config_movespeed_type_lookup[type] + return read[type] /// Go through the list of movespeed modifiers and calculate a final movespeed. ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE! /mob/proc/update_movespeed() @@ -198,7 +216,7 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache) conflict_tracker[conflict] = amt else continue - . += amt + . = M.apply_multiplicative(., src) var/old = cached_multiplicative_slowdown // CITAEDL EDIT - To make things a bit less jarring, when in situations where // your delay decreases, "give" the delay back to the client cached_multiplicative_slowdown = . @@ -220,6 +238,13 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache) var/datum/movespeed_modifier/M = movespeed_modification[id] . += M.multiplicative_slowdown +/** + * Gets the movespeed modifier datum of a modifier on a mob. Returns null if not found. + * DANGER: IT IS UP TO THE PERSON USING THIS TO MAKE SURE THE MODIFIER IS NOT MODIFIED IF IT HAPPENS TO BE GLOBAL/CACHED. + */ +/mob/proc/get_movespeed_modifier_datum(id) + return movespeed_modification[id] + /// Checks if a move speed modifier is valid and not missing any data /proc/movespeed_data_null_check(datum/movespeed_modifier/M) //Determines if a data list is not meaningful and should be discarded. . = TRUE diff --git a/code/modules/movespeed/modifiers/items.dm b/code/modules/movespeed/modifiers/items.dm index 94dc2a1553..a8510ecd86 100644 --- a/code/modules/movespeed/modifiers/items.dm +++ b/code/modules/movespeed/modifiers/items.dm @@ -1,12 +1,13 @@ /datum/movespeed_modifier/jetpack conflicts_with = MOVE_CONFLICT_JETPACK movetypes = FLOATING + multiplicative_slowdown = -1 /datum/movespeed_modifier/jetpack/cybernetic - multiplicative_slowdown = -0.5 + multiplicative_slowdown = -1.25 /datum/movespeed_modifier/jetpack/fullspeed - multiplicative_slowdown = -2 + multiplicative_slowdown = -1.5 /datum/movespeed_modifier/die_of_fate multiplicative_slowdown = 1 diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm index d17767bb1f..f6cf84eb83 100644 --- a/code/modules/movespeed/modifiers/mobs.dm +++ b/code/modules/movespeed/modifiers/mobs.dm @@ -111,6 +111,12 @@ /datum/movespeed_modifier/mob_config_speedmod variable = TRUE + blacklisted_movetypes = FLOATING + flags = IGNORE_NOSLOW + +/datum/movespeed_modifier/mob_config_speedmod_floating + variable = TRUE + movetypes = FLOATING flags = IGNORE_NOSLOW /datum/movespeed_modifier/liver_cirrhosis @@ -120,3 +126,30 @@ /datum/movespeed_modifier/active_block variable = TRUE flags = IGNORE_NOSLOW + +/datum/movespeed_modifier/sprinting + flags = IGNORE_NOSLOW + blacklisted_movetypes = FLOATING + priority = 100 + +/// for speed reasons this is sorta copypasty. +/datum/movespeed_modifier/sprinting/apply_multiplicative(existing, mob/target) + . = existing + if(target.m_intent != MOVE_INTENT_RUN) + return + if(isliving(target)) + var/mob/living/L = target + if(!(L.mobility_flags & MOBILITY_STAND)) + return + var/static/datum/config_entry/number/movedelay/sprint_max_tiles_increase/SMTI + if(!SMTI) + SMTI = CONFIG_GET_ENTRY(number/movedelay/sprint_max_tiles_increase) + var/static/datum/config_entry/number/movedelay/sprint_speed_increase/SSI + if(!SSI) + SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase) + var/static/datum/config_entry/number/movedelay/sprint_absolute_max_tiles/SAMT + if(!SAMT) + SAMT = CONFIG_GET_ENTRY(number/movedelay/sprint_absolute_max_tiles) + var/current_tiles = 10 / max(existing, world.tick_lag) + var/minimum_speed = 10 / min(max(SAMT.config_entry_value, current_tiles), current_tiles + SMTI.config_entry_value) + . = min(., max(minimum_speed, existing - SSI.config_entry_value)) diff --git a/code/modules/plumbing/plumbers/autohydro.dm b/code/modules/plumbing/plumbers/autohydro.dm index 20b8e6012a..9c358d8d6e 100644 --- a/code/modules/plumbing/plumbers/autohydro.dm +++ b/code/modules/plumbing/plumbers/autohydro.dm @@ -30,20 +30,8 @@ . = ..() START_PROCESSING(SSobj, src) create_reagents(100 , AMOUNT_VISIBLE) - AddComponent(/datum/component/plumbing/simple_demand) - -/obj/machinery/hydroponics/constructable/automagic/ComponentInitialize() - . = ..() - AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated)) - - -/obj/machinery/hydroponics/constructable/proc/can_be_rotated(mob/user, rotation_type) - return !anchored /obj/machinery/hydroponics/constructable/automagic/process() - if(reagents) - reagents.add_reagent(reagents) - reagents.clear_reagents() if(dead) dead = 0 qdel(myseed) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 3f1e688120..59f869627c 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -548,16 +548,23 @@ By design, d1 is the smallest direction and d2 is the highest return ..() var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected)) - if(affecting && affecting.status == BODYPART_ROBOTIC) - //only heal to 25 if limb is damaged to or past 25 burn, otherwise heal normally - var/difference = affecting.burn_dam - 25 + if(affecting && affecting.is_robotic_limb()) + //only heal to threshhold_passed_mindamage if limb is damaged to or past threshhold, otherwise heal normally + var/damage var/heal_amount = 15 - if(difference >= 0) - heal_amount = difference + if(user == H) user.visible_message("[user] starts to fix some of the wires in [H]'s [affecting.name].", "You start fixing some of the wires in [H]'s [affecting.name].") if(!do_mob(user, H, 50)) return + damage = affecting.burn_dam + affecting.update_threshhold_state(brute = FALSE) + if(affecting.threshhold_burn_passed) + heal_amount = min(heal_amount, damage - affecting.threshhold_passed_mindamage) + + if(!heal_amount) + to_chat(user, "[user == H ? "Your" : "[H]'s"] [affecting.name] appears to have suffered severe internal damage and requires surgery to repair further.") + return if(item_heal_robotic(H, user, 0, heal_amount)) use(1) return diff --git a/code/modules/power/rtg.dm b/code/modules/power/rtg.dm index b558031fab..c19927e84c 100644 --- a/code/modules/power/rtg.dm +++ b/code/modules/power/rtg.dm @@ -53,6 +53,33 @@ power_gen = 1250 // 2500 on T1, 10000 on T4. circuit = /obj/item/circuitboard/machine/rtg/advanced +/obj/machinery/power/rtg/advanced/fullupgrade //fully ugpraded stock parts + desc = "An advanced RTG capable of moderating isotope decay, increasing power output but reducing lifetime. It uses plasma-fueled radiation collectors to increase output even further. This model is fully upgraded with the latest tech available in this quadrant." + +/obj/machinery/power/rtg/advanced/fullupgrade/Initialize() + . = ..() + //This looks terrifying. And apparently instancing vars and modifying the amount variable causes runtime errors. Guess we're sticking to copy pasta, thanks, byond. + component_parts = list() + component_parts += new /obj/item/circuitboard/machine/rtg/advanced(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/uranium(null) + component_parts += new /obj/item/stack/sheet/mineral/plasma(null) + component_parts += new /obj/item/stack/sheet/mineral/plasma(null) + component_parts += new /obj/item/stack/sheet/mineral/plasma(null) + component_parts += new /obj/item/stack/sheet/mineral/plasma(null) + component_parts += new /obj/item/stack/sheet/mineral/plasma(null) + component_parts += new /obj/item/stock_parts/capacitor/quadratic(null) + component_parts += new /obj/item/stock_parts/micro_laser/quadultra(null) + RefreshParts() + // Void Core, power source for Abductor ships and bases. // Provides a lot of power, but tends to explode when mistreated. diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index e3fdbd3654..f385b640aa 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -928,8 +928,15 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/mob/living/user = AM if(user.status_flags & GODMODE) return - message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)].") - investigate_log("has consumed [key_name(user)].", INVESTIGATE_SUPERMATTER) + var/add + if(user.mind?.assigned_role == "Clown") + var/denergy = rand(-1000, 1000) + var/ddamage = rand(-150, clamp(150, 0, (explosion_point - damage) + 150)) + power += denergy + damage += ddamage + add = ", adding [denergy] energy and [ddamage] damage to the crystal" + message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)][add].") + investigate_log("has consumed [key_name(user)][add].", INVESTIGATE_SUPERMATTER) user.dust(force = TRUE) if(power_changes) matter_power += 200 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 1369115b0d..1c3a0d230f 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -154,8 +154,8 @@ shake_camera(user, recoil + 1, recoil) if(stam_cost) //CIT CHANGE - makes gun recoil cause staminaloss - var/safe_cost = clamp(stam_cost, 0, STAMINA_NEAR_CRIT - user.getStaminaLoss())*(firing && burst_size >= 2 ? 1/burst_size : 1) - user.adjustStaminaLossBuffered(safe_cost) //CIT CHANGE - ditto + var/safe_cost = clamp(stam_cost, 0, user.stamina_buffer)*(firing && burst_size >= 2 ? 1/burst_size : 1) + user.UseStaminaBuffer(safe_cost) if(suppressed) playsound(user, fire_sound, 10, 1) @@ -590,6 +590,9 @@ update_icon() /obj/item/gun/proc/getinaccuracy(mob/living/user, bonus_spread, stamloss) + return 0 // Replacement TBD: Exponential curved aim instability system. + +/* if(inaccuracy_modifier == 0) return bonus_spread var/base_inaccuracy = weapon_weight * 25 * inaccuracy_modifier @@ -612,6 +615,7 @@ if(mult < 0) //accurate weapons should provide a proper bonus with negative inaccuracy. the opposite is true too. mult *= 1/inaccuracy_modifier return max(bonus_spread + (base_inaccuracy * mult), 0) //no negative spread. +*/ /obj/item/gun/proc/getstamcost(mob/living/carbon/user) . = recoil diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index ecf6e538b8..7b3ac36ef6 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -44,10 +44,9 @@ if(HAS_TRAIT(user, TRAIT_FAST_PUMP)) recentpump = world.time + 2 else + if(!user.UseStaminaBuffer(2, warn = TRUE)) + return recentpump = world.time + 10 - if(istype(user))//CIT CHANGE - makes pumping shotguns cost a lil bit of stamina. - user.adjustStaminaLossBuffered(2) //CIT CHANGE - DITTO. make this scale inversely to the strength stat when stats/skills are added - return /obj/item/gun/ballistic/shotgun/blow_up(mob/user) . = 0 diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 19ca42022d..cd91a7670d 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -233,7 +233,7 @@ p_blue.link_portal(p_orange) /obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W, turf/target) - var/obj/effect/portal/P = new /obj/effect/portal(target, src, 300, null, FALSE, null, atmos_link) + var/obj/effect/portal/P = new /obj/effect/portal(target, 300, null, FALSE, null, atmos_link) if(istype(W, /obj/item/projectile/beam/wormhole/orange)) qdel(p_orange) p_orange = P diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 4e81c5aeb6..fd442bb7a6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -245,7 +245,7 @@ if(starting) splatter_dir = get_dir(starting, target_loca) var/obj/item/bodypart/B = L.get_bodypart(def_zone) - if(B && B.status == BODYPART_ROBOTIC) // So if you hit a robotic, it sparks instead of bloodspatters + if(B && B.is_robotic_limb()) // So if you hit a robotic, it sparks instead of bloodspatters - Hybrid limbs don't bleed from this as of now too, subject to balance.. probably. do_sparks(2, FALSE, target.loc) if(prob(25)) new /obj/effect/decal/cleanable/oil(target_loca) diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index c2bb678904..bbc44730b6 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -1969,7 +1969,7 @@ All effects don't start immediately, but rather get worse over time; the rate is /datum/reagent/consumable/ethanol/bug_spray/on_mob_life(mob/living/carbon/M) //Bugs should not drink Bug spray. - if(isinsect(M) || isflyperson(M)) + if(isinsect(M) || isflyperson(M) || isarachnid(M)) M.adjustToxLoss(1,0) return ..() @@ -2283,7 +2283,7 @@ All effects don't start immediately, but rather get worse over time; the rate is boozepwr = 50 /datum/reagent/consumable/ethanol/species_drink/on_mob_life(mob/living/carbon/C) - if(C.dna.species && C.dna.species.species_type == species_required) //species have a species_type variable that refers to one of the drinks + if(C.dna.species && C.dna.species.species_category == species_required) //species have a species_category variable that refers to one of the drinks quality = RACE_DRINK else C.adjust_disgust(disgust) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 0021a49e51..e64647028c 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -781,6 +781,12 @@ race = /datum/species/lizard/ashwalker mutationtext = "The pain subsides. You feel... savage." +/datum/reagent/mutationtoxin/arachnid + name = "Arachnid Mutation Toxin" + description = "A glowing toxin." + color = "#5EFF3B" //RGB: 94, 255, 59 + race = /datum/species/arachnid + mutationtext = "The pain subsides. You feel... silky." //DANGEROUS RACES /datum/reagent/mutationtoxin/shadow diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 0ab8a69aa0..019394e3c8 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -715,21 +715,27 @@ required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/medicine/synaptizine = 1, /datum/reagent/water = 1) /datum/chemical_reaction/cat - name = "felinid mutation toxic" + name = "felinid mutation toxin" id = /datum/reagent/mutationtoxin/felinid results = list(/datum/reagent/mutationtoxin/felinid = 1) required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/ammonia = 1, /datum/reagent/water = 1, /datum/reagent/pax/catnip = 1, /datum/reagent/mutationtoxin = 1) required_temp = 450 /datum/chemical_reaction/moff - name = "insect mutation toxic" + name = "insect mutation toxin" id = /datum/reagent/mutationtoxin/insect results = list(/datum/reagent/mutationtoxin/insect = 1) required_reagents = list(/datum/reagent/liquid_dark_matter = 2, /datum/reagent/ammonia = 5, /datum/reagent/lithium = 1, /datum/reagent/mutationtoxin = 1) required_temp = 320 +/datum/chemical_reaction/mutationtoxin/arachnid + name = "arachnid mutation toxin" + id = /datum/reagent/mutationtoxin/arachnid + results = list(/datum/reagent/mutationtoxin/arachnid = 1) + required_reagents = list(/datum/reagent/mutationtoxin/insect = 1, /datum/reagent/toxin/heparin = 10) + /datum/chemical_reaction/notlight //Harder to make do to it being a hard race to play - name = "shadow muatatuin toxic" + name = "shadow mutation toxin" id = /datum/reagent/mutationtoxin/shadow results = list(/datum/reagent/mutationtoxin/shadow = 1) required_reagents = list(/datum/reagent/liquid_dark_matter = 5, /datum/reagent/medicine/synaptizine = 10, /datum/reagent/medicine/oculine = 10, /datum/reagent/mutationtoxin = 1) diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 835ffe2d89..5aeac1bc38 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -391,10 +391,12 @@ if(!affecting) to_chat(user, "The limb is missing!") return - if(affecting.status != BODYPART_ORGANIC) + if(!affecting.is_organic_limb()) to_chat(user, "Medicine won't work on a robotic limb!") return - + else if(!affecting.is_organic_limb(FALSE) && mode != HYPO_INJECT) + to_chat(user, "Biomechanical limbs can only be treated via their integrated injection port, not via spraying!") + return //Always log attemped injections for admins var/contained = vial.reagents.log_list() log_combat(user, L, "attemped to inject", src, addition="which had [contained]") diff --git a/code/modules/reagents/reagent_containers/medspray.dm b/code/modules/reagents/reagent_containers/medspray.dm index 40ad167531..052e85a3d1 100644 --- a/code/modules/reagents/reagent_containers/medspray.dm +++ b/code/modules/reagents/reagent_containers/medspray.dm @@ -46,9 +46,11 @@ return if(!L.can_inject(user, TRUE, user.zone_selected, FALSE, TRUE)) //stopped by clothing, like patches return - if(affecting.status != BODYPART_ORGANIC) + if(!affecting.is_organic_limb()) to_chat(user, "Medicine won't work on a robotic limb!") return + else if(!affecting.is_organic_limb(FALSE)) + to_chat(user, "Medical sprays won't work on a biomechanical limb!") if(L == user) L.visible_message("[user] attempts to [apply_method] [src] on [user.p_them()]self.") diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm index 1e23f46d9f..8cf98008db 100644 --- a/code/modules/reagents/reagent_containers/patch.dm +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -19,8 +19,10 @@ return if(!L.can_inject(user, TRUE, user.zone_selected, FALSE, TRUE)) //stopped by clothing, not by species immunity. return - if(affecting.status != BODYPART_ORGANIC) + if(!affecting.is_organic_limb()) to_chat(user, "Medicine won't work on a robotic limb!") + else if(!affecting.is_organic_limb(FALSE)) + to_chat(user, "Medical patches won't work on a biomechanical limb!") return ..() diff --git a/code/modules/research/designs/biogenerator_designs.dm b/code/modules/research/designs/biogenerator_designs.dm index bdaf39732f..ed12d4945e 100644 --- a/code/modules/research/designs/biogenerator_designs.dm +++ b/code/modules/research/designs/biogenerator_designs.dm @@ -181,6 +181,14 @@ build_path = /obj/item/storage/belt/janitor category = list("initial","Organic Materials") +/datum/design/plantbelt + name = "Botanical Belt" + id = "plantbelt" + build_type = BIOGENERATOR + materials = list(/datum/material/biomass= 300) + build_path = /obj/item/storage/belt/plant + category = list("initial","Organic Materials") + /datum/design/s_holster name = "Shoulder Holster" id = "s_holster" diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 7013412e6d..b178f936b7 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -32,7 +32,7 @@ materials = list(/datum/material/glass = 3000, /datum/material/plasma = 3000, /datum/material/diamond = 250, /datum/material/bluespace = 250) build_path = /obj/item/reagent_containers/glass/beaker/bluespace category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_SERVICE /datum/design/noreactbeaker name = "Cryostasis Beaker" @@ -973,6 +973,10 @@ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +///////////////////////////////////////// +//////////// Plumbing ////////// +///////////////////////////////////////// + /datum/design/acclimator name = "Plumbing Acclimator" desc = "A heating and cooling device for pipes!" @@ -1126,3 +1130,14 @@ build_path = /obj/item/construction/plumbing category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/rplunger + name = "Reinforced Plunger" + desc = "A plunger designed for heavy duty clogs." + id = "rplunger" + build_type = PROTOLATHE + materials = list(/datum/material/plasma = 1000, /datum/material/iron = 1000, /datum/material/glass = 1000) + construction_time = 15 + build_path = /obj/item/plunger/reinforced + category = list ("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO diff --git a/code/modules/research/nanites/nanite_programs/healing.dm b/code/modules/research/nanites/nanite_programs/healing.dm index 18307ce2c5..f963114e38 100644 --- a/code/modules/research/nanites/nanite_programs/healing.dm +++ b/code/modules/research/nanites/nanite_programs/healing.dm @@ -121,7 +121,7 @@ if(iscarbon(host_mob)) var/mob/living/carbon/C = host_mob - var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = BODYPART_ROBOTIC) + var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = BODYPART_ROBOTIC | BODYPART_HYBRID) if(!parts.len) return FALSE else @@ -132,7 +132,7 @@ /datum/nanite_program/repairing/active_effect(mob/living/M) if(iscarbon(host_mob)) var/mob/living/carbon/C = host_mob - var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = BODYPART_ROBOTIC) + var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = BODYPART_ROBOTIC | BODYPART_HYBRID) if(!parts.len) return var/update = FALSE diff --git a/code/modules/research/techweb/nodes/medical_nodes.dm b/code/modules/research/techweb/nodes/medical_nodes.dm index 150e420c09..7b9b2f2923 100644 --- a/code/modules/research/techweb/nodes/medical_nodes.dm +++ b/code/modules/research/techweb/nodes/medical_nodes.dm @@ -38,7 +38,7 @@ display_name = "Advanced Plumbing Technology" description = "Plumbing RCD." prereq_ids = list("plumbing", "adv_engi") - design_ids = list("plumb_rcd", "autohydrotray") + design_ids = list("plumb_rcd", "autohydrotray", "rplunger") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) //////////////////////Cybernetics///////////////////// diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 80740ee6f7..beef8559a4 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -71,7 +71,12 @@ var/medium_burn_msg = "blistered" var/heavy_burn_msg = "peeling away" - var/render_like_organic = FALSE // forces limb to render as if it were an organic limb + + //Some special vars for robotic bodyparts, in the base type to prevent needing typecasting / fancy checks. + var/easy_heal_threshhold = -1 //If greater or equal to zero, if limb damage of a type passes this threshhold, it cannot be healed beyond threshhold_passed_mindamage. Only needed for robotic limbs, but is in the basetype to prevent needing spaghetti-checks. + var/threshhold_passed_mindamage = 0 //If the threshhold got passed, what is the minimum damage this limb can be healed to? Loses the threshhold-passed state healing is started while below mindamage. + var/threshhold_brute_passed = FALSE + var/threshhold_burn_passed = FALSE //Ugly but neccessary vars that might get replaced with a flag lateron maybe sometime. /// The wounds currently afflicting this body part var/list/wounds @@ -143,7 +148,7 @@ /obj/item/bodypart/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) ..() - if(status != BODYPART_ROBOTIC) + if(!is_robotic_limb()) playsound(get_turf(src), 'sound/misc/splort.ogg', 50, 1, -1) pixel_x = rand(-3, 3) pixel_y = rand(-3, 3) @@ -151,7 +156,7 @@ //empties the bodypart from its organs and other things inside it /obj/item/bodypart/proc/drop_organs(mob/user) var/turf/T = get_turf(src) - if(status != BODYPART_ROBOTIC) + if(!is_robotic_limb()) playsound(T, 'sound/misc/splort.ogg', 50, 1, -1) if(current_gauze) QDEL_NULL(current_gauze) @@ -458,10 +463,10 @@ //Cannot remove negative damage (i.e. apply damage) /obj/item/bodypart/proc/heal_damage(brute, burn, stamina, only_robotic = FALSE, only_organic = TRUE, updating_health = TRUE) - if(only_robotic && status != BODYPART_ROBOTIC) //This makes organic limbs not heal when the proc is in Robotic mode. + if(only_robotic && !is_robotic_limb()) //This makes organic limbs not heal when the proc is in Robotic mode. return - if(only_organic && status != BODYPART_ORGANIC) //This makes robolimbs not healable by chems. + if(only_organic && !is_organic_limb(FALSE)) //This makes robolimbs and hybridlimbs not healable by chems. return brute_dam = round(max(brute_dam - brute, 0), DAMAGE_PRECISION) @@ -503,7 +508,7 @@ if(!last_maxed && !silent) owner.emote("scream") last_maxed = TRUE - if(!is_organic_limb() || stamina_dam >= max_damage) + if(!is_organic_limb(FALSE) || stamina_dam >= max_damage) return BODYPART_DISABLED_DAMAGE else if(disabled && (get_damage(TRUE) <= (max_damage * 0.8))) // reenabled at 80% now instead of 50% as of wounds update last_maxed = FALSE @@ -545,7 +550,8 @@ return FALSE //Change organ status -/obj/item/bodypart/proc/change_bodypart_status(new_limb_status, heal_limb, change_icon_to_default) +/obj/item/bodypart/proc/change_bodypart_status(new_limb_status, heal_limb, change_icon_to_default, no_update = FALSE) + var/old_status = status status = new_limb_status if(heal_limb) burn_dam = 0 @@ -553,20 +559,48 @@ brutestate = 0 burnstate = 0 + if(status == BODYPART_HYBRID) + easy_heal_threshhold = HYBRID_BODYPART_DAMAGE_THRESHHOLD + threshhold_passed_mindamage = HYBRID_BODYPART_THESHHOLD_MINDAMAGE + else if(old_status == BODYPART_HYBRID) + easy_heal_threshhold = initial(easy_heal_threshhold) + threshhold_passed_mindamage = initial(threshhold_passed_mindamage) + + update_threshhold_state() + if(change_icon_to_default) - if(status == BODYPART_ORGANIC) + if(is_organic_limb(FALSE)) icon = base_bp_icon || DEFAULT_BODYPART_ICON_ORGANIC - else if(status == BODYPART_ROBOTIC) + else if(is_robotic_limb()) icon = base_bp_icon || DEFAULT_BODYPART_ICON_ROBOTIC - if(owner) + if(owner && !no_update) //Only use no_update if you are sure the bodypart will get updated from other sources anyways, to prevent unneccessary processing use. owner.updatehealth() owner.update_body() //if our head becomes robotic, we remove the lizard horns and human hair. owner.update_hair() owner.update_damage_overlays() -/obj/item/bodypart/proc/is_organic_limb() - return (status == BODYPART_ORGANIC) +/obj/item/bodypart/proc/is_organic_limb(hybrid_allowed = TRUE) + if(!hybrid_allowed) + return (status == BODYPART_ORGANIC) + return ((status == BODYPART_ORGANIC) || (status == BODYPART_HYBRID)) //Goodbye if(B.status == BODYPART_ORGANIC || B.status == BODYPART_HYBRID) + +/obj/item/bodypart/proc/is_robotic_limb(hybrid_allowed = TRUE) + if(!hybrid_allowed) + return (status == BODYPART_ROBOTIC) + return ((status == BODYPART_ROBOTIC) || (status == BODYPART_HYBRID)) + +/obj/item/bodypart/proc/update_threshhold_state(brute = TRUE, burn = TRUE) + if(brute) + if(brute_dam < threshhold_passed_mindamage || easy_heal_threshhold < 0) + threshhold_brute_passed = FALSE + else if(brute_dam >= easy_heal_threshhold) + threshhold_brute_passed = TRUE + if(burn) + if(burn_dam < threshhold_passed_mindamage || easy_heal_threshhold < 0) + threshhold_burn_passed = FALSE + else if(burn_dam >= easy_heal_threshhold) + threshhold_burn_passed = TRUE //we inform the bodypart of the changes that happened to the owner, or give it the informations from a source mob. /obj/item/bodypart/proc/update_limb(dropping_limb, mob/living/carbon/source) @@ -581,7 +615,7 @@ C = owner no_update = FALSE - if(HAS_TRAIT(C, TRAIT_HUSK) && (is_organic_limb() || render_like_organic)) + if(HAS_TRAIT(C, TRAIT_HUSK) && is_organic_limb()) species_id = "husk" //overrides species_id dmg_overlay_type = "" //no damage overlay shown when husked should_draw_gender = FALSE @@ -656,8 +690,11 @@ body_markings = null aux_marking = null - if(species_id in GLOB.greyscale_limb_types) //should they have greyscales? - base_bp_icon = DEFAULT_BODYPART_ICON_ORGANIC + if(S.override_bp_icon) + base_bp_icon = S.override_bp_icon + else + if(species_id in GLOB.greyscale_limb_types) //should they have greyscales? + base_bp_icon = DEFAULT_BODYPART_ICON_ORGANIC if(base_bp_icon != DEFAULT_BODYPART_ICON) color_src = mut_colors ? MUTCOLORS : ((H.dna.skin_tone_override && S.use_skintones == USE_SKINTONES_GRAYSCALE_CUSTOM) ? CUSTOM_SKINTONE : SKINTONE) @@ -672,9 +709,9 @@ else if(animal_origin == MONKEY_BODYPART) //currently monkeys are the only non human mob to have damage overlays. dmg_overlay_type = animal_origin - if(status == BODYPART_ROBOTIC) + if(is_robotic_limb()) dmg_overlay_type = "robotic" - if(!render_like_organic) + if(is_robotic_limb(FALSE)) body_markings = null aux_marking = null @@ -711,7 +748,7 @@ if(burnstate) . += image('icons/mob/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_0[burnstate]", -DAMAGE_LAYER, image_dir) - if(!isnull(body_markings) && status == BODYPART_ORGANIC) + if(!isnull(body_markings) && is_organic_limb(FALSE)) if(!use_digitigrade) if(body_zone == BODY_ZONE_CHEST) . += image(body_markings_icon, "[body_markings]_[body_zone]_[icon_gender]", -MARKING_LAYER, image_dir) @@ -728,7 +765,7 @@ . += limb if(animal_origin) - if(is_organic_limb()) + if(is_organic_limb(FALSE)) limb.icon = 'icons/mob/animal_parts.dmi' if(species_id == "husk") limb.icon_state = "[animal_origin]_husk_[body_zone]" @@ -742,7 +779,7 @@ if((body_zone != BODY_ZONE_HEAD && body_zone != BODY_ZONE_CHEST)) should_draw_gender = FALSE - if(is_organic_limb() || render_like_organic) + if(is_organic_limb()) limb.icon = base_bp_icon || 'icons/mob/human_parts.dmi' if(should_draw_gender) limb.icon_state = "[species_id]_[body_zone]_[icon_gender]" @@ -888,7 +925,7 @@ update_disabled() /obj/item/bodypart/proc/get_bleed_rate() - if(status != BODYPART_ORGANIC) // maybe in the future we can bleed oil from aug parts, but not now + if(!is_organic_limb()) // maybe in the future we can bleed oil from aug parts, but not now return var/bleed_rate = 0 if(generic_bleedstacks > 0) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 481bafbb3d..9c42da598d 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -436,6 +436,5 @@ scaries.generate(L, phantom_loss) L.attach_limb(src, 1) if(ROBOTIC_LIMBS in dna.species.species_traits) //Snowflake trait moment, but needed. - L.render_like_organic = TRUE - L.change_bodypart_status(BODYPART_ROBOTIC, FALSE, TRUE) //Haha what if IPC-lings actually regenerated the right limbs instead of organic ones? That'd be pretty cool, right? + L.change_bodypart_status(BODYPART_HYBRID, FALSE, TRUE) //Haha what if IPC-lings actually regenerated the right limbs instead of organic ones? That'd be pretty cool, right? return TRUE diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 8b1473fed2..8c283f5ebc 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -45,7 +45,7 @@ /obj/item/bodypart/head/drop_organs(mob/user) var/turf/T = get_turf(src) - if(status != BODYPART_ROBOTIC) + if(!is_robotic_limb()) playsound(T, 'sound/misc/splort.ogg', 50, 1, -1) for(var/obj/item/I in src) if(I == brain) @@ -141,7 +141,7 @@ . = ..() if(dropped) //certain overlays only appear when the limb is being detached from its owner. - if(status != BODYPART_ROBOTIC) //having a robotic head hides certain features. + if(!is_robotic_limb(FALSE)) //having a robotic head hides certain features. //facial hair if(facial_hair_style) var/datum/sprite_accessory/S = GLOB.facial_hair_styles_list[facial_hair_style] diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm index 39e660203e..d08d8bfa09 100644 --- a/code/modules/surgery/bodyparts/robot_bodyparts.dm +++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm @@ -22,6 +22,8 @@ brute_reduction = 2 burn_reduction = 1 + easy_heal_threshhold = 35 //Resistant against damage, but high mindamage once the threshhold is passed + threshhold_passed_mindamage = 25 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -43,6 +45,8 @@ brute_reduction = 2 burn_reduction = 1 + easy_heal_threshhold = 35 //Resistant against damage, but high mindamage once the threshhold is passed + threshhold_passed_mindamage = 25 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -64,6 +68,8 @@ brute_reduction = 2 burn_reduction = 1 + easy_heal_threshhold = 35 //Resistant against damage, but high mindamage once the threshhold is passed + threshhold_passed_mindamage = 25 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -85,6 +91,8 @@ brute_reduction = 2 burn_reduction = 1 + easy_heal_threshhold = 35 //Resistant against damage, but high mindamage once the threshhold is passed + threshhold_passed_mindamage = 25 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -105,6 +113,8 @@ brute_reduction = 2 burn_reduction = 1 + easy_heal_threshhold = 40 //Resistant against damage, but high mindamage once the threshhold is passed + threshhold_passed_mindamage = 25 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -166,6 +176,8 @@ brute_reduction = 5 burn_reduction = 4 + easy_heal_threshhold = 40 //Resistant against damage, but high mindamage once the threshhold is passed + threshhold_passed_mindamage = 20 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -242,6 +254,8 @@ brute_reduction = 0 burn_reduction = 0 max_damage = 20 + easy_heal_threshhold = 15 //Weak. Low threshhold, but also relatively low mindamage + threshhold_passed_mindamage = 10 /obj/item/bodypart/r_arm/robot/surplus name = "surplus prosthetic right arm" @@ -250,6 +264,8 @@ brute_reduction = 0 burn_reduction = 0 max_damage = 20 + easy_heal_threshhold = 15 //Weak. Low threshhold, but also relatively low mindamage + threshhold_passed_mindamage = 10 /obj/item/bodypart/l_leg/robot/surplus name = "surplus prosthetic left leg" @@ -258,6 +274,8 @@ brute_reduction = 0 burn_reduction = 0 max_damage = 20 + easy_heal_threshhold = 15 //Weak. Low threshhold, but also relatively low mindamage + threshhold_passed_mindamage = 10 /obj/item/bodypart/r_leg/robot/surplus name = "surplus prosthetic right leg" @@ -266,39 +284,49 @@ brute_reduction = 0 burn_reduction = 0 max_damage = 20 + easy_heal_threshhold = 15 //Weak. Low threshhold, but also relatively low mindamage + threshhold_passed_mindamage = 10 -// Upgraded Surplus lims - Better then robotic lims +// Upgraded Surplus lims - Better then robotic limbs /obj/item/bodypart/l_arm/robot/surplus_upgraded name = "reinforced surplus prosthetic left arm" - desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of stronger parts." + desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of parts with more fallbacks against internal damage." icon = 'icons/mob/augmentation/surplus_augments.dmi' brute_reduction = 3 burn_reduction = 2 max_damage = 55 + easy_heal_threshhold = 20 //Lower threshhold than true robotic limbs, but very low mindamage too. + threshhold_passed_mindamage = 5 /obj/item/bodypart/r_arm/robot/surplus_upgraded name = "reinforced surplus prosthetic right arm" - desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of stronger parts." + desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of parts with more fallbacks against internal damage." icon = 'icons/mob/augmentation/surplus_augments.dmi' brute_reduction = 3 burn_reduction = 2 max_damage = 55 + easy_heal_threshhold = 20 //Lower threshhold than true robotic limbs, but very low mindamage too. + threshhold_passed_mindamage = 5 /obj/item/bodypart/l_leg/robot/surplus_upgraded name = "reinforced surplus prosthetic left leg" - desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of stronger parts." + desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of parts with more fallbacks against internal damage." icon = 'icons/mob/augmentation/surplus_augments.dmi' brute_reduction = 3 burn_reduction = 2 max_damage = 55 + easy_heal_threshhold = 20 //Lower threshhold than true robotic limbs, but very low mindamage too. + threshhold_passed_mindamage = 5 /obj/item/bodypart/r_leg/robot/surplus_upgraded name = "reinforced surplus prosthetic right leg" - desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of stronger parts." + desc = "A skeletal, robotic limb. This one is reinforced to provide better protection, and is made of parts with more fallbacks against internal damage." icon = 'icons/mob/augmentation/surplus_augments.dmi' brute_reduction = 3 burn_reduction = 2 max_damage = 55 + easy_heal_threshhold = 20 //Lower threshhold than true robotic limbs, but very low mindamage too. + threshhold_passed_mindamage = 5 #undef ROBOTIC_LIGHT_BRUTE_MSG #undef ROBOTIC_MEDIUM_BRUTE_MSG diff --git a/code/modules/surgery/brain_surgery.dm b/code/modules/surgery/brain_surgery.dm index 239bd8a98d..cf626ce7b4 100644 --- a/code/modules/surgery/brain_surgery.dm +++ b/code/modules/surgery/brain_surgery.dm @@ -10,14 +10,14 @@ target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey) possible_locs = list(BODY_ZONE_HEAD) - requires_bodypart_type = BODYPART_ORGANIC + requires_bodypart_type = 0 /datum/surgery_step/fix_brain name = "fix brain" implements = list(TOOL_HEMOSTAT = 85, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) //don't worry, pouring some alcohol on their open brain will get that chance to 100 time = 120 //long and complicated /datum/surgery/brain_surgery/can_start(mob/user, mob/living/carbon/target, obj/item/tool) var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN) - if(!B) + if(!B || istype(B, /obj/item/organ/brain/ipc)) return FALSE return TRUE diff --git a/code/modules/surgery/helpers.dm b/code/modules/surgery/helpers.dm index 7a92b39692..652c49519a 100644 --- a/code/modules/surgery/helpers.dm +++ b/code/modules/surgery/helpers.dm @@ -26,8 +26,17 @@ if(affecting) if(!S.requires_bodypart) continue - if(S.requires_bodypart_type && affecting.status != S.requires_bodypart_type) - continue + if(S.requires_bodypart_type) //ugly but it'll do. + switch(S.requires_bodypart_type) + if(BODYPART_ORGANIC) + if(!affecting.is_organic_limb(FALSE)) + continue + if(BODYPART_ROBOTIC) + if(!affecting.is_robotic_limb()) + continue + if(BODYPART_HYBRID) + if(!affecting.is_organic_limb() || !affecting.is_robotic_limb()) + continue if(S.requires_real_bodypart && affecting.is_pseudopart) continue else if(C && S.requires_bodypart) //mob with no limb in surgery zone when we need a limb @@ -58,8 +67,17 @@ if(affecting) if(!S.requires_bodypart) return - if(S.requires_bodypart_type && affecting.status != S.requires_bodypart_type) - return + if(S.requires_bodypart_type) //*scream + switch(S.requires_bodypart_type) + if(BODYPART_ORGANIC) + if(!affecting.is_organic_limb(FALSE)) + return + if(BODYPART_ROBOTIC) + if(!affecting.is_robotic_limb()) + return + if(BODYPART_HYBRID) + if(!affecting.is_organic_limb() || !affecting.is_robotic_limb()) + return else if(C && S.requires_bodypart) return if(S.lying_required && !(M.lying)) @@ -91,7 +109,7 @@ else if(S.can_cancel) var/required_tool_type = TOOL_CAUTERY var/obj/item/close_tool = user.get_inactive_held_item() - var/is_robotic = S.requires_bodypart_type == BODYPART_ROBOTIC + var/is_robotic = (S.requires_bodypart_type == BODYPART_ROBOTIC || S.requires_bodypart_type == BODYPART_HYBRID) if(is_robotic) required_tool_type = TOOL_SCREWDRIVER if(iscyborg(user)) diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm index 92059f04d4..059a5aaa34 100644 --- a/code/modules/surgery/limb_augmentation.dm +++ b/code/modules/surgery/limb_augmentation.dm @@ -10,7 +10,7 @@ if(istype(tool, /obj/item/organ_storage) && istype(tool.contents[1], /obj/item/bodypart)) tool = tool.contents[1] var/obj/item/bodypart/aug = tool - if(aug.status != BODYPART_ROBOTIC) + if(!aug.is_robotic_limb()) to_chat(user, "That's not an augment, silly!") return -1 if(aug.body_zone != target_zone) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index b4c58b31ed..a088eb2e4f 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -248,7 +248,7 @@ /obj/item/organ/eyes/robotic/glow/Initialize() . = ..() - mob_overlay = image('icons/mob/eyes.dmi', "eyes_glow_gs") + mob_overlay = image('icons/mob/human_face.dmi', "eyes_glow_gs") /obj/item/organ/eyes/robotic/glow/Destroy() terminate_effects() @@ -430,6 +430,12 @@ if(severity >= 70) owner.adjustOrganLoss(ORGAN_SLOT_EYES, 20) +/obj/item/organ/eyes/night_vision/arachnid + name = "arachnid eyes" + desc = "These eyes seem to have increased sensitivity to bright light, offset by basic night vision." + see_in_dark = 4 + flash_protect = -1 + #undef BLURRY_VISION_ONE #undef BLURRY_VISION_TWO #undef BLIND_VISION_THREE diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 929b42f96d..5c26235193 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -258,3 +258,22 @@ /obj/item/organ/tongue/ethereal/Initialize(mapload) . = ..() languages_possible = languages_possible_ethereal + +/obj/item/organ/tongue/arachnid + name = "inner mandible" + desc = "A set of soft, spoon-esque mandibles closer to the mouth opening, that allow for basic speech, and the ability to speak Rachnidian." + say_mod = "chitters" + var/static/list/languages_possible_arachnid = typecacheof(list( + /datum/language/common, + /datum/language/draconic, + /datum/language/codespeak, + /datum/language/monkey, + /datum/language/narsie, + /datum/language/beachbum, + /datum/language/aphasia, + /datum/language/arachnid, + )) + +/obj/item/organ/tongue/arachnid/Initialize(mapload) + . = ..() + languages_possible = languages_possible_arachnid \ No newline at end of file diff --git a/code/modules/surgery/prosthetic_replacement.dm b/code/modules/surgery/prosthetic_replacement.dm index 8eac5b7895..b0102da7b7 100644 --- a/code/modules/surgery/prosthetic_replacement.dm +++ b/code/modules/surgery/prosthetic_replacement.dm @@ -29,10 +29,10 @@ if(istype(tool, /obj/item/bodypart)) var/obj/item/bodypart/BP = tool if(ismonkey(target))// monkey patient only accept organic monkey limbs - if(BP.status == BODYPART_ROBOTIC || BP.animal_origin != MONKEY_BODYPART) + if(BP.is_robotic_limb() || BP.animal_origin != MONKEY_BODYPART) to_chat(user, "[BP] doesn't match the patient's morphology.") return -1 - if(BP.status != BODYPART_ROBOTIC) + if(!BP.is_robotic_limb()) organ_rejection_dam = 10 if(ishuman(target)) if(BP.animal_origin) diff --git a/code/modules/surgery/robot_brain_surgery.dm b/code/modules/surgery/robot_brain_surgery.dm index 5feca7382f..140dbf74c0 100644 --- a/code/modules/surgery/robot_brain_surgery.dm +++ b/code/modules/surgery/robot_brain_surgery.dm @@ -10,7 +10,7 @@ target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey) possible_locs = list(BODY_ZONE_HEAD) - requires_bodypart_type = BODYPART_ROBOTIC + requires_bodypart_type = 0 desc = "A surgical procedure that restores the default behavior logic and personality matrix of an IPC posibrain." /datum/surgery_step/fix_robot_brain @@ -20,7 +20,7 @@ /datum/surgery/robot_brain_surgery/can_start(mob/user, mob/living/carbon/target, obj/item/tool) var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN) - if(!B) + if(!B || !istype(B, /obj/item/organ/brain/ipc)) //No cheating! return FALSE return TRUE diff --git a/code/modules/surgery/robot_healing.dm b/code/modules/surgery/robot_healing.dm index 6355410a79..8fde1ed33c 100644 --- a/code/modules/surgery/robot_healing.dm +++ b/code/modules/surgery/robot_healing.dm @@ -12,7 +12,7 @@ target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey) possible_locs = list(BODY_ZONE_CHEST) - requires_bodypart_type = BODYPART_ROBOTIC + requires_bodypart_type = 0 //You can do this on anyone, but it won't really be useful on people without augments. ignore_clothes = TRUE var/antispam = FALSE var/healing_step_type = /datum/surgery_step/robot_heal/basic @@ -42,6 +42,16 @@ return FALSE return TRUE +/datum/surgery/robot_healing/can_start(mob/user, mob/living/carbon/target, obj/item/tool) + var/possible = FALSE + for(var/obj/item/bodypart/B in target.bodyparts) + if(B.is_robotic_limb()) + possible = TRUE + break + if(!possible) + return FALSE + return TRUE + /datum/surgery_step/robot_heal/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) var/woundtype if(implement_type == TOOL_WELDER) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index e758a43589..37fa35946c 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -2,13 +2,41 @@ //Keep this sorted alphabetically #ifdef UNIT_TESTS +/// Asserts that a condition is true +/// If the condition is not true, fails the test +#define TEST_ASSERT(assertion, reason) if (!(assertion)) { return Fail("Assertion failed: [reason || "No reason"]") } + +/// Asserts that the two parameters passed are equal, fails otherwise +/// Optionally allows an additional message in the case of a failure +#define TEST_ASSERT_EQUAL(a, b, message) if ((a) != (b)) { return Fail("Expected [isnull(a) ? "null" : a] to be equal to [isnull(b) ? "null" : b].[message ? " [message]" : ""]") } + #include "anchored_mobs.dm" +#include "bespoke_id.dm" +// #include "binary_insert.dm" +// #include "card_mismatch.dm" shame we don't have this! +#include "chain_pull_through_space.dm" #include "character_saving.dm" #include "component_tests.dm" +// #include "confusion.dm" +// #include "keybinding_init.dm" +#include "machine_disassembly.dm" +#include "medical_wounds.dm" +// #include "metabolizing.dm" +// #include "outfit_sanity.dm" +// #include "plantgrowth_tests.dm" +// #include "quick_swap_sanity.dm" - we don't have quick swap yet #include "reagent_id_typos.dm" #include "reagent_recipe_collisions.dm" +#include "resist.dm" +// #include "say.dm" //no saymods, someone update saycode please. +// #include "siunit.dm" #include "spawn_humans.dm" +// #include "species_whitelists.dm" #include "subsystem_init.dm" +// #include "surgeries.dm" // fails at random due to a race condition, commented out for now #include "timer_sanity.dm" #include "unit_test.dm" + +#undef TEST_ASSERT +#undef TEST_ASSERT_EQUAL #endif diff --git a/code/modules/unit_tests/anchored_mobs.dm b/code/modules/unit_tests/anchored_mobs.dm index 5324179bb7..103b97e7a9 100644 --- a/code/modules/unit_tests/anchored_mobs.dm +++ b/code/modules/unit_tests/anchored_mobs.dm @@ -6,4 +6,4 @@ L += "[i]" if(!L.len) return //passed! - Fail("The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]") \ No newline at end of file + Fail("The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]") diff --git a/code/modules/unit_tests/bespoke_id.dm b/code/modules/unit_tests/bespoke_id.dm new file mode 100644 index 0000000000..06676c626c --- /dev/null +++ b/code/modules/unit_tests/bespoke_id.dm @@ -0,0 +1,8 @@ +/datum/unit_test/bespoke_id/Run() + var/datum/element/base = /datum/element + var/base_index = initial(base.id_arg_index) + + for(var/i in subtypesof(/datum/element)) + var/datum/element/faketype = i + if((initial(faketype.element_flags) & ELEMENT_BESPOKE) && initial(faketype.id_arg_index) == base_index) + Fail("A bespoke element was not configured with a proper id_arg_index: [faketype]") diff --git a/code/modules/unit_tests/binary_insert.dm b/code/modules/unit_tests/binary_insert.dm new file mode 100644 index 0000000000..ac7f58208e --- /dev/null +++ b/code/modules/unit_tests/binary_insert.dm @@ -0,0 +1,26 @@ +/// A test to ensure the sanity of BINARY_INSERT +/datum/unit_test/binary_insert/Run() + var/list/datum/binary_insert_node/nodes = list() + + var/datum/binary_insert_node/node_a = new /datum/binary_insert_node(10) + BINARY_INSERT(node_a, nodes, /datum/binary_insert_node, node_a, x, COMPARE_KEY) + TEST_ASSERT_EQUAL(nodes.len, 1, "List should have one node") + + var/datum/binary_insert_node/node_b = new /datum/binary_insert_node(5) + BINARY_INSERT(node_b, nodes, /datum/binary_insert_node, node_b, x, COMPARE_KEY) + TEST_ASSERT_EQUAL(nodes.len, 2, "List should have two nodes") + TEST_ASSERT_EQUAL(nodes[1].x, 5, "The first node should be the one with 5") + TEST_ASSERT_EQUAL(nodes[2].x, 10, "The second node should be the one with 10") + + var/datum/binary_insert_node/node_c = new /datum/binary_insert_node(15) + BINARY_INSERT(node_c, nodes, /datum/binary_insert_node, node_c, x, COMPARE_KEY) + TEST_ASSERT_EQUAL(nodes.len, 3, "List should have three nodes") + TEST_ASSERT_EQUAL(nodes[1].x, 5, "The first node should be the one with 5") + TEST_ASSERT_EQUAL(nodes[2].x, 10, "The second node should be the one with 10") + TEST_ASSERT_EQUAL(nodes[3].x, 15, "The third node should be the one with 15") + +/datum/binary_insert_node + var/x + +/datum/binary_insert_node/New(_x) + x = _x diff --git a/code/modules/unit_tests/chain_pull_through_space.dm b/code/modules/unit_tests/chain_pull_through_space.dm new file mode 100644 index 0000000000..ffdd1bf7c9 --- /dev/null +++ b/code/modules/unit_tests/chain_pull_through_space.dm @@ -0,0 +1,62 @@ +/datum/unit_test/chain_pull_through_space + var/turf/open/space/space_tile + var/turf/claimed_tile + var/mob/living/carbon/human/alice + var/mob/living/carbon/human/bob + var/mob/living/carbon/human/charlie + +/datum/unit_test/chain_pull_through_space/New() + ..() + + // Create a space tile that goes to another z-level + claimed_tile = run_loc_bottom_left + + space_tile = new(locate(run_loc_bottom_left.x, run_loc_bottom_left.y, run_loc_bottom_left.z)) + space_tile.destination_x = 100 + space_tile.destination_y = 100 + space_tile.destination_z = 5 + + // Create our list of humans, all adjacent to one another + alice = new(locate(run_loc_bottom_left.x + 2, run_loc_bottom_left.y, run_loc_bottom_left.z)) + alice.name = "Alice" + + bob = new(locate(run_loc_bottom_left.x + 3, run_loc_bottom_left.y, run_loc_bottom_left.z)) + bob.name = "Bob" + + charlie = new(locate(run_loc_bottom_left.x + 4, run_loc_bottom_left.y, run_loc_bottom_left.z)) + charlie.name = "Charlie" + +/datum/unit_test/chain_pull_through_space/Destroy() + space_tile.copyTurf(claimed_tile) + qdel(alice) + qdel(bob) + qdel(charlie) + return ..() + +/datum/unit_test/chain_pull_through_space/Run() + // Alice pulls Bob, who pulls Charlie + // Normally, when Alice moves forward, the rest follow + alice.start_pulling(bob) + bob.start_pulling(charlie) + + // Walk normally to the left, make sure we're still a chain + alice.Move(locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z)) + if (bob.x != run_loc_bottom_left.x + 2) + return Fail("During normal move, Bob was not at the correct x ([bob.x])") + if (charlie.x != run_loc_bottom_left.x + 3) + return Fail("During normal move, Charlie was not at the correct x ([charlie.x])") + + // We're going through the space turf now that should teleport us + alice.Move(run_loc_bottom_left) + if (alice.z != space_tile.destination_z) + return Fail("Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])") + + if (bob.z != space_tile.destination_z) + return Fail("Bob did not teleport to the destination z-level. Current location: ([bob.x], [bob.y], [bob.z])") + if (!bob.Adjacent(alice)) + return Fail("Bob is not adjacent to Alice. Bob is at [bob.x], Alice is at [alice.x]") + + if (charlie.z != space_tile.destination_z) + return Fail("Charlie did not teleport to the destination z-level. Current location: ([charlie.x], [charlie.y], [charlie.z])") + if (!charlie.Adjacent(bob)) + return Fail("Charlie is not adjacent to Bob. Charlie is at [charlie.x], Bob is at [bob.x]") diff --git a/code/modules/unit_tests/component_tests.dm b/code/modules/unit_tests/component_tests.dm index 409d7f4322..0099d7508c 100644 --- a/code/modules/unit_tests/component_tests.dm +++ b/code/modules/unit_tests/component_tests.dm @@ -9,4 +9,4 @@ if(dupe_type && !ispath(dupe_type)) bad_dts += t if(length(bad_dms) || length(bad_dts)) - Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") \ No newline at end of file + Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") diff --git a/code/modules/unit_tests/machine_disassembly.dm b/code/modules/unit_tests/machine_disassembly.dm new file mode 100644 index 0000000000..bcc769bcf2 --- /dev/null +++ b/code/modules/unit_tests/machine_disassembly.dm @@ -0,0 +1,13 @@ +/// Ensures that when disassembling a machine, all the parts are given back +/datum/unit_test/machine_disassembly/Run() + var/obj/machinery/freezer = allocate(/obj/machinery/atmospherics/components/unary/thermomachine/freezer) + + var/turf/freezer_location = freezer.loc + freezer_location.ChangeTurf(/turf/open/floor/plasteel) + freezer.deconstruct() + + // Check that the components are created + TEST_ASSERT(locate(/obj/item/stock_parts/micro_laser) in freezer_location, "Couldn't find micro-laser when disassembling freezer") + + // Check that the circuit board itself is created + TEST_ASSERT(locate(/obj/item/circuitboard/machine/thermomachine/freezer) in freezer_location, "Couldn't find the circuit board when disassembling freezer") diff --git a/code/modules/unit_tests/medical_wounds.dm b/code/modules/unit_tests/medical_wounds.dm new file mode 100644 index 0000000000..75c08931f1 --- /dev/null +++ b/code/modules/unit_tests/medical_wounds.dm @@ -0,0 +1,87 @@ +/// This test is used to make sure a flesh-and-bone base human can suffer all the types of wounds, and that suffering more severe wounds removes and replaces the lesser wound. Also tests that [/mob/living/carbon/proc/fully_heal] removes all wounds +/datum/unit_test/test_human_base/Run() + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + + /// the limbs have no wound resistance like the chest and head do, so let's go with the r_arm + var/obj/item/bodypart/tested_part = victim.get_bodypart(BODY_ZONE_R_ARM) + /// In order of the wound types we're trying to inflict, what sharpness do we need to deal them? + var/list/sharps = list(SHARP_NONE, SHARP_EDGED, SHARP_POINTY, SHARP_NONE) + /// Since burn wounds need burn damage, duh + var/list/dam_types = list(BRUTE, BRUTE, BRUTE, BURN) + + var/i = 1 + var/list/iter_test_wound_list + + for(iter_test_wound_list in list(list(/datum/wound/blunt/moderate, /datum/wound/blunt/severe, /datum/wound/blunt/critical),\ + list(/datum/wound/slash/moderate, /datum/wound/slash/severe, /datum/wound/slash/critical),\ + list(/datum/wound/pierce/moderate, /datum/wound/pierce/severe, /datum/wound/pierce/critical),\ + list(/datum/wound/burn/moderate, /datum/wound/burn/severe, /datum/wound/burn/critical))) + + TEST_ASSERT_EQUAL(length(victim.all_wounds), 0, "Patient is somehow wounded before test") + var/datum/wound/iter_test_wound + var/threshold_penalty = 0 + + for(iter_test_wound in iter_test_wound_list) + var/threshold = initial(iter_test_wound.threshold_minimum) - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty + if(dam_types[i] == BRUTE) + tested_part.receive_damage(WOUND_MINIMUM_DAMAGE, 0, wound_bonus = threshold, sharpness=sharps[i]) + else if(dam_types[i] == BURN) + tested_part.receive_damage(0, WOUND_MINIMUM_DAMAGE, wound_bonus = threshold, sharpness=sharps[i]) + + TEST_ASSERT(length(victim.all_wounds), "Patient has no wounds when one wound is expected. Severity: [initial(iter_test_wound.severity)]") + TEST_ASSERT_EQUAL(length(victim.all_wounds), 1, "Patient has more than one wound when only one is expected. Severity: [initial(iter_test_wound.severity)]") + var/datum/wound/actual_wound = victim.all_wounds[1] + TEST_ASSERT_EQUAL(actual_wound.type, iter_test_wound, "Patient has wound of incorrect severity. Expected: [initial(iter_test_wound.name)] Got: [actual_wound]") + threshold_penalty = actual_wound.threshold_penalty + i++ + victim.fully_heal(TRUE) // should clear all wounds between types + + +/// This test is used for making sure species with bones but no flesh (skeletons, plasmamen) can only suffer BONE_WOUNDS, and nothing tagged with FLESH_WOUND (it's possible to require both) +/datum/unit_test/test_human_bone/Run() + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + + /// the limbs have no wound resistance like the chest and head do, so let's go with the r_arm + var/obj/item/bodypart/tested_part = victim.get_bodypart(BODY_ZONE_R_ARM) + /// In order of the wound types we're trying to inflict, what sharpness do we need to deal them? + var/list/sharps = list(SHARP_NONE, SHARP_EDGED, SHARP_POINTY, SHARP_NONE) + /// Since burn wounds need burn damage, duh + var/list/dam_types = list(BRUTE, BRUTE, BRUTE, BURN) + + var/i = 1 + var/list/iter_test_wound_list + victim.dna.species.species_traits &= HAS_FLESH // take away the base human's flesh (ouchie!) ((not actually ouchie, this just affects their wounds and dismemberment handling)) + + for(iter_test_wound_list in list(list(/datum/wound/blunt/moderate, /datum/wound/blunt/severe, /datum/wound/blunt/critical),\ + list(/datum/wound/slash/moderate, /datum/wound/slash/severe, /datum/wound/slash/critical),\ + list(/datum/wound/pierce/moderate, /datum/wound/pierce/severe, /datum/wound/pierce/critical),\ + list(/datum/wound/burn/moderate, /datum/wound/burn/severe, /datum/wound/burn/critical))) + + TEST_ASSERT_EQUAL(length(victim.all_wounds), 0, "Patient is somehow wounded before test") + var/datum/wound/iter_test_wound + var/threshold_penalty = 0 + + for(iter_test_wound in iter_test_wound_list) + var/threshold = initial(iter_test_wound.threshold_minimum) - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty + if(dam_types[i] == BRUTE) + tested_part.receive_damage(WOUND_MINIMUM_DAMAGE, 0, wound_bonus = threshold, sharpness=sharps[i]) + else if(dam_types[i] == BURN) + tested_part.receive_damage(0, WOUND_MINIMUM_DAMAGE, wound_bonus = threshold, sharpness=sharps[i]) + + // so if we just tried to deal a flesh wound, make sure we didn't actually suffer it. We may have suffered a bone wound instead, but we just want to make sure we don't have a flesh wound + if(initial(iter_test_wound.wound_flags) & FLESH_WOUND) + if(!length(victim.all_wounds)) // not having a wound is good news + continue + else // we have to check that it's actually a bone wound and not the intended wound type + TEST_ASSERT_EQUAL(length(victim.all_wounds), 1, "Patient has more than one wound when only one is expected. Severity: [initial(iter_test_wound.severity)]") + var/datum/wound/actual_wound = victim.all_wounds[1] + TEST_ASSERT((actual_wound.wound_flags & ~FLESH_WOUND), "Patient has flesh wound despite no HAS_FLESH flag, expected either no wound or bone wound. Offending wound: [actual_wound]") + threshold_penalty = actual_wound.threshold_penalty + else // otherwise if it's a bone wound, check that we have it per usual + TEST_ASSERT(length(victim.all_wounds), "Patient has no wounds when one wound is expected. Severity: [initial(iter_test_wound.severity)]") + TEST_ASSERT_EQUAL(length(victim.all_wounds), 1, "Patient has more than one wound when only one is expected. Severity: [initial(iter_test_wound.severity)]") + var/datum/wound/actual_wound = victim.all_wounds[1] + TEST_ASSERT_EQUAL(actual_wound.type, iter_test_wound, "Patient has wound of incorrect severity. Expected: [initial(iter_test_wound.name)] Got: [actual_wound]") + threshold_penalty = actual_wound.threshold_penalty + i++ + victim.fully_heal(TRUE) // should clear all wounds between types diff --git a/code/modules/unit_tests/metabolizing.dm b/code/modules/unit_tests/metabolizing.dm new file mode 100644 index 0000000000..895762c0ec --- /dev/null +++ b/code/modules/unit_tests/metabolizing.dm @@ -0,0 +1,19 @@ +/datum/unit_test/metabolization/Run() + // Pause natural mob life so it can be handled entirely by the test + SSmobs.pause() + + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + var/mob/living/carbon/monkey/monkey = allocate(/mob/living/carbon/monkey) + + for (var/reagent_type in subtypesof(/datum/reagent)) + test_reagent(human, reagent_type) + test_reagent(monkey, reagent_type) + +/datum/unit_test/metabolization/proc/test_reagent(mob/living/carbon/C, reagent_type) + C.reagents.add_reagent(reagent_type, 10) + C.reagents.metabolize(C, can_overdose = TRUE) + C.reagents.clear_reagents() + +/datum/unit_test/metabolization/Destroy() + SSmobs.ignite() + return ..() diff --git a/code/modules/unit_tests/outfit_sanity.dm b/code/modules/unit_tests/outfit_sanity.dm new file mode 100644 index 0000000000..235820f9e9 --- /dev/null +++ b/code/modules/unit_tests/outfit_sanity.dm @@ -0,0 +1,50 @@ +#define CHECK_OUTFIT_SLOT(outfit_key, slot_name) if (outfit.##outfit_key) { \ + H.equip_to_slot_or_del(new outfit.##outfit_key(H), ##slot_name, TRUE); \ + /* We don't check the result of equip_to_slot_or_del because it returns false for random jumpsuits, as they delete themselves on init */ \ + if (!H.get_item_by_slot(##slot_name)) { \ + Fail("[outfit.name]'s [#outfit_key] is invalid!"); \ + } \ +} + +/datum/unit_test/outfit_sanity/Run() + var/mob/living/carbon/human/H = allocate(/mob/living/carbon/human) + + for (var/outfit_type in subtypesof(/datum/outfit)) + // Only make one human and keep undressing it because it's much faster + for (var/obj/item/I in H.get_equipped_items(include_pockets = TRUE)) + qdel(I) + + var/datum/outfit/outfit = new outfit_type + outfit.pre_equip(H, TRUE) + + CHECK_OUTFIT_SLOT(uniform, ITEM_SLOT_ICLOTHING) + CHECK_OUTFIT_SLOT(suit, ITEM_SLOT_OCLOTHING) + CHECK_OUTFIT_SLOT(back, ITEM_SLOT_BACK) + CHECK_OUTFIT_SLOT(belt, ITEM_SLOT_BELT) + CHECK_OUTFIT_SLOT(gloves, ITEM_SLOT_GLOVES) + CHECK_OUTFIT_SLOT(shoes, ITEM_SLOT_FEET) + CHECK_OUTFIT_SLOT(head, ITEM_SLOT_HEAD) + CHECK_OUTFIT_SLOT(mask, ITEM_SLOT_MASK) + CHECK_OUTFIT_SLOT(neck, ITEM_SLOT_NECK) + CHECK_OUTFIT_SLOT(ears, ITEM_SLOT_EARS) + CHECK_OUTFIT_SLOT(glasses, ITEM_SLOT_EYES) + CHECK_OUTFIT_SLOT(id, ITEM_SLOT_ID) + CHECK_OUTFIT_SLOT(suit_store, ITEM_SLOT_SUITSTORE) + CHECK_OUTFIT_SLOT(l_pocket, ITEM_SLOT_POCKET) + CHECK_OUTFIT_SLOT(r_pocket, ITEM_SLOT_POCKET) + + if (outfit.backpack_contents || outfit.box) + var/list/backpack_contents = outfit.backpack_contents?.Copy() + if (outfit.box) + if (!backpack_contents) + backpack_contents = list() + backpack_contents.Insert(1, outfit.box) + backpack_contents[outfit.box] = 1 + + for (var/path in backpack_contents) + var/number = backpack_contents[path] || 1 + for (var/_ in 1 to number) + if (!H.equip_to_slot_or_del(new path(H), ITEM_SLOT_BACKPACK, TRUE)) + Fail("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") + +#undef CHECK_OUTFIT_SLOT diff --git a/code/modules/unit_tests/plantgrowth_tests.dm b/code/modules/unit_tests/plantgrowth_tests.dm new file mode 100644 index 0000000000..6b40236860 --- /dev/null +++ b/code/modules/unit_tests/plantgrowth_tests.dm @@ -0,0 +1,27 @@ + +// Checks plants for broken tray icons. Use Advanced Proc Call to activate. +// Maybe some day it would be used as unit test. +// -------- IT IS NOW! +/datum/unit_test/plantgrowth/Run() + var/list/states = icon_states('icons/obj/hydroponics/growing.dmi') + states |= icon_states('icons/obj/hydroponics/growing_fruits.dmi') + states |= icon_states('icons/obj/hydroponics/growing_flowers.dmi') + states |= icon_states('icons/obj/hydroponics/growing_mushrooms.dmi') + states |= icon_states('icons/obj/hydroponics/growing_vegetables.dmi') + states |= icon_states('goon/icons/obj/hydroponics.dmi') + var/list/paths = subtypesof(/obj/item/seeds) - /obj/item/seeds - typesof(/obj/item/seeds/sample) - /obj/item/seeds/lavaland + + for(var/seedpath in paths) + var/obj/item/seeds/seed = new seedpath + + for(var/i in 1 to seed.growthstages) + if("[seed.icon_grow][i]" in states) + continue + Fail("[seed.name] ([seed.type]) lacks the [seed.icon_grow][i] icon!") + + if(!(seed.icon_dead in states)) + Fail("[seed.name] ([seed.type]) lacks the [seed.icon_dead] icon!") + + if(seed.icon_harvest) // mushrooms have no grown sprites, same for items with no product + if(!(seed.icon_harvest in states)) + Fail("[seed.name] ([seed.type]) lacks the [seed.icon_harvest] icon!") diff --git a/code/modules/unit_tests/quick_swap_sanity.dm b/code/modules/unit_tests/quick_swap_sanity.dm new file mode 100644 index 0000000000..85e73f9b6a --- /dev/null +++ b/code/modules/unit_tests/quick_swap_sanity.dm @@ -0,0 +1,31 @@ +/// Test that quick swap correctly swaps items and invalidates suit storage +/datum/unit_test/quick_swap_sanity/Run() + // Create a human with a medical winter coat and a health analyzer in suit storage + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + + var/obj/item/coat = allocate(/obj/item/clothing/suit/hooded/wintercoat/medical) + TEST_ASSERT(human.equip_to_slot_if_possible(coat, ITEM_SLOT_OCLOTHING), "Couldn't equip winter coat") + + var/obj/item/analyzer = allocate(/obj/item/healthanalyzer) + TEST_ASSERT(human.equip_to_slot_if_possible(analyzer, ITEM_SLOT_SUITSTORE), "Couldn't equip health analyzer") + + // Then, have them quick swap between the coat and a space suit + var/obj/item/hardsuit = allocate(/obj/item/clothing/suit/space/hardsuit) + TEST_ASSERT(human.equip_to_appropriate_slot(hardsuit, swap = TRUE), "Couldn't quick swap to hardsuit") + + // Check if the human has the hardsuit on + TEST_ASSERT_EQUAL(human.wear_suit, hardsuit, "Human didn't equip the hardsuit") + + // Make sure the health analyzer was dropped as part of the swap + // Since health analyzers are an invalid suit storage item + TEST_ASSERT_EQUAL(human.s_store, null, "Human didn't drop the health analyzer") + + // Give the human an emergency oxygen tank + // This is valid suit storage for both the winter coat AND the hardsuit + var/obj/item/tank = allocate(/obj/item/tank/internals/emergency_oxygen) + TEST_ASSERT(human.equip_to_slot_if_possible(tank, ITEM_SLOT_SUITSTORE), "Couldn't equip emergency oxygen tank") + + // Now, quick swap back to the coat + // Since the tank is a valid suit storage item, it should not be dropped + TEST_ASSERT(human.equip_to_appropriate_slot(coat, swap = TRUE), "Couldn't quick swap to coat") + TEST_ASSERT_EQUAL(human.s_store, tank, "Human dropped the oxygen tank, when it was a valid item to keep in suit storage") diff --git a/code/modules/unit_tests/resist.dm b/code/modules/unit_tests/resist.dm new file mode 100644 index 0000000000..9fe5cd1114 --- /dev/null +++ b/code/modules/unit_tests/resist.dm @@ -0,0 +1,29 @@ +/// Test that stop, drop, and roll lowers fire stacks +/datum/unit_test/stop_drop_and_roll/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + + TEST_ASSERT_EQUAL(human.fire_stacks, 0, "Human does not have 0 fire stacks pre-ignition") + + human.adjust_fire_stacks(5) + human.IgniteMob() + + TEST_ASSERT_EQUAL(human.fire_stacks, 5, "Human does not have 5 fire stacks pre-resist") + + // Stop, drop, and roll has a sleep call. This would delay the test, and is not necessary. + CallAsync(human, /mob/living/verb/resist) + + TEST_ASSERT(human.fire_stacks < 5, "Human did not lower fire stacks after resisting") + +/// Test that you can resist out of a container +/datum/unit_test/container_resist/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + var/obj/structure/closet/closet = allocate(/obj/structure/closet, get_turf(human)) + + closet.open(human) + TEST_ASSERT(!(human in closet.contents), "Human was in the contents of an open closet") + + closet.close(human) + TEST_ASSERT(human in closet.contents, "Human was not in the contents of the closed closet") + + human.resist() + TEST_ASSERT(!(human in closet.contents), "Human resisted out of a standard closet, but was still in it") diff --git a/code/modules/unit_tests/say.dm b/code/modules/unit_tests/say.dm new file mode 100644 index 0000000000..3fe6675ab4 --- /dev/null +++ b/code/modules/unit_tests/say.dm @@ -0,0 +1,24 @@ +/// Test to verify message mods are parsed correctly +/datum/unit_test/get_message_mods + var/mob/host_mob + +/datum/unit_test/get_message_mods/Run() + host_mob = allocate(/mob/living/carbon/human) + + test("Hello", "Hello", list()) + test(";HELP", "HELP", list(MODE_HEADSET = TRUE)) + test(";%Never gonna give you up", "Never gonna give you up", list(MODE_HEADSET = TRUE, MODE_SING = TRUE)) + test(".s Gun plz", "Gun plz", list(RADIO_KEY = RADIO_KEY_SECURITY, RADIO_EXTENSION = RADIO_CHANNEL_SECURITY)) + test("...What", "...What", list()) + //note to lettern: add the ++, ||, __, and the verb*text checks + +/datum/unit_test/get_message_mods/proc/test(message, expected_message, list/expected_mods) + var/list/mods = list() + TEST_ASSERT_EQUAL(host_mob.get_message_mods(message, mods), expected_message, "Chopped message was not what we expected. Message: [message]") + + for (var/mod_key in mods) + TEST_ASSERT_EQUAL(mods[mod_key], expected_mods[mod_key], "The value for [mod_key] was not what we expected. Message: [message]") + expected_mods -= mod_key + + if (expected_mods.len) + Fail("Some message mods were expected, but were not returned by get_message_mods: [json_encode(expected_mods)]. Message: [message]") diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm index 0500deae0a..7189e87277 100644 --- a/code/modules/unit_tests/spawn_humans.dm +++ b/code/modules/unit_tests/spawn_humans.dm @@ -1,7 +1,7 @@ /datum/unit_test/spawn_humans/Run() - var/locs = block(run_loc_bottom_left, run_loc_top_right) + var/locs = block(run_loc_bottom_left, run_loc_top_right) - for(var/I in 1 to 5) - new /mob/living/carbon/human(pick(locs)) + for(var/I in 1 to 5) + new /mob/living/carbon/human(pick(locs)) - sleep(50) + sleep(50) diff --git a/code/modules/unit_tests/subsystem_init.dm b/code/modules/unit_tests/subsystem_init.dm index f768f03f78..7d5473bc1b 100644 --- a/code/modules/unit_tests/subsystem_init.dm +++ b/code/modules/unit_tests/subsystem_init.dm @@ -4,4 +4,4 @@ if(ss.flags & SS_NO_INIT) continue if(!ss.initialized) - Fail("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.") \ No newline at end of file + Fail("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.") diff --git a/code/modules/unit_tests/surgeries.dm b/code/modules/unit_tests/surgeries.dm new file mode 100644 index 0000000000..7b8145ac19 --- /dev/null +++ b/code/modules/unit_tests/surgeries.dm @@ -0,0 +1,81 @@ +/datum/unit_test/amputation/Run() + var/mob/living/carbon/human/patient = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human) + + TEST_ASSERT_EQUAL(patient.get_missing_limbs().len, 0, "Patient is somehow missing limbs before surgery") + + var/datum/surgery/amputation/surgery = new(patient, BODY_ZONE_R_ARM, patient.get_bodypart(BODY_ZONE_R_ARM)) + + var/datum/surgery_step/sever_limb/sever_limb = new + sever_limb.success(user, patient, BODY_ZONE_R_ARM, null, surgery) + + TEST_ASSERT_EQUAL(patient.get_missing_limbs().len, 1, "Patient did not lose any limbs") + TEST_ASSERT_EQUAL(patient.get_missing_limbs()[1], BODY_ZONE_R_ARM, "Patient is missing a limb that isn't the one we operated on") + +/datum/unit_test/brain_surgery/Run() + var/mob/living/carbon/human/patient = allocate(/mob/living/carbon/human) + patient.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY) + patient.setOrganLoss(ORGAN_SLOT_BRAIN, 20) + + TEST_ASSERT(patient.has_trauma_type(), "Patient does not have any traumas, despite being given one") + + var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human) + + var/datum/surgery_step/fix_brain/fix_brain = new + fix_brain.success(user, patient) + + TEST_ASSERT(!patient.has_trauma_type(), "Patient kept their brain trauma after brain surgery") + TEST_ASSERT(patient.getOrganLoss(ORGAN_SLOT_BRAIN) < 20, "Patient did not heal their brain damage after brain surgery") + +/datum/unit_test/multiple_surgeries/Run() + var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/patient_zero = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/patient_one = allocate(/mob/living/carbon/human) + + var/obj/item/scalpel/scalpel = allocate(/obj/item/scalpel) + + var/datum/surgery_step/incise/surgery_step = new + var/datum/surgery/organ_manipulation/surgery_for_zero = new + + INVOKE_ASYNC(surgery_step, /datum/surgery_step/proc/initiate, user, patient_zero, BODY_ZONE_CHEST, scalpel, surgery_for_zero) + TEST_ASSERT(surgery_for_zero.step_in_progress, "Surgery on patient zero was not initiated") + + var/datum/surgery/organ_manipulation/surgery_for_one = new + + sleep(0.2) // if we don't have this, then the next surgery step can start *before* the previous one does, which is no good + + // Without waiting for the incision to complete, try to start a new surgery + TEST_ASSERT(!surgery_step.initiate(user, patient_one, BODY_ZONE_CHEST, scalpel, surgery_for_one), "Was allowed to start a second surgery without the rod of asclepius") + TEST_ASSERT(!surgery_for_one.step_in_progress, "Surgery for patient one is somehow in progress, despite not initiating") + + user.apply_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH) + INVOKE_ASYNC(surgery_step, /datum/surgery_step/proc/initiate, user, patient_one, BODY_ZONE_CHEST, scalpel, surgery_for_one) + TEST_ASSERT(surgery_for_one.step_in_progress, "Surgery on patient one was not initiated, despite having rod of asclepius") + +/datum/unit_test/tend_wounds/Run() + var/mob/living/carbon/human/patient = allocate(/mob/living/carbon/human) + patient.take_overall_damage(100, 100) + + var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human) + + // Test that tending wounds actually lowers damage + var/datum/surgery_step/heal/brute/basic/basic_brute_heal = new + basic_brute_heal.success(user, patient, BODY_ZONE_CHEST) + TEST_ASSERT(patient.getBruteLoss() < 100, "Tending brute wounds didn't lower brute damage ([patient.getBruteLoss()])") + + var/datum/surgery_step/heal/burn/basic/basic_burn_heal = new + basic_burn_heal.success(user, patient, BODY_ZONE_CHEST) + TEST_ASSERT(patient.getFireLoss() < 100, "Tending burn wounds didn't lower burn damage ([patient.getFireLoss()])") + + // Test that wearing clothing lowers heal amount + var/mob/living/carbon/human/naked_patient = allocate(/mob/living/carbon/human) + naked_patient.take_overall_damage(100) + + var/mob/living/carbon/human/clothed_patient = allocate(/mob/living/carbon/human) + clothed_patient.equipOutfit(/datum/outfit/job/doctor, TRUE) + clothed_patient.take_overall_damage(100) + + basic_brute_heal.success(user, naked_patient, BODY_ZONE_CHEST) + basic_brute_heal.success(user, clothed_patient, BODY_ZONE_CHEST) + + TEST_ASSERT(naked_patient.getBruteLoss() < clothed_patient.getBruteLoss(), "Naked patient did not heal more from wounds tending than a clothed patient") diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index 49974f2cb0..36b406e75e 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -1,14 +1,9 @@ /* - Usage: Override /Run() to run your test code - Call Fail() to fail the test (You should specify a reason) - You may use /New() and /Destroy() for setup/teardown respectively - You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testing - */ GLOBAL_DATUM(current_test, /datum/unit_test) @@ -18,16 +13,18 @@ GLOBAL_VAR(test_log) /datum/unit_test //Bit of metadata for the future maybe var/list/procs_tested - + //usable vars var/turf/run_loc_bottom_left var/turf/run_loc_top_right //internal shit var/succeeded = TRUE + var/list/allocated var/list/fail_reasons /datum/unit_test/New() + allocated = new run_loc_bottom_left = locate(1, 1, 1) run_loc_top_right = locate(5, 5, 1) @@ -35,6 +32,7 @@ GLOBAL_VAR(test_log) //clear the test area for(var/atom/movable/AM in block(run_loc_bottom_left, run_loc_top_right)) qdel(AM) + QDEL_LIST(allocated) return ..() /datum/unit_test/proc/Run() @@ -48,6 +46,18 @@ GLOBAL_VAR(test_log) LAZYADD(fail_reasons, reason) +/// Allocates an instance of the provided type, and places it somewhere in an available loc +/// Instances allocated through this proc will be destroyed when the test is over +/datum/unit_test/proc/allocate(type, ...) + var/list/arguments = args.Copy(2) + if (!arguments.len) + arguments = list(run_loc_bottom_left) + else if (arguments[1] == null) + arguments[1] = run_loc_bottom_left + var/instance = new type(arglist(arguments)) + allocated += instance + return instance + /proc/RunUnitTests() CHECK_TICK diff --git a/code/modules/uplink/uplink_items/uplink_clothing.dm b/code/modules/uplink/uplink_items/uplink_clothing.dm index 745eddcc07..6163e5722a 100644 --- a/code/modules/uplink/uplink_items/uplink_clothing.dm +++ b/code/modules/uplink/uplink_items/uplink_clothing.dm @@ -92,8 +92,8 @@ cost = 6 exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) -/datum/uplink_item/device_tools/guerillagloves - name = "Guerilla Gloves" +/datum/uplink_item/device_tools/guerrillagloves + name = "Guerrilla Gloves" desc = "A pair of highly robust combat gripper gloves that excels at performing takedowns at close range, with an added lining of insulation. Careful not to hit a wall!" item = /obj/item/clothing/gloves/tackler/combat/insulated include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 26bee38f7b..a115300085 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -529,7 +529,7 @@ GLOBAL_LIST_EMPTY(vending_products) if(5) // limb squish! for(var/i in C.bodyparts) var/obj/item/bodypart/squish_part = i - if(squish_part.is_organic_limb() || squish_part.render_like_organic) + if(squish_part.is_organic_limb()) var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate)) squish_part.force_wound_upwards(type_wound) else diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm index 8ad4b0568c..c3540777ab 100644 --- a/code/modules/vending/security.dm +++ b/code/modules/vending/security.dm @@ -15,7 +15,7 @@ /obj/item/secbat = 5) contraband = list(/obj/item/clothing/glasses/sunglasses = 2, /obj/item/storage/fancy/donut_box = 2, - /obj/item/ssword_kit = 1) + /obj/item/storage/belt/sabre/secbelt = 1) premium = list(/obj/item/coin/antagtoken = 1, /obj/item/clothing/head/helmet/blueshirt = 1, /obj/item/clothing/suit/armor/vest/blueshirt = 1, diff --git a/config/game_options.txt b/config/game_options.txt index a5b0d0b8c4..bb6c5d8f01 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -477,6 +477,7 @@ ROUNDSTART_RACES xeno ROUNDSTART_RACES slimeperson ROUNDSTART_RACES guilmon ROUNDSTART_RACES ipc +ROUNDSTART_RACES arachnid ##------------------------------------------------------------------------------------------- diff --git a/html/changelogs/AutoChangeLog-pr-13014.yml b/html/changelogs/AutoChangeLog-pr-13014.yml new file mode 100644 index 0000000000..861e797669 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13014.yml @@ -0,0 +1,4 @@ +author: "Hatterhat" +delete-after: True +changes: + - rscadd: "The wastes of Lavaland and the icy caverns of Snow Taxi rumble in unison." diff --git a/html/changelogs/AutoChangeLog-pr-13193.yml b/html/changelogs/AutoChangeLog-pr-13193.yml new file mode 100644 index 0000000000..980a9366d8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13193.yml @@ -0,0 +1,4 @@ +author: "silicons" +delete-after: True +changes: + - tweak: "medium screens are better now" diff --git a/html/changelogs/AutoChangeLog-pr-13233.yml b/html/changelogs/AutoChangeLog-pr-13233.yml new file mode 100644 index 0000000000..4366abec64 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13233.yml @@ -0,0 +1,6 @@ +author: "Hatterhat" +delete-after: True +changes: + - rscadd: "Changeling bone gauntlets! They punch the shit out of people really good." + - tweak: "Guerilla gloves and gorilla gloves inherit the strip modifiers of their predecessors, because apparently they had those." + - balance: "Pugilists now always hit the targeted limb and never miss." diff --git a/html/changelogs/AutoChangeLog-pr-13346.yml b/html/changelogs/AutoChangeLog-pr-13346.yml new file mode 100644 index 0000000000..2e3d4e3280 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13346.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - refactor: "Dwarf speech is no longer absolutely paranoid about word replacement." diff --git a/html/changelogs/AutoChangeLog-pr-13349.yml b/html/changelogs/AutoChangeLog-pr-13349.yml new file mode 100644 index 0000000000..2469b31ecd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13349.yml @@ -0,0 +1,4 @@ +author: "LetterN" +delete-after: True +changes: + - code_imp: "Updates git and build tests." diff --git a/html/changelogs/AutoChangeLog-pr-13473.yml b/html/changelogs/AutoChangeLog-pr-13473.yml new file mode 100644 index 0000000000..aebdfbb3cb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13473.yml @@ -0,0 +1,4 @@ +author: "Tupinambis" +delete-after: True +changes: + - rscadd: "Arachnids (spider people) with limited night vision, flash vulnerability, and webbing." diff --git a/html/changelogs/AutoChangeLog-pr-13497.yml b/html/changelogs/AutoChangeLog-pr-13497.yml new file mode 100644 index 0000000000..c5f569bad3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13497.yml @@ -0,0 +1,5 @@ +author: "Zandario" +delete-after: True +changes: + - code_imp: "Added some framework for future species expansions, including clothing refitting." + - refactor: "Made majority of the relevant Species IDs and Categories pre-defined, also for easier expansion and use." diff --git a/html/changelogs/AutoChangeLog-pr-13525.yml b/html/changelogs/AutoChangeLog-pr-13525.yml new file mode 100644 index 0000000000..eb441f04c3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13525.yml @@ -0,0 +1,4 @@ +author: "Detective-Google" +delete-after: True +changes: + - bugfix: "Hilbert's jukebox works" diff --git a/html/changelogs/AutoChangeLog-pr-13527.yml b/html/changelogs/AutoChangeLog-pr-13527.yml new file mode 100644 index 0000000000..7e65116e83 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13527.yml @@ -0,0 +1,4 @@ +author: "ArchieBeepBoop" +delete-after: True +changes: + - rscadd: "Upgraded Advanced RTG Machine Preset" diff --git a/html/changelogs/AutoChangeLog-pr-13528.yml b/html/changelogs/AutoChangeLog-pr-13528.yml new file mode 100644 index 0000000000..b7bd0d4618 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13528.yml @@ -0,0 +1,11 @@ +author: "DeltaFire15" +delete-after: True +changes: + - balance: "Robotic Limbs now each have their own damage threshhold values" + - balance: "Robotic Limb damage threshholds are now seperated into threshhold itself and mindamage when passed +balance; Hybrid limbs can now be injected with hypos, but not sprayed (Still not healed by chems)" + - tweak: "Brain surgery has been tweaked back to allowing robotic limbs, blacklisting IPC brains instead." + - tweak: "Robot brain surgery can now be used on organic heads, if there is a IPC brain in them somehow." + - tweak: "The robot limb heal surgery can now be used even if the target's torso is not robotic, as long as they have robotic limbs" + - refactor: "BODYPART_ROBOTIC / BODYPART_ORGANIC checks replaced with helper-procs whereever possible." + - code_imp: "Added a BODYPART_HYBRID define for robotic bodyparts that behave organic in some regards." diff --git a/html/changelogs/AutoChangeLog-pr-13529.yml b/html/changelogs/AutoChangeLog-pr-13529.yml new file mode 100644 index 0000000000..54f775b5c8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13529.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - tweak: "Planetary atmos no longer does superconduction." diff --git a/html/changelogs/AutoChangeLog-pr-13530.yml b/html/changelogs/AutoChangeLog-pr-13530.yml new file mode 100644 index 0000000000..b99ed1c545 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13530.yml @@ -0,0 +1,4 @@ +author: "silicons" +delete-after: True +changes: + - rscadd: "Clowns now have unpredictable effects on supermatter crystals when dusting from contact." diff --git a/html/changelogs/AutoChangeLog-pr-13533.yml b/html/changelogs/AutoChangeLog-pr-13533.yml new file mode 100644 index 0000000000..1ba14c689c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13533.yml @@ -0,0 +1,4 @@ +author: "shellspeed1" +delete-after: True +changes: + - rscadd: "A new recipe for a spicy has been given to us by a strange business man." diff --git a/html/changelogs/AutoChangeLog-pr-13535.yml b/html/changelogs/AutoChangeLog-pr-13535.yml new file mode 100644 index 0000000000..b020587eb8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13535.yml @@ -0,0 +1,7 @@ +author: "Hatterhat" +delete-after: True +changes: + - rscadd: "Energy sabre reskin for the energy sword - access via alt-click." + - bugfix: "Alt-click reskins are fixed." + - tweak: "Defibrillators and their many, many overlays were moved to another .dmi." + - tweak: "You can now change the color of an energy sword via multitool. Not deswords. Yet." diff --git a/html/changelogs/AutoChangeLog-pr-13540.yml b/html/changelogs/AutoChangeLog-pr-13540.yml new file mode 100644 index 0000000000..0d7d9b8d2c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13540.yml @@ -0,0 +1,6 @@ +author: "ArcaneMusic, The0bserver-sys" +delete-after: True +changes: + - rscadd: "New from Hydrowear LLC: The Botanical Belt! This handy yellow belt lets you hold most of your botany gear, and a few beakers for reduced bag and floor clutter!" + - tweak: "Gives Hydrotrays plumbing pipes automatically, allowing you to make a self sustaining tray via plumbing." + - tweak: "Gives Service access to Bluespace Beakers, at last, gives Cargo, Science, and Medical the ability to construct reinforced plungers for use on lavaland." diff --git a/html/changelogs/AutoChangeLog-pr-13543.yml b/html/changelogs/AutoChangeLog-pr-13543.yml new file mode 100644 index 0000000000..be2cb215c9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13543.yml @@ -0,0 +1,5 @@ +author: "zeroisthebiggay" +delete-after: True +changes: + - rscadd: "The legion megafauna has been reworked. The fight should now be both slightly harder and faster." + - balance: "You can no longer cheese the colossus by being a sand golem and simply being immune." diff --git a/html/changelogs/AutoChangeLog-pr-13544.yml b/html/changelogs/AutoChangeLog-pr-13544.yml new file mode 100644 index 0000000000..61cf99090d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13544.yml @@ -0,0 +1,4 @@ +author: "DeltaFire15" +delete-after: True +changes: + - bugfix: "Portals no longer runtime because of incorrect args." diff --git a/html/changelogs/AutoChangeLog-pr-13547.yml b/html/changelogs/AutoChangeLog-pr-13547.yml new file mode 100644 index 0000000000..0355a871d1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13547.yml @@ -0,0 +1,4 @@ +author: "zeroisthebiggay" +delete-after: True +changes: + - rscadd: "secsheath for your cool stunsword at your local security vendor. you gotta hack it first though." diff --git a/html/changelogs/AutoChangeLog-pr-13549.yml b/html/changelogs/AutoChangeLog-pr-13549.yml new file mode 100644 index 0000000000..5d46fbcb6c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13549.yml @@ -0,0 +1,4 @@ +author: "BlueWildrose" +delete-after: True +changes: + - bugfix: "Fixes cloning computer UI not updating when pressing certain buttons - also adds extra check for names to update a message" diff --git a/html/changelogs/AutoChangeLog-pr-13552.yml b/html/changelogs/AutoChangeLog-pr-13552.yml new file mode 100644 index 0000000000..1504d1d3a0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13552.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - bugfix: "Multi-surgery unit test no longer fails at random." diff --git a/html/changelogs/AutoChangeLog-pr-13553.yml b/html/changelogs/AutoChangeLog-pr-13553.yml new file mode 100644 index 0000000000..2759fa2754 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13553.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - bugfix: "eye sprites should look normal once more" diff --git a/html/changelogs/AutoChangeLog-pr-13554.yml b/html/changelogs/AutoChangeLog-pr-13554.yml new file mode 100644 index 0000000000..3a14adff37 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13554.yml @@ -0,0 +1,4 @@ +author: "Vynzill" +delete-after: True +changes: + - bugfix: "fixes high luminosity eyes" diff --git a/html/changelogs/AutoChangeLog-pr-13561.yml b/html/changelogs/AutoChangeLog-pr-13561.yml new file mode 100644 index 0000000000..097336bc3b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13561.yml @@ -0,0 +1,4 @@ +author: "DeltaFire15" +delete-after: True +changes: + - bugfix: "Your target cryoing will no longer give you a free greentext." diff --git a/html/changelogs/AutoChangeLog-pr-13563.yml b/html/changelogs/AutoChangeLog-pr-13563.yml new file mode 100644 index 0000000000..c4c9b68c39 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13563.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - tweak: "cloning now correctly copies your blood colour, body sprite type and eye type" diff --git a/html/changelogs/AutoChangeLog-pr-13566.yml b/html/changelogs/AutoChangeLog-pr-13566.yml new file mode 100644 index 0000000000..14ee8755a9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13566.yml @@ -0,0 +1,4 @@ +author: "zeroisthebiggay" +delete-after: True +changes: + - bugfix: "legion now drops chests" diff --git a/html/changelogs/AutoChangeLog-pr-13573.yml b/html/changelogs/AutoChangeLog-pr-13573.yml new file mode 100644 index 0000000000..9bac0ce44d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13573.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - bugfix: "removed a unit test causing master to fail" diff --git a/icons/misc/language.dmi b/icons/misc/language.dmi index 9501dc9216..a33f914885 100644 Binary files a/icons/misc/language.dmi and b/icons/misc/language.dmi differ diff --git a/icons/mob/actions/actions_changeling.dmi b/icons/mob/actions/actions_changeling.dmi index ce6b81e13b..2f384c6119 100644 Binary files a/icons/mob/actions/actions_changeling.dmi and b/icons/mob/actions/actions_changeling.dmi differ diff --git a/icons/mob/arachnid_legs.dmi b/icons/mob/arachnid_legs.dmi new file mode 100644 index 0000000000..bdd67a518f Binary files /dev/null and b/icons/mob/arachnid_legs.dmi differ diff --git a/icons/mob/arachnid_mandibles.dmi b/icons/mob/arachnid_mandibles.dmi new file mode 100644 index 0000000000..9369e2d5fa Binary files /dev/null and b/icons/mob/arachnid_mandibles.dmi differ diff --git a/icons/mob/arachnid_parts.dmi b/icons/mob/arachnid_parts.dmi new file mode 100644 index 0000000000..1b70935450 Binary files /dev/null and b/icons/mob/arachnid_parts.dmi differ diff --git a/icons/mob/arachnid_spinneret.dmi b/icons/mob/arachnid_spinneret.dmi new file mode 100644 index 0000000000..688ff90e0d Binary files /dev/null and b/icons/mob/arachnid_spinneret.dmi differ diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi index 4ac82ca299..f84e360179 100644 Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi index 44499649f9..b95c377e12 100644 Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ diff --git a/icons/mob/eyes.dmi b/icons/mob/eyes.dmi index be25096615..4b8dba160a 100644 Binary files a/icons/mob/eyes.dmi and b/icons/mob/eyes.dmi differ diff --git a/icons/mob/human_parts.dmi b/icons/mob/human_parts.dmi index 173995e6c4..34a1ada483 100644 Binary files a/icons/mob/human_parts.dmi and b/icons/mob/human_parts.dmi differ diff --git a/icons/mob/inhands/weapons/swords_lefthand.dmi b/icons/mob/inhands/weapons/swords_lefthand.dmi index 23d80af9ef..5ff248de68 100644 Binary files a/icons/mob/inhands/weapons/swords_lefthand.dmi and b/icons/mob/inhands/weapons/swords_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/swords_righthand.dmi b/icons/mob/inhands/weapons/swords_righthand.dmi index 702c0299b5..0332e60fb5 100644 Binary files a/icons/mob/inhands/weapons/swords_righthand.dmi and b/icons/mob/inhands/weapons/swords_righthand.dmi differ diff --git a/icons/mob/lavaland/64x64megafauna.dmi b/icons/mob/lavaland/64x64megafauna.dmi index 1794c789de..997291ec2c 100644 Binary files a/icons/mob/lavaland/64x64megafauna.dmi and b/icons/mob/lavaland/64x64megafauna.dmi differ diff --git a/icons/mob/lavaland/96x96megafauna.dmi b/icons/mob/lavaland/96x96megafauna.dmi index e13419532d..4c19d96f42 100644 Binary files a/icons/mob/lavaland/96x96megafauna.dmi and b/icons/mob/lavaland/96x96megafauna.dmi differ diff --git a/icons/mob/lavaland/lavaland_monsters.dmi b/icons/mob/lavaland/lavaland_monsters.dmi index f8394dd235..30750b216d 100644 Binary files a/icons/mob/lavaland/lavaland_monsters.dmi and b/icons/mob/lavaland/lavaland_monsters.dmi differ diff --git a/icons/mob/lavaland/legion.dmi b/icons/mob/lavaland/legion.dmi index d8d165b79a..c9d58c0a24 100644 Binary files a/icons/mob/lavaland/legion.dmi and b/icons/mob/lavaland/legion.dmi differ diff --git a/icons/obj/clothing/belt_overlays.dmi b/icons/obj/clothing/belt_overlays.dmi index 1c22a0ad1b..cdb2567268 100644 Binary files a/icons/obj/clothing/belt_overlays.dmi and b/icons/obj/clothing/belt_overlays.dmi differ diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi index 1a98872463..48aaa3a418 100644 Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi index c0f78ee604..0feb6a75db 100644 Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index 8fbb2abe1e..4f650f8561 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/defibrillators.dmi b/icons/obj/defibrillators.dmi new file mode 100644 index 0000000000..f838f40030 Binary files /dev/null and b/icons/obj/defibrillators.dmi differ diff --git a/icons/obj/food/soupsalad.dmi b/icons/obj/food/soupsalad.dmi index ca9c150480..c4d7c184b6 100644 Binary files a/icons/obj/food/soupsalad.dmi and b/icons/obj/food/soupsalad.dmi differ diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi index c5aca2394f..9b73fa706b 100644 Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ diff --git a/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm b/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm index 17c3f17ad8..0457b853b7 100644 --- a/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm +++ b/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm @@ -1,16 +1,3 @@ -/mob/living/carbon/adjustStaminaLossBuffered(amount, updating_health = 1) - if(status_flags & GODMODE) - return 0 - if(CONFIG_GET(flag/disable_stambuffer)) - return - var/directstamloss = (bufferedstam + amount) - stambuffer - if(directstamloss > 0) - adjustStaminaLoss(directstamloss) - bufferedstam = clamp(bufferedstam + amount, 0, stambuffer) - stambufferregentime = world.time + 10 - if(updating_health) - update_health_hud() - /mob/living/carbon/adjustStaminaLoss(amount, updating_health = TRUE, forced = FALSE, affected_zone = BODY_ZONE_CHEST) if(!forced && (status_flags & GODMODE)) return FALSE diff --git a/modular_citadel/code/modules/mob/living/damage_procs.dm b/modular_citadel/code/modules/mob/living/damage_procs.dm deleted file mode 100644 index a399c17c71..0000000000 --- a/modular_citadel/code/modules/mob/living/damage_procs.dm +++ /dev/null @@ -1,2 +0,0 @@ -/mob/living/proc/adjustStaminaLossBuffered(amount, updating_health = TRUE, forced = FALSE) - return diff --git a/modular_citadel/code/modules/mob/living/living.dm b/modular_citadel/code/modules/mob/living/living.dm index 01867e9dcc..b5be028607 100644 --- a/modular_citadel/code/modules/mob/living/living.dm +++ b/modular_citadel/code/modules/mob/living/living.dm @@ -1,5 +1,3 @@ -/mob/living - /atom var/pseudo_z_axis @@ -24,26 +22,3 @@ if(.) pseudo_z_axis = newloc.get_fake_z() pixel_z = pseudo_z_axis - -/mob/living/carbon/update_stamina() - var/total_health = getStaminaLoss() - if(total_health >= STAMINA_SOFTCRIT) - if(!(combat_flags & COMBAT_FLAG_SOFT_STAMCRIT)) - ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_SOFT_STAMCRIT) - else - if(combat_flags & COMBAT_FLAG_SOFT_STAMCRIT) - DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_SOFT_STAMCRIT) - if(total_health) - if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health >= STAMINA_CRIT && !stat) - to_chat(src, "You're too exhausted to keep going...") - set_resting(TRUE, FALSE, FALSE) - SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE) - ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT) - filters += CIT_FILTER_STAMINACRIT - update_mobility() - if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_SOFTCRIT) - to_chat(src, "You don't feel nearly as exhausted anymore.") - DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT | COMBAT_FLAG_SOFT_STAMCRIT) - filters -= CIT_FILTER_STAMINACRIT - update_mobility() - update_health_hud() diff --git a/modular_citadel/icons/ui/screen_gen.dmi b/modular_citadel/icons/ui/screen_gen.dmi index d006185a3c..510d312f1c 100644 Binary files a/modular_citadel/icons/ui/screen_gen.dmi and b/modular_citadel/icons/ui/screen_gen.dmi differ diff --git a/strings/names/spider_first.txt b/strings/names/spider_first.txt new file mode 100644 index 0000000000..06fbae057c --- /dev/null +++ b/strings/names/spider_first.txt @@ -0,0 +1,30 @@ +Zerzir +Lalnuth +Avisreb +Qeqarnai +Rhikkiezhith +Necaqtex +Aqi +Shozhish +Sraiza +Ranqu +Zellalshi +Necoq +Zaqod +Salaree +Xavis +Cheqirni +Nieqi +Cakirkix +Ivad +Shenqazhe +Azurte +Lizire +Ivur +Kavur +Raicheca +Iqashe +Eq'za +Sak'sad +Hiezih +Cessix \ No newline at end of file diff --git a/strings/names/spider_last.txt b/strings/names/spider_last.txt new file mode 100644 index 0000000000..2b663f6df3 --- /dev/null +++ b/strings/names/spider_last.txt @@ -0,0 +1,30 @@ +Ik'sir +Sechathi +Qok'sut +Yeqied +Iravhoh +Kriaqux +Yikih +Khaqa +Azasnet +Qhecid +Qhin'qu +Zhechikzor +Qhovi +Hirath +Szornud +Zasokaq +Lhaqish +Qhiretid +Avizad +Qallazi +Qhizrud +Qicirne +Sezuveth +Zelriker +Rhiallor +Zhaliesh +Qoutirk'ab +Chavi +Riel'shes +Khentax \ No newline at end of file diff --git a/tgstation.dme b/tgstation.dme index c27d68e7bc..14a6a7cd10 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -108,6 +108,7 @@ #include "code\__DEFINES\sight.dm" #include "code\__DEFINES\sound.dm" #include "code\__DEFINES\spaceman_dmm.dm" +#include "code\__DEFINES\species.dm" #include "code\__DEFINES\stat.dm" #include "code\__DEFINES\stat_tracking.dm" #include "code\__DEFINES\status_effects.dm" @@ -138,6 +139,7 @@ #include "code\__DEFINES\combat\attack_types.dm" #include "code\__DEFINES\combat\block.dm" #include "code\__DEFINES\combat\block_parry.dm" +#include "code\__DEFINES\combat\stamina_combat.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" @@ -292,6 +294,7 @@ #include "code\controllers\configuration\entries\plushies.dm" #include "code\controllers\configuration\entries\policy.dm" #include "code\controllers\configuration\entries\resources.dm" +#include "code\controllers\configuration\entries\stamina_combat.dm" #include "code\controllers\subsystem\acid.dm" #include "code\controllers\subsystem\adjacent_air.dm" #include "code\controllers\subsystem\air.dm" @@ -326,6 +329,7 @@ #include "code\controllers\subsystem\materials.dm" #include "code\controllers\subsystem\medals.dm" #include "code\controllers\subsystem\minimaps.dm" +#include "code\controllers\subsystem\minimum_spawns.dm" #include "code\controllers\subsystem\minor_mapping.dm" #include "code\controllers\subsystem\mobs.dm" #include "code\controllers\subsystem\nightshift.dm" @@ -947,6 +951,7 @@ #include "code\game\objects\structures.dm" #include "code\game\objects\effects\alien_acid.dm" #include "code\game\objects\effects\anomalies.dm" +#include "code\game\objects\effects\arachnid_web.dm" #include "code\game\objects\effects\blessing.dm" #include "code\game\objects\effects\bump_teleporter.dm" #include "code\game\objects\effects\contraband.dm" @@ -2281,7 +2286,6 @@ #include "code\modules\jobs\job_types\station_engineer.dm" #include "code\modules\jobs\job_types\virologist.dm" #include "code\modules\jobs\job_types\warden.dm" -#include "code\modules\jobs\map_changes\map_changes.dm" #include "code\modules\keybindings\bindings_atom.dm" #include "code\modules\keybindings\bindings_client.dm" #include "code\modules\keybindings\focus.dm" @@ -2300,6 +2304,7 @@ #include "code\modules\keybindings\keybind\robot.dm" #include "code\modules\keybindings\keybind\targeting.dm" #include "code\modules\language\aphasia.dm" +#include "code\modules\language\arachnid.dm" #include "code\modules\language\beachbum.dm" #include "code\modules\language\codespeak.dm" #include "code\modules\language\common.dm" @@ -2418,6 +2423,7 @@ #include "code\modules\mob\dead\new_player\preferences_setup.dm" #include "code\modules\mob\dead\new_player\sprite_accessories\_sprite_accessories.dm" #include "code\modules\mob\dead\new_player\sprite_accessories\alienpeople.dm" +#include "code\modules\mob\dead\new_player\sprite_accessories\arachnid.dm" #include "code\modules\mob\dead\new_player\sprite_accessories\body_markings.dm" #include "code\modules\mob\dead\new_player\sprite_accessories\caps.dm" #include "code\modules\mob\dead\new_player\sprite_accessories\Citadel_Snowflake.dm" @@ -2463,6 +2469,7 @@ #include "code\modules\mob\living\login.dm" #include "code\modules\mob\living\logout.dm" #include "code\modules\mob\living\say.dm" +#include "code\modules\mob\living\stamina_buffer.dm" #include "code\modules\mob\living\status_procs.dm" #include "code\modules\mob\living\taste.dm" #include "code\modules\mob\living\update_icons.dm" @@ -2545,9 +2552,11 @@ #include "code\modules\mob\living\carbon\human\status_procs.dm" #include "code\modules\mob\living\carbon\human\typing_indicator.dm" #include "code\modules\mob\living\carbon\human\update_icons.dm" -#include "code\modules\mob\living\carbon\human\species_types\abductors.dm" +#include "code\modules\mob\living\carbon\human\species_types\abductor.dm" #include "code\modules\mob\living\carbon\human\species_types\android.dm" #include "code\modules\mob\living\carbon\human\species_types\angel.dm" +#include "code\modules\mob\living\carbon\human\species_types\anthromorph.dm" +#include "code\modules\mob\living\carbon\human\species_types\arachnid.dm" #include "code\modules\mob\living\carbon\human\species_types\bugmen.dm" #include "code\modules\mob\living\carbon\human\species_types\corporate.dm" #include "code\modules\mob\living\carbon\human\species_types\dullahan.dm" @@ -2555,7 +2564,6 @@ #include "code\modules\mob\living\carbon\human\species_types\ethereal.dm" #include "code\modules\mob\living\carbon\human\species_types\felinid.dm" #include "code\modules\mob\living\carbon\human\species_types\flypeople.dm" -#include "code\modules\mob\living\carbon\human\species_types\furrypeople.dm" #include "code\modules\mob\living\carbon\human\species_types\golems.dm" #include "code\modules\mob\living\carbon\human\species_types\humans.dm" #include "code\modules\mob\living\carbon\human\species_types\ipc.dm" @@ -3602,7 +3610,6 @@ #include "modular_citadel\code\modules\mentor\mentorpm.dm" #include "modular_citadel\code\modules\mentor\mentorsay.dm" #include "modular_citadel\code\modules\mob\cit_emotes.dm" -#include "modular_citadel\code\modules\mob\living\damage_procs.dm" #include "modular_citadel\code\modules\mob\living\living.dm" #include "modular_citadel\code\modules\mob\living\carbon\carbon.dm" #include "modular_citadel\code\modules\mob\living\carbon\damage_procs.dm" diff --git a/tools/travis/check_filedirs.sh b/tools/travis/check_filedirs.sh index 30df015763..b7bac4b7a2 100755 --- a/tools/travis/check_filedirs.sh +++ b/tools/travis/check_filedirs.sh @@ -1,15 +1,15 @@ + #!/bin/bash if [ -n "$1" ] then dme=$1 else - echo "Specify a DME to check" + echo "ERROR: Specify a DME to check" exit 1 fi if [[ $(awk '/BEGIN_FILE_DIR/{flag=1;next}/END_FILE_DIR/{flag=0}flag' $dme | wc -l) -ne 1 ]] then - echo "File DIR was ticked, please untick it, see: https://tgstation13.org/phpBB/viewtopic.php?f=5&t=321 for more" + echo "ERROR: File DIR was ticked, please untick it, see: https://tgstation13.org/phpBB/viewtopic.php?f=5&t=321 for more" exit 1 fi - diff --git a/tools/travis/check_grep.sh b/tools/travis/check_grep.sh index 2117b570eb..b85f775eb9 100755 --- a/tools/travis/check_grep.sh +++ b/tools/travis/check_grep.sh @@ -7,43 +7,70 @@ shopt -s globstar st=0 if grep -El '^\".+\" = \(.+\)' _maps/**/*.dmm; then - echo "Non-TGM formatted map detected. Please convert it using Map Merger!" + echo "ERROR: Non-TGM formatted map detected. Please convert it using Map Merger!" st=1 fi; if grep -P '^\ttag = \"icon' _maps/**/*.dmm; then - echo "tag vars from icon state generation detected in maps, please remove them." + echo "ERROR: tag vars from icon state generation detected in maps, please remove them." st=1 fi; -if grep 'step_[xy]' _maps/**/*.dmm; then - echo "step_x/step_y variables detected in maps, please remove them." +if grep -P 'step_[xy]' _maps/**/*.dmm; then + echo "ERROR: step_x/step_y variables detected in maps, please remove them." st=1 fi; -if grep 'pixel_[xy] = 0' _maps/**/*.dmm; then - echo "pixel_x/pixel_y = 0 variables detected in maps, please review to ensure they are not dirty varedits." +if grep -P 'pixel_[^xy]' _maps/**/*.dmm; then + echo "ERROR: incorrect pixel offset variables detected in maps, please remove them." + st=1 fi; if grep -P '\td[1-2] =' _maps/**/*.dmm; then - echo "d1/d2 cable variables detected in maps, please remove them." + echo "ERROR: d1/d2 cable variables detected in maps, please remove them." st=1 fi; -if grep '^/area/.+[\{]' _maps/**/*.dmm; then - echo "Vareditted /area path use detected in maps, please replace with proper paths." +if grep -P '^/area/.+[\{]' _maps/**/*.dmm; then + echo "WARNING: Vareditted /area path use detected in maps, please replace with proper paths." + #st=1 +fi; +if grep -P '\W\/turf\s*[,\){]' _maps/**/*.dmm; then + echo "ERROR: base /turf path use detected in maps, please replace with proper paths." st=1 fi; -if grep '\W\/turf\s*[,\){]' _maps/**/*.dmm; then - echo "base /turf path use detected in maps, please replace with proper paths." - st=1 -fi; -if grep '^/*var/' code/**/*.dm; then - echo "Unmanaged global var use detected in code, please use the helpers." +if grep -P '^/*var/' code/**/*.dm; then + echo "ERROR: Unmanaged global var use detected in code, please use the helpers." st=1 fi; +nl=' +' +nl=$'\n' +while read f; do + t=$(tail -c2 "$f"; printf x); r1="${nl}$"; r2="${nl}${r1}" + if [[ ! ${t%x} =~ $r1 ]]; then + echo "file $f is missing a trailing newline" + #st=1 + fi; +done < <(find . -type f -name '*.dm') if grep -i 'centcomm' code/**/*.dm; then - echo "Misspelling(s) of CENTCOM detected in code, please remove the extra M(s)." + echo "ERROR: Misspelling(s) of CENTCOM detected in code, please remove the extra M(s)." st=1 fi; if grep -i 'centcomm' _maps/**/*.dmm; then - echo "Misspelling(s) of CENTCOM detected in maps, please remove the extra M(s)." + echo "ERROR: Misspelling(s) of CENTCOM detected in maps, please remove the extra M(s)." st=1 fi; +if ls _maps/*.json | grep -P "[A-Z]"; then + echo "Uppercase in a map json detected, these must be all lowercase." + st=1 +fi; +for json in _maps/*.json +do + map_path=$(jq -r '.map_path' $json) + while read map_file; do + filename="_maps/$map_path/$map_file" + if [ ! -f $filename ] + then + echo "found invalid file reference to $filename in _maps/$json" + st=1 + fi + done < <(jq -r '[.map_file] | flatten | .[]' $json) +done exit $st diff --git a/tools/travis/install_dreamchecker.sh b/tools/travis/install_dreamchecker.sh deleted file mode 100755 index 4423e5aafb..0000000000 --- a/tools/travis/install_dreamchecker.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -euo pipefail - -source dependencies.sh - -wget -O ~/dreamchecker "https://github.com/SpaceManiac/SpacemanDMM/releases/download/$SPACEMAN_DMM_VERSION/dreamchecker" -chmod +x ~/dreamchecker -~/dreamchecker --version diff --git a/tools/travis/install_spaceman_dmm.sh b/tools/travis/install_spaceman_dmm.sh new file mode 100755 index 0000000000..39464193f8 --- /dev/null +++ b/tools/travis/install_spaceman_dmm.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -euo pipefail + +source dependencies.sh + +wget -O ~/$1 "https://github.com/SpaceManiac/SpacemanDMM/releases/download/$SPACEMAN_DMM_VERSION/$1" +chmod +x ~/$1 +~/$1 --version diff --git a/tools/travis/run_server.sh b/tools/travis/run_server.sh index 975e8f6884..04c491cd5c 100755 --- a/tools/travis/run_server.sh +++ b/tools/travis/run_server.sh @@ -2,6 +2,7 @@ set -euo pipefail tools/deploy.sh travis_test +rm travis_test/*.dll mkdir travis_test/config #test config