Merge branch 'master' into Clinging-to-hope

This commit is contained in:
Qwertytoforty
2021-11-14 16:43:35 -05:00
committed by GitHub
374 changed files with 20608 additions and 22123 deletions
+3 -2
View File
@@ -5,9 +5,9 @@
### AffectedArc07
# Actual Code
/code/controllers/subsystem/instancing.dm @AffectedArc07
/code/controllers/subsystem/mapping.dm @AffectedArc07
/code/controllers/subsystem/ticker.dm @AffectedArc07
/tgui/ @AffectedArc07
# CI + Tooling
/.github/workflows/ @AffectedArc07
@@ -18,7 +18,8 @@
dreamchecker.exe @AffectedArc07
rust_g.dll @AffectedArc07
### dearmochi
/tgui/ @dearmochi
### Fox-McCloud
/code/controllers/subsystem/air.dm @Fox-McCloud
+3 -2
View File
@@ -671,9 +671,10 @@ responsible for properly tagging new pull requests and issues, moderating commen
pull requests/issues, and merging/closing pull requests.
### Maintainer List
* [AffectedArc07](https://github.com/AffectedArc07)
* [Ansari](https://github.com/variableundefined)
* [Crazy Lemon](https://github.com/Crazylemon64)
* [Crazylemon](https://github.com/marlyn-x86)
* [dearmochi](https://github.com/dearmochi)
* [Fox P McCloud](https://github.com/Fox-McCloud)
### Maintainer instructions
+2 -2
View File
@@ -14,7 +14,7 @@ When that's installed, right click in any folder and click on "Git Bash".
When that opens, type in:
```sh
git clone https://github.com/ParadiseSS13/Paradise.git
git clone https://github.com/ParadiseSS13/Paradise.git --depth 1
```
(hint: hold down ctrl and press insert to paste into git bash)
@@ -88,7 +88,7 @@ and then copy the `config` and `data` folders over.
If you used the git method, you simply need to type this in to git bash:
```sh
git pull
git pull --depth 1
```
When you have done this, you'll need to recompile the code, but then it should work fine and be up to date with the live server.
+17
View File
@@ -0,0 +1,17 @@
no_balance_label = "GBP: No Update"
reset_label = "GBP: Reset"
[points]
"Administration" = 2
"Balance" = -8
"Code Improvement" = 1
"Feature" = -9
"Fix" = 3
"GC Related" = 12
"Grammar and Formatting" = 1
"High Priority" = 20
"Infrastructure" = 5
"Performance" = 12
"Refactor" = 3
"Sound" = 1
"Sprites" = 1
+47
View File
@@ -0,0 +1,47 @@
# This generates GBP balances
name: GBP
on:
pull_request_target:
types: [closed, opened]
jobs:
gbp:
runs-on: ubuntu-20.04
steps:
- name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
id: value_holder
env:
ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
run: |
unset SECRET_EXISTS
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS"
- name: Checkout
if: steps.value_holder.outputs.ACTIONS_ENABLED
uses: actions/checkout@v2
- name: Setup git
if: steps.value_holder.outputs.ACTIONS_ENABLED
run: |
git config --global user.name "gbp-action"
git config --global user.email "<>"
- name: Checkout alternate branch
if: steps.value_holder.outputs.ACTIONS_ENABLED
uses: actions/checkout@v2
with:
ref: "gbp-balances" # The branch name
path: gbp-balances
# This is to ensure we keep the gbp.toml from master
# without having to update our separate branch.
- name: Copy configuration
if: steps.value_holder.outputs.ACTIONS_ENABLED
run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
- name: GBP action
if: steps.value_holder.outputs.ACTIONS_ENABLED
uses: tgstation/gbp-action@master
with:
branch: "gbp-balances"
directory: ./gbp-balances
token: ${{ secrets.GITHUB_TOKEN }}
+51
View File
@@ -0,0 +1,51 @@
# This collects balances to one merged file
name: GBP Collection
# Every hour at the :20 minute mark. GitHub tells us to pick odd hours, instead of just using the start.
on:
schedule:
- cron: "20 * * * *"
workflow_dispatch:
jobs:
gbp_collection:
runs-on: ubuntu-20.04
steps:
- name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
id: value_holder
env:
ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
run: |
unset SECRET_EXISTS
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS"
- name: Checkout
if: steps.value_holder.outputs.ACTIONS_ENABLED
uses: actions/checkout@v2
- name: Setup git
if: steps.value_holder.outputs.ACTIONS_ENABLED
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Checkout alternate branch
if: steps.value_holder.outputs.ACTIONS_ENABLED
uses: actions/checkout@v2
with:
ref: "gbp-balances" # The branch name
path: gbp-balances
# This is to ensure we keep the gbp.toml from master
# without having to update our separate branch.
- name: Copy configuration
if: steps.value_holder.outputs.ACTIONS_ENABLED
run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
- name: GBP action
if: steps.value_holder.outputs.ACTIONS_ENABLED
uses: tgstation/gbp-action@master
with:
collect: "true"
directory: ./gbp-balances
token: ${{ secrets.GITHUB_TOKEN }}
+21
View File
@@ -75,6 +75,10 @@ CREATE TABLE `characters` (
`body_accessory` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`gear` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`autohiss` tinyint(1) NOT NULL,
`hair_gradient` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
`hair_gradient_offset` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0,0',
`hair_gradient_colour` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '#000000',
`hair_gradient_alpha` tinyint(3) UNSIGNED NOT NULL DEFAULT '255',
PRIMARY KEY (`id`),
KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=125467 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -114,6 +118,7 @@ CREATE TABLE `death` (
`pod` text NOT NULL COMMENT 'Place of death',
`coord` text NOT NULL COMMENT 'X, Y, Z POD',
`tod` datetime NOT NULL COMMENT 'Time of death',
`server_id` TEXT NULL DEFAULT NULL,
`job` text NOT NULL,
`special` text NOT NULL,
`name` text NOT NULL,
@@ -197,6 +202,7 @@ CREATE TABLE `ban` (
`bantime` datetime NOT NULL,
`ban_round_id` INT(11) NULL DEFAULT NULL,
`serverip` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`server_id` VARCHAR(50) NULL DEFAULT NULL COLLATE utf8mb4_general_ci,
`bantype` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`reason` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`job` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
@@ -320,6 +326,7 @@ CREATE TABLE `karma` (
`receiverspecial` text,
`isnegative` tinyint(1) DEFAULT NULL,
`spenderip` text NOT NULL,
`server_id` TEXT NULL DEFAULT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4;
@@ -374,6 +381,7 @@ CREATE TABLE `legacy_population` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`playercount` int(11) DEFAULT NULL,
`admincount` int(11) DEFAULT NULL,
`server_id` VARCHAR(50) NULL DEFAULT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4;
@@ -532,6 +540,7 @@ CREATE TABLE `connection_log` (
`ckey` varchar(32) NOT NULL,
`ip` varchar(32) NOT NULL,
`computerid` varchar(32) NOT NULL,
`server_id` VARCHAR(50) NULL DEFAULT NULL,
`result` ENUM('ESTABLISHED','DROPPED - IPINTEL','DROPPED - BANNED','DROPPED - INVALID') NOT NULL DEFAULT 'ESTABLISHED' COLLATE 'utf8mb4_general_ci',
PRIMARY KEY (`id`),
KEY `ckey` (`ckey`),
@@ -584,6 +593,7 @@ CREATE TABLE `round` (
`shuttle_name` VARCHAR(64) NULL,
`map_name` VARCHAR(32) NULL,
`station_name` VARCHAR(80) NULL,
`server_id` VARCHAR(50) NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -612,3 +622,14 @@ CREATE TABLE `pai_saves` (
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ckey` (`ckey`) USING BTREE
) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
--
-- Table structure for table `instance_data_cache`
--
CREATE TABLE `instance_data_cache` (
`server_id` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`key_name` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`key_value` VARCHAR(12345) NOT NULL COLLATE 'utf8mb4_general_ci',
`last_updated` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`server_id`, `key_name`) USING HASH
) COLLATE='utf8mb4_general_ci' ENGINE=MEMORY;
+32
View File
@@ -0,0 +1,32 @@
# Updates DB from 26 to 27 -AffectedArc07
# Adds a new table for instance data caching, and server_id fields on other tables
CREATE TABLE `instance_data_cache` (
`server_id` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`key_name` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`key_value` VARCHAR(12345) NOT NULL COLLATE 'utf8mb4_general_ci',
`last_updated` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`server_id`, `key_name`) USING HASH
) COLLATE='utf8mb4_general_ci' ENGINE=MEMORY;
ALTER TABLE `ban`
ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `serverip`;
ALTER TABLE `connection_log`
ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `computerid`;
ALTER TABLE `death`
ADD COLUMN `server_id` TEXT NULL AFTER `tod`;
ALTER TABLE `karma`
ADD COLUMN `server_id` TEXT NULL AFTER `spenderip`;
ALTER TABLE `legacy_population`
ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `admincount`;
# Notes already has a column for this
ALTER TABLE `notes`
CHANGE COLUMN `server` `server` VARCHAR(50) NULL COLLATE 'utf8mb4_general_ci' AFTER `edits`;
ALTER TABLE `round`
ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `station_name`;
+6
View File
@@ -0,0 +1,6 @@
# Updates DB from 27 to 28 -dearmochi
# Adds support for hair gradient
ALTER TABLE `characters` ADD COLUMN `hair_gradient` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL;
ALTER TABLE `characters` ADD COLUMN `hair_gradient_offset` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0,0';
ALTER TABLE `characters` ADD COLUMN `hair_gradient_colour` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '#000000';
ALTER TABLE `characters` ADD COLUMN `hair_gradient_alpha` tinyint(3) UNSIGNED NOT NULL DEFAULT '255'
+10234 -11033
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -3238,12 +3238,12 @@
})
"aiM" = (
/obj/structure/rack,
/obj/item/gun/energy/gun/advtaser{
/obj/item/gun/energy/disabler{
pixel_x = -3;
pixel_y = 3
},
/obj/item/gun/energy/gun/advtaser,
/obj/item/gun/energy/gun/advtaser{
/obj/item/gun/energy/disabler,
/obj/item/gun/energy/disabler{
pixel_x = 3;
pixel_y = -3
},
@@ -43751,7 +43751,7 @@
name = "\improper Secure Lab"
})
"bPO" = (
/obj/structure/foodcart,
/obj/machinery/smartfridge/foodcart,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor"
},
@@ -54,7 +54,10 @@
/turf/simulated/floor/plating/asteroid/snow/atmosphere,
/area/ruin/powered/snow_biodome)
"al" = (
/obj/effect/decal/cleanable/blood/oil,
/obj/item/paper/crumpled/bloody/fluff/ruins/snowbiodome/cabin3,
/obj/effect/decal/cleanable/blood,
/obj/effect/decal/cleanable/blood/innards,
/obj/item/organ/internal/heart,
/turf/simulated/floor/plating,
/area/ruin/powered/snow_biodome)
"am" = (
@@ -99,11 +102,12 @@
/area/ruin/powered/snow_cabin)
"aw" = (
/obj/structure/table/wood,
/obj/item/reagent_containers/food/snacks/beans,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"ax" = (
/obj/structure/table/wood,
/obj/item/reagent_containers/food/snacks/beans,
/obj/machinery/kitchen_machine/microwave,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"ay" = (
@@ -150,7 +154,7 @@
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"aH" = (
/mob/living/simple_animal/hostile/skeleton/eskimo,
/mob/living/simple_animal/hostile/skeleton/arctic,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"aI" = (
@@ -164,6 +168,7 @@
"aK" = (
/obj/structure/table/wood,
/obj/item/key,
/obj/item/paper/fluff/ruins/snowbiodome/cabin1,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"aL" = (
@@ -211,6 +216,7 @@
/obj/machinery/light/small{
dir = 8
},
/obj/effect/decal/cleanable/blood/writing,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"bx" = (
@@ -253,15 +259,14 @@
/obj/machinery/light/small,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"eb" = (
/obj/machinery/light/small{
dir = 4
},
/turf/simulated/floor/pod/dark,
"dV" = (
/obj/effect/decal/cleanable/blood/old,
/obj/structure/sacrificealtar,
/obj/item/kitchen/knife/combat/survival/bone,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"eg" = (
/obj/machinery/vending/coffee,
/turf/simulated/floor/pod/dark,
/turf/simulated/wall/mineral/silver,
/area/ruin/powered/snow_biodome)
"gh" = (
/obj/machinery/light/small{
@@ -280,6 +285,36 @@
/obj/effect/mapping_helpers/no_lava,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"iA" = (
/obj/structure/reagent_dispensers/fueltank,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"iE" = (
/obj/effect/decal/cleanable/blood/writing{
dir = 4
},
/turf/simulated/floor/plating/asteroid/snow/atmosphere,
/area/ruin/powered/snow_biodome)
"jL" = (
/obj/effect/decal/cleanable/blood/writing,
/obj/effect/decal/cleanable/blood/writing,
/obj/effect/decal/cleanable/blood/writing{
dir = 4
},
/turf/simulated/floor/plating/asteroid/snow/atmosphere,
/area/ruin/powered/snow_biodome)
"ks" = (
/obj/effect/decal/cleanable/blood/drip,
/turf/simulated/floor/plating,
/area/ruin/powered/snow_biodome)
"mh" = (
/obj/effect/decal/cleanable/blood/drip,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"mt" = (
/obj/effect/decal/cleanable/blood/drip,
/turf/simulated/floor/plating/asteroid/snow/atmosphere,
/area/ruin/powered/snow_biodome)
"qt" = (
/obj/machinery/door/airlock/silver,
/obj/structure/fans/tiny,
@@ -289,29 +324,57 @@
/obj/effect/mapping_helpers/no_lava,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors/explored)
"th" = (
/obj/effect/decal/cleanable/blood/writing{
dir = 8
},
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"tl" = (
/turf/simulated/floor/pod/light,
/area/ruin/powered/snow_biodome)
"tI" = (
/obj/item/paper/fluff/ruins/snowbiodome/cabin2,
/obj/effect/decal/cleanable/blood/writing,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"vf" = (
/obj/machinery/door/airlock/multi_tile/glass,
/obj/structure/fans/tiny,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_cabin)
"xU" = (
/obj/machinery/light/small{
dir = 8
},
/obj/item/storage/toolbox/mechanical,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"zT" = (
/obj/machinery/door/airlock/silver,
/obj/structure/fans/tiny,
/turf/simulated/floor/plating,
"zD" = (
/obj/effect/mob_spawn/human/skeleton,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"zH" = (
/obj/effect/decal/cleanable/blood,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"AJ" = (
/obj/item/twohanded/required/chainsaw,
/turf/simulated/floor/plating/asteroid/snow/atmosphere,
/area/ruin/powered/snow_biodome)
"AM" = (
/obj/structure/table,
/obj/item/pen,
/obj/item/paper,
/obj/machinery/vending/coffee/free,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"BI" = (
/obj/effect/spawner/window,
/obj/structure/barricade/wooden/crude/snow,
/turf/simulated/floor/wood,
/area/ruin/powered/snow_cabin)
"CD" = (
/obj/structure/closet/secure_closet/freezer/fridge/open,
/turf/simulated/floor/plating,
/area/ruin/powered/snow_biodome)
"Dd" = (
/obj/structure/rack,
/obj/item/clothing/suit/hooded/wintercoat/science,
@@ -320,8 +383,8 @@
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"Ef" = (
/obj/structure/reagent_dispensers/fueltank,
/turf/simulated/floor/pod/dark,
/obj/structure/flora/rock/icy,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"Ez" = (
/obj/structure/fans/tiny,
@@ -331,40 +394,47 @@
"HP" = (
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"HR" = (
/obj/structure/closet/secure_closet/freezer/fridge/open,
/turf/simulated/floor/pod/dark,
"JB" = (
/obj/structure/barricade/wooden,
/obj/structure/barricade/wooden/crude/snow,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"JZ" = (
/obj/structure/table,
/obj/machinery/kitchen_machine/microwave,
/turf/simulated/floor/pod/dark,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"KS" = (
/obj/structure/closet,
/obj/machinery/light/small{
dir = 4
},
/obj/item/twohanded/required/chainsaw,
/turf/simulated/floor/pod/dark,
"LO" = (
/obj/item/paper/fluff/ruins/snowbiodome/altar,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"Mp" = (
/obj/item/clothing/mask/balaclava,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"NO" = (
/obj/effect/decal/cleanable/blood/drip,
/obj/effect/decal/cleanable/blood/writing,
/turf/simulated/floor/plating/asteroid/snow/atmosphere,
/area/ruin/powered/snow_biodome)
"Oj" = (
/obj/structure/table,
/turf/simulated/floor/pod/dark,
/obj/machinery/light{
dir = 4
},
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"PD" = (
/obj/machinery/door/airlock/hatch,
/obj/structure/fans/tiny,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"PI" = (
/obj/effect/decal/cleanable/blood,
/obj/effect/decal/cleanable/blood/footprints,
/turf/simulated/floor/mineral/silver,
/area/ruin/powered/snow_biodome)
"PK" = (
/obj/structure/table,
/obj/item/pen,
/obj/item/paper_bin,
/obj/item/paper/fluff/ruins/snowbiodome/entrance,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"Qa" = (
@@ -378,6 +448,8 @@
"QK" = (
/obj/structure/table,
/obj/item/storage/fancy/cigarettes/cigpack_carp,
/obj/item/paper,
/obj/item/pen,
/turf/simulated/floor/pod/dark,
/area/ruin/powered/snow_biodome)
"QN" = (
@@ -385,8 +457,7 @@
/turf/simulated/floor/plating,
/area/ruin/powered/snow_biodome)
"Sj" = (
/obj/effect/decal/cleanable/blood/oil,
/turf/simulated/floor/pod/dark,
/turf/simulated/wall/mineral/wood/nonmetal,
/area/ruin/powered/snow_biodome)
"Ub" = (
/obj/structure/filingcabinet,
@@ -441,7 +512,7 @@ Wg
Wg
Wg
Dd
gh
xU
HP
tl
gh
@@ -478,7 +549,7 @@ HP
tl
PK
gz
HP
iA
Wg
ak
ak
@@ -499,9 +570,9 @@ aa
Wg
Wg
ak
aC
ak
ak
az
ak
Wg
QN
@@ -513,7 +584,7 @@ QN
QN
Wg
ak
ak
az
ak
ak
ak
@@ -574,7 +645,7 @@ ak
ak
ak
ak
ak
az
ak
aQ
ak
@@ -605,7 +676,7 @@ ak
ak
az
ak
aC
ak
ak
ak
aI
@@ -674,7 +745,7 @@ ak
ak
ak
ak
aC
ak
ak
ak
ak
@@ -687,7 +758,7 @@ tb
(10,1,1) = {"
Wg
Wg
af
CD
Wg
ak
ak
@@ -754,9 +825,9 @@ ad
af
ar
bw
at
tI
aD
at
mh
aL
aq
ak
@@ -783,12 +854,12 @@ tb
(13,1,1) = {"
Wg
ae
af
ks
aq
au
au
aq
at
th
bz
aq
ak
@@ -814,7 +885,7 @@ tb
"}
(14,1,1) = {"
Wg
af
ks
al
aq
aq
@@ -823,7 +894,7 @@ aE
at
at
aq
ak
ap
ak
aC
ak
@@ -855,7 +926,7 @@ at
aH
aM
vf
ap
ak
ak
ak
ak
@@ -917,7 +988,7 @@ ax
aK
aF
at
at
mh
aq
ak
aI
@@ -953,8 +1024,8 @@ bz
aq
aR
ak
ak
ak
NO
jL
ak
ab
ak
@@ -986,7 +1057,7 @@ aq
ak
ak
ak
az
iE
ak
ak
ak
@@ -1011,8 +1082,8 @@ af
aq
aq
aq
aq
aq
BI
BI
aq
aq
bD
@@ -1049,7 +1120,7 @@ ak
ak
ak
ak
ak
mt
ak
aC
ak
@@ -1080,12 +1151,12 @@ ak
ap
ak
ak
aC
ak
ak
mt
ak
ak
ak
ak
aI
ak
ak
ak
@@ -1114,11 +1185,11 @@ ak
ak
ak
ak
az
ak
aQ
iE
ak
ak
ak
aC
ak
aI
ak
@@ -1137,7 +1208,7 @@ aa
Wg
Wg
ak
ak
AJ
aB
ak
ak
@@ -1145,12 +1216,12 @@ aC
ak
ak
ak
eg
ak
aI
ak
ap
ak
iE
ak
eg
aC
ak
ak
ak
@@ -1177,13 +1248,13 @@ ak
aQ
ak
ak
Ef
mt
JZ
ak
aI
aB
ak
ak
ak
ak
aQ
JZ
ak
ak
aB
@@ -1207,19 +1278,19 @@ az
ak
ak
ak
bB
JZ
bM
Sj
Sj
JB
Sj
Sj
bD
ak
ak
ak
ak
ak
az
ak
bB
ak
ak
az
ak
ak
ak
Wg
@@ -1239,15 +1310,15 @@ ak
ak
ak
ak
Wg
QN
QN
QN
zT
QN
QN
QN
Wg
aC
eg
Sj
JZ
PI
zD
Sj
eg
aC
ak
ak
ak
@@ -1270,16 +1341,16 @@ Wg
Wg
ak
bB
JZ
ak
Wg
eg
HP
tl
tl
tl
aB
Sj
xU
Wg
zD
dV
LO
Sj
ak
ak
ak
bB
ak
@@ -1304,13 +1375,13 @@ Wg
Wg
Wg
Wg
HR
eb
gz
JZ
Sj
zH
Oj
JZ
KS
Ef
Sj
ap
Wg
Wg
Wg
@@ -25,7 +25,9 @@
/turf/simulated/floor/mineral/titanium/blue/airless,
/area/ruin/powered)
"i" = (
/obj/machinery/computer/shuttle,
/obj/machinery/computer{
name = "shuttle control console"
},
/turf/simulated/floor/mineral/titanium/blue/airless,
/area/ruin/powered)
"j" = (
@@ -46,7 +48,9 @@
/turf/simulated/floor/mineral/titanium/blue/airless,
/area/ruin/powered)
"n" = (
/obj/machinery/computer/mecha,
/obj/machinery/computer{
name = "exosuit control console"
},
/turf/simulated/floor/mineral/titanium/blue/airless,
/area/ruin/powered)
"p" = (
+99 -116
View File
@@ -515,9 +515,6 @@
"bm" = (
/turf/simulated/wall/mineral/titanium/nodecon/nodiagonal,
/area/derelict/crew_quarters)
"bn" = (
/turf/simulated/wall/mineral/titanium/nodecon/nodiagonal,
/area/derelict/arrival)
"bo" = (
/obj/structure/cable{
d2 = 4;
@@ -3752,11 +3749,6 @@
icon_state = "dark"
},
/area/derelict/crew_quarters)
"hU" = (
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
/area/derelict/crew_quarters)
"hV" = (
/obj/structure/table,
/obj/effect/decal/cleanable/dirt,
@@ -7143,14 +7135,6 @@
icon_state = "hydrofloor"
},
/area/derelict/crew_quarters)
"pg" = (
/obj/structure/closet/walllocker{
pixel_y = -32
},
/turf/simulated/floor/plasteel{
icon_state = "orangefull"
},
/area/derelict/crew_quarters)
"ph" = (
/obj/structure/holohoop{
dir = 1
@@ -8002,7 +7986,6 @@
},
/area/derelict/crew_quarters)
"rn" = (
/obj/structure/closet/medical_wall,
/turf/simulated/wall/mineral/titanium/nodecon/nodiagonal,
/area/derelict/arrival)
"ro" = (
@@ -9173,12 +9156,12 @@ ac
ac
ac
QN
bn
rn
jm
bn
bn
rn
rn
lu
bn
rn
QN
ac
ac
@@ -9264,14 +9247,14 @@ ac
ac
ac
ac
bn
rn
iS
jn
jS
kL
hY
lK
bn
rn
ac
ac
ac
@@ -9354,17 +9337,17 @@ ac
ac
ac
aW
bn
bn
bn
rn
rn
rn
iT
hY
jT
kM
lv
lL
bn
bn
rn
rn
aW
ac
ac
@@ -9442,10 +9425,10 @@ ac
ac
ac
aW
bn
rn
gJ
bn
bn
rn
rn
hI
hX
iv
@@ -9458,8 +9441,8 @@ hY
me
mu
mG
bn
bn
rn
rn
mG
aW
ac
@@ -9531,8 +9514,8 @@ ac
ac
ac
aW
bn
bn
rn
rn
dO
gq
gK
@@ -9549,12 +9532,12 @@ lw
hY
mf
mv
bn
rn
mR
ni
nw
mG
bn
rn
mG
aW
ac
@@ -9622,7 +9605,7 @@ ac
ac
ac
aW
bn
rn
ff
fA
dL
@@ -9712,16 +9695,16 @@ ac
ac
ac
aW
bn
bn
bn
rn
rn
rn
de
do
do
do
do
hb
bn
rn
hK
hY
ix
@@ -9905,7 +9888,7 @@ do
do
gM
hc
bn
rn
hM
ia
iz
@@ -9985,7 +9968,7 @@ ac
ac
ac
aW
bn
rn
dP
dV
dV
@@ -9997,19 +9980,19 @@ fY
do
gN
hd
bn
bn
bn
bn
bn
rn
rn
rn
rn
rn
jr
ka
kT
ly
bn
bn
bn
bn
rn
rn
rn
rn
mV
nm
mS
@@ -10075,9 +10058,9 @@ ac
ac
ac
aW
bn
bn
bn
rn
rn
rn
dQ
do
do
@@ -10088,21 +10071,21 @@ fE
fZ
gs
dO
bn
rn
aW
ac
ac
ac
aW
bn
rn
kb
bn
bn
rn
rn
aW
ac
ac
aW
bn
rn
mG
ny
nK
@@ -10166,7 +10149,7 @@ ac
ac
ac
aW
bn
rn
dm
dA
dL
@@ -10174,10 +10157,10 @@ do
do
do
eo
bn
rn
fh
fF
bn
rn
fG
aW
ac
@@ -10257,7 +10240,7 @@ ac
ac
ac
aW
bn
rn
dd
dn
do
@@ -10266,8 +10249,8 @@ do
do
ee
ep
bn
bn
rn
rn
fG
aW
ac
@@ -10348,7 +10331,7 @@ ac
ac
ac
aW
bn
rn
cL
de
do
@@ -10358,7 +10341,7 @@ do
do
ef
eq
bn
rn
aW
ac
ac
@@ -10439,9 +10422,9 @@ ac
ac
ac
aW
bn
bn
bn
rn
rn
rn
df
dp
dB
@@ -10530,10 +10513,10 @@ ac
ac
ac
ac
bn
rn
cl
cz
bn
rn
de
dq
dC
@@ -10721,7 +10704,7 @@ cN
cB
dr
dE
bn
rn
dS
ac
ac
@@ -10804,7 +10787,7 @@ ac
ac
ac
ac
bn
rn
bF
bT
co
@@ -10812,7 +10795,7 @@ cA
cM
dg
ds
bn
rn
aW
ac
ac
@@ -10858,7 +10841,7 @@ mS
qC
mU
qY
bn
rn
ac
ac
ac
@@ -10896,12 +10879,12 @@ ac
ac
ac
aW
bn
rn
bG
bU
cp
cC
bn
rn
dh
dt
dF
@@ -10942,7 +10925,7 @@ ac
ac
ac
ag
bn
rn
pT
mU
mU
@@ -11035,15 +11018,15 @@ aj
ac
ag
aW
bn
bn
rn
rn
qh
qs
qD
qL
bn
rn
ro
bn
rn
aW
ac
ac
@@ -11128,7 +11111,7 @@ ag
ag
ag
aW
bn
rn
qi
qt
ka
@@ -11136,7 +11119,7 @@ hY
ra
qy
rw
bn
rn
ac
ac
ac
@@ -11505,7 +11488,7 @@ re
qM
hY
qw
bn
rn
aW
ac
ac
@@ -11598,7 +11581,7 @@ hY
hY
qy
rJ
bn
rn
ac
ac
ac
@@ -11683,7 +11666,7 @@ ac
ac
ac
aW
bn
rn
qN
rf
hY
@@ -11966,7 +11949,7 @@ hY
hY
rk
rN
bn
rn
ac
ac
ac
@@ -12059,7 +12042,7 @@ rq
hY
rO
rZ
bn
rn
aW
ac
ac
@@ -12142,7 +12125,7 @@ if
if
if
if
bn
rn
qy
qR
ri
@@ -12242,9 +12225,9 @@ rs
rz
qu
rQ
bn
bn
bn
rn
rn
rn
sh
ac
kJ
@@ -12326,7 +12309,7 @@ if
if
if
if
bn
rn
qw
qS
rk
@@ -12419,7 +12402,7 @@ ac
ac
ac
aW
bn
rn
qv
hY
ru
@@ -12427,7 +12410,7 @@ ru
hY
rS
rZ
bn
rn
aW
ac
ac
@@ -12518,7 +12501,7 @@ hY
hY
rk
rT
bn
rn
ac
ac
ac
@@ -12787,7 +12770,7 @@ ac
ac
ac
aW
bn
rn
qN
qt
hY
@@ -12886,7 +12869,7 @@ hY
hY
qy
rX
bn
rn
ac
ac
ac
@@ -12977,7 +12960,7 @@ hY
hY
hY
rG
bn
rn
aW
ac
ac
@@ -13336,7 +13319,7 @@ ac
ag
ag
aW
bn
rn
qm
qz
jn
@@ -13344,7 +13327,7 @@ hY
rl
rv
rD
bn
rn
ac
ac
ac
@@ -14342,7 +14325,7 @@ ok
oB
ok
pb
pg
nV
bm
py
dJ
@@ -14690,16 +14673,16 @@ go
gV
hn
aV
hU
hU
pC
pC
hT
jf
hU
pC
kC
ll
hU
hU
hU
pC
pC
pC
hT
bm
ng
@@ -14783,16 +14766,16 @@ gW
gH
hH
hT
hU
pC
iL
eD
jI
kD
lm
eD
hU
hU
hU
pC
pC
pC
mQ
ng
ng
@@ -3487,7 +3487,6 @@
"jx" = (
/obj/structure/table,
/obj/structure/window/reinforced,
/obj/item/batterer,
/turf/simulated/floor/plasteel/airless{
dir = 4;
icon_state = "whitered"
+11 -124
View File
@@ -2091,19 +2091,8 @@
name = "Syndicate Outpost"
})
"dr" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective_locked";
name = "personal closet";
req_access_txt = "150"
},
/obj/item/ammo_box/magazine/m10mm,
/obj/item/ammo_box/magazine/m10mm,
/obj/structure/closet/cabinet,
/obj/item/suppressor,
/turf/simulated/floor/plasteel/airless{
dir = 8;
@@ -2114,18 +2103,7 @@
name = "Syndicate Outpost"
})
"ds" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "150"
},
/obj/structure/closet/cabinet,
/obj/item/stack/spacecash/c50,
/turf/simulated/floor/wood,
/area/awaycontent/a4{
@@ -3954,16 +3932,6 @@
/turf/simulated/floor/plating,
/area/awaycontent/a7)
"gj" = (
/obj/structure/closet/secure_closet{
icon_broken = "secbroken";
icon_closed = "sec";
icon_locked = "sec1";
icon_off = "secoff";
icon_opened = "secopen";
icon_state = "sec1";
name = "security officer's locker";
req_access_txt = "271"
},
/obj/item/clothing/suit/armor/vest,
/obj/item/reagent_containers/spray/pepper,
/obj/item/grenade/flashbang,
@@ -3977,6 +3945,7 @@
pixel_y = 23;
req_access = null
},
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "red"
@@ -5142,18 +5111,8 @@
name = "MO19 Arrivals"
})
"hX" = (
/obj/structure/closet/secure_closet{
icon_broken = "secureresbroken";
icon_closed = "secureres";
icon_locked = "secureres1";
icon_off = "secureresoff";
icon_opened = "secureresopen";
icon_state = "secureres";
locked = 0;
name = "scientist's locker";
req_access_txt = "271"
},
/obj/item/clothing/suit/storage/labcoat,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel,
/area/awaycontent/a2{
name = "MO19 Research"
@@ -5392,20 +5351,11 @@
})
"iq" = (
/obj/structure/window/reinforced,
/obj/structure/closet/secure_closet{
icon_broken = "secureresbroken";
icon_closed = "secureres";
icon_locked = "secureres1";
icon_off = "secureresoff";
icon_opened = "secureresopen";
icon_state = "secureres1";
name = "scientist's locker";
req_access_txt = "271"
},
/obj/item/clothing/suit/storage/labcoat,
/obj/item/tank/internals/air,
/obj/item/clothing/mask/gas,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel,
/area/awaycontent/a2{
name = "MO19 Research"
@@ -5961,18 +5911,9 @@
name = "MO19 Arrivals"
})
"jr" = (
/obj/structure/closet/secure_closet{
icon_broken = "rdsecurebroken";
icon_closed = "rdsecure";
icon_locked = "rdsecure1";
icon_off = "rdsecureoff";
icon_opened = "rdsecureopen";
icon_state = "rdsecure1";
name = "research director's locker";
req_access_txt = "271"
},
/obj/item/storage/backpack/satchel_tox,
/obj/item/clothing/gloves/color/latex,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 5;
icon_state = "cafeteria"
@@ -8696,18 +8637,7 @@
name = "MO19 Arrivals"
})
"oA" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/obj/item/clothing/under/suit_jacket/navy,
/turf/simulated/floor/carpet,
/area/awaycontent/a1{
@@ -8817,21 +8747,11 @@
name = "MO19 Arrivals"
})
"oK" = (
/obj/structure/closet/secure_closet{
icon_broken = "fridgebroken";
icon_closed = "fridge";
icon_locked = "fridge1";
icon_off = "fridgeoff";
icon_opened = "fridgeopen";
icon_state = "fridge";
locked = 0;
name = "meat fridge";
req_access_txt = "271"
},
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/structure/closet/secure_closet/freezer/meat,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor";
temperature = 273.15
@@ -8841,7 +8761,6 @@
})
"oL" = (
/obj/structure/closet/secure_closet{
icon_state = "secure";
locked = 0;
name = "kitchen Cabinet";
req_access_txt = "271"
@@ -8858,21 +8777,11 @@
name = "MO19 Arrivals"
})
"oM" = (
/obj/structure/closet/secure_closet{
icon_broken = "fridgebroken";
icon_closed = "fridge";
icon_locked = "fridge1";
icon_off = "fridgeoff";
icon_opened = "fridgeopen";
icon_state = "fridge";
locked = 0;
name = "refrigerator";
req_access_txt = "271"
},
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/storage/fancy/egg_box,
/obj/structure/closet/secure_closet/freezer/fridge,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor";
temperature = 273.15
@@ -8981,18 +8890,7 @@
name = "MO19 Arrivals"
})
"oZ" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/obj/item/clothing/under/assistantformal,
/turf/simulated/floor/carpet,
/area/awaycontent/a1{
@@ -9015,18 +8913,7 @@
name = "MO19 Arrivals"
})
"pg" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/obj/item/clothing/under/suit_jacket/burgundy,
/turf/simulated/floor/carpet,
/area/awaycontent/a1{
+19 -199
View File
@@ -196,18 +196,7 @@
/turf/simulated/floor/carpet,
/area/awaymission/UO71/plaza)
"aF" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/obj/item/clothing/under/suit_jacket/female,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/plaza)
@@ -420,18 +409,7 @@
/turf/simulated/floor/carpet,
/area/awaymission/UO71/plaza)
"bh" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/plaza)
"bi" = (
@@ -752,18 +730,7 @@
/area/awaymission/UO71/plaza)
"ci" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/obj/item/clothing/under/pj/blue,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/plaza)
@@ -1217,17 +1184,7 @@
},
/area/awaymission/UO71/plaza)
"ds" = (
/obj/structure/closet/secure_closet{
icon_broken = "hydrosecurebroken";
icon_closed = "hydrosecure";
icon_locked = "hydrosecure1";
icon_off = "hydrosecureoff";
icon_opened = "hydrosecureopen";
icon_state = "hydrosecure";
locked = 0;
name = "botanist's locker";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet,
/obj/item/plant_analyzer,
/obj/item/clothing/mask/bandana,
/obj/item/hatchet,
@@ -1257,16 +1214,7 @@
},
/area/awaymission/UO71/plaza)
"dw" = (
/obj/structure/closet/secure_closet{
icon_broken = "secbroken";
icon_closed = "sec";
icon_locked = "sec1";
icon_off = "secoff";
icon_opened = "secopen";
icon_state = "sec1";
name = "security officer's locker";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet,
/obj/item/storage/belt/security,
/obj/item/flash,
/obj/effect/decal/cleanable/dirt,
@@ -1372,17 +1320,7 @@
},
/area/awaymission/UO71/plaza)
"dK" = (
/obj/structure/closet/secure_closet{
icon_broken = "hydrosecurebroken";
icon_closed = "hydrosecure";
icon_locked = "hydrosecure1";
icon_off = "hydrosecureoff";
icon_opened = "hydrosecureopen";
icon_state = "hydrosecure";
locked = 0;
name = "botanist's locker";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet,
/obj/item/clothing/suit/apron,
/obj/item/clothing/mask/bandana,
/turf/simulated/floor/plasteel{
@@ -2200,17 +2138,7 @@
},
/area/awaymission/UO71/centralhall)
"fH" = (
/obj/structure/closet/secure_closet{
icon_broken = "fridgebroken";
icon_closed = "fridge";
icon_locked = "fridge1";
icon_off = "fridgeoff";
icon_opened = "fridgeopen";
icon_state = "fridge";
locked = 0;
name = "meat fridge";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet/freezer/meat,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
@@ -3316,17 +3244,7 @@
},
/area/awaymission/UO71/centralhall)
"ir" = (
/obj/structure/closet/secure_closet{
icon_broken = "fridgebroken";
icon_closed = "fridge";
icon_locked = "fridge1";
icon_off = "fridgeoff";
icon_opened = "fridgeopen";
icon_state = "fridge";
locked = 0;
name = "refrigerator";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet/freezer/fridge,
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/reagent_containers/food/condiment/milk,
@@ -3524,7 +3442,6 @@
/area/awaymission/UO71/centralhall)
"iX" = (
/obj/structure/closet/secure_closet{
icon_state = "secure";
locked = 0;
name = "kitchen Cabinet";
req_access_txt = "271"
@@ -5271,18 +5188,7 @@
/turf/simulated/floor/carpet,
/area/awaymission/UO71/centralhall)
"mF" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/obj/item/clothing/under/pj/red,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/centralhall)
@@ -5464,16 +5370,7 @@
/turf/simulated/floor/plating,
/area/awaymission/UO71/science)
"mX" = (
/obj/structure/closet/secure_closet{
icon_broken = "rdsecurebroken";
icon_closed = "rdsecure";
icon_locked = "rdsecure1";
icon_off = "rdsecureoff";
icon_opened = "rdsecureopen";
icon_state = "rdsecure1";
name = "research director's locker";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet,
/obj/item/storage/backpack/satchel_tox,
/obj/item/clothing/gloves/color/latex,
/obj/item/clothing/suit/storage/labcoat/science,
@@ -5589,18 +5486,7 @@
/turf/simulated/floor/carpet,
/area/awaymission/UO71/centralhall)
"nj" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/obj/item/clothing/under/suit_jacket/navy,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/centralhall)
@@ -6700,16 +6586,7 @@
},
/area/awaymission/UO71/science)
"pb" = (
/obj/structure/closet/secure_closet{
icon_broken = "secbroken";
icon_closed = "sec";
icon_locked = "sec1";
icon_off = "secoff";
icon_opened = "secopen";
icon_state = "sec1";
name = "security officer's locker";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet,
/obj/item/flash,
/obj/item/reagent_containers/spray/pepper,
/obj/item/restraints/handcuffs,
@@ -7644,15 +7521,8 @@
},
/area/awaymission/UO71/eng)
"qY" = (
/obj/structure/closet/secure_closet{
icon_broken = "secureengbroken";
icon_closed = "secureeng";
icon_locked = "secureeng1";
icon_off = "secureengoff";
icon_opened = "secureengopen";
icon_state = "secureeng1";
name = "engineer's locker";
req_access_txt = "271"
/obj/structure/closet/secure_closet/engineering_personal{
req_access = list(271)
},
/obj/structure/window/reinforced{
dir = 8
@@ -9557,16 +9427,7 @@
pixel_y = 6;
req_access_txt = "271"
},
/obj/structure/closet/secure_closet{
icon_broken = "secbroken";
icon_closed = "sec";
icon_locked = "sec1";
icon_off = "secoff";
icon_opened = "secopen";
icon_state = "sec1";
name = "security officer's locker";
req_access_txt = "271"
},
/obj/structure/closet/secure_closet,
/obj/item/clothing/suit/armor/vest,
/obj/item/clothing/head/helmet,
/turf/simulated/floor/plasteel{
@@ -10743,18 +10604,7 @@
/turf/simulated/floor/carpet,
/area/awaymission/UO71/mining)
"xj" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/mining)
"xk" = (
@@ -11063,18 +10913,7 @@
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 4
},
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/mining)
"xQ" = (
@@ -11091,15 +10930,7 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
},
/obj/structure/closet/secure_closet/engineering_personal{
icon_broken = "miningsecbroken";
icon_closed = "miningsec";
icon_locked = "miningsec1";
icon_off = "miningsecoff";
icon_opened = "miningsecopen";
icon_state = "miningsec";
locked = 0;
name = "miner's equipment";
/obj/structure/closet/secure_closet/miner{
req_access = list(271)
},
/obj/item/storage/backpack/satchel_eng,
@@ -11689,18 +11520,7 @@
/turf/simulated/floor/plasteel,
/area/awaymission/UO71/queen)
"zn" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "271"
},
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/loot)
"zo" = (
@@ -192,8 +192,8 @@
name = "UO45 Central Hall"
})
"aE" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/structure/closet/emcloset,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -258,11 +258,11 @@
name = "UO45 Central Hall"
})
"aL" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/structure/sign/poster/official/safety_internals{
pixel_x = -32
},
/obj/structure/closet/emcloset,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -1082,8 +1082,8 @@
name = "UO45 Central Hall"
})
"cw" = (
/obj/structure/closet,
/obj/item/storage/box/lights/mixed,
/obj/structure/closet,
/turf/simulated/floor/plating,
/area/awaycontent/a1{
name = "UO45 Central Hall"
@@ -1119,19 +1119,8 @@
})
"cz" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "201"
},
/obj/item/clothing/under/pj/blue,
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaycontent/a1{
name = "UO45 Central Hall"
@@ -1141,19 +1130,8 @@
dir = 8;
initialize_directions = 7
},
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "201"
},
/obj/item/clothing/under/suit_jacket/female,
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaycontent/a1{
name = "UO45 Central Hall"
@@ -1196,8 +1174,8 @@
name = "UO45 Central Hall"
})
"cE" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/structure/closet/emcloset,
/turf/simulated/floor/plating,
/area/awaycontent/a1{
name = "UO45 Central Hall"
@@ -1434,8 +1412,8 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 5
},
/obj/structure/closet,
/obj/item/poster/random_contraband,
/obj/structure/closet,
/turf/simulated/floor/plating,
/area/awaycontent/a1{
name = "UO45 Central Hall"
@@ -1801,20 +1779,10 @@
name = "UO45 Central Hall"
})
"dM" = (
/obj/structure/closet/secure_closet{
icon_broken = "hydrosecurebroken";
icon_closed = "hydrosecure";
icon_locked = "hydrosecure1";
icon_off = "hydrosecureoff";
icon_opened = "hydrosecureopen";
icon_state = "hydrosecure";
locked = 0;
name = "botanist's locker";
req_access_txt = "201"
},
/obj/item/plant_analyzer,
/obj/item/clothing/mask/bandana,
/obj/item/hatchet,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "vault"
@@ -1861,19 +1829,10 @@
name = "UO45 Central Hall"
})
"dQ" = (
/obj/structure/closet/secure_closet{
icon_broken = "secbroken";
icon_closed = "sec";
icon_locked = "sec1";
icon_off = "secoff";
icon_opened = "secopen";
icon_state = "sec1";
name = "security officer's locker";
req_access_txt = "201"
},
/obj/item/storage/belt/security,
/obj/item/flash,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 9;
icon_state = "red"
@@ -2009,20 +1968,10 @@
name = "UO45 Central Hall"
})
"ee" = (
/obj/structure/closet/secure_closet{
icon_broken = "hydrosecurebroken";
icon_closed = "hydrosecure";
icon_locked = "hydrosecure1";
icon_off = "hydrosecureoff";
icon_opened = "hydrosecureopen";
icon_state = "hydrosecure";
locked = 0;
name = "botanist's locker";
req_access_txt = "201"
},
/obj/item/clothing/suit/apron,
/obj/item/clothing/mask/bandana,
/obj/item/cultivator,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "vault"
@@ -2475,7 +2424,6 @@
name = "UO45 Central Hall"
})
"eW" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/structure/sign/securearea{
desc = "A warning sign which reads 'EXTERNAL AIRLOCK'";
@@ -2749,11 +2697,11 @@
name = "UO45 Central Hall"
})
"fu" = (
/obj/structure/closet/crate/hydroponics,
/obj/item/shovel/spade,
/obj/item/wrench,
/obj/item/screwdriver,
/obj/item/reagent_containers/glass/bucket,
/obj/structure/closet/crate/hydroponics,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -3211,13 +3159,13 @@
name = "UO45 Crew Quarters"
})
"gn" = (
/obj/item/storage/box/mousetraps,
/obj/item/clothing/under/waiter,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/crate{
desc = "It's a storage unit for kitchen clothes and equipment.";
name = "Kitchen Crate"
},
/obj/item/storage/box/mousetraps,
/obj/item/clothing/under/waiter,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor"
},
@@ -3225,21 +3173,11 @@
name = "UO45 Crew Quarters"
})
"go" = (
/obj/structure/closet/secure_closet{
icon_broken = "fridgebroken";
icon_closed = "fridge";
icon_locked = "fridge1";
icon_off = "fridgeoff";
icon_opened = "fridgeopen";
icon_state = "fridge";
locked = 0;
name = "meat fridge";
req_access_txt = "201"
},
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/item/reagent_containers/food/snacks/meat/monkey,
/obj/structure/closet/secure_closet/freezer/meat,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor"
},
@@ -3867,12 +3805,12 @@
name = "UO45 Gateway"
})
"hz" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/structure/sign/poster/official/safety_internals{
pixel_y = 32
},
/obj/effect/landmark/burnturf,
/obj/structure/closet/emcloset,
/turf/simulated/floor/plasteel,
/area/awaycontent/a2{
name = "UO45 Crew Quarters"
@@ -4229,8 +4167,8 @@
name = "UO45 Gateway"
})
"if" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/structure/closet/emcloset,
/turf/simulated/floor/plating,
/area/awaycontent/a6{
name = "UO45 Gateway"
@@ -4569,22 +4507,12 @@
name = "UO45 Crew Quarters"
})
"iM" = (
/obj/structure/closet/secure_closet{
icon_broken = "fridgebroken";
icon_closed = "fridge";
icon_locked = "fridge1";
icon_off = "fridgeoff";
icon_opened = "fridgeopen";
icon_state = "fridge";
locked = 0;
name = "refrigerator";
req_access_txt = "201"
},
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/storage/fancy/egg_box,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet/freezer/fridge,
/turf/simulated/floor/plasteel{
dir = 5;
icon_state = "cafeteria"
@@ -4611,9 +4539,9 @@
name = "UO45 Central Hall"
})
"iO" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/effect/landmark/damageturf,
/obj/structure/closet/emcloset,
/turf/simulated/floor/plating,
/area/awaycontent/a1{
name = "UO45 Central Hall"
@@ -4664,8 +4592,8 @@
name = "UO45 Gateway"
})
"iT" = (
/obj/structure/closet/l3closet/scientist,
/obj/effect/decal/warning_stripes/north,
/obj/structure/closet/l3closet/scientist,
/turf/simulated/floor/plasteel,
/area/awaycontent/a6{
name = "UO45 Gateway"
@@ -4821,16 +4749,15 @@
name = "UO45 Crew Quarters"
})
"jg" = (
/obj/structure/closet/secure_closet{
icon_state = "secure";
locked = 0;
name = "kitchen Cabinet";
req_access_txt = "201"
},
/obj/item/reagent_containers/food/condiment/flour,
/obj/item/reagent_containers/food/condiment/flour,
/obj/item/reagent_containers/food/condiment/sugar,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet{
locked = 0;
name = "kitchen Cabinet";
req_access_txt = "271"
},
/turf/simulated/floor/plasteel{
dir = 5;
icon_state = "cafeteria"
@@ -4849,8 +4776,8 @@
name = "UO45 Gateway"
})
"ji" = (
/obj/structure/closet/l3closet/scientist,
/obj/effect/decal/warning_stripes/northeast,
/obj/structure/closet/l3closet/scientist,
/turf/simulated/floor/plasteel,
/area/awaycontent/a6{
name = "UO45 Gateway"
@@ -5091,17 +5018,6 @@
name = "UO45 Central Hall"
})
"jG" = (
/obj/structure/closet/secure_closet{
icon_broken = "secureresbroken";
icon_closed = "secureres";
icon_locked = "secureres1";
icon_off = "secureresoff";
icon_opened = "secureresopen";
icon_state = "secureres";
locked = 0;
name = "scientist's locker";
req_access_txt = "201"
},
/obj/item/tank/internals/air,
/obj/item/clothing/mask/gas,
/obj/effect/decal/cleanable/dirt,
@@ -5115,17 +5031,6 @@
/obj/machinery/light/small{
dir = 1
},
/obj/structure/closet/secure_closet{
icon_broken = "secureresbroken";
icon_closed = "secureres";
icon_locked = "secureres1";
icon_off = "secureresoff";
icon_opened = "secureresopen";
icon_state = "secureres";
locked = 0;
name = "scientist's locker";
req_access_txt = "201"
},
/obj/item/clothing/gloves/color/latex,
/turf/simulated/floor/plasteel{
icon_state = "white"
@@ -5629,13 +5534,13 @@
name = "UO45 Research"
})
"kx" = (
/obj/structure/closet/crate,
/obj/effect/decal/cleanable/dirt,
/obj/item/storage/toolbox/mechanical{
pixel_x = -2;
pixel_y = -1
},
/obj/item/multitool,
/obj/structure/closet/crate,
/turf/simulated/floor/plating,
/area/awaycontent/a1{
name = "UO45 Central Hall"
@@ -6729,7 +6634,6 @@
name = "UO45 Engineering"
})
"lZ" = (
/obj/structure/closet/crate,
/obj/item/stack/sheet/mineral/plasma{
amount = 26
},
@@ -6742,6 +6646,7 @@
maxcharge = 15000
},
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/crate,
/turf/simulated/floor/plating,
/area/awaycontent/a3{
name = "UO45 Engineering"
@@ -6876,12 +6781,12 @@
name = "UO45 Research"
})
"mm" = (
/obj/structure/closet/firecloset,
/obj/machinery/light/small,
/obj/structure/sign/securearea{
pixel_y = -32
},
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/firecloset,
/turf/simulated/floor/plasteel{
icon_state = "warnwhite"
},
@@ -6987,19 +6892,8 @@
name = "UO45 Crew Quarters"
})
"mw" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "201"
},
/obj/item/clothing/under/pj/red,
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaycontent/a2{
name = "UO45 Crew Quarters"
@@ -7051,7 +6945,6 @@
name = "UO45 Engineering"
})
"mB" = (
/obj/structure/closet/crate,
/obj/item/stack/sheet/metal{
amount = 50
},
@@ -7061,6 +6954,7 @@
/obj/item/stack/rods{
amount = 50
},
/obj/structure/closet/crate,
/turf/simulated/floor/plating,
/area/awaycontent/a3{
name = "UO45 Engineering"
@@ -7135,20 +7029,11 @@
name = "UO45 Gateway"
})
"mI" = (
/obj/structure/closet/secure_closet{
icon_broken = "rdsecurebroken";
icon_closed = "rdsecure";
icon_locked = "rdsecure1";
icon_off = "rdsecureoff";
icon_opened = "rdsecureopen";
icon_state = "rdsecure1";
name = "research director's locker";
req_access_txt = "201"
},
/obj/item/storage/backpack/satchel_tox,
/obj/item/clothing/gloves/color/latex,
/obj/item/clothing/suit/storage/labcoat/science,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 5;
icon_state = "cafeteria"
@@ -7334,19 +7219,8 @@
name = "UO45 Crew Quarters"
})
"mZ" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "201"
},
/obj/item/clothing/under/suit_jacket/navy,
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaycontent/a2{
name = "UO45 Crew Quarters"
@@ -7657,6 +7531,7 @@
})
"nz" = (
/obj/machinery/vending/coffee,
/obj/structure/closet/emcloset,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -8474,6 +8349,7 @@
pixel_x = -30
},
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "red"
@@ -8893,16 +8769,6 @@
name = "UO45 Research"
})
"pi" = (
/obj/structure/closet/secure_closet{
icon_broken = "secbroken";
icon_closed = "sec";
icon_locked = "sec1";
icon_off = "secoff";
icon_opened = "secopen";
icon_state = "sec1";
name = "security officer's locker";
req_access_txt = "201"
},
/obj/machinery/atmospherics/unary/vent_scrubber/on,
/obj/item/restraints/handcuffs,
/obj/item/flash,
@@ -9369,7 +9235,6 @@
name = "UO45 Engineering"
})
"qa" = (
/obj/structure/closet/crate,
/obj/item/storage/box/lights/mixed,
/obj/item/poster/random_contraband,
/obj/effect/decal/cleanable/dirt,
@@ -9553,6 +9418,7 @@
})
"qq" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/l3closet/general,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "whitehall"
@@ -9729,13 +9595,13 @@
dir = 4;
layer = 2.9
},
/obj/structure/closet/secure_closet/engineering_personal{
req_access = list(201)
},
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/visible/purple{
dir = 4
},
/obj/structure/closet/secure_closet/engineering_personal{
req_access = list(271)
},
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "yellow"
@@ -9759,16 +9625,6 @@
name = "UO45 Engineering"
})
"qH" = (
/obj/structure/closet/secure_closet{
icon_broken = "secureengbroken";
icon_closed = "secureeng";
icon_locked = "secureeng1";
icon_off = "secureengoff";
icon_opened = "secureengopen";
icon_state = "secureeng1";
name = "engineer's locker";
req_access_txt = "201"
},
/obj/structure/window/reinforced{
dir = 8
},
@@ -9779,6 +9635,9 @@
/obj/machinery/atmospherics/pipe/simple/visible/purple{
dir = 4
},
/obj/structure/closet/secure_closet/engineering_personal{
req_access = list(271)
},
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "yellow"
@@ -10989,7 +10848,6 @@
name = "UO45 Engineering"
})
"sr" = (
/obj/structure/closet/l3closet/general,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel{
dir = 1;
@@ -11045,7 +10903,6 @@
name = "UO45 Research"
})
"sw" = (
/obj/structure/closet/l3closet/general,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/floor/plasteel{
dir = 1;
@@ -11355,9 +11212,9 @@
name = "UO45 Engineering"
})
"sU" = (
/obj/structure/closet,
/obj/item/storage/belt/utility,
/obj/effect/landmark/burnturf,
/obj/structure/closet,
/turf/simulated/floor/plating,
/area/awaycontent/a2{
name = "UO45 Crew Quarters"
@@ -11447,7 +11304,6 @@
name = "UO45 Engineering"
})
"te" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/structure/sign/securearea{
desc = "A warning sign which reads 'EXTERNAL AIRLOCK'";
@@ -11733,7 +11589,6 @@
name = "UO45 Engineering"
})
"tA" = (
/obj/structure/closet/firecloset,
/obj/effect/decal/warning_stripes/northwest,
/turf/simulated/floor/plating,
/area/awaycontent/a5{
@@ -12057,18 +11912,9 @@
pixel_y = 6;
req_access_txt = "201"
},
/obj/structure/closet/secure_closet{
icon_broken = "secbroken";
icon_closed = "sec";
icon_locked = "sec1";
icon_off = "secoff";
icon_opened = "secopen";
icon_state = "sec1";
name = "security officer's locker";
req_access_txt = "201"
},
/obj/item/clothing/suit/armor/vest,
/obj/item/clothing/head/helmet,
/obj/structure/closet/secure_closet,
/turf/simulated/floor/plasteel{
dir = 6;
icon_state = "red"
@@ -12256,6 +12102,7 @@
},
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/warning_stripes/west,
/obj/structure/closet/firecloset,
/turf/simulated/floor/plating,
/area/awaycontent/a5{
name = "UO45 Research"
@@ -12666,22 +12513,22 @@
name = "UO45 Engineering"
})
"vc" = (
/obj/structure/closet/emcloset,
/obj/item/clothing/mask/breath,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
/obj/effect/decal/warning_stripes/yellow/hollow,
/obj/structure/closet/emcloset,
/turf/simulated/floor/plasteel,
/area/awaycontent/a3{
name = "UO45 Engineering"
})
"vd" = (
/obj/structure/closet/firecloset,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
/obj/effect/decal/warning_stripes/yellow/hollow,
/obj/structure/closet/firecloset,
/turf/simulated/floor/plasteel,
/area/awaycontent/a3{
name = "UO45 Engineering"
@@ -12739,11 +12586,11 @@
name = "UO45 Engineering"
})
"vi" = (
/obj/structure/closet/firecloset,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 9
},
/obj/effect/decal/warning_stripes/yellow/hollow,
/obj/structure/closet/firecloset,
/turf/simulated/floor/plasteel,
/area/awaycontent/a3{
name = "UO45 Engineering"
@@ -13138,10 +12985,10 @@
dir = 1;
pixel_y = -24
},
/obj/structure/closet/secure_closet/miner{
req_access = list(201)
},
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet/miner{
req_access = list(271)
},
/turf/simulated/floor/plasteel,
/area/awaycontent/a4{
name = "UO45 Mining"
@@ -13428,18 +13275,7 @@
name = "UO45 Mining"
})
"wq" = (
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "201"
},
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaycontent/a4{
name = "UO45 Mining"
@@ -13806,19 +13642,11 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
},
/obj/structure/closet/secure_closet/engineering_personal{
icon_broken = "miningsecbroken";
icon_closed = "miningsec";
icon_locked = "miningsec1";
icon_off = "miningsecoff";
icon_opened = "miningsecopen";
icon_state = "miningsec";
locked = 0;
name = "miner's equipment";
req_access = list(201)
},
/obj/item/storage/backpack/satchel_eng,
/obj/item/clothing/gloves/fingerless,
/obj/structure/closet/secure_closet/miner{
req_access = list(271)
},
/turf/simulated/floor/plasteel{
icon_state = "browncorner"
},
@@ -13848,18 +13676,7 @@
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 4
},
/obj/structure/closet/secure_closet{
desc = "It's a secure locker for personnel. The first card swiped gains control.";
icon_broken = "cabinetdetective_broken";
icon_closed = "cabinetdetective";
icon_locked = "cabinetdetective_locked";
icon_off = "cabinetdetective_broken";
icon_opened = "cabinetdetective_open";
icon_state = "cabinetdetective";
locked = 0;
name = "personal closet";
req_access_txt = "201"
},
/obj/structure/closet/cabinet,
/turf/simulated/floor/carpet,
/area/awaycontent/a4{
name = "UO45 Mining"
@@ -14045,7 +13862,6 @@
dir = 4;
initialize_directions = 11
},
/obj/structure/closet/crate,
/obj/item/stack/sheet/metal{
amount = 26
},
@@ -14053,6 +13869,7 @@
amount = 19
},
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/crate,
/turf/simulated/floor/plasteel,
/area/awaycontent/a4{
name = "UO45 Mining"
@@ -14091,7 +13908,6 @@
dir = 4;
pixel_x = 24
},
/obj/structure/closet/crate,
/obj/item/stack/sheet/mineral/plasma{
amount = 6
},
@@ -14148,17 +13964,15 @@
name = "UO45 Mining"
})
"xv" = (
/obj/machinery/alarm/monitor{
dir = 4;
locked = 0;
pixel_x = -23;
req_access = null
},
/obj/structure/closet/emcloset,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel,
/area/awaycontent/a4{
name = "UO45 Mining"
/obj/structure/closet/cabinet,
/turf/simulated/mineral/random/labormineral,
/area/awaycontent/a7{
always_unpowered = 1;
name = "UO45 Caves";
power_environ = 0;
power_equip = 0;
power_light = 0;
poweralm = 0
})
"xw" = (
/obj/structure/table,
@@ -14512,15 +14326,10 @@
poweralm = 0
})
"yn" = (
/obj/structure/closet/crate,
/turf/simulated/floor/plating/asteroid/airless,
/area/awaycontent/a7{
always_unpowered = 1;
name = "UO45 Caves";
power_environ = 0;
power_equip = 0;
power_light = 0;
poweralm = 0
/obj/structure/closet/emcloset,
/turf/simulated/wall/r_wall/rust,
/area/awaycontent/a4{
name = "UO45 Mining"
})
"yo" = (
/obj/structure/alien/weeds{
@@ -24626,11 +24435,11 @@ aB
aB
aC
aC
xv
ac
ac
ac
ac
ac
xv
ac
ac
bQ
@@ -25901,7 +25710,7 @@ yl
fo
fo
fo
yn
fo
ac
ac
ac
@@ -26044,7 +25853,7 @@ xG
xO
xn
wx
xv
vy
xB
wx
ev
@@ -26496,7 +26305,7 @@ vJ
vJ
vJ
vJ
vJ
yn
yl
fo
fo
@@ -29954,7 +29763,7 @@ ac
ac
ac
ac
ac
xv
ac
ac
ac
File diff suppressed because it is too large Load Diff
+4 -8
View File
@@ -4044,9 +4044,9 @@
/area/shuttle/gamma/space)
"oC" = (
/obj/structure/closet/secure_closet/guncabinet,
/obj/item/gun/energy/sniperrifle,
/obj/item/gun/energy/sniperrifle,
/obj/item/gun/energy/sniperrifle,
/obj/item/gun/energy/ionrifle,
/obj/item/gun/energy/ionrifle,
/obj/item/gun/energy/ionrifle,
/obj/machinery/light/spot{
dir = 4
},
@@ -4558,11 +4558,6 @@
/obj/item/ammo_box/magazine/m45,
/obj/item/ammo_box/magazine/m45,
/obj/item/ammo_box/magazine/m45,
/obj/structure/closet{
icon_closed = "cabinet_closed";
icon_opened = "cabinet_open";
icon_state = "cabinet_closed"
},
/obj/item/clothing/head/helmet/space/deathsquad/beret,
/obj/item/clothing/shoes/combat,
/obj/item/clothing/under/syndicate/combat,
@@ -4577,6 +4572,7 @@
pixel_x = 6;
pixel_y = -6
},
/obj/structure/closet/cabinet,
/turf/simulated/floor/plasteel/grimy,
/area/centcom/specops)
"ql" = (
+5 -5
View File
@@ -786,11 +786,11 @@
/area/shuttle/administration)
"PV" = (
/obj/structure/rack,
/obj/item/gun/energy/sniperrifle{
/obj/item/gun/energy/ionrifle{
pixel_y = -4
},
/obj/item/gun/energy/sniperrifle,
/obj/item/gun/energy/sniperrifle{
/obj/item/gun/energy/ionrifle,
/obj/item/gun/energy/ionrifle{
pixel_y = 3
},
/obj/structure/window/reinforced{
@@ -882,11 +882,11 @@
/obj/structure/window/reinforced{
dir = 8
},
/obj/item/gun/energy/gun/advtaser{
/obj/item/gun/energy/disabler{
pixel_x = 3;
pixel_y = -3
},
/obj/item/gun/energy/gun/advtaser,
/obj/item/gun/energy/disabler,
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/administration)
"WV" = (
+2
View File
@@ -0,0 +1,2 @@
/// Version of RUST-G that this codebase wants
#define RUST_G_VERSION "0.5.0-P"
+4
View File
@@ -123,3 +123,7 @@
#define COLOR_THEME_OPERATIVE "#B8221F"
#define COLOR_THEME_GLASS "#75A4C4"
#define COLOR_THEME_CLOCKWORK "#CFBA47"
// Color matrix utilities
#define COLOR_MATRIX_ADD(C) list(COLOR_RED, COLOR_GREEN, COLOR_BLUE, C)
#define COLOR_MATRIX_OVERLAY(C) list(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, C)
+1 -1
View File
@@ -181,7 +181,7 @@ GLOBAL_LIST_INIT(clawfootstep, list(
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0),
FOOTSTEP_GLASS = list(list(
FOOTSTEP_GLASS_BAREFOOT = list(list(
'sound/effects/footstep/glassbarefoot1.ogg',
'sound/effects/footstep/glassbarefoot2.ogg',
'sound/effects/footstep/glassbarefoot3.ogg'), 100, 1),
+25 -24
View File
@@ -7,58 +7,59 @@
#define JOBCAT_ENGSEC (1<<0)
#define JOB_CAPTAIN (1<<0)
#define JOB_HOS (1<<1)
#define JOB_WARDEN (1<<2)
#define JOB_CAPTAIN (1<<0)
#define JOB_HOS (1<<1)
#define JOB_WARDEN (1<<2)
#define JOB_DETECTIVE (1<<3)
#define JOB_OFFICER (1<<4)
#define JOB_OFFICER (1<<4)
#define JOB_CHIEF (1<<5)
#define JOB_ENGINEER (1<<6)
#define JOB_ATMOSTECH (1<<7)
#define JOB_AI (1<<8)
#define JOB_CYBORG (1<<9)
#define JOB_CENTCOM (1<<10)
#define JOB_AI (1<<8)
#define JOB_CYBORG (1<<9)
#define JOB_CENTCOM (1<<10)
#define JOB_SYNDICATE (1<<11)
#define JOB_JUDGE (1<<12)
#define JOBCAT_MEDSCI (1<<1)
#define JOB_RD (1<<0)
#define JOB_RD (1<<0)
#define JOB_SCIENTIST (1<<1)
#define JOB_CHEMIST (1<<2)
#define JOB_CMO (1<<3)
#define JOB_DOCTOR (1<<4)
#define JOB_GENETICIST (1<<5)
#define JOB_VIROLOGIST (1<<6)
#define JOB_CHEMIST (1<<2)
#define JOB_CMO (1<<3)
#define JOB_DOCTOR (1<<4)
#define JOB_GENETICIST (1<<5)
#define JOB_VIROLOGIST (1<<6)
#define JOB_PSYCHIATRIST (1<<7)
#define JOB_ROBOTICIST (1<<8)
#define JOB_ROBOTICIST (1<<8)
#define JOB_PARAMEDIC (1<<9)
#define JOB_CORONER (1<<10)
#define JOB_CORONER (1<<10)
#define JOBCAT_SUPPORT (1<<2)
#define JOB_HOP (1<<0)
#define JOB_HOP (1<<0)
#define JOB_BARTENDER (1<<1)
#define JOB_BOTANIST (1<<2)
#define JOB_CHEF (1<<3)
#define JOB_JANITOR (1<<4)
#define JOB_JANITOR (1<<4)
#define JOB_LIBRARIAN (1<<5)
#define JOB_QUARTERMASTER (1<<6)
#define JOB_CARGOTECH (1<<7)
#define JOB_MINER (1<<8)
#define JOB_LAWYER (1<<9)
#define JOB_LAWYER (1<<9)
#define JOB_CHAPLAIN (1<<10)
#define JOB_CLOWN (1<<11)
#define JOB_MIME (1<<12)
#define JOB_CIVILIAN (1<<13)
#define JOB_EXPLORER (1<<14)
#define JOBCAT_KARMA (1<<3)
#define JOBCAT_KARMA (1<<3)
#define JOB_NANO (1<<0)
#define JOB_BLUESHIELD (1<<1)
#define JOB_BARBER (1<<3)
#define JOB_BLUESHIELD (1<<1)
#define JOB_BARBER (1<<3)
// #define JOB_MECHANIC (1<<4) // AA07 2021-10-02 - Removed: Kept for history sake
#define JOB_BRIGDOC (1<<5)
#define JOB_JUDGE (1<<6)
// #define JOB_PILOT (1<<7) // AA07 2021-10-02 - Removed: Kept for history sake
#define JOB_BRIGDOC (1<<5)
// #define JOB_JUDGE (1<<6) // AA07 2021-10-09 - Moved to ENGSEC (Non karma): Define kept for history sake
// #define JOB_PILOT (1<<7) // AA07 2021-10-02 - Removed: Kept for history sake
+4 -1
View File
@@ -365,7 +365,7 @@
#define INVESTIGATE_BOMB "bombs"
// The SQL version required by this version of the code
#define SQL_VERSION 26
#define SQL_VERSION 28
// Vending machine stuff
#define CAT_NORMAL 1
@@ -496,3 +496,6 @@
// Runechat symbol types
#define RUNECHAT_SYMBOL_EMOTE 1
/// Waits at a line of code until X is true
#define UNTIL(X) while(!(X)) sleep(world.tick_lag)
+102 -41
View File
@@ -1,80 +1,130 @@
// rust_g.dm - DM API for rust_g extension library
//
// To configure, create a `rust_g.config.dm` and set what you care about from
// the following options:
//
// #define RUST_G "path/to/rust_g"
// Override the .dll/.so detection logic with a fixed path or with detection
// logic of your own.
//
// #define RUSTG_OVERRIDE_BUILTINS
// Enable replacement rust-g functions for certain builtins. Off by default.
#ifndef RUST_G
/// Locator for the RUSTG DLL or SO depending on system type. Override if needed.
#define RUST_G (world.system_type == UNIX ? "./librust_g.so" : "./rust_g.dll")
// Default automatic RUST_G detection.
// On Windows, looks in the standard places for `rust_g.dll`.
// On Linux, looks in `.`, `$LD_LIBRARY_PATH`, and `~/.byond/bin` for either of
// `librust_g.so` (preferred) or `rust_g` (old).
/* This comment bypasses grep checks */ /var/__rust_g
/proc/__detect_rust_g()
if (world.system_type == UNIX)
if (fexists("./librust_g.so"))
// No need for LD_LIBRARY_PATH badness.
return __rust_g = "./librust_g.so"
else if (fexists("./rust_g"))
// Old dumb filename.
return __rust_g = "./rust_g"
else if (fexists("[world.GetConfig("env", "HOME")]/.byond/bin/rust_g"))
// Old dumb filename in `~/.byond/bin`.
return __rust_g = "rust_g"
else
// It's not in the current directory, so try others
return __rust_g = "librust_g.so"
else
return __rust_g = "rust_g.dll"
#define RUST_G (__rust_g || __detect_rust_g())
#endif
// Gets the version of RUSTG
/// Gets the version of rust_g
/proc/rustg_get_version() return call(RUST_G, "get_version")()
// Defines for internal job subsystem //
#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
#define RUSTG_JOB_ERROR "JOB PANICKED"
// Cellular Noise Operations //
// DMI related operations //
/**
* This proc generates a cellular automata noise grid which can be used in procedural generation methods.
*
* Returns a single string that goes row by row, with values of 1 representing an alive cell, and a value of 0 representing a dead cell.
*
* Arguments:
* * percentage: The chance of a turf starting closed
* * smoothing_iterations: The amount of iterations the cellular automata simulates before returning the results
* * birth_limit: If the number of neighboring cells is higher than this amount, a cell is born
* * death_limit: If the number of neighboring cells is lower than this amount, a cell dies
* * width: The width of the grid.
* * height: The height of the grid.
*/
#define rustg_cnoise_generate(percentage, smoothing_iterations, birth_limit, death_limit, width, height) \
call(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height)
// DMI Operations //
#define rustg_dmi_strip_metadata(fname) call(RUST_G, "dmi_strip_metadata")(fname)
#define rustg_dmi_create_png(path, width, height, data) call(RUST_G, "dmi_create_png")(path, width, height, data)
#define rustg_dmi_resize_png(path, width, height, resizetype) call(RUST_G, "dmi_resize_png")(path, width, height, resizetype)
// Noise related operations //
#define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y)
// File related operations //
// File Operations //
#define rustg_file_read(fname) call(RUST_G, "file_read")(fname)
#define rustg_file_exists(fname) call(RUST_G, "file_exists")(fname)
#define rustg_file_write(text, fname) call(RUST_G, "file_write")(text, fname)
#define rustg_file_append(text, fname) call(RUST_G, "file_append")(text, fname)
#ifdef RUSTG_OVERRIDE_BUILTINS
#define file2text(fname) rustg_file_read(fname)
#define text2file(text, fname) rustg_file_append(text, fname)
#define file2text(fname) rustg_file_read("[fname]")
#define text2file(text, fname) rustg_file_append(text, "[fname]")
#endif
// Git related operations //
// Git Operations //
#define rustg_git_revparse(rev) call(RUST_G, "rg_git_revparse")(rev)
#define rustg_git_commit_date(rev) call(RUST_G, "rg_git_commit_date")(rev)
// Hash related operations //
// Hashing Operations //
#define rustg_hash_string(algorithm, text) call(RUST_G, "hash_string")(algorithm, text)
#define rustg_hash_file(algorithm, fname) call(RUST_G, "hash_file")(algorithm, fname)
#define rustg_hash_generate_totp(seed) call(RUST_G, "generate_totp")(seed)
#define rustg_hash_generate_totp_tolerance(seed, tolerance) call(RUST_G, "generate_totp_tolerance")(seed, tolerance)
#define RUSTG_HASH_MD5 "md5"
#define RUSTG_HASH_SHA1 "sha1"
#define RUSTG_HASH_SHA256 "sha256"
#define RUSTG_HASH_SHA512 "sha512"
#define RUSTG_HASH_XXH64 "xxh64"
#define RUSTG_HASH_BASE64 "base64"
#ifdef RUSTG_OVERRIDE_BUILTINS
#define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing))
#define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing))
#endif
// Logging stuff //
#define rustg_log_write(fname, text) call(RUST_G, "log_write")(fname, text)
/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()
// URL encoding stuff
#define rustg_url_encode(text) call(RUST_G, "url_encode")(text)
#define rustg_url_decode(text) call(RUST_G, "url_decode")(text)
#ifdef RUSTG_OVERRIDE_BUILTINS
#define url_encode(text) rustg_url_encode(text)
#define url_decode(text) rustg_url_decode(text)
#endif
// HTTP library stuff //
// HTTP Operations //
#define RUSTG_HTTP_METHOD_GET "get"
#define RUSTG_HTTP_METHOD_PUT "put"
#define RUSTG_HTTP_METHOD_DELETE "delete"
#define RUSTG_HTTP_METHOD_PATCH "patch"
#define RUSTG_HTTP_METHOD_HEAD "head"
#define RUSTG_HTTP_METHOD_POST "post"
// Commented out because this thing locks up the entire DD process when you use it
// DO NOT USE FOR THE LOVE OF GOD
// #define rustg_http_request_blocking(method, url, body, headers) call(RUST_G, "http_request_blocking")(method, url, body, headers)
#define rustg_http_request_async(method, url, body, headers) call(RUST_G, "http_request_async")(method, url, body, headers)
#define rustg_http_request_blocking(method, url, body, headers, options) call(RUST_G, "http_request_blocking")(method, url, body, headers, options)
#define rustg_http_request_async(method, url, body, headers, options) call(RUST_G, "http_request_async")(method, url, body, headers, options)
#define rustg_http_check_request(req_id) call(RUST_G, "http_check_request")(req_id)
/proc/rustg_create_async_http_client() return call(RUST_G, "start_http_client")()
/proc/rustg_close_async_http_client() return call(RUST_G, "shutdown_http_client")()
// SQL stuff //
// Jobs Subsystem Operations //
#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
#define RUSTG_JOB_ERROR "JOB PANICKED"
// JSON Operations //
#define rustg_json_is_valid(text) (call(RUST_G, "json_is_valid")(text) == "true")
// Logging Operations //
#define rustg_log_write(fname, text) call(RUST_G, "log_write")(fname, text)
/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()
// Noise Operations //
#define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y)
// SQL Opeartions //
#define rustg_sql_connect_pool(options) call(RUST_G, "sql_connect_pool")(options)
#define rustg_sql_query_async(handle, query, params) call(RUST_G, "sql_query_async")(handle, query, params)
#define rustg_sql_query_blocking(handle, query, params) call(RUST_G, "sql_query_blocking")(handle, query, params)
@@ -82,8 +132,19 @@
#define rustg_sql_disconnect_pool(handle) call(RUST_G, "sql_disconnect_pool")(handle)
#define rustg_sql_check_query(job_id) call(RUST_G, "sql_check_query")("[job_id]")
// toml2json stuff //
#define rustg_toml2json(tomlfile) call(RUST_G, "toml2json")(tomlfile)
// TOML Operations //
#define rustg_read_toml_file(path) json_decode(call(RUST_G, "toml_file_to_json")(path) || "null")
// Unzip Operations //
#define rustg_unzip_download_async(url, unzip_directory) call(RUST_G, "unzip_download_async")(url, unzip_directory)
#define rustg_unzip_check(job_id) call(RUST_G, "unzip_check")("[job_id]")
// URL Encoder/Decoder Operations //
#define rustg_url_encode(text) call(RUST_G, "url_encode")("[text]")
#define rustg_url_decode(text) call(RUST_G, "url_decode")(text)
#ifdef RUSTG_OVERRIDE_BUILTINS
#define url_encode(text) rustg_url_encode(text)
#define url_decode(text) rustg_url_decode(text)
#endif
// RUSTG Version //
#define RUST_G_VERSION "0.4.5-P3"
+8
View File
@@ -34,8 +34,14 @@
#define STATUS_EFFECT_BLOODDRUNK /datum/status_effect/blooddrunk //Stun immunity and greatly reduced damage taken
#define STATUS_EFFECT_SPEEDLEGS /datum/status_effect/speedlegs //Handles cling speed boost and chemical cost.
#define STATUS_EFFECT_BLOOD_SWELL /datum/status_effect/bloodswell //stun resistance and halved damage for gargantua vampires
#define STATUS_EFFECT_BLOOD_RUSH /datum/status_effect/blood_rush // speed boost for gargantua vampires
/////////////
// DEBUFFS //
/////////////
@@ -80,6 +86,8 @@
#define STATUS_EFFECT_HIGHFIVE /datum/status_effect/high_five
#define STATUS_EFFECT_CHARGING /datum/status_effect/charging
//#define STATUS_EFFECT_SIGILMARK /datum/status_effect/sigil_mark
#define STATUS_EFFECT_CRUSHERDAMAGETRACKING /datum/status_effect/crusher_damage //tracks total kinetic crusher damage on a target
+11
View File
@@ -0,0 +1,11 @@
#define SUBCLASS_HEMOMANCER /datum/vampire_subclass/hemomancer
#define SUBCLASS_GARGANTUA /datum/vampire_subclass/gargantua
#define SUBCLASS_UMBRAE /datum/vampire_subclass/umbrae
#define SUBCLASS_ANCIENT /datum/vampire_subclass/ancient
#define BLOOD_DRAIN_LIMIT 200 // the amount of blood a vampire can drain from a person.
#define FULLPOWER_DRAINED_REQUIREMENT 8 // the number of people you need to suck to become full powered.
#define FULLPOWER_BLOODTOTAL_REQUIREMENT 1000 // the amount of blood you need to suck to get full power.
#define VAMPIRE_NULLIFICATION_CAP 120 // the maximum amount a vampire can be nullified naturally.
#define VAMPIRE_COMPLETE_NULLIFICATION 100 // the point of nullification where vampires can no longer use abilities.
+40
View File
@@ -0,0 +1,40 @@
// This file contains procs for interacting with the internal API
/**
* Internal API Caller
*
* Makes calls to the internal Paradise API and returns a [/datum/http_response].
*
* Arguments:
* * method - The relevant HTTP method to use
* * path - The path of the API call. DO NOT USE A LEADING SLASH
* * body - The request body, if applicable
*/
/proc/MakeAPICall(method, path, body)
if(!method || !path)
// Needs valid params
return null
if(!GLOB.configuration.system.api_host || !GLOB.configuration.system.api_key)
// Needs these set in config
return null
if(IsAdminAdvancedProcCall())
// Admins shouldnt fuck with this
to_chat(usr, "<span class='boldannounce'>API interaction blocked: Advanced ProcCall detected.</span>")
message_admins("[key_name(usr)] attempted to interact with the internal API via advanced proc-call")
log_admin("[key_name(usr)] attempted to interact with the internal API via advanced proc-call")
return
var/datum/http_request/req = new()
var/target_url = "[GLOB.configuration.system.api_host]/[path]"
// You may be asking, "Hey AA, why is the above a var instead of just using it directly?"
// Ill tell you why. This lets you breakpoint it in the debugger to see if the URL is
// what you wanted or not, given how slashes and stuff can break it.
req.prepare(method, target_url, body, list("AuthKey" = GLOB.configuration.system.api_key))
req.begin_async()
// Check if we are complete
UNTIL(req.is_complete())
var/datum/http_response/res = req.into_response()
return res
+2
View File
@@ -10,6 +10,8 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list)
//hair
init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list)
//hair gradients
init_sprite_accessory_subtypes(/datum/sprite_accessory/hair_gradient, GLOB.hair_gradients_list)
//facial hair
init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list)
//underwear
+3
View File
@@ -179,10 +179,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NODECAY "no_decay"
#define TRAIT_NOEXAMINE "no_examine"
#define TRAIT_NOPAIN "no_pain"
#define TRAIT_FORCE_DOORS "force_doors"
//***** ITEM TRAITS *****//
/// Show what machine/door wires do when held.
#define TRAIT_SHOW_WIRE_INFO "show_wire_info"
#define TRAIT_BUTCHERS_HUMANS "butchers_humans"
//
// common trait sources
@@ -197,6 +199,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define CLOTHING_TRAIT "clothing"
#define CULT_TRAIT "cult"
#define INNATE_TRAIT "innate"
#define VAMPIRE_TRAIT "vampire"
// unique trait sources
#define STATUE_MUTE "statue"
+3 -5
View File
@@ -2047,9 +2047,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
)
return _list
/// Waits at a line of code until X is true
#define UNTIL(X) while(!(X)) sleep(world.tick_lag)
// Check if the source atom contains another atom
/atom/proc/contains(atom/location)
if(!location)
@@ -2061,11 +2058,12 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
/proc/log_connection(ckey, ip, cid, connection_type)
ASSERT(connection_type in list(CONNECTION_TYPE_ESTABLISHED, CONNECTION_TYPE_DROPPED_IPINTEL, CONNECTION_TYPE_DROPPED_BANNED, CONNECTION_TYPE_DROPPED_INVALID))
var/datum/db_query/query_accesslog = SSdbcore.NewQuery("INSERT INTO connection_log (`datetime`, `ckey`, `ip`, `computerid`, `result`) VALUES(Now(), :ckey, :ip, :cid, :result)", list(
var/datum/db_query/query_accesslog = SSdbcore.NewQuery("INSERT INTO connection_log (`datetime`, `ckey`, `ip`, `computerid`, `result`, `server_id`) VALUES(Now(), :ckey, :ip, :cid, :result, :server_id)", list(
"ckey" = ckey,
"ip" = "[ip ? ip : ""]", // This is important. NULL is not the same as "", and if you directly open the `.dmb` file, you get a NULL IP.
"cid" = cid,
"result" = connection_type
"result" = connection_type,
"server_id" = GLOB.configuration.system.instance_id
))
query_accesslog.warn_execute()
qdel(query_accesslog)
+1 -1
View File
@@ -11,7 +11,7 @@ GLOBAL_LIST_INIT(hair_styles_full_list, list()) //fluff hair styles
GLOBAL_LIST_INIT(facial_hair_styles_list, list()) //stores /datum/sprite_accessory/facial_hair indexed by name
GLOBAL_LIST_INIT(facial_hair_styles_male_list, list())
GLOBAL_LIST_INIT(facial_hair_styles_female_list, list())
GLOBAL_LIST_INIT(skin_styles_female_list, list()) //unused
GLOBAL_LIST_EMPTY(hair_gradients_list) //stores /datum/sprite_accessory/hair_gradient indexed by name
//Underwear
GLOBAL_LIST_INIT(underwear_list, list()) //stores /datum/sprite_accessory/underwear indexed by name
GLOBAL_LIST_INIT(underwear_m, list()) //stores only underwear name
+4 -2
View File
@@ -60,10 +60,12 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NOGERMS" = TRAIT_NOGERMS,
"TRAIT_NODECAY" = TRAIT_NODECAY,
"TRAIT_NOEXAMINE" = TRAIT_NOEXAMINE,
"TRAIT_NOPAIN" = TRAIT_NOPAIN
"TRAIT_NOPAIN" = TRAIT_NOPAIN,
"TRAIT_FORCE_DOORS" = TRAIT_FORCE_DOORS,
),
/obj/item = list(
"TRAIT_SHOW_WIRE_INFO" = TRAIT_SHOW_WIRE_INFO
"TRAIT_SHOW_WIRE_INFO" = TRAIT_SHOW_WIRE_INFO,
"TRAIT_BUTCHER_HUMANS" = TRAIT_BUTCHERS_HUMANS
)
))
+7
View File
@@ -622,6 +622,13 @@ so as to remain in compliance with the most up-to-date laws."
stone = null
return ..()
/obj/screen/alert/notify_mapvote
name = "Map Vote"
desc = "Vote on which map you would like to play on next!"
icon_state = "map_vote"
/obj/screen/alert/notify_mapvote/Click()
SSvote.browse_to(usr.client)
//OBJECT-BASED
+2 -11
View File
@@ -192,17 +192,12 @@
if(L.view_sized != C.view)
L.update_o(C.view)
var/change_x
var/change_y
if(L.absolute)
L.offset_x = -(posobj.x - SSparallax.planet_x_offset) * L.speed
L.offset_y = -(posobj.y - SSparallax.planet_y_offset) * L.speed
else
change_x = offset_x * L.speed
L.offset_x -= change_x
change_y = offset_y * L.speed
L.offset_y -= change_y
L.offset_x -= offset_x * L.speed
L.offset_y -= offset_y * L.speed
if(L.offset_x > 240)
L.offset_x -= 480
@@ -213,10 +208,6 @@
if(L.offset_y < -240)
L.offset_y += 480
if(!(areaobj.parallax_movedir && areaobj.moving) && C.dont_animate_parallax <= world.time && (offset_x || offset_y) && abs(offset_x) <= max(C.parallax_throttle/world.tick_lag+1,1) && abs(offset_y) <= max(C.parallax_throttle/world.tick_lag+1,1) && (round(abs(change_x)) > 1 || round(abs(change_y)) > 1))
L.transform = matrix(1, 0, offset_x*L.speed, 0, 1, offset_y*L.speed)
animate(L, transform=matrix(), time = last_delay)
L.screen_loc = "CENTER-7:[round(L.offset_x,1)],CENTER-7:[round(L.offset_y,1)]"
/atom/movable/proc/update_parallax_contents()
+11 -4
View File
@@ -72,6 +72,7 @@
playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), 1, -1)
else
SEND_SIGNAL(M, COMSIG_ITEM_ATTACK)
add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), TRUE, extrarange = stealthy_audio ? SILENCED_SOUND_EXTRARANGE : -1, falloff_distance = 0)
@@ -81,8 +82,6 @@
user.do_attack_animation(M)
. = M.attacked_by(src, user, def_zone)
add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
add_fingerprint(user)
@@ -100,14 +99,22 @@
return
/obj/attacked_by(obj/item/I, mob/living/user)
var/damage = I.force
if(I.force)
user.visible_message("<span class='danger'>[user] has hit [src] with [I]!</span>", "<span class='danger'>You hit [src] with [I]!</span>")
take_damage(I.force, I.damtype, MELEE, 1)
if(ishuman(user))
var/mob/living/carbon/human/H = user
damage += H.physiology.melee_bonus
take_damage(damage, I.damtype, MELEE, 1)
/mob/living/attacked_by(obj/item/I, mob/living/user, def_zone)
send_item_attack_message(I, user)
if(I.force)
apply_damage(I.force, I.damtype, def_zone)
var/bonus_damage = 0
if(ishuman(user))
var/mob/living/carbon/human/H = user
bonus_damage = H.physiology.melee_bonus
apply_damage(I.force + bonus_damage, I.damtype, def_zone)
if(I.damtype == BRUTE)
if(prob(33))
I.add_mob_blood(src)
@@ -46,6 +46,8 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new())
var/datum/configuration_section/url_configuration/url
/// Holder for the voting configuration datum
var/datum/configuration_section/vote_configuration/vote
/// Raw data. Stored here to avoid passing data between procs constantly
var/list/raw_data = list()
/datum/server_configuration/Destroy(force)
@@ -53,6 +55,16 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new())
// This is going to stay existing. I dont care.
return QDEL_HINT_LETMELIVE
/datum/server_configuration/vv_get_var(var_name)
if(var_name == "raw_data") // NO!
return FALSE
. = ..()
/datum/server_configuration/vv_edit_var(var_name, var_value)
if(var_name == "raw_data") // NO!
return FALSE
. = ..()
/datum/server_configuration/CanProcCall(procname)
return FALSE // No thanks
@@ -85,34 +97,66 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new())
var/config_file = "config/config.toml"
if(!fexists(config_file))
config_file = "config/example/config.toml" // Fallback to example if user hasnt setup config properly
var/raw_json = rustg_toml2json(config_file)
var/list/raw_config_data = json_decode(raw_json)
raw_data = rustg_read_toml_file(config_file)
// Now pass through all our stuff
admin.load_data(raw_config_data["admin_configuration"])
afk.load_data(raw_config_data["afk_configuration"])
custom_sprites.load_data(raw_config_data["custom_sprites_configuration"])
database.load_data(raw_config_data["database_configuration"])
discord.load_data(raw_config_data["discord_configuration"])
event.load_data(raw_config_data["event_configuration"])
gamemode.load_data(raw_config_data["gamemode_configuration"])
gateway.load_data(raw_config_data["gateway_configuration"])
general.load_data(raw_config_data["general_configuration"])
ipintel.load_data(raw_config_data["ipintel_configuration"])
jobs.load_data(raw_config_data["job_configuration"])
logging.load_data(raw_config_data["logging_configuration"])
mc.load_data(raw_config_data["mc_configuration"])
metrics.load_data(raw_config_data["metrics_configuration"])
movement.load_data(raw_config_data["movement_configuration"])
overflow.load_data(raw_config_data["overflow_configuration"])
ruins.load_data(raw_config_data["ruin_configuration"])
system.load_data(raw_config_data["system_configuration"])
url.load_data(raw_config_data["url_configuration"])
vote.load_data(raw_config_data["voting_configuration"])
load_all_sections()
// Clear our list to save RAM
raw_data = list()
// And report the load
DIRECT_OUTPUT(world.log, "Config loaded in [stop_watch(start)]s")
/datum/server_configuration/proc/load_all_sections()
safe_load(admin, "admin_configuration")
safe_load(afk, "afk_configuration")
safe_load(custom_sprites, "custom_sprites_configuration")
safe_load(database, "database_configuration")
safe_load(discord, "discord_configuration")
safe_load(event, "event_configuration")
safe_load(gamemode, "gamemode_configuration")
safe_load(gateway, "gateway_configuration")
safe_load(general, "general_configuration")
safe_load(ipintel, "ipintel_configuration")
safe_load(jobs, "job_configuration")
safe_load(logging, "logging_configuration")
safe_load(mc, "mc_configuration")
safe_load(metrics, "metrics_configuration")
safe_load(movement, "movement_configuration")
safe_load(overflow, "overflow_configuration")
safe_load(ruins, "ruin_configuration")
safe_load(system, "system_configuration")
safe_load(url, "url_configuration")
safe_load(vote, "voting_configuration")
// Proc to load up instance-specific overrides
/datum/server_configuration/proc/load_overrides()
var/override_file = "config/overrides_[world.port].toml"
if(!fexists(override_file))
DIRECT_OUTPUT(world.log, "Overrides not found for this instance.")
return
DIRECT_OUTPUT(world.log, "Overrides found for this instance. Loading them.")
var/start = start_watch() // Time tracking
raw_data = rustg_read_toml_file(override_file)
// Now safely load our overrides.
// Due to the nature of config wrappers, only vars that exist in the config file are applied to the config datums.
// This means that an override missing a key doesnt null it out from the main server
load_all_sections()
// Clear our list to save RAM
raw_data = list()
// And report the load
DIRECT_OUTPUT(world.log, "Config overrides loaded in [stop_watch(start)]s")
// Only loads the data for a config section if that key exists in the JSON
/datum/server_configuration/proc/safe_load(datum/configuration_section/CS, section)
if(!isnull(raw_data[section]))
CS.load_data(raw_data[section])
/datum/configuration_section
/// See __config_defines.dm
@@ -14,10 +14,16 @@
var/is_production = FALSE
/// If above is true, you can run a shell command
var/shutdown_shell_command = null
/// 2FA backend server host
var/_2fa_auth_host = null
/// Internal API host
var/api_host = null
/// Internal API key
var/api_key = null
/// List of IP addresses which bypass world topic rate limiting
var/list/topic_ip_ratelimit_bypass = list()
/// Server instance ID
var/instance_id = "paradise_main"
/// Server internal IP
var/internal_ip = "127.0.0.1"
/datum/configuration_section/system_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
@@ -28,6 +34,10 @@
CONFIG_LOAD_STR(medal_hub_address, data["medal_hub_address"])
CONFIG_LOAD_STR(medal_hub_password, data["medal_hub_password"])
CONFIG_LOAD_STR(shutdown_shell_command, data["shutdown_shell_command"])
CONFIG_LOAD_STR(_2fa_auth_host, data["_2fa_auth_host"])
CONFIG_LOAD_STR(api_host, data["api_host"])
CONFIG_LOAD_STR(api_key, data["api_key"])
CONFIG_LOAD_LIST(topic_ip_ratelimit_bypass, data["topic_ip_ratelimit_bypass"])
CONFIG_LOAD_STR(instance_id, data["instance_id"])
CONFIG_LOAD_STR(internal_ip, data["internal_ip"])
+1 -1
View File
@@ -62,7 +62,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
/// For scheduling different subsystems for different stages of the round
var/current_runlevel
/// Do we want to sleep until players log in?
var/sleep_offline_after_initializations = TRUE
var/sleep_offline_after_initializations = FALSE // No we dont
var/static/restart_clear = 0
var/static/restart_timeout = 0
+4 -3
View File
@@ -287,8 +287,8 @@ SUBSYSTEM_DEF(blackbox)
lakey = L.lastattackerckey
var/datum/db_query/deathquery = SSdbcore.NewQuery({"
INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord)
VALUES (:name, :key, :job, :special, :pod, NOW(), :laname, :lakey, :gender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)"},
INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord, server_id)
VALUES (:name, :key, :job, :special, :pod, NOW(), :laname, :lakey, :gender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord, :server_id)"},
list(
"name" = L.real_name,
"key" = L.key,
@@ -302,7 +302,8 @@ SUBSYSTEM_DEF(blackbox)
"fireloss" = L.getFireLoss(),
"brainloss" = L.getBrainLoss(),
"oxyloss" = L.getOxyLoss(),
"coord" = "[L.x], [L.y], [L.z]"
"coord" = "[L.x], [L.y], [L.z]",
"server_id" = GLOB.configuration.system.instance_id
)
)
deathquery.warn_execute()
+2 -2
View File
@@ -160,8 +160,8 @@ SUBSYSTEM_DEF(dbcore)
if(!IsConnected())
return
var/datum/db_query/query_round_initialize = SSdbcore.NewQuery(
"INSERT INTO round (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(:internet_address), :port)",
list("internet_address" = world.internet_address || "0", "port" = "[world.port]")
"INSERT INTO round (initialize_datetime, server_ip, server_port, server_id) VALUES (Now(), INET_ATON(:internet_address), :port, :server_id)",
list("internet_address" = world.internet_address || "0", "port" = "[world.port]", "server_id" = GLOB.configuration.system.instance_id)
)
query_round_initialize.Execute(async = FALSE)
GLOB.round_id = "[query_round_initialize.last_insert_id]"
+3 -3
View File
@@ -27,7 +27,7 @@ SUBSYSTEM_DEF(discord)
webhook_urls = GLOB.configuration.discord.mentor_webhook_urls
var/datum/discord_webhook_payload/dwp = new()
dwp.webhook_content = content
dwp.webhook_content = "**\[[GLOB.configuration.system.instance_id]]** [content]"
for(var/url in webhook_urls)
SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json"))
@@ -73,7 +73,7 @@ SUBSYSTEM_DEF(discord)
var/message = "[content] [alerttext] [add_ping ? handle_administrator_ping() : ""]"
var/datum/discord_webhook_payload/dwp = new()
dwp.webhook_content = message
dwp.webhook_content = "**\[[GLOB.configuration.system.instance_id]]** [message]"
for(var/url in GLOB.configuration.discord.admin_webhook_urls)
SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json"))
@@ -94,7 +94,7 @@ SUBSYSTEM_DEF(discord)
var/message = "[content] [alerttext][add_ping ? handle_mentor_ping() : ""]"
var/datum/discord_webhook_payload/dwp = new()
dwp.webhook_content = message
dwp.webhook_content = "**\[[GLOB.configuration.system.instance_id]]** [message]"
for(var/url in GLOB.configuration.discord.mentor_webhook_urls)
SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json"))
+184
View File
@@ -0,0 +1,184 @@
SUBSYSTEM_DEF(instancing)
name = "Instancing"
runlevels = RUNLEVEL_INIT | RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 30 SECONDS
flags = SS_KEEP_TIMING
/datum/controller/subsystem/instancing/Initialize(start_timeofday)
// Dont even bother if we arent connected
if(!SSdbcore.IsConnected())
flags |= SS_NO_FIRE
return ..()
update_heartbeat() // Make sure you do this before announcing to peers, or no one will hear your announcement
var/startup_msg = "The server <code>[GLOB.configuration.general.server_name]</code> is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name]). You can connect with the <code>Switch Server</code> verb."
message_all_peers(startup_msg)
return ..()
/datum/controller/subsystem/instancing/fire(resumed)
update_heartbeat()
update_playercache()
/**
* Playercache updater
*
* Updates the player cache in the DB. Different from heartbeat so we can force invoke it on player operations
*/
/datum/controller/subsystem/instancing/proc/update_playercache(optional_ckey)
// You may be wondering, why the fuck is an "optional ckey" variable here
// Well, this is invoked in client/New(), and needs to read from GLOB.clients
// However, this proc sleeps, and if you sleep during client/New() once the client is in GLOB.clients, stuff breaks bad
// (See my comment rambling in client/New())
// By passing the ckey through, we can sleep in this proc and still get the data
if(!SSdbcore.IsConnected())
return
// First iterate clients to get ckeys
var/list/ckeys = list()
for(var/client/C in GLOB.clients) // No code review. I am not doing the `as anything` bullshit, because we *do* need the type checks here to avoid null clients which do happen sometimes
ckeys += C.ckey
// Add our optional
if(optional_ckey)
ckeys += optional_ckey
// Note: We dont have to sort the list here. The only time this is read for is a search,
// and order doesnt matter for that.
var/ckey_json = json_encode(ckeys)
// Yes I care about performance savings this much here to mass execute this shit
var/list/datum/db_query/queries = list()
queries += SSdbcore.NewQuery("UPDATE instance_data_cache SET key_value=:json WHERE key_name='playerlist' AND server_id=:sid", list(
"json" = ckey_json,
"sid" = GLOB.configuration.system.instance_id
))
queries += SSdbcore.NewQuery("UPDATE instance_data_cache SET key_value=:count WHERE key_name='playercount' AND server_id=:sid", list(
"count" = length(ckeys),
"sid" = GLOB.configuration.system.instance_id
))
SSdbcore.MassExecute(queries, TRUE, TRUE, FALSE, FALSE)
/**
* Heartbeat updater
*
* Updates the heartbeat in the DB. Used so other servers can see when this one was alive
*/
/datum/controller/subsystem/instancing/proc/update_heartbeat()
// this could probably just go in fire() but clean code and profiler ease who cares
var/datum/db_query/dbq = SSdbcore.NewQuery("UPDATE instance_data_cache SET key_value=NOW() WHERE key_name='heartbeat' AND server_id=:sid", list(
"sid" = GLOB.configuration.system.instance_id
))
dbq.warn_execute()
qdel(dbq)
/**
* Seed data
*
* Seeds all our data into the DB for other servers to discover from.
* This is called during world/New() instead of on initialize so it can be done *instantly*
*/
/datum/controller/subsystem/instancing/proc/seed_data()
// We need to seed a lot of keys, so lets just use a key-value-pair-map to do this easily
var/list/kvp_map = list()
kvp_map["server_name"] = GLOB.configuration.general.server_name // Name of the server
kvp_map["server_port"] = world.port // Server port (used for redirection and topics)
kvp_map["topic_key"] = GLOB.configuration.system.topic_key // Server topic key (used for topics)
kvp_map["internal_ip"] = GLOB.configuration.system.internal_ip // Server internal IP (used for topics)
kvp_map["playercount"] = length(GLOB.clients) // Server client count (used for status info)
kvp_map["playerlist"] = json_encode(list()) // Server client list. Used for dupe login checks. This gets filled in later
kvp_map["heartbeat"] = SQLtime() // SQL timestamp for heartbeat purposes. Any server without a heartbeat in the last 60 seconds can be considered dead
// Also note for above. You may say "But AA you dont need to JSON encode it, just use "\[]"."
// Well to that I say, no. This is meant to be JSON regardless, and it should represent that. This proc is ran once during world/New()
// An extra nanosecond of load will make zero difference.
for(var/key in kvp_map)
var/datum/db_query/dbq = SSdbcore.NewQuery("INSERT INTO instance_data_cache (server_id, key_name, key_value) VALUES (:sid, :kn, :kv) ON DUPLICATE KEY UPDATE key_value=:kv2", // Is this necessary? Who knows!
list(
"sid" = GLOB.configuration.system.instance_id,
"kn" = key,
"kv" = "[kvp_map[key]]", // String encoding IS necessary since these tables use strings, not ints
"kv2" = "[kvp_map[key]]", // Dont know if I need the second but better to be safe
)
)
dbq.warn_execute(FALSE) // Do NOT async execute here because world/New() shouldnt sleep. EVER. You get issues if you do.
qdel(dbq)
/**
* Message all peers
*
* Wrapper for [topic_all_peers] to format the input into a message topic. Will send a server-wide announcement to the other servers
*
* Arguments:
* * message - Message to send to the other servers
*/
/datum/controller/subsystem/instancing/proc/message_all_peers(message)
if(!SSdbcore.IsConnected())
return
var/topic_string = "instance_announce&msg=[url_encode(message)]"
topic_all_peers(topic_string)
/**
* Sends a topic to all peers
*
* Sends a raw topic to the other servers. WILL APPEND &key=[commskey] ON THE END. PLEASE ACCOUNT FOR THIS.
*
* Arguments:
* * raw_topic - The raw topic to send to the other servers
*/
/datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic)
// Someone here is going to say "AA you shouldnt put load on the DB server you can do sorting in BYOND"
// Well let me put it this way. The DB server is an entirely different machine to BYOND, with this entire dataset being stored in its RAM, not even on disk
// By making the DB server do the work, we can offload from BYOND, which is already strained
var/datum/db_query/dbq1 = SSdbcore.NewQuery({"
SELECT server_id, key_name, key_value FROM instance_data_cache WHERE server_id IN
(SELECT server_id FROM instance_data_cache WHERE server_id !=:sid AND
key_name='heartbeat' AND last_updated BETWEEN NOW() - INTERVAL 60 SECOND AND NOW())
AND key_name IN ("topic_key", "internal_ip", "server_port")"}, list(
"sid" = GLOB.configuration.system.instance_id
))
if(!dbq1.warn_execute())
qdel(dbq1)
return
var/servers_outer = list()
while(dbq1.NextRow())
if(!servers_outer[dbq1.item[1]])
servers_outer[dbq1.item[1]] = list()
servers_outer[dbq1.item[1]][dbq1.item[2]] = dbq1.item[3] // This should assoc load our data
qdel(dbq1)
for(var/server in servers_outer)
var/server_data = servers_outer[server]
world.Export("byond://[server_data["internal_ip"]]:[server_data["server_port"]]?[raw_topic]&key=[server_data["topic_key"]]")
/**
* Player checker
*
* Check all connected peers to see if a player exists in the player cache
* This is used to make sure players dont log into 2 servers at once
* Arguments:
* ckey - The ckey to check if they are logged into another server
*/
/datum/controller/subsystem/instancing/proc/check_player(ckey)
// Please see above rant on L127
var/datum/db_query/dbq1 = SSdbcore.NewQuery({"
SELECT server_id, key_value FROM instance_data_cache WHERE server_id IN
(SELECT server_id FROM instance_data_cache WHERE server_id != :sid AND
key_name='heartbeat' AND last_updated BETWEEN NOW() - INTERVAL 60 SECOND AND NOW())
AND key_name IN ("playerlist")"}, list(
"sid" = GLOB.configuration.system.instance_id
))
if(!dbq1.warn_execute())
qdel(dbq1)
return
while(dbq1.NextRow())
var/list/other_server_cache = json_decode(dbq1.item[2])
if(ckey in other_server_cache)
var/target_server = dbq1.item[1] // Yes. This var is necessary.
qdel(dbq1)
return target_server
qdel(dbq1)
return null // If we are here, it means we didnt find our player on another server
+1
View File
@@ -28,6 +28,7 @@ SUBSYSTEM_DEF(metrics)
out["elapsed_real"] = (REALTIMEOFDAY - world_init_time)
out["client_count"] = length(GLOB.clients)
out["round_id"] = text2num(GLOB.round_id) // This is so we can filter the metrics by a single round ID
out["server_id"] = GLOB.configuration.system.instance_id // And this is so we can filter by instance ID
// Funnel in all SS metrics
var/list/ss_data = list()
+3 -2
View File
@@ -18,10 +18,11 @@ SUBSYSTEM_DEF(statistics)
return
else
var/datum/db_query/statquery = SSdbcore.NewQuery(
"INSERT INTO legacy_population (playercount, admincount, time) VALUES (:playercount, :admincount, NOW())",
"INSERT INTO legacy_population (playercount, admincount, time, server_id) VALUES (:playercount, :admincount, NOW(), :server_id)",
list(
"playercount" = length(GLOB.clients),
"admincount" = length(GLOB.admins)
"admincount" = length(GLOB.admins),
"server_id" = GLOB.configuration.system.instance_id
)
)
statquery.warn_execute()
+54 -1
View File
@@ -408,12 +408,65 @@ SUBSYSTEM_DEF(ticker)
if(player.mind.assigned_role != player.mind.special_role)
SSjobs.AssignRank(player, player.mind.assigned_role, FALSE)
SSjobs.EquipRank(player, player.mind.assigned_role, FALSE)
EquipCustomItems(player)
equip_cuis(player)
if(captainless)
for(var/mob/M in GLOB.player_list)
if(!isnewplayer(M))
to_chat(M, "Captainship not forced on anyone.")
/datum/controller/subsystem/ticker/proc/equip_cuis(mob/living/carbon/human/H)
if(!H.client)
return // If they are spawning without a client (somehow), they *cant* have a CUI list
for(var/datum/custom_user_item/cui in H.client.cui_entries)
// Skip items with invalid character names
if((cui.characer_name != H.real_name) && !cui.all_characters_allowed)
continue
var/ok = FALSE
if(!cui.all_jobs_allowed)
var/alt_blocked = FALSE
if(H.mind.role_alt_title)
if(!(H.mind.role_alt_title in cui.allowed_jobs))
alt_blocked = TRUE
if(!(H.mind.assigned_role in cui.allowed_jobs) || alt_blocked)
continue
var/obj/item/I = new cui.object_typepath()
var/name_override = cui.item_name_override
var/desc_override = cui.item_desc_override
if(name_override)
I.name = name_override
if(desc_override)
I.desc = desc_override
if(istype(H.back, /obj/item/storage)) // Try to place it in something on the mob's back
var/obj/item/storage/S = H.back
if(length(S.contents) < S.storage_slots)
I.forceMove(H.back)
ok = TRUE
to_chat(H, "<span class='notice'>Your [I.name] has been added to your [H.back.name].</span>")
if(!ok)
for(var/obj/item/storage/S in H.contents) // Try to place it in any item that can store stuff, on the mob.
if(length(S.contents) < S.storage_slots)
I.forceMove(S)
ok = TRUE
to_chat(H, "<span class='notice'>Your [I.name] has been added to your [S.name].</span>")
break
if(!ok) // Finally, since everything else failed, place it on the ground
var/turf/T = get_turf(H)
if(T)
I.forceMove(T)
to_chat(H, "<span class='notice'>Your [I.name] is on the [T.name] below you.</span>")
else
to_chat(H, "<span class='notice'>Your [I.name] couldnt spawn anywhere on you or even on the floor below you. Please file a bug report.</span>")
qdel(I)
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
var/m
if(selected_tip)
+5 -1
View File
@@ -266,8 +266,12 @@ SUBSYSTEM_DEF(vote)
<a href='?src=[UID()];vote=open'>Click here or type vote to place your vote.</a>
You have [GLOB.configuration.vote.vote_time / 10] seconds to vote.</font>"})
switch(vote_type)
if("crew transfer", "gamemode", "custom", "map")
if("crew transfer", "gamemode", "custom")
SEND_SOUND(world, sound('sound/ambience/alarm4.ogg'))
if("map")
SEND_SOUND(world, sound('sound/ambience/alarm4.ogg'))
for(var/mob/M in GLOB.player_list)
M.throw_alert("Map Vote", /obj/screen/alert/notify_mapvote, timeout_override = GLOB.configuration.vote.vote_time)
if(mode == "gamemode" && SSticker.ticker_going)
SSticker.ticker_going = FALSE
to_chat(world, "<font color='red'><b>Round start has been delayed.</b></font>")
+7
View File
@@ -423,6 +423,13 @@
/datum/action/item_action/remove_badge
name = "Remove Holobadge"
// Clown Acrobat Shoes
/datum/action/item_action/slipping
name = "Tactical Slip"
desc = "Activates the clown shoes' ankle-stimulating module, allowing the user to do a short slip forward going under anyone."
button_icon_state = "clown"
// Jump boots
/datum/action/item_action/bhop
name = "Activate Jump Boots"
+66
View File
@@ -0,0 +1,66 @@
/**
* # Custom User Item
*
* Holder for CUIs
*
* This datum is a holder that is essentially a "model" of the `customuseritems`
* database table, and is used for giving people their CUIs on spawn.
* It is instanced as part of the client data loading framework on the client.
*
*/
/datum/custom_user_item
/// Can this be used on all characters?
var/all_characters_allowed = FALSE
/// Name of the character that can have this item.
var/characer_name
/// Are all jobs allowed?
var/all_jobs_allowed = FALSE
/// List of allowed jobs
var/list/allowed_jobs = list()
/// Custom item typepath
var/object_typepath
/// Custom item name override
var/item_name_override
/// Custom item description override
var/item_desc_override
/// Raw job mask
var/raw_job_mask
/**
* CUI Info Parser
*
* Parses all the raw info into usable stuff, and also does validity checks
* Returns TRUE if its a valid item, and FALSE if not
*
* Arguments:
* * owning_ckey - Player who owns this item. Used for logging purposes.
*/
/datum/custom_user_item/proc/parse_info(owning_ckey)
. = FALSE // Setting this here means it will return false even if it runtimes
// Sort path
if(!object_typepath || !ispath(object_typepath))
stack_trace("Incorrect database entry found in table 'customuseritems' path value is [object_typepath ? object_typepath : "(NULL)"], which doesnt exist. Ask the host to look at CUI entries for [owning_ckey]")
return
// Sort job mask
if(raw_job_mask == "*")
all_jobs_allowed = TRUE
else
var/list/local_allowed_jobs = splittext(raw_job_mask, ",")
for(var/i in 1 to length(local_allowed_jobs))
if(istext(local_allowed_jobs[i]))
local_allowed_jobs[i] = trim(local_allowed_jobs[i])
allowed_jobs = local_allowed_jobs
// Sort character name
if(characer_name == "*")
all_characters_allowed = TRUE
return TRUE
/datum/custom_user_item/vv_edit_var(var_name, var_value)
return FALSE // fuck off
@@ -49,6 +49,7 @@ Bonus
healed += min(E.brute_dam, get_damage) + min(E.burn_dam, get_damage)
E.heal_damage(get_damage, get_damage, 0, 0)
M.adjustToxLoss(healed)
M.UpdateAppearance()
else
@@ -56,6 +57,7 @@ Bonus
M.adjustFireLoss(-get_damage)
M.adjustBruteLoss(-get_damage)
M.adjustToxLoss(get_damage)
M.UpdateAppearance()
else
return
+16 -3
View File
@@ -19,6 +19,8 @@
var/headers
/// URL that the request is being sent to
var/url
/// If present, response body will be saved to this file.
var/output_file
/// The raw response, which will be decoeded into a [/datum/http_response]
var/_raw_response
/// Callback for executing after async requests. Will be called with an argument of [/datum/http_response] as first argument
@@ -42,7 +44,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
* * _body - The body of the request, if applicable
* * _headers - Associative list of HTTP headers to send, if applicab;e
*/
/datum/http_request/proc/prepare(_method, _url, _body = "", list/_headers)
/datum/http_request/proc/prepare(_method, _url, _body = "", list/_headers, _output_file)
if(!length(_headers))
headers = ""
else
@@ -51,6 +53,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
method = _method
url = _url
body = _body
output_file = _output_file
/**
* Blocking executor
@@ -60,7 +63,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
*/
/datum/http_request/proc/execute_blocking()
CRASH("Attempted to execute a blocking HTTP request")
// _raw_response = rustg_http_request_blocking(method, url, body, headers)
// _raw_response = rustg_http_request_blocking(method, url, body, headers, build_options())
/**
* Async execution starter
@@ -73,7 +76,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
if(in_progress)
CRASH("Attempted to re-use a request object.")
id = rustg_http_request_async(method, url, body, headers)
id = rustg_http_request_async(method, url, body, headers, build_options())
if(isnull(text2num(id)))
_raw_response = "Proc error: [id]"
@@ -81,6 +84,16 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
else
in_progress = TRUE
/**
* Options builder
*
* Builds options for if we want to download files with SShttp
*/
/datum/http_request/proc/build_options()
if(output_file)
return json_encode(list("output_filename" = output_file, "body_filename" = null))
return null
/**
* Async completion checker
*
+6 -3
View File
@@ -86,7 +86,7 @@
return ..()
/datum/mind/proc/get_display_key()
var/clientKey = current?.client.get_display_key()
var/clientKey = current?.client?.get_display_key()
return clientKey ? clientKey : key
/datum/mind/proc/transfer_to(mob/living/new_character)
@@ -107,6 +107,8 @@
for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body
var/datum/antagonist/A = a
A.on_body_transfer(old_current, current)
if(vampire)
vampire.update_owner(new_character)
transfer_antag_huds(hud_to_transfer) //inherit the antag HUD
transfer_actions(new_character)
if(martial_art)
@@ -1544,14 +1546,15 @@
SSticker.mode.equip_syndicate(current)
/datum/mind/proc/make_Vampire()
/datum/mind/proc/make_vampire(ancient_vampire = FALSE)
if(!(src in SSticker.mode.vampires))
SSticker.mode.vampires += src
SSticker.mode.grant_vampire_powers(current)
special_role = SPECIAL_ROLE_VAMPIRE
SSticker.mode.forge_vampire_objectives(src)
SSticker.mode.greet_vampire(src)
SSticker.mode.update_vampire_icons_added(src)
if(!ancient_vampire)
SSticker.mode.forge_vampire_objectives(src)
/datum/mind/proc/make_Changeling()
if(!(src in SSticker.mode.changelings))
+9 -15
View File
@@ -1103,21 +1103,15 @@
if(H.mind)
if(!H.mind.vampire)
H.make_vampire()
if(H.mind.vampire)
H.mind.vampire.bloodusable = 9999
H.mind.vampire.bloodtotal = 9999
H.mind.vampire.check_vampire_upgrade(0)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shapeshift/bats)
to_chat(H, "You have gained the ability to shapeshift into bat form. This is a weak form with no abilities, only useful for stealth.")
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shapeshift/hellhound)
to_chat(H, "You have gained the ability to shapeshift into lesser hellhound form. This is a combat form with different abilities, tough but not invincible. It can regenerate itself over time by resting.")
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/raise_vampires)
to_chat(H, "You have gained the ability to Raise Vampires. This extremely powerful AOE ability affects all humans near you. Vampires/thralls are healed. Corpses are raised as vampires. Others are stunned, then brain damaged, then killed.")
H.dna.SetSEState(GLOB.jumpblock, 1)
singlemutcheck(H, GLOB.jumpblock, MUTCHK_FORCED)
H.update_mutations()
H.gene_stability = 100
H.mind.make_vampire(TRUE)
H.mind.vampire.bloodusable = 9999
H.mind.vampire.bloodtotal = 9999
H.mind.offstation_role = TRUE
H.mind.vampire.add_subclass(SUBCLASS_ANCIENT, FALSE)
H.dna.SetSEState(GLOB.jumpblock, TRUE)
singlemutcheck(H, GLOB.jumpblock, MUTCHK_FORCED)
H.update_mutations()
H.gene_stability = 100
/datum/outfit/admin/wizard
name = "Blue Wizard"
+47 -7
View File
@@ -131,6 +131,11 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
var/special_availability_check = 0//Whether the spell needs to bypass the action button's IsAvailable()
var/sound = null //The sound the spell makes when it is cast
/// If the ability is for vampires
var/vampire_ability = FALSE
var/required_blood = 0
var/gain_desc = null
var/deduct_blood_on_cast = TRUE
/* Checks if the user can cast the spell
* @param charge_check If the proc should do the cooldown check
@@ -190,6 +195,8 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
charge_counter = charge_max
else
start_recharge()
if(!gain_desc)
gain_desc = "You can now use [src]."
/obj/effect/proc_holder/spell/Destroy()
QDEL_NULL(action)
@@ -238,6 +245,9 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
action.UpdateButtonIcon()
/obj/effect/proc_holder/spell/proc/before_cast(list/targets)
if(vampire_ability)
if(!before_cast_vampire(targets))
return
if(overlay)
for(var/atom/target in targets)
var/location
@@ -594,25 +604,55 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
var/mob/living/carbon/human/H = user
var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list
var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list)
if(clothes_req && !clothcheck && !clothcheck2) //clothes check
if(clothes_req && !clothcheck && !clothcheck2 && !vampire_ability) //clothes check
var/obj/item/clothing/robe = H.wear_suit
var/obj/item/clothing/hat = H.head
var/obj/item/clothing/shoes = H.shoes
if(!robe || !hat || !shoes)
if(show_message)
to_chat(user, "<span class='notice'>Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.</span>")
return 0
return FALSE
if(!robe.magical || !hat.magical || !shoes.magical)
if(show_message)
to_chat(user, "<span class='notice'>Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.</span>")
return 0
return FALSE
else
if(clothes_req || human_req)
if(clothes_req || human_req || vampire_ability)
if(show_message)
to_chat(user, "<span class='notice'>This spell can only be cast by humans!</span>")
return 0
return FALSE
if(nonabstract_req && (isbrain(user) || ispAI(user)))
if(show_message)
to_chat(user, "<span class='notice'>This spell can only be cast by physical beings!</span>")
return 0
return 1
return FALSE
if(vampire_ability)
var/datum/vampire/vampire = user.mind.vampire
if(!vampire)
return FALSE
var/fullpower = vampire.get_ability(/datum/vampire_passive/full)
if(user.stat >= DEAD)
if(show_message)
to_chat(user, "<span class='warning'>Not while you're dead!</span>")
return FALSE
if(vampire.nullified >= VAMPIRE_COMPLETE_NULLIFICATION && !fullpower) // above 100 nullification vampire powers are useless
if(show_message)
to_chat(user, "<span class='warning'>Something is blocking your powers!</span>")
return FALSE
if(vampire.bloodusable < required_blood)
if(show_message)
to_chat(user, "<span class='warning'>You require at least [required_blood] units of usable blood to do that!</span>")
return FALSE
//chapel check
if(istype(get_area(user), /area/chapel) && !fullpower)
if(show_message)
to_chat(user, "<span class='warning'>Your powers are useless on this holy ground.</span>")
return FALSE
return TRUE
+42 -13
View File
@@ -12,16 +12,18 @@
include_user = 1
nonabstract_req = 1
centcom_cancast = 0 //Prevent people from getting to centcom
var/sound1 = 'sound/magic/ethereal_enter.ogg'
var/jaunt_duration = 50 //in deciseconds
var/jaunt_in_time = 5
var/jaunt_in_type = /obj/effect/temp_visual/wizard
var/jaunt_out_type = /obj/effect/temp_visual/wizard/out
var/jaunt_type_path = /obj/effect/dummy/spell_jaunt
var/jaunt_water_effect = TRUE
action_icon_state = "jaunt"
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets, mob/user = usr) //magnets, so mostly hardcoded
playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1)
playsound(get_turf(user), sound1, 50, 1, -1)
for(var/mob/living/target in targets)
if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains
to_chat(target, "<span class='warning'>You are somehow too bound to your current location to abandon it.</span>")
@@ -31,13 +33,14 @@
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target)
target.notransform = 1
var/turf/mobloc = get_turf(target)
var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(mobloc)
var/obj/effect/dummy/spell_jaunt/holder = new jaunt_type_path(mobloc)
new jaunt_out_type(mobloc, target.dir)
target.ExtinguishMob()
target.forceMove(holder)
target.reset_perspective(holder)
target.notransform = 0 //mob is safely inside holder now, no need for protection.
jaunt_steam(mobloc)
if(jaunt_water_effect)
jaunt_steam(mobloc)
sleep(jaunt_duration)
@@ -45,22 +48,28 @@
qdel(holder)
return
mobloc = get_turf(target.loc)
jaunt_steam(mobloc)
if(jaunt_water_effect)
jaunt_steam(mobloc)
target.canmove = 0
holder.reappearing = 1
playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1)
sleep(25 - jaunt_in_time)
sleep(jaunt_in_time * 4)
new jaunt_in_type(mobloc, holder.dir)
target.setDir(holder.dir)
sleep(jaunt_in_time)
qdel(holder)
if(!QDELETED(target))
if(mobloc.density)
if(is_blocked_turf(mobloc, TRUE))
for(var/turf/T in orange(7))
if(T)
if(target.Move(T))
break
target.canmove = 1
if(isspaceturf(T))
continue
if(target.Move(T))
target.remove_CC()
return
for(var/turf/space/S in orange(7))
if(target.Move(S))
break
target.remove_CC()
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc)
var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread()
@@ -90,14 +99,34 @@
return
var/turf/newLoc = get_step(src,direction)
setDir(direction)
if(!(newLoc.flags & NOJAUNT))
if(can_move(newLoc))
forceMove(newLoc)
else
to_chat(user, "<span class='warning'>Some strange aura is blocking the way!</span>")
to_chat(user, "<span class='warning'>Something is blocking the way!</span>")
movedelay = world.time + movespeed
/obj/effect/dummy/spell_jaunt/proc/can_move(turf/T)
if(T.flags & NOJAUNT)
return FALSE
return TRUE
/obj/effect/dummy/spell_jaunt/ex_act(blah)
return
/obj/effect/dummy/spell_jaunt/bullet_act(blah)
return
/obj/effect/dummy/spell_jaunt/blood_pool
name = "sanguine pool"
desc = "a pool of living blood."
movespeed = 1.5
/obj/effect/dummy/spell_jaunt/blood_pool/relaymove(mob/user, direction)
..()
new /obj/effect/decal/cleanable/blood(loc)
/obj/effect/dummy/spell_jaunt/blood_pool/can_move(turf/T)
if(isspaceturf(T) || T.density)
return FALSE
return TRUE
+3 -3
View File
@@ -93,11 +93,11 @@
/obj/effect/proc_holder/spell/targeted/mime/fingergun
name = "Finger Gun"
desc = "Shoot stunning, invisible bullets out of your fingers! 6 bullets available per cast. Use your fingers to holster them manually."
desc = "Shoot stunning, invisible bullets out of your fingers! 3 bullets available per cast. Use your fingers to holster them manually."
school = "mime"
panel = "Mime"
clothes_req = 0
charge_max = 600
charge_max = 300
range = -1
include_user = 1
human_req = 1
@@ -117,7 +117,7 @@
revert_cast(user)
/obj/effect/proc_holder/spell/targeted/mime/fingergun/fake
desc = "Pretend you're shooting bullets out of your fingers! 6 bullets available per cast. Use your fingers to holster them manually."
desc = "Pretend you're shooting bullets out of your fingers! 3 bullets available per cast. Use your fingers to holster them manually."
gun = /obj/item/gun/projectile/revolver/fingergun/fake
// Mime Spellbooks
+2
View File
@@ -95,6 +95,7 @@
invocation = "none"
invocation_type = "none"
action_icon_state = "vampire_bats"
gain_desc = "You have gained the ability to shapeshift into bat form. This is a weak form with no abilities, only useful for stealth."
shapeshift_type = /mob/living/simple_animal/hostile/scarybat/adminvampire
current_shapes = list(/mob/living/simple_animal/hostile/scarybat/adminvampire)
@@ -108,6 +109,7 @@
invocation_type = "none"
action_background_icon_state = "bg_demon"
action_icon_state = "glare"
gain_desc = "You have gained the ability to shapeshift into lesser hellhound form. This is a combat form with different abilities, tough but not invincible. It can regenerate itself over time by resting."
shapeshift_type = /mob/living/simple_animal/hostile/hellhound
current_shapes = list(/mob/living/simple_animal/hostile/hellhound)
+11 -2
View File
@@ -8,12 +8,16 @@
var/include_space = 0 //whether it includes space tiles in possible teleport locations
var/include_dense = 0 //whether it includes dense tiles in possible teleport locations
/// Whether the spell can teleport to light locations
var/include_light_turfs = TRUE
var/sound1 = 'sound/weapons/zapbang.ogg'
var/sound2 = 'sound/weapons/zapbang.ogg'
/obj/effect/proc_holder/spell/targeted/turf_teleport/cast(list/targets,mob/living/user = usr)
playsound(get_turf(user), sound1, 50,1)
if(sound1)
playsound(get_turf(user), sound1, 50,1)
for(var/mob/living/target in targets)
var/list/turfs = new/list()
for(var/turf/T in range(target,outer_tele_radius))
@@ -22,6 +26,10 @@
if(T.density && !include_dense) continue
if(T.x>world.maxx-outer_tele_radius || T.x<outer_tele_radius) continue //putting them at the edge is dumb
if(T.y>world.maxy-outer_tele_radius || T.y<outer_tele_radius) continue
if(!include_light_turfs)
var/lightingcount = T.get_lumcount() * 10
if(lightingcount > 2)
continue
turfs += T
if(!turfs.len)
@@ -37,4 +45,5 @@
return
target.forceMove(picked)
playsound(get_turf(user), sound2, 50,1)
if(sound2)
playsound(get_turf(user), sound2, 50,1)
+53
View File
@@ -134,6 +134,59 @@
if(islist(owner.stun_absorption) && owner.stun_absorption["blooddrunk"])
owner.stun_absorption -= "blooddrunk"
/datum/status_effect/bloodswell
id = "bloodswell"
duration = 30 SECONDS
tick_interval = 0
alert_type = /obj/screen/alert/status_effect/blood_swell
var/bonus_damage_applied = FALSE
/obj/screen/alert/status_effect/blood_swell
name = "Blood Swell"
desc = "Your body has been infused with crimson magics, your resistance to attacks has greatly increased!"
icon = 'icons/mob/actions/actions.dmi'
icon_state = "blood_swell_status"
/datum/status_effect/bloodswell/on_apply()
. = ..()
if(!. || !ishuman(owner))
return FALSE
ADD_TRAIT(owner, TRAIT_CHUNKYFINGERS, VAMPIRE_TRAIT)
var/mob/living/carbon/human/H = owner
H.physiology.brute_mod *= 0.5
H.physiology.burn_mod *= 0.8
H.physiology.stamina_mod *= 0.5
H.physiology.stun_mod *= 0.5
if(owner.mind.vampire.get_ability(/datum/vampire_passive/blood_swell_upgrade))
bonus_damage_applied = TRUE
H.physiology.melee_bonus += 10
H.dna.species.punchstunthreshold += 8 //higher chance to stun but not 100%
/datum/status_effect/bloodswell/on_remove()
if(!ishuman(owner))
return
REMOVE_TRAIT(owner, TRAIT_CHUNKYFINGERS, VAMPIRE_TRAIT)
var/mob/living/carbon/human/H = owner
H.physiology.brute_mod /= 0.5
H.physiology.burn_mod /= 0.8
H.physiology.stamina_mod /= 0.5
H.physiology.stun_mod /= 0.5
if(bonus_damage_applied)
bonus_damage_applied = FALSE
H.physiology.melee_bonus -= 10
H.dna.species.punchstunthreshold -= 8
/datum/status_effect/blood_rush
alert_type = null
duration = 10 SECONDS
/datum/status_effect/blood_rush/on_apply()
ADD_TRAIT(owner, TRAIT_GOTTAGOFAST, VAMPIRE_TRAIT)
return TRUE
/datum/status_effect/blood_rush/on_remove()
REMOVE_TRAIT(owner, TRAIT_GOTTAGOFAST, VAMPIRE_TRAIT)
/datum/status_effect/exercised
id = "Exercised"
duration = 1200
+4
View File
@@ -45,3 +45,7 @@
/datum/status_effect/high_five/on_remove()
owner.visible_message("[owner] was left hanging....")
/datum/status_effect/charging
id = "charging"
alert_type = null
+13 -9
View File
@@ -272,14 +272,6 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
cost = 15
containername = "laser crate"
/datum/supply_packs/security/taser
name = "Stun Guns Crate"
contains = list(/obj/item/gun/energy/gun/advtaser,
/obj/item/gun/energy/gun/advtaser,
/obj/item/gun/energy/gun/advtaser)
cost = 15
containername = "stun gun crate"
/datum/supply_packs/security/disabler
name = "Disabler Crate"
contains = list(/obj/item/gun/energy/disabler,
@@ -507,7 +499,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
/obj/item/reagent_containers/spray/pepper,
/obj/item/flash,
/obj/item/grenade/flashbang,
/obj/item/storage/belt/security/sec,
/obj/item/storage/belt/security,
/obj/item/holosign_creator/security,
/obj/item/clothing/mask/gas/sechailer,
/obj/item/clothing/glasses/hud/security/sunglasses,
@@ -1216,6 +1208,12 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
containertype = /obj/structure/closet/critter/deer
containername = "deer crate"
/datum/supply_packs/organic/bunny
name = "Bunny Crate"
cost = 20
containertype = /obj/structure/closet/critter/bunny
containername = "bunny crate"
////// hippy gear
/datum/supply_packs/organic/hydroponics // -- Skie
@@ -1456,6 +1454,12 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
containertype = /obj/structure/closet/crate/secure
containername = "shaft miner starter kit"
/datum/supply_packs/misc/carpet
name = "Carpet Crate"
cost = 20
contains = list(/obj/item/stack/tile/carpet/twenty)
containername = "carpet crate"
///////////// Paper Work
+24 -21
View File
@@ -167,18 +167,19 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
//Clown
/datum/uplink_item/jobspecific/clowngrenade
name = "Banana Grenade"
desc = "A grenade that explodes into HONK! brand banana peels that are genetically modified to be extra slippery and extrude caustic acid when stepped on"
desc = "A grenade that explodes into HONK! brand banana peels that are genetically modified to be extra slippery and extrude caustic acid when stepped on."
reference = "BG"
item = /obj/item/grenade/clown_grenade
cost = 5
job = list("Clown")
/datum/uplink_item/jobspecific/clownmagboots
name = "Clown Magboots"
desc = "A pair of modified clown shoes fitted with an advanced magnetic traction system. Look and sound exactly like regular clown shoes unless closely inspected."
reference = "CM"
item = /obj/item/clothing/shoes/magboots/clown
/datum/uplink_item/jobspecific/clownslippers
name = "Clown Acrobatic Shoes"
desc = "A pair of modified clown shoes fitted with a built-in propulsion system that allows the user to perform a short slip below anyone. Turning on the waddle dampeners removes the slowdown on the shoes."
reference = "CAS"
item = /obj/item/clothing/shoes/clown_shoes/slippers
cost = 3
surplus = 75
job = list("Clown")
/datum/uplink_item/jobspecific/trick_revolver
@@ -560,13 +561,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/twohanded/chainsaw
cost = 13
/datum/uplink_item/dangerous/batterer
name = "Mind Batterer"
desc = "A device that has a chance of knocking down people around you for a long amount of time. 50% chance per person. The user is unaffected. Has 5 charges."
reference = "BTR"
item = /obj/item/batterer
cost = 5
// SUPPORT AND MECHAS
/datum/uplink_item/support
@@ -707,12 +701,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 2
gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/ammo/bullstun
name = "Bulldog - 12g Stun Slug Magazine"
desc = "An alternative 8-round stun slug magazine for use in the Bulldog shotgun. Saying that they're completely non-lethal would be lying."
reference = "12SS"
item = /obj/item/ammo_box/magazine/m12g/stun
cost = 3
/datum/uplink_item/ammo/bullmeteor
name = "12g Meteorslug Shells"
desc = "An alternative 8-round meteorslug magazine for use in the Bulldog shotgun. Great for blasting airlocks off their frames and knocking down enemies."
reference = "12MS"
item = /obj/item/ammo_box/magazine/m12g/meteor
cost = 2
gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/ammo/bulldragon
@@ -876,6 +870,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/CQC_manual
cost = 13
cant_discount = TRUE
gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/stealthy_weapons/cameraflash
name = "Camera Flash"
@@ -972,6 +967,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 5
gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/stealthy_weapons/combat_minus
name = "Experimental Krav Gloves"
desc = "Experimental gloves with installed nanochips that teach you Krav Maga when worn, great as a cheap backup weapon. Warning, the nanochips will override any other fighting styles such as CQC. Do not look as fly as the Warden's"
reference = "CGM"
item = /obj/item/clothing/gloves/color/black/krav_maga
cost = 10
excludefrom = list(/datum/game_mode/nuclear)
// GRENADES AND EXPLOSIVES
/datum/uplink_item/explosives
@@ -979,7 +982,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/explosives/plastic_explosives
name = "Composition C-4"
desc = "C-4 is plastic explosive of the common variety Composition C. You can use it to breach walls or connect an assembly to its wiring to make it remotely detonable. It has a modifiable timer with a minimum setting of 10 seconds."
desc = "C-4 is plastic explosive of the common variety Composition C. Reliably destroys the object it's placed on, assuming it isn't bomb resistant. Does not stick to crewmembers. Will only destroy station floors if placed directly on it. It has a modifiable timer with a minimum setting of 10 seconds."
reference = "C4"
item = /obj/item/grenade/plastic/c4
cost = 1
@@ -1004,7 +1007,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
name = "Composition X-4"
desc = "X-4 is a shaped charge designed to be safe to the user while causing maximum damage to the occupants of the room beach breached. It has a modifiable timer with a minimum setting of 10 seconds."
reference = "X4"
item = /obj/item/grenade/plastic/x4
item = /obj/item/grenade/plastic/c4/x4
cost = 2
gamemodes = list(/datum/game_mode/nuclear)
+4 -3
View File
@@ -1371,8 +1371,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/security/brig/prison_break()
for(var/obj/structure/closet/secure_closet/brig/temp_closet in src)
temp_closet.locked = 0
temp_closet.icon_state = temp_closet.icon_closed
temp_closet.locked = FALSE
temp_closet.close()
for(var/obj/machinery/door_timer/temp_timer in src)
temp_timer.releasetime = 1
..()
@@ -1391,7 +1391,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/security/prison/prison_break()
for(var/obj/structure/closet/secure_closet/brig/temp_closet in src)
temp_closet.locked = 0
temp_closet.icon_state = temp_closet.icon_closed
temp_closet.close()
temp_closet.update_icon()
for(var/obj/machinery/door_timer/temp_timer in src)
temp_timer.releasetime = 1
..()
+1 -1
View File
@@ -501,7 +501,7 @@
log_game("Teleport spell failed - no other teleport runes")
return
if(!is_level_reachable(user.z))
to_chat(user, "<span class='cultitalic'>You are not in the right dimension!</span>")
to_chat(user, "<span class='cultitalic'>You are too far away from the station to teleport!</span>")
log_game("Teleport spell failed - user in away mission")
return
+5
View File
@@ -139,6 +139,11 @@
to_chat(user, "<span class='cultitalic'>The veil is not weak enough here to summon a cultist, you must be on station!</span>")
return
if(ispath(rune, /obj/effect/rune/teleport))
if(!is_level_reachable(user.z))
to_chat(user, "<span class='cultitalic'>You are too far away from the station to teleport!</span>")
return
var/old_color = user.color // we'll temporarily redden the user for better feedback to fellow cultists. Store this to revert them back.
if(narsie_rune)
if(!narsie_rune_check(user, A))
+1 -1
View File
@@ -461,7 +461,7 @@ structure_check() searches for nearby cultist structures required for the invoca
return
if(!is_level_reachable(user.z))
to_chat(user, "<span class='cultitalic'>You are not in the right dimension!</span>")
to_chat(user, "<span class='cultitalic'>You are too far away from the station to teleport!</span>")
log_game("Teleport rune failed - user in away mission")
fail_invoke()
return
@@ -786,3 +786,24 @@
/datum/AI_Module/large/cameracrack/upgrade(mob/living/silicon/ai/AI)
if(AI.builtInCamera)
QDEL_NULL(AI.builtInCamera)
/datum/AI_Module/large/engi_upgrade
module_name = "Engineering Cyborg Emitter Upgrade"
mod_pick_name = "emitter"
description = "Downloads firmware that activates the built in emitter in all engineering cyborgs linked to you. Cyborgs built after this upgrade will have it pre-installed."
cost = 50 // IDK look into this
one_purchase = TRUE
upgrade = TRUE
unlock_text = "<span class='notice'>Firmware downloaded. Bugs removed. Built in emitters operating at 73% efficiency.</span>"
unlock_sound = 'sound/items/rped.ogg'
/datum/AI_Module/large/engi_upgrade/upgrade(mob/living/silicon/ai/AI)
AI.purchased_modules += /obj/item/robot_module/engineering
log_game("[key_name(usr)] purchased emitters for all engineering cyborgs.")
message_admins("<span class='notice'>[key_name_admin(usr)] purchased emitters for all engineering cyborgs!</span>")
for(var/mob/living/silicon/robot/R in AI.connected_robots)
if(!istype(R.module, /obj/item/robot_module/engineering))
continue
R.module.malfhacked = TRUE
R.module.rebuild_modules()
to_chat(R, "<span class='notice'>New firmware downloaded. Emitter is now online.</span>")
@@ -736,7 +736,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
desc = "Contains secrets of the universe."
icon_state = "abductor"
icon_closed = "abductor"
icon_opened = "abductoropen"
icon_opened = "abductor_open"
material_drop = /obj/item/stack/sheet/mineral/abductor
/obj/structure/door_assembly/door_assembly_abductor
@@ -42,19 +42,32 @@
speed = 1
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
var/essence = 75 //The resource of revenants. Max health is equal to three times this amount
var/essence_regen_cap = 75 //The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount.
var/essence_regenerating = 1 //If the revenant regenerates essence or not; 1 for yes, 0 for no
var/essence_regen_amount = 5 //How much essence regenerates
var/essence_accumulated = 0 //How much essence the revenant has stolen
var/revealed = 0 //If the revenant can take damage from normal sources.
var/unreveal_time = 0 //How long the revenant is revealed for, is about 2 seconds times this var.
var/unstun_time = 0 //How long the revenant is stunned for, is about 2 seconds times this var.
var/inhibited = 0 //If the revenant's abilities are blocked by a chaplain's power.
var/essence_drained = 0 //How much essence the revenant has drained.
var/draining = 0 //If the revenant is draining someone.
var/list/drained_mobs = list() //Cannot harvest the same mob twice
var/perfectsouls = 0 //How many perfect, regen-cap increasing souls the revenant has.
///The resource of revenants. Max health is equal to three times this amount
var/essence = 75
///The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount.
var/essence_regen_cap = 75
///If the revenant regenerates essence or not; 1 for yes, 0 for no
var/essence_regenerating = TRUE
///How much essence regenerates
var/essence_regen_amount = 5
///How much essence the revenant has stolen
var/essence_accumulated = 0
///If the revenant can take damage from normal sources.
var/revealed = FALSE
///How long the revenant is revealed for, is about 2 seconds times this var.
var/unreveal_time = 0
///How long the revenant is stunned for, is about 2 seconds times this var.
var/unstun_time = 0
///If the revenant's abilities are blocked by a chaplain's power.
var/inhibited = FALSE
///How much essence the revenant has drained.
var/essence_drained = 0
///If the revenant is draining someone.
var/draining = FALSE
/// contains a list of UIDs of mobs who have been drained. cannot drain the same mob twice.
var/list/drained_mobs = list()
///How many perfect, regen-cap increasing souls the revenant has.
var/perfectsouls = 0
/mob/living/simple_animal/revenant/Life(seconds, times_fired)
@@ -232,25 +245,21 @@
..()
/mob/living/simple_animal/revenant/proc/castcheck(essence_cost)
if(!src)
return
if(holy_check(src))
return
var/turf/T = get_turf(src)
if(istype(T, /turf/simulated/wall))
to_chat(src, "<span class='revenwarning'>You cannot use abilities from inside of a wall.</span>")
return 0
if(src.inhibited)
if(inhibited)
to_chat(src, "<span class='revenwarning'>Your powers have been suppressed by nulling energy!</span>")
return 0
if(!src.change_essence_amount(essence_cost, 1))
if(!change_essence_amount(essence_cost, 1))
to_chat(src, "<span class='revenwarning'>You lack the essence to use that ability.</span>")
return 0
return 1
/mob/living/simple_animal/revenant/proc/change_essence_amount(essence_amt, silent = 0, source = null)
if(!src)
return
if(essence + essence_amt <= 0)
return
essence = max(0, essence+essence_amt)
@@ -264,8 +273,6 @@
return 1
/mob/living/simple_animal/revenant/proc/reveal(time)
if(!src)
return
if(time <= 0)
return
revealed = 1
@@ -280,8 +287,6 @@
update_spooky_icon()
/mob/living/simple_animal/revenant/proc/stun(time)
if(!src)
return
if(time <= 0)
return
notransform = 1
@@ -31,7 +31,8 @@
if(draining)
to_chat(src, "<span class='revenwarning'>You are already siphoning the essence of a soul!</span>")
return
if(target in drained_mobs)
var/mob_UID = target.UID()
if(mob_UID in drained_mobs)
to_chat(src, "<span class='revenwarning'>[target]'s soul is dead and empty.</span>")
return
if(!target.stat)
@@ -39,7 +40,7 @@
if(prob(10))
to_chat(target, "You feel as if you are being watched.")
return
draining = 1
draining = TRUE
essence_drained = rand(15, 20)
to_chat(src, "<span class='revennotice'>You search for the soul of [target].</span>")
if(do_after(src, 10, 0, target = target)) //did they get deleted in that second?
@@ -65,7 +66,7 @@
if(!target.stat)
to_chat(src, "<span class='revenwarning'>They are now powerful enough to fight off your draining.</span>")
to_chat(target, "<span class='boldannounce'>You feel something tugging across your body before subsiding.</span>")
draining = 0
draining = FALSE
return //hey, wait a minute...
to_chat(src, "<span class='revenminor'>You begin siphoning essence from [target]'s soul.</span>")
if(target.stat != DEAD)
@@ -84,7 +85,8 @@
to_chat(src, "<span class='revennotice'>[target]'s soul has been considerably weakened and will yield no more essence for the time being.</span>")
target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
"<span class='revenwarning'>Violets lights, dancing in your vision, getting clo--</span>")
drained_mobs.Add(target)
drained_mobs.Add(mob_UID)
add_attack_logs(src, target, "revenant harvested soul")
target.death(0)
else
to_chat(src, "<span class='revenwarning'>[target ? "[target] has":"They have"] been drawn out of your grasp. The link has been broken.</span>")
@@ -96,10 +98,10 @@
return
else
to_chat(src, "<span class='revenwarning'>You are not close enough to siphon [target ? "[target]'s":"their"] soul. The link has been broken.</span>")
draining = 0
draining = FALSE
essence_drained = 0
return
draining = 0
draining = FALSE
essence_drained = 0
return
@@ -142,7 +144,7 @@
name = "Report this to a coder"
var/reveal = 80 //How long it reveals the revenant in deciseconds
var/stun = 20 //How long it stuns the revenant in deciseconds
var/locked = 1 //If it's locked and needs to be unlocked before use
var/locked = TRUE //If it's locked and needs to be unlocked before use
var/unlock_amount = 100 //How much essence it costs to unlock
var/cast_amount = 50 //How much essence it costs to use
@@ -155,36 +157,36 @@
/obj/effect/proc_holder/spell/aoe_turf/revenant/can_cast(mob/living/simple_animal/revenant/user = usr, charge_check = TRUE, show_message = FALSE)
if(user.inhibited)
return 0
return FALSE
if(charge_counter < charge_max)
return 0
return FALSE
if(locked)
if(user.essence <= unlock_amount)
return 0
return FALSE
if(user.essence <= cast_amount)
return 0
return 1
return FALSE
return TRUE
/obj/effect/proc_holder/spell/aoe_turf/revenant/proc/attempt_cast(mob/living/simple_animal/revenant/user = usr)
if(locked)
if(!user.castcheck(-unlock_amount))
charge_counter = charge_max
return 0
return FALSE
name = "[initial(name)] ([cast_amount]E)"
to_chat(user, "<span class='revennotice'>You have unlocked [initial(name)]!</span>")
panel = "Revenant Abilities"
locked = 0
locked = FALSE
charge_counter = charge_max
return 0
return FALSE
if(!user.castcheck(-cast_amount))
charge_counter = charge_max
return 0
return FALSE
name = "[initial(name)] ([cast_amount]E)"
user.reveal(reveal)
user.stun(stun)
if(action)
action.UpdateButtonIcon()
return 1
return TRUE
//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload
@@ -201,25 +203,29 @@
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
spawn(0)
for(var/obj/machinery/light/L in T.contents)
spawn(0)
if(!L.on)
return
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</span>")
do_sparks(4, 0, L)
new/obj/effect/temp_visual/revenant(L.loc)
sleep(20)
if(!L.on) //wait, wait, don't shock me
return
flick("[L.base_state]2", L)
for(var/mob/living/M in view(shock_range, L))
if(M == user)
return
M.Beam(L,icon_state="purple_lightning",icon='icons/effects/effects.dmi',time=5)
M.electrocute_act(shock_damage, L, flags = SHOCK_NOGLOVES)
do_sparks(4, 0, M)
playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)
select_lights(T, user)
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/select_lights(turf/T, mob/living/simple_animal/revenant/user)
for(var/obj/machinery/light/L in T.contents)
INVOKE_ASYNC(src, .proc/shock_lights, L, user)
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/shock_lights(obj/machinery/light/L, mob/living/simple_animal/revenant/user)
if(!L.on)
return
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</span>")
do_sparks(4, 0, L)
new /obj/effect/temp_visual/revenant(L.loc)
sleep(2 SECONDS)
if(!L.on) //wait, wait, don't shock me
return
flick("[L.base_state]2", L)
for(var/mob/living/M in view(shock_range, L))
if(M == user)
continue
M.Beam(L, icon_state = "purple_lightning", icon = 'icons/effects/effects.dmi', time = 0.5 SECONDS)
M.electrocute_act(shock_damage, L, flags = SHOCK_NOGLOVES)
do_sparks(4, 0, M)
playsound(M, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
//Defile: Corrupts nearby stuff, unblesses floor tiles.
/obj/effect/proc_holder/spell/aoe_turf/revenant/defile
@@ -232,46 +238,14 @@
unlock_amount = 75
cast_amount = 30
action_icon_state = "defile"
var/stamdamage= 25
var/toxdamage = 5
var/confusion = 20
var/maxconfusion = 30
/obj/effect/proc_holder/spell/aoe_turf/revenant/defile/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
spawn(0)
if(T.flags & NOJAUNT)
T.flags -= NOJAUNT
new/obj/effect/temp_visual/revenant(T)
for(var/mob/living/carbon/human/human in T.contents)
to_chat(human, "<span class='warning'>You suddenly feel [pick("sick and tired", "tired and confused", "nauseated", "dizzy")].</span>")
human.adjustStaminaLoss(stamdamage)
human.adjustToxLoss(toxdamage)
human.AdjustConfused(confusion, bound_lower = 0, bound_upper = maxconfusion)
new/obj/effect/temp_visual/revenant(human.loc)
if(!istype(T, /turf/simulated/wall/indestructible) && !istype(T, /turf/simulated/wall/rust) && !istype(T, /turf/simulated/wall/r_wall) && istype(T, /turf/simulated/wall) && prob(15))
new/obj/effect/temp_visual/revenant(T)
T.ChangeTurf(/turf/simulated/wall/rust)
if(!istype(T, /turf/simulated/wall/r_wall/rust) && istype(T, /turf/simulated/wall/r_wall) && prob(15))
new/obj/effect/temp_visual/revenant(T)
T.ChangeTurf(/turf/simulated/wall/r_wall/rust)
for(var/obj/structure/window/window in T.contents)
window.take_damage(rand(30,80))
if(window && window.fulltile)
new/obj/effect/temp_visual/revenant/cracks(window.loc)
for(var/obj/structure/closet/closet in T.contents)
closet.open()
if(!istype(T, /turf/simulated/floor/plating) && !istype(T, /turf/simulated/floor/engine/cult) && istype(T, /turf/simulated/floor) && prob(15))
var/turf/simulated/floor/floor = T
if(floor.intact && floor.floor_tile)
new floor.floor_tile(floor)
floor.broken = 0
floor.burnt = 0
floor.make_plating(1)
for(var/obj/machinery/light/light in T.contents)
light.flicker(30) //spooky
if(!attempt_cast(user))
return
for(var/turf/T in targets)
T.defile()
for(var/atom/A in T.contents)
A.defile()
//Malfunction: Makes bad stuff happen to robots and machines.
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction
@@ -287,29 +261,107 @@
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
spawn(0)
for(var/mob/living/simple_animal/bot/bot in T.contents)
if(!bot.emagged)
new/obj/effect/temp_visual/revenant(bot.loc)
bot.locked = 0
bot.open = 1
bot.emag_act(null)
for(var/mob/living/carbon/human/human in T.contents)
to_chat(human, "<span class='warning'>You feel [pick("your sense of direction flicker out", "a stabbing pain in your head", "your mind fill with static")].</span>")
new/obj/effect/temp_visual/revenant(human.loc)
human.emp_act(1)
for(var/obj/thing in T.contents)
if(istype(thing, /obj/machinery/power/apc) || istype(thing, /obj/machinery/power/smes)) //Doesn't work on dominators, SMES and APCs, to prevent kekkery
continue
if(prob(20))
if(prob(50))
new/obj/effect/temp_visual/revenant(thing.loc)
thing.emag_act(null)
else
if(!istype(thing, /obj/machinery/clonepod)) //I hate everything but mostly the fact there's no better way to do this without just not affecting it at all
thing.emp_act(1)
for(var/mob/living/silicon/robot/S in T.contents) //Only works on cyborgs, not AI
playsound(S, 'sound/machines/warning-buzzer.ogg', 50, 1)
new/obj/effect/temp_visual/revenant(S.loc)
S.spark_system.start()
S.emp_act(1)
INVOKE_ASYNC(src, .proc/effect, user, T)
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction/proc/effect(mob/living/simple_animal/revenant/user, turf/T)
T.rev_malfunction()
for(var/atom/A in T.contents)
A.rev_malfunction()
/atom/proc/defile()
return
/atom/proc/rev_malfunction()
return
/mob/living/carbon/human/rev_malfunction()
to_chat(src, "<span class='warning'>You feel [pick("your sense of direction flicker out", "a stabbing pain in your head", "your mind fill with static")].</span>")
new /obj/effect/temp_visual/revenant(loc)
emp_act(1)
/mob/living/simple_animal/bot/rev_malfunction()
if(!emagged)
new /obj/effect/temp_visual/revenant(loc)
locked = FALSE
open = TRUE
emag_act(null)
/obj/rev_malfunction()
if(prob(20))
if(prob(50))
new /obj/effect/temp_visual/revenant(loc)
emag_act(null)
/obj/machinery/clonepod/rev_malfunction()
emag_act(null)
/obj/machinery/power/apc/rev_malfunction()
return
/obj/machinery/power/smes/rev_malfunction()
return
/mob/living/silicon/robot/rev_malfunction()
playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 1)
new /obj/effect/temp_visual/revenant(loc)
spark_system.start()
emp_act(1)
/turf/defile()
if(flags & NOJAUNT)
flags &= ~NOJAUNT
new /obj/effect/temp_visual/revenant(loc)
/turf/simulated/wall/defile()
..()
if(prob(15))
new/obj/effect/temp_visual/revenant(loc)
ChangeTurf(/turf/simulated/wall/rust)
/turf/simulated/wall/indestructible/defile()
return
/turf/simulated/wall/r_wall/defile()
..()
if(prob(15))
new/obj/effect/temp_visual/revenant(loc)
ChangeTurf(/turf/simulated/wall/r_wall/rust)
/mob/living/carbon/human/defile()
to_chat(src, "<span class='warning'>You suddenly feel [pick("sick and tired", "tired and confused", "nauseated", "dizzy")].</span>")
adjustStaminaLoss(25)
adjustToxLoss(5)
AdjustConfused(20, bound_lower = 0, bound_upper = 30)
new /obj/effect/temp_visual/revenant(loc)
/obj/structure/window/defile()
take_damage(rand(30,80))
if(fulltile)
new /obj/effect/temp_visual/revenant/cracks(loc)
/obj/structure/closet/defile()
open()
/turf/simulated/floor/defile()
..()
if(prob(15))
if(intact && floor_tile)
new floor_tile(src)
broken = 0
burnt = 0
make_plating(1)
/turf/simulated/floor/plating/defile()
if(flags & NOJAUNT)
flags &= ~NOJAUNT
new /obj/effect/temp_visual/revenant(loc)
/turf/simulated/floor/engine/cult/defile()
if(flags & NOJAUNT)
flags &= ~NOJAUNT
new /obj/effect/temp_visual/revenant(loc)
/obj/machinery/light/defile()
flicker(30)
@@ -115,6 +115,12 @@
speed = 0
boost = world.time + 60
// Midround slaughter demon, less tanky
/mob/living/simple_animal/slaughter/lesser
maxHealth = 130
health = 130
// Cult slaughter demon
/mob/living/simple_animal/slaughter/cult //Summoned as part of the cult objective "Bring the Slaughter"
name = "harbinger of the slaughter"
+9
View File
@@ -205,6 +205,15 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
/datum/objective/protect/mindslave //subytpe for mindslave implants
/datum/objective/protect/mindslave/on_target_cryo()
if(owner?.current)
to_chat(owner.current, "<BR><span class='userdanger'>You notice that your master has entered cryogenic storage, and revert to your normal self, until they return again. You are no longer a mindslave!</span>")
SEND_SOUND(owner.current, sound('sound/ambience/alarm4.ogg'))
owner.remove_antag_datum(/datum/antagonist/mindslave)
SSticker.mode.implanted.Remove(owner)
log_admin("[key_name(owner.current)]'s mindslave master has cryo'd, and is no longer a mindslave.")
message_admins("[key_name_admin(owner.current)]'s mindslave master has cryo'd, and is no longer a mindslave.") //Since they were on antag hud earlier, this feels important to log
qdel(src)
/datum/objective/hijack
martyr_compatible = 0 //Technically you won't get both anyway.
+4
View File
@@ -33,6 +33,10 @@
var/list/possible_traitors = get_players_for_role(ROLE_TRAITOR)
for(var/datum/mind/candidate in possible_traitors)
if(candidate.special_role == SPECIAL_ROLE_VAMPIRE) // no traitor vampires
possible_traitors.Remove(candidate)
// stop setup if no possible traitors
if(!possible_traitors.len)
return 0
+1 -1
View File
@@ -13,7 +13,7 @@
/datum/game_mode/traitor/vampire/announce()
to_chat(world, "<B>The current game mode is - Traitor+Vampire!</B>")
to_chat(world, "<B>There is a Vampire from Space Transylvania on the station along with some syndicate operatives out for their own gain! Do not let the vampire and the traitors succeed!</B>")
to_chat(world, "<B>There are Bluespace Vampires infesting your fellow crew on the station along with some syndicate operatives out for their own gain! Do not let the vampires and the traitors succeed!</B>")
/datum/game_mode/traitor/vampire/pre_setup()
+137 -97
View File
@@ -1,9 +1,7 @@
//This is the gamemode file for the ported goon gamemode vampires.
//They get a traitor objective and a blood sucking objective
/datum/game_mode
var/list/datum/mind/vampires = list()
var/list/datum/mind/vampire_enthralled = list() //those controlled by a vampire
var/list/vampire_thralls = list() //vammpires controlling somebody
var/list/vampire_thralls = list() //vampires controlling somebody
/datum/game_mode/vampire
name = "vampire"
@@ -36,7 +34,7 @@
/datum/game_mode/vampire/announce()
to_chat(world, "<B>The current game mode is - Vampires!</B>")
to_chat(world, "<B>There are Vampires from Space Transylvania on the station, keep your blood close and neck safe!</B>")
to_chat(world, "<B>There are Bluespace Vampires infesting your fellow crewmates, keep your blood close and neck safe!</B>")
/datum/game_mode/vampire/pre_setup()
@@ -84,8 +82,8 @@
text += "died"
else
text += "survived"
if(vampire.current.real_name != vampire.name)
text += " as [vampire.current.real_name]"
if(vampire.vampire.subclass)
text += " as a [vampire.vampire.subclass.name]"
else
text += "body destroyed"
text += ")"
@@ -120,15 +118,15 @@
/datum/game_mode/proc/auto_declare_completion_enthralled()
if(vampire_enthralled.len)
var/text = "<FONT size = 2><B>The Enthralled were:</B></FONT>"
for(var/datum/mind/Mind in vampire_enthralled)
text += "<br>[Mind.key] was [Mind.name] ("
if(Mind.current)
if(Mind.current.stat == DEAD)
for(var/datum/mind/mind in vampire_enthralled)
text += "<br>[mind.key] was [mind.name] ("
if(mind.current)
if(mind.current.stat == DEAD)
text += "died"
else
text += "survived"
if(Mind.current.real_name != Mind.name)
text += " as [Mind.current.real_name]"
if(mind.current.real_name != mind.name)
text += " as [mind.current.real_name]"
else
text += "body destroyed"
text += ")"
@@ -168,9 +166,18 @@
return
/datum/game_mode/proc/grant_vampire_powers(mob/living/carbon/vampire_mob)
if(!istype(vampire_mob))
if(!istype(vampire_mob) || !vampire_mob.mind)
return
vampire_mob.make_vampire()
var/datum/vampire/vamp
if(!vampire_mob.mind.vampire)
vamp = new /datum/vampire()
vamp.owner = vampire_mob
vampire_mob.mind.vampire = vamp
else
vamp = vampire_mob.mind.vampire
QDEL_LIST(vamp.powers)
vamp.check_vampire_upgrade(FALSE)
/datum/game_mode/proc/greet_vampire(datum/mind/vampire, you_are=1)
var/dat
@@ -178,7 +185,7 @@
SEND_SOUND(vampire.current, sound('sound/ambience/antag/vampalert.ogg'))
dat = "<span class='danger'>You are a Vampire!</span><br>"
dat += {"To bite someone, target the head and use harm intent with an empty hand. Drink blood to gain new powers.
You are weak to holy things and starlight. Don't go into space and avoid the Chaplain, the chapel and especially Holy Water."}
You are weak to holy things, starlight and fire. Don't go into space and avoid the Chaplain, the chapel and especially Holy Water."}
to_chat(vampire.current, dat)
to_chat(vampire.current, "<B>You must complete the following tasks:</B>")
@@ -195,42 +202,51 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
obj_count++
to_chat(vampire.current, "<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Vampire)</span>")
return
/datum/vampire
var/bloodtotal = 0 // CHANGE TO ZERO WHEN PLAYTESTING HAPPENS
var/bloodusable = 0 // CHANGE TO ZERO WHEN PLAYTESTING HAPPENS
var/bloodtotal = 0
var/bloodusable = 0
/// the mob tied to the vampire
var/mob/living/owner = null
var/gender = FEMALE
var/iscloaking = 0 // handles the vampire cloak toggle
var/list/powers = list() // list of available powers and passives
var/mob/living/carbon/human/draining // who the vampire is draining of blood
var/nullified = 0 //Nullrod makes them useless for a short while.
var/list/upgrade_tiers = list(
/obj/effect/proc_holder/spell/vampire/self/rejuvenate = 0,
/obj/effect/proc_holder/spell/vampire/targetted/hypnotise = 0,
/obj/effect/proc_holder/spell/vampire/mob_aoe/glare = 0,
/datum/vampire_passive/vision = 100,
/obj/effect/proc_holder/spell/vampire/self/shapeshift = 100,
/obj/effect/proc_holder/spell/vampire/self/cloak = 150,
/obj/effect/proc_holder/spell/vampire/targetted/disease = 150,
/obj/effect/proc_holder/spell/vampire/bats = 200,
/obj/effect/proc_holder/spell/vampire/self/screech = 200,
/datum/vampire_passive/regen = 200,
/obj/effect/proc_holder/spell/vampire/shadowstep = 250,
/obj/effect/proc_holder/spell/vampire/self/jaunt = 300,
/obj/effect/proc_holder/spell/vampire/targetted/enthrall = 300,
/datum/vampire_passive/full = 500)
/// what vampire subclass the vampire is.
var/datum/vampire_subclass/subclass
/// handles the vampire cloak toggle
var/iscloaking = FALSE
/// list of available powers and passives
var/list/powers = list()
/// who the vampire is draining of blood
var/mob/living/carbon/human/draining
/// Nullrods and holywater make their abilities cost more
var/nullified = 0
/// a list of powers that all vampires unlock and at what blood level they unlock them, the rest of their powers are found in the vampire_subclass datum
var/list/upgrade_tiers = list(/obj/effect/proc_holder/spell/self/vampire/rejuvenate = 0,
/obj/effect/proc_holder/spell/mob_aoe/glare = 0,
/datum/vampire_passive/vision = 100,
/obj/effect/proc_holder/spell/self/vampire/specialize = 150,
/datum/vampire_passive/regen = 200,
/obj/effect/proc_holder/spell/targeted/turf_teleport/shadow_step = 250)
/// list of the peoples UIDs that we have drained, and how much blood from each one
var/list/drained_humans = list()
/datum/vampire/Destroy(force, ...)
owner = null
draining = null
QDEL_NULL(subclass)
QDEL_LIST(powers)
return ..()
/datum/vampire/proc/adjust_nullification(base, extra)
// First hit should give full nullification, while subsequent hits increase the value slower
nullified = max(nullified + extra, base)
/datum/vampire/New(gend = FEMALE)
gender = gend
nullified = clamp(nullified + extra, base, VAMPIRE_NULLIFICATION_CAP)
/datum/vampire/proc/force_add_ability(path)
var/spell = new path(owner)
if(istype(spell, /obj/effect/proc_holder/spell))
owner.mind.AddSpell(spell)
if(istype(spell, /datum/vampire_passive))
var/datum/vampire_passive/passive = spell
passive.owner = owner
powers += spell
owner.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp gets new vision based on his powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
@@ -256,20 +272,6 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
if(current.mind && current.mind.vampire && current.mind.vampire.owner && (current.mind.vampire.owner != current))
current.mind.vampire.owner = current
/mob/proc/make_vampire()
if(!mind)
return
var/datum/vampire/vamp
if(!mind.vampire)
vamp = new /datum/vampire(gender)
vamp.owner = src
mind.vampire = vamp
else
vamp = mind.vampire
vamp.powers.Cut()
vamp.check_vampire_upgrade(0)
/datum/vampire/proc/remove_vampire_powers()
for(var/P in powers)
remove_ability(P)
@@ -278,12 +280,14 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
if(hud.vampire_blood_display)
hud.remove_vampire_hud()
owner.alpha = 255
REMOVE_TRAITS_IN(owner, "vampire")
/datum/vampire/proc/handle_bloodsucking(mob/living/carbon/human/H)
#define BLOOD_GAINED_MODIFIER 0.5
/datum/vampire/proc/handle_bloodsucking(mob/living/carbon/human/H, suck_rate = 5 SECONDS)
draining = H
var/unique_suck_id = H.UID()
var/blood = 0
var/old_bloodtotal = 0 //used to see if we increased our blood total
var/old_bloodusable = 0 //used to see if we increased our blood usable
var/blood_volume_warning = 9999 //Blood volume threshold for warnings
if(owner.is_muzzled())
to_chat(owner, "<span class='warning'>[owner.wear_mask] prevents you from biting [H]!</span>")
@@ -295,25 +299,24 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
H.LAssailant = null
else
H.LAssailant = owner
while(do_mob(owner, H, 50))
while(do_mob(owner, H, suck_rate))
if(!(owner.mind in SSticker.mode.vampires))
to_chat(owner, "<span class='userdanger'>Your fangs have disappeared!</span>")
return
old_bloodtotal = bloodtotal
old_bloodusable = bloodusable
owner.do_attack_animation(H, ATTACK_EFFECT_BITE)
if(unique_suck_id in drained_humans)
if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT)
to_chat(owner, "<span class='warning'>You have drained most of the life force from [H]'s blood, and you will get no more useable blood from them!</span>")
H.blood_volume = max(H.blood_volume - 25, 0)
owner.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.nutrition + 5))
continue
if(H.stat < DEAD)
if(H.ckey || H.player_ghosted) //Requires ckey regardless if monkey or humanoid, or the body has been ghosted before it died
blood = min(20, H.blood_volume) // if they have less than 20 blood, give them the remnant else they get 20 blood
bloodtotal += blood / 2 //divide by 2 to counted the double suction since removing cloneloss -Melandor0
bloodusable += blood / 2
else
if(H.ckey || H.player_ghosted)
blood = min(5, H.blood_volume) // The dead only give 5 blood
bloodtotal += blood
if(old_bloodtotal != bloodtotal)
if(H.ckey || H.player_ghosted) // Requires ckey regardless if monkey or human, and has not ghosted, otherwise no power
to_chat(owner, "<span class='notice'><b>You have accumulated [bloodtotal] [bloodtotal > 1 ? "units" : "unit"] of blood[bloodusable != old_bloodusable ? ", and have [bloodusable] left to use" : ""].</b></span>")
check_vampire_upgrade()
blood = min(20, H.blood_volume)
adjust_blood(H, blood * BLOOD_GAINED_MODIFIER)
to_chat(owner, "<span class='notice'><b>You have accumulated [bloodtotal] unit\s of blood, and have [bloodusable] left to use.</b></span>")
H.blood_volume = max(H.blood_volume - 25, 0)
//Blood level warnings (Code 'borrowed' from Fulp)
if(H.blood_volume)
@@ -325,20 +328,18 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
else
to_chat(owner, "<span class='warning'>You have bled your victim dry!</span>")
break
if(ishuman(owner))
var/mob/living/carbon/human/V = owner
if(!H.ckey && !H.player_ghosted)//Only runs if there is no ckey and the body has not being ghosted while alive
to_chat(V, "<span class='notice'><b>Feeding on [H] reduces your thirst, but you get no usable blood from them.</b></span>")
V.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, V.nutrition + 5))
else
V.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, V.nutrition + (blood / 2)))
if(!H.ckey && !H.player_ghosted)//Only runs if there is no ckey and the body has not being ghosted while alive
to_chat(owner, "<span class='notice'><b>Feeding on [H] reduces your thirst, but you get no usable blood from them.</b></span>")
owner.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.nutrition + 5))
else
owner.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.nutrition + (blood / 2)))
draining = null
to_chat(owner, "<span class='notice'>You stop draining [H.name] of blood.</span>")
/datum/vampire/proc/check_vampire_upgrade(announce = 1)
#undef BLOOD_GAINED_MODIFIER
/datum/vampire/proc/check_vampire_upgrade(announce = TRUE)
var/list/old_powers = powers.Copy()
for(var/ptype in upgrade_tiers)
@@ -346,18 +347,30 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
if(bloodtotal >= level)
add_ability(ptype)
if(!subclass)
return
subclass.add_subclass_ability(src)
check_full_power_upgrade()
if(announce)
announce_new_power(old_powers)
/datum/vampire/proc/check_full_power_upgrade()
if(length(drained_humans) >= FULLPOWER_DRAINED_REQUIREMENT && bloodtotal >= FULLPOWER_BLOODTOTAL_REQUIREMENT)
subclass.add_full_power_abilities(src)
/datum/vampire/proc/announce_new_power(list/old_powers)
for(var/p in powers)
if(!(p in old_powers))
if(istype(p, /obj/effect/proc_holder/spell/vampire))
var/obj/effect/proc_holder/spell/vampire/power = p
to_chat(owner, "<span class='notice'>[power.gain_desc]</span>")
if(istype(p, /obj/effect/proc_holder/spell))
var/obj/effect/proc_holder/spell/power = p
to_chat(owner, "<span class='boldnotice'>[power.gain_desc]</span>")
else if(istype(p, /datum/vampire_passive))
var/datum/vampire_passive/power = p
to_chat(owner, "<span class='notice'>[power.gain_desc]</span>")
to_chat(owner, "<span class='boldnotice'>[power.gain_desc]</span>")
/datum/game_mode/proc/remove_vampire(datum/mind/vampire_mind)
if(vampire_mind in vampires)
@@ -414,6 +427,9 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
var/turf/T = locate(round(ax, 0.5), round(ay, 0.5), owner.z)
if(!T)
return
if(T.x == 1 || T.x == world.maxx || T.y == 1 || T.y == world.maxy)
break
@@ -427,6 +443,8 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
to_chat(owner, "<span class='userdanger'>Your body is turning to ash, get out of the light now!</span>")
owner.adjustCloneLoss(10) //I'm melting!
vamp_burn(85)
if(owner.cloneloss >= 100)
owner.dust()
/datum/vampire/proc/handle_vampire()
if(owner.hud_used)
@@ -440,31 +458,49 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
hud.show_hud(hud.hud_version)
hud.vampire_blood_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font face='Small Fonts' color='#ce0202'>[bloodusable]</font></div>"
handle_vampire_cloak()
if(istype(owner.loc, /turf/space))
if(istype(get_turf(owner), /turf/space))
check_sun()
if(istype(owner.loc.loc, /area/chapel) && !get_ability(/datum/vampire_passive/full))
if(istype(get_area(owner), /area/chapel) && !get_ability(/datum/vampire_passive/full))
vamp_burn(7)
nullified = max(0, nullified - 1)
nullified = max(0, nullified - 2)
/datum/vampire/proc/handle_vampire_cloak()
if(!ishuman(owner))
owner.alpha = 255
return
var/turf/simulated/T = get_turf(owner)
var/light_available = T.get_lumcount(0.5) * 10
var/light_available = T.get_lumcount() * 10
if(!istype(T))
return 0
return
if(!iscloaking)
if(!iscloaking || owner.on_fire)
owner.alpha = 255
return 0
REMOVE_TRAIT(owner, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
return
if(light_available <= 2)
owner.alpha = round((255 * 0.15))
return 1
else
owner.alpha = round((255 * 0.80))
owner.alpha = 38 // round(255 * 0.15)
ADD_TRAIT(owner, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
return
REMOVE_TRAIT(owner, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
owner.alpha = 204 // 255 * 0.80
/datum/vampire/proc/adjust_blood(mob/living/carbon/C, blood_amount = 0)
if(C)
var/unique_suck_id = C.UID()
if(!(unique_suck_id in drained_humans))
drained_humans[unique_suck_id] = 0
if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT)
return
drained_humans[unique_suck_id] += blood_amount
bloodtotal += blood_amount
bloodusable += blood_amount
check_vampire_upgrade(TRUE)
for(var/obj/effect/proc_holder/spell/S in powers)
if(S.action)
S.action.UpdateButtonIcon()
/datum/vampire/proc/vamp_burn(burn_chance)
if(prob(burn_chance) && owner.health >= 50)
@@ -491,3 +527,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
static_inventory -= vampire_blood_display
QDEL_NULL(vampire_blood_display)
show_hud(hud_version)
/datum/vampire/vv_edit_var(var_name, var_value)
. = ..()
check_vampire_upgrade(TRUE)
@@ -1,611 +0,0 @@
//This should hold all the vampire related powers
/obj/effect/proc_holder/spell/vampire
panel = "Vampire"
school = "vampire"
clothes_req = 0
range = 1
charge_max = 1800
action_background_icon_state = "bg_vampire"
holy_area_cancast = FALSE //Stops cult magic from working on holy ground eg: chapel
var/required_blood = 0
var/gain_desc = null
var/deduct_blood_on_cast = TRUE //Do we want to take the blood when this is cast, or at a later point?
/obj/effect/proc_holder/spell/vampire/New()
..()
if(!gain_desc)
gain_desc = "You have gained \the [src] ability."
/obj/effect/proc_holder/spell/vampire/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/user = usr)
if(!user.mind)
return 0
if(!ishuman(user))
to_chat(user, "<span class='warning'>You are in too weak of a form to do this!</span>")
return 0
var/datum/vampire/vampire = user.mind.vampire
if(!vampire)
return 0
var/fullpower = vampire.get_ability(/datum/vampire_passive/full)
if(user.stat >= DEAD)
to_chat(user, "<span class='warning'>Not when you're dead!</span>")
return 0
if(vampire.nullified && !fullpower)
to_chat(user, "<span class='warning'>Something is blocking your powers!</span>")
return 0
if(vampire.bloodusable < required_blood)
to_chat(user, "<span class='warning'>You require at least [required_blood] units of usable blood to do that!</span>")
return 0
return ..()
/obj/effect/proc_holder/spell/vampire/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE)
if(!user.mind)
return 0
if(!ishuman(user))
return 0
var/datum/vampire/vampire = user.mind.vampire
if(!vampire)
return 0
var/fullpower = vampire.get_ability(/datum/vampire_passive/full)
if(user.stat >= DEAD)
return 0
if(vampire.nullified && !fullpower)
return 0
if(vampire.bloodusable < required_blood)
return 0
if(istype(loc.loc, /area/chapel) && !fullpower)
return 0
return ..()
/obj/effect/proc_holder/spell/vampire/proc/affects(mob/target, mob/user = usr)
//Other vampires aren't affected
if(target.mind && target.mind.vampire)
return 0
//Vampires who have reached their full potential can affect nearly everything
if(user.mind.vampire.get_ability(/datum/vampire_passive/full))
return 1
//Holy characters are resistant to vampire powers
if(target.mind && target.mind.isholy)
return 0
return 1
/obj/effect/proc_holder/spell/vampire/proc/can_reach(mob/M as mob)
if(M.loc == usr.loc)
return 1 //target and source are in the same thing
return M in oview_or_orange(range, usr, selection_type)
/obj/effect/proc_holder/spell/vampire/before_cast(list/targets)
// sanity check before we cast
if(!usr.mind || !usr.mind.vampire)
targets.Cut()
return
if(!required_blood)
return
// enforce blood
var/datum/vampire/vampire = usr.mind.vampire
if(required_blood <= vampire.bloodusable)
if(!deduct_blood_on_cast) //don't take the blood yet if this is false!
return
vampire.bloodusable -= required_blood
else
// stop!!
targets.Cut()
if(targets.len)
to_chat(usr, "<span class='notice'><b>You have [vampire.bloodusable] left to use.</b></span>")
/obj/effect/proc_holder/spell/vampire/targetted/choose_targets(mob/user = usr)
var/list/possible_targets[0]
for(var/mob/living/carbon/C in oview_or_orange(range, user, selection_type))
possible_targets += C
var/mob/living/carbon/T = input(user, "Choose your victim.", name) as null|mob in possible_targets
if(!T || !can_reach(T))
revert_cast(user)
return
perform(list(T), user = user)
/obj/effect/proc_holder/spell/vampire/self/choose_targets(mob/user = usr)
perform(list(user))
/obj/effect/proc_holder/spell/vampire/mob_aoe/choose_targets(mob/user = usr)
var/list/targets[0]
for(var/mob/living/carbon/C in oview_or_orange(range, user, selection_type))
targets += C
if(!targets.len)
revert_cast(user)
return
perform(targets, user = user)
/datum/vampire_passive
var/gain_desc
/datum/vampire_passive/New()
..()
if(!gain_desc)
gain_desc = "You have gained \the [src] ability."
////////////////////////////////////////////////////////////////////////////////////////////////////////
/obj/effect/proc_holder/spell/vampire/self/rejuvenate
name = "Rejuvenate"
desc= "Use reserve blood to enliven your body, removing any incapacitating effects."
action_icon_state = "vampire_rejuvinate"
charge_max = 200
stat_allowed = 1
/obj/effect/proc_holder/spell/vampire/self/rejuvenate/cast(list/targets, mob/user = usr)
var/mob/living/U = user
user.SetWeakened(0)
user.SetStunned(0)
user.SetParalysis(0)
user.SetSleeping(0)
U.adjustStaminaLoss(-75)
to_chat(user, "<span class='notice'>You instill your body with clean blood and remove any incapacitating effects.</span>")
spawn(1)
if(usr.mind.vampire.get_ability(/datum/vampire_passive/regen))
for(var/i = 1 to 5)
U.adjustBruteLoss(-2)
U.adjustOxyLoss(-5)
U.adjustToxLoss(-2)
U.adjustFireLoss(-2)
sleep(35)
/obj/effect/proc_holder/spell/vampire/targetted/hypnotise
name = "Hypnotise (20)"
desc= "A piercing stare that incapacitates your victim for a good length of time."
action_icon_state = "vampire_hypnotise"
required_blood = 20
/obj/effect/proc_holder/spell/vampire/targetted/hypnotise/cast(list/targets, mob/user = usr)
for(var/mob/living/target in targets)
user.visible_message("<span class='warning'>[user]'s eyes flash briefly as [user.p_they()] stare[user.p_s()] into [target]'s eyes</span>")
if(do_mob(user, target, 50))
if(!affects(target))
to_chat(user, "<span class='warning'>Your piercing gaze fails to knock out [target].</span>")
to_chat(target, "<span class='notice'>[user]'s feeble gaze is ineffective.</span>")
else
to_chat(user, "<span class='warning'>Your piercing gaze knocks out [target].</span>")
to_chat(target, "<span class='warning'>You find yourself unable to move and barely able to speak.</span>")
target.Weaken(10)
target.Stun(10)
target.stuttering = 10
else
revert_cast(usr)
to_chat(usr, "<span class='warning'>You broke your gaze.</span>")
/obj/effect/proc_holder/spell/vampire/targetted/disease
name = "Diseased Touch (100)"
desc = "Touches your victim with infected blood giving them Grave Fever, which will, left untreated, causes toxic building and frequent collapsing."
gain_desc = "You have gained the Diseased Touch ability which causes those you touch to become weak unless treated medically."
action_icon_state = "vampire_disease"
required_blood = 100
/obj/effect/proc_holder/spell/vampire/targetted/disease/cast(list/targets, mob/user = usr)
for(var/mob/living/carbon/target in targets)
to_chat(user, "<span class='warning'>You stealthily infect [target] with your diseased touch.</span>")
target.help_shake_act(user)
if(!affects(target))
to_chat(user, "<span class='warning'>They seem to be unaffected.</span>")
continue
var/datum/disease/D = new /datum/disease/vampire
target.ForceContractDisease(D)
/obj/effect/proc_holder/spell/vampire/mob_aoe/glare
name = "Glare"
desc = "A scary glare that incapacitates people for a short while around you."
action_icon_state = "vampire_glare"
charge_max = 300
stat_allowed = 1
/obj/effect/proc_holder/spell/vampire/mob_aoe/glare/cast(list/targets, mob/user = usr)
user.visible_message("<span class='warning'>[user]'s eyes emit a blinding flash!</span>")
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(istype(H.glasses, /obj/item/clothing/glasses/sunglasses/blindfold))
var/obj/item/clothing/glasses/sunglasses/blindfold/B = H.glasses
if(B.tint)
to_chat(user, "<span class='warning'>You're blindfolded!</span>")
return
for(var/mob/living/target in targets)
if(!affects(target))
continue
target.Stun(5)
target.Weaken(5)
target.stuttering = 20
to_chat(target, "<span class='warning'>You are blinded by [user]'s glare.</span>")
add_attack_logs(user, target, "(Vampire) Glared at")
/obj/effect/proc_holder/spell/vampire/self/shapeshift
name = "Shapeshift (50)"
desc = "Changes your name and appearance at the cost of 50 blood and has a cooldown of 3 minutes."
gain_desc = "You have gained the shapeshifting ability, at the cost of stored blood you can change your form permanently."
action_icon_state = "genetic_poly"
required_blood = 50
/obj/effect/proc_holder/spell/vampire/self/shapeshift/cast(list/targets, mob/user = usr)
user.visible_message("<span class='warning'>[user] transforms!</span>")
if(ishuman(user))
var/mob/living/carbon/human/H = user
scramble(1, H, 100)
H.real_name = random_name(H.gender, H.dna.species.name) //Give them a name that makes sense for their species.
H.sync_organ_dna(assimilate = 1)
H.update_body()
H.reset_hair() //No more winding up with hairstyles you're not supposed to have, and blowing your cover.
H.reset_markings() //...Or markings.
H.dna.ResetUIFrom(H)
H.flavor_text = ""
user.update_icons()
/obj/effect/proc_holder/spell/vampire/self/screech
name = "Chiropteran Screech (30)"
desc = "An extremely loud shriek that stuns nearby humans and breaks windows as well."
gain_desc = "You have gained the Chiropteran Screech ability which stuns anything with ears in a large radius and shatters glass in the process."
action_icon_state = "vampire_screech"
required_blood = 30
/obj/effect/proc_holder/spell/vampire/self/screech/cast(list/targets, mob/user = usr)
user.visible_message("<span class='warning'>[user] lets out an ear piercing shriek!</span>", "<span class='warning'>You let out a loud shriek.</span>", "<span class='warning'>You hear a loud painful shriek!</span>")
for(var/mob/living/carbon/C in hearers(4))
if(C == user)
continue
if(ishuman(C))
var/mob/living/carbon/human/H = C
if(H.check_ear_prot() >= HEARING_PROTECTION_TOTAL)
continue
if(!affects(C))
continue
to_chat(C, "<span class='warning'><font size='3'><b>You hear a ear piercing shriek and your senses dull!</font></b></span>")
C.Weaken(4)
C.AdjustEarDamage(0, 20)
C.Stuttering(20)
C.Stun(4)
C.Jitter(150)
for(var/obj/structure/window/W in view(4))
W.deconstruct(FALSE)
playsound(user.loc, 'sound/effects/creepyshriek.ogg', 100, 1)
/proc/isvampirethrall(mob/living/M as mob)
return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.vampire_enthralled)
/obj/effect/proc_holder/spell/vampire/targetted/enthrall
name = "Enthrall (300)"
desc = "You use a large portion of your power to sway those loyal to none to be loyal to you only."
gain_desc = "You have gained the Enthrall ability which at a heavy blood cost allows you to enslave a human that is not loyal to any other for a random period of time."
action_icon_state = "vampire_enthrall"
required_blood = 300
deduct_blood_on_cast = FALSE
/obj/effect/proc_holder/spell/vampire/targetted/enthrall/cast(list/targets, mob/user = usr)
var/datum/vampire/vampire = user.mind.vampire
for(var/mob/living/target in targets)
user.visible_message("<span class='warning'>[user] bites [target]'s neck!</span>", "<span class='warning'>You bite [target]'s neck and begin the flow of power.</span>")
to_chat(target, "<span class='warning'>You feel the tendrils of evil invade your mind.</span>")
if(!ishuman(target))
to_chat(user, "<span class='warning'>You can only enthrall humans.</span>")
break
if(do_mob(user, target, 50))
if(can_enthrall(user, target))
handle_enthrall(user, target)
vampire.bloodusable -= required_blood //we take the blood after enthralling, not before
else
revert_cast(user)
to_chat(user, "<span class='warning'>You or your target either moved or you dont have enough usable blood.</span>")
/obj/effect/proc_holder/spell/vampire/targetted/enthrall/proc/can_enthrall(mob/living/user, mob/living/carbon/C)
var/enthrall_safe = 0
for(var/obj/item/implant/mindshield/L in C)
if(L && L.implanted)
enthrall_safe = 1
break
for(var/obj/item/implant/traitor/T in C)
if(T && T.implanted)
enthrall_safe = 1
break
if(!C)
log_runtime(EXCEPTION("something bad happened on enthralling a mob, attacker is [user] [user.key] \ref[user]"), user)
return 0
if(!C.mind)
to_chat(user, "<span class='warning'>[C.name]'s mind is not there for you to enthrall.</span>")
return 0
if(enthrall_safe || ( C.mind in SSticker.mode.vampires )||( C.mind.vampire )||( C.mind in SSticker.mode.vampire_enthralled ))
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>You feel a familiar sensation in your skull that quickly dissipates.</span>")
return 0
if(!affects(C))
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>Your faith of [SSticker.Bible_deity_name] has kept your mind clear of all evil.</span>")
return 0
if(!ishuman(C))
to_chat(user, "<span class='warning'>You can only enthrall humans!</span>")
return 0
return 1
/obj/effect/proc_holder/spell/vampire/targetted/enthrall/proc/handle_enthrall(mob/living/user, mob/living/carbon/human/H as mob)
if(!istype(H))
return 0
var/ref = "\ref[user.mind]"
if(!(ref in SSticker.mode.vampire_thralls))
SSticker.mode.vampire_thralls[ref] = list(H.mind)
else
SSticker.mode.vampire_thralls[ref] += H.mind
SSticker.mode.update_vampire_icons_added(H.mind)
SSticker.mode.update_vampire_icons_added(user.mind)
var/datum/mindslaves/slaved = user.mind.som
H.mind.som = slaved
slaved.serv += H
slaved.add_serv_hud(user.mind, "vampire")//handles master servent icons
slaved.add_serv_hud(H.mind, "vampthrall")
SSticker.mode.vampire_enthralled.Add(H.mind)
SSticker.mode.vampire_enthralled[H.mind] = user.mind
H.mind.special_role = SPECIAL_ROLE_VAMPIRE_THRALL
var/datum/objective/protect/serve_objective = new
serve_objective.owner = user.mind
serve_objective.target = H.mind
serve_objective.explanation_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command."
H.mind.objectives += serve_objective
to_chat(H, "<span class='biggerdanger'>You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command.</span>")
to_chat(user, "<span class='warning'>You have successfully Enthralled [H]. <i>If [H.p_they()] refuse[H.p_s()] to do as you say just adminhelp.</i></span>")
H.Stun(2)
add_attack_logs(user, H, "Vampire-thralled")
/obj/effect/proc_holder/spell/vampire/self/cloak
name = "Cloak of Darkness"
desc = "Toggles whether you are currently cloaking yourself in darkness."
gain_desc = "You have gained the Cloak of Darkness ability which when toggled makes you near invisible in the shroud of darkness."
action_icon_state = "vampire_cloak"
charge_max = 10
/obj/effect/proc_holder/spell/vampire/self/cloak/New()
..()
update_name()
/obj/effect/proc_holder/spell/vampire/self/cloak/proc/update_name()
var/mob/living/user = loc
if(!ishuman(user) || !user.mind || !user.mind.vampire)
return
name = "[initial(name)] ([user.mind.vampire.iscloaking ? "Deactivate" : "Activate"])"
/obj/effect/proc_holder/spell/vampire/self/cloak/cast(list/targets, mob/user = usr)
var/datum/vampire/V = user.mind.vampire
V.iscloaking = !V.iscloaking
update_name()
to_chat(user, "<span class='notice'>You will now be [V.iscloaking ? "hidden" : "seen"] in darkness.</span>")
/obj/effect/proc_holder/spell/vampire/bats
name = "Summon Bats (75)"
desc = "You summon a pair of space bats who attack nearby targets until they or their target is dead."
gain_desc = "You have gained the Summon Bats ability."
action_icon_state = "vampire_bats"
charge_max = 1200
required_blood = 75
var/num_bats = 2
/obj/effect/proc_holder/spell/vampire/bats/choose_targets(mob/user = usr)
var/list/turf/locs = new
for(var/direction in GLOB.alldirs) //looking for bat spawns
if(locs.len == num_bats) //we found 2 locations and thats all we need
break
var/turf/T = get_step(usr, direction) //getting a loc in that direction
if(AStar(user, T, /turf/proc/Distance, 1, simulated_only = 0)) // if a path exists, so no dense objects in the way its valid salid
locs += T
// pad with player location
for(var/i = locs.len + 1 to num_bats)
locs += user.loc
perform(locs, user = user)
/obj/effect/proc_holder/spell/vampire/bats/cast(list/targets, mob/user = usr)
for(var/T in targets)
new /mob/living/simple_animal/hostile/scarybat(T, user)
/obj/effect/proc_holder/spell/vampire/self/jaunt
name = "Mist Form (30)"
desc = "You take on the form of mist for a short period of time."
gain_desc = "You have gained the Mist Form ability which allows you to take on the form of mist for a short period and pass over any obstacle in your path."
action_icon_state = "mist"
charge_max = 600
required_blood = 30
centcom_cancast = 0
var/jaunt_duration = 50 //in deciseconds
/obj/effect/proc_holder/spell/vampire/self/jaunt/cast(list/targets, mob/user = usr)
spawn(0)
var/mob/living/U = user
var/originalloc = get_turf(user.loc)
var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(originalloc)
var/atom/movable/overlay/animation = new /atom/movable/overlay(originalloc)
animation.name = "blood"
animation.density = 0
animation.anchored = 1
animation.icon = 'icons/mob/mob.dmi'
animation.icon_state = "empty"
animation.layer = 5
animation.master = holder
U.ExtinguishMob()
flick("mist", animation)
user.forceMove(holder)
user.client.eye = holder
sleep(jaunt_duration)
var/mobloc = get_turf(user.loc)
animation.loc = mobloc
user.canmove = 0
sleep(20)
flick("mist_reappear", animation)
sleep(5)
if(!user.Move(mobloc))
for(var/turf/T in orange(7, mobloc))
if(T)
if(user.Move(T))
break
user.canmove = 1
user.client.eye = user
qdel(animation)
qdel(holder)
// Blink for vamps
// Less smoke spam.
/obj/effect/proc_holder/spell/vampire/shadowstep
name = "Shadowstep (30)"
desc = "Vanish into the shadows."
gain_desc = "You have gained the ability to shadowstep, which makes you disappear into nearby shadows at the cost of blood."
action_icon_state = "shadowblink"
charge_max = 20
required_blood = 30
centcom_cancast = 0
// Teleport radii
var/inner_tele_radius = 0
var/outer_tele_radius = 6
// Maximum lighting_lumcount.
var/max_lum = 1
/obj/effect/proc_holder/spell/vampire/shadowstep/choose_targets(mob/user = usr)
var/list/turfs = new/list()
for(var/turf/T in range(user, outer_tele_radius))
if(T in range(user, inner_tele_radius))
continue
if(istype(T, /turf/space))
continue
if(T.density)
continue
if(T.x > world.maxx-outer_tele_radius || T.x < outer_tele_radius)
continue //putting them at the edge is dumb
if(T.y > world.maxy-outer_tele_radius || T.y < outer_tele_radius)
continue
var/lightingcount = T.get_lumcount(0.5) * 10
// LIGHTING CHECK
if(lightingcount > max_lum)
continue
turfs += T
if(!turfs.len)
revert_cast(user)
to_chat(user, "<span class='warning'>You cannot find darkness to step to.</span>")
return
turfs = list(pick(turfs)) // Pick a single turf for the vampire to jump to.
perform(turfs, user = user)
// `targets` should only ever contain the 1 valid turf we're jumping to, even though its a list, that's just how the cast() proc works.
/obj/effect/proc_holder/spell/vampire/shadowstep/cast(list/targets, mob/user = usr)
spawn(0)
if(!LAZYLEN(targets)) // If for some reason the turf got deleted.
return
var/mob/living/U = user
U.ExtinguishMob()
var/atom/movable/overlay/animation = new /atom/movable/overlay(get_turf(user))
animation.name = user.name
animation.density = 0
animation.anchored = 1
animation.icon = user.icon
animation.alpha = 127
animation.layer = 5
//animation.master = src
user.forceMove(targets[1])
spawn(10)
qdel(animation)
/datum/vampire_passive/regen
gain_desc = "Your rejuvination abilities have improved and will now heal you over time when used."
/datum/vampire_passive/vision
gain_desc = "Your vampiric vision has improved."
/datum/vampire_passive/full
gain_desc = "You have reached your full potential and are no longer weak to the effects of anything holy and your vision has been improved greatly."
/obj/effect/proc_holder/spell/targeted/raise_vampires
name = "Raise Vampires"
desc = "Summons deadly vampires from bluespace."
school = "transmutation"
charge_max = 100
clothes_req = 0
human_req = 1
invocation = "none"
invocation_type = "none"
max_targets = 0
range = 3
cooldown_min = 20
action_icon_state = "revive_thrall"
sound = 'sound/magic/wandodeath.ogg'
/obj/effect/proc_holder/spell/targeted/raise_vampires/cast(list/targets, mob/user = usr)
new /obj/effect/temp_visual/cult/sparks(user.loc)
var/turf/T = get_turf(user)
to_chat(user, "<span class='warning'>You call out within bluespace, summoning more vampiric spirits to aid you!</span>")
for(var/mob/living/carbon/human/H in targets)
T.Beam(H, "sendbeam", 'icons/effects/effects.dmi', time=30, maxdistance=7, beam_type=/obj/effect/ebeam)
new /obj/effect/temp_visual/cult/sparks(H.loc)
H.raise_vampire(user)
/mob/living/carbon/human/proc/raise_vampire(mob/M)
if(!istype(M))
log_debug("human/proc/raise_vampire called with invalid argument.")
return
if(!mind)
visible_message("[src] looks to be too stupid to understand what is going on.")
return
if(dna && (NO_BLOOD in dna.species.species_traits) || dna.species.exotic_blood || !blood_volume)
visible_message("[src] looks unfazed!")
return
if(mind.vampire || mind.special_role == SPECIAL_ROLE_VAMPIRE || mind.special_role == SPECIAL_ROLE_VAMPIRE_THRALL)
visible_message("<span class='notice'>[src] looks refreshed!</span>")
adjustBruteLoss(-60)
adjustFireLoss(-60)
for(var/obj/item/organ/external/E in bodyparts)
if(prob(25))
E.mend_fracture()
return
if(stat != DEAD)
if(IsWeakened())
visible_message("<span class='warning'>[src] looks to be in pain!</span>")
adjustBrainLoss(60)
else
visible_message("<span class='warning'>[src] looks to be stunned by the energy!</span>")
Weaken(20)
return
for(var/obj/item/implant/mindshield/L in src)
if(L && L.implanted)
qdel(L)
for(var/obj/item/implant/traitor/T in src)
if(T && T.implanted)
qdel(T)
visible_message("<span class='warning'>[src] gets an eerie red glow in their eyes!</span>")
var/datum/objective/protect/protect_objective = new
protect_objective.owner = mind
protect_objective.target = M.mind
protect_objective.explanation_text = "Protect [M.real_name]."
mind.objectives += protect_objective
add_attack_logs(M, src, "Vampire-sired")
mind.make_Vampire()
revive()
Weaken(20)
@@ -0,0 +1,79 @@
/obj/effect/proc_holder/spell/self/vampire/blood_swell
name = "Blood Swell (30)"
desc = "You infuse your body with blood, making you highly resistant to stuns and physical damage. However, this makes you unable to fire ranged weapons while it is active."
gain_desc = "You have gained the ability to temporarly resist large amounts of stuns and physical damage."
charge_max = 40 SECONDS
required_blood = 30
action_icon_state = "blood_swell"
/obj/effect/proc_holder/spell/self/vampire/blood_swell/cast(list/targets, mob/user)
var/mob/living/target = targets[1]
if(ishuman(target))
var/mob/living/carbon/human/H = target
H.apply_status_effect(STATUS_EFFECT_BLOOD_SWELL)
/datum/vampire_passive/blood_swell_upgrade
gain_desc = "While blood swell is active all of your melee attacks deal increased damage."
/obj/effect/proc_holder/spell/self/vampire/overwhelming_force
name = "Overwhelming Force"
desc = "When toggled you will automatically pry open doors that you bump into if you do not have access."
gain_desc = "You have gained the ability to force open doors at a small blood cost."
charge_max = 2 SECONDS
action_icon_state = "OH_YEAAAAH"
/obj/effect/proc_holder/spell/self/vampire/overwhelming_force/cast(list/targets, mob/user)
if(!HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
to_chat(user, "<span class='warning'>You feel MIGHTY!</span>")
ADD_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT)
user.status_flags &= ~CANPUSH
user.move_resist = MOVE_FORCE_STRONG
else
REMOVE_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT)
user.move_resist = MOVE_FORCE_DEFAULT
user.status_flags |= CANPUSH
/obj/effect/proc_holder/spell/self/vampire/blood_rush
name = "Blood Rush (30)"
desc = "Infuse yourself with blood magic to boost your movement speed."
gain_desc = "You have gained the ability to temporarily move at high speeds."
charge_max = 30 SECONDS
required_blood = 30
action_icon_state = "blood_rush"
/obj/effect/proc_holder/spell/self/vampire/blood_rush/cast(list/targets, mob/user)
var/mob/living/target = targets[1]
if(ishuman(target))
var/mob/living/carbon/human/H = target
to_chat(H, "<span class='notice'>You feel a rush of energy!</span>")
H.apply_status_effect(STATUS_EFFECT_BLOOD_RUSH)
/obj/effect/proc_holder/spell/targeted/click/charge
name = "Charge (30)"
desc = "You charge at wherever you click on screen, dealing large amounts of damage, stunning and destroying walls and other objects."
gain_desc = "You can now charge at a target on screen, dealing massive damage and destroying structures."
required_blood = 30
charge_max = 30 SECONDS
vampire_ability = TRUE
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
action_icon_state = "vampire_charge"
allowed_type = /atom
range = 7
auto_target_single = FALSE
click_radius = -1
/obj/effect/proc_holder/spell/targeted/click/charge/can_cast(mob/user, charge_check, show_message)
var/mob/living/L = user
if(L.IsWeakened() || L.resting)
return FALSE
return ..()
/obj/effect/proc_holder/spell/targeted/click/charge/cast(list/targets, mob/user)
var/target = targets[1]
if(isliving(user))
var/mob/living/L = user
L.apply_status_effect(STATUS_EFFECT_CHARGING)
L.throw_at(target, range, 1, L, FALSE, callback = CALLBACK(L, /mob/living/.proc/remove_status_effect, STATUS_EFFECT_CHARGING))
@@ -0,0 +1,243 @@
/obj/effect/proc_holder/spell/self/vampire/vamp_claws
name = "Vampiric Claws (30)"
desc = "You channel blood magics to forge deadly vampiric claws that leech blood and strike rapidly. Cannot be used if you are holding something that cannot be dropped."
gain_desc = "You have gained the ability to forge your hands into vampiric claws."
charge_max = 30 SECONDS
required_blood = 30
action_icon_state = "vampire_claws"
/obj/effect/proc_holder/spell/self/vampire/vamp_claws/cast(mob/user)
if(user.l_hand || user.r_hand)
to_chat(user, "<span class='notice'>You drop what was in your hands as large blades spring from your fingers!</span>")
user.drop_l_hand()
user.drop_r_hand()
else
to_chat(user, "<span class='notice'>Large blades of blood spring from your fingers!</span>")
var/obj/item/twohanded/required/vamp_claws/claws = new /obj/item/twohanded/required/vamp_claws(user.loc)
user.put_in_hands(claws)
/obj/effect/proc_holder/spell/self/vampire/vamp_claws/can_cast(mob/user, charge_check, show_message)
var/mob/living/L = user
if(L.canUnEquip(L.l_hand) && L.canUnEquip(L.r_hand))
return ..()
/obj/item/twohanded/required/vamp_claws
name = "vampiric claws"
desc = "A pair of eldritch claws made of living blood, they seem to flow yet they are solid"
icon = 'icons/effects/vampire_effects.dmi'
icon_state = "vamp_claws"
w_class = WEIGHT_CLASS_BULKY
flags = ABSTRACT | NODROP | DROPDEL
force = 10
force_wielded = 10
armour_penetration = 20
block_chance = 50
sharp = TRUE
attack_effect_override = ATTACK_EFFECT_CLAW
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut", "savaged", "clawed")
sprite_sheets_inhand = list("Vox" = 'icons/mob/clothing/species/vox/held.dmi', "Drask" = 'icons/mob/clothing/species/drask/held.dmi')
var/durability = 20
var/blood_drain_amount = 15
var/blood_absorbed_amount = 5
/obj/item/twohanded/required/vamp_claws/afterattack(atom/target, mob/user, proximity)
if(!user.mind?.vampire)
return
if(iscarbon(target))
var/mob/living/carbon/C = target
if(C.ckey && C.stat != DEAD && C.affects_vampire() && !(NO_BLOOD in C.dna.species.species_traits))
C.bleed(blood_drain_amount)
user.mind.vampire.adjust_blood(C, blood_absorbed_amount)
durability -= 1
if(durability <= 0)
qdel(src)
to_chat(user, "<span class='warning'>Your claws shatter!</span>")
/obj/item/twohanded/required/vamp_claws/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/twohanded/required/vamp_claws/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/twohanded/required/vamp_claws/process()
durability -= 1
if(durability <= 0)
qdel(src)
/obj/item/twohanded/required/vamp_claws/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type)
if(attack_type == PROJECTILE_ATTACK)
final_block_chance = 0
return ..()
/obj/item/twohanded/required/vamp_claws/melee_attack_chain(mob/user, atom/target, params)
..()
if(wielded)
user.changeNext_move(CLICK_CD_MELEE * 0.5)
/obj/item/twohanded/required/vamp_claws/attack_self(mob/user)
to_chat(user, "<span class='notice'>You dispel your claws!</span>")
qdel(src)
/obj/effect/proc_holder/spell/targeted/click/blood_tendrils
name = "Blood Tendrils (10)"
desc = "You summon blood tendrils from bluespace after a delay to ensnare people in an area, slowing them down."
gain_desc = "You have gained the ability to summon blood tendrils to slow people down in an area that you target."
required_blood = 10
vampire_ability = TRUE
click_radius = 1
charge_max = 30 SECONDS
allowed_type = /atom
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
action_icon_state = "blood_tendrils"
sound = 'sound/misc/enter_blood.ogg'
var/area_of_affect = 1
selection_activated_message = "<span class='notice'>You channel blood magics to weaken the bluespace veil. <B>Left-click to cast at a target area!</B></span>"
selection_deactivated_message = "<span class='notice'>Your magics subside.</span>"
/obj/effect/proc_holder/spell/targeted/click/blood_tendrils/cast(list/targets, mob/user)
var/turf/T = get_turf(targets[1]) // there should only ever be one entry in targets for this spell
for(var/turf/simulated/blood_turf in view(area_of_affect, T))
if(blood_turf.density)
continue
new /obj/effect/temp_visual/blood_tendril(blood_turf)
addtimer(CALLBACK(src, .proc/apply_slowdown, T, area_of_affect, 3, user), 0.5 SECONDS)
/obj/effect/proc_holder/spell/targeted/click/blood_tendrils/proc/apply_slowdown(turf/T, distance, slowed_amount, mob/user)
for(var/mob/living/L in view(distance, T))
if(L.affects_vampire(user))
L.AdjustSlowed(slowed_amount)
L.visible_message("<span class='warning'>[L] gets ensared in blood tendrils, restricting [L.p_their()] movement!</span>")
new /obj/effect/temp_visual/blood_tendril/long(get_turf(L))
/obj/effect/temp_visual/blood_tendril
icon = 'icons/effects/vampire_effects.dmi'
icon_state = "blood_tendril"
duration = 1 SECONDS
/obj/effect/temp_visual/blood_tendril/long
duration = 2 SECONDS
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/blood_pool
name = "Sanguine Pool (50)"
desc = "You shift your form into a pool of blood, making you invulnerable and able to move through anything that's not a wall or space. You leave a trail of blood behind you when you do this."
gain_desc = "You have gained the ability to shift into a pool of blood, allowing you to evade pursuers with great mobility."
vampire_ability = TRUE
required_blood = 50
jaunt_duration = 3 SECONDS
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
action_icon_state = "blood_pool"
jaunt_type_path = /obj/effect/dummy/spell_jaunt/blood_pool
jaunt_water_effect = FALSE
jaunt_out_type = /obj/effect/temp_visual/dir_setting/cult/phase/out
jaunt_in_type = /obj/effect/temp_visual/dir_setting/cult/phase
jaunt_in_time = 0
sound1 = 'sound/misc/enter_blood.ogg'
/obj/effect/proc_holder/spell/blood_eruption
name = "Blood Eruption (100)"
desc = "Every pool of blood in 4 tiles erupts with a spike of living blood, damaging anyone stood on it."
gain_desc = "You have gained the ability to weaponise pools of blood to damage those stood on them."
vampire_ability = TRUE
required_blood = 100
charge_max = 200 SECONDS
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
action_icon_state = "blood_spikes"
/obj/effect/proc_holder/spell/blood_eruption/cast(list/targets, mob/user)
for(var/mob/living/L in targets)
var/turf/T = get_turf(L)
var/obj/effect/decal/cleanable/blood/B = locate(/obj/effect/decal/cleanable/blood) in T
var/obj/effect/temp_visual/blood_spike/spike = new /obj/effect/temp_visual/blood_spike(T)
spike.color = B.basecolor
playsound(L, 'sound/misc/demon_attack1.ogg', 50, TRUE)
L.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
L.visible_message("<span class='warning'><b>[L] gets impaled by a spike of living blood!</b></span>")
/obj/effect/proc_holder/spell/blood_eruption/choose_targets(mob/user)
var/list/targets = list()
for(var/mob/living/L in view(4, user))
var/turf/T = get_turf(L)
if(locate(/obj/effect/decal/cleanable/blood) in T)
if(L.affects_vampire(user) && !isLivingSSD(L))
targets.Add(L)
if(!length(targets))
revert_cast(user)
return
perform(targets)
/obj/effect/temp_visual/blood_spike
icon = 'icons/effects/vampire_effects.dmi'
icon_state = "bloodspike_white"
duration = 0.3 SECONDS
/obj/effect/proc_holder/spell/self/vampire/blood_spill
name = "The Blood Bringers Rite"
desc = "When toggled, everyone around you begins to bleed profusely."
gain_desc = "You have gained the ability to rip the very life force out of people and absorb it, healing you."
charge_max = 10 SECONDS
action_icon_state = "blood_bringers_rite"
required_blood = 10
/obj/effect/proc_holder/spell/self/vampire/blood_spill/cast(list/targets, mob/user)
var/mob/target = targets[1]
if(!target.mind.vampire.get_ability(/datum/vampire_passive/blood_spill))
target.mind.vampire.force_add_ability(/datum/vampire_passive/blood_spill)
else
for(var/datum/vampire_passive/blood_spill/B in target.mind.vampire.powers)
target.mind.vampire.remove_ability(B)
/datum/vampire_passive/blood_spill
var/max_beams = 10
/datum/vampire_passive/blood_spill/New()
..()
START_PROCESSING(SSobj, src)
/datum/vampire_passive/blood_spill/Destroy(force, ...)
STOP_PROCESSING(SSobj, src)
return ..()
/datum/vampire_passive/blood_spill/process()
var/beam_number = 0
var/turf/T = get_turf(owner)
for(var/mob/living/carbon/human/H in view(7, T))
if(NO_BLOOD in H.dna.species.species_traits)
continue
if(!H.affects_vampire(owner) || H.stat)
continue
var/drain_amount = rand(5, 10)
beam_number++
H.bleed(drain_amount)
H.Beam(owner, icon_state = "drainbeam", time = 2 SECONDS)
H.adjustBruteLoss(2)
owner.heal_overall_damage(8, 2, TRUE)
owner.adjustStaminaLoss(-15)
owner.AdjustStunned(-1)
owner.AdjustWeakened(-1)
if(drain_amount == 10)
to_chat(H, "<span class='warning'>You feel your life force draining!</b></span>")
if(beam_number >= max_beams)
break
owner.mind.vampire.bloodusable = max(owner.mind.vampire.bloodusable - 10, 0)
if(!owner.mind.vampire.bloodusable || owner.stat == DEAD)
owner.mind.vampire.remove_ability(src)
@@ -0,0 +1,188 @@
/obj/effect/proc_holder/spell/self/vampire/cloak
name = "Cloak of Darkness"
desc = "Toggles whether you are currently cloaking yourself in darkness. When in darkness and toggled on, you move at increased speeds."
gain_desc = "You have gained the Cloak of Darkness ability, which when toggled makes you nearly invisible and highly agile in the shroud of darkness."
action_icon_state = "vampire_cloak"
charge_max = 2 SECONDS
/obj/effect/proc_holder/spell/self/vampire/cloak/New()
..()
update_name()
/obj/effect/proc_holder/spell/self/vampire/cloak/proc/update_name()
var/mob/living/user = loc
if(!ishuman(user) || !user.mind || !user.mind.vampire)
return
action.button.name = "[initial(name)] ([user.mind.vampire.iscloaking ? "Deactivate" : "Activate"])"
/obj/effect/proc_holder/spell/self/vampire/cloak/cast(list/targets, mob/user = usr)
var/datum/vampire/V = user.mind.vampire
V.iscloaking = !V.iscloaking
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(V.iscloaking)
H.physiology.burn_mod *= 1.3
user.RegisterSignal(user, COMSIG_LIVING_IGNITED, /mob/living.proc/update_vampire_cloak)
else
user.UnregisterSignal(user, COMSIG_LIVING_IGNITED)
H.physiology.burn_mod /= 1.3
update_name()
to_chat(user, "<span class='notice'>You will now be [V.iscloaking ? "hidden" : "seen"] in darkness.</span>")
/mob/living/proc/update_vampire_cloak()
SIGNAL_HANDLER
mind.vampire.handle_vampire_cloak()
/obj/effect/proc_holder/spell/targeted/click/shadow_snare
name = "Shadow Snare (20)"
desc = "You summon a trap on the ground. When crossed it will blind the target, extinguish any lights they may have, and ensnare them."
gain_desc = "You have gained the ability to summon a trap that will blind, ensnare, and turn off the lights of anyone who crosses it."
charge_max = 20 SECONDS
required_blood = 20
vampire_ability = TRUE
allowed_type = /turf/simulated
click_radius = -1
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
action_icon_state = "shadow_snare"
/obj/effect/proc_holder/spell/targeted/click/shadow_snare/cast(list/targets, mob/user)
var/turf/target = targets[1]
new /obj/item/restraints/legcuffs/beartrap/shadow_snare(target)
/obj/item/restraints/legcuffs/beartrap/shadow_snare
name = "shadow snare"
desc = "An almost transparent trap that melts into the shadows."
alpha = 60
armed = TRUE
anchored = TRUE
breakouttime = 5 SECONDS
flags = DROPDEL
/obj/item/restraints/legcuffs/beartrap/shadow_snare/Crossed(AM, oldloc)
if(!iscarbon(AM) || !armed)
return
var/mob/living/carbon/C = AM
if(!C.affects_vampire()) // no parameter here so holy always protects
return
C.extinguish_light()
C.EyeBlind(10)
STOP_PROCESSING(SSobj, src) // won't wither away once you are trapped
..()
if(!iscarbon(loc)) // if it fails to latch onto someone for whatever reason, delete itself, we don't want unarmed ones lying around.
qdel(src)
/obj/item/restraints/legcuffs/beartrap/shadow_snare/attack_hand(mob/user)
Crossed(user)
/obj/item/restraints/legcuffs/beartrap/shadow_snare/attack_tk(mob/user)
if(iscarbon(user))
to_chat(user, "<span class='userdanger'>The snare sends a psychic backlash!</span>")
user.EyeBlind(10)
/obj/item/restraints/legcuffs/beartrap/shadow_snare/process()
var/turf/T = get_turf(src)
var/lighting_count = T.get_lumcount() * 10
if(lighting_count > 2)
obj_integrity -= 50
if(obj_integrity <= 0)
visible_message("<span class='notice'>[src] withers away.</span>")
qdel(src)
/obj/item/restraints/legcuffs/beartrap/shadow_snare/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/restraints/legcuffs/beartrap/shadow_snare/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/proc_holder/spell/targeted/click/dark_passage
name = "Dark Passage (30)"
desc = "You teleport to a targeted turf."
gain_desc = "You have gained the ability to blink a short distance towards a targeted turf."
charge_max = 40 SECONDS
required_blood = 30
vampire_ability = TRUE
allowed_type = /turf/simulated
click_radius = 0
centcom_cancast = FALSE
action_icon_state = "dark_passage"
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
/obj/effect/proc_holder/spell/targeted/click/dark_passage/cast(list/targets, mob/user)
var/turf/target = get_turf(targets[1])
new /obj/effect/temp_visual/vamp_mist_out(get_turf(user))
user.forceMove(target)
/obj/effect/temp_visual/vamp_mist_out
duration = 2 SECONDS
icon = 'icons/mob/mob.dmi'
icon_state = "mist"
/obj/effect/proc_holder/spell/aoe_turf/vamp_extinguish
name = "Extinguish"
desc = "You extinguish any light source in an area around you."
gain_desc = "You have gained the ability to extinguish nearby light sources."
charge_max = 20 SECONDS
vampire_ability = TRUE
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
action_icon_state = "vampire_extinguish"
/obj/effect/proc_holder/spell/aoe_turf/vamp_extinguish/cast(list/targets, mob/user = usr)
for(var/turf/T in targets)
T.extinguish_light()
for(var/atom/A in T.contents)
A.extinguish_light()
/obj/effect/proc_holder/spell/self/vampire/eternal_darkness
name = "Eternal Darkness"
desc = "When toggled, you shroud the area around you in darkness and slowly lower the body temperature of people nearby."
gain_desc = "You have gained the ability to shroud the area around you in darkness, only the strongest of lights can pierce your unholy powers."
charge_max = 10 SECONDS
action_icon_state = "eternal_darkness"
required_blood = 5
var/shroud_power = -4
/obj/effect/proc_holder/spell/self/vampire/eternal_darkness/cast(list/targets, mob/user)
var/mob/target = targets[1]
if(!target.mind.vampire.get_ability(/datum/vampire_passive/eternal_darkness))
target.mind.vampire.force_add_ability(/datum/vampire_passive/eternal_darkness)
target.set_light(6, shroud_power, "#AAD84B")
else
for(var/datum/vampire_passive/eternal_darkness/E in target.mind.vampire.powers)
target.mind.vampire.remove_ability(E)
/datum/vampire_passive/eternal_darkness
gain_desc = "You surround yourself in a unnatural darkness, freezing those around you."
/datum/vampire_passive/eternal_darkness/New()
..()
START_PROCESSING(SSobj, src)
/datum/vampire_passive/eternal_darkness/Destroy(force, ...)
owner.remove_light()
STOP_PROCESSING(SSobj, src)
return ..()
/datum/vampire_passive/eternal_darkness/process()
for(var/mob/living/L in view(6, owner))
if(L.affects_vampire(owner))
L.adjust_bodytemperature(-20 * TEMPERATURE_DAMAGE_COEFFICIENT)
owner.mind.vampire.bloodusable = max(owner.mind.vampire.bloodusable - 5, 0)
if(!owner.mind.vampire.bloodusable || owner.stat == DEAD)
owner.mind.vampire.remove_ability(src)
/datum/vampire_passive/xray
gain_desc = "You can now see through walls, incase you hadn't noticed."
@@ -0,0 +1,464 @@
//This should hold all the vampire related powers
/mob/living/proc/affects_vampire(mob/user)
//Other vampires aren't affected
if(mind?.vampire)
return FALSE
//Vampires who have reached their full potential can affect nearly everything
if(user?.mind.vampire.get_ability(/datum/vampire_passive/full))
return TRUE
//Holy characters are resistant to vampire powers
if(mind?.isholy)
return FALSE
return TRUE
/obj/effect/proc_holder/spell/self/choose_targets(mob/user = usr)
perform(list(user))
/obj/effect/proc_holder/spell/mob_aoe/choose_targets(mob/user = usr)
var/list/targets[0]
for(var/mob/living/L in view(range, user))
if(L == user)
continue
targets += L
if(!length(targets))
revert_cast(user)
return
perform(targets, user = user)
/obj/effect/proc_holder/spell/proc/before_cast_vampire(list/targets)
// sanity check before we cast
if(!usr.mind || !usr.mind.vampire)
targets.Cut()
return FALSE
if(!required_blood)
return
// enforce blood
var/datum/vampire/vampire = usr.mind.vampire
var/blood_cost_modifier = 1 + vampire.nullified / 100
var/blood_cost = round(required_blood * blood_cost_modifier)
if(blood_cost <= vampire.bloodusable)
if(!deduct_blood_on_cast) //don't take the blood yet if this is false!
return
vampire.bloodusable -= blood_cost
SSblackbox.record_feedback("tally", "vampire_powers_used", 1, "[name]")
to_chat(usr, "<span class='boldnotice'>You have [vampire.bloodusable] left to use.</span>")
return TRUE
else
// stop!!
targets.Cut()
return FALSE
/datum/vampire_passive
var/gain_desc
var/mob/living/owner = null
/datum/vampire_passive/New()
..()
if(!gain_desc)
gain_desc = "You can now use [src]."
/datum/vampire_passive/Destroy(force, ...)
owner = null
return ..()
/obj/effect/proc_holder/spell/self/vampire
vampire_ability = TRUE
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
/obj/effect/proc_holder/spell/self/vampire/rejuvenate
name = "Rejuvenate"
desc = "Use reserve blood to enliven your body, removing any incapacitating effects."
action_icon_state = "vampire_rejuvinate"
charge_max = 200
stat_allowed = 1
/obj/effect/proc_holder/spell/self/vampire/rejuvenate/cast(list/targets, mob/user = usr)
var/mob/living/U = user
U.SetWeakened(0)
U.SetStunned(0)
U.SetParalysis(0)
U.SetSleeping(0)
U.SetConfused(0)
U.adjustStaminaLoss(-100)
to_chat(user, "<span class='notice'>You instill your body with clean blood and remove any incapacitating effects.</span>")
var/rejuv_bonus = U.mind.vampire.get_rejuv_bonus()
if(rejuv_bonus)
INVOKE_ASYNC(src, .proc/heal, U, rejuv_bonus)
/obj/effect/proc_holder/spell/self/vampire/rejuvenate/proc/heal(mob/living/user, rejuv_bonus)
for(var/i in 1 to 5)
user.adjustBruteLoss(-2 * rejuv_bonus)
user.adjustOxyLoss(-5 * rejuv_bonus)
user.adjustToxLoss(-2 * rejuv_bonus)
user.adjustFireLoss(-2 * rejuv_bonus)
for(var/datum/reagent/R in user.reagents.reagent_list)
if(!R.harmless)
user.reagents.remove_reagent(R.id, 2 * rejuv_bonus)
sleep(35)
/datum/vampire/proc/get_rejuv_bonus()
var/rejuv_multiplier = 0
if(!get_ability(/datum/vampire_passive/regen))
return rejuv_multiplier
if(subclass?.improved_rejuv_healing)
rejuv_multiplier = clamp((100 - owner.health) / 20, 1, 5) // brute and burn healing between 5 and 50
return rejuv_multiplier
return 1
/obj/effect/proc_holder/spell/self/vampire/specialize
name = "Choose Specialization"
desc = "Choose what sub-class of vampire you want to evolve into."
gain_desc = "You can now choose what specialization of vampire you want to evolve into."
charge_max = 2 SECONDS
action_icon_state = "select_class"
/obj/effect/proc_holder/spell/self/vampire/specialize/cast(mob/user)
ui_interact(user)
/obj/effect/proc_holder/spell/self/vampire/specialize/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "SpecMenu", "Specialisation Menu", 900, 600, master_ui, state)
ui.set_autoupdate(FALSE)
ui.open()
/obj/effect/proc_holder/spell/self/vampire/specialize/ui_data(mob/user)
var/datum/vampire/vamp = user.mind.vampire
var/list/data = list("subclasses" = vamp.subclass)
return data
/obj/effect/proc_holder/spell/self/vampire/specialize/ui_act(action, list/params)
if(..())
return
var/datum/vampire/vamp = usr.mind.vampire
if(vamp.subclass)
vamp.upgrade_tiers -= type
vamp.remove_ability(src)
return
switch(action)
if("umbrae")
vamp.add_subclass(SUBCLASS_UMBRAE)
vamp.upgrade_tiers -= type
vamp.remove_ability(src)
if("hemomancer")
vamp.add_subclass(SUBCLASS_HEMOMANCER)
vamp.upgrade_tiers -= type
vamp.remove_ability(src)
if("gargantua")
vamp.add_subclass(SUBCLASS_GARGANTUA)
vamp.upgrade_tiers -= type
vamp.remove_ability(src)
/datum/vampire/proc/add_subclass(subclass_to_add, announce = TRUE)
var/datum/vampire_subclass/new_subclass = new subclass_to_add
subclass = new_subclass
check_vampire_upgrade(announce)
SSblackbox.record_feedback("nested tally", "vampire_subclasses", 1, list("[new_subclass.name]"))
/obj/effect/proc_holder/spell/mob_aoe/glare
name = "Glare"
desc = "Your eyes flash, stunning and silencing anyone infront of you. It has lesser effects for those around you."
action_icon_state = "vampire_glare"
charge_max = 30 SECONDS
stat_allowed = 1
range = 1
vampire_ability = TRUE
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
/// No deviation at all. Flashed from the front or front-left/front-right. Alternatively, flashed in direct view.
#define DEVIATION_NONE 3
/// Partial deviation. Flashed from the side. Alternatively, flashed out the corner of your eyes.
#define DEVIATION_PARTIAL 2
/// Full deviation. Flashed from directly behind or behind-left/behind-rack. Not flashed at all.
#define DEVIATION_FULL 1
/obj/effect/proc_holder/spell/mob_aoe/glare/cast(list/targets, mob/living/user = usr)
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(istype(H.glasses, /obj/item/clothing/glasses/sunglasses/blindfold))
var/obj/item/clothing/glasses/sunglasses/blindfold/B = H.glasses
if(B.tint)
to_chat(user, "<span class='warning'>You're blindfolded!</span>")
return
user.mob_light(LIGHT_COLOR_BLOOD_MAGIC, 3, _duration = 2)
user.visible_message("<span class='warning'>[user]'s eyes emit a blinding flash!</span>")
for(var/mob/living/target in targets)
if(!target.affects_vampire(user))
continue
var/deviation
if(user.weakened || user.resting)
deviation = DEVIATION_PARTIAL
else
deviation = calculate_deviation(target, user)
if(deviation == DEVIATION_FULL)
target.AdjustConfused(3)
target.adjustStaminaLoss(40)
else if(deviation == DEVIATION_PARTIAL)
target.Weaken(1)
target.AdjustConfused(3)
target.adjustStaminaLoss(40)
else
target.adjustStaminaLoss(120)
target.Weaken(6)
target.AdjustSilence(3)
target.flash_eyes(1, TRUE, TRUE)
to_chat(target, "<span class='warning'>You are blinded by [user]'s glare.</span>")
add_attack_logs(user, target, "(Vampire) Glared at")
/obj/effect/proc_holder/spell/mob_aoe/glare/proc/calculate_deviation(mob/victim, mob/attacker)
// Are they on the same tile? We'll return partial deviation. This may be someone flashing while lying down
if(victim.loc == attacker.loc)
return DEVIATION_PARTIAL
// If the victim was looking at the attacker, this is the direction they'd have to be facing.
var/attacker_to_victim = get_dir(attacker, victim)
// The victim's dir is necessarily a cardinal value.
var/attacker_dir = attacker.dir
// - - -
// - V - Attacker facing south
// # # #
// Attacker within 45 degrees of where the victim is facing.
if(attacker_dir & attacker_to_victim)
return DEVIATION_NONE
// # # #
// - V - Attacker facing south
// - - -
// Victim at 135 or more degrees of where the victim is facing.
if(attacker_dir & GetOppositeDir(attacker_to_victim))
return DEVIATION_FULL
// - - -
// # V # Attacker facing south
// - - -
// Victim lateral to the victim.
return DEVIATION_PARTIAL
#undef DEVIATION_NONE
#undef DEVIATION_PARTIAL
#undef DEVIATION_FULL
/datum/vampire_passive/regen
gain_desc = "Your rejuvenation abilities have improved and will now heal you over time when used."
/datum/vampire_passive/vision
gain_desc = "Your vampiric vision has improved."
/datum/vampire_passive/full
gain_desc = "You have reached your full potential. You are no longer weak to the effects of anything holy and your vision has improved greatly."
/obj/effect/proc_holder/spell/targeted/raise_vampires
name = "Raise Vampires"
desc = "Summons deadly vampires from bluespace."
school = "transmutation"
charge_max = 100
clothes_req = 0
human_req = 1
invocation = "none"
invocation_type = "none"
max_targets = 0
range = 3
cooldown_min = 20
action_icon_state = "revive_thrall"
vampire_ability = TRUE
sound = 'sound/magic/wandodeath.ogg'
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
gain_desc = "You have gained the ability to Raise Vampires. This extremely powerful AOE ability affects all humans near you. Vampires/thralls are healed. Corpses are raised as vampires. Others are stunned, then brain damaged, then killed."
/obj/effect/proc_holder/spell/targeted/raise_vampires/cast(list/targets, mob/user = usr)
new /obj/effect/temp_visual/cult/sparks(user.loc)
var/turf/T = get_turf(user)
to_chat(user, "<span class='warning'>You call out within bluespace, summoning more vampiric spirits to aid you!</span>")
for(var/mob/living/carbon/human/H in targets)
T.Beam(H, "sendbeam", 'icons/effects/effects.dmi', time = 30, maxdistance = 7, beam_type = /obj/effect/ebeam)
new /obj/effect/temp_visual/cult/sparks(H.loc)
raise_vampire(user, H)
/obj/effect/proc_holder/spell/targeted/raise_vampires/proc/raise_vampire(mob/M, mob/living/carbon/human/H)
if(!istype(M) || !istype(H))
return
if(!H.mind)
visible_message("[H] looks to be too stupid to understand what is going on.")
return
if(H.dna && (NO_BLOOD in H.dna.species.species_traits) || H.dna.species.exotic_blood || !H.blood_volume)
visible_message("[H] looks unfazed!")
return
if(H.mind.vampire || H.mind.special_role == SPECIAL_ROLE_VAMPIRE || H.mind.special_role == SPECIAL_ROLE_VAMPIRE_THRALL)
visible_message("<span class='notice'>[H] looks refreshed!</span>")
H.adjustBruteLoss(-60)
H.adjustFireLoss(-60)
for(var/obj/item/organ/external/E in H.bodyparts)
if(prob(25))
E.mend_fracture()
E.internal_bleeding = FALSE
return
if(H.stat != DEAD)
if(H.IsWeakened())
visible_message("<span class='warning'>[H] looks to be in pain!</span>")
H.adjustBrainLoss(60)
else
visible_message("<span class='warning'>[H] looks to be stunned by the energy!</span>")
H.Weaken(20)
return
for(var/obj/item/implant/mindshield/L in H)
if(L && L.implanted)
qdel(L)
for(var/obj/item/implant/traitor/T in H)
if(T && T.implanted)
qdel(T)
visible_message("<span class='warning'>[H] gets an eerie red glow in their eyes!</span>")
var/datum/objective/protect/protect_objective = new
protect_objective.owner = H.mind
protect_objective.target = M.mind
protect_objective.explanation_text = "Protect [M.real_name]."
H.mind.objectives += protect_objective
add_attack_logs(M, H, "Vampire-sired")
H.mind.make_vampire()
H.revive()
H.Weaken(20)
/obj/effect/proc_holder/spell/targeted/turf_teleport/shadow_step
name = "Shadow Step (30)"
desc = "Teleport to a nearby dark region"
gain_desc = "You have gained the ability to shadowstep, which makes you disappear into nearby shadows at the cost of blood."
action_icon_state = "shadowblink"
charge_max = 20
required_blood = 30
centcom_cancast = FALSE
vampire_ability = TRUE
include_space = FALSE
range = -1
include_user = TRUE
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
// Teleport radii
inner_tele_radius = 0
outer_tele_radius = 6
include_light_turfs = FALSE
sound1 = null
sound2 = null
// pure adminbus at the moment
/proc/isvampirethrall(mob/living/M)
return istype(M) && M.mind && SSticker.mode && (M.mind in SSticker.mode.vampire_enthralled)
/obj/effect/proc_holder/spell/targeted/enthrall
name = "Enthrall (150)"
desc = "You use a large portion of your power to sway those loyal to none to be loyal to you only."
gain_desc = "You have gained the ability to thrall people to your will."
action_icon_state = "vampire_enthrall"
required_blood = 150
deduct_blood_on_cast = FALSE
vampire_ability = TRUE
humans_only = TRUE
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
/obj/effect/proc_holder/spell/targeted/enthrall/cast(list/targets, mob/user = usr)
var/datum/vampire/vampire = user.mind.vampire
for(var/mob/living/target in targets)
user.visible_message("<span class='warning'>[user] bites [target]'s neck!</span>", "<span class='warning'>You bite [target]'s neck and begin the flow of power.</span>")
to_chat(target, "<span class='warning'>You feel the tendrils of evil invade your mind.</span>")
if(do_mob(user, target, 50))
if(can_enthrall(user, target))
handle_enthrall(user, target)
var/blood_cost_modifier = 1 + vampire.nullified/100
var/blood_cost = round(required_blood * blood_cost_modifier)
vampire.bloodusable -= blood_cost //we take the blood after enthralling, not before
else
revert_cast(user)
to_chat(user, "<span class='warning'>You or your target either moved or you dont have enough usable blood.</span>")
/obj/effect/proc_holder/spell/targeted/enthrall/proc/can_enthrall(mob/living/user, mob/living/carbon/C)
var/enthrall_safe = 0
for(var/obj/item/implant/mindshield/L in C)
if(L && L.implanted)
enthrall_safe = 1
break
for(var/obj/item/implant/traitor/T in C)
if(T && T.implanted)
enthrall_safe = 1
break
if(!C)
log_runtime(EXCEPTION("something bad happened on enthralling a mob, attacker is [user] [user.key] \ref[user]"), user)
return FALSE
if(!C.mind)
to_chat(user, "<span class='warning'>[C.name]'s mind is not there for you to enthrall.</span>")
return FALSE
if(enthrall_safe || (C.mind in SSticker.mode.vampires) || (C.mind.vampire) || (C.mind in SSticker.mode.vampire_enthralled))
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>You feel a familiar sensation in your skull that quickly dissipates.</span>")
return FALSE
if(!C.affects_vampire(user))
if(C.mind.isholy)
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>Your faith in [SSticker.Bible_deity_name] has kept your mind clear of all evil.</span>")
else
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>You resist the attack on your mind.</span>")
return FALSE
if(!ishuman(C))
to_chat(user, "<span class='warning'>You can only enthrall sentient humanoids!</span>")
return FALSE
return TRUE
/obj/effect/proc_holder/spell/targeted/enthrall/proc/handle_enthrall(mob/living/user, mob/living/carbon/human/H)
if(!istype(H))
return 0
var/ref = "\ref[user.mind]"
if(!(ref in SSticker.mode.vampire_thralls))
SSticker.mode.vampire_thralls[ref] = list(H.mind)
else
SSticker.mode.vampire_thralls[ref] += H.mind
SSticker.mode.update_vampire_icons_added(H.mind)
SSticker.mode.update_vampire_icons_added(user.mind)
var/datum/mindslaves/slaved = user.mind.som
if(!slaved)
slaved = new()
slaved.masters = user.mind
H.mind.som = slaved
slaved.serv += H
slaved.add_serv_hud(user.mind, "vampire")//handles master servent icons
slaved.add_serv_hud(H.mind, "vampthrall")
SSticker.mode.vampire_enthralled.Add(H.mind)
SSticker.mode.vampire_enthralled[H.mind] = user.mind
H.mind.special_role = SPECIAL_ROLE_VAMPIRE_THRALL
var/datum/objective/protect/serve_objective = new
serve_objective.owner = user.mind
serve_objective.target = H.mind
serve_objective.explanation_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command."
H.mind.objectives += serve_objective
to_chat(H, "<span class='biggerdanger'>You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command.</span>")
to_chat(user, "<span class='warning'>You have successfully Enthralled [H]. <i>If [H.p_they()] refuse[H.p_s()] to do as you say just adminhelp.</i></span>")
H.Stun(2)
add_attack_logs(user, H, "Vampire-thralled")
@@ -0,0 +1,70 @@
/datum/vampire_subclass
/// The subclass' name. Used for blackbox logging.
var/name = "yell at coderbus"
/// A list of powers that a vampire unlocks. The value of the list entry is equal to the blood total required for the vampire to unlock it.
var/list/standard_powers
/// A list of the powers a vampire unlocks when it reaches full power.
var/list/fully_powered_abilities
/// Whether or not a vampire heals more based on damage taken.
var/improved_rejuv_healing = FALSE
/datum/vampire_subclass/proc/add_subclass_ability(datum/vampire/vamp)
for(var/thing in standard_powers)
if(vamp.bloodtotal >= standard_powers[thing])
vamp.add_ability(thing)
/datum/vampire_subclass/proc/add_full_power_abilities(datum/vampire/vamp)
for(var/thing in fully_powered_abilities)
vamp.add_ability(thing)
/datum/vampire_subclass/umbrae
name = "umbrae"
standard_powers = list(/obj/effect/proc_holder/spell/self/vampire/cloak = 150,
/obj/effect/proc_holder/spell/targeted/click/shadow_snare = 250,
/obj/effect/proc_holder/spell/targeted/click/dark_passage = 400,
/obj/effect/proc_holder/spell/aoe_turf/vamp_extinguish = 600)
fully_powered_abilities = list(/datum/vampire_passive/full,
/obj/effect/proc_holder/spell/self/vampire/eternal_darkness,
/datum/vampire_passive/xray)
/datum/vampire_subclass/hemomancer
name = "hemomancer"
standard_powers = list(/obj/effect/proc_holder/spell/self/vampire/vamp_claws = 150,
/obj/effect/proc_holder/spell/targeted/click/blood_tendrils = 250,
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/blood_pool = 400,
/obj/effect/proc_holder/spell/blood_eruption = 600)
fully_powered_abilities = list(/datum/vampire_passive/full,
/obj/effect/proc_holder/spell/self/vampire/blood_spill)
/datum/vampire_subclass/gargantua
name = "gargantua"
standard_powers = list(/obj/effect/proc_holder/spell/self/vampire/blood_swell = 150,
/obj/effect/proc_holder/spell/self/vampire/blood_rush = 250,
/datum/vampire_passive/blood_swell_upgrade = 400,
/obj/effect/proc_holder/spell/self/vampire/overwhelming_force = 600)
fully_powered_abilities = list(/datum/vampire_passive/full,
/obj/effect/proc_holder/spell/targeted/click/charge)
improved_rejuv_healing = TRUE
/datum/vampire_subclass/ancient
name = "ancient"
standard_powers = list(/obj/effect/proc_holder/spell/self/vampire/vamp_claws,
/obj/effect/proc_holder/spell/self/vampire/blood_swell,
/obj/effect/proc_holder/spell/self/vampire/cloak,
/obj/effect/proc_holder/spell/targeted/click/blood_tendrils,
/obj/effect/proc_holder/spell/self/vampire/blood_rush,
/obj/effect/proc_holder/spell/targeted/click/shadow_snare,
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/blood_pool,
/datum/vampire_passive/blood_swell_upgrade,
/obj/effect/proc_holder/spell/targeted/click/dark_passage,
/obj/effect/proc_holder/spell/blood_eruption,
/obj/effect/proc_holder/spell/self/vampire/overwhelming_force,
/obj/effect/proc_holder/spell/aoe_turf/vamp_extinguish,
/obj/effect/proc_holder/spell/targeted/raise_vampires,
/obj/effect/proc_holder/spell/targeted/enthrall,
/datum/vampire_passive/full,
/obj/effect/proc_holder/spell/self/vampire/blood_spill,
/obj/effect/proc_holder/spell/targeted/click/charge,
/obj/effect/proc_holder/spell/self/vampire/eternal_darkness,
/datum/vampire_passive/xray)
improved_rejuv_healing = TRUE
+4
View File
@@ -83,6 +83,10 @@
if(!ishuman(M)) //If target is not a human
return ..()
if(!M.mind)
to_chat(user, "<span class='warning'>This being has no soul!</span>")
return ..()
if(M.has_brain_worms()) //Borer stuff - RR
to_chat(user, "<span class='warning'>This being is corrupted by an alien intelligence and cannot be soul trapped.</span>")
return ..()
+15
View File
@@ -343,6 +343,21 @@
playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1)
return TRUE
/datum/spellbook_entry/summon/slience_ghosts
name = "Silence Ghosts"
desc = "Tired of people talking behind your back, and spooking you? Why not silence them, and make the dead deader."
cost = 2
log_name = "SLG"
is_ragin_restricted = TRUE //Salt needs to flow here, to be honest
/datum/spellbook_entry/summon/slience_ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
new /datum/event/wizard/ghost_mute()
active = TRUE
to_chat(user, "<span class='notice'>You have silenced all ghosts!</span>")
playsound(get_turf(user), 'sound/effects/ghost.ogg', 50, 1)
message_admins("[key_name_admin(usr)] silenced all ghosts as a wizard! (Deadchat is now DISABLED)")
return TRUE
/datum/spellbook_entry/summon/guns
name = "Summon Guns"
desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first."
+2 -2
View File
@@ -80,7 +80,7 @@
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
id = /obj/item/card/id/security
l_pocket = /obj/item/flash
suit_store = /obj/item/gun/energy/gun/advtaser
suit_store = /obj/item/gun/energy/disabler
pda = /obj/item/pda/warden
backpack_contents = list(
/obj/item/restraints/handcuffs = 1
@@ -180,7 +180,7 @@
l_ear = /obj/item/radio/headset/headset_sec/alt
id = /obj/item/card/id/security
l_pocket = /obj/item/flash
suit_store = /obj/item/gun/energy/gun/advtaser
suit_store = /obj/item/gun/energy/disabler
pda = /obj/item/pda/security
backpack_contents = list(
/obj/item/restraints/handcuffs = 1
+6 -4
View File
@@ -194,16 +194,18 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
/datum/job/judge
title = "Magistrate"
flag = JOB_JUDGE
department_flag = JOBCAT_KARMA
department_flag = JOBCAT_ENGSEC
total_positions = 1
spawn_positions = 1
supervisors = "the Nanotrasen Supreme Court"
department_head = list("Captain")
selection_color = "#ddddff"
req_admin_notify = 1
is_legal = 1
req_admin_notify = TRUE
is_legal = TRUE
transfer_allowed = FALSE
minimal_player_age = 30
exp_requirements = 6000 // 100 hours baby
exp_type = EXP_TYPE_SECURITY
access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS,
ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS,
ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE,
@@ -261,7 +263,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
uniform = /obj/item/clothing/under/rank/internalaffairs
suit = /obj/item/clothing/suit/storage/internalaffairs
shoes = /obj/item/clothing/shoes/brown
l_ear = /obj/item/radio/headset/headset_iaa
l_ear = /obj/item/radio/headset/headset_iaa/alt
glasses = /obj/item/clothing/glasses/hud/security/sunglasses/read_only
id = /obj/item/card/id/security
l_pocket = /obj/item/laser_pointer
+1 -2
View File
@@ -96,8 +96,7 @@ GLOBAL_LIST_INIT(whitelisted_positions, list(
"Blueshield",
"Nanotrasen Representative",
"Barber",
"Brig Physician",
"Magistrate"
"Brig Physician"
))
@@ -420,7 +420,7 @@
item_state = "claymorered"
/obj/item/holo/esword
name = "Holographic Energy Sword"
name = "holographic energy sword"
desc = "This looks like a real energy sword!"
icon_state = "sword0"
hitsound = "swing_hit"
+11
View File
@@ -1287,6 +1287,17 @@ About the new airlock wires panel:
if(duration > electrified_until)
electrify(duration)
/obj/machinery/door/airlock/ex_act(severity)
if(resistance_flags & INDESTRUCTIBLE)
return
switch(severity)
if(EXPLODE_DEVASTATE) //Destroy the airlock completely.
qdel(src)
if(EXPLODE_HEAVY) //Deconstruct the airlock, leaving damaged airlock frame and parts behind
deconstruct(FALSE, null)
if(EXPLODE_LIGHT) //Deals 150 damage to the airlock, half a standard airlock's integrity
take_damage(150)
/obj/machinery/door/airlock/attack_alien(mob/living/carbon/alien/humanoid/user)
add_fingerprint(user)
if(isElectrified())
+3 -2
View File
@@ -206,8 +206,9 @@
continue
if(C.opened && !C.close())
continue
C.locked = 1
C.icon_state = C.icon_locked
C.locked = TRUE
C.close()
C.update_icon()
for(var/obj/machinery/treadmill_monitor/T in targets)
T.total_joules = 0
+17 -5
View File
@@ -149,6 +149,23 @@
B.door_opened(src)
else
do_animate("deny")
if(HAS_TRAIT(user, TRAIT_FORCE_DOORS))
if(user.mind?.vampire && HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
if(!user.mind.vampire.bloodusable)
REMOVE_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT)
return
if(welded)
to_chat(user, "<span class='warning'>The door is welded.</span>")
return
if(locked)
to_chat(user, "<span class='warning'>The door is bolted.</span>")
return
if(density)
visible_message("<span class='danger'>[user] forces the door open!</span>")
playsound(loc, "sparks", 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
open(TRUE)
if(user.mind?.vampire && HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
user.mind.vampire.bloodusable = max(user.mind.vampire.bloodusable - 5, 0)
/obj/machinery/door/attack_ai(mob/user)
return attack_hand(user)
@@ -378,11 +395,6 @@
if(!stat) //Opens only powered doors.
open() //Open everything!
/obj/machinery/door/ex_act(severity)
//if it blows up a wall it should blow up a door
..(severity ? max(1, severity - 1) : 0)
/obj/machinery/door/GetExplosionBlock()
return density ? real_explosion_block : 0
+2 -2
View File
@@ -720,7 +720,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
return A
/obj/machinery/porta_turret/centcom
name = "Centcom Turret"
name = "\improper Centcomm turret"
enabled = FALSE
ailock = TRUE
check_synth = FALSE
@@ -732,7 +732,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
region_max = REGION_CENTCOMM // Non-turretcontrolled turrets at CC can have their access customized to check for CC accesses.
/obj/machinery/porta_turret/centcom/pulse
name = "Pulse Turret"
name = "pulse turret"
health = 200
enabled = TRUE
lethal = TRUE
+3
View File
@@ -168,6 +168,9 @@
use_power = IDLE_POWER_USE
return
/obj/machinery/recharge_station/force_eject_occupant(mob/target)
go_out()
/obj/machinery/recharge_station/verb/move_eject()
set category = "Object"
set src in oview(1)
+11
View File
@@ -89,6 +89,15 @@
/obj/machinery/suit_storage_unit/ce/secure
secure = TRUE
/obj/machinery/suit_storage_unit/rd
name = "research director's suit storage unit"
suit_type = /obj/item/clothing/suit/space/hardsuit/rd
mask_type = /obj/item/clothing/mask/gas
req_access = list(ACCESS_RD)
/obj/machinery/suit_storage_unit/rd/secure
secure = TRUE
/obj/machinery/suit_storage_unit/security
name = "security suit storage unit"
suit_type = /obj/item/clothing/suit/space/hardsuit/security
@@ -313,6 +322,8 @@
if(shock(user, 100))
return
if(!is_operational())
if(user.a_intent != INTENT_HELP)
return ..()
if(panel_open)
to_chat(usr, "<span class='warning'>Close the maintenance panel first.</span>")
else
+1 -1
View File
@@ -440,7 +440,7 @@
range_flame = 2
/obj/item/bombcore/emp
name = "EMP bomb core"
name = "\improper EMP bomb core"
var/light_emp = 36
var/heavy_emp = 18
var/pulse_number = 1 //Since one EMP wont destroy anything other then consoles and IPCS, here is an option to have multiple pulses when dentonating. DO NOT USE THIS WITH REALLY LARGE AREAS
+15 -14
View File
@@ -1804,30 +1804,31 @@
icon_state = "secdrobe"
ads_list = list("Beat perps in style!", "It's red so you can't see the blood!", "You have the right to be fashionable!", "Now you can be the fashion police you always wanted to be!")
vend_reply = "Thank you for using the SecDrobe!"
products = list(/obj/item/clothing/under/rank/security = 4,
products = list(/obj/item/clothing/under/rank/security/corp = 4,
/obj/item/clothing/under/rank/dispatch = 4,
/obj/item/clothing/under/rank/security/skirt = 4,
/obj/item/clothing/under/rank/security = 4,
/obj/item/clothing/under/rank/security2 = 4,
/obj/item/clothing/under/rank/security/formal = 4,
/obj/item/clothing/under/rank/security/skirt = 4,
/obj/item/clothing/under/rank/security/corp = 4,
/obj/item/clothing/under/rank/dispatch = 4,
/obj/item/clothing/head/soft/sec/corp = 4,
/obj/item/clothing/head/officer = 4,
/obj/item/clothing/head/beret/sec = 4,
/obj/item/clothing/head/soft/sec = 4,
/obj/item/clothing/head/soft/sec/corp = 4,
/obj/item/clothing/suit/armor/secjacket = 4,
/obj/item/clothing/suit/jacket/secbomber = 2,
/obj/item/clothing/suit/armor/secjacket = 4,
/obj/item/clothing/suit/hooded/wintercoat/security = 4,
/obj/item/clothing/gloves/color/black = 4,
/obj/item/clothing/accessory/armband/sec = 6,
/obj/item/clothing/shoes/laceup = 4,
/obj/item/clothing/shoes/jackboots = 4,
/obj/item/clothing/shoes/jackboots/jacksandals = 4,
/obj/item/clothing/shoes/laceup = 4,
/obj/item/storage/backpack/duffel/security = 2,
/obj/item/storage/backpack/security = 2,
/obj/item/storage/backpack/satchel_sec = 2,
/obj/item/storage/backpack/duffel/security = 2)
premium = list(/obj/item/clothing/mask/gas/sechailer/swat = 2,
/obj/item/clothing/mask/balaclava = 1)
contraband = list(/obj/item/toy/figure/crew/secofficer = 1,
/obj/item/toy/figure/crew/hos = 1)
/obj/item/clothing/gloves/color/black = 4,
/obj/item/clothing/accessory/armband/sec = 6)
premium = list(/obj/item/clothing/mask/balaclava = 1,
/obj/item/clothing/mask/gas/sechailer/swat = 2)
contraband = list(/obj/item/toy/figure/crew/hos = 1,
/obj/item/toy/figure/crew/secofficer = 1)
refill_canister = /obj/item/vending_refill/secdrobe
/obj/machinery/vending/detdrobe
+1 -1
View File
@@ -28,7 +28,7 @@
/obj/mecha/combat/gygax/loaded/New()
..()
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/taser
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/disabler
ME.attach(src)
ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang
ME.attach(src)
+8 -53
View File
@@ -1,4 +1,4 @@
// Teleporter, Wormhole generator, Gravitational catapult, Armor booster modules,
// Teleporter, Gravitational catapult, Armor booster modules,
// Repair droid, Tesla Energy relay, Generators
////////////////////////////////////////////// TELEPORTER ///////////////////////////////////////////////
@@ -29,53 +29,6 @@
energy_drain = 1000
tele_precision = 1
////////////////////////////////////////////// WORMHOLE GENERATOR //////////////////////////////////////////
/obj/item/mecha_parts/mecha_equipment/wormhole_generator
name = "mounted wormhole generator"
desc = "An exosuit module that allows generating of small quasi-stable wormholes."
icon_state = "mecha_wholegen"
origin_tech = "bluespace=4;magnets=4;plasmatech=2"
equip_cooldown = 50
energy_drain = 300
range = MECHA_RANGED
/obj/item/mecha_parts/mecha_equipment/wormhole_generator/action(atom/target)
if(!action_checks(target) || !is_teleport_allowed(loc.z))
return
var/list/theareas = get_areas_in_range(100, chassis)
if(!theareas.len)
return
var/area/thearea = pick(theareas)
var/list/L = list()
var/turf/pos = get_turf(src)
for(var/turf/T in get_area_turfs(thearea.type))
if(!T.density && pos.z == T.z)
var/clear = 1
for(var/obj/O in T)
if(O.density)
clear = 0
break
if(clear)
L+=T
if(!L.len)
return
var/turf/target_turf = pick(L)
if(!target_turf)
return
var/obj/effect/portal/P = new /obj/effect/portal(get_turf(target), target_turf)
P.icon = 'icons/obj/objects.dmi'
P.failchance = 0
P.icon_state = "anom"
P.name = "wormhole"
message_admins("[key_name_admin(chassis.occupant, chassis.occupant.client)]([ADMIN_QUE(chassis.occupant,"?")]) ([ADMIN_FLW(chassis.occupant,"FLW")]) used a Wormhole Generator in ([loc.x],[loc.y],[loc.z] - <A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[loc.x];Y=[loc.y];Z=[loc.z]'>JMP</a>)",0,1)
log_game("[key_name(chassis.occupant)] used a Wormhole Generator in ([loc.x],[loc.y],[loc.z])")
src = null
spawn(rand(150,300))
qdel(P)
return 1
/////////////////////////////////////// GRAVITATIONAL CATAPULT ///////////////////////////////////////////
/obj/item/mecha_parts/mecha_equipment/gravcatapult
@@ -243,18 +196,20 @@
set_ready_state(1)
return
var/h_boost = health_boost
var/repaired = 0
var/repaired = FALSE
if(chassis.internal_damage & MECHA_INT_SHORT_CIRCUIT)
h_boost *= -2
h_boost = 0
chassis.take_damage(2, BURN) //short circuiting droids do damage
repaired = TRUE
else if(chassis.internal_damage && prob(15))
for(var/int_dam_flag in repairable_damage)
if(chassis.internal_damage & int_dam_flag)
chassis.clearInternalDamage(int_dam_flag)
repaired = 1
repaired = TRUE
break
if(h_boost<0 || chassis.obj_integrity < chassis.max_integrity)
if(chassis.obj_integrity < chassis.max_integrity && h_boost > 0)
chassis.obj_integrity += min(h_boost, chassis.max_integrity-chassis.obj_integrity)
repaired = 1
repaired = TRUE
if(repaired)
if(!chassis.use_power(energy_drain))
STOP_PROCESSING(SSobj, src)
+7 -5
View File
@@ -77,12 +77,14 @@
fire_sound = 'sound/weapons/laser.ogg'
harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/disabler
name = "\improper CH-PD Disabler"
origin_tech = "combat=3"
/obj/item/mecha_parts/mecha_equipment/weapon/energy/disabler
equip_cooldown = 8
name = "\improper CH-DS \"Peacemaker\" disabler"
desc = "A weapon for combat exosuits. Shoots basic disablers."
icon_state = "mecha_disabler"
energy_drain = 30
projectile = /obj/item/projectile/beam/disabler
projectiles_per_shot = 2
projectile_delay = 1
fire_sound = 'sound/weapons/taser2.ogg'
harmful = FALSE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy

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