Merge remote-tracking branch 'Aurora/master' into beach_episode

This commit is contained in:
Matt Atlas
2026-06-07 22:53:32 +02:00
1207 changed files with 35281 additions and 58920 deletions
+17
View File
@@ -0,0 +1,17 @@
# This action is a wrapper around `oven-sh/setup-bun` to use the version specified in
# `dependencies.sh`.
name: Setup Bun
description: Install Bun using the version specified in `dependencies.sh`
runs:
using: composite
steps:
- name: Configure Bun version
shell: bash
run: |
source dependencies.sh
echo "BUN_VERSION_REQUIRED=$BUN_VERSION" >> $GITHUB_ENV
- name: Install Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION_REQUIRED }}
+26
View File
@@ -0,0 +1,26 @@
# This action is a wrapper around `actions/setup-node`, to use the version specified in
# `dependencies.sh`.
name: Setup Node
description: Install Node using the version specified in `dependencies.sh`; additionally, restores the Yarn cache if one exists
inputs:
restore-yarn-cache:
description: "If `true`, restores the Yarn cache alongside installing node."
required: false
type: boolean
default: false
runs:
using: composite
steps:
- name: Configure Node version
shell: bash
run: |
source dependencies.sh
echo "NODE_VERSION_REQUIRED=$NODE_VERSION_LTS" >> $GITHUB_ENV
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION_REQUIRED }}
cache: ${{ fromJSON(inputs.restore-yarn-cache) && 'yarn' || '' }}
cache-dependency-path: ${{ fromJSON(inputs.restore-yarn-cache) && 'tgui/yarn.lock' || '' }}
+5 -8
View File
@@ -69,6 +69,7 @@ jobs:
bash tools/ci/check_filedirs.sh aurorastation.dme
bash tools/ci/check_changelogs.sh
bash tools/ci/check_grep.sh $PWD
bash tools/ci/check_shell_scripts.sh
awk -f tools/indentation.awk **/*.dm
- name: "Check if running CI"
@@ -281,18 +282,14 @@ jobs:
- name: Set ENV variables
run: bash dependencies.sh
#Setup NodeJS using YARN as cache
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION_LTS }}
cache: "yarn"
cache-dependency-path: tgui/yarn.lock
#Setup bun
- name: Setup Bun
uses: ./.github/actions/setup_bun
#Lint TGUI
- name: Check Tgui
run: |
tools/build/build --ci lint tgui-test
tools/build/build.sh --ci lint tgui-test
###########################################
############### MISC LINTING ##############
+31
View File
@@ -0,0 +1,31 @@
name: "Build TGUI"
on:
workflow_dispatch:
jobs:
update-tgui:
concurrency: tgui
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-24.04
steps:
- name: Install Bun
uses: ./.github/actions/setup_bun
- name: Update TGUI
run: |
tools/bootstrap/javascript tools/build/build.sh tgui
- name: Commit TGUI
run: |
git pull origin master
git config --local user.email "action@github.com"
git config --local user.name "AuroraBuildBot"
git add --force tgui/public/*
git commit -m "Automatic TGUI compile [ci skip]" -a || true
- name: Push
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.BOT_TOKEN_AURORA }}
+3
View File
@@ -73,3 +73,6 @@ define_sanity_output.txt
# Tracy dumps
*.utracy
# JavaScript tools
**/node_modules
+4 -2
View File
@@ -6,7 +6,9 @@
"dbaeumer.vscode-eslint",
"vscode.typescript-language-features",
"redhat.vscode-yaml",
"esbenp.prettier-vscode",
"rome.rome"
"rome.rome",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"biomejs.biome"
]
}
+10 -8
View File
@@ -1,25 +1,27 @@
{
"eslint.nodePath": "./tgui/.yarn/sdks",
"eslint.workingDirectories": ["./tgui"],
"prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.cjs",
"typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
"source.organizeImports.biome": "explicit",
"source.fixAll.biome": "explicit"
},
"editor.insertSpaces": false,
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"gitlens.advanced.blame.customArguments": ["-w"],
"[javascript][typescript][typescriptreact][javascriptreact][html][scss][css][json][jsonc][markdown][yaml]": {
"editor.rulers": [80],
"[javascript][typescript][javascriptreact][typescriptreact][css][json][jsonc]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.rulers": [80]
},
"[yaml][markdown][html][scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
"editor.formatOnSave": true,
"editor.rulers": [80]
},
"workbench.editorAssociations": {
"*.dmi": "dmiEditor.dmiEditor"
+4 -34
View File
@@ -16,8 +16,7 @@
},
"problemMatcher": [
"$dreammaker",
"$tsc",
"$eslint-stylish"
"$tsc"
],
"group": {
"kind": "build",
@@ -61,8 +60,7 @@
"command": ".\\bin\\tgui-build.cmd"
},
"problemMatcher": [
"$tsc",
"$eslint-stylish"
"$tsc"
],
"group": "build",
"label": "tgui: build"
@@ -74,38 +72,11 @@
"command": ".\\bin\\tgui-dev.cmd"
},
"problemMatcher": [
"$tsc",
"$eslint-stylish"
"$tsc"
],
"group": "build",
"label": "tgui: dev server"
},
{
"type": "shell",
"command": "bin/tgui-bench",
"windows": {
"command": ".\\bin\\tgui-bench.cmd"
},
"problemMatcher": [
"$tsc",
"$eslint-stylish"
],
"group": "build",
"label": "tgui: bench"
},
{
"type": "shell",
"command": "bin/tgui-sonar",
"windows": {
"command": ".\\bin\\tgui-sonar.cmd"
},
"problemMatcher": [
"$tsc",
"$eslint-stylish"
],
"group": "build",
"label": "tgui: sonar"
},
{
"type": "shell",
"command": "bin/tgfont",
@@ -113,8 +84,7 @@
"command": ".\\bin\\tgfont.cmd"
},
"problemMatcher": [
"$tsc",
"$eslint-stylish"
"$tsc"
],
"group": "build",
"label": "tgui: rebuild tgfont"
+10 -9
View File
@@ -155,6 +155,7 @@
#include "code\__DEFINES\targeting.dm"
#include "code\__DEFINES\technomancer.dm"
#include "code\__DEFINES\text.dm"
#include "code\__DEFINES\tgs.config.dm"
#include "code\__DEFINES\tgs.dm"
#include "code\__DEFINES\tgui.dm"
#include "code\__DEFINES\time.dm"
@@ -184,6 +185,7 @@
#include "code\__DEFINES\dcs\signals\signals_record.dm"
#include "code\__DEFINES\dcs\signals\signals_spatial_grid.dm"
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
#include "code\__DEFINES\dcs\signals\signals_tgui.dm"
#include "code\__DEFINES\dcs\signals\signals_tools.dm"
#include "code\__DEFINES\dcs\signals\signals_turf.dm"
#include "code\__DEFINES\dcs\signals\signals_weather.dm"
@@ -404,6 +406,7 @@
#include "code\controllers\subsystems\stickyban.dm"
#include "code\controllers\subsystems\sun.dm"
#include "code\controllers\subsystems\sunlight.dm"
#include "code\controllers\subsystems\tgui.dm"
#include "code\controllers\subsystems\throwing.dm"
#include "code\controllers\subsystems\ticker.dm"
#include "code\controllers\subsystems\time_track.dm"
@@ -448,7 +451,6 @@
#include "code\controllers\subsystems\processing\electronics.dm"
#include "code\controllers\subsystems\processing\fast_process.dm"
#include "code\controllers\subsystems\processing\modifiers.dm"
#include "code\controllers\subsystems\processing\nanoui.dm"
#include "code\controllers\subsystems\processing\ntsl2.dm"
#include "code\controllers\subsystems\processing\obj_tab_items.dm"
#include "code\controllers\subsystems\processing\odyssey.dm"
@@ -457,7 +459,6 @@
#include "code\controllers\subsystems\processing\projectiles.dm"
#include "code\controllers\subsystems\processing\psi.dm"
#include "code\controllers\subsystems\processing\shuttle.dm"
#include "code\controllers\subsystems\processing\tgui.dm"
#include "code\datums\ai_law_sets.dm"
#include "code\datums\ai_laws.dm"
#include "code\datums\beam.dm"
@@ -465,6 +466,7 @@
#include "code\datums\callback.dm"
#include "code\datums\cargo.dm"
#include "code\datums\category.dm"
#include "code\datums\chat_payload.dm"
#include "code\datums\computerfiles.dm"
#include "code\datums\datum.dm"
#include "code\datums\datumvars.dm"
@@ -616,6 +618,7 @@
#include "code\datums\outfits\event\outfit_killers.dm"
#include "code\datums\outfits\event\outfit_megacorps.dm"
#include "code\datums\outfits\event\outfit_nanotrasen.dm"
#include "code\datums\outfits\event\outfit_oe.dm"
#include "code\datums\outfits\event\outfit_scc.dm"
#include "code\datums\outfits\event\outfit_siib.dm"
#include "code\datums\outfits\event\outfit_tfcl.dm"
@@ -3166,13 +3169,6 @@
#include "code\modules\multiz\zmimic\mimic_common.dm"
#include "code\modules\multiz\zmimic\mimic_movable.dm"
#include "code\modules\multiz\zmimic\mimic_turf.dm"
#include "code\modules\nano\nanoexternal.dm"
#include "code\modules\nano\nanomapgen.dm"
#include "code\modules\nano\nanoui.dm"
#include "code\modules\nano\modules\follow_menu.dm"
#include "code\modules\nano\modules\human_appearance.dm"
#include "code\modules\nano\modules\nano_module.dm"
#include "code\modules\nano\modules\narrate_panel.dm"
#include "code\modules\ntsl2\guide.dm"
#include "code\modules\ntsl2\ntsl2_program.dm"
#include "code\modules\ntsl2\ntsl2_types.dm"
@@ -3935,8 +3931,11 @@
#include "code\modules\tgui\modules\armor_values.dm"
#include "code\modules\tgui\modules\faction_select.dm"
#include "code\modules\tgui\modules\flavor_text.dm"
#include "code\modules\tgui\modules\follow_menu.dm"
#include "code\modules\tgui\modules\hivenet_manifest.dm"
#include "code\modules\tgui\modules\human_appearance.dm"
#include "code\modules\tgui\modules\ipc_diagnostic.dm"
#include "code\modules\tgui\modules\narrate_panel.dm"
#include "code\modules\tgui\modules\neural_configuration.dm"
#include "code\modules\tgui\states\admin.dm"
#include "code\modules\tgui\states\always.dm"
@@ -3959,6 +3958,8 @@
#include "code\modules\tgui\states\self.dm"
#include "code\modules\tgui\states\zlevel.dm"
#include "code\modules\tgui_input\alert.dm"
#include "code\modules\tgui_input\checkboxes.dm"
#include "code\modules\tgui_input\keycombo.dm"
#include "code\modules\tgui_input\list.dm"
#include "code\modules\tgui_input\number.dm"
#include "code\modules\tgui_input\text.dm"
+75
View File
@@ -0,0 +1,75 @@
{
"$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
},
"enabled": true
},
"css": {
"formatter": {
"quoteStyle": "double"
}
},
"files": {
"ignoreUnknown": false,
"includes": [
"**",
"!**/node_modules",
"!tgui/public",
"tgui/public/tgui.html",
"!tgui/packages/tgui-setup/helpers.js"
]
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"lineEnding": "lf"
},
"javascript": {
"formatter": {
"indentStyle": "space",
"quoteStyle": "single"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"a11y": "off",
"correctness": {
"noUnusedImports": "warn",
"noUnusedVariables": "off",
"useExhaustiveDependencies": "off",
"noUnusedFunctionParameters": "off",
"useHookAtTopLevel": "off"
},
"security": {
"noDangerouslySetInnerHtml": "off"
},
"style": {
"noNonNullAssertion": "off",
"useSelfClosingElements": "error",
"useTemplate": "off",
"useNodejsImportProtocol": "off"
},
"suspicious": {
"noArrayIndexKey": "off",
"noExplicitAny": "off",
"noImplicitAnyLet": "off",
"noShadowRestrictedNames": "off"
},
"complexity": {
"noImportantStyles": "off",
"useOptionalChain": "off"
}
}
},
"vcs": {
"clientKind": "git",
"enabled": false,
"useIgnoreFile": false
}
}
+32
View File
@@ -0,0 +1,32 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"devDependencies": {
"@biomejs/biome": "^2.1.2",
"prettier": "^3.6.2",
},
},
},
"packages": {
"@biomejs/biome": ["@biomejs/biome@2.4.15", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.15", "@biomejs/cli-darwin-x64": "2.4.15", "@biomejs/cli-linux-arm64": "2.4.15", "@biomejs/cli-linux-arm64-musl": "2.4.15", "@biomejs/cli-linux-x64": "2.4.15", "@biomejs/cli-linux-x64-musl": "2.4.15", "@biomejs/cli-win32-arm64": "2.4.15", "@biomejs/cli-win32-x64": "2.4.15" }, "bin": { "biome": "bin/biome" } }, "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw=="],
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg=="],
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ=="],
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug=="],
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ=="],
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g=="],
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w=="],
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w=="],
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.15", "", { "os": "win32", "cpu": "x64" }, "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ=="],
"prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="],
}
}
-6
View File
@@ -2,7 +2,6 @@
Contains helper procs for airflow, handled in /connection_group.
*/
/mob/var/tmp/last_airflow_stun = 0
/mob/proc/airflow_stun()
if(stat == 2)
return 0
@@ -60,11 +59,6 @@ Contains helper procs for airflow, handled in /connection_group.
if(4,5)
if(n < GLOB.vsc.airflow_medium_pressure) return 0
/atom/movable/var/tmp/turf/airflow_dest
/atom/movable/var/tmp/airflow_speed = 0
/atom/movable/var/tmp/airflow_time = 0
/atom/movable/var/tmp/last_airflow = 0
/atom/movable/proc/AirflowCanMove(n)
return 1
-2
View File
@@ -20,8 +20,6 @@ GLOBAL_DATUM_INIT(contamination_overlay, /image, image('icons/effects/contaminat
///Does being in sleeping gas cause you to hallucinate?
var/N2O_HALLUCINATION = TRUE
/obj/var/contaminated = 0
/obj/item/proc/can_contaminate()
if(item_flags & ITEM_FLAG_PHORON_GUARD)
return FALSE
+2
View File
@@ -56,6 +56,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define PASSDOORHATCH (1<<15)
#define PASSTRACE (1<<16) //Used by turrets in the check_trajectory proc to target mobs hiding behind certain things (such as closets)
#define PASSRAILING (1<<17)
/// Allows you to pass over energy shields, these also have additional handling for blocking projectiles in their on_hit().
#define PASSSHIELD (1<<18)
//Movement Types
#define GROUND (1<<0)
+21
View File
@@ -32,3 +32,24 @@
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
#define NICE_SHOT_RICOCHET_BONUS 10 //if the shooter has the NICE_SHOT trait and they fire a ricocheting projectile, add this to the ricochet chance and auto aim angle
//attack visual effects
#define ATTACK_EFFECT_PUNCH "punch"
#define ATTACK_EFFECT_KICK "kick"
#define ATTACK_EFFECT_SMASH "smash"
#define ATTACK_EFFECT_CLAW "claw"
#define ATTACK_EFFECT_SLASH "slash"
#define ATTACK_EFFECT_DISARM "disarm"
#define ATTACK_EFFECT_BITE "bite"
#define ATTACK_EFFECT_MECHFIRE "mech_fire"
#define ATTACK_EFFECT_MECHTOXIN "mech_toxin"
#define ATTACK_EFFECT_BOOP "boop" //Honk
#define ATTACK_EFFECT_VOID "void"
#define ATTACK_EFFECT_CPR "cpr"
/// Attack animation for sharp items
#define ATTACK_ANIMATION_SLASH "slash"
/// Attack animation for pointy items
#define ATTACK_ANIMATION_PIERCE "pierce"
/// Animation for blunt attacks
#define ATTACK_ANIMATION_BLUNT "blunt"
@@ -9,6 +9,8 @@
#define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON "atom_init_success_on"
///from base of atom/examine(): (/mob, list/examine_text)
#define COMSIG_ATOM_EXAMINE "atom_examine"
///from base of atom/examine_tags(): (/mob, list/examine_tags)
#define COMSIG_ATOM_EXAMINE_TAGS "atom_examine_tags"
///from base of atom/Entered(): (atom/movable/arrived, atom/old_loc, list/atom/old_locs)
#define COMSIG_ATOM_ENTERED "atom_entered"
/// Sent from the atom that just Entered src. From base of atom/Entered(): (/atom/destination, atom/old_loc, list/atom/old_locs)
@@ -46,6 +46,9 @@
/// Sent when a mob rests.
#define COMSIG_MOB_RESTED "mob_rested"
/// Sent from /mob/proc/update_canmove() when the mob transitions into lying down.
#define COMSIG_MOB_LYING_DOWN "mob_lying_down"
/// From /obj/item/organ/external/take_damage. Updates the limb's colour matrix. Very laggy, so we do it on reaction to stuff.
#define COMSIG_UPDATE_LIMB_IMAGE "update_limb_image"
@@ -24,6 +24,8 @@
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self"
//from base of obj/item/attack_self_secondary(): (/mob)
#define COMSIG_ITEM_ATTACK_SELF_SECONDARY "item_attack_self_secondary"
/// Sent from /obj/item/proc/animate_attack() : (atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override, list/angle_override)
#define COMSIG_ITEM_ATTACK_ANIMATION "item_attack_animation"
///from base of [obj/item/attack()]: (atom/target, mob/user, proximity_flag, click_parameters)
#define COMSIG_ITEM_AFTERATTACK "item_afterattack"
@@ -0,0 +1,2 @@
/// Window is fully visible and we can make fragile calls
#define COMSIG_TGUI_WINDOW_VISIBLE "tgui_window_visible"
-2
View File
@@ -51,8 +51,6 @@
#define LEFT 1
#define RIGHT 2
#define FIST_ATTACK_ANIMATION -1
// Pulse levels, very simplified.
#define PULSE_NONE 0 // So !M.pulse checks would be possible.
#define PULSE_SLOW 1 // <60 bpm
+2 -2
View File
@@ -8,9 +8,9 @@
///The maximum improvement that can be applied to a weapon component.
#define IMPROVEMENT_CAP 100
///The maximum increase an individual variable can recieve over it's initial value.
#define INCREASE_CAP 2
#define INCREASE_CAP 1.5
///The maximum decrease an individual variable can recieve under it's initial value.
#define DECREASE_CAP 0.2
#define DECREASE_CAP 0.5
///All improvements are multiplied by this value, tweak down if they are too strong, up if they are too weak.
#define IMPROVEMENT_MULTIPLIER 1
+2 -2
View File
@@ -49,8 +49,8 @@
*/
#define COMSIG_GRAVITY_WEAKNESS_EVENT "gravity_weakness_event"
/// Raised external organ (a limb) takes damage. Used for the synthetic endoskeleton at the moment. Must supply damage.
#define COMSIG_EXTERNAL_ORGAN_DAMAGE "machine_internal_damage"
/// This is raised when you want to send damage to the synthetic endoskeleton. Must supply a damage number.
#define COMSIG_DAMAGE_TO_ENDOSKELETON "machine_internal_damage"
/// Sent when the burst damage is cleared by the posibrain.
#define COMSIG_SYNTH_EMP_DAMAGE_CLEARED "emp_damage_cleared"
+1
View File
@@ -23,6 +23,7 @@
#define SHIP_AMMO_IMPACT_HE "high explosive"
#define SHIP_AMMO_IMPACT_PROBE "sensor probe"
#define SHIP_AMMO_IMPACT_FMJ "full metal jacket"
#define SHIP_AMMO_IMPACT_FRAG "fragmentation"
#define SHIP_AMMO_IMPACT_AP "armour-piercing"
#define SHIP_AMMO_IMPACT_LASER "laser"
#define SHIP_AMMO_IMPACT_BUNKERBUSTER "bunker-buster"
+4
View File
@@ -24,6 +24,7 @@
#define SPAN_COLOR(color, str) SPAN_STYLE("color: [color]", "[str]")
#define SPAN_CAUTION(str) ("<span class='caution'>" + str + "</span>")
#define SPAN_STORYTELLER(str) ("<span class='storyteller'>" + str + "</span>")
#define SPAN_BOLDANNOUNCE(str) ("<span class='boldannounce'>" + str + "</span>")
#define SPAN_MACHINE_WARNING(str) ("<span class='machine-warning'>" + str + "</span>")
#define SPAN_MACHINE_DANGER(str) ("<span class='machine-danger'>" + str + "</span>")
@@ -42,6 +43,9 @@
#define SPAN_BOLDNOTICE(str) ("<span class='boldnotice'>" + str + "</span>")
#define SPAN_BOLDWARNING(X) "<span class='boldwarning'>[X]</span>"
// Spans that use embedded tgui components:
#define SPAN_TOOLTIP(tip, main_text) ("<span data-component=\"Tooltip\" data-content=\"" + tip + "\" class=\"tooltip\">" + main_text + "</span>")
/*
#####################
Font sizes
-1
View File
@@ -36,7 +36,6 @@
#define SS_PRIORITY_STATPANELS 25 // Statpanels.
#define SS_PRIORITY_LIGHTING 25 // Queued lighting engine updates.
#define SS_PRIORITY_MACHINERY 25 // Machinery + powernet ticks.
#define SS_PRIORITY_NANOUI 25 // UI updates.
#define SS_PRIORITY_ELECTRONICS 20 // Integrated Electronics processing.
#define SS_PRIORITY_CALAMITY 20 // Singularity, Tesla, Nar'sie, blob, etc.
#define SS_PRIORITY_EVENT 20
+1
View File
@@ -277,6 +277,7 @@
#define FIRE_PRIORITY_SPACEDRIFT 30
#define FIRE_PRIORITY_DEFAULT 50
#define FIRE_PRIORITY_MOBS 100
#define FIRE_PRIORITY_TGUI 110
#define FIRE_PRIORITY_STATPANEL 390
#define FIRE_PRIORITY_CHAT 400
#define FIRE_PRIORITY_RUNECHAT 410
+1 -1
View File
@@ -50,7 +50,7 @@
#define CHAT_MESSAGE_WIDTH 112
// Setting this much higher than 1024 could allow spammers to DOS the server easily.
#define MAX_MESSAGE_LEN 1024
#define MAX_MESSAGE_LEN 4096
#define MAX_PAPER_MESSAGE_LEN 3072
#define MAX_BOOK_MESSAGE_LEN 9216
#define MAX_LNAME_LEN 64
+11
View File
@@ -0,0 +1,11 @@
#define TGS_EXTERNAL_CONFIGURATION
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
#define TGS_READ_GLOBAL(Name) GLOB.##Name
#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
#define TGS_WORLD_ANNOUNCE(message) to_chat(world, SPAN_BOLDANNOUNCE("[html_encode(##message)]"))
#define TGS_INFO_LOG(message) log_world("TGS Info: [##message]")
#define TGS_WARNING_LOG(message) log_world("TGS Warn: [##message]")
#define TGS_ERROR_LOG(message) stack_trace("TGS Error: [##message]")
#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
#define TGS_CLIENT_COUNT GLOB.clients.len
#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path)
+132 -44
View File
@@ -1,55 +1,68 @@
// tgstation-server DMAPI
// The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119.
#define TGS_DMAPI_VERSION "6.6.2"
#define TGS_DMAPI_VERSION "7.3.3"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
// CONFIGURATION
/// Create this define if you want to do TGS configuration outside of this file.
/// Consumers SHOULD create this define if you want to do TGS configuration outside of this file.
#ifndef TGS_EXTERNAL_CONFIGURATION
// Comment this out once you've filled in the below.
//#error TGS API unconfigured
// Consumers MUST comment this out once you've filled in the below and are not using [TGS_EXTERNAL_CONFIGURATION].
// #error TGS API unconfigured
// Uncomment this if you wish to allow the game to interact with TGS 3..
// Consumers MUST uncomment this if you wish to allow the game to interact with TGS version 3.
// This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()().
//#define TGS_V3_API
// Required interfaces (fill in with your codebase equivalent):
/// Create a global variable named `Name` and set it to `Value`.
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) var/global/##Name = ##Value
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value)
/// Read the value in the global variable `Name`.
#define TGS_READ_GLOBAL(Name) global.##Name
#define TGS_READ_GLOBAL(Name)
/// Set the value in the global variable `Name` to `Value`.
#define TGS_WRITE_GLOBAL(Name, Value) global.##Name = ##Value
#define TGS_WRITE_GLOBAL(Name, Value)
/// Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game use of DD -> TGS capabilities.
#define TGS_PROTECT_DATUM(Path)
/// Display an announcement `message` from the server to all players.
#define TGS_WORLD_ANNOUNCE(message) world << "<b>TGS Notification</b>: [html_encode(##message)]"
#define TGS_WORLD_ANNOUNCE(message)
/// Notify current in-game administrators of a string `event`.
#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
#define TGS_NOTIFY_ADMINS(event)
/// Write an info `message` to a server log.
#define TGS_INFO_LOG(message) log_tgs("[##message]")
#define TGS_INFO_LOG(message)
/// Write an warning `message` to a server log.
#define TGS_WARNING_LOG(message) log_tgs("[##message]", SEVERITY_WARNING)
#define TGS_WARNING_LOG(message)
/// Write an error `message` to a server log.
#define TGS_ERROR_LOG(message) log_tgs("[##message]", SEVERITY_ERROR)
#define TGS_ERROR_LOG(message)
/// Get the number of connected /clients.
#define TGS_CLIENT_COUNT clients.len
#define TGS_CLIENT_COUNT
#endif
#ifndef TGS_FILE2TEXT_NATIVE
#ifdef file2text
#error Your codebase is re-defining the BYOND proc file2text. The DMAPI requires the native version to read the result of world.Export(). You SHOULD fix this by adding "#define TGS_FILE2TEXT_NATIVE file2text" before your override of file2text to allow the DMAPI to use the native version. This will only be used for world.Export(), not regular file accesses
#endif
#define TGS_FILE2TEXT_NATIVE file2text
#endif
// SpacemanDMM compatibility
#ifndef CAN_BE_REDEFINED
#define CAN_BE_REDEFINED(X)
#endif
// EVENT CODES
/// Before a reboot mode change, extras parameters are the current and new reboot mode enums.
@@ -73,12 +86,12 @@
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path.
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND.
#define TGS_EVENT_BYOND_INSTALL_START 5
/// When a BYOND install operation fails. Parameters: Error message
#define TGS_EVENT_BYOND_INSTALL_FAIL 6
/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND.
#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
/// Before a engine install operation begins. Parameters: Version string of the installing engine.
#define TGS_EVENT_ENGINE_INSTALL_START 5
/// When a engine install operation fails. Parameters: Error message
#define TGS_EVENT_ENGINE_INSTALL_FAIL 6
/// When the active engine version changes. Parameters: (Nullable) Version string of the current engine, version string of the new engine.
#define TGS_EVENT_ENGINE_ACTIVE_VERSION_CHANGE 7
/// When the compiler starts running. Parameters: Game directory path, origin commit SHA.
#define TGS_EVENT_COMPILE_START 8
/// When a compile is cancelled. No parameters.
@@ -108,7 +121,7 @@
// #define TGS_EVENT_DREAM_DAEMON_LAUNCH 22
/// After a single submodule update is performed. Parameters: Updated submodule name.
#define TGS_EVENT_REPO_SUBMODULE_UPDATE 23
/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, byond version.
/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, version string of the used engine.
#define TGS_EVENT_PRE_DREAM_MAKER 24
/// Whenever a deployment folder is deleted from disk. Parameters: Game directory path.
#define TGS_EVENT_DEPLOYMENT_CLEANUP 25
@@ -122,6 +135,7 @@
/// The watchdog will restart on reboot.
#define TGS_REBOOT_MODE_RESTART 2
// Note that security levels are currently meaningless in OpenDream
/// DreamDaemon Trusted security level.
#define TGS_SECURITY_TRUSTED 0
/// DreamDaemon Safe security level.
@@ -136,20 +150,26 @@
/// DreamDaemon invisible visibility level.
#define TGS_VISIBILITY_INVISIBLE 2
/// The Build Your Own Net Dream engine.
#define TGS_ENGINE_TYPE_BYOND 0
/// The OpenDream engine.
#define TGS_ENGINE_TYPE_OPENDREAM 1
//REQUIRED HOOKS
/**
* Call this somewhere in [/world/proc/New] that is always run. This function may sleep!
* Consumers MUST call this somewhere in [/world/proc/New] that is always run. This function may sleep!
*
* * event_handler - Optional user defined [/datum/tgs_event_handler].
* * minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated. Can be one of [TGS_SECURITY_ULTRASAFE], [TGS_SECURITY_SAFE], or [TGS_SECURITY_TRUSTED].
* * http_handler - Optional user defined [/datum/tgs_http_handler].
*/
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler)
CAN_BE_REDEFINED(TRUE)
return
/**
* Call this when your initializations are complete and your game is ready to play before any player interactions happen.
* Consumers MUST call this when world initializations are complete and the game is ready to play before any player interactions happen.
*
* This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running.
* Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184
@@ -159,12 +179,10 @@
CAN_BE_REDEFINED(TRUE)
return
/// Put this at the start of [/world/proc/Topic].
/// Consumers MUST run this macro at the start of [/world/proc/Topic].
#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
/**
* Call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
*/
/// Consumers MUST call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
/world/proc/TgsReboot()
CAN_BE_REDEFINED(TRUE)
return
@@ -261,7 +279,7 @@
/// The [/datum/tgs_chat_channel] the user was from.
var/datum/tgs_chat_channel/channel
/// User definable handler for TGS events.
/// User definable handler for TGS events This abstract version SHOULD be overridden to be used.
/datum/tgs_event_handler
/// If the handler receieves [TGS_EVENT_HEALTH_CHECK] events.
var/receive_health_checks = FALSE
@@ -275,7 +293,41 @@
set waitfor = FALSE
return
/// User definable chat command.
/// User definable handler for HTTP calls. This abstract version MUST be overridden to be used.
/datum/tgs_http_handler
/**
* User definable callback for executing HTTP GET requests.
* MUST perform BYOND sleeps while the request is in flight.
* MUST return a [/datum/tgs_http_result].
* SHOULD log its own errors
*
* url - The full URL to execute the GET request for including query parameters.
*/
/datum/tgs_http_handler/proc/PerformGet(url)
CRASH("[type]/PerformGet not implemented!")
/// Result of a [/datum/tgs_http_handler] call. MUST NOT be overridden.
/datum/tgs_http_result
/// HTTP response as text
var/response_text
/// Boolean request success flag. Set for any 2XX response code.
var/success
/**
* Create a [/datum/tgs_http_result].
*
* * response_text - HTTP response as text. Must be provided in New().
* * success - Boolean request success flag. Set for any 2XX response code. Must be provided in New().
*/
/datum/tgs_http_result/New(response_text, success)
if(response_text && !istext(response_text))
CRASH("response_text was not text!")
src.response_text = response_text
src.success = success
/// User definable chat command. This abstract version MUST be overridden to be used.
/datum/tgs_chat_command
/// The string to trigger this command on a chat bot. e.g `@bot name ...` or `!tgs name ...`.
var/name = ""
@@ -288,29 +340,36 @@
/**
* Process command activation. Should return a [/datum/tgs_message_content] to respond to the issuer with.
* MUST be implemented
*
* sender - The [/datum/tgs_chat_user] who issued the command.
* params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
* * sender - The [/datum/tgs_chat_user] who issued the command.
* * params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
*/
/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
CRASH("[type] has no implementation for Run()")
/// User definable chat message.
/// User definable chat message. MUST NOT be overridden.
/datum/tgs_message_content
/// The tring content of the message. Must be provided in New().
/// The string content of the message. Must be provided in New().
var/text
/// The [/datum/tgs_chat_embed] to embed in the message. Not supported on all chat providers.
var/datum/tgs_chat_embed/structure/embed
/**
* Create a [/datum/tgs_message_content].
*
* * text - The string content of the message.
*/
/datum/tgs_message_content/New(text)
..()
if(!istext(text))
TGS_ERROR_LOG("[/datum/tgs_message_content] created with no text!")
text = null
src.text = text
/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/channel#embed-object-embed-structure for details.
/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/message#embed-object for details.
/datum/tgs_chat_embed/structure
var/title
var/description
@@ -322,13 +381,13 @@
/// Colour must be #AARRGGBB or #RRGGBB hex string.
var/colour
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-image-structure for details.
var/datum/tgs_chat_embed/media/image
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-thumbnail-structure for details.
var/datum/tgs_chat_embed/media/thumbnail
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-video-structure for details.
var/datum/tgs_chat_embed/media/video
var/datum/tgs_chat_embed/footer/footer
@@ -337,7 +396,7 @@
var/list/datum/tgs_chat_embed/field/fields
/// Common datum for similar discord embed medias.
/// Common datum for similar Discord embed medias.
/datum/tgs_chat_embed/media
/// Must be set in New().
var/url
@@ -345,48 +404,58 @@
var/height
var/proxy_url
/// Create a [/datum/tgs_chat_embed].
/datum/tgs_chat_embed/media/New(url)
..()
if(!istext(url))
CRASH("[/datum/tgs_chat_embed/media] created with no url!")
src.url = url
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-footer-structure for details.
/datum/tgs_chat_embed/footer
/// Must be set in New().
var/text
var/icon_url
var/proxy_icon_url
/// Create a [/datum/tgs_chat_embed/footer].
/datum/tgs_chat_embed/footer/New(text)
..()
if(!istext(text))
CRASH("[/datum/tgs_chat_embed/footer] created with no text!")
src.text = text
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-provider-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-provider-structure for details.
/datum/tgs_chat_embed/provider
var/name
var/url
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure for details. Must have name set in New().
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-author-structure for details. Must have name set in New().
/datum/tgs_chat_embed/provider/author
var/icon_url
var/proxy_icon_url
/// Create a [/datum/tgs_chat_embed/footer].
/datum/tgs_chat_embed/provider/author/New(name)
..()
if(!istext(name))
CRASH("[/datum/tgs_chat_embed/provider/author] created with no name!")
src.name = name
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure for details. Must have name and value set in New().
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-field-structure for details.
/datum/tgs_chat_embed/field
/// Must be set in New().
var/name
/// Must be set in New().
var/value
var/is_inline
/// Create a [/datum/tgs_chat_embed/field].
/datum/tgs_chat_embed/field/New(name, value)
..()
if(!istext(name))
CRASH("[/datum/tgs_chat_embed/field] created with no name!")
@@ -429,6 +498,7 @@
/**
* Send a message to connected chats. This function may sleep!
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
*
* message - The [/datum/tgs_message_content] to send.
* admin_only: If [TRUE], message will be sent to admin connected chats. Vice-versa applies.
@@ -439,6 +509,7 @@
/**
* Send a private message to a specific user. This function may sleep!
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
*
* message - The [/datum/tgs_message_content] to send.
* user: The [/datum/tgs_chat_user] to PM.
@@ -449,6 +520,7 @@
/**
* Send a message to connected chats that are flagged as game-related in TGS. This function may sleep!
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
*
* message - The [/datum/tgs_message_content] to send.
* channels - Optional list of [/datum/tgs_chat_channel]s to restrict the message to.
@@ -462,6 +534,11 @@
CAN_BE_REDEFINED(TRUE)
return
/// Returns the running engine type
/world/proc/TgsEngine()
CAN_BE_REDEFINED(TRUE)
return
/// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
/world/proc/TgsApiVersion()
CAN_BE_REDEFINED(TRUE)
@@ -497,10 +574,21 @@
CAN_BE_REDEFINED(TRUE)
return
/**
* Trigger an event in TGS. Requires TGS version >= 6.3.0. Returns [TRUE] if the event was triggered successfully, [FALSE] otherwise. This function may sleep!
*
* event_name - The name of the event to trigger
* parameters - Optional list of string parameters to pass as arguments to the event script. The first parameter passed to a script will always be the running game's directory followed by these parameters.
* wait_for_completion - If set, this function will not return until the event has run to completion.
*/
/world/proc/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
CAN_BE_REDEFINED(TRUE)
return
/*
The MIT License
Copyright (c) 2017-2023 Jordan Brown
Copyright (c) 2017-2024 Jordan Brown
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
+10
View File
@@ -24,6 +24,16 @@
/// Window is free and ready to receive data
#define TGUI_WINDOW_READY 2
/// Though not the maximum renderable ByondUis within tgui, this is the maximum that the server will manage per-UI
#define TGUI_MANAGED_BYONDUI_LIMIT 10
// These are defines instead of being inline, as they're being sent over
// from tgui-core, so can't be easily played with
#define TGUI_MANAGED_BYONDUI_TYPE_RENDER "renderByondUi"
#define TGUI_MANAGED_BYONDUI_TYPE_UNMOUNT "unmountByondUi"
#define TGUI_MANAGED_BYONDUI_PAYLOAD_ID "renderByondUi"
/// Get a window id based on the provided pool index
#define TGUI_WINDOW_ID(index) "tgui-window-[index]"
/// Get a pool index of the provided window id
+3
View File
@@ -94,6 +94,9 @@
/proc/is_shuttle_area(var/area/A)
. = istype(A,/area/horizon/shuttle)
/proc/is_shieldless_exterior(var/area/A)
. = istype(A,/area/horizon/exterior/no_shields)
/proc/is_area_with_turf(var/area/A)
. = isnum(A.x)
+44 -9
View File
@@ -274,7 +274,7 @@
/**
* Blend atoms
*/
/atom/proc/handle_blending(adjacencies, var/list/dir_mods)
/atom/proc/handle_blending(adjacencies, list/dir_mods)
SHOULD_NOT_SLEEP(TRUE)
LAZYINITLIST(dir_mods)
@@ -282,10 +282,10 @@
var/walls_found = 0
for(var/adjacency in list(N_NORTH, N_EAST, N_SOUTH, N_WEST))
if(adjacencies & adjacency)
var/turf/T = get_step(src, REVERSE_DIR(adjacency))
var/turf/T = get_step(src, GLOB.ndir_to_dir[adjacency]) // get_step() expects byond native directions, so we convert N_* bitfields
if(is_type_in_list(T, can_blend_with))
if(attach_overlay)
AddOverlays("[REVERSE_DIR(adjacency)]_[attach_overlay]")
AddOverlays("[GLOB.ndir_to_dir[adjacency]]_[attach_overlay]")
walls_found |= adjacency
dir_mods["[adjacency]"] = "-[blend_overlay]"
for(var/adjacency in list(N_NORTH, N_SOUTH))
@@ -297,7 +297,7 @@
var/has_adjacency = walls_found & adjacency
var/has_diagonal = walls_found & diagonal
if(((adjacencies & adjacency) && (adjacencies && diagonal)) && (has_adjacency || has_diagonal))
if(((adjacencies & adjacency) && (adjacencies & diagonal)) && (has_adjacency || has_diagonal))
dir_mods["[adjacency][diagonal]"] = "-[prefix][has_adjacency ? "wall" : "win"]-[suffix][has_diagonal ? "wall" : "win"]"
if(attach_overlay)
AddOverlays("[prefix][suffix]_[attach_overlay]")
@@ -305,10 +305,15 @@
#undef ndir_to_initial
/atom/proc/cardinal_smooth(adjacencies)
/atom/proc/cardinal_smooth(adjacencies, list/dir_mods)
SHOULD_NOT_SLEEP(TRUE)
/**
* Below here we evaluate an object by their 4 corners and tell them how they're supposed to smooth depending on its adjacencies.
* The adjacencies we care about is the things that the atom can smooth with, determined in 'calculate_adjacencies()'.
* Think this as mapping an icon, every 16x16 corner counts.
*/
//NW CORNER
// NW CORNER
var/nw = "1-i"
if((adjacencies & N_NORTH) && (adjacencies & N_WEST))
if(adjacencies & N_NORTHWEST)
@@ -321,7 +326,7 @@
else if(adjacencies & N_WEST)
nw = "1-w"
//NE CORNER
// NE CORNER
var/ne = "2-i"
if((adjacencies & N_NORTH) && (adjacencies & N_EAST))
if(adjacencies & N_NORTHEAST)
@@ -334,7 +339,7 @@
else if(adjacencies & N_EAST)
ne = "2-e"
//SW CORNER
// SW CORNER
var/sw = "3-i"
if((adjacencies & N_SOUTH) && (adjacencies & N_WEST))
if(adjacencies & N_SOUTHWEST)
@@ -347,7 +352,7 @@
else if(adjacencies & N_WEST)
sw = "3-w"
//SE CORNER
// SE CORNER
var/se = "4-i"
if((adjacencies & N_SOUTH) && (adjacencies & N_EAST))
if(adjacencies & N_SOUTHEAST)
@@ -360,6 +365,36 @@
else if(adjacencies & N_EAST)
se = "4-e"
/**
* Here we handle blend overlays. This is an extra step if we want our icons to take account whatever is defined in their 'can_blend_with' list
* and apply a different smooth overlay for them. An example to this is windows, they'll smooth regularly among themselves but if there's
* a wall next to them they'll apply a junction part. See 'handle_blending()' for blending direction modifiers (dir_mods).
*/
if(length(dir_mods))
nw += LAZYACCESS(dir_mods, "[N_NORTH][N_WEST][N_NORTHWEST]") \
|| LAZYACCESS(dir_mods, "[N_NORTH][N_WEST]") \
|| LAZYACCESS(dir_mods, "[N_NORTH]") \
|| LAZYACCESS(dir_mods, "[N_WEST]")
ne += LAZYACCESS(dir_mods, "[N_NORTH][N_EAST][N_NORTHEAST]") \
|| LAZYACCESS(dir_mods, "[N_NORTH][N_EAST]") \
|| LAZYACCESS(dir_mods, "[N_NORTH]") \
|| LAZYACCESS(dir_mods, "[N_EAST]")
sw += LAZYACCESS(dir_mods, "[N_SOUTH][N_WEST][N_SOUTHWEST]") \
|| LAZYACCESS(dir_mods, "[N_SOUTH][N_WEST]") \
|| LAZYACCESS(dir_mods, "[N_SOUTH]") \
|| LAZYACCESS(dir_mods, "[N_WEST]")
se += LAZYACCESS(dir_mods, "[N_SOUTH][N_EAST][N_SOUTHEAST]") \
|| LAZYACCESS(dir_mods, "[N_SOUTH][N_EAST]") \
|| LAZYACCESS(dir_mods, "[N_SOUTH]") \
|| LAZYACCESS(dir_mods, "[N_EAST]")
/**
* Here we apply all the 4 corners we calculated and clean the cached corners.
*/
var/list/New
var/list/Old
var/cut_f = smoothing_hints & SMOOTHHINT_CUT_F
+28
View File
@@ -912,3 +912,31 @@
/proc/endswith(input_text, ending)
var/input_length = LAZYLEN(ending)
return !!findtext(input_text, ending, -input_length)
///Returns a string based on the weight class define used as argument
/proc/weight_class_to_text(w_class)
switch(w_class)
if(WEIGHT_CLASS_TINY)
. = "tiny"
if(WEIGHT_CLASS_SMALL)
. = "small"
if(WEIGHT_CLASS_NORMAL)
. = "normal-sized"
if(WEIGHT_CLASS_BULKY)
. = "bulky"
if(WEIGHT_CLASS_HUGE)
. = "huge"
if(WEIGHT_CLASS_GIGANTIC)
. = "gigantic"
else
. = ""
/proc/weight_class_to_tooltip(w_class)
switch(w_class)
if(WEIGHT_CLASS_TINY to WEIGHT_CLASS_SMALL)
return "This item can fit into pockets, boxes and backpacks."
if(WEIGHT_CLASS_NORMAL)
return "This item can fit into backpacks."
if(WEIGHT_CLASS_BULKY to WEIGHT_CLASS_GIGANTIC)
return "This item is too large to fit into any standard storage."
return ""
+2
View File
@@ -50,6 +50,8 @@
turfs += T
if(turfs.len)
return pick(turfs)
else
return null
///Returns a turf based on text inputs, original turf and viewing client
/proc/parse_caught_click_modifiers(list/modifiers, turf/origin, client/viewing_client)
-5
View File
@@ -623,11 +623,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
if (progbar)
qdel(progbar)
/// Integer. Unique sequential ID from the `do_after` proc used to validate `DO_USER_UNIQUE_ACT` flag checks.
/mob/var/do_unique_user_handle = 0
/// The mob currently interacting with the atom during a `do_after` timer. Used to validate `DO_TARGET_UNIQUE_ACT` flag checks.
/atom/var/mob/do_unique_target_user
/**
* Timed actions involving one mob user and (optionally) one target.
*
+12
View File
@@ -93,6 +93,18 @@ GLOBAL_LIST_INIT(alldirs, list(
SOUTHWEST,
))
/// Used in N_* directions (defined in \code\_DEFINES\icon_smoothing.dm) to Byond native conversions
GLOBAL_LIST_INIT(ndir_to_dir, alist(
N_NORTH = NORTH,
N_SOUTH = SOUTH,
N_EAST = EAST,
N_WEST = WEST,
N_NORTHEAST = NORTHEAST,
N_NORTHWEST = NORTHWEST,
N_SOUTHEAST = SOUTHEAST,
N_SOUTHWEST = SOUTHWEST
))
/// Just a list of all the area objects in the game
/// Note, areas can have duplicate types
GLOBAL_LIST_EMPTY(areas)
-4
View File
@@ -3,10 +3,6 @@
~Sayu
*/
// 1 decisecond click delay (above and beyond mob/next_move)
/mob/var/next_click = 0
/**
* Before anything else, defer these calls to a per-mobtype handler. This allows us to
* remove istype() spaghetti code, but requires the addition of other handler procs to simplify it.
-9
View File
@@ -11,15 +11,6 @@ if(!click_handlers) { \
click_handlers += new/datum/click_handler/default(src) \
}
/**
* LAZYLIST (Instances of `/datum/click_handler`). Click handlers for this mob that should intercept and handle click
* calls.
*
* The 'topmost'/'active' click handler for the mob is the handler currently at index `1`. By default, this will be
* `/datum/click_handler/default`.
*/
/mob/var/list/click_handlers
// In the mob code, because otherwise it's a redefinition
// /mob/Destroy()
// QDEL_LIST(click_handlers)
+16
View File
@@ -284,6 +284,22 @@
check_flags = AB_CHECK_STUNNED|AB_CHECK_ALIVE|AB_CHECK_INSIDE
button_icon_state = "night_eyes"
/datum/action/item_action/organ/night_eyes/Trigger()
if(!Checks())
return
var/obj/item/organ/internal/eyes/night/target_eyes = target
target_eyes.night_vision()
/datum/action/item_action/organ/extended_eyes
check_flags = AB_CHECK_STUNNED|AB_CHECK_ALIVE|AB_CHECK_INSIDE
button_icon_state = "night_eyes"
/datum/action/item_action/organ/extended_eyes/Trigger()
if(!Checks())
return
var/obj/item/organ/internal/eyes/night/target_eyes = target
target_eyes.extended_vision()
/datum/action/item_action/organ/night_eyes/rev
check_flags = AB_CHECK_ALIVE|AB_CHECK_INSIDE
button_icon_state = "rev_eyes"
+30 -26
View File
@@ -5,11 +5,6 @@
GLOBAL_DATUM_INIT(global_hud, /datum/global_hud, new)
GLOBAL_LIST(global_huds)
/datum/hud/var/atom/movable/screen/grab_intent
/datum/hud/var/atom/movable/screen/hurt_intent
/datum/hud/var/atom/movable/screen/disarm_intent
/datum/hud/var/atom/movable/screen/help_intent
/datum/global_hud
var/atom/movable/screen/vr_control
var/atom/movable/screen/druggy
@@ -111,12 +106,11 @@ GLOBAL_LIST(global_huds)
O.layer = IMPAIRED_LAYER
O.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/*
The hud datum
Used to show and hide huds for all the different mob types,
including inventories and item quick actions.
*/
/**
* The hud datum
* Used to show and hide huds for all the different mob types,
* including inventories and item quick actions.
*/
/datum/hud
///The mob that possesses the HUD
var/mob/mymob
@@ -136,12 +130,21 @@ GLOBAL_LIST(global_huds)
///Boolean, if the action buttons are hidden
var/action_buttons_hidden = FALSE
/*
STOP ADDING SNOWFLAKE HUD ELEMENTS LIKE /datum/hud/var/atom/movable/screen/help_intent
IN DIFFERENT FILES ENTIRELY. IF YOU ARE ADDING A NEW HUD ELEMENT TO THIS, PUT IT IN THIS FILE UNDER THIS LIST,
AND INCLUDE A QDEL_NULL() FOR IT IN THE DESTROY PROC.
*/
var/atom/movable/screen/blobpwrdisplay
var/atom/movable/screen/blobhealthdisplay
var/atom/movable/screen/r_hand_hud_object
var/atom/movable/screen/l_hand_hud_object
var/atom/movable/screen/action_intent
var/atom/movable/screen/movement_intent/move_intent
var/atom/movable/screen/grab_intent
var/atom/movable/screen/hurt_intent
var/atom/movable/screen/disarm_intent
var/atom/movable/screen/help_intent
var/list/adding
var/list/other
@@ -177,26 +180,27 @@ GLOBAL_LIST(global_huds)
..()
/datum/hud/Destroy()
grab_intent = null
hurt_intent = null
disarm_intent = null
help_intent = null
blobpwrdisplay = null
blobhealthdisplay = null
r_hand_hud_object = null
l_hand_hud_object = null
action_intent = null
move_intent = null
adding = null
other = null
hotkeybuttons = null
// item_action_list = null // ?
mymob = null
QDEL_NULL(blobpwrdisplay)
QDEL_NULL(blobhealthdisplay)
QDEL_NULL(r_hand_hud_object)
QDEL_NULL(l_hand_hud_object)
QDEL_NULL(action_intent)
QDEL_NULL(move_intent)
QDEL_NULL(grab_intent)
QDEL_NULL(hurt_intent)
QDEL_NULL(disarm_intent)
QDEL_NULL(help_intent)
adding?.Cut()
other?.Cut()
QDEL_LIST(hotkeybuttons)
QDEL_LIST_ASSOC_VAL(plane_masters)
QDEL_LIST_ASSOC_VAL(plane_master_controllers)
QDEL_NULL(hide_actions_toggle)
. = ..()
return ..()
/datum/hud/proc/hidden_inventory_update()
if(!mymob)
+2 -2
View File
@@ -26,10 +26,10 @@
/atom/movable/screen/Destroy(force = FALSE)
master = null
screen_loc = null
if(hud?.mymob?.client)
if(length(hud?.mymob?.client?.screen))
hud.mymob.client.screen -= src
hud = null
. = ..()
return ..()
/// Screen elements are always on top of the players screen and don't move so yes they are adjacent
/atom/movable/screen/Adjacent(atom/neighbor, atom/target, atom/movable/mover)
+1 -1
View File
@@ -150,7 +150,7 @@ This calls [atom/proc/tool_act], among others.
return 0
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(target_mob, src)
user.do_attack_animation(target_mob, used_item = src)
if(!user.aura_check(AURA_TYPE_WEAPON, src, user))
return FALSE
+72 -21
View File
@@ -10,31 +10,82 @@ SUBSYSTEM_DEF(chat)
flags = SS_TICKER | SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
init_order = INITSTAGE_MAX
var/list/payload_by_client = list()
/// Assosciates a ckey with a list of messages to send to them.
var/list/list/datum/chat_payload/client_to_payloads = list()
/// Associates a ckey with an assosciative list of their last CHAT_RELIABILITY_HISTORY_SIZE messages.
var/list/list/datum/chat_payload/client_to_reliability_history = list()
/// Assosciates a ckey with their next sequence number.
var/list/client_to_sequence_number = list()
/datum/controller/subsystem/chat/proc/generate_payload(client/target, message_data)
var/sequence = client_to_sequence_number[target.ckey]
client_to_sequence_number[target.ckey] += 1
var/datum/chat_payload/payload = new
payload.sequence = sequence
payload.content = message_data
if(!(target.ckey in client_to_reliability_history))
client_to_reliability_history[target.ckey] = list()
var/list/client_history = client_to_reliability_history[target.ckey]
client_history["[sequence]"] = payload
if(length(client_history) > 5)
var/oldest = text2num(client_history[1])
for(var/index in 2 to length(client_history))
var/test = text2num(client_history[index])
if(test < oldest)
oldest = test
client_history -= "[oldest]"
return payload
/datum/controller/subsystem/chat/proc/send_payload_to_client(client/target, datum/chat_payload/payload)
target.tgui_panel.window.send_message("chat/message", payload.into_message())
SEND_TEXT(target, payload.get_content_as_html())
/datum/controller/subsystem/chat/fire()
for(var/key in payload_by_client)
var/client/client = key
var/payload = payload_by_client[key]
payload_by_client -= key
if(client)
// Send to tgchat
client.tgui_panel?.window.send_message("chat/message", payload)
// Send to old chat
for(var/message in payload)
to_target(client, message_to_html(message))
for(var/ckey in client_to_payloads)
var/client/target = GLOB.directory[ckey]
if(isnull(target)) // verify client still exists
LAZYREMOVE(client_to_payloads, ckey)
continue
for(var/datum/chat_payload/payload as anything in client_to_payloads[ckey])
send_payload_to_client(target, payload)
LAZYREMOVE(client_to_payloads, ckey)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/chat/proc/queue(target, message)
if(islist(target))
for(var/_target in target)
var/client/client = CLIENT_FROM_VAR(_target)
if(client)
LAZYADD(payload_by_client[client], list(message))
/datum/controller/subsystem/chat/proc/queue(queue_target, list/message_data)
var/list/targets = islist(queue_target) ? queue_target : list(queue_target)
for(var/target in targets)
var/client/client = CLIENT_FROM_VAR(target)
if(isnull(client))
continue
LAZYADDASSOCLIST(client_to_payloads, client.ckey, generate_payload(client, message_data))
/datum/controller/subsystem/chat/proc/send_immediate(send_target, list/message_data)
var/list/targets = islist(send_target) ? send_target : list(send_target)
for(var/target in targets)
var/client/client = CLIENT_FROM_VAR(target)
if(isnull(client))
continue
send_payload_to_client(client, generate_payload(client, message_data))
/datum/controller/subsystem/chat/proc/handle_resend(client/client, sequence)
var/list/client_history = client_to_reliability_history[client.ckey]
sequence = "[sequence]"
if(isnull(client_history) || !(sequence in client_history))
return
var/client/client = CLIENT_FROM_VAR(target)
if(client)
LAZYADD(payload_by_client[client], list(message))
var/datum/chat_payload/payload = client_history[sequence]
if(payload.resends > 3)
return // we tried but byond said no
payload.resends += 1
send_payload_to_client(client, client_history[sequence])
+13 -1
View File
@@ -306,6 +306,18 @@ SUBSYSTEM_DEF(garbage)
if (time > 0.1 SECONDS)
postpone(time)
// Standard sentry logging for hard dels other than the two subsystems that will always hard del every round
var/sentry_threshold = 0.25 SECONDS
if (time > sentry_threshold && SSsentry)
SSsentry.capture_message(
"Hard delete: [type]",
"warning",
"garbage",
tags = list("datum_type" = "[type]"),
extra = list("ref_id" = refID, "time_ms" = tick_usage, "details" = detail)
)
var/threshold = 0.5 // Used to be CONFIG_GET(number/hard_deletes_overrun_threshold)
if (threshold && (time > threshold SECONDS))
if (!(type_info.qdel_flags & QDEL_ITEM_ADMINS_WARNED))
@@ -318,7 +330,7 @@ SUBSYSTEM_DEF(garbage)
"warning",
"garbage",
tags = list("datum_type" = "[type]"),
extra = list("ref_id" = refID, "time_ms" = tick_usage)
extra = list("ref_id" = refID, "time_ms" = tick_usage, "details" = detail)
)
type_info.hard_deletes_over_threshold++
var/overrun_limit = 0 // Used to be CONFIG_GET(number/hard_deletes_overrun_limit)
@@ -23,37 +23,50 @@ SUBSYSTEM_DEF(atlas)
/**
* Note that the dirs here are REVERSE because they're used for entry points, so it'd be the dir facing starboard for example.
* These are strings because otherwise the list indexes would be out of bounds. Thanks BYOND.
*
* SOUTH = 1, NORTH = 2, WEST = 4, EAST = 8,
* NORTH-EAST = 5, SOUTH-EAST = 6, NORTH-WEST = 9, SOUTH-WEST = 10,
*/
var/list/naval_to_dir = list(
"1" = list(
"1" = list( //Ship Fore is facing North. (These are reversed, a shot travelling south would enter from the north.)
"starboard" = WEST,
"port" = EAST,
"fore" = SOUTH,
"aft" = NORTH
"aft" = NORTH,
"aft-starboard" = NORTH|WEST,
"fore-starboard" = SOUTH|WEST,
"aft-port" = NORTH|EAST,
"fore-port" = SOUTH|EAST
),
"2" = list(
"2" = list( //Ship Fore is facing South.
"starboard" = EAST,
"port" = WEST,
"fore" = NORTH,
"aft" = SOUTH
"aft" = SOUTH,
"aft-starboard" = SOUTH|EAST,
"fore-starboard" = NORTH|EAST,
"aft-port" = SOUTH|WEST,
"fore-port" = NORTH|WEST
),
"4" = list(
"4" = list( //Ship Fore is facing West.
"starboard" = NORTH,
"port" = SOUTH,
"fore" = WEST,
"aft" = EAST
"aft" = EAST,
"aft-starboard" = EAST|NORTH,
"fore-starboard" = WEST|NORTH,
"aft-port" = EAST|SOUTH,
"fore-port" = WEST|SOUTH
),
"4" = list(
"starboard" = NORTH,
"port" = SOUTH,
"fore" = WEST,
"aft" = EAST
),
"8" = list(
"8" = list( //Ship Fore is facing East.
"starboard" = SOUTH,
"port" = NORTH,
"fore" = EAST,
"aft" = WEST
"aft" = WEST,
"aft-starboard" = WEST|SOUTH,
"fore-starboard" = EAST|SOUTH,
"aft-port" = WEST|NORTH,
"fore-port" = EAST|NORTH
)
)
@@ -62,49 +75,49 @@ SUBSYSTEM_DEF(atlas)
"1" = "aft",
"2" = "fore",
"4" = "port",
"5" = "port",
"6" = "port",
"5" = "aft-port",
"6" = "fore-port",
"8" = "starboard",
"9" = "starboard",
"10" = "starboard"
"9" = "aft-starboard",
"10" = "fore-starboard"
),
"2" = list(
"1" = "fore",
"2" = "aft",
"4" = "starboard",
"5" = "starboard",
"6" = "starboard",
"5" = "fore-starboard",
"6" = "aft-starboard",
"8" = "port",
"9" = "port",
"10" = "port"
"9" = "fore-port",
"10" = "aft-port"
),
"4" = list(
"1" = "starboard",
"2" = "port",
"4" = "aft",
"5" = "starboard",
"6" = "port",
"5" = "starboard-aft",
"6" = "port-aft",
"8" = "fore",
"9" = "starboard",
"10" = "port"
"9" = "starboard-fore",
"10" = "port-fore"
),
"5" = list( //northeast
"1" = "starboard",
"2" = "port",
"4" = "port",
"1" = "aft-starboard",
"2" = "fore-port",
"4" = "aft-port",
"5" = "aft",
"6" = "port",
"8" = "starboard",
"8" = "fore-starboard",
"9" = "starboard",
"10" = "fore"
),
"6" = list( //southeast
"1" = "starboard",
"2" = "port",
"4" = "starboard",
"1" = "fore-starboard",
"2" = "aft-port",
"4" = "aft-starboard",
"5" = "starboard",
"6" = "aft",
"8" = "port",
"8" = "fore-port",
"9" = "fore",
"10" = "port"
),
@@ -112,31 +125,31 @@ SUBSYSTEM_DEF(atlas)
"1" = "port",
"2" = "starboard",
"4" = "fore",
"5" = "port",
"6" = "starboard",
"5" = "port-fore",
"6" = "starboard-fore",
"8" = "aft",
"9" = "port",
"10" = "starboard"
"9" = "port-aft",
"10" = "starboard-aft"
),
"9" = list( //northwest
"1" = "port",
"2" = "starboard",
"4" = "port",
"1" = "aft-port",
"2" = "fore-starboard",
"4" = "fore-port",
"5" = "port",
"6" = "fore",
"8" = "starboard",
"6" = "aft-starboard",
"8" = "fore",
"9" = "aft",
"10" = "starboard"
),
"10" = list( //southwest
"1" = "port",
"2" = "starboard",
"4" = "starboard",
"1" = "fore-port",
"2" = "aft-starboard",
"4" = "fore-starboard",
"5" = "fore",
"6" = "starboard",
"8" = "port",
"9" = "port",
"10" = "aft"
"8" = "aft-port",
"9" = "aft",
"10" = "port"
)
)
-3
View File
@@ -410,9 +410,6 @@ SUBSYSTEM_DEF(jobs)
Debug("ER/([H]): Completed.")
return H
/mob/living/carbon/human
var/tmp/centcomm_despawn_timer
/mob/living/proc/centcomm_timeout()
if (!istype(get_area(src), /area/centcom/spawning))
return FALSE
-3
View File
@@ -66,9 +66,6 @@ SUBSYSTEM_DEF(mob_ai)
mutexes -= mob_weakref
/mob
var/thinking_enabled = FALSE
/**
* Perform AI logic for a mob, called by the AI subsystem
*
@@ -50,12 +50,16 @@ SUBSYSTEM_DEF(nightlight)
/datum/controller/subsystem/nightlight/proc/get_apc_list(var/whitelisted_only = 1)
var/list/obj/structure/machinery/power/apc/lighting_apcs = list()
for (var/A in get_sorted_areas())
var/area/B = A
if (B.no_light_control || (!(B.allow_nightmode) && whitelisted_only))
for (var/obj/structure/machinery/power/apc/APC in SSmachinery.apc_units)
if (QDELETED(APC) || APC.aidisabled)
continue
if (B.apc && !B.apc.aidisabled)
lighting_apcs += B.apc
var/area/apc_area = APC.area || get_area(APC)
if (!apc_area)
continue
if (apc_area.no_light_control || (!(apc_area.allow_nightmode) && whitelisted_only))
continue
lighting_apcs += APC
CHECK_TICK
+7
View File
@@ -12,6 +12,8 @@ SUBSYSTEM_DEF(plants)
var/list/seeds = list()
/// Gene obfuscation for delicious trial and error goodness.
var/list/gene_tag_masks = list()
/// Reverse lookup for gene_tag_masks, keyed by real gene tag.
var/list/gene_masks_by_tag = list()
/// Stores images of growth, fruits and seeds.
var/list/plant_icon_cache = list()
/// List of all harvested product sprites.
@@ -83,6 +85,7 @@ SUBSYSTEM_DEF(plants)
used_masks += gene_mask
plant_traits -= gene_tag
gene_tag_masks[gene_mask] = gene_tag
gene_masks_by_tag[gene_tag] = gene_mask
plant_gene_datums[gene_mask] = G
gene_masked_list += (list(list("tag" = gene_tag, "mask" = gene_mask)))
@@ -93,6 +96,7 @@ SUBSYSTEM_DEF(plants)
src.product_descs = SSplants.product_descs
src.seeds = SSplants.seeds
src.gene_tag_masks = SSplants.gene_tag_masks
src.gene_masks_by_tag = SSplants.gene_masks_by_tag
src.plant_icon_cache = SSplants.plant_icon_cache
src.plant_sprites = SSplants.plant_sprites
src.plant_product_sprites = SSplants.plant_product_sprites
@@ -146,6 +150,9 @@ SUBSYSTEM_DEF(plants)
SET_SEED_TRAIT(seed, TRAIT_HIGHKPA_TOLERANCE, 200)
return seed
/datum/controller/subsystem/plants/proc/get_gene_mask(var/gene_tag)
return gene_masks_by_tag[gene_tag] || gene_tag
/// Debug for testing seed genes.
/client/proc/show_plant_genes()
set category = "Debug"
@@ -1,241 +0,0 @@
PROCESSING_SUBSYSTEM_DEF(nanoui)
// Subsystem stuff.
name = "NanoUI"
flags = SS_NO_INIT
priority = SS_PRIORITY_NANOUI
stat_tag = "A"
// NanoUI stuff.
var/list/open_nanouis = list()
/**
* Get an open /nanoui ui for the current user, src_object and ui_key and try to update it with data
*
* * user - /mob The mob who opened/owns the ui
* * src_object - /obj|/mob The obj or mob which the ui belongs to
* * ui_key - A string key used for the ui
* * data - List, the data to be passed to the ui, if it exists
* * force_open - Boolean, the ui is being forced to (re)open, so close ui if it exists (instead of updating)
*
* Returns the `/nanoui` found ui, for null if none exists
*/
/datum/controller/subsystem/processing/nanoui/proc/try_update_ui(mob/user, src_object, ui_key, datum/nanoui/ui, data, force_open = FALSE)
if (!ui) // no ui has been passed, so we'll search for one
ui = get_open_ui(user, src_object, ui_key)
if (ui)
// The UI is already open
if (!force_open)
ui.push_data(data)
return ui
else
ui.reinitialise(new_initial_data=data)
return ui
return null
/**
* Get an open /nanoui ui for the current user, src_object and ui_key
*
* * user - The `/mob` who opened/owns the ui
* * src_object - The `/obj` or `/mob` which the ui belongs to
* * ui_key - A string key used for the ui
*/
/datum/controller/subsystem/processing/nanoui/proc/get_open_ui(mob/user, src_object, ui_key)
var/src_object_key = REF(src_object)
if (!LAZYLEN(open_nanouis[src_object_key]) || !LAZYLEN(open_nanouis[src_object_key][ui_key]))
return null
for (var/datum/nanoui/ui in open_nanouis[src_object_key][ui_key])
if (ui.user == user)
return ui
//testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - ui not found")
return null
/**
* Update all `/nanoui` uis attached to src_object
*
* * src_object - The `/obj` or `/mob` which the uis are attached to
*
* Returns the number of UIs updated
*/
/datum/controller/subsystem/processing/nanoui/proc/update_uis(src_object)
var/src_object_key = REF(src_object)
if (!LAZYLEN(open_nanouis[src_object_key]))
return 0
. = 0
var/list/obj_uis = open_nanouis[src_object_key]
for (var/ui_key in obj_uis)
for (var/thing in obj_uis[ui_key])
var/datum/nanoui/ui = thing
if(ui && ui.src_object && ui.user && ui.src_object.ui_host())
ui.process(1)
.++
/**
* Close all `/nanoui` uis attached to src_object
*
* * src_object - The `/obj` or `/mob` which the uis are attached to
*
* Returns the number of UIs closed
*/
/datum/controller/subsystem/processing/nanoui/proc/close_uis(src_object)
var/src_object_key = REF(src_object)
if (!open_nanouis[src_object_key] || !islist(open_nanouis[src_object_key]))
return 0
. = 0
var/list/obj_uis = open_nanouis[src_object_key]
for (var/ui_key in obj_uis)
for (var/thing in obj_uis[ui_key])
var/datum/nanoui/ui = thing
if(ui && ui.src_object && ui.user && ui.src_object.ui_host())
ui.close()
.++
/**
* Update /nanoui uis belonging to user
*
* * user - The `/mob` who owns the uis
* * src_object - An `/obj` or `/mob` that, if is provided, only update uis which are attached to it (optional)
* * ui_key - A string, if ui_key is provided, only update uis with a matching ui_key (optional)
*
* Returns the number of UIs updated
*/
/datum/controller/subsystem/processing/nanoui/proc/update_user_uis(mob/user, src_object, ui_key)
if (!LAZYLEN(user.open_nanouis))
return 0 // has no open uis
. = 0
for (var/thing in user.open_nanouis)
var/datum/nanoui/ui = thing
if (NULL_OR_EQUAL(src_object, ui.src_object) && NULL_OR_EQUAL(ui_key, ui.ui_key))
ui.process(1)
.++
/datum/controller/subsystem/processing/nanoui/proc/close_user_uis(mob/user, src_object, ui_key)
if (!LAZYLEN(user.open_nanouis))
return 0
for (var/thing in user.open_nanouis)
var/datum/nanoui/ui = thing
if (NULL_OR_EQUAL(src_object, ui.src_object) && NULL_OR_EQUAL(ui_key, ui.ui_key))
ui.close()
.++
//testing("nanomanager/close_user_uis mob [user.name] closed [open_nanouis.len] of [.] uis")
/**
* Add a /nanoui ui to the list of open uis
* This is called by the /nanoui open() proc
*
* * ui - The `/nanoui` ui to add
*/
/datum/controller/subsystem/processing/nanoui/proc/ui_opened(datum/nanoui/ui)
var/src_object_key = REF(ui.src_object)
LAZYINITLIST(open_nanouis[src_object_key])
LAZYADD(ui.user.open_nanouis, ui)
LAZYADD(open_nanouis[src_object_key][ui.ui_key], ui)
START_PROCESSING(SSnanoui, ui)
//testing("nanomanager/ui_opened mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_nanouis [ui.user.open_nanouis.len] | uis [uis.len] | processing_uis [processing_uis.len]")
/**
* Remove a /nanoui ui from the list of open uis
* This is called by the /nanoui close() proc
*
* * ui - A `/nanoui` to remove
*
* Returns FALSE if no ui was removed, TRUE if removed successfully
*/
/datum/controller/subsystem/processing/nanoui/proc/ui_closed(datum/nanoui/ui)
var/src_object_key = REF(ui.src_object)
var/ui_key = ui.ui_key
var/list/obj_uis = open_nanouis[src_object_key]
if (!LAZYLEN(obj_uis) || !obj_uis[ui_key])
return 0 // Wasn't open.
STOP_PROCESSING(SSnanoui, ui)
if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface)
LAZYREMOVE(ui.user.open_nanouis, ui)
obj_uis[ui_key] -= ui
if (!LAZYLEN(obj_uis[ui_key]))
obj_uis -= ui_key
if (!LAZYLEN(obj_uis))
open_nanouis -= src_object_key
//testing("nanomanager/ui_closed mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_nanouis [ui.user.open_nanouis.len] | uis [uis.len] | processing_uis [processing_uis.len]")
return 1
/**
* This is called on user logout
* Closes/clears all uis attached to the user's `/mob`
*
* * user - The user's `/mob`
*/
/datum/controller/subsystem/processing/nanoui/proc/user_logout(mob/user)
return close_user_uis(user)
/**
* This is called when a player transfers from one mob to another
* Transfers all open UIs to the new mob
*
* * oldMob - The user's old `/mob`
* * newMob - The user's new `/mob`
*/
/datum/controller/subsystem/processing/nanoui/proc/user_transferred(mob/oldMob, mob/newMob)
//testing("nanomanager/user_transferred from mob [oldMob.name] to mob [newMob.name]")
if (!oldMob || !LAZYLEN(oldMob.open_nanouis) || !LAZYLEN(open_nanouis))
//testing("nanomanager/user_transferred mob [oldMob.name] has no open uis")
return 0 // has no open uis
for (var/thing in oldMob.open_nanouis)
var/datum/nanoui/ui = thing
ui.user = newMob
LAZYADD(newMob.open_nanouis, ui)
oldMob.open_nanouis = null
return 1 // success
/datum/asset/nanoui
var/list/common = list()
var/list/common_dirs = list(
"nano/css/",
"nano/js/",
"nano/images/",
"nano/images/status_icons/",
"nano/templates/"
)
var/list/uncommon_dirs = list()
/datum/asset/nanoui/register()
// Crawl the directories to find files.
for (var/path in common_dirs)
var/list/filenames = flist(path)
for(var/filename in filenames)
if(copytext(filename, length(filename)) != "/") // Ignore directories.
if(fexists(path + filename))
common[filename] = fcopy_rsc(path + filename)
SSassets.transport.register_asset(filename, common[filename])
for (var/path in uncommon_dirs)
var/list/filenames = flist(path)
for(var/filename in filenames)
if(copytext(filename, length(filename)) != "/") // Ignore directories.
if(fexists(path + filename))
SSassets.transport.register_asset(filename, fcopy_rsc(path + filename))
/datum/asset/nanoui/send(client, uncommon)
if(!islist(uncommon))
uncommon = list(uncommon)
SSassets.transport.send_assets(client, uncommon)
SSassets.transport.send_assets(client, common)
+1 -1
View File
@@ -213,7 +213,7 @@ SUBSYSTEM_DEF(records)
return GLOB.always_state
/datum/controller/subsystem/records/ui_status(mob/user, datum/ui_state/state)
return (isnewplayer(user) || isghost(user) || issilicon(user)) ? UI_INTERACTIVE : UI_CLOSE
return (isnewplayer(user) || isghost(user) || tgui_silicon_user(user)) ? UI_INTERACTIVE : UI_CLOSE
/datum/controller/subsystem/records/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
+8
View File
@@ -186,6 +186,11 @@ SUBSYSTEM_DEF(statpanels)
atoms_to_display += turf_content
/// Set the atoms we're meant to display
// Lazy-init: SSstatpanels can fire for a client whose obj_window was never
// created (mob transfer / relogin) or was nulled by /datum/object_window_info/Destroy()
// while mob.listed_turf survived. Without this guard we'd null-deref atoms_to_show.
if(!target.obj_window)
target.obj_window = new /datum/object_window_info(target)
var/datum/object_window_info/obj_window = target.obj_window
obj_window.atoms_to_show = atoms_to_display
START_PROCESSING(SSobj_tab_items, obj_window)
@@ -210,6 +215,9 @@ SUBSYSTEM_DEF(statpanels)
// No turf? go away
if(!load_from.mob?.listed_turf)
return list()
// Lazy-init for the same lifecycle reasons as set_turf_examine_tab
if(!load_from.obj_window)
load_from.obj_window = new /datum/object_window_info(load_from)
var/datum/object_window_info/obj_window = load_from.obj_window
var/list/already_seen = obj_window.atoms_to_images
var/list/to_make = obj_window.atoms_to_imagify
@@ -10,11 +10,11 @@
*
*/
PROCESSING_SUBSYSTEM_DEF(tgui)
SUBSYSTEM_DEF(tgui)
name = "tgui"
wait = 9
flags = SS_NO_INIT
priority = SS_PRIORITY_NANOUI
priority = FIRE_PRIORITY_TGUI
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
/// A list of UIs scheduled to process
@@ -24,54 +24,39 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
/// The HTML base used for all UIs.
var/basehtml
/datum/controller/subsystem/processing/tgui/PreInit()
basehtml = file2text('tgui/public/tgui.html')
// Inject inline polyfills
var/polyfill = file2text('tgui/public/tgui-polyfill.min.js')
polyfill = "<script>\n[polyfill]\n</script>"
basehtml = replacetextEx(basehtml, "<!-- tgui:inline-polyfill -->", polyfill)
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "NanoTrasen © 2457-[text2num(time2text(world.realtime, "YYYY")) + 442]")
/datum/controller/subsystem/tgui/PreInit()
basehtml = file2text("tgui/public/tgui.html")
basehtml = replacetext(basehtml, "tgui:stylesheet", MAP_STYLESHEET)
// Inject inline helper functions
var/helpers = file2text("tgui/public/helpers.min.js")
helpers = "<script type='text/javascript'>\n[helpers]\n</script>"
basehtml = replacetextEx(basehtml, "<!-- tgui:helpers -->", helpers)
/datum/controller/subsystem/processing/tgui/OnConfigLoad()
var/storage_iframe = GLOB.config.storage_cdn_iframe
if(storage_iframe && storage_iframe != /datum/configuration::storage_cdn_iframe)
basehtml = replacetextEx(basehtml, "tgui:storagecdn", storage_iframe)
return
// Inject inline ntos-error styles
var/ntos_error = file2text("tgui/public/ntos-error.min.css")
ntos_error = "<style type='text/css'>\n[ntos_error]\n</style>"
basehtml = replacetextEx(basehtml, "<!-- tgui:ntos-error -->", ntos_error)
if(GLOB.config.asset_transport == "webroot")
var/datum/asset_transport/webroot/webroot = SSassets.transport
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "Nanotrasen (c) 2395-[num2text(GLOB.game_year)]")
var/datum/asset_cache_item/item = webroot.register_asset("iframe.html", file("tgui/public/iframe.html"))
basehtml = replacetext(basehtml, "tgui:storagecdn", webroot.get_asset_url("iframe.html", item))
return
if(!storage_iframe)
return
basehtml = replacetextEx(basehtml, "tgui:storagecdn", storage_iframe)
/datum/controller/subsystem/processing/tgui/Shutdown()
/datum/controller/subsystem/tgui/Shutdown()
close_all_uis()
/datum/controller/subsystem/processing/tgui/stat_entry(msg)
/datum/controller/subsystem/tgui/stat_entry(msg)
msg = "P:[length(all_uis)]"
return ..()
/datum/controller/subsystem/processing/tgui/fire(resumed = FALSE)
CAN_BE_REDEFINED(TRUE)
/datum/controller/subsystem/tgui/fire(resumed = FALSE)
if(!resumed)
src.current_run = all_uis.Copy()
// Cache for sanic speed (lists are references anyways)
var/list/current_run = src.current_run
var/seconds_per_tick = wait * 0.1
while(current_run.len)
var/datum/tgui/ui = current_run[current_run.len]
current_run.len--
// TODO: Move user/src_object check to process()
if(ui?.user && ui.src_object)
ui.process(seconds_per_tick)
ui.process(wait * 0.1)
else
ui.close(0)
if(MC_TICK_CHECK)
@@ -84,9 +69,9 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
* Returns null if pool was exhausted.
*
* required user mob
* return datum/tgui
* return datum/tgui_window
*/
/datum/controller/subsystem/processing/tgui/proc/request_pooled_window(mob/user)
/datum/controller/subsystem/tgui/proc/request_pooled_window(mob/user)
if(!user.client)
return null
var/list/windows = user.client.tgui_windows
@@ -122,7 +107,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* required user mob
*/
/datum/controller/subsystem/processing/tgui/proc/force_close_all_windows(mob/user)
/datum/controller/subsystem/tgui/proc/force_close_all_windows(mob/user)
log_tgui(user, context = "SStgui/force_close_all_windows")
if(user.client)
user.client.tgui_windows = list()
@@ -138,14 +123,12 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
* required user mob
* required window_id string
*/
/datum/controller/subsystem/processing/tgui/proc/force_close_window(mob/user, window_id)
/datum/controller/subsystem/tgui/proc/force_close_window(mob/user, window_id)
log_tgui(user, context = "SStgui/force_close_window")
// Close all tgui datums based on window_id.
for(var/datum/tgui/ui in user.tgui_open_uis)
if(ui.window && ui.window.id == window_id)
ui.close(can_be_suspended = FALSE)
// Unset machine just to be sure.
user.unset_machine()
// Close window directly just to be sure.
user << browse(null, "window=[window_id]")
@@ -161,7 +144,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return datum/tgui The found UI.
*/
/datum/controller/subsystem/processing/tgui/proc/try_update_ui(
/datum/controller/subsystem/tgui/proc/try_update_ui(
mob/user,
datum/src_object,
datum/tgui/ui)
@@ -191,7 +174,8 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return datum/tgui The found UI.
*/
/datum/controller/subsystem/processing/tgui/proc/get_open_ui(mob/user, datum/src_object)
/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object)
// No UIs opened for this src_object
if(!LAZYLEN(src_object?.open_uis))
return null
for(var/datum/tgui/ui in src_object.open_uis)
@@ -209,15 +193,15 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return int The number of UIs updated.
*/
/datum/controller/subsystem/processing/tgui/proc/update_uis(datum/src_object)
/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
// No UIs opened for this src_object
if(!LAZYLEN(src_object?.open_uis))
return 0
var/count = 0
var/seconds_per_tick = wait * 0.1
for(var/datum/tgui/ui in src_object.open_uis)
// Check if UI is valid.
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
INVOKE_ASYNC(ui, TYPE_PROC_REF(/datum/tgui, process), seconds_per_tick, TRUE)
INVOKE_ASYNC(ui, TYPE_PROC_REF(/datum/tgui, process), wait * 0.1, TRUE)
count++
return count
@@ -230,7 +214,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return int The number of UIs closed.
*/
/datum/controller/subsystem/processing/tgui/proc/close_uis(datum/src_object)
/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
// No UIs opened for this src_object
if(!LAZYLEN(src_object?.open_uis))
return 0
@@ -249,7 +233,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return int The number of UIs closed.
*/
/datum/controller/subsystem/processing/tgui/proc/close_all_uis()
/datum/controller/subsystem/tgui/proc/close_all_uis()
var/count = 0
for(var/datum/tgui/ui in all_uis)
// Check if UI is valid.
@@ -268,14 +252,13 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return int The number of UIs updated.
*/
/datum/controller/subsystem/processing/tgui/proc/update_user_uis(mob/user, datum/src_object)
/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object)
var/count = 0
if(length(user?.tgui_open_uis) == 0)
return count
var/seconds_per_tick = wait * 0.1
for(var/datum/tgui/ui in user.tgui_open_uis)
if(isnull(src_object) || ui.src_object == src_object)
ui.process(seconds_per_tick, force = 1)
ui.process(wait * 0.1, force = 1)
count++
return count
@@ -289,7 +272,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return int The number of UIs closed.
*/
/datum/controller/subsystem/processing/tgui/proc/close_user_uis(mob/user, datum/src_object)
/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object)
var/count = 0
if(length(user?.tgui_open_uis) == 0)
return count
@@ -306,7 +289,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* required ui datum/tgui The UI to be added.
*/
/datum/controller/subsystem/processing/tgui/proc/on_open(datum/tgui/ui)
/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
ui.user?.tgui_open_uis |= ui
LAZYOR(ui.src_object.open_uis, ui)
all_uis |= ui
@@ -320,7 +303,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return bool If the UI was removed or not.
*/
/datum/controller/subsystem/processing/tgui/proc/on_close(datum/tgui/ui)
/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
// Remove it from the list of processing UIs.
all_uis -= ui
current_run -= ui
@@ -340,7 +323,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return int The number of UIs closed.
*/
/datum/controller/subsystem/processing/tgui/proc/on_logout(mob/user)
/datum/controller/subsystem/tgui/proc/on_logout(mob/user)
close_user_uis(user)
/**
@@ -353,7 +336,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
*
* return bool If the UIs were transferred.
*/
/datum/controller/subsystem/processing/tgui/proc/on_transfer(mob/source, mob/target)
/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
// The old mob had no open UIs.
if(length(source?.tgui_open_uis) == 0)
return FALSE
@@ -55,11 +55,6 @@ SUBSYSTEM_DEF(virtualreality)
if(bound && network)
bounded[network].Remove(bound)
/mob
var/mob/living/vr_mob = null // In which mob is our mind
var/mob/living/old_mob = null // Which mob is our old mob
/// Return to original body
/mob/proc/body_return()
set name = "Return to Body"
+16
View File
@@ -0,0 +1,16 @@
/// Stores information about a chat payload
/datum/chat_payload
/// Sequence number of this payload
var/sequence = 0
/// Message we are sending
var/list/content
/// Resend count
var/resends = 0
/// Converts the chat payload into a JSON string
/datum/chat_payload/proc/into_message()
return "{\"sequence\":[sequence],\"content\":[json_encode(content)]}"
/// Returns an HTML-encoded message from our contents.
/datum/chat_payload/proc/get_content_as_html()
return message_to_html(content)
@@ -23,7 +23,7 @@
var/key = get_armor_key(damage_type, damage_flags)
var/damage_taken
if(blocked)
damage_taken = round(damage * blocked)
damage_taken = damage * (1 - blocked)
else
damage_taken = damage
var/new_armor = max(0, get_value(key) - armor_degradation_coef * damage_taken)
+6 -2
View File
@@ -95,7 +95,9 @@
shift_pixel_x = 10
animate(src, pixel_x = shift_pixel_x, pixel_y = shift_pixel_y, time = 1)
if(direction == NORTH)
src.add_filter("cutout", 1, alpha_mask_filter(icon = icon('icons/effects/effects.dmi', "cutout")))
src.appearance_flags |= KEEP_TOGETHER
src.add_filter("cutout", 1, alpha_mask_filter(y = 0, icon = icon('icons/effects/effects.dmi', "white")))
animate(src.get_filter("cutout"), y = 10, time = 1, flags = ANIMATION_PARALLEL)
if(direction == SOUTH)
src.layer = ABOVE_DOOR_LAYER
set_density(FALSE)
@@ -105,7 +107,7 @@
SPAN_NOTICE("[src] leans against [lean_target]."),
SPAN_NOTICE("You lean against [lean_target]."),
)
RegisterSignals(src, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_RESISTED), PROC_REF(stop_leaning), src)
RegisterSignals(src, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_RESISTED, COMSIG_MOB_LYING_DOWN), PROC_REF(stop_leaning), src)
/mob/living/proc/stop_leaning()
SIGNAL_HANDLER
@@ -116,11 +118,13 @@
UnregisterSignal(src, list(
COMSIG_MOVABLE_MOVED,
COMSIG_MOB_RESISTED,
COMSIG_MOB_LYING_DOWN,
))
SEND_SIGNAL(src, COMSIG_LIVING_STOPPED_LEANING)
to_chat(src, SPAN_NOTICE("You stop leaning on the wall."))
REMOVE_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_WALL_LEANING)
REMOVE_TRAIT(src, TRAIT_LEANING, TRAIT_SOURCE_WALL_LEANING)
appearance_flags &= ~KEEP_TOGETHER
remove_filter("cutout")
/datum/component/leanable/proc/stopped_leaning(datum/source)
-5
View File
@@ -47,11 +47,6 @@
lan = null
return lan
/datum/component/local_network_member/nano_host()
if(parent)
return parent.nano_host()
. = ..()
/datum/component/local_network_member/proc/get_new_tag(mob/user)
var/new_ident = input(user, "Enter a new ident tag.", "[parent]", id_tag) as null|text
if(new_ident && parent && user.Adjacent(parent) && CanInteract(user, GLOB.physical_state))
@@ -84,6 +84,15 @@
reagent_results = list()
return TRUE
/datum/component/health_analyzer/ui_status(mob/user, datum/ui_state/state)
var/obj/item/rig_module/containing_rig_module = owner?.loc
if(!containing_rig_module)
return ..()
else if(containing_rig_module)
return UI_INTERACTIVE
return UI_CLOSE
/datum/component/health_analyzer/proc/attack(mob/living/target_mob, mob/living/user, target_zone)
sound_scan = TRUE
@@ -104,6 +113,8 @@
user.visible_message("\The [user] stops scanning \the [target_mob].")
/datum/component/health_analyzer/proc/attack_self(mob/user)
if(scan_title == "" || scan_title == null)
health_scan_mob(user, user, FALSE, sound_scan = sound_scan)
ui_interact(user)
owner.add_fingerprint(user)
@@ -39,8 +39,8 @@ ABSTRACT_TYPE(/singleton/overhead_emote)
var/self_message = SPAN_NOTICE("You lift your hand to meet [original]'s, delivering a stunning [emote_description]!")
reciprocator.visible_message(others_message, self_message)
INVOKE_ASYNC(reciprocator, TYPE_PROC_REF(/atom/movable, do_attack_animation), original, FIST_ATTACK_ANIMATION)
INVOKE_ASYNC(original, TYPE_PROC_REF(/atom/movable, do_attack_animation), reciprocator, FIST_ATTACK_ANIMATION)
INVOKE_ASYNC(reciprocator, TYPE_PROC_REF(/atom/movable, do_attack_animation), original)
INVOKE_ASYNC(original, TYPE_PROC_REF(/atom/movable, do_attack_animation), reciprocator)
if(emote_sound)
playsound(reciprocator.loc, emote_sound, 30, 1)
@@ -54,8 +54,8 @@ ABSTRACT_TYPE(/singleton/overhead_emote)
var/self_message = SPAN_NOTICE("You lift your hand to meet [original]'s, who was expecting a [original_emote.emote_description], but received a [fail_emote.emote_description] instead.")
reciprocator.visible_message(others_message, self_message)
INVOKE_ASYNC(reciprocator, TYPE_PROC_REF(/atom/movable, do_attack_animation), original, FIST_ATTACK_ANIMATION)
INVOKE_ASYNC(original, TYPE_PROC_REF(/atom/movable, do_attack_animation), reciprocator, FIST_ATTACK_ANIMATION)
INVOKE_ASYNC(reciprocator, TYPE_PROC_REF(/atom/movable, do_attack_animation), original)
INVOKE_ASYNC(original, TYPE_PROC_REF(/atom/movable, do_attack_animation), reciprocator)
playsound(reciprocator.loc, SFX_PUNCH_MISS, 30, 1)
@@ -63,7 +63,7 @@
return
to_chat(minister, SPAN_BAD("Why should you care how [ministree] feels?"))
*moodlet_value = 0
*moodlet_value = *moodlet_value * 0.5
/datum/component/timed_life/psiblock_drugs/proc/modify_ministry_receiving(ministree, minister, moodlet_value)
SIGNAL_HANDLER
@@ -71,7 +71,7 @@
return
to_chat(ministree, SPAN_BAD("You feel nothing from [minister]'s words."))
*moodlet_value = 0
*moodlet_value = *moodlet_value * 0.5
/datum/component/timed_life/psiblock_drugs/proc/modify_leadership_empathy(leader, moodlet_value)
SIGNAL_HANDLER
@@ -79,7 +79,7 @@
return
to_chat(leader, SPAN_BAD("Why should you care about how others feel?"))
*moodlet_value = 0
*moodlet_value = *moodlet_value * 0.5
/datum/component/timed_life/psiblock_drugs/process(seconds_per_tick)
@@ -23,7 +23,7 @@
log_debug("Synthetic endoskeleton component spawned on non-IPC. Deleting.")
qdel_self()
RegisterSignal(owner, COMSIG_EXTERNAL_ORGAN_DAMAGE, PROC_REF(receive_damage))
RegisterSignal(owner, COMSIG_DAMAGE_TO_ENDOSKELETON, PROC_REF(receive_damage))
RegisterSignal(owner, COMSIG_SYNTH_ENDOSKELETON_REPAIR, PROC_REF(heal_damage))
RegisterSignal(owner, COMSIG_SYNTH_ENDOSKELETON_FULL_REPAIR, PROC_REF(full_repair))
@@ -55,6 +55,7 @@
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 1)
if(0.75 to 1)
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 1.5)
SEND_SIGNAL(owner, COMSIG_SYNTH_SET_SELF_PRESERVATION, FALSE)
if(!damage)
STOP_PROCESSING(SSprocessing, src)
-11
View File
@@ -27,17 +27,6 @@
#define langchat_client_enabled(M) (M && M.client && M.client.prefs && (M.client.prefs.toggles_secondary & FLOATING_MESSAGES))
/*
* Duplicate vars and logic created for untranslated images for the sake of getting an untranslated langchat to display for listeners who do not understand
* the language being spoken. Someone could certainly think of cleaner ways to do this, but for want of a better solution right now, it has been implemented
* in this rote manner to make it easier to strip out in future if it needs replaced.
*/
/atom/var/image/langchat_image
/atom/var/image/langchat_image_untranslated
/atom/var/list/mob/langchat_listeners
/atom/var/list/mob/langchat_listeners_untranslated
/// Hides the images, if they exist. Do not null the langchat images; they are rotated when the mob is buckled or proned to maintain text orientation.
/atom/proc/langchat_drop_images()
if(langchat_listeners)
-1
View File
@@ -94,7 +94,6 @@
current.remove_vampire_powers()
current.mind = null
SSnanoui.user_transferred(current, new_character)
SStgui.on_transfer(current, new_character)
if(current.client && GLOB.ticket_panels[current.client])
var/datum/ticket_panel/tp = GLOB.ticket_panels[current.client]
+23
View File
@@ -0,0 +1,23 @@
/obj/outfit/admin/oe_enforcer
name = "Outer Eyes Enforcer"
uniform = /obj/item/clothing/under/color/purple
shoes = /obj/item/clothing/shoes/jackboots
gloves = /obj/item/clothing/gloves/black
suit = /obj/item/clothing/suit/space/void/outereyes
suit_store = /obj/item/gun/projectile/automatic/rifle/dnac
mask = /obj/item/clothing/mask/balaclava
belt = /obj/item/storage/belt/military
l_ear = /obj/item/radio/headset/raider
head = /obj/item/clothing/head/helmet/space/void/outereyes
r_pocket = /obj/item/melee/energy/sword/knife
l_pocket = /obj/item/coin/phoron
accessory = /obj/item/clothing/accessory/holster/hip
accessory_contents = list(/obj/item/gun/projectile/xanupistol = 1)
belt_contents = list(
/obj/item/grenade/flashbang = 2,
/obj/item/ammo_magazine/a65 = 3,
/obj/item/ammo_magazine/c46m/extended = 2
)
+28 -2
View File
@@ -69,12 +69,38 @@
/singleton/skill/anatomy
name = "Anatomy"
description = "Not currently implemented."
description = "Governs the speed at which you can check yourself for injuries, as well as the quality of the information obtained when doing so."
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_TRAINED
uneducated_skill_cap = SKILL_LEVEL_PROFESSIONAL
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_MEDICAL
component_type = ANATOMY_SKILL_COMPONENT
skill_level_descriptions = alist(
SKILL_LEVEL_UNFAMILIAR = "You have zero training or knowledge of anatomy.<br>" \
+ " - You can only distinguish visible damage like cuts and burns.",
SKILL_LEVEL_FAMILIAR = "You have minimal training on the basics of anatomy.<br>" \
+ "You can diagnose the following conditions: <br>" \
+ " - External injuries.<br>" \
+ " - Limb dislocation.<br>" \
+ " - Simple bleeds.",
SKILL_LEVEL_TRAINED = "You have years of formal training and experience with anatomy.<br>" \
+ "You can diagnose the following conditions: <br>" \
+ " - External injuries at a high level of detail.<br>" \
+ " - Limb dislocation.<br>" \
+ " - Broken bones.<br>" \
+ " - Distinguish between simple bleeds and severed arteries.<br>" \
+ " - Limb necrosis.<br>" \
+ " - Tag injuries by triage priority.",
SKILL_LEVEL_PROFESSIONAL = "You have extensive training and experience with anatomy.<br>" \
+ "You can diagnose the following conditions at the highest level of detail: <br>" \
+ " - External injuries at a high level of detail.<br>" \
+ " - Limb dislocation.<br>" \
+ " - Broken bones.<br>" \
+ " - Distinguish between simple bleeds and severed arteries.<br>" \
+ " - Limb necrosis.<br>" \
+ " - Tag injuries by triage priority."
)
required = TRUE
/singleton/skill/forensics
name = "Forensics"
-169
View File
@@ -38,11 +38,6 @@
icon_state = "tank_midriff"
has_color = TRUE
/datum/category_item/underwear/undershirt/tank_top_midriff_short
name = "Tank Top, Midriff Short"
icon_state = "tank_midriff_short"
has_color = TRUE
/datum/category_item/underwear/undershirt/tank_top_expedition
name = "Tank Top, Expedition"
icon_state = "tank_expedition"
@@ -59,14 +54,6 @@
name = "Tank Top, Fleet Feminine"
icon_state = "tank_fleet_fem"
/datum/category_item/underwear/undershirt/tank_top_fire
name = "Tank Top, Fire"
icon_state = "tank_fire"
/datum/category_item/underwear/undershirt/tank_top_fire_fem
name = "Tank Top, Fire Feminine"
icon_state = "tank_fire_fem"
/datum/category_item/underwear/undershirt/tank_top_stripes
name = "Tank Top, Stripes"
icon_state = "tank_stripes"
@@ -83,22 +70,6 @@
name = "Tank Top, Stripes Alt Feminine"
icon_state = "tank_stripes_alt_fem"
/datum/category_item/underwear/undershirt/tank_top_sun
name = "Tank Top, Sun"
icon_state = "tank_sun"
/datum/category_item/underwear/undershirt/tank_top_sun_fem
name = "Tank Top, Sun Feminine"
icon_state = "tank_sun_fem"
/datum/category_item/underwear/undershirt/tank_top_sun_alt
name = "Tank Top, Sun Alt"
icon_state = "tank_sun_alt"
/datum/category_item/underwear/undershirt/tank_top_sun_alt_fem
name = "Tank Top, Sun Alt Feminine"
icon_state = "tank_sun_alt_fem"
/datum/category_item/underwear/undershirt/tank_top_tight
name = "Tank Top, Tight"
icon_state = "tank_tight"
@@ -137,95 +108,11 @@
icon_state = "undershirt_fem"
has_color = TRUE
/datum/category_item/underwear/undershirt/undershirt_heart
name = "Undershirt, Heart"
icon_state = "heart"
/datum/category_item/underwear/undershirt/undershirt_heart_fem
name = "Undershirt, Heart Feminine"
icon_state = "heart_fem"
/datum/category_item/underwear/undershirt/undershirt_heart_echelon
name = "Undershirt, Heart Echelon"
icon_state = "heart_echelon"
/datum/category_item/underwear/undershirt/undershirt_heart_echelon_fem
name = "Undershirt, Heart Echelon Feminine"
icon_state = "heart_echelon_fem"
/datum/category_item/underwear/undershirt/shirt_love_nt
name = "Undershirt, I<3NT"
icon_state = "ilovent"
/datum/category_item/underwear/undershirt/shirt_love_nt_fem
name = "Undershirt, I<3NT Feminine"
icon_state = "ilovent_fem"
/datum/category_item/underwear/undershirt/shirt
name = "Shirt"
icon_state = "shirt"
has_color = TRUE
/datum/category_item/underwear/undershirt/shirt_alien
name = "Shirt, Alien"
icon_state = "shirt_alien"
/datum/category_item/underwear/undershirt/shirt_band
name = "Shirt, Band"
icon_state = "shirt_band"
/datum/category_item/underwear/undershirt/shirt_clown
name = "Shirt, Clown"
icon_state = "shirt_clown"
/datum/category_item/underwear/undershirt/shirt_ian
name = "Shirt, Ian"
icon_state = "shirt_ian"
/datum/category_item/underwear/undershirt/shirt_jersey
name = "Shirt, Blue Jersey"
icon_state = "shirt_jersey"
/datum/category_item/underwear/undershirt/shirt_jersey_red
name = "Shirt, Red Jersey"
icon_state = "shirt_jersey_red"
/datum/category_item/underwear/undershirt/shirt_meat
name = "Shirt, Meat"
icon_state = "shirt_meat"
/datum/category_item/underwear/undershirt/shirt_scc
name = "Shirt, SCC"
icon_state = "shirt_scc"
/datum/category_item/underwear/undershirt/shirt_scc_fem
name = "Shirt, SCC feminine"
icon_state = "shirt_scc_fem"
/datum/category_item/underwear/undershirt/shirt_nt
name = "Shirt, NT"
icon_state = "shirt_nano"
/datum/category_item/underwear/undershirt/shirt_nt_red
name = "Shirt, NT Red"
icon_state = "shirt_nano_red"
/datum/category_item/underwear/undershirt/shirt_peace
name = "Shirt, Peace"
icon_state = "shirt_peace"
/datum/category_item/underwear/undershirt/shirt_question
name = "Shirt, Question"
icon_state = "shirt_question"
/datum/category_item/underwear/undershirt/shirt_skull
name = "Shirt, Skull"
icon_state = "shirt_skull"
/datum/category_item/underwear/undershirt/shirt_ss13
name = "Shirt, SS13"
icon_state = "shirt_ss13"
/datum/category_item/underwear/undershirt/shirt_blue_striped
name = "Shirt, Blue Stripes"
icon_state = "shirt_stripes"
@@ -234,34 +121,10 @@
name = "Shirt, Blue Stripes Alt"
icon_state = "shirt_stripes_alt"
/datum/category_item/underwear/undershirt/shirt_tiedye
name = "Shirt, Tiedye"
icon_state = "shirt_tiedye"
/datum/category_item/underwear/undershirt/shirt_tight
name = "Shirt, Tight"
icon_state = "tight"
/datum/category_item/underwear/undershirt/shirt_wing
name = "Shirt, Wing"
icon_state = "wing_shirt"
/datum/category_item/underwear/undershirt/shirt_pinkblack
name = "Shirt, Pink-Black"
icon_state = "pinkblack_tshirt"
/datum/category_item/underwear/undershirt/sport_shirt_blue
name = "Sport Shirt, Blue"
icon_state = "sportblue"
/datum/category_item/underwear/undershirt/sport_shirt_green
name = "Sport Shirt, Green"
icon_state = "sportgreen"
/datum/category_item/underwear/undershirt/sport_shirt_red
name = "Sport Shirt, Red"
icon_state = "sportred"
/datum/category_item/underwear/undershirt/shirt_army
name = "Shirt, Army"
icon_state = "shirt_army"
@@ -286,38 +149,6 @@
name = "Shirt, Fleet Feminine"
icon_state = "shirt_fleet_fem"
/datum/category_item/underwear/undershirt/shirt_zavodskoi
name = "Shirt, Zavodskoi Interstellar"
icon_state = "shirt_necropolis"
/datum/category_item/underwear/undershirt/shirt_zavodskoi_fem
name = "Shirt, Zavodskoi Interstellar Feminine"
icon_state = "shirt_necropolis_fem"
/datum/category_item/underwear/undershirt/shirt_epmc
name = "Shirt, EPMC"
icon_state = "shirt_epmc"
/datum/category_item/underwear/undershirt/shirt_epmc_fem
name = "Shirt, EPMC Feminine"
icon_state = "shirt_epmc_fem"
/datum/category_item/underwear/undershirt/bowlinga
name = "Bowling Shirt, Aqua"
icon_state = "bowlinga"
/datum/category_item/underwear/undershirt/bowlingp
name = "Bowling Shirt, Pink"
icon_state = "bowlingp"
/datum/category_item/underwear/undershirt/bowlingr
name = "Bowling Shirt, Red"
icon_state = "bowlingr"
/datum/category_item/underwear/undershirt/bowlingw
name = "Bowling Shirt, White"
icon_state = "bowlingw"
/datum/category_item/underwear/undershirt/longjon
name = "Long John Shirt"
icon_state = "ljont"
+7 -7
View File
@@ -123,11 +123,11 @@
path = /obj/item/ammo_magazine/super_heavy
desc = "A spare magazine, for the super heavy K2557 pistol."
/datum/uplink_item/item/ammo/shotgun_slug
name = "Shotgun Slug"
telecrystal_cost = 1
path = /obj/item/ammo_casing/shotgun
desc = "A shotgun slug."
/datum/uplink_item/item/ammo/shotgun_slugs
name = "Box of Slug Shells"
telecrystal_cost = 2
path = /obj/item/storage/box/shells/slugs
desc = "Contains eight shotgun slug shells."
/datum/uplink_item/item/ammo/tungsten_ammo_box
name = "Tungsten Ammo Box"
@@ -137,13 +137,13 @@
/datum/uplink_item/item/ammo/slug_magazine
name = "Slug Magazine"
telecrystal_cost = 8
telecrystal_cost = 2
path = /obj/item/ammo_magazine/assault_shotgun
desc = "A magazine for an assault shotgun, loaded with slug shells."
/datum/uplink_item/item/ammo/buckshot_magazine
name = "Buckshot Magazine"
telecrystal_cost = 4
telecrystal_cost = 1
path = /obj/item/ammo_magazine/assault_shotgun/shells
desc = "A magazine for an assault shotgun, loaded with buckshot shells."
+1 -1
View File
@@ -54,7 +54,7 @@
record.sex = I.sex
record.employer = I.employer_faction
var/datum/faction/id_faction = SSjobs.name_factions[I.employer_faction]
var/faction_abbreviation = id_faction.title_suffix
var/faction_abbreviation = id_faction?.title_suffix
var/assignment = "[I.assignment][ faction_abbreviation ? " ([faction_abbreviation])" : ""]"
record.rank = assignment
record.real_rank = assignment
@@ -104,13 +104,6 @@
path = /obj/item/rig_module/storage
desc = "A small hardsuit mounted storage unit, capable of holding a few items."
/datum/uplink_item/item/hardsuit_modules/vitalscanner
name = "Vitals Tracker Module"
telecrystal_cost = 1
bluecrystal_cost = 1
path = /obj/item/rig_module/device/healthscanner/vitalscanner
desc = "A module that allows a hardsuit to scan the users vitals, and give readouts of them."
/datum/uplink_item/item/hardsuit_modules/basicinjector
name = "Emergency Chemical Injector"
telecrystal_cost = 2
+10 -3
View File
@@ -6,7 +6,8 @@
wires = list(
WIRE_POWER,
WIRE_IDSCAN, WIRE_AI,
WIRE_PANIC, WIRE_ALARM
WIRE_PANIC, WIRE_ALARM,
WIRE_RCON
)
add_duds(3)
..()
@@ -24,6 +25,7 @@
. += A.locked ? "The air alarm is locked." : "The air alarm is unlocked."
. += (A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "The Air Alarm is offline." : "The Air Alarm is working properly!"
. += A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on."
. += A.rcon_setting ? "The remote control link is enabled." : "The remote control link is disabled."
/datum/wires/alarm/on_cut(wire, mend, source)
var/obj/structure/machinery/alarm/A = holder
@@ -51,6 +53,12 @@
A.post_alert(2)
A.update_icon()
if(WIRE_RCON)
if(!mend)
A.rcon_setting = FALSE
else
A.rcon_setting = TRUE
/datum/wires/alarm/on_pulse(wire)
var/obj/structure/machinery/alarm/A = holder
switch(wire)
@@ -67,7 +75,6 @@
A.shorted = 0
A.update_icon()
if (WIRE_AI)
if (A.aidisabled == 0)
A.aidisabled = 1
@@ -83,7 +90,7 @@
A.mode = 1 // AALARM_MODE_SCRUB
A.apply_mode()
if(WIRE_ALARM)
if(WIRE_ALARM, WIRE_RCON)
if (A.alarm_area.atmosalert(0, A))
A.post_alert(0)
A.update_icon()
+2 -2
View File
@@ -31,7 +31,7 @@
if(WIRE_TRANSMIT)
R.set_broadcasting(!R.get_broadcasting() && !is_cut(WIRE_SIGNAL))
SSnanoui.update_uis(holder)
SStgui.update_uis(holder)
/datum/wires/radio/on_cut(wire, mend, source)
var/obj/item/radio/R = holder
@@ -45,4 +45,4 @@
if(WIRE_TRANSMIT)
R.set_broadcasting(mend && !is_cut(WIRE_SIGNAL))
SSnanoui.update_uis(holder)
SStgui.update_uis(holder)
+1 -1
View File
@@ -215,7 +215,7 @@
target_mob.standard_weapon_hit_effects(src, user, damageamount, armorpercent, target_zone)
user.visible_message("<span class='[class]'>[endmessage3rd][punct]</span>", "<span class='[class]'>[endmessage1st][punct]</span>")
user.do_attack_animation(target_mob)
user.do_attack_animation(target_mob, used_item = src)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(soundname)
+22 -6
View File
@@ -138,19 +138,31 @@
/// This atom's cache of overlays that can only be removed explicitly, like C4. Do not manipulate directly- See SSoverlays.
var/list/atom_protected_overlay_cache
/// The mob currently interacting with the atom during a `do_after` timer. Used to validate `DO_TARGET_UNIQUE_ACT` flag checks.
var/mob/do_unique_target_user
/*
* Duplicate vars and logic created for untranslated images for the sake of getting an untranslated langchat to display for listeners who do not understand
* the language being spoken. Someone could certainly think of cleaner ways to do this, but for want of a better solution right now, it has been implemented
* in this rote manner to make it easier to strip out in future if it needs replaced.
*/
var/image/langchat_image
var/image/langchat_image_untranslated
var/list/mob/langchat_listeners
var/list/mob/langchat_listeners_untranslated
/atom/Destroy(force)
if(opacity)
updateVisibility(src)
if(reagents)
QDEL_NULL(reagents)
QDEL_NULL(reagents)
// Checking length(overlays) before cutting has significant speed benefits
if(length(overlays))
overlays.Cut()
if(light)
QDEL_NULL(light)
QDEL_NULL(light)
if(smoothing_flags & SMOOTH_QUEUED)
SSicon_smooth.remove_from_queues(src)
@@ -167,8 +179,12 @@
// The component is attached to us normaly and will be deleted elsewhere
orbiters = null
. = ..()
do_unique_target_user = null
QDEL_NULL(langchat_image)
QDEL_NULL(langchat_image_untranslated)
langchat_listeners?.Cut()
langchat_listeners_untranslated?.Cut()
return ..()
/atom/proc/handle_ricochet(obj/projectile/ricocheting_projectile)
var/turf/p_turf = get_turf(ricocheting_projectile)
+38
View File
@@ -87,6 +87,15 @@
if(src.desc)
. += src.desc
var/list/tags_list = examine_tags(user)
if(length(tags_list))
var/tag_string = list()
for (var/atom_tag in tags_list)
tag_string += (isnull(tags_list[atom_tag]) ? atom_tag : SPAN_TOOLTIP(tags_list[atom_tag], atom_tag))
// some regex to ensure that we don't add another "and" if the final element's main text (not tooltip) has one
tag_string = english_list(tag_string, and_text = (findtext(tag_string[length(tag_string)], regex(@">.*?and .*?<"))) ? " " : " and ")
. += "It is a [tag_string] [examine_descriptor(user)]."
// Returns a SPAN_* based on health, if configured.
var/list/condition_hints = src.condition_hints()
if(length(condition_hints))
@@ -145,6 +154,35 @@
if(H.glasses)
H.glasses.glasses_examine_atom(src, H)
/// What this atom should be called in examine tags
/atom/proc/examine_descriptor(mob/user)
return "object"
/**
* A list of "tags" displayed after atom's description in examine.
* This should return an assoc list of tags -> tooltips for them. If item is null, then no tooltip is assigned.
*
* * TGUI tooltips (not the main text) in chat cannot use HTML stuff at all, so
* trying something like `<b><big>ffff</big></b>` will not work for tooltips.
*
* For example:
* ```byond
* . = list()
* .["small"] = "It is a small item."
* .["fireproof"] = "It is made of fire-retardant materials."
* .["and conductive"] = "It's made of conductive materials and whatnot. Blah blah blah." // having "and " in the end tag's main text/key works too!
* ```
* will result in
*
* It is a *small*, *fireproof* *and conductive* item.
*
* where "item" is pulled from [/atom/proc/examine_descriptor]
*/
/atom/proc/examine_tags(mob/user)
. = list()
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE_TAGS, user, .)
/**
* Used to check if "examine_fluff" from the HTML link in examine() is true, i.e. if it was clicked.
*/
+12
View File
@@ -543,6 +543,18 @@
if(src.z == H.z && get_dist(src, H) <= range)
H.intent_listen(src, message)
/proc/get_intent_listeners(var/atom/source, var/range = 7, var/list/hearers = list())
SHOULD_NOT_SLEEP(TRUE)
var/list/listeners = list()
if(air_sound(source))
if(!length(hearers))
hearers = get_hearers_in_view(range, source)
for(var/mob/living/carbon/human/H as anything in GLOB.intent_listener)
if((H in hearers))
if(source.z == H.z && get_dist(source, H) <= range)
listeners += H
return listeners
/atom/movable/proc/dropInto(var/atom/destination)
while(istype(destination))
var/atom/drop_destination = destination.onDropInto(src)
+203 -4
View File
@@ -87,6 +87,11 @@
/// Whether this atom should have its dir automatically changed when it moves. Setting this to FALSE allows for things such as directional windows to retain dir on moving without snowflake code all of the place.
var/set_dir_on_move = TRUE
var/tmp/turf/airflow_dest
var/tmp/airflow_speed = 0
var/tmp/airflow_time = 0
var/tmp/last_airflow = 0
/atom/movable/Initialize(mapload, ...)
. = ..()
update_emissive_blocker()
@@ -114,14 +119,11 @@
QDEL_LAZYLIST(contained_mobs)
. = ..()
for(var/movable_content in contents)
qdel(movable_content)
//Pretend this is moveToNullspace()
moveToNullspace()
loc = null
//This absolutely must be after moveToNullspace()
//We rely on Entered and Exited to manage this list, and the copy of this list that is on any /atom/movable "Containers"
@@ -150,6 +152,9 @@
QDEL_NULL(light)
QDEL_NULL(static_light)
airflow_dest = null
loc = null
return ..()
/atom/movable/proc/moveToNullspace()
. = TRUE
@@ -577,11 +582,19 @@
/* END Spatial grid stuffs */
for(var/datum/dynamic_light_source/light as anything in hybrid_light_sources)
if(!light) // datum was deleted but list entry not yet pruned
LAZYREMOVE(hybrid_light_sources, light)
continue
if(!light.source_atom)
continue
light.source_atom.update_light()
if(!isturf(loc))
light.find_containing_atom()
for(var/datum/static_light_source/L as anything in static_light_sources) // Cycle through the light sources on this atom and tell them to update.
L.source_atom.static_update_light()
if(!L) // datum was deleted but list entry not yet pruned
LAZYREMOVE(static_light_sources, L)
continue
L.source_atom?.static_update_light()
/atom/movable/Exited(atom/movable/gone, direction)
. = ..()
@@ -817,6 +830,192 @@
// This is instant on byond's end, but to our clients this looks like a quick drop
animate(src, alpha = old_alpha, pixel_x = old_x, pixel_y = old_y, transform = old_transform, time = 3, easing = CUBIC_EASING)
/atom/movable/proc/do_item_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, animation_type)
if (!visual_effect_icon)
if (used_item)
used_item.animate_attack(src, attacked_atom, animation_type)
return
var/image/attack_image = image(icon = 'icons/effects/effects.dmi', icon_state = visual_effect_icon)
attack_image.plane = attacked_atom.plane + 1
// Scale the icon.
attack_image.transform *= 0.4
// The icon should not rotate.
attack_image.appearance_flags = APPEARANCE_UI
var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS)
var/matrix/copy_transform = new(transform)
animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), time = 0.3 SECONDS)
animate(time = 0.1 SECONDS)
animate(alpha = 0, time = 0.3 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
/obj/item/proc/animate_attack(atom/movable/attacker, atom/attacked_atom, animation_type)
var/list/image_override = list()
var/list/animation_override = list()
var/used_icon_angle = icon_angle
var/list/angle_override = list()
SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_ANIMATION, attacker, attacked_atom, animation_type, image_override, animation_override, angle_override)
var/image/attack_image = null
if (!length(image_override))
attack_image = isnull(attack_icon) ? image(icon = src) : image(icon = attack_icon, icon_state = attack_icon_state)
else
attack_image = image_override[1]
if (length(animation_override))
animation_type = animation_override[1]
else if (!animation_type)
var/damage_flags = damage_flags()
if(damage_flags & DAMAGE_FLAG_SHARP|DAMAGE_FLAG_EDGE)
animation_type = pick(ATTACK_ANIMATION_SLASH, ATTACK_ANIMATION_PIERCE)
else if(damage_flags & DAMAGE_FLAG_SHARP)
animation_type = ATTACK_ANIMATION_SLASH
else if (damage_flags & DAMAGE_FLAG_EDGE)
animation_type = ATTACK_ANIMATION_PIERCE
else
animation_type = ATTACK_ANIMATION_BLUNT
if (length(angle_override))
used_icon_angle = angle_override[1]
attack_image.plane = attacked_atom.plane + 1
attack_image.pixel_w = attacker.get_standard_pixel_x() + attacker.pixel_w - attacked_atom.get_standard_pixel_x() - attacked_atom.pixel_w
attack_image.pixel_z = attacker.get_standard_pixel_y() + attacker.pixel_z - attacked_atom.get_standard_pixel_y() - attacked_atom.pixel_z
// Scale the icon.
attack_image.transform *= 0.5
// The icon should not rotate.
attack_image.appearance_flags = APPEARANCE_UI
var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS)
var/matrix/copy_transform = new(attacker.transform)
var/x_sign = 0
var/y_sign = 0
var/direction = get_dir(attacker, attacked_atom)
if (direction & NORTH)
y_sign = -1
else if (direction & SOUTH)
y_sign = 1
if (direction & EAST)
x_sign = -1
else if (direction & WEST)
x_sign = 1
// Attacking self, or something on the same turf as us
if (!direction)
y_sign = 1
// Not a fan of this, but its the "cleanest" way to animate this
x_sign = 0.25 * (prob(50) ? 1 : -1)
// For piercing attacks
direction = SOUTH
// And animate the attack!
switch (animation_type)
if (ATTACK_ANIMATION_BLUNT)
attack.pixel_x = 14 * x_sign
attack.pixel_y = 12 * y_sign
animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), pixel_x = 4 * x_sign, pixel_y = 3 * y_sign, time = 0.2 SECONDS)
animate(time = 0.1 SECONDS)
animate(alpha = 0, time = 0.1 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
if (ATTACK_ANIMATION_PIERCE)
var/attack_angle = dir2angle(direction) + rand(-7, 7)
// Deducting 90 because we're assuming that icon_angle of 0 means an east-facing sprite
var/anim_angle = attack_angle - 90 - used_icon_angle
var/angle_mult = 1
if (x_sign && y_sign)
angle_mult = 1.4
attack.pixel_x = 22 * x_sign * angle_mult
attack.pixel_y = 18 * y_sign * angle_mult
attack.transform = attack.transform.Turn(anim_angle)
copy_transform = copy_transform.Turn(anim_angle)
animate(
attack,
pixel_x = (22 * x_sign - 12 * sin(attack_angle)) * angle_mult,
pixel_y = (18 * y_sign - 8 * cos(attack_angle)) * angle_mult,
time = 0.1 SECONDS,
easing = CUBIC_EASING|EASE_IN,
)
animate(
attack,
alpha = 175,
transform = copy_transform.Scale(0.75),
pixel_x = (22 * x_sign + 26 * sin(attack_angle)) * angle_mult,
pixel_y = (18 * y_sign + 22 * cos(attack_angle)) * angle_mult,
time = 0.3 SECONDS,
easing = CUBIC_EASING|EASE_OUT,
)
animate(
alpha = 0,
pixel_x = -3 * -(x_sign + sin(attack_angle)),
pixel_y = -2 * -(y_sign + cos(attack_angle)),
time = 0.1 SECONDS,
easing = CIRCULAR_EASING|EASE_OUT
)
if (ATTACK_ANIMATION_SLASH)
attack.pixel_x = 18 * x_sign
attack.pixel_y = 14 * y_sign
var/x_rot_sign = 0
var/y_rot_sign = 0
var/attack_dir = (prob(50) ? 1 : -1)
var/anim_angle = dir2angle(direction) - 90 - used_icon_angle
if (x_sign)
y_rot_sign = attack_dir
if (y_sign)
x_rot_sign = attack_dir
// Animations are flipped, so flip us too!
if (x_sign > 0 || y_sign < 0)
attack_dir *= -1
// We're swinging diagonally, use separate logic
var/anim_dir = attack_dir
if (x_sign && y_sign)
if (attack_dir < 0)
x_rot_sign = -x_sign * 1.4
y_rot_sign = 0
else
x_rot_sign = 0
y_rot_sign = -y_sign * 1.4
// Flip us if we've been flipped *unless* we're flipped due to both axis
if ((x_sign < 0 && y_sign > 0) || (x_sign > 0 && y_sign < 0))
anim_dir *= -1
attack.pixel_x += 10 * x_rot_sign
attack.pixel_y += 8 * y_rot_sign
attack.transform = attack.transform.Turn(anim_angle - 45 * anim_dir)
copy_transform = copy_transform.Scale(0.75)
animate(attack, alpha = 175, time = 0.3 SECONDS, flags = ANIMATION_PARALLEL)
animate(time = 0.1 SECONDS)
animate(alpha = 0, time = 0.1 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
animate(attack, transform = copy_transform.Turn(anim_angle + 45 * anim_dir), time = 0.3 SECONDS, flags = ANIMATION_PARALLEL)
var/x_return = 10 * -x_rot_sign
var/y_return = 8 * -y_rot_sign
if (!x_rot_sign)
x_return = 18 * x_sign
if (!y_rot_sign)
y_return = 14 * y_sign
var/angle_mult = 1
if (x_sign && y_sign)
angle_mult = 1.4
if (attack_dir > 0)
x_return = 8 * x_sign
y_return = 14 * y_sign
else
x_return = 18 * x_sign
y_return = 6 * y_sign
animate(attack, pixel_x = 4 * x_sign * angle_mult, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_IN, flags = ANIMATION_PARALLEL)
animate(pixel_x = x_return, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_OUT)
animate(attack, pixel_y = 3 * y_sign * angle_mult, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_IN, flags = ANIMATION_PARALLEL)
animate(pixel_y = y_return, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_OUT)
/atom/movable/proc/get_floating_chat_x_offset()
return 0
-5
View File
@@ -1,8 +1,3 @@
/mob
//thou shall always be able to see the Geometer of Blood
var/image/narsimage = null
var/image/narglow = null
/mob/proc/cultify()
return
@@ -54,11 +54,6 @@
spawn (0)
AM.singularity_pull(src, src.current_size)
/mob
//thou shall always be able to see the rift
var/image/riftimage = null
/mob/proc/see_rift(var/obj/singularity/narsie/large/exit/R)
var/turf/T_mob = get_turf(src)
if((R.z == T_mob.z) && (get_dist(R,T_mob) <= (R.consume_range+10)) && !(R in view(T_mob)))
@@ -8,13 +8,6 @@
#define TECHNOMANCER_INSTABILITY_PRECISION 0.1 // Instability is rounded to this.
#define TECHNOMANCER_INSTABILITY_MIN_GLOW 10 // When above this number, the entity starts glowing, affecting others.
/mob/living
atom_flags = CRITICAL_ATOM
var/instability = 0
var/last_instability = 0 // Used to calculate instability delta.
var/last_instability_event = null // most recent world.time that something bad happened due to instability.
// Proc: adjust_instability()
// Parameters: 0
// Description: Does nothing, because inheritance.
+2 -2
View File
@@ -180,7 +180,7 @@
/obj/structure/foamedmetal/attack_hand(var/mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(src, FIST_ATTACK_ANIMATION)
user.do_attack_animation(src)
if ((user.mutations & HULK) || (prob(75 - metal * 25)))
user.visible_message(SPAN_WARNING("[user] smashes through the foamed metal."), SPAN_NOTICE("You smash through the metal foam wall."))
qdel(src)
@@ -238,7 +238,7 @@
qdel(src)
return TRUE
user.do_attack_animation(src, attacking_item)
user.do_attack_animation(src, used_item = attacking_item)
if(prob(attacking_item.force * 20 - metal * 25))
user.visible_message(SPAN_WARNING("[user] smashes through the foamed metal."), SPAN_NOTICE("You smash through the foamed metal with \the [attacking_item]."))
qdel(src)
@@ -14,10 +14,6 @@ would spawn and follow the beaker, even if it is carried or thrown.
pass_flags = PASSTABLE | PASSGRILLE
blocks_emissive = EMISSIVE_BLOCK_GENERIC
/obj/effect/Destroy()
QDEL_NULL(reagents)
return ..()
/datum/effect/effect/system
var/number = 3
var/cardinals = 0
+5 -6
View File
@@ -1,25 +1,24 @@
/proc/gibs(atom/location, var/list/viruses, var/datum/dna/MobDNA, gibber_type = /obj/effect/gibspawner/generic, var/fleshcolor, var/bloodcolor)
new gibber_type(location,viruses,MobDNA,fleshcolor,bloodcolor)
/proc/gibs(atom/location, var/datum/dna/MobDNA, gibber_type = /obj/effect/gibspawner/generic, var/fleshcolor, var/bloodcolor)
new gibber_type(location,MobDNA,fleshcolor,bloodcolor)
/obj/effect/gibspawner
icon = 'icons/effects/map_effects.dmi'
icon_state = "gibspawner"
var/sparks = 0 //whether sparks spread on Gib()
var/virusProb = 20 //the chance for viruses to spread on the gibs
var/list/gibtypes = list()
var/list/gibamounts = list()
var/list/gibdirections = list() //of lists
var/fleshcolor //Used for gibbed humans.
var/bloodcolor //Used for gibbed humans.
/obj/effect/gibspawner/Initialize(mapload, list/viruses, datum/dna/MobDNA, fleshcolor, bloodcolor)
/obj/effect/gibspawner/Initialize(mapload, datum/dna/MobDNA, fleshcolor, bloodcolor)
. = ..()
if(fleshcolor) src.fleshcolor = fleshcolor
if(bloodcolor) src.bloodcolor = bloodcolor
Gib(loc,viruses,MobDNA)
Gib(loc,MobDNA)
/obj/effect/gibspawner/proc/Gib(atom/location, var/list/viruses = list(), var/datum/dna/MobDNA = null)
/obj/effect/gibspawner/proc/Gib(atom/location, var/datum/dna/MobDNA = null)
if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len)
to_world(SPAN_WARNING("Gib list length mismatch!"))
return
+39 -21
View File
@@ -136,6 +136,8 @@
var/zoomdevicename
/// Boolean, `TRUE` if item is actively being used to zoom. For scoped guns and binoculars.
var/zoom = FALSE
/// The message used when stopping looking through a pair of binoculars/scope
var/zoom_out_message
/// Boolean, if item_state, lefthand, righthand, and worn sprite are all in one dmi
var/contained_sprite = FALSE
@@ -205,6 +207,13 @@
/// Used to override hardcoded clothing dmis in human clothing pr
var/icon_override
/// Angle of the icon, used for piercing and slashing attack animations, clockwise from *east-facing* sprites
var/icon_angle = 0
///icon file for an alternate attack icon
var/attack_icon
///icon state for an alternate attack icon
var/attack_icon_state
var/charge_failure_message = " cannot be recharged."
var/held_maptext
@@ -259,8 +268,16 @@
var/mob/m = loc
m.drop_from_inventory(src, null)
if(!QDELETED(action))
if(islist(action))
var/list/action_list = action
for(var/i in 1 to action_list.len)
var/datum/action/A = action_list[i]
if(!QDELETED(A))
QDEL_NULL(A)
action_list.Cut()
else if(!QDELETED(action))
QDEL_NULL(action) // /mob/living/proc/handle_actions() creates it, for ungodly reasons
action = null
if(!QDELETED(hidden_uplink))
@@ -327,23 +344,22 @@
I.forceMove(T)
/obj/item/get_examine_text(mob/user, distance, is_adjacent, infix, suffix, get_extended = FALSE)
var/size
switch(src.w_class)
if (WEIGHT_CLASS_HUGE to INFINITY)
size = "huge"
if (WEIGHT_CLASS_BULKY to WEIGHT_CLASS_HUGE)
size = "bulky"
if (WEIGHT_CLASS_NORMAL to WEIGHT_CLASS_BULKY)
size = "normal-sized"
if (WEIGHT_CLASS_SMALL to WEIGHT_CLASS_NORMAL)
size = "small"
if (0 to WEIGHT_CLASS_SMALL)
size = "tiny"
//Changed this switch to ranges instead of tiered values, to cope with granularity and also
//things outside its range ~Nanako
/obj/item/examine_descriptor(mob/user)
return "item"
. = ..(user, distance, is_adjacent, "It is a [size] item.", get_extended = get_extended)
/obj/item/examine_tags(mob/user)
var/list/parent_tags = ..()
parent_tags.Insert(1, weight_class_to_text(w_class)) // To make size display first, otherwise it looks goofy
. = parent_tags
.[weight_class_to_text(w_class)] = weight_class_to_tooltip(w_class)
if (siemens_coefficient == 0)
.["insulated"] = "It is made from a robust electrical insulator and will block any electricity passing through it!"
else if (siemens_coefficient <= 0.5)
.["partially insulated"] = "It is made from a poor insulator that will dampen (but not fully block) electric shocks passing through it."
/obj/item/get_examine_text(mob/user, distance, is_adjacent, infix, suffix, get_extended = FALSE)
. = ..(user, distance, is_adjacent, get_extended = get_extended)
var/datum/component/armor/armor_component = GetComponent(/datum/component/armor)
if(armor_component && !armor_component.hidden)
. += FONT_SMALL(SPAN_NOTICE("\[?\] This item has armor values. <a href='byond://?src=[REF(src)];examine_armor=1'>\[Show Armor Values\]</a>"))
@@ -1005,9 +1021,11 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
M.client.pixel_x = 0
M.client.pixel_y = 0
if(!cannotzoom)
if(show_zoom_message)
if(!cannotzoom && show_zoom_message)
if(!zoom_out_message)
M.visible_message("[zoomdevicename ? "<b>[M]</b> looks up from \the [src.name]" : "<b>[M]</b> lowers \the [src.name]"].")
else
M.visible_message("[zoomdevicename ? "<b>[M]</b>[zoom_out_message] \the [src.name]." : "<b>[M]</b>[zoom_out_message]"]")
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -1328,10 +1346,10 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
. = ..()
if(in_inventory || in_storage)
var/mob/user = usr
if(!(user.client.prefs.toggles_secondary & HIDE_ITEM_TOOLTIPS))
tip_timer = addtimer(CALLBACK(src, PROC_REF(openTip), location, control, params, user), 8, TIMER_STOPPABLE)
if(QDELETED(src))
return
if(!(user.client.prefs.toggles_secondary & HIDE_ITEM_TOOLTIPS))
tip_timer = addtimer(CALLBACK(src, PROC_REF(openTip), location, control, params, user), 8, TIMER_STOPPABLE)
if(!(user.client.prefs.toggles_secondary & SEE_ITEM_OUTLINES))
return
var/mob/living/L = user
@@ -4,7 +4,7 @@ pixel_y = 20;
#define PRESET_SOUTH \
dir = SOUTH; \
pixel_y = -6;
pixel_y = -2;
#define PRESET_WEST \
dir = WEST; \
@@ -1,7 +1,7 @@
/obj/item/suit_cooling_unit
name = "portable suit cooling unit"
desc = "A portable heat sink and liquid cooled radiator that can be hooked up to a space suit's existing temperature controls to provide industrial levels of cooling."
w_class = WEIGHT_CLASS_BULKY
w_class = WEIGHT_CLASS_NORMAL
icon = 'icons/obj/item/suitcooler.dmi'
icon_state = "suitcooler0"
item_state = "coolingpack"
@@ -17,14 +17,25 @@
origin_tech = list(TECH_MAGNET = 2, TECH_MATERIAL = 2)
var/celltype = /obj/item/cell/high
// Type of cell the IPC cooling unit starts with.
var/celltype = /obj/item/cell/apc
matter = list(MATERIAL_ALUMINIUM = 25000, MATERIAL_GLASS = 3500)
var/on = 0 //is it turned on?
var/cover_open = 0 //is the cover open?
// Is it turned on?
var/on = 0
// Is the cover open?
var/cover_open = 0
var/obj/item/cell/cell
var/max_cooling = 24 //in degrees kelvin per second - probably don't need to mess with heat capacity here
var/charge_consumption = 8.3 //charge per second at max_cooling
// In degrees kelvin per second - probably don't need to mess with heat capacity here
var/max_cooling = 24
// Base charge consumption per second. Modified by how much cooling work the cooler is doing.
var/charge_consumption = 15.4
// Target to cool an IPC to.
var/thermostat = T20C
//TODO: make it heat up the surroundings when not in space
@@ -241,3 +252,6 @@
/obj/item/suit_cooling_unit/no_cell
celltype = null
/obj/item/suit_cooling_unit/hyper_cell // Mostly for special spawns, events, etc., so they don't have to worry about cell charge.
celltype = /obj/item/cell/hyper
@@ -42,7 +42,7 @@
else if(crystal_type == CRYSTAL_TYPE_BLUECRYSTAL)
I.hidden_uplink.bluecrystals += amount
I.hidden_uplink.update_tgui_data()
SSnanoui.update_uis(I.hidden_uplink)
SStgui.update_uis(I.hidden_uplink)
use(amount)
to_chat(user, SPAN_NOTICE("You slot \the [src] into \the [I] and charge its internal uplink."))
@@ -4,7 +4,9 @@
build_path = /obj/structure/machinery/shield_gen/external
origin_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3)
req_components = list(
"/obj/item/stock_parts/manipulator/pico" = 2,
"/obj/item/stock_parts/manipulator" = 2,
"/obj/item/stock_parts/micro_laser" = 2,
"/obj/item/stock_parts/capacitor" = 2,
"/obj/item/stock_parts/subspace/transmitter" = 1,
"/obj/item/stock_parts/subspace/crystal" = 1,
"/obj/item/stock_parts/subspace/amplifier" = 1,
@@ -17,7 +19,9 @@
build_path = /obj/structure/machinery/shield_gen
origin_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3)
req_components = list(
"/obj/item/stock_parts/manipulator/pico" = 2,
"/obj/item/stock_parts/manipulator" = 2,
"/obj/item/stock_parts/micro_laser" = 2,
"/obj/item/stock_parts/capacitor" = 2,
"/obj/item/stock_parts/subspace/transmitter" = 1,
"/obj/item/stock_parts/subspace/crystal" = 1,
"/obj/item/stock_parts/subspace/amplifier" = 1,
@@ -30,7 +34,8 @@
build_path = /obj/structure/machinery/shield_capacitor
origin_tech = list(TECH_MAGNET = 3, TECH_POWER = 4)
req_components = list(
"/obj/item/stock_parts/manipulator/pico" = 2,
"/obj/item/stock_parts/capacitor" = 4,
"/obj/item/stock_parts/micro_laser" = 1,
"/obj/item/stock_parts/subspace/filter" = 1,
"/obj/item/stock_parts/subspace/treatment" = 1,
"/obj/item/stock_parts/subspace/analyzer" = 1,
@@ -36,13 +36,21 @@
if(M.flash_act(ignore_inherent = TRUE))
M.Weaken(10)
// 1 - 9x/70 gives us 100% at zero, 87% at 1 turf, all the way to 10% at 7
var/bang_intensity = 1 - (9 * get_dist(T, M) / 70)
M.noise_act(intensity = EAR_PROTECTION_MODERATE, damage_pwr = 10 * bang_intensity, deafen_pwr = 15 * (1 - bang_intensity))
var/distance_to_grenade = get_dist(T, M)
if(M.get_hearing_sensitivity()) // we only stun if they've got sensitive ears
// 1 - 9x/70 gives us 100% at zero, 87% at 1 turf, all the way to 10% at 7
var/bang_intensity = 1 - (9 * distance_to_grenade / 70)
var/ear_damage = 10 * bang_intensity
var/deafen_damage = 15 * (1 - bang_intensity)
// we only stun if they've got sensitive ears and got it active as well as if it is close enough.
if(M.get_hearing_sensitivity() && astype(M, /mob/living/carbon/human)?.is_listening() && distance_to_grenade < 5)
// checking for protection is handled by noise_act
M.noise_act(intensity = EAR_PROTECTION_MAJOR, stun_pwr = 2)
M.noise_act(intensity = EAR_PROTECTION_MAJOR, stun_pwr = 2, damage_pwr = ear_damage, deafen_pwr = deafen_damage)
else
M.noise_act(intensity = EAR_PROTECTION_MODERATE, damage_pwr = ear_damage, deafen_pwr = deafen_damage)
M.disable_cloaking_device()
M.update_icon()
@@ -1,8 +1,29 @@
/proc/fragem(var/source,var/fragx,var/fragy,var/light_dam,var/flash_dam,var/p_dam,var/p_range,var/can_cover=TRUE,var/shard_range = 50)
/**
* Fragem works by shooting a projectile containing fragments at every tile in a circle around it.
*
* Source is the tile the projectiles spawn on.
*
* fragx and fragy are the upper and lower bounds of the random number of fragments produced.
*
* light_dam and flash_dam are the parameters for the explosion produced by the grenade. light_dam is the radius of minor explosive damage, flash_dam is the radius of the flash effect.
*
* p_dam is the damage of each individual fragments.
*
* p_range is the distance each projectile travels before losing a fragment. When a projectile loses all of its fragments, it is deleted.
*
* can_cover determines if a mob can lie on the source turf to absorb most of the fragments.
*
* shard_range is the maximum range of the projectiles produced by the fragem. This is separate from p_range to allow for projectiles that lose fragments over distance but have a hard maximum range. Default 50
*
* spread_range is the radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern. Default 7
*
* maim_rate is the bonus for projectiles to decapitate or gib limbs. Negative values reduce this chance, positive values increase it. There is a minimum maim multiplier of 0.1, see handle_limb_gibbing for details.
*/
/proc/fragem(var/source,var/fragx,var/fragy,var/light_dam,var/flash_dam,var/p_dam,var/p_range,var/can_cover=TRUE,var/shard_range = 50,var/spread_range = 7,var/maim_rate)
var/turf/O = get_turf(source)
var/fragger = rand(fragx,fragy)
explosion(O, -1, -1, light_dam, flash_dam)
var/list/target_turfs = getcircle(O, 7)
var/list/target_turfs = getcircle(O, spread_range)
var/fragments_per_projectile = round(fragger/target_turfs.len)
for(var/turf/T in target_turfs)
@@ -12,9 +33,10 @@
P.damage = p_dam
P.pellets = fragments_per_projectile
P.range_step = p_range
if (maim_rate)
P.maim_rate = maim_rate
P.range = shard_range
P.name = "shrapnel"
P.preparePixelProjectile(T, get_turf(source))
P.firer = source
P.fired_from = source
@@ -106,6 +106,11 @@
/obj/item/material/shard/shrapnel/Initialize(newloc, material_key)
. = ..(loc, MATERIAL_STEEL)
/obj/item/material/shard/shrapnel/large/Initialize(newloc, material_key) //Shrapnel large enough to be removed by hand. Used by ship weapons.
. = ..(loc, MATERIAL_STEEL)
icon_state = "large"
w_class = WEIGHT_CLASS_NORMAL
/obj/item/material/shard/shrapnel/flechette/Initialize(newloc, material_key)
. = ..(loc, MATERIAL_TITANIUM)
@@ -200,11 +200,12 @@
/obj/item/material/twohanded/fireaxe // DEM AXES MAN, marker -Agouri
icon_state = "fireaxe0"
base_icon = "fireaxe"
icon_angle = -180
name = "fire axe"
desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
unwielded_force_divisor = 0.25
force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor
sharp = 1
sharp = FALSE
edge = TRUE
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BACK
@@ -243,6 +244,7 @@
/obj/item/material/twohanded/spear
icon_state = "spearglass0"
base_icon = "spearglass"
icon_angle = -45
name = "spear"
desc = "A haphazardly-constructed yet still deadly weapon of ancient design."
force = 15
@@ -33,6 +33,7 @@
use_sound = 'sound/items/storage/toolbox.ogg'
drop_sound = 'sound/items/drop/toolbox.ogg'
pickup_sound = 'sound/items/pickup/toolbox.ogg'
rustle_sound = 'sound/items/rustle/toolbox.ogg'
/obj/item/storage/briefcase/nt
name = "\improper NT briefcase"
@@ -13,6 +13,7 @@
use_sound = 'sound/items/storage/pillbottle.ogg'
drop_sound = 'sound/items/drop/pillbottle.ogg'
pickup_sound = 'sound/items/pickup/pillbottle.ogg'
rustle_sound = 'sound/items/pickup/pillbottle.ogg'
max_storage_space = DEFAULT_BOX_STORAGE
/obj/item/storage/pill_bottle/attack_self(mob/living/user)
+2 -2
View File
@@ -89,7 +89,7 @@
/obj/effect/energy_net/attack_hand(var/mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(src, FIST_ATTACK_ANIMATION)
user.do_attack_animation(src)
if(user == affecting)
to_chat(user, SPAN_WARNING("You can't claw at \the [src] while trapped inside it! You need to use a weapon."))
return
@@ -110,7 +110,7 @@
/obj/effect/energy_net/attackby(obj/item/attacking_item, mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(src, attacking_item)
user.do_attack_animation(src, used_item = attacking_item)
var/attack_force = attacking_item.force
if(user == affecting)
attack_force /= 2
+9
View File
@@ -108,6 +108,15 @@
var/persistant_objects_expiration_time_days = PERSISTENT_DEFAULT_EXPIRATION_DAYS
/* END PERSISTENCE VARS */
/// for easy reference of talking atoms
var/datum/talking_atom/talking_atom
/// Whether this object has been contaminated by atmos gasses like chlorine or phoron.
var/contaminated = FALSE
/// Allow overriding rad resistance.
var/rad_resistance_modifier = 1
/obj/Initialize(mapload, ...)
. = ..()
if(maxhealth)
+23 -7
View File
@@ -6,16 +6,20 @@
pass_flags_self = PASSSTRUCTURE
should_use_health = TRUE
var/material_alteration = MATERIAL_ALTERATION_ALL // Overrides for material shit. Set them manually if you don't want colors etc. See wood chairs/office chairs.
/// Overrides for material shit. Set them manually if you don't want colors etc. See wood chairs/office chairs.
var/material_alteration = MATERIAL_ALTERATION_ALL
var/climbable
var/parts
var/list/climbers
var/list/footstep_sound //footstep sounds when stepped on
/// Footstep sounds when stepped on
var/list/footstep_sound
var/material/material
var/build_amt = 2 // used by some structures to determine into how many pieces they should disassemble into or be made with
/// Used by some structures to determine into how many pieces they should disassemble into or be made with
var/build_amt = 2
var/slowdown = 0 //amount that pulling mobs have their movement delayed by
/// Amount that pulling mobs have their movement delayed by
var/slowdown = 0
/obj/structure/Initialize(mapload)
. = ..()
@@ -40,6 +44,14 @@
material = null
return ..()
/obj/structure/examine_descriptor(mob/user)
return "structure"
/obj/structure/examine_tags(atom/source, mob/user, list/examine_list)
. = ..()
if(climbable)
.["climbable"] = "It can be climbed on, either by dragging your mob onto it or middle-clicking it."
/obj/structure/attackby(obj/item/attacking_item, mob/user, params)
. = ..()
if(user?.a_intent == I_HURT && maxhealth)
@@ -95,9 +107,13 @@
else
dismantle_material = get_material()
if(should_use_health && health <= 0)
build_amt /= rand(2, 4) //if the structure is destroyed by damage, it will yield less materials
for(var/i = 1 to build_amt)
dismantle_material.place_sheet(loc)
build_amt /= rand(2, 4) //if the structure is destroyed by damage, it will yield less materials.
build_amt = max(1, min(build_amt, 5)) //Bound between 5 and 1, as shards don't stack into sheets.
for(var/i = 1 to build_amt)
dismantle_material.place_shard(loc)
else
for(var/i = 1 to build_amt)
dismantle_material.place_sheet(loc)
qdel(src)
/obj/structure/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+6 -1
View File
@@ -250,12 +250,17 @@ Class Procs:
for(var/i = 1 to 2)
if(prob(50))
metal_to_spawn++
else
new /obj/item/material/shard(current_turf, DEFAULT_WALL_MATERIAL)
if(metal_to_spawn)
new /obj/item/stack/material/steel(get_turf(src), metal_to_spawn)
if(!should_use_health)
return FALSE
qdel(src)
/obj/structure/machinery/examine_descriptor(mob/user)
return "machine"
// /obj/structure/machinery/proc/process_all()
// /* Uncomment this if/when you need component processing
// if(processing_flags & MACHINERY_PROCESS_COMPONENTS)
@@ -641,5 +646,5 @@ Class Procs:
/obj/structure/machinery/ui_status(mob/user, datum/ui_state/state)
. = ..()
if(. < UI_INTERACTIVE)
if(user.machine)
if(user?.machine)
user.unset_machine()
@@ -1498,6 +1498,10 @@
new /obj/item/coin/phoron(src)
new /obj/item/coin/phoron(src)
new /obj/item/coin/phoron(src)
new /obj/item/modkit/multi_species(src)
new /obj/item/modkit/multi_species(src)
new /obj/item/modkit/multi_species(src)
new /obj/item/modkit/multi_species(src)
/obj/structure/closet/crate/secure/gear_loadout/outereyes/single/fill()
new /obj/item/clothing/head/helmet/space/void/outereyes(src)
@@ -1514,6 +1518,7 @@
new /obj/item/ammo_magazine/c46m/extended(src)
new /obj/item/melee/energy/sword/knife(src)
new /obj/item/coin/phoron(src)
new /obj/item/modkit/multi_species(src)
/obj/structure/closet/crate/secure/gear_loadout/zavodskoi/fill()
new /obj/item/rig/combat/zavod_heavy(src)
@@ -6,6 +6,7 @@
maxhealth = OBJECT_HEALTH_MEDIUM
color = COLOR_GRAY20
build_amt = 4
pass_flags_self = PASSRAILING
layer = WINDOW_FRAME_LAYER
anchored = TRUE
density = TRUE
@@ -59,6 +60,11 @@
return TRUE
return FALSE
/obj/structure/window_frame/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
if(. != BULLET_ACT_HIT)
return .
/obj/structure/window_frame/attackby(obj/item/attacking_item, mob/user)
if((attacking_item.tool_behaviour == TOOL_SCREWDRIVER) && (istype(loc, /turf/simulated) || anchored))
if(has_glass_installed)

Some files were not shown because too many files have changed in this diff Show More