diff --git a/README.md b/README.md
index af85b6acb99..158d7cf8414 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,7 @@ Space Station 13 is a paranoia-laden round-based roleplaying game set against th
You are responsible for the testing of your content. You should not mark a pull request ready for review until you have actually tested it. If you require a separate client for testing, you can use a guest account by logging out of BYOND and connecting to your test server.
## DOWNLOADING
+
[Downloading](.github/guides/DOWNLOADING.md)
[Running on the server](.github/guides/RUNNING_A_SERVER.md)
diff --git a/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm b/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm
index 1ada129b23f..99ba6f32bc0 100644
--- a/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm
+++ b/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm
@@ -8195,11 +8195,11 @@
/turf/open/floor/iron/cafeteria,
/area/ruin/space/has_grav/skyrat/interdynefob/service/diner)
"MB" = (
-/obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown{
+/obj/item/clothing/suit/toggle/labcoat/hospitalgown{
pixel_x = -3;
pixel_y = 4
},
-/obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown{
+/obj/item/clothing/suit/toggle/labcoat/hospitalgown{
pixel_x = -7
},
/obj/structure/table/glass,
diff --git a/_maps/map_files/NSVBlueshift/Blueshift.dmm b/_maps/map_files/NSVBlueshift/Blueshift.dmm
index 178b97cfeb7..0ddf09e3f56 100644
--- a/_maps/map_files/NSVBlueshift/Blueshift.dmm
+++ b/_maps/map_files/NSVBlueshift/Blueshift.dmm
@@ -8964,7 +8964,12 @@
/obj/machinery/computer/security/qm{
dir = 1
},
-/obj/machinery/requests_console/directional/south,
+/obj/machinery/requests_console/directional/south{
+ department = "Quartermaster's Office";
+ name = "Quartermaster's Requests Console"
+ },
+/obj/effect/mapping_helpers/requests_console/announcement,
+/obj/effect/mapping_helpers/requests_console/supplies,
/turf/open/floor/iron/dark,
/area/station/command/heads_quarters/qm)
"bLI" = (
@@ -12637,6 +12642,15 @@
/obj/item/seeds/nettle,
/turf/open/floor/plating,
/area/station/maintenance/starboard/fore)
+"ctT" = (
+/obj/structure/disposaloutlet{
+ dir = 4
+ },
+/obj/structure/disposalpipe/trunk{
+ dir = 8
+ },
+/turf/open/floor/iron/shuttle/evac/airless,
+/area/space/nearstation)
"cua" = (
/obj/structure/table/wood,
/obj/item/flashlight/lamp,
@@ -13534,6 +13548,9 @@
/obj/effect/turf_decal/bot,
/obj/structure/cable,
/obj/effect/turf_decal/tile/green/fourcorners,
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
/turf/open/floor/iron/white,
/area/station/medical/virology)
"cBq" = (
@@ -16685,7 +16702,7 @@
dir = 1
},
/turf/open/floor/iron/shuttle/evac/airless,
-/area/station/medical/virology)
+/area/space/nearstation)
"dfV" = (
/obj/item/kirbyplants/random,
/obj/effect/turf_decal/tile/neutral{
@@ -21809,13 +21826,11 @@
/area/station/maintenance/department/science/central)
"egB" = (
/obj/structure/cable,
-/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
-/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/machinery/duct,
+/obj/effect/spawner/structure/window/reinforced,
/obj/structure/disposalpipe/segment{
- dir = 6
+ dir = 4
},
-/turf/open/floor/iron/white,
+/turf/open/floor/plating,
/area/station/medical/virology)
"egE" = (
/obj/structure/cable,
@@ -22696,7 +22711,7 @@
/obj/effect/turf_decal/stripes{
dir = 10
},
-/obj/machinery/modular_computer/preset/civilian{
+/obj/machinery/computer/rdconsole{
dir = 4
},
/turf/open/floor/iron,
@@ -27348,6 +27363,9 @@
/obj/item/clothing/glasses/science,
/obj/machinery/light_switch/directional/south,
/obj/structure/cable,
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
/turf/open/floor/iron,
/area/station/medical/virology)
"fgo" = (
@@ -28032,6 +28050,9 @@
},
/obj/item/storage/box/syringes,
/obj/structure/cable,
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
/turf/open/floor/iron,
/area/station/medical/virology)
"fnv" = (
@@ -33825,7 +33846,6 @@
/area/station/ai_monitored/command/storage/eva/upper)
"gux" = (
/obj/structure/table/wood,
-/obj/item/canvas/drawingtablet,
/turf/open/floor/iron/dark,
/area/station/service/library)
"guy" = (
@@ -38400,7 +38420,6 @@
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/structure/disposalpipe/segment,
/obj/effect/mapping_helpers/airlock/access/all/medical/virology,
/obj/machinery/duct,
/obj/effect/turf_decal/tile/green/opposingcorners,
@@ -60702,10 +60721,13 @@
/area/station/security/office)
"lCT" = (
/obj/machinery/light/directional/north,
-/obj/machinery/requests_console/directional/north,
/obj/structure/chair/office{
dir = 4
},
+/obj/machinery/requests_console/directional/north{
+ department = "Ordinance";
+ name = "Ordinance Requests Console"
+ },
/turf/open/floor/iron,
/area/station/science/ordnance)
"lCV" = (
@@ -61563,7 +61585,6 @@
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/structure/disposalpipe/segment,
/obj/effect/mapping_helpers/airlock/access/all/medical/virology,
/obj/machinery/duct,
/obj/effect/turf_decal/tile/green/opposingcorners,
@@ -67117,7 +67138,6 @@
/obj/effect/spawner/structure/window/reinforced,
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/structure/disposalpipe/segment,
/turf/open/floor/plating,
/area/station/medical/virology)
"mPH" = (
@@ -95938,7 +95958,6 @@
/obj/machinery/light/small/directional/west,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/structure/disposalpipe/segment,
/obj/machinery/duct,
/obj/effect/turf_decal/tile/green/opposingcorners,
/turf/open/floor/iron/white,
@@ -104025,9 +104044,6 @@
/obj/effect/turf_decal/trimline/green/filled/line{
dir = 4
},
-/obj/structure/disposalpipe/trunk{
- dir = 8
- },
/obj/machinery/disposal/bin{
desc = "A pneumatic waste disposal unit. This one leads into space!";
name = "deathsposal unit"
@@ -104035,6 +104051,9 @@
/obj/effect/turf_decal/delivery,
/obj/structure/cable,
/obj/machinery/airalarm/directional/south,
+/obj/structure/disposalpipe/trunk{
+ dir = 4
+ },
/turf/open/floor/iron/white,
/area/station/medical/virology)
"tOJ" = (
@@ -111616,7 +111635,6 @@
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/structure/disposalpipe/segment,
/obj/machinery/duct,
/obj/effect/turf_decal/tile/green/opposingcorners{
dir = 1
@@ -114301,8 +114319,11 @@
/obj/item/book/manual/wiki/robotics_cyborgs{
pixel_x = -7
},
-/obj/machinery/requests_console/directional/south,
/obj/item/radio/intercom/directional/east,
+/obj/machinery/requests_console/directional/south{
+ department = "Robotics";
+ name = "Robotics Requests Console"
+ },
/turf/open/floor/iron/dark,
/area/station/science/robotics)
"vKR" = (
@@ -115828,13 +115849,7 @@
/turf/open/floor/wood,
/area/station/service/hydroponics/garden/abandoned)
"waY" = (
-/obj/structure/disposaloutlet{
- dir = 4
- },
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/structure/disposalpipe/trunk{
- dir = 1
- },
/turf/open/floor/iron/shuttle/evac/airless,
/area/space/nearstation)
"waZ" = (
@@ -238323,7 +238338,7 @@ cEu
sxF
blr
kIp
-egB
+cEu
lLg
vlQ
soi
@@ -239608,7 +239623,7 @@ eSa
qSQ
hQZ
qSQ
-qSQ
+egB
qSQ
aVL
aVL
@@ -239865,7 +239880,7 @@ nvJ
nvJ
nvJ
nvJ
-soz
+ctT
soz
soz
soz
@@ -241714,7 +241729,7 @@ ulx
ulx
ulx
ulx
-fZI
+ulx
ulx
ulx
ulx
@@ -241971,7 +241986,7 @@ ulx
ulx
ulx
ulx
-fZI
+ulx
ulx
ulx
ulx
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 06813a00520..faab92aee8c 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -333,6 +333,7 @@ GLOBAL_LIST_INIT(human_invader_antagonists, list(
#define ANTAG_GROUP_FUGITIVES "Escaped Fugitives"
#define ANTAG_GROUP_HUNTERS "Bounty Hunters"
#define ANTAG_GROUP_PARADOX "Spacetime Aberrations"
+#define ANTAG_GROUP_CREW "Deviant Crew"
// This flag disables certain checks that presume antagonist datums mean 'baddie'.
diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm
index 88aafd93dd5..6a28332c529 100644
--- a/code/__DEFINES/atom_hud.dm
+++ b/code/__DEFINES/atom_hud.dm
@@ -95,6 +95,7 @@
#define SECHUD_BARTENDER "hudbartender"
#define SECHUD_BITRUNNER "hudbitrunner"
#define SECHUD_BOTANIST "hudbotanist"
+#define SECHUD_BRIDGE_ASSISTANT "hudbridgeassistant"
#define SECHUD_CAPTAIN "hudcaptain"
#define SECHUD_CARGO_TECHNICIAN "hudcargotechnician"
#define SECHUD_CHAPLAIN "hudchaplain"
diff --git a/code/__DEFINES/dcs/signals/signals_circuit.dm b/code/__DEFINES/dcs/signals/signals_circuit.dm
index 1b7b6b53c4f..c0676d768ce 100644
--- a/code/__DEFINES/dcs/signals/signals_circuit.dm
+++ b/code/__DEFINES/dcs/signals/signals_circuit.dm
@@ -75,3 +75,9 @@
///Called when an Ntnet sender is sending Ntnet data
#define COMSIG_GLOB_CIRCUIT_NTNET_DATA_SENT "!circuit_ntnet_data_sent"
+
+/// Called when an equipment action component is added to a shell (/obj/item/circuit_component/equipment_action/action_comp)
+#define COMSIG_CIRCUIT_ACTION_COMPONENT_REGISTERED "circuit_action_component_registered"
+
+/// Called when an equipment action component is removed from a shell (/obj/item/circuit_component/equipment_action/action_comp)
+#define COMSIG_CIRCUIT_ACTION_COMPONENT_UNREGISTERED "circuit_action_component_unregistered"
diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm
index 3c7399d98fa..41c2b4b7459 100644
--- a/code/__DEFINES/dcs/signals/signals_global.dm
+++ b/code/__DEFINES/dcs/signals/signals_global.dm
@@ -84,6 +84,9 @@
/// (new_name, old_name)
#define COMSIG_GLOB_STATION_NAME_CHANGED "!station_name_changed"
+/// Global signal sent before we decide what job everyone has
+#define COMSIG_GLOB_PRE_JOBS_ASSIGNED "!pre_roles_assigned"
+
/// global signal when a global nullrod type is picked
#define COMSIG_GLOB_NULLROD_PICKED "!nullrod_picked"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm
index 8be61d93898..47f5b748599 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm
@@ -2,3 +2,9 @@
#define COMSIG_PROCESS_BORGCHARGER_OCCUPANT "living_charge"
///sent from borg mobs to itself, for tools to catch an upcoming destroy() due to safe decon (rather than detonation)
#define COMSIG_BORG_SAFE_DECONSTRUCT "borg_safe_decon"
+///called from /obj/item/borg/cyborghug/attack proc
+#define COMSIG_BORG_HUG_MOB "borg_hug_mob"
+ ///returned if this action was handled by signal handler.
+ #define COMSIG_BORG_HUG_HANDLED 1
+///called from /mob/living/silicon/attack_hand proc
+#define COMSIG_MOB_PAT_BORG "mob_pat_borg"
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 7feca0782dc..0418102fe41 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -48,6 +48,7 @@
#define JOB_RESEARCH_DIRECTOR "Research Director"
#define JOB_CHIEF_ENGINEER "Chief Engineer"
#define JOB_CHIEF_MEDICAL_OFFICER "Chief Medical Officer"
+#define JOB_BRIDGE_ASSISTANT "Bridge Assistant"
//Silicon
#define JOB_AI "AI"
#define JOB_CYBORG "Cyborg"
@@ -80,6 +81,7 @@
//Supply
#define JOB_QUARTERMASTER "Quartermaster"
#define JOB_CARGO_TECHNICIAN "Cargo Technician"
+#define JOB_CARGO_GORILLA "Cargo Gorilla"
#define JOB_SHAFT_MINER "Shaft Miner"
#define JOB_BITRUNNER "Bitrunner"
#define JOB_CUSTOMS_AGENT "Customs Agent" // SKYRAT EDIT ADDITION
@@ -145,50 +147,52 @@
#define JOB_DISPLAY_ORDER_ASSISTANT 1
#define JOB_DISPLAY_ORDER_CAPTAIN 2
#define JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL 3
-#define JOB_DISPLAY_ORDER_BARTENDER 4
-#define JOB_DISPLAY_ORDER_BOTANIST 5
-#define JOB_DISPLAY_ORDER_COOK 6
-#define JOB_DISPLAY_ORDER_JANITOR 7
-#define JOB_DISPLAY_ORDER_CLOWN 8
-#define JOB_DISPLAY_ORDER_MIME 9
-#define JOB_DISPLAY_ORDER_CURATOR 10
-#define JOB_DISPLAY_ORDER_LAWYER 11
-#define JOB_DISPLAY_ORDER_CHAPLAIN 12
-#define JOB_DISPLAY_ORDER_PSYCHOLOGIST 13
-#define JOB_DISPLAY_ORDER_AI 14
-#define JOB_DISPLAY_ORDER_CYBORG 15
-#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 16
-#define JOB_DISPLAY_ORDER_STATION_ENGINEER 17
-#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 18
-#define JOB_DISPLAY_ORDER_QUARTERMASTER 19
-#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 20
-#define JOB_DISPLAY_ORDER_SHAFT_MINER 21
-#define JOB_DISPLAY_ORDER_BITRUNNER 22
-#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 23
-#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 24
-#define JOB_DISPLAY_ORDER_PARAMEDIC 25
-#define JOB_DISPLAY_ORDER_CHEMIST 26
-#define JOB_DISPLAY_ORDER_VIROLOGIST 27
-#define JOB_DISPLAY_ORDER_CORONER 28
-#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 29
-#define JOB_DISPLAY_ORDER_SCIENTIST 30
-#define JOB_DISPLAY_ORDER_ROBOTICIST 31
-#define JOB_DISPLAY_ORDER_GENETICIST 32
-#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 33
-#define JOB_DISPLAY_ORDER_WARDEN 34
-#define JOB_DISPLAY_ORDER_DETECTIVE 35
-#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 36
-#define JOB_DISPLAY_ORDER_PRISONER 37
-#define JOB_DISPLAY_ORDER_SECURITY_MEDIC 38 //SKYRAT EDIT ADDITON
-#define JOB_DISPLAY_ORDER_CORRECTIONS_OFFICER 39 //SKYRAT EDIT ADDITON
-#define JOB_DISPLAY_ORDER_NANOTRASEN_CONSULTANT 40 //SKYRAT EDIT ADDITON
-#define JOB_DISPLAY_ORDER_BLUESHIELD 41 //SKYRAT EDIT ADDITON
-#define JOB_DISPLAY_ORDER_ORDERLY 42 //SKYRAT EDIT ADDITION
-#define JOB_DISPLAY_ORDER_SCIENCE_GUARD 43 //SKYRAT EDIT ADDITION
-#define JOB_DISPLAY_ORDER_BOUNCER 44 //SKYRAT EDIT ADDITION
-#define JOB_DISPLAY_ORDER_ENGINEER_GUARD 45 //SKYRAT EDIT ADDITION
-#define JOB_DISPLAY_ORDER_CUSTOMS_AGENT 46 //SKYRAT EDIT ADDITION
-#define JOB_DISPLAY_ORDER_EXP_CORPS 47 //SKYRAT EDIT ADDITON
+#define JOB_DISPLAY_ORDER_BRIDGE_ASSISTANT 4
+#define JOB_DISPLAY_ORDER_BARTENDER 5
+#define JOB_DISPLAY_ORDER_BOTANIST 6
+#define JOB_DISPLAY_ORDER_COOK 7
+#define JOB_DISPLAY_ORDER_JANITOR 8
+#define JOB_DISPLAY_ORDER_CLOWN 9
+#define JOB_DISPLAY_ORDER_MIME 10
+#define JOB_DISPLAY_ORDER_CURATOR 11
+#define JOB_DISPLAY_ORDER_LAWYER 12
+#define JOB_DISPLAY_ORDER_CHAPLAIN 13
+#define JOB_DISPLAY_ORDER_PSYCHOLOGIST 14
+#define JOB_DISPLAY_ORDER_AI 15
+#define JOB_DISPLAY_ORDER_CYBORG 16
+#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 17
+#define JOB_DISPLAY_ORDER_STATION_ENGINEER 18
+#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 19
+#define JOB_DISPLAY_ORDER_QUARTERMASTER 20
+#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 21
+#define JOB_DISPLAY_ORDER_SHAFT_MINER 22
+#define JOB_DISPLAY_ORDER_BITRUNNER 23
+#define JOB_DISPLAY_ORDER_CARGO_GORILLA 24
+#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 25
+#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 26
+#define JOB_DISPLAY_ORDER_PARAMEDIC 27
+#define JOB_DISPLAY_ORDER_CHEMIST 28
+#define JOB_DISPLAY_ORDER_VIROLOGIST 29
+#define JOB_DISPLAY_ORDER_CORONER 30
+#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 31
+#define JOB_DISPLAY_ORDER_SCIENTIST 32
+#define JOB_DISPLAY_ORDER_ROBOTICIST 33
+#define JOB_DISPLAY_ORDER_GENETICIST 34
+#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 35
+#define JOB_DISPLAY_ORDER_WARDEN 36
+#define JOB_DISPLAY_ORDER_DETECTIVE 37
+#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 38
+#define JOB_DISPLAY_ORDER_PRISONER 39
+#define JOB_DISPLAY_ORDER_SECURITY_MEDIC 100 //SKYRAT EDIT ADDITON
+#define JOB_DISPLAY_ORDER_CORRECTIONS_OFFICER 101 //SKYRAT EDIT ADDITON
+#define JOB_DISPLAY_ORDER_NANOTRASEN_CONSULTANT 102 //SKYRAT EDIT ADDITON
+#define JOB_DISPLAY_ORDER_BLUESHIELD 103 //SKYRAT EDIT ADDITON
+#define JOB_DISPLAY_ORDER_ORDERLY 104 //SKYRAT EDIT ADDITION
+#define JOB_DISPLAY_ORDER_SCIENCE_GUARD 105 //SKYRAT EDIT ADDITION
+#define JOB_DISPLAY_ORDER_BOUNCER 106 //SKYRAT EDIT ADDITION
+#define JOB_DISPLAY_ORDER_ENGINEER_GUARD 107 //SKYRAT EDIT ADDITION
+#define JOB_DISPLAY_ORDER_CUSTOMS_AGENT 108 //SKYRAT EDIT ADDITION
+#define JOB_DISPLAY_ORDER_EXP_CORPS 109 //SKYRAT EDIT ADDITON
#define DEPARTMENT_UNASSIGNED "No Department"
@@ -236,9 +240,19 @@
#define JOB_CAN_BE_INTERN (1<<8)
/// This job cannot have more slots opened by the Head of Personnel (but admins or other random events can still do this).
#define JOB_CANNOT_OPEN_SLOTS (1<<9)
+/// This job will not display on the job menu when there are no slots available, instead of appearing greyed out
+#define JOB_HIDE_WHEN_EMPTY (1<<10)
+/// This job cannot be signed up for at round start or recorded in your preferences
+#define JOB_LATEJOIN_ONLY (1<<11)
+/// This job is a head of staff.
+#define JOB_HEAD_OF_STAFF (1<<12)
/// Combination flag for jobs which are considered regular crew members of the station.
#define STATION_JOB_FLAGS (JOB_ANNOUNCE_ARRIVAL|JOB_CREW_MANIFEST|JOB_EQUIP_RANK|JOB_CREW_MEMBER|JOB_NEW_PLAYER_JOINABLE|JOB_REOPEN_ON_ROUNDSTART_LOSS|JOB_ASSIGN_QUIRKS|JOB_CAN_BE_INTERN)
+/// Combination flag for jobs which are considered heads of staff.
+#define HEAD_OF_STAFF_JOB_FLAGS (JOB_BOLD_SELECT_TEXT|JOB_CANNOT_OPEN_SLOTS|JOB_HEAD_OF_STAFF)
+/// Combination flag for jobs which are enabled by station traits.
+#define STATION_TRAIT_JOB_FLAGS (JOB_CANNOT_OPEN_SLOTS|JOB_HIDE_WHEN_EMPTY|JOB_LATEJOIN_ONLY&~JOB_REOPEN_ON_ROUNDSTART_LOSS)
#define FACTION_NONE "None"
#define FACTION_STATION "Station"
@@ -251,3 +265,6 @@
#define SUPERVISOR_HOS "the Head of Security"
#define SUPERVISOR_QM "the Quartermaster"
#define SUPERVISOR_RD "the Research Director"
+
+/// Mind traits that should be shared by every head of staff. has to be this way cause byond lists lol
+#define HEAD_OF_STAFF_MIND_TRAITS TRAIT_FAST_TYING, TRAIT_HIGH_VALUE_RANSOM
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 0cdad4f7241..1ba6879005b 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -2,35 +2,31 @@
//KEEP THESE IN A NICE ACSCENDING ORDER, PLEASE
//NEVER HAVE ANYTHING BELOW THIS PLANE ADJUST IF YOU NEED MORE SPACE
-#define LOWEST_EVER_PLANE -100
+#define LOWEST_EVER_PLANE -50
-#define FIELD_OF_VISION_BLOCKER_PLANE -90
+// Doesn't really layer, just throwing this in here cause it's the best place imo
+#define FIELD_OF_VISION_BLOCKER_PLANE -45
#define FIELD_OF_VISION_BLOCKER_RENDER_TARGET "*FIELD_OF_VISION_BLOCKER_RENDER_TARGET"
-#define CLICKCATCHER_PLANE -80
+#define CLICKCATCHER_PLANE -40
-#define PLANE_SPACE -25
+#define PLANE_SPACE -21
#define PLANE_SPACE_PARALLAX -20
-#define GRAVITY_PULSE_PLANE -13
+#define GRAVITY_PULSE_PLANE -12
#define GRAVITY_PULSE_RENDER_TARGET "*GRAVPULSE_RENDER_TARGET"
-#define RENDER_PLANE_TRANSPARENT -12 //Transparent plane that shows openspace underneath the floor
+#define RENDER_PLANE_TRANSPARENT -11 //Transparent plane that shows openspace underneath the floor
-#define TRANSPARENT_FLOOR_PLANE -11
+#define TRANSPARENT_FLOOR_PLANE -10
-#define FLOOR_PLANE -10
-
-#define WALL_PLANE -9
-#define GAME_PLANE -8
-#define GAME_PLANE_FOV_HIDDEN -7
-#define GAME_PLANE_UPPER -6
-#define WALL_PLANE_UPPER -5
-#define GAME_PLANE_UPPER_FOV_HIDDEN -4
+#define FLOOR_PLANE -6
+#define WALL_PLANE -5
+#define GAME_PLANE -4
+#define ABOVE_GAME_PLANE -3
///Slightly above the game plane but does not catch mouse clicks. Useful for certain visuals that should be clicked through, like seethrough trees
-#define SEETHROUGH_PLANE -3
-#define ABOVE_GAME_PLANE -2
+#define SEETHROUGH_PLANE -2
#define RENDER_PLANE_GAME_WORLD -1
@@ -91,31 +87,36 @@
//-------------------- HUD ---------------------
//HUD layer defines
-#define HUD_PLANE 40
-#define ABOVE_HUD_PLANE 41
+#define HUD_PLANE 35
+#define ABOVE_HUD_PLANE 36
///Plane of the "splash" icon used that shows on the lobby screen
-#define SPLASHSCREEN_PLANE 50
+#define SPLASHSCREEN_PLANE 37
// The largest plane here must still be less than RENDER_PLANE_GAME
//-------------------- Rendering ---------------------
-#define RENDER_PLANE_GAME 100
-#define RENDER_PLANE_NON_GAME 101
+#define RENDER_PLANE_GAME 40
+/// If fov is enabled we'll draw game to this and do shit to it
+#define RENDER_PLANE_GAME_MASKED 41
+/// The bit of the game plane that is let alone is sent here
+#define RENDER_PLANE_GAME_UNMASKED 42
+#define RENDER_PLANE_NON_GAME 45
// Only VERY special planes should be here, as they are above not just the game, but the UI planes as well.
/// Plane related to the menu when pressing Escape.
/// Needed so that we can apply a blur effect to EVERYTHING, and guarantee we are above all UI.
-#define ESCAPE_MENU_PLANE 105
+#define ESCAPE_MENU_PLANE 46
-#define RENDER_PLANE_MASTER 110
+#define RENDER_PLANE_MASTER 50
// Lummox I swear to god I will find you
// NOTE! You can only ever have planes greater then -10000, if you add too many with large offsets you will brick multiz
// Same can be said for large multiz maps. Tread carefully mappers
#define HIGHEST_EVER_PLANE RENDER_PLANE_MASTER
/// The range unique planes can be in
+/// Try and keep this to a nice whole number, so it's easy to look at a plane var and know what's going on
#define PLANE_RANGE (HIGHEST_EVER_PLANE - LOWEST_EVER_PLANE)
// PLANE_SPACE layer(s)
@@ -188,8 +189,6 @@
#define NOT_HIGH_OBJ_LAYER 3.5
#define HIGH_OBJ_LAYER 3.6
#define BELOW_MOB_LAYER 3.7
-
-// GAME_PLANE_FOV_HIDDEN layers
#define LOW_MOB_LAYER 3.75
#define LYING_MOB_LAYER 3.8
#define VEHICLE_LAYER 3.9
@@ -199,21 +198,16 @@
#define MOB_ABOVE_PIGGYBACK_LAYER 4.06
#define MOB_UPPER_LAYER 4.07
#define HITSCAN_PROJECTILE_LAYER 4.09 //above all mob but still hidden by FoV
-
-// GAME_PLANE_UPPER layers
#define ABOVE_MOB_LAYER 4.1
#define WALL_OBJ_LAYER 4.25
#define TRAM_SIGNAL_LAYER 4.26
-// WALL_PLANE_UPPER layers
#define EDGED_TURF_LAYER 4.3
#define ON_EDGED_TURF_LAYER 4.35
-
-// GAME_PLANE_UPPER_FOV_HIDDEN layers
#define SPACEVINE_LAYER 4.4
#define LARGE_MOB_LAYER 4.5
#define SPACEVINE_MOB_LAYER 4.6
-// Intermediate layer used by both GAME_PLANE_FOV_HIDDEN and ABOVE_GAME_PLANE
+// Intermediate layer used by both GAME_PLANE and ABOVE_GAME_PLANE
#define ABOVE_ALL_MOB_LAYER 4.7
// ABOVE_GAME_PLANE layers
@@ -229,6 +223,8 @@
*/
#define WATER_VISUAL_OVERLAY_LAYER 1000
+// SEETHROUGH_PLANE layers here, tho it has no layer values
+
//---------- LIGHTING -------------
// LIGHTING_PLANE layers
diff --git a/code/__DEFINES/matrices.dm b/code/__DEFINES/matrices.dm
index 1bb86f9ff47..26ff5a7232a 100644
--- a/code/__DEFINES/matrices.dm
+++ b/code/__DEFINES/matrices.dm
@@ -1,3 +1,27 @@
/// Helper macro for creating a matrix at the given offsets.
/// Works at compile time.
#define TRANSLATE_MATRIX(offset_x, offset_y) matrix(1, 0, (offset_x), 0, 1, (offset_y))
+/// The color matrix of an image which colors haven't been altered. Does nothing.
+#define COLOR_MATRIX_IDENTITY list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
+/// Color inversion
+#define COLOR_MATRIX_INVERT list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0)
+///Sepiatone
+#define COLOR_MATRIX_SEPIATONE list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0)
+///Grayscale
+#define COLOR_MATRIX_GRAYSCALE list(0.33,0.33,0.33,0, 0.59,0.59,0.59,0, 0.11,0.11,0.11,0, 0,0,0,1, 0,0,0,0)
+///Polaroid colors
+#define COLOR_MATRIX_POLAROID list(1.438,-0.062,-0.062,0, -0.122,1.378,-0.122,0, -0.016,-0.016,1.483,0, 0,0,0,1, 0,0,0,0)
+/// Converts reds to blue, green to red and blue to green.
+#define COLOR_MATRIX_BRG list(0,0,1,0, 0,1,0,0, 1,0,0,0, 0,0,0,1, 0,0,0,0)
+/// Black & White
+#define COLOR_MATRIX_BLACK_WHITE list(1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 0,0,0,1, -1,-1,-1,0)
+/**
+ * Adds/subtracts overall lightness
+ * 0 is identity, 1 makes everything white, -1 makes everything black
+ */
+#define COLOR_MATRIX_LIGHTNESS(power) list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, power,power,power,0)
+/**
+ * Changes distance colors have from rgb(127,127,127) grey
+ * 1 is identity. 0 makes everything grey >1 blows out colors and greys
+ */
+#define COLOR_MATRIX_CONTRAST(val) list(val,0,0,0, 0,val,0,0, 0,0,val,0, 0,0,0,1, (1-val)*0.5,(1-val)*0.5,(1-val)*0.5,0)
diff --git a/code/__DEFINES/modular_computer.dm b/code/__DEFINES/modular_computer.dm
index 1c9f3b59ecd..5158312d03f 100644
--- a/code/__DEFINES/modular_computer.dm
+++ b/code/__DEFINES/modular_computer.dm
@@ -1,37 +1,76 @@
-//NTNet stuff, for modular computers
-
-//Caps for NTNet logging. Less than 10 would make logging useless anyway, more than 500 may make the log browser too laggy. Defaults to 100 unless user changes it.
-#define MAX_NTNET_LOGS 300
-#define MIN_NTNET_LOGS 10
-
-//Program bitflags
+/**
+ * can_run_on_flags bitflags
+ * Used by programs to tell what type of ModPC it can run on.
+ * Everything a program can run on needs valid icons for each individual one.
+ */
+///Runs on everything.
#define PROGRAM_ALL ALL
+///Can run on Modular PC Consoles
#define PROGRAM_CONSOLE (1<<0)
+///Can run on Laptops.
#define PROGRAM_LAPTOP (1<<1)
-#define PROGRAM_TABLET (1<<2)
-//Program categories
-#define PROGRAM_CATEGORY_CREW "Crew"
-#define PROGRAM_CATEGORY_ENGI "Engineering"
-#define PROGRAM_CATEGORY_SUPL "Supply"
-#define PROGRAM_CATEGORY_SCI "Science"
-#define PROGRAM_CATEGORY_MISC "Other"
+///Can run on PDAs.
+#define PROGRAM_PDA (1<<2)
+/**
+ * program_flags
+ * Used by programs to tell the ModPC any special functions it has.
+ */
+///If the program requires NTNet to be online for it to work.
+#define PROGRAM_REQUIRES_NTNET (1<<0)
+///The program can be downloaded from the default NTNet downloader store.
+#define PROGRAM_ON_NTNET_STORE (1<<1)
+///The program can only be downloaded from the Syndinet store, usually nukie/emagged pda.
+#define PROGRAM_ON_SYNDINET_STORE (1<<2)
+///The program is unique and will delete itself upon being transferred to ensure only one copy exists.
+#define PROGRAM_UNIQUE_COPY (1<<3)
+///The program is a header and will show up at the top of the ModPC's UI.
+#define PROGRAM_HEADER (1<<4)
+///The program will run despite the ModPC not having any power in it.
+#define PROGRAM_RUNS_WITHOUT_POWER (1<<5)
+
+//Program categories
+#define PROGRAM_CATEGORY_DEVICE "Device Tools"
+#define PROGRAM_CATEGORY_EQUIPMENT "Equipment"
+#define PROGRAM_CATEGORY_GAMES "Games"
+#define PROGRAM_CATEGORY_SECURITY "Security & Records"
+#define PROGRAM_CATEGORY_ENGINEERING "Engineering"
+#define PROGRAM_CATEGORY_SUPPLY "Supply"
+#define PROGRAM_CATEGORY_SCIENCE "Science"
+
+///The default amount a program should take in cell use.
+#define PROGRAM_BASIC_CELL_USE 15
+
+///This app grants a minor protection against being PDA bombed if installed.
+///(can sometimes prevent it from being sent, while wasting a PDA bomb from the sender).
#define DETOMATIX_RESIST_MINOR 1
+///This app grants a larger protection against being PDA bombed if installed.
+///(can sometimes prevent it from being sent, while wasting a PDA bomb from the sender).
#define DETOMATIX_RESIST_MAJOR 2
-//NTNet transfer speeds, used when downloading/uploading a file/program.
-#define NTNETSPEED_LOWSIGNAL 0.5 // GQ/s transfer speed when the device is wirelessly connected and on Low signal
-#define NTNETSPEED_HIGHSIGNAL 1 // GQ/s transfer speed when the device is wirelessly connected and on High signal
-#define NTNETSPEED_ETHERNET 2 // GQ/s transfer speed when the device is using wired connection
+/**
+ * NTNet transfer speeds, used when downloading/uploading a file/program.
+ * The define is how fast it will download an app every program's process_tick.
+ */
+///Used for wireless devices with low signal.
+#define NTNETSPEED_LOWSIGNAL 0.5
+///Used for wireless devices with high signal.
+#define NTNETSPEED_HIGHSIGNAL 1
+///Used for laptops with a high signal, or computers, which is connected regardless of z level.
+#define NTNETSPEED_ETHERNET 2
-// NTNet connection signals
-///When you're away from the station/mining base and not on a console, you can't access the internet
+/**
+ * NTNet connection signals
+ * Used to calculate the defines above from NTNet Downloader, this is how
+ * good a ModPC's signal is.
+ */
+///When you're away from the station/mining base and not on a console, you can't access the internet.
#define NTNET_NO_SIGNAL 0
///Low signal, so away from the station, but still connected
#define NTNET_LOW_SIGNAL 1
-///On station, good signal
+///On station with good signal.
#define NTNET_GOOD_SIGNAL 2
-///Using a Computer, ethernet-connected.
+///Using a Computer or Laptop with good signal, ethernet-connected.
#define NTNET_ETHERNET_SIGNAL 3
/// The default ringtone of the Messenger app.
@@ -40,6 +79,11 @@
/// The maximum length of the ringtone of the Messenger app.
#define MESSENGER_RINGTONE_MAX_LENGTH 20
+/**
+ * PDA Themes
+ * For these to work, the defines must be defined in tgui/styles/themes/[define].scss
+ */
+
///Default NtOS PDA theme
#define PDA_THEME_NTOS "ntos"
#define PDA_THEME_DARK_MODE "ntos_darkmode"
diff --git a/code/__DEFINES/organ_movement.dm b/code/__DEFINES/organ_movement.dm
new file mode 100644
index 00000000000..16f003ede81
--- /dev/null
+++ b/code/__DEFINES/organ_movement.dm
@@ -0,0 +1,4 @@
+/// Delete the organ if replaced
+#define DELETE_IF_REPLACED (1<<0)
+/// When deleting a brain, we don't delete the identity and the player can keep playing
+#define NO_ID_TRANSFER (1<<1)
diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm
index 42e6356d087..26b66f0de60 100644
--- a/code/__DEFINES/robots.dm
+++ b/code/__DEFINES/robots.dm
@@ -334,4 +334,4 @@ DEFINE_BITFIELD(janitor_mode_flags, list(
#define MEDIBOT_VOICED_THIS_HURTS "This hurts, my pain is real!"
#define MEDIBOT_VOICED_THE_END "Is this the end?"
#define MEDIBOT_VOICED_NOOO "Nooo!"
-#define MEDIBOT_VOICED_CHICKEN "LOOK AT ME?! i am a chicken."
+#define MEDIBOT_VOICED_CHICKEN "LOOK AT ME?! I am a chicken."
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index cd8bb8f5e20..d4f21c09aa1 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -43,7 +43,6 @@
#define ROLE_WIZARD_MIDROUND "Wizard (Midround)"
//SKYRAT EDIT START
#define ROLE_BORER "Borer"
-#define ROLE_DRIFTING_CONTRACTOR "Drifting Contractor"
#define ROLE_LONE_INFILTRATOR "Lone Infiltrator"
#define ROLE_MUTANT "Mutated Abomination"
#define ROLE_CLOCK_CULTIST "Clock Cultist"
@@ -173,7 +172,6 @@ GLOBAL_LIST_INIT(special_roles, list(
//SKYRAT EDIT START
ROLE_LONE_INFILTRATOR = 0,
ROLE_BORER = 0,
- ROLE_DRIFTING_CONTRACTOR = 14,
ROLE_MUTANT = 0,
//SKYRAT EDIT END
diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm
index cbd29cd4218..a30e980770b 100644
--- a/code/__DEFINES/span.dm
+++ b/code/__DEFINES/span.dm
@@ -13,6 +13,7 @@
#define span_alertwarning(str) ("" + str + "")
#define span_alien(str) ("" + str + "")
#define span_announce(str) ("" + str + "")
+#define span_announcement_header(str) ("" + str + "")
#define span_average(str) ("" + str + "")
@@ -70,6 +71,7 @@
#define span_interface(str) ("" + str + "")
#define span_linkify(str) ("" + str + "")
#define span_looc(str) ("" + str + "")
+#define span_major_announcement_text(str) ("" + str + "")
#define span_medal(str) ("" + str + "")
#define span_medradio(str) ("" + str + "")
#define span_memo(str) ("" + str + "")
diff --git a/code/__DEFINES/station.dm b/code/__DEFINES/station.dm
index ddaa82daffc..63430b52c37 100644
--- a/code/__DEFINES/station.dm
+++ b/code/__DEFINES/station.dm
@@ -2,12 +2,10 @@
#define STATION_TRAIT_NEUTRAL 2
#define STATION_TRAIT_NEGATIVE 3
-/// For traits that shouldn't be selected, like abstract types (wow)
-#define STATION_TRAIT_ABSTRACT (1<<0)
/// Only run on planet stations
-#define STATION_TRAIT_PLANETARY (1<<1)
+#define STATION_TRAIT_PLANETARY (1<<0)
/// Only run on space stations
-#define STATION_TRAIT_SPACE_BOUND (1<<2)
+#define STATION_TRAIT_SPACE_BOUND (1<<1)
/// Not restricted by space or planet, can always just happen
#define STATION_TRAIT_MAP_UNRESTRICTED STATION_TRAIT_PLANETARY | STATION_TRAIT_SPACE_BOUND
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 1a7b013aeba..a848ea64684 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -415,6 +415,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
// Being close enough to the supermatter makes it heal at higher temperatures
// and emit less heat. Present on /mob or /datum/mind
#define TRAIT_SUPERMATTER_SOOTHER "supermatter_soother"
+/// Mob has fov applied to it
+#define TRAIT_FOV_APPLIED "fov_applied"
/// Trait added when a revenant is visible.
#define TRAIT_REVENANT_REVEALED "revenant_revealed"
@@ -487,6 +489,15 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Is the mob standing on an elevated surface? This prevents them from dropping down if not elevated first.
#define TRAIT_ON_ELEVATED_SURFACE "on_elevated_surface"
+/// Prevents you from twohanding weapons.
+#define TRAIT_NO_TWOHANDING "no_twohanding"
+
+/// Halves the time of tying a tie.
+#define TRAIT_FAST_TYING "fast_tying"
+
+/// Sells for more money on the pirate bounty pad.
+#define TRAIT_HIGH_VALUE_RANSOM "high_value_ransom"
+
// METABOLISMS
// Various jobs on the station have historically had better reactions
// to various drinks and foodstuffs. Security liking donuts is a classic
@@ -815,6 +826,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Similar trait given to temporary bodies inhabited by players
#define TRAIT_TEMPORARY_BODY "temporary_body"
+/// Trait given to objects with the wallmounted component
+#define TRAIT_WALLMOUNTED "wallmounted"
+
/// Trait given to mechs that can have orebox functionality on movement
#define TRAIT_OREBOX_FUNCTIONAL "orebox_functional"
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index f0b67a95b92..f9cdbe4326d 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -275,5 +275,7 @@
#define ANALYZER_TRAIT "analyzer_trait"
+/// Trait from an organ being inside a bodypart
+#define ORGAN_INSIDE_BODY_TRAIT "organ_inside_body"
/// Trait when something was labelled by a pen.
#define PEN_LABEL_TRAIT "pen_label"
diff --git a/code/__DEFINES/uplink.dm b/code/__DEFINES/uplink.dm
index fcc2118348c..d6412e0e4d1 100644
--- a/code/__DEFINES/uplink.dm
+++ b/code/__DEFINES/uplink.dm
@@ -9,6 +9,9 @@
/// This item is purchasable to clown ops
#define UPLINK_CLOWN_OPS (1 << 2)
+/// This item is purchasable to infiltrators (midround traitors)
+#define UPLINK_INFILTRATORS (1 << 3)
+
/// Progression gets turned into a user-friendly form. This is just an abstract equation that makes progression not too large.
#define DISPLAY_PROGRESSION(time) round(time/60, 0.01)
diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm
index 787dfe13957..dfa930f7a76 100644
--- a/code/__DEFINES/vv.dm
+++ b/code/__DEFINES/vv.dm
@@ -1,6 +1,6 @@
#define VV_NUM "Number"
#define VV_TEXT "Text"
-#define VV_MESSAGE "Mutiline Text"
+#define VV_MESSAGE "Multiline Text"
#define VV_COLOR "Color"
#define VV_COLOR_MATRIX "Color Matrix"
#define VV_ICON "Icon"
diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm
index 2b4c528abc2..633dcc360c2 100644
--- a/code/__DEFINES/wires.dm
+++ b/code/__DEFINES/wires.dm
@@ -64,6 +64,12 @@
#define WIRE_ZAP1 "High Voltage Circuit 1"
#define WIRE_ZAP2 "High Voltage Circuit 2"
#define WIRE_OVERCLOCK "Overclock"
+#define WIRE_VALVE "Valve"
+#define WIRE_SHIELDING "Shielding"
+#define WIRE_REGULATOR_MIN "Minimize Regulator Pressure"
+#define WIRE_REGULATOR_MAX "Maximize Regulator Pressure"
+#define WIRE_TANK_EJECT "Eject Tank"
+#define WIRE_REACTION_SUPPRESSION "Reaction Suppression"
// Wire states for the AI
#define AI_WIRE_NORMAL 0
diff --git a/code/__DEFINES/~skyrat_defines/examine_defines.dm b/code/__DEFINES/~skyrat_defines/examine_defines.dm
index 933717b3178..2c942cd3215 100644
--- a/code/__DEFINES/~skyrat_defines/examine_defines.dm
+++ b/code/__DEFINES/~skyrat_defines/examine_defines.dm
@@ -12,4 +12,3 @@ See the examinemore module for information.
#define EXAMINE_CHECK_ROLE "role" //For displaying description information based on a specific ROLE, e.g. traitor. Remember to set the special_desc_role var on the item.
#define EXAMINE_CHECK_JOB "job" //For displaying descriptors for specific jobs, e.g scientist. Remember to set the special_desc_job var on the item.
#define EXAMINE_CHECK_FACTION "faction" //For displaying descriptors for mob factions, e.g. a zombie, or... turrets. Or syndicate. Remember to set special_desc_factions.
-#define EXAMINE_CHECK_CONTRACTOR "contractor" // For contractors and syndicate agents.
diff --git a/code/__HELPERS/announcements.dm b/code/__HELPERS/announcements.dm
new file mode 100644
index 00000000000..d0deab12e99
--- /dev/null
+++ b/code/__HELPERS/announcements.dm
@@ -0,0 +1,76 @@
+/**
+ * Sends a div formatted chat box announcement
+ *
+ * Formatted like:
+ *
+ * " Server Announcement " (or sender_override)
+ *
+ * " Title "
+ *
+ * " Text "
+ *
+ * Arguments
+ * * text - required, the text to announce
+ * * title - optional, the title of the announcement.
+ * * players - optional, a list of all players to send the message to. defaults to the entire world
+ * * play_sound - if TRUE, play a sound with the announcement (based on player option)
+ * * sound_override - optional, override the default announcement sound
+ * * sender_override - optional, modifies the sender of the announcement
+ * * encode_title - if TRUE, the title will be HTML encoded
+ * * encode_text - if TRUE, the text will be HTML encoded
+ * * color_override - optional, set a color for the announcement box
+ */
+
+/proc/send_formatted_announcement(
+ text,
+ title = "",
+ players,
+ play_sound = TRUE,
+ sound_override = 'sound/ai/default/attention.ogg',
+ sender_override = "Server Admin Announcement",
+ encode_title = TRUE,
+ encode_text = TRUE,
+ color_override = "grey",
+)
+ if(isnull(text))
+ return
+
+ var/list/announcement_strings = list()
+
+ if(encode_title && title && length(title) > 0)
+ title = html_encode(title)
+ if(encode_text)
+ text = html_encode(text)
+ if(!length(text))
+ return
+
+ announcement_strings += span_announcement_header(generate_unique_announcement_header(title, sender_override))
+ announcement_strings += span_major_announcement_text(text)
+ var/finalized_announcement = create_announcement_div(jointext(announcement_strings, ""), color_override)
+
+ if(islist(players))
+ for(var/mob/target in players)
+ to_chat(target, finalized_announcement)
+ if(play_sound && target.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements))
+ SEND_SOUND(target, sound(sound_override))
+ else
+ to_chat(world, finalized_announcement)
+
+ if(!play_sound)
+ return
+
+ for(var/mob/player in GLOB.player_list)
+ if(player.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements))
+ SEND_SOUND(player, sound(sound_override))
+
+/**
+ * Inserts a span styled message into an alert box div
+ *
+ *
+ * Arguments
+ * * message - required, the message contents
+ * * color - optional, set a div color other than default
+ */
+/proc/create_announcement_div(message, color = "default")
+ var/processed_message = "
[message]
"
+ return processed_message
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index adf8722b120..99c7dd562ea 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -174,145 +174,6 @@
active_players++
return active_players
-///Show the poll window to the candidate mobs
-/proc/show_candidate_poll_window(mob/candidate_mob, poll_time, question, list/candidates, ignore_category, time_passed, flashwindow = TRUE)
- set waitfor = 0
-
- // Universal opt-out for all players.
- if ((!candidate_mob.client.prefs.read_preference(/datum/preference/toggle/ghost_roles)))
- return
-
- // Opt-out for admins whom are currently adminned.
- if ((!candidate_mob.client.prefs.read_preference(/datum/preference/toggle/ghost_roles_as_admin)) && candidate_mob.client.holder)
- return
-
- SEND_SOUND(candidate_mob, 'sound/misc/notice2.ogg') //Alerting them to their consideration
- if(flashwindow)
- window_flash(candidate_mob.client)
- var/list/answers = ignore_category ? list("Yes", "No", "Never for this round") : list("Yes", "No")
- switch(tgui_alert(candidate_mob, question, "A limited-time offer!", answers, poll_time, autofocus = FALSE))
- if("Yes")
- to_chat(candidate_mob, span_notice("Choice registered: Yes."))
- if(time_passed + poll_time <= world.time)
- to_chat(candidate_mob, span_danger("Sorry, you answered too late to be considered!"))
- SEND_SOUND(candidate_mob, 'sound/machines/buzz-sigh.ogg')
- candidates -= candidate_mob
- else
- candidates += candidate_mob
- if("No")
- to_chat(candidate_mob, span_danger("Choice registered: No."))
- candidates -= candidate_mob
- if("Never for this round")
- var/list/ignore_list = GLOB.poll_ignore[ignore_category]
- if(!ignore_list)
- GLOB.poll_ignore[ignore_category] = list()
- GLOB.poll_ignore[ignore_category] += candidate_mob.ckey
- to_chat(candidate_mob, span_danger("Choice registered: Never for this round."))
- candidates -= candidate_mob
- else
- candidates -= candidate_mob
-
-///Wrapper to send all ghosts the poll to ask them if they want to be considered for a mob.
-/proc/poll_ghost_candidates(question, jobban_type, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE)
- var/list/candidates = list()
- if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
- return candidates
-
- for(var/mob/dead/observer/ghost_player in GLOB.player_list)
- candidates += ghost_player
-
- return poll_candidates(question, jobban_type, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
-
-///Calls the show_candidate_poll_window() to all eligible ghosts
-/proc/poll_candidates(question, jobban_type, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null)
- if (group.len == 0)
- return list()
-
- var/time_passed = world.time
- if (!question)
- question = "Would you like to be a special role?"
- var/list/result = list()
- for(var/mob/candidate_mob as anything in group)
- if(!candidate_mob.key || !candidate_mob.client || (ignore_category && GLOB.poll_ignore[ignore_category] && (candidate_mob.ckey in GLOB.poll_ignore[ignore_category])))
- continue
- //SKYRAT EDIT ADDITION BEGIN
- if(is_banned_from(candidate_mob.ckey, BAN_GHOST_TAKEOVER) || is_banned_from(candidate_mob.ckey, BAN_ANTAGONIST))
- to_chat(candidate_mob, "There was a ghost prompt for: [question], unfortunately you are banned from ghost takeovers.")
- continue
- //SKYRAT EDIT END
- if(be_special_flag)
- if(!(candidate_mob.client.prefs) || !(be_special_flag in candidate_mob.client.prefs.be_special))
- continue
-
- var/required_time = GLOB.special_roles[be_special_flag] || 0
- if (candidate_mob.client && candidate_mob.client.get_remaining_days(required_time) > 0)
- continue
- if(jobban_type)
- if(is_banned_from(candidate_mob.ckey, list(jobban_type, ROLE_SYNDICATE)) || QDELETED(candidate_mob))
- continue
-
- show_candidate_poll_window(candidate_mob, poll_time, question, result, ignore_category, time_passed, flashwindow)
- sleep(poll_time)
-
- //Check all our candidates, to make sure they didn't log off or get deleted during the wait period.
- for(var/mob/asking_mob in result)
- if(!asking_mob.key || !asking_mob.client)
- result -= asking_mob
-
- list_clear_nulls(result)
-
- return result
-
-/**
- * Returns a list of ghosts that are eligible to take over and wish to be considered for a mob.
- *
- * Arguments:
- * * question - question to show players as part of poll
- * * jobban_type - Type of jobban to use to filter out potential candidates.
- * * be_special_flag - The required role that the player has to have enabled to see the prompt.
- * * poll_time - Length of time in deciseconds that the poll input box exists before closing.
- * * target_mob - The mob that is being polled for.
- * * ignore_category - The notification preference that hides the prompt.
- */
-/proc/poll_candidates_for_mob(question, jobban_type, be_special_flag = 0, poll_time = 30 SECONDS, mob/target_mob, ignore_category = null)
- var/static/list/mob/currently_polling_mobs = list()
-
- if(currently_polling_mobs.Find(target_mob))
- return list()
-
- currently_polling_mobs += target_mob
-
- var/list/possible_candidates = poll_ghost_candidates(question, jobban_type, be_special_flag, poll_time, ignore_category)
-
- currently_polling_mobs -= target_mob
- if(!target_mob || QDELETED(target_mob) || !target_mob.loc)
- return list()
-
- return possible_candidates
-
-/**
- * Returns a list of ghosts that are eligible to take over and wish to be considered for a mob.
- *
- * Arguments:
- * * question - question to show players as part of poll
- * * jobban_type - Type of jobban to use to filter out potential candidates.
- * * be_special_flag - The required role that the player has to have enabled to see the prompt.
- * * poll_time - Length of time in deciseconds that the poll input box exists before closing.
- * * mobs - The list of mobs being polled for. This list is mutated and invalid mobs are removed from it before the proc returns.
- * * ignore_category - The notification preference that hides the prompt.
- */
-/proc/poll_candidates_for_mobs(question, jobban_type, be_special_flag = 0, poll_time = 30 SECONDS, list/mobs, ignore_category = null)
- var/list/candidate_list = poll_ghost_candidates(question, jobban_type, be_special_flag, poll_time, ignore_category)
-
- for(var/mob/potential_mob as anything in mobs)
- if(QDELETED(potential_mob) || !potential_mob.loc)
- mobs -= potential_mob
-
- if(!length(mobs))
- return list()
-
- return candidate_list
-
///Uses stripped down and bastardized code from respawn character
/proc/make_body(mob/dead/observer/ghost_player)
if(!ghost_player || !ghost_player.key)
diff --git a/code/__HELPERS/logging/dynamic.dm b/code/__HELPERS/logging/dynamic.dm
index 51bba86ca05..488aeaec3be 100644
--- a/code/__HELPERS/logging/dynamic.dm
+++ b/code/__HELPERS/logging/dynamic.dm
@@ -1,5 +1,5 @@
/// Log to dynamic and message admins
-/datum/game_mode/dynamic/proc/log_dynamic_and_announce(text)
+/datum/controller/subsystem/dynamic/proc/log_dynamic_and_announce(text)
message_admins("DYNAMIC: [text]")
log_dynamic("[text]")
diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm
index 075a5e5ae0e..e6a9e038849 100644
--- a/code/__HELPERS/matrices.dm
+++ b/code/__HELPERS/matrices.dm
@@ -102,15 +102,6 @@ list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0)
list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0)
*/
-//Does nothing
-/proc/color_matrix_identity()
- return list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
-
-//Adds/subtracts overall lightness
-//0 is identity, 1 makes everything white, -1 makes everything black
-/proc/color_matrix_lightness(power)
- return list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, power,power,power,0)
-
//Changes distance hues have from grey while maintaining the overall lightness. Greys are unaffected.
//1 is identity, 0 is greyscale, >1 oversaturates colors
/proc/color_matrix_saturation(value)
@@ -121,12 +112,6 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
return list(R + value,R,R,0, G,G + value,G,0, B,B,B + value,0, 0,0,0,1, 0,0,0,0)
-//Changes distance colors have from rgb(127,127,127) grey
-//1 is identity. 0 makes everything grey >1 blows out colors and greys
-/proc/color_matrix_contrast(value)
- var/add = (1 - value) / 2
- return list(value,0,0,0, 0,value,0,0, 0,0,value,0, 0,0,0,1, add,add,add,0)
-
//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting greys
//0 is identity, 120 moves reds to greens, 240 moves reds to blues
/proc/color_matrix_rotate_hue(angle)
@@ -159,9 +144,9 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
//Returns a matrix addition of A with B
/proc/color_matrix_add(list/A, list/B)
if(!istype(A) || !istype(B))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
if(A.len != 20 || B.len != 20)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
var/list/output = list()
output.len = 20
for(var/value in 1 to 20)
@@ -171,9 +156,9 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
//Returns a matrix multiplication of A with B
/proc/color_matrix_multiply(list/A, list/B)
if(!istype(A) || !istype(B))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
if(A.len != 20 || B.len != 20)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
var/list/output = list()
output.len = 20
var/x = 1
@@ -191,14 +176,14 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
*/
/proc/color_to_full_rgba_matrix(color, return_identity_on_fail = TRUE)
if(!color)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
if(istext(color))
var/list/L = ReadRGB(color)
if(!L)
var/message = "Invalid/unsupported color ([color]) argument in color_to_full_rgba_matrix()"
if(return_identity_on_fail)
stack_trace(message)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
CRASH(message)
return list(L[1]/255,0,0,0, 0,L[2]/255,0,0, 0,0,L[3]/255,0, 0,0,0,L.len>3?L[4]/255:1, 0,0,0,0)
if(!islist(color)) //invalid format
@@ -231,7 +216,7 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
var/message = "Invalid/unsupported color (list of length [L.len]) argument in color_to_full_rgba_matrix()"
if(return_identity_on_fail)
stack_trace(message)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
CRASH(message)
#undef LUMA_R
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 5a408937a04..ffddf70e239 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -707,11 +707,6 @@ GLOBAL_LIST_EMPTY(species_list)
if(isliving(occupant))
mob_occupant = occupant
- else if(isbodypart(occupant))
- var/obj/item/bodypart/head/head = occupant
-
- mob_occupant = head.brainmob
-
else if(isorgan(occupant))
var/obj/item/organ/internal/brain/brain = occupant
mob_occupant = brain.brainmob
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index bad17bdc28f..363ac2fc7a5 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -245,7 +245,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
*/
//Set news report and mode result
- mode.set_round_result()
+ SSdynamic.set_round_result()
to_chat(world, span_infoplain(span_big(span_bold("
The round has ended."))))
log_game("The round has ended.")
@@ -364,17 +364,17 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
//ignore this comment, it fixes the broken sytax parsing caused by the " above
else
parts += "[FOURSPACES]Nobody died this shift!"
- if(istype(SSticker.mode, /datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- parts += "[FOURSPACES]Threat level: [mode.threat_level]"
- parts += "[FOURSPACES]Threat left: [mode.mid_round_budget]"
- if(mode.roundend_threat_log.len)
- parts += "[FOURSPACES]Threat edits:"
- for(var/entry as anything in mode.roundend_threat_log)
- parts += "[FOURSPACES][FOURSPACES][entry] "
- parts += "[FOURSPACES]Executed rules:"
- for(var/datum/dynamic_ruleset/rule in mode.executed_rules)
- parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost + rule.scaled_times * rule.scaling_cost] threat"
+
+ parts += "[FOURSPACES]Threat level: [SSdynamic.threat_level]"
+ parts += "[FOURSPACES]Threat left: [SSdynamic.mid_round_budget]"
+ if(SSdynamic.roundend_threat_log.len)
+ parts += "[FOURSPACES]Threat edits:"
+ for(var/entry as anything in SSdynamic.roundend_threat_log)
+ parts += "[FOURSPACES][FOURSPACES][entry] "
+ parts += "[FOURSPACES]Executed rules:"
+ for(var/datum/dynamic_ruleset/rule in SSdynamic.executed_rules)
+ parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost + rule.scaled_times * rule.scaling_cost] threat"
+
return parts.Join(" ")
/client/proc/roundend_report_file()
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index 3b2d52fbbb6..98e48852094 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -337,7 +337,7 @@ GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH,
/proc/color_hex2color_matrix(string)
var/length = length(string)
if((length != 7 && length != 9) || length != length_char(string))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
var/r = hex2num(copytext(string, 2, 4))/255
var/g = hex2num(copytext(string, 4, 6))/255
var/b = hex2num(copytext(string, 6, 8))/255
@@ -345,7 +345,7 @@ GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH,
if(length == 9)
a = hex2num(copytext(string, 8, 10))/255
if(!isnum(r) || !isnum(g) || !isnum(b) || !isnum(a))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
return list(r,0,0,0, 0,g,0,0, 0,0,b,0, 0,0,0,a, 0,0,0,0)
//will drop all values not on the diagonal
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index d66b467ca3f..5a51977f9ad 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -259,6 +259,8 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items
/obj/item/computer_disk/maintenance/scanner = 1,
///obj/item/computer_disk/maintenance/camera = 1, //SKYRAT EDIT REMOVAL - Available To Crew Now
/obj/item/computer_disk/maintenance/modsuit_control = 1,
+ /obj/item/computer_disk/maintenance/spectre_meter = 1,
+ /obj/item/computer_disk/maintenance/arcade = 1,
/obj/item/computer_disk/maintenance/theme = 3,
) = 3, //SKYRAT EDIT CHANGE - Original : 4
diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm
index 8ac2f0d3ddf..b77942bad87 100644
--- a/code/_globalvars/lists/mapping.dm
+++ b/code/_globalvars/lists/mapping.dm
@@ -107,6 +107,7 @@ GLOBAL_LIST_EMPTY(generic_event_spawns)
/// essentially allowing a user to override generic job spawnpoints with a specific one
GLOBAL_LIST_EMPTY(jobspawn_overrides)
+GLOBAL_LIST_EMPTY(gorilla_start)
GLOBAL_LIST_EMPTY(wizardstart)
GLOBAL_LIST_EMPTY(nukeop_start)
GLOBAL_LIST_EMPTY(nukeop_leader_start)
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index bc195bcf5f4..c51fbaa9eb7 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -25,6 +25,7 @@ GLOBAL_LIST_INIT(megacarp_first_names, world.file2list("strings/names/megacarp1.
GLOBAL_LIST_INIT(megacarp_last_names, world.file2list("strings/names/megacarp2.txt"))
GLOBAL_LIST_INIT(cyberauth_names, world.file2list("strings/names/cyberauth.txt"))
GLOBAL_LIST_INIT(syndicate_monkey_names, world.file2list("strings/names/syndicate_monkey.txt"))
+GLOBAL_LIST_INIT(cargorilla_names, world.file2list("strings/names/cargorilla.txt"))
GLOBAL_LIST_INIT(guardian_first_names, world.file2list("strings/names/guardian_descriptions.txt"))
GLOBAL_LIST_INIT(guardian_tech_surnames, world.file2list("strings/names/guardian_gamepieces.txt"))
GLOBAL_LIST_INIT(guardian_fantasy_surnames, world.file2list("strings/names/guardian_tarot.txt"))
diff --git a/code/_globalvars/lists/quirks.dm b/code/_globalvars/lists/quirks.dm
index 5db4f53ed71..a6031b1745e 100644
--- a/code/_globalvars/lists/quirks.dm
+++ b/code/_globalvars/lists/quirks.dm
@@ -19,3 +19,10 @@ GLOBAL_LIST_INIT(limb_choice, list(
"Left Leg" = /obj/item/bodypart/leg/left/robot/surplus,
"Right Leg" = /obj/item/bodypart/leg/right/robot/surplus,
))
+
+GLOBAL_LIST_INIT(limb_choice_transhuman, list(
+ "Left Arm" = /obj/item/bodypart/arm/left/robot,
+ "Right Arm" = /obj/item/bodypart/arm/right/robot,
+ "Left Leg" = /obj/item/bodypart/leg/left/robot,
+ "Right Leg" = /obj/item/bodypart/leg/right/robot,
+))
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 7388770cab4..5a4a0181e26 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -98,6 +98,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
/datum/wound = list(
"TRAIT_WOUND_SCANNED" = TRAIT_WOUND_SCANNED,
),
+ /obj = list(
+ "TRAIT_WALLMOUNTED" = TRAIT_WALLMOUNTED,
+ ),
/mob = list(
"TRAIT_ABDUCTOR_SCIENTIST_TRAINING" = TRAIT_ABDUCTOR_SCIENTIST_TRAINING,
"TRAIT_ABDUCTOR_TRAINING" = TRAIT_ABDUCTOR_TRAINING,
@@ -188,6 +191,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_EXPANDED_FOV" = TRAIT_EXPANDED_FOV,
"TRAIT_EXTROVERT" = TRAIT_EXTROVERT,
"TRAIT_FAKEDEATH" = TRAIT_FAKEDEATH,
+ "TRAIT_FAST_TYING" = TRAIT_FAST_TYING,
"TRAIT_FASTMED" = TRAIT_FASTMED,
"TRAIT_FAT" = TRAIT_FAT,
"TRAIT_FEARLESS" = TRAIT_FEARLESS,
@@ -200,6 +204,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION" = TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION,
"TRAIT_FORCED_GRAVITY" = TRAIT_FORCED_GRAVITY,
"TRAIT_FORCED_STANDING" = TRAIT_FORCED_STANDING,
+ "TRAIT_FOV_APPLIED" = TRAIT_FOV_APPLIED,
"TRAIT_FREE_FLOAT_MOVEMENT" = TRAIT_FREE_FLOAT_MOVEMENT,
"TRAIT_FREE_HYPERSPACE_MOVEMENT" = TRAIT_FREE_HYPERSPACE_MOVEMENT,
"TRAIT_FREE_HYPERSPACE_SOFTCORDON_MOVEMENT" = TRAIT_FREE_HYPERSPACE_SOFTCORDON_MOVEMENT,
@@ -229,6 +234,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_HEAVY_DRINKER" = TRAIT_HEAVY_DRINKER,
"TRAIT_HEAVY_SLEEPER" = TRAIT_HEAVY_SLEEPER,
"TRAIT_HIDE_EXTERNAL_ORGANS" = TRAIT_HIDE_EXTERNAL_ORGANS,
+ "TRAIT_HIGH_VALUE_RANSOM" = TRAIT_HIGH_VALUE_RANSOM,
"TRAIT_HOLY" = TRAIT_HOLY,
"TRAIT_HOPELESSLY_ADDICTED" = TRAIT_HOPELESSLY_ADDICTED,
"TRAIT_HOT_SPRING_CURSED" = TRAIT_HOT_SPRING_CURSED,
@@ -304,6 +310,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_SOUL" = TRAIT_NO_SOUL,
"TRAIT_NO_STRIP" = TRAIT_NO_STRIP,
"TRAIT_NO_TRANSFORM" = TRAIT_NO_TRANSFORM,
+ "TRAIT_NO_TWOHANDING" = TRAIT_NO_TWOHANDING,
"TRAIT_NOCRITDAMAGE" = TRAIT_NOCRITDAMAGE,
"TRAIT_NO_UNDERWEAR" = TRAIT_NO_UNDERWEAR,
"TRAIT_NO_ZOMBIFY" = TRAIT_NO_ZOMBIFY,
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 2f3df52dfd3..48fa6fdd88d 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -12,7 +12,7 @@
*flicks are forwarded to master
*override makes it so the alert is not replaced until cleared by a clear_alert with clear_override, and it's used for hallucinations.
*/
-/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE)
+/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim = FALSE)
if(!category || QDELETED(src))
return
@@ -39,7 +39,7 @@
// No need to update existing alert
return thealert
// Reset timeout of existing alert
- var/timeout = initial(thealert.timeout)
+ var/timeout = timeout_override || initial(thealert.timeout)
addtimer(CALLBACK(src, PROC_REF(alert_timeout), thealert, category), timeout)
thealert.timeout = world.time + timeout - world.tick_lag
return thealert
@@ -67,9 +67,11 @@
alerts[category] = thealert
if(client && hud_used)
hud_used.reorganize_alerts()
- thealert.transform = matrix(32, 6, MATRIX_TRANSLATE)
- animate(thealert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
-
+ if(!no_anim)
+ thealert.transform = matrix(32, 6, MATRIX_TRANSLATE)
+ animate(thealert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
+ if(timeout_override)
+ thealert.timeout = timeout_override
if(thealert.timeout)
addtimer(CALLBACK(src, PROC_REF(alert_timeout), thealert, category), thealert.timeout)
thealert.timeout = world.time + thealert.timeout - world.tick_lag
@@ -802,6 +804,153 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
ghost_owner.observer_view(target)
+/atom/movable/screen/alert/poll_alert
+ name = "Looking for candidates"
+ icon_state = "template"
+ timeout = 30 SECONDS
+ ghost_screentips = TRUE
+ /// If true you need to call START_PROCESSING manually
+ var/show_time_left = FALSE
+ /// MA for maptext showing time left for poll
+ var/mutable_appearance/time_left_overlay
+ /// MA for overlay showing that you're signed up to poll
+ var/mutable_appearance/signed_up_overlay
+ /// MA for maptext overlay showing how many polls are stacked together
+ var/mutable_appearance/stacks_overlay
+ /// MA for maptext overlay showing how many candidates are signed up to a poll
+ var/mutable_appearance/candidates_num_overlay
+ /// MA for maptext overlay of poll's role name or question
+ var/mutable_appearance/role_overlay
+ /// If set, on Click() it'll register the player as a candidate
+ var/datum/candidate_poll/poll
+
+/atom/movable/screen/alert/poll_alert/Initialize(mapload)
+ . = ..()
+ register_context()
+
+/atom/movable/screen/alert/poll_alert/proc/set_role_overlay()
+ var/role_or_only_question = poll.role || "?"
+ role_overlay = new
+ role_overlay.screen_loc = screen_loc
+ role_overlay.maptext = MAPTEXT("[full_capitalize(role_or_only_question)]")
+ role_overlay.maptext_width = 128
+ role_overlay.transform = role_overlay.transform.Translate(-128, 0)
+ add_overlay(role_overlay)
+
+/atom/movable/screen/alert/poll_alert/Destroy()
+ QDEL_NULL(role_overlay)
+ QDEL_NULL(time_left_overlay)
+ QDEL_NULL(stacks_overlay)
+ QDEL_NULL(candidates_num_overlay)
+ QDEL_NULL(signed_up_overlay)
+ return ..()
+
+/atom/movable/screen/alert/poll_alert/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ var/left_click_text
+ if(poll)
+ if(owner in poll.signed_up)
+ left_click_text = "Leave"
+ else
+ left_click_text = "Enter"
+ context[SCREENTIP_CONTEXT_LMB] = "[left_click_text] Poll"
+ if(poll.ignoring_category)
+ var/selected_never = FALSE
+ if(owner.ckey in GLOB.poll_ignore[poll.ignoring_category])
+ selected_never = TRUE
+ context[SCREENTIP_CONTEXT_ALT_LMB] = "[selected_never ? "Cancel " : ""]Never For This Round"
+ if(poll.jump_to_me && isobserver(owner))
+ context[SCREENTIP_CONTEXT_CTRL_LMB] = "Jump To"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/atom/movable/screen/alert/poll_alert/process()
+ if(show_time_left)
+ var/timeleft = timeout - world.time
+ if(timeleft <= 0)
+ return PROCESS_KILL
+ cut_overlay(time_left_overlay)
+ time_left_overlay = new
+ time_left_overlay.maptext = MAPTEXT("[CEILING(timeleft / (1 SECONDS), 1)]")
+ time_left_overlay.transform = time_left_overlay.transform.Translate(4, 19)
+ add_overlay(time_left_overlay)
+ if(isnull(poll))
+ return
+ ..()
+
+/atom/movable/screen/alert/poll_alert/Click(location, control, params)
+ . = ..()
+ if(!. || isnull(poll))
+ return
+ var/list/modifiers = params2list(params)
+ if(LAZYACCESS(modifiers, ALT_CLICK) && poll.ignoring_category)
+ set_never_round()
+ return
+ if(LAZYACCESS(modifiers, CTRL_CLICK) && poll.jump_to_me)
+ jump_to_pic_source()
+ return
+ handle_sign_up()
+
+/atom/movable/screen/alert/poll_alert/proc/handle_sign_up()
+ if(owner in poll.signed_up)
+ poll.remove_candidate(owner)
+ else if(!(owner.ckey in GLOB.poll_ignore[poll.ignoring_category]))
+ poll.sign_up(owner)
+
+/atom/movable/screen/alert/poll_alert/proc/set_never_round()
+ if(!(owner.ckey in GLOB.poll_ignore[poll.ignoring_category]))
+ poll.do_never_for_this_round(owner)
+ color = "red"
+ return
+ poll.undo_never_for_this_round(owner)
+ color = initial(color)
+
+/atom/movable/screen/alert/poll_alert/proc/jump_to_pic_source()
+ if(!poll?.jump_to_me || !isobserver(owner))
+ return
+ var/turf/target_turf = get_turf(poll.jump_to_me)
+ if(target_turf && isturf(target_turf))
+ owner.abstract_move(target_turf)
+
+/atom/movable/screen/alert/poll_alert/Topic(href, href_list)
+ if(href_list["never"])
+ set_never_round()
+ return
+ if(href_list["signup"])
+ handle_sign_up()
+ if(href_list["jump"])
+ jump_to_pic_source()
+ return
+
+/atom/movable/screen/alert/poll_alert/proc/update_signed_up_overlay()
+ signed_up_overlay = mutable_appearance('icons/hud/screen_gen.dmi', icon_state = "selector")
+ if(owner in poll.signed_up)
+ add_overlay(signed_up_overlay)
+ else
+ cut_overlay(signed_up_overlay)
+
+/atom/movable/screen/alert/poll_alert/proc/update_candidates_number_overlay()
+ cut_overlay(candidates_num_overlay)
+ if(!length(poll.signed_up))
+ return
+ candidates_num_overlay = new
+ candidates_num_overlay.maptext = MAPTEXT("[length(poll.signed_up)]")
+ candidates_num_overlay.transform = candidates_num_overlay.transform.Translate(-4, 2)
+ add_overlay(candidates_num_overlay)
+
+/atom/movable/screen/alert/poll_alert/proc/update_stacks_overlay()
+ cut_overlay(stacks_overlay)
+ var/stack_number = 1
+ for(var/datum/candidate_poll/other_poll as anything in SSpolling.currently_polling)
+ if(other_poll != poll && other_poll.poll_key == poll.poll_key && !other_poll.finished)
+ stack_number++
+ if(stack_number <= 1)
+ return
+ stacks_overlay = new
+ stacks_overlay.maptext = MAPTEXT("[stack_number]x")
+ stacks_overlay.transform = stacks_overlay.transform.Translate(3, 2)
+ stacks_overlay.layer = layer
+ add_overlay(stacks_overlay)
+
//OBJECT-BASED
/atom/movable/screen/alert/buckled
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
index 376164235a7..a51b61cd396 100644
--- a/code/_onclick/hud/new_player.dm
+++ b/code/_onclick/hud/new_player.dm
@@ -1,38 +1,66 @@
/* SKYRAT EDIT REMOVAL - Custom HTML Lobby Screen
#define SHUTTER_MOVEMENT_DURATION 0.4 SECONDS
#define SHUTTER_WAIT_DURATION 0.2 SECONDS
+/// Maximum number of station trait buttons we will display, please think hard before creating scenarios where there are more than this
+#define MAX_STATION_TRAIT_BUTTONS_VERTICAL 3
/datum/hud/new_player
///Whether the menu is currently on the client's screen or not
var/menu_hud_status = TRUE
/datum/hud/new_player/New(mob/owner)
- ..()
+ . = ..()
- if(!owner || !owner.client)
+ if (!owner || !owner.client)
return
- if(owner.client.interviewee)
+ if (owner.client.interviewee)
return
var/list/buttons = subtypesof(/atom/movable/screen/lobby)
- for(var/button_type in buttons)
- var/atom/movable/screen/lobby/lobbyscreen = new button_type(our_hud = src)
+ for (var/atom/movable/screen/lobby/lobbyscreen as anything in buttons)
+ if (!initial(lobbyscreen.always_available))
+ continue
+ lobbyscreen = new lobbyscreen(our_hud = src)
lobbyscreen.SlowInit()
static_inventory += lobbyscreen
- if(!lobbyscreen.always_shown)
+ if (!lobbyscreen.always_shown)
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_COLLAPSED, TYPE_PROC_REF(/atom/movable/screen/lobby, collapse_button))
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_EXPANDED, TYPE_PROC_REF(/atom/movable/screen/lobby, expand_button))
- if(istype(lobbyscreen, /atom/movable/screen/lobby/button))
+ if (istype(lobbyscreen, /atom/movable/screen/lobby/button))
var/atom/movable/screen/lobby/button/lobby_button = lobbyscreen
lobby_button.owner = REF(owner)
+ add_station_trait_buttons()
+
+/// Display buttons for relevant station traits
+/datum/hud/new_player/proc/add_station_trait_buttons()
+ if (!mymob?.client || mymob.client.interviewee || !length(GLOB.lobby_station_traits))
+ return
+ var/buttons_created = 0
+ var/y_offset = 397
+ var/y_button_offset = 27
+ for (var/datum/station_trait/trait as anything in GLOB.lobby_station_traits)
+ if (!trait.can_display_lobby_button(mymob.client))
+ continue
+ var/atom/movable/screen/lobby/button/sign_up/sign_up_button = new(our_hud = src)
+ sign_up_button.SlowInit()
+ sign_up_button.owner = REF(mymob)
+ sign_up_button.screen_loc = offset_to_screen_loc(233, y_offset, mymob.client.view)
+ y_offset += y_button_offset
+ static_inventory += sign_up_button
+ trait.setup_lobby_button(sign_up_button)
+ buttons_created++
+ if (buttons_created >= MAX_STATION_TRAIT_BUTTONS_VERTICAL)
+ return
/atom/movable/screen/lobby
plane = SPLASHSCREEN_PLANE
layer = LOBBY_MENU_LAYER
screen_loc = "TOP,CENTER"
- ///Whether this HUD element can be hidden from the client's "screen" (moved off-screen) or not
+ /// Whether this HUD element can be hidden from the client's "screen" (moved off-screen) or not
var/always_shown = FALSE
+ /// If true we will create this button every time the HUD is generated
+ var/always_available = TRUE
///Set the HUD in New, as lobby screens are made before Atoms are Initialized.
/atom/movable/screen/lobby/New(loc, datum/hud/our_hud, ...)
@@ -413,6 +441,23 @@
var/mob/dead/new_player/new_player = hud.mymob
new_player.handle_player_polling()
+/// A generic "sign up" button used by station traits
+/atom/movable/screen/lobby/button/sign_up
+ icon = 'icons/hud/lobby/signup_button.dmi'
+ icon_state = "signup"
+ base_icon_state = "signup"
+ always_available = FALSE
+
+/atom/movable/screen/lobby/button/sign_up/MouseEntered(location, control, params)
+ . = ..()
+ if(QDELETED(src) || !desc)
+ return
+ openToolTip(usr, tip_src = src, params = params, title = name, content = desc,)
+
+/atom/movable/screen/lobby/button/sign_up/MouseExited()
+ . = ..()
+ closeToolTip(usr)
+
/atom/movable/screen/lobby/button/collapse
name = "Collapse Lobby Menu"
icon = 'icons/hud/lobby/collapse_expand.dmi'
@@ -540,5 +585,5 @@
#undef SHUTTER_MOVEMENT_DURATION
#undef SHUTTER_WAIT_DURATION
-
+#undef MAX_STATION_TRAIT_BUTTONS_VERTICAL
*/ // SKYRAT EDIT END
diff --git a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
index 8988b2f7f33..63206afdaa9 100644
--- a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
+++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
@@ -1,3 +1,38 @@
+/atom/movable/screen/plane_master/field_of_vision_blocker
+ name = "Field of vision blocker"
+ documentation = "This is one of those planes that's only used as a filter. It cuts out a portion of the game plate and does effects to it."
+ plane = FIELD_OF_VISION_BLOCKER_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ render_relay_planes = list()
+ // We do NOT allow offsetting, because there's no case where you would want to block only one layer, at least currently
+ allows_offsetting = FALSE
+ // We mark as multiz_scaled FALSE so transforms don't effect us, and we draw to the planes below us as if they were us.
+ // This is safe because we will ALWAYS be on the top z layer, so it DON'T MATTER
+ multiz_scaled = FALSE
+
+/atom/movable/screen/plane_master/field_of_vision_blocker/show_to(mob/mymob)
+ . = ..()
+ if(!. || !mymob)
+ return .
+ RegisterSignal(mymob, SIGNAL_ADDTRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_enabled), override = TRUE)
+ RegisterSignal(mymob, SIGNAL_REMOVETRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_disabled), override = TRUE)
+ if(HAS_TRAIT(mymob, TRAIT_FOV_APPLIED))
+ fov_enabled(mymob)
+ else
+ fov_disabled(mymob)
+
+/atom/movable/screen/plane_master/field_of_vision_blocker/proc/fov_enabled(mob/source)
+ SIGNAL_HANDLER
+ if(force_hidden == FALSE)
+ return
+ unhide_plane(source)
+
+/atom/movable/screen/plane_master/field_of_vision_blocker/proc/fov_disabled(mob/source)
+ SIGNAL_HANDLER
+ hide_plane(source)
+
/atom/movable/screen/plane_master/clickcatcher
name = "Click Catcher"
documentation = "Contains the screen object we use as a backdrop to catch clicks on portions of the screen that would otherwise contain nothing else. \
@@ -168,69 +203,17 @@
add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
/atom/movable/screen/plane_master/game
- name = "Lower game world"
- documentation = "Exists mostly because of FOV shit. Basically, if you've just got a normal not ABOVE fov thing, and you don't want it masked, stick it here yeah?"
+ name = "Game"
+ documentation = "Holds most non floor/wall things. Anything on this plane \"wants\" to interlayer depending on position."
plane = GAME_PLANE
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-/atom/movable/screen/plane_master/game_world_fov_hidden
- name = "lower game world fov hidden"
- documentation = "If you want something to be hidden by fov, stick it on this plane. We're masked by the fov blocker plane, so the items on us can actually well, disappear."
- plane = GAME_PLANE_FOV_HIDDEN
+/atom/movable/screen/plane_master/game_world_above
+ name = "Upper Game"
+ documentation = "For stuff you want to draw like the game plane, but not ever below its contents"
+ plane = ABOVE_GAME_PLANE
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-/atom/movable/screen/plane_master/game_world_fov_hidden/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
-
-/atom/movable/screen/plane_master/field_of_vision_blocker
- name = "Field of vision blocker"
- documentation = "This is one of those planes that's only used as a filter. It masks out things that want to be hidden by fov.\
- Literally just contains FOV images, or masks."
- plane = FIELD_OF_VISION_BLOCKER_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- render_relay_planes = list()
- // We do NOT allow offsetting, because there's no case where you would want to block only one layer, at least currently
- allows_offsetting = FALSE
- start_hidden = TRUE
- // We mark as multiz_scaled FALSE so transforms don't effect us, and we draw to the planes below us as if they were us.
- // This is safe because we will ALWAYS be on the top z layer, so it DON'T MATTER
- multiz_scaled = FALSE
-
-/atom/movable/screen/plane_master/field_of_vision_blocker/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- mirror_parent_hidden()
-
-/atom/movable/screen/plane_master/game_world_upper
- name = "Upper game world"
- documentation = "Ok so fov is kinda fucky, because planes in byond serve both as effect groupings and as rendering orderers. Since that's true, we need a plane that we can stick stuff that draws above fov blocked stuff on."
- plane = GAME_PLANE_UPPER
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
-/atom/movable/screen/plane_master/wall_upper
- name = "Upper wall"
- documentation = "There are some walls that want to render above most things (mostly minerals since they shift over.\
- We draw them to their own plane so we can hijack them for our emissive mask stuff"
- plane = WALL_PLANE_UPPER
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE)
-
-/atom/movable/screen/plane_master/wall_upper/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
-
-/atom/movable/screen/plane_master/game_world_upper_fov_hidden
- name = "Upper game world fov hidden"
- documentation = "Just as we need a place to draw things \"above\" the hidden fov plane, we also need to be able to hide stuff that draws over the upper game plane."
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
-/atom/movable/screen/plane_master/game_world_upper_fov_hidden/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- // Dupe of the other hidden plane
- add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
-
/atom/movable/screen/plane_master/seethrough
name = "Seethrough"
documentation = "Holds the seethrough versions (done using image overrides) of large objects. Mouse transparent, so you can click through them."
@@ -239,12 +222,6 @@
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
start_hidden = TRUE
-/atom/movable/screen/plane_master/game_world_above
- name = "Above game world"
- documentation = "We need a place that's unmasked by fov that also draws above the upper game world fov hidden plane. I told you fov was hacky man."
- plane = ABOVE_GAME_PLANE
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
/**
* Plane master that byond will by default draw to
* Shouldn't be used, exists to prevent people using plane 0
diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm
index 02c43d34767..a4ec391c784 100644
--- a/code/_onclick/hud/rendering/render_plate.dm
+++ b/code/_onclick/hud/rendering/render_plate.dm
@@ -72,7 +72,8 @@
/atom/movable/screen/plane_master/rendering_plate/game_plate
name = "Game rendering plate"
documentation = "Holds all objects that are ahhh, in character? is maybe the best way to describe it.\
- We apply a displacement effect from the gravity pulse plane too, so we can warp the game world."
+ We apply a displacement effect from the gravity pulse plane too, so we can warp the game world.\
+ If we have fov enabled we'll relay this onto two different rendering plates to apply fov effects to only a portion. If not, we just draw straight to master"
plane = RENDER_PLANE_GAME
render_relay_planes = list(RENDER_PLANE_MASTER)
@@ -84,6 +85,97 @@
// We're basically using alpha as better constant here btw
add_filter("spook_color", 2, color_matrix_filter(list(0.75,0.13,0.13,0, 0.13,0.7,0.13,0, 0.13,0.13,0.75,0, -0.06,-0.09,-0.08,1, 0,0,0,0)))
+/atom/movable/screen/plane_master/rendering_plate/game_plate/show_to(mob/mymob)
+ . = ..()
+ if(!. || !mymob)
+ return .
+ RegisterSignal(mymob, SIGNAL_ADDTRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_enabled), override = TRUE)
+ RegisterSignal(mymob, SIGNAL_REMOVETRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_disabled), override = TRUE)
+ if(HAS_TRAIT(mymob, TRAIT_FOV_APPLIED))
+ fov_enabled(mymob)
+ else
+ fov_disabled(mymob)
+
+/atom/movable/screen/plane_master/rendering_plate/game_plate/proc/fov_enabled(mob/source)
+ SIGNAL_HANDLER
+ add_relay_to(GET_NEW_PLANE(RENDER_PLANE_GAME_UNMASKED, offset))
+ add_relay_to(GET_NEW_PLANE(RENDER_PLANE_GAME_MASKED, offset))
+ remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_MASTER, offset))
+
+/atom/movable/screen/plane_master/rendering_plate/game_plate/proc/fov_disabled(mob/source)
+ SIGNAL_HANDLER
+ remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME_UNMASKED, offset))
+ remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME_MASKED, offset))
+ add_relay_to(GET_NEW_PLANE(RENDER_PLANE_MASTER, offset))
+
+///renders the parts of the plate unmasked by fov
+/atom/movable/screen/plane_master/rendering_plate/unmasked_game_plate
+ name = "Unmasked Game rendering plate"
+ documentation = "Holds the bits of the game plate that aren't impacted by fov.\
+ We use an alpha mask to cut out the bits we plan on dealing with elsewhere"
+ plane = RENDER_PLANE_GAME_UNMASKED
+ render_relay_planes = list(RENDER_PLANE_MASTER)
+
+/atom/movable/screen/plane_master/rendering_plate/unmasked_game_plate/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ add_filter("fov_handled", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
+
+/atom/movable/screen/plane_master/rendering_plate/unmasked_game_plate/show_to(mob/mymob)
+ . = ..()
+ if(!. || !mymob)
+ return .
+ RegisterSignal(mymob, SIGNAL_ADDTRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_enabled), override = TRUE)
+ RegisterSignal(mymob, SIGNAL_REMOVETRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_disabled), override = TRUE)
+ if(HAS_TRAIT(mymob, TRAIT_FOV_APPLIED))
+ fov_enabled(mymob)
+ else
+ fov_disabled(mymob)
+
+/atom/movable/screen/plane_master/rendering_plate/unmasked_game_plate/proc/fov_enabled(mob/source)
+ SIGNAL_HANDLER
+ if(force_hidden == FALSE)
+ return
+ unhide_plane(source)
+
+/atom/movable/screen/plane_master/rendering_plate/unmasked_game_plate/proc/fov_disabled(mob/source)
+ SIGNAL_HANDLER
+ hide_plane(source)
+
+///renders the parts of the plate masked by fov
+/atom/movable/screen/plane_master/rendering_plate/masked_game_plate
+ name = "FOV Game rendering plate"
+ documentation = "Contains the bits of the game plate that are hidden by some form of fov\
+ Applies a color matrix to dim and create contrast, alongside a blur. Goal is only half being able to see stuff"
+ plane = RENDER_PLANE_GAME_MASKED
+ render_relay_planes = list(RENDER_PLANE_MASTER)
+
+/atom/movable/screen/plane_master/rendering_plate/masked_game_plate/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ add_filter("fov_blur", 1, gauss_blur_filter(1.8))
+ add_filter("fov_handled_space", 2, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset)))
+ add_filter("fov_matrix", 3, color_matrix_filter(list(0.5,-0.15,-0.15,0, -0.15,0.5,-0.15,0, -0.15,-0.15,0.5,0, 0,0,0,1, 0,0,0,0)))
+
+/atom/movable/screen/plane_master/rendering_plate/masked_game_plate/show_to(mob/mymob)
+ . = ..()
+ if(!. || !mymob)
+ return .
+ RegisterSignal(mymob, SIGNAL_ADDTRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_enabled), override = TRUE)
+ RegisterSignal(mymob, SIGNAL_REMOVETRAIT(TRAIT_FOV_APPLIED), PROC_REF(fov_disabled), override = TRUE)
+ if(HAS_TRAIT(mymob, TRAIT_FOV_APPLIED))
+ fov_enabled(mymob)
+ else
+ fov_disabled(mymob)
+
+/atom/movable/screen/plane_master/rendering_plate/masked_game_plate/proc/fov_enabled(mob/source)
+ SIGNAL_HANDLER
+ if(force_hidden == FALSE)
+ return
+ unhide_plane(source)
+
+/atom/movable/screen/plane_master/rendering_plate/masked_game_plate/proc/fov_disabled(mob/source)
+ SIGNAL_HANDLER
+ hide_plane(source)
+
// Blackness renders weird when you view down openspace, because of transforms and borders and such
// This is a consequence of not using lummy's grouped transparency, but I couldn't get that to work without totally fucking up
// Sight flags, and shooting vis_contents usage to the moon. So we're doin it different.
@@ -293,7 +385,6 @@
plane = RENDER_PLANE_NON_GAME
render_relay_planes = list(RENDER_PLANE_MASTER)
-
/**
* Plane master proc called in Initialize() that creates relay objects, and sets them uo as needed
* Sets:
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index f520dd007d5..899ce562472 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -55,6 +55,10 @@
/atom/movable/screen/proc/component_click(atom/movable/screen/component_button/component, params)
return
+/// Returns the mob this is being displayed to, if any
+/atom/movable/screen/proc/get_mob()
+ return hud?.mymob
+
/atom/movable/screen/text
icon = null
icon_state = null
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 16791b7fd7e..09e4dec760c 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -317,7 +317,7 @@
/datum/config_entry/string/banappeals
/datum/config_entry/string/wikiurl
- default = "https://wiki.skyrat13.tk/w/index.php" //SKYRAT EDIT - Original: "http://www.tgstation13.org/wiki"
+ default = "https://wiki.skyrat13.space/index.php" //SKYRAT EDIT - Original: "http://www.tgstation13.org/wiki"
/datum/config_entry/string/forumurl
default = "http://tgstation13.org/phpBB/index.php"
diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm
index 365fdab921d..a3520ab1ee3 100644
--- a/code/controllers/globals.dm
+++ b/code/controllers/globals.dm
@@ -19,7 +19,7 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
controller_vars["vars"] = null
gvars_datum_in_built_vars = controller_vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order))
-#if DM_VERSION >= 515 && DM_BUILD > 1620
+#if MIN_COMPILER_VERSION >= 515 && MIN_COMPILER_BUILD > 1620
#warn datum.vars hanging a ref should now be fixed, there should be no reason to remove the vars list from our controller's vars list anymore
#endif
QDEL_IN(exclude_these, 0) //signal logging isn't ready
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm
similarity index 80%
rename from code/game/gamemodes/dynamic/dynamic.dm
rename to code/controllers/subsystem/dynamic/dynamic.dm
index 01d4149369f..850b5e57b89 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/controllers/subsystem/dynamic/dynamic.dm
@@ -20,7 +20,11 @@ GLOBAL_LIST_EMPTY(dynamic_station_traits)
/// Rulesets which have been forcibly enabled or disabled
GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
-/datum/game_mode/dynamic
+SUBSYSTEM_DEF(dynamic)
+ name = "Dynamic"
+ flags = SS_NO_INIT
+ wait = 1 SECONDS
+
// Threat logging vars
/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
var/threat_level = 0
@@ -193,7 +197,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
VAR_PRIVATE/next_midround_injection
-/datum/game_mode/dynamic/admin_panel()
+/datum/controller/subsystem/dynamic/proc/admin_panel()
var/list/dat = list("Game Mode Panel
Game Mode Panel
")
dat += "Dynamic Mode \[VV\]\[Refresh\] "
dat += "Threat Level: [threat_level] "
@@ -231,7 +235,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
-/datum/game_mode/dynamic/Topic(href, href_list)
+/datum/controller/subsystem/dynamic/Topic(href, href_list)
if (..()) // Sanity, maybe ?
return
if(!check_rights(R_ADMIN))
@@ -289,8 +293,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
admin_panel() // Refreshes the window
-// Checks if there are HIGH_IMPACT_RULESETs and calls the rule's round_result() proc
-/datum/game_mode/dynamic/set_round_result()
+// Set result and news report here
+/datum/controller/subsystem/dynamic/proc/set_round_result()
// If it got to this part, just pick one high impact ruleset if it exists
for(var/datum/dynamic_ruleset/rule in executed_rules)
if(rule.flags & HIGH_IMPACT_RULESET)
@@ -299,9 +303,20 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(SSticker.news_report)
return
- return ..()
+ SSticker.mode_result = "undefined"
-/datum/game_mode/dynamic/proc/send_intercept()
+ // Something nuked the station - it wasn't nuke ops (they set their own via their rulset)
+ if(GLOB.station_was_nuked)
+ SSticker.news_report = STATION_NUKED
+
+ if(SSsupermatter_cascade.cascade_initiated)
+ SSticker.news_report = SUPERMATTER_CASCADE
+
+ // Only show this one if we have nothing better to show
+ if(EMERGENCY_ESCAPED_OR_ENDGAMED && !SSticker.news_report)
+ SSticker.news_report = SSshuttle.emergency?.is_hijacked() ? SHUTTLE_HIJACK : STATION_EVACUATED
+
+/datum/controller/subsystem/dynamic/proc/send_intercept()
if(SScommunications.block_command_report) //If we don't want the report to be printed just yet, we put it off until it's ready
addtimer(CALLBACK(src, PROC_REF(send_intercept)), 10 SECONDS)
return
@@ -316,22 +331,46 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
min_threat = min(ruleset.cost, min_threat)
var/greenshift = GLOB.dynamic_forced_extended || (threat_level < min_threat && shown_threat < min_threat) //if both shown and real threat are below any ruleset, its extended time
- generate_station_goals(greenshift)
- . += generate_station_goal_report()
- . += generate_station_trait_report()
+ generate_station_goals(greenshift ? INFINITY : CONFIG_GET(number/station_goal_budget))
+
+ if (GLOB.station_goals.len > 0)
+ var/list/texts = list("Special Orders for [station_name()]: ")
+ for(var/datum/station_goal/station_goal as anything in GLOB.station_goals)
+ station_goal.on_report()
+ texts += station_goal.get_report()
+
+ . += texts.Join("")
+
+ var/list/trait_list_strings = list()
+ for(var/datum/station_trait/station_trait as anything in SSstation.station_traits)
+ if(!station_trait.show_in_report)
+ continue
+ trait_list_strings += "[station_trait.get_report()] "
+ if(trait_list_strings.len > 0)
+ . += "Identified shift divergencies: " + trait_list_strings.Join()
+
if(length(SScommunications.command_report_footnotes))
- . += generate_report_footnote()
+ var/footnote_pile = ""
+
+ for(var/datum/command_footnote/footnote in SScommunications.command_report_footnotes)
+ footnote_pile += "[footnote.message] "
+ footnote_pile += "[footnote.signature] "
+ footnote_pile += " "
+
+ . += "Additional Notes:
" + footnote_pile
print_command_report(., "[command_name()] Status Summary", announce=FALSE)
if(greenshift)
- priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no credible threats to [station_name()]. All station construction projects have been authorized. Have a secure shift!", "Security Report", SSstation.announcer.get_rand_report_sound())
+ priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no credible threats to [station_name()]. All station construction projects have been authorized. Have a secure shift!", "Security Report", SSstation.announcer.get_rand_report_sound(), color_override = "green")
else
- priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", ANNOUNCER_INTERCEPT)
if(SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_BLUE)
- SSsecurity_level.set_level(SEC_LEVEL_BLUE)
+ SSsecurity_level.set_level(SEC_LEVEL_BLUE, announce = FALSE)
+ priority_announce("[SSsecurity_level.current_security_level.elevating_to_announcement]\n\nA summary has been copied and printed to all communications consoles.", "Security level elevated.", ANNOUNCER_INTERCEPT, color_override = SSsecurity_level.current_security_level.announcement_color)
+
+ return .
/// Generate the advisory level depending on the shown threat level.
-/datum/game_mode/dynamic/proc/generate_advisory_level()
+/datum/controller/subsystem/dynamic/proc/generate_advisory_level()
var/advisory_string = ""
if (prob(PULSAR_REPORT_CHANCE))
if(HAS_TRAIT(SSstation, STATION_TRAIT_BANANIUM_SHIPMENTS))
@@ -376,7 +415,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return advisory_string
-/datum/game_mode/dynamic/proc/show_threatlog(mob/admin)
+/datum/controller/subsystem/dynamic/proc/show_threatlog(mob/admin)
if(!SSticker.HasRoundStarted())
tgui_alert(usr, "The round hasn't started yet!")
return
@@ -395,7 +434,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
usr << browse(out.Join(), "window=threatlog;size=700x500")
/// Generates the threat level using lorentz distribution and assigns peaceful_percentage.
-/datum/game_mode/dynamic/proc/generate_threat()
+/datum/controller/subsystem/dynamic/proc/generate_threat()
// At lower pop levels we run a Liner Interpolation against the max threat based proportionally on the number
// of players ready. This creates a balanced lorentz curve within a smaller range than 0 to max_threat_level.
var/calculated_max_threat = (SSticker.totalPlayersReady < low_pop_player_threshold) ? LERP(low_pop_maximum_threat, max_threat_level, SSticker.totalPlayersReady / low_pop_player_threshold) : max_threat_level
@@ -410,12 +449,12 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
peaceful_percentage = (threat_level/max_threat_level)*100
/// Generates the midround and roundstart budgets
-/datum/game_mode/dynamic/proc/generate_budgets()
+/datum/controller/subsystem/dynamic/proc/generate_budgets()
round_start_budget = lorentz_to_amount(roundstart_split_curve_centre, roundstart_split_curve_width, threat_level, 0.1)
initial_round_start_budget = round_start_budget
mid_round_budget = threat_level - round_start_budget
-/datum/game_mode/dynamic/proc/setup_parameters()
+/datum/controller/subsystem/dynamic/proc/setup_parameters()
log_dynamic("Dynamic mode parameters for the round:")
log_dynamic("Centre is [threat_curve_centre], Width is [threat_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
log_dynamic("Stacking limit is [GLOB.dynamic_stacking_limit].")
@@ -448,17 +487,18 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
)
return TRUE
-/datum/game_mode/dynamic/proc/setup_shown_threat()
+/datum/controller/subsystem/dynamic/proc/setup_shown_threat()
if (prob(FAKE_REPORT_CHANCE))
shown_threat = rand(1, 100)
else
shown_threat = clamp(threat_level + rand(REPORT_NEG_DIVERGENCE, REPORT_POS_DIVERGENCE), 0, 100)
-/datum/game_mode/dynamic/proc/set_cooldowns()
+/datum/controller/subsystem/dynamic/proc/set_cooldowns()
var/latejoin_injection_cooldown_middle = 0.5*(latejoin_delay_max + latejoin_delay_min)
latejoin_injection_cooldown = round(clamp(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), latejoin_delay_min, latejoin_delay_max)) + world.time
-/datum/game_mode/dynamic/pre_setup()
+// Called BEFORE everyone is equipped with their job
+/datum/controller/subsystem/dynamic/proc/pre_setup()
if(CONFIG_GET(flag/dynamic_config_enabled))
var/json_file = file("[global.config.directory]/dynamic.json")
if(fexists(json_file))
@@ -517,29 +557,102 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
candidates.Cut()
return TRUE
-/datum/game_mode/dynamic/post_setup(report)
+// Called AFTER everyone is equipped with their job
+/datum/controller/subsystem/dynamic/proc/post_setup(report)
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
rule.candidates.Cut() // The rule should not use candidates at this point as they all are null.
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/game_mode/dynamic/, execute_roundstart_rule), rule), rule.delay)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/dynamic/, execute_roundstart_rule), rule), rule.delay)
if (!CONFIG_GET(flag/no_intercept_report))
addtimer(CALLBACK(src, PROC_REF(send_intercept)), rand(waittime_l, waittime_h))
-
//SKYRAT EDIT START - DIVERGENCY/GOALS REPORT
// else // BUBBER EDIT REMOVAL
// addtimer(CALLBACK(src, PROC_REF(send_trait_report)), rand(waittime_l, waittime_h)) // BUBBER EDIT REMOVAL
//SKYRAT EDIT END
+ addtimer(CALLBACK(src, PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME)
+
+ if(CONFIG_GET(flag/reopen_roundstart_suicide_roles))
+ var/delay = CONFIG_GET(number/reopen_roundstart_suicide_roles_delay)
+ if(delay)
+ delay *= (1 SECONDS)
+ else
+ delay = (4 MINUTES) //default to 4 minutes if the delay isn't defined.
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reopen_roundstart_suicide_roles)), delay)
+
+ if(SSdbcore.Connect())
+ var/list/to_set = list()
+ var/arguments = list()
+ if(GLOB.revdata.originmastercommit)
+ to_set += "commit_hash = :commit_hash"
+ arguments["commit_hash"] = GLOB.revdata.originmastercommit
+ if(to_set.len)
+ arguments["round_id"] = GLOB.round_id
+ var/datum/db_query/query_round_game_mode = SSdbcore.NewQuery(
+ "UPDATE [format_table_name("round")] SET [to_set.Join(", ")] WHERE id = :round_id",
+ arguments
+ )
+ query_round_game_mode.Execute()
+ qdel(query_round_game_mode)
+ return TRUE
+
+/datum/controller/subsystem/dynamic/proc/display_roundstart_logout_report()
+ var/list/msg = list("[span_boldnotice("Roundstart logout report")]\n\n")
+ for(var/i in GLOB.mob_living_list)
+ var/mob/living/L = i
+ var/mob/living/carbon/C = L
+ if (istype(C) && !C.last_mind)
+ continue // never had a client
+
+ if(L.ckey && !GLOB.directory[L.ckey])
+ msg += "[L.name] ([L.key]), the [L.job] (Disconnected)\n"
+
+
+ if(L.ckey && L.client)
+ var/failed = FALSE
+ if(L.client.inactivity >= ROUNDSTART_LOGOUT_AFK_THRESHOLD) //Connected, but inactive (alt+tabbed or something)
+ msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n"
+ failed = TRUE //AFK client
+ if(!failed && L.stat)
+ if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
+ msg += "[L.name] ([L.key]), the [L.job] ([span_boldannounce("Suicide")])\n"
+ failed = TRUE //Disconnected client
+ if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT))
+ msg += "[L.name] ([L.key]), the [L.job] (Dying)\n"
+ failed = TRUE //Unconscious
+ if(!failed && L.stat == DEAD)
+ msg += "[L.name] ([L.key]), the [L.job] (Dead)\n"
+ failed = TRUE //Dead
+
+ continue //Happy connected client
+ for(var/mob/dead/observer/D in GLOB.dead_mob_list)
+ if(D.mind && D.mind.current == L)
+ if(L.stat == DEAD)
+ if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Suicide")])\n"
+ continue //Disconnected client
+ else
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
+ continue //Dead mob, ghost abandoned
+ else
+ if(D.can_reenter_corpse)
+ continue //Adminghost, or cult/wizard ghost
+ else
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Ghosted")])\n"
+ continue //Ghosted while alive
+
+ var/concatenated_message = msg.Join()
+ log_admin(concatenated_message)
+ to_chat(GLOB.admins, concatenated_message)
- ..()
/// Initializes the internal ruleset variables
-/datum/game_mode/dynamic/proc/setup_rulesets()
+/datum/controller/subsystem/dynamic/proc/setup_rulesets()
midround_rules = init_rulesets(/datum/dynamic_ruleset/midround)
latejoin_rules = init_rulesets(/datum/dynamic_ruleset/latejoin)
/// Returns a list of the provided rulesets.
/// Configures their variables to match config.
-/datum/game_mode/dynamic/proc/init_rulesets(ruleset_subtype)
+/datum/controller/subsystem/dynamic/proc/init_rulesets(ruleset_subtype)
var/list/rulesets = list()
for (var/datum/dynamic_ruleset/ruleset_type as anything in subtypesof(ruleset_subtype))
@@ -556,14 +669,13 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return rulesets
/// A simple roundstart proc used when dynamic_forced_roundstart_ruleset has rules in it.
-/datum/game_mode/dynamic/proc/rigged_roundstart()
+/datum/controller/subsystem/dynamic/proc/rigged_roundstart()
message_admins("[GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
log_dynamic("[GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
for (var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
configure_ruleset(rule)
message_admins("Drafting players for forced ruleset [rule.name].")
log_dynamic("Drafting players for forced ruleset [rule.name].")
- rule.mode = src
rule.acceptable(roundstart_pop_ready, threat_level) // Assigns some vars in the modes, running it here for consistency
rule.candidates = candidates.Copy()
rule.trim_candidates()
@@ -577,7 +689,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
spend_roundstart_budget(picking_roundstart_rule(rule, scaled_times, forced = TRUE))
-/datum/game_mode/dynamic/proc/roundstart(list/roundstart_rules)
+/datum/controller/subsystem/dynamic/proc/roundstart(list/roundstart_rules)
if (GLOB.dynamic_forced_extended)
log_dynamic("Starting a round of forced extended.")
return TRUE
@@ -636,7 +748,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
update_log()
/// Initializes the round start ruleset provided to it. Returns how much threat to spend.
-/datum/game_mode/dynamic/proc/picking_roundstart_rule(datum/dynamic_ruleset/roundstart/ruleset, scaled_times = 0, forced = FALSE)
+/datum/controller/subsystem/dynamic/proc/picking_roundstart_rule(datum/dynamic_ruleset/roundstart/ruleset, scaled_times = 0, forced = FALSE)
log_dynamic("Picked a ruleset: [ruleset.name], scaled [scaled_times] times")
ruleset.trim_candidates()
@@ -655,7 +767,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return 0
/// Mainly here to facilitate delayed rulesets. All roundstart rulesets are executed with a timered callback to this proc.
-/datum/game_mode/dynamic/proc/execute_roundstart_rule(sent_rule)
+/datum/controller/subsystem/dynamic/proc/execute_roundstart_rule(sent_rule)
var/datum/dynamic_ruleset/rule = sent_rule
if(rule.execute())
if(rule.persistent)
@@ -670,7 +782,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return FALSE
/// An experimental proc to allow admins to call rules on the fly or have rules call other rules.
-/datum/game_mode/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE, ignore_cost = FALSE)
+/datum/controller/subsystem/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE, ignore_cost = FALSE)
var/datum/dynamic_ruleset/midround/new_rule
if(ispath(ruletype))
new_rule = new ruletype() // You should only use it to call midround rules though.
@@ -719,7 +831,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
new_rule.forget_startup()
return FALSE
-/datum/game_mode/dynamic/process()
+/datum/controller/subsystem/dynamic/fire()
for (var/datum/dynamic_ruleset/rule in current_rules)
if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing.
current_rules -= rule
@@ -727,14 +839,14 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
try_midround_roll()
/// Removes type from the list
-/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
+/datum/controller/subsystem/dynamic/proc/remove_from_list(list/type_list, type)
for(var/I in type_list)
if(istype(I, type))
type_list -= I
return type_list
/// Checks if a type in blocking_list is in rule_list.
-/datum/game_mode/dynamic/proc/check_blocking(list/blocking_list, list/rule_list)
+/datum/controller/subsystem/dynamic/proc/check_blocking(list/blocking_list, list/rule_list)
if(blocking_list.len > 0)
for(var/blocking in blocking_list)
for(var/_executed in rule_list)
@@ -744,7 +856,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return TRUE
return FALSE
-/datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer)
+/// Handles late-join antag assignments
+/datum/controller/subsystem/dynamic/proc/make_antag_chance(mob/living/carbon/human/newPlayer)
if (GLOB.dynamic_forced_extended)
return
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left
@@ -794,7 +907,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
* This proc handles the execution of a latejoin ruleset, including removing it from latejoin rulesets if not repeatable,
* upping the injection cooldown, and starting a timer to execute the ruleset on delay.
*/
-/datum/game_mode/dynamic/proc/handle_executing_latejoin(datum/dynamic_ruleset/ruleset, mob/living/carbon/human/only_candidate, forced = FALSE)
+/datum/controller/subsystem/dynamic/proc/handle_executing_latejoin(datum/dynamic_ruleset/ruleset, mob/living/carbon/human/only_candidate, forced = FALSE)
ruleset.candidates = list(only_candidate)
ruleset.trim_candidates()
ruleset.load_templates()
@@ -814,13 +927,16 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return TRUE
/// Apply configurations to rule.
-/datum/game_mode/dynamic/proc/configure_ruleset(datum/dynamic_ruleset/ruleset)
+/datum/controller/subsystem/dynamic/proc/configure_ruleset(datum/dynamic_ruleset/ruleset)
var/rule_conf = LAZYACCESSASSOC(configuration, ruleset.ruletype, ruleset.name)
for(var/variable in rule_conf)
if(!(variable in ruleset.vars))
stack_trace("Invalid dynamic configuration variable [variable] in [ruleset.ruletype] [ruleset.name].")
continue
ruleset.vars[variable] = rule_conf[variable]
+ ruleset.restricted_roles |= SSstation.antag_restricted_roles
+ if(length(ruleset.protected_roles)) //if we care to protect any role, we should protect station trait roles too
+ ruleset.protected_roles |= SSstation.antag_protected_roles
if(CONFIG_GET(flag/protect_roles_from_antagonist))
ruleset.restricted_roles |= ruleset.protected_roles
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
@@ -839,14 +955,14 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
// SKYRAT EDIT END
/// Get station traits and call for their config
-/datum/game_mode/dynamic/proc/configure_station_trait_costs()
+/datum/controller/subsystem/dynamic/proc/configure_station_trait_costs()
if(!CONFIG_GET(flag/dynamic_config_enabled))
return
for(var/datum/station_trait/station_trait as anything in GLOB.dynamic_station_traits)
configure_station_trait(station_trait)
/// Apply configuration for station trait costs
-/datum/game_mode/dynamic/proc/configure_station_trait(datum/station_trait/station_trait)
+/datum/controller/subsystem/dynamic/proc/configure_station_trait(datum/station_trait/station_trait)
var/list/station_trait_config = LAZYACCESSASSOC(configuration, "Station", station_trait.dynamic_threat_id)
var/cost = station_trait_config["cost"]
@@ -859,31 +975,31 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
GLOB.dynamic_station_traits[station_trait] = cost
/// Refund threat, but no more than threat_level.
-/datum/game_mode/dynamic/proc/refund_threat(regain)
+/datum/controller/subsystem/dynamic/proc/refund_threat(regain)
mid_round_budget = min(threat_level, mid_round_budget + regain)
/// Generate threat and increase the threat_level if it goes beyond, capped at 100
-/datum/game_mode/dynamic/proc/create_threat(gain, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/create_threat(gain, list/threat_log, reason)
mid_round_budget = min(100, mid_round_budget + gain)
if(mid_round_budget > threat_level)
threat_level = mid_round_budget
for(var/list/logs in threat_log)
log_threat(gain, logs, reason)
-/datum/game_mode/dynamic/proc/log_threat(threat_change, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/log_threat(threat_change, list/threat_log, reason)
var/gain_or_loss = "+"
if(threat_change < 0)
gain_or_loss = "-"
threat_log += "Threat [gain_or_loss][abs(threat_change)] - [reason]."
/// Expend round start threat, can't fall under 0.
-/datum/game_mode/dynamic/proc/spend_roundstart_budget(cost, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/spend_roundstart_budget(cost, list/threat_log, reason)
round_start_budget = max(round_start_budget - cost,0)
if (!isnull(threat_log))
log_threat(-cost, threat_log, reason)
/// Expend midround threat, can't fall under 0.
-/datum/game_mode/dynamic/proc/spend_midround_budget(cost, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/spend_midround_budget(cost, list/threat_log, reason)
mid_round_budget = max(mid_round_budget - cost,0)
if (!isnull(threat_log))
log_threat(-cost, threat_log, reason)
@@ -896,7 +1012,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
* threat and then rounded to the nearest interval.
* rand() calls without arguments returns a value between 0 and 1, allowing for smaller intervals.
*/
-/datum/game_mode/dynamic/proc/lorentz_to_amount(centre = 0, scale = 1.8, max_threat = 100, interval = 1)
+/datum/controller/subsystem/dynamic/proc/lorentz_to_amount(centre = 0, scale = 1.8, max_threat = 100, interval = 1)
var/location = RANDOM_DECIMAL(-MAXIMUM_DYN_DISTANCE, MAXIMUM_DYN_DISTANCE) * rand()
var/lorentz_result = LORENTZ_CUMULATIVE_DISTRIBUTION(centre, location, scale)
var/std_threat = lorentz_result * max_threat
@@ -905,6 +1021,44 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
var/upper_deviation = max((max_threat - std_threat) * (centre-location)/MAXIMUM_DYN_DISTANCE, 0)
return clamp(round(std_threat + upper_deviation - lower_deviation, interval), 0, 100)
+/proc/reopen_roundstart_suicide_roles()
+ var/include_command = CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_positions)
+ var/list/reopened_jobs = list()
+
+ for(var/mob/living/quitter in GLOB.suicided_mob_list)
+ var/datum/job/job = SSjob.GetJob(quitter.job)
+ if(!job || !(job.job_flags & JOB_REOPEN_ON_ROUNDSTART_LOSS))
+ continue
+ if(!include_command && job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ continue
+ job.current_positions = max(job.current_positions - 1, 0)
+ reopened_jobs += quitter.job
+
+ if(CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_report))
+ if(reopened_jobs.len)
+ var/reopened_job_report_positions
+ for(var/dead_dudes_job in reopened_jobs)
+ reopened_job_report_positions = "[reopened_job_report_positions ? "[reopened_job_report_positions]\n":""][dead_dudes_job]"
+
+ var/suicide_command_report = {"
+ [command_name()] Human Resources Board
+ Notice of Personnel Change
+ To personnel management staff aboard [station_name()]:
+ Our medical staff have detected a series of anomalies in the vital sensors
+ of some of the staff aboard your station.
+ Further investigation into the situation on our end resulted in us discovering
+ a series of rather... unforturnate decisions that were made on the part of said staff.
+ As such, we have taken the liberty to automatically reopen employment opportunities for the positions of the crew members
+ who have decided not to partake in our research. We will be forwarding their cases to our employment review board
+ to determine their eligibility for continued service with the company (and of course the
+ continued storage of cloning records within the central medical backup server.)
+ The following positions have been reopened on our behalf:
+ [reopened_job_report_positions]
+ "}
+
+ print_command_report(suicide_command_report, "Central Command Personnel Update")
+
+
#undef MAXIMUM_DYN_DISTANCE
#undef FAKE_REPORT_CHANCE
diff --git a/code/game/gamemodes/dynamic/dynamic_hijacking.dm b/code/controllers/subsystem/dynamic/dynamic_hijacking.dm
similarity index 86%
rename from code/game/gamemodes/dynamic/dynamic_hijacking.dm
rename to code/controllers/subsystem/dynamic/dynamic_hijacking.dm
index 62c9a5d2124..7577cbcd84f 100644
--- a/code/game/gamemodes/dynamic/dynamic_hijacking.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_hijacking.dm
@@ -1,7 +1,7 @@
-/datum/game_mode/dynamic/proc/setup_hijacking()
+/datum/controller/subsystem/dynamic/proc/setup_hijacking()
RegisterSignal(SSdcs, COMSIG_GLOB_PRE_RANDOM_EVENT, PROC_REF(on_pre_random_event))
-/datum/game_mode/dynamic/proc/on_pre_random_event(datum/source, datum/round_event_control/round_event_control)
+/datum/controller/subsystem/dynamic/proc/on_pre_random_event(datum/source, datum/round_event_control/round_event_control)
SIGNAL_HANDLER
if (!round_event_control.dynamic_should_hijack)
return
diff --git a/code/game/gamemodes/dynamic/dynamic_logging.dm b/code/controllers/subsystem/dynamic/dynamic_logging.dm
similarity index 95%
rename from code/game/gamemodes/dynamic/dynamic_logging.dm
rename to code/controllers/subsystem/dynamic/dynamic_logging.dm
index 7490894b526..16bd56a7303 100644
--- a/code/game/gamemodes/dynamic/dynamic_logging.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_logging.dm
@@ -69,7 +69,7 @@
return serialization
/// Updates the log for the current snapshots.
-/datum/game_mode/dynamic/proc/update_log()
+/datum/controller/subsystem/dynamic/proc/update_log()
var/list/serialized = list()
serialized["threat_level"] = threat_level
serialized["round_start_budget"] = initial_round_start_budget
@@ -84,7 +84,7 @@
rustg_file_write(json_encode(serialized), "[GLOB.log_directory]/dynamic.json")
/// Creates a new snapshot with the given rulesets chosen, and writes to the JSON output.
-/datum/game_mode/dynamic/proc/new_snapshot(datum/dynamic_ruleset/ruleset_chosen)
+/datum/controller/subsystem/dynamic/proc/new_snapshot(datum/dynamic_ruleset/ruleset_chosen)
var/datum/dynamic_snapshot/new_snapshot = new
new_snapshot.remaining_threat = mid_round_budget
diff --git a/code/game/gamemodes/dynamic/dynamic_midround_rolling.dm b/code/controllers/subsystem/dynamic/dynamic_midround_rolling.dm
similarity index 95%
rename from code/game/gamemodes/dynamic/dynamic_midround_rolling.dm
rename to code/controllers/subsystem/dynamic/dynamic_midround_rolling.dm
index c3e295ae875..968037b9fa2 100644
--- a/code/game/gamemodes/dynamic/dynamic_midround_rolling.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_midround_rolling.dm
@@ -1,7 +1,7 @@
/// Returns the world.time of the next midround injection.
/// Will return a cached result from `next_midround_injection`, the variable.
/// If that variable is null, will generate a new one.
-/datum/game_mode/dynamic/proc/next_midround_injection()
+/datum/controller/subsystem/dynamic/proc/next_midround_injection()
if (!isnull(next_midround_injection))
return next_midround_injection
@@ -16,7 +16,7 @@
return last_midround_injection_attempt + distance
-/datum/game_mode/dynamic/proc/try_midround_roll()
+/datum/controller/subsystem/dynamic/proc/try_midround_roll()
if (!forced_injection && next_midround_injection() > world.time)
return
@@ -90,7 +90,7 @@
log_dynamic_and_announce("No midround rulesets could be drafted. ([heavy_light_log_count])")
/// Gets the chance for a heavy ruleset midround injection, the dry_run argument is only used for forced injection.
-/datum/game_mode/dynamic/proc/get_heavy_midround_injection_chance(dry_run)
+/datum/controller/subsystem/dynamic/proc/get_heavy_midround_injection_chance(dry_run)
var/chance_modifier = 1
var/next_midround_roll = next_midround_injection() - SSticker.round_start_time
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm
similarity index 92%
rename from code/game/gamemodes/dynamic/dynamic_rulesets.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets.dm
index b334360636a..84b6665d822 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm
@@ -7,7 +7,7 @@
var/ruletype = ""
/// If set to TRUE, the rule won't be discarded after being executed, and dynamic will call rule_process() every time it ticks.
var/persistent = FALSE
- /// If set to TRUE, dynamic mode will be able to draft this ruleset again later on. (doesn't apply for roundstart rules)
+ /// If set to TRUE, dynamic will be able to draft this ruleset again later on. (doesn't apply for roundstart rules)
var/repeatable = FALSE
/// If set higher than 0 decreases weight by itself causing the ruleset to appear less often the more it is repeated.
var/repeatable_weight_decrease = 2
@@ -41,7 +41,7 @@
var/required_candidates = 0
/// 0 -> 9, probability for this rule to be picked against other rules. If zero this will effectively disable the rule.
var/weight = 5
- /// Threat cost for this rule, this is decreased from the mode's threat when the rule is executed.
+ /// Threat cost for this rule, this is decreased from the threat level when the rule is executed.
var/cost = 0
/// Cost per level the rule scales up.
var/scaling_cost = 0
@@ -51,13 +51,11 @@
var/total_cost = 0
/// A flag that determines how the ruleset is handled. Check __DEFINES/dynamic.dm for an explanation of the accepted values.
var/flags = NONE
- /// Pop range per requirement. If zero defaults to mode's pop_per_requirement.
+ /// Pop range per requirement. If zero defaults to dynamic's pop_per_requirement.
var/pop_per_requirement = 0
/// Requirements are the threat level requirements per pop range.
/// With the default values, The rule will never get drafted below 10 threat level (aka: "peaceful extended"), and it requires a higher threat level at lower pops.
var/list/requirements = list(40,30,20,10,10,10,10,10,10,10)
- /// Reference to the mode, use this instead of SSticker.mode.
- var/datum/game_mode/dynamic/mode = null
/// If a role is to be considered another for the purpose of banning.
var/antag_flag_override = null
/// If set, will check this preference instead of antag_flag.
@@ -92,8 +90,6 @@
// side effects here. Dynamic rulesets should be stateless anyway.
SHOULD_NOT_OVERRIDE(TRUE)
- mode = SSticker.mode
-
..()
/datum/dynamic_ruleset/roundstart // One or more of those drafted at roundstart
@@ -135,7 +131,7 @@
/// Sets the current threat indices and returns true if we're inside of them
/datum/dynamic_ruleset/proc/is_valid_threat(population, threat_level)
- pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : mode.pop_per_requirement
+ pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : SSdynamic.pop_per_requirement
indice_pop = min(requirements.len,round(population/pop_per_requirement)+1)
return threat_level >= requirements[indice_pop]
@@ -148,14 +144,14 @@
return 0
var/antag_fraction = 0
- for(var/_ruleset in (mode.executed_rules + list(src))) // we care about the antags we *will* assign, too
+ for(var/_ruleset in (SSdynamic.executed_rules + list(src))) // we care about the antags we *will* assign, too
var/datum/dynamic_ruleset/ruleset = _ruleset
- antag_fraction += ((1 + ruleset.scaled_times) * ruleset.get_antag_cap(population)) / mode.roundstart_pop_ready
+ antag_fraction += ((1 + ruleset.scaled_times) * ruleset.get_antag_cap(population)) / SSdynamic.roundstart_pop_ready
for(var/i in 1 to max_scale)
if(antag_fraction < 0.25)
scaled_times += 1
- antag_fraction += get_antag_cap(population) / mode.roundstart_pop_ready // we added new antags, gotta update the %
+ antag_fraction += get_antag_cap(population) / SSdynamic.roundstart_pop_ready // we added new antags, gotta update the %
return scaled_times * scaling_cost
@@ -170,7 +166,7 @@
/datum/dynamic_ruleset/proc/rule_process()
return
-/// Called on game mode pre_setup for roundstart rulesets.
+/// Called on pre_setup for roundstart rulesets.
/// Do everything you need to do before job is assigned here.
/// IMPORTANT: ASSIGN special_role HERE
/datum/dynamic_ruleset/proc/pre_execute()
@@ -206,15 +202,15 @@
/// Runs from gamemode process() if ruleset fails to start, like delayed rulesets not getting valid candidates.
/// This one only handles refunding the threat, override in ruleset to clean up the rest.
/datum/dynamic_ruleset/proc/clean_up()
- mode.refund_threat(cost + (scaled_times * scaling_cost))
- mode.threat_log += "[worldtime2text()]: [ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]. Failed to execute."
+ SSdynamic.refund_threat(cost + (scaled_times * scaling_cost))
+ SSdynamic.threat_log += "[worldtime2text()]: [ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]. Failed to execute."
/// Gets weight of the ruleset
/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0
/// Note: If you don't want repeatable rulesets to decrease their weight use the weight variable directly
/datum/dynamic_ruleset/proc/get_weight()
if(repeatable && weight > 1 && repeatable_weight_decrease > 0)
- for(var/datum/dynamic_ruleset/DR in mode.executed_rules)
+ for(var/datum/dynamic_ruleset/DR in SSdynamic.executed_rules)
if(istype(DR, type))
weight = max(weight-repeatable_weight_decrease,1)
return weight
@@ -233,7 +229,7 @@
/datum/dynamic_ruleset/proc/trim_candidates()
return
-/// Set mode result and news report here.
+/// Set mode_result and news report here.
/// Only called if ruleset is flagged as HIGH_IMPACT_RULESET
/datum/dynamic_ruleset/proc/round_result()
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
similarity index 98%
rename from code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
index 3c35b16a5d1..4f91e2a8a43 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
@@ -37,7 +37,7 @@
if (M.mind && (M.mind.assigned_role.title in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role.title in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
- var/threat = round(mode.threat_level/10)
+ var/threat = round(SSdynamic.threat_level/10)
var/ruleset_forced = (GLOB.dynamic_forced_rulesets[type] || RULESET_NOT_FORCED) == RULESET_FORCE_ENABLED
if (!ruleset_forced && job_check < required_enemies[threat])
log_dynamic("FAIL: [src] is not ready, because there are not enough enemies: [required_enemies[threat]] needed, [job_check] found")
@@ -136,7 +136,7 @@
return FALSE
var/head_check = 0
for(var/mob/player in GLOB.alive_player_list)
- if (player.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if (player.mind.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
head_check++
return (head_check >= required_heads_of_staff)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
similarity index 97%
rename from code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
index 4f04c2c1aa4..de995a201ad 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
@@ -116,7 +116,7 @@
if (M.mind && (M.mind.assigned_role.title in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role.title in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
- var/threat = round(mode.threat_level/10)
+ var/threat = round(SSdynamic.threat_level/10)
var/ruleset_forced = (GLOB.dynamic_forced_rulesets[type] || RULESET_NOT_FORCED) == RULESET_FORCE_ENABLED
if (!ruleset_forced && job_check < required_enemies[threat])
log_dynamic("FAIL: [src] is not ready, because there are not enough enemies: [required_enemies[threat]] needed, [job_check] found")
@@ -140,23 +140,23 @@
message_admins("Possible volunteers was 0. This shouldn't appear, because of ready(), unless you forced it!")
return
- mode.log_dynamic_and_announce("Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
- candidates = poll_ghost_candidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag_override, antag_flag || antag_flag_override, poll_time = 300)
+ SSdynamic.log_dynamic_and_announce("Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
+ candidates = SSpolling.poll_ghost_candidates("Looking for volunteers to become [antag_flag] for [name]", check_jobban = antag_flag_override, role = antag_flag || antag_flag_override, poll_time = 30 SECONDS, pic_source = /obj/structure/sign/poster/contraband/syndicate_recruitment, role_name_text = antag_flag)
if(!candidates || candidates.len <= 0)
- mode.log_dynamic_and_announce("The ruleset [name] received no applications.")
- mode.executed_rules -= src
+ SSdynamic.log_dynamic_and_announce("The ruleset [name] received no applications.")
+ SSdynamic.executed_rules -= src
attempt_replacement()
return
- mode.log_dynamic_and_announce("[candidates.len] players volunteered for [name].")
+ SSdynamic.log_dynamic_and_announce("[candidates.len] players volunteered for [name].")
review_applications()
/// Here is where you can check if your ghost applicants are valid for the ruleset.
/// Called by send_applications().
/datum/dynamic_ruleset/midround/from_ghosts/proc/review_applications()
if(candidates.len < required_applicants)
- mode.executed_rules -= src
+ SSdynamic.executed_rules -= src
return
for (var/i = 1, i <= required_candidates, i++)
if(candidates.len <= 0)
@@ -208,12 +208,12 @@
/datum/dynamic_ruleset/midround/from_ghosts/proc/attempt_replacement()
var/datum/dynamic_ruleset/midround/from_living/autotraitor/sleeper_agent = new
- mode.configure_ruleset(sleeper_agent)
+ SSdynamic.configure_ruleset(sleeper_agent)
- if (!mode.picking_specific_rule(sleeper_agent))
+ if (!SSdynamic.picking_specific_rule(sleeper_agent))
return
- mode.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator)
+ SSdynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator)
///subtype to handle checking players
/datum/dynamic_ruleset/midround/from_living
@@ -386,7 +386,7 @@
var/list/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat_level=0)
- if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in mode.executed_rules)
+ if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in SSdynamic.executed_rules)
return FALSE // Unavailable if nuke ops were already sent at roundstart
indice_pop = min(operative_cap.len, round(living_players.len/5)+1)
required_candidates = operative_cap[indice_pop]
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
similarity index 99%
rename from code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
index 68395062b95..c7d35e0a041 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
@@ -606,9 +606,9 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
. = ..()
message_admins("Starting a round of extended.")
log_game("Starting a round of extended.")
- mode.spend_roundstart_budget(mode.round_start_budget)
- mode.spend_midround_budget(mode.mid_round_budget)
- mode.threat_log += "[worldtime2text()]: Extended ruleset set threat to 0."
+ SSdynamic.spend_roundstart_budget(SSdynamic.round_start_budget)
+ SSdynamic.spend_midround_budget(SSdynamic.mid_round_budget)
+ SSdynamic.threat_log += "[worldtime2text()]: Extended ruleset set threat to 0."
return TRUE
//////////////////////////////////////////////
diff --git a/code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm b/code/controllers/subsystem/dynamic/dynamic_unfavorable_situation.dm
similarity index 90%
rename from code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm
rename to code/controllers/subsystem/dynamic/dynamic_unfavorable_situation.dm
index 1be6511433c..994f2e3f5de 100644
--- a/code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_unfavorable_situation.dm
@@ -2,12 +2,12 @@
/// This is used for impactful events like traitors hacking and creating more threat, or a revolutions victory.
/// It tries to spawn a heavy midround if possible, otherwise it will trigger a "bad" random event after a short period.
/// Calling this function will not use up any threat.
-/datum/game_mode/dynamic/proc/unfavorable_situation()
+/datum/controller/subsystem/dynamic/proc/unfavorable_situation()
SHOULD_NOT_SLEEP(TRUE)
INVOKE_ASYNC(src, PROC_REF(_unfavorable_situation))
-/datum/game_mode/dynamic/proc/_unfavorable_situation()
+/datum/controller/subsystem/dynamic/proc/_unfavorable_situation()
var/static/list/unfavorable_random_events = list()
if (!length(unfavorable_random_events))
unfavorable_random_events = generate_unfavourable_events()
@@ -24,7 +24,7 @@
picking_specific_rule(heavy_ruleset, forced = TRUE, ignore_cost = TRUE)
/// Return a valid heavy dynamic ruleset, or an empty list if there's no time to run any rulesets
-/datum/game_mode/dynamic/proc/generate_unfavourable_heavy_rulesets()
+/datum/controller/subsystem/dynamic/proc/generate_unfavourable_heavy_rulesets()
if (EMERGENCY_PAST_POINT_OF_NO_RETURN)
return list()
@@ -58,7 +58,7 @@
return possible_heavies
/// Filter the below list by which events can actually run on this map
-/datum/game_mode/dynamic/proc/generate_unfavourable_events()
+/datum/controller/subsystem/dynamic/proc/generate_unfavourable_events()
var/static/list/unfavorable_random_events = list(
/datum/round_event_control/earthquake,
/datum/round_event_control/immovable_rod,
diff --git a/code/game/gamemodes/dynamic/readme.md b/code/controllers/subsystem/dynamic/readme.md
similarity index 92%
rename from code/game/gamemodes/dynamic/readme.md
rename to code/controllers/subsystem/dynamic/readme.md
index d663e88a239..ec21626945e 100644
--- a/code/game/gamemodes/dynamic/readme.md
+++ b/code/controllers/subsystem/dynamic/readme.md
@@ -12,10 +12,10 @@ This threat is split into two separate budgets--`round_start_budget` and `mid_ro
This split is done with a similar method, known as the ["lorentz distribution"](https://en.wikipedia.org/wiki/Cauchy_distribution), exists to create a bell curve that ensures that while most rounds will have a threat level around ~50, chaotic and tame rounds still exist for variety.
-The process of creating these numbers occurs in `/datum/game_mode/dynamic/proc/generate_threat` (for creating the threat level) and `/datum/game_mode/dynamic/proc/generate_budgets` (for splitting the threat level into budgets).
+The process of creating these numbers occurs in `/datum/controller/subsystem/dynamic/proc/generate_threat` (for creating the threat level) and `/datum/controller/subsystem/dynamic/proc/generate_budgets` (for splitting the threat level into budgets).
## Deciding roundstart threats
-In `/datum/game_mode/dynamic/proc/roundstart()` (called when no admin chooses the rulesets explicitly), Dynamic uses the available roundstart budget to pick threats. This is done through the following system:
+In `/datum/controller/subsystem/dynamic/proc/roundstart()` (called when no admin chooses the rulesets explicitly), Dynamic uses the available roundstart budget to pick threats. This is done through the following system:
- All roundstart rulesets (remember, `/datum/dynamic_ruleset/roundstart`) are put into an associative list with their weight as the values (`drafted_rules`).
- Until there is either no roundstart budget left, or until there is no ruleset we can choose from with the available threat, a `pickweight` is done based on the drafted_rules. If the same threat is picked twice, it will "scale up". The meaning of this depends on the ruleset itself, using the `scaled_times` variable; traitors for instance will create more the higher they scale.
@@ -24,7 +24,7 @@ In `/datum/game_mode/dynamic/proc/roundstart()` (called when no admin chooses th
- After all roundstart threats are chosen, `/datum/dynamic_ruleset/proc/picking_roundstart_rule` is called for each, passing in the ruleset and the number of times it is scaled.
- In this stage, `pre_execute` is called, which is the function that will determine what players get what antagonists. If this function returns FALSE for whatever reason (in the case of an error), then its threat is refunded.
-After this process is done, any leftover roundstart threat will be given to the existing midround budget (done in `/datum/game_mode/dynamic/pre_setup()`).
+After this process is done, any leftover roundstart threat will be given to the existing midround budget (done in `/datum/controller/subsystem/dynamic/pre_setup()`).
## Deciding midround threats
@@ -105,13 +105,13 @@ Midround - Rulesets have additional types
## Configuration and variables
### Configuration
-Configuration can be done through a `config/dynamic.json` file. One is provided as example in the codebase. This config file, loaded in `/datum/game_mode/dynamic/pre_setup()`, directly overrides the values in the codebase, and so is perfect for making some rulesets harder/easier to get, turning them off completely, changing how much they cost, etc.
+Configuration can be done through a `config/dynamic.json` file. One is provided as example in the codebase. This config file, loaded in `/datum/controller/subsystem/dynamic/pre_setup()`, directly overrides the values in the codebase, and so is perfect for making some rulesets harder/easier to get, turning them off completely, changing how much they cost, etc.
The format of this file is:
```json
{
"Dynamic": {
- /* Configuration in here will directly override `/datum/game_mode/dynamic` itself. */
+ /* Configuration in here will directly override `/datum/controller/subsystem/dynamic` itself. */
/* Keys are variable names, values are their new values. */
},
@@ -172,7 +172,7 @@ This is not a complete list--search "configurable" in this README to learn more.
The "Dynamic" key has the following configurable values:
- `pop_per_requirement` - The default value of `pop_per_requirement` for any ruleset that does not explicitly set it. Defaults to 6.
- `latejoin_delay_min`, `latejoin_delay_max` - The time range, in deciseconds (take your seconds, and multiply by 10), for a latejoin to attempt rolling. Once this timer is finished, a new one will be created within the same range.
- - Suppose you have a `latejoin_delay_min` of 600 (60 seconds, 1 minute) and a `latejoin_delay_max` of 1800 (180 seconds, 3 minutes). Once the round starts, a random number in this range will be picked--let's suppose 1.5 minutes. After 1.5 minutes, Dynamic will decide if a latejoin threat should be created (a probability of `/datum/game_mode/dynamic/proc/get_injection_chance()`). Regardless of its decision, a new timer will be started within the range of 1 to 3 minutes, repeatedly.
+ - Suppose you have a `latejoin_delay_min` of 600 (60 seconds, 1 minute) and a `latejoin_delay_max` of 1800 (180 seconds, 3 minutes). Once the round starts, a random number in this range will be picked--let's suppose 1.5 minutes. After 1.5 minutes, Dynamic will decide if a latejoin threat should be created (a probability of `/datum/controller/subsystem/dynamic/proc/get_injection_chance()`). Regardless of its decision, a new timer will be started within the range of 1 to 3 minutes, repeatedly.
- `threat_curve_centre` - A number between -5 and +5. A negative value will give a more peaceful round and a positive value will give a round with higher threat.
- `threat_curve_width` - A number between 0.5 and 4. Higher value will favour extreme rounds and lower value rounds closer to the average.
- `roundstart_split_curve_centre` - A number between -5 and +5. Equivalent to threat_curve_centre, but for the budget split. A negative value will weigh towards midround rulesets, and a positive value will weight towards roundstart ones.
@@ -187,7 +187,7 @@ Random events have the potential to be hijacked by Dynamic to keep the pace of m
`/datum/round_event_control/dynamic_should_hijack` is a variable to random events to allow Dynamic to hijack them, and defaults to FALSE. This is set to TRUE for random events that spawn antagonists.
-In `/datum/game_mode/dynamic/on_pre_random_event` (in `dynamic_hijacking.dm`), Dynamic hooks to random events. If the `dynamic_should_hijack` variable is TRUE, the following sequence of events occurs:
+In `/datum/controller/subsystem/dynamic/on_pre_random_event` (in `dynamic_hijacking.dm`), Dynamic hooks to random events. If the `dynamic_should_hijack` variable is TRUE, the following sequence of events occurs:

diff --git a/code/game/gamemodes/dynamic/ruleset_picking.dm b/code/controllers/subsystem/dynamic/ruleset_picking.dm
similarity index 91%
rename from code/game/gamemodes/dynamic/ruleset_picking.dm
rename to code/controllers/subsystem/dynamic/ruleset_picking.dm
index 514738a4c8a..1d77eef3ab6 100644
--- a/code/game/gamemodes/dynamic/ruleset_picking.dm
+++ b/code/controllers/subsystem/dynamic/ruleset_picking.dm
@@ -8,7 +8,7 @@
*
* * max_allowed_attempts - Allows you to configure how many times the proc will attempt to pick a ruleset before giving up.
*/
-/datum/game_mode/dynamic/proc/pick_ruleset(list/drafted_rules, max_allowed_attempts = INFINITY)
+/datum/controller/subsystem/dynamic/proc/pick_ruleset(list/drafted_rules, max_allowed_attempts = INFINITY)
if (only_ruleset_executed)
log_dynamic("FAIL: only_ruleset_executed")
return null
@@ -52,7 +52,7 @@
return null
/// Executes a random midround ruleset from the list of drafted rules.
-/datum/game_mode/dynamic/proc/pick_midround_rule(list/drafted_rules, description)
+/datum/controller/subsystem/dynamic/proc/pick_midround_rule(list/drafted_rules, description)
log_dynamic("Rolling [drafted_rules.len] [description]")
var/datum/dynamic_ruleset/rule = pick_ruleset(drafted_rules)
@@ -95,7 +95,7 @@
// SKYRAT EDIT ADDITION END - Event notification
/// Fired after admins do not cancel a midround injection.
-/datum/game_mode/dynamic/proc/execute_midround_rule(datum/dynamic_ruleset/rule)
+/datum/controller/subsystem/dynamic/proc/execute_midround_rule(datum/dynamic_ruleset/rule)
current_midround_rulesets = null
midround_injection_timer_id = null
if (!rule.repeatable)
@@ -103,7 +103,7 @@
addtimer(CALLBACK(src, PROC_REF(execute_midround_latejoin_rule), rule), rule.delay)
/// Mainly here to facilitate delayed rulesets. All midround/latejoin rulesets are executed with a timered callback to this proc.
-/datum/game_mode/dynamic/proc/execute_midround_latejoin_rule(sent_rule)
+/datum/controller/subsystem/dynamic/proc/execute_midround_latejoin_rule(sent_rule)
var/datum/dynamic_ruleset/rule = sent_rule
spend_midround_budget(rule.cost, threat_log, "[worldtime2text()]: [rule.ruletype] [rule.name]")
rule.pre_execute(GLOB.alive_player_list.len)
@@ -129,7 +129,7 @@
return FALSE
/// Fired when an admin cancels the current midround injection.
-/datum/game_mode/dynamic/proc/admin_cancel_midround(mob/user, timer_id)
+/datum/controller/subsystem/dynamic/proc/admin_cancel_midround(mob/user, timer_id)
if (midround_injection_timer_id != timer_id || !deltimer(midround_injection_timer_id))
to_chat(user, span_notice("Too late!"))
return
@@ -140,7 +140,7 @@
current_midround_rulesets = null
/// Fired when an admin requests a different midround injection.
-/datum/game_mode/dynamic/proc/admin_different_midround(mob/user, timer_id)
+/datum/controller/subsystem/dynamic/proc/admin_different_midround(mob/user, timer_id)
if (midround_injection_timer_id != timer_id || !deltimer(midround_injection_timer_id))
to_chat(user, span_notice("Too late!"))
return
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 8376059e910..72b9eb622ce 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -94,6 +94,18 @@ SUBSYSTEM_DEF(job)
set_overflow_role(CONFIG_GET(string/overflow_job)) // this must always go after load_jobs_from_config() due to how the legacy systems operate, this always takes precedent.
return SS_INIT_SUCCESS
+/// Returns a list of jobs that we are allowed to fuck with during random events
+/datum/controller/subsystem/job/proc/get_valid_overflow_jobs()
+ var/static/list/overflow_jobs
+ if (!isnull(overflow_jobs))
+ return overflow_jobs
+
+ overflow_jobs = list()
+ for (var/datum/job/check_job in joinable_occupations)
+ if (!check_job.allow_bureaucratic_error)
+ continue
+ overflow_jobs += check_job
+ return overflow_jobs
/datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role)
var/datum/job/new_overflow = ispath(new_overflow_role) ? GetJobType(new_overflow_role) : GetJob(new_overflow_role)
@@ -755,7 +767,7 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/get_living_heads()
. = list()
for(var/datum/mind/head as anything in get_crewmember_minds())
- if(!(head.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(head.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
if(isnull(head.current) || head.current.stat == DEAD)
continue
@@ -765,7 +777,7 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/get_all_heads()
. = list()
for(var/datum/mind/head as anything in get_crewmember_minds())
- if(head.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(head.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
. += head
/// Returns a list of minds of all security members who are alive
diff --git a/code/controllers/subsystem/modular_computers.dm b/code/controllers/subsystem/modular_computers.dm
index ad0be83bb52..3c861850cdd 100644
--- a/code/controllers/subsystem/modular_computers.dm
+++ b/code/controllers/subsystem/modular_computers.dm
@@ -5,8 +5,9 @@ SUBSYSTEM_DEF(modular_computers)
name = "Modular Computers"
flags = SS_NO_FIRE
- ///List of all ModPC logging
- var/list/logs = list()
+ ///List of all logs generated by ModPCs through the round.
+ ///Stops at MAX_LOG_COUNT and must be purged to keep logging.
+ var/list/modpc_logs = list()
///List of all programs available to download from the NTNet store.
var/list/available_station_software = list()
@@ -30,13 +31,13 @@ SUBSYSTEM_DEF(modular_computers)
/datum/controller/subsystem/modular_computers/proc/build_software_lists()
for(var/datum/computer_file/program/prog as anything in subtypesof(/datum/computer_file/program))
// Has no TGUI file so is not meant to be a downloadable thing.
- if(!initial(prog.tgui_id))
+ if(!initial(prog.tgui_id) || !initial(prog.filename))
continue
prog = new prog
- if(prog.available_on_ntnet)
+ if(prog.program_flags & PROGRAM_ON_NTNET_STORE)
available_station_software.Add(prog)
- if(prog.available_on_syndinet)
+ if(prog.program_flags & PROGRAM_ON_SYNDINET_STORE)
available_antag_software.Add(prog)
///Attempts to find a new file through searching the available stores with its name.
@@ -65,17 +66,17 @@ SUBSYSTEM_DEF(modular_computers)
log_text += log_string
log_string = log_text.Join()
- logs.Add(log_string)
+ modpc_logs.Add(log_string)
// We have too many logs, remove the oldest entries until we get into the limit
- if(logs.len > MAX_LOG_COUNT)
- logs = logs.Copy(logs.len - MAX_LOG_COUNT, 0)
+ if(modpc_logs.len > MAX_LOG_COUNT)
+ modpc_logs = modpc_logs.Copy(modpc_logs.len - MAX_LOG_COUNT, 0)
/**
* Removes all station logs and leaves it with an alert that it's been wiped.
*/
/datum/controller/subsystem/modular_computers/proc/purge_logs()
- logs = list()
+ modpc_logs = list()
add_log("-!- LOGS DELETED BY SYSTEM OPERATOR -!-")
/**
diff --git a/code/controllers/subsystem/polling.dm b/code/controllers/subsystem/polling.dm
new file mode 100644
index 00000000000..479358cc4fd
--- /dev/null
+++ b/code/controllers/subsystem/polling.dm
@@ -0,0 +1,216 @@
+SUBSYSTEM_DEF(polling)
+ name = "Polling"
+ flags = SS_BACKGROUND | SS_NO_INIT
+ wait = 1 SECONDS
+ runlevels = RUNLEVEL_GAME
+ /// List of polls currently ongoing, to be checked on next fire()
+ var/list/datum/candidate_poll/currently_polling
+ /// Number of polls performed since the start
+ var/total_polls = 0
+
+/datum/controller/subsystem/polling/fire()
+ if(!currently_polling) // if polls_active is TRUE then this shouldn't happen, but still..
+ currently_polling = list()
+
+ for(var/datum/candidate_poll/running_poll as anything in currently_polling)
+ if(running_poll.time_left() <= 0)
+ polling_finished(running_poll)
+
+/datum/controller/subsystem/polling/proc/poll_candidates(question, role, check_jobban, poll_time = 30 SECONDS, ignore_category = null, flash_window = TRUE, list/group = null, pic_source, role_name_text)
+ if(group.len == 0)
+ return list()
+ if(role && !role_name_text)
+ role_name_text = role
+ if(role_name_text && !question)
+ question = "Do you want to play as [full_capitalize(role_name_text)]?"
+ if(!question)
+ question = "Do you want to play as a special role?"
+ log_game("Polling candidates [role_name_text ? "for [role_name_text]" : "\"[question]\""] for [DisplayTimeText(poll_time)] seconds")
+
+ // Start firing
+ total_polls++
+
+ var/jumpable = isatom(pic_source) ? pic_source : null
+
+ var/datum/candidate_poll/new_poll = new(role_name_text, question, poll_time, ignore_category, jumpable)
+ LAZYADD(currently_polling, new_poll)
+
+ var/category = "[new_poll.poll_key]_poll_alert"
+
+ for(var/mob/candidate_mob as anything in group)
+ // Universal opt-out for all players.
+ if((!candidate_mob.client.prefs.read_preference(/datum/preference/toggle/ghost_roles)))
+ continue
+ // Opt-out for admins whom are currently adminned.
+ if((!candidate_mob.client.prefs.read_preference(/datum/preference/toggle/ghost_roles_as_admin)) && candidate_mob.client.holder)
+ continue
+ if(!is_eligible(candidate_mob, role, check_jobban, ignore_category))
+ continue
+
+ SEND_SOUND(candidate_mob, 'sound/misc/notice2.ogg')
+ if(flash_window)
+ window_flash(candidate_mob.client)
+
+ // If we somehow send two polls for the same mob type, but with a duration on the second one shorter than the time left on the first one,
+ // we need to keep the first one's timeout rather than use the shorter one
+ var/atom/movable/screen/alert/poll_alert/current_alert = LAZYACCESS(candidate_mob.alerts, category)
+ var/alert_time = poll_time
+ var/alert_poll = new_poll
+ if(current_alert && current_alert.timeout > (world.time + poll_time - world.tick_lag))
+ alert_time = current_alert.timeout - world.time + world.tick_lag
+ alert_poll = current_alert.poll
+
+ // Send them an on-screen alert
+ var/atom/movable/screen/alert/poll_alert/poll_alert_button = candidate_mob.throw_alert(category, /atom/movable/screen/alert/poll_alert, timeout_override = alert_time, no_anim = TRUE)
+ if(!poll_alert_button)
+ continue
+
+ poll_alert_button.icon = ui_style2icon(candidate_mob.client?.prefs?.read_preference(/datum/preference/choiced/ui_style))
+ poll_alert_button.desc = "[question]"
+ poll_alert_button.show_time_left = TRUE
+ poll_alert_button.poll = alert_poll
+ poll_alert_button.poll.alert_button = poll_alert_button
+ poll_alert_button.set_role_overlay()
+ poll_alert_button.update_stacks_overlay()
+
+ // Sign up inheritance and stacking
+ var/inherited_sign_up = FALSE
+ for(var/existing_poll in currently_polling)
+ var/datum/candidate_poll/other_poll = existing_poll
+ if(new_poll != other_poll && new_poll.poll_key == other_poll.poll_key)
+ // If there's already a poll for an identical mob type ongoing and the client is signed up for it, sign them up for this one
+ if(!inherited_sign_up && (candidate_mob in other_poll.signed_up) && new_poll.sign_up(candidate_mob, TRUE))
+ inherited_sign_up = TRUE
+
+ // Image to display
+ var/image/poll_image
+ if(pic_source)
+ if(!ispath(pic_source))
+ var/atom/the_pic_source = pic_source
+ var/old_layer = the_pic_source.layer
+ var/old_plane = the_pic_source.plane
+ the_pic_source.plane = poll_alert_button.plane
+ the_pic_source.layer = FLOAT_LAYER
+ poll_alert_button.add_overlay(the_pic_source)
+ the_pic_source.layer = old_layer
+ the_pic_source.plane = old_plane
+ else
+ poll_image = image(pic_source, layer = FLOAT_LAYER)
+ else
+ // Just use a generic image
+ poll_image = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER)
+
+ if(poll_image)
+ poll_image.plane = poll_alert_button.plane
+ poll_alert_button.add_overlay(poll_image)
+
+ // Chat message
+ var/act_jump = ""
+ if(isatom(pic_source) && isobserver(candidate_mob))
+ act_jump = "\[Teleport]"
+ var/act_signup = "\[Sign Up]"
+ var/act_never = ""
+ if(ignore_category)
+ act_never = "\[Never For This Round]"
+ to_chat(candidate_mob, span_boldnotice(examine_block("Now looking for candidates [role_name_text ? "to play as \an [role_name_text]." : "\"[question]\""] [act_jump] [act_signup] [act_never]")))
+
+ // Start processing it so it updates visually the timer
+ START_PROCESSING(SSprocessing, poll_alert_button)
+
+ // Sleep until the time is up
+ UNTIL(new_poll.finished)
+ return new_poll.signed_up
+
+/datum/controller/subsystem/polling/proc/poll_ghost_candidates(question, role, check_jobban, poll_time = 30 SECONDS, ignore_category = null, flashwindow = TRUE, pic_source, role_name_text)
+ var/list/candidates = list()
+ if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
+ return candidates
+
+ for(var/mob/dead/observer/ghost_player in GLOB.player_list)
+ candidates += ghost_player
+
+ return poll_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, candidates, pic_source, role_name_text)
+
+/datum/controller/subsystem/polling/proc/poll_ghost_candidates_for_mob(question, role, check_jobban, poll_time = 30 SECONDS, mob/target_mob, ignore_category = null, flashwindow = TRUE, pic_source, role_name_text)
+ var/static/list/mob/currently_polling_mobs = list()
+
+ if(currently_polling_mobs.Find(target_mob))
+ return list()
+
+ currently_polling_mobs += target_mob
+
+ var/list/possible_candidates = poll_ghost_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, pic_source, role_name_text)
+
+ currently_polling_mobs -= target_mob
+ if(!target_mob || QDELETED(target_mob) || !target_mob.loc)
+ return list()
+
+ return possible_candidates
+
+/datum/controller/subsystem/polling/proc/poll_ghost_candidates_for_mobs(question, role, check_jobban, poll_time = 30 SECONDS, list/mobs, ignore_category = null, flashwindow = TRUE, pic_source, role_name_text)
+ var/list/candidate_list = poll_ghost_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, pic_source, role_name_text)
+
+ for(var/mob/potential_mob as anything in mobs)
+ if(QDELETED(potential_mob) || !potential_mob.loc)
+ mobs -= potential_mob
+
+ if(!length(mobs))
+ return list()
+
+ return candidate_list
+
+/datum/controller/subsystem/polling/proc/is_eligible(mob/potential_candidate, role, check_jobban, the_ignore_category)
+ if(isnull(potential_candidate.key) || isnull(potential_candidate.client))
+ return FALSE
+ if(the_ignore_category)
+ if(potential_candidate.ckey in GLOB.poll_ignore[the_ignore_category])
+ return FALSE
+ if(role)
+ if(!(role in potential_candidate.client.prefs.be_special))
+ return FALSE
+ var/required_time = GLOB.special_roles[role] || 0
+ if(potential_candidate.client && potential_candidate.client.get_remaining_days(required_time) > 0)
+ return FALSE
+
+ if(check_jobban)
+ if(is_banned_from(potential_candidate.ckey, list(check_jobban, ROLE_SYNDICATE)))
+ return FALSE
+
+ //SKYRAT EDIT ADDITION BEGIN
+ if(is_banned_from(potential_candidate.ckey, BAN_GHOST_TAKEOVER) || is_banned_from(potential_candidate.ckey, BAN_ANTAGONIST))
+ to_chat(potential_candidate, "There was a ghost prompt for: [role], unfortunately you are banned from ghost takeovers.")
+ return FALSE
+ //SKYRAT EDIT END
+
+ return TRUE
+
+/datum/controller/subsystem/polling/proc/polling_finished(datum/candidate_poll/finishing_poll)
+ // Trim players who aren't eligible anymore
+ var/length_pre_trim = length(finishing_poll.signed_up)
+ finishing_poll.trim_candidates()
+ log_game("Candidate poll [finishing_poll.role ? "for [finishing_poll.role]" : "\"[finishing_poll.question]\""] finished. [length_pre_trim] players signed up, [length(finishing_poll.signed_up)] after trimming")
+ finishing_poll.finished = TRUE
+ finishing_poll.alert_button.update_stacks_overlay()
+ currently_polling -= finishing_poll
+
+/datum/controller/subsystem/polling/stat_entry(msg)
+ msg += "Active: [length(currently_polling)] | Total: [total_polls]"
+ var/datum/candidate_poll/soonest_to_complete = get_next_poll_to_finish()
+ if(soonest_to_complete)
+ msg += " | Next: [DisplayTimeText(soonest_to_complete.time_left())] ([length(soonest_to_complete.signed_up)] candidates)"
+ return ..()
+
+/datum/controller/subsystem/polling/proc/get_next_poll_to_finish()
+ var/lowest_time_left = INFINITY
+ var/next_poll_to_finish
+ for(var/datum/candidate_poll/poll as anything in currently_polling)
+ var/time_left = poll.time_left()
+ if(time_left >= lowest_time_left)
+ continue
+ lowest_time_left = time_left
+ next_poll_to_finish = poll
+
+ if(isnull(next_poll_to_finish))
+ return FALSE
+
+ return next_poll_to_finish
diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm
index 532749c55e0..7c397bd44c9 100644
--- a/code/controllers/subsystem/processing/quirks.dm
+++ b/code/controllers/subsystem/processing/quirks.dm
@@ -12,7 +12,7 @@ GLOBAL_LIST_INIT_TYPED(quirk_blacklist, /list/datum/quirk, list(
list(/datum/quirk/item_quirk/clown_enjoyer, /datum/quirk/item_quirk/mime_fan, /datum/quirk/item_quirk/pride_pin),
list(/datum/quirk/bad_touch, /datum/quirk/friendly),
list(/datum/quirk/extrovert, /datum/quirk/introvert),
- list(/datum/quirk/prosthetic_limb, /datum/quirk/quadruple_amputee, /datum/quirk/body_purist),
+ list(/datum/quirk/prosthetic_limb, /datum/quirk/quadruple_amputee, /datum/quirk/transhumanist, /datum/quirk/body_purist),
list(/datum/quirk/prosthetic_organ, /datum/quirk/tin_man, /datum/quirk/body_purist),
list(/datum/quirk/quadruple_amputee, /datum/quirk/paraplegic, /datum/quirk/hemiplegic),
//list(/datum/quirk/quadruple_amputee, /datum/quirk/frail), // SKYRAT EDIT REMOVAL- Since we have synth wounds now, frail has a large downside for prosthetics and such
@@ -111,7 +111,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
var/datum/quirk/quirk_type = quirks[quirk_name]
if(ispath(quirk_type))
if(user.add_quirk(quirk_type, override_client = applied_client))
- SSblackbox.record_feedback("nested tally", "quirks_taken", 1, list("[quirk_name]"))
+ SSblackbox.record_feedback("tally", "quirks_taken", 1, "[quirk_name]")
else
stack_trace("Invalid quirk \"[quirk_name]\" in client [applied_client.ckey] preferences")
applied_client.prefs.all_quirks -= quirk_name
diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm
index b30a276fc36..5c9795b87fd 100644
--- a/code/controllers/subsystem/processing/station.dm
+++ b/code/controllers/subsystem/processing/station.dm
@@ -11,6 +11,10 @@ PROCESSING_SUBSYSTEM_DEF(station)
var/list/selectable_traits_by_types = list(STATION_TRAIT_POSITIVE = list(), STATION_TRAIT_NEUTRAL = list(), STATION_TRAIT_NEGATIVE = list())
///Currently active announcer. Starts as a type but gets initialized after traits are selected
var/datum/centcom_announcer/announcer = /datum/centcom_announcer/default
+ ///A list of trait roles that should be protected from antag
+ var/list/antag_protected_roles = list()
+ ///A list of trait roles that should never be able to roll antag
+ var/list/antag_restricted_roles = list()
/datum/controller/subsystem/processing/station/Initialize()
@@ -18,6 +22,7 @@ PROCESSING_SUBSYSTEM_DEF(station)
// Autowiki also wants consistent outputs, for example making sure the vending machine page always reports the normal products
#if !defined(UNIT_TESTS) && !defined(AUTOWIKI)
SetupTraits()
+ //display_lobby_traits() SKYRAT EDIT REMOVAL
#endif
announcer = new announcer() //Initialize the station's announcer datum
@@ -57,7 +62,7 @@ PROCESSING_SUBSYSTEM_DEF(station)
setup_trait(trait_typepath)
continue
- if(initial(trait_typepath.trait_flags) & STATION_TRAIT_ABSTRACT)
+ if(initial(trait_typepath.abstract_type) == trait_typepath)
continue //Dont add abstract ones to it
if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_PLANETARY) && SSmapping.is_planetary()) // we're on a planet but we can't do planet ;_;
@@ -96,3 +101,14 @@ PROCESSING_SUBSYSTEM_DEF(station)
for(var/i in trait_instance.blacklist)
var/datum/station_trait/trait_to_remove = i
selectable_traits_by_types[initial(trait_to_remove.trait_type)] -= trait_to_remove
+
+/* SKYRAT EDIT REMOVAL
+/// Update station trait lobby buttons for clients who joined before we initialised this subsystem
+/datum/controller/subsystem/processing/station/proc/display_lobby_traits()
+ for (var/mob/dead/new_player/player as anything in GLOB.new_player_list)
+ var/datum/hud/new_player/observer_hud = player.hud_used
+ if (!istype(observer_hud))
+ continue
+ observer_hud.add_station_trait_buttons()
+ observer_hud.show_hud(observer_hud.hud_version)
+*/
diff --git a/code/controllers/subsystem/security_level.dm b/code/controllers/subsystem/security_level.dm
index df5ea642ce3..cb61af0f3e0 100644
--- a/code/controllers/subsystem/security_level.dm
+++ b/code/controllers/subsystem/security_level.dm
@@ -28,8 +28,9 @@ SUBSYSTEM_DEF(security_level)
*
* Arguments:
* * new_level - The new security level that will become our current level
+ * * announce - Play the announcement, set FALSE if you're doing your own custom announcement to prevent duplicates
*/
-/datum/controller/subsystem/security_level/proc/set_level(new_level)
+/datum/controller/subsystem/security_level/proc/set_level(new_level, announce = TRUE)
new_level = istext(new_level) ? new_level : number_level_to_text(new_level)
if(new_level == current_security_level.name) // If we are already at the desired level, do nothing
return
@@ -42,7 +43,8 @@ SUBSYSTEM_DEF(security_level)
if(SSnightshift.can_fire && (selected_level.number_level >= SEC_LEVEL_RED || current_security_level.number_level >= SEC_LEVEL_RED))
SSnightshift.next_fire = world.time + 7 SECONDS // Fire nightshift after the security level announcement is complete
- level_announce(selected_level, current_security_level.number_level) // We want to announce BEFORE updating to the new level
+ if(announce)
+ level_announce(selected_level, current_security_level.number_level) // We want to announce BEFORE updating to the new level
SSsecurity_level.current_security_level = selected_level
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 97b2c85b2aa..06eb8d1b882 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -20,8 +20,6 @@ SUBSYSTEM_DEF(ticker)
/// Boolean to track and check if our subsystem setup is done.
var/setup_done = FALSE
- var/datum/game_mode/mode = null
-
var/login_music //music played in pregame lobby
var/round_end_sound //music/jingle played when the world reboots
var/round_end_sound_sent = TRUE //If all clients have loaded it
@@ -219,10 +217,9 @@ SUBSYSTEM_DEF(ticker)
SEND_SIGNAL(src, COMSIG_TICKER_ERROR_SETTING_UP)
if(GAME_STATE_PLAYING)
- mode.process(wait * 0.1)
check_queue()
- if(!roundend_check_paused && (mode.check_finished() || force_ending))
+ if(!roundend_check_paused && (check_finished() || force_ending))
current_state = GAME_STATE_FINISHED
toggle_ooc(TRUE) // Turn it on
toggle_dooc(TRUE)
@@ -230,25 +227,35 @@ SUBSYSTEM_DEF(ticker)
check_maprotate()
Master.SetRunLevel(RUNLEVEL_POSTGAME)
+/// Checks if the round should be ending, called every ticker tick
+/datum/controller/subsystem/ticker/proc/check_finished()
+ if(!setup_done)
+ return FALSE
+ if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME))
+ return TRUE
+ if(GLOB.station_was_nuked)
+ return TRUE
+ if(GLOB.revolutionary_win)
+ return TRUE
+ return FALSE
+
/datum/controller/subsystem/ticker/proc/setup()
to_chat(world, span_boldannounce("Starting game..."))
var/init_start = world.timeofday
- mode = new /datum/game_mode/dynamic
-
CHECK_TICK
- //Configure mode and assign player to special mode stuff
- var/can_continue = 0
- can_continue = src.mode.pre_setup() //Choose antagonists
+ //Configure mode and assign player to antagonists
+ var/can_continue = FALSE
+ can_continue = SSdynamic.pre_setup() //Choose antagonists
CHECK_TICK
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_JOBS_ASSIGNED, src)
can_continue = can_continue && SSjob.DivideOccupations() //Distribute jobs
CHECK_TICK
if(!GLOB.Debug2)
if(!can_continue)
log_game("Game failed pre_setup")
- QDEL_NULL(mode)
to_chat(world, "Error setting up game. Reverting to pre-game lobby.")
SSjob.ResetOccupations()
return FALSE
@@ -308,7 +315,7 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/PostSetup()
set waitfor = FALSE
- mode.post_setup()
+ SSdynamic.post_setup()
GLOB.start_state = new /datum/station_state()
GLOB.start_state.count()
@@ -554,7 +561,6 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/Recover()
current_state = SSticker.current_state
force_ending = SSticker.force_ending
- mode = SSticker.mode
login_music = SSticker.login_music
round_end_sound = SSticker.round_end_sound
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index 2d134eacb66..c4c3b85b4c9 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -90,6 +90,7 @@ SUBSYSTEM_DEF(vote)
var/list/vote_choice_data = list()
for(var/choice in current_vote.choices)
var/choice_votes = current_vote.choices[choice]
+ total_votes += choice_votes
vote_choice_data["[choice]"] = choice_votes
// stringify the winners to prevent potential unimplemented serialization errors.
diff --git a/code/datums/actions/mobs/lava_swoop.dm b/code/datums/actions/mobs/lava_swoop.dm
index 7532ccfda08..a5acc29bab6 100644
--- a/code/datums/actions/mobs/lava_swoop.dm
+++ b/code/datums/actions/mobs/lava_swoop.dm
@@ -199,7 +199,6 @@
icon = 'icons/mob/simple/lavaland/96x96megafauna.dmi'
icon_state = "dragon"
layer = ABOVE_ALL_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
pixel_x = -32
duration = 10
randomdir = FALSE
diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm
index 9a7ec19cd5e..4f61a261659 100644
--- a/code/datums/ai/generic/generic_behaviors.dm
+++ b/code/datums/ai/generic/generic_behaviors.dm
@@ -359,6 +359,10 @@
continue
if(thing.IsObscured())
continue
+ if(isitem(thing))
+ var/obj/item/item = thing
+ if(item.item_flags & ABSTRACT)
+ continue
possible_targets += thing
if(!possible_targets.len)
finish_action(controller, FALSE)
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 6b138387290..152de6ec1e1 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -179,7 +179,6 @@
/obj/effect/ebeam
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
layer = ABOVE_ALL_MOB_LAYER
anchored = TRUE
var/emissive = TRUE
diff --git a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
index 7dd453163c5..15fd6dc56a4 100644
--- a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
+++ b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
@@ -93,26 +93,26 @@
CRASH("External organ has no feature list, it will render invisible")
///Give the organ its color. Force will override the existing one.
-/datum/bodypart_overlay/mutant/proc/inherit_color(obj/item/bodypart/ownerlimb, force)
- if(isnull(ownerlimb))
+/datum/bodypart_overlay/mutant/proc/inherit_color(obj/item/bodypart/bodypart_owner, force)
+ if(isnull(bodypart_owner))
draw_color = null
- alpha = 255 // SKYRAT EDIT - Mutant bodyparts transparency are based on limb transparency
+ alpha = 255 // SKYRAT ADDITION - Mutant bodyparts transparency are based on limb transparency
return TRUE
if(draw_color && !force)
return FALSE
- alpha = ownerlimb.alpha // SKYRAT EDIT - Mutant bodyparts transparency are based on limb transparency
+ alpha = bodypart_owner.alpha // SKYRAT ADDITION - Mutant bodyparts transparency are based on limb transparency
switch(color_source)
if(ORGAN_COLOR_OVERRIDE)
- draw_color = override_color(ownerlimb.draw_color)
+ draw_color = override_color(bodypart_owner.draw_color)
if(ORGAN_COLOR_INHERIT)
- draw_color = ownerlimb.draw_color
+ draw_color = bodypart_owner.draw_color
if(ORGAN_COLOR_HAIR)
- if(!ishuman(ownerlimb.owner))
+ if(!ishuman(bodypart_owner.owner))
return
- var/mob/living/carbon/human/human_owner = ownerlimb.owner
- var/obj/item/bodypart/head/my_head = human_owner.get_bodypart(BODY_ZONE_HEAD) //not always the same as ownerlimb
+ var/mob/living/carbon/human/human_owner = bodypart_owner.owner
+ var/obj/item/bodypart/head/my_head = human_owner.get_bodypart(BODY_ZONE_HEAD) //not always the same as bodypart_owner
//head hair color takes priority, owner hair color is a backup if we lack a head or something
if(my_head)
draw_color = my_head.hair_color
diff --git a/code/datums/brain_damage/special.dm b/code/datums/brain_damage/special.dm
index 81d354a19e8..d9348f98f4b 100644
--- a/code/datums/brain_damage/special.dm
+++ b/code/datums/brain_damage/special.dm
@@ -100,7 +100,6 @@
image_icon = 'icons/effects/effects.dmi'
image_state = "bluestream"
image_layer = ABOVE_MOB_LAYER
- image_plane = GAME_PLANE_UPPER
var/obj/effect/client_image_holder/bluespace_stream/linked_to
/obj/effect/client_image_holder/bluespace_stream/Initialize(mapload, list/mobs_which_see_us)
diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm
index 7cbcdd6a4d8..f6e83c9537c 100644
--- a/code/datums/brain_damage/split_personality.dm
+++ b/code/datums/brain_damage/split_personality.dm
@@ -211,7 +211,7 @@
/datum/brain_trauma/severe/split_personality/brainwashing/get_ghost()
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [owner.real_name]'s brainwashed mind?", null, null, 7.5 SECONDS, stranger_backseat)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [owner.real_name]'s brainwashed mind?", poll_time = 7.5 SECONDS, target_mob = stranger_backseat, pic_source = owner, role_name_text = "brainwashed mind")
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
stranger_backseat.key = C.key
diff --git a/code/datums/candidate_poll.dm b/code/datums/candidate_poll.dm
new file mode 100644
index 00000000000..cec4e268bef
--- /dev/null
+++ b/code/datums/candidate_poll.dm
@@ -0,0 +1,110 @@
+/// The datum that describes one instance of candidate polling
+/datum/candidate_poll
+ /// The role the poll is for
+ var/role
+ /// The question asked to potential candidates
+ var/question
+ /// The duration of the poll
+ var/duration
+ /// the atom observers can jump/teleport to
+ var/atom/jump_to_me
+ /// Never For This Round category
+ var/ignoring_category
+ /// The players who signed up to this poll
+ var/list/mob/signed_up
+ /// the linked alert button
+ var/atom/movable/screen/alert/poll_alert/alert_button
+ /// The world.time at which the poll was created
+ var/time_started
+ /// Whether the polling is finished
+ var/finished = FALSE
+ /// Used to categorize in the alerts system and identify polls of same question+role so we can stack the alert buttons
+ var/poll_key
+
+/datum/candidate_poll/New(polled_role, polled_question, poll_duration, poll_ignoring_category, poll_jumpable)
+ role = polled_role
+ question = polled_question
+ duration = poll_duration
+ ignoring_category = poll_ignoring_category
+ jump_to_me = poll_jumpable
+ signed_up = list()
+ time_started = world.time
+ poll_key = "[question]_[role ? role : "0"]"
+ return ..()
+
+/datum/candidate_poll/Destroy()
+ QDEL_NULL(alert_button)
+ jump_to_me = null
+ signed_up.Cut()
+ return ..()
+
+/datum/candidate_poll/proc/sign_up(mob/candidate, silent = FALSE)
+ if(!istype(candidate) || isnull(candidate.key) || isnull(candidate.client))
+ return FALSE
+ if(candidate in signed_up)
+ if(!silent)
+ to_chat(candidate, span_warning("You have already signed up for this!"))
+ return FALSE
+ if(time_left() <= 0)
+ if(!silent)
+ to_chat(candidate, span_danger("Sorry, you were too late for the consideration!"))
+ SEND_SOUND(candidate, 'sound/machines/buzz-sigh.ogg')
+ return FALSE
+
+ signed_up += candidate
+ if(!silent)
+ to_chat(candidate, span_notice("You have signed up for [role]! A candidate will be picked randomly soon."))
+ // Sign them up for any other polls with the same mob type
+ for(var/datum/candidate_poll/existing_poll as anything in SSpolling.currently_polling)
+ if(src != existing_poll && poll_key == existing_poll.poll_key && !(candidate in existing_poll.signed_up))
+ existing_poll.sign_up(candidate, TRUE)
+ if(alert_button)
+ alert_button.update_candidates_number_overlay()
+ alert_button.update_signed_up_overlay()
+ return TRUE
+
+/datum/candidate_poll/proc/remove_candidate(mob/candidate, silent = FALSE)
+ if(!istype(candidate) || isnull(candidate.key) || isnull(candidate.client))
+ return FALSE
+ if(!(candidate in signed_up))
+ if(!silent)
+ to_chat(candidate, span_warning("You aren't signed up for this!"))
+ return FALSE
+
+ if(time_left() <= 0)
+ if(!silent)
+ to_chat(candidate, span_danger("It's too late to unregister yourself, selection has already begun!"))
+ return FALSE
+
+ signed_up -= candidate
+ if(!silent)
+ to_chat(candidate, span_danger("You have been unregistered as a candidate for [role]. You can sign up again before the poll ends."))
+
+ for(var/datum/candidate_poll/existing_poll as anything in SSpolling.currently_polling)
+ if(src != existing_poll && poll_key == existing_poll.poll_key && (candidate in existing_poll.signed_up))
+ existing_poll.remove_candidate(candidate, TRUE)
+ if(alert_button)
+ alert_button.update_candidates_number_overlay()
+ alert_button.update_signed_up_overlay()
+ return TRUE
+
+/datum/candidate_poll/proc/do_never_for_this_round(mob/candidate)
+ var/list/ignore_list = GLOB.poll_ignore[ignoring_category]
+ if(!ignore_list)
+ GLOB.poll_ignore[ignoring_category] = list()
+ GLOB.poll_ignore[ignoring_category] += candidate.ckey
+ to_chat(candidate, span_danger("Choice registered: Never for this round."))
+ remove_candidate(candidate, silent = TRUE)
+
+/datum/candidate_poll/proc/undo_never_for_this_round(mob/candidate)
+ GLOB.poll_ignore[ignoring_category] -= candidate.ckey
+ to_chat(candidate, span_notice("Choice registered: Eligible for this round"))
+
+/datum/candidate_poll/proc/trim_candidates()
+ list_clear_nulls(signed_up)
+ for(var/mob/candidate as anything in signed_up)
+ if(isnull(candidate.key) || isnull(candidate.client))
+ signed_up -= candidate
+
+/datum/candidate_poll/proc/time_left()
+ return duration - (world.time - time_started)
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index e16ab582b77..832ad22698b 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -106,6 +106,10 @@
for(var/atom/movable/AM in range(radius_range, a))
if((AM.flags_1 & HOLOGRAM_1) || (blacklist && (AM.type in blacklist)))
continue
+ if(isitem(AM))
+ var/obj/item/item = AM
+ if(item.item_flags & ABSTRACT) //let's not tempt fate, shall we?
+ continue
. += AM
/datum/component/personal_crafting/proc/get_surroundings(atom/a, list/blacklist=null)
diff --git a/code/datums/components/fov_handler.dm b/code/datums/components/fov_handler.dm
index 0390357aced..9f64c648502 100644
--- a/code/datums/components/fov_handler.dm
+++ b/code/datums/components/fov_handler.dm
@@ -10,8 +10,6 @@
var/fov_angle = FOV_180_DEGREES
/// The blocker mask applied to a client's screen
var/atom/movable/screen/fov_blocker/blocker_mask
- /// The shadow mask applied to a client's screen
- var/atom/movable/screen/fov_shadow/visual_shadow
/datum/component/fov_handler/Initialize(fov_type = FOV_180_DEGREES)
if(!isliving(parent))
@@ -22,13 +20,9 @@
qdel(src) //no QDEL hint for components, and we dont want this to print a warning regarding bad component application
return
- for(var/atom/movable/screen/plane_master/plane_master as anything in mob_parent.hud_used.get_true_plane_masters(FIELD_OF_VISION_BLOCKER_PLANE))
- plane_master.unhide_plane(mob_parent)
+ ADD_TRAIT(mob_parent, TRAIT_FOV_APPLIED, REF(src))
blocker_mask = new
- visual_shadow = new
- //visual_shadow.alpha = parent_client?.prefs.read_preference(/datum/preference/numeric/fov_darkness) //SKYRAT EDIT REMOVAL
- update_visual_shadow_alpha() //SKYRAT EDIT ADDITION
set_fov_angle(fov_type)
on_dir_change(mob_parent, mob_parent.dir, mob_parent.dir)
update_fov_size()
@@ -36,21 +30,17 @@
/datum/component/fov_handler/Destroy()
var/mob/living/mob_parent = parent
- for(var/atom/movable/screen/plane_master/plane_master as anything in mob_parent.hud_used.get_true_plane_masters(FIELD_OF_VISION_BLOCKER_PLANE))
- plane_master.hide_plane(mob_parent)
+ REMOVE_TRAIT(mob_parent, TRAIT_FOV_APPLIED, REF(src))
if(applied_mask)
remove_mask()
if(blocker_mask) // In a case of early deletion due to volatile client
QDEL_NULL(blocker_mask)
- if(visual_shadow) // In a case of early deletion due to volatile client
- QDEL_NULL(visual_shadow)
return ..()
/datum/component/fov_handler/proc/set_fov_angle(new_angle)
fov_angle = new_angle
blocker_mask.icon_state = "[fov_angle]"
- visual_shadow.icon_state = "[fov_angle]_v"
/// Updates the size of the FOV masks by comparing them to client view size.
/datum/component/fov_handler/proc/update_fov_size()
@@ -71,8 +61,8 @@
var/y_scale = view_size[2] / current_fov_y
current_fov_x = view_size[1]
current_fov_y = view_size[2]
- visual_shadow.transform = blocker_mask.transform = new_matrix.Scale(x_scale, y_scale)
- visual_shadow.transform = blocker_mask.transform = new_matrix.Translate(x_shift * 16, y_shift * 16)
+ blocker_mask.transform = new_matrix.Scale(x_scale, y_scale)
+ blocker_mask.transform = new_matrix.Translate(x_shift * 16, y_shift * 16)
/// Updates the mask application to client by checking `stat` and `eye`
/datum/component/fov_handler/proc/update_mask()
@@ -99,13 +89,10 @@
var/client/parent_client = parent_mob.client
// Prevents stupid ass hard deletes
parent_mob.hud_used.always_visible_inventory -= blocker_mask
- parent_mob.hud_used.always_visible_inventory -= visual_shadow
if(!parent_client) //Love client volatility!!
return
applied_mask = FALSE
parent_client.screen -= blocker_mask
- parent_client.screen -= visual_shadow
-
/datum/component/fov_handler/proc/add_mask()
var/mob/parent_mob = parent
@@ -114,15 +101,12 @@
return
applied_mask = TRUE
parent_client.screen += blocker_mask
- parent_client.screen += visual_shadow
parent_mob.hud_used.always_visible_inventory += blocker_mask
- parent_mob.hud_used.always_visible_inventory += visual_shadow
/// When a direction of the user changes, so do the masks
/datum/component/fov_handler/proc/on_dir_change(mob/source, old_dir, new_dir)
SIGNAL_HANDLER
blocker_mask.dir = new_dir
- visual_shadow.dir = new_dir
/// When a mob logs out, delete the component
/datum/component/fov_handler/proc/mob_logout(mob/source)
@@ -137,19 +121,9 @@
RegisterSignal(parent, COMSIG_MOB_CLIENT_CHANGE_VIEW, PROC_REF(update_fov_size))
RegisterSignal(parent, COMSIG_MOB_RESET_PERSPECTIVE, PROC_REF(update_mask))
RegisterSignal(parent, COMSIG_MOB_LOGOUT, PROC_REF(mob_logout))
- RegisterSignal(parent, COMSIG_LIVING_COMBAT_MODE_TOGGLE, PROC_REF(update_visual_shadow_alpha)) //SKYRAT EDIT ADDITION
/datum/component/fov_handler/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_MOB_RESET_PERSPECTIVE, COMSIG_ATOM_DIR_CHANGE, COMSIG_LIVING_DEATH, COMSIG_LIVING_REVIVE, COMSIG_MOB_LOGOUT))
UnregisterSignal(parent, COMSIG_LIVING_COMBAT_MODE_TOGGLE) //SKYRAT EDIT ADDITION
-//SKYRAT EDIT ADDITION BEGIN
-/// When toggling combat mode, we update the alpha of the shadow mask
-/datum/component/fov_handler/proc/update_visual_shadow_alpha()
- SIGNAL_HANDLER
- var/mob/living/parent_mob = parent
- var/pref_to_read = parent_mob.combat_mode ? /datum/preference/numeric/fov_darkness : /datum/preference/numeric/out_of_combat_fov_darkness
- var/target_alpha = parent_mob.client.prefs.read_preference(pref_to_read)
- visual_shadow.alpha = target_alpha
-//SKYRAT EDIT ADDITION END
diff --git a/code/datums/components/ghost_direct_control.dm b/code/datums/components/ghost_direct_control.dm
index c4d08380db1..6942b72c480 100644
--- a/code/datums/components/ghost_direct_control.dm
+++ b/code/datums/components/ghost_direct_control.dm
@@ -30,7 +30,7 @@
src.ban_type = ban_type
src.assumed_control_message = assumed_control_message || "You are [parent]!"
src.extra_control_checks = extra_control_checks
- src.after_assumed_control= after_assumed_control
+ src.after_assumed_control = after_assumed_control
var/mob/mob_parent = parent
LAZYADD(GLOB.joinable_mobs[format_text("[initial(mob_parent.name)]")], mob_parent)
@@ -42,9 +42,10 @@
. = ..()
RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, PROC_REF(on_ghost_clicked))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examined))
+ RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login))
/datum/component/ghost_direct_control/UnregisterFromParent()
- UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_ATOM_EXAMINE))
+ UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_ATOM_EXAMINE, COMSIG_MOB_LOGIN))
return ..()
/datum/component/ghost_direct_control/Destroy(force, silent)
@@ -73,12 +74,14 @@
if (!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER))
return
awaiting_ghosts = TRUE
- var/list/mob/dead/observer/candidates = poll_ghost_candidates(
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(
question = "Do you want to play as [role_name]?",
- jobban_type = ban_type,
- be_special_flag = ban_type,
+ check_jobban = ban_type,
+ role = ban_type,
poll_time = poll_length,
ignore_category = poll_ignore_key,
+ pic_source = parent,
+ role_name_text = role_name,
)
awaiting_ghosts = FALSE
if (!LAZYLEN(candidates))
@@ -138,3 +141,8 @@
to_chat(new_body, span_boldnotice(assumed_control_message))
after_assumed_control?.Invoke(harbinger)
qdel(src)
+
+/// When someone else assumes control via some other means, get rid of our component
+/datum/component/ghost_direct_control/proc/on_login()
+ SIGNAL_HANDLER
+ qdel(src)
diff --git a/code/datums/components/riding/riding.dm b/code/datums/components/riding/riding.dm
index c8c969a8c6b..98024e1a00e 100644
--- a/code/datums/components/riding/riding.dm
+++ b/code/datums/components/riding/riding.dm
@@ -26,8 +26,6 @@
var/list/directional_vehicle_layers = list()
/// same as above but instead of layer you have a list(px, py)
var/list/directional_vehicle_offsets = list()
- /// planes of the rider
- var/list/directional_rider_planes = list()
/// allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
var/list/allowed_turf_typecache
/// allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
@@ -108,7 +106,6 @@
var/atom/movable/movable_parent = parent
handle_vehicle_layer(movable_parent.dir)
handle_vehicle_offsets(movable_parent.dir)
- handle_rider_plane(movable_parent.dir)
if(rider.pulling == source)
rider.stop_pulling()
@@ -138,20 +135,9 @@
. = AM.layer
AM.layer = .
-/datum/component/riding/proc/handle_rider_plane(dir)
- var/atom/movable/movable_parent = parent
- var/target_plane = directional_rider_planes["[dir]"]
- if(isnull(target_plane))
- return
- for(var/mob/buckled_mob in movable_parent.buckled_mobs)
- SET_PLANE_EXPLICIT(buckled_mob, target_plane, movable_parent)
-
/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
directional_vehicle_layers["[dir]"] = layer
-/datum/component/riding/proc/set_rider_dir_plane(dir, plane)
- directional_rider_planes["[dir]"] = plane
-
/// This is called after the ridden atom is successfully moved and is used to handle icon stuff
/datum/component/riding/proc/vehicle_moved(datum/source, oldloc, dir, forced)
SIGNAL_HANDLER
@@ -166,7 +152,6 @@
return // runtimed with piggy's without this, look into this more
handle_vehicle_offsets(dir)
handle_vehicle_layer(dir)
- handle_rider_plane(dir)
/// Turning is like moving
/datum/component/riding/proc/vehicle_turned(datum/source, _old_dir, new_dir)
diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm
index 0ad6c1e4c7f..032113a8f7e 100644
--- a/code/datums/components/riding/riding_mob.dm
+++ b/code/datums/components/riding/riding_mob.dm
@@ -527,10 +527,6 @@
/datum/component/riding/creature/leaper/handle_specials()
. = ..()
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(17, 46), TEXT_SOUTH = list(17,51), TEXT_EAST = list(27, 46), TEXT_WEST = list(6, 46)))
- set_rider_dir_plane(SOUTH, GAME_PLANE_UPPER)
- set_rider_dir_plane(NORTH, GAME_PLANE)
- set_rider_dir_plane(EAST, GAME_PLANE_UPPER)
- set_rider_dir_plane(WEST, GAME_PLANE_UPPER)
/datum/component/riding/creature/leaper/Initialize(mob/living/riding_mob, force = FALSE, ride_check_flags = NONE, potion_boost = FALSE)
. = ..()
diff --git a/code/datums/components/tactical.dm b/code/datums/components/tactical.dm
index ec78f3dfeb9..e8e54926949 100644
--- a/code/datums/components/tactical.dm
+++ b/code/datums/components/tactical.dm
@@ -40,7 +40,6 @@
var/obj/item/master = parent
var/image/I = image(icon = master.icon, icon_state = master.icon_state, loc = user)
- SET_PLANE_EXPLICIT(I, GAME_PLANE_FOV_HIDDEN, master)
I.copy_overlays(master)
I.override = TRUE
source.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission", I)
diff --git a/code/datums/components/twohanded.dm b/code/datums/components/twohanded.dm
index 37df7308217..89db2d27c83 100644
--- a/code/datums/components/twohanded.dm
+++ b/code/datums/components/twohanded.dm
@@ -173,17 +173,25 @@
/datum/component/two_handed/proc/wield(mob/living/carbon/user)
if(wielded)
return
+ var/atom/atom_parent = parent
+ if(HAS_TRAIT(user, TRAIT_NO_TWOHANDING))
+ if(require_twohands)
+ atom_parent.balloon_alert(user, "too weak to wield!")
+ user.dropItemToGround(parent, force = TRUE)
+ else
+ atom_parent.balloon_alert(user, "too weak to wield with both hands!")
+ return
if(user.get_inactive_held_item())
if(require_twohands)
- to_chat(user, span_notice("[parent] is too cumbersome to carry in one hand!"))
- user.dropItemToGround(parent, force=TRUE)
+ atom_parent.balloon_alert(user, "can't carry in one hand!")
+ user.dropItemToGround(parent, force = TRUE)
else
- to_chat(user, span_warning("You need your other hand to be empty!"))
+ atom_parent.balloon_alert(user, "holding something in other hand!")
return
if(user.usable_hands < 2)
if(require_twohands)
user.dropItemToGround(parent, force=TRUE)
- to_chat(user, span_warning("You don't have enough intact hands."))
+ atom_parent.balloon_alert(user, "not enough hands!")
return
// wield update status
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index 03740417678..adb10f128ae 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -59,7 +59,7 @@
RegisterSignal(parent, COMSIG_IMPLANT_IMPLANTING, PROC_REF(implanting))
RegisterSignal(parent, COMSIG_IMPLANT_OTHER, PROC_REF(old_implant))
RegisterSignal(parent, COMSIG_IMPLANT_EXISTING_UPLINK, PROC_REF(new_implant))
- else if(istype(parent, /obj/item/modular_computer/pda))
+ else if(istype(parent, /obj/item/modular_computer))
RegisterSignal(parent, COMSIG_TABLET_CHANGE_ID, PROC_REF(new_ringtone))
RegisterSignal(parent, COMSIG_TABLET_CHECK_DETONATE, PROC_REF(check_detonate))
else if(istype(parent, /obj/item/radio))
@@ -455,7 +455,7 @@
/datum/component/uplink/proc/setup_unlock_code()
unlock_code = generate_code()
var/obj/item/P = parent
- if(istype(parent,/obj/item/modular_computer/pda))
+ if(istype(parent,/obj/item/modular_computer))
unlock_note = "Uplink Passcode: [unlock_code] ([P.name])."
else if(istype(parent,/obj/item/radio))
unlock_note = "Radio Passcode: [unlock_code] ([P.name], [RADIO_TOKEN_UPLINK] channel)."
@@ -465,7 +465,7 @@
/datum/component/uplink/proc/generate_code()
var/returnable_code = ""
- if(istype(parent, /obj/item/modular_computer/pda))
+ if(istype(parent, /obj/item/modular_computer))
returnable_code = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]"
else if(istype(parent, /obj/item/radio))
diff --git a/code/datums/components/wall_mounted.dm b/code/datums/components/wall_mounted.dm
index 8d1722f89fe..00482592f2b 100644
--- a/code/datums/components/wall_mounted.dm
+++ b/code/datums/components/wall_mounted.dm
@@ -16,12 +16,14 @@
on_drop = on_drop_callback
/datum/component/wall_mounted/RegisterWithParent()
+ ADD_TRAIT(parent, TRAIT_WALLMOUNTED, REF(src))
RegisterSignal(hanging_wall_turf, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(hanging_wall_turf, COMSIG_TURF_CHANGE, PROC_REF(on_turf_changing))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(drop_wallmount))
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_linked_destroyed))
/datum/component/wall_mounted/UnregisterFromParent()
+ REMOVE_TRAIT(parent, TRAIT_WALLMOUNTED, REF(src))
UnregisterSignal(hanging_wall_turf, list(COMSIG_ATOM_EXAMINE, COMSIG_TURF_CHANGE))
UnregisterSignal(parent, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED))
hanging_wall_turf = null
diff --git a/code/datums/diseases/gastrolisis.dm b/code/datums/diseases/gastrolisis.dm
index c3a24903b81..aab0fcded1d 100644
--- a/code/datums/diseases/gastrolisis.dm
+++ b/code/datums/diseases/gastrolisis.dm
@@ -40,7 +40,7 @@
var/obj/item/organ/internal/eyes/eyes = locate(/obj/item/organ/internal/eyes/snail) in affected_mob.organs
if(!eyes && SPT_PROB(2.5, seconds_per_tick))
var/obj/item/organ/internal/eyes/snail/new_eyes = new()
- new_eyes.Insert(affected_mob, drop_if_replaced = TRUE)
+ new_eyes.Insert(affected_mob)
affected_mob.visible_message(span_warning("[affected_mob]'s eyes fall out, with snail eyes taking its place!"), \
span_userdanger("You scream in pain as your eyes are pushed out by your new snail eyes!"))
affected_mob.emote("scream")
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index 259a1b28dff..741520ed6ae 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -84,7 +84,7 @@
/datum/disease/transformation/proc/replace_banned_player(mob/living/new_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed.
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [affected_mob.real_name]?", bantype, bantype, 5 SECONDS, affected_mob)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [affected_mob.real_name]?", check_jobban = bantype, role = bantype, poll_time = 5 SECONDS, target_mob = affected_mob, pic_source = affected_mob, role_name_text = "transformation victim")
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(affected_mob, span_userdanger("Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!"))
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 3651735ddfd..2a7423a1fc1 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -604,7 +604,6 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
update_body(is_creating = TRUE)
update_mutations_overlay()// no lizard with human hulk overlay please.
-
/mob/proc/has_dna()
return
diff --git a/code/datums/elements/damage_threshold.dm b/code/datums/elements/damage_threshold.dm
index 918c984b066..60c87dc5ed5 100644
--- a/code/datums/elements/damage_threshold.dm
+++ b/code/datums/elements/damage_threshold.dm
@@ -40,7 +40,7 @@
var/obj/item/item_hitting = hitby
var/tap_vol = istype(item_hitting) ? item_hitting.get_clamped_volume() : 50
source.visible_message(
- span_warning("[src] looks unharmed!"),
+ span_warning("[source] looks unharmed!"),
span_warning("[attack_text] deals no damage to you!"),
span_hear("You hear a thud."),
COMBAT_MESSAGE_RANGE,
diff --git a/code/datums/elements/immerse.dm b/code/datums/elements/immerse.dm
index 1a3b37d6530..900c096aa65 100644
--- a/code/datums/elements/immerse.dm
+++ b/code/datums/elements/immerse.dm
@@ -109,7 +109,7 @@
SIGNAL_HANDLER
if(HAS_TRAIT(movable, TRAIT_IMMERSED))
return
- if(movable.layer >= ABOVE_ALL_MOB_LAYER || !ISINRANGE(movable.plane, MUTATE_PLANE(FLOOR_PLANE, source), MUTATE_PLANE(GAME_PLANE_UPPER_FOV_HIDDEN, source)))
+ if(movable.layer >= ABOVE_ALL_MOB_LAYER || !ISINRANGE(movable.plane, MUTATE_PLANE(FLOOR_PLANE, source), MUTATE_PLANE(GAME_PLANE, source)))
return
if(is_type_in_typecache(movable, movables_to_ignore))
return
diff --git a/code/datums/elements/snail_crawl.dm b/code/datums/elements/snail_crawl.dm
index 9893c9922ca..80b3f66e03b 100644
--- a/code/datums/elements/snail_crawl.dm
+++ b/code/datums/elements/snail_crawl.dm
@@ -23,14 +23,24 @@
if(snail.resting && !snail.buckled && lubricate(snail))
snail.add_movespeed_modifier(/datum/movespeed_modifier/snail_crawl)
+ //SKYRAT EDIT ADDITION BEGIN - This is to prevent snails from achieving FTL speeds without gravity, think of it like snails affixing to walls irl.
+ ADD_TRAIT(snail, TRAIT_NEGATES_GRAVITY, TRAIT_GENERIC)
+ snail.AddElement(/datum/element/forced_gravity, 0)
+ if(HAS_TRAIT(snail, TRAIT_SETTLER)) //This is to keep settlers from reaching FTL speeds too.
+ snail.remove_movespeed_modifier(/datum/movespeed_modifier/snail_crawl)
+ //SKYRAT EDIT ADDITION END
else
snail.remove_movespeed_modifier(/datum/movespeed_modifier/snail_crawl)
+ //SKYRAT EDIT ADDITION BEGIN - This clears the forced gravity so they're affected by it while standing.
+ REMOVE_TRAIT(snail, TRAIT_NEGATES_GRAVITY, TRAIT_GENERIC)
+ snail.RemoveElement(/datum/element/forced_gravity, 0)
+ //SKYRAT EDIT ADDITION END
/datum/element/snailcrawl/proc/lubricate(atom/movable/snail)
SIGNAL_HANDLER
var/turf/open/OT = get_turf(snail)
if(istype(OT))
- OT.MakeSlippery(TURF_WET_WATER, 1 SECONDS) //SKYRAT EDIT: Roundstart Snails - No more lube
- OT.wash(CLEAN_WASH) //SKYRAT EDIT: Roundstart Snails - Snails Keep Clean
+ OT.MakeSlippery(TURF_WET_WATER, 1 SECONDS) //SKYRAT EDIT CHANGE: Roundstart Snails - No more lube - ORIGINAL: OT.MakeSlippery(TURF_WET_LUBE, 20)
+ OT.wash(CLEAN_WASH) //SKYRAT EDIT ADDITION: Roundstart Snails - Snails Keep Clean
return TRUE
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 9a4a2f263e6..64c7de25764 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -119,7 +119,10 @@
TIMER_COOLDOWN_START(user, type, audio_cooldown)
//SKYRAT EDIT CHANGE BEGIN
//playsound(user, tmp_sound, 50, vary) - SKYRAT EDIT - ORIGINAL
- playsound(user, tmp_sound, sound_volume, vary)
+ if(istype(src, /datum/emote/living/lewd))
+ play_lewd_sound(user, tmp_sound, sound_volume, vary, pref_to_check = /datum/preference/toggle/erp/sounds)
+ else
+ playsound(user, tmp_sound, sound_volume, vary)
//SKYRAT EDIT CHANGE END
var/user_turf = get_turf(user)
@@ -128,27 +131,30 @@
if(!ghost.client || isnewplayer(ghost))
continue
if(get_chat_toggles(ghost.client) & CHAT_GHOSTSIGHT && !(ghost in viewers(user_turf, null)))
- ghost.show_message("[FOLLOW_LINK(ghost, user)] [dchatmsg]")
+ if(pref_check_emote(ghost)) // SKYRAT EDIT ADDITION - Pref checked emotes
+ ghost.show_message("[FOLLOW_LINK(ghost, user)] [dchatmsg]") // SKYRAT EDIT CHANGE - Indented
if(emote_type & (EMOTE_AUDIBLE | EMOTE_VISIBLE)) //emote is audible and visible
- user.audible_message(msg, deaf_message = "You see how [user][space][msg]", audible_message_flags = EMOTE_MESSAGE, separation = space) // SKYRAT EDIT - Better emotes - ORIGINAL: user.audible_message(msg, deaf_message = "You see how [user] [msg]", audible_message_flags = EMOTE_MESSAGE)
+ user.audible_message(msg, deaf_message = "You see how [user][space][msg]", audible_message_flags = EMOTE_MESSAGE, separation = space, pref_to_check = pref_to_check) // SKYRAT EDIT - Better emotes - ORIGINAL: user.audible_message(msg, deaf_message = "You see how [user] [msg]", audible_message_flags = EMOTE_MESSAGE)
else if(emote_type & EMOTE_VISIBLE) //emote is only visible
- user.visible_message(msg, visible_message_flags = EMOTE_MESSAGE, separation = space) // SKYRAT EDIT - Better emotes - ORIGINAL: user.visible_message(msg, visible_message_flags = EMOTE_MESSAGE)
+ user.visible_message(msg, visible_message_flags = EMOTE_MESSAGE, separation = space, pref_to_check = pref_to_check) // SKYRAT EDIT - Better emotes - ORIGINAL: user.visible_message(msg, visible_message_flags = EMOTE_MESSAGE)
if(emote_type & EMOTE_IMPORTANT)
for(var/mob/living/viewer in viewers())
if(viewer.is_blind() && !viewer.can_hear())
- to_chat(viewer, msg)
+ if(pref_check_emote(viewer)) // SKYRAT EDIT ADDITION - Pref checked emotes
+ to_chat(viewer, msg) // SKYRAT EDIT CHANGE - Indented
// SKYRAT EDIT -- BEGIN -- ADDITION -- AI QOL - RELAY EMOTES OVER HOLOPADS
var/obj/effect/overlay/holo_pad_hologram/hologram = GLOB.hologram_impersonators[user]
if(hologram)
if(emote_type & (EMOTE_AUDIBLE | EMOTE_VISIBLE))
- hologram.audible_message(msg, deaf_message = span_emote("You see how [user] [msg]"), audible_message_flags = EMOTE_MESSAGE)
+ hologram.audible_message(msg, deaf_message = span_emote("You see how [user] [msg]"), audible_message_flags = EMOTE_MESSAGE, pref_to_check = pref_to_check)
else if(emote_type & EMOTE_VISIBLE)
- hologram.visible_message(msg, visible_message_flags = EMOTE_MESSAGE)
+ hologram.visible_message(msg, visible_message_flags = EMOTE_MESSAGE, pref_to_check = pref_to_check)
if(emote_type & EMOTE_IMPORTANT)
for(var/mob/living/viewer in viewers(world.view, hologram))
if(viewer.is_blind() && !viewer.can_hear())
- to_chat(viewer, msg)
+ if(pref_check_emote(viewer))
+ to_chat(viewer, msg)
// SKYRAT EDIT -- END
/**
diff --git a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
index 5ff1fde7c27..2f0b406de06 100644
--- a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
+++ b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
@@ -80,6 +80,15 @@ SKYRAT EDIT END */
name = "Party Hat (Worn)"
icon_file = 'icons/mob/clothing/head/costume.dmi'
+/datum/greyscale_config/santa_hat
+ name = "Santa Hat"
+ icon_file = 'icons/obj/clothing/head/costume.dmi'
+ json_config = 'code/datums/greyscale/json_configs/santa_hat.json'
+
+/datum/greyscale_config/santa_hat/worn
+ name = "Santa Hat (Worn)"
+ icon_file = 'icons/mob/clothing/head/costume.dmi'
+
//
// MASKS
//
@@ -354,6 +363,18 @@ SKYRAT EDIT END */
icon_file = 'icons/mob/clothing/under/shorts_pants_shirts.dmi'
json_config = 'code/datums/greyscale/json_configs/buttondown_skirt_worn.json'
+//
+// LABCOATS
+//
+
+/datum/greyscale_config/labcoat
+ name = "Labcoat"
+ icon_file = 'icons/obj/clothing/suits/labcoat.dmi'
+ json_config = 'code/datums/greyscale/json_configs/labcoat.json'
+
+/datum/greyscale_config/labcoat/worn
+ name = "Labcoat (Worn)"
+ icon_file = 'icons/mob/clothing/suits/labcoat.dmi'
//
// SUITS
diff --git a/code/datums/greyscale/json_configs/labcoat.json b/code/datums/greyscale/json_configs/labcoat.json
new file mode 100644
index 00000000000..f95b86893b0
--- /dev/null
+++ b/code/datums/greyscale/json_configs/labcoat.json
@@ -0,0 +1,54 @@
+{
+ "labcoat_job": [
+ {
+ "type": "icon_state",
+ "icon_state": "labcoat_job",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sash",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "shoulder",
+ "blend_mode": "overlay",
+ "color_ids": [ 3 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "back",
+ "blend_mode": "overlay",
+ "color_ids": [ 4 ]
+ }
+ ],
+ "labcoat_job_t": [
+ {
+ "type": "icon_state",
+ "icon_state": "labcoat_job_t",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sash_t",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "shoulder",
+ "blend_mode": "overlay",
+ "color_ids": [ 3 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "back_t",
+ "blend_mode": "overlay",
+ "color_ids": [ 4 ]
+ }
+ ]
+}
diff --git a/code/datums/greyscale/json_configs/santa_hat.json b/code/datums/greyscale/json_configs/santa_hat.json
new file mode 100644
index 00000000000..dda130d59c7
--- /dev/null
+++ b/code/datums/greyscale/json_configs/santa_hat.json
@@ -0,0 +1,16 @@
+{
+ "santa_hat": [
+ {
+ "type": "icon_state",
+ "icon_state": "santa_base",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "santa_trim",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ }
+ ]
+}
diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm
index 72a23adf768..ea46cc91319 100644
--- a/code/datums/id_trim/jobs.dm
+++ b/code/datums/id_trim/jobs.dm
@@ -203,6 +203,28 @@
)
job = /datum/job/botanist
+/datum/id_trim/job/bridge_assistant
+ assignment = "Bridge Assistant"
+ trim_state = "trim_assistant"
+ department_color = COLOR_COMMAND_BLUE
+ subdepartment_color = COLOR_COMMAND_BLUE
+ sechud_icon_state = SECHUD_BRIDGE_ASSISTANT
+ minimal_access = list(
+ ACCESS_COMMAND,
+ ACCESS_EVA,
+ ACCESS_GATEWAY,
+ ACCESS_MAINT_TUNNELS,
+ ACCESS_RC_ANNOUNCE,
+ ACCESS_TELEPORTER,
+ ACCESS_WEAPONS,
+ )
+ extra_access = list()
+ template_access = list(
+ ACCESS_CAPTAIN,
+ ACCESS_CHANGE_IDS,
+ )
+ job = /datum/job/bridge_assistant
+
/datum/id_trim/job/captain
assignment = "Captain"
intern_alt_name = "Captain-in-Training"
diff --git a/code/datums/json_savefile.dm b/code/datums/json_savefile.dm
index 08f8cf2d616..dd2a6af0b98 100644
--- a/code/datums/json_savefile.dm
+++ b/code/datums/json_savefile.dm
@@ -57,7 +57,7 @@ GENERAL_PROTECT_DATUM(/datum/json_savefile)
/datum/json_savefile/proc/save()
if(path)
- rustg_file_write(json_encode(tree), path)
+ rustg_file_write(json_encode(tree, JSON_PRETTY_PRINT), path)
/datum/json_savefile/serialize_list(list/options, list/semvers)
SHOULD_CALL_PARENT(FALSE)
diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm
index b8847cd6521..9126f0a090e 100644
--- a/code/datums/mood_events/generic_positive_events.dm
+++ b/code/datums/mood_events/generic_positive_events.dm
@@ -185,12 +185,6 @@
mood_change = 6
timeout = 5 MINUTES
-/datum/mood_event/pet_borg
- description = "I just love my robotic friends!"
- mood_change = 3
- timeout = 5 MINUTES
- required_job = list(/datum/job/research_director, /datum/job/scientist, /datum/job/roboticist, /datum/job/geneticist)
-
/datum/mood_event/bottle_flip
description = "The bottle landing like that was satisfying."
mood_change = 2
diff --git a/code/datums/mood_events/needs_events.dm b/code/datums/mood_events/needs_events.dm
index ceee687918e..dd710554d8d 100644
--- a/code/datums/mood_events/needs_events.dm
+++ b/code/datums/mood_events/needs_events.dm
@@ -76,3 +76,47 @@
description = "There's nothing like the feeling of a freshly laundered jumpsuit."
mood_change = 2
timeout = 10 MINUTES
+
+/datum/mood_event/surrounded_by_silicon
+ description = "I'm surrounded by perfect lifeforms!!"
+ mood_change = 8
+
+/datum/mood_event/around_many_silicon
+ description = "So many silicon lifeforms near me!"
+ mood_change = 4
+
+/datum/mood_event/around_silicon
+ description = "The silicon lifeforms near me are absolutely perfect."
+ mood_change = 2
+
+/datum/mood_event/around_organic
+ description = "The organics near me remind me of the inferiority of flesh."
+ mood_change = -2
+
+/datum/mood_event/around_many_organic
+ description = "So many disgusting organics!"
+ mood_change = -4
+
+/datum/mood_event/surrounded_by_organic
+ description = "I'm surrounded by disgusting organics!!"
+ mood_change = -8
+
+/datum/mood_event/completely_robotic
+ description = "I've abandoned my feeble flesh, my form is perfect!!"
+ mood_change = 8
+
+/datum/mood_event/very_robotic
+ description = "I'm more robot than organic!"
+ mood_change = 4
+
+/datum/mood_event/balanced_robotic
+ description = "I'm part machine, part organic."
+ mood_change = 0
+
+/datum/mood_event/very_organic
+ description = "I hate this feeble and weak flesh!"
+ mood_change = -4
+
+/datum/mood_event/completely_organic
+ description = "I'm completely organic, this is miserable!!"
+ mood_change = -8
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index 0fdf2820d2f..38806aa7529 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -524,14 +524,16 @@
var/obj/item/organ/internal/brain/brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN)
if(brain)
+ brain.Remove(owner, special = TRUE)
brain.zone = BODY_ZONE_CHEST
+ brain.Insert(owner, special = TRUE)
var/obj/item/bodypart/head/head = owner.get_bodypart(BODY_ZONE_HEAD)
if(head)
owner.visible_message(span_warning("[owner]'s head splatters with a sickening crunch!"), ignored_mobs = list(owner))
new /obj/effect/gibspawner/generic(get_turf(owner), owner)
- head.dismember(dam_type = BRUTE, silent = TRUE)
head.drop_organs()
+ head.dismember(dam_type = BRUTE, silent = TRUE)
qdel(head)
RegisterSignal(owner, COMSIG_ATTEMPT_CARBON_ATTACH_LIMB, PROC_REF(abort_attachment))
@@ -539,14 +541,18 @@
. = ..()
if(.)
return TRUE
- var/obj/item/organ/internal/brain/brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN)
- if(brain) //so this doesn't instantly kill you. we could delete the brain, but it lets people cure brain issues they /really/ shouldn't be
- brain.zone = initial(brain.zone)
+
UnregisterSignal(owner, COMSIG_ATTEMPT_CARBON_ATTACH_LIMB)
var/successful = owner.regenerate_limb(BODY_ZONE_HEAD)
if(!successful)
stack_trace("HARS mutation head regeneration failed! (usually caused by headless syndrome having a head)")
return TRUE
+ var/obj/item/organ/internal/brain/brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN)
+ if(brain)
+ brain.Remove(owner, special = TRUE)
+ brain.zone = initial(brain.zone)
+ brain.Insert(owner, special = TRUE)
+
owner.dna.species.regenerate_organs(owner, replace_current = FALSE, excluded_zones = list(BODY_ZONE_CHEST)) //replace_current needs to be FALSE to prevent weird adding and removing mutation healing
owner.apply_damage(damage = 50, damagetype = BRUTE, def_zone = BODY_ZONE_HEAD) //and this to DISCOURAGE organ farming, or at least not make it free.
owner.visible_message(span_warning("[owner]'s head returns with a sickening crunch!"), span_warning("Your head regrows with a sickening crack! Ouch."))
diff --git a/code/datums/proximity_monitor/fields/timestop.dm b/code/datums/proximity_monitor/fields/timestop.dm
index 86ea41aee01..c48759c1deb 100644
--- a/code/datums/proximity_monitor/fields/timestop.dm
+++ b/code/datums/proximity_monitor/fields/timestop.dm
@@ -238,7 +238,7 @@
//you don't look quite right, is something the matter?
/datum/proximity_monitor/advanced/timestop/proc/into_the_negative_zone(atom/A)
- A.add_atom_colour(list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0), TEMPORARY_COLOUR_PRIORITY)
+ A.add_atom_colour(COLOR_MATRIX_INVERT, TEMPORARY_COLOUR_PRIORITY)
//let's put some colour back into your cheeks
/datum/proximity_monitor/advanced/timestop/proc/escape_the_negative_zone(atom/A)
diff --git a/code/datums/quirks/negative_quirks/junkie.dm b/code/datums/quirks/negative_quirks/junkie.dm
index 269f6d2d96e..2ce29f743c3 100644
--- a/code/datums/quirks/negative_quirks/junkie.dm
+++ b/code/datums/quirks/negative_quirks/junkie.dm
@@ -134,7 +134,7 @@
smoker_lungs = /obj/item/organ/internal/lungs/smoker_lungs
if(!isnull(smoker_lungs))
smoker_lungs = new smoker_lungs
- smoker_lungs.Insert(carbon_holder, special = TRUE, drop_if_replaced = FALSE)
+ smoker_lungs.Insert(carbon_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/item_quirk/junkie/smoker/process(seconds_per_tick)
. = ..()
diff --git a/code/datums/quirks/negative_quirks/prosthetic_organ.dm b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
index 6330035b5a7..0fd5061a787 100644
--- a/code/datums/quirks/negative_quirks/prosthetic_organ.dm
+++ b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
@@ -49,7 +49,7 @@
medical_record_text = "During physical examination, patient was found to have a low-budget prosthetic [slot_string]. \
Removal of these organs is known to be dangerous to the patient as well as the practitioner."
old_organ = human_holder.get_organ_slot(organ_slot)
- if(prosthetic.Insert(human_holder, special = TRUE, drop_if_replaced = TRUE))
+ if(prosthetic.Insert(human_holder, special = TRUE))
old_organ.moveToNullspace()
STOP_PROCESSING(SSobj, old_organ)
diff --git a/code/datums/quirks/negative_quirks/tin_man.dm b/code/datums/quirks/negative_quirks/tin_man.dm
index 5a4ab4b1357..e6d411bc772 100644
--- a/code/datums/quirks/negative_quirks/tin_man.dm
+++ b/code/datums/quirks/negative_quirks/tin_man.dm
@@ -1,7 +1,7 @@
/datum/quirk/tin_man
name = "Tin Man"
desc = "Oops! All Prosthetics! Due to some truly cruel cosmic punishment, most of your internal organs have been replaced with surplus prosthetics."
- icon = FA_ICON_ROBOT
+ icon = FA_ICON_USER_GEAR
value = -6
medical_record_text = "During physical examination, patient was found to have numerous low-budget prosthetic internal organs. \
Removal of these organs is known to be dangerous to the patient as well as the practitioner."
@@ -30,7 +30,7 @@
for(var/organ_slot in possible_organ_slots)
var/organ_path = possible_organ_slots[organ_slot]
var/obj/item/organ/new_organ = new organ_path()
- new_organ.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_organ.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/tin_man/post_add()
to_chat(quirk_holder, span_boldannounce("Most of your internal organs have been replaced with surplus prosthetics. They are fragile and will easily come apart under duress. \
diff --git a/code/datums/quirks/neutral_quirks/transhumanist.dm b/code/datums/quirks/neutral_quirks/transhumanist.dm
new file mode 100644
index 00000000000..b2cf60143a2
--- /dev/null
+++ b/code/datums/quirks/neutral_quirks/transhumanist.dm
@@ -0,0 +1,174 @@
+#define MOOD_CATEGORY_TRANSHUMANIST_PEOPLE "transhumanist_people"
+#define MOOD_CATEGORY_TRANSHUMANIST_BODYPART "transhumanist_bodypart"
+// The number of silicons minus the number of organics determines the level
+#define TRANSHUMANIST_LEVEL_ECSTATIC 4
+#define TRANSHUMANIST_LEVEL_HAPPY 1
+#define TRANSHUMANIST_LEVEL_NEUTRAL 0
+#define TRANSHUMANIST_LEVEL_UNHAPPY -2
+#define TRANSHUMANIST_LEVEL_ANGRY -5
+
+#define BODYPART_SCORE_ORGANIC 1
+#define BODYPART_SCORE_SILICON 2
+#define BODYPART_SCORE_OTHER_BODYTYPES 3
+#define BODYPART_SCORE_OVERALL 4
+
+
+/datum/quirk/transhumanist
+ name = "Transhumanist"
+ desc = "You see silicon life as the perfect lifeform and despise organic flesh. You are happier around silicons, but get frustrated when around organics. You seek to replace your fleshy limbs with their silicon counterparts. You start with a robotic limb."
+ icon = FA_ICON_ROBOT
+ quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_PROCESSES|QUIRK_MOODLET_BASED
+ value = 0
+ gain_text = span_notice("You have a desire to ditch your feeble organic flesh and surround yourself with robots.")
+ lose_text = span_danger("Robots don't seem all that great anymore.")
+ medical_record_text = "Patient reports hating pathetic creatures of meat and bone."
+ mail_goodies = list(
+ /obj/item/stock_parts/cell/potato,
+ /obj/item/stack/cable_coil,
+ /obj/item/toy/talking/ai,
+ /obj/item/toy/figure/borg,
+ )
+ var/slot_string
+ var/obj/item/bodypart/old_limb
+
+/datum/quirk/transhumanist/add(client/client_source)
+ RegisterSignal(quirk_holder, COMSIG_CARBON_POST_ATTACH_LIMB, PROC_REF(calculate_bodypart_score))
+ RegisterSignal(quirk_holder, COMSIG_CARBON_POST_REMOVE_LIMB, PROC_REF(calculate_bodypart_score))
+ RegisterSignal(quirk_holder, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(calculate_bodypart_score))
+ RegisterSignal(quirk_holder, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(calculate_bodypart_score))
+ calculate_bodypart_score()
+
+/datum/quirk/transhumanist/remove()
+ UnregisterSignal(quirk_holder, list(COMSIG_CARBON_REMOVE_LIMB, COMSIG_CARBON_ATTACH_LIMB))
+
+/datum/quirk/transhumanist/proc/get_bodypart_score(mob/living/carbon/target, limbs_only = FALSE)
+ var/organic_bodytypes = 0
+ var/silicon_bodytypes = 0
+ var/other_bodytypes = FALSE
+ for(var/obj/item/bodypart/part as anything in target.bodyparts)
+ if(part.bodytype & BODYTYPE_ROBOTIC)
+ silicon_bodytypes += 1
+ else if(part.bodytype & BODYTYPE_ORGANIC)
+ organic_bodytypes += 0.1
+ else
+ other_bodytypes = TRUE
+
+ if(!limbs_only)
+ for(var/obj/item/organ/organ as anything in target.organs)
+ if(organ.organ_flags & ORGAN_ROBOTIC)
+ silicon_bodytypes += 0.25
+ else if(organ.organ_flags & ORGAN_ORGANIC)
+ organic_bodytypes += 0.02
+
+ return list(
+ BODYPART_SCORE_ORGANIC = organic_bodytypes,
+ BODYPART_SCORE_SILICON = silicon_bodytypes,
+ BODYPART_SCORE_OTHER_BODYTYPES = other_bodytypes,
+ BODYPART_SCORE_OVERALL = silicon_bodytypes - organic_bodytypes
+ )
+
+
+/datum/quirk/transhumanist/proc/calculate_bodypart_score()
+ SIGNAL_HANDLER
+ var/list/score = get_bodypart_score(quirk_holder)
+ var/organic_bodytypes = score[BODYPART_SCORE_ORGANIC]
+ var/silicon_bodytypes = score[BODYPART_SCORE_SILICON]
+ var/other_bodytypes = score[BODYPART_SCORE_OTHER_BODYTYPES]
+
+ if(!other_bodytypes)
+ if(organic_bodytypes <= 0.02)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_BODYPART, /datum/mood_event/completely_robotic)
+ return
+ else if(silicon_bodytypes == 0)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_BODYPART, /datum/mood_event/completely_organic)
+ return
+ else if(silicon_bodytypes == 0 && organic_bodytypes == 0)
+ quirk_holder.clear_mood_event(MOOD_CATEGORY_TRANSHUMANIST_BODYPART)
+ return
+
+ var/bodypart_score = score[BODYPART_SCORE_OVERALL]
+ switch(bodypart_score)
+ if(3 to INFINITY)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_BODYPART, /datum/mood_event/very_robotic)
+ if(0 to 3)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_BODYPART, /datum/mood_event/balanced_robotic)
+ if(-INFINITY to 0)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_BODYPART, /datum/mood_event/very_organic)
+
+
+/datum/quirk/transhumanist/add_unique(client/client_source)
+ var/limb_type = GLOB.limb_choice_transhuman[client_source?.prefs?.read_preference(/datum/preference/choiced/prosthetic)]
+ if(isnull(limb_type)) //Client gone or they chose a random prosthetic
+ limb_type = GLOB.limb_choice_transhuman[pick(GLOB.limb_choice_transhuman)]
+
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ var/obj/item/bodypart/new_part = new limb_type()
+
+ slot_string = "[new_part.plaintext_zone]"
+ old_limb = human_holder.return_and_replace_bodypart(new_part, special = TRUE)
+
+/datum/quirk/transhumanist/post_add()
+ if(slot_string)
+ to_chat(quirk_holder, span_boldannounce("Your [slot_string] has been replaced with a robot arm. You need to use a welding tool and cables to repair it, instead of sutures and regenerative meshes."))
+
+/datum/quirk/transhumanist/remove()
+ if(old_limb)
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ human_holder.del_and_replace_bodypart(old_limb, special = TRUE)
+ old_limb = null
+ quirk_holder.clear_mood_event(MOOD_CATEGORY_TRANSHUMANIST_BODYPART)
+ quirk_holder.clear_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE)
+
+/datum/quirk/transhumanist/process(seconds_per_tick)
+ var/organics_nearby = 0
+ var/silicons_nearby = 0
+
+ // Only cares about things that are nearby
+ var/list/mobs = get_hearers_in_LOS(3, quirk_holder)
+
+ for(var/mob/living/target in mobs)
+ if(!isturf(target.loc) || target == quirk_holder || target.alpha <= 128 || target.invisibility > quirk_holder.see_invisible)
+ continue
+
+ if(target.mob_biotypes & MOB_ORGANIC)
+ var/list/score = get_bodypart_score(target, limbs_only = TRUE)
+ // For an average human, they'll need 2 augmented limbs to not get counted as an organic nor a silicon.
+ // If some monstrosity has 20-30 organic limbs, they'll likely need more.
+ if(score[BODYPART_SCORE_OVERALL] < 1)
+ organics_nearby += 1
+ else if(score[BODYPART_SCORE_ORGANIC] == 0)
+ silicons_nearby += 1
+ else if(target.mob_biotypes & MOB_ROBOTIC)
+ // Dead silicons don't count, they're basically just machinery
+ if(target.stat != DEAD)
+ silicons_nearby += 1
+
+ var/mood_result = silicons_nearby - organics_nearby
+
+ switch(mood_result)
+ if(TRANSHUMANIST_LEVEL_ECSTATIC to INFINITY)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE, /datum/mood_event/surrounded_by_silicon)
+ if(TRANSHUMANIST_LEVEL_HAPPY to TRANSHUMANIST_LEVEL_ECSTATIC)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE, /datum/mood_event/around_many_silicon)
+ if(TRANSHUMANIST_LEVEL_NEUTRAL + 0.01 to TRANSHUMANIST_LEVEL_HAPPY)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE, /datum/mood_event/around_silicon)
+ if(TRANSHUMANIST_LEVEL_NEUTRAL)
+ quirk_holder.clear_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE)
+ if(TRANSHUMANIST_LEVEL_UNHAPPY to TRANSHUMANIST_LEVEL_NEUTRAL - 0.01)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE, /datum/mood_event/around_organic)
+ if(TRANSHUMANIST_LEVEL_ANGRY to TRANSHUMANIST_LEVEL_UNHAPPY)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE, /datum/mood_event/around_many_organic)
+ if(-INFINITY to TRANSHUMANIST_LEVEL_ANGRY)
+ quirk_holder.add_mood_event(MOOD_CATEGORY_TRANSHUMANIST_PEOPLE, /datum/mood_event/surrounded_by_organic)
+
+#undef MOOD_CATEGORY_TRANSHUMANIST_PEOPLE
+#undef MOOD_CATEGORY_TRANSHUMANIST_BODYPART
+#undef TRANSHUMANIST_LEVEL_ECSTATIC
+#undef TRANSHUMANIST_LEVEL_HAPPY
+#undef TRANSHUMANIST_LEVEL_NEUTRAL
+#undef TRANSHUMANIST_LEVEL_UNHAPPY
+#undef TRANSHUMANIST_LEVEL_ANGRY
+#undef BODYPART_SCORE_ORGANIC
+#undef BODYPART_SCORE_SILICON
+#undef BODYPART_SCORE_OTHER_BODYTYPES
+#undef BODYPART_SCORE_OVERALL
diff --git a/code/datums/records/manifest.dm b/code/datums/records/manifest.dm
index 7b43b7644ef..4ab24ee69c9 100644
--- a/code/datums/records/manifest.dm
+++ b/code/datums/records/manifest.dm
@@ -174,3 +174,36 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
target.rank = assignment
target.trim = trim
+
+/datum/manifest/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/manifest/ui_status(mob/user, datum/ui_state/state)
+ return (isnewplayer(user) || isobserver(user) || isAI(user) || ispAI(user) || user.client?.holder) ? UI_INTERACTIVE : UI_CLOSE
+
+/datum/manifest/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if (!ui)
+ ui = new(user, src, "CrewManifest")
+ ui.open()
+
+/datum/manifest/ui_data(mob/user)
+ var/list/positions = list()
+ for(var/datum/job_department/department as anything in SSjob.joinable_departments)
+ var/open = 0
+ var/list/exceptions = list()
+ for(var/datum/job/job as anything in department.department_jobs)
+ if(job.total_positions == -1)
+ exceptions += job.title
+ continue
+ var/open_slots = job.total_positions - job.current_positions
+ if(open_slots < 1)
+ continue
+ open += open_slots
+ positions[department.department_name] = list("exceptions" = exceptions, "open" = open)
+
+ return list(
+ "manifest" = get_manifest(),
+ "positions" = positions
+ )
+
diff --git a/code/datums/station_traits/_station_trait.dm b/code/datums/station_traits/_station_trait.dm
index 8e8303c0366..54a711aaba6 100644
--- a/code/datums/station_traits/_station_trait.dm
+++ b/code/datums/station_traits/_station_trait.dm
@@ -1,3 +1,6 @@
+/// Station traits displayed in the lobby
+GLOBAL_LIST_EMPTY(lobby_station_traits)
+
///Base class of station traits. These are used to influence rounds in one way or the other by influencing the levers of the station.
/datum/station_trait
///Name of the trait
@@ -22,10 +25,16 @@
var/trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
/// Whether or not this trait can be reverted by an admin
var/can_revert = TRUE
+ /// If set to true we'll show a button on the lobby to notify people about this trait
+ var/sign_up_button = FALSE
+ /// Lobby buttons controlled by this trait
+ var/list/lobby_buttons = list()
/// The ID that we look for in dynamic.json. Not synced with 'name' because I can already see this go wrong
var/dynamic_threat_id
/// If ran during dynamic, do we reduce the total threat? Will be overriden by config if set
var/threat_reduction = 0
+ /// Trait should not be instantiated in a round if its type matches this type
+ var/abstract_type = /datum/station_trait
/datum/station_trait/New()
. = ..()
@@ -34,7 +43,8 @@
if(threat_reduction)
GLOB.dynamic_station_traits[src] = threat_reduction
-
+ if(sign_up_button)
+ GLOB.lobby_station_traits += src
if(trait_processes)
START_PROCESSING(SSstation, src)
if(trait_to_give)
@@ -43,14 +53,10 @@
/datum/station_trait/Destroy()
SSstation.station_traits -= src
GLOB.dynamic_station_traits.Remove(src)
+ destroy_lobby_buttons()
return ..()
-/// Proc ran when round starts. Use this for roundstart effects.
-/datum/station_trait/proc/on_round_start()
- SIGNAL_HANDLER
- return
-
-///type of info the centcom report has on this trait, if any.
+/// Returns the type of info the centcom report has on this trait, if any.
/datum/station_trait/proc/get_report()
return "[name] - [report_message]"
@@ -64,7 +70,7 @@
qdel(src)
-///Called by decals if they can be colored, to see if we got some cool colors for them. Only takes the first station trait
+/// Called by decals if they can be colored, to see if we got some cool colors for them. Only takes the first station trait
/proc/request_station_colors(atom/thing_to_color, pattern)
for(var/datum/station_trait/trait in SSstation.station_traits)
var/decal_color = trait.get_decal_color(thing_to_color, pattern || PATTERN_DEFAULT)
@@ -72,6 +78,52 @@
return decal_color
return null
-///Return a color for the decals, if any
+/// Return a color for the decals, if any
/datum/station_trait/proc/get_decal_color(thing_to_color, pattern)
return
+
+/// Return TRUE if we want to show a lobby button, by default we assume we don't want it after the round begins
+/datum/station_trait/proc/can_display_lobby_button(client/player)
+ return sign_up_button && !SSticker.HasRoundStarted()
+
+/* SKYRAT EDIT REMOVAL
+/// Apply any additional handling we need to our lobby button
+/datum/station_trait/proc/setup_lobby_button(atom/movable/screen/lobby/button/sign_up/lobby_button)
+ SHOULD_CALL_PARENT(TRUE)
+ lobby_buttons |= lobby_button
+ RegisterSignal(lobby_button, COMSIG_ATOM_UPDATE_ICON, PROC_REF(on_lobby_button_update_icon))
+ RegisterSignal(lobby_button, COMSIG_CLICK, PROC_REF(on_lobby_button_click))
+ RegisterSignal(lobby_button, COMSIG_QDELETING, PROC_REF(on_lobby_button_destroyed))
+ lobby_button.update_appearance(UPDATE_ICON)
+
+/// Called when our lobby button is clicked on
+/datum/station_trait/proc/on_lobby_button_click(atom/movable/screen/lobby/button/sign_up/lobby_button, location, control, params, mob/dead/new_player/user)
+ SIGNAL_HANDLER
+ return
+
+/// Called when our lobby button tries to update its appearance
+/datum/station_trait/proc/on_lobby_button_update_icon(atom/movable/screen/lobby/button/sign_up/lobby_button, updates)
+ SIGNAL_HANDLER
+ return
+
+/// Don't hold references to deleted buttons
+/datum/station_trait/proc/on_lobby_button_destroyed(atom/movable/screen/lobby/button/sign_up/lobby_button)
+ SIGNAL_HANDLER
+ lobby_buttons -= lobby_button
+*/
+
+/// Proc ran when round starts. Use this for roundstart effects. By default we clean up our buttons here.
+/datum/station_trait/proc/on_round_start()
+ SIGNAL_HANDLER
+ destroy_lobby_buttons()
+
+/// Remove all of our active lobby buttons
+/datum/station_trait/proc/destroy_lobby_buttons()
+ for (var/atom/movable/screen/button as anything in lobby_buttons)
+ var/mob/hud_owner = button.get_mob()
+ qdel(button)
+ if (QDELETED(hud_owner))
+ continue
+ var/datum/hud/using_hud = hud_owner.hud_used
+ using_hud?.show_hud(using_hud?.hud_version)
+ lobby_buttons = list()
diff --git a/code/datums/station_traits/job_traits.dm b/code/datums/station_traits/job_traits.dm
new file mode 100644
index 00000000000..4d6de5e6951
--- /dev/null
+++ b/code/datums/station_traits/job_traits.dm
@@ -0,0 +1,170 @@
+#define CAN_ROLL_ALWAYS 1 //always can roll for antag
+#define CAN_ROLL_PROTECTED 2 //can roll if config lets protected roles roll
+#define CAN_ROLL_NEVER 3 //never roll antag
+
+/**
+ * A station trait which enables a temporary job
+ * Generally speaking these should always all be mutually exclusive, don't have too many at once
+ */
+/datum/station_trait/job
+ sign_up_button = TRUE
+ abstract_type = /datum/station_trait/job
+ /// What tooltip to show on the button
+ var/button_desc = "Sign up to gain some kind of unusual job, not available in most rounds."
+ /// Can this job roll antag?
+ var/can_roll_antag = CAN_ROLL_ALWAYS
+ /// How many positions to spawn?
+ var/position_amount = 1
+ /// Type of job to enable
+ var/datum/job/job_to_add = /datum/job/clown
+ /// Who signed up to this in the lobby
+ var/list/lobby_candidates
+
+/datum/station_trait/job/New()
+ . = ..()
+ switch(can_roll_antag)
+ if(CAN_ROLL_PROTECTED)
+ SSstation.antag_protected_roles += job_to_add::title
+ if(CAN_ROLL_NEVER)
+ SSstation.antag_restricted_roles += job_to_add::title
+ blacklist += subtypesof(/datum/station_trait/job) - type // All but ourselves
+ RegisterSignal(SSdcs, COMSIG_GLOB_PRE_JOBS_ASSIGNED, PROC_REF(pre_jobs_assigned))
+/* SKYRAT EDIT REMOVAL
+/datum/station_trait/job/setup_lobby_button(atom/movable/screen/lobby/button/sign_up/lobby_button)
+ RegisterSignal(lobby_button, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_lobby_button_update_overlays))
+ lobby_button.desc = button_desc
+ return ..()
+
+/datum/station_trait/job/on_lobby_button_click(atom/movable/screen/lobby/button/sign_up/lobby_button, location, control, params, mob/dead/new_player/user)
+ if (LAZYFIND(lobby_candidates, user))
+ LAZYREMOVE(lobby_candidates, user)
+ else
+ LAZYADD(lobby_candidates, user)
+
+/datum/station_trait/job/on_lobby_button_update_icon(atom/movable/screen/lobby/button/sign_up/lobby_button, updates)
+ if (LAZYFIND(lobby_candidates, lobby_button.get_mob()))
+ lobby_button.base_icon_state = "signup_on"
+ else
+ lobby_button.base_icon_state = "signup"
+
+/// Add an overlay based on whether you are actively signed up for this role
+/datum/station_trait/job/proc/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
+ SIGNAL_HANDLER
+ overlays += LAZYFIND(lobby_candidates, lobby_button.get_mob()) ? "tick" : "cross"
+*/
+/// Called before we start assigning roles, assign ours first
+/datum/station_trait/job/proc/pre_jobs_assigned()
+ SIGNAL_HANDLER
+ sign_up_button = FALSE
+ destroy_lobby_buttons()
+ for (var/mob/dead/new_player/signee as anything in lobby_candidates)
+ if (isnull(signee) || !signee.client || !signee.mind || signee.ready != PLAYER_READY_TO_PLAY)
+ LAZYREMOVE(lobby_candidates, signee)
+ if (!LAZYLEN(lobby_candidates))
+ on_failed_assignment()
+ return // Nobody signed up :(
+ for(var/_ in 1 to position_amount)
+ var/mob/dead/new_player/picked_player = pick_n_take(lobby_candidates)
+ picked_player.mind.assigned_role = new job_to_add()
+ lobby_candidates = null
+
+/// Called if we didn't assign a role before the round began, we add it to the latejoin menu instead
+/datum/station_trait/job/proc/on_failed_assignment()
+ var/datum/job/our_job = SSjob.GetJob(job_to_add::title)
+ our_job.total_positions = position_amount
+
+/datum/station_trait/job/can_display_lobby_button(client/player)
+ var/datum/job/our_job = SSjob.GetJob(job_to_add::title)
+ return our_job.player_old_enough(player) && ..()
+
+/// Adds a gorilla to the cargo department, replacing the sloth and the mech
+/datum/station_trait/job/cargorilla
+ name = "Cargo Gorilla"
+ button_desc = "Sign up to become the Cargo Gorilla, a peaceful shepherd of boxes."
+ weight = 1
+ show_in_report = FALSE // Selective attention test. Did you spot the gorilla?
+ can_roll_antag = CAN_ROLL_NEVER
+ job_to_add = /datum/job/cargo_gorilla
+
+/datum/station_trait/job/cargorilla/New()
+ . = ..()
+ RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(replace_cargo))
+
+/* SKYRAT EDIT REMOVAL
+/datum/station_trait/job/cargorilla/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
+ . = ..()
+ overlays += LAZYFIND(lobby_candidates, lobby_button.get_mob()) ? "gorilla_on" : "gorilla_off"
+*/
+/// Remove the cargo equipment and personnel that are being replaced by a gorilla.
+/datum/station_trait/job/cargorilla/proc/replace_cargo(datum/source)
+ SIGNAL_HANDLER
+ var/mob/living/basic/sloth/cargo_sloth = GLOB.cargo_sloth
+ if(isnull(cargo_sloth))
+ lobby_candidates = list()
+ destroy_lobby_buttons() // Sorry folks
+ sign_up_button = FALSE
+ return
+
+ // hmm our sloth looks funny today
+ qdel(cargo_sloth)
+ // monkey carries the crates, the age of robot is over
+ if(GLOB.cargo_ripley)
+ qdel(GLOB.cargo_ripley)
+
+/datum/station_trait/job/bridge_assistant
+ name = "Bridge Assistant"
+ button_desc = "Sign up to become the Bridge Assistant and watch over the Bridge."
+ weight = 2
+ report_message = "We have installed a Bridge Assistant on your station."
+ show_in_report = TRUE
+ can_roll_antag = CAN_ROLL_PROTECTED
+ job_to_add = /datum/job/bridge_assistant
+
+/datum/station_trait/job/bridge_assistant/New()
+ . = ..()
+ RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(add_coffeemaker))
+/* SKYRAT EDIT REMOVAL
+/datum/station_trait/job/bridge_assistant/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
+ . = ..()
+ overlays += "bridge_assistant"
+*/
+/// Creates a coffeemaker in the bridge, if we don't have one yet.
+/datum/station_trait/job/bridge_assistant/proc/add_coffeemaker(datum/source)
+ SIGNAL_HANDLER
+ var/area/bridge = GLOB.areas_by_type[/area/station/command/bridge]
+ if(isnull(bridge)) //no bridge, what will he assist?
+ return
+ var/list/possible_coffeemaker_positions = list(/area/station/command/bridge, /area/station/command/meeting_room)
+ var/list/coffeemakers = SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/coffeemaker)
+ for(var/obj/machinery/coffeemaker as anything in coffeemakers) //don't spawn a coffeemaker if there is already one on the bridge
+ if(is_type_in_list(get_area(coffeemaker), possible_coffeemaker_positions))
+ return
+ var/list/tables = list()
+ for(var/turf/area_turf as anything in bridge.get_contained_turfs())
+ var/obj/structure/table/table = locate() in area_turf
+ if(isnull(table))
+ continue
+ if(area_turf.is_blocked_turf(ignore_atoms = list(table))) //don't spawn a coffeemaker on a fax machine or smth
+ continue
+ tables += table
+ if(!length(tables))
+ return
+ var/picked_table = pick_n_take(tables)
+ var/picked_turf = get_turf(picked_table)
+ if(length(tables))
+ var/another_table = pick(tables)
+ for(var/obj/thing_on_table in picked_turf) //if there's paper bins or other shit on the table, get that off
+ if(thing_on_table == picked_table)
+ continue
+ if(HAS_TRAIT(thing_on_table, TRAIT_WALLMOUNTED) || (thing_on_table.flags_1 & ON_BORDER_1) || thing_on_table.layer < TABLE_LAYER)
+ continue
+ if(thing_on_table.invisibility || !thing_on_table.alpha || !thing_on_table.mouse_opacity)
+ continue
+ thing_on_table.forceMove(get_turf(another_table))
+ new /obj/machinery/coffeemaker/impressa(picked_turf)
+ new /obj/item/reagent_containers/cup/coffeepot(picked_turf)
+ new /obj/item/storage/box/coffeepack(picked_turf)
+
+#undef CAN_ROLL_ALWAYS
+#undef CAN_ROLL_PROTECTED
+#undef CAN_ROLL_NEVER
diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm
index 5c37fd7ede5..e623c9b1870 100644
--- a/code/datums/station_traits/negative_traits.dm
+++ b/code/datums/station_traits/negative_traits.dm
@@ -123,7 +123,7 @@
/datum/station_trait/overflow_job_bureaucracy/proc/set_overflow_job_override(datum/source)
SIGNAL_HANDLER
- var/datum/job/picked_job = pick(SSjob.joinable_occupations)
+ var/datum/job/picked_job = pick(SSjob.get_valid_overflow_jobs())
chosen_job_name = lowertext(picked_job.title) // like Chief Engineers vs like chief engineers
SSjob.set_overflow_role(picked_job.type)
@@ -254,7 +254,7 @@
name = "Random Event Modifier"
report_message = "A random event has been modified this shift! Someone forgot to set this!"
show_in_report = TRUE
- trait_flags = STATION_TRAIT_ABSTRACT
+ abstract_type = /datum/station_trait/random_event_weight_modifier
weight = 0
/// The path to the round_event_control that we modify.
@@ -277,7 +277,6 @@
name = "Ionic Stormfront"
report_message = "An ionic stormfront is passing over your station's system. Expect an increased likelihood of ion storms afflicting your station's silicon units."
trait_type = STATION_TRAIT_NEGATIVE
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 3
event_control_path = /datum/round_event_control/ion_storm
weight_multiplier = 2
@@ -286,7 +285,6 @@
name = "Radiation Stormfront"
report_message = "A radioactive stormfront is passing through your station's system. Expect an increased likelihood of radiation storms passing over your station, as well the potential for multiple radiation storms to occur during your shift."
trait_type = STATION_TRAIT_NEGATIVE
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 2
event_control_path = /datum/round_event_control/radiation_storm
weight_multiplier = 1.5
@@ -296,7 +294,6 @@
name = "Dust Stormfront"
report_message = "The space around your station is clouded by heavy pockets of space dust. Expect an increased likelyhood of space dust storms damaging the station hull."
trait_type = STATION_TRAIT_NEGATIVE
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 2
event_control_path = /datum/round_event_control/meteor_wave/dust_storm
weight_multiplier = 2
@@ -424,7 +421,7 @@
///Station traits that influence the space background and apply some unique effects!
/datum/station_trait/nebula
name = "Nebula"
- trait_flags = STATION_TRAIT_ABSTRACT
+ abstract_type = /datum/station_trait/nebula
weight = 0
show_in_report = TRUE
@@ -445,7 +442,7 @@
///Station nebula that incur some sort of effect if no shielding is created
/datum/station_trait/nebula/hostile
- trait_flags = STATION_TRAIT_ABSTRACT
+ abstract_type = /datum/station_trait/nebula/hostile
trait_processes = TRUE
///Intensity of the nebula
@@ -675,7 +672,7 @@
///Starts a storm on roundstart
/datum/station_trait/storm
- trait_flags = STATION_TRAIT_ABSTRACT
+ abstract_type = /datum/station_trait/storm
var/datum/weather/storm_type
/datum/station_trait/storm/on_round_start()
diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm
index f0b2c625537..5d49155a3c1 100644
--- a/code/datums/station_traits/neutral_traits.dm
+++ b/code/datums/station_traits/neutral_traits.dm
@@ -2,7 +2,7 @@
name = "Bananium Shipment"
trait_type = STATION_TRAIT_NEUTRAL
weight = 5
- report_message = "Rumors has it that the clown planet has been sending support packages to clowns in this system"
+ report_message = "Rumors has it that the clown planet has been sending support packages to clowns in this system."
trait_to_give = STATION_TRAIT_BANANIUM_SHIPMENTS
/datum/station_trait/unnatural_atmosphere
@@ -10,7 +10,7 @@
trait_type = STATION_TRAIT_NEUTRAL
weight = 5
show_in_report = TRUE
- report_message = "System's local planet has irregular atmospherical properties"
+ report_message = "System's local planet has irregular atmospherical properties."
trait_to_give = STATION_TRAIT_UNNATURAL_ATMOSPHERE
// This station trait modifies the atmosphere, which is too far past the time admins are able to revert it
@@ -141,60 +141,6 @@
var/new_colored_assistant_type = pick(subtypesof(/datum/colored_assistant) - get_configured_colored_assistant_type())
GLOB.colored_assistant = new new_colored_assistant_type
-/datum/station_trait/cargorilla
- name = "Cargo Gorilla"
- trait_type = STATION_TRAIT_NEUTRAL
- weight = 1
- show_in_report = FALSE // Selective attention test. Did you spot the gorilla?
-
- /// The gorilla we created, we only hold this ref until the round starts.
- var/mob/living/basic/gorilla/cargorilla/cargorilla
-
-/datum/station_trait/cargorilla/New()
- . = ..()
- RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(replace_cargo))
-
-/// Replace some cargo equipment and 'personnel' with a gorilla.
-/datum/station_trait/cargorilla/proc/replace_cargo(datum/source)
- SIGNAL_HANDLER
-
- var/mob/living/basic/sloth/cargo_sloth = GLOB.cargo_sloth
- if(isnull(cargo_sloth))
- return
-
- cargorilla = new(cargo_sloth.loc)
- cargorilla.name = cargo_sloth.name
- // We do a poll on roundstart, don't let ghosts in early
- INVOKE_ASYNC(src, PROC_REF(make_id_for_gorilla))
- // hm our sloth looks funny today
- qdel(cargo_sloth)
-
- // monkey carries the crates, the age of robot is over
- if(GLOB.cargo_ripley)
- qdel(GLOB.cargo_ripley)
-
-/// Makes an ID card for the gorilla
-/datum/station_trait/cargorilla/proc/make_id_for_gorilla()
- var/obj/item/card/id/advanced/cargo_gorilla/gorilla_id = new(cargorilla.loc)
- gorilla_id.registered_name = cargorilla.name
- gorilla_id.update_label()
-
- cargorilla.put_in_hands(gorilla_id, del_on_fail = TRUE)
-
-/datum/station_trait/cargorilla/on_round_start()
- if(!cargorilla)
- return
-
- addtimer(CALLBACK(src, PROC_REF(get_ghost_for_gorilla), cargorilla), 12 SECONDS) // give ghosts a bit of time to funnel in
- cargorilla = null
-
-/// Get us a ghost for the gorilla.
-/datum/station_trait/cargorilla/proc/get_ghost_for_gorilla(mob/living/basic/gorilla/cargorilla/gorilla)
- if(QDELETED(gorilla))
- return
-
- gorilla.poll_for_gorilla()
-
/datum/station_trait/birthday
name = "Employee Birthday"
trait_type = STATION_TRAIT_NEUTRAL
@@ -251,7 +197,7 @@
/datum/station_trait/birthday/proc/announce_birthday()
- report_message = "We here at Nanotrasen would all like to wish [birthday_person ? birthday_person_name : "Employee Name"] a very happy birthday"
+ report_message = "We here at Nanotrasen would all like to wish [birthday_person ? birthday_person_name : "Employee Name"] a very happy birthday."
priority_announce("Happy birthday to [birthday_person ? birthday_person_name : "Employee Name"]! Nanotrasen wishes you a very happy [birthday_person ? thtotext(birthday_person.age + 1) : "255th"] birthday.")
if(birthday_person)
playsound(birthday_person, 'sound/items/party_horn.ogg', 50)
diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm
index b8f349a5e8f..4ed552fa393 100644
--- a/code/datums/station_traits/positive_traits.dm
+++ b/code/datums/station_traits/positive_traits.dm
@@ -100,7 +100,7 @@
name = "deathrattled department"
trait_type = STATION_TRAIT_POSITIVE
show_in_report = TRUE
- trait_flags = STATION_TRAIT_ABSTRACT
+ abstract_type = /datum/station_trait/deathrattle_department
blacklist = list(/datum/station_trait/deathrattle_all)
var/department_to_apply_to
@@ -128,49 +128,42 @@
/datum/station_trait/deathrattle_department/service
name = "Deathrattled Service"
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 1
department_to_apply_to = DEPARTMENT_BITFLAG_SERVICE
department_name = "Service"
/datum/station_trait/deathrattle_department/cargo
name = "Deathrattled Cargo"
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 1
department_to_apply_to = DEPARTMENT_BITFLAG_CARGO
department_name = "Cargo"
/datum/station_trait/deathrattle_department/engineering
name = "Deathrattled Engineering"
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 1
department_to_apply_to = DEPARTMENT_BITFLAG_ENGINEERING
department_name = "Engineering"
/datum/station_trait/deathrattle_department/command
name = "Deathrattled Command"
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 1
department_to_apply_to = DEPARTMENT_BITFLAG_COMMAND
department_name = "Command"
/datum/station_trait/deathrattle_department/science
name = "Deathrattled Science"
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 1
department_to_apply_to = DEPARTMENT_BITFLAG_SCIENCE
department_name = "Science"
/datum/station_trait/deathrattle_department/security
name = "Deathrattled Security"
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 1
department_to_apply_to = DEPARTMENT_BITFLAG_SECURITY
department_name = "Security"
/datum/station_trait/deathrattle_department/medical
name = "Deathrattled Medical"
- trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
weight = 1
department_to_apply_to = DEPARTMENT_BITFLAG_MEDICAL
department_name = "Medical"
@@ -272,7 +265,7 @@
ai.eyeobj.relay_speech = TRUE //surveillance upgrade. the ai gets cybernetics too.
return
var/obj/item/organ/internal/cybernetic = new cybernetic_type()
- cybernetic.Insert(spawned, special = TRUE, drop_if_replaced = FALSE)
+ cybernetic.Insert(spawned, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/station_trait/luxury_escape_pods
name = "Luxury Escape Pods"
diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm
index 2865a230f43..18f99a91f89 100644
--- a/code/datums/status_effects/debuffs/debuffs.dm
+++ b/code/datums/status_effects/debuffs/debuffs.dm
@@ -741,6 +741,13 @@
alert_type = null
var/msg_stage = 0//so you dont get the most intense messages immediately
+/datum/status_effect/fake_virus/on_apply()
+ if(HAS_TRAIT(owner, TRAIT_VIRUSIMMUNE))
+ return FALSE
+ if(owner.stat != CONSCIOUS)
+ return FALSE
+ return TRUE
+
/datum/status_effect/fake_virus/tick(seconds_between_ticks)
var/fake_msg = ""
var/fake_emote = ""
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index 50c93e7db8f..84f30ab7855 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -522,7 +522,7 @@
return
if(prob(1))//low chance of the alternative reality returning to monkey
var/obj/item/organ/external/tail/monkey/monkey_tail = new ()
- monkey_tail.Insert(human_mob, drop_if_replaced = FALSE)
+ monkey_tail.Insert(human_mob, movement_flags = DELETE_IF_REPLACED)
var/datum/species/human_species = human_mob.dna?.species
if(human_species)
human_species.randomize_active_features(human_mob)
diff --git a/code/datums/wires/canister.dm b/code/datums/wires/canister.dm
new file mode 100644
index 00000000000..c692ff22872
--- /dev/null
+++ b/code/datums/wires/canister.dm
@@ -0,0 +1,33 @@
+/datum/wires/canister
+ holder_type = /obj/machinery/portable_atmospherics/canister
+ proper_name = "Canister"
+
+/datum/wires/canister/New(atom/holder)
+ wires = list(WIRE_VALVE, WIRE_SHIELDING, WIRE_REGULATOR_MIN, WIRE_REGULATOR_MAX, WIRE_TANK_EJECT, WIRE_REACTION_SUPPRESSION)
+ ..()
+
+/datum/wires/canister/on_pulse(wire)
+ var/obj/machinery/portable_atmospherics/canister/the_canister = holder
+ if(!the_canister.internal_cell)
+ return
+ switch(wire)
+ if(WIRE_VALVE)
+ the_canister.toggle_valve(usr, wire_pulsed = TRUE)
+ if(WIRE_SHIELDING)
+ the_canister.toggle_shielding(usr, wire_pulsed = TRUE)
+ if(WIRE_TANK_EJECT)
+ the_canister.eject_tank(usr, wire_pulsed = TRUE)
+ if(WIRE_REGULATOR_MIN)
+ the_canister.release_pressure = CAN_MIN_RELEASE_PRESSURE
+ the_canister.investigate_log("was set to [the_canister.release_pressure] kPa by [key_name(usr)] via wire pulse.", INVESTIGATE_ATMOS)
+ if(WIRE_REGULATOR_MAX)
+ the_canister.release_pressure = CAN_MAX_RELEASE_PRESSURE
+ the_canister.investigate_log("was set to [the_canister.release_pressure] kPa by [key_name(usr)] via wire pulse.", INVESTIGATE_ATMOS)
+ if(WIRE_REACTION_SUPPRESSION)
+ the_canister.toggle_reaction_suppression(usr, wire_pulsed = TRUE)
+
+/datum/wires/canister/can_reveal_wires(mob/user)
+ if(HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ return TRUE
+
+ return ..()
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index 92c2a6b8657..0c0b4ba36a1 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -133,6 +133,8 @@
/// How this atom should react to having its astar blocking checked
var/can_astar_pass = CANASTARPASS_DENSITY
+ ///whether ghosts can see screentips on it
+ var/ghost_screentips = FALSE
/**
* Top level of the destroy chain for most atoms
@@ -866,7 +868,7 @@
var/extra_lines = 0
var/extra_context = ""
- if (isliving(user) || isovermind(user) || isaicamera(user))
+ if(isliving(user) || isovermind(user) || isaicamera(user) || (ghost_screentips && isobserver(user)))
var/obj/item/held_item = user.get_active_held_item()
if (flags_1 & HAS_CONTEXTUAL_SCREENTIPS_1 || held_item?.item_flags & ITEM_HAS_CONTEXTUAL_SCREENTIPS)
diff --git a/code/game/gamemodes/dynamic/dynamic_simulations.dm b/code/game/gamemodes/dynamic/dynamic_simulations.dm
deleted file mode 100644
index 63da54becee..00000000000
--- a/code/game/gamemodes/dynamic/dynamic_simulations.dm
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifdef TESTING
-/datum/dynamic_simulation
- var/datum/game_mode/dynamic/gamemode
- var/datum/dynamic_simulation_config/config
- var/list/mock_candidates = list()
-
-/datum/dynamic_simulation/proc/initialize_gamemode(forced_threat)
- gamemode = new
-
- if (forced_threat)
- gamemode.create_threat(forced_threat)
- else
- gamemode.generate_threat()
-
- gamemode.generate_budgets()
- gamemode.set_cooldowns()
-
-/datum/dynamic_simulation/proc/create_candidates(players)
- GLOB.new_player_list.Cut()
-
- for (var/_ in 1 to players)
- var/mob/dead/new_player/mock_new_player = new
- mock_new_player.ready = PLAYER_READY_TO_PLAY
-
- var/datum/mind/mock_mind = new
- mock_new_player.mind = mock_mind
-
- var/datum/client_interface/mock_client = new
-
- var/datum/preferences/prefs = new
- var/list/be_special = list()
- for (var/special_role in GLOB.special_roles)
- be_special += special_role
-
- prefs.be_special = be_special
- mock_client.prefs = prefs
-
- mock_new_player.mock_client = mock_client
-
- mock_candidates += mock_new_player
-
-/datum/dynamic_simulation/proc/simulate(datum/dynamic_simulation_config/config)
- src.config = config
-
- initialize_gamemode(config.forced_threat_level)
- create_candidates(config.roundstart_players)
- gamemode.pre_setup()
-
- var/total_antags = 0
- for (var/_ruleset in gamemode.executed_rules)
- var/datum/dynamic_ruleset/ruleset = _ruleset
- total_antags += ruleset.assigned.len
-
- return list(
- "roundstart_players" = config.roundstart_players,
- "threat_level" = gamemode.threat_level,
- "snapshot" = list(
- "antag_percent" = total_antags / config.roundstart_players,
- "remaining_threat" = gamemode.mid_round_budget,
- "rulesets" = gamemode.executed_rules.Copy(),
- ),
- )
-
-/datum/dynamic_simulation_config
- /// How many players round start should there be?
- var/roundstart_players
-
- /// Optional, force this threat level instead of picking randomly through the lorentz distribution
- var/forced_threat_level
-
-/client/proc/run_dynamic_simulations()
- set name = "Run Dynamic Simulations"
- set category = "Debug"
-
- var/simulations = input(usr, "Enter number of simulations") as num
- var/roundstart_players = input(usr, "Enter number of round start players") as num
- var/forced_threat_level = input(usr, "Enter forced threat level, if you want one") as num | null
-
- SSticker.mode = new /datum/game_mode/dynamic
- message_admins("Running dynamic simulations...")
-
- var/list/outputs = list()
-
- var/datum/dynamic_simulation_config/dynamic_config = new
-
- if (roundstart_players)
- dynamic_config.roundstart_players = roundstart_players
-
- if (forced_threat_level)
- dynamic_config.forced_threat_level = forced_threat_level
-
- for (var/count in 1 to simulations)
- var/datum/dynamic_simulation/simulator = new
- var/output = simulator.simulate(dynamic_config)
- outputs += list(output)
-
- if (CHECK_TICK)
- log_world("[count]/[simulations]")
-
- message_admins("Writing file...")
- WRITE_FILE(file("[GLOB.log_directory]/dynamic_simulations.json"), json_encode(outputs))
- message_admins("Writing complete.")
-
-/proc/export_dynamic_json_of(ruleset_list)
- var/list/export = list()
-
- for (var/_ruleset in ruleset_list)
- var/datum/dynamic_ruleset/ruleset = _ruleset
- export[ruleset.name] = list(
- "repeatable_weight_decrease" = ruleset.repeatable_weight_decrease,
- "weight" = ruleset.weight,
- "cost" = ruleset.cost,
- "scaling_cost" = ruleset.scaling_cost,
- "antag_cap" = ruleset.antag_cap,
- "pop_per_requirement" = ruleset.pop_per_requirement,
- "requirements" = ruleset.requirements,
- "base_prob" = ruleset.base_prob,
- )
-
- return export
-
-#endif
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
deleted file mode 100644
index 39dcb81920a..00000000000
--- a/code/game/gamemodes/game_mode.dm
+++ /dev/null
@@ -1,228 +0,0 @@
-
-
-/*
- * GAMEMODES (by Rastaf0)
- *
- * In the new mode system all special roles are fully supported.
- * You can have proper wizards/traitors/changelings/cultists during any mode.
- * Only two things really depends on gamemode:
- * 1. Starting roles, equipment and preparations
- * 2. Conditions of finishing the round.
- *
- */
-
-
-/datum/game_mode
-
-///Attempts to select players for special roles the mode might have.
-/datum/game_mode/proc/pre_setup()
- return TRUE
-
-///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
-/datum/game_mode/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report.
- if(!report)
- report = !CONFIG_GET(flag/no_intercept_report)
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME)
-
- if(CONFIG_GET(flag/reopen_roundstart_suicide_roles))
- var/delay = CONFIG_GET(number/reopen_roundstart_suicide_roles_delay)
- if(delay)
- delay = (delay SECONDS)
- else
- delay = (4 MINUTES) //default to 4 minutes if the delay isn't defined.
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reopen_roundstart_suicide_roles)), delay)
-
- if(SSdbcore.Connect())
- var/list/to_set = list()
- var/arguments = list()
- if(SSticker.mode)
- to_set += "game_mode = :game_mode"
- arguments["game_mode"] = SSticker.mode
- if(GLOB.revdata.originmastercommit)
- to_set += "commit_hash = :commit_hash"
- arguments["commit_hash"] = GLOB.revdata.originmastercommit
- if(to_set.len)
- arguments["round_id"] = GLOB.round_id
- var/datum/db_query/query_round_game_mode = SSdbcore.NewQuery(
- "UPDATE [format_table_name("round")] SET [to_set.Join(", ")] WHERE id = :round_id",
- arguments
- )
- query_round_game_mode.Execute()
- qdel(query_round_game_mode)
- return TRUE
-
-
-///Handles late-join antag assignments
-/datum/game_mode/proc/make_antag_chance(mob/living/carbon/human/character)
- return
-
-/// Checks if the round should be ending, called every ticker tick
-/datum/game_mode/proc/check_finished()
- if(!SSticker.setup_done)
- return FALSE
- if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME))
- return TRUE
- if(GLOB.station_was_nuked)
- return TRUE
- if(GLOB.revolutionary_win)
- return TRUE
- return FALSE
-
-/*
- * Generate a list of station goals available to purchase to report to the crew.
- *
- * Returns a formatted string all station goals that are available to the station.
- */
-/datum/game_mode/proc/generate_station_goal_report()
- if(!GLOB.station_goals.len)
- return
- . = "Special Orders for [station_name()]: "
- var/list/goal_reports = list()
- for(var/datum/station_goal/station_goal as anything in GLOB.station_goals)
- station_goal.on_report()
- goal_reports += station_goal.get_report()
-
- . += goal_reports.Join("")
- return
-
-/*
- * Generate a list of active station traits to report to the crew.
- *
- * Returns a formatted string of all station traits (that are shown) affecting the station.
- */
-/datum/game_mode/proc/generate_station_trait_report()
- var/trait_list_string = ""
- for(var/datum/station_trait/station_trait as anything in SSstation.station_traits)
- if(!station_trait.show_in_report)
- continue
- trait_list_string += "[station_trait.get_report()] "
- if(trait_list_string != "")
- return "Identified shift divergencies: " + trait_list_string
- return
-
-/datum/game_mode/proc/generate_report_footnote()
- var/footnote_pile = ""
-
- for(var/datum/command_footnote/footnote in SScommunications.command_report_footnotes)
- footnote_pile += "[footnote.message] "
- footnote_pile += "[footnote.signature] "
- footnote_pile += " "
-
- return "Additional Notes:
\
- Our medical staff have detected a series of anomalies in the vital sensors \
- of some of the staff aboard your station.
\
- Further investigation into the situation on our end resulted in us discovering \
- a series of rather... unforturnate decisions that were made on the part of said staff.
\
- As such, we have taken the liberty to automatically reopen employment opportunities for the positions of the crew members \
- who have decided not to partake in our research. We will be forwarding their cases to our employment review board \
- to determine their eligibility for continued service with the company (and of course the \
- continued storage of cloning records within the central medical backup server.)
\
- The following positions have been reopened on our behalf:
\
- [reopened_job_report_positions]"
-
- print_command_report(suicide_command_report, "Central Command Personnel Update")
-
-//////////////////////////
-//Reports player logouts//
-//////////////////////////
-/proc/display_roundstart_logout_report()
- var/list/msg = list("[span_boldnotice("Roundstart logout report")]\n\n")
- for(var/i in GLOB.mob_living_list)
- var/mob/living/L = i
- var/mob/living/carbon/C = L
- if (istype(C) && !C.last_mind)
- continue // never had a client
-
- if(L.ckey && !GLOB.directory[L.ckey])
- msg += "[L.name] ([L.key]), the [L.job] (Disconnected)\n"
-
-
- if(L.ckey && L.client)
- var/failed = FALSE
- if(L.client.inactivity >= ROUNDSTART_LOGOUT_AFK_THRESHOLD) //Connected, but inactive (alt+tabbed or something)
- msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n"
- failed = TRUE //AFK client
- if(!failed && L.stat)
- if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
- msg += "[L.name] ([L.key]), the [L.job] ([span_boldannounce("Suicide")])\n"
- failed = TRUE //Disconnected client
- if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT))
- msg += "[L.name] ([L.key]), the [L.job] (Dying)\n"
- failed = TRUE //Unconscious
- if(!failed && L.stat == DEAD)
- msg += "[L.name] ([L.key]), the [L.job] (Dead)\n"
- failed = TRUE //Dead
-
- continue //Happy connected client
- for(var/mob/dead/observer/D in GLOB.dead_mob_list)
- if(D.mind && D.mind.current == L)
- if(L.stat == DEAD)
- if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Suicide")])\n"
- continue //Disconnected client
- else
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
- continue //Dead mob, ghost abandoned
- else
- if(D.can_reenter_corpse)
- continue //Adminghost, or cult/wizard ghost
- else
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Ghosted")])\n"
- continue //Ghosted while alive
-
- var/concatenated_message = msg.Join()
- log_admin(concatenated_message)
- to_chat(GLOB.admins, concatenated_message)
-
-/datum/game_mode/proc/generate_station_goals(greenshift)
- var/goal_budget = greenshift ? INFINITY : CONFIG_GET(number/station_goal_budget)
- var/list/possible = subtypesof(/datum/station_goal)
- // Remove all goals that require space if space is not present
- if(SSmapping.is_planetary())
- for(var/datum/station_goal/goal as anything in possible)
- if(initial(goal.requires_space))
- possible -= goal
- var/goal_weights = 0
- while(possible.len && goal_weights < goal_budget)
- var/datum/station_goal/picked = pick_n_take(possible)
- goal_weights += initial(picked.weight)
- GLOB.station_goals += new picked
-
-//Set result and news report here
-/datum/game_mode/proc/set_round_result()
- SSticker.mode_result = "undefined"
- // Something nuked the station - it wasn't nuke ops (they set their own via their rulset)
- if(GLOB.station_was_nuked)
- SSticker.news_report = STATION_NUKED
- if(SSsupermatter_cascade.cascade_initiated)
- SSticker.news_report = SUPERMATTER_CASCADE
- // Only show this one if we have nothing better to show
- if(EMERGENCY_ESCAPED_OR_ENDGAMED && !SSticker.news_report)
- SSticker.news_report = SSshuttle.emergency?.is_hijacked() ? SHUTTLE_HIJACK : STATION_EVACUATED
-
-/// Mode specific admin panel.
-/datum/game_mode/proc/admin_panel()
- return
diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm
index 85f0eda1bcf..40670a22f44 100644
--- a/code/game/machinery/bank_machine.dm
+++ b/code/game/machinery/bank_machine.dm
@@ -103,7 +103,6 @@
if(.)
return
- // BUBBER EDIT BEGIN: NO MORE SIPHONING
switch(action)
if("siphon")
if(is_station_level(src.z) || is_centcom_level(src.z))
@@ -112,11 +111,17 @@
else
say("Error: Console not in reach of station, withdrawal cannot begin.")
. = TRUE
- // BUBBER EDIT END
if("halt")
say("Station credit withdrawal halted.")
end_siphon()
. = TRUE
+
+/obj/machinery/computer/bank_machine/on_changed_z_level()
+ . = ..()
+ if(siphoning && !(is_station_level(src.z) || is_centcom_level(src.z)))
+ say("Error: Console not in reach of station. Siphon halted.")
+ end_siphon()
+
/obj/machinery/computer/bank_machine/proc/end_siphon()
siphoning = FALSE
unauthorized = FALSE
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 860c708ff59..f3008ca4ebc 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -6,7 +6,6 @@
use_power = ACTIVE_POWER_USE
active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.02
layer = WALL_OBJ_LAYER
- plane = GAME_PLANE_UPPER
resistance_flags = FIRE_PROOF
damage_deflection = 12
armor_type = /datum/armor/machinery_camera
@@ -342,7 +341,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
return
// OTHER
- if(istype(attacking_item, /obj/item/modular_computer/pda))
+ if(istype(attacking_item, /obj/item/modular_computer))
var/itemname = ""
var/info = ""
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index f32eff91c4e..c8504a919f1 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -931,7 +931,6 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
hacker.log_message("hacked a communications console, resulting in: [picked_option].", LOG_GAME, log_globally = TRUE)
switch(picked_option)
if(HACK_PIRATE) // Triggers pirates, which the crew may be able to pay off to prevent
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
var/list/pirate_rulesets = list(
/datum/dynamic_ruleset/midround/pirates,
/datum/dynamic_ruleset/midround/dangerous_pirates,
@@ -940,7 +939,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
"Attention crew: sector monitoring reports a massive jump-trace from an enemy vessel destined for your system. Prepare for imminent hostile contact.",
"[command_name()] High-Priority Update",
)
- dynamic.picking_specific_rule(pick(pirate_rulesets), forced = TRUE, ignore_cost = TRUE)
+ SSdynamic.picking_specific_rule(pick(pirate_rulesets), forced = TRUE, ignore_cost = TRUE)
if(HACK_FUGITIVES) // Triggers fugitives, which can cause confusion / chaos as the crew decides which side help
priority_announce(
@@ -961,22 +960,20 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
continue
shake_camera(crew_member, 15, 1)
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
- dynamic.unfavorable_situation()
+ SSdynamic.unfavorable_situation()
if(HACK_SLEEPER) // Trigger one or multiple sleeper agents with the crew (or for latejoining crew)
var/datum/dynamic_ruleset/midround/sleeper_agent_type = /datum/dynamic_ruleset/midround/from_living/autotraitor
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
var/max_number_of_sleepers = clamp(round(length(GLOB.alive_player_list) / 20), 1, 3)
var/num_agents_created = 0
for(var/num_agents in 1 to rand(1, max_number_of_sleepers))
- if(!dynamic.picking_specific_rule(sleeper_agent_type, forced = TRUE, ignore_cost = TRUE))
+ if(!SSdynamic.picking_specific_rule(sleeper_agent_type, forced = TRUE, ignore_cost = TRUE))
break
num_agents_created++
if(num_agents_created <= 0)
// We failed to run any midround sleeper agents, so let's be patient and run latejoin traitor
- dynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator, forced = TRUE, ignore_cost = TRUE)
+ SSdynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator, forced = TRUE, ignore_cost = TRUE)
else
// We spawned some sleeper agents, nice - give them a report to kickstart the paranoia
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index d542555af84..de60d84dafd 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -41,7 +41,7 @@
req_access = null
anchored = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
/datum/track
var/song_name = "generic"
diff --git a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
index cbe6e1c31c7..afbb8404060 100644
--- a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
@@ -48,7 +48,7 @@
AddElement(/datum/element/noticable_organ, "teeth are big and sharp.", BODY_ZONE_PRECISE_MOUTH)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp)
-/obj/item/organ/internal/tongue/carp/on_insert(mob/living/carbon/tongue_owner)
+/obj/item/organ/internal/tongue/carp/on_mob_insert(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
if(!ishuman(tongue_owner))
return
@@ -57,12 +57,14 @@
return
var/datum/species/rec_species = human_receiver.dna.species
rec_species.update_no_equip_flags(tongue_owner, rec_species.no_equip_flags | ITEM_SLOT_MASK)
- var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD)
- head.unarmed_damage_low = 10
- head.unarmed_damage_high = 15
- head.unarmed_effectiveness = 15
-/obj/item/organ/internal/tongue/carp/on_remove(mob/living/carbon/tongue_owner)
+/obj/item/organ/internal/tongue/carp/on_bodypart_insert(obj/item/bodypart/limb)
+ . = ..()
+ limb.unarmed_damage_low = 10
+ limb.unarmed_damage_high = 15
+ limb.unarmed_effectiveness = 15
+
+/obj/item/organ/internal/tongue/carp/on_mob_remove(mob/living/carbon/tongue_owner)
. = ..()
if(!ishuman(tongue_owner))
return
@@ -71,7 +73,10 @@
return
var/datum/species/rec_species = human_receiver.dna.species
rec_species.update_no_equip_flags(tongue_owner, initial(rec_species.no_equip_flags))
- var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD)
+
+/obj/item/organ/internal/tongue/carp/on_bodypart_remove(obj/item/bodypart/head)
+ . = ..()
+
head.unarmed_damage_low = initial(head.unarmed_damage_low)
head.unarmed_damage_high = initial(head.unarmed_damage_high)
head.unarmed_effectiveness = initial(head.unarmed_effectiveness)
@@ -110,13 +115,13 @@
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp)
AddElement(/datum/element/noticable_organ, "seem%PRONOUN_S unable to stay still.")
-/obj/item/organ/internal/brain/carp/on_insert(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/carp/on_mob_insert(mob/living/carbon/brain_owner)
. = ..()
cooldown_timer = addtimer(CALLBACK(src, PROC_REF(unsatisfied_nomad)), cooldown_time, TIMER_STOPPABLE|TIMER_OVERRIDE|TIMER_UNIQUE)
RegisterSignal(brain_owner, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(satisfied_nomad))
//technically you could get around the mood issue by extracting and reimplanting the brain but it will be far easier to just go one z there and back
-/obj/item/organ/internal/brain/carp/on_remove(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/carp/on_mob_remove(mob/living/carbon/brain_owner)
. = ..()
UnregisterSignal(brain_owner, COMSIG_MOVABLE_Z_CHANGED)
deltimer(cooldown_timer)
diff --git a/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm
index 57a26d36842..a9d2d956f7f 100644
--- a/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm
@@ -8,7 +8,7 @@
//SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION
/*
-/obj/item/organ/internal/ears/fox/on_insert(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/fox/on_mob_insert(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
@@ -16,7 +16,7 @@
ear_owner.dna.update_uf_block(DNA_EARS_BLOCK)
ear_owner.update_body()
-/obj/item/organ/internal/ears/fox/on_remove(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/fox/on_mob_remove(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
diff --git a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
index 6cd753eb760..477b461bdf3 100644
--- a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
@@ -66,7 +66,7 @@
AddElement(/datum/element/noticable_organ, "arm is just a mass of plate and tendrils.", BODY_ZONE_CHEST)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/goliath)
-/obj/item/organ/internal/brain/goliath/on_insert(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/goliath/on_mob_insert(mob/living/carbon/brain_owner)
. = ..()
if(!ishuman(brain_owner))
return
@@ -78,7 +78,7 @@
hammer = new/obj/item/goliath_infuser_hammer
brain_owner.put_in_hands(hammer)
-/obj/item/organ/internal/brain/goliath/on_remove(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/goliath/on_mob_remove(mob/living/carbon/brain_owner)
. = ..()
UnregisterSignal(brain_owner)
if(!ishuman(brain_owner))
diff --git a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
index 515c56622c4..2a5c776709c 100644
--- a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
@@ -33,7 +33,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola)
AddElement(/datum/element/noticable_organ, "radiate%PRONOUN_S an aura of serenity.")
-/obj/item/organ/internal/heart/gondola/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/internal/heart/gondola/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(!(FACTION_HOSTILE in receiver.faction))
factions_to_remove += FACTION_HOSTILE
@@ -41,7 +41,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
factions_to_remove += FACTION_MINING
receiver.faction |= list(FACTION_HOSTILE, FACTION_MINING)
-/obj/item/organ/internal/heart/gondola/Remove(mob/living/carbon/heartless, special)
+/obj/item/organ/internal/heart/gondola/Remove(mob/living/carbon/heartless, special, movement_flags)
. = ..()
for(var/faction in factions_to_remove)
heartless.faction -= faction
@@ -63,11 +63,11 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
AddElement(/datum/element/noticable_organ, "mouth is permanently affixed into a relaxed smile.", BODY_ZONE_PRECISE_MOUTH)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola)
-/obj/item/organ/internal/tongue/gondola/Insert(mob/living/carbon/tongue_owner, special, drop_if_replaced)
+/obj/item/organ/internal/tongue/gondola/Insert(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
tongue_owner.add_mood_event("gondola_zen", /datum/mood_event/gondola_serenity)
-/obj/item/organ/internal/tongue/gondola/Remove(mob/living/carbon/tongue_owner, special)
+/obj/item/organ/internal/tongue/gondola/Remove(mob/living/carbon/tongue_owner, special, movement_flags)
tongue_owner.clear_mood_event("gondola_zen")
return ..()
@@ -89,7 +89,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
AddElement(/datum/element/noticable_organ, "right arm has small needles breaching the skin all over it.", BODY_ZONE_R_ARM)
pax_hugs = new
-/obj/item/organ/internal/liver/gondola/Insert(mob/living/carbon/liver_owner, special, drop_if_replaced)
+/obj/item/organ/internal/liver/gondola/Insert(mob/living/carbon/liver_owner, special, movement_flags)
. = ..()
var/has_left = liver_owner.has_left_hand(check_disabled = FALSE)
var/has_right = liver_owner.has_right_hand(check_disabled = FALSE)
@@ -104,7 +104,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
RegisterSignal(liver_owner, COMSIG_HUMAN_EQUIPPING_ITEM, PROC_REF(on_owner_equipping_item))
RegisterSignal(liver_owner, COMSIG_LIVING_TRY_PULL, PROC_REF(on_owner_try_pull))
-/obj/item/organ/internal/liver/gondola/Remove(mob/living/carbon/liver_owner, special)
+/obj/item/organ/internal/liver/gondola/Remove(mob/living/carbon/liver_owner, special, movement_flags)
. = ..()
pax_hugs.remove(liver_owner)
UnregisterSignal(liver_owner, list(COMSIG_HUMAN_EQUIPPING_ITEM, COMSIG_LIVING_TRY_PULL))
diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
index 96c33871a40..092a4c7889a 100644
--- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
@@ -63,7 +63,7 @@
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/rat)
AddElement(/datum/element/noticable_organ, "hunch%PRONOUN_ES over unnaturally!")
-/obj/item/organ/internal/heart/rat/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/internal/heart/rat/on_mob_insert(mob/living/carbon/receiver)
. = ..()
if(!. || !ishuman(receiver))
return
@@ -75,7 +75,7 @@
if(human_receiver.physiology)
human_receiver.physiology.damage_resistance -= 50
-/obj/item/organ/internal/heart/rat/on_remove(mob/living/carbon/heartless, special)
+/obj/item/organ/internal/heart/rat/on_mob_remove(mob/living/carbon/heartless, special)
. = ..()
if(!ishuman(heartless))
return
@@ -113,11 +113,11 @@
if(message == "hi?")
speech_args[SPEECH_MESSAGE] = "Um... cheesed to meet you?"
-/obj/item/organ/internal/tongue/rat/on_insert(mob/living/carbon/tongue_owner, special, drop_if_replaced)
+/obj/item/organ/internal/tongue/rat/on_mob_insert(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
RegisterSignal(tongue_owner, COMSIG_CARBON_ITEM_GIVEN, PROC_REF(its_on_the_mouse))
-/obj/item/organ/internal/tongue/rat/on_remove(mob/living/carbon/tongue_owner)
+/obj/item/organ/internal/tongue/rat/on_mob_remove(mob/living/carbon/tongue_owner)
. = ..()
UnregisterSignal(tongue_owner, COMSIG_CARBON_ITEM_GIVEN)
diff --git a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm
index f10c9b039f2..0644bca0354 100644
--- a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm
@@ -71,7 +71,7 @@
QDEL_NULL(roach_shell)
return ..()
-/obj/item/organ/internal/heart/roach/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/heart/roach/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner))
return
@@ -82,11 +82,11 @@
RegisterSignal(human_owner, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(do_block_effect))
human_owner.physiology.knockdown_mod *= 3
- var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST)
- chest.add_bodypart_overlay(roach_shell)
- human_owner.update_body_parts()
+/obj/item/organ/internal/heart/roach/on_bodypart_insert(obj/item/bodypart/limb)
+ . = ..()
+ limb.add_bodypart_overlay(roach_shell)
-/obj/item/organ/internal/heart/roach/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/heart/roach/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner) || QDELETED(organ_owner))
return
@@ -96,9 +96,10 @@
UnregisterSignal(human_owner, list(COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, COMSIG_MOB_AFTER_APPLY_DAMAGE))
human_owner.physiology.knockdown_mod /= 3
- var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST)
- chest.remove_bodypart_overlay(roach_shell)
- human_owner.update_body_parts()
+/obj/item/organ/internal/heart/roach/on_bodypart_remove(obj/item/bodypart/limb)
+ . = ..()
+
+ limb.remove_bodypart_overlay(roach_shell)
/**
* Signal proc for [COMSIG_MOB_APPLY_DAMAGE_MODIFIERS]
@@ -193,7 +194,7 @@
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach)
-/obj/item/organ/internal/liver/roach/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/roach/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner))
return
@@ -201,13 +202,13 @@
var/mob/living/carbon/human/human_owner = owner
human_owner.physiology.tox_mod *= 2
-/obj/item/organ/internal/liver/roach/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/roach/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner) || QDELETED(organ_owner))
return
var/mob/living/carbon/human/human_owner = organ_owner
- human_owner.physiology.tox_mod /= 2
+ human_owner.physiology.tox_mod *= 0.5
/// Roach appendix:
/// No appendicitus! weee!
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index c01fb139c35..bc0d480af64 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -123,7 +123,7 @@ Possible to do for anyone motivated enough:
/obj/machinery/holopad/tutorial
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
on_network = FALSE
///Proximity monitor associated with this atom, needed for proximity checks.
var/datum/proximity_monitor/proximity_monitor
diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm
index caedf9fffeb..38d88b013d1 100644
--- a/code/game/machinery/limbgrower.dm
+++ b/code/game/machinery/limbgrower.dm
@@ -283,7 +283,7 @@
/obj/machinery/limbgrower/fullupgrade //Inherently cheaper organ production. This is to NEVER be inherently emagged, no valids.
desc = "It grows new limbs using Synthflesh. This alien model seems more efficient."
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
circuit = /obj/item/circuitboard/machine/limbgrower/fullupgrade
/obj/machinery/limbgrower/fullupgrade/Initialize(mapload)
diff --git a/code/game/machinery/medipen_refiller.dm b/code/game/machinery/medipen_refiller.dm
index b56e35b2389..5563000183c 100644
--- a/code/game/machinery/medipen_refiller.dm
+++ b/code/game/machinery/medipen_refiller.dm
@@ -76,7 +76,7 @@
return
add_overlay("active")
if(do_after(user, 2 SECONDS, src))
- medipen.reagents.maximum_volume = initial(medipen.reagents.maximum_volume)
+ medipen.used_up = FALSE
medipen.add_initial_reagents()
reagents.remove_reagent(allowed_pens[medipen.type], 10)
balloon_alert(user, "refilled")
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index 11f4bb1dd55..5a7e1da9984 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -556,7 +556,7 @@ DEFINE_BITFIELD(turret_flags, list(
// If we aren't shooting heads then return a threatcount of 0
if (!(turret_flags & TURRET_FLAG_SHOOT_HEADS))
var/datum/job/apparent_job = SSjob.GetJob(perp.get_assignment())
- if(apparent_job?.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(apparent_job?.job_flags & JOB_HEAD_OF_STAFF)
return 0
if(turret_flags & TURRET_FLAG_AUTH_WEAPONS) //check for weapon authorization
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index a17d9b845b0..50751d95b5f 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -151,6 +151,10 @@
var/obj/item/mmi/as_mmi = thing
if(istype(thing, /obj/item/organ/internal/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(thing, /obj/item/dullahan_relay))
living_detected = TRUE
+ if(isitem(as_object))
+ var/obj/item/as_item = as_object
+ if(as_item.item_flags & ABSTRACT) //also catches organs and bodyparts *stares*
+ continue
nom += thing
else if(isliving(thing))
living_detected = TRUE
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
index 91ce119d3df..d1206cfc1c5 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -98,7 +98,6 @@
max_integrity = 20
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
/obj/structure/emergency_shield/cult/barrier
density = FALSE //toggled on right away by the parent rune
diff --git a/code/game/machinery/telecomms/machines/allinone.dm b/code/game/machinery/telecomms/machines/allinone.dm
index dd1e4a3f610..931979f6b0a 100644
--- a/code/game/machinery/telecomms/machines/allinone.dm
+++ b/code/game/machinery/telecomms/machines/allinone.dm
@@ -20,7 +20,7 @@
/obj/machinery/telecomms/allinone/indestructible
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
/obj/machinery/telecomms/allinone/receive_signal(datum/signal/subspace/signal)
if(!istype(signal) || signal.transmission_method != TRANSMISSION_SUBSPACE) // receives on subspace only
diff --git a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
index a219b2f5492..3e6d84d809a 100644
--- a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
+++ b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
@@ -180,7 +180,7 @@
candidate_list += GLOB.current_observers_list
candidate_list += GLOB.dead_player_list
- var/list/candidates = poll_candidates("Would you like to participate in a spooky ghost swarm? (Warning: you will not be able to return to your body!)", ROLE_SENTIENCE, FALSE, 10 SECONDS, group = candidate_list)
+ var/list/candidates = SSpolling.poll_candidates("Would you like to participate in a spooky ghost swarm? (Warning: you will not be able to return to your body!)", check_jobban = ROLE_SENTIENCE, poll_time = 10 SECONDS, group = candidate_list, pic_source = src, role_name_text = "ghost swarm")
for(var/mob/dead/observer/candidate_ghost as anything in candidates)
var/mob/living/basic/ghost/swarm/new_ghost = new(get_turf(src))
ghosts_spawned += new_ghost
diff --git a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
index d3ee84181ed..87221eeec9f 100644
--- a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
+++ b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
@@ -39,7 +39,7 @@
var/datum/action/innate/slime/reproduce/repro_action = new
repro_action.Grant(pyro)
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENCE, null, 10 SECONDS, pyro, POLL_IGNORE_PYROSLIME)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a pyroclastic anomaly slime?", check_jobban = ROLE_SENTIENCE, poll_time = 10 SECONDS, target_mob = pyro, ignore_category = POLL_IGNORE_PYROSLIME, pic_source = pyro, role_name_text = "pyroclastic anomaly slime")
if(!LAZYLEN(candidates))
return
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index daaddb00ce0..8a80692f1a9 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -129,7 +129,6 @@
desc = "Somebody should remove that."
gender = NEUTER
layer = WALL_OBJ_LAYER
- plane = GAME_PLANE_UPPER
icon_state = "cobweb1"
resistance_flags = FLAMMABLE
beauty = -100
diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
index 9a13e68c1b1..efb5b1fbb91 100644
--- a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
@@ -17,7 +17,6 @@
anchored = TRUE
density = FALSE
layer = EDGED_TURF_LAYER
- plane = GAME_PLANE_UPPER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
animate_movement = NO_STEPS
/// The types of turfs that this foam cannot spread to.
@@ -298,7 +297,6 @@
opacity = TRUE // changed in New()
anchored = TRUE
layer = EDGED_TURF_LAYER
- plane = GAME_PLANE_UPPER
resistance_flags = FIRE_PROOF | ACID_PROOF
name = "foamed metal"
desc = "A lightweight foamed metal wall that can be used as base to construct a wall."
diff --git a/code/game/objects/effects/spiderwebs.dm b/code/game/objects/effects/spiderwebs.dm
index 9a3d6c9c8e3..5023f9bd825 100644
--- a/code/game/objects/effects/spiderwebs.dm
+++ b/code/game/objects/effects/spiderwebs.dm
@@ -114,7 +114,7 @@
opacity = TRUE
density = TRUE
max_integrity = 90
- plane = GAME_PLANE_UPPER
+ layer = ABOVE_MOB_LAYER
resistance_flags = FIRE_PROOF | FREEZE_PROOF
/obj/structure/spider/solid/Initialize(mapload)
@@ -130,7 +130,7 @@
opacity = TRUE
max_integrity = 60
alpha = 200
- plane = GAME_PLANE_UPPER
+ layer = ABOVE_MOB_LAYER
resistance_flags = FIRE_PROOF | FREEZE_PROOF
/obj/structure/spider/passage/Initialize(mapload)
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 5b9f5a5a06b..ece26c9de56 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -21,7 +21,6 @@
if(SOUTH)
target_pixel_y = -16
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
if(EAST)
target_pixel_x = 16
if(WEST)
@@ -36,12 +35,10 @@
target_pixel_x = 16
target_pixel_y = -16
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
if(SOUTHWEST)
target_pixel_x = -16
target_pixel_y = -16
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration)
/obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter
diff --git a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm
index 1e8577bbd3a..81c854f2c3e 100644
--- a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm
+++ b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm
@@ -3,7 +3,6 @@
icon = 'icons/obj/weapons/guns/projectiles.dmi'
icon_state = "nothing"
layer = HITSCAN_PROJECTILE_LAYER
- plane = GAME_PLANE_FOV_HIDDEN
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
appearance_flags = LONG_GLIDE
diff --git a/code/game/objects/effects/temporary_visuals/temporary_visual.dm b/code/game/objects/effects/temporary_visuals/temporary_visual.dm
index 0d5b7bf1463..9669b4ed290 100644
--- a/code/game/objects/effects/temporary_visuals/temporary_visual.dm
+++ b/code/game/objects/effects/temporary_visuals/temporary_visual.dm
@@ -3,7 +3,6 @@
icon_state = "nothing"
anchored = TRUE
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
///time, in deciseconds, that this object will exist
var/duration = 10
diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm
index a7a17de8814..c5bd01c196d 100644
--- a/code/game/objects/items/airlock_painter.dm
+++ b/code/game/objects/items/airlock_painter.dm
@@ -327,7 +327,6 @@
/datum/asset/spritesheet/decals
name = "floor_decals"
- cross_round_cachable = TRUE
/// The floor icon used for blend_preview_floor()
var/preview_floor_icon = 'icons/turf/floors.dmi'
diff --git a/code/game/objects/items/body_egg.dm b/code/game/objects/items/body_egg.dm
index f95b9f2e164..d244d8c55cc 100644
--- a/code/game/objects/items/body_egg.dm
+++ b/code/game/objects/items/body_egg.dm
@@ -15,7 +15,7 @@
if(iscarbon(loc))
Insert(loc)
-/obj/item/organ/internal/body_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/body_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
if(!.)
return
@@ -23,7 +23,7 @@
egg_owner.med_hud_set_status()
INVOKE_ASYNC(src, PROC_REF(AddInfectionImages), egg_owner)
-/obj/item/organ/internal/body_egg/Remove(mob/living/carbon/egg_owner, special = FALSE)
+/obj/item/organ/internal/body_egg/Remove(mob/living/carbon/egg_owner, special, movement_flags)
. = ..()
egg_owner.remove_traits(list(TRAIT_XENO_HOST, TRAIT_XENO_IMMUNE), ORGAN_TRAIT)
egg_owner.med_hud_set_status()
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 195895c44c3..b1780d24513 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -1005,7 +1005,7 @@
if(istype(old_loc, /obj/item/storage/wallet))
UnregisterSignal(old_loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
- if(istype(old_loc, /obj/item/modular_computer/pda))
+ if(istype(old_loc, /obj/item/modular_computer))
UnregisterSignal(old_loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
//New loc
@@ -1013,7 +1013,7 @@
RegisterSignal(loc, COMSIG_ITEM_EQUIPPED, PROC_REF(update_intern_status))
RegisterSignal(loc, COMSIG_ITEM_DROPPED, PROC_REF(remove_intern_status))
- if(istype(loc, /obj/item/modular_computer/pda))
+ if(istype(loc, /obj/item/modular_computer))
RegisterSignal(loc, COMSIG_ITEM_EQUIPPED, PROC_REF(update_intern_status))
RegisterSignal(loc, COMSIG_ITEM_DROPPED, PROC_REF(remove_intern_status))
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index 789f6250441..4e63fc00c43 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -425,7 +425,7 @@
var/mob/living/carbon/human/human_servant = new(drop_location())
do_smoke(0, holder = src, location = drop_location())
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [user.real_name]'s Servant?", ROLE_WIZARD, ROLE_WIZARD, 5 SECONDS, human_servant)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [user.real_name]'s Servant?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 5 SECONDS, target_mob = human_servant, pic_source = user, role_name_text = "dice servant")
if(LAZYLEN(candidates))
var/mob/dead/observer/candidate = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(candidate)] was spawned as Dice Servant")
diff --git a/code/game/objects/items/inducer.dm b/code/game/objects/items/inducer.dm
index f6ed89a870f..35c73a8cb95 100644
--- a/code/game/objects/items/inducer.dm
+++ b/code/game/objects/items/inducer.dm
@@ -172,12 +172,19 @@
return
. += "inducer-[cell ? "bat" : "nobat"]"
+/obj/item/inducer/empty
+ cell_type = null
+ opened = TRUE
+
+/obj/item/inducer/orderable
+ cell_type = /obj/item/stock_parts/cell/inducer_supply
+ opened = FALSE
+
/obj/item/inducer/sci
icon_state = "inducer-sci"
inhand_icon_state = "inducer-sci"
desc = "A tool for inductively charging internal power cells. This one has a science color scheme, and is less potent than its engineering counterpart."
cell_type = null
- powertransfer = 500
opened = TRUE
/obj/item/inducer/sci/Initialize(mapload)
diff --git a/code/game/objects/items/kirby_plants/kirbyplants.dm b/code/game/objects/items/kirby_plants/kirbyplants.dm
index 74a0c8637e8..75363eedaf4 100644
--- a/code/game/objects/items/kirby_plants/kirbyplants.dm
+++ b/code/game/objects/items/kirby_plants/kirbyplants.dm
@@ -7,7 +7,6 @@
base_icon_state = "plant-01"
desc = "A little bit of nature contained in a pot."
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
w_class = WEIGHT_CLASS_HUGE
force = 10
throwforce = 13
diff --git a/code/game/objects/items/robot/items/generic.dm b/code/game/objects/items/robot/items/generic.dm
index 081a3e0abbc..7ebd87408f6 100644
--- a/code/game/objects/items/robot/items/generic.dm
+++ b/code/game/objects/items/robot/items/generic.dm
@@ -43,11 +43,15 @@
attacked_mob.set_jitter_if_lower(5 SECONDS)
if(issilicon(attacked_mob))
attacked_mob.emp_act(EMP_HEAVY)
- attacked_mob.visible_message(span_danger("[user] shocks [attacked_mob] with [src]!"), \
- span_userdanger("[user] shocks you with [src]!"))
+ attacked_mob.visible_message(
+ span_danger("[user] shocks [attacked_mob] with [src]!"),
+ span_userdanger("[user] shocks you with [src]!"),
+ )
else
- attacked_mob.visible_message(span_danger("[user] prods [attacked_mob] with [src]!"), \
- span_userdanger("[user] prods you with [src]!"))
+ attacked_mob.visible_message(
+ span_danger("[user] prods [attacked_mob] with [src]!"),
+ span_userdanger("[user] prods you with [src]!"),
+ )
playsound(loc, 'sound/weapons/egloves.ogg', 50, TRUE, -1)
cooldown_check = world.time + cooldown
@@ -102,41 +106,57 @@
attacked_mob.attack_hand(user, modifiers) //This enables borgs to get the floating heart icon and mob emote from simple_animal's that have petbonus == true.
return
if(user.zone_selected == BODY_ZONE_HEAD)
- user.visible_message(span_notice("[user] playfully boops [attacked_mob] on the head!"), \
- span_notice("You playfully boop [attacked_mob] on the head!"))
+ user.visible_message(
+ span_notice("[user] playfully boops [attacked_mob] on the head!"),
+ span_notice("You playfully boop [attacked_mob] on the head!"),
+ )
user.do_attack_animation(attacked_mob, ATTACK_EFFECT_BOOP)
playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE, -1)
else if(ishuman(attacked_mob))
if(user.body_position == LYING_DOWN)
- user.visible_message(span_notice("[user] shakes [attacked_mob] trying to get [attacked_mob.p_them()] up!"), \
- span_notice("You shake [attacked_mob] trying to get [attacked_mob.p_them()] up!"))
+ user.visible_message(
+ span_notice("[user] shakes [attacked_mob] trying to get [attacked_mob.p_them()] up!"),
+ span_notice("You shake [attacked_mob] trying to get [attacked_mob.p_them()] up!"),
+ )
else
- user.visible_message(span_notice("[user] hugs [attacked_mob] to make [attacked_mob.p_them()] feel better!"), \
- span_notice("You hug [attacked_mob] to make [attacked_mob.p_them()] feel better!"))
+ user.visible_message(
+ span_notice("[user] hugs [attacked_mob] to make [attacked_mob.p_them()] feel better!"),
+ span_notice("You hug [attacked_mob] to make [attacked_mob.p_them()] feel better!"),
+ )
if(attacked_mob.resting)
attacked_mob.set_resting(FALSE, TRUE)
else
- user.visible_message(span_notice("[user] pets [attacked_mob]!"), \
- span_notice("You pet [attacked_mob]!"))
+ user.visible_message(
+ span_notice("[user] pets [attacked_mob]!"),
+ span_notice("You pet [attacked_mob]!"),
+ )
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
if(HUG_MODE_HUG)
if(ishuman(attacked_mob))
attacked_mob.adjust_status_effects_on_shake_up()
if(attacked_mob.body_position == LYING_DOWN)
- user.visible_message(span_notice("[user] shakes [attacked_mob] trying to get [attacked_mob.p_them()] up!"), \
- span_notice("You shake [attacked_mob] trying to get [attacked_mob.p_them()] up!"))
+ user.visible_message(
+ span_notice("[user] shakes [attacked_mob] trying to get [attacked_mob.p_them()] up!"),
+ span_notice("You shake [attacked_mob] trying to get [attacked_mob.p_them()] up!"),
+ )
else if(user.zone_selected == BODY_ZONE_HEAD)
- user.visible_message(span_warning("[user] bops [attacked_mob] on the head!"), \
- span_warning("You bop [attacked_mob] on the head!"))
+ user.visible_message(span_warning("[user] bops [attacked_mob] on the head!"),
+ span_warning("You bop [attacked_mob] on the head!"),
+ )
user.do_attack_animation(attacked_mob, ATTACK_EFFECT_PUNCH)
else
- user.visible_message(span_warning("[user] hugs [attacked_mob] in a firm bear-hug! [attacked_mob] looks uncomfortable..."), \
- span_warning("You hug [attacked_mob] firmly to make [attacked_mob.p_them()] feel better! [attacked_mob] looks uncomfortable..."))
+ if(!(SEND_SIGNAL(attacked_mob, COMSIG_BORG_HUG_MOB, user) & COMSIG_BORG_HUG_HANDLED))
+ user.visible_message(
+ span_warning("[user] hugs [attacked_mob] in a firm bear-hug! [attacked_mob] looks uncomfortable..."),
+ span_warning("You hug [attacked_mob] firmly to make [attacked_mob.p_them()] feel better! [attacked_mob] looks uncomfortable..."),
+ )
if(attacked_mob.resting)
attacked_mob.set_resting(FALSE, TRUE)
else
- user.visible_message(span_warning("[user] bops [attacked_mob] on the head!"), \
- span_warning("You bop [attacked_mob] on the head!"))
+ user.visible_message(
+ span_warning("[user] bops [attacked_mob] on the head!"),
+ span_warning("You bop [attacked_mob] on the head!"),
+ )
playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE, -1)
if(HUG_MODE_SHOCK)
if (!COOLDOWN_FINISHED(src, shock_cooldown))
@@ -145,16 +165,22 @@
attacked_mob.electrocute_act(5, "[user]", flags = SHOCK_NOGLOVES | SHOCK_NOSTUN)
attacked_mob.dropItemToGround(attacked_mob.get_active_held_item())
attacked_mob.dropItemToGround(attacked_mob.get_inactive_held_item())
- user.visible_message(span_userdanger("[user] electrocutes [attacked_mob] with [user.p_their()] touch!"), \
- span_danger("You electrocute [attacked_mob] with your touch!"))
+ user.visible_message(
+ span_userdanger("[user] electrocutes [attacked_mob] with [user.p_their()] touch!"),
+ span_danger("You electrocute [attacked_mob] with your touch!"),
+ )
else
if(!iscyborg(attacked_mob))
attacked_mob.adjustFireLoss(10)
- user.visible_message(span_userdanger("[user] shocks [attacked_mob]!"), \
- span_danger("You shock [attacked_mob]!"))
+ user.visible_message(
+ span_userdanger("[user] shocks [attacked_mob]!"),
+ span_danger("You shock [attacked_mob]!"),
+ )
else
- user.visible_message(span_userdanger("[user] shocks [attacked_mob]. It does not seem to have an effect"), \
- span_danger("You shock [attacked_mob] to no effect."))
+ user.visible_message(
+ span_userdanger("[user] shocks [attacked_mob]. It does not seem to have an effect"),
+ span_danger("You shock [attacked_mob] to no effect."),
+ )
playsound(loc, 'sound/effects/sparks2.ogg', 50, TRUE, -1)
user.cell.charge -= 500
COOLDOWN_START(src, shock_cooldown, HUG_SHOCK_COOLDOWN)
@@ -162,11 +188,15 @@
if (!COOLDOWN_FINISHED(src, crush_cooldown))
return
if(ishuman(attacked_mob))
- user.visible_message(span_userdanger("[user] crushes [attacked_mob] in [user.p_their()] grip!"), \
- span_danger("You crush [attacked_mob] in your grip!"))
+ user.visible_message(
+ span_userdanger("[user] crushes [attacked_mob] in [user.p_their()] grip!"),
+ span_danger("You crush [attacked_mob] in your grip!"),
+ )
else
- user.visible_message(span_userdanger("[user] crushes [attacked_mob]!"), \
- span_danger("You crush [attacked_mob]!"))
+ user.visible_message(
+ span_userdanger("[user] crushes [attacked_mob]!"),
+ span_danger("You crush [attacked_mob]!"),
+ )
playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE, -1)
attacked_mob.adjustBruteLoss(15)
user.cell.charge -= 300
@@ -337,9 +367,11 @@
safety = FALSE
if(safety == TRUE)
- user.visible_message("[user] blares out a near-deafening siren from its speakers!", \
- span_userdanger("Your siren blares around [iscyborg(user) ? "you" : "and confuses you"]!"), \
- span_danger("The siren pierces your hearing!"))
+ user.visible_message(
+ "[user] blares out a near-deafening siren from its speakers!",
+ span_userdanger("Your siren blares around [iscyborg(user) ? "you" : "and confuses you"]!"),
+ span_danger("The siren pierces your hearing!"),
+ )
for(var/mob/living/carbon/carbon in get_hearers_in_view(9, user))
if(carbon.get_ear_protection())
continue
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index a67788f2725..fa1ec5cfd8d 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -590,7 +590,7 @@
new /obj/item/surgicaldrill(src)
new /obj/item/cautery(src)
new /obj/item/surgical_drapes(src)
- new /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown(src) //SKYRAT EDIT ADDITION
+ new /obj/item/clothing/suit/toggle/labcoat/hospitalgown(src) //SKYRAT EDIT ADDITION
new /obj/item/clothing/mask/surgical(src)
new /obj/item/blood_filter(src)
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 01f075597c4..8ac1a726c71 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -190,6 +190,26 @@
to_preload += /obj/item/extinguisher/mini
return to_preload
+/obj/item/storage/belt/utility/full/inducer/PopulateContents()
+ SSwardrobe.provide_type(/obj/item/screwdriver, src)
+ SSwardrobe.provide_type(/obj/item/wrench, src)
+ SSwardrobe.provide_type(/obj/item/weldingtool, src)
+ SSwardrobe.provide_type(/obj/item/crowbar/red, src)
+ SSwardrobe.provide_type(/obj/item/wirecutters, src)
+ SSwardrobe.provide_type(/obj/item/multitool, src)
+ SSwardrobe.provide_type(/obj/item/inducer, src)
+
+/obj/item/storage/belt/utility/full/inducer/get_types_to_preload()
+ var/list/to_preload = list() //Yes this is a pain. Yes this is the point
+ to_preload += /obj/item/screwdriver
+ to_preload += /obj/item/wrench
+ to_preload += /obj/item/weldingtool
+ to_preload += /obj/item/crowbar
+ to_preload += /obj/item/wirecutters
+ to_preload += /obj/item/multitool
+ to_preload += /obj/item/inducer
+ return to_preload
+
/obj/item/storage/belt/utility/syndicate
preload = FALSE
@@ -225,7 +245,7 @@
/obj/item/clothing/mask/breath,
/obj/item/clothing/mask/muzzle,
/obj/item/clothing/mask/surgical,
- /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown, //SKYRAT EDIT ADDITION - adds surgery gowns to belts
+ /obj/item/clothing/suit/toggle/labcoat/hospitalgown, //SKYRAT EDIT ADDITION - adds surgery gowns to belts
/obj/item/construction/plumbing,
/obj/item/dnainjector,
/obj/item/extinguisher/mini,
diff --git a/code/game/objects/items/storage/garment.dm b/code/game/objects/items/storage/garment.dm
index f791b530fea..9d2c7dcbca4 100644
--- a/code/game/objects/items/storage/garment.dm
+++ b/code/game/objects/items/storage/garment.dm
@@ -89,6 +89,7 @@
new /obj/item/clothing/suit/armor/vest/leather(src)
new /obj/item/clothing/glasses/hud/security/sunglasses/eyepatch(src)
new /obj/item/clothing/glasses/hud/security/sunglasses/gars/giga(src)
+ new /obj/item/clothing/head/hats/hos/beret(src)
new /obj/item/clothing/head/hats/hos/cap(src)
new /obj/item/clothing/mask/gas/sechailer/swat(src)
new /obj/item/clothing/neck/cloak/hos(src)
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index cba35c8b95c..94c8847577e 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -276,63 +276,6 @@
new /obj/item/implanter/freedom(src) // 5 tc
new /obj/item/stack/telecrystal(src) //The failsafe/self destruct isn't an item we can physically include in the kit, but 1 TC is technically enough to buy the equivalent.
-/obj/item/storage/box/syndicate/contract_kit //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- name = "Contract Kit"
- desc = "Supplied to Syndicate contractors."
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/storage/box/syndicate/contractor_loadout //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- name = "Standard Loadout"
- desc = "Supplied to Syndicate contractors, providing their specialised space suit and chameleon uniform."
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/paper/contractor_guide //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- name = "Contractor Guide"
-
-/obj/item/paper/contractor_guide/Initialize(mapload) //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- default_raw_text = {"
Welcome agent, congratulations on your new position as contractor. On top of your already assigned objectives,
- this kit will provide you contracts to take on for TC payments.
-
-
Provided within, we give your specialist contractor space suit. It's even more compact, being able to fit into a pocket, and faster than the
- Syndicate space suit available to you on the uplink. We also provide your chameleon jumpsuit and mask, both of which can be changed
- to any form you need for the moment. The cigarettes are a special blend - it'll heal your injuries slowly overtime.
-
-
Your standard issue contractor baton hits harder than the ones you might be used to, and likely be your go to weapon for kidnapping your
- targets. The three additional items have been randomly selected from what we had available. We hope they're useful to you for your mission.
-
-
The contractor hub, available at the top right of the uplink, will provide you unique items and abilities. These are bought using Contractor Rep,
- with two Rep being provided each time you complete a contract.
-
-
Using the tablet
-
-
Open the Syndicate Contract Uplink program.
-
Here, you can accept a contract, and redeem your TC payments from completed contracts.
-
The payment number shown in brackets is the bonus you'll receive when bringing your target alive. You receive the
- other number regardless of if they were alive or dead.
-
Contracts are completed by bringing the target to designated dropoff, calling for extraction, and putting them
- inside the pod.
-
-
-
Be careful when accepting a contract. While you'll be able to see the location of the dropoff point, cancelling will make it
- unavailable to take on again.
-
The tablet can also be recharged at any cell charger.
-
Extracting
-
-
Make sure both yourself and your target are at the dropoff.
-
Call the extraction, and stand back from the drop point.
-
If it fails, make sure your target is inside, and there's a free space for the pod to land.
-
Grab your target, and drag them into the pod.
-
-
Ransoms
-
We need your target for our own reasons, but we ransom them back to your mission area once their use is served. They will return back
- from where you sent them off from in several minutes time. Don't worry, we give you a cut of what we get paid. We pay this into whatever
- ID card you have equipped, on top of the TC payment we give.
-
-
Good luck agent. You can burn this document with the supplied lighter.
"}
- return ..()
-
/obj/item/storage/box/syndie_kit
name = "box"
desc = "A sleek, sturdy box."
@@ -821,6 +764,63 @@
/obj/item/food/grown/apple = 1,
), src)
+/obj/item/storage/box/syndicate/contract_kit
+ name = "Contract Kit"
+ desc = "Supplied to Syndicate contractors."
+ icon_state = "syndiebox"
+ illustration = "writing_syndie"
+
+/obj/item/storage/box/syndicate/contract_kit/PopulateContents()
+ new /obj/item/modular_computer/pda/syndicate_contract_uplink(src)
+ new /obj/item/storage/box/syndicate/contractor_loadout(src)
+ new /obj/item/melee/baton/telescopic/contractor_baton(src)
+
+ // All about 4 TC or less - some nukeops only items, but fit nicely to the theme.
+ var/static/list/item_list = list(
+ /obj/item/storage/backpack/duffelbag/syndie/x4,
+ /obj/item/storage/box/syndie_kit/throwing_weapons,
+ /obj/item/gun/syringe/syndicate,
+ /obj/item/pen/edagger,
+ /obj/item/pen/sleepy,
+ /obj/item/flashlight/emp,
+ /obj/item/reagent_containers/syringe/mulligan,
+ /obj/item/clothing/shoes/chameleon/noslip,
+ /obj/item/storage/medkit/tactical,
+ /obj/item/encryptionkey/syndicate,
+ /obj/item/clothing/glasses/thermal/syndi,
+ /obj/item/slimepotion/slime/sentience/nuclear,
+ /obj/item/storage/box/syndie_kit/imp_radio,
+ /obj/item/storage/box/syndie_kit/imp_uplink,
+ /obj/item/clothing/gloves/krav_maga/combatglovesplus,
+ /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot,
+ /obj/item/reagent_containers/hypospray/medipen/stimulants,
+ /obj/item/storage/box/syndie_kit/imp_freedom,
+ /obj/item/toy/eightball/haunted,
+ )
+ for(var/i in 1 to 3)
+ var/selected_item = pick_n_take(item_list)
+ new selected_item(src)
+
+ // Paper guide is always last.
+ new /obj/item/paper/contractor_guide(src)
+
+/obj/item/storage/box/syndicate/contractor_loadout
+ name = "Standard Loadout"
+ desc = "Supplied to Syndicate contractors, providing their specialised space suit and chameleon uniform."
+ icon_state = "syndiebox"
+ illustration = "writing_syndie"
+
+/obj/item/storage/box/syndicate/contractor_loadout/PopulateContents()
+ /* SKYRAT EDIT REMOVAL BEGIN - Contractors get MODSuits instead, see contractor modular for overrides
+ new /obj/item/clothing/head/helmet/space/syndicate/contract(src)
+ new /obj/item/clothing/suit/space/syndicate/contract(src)
+ SKYRAT EDIT REMOVAL END */
+ new /obj/item/clothing/under/chameleon(src)
+ new /obj/item/clothing/mask/chameleon(src)
+ new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
+ new /obj/item/card/id/advanced/chameleon(src)
+ new /obj/item/lighter(src)
+
#undef KIT_RECON
#undef KIT_BLOODY_SPAI
#undef KIT_STEALTHY
diff --git a/code/game/objects/items/surgery_tray.dm b/code/game/objects/items/surgery_tray.dm
index 680698e3318..1839245929e 100644
--- a/code/game/objects/items/surgery_tray.dm
+++ b/code/game/objects/items/surgery_tray.dm
@@ -12,7 +12,7 @@
/obj/item/cautery,
/obj/item/circular_saw,
/obj/item/clothing/mask/surgical,
- /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown, // SKYRAT EDIT ADDITION
+ /obj/item/clothing/suit/toggle/labcoat/hospitalgown, // SKYRAT EDIT ADDITION
/obj/item/hemostat,
/obj/item/razor,
/obj/item/reagent_containers/medigel,
@@ -197,7 +197,7 @@
new /obj/item/cautery(src)
new /obj/item/circular_saw(src)
new /obj/item/clothing/mask/surgical(src)
- new /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown(src) // SKYRAT EDIT ADDITION
+ new /obj/item/clothing/suit/toggle/labcoat/hospitalgown(src) // SKYRAT EDIT ADDITION
new /obj/item/hemostat(src)
new /obj/item/razor/surgery(src)
new /obj/item/retractor(src)
@@ -219,7 +219,7 @@
new /obj/item/cautery/cruel(src)
new /obj/item/circular_saw(src)
new /obj/item/clothing/mask/surgical(src)
- new /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown(src) // SKYRAT EDIT ADDITION
+ new /obj/item/clothing/suit/toggle/labcoat/hospitalgown(src) // SKYRAT EDIT ADDITION
new /obj/item/hemostat/cruel(src)
new /obj/item/razor/surgery(src)
new /obj/item/retractor/cruel(src)
diff --git a/code/game/objects/items/theft_tools.dm b/code/game/objects/items/theft_tools.dm
index 8f357e05139..8b7ff362046 100644
--- a/code/game/objects/items/theft_tools.dm
+++ b/code/game/objects/items/theft_tools.dm
@@ -157,13 +157,10 @@
inhand_icon_state = null //touching it dusts you, so no need for an inhand icon.
pulseicon = "supermatter_sliver_pulse"
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
-
/obj/item/nuke_core/supermatter_sliver/attack_tk(mob/user) // no TK dusting memes
return
-
/obj/item/nuke_core/supermatter_sliver/can_be_pulled(user) // no drag memes
return FALSE
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index c83fefb2fce..059f78b80c9 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -161,7 +161,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/highlander/process()
if(ishuman(loc))
var/mob/living/carbon/human/holder = loc
- SET_PLANE_EXPLICIT(holder, GAME_PLANE_UPPER_FOV_HIDDEN, src) //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS)
+ layer = ABOVE_ALL_MOB_LAYER //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS)
ADD_TRAIT(holder, TRAIT_NOBLOOD, HIGHLANDER_TRAIT) //AND WE WON'T BLEED OUT LIKE COWARDS
else
if(!(flags_1 & ADMIN_SPAWNED_1))
@@ -285,7 +285,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
return INITIALIZE_HINT_QDEL
/obj/item/claymore/highlander/robot/process()
- SET_PLANE_IMPLICIT(loc, GAME_PLANE_UPPER_FOV_HIDDEN)
+ layer = ABOVE_ALL_MOB_LAYER
/obj/item/katana
name = "katana"
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index e637ecc274e..886d0b9bcba 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -338,7 +338,6 @@
integrity_failure = 0.05
var/status = GROWING //can be GROWING, GROWN or BURST; all mutually exclusive
layer = MOB_LAYER
- plane = GAME_PLANE_FOV_HIDDEN
/// Ref to the hugger within.
var/obj/item/clothing/mask/facehugger/child
///Proximity monitor associated with this atom, needed for proximity checks.
diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm
index fee4703d117..32e1226c93e 100644
--- a/code/game/objects/structures/beds_chairs/alien_nest.dm
+++ b/code/game/objects/structures/beds_chairs/alien_nest.dm
@@ -12,7 +12,7 @@
smoothing_groups = SMOOTH_GROUP_ALIEN_NEST
canSmoothWith = SMOOTH_GROUP_ALIEN_NEST
build_stack_type = null
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
elevation = 0
var/static/mutable_appearance/nest_overlay = mutable_appearance('icons/mob/nonhuman-player/alien.dmi', "nestoverlay", LYING_MOB_LAYER)
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 6dd9c6cd2c7..be83e86ca7b 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -109,10 +109,8 @@
/obj/structure/chair/proc/handle_layer()
if(has_buckled_mobs() && dir == NORTH)
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER_FOV_HIDDEN)
else
layer = OBJ_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
/obj/structure/chair/post_buckle_mob(mob/living/M)
. = ..()
@@ -184,7 +182,6 @@
/obj/structure/chair/comfy/proc/gen_armrest()
armrest = GetArmrest()
armrest.layer = ABOVE_MOB_LAYER
- SET_PLANE_EXPLICIT(armrest, GAME_PLANE_UPPER, src)
update_armrest()
/obj/structure/chair/comfy/proc/GetArmrest()
@@ -495,7 +492,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
icon_state = null
buildstacktype = null
item_chair = null
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
alpha = 0
/obj/structure/chair/mime/post_buckle_mob(mob/living/M)
diff --git a/code/game/objects/structures/beds_chairs/pew.dm b/code/game/objects/structures/beds_chairs/pew.dm
index 21bf0fbf09c..6388247e8c4 100644
--- a/code/game/objects/structures/beds_chairs/pew.dm
+++ b/code/game/objects/structures/beds_chairs/pew.dm
@@ -33,10 +33,8 @@
/obj/structure/chair/pew/left/proc/gen_armrest()
leftpewarmrest = GetLeftPewArmrest()
leftpewarmrest.layer = ABOVE_MOB_LAYER
- SET_PLANE_EXPLICIT(leftpewarmrest, GAME_PLANE_UPPER, src)
update_leftpewarmrest()
-
/obj/structure/chair/pew/left/proc/GetLeftPewArmrest()
return mutable_appearance('icons/obj/chairs_wide.dmi', "pewend_left_armrest")
@@ -76,7 +74,6 @@
/obj/structure/chair/pew/right/proc/gen_armrest()
rightpewarmrest = GetRightPewArmrest()
rightpewarmrest.layer = ABOVE_MOB_LAYER
- SET_PLANE_EXPLICIT(rightpewarmrest, GAME_PLANE_UPPER, src)
update_rightpewarmrest()
/obj/structure/chair/pew/right/proc/GetRightPewArmrest()
diff --git a/code/game/objects/structures/beds_chairs/sofa.dm b/code/game/objects/structures/beds_chairs/sofa.dm
index 076f95f4dc9..bf9a221929b 100644
--- a/code/game/objects/structures/beds_chairs/sofa.dm
+++ b/code/game/objects/structures/beds_chairs/sofa.dm
@@ -36,7 +36,6 @@ path/corner/color_name {\
/obj/structure/chair/sofa/proc/gen_armrest()
armrest = mutable_appearance(initial(icon), "[icon_state]_armrest", ABOVE_MOB_LAYER)
- SET_PLANE_EXPLICIT(armrest, GAME_PLANE_UPPER, src)
update_armrest()
/obj/structure/chair/sofa/electrify_self(obj/item/assembly/shock_kit/input_shock_kit, mob/user, list/overlays_from_child_procs)
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index 8517a321ceb..c440976b409 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -86,7 +86,6 @@ LINEN BINS
/obj/item/bedsheet/proc/coverup(mob/living/sleeper)
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
pixel_x = 0
pixel_y = 0
balloon_alert(sleeper, "covered")
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 6d58bce0b32..63c63c1664a 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -57,6 +57,7 @@
new /obj/item/computer_disk/command/hos(src)
new /obj/item/radio/headset/heads/hos(src)
+ new /obj/item/radio/headset/heads/hos/alt(src)
new /obj/item/storage/bag/garment/hos(src)
new /obj/item/storage/lockbox/medal/sec(src)
new /obj/item/megaphone/sec(src)
diff --git a/code/game/objects/structures/deployable_turret.dm b/code/game/objects/structures/deployable_turret.dm
index 0e810526bdc..ac4b35678c9 100644
--- a/code/game/objects/structures/deployable_turret.dm
+++ b/code/game/objects/structures/deployable_turret.dm
@@ -12,7 +12,6 @@
max_integrity = 100
buckle_lying = 0
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
var/view_range = 2.5
var/cooldown = 0
/// The projectile that the turret fires
@@ -101,7 +100,6 @@
M.put_in_hands(TC)
M.pixel_y = 14
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
setDir(SOUTH)
playsound(src,'sound/mecha/mechmove01.ogg', 50, TRUE)
set_anchored(TRUE)
@@ -136,43 +134,34 @@
switch(dir)
if(NORTH)
layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
user.pixel_x = 0
user.pixel_y = -14
if(NORTHEAST)
layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
user.pixel_x = -8
user.pixel_y = -4
if(EAST)
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
user.pixel_x = -14
user.pixel_y = 0
if(SOUTHEAST)
layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
user.pixel_x = -8
user.pixel_y = 4
if(SOUTH)
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
- plane = GAME_PLANE_UPPER
user.pixel_x = 0
user.pixel_y = 14
if(SOUTHWEST)
layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
user.pixel_x = 8
user.pixel_y = 4
if(WEST)
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
user.pixel_x = 14
user.pixel_y = 0
if(NORTHWEST)
layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
user.pixel_x = 8
user.pixel_y = -4
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 728232579e9..327f7a054c8 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -450,11 +450,12 @@
name = "xmas tree"
desc = "A wondrous decorated Christmas tree."
icon_state = "pine_c"
- obj_flags = NO_DECONSTRUCTION //protected by the christmas spirit
/obj/structure/flora/tree/pine/xmas/presents
icon_state = "pinepresents"
desc = "A wondrous decorated Christmas tree. It has presents!"
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF //protected by the christmas spirit
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
var/gift_type = /obj/item/gift/anything
var/unlimited = FALSE
var/static/list/took_presents //shared between all xmas trees
diff --git a/code/game/objects/structures/fluff.dm b/code/game/objects/structures/fluff.dm
index 57207cf7aba..a38659aaefe 100644
--- a/code/game/objects/structures/fluff.dm
+++ b/code/game/objects/structures/fluff.dm
@@ -74,7 +74,7 @@
density = TRUE
deconstructible = FALSE
layer = EDGED_TURF_LAYER
- plane = GAME_PLANE_UPPER
+
/**
* A variety of statue in disrepair; parts are broken off and a gemstone is missing
*/
diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm
index ca019c05c12..bc56b3838ca 100644
--- a/code/game/objects/structures/guillotine.dm
+++ b/code/game/objects/structures/guillotine.dm
@@ -42,7 +42,6 @@
buckle_lying = 0
buckle_prevents_pull = TRUE
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
/// The sound the guillotine makes when it successfully cuts off a head
var/drop_sound = 'sound/weapons/guillotine.ogg'
/// The current state of the blade
diff --git a/code/game/objects/structures/gym/weight_machine.dm b/code/game/objects/structures/gym/weight_machine.dm
index a7426e2f465..055c9788c95 100644
--- a/code/game/objects/structures/gym/weight_machine.dm
+++ b/code/game/objects/structures/gym/weight_machine.dm
@@ -152,7 +152,6 @@
end_workout()
return FALSE
var/mutable_appearance/workout = mutable_appearance(icon, "[base_icon_state]-o", ABOVE_MOB_LAYER)
- SET_PLANE_EXPLICIT(workout, GAME_PLANE_UPPER, src)
flick_overlay_view(workout, 0.8 SECONDS)
flick("[base_icon_state]-u", src)
var/mob/living/user = buckled_mobs[1]
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index bd20ef405fc..2fa25dee9fa 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -24,7 +24,7 @@
var/turf/our_turf = get_turf(src)
if(use_vis_overlay)
alpha = 0
- SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, MUTATE_PLANE(GAME_PLANE_UPPER, our_turf), dir, add_appearance_flags = RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
+ SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, MUTATE_PLANE(GAME_PLANE, our_turf), dir, add_appearance_flags = RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
if(source_projector)
projector = source_projector
LAZYADD(projector.signs, src)
diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm
index 6ac313883df..f4834ecf48a 100644
--- a/code/game/objects/structures/plasticflaps.dm
+++ b/code/game/objects/structures/plasticflaps.dm
@@ -36,7 +36,7 @@
/obj/structure/plasticflaps/proc/gen_overlay()
var/turf/our_turf = get_turf(src)
- SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, MUTATE_PLANE(GAME_PLANE_UPPER, our_turf), dir, add_appearance_flags = RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
+ SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, MUTATE_PLANE(GAME_PLANE, our_turf), dir, add_appearance_flags = RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
/obj/structure/plasticflaps/examine(mob/user)
. = ..()
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index fb69570eea5..3604bd06c63 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -9,7 +9,6 @@
anchored = TRUE
pass_flags_self = LETPASSTHROW|PASSSTRUCTURE
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
/// armor is a little bit less than a grille. max_integrity about half that of a grille.
armor_type = /datum/armor/structure_railing
max_integrity = 25
@@ -164,7 +163,6 @@
icon = 'icons/obj/structures.dmi'
icon_state = "wooden_railing"
item_deconstruct = /obj/item/stack/sheet/mineral/wood
- plane = GAME_PLANE_FOV_HIDDEN
layer = ABOVE_MOB_LAYER
/obj/structure/railing/wooden_fence/Initialize(mapload)
diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm
index ce3d8414398..d5f2ac1ec22 100644
--- a/code/game/objects/structures/shower.dm
+++ b/code/game/objects/structures/shower.dm
@@ -204,7 +204,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
return
var/mutable_appearance/water_falling = mutable_appearance('icons/obj/watercloset.dmi', "water", ABOVE_MOB_LAYER)
water_falling.color = mix_color_from_reagents(reagents.reagent_list)
- SET_PLANE_EXPLICIT(water_falling, GAME_PLANE_UPPER, src)
switch(dir)
if(NORTH)
water_falling.pixel_y += pixel_shift
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 531df729957..b343cd85f50 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -760,12 +760,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen, (-16))
open = !open
if(open)
layer = SIGN_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
set_density(FALSE)
set_opacity(FALSE)
else
layer = WALL_OBJ_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
set_density(TRUE)
if(opaque_closed)
set_opacity(TRUE)
@@ -878,7 +876,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen, (-16))
/obj/structure/curtain/cloth/fancy/mechanical/proc/close()
icon_state = "[icon_type]-closed"
layer = WALL_OBJ_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
set_density(TRUE)
open = FALSE
if(opaque_closed)
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index f809a3c32fd..678a9a25314 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -803,7 +803,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/tinted/frosted/spaw
/obj/structure/window/reinforced/shuttle/indestructible
name = "hardened shuttle window"
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
flags_1 = PREVENT_CLICK_UNDER_1
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm
index 173033c01c9..ec02e5b27a7 100644
--- a/code/game/turfs/closed/minerals.dm
+++ b/code/game/turfs/closed/minerals.dm
@@ -6,13 +6,17 @@
name = "rock"
icon = MAP_SWITCH('modular_skyrat/modules/liquids/icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi') // SKYRAT EDIT CHANGE
icon_state = "rock"
+ smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MINERAL_WALLS
+ canSmoothWith = SMOOTH_GROUP_MINERAL_WALLS
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
baseturfs = /turf/open/misc/asteroid/airless
initial_gas_mix = AIRLESS_ATMOS
opacity = TRUE
density = TRUE
+ // We're a BIG wall, larger then 32x32, so we need to be on the game plane
+ // Otherwise we'll draw under shit in weird ways
+ plane = GAME_PLANE
layer = EDGED_TURF_LAYER
- plane = WALL_PLANE_UPPER
base_icon_state = "smoothrocks"
// This is static
@@ -37,14 +41,15 @@
/turf/closed/mineral/Initialize(mapload)
- var/static/list/smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MINERAL_WALLS
- var/static/list/canSmoothWith = SMOOTH_GROUP_MINERAL_WALLS
-
- // The cost of the list() being in the type def is very large for something as common as minerals
- src.smoothing_groups = smoothing_groups
- src.canSmoothWith = canSmoothWith
-
- return ..()
+ . = ..()
+ // Mineral turfs are big, so they need to be on the game plane at a high layer
+ // But they're also turfs, so we need to cut them out from the light mask plane
+ // So we draw them as if they were on the game plane, and then overlay a copy onto
+ // The wall plane (so emissives/light masks behave)
+ // I am so sorry
+ var/static/mutable_appearance/wall_overlay = mutable_appearance('icons/turf/mining.dmi', "rock", appearance_flags = RESET_TRANSFORM)
+ wall_overlay.plane = MUTATE_PLANE(WALL_PLANE, src)
+ overlays += wall_overlay
// Inlined version of the bump click element. way faster this way, the element's nice but it's too much overhead
/turf/closed/mineral/Bumped(atom/movable/bumped_atom)
@@ -689,7 +694,6 @@
/turf/closed/mineral/gibtonite/proc/explosive_reaction(mob/user = null)
if(stage == GIBTONITE_UNSTRUCK)
activated_overlay = mutable_appearance('icons/turf/smoothrocks_overlays.dmi', "rock_Gibtonite_inactive", ON_EDGED_TURF_LAYER) //shows in gaps between pulses if there are any
- SET_PLANE(activated_overlay, WALL_PLANE_UPPER, src)
add_overlay(activated_overlay)
name = "gibtonite deposit"
desc = "An active gibtonite reserve. Run!"
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index 69060ac8568..1a174723d85 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -68,7 +68,7 @@
// But that's rare, and I'm ok with that, quartering our light source count is useful
var/mutable_appearance/light_mask = mutable_appearance(mask_icon, mask_state, LIGHTING_MASK_LAYER, src, LIGHTING_PLANE)
light_mask.blend_mode = BLEND_MULTIPLY
- light_mask.color = list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0)
+ light_mask.color = COLOR_MATRIX_INVERT
. += light_mask
/// Refreshes this lava turf's lighting
@@ -420,7 +420,7 @@
if(BODY_ZONE_HEAD)
plasmalimb = new /obj/item/bodypart/head/plasmaman
- burn_human.del_and_replace_bodypart(plasmalimb)
+ burn_human.del_and_replace_bodypart(plasmalimb, special = TRUE)
burn_human.update_body_parts()
burn_human.emote("scream")
burn_human.visible_message(span_warning("[burn_human]'s [burn_limb.plaintext_zone] melts down to the bone!"), \
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index a1f083f6139..99aa63325ad 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -31,8 +31,7 @@
dat += ""
if(SSticker.IsRoundInProgress())
dat += "(Game Mode Panel) "
- if(istype(SSticker.mode, /datum/game_mode/dynamic))
- dat += "(Manage Dynamic Rulesets) "
+ dat += "(Manage Dynamic Rulesets) "
dat += {"
Create Object
@@ -147,9 +146,6 @@
user << browse(dat, "window=dyn_mode_options;size=900x650")
/datum/admins/proc/dynamic_ruleset_manager(mob/user)
- if (SSticker.current_state > GAME_STATE_PREGAME && !istype(SSticker.mode, /datum/game_mode/dynamic))
- return // Not running dynamic
-
var/dat = "
Dynamic Ruleset Management
\
Change these options to forcibly enable or disable dynamic rulesets. \
Disabled rulesets will never run, even if they would otherwise be valid. \
@@ -172,11 +168,10 @@
user << browse(dat, "window=dyn_mode_options;size=900x650")
return
- var/datum/game_mode/dynamic/current_mode = SSticker.mode
var/pop_count = length(GLOB.alive_player_list)
- var/threat_level = current_mode.threat_level
- dat += dynamic_ruleset_category_during_round_display("Latejoin", current_mode.latejoin_rules, pop_count, threat_level)
- dat += dynamic_ruleset_category_during_round_display("Midround", current_mode.midround_rules, pop_count, threat_level)
+ var/threat_level = SSdynamic.threat_level
+ dat += dynamic_ruleset_category_during_round_display("Latejoin", SSdynamic.latejoin_rules, pop_count, threat_level)
+ dat += dynamic_ruleset_category_during_round_display("Midround", SSdynamic.midround_rules, pop_count, threat_level)
user << browse(dat, "window=dyn_mode_options;size=900x650")
/datum/admins/proc/dynamic_ruleset_category_pre_start_display(title, list/rules)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 66cd40fe175..437f7fc90c4 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -195,7 +195,6 @@ GLOBAL_PROTECT(admin_verbs_debug)
return list(
#ifdef TESTING /* Keep these at the top to not make the list look fugly */
/client/proc/check_missing_sprites,
- /client/proc/run_dynamic_simulations,
#endif
/proc/machine_upgrade,
/datum/admins/proc/create_or_modify_area,
diff --git a/code/modules/admin/check_antagonists.dm b/code/modules/admin/check_antagonists.dm
index 770d1b9c637..96a15c3fe37 100644
--- a/code/modules/admin/check_antagonists.dm
+++ b/code/modules/admin/check_antagonists.dm
@@ -95,8 +95,7 @@
tgui_alert(usr, "The game hasn't started yet!")
return
var/list/dat = list("Round Status
Round Status
")
- if(istype(SSticker.mode, /datum/game_mode/dynamic)) // Currently only used by dynamic. If more start using this, find a better way.
- dat += "Game Mode Panel "
+ dat += "Game Mode Panel "
dat += "Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)] "
dat += "Emergency shuttle "
if(EMERGENCY_IDLE_OR_RECALLED)
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index 979e51bd5a1..790cdc22d81 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -87,8 +87,16 @@
if (!possessable.ckey && possessable.stat == CONSCIOUS) // Only assign ghosts to living, non-occupied mobs!
bodies += possessable
- var/question = "Would you like to be [group_name]?"
- var/list/candidates = poll_candidates_for_mobs(question, ROLE_SENTIENCE, ROLE_SENTIENCE, 10 SECONDS, bodies, POLL_IGNORE_SHUTTLE_DENIZENS)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mobs(
+ question = "Would you like to be [group_name]?",
+ role = ROLE_SENTIENCE,
+ check_jobban = ROLE_SENTIENCE,
+ poll_time = 10 SECONDS,
+ mobs = bodies,
+ ignore_category = POLL_IGNORE_SHUTTLE_DENIZENS,
+ pic_source = src,
+ role_name_text = "sentience fun balloon",
+ )
while(LAZYLEN(candidates) && LAZYLEN(bodies))
var/mob/dead/observer/C = pick_n_take(candidates)
var/mob/living/body = pick_n_take(bodies)
diff --git a/code/modules/admin/smites/imaginary_friend_special.dm b/code/modules/admin/smites/imaginary_friend_special.dm
index e95f8c52cb1..5b2bc6ba805 100644
--- a/code/modules/admin/smites/imaginary_friend_special.dm
+++ b/code/modules/admin/smites/imaginary_friend_special.dm
@@ -57,11 +57,12 @@
if (isnull(how_many) || how_many < 1)
return FALSE
- var/list/volunteers = poll_ghost_candidates(
+ var/list/volunteers = SSpolling.poll_ghost_candidates(
question = "Do you want to play as an imaginary friend?",
- jobban_type = ROLE_PAI,
+ check_jobban = ROLE_PAI,
poll_time = 10 SECONDS,
ignore_category = POLL_IGNORE_IMAGINARYFRIEND,
+ role_name_text = "imaginary friend",
)
var/volunteer_count = length(volunteers)
if (volunteer_count == 0)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index a618039c545..2f29fe9bde2 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -56,8 +56,8 @@
if(!check_rights(R_ADMIN))
return
- if (!SSticker.mode)
- to_chat(usr, "Not until the round starts!", confidential = TRUE)
+ if (!SSticker.HasRoundStarted())
+ to_chat(usr, span_danger("Not until the round starts!"), confidential = TRUE)
return
var/opt = null
@@ -104,7 +104,7 @@
else if(href_list["gamemode_panel"])
if(!check_rights(R_ADMIN))
return
- SSticker.mode.admin_panel()
+ SSdynamic.admin_panel()
else if(href_list["call_shuttle"])
if(!check_rights(R_ADMIN))
@@ -435,7 +435,7 @@
else if(href_list["f_dynamic_roundstart"])
if(!check_rights(R_ADMIN))
return
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
return tgui_alert(usr, "The game has already started.")
var/roundstart_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart))
@@ -502,7 +502,7 @@
if(!check_rights(R_ADMIN))
return
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
return tgui_alert(usr, "The game has already started.")
dynamic_mode_options(usr)
@@ -536,7 +536,7 @@
if(!check_rights(R_ADMIN))
return
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
return tgui_alert(usr, "The game has already started.")
var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num
@@ -1844,8 +1844,7 @@
if(!check_rights(R_ADMIN))
return
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
- if(!dynamic.picking_specific_rule(/datum/dynamic_ruleset/midround/from_living/opfor_candidate, forced = TRUE, ignore_cost = TRUE))
+ if(!SSdynamic.picking_specific_rule(/datum/dynamic_ruleset/midround/from_living/opfor_candidate, forced = TRUE, ignore_cost = TRUE))
message_admins("An OPFOR candidate could not be selected.")
// SKYRAT ADDITION END
diff --git a/code/modules/admin/verbs/admin.dm b/code/modules/admin/verbs/admin.dm
index 0bd348076aa..f2c3b402243 100644
--- a/code/modules/admin/verbs/admin.dm
+++ b/code/modules/admin/verbs/admin.dm
@@ -37,7 +37,7 @@
if(message)
if(!check_rights(R_SERVER,0))
message = adminscrub(message,500)
- to_chat(world, "[span_adminnotice("[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:")]\n \t [message]", confidential = TRUE)
+ send_formatted_announcement(message, "From [usr.client.holder.fakekey ? "Administrator" : usr.key]")
log_admin("Announce: [key_name(usr)] : [message]")
BLACKBOX_LOG_ADMIN_VERB("Announce")
@@ -174,10 +174,8 @@
if(confirm != "Yes")
return
- for(var/obj/item/W in M)
- if(!M.dropItemToGround(W))
- qdel(W)
- M.regenerate_icons()
+ M.drop_everything(del_on_drop = FALSE, force = TRUE, del_if_nodrop = TRUE)
+ M.regenerate_icons()
log_admin("[key_name(usr)] made [key_name(M)] drop everything!")
var/msg = "[key_name_admin(usr)] made [ADMIN_LOOKUPFLW(M)] drop everything!"
diff --git a/code/modules/admin/verbs/anonymousnames.dm b/code/modules/admin/verbs/anonymousnames.dm
index 6f01b88f113..9a71d68637a 100644
--- a/code/modules/admin/verbs/anonymousnames.dm
+++ b/code/modules/admin/verbs/anonymousnames.dm
@@ -155,7 +155,7 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
priority_announce("As punishment for this station's poor productivity when compared to neighbor stations, names and identities will be restricted until further notice.", "Finance Report", SSstation.announcer.get_rand_alert_sound())
/datum/anonymous_theme/employees/anonymous_name(mob/target)
- var/is_head_of_staff = target.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND
+ var/is_head_of_staff = target.mind.assigned_role.job_flags & JOB_HEAD_OF_STAFF
var/name = "[is_head_of_staff ? "Manager" : "Employee"] "
for(var/i in 1 to 6)
if(prob(30) || i == 1)
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 15bb756cb3d..7d6ab6e2548 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -167,9 +167,9 @@
id.update_icon()
if(worn)
- if(istype(worn, /obj/item/modular_computer/pda))
- var/obj/item/modular_computer/pda/PDA = worn
- PDA.InsertID(id, H)
+ if(istype(worn, /obj/item/modular_computer))
+ var/obj/item/modular_computer/worn_computer = worn
+ worn_computer.InsertID(id, H)
else if(istype(worn, /obj/item/storage/wallet))
var/obj/item/storage/wallet/W = worn
diff --git a/code/modules/admin/verbs/ert.dm b/code/modules/admin/verbs/ert.dm
index 7db4c7cccc5..91950d25ba9 100644
--- a/code/modules/admin/verbs/ert.dm
+++ b/code/modules/admin/verbs/ert.dm
@@ -123,7 +123,7 @@
var/list/spawnpoints = GLOB.emergencyresponseteamspawn
var/index = 0
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you wish to be considered for [ertemplate.polldesc]?", "deathsquad")
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for [ertemplate.polldesc]?", check_jobban = "deathsquad", pic_source = /obj/item/card/id/advanced/centcom/ert, role_name_text = "emergency response team")
var/teamSpawned = FALSE
// This list will take priority over spawnpoints if not empty
diff --git a/code/modules/admin/verbs/list_exposer.dm b/code/modules/admin/verbs/list_exposer.dm
index 445097e8bef..5ea2e227750 100644
--- a/code/modules/admin/verbs/list_exposer.dm
+++ b/code/modules/admin/verbs/list_exposer.dm
@@ -51,12 +51,7 @@
if(!SSticker.HasRoundStarted())
tgui_alert(usr, "The game hasn't started yet!")
return
- var/data = "Showing Crew Manifest."
- data += "
Name
Position
"
- for(var/datum/record/crew/entry in GLOB.manifest.general)
- data += "
"
- usr << browse(data, "window=manifest;size=440x410")
+ GLOB.manifest.ui_interact(usr)
/datum/admins/proc/output_ai_laws()
var/law_bound_entities = 0
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index 6e26fb9754a..f9879428ddf 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -405,7 +405,7 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
var/list/candidates = list()
if (prefs["offerghosts"]["value"] == "Yes")
- candidates = poll_ghost_candidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), ROLE_TRAITOR)
+ candidates = SSpolling.poll_ghost_candidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), check_jobban = ROLE_TRAITOR, pic_source = pathToSpawn, role_name_text = "portal storm")
if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"])
message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]")
@@ -506,8 +506,8 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None")
var/obj/item/organ/internal/ears/cat/ears = new
var/obj/item/organ/external/tail/cat/tail = new
- ears.Insert(H, drop_if_replaced=FALSE)
- tail.Insert(H, drop_if_replaced=FALSE)
+ ears.Insert(H, movement_flags = DELETE_IF_REPLACED)
+ tail.Insert(H, movement_flags = DELETE_IF_REPLACED)
var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san"), "[PLURAL]" = list("san")) //John Robust -> Robust-kun
var/list/names = splittext(H.real_name," ")
var/forename = names.len > 1 ? names[2] : names[1]
@@ -565,7 +565,7 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
if(teamsize <= 0)
return FALSE
- candidates = poll_ghost_candidates("Do you wish to be considered for a Nanotrasen emergency response drone?", "Drone")
+ candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a Nanotrasen emergency response drone?", check_jobban = ROLE_DRONE, pic_source = /mob/living/basic/drone/classic, role_name_text = "nanotrasen emergency response drone")
if(length(candidates) == 0)
return FALSE
diff --git a/code/modules/admin/view_variables/color_matrix_editor.dm b/code/modules/admin/view_variables/color_matrix_editor.dm
index 5ea6d237a6a..7fde4c24590 100644
--- a/code/modules/admin/view_variables/color_matrix_editor.dm
+++ b/code/modules/admin/view_variables/color_matrix_editor.dm
@@ -12,7 +12,7 @@
else if(istext(_target?.color))
current_color = color_hex2color_matrix(_target.color)
else
- current_color = color_matrix_identity()
+ current_color = COLOR_MATRIX_IDENTITY
var/mutable_appearance/view = image('icons/misc/colortest.dmi', "colors")
if(_target)
diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm
index c59391371df..d493831da67 100644
--- a/code/modules/admin/view_variables/get_variables.dm
+++ b/code/modules/admin/view_variables/get_variables.dm
@@ -338,7 +338,7 @@
if(VV_COLOR_MATRIX)
.["value"] = open_color_matrix_editor()
- if(.["value"] == color_matrix_identity()) //identity is equivalent to null
+ if(.["value"] == COLOR_MATRIX_IDENTITY) //identity is equivalent to null
.["class"] = null
if(VV_INFINITY)
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index e8d6a685ae4..d36fb104e2f 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -293,7 +293,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [name]?", "[name]", job_rank, 5 SECONDS, owner.current)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [name]?", check_jobban = "[name]", role = job_rank, poll_time = 5 SECONDS, target_mob = owner.current, pic_source = owner.current, role_name_text = name)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
@@ -463,6 +463,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy)
dummy = dummy || new /mob/living/carbon/human/dummy/consistent
dummy.equipOutfit(outfit, visualsOnly = TRUE)
+ dummy.wear_suit?.update_greyscale()
var/icon = getFlatIcon(dummy)
// We don't want to qdel the dummy right away, since its items haven't initialized yet.
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index 4b432b6b6f1..6ff809b59cb 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -55,7 +55,7 @@
/obj/item/antag_spawner/contract/proc/poll_for_student(mob/living/carbon/human/teacher, apprentice_school)
balloon_alert(teacher, "contacting apprentice...")
polling = TRUE
- var/list/candidates = poll_candidates_for_mob("Do you want to play as a wizard's [apprentice_school] apprentice?", ROLE_WIZARD, ROLE_WIZARD, 15 SECONDS, src)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a wizard's [apprentice_school] apprentice?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 15 SECONDS, target_mob = src, pic_source = teacher, role_name_text = "wizard apprentice")
polling = FALSE
if(!LAZYLEN(candidates))
to_chat(teacher, span_warning("Unable to reach your apprentice! You can either attack the spellbook with the contract to refund your points, or wait and try again later."))
@@ -134,7 +134,7 @@
return
to_chat(user, span_notice("You activate [src] and wait for confirmation."))
- var/list/nuke_candidates = poll_ghost_candidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE)
+ var/list/nuke_candidates = SSpolling.poll_ghost_candidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", check_jobban = ROLE_OPERATIVE, role = ROLE_OPERATIVE, poll_time = 15 SECONDS, ignore_category = POLL_IGNORE_SYNDICATE, pic_source = src, role_name_text = "syndicate [borg_to_spawn ? "[borg_to_spawn] cyborg":"operative"]")
if(LAZYLEN(nuke_candidates))
if(QDELETED(src) || !check_usability(user))
return
@@ -252,7 +252,7 @@
return
if(used)
return
- var/list/candidates = poll_candidates_for_mob("Do you want to play as a [initial(demon_type.name)]?", ROLE_ALIEN, ROLE_ALIEN, 5 SECONDS, src)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [initial(demon_type.name)]?", check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, poll_time = 5 SECONDS, target_mob = src, pic_source = src, role_name_text = initial(demon_type.name))
if(LAZYLEN(candidates))
if(used || QDELETED(src))
return
@@ -332,7 +332,7 @@
return
to_chat(user, span_notice("You activate [src] and wait for confirmation."))
- var/list/baddie_candidates = poll_ghost_candidates("Do you want to play as a [role_to_play]?", poll_role_check, poll_role_check, 10 SECONDS, poll_ignore_category)
+ var/list/baddie_candidates = SSpolling.poll_ghost_candidates("Do you want to play as a [role_to_play]?", check_jobban = poll_role_check, role = poll_role_check, poll_time = 10 SECONDS, ignore_category = poll_ignore_category, pic_source = src, role_name_text = role_to_play)
if(!LAZYLEN(baddie_candidates))
to_chat(user, span_warning(fail_text))
return
diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm
index e29388c9fd6..960851280e3 100644
--- a/code/modules/antagonists/abductor/equipment/gland.dm
+++ b/code/modules/antagonists/abductor/equipment/gland.dm
@@ -84,7 +84,7 @@
active_mind_control = FALSE
return TRUE
-/obj/item/organ/internal/heart/gland/Remove(mob/living/carbon/gland_owner, special = FALSE)
+/obj/item/organ/internal/heart/gland/Remove(mob/living/carbon/gland_owner, special, movement_flags)
. = ..()
active = FALSE
if(initial(uses) == 1)
@@ -93,7 +93,7 @@
hud.remove_atom_from_hud(gland_owner)
clear_mind_control()
-/obj/item/organ/internal/heart/gland/Insert(mob/living/carbon/gland_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/heart/gland/Insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
if(!.)
return
diff --git a/code/modules/antagonists/abductor/equipment/glands/electric.dm b/code/modules/antagonists/abductor/equipment/glands/electric.dm
index a3107af0217..a5ec2cfde21 100644
--- a/code/modules/antagonists/abductor/equipment/glands/electric.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/electric.dm
@@ -7,11 +7,11 @@
mind_control_uses = 2
mind_control_duration = 900
-/obj/item/organ/internal/heart/gland/electric/on_insert(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/electric/on_mob_insert(mob/living/carbon/gland_owner)
. = ..()
ADD_TRAIT(gland_owner, TRAIT_SHOCKIMMUNE, ABDUCTOR_GLAND_TRAIT)
-/obj/item/organ/internal/heart/gland/electric/on_remove(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/electric/on_mob_remove(mob/living/carbon/gland_owner)
. = ..()
REMOVE_TRAIT(gland_owner, TRAIT_SHOCKIMMUNE, ABDUCTOR_GLAND_TRAIT)
diff --git a/code/modules/antagonists/abductor/equipment/glands/slime.dm b/code/modules/antagonists/abductor/equipment/glands/slime.dm
index 60e680bc2a9..853f0bb6473 100644
--- a/code/modules/antagonists/abductor/equipment/glands/slime.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/slime.dm
@@ -7,12 +7,12 @@
mind_control_uses = 1
mind_control_duration = 2400
-/obj/item/organ/internal/heart/gland/slime/on_insert(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/slime/on_mob_insert(mob/living/carbon/gland_owner)
. = ..()
gland_owner.faction |= FACTION_SLIME
gland_owner.grant_language(/datum/language/slime, source = LANGUAGE_GLAND)
-/obj/item/organ/internal/heart/gland/slime/on_remove(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/slime/on_mob_remove(mob/living/carbon/gland_owner)
. = ..()
gland_owner.faction -= FACTION_SLIME
gland_owner.remove_language(/datum/language/slime, source = LANGUAGE_GLAND)
diff --git a/code/modules/antagonists/brainwashing/brainwashing.dm b/code/modules/antagonists/brainwashing/brainwashing.dm
index e58f54f2c65..7a84fc183c4 100644
--- a/code/modules/antagonists/brainwashing/brainwashing.dm
+++ b/code/modules/antagonists/brainwashing/brainwashing.dm
@@ -33,7 +33,7 @@
roundend_category = "brainwashed victims"
show_in_antagpanel = TRUE
antag_hud_name = "brainwashed"
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
ui_name = "AntagInfoBrainwashed"
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 58f161cf1d1..04a1eb903b8 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -437,7 +437,8 @@
purchased_powers[power_path] = new_action
new_action.on_purchase(owner.current) // Grant() is ran in this proc, see changeling_powers.dm.
- log_changeling_power("[key_name(owner)] adapted the [new_action] power")
+ log_changeling_power("[key_name(owner)] adapted the [new_action.name] power")
+ SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, new_action.name)
return TRUE
diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm
index a4d9044d977..9e8c0fa04e9 100644
--- a/code/modules/antagonists/changeling/changeling_power.dm
+++ b/code/modules/antagonists/changeling/changeling_power.dm
@@ -40,8 +40,6 @@ the same goes for Remove(). if you override Remove(), call parent or else your p
*/
/datum/action/changeling/proc/on_purchase(mob/user, is_respec)
- if(!is_respec)
- SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, name)
Grant(user)//how powers are added rather than the checks in mob.dm
/datum/action/changeling/Trigger(trigger_flags)
diff --git a/code/modules/antagonists/changeling/powers/headcrab.dm b/code/modules/antagonists/changeling/powers/headcrab.dm
index f608d1620b8..30970832df0 100644
--- a/code/modules/antagonists/changeling/powers/headcrab.dm
+++ b/code/modules/antagonists/changeling/powers/headcrab.dm
@@ -6,10 +6,12 @@
chemical_cost = 20
dna_cost = 1
req_human = TRUE
+ req_stat = DEAD
+ ignores_fakedeath = TRUE
/datum/action/changeling/headcrab/sting_action(mob/living/user)
set waitfor = FALSE
- var/confirm = tgui_alert(user, "Are we sure we wish to kill ourself and create a headslug?", "Last Resort", list("Yes", "No"))
+ var/confirm = tgui_alert(user, "Are we sure we wish to destroy our body and create a headslug?", "Last Resort", list("Yes", "No"))
if(confirm != "Yes")
return
@@ -23,9 +25,9 @@
if(!eyes || blinded_human.is_blind())
continue
to_chat(blinded_human, span_userdanger("You are blinded by a shower of blood!"))
- blinded_human.Stun(2 SECONDS)
+ blinded_human.Stun(4 SECONDS)
blinded_human.set_eye_blur_if_lower(40 SECONDS)
- blinded_human.adjust_confusion(3 SECONDS)
+ blinded_human.adjust_confusion(12 SECONDS)
for(var/mob/living/silicon/blinded_silicon in range(2,user))
to_chat(blinded_silicon, span_userdanger("Your sensors are disabled by a shower of blood!"))
@@ -39,6 +41,7 @@
. = TRUE
addtimer(CALLBACK(src, PROC_REF(spawn_headcrab), stored_mind, user_turf, organs), 1 SECONDS)
+/// Creates the headrab to occupy
/datum/action/changeling/headcrab/proc/spawn_headcrab(datum/mind/stored_mind, turf/spawn_location, list/organs)
var/mob/living/basic/headslug/crab = new(spawn_location)
for(var/obj/item/organ/I in organs)
diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm
index 86557d2d529..00bdc960644 100644
--- a/code/modules/antagonists/cult/cult_comms.dm
+++ b/code/modules/antagonists/cult/cult_comms.dm
@@ -127,7 +127,7 @@
if(B.current && B.current != Nominee && !B.current.incapacitated())
SEND_SOUND(B.current, 'sound/magic/exit_blood.ogg')
asked_cultists += B.current
- var/list/yes_voters = poll_candidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 300, group = asked_cultists)
+ var/list/yes_voters = SSpolling.poll_candidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 30 SECONDS, group = asked_cultists, pic_source = Nominee, role_name_text = "cult master")
if(QDELETED(Nominee) || Nominee.incapacitated())
team.cult_vote_called = FALSE
for(var/datum/mind/B in team.members)
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index 286fe4973dd..c445e73e90e 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -694,7 +694,7 @@ GLOBAL_VAR_INIT(narsie_summon_count, 0)
if(!mob_to_revive.client || mob_to_revive.client.is_afk())
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [mob_to_revive.real_name], an inactive blood cultist?", ROLE_CULTIST, ROLE_CULTIST, 5 SECONDS, mob_to_revive)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [mob_to_revive.real_name], an inactive blood cultist?", check_jobban = ROLE_CULTIST, role = ROLE_CULTIST, poll_time = 5 SECONDS, target_mob = mob_to_revive, pic_source = mob_to_revive)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.")
diff --git a/code/modules/antagonists/fugitive/fugitive_outfits.dm b/code/modules/antagonists/fugitive/fugitive_outfits.dm
index ba1fe87bfbb..3efab657101 100644
--- a/code/modules/antagonists/fugitive/fugitive_outfits.dm
+++ b/code/modules/antagonists/fugitive/fugitive_outfits.dm
@@ -67,7 +67,7 @@
if(visualsOnly)
return
var/obj/item/organ/internal/eyes/robotic/glow/eyes = new()
- eyes.Insert(H, drop_if_replaced = FALSE)
+ eyes.Insert(H, movement_flags = DELETE_IF_REPLACED)
/datum/outfit/invisible_man
name = "Invisible Man"
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index 17cf0ce86d3..d6740521e80 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -411,7 +411,7 @@
var/num_heads = 0
for(var/mob/player in GLOB.alive_player_list)
- if(player.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(player.mind.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
num_heads++
var/datum/objective/minor_sacrifice/sac_objective = new()
diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm
index b0d2878b455..1cbf98585a3 100644
--- a/code/modules/antagonists/heretic/heretic_knowledge.dm
+++ b/code/modules/antagonists/heretic/heretic_knowledge.dm
@@ -537,7 +537,7 @@
animate(summoned, 10 SECONDS, alpha = 155)
message_admins("A [summoned.name] is being summoned by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(summoned)].")
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [summoned.name]?", ROLE_HERETIC, FALSE, 10 SECONDS, summoned, poll_ignore_define)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [summoned.name]?", check_jobban = ROLE_HERETIC, poll_time = 10 SECONDS, target_mob = summoned, ignore_category = poll_ignore_define, pic_source = summoned, role_name_text = summoned.name)
if(!LAZYLEN(candidates))
loc.balloon_alert(user, "ritual failed, no ghosts!")
animate(summoned, 0.5 SECONDS, alpha = 0)
diff --git a/code/modules/antagonists/heretic/knife_effect.dm b/code/modules/antagonists/heretic/knife_effect.dm
index b1a02fb6800..0bf5a5a4b8c 100644
--- a/code/modules/antagonists/heretic/knife_effect.dm
+++ b/code/modules/antagonists/heretic/knife_effect.dm
@@ -3,7 +3,7 @@
name = "knife"
icon = 'icons/obj/service/kitchen.dmi'
icon_state = "knife"
- plane = GAME_PLANE_FOV_HIDDEN
+ layer = LOW_MOB_LAYER
/// The color the knife glows around it.
var/glow_color = "#ececff"
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index 0d61bcc7b46..6c0def3f67d 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -169,7 +169,7 @@
if(!soon_to_be_ghoul.mind || !soon_to_be_ghoul.client)
message_admins("[ADMIN_LOOKUPFLW(user)] is creating a voiceless dead of a body with no player.")
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", ROLE_HERETIC, ROLE_HERETIC, 5 SECONDS, soon_to_be_ghoul)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", check_jobban = ROLE_HERETIC, role = ROLE_HERETIC, poll_time = 5 SECONDS, target_mob = soon_to_be_ghoul, pic_source = soon_to_be_ghoul, role_name_text = "voiceless dead")
if(!LAZYLEN(candidates))
loc.balloon_alert(user, "ritual failed, no ghosts!")
return FALSE
diff --git a/code/modules/antagonists/heretic/knowledge/lock_lore.dm b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
index 6c6df4c6567..3550e287b6a 100644
--- a/code/modules/antagonists/heretic/knowledge/lock_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
@@ -199,8 +199,7 @@
for(var/mob/living/carbon/human/body in atoms)
if(body.stat != DEAD)
continue
- var/obj/item/bodypart/chest = body.get_bodypart(BODY_ZONE_CHEST)
- if(LAZYLEN(chest.get_organs()))
+ if(LAZYLEN(body.get_organs_for_zone(BODY_ZONE_CHEST)))
to_chat(user, span_hierophant_warning("[body] has organs in their chest."))
continue
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
index fbf582c178e..3ad11b1562b 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
@@ -130,7 +130,7 @@
// First target, any command.
for(var/datum/mind/head_mind as anything in shuffle(valid_targets))
- if(head_mind.assigned_role?.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(head_mind.assigned_role?.job_flags & JOB_HEAD_OF_STAFF)
final_targets += head_mind
valid_targets -= head_mind
break
@@ -187,8 +187,8 @@
heretic_datum.remove_sacrifice_target(sacrifice)
var/feedback = "Your patrons accept your offer"
- var/sac_department_flag = sacrifice.mind?.assigned_role?.departments_bitflags | sacrifice.last_mind?.assigned_role?.departments_bitflags
- if(sac_department_flag & DEPARTMENT_BITFLAG_COMMAND)
+ var/sac_job_flag = sacrifice.mind?.assigned_role?.job_flags | sacrifice.last_mind?.assigned_role?.job_flags
+ if(sac_job_flag & JOB_HEAD_OF_STAFF)
heretic_datum.knowledge_points++
heretic_datum.high_value_sacrifices++
feedback += " graciously"
diff --git a/code/modules/antagonists/heretic/structures/lock_final.dm b/code/modules/antagonists/heretic/structures/lock_final.dm
index c8232062931..8cb6c06f3cb 100644
--- a/code/modules/antagonists/heretic/structures/lock_final.dm
+++ b/code/modules/antagonists/heretic/structures/lock_final.dm
@@ -37,7 +37,7 @@
/// Ask ghosts if they want to make some noise
/obj/structure/lock_tear/proc/poll_ghosts()
- var/list/candidates = poll_ghost_candidates("Would you like to be a random eldritch monster attacking the crew?", ROLE_SENTIENCE, ROLE_SENTIENCE, 10 SECONDS, POLL_IGNORE_HERETIC_MONSTER)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Would you like to be a random eldritch monster attacking the crew?", check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_HERETIC_MONSTER, pic_source = src, role_name_text = "eldritch monster")
while(LAZYLEN(candidates))
var/mob/dead/observer/candidate = pick_n_take(candidates)
ghost_to_monster(candidate, should_ask = FALSE)
diff --git a/code/modules/antagonists/heretic/transmutation_rune.dm b/code/modules/antagonists/heretic/transmutation_rune.dm
index f625e809432..92f5334a076 100644
--- a/code/modules/antagonists/heretic/transmutation_rune.dm
+++ b/code/modules/antagonists/heretic/transmutation_rune.dm
@@ -80,6 +80,10 @@
for(var/atom/close_atom as anything in range(1, src))
if(!ismovable(close_atom))
continue
+ if(isitem(close_atom))
+ var/obj/item/close_item = close_atom
+ if(close_item.item_flags & ABSTRACT) //woops sacrificed your own head
+ continue
if(close_atom.invisibility)
continue
if(close_atom == user)
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index 98659ef1941..077bd2158b8 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -52,9 +52,8 @@
if(!istype(H))
return
- for(var/obj/item/I in H)
- if(!H.dropItemToGround(I))
- qdel(I)
+ H.drop_everything(del_on_drop = FALSE, force = TRUE, del_if_nodrop = TRUE)
+
H.regenerate_icons()
H.revive(ADMIN_HEAL_ALL)
H.equip_to_slot_or_del(new /obj/item/clothing/under/costume/kilt/highlander(H), ITEM_SLOT_ICLOTHING)
diff --git a/code/modules/antagonists/hypnotized/hypnotized.dm b/code/modules/antagonists/hypnotized/hypnotized.dm
index 2ee17b671aa..4f1f49aa3be 100644
--- a/code/modules/antagonists/hypnotized/hypnotized.dm
+++ b/code/modules/antagonists/hypnotized/hypnotized.dm
@@ -6,7 +6,7 @@
antag_hud_name = "brainwashed"
ui_name = "AntagInfoBrainwashed"
show_in_antagpanel = TRUE
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index 1a93a1f4af6..fc17b6924df 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -891,7 +891,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/datum/ai_module/upgrade/mecha_domination
name = "Unlock Mech Domination"
description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. \
- Once uploaded to the mech, it is impossible to leave. Do not allow the mech to leave the station's vicinity or allow it to be destroyed. \
+ Do not allow the mech to leave the station's vicinity or allow it to be destroyed. \
Upgrade is done immediately upon purchase."
cost = 30
upgrade = TRUE
diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm
index ce5ff427a21..c06f1e322a2 100644
--- a/code/modules/antagonists/nightmare/nightmare_organs.dm
+++ b/code/modules/antagonists/nightmare/nightmare_organs.dm
@@ -9,13 +9,15 @@
desc = "A fleshy growth that was dug out of the skull of a Nightmare."
icon = 'icons/obj/medical/organs/organs.dmi'
icon_state = "brain-x-d"
+ applied_status = /datum/status_effect/shadow_regeneration/nightmare
///Our associated shadow jaunt spell, for all nightmares
var/datum/action/cooldown/spell/jaunt/shadow_walk/our_jaunt
///Our associated terrorize spell, for antagonist nightmares
var/datum/action/cooldown/spell/pointed/terrorize/terrorize_spell
-/obj/item/organ/internal/brain/shadow/nightmare/on_insert(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/shadow/nightmare/on_mob_insert(mob/living/carbon/brain_owner)
. = ..()
+
if(brain_owner.dna.species.id != SPECIES_NIGHTMARE)
brain_owner.set_species(/datum/species/shadow/nightmare)
visible_message(span_warning("[brain_owner] thrashes as [src] takes root in [brain_owner.p_their()] body!"))
@@ -27,19 +29,32 @@
terrorize_spell = new(src)
terrorize_spell.Grant(brain_owner)
- RegisterSignal(brain_owner, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(dodge_bullets))
-
-/obj/item/organ/internal/brain/shadow/nightmare/on_remove(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/shadow/nightmare/on_mob_remove(mob/living/carbon/brain_owner)
. = ..()
QDEL_NULL(our_jaunt)
QDEL_NULL(terrorize_spell)
- UnregisterSignal(brain_owner, COMSIG_ATOM_PRE_BULLET_ACT)
-/obj/item/organ/internal/brain/shadow/nightmare/proc/dodge_bullets(mob/living/carbon/human/source, obj/projectile/hitting_projectile, def_zone)
+/atom/movable/screen/alert/status_effect/shadow_regeneration/nightmare
+ name = "Lightless Domain"
+ desc = "Bathed in soothing darkness you will slowly regenerate, even past the point of death. \
+ Heightened reflexes will allow you to dodge projectile weapons."
+
+/datum/status_effect/shadow_regeneration/nightmare
+ alert_type = /atom/movable/screen/alert/status_effect/shadow_regeneration/nightmare
+
+/datum/status_effect/shadow_regeneration/nightmare/on_apply()
+ . = ..()
+ if (!.)
+ return FALSE
+ RegisterSignal(owner, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(dodge_bullets))
+ return TRUE
+
+/datum/status_effect/shadow_regeneration/nightmare/on_remove()
+ UnregisterSignal(owner, COMSIG_ATOM_PRE_BULLET_ACT)
+ return ..()
+
+/datum/status_effect/shadow_regeneration/nightmare/proc/dodge_bullets(mob/living/carbon/human/source, obj/projectile/hitting_projectile, def_zone)
SIGNAL_HANDLER
- var/turf/dodge_turf = source.loc
- if(!istype(dodge_turf) || dodge_turf.get_lumcount() >= SHADOW_SPECIES_LIGHT_THRESHOLD)
- return NONE
source.visible_message(
span_danger("[source] dances in the shadows, evading [hitting_projectile]!"),
span_danger("You evade [hitting_projectile] with the cover of darkness!"),
@@ -76,13 +91,13 @@
user.temporarilyRemoveItemFromInventory(src, TRUE)
Insert(user)
-/obj/item/organ/internal/heart/nightmare/on_insert(mob/living/carbon/heart_owner, special)
+/obj/item/organ/internal/heart/nightmare/on_mob_insert(mob/living/carbon/heart_owner, special)
. = ..()
if(special != HEART_SPECIAL_SHADOWIFY)
blade = new/obj/item/light_eater
heart_owner.put_in_hands(blade)
-/obj/item/organ/internal/heart/nightmare/on_remove(mob/living/carbon/heart_owner, special)
+/obj/item/organ/internal/heart/nightmare/on_mob_remove(mob/living/carbon/heart_owner, special)
. = ..()
respawn_progress = 0
if(blade && special != HEART_SPECIAL_SHADOWIFY)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
index 0c4422c6213..190b9bcfe34 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
@@ -524,7 +524,7 @@ GLOBAL_VAR(station_nuke_source)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NUKE_DEVICE_DETONATING, src)
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
SSticker.roundend_check_paused = TRUE
addtimer(CALLBACK(src, PROC_REF(actually_explode)), 10 SECONDS)
return TRUE
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 7690ab19efd..68fe9b24778 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -569,12 +569,14 @@
var/tc_to_spawn = tgui_input_number(admin, "How much TC to spawn with?", "TC", 0, 100)
- var/list/nuke_candidates = poll_ghost_candidates(
+ var/list/nuke_candidates = SSpolling.poll_ghost_candidates(
"Do you want to play as an emergency syndicate reinforcement?",
- ROLE_OPERATIVE,
- ROLE_OPERATIVE,
- 30 SECONDS,
- POLL_IGNORE_SYNDICATE,
+ check_jobban = ROLE_OPERATIVE,
+ role = ROLE_OPERATIVE,
+ poll_time = 30 SECONDS,
+ ignore_category = POLL_IGNORE_SYNDICATE,
+ pic_source = /obj/structure/sign/poster/contraband/gorlex_recruitment,
+ role_name_text = "syndicate reinforcement",
)
nuke_candidates -= admin // may be easy to fat-finger say yes. so just don't
diff --git a/code/modules/antagonists/obsessed/obsessed.dm b/code/modules/antagonists/obsessed/obsessed.dm
index 8f51fb96646..35661bc9327 100644
--- a/code/modules/antagonists/obsessed/obsessed.dm
+++ b/code/modules/antagonists/obsessed/obsessed.dm
@@ -1,8 +1,9 @@
/datum/antagonist/obsessed
name = "Obsessed"
show_in_antagpanel = TRUE
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
job_rank = ROLE_OBSESSED
+ show_to_ghosts = TRUE
antag_hud_name = "obsessed"
show_name_in_check_antagonists = TRUE
roundend_category = "obsessed"
diff --git a/code/modules/antagonists/pirate/pirate_event.dm b/code/modules/antagonists/pirate/pirate_event.dm
index fd1dcc84034..bcee6f15675 100644
--- a/code/modules/antagonists/pirate/pirate_event.dm
+++ b/code/modules/antagonists/pirate/pirate_event.dm
@@ -66,7 +66,7 @@
if(chosen_gang.paid_off)
return
- var/list/candidates = poll_ghost_candidates("Do you wish to be considered for a pirate crew of [chosen_gang.name]?", ROLE_TRAITOR)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a pirate crew of [chosen_gang.name]?", check_jobban = ROLE_TRAITOR, pic_source = /obj/item/claymore/cutlass, role_name_text = "pirate crew")
shuffle_inplace(candidates)
var/template_key = "pirate_[chosen_gang.ship_template_id]"
diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
index 7521ff15549..3696b4ba976 100644
--- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
+++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
@@ -396,7 +396,7 @@
return 0
else if(FACTION_PIRATE in ransomee.faction) //can't ransom your fellow pirates to CentCom!
return 0
- else if(ransomee.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ else if(HAS_TRAIT(ransomee, TRAIT_HIGH_VALUE_RANSOM))
return 3000
else
return 1000
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index 6ee5ba5dcc7..708f94c3fc3 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -12,7 +12,7 @@
var/deconversion_source
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
- if(new_owner.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(new_owner.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
return FALSE
if(new_owner.unconvertable)
return FALSE
diff --git a/code/modules/antagonists/traitor/contractor/contract_teammate.dm b/code/modules/antagonists/traitor/contractor/contract_teammate.dm
new file mode 100644
index 00000000000..1aebd9fb951
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/contract_teammate.dm
@@ -0,0 +1,72 @@
+///Spawns a contractor partner to a spawning user, with a given key to assign to the new player.
+/proc/spawn_contractor_partner(mob/living/user, key)
+ var/mob/living/carbon/human/partner = new()
+ var/datum/outfit/contractor_partner/partner_outfit = new()
+
+ partner_outfit.equip(partner)
+
+ var/obj/structure/closet/supplypod/arrival_pod = new(null, STYLE_SYNDICATE)
+ arrival_pod.explosionSize = list(0,0,0,1)
+ arrival_pod.bluespace = TRUE
+
+ var/turf/free_location = find_obstruction_free_location(2, user)
+
+ // We really want to send them - if we can't find a nice location just land it on top of them.
+ if (!free_location)
+ free_location = get_turf(user)
+
+ partner.forceMove(arrival_pod)
+ partner.ckey = key
+
+ /// We give a reference to the mind that'll be the support unit
+ var/datum/antagonist/traitor/contractor_support/new_datum = partner.mind.add_antag_datum(/datum/antagonist/traitor/contractor_support)
+
+ to_chat(partner, "\n[span_alertwarning("[user.real_name] is your superior. Follow any, and all orders given by them. You're here to support their mission only.")]")
+ to_chat(partner, "[span_alertwarning("Should they perish, or be otherwise unavailable, you're to assist other active agents in this mission area to the best of your ability.")]")
+
+ new /obj/effect/pod_landingzone(free_location, arrival_pod)
+ return new_datum
+
+/// Support unit gets it's own very basic antag datum for admin logging.
+/datum/antagonist/traitor/contractor_support
+ name = "Contractor Support Unit"
+ show_in_roundend = FALSE
+ give_objectives = TRUE
+ give_uplink = FALSE
+
+/datum/antagonist/traitor/contractor_support/forge_traitor_objectives()
+ var/datum/objective/generic_objective = new
+ generic_objective.name = "Follow Contractor's Orders"
+ generic_objective.explanation_text = "Follow your orders. Assist agents in this mission area."
+ generic_objective.completed = TRUE
+ objectives += generic_objective
+
+/datum/antagonist/traitor/contractor_support/forge_ending_objective()
+ return
+
+/datum/outfit/contractor_partner
+ name = "Contractor Support Unit"
+
+ uniform = /obj/item/clothing/under/chameleon
+ suit = /obj/item/clothing/suit/chameleon
+ back = /obj/item/storage/backpack
+ belt = /obj/item/modular_computer/pda/chameleon
+ mask = /obj/item/clothing/mask/cigarette/syndicate
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
+ ears = /obj/item/radio/headset/chameleon
+ id = /obj/item/card/id/advanced/chameleon
+ r_hand = /obj/item/storage/toolbox/syndicate
+ id_trim = /datum/id_trim/chameleon/operative
+
+ backpack_contents = list(
+ /obj/item/storage/box/survival,
+ /obj/item/implanter/uplink,
+ /obj/item/clothing/mask/chameleon,
+ /obj/item/storage/fancy/cigarettes/cigpack_syndicate,
+ /obj/item/lighter,
+ )
+
+/datum/outfit/contractor_partner/post_equip(mob/living/carbon/human/H, visualsOnly)
+ . = ..()
+ var/obj/item/clothing/mask/cigarette/syndicate/cig = H.get_item_by_slot(ITEM_SLOT_MASK)
+ cig.light()
diff --git a/code/modules/antagonists/traitor/contractor/contractor_hub.dm b/code/modules/antagonists/traitor/contractor/contractor_hub.dm
new file mode 100644
index 00000000000..28a9b8e0017
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/contractor_hub.dm
@@ -0,0 +1,69 @@
+/datum/contractor_hub
+ ///The current contract in progress, and can be null if no contract is in progress.
+ var/datum/syndicate_contract/current_contract
+ ///List of all available syndicate contracts that can be taken.
+ var/list/datum/syndicate_contract/assigned_contracts = list()
+
+ ///Reference to a contractor teammate, if one has been purchased.
+ var/datum/antagonist/traitor/contractor_support/contractor_teammate
+
+ ///List of all people currently used as targets, to not roll doubles.
+ var/list/assigned_targets = list()
+
+ ///Amount of contracts that have already been completed, for flavor in the UI & round-end logs.
+ var/contracts_completed = 0
+ ///How much TC has been paid out, for flavor in the UI & round-end logs.
+ var/contract_TC_payed_out = 0
+ ///How much TC we can cash out currently. Used when redeeming TC and for round-end logs.
+ var/contract_TC_to_redeem = 0
+
+/datum/contractor_hub/proc/create_contracts(datum/mind/owner)
+ // 6 initial contracts
+ var/list/to_generate = list(
+ CONTRACT_PAYOUT_LARGE,
+ CONTRACT_PAYOUT_MEDIUM,
+ CONTRACT_PAYOUT_SMALL,
+ CONTRACT_PAYOUT_SMALL,
+ CONTRACT_PAYOUT_SMALL,
+ CONTRACT_PAYOUT_SMALL
+ )
+
+ //What the fuck
+ if(length(to_generate) > length(GLOB.manifest.locked))
+ to_generate.Cut(1, length(GLOB.manifest.locked))
+
+ // We don't want the sum of all the payouts to be under this amount
+ var/lowest_TC_threshold = 30
+
+ var/total = 0
+ var/lowest_paying_sum = 0
+ var/datum/syndicate_contract/lowest_paying_contract
+
+ // Randomise order, so we don't have contracts always in payout order.
+ to_generate = shuffle(to_generate)
+
+ // Support contract generation happening multiple times
+ var/start_index = 1
+ if (assigned_contracts.len != 0)
+ start_index = assigned_contracts.len + 1
+
+ // Generate contracts, and find the lowest paying.
+ for(var/i in 1 to to_generate.len)
+ var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, to_generate[i])
+ var/contract_payout_total = contract_to_add.contract.payout + contract_to_add.contract.payout_bonus
+
+ assigned_targets.Add(contract_to_add.contract.target)
+
+ if (!lowest_paying_contract || (contract_payout_total < lowest_paying_sum))
+ lowest_paying_sum = contract_payout_total
+ lowest_paying_contract = contract_to_add
+
+ total += contract_payout_total
+ contract_to_add.id = start_index
+ assigned_contracts.Add(contract_to_add)
+
+ start_index++
+
+ // If the threshold for TC payouts isn't reached, boost the lowest paying contract
+ if (total < lowest_TC_threshold)
+ lowest_paying_contract.contract.payout_bonus += (lowest_TC_threshold - total)
diff --git a/code/modules/antagonists/traitor/contractor/contractor_items.dm b/code/modules/antagonists/traitor/contractor/contractor_items.dm
new file mode 100644
index 00000000000..90c495358f2
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/contractor_items.dm
@@ -0,0 +1,53 @@
+/obj/item/pinpointer/crew/contractor
+ name = "contractor pinpointer"
+ desc = "A handheld tracking device that locks onto certain signals. Ignores suit sensors, but is much less accurate."
+ icon_state = "pinpointer_syndicate"
+ worn_icon_state = "pinpointer_black"
+ minimum_range = 25
+ has_owner = TRUE
+ ignore_suit_sensor_level = TRUE
+
+/obj/item/storage/box/contractor/fulton_extraction
+ name = "Fulton Extraction Kit"
+ icon_state = "syndiebox"
+ illustration = "writing_syndie"
+
+/obj/item/storage/box/contractor/fulton_extraction/PopulateContents()
+ new /obj/item/extraction_pack/syndicate(src)
+ new /obj/item/fulton_core(src)
+
+/obj/item/paper/contractor_guide
+ name = "Contractor Guide"
+ default_raw_text = {"Welcome agent, congratulations on your new position as contractor. On top of your already assigned objectives,\
+ this kit will provide you contracts to take on for TC payments.\
+
Provided within, we give your specialist contractor space suit. It's even more compact, being able to fit into a pocket, and faster than the\
+ Syndicate space suit available to you on the uplink. We also provide your chameleon jumpsuit and mask, both of which can be changed\
+ to any form you need for the moment. The cigarettes are a special blend - it'll heal your injuries slowly overtime.
\
+
Your standard issue contractor baton hits harder than the ones you might be used to, and likely be your go to weapon for kidnapping your\
+ targets. The three additional items have been randomly selected from what we had available. We hope they're useful to you for your mission.
\
+
The contractor hub, available at the top right of the uplink, will provide you unique items and abilities. These are bought using Contractor Rep,\
+ with two Rep being provided each time you complete a contract.
\
+
Using the tablet
\
+ \
+
Open the Syndicate Contract Uplink program.
\
+
Here, you can accept a contract, and redeem your TC payments from completed contracts.
\
+
The payment number shown in brackets is the bonus you'll receive when bringing your target alive. You receive the\
+ other number regardless of if they were alive or dead.
\
+
Contracts are completed by bringing the target to designated dropoff, calling for extraction, and putting them\
+ inside the pod.
\
+ \
+
Be careful when accepting a contract. While you'll be able to see the location of the dropoff point, cancelling will make it\
+ unavailable to take on again.
\
+
The tablet can also be recharged at any cell charger.
\
+
Extracting
\
+ \
+
Make sure both yourself and your target are at the dropoff.
\
+
Call the extraction, and stand back from the drop point.
\
+
If it fails, make sure your target is inside, and there's a free space for the pod to land.
\
+
Grab your target, and drag them into the pod.
\
+ \
+
Ransoms
\
+
We need your target for our own reasons, but we ransom them back to your mission area once their use is served. They will return back\
+ from where you sent them off from in several minutes time. Don't worry, we give you a cut of what we get paid. We pay this into whatever\
+ ID card you have equipped, on top of the TC payment we give.
\
+
Good luck agent. You can burn this document with the supplied lighter.
"}
diff --git a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
new file mode 100644
index 00000000000..3f7370031da
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
@@ -0,0 +1,271 @@
+/datum/syndicate_contract
+ ///The 'id' of this particular contract. Used to keep track of statuses from TGUI.
+ var/id
+ ///The current status of the contract. Starts off by default.
+ var/status = CONTRACT_STATUS_INACTIVE
+ ///The related Objective datum for the contract, holding the target and such.
+ var/datum/objective/contract/contract
+ ///The job position of the target.
+ var/target_rank
+ ///How much we will pay out upon completion. This is not the TC completion, it's typically credits.
+ var/ransom = 0
+ ///The level of payout, which affects the TC we get paid on completion.
+ var/payout_type
+ ///Flavortext wanted message for the person we're after.
+ var/wanted_message
+ ///List of everything found on the victim at the time of contracting, used to return their stuff afterwards.
+ var/list/victim_belongings = list()
+
+/datum/syndicate_contract/New(contract_owner, blacklist, type=CONTRACT_PAYOUT_SMALL)
+ contract = new(src)
+ contract.owner = contract_owner
+ payout_type = type
+
+ generate(blacklist)
+
+/datum/syndicate_contract/proc/generate(blacklist)
+ contract.find_target(null, blacklist)
+
+ var/datum/record/crew/record
+ if (contract.target)
+ record = find_record(contract.target.name)
+
+ if (record)
+ target_rank = record.rank
+ else
+ target_rank = "Unknown"
+
+ if (payout_type == CONTRACT_PAYOUT_LARGE)
+ contract.payout_bonus = rand(9,13)
+ else if (payout_type == CONTRACT_PAYOUT_MEDIUM)
+ contract.payout_bonus = rand(6,8)
+ else
+ contract.payout_bonus = rand(2,4)
+
+ contract.payout = rand(0, 2)
+ contract.generate_dropoff()
+
+ ransom = 100 * rand(18, 45)
+
+ var/base = pick_list(WANTED_FILE, "basemessage")
+ var/verb_string = pick_list(WANTED_FILE, "verb")
+ var/noun = pick_list_weighted(WANTED_FILE, "noun")
+ var/location = pick_list_weighted(WANTED_FILE, "location")
+ wanted_message = "[base] [verb_string] [noun] [location]."
+
+/datum/syndicate_contract/proc/handle_extraction(mob/living/user)
+ if (contract.target && contract.dropoff_check(user, contract.target.current))
+
+ var/turf/free_location = find_obstruction_free_location(3, user, contract.dropoff)
+
+ if (free_location)
+ // We've got a valid location, launch.
+ launch_extraction_pod(free_location)
+ return TRUE
+
+ return FALSE
+
+// Launch the pod to collect our victim.
+/datum/syndicate_contract/proc/launch_extraction_pod(turf/empty_pod_turf)
+ var/obj/structure/closet/supplypod/extractionpod/empty_pod = new()
+
+ RegisterSignal(empty_pod, COMSIG_ATOM_ENTERED, PROC_REF(enter_check))
+
+ empty_pod.stay_after_drop = TRUE
+ empty_pod.reversing = TRUE
+ empty_pod.explosionSize = list(0,0,0,1)
+ empty_pod.leavingSound = 'sound/effects/podwoosh.ogg'
+
+ new /obj/effect/pod_landingzone(empty_pod_turf, empty_pod)
+
+/datum/syndicate_contract/proc/enter_check(datum/source, sent_mob)
+ SIGNAL_HANDLER
+
+ if(!istype(source, /obj/structure/closet/supplypod/extractionpod))
+ return
+ if(!isliving(sent_mob))
+ return
+ var/mob/living/person_sent = sent_mob
+ var/datum/antagonist/traitor/traitor_data = contract.owner.has_antag_datum(/datum/antagonist/traitor)
+ if(person_sent == contract.target.current)
+ traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem += contract.payout
+ traitor_data.uplink_handler.contractor_hub.contracts_completed++
+ if(person_sent.stat != DEAD)
+ traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem += contract.payout_bonus
+ status = CONTRACT_STATUS_COMPLETE
+ if(traitor_data.uplink_handler.contractor_hub.current_contract == src)
+ traitor_data.uplink_handler.contractor_hub.current_contract = null
+ else
+ status = CONTRACT_STATUS_ABORTED // Sending a target that wasn't even yours is as good as just aborting it
+ if(traitor_data.uplink_handler.contractor_hub.current_contract == src)
+ traitor_data.uplink_handler.contractor_hub.current_contract = null
+
+ if(iscarbon(person_sent))
+ for(var/obj/item/person_contents in person_sent.gather_belongings())
+ if(ishuman(person_sent))
+ var/mob/living/carbon/human/human_sent = person_sent
+ if(person_contents == human_sent.w_uniform)
+ continue //So all they're left with are shoes and uniform.
+ if(person_contents == human_sent.shoes)
+ continue
+ person_sent.transferItemToLoc(person_contents)
+ victim_belongings.Add(WEAKREF(person_contents))
+
+ var/obj/structure/closet/supplypod/extractionpod/pod = source
+ // Handle the pod returning
+ pod.startExitSequence(pod)
+
+ if(ishuman(person_sent))
+ var/mob/living/carbon/human/target = person_sent
+ // After we remove items, at least give them what they need to live.
+ target.dna.species.give_important_for_life(target)
+
+ //we'll start the effects in a few seconds since it takes a moment for the pod to leave.
+ addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), person_sent), 3 SECONDS)
+
+ // This is slightly delayed because of the sleep calls above to handle the narrative.
+ // We don't want to tell the station instantly.
+ var/points_to_check
+ var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(cargo_account)
+ points_to_check = cargo_account.account_balance
+ if(points_to_check >= ransom)
+ cargo_account.adjust_money(-ransom)
+ else
+ cargo_account.adjust_money(-points_to_check)
+ priority_announce(
+ text = "One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \
+ As is policy we've taken a portion of the station's funds to offset the overall cost.",
+ sender_override = "Nanotrasen Asset Protection")
+
+ addtimer(CALLBACK(src, PROC_REF(finish_enter)), 3 SECONDS)
+
+/datum/syndicate_contract/proc/finish_enter()
+ // Pay contractor their portion of ransom
+ if(status != CONTRACT_STATUS_COMPLETE)
+ return
+ var/obj/item/card/id/contractor_id = contract.owner.current?.get_idcard(TRUE)
+ if(!contractor_id || !contractor_id.registered_account)
+ return
+ contractor_id.registered_account.adjust_money(ransom * 0.35)
+ contractor_id.registered_account.bank_card_talk("We've processed the ransom, agent. \
+ Here's your cut - your balance is now [contractor_id.registered_account.account_balance] cr.", TRUE)
+
+#define VICTIM_EXPERIENCE_START 0
+#define VICTIM_EXPERIENCE_FIRST_HIT 1
+#define VICTIM_EXPERIENCE_SECOND_HIT 2
+#define VICTIM_EXPERIENCE_THIRD_HIT 3
+#define VICTIM_EXPERIENCE_LAST_HIT 4
+
+/**
+ * handle_victim_experience
+ *
+ * Handles the effects given to victims upon being contracted.
+ * We heal them up and cause them immersive effects, just for fun.
+ * Args:
+ * victim - The person we're harassing
+ * level - The current stage of harassement they are facing. This increases by itself, looping until finished.
+ */
+/datum/syndicate_contract/proc/handle_victim_experience(mob/living/victim, level = VICTIM_EXPERIENCE_START)
+ // Ship 'em back - dead or alive, 4 minutes wait.
+ // Even if they weren't the target, we're still treating them the same.
+ if(!level)
+ addtimer(CALLBACK(src, PROC_REF(return_victim), victim), (60 * 10) * 4)
+ if(victim.stat == DEAD)
+ return
+
+ var/time_until_next
+ switch(level)
+ if(VICTIM_EXPERIENCE_START)
+ // Heal them up - gets them out of crit/soft crit. If omnizine is removed in the future, this needs to be replaced with a
+ // method of healing them, consequence free, to a reasonable amount of health.
+ victim.reagents.add_reagent(/datum/reagent/medicine/omnizine, amount = 20)
+ victim.flash_act()
+ victim.adjust_confusion(1 SECONDS)
+ victim.adjust_eye_blur(5 SECONDS)
+ to_chat(victim, span_warning("You feel strange..."))
+ time_until_next = 6 SECONDS
+ if(VICTIM_EXPERIENCE_FIRST_HIT)
+ to_chat(victim, span_warning("That pod did something to you..."))
+ victim.adjust_dizzy(3.5 SECONDS)
+ time_until_next = 6.5 SECONDS
+ if(VICTIM_EXPERIENCE_SECOND_HIT)
+ to_chat(victim, span_warning("Your head pounds... It feels like it's going to burst out your skull!"))
+ victim.flash_act()
+ victim.adjust_confusion(2 SECONDS)
+ victim.adjust_eye_blur(3 SECONDS)
+ time_until_next = 3 SECONDS
+ if(VICTIM_EXPERIENCE_THIRD_HIT)
+ to_chat(victim, span_warning("Your head pounds..."))
+ time_until_next = 10 SECONDS
+ if(VICTIM_EXPERIENCE_LAST_HIT)
+ victim.flash_act()
+ victim.Unconscious(200)
+ to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Your mind held many valuable secrets - \
+ we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
+ so it's only a matter of time before we ship you back...\""))
+ victim.adjust_eye_blur(10 SECONDS)
+ victim.adjust_dizzy(1.5 SECONDS)
+ victim.adjust_confusion(2 SECONDS)
+
+ level++ //move onto the next level.
+ if(time_until_next)
+ addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), victim, level), time_until_next)
+
+#undef VICTIM_EXPERIENCE_START
+#undef VICTIM_EXPERIENCE_FIRST_HIT
+#undef VICTIM_EXPERIENCE_SECOND_HIT
+#undef VICTIM_EXPERIENCE_THIRD_HIT
+#undef VICTIM_EXPERIENCE_LAST_HIT
+
+// We're returning the victim
+/datum/syndicate_contract/proc/return_victim(mob/living/victim)
+ var/list/possible_drop_loc = list()
+
+ for(var/turf/possible_drop in contract.dropoff.contents)
+ if(!isspaceturf(possible_drop) && !isclosedturf(possible_drop))
+ if(!possible_drop.is_blocked_turf())
+ possible_drop_loc.Add(possible_drop)
+
+ if(!possible_drop_loc.len)
+ to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Seems where you got sent here from won't \
+ be able to handle our pod... You will die here instead.\""))
+ if(iscarbon(victim))
+ var/mob/living/carbon/carbon_victim = victim
+ if(carbon_victim.can_heartattack())
+ carbon_victim.set_heartattack(TRUE)
+ return
+
+ var/pod_rand_loc = rand(1, possible_drop_loc.len)
+ var/obj/structure/closet/supplypod/return_pod = new()
+ return_pod.bluespace = TRUE
+ return_pod.explosionSize = list(0,0,0,0)
+ return_pod.style = STYLE_SYNDICATE
+
+ do_sparks(8, FALSE, victim)
+ victim.visible_message(span_notice("[victim] vanishes..."))
+
+ for(var/datum/weakref/belonging_ref in victim_belongings)
+ var/obj/item/belonging = belonging_ref.resolve()
+ if(!belonging)
+ continue
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ //So all they're left with are shoes and uniform.
+ if(belonging == human_victim.w_uniform)
+ continue
+ if(belonging == human_victim.shoes)
+ continue
+ belonging.forceMove(return_pod)
+
+ for(var/obj/item/W in victim_belongings)
+ W.forceMove(return_pod)
+
+ victim.forceMove(return_pod)
+
+ victim.flash_act()
+ victim.adjust_eye_blur(3 SECONDS)
+ victim.adjust_dizzy(3.5 SECONDS)
+ victim.adjust_confusion(2 SECONDS)
+
+ new /obj/effect/pod_landingzone(possible_drop_loc[pod_rand_loc], return_pod)
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 31eedd09cce..d338abca641 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -17,6 +17,10 @@
can_assign_self_objectives = TRUE
default_custom_objective = "Perform an overcomplicated heist on valuable Nanotrasen assets."
hardcore_random_bonus = TRUE
+
+ ///The flag of uplink that this traitor is supposed to have.
+ var/uplink_flag_given = UPLINK_TRAITORS
+
var/give_objectives = TRUE
/// Whether to give secondary objectives to the traitor, which aren't necessary but can be completed for a progression and TC boost.
var/give_secondary_objectives = TRUE
@@ -53,6 +57,7 @@
// There will still be a timelock on uplink items
name = "\improper Infiltrator"
give_secondary_objectives = FALSE
+ uplink_flag_given = UPLINK_TRAITORS | UPLINK_INFILTRATORS
/datum/antagonist/traitor/infiltrator/sleeper_agent
name = "\improper Syndicate Sleeper Agent"
@@ -75,6 +80,7 @@
uplink.uplink_handler = uplink_handler
else
uplink_handler = uplink.uplink_handler
+ uplink_handler.uplink_flag = uplink_flag_given
uplink_handler.primary_objectives = objectives
uplink_handler.has_progression = TRUE
SStraitor.register_uplink_handler(uplink_handler)
@@ -351,6 +357,8 @@
result += objectives_text
if(uplink_handler)
+ if (uplink_handler.contractor_hub)
+ result += contractor_round_end()
result += " The traitor had a total of [DISPLAY_PROGRESSION(uplink_handler.progression_points)] Reputation and [uplink_handler.telecrystals] Unused Telecrystals."
var/special_role_text = lowertext(name)
@@ -363,6 +371,23 @@
return result.Join(" ")
+///Tells how many contracts have been completed.
+/datum/antagonist/traitor/proc/contractor_round_end()
+ var/completed_contracts = uplink_handler.contractor_hub.contracts_completed
+ var/tc_total = uplink_handler.contractor_hub.contract_TC_payed_out + uplink_handler.contractor_hub.contract_TC_to_redeem
+
+ var/datum/antagonist/traitor/contractor_support/contractor_support_unit = uplink_handler.contractor_hub.contractor_teammate
+
+ if(completed_contracts <= 0)
+ return
+ var/plural_check = "contract"
+ if (completed_contracts > 1)
+ plural_check = "contracts"
+ var/sent_data = "Completed [span_greentext("[completed_contracts]")] [plural_check] for a total of [span_greentext("[tc_total] TC")]! "
+ if(contractor_support_unit)
+ sent_data += "[contractor_support_unit.owner.key] played [contractor_support_unit.owner.current.name], their contractor support unit. "
+ return sent_data
+
/datum/antagonist/traitor/roundend_report_footer()
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
diff --git a/code/modules/antagonists/traitor/objectives/assassination.dm b/code/modules/antagonists/traitor/objectives/assassination.dm
index f3b76874213..9fae7ed3352 100644
--- a/code/modules/antagonists/traitor/objectives/assassination.dm
+++ b/code/modules/antagonists/traitor/objectives/assassination.dm
@@ -201,10 +201,10 @@
continue
//removes heads of staff from being targets from non heads of staff assassinations, and vice versa
if(heads_of_staff)
- if(!(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
else
- if((possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if((possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
possible_targets += possible_target
for(var/datum/traitor_objective/target_player/objective as anything in possible_duplicates)
diff --git a/code/modules/antagonists/traitor/objectives/eyesnatching.dm b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
index 5cf95e90384..5773c4e90e0 100644
--- a/code/modules/antagonists/traitor/objectives/eyesnatching.dm
+++ b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
@@ -74,10 +74,10 @@
continue
if(heads_of_staff)
- if(!(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
else
- if(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
continue
var/mob/living/carbon/human/targets_current = possible_target.current
diff --git a/code/modules/antagonists/traitor/objectives/infect.dm b/code/modules/antagonists/traitor/objectives/infect.dm
index 2671a615fa8..5f761a273fc 100644
--- a/code/modules/antagonists/traitor/objectives/infect.dm
+++ b/code/modules/antagonists/traitor/objectives/infect.dm
@@ -101,10 +101,10 @@
continue
//removes heads of staff from being targets from non heads of staff assassinations, and vice versa
if(heads_of_staff)
- if(!(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
else
- if((possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if((possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
possible_targets += possible_target
for(var/datum/traitor_objective/target_player/objective as anything in possible_duplicates)
diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm
index d74b586efae..732230f9248 100644
--- a/code/modules/antagonists/traitor/objectives/kidnapping.dm
+++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm
@@ -227,7 +227,7 @@
if(sent_mob.mind)
ADD_TRAIT(sent_mob.mind, TRAIT_HAS_BEEN_KIDNAPPED, TRAIT_GENERIC)
- for(var/obj/item/belonging in gather_belongings(sent_mob))
+ for(var/obj/item/belonging in sent_mob.gather_belongings())
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
continue
@@ -298,7 +298,7 @@
do_sparks(8, FALSE, sent_mob)
sent_mob.visible_message(span_notice("[sent_mob] vanishes!"))
- for(var/obj/item/belonging in gather_belongings(sent_mob))
+ for(var/obj/item/belonging in sent_mob.gather_belongings())
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
continue
sent_mob.dropItemToGround(belonging) // No souvenirs, except shoes and t-shirts
@@ -317,10 +317,3 @@
sent_mob.dna.species.give_important_for_life(sent_mob) // so plasmamen do not get left for dead
new /obj/effect/pod_landingzone(pick(possible_turfs), return_pod)
-
-/// Returns a list of things that the provided mob has which we would rather that they do not have
-/datum/traitor_objective/target_player/kidnapping/proc/gather_belongings(mob/living/carbon/human/kidnapee)
- var/list/belongings = kidnapee.get_all_gear()
- for (var/obj/item/implant/storage/internal_bag in kidnapee.implants)
- belongings += internal_bag.contents
- return belongings
diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm
index 06ce9667035..5d164d8d3d9 100644
--- a/code/modules/antagonists/traitor/uplink_handler.dm
+++ b/code/modules/antagonists/traitor/uplink_handler.dm
@@ -52,6 +52,8 @@
var/datum/callback/can_replace_objectives
/// Callback which performs that operation
var/datum/callback/replace_objectives
+ ///Reference to a contractor hub that the infiltrator can run, if they purchase it.
+ var/datum/contractor_hub/contractor_hub
/datum/uplink_handler/New()
. = ..()
@@ -73,6 +75,8 @@
/// Checks for uplink flags as well as items restricted to roles and species
/datum/uplink_handler/proc/check_if_restricted(datum/uplink_item/to_purchase)
+ if(!to_purchase.can_be_bought(src))
+ return FALSE
if((to_purchase in extra_purchasable))
return TRUE
if(!(to_purchase.purchasable_from & uplink_flag))
@@ -99,7 +103,7 @@
return FALSE
var/current_stock = item_stock[to_purchase.stock_key]
- var/stock = current_stock != null? current_stock : INFINITY
+ var/stock = current_stock != null ? current_stock : INFINITY
if(telecrystals < to_purchase.cost || stock <= 0 || not_enough_reputation(to_purchase))
return FALSE
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index 9b01b42410a..9ced62cb4f6 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -503,7 +503,7 @@
SM.Grant(newstruct)
newstruct.key = target.key
var/atom/movable/screen/alert/bloodsense/BS
- if(newstruct.mind && ((stoner && IS_CULTIST(stoner)) || cultoverride) && SSticker?.mode)
+ if(newstruct.mind && ((stoner && IS_CULTIST(stoner)) || cultoverride) && SSticker.HasRoundStarted())
newstruct.mind.add_antag_datum(/datum/antagonist/cult/construct)
if(IS_CULTIST(stoner) || cultoverride)
to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow [stoner ? stoner.p_their() : "their"] orders and help [stoner ? stoner.p_them() : "them"] complete [stoner ? stoner.p_their() : "their"] goals at all costs.")
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
index c31bb4ce2ce..fe5f69fd9fa 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
@@ -21,8 +21,7 @@
/datum/spellbook_entry/summon/guns/can_be_purchased()
// Summon Guns requires 98 threat.
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
// Also must be config enabled
return !CONFIG_GET(flag/no_summon_guns)
@@ -39,8 +38,7 @@
/datum/spellbook_entry/summon/magic/can_be_purchased()
// Summon Magic requires 98 threat.
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
// Also must be config enabled
return !CONFIG_GET(flag/no_summon_magic)
@@ -60,8 +58,7 @@
/datum/spellbook_entry/summon/events/can_be_purchased()
// Summon Events requires 98 threat.
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
// Also, must be config enabled
return !CONFIG_GET(flag/no_summon_events)
@@ -138,8 +135,7 @@
return ..()
/datum/spellbook_entry/summon/specific_spell/can_be_purchased()
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
if(GLOB.mass_teaching)
return FALSE
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
index 94b6254d459..a951a5daf42 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
@@ -51,8 +51,7 @@
if (DOOM_METEORS)
var/datum/dynamic_ruleset/roundstart/meteor/meteors = new()
meteors.meteordelay = 0
- var/datum/game_mode/dynamic/mode = SSticker.mode
- mode.execute_roundstart_rule(meteors) // Meteors will continue until morale is crushed.
+ SSdynamic.execute_roundstart_rule(meteors) // Meteors will continue until morale is crushed.
priority_announce("Meteors have been detected on collision course with the station.", "Meteor Alert", ANNOUNCER_METEORS)
#undef DOOM_SINGULARITY
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm
index b92ae4d2f20..7dc514f2e18 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm
@@ -71,7 +71,7 @@
name = "\improper Wizard Prank Victim"
roundend_category = "wizard prank victims"
show_in_antagpanel = FALSE
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
silent = TRUE
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 1e8df43cfbe..3efb007ab6e 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -262,6 +262,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
name = "Wizard Apprentice"
antag_hud_name = "apprentice"
can_assign_self_objectives = FALSE
+ move_to_lair = FALSE
var/datum/mind/master
var/school = APPRENTICE_DESTRUCTION
outfit_type = /datum/outfit/wizard/apprentice
diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm
index 717f4335969..2158104b6ee 100644
--- a/code/modules/art/statues.dm
+++ b/code/modules/art/statues.dm
@@ -569,7 +569,7 @@ Moving interrupts
content_ma.pixel_y = 0
content_ma.alpha = 255
- var/static/list/plane_whitelist = list(FLOAT_PLANE, GAME_PLANE, GAME_PLANE_UPPER, GAME_PLANE_FOV_HIDDEN, GAME_PLANE_UPPER, GAME_PLANE_UPPER_FOV_HIDDEN, FLOOR_PLANE)
+ var/static/list/plane_whitelist = list(FLOAT_PLANE, GAME_PLANE, FLOOR_PLANE)
/// Ideally we'd have knowledge what we're removing but i'd have to be done on target appearance retrieval
var/list/overlays_to_remove = list()
diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm
index c5a23c105d8..8512c253bb0 100644
--- a/code/modules/asset_cache/asset_list.dm
+++ b/code/modules/asset_cache/asset_list.dm
@@ -26,6 +26,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
/// Whether or not this asset can be cached across rounds of the same commit under the `CACHE_ASSETS` config.
/// This is not a *guarantee* the asset will be cached. Not all asset subtypes respect this field, and the
/// config can, of course, be disabled.
+ /// Disable this if your asset can change between rounds on the same exact version of the code.
var/cross_round_cachable = FALSE
/datum/asset/New()
@@ -133,6 +134,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/spritesheet
_abstract = /datum/asset/spritesheet
+ cross_round_cachable = TRUE
var/name
/// List of arguments to pass into queuedInsert
/// Exists so we can queue icon insertion, mostly for stuff like preferences
@@ -145,6 +147,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
/// If this asset should be fully loaded on new
/// Defaults to false so we can process this stuff nicely
var/load_immediately = FALSE
+ VAR_PRIVATE
+ // Kept in state so that the result is the same, even when the files are created, for this run
+ should_refresh = null
/datum/asset/spritesheet/proc/should_load_immediately()
#ifdef DO_NOT_DEFER_ASSETS
@@ -158,9 +163,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
if (..())
return TRUE
- // Static so that the result is the same, even when the files are created, for this run
- var/static/should_refresh = null
-
if (isnull(should_refresh))
// `fexists` seems to always fail on static-time
should_refresh = !fexists("[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css")
@@ -366,6 +368,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving)
if (!I || !length(icon_states(I))) // that direction or state doesn't exist
return
+
+ var/start_usage = world.tick_usage
+
//any sprite modifications we want to do (aka, coloring a greyscaled asset)
I = ModifyInserted(I)
var/size_id = "[I.Width()]x[I.Height()]"
@@ -393,6 +398,8 @@ GLOBAL_LIST_EMPTY(asset_datums)
sizes[size_id] = size = list(1, I, null)
sprites[sprite_name] = list(size_id, 0)
+ SSblackbox.record_feedback("tally", "spritesheet_queued_insert_time", TICK_USAGE_TO_MS(start_usage), name)
+
/**
* A simple proc handing the Icon for you to modify before it gets turned into an asset.
*
diff --git a/code/modules/asset_cache/assets/emojipedia.dm b/code/modules/asset_cache/assets/emojipedia.dm
index 2197f1bff22..200e3770ff4 100644
--- a/code/modules/asset_cache/assets/emojipedia.dm
+++ b/code/modules/asset_cache/assets/emojipedia.dm
@@ -1,7 +1,5 @@
/datum/asset/spritesheet/emojipedia
name = "emojipedia"
- cross_round_cachable = TRUE // The Emoji DMI is static and doesn't change without a commit mis-match.
/datum/asset/spritesheet/emojipedia/create_spritesheets()
InsertAll("", EMOJI_SET)
-
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
index b71dc05d776..8a9deaf5dea 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
@@ -10,7 +10,6 @@
name = "crystallizer"
desc = "Used to crystallize or solidify gases."
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
density = TRUE
max_integrity = 300
armor_type = /datum/armor/binary_crystallizer
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 22c0f509d16..0ae93c05831 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -17,7 +17,6 @@
// and will crop the head off.
icon_state = "mask_bg"
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
pixel_y = 22
appearance_flags = KEEP_TOGETHER
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index 7479b92c420..bef8eb7300d 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -36,10 +36,14 @@
var/shielding_powered = FALSE
///The powercell used to enable shielding
var/obj/item/stock_parts/cell/internal_cell
- ///Is the cell hatch opened
- var/cell_container_opened = FALSE
///used while processing to update appearance only when its pressure state changes
var/current_pressure_state
+ /// An assembly attached to the canister
+ var/obj/item/assembly_holder/rig = null
+ /// The person who attached an assembly to this canister, usually for ignition purposes in tandem with valve wire pulse
+ var/mob/last_rigger = ""
+ //overlay of attached assemblies
+ var/mutable_appearance/assemblies_overlay
/datum/armor/portable_atmospherics_canister
melee = 50
@@ -56,6 +60,8 @@
if(mapload)
internal_cell = new /obj/item/stock_parts/cell/high(src)
+ set_wires(new /datum/wires/canister(src))
+
if(existing_mixture)
air_contents.copy_from(existing_mixture)
else
@@ -73,6 +79,18 @@
AddElement(/datum/element/atmos_sensitive, mapload)
AddElement(/datum/element/volatile_gas_storage)
AddComponent(/datum/component/gas_leaker, leak_rate=0.01)
+ register_context()
+
+/obj/machinery/portable_atmospherics/canister/Destroy()
+ QDEL_NULL(wires)
+ return ..()
+
+/obj/machinery/portable_atmospherics/canister/Exited(atom/movable/gone)
+ if(gone == rig)
+ rig = null
+ last_rigger = null
+ cut_overlays(assemblies_overlay)
+ return ..()
/obj/machinery/portable_atmospherics/canister/interact(mob/user)
. = ..()
@@ -81,6 +99,30 @@
playsound(src, 'sound/misc/compiler-failure.ogg', 50, TRUE)
return
+/obj/machinery/portable_atmospherics/canister/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ if(rig)
+ context[SCREENTIP_CONTEXT_RMB] = "Remove rigged device"
+ if(holding)
+ context[SCREENTIP_CONTEXT_ALT_LMB] = "Remove tank"
+ if(!held_item)
+ return CONTEXTUAL_SCREENTIP_SET
+ if(istype(held_item, /obj/item/assembly_holder))
+ context[SCREENTIP_CONTEXT_LMB] = "Rig"
+ if(istype(held_item, /obj/item/stock_parts/cell))
+ context[SCREENTIP_CONTEXT_LMB] = "Insert cell"
+ switch(held_item.tool_behaviour)
+ if(TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] hatch"
+ if(TOOL_CROWBAR)
+ if(panel_open && internal_cell)
+ context[SCREENTIP_CONTEXT_LMB] = "Remove cell"
+ if(TOOL_WELDER)
+ context[SCREENTIP_CONTEXT_LMB] = "Repair"
+ context[SCREENTIP_CONTEXT_RMB] = "Dismantle"
+
+ return CONTEXTUAL_SCREENTIP_SET
+
/obj/machinery/portable_atmospherics/canister/examine(user)
. = ..()
. += span_notice("A sticker on its side says MAX SAFE PRESSURE: [siunit_pressure(initial(pressure_limit), 0)]; MAX SAFE TEMPERATURE: [siunit(temp_limit, "K", 0)].")
@@ -88,8 +130,13 @@
. += span_notice("The internal cell has [internal_cell.percent()]% of its total charge.")
else
. += span_notice("Warning, no cell installed, use a screwdriver to open the hatch and insert one.")
- if(cell_container_opened)
- . += span_notice("Cell hatch open, close it with a screwdriver.")
+ if(panel_open)
+ . += span_notice("Hatch open, close it with a screwdriver.")
+ if(get_dist(user, src) <= 2)
+ if(rig)
+ . += span_warning("There is some kind of device rigged to the canister!")
+ else
+ . += span_notice("It looks like you could rig a device to the canister.")
// Please keep the canister types sorted
// Basic canister per gas below here
@@ -286,7 +333,7 @@
. += mutable_appearance('icons/obj/pipes_n_cables/canisters.dmi', "shielding")
. += emissive_appearance('icons/obj/pipes_n_cables/canisters.dmi', "shielding", src)
- if(cell_container_opened)
+ if(panel_open)
. += mutable_appearance('icons/obj/pipes_n_cables/canisters.dmi', "cell_hatch")
///Function is used to actually set the overlays
@@ -352,30 +399,69 @@
/obj/machinery/portable_atmospherics/canister/attackby(obj/item/item, mob/user, params)
if(istype(item, /obj/item/stock_parts/cell))
var/obj/item/stock_parts/cell/active_cell = item
- if(!cell_container_opened)
- balloon_alert(user, "open the hatch first")
+ if(!panel_open)
+ balloon_alert(user, "open hatch first!")
return TRUE
if(!user.transferItemToLoc(active_cell, src))
return TRUE
if(internal_cell)
user.put_in_hands(internal_cell)
- balloon_alert(user, "you successfully replace the cell")
+ balloon_alert(user, "you replace the cell")
else
- balloon_alert(user, "you successfully install the cell")
+ balloon_alert(user, "you install the cell")
internal_cell = active_cell
return TRUE
+ if(is_wire_tool(item) && panel_open)
+ wires.interact(user)
+ return
+ if(istype(item, /obj/item/assembly_holder))
+ if(rig)
+ balloon_alert(user, "another device is in the way!")
+ return ..()
+ var/obj/item/assembly_holder/holder = item
+
+ user.balloon_alert_to_viewers("attaching rig...")
+ add_fingerprint(user)
+ if(!do_after(user, 2 SECONDS, target = src) || !user.transferItemToLoc(holder, src))
+ return
+ rig = holder
+ holder.master = src
+ holder.on_attach()
+ assemblies_overlay = holder
+ assemblies_overlay.layer = 3.3
+ assemblies_overlay.pixel_y -= 5
+ add_overlay(assemblies_overlay)
+ log_bomber(user, "attached [holder.name] to ", src)
+ last_rigger = user
+ user.balloon_alert_to_viewers("attached rig")
+ return
return ..()
+/obj/machinery/portable_atmospherics/canister/attack_hand_secondary(mob/user, list/modifiers)
+ if(!rig)
+ return
+ // mousetrap rigs only make sense if you can set them off, can't step on them
+ // If you see a mousetrap-rigged fuel tank, just leave it alone
+ rig.on_found()
+ if(QDELETED(src))
+ return
+ user.balloon_alert_to_viewers("detaching rig...")
+ if(!do_after(user, 2 SECONDS, target = src))
+ return
+ user.balloon_alert_to_viewers("detached rig")
+ user.log_message("detached [rig] from [src].", LOG_GAME)
+ if(!user.put_in_hands(rig))
+ rig.forceMove(get_turf(user))
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
/obj/machinery/portable_atmospherics/canister/screwdriver_act(mob/living/user, obj/item/screwdriver)
- screwdriver.play_tool_sound(src, 50)
- cell_container_opened = !cell_container_opened
- to_chat(user, span_notice("You [cell_container_opened ? "open" : "close"] the cell container hatch of [src]."))
- update_appearance()
- return ITEM_INTERACT_SUCCESS
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, screwdriver))
+ update_appearance()
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/portable_atmospherics/canister/crowbar_act(mob/living/user, obj/item/tool)
- if(!cell_container_opened || !internal_cell)
+ if(!panel_open || !internal_cell)
return ITEM_INTERACT_BLOCKING
internal_cell.forceMove(drop_location())
@@ -606,64 +692,19 @@
investigate_log("was set to [release_pressure] kPa by [key_name(usr)].", INVESTIGATE_ATMOS)
if("valve")
- var/logmsg
- var/admin_msg
- var/danger = FALSE
- var/n = 0
- valve_open = !valve_open
- if(valve_open)
- SSair.start_processing_machine(src)
- logmsg = "Valve was opened by [key_name(usr)], starting a transfer into \the [holding || "air"]. "
- if(!holding)
- var/list/gaseslog = list() //list for logging all gases in canister
- for(var/id in air_contents.gases)
- var/gas = air_contents.gases[id]
- gaseslog[gas[GAS_META][META_GAS_NAME]] = gas[MOLES] //adds gases to gaseslog
- if(!gas[GAS_META][META_GAS_DANGER])
- continue
- if(gas[MOLES] > (gas[GAS_META][META_GAS_MOLES_VISIBLE] || MOLES_GAS_VISIBLE)) //if moles_visible is undefined, default to default visibility
- danger = TRUE //at least 1 danger gas
- logmsg = "[key_name(usr)] opened a canister that contains the following:"
- admin_msg = "[ADMIN_LOOKUPFLW(usr)] opened a canister that contains the following at [ADMIN_VERBOSEJMP(src)]:"
- for(var/name in gaseslog)
- n = n + 1
- logmsg += "\n[name]: [gaseslog[name]] moles."
- if(n <= 5) //the first five gases added
- admin_msg += "\n[name]: [gaseslog[name]] moles."
- if(n == 5 && length(gaseslog) > 5) //message added if more than 5 gases
- admin_msg += "\nToo many gases to log. Check investigate log."
- if(danger) //sent to admin's chat if contains dangerous gases
- message_admins(admin_msg)
- else
- logmsg = "valve was closed by [key_name(usr)], stopping the transfer into \the [holding || "air"]. "
- investigate_log(logmsg, INVESTIGATE_ATMOS)
- release_log += logmsg
+ toggle_valve(usr)
. = TRUE
if("eject")
- if(holding)
- if(valve_open)
- message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the [span_boldannounce("air")].")
- usr.investigate_log("removed the [holding], leaving the valve open and transferring into the [span_boldannounce("air")].", INVESTIGATE_ATMOS)
- replace_tank(usr, FALSE)
+ if(eject_tank(usr))
. = TRUE
if("shielding")
- shielding_powered = !shielding_powered
- SSair.start_processing_machine(src)
- message_admins("[ADMIN_LOOKUPFLW(usr)] turned [shielding_powered ? "on" : "off"] the [src] powered shielding.")
- usr.investigate_log("turned [shielding_powered ? "on" : "off"] the [src] powered shielding.")
- update_appearance()
+ toggle_shielding(usr)
. = TRUE
if("reaction_suppression")
- if(!nob_crystal_inserted)
- stack_trace("[usr] tried to toggle reaction suppression on a canister without a noblium crystal inside, possible href exploit attempt.")
- return
- suppress_reactions = !suppress_reactions
- SSair.start_processing_machine(src)
- message_admins("[ADMIN_LOOKUPFLW(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.")
- usr.investigate_log("turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.")
+ toggle_reaction_suppression(usr)
. = TRUE
if("recolor")
@@ -682,6 +723,70 @@
update_appearance()
+
+/// Opens/closes the canister valve
+/obj/machinery/portable_atmospherics/canister/proc/toggle_valve(mob/user, wire_pulsed = FALSE)
+ var/logmsg
+ var/admin_msg
+ var/danger = FALSE
+ var/n = 0
+ valve_open = !valve_open
+ if(valve_open)
+ SSair.start_processing_machine(src)
+ logmsg = "Valve was opened by [key_name(user)] [wire_pulsed ? "via wire pulse" : ""], starting a transfer into \the [holding || "air"]. "
+ if(!holding)
+ var/list/gaseslog = list() //list for logging all gases in canister
+ for(var/id in air_contents.gases)
+ var/gas = air_contents.gases[id]
+ gaseslog[gas[GAS_META][META_GAS_NAME]] = gas[MOLES] //adds gases to gaseslog
+ if(!gas[GAS_META][META_GAS_DANGER])
+ continue
+ if(gas[MOLES] > (gas[GAS_META][META_GAS_MOLES_VISIBLE] || MOLES_GAS_VISIBLE)) //if moles_visible is undefined, default to default visibility
+ danger = TRUE //at least 1 danger gas
+ logmsg = "[key_name(user)] opened a canister [wire_pulsed ? "via wire pulse" : ""] that contains the following:"
+ admin_msg = "[ADMIN_LOOKUPFLW(user)] opened a canister [wire_pulsed ? "via wire pulse" : ""] that contains the following at [ADMIN_VERBOSEJMP(src)]:"
+ for(var/name in gaseslog)
+ n = n + 1
+ logmsg += "\n[name]: [gaseslog[name]] moles."
+ if(n <= 5) //the first five gases added
+ admin_msg += "\n[name]: [gaseslog[name]] moles."
+ if(n == 5 && length(gaseslog) > 5) //message added if more than 5 gases
+ admin_msg += "\nToo many gases to log. Check investigate log."
+ if(danger) //sent to admin's chat if contains dangerous gases
+ message_admins(admin_msg)
+ else
+ logmsg = "valve was closed by [key_name(user)] [wire_pulsed ? "via wire pulse" : ""], stopping the transfer into \the [holding || "air"]. "
+ investigate_log(logmsg, INVESTIGATE_ATMOS)
+ release_log += logmsg
+
+/// Turns canister shielding on or off
+/obj/machinery/portable_atmospherics/canister/proc/toggle_shielding(mob/user, wire_pulsed = FALSE)
+ shielding_powered = !shielding_powered
+ SSair.start_processing_machine(src)
+ message_admins("[ADMIN_LOOKUPFLW(user)] turned [shielding_powered ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] powered shielding.")
+ user.investigate_log("turned [shielding_powered ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] powered shielding.")
+ update_appearance()
+
+/// Ejects tank from canister, if any
+/obj/machinery/portable_atmospherics/canister/proc/eject_tank(mob/user, wire_pulsed = FALSE)
+ if(holding)
+ if(valve_open)
+ message_admins("[ADMIN_LOOKUPFLW(user)] removed [holding] from [src] with valve still open [wire_pulsed ? "via wire pulse" : ""] at [ADMIN_VERBOSEJMP(src)] releasing contents into the [span_boldannounce("air")].")
+ user.investigate_log("removed the [holding] [wire_pulsed ? "via wire pulse" : ""], leaving the valve open and transferring into the [span_boldannounce("air")].", INVESTIGATE_ATMOS)
+ replace_tank(user, FALSE)
+ return TRUE
+
+/// Turns hyper-noblium crystal reaction suppression in the canister on or off
+/obj/machinery/portable_atmospherics/canister/proc/toggle_reaction_suppression(mob/user, wire_pulsed = FALSE)
+ if(!nob_crystal_inserted)
+ if(!wire_pulsed)
+ stack_trace("[user] tried to toggle reaction suppression on a canister without a noblium crystal inside and without pulsing wires, possible href exploit attempt.")
+ return
+ suppress_reactions = !suppress_reactions
+ SSair.start_processing_machine(src)
+ message_admins("[ADMIN_LOOKUPFLW(user)] turned [suppress_reactions ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] reaction suppression.")
+ user.investigate_log("turned [suppress_reactions ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] reaction suppression.")
+
/obj/machinery/portable_atmospherics/canister/proc/recolor(datum/greyscale_modify_menu/menu)
set_greyscale(menu.split_colors, menu.config.type)
diff --git a/code/modules/awaymissions/mission_code/murderdome.dm b/code/modules/awaymissions/mission_code/murderdome.dm
index c99b9d91d5a..2e1154d29cf 100644
--- a/code/modules/awaymissions/mission_code/murderdome.dm
+++ b/code/modules/awaymissions/mission_code/murderdome.dm
@@ -1,7 +1,7 @@
/obj/structure/window/reinforced/fulltile/indestructible
name = "robust window"
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
flags_1 = PREVENT_CLICK_UNDER_1
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
diff --git a/code/modules/basketball/hoop.dm b/code/modules/basketball/hoop.dm
index dd0585e0742..32699de1dc9 100644
--- a/code/modules/basketball/hoop.dm
+++ b/code/modules/basketball/hoop.dm
@@ -53,9 +53,6 @@
/obj/structure/hoop/update_overlays()
. = ..()
- if(dir & NORTH)
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
-
var/dir_offset_x = 0
var/dir_offset_y = 0
@@ -72,7 +69,6 @@
var/mutable_appearance/scoreboard = mutable_appearance('icons/obj/signs.dmi', "basketball_scorecard")
scoreboard.pixel_x = dir_offset_x
scoreboard.pixel_y = dir_offset_y
- SET_PLANE_EXPLICIT(scoreboard, GAME_PLANE, src)
. += scoreboard
var/ones = total_score % 10
diff --git a/code/modules/bitrunning/job.dm b/code/modules/bitrunning/job.dm
index 57581753c0f..1e749a3c7a5 100644
--- a/code/modules/bitrunning/job.dm
+++ b/code/modules/bitrunning/job.dm
@@ -29,7 +29,7 @@
/obj/item/food/cornchips/blue = 1,
)
rpg_title = "Recluse"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/bitrunner
name = "Bitrunner"
diff --git a/code/modules/bitrunning/orders/disks.dm b/code/modules/bitrunning/orders/disks.dm
index ced1dde883a..3015cd1ee61 100644
--- a/code/modules/bitrunning/orders/disks.dm
+++ b/code/modules/bitrunning/orders/disks.dm
@@ -4,23 +4,29 @@
/datum/orderable_item/bitrunning_tech/item_tier1
cost_per_order = 1000
item_path = /obj/item/bitrunning_disk/item/tier1
+ desc = "This disk contains a program that lets you equip a medical beamgun, a C4 explosive, or a box of infinite pizza."
/datum/orderable_item/bitrunning_tech/item_tier2
cost_per_order = 1500
item_path = /obj/item/bitrunning_disk/item/tier2
+ desc = "This disk contains a program that lets you equip a luxury medipen, a pistol, or an armour vest."
/datum/orderable_item/bitrunning_tech/item_tier3
cost_per_order = 2500
item_path = /obj/item/bitrunning_disk/item/tier3
+ desc = "This disk contains a program that lets you equip an advanced energy gun, a dual bladed energy sword, or a minibomb."
/datum/orderable_item/bitrunning_tech/ability_tier1
cost_per_order = 1000
item_path = /obj/item/bitrunning_disk/ability/tier1
+ desc = "This disk contains a program that lets you cast Summon Cheese or Lesser Heal."
/datum/orderable_item/bitrunning_tech/ability_tier2
cost_per_order = 1800
item_path = /obj/item/bitrunning_disk/ability/tier2
+ desc = "This disk contains a program that lets you cast Fireball, Lightning Bolt, or Forcewall."
/datum/orderable_item/bitrunning_tech/ability_tier3
cost_per_order = 3200
item_path = /obj/item/bitrunning_disk/ability/tier3
+ desc = "This disk contains a program that lets you shapeshift into a lesser ashdrake, or a polar bear."
diff --git a/code/modules/capture_the_flag/ctf_game.dm b/code/modules/capture_the_flag/ctf_game.dm
index 980cf1a0f69..f333b370d48 100644
--- a/code/modules/capture_the_flag/ctf_game.dm
+++ b/code/modules/capture_the_flag/ctf_game.dm
@@ -464,7 +464,7 @@
/obj/structure/table/reinforced/ctf
resistance_flags = INDESTRUCTIBLE
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
#define CTF_LOADING_UNLOADED 0
#define CTF_LOADING_LOADING 1
diff --git a/code/modules/cargo/gondolapod.dm b/code/modules/cargo/gondolapod.dm
index f3dca7a3dca..36ceff8ad51 100644
--- a/code/modules/cargo/gondolapod.dm
+++ b/code/modules/cargo/gondolapod.dm
@@ -65,13 +65,13 @@
/mob/living/simple_animal/pet/gondola/gondolapod/setOpened()
opened = TRUE
- SET_PLANE_IMPLICIT(src, GAME_PLANE)
+ layer = initial(layer)
update_appearance()
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/, setClosed)), 50)
/mob/living/simple_animal/pet/gondola/gondolapod/setClosed()
opened = FALSE
- SET_PLANE_IMPLICIT(src, GAME_PLANE_FOV_HIDDEN)
+ layer = LOW_MOB_LAYER
update_appearance()
/mob/living/simple_animal/pet/gondola/gondolapod/death()
diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm
index cf08c773d06..9dbd2d05130 100644
--- a/code/modules/cargo/goodies.dm
+++ b/code/modules/cargo/goodies.dm
@@ -4,6 +4,12 @@
group = "Goodies"
goody = TRUE
+/datum/supply_pack/goody/clear_pda
+ name = "Mint Condition Nanotrasen Clear PDA"
+ desc = "Mint condition, freshly repackaged! A valuable collector's item normally valued at over 2.5 million credits, now available for a steal!"
+ cost = 100000
+ contains = list(/obj/item/modular_computer/pda/clear)
+
/datum/supply_pack/goody/dumdum38
name = ".38 DumDum Speedloader"
desc = "Contains one speedloader of .38 DumDum ammunition, good for embedding in soft targets."
diff --git a/code/modules/cargo/markets/market_items/tools.dm b/code/modules/cargo/markets/market_items/tools.dm
index 1be815e4f6b..c20834e640b 100644
--- a/code/modules/cargo/markets/market_items/tools.dm
+++ b/code/modules/cargo/markets/market_items/tools.dm
@@ -96,3 +96,12 @@
price_max = CARGO_CRATE_VALUE * 1.615
stock_max = 2
availability_prob = 50
+
+/datum/market_item/tool/program_disk
+ name = "Bootleg Data Disk"
+ desc = "A data disk containing EXCLUSIVE and LIMITED modular programs. Legally, we're not allowed to tell you how we aquired them."
+ item = /obj/item/computer_disk/black_market
+ price_min = CARGO_CRATE_VALUE * 0.75
+ price_max = CARGO_CRATE_VALUE * 2
+ stock_max = 3
+ availability_prob = 40
diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm
index ae03ffb0190..446da624e13 100644
--- a/code/modules/cargo/packs/engineering.dm
+++ b/code/modules/cargo/packs/engineering.dm
@@ -73,7 +73,7 @@
desc = "No rechargers? No problem, with the NT-75 EPI, you can recharge any standard \
cell-based equipment anytime, anywhere. Contains two Inducers."
cost = CARGO_CRATE_VALUE * 4
- contains = list(/obj/item/inducer/sci {cell_type = /obj/item/stock_parts/cell/inducer_supply; opened = 0} = 2) //FALSE doesn't work in modified type paths apparently.
+ contains = list(/obj/item/inducer/orderable = 2)
crate_name = "inducer crate"
crate_type = /obj/structure/closet/crate/engineering/electrical
diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm
index 707267e7143..158e466c107 100644
--- a/code/modules/client/client_colour.dm
+++ b/code/modules/client/client_colour.dm
@@ -191,7 +191,7 @@
colour = list(/*R*/ 0,0,0,0, /*G*/ 0,175,0,0, /*B*/ 0,0,0,0, /*A*/ 0,0,0,1, /*C*/0,-130,0,0) // Matrix colors
/datum/client_colour/monochrome
- colour = list(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
+ colour = COLOR_MATRIX_GRAYSCALE
priority = PRIORITY_HIGH //we can't see colors anyway!
override = TRUE
fade_in = 20
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 747db2afbc1..ca35d6cc833 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -3,10 +3,7 @@
////////////
GLOBAL_LIST_INIT(blacklisted_builds, list(
- "1407" = "bug preventing client display overrides from working leads to clients being able to see things/mobs they shouldn't be able to see",
- "1408" = "bug preventing client display overrides from working leads to clients being able to see things/mobs they shouldn't be able to see",
- "1428" = "bug causing right-click menus to show too many verbs that's been fixed in version 1429",
-
+ "1622" = "Bug breaking rendering can lead to wallhacks.",
))
#define LIMITER_SIZE 5
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 741c7f7a36f..1811f3f8715 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -325,9 +325,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return TRUE
if ("open_loadout")
- if(parent.open_loadout_ui)
- parent.open_loadout_ui.ui_interact(usr)
+ var/datum/loadout_manager/open_loadout_ui = parent.open_loadout_ui?.resolve()
+ if(open_loadout_ui)
+ open_loadout_ui.ui_interact(usr)
else
+ parent.open_loadout_ui = null
var/datum/loadout_manager/tgui = new(usr)
tgui.ui_interact(usr)
return TRUE
@@ -621,7 +623,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind)
if(!read_preference(/datum/preference/toggle/random_hardcore))
return FALSE
- if(job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) //No command staff
+ if(job.job_flags & JOB_HEAD_OF_STAFF) //No heads of staff
return FALSE
for(var/datum/antagonist/antag as anything in mind.antag_datums)
if(antag.get_team()) //No team antags
diff --git a/code/modules/client/preferences/assets.dm b/code/modules/client/preferences/assets.dm
index 6fa29b9f90f..a75178cc3e1 100644
--- a/code/modules/client/preferences/assets.dm
+++ b/code/modules/client/preferences/assets.dm
@@ -2,7 +2,6 @@
/datum/asset/spritesheet/preferences
name = "preferences"
early = TRUE
- cross_round_cachable = TRUE
/datum/asset/spritesheet/preferences/create_spritesheets()
var/list/to_insert = list()
diff --git a/code/modules/client/preferences/fov_darkness.dm b/code/modules/client/preferences/fov_darkness.dm
deleted file mode 100644
index b0146e10b40..00000000000
--- a/code/modules/client/preferences/fov_darkness.dm
+++ /dev/null
@@ -1,17 +0,0 @@
-/datum/preference/numeric/fov_darkness
- category = PREFERENCE_CATEGORY_GAME_PREFERENCES
- savefile_key = "fov_darkness"
- savefile_identifier = PREFERENCE_PLAYER
-
- minimum = 0
- maximum = 255
-
-/datum/preference/numeric/fov_darkness/create_default_value()
- return 255
-
-/datum/preference/numeric/fov_darkness/apply_to_client_updated(client/client, value)
- if(client.mob)
- var/datum/component/fov_handler/fov_component = client.mob.GetComponent(/datum/component/fov_handler)
- if(!fov_component)
- return
- fov_component.visual_shadow.alpha = value
diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm
index 52593aad912..13e3bfa718f 100644
--- a/code/modules/client/preferences/middleware/antags.dm
+++ b/code/modules/client/preferences/middleware/antags.dm
@@ -108,7 +108,6 @@
/datum/asset/spritesheet/antagonists
name = "antagonists"
early = TRUE
- cross_round_cachable = TRUE
/// Mapping of spritesheet keys -> icons
var/list/antag_icons = list()
@@ -119,7 +118,6 @@
ROLE_GLITCH = /datum/antagonist/bitrunning_glitch,
ROLE_FUGITIVE = /datum/antagonist/fugitive,
ROLE_LONE_OPERATIVE = /datum/antagonist/nukeop/lone,
- ROLE_DRIFTING_CONTRACTOR = /datum/antagonist/contractor, //SKYRAT EDIT
ROLE_SENTIENCE = /datum/antagonist/sentient_creature,
)
diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm
index 319547e0d3c..8b6304efe0b 100644
--- a/code/modules/client/preferences/middleware/jobs.dm
+++ b/code/modules/client/preferences/middleware/jobs.dm
@@ -53,6 +53,8 @@
var/list/jobs = list()
for (var/datum/job/job as anything in SSjob.joinable_occupations)
+ if (job.job_flags & JOB_LATEJOIN_ONLY)
+ continue
var/datum/job_department/department_type = job.department_for_prefs || job.departments_list?[1]
if (isnull(department_type))
stack_trace("[job] does not have a department set, yet is a joinable occupation!")
@@ -122,6 +124,8 @@
var/list/job_required_experience = list()
for (var/datum/job/job as anything in SSjob.all_occupations)
+ if (job.job_flags & JOB_LATEJOIN_ONLY)
+ continue
var/required_playtime_remaining = job.required_playtime_remaining(user.client)
if (required_playtime_remaining)
job_required_experience[job.title] = list(
diff --git a/code/modules/client/preferences/middleware/species.dm b/code/modules/client/preferences/middleware/species.dm
index 16954cf8bf7..b8c5e000680 100644
--- a/code/modules/client/preferences/middleware/species.dm
+++ b/code/modules/client/preferences/middleware/species.dm
@@ -9,7 +9,6 @@
/datum/asset/spritesheet/species
name = "species"
early = TRUE
- cross_round_cachable = TRUE
/datum/asset/spritesheet/species/create_spritesheets()
var/list/to_insert = list()
diff --git a/code/modules/clothing/head/hat.dm b/code/modules/clothing/head/hat.dm
index 104bf812444..1b65af66d33 100644
--- a/code/modules/clothing/head/hat.dm
+++ b/code/modules/clothing/head/hat.dm
@@ -180,6 +180,15 @@
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
dog_fashion = /datum/dog_fashion/head/santa
+/obj/item/clothing/head/costume/santa/gags
+ name = "santa hat"
+ desc = "On the first day of christmas my employer gave to me!"
+ icon_state = "santa_hat"
+ greyscale_colors = "#cc0000#f8f8f8"
+ greyscale_config = /datum/greyscale_config/santa_hat
+ greyscale_config_worn = /datum/greyscale_config/santa_hat/worn
+ flags_1 = IS_PLAYER_COLORABLE_1
+
/obj/item/clothing/head/costume/jester
name = "jester hat"
desc = "A hat with bells, to add some merriness to the suit."
diff --git a/code/modules/clothing/head/mind_monkey_helmet.dm b/code/modules/clothing/head/mind_monkey_helmet.dm
index d1c423b5b03..eaa7c1cd011 100644
--- a/code/modules/clothing/head/mind_monkey_helmet.dm
+++ b/code/modules/clothing/head/mind_monkey_helmet.dm
@@ -47,7 +47,7 @@
playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
RegisterSignal(magnification, COMSIG_SPECIES_LOSS, PROC_REF(make_fall_off))
polling = TRUE
- var/list/candidates = poll_candidates_for_mob("Do you want to play as a mind magnified monkey?", ROLE_MONKEY_HELMET, null, 5 SECONDS, magnification, POLL_IGNORE_MONKEY_HELMET)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a mind magnified monkey?", check_jobban = ROLE_MONKEY_HELMET, poll_time = 5 SECONDS, target_mob = magnification, ignore_category = POLL_IGNORE_MONKEY_HELMET, pic_source = magnification, role_name_text = "mind-magnified monkey")
polling = FALSE
if(!magnification)
return
diff --git a/code/modules/clothing/masks/gas_filter.dm b/code/modules/clothing/masks/gas_filter.dm
index c77c37de224..08ae650c247 100644
--- a/code/modules/clothing/masks/gas_filter.dm
+++ b/code/modules/clothing/masks/gas_filter.dm
@@ -39,7 +39,8 @@
/datum/gas/nitrium,
/datum/gas/freon,
/datum/gas/hypernoblium,
- /datum/gas/bz
+ /datum/gas/bz,
+ /datum/gas/miasma,
)
///List of gases with low filter priority
var/list/low_filtering_gases = list(
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index b87a3f7b221..a4c6393ed6f 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -81,10 +81,10 @@
var/tie_timer_actual = tie_timer
// Mirrors give you a boost to your tying speed. I realize this stacks and I think that's hilarious.
for(var/obj/structure/mirror/reflection in view(2, user))
- tie_timer_actual /= 1.25
+ tie_timer_actual *= 0.8
// Heads of staff are experts at tying their ties.
- if(user.mind?.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
- tie_timer_actual /= 2
+ if(HAS_TRAIT(user, TRAIT_FAST_TYING))
+ tie_timer_actual *= 0.5
// Tie/Untie our tie
if(!do_after(user, tie_timer_actual))
to_chat(user, span_notice("Your fingers fumble away from [src] as your concentration breaks."))
diff --git a/code/modules/clothing/outfits/plasmaman.dm b/code/modules/clothing/outfits/plasmaman.dm
index a422d2d736e..40d7cfa888e 100644
--- a/code/modules/clothing/outfits/plasmaman.dm
+++ b/code/modules/clothing/outfits/plasmaman.dm
@@ -288,3 +288,10 @@
uniform = /obj/item/clothing/under/plasmaman/bitrunner
gloves = /obj/item/clothing/gloves/color/plasmaman/black
head = /obj/item/clothing/head/helmet/space/plasmaman/bitrunner
+
+/datum/outfit/plasmaman/bridge_assistant
+ name = "Bridge Assistant Plasmaman"
+
+ uniform = /obj/item/clothing/under/plasmaman //call me when this is gags and not 10 million new assets
+ gloves = /obj/item/clothing/gloves/color/plasmaman/black
+ head = /obj/item/clothing/head/helmet/space/plasmaman
diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm
index 7a3e155d6f8..f60b1f2d359 100644
--- a/code/modules/clothing/suits/labcoat.dm
+++ b/code/modules/clothing/suits/labcoat.dm
@@ -74,12 +74,18 @@
/obj/item/clothing/suit/toggle/labcoat/genetics
name = "geneticist labcoat"
desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder."
- icon_state = "labcoat_gen"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#4A77A1#4A77A1#7095C2"
/obj/item/clothing/suit/toggle/labcoat/chemist
name = "chemist labcoat"
desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder."
- icon_state = "labcoat_chem"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#F17420#F17420#EB6F2C"
/obj/item/clothing/suit/toggle/labcoat/chemist/Initialize(mapload)
. = ..()
@@ -88,7 +94,10 @@
/obj/item/clothing/suit/toggle/labcoat/virologist
name = "virologist labcoat"
desc = "A suit that protects against minor chemical spills. Has a green stripe on the shoulder."
- icon_state = "labcoat_vir"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#198019#198019#40992E"
/obj/item/clothing/suit/toggle/labcoat/virologist/Initialize(mapload)
. = ..()
@@ -97,7 +106,10 @@
/obj/item/clothing/suit/toggle/labcoat/coroner
name = "coroner labcoat"
desc = "A suit that protects against minor chemical spills. Has a black stripe on the shoulder."
- icon_state = "labcoat_coroner"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#2D2D33#2D2D33#39393F"
/obj/item/clothing/suit/toggle/labcoat/coroner/Initialize(mapload)
. = ..()
@@ -109,7 +121,10 @@
/obj/item/clothing/suit/toggle/labcoat/science
name = "scientist labcoat"
desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder."
- icon_state = "labcoat_sci"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#7E1980#7E1980#B347A1"
/obj/item/clothing/suit/toggle/labcoat/science/Initialize(mapload)
. = ..()
@@ -118,9 +133,15 @@
/obj/item/clothing/suit/toggle/labcoat/roboticist
name = "roboticist labcoat"
desc = "More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads."
- icon_state = "labcoat_robo"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#88242D#88242D#39393F"
/obj/item/clothing/suit/toggle/labcoat/interdyne
name = "interdyne labcoat"
desc = "More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads."
- icon_state = "labcoat_robo"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#88242D#88242D#39393F"
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index 3a6defdd665..39865fa956d 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -87,8 +87,7 @@
if(ispath(typepath, /datum/round_event/ghost_role) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT))
return FALSE
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
- if (istype(dynamic) && dynamic_should_hijack && dynamic.random_event_hijacked != HIJACKED_NOTHING)
+ if (dynamic_should_hijack && SSdynamic.random_event_hijacked != HIJACKED_NOTHING)
return FALSE
return TRUE
diff --git a/code/modules/events/bureaucratic_error.dm b/code/modules/events/bureaucratic_error.dm
index add5ad58b4a..0e44ac391c1 100644
--- a/code/modules/events/bureaucratic_error.dm
+++ b/code/modules/events/bureaucratic_error.dm
@@ -13,21 +13,17 @@
priority_announce("A recent bureaucratic error in the Organic Resources Department may result in personnel shortages in some departments and redundant staffing in others.", "Paperwork Mishap Alert")
/datum/round_event/bureaucratic_error/start()
- var/list/jobs = SSjob.joinable_occupations.Copy()
- /*
- if(prob(33)) // Only allows latejoining as a single role. Add latejoin AI bluespace pods for fun later.
+ var/list/jobs = SSjob.get_valid_overflow_jobs()
+ /* SKYRAT EDIT REMOVAL START
+ if(prob(33)) // Only allows latejoining as a single role.
var/datum/job/overflow = pick_n_take(jobs)
overflow.spawn_positions = -1
overflow.total_positions = -1 // Ensures infinite slots as this role. Assistant will still be open for those that cant play it.
for(var/job in jobs)
var/datum/job/current = job
- if(!current.allow_bureaucratic_error)
- continue
current.total_positions = 0
- else // Adds/removes a random amount of job slots from all jobs.
+ return
*/ // SKYRAT EDIT REMOVAL - no more locking off jobs
+ // Adds/removes a random amount of job slots from all jobs.
for(var/datum/job/current as anything in jobs)
- if(!current.allow_bureaucratic_error)
- continue
- var/ran = rand(1,4) // SKYRAT EDIT - no more locking off jobs
- current.total_positions = max(current.total_positions + ran, 0)
+ current.total_positions = max(current.total_positions + rand(-2,4), 1) // SKYRAT EDIT CHANGE - No more locking off jobs - ORIGINAL: current.total_positions = max(current.total_positions + rand(-2,4), 0)
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 38b313a6417..3be09528abd 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -61,10 +61,12 @@
continue
if(length(candidate.diseases)) //Is our candidate already sick?
continue
- // SKYRAT EDIT ADD START - Station/area event candidate filtering
+ if(!is_station_level(candidate.z) && !is_mining_level(candidate.z)) //Diseases can't really spread if the vector is in deep space.
+ continue
+ // SKYRAT EDIT ADDITION START - Station/area event candidate filtering.
if(engaged_role_play_check(candidate, station = TRUE, dorms = TRUE))
continue
- // SKYRAT EDIT ADD END
+ // SKYRAT EDIT ADDITION END
disease_candidates += candidate
///Handles checking and alerting admins about the number of valid candidates
diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm
index f690b1c4a8d..93572d244f5 100644
--- a/code/modules/events/fake_virus.dm
+++ b/code/modules/events/fake_virus.dm
@@ -7,8 +7,10 @@
/datum/round_event/fake_virus/start()
var/list/fake_virus_victims = list()
- for(var/mob/living/carbon/human/victim in shuffle(GLOB.player_list))
- if(victim.stat == DEAD || HAS_TRAIT(victim, TRAIT_CRITICAL_CONDITION) || !(victim.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
+ for(var/mob/living/carbon/human/victim in GLOB.player_list)
+ if(victim.stat != CONSCIOUS || HAS_TRAIT(victim, TRAIT_VIRUSIMMUNE))
+ continue
+ if(!(victim.mind?.assigned_role.job_flags & JOB_CREW_MEMBER))
continue
// SKYRAT EDIT ADD START - Station/area event candidate filtering
if(engaged_role_play_check(fake_virus_victims, station = TRUE, dorms = TRUE))
@@ -17,21 +19,21 @@
fake_virus_victims += victim
//first we do hard status effect victims
- var/defacto_min = min(3, LAZYLEN(fake_virus_victims))
- if(defacto_min)// event will hit 1-3 people by default, but will do 1-2 or just 1 if only those many candidates are available
- for(var/i in 1 to rand(1,defacto_min))
- var/mob/living/carbon/human/hypochondriac = pick(fake_virus_victims)
- hypochondriac.apply_status_effect(/datum/status_effect/fake_virus)
- fake_virus_victims -= hypochondriac
- announce_to_ghosts(hypochondriac)
+ var/defacto_min = min(3, length(fake_virus_victims))
+ if(defacto_min <= 0)// event will hit 1-3 people by default, but will do 1-2 or just 1 if only those many candidates are available
+ return
+ for(var/i in 1 to rand(1, defacto_min))
+ var/mob/living/carbon/human/hypochondriac = pick_n_take(fake_virus_victims)
+ hypochondriac.apply_status_effect(/datum/status_effect/fake_virus)
+ announce_to_ghosts(hypochondriac)
//then we do light one-message victims who simply cough or whatever once (have to repeat the process since the last operation modified our candidates list)
- defacto_min = min(5, LAZYLEN(fake_virus_victims))
- if(defacto_min)
- for(var/i in 1 to rand(1,defacto_min))
- var/mob/living/carbon/human/onecoughman = pick(fake_virus_victims)
- if(prob(25))//1/4 odds to get a spooky message instead of coughing out loud
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), onecoughman, span_warning("[pick("Your head hurts.", "Your head pounds.")]")), rand(30,150))
- else
- addtimer(CALLBACK(onecoughman, TYPE_PROC_REF(/mob, emote), pick("cough", "sniff", "sneeze")), rand(30,150))//deliver the message with a slightly randomized time interval so there arent multiple people coughing at the exact same time
- fake_virus_victims -= onecoughman
+ defacto_min = min(5, length(fake_virus_victims))
+ if(defacto_min <= 0)
+ return
+ for(var/i in 1 to rand(1, defacto_min))
+ var/mob/living/carbon/human/onecoughman = pick_n_take(fake_virus_victims)
+ if(prob(25))//1/4 odds to get a spooky message instead of coughing out loud
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), onecoughman, span_warning("[pick("Your head hurts.", "Your head pounds.")]")), rand(3 SECONDS, 15 SECONDS))
+ else
+ addtimer(CALLBACK(onecoughman, TYPE_PROC_REF(/mob, emote), pick("cough", "sniff", "sneeze")), rand(3 SECONDS, 15 SECONDS))//deliver the message with a slightly randomized time interval so there arent multiple people coughing at the exact same time
diff --git a/code/modules/events/ghost_role/_ghost_role.dm b/code/modules/events/ghost_role/_ghost_role.dm
index 90b6b36dca3..336fb4c03bb 100644
--- a/code/modules/events/ghost_role/_ghost_role.dm
+++ b/code/modules/events/ghost_role/_ghost_role.dm
@@ -3,8 +3,6 @@
/datum/round_event/ghost_role
fakeable = FALSE
- /// Members of this list will be placed at the front of the candicacy list, in front of the (shuffled) normal candidates.
- var/list/priority_candidates = list() //expected to contain 0 or more /clients (or things with .key)
/// The minimum number of signups required for the event to continue past the polling period
var/minimum_required = 1
/// The name of the role, to be displayed in logs/polls/etc.
@@ -84,29 +82,4 @@
/datum/round_event/ghost_role/proc/spawn_role()
return FALSE
-/**
- * Gathers the candidates to select our ghost roles from.
- *
- * Returns a list of candidates in priority order, with candidates from
- * `priority_candidates` first, and ghost roles randomly shuffled and
- * appended after.
- *
- * jobban - The jobban flag to exclude players from the polling pool with.
- * be_special - The "special role" flag for the ghost candidacy poll.
- */
-
-/datum/round_event/ghost_role/proc/get_candidates(jobban, be_special)
- var/list/mob/dead/observer/regular_candidates
- // don't get their hopes up
- if(priority_candidates.len < minimum_required)
- regular_candidates = poll_ghost_candidates("Do you wish to be considered for the special role of '[role_name]'?", jobban, be_special)
- else
- regular_candidates = list()
-
- shuffle_inplace(regular_candidates)
-
- var/list/candidates = priority_candidates + regular_candidates
-
- return candidates
-
#undef MAX_SPAWN_ATTEMPT
diff --git a/code/modules/events/ghost_role/abductor.dm b/code/modules/events/ghost_role/abductor.dm
index f6928222cfd..65fe4a142f5 100644
--- a/code/modules/events/ghost_role/abductor.dm
+++ b/code/modules/events/ghost_role/abductor.dm
@@ -14,7 +14,7 @@
fakeable = FALSE //Nothing to fake here
/datum/round_event/ghost_role/abductor/spawn_role()
- var/list/mob/dead/observer/candidates = get_candidates(ROLE_ABDUCTOR, ROLE_ABDUCTOR)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ABDUCTOR, role = ROLE_ABDUCTOR, pic_source = /obj/item/melee/baton/abductor, role_name_text = role_name)
if(candidates.len < 2)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/alien_infestation.dm b/code/modules/events/ghost_role/alien_infestation.dm
index 8c3741ccef2..f4078e52e65 100644
--- a/code/modules/events/ghost_role/alien_infestation.dm
+++ b/code/modules/events/ghost_role/alien_infestation.dm
@@ -62,7 +62,7 @@
message_admins("An event attempted to spawn an alien but no suitable vents were found. Shutting down.")
return MAP_ERROR
- var/list/candidates = get_candidates(ROLE_ALIEN, ROLE_ALIEN)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /mob/living/carbon/alien/larva, role_name_text = role_name)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/blob.dm b/code/modules/events/ghost_role/blob.dm
index 51219f9fd8d..70640512699 100644
--- a/code/modules/events/ghost_role/blob.dm
+++ b/code/modules/events/ghost_role/blob.dm
@@ -29,7 +29,11 @@
/datum/round_event/ghost_role/blob/spawn_role()
if(!GLOB.blobstart.len)
return MAP_ERROR
- var/list/candidates = get_candidates(ROLE_BLOB, ROLE_BLOB)
+ var/icon/blob_icon = icon('icons/mob/nonhuman-player/blob.dmi', icon_state = "blob_core")
+ blob_icon.Blend("#9ACD32", ICON_MULTIPLY)
+ blob_icon.Blend(icon('icons/mob/nonhuman-player/blob.dmi', "blob_core_overlay"), ICON_OVERLAY)
+ var/image/blob_image = image(blob_icon)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_BLOB, role = ROLE_BLOB, pic_source = blob_image, role_name_text = role_name)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
var/mob/dead/observer/new_blob = pick(candidates)
diff --git a/code/modules/events/ghost_role/changeling_event.dm b/code/modules/events/ghost_role/changeling_event.dm
index 570a6166093..43b4ca48af5 100644
--- a/code/modules/events/ghost_role/changeling_event.dm
+++ b/code/modules/events/ghost_role/changeling_event.dm
@@ -21,7 +21,7 @@
fakeable = FALSE
/datum/round_event/ghost_role/changeling/spawn_role()
- var/list/mob/dead/observer/candidate = get_candidates(ROLE_CHANGELING, ROLE_CHANGELING_MIDROUND)
+ var/list/mob/dead/observer/candidate = SSpolling.poll_ghost_candidates(check_jobban = ROLE_CHANGELING, role = ROLE_CHANGELING_MIDROUND, pic_source = /obj/item/melee/arm_blade, role_name_text = role_name)
if(!candidate.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/fugitive_event.dm b/code/modules/events/ghost_role/fugitive_event.dm
index d272ba637ce..1556eb04558 100644
--- a/code/modules/events/ghost_role/fugitive_event.dm
+++ b/code/modules/events/ghost_role/fugitive_event.dm
@@ -20,7 +20,7 @@
if(isnull(landing_turf))
return MAP_ERROR
var/list/possible_backstories = list()
- var/list/candidates = get_candidates(ROLE_FUGITIVE, ROLE_FUGITIVE)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_FUGITIVE, role = ROLE_FUGITIVE, pic_source = /obj/item/card/id/advanced/prisoner)
if(!length(candidates))
return NOT_ENOUGH_PLAYERS
@@ -105,7 +105,7 @@
//security team gets called in after 10 minutes of prep to find the refugees
/datum/round_event/ghost_role/fugitives/proc/spawn_hunters(backstory)
- var/list/candidates = poll_ghost_candidates("Do you wish to be considered for a group of [backstory]?", ROLE_FUGITIVE_HUNTER)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a group of [backstory]?", check_jobban = ROLE_FUGITIVE_HUNTER, pic_source = /obj/machinery/sleeper, role_name_text = backstory)
shuffle_inplace(candidates)
var/datum/map_template/shuttle/hunter/ship
diff --git a/code/modules/events/ghost_role/morph_event.dm b/code/modules/events/ghost_role/morph_event.dm
index e9ac8bb9ea0..c9133863f8c 100644
--- a/code/modules/events/ghost_role/morph_event.dm
+++ b/code/modules/events/ghost_role/morph_event.dm
@@ -13,7 +13,7 @@
role_name = "morphling"
/datum/round_event/ghost_role/morph/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, ROLE_ALIEN)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /mob/living/basic/morph, role_name_text = "morph")
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/nightmare.dm b/code/modules/events/ghost_role/nightmare.dm
index 57b942988cd..ffb206c476d 100644
--- a/code/modules/events/ghost_role/nightmare.dm
+++ b/code/modules/events/ghost_role/nightmare.dm
@@ -15,7 +15,7 @@
fakeable = FALSE
/datum/round_event/ghost_role/nightmare/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, ROLE_NIGHTMARE)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_NIGHTMARE, role_name_text = role_name)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/operative.dm b/code/modules/events/ghost_role/operative.dm
index 4cf8da639bb..fcea52e3c02 100644
--- a/code/modules/events/ghost_role/operative.dm
+++ b/code/modules/events/ghost_role/operative.dm
@@ -12,7 +12,7 @@
fakeable = FALSE
/datum/round_event/ghost_role/operative/spawn_role()
- var/list/candidates = get_candidates(ROLE_OPERATIVE, ROLE_LONE_OPERATIVE)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_OPERATIVE, role = ROLE_LONE_OPERATIVE, pic_source = /obj/machinery/nuclearbomb)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/revenant_event.dm b/code/modules/events/ghost_role/revenant_event.dm
index 4ed05bbc82c..6cdfc2c4c9e 100644
--- a/code/modules/events/ghost_role/revenant_event.dm
+++ b/code/modules/events/ghost_role/revenant_event.dm
@@ -30,7 +30,7 @@
message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.")
return WAITING_FOR_SOMETHING
- var/list/candidates = get_candidates(ROLE_REVENANT, ROLE_REVENANT)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_REVENANT, role = ROLE_REVENANT, pic_source = /mob/living/basic/revenant)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm
index f33333ddb1b..8ebd30ad7b3 100644
--- a/code/modules/events/ghost_role/sentience.dm
+++ b/code/modules/events/ghost_role/sentience.dm
@@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
/datum/round_event/ghost_role/sentience/spawn_role()
var/list/mob/dead/observer/candidates
- candidates = get_candidates(ROLE_SENTIENCE, ROLE_SENTIENCE)
+ candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, pic_source = /obj/item/slimepotion/slime/sentience, role_name_text = role_name)
// find our chosen mob to breathe life into
// Mobs have to be simple animals, mindless, on station, and NOT holograms.
diff --git a/code/modules/events/ghost_role/sentient_disease.dm b/code/modules/events/ghost_role/sentient_disease.dm
index 5efa4e6915a..44f78d7c08c 100644
--- a/code/modules/events/ghost_role/sentient_disease.dm
+++ b/code/modules/events/ghost_role/sentient_disease.dm
@@ -13,7 +13,7 @@
role_name = "sentient disease"
/datum/round_event/ghost_role/sentient_disease/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, ROLE_ALIEN)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /obj/structure/sign/warning/biohazard, role_name_text = role_name)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/slaughter_event.dm b/code/modules/events/ghost_role/slaughter_event.dm
index 8cb2b729aa4..f4628344d2f 100644
--- a/code/modules/events/ghost_role/slaughter_event.dm
+++ b/code/modules/events/ghost_role/slaughter_event.dm
@@ -16,7 +16,7 @@
role_name = "slaughter demon"
/datum/round_event/ghost_role/slaughter/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, ROLE_ALIEN)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /mob/living/basic/demon/slaughter, role_name_text = role_name)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/space_dragon.dm b/code/modules/events/ghost_role/space_dragon.dm
index dd75a97a869..0a328f6dc8d 100644
--- a/code/modules/events/ghost_role/space_dragon.dm
+++ b/code/modules/events/ghost_role/space_dragon.dm
@@ -20,7 +20,7 @@
/datum/round_event/ghost_role/space_dragon/spawn_role()
- var/list/candidates = get_candidates(ROLE_SPACE_DRAGON, ROLE_SPACE_DRAGON)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SPACE_DRAGON, role = ROLE_SPACE_DRAGON, pic_source = /mob/living/basic/space_dragon)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/ghost_role/space_ninja.dm b/code/modules/events/ghost_role/space_ninja.dm
index 3aa7f5ee1ac..745a905b802 100644
--- a/code/modules/events/ghost_role/space_ninja.dm
+++ b/code/modules/events/ghost_role/space_ninja.dm
@@ -19,7 +19,7 @@
return MAP_ERROR
//selecting a candidate player
- var/list/candidates = get_candidates(ROLE_NINJA, ROLE_NINJA)
+ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_NINJA, role = ROLE_NINJA, pic_source = /obj/item/energy_katana)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index 3726da4c650..cad343f7deb 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -84,7 +84,7 @@
priority_announce("Santa is coming to town!", "Unknown Transmission")
/datum/round_event/santa/start()
- var/list/candidates = poll_ghost_candidates("Santa is coming to town! Do you want to be Santa?", poll_time=150)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Santa is coming to town! Do you want to be Santa?", poll_time = 15 SECONDS, pic_source = /obj/item/clothing/head/costume/santa, role_name_text = "santa")
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
santa = new /mob/living/carbon/human(pick(GLOB.blobstart))
diff --git a/code/modules/events/space_vines/vine_structure.dm b/code/modules/events/space_vines/vine_structure.dm
index 155a4c5c083..5788c33aa2b 100644
--- a/code/modules/events/space_vines/vine_structure.dm
+++ b/code/modules/events/space_vines/vine_structure.dm
@@ -7,7 +7,6 @@
anchored = TRUE
density = FALSE
layer = SPACEVINE_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
mouse_opacity = MOUSE_OPACITY_OPAQUE //Clicking anywhere on the turf is good enough
pass_flags = PASSTABLE | PASSGRILLE
max_integrity = 50
diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm
index 4a927c052ed..82994226142 100644
--- a/code/modules/events/wizard/imposter.dm
+++ b/code/modules/events/wizard/imposter.dm
@@ -13,7 +13,7 @@
if(!ishuman(M.current))
continue
var/mob/living/carbon/human/W = M.current
- var/list/candidates = poll_ghost_candidates("Would you like to be an imposter wizard?", ROLE_WIZARD)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Would you like to be an imposter wizard?", check_jobban = ROLE_WIZARD, pic_source = /obj/item/clothing/head/wizard, role_name_text = "imposter wizard")
if(!length(candidates))
return //Sad Trombone
var/mob/dead/observer/C = pick(candidates)
diff --git a/code/modules/food_and_drinks/machinery/food_cart.dm b/code/modules/food_and_drinks/machinery/food_cart.dm
index b7a47b11d62..da92d040f27 100644
--- a/code/modules/food_and_drinks/machinery/food_cart.dm
+++ b/code/modules/food_and_drinks/machinery/food_cart.dm
@@ -8,7 +8,7 @@
anchored = FALSE
use_power = NO_POWER_USE
req_access = list(ACCESS_KITCHEN)
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
var/unpacked = FALSE
var/obj/machinery/griddle/stand/cart_griddle
var/obj/machinery/smartfridge/food/cart_smartfridge
@@ -137,4 +137,3 @@
icon = 'icons/obj/fluff/3x3.dmi'
icon_state = "stand"
layer = ABOVE_MOB_LAYER//big mobs will still go over the tent, this is fine and cool
- plane = GAME_PLANE_UPPER
diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm
index 5333643e955..0471f5e92ef 100644
--- a/code/modules/food_and_drinks/machinery/microwave.dm
+++ b/code/modules/food_and_drinks/machinery/microwave.dm
@@ -418,8 +418,8 @@
return TRUE
return ..()
- if(vampire_charging_capable && istype(item, /obj/item/modular_computer/pda) && ingredients.len > 0)
- balloon_alert(user, "max 1 pda!")
+ if(vampire_charging_capable && istype(item, /obj/item/modular_computer) && ingredients.len > 0)
+ balloon_alert(user, "max 1 device!")
return FALSE
if(istype(item, /obj/item/storage))
@@ -577,7 +577,7 @@
if(cell_powered && cell?.charge < TIER_1_CELL_CHARGE_RATE * efficiency)
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
- balloon_alert(cooker, "replace cell!")
+ balloon_alert(cooker, "no power draw!")
return
if(cooker && HAS_TRAIT(cooker, TRAIT_CURSED) && prob(7))
@@ -593,7 +593,7 @@
for(var/atom/movable/potential_fooditem as anything in ingredients)
if(IS_EDIBLE(potential_fooditem))
non_food_ingedients--
- if(istype(potential_fooditem, /obj/item/modular_computer/pda) && prob(75))
+ if(istype(potential_fooditem, /obj/item/modular_computer) && prob(75))
pda_failure = TRUE
notify_ghosts(
"[cooker] has overheated their PDA!",
@@ -610,11 +610,13 @@
start(cooker)
/obj/machinery/microwave/proc/wzhzhzh()
+ if(cell_powered && !isnull(cell))
+ if(!cell.use(TIER_1_CELL_CHARGE_RATE * efficiency))
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ return
+
visible_message(span_notice("\The [src] turns on."), null, span_hear("You hear a microwave humming."))
operating = TRUE
- if(cell_powered && !isnull(cell))
- cell.use(TIER_1_CELL_CHARGE_RATE * efficiency)
-
set_light(l_range = 1.5, l_power = 1.2, l_on = TRUE)
soundloop.start()
update_appearance()
@@ -764,7 +766,7 @@
* * cooker - The mob that initiated the cook cycle, can be null if no apparent mob triggered it (such as via emp)
*/
/obj/machinery/microwave/proc/vampire(mob/cooker)
- var/obj/item/modular_computer/pda/vampire_pda = LAZYACCESS(ingredients, 1)
+ var/obj/item/modular_computer/vampire_pda = LAZYACCESS(ingredients, 1)
if(isnull(vampire_pda))
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
after_finish_loop()
@@ -796,7 +798,7 @@
// We should only be charging PDAs
for(var/atom/movable/potential_item as anything in ingredients)
- if(!istype(potential_item, /obj/item/modular_computer/pda))
+ if(!istype(potential_item, /obj/item/modular_computer))
balloon_alert(cooker, "pda only!")
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
eject()
@@ -817,7 +819,7 @@
pre_fail()
return
- if(!vampire_charge_amount || !length(ingredients) || (!isnull(cell) && !cell.charge) || vampire_charge_amount < 25)
+ if(!vampire_charge_amount || !length(ingredients) || isnull(cell) || !cell.charge || vampire_charge_amount < 25)
vampire_cell = null
charge_loop_finish(cooker)
return
diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm
index 046e835c50c..1b95c69b7ae 100644
--- a/code/modules/hydroponics/grown/towercap.dm
+++ b/code/modules/hydroponics/grown/towercap.dm
@@ -146,7 +146,7 @@
build_stab_overlay()
/obj/structure/punji_sticks/proc/build_stab_overlay()
- stab_overlay = mutable_appearance(icon, "[icon_state]_stab", layer = ABOVE_MOB_LAYER, offset_spokesman = src, plane = GAME_PLANE_FOV_HIDDEN)
+ stab_overlay = mutable_appearance(icon, "[icon_state]_stab", layer = ABOVE_MOB_LAYER)
/obj/structure/punji_sticks/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
. = ..()
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 72f897106dc..d1eb12a4ea9 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -1159,7 +1159,7 @@
circuit = null
density = FALSE
use_power = NO_POWER_USE
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
unwrenchable = FALSE
self_sustaining_overlay_icon_state = null
maxnutri = 15
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index a6e324e53b8..e206fe136ac 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -103,7 +103,7 @@
/// List of family heirlooms this job can get with the family heirloom quirk. List of types.
var/list/family_heirlooms
- /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS)
+ /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS | JOB_HEAD_OF_STAFF)
var/job_flags = NONE
/// Multiplier for general usage of the voice of god.
@@ -533,7 +533,7 @@
if(!player_client)
return // Disconnected while checking for the appearance ban.
- var/require_human = CONFIG_GET(flag/enforce_human_authority) && (job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ var/require_human = CONFIG_GET(flag/enforce_human_authority) && (job.job_flags & JOB_HEAD_OF_STAFF)
if(require_human)
var/all_authority_require_human = CONFIG_GET(flag/enforce_human_authority_on_everyone)
if(!all_authority_require_human && job.ignore_human_authority)
diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm
index 89e7d04742c..fce0aade6ca 100644
--- a/code/modules/jobs/job_types/assistant.dm
+++ b/code/modules/jobs/job_types/assistant.dm
@@ -48,14 +48,16 @@ Assistant
/datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/target)
..()
+ give_hat()
+ give_jumpsuit(target)
+
+/datum/outfit/job/assistant/proc/give_hat()
for(var/holidayname in GLOB.holidays)
var/datum/holiday/holiday_today = GLOB.holidays[holidayname]
var/obj/item/special_hat = holiday_today.holiday_hat
if(prob(HOLIDAY_HAT_CHANCE) && !isnull(special_hat) && isnull(head))
head = special_hat
- give_jumpsuit(target)
-
/datum/outfit/job/assistant/proc/give_jumpsuit(mob/living/carbon/human/target)
var/static/jumpsuit_number = 0
jumpsuit_number += 1
@@ -81,6 +83,8 @@ Assistant
/datum/outfit/job/assistant/consistent
name = "Assistant - Consistent"
+/datum/outfit/job/assistant/consistent/give_hat()
+ return
/datum/outfit/job/assistant/consistent/give_jumpsuit(mob/living/carbon/human/target)
uniform = /obj/item/clothing/under/color/grey
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 7b80262d4a8..2e31af7923a 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -23,6 +23,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_CMD // SKYRAT EDIT - Original: paycheck_department = ACCOUNT_SEC
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_CAPTAIN
@@ -42,7 +43,7 @@
/obj/item/skillchip/sabrage = 5,
)
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
rpg_title = "Star Duke"
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/cargo_gorilla.dm b/code/modules/jobs/job_types/cargo_gorilla.dm
new file mode 100644
index 00000000000..96a79e3a4d3
--- /dev/null
+++ b/code/modules/jobs/job_types/cargo_gorilla.dm
@@ -0,0 +1,50 @@
+/datum/job/cargo_gorilla
+ title = JOB_CARGO_GORILLA
+ description = "Assist the supply department by moving freight and disposing of unwanted fruits."
+ department_head = list(JOB_QUARTERMASTER)
+ faction = FACTION_STATION
+ total_positions = 0
+ spawn_positions = 0
+ supervisors = SUPERVISOR_QM
+ spawn_type = /mob/living/basic/gorilla/cargorilla
+ config_tag = "CARGO_GORILLA"
+ random_spawns_possible = FALSE
+ display_order = JOB_DISPLAY_ORDER_CARGO_GORILLA
+ departments_list = list(/datum/job_department/cargo)
+ mail_goodies = list(
+ /obj/item/food/grown/banana = 1,
+ )
+ rpg_title = "Beast of Burden"
+ allow_bureaucratic_error = FALSE
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_CANNOT_OPEN_SLOTS | JOB_HIDE_WHEN_EMPTY | JOB_LATEJOIN_ONLY
+
+/datum/job/cargo_gorilla/get_roundstart_spawn_point()
+ if (length(GLOB.gorilla_start))
+ return pick(GLOB.gorilla_start)
+ return ..()
+
+/datum/job/cargo_gorilla/get_spawn_mob(client/player_client, atom/spawn_point)
+ if (!player_client)
+ return
+ var/mob/living/the_big_man = new spawn_type(get_turf(spawn_point))
+ the_big_man.fully_replace_character_name(the_big_man.real_name, pick(GLOB.cargorilla_names))
+ return the_big_man
+
+/datum/job/cargo_gorilla/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ // Gorilla with a wage, what's he buyin?
+ var/datum/bank_account/bank_account = new(spawned.real_name, src)
+ bank_account.payday(STARTING_PAYCHECKS, TRUE)
+ bank_account.replaceable = FALSE
+ spawned.add_mob_memory(/datum/memory/key/account, remembered_id = bank_account.account_id)
+
+ var/obj/item/card/id/advanced/cargo_gorilla/gorilla_id = new(spawned.loc)
+ gorilla_id.registered_name = spawned.name
+ gorilla_id.update_label()
+ gorilla_id.registered_account = bank_account
+ bank_account.bank_cards += gorilla_id
+ spawned.put_in_hands(gorilla_id, del_on_fail = TRUE)
+
+ to_chat(spawned, span_boldnotice("You are Cargorilla, a pacifist friend of the station and carrier of freight."))
+ to_chat(spawned, span_notice("You can pick up crates by clicking on them, and drop them by clicking on the ground."))
+ spawned.mind.special_role = "Cargorilla"
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
index 3e752910a29..0dffeda5781 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
@@ -10,7 +10,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and
desc = "This shard seems to be directly linked to some sinister entity. It might be your god! It also gives you a really horrible rash when you hold onto it for too long."
items_to_create = list(/obj/item/vorpalscythe)
-/obj/item/organ/internal/cyberimp/arm/shard/scythe/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/internal/cyberimp/arm/shard/scythe/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(receiver.mind)
ADD_TRAIT(receiver.mind, TRAIT_MORBID, ORGAN_TRAIT)
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index fcb26731ca1..fd568464bb6 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -27,6 +27,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_ENG
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ENGINEER_METABOLISM, TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_CHIEF_ENGINEER
@@ -43,7 +44,7 @@
/obj/effect/spawner/random/engineering/tool_advanced = 3
)
rpg_title = "Head Crystallomancer"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index 998c19a215d..e20ef7c19ed 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -27,6 +27,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_MED
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_MEDICAL_METABOLISM, TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER
@@ -40,7 +41,7 @@
)
family_heirlooms = list(/obj/item/storage/medkit/ancient/heirloom, /obj/item/scalpel, /obj/item/hemostat, /obj/item/circular_saw, /obj/item/retractor, /obj/item/cautery, /obj/item/statuebust/hippocratic)
rpg_title = "High Cleric"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index 7cab0feab96..1cd3e99bbef 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -28,6 +28,7 @@
paycheck_department = ACCOUNT_SRV
bounty_types = CIV_JOB_RANDOM
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL
@@ -39,7 +40,7 @@
family_heirlooms = list(/obj/item/reagent_containers/cup/glass/trophy/silver_cup)
rpg_title = "Guild Questgiver"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 5fa3fcaa948..cc41ee7d01f 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -24,6 +24,7 @@
/datum/job_department/command,
)
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM, TRAIT_ROYAL_METABOLISM)
paycheck = PAYCHECK_COMMAND
@@ -34,7 +35,7 @@
family_heirlooms = list(/obj/item/book/manual/wiki/security_space_law)
rpg_title = "Guard Leader"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index cf4c9f65910..4e49507864a 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -23,7 +23,7 @@
family_heirlooms = list(/obj/item/pen/blue)
rpg_title = "Defeated Miniboss"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | JOB_CANNOT_OPEN_SLOTS & ~JOB_REOPEN_ON_ROUNDSTART_LOSS
/datum/job/prisoner/New()
. = ..()
diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm
index 0ca4b0a1052..858ce8b6455 100644
--- a/code/modules/jobs/job_types/quartermaster.dm
+++ b/code/modules/jobs/job_types/quartermaster.dm
@@ -20,6 +20,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_CAR
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM) // finally upgraded
display_order = JOB_DISPLAY_ORDER_QUARTERMASTER
@@ -33,7 +34,7 @@
/obj/item/circuitboard/machine/emitter = 3
)
rpg_title = "Steward"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
ignore_human_authority = TRUE
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index f2994388838..1142ba033ff 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -28,6 +28,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_SCI
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM, TRAIT_BALLMER_SCIENTIST)
display_order = JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR
@@ -41,7 +42,7 @@
family_heirlooms = list(/obj/item/toy/plush/slimeplushie)
rpg_title = "Archmagister"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/personal_ai.dm b/code/modules/jobs/job_types/spawner/personal_ai.dm
similarity index 100%
rename from code/modules/jobs/job_types/personal_ai.dm
rename to code/modules/jobs/job_types/spawner/personal_ai.dm
diff --git a/code/modules/jobs/job_types/positronic_brain.dm b/code/modules/jobs/job_types/spawner/positronic_brain.dm
similarity index 100%
rename from code/modules/jobs/job_types/positronic_brain.dm
rename to code/modules/jobs/job_types/spawner/positronic_brain.dm
diff --git a/code/modules/jobs/job_types/servant_golem.dm b/code/modules/jobs/job_types/spawner/servant_golem.dm
similarity index 100%
rename from code/modules/jobs/job_types/servant_golem.dm
rename to code/modules/jobs/job_types/spawner/servant_golem.dm
diff --git a/code/modules/jobs/job_types/station_trait/bridge_assistant.dm b/code/modules/jobs/job_types/station_trait/bridge_assistant.dm
new file mode 100644
index 00000000000..ebc1af0ee1a
--- /dev/null
+++ b/code/modules/jobs/job_types/station_trait/bridge_assistant.dm
@@ -0,0 +1,79 @@
+/datum/job/bridge_assistant
+ title = JOB_BRIDGE_ASSISTANT
+ description = "Watch over the Bridge, command its consoles, and spend your days brewing coffee for higher-ups."
+ auto_deadmin_role_flags = DEADMIN_POSITION_HEAD //not really a head but close enough
+ department_head = list(JOB_CAPTAIN)
+ faction = FACTION_STATION
+ total_positions = 0
+ spawn_positions = 0
+ supervisors = "the Captain, and in non-Bridge related situations the other heads"
+ minimal_player_age = 7
+ exp_requirements = 300
+ exp_required_type = EXP_TYPE_CREW
+ exp_granted_type = EXP_TYPE_CREW
+ config_tag = "BRIDGE_ASSISTANT"
+
+ outfit = /datum/outfit/job/bridge_assistant
+ plasmaman_outfit = /datum/outfit/plasmaman/bridge_assistant
+
+ paycheck = PAYCHECK_CREW
+ paycheck_department = ACCOUNT_CIV
+
+ liver_traits = list(TRAIT_PRETENDER_ROYAL_METABOLISM)
+
+ display_order = JOB_DISPLAY_ORDER_BRIDGE_ASSISTANT
+ departments_list = list(/datum/job_department/command)
+
+ family_heirlooms = list(/obj/item/banner/command/mundane)
+
+ mail_goodies = list(
+ /obj/item/storage/fancy/cigarettes = 1,
+ /obj/item/pen/fountain = 1,
+ )
+ rpg_title = "Royal Guard"
+ allow_bureaucratic_error = FALSE
+ job_flags = STATION_JOB_FLAGS | STATION_TRAIT_JOB_FLAGS
+ ignore_human_authority = TRUE
+
+/datum/job/bridge_assistant/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ ADD_TRAIT(spawned, TRAIT_NO_TWOHANDING, JOB_TRAIT)
+
+/datum/job/bridge_assistant/get_roundstart_spawn_point()
+ var/list/chair_turfs = list()
+ var/list/possible_turfs = list()
+ var/area/bridge = GLOB.areas_by_type[/area/station/command/bridge]
+ if(isnull(bridge))
+ return ..() //if no bridge, spawn on the arrivals shuttle (but also what the fuck)
+ for(var/turf/possible_turf as anything in bridge.get_contained_turfs())
+ if(possible_turf.is_blocked_turf())
+ continue
+ if(locate(/obj/structure/chair) in possible_turf)
+ chair_turfs += possible_turf
+ continue
+ possible_turfs += possible_turf
+ if(length(chair_turfs))
+ return pick(chair_turfs) //prioritize turfs with a chair
+ if(length(possible_turfs))
+ return pick(possible_turfs) //if none, just pick a random turf in the bridge
+ return ..() //if the bridge has no turfs, spawn on the arrivals shuttle
+
+/datum/outfit/job/bridge_assistant
+ name = "Bridge Assistant"
+ jobtype = /datum/job/bridge_assistant
+
+ id_trim = /datum/id_trim/job/bridge_assistant
+ backpack_contents = list(
+ /obj/item/modular_computer/pda/bridge_assistant = 1,
+ )
+
+ uniform = /obj/item/clothing/under/trek/command/next
+ neck = /obj/item/clothing/neck/large_scarf/blue
+ belt = /obj/item/storage/belt/utility/full/inducer
+ ears = /obj/item/radio/headset/headset_com
+ glasses = /obj/item/clothing/glasses/sunglasses
+ gloves = /obj/item/clothing/gloves/fingerless
+ head = /obj/item/clothing/head/soft/black
+ shoes = /obj/item/clothing/shoes/laceup
+ l_pocket = /obj/item/gun/energy/e_gun/mini
+ r_pocket = /obj/item/assembly/flash/handheld
diff --git a/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm b/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm
new file mode 100644
index 00000000000..87363bf9b7b
--- /dev/null
+++ b/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm
@@ -0,0 +1,50 @@
+/datum/job/cargo_gorilla
+ title = JOB_CARGO_GORILLA
+ description = "Assist the supply department by moving freight and disposing of unwanted fruits."
+ department_head = list(JOB_QUARTERMASTER)
+ faction = FACTION_STATION
+ total_positions = 0
+ spawn_positions = 0
+ supervisors = SUPERVISOR_QM
+ spawn_type = /mob/living/basic/gorilla/cargorilla
+ config_tag = "CARGO_GORILLA"
+ random_spawns_possible = FALSE
+ display_order = JOB_DISPLAY_ORDER_CARGO_GORILLA
+ departments_list = list(/datum/job_department/cargo)
+ mail_goodies = list(
+ /obj/item/food/grown/banana = 1,
+ )
+ rpg_title = "Beast of Burden"
+ allow_bureaucratic_error = FALSE
+ job_flags = STATION_TRAIT_JOB_FLAGS | JOB_ANNOUNCE_ARRIVAL | JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK
+
+/datum/job/cargo_gorilla/get_roundstart_spawn_point()
+ if (length(GLOB.gorilla_start))
+ return pick(GLOB.gorilla_start)
+ return ..()
+
+/datum/job/cargo_gorilla/get_spawn_mob(client/player_client, atom/spawn_point)
+ if (!player_client)
+ return
+ var/mob/living/the_big_man = new spawn_type(get_turf(spawn_point))
+ the_big_man.fully_replace_character_name(the_big_man.real_name, pick(GLOB.cargorilla_names))
+ return the_big_man
+
+/datum/job/cargo_gorilla/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ // Gorilla with a wage, what's he buyin?
+ var/datum/bank_account/bank_account = new(spawned.real_name, src)
+ bank_account.payday(STARTING_PAYCHECKS, TRUE)
+ bank_account.replaceable = FALSE
+ spawned.add_mob_memory(/datum/memory/key/account, remembered_id = bank_account.account_id)
+
+ var/obj/item/card/id/advanced/cargo_gorilla/gorilla_id = new(spawned.loc)
+ gorilla_id.registered_name = spawned.name
+ gorilla_id.update_label()
+ gorilla_id.registered_account = bank_account
+ bank_account.bank_cards += gorilla_id
+ spawned.put_in_hands(gorilla_id, del_on_fail = TRUE)
+
+ to_chat(spawned, span_boldnotice("You are Cargorilla, a pacifist friend of the station and carrier of freight."))
+ to_chat(spawned, span_notice("You can pick up crates by clicking on them, and drop them by clicking on the ground."))
+ spawned.mind.special_role = "Cargorilla"
diff --git a/code/modules/library/skill_learning/skillchip.dm b/code/modules/library/skill_learning/skillchip.dm
index a58fbec7c75..2ef7a20e680 100644
--- a/code/modules/library/skill_learning/skillchip.dm
+++ b/code/modules/library/skill_learning/skillchip.dm
@@ -106,7 +106,7 @@
return "Skillchip is not active."
// Should not happen. Holding brain is destroyed and the chip hasn't had its state set appropriately.
- if(QDELETED(holding_brain))
+ if(!holding_brain)
stack_trace("Skillchip's owner is null or qdeleted brain.")
return "Skillchip cannot detect viable brain."
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm b/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
index 75ac48c2467..f3b321b88b2 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
@@ -143,7 +143,6 @@
icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
icon_state = "watcher_baby"
layer = EDGED_TURF_LAYER // Don't render under lightbulbs
- plane = GAME_PLANE_UPPER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
pixel_y = 22
alpha = 0
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
index f2734bb7b05..6ce6056b477 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
@@ -81,7 +81,6 @@
icon = 'icons/effects/96x96.dmi'
icon_state = "gate_blocker"
layer = EDGED_TURF_LAYER
- plane = GAME_PLANE_UPPER
pixel_x = -32
pixel_y = -32
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
diff --git a/code/modules/mining/equipment/monster_organs/monster_organ.dm b/code/modules/mining/equipment/monster_organs/monster_organ.dm
index 699564c9ed7..8c1b031171f 100644
--- a/code/modules/mining/equipment/monster_organs/monster_organ.dm
+++ b/code/modules/mining/equipment/monster_organs/monster_organ.dm
@@ -69,7 +69,7 @@
deltimer(decay_timer)
return ..()
-/obj/item/organ/internal/monster_core/Insert(mob/living/carbon/target_carbon, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/monster_core/Insert(mob/living/carbon/target_carbon, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -83,7 +83,7 @@
target_carbon.visible_message(span_notice("[src] stabilizes as it's inserted."))
return TRUE
-/obj/item/organ/internal/monster_core/Remove(mob/living/carbon/target_carbon, special = 0)
+/obj/item/organ/internal/monster_core/Remove(mob/living/carbon/target_carbon, special, movement_flags)
if (!inert && !special)
owner.visible_message(span_notice("[src] rapidly decays as it's removed."))
go_inert()
diff --git a/code/modules/mining/equipment/monster_organs/rush_gland.dm b/code/modules/mining/equipment/monster_organs/rush_gland.dm
index 3554d67b2a6..b3932afdaab 100644
--- a/code/modules/mining/equipment/monster_organs/rush_gland.dm
+++ b/code/modules/mining/equipment/monster_organs/rush_gland.dm
@@ -21,11 +21,11 @@
if (owner.health <= HEALTH_DANGER_ZONE)
trigger_organ_action()
-/obj/item/organ/internal/monster_core/rush_gland/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/monster_core/rush_gland/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
RegisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED, PROC_REF(trigger_organ_action))
-/obj/item/organ/internal/monster_core/rush_gland/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/monster_core/rush_gland/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED)
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index c04ad5c6ff1..95ee1015a95 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -201,7 +201,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/survival_pod/spawne
light_color = COLOR_VERY_PALE_LIME_GREEN
max_n_of_items = 10
pixel_y = -4
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
/obj/machinery/smartfridge/survival_pod/Initialize(mapload)
AddElement(/datum/element/update_icon_blocker)
diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm
index 0b42fd23212..006c920d4b9 100644
--- a/code/modules/mining/fulton.dm
+++ b/code/modules/mining/fulton.dm
@@ -243,3 +243,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
/obj/effect/extraction_holder/singularity_pull()
return
+
+/obj/item/extraction_pack/syndicate
+ name = "syndicate fulton extraction pack"
+ can_use_indoors = TRUE
diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm
deleted file mode 100644
index 6a2c84622a2..00000000000
--- a/code/modules/mob/dead/crew_manifest.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/datum/crew_manifest
-
-/datum/crew_manifest/ui_state(mob/user)
- return GLOB.always_state
-
-/datum/crew_manifest/ui_status(mob/user, datum/ui_state/state)
- return (isnewplayer(user) || isobserver(user) || isAI(user) || ispAI(user)) ? UI_INTERACTIVE : UI_CLOSE
-
-/datum/crew_manifest/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if (!ui)
- ui = new(user, src, "CrewManifest")
- ui.open()
-
-/datum/crew_manifest/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
- . = ..()
- if(.)
- return
-
-/datum/crew_manifest/ui_data(mob/user)
- var/list/positions = list()
- for(var/datum/job_department/department as anything in SSjob.joinable_departments)
- var/open = 0
- var/list/exceptions = list()
- for(var/datum/job/job as anything in department.department_jobs)
- if(job.total_positions == -1)
- exceptions += job.title
- continue
- var/open_slots = job.total_positions - job.current_positions
- if(open_slots < 1)
- continue
- open += open_slots
- positions[department.department_name] = list("exceptions" = exceptions, "open" = open)
-
- return list(
- "manifest" = GLOB.manifest.get_manifest(),
- "positions" = positions
- )
diff --git a/code/modules/mob/dead/new_player/latejoin_menu.dm b/code/modules/mob/dead/new_player/latejoin_menu.dm
index 059a5014d0c..d2db3aa34b7 100644
--- a/code/modules/mob/dead/new_player/latejoin_menu.dm
+++ b/code/modules/mob/dead/new_player/latejoin_menu.dm
@@ -80,6 +80,8 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
)
if(job_availability != JOB_AVAILABLE)
+ if (job_datum.job_flags & JOB_HIDE_WHEN_EMPTY)
+ continue
job_data["unavailable_reason"] = get_job_unavailable_error_message(job_availability, job_datum.title)
if(job_datum.total_positions < 0)
@@ -95,6 +97,7 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
/datum/latejoin_menu/ui_static_data(mob/user)
var/list/departments = list()
+ var/mob/dead/new_player/owner = user
for(var/datum/job_department/department as anything in SSjob.joinable_departments)
var/list/department_jobs = list()
@@ -108,6 +111,8 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
//Jobs under multiple departments should only be displayed if this is their first department or the command department
if(LAZYLEN(job_datum.departments_list) > 1 && job_datum.departments_list[1] != department.type && !(job_datum.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
continue
+ if((job_datum.job_flags & JOB_HIDE_WHEN_EMPTY) && owner.IsJobUnavailable(job_datum.title, latejoin = TRUE) != JOB_AVAILABLE)
+ continue
var/list/job_data = list(
"command" = !!(job_datum.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND),
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 7f3e34b9c0b..e8920a0bb90 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -268,10 +268,10 @@
if(SSshuttle.emergency)
switch(SSshuttle.emergency.mode)
if(SHUTTLE_RECALL, SHUTTLE_IDLE)
- SSticker.mode.make_antag_chance(humanc)
+ SSdynamic.make_antag_chance(humanc)
if(SHUTTLE_CALL)
if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergency_call_time)*0.5)
- SSticker.mode.make_antag_chance(humanc)
+ SSdynamic.make_antag_chance(humanc)
if((job.job_flags & JOB_ASSIGN_QUIRKS) && humanc && CONFIG_GET(flag/roundstart_traits))
SSquirks.AssignQuirks(humanc, humanc.client)
@@ -339,11 +339,7 @@
return
client.crew_manifest_delay = world.time + (1 SECONDS)
- if(!GLOB.crew_manifest_tgui)
- GLOB.crew_manifest_tgui = new /datum/crew_manifest(src)
-
-
- GLOB.crew_manifest_tgui.ui_interact(src)
+ GLOB.manifest.ui_interact(src)
/mob/dead/new_player/Move()
return 0
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index af03f6c8e09..3dd88935437 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -729,10 +729,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return
client.crew_manifest_delay = world.time + (1 SECONDS)
- if(!GLOB.crew_manifest_tgui)
- GLOB.crew_manifest_tgui = new /datum/crew_manifest(src)
-
- GLOB.crew_manifest_tgui.ui_interact(src)
+ GLOB.manifest.ui_interact(src)
//this is called when a ghost is drag clicked to something.
/mob/dead/observer/MouseDrop(atom/over)
@@ -988,7 +985,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(is_secret_level(mob_eye.z) && !client?.holder)
set_sight(null) //we dont want ghosts to see through walls in secret areas
RegisterSignal(mob_eye, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_observing_z_changed))
- if(mob_eye.hud_used && src != usr) // can't view your own inventory and hud but you're either A: permanently ghosted out. Or B: dead and it barely matters.
+ if(mob_eye.hud_used)
client.clear_screen()
LAZYOR(mob_eye.observers, src)
mob_eye.hud_used.show_hud(mob_eye.hud_used.hud_version, src)
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 4660a1b0546..e261c8ddb01 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -485,6 +485,19 @@
DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(execute_quick_equip)))
+/// Safely drop everything, without deconstructing the mob
+/mob/proc/drop_everything(del_on_drop, force, del_if_nodrop)
+ . = list()
+ for(var/obj/item/item in src)
+ if(!dropItemToGround(item, force))
+ if(del_if_nodrop && !(item.item_flags & ABSTRACT))
+ qdel(item)
+ if(del_on_drop)
+ qdel(item)
+ //Anything thats not deleted and isn't in the mob, so everything that is succesfully dropped to the ground, is returned
+ if(!QDELETED(item) && !(item in src))
+ . += item
+
///proc extender of [/mob/verb/quick_equip] used to make the verb queuable if the server is overloaded
/mob/proc/execute_quick_equip()
var/obj/item/I = get_active_held_item()
@@ -544,14 +557,21 @@
..() //Don't redraw hands until we have organs for them
//GetAllContents that is reasonable and not stupid
-/mob/living/carbon/proc/get_all_gear()
+/mob/living/proc/get_all_gear()
var/list/processing_list = get_equipped_items(include_pockets = TRUE, include_accessories = TRUE) + held_items
list_clear_nulls(processing_list) // handles empty hands
var/i = 0
- while(i < length(processing_list) )
+ while(i < length(processing_list))
var/atom/A = processing_list[++i]
if(A.atom_storage)
var/list/item_stuff = list()
A.atom_storage.return_inv(item_stuff)
processing_list += item_stuff
return processing_list
+
+/// Returns a list of things that the provided mob has, including any storage-capable implants.
+/mob/living/proc/gather_belongings()
+ var/list/belongings = get_all_gear()
+ for (var/obj/item/implant/storage/internal_bag in implants)
+ belongings += internal_bag.contents
+ return belongings
diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm
index 88f61948f8c..81d69ce7701 100644
--- a/code/modules/mob/living/basic/bots/_bots.dm
+++ b/code/modules/mob/living/basic/bots/_bots.dm
@@ -809,3 +809,6 @@ GLOBAL_LIST_INIT(command_strings, list(
/mob/living/basic/bot/proc/after_attacked(datum/source, atom/attacker, attack_flags)
if(attack_flags & ATTACKER_DAMAGING_ATTACK)
do_sparks(number = 5, cardinal_only = TRUE, source = src)
+
+/mob/living/basic/bot/spawn_gibs(drop_bitflags = NONE)
+ new /obj/effect/gibspawner/robot(drop_location(), src)
diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
index 1e17938c5fd..64cff6780df 100644
--- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
+++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
@@ -158,31 +158,4 @@
ADD_TRAIT(src, TRAIT_PACIFISM, INNATE_TRAIT)
AddComponent(/datum/component/crate_carrier)
-/mob/living/basic/gorilla/cargorilla/death(gibbed)
- var/datum/component/potential_component = GetComponent(/datum/component/ghost_direct_control)
- if(!QDELETED(potential_component))
- qdel(potential_component)
- return ..()
-
-/**
- * Poll ghosts for control of the gorilla. Not added in init because we only want to poll when the round starts.
- * Preferably in future we can replace this with a popup on the lobby to queue to become a gorilla.
- */
-/mob/living/basic/gorilla/cargorilla/proc/poll_for_gorilla()
- AddComponent(\
- /datum/component/ghost_direct_control,\
- poll_candidates = TRUE,\
- poll_length = 30 SECONDS,\
- role_name = "Cargorilla",\
- assumed_control_message = "You are Cargorilla, a pacifist friend of the station and carrier of freight.",\
- poll_ignore_key = POLL_IGNORE_CARGORILLA,\
- after_assumed_control = CALLBACK(src, PROC_REF(became_player_controlled)),\
- )
-
-/// Called once a ghost assumes control
-/mob/living/basic/gorilla/cargorilla/proc/became_player_controlled()
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/cargo_technician))
- mind.special_role = "Cargorilla"
- to_chat(src, span_notice("You can pick up crates by clicking on them, and drop them by clicking on the ground."))
-
#undef GORILLA_HANDS_LAYER
diff --git a/code/modules/mob/living/basic/guardian/guardian_creator.dm b/code/modules/mob/living/basic/guardian/guardian_creator.dm
index 7ebc9737b41..d01383427df 100644
--- a/code/modules/mob/living/basic/guardian/guardian_creator.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_creator.dm
@@ -87,11 +87,13 @@ GLOBAL_LIST_INIT(guardian_radial_images, setup_guardian_radial())
used = TRUE
to_chat(user, use_message)
var/guardian_type_name = random ? "Random" : capitalize(initial(guardian_path.creator_name))
- var/list/mob/dead/observer/candidates = poll_ghost_candidates(
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(
"Do you want to play as [user.real_name]'s [guardian_type_name] [mob_name]?",
- jobban_type = ROLE_PAI,
+ check_jobban = ROLE_PAI,
poll_time = 10 SECONDS,
ignore_category = POLL_IGNORE_HOLOPARASITE,
+ pic_source = src,
+ role_name_text = "guardian spirit",
)
if(LAZYLEN(candidates))
var/mob/dead/observer/candidate = pick(candidates)
diff --git a/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm b/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm
index 31426475754..b2ac9d66e8c 100644
--- a/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm
@@ -92,4 +92,3 @@
/obj/effect/ebeam/chain
name = "lightning chain"
layer = LYING_MOB_LAYER
- plane = GAME_PLANE_FOV_HIDDEN
diff --git a/code/modules/mob/living/basic/guardian/guardian_verbs.dm b/code/modules/mob/living/basic/guardian/guardian_verbs.dm
index 02d1fd1ed3a..7377ad15282 100644
--- a/code/modules/mob/living/basic/guardian/guardian_verbs.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_verbs.dm
@@ -166,7 +166,7 @@
return FALSE
to_chat(owner, span_holoparasite("You attempt to reset [span_bold(chosen_guardian.real_name)]'s personality..."))
- var/list/mob/dead/observer/ghost_candidates = poll_ghost_candidates("Do you want to play as [owner.real_name]'s [chosen_guardian.theme.name]?", ROLE_PAI, FALSE, 100)
+ var/list/mob/dead/observer/ghost_candidates = SSpolling.poll_ghost_candidates("Do you want to play as [owner.real_name]'s [chosen_guardian.theme.name]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, pic_source = chosen_guardian, role_name_text = chosen_guardian.theme.name)
if (!LAZYLEN(ghost_candidates))
to_chat(owner, span_holoparasite("Your attempt to reset the personality of \
[span_bold(chosen_guardian.real_name)] appears to have failed... \
diff --git a/code/modules/mob/living/basic/heretic/star_gazer.dm b/code/modules/mob/living/basic/heretic/star_gazer.dm
index 8f454fd7f78..e503cd65aea 100644
--- a/code/modules/mob/living/basic/heretic/star_gazer.dm
+++ b/code/modules/mob/living/basic/heretic/star_gazer.dm
@@ -34,7 +34,6 @@
can_buckle_to = FALSE
mob_size = MOB_SIZE_HUGE
layer = LARGE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
ai_controller = /datum/ai_controller/basic_controller/star_gazer
diff --git a/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm b/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm
index 8ff9edd1476..efc09410db4 100644
--- a/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm
+++ b/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm
@@ -42,7 +42,6 @@
icon = 'icons/obj/weapons/guns/projectiles.dmi'
icon_state = "leaper_bubble_pop"
layer = ABOVE_ALL_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
duration = 3 SECONDS
/obj/effect/temp_visual/leaper_projectile_impact/Initialize(mapload)
@@ -160,7 +159,6 @@
icon = 'icons/obj/weapons/guns/projectiles.dmi'
icon_state = "leaper"
layer = ABOVE_ALL_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
duration = 1 SECONDS
/obj/effect/temp_visual/blood_drop_rising/Initialize(mapload)
@@ -172,7 +170,6 @@
icon = 'icons/effects/effects.dmi'
icon_state = "blood_effect_falling"
layer = ABOVE_ALL_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
duration = 0.7 SECONDS
pixel_y = 60
diff --git a/code/modules/mob/living/basic/jungle/venus_human_trap.dm b/code/modules/mob/living/basic/jungle/venus_human_trap.dm
index b59ec233bd0..f9306afe20e 100644
--- a/code/modules/mob/living/basic/jungle/venus_human_trap.dm
+++ b/code/modules/mob/living/basic/jungle/venus_human_trap.dm
@@ -16,7 +16,6 @@
icon = 'icons/mob/spacevines.dmi'
icon_state = "bud0"
layer = SPACEVINE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
opacity = FALSE
canSmoothWith = null
smoothing_flags = NONE
@@ -135,7 +134,6 @@
health_doll_icon = "venus_human_trap"
mob_biotypes = MOB_ORGANIC | MOB_PLANT
layer = SPACEVINE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
health = 100
maxHealth = 100
obj_damage = 60
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion.dm b/code/modules/mob/living/basic/lavaland/legion/legion.dm
index 080f397226e..3a18a0703e6 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion.dm
@@ -83,7 +83,7 @@
return
// Congratulations you have won a special prize: cancer
var/obj/item/organ/internal/legion_tumour/cancer = new()
- cancer.Insert(consumed, special = TRUE, drop_if_replaced = FALSE)
+ cancer.Insert(consumed, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/// A Legion which only drops skeletons instead of corpses which might have fun loot, so it cannot be farmed
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
index 078af57de2a..55c1e6426b3 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
@@ -50,7 +50,7 @@
animate(transform = matrix(), time = 0.5 SECONDS / speed_divider, easing = SINE_EASING | EASE_IN)
animate(transform = matrix(), time = 2 SECONDS / speed_divider)
-/obj/item/organ/internal/legion_tumour/Remove(mob/living/carbon/egg_owner, special)
+/obj/item/organ/internal/legion_tumour/Remove(mob/living/carbon/egg_owner, special, movement_flags)
. = ..()
stage = 0
elapsed_time = 0
diff --git a/code/modules/mob/living/basic/pets/sloth.dm b/code/modules/mob/living/basic/pets/sloth.dm
index 0b1546ccf93..125a3a7b97d 100644
--- a/code/modules/mob/living/basic/pets/sloth.dm
+++ b/code/modules/mob/living/basic/pets/sloth.dm
@@ -44,11 +44,12 @@ GLOBAL_DATUM(cargo_sloth, /mob/living/basic/sloth)
AddElement(/datum/element/ai_retaliate)
AddComponent(/datum/component/tree_climber)
- if(!mapload || isnull(GLOB.cargo_sloth) || !is_station_level(z))
+ if(!mapload || !isnull(GLOB.cargo_sloth) || !is_station_level(z))
return
// If someone adds non-cargo sloths to maps we'll have a problem but we're fine for now
GLOB.cargo_sloth = src
+ GLOB.gorilla_start += get_turf(src)
/mob/living/basic/sloth/Destroy()
if(GLOB.cargo_sloth == src)
diff --git a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
index 8cdd7a6cf53..1cc7549f0ea 100644
--- a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
+++ b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
@@ -33,13 +33,13 @@
user.temporarilyRemoveItemFromInventory(src, TRUE)
src.Insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E
-/obj/item/organ/internal/heart/demon/on_insert(mob/living/carbon/heart_owner)
+/obj/item/organ/internal/heart/demon/on_mob_insert(mob/living/carbon/heart_owner)
. = ..()
// Gives a non-eat-people crawl to the new owner
var/datum/action/cooldown/spell/jaunt/bloodcrawl/crawl = new(heart_owner)
crawl.Grant(heart_owner)
-/obj/item/organ/internal/heart/demon/on_remove(mob/living/carbon/heart_owner, special = FALSE)
+/obj/item/organ/internal/heart/demon/on_mob_remove(mob/living/carbon/heart_owner, special = FALSE)
. = ..()
var/datum/action/cooldown/spell/jaunt/bloodcrawl/crawl = locate() in heart_owner.actions
qdel(crawl)
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm
index a9e17a9b305..ea153b03c06 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm
@@ -86,7 +86,7 @@
/// Handles giving the revenant a new client to control it
/obj/item/ectoplasm/revenant/proc/get_new_user()
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
- var/list/candidates = poll_candidates_for_mob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, ROLE_REVENANT, 5 SECONDS, revenant)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to be [revenant.name] (reforming)?", check_jobban = ROLE_REVENANT, role = ROLE_REVENANT, poll_time = 5 SECONDS, target_mob = revenant, pic_source = revenant)
if(!LAZYLEN(candidates))
message_admins("No candidates were found for the new revenant.")
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index bc0d2e3d8e8..0818532cc4d 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -286,7 +286,7 @@
return /datum/reagent/blood
/mob/living/carbon/human/get_blood_id()
- if(HAS_TRAIT(src, TRAIT_HUSK))
+ if(HAS_TRAIT(src, TRAIT_HUSK) || !dna)
return
if(check_holidays(APRIL_FOOLS) && is_clown_job(mind?.assigned_role))
return /datum/reagent/colorful_reagent
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 8f29a7d2be7..a613e2ad839 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -6,7 +6,6 @@
throw_speed = 3
throw_range = 5
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_BRAIN
organ_flags = ORGAN_ORGANIC | ORGAN_VITAL
@@ -47,7 +46,14 @@
// Brain size logic
transform = transform.Scale(brain_size)
-/obj/item/organ/internal/brain/Insert(mob/living/carbon/brain_owner, special = FALSE, drop_if_replaced = TRUE, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/examine()
+ . = ..()
+ if(brain_size < 1)
+ . += span_notice("It is a bit on the smaller side...")
+ if(brain_size > 1)
+ . += span_notice("It is bigger than average...")
+
+/obj/item/organ/internal/brain/mob_insert(mob/living/carbon/brain_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -55,7 +61,7 @@
name = initial(name)
// Special check for if you're trapped in a body you can't control because it's owned by a ling.
- if(brain_owner?.mind?.has_antag_datum(/datum/antagonist/changeling) && !no_id_transfer)
+ if(brain_owner?.mind?.has_antag_datum(/datum/antagonist/changeling) && !(movement_flags & NO_ID_TRANSFER))
if(brainmob && !(brain_owner.stat == DEAD || (HAS_TRAIT(brain_owner, TRAIT_DEATHCOMA))))
to_chat(brainmob, span_danger("You can't feel your body! You're still just a brain!"))
forceMove(brain_owner)
@@ -102,25 +108,11 @@
//Update the body's icon so it doesnt appear debrained anymore
brain_owner.update_body_parts()
-/obj/item/organ/internal/brain/on_insert(mob/living/carbon/organ_owner, special)
- // Are we inserting into a new mob from a head?
- // If yes, we want to quickly steal the brainmob from the head before we do anything else.
- // This is usually stuff like reattaching dismembered/amputated heads.
- if(istype(loc, /obj/item/bodypart/head))
- var/obj/item/bodypart/head/brain_holder = loc
- if(brain_holder.brainmob)
- brainmob = brain_holder.brainmob
- brain_holder.brainmob = null
- brainmob.container = null
- brainmob.forceMove(src)
-
- return ..()
-
-/obj/item/organ/internal/brain/Remove(mob/living/carbon/brain_owner, special = 0, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
// Delete skillchips first as parent proc sets owner to null, and skillchips need to know the brain's owner.
- if(!QDELETED(brain_owner) && length(skillchips))
+ if(!QDELETED(organ_owner) && length(skillchips))
if(!special)
- to_chat(brain_owner, span_notice("You feel your skillchips enable emergency power saving mode, deactivating as your brain leaves your body..."))
+ to_chat(organ_owner, span_notice("You feel your skillchips enable emergency power saving mode, deactivating as your brain leaves your body..."))
for(var/chip in skillchips)
var/obj/item/skillchip/skillchip = chip
// Run the try_ proc with force = TRUE.
@@ -133,10 +125,11 @@
BT.on_lose(TRUE)
BT.owner = null
- if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !no_id_transfer)
- transfer_identity(brain_owner)
- brain_owner.update_body_parts()
- brain_owner.clear_mood_event("brain_damage")
+ if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !(movement_flags & NO_ID_TRANSFER))
+ transfer_identity(organ_owner)
+ if(!special)
+ organ_owner.update_body_parts()
+ organ_owner.clear_mood_event("brain_damage")
/obj/item/organ/internal/brain/proc/transfer_identity(mob/living/L)
name = "[L.name]'s [initial(name)]"
@@ -417,11 +410,11 @@
icon_state = "random_fly_4"
organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP)
-/obj/item/organ/internal/brain/lustrous/before_organ_replacement(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/brain/lustrous/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
organ_owner.cure_trauma_type(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE)
-/obj/item/organ/internal/brain/lustrous/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/brain/lustrous/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
organ_owner.gain_trauma(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE)
@@ -580,6 +573,6 @@
/// Brains REALLY like ghosting people. we need special tricks to avoid that, namely removing the old brain with no_id_transfer
/obj/item/organ/internal/brain/replace_into(mob/living/carbon/new_owner)
var/obj/item/organ/internal/brain/old_brain = new_owner.get_organ_slot(ORGAN_SLOT_BRAIN)
- old_brain.Remove(new_owner, special = TRUE, no_id_transfer = TRUE)
+ old_brain.Remove(new_owner, special = TRUE, movement_flags = NO_ID_TRANSFER)
qdel(old_brain)
- return Insert(new_owner, special = TRUE, drop_if_replaced = FALSE, no_id_transfer = TRUE)
+ return Insert(new_owner, special = TRUE, movement_flags = NO_ID_TRANSFER | DELETE_IF_REPLACED)
diff --git a/code/modules/mob/living/carbon/alien/adult/queen.dm b/code/modules/mob/living/carbon/alien/adult/queen.dm
index df03ce3fa9e..f2093738eac 100644
--- a/code/modules/mob/living/carbon/alien/adult/queen.dm
+++ b/code/modules/mob/living/carbon/alien/adult/queen.dm
@@ -9,7 +9,6 @@
bubble_icon = "alienroyal"
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //above most mobs, but below speechbubbles
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper.
butcher_results = list(/obj/item/food/meat/slab/xeno = 20, /obj/item/stack/sheet/animalhide/xeno = 3)
@@ -23,11 +22,11 @@
/mob/living/carbon/alien/adult/royal/on_lying_down(new_lying_angle)
. = ..()
- SET_PLANE_IMPLICIT(src, GAME_PLANE_FOV_HIDDEN) //So it won't hide smaller mobs.
+ layer = LYING_MOB_LAYER
/mob/living/carbon/alien/adult/royal/on_standing_up(new_lying_angle)
. = ..()
- SET_PLANE_IMPLICIT(src, initial(plane))
+ layer = initial(layer)
/mob/living/carbon/alien/adult/royal/can_inject(mob/user, target_zone, injection_flags)
return FALSE
diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm
index a29b126dd8d..66f555b639a 100644
--- a/code/modules/mob/living/carbon/alien/organs.dm
+++ b/code/modules/mob/living/carbon/alien/organs.dm
@@ -69,14 +69,14 @@
else
owner.adjustPlasma(0.1 * plasma_rate * delta_time)
-/obj/item/organ/internal/alien/plasmavessel/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/alien/plasmavessel/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
if(isalien(organ_owner))
var/mob/living/carbon/alien/target_alien = organ_owner
target_alien.updatePlasmaDisplay()
RegisterSignal(organ_owner, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
-/obj/item/organ/internal/alien/plasmavessel/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/alien/plasmavessel/on_mob_remove(mob/living/carbon/organ_owner)
. = ..()
if(isalien(organ_owner))
var/mob/living/carbon/alien/organ_owner_alien = organ_owner
@@ -95,18 +95,18 @@
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_XENO_HIVENODE
w_class = WEIGHT_CLASS_TINY
+ organ_traits = list(TRAIT_XENO_IMMUNE)
actions_types = list(/datum/action/cooldown/alien/whisper)
/// Indicates if the queen died recently, aliens are heavily weakened while this is active.
var/recent_queen_death = FALSE
-/obj/item/organ/internal/alien/hivenode/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/alien/hivenode/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
organ_owner.faction |= ROLE_ALIEN
- ADD_TRAIT(organ_owner, TRAIT_XENO_IMMUNE, ORGAN_TRAIT)
-/obj/item/organ/internal/alien/hivenode/Remove(mob/living/carbon/organ_owner, special = FALSE)
- organ_owner.faction -= ROLE_ALIEN
- REMOVE_TRAIT(organ_owner, TRAIT_XENO_IMMUNE, ORGAN_TRAIT)
+/obj/item/organ/internal/alien/hivenode/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE)
+ if(organ_owner)
+ organ_owner.faction -= ROLE_ALIEN
return ..()
//When the alien queen dies, all aliens suffer a penalty as punishment for failing to protect her.
@@ -232,11 +232,11 @@
stomach_contents -= source
UnregisterSignal(source, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_DEATH, COMSIG_QDELETING))
-/obj/item/organ/internal/stomach/alien/Insert(mob/living/carbon/stomach_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/stomach/alien/Insert(mob/living/carbon/stomach_owner, special, movement_flags)
RegisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE, PROC_REF(something_moved))
return ..()
-/obj/item/organ/internal/stomach/alien/Remove(mob/living/carbon/stomach_owner, special = FALSE)
+/obj/item/organ/internal/stomach/alien/Remove(mob/living/carbon/stomach_owner, special, movement_flags)
UnregisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE)
return ..()
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index b3435a7d879..db0ad4c145c 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -22,7 +22,6 @@
tint = 3
flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
layer = MOB_LAYER
- plane = GAME_PLANE_FOV_HIDDEN
max_integrity = 100
item_flags = XENOMORPH_HOLDABLE
special_desc_requirement = EXAMINE_CHECK_JOB //SKYRAT EDIT
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index f389fb81281..fd2dec2e7fa 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -993,7 +993,6 @@
for(var/obj/item/bodypart/bodypart_path as anything in bodyparts_paths)
var/real_body_part_path = overrides?[initial(bodypart_path.body_zone)] || bodypart_path
var/obj/item/bodypart/bodypart_instance = new real_body_part_path()
- bodypart_instance.set_owner(src)
add_bodypart(bodypart_instance)
/// Called when a new hand is added
@@ -1010,8 +1009,12 @@
/mob/living/carbon/proc/add_bodypart(obj/item/bodypart/new_bodypart)
SHOULD_NOT_OVERRIDE(TRUE)
+ new_bodypart.on_adding(src)
bodyparts += new_bodypart
- new_bodypart.set_owner(src)
+ new_bodypart.update_owner(src)
+
+ for(var/obj/item/organ/organ in new_bodypart)
+ organ.mob_insert(src)
switch(new_bodypart.body_part)
if(LEG_LEFT, LEG_RIGHT)
@@ -1026,10 +1029,17 @@
synchronize_bodytypes()
///Proc to hook behavior on bodypart removals. Do not directly call. You're looking for [/obj/item/bodypart/proc/drop_limb()].
-/mob/living/carbon/proc/remove_bodypart(obj/item/bodypart/old_bodypart)
+/mob/living/carbon/proc/remove_bodypart(obj/item/bodypart/old_bodypart, special)
SHOULD_NOT_OVERRIDE(TRUE)
- old_bodypart.on_removal()
+ if(special)
+ for(var/obj/item/organ/organ in old_bodypart)
+ organ.bodypart_remove(limb_owner = src, movement_flags = NO_ID_TRANSFER)
+ else
+ for(var/obj/item/organ/organ in old_bodypart)
+ organ.mob_remove(src, special)
+
+ old_bodypart.on_removal(src)
bodyparts -= old_bodypart
switch(old_bodypart.body_part)
@@ -1435,3 +1445,8 @@
our_splatter.blood_dna_info = get_blood_dna_list()
var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength)
our_splatter.fly_towards(targ, splatter_strength)
+
+/mob/living/carbon/dropItemToGround(obj/item/item, force = FALSE, silent = FALSE, invdrop = TRUE)
+ if(item && ((item in organs) || (item in bodyparts))) //let's not do this, aight?
+ return FALSE
+ return ..()
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 78b8554361b..85a6b06a2d3 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -28,9 +28,9 @@
add_memory_in_range(src, 7, /datum/memory/witness_gib, protagonist = src)
if(drop_bitflags & DROP_ITEMS)
for(var/obj/item/W in src)
- dropItemToGround(W)
- if(prob(50))
- step(W, pick(GLOB.alldirs))
+ if(dropItemToGround(W))
+ if(prob(50))
+ step(W, pick(GLOB.alldirs))
var/atom/Tsec = drop_location()
for(var/mob/M in src)
M.forceMove(Tsec)
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 2f68119c192..cf1c9223eb1 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -375,19 +375,17 @@ GLOBAL_LIST_EMPTY(features_by_species)
health_pct = (existing_organ.maxHealth - existing_organ.damage) / existing_organ.maxHealth
if(slot == ORGAN_SLOT_BRAIN)
var/obj/item/organ/internal/brain/existing_brain = existing_organ
- if(!existing_brain.decoy_override)
- existing_brain.before_organ_replacement(new_organ)
- existing_brain.Remove(organ_holder, special = TRUE, no_id_transfer = TRUE)
- QDEL_NULL(existing_organ)
+ existing_brain.before_organ_replacement(new_organ)
+ existing_brain.Remove(organ_holder, special = TRUE, movement_flags = NO_ID_TRANSFER)
else
existing_organ.before_organ_replacement(new_organ)
existing_organ.Remove(organ_holder, special = TRUE)
- QDEL_NULL(existing_organ)
- if(isnull(existing_organ) && should_have && !(new_organ.zone in excluded_zones))
+ QDEL_NULL(existing_organ)
+ if(isnull(existing_organ) && should_have && !(new_organ.zone in excluded_zones) && organ_holder.get_bodypart(deprecise_zone(new_organ.zone)))
used_neworgan = TRUE
new_organ.set_organ_damage(new_organ.maxHealth * (1 - health_pct))
- new_organ.Insert(organ_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_organ.Insert(organ_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(!used_neworgan)
QDEL_NULL(new_organ)
@@ -430,7 +428,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(current_organ)
current_organ.before_organ_replacement(replacement)
// organ.Insert will qdel any current organs in that slot, so we don't need to.
- replacement.Insert(organ_holder, special=TRUE, drop_if_replaced=FALSE)
+ replacement.Insert(organ_holder, special=TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/species/proc/worn_items_fit_body_check(mob/living/carbon/wearer)
for(var/obj/item/equipped_item in wearer.get_all_worn_items())
@@ -497,7 +495,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
//Load a persons preferences from DNA
var/obj/item/organ/external/new_organ = SSwardrobe.provide_type(organ_path)
- new_organ.Insert(human, special=TRUE, drop_if_replaced=FALSE)
+ new_organ.Insert(human, special=TRUE, movement_flags = DELETE_IF_REPLACED)
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 6780a427b50..0632ed03f89 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -366,8 +366,7 @@
if(!key)
msg += "[span_deadsay("[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely.")]\n"
else if(!client)
- //msg += "[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon.\n" //ORIGINAL
- msg += "[t_He] [t_has] a blank, absent-minded stare and [t_has] been completely unresponsive to anything for [round(((world.time - lastclienttime) / (1 MINUTES)),1)] minutes. [t_He] may snap out of it soon.\n" //SKYRAT CHANGE ADDITION - SSD_INDICATOR
+ msg += "[span_deadsay("[t_He] [t_has] a blank, absent-minded stare and [t_has] been completely unresponsive to anything for [round(((world.time - lastclienttime) / (1 MINUTES)),1)] minutes. [t_He] may snap out of it soon.")]\n" // SKYRAT EDIT CHANGE - SSD_INDICATOR - ORIGINAL: msg += "[span_deadsay("[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon.")]\n"
var/scar_severity = 0
for(var/i in all_scars)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index c0a1836dcfb..33e478c6ee1 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -13,6 +13,7 @@
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
can_be_shoved_into = TRUE
initial_language_holder = /datum/language_holder/empty // We get stuff from our species
+ flags_1 = PREVENT_CONTENTS_EXPLOSION_1
maxHealth = HUMAN_MAXHEALTH //SKYRAT EDIT ADDITION
health = HUMAN_MAXHEALTH //SKYRAT EDIT ADDITION
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index a20758b17ae..7e7af092b99 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -91,7 +91,7 @@
//Useful when player is being seen by other mobs
/mob/living/carbon/human/proc/get_id_name(if_no_id = "Unknown")
var/obj/item/storage/wallet/wallet = wear_id
- var/obj/item/modular_computer/pda/pda = wear_id
+ var/obj/item/modular_computer/pda = wear_id
var/obj/item/card/id/id = wear_id
if(HAS_TRAIT(src, TRAIT_UNKNOWN))
. = if_no_id //You get NOTHING, no id name, good day sir
diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm
index 636f40fd09b..3823c61ab03 100644
--- a/code/modules/mob/living/carbon/human/species_types/felinid.dm
+++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm
@@ -43,7 +43,7 @@
target_human.dna.features["ears"] = "Cat"
if(target_human.dna.features["ears"] == "Cat")
var/obj/item/organ/internal/ears/cat/ears = new
- ears.Insert(target_human, drop_if_replaced = FALSE)
+ ears.Insert(target_human, movement_flags = DELETE_IF_REPLACED)
else
mutantears = /obj/item/organ/internal/ears
return ..()
@@ -95,8 +95,8 @@
// Humans get converted directly to felinids, and the key is handled in on_species_gain.
// Now when we get mob.dna.features[feature_key], it returns None, which is why the tail is invisible.
// stored_feature_id is only set once (the first time an organ is inserted), so this should be safe.
- kitty_ears.Insert(soon_to_be_felinid, special = TRUE, drop_if_replaced = FALSE)
- kitty_tail.Insert(soon_to_be_felinid, special = TRUE, drop_if_replaced = FALSE)
+ kitty_ears.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED)
+ kitty_tail.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(!silent)
to_chat(soon_to_be_felinid, span_boldnotice("Something is nya~t right."))
playsound(get_turf(soon_to_be_felinid), 'sound/effects/meow1.ogg', 50, TRUE, -1)
@@ -119,16 +119,16 @@
for(var/external_organ in target_species.external_organs)
if(ispath(external_organ, /obj/item/organ/external/tail))
var/obj/item/organ/external/tail/new_tail = new external_organ()
- new_tail.Insert(purrbated_human, special = TRUE, drop_if_replaced = FALSE)
+ new_tail.Insert(purrbated_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// Don't forget the spines we removed earlier
else if(ispath(external_organ, /obj/item/organ/external/spines))
var/obj/item/organ/external/spines/new_spines = new external_organ()
- new_spines.Insert(purrbated_human, special = TRUE, drop_if_replaced = FALSE)
+ new_spines.Insert(purrbated_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
var/obj/item/organ/internal/ears/old_ears = purrbated_human.get_organ_slot(ORGAN_SLOT_EARS)
if(istype(old_ears, /obj/item/organ/internal/ears/cat))
var/obj/item/organ/new_ears = new target_species.mutantears()
- new_ears.Insert(purrbated_human, special = TRUE, drop_if_replaced = FALSE)
+ new_ears.Insert(purrbated_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(!silent)
to_chat(purrbated_human, span_boldnotice("You are no longer a cat."))
diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
index 1bffa4aa8b0..2a3851b4cca 100644
--- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm
+++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
@@ -167,11 +167,11 @@
build_all_button_icons()
-/obj/item/organ/internal/brain/primate/on_insert(mob/living/carbon/primate)
+/obj/item/organ/internal/brain/primate/on_mob_insert(mob/living/carbon/primate)
. = ..()
RegisterSignal(primate, COMSIG_MOVABLE_CROSS, PROC_REF(on_crossed), TRUE)
-/obj/item/organ/internal/brain/primate/on_remove(mob/living/carbon/primate)
+/obj/item/organ/internal/brain/primate/on_mob_remove(mob/living/carbon/primate)
. = ..()
UnregisterSignal(primate, COMSIG_MOVABLE_CROSS)
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index 1bd77a4356f..fc43e377bc9 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -89,11 +89,20 @@
return to_add
+/obj/item/organ/internal/eyes/shadow
+ name = "burning red eyes"
+ desc = "Even without their shadowy owner, looking at these eyes gives you a sense of dread."
+ icon = 'icons/obj/medical/organs/shadow_organs.dmi'
+ color_cutoffs = list(20, 10, 40)
+ pepperspray_protect = TRUE
+
/// the key to some of their powers
/obj/item/organ/internal/brain/shadow
name = "shadowling tumor"
desc = "Something that was once a brain, before being remolded by a shadowling. It has adapted to the dark, irreversibly."
icon = 'icons/obj/medical/organs/shadow_organs.dmi'
+ /// What status effect do we gain while in darkness?
+ var/applied_status = /datum/status_effect/shadow_regeneration
/obj/item/organ/internal/brain/shadow/on_life(seconds_per_tick, times_fired)
. = ..()
@@ -102,14 +111,34 @@
return
var/light_amount = owner_turf.get_lumcount()
- if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD) //if there's enough light, start dying
+ if (light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) //heal in the dark
+ owner.apply_status_effect(applied_status)
+ if (!owner.has_status_effect(applied_status))
owner.take_overall_damage(brute = 0.5 * seconds_per_tick, burn = 0.5 * seconds_per_tick, required_bodytype = BODYTYPE_ORGANIC)
- else if (light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) //heal in the dark
- owner.heal_overall_damage(brute = 0.5 * seconds_per_tick, burn = 0.5 * seconds_per_tick, required_bodytype = BODYTYPE_ORGANIC)
-/obj/item/organ/internal/eyes/shadow
- name = "burning red eyes"
- desc = "Even without their shadowy owner, looking at these eyes gives you a sense of dread."
- icon = 'icons/obj/medical/organs/shadow_organs.dmi'
- color_cutoffs = list(20, 10, 40)
- pepperspray_protect = TRUE
+/// Heal in darkness and potentially trigger other effects, persists for a short duration after leaving
+/datum/status_effect/shadow_regeneration
+ id = "shadow_regeneration"
+ duration = 2 SECONDS
+ status_type = STATUS_EFFECT_REFRESH
+ alert_type = /atom/movable/screen/alert/status_effect/shadow_regeneration
+
+/datum/status_effect/shadow_regeneration/on_apply()
+ . = ..()
+ if (!.)
+ return FALSE
+ heal_owner()
+ return TRUE
+
+/datum/status_effect/shadow_regeneration/refresh(effect)
+ . = ..()
+ heal_owner()
+
+/// Regenerate health whenever this status effect is applied or reapplied
+/datum/status_effect/shadow_regeneration/proc/heal_owner()
+ owner.heal_overall_damage(brute = 1, burn = 1, required_bodytype = BODYTYPE_ORGANIC)
+
+/atom/movable/screen/alert/status_effect/shadow_regeneration
+ name = "Shadow Regeneration"
+ desc = "Bathed in soothing darkness, you will slowly heal yourself."
+ icon_state = "lightless"
diff --git a/code/modules/mob/living/carbon/human/species_types/snail.dm b/code/modules/mob/living/carbon/human/species_types/snail.dm
index 036b5ad5c24..6f2f12ebe03 100644
--- a/code/modules/mob/living/carbon/human/species_types/snail.dm
+++ b/code/modules/mob/living/carbon/human/species_types/snail.dm
@@ -3,9 +3,9 @@
id = SPECIES_SNAIL
inherent_traits = list(
TRAIT_MUTANT_COLORS,
- // TRAIT_NO_UNDERWEAR, //SKYRAT EDIT - Snails deserve to wear underwear
+ // TRAIT_NO_UNDERWEAR, //SKYRAT EDIT REMOVAL - Roundstart Snails - Snails deserve to wear underwear
TRAIT_NO_SLIP_ALL,
- TRAIT_WATER_BREATHING, //SKYRAT EDIT - Roundstart Snails
+ TRAIT_WATER_BREATHING, //SKYRAT EDIT ADDITION - Roundstart Snails - Sea snails exist, and land snails can weather being underwater for almost a day.
)
coldmod = 0.5 //snails only come out when its cold and wet
@@ -13,11 +13,9 @@
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | RACE_SWAP
sexes = FALSE //snails are hermaphrodites
- eyes_icon = 'modular_skyrat/modules/organs/icons/snail_eyes.dmi' // SKYRAT EDIT - Roundstart Snails
mutanteyes = /obj/item/organ/internal/eyes/snail
mutanttongue = /obj/item/organ/internal/tongue/snail
- mutantliver = /obj/item/organ/internal/liver/snail //SKYRAT EDIT - Roundstart Snails
- // exotic_blood = /datum/reagent/lube // SKYRAT EDIT REMOVAL: Roundstart Snails - No more lube
+ exotic_blood = /datum/reagent/lube
bodypart_overrides = list(
BODY_ZONE_HEAD = /obj/item/bodypart/head/snail,
@@ -50,9 +48,8 @@
if(new_snailperson.dropItemToGround(bag)) //returns TRUE even if its null
new_snailperson.equip_to_slot_or_del(new /obj/item/storage/backpack/snail(new_snailperson), ITEM_SLOT_BACK)
new_snailperson.AddElement(/datum/element/snailcrawl)
- new_snailperson.update_icons() //SKYRAT EDIT: Roundstart Snails
- //if(ishuman(new_snailperson)) //SKYRAT EDIT: Snails don't have exotic blood here!
- // update_mail_goodies(new_snailperson) //SKYRAT EDIT END
+ if(ishuman(new_snailperson))
+ update_mail_goodies(new_snailperson)
/datum/species/snail/on_species_loss(mob/living/carbon/former_snailperson, datum/species/new_species, pref_load)
. = ..()
@@ -63,12 +60,12 @@
former_snailperson.temporarilyRemoveItemFromInventory(bag, TRUE)
qdel(bag)
-/*/datum/species/snail/update_quirk_mail_goodies(mob/living/carbon/human/recipient, datum/quirk/quirk, list/mail_goodies = list()) //SKYRAT EDIT: Snails don't have exotic blood here!
+/datum/species/snail/update_quirk_mail_goodies(mob/living/carbon/human/recipient, datum/quirk/quirk, list/mail_goodies = list())
if(istype(quirk, /datum/quirk/blooddeficiency))
mail_goodies += list(
/obj/item/reagent_containers/blood/snail
)
- return ..()*/ //SKYRAT EDIT END
+ return ..()
/obj/item/storage/backpack/snail
name = "snail shell"
@@ -80,81 +77,8 @@
armor_type = /datum/armor/backpack_snail
max_integrity = 200
resistance_flags = FIRE_PROOF | ACID_PROOF
- //SKYRAT EDIT BEGIN - Roundstart Snails
- slowdown = 6 // The snail's shell is what's making them slow.
- alternate_worn_layer = ABOVE_BODY_FRONT_LAYER //This makes them layer over tails like the cult backpack; some tails really shouldn't appear over them!
- uses_advanced_reskins = TRUE
- unique_reskin = list(
- "Conical Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "coneshell",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "coneshell"
- ),
- "Round Shell" = list(
- RESKIN_ICON = 'icons/obj/storage/backpack.dmi',
- RESKIN_ICON_STATE = "snailshell",
- RESKIN_WORN_ICON = 'icons/mob/clothing/back/backpack.dmi',
- RESKIN_WORN_ICON_STATE = "snailshell"
- ),
- "Cinnamon Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "cinnamonshell",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "cinnamonshell"
- ),
- "Caramel Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "caramelshell",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "caramelshell"
- ),
- "Metal Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "mechashell",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "mechashell"
- ),
- "Pyramid Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "pyramidshell",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "pyramidshell"
- ),
- "Ivory Pyramid Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "pyramidshellwhite",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "pyramidshellwhite"
- ),
- "Spiral Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "spiralshell",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "spiralshell"
- ),
- "Ivory Spiral Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "spiralshellwhite",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "spiralshellwhite"
- ),
- "Rocky Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "rockshell",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "rockshell"
- ),
- "Ivory Rocky Shell" = list(
- RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
- RESKIN_ICON_STATE = "rockshellwhite",
- RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
- RESKIN_WORN_ICON_STATE = "rockshellwhite"
- ),
- )
- //SKYRAT EDIT END - Roundstart Snails
-/datum/armor/backpack_snail
+/datum/armor/backpack_snail //SKYRAT COMMENT CLARIFICATION - Roundstart Snails - These armor values don't actually do any protection of the wearer, this is for checking direct damage to the backpack. Damage resistance stuff is in their heart file.
melee = 40
bullet = 30
laser = 30
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 6e56b13055b..427f1f5f71b 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -203,11 +203,11 @@
name = "vampire heart"
color = "#1C1C1C"
-/obj/item/organ/internal/heart/vampire/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/internal/heart/vampire/on_mob_insert(mob/living/carbon/receiver)
. = ..()
RegisterSignal(receiver, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
-/obj/item/organ/internal/heart/vampire/on_remove(mob/living/carbon/heartless)
+/obj/item/organ/internal/heart/vampire/on_mob_remove(mob/living/carbon/heartless)
. = ..()
UnregisterSignal(heartless, COMSIG_MOB_GET_STATUS_TAB_ITEMS)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index dd7a8d474bb..a3b6c0d229c 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1469,8 +1469,8 @@
else
for(var/obj/item/item in src)
if(!dropItemToGround(item))
- qdel(item)
- continue
+ if(!(item.item_flags & ABSTRACT))
+ qdel(item)
item_contents += item
var/mob/living/new_mob
@@ -2701,7 +2701,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
if(isnull(guardian_client))
return
else if(guardian_client == "Poll Ghosts")
- var/list/candidates = poll_ghost_candidates("Do you want to play as an admin created Guardian Spirit of [real_name]?", ROLE_PAI, FALSE, 100, POLL_IGNORE_HOLOPARASITE)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Do you want to play as an admin created Guardian Spirit of [real_name]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_HOLOPARASITE, pic_source = src, role_name_text = "guardian spirit")
if(LAZYLEN(candidates))
var/mob/dead/observer/candidate = pick(candidates)
guardian_client = candidate.client
@@ -2733,3 +2733,21 @@ GLOBAL_LIST_EMPTY(fire_appearances)
message_admins(span_adminnotice("[key_name_admin(admin)] gave a guardian spirit controlled by [guardian_client || "AI"] to [src]."))
log_admin("[key_name(admin)] gave a guardian spirit controlled by [guardian_client] to [src].")
BLACKBOX_LOG_ADMIN_VERB("Give Guardian Spirit")
+
+/mob/living/verb/lookup()
+ set name = "Look Up"
+ set category = "IC"
+
+ if(client.perspective != MOB_PERSPECTIVE)
+ end_look_up()
+ else
+ look_up()
+
+/mob/living/verb/lookdown()
+ set name = "Look Down"
+ set category = "IC"
+
+ if(client.perspective != MOB_PERSPECTIVE)
+ end_look_down()
+ else
+ look_down()
diff --git a/code/modules/mob/living/living_fov.dm b/code/modules/mob/living/living_fov.dm
index 6d47085855b..0f400017515 100644
--- a/code/modules/mob/living/living_fov.dm
+++ b/code/modules/mob/living/living_fov.dm
@@ -10,7 +10,7 @@
// ^ If that case has changed and you need that check, add it.
var/rel_x = observed_atom.x - my_turf.x
var/rel_y = observed_atom.y - my_turf.y
- if(fov_view && observed_atom.plane == GAME_PLANE_FOV_HIDDEN) //SKYRAT EDIT CHANGE
+ if(fov_view)
if(rel_x >= -1 && rel_x <= 1 && rel_y >= -1 && rel_y <= 1) //Cheap way to check inside that 3x3 box around you
return TRUE //Also checks if both are 0 to stop division by zero
@@ -133,10 +133,3 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
plane = FIELD_OF_VISION_BLOCKER_PLANE
screen_loc = "BOTTOM,LEFT"
-
-/atom/movable/screen/fov_shadow
- icon = 'icons/effects/fov/field_of_view.dmi'
- icon_state = "90_v"
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- plane = ABOVE_LIGHTING_PLANE
- screen_loc = "BOTTOM,LEFT"
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 8a1f20d28c4..7e6dda33d8e 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -349,10 +349,7 @@
return
client.crew_manifest_delay = world.time + (1 SECONDS)
- if(!GLOB.crew_manifest_tgui)
- GLOB.crew_manifest_tgui = new /datum/crew_manifest(src)
-
- GLOB.crew_manifest_tgui.ui_interact(src)
+ GLOB.manifest.ui_interact(src)
/mob/living/silicon/proc/set_autosay() //For allowing the AI and borgs to set the radio behavior of auto announcements (state laws, arrivals).
if(!radio)
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 249cef26d5b..cf894e21baf 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -78,7 +78,7 @@
visible_message(span_notice("[user] pets [src]."), \
span_notice("[user] pets you."), null, null, user)
to_chat(user, span_notice("You pet [src]."))
- user.add_mood_event("pet_borg", /datum/mood_event/pet_borg)
+ SEND_SIGNAL(user, COMSIG_MOB_PAT_BORG)
/mob/living/silicon/check_block(atom/hitby, damage, attack_text, attack_type, armour_penetration, damage_type, attack_flag)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 4ac5b20f43a..39ac1fd02e3 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -1230,3 +1230,6 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/rust_heretic_act()
adjustBruteLoss(400)
+
+/mob/living/simple_animal/bot/spawn_gibs(drop_bitflags = NONE)
+ new /obj/effect/gibspawner/robot(drop_location(), src)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/_megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/_megafauna.dm
index a71f5a1a6a2..200800f3d05 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/_megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/_megafauna.dm
@@ -25,7 +25,6 @@
pull_force = MOVE_FORCE_OVERPOWERING
mob_size = MOB_SIZE_HUGE
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
- plane = GAME_PLANE_UPPER // SKYRAT EDIT - ORIGINAL: plane = GAME_PLANE_UPPER_FOV_HIDDEN
mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
/// Crusher loot dropped when the megafauna is killed with a crusher
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 8531f13c2f5..3cda123814c 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -174,7 +174,7 @@
return FALSE
return ..()
-/mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ") // SKYRAT EDIT ADDITION - Better emotes - Original: /mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE)
+/mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ", pref_to_check) // SKYRAT EDIT ADDITION - Better emotes - Original: /mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE), pref checks
if(swooping & SWOOP_INVULNERABLE) //to suppress attack messages without overriding every single proc that could send a message saying we got hit
return
return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
index c4ab1f1c417..a3d56af8b89 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
@@ -23,7 +23,6 @@
environment_smash = ENVIRONMENT_SMASH_NONE
sentience_type = SENTIENCE_BOSS
layer = LARGE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
var/mob/living/set_target
var/datum/move_loop/has_target/force_move/our_loop
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index b1430b19324..6386fa272b7 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -18,7 +18,6 @@
harm_intent_damage = 0 //Punching elites gets you nowhere
stat_attack = HARD_CRIT
layer = LARGE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
sentience_type = SENTIENCE_BOSS
var/chosen_attack = 1
var/list/attack_action_types = list()
@@ -188,7 +187,7 @@ While using this makes the system rely on OnFire, it still gives options for tim
addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30)
return
visible_message(span_boldwarning("Something within [src] stirs..."))
- var/list/candidates = poll_candidates_for_mob("Do you want to play as a lavaland elite?", ROLE_SENTIENCE, ROLE_SENTIENCE, 5 SECONDS, src, POLL_IGNORE_LAVALAND_ELITE)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a lavaland elite?", check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, poll_time = 5 SECONDS, target_mob = src, ignore_category = POLL_IGNORE_LAVALAND_ELITE, pic_source = src, role_name_text = "lavaland elite")
if(candidates.len)
audible_message(span_boldwarning("The stirring sounds increase in volume!"))
elitemind = pick(candidates)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index db997552a8e..f260344b065 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -267,7 +267,7 @@
* * vision_distance (optional) define how many tiles away the message can be seen.
* * ignored_mob (optional) doesn't show any message to a given mob if TRUE.
*/
-/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ") // SKYRAT EDIT ADDITION - SEPERATION
+/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ", pref_to_check) // SKYRAT EDIT ADDITION - separation, pref checks
var/turf/T = get_turf(src)
if(!T)
return
@@ -298,6 +298,10 @@
for(var/mob/M in hearers)
if(!M.client)
continue
+ // SKYRAT EDIT ADDITION - Emote pref checks
+ if(pref_to_check && !M.client?.prefs.read_preference(pref_to_check))
+ continue
+ // SKYRAT EDIT END
//This entire if/else chain could be in two lines but isn't for readibilties sake.
var/msg = message
@@ -323,7 +327,7 @@
///Adds the functionality to self_message.
-/mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ") // SKYRAT EDIT ADDITION - Better emotes
+/mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ", pref_to_check) // SKYRAT EDIT ADDITION - Better emotes, pref checks
. = ..()
if(self_message)
show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE)
@@ -338,7 +342,7 @@
* * deaf_message (optional) is what deaf people will see.
* * hearing_distance (optional) is the range, how many tiles away the message can be heard.
*/
-/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE, separation = " ") // SKYRAT EDIT ADDITION - Better emotes
+/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE, separation = " ", pref_to_check) // SKYRAT EDIT ADDITION - Better emotes, pref checks
var/list/hearers = get_hearers_in_view(hearing_distance, src)
//SKYRAT EDIT ADDITION BEGIN - AI QoL
@@ -358,6 +362,10 @@
if(audible_message_flags & EMOTE_MESSAGE)
message = "[src][separation][message]" //SKYRAT EDIT CHANGE
for(var/mob/M in hearers)
+ // SKYRAT EDIT ADDITION - Emote pref checks
+ if(pref_to_check && !M.client?.prefs.read_preference(pref_to_check))
+ continue
+ // SKYRAT EDIT END
if(audible_message_flags & EMOTE_MESSAGE && runechat_prefs_check(M, audible_message_flags) && M.can_hear())
M.create_chat_message(src, raw_message = raw_msg, runechat_flags = audible_message_flags)
M.show_message(message, MSG_AUDIBLE, deaf_message, MSG_VISUAL)
@@ -373,7 +381,7 @@
* * deaf_message (optional) is what deaf people will see.
* * hearing_distance (optional) is the range, how many tiles away the message can be heard.
*/
-/mob/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE, separation = " ") // SKYRAT EDIT ADDITION - Better emotes
+/mob/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE, separation = " ", pref_to_check) // SKYRAT EDIT ADDITION - Better emotes, pref checks
. = ..()
if(self_message)
show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL)
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index be218941aea..245afb86932 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -9,7 +9,6 @@
/mob
density = TRUE
layer = MOB_LAYER
- plane = GAME_PLANE_FOV_HIDDEN
animate_movement = SLIDE_STEPS
hud_possible = list(ANTAG_HUD)
pressure_resistance = 8
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 19ca3e7736b..8c6ebe11496 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -364,7 +364,7 @@
var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/)
if(A)
poll_message = "[poll_message] Status: [A.name]."
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob(poll_message, ROLE_PAI, FALSE, 10 SECONDS, M)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob(poll_message, check_jobban = ROLE_PAI, poll_time = 10 SECONDS, target_mob = M, pic_source = M, role_name_text = "ghost control")
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
diff --git a/code/modules/mob/mob_lists.dm b/code/modules/mob/mob_lists.dm
index f0a5275bf71..75904737c3f 100644
--- a/code/modules/mob/mob_lists.dm
+++ b/code/modules/mob/mob_lists.dm
@@ -53,7 +53,7 @@
GLOB.keyloop_list |= src
else if(stat != DEAD || !SSlag_switch?.measures[DISABLE_DEAD_KEYLOOP])
GLOB.keyloop_list |= src
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(stat == DEAD)
add_to_current_dead_players()
@@ -65,7 +65,7 @@
SHOULD_CALL_PARENT(TRUE)
GLOB.player_list -= src
GLOB.keyloop_list -= src
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(stat == DEAD)
remove_from_current_dead_players()
@@ -75,12 +75,12 @@
///Adds the cliented mob reference to either the list of dead player-mobs or to the list of observers, depending on how they joined the game.
/mob/proc/add_to_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.dead_player_list |= src
/mob/dead/observer/add_to_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(started_as_observer)
GLOB.current_observers_list |= src
@@ -92,12 +92,12 @@
///Removes the mob reference from either the list of dead player-mobs or from the list of observers, depending on how they joined the game.
/mob/proc/remove_from_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.dead_player_list -= src
/mob/dead/observer/remove_from_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(started_as_observer)
GLOB.current_observers_list -= src
@@ -107,7 +107,7 @@
///Adds the cliented mob reference to the list of living player-mobs. If the mob is an antag, it adds it to the list of living antag player-mobs.
/mob/proc/add_to_current_living_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.alive_player_list |= src
if(mind && (mind.special_role || length(mind.antag_datums)))
@@ -115,7 +115,7 @@
///Removes the mob reference from the list of living player-mobs. If the mob is an antag, it removes it from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.alive_player_list -= src
if(LAZYLEN(mind?.antag_datums))
@@ -124,7 +124,7 @@
///Adds the cliented mob reference to the list of living antag player-mobs.
/mob/proc/add_to_current_living_antags()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if (length(mind.antag_datums) == 0)
@@ -137,6 +137,6 @@
///Removes the mob reference from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_antags()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.current_living_antags -= src
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 4189d08f3bd..233de1d9435 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -496,7 +496,6 @@
return FALSE
//SKYRAT EDIT ADDITION END
move_intent = MOVE_INTENT_RUN
- SET_PLANE_IMPLICIT(src, (move_intent == MOVE_INTENT_WALK && !HAS_TRAIT(src, TRAIT_OVERSIZED)) ? GAME_PLANE_FOV_HIDDEN : GAME_PLANE) //SKYRAT EDIT ADDITION - Oversized Overhaul
if(hud_used?.static_inventory)
for(var/atom/movable/screen/mov_intent/selector in hud_used.static_inventory)
selector.update_appearance()
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 1c5543bbb59..ca82e13e803 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -127,8 +127,6 @@
/mob/living/carbon/human/AIize(client/preference_source, transfer_after = TRUE)
if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
- for(var/t in bodyparts)
- qdel(t)
return ..()
@@ -173,11 +171,8 @@
ADD_TRAIT(src, TRAIT_NO_TRANSFORM, TEMPORARY_TRANSFORMATION_TRAIT)
Paralyze(1, ignore_canstun = TRUE)
- for(var/obj/item/W in src)
- if(delete_items)
- qdel(W)
- else
- dropItemToGround(W)
+ drop_everything(delete_items)
+
regenerate_icons()
icon = null
SetInvisibility(INVISIBILITY_MAXIMUM)
@@ -189,7 +184,7 @@
to_chat(src, "You are job banned from cyborg! Appeal your job ban if you want to avoid this in the future!")
ghostize(FALSE)
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [src]?", "Cyborg", null, 5 SECONDS, src)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [src]?", check_jobban = JOB_CYBORG, poll_time = 5 SECONDS, target_mob = src, pic_source = src, role_name_text = "cyborg")
if(LAZYLEN(candidates))
var/mob/dead/observer/chosen_candidate = pick(candidates)
message_admins("[key_name_admin(chosen_candidate)] has taken control of ([key_name_admin(src)]) to replace a jobbanned player.")
diff --git a/code/modules/mob_spawn/corpses/mining_corpses.dm b/code/modules/mob_spawn/corpses/mining_corpses.dm
index 8b7ad474b16..972bb5c3fa5 100644
--- a/code/modules/mob_spawn/corpses/mining_corpses.dm
+++ b/code/modules/mob_spawn/corpses/mining_corpses.dm
@@ -27,7 +27,7 @@
/obj/effect/mob_spawn/corpse/human/legioninfested/special(mob/living/carbon/human/spawned_human)
. = ..()
var/obj/item/organ/internal/legion_tumour/cancer = new()
- cancer.Insert(spawned_human, special = TRUE, drop_if_replaced = FALSE)
+ cancer.Insert(spawned_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/// Returns the outfit worn by our corpse
/obj/effect/mob_spawn/corpse/human/legioninfested/proc/select_outfit()
diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm
index 111ea425b6a..2a2ed02c022 100644
--- a/code/modules/mod/mod_actions.dm
+++ b/code/modules/mod/mod_actions.dm
@@ -117,26 +117,43 @@
/datum/action/item_action/mod/panel/ai
ai_action = TRUE
-/datum/action/item_action/mod/pinned_module
+/datum/action/item_action/mod/pinnable
+ /// A reference to the mob we are pinned to.
+ var/mob/pinner
+
+/datum/action/item_action/mod/pinnable/New(Target, mob/user)
+ . = ..()
+ var/obj/item/mod/control/mod = Target
+ if(user == mod.ai_assistant)
+ ai_action = TRUE
+ pinner = user
+ RegisterSignal(user, COMSIG_QDELETING, PROC_REF(pinner_deleted))
+
+/datum/action/item_action/mod/pinnable/Grant(mob/user)
+ if(pinner != user)
+ return
+ return ..()
+
+/// If the guy whose UI we are pinned to got deleted
+/datum/action/item_action/mod/pinnable/proc/pinner_deleted()
+ SIGNAL_HANDLER
+ pinner = null
+ qdel(src)
+
+/datum/action/item_action/mod/pinnable/module
desc = "Activate the module."
/// Overrides the icon applications.
var/override = FALSE
/// Module we are linked to.
var/obj/item/mod/module/module
- /// A reference to the mob we are pinned to.
- var/mob/pinner
/// Timer until we remove our cooldown overlay
var/cooldown_timer
-/datum/action/item_action/mod/pinned_module/New(Target, obj/item/mod/module/linked_module, mob/user)
- var/obj/item/mod/control/mod = Target
- if(user == mod.ai_assistant)
- ai_action = TRUE
+/datum/action/item_action/mod/pinnable/module/New(Target, mob/user, obj/item/mod/module/linked_module)
button_icon = linked_module.icon
button_icon_state = linked_module.icon_state
. = ..()
module = linked_module
- pinner = user
module.pinned_to[REF(user)] = src
if(linked_module.allow_flags & MODULE_ALLOW_INCAPACITATED)
// clears check hands and check conscious
@@ -149,9 +166,8 @@
COMSIG_MODULE_USED,
), PROC_REF(module_interacted_with))
RegisterSignal(linked_module, COMSIG_MODULE_COOLDOWN_STARTED, PROC_REF(cooldown_started))
- RegisterSignal(user, COMSIG_QDELETING, PROC_REF(pinner_deleted))
-/datum/action/item_action/mod/pinned_module/Destroy()
+/datum/action/item_action/mod/pinnable/module/Destroy()
deltimer(cooldown_timer)
UnregisterSignal(module, list(
COMSIG_MODULE_ACTIVATED,
@@ -164,23 +180,13 @@
pinner = null
return ..()
-/datum/action/item_action/mod/pinned_module/Grant(mob/user)
- if(pinner != user)
- return
- return ..()
-
-/datum/action/item_action/mod/pinned_module/Trigger(trigger_flags)
+/datum/action/item_action/mod/pinnable/module/Trigger(trigger_flags)
. = ..()
if(!.)
return
module.on_select()
-/// If the guy whose UI we are pinned to got deleted
-/datum/action/item_action/mod/pinned_module/proc/pinner_deleted()
- pinner = null
- qdel(src)
-
-/datum/action/item_action/mod/pinned_module/apply_button_overlay(atom/movable/screen/movable/action_button/current_button, force)
+/datum/action/item_action/mod/pinnable/module/apply_button_overlay(atom/movable/screen/movable/action_button/current_button, force)
current_button.cut_overlays()
if(override)
return ..()
@@ -194,12 +200,12 @@
current_button.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown"))
return ..()
-/datum/action/item_action/mod/pinned_module/proc/module_interacted_with(datum/source)
+/datum/action/item_action/mod/pinnable/module/proc/module_interacted_with(datum/source)
SIGNAL_HANDLER
build_all_button_icons(UPDATE_BUTTON_OVERLAY|UPDATE_BUTTON_STATUS)
-/datum/action/item_action/mod/pinned_module/proc/cooldown_started(datum/source, cooldown_time)
+/datum/action/item_action/mod/pinnable/module/proc/cooldown_started(datum/source, cooldown_time)
SIGNAL_HANDLER
deltimer(cooldown_timer)
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index a32c8b28b26..88bb8ac7b60 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -271,9 +271,9 @@
// Grant pinned actions to pin owners, gives AI pinned actions to the AI and not the wearer
/obj/item/mod/control/grant_action_to_bearer(datum/action/action)
- if (!istype(action, /datum/action/item_action/mod/pinned_module))
+ if (!istype(action, /datum/action/item_action/mod/pinnable))
return ..()
- var/datum/action/item_action/mod/pinned_module/pinned = action
+ var/datum/action/item_action/mod/pinnable/pinned = action
give_item_action(action, pinned.pinner, slot_flags)
/obj/item/mod/control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
diff --git a/code/modules/mod/mod_link.dm b/code/modules/mod/mod_link.dm
index 12ce7fa4827..5781a040092 100644
--- a/code/modules/mod/mod_link.dm
+++ b/code/modules/mod/mod_link.dm
@@ -47,23 +47,19 @@
if(newdir & NORTH)
other_visual.pixel_y = 13
other_visual.layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(other_visual, GAME_PLANE_FOV_HIDDEN)
if(newdir & SOUTH)
other_visual.pixel_y = -24
other_visual.layer = ABOVE_ALL_MOB_LAYER
- SET_PLANE_IMPLICIT(other_visual, GAME_PLANE_UPPER_FOV_HIDDEN)
new_transform.Scale(-1, 1)
new_transform.Translate(-1, 0)
if(newdir & EAST)
other_visual.pixel_x = 14
other_visual.layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(other_visual, GAME_PLANE_FOV_HIDDEN)
new_transform.Shear(0.5, 0)
new_transform.Scale(0.65, 1)
if(newdir & WEST)
other_visual.pixel_x = -14
other_visual.layer = BELOW_MOB_LAYER
- SET_PLANE_IMPLICIT(other_visual, GAME_PLANE_FOV_HIDDEN)
new_transform.Shear(-0.5, 0)
new_transform.Scale(0.65, 1)
other_visual.transform = new_transform
diff --git a/code/modules/mod/mod_paint.dm b/code/modules/mod/mod_paint.dm
index 351859e5f2b..320276cd00d 100644
--- a/code/modules/mod/mod_paint.dm
+++ b/code/modules/mod/mod_paint.dm
@@ -16,7 +16,7 @@
/obj/item/mod/paint/Initialize(mapload)
. = ..()
- current_color = color_matrix_identity()
+ current_color = COLOR_MATRIX_IDENTITY
/obj/item/mod/paint/examine(mob/user)
. = ..()
@@ -66,7 +66,7 @@
. = ..()
editing_mod = null
QDEL_NULL(proxy_view)
- current_color = color_matrix_identity()
+ current_color = COLOR_MATRIX_IDENTITY
/obj/item/mod/paint/ui_status(mob/user)
if(check_menu(editing_mod, user))
diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm
index eaf4b75d7ee..575518affbb 100644
--- a/code/modules/mod/mod_ui.dm
+++ b/code/modules/mod/mod_ui.dm
@@ -53,7 +53,7 @@
"cooldown" = round(COOLDOWN_TIMELEFT(module, cooldown_timer), 1 SECONDS),
"id" = module.tgui_id,
"ref" = REF(module),
- "configuration_data" = module.get_configuration()
+ "configuration_data" = module.get_configuration(user)
))
data["module_custom_status"] = module_custom_status
data["module_info"] = module_info
diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm
index d3d05d205ab..874f5f3dbdf 100644
--- a/code/modules/mod/modules/_module.dm
+++ b/code/modules/mod/modules/_module.dm
@@ -250,7 +250,7 @@
return list()
/// Creates a list of configuring options for this module
-/obj/item/mod/module/proc/get_configuration()
+/obj/item/mod/module/proc/get_configuration(mob/user)
return list()
/// Generates an element of the get_configuration list with a display name, type and value
@@ -325,12 +325,12 @@
if(module_type == MODULE_PASSIVE)
return
- var/datum/action/item_action/mod/pinned_module/existing_action = pinned_to[REF(user)]
+ var/datum/action/item_action/mod/pinnable/module/existing_action = pinned_to[REF(user)]
if(existing_action)
mod.remove_item_action(existing_action)
return
- var/datum/action/item_action/mod/pinned_module/new_action = new(mod, src, user)
+ var/datum/action/item_action/mod/pinnable/module/new_action = new(mod, user, src)
mod.add_item_action(new_action)
/// On drop key, concels a device item.
diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm
index 9bda5fef503..15f2a543239 100644
--- a/code/modules/mod/modules/modules_supply.dm
+++ b/code/modules/mod/modules/modules_supply.dm
@@ -245,7 +245,7 @@
mod.wearer.visible_message(span_warning("[mod.wearer] starts whirring!"), \
blind_message = span_hear("You hear a whirring sound."))
playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE)
- lightning = mutable_appearance('icons/effects/effects.dmi', "electricity3", offset_spokesman = src, plane = GAME_PLANE_FOV_HIDDEN)
+ lightning = mutable_appearance('icons/effects/effects.dmi', "electricity3", layer = LOW_MOB_LAYER)
mod.wearer.add_overlay(lightning)
balloon_alert(mod.wearer, "you start charging...")
var/power = launch_time
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 0492cb71489..420bfa74b54 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -47,8 +47,8 @@
var/max_idle_programs = 2
///Flag of the type of device the modular computer is, deciding what types of apps it can run.
- var/hardware_flag = NONE
-// Options: PROGRAM_ALL | PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_TABLET
+ var/hardware_flag = PROGRAM_ALL
+// Options: PROGRAM_ALL | PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_PDA
///The theme, used for the main menu and file browser apps.
var/device_theme = PDA_THEME_NTOS
@@ -402,7 +402,7 @@
return
if(enabled)
- . += active_program ? mutable_appearance(init_icon, active_program.program_icon_state) : mutable_appearance(init_icon, icon_state_menu)
+ . += active_program ? mutable_appearance(init_icon, active_program.program_open_overlay) : mutable_appearance(init_icon, icon_state_menu)
if(atom_integrity <= integrity_failure * max_integrity)
. += mutable_appearance(init_icon, "bsod")
. += mutable_appearance(init_icon, "broken")
@@ -472,13 +472,13 @@
shutdown_computer()
return
- if(active_program && active_program.requires_ntnet && !get_ntnet_status())
+ if(active_program && (active_program.program_flags & PROGRAM_REQUIRES_NTNET) && !get_ntnet_status())
active_program.event_networkfailure(FALSE) // Active program requires NTNet to run but we've just lost connection. Crash.
for(var/datum/computer_file/program/idle_programs as anything in idle_threads)
idle_programs.process_tick(seconds_per_tick)
idle_programs.ntnet_status = get_ntnet_status()
- if(idle_programs.requires_ntnet && !idle_programs.ntnet_status)
+ if((idle_programs.program_flags & PROGRAM_REQUIRES_NTNET) && !idle_programs.ntnet_status)
idle_programs.event_networkfailure(TRUE)
if(active_program)
@@ -506,6 +506,8 @@
physical.loc.visible_message(span_notice("[icon2html(physical, viewers(physical.loc))] \The [src] displays a [caller.filedesc] notification: [alerttext]"))
/obj/item/modular_computer/proc/ring(ringtone) // bring bring
+ if(!use_power())
+ return
if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED))
playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 50, TRUE)
else
@@ -522,8 +524,9 @@
data["PC_device_theme"] = device_theme
if(internal_cell)
+ data["PC_lowpower_mode"] = !internal_cell.charge
switch(internal_cell.percent())
- if(80 to 200) // 100 should be maximal but just in case..
+ if(80 to INFINITY)
data["PC_batteryicon"] = "batt_100.gif"
if(60 to 80)
data["PC_batteryicon"] = "batt_80.gif"
@@ -537,6 +540,7 @@
data["PC_batteryicon"] = "batt_5.gif"
data["PC_batterypercent"] = "[round(internal_cell.percent())]%"
else
+ data["PC_lowpower_mode"] = FALSE
data["PC_batteryicon"] = null
data["PC_batterypercent"] = null
@@ -569,7 +573,8 @@
CRASH("tried to open program that does not belong to this computer")
if(!program || !istype(program)) // Program not found or it's not executable program.
- to_chat(user, span_danger("\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning."))
+ if(user)
+ to_chat(user, span_danger("\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning."))
return FALSE
// The program is already running. Resume it.
@@ -582,15 +587,17 @@
update_appearance(UPDATE_ICON)
return TRUE
- if(!program.is_supported_by_hardware(hardware_flag, 1, user))
+ if(!program.is_supported_by_hardware(hardware_flag, loud = TRUE, user = user))
return FALSE
if(idle_threads.len > max_idle_programs)
- to_chat(user, span_danger("\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error."))
+ if(user)
+ to_chat(user, span_danger("\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error."))
return FALSE
- if(program.requires_ntnet && !get_ntnet_status()) // The program requires NTNet connection, but we are not connected to NTNet.
- to_chat(user, span_danger("\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning."))
+ if(program.program_flags & PROGRAM_REQUIRES_NTNET && !get_ntnet_status()) // The program requires NTNet connection, but we are not connected to NTNet.
+ if(user)
+ to_chat(user, span_danger("\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning."))
return FALSE
if(!program.on_start(user))
@@ -633,9 +640,9 @@
return SSmodular_computers.add_log("[src]: [text]")
/obj/item/modular_computer/proc/close_all_programs()
- active_program = null
+ active_program?.kill_program()
for(var/datum/computer_file/program/idle as anything in idle_threads)
- idle_threads.Remove(idle)
+ idle.kill_program()
/obj/item/modular_computer/proc/shutdown_computer(loud = TRUE)
close_all_programs()
@@ -675,7 +682,7 @@
* It is separated from ui_act() to be overwritten as needed.
*/
/obj/item/modular_computer/proc/toggle_flashlight(mob/user)
- if(!has_light)
+ if(!has_light || !internal_cell || !internal_cell.charge)
return FALSE
if(!COOLDOWN_FINISHED(src, disabled_time))
balloon_alert(user, "disrupted!")
@@ -898,3 +905,14 @@
inserted_pai = null
update_appearance(UPDATE_ICON)
return TRUE
+
+/**
+ * Debug ModPC
+ * Used to spawn all programs for Create and Destroy unit test.
+ */
+/obj/item/modular_computer/debug
+ max_capacity = INFINITY
+
+/obj/item/modular_computer/debug/Initialize(mapload)
+ starting_programs += subtypesof(/datum/computer_file/program)
+ return ..()
diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm
index 24950621de6..b68df9a4671 100644
--- a/code/modules/modular_computers/computers/item/computer_power.dm
+++ b/code/modules/modular_computers/computers/item/computer_power.dm
@@ -1,18 +1,19 @@
///The multiplier given to the base overtime charge drain value if its flashlight is on.
#define FLASHLIGHT_DRAIN_MULTIPLIER 1.1
-// Tries to draw power from charger or, if no operational charger is present, from power cell.
+///Draws power from its rightful source (area if its a computer, the cell otherwise)
+///Takes into account special cases, like silicon PDAs through override, and nopower apps.
/obj/item/modular_computer/proc/use_power(amount = 0)
if(check_power_override())
return TRUE
- if(ismachinery(physical))
- var/obj/machinery/machine_holder = physical
- if(machine_holder.powered())
- machine_holder.use_power(amount)
- return TRUE
-
- if(!internal_cell || !internal_cell.charge)
+ if(!internal_cell)
+ return FALSE
+ if(!internal_cell.charge && (isnull(active_program) || !(active_program.program_flags & PROGRAM_RUNS_WITHOUT_POWER)))
+ close_all_programs()
+ for(var/datum/computer_file/program/programs as anything in stored_files)
+ if((programs.program_flags & PROGRAM_RUNS_WITHOUT_POWER) && open_program(program = programs))
+ return TRUE
return FALSE
if(!internal_cell.use(amount JOULES))
@@ -25,9 +26,9 @@
return internal_cell.give(amount)
return 0
-// Used in following function to reduce copypaste
+///Shuts down the computer from powerloss.
/obj/item/modular_computer/proc/power_failure()
- if(!enabled) // Shut down the computer
+ if(!enabled)
return
if(active_program)
active_program.event_powerfailure()
@@ -57,9 +58,10 @@
power_failure()
return FALSE
-///Used by subtypes for special cases for power usage, returns TRUE if it should stop the use_power chain.
+///Returns TRUE if the PC should not be using any power, FALSE otherwise.
+///Checks to see if the current app allows to be ran without power, if so we'll run with it.
/obj/item/modular_computer/proc/check_power_override()
- return FALSE
+ return (!internal_cell?.charge && (active_program?.program_flags & PROGRAM_RUNS_WITHOUT_POWER))
//Integrated (Silicon) tablets don't drain power, because the tablet is required to state laws, so it being disabled WILL cause problems.
/obj/item/modular_computer/pda/silicon/check_power_override()
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index a09fc49082c..436b261aca3 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -8,23 +8,24 @@
* This is best called when you're actually changing the app, as we don't check
* if we're swapping to the current UI repeatedly.
* Args:
- * user - The person whose UI we're updating.
+ * user - The person whose UI we're updating. Only necessary if we're opening the UI for the first time.
*/
/obj/item/modular_computer/proc/update_tablet_open_uis(mob/user)
- var/datum/tgui/active_ui = SStgui.get_open_ui(user, src)
- if(!active_ui)
- if(active_program)
- active_ui = new(user, src, active_program.tgui_id, active_program.filedesc)
- active_program.ui_interact(user, active_ui)
- else
- active_ui = new(user, src, "NtosMain")
- return active_ui.open()
+ if(user)
+ var/datum/tgui/active_ui = SStgui.get_open_ui(user, src)
+ if(!active_ui)
+ if(active_program)
+ active_ui = new(user, src, active_program.tgui_id, active_program.filedesc)
+ active_program.ui_interact(user, active_ui)
+ else
+ active_ui = new(user, src, "NtosMain")
+ return active_ui.open()
for (var/datum/tgui/window as anything in open_uis)
if(active_program)
window.interface = active_program.tgui_id
window.title = active_program.filedesc
- active_program.ui_interact(user, window)
+ active_program.ui_interact(window.user, window)
else
window.interface = "NtosMain"
window.send_assets()
@@ -113,7 +114,7 @@
data["programs"] += list(list(
"name" = program.filename,
"desc" = program.filedesc,
- "header_program" = program.header_program,
+ "header_program" = !!(program.program_flags & PROGRAM_HEADER),
"running" = !!(program in idle_threads),
"icon" = program.program_icon,
"alert" = program.alert_pending,
@@ -136,14 +137,14 @@
switch(action)
if("PC_exit")
//you can't close apps in emergency mode.
- if(isnull(internal_cell) || internal_cell.charge)
+ if(internal_cell.charge)
active_program.kill_program(usr)
return TRUE
if("PC_shutdown")
shutdown_computer()
return TRUE
if("PC_minimize")
- if(!active_program || (!isnull(internal_cell) && !internal_cell.charge))
+ if(!active_program || !internal_cell.charge)
return
active_program.background_program()
return TRUE
diff --git a/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm b/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm
index 1ef9fab39ce..430c101668d 100644
--- a/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm
+++ b/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm
@@ -14,6 +14,14 @@
/obj/item/computer_disk/maintenance/modsuit_control
starting_programs = list(/datum/computer_file/program/maintenance/modsuit_control)
+///Returns A 'spookiness' value based on the number of ghastly creature and hauntium and their distance from the PC.
+/obj/item/computer_disk/maintenance/spectre_meter
+ starting_programs = list(/datum/computer_file/program/maintenance/spectre_meter)
+
+///A version of the arcade program with less HP/MP for the enemy and more for the player
+/obj/item/computer_disk/maintenance/arcade
+ starting_programs = list(/datum/computer_file/program/arcade/eazy)
+
/obj/item/computer_disk/maintenance/theme/Initialize(mapload)
starting_programs = list(pick(subtypesof(/datum/computer_file/program/maintenance/theme)))
return ..()
diff --git a/code/modules/modular_computers/computers/item/disks/unique_disks.dm b/code/modules/modular_computers/computers/item/disks/unique_disks.dm
index 144fa52c65a..1fd31957bef 100644
--- a/code/modules/modular_computers/computers/item/disks/unique_disks.dm
+++ b/code/modules/modular_computers/computers/item/disks/unique_disks.dm
@@ -6,3 +6,33 @@
/obj/item/computer_disk/syndicate/camera_app
starting_programs = list(/datum/computer_file/program/secureye/syndicate)
+
+/obj/item/computer_disk/syndicate/contractor
+ starting_programs = list(/datum/computer_file/program/contract_uplink)
+
+/obj/item/computer_disk/black_market
+ desc = "Removable disk used to store data. This one has a smudged piece of paper glued to it, reading \"PC softwarez\"."
+
+/obj/item/computer_disk/black_market/Initialize(mapload)
+ icon_state = "datadisk[rand(0, 10)]"
+ //Populated with programs not found in the verified downloader app or that require access to download (but not to run).
+ var/list/potential_programs = list(
+ /datum/computer_file/program/arcade/eazy,
+ /datum/computer_file/program/radar/lifeline,
+ /datum/computer_file/program/radar/custodial_locator,
+ /datum/computer_file/program/supermatter_monitor,
+ /datum/computer_file/program/newscaster,
+ /datum/computer_file/program/secureye,
+ /datum/computer_file/program/crew_manifest,
+ /datum/computer_file/program/status,
+ )
+ potential_programs += subtypesof(/datum/computer_file/program/maintenance) - /datum/computer_file/program/maintenance/theme
+
+ var/total_programs_size = 0
+ for(var/i in 1 to rand(2, 4))
+ var/datum/computer_file/program/to_add = pick_n_take(potential_programs)
+ total_programs_size += initial(to_add.size)
+ starting_programs += to_add
+ ///Make sure the disk has enough space for all the programs
+ max_capacity = max(total_programs_size, max_capacity)
+ return ..()
diff --git a/code/modules/modular_computers/computers/item/pda.dm b/code/modules/modular_computers/computers/item/pda.dm
index 0c027b5f30b..0c2580a32d4 100644
--- a/code/modules/modular_computers/computers/item/pda.dm
+++ b/code/modules/modular_computers/computers/item/pda.dm
@@ -18,7 +18,7 @@
icon_state_menu = "menu"
max_capacity = 64
allow_chunky = TRUE
- hardware_flag = PROGRAM_TABLET
+ hardware_flag = PROGRAM_PDA
max_idle_programs = 2
w_class = WEIGHT_CLASS_SMALL
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
@@ -276,6 +276,19 @@
if(msg)
msg.invisible = TRUE
+/obj/item/modular_computer/pda/syndicate_contract_uplink
+ name = "contractor tablet"
+ device_theme = PDA_THEME_SYNDICATE
+ icon_state_menu = "contractor-assign"
+ comp_light_luminosity = 6.3
+ has_pda_programs = FALSE
+ greyscale_config = /datum/greyscale_config/tablet/stripe_double
+ greyscale_colors = "#696969#000000#FFA500"
+
+ starting_programs = list(
+ /datum/computer_file/program/contract_uplink,
+ )
+
/**
* Silicon PDA
*
@@ -368,7 +381,7 @@
return robotact
qdel(robotact)
robotact = null
- CRASH("Cyborg [silicon_owner]'s tablet hard drive rejected recieving a new copy of the self-manage app. To fix, check the hard drive's space remaining. Please make a bug report about this.")
+ CRASH("Cyborg [silicon_owner]'s tablet hard drive rejected receiving a new copy of the self-manage app. To fix, check the hard drive's space remaining. Please make a bug report about this.")
//Makes the light settings reflect the borg's headlamp settings
/obj/item/modular_computer/pda/silicon/cyborg/ui_data(mob/user)
diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm
index 01c04ed68cd..ea4839ac944 100644
--- a/code/modules/modular_computers/computers/item/processor.dm
+++ b/code/modules/modular_computers/computers/item/processor.dm
@@ -44,6 +44,13 @@
machinery_computer = null
return ..()
+/obj/item/modular_computer/processor/use_power(amount = 0)
+ var/obj/machinery/machine_holder = physical
+ if(machine_holder.powered())
+ machine_holder.use_power(amount)
+ return TRUE
+ return FALSE
+
/obj/item/modular_computer/processor/relay_qdel()
qdel(machinery_computer)
diff --git a/code/modules/modular_computers/computers/item/role_tablet_presets.dm b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
index 4ccdfe8ec21..339def50add 100644
--- a/code/modules/modular_computers/computers/item/role_tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
@@ -103,7 +103,7 @@
/obj/item/modular_computer/pda/heads/quartermaster
name = "quartermaster PDA"
- greyscale_config = /datum/greyscale_config/tablet/stripe_thick
+ greyscale_config = /datum/greyscale_config/tablet/stripe_thick/head
greyscale_colors = "#c4b787#18191e#8b4c31"
inserted_item = /obj/item/pen/survival
stored_paper = 20
@@ -414,7 +414,7 @@
)
/**
- * No Department
+ * No Department/Station Trait
*/
/obj/item/modular_computer/pda/assistant
@@ -423,6 +423,14 @@
/datum/computer_file/program/bounty_board,
)
+/obj/item/modular_computer/pda/bridge_assistant
+ name = "bridge assistant PDA"
+ greyscale_colors = "#374f7e#a92323"
+ starting_programs = list(
+ /datum/computer_file/program/crew_manifest,
+ /datum/computer_file/program/status,
+ )
+
/**
* Non-roles
*/
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index aa6866ff7c5..7fd019c1bee 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -82,7 +82,7 @@
return .
if(cpu.enabled)
- . += cpu.active_program?.program_icon_state || screen_icon_state_menu
+ . += cpu.active_program?.program_open_overlay || screen_icon_state_menu
else if(!(machine_stat & NOPOWER))
. += screen_icon_screensaver
diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md
index 925f2f5668b..ba11790f2fd 100644
--- a/code/modules/modular_computers/documentation.md
+++ b/code/modules/modular_computers/documentation.md
@@ -44,7 +44,7 @@ This is how the base program is setup. the rest is mostly tgui stuff. I'll use t
/// ditto but on emagged syndie net. Use this for antag programs
available_on_syndinet = 0
- /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination)
+ /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_PDA combination)
/// or PROGRAM_ALL. Use this to limit what kind of machines can run the
/// program. For example, comms program should be limited to consoles and laptops.
usage_flags = PROGRAM_ALL
diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm
index 99fc288ffaf..948257aa83c 100644
--- a/code/modules/modular_computers/file_system/computer_file.dm
+++ b/code/modules/modular_computers/file_system/computer_file.dm
@@ -86,6 +86,8 @@
* * background - Whether the app is running in the background.
*/
/datum/computer_file/program/proc/event_powerfailure()
+ if(program_flags & PROGRAM_RUNS_WITHOUT_POWER)
+ return
kill_program()
/**
diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm
index d050bd804b3..5ddb064f319 100644
--- a/code/modules/modular_computers/file_system/program.dm
+++ b/code/modules/modular_computers/file_system/program.dm
@@ -1,12 +1,15 @@
-///The default amount a program should take in cell use.
-#define PROGRAM_BASIC_CELL_USE 15
-
// /program/ files are executable programs that do things.
/datum/computer_file/program
filetype = "PRG"
/// File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NTNET!
filename = "UnknownProgram"
+ /// Program-specific bitflags that tell the app what it runs on.
+ /// (PROGRAM_ALL | PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_PDA)
+ var/can_run_on_flags = PROGRAM_ALL
+ /// Program-specific bitflags that tells the ModPC what the app is able to do special.
+ /// (PROGRAM_REQUIRES_NTNET|PROGRAM_ON_NTNET_STORE|PROGRAM_ON_SYNDINET_STORE|PROGRAM_UNIQUE_COPY|PROGRAM_HEADER|PROGRAM_RUNS_WITHOUT_POWER)
+ var/program_flags = PROGRAM_ON_NTNET_STORE
///How much power running this program costs.
var/power_cell_use = PROGRAM_BASIC_CELL_USE
///List of required accesses to *run* the program. Any match will do.
@@ -18,23 +21,16 @@
var/filedesc = "Unknown Program"
/// Short description of this program's function.
var/extended_desc = "N/A"
- /// Category in the NTDownloader.
- var/category = PROGRAM_CATEGORY_MISC
- /// Program-specific screen icon state
- var/program_icon_state = null
- ///Boolean on whether the program will appear at the top on PDA menus, or in the app list with everything else.
- var/header_program = FALSE
- /// Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes.
- var/requires_ntnet = FALSE
+ ///What category this program can be found in within NTNetDownloader.
+ ///This is required if PROGRAM_ON_NTNET_STORE or PROGRAM_ON_SYNDINET_STORE is on.
+ var/downloader_category = PROGRAM_CATEGORY_DEVICE
+ ///The overlay to add ontop of the ModPC running the app while it's open.
+ ///This is taken from the same file as the ModPC, so you can use can_run_on_flags to prevent
+ ///the program from being used on devices that don't have sprites for it.
+ var/program_open_overlay = null
/// NTNet status, updated every tick by computer running this program. Don't use this for checks if NTNet works, computers do that. Use this for calculations, etc.
var/ntnet_status = 1
- /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL
- var/usage_flags = PROGRAM_ALL
- /// Whether the program can be downloaded from NTNet. Set to FALSE to disable.
- var/available_on_ntnet = TRUE
- /// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to TRUE to enable.
- var/available_on_syndinet = FALSE
- /// Name of the tgui interface
+ /// Name of the tgui interface. If this is not defined, this will not be available in NTNet.
var/tgui_id
/// Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images must also be inserted into /datum/asset/simple/headers.
var/ui_header = null
@@ -48,17 +44,15 @@
var/alert_pending = FALSE
/// How well this program will help combat detomatix viruses.
var/detomatix_resistance = NONE
- ///Boolean on whether or not only one copy of the app can exist. This means it deletes itself when cloned elsewhere.
- var/unique_copy = FALSE
/datum/computer_file/program/clone()
var/datum/computer_file/program/temp = ..()
temp.run_access = run_access
temp.filedesc = filedesc
- temp.program_icon_state = program_icon_state
- temp.requires_ntnet = requires_ntnet
- temp.usage_flags = usage_flags
- if(unique_copy)
+ temp.program_open_overlay = program_open_overlay
+ temp.program_flags = program_flags
+ temp.can_run_on_flags = can_run_on_flags
+ if(program_flags & PROGRAM_UNIQUE_COPY)
if(computer)
computer.remove_file(src)
if(disk_host)
@@ -105,7 +99,7 @@
///Makes sure a program can run on this hardware (for apps limited to tablets/computers/laptops)
/datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = NONE, loud = FALSE, mob/user)
- if(!(hardware_flag & usage_flags))
+ if(!(hardware_flag & can_run_on_flags))
if(loud && computer && user)
to_chat(user, span_danger("\The [computer] flashes a \"Hardware Error - Incompatible software\" warning."))
return FALSE
@@ -127,13 +121,13 @@
* access can contain a list of access numbers to check against. If access is not empty, it will be used istead of checking any inserted ID.
*/
/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, downloading = FALSE, list/access)
- if(issilicon(user) && !ispAI(user))
- return TRUE
+ if(user)
+ if(issilicon(user) && !ispAI(user))
+ return TRUE
+ if(isAdminGhostAI(user))
+ return TRUE
- if(isAdminGhostAI(user))
- return TRUE
-
- if(computer && (computer.obj_flags & EMAGGED) && (available_on_syndinet || !downloading)) //emagged can run anything on syndinet, and can bypass execution locks, but not download.
+ if(computer && (computer.obj_flags & EMAGGED) && (program_flags & PROGRAM_ON_SYNDINET_STORE || !downloading)) //emagged can run anything on syndinet, and can bypass execution locks, but not download.
return TRUE
if(!access_to_check)
@@ -150,7 +144,7 @@
accesscard = computer.computer_id_slot?.GetID()
if(!accesscard)
- if(loud)
+ if(loud && user)
to_chat(user, span_danger("\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning."))
return FALSE
access = accesscard.GetAccess()
@@ -159,7 +153,7 @@
if(singular_access in access) //For loop checks every individual access entry in the access list. If the user's ID has access to any entry, then we're good.
return TRUE
- if(loud)
+ if(loud && user)
to_chat(user, span_danger("\The [computer] flashes an \"Access Denied\" warning."))
return FALSE
@@ -174,7 +168,7 @@
/datum/computer_file/program/proc/on_start(mob/living/user)
SHOULD_CALL_PARENT(TRUE)
if(can_run(user, loud = TRUE))
- if(requires_ntnet)
+ if(program_flags & PROGRAM_REQUIRES_NTNET)
var/obj/item/card/id/ID = computer.computer_id_slot?.GetID()
generate_network_log("Connection opened -- Program ID:[filename] User:[ID?"[ID.registered_name]":"None"]")
return TRUE
@@ -193,12 +187,12 @@
if(src == computer.active_program)
computer.active_program = null
- if(computer.enabled)
- computer.update_tablet_open_uis(usr)
+ if(!QDELETED(computer) && computer.enabled)
+ INVOKE_ASYNC(computer, TYPE_PROC_REF(/obj/item/modular_computer, update_tablet_open_uis), user)
if(src in computer.idle_threads)
computer.idle_threads.Remove(src)
- if(requires_ntnet)
+ if(program_flags & PROGRAM_REQUIRES_NTNET)
var/obj/item/card/id/ID = computer.computer_id_slot?.GetID()
generate_network_log("Connection closed -- Program ID: [filename] User:[ID ? "[ID.registered_name]" : "None"]")
@@ -208,7 +202,7 @@
///Sends the running program to the background/idle threads. Header programs can't be minimized and will kill instead.
/datum/computer_file/program/proc/background_program()
SHOULD_CALL_PARENT(TRUE)
- if(header_program)
+ if(program_flags & PROGRAM_HEADER)
return kill_program()
computer.idle_threads.Add(src)
@@ -217,5 +211,3 @@
computer.update_tablet_open_uis(usr)
computer.update_appearance(UPDATE_ICON)
return TRUE
-
-#undef PROGRAM_BASIC_CELL_USE
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index 0ee695f8fd6..9fc51a00f57 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -1,14 +1,12 @@
/datum/computer_file/program/ai_restorer
filename = "ai_restore"
filedesc = "AI Manager & Restorer"
- category = PROGRAM_CATEGORY_SCI
- program_icon_state = "generic"
+ downloader_category = PROGRAM_CATEGORY_SCIENCE
+ program_open_overlay = "generic"
extended_desc = "Firmware Restoration Kit, capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot."
size = 12
- requires_ntnet = FALSE
- usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
+ can_run_on_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
download_access = list(ACCESS_RD)
- available_on_ntnet = TRUE
tgui_id = "NtosAiRestorer"
program_icon = "laptop-code"
diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm
index c1aedb9d5d9..86452eb45d0 100644
--- a/code/modules/modular_computers/file_system/programs/alarm.dm
+++ b/code/modules/modular_computers/file_system/programs/alarm.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/alarm_monitor
filename = "alarmmonitor"
filedesc = "Canary"
- category = PROGRAM_CATEGORY_ENGI
+ downloader_category = PROGRAM_CATEGORY_ENGINEERING
ui_header = "alarm_green.gif"
- program_icon_state = "alert-green"
+ program_open_overlay = "alert-green"
extended_desc = "This program provides visual interface for a station's alarm system."
- requires_ntnet = 1
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 4
tgui_id = "NtosStationAlertConsole"
program_icon = "bell"
@@ -37,12 +37,12 @@
has_alert = (length(alert_control.listener.alarms) > 0)
if(!has_alert)
- program_icon_state = "alert-green"
+ program_open_overlay = "alert-green"
ui_header = "alarm_green.gif"
else
// If we don't know the status, assume the worst.
// Technically we should never have anything other than a truthy or falsy value
// but this will allow for unknown values to fall through to be an actual alert.
- program_icon_state = "alert-red"
+ program_open_overlay = "alert-red"
ui_header = "alarm_red.gif"
update_computer_icon() // Always update the icon after we check our conditional because we might've changed it
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contractor_program.dm b/code/modules/modular_computers/file_system/programs/antagonist/contractor_program.dm
new file mode 100644
index 00000000000..77ac06f4feb
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/antagonist/contractor_program.dm
@@ -0,0 +1,169 @@
+/datum/computer_file/program/contract_uplink
+ filename = "contractor uplink"
+ filedesc = "Syndicate Contractor Uplink"
+ extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
+ program_open_overlay = "contractor-assign"
+ program_icon = "tasks"
+ size = 10
+
+ program_flags = PROGRAM_ON_SYNDINET_STORE | PROGRAM_UNIQUE_COPY
+ can_run_on_flags = PROGRAM_PDA //this is all we've got sprites for :sob:
+ undeletable = TRUE
+ tgui_id = "SyndicateContractor"
+
+ ///The traitor datum stored on the program. Starts off as null and is set by the player.
+ var/datum/antagonist/traitor/traitor_data
+ ///The error screen sent to the UI so they can show the player.
+ var/error = ""
+ ///Boolean on whether the UI is on the Information screen.
+ var/info_screen = TRUE
+ ///Boolean on whether the program is being loaded for the first time, for a unique screen animation.
+ var/first_load = TRUE
+
+/datum/computer_file/program/contract_uplink/clone()
+ var/datum/computer_file/program/contract_uplink/temp = ..()
+ temp.traitor_data = traitor_data
+ return temp
+
+/datum/computer_file/program/contract_uplink/Destroy(force)
+ traitor_data = null
+ return ..()
+
+/datum/computer_file/program/contract_uplink/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+
+ var/mob/living/user = usr
+
+ switch(action)
+ if("PRG_contract-accept")
+ var/contract_id = text2num(params["contract_id"])
+ traitor_data.uplink_handler.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ACTIVE
+ traitor_data.uplink_handler.contractor_hub.current_contract = traitor_data.uplink_handler.contractor_hub.assigned_contracts[contract_id]
+ program_open_overlay = "contractor-contract"
+ return TRUE
+
+ if("PRG_login")
+ var/datum/antagonist/traitor/traitor_user = user.mind.has_antag_datum(/datum/antagonist/traitor)
+ if(!traitor_user)
+ error = "UNAUTHORIZED USER"
+ return TRUE
+
+ traitor_data = traitor_user
+ if(!traitor_data.uplink_handler.contractor_hub)
+ traitor_data.uplink_handler.contractor_hub = new
+ traitor_data.uplink_handler.contractor_hub.create_contracts(traitor_user.owner)
+ user.playsound_local(user, 'sound/effects/contractstartup.ogg', 100, FALSE)
+ program_open_overlay = "contractor-contractlist"
+ return TRUE
+
+ if("PRG_call_extraction")
+ if (traitor_data.uplink_handler.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING)
+ if (traitor_data.uplink_handler.contractor_hub.current_contract.handle_extraction(user))
+ user.playsound_local(user, 'sound/effects/confirmdropoff.ogg', 100, TRUE)
+ traitor_data.uplink_handler.contractor_hub.current_contract.status = CONTRACT_STATUS_EXTRACTING
+
+ program_open_overlay = "contractor-extracted"
+ else
+ user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
+ error = "Either both you or your target aren't at the dropoff location, or the pod hasn't got a valid place to land. Clear space, or make sure you're both inside."
+ else
+ user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
+ error = "Already extracting... Place the target into the pod. If the pod was destroyed, this contract is no longer possible."
+
+ return TRUE
+ if("PRG_contract_abort")
+ var/contract_id = traitor_data.uplink_handler.contractor_hub.current_contract.id
+
+ traitor_data.uplink_handler.contractor_hub.current_contract = null
+ traitor_data.uplink_handler.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED
+
+ program_open_overlay = "contractor-contractlist"
+
+ return TRUE
+ if("PRG_redeem_TC")
+ if (traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem)
+ var/obj/item/stack/telecrystal/crystals = new /obj/item/stack/telecrystal(get_turf(user), traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(H.put_in_hands(crystals))
+ to_chat(H, span_notice("Your payment materializes into your hands!"))
+ else
+ to_chat(user, span_notice("Your payment materializes onto the floor."))
+
+ traitor_data.uplink_handler.contractor_hub.contract_TC_payed_out += traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem
+ traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem = 0
+ return TRUE
+ else
+ user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
+ return TRUE
+ if ("PRG_clear_error")
+ error = ""
+ return TRUE
+ if("PRG_set_first_load_finished")
+ first_load = FALSE
+ return TRUE
+ if("PRG_toggle_info")
+ info_screen = !info_screen
+ return TRUE
+
+/datum/computer_file/program/contract_uplink/ui_data(mob/user)
+ var/list/data = list()
+
+ data["first_load"] = first_load
+ data["logged_in"] = !!traitor_data
+ data["station_name"] = GLOB.station_name
+ data["info_screen"] = info_screen
+ data["error"] = error
+
+ if(!traitor_data)
+ data["ongoing_contract"] = FALSE
+ data["extraction_enroute"] = FALSE
+ update_computer_icon()
+ return data
+
+ data["ongoing_contract"] = !!traitor_data.uplink_handler.contractor_hub.current_contract
+ if(traitor_data.uplink_handler.contractor_hub.current_contract)
+ program_open_overlay = "contractor-contract"
+ if (traitor_data.uplink_handler.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING)
+ data["extraction_enroute"] = TRUE
+ program_open_overlay = "contractor-extracted"
+ else
+ data["extraction_enroute"] = FALSE
+ var/turf/curr = get_turf(user)
+ var/turf/dropoff_turf
+ data["current_location"] = "[get_area_name(curr, TRUE)]"
+ for (var/turf/content in traitor_data.uplink_handler.contractor_hub.current_contract.contract.dropoff.contents)
+ if (isturf(content))
+ dropoff_turf = content
+ break
+ var/direction
+ if(curr.z == dropoff_turf.z) //Direction calculations for same z-level only
+ direction = uppertext(dir2text(get_dir(curr, dropoff_turf))) //Direction text (East, etc). Not as precise, but still helpful.
+ if(get_area(user) == traitor_data.uplink_handler.contractor_hub.current_contract.contract.dropoff)
+ direction = "LOCATION CONFIRMED"
+ else
+ direction = "???"
+ data["dropoff_direction"] = direction
+ data["redeemable_tc"] = traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem
+ data["earned_tc"] = traitor_data.uplink_handler.contractor_hub.contract_TC_payed_out
+ data["contracts_completed"] = traitor_data.uplink_handler.contractor_hub.contracts_completed
+ for (var/datum/syndicate_contract/contract in traitor_data.uplink_handler.contractor_hub.assigned_contracts)
+ if(!contract.contract)
+ stack_trace("Syndiate contract with null contract objective found in [traitor_data.owner]'s contractor hub!")
+ contract.status = CONTRACT_STATUS_ABORTED
+ continue
+ data["contracts"] += list(list(
+ "target" = contract.contract.target,
+ "target_rank" = contract.target_rank,
+ "payout" = contract.contract.payout,
+ "payout_bonus" = contract.contract.payout_bonus,
+ "dropoff" = contract.contract.dropoff,
+ "id" = contract.id,
+ "status" = contract.status,
+ "message" = contract.wanted_message,
+ ))
+
+ update_computer_icon()
+ return data
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
index be15e90faea..d3b7e9d5b86 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
@@ -1,13 +1,11 @@
/datum/computer_file/program/ntnet_dos
filename = "ntn_dos"
filedesc = "DoS Traffic Generator"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "hostile"
+ downloader_category = PROGRAM_CATEGORY_DEVICE
+ program_open_overlay = "hostile"
extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect"
size = 20
- requires_ntnet = TRUE
- available_on_ntnet = FALSE
- available_on_syndinet = TRUE
+ program_flags = PROGRAM_ON_SYNDINET_STORE | PROGRAM_REQUIRES_NTNET
tgui_id = "NtosNetDos"
program_icon = "satellite-dish"
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
index 88aa48d0c31..3e0ac275c14 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
@@ -1,13 +1,11 @@
/datum/computer_file/program/revelation
filename = "revelation"
filedesc = "Revelation"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "hostile"
+ downloader_category = PROGRAM_CATEGORY_DEVICE
+ program_open_overlay = "hostile"
extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution."
size = 13
- requires_ntnet = FALSE
- available_on_ntnet = FALSE
- available_on_syndinet = TRUE
+ program_flags = PROGRAM_ON_SYNDINET_STORE
tgui_id = "NtosRevelation"
program_icon = "magnet"
var/armed = 0
diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm
index a51d3345d10..9f4db1963c5 100644
--- a/code/modules/modular_computers/file_system/programs/arcade.dm
+++ b/code/modules/modular_computers/file_system/programs/arcade.dm
@@ -1,9 +1,9 @@
/datum/computer_file/program/arcade
filename = "dsarcade"
filedesc = "Donksoft Micro Arcade"
- program_icon_state = "arcade"
+ program_open_overlay = "arcade"
extended_desc = "This port of the classic game 'Outbomb Cuban Pete', redesigned to run on tablets, with thrilling graphics and chilling storytelling."
- requires_ntnet = FALSE
+ downloader_category = PROGRAM_CATEGORY_GAMES
size = 6
tgui_id = "NtosArcade"
program_icon = "gamepad"
@@ -23,6 +23,20 @@
///Determines which boss image to use on the UI.
var/boss_id = 1
+///Lazy version of the arade that can be found in maintenance disks
+/datum/computer_file/program/arcade/eazy
+ filename = "dsarcadeez"
+ filedesc = "Donksoft Micro Arcade Ez"
+ filetype = "MNT"
+ program_flags = PROGRAM_UNIQUE_COPY
+ extended_desc = "Some sort of fan-made conversion of the classic game 'Outbomb Cuban Pete'. This one has you fight the weaker 'George Melon' instead."
+ boss_hp = 40
+ boss_mp = 10
+ player_hp = 35
+ player_mp = 15
+ heads_up = "Are you a bad enough dude to grief the station?"
+ boss_name = "George Melon"
+
/datum/computer_file/program/arcade/proc/game_check(mob/user)
sleep(0.5 SECONDS)
user?.mind?.adjust_experience(/datum/skill/gaming, 1)
@@ -30,7 +44,7 @@
heads_up = "You have crushed [boss_name]! Rejoice!"
playsound(computer.loc, 'sound/arcade/win.ogg', 50)
game_active = FALSE
- program_icon_state = "arcade_off"
+ program_open_overlay = "arcade_off"
if(istype(computer))
computer.update_appearance()
ticket_count += 1
@@ -41,7 +55,7 @@
heads_up = "You have been defeated... how will the station survive?"
playsound(computer.loc, 'sound/arcade/lose.ogg', 50)
game_active = FALSE
- program_icon_state = "arcade_off"
+ program_open_overlay = "arcade_off"
if(istype(computer))
computer.update_appearance()
user?.mind?.adjust_experience(/datum/skill/gaming, 10)
@@ -159,11 +173,11 @@
return TRUE
if("Start_Game")
game_active = TRUE
- boss_hp = 45
- player_hp = 30
- player_mp = 10
+ boss_hp = initial(boss_hp)
+ player_hp = initial(player_hp)
+ player_mp = initial(player_mp)
heads_up = "You stand before [boss_name]! Prepare for battle!"
- program_icon_state = "arcade"
+ program_open_overlay = "arcade"
boss_id = rand(1,6)
pause_state = FALSE
if(istype(computer))
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
index 6264531428f..fd520fbb385 100644
--- a/code/modules/modular_computers/file_system/programs/atmosscan.dm
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -6,8 +6,8 @@
/datum/computer_file/program/atmosscan
filename = "atmosscan"
filedesc = "AtmoZphere"
- category = PROGRAM_CATEGORY_ENGI
- program_icon_state = "air"
+ downloader_category = PROGRAM_CATEGORY_ENGINEERING
+ program_open_overlay = "air"
extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device."
size = 4
tgui_id = "NtosGasAnalyzer"
@@ -57,7 +57,7 @@
var/list/data = list()
var/turf/turf = get_turf(computer)
data["atmozphereMode"] = atmozphere_mode
- data["clickAtmozphereCompatible"] = (computer.hardware_flag & PROGRAM_TABLET)
+ data["clickAtmozphereCompatible"] = (computer.hardware_flag & PROGRAM_PDA)
switch (atmozphere_mode) //Null air wont cause errors, don't worry.
if(ATMOZPHERE_SCAN_ENV)
var/datum/gas_mixture/air = turf?.return_air()
@@ -74,7 +74,7 @@
atmozphere_mode = ATMOZPHERE_SCAN_ENV
UnregisterSignal(computer, COMSIG_ITEM_ATTACK_SELF_SECONDARY)
return TRUE
- if(!(computer.hardware_flag & PROGRAM_TABLET))
+ if(!(computer.hardware_flag & PROGRAM_PDA))
computer.say("Device incompatible for scanning objects!")
return FALSE
atmozphere_mode = ATMOZPHERE_SCAN_CLICK
diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
index fae48fbff2f..196ab033408 100644
--- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/borg_monitor
filename = "siliconnect"
filedesc = "SiliConnect"
- category = PROGRAM_CATEGORY_SCI
+ downloader_category = PROGRAM_CATEGORY_SCIENCE
ui_header = "borg_mon.gif"
- program_icon_state = "generic"
+ program_open_overlay = "generic"
extended_desc = "This program allows for remote monitoring of station cyborgs."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
download_access = list(ACCESS_ROBOTICS)
size = 5
tgui_id = "NtosCyborgRemoteMonitor"
@@ -148,13 +148,11 @@
/datum/computer_file/program/borg_monitor/syndicate
filename = "roboverlord"
filedesc = "Roboverlord"
- category = PROGRAM_CATEGORY_SCI
+ downloader_category = PROGRAM_CATEGORY_SCIENCE
ui_header = "borg_mon.gif"
- program_icon_state = "generic"
+ program_open_overlay = "generic"
extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs."
- requires_ntnet = FALSE
- available_on_ntnet = FALSE
- available_on_syndinet = TRUE
+ program_flags = PROGRAM_ON_SYNDINET_STORE
download_access = list()
/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R)
diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm
index cb7f6c288bb..4bbad8d1d8b 100644
--- a/code/modules/modular_computers/file_system/programs/bounty_board.dm
+++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm
@@ -1,10 +1,10 @@
/datum/computer_file/program/bounty_board
filename = "bountyboard"
filedesc = "Bounty Board Request Network"
- category = PROGRAM_CATEGORY_SUPL
- program_icon_state = "bountyboard"
+ downloader_category = PROGRAM_CATEGORY_SUPPLY
+ program_open_overlay = "bountyboard"
extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 10
tgui_id = "NtosBountyBoard"
///Reference to the currently logged in user.
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index d2133697194..b88636a7de8 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/budgetorders
filename = "orderapp"
filedesc = "NT IRN"
- category = PROGRAM_CATEGORY_SUPL
- program_icon_state = "request"
+ downloader_category = PROGRAM_CATEGORY_SUPPLY
+ program_open_overlay = "request"
extended_desc = "Nanotrasen Internal Requisition Network interface for supply purchasing using a department budget account."
- requires_ntnet = TRUE
- usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
+ can_run_on_flags = PROGRAM_LAPTOP | PROGRAM_PDA
size = 10
tgui_id = "NtosCargo"
///Are you actually placing orders with it?
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index 05ac394b7bd..c41aa8ae29a 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -1,11 +1,10 @@
/datum/computer_file/program/card_mod
filename = "plexagonidwriter"
filedesc = "Plexagon Access Management"
- category = PROGRAM_CATEGORY_CREW
- program_icon_state = "id"
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
+ program_open_overlay = "id"
extended_desc = "Program for programming employee ID cards to access parts of the station."
download_access = list(ACCESS_COMMAND)
- requires_ntnet = 0
size = 8
tgui_id = "NtosCard"
program_icon = "id-card"
diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm
index 462c17aba70..92c6e152f71 100644
--- a/code/modules/modular_computers/file_system/programs/cargoship.dm
+++ b/code/modules/modular_computers/file_system/programs/cargoship.dm
@@ -1,8 +1,8 @@
/datum/computer_file/program/shipping
filename = "shipping"
filedesc = "GrandArk Exporter"
- category = PROGRAM_CATEGORY_SUPL
- program_icon_state = "shipping"
+ downloader_category = PROGRAM_CATEGORY_SUPPLY
+ program_open_overlay = "shipping"
extended_desc = "A combination printer/scanner app that enables modular computers to print barcodes for easy scanning and shipping."
size = 6
tgui_id = "NtosShipping"
diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/file_system/programs/chatroom/conversation.dm
similarity index 100%
rename from code/modules/modular_computers/NTNet/NTNRC/conversation.dm
rename to code/modules/modular_computers/file_system/programs/chatroom/conversation.dm
diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/chatroom/ntnrc_client.dm
similarity index 98%
rename from code/modules/modular_computers/file_system/programs/ntnrc_client.dm
rename to code/modules/modular_computers/file_system/programs/chatroom/ntnrc_client.dm
index aa780fc3cf8..1067c3f392d 100644
--- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
+++ b/code/modules/modular_computers/file_system/programs/chatroom/ntnrc_client.dm
@@ -7,13 +7,12 @@
/datum/computer_file/program/chatclient
filename = "ntnrc_client"
filedesc = "Chat Client"
- category = PROGRAM_CATEGORY_CREW
- program_icon_state = "command"
+ downloader_category = PROGRAM_CATEGORY_DEVICE
+ program_open_overlay = "command"
extended_desc = "This program allows communication over NTNRC network"
size = 8
- requires_ntnet = TRUE
ui_header = "ntnrc_idle.gif"
- available_on_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
tgui_id = "NtosNetChat"
program_icon = "comment-alt"
alert_able = TRUE
diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
index 85f9a4c5ffe..f7e3df473ea 100644
--- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm
+++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/crew_manifest
filename = "plexagoncrew"
filedesc = "Plexagon Crew List"
- category = PROGRAM_CATEGORY_CREW
- program_icon_state = "id"
+ downloader_category = PROGRAM_CATEGORY_SECURITY
+ program_open_overlay = "id"
extended_desc = "Program for viewing and printing the current crew manifest"
download_access = list(ACCESS_SECURITY, ACCESS_COMMAND)
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 4
tgui_id = "NtosCrewManifest"
program_icon = "clipboard-list"
diff --git a/code/modules/modular_computers/file_system/programs/emojipedia.dm b/code/modules/modular_computers/file_system/programs/emojipedia.dm
index 277311491a7..6aa81b506d5 100644
--- a/code/modules/modular_computers/file_system/programs/emojipedia.dm
+++ b/code/modules/modular_computers/file_system/programs/emojipedia.dm
@@ -2,8 +2,8 @@
/datum/computer_file/program/emojipedia
filename = "emojipedia"
filedesc = "EmojiPedia"
- category = PROGRAM_CATEGORY_CREW // we want everyone to be able to access this application, since everyone can send emoji via PDA messages
- program_icon_state = "generic"
+ downloader_category = PROGRAM_CATEGORY_DEVICE // we want everyone to be able to access this application, since everyone can send emoji via PDA messages
+ program_open_overlay = "generic"
extended_desc = "This program allows you to view all the emojis you can send via PDA messages."
size = 3
tgui_id = "NtosEmojipedia"
diff --git a/code/modules/modular_computers/file_system/programs/file_browser.dm b/code/modules/modular_computers/file_system/programs/file_browser.dm
index 50fa29ccd5c..c9133515507 100644
--- a/code/modules/modular_computers/file_system/programs/file_browser.dm
+++ b/code/modules/modular_computers/file_system/programs/file_browser.dm
@@ -2,10 +2,9 @@
filename = "filemanager"
filedesc = "File Manager"
extended_desc = "This program allows management of files."
- program_icon_state = "generic"
+ program_open_overlay = "generic"
size = 8
- requires_ntnet = FALSE
- available_on_ntnet = FALSE
+ program_flags = NONE
undeletable = TRUE
tgui_id = "NtosFileManager"
program_icon = "folder"
diff --git a/code/modules/modular_computers/file_system/programs/frontier.dm b/code/modules/modular_computers/file_system/programs/frontier.dm
index 3b001e9f39d..884e635364c 100644
--- a/code/modules/modular_computers/file_system/programs/frontier.dm
+++ b/code/modules/modular_computers/file_system/programs/frontier.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/scipaper_program
filename = "ntfrontier"
filedesc = "NT Frontier"
- category = PROGRAM_CATEGORY_SCI
+ downloader_category = PROGRAM_CATEGORY_SCIENCE
extended_desc = "Scientific paper publication and navigation software."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 12
- program_icon_state = "research"
+ program_open_overlay = "research"
tgui_id = "NtosScipaper"
program_icon = "paper-plane"
download_access = list(ACCESS_ORDNANCE)
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index c04fddcd396..868c811a84f 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -4,11 +4,11 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
/datum/computer_file/program/job_management
filename = "plexagoncore"
filedesc = "Plexagon HR Core"
- category = PROGRAM_CATEGORY_CREW
- program_icon_state = "id"
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
+ program_open_overlay = "id"
extended_desc = "Program for viewing and changing job slot availability."
download_access = list(ACCESS_COMMAND)
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 4
tgui_id = "NtosJobManager"
program_icon = "address-book"
diff --git a/code/modules/modular_computers/file_system/programs/mafia_ntos.dm b/code/modules/modular_computers/file_system/programs/mafia_ntos.dm
index bbcdd39d32c..c970349bd20 100644
--- a/code/modules/modular_computers/file_system/programs/mafia_ntos.dm
+++ b/code/modules/modular_computers/file_system/programs/mafia_ntos.dm
@@ -1,9 +1,9 @@
/datum/computer_file/program/mafia
filename = "mafia"
filedesc = "Mafia"
- program_icon_state = "mafia"
+ program_open_overlay = "mafia"
extended_desc = "A program that allows you to play the infamous Mafia game, straight from your Modular PC."
- requires_ntnet = FALSE
+ downloader_category = PROGRAM_CATEGORY_GAMES
size = 6
tgui_id = "NtosMafiaPanel"
program_icon = "user-secret"
diff --git a/code/modules/modular_computers/file_system/programs/maintenance/_maintenance_program.dm b/code/modules/modular_computers/file_system/programs/maintenance/_maintenance_program.dm
index 25a2d1ad941..5cd31293f77 100644
--- a/code/modules/modular_computers/file_system/programs/maintenance/_maintenance_program.dm
+++ b/code/modules/modular_computers/file_system/programs/maintenance/_maintenance_program.dm
@@ -6,5 +6,4 @@
*/
/datum/computer_file/program/maintenance
filetype = "MNT"
- available_on_ntnet = FALSE
- unique_copy = TRUE
+ program_flags = PROGRAM_UNIQUE_COPY
diff --git a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
index 50276a1bcd8..c729e3ed5c4 100644
--- a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
+++ b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/maintenance/camera
filename = "camera_app"
filedesc = "Camera"
- program_icon_state = "camera"
- category = PROGRAM_CATEGORY_MISC
+ program_open_overlay = "camera"
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
extended_desc = "This program allows the taking of pictures."
size = 4
- usage_flags = PROGRAM_TABLET
+ can_run_on_flags = PROGRAM_PDA
tgui_id = "NtosCamera"
program_icon = "camera"
@@ -33,6 +33,8 @@
if(internal_picture)
QDEL_NULL(internal_picture)
var/turf/our_turf = get_turf(tapped_atom)
+ var/spooky_camera = locate(/datum/computer_file/program/maintenance/spectre_meter) in computer.stored_files
+ internal_camera.see_ghosts = spooky_camera ? CAMERA_SEE_GHOSTS_BASIC : CAMERA_NO_GHOSTS
internal_picture = internal_camera.captureimage(our_turf, user, internal_camera.picture_size_x + 1, internal_camera.picture_size_y + 1)
picture_number++
computer.save_photo(internal_picture.picture_image)
diff --git a/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm b/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm
index 9f8971fb7fa..c67b001942c 100644
--- a/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm
+++ b/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm
@@ -1,8 +1,8 @@
/datum/computer_file/program/maintenance/modsuit_control
filename = "modsuit_control"
filedesc = "MODsuit Control"
- program_icon_state = "modsuit_control"
- category = PROGRAM_CATEGORY_SCI
+ program_open_overlay = "modsuit_control"
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
extended_desc = "This program allows people to connect a MODsuit to it, allowing remote control."
size = 2
tgui_id = "NtosMODsuit"
@@ -28,6 +28,7 @@
return TRUE
/datum/computer_file/program/maintenance/modsuit_control/proc/unsync_modsuit(atom/source)
+ SIGNAL_HANDLER
UnregisterSignal(controlled_suit, COMSIG_QDELETING)
controlled_suit = null
diff --git a/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm b/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm
index 4c332d0d7d8..7cf05170eb8 100644
--- a/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm
+++ b/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm
@@ -1,10 +1,10 @@
/datum/computer_file/program/maintenance/phys_scanner
filename = "phys_scanner"
filedesc = "Physical Scanner"
- category = PROGRAM_CATEGORY_MISC
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
extended_desc = "This program allows the tablet to scan physical objects and display a data output."
size = 2
- usage_flags = PROGRAM_TABLET
+ can_run_on_flags = PROGRAM_PDA
tgui_id = "NtosPhysScanner"
program_icon = "barcode"
/// Information from the last scanned person, to display on the app.
diff --git a/code/modules/modular_computers/file_system/programs/maintenance/spectre_meter.dm b/code/modules/modular_computers/file_system/programs/maintenance/spectre_meter.dm
new file mode 100644
index 00000000000..8a3a3e6cb6e
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/maintenance/spectre_meter.dm
@@ -0,0 +1,135 @@
+
+#define SPOOK_VALUE_SAME_TURF_MULT 1.5
+#define SPOOK_VALUE_LIVING_MULT 6
+#define SPOOK_VALUE_DEF_MOB 10
+#define SPOOK_VALUE_ICON_STATE_MAX 120
+#define SPOOK_VALUE_SEGMENT 15
+
+/datum/computer_file/program/maintenance/spectre_meter
+ filename = "spectre_meter"
+ filedesc = "Spectre-Meter"
+ power_cell_use = PROGRAM_BASIC_CELL_USE * 2
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
+ extended_desc = "A program used to somehow detect nearby spectral presence. Combine with the camera app to take photos of ghosts."
+ size = 7
+ can_run_on_flags = PROGRAM_LAPTOP|PROGRAM_PDA
+ tgui_id = "NtosSpectreMeter"
+ program_icon = "ghost"
+ program_open_overlay = "spectre_meter_0"
+ /// The cooldown for manual scans
+ COOLDOWN_DECLARE(manual_scan_cd)
+ /// Whether the automatic scan mode is active or not
+ var/auto_mode = FALSE
+ /// The value reported by the last scan.
+ var/last_spook_value = 0
+ var/datum/looping_sound/spectre_meter/soundloop
+
+/datum/computer_file/program/maintenance/spectre_meter/on_start(mob/user)
+ . = ..()
+ if(.)
+ soundloop = new()
+
+/datum/computer_file/program/maintenance/spectre_meter/kill_program()
+ QDEL_NULL(soundloop)
+ auto_mode = FALSE
+ last_spook_value = 0
+ program_open_overlay = "spectre_meter_0"
+ power_cell_use = PROGRAM_BASIC_CELL_USE
+ STOP_PROCESSING(SSprocessing, src)
+ return ..()
+
+/datum/computer_file/program/maintenance/spectre_meter/ui_data(mob/user)
+ var/list/data = list()
+ data["spook_value"] = last_spook_value
+ data["auto_mode"] = auto_mode
+ data["on_cooldown"] = !COOLDOWN_FINISHED(src, manual_scan_cd)
+ return data
+
+/datum/computer_file/program/maintenance/spectre_meter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ switch(action)
+ if("manual_scan")
+ if(COOLDOWN_FINISHED(src, manual_scan_cd))
+ INVOKE_ASYNC(src, PROC_REF(scan_surroundings))
+ COOLDOWN_START(src, manual_scan_cd, 2 SECONDS)
+ playsound(computer, 'sound/effects/ping_hit.ogg', vol = 40, vary = TRUE)
+ return TRUE
+ if("toggle_mode")
+ auto_mode = !auto_mode
+ if(auto_mode)
+ ///We want SSprocess. It fires twice as fast than the standard SSobjs used by [computer_file/program/process_tick]
+ START_PROCESSING(SSprocessing, src)
+ soundloop.start(computer)
+ else
+ STOP_PROCESSING(SSprocessing, src)
+ soundloop.stop(TRUE)
+ power_cell_use = auto_mode ? PROGRAM_BASIC_CELL_USE * 3 : PROGRAM_BASIC_CELL_USE
+ return TRUE
+
+/datum/computer_file/program/maintenance/spectre_meter/process(seconds_per_tick)
+ if(auto_mode)
+ INVOKE_ASYNC(src, PROC_REF(scan_surroundings))
+
+///Return the "spook level" of the area the computer is in.
+/datum/computer_file/program/maintenance/spectre_meter/proc/scan_surroundings()
+ var/spook_value = 0
+ var/turf/turf = get_turf(computer)
+
+ for(var/atom/atom as anything in range(5, turf))
+ var/spook_amount = 0
+ if(ismob(atom))
+ ///ghastly mobs count toward spookiness more than observers.
+ var/spook_value_mult = 0
+ if(isliving(atom))
+ var/mob/living/living = atom
+ if(living.mob_biotypes & MOB_SPIRIT)
+ spook_value_mult = SPOOK_VALUE_LIVING_MULT
+ else if(isobserver(atom))
+ spook_value_mult = 1
+ spook_amount += SPOOK_VALUE_DEF_MOB * spook_value_mult
+ var/list/materials = atom.has_material_type(/datum/material/hauntium)
+ if(materials)
+ spook_amount += materials[/datum/material/hauntium]/SHEET_MATERIAL_AMOUNT
+ spook_amount += atom.reagents?.get_reagent_amount(/datum/reagent/hauntium)/20
+ if(!spook_amount)
+ continue
+ if(atom.loc == turf)
+ spook_amount *= SPOOK_VALUE_SAME_TURF_MULT
+ spook_value += spook_amount/max(get_dist(turf, atom), 1)
+ CHECK_TICK
+
+ soundloop.last_spook_value = last_spook_value = round(spook_value)
+ var/old_open_overlay = program_open_overlay
+ program_open_overlay = "spectre_meter_[min(FLOOR(last_spook_value, SPOOK_VALUE_SEGMENT), SPOOK_VALUE_ICON_STATE_MAX)]"
+ if(program_open_overlay != old_open_overlay)
+ computer.update_appearance(UPDATE_OVERLAYS)
+
+/datum/looping_sound/spectre_meter
+ mid_sounds = /datum/looping_sound/geiger::mid_sounds
+ mid_length = 2
+ volume = 12
+ var/last_spook_value = 0
+
+/datum/looping_sound/spectre_meter/get_sound()
+ var/index = 1
+ switch(last_spook_value)
+ if(0 to 14)
+ return null
+ if(14 to 40)
+ index = 1
+ if(40 to 65)
+ index = 2
+ if(65 to 90)
+ index = 3
+ else
+ index = 4
+ return ..(mid_sounds[index])
+
+/datum/looping_sound/spectre_meter/stop(null_parent = FALSE)
+ last_spook_value = 0
+ return ..()
+
+#undef SPOOK_VALUE_SAME_TURF_MULT
+#undef SPOOK_VALUE_LIVING_MULT
+#undef SPOOK_VALUE_DEF_MOB
+#undef SPOOK_VALUE_ICON_STATE_MAX
+#undef SPOOK_VALUE_SEGMENT
diff --git a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
index 637b2b33f16..0b69fd5230f 100644
--- a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
+++ b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
@@ -8,14 +8,14 @@
/datum/computer_file/program/messenger
filename = "nt_messenger"
filedesc = "Direct Messenger"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "command"
+ downloader_category = PROGRAM_CATEGORY_DEVICE
+ program_open_overlay = "command"
extended_desc = "This program allows old-school communication with other modular devices."
size = 0
undeletable = TRUE // It comes by default in tablets, can't be downloaded, takes no space and should obviously not be able to be deleted.
- header_program = TRUE
- available_on_ntnet = FALSE
- usage_flags = PROGRAM_TABLET
+ power_cell_use = NONE
+ program_flags = PROGRAM_HEADER | PROGRAM_RUNS_WITHOUT_POWER
+ can_run_on_flags = PROGRAM_PDA
ui_header = "ntnrc_idle.gif"
tgui_id = "NtosMessenger"
program_icon = "comment-alt"
diff --git a/code/modules/modular_computers/file_system/programs/newscasterapp.dm b/code/modules/modular_computers/file_system/programs/newscasterapp.dm
index 27aafee3a41..a19f38fb777 100644
--- a/code/modules/modular_computers/file_system/programs/newscasterapp.dm
+++ b/code/modules/modular_computers/file_system/programs/newscasterapp.dm
@@ -2,12 +2,11 @@
filename = "newscasterapp"
filedesc = "Newscaster"
download_access = list(ACCESS_LIBRARY)
- category = PROGRAM_CATEGORY_CREW
- program_icon_state = "bountyboard"
+ downloader_category = PROGRAM_CATEGORY_GAMES
+ program_open_overlay = "bountyboard"
extended_desc = "This program allows any user to access the Newscaster network from anywhere."
size = 2
- requires_ntnet = TRUE
- available_on_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
tgui_id = "NtosNewscaster"
program_icon = "newspaper"
///The UI we use for the newscaster
diff --git a/code/modules/modular_computers/file_system/programs/notepad.dm b/code/modules/modular_computers/file_system/programs/notepad.dm
index c0232fe3888..a2fdd5929e8 100644
--- a/code/modules/modular_computers/file_system/programs/notepad.dm
+++ b/code/modules/modular_computers/file_system/programs/notepad.dm
@@ -1,13 +1,13 @@
/datum/computer_file/program/notepad
filename = "notepad"
filedesc = "Notepad"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "generic"
+ downloader_category = PROGRAM_CATEGORY_DEVICE
+ program_open_overlay = "generic"
extended_desc = "Jot down your work-safe thoughts and what not."
size = 2
tgui_id = "NtosNotepad"
program_icon = "book"
- usage_flags = PROGRAM_ALL
+ can_run_on_flags = PROGRAM_ALL
var/written_note = "Congratulations on your station upgrading to the new NtOS and Thinktronic based collaboration effort, \
bringing you the best in electronics and software since 2467!\n\
diff --git a/code/modules/modular_computers/file_system/programs/nt_pay.dm b/code/modules/modular_computers/file_system/programs/nt_pay.dm
index 8724375d07b..abaf03fa1cc 100644
--- a/code/modules/modular_computers/file_system/programs/nt_pay.dm
+++ b/code/modules/modular_computers/file_system/programs/nt_pay.dm
@@ -1,13 +1,13 @@
/datum/computer_file/program/nt_pay
filename = "ntpay"
filedesc = "Nanotrasen Pay System"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "generic"
+ downloader_category = PROGRAM_CATEGORY_DEVICE
+ program_open_overlay = "generic"
extended_desc = "An application that locally (in your sector) helps to transfer money or track your expenses and profits."
size = 2
tgui_id = "NtosPay"
program_icon = "money-bill-wave"
- usage_flags = PROGRAM_ALL
+ can_run_on_flags = PROGRAM_ALL
///Reference to the currently logged in user.
var/datum/bank_account/current_user
///Pay token, by which we can send credits
diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
index a5aaee6dca4..728f94372cf 100644
--- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
@@ -1,32 +1,39 @@
/datum/computer_file/program/ntnetdownload
filename = "ntsoftwarehub"
filedesc = "NT Software Hub"
- program_icon_state = "generic"
+ program_open_overlay = "generic"
extended_desc = "This program allows downloads of software from official NT repositories"
undeletable = TRUE
size = 4
- requires_ntnet = TRUE
- available_on_ntnet = FALSE
+ program_flags = PROGRAM_REQUIRES_NTNET
tgui_id = "NtosNetDownloader"
program_icon = "download"
+ ///The program currently being downloaded.
var/datum/computer_file/program/downloaded_file
+ ///Boolean on whether the `downloaded_file` is being downloaded from the Syndicate store,
+ ///in which case it will appear as 'ENCRYPTED' in logs, rather than display file name.
var/hacked_download = FALSE
- var/download_completion = FALSE //GQ of downloaded data.
- var/download_netspeed = 0
- var/downloaderror = ""
+ ///How much of the data has been downloaded.
+ var/download_completion
+ ///The error message being displayed to the user, if necessary. Null if there isn't one.
+ var/downloaderror
+ ///The list of categories to display in the UI, in order of which they appear.
var/static/list/show_categories = list(
- PROGRAM_CATEGORY_CREW,
- PROGRAM_CATEGORY_ENGI,
- PROGRAM_CATEGORY_SCI,
- PROGRAM_CATEGORY_SUPL,
- PROGRAM_CATEGORY_MISC,
+ PROGRAM_CATEGORY_DEVICE,
+ PROGRAM_CATEGORY_EQUIPMENT,
+ PROGRAM_CATEGORY_GAMES,
+ PROGRAM_CATEGORY_SECURITY,
+ PROGRAM_CATEGORY_ENGINEERING,
+ PROGRAM_CATEGORY_SUPPLY,
+ PROGRAM_CATEGORY_SCIENCE,
)
/datum/computer_file/program/ntnetdownload/kill_program(mob/user)
- . = ..()
+ abort_file_download()
ui_header = null
+ . = ..()
/datum/computer_file/program/ntnetdownload/proc/begin_file_download(filename)
if(downloaded_file)
@@ -38,7 +45,7 @@
return FALSE
// Attempting to download antag only program, but without having emagged/syndicate computer. No.
- if(PRG.available_on_syndinet && !(computer.obj_flags & EMAGGED))
+ if((PRG.program_flags & PROGRAM_ON_SYNDINET_STORE) && !(computer.obj_flags & EMAGGED))
return FALSE
if(!computer || !computer.can_store_file(PRG))
@@ -83,7 +90,7 @@
if(download_completion >= downloaded_file.size)
complete_file_download()
// Download speed according to connectivity state. NTNet server is assumed to be on unlimited speed so we're limited by our local connectivity
- download_netspeed = 0
+ var/download_netspeed
// Speed defines are found in misc.dm
switch(ntnet_status)
if(NTNET_LOW_SIGNAL)
@@ -92,7 +99,8 @@
download_netspeed = NTNETSPEED_HIGHSIGNAL
if(NTNET_ETHERNET_SIGNAL)
download_netspeed = NTNETSPEED_ETHERNET
- download_completion += download_netspeed
+ if(download_netspeed)
+ download_completion += download_netspeed
/datum/computer_file/program/ntnetdownload/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
switch(action)
@@ -103,7 +111,6 @@
if("PRG_reseterror")
if(downloaderror)
download_completion = FALSE
- download_netspeed = FALSE
downloaded_file = null
downloaderror = ""
return TRUE
@@ -121,7 +128,6 @@
data["downloadname"] = downloaded_file.filename
data["downloaddesc"] = downloaded_file.filedesc
data["downloadsize"] = downloaded_file.size
- data["downloadspeed"] = download_netspeed
data["downloadcompletion"] = round(download_completion, 0.1)
data["disk_size"] = computer.max_capacity
@@ -129,35 +135,27 @@
data["emagged"] = (computer.obj_flags & EMAGGED)
var/list/repo = SSmodular_computers.available_antag_software | SSmodular_computers.available_station_software
- var/list/program_categories = list()
for(var/datum/computer_file/program/programs as anything in repo)
- if(!(programs.category in program_categories))
- program_categories.Add(programs.category)
data["programs"] += list(list(
"icon" = programs.program_icon,
"filename" = programs.filename,
"filedesc" = programs.filedesc,
"fileinfo" = programs.extended_desc,
- "category" = programs.category,
+ "category" = programs.downloader_category,
"installed" = !!computer.find_file_by_name(programs.filename),
"compatible" = check_compatibility(programs),
"size" = programs.size,
"access" = programs.can_run(user, downloading = TRUE, access = access),
- "verifiedsource" = programs.available_on_ntnet,
+ "verifiedsource" = !!(programs.program_flags & PROGRAM_ON_NTNET_STORE),
))
- data["categories"] = show_categories & program_categories
+ data["categories"] = show_categories
return data
-/datum/computer_file/program/ntnetdownload/proc/check_compatibility(datum/computer_file/program/P)
- var/hardflag = computer.hardware_flag
-
- if(P?.is_supported_by_hardware(hardware_flag = hardflag, loud = FALSE))
- return TRUE
- return FALSE
-
-/datum/computer_file/program/ntnetdownload/kill_program(mob/user)
- abort_file_download()
- return ..()
+///Checks if a provided `program_to_check` is compatible to be downloaded on our computer.
+/datum/computer_file/program/ntnetdownload/proc/check_compatibility(datum/computer_file/program/program_to_check)
+ if(!program_to_check || !program_to_check.is_supported_by_hardware(hardware_flag = computer.hardware_flag, loud = FALSE))
+ return FALSE
+ return TRUE
diff --git a/code/modules/modular_computers/file_system/programs/portrait_printer.dm b/code/modules/modular_computers/file_system/programs/portrait_printer.dm
index cdfa24be1b1..6fc1358ccaf 100644
--- a/code/modules/modular_computers/file_system/programs/portrait_printer.dm
+++ b/code/modules/modular_computers/file_system/programs/portrait_printer.dm
@@ -12,12 +12,12 @@
/datum/computer_file/program/portrait_printer
filename = "PortraitPrinter"
filedesc = "Marlowe Treeby's Art Galaxy"
- category = PROGRAM_CATEGORY_CREW
- program_icon_state = "dummy"
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
+ program_open_overlay = "dummy"
extended_desc = "This program connects to a Spinward Sector community art site for viewing and printing art."
download_access = list(ACCESS_LIBRARY)
- usage_flags = PROGRAM_CONSOLE
- requires_ntnet = TRUE
+ can_run_on_flags = PROGRAM_CONSOLE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 9
tgui_id = "NtosPortraitPrinter"
program_icon = "paint-brush"
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index 9a8026ccd6c..c5a8eba952b 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -3,13 +3,13 @@
/datum/computer_file/program/power_monitor
filename = "ampcheck"
filedesc = "AmpCheck"
- category = PROGRAM_CATEGORY_ENGI
- program_icon_state = "power_monitor"
+ downloader_category = PROGRAM_CATEGORY_ENGINEERING
+ program_open_overlay = "power_monitor"
extended_desc = "This program connects to sensors around the station to provide information about electrical systems"
ui_header = "power_norm.gif"
download_access = list(ACCESS_ENGINEERING)
- usage_flags = PROGRAM_CONSOLE
- requires_ntnet = FALSE
+ can_run_on_flags = PROGRAM_CONSOLE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 8
tgui_id = "NtosPowerMonitor"
program_icon = "plug"
diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm
index 7c074040cef..59647db7dce 100644
--- a/code/modules/modular_computers/file_system/programs/radar.dm
+++ b/code/modules/modular_computers/file_system/programs/radar.dm
@@ -1,12 +1,11 @@
/datum/computer_file/program/radar //generic parent that handles most of the process
filename = "genericfinder"
filedesc = "debug_finder"
- category = PROGRAM_CATEGORY_CREW
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
ui_header = "borg_mon.gif" //DEBUG -- new icon before PR
- program_icon_state = "radarntos"
- requires_ntnet = TRUE
- available_on_ntnet = FALSE
- usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
+ program_open_overlay = "radarntos"
+ program_flags = PROGRAM_REQUIRES_NTNET
+ can_run_on_flags = PROGRAM_LAPTOP | PROGRAM_PDA
size = 5
tgui_id = "NtosRadar"
///List of trackable entities. Updated by the scan() proc.
@@ -15,7 +14,7 @@
var/selected
///Used to store when the next scan is available.
COOLDOWN_DECLARE(next_scan)
- ///Used to keep track of the last value program_icon_state was set to, to prevent constant unnecessary update_appearance() calls
+ ///Used to keep track of the last value program_open_overlay was set to, to prevent constant unnecessary update_appearance() calls
var/last_icon_state = ""
///Used by the tgui interface, themed NT or Syndicate.
var/arrowstyle = "ntosradarpointer.png"
@@ -181,10 +180,10 @@
var/atom/movable/signal = find_atom()
if(!trackable(signal))
- program_icon_state = "[initial(program_icon_state)]lost"
- if(last_icon_state != program_icon_state)
+ program_open_overlay = "[initial(program_open_overlay)]lost"
+ if(last_icon_state != program_open_overlay)
computer.update_appearance()
- last_icon_state = program_icon_state
+ last_icon_state = program_open_overlay
return
var/here_turf = get_turf(computer)
@@ -192,17 +191,17 @@
var/trackdistance = get_dist_euclidian(here_turf, target_turf)
switch(trackdistance)
if(0)
- program_icon_state = "[initial(program_icon_state)]direct"
+ program_open_overlay = "[initial(program_open_overlay)]direct"
if(1 to 12)
- program_icon_state = "[initial(program_icon_state)]close"
+ program_open_overlay = "[initial(program_open_overlay)]close"
if(13 to 24)
- program_icon_state = "[initial(program_icon_state)]medium"
+ program_open_overlay = "[initial(program_open_overlay)]medium"
if(25 to INFINITY)
- program_icon_state = "[initial(program_icon_state)]far"
+ program_open_overlay = "[initial(program_open_overlay)]far"
- if(last_icon_state != program_icon_state)
+ if(last_icon_state != program_open_overlay)
computer.update_appearance()
- last_icon_state = program_icon_state
+ last_icon_state = program_open_overlay
computer.setDir(get_dir(here_turf, target_turf))
//We can use process_tick to restart fast processing, since the computer will be running this constantly either way.
@@ -219,9 +218,8 @@
filename = "lifeline"
filedesc = "Lifeline"
extended_desc = "This program allows for tracking of crew members via their suit sensors."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
download_access = list(ACCESS_MEDICAL)
- available_on_ntnet = TRUE
program_icon = "heartbeat"
/datum/computer_file/program/radar/lifeline/find_atom()
@@ -259,9 +257,8 @@
filename = "custodiallocator"
filedesc = "Custodial Locator"
extended_desc = "This program allows for tracking of custodial equipment."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
download_access = list(ACCESS_JANITOR)
- available_on_ntnet = TRUE
program_icon = "broom"
size = 2
detomatix_resistance = DETOMATIX_RESIST_MINOR
@@ -302,12 +299,9 @@
/datum/computer_file/program/radar/fission360
filename = "fission360"
filedesc = "Fission360"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "radarsyndicate"
+ program_open_overlay = "radarsyndicate"
extended_desc = "This program allows for tracking of nuclear authorization disks and warheads."
- requires_ntnet = FALSE
- available_on_ntnet = FALSE
- available_on_syndinet = TRUE
+ program_flags = PROGRAM_ON_SYNDINET_STORE
tgui_id = "NtosRadarSyndicate"
program_icon = "bomb"
arrowstyle = "ntosradarpointerS.png"
diff --git a/code/modules/modular_computers/file_system/programs/records.dm b/code/modules/modular_computers/file_system/programs/records.dm
index 12c682f5626..063c19d35e1 100644
--- a/code/modules/modular_computers/file_system/programs/records.dm
+++ b/code/modules/modular_computers/file_system/programs/records.dm
@@ -2,13 +2,13 @@
filename = "ntrecords"
filedesc = "Records"
extended_desc = "Allows the user to view several basic records from the crew."
- category = PROGRAM_CATEGORY_MISC
+ downloader_category = PROGRAM_CATEGORY_SECURITY
program_icon = "clipboard"
- program_icon_state = "crew"
+ program_open_overlay = "crew"
tgui_id = "NtosRecords"
size = 4
- usage_flags = PROGRAM_TABLET | PROGRAM_LAPTOP
- available_on_ntnet = FALSE
+ can_run_on_flags = PROGRAM_PDA | PROGRAM_LAPTOP
+ program_flags = NONE
detomatix_resistance = DETOMATIX_RESIST_MINOR
var/mode
@@ -19,7 +19,7 @@
program_icon = "book-medical"
extended_desc = "Allows the user to view several basic medical records from the crew."
download_access = list(ACCESS_MEDICAL, ACCESS_FLAG_COMMAND)
- available_on_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE
mode = "medical"
/datum/computer_file/program/records/security
@@ -27,7 +27,7 @@
filename = "secrecords"
extended_desc = "Allows the user to view several basic security records from the crew."
download_access = list(ACCESS_SECURITY, ACCESS_FLAG_COMMAND)
- available_on_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE
mode = "security"
/datum/computer_file/program/records/proc/GetRecordsReadable()
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
index 52bfafdcf8e..37ae20f5260 100644
--- a/code/modules/modular_computers/file_system/programs/robocontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -2,10 +2,10 @@
/datum/computer_file/program/robocontrol
filename = "botkeeper"
filedesc = "BotKeeper"
- category = PROGRAM_CATEGORY_SCI
- program_icon_state = "robot"
+ downloader_category = PROGRAM_CATEGORY_SCIENCE
+ program_open_overlay = "robot"
extended_desc = "A remote controller used for giving basic commands to non-sentient robots."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 6
tgui_id = "NtosRoboControl"
program_icon = "robot"
diff --git a/code/modules/modular_computers/file_system/programs/robotact.dm b/code/modules/modular_computers/file_system/programs/robotact.dm
index e64691ccba2..d7a7122090d 100644
--- a/code/modules/modular_computers/file_system/programs/robotact.dm
+++ b/code/modules/modular_computers/file_system/programs/robotact.dm
@@ -1,14 +1,13 @@
/datum/computer_file/program/robotact
filename = "robotact"
filedesc = "RoboTact"
- category = PROGRAM_CATEGORY_SCI
+ downloader_category = PROGRAM_CATEGORY_SCIENCE
extended_desc = "A built-in app for cyborg self-management and diagnostics."
ui_header = "robotact.gif" //DEBUG -- new icon before PR
- program_icon_state = "command"
- requires_ntnet = FALSE
- available_on_ntnet = FALSE
+ program_open_overlay = "command"
+ program_flags = NONE
undeletable = TRUE
- usage_flags = PROGRAM_TABLET
+ can_run_on_flags = PROGRAM_PDA
size = 5
tgui_id = "NtosRobotact"
program_icon = "terminal"
@@ -21,7 +20,7 @@
if(.)
var/obj/item/modular_computer/pda/silicon/tablet = computer
if(tablet.device_theme == PDA_THEME_SYNDICATE)
- program_icon_state = "command-syndicate"
+ program_open_overlay = "command-syndicate"
return TRUE
return FALSE
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index bb31d74f9ab..c9e4fc087f3 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -3,13 +3,13 @@
/datum/computer_file/program/secureye
filename = "secureye"
filedesc = "SecurEye"
- category = PROGRAM_CATEGORY_MISC
+ downloader_category = PROGRAM_CATEGORY_SECURITY
ui_header = "borg_mon.gif"
- program_icon_state = "generic"
+ program_open_overlay = "generic"
extended_desc = "This program allows access to standard security camera networks."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
download_access = list(ACCESS_SECURITY)
- usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
+ can_run_on_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
size = 5
tgui_id = "NtosSecurEye"
program_icon = "eye"
@@ -40,11 +40,8 @@
filedesc = "SyndEye"
extended_desc = "This program allows for illegal access to security camera networks."
download_access = list()
- available_on_ntnet = FALSE
- available_on_syndinet = TRUE
- requires_ntnet = FALSE
- usage_flags = PROGRAM_ALL
- unique_copy = TRUE
+ can_run_on_flags = PROGRAM_ALL
+ program_flags = PROGRAM_ON_SYNDINET_STORE | PROGRAM_UNIQUE_COPY
network = list("ss13", "mine", "rd", "labor", "ordnance", "minisat")
spying = TRUE
diff --git a/code/modules/modular_computers/file_system/programs/signalcommander.dm b/code/modules/modular_computers/file_system/programs/signalcommander.dm
index e8140b62b17..2032ad5461b 100644
--- a/code/modules/modular_computers/file_system/programs/signalcommander.dm
+++ b/code/modules/modular_computers/file_system/programs/signalcommander.dm
@@ -1,13 +1,13 @@
/datum/computer_file/program/signal_commander
filename = "signaler"
filedesc = "SignalCommander"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "signal"
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
+ program_open_overlay = "signal"
extended_desc = "A small built-in frequency app that sends out signaller signals with the appropriate hardware."
size = 2
tgui_id = "NtosSignaler"
program_icon = "satellite-dish"
- usage_flags = PROGRAM_TABLET | PROGRAM_LAPTOP
+ can_run_on_flags = PROGRAM_PDA | PROGRAM_LAPTOP
///What is the saved signal frequency?
var/signal_frequency = FREQ_SIGNALER
/// What is the saved signal code?
diff --git a/code/modules/modular_computers/file_system/programs/skill_tracker.dm b/code/modules/modular_computers/file_system/programs/skill_tracker.dm
index c68cffb3374..9063e3e239d 100644
--- a/code/modules/modular_computers/file_system/programs/skill_tracker.dm
+++ b/code/modules/modular_computers/file_system/programs/skill_tracker.dm
@@ -1,13 +1,13 @@
/datum/computer_file/program/skill_tracker
filename = "skilltracker"
filedesc = "ExperTrak Skill Tracker"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "generic"
+ downloader_category = PROGRAM_CATEGORY_DEVICE
+ program_open_overlay = "generic"
extended_desc = "Scan and view your current marketable job skills."
size = 2
tgui_id = "NtosSkillTracker"
program_icon = "medal"
- usage_flags = PROGRAM_TABLET // Must be a handheld device to read read your chakras or whatever
+ can_run_on_flags = PROGRAM_PDA // Must be a handheld device to read read your chakras or whatever
/datum/computer_file/program/skill_tracker/ui_data(mob/user)
var/list/data = list()
diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
index e310d402e9e..0002aa983cc 100644
--- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/supermatter_monitor
filename = "ntcims"
filedesc = "NT CIMS"
- category = PROGRAM_CATEGORY_ENGI
+ downloader_category = PROGRAM_CATEGORY_ENGINEERING
ui_header = "smmon_0.gif"
- program_icon_state = "smmon_0"
+ program_open_overlay = "smmon_0"
extended_desc = "Crystal Integrity Monitoring System, connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
download_access = list(ACCESS_CONSTRUCTION)
size = 5
tgui_id = "NtosSupermatter"
@@ -109,6 +109,6 @@
if(last_status != new_status)
last_status = new_status
ui_header = "smmon_[last_status].gif"
- program_icon_state = "smmon_[last_status]"
+ program_open_overlay = "smmon_[last_status]"
if(istype(computer))
computer.update_appearance()
diff --git a/code/modules/modular_computers/file_system/programs/statusdisplay.dm b/code/modules/modular_computers/file_system/programs/statusdisplay.dm
index 9350254f646..9d21b477971 100644
--- a/code/modules/modular_computers/file_system/programs/statusdisplay.dm
+++ b/code/modules/modular_computers/file_system/programs/statusdisplay.dm
@@ -2,15 +2,14 @@
filename = "statusdisplay"
filedesc = "Status Display"
program_icon = "signal"
- program_icon_state = "generic"
- requires_ntnet = TRUE
+ program_open_overlay = "generic"
size = 1
extended_desc = "An app used to change the message on the station status displays."
tgui_id = "NtosStatus"
- usage_flags = PROGRAM_ALL
- available_on_ntnet = FALSE
+ can_run_on_flags = PROGRAM_ALL
+ program_flags = PROGRAM_REQUIRES_NTNET
var/upper_text = ""
var/lower_text = ""
diff --git a/code/modules/modular_computers/file_system/programs/techweb.dm b/code/modules/modular_computers/file_system/programs/techweb.dm
index ad503422b47..3d9af432b69 100644
--- a/code/modules/modular_computers/file_system/programs/techweb.dm
+++ b/code/modules/modular_computers/file_system/programs/techweb.dm
@@ -1,10 +1,10 @@
/datum/computer_file/program/science
filename = "experi_track"
filedesc = "Nanotrasen Science Hub"
- category = PROGRAM_CATEGORY_SCI
- program_icon_state = "research"
+ downloader_category = PROGRAM_CATEGORY_SCIENCE
+ program_open_overlay = "research"
extended_desc = "Connect to the internal science server in order to assist in station research efforts."
- requires_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
size = 10
tgui_id = "NtosTechweb"
program_icon = "atom"
diff --git a/code/modules/modular_computers/file_system/programs/theme_selector.dm b/code/modules/modular_computers/file_system/programs/theme_selector.dm
index 9bc15a1a00b..5fdf1c8d365 100644
--- a/code/modules/modular_computers/file_system/programs/theme_selector.dm
+++ b/code/modules/modular_computers/file_system/programs/theme_selector.dm
@@ -2,12 +2,10 @@
filename = "themeify"
filedesc = "Themeify"
extended_desc = "This program allows configuration of your device's theme."
- program_icon_state = "generic"
+ program_open_overlay = "generic"
undeletable = TRUE
size = 0
- header_program = TRUE
- available_on_ntnet = TRUE
- requires_ntnet = FALSE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_HEADER
tgui_id = "NtosThemeConfigure"
program_icon = "paint-roller"
diff --git a/code/modules/modular_computers/file_system/programs/wirecarp.dm b/code/modules/modular_computers/file_system/programs/wirecarp.dm
index ed83f2ca334..64ef7137b00 100644
--- a/code/modules/modular_computers/file_system/programs/wirecarp.dm
+++ b/code/modules/modular_computers/file_system/programs/wirecarp.dm
@@ -1,13 +1,12 @@
/datum/computer_file/program/ntnetmonitor
filename = "wirecarp"
filedesc = "WireCarp"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "comm_monitor"
+ downloader_category = PROGRAM_CATEGORY_SECURITY
+ program_open_overlay = "comm_monitor"
extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes"
size = 12
- requires_ntnet = TRUE
run_access = list(ACCESS_NETWORK) //NETWORK CONTROL IS A MORE SECURE PROGRAM.
- available_on_ntnet = TRUE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
tgui_id = "NtosNetMonitor"
program_icon = "network-wired"
@@ -51,7 +50,7 @@
data["idsalarm"] = SSmodular_computers.intrusion_detection_alarm
data["ntnetlogs"] = list()
- for(var/i in SSmodular_computers.logs)
+ for(var/i in SSmodular_computers.modpc_logs)
data["ntnetlogs"] += list(list("entry" = i))
data["tablets"] = list()
diff --git a/code/modules/pai/pai.dm b/code/modules/pai/pai.dm
index 832718f0b17..3c7bd93c3bd 100644
--- a/code/modules/pai/pai.dm
+++ b/code/modules/pai/pai.dm
@@ -259,6 +259,15 @@
held_state = "[chassis]"
return ..()
+/mob/living/silicon/pai/set_stat(new_stat)
+ . = ..()
+ update_stat()
+
+/mob/living/silicon/pai/on_knockedout_trait_loss(datum/source)
+ . = ..()
+ set_stat(CONSCIOUS)
+ update_stat()
+
/**
* Resolves the weakref of the pai's master.
* If the master has been deleted, calls reset_software().
diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm
index b33c37290c2..dae12f33bd4 100644
--- a/code/modules/photography/camera/camera.dm
+++ b/code/modules/photography/camera/camera.dm
@@ -121,6 +121,9 @@
return FALSE
else if(!(get_turf(target) in get_hear(world.view, user)))
return FALSE
+ else if(isliving(loc))
+ if(!(get_turf(target) in view(world.view, loc)))
+ return FALSE
else //user is an atom or null
if(!(get_turf(target) in view(world.view, user || src)))
return FALSE
@@ -241,27 +244,30 @@
printpicture(user, picture)
/obj/item/camera/proc/printpicture(mob/user, datum/picture/picture) //Normal camera proc for creating photos
- if(!user)
- return
pictures_left--
var/obj/item/photo/new_photo = new(get_turf(src), picture)
- if(in_range(new_photo, user) && user.put_in_hands(new_photo)) //needed because of TK
- to_chat(user, span_notice("[pictures_left] photos left."))
+ if(user)
+ if(in_range(new_photo, user) && user.put_in_hands(new_photo)) //needed because of TK
+ to_chat(user, span_notice("[pictures_left] photos left."))
- if(can_customise)
- var/customise = tgui_alert(user, "Do you want to customize the photo?", "Customization", list("Yes", "No"))
- if(customise == "Yes")
- var/name1 = tgui_input_text(user, "Set a name for this photo, or leave blank.", "Name", max_length = 32)
- var/desc1 = tgui_input_text(user, "Set a description to add to photo, or leave blank.", "Description", max_length = 128)
- var/caption = tgui_input_text(user, "Set a caption for this photo, or leave blank.", "Caption", max_length = 256)
- if(name1)
- picture.picture_name = name1
- if(desc1)
- picture.picture_desc = "[desc1] - [picture.picture_desc]"
- if(caption)
- picture.caption = caption
- else if(default_picture_name)
- picture.picture_name = default_picture_name
+ if(can_customise)
+ var/customise = tgui_alert(user, "Do you want to customize the photo?", "Customization", list("Yes", "No"))
+ if(customise == "Yes")
+ var/name1 = tgui_input_text(user, "Set a name for this photo, or leave blank.", "Name", max_length = 32)
+ var/desc1 = tgui_input_text(user, "Set a description to add to photo, or leave blank.", "Description", max_length = 128)
+ var/caption = tgui_input_text(user, "Set a caption for this photo, or leave blank.", "Caption", max_length = 256)
+ if(name1)
+ picture.picture_name = name1
+ if(desc1)
+ picture.picture_desc = "[desc1] - [picture.picture_desc]"
+ if(caption)
+ picture.caption = caption
+ else if(default_picture_name)
+ picture.picture_name = default_picture_name
+ else if(isliving(loc))
+ var/mob/living/holder = loc
+ if(holder.put_in_hands(new_photo))
+ to_chat(holder, span_notice("[pictures_left] photos left."))
new_photo.set_picture(picture, TRUE, TRUE)
if(CONFIG_GET(flag/picture_logging_camera))
@@ -327,6 +333,6 @@
return
if(!camera.can_target(target))
return
- INVOKE_ASYNC(camera, TYPE_PROC_REF(/obj/item/camera, captureimage), target, null, camera.picture_size_y - 1, camera.picture_size_y - 1)
+ INVOKE_ASYNC(camera, TYPE_PROC_REF(/obj/item/camera, captureimage), target, null, camera.picture_size_x - 1, camera.picture_size_y - 1)
#undef CAMERA_PICTURE_SIZE_HARD_LIMIT
diff --git a/code/modules/plumbing/plumbers/plumbing_buffer.dm b/code/modules/plumbing/plumbers/plumbing_buffer.dm
index b2bb21bc24e..7b3ef306d04 100644
--- a/code/modules/plumbing/plumbers/plumbing_buffer.dm
+++ b/code/modules/plumbing/plumbers/plumbing_buffer.dm
@@ -33,11 +33,11 @@
SIGNAL_HANDLER
if(!buffer_net)
return
- if(reagents.total_volume + CHEMICAL_QUANTISATION_LEVEL >= activation_volume && mode == UNREADY)
+ if(reagents.total_volume >= activation_volume && mode == UNREADY)
mode = IDLE
buffer_net.check_active()
- else if(reagents.total_volume + CHEMICAL_QUANTISATION_LEVEL < activation_volume && mode != UNREADY)
+ else if(reagents.total_volume < activation_volume && mode != UNREADY)
mode = UNREADY
buffer_net.check_active()
diff --git a/code/modules/plumbing/plumbers/reaction_chamber.dm b/code/modules/plumbing/plumbers/reaction_chamber.dm
index 965b428acbd..2b56bfb4ae6 100644
--- a/code/modules/plumbing/plumbers/reaction_chamber.dm
+++ b/code/modules/plumbing/plumbers/reaction_chamber.dm
@@ -43,7 +43,7 @@
/obj/machinery/plumbing/reaction_chamber/proc/on_reagent_change(datum/reagents/holder, ...)
SIGNAL_HANDLER
- if(holder.total_volume <= CHEMICAL_VOLUME_ROUNDING && emptying) //we were emptying, but now we aren't
+ if(!holder.total_volume && emptying) //we were emptying, but now we aren't
emptying = FALSE
holder.flags |= NO_REACT
return NONE
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index eb2a5b4d1a2..842772ed0b9 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -20,12 +20,12 @@
throw_speed = 2
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
+ custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*7, /datum/material/glass=SMALL_MATERIAL_AMOUNT*0.5)
+ grind_results = list(/datum/reagent/lithium = 15, /datum/reagent/iron = 5, /datum/reagent/silicon = 5)
///Current charge in cell units
var/charge = 0
///Maximum charge in cell units
var/maxcharge = STANDARD_CELL_CHARGE
- custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*7, /datum/material/glass=SMALL_MATERIAL_AMOUNT*0.5)
- grind_results = list(/datum/reagent/lithium = 15, /datum/reagent/iron = 5, /datum/reagent/silicon = 5)
///If the cell has been booby-trapped by injecting it with plasma. Chance on use() to explode.
var/rigged = FALSE
///If the power cell was damaged by an explosion, chance for it to become corrupted and function the same as rigged.
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 93e3a2a7e4b..a0c6a76f644 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -190,7 +190,6 @@ GLOBAL_LIST_EMPTY(gravity_generators)
if(count <= 3) // Their sprite is the top part of the generator
part.set_density(FALSE)
part.layer = WALL_OBJ_LAYER
- SET_PLANE(part, GAME_PLANE_UPPER, our_turf)
part.sprite_number = count
part.main_part = src
generator_parts += part
diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm
index 8559ae9fb5a..a441e2b0a71 100644
--- a/code/modules/power/lighting/light.dm
+++ b/code/modules/power/lighting/light.dm
@@ -5,7 +5,6 @@
icon_state = "tube"
desc = "A lighting fixture."
layer = WALL_OBJ_LAYER
- plane = GAME_PLANE_UPPER
max_integrity = 100
use_power = ACTIVE_POWER_USE
idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.02
diff --git a/code/modules/power/lighting/light_construct.dm b/code/modules/power/lighting/light_construct.dm
index 5be904dad86..905ae72c2e3 100644
--- a/code/modules/power/lighting/light_construct.dm
+++ b/code/modules/power/lighting/light_construct.dm
@@ -5,7 +5,6 @@
icon_state = "tube-construct-stage1"
anchored = TRUE
layer = WALL_OBJ_LAYER
- plane = GAME_PLANE_UPPER
max_integrity = 200
armor_type = /datum/armor/structure_light_construct
diff --git a/code/modules/power/supermatter/supermatter_variants.dm b/code/modules/power/supermatter/supermatter_variants.dm
index 2390ab3d0b7..9d69066a535 100644
--- a/code/modules/power/supermatter/supermatter_variants.dm
+++ b/code/modules/power/supermatter/supermatter_variants.dm
@@ -19,7 +19,6 @@
absorption_ratio = 0.125
explosion_power = 12
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
moveable = TRUE
/// Shard SM with it's processing disabled.
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 3e08d92a859..5d2bcd9f4d2 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -13,7 +13,6 @@
generic_canpass = FALSE
blocks_emissive = EMISSIVE_BLOCK_GENERIC
layer = MOB_LAYER
- plane = GAME_PLANE_FOV_HIDDEN
//The sound this plays on impact.
var/hitsound // SKYRAT EDIT CHANGE
var/hitsound_wall = ""
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 2f3214d5d90..45a1b8cb577 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -400,7 +400,7 @@
var/datum/antagonist/A = target.mind.has_antag_datum(/datum/antagonist/)
if(A)
poll_message = "[poll_message] Status:[A.name]."
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob(poll_message, ROLE_PAI, FALSE, 10 SECONDS, target)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob(poll_message, check_jobban = ROLE_PAI, poll_time = 10 SECONDS, target_mob = target, pic_source = target, role_name_text = "bolt of possession")
if(target.stat == DEAD)//boo.
return
if(LAZYLEN(candidates))
diff --git a/code/modules/projectiles/projectile/special/curse.dm b/code/modules/projectiles/projectile/special/curse.dm
index 25eef224e0d..03b2e0feb53 100644
--- a/code/modules/projectiles/projectile/special/curse.dm
+++ b/code/modules/projectiles/projectile/special/curse.dm
@@ -7,7 +7,6 @@
base_icon_state = "cursehand"
hitsound = 'sound/effects/curse4.ogg'
layer = LARGE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
damage_type = BURN
damage = 10
paralyze = 20
diff --git a/code/modules/reagents/chemistry/equilibrium.dm b/code/modules/reagents/chemistry/equilibrium.dm
index a6f21a6c6df..c9d6226930c 100644
--- a/code/modules/reagents/chemistry/equilibrium.dm
+++ b/code/modules/reagents/chemistry/equilibrium.dm
@@ -168,7 +168,7 @@
step_target_vol = 0
reacted_vol = 0 //Because volumes can be lost mid reactions
for(var/product in reaction.results)
- step_target_vol += (multiplier * reaction.results[product])
+ step_target_vol += multiplier * reaction.results[product]
reacted_vol += holder.get_reagent_amount(product)
target_vol = reacted_vol + step_target_vol
return TRUE
@@ -277,7 +277,7 @@
//Calculate DeltaT (Deviation of T from optimal)
if(!reaction.is_cold_recipe)
if (cached_temp < reaction.optimal_temp && cached_temp >= reaction.required_temp)
- delta_t = (((cached_temp - reaction.required_temp) ** reaction.temp_exponent_factor) / ((reaction.optimal_temp - reaction.required_temp) ** reaction.temp_exponent_factor))
+ delta_t = ((cached_temp - reaction.required_temp) / (reaction.optimal_temp - reaction.required_temp)) ** reaction.temp_exponent_factor
else if (cached_temp >= reaction.optimal_temp)
delta_t = 1
else //too hot
@@ -286,7 +286,7 @@
return
else
if (cached_temp > reaction.optimal_temp && cached_temp <= reaction.required_temp)
- delta_t = (((reaction.required_temp - cached_temp) ** reaction.temp_exponent_factor) / ((reaction.required_temp - reaction.optimal_temp) ** reaction.temp_exponent_factor))
+ delta_t = ((reaction.required_temp - cached_temp) / (reaction.required_temp - reaction.optimal_temp)) ** reaction.temp_exponent_factor
else if (cached_temp <= reaction.optimal_temp)
delta_t = 1
else //Too cold
@@ -312,34 +312,39 @@
purity *= purity_modifier
//Now we calculate how much to add - this is normalised to the rate up limiter
- var/delta_chem_factor = (reaction.rate_up_lim * delta_t) * seconds_per_tick//add/remove factor
+ var/delta_chem_factor = reaction.rate_up_lim * delta_t * seconds_per_tick//add/remove factor
//keep limited
if(delta_chem_factor > step_target_vol)
delta_chem_factor = step_target_vol
//Normalise to multiproducts
- delta_chem_factor = round(delta_chem_factor / product_ratio, CHEMICAL_VOLUME_ROUNDING)
+ delta_chem_factor = round(delta_chem_factor / product_ratio, CHEMICAL_QUANTISATION_LEVEL)
if(delta_chem_factor <= 0)
to_delete = TRUE
return
//Calculate how much product to make and how much reactant to remove factors..
- for(var/reagent in reaction.required_reagents)
- holder.remove_reagent(reagent, (delta_chem_factor * reaction.required_reagents[reagent]))
+ var/required_amount
+ for(var/datum/reagent/requirement as anything in reaction.required_reagents)
+ required_amount = reaction.required_reagents[requirement]
+ if(!holder.remove_reagent(requirement, delta_chem_factor * required_amount))
+ to_delete = TRUE
+ return
//Apply pH changes
var/pH_adjust
if(reaction.reaction_flags & REACTION_PH_VOL_CONSTANT)
- pH_adjust = ((delta_chem_factor * reaction.required_reagents[reagent]) / target_vol) * (reaction.H_ion_release * h_ion_mod)
+ pH_adjust = ((delta_chem_factor * required_amount) / target_vol) * (reaction.H_ion_release * h_ion_mod)
else //Default adds pH independant of volume
- pH_adjust = (delta_chem_factor * reaction.required_reagents[reagent]) * (reaction.H_ion_release * h_ion_mod)
- holder.adjust_specific_reagent_ph(reagent, pH_adjust)
+ pH_adjust = (delta_chem_factor * required_amount) * (reaction.H_ion_release * h_ion_mod)
+ holder.adjust_specific_reagent_ph(requirement, pH_adjust)
var/step_add
var/total_step_added = 0
- for(var/product in reaction.results)
+ for(var/datum/reagent/product as anything in reaction.results)
//create the products
- step_add = delta_chem_factor * reaction.results[product]
- //Default handiling
- holder.add_reagent(product, step_add, null, cached_temp, purity, override_base_ph = TRUE)
+ step_add = holder.add_reagent(product, delta_chem_factor * reaction.results[product], null, cached_temp, purity, override_base_ph = TRUE, no_react = TRUE)
+ if(!step_add)
+ to_delete = TRUE
+ return
//Apply pH changes
var/pH_adjust
@@ -348,6 +353,8 @@
else
pH_adjust = step_add * (reaction.H_ion_release * h_ion_mod)
holder.adjust_specific_reagent_ph(product, pH_adjust)
+
+ //record amounts created
reacted_vol += step_add
total_step_added += step_add
@@ -377,14 +384,13 @@
//post reaction checks
if(!(check_fail_states(total_step_added)))
to_delete = TRUE
+ return
//end reactions faster so plumbing is faster
//length is so that plumbing is faster - but it doesn't disable competitive reactions. Basically, competitive reactions will likely reach their step target at the start, so this will disable that. We want to avoid that. But equally, we do want to full stop a holder from reacting asap so plumbing isn't waiting an tick to resolve.
- if((step_add >= step_target_vol) && (length(holder.reaction_list == 1)))
+ if((step_add >= step_target_vol) && (length(holder.reaction_list) == 1))
to_delete = TRUE
- holder.update_total()
-
/*
* Calculates the total sum normalised purity of ALL reagents in a holder
* Currently calculates it irrespective of required reagents at the start, but this should be changed if this is powergamed to required reagents
diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm
index ed7e8349055..867245e9c25 100644
--- a/code/modules/reagents/chemistry/holder/holder.dm
+++ b/code/modules/reagents/chemistry/holder/holder.dm
@@ -129,7 +129,6 @@
amount = adjusted_vol
has_split = TRUE
- update_total()
var/cached_total = total_volume
if(cached_total + amount > maximum_volume)
amount = maximum_volume - cached_total //Doesnt fit in. Make it disappear. shouldn't happen. Will happen.
@@ -195,7 +194,7 @@
set_temperature(reagtemp)
SEND_SIGNAL(src, COMSIG_REAGENTS_NEW_REAGENT, new_reagent, amount, reagtemp, data, no_react)
- if(!no_react)
+ if(!no_react && !is_reacting)
handle_reactions()
return amount
@@ -643,7 +642,7 @@
reagent_volume = round(reagent.volume, CHEMICAL_QUANTISATION_LEVEL) //round to this many decimal places
//remove very small amounts of reagents
- if((reagent_volume <= 0.05 && !is_reacting) || reagent_volume <= CHEMICAL_QUANTISATION_LEVEL)
+ if(!reagent_volume || (reagent_volume <= 0.05 && !is_reacting))
//end metabolization
if(isliving(my_atom))
if(reagent.metabolizing)
@@ -663,14 +662,14 @@
//compute volume & ph like we would normally
. += reagent_volume
- total_ph += (reagent.ph * reagent_volume)
+ total_ph += reagent.ph * reagent_volume
//reasign rounded value
reagent.volume = reagent_volume
//assign the final values, rounding up can sometimes cause overflow so bring it down
total_volume = min(round(., CHEMICAL_VOLUME_ROUNDING), maximum_volume)
- if(!.)
+ if(!total_volume)
ph = CHEMICAL_NORMAL_PH
else
ph = clamp(total_ph / total_volume, CHEMICAL_MIN_PH, CHEMICAL_MAX_PH)
diff --git a/code/modules/reagents/chemistry/holder/properties.dm b/code/modules/reagents/chemistry/holder/properties.dm
index c714c922da5..b949866a6d4 100644
--- a/code/modules/reagents/chemistry/holder/properties.dm
+++ b/code/modules/reagents/chemistry/holder/properties.dm
@@ -32,7 +32,7 @@
//short cut to break when we have found our one exact type
if(type_check == REAGENT_STRICT_TYPE)
- break
+ return total_amount
return round(total_amount, CHEMICAL_VOLUME_ROUNDING)
diff --git a/code/modules/reagents/chemistry/holder/reactions.dm b/code/modules/reagents/chemistry/holder/reactions.dm
index 3aff4db3d5b..e1ac01dfa93 100644
--- a/code/modules/reagents/chemistry/holder/reactions.dm
+++ b/code/modules/reagents/chemistry/holder/reactions.dm
@@ -190,7 +190,6 @@
if(!LAZYLEN(reaction_list))
finish_reacting()
else
- update_total()
handle_reactions()
/*
@@ -236,8 +235,6 @@
is_reacting = FALSE
LAZYNULL(previous_reagent_list) //reset it to 0 - because any change will be different now.
update_total()
- if(!QDELING(src))
- handle_reactions() //Should be okay without. Each step checks.
/*
* Force stops the current holder/reagents datum from reacting
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 1e5f828947a..2c3dd704235 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -448,40 +448,36 @@
return null
/obj/machinery/chem_dispenser/wrench_act(mob/living/user, obj/item/tool)
- . = ..()
if(default_unfasten_wrench(user, tool) == SUCCESSFUL_UNFASTEN)
return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
/obj/machinery/chem_dispenser/screwdriver_act(mob/living/user, obj/item/tool)
- . = ..()
if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
update_appearance()
return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
/obj/machinery/chem_dispenser/crowbar_act(mob/living/user, obj/item/tool)
- . = ..()
if(default_deconstruction_crowbar(tool))
return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
-/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/living/user, params)
- if(is_reagent_container(I) && !(I.item_flags & ABSTRACT) && I.is_open_container())
- var/obj/item/reagent_containers/B = I
- //SKYRAT EDIT BEGIN - CHEMISTRY QOL
+/obj/machinery/chem_dispenser/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
+ if(is_reagent_container(tool) && !(tool.item_flags & ABSTRACT) && tool.is_open_container())
+ //SKYRAT EDIT ADDITION START - CHEMISTRY QOL
+ var/obj/item/reagent_containers/container = tool
if(customTransferAmount)
transferAmounts -= customTransferAmount
- transferAmounts = B.possible_transfer_amounts
- //SKYRAT EDIT END
- . = TRUE //no afterattack
- if(!user.transferItemToLoc(B, src))
- return
- replace_beaker(user, B)
- to_chat(user, span_notice("You add [B] to [src]."))
+ transferAmounts = container.possible_transfer_amounts
+ //SKYRAT EDIT ADDITION END
+ if(!user.transferItemToLoc(tool, src))
+ return ..()
+ replace_beaker(user, tool)
ui_interact(user)
- else if(!user.combat_mode && !istype(I, /obj/item/card/emag))
- to_chat(user, span_warning("You can't load [I] into [src]!"))
- return ..()
- else
- return ..()
+ return ITEM_INTERACT_SUCCESS
+
+ return ..()
/obj/machinery/chem_dispenser/get_cell()
return cell
@@ -785,7 +781,7 @@
/obj/machinery/chem_dispenser/mutagensaltpeter
name = "botanical chemical dispenser"
desc = "Creates and dispenses chemicals useful for botany."
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
circuit = /obj/item/circuitboard/machine/chem_dispenser/mutagensaltpeter
/// The default list of dispensable reagents available in the mutagensaltpeter chem dispenser
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index 2d17d200153..232b99255b7 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -125,30 +125,36 @@
/obj/machinery/chem_master/wrench_act(mob/living/user, obj/item/tool)
- . = ..()
- default_unfasten_wrench(user, tool)
- return ITEM_INTERACT_SUCCESS
+ if(default_unfasten_wrench(user, tool) == SUCCESSFUL_UNFASTEN)
+ return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
-/obj/machinery/chem_master/attackby(obj/item/item, mob/user, params)
- if(default_deconstruction_screwdriver(user, icon_state, icon_state, item))
+/obj/machinery/chem_master/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
update_appearance(UPDATE_ICON)
- return
- if(default_deconstruction_crowbar(item))
- return
- if(is_reagent_container(item) && !(item.item_flags & ABSTRACT) && item.is_open_container())
- . = TRUE // No afterattack
- var/obj/item/reagent_containers/beaker = item
- replace_beaker(user, beaker)
+ return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
+
+/obj/machinery/chem_master/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
+
+/obj/machinery/chem_master/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
+ if(is_reagent_container(tool) && !(tool.item_flags & ABSTRACT) && tool.is_open_container())
+ replace_beaker(user, tool)
if(!panel_open)
ui_interact(user)
+ return ITEM_INTERACT_SUCCESS
+
return ..()
/obj/machinery/chem_master/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
- return
+ return .
if(!can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH|FORBID_TELEKINESIS_REACH))
- return
+ return .
replace_beaker(user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
diff --git a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
index 6cee41bb3eb..6177baa269a 100644
--- a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
@@ -6,7 +6,7 @@
base_icon_state = "dispenser"
amount = 10
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
use_power = NO_POWER_USE
var/static/list/shortcuts = list(
"meth" = /datum/reagent/drug/methamphetamine
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 3afe28c16d9..908134d3332 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -409,7 +409,6 @@
/datum/reagent/water/holywater/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
- . = ..()
data["deciseconds_metabolized"] += (seconds_per_tick * 1 SECONDS * REM)
diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm
index 9342ce11b6e..93bd1dcf39c 100644
--- a/code/modules/reagents/chemistry/recipes.dm
+++ b/code/modules/reagents/chemistry/recipes.dm
@@ -189,7 +189,8 @@
var/datum/reagent/reagent = holder.has_reagent(id)
if(!reagent)
return
- reagent.volume = round((reagent.volume * 0.98), CHEMICAL_QUANTISATION_LEVEL) //Slowly lower yield per tick
+ reagent.volume *= 0.98 //Slowly lower yield per tick
+ holder.update_total()
/**
* Occurs when a reation is too impure (i.e. it's below purity_min)
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index f1fb70faf38..78942431bb8 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -15,7 +15,7 @@
/datum/chemical_reaction/reagent_explosion/nitroglycerin/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
- if(holder.has_reagent(/datum/reagent/exotic_stabilizer,round(created_volume / 25, CHEMICAL_QUANTISATION_LEVEL)))
+ if(holder.has_reagent(/datum/reagent/exotic_stabilizer, created_volume / 25))
return
holder.remove_reagent(/datum/reagent/nitroglycerin, created_volume * 2)
..()
@@ -78,10 +78,10 @@
strengthdiv = 3
/datum/chemical_reaction/reagent_explosion/tatp/update_info()
- required_temp = 450 + rand(-49,49) //this gets loaded only on round start
+ required_temp = 450 + rand(-49, 49) //this gets loaded only on round start
/datum/chemical_reaction/reagent_explosion/tatp/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
- if(holder.has_reagent(/datum/reagent/exotic_stabilizer,round(created_volume / 50, CHEMICAL_QUANTISATION_LEVEL))) // we like exotic stabilizer
+ if(holder.has_reagent(/datum/reagent/exotic_stabilizer, created_volume / 50)) // we like exotic stabilizer
return
holder.remove_reagent(/datum/reagent/tatp, created_volume)
..()
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index 1eed3c36b76..013d9831ee7 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -601,9 +601,11 @@
if(!do_after(user, 2 SECONDS, src)) //takes longer because you are supposed to take the foil off the bottle first
return
- ///The bonus to success chance that the user gets for being a command role
- var/command_bonus = user.mind?.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND ? 20 : 0
- ///The bonus to success chance that the user gets for having a sabrage skillchip installed/otherwise having the trait through other means
+ //The bonus to success chance that the user gets for being a command role
+ var/obj/item/organ/internal/liver/liver = user.get_organ_slot(ORGAN_SLOT_LIVER)
+ var/command_bonus = (!isnull(liver) && HAS_TRAIT(liver, TRAIT_ROYAL_METABOLISM)) ? 20 : 0
+
+ //The bonus to success chance that the user gets for having a sabrage skillchip installed/otherwise having the trait through other means
var/skillchip_bonus = HAS_TRAIT(user, TRAIT_SABRAGE_PRO) ? 35 : 0
//calculate success chance. example: captain's sabre - 15 force = 75% chance
var/sabrage_chance = (attacking_item.force * sabrage_success_percentile) + command_bonus + skillchip_bonus
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 0c9107ab19a..af246ae6e67 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -17,6 +17,8 @@
var/infinite = FALSE
/// If TRUE, won't play a noise when injecting.
var/stealthy = FALSE
+ /// If TRUE, the hypospray will be permanently unusable.
+ var/used_up = FALSE
/obj/item/reagent_containers/hypospray/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
@@ -26,8 +28,8 @@
///Handles all injection checks, injection and logging.
/obj/item/reagent_containers/hypospray/proc/inject(mob/living/affected_mob, mob/user)
- if(!reagents.total_volume)
- to_chat(user, span_warning("[src] is empty!"))
+ if(used_up)
+ to_chat(user, span_warning("[src] tip is broken and is now unusable!"))
return FALSE
if(!iscarbon(affected_mob))
return FALSE
@@ -39,7 +41,7 @@
var/contained = english_list(injected)
log_combat(user, affected_mob, "attempted to inject", src, "([contained])")
- if(reagents.total_volume && (ignore_flags || affected_mob.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))) // Ignore flag should be checked first or there will be an error message.
+ if(!used_up && (ignore_flags || affected_mob.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))) // Ignore flag should be checked first or there will be an error message.
to_chat(affected_mob, span_warning("You feel a tiny prick!"))
to_chat(user, span_notice("You inject [affected_mob] with [src]."))
if(!stealthy)
@@ -134,8 +136,8 @@
/obj/item/reagent_containers/hypospray/medipen/inject(mob/living/affected_mob, mob/user)
. = ..()
- if(.)
- reagents.maximum_volume = 0 //Makes them useless afterwards
+ if(. && !reagents.total_volume)
+ used_up = TRUE //Makes them useless afterwards
reagents.flags = NONE
update_appearance()
diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm
index 5e9b0f0327b..f43e33aa25a 100644
--- a/code/modules/religion/burdened/psyker.dm
+++ b/code/modules/religion/burdened/psyker.dm
@@ -10,12 +10,12 @@
organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP, TRAIT_ANTIMAGIC_NO_SELFBLOCK)
w_class = WEIGHT_CLASS_NORMAL
-/obj/item/organ/internal/brain/psyker/on_insert(mob/living/carbon/inserted_into)
+/obj/item/organ/internal/brain/psyker/on_mob_insert(mob/living/carbon/inserted_into)
. = ..()
inserted_into.AddComponent(/datum/component/echolocation, blocking_trait = TRAIT_DUMB, echo_group = "psyker", echo_icon = "psyker", color_path = /datum/client_colour/psyker)
inserted_into.AddComponent(/datum/component/anti_magic, antimagic_flags = MAGIC_RESISTANCE_MIND)
-/obj/item/organ/internal/brain/psyker/on_remove(mob/living/carbon/removed_from)
+/obj/item/organ/internal/brain/psyker/on_mob_remove(mob/living/carbon/removed_from)
. = ..()
qdel(removed_from.GetComponent(/datum/component/echolocation))
qdel(removed_from.GetComponent(/datum/component/anti_magic))
@@ -82,9 +82,9 @@
qdel(old_head)
var/obj/item/organ/internal/brain/psyker/psyker_brain = new()
old_brain.before_organ_replacement(psyker_brain)
- old_brain.Remove(src, special = TRUE, no_id_transfer = TRUE)
+ old_brain.Remove(src, special = TRUE, movement_flags = NO_ID_TRANSFER)
qdel(old_brain)
- psyker_brain.Insert(src, special = TRUE, drop_if_replaced = FALSE)
+ psyker_brain.Insert(src, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(old_eyes)
qdel(old_eyes)
return TRUE
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index 44551c9892a..11e6f026f68 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -661,7 +661,7 @@
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_SECURITY
)
- departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
+ departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_SECURITY
/datum/design/board/vendor
name = "Vendor Board"
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 150c69bc21f..9f83ae957ee 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -352,7 +352,7 @@
desc = "A portable, foldable version of the medical bed. Perfect for paramedics or whenever you have mass casualties!"
id = "medicalbed_emergency"
build_type = PROTOLATHE | AWAY_LATHE
- materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT * 2.7, /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 1.7, /datum/material/diamond = SMALL_MATERIAL_AMOUNT * 5, /datum/material/bluespace = SMALL_MATERIAL_AMOUNT * 5)
+ materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT * 2.7, /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 1.7)
build_path = /obj/item/emergency_bed
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL
diff --git a/code/modules/research/designs/power_designs.dm b/code/modules/research/designs/power_designs.dm
index ea617f36428..80844562eb4 100644
--- a/code/modules/research/designs/power_designs.dm
+++ b/code/modules/research/designs/power_designs.dm
@@ -77,7 +77,19 @@
category = list(
RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_ENGINEERING
)
- departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/inducerengi
+ name = "Inducer"
+ desc = "The NT-75 Electromagnetic Power Inducer can wirelessly induce electric charge in an object, allowing you to recharge power cells without having to remove them."
+ id = "inducerengi"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT)
+ build_path = /obj/item/inducer/empty
+ category = list(
+ RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_ENGINEERING
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
/datum/design/board/pacman
name = "PACMAN Board"
diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm
index 5783ee36082..3606dd67e2e 100644
--- a/code/modules/research/designs/wiremod_designs.dm
+++ b/code/modules/research/designs/wiremod_designs.dm
@@ -357,15 +357,10 @@
id = "comp_pinpointer"
build_path = /obj/item/circuit_component/pinpointer
-/datum/design/component/bci
- category = list(
- RND_CATEGORY_CIRCUITRY + RND_SUBCATEGORY_CIRCUITRY_BCI_COMPONENTS
- )
-
-/datum/design/component/bci/bci_action
- name = "BCI Action Component"
- id = "comp_bci_action"
- build_path = /obj/item/circuit_component/equipment_action/bci
+/datum/design/component/equipment_action
+ name = "Equipment Action Component"
+ id = "comp_equip_action"
+ build_path = /obj/item/circuit_component/equipment_action
/datum/design/component/bci/object_overlay
name = "Object Overlay Component"
@@ -417,11 +412,6 @@
id = "comp_filter_list"
build_path = /obj/item/circuit_component/filter_list
-/datum/design/component/mod_action
- name = "MOD Action Component"
- id = "comp_mod_action"
- build_path = /obj/item/circuit_component/equipment_action/mod
-
/datum/design/component/id_getter
name = "ID Getter Component"
id = "comp_id_getter"
diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm
index 5bda3865df2..86ed41e4cb4 100644
--- a/code/modules/research/techweb/_techweb.dm
+++ b/code/modules/research/techweb/_techweb.dm
@@ -47,11 +47,6 @@
/// Completing these experiments will have a refund.
var/list/datum/experiment/skipped_experiment_types = list()
- /// If science researches something without completing its discount experiments,
- /// they have the option to complete them later for a refund
- /// This ratio determines how much of the original discount is refunded
- var/skipped_experiment_refund_ratio = 0.66
-
///All RD consoles connected to this individual techweb.
var/list/obj/machinery/computer/rdconsole/consoles_accessing = list()
///All research servers connected to this individual techweb.
@@ -353,7 +348,7 @@
for(var/missed_experiment in node.discount_experiments)
if(completed_experiments[missed_experiment] || skipped_experiment_types[missed_experiment])
continue
- skipped_experiment_types[missed_experiment] = node.discount_experiments[missed_experiment] * skipped_experiment_refund_ratio
+ skipped_experiment_types[missed_experiment] = node.discount_experiments[missed_experiment]
// Gain the experiments from the new node
for(var/id in node.unlock_ids)
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 733cd707578..b12cca4b859 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -854,7 +854,7 @@
design_ids = list(
"assembly_shell",
"bot_shell",
- "comp_mod_action",
+ "comp_equip_action",
"controller_shell",
"dispenser_shell",
"door_shell",
@@ -876,7 +876,6 @@
"bci_implanter",
"bci_shell",
"comp_bar_overlay",
- "comp_bci_action",
"comp_counter_overlay",
"comp_install_detector",
"comp_object_overlay",
@@ -1115,6 +1114,7 @@
"holosignrestaurant",
"holosignbar",
"inducer",
+ "inducerengi",
"tray_goggles",
"holopad",
"vendatray",
diff --git a/code/modules/shuttle/battlecruiser_starfury.dm b/code/modules/shuttle/battlecruiser_starfury.dm
index d8dc3c312f7..e95ff4243f5 100644
--- a/code/modules/shuttle/battlecruiser_starfury.dm
+++ b/code/modules/shuttle/battlecruiser_starfury.dm
@@ -135,7 +135,7 @@
*/
/proc/summon_battlecruiser(datum/team/battlecruiser/team)
- var/list/candidates = poll_ghost_candidates("Do you wish to be considered for battlecruiser crew?", ROLE_TRAITOR)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for battlecruiser crew?", check_jobban = ROLE_TRAITOR, pic_source = /obj/machinery/sleeper/syndie, role_name_text = "battlecruiser crew")
shuffle_inplace(candidates)
var/datum/map_template/ship = SSmapping.map_templates["battlecruiser_starfury.dmm"]
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index d1b30ddfad5..73de05a8f37 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -744,7 +744,7 @@
return INITIALIZE_HINT_QDEL
/obj/docking_port/stationary/random/icemoon
- target_area = /area/icemoon/surface/outdoors
+ target_area = /area/icemoon/surface/outdoors/unexplored/rivers/no_monsters
//Pod suits/pickaxes
diff --git a/code/modules/shuttle/shuttle_events/player_controlled.dm b/code/modules/shuttle/shuttle_events/player_controlled.dm
index 40bc29f8017..77fee390a68 100644
--- a/code/modules/shuttle/shuttle_events/player_controlled.dm
+++ b/code/modules/shuttle/shuttle_events/player_controlled.dm
@@ -17,7 +17,7 @@
/// Attempt to grant control of a mob to ghosts before spawning it in. if spawn_anyway_if_no_player = TRUE, we spawn the mob even if there's no ghosts
/datum/shuttle_event/simple_spawner/player_controlled/proc/try_grant_ghost_control(spawn_type)
- var/list/candidates = poll_ghost_candidates(ghost_alert_string + " (Warning: you will not be able to return to your body!)", role_type, FALSE, 10 SECONDS)
+ var/list/candidates = SSpolling.poll_ghost_candidates(ghost_alert_string + " (Warning: you will not be able to return to your body!)", check_jobban = role_type, poll_time = 10 SECONDS, pic_source = spawn_type, role_name_text = "shot at shuttle")
if(!candidates.len && !spawn_anyway_if_no_player)
return
diff --git a/code/modules/shuttle/spaceship_navigation_beacon.dm b/code/modules/shuttle/spaceship_navigation_beacon.dm
index 71e88c7fcc4..7338d336bde 100644
--- a/code/modules/shuttle/spaceship_navigation_beacon.dm
+++ b/code/modules/shuttle/spaceship_navigation_beacon.dm
@@ -5,7 +5,7 @@
icon_state = "beacon_active"
base_icon_state = "beacon"
density = TRUE
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
/// Locked beacons cannot be jumped to by ships.
var/locked = FALSE
diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm
index 089e8b1f9fe..b897bb76111 100644
--- a/code/modules/shuttle/special.dm
+++ b/code/modules/shuttle/special.dm
@@ -79,7 +79,7 @@
var/obj/machinery/power/emitter/energycannon/magical/our_statue
var/list/mob/living/sleepers = list()
var/never_spoken = TRUE
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
/obj/structure/table/abductor/wabbajack/Initialize(mapload)
. = ..()
@@ -201,7 +201,7 @@
/obj/structure/table/wood/shuttle_bar
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
max_integrity = 1000
var/boot_dir = 1
diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm
index 53d625dc758..4c59d9848fc 100644
--- a/code/modules/shuttle/syndicate.dm
+++ b/code/modules/shuttle/syndicate.dm
@@ -11,7 +11,7 @@
shuttleId = "syndicate"
possible_destinations = "syndicate_away;syndicate_z5;syndicate_ne;syndicate_nw;syndicate_n;syndicate_se;syndicate_sw;syndicate_s;syndicate_custom"
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
/obj/machinery/computer/shuttle/syndicate/launch_check(mob/user)
. = ..()
diff --git a/code/modules/spells/spell_types/self/summonitem.dm b/code/modules/spells/spell_types/self/summonitem.dm
index 62d6b078161..ab99f35271d 100644
--- a/code/modules/spells/spell_types/self/summonitem.dm
+++ b/code/modules/spells/spell_types/self/summonitem.dm
@@ -138,7 +138,6 @@
item_to_retrieve = null
break
- SEND_SIGNAL(holding_mark, COMSIG_MAGIC_RECALL, caster, item_to_retrieve)
holding_mark.dropItemToGround(item_to_retrieve)
else if(isobj(item_to_retrieve.loc))
@@ -157,15 +156,6 @@
infinite_recursion += 1
- else
- // Organs are usually stored in nullspace
- if(isorgan(item_to_retrieve))
- var/obj/item/organ/organ = item_to_retrieve
- if(organ.owner)
- // If this code ever runs I will be happy
- log_combat(caster, organ.owner, "magically removed [organ.name] from", addition = "COMBAT MODE: [uppertext(caster.combat_mode)]")
- organ.Remove(organ.owner)
-
if(!item_to_retrieve)
return
@@ -176,6 +166,8 @@
else
item_to_retrieve.forceMove(caster.drop_location())
item_to_retrieve.loc.visible_message(span_warning("[item_to_retrieve] suddenly appears!"))
+
+ SEND_SIGNAL(item_to_retrieve, COMSIG_MAGIC_RECALL, caster, item_to_retrieve)
playsound(get_turf(item_to_retrieve), 'sound/magic/summonitems_generic.ogg', 50, TRUE)
/datum/action/cooldown/spell/summonitem/abductor
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index 1d6b546bcb9..9e5de07ab7d 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -204,7 +204,6 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE)
/obj/machinery/bsa/full/proc/get_layer()
top_layer = mutable_appearance(icon, layer = ABOVE_MOB_LAYER)
- SET_PLANE_EXPLICIT(top_layer, GAME_PLANE_UPPER, src)
switch(dir)
if(WEST)
top_layer.icon_state = "top_west"
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index 03f69c2664c..83fb1f0ea7f 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -101,12 +101,11 @@
F.parent = src
fillers += F
- if(SSticker.mode)
- var/datum/station_goal/dna_vault/dna_vault_goal = locate() in GLOB.station_goals
- if (!isnull(dna_vault_goal))
- animals_max = dna_vault_goal.animal_count
- plants_max = dna_vault_goal.plant_count
- dna_max = dna_vault_goal.human_count
+ var/datum/station_goal/dna_vault/dna_vault_goal = locate() in GLOB.station_goals
+ if (!isnull(dna_vault_goal))
+ animals_max = dna_vault_goal.animal_count
+ plants_max = dna_vault_goal.plant_count
+ dna_max = dna_vault_goal.human_count
return ..()
diff --git a/code/modules/station_goals/generate_goals.dm b/code/modules/station_goals/generate_goals.dm
new file mode 100644
index 00000000000..102212c1212
--- /dev/null
+++ b/code/modules/station_goals/generate_goals.dm
@@ -0,0 +1,13 @@
+/// Creates the initial station goals.
+/proc/generate_station_goals(goal_budget)
+ var/list/possible = subtypesof(/datum/station_goal)
+ // Remove all goals that require space if space is not present
+ if(SSmapping.is_planetary())
+ for(var/datum/station_goal/goal as anything in possible)
+ if(initial(goal.requires_space))
+ possible -= goal
+ var/goal_weights = 0
+ while(possible.len && goal_weights < goal_budget)
+ var/datum/station_goal/picked = pick_n_take(possible)
+ goal_weights += initial(picked.weight)
+ GLOB.station_goals += new picked
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 5b0e7c27f11..d47bdec35d1 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -6,6 +6,7 @@
w_class = WEIGHT_CLASS_SMALL
icon = 'icons/mob/human/bodyparts.dmi'
icon_state = "" //Leave this blank! Bodyparts are built using overlays
+ flags_1 = PREVENT_CONTENTS_EXPLOSION_1 //actually mindblowing
/// The icon for Organic limbs using greyscale
VAR_PROTECTED/icon_greyscale = DEFAULT_BODYPART_ICON_ORGANIC
///The icon for non-greyscale limbs
@@ -19,7 +20,7 @@
layer = BELOW_MOB_LAYER //so it isn't hidden behind objects when on the floor
grind_results = list(/datum/reagent/bone_dust = 10, /datum/reagent/consumable/liquidgibs = 5) // robotic bodyparts and chests/heads cannot be ground
/// The mob that "owns" this limb
- /// DO NOT MODIFY DIRECTLY. Use set_owner()
+ /// DO NOT MODIFY DIRECTLY. Use update_owner()
var/mob/living/carbon/owner
/// If this limb can be scarred.
@@ -155,9 +156,6 @@
/// If something is currently grasping this bodypart and trying to staunch bleeding (see [/obj/item/hand_item/self_grasp])
var/obj/item/hand_item/self_grasp/grasped_by
- ///A list of all the external organs we've got stored to draw horns, wings and stuff with (special because we are actually in the limbs unlike normal organs :/ )
- ///If someone ever comes around to making all organs exist in the bodyparts, you can just remove this and use a typed loop
- var/list/obj/item/organ/external/external_organs = list()
///A list of all bodypart overlays to draw
var/list/bodypart_overlays = list()
@@ -232,31 +230,40 @@
refresh_bleed_rate()
/obj/item/bodypart/Destroy()
- if(owner)
- owner.remove_bodypart(src)
- set_owner(null)
+ if(owner && !QDELETED(owner))
+ forced_removal(special = FALSE, dismembered = TRUE, move_to_floor = FALSE)
+ update_owner(null)
for(var/wound in wounds)
qdel(wound) // wounds is a lazylist, and each wound removes itself from it on deletion.
if(length(wounds))
stack_trace("[type] qdeleted with [length(wounds)] uncleared wounds")
wounds.Cut()
- if(length(external_organs))
- for(var/obj/item/organ/external/external_organ as anything in external_organs)
- external_organs -= external_organ
- qdel(external_organ) // It handles removing its references to this limb on its own.
+ owner = null
+
+ for(var/atom/movable/movable in contents)
+ qdel(movable)
- external_organs = list()
QDEL_LIST_ASSOC_VAL(feature_offsets)
return ..()
-/obj/item/bodypart/forceMove(atom/destination) //Please. Never forcemove a limb if its's actually in use. This is only for borgs.
- SHOULD_CALL_PARENT(TRUE)
+/obj/item/bodypart/ex_act(severity, target)
+ if(owner) //trust me bro you dont want this
+ return FALSE
+ return ..()
- . = ..()
- if(isturf(destination))
- update_icon_dropped()
+
+/obj/item/bodypart/proc/on_forced_removal(atom/old_loc, dir, forced, list/old_locs)
+ SIGNAL_HANDLER
+
+ forced_removal(special = FALSE, dismembered = TRUE, move_to_floor = FALSE)
+
+/// In-case someone, somehow only teleports someones limb
+/obj/item/bodypart/proc/forced_removal(special, dismembered, move_to_floor)
+ drop_limb(special, dismembered, move_to_floor)
+
+ update_icon_dropped()
/obj/item/bodypart/examine(mob/user)
SHOULD_CALL_PARENT(TRUE)
@@ -407,32 +414,25 @@
var/atom/drop_loc = drop_location()
if(IS_ORGANIC_LIMB(src))
playsound(drop_loc, 'sound/misc/splort.ogg', 50, TRUE, -1)
+
QDEL_NULL(current_gauze)
- for(var/obj/item/organ/bodypart_organ as anything in get_organs())
- bodypart_organ.transfer_to_limb(src, owner)
- for(var/obj/item/organ/external/external as anything in external_organs)
- external.remove_from_limb()
- external.forceMove(drop_loc)
- for(var/obj/item/item_in_bodypart in src)
- item_in_bodypart.forceMove(drop_loc)
+
+ for(var/obj/item/organ/bodypart_organ in contents)
+ if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE)
+ continue
+ if(owner)
+ bodypart_organ.Remove(bodypart_organ.owner)
+ else
+ if(bodypart_organ.bodypart_remove(src))
+ if(drop_loc) //can be null if being deleted
+ bodypart_organ.forceMove(get_turf(drop_loc))
+
+ if(drop_loc) //can be null during deletion
+ for(var/atom/movable/movable as anything in src)
+ movable.forceMove(drop_loc)
update_icon_dropped()
-///since organs aren't actually stored in the bodypart themselves while attached to a person, we have to query the owner for what we should have
-/obj/item/bodypart/proc/get_organs()
- SHOULD_CALL_PARENT(TRUE)
- RETURN_TYPE(/list)
-
- if(!owner)
- return FALSE
-
- var/list/bodypart_organs
- for(var/obj/item/organ/organ_check as anything in owner.organs) //internal organs inside the dismembered limb are dropped.
- if(check_zone(organ_check.zone) == body_zone)
- LAZYADD(bodypart_organs, organ_check) // this way if we don't have any, it'll just return null
-
- return bodypart_organs
-
//Return TRUE to get whatever mob this is in to update health.
/obj/item/bodypart/proc/on_life(seconds_per_tick, times_fired)
SHOULD_CALL_PARENT(TRUE)
@@ -760,70 +760,91 @@
owner.update_health_hud() //update the healthdoll
owner.update_body()
-///Proc to change the value of the `owner` variable and react to the event of its change.
-/obj/item/bodypart/proc/set_owner(new_owner)
- SHOULD_CALL_PARENT(TRUE)
+/// Proc to change the value of the `owner` variable and react to the event of its change.
+/obj/item/bodypart/proc/update_owner(new_owner)
+ SHOULD_NOT_OVERRIDE(TRUE)
+
if(owner == new_owner)
return FALSE //`null` is a valid option, so we need to use a num var to make it clear no change was made.
- var/mob/living/carbon/old_owner = owner
- owner = new_owner
- SEND_SIGNAL(src, COMSIG_BODYPART_CHANGED_OWNER, new_owner, old_owner)
- var/needs_update_disabled = FALSE //Only really relevant if there's an owner
- if(old_owner)
- if(held_index)
- old_owner.on_lost_hand(src)
- if(old_owner.hud_used)
- var/atom/movable/screen/inventory/hand/hand = old_owner.hud_used.hand_slots["[held_index]"]
- if(hand)
- hand.update_appearance()
- old_owner.update_worn_gloves()
- if(speed_modifier)
- old_owner.update_bodypart_speed_modifier()
- if(length(bodypart_traits))
- old_owner.remove_traits(bodypart_traits, bodypart_trait_source)
- if(initial(can_be_disabled))
- if(HAS_TRAIT(old_owner, TRAIT_NOLIMBDISABLE))
- if(!owner || !HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
- set_can_be_disabled(initial(can_be_disabled))
- needs_update_disabled = TRUE
- UnregisterSignal(old_owner, list(
- SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE),
- SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE),
- SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD),
- SIGNAL_ADDTRAIT(TRAIT_NOBLOOD),
- ))
- UnregisterSignal(old_owner, COMSIG_ATOM_RESTYLE)
+
+ SEND_SIGNAL(src, COMSIG_BODYPART_CHANGED_OWNER, new_owner, owner)
+
if(owner)
- if(held_index)
- owner.on_added_hand(src, held_index)
- if(owner.hud_used)
- var/atom/movable/screen/inventory/hand/hand = owner.hud_used.hand_slots["[held_index]"]
- if(hand)
- hand.update_appearance()
- owner.update_worn_gloves()
- if(speed_modifier)
- owner.update_bodypart_speed_modifier()
- if(length(bodypart_traits))
- owner.add_traits(bodypart_traits, bodypart_trait_source)
- if(initial(can_be_disabled))
- if(HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
- set_can_be_disabled(FALSE)
- needs_update_disabled = FALSE
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_loss))
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_gain))
- // Bleeding stuff
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_loss))
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_gain))
-
- if(needs_update_disabled)
- update_disabled()
-
- RegisterSignal(owner, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle_mob))
+ . = owner //return value is old owner
+ clear_ownership(owner)
+ if(new_owner)
+ apply_ownership(new_owner)
refresh_bleed_rate()
- return old_owner
+ return .
+
+/// Run all necessary procs to remove a limbs ownership and remove the appropriate signals and traits
+/obj/item/bodypart/proc/clear_ownership(mob/living/carbon/old_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ owner = null
+
+ if(speed_modifier)
+ old_owner.update_bodypart_speed_modifier()
+ if(length(bodypart_traits))
+ old_owner.remove_traits(bodypart_traits, bodypart_trait_source)
+
+ UnregisterSignal(old_owner, list(
+ SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE),
+ SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE),
+ SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD),
+ SIGNAL_ADDTRAIT(TRAIT_NOBLOOD),
+ ))
+
+ UnregisterSignal(old_owner, COMSIG_ATOM_RESTYLE)
+
+/// Apply ownership of a limb to someone, giving the appropriate traits, updates and signals
+/obj/item/bodypart/proc/apply_ownership(mob/living/carbon/new_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ owner = new_owner
+
+ if(speed_modifier)
+ owner.update_bodypart_speed_modifier()
+ if(length(bodypart_traits))
+ owner.add_traits(bodypart_traits, bodypart_trait_source)
+
+ if(initial(can_be_disabled))
+ if(HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
+ set_can_be_disabled(FALSE)
+
+ // Listen to disable traits being added
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_loss))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_gain))
+
+ // Listen to no blood traits being added
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_loss))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_gain))
+
+ if(can_be_disabled)
+ update_disabled()
+
+ RegisterSignal(owner, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle_mob))
+
+ forceMove(owner)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_forced_removal)) //this must be set after we moved, or we insta gib
+
+/// Called on addition of a bodypart
+/obj/item/bodypart/proc/on_adding(mob/living/carbon/new_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ item_flags |= ABSTRACT
+ ADD_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
+
+/// Called on removal of a bodypart.
+/obj/item/bodypart/proc/on_removal(mob/living/carbon/old_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
+
+ item_flags &= ~ABSTRACT
+ REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
-/obj/item/bodypart/proc/on_removal()
if(!length(bodypart_traits))
return
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 22e3cccb4a8..8595a62b802 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -67,7 +67,11 @@
if(wounding_type != WOUND_BURN && isturf(chest_owner.loc) && can_bleed())
chest_owner.add_splatter_floor(chest_owner.loc)
playsound(get_turf(chest_owner), 'sound/misc/splort.ogg', 80, TRUE)
- for(var/obj/item/organ/organ as anything in chest_owner.organs)
+ for(var/obj/item/organ/organ in contents)
+ // SKYRAT EDIT START - Non-spillable organs
+ if(!organ.drop_when_organ_spilling)
+ continue
+ // SKYRAT EDIT END
var/org_zone = check_zone(organ.zone)
if(org_zone != BODY_ZONE_CHEST)
continue
@@ -79,21 +83,13 @@
organ.forceMove(chest_owner.loc)
. += organ
- for(var/obj/item/organ/external/ext_organ as anything in src.external_organs)
- if(!(ext_organ.organ_flags & ORGAN_UNREMOVABLE))
- ext_organ.Remove(chest_owner)
- ext_organ.forceMove(chest_owner.loc)
- . += ext_organ
-
if(cavity_item)
cavity_item.forceMove(chest_owner.loc)
. += cavity_item
cavity_item = null
- return .
-
///limb removal. The "special" argument is used for swapping a limb with a new one without the effects of losing a limb kicking in.
-/obj/item/bodypart/proc/drop_limb(special, dismembered)
+/obj/item/bodypart/proc/drop_limb(special, dismembered, move_to_floor = TRUE)
if(!owner)
return
var/atom/drop_loc = owner.drop_location()
@@ -102,16 +98,13 @@
SEND_SIGNAL(src, COMSIG_BODYPART_REMOVED, owner, special, dismembered)
update_limb(dropping_limb = TRUE)
bodypart_flags &= ~BODYPART_IMPLANTED //limb is out and about, it can't really be considered an implant
- owner.remove_bodypart(src)
+ owner.remove_bodypart(src, special)
for(var/datum/scar/scar as anything in scars)
scar.victim = null
LAZYREMOVE(owner.all_scars, scar)
- for(var/obj/item/organ/external/ext_organ as anything in external_organs)
- ext_organ.transfer_to_limb(src, null) //Null is the second arg because the bodypart is being removed from it's owner.
-
- var/mob/living/carbon/phantom_owner = set_owner(null) // so we can still refer to the guy who lost their limb after said limb forgets 'em
+ var/mob/living/carbon/phantom_owner = update_owner(null) // so we can still refer to the guy who lost their limb after said limb forgets 'em
for(var/datum/wound/wound as anything in wounds)
wound.remove_wound(TRUE)
@@ -135,27 +128,22 @@
to_chat(phantom_owner, span_warning("You feel your [mutation] deactivating from the loss of your [body_zone]!"))
phantom_owner.dna.force_lose(mutation)
- for(var/obj/item/organ/organ as anything in phantom_owner.organs) //internal organs inside the dismembered limb are dropped.
- var/org_zone = check_zone(organ.zone)
- if(org_zone != body_zone)
- continue
- organ.transfer_to_limb(src, phantom_owner)
-
update_icon_dropped()
phantom_owner.update_health_hud() //update the healthdoll
phantom_owner.update_body()
phantom_owner.update_body_parts()
- if(!drop_loc) // drop_loc = null happens when a "dummy human" used for rendering icons on prefs screen gets its limbs replaced.
- qdel(src)
- return
-
if(bodypart_flags & BODYPART_PSEUDOPART)
drop_organs(phantom_owner) //Psuedoparts shouldn't have organs, but just in case
qdel(src)
return
- forceMove(drop_loc)
+ if(move_to_floor)
+ if(!drop_loc) // drop_loc = null happens when a "dummy human" used for rendering icons on prefs screen gets its limbs replaced.
+ qdel(src)
+ return
+ forceMove(drop_loc)
+
SEND_SIGNAL(phantom_owner, COMSIG_CARBON_POST_REMOVE_LIMB, src, special, dismembered)
/**
@@ -205,48 +193,45 @@
var/datum/wound/loss/dismembering = new
return dismembering.apply_dismember(src, wounding_type)
-///Transfers the organ to the limb, and to the limb's owner, if it has one. This is done on drop_limb().
-/obj/item/organ/proc/transfer_to_limb(obj/item/bodypart/bodypart, mob/living/carbon/bodypart_owner)
- Remove(bodypart_owner)
- add_to_limb(bodypart)
-
-///Adds the organ to a bodypart, used in transfer_to_limb()
-/obj/item/organ/proc/add_to_limb(obj/item/bodypart/bodypart)
- forceMove(bodypart)
-
-///Removes the organ from the limb, placing it into nullspace.
-/obj/item/organ/proc/remove_from_limb()
- moveToNullspace()
-
-/obj/item/organ/internal/brain/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
- Remove(head_owner) //Changeling brain concerns are now handled in Remove
- forceMove(head)
- head.brain = src
- if(brainmob)
- head.brainmob = brainmob
- brainmob = null
- head.brainmob.forceMove(head)
- head.brainmob.set_stat(DEAD)
-
-/obj/item/organ/internal/eyes/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
+/obj/item/organ/internal/eyes/on_bodypart_insert(obj/item/bodypart/head/head)
head.eyes = src
..()
-/obj/item/organ/internal/ears/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
+/obj/item/organ/internal/ears/on_bodypart_insert(obj/item/bodypart/head/head)
head.ears = src
..()
-/obj/item/organ/internal/tongue/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
+/obj/item/organ/internal/tongue/on_bodypart_insert(obj/item/bodypart/head/head)
head.tongue = src
..()
-/obj/item/bodypart/chest/drop_limb(special)
+/obj/item/organ/internal/brain/on_bodypart_insert(obj/item/bodypart/head/head)
+ head.brain = src
+ ..()
+
+/obj/item/organ/internal/eyes/on_bodypart_remove(obj/item/bodypart/head/head)
+ head.eyes = null
+ ..()
+
+/obj/item/organ/internal/ears/on_bodypart_remove(obj/item/bodypart/head/head)
+ head.ears = null
+ ..()
+
+/obj/item/organ/internal/tongue/on_bodypart_remove(obj/item/bodypart/head/head)
+ head.tongue = null
+ ..()
+
+/obj/item/organ/internal/brain/on_bodypart_remove(obj/item/bodypart/head/head)
+ head.brain = null
+ ..()
+
+/obj/item/bodypart/chest/drop_limb(special, dismembered, move_to_floor = TRUE)
if(special)
return ..()
//if this is not a special drop, this is a mistake
return FALSE
-/obj/item/bodypart/arm/drop_limb(special)
+/obj/item/bodypart/arm/drop_limb(special, dismembered, move_to_floor = TRUE)
var/mob/living/carbon/arm_owner = owner
if(special || !arm_owner)
@@ -269,7 +254,7 @@
arm_owner.update_worn_gloves() //to remove the bloody hands overlay
return ..()
-/obj/item/bodypart/leg/drop_limb(special)
+/obj/item/bodypart/leg/drop_limb(special, dismembered, move_to_floor = TRUE)
if(owner && !special)
if(owner.legcuffed)
owner.legcuffed.forceMove(owner.drop_location()) //At this point bodypart is still in nullspace
@@ -280,7 +265,7 @@
owner.dropItemToGround(owner.shoes, TRUE)
return ..()
-/obj/item/bodypart/head/drop_limb(special)
+/obj/item/bodypart/head/drop_limb(special, dismembered, move_to_floor = TRUE)
if(!special)
//Drop all worn head items
for(var/obj/item/head_item as anything in list(owner.glasses, owner.ears, owner.wear_mask, owner.head))
@@ -336,8 +321,6 @@
SEND_SIGNAL(new_limb_owner, COMSIG_CARBON_ATTACH_LIMB, src, special)
SEND_SIGNAL(src, COMSIG_BODYPART_ATTACHED, new_limb_owner, special)
- moveToNullspace()
- set_owner(new_limb_owner)
new_limb_owner.add_bodypart(src)
LAZYREMOVE(new_limb_owner.body_zone_dismembered_by, body_zone)
@@ -350,8 +333,10 @@
qdel(attach_surgery)
break
- for(var/obj/item/organ/limb_organ in contents)
- limb_organ.Insert(new_limb_owner, TRUE)
+ for(var/obj/item/organ/organ as anything in new_limb_owner.organs)
+ if(deprecise_zone(organ.zone) != body_zone)
+ continue
+ organ.bodypart_insert(src)
for(var/datum/wound/wound as anything in wounds)
// we have to remove the wound from the limb wound list first, so that we can reapply it fresh with the new person
@@ -391,15 +376,6 @@
if(!.)
return
- if(brain)
- brain = null
- if(tongue)
- tongue = null
- if(ears)
- ears = null
- if(eyes)
- eyes = null
-
if(old_real_name)
new_head_owner.real_name = old_real_name
real_name = new_head_owner.real_name
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 94551211def..097f9d5e216 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -26,7 +26,6 @@
unarmed_effectiveness = 0
bodypart_trait_source = HEAD_TRAIT
- var/mob/living/brain/brainmob //The current occupant.
var/obj/item/organ/internal/brain/brain //The brain organ
var/obj/item/organ/internal/eyes/eyes
var/obj/item/organ/internal/ears/ears
@@ -93,12 +92,6 @@
var/datum/worn_feature_offset/worn_face_offset
/obj/item/bodypart/head/Destroy()
- QDEL_NULL(brainmob) //order is sensitive, see warning in Exited() below
- QDEL_NULL(brain)
- QDEL_NULL(eyes)
- QDEL_NULL(ears)
- QDEL_NULL(tongue)
-
QDEL_NULL(worn_ears_offset)
QDEL_NULL(worn_glasses_offset)
QDEL_NULL(worn_mask_offset)
@@ -110,11 +103,6 @@
if(gone == brain)
brain = null
update_icon_dropped()
- if(!QDELETED(brainmob)) //this shouldn't happen without badminnery.
- message_admins("Brainmob: ([ADMIN_LOOKUPFLW(brainmob)]) was left stranded in [src] at [ADMIN_VERBOSEJMP(src)] without a brain!")
- brainmob.log_message(", brainmob, was left stranded in [src] without a brain", LOG_GAME)
- if(gone == brainmob)
- brainmob = null
if(gone == eyes)
eyes = null
update_icon_dropped()
@@ -129,12 +117,12 @@
if(show_organs_on_examine && IS_ORGANIC_LIMB(src))
if(!brain)
. += span_info("The brain has been removed from [src].")
- else if(brain.suicided || (brainmob && HAS_TRAIT(brainmob, TRAIT_SUICIDED)))
+ else if(brain.suicided || (brain.brainmob && HAS_TRAIT(brain.brainmob, TRAIT_SUICIDED)))
. += span_info("There's a miserable expression on [real_name]'s face; they must have really hated life. There's no hope of recovery.")
- else if(brainmob?.health <= HEALTH_THRESHOLD_DEAD)
- . += span_info("It's leaking some kind of... clear fluid? The brain inside must be in pretty bad shape.")
- else if(brainmob)
- if(brainmob.key || brainmob.get_ghost(FALSE, TRUE))
+ else if(brain.brainmob)
+ if(brain.brainmob?.health <= HEALTH_THRESHOLD_DEAD)
+ . += span_info("It's leaking some kind of... clear fluid? The brain inside must be in pretty bad shape.")
+ if(brain.brainmob.key || brain.brainmob.get_ghost(FALSE, TRUE))
. += span_info("Its muscles are twitching slightly... It seems to have some life still in it.")
else
. += span_info("It's completely lifeless. Perhaps there'll be a chance for them later.")
@@ -158,33 +146,12 @@
return ..()
/obj/item/bodypart/head/drop_organs(mob/user, violent_removal)
- var/atom/drop_loc = drop_location()
- for(var/obj/item/head_item in src)
- if(head_item == brain)
- if(user)
- user.visible_message(span_warning("[user] saws [src] open and pulls out a brain!"), span_notice("You saw [src] open and pull out a brain."))
- if(brainmob)
- brainmob.container = null
- brain.brainmob = brainmob
- brainmob = null
- if(violent_removal && prob(rand(80, 100))) //ghetto surgery can damage the brain.
- to_chat(user, span_warning("[brain] was damaged in the process!"))
- brain.set_organ_damage(brain.maxHealth)
- brain.forceMove(drop_loc)
- brain = null
- update_icon_dropped()
- else
- if(istype(head_item, /obj/item/reagent_containers/pill))
- for(var/datum/action/item_action/hands_free/activate_pill/pill_action in head_item.actions)
- qdel(pill_action)
- else if(isorgan(head_item))
- var/obj/item/organ/organ = head_item
- if(organ.organ_flags & ORGAN_UNREMOVABLE)
- continue
- head_item.forceMove(drop_loc)
- eyes = null
- ears = null
- tongue = null
+ if(user)
+ user.visible_message(span_warning("[user] saws [src] open and pulls out a brain!"), span_notice("You saw [src] open and pull out a brain."))
+ if(brain && violent_removal && prob(90)) //ghetto surgery can damage the brain.
+ to_chat(user, span_warning("[brain] was damaged in the process!"))
+ brain.set_organ_damage(brain.maxHealth)
+
update_limb()
return ..()
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index c93fdf10b47..52671b5fbc1 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -15,6 +15,7 @@
/mob/living/carbon/proc/del_and_replace_bodypart(obj/item/bodypart/new_limb, special)
var/obj/item/bodypart/old_limb = get_bodypart(new_limb.body_zone)
if(old_limb)
+ old_limb.drop_limb(special = TRUE)
qdel(old_limb)
new_limb.try_attach_limb(src, special = special)
@@ -172,7 +173,7 @@
/mob/living/carbon/proc/synchronize_bodytypes()
var/all_limb_flags = NONE
for(var/obj/item/bodypart/limb as anything in bodyparts)
- for(var/obj/item/organ/external/ext_organ as anything in limb.external_organs)
+ for(var/obj/item/organ/external/ext_organ in limb)
all_limb_flags |= ext_organ.external_bodytypes
all_limb_flags |= limb.bodytype
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index 25f1ab985b6..c657e89cefd 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -35,8 +35,18 @@
/// Which functional (i.e. flightpotion) wing types (if any) does this bodypart support? If count is >1 a radial menu is used to choose between all icons in list
var/list/wing_types = list(/obj/item/organ/external/wings/functional/angel)
+/obj/item/bodypart/chest/forced_removal(dismembered, special, move_to_floor)
+ var/mob/living/carbon/old_owner = owner
+ ..(special = TRUE) //special because we're self destructing
+
+ //If someones chest is teleported away, they die pretty hard
+ if(!old_owner)
+ return
+ message_admins("[ADMIN_LOOKUPFLW(old_owner)] was gibbed after their chest teleported to [ADMIN_VERBOSEJMP(loc)].")
+ old_owner.gib(DROP_ALL_REMAINS)
+
/obj/item/bodypart/chest/can_dismember(obj/item/item)
- if(owner.stat < HARD_CRIT || !get_organs())
+ if(owner.stat < HARD_CRIT || !contents.len)
return FALSE
return ..()
@@ -127,6 +137,40 @@
QDEL_NULL(held_hand_offset)
return ..()
+/// We need to clear out hand hud items and appearance, so do that here
+/obj/item/bodypart/arm/clear_ownership(mob/living/carbon/old_owner)
+ ..()
+
+ old_owner.update_worn_gloves()
+
+ if(!held_index)
+ return
+
+ old_owner.on_lost_hand(src)
+
+ if(!old_owner.hud_used)
+ return
+
+ var/atom/movable/screen/inventory/hand/hand = old_owner.hud_used.hand_slots["[held_index]"]
+ hand?.update_appearance()
+
+/// We need to add hand hud items and appearance, so do that here
+/obj/item/bodypart/arm/apply_ownership(mob/living/carbon/new_owner)
+ ..()
+
+ new_owner.update_worn_gloves()
+
+ if(!held_index)
+ return
+
+ new_owner.on_added_hand(src, held_index)
+
+ if(!new_owner.hud_used)
+ return
+
+ var/atom/movable/screen/inventory/hand/hand = new_owner.hud_used.hand_slots["[held_index]"]
+ hand.update_appearance()
+
/obj/item/bodypart/arm/left
name = "left arm"
desc = "Did you know that the word 'sinister' stems originally from the \
@@ -143,27 +187,22 @@
px_y = 0
bodypart_trait_source = LEFT_ARM_TRAIT
+/obj/item/bodypart/arm/left/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_ARM))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_gain))
+ ..()
-/obj/item/bodypart/arm/left/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_ARM))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
-
+/obj/item/bodypart/arm/left/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_ARM))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
+ ..()
///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_ARM trait.
/obj/item/bodypart/arm/left/proc/on_owner_paralysis_gain(mob/living/carbon/source)
@@ -172,7 +211,6 @@
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_L_ARM trait.
/obj/item/bodypart/arm/left/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
@@ -180,7 +218,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_gain))
-
/obj/item/bodypart/arm/left/set_disabled(new_disabled)
. = ..()
if(isnull(.) || !owner)
@@ -200,7 +237,6 @@
var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
hand_screen_object?.update_appearance()
-
/obj/item/bodypart/arm/left/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -233,7 +269,6 @@
should_draw_greyscale = FALSE
appendage_noun = "scythe-like hand"
-
/obj/item/bodypart/arm/right
name = "right arm"
desc = "Over 87% of humans are right handed. That figure is much lower \
@@ -249,26 +284,22 @@
px_y = 0
bodypart_trait_source = RIGHT_ARM_TRAIT
-/obj/item/bodypart/arm/right/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_ARM))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
+/obj/item/bodypart/arm/right/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_ARM))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_gain))
+ ..()
+/obj/item/bodypart/arm/right/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_ARM))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
+ ..()
///Proc to react to the owner gaining the TRAIT_PARALYSIS_R_ARM trait.
/obj/item/bodypart/arm/right/proc/on_owner_paralysis_gain(mob/living/carbon/source)
@@ -277,7 +308,6 @@
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_R_ARM trait.
/obj/item/bodypart/arm/right/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
@@ -285,7 +315,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_gain))
-
/obj/item/bodypart/arm/right/set_disabled(new_disabled)
. = ..()
if(isnull(.) || !owner)
@@ -305,7 +334,6 @@
var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
hand_screen_object?.update_appearance()
-
/obj/item/bodypart/arm/right/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -375,35 +403,30 @@
can_be_disabled = TRUE
bodypart_trait_source = LEFT_LEG_TRAIT
-/obj/item/bodypart/leg/left/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_LEG))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
+/obj/item/bodypart/leg/left/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_LEG))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_gain))
+ ..()
+/obj/item/bodypart/leg/left/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_LEG))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
+ ..()
-///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_LEG trait.
+///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_ARM trait.
/obj/item/bodypart/leg/left/proc/on_owner_paralysis_gain(mob/living/carbon/source)
SIGNAL_HANDLER
ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_L_LEG trait.
/obj/item/bodypart/leg/left/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
@@ -411,7 +434,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_gain))
-
/obj/item/bodypart/leg/left/set_disabled(new_disabled)
. = ..()
if(isnull(.) || !owner)
@@ -469,26 +491,22 @@
px_y = 12
bodypart_trait_source = RIGHT_LEG_TRAIT
-/obj/item/bodypart/leg/right/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_LEG))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
+/obj/item/bodypart/leg/right/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_LEG))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_gain))
+ ..()
+/obj/item/bodypart/leg/right/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_LEG))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
+ ..()
///Proc to react to the owner gaining the TRAIT_PARALYSIS_R_LEG trait.
/obj/item/bodypart/leg/right/proc/on_owner_paralysis_gain(mob/living/carbon/source)
@@ -497,7 +515,6 @@
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_R_LEG trait.
/obj/item/bodypart/leg/right/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
index 3aae6696ed6..e1521cad3e5 100644
--- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
@@ -518,17 +518,16 @@
)
return ..()
-/obj/item/bodypart/arm/left/golem/set_owner(new_owner)
+/obj/item/bodypart/arm/left/golem/clear_ownership(mob/living/carbon/old_owner)
. = ..()
- if (. == FALSE)
- return
- if (owner)
- owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
- if (isnull(.))
- return
- var/mob/living/carbon/old_owner = .
+
old_owner.RemoveComponentSource(REF(src), /datum/component/shovel_hands)
+/obj/item/bodypart/arm/left/golem/apply_ownership(mob/living/carbon/new_owner)
+ . = ..()
+
+ new_owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
+
/obj/item/bodypart/arm/right/golem
icon = 'icons/mob/human/species/golems.dmi'
icon_static = 'icons/mob/human/species/golems.dmi'
@@ -552,17 +551,16 @@
)
return ..()
-/obj/item/bodypart/arm/right/golem/set_owner(new_owner)
+/obj/item/bodypart/arm/right/golem/clear_ownership(mob/living/carbon/old_owner)
. = ..()
- if (. == FALSE)
- return
- if (owner)
- owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
- if (isnull(.))
- return
- var/mob/living/carbon/old_owner = .
+
old_owner.RemoveComponentSource(REF(src), /datum/component/shovel_hands)
+/obj/item/bodypart/arm/right/golem/apply_ownership(mob/living/carbon/new_owner)
+ . = ..()
+
+ new_owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
+
/obj/item/bodypart/leg/left/golem
icon = 'icons/mob/human/species/golems.dmi'
icon_static = 'icons/mob/human/species/golems.dmi'
diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm
index a3feba76fec..d7f08a7be35 100644
--- a/code/modules/surgery/organs/_organ.dm
+++ b/code/modules/surgery/organs/_organ.dm
@@ -6,6 +6,8 @@
throwforce = 0
/// The mob that owns this organ.
var/mob/living/carbon/owner = null
+ /// Reference to the limb we're inside of
+ var/obj/item/bodypart/bodypart_owner
/// The cached info about the blood this organ belongs to
var/list/blood_dna_info = list("Synthetic DNA" = "O+") // not every organ spawns inside a person
/// The body zone this organ is supposed to inhabit.
@@ -77,134 +79,16 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
volume = reagent_vol,\
after_eat = CALLBACK(src, PROC_REF(OnEatFrom)))
- if(!IS_ROBOTIC_ORGAN(src))
- add_blood_DNA(blood_dna_info)
-
-/*
- * Insert the organ into the select mob.
- *
- * receiver - the mob who will get our organ
- * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
- * drop_if_replaced - if there's an organ in the slot already, whether we drop it afterwards
- */
-/obj/item/organ/proc/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
- SHOULD_CALL_PARENT(TRUE)
-
- if(!iscarbon(receiver) || owner == receiver)
- return FALSE
-
- var/obj/item/organ/replaced = receiver.get_organ_slot(slot)
- if(replaced)
- replaced.Remove(receiver, special = TRUE)
- if(drop_if_replaced)
- replaced.forceMove(get_turf(receiver))
- else
- qdel(replaced)
-
- receiver.organs |= src
- receiver.organs_slot[slot] = src
- owner = receiver
-
- if(!IS_ROBOTIC_ORGAN(src) && (organ_flags & ORGAN_VIRGIN))
- blood_dna_info = receiver.get_blood_dna_list()
- // need to remove the synethic blood DNA that is initialized
- // wash also adds the blood dna again
- wash(CLEAN_TYPE_BLOOD)
- organ_flags &= ~ORGAN_VIRGIN
-
-
- // Apply unique side-effects. Return value does not matter.
- on_insert(receiver, special)
-
- return TRUE
-
-/// Called after the organ is inserted into a mob.
-/// Adds Traits, Actions, and Status Effects on the mob in which the organ is impanted.
-/// Override this proc to create unique side-effects for inserting your organ. Must be called by overrides.
-/obj/item/organ/proc/on_insert(mob/living/carbon/organ_owner, special)
- SHOULD_CALL_PARENT(TRUE)
-
- moveToNullspace()
-
- for(var/trait in organ_traits)
- ADD_TRAIT(organ_owner, trait, REF(src))
-
- for(var/datum/action/action as anything in actions)
- action.Grant(organ_owner)
-
- for(var/datum/status_effect/effect as anything in organ_effects)
- organ_owner.apply_status_effect(effect, type)
-
- RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_owner_examine))
- SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, organ_owner)
- SEND_SIGNAL(organ_owner, COMSIG_CARBON_GAIN_ORGAN, src, special)
-
-/*
- * Remove the organ from the select mob.
- *
- * * organ_owner - the mob who owns our organ, that we're removing the organ from.
- * * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
- */
-/obj/item/organ/proc/Remove(mob/living/carbon/organ_owner, special = FALSE)
- SHOULD_CALL_PARENT(TRUE)
-
- organ_owner.organs -= src
- if(organ_owner.organs_slot[slot] == src)
- organ_owner.organs_slot.Remove(slot)
-
- owner = null
-
- // Apply or reset unique side-effects. Return value does not matter.
- on_remove(organ_owner, special)
-
- return TRUE
-
-/// Called after the organ is removed from a mob.
-/// Removes Traits, Actions, and Status Effects on the mob in which the organ was impanted.
-/// Override this proc to create unique side-effects for removing your organ. Must be called by overrides.
-/obj/item/organ/proc/on_remove(mob/living/carbon/organ_owner, special)
- SHOULD_CALL_PARENT(TRUE)
-
- if(!iscarbon(organ_owner))
- stack_trace("Organ removal should not be happening on non carbon mobs: [organ_owner]")
-
- for(var/trait in organ_traits)
- REMOVE_TRAIT(organ_owner, trait, REF(src))
-
- for(var/datum/action/action as anything in actions)
- action.Remove(organ_owner)
-
- for(var/datum/status_effect/effect as anything in organ_effects)
- organ_owner.remove_status_effect(effect, type)
-
- UnregisterSignal(organ_owner, COMSIG_ATOM_EXAMINE)
- SEND_SIGNAL(src, COMSIG_ORGAN_REMOVED, organ_owner)
- SEND_SIGNAL(organ_owner, COMSIG_CARBON_LOSE_ORGAN, src, special)
-
- // We don't need to readd things to the organ if it's getting deleted
- if(QDELING(src))
- return
-
- if(!IS_ROBOTIC_ORGAN(src) && !(item_flags & NO_BLOOD_ON_ITEM))
- AddElement(/datum/element/decal/blood)
-
- var/list/diseases = organ_owner.get_static_viruses()
- if(!LAZYLEN(diseases))
- return
-
- var/list/datum/disease/diseases_to_add = list()
- for(var/datum/disease/disease as anything in diseases)
- // robotic organs are immune to disease unless 'inorganic biology' symptom is present
- if(IS_ROBOTIC_ORGAN(src) && !(disease.infectable_biotypes & MOB_ROBOTIC))
- continue
-
- // admin or special viruses that should not be reproduced
- if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
- continue
-
- diseases_to_add += disease
- if(LAZYLEN(diseases_to_add))
- AddComponent(/datum/component/infective, diseases_to_add)
+/obj/item/organ/Destroy()
+ if(bodypart_owner && !owner && !QDELETED(bodypart_owner))
+ bodypart_remove(bodypart_owner)
+ else if(owner)
+ // The special flag is important, because otherwise mobs can die
+ // while undergoing transformation into different mobs.
+ Remove(owner, special=TRUE)
+ else
+ STOP_PROCESSING(SSobj, src)
+ return ..()
/// Add a Trait to an organ that it will give its owner.
/obj/item/organ/proc/add_organ_trait(trait)
@@ -241,15 +125,6 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/obj/item/organ/proc/on_find(mob/living/finder)
return
-/**
- * Proc that gets called when the organ is surgically removed by someone, can be used for special effects
- * Currently only used so surplus organs can explode when surgically removed.
- */
-/obj/item/organ/proc/on_surgical_removal(mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
- SHOULD_CALL_PARENT(TRUE)
- SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, old_owner, target_zone, tool)
- RemoveElement(/datum/element/decal/blood)
-
/obj/item/organ/wash(clean_types)
. = ..()
@@ -452,4 +327,4 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/// Tries to replace the existing organ on the passed mob with this one, with special handling for replacing a brain without ghosting target
/obj/item/organ/proc/replace_into(mob/living/carbon/new_owner)
- return Insert(new_owner, special = TRUE, drop_if_replaced = FALSE)
+ return Insert(new_owner, special = TRUE, movement_flags = DELETE_IF_REPLACED)
diff --git a/code/modules/surgery/organs/external/_external_organ.dm b/code/modules/surgery/organs/external/_external_organ.dm
index fd1af3d5f93..6dfaab03a94 100644
--- a/code/modules/surgery/organs/external/_external_organ.dm
+++ b/code/modules/surgery/organs/external/_external_organ.dm
@@ -12,8 +12,6 @@
///The overlay datum that actually draws stuff on the limb
var/datum/bodypart_overlay/mutant/bodypart_overlay
- ///Reference to the limb we're inside of
- var/obj/item/bodypart/ownerlimb
///If not null, overrides the appearance with this sprite accessory datum
var/sprite_accessory_override
@@ -25,7 +23,7 @@
///Set to EXTERNAL_BEHIND, EXTERNAL_FRONT or EXTERNAL_ADJACENT if you want to draw one of those layers as the object sprite. FALSE to use your own
///This will not work if it doesn't have a limb to generate it's icon with
var/use_mob_sprite_as_obj_sprite = FALSE
- ///Does this organ have any bodytypes to pass to it's ownerlimb?
+ ///Does this organ have any bodytypes to pass to it's bodypart_owner?
var/external_bodytypes = NONE
///Which flags does a 'modification tool' need to have to restyle us, if it all possible (located in code/_DEFINES/mobs)
var/restyle_flags = NONE
@@ -59,23 +57,10 @@
if(restyle_flags)
RegisterSignal(src, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle))
-/obj/item/organ/external/Destroy()
- if(owner)
- Remove(owner, special = TRUE)
- else if(ownerlimb)
- remove_from_limb()
-
- return ..()
-
-/obj/item/organ/external/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/mob_insert(mob/living/carbon/receiver, special, movement_flags)
if(!should_external_organ_apply_to(type, receiver))
stack_trace("adding a [type] to a [receiver.type] when it shouldn't be!")
- var/obj/item/bodypart/limb = receiver.get_bodypart(deprecise_zone(zone))
-
- if(!limb)
- return FALSE
-
. = ..()
if(!.)
@@ -91,54 +76,28 @@
// SKYRAT EDIT CHANGE END
bodypart_overlay.imprint_on_next_insertion = FALSE
- ownerlimb = limb
- add_to_limb(ownerlimb)
-
if(external_bodytypes)
receiver.synchronize_bodytypes()
receiver.update_body_parts()
-/obj/item/organ/external/Remove(mob/living/carbon/organ_owner, special, moving)
- . = ..()
-
- if(ownerlimb)
- remove_from_limb()
- if(!moving && use_mob_sprite_as_obj_sprite) //so we're being taken out and dropped
- update_appearance(UPDATE_OVERLAYS)
-
- if(organ_owner)
+/obj/item/organ/external/mob_remove(mob/living/carbon/organ_owner, special, moving)
+ if(!special)
+ organ_owner.synchronize_bodytypes()
organ_owner.update_body_parts()
-
-
-/obj/item/organ/external/on_remove(mob/living/carbon/organ_owner, special)
- . = ..()
- color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail
-
-///Transfers the organ to the limb, and to the limb's owner, if it has one.
-/obj/item/organ/external/transfer_to_limb(obj/item/bodypart/bodypart, mob/living/carbon/bodypart_owner)
- if(owner)
- Remove(owner, moving = TRUE)
- else if(ownerlimb)
- remove_from_limb()
-
- if(bodypart_owner)
- Insert(bodypart_owner, TRUE)
- else
- add_to_limb(bodypart)
-
-/obj/item/organ/external/add_to_limb(obj/item/bodypart/bodypart)
- bodypart.external_organs += src
- ownerlimb = bodypart
- ownerlimb.add_bodypart_overlay(bodypart_overlay)
return ..()
-/obj/item/organ/external/remove_from_limb()
- ownerlimb.external_organs -= src
- ownerlimb.remove_bodypart_overlay(bodypart_overlay)
- if(ownerlimb.owner && external_bodytypes)
- ownerlimb.owner.synchronize_bodytypes()
- ownerlimb = null
+/obj/item/organ/external/on_bodypart_insert(obj/item/bodypart/bodypart)
+ bodypart.add_bodypart_overlay(bodypart_overlay)
+ return ..()
+
+/obj/item/organ/external/on_bodypart_remove(obj/item/bodypart/bodypart)
+ bodypart.remove_bodypart_overlay(bodypart_overlay)
+
+ if(use_mob_sprite_as_obj_sprite)
+ update_appearance(UPDATE_OVERLAYS)
+
+ color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail
return ..()
/proc/should_external_organ_apply_to(obj/item/organ/external/organpath, mob/living/carbon/target)
@@ -172,8 +131,8 @@
if(owner) //are we in a person?
owner.update_body_parts()
- else if(ownerlimb) //are we in a limb?
- ownerlimb.update_icon_dropped()
+ else if(bodypart_owner) //are we in a limb?
+ bodypart_owner.update_icon_dropped()
//else if(use_mob_sprite_as_obj_sprite) //are we out in the world, unprotected by flesh?
/obj/item/organ/external/on_life(seconds_per_tick, times_fired)
@@ -188,7 +147,7 @@
//Build the mob sprite and use it as our overlay
for(var/external_layer in bodypart_overlay.all_layers)
if(bodypart_overlay.layers & external_layer)
- . += bodypart_overlay.get_overlay(external_layer, ownerlimb)
+ . += bodypart_overlay.get_overlay(external_layer, bodypart_owner)
///The horns of a lizard!
/obj/item/organ/external/horns
@@ -294,16 +253,17 @@
///Store our old datum here for if our antennae are healed
var/original_sprite_datum
-/obj/item/organ/external/antennae/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/antennae/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(!.)
return
RegisterSignal(receiver, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_antennae))
RegisterSignal(receiver, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_antennae))
-/obj/item/organ/external/antennae/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/antennae/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
- UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL))
+ if(organ_owner)
+ UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL))
///check if our antennae can burn off ;_;
/obj/item/organ/external/antennae/proc/try_burn_antennae(mob/living/carbon/human/human)
diff --git a/code/modules/surgery/organs/external/restyling.dm b/code/modules/surgery/organs/external/restyling.dm
index 454e4395ae6..7d6be1b6d58 100644
--- a/code/modules/surgery/organs/external/restyling.dm
+++ b/code/modules/surgery/organs/external/restyling.dm
@@ -31,7 +31,7 @@
///Asks the external organs inside the limb if they can restyle
/obj/item/bodypart/proc/attempt_feature_restyle(atom/source, mob/living/trimmer, atom/movable/original_target, body_zone, restyle_type, style_speed)
var/list/valid_features = list()
- for(var/obj/item/organ/external/feature in external_organs)
+ for(var/obj/item/organ/external/feature in contents)
if(feature.restyle_flags & restyle_type)
valid_features.Add(feature)
diff --git a/code/modules/surgery/organs/external/spines.dm b/code/modules/surgery/organs/external/spines.dm
index 1d2378a58f9..98cf399a540 100644
--- a/code/modules/surgery/organs/external/spines.dm
+++ b/code/modules/surgery/organs/external/spines.dm
@@ -16,13 +16,13 @@
///A two-way reference between the tail and the spines because of wagging sprites. Bruh.
var/obj/item/organ/external/tail/lizard/paired_tail
-/obj/item/organ/external/spines/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/spines/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(.)
paired_tail = locate(/obj/item/organ/external/tail/lizard) in receiver.organs //We want specifically a lizard tail, so we don't use the slot.
paired_tail?.paired_spines = src
-/obj/item/organ/external/spines/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/spines/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
if(paired_tail)
paired_tail.paired_spines = null
diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm
index 3235ddf7dbc..48c35870283 100644
--- a/code/modules/surgery/organs/external/tails.dm
+++ b/code/modules/surgery/organs/external/tails.dm
@@ -18,7 +18,7 @@
///The original owner of this tail
var/original_owner //Yay, snowflake code!
-/obj/item/organ/external/tail/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/tail/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(.)
RegisterSignal(receiver, COMSIG_ORGAN_WAG_TAIL, PROC_REF(wag))
@@ -32,15 +32,12 @@
else if(type in receiver.dna.species.external_organs)
receiver.add_mood_event("wrong_tail_regained", /datum/mood_event/tail_regained_wrong)
-/obj/item/organ/external/tail/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/tail/on_mob_remove(mob/living/carbon/organ_owner, special)
+ . = ..()
+
if(wag_flags & WAG_WAGGING)
wag(organ_owner, start = FALSE)
- return ..()
-
-/obj/item/organ/external/tail/on_remove(mob/living/carbon/organ_owner, special)
- . = ..()
-
UnregisterSignal(organ_owner, COMSIG_ORGAN_WAG_TAIL)
if(type in organ_owner.dna.species.external_organs)
@@ -141,13 +138,13 @@
///A reference to the paired_spines, since for some fucking reason tail spines are tied to the spines themselves.
var/obj/item/organ/external/spines/paired_spines
-/obj/item/organ/external/tail/lizard/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/tail/lizard/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(.)
- paired_spines = ownerlimb.owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_SPINES)
+ paired_spines = bodypart_owner.owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_SPINES)
paired_spines?.paired_tail = src
-/obj/item/organ/external/tail/lizard/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/tail/lizard/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
if(paired_spines)
paired_spines.paired_tail = null
diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm
index 42e41b70279..821e83bfb1d 100644
--- a/code/modules/surgery/organs/external/wings/functional_wings.dm
+++ b/code/modules/surgery/organs/external/wings/functional_wings.dm
@@ -29,13 +29,13 @@
// grind_results = list(/datum/reagent/flightpotion = 5)
food_reagents = list(/datum/reagent/flightpotion = 5)
-/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(. && isnull(fly))
fly = new
fly.Grant(receiver)
-/obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
fly.Remove(organ_owner)
diff --git a/code/modules/surgery/organs/external/wings/moth_wings.dm b/code/modules/surgery/organs/external/wings/moth_wings.dm
index 595b907673b..3891527a025 100644
--- a/code/modules/surgery/organs/external/wings/moth_wings.dm
+++ b/code/modules/surgery/organs/external/wings/moth_wings.dm
@@ -14,13 +14,13 @@
///Store our old datum here for if our burned wings are healed
var/original_sprite_datum
-/obj/item/organ/external/wings/moth/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/external/wings/moth/on_mob_insert(mob/living/carbon/receiver)
. = ..()
RegisterSignal(receiver, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_wings))
RegisterSignal(receiver, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_wings))
RegisterSignal(receiver, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(update_float_move))
-/obj/item/organ/external/wings/moth/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/external/wings/moth/on_mob_remove(mob/living/carbon/organ_owner)
. = ..()
UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_MOVABLE_PRE_MOVE))
REMOVE_TRAIT(organ_owner, TRAIT_FREE_FLOAT_MOVEMENT, REF(src))
diff --git a/code/modules/surgery/organs/internal/_internal_organ.dm b/code/modules/surgery/organs/internal/_internal_organ.dm
index 4e1b6b73701..d7273e1f4ba 100644
--- a/code/modules/surgery/organs/internal/_internal_organ.dm
+++ b/code/modules/surgery/organs/internal/_internal_organ.dm
@@ -5,19 +5,8 @@
. = ..()
START_PROCESSING(SSobj, src)
-/obj/item/organ/internal/Destroy()
- if(owner)
- // The special flag is important, because otherwise mobs can die
- // while undergoing transformation into different mobs.
- Remove(owner, special=TRUE)
- else
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/item/organ/internal/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
- if(!. || !owner)
- return
// organs_slot must ALWAYS be ordered in the same way as organ_process_order
// Otherwise life processing breaks down
@@ -25,18 +14,16 @@
STOP_PROCESSING(SSobj, src)
-/obj/item/organ/internal/Remove(mob/living/carbon/organ_owner, special = FALSE)
+/obj/item/organ/internal/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE)
. = ..()
- if(organ_owner)
- if((organ_flags & ORGAN_VITAL) && !special && !(organ_owner.status_flags & GODMODE))
- if(organ_owner.stat != DEAD)
- organ_owner.investigate_log("has been killed by losing a vital organ ([src]).", INVESTIGATE_DEATHS)
- organ_owner.death()
+ if((organ_flags & ORGAN_VITAL) && !special && !(organ_owner.status_flags & GODMODE))
+ if(organ_owner.stat != DEAD)
+ organ_owner.investigate_log("has been killed by losing a vital organ ([src]).", INVESTIGATE_DEATHS)
+ organ_owner.death()
START_PROCESSING(SSobj, src)
-
/obj/item/organ/internal/process(seconds_per_tick, times_fired)
on_death(seconds_per_tick, times_fired) //Kinda hate doing it like this, but I really don't want to call process directly.
diff --git a/code/modules/surgery/organs/internal/appendix/_appendix.dm b/code/modules/surgery/organs/internal/appendix/_appendix.dm
index ee87d674dc4..e5190f1282e 100644
--- a/code/modules/surgery/organs/internal/appendix/_appendix.dm
+++ b/code/modules/surgery/organs/internal/appendix/_appendix.dm
@@ -76,12 +76,12 @@
/obj/item/organ/internal/appendix/get_availability(datum/species/owner_species, mob/living/owner_mob)
return owner_species.mutantappendix
-/obj/item/organ/internal/appendix/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/on_mob_remove(mob/living/carbon/organ_owner)
. = ..()
REMOVE_TRAIT(organ_owner, TRAIT_DISEASELIKE_SEVERITY_MEDIUM, type)
organ_owner.med_hud_set_status()
-/obj/item/organ/internal/appendix/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
if(inflamation_stage)
ADD_TRAIT(organ_owner, TRAIT_DISEASELIKE_SEVERITY_MEDIUM, type)
diff --git a/code/modules/surgery/organs/internal/appendix/appendix_golem.dm b/code/modules/surgery/organs/internal/appendix/appendix_golem.dm
index ede8dfa1e6b..5510b4bf967 100644
--- a/code/modules/surgery/organs/internal/appendix/appendix_golem.dm
+++ b/code/modules/surgery/organs/internal/appendix/appendix_golem.dm
@@ -12,7 +12,7 @@
. = ..()
smelter = new(src)
-/obj/item/organ/internal/appendix/golem/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/golem/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(check_for_lava))
@@ -25,7 +25,7 @@
if (smelter.owner != owner)
smelter.Grant(owner)
-/obj/item/organ/internal/appendix/golem/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/golem/on_mob_remove(mob/living/carbon/organ_owner)
UnregisterSignal(organ_owner, COMSIG_MOVABLE_MOVED)
smelter?.Remove(organ_owner) // Might have been deleted by Destroy already
return ..()
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
index 94a4aba90b3..463e34977ee 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
@@ -76,7 +76,7 @@
to_chat(user, span_notice("You modify [src] to be installed on the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."))
update_appearance()
-/obj/item/organ/internal/cyberimp/arm/on_insert(mob/living/carbon/arm_owner)
+/obj/item/organ/internal/cyberimp/arm/on_mob_insert(mob/living/carbon/arm_owner)
. = ..()
var/side = zone == BODY_ZONE_R_ARM? RIGHT_HANDS : LEFT_HANDS
hand = arm_owner.hand_bodyparts[side]
@@ -84,7 +84,7 @@
RegisterSignal(hand, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_item_attack_self)) //If the limb gets an attack-self, open the menu. Only happens when hand is empty
RegisterSignal(arm_owner, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(dropkey)) //We're nodrop, but we'll watch for the drop hotkey anyway and then stow if possible.
-/obj/item/organ/internal/cyberimp/arm/on_remove(mob/living/carbon/arm_owner)
+/obj/item/organ/internal/cyberimp/arm/on_mob_remove(mob/living/carbon/arm_owner)
. = ..()
Retract()
if(hand)
@@ -379,14 +379,14 @@
///How long will the implant malfunction if it is EMP'd
var/emp_base_duration = 9 SECONDS
-/obj/item/organ/internal/cyberimp/arm/muscle/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/cyberimp/arm/muscle/on_mob_insert(mob/living/carbon/arm_owner)
. = ..()
- if(ishuman(receiver)) //Sorry, only humans
- RegisterSignal(receiver, COMSIG_LIVING_EARLY_UNARMED_ATTACK, PROC_REF(on_attack_hand))
+ if(ishuman(arm_owner)) //Sorry, only humans
+ RegisterSignal(arm_owner, COMSIG_LIVING_EARLY_UNARMED_ATTACK, PROC_REF(on_attack_hand))
-/obj/item/organ/internal/cyberimp/arm/muscle/Remove(mob/living/carbon/implant_owner, special = 0)
+/obj/item/organ/internal/cyberimp/arm/muscle/on_mob_remove(mob/living/carbon/arm_owner)
. = ..()
- UnregisterSignal(implant_owner, COMSIG_LIVING_EARLY_UNARMED_ATTACK)
+ UnregisterSignal(arm_owner, COMSIG_LIVING_EARLY_UNARMED_ATTACK)
/obj/item/organ/internal/cyberimp/arm/muscle/emp_act(severity)
. = ..()
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm
index d97df1d043b..513f0794c31 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm
@@ -181,7 +181,7 @@
/datum/effect_system/trail_follow/ion \
)
-/obj/item/organ/internal/cyberimp/chest/thrusters/Remove(mob/living/carbon/thruster_owner, special = 0)
+/obj/item/organ/internal/cyberimp/chest/thrusters/Remove(mob/living/carbon/thruster_owner, special, movement_flags)
if(on)
deactivate(silent = TRUE)
..()
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm
index 869d4cc1c9e..cec0241ece3 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm
@@ -31,7 +31,7 @@
hud.show_to(eye_owner)
toggled_on = TRUE
-/obj/item/organ/internal/cyberimp/eyes/hud/Insert(mob/living/carbon/eye_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/cyberimp/eyes/hud/Insert(mob/living/carbon/eye_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -42,7 +42,7 @@
ADD_TRAIT(eye_owner, HUD_trait, ORGAN_TRAIT)
toggled_on = TRUE
-/obj/item/organ/internal/cyberimp/eyes/hud/Remove(mob/living/carbon/eye_owner, special = FALSE)
+/obj/item/organ/internal/cyberimp/eyes/hud/Remove(mob/living/carbon/eye_owner, special, movement_flags)
. = ..()
if(HUD_type)
var/datum/atom_hud/hud = GLOB.huds[HUD_type]
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
index f0578832969..67a02e71d7e 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
@@ -87,7 +87,7 @@
stored_items = list()
-/obj/item/organ/internal/cyberimp/brain/anti_drop/Remove(mob/living/carbon/implant_owner, special = 0)
+/obj/item/organ/internal/cyberimp/brain/anti_drop/Remove(mob/living/carbon/implant_owner, special, movement_flags)
if(active)
ui_action_click()
..()
@@ -113,11 +113,11 @@
var/stun_cap_amount = 40
-/obj/item/organ/internal/cyberimp/brain/anti_stun/on_remove(mob/living/carbon/implant_owner)
+/obj/item/organ/internal/cyberimp/brain/anti_stun/on_mob_remove(mob/living/carbon/implant_owner)
. = ..()
UnregisterSignal(implant_owner, signalCache)
-/obj/item/organ/internal/cyberimp/brain/anti_stun/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/internal/cyberimp/brain/anti_stun/on_mob_insert(mob/living/carbon/receiver)
. = ..()
RegisterSignals(receiver, signalCache, PROC_REF(on_signal))
diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm
index b77efe90c2e..5da30b0d061 100644
--- a/code/modules/surgery/organs/internal/ears/_ears.dm
+++ b/code/modules/surgery/organs/internal/ears/_ears.dm
@@ -70,7 +70,7 @@
//SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION
/*
-/obj/item/organ/internal/ears/cat/on_insert(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/cat/on_mob_insert(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
@@ -78,7 +78,7 @@
ear_owner.dna.update_uf_block(DNA_EARS_BLOCK)
ear_owner.update_body()
-/obj/item/organ/internal/ears/cat/on_remove(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/cat/on_mob_remove(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
@@ -91,13 +91,13 @@
name = "penguin ears"
desc = "The source of a penguin's happy feet."
-/obj/item/organ/internal/ears/penguin/on_insert(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/penguin/on_mob_insert(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner))
to_chat(ear_owner, span_notice("You suddenly feel like you've lost your balance."))
ear_owner.AddElement(/datum/element/waddling)
-/obj/item/organ/internal/ears/penguin/on_remove(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/penguin/on_mob_remove(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner))
to_chat(ear_owner, span_notice("Your sense of balance comes back to you."))
@@ -126,11 +126,11 @@
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
-/obj/item/organ/internal/ears/cybernetic/whisper/on_insert(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/whisper/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
-/obj/item/organ/internal/ears/cybernetic/whisper/on_remove(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/whisper/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
@@ -142,11 +142,11 @@
// Same sensitivity as felinid ears
damage_multiplier = 2
-/obj/item/organ/internal/ears/cybernetic/xray/on_insert(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/xray/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
-/obj/item/organ/internal/ears/cybernetic/xray/on_remove(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/xray/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index 5ef52f589ca..f21e67552a3 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -52,7 +52,7 @@
/// Native FOV that will be applied if a config is enabled
var/native_fov = FOV_180_DEGREES //SKYRAT EDIT CHANGE
-/obj/item/organ/internal/eyes/Insert(mob/living/carbon/eye_recipient, special = FALSE, drop_if_replaced = FALSE)
+/obj/item/organ/internal/eyes/Insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED)
// If we don't do this before everything else, heterochromia will be reset leading to eye_color_right no longer being accurate
if(ishuman(eye_recipient))
var/mob/living/carbon/human/human_recipient = eye_recipient
@@ -99,7 +99,7 @@
if(call_update)
affected_human.update_body()
-/obj/item/organ/internal/eyes/Remove(mob/living/carbon/eye_owner, special = FALSE)
+/obj/item/organ/internal/eyes/Remove(mob/living/carbon/eye_owner, special, movement_flags)
. = ..()
if(ishuman(eye_owner))
var/mob/living/carbon/human/human_owner = eye_owner
@@ -109,7 +109,8 @@
human_owner.eye_color_right = old_eye_color_right
if(native_fov)
eye_owner.remove_fov_trait(type)
- human_owner.update_body()
+ if(!special)
+ human_owner.update_body()
// Cure blindness from eye damage
eye_owner.cure_blind(EYE_DAMAGE)
@@ -353,11 +354,11 @@
eye_color_right = "000"
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
-/obj/item/organ/internal/eyes/robotic/xray/on_insert(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/robotic/xray/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
-/obj/item/organ/internal/eyes/robotic/xray/on_remove(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/robotic/xray/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
REMOVE_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
@@ -385,7 +386,7 @@
/obj/item/organ/internal/eyes/robotic/flashlight/emp_act(severity)
return
-/obj/item/organ/internal/eyes/robotic/flashlight/on_insert(mob/living/carbon/victim)
+/obj/item/organ/internal/eyes/robotic/flashlight/on_mob_insert(mob/living/carbon/victim)
. = ..()
if(!eye)
eye = new /obj/item/flashlight/eyelight()
@@ -394,7 +395,7 @@
eye.update_brightness(victim)
victim.become_blind(FLASHLIGHT_EYES)
-/obj/item/organ/internal/eyes/robotic/flashlight/on_remove(mob/living/carbon/victim)
+/obj/item/organ/internal/eyes/robotic/flashlight/on_mob_remove(mob/living/carbon/victim)
. = ..()
eye.set_light_on(FALSE)
eye.update_brightness(victim)
@@ -451,18 +452,18 @@
deactivate(close_ui = TRUE)
/// Set the initial color of the eyes on insert to be the mob's previous eye color.
-/obj/item/organ/internal/eyes/robotic/glow/Insert(mob/living/carbon/eye_recipient, special = FALSE, drop_if_replaced = FALSE)
+/obj/item/organ/internal/eyes/robotic/glow/Insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
left_eye_color_string = old_eye_color_left
right_eye_color_string = old_eye_color_right
update_mob_eye_color(eye_recipient)
-/obj/item/organ/internal/eyes/robotic/glow/on_insert(mob/living/carbon/eye_recipient)
+/obj/item/organ/internal/eyes/robotic/glow/on_mob_insert(mob/living/carbon/eye_recipient)
. = ..()
deactivate(close_ui = TRUE)
eye.forceMove(eye_recipient)
-/obj/item/organ/internal/eyes/robotic/glow/on_remove(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/robotic/glow/on_mob_remove(mob/living/carbon/eye_owner)
deactivate(eye_owner, close_ui = TRUE)
if(!QDELETED(eye))
eye.forceMove(src)
@@ -750,7 +751,7 @@
high_light_cutoff = list(30, 35, 50)
var/obj/item/flashlight/eyelight/adapted/adapt_light
-/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/on_insert(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
//add lighting
if(!adapt_light)
@@ -769,7 +770,7 @@
apply_organ_damage(-10) //heal quickly
. = ..()
-/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/Remove(mob/living/carbon/unadapted, special = FALSE)
+/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/on_mob_remove(mob/living/carbon/unadapted, special = FALSE)
//remove lighting
adapt_light.set_light_on(FALSE)
adapt_light.update_brightness(unadapted)
diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm
index 8a5646d1814..e2ca8725b9d 100644
--- a/code/modules/surgery/organs/internal/heart/_heart.dm
+++ b/code/modules/surgery/organs/internal/heart/_heart.dm
@@ -33,7 +33,7 @@
. = ..()
icon_state = "[base_icon_state]-[beating ? "on" : "off"]"
-/obj/item/organ/internal/heart/Remove(mob/living/carbon/heartless, special = 0)
+/obj/item/organ/internal/heart/Remove(mob/living/carbon/heartless, special, movement_flags)
. = ..()
if(!special)
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS)
@@ -146,12 +146,14 @@
else
return ..()
-/obj/item/organ/internal/heart/cursed/on_insert(mob/living/carbon/accursed)
+/obj/item/organ/internal/heart/cursed/on_mob_insert(mob/living/carbon/accursed)
. = ..()
+
accursed.AddComponent(/datum/component/manual_heart, pump_delay = pump_delay, blood_loss = blood_loss, heal_brute = heal_brute, heal_burn = heal_burn, heal_oxy = heal_oxy)
-/obj/item/organ/internal/heart/cursed/Remove(mob/living/carbon/accursed, special = FALSE)
+/obj/item/organ/internal/heart/cursed/on_mob_remove(mob/living/carbon/accursed, special = FALSE)
. = ..()
+
qdel(accursed.GetComponent(/datum/component/manual_heart))
/obj/item/organ/internal/heart/cybernetic
diff --git a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
index 78ee55b0b28..50659115872 100644
--- a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
+++ b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
@@ -21,7 +21,7 @@
add_atom_colour(ethereal_color, FIXED_COLOUR_PRIORITY)
update_appearance()
-/obj/item/organ/internal/heart/ethereal/Insert(mob/living/carbon/heart_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/heart/ethereal/Insert(mob/living/carbon/heart_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -29,7 +29,7 @@
RegisterSignal(heart_owner, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(on_owner_fully_heal))
RegisterSignal(heart_owner, COMSIG_QDELETING, PROC_REF(owner_deleted))
-/obj/item/organ/internal/heart/ethereal/Remove(mob/living/carbon/heart_owner, special = FALSE)
+/obj/item/organ/internal/heart/ethereal/Remove(mob/living/carbon/heart_owner, special, movement_flags)
UnregisterSignal(heart_owner, list(COMSIG_MOB_STATCHANGE, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_QDELETING))
REMOVE_TRAIT(heart_owner, TRAIT_CORPSELOCKED, SPECIES_TRAIT)
stop_crystalization_process(heart_owner)
diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm
index 28f74d9cf52..db619536c5a 100644
--- a/code/modules/surgery/organs/internal/liver/_liver.dm
+++ b/code/modules/surgery/organs/internal/liver/_liver.dm
@@ -61,12 +61,12 @@
qdel(GetComponent(/datum/component/squeak))
/// Registers COMSIG_SPECIES_HANDLE_CHEMICAL from owner
-/obj/item/organ/internal/liver/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(organ_owner, COMSIG_SPECIES_HANDLE_CHEMICAL, PROC_REF(handle_chemical))
/// Unregisters COMSIG_SPECIES_HANDLE_CHEMICAL from owner
-/obj/item/organ/internal/liver/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_SPECIES_HANDLE_CHEMICAL)
@@ -134,7 +134,7 @@
return
var/obj/belly = owner.get_organ_slot(ORGAN_SLOT_STOMACH)
- var/list/cached_reagents = owner.reagents.reagent_list
+ var/list/cached_reagents = owner.reagents?.reagent_list
var/liver_damage = 0
var/provide_pain_message = HAS_NO_TOXIN
@@ -142,7 +142,7 @@
for(var/datum/reagent/toxin/toxin in cached_reagents)
if(toxin.affected_organ_flags && !(organ_flags & toxin.affected_organ_flags)) //this particular toxin does not affect this type of organ
continue
- var/amount = round(toxin.volume, CHEMICAL_QUANTISATION_LEVEL) // this is an optimization
+ var/amount = toxin.volume
if(belly)
amount += belly.reagents.get_reagent_amount(toxin.type)
@@ -152,7 +152,7 @@
if(provide_pain_message != HAS_PAINFUL_TOXIN)
provide_pain_message = toxin.silent_toxin ? HAS_SILENT_TOXIN : HAS_PAINFUL_TOXIN
- owner.reagents.metabolize(owner, seconds_per_tick, times_fired, can_overdose = TRUE)
+ owner.reagents?.metabolize(owner, seconds_per_tick, times_fired, can_overdose = TRUE)
if(liver_damage)
apply_organ_damage(min(liver_damage * seconds_per_tick , MAX_TOXIN_LIVER_DAMAGE * seconds_per_tick))
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index 2e99f70d989..874ec320648 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -153,7 +153,7 @@
add_gas_reaction(/datum/gas/zauker, while_present = PROC_REF(too_much_zauker))
///Simply exists so that you don't keep any alerts from your previous lack of lungs.
-/obj/item/organ/internal/lungs/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/lungs/Insert(mob/living/carbon/receiver, special = FALSE, movement_flags)
. = ..()
if(!.)
return .
@@ -163,7 +163,7 @@
receiver.clear_alert(ALERT_NOT_ENOUGH_PLASMA)
receiver.clear_alert(ALERT_NOT_ENOUGH_N2O)
-/obj/item/organ/internal/lungs/Remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/lungs/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
// This is very "manual" I realize, but it's useful to ensure cleanup for gases we're removing happens
// Avoids stuck alerts and such
diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm
index 81047ce0d8b..e0e01fbd797 100644
--- a/code/modules/surgery/organs/internal/stomach/_stomach.dm
+++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm
@@ -55,7 +55,7 @@
var/mob/living/carbon/body = owner
// digest food, sent all reagents that can metabolize to the body
- for(var/datum/reagent/bit as anything in reagents.reagent_list)
+ for(var/datum/reagent/bit as anything in reagents?.reagent_list)
// If the reagent does not metabolize then it will sit in the stomach
// This has an effect on items like plastic causing them to take up space in the stomach
@@ -93,7 +93,7 @@
return
//We are checking if we have nutriment in a damaged stomach.
- var/datum/reagent/nutri = locate(/datum/reagent/consumable/nutriment) in reagents.reagent_list
+ var/datum/reagent/nutri = locate(/datum/reagent/consumable/nutriment) in reagents?.reagent_list
//No nutriment found lets exit out
if(!nutri)
return
@@ -263,7 +263,7 @@
disgusted.throw_alert(ALERT_DISGUST, /atom/movable/screen/alert/disgusted)
disgusted.add_mood_event("disgust", /datum/mood_event/disgusted)
-/obj/item/organ/internal/stomach/Remove(mob/living/carbon/stomach_owner, special = 0)
+/obj/item/organ/internal/stomach/Remove(mob/living/carbon/stomach_owner, special, movement_flags)
if(ishuman(stomach_owner))
var/mob/living/carbon/human/human_owner = owner
human_owner.clear_alert(ALERT_DISGUST)
diff --git a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm
index c1632f33329..68f9d9428a0 100644
--- a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm
+++ b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm
@@ -13,12 +13,12 @@
adjust_charge(-ETHEREAL_CHARGE_FACTOR * seconds_per_tick)
handle_charge(owner, seconds_per_tick, times_fired)
-/obj/item/organ/internal/stomach/ethereal/on_insert(mob/living/carbon/stomach_owner)
+/obj/item/organ/internal/stomach/ethereal/on_mob_insert(mob/living/carbon/stomach_owner)
. = ..()
RegisterSignal(stomach_owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(charge))
RegisterSignal(stomach_owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_electrocute))
-/obj/item/organ/internal/stomach/ethereal/on_remove(mob/living/carbon/stomach_owner)
+/obj/item/organ/internal/stomach/ethereal/on_mob_remove(mob/living/carbon/stomach_owner)
. = ..()
UnregisterSignal(stomach_owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT)
UnregisterSignal(stomach_owner, COMSIG_LIVING_ELECTROCUTE_ACT)
diff --git a/code/modules/surgery/organs/internal/stomach/stomach_golem.dm b/code/modules/surgery/organs/internal/stomach/stomach_golem.dm
index 5f721fa52b1..a1f5ce6c70e 100644
--- a/code/modules/surgery/organs/internal/stomach/stomach_golem.dm
+++ b/code/modules/surgery/organs/internal/stomach/stomach_golem.dm
@@ -11,11 +11,11 @@
/// How slow are you if you have absolutely nothing in the tank?
var/max_hunger_slowdown = 4
-/obj/item/organ/internal/stomach/golem/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/stomach/golem/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(owner, COMSIG_CARBON_ATTEMPT_EAT, PROC_REF(try_eating))
-/obj/item/organ/internal/stomach/golem/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/stomach/golem/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_CARBON_ATTEMPT_EAT)
organ_owner.remove_movespeed_modifier(/datum/movespeed_modifier/golem_hunger)
diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm
index 00f95c376e6..6bcc61ca050 100644
--- a/code/modules/surgery/organs/internal/tongue/_tongue.dm
+++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm
@@ -120,7 +120,7 @@
food_taste_reaction = FOOD_LIKED
return food_taste_reaction
-/obj/item/organ/internal/tongue/Insert(mob/living/carbon/tongue_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/tongue/Insert(mob/living/carbon/tongue_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -137,7 +137,7 @@
ADD_TRAIT(tongue_owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
apply_tongue_effects()
-/obj/item/organ/internal/tongue/Remove(mob/living/carbon/tongue_owner, special = FALSE)
+/obj/item/organ/internal/tongue/Remove(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
temp_say_mod = ""
UnregisterSignal(tongue_owner, COMSIG_MOB_SAY)
diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm
new file mode 100644
index 00000000000..073ef4b1137
--- /dev/null
+++ b/code/modules/surgery/organs/organ_movement.dm
@@ -0,0 +1,232 @@
+// There are two kinds of organ movement: mob movement and limb movement
+// If you pull someones brain out, you remove it from the mob and the limb
+// If you take someones head off, you remove it from the mob but not the limb
+// If you remove the brain from an already decapitated head, you remove it from the limb but not the mob
+
+// Keep the seperation of limb removal and mob removal absolute
+
+/*
+ * Insert the organ into the select mob.
+ *
+ * receiver - the mob who will get our organ
+ * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ * movement_flags - Flags for how we behave in movement. See DEFINES/organ_movement for flags
+ */
+/obj/item/organ/proc/Insert(mob/living/carbon/receiver, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ mob_insert(receiver, special, movement_flags)
+ bodypart_insert(limb_owner = receiver, movement_flags = movement_flags)
+
+ return TRUE
+
+/*
+ * Remove the organ from the select mob.
+ *
+ * * organ_owner - the mob who owns our organ, that we're removing the organ from. Can be null
+ * * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ */
+/obj/item/organ/proc/Remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ mob_remove(organ_owner, special, movement_flags)
+ bodypart_remove(limb_owner = organ_owner, movement_flags = movement_flags)
+
+/*
+ * Insert the organ into the select mob.
+ *
+ * receiver - the mob who will get our organ
+ * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ * movement_flags - Flags for how we behave in movement. See DEFINES/organ_movement for flags
+ */
+/obj/item/organ/proc/mob_insert(mob/living/carbon/receiver, special, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!iscarbon(receiver))
+ stack_trace("Tried to insert organ into non-carbon: [receiver.type]")
+ return
+
+ if(owner == receiver)
+ stack_trace("Organ receiver is already organ owner")
+ return
+
+ var/obj/item/organ/replaced = receiver.get_organ_slot(slot)
+ if(replaced)
+ replaced.Remove(receiver, special = TRUE)
+ if(movement_flags & DELETE_IF_REPLACED)
+ qdel(replaced)
+ else
+ replaced.forceMove(get_turf(receiver))
+
+ if(!IS_ROBOTIC_ORGAN(src) && (organ_flags & ORGAN_VIRGIN))
+ blood_dna_info = receiver.get_blood_dna_list()
+ // need to remove the synethic blood DNA that is initialized
+ // wash also adds the blood dna again
+ wash(CLEAN_TYPE_BLOOD)
+ organ_flags &= ~ORGAN_VIRGIN
+
+ receiver.organs |= src
+ receiver.organs_slot[slot] = src
+ owner = receiver
+
+ on_mob_insert(receiver, special)
+
+ return TRUE
+
+/// Called after the organ is inserted into a mob.
+/// Adds Traits, Actions, and Status Effects on the mob in which the organ is impanted.
+/// Override this proc to create unique side-effects for inserting your organ. Must be called by overrides.
+/obj/item/organ/proc/on_mob_insert(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ for(var/trait in organ_traits)
+ ADD_TRAIT(organ_owner, trait, REF(src))
+
+ for(var/datum/action/action as anything in actions)
+ action.Grant(organ_owner)
+
+ for(var/datum/status_effect/effect as anything in organ_effects)
+ organ_owner.apply_status_effect(effect, type)
+
+ RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_owner_examine))
+ SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, organ_owner)
+ SEND_SIGNAL(organ_owner, COMSIG_CARBON_GAIN_ORGAN, src, special)
+
+/// Insert an organ into a limb, assume the limb as always detached and include no owner operations here (except the get_bodypart helper here I guess)
+/// Give EITHER a limb OR a limb owner
+/obj/item/organ/proc/bodypart_insert(obj/item/bodypart/bodypart, mob/living/carbon/limb_owner, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(limb_owner)
+ bodypart = limb_owner.get_bodypart(deprecise_zone(zone))
+
+ // The true movement
+ forceMove(bodypart)
+ bodypart.contents |= src
+ bodypart_owner = bodypart
+
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(forced_removal))
+
+ // Apply unique side-effects. Return value does not matter.
+ on_bodypart_insert(bodypart)
+
+ return TRUE
+
+/// Add any limb specific effects you might want here
+/obj/item/organ/proc/on_bodypart_insert(obj/item/bodypart/limb, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ item_flags |= ABSTRACT
+ ADD_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
+ interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
+
+/*
+ * Remove the organ from the select mob.
+ *
+ * * organ_owner - the mob who owns our organ, that we're removing the organ from. Can be null
+ * * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ */
+/obj/item/organ/proc/mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(organ_owner)
+ if(organ_owner.organs_slot[slot] == src)
+ organ_owner.organs_slot.Remove(slot)
+ organ_owner.organs -= src
+
+ owner = null
+
+ on_mob_remove(organ_owner, special)
+
+ return TRUE
+
+/// Called after the organ is removed from a mob.
+/// Removes Traits, Actions, and Status Effects on the mob in which the organ was impanted.
+/// Override this proc to create unique side-effects for removing your organ. Must be called by overrides.
+/obj/item/organ/proc/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!iscarbon(organ_owner))
+ stack_trace("Organ removal should not be happening on non carbon mobs: [organ_owner]")
+
+ for(var/trait in organ_traits)
+ REMOVE_TRAIT(organ_owner, trait, REF(src))
+
+ for(var/datum/action/action as anything in actions)
+ action.Remove(organ_owner)
+
+ for(var/datum/status_effect/effect as anything in organ_effects)
+ organ_owner.remove_status_effect(effect, type)
+
+ UnregisterSignal(organ_owner, COMSIG_ATOM_EXAMINE)
+ SEND_SIGNAL(src, COMSIG_ORGAN_REMOVED, organ_owner)
+ SEND_SIGNAL(organ_owner, COMSIG_CARBON_LOSE_ORGAN, src, special)
+
+ var/list/diseases = organ_owner.get_static_viruses()
+ if(!LAZYLEN(diseases))
+ return
+
+ var/list/datum/disease/diseases_to_add = list()
+ for(var/datum/disease/disease as anything in diseases)
+ // robotic organs are immune to disease unless 'inorganic biology' symptom is present
+ if(IS_ROBOTIC_ORGAN(src) && !(disease.infectable_biotypes & MOB_ROBOTIC))
+ continue
+
+ // admin or special viruses that should not be reproduced
+ if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
+ continue
+
+ diseases_to_add += disease
+
+ if(LAZYLEN(diseases_to_add))
+ AddComponent(/datum/component/infective, diseases_to_add)
+
+/// Called to remove an organ from a limb. Do not put any mob operations here (except the bodypart_getter at the start)
+/// Give EITHER a limb OR a limb_owner
+/obj/item/organ/proc/bodypart_remove(obj/item/bodypart/limb, mob/living/carbon/limb_owner, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!isnull(limb_owner))
+ limb = limb_owner.get_bodypart(deprecise_zone(zone))
+
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED) //DONT MOVE THIS!!!! we moves the organ right after, so we unregister before we move them physically
+
+ // The true movement is here
+ moveToNullspace()
+ bodypart_owner.contents -= src
+ bodypart_owner = null
+
+ on_bodypart_remove(limb)
+
+ return TRUE
+
+/// Called on limb removal to remove limb specific limb effects or statusses
+/obj/item/organ/proc/on_bodypart_remove(obj/item/bodypart/limb, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!IS_ROBOTIC_ORGAN(src) && !(item_flags & NO_BLOOD_ON_ITEM) && !QDELING(src))
+ AddElement(/datum/element/decal/blood)
+
+ item_flags &= ~ABSTRACT
+ REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
+ interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
+
+/// In space station videogame, nothing is sacred. If somehow an organ is removed unexpectedly, handle it properly
+/obj/item/organ/proc/forced_removal()
+ SIGNAL_HANDLER
+
+ if(owner)
+ Remove(owner)
+ else if(bodypart_owner)
+ bodypart_remove(bodypart_owner)
+ else
+ stack_trace("Force removed an already removed organ!")
+
+/**
+ * Proc that gets called when the organ is surgically removed by someone, can be used for special effects
+ * Currently only used so surplus organs can explode when surgically removed.
+ */
+/obj/item/organ/proc/on_surgical_removal(mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, old_owner, target_zone, tool)
+ RemoveElement(/datum/element/decal/blood)
diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm
index 49539920920..6d6505d1dc5 100644
--- a/code/modules/tooltip/tooltip.dm
+++ b/code/modules/tooltip/tooltip.dm
@@ -107,22 +107,22 @@ Notes:
//Open a tooltip for user, at a location based on params
//Theme is a CSS class in tooltip.html, by default this wrapper chooses a CSS class based on the user's UI_style (Midnight, Plasmafire, Retro, etc)
//Includes sanity.checks
-/proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null,title = "",content = "",theme = "")
- if(istype(user))
- if(user.client && user.client.tooltips)
- var/ui_style = user.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)
- if(!theme && ui_style)
- theme = lowertext(ui_style)
- if(!theme)
- theme = "default"
- user.client.tooltips.show(tip_src, params,title,content,theme)
+/proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null, title = "", content = "", theme = "")
+ if(!istype(user) || !user.client?.tooltips)
+ return
+ var/ui_style = user.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)
+ if(!theme && ui_style)
+ theme = lowertext(ui_style)
+ if(!theme)
+ theme = "default"
+ user.client.tooltips.show(tip_src, params, title, content, theme)
//Arbitrarily close a user's tooltip
//Includes sanity checks.
/proc/closeToolTip(mob/user)
- if(istype(user))
- if(user.client && user.client.tooltips)
- user.client.tooltips.hide()
+ if(!istype(user) || !user.client?.tooltips)
+ return
+ user.client.tooltips.hide()
diff --git a/code/modules/transport/tram/tram_controller.dm b/code/modules/transport/tram/tram_controller.dm
index 26b79af2de4..79348c4d2a6 100644
--- a/code/modules/transport/tram/tram_controller.dm
+++ b/code/modules/transport/tram/tram_controller.dm
@@ -697,7 +697,7 @@
/obj/machinery/transport/tram_controller/hilbert
configured_transport_id = HILBERT_LINE_1
- obj_flags = NO_DECONSTRUCTION
+ obj_flags = /obj::obj_flags | NO_DECONSTRUCTION
/obj/machinery/transport/tram_controller/Initialize(mapload)
. = ..()
diff --git a/code/modules/transport/tram/tram_signals.dm b/code/modules/transport/tram/tram_signals.dm
index 53fcd97cffe..9e297e8e829 100644
--- a/code/modules/transport/tram/tram_signals.dm
+++ b/code/modules/transport/tram/tram_signals.dm
@@ -5,7 +5,6 @@
icon = 'icons/obj/tram/crossing_signal.dmi'
icon_state = "crossing-inbound"
base_icon_state = "crossing-inbound"
- plane = GAME_PLANE_UPPER
layer = TRAM_SIGNAL_LAYER
max_integrity = 250
integrity_failure = 0.25
@@ -72,7 +71,7 @@
desc = "Indicates to pedestrians if it's safe to cross the tracks."
icon = 'icons/obj/tram/crossing_signal.dmi'
icon_state = "crossing-inbound"
- plane = GAME_PLANE_UPPER
+ layer = TRAM_SIGNAL_LAYER
max_integrity = 250
integrity_failure = 0.25
idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 2.4
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 56c10327441..22f5ef72934 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -195,6 +195,7 @@
#include "objectives.dm"
#include "operating_table.dm"
#include "orderable_items.dm"
+#include "organ_bodypart_shuffle.dm"
#include "organ_set_bonus.dm"
#include "organs.dm"
#include "outfit_sanity.dm"
@@ -275,6 +276,7 @@
#include "worn_icons.dm"
// SKYRAT EDIT START
#include "~skyrat\automapper.dm"
+#include "~skyrat\loadout_dupes.dm"
#include "~skyrat\opposing_force.dm"
// SKYRAT EDIT END
// END_INCLUDE
diff --git a/code/modules/unit_tests/heretic_rituals.dm b/code/modules/unit_tests/heretic_rituals.dm
index 4ac5bce8d3d..b55136cface 100644
--- a/code/modules/unit_tests/heretic_rituals.dm
+++ b/code/modules/unit_tests/heretic_rituals.dm
@@ -66,7 +66,10 @@
if(islist(ritual_item_path))
ritual_item_path = pick(ritual_item_path)
for(var/i in 1 to amount_to_create)
- created_atoms += new ritual_item_path(get_turf(our_heretic))
+ var/obj/item/item = new ritual_item_path(get_turf(our_heretic))
+ if(isitem(item))
+ item.item_flags &= ~ABSTRACT
+ created_atoms += item
// Now, we can ACTUALLY run the ritual. Let's do it.
// Attempt to run the knowledge via the sacrifice rune.
@@ -106,6 +109,10 @@
for(var/atom/thing as anything in nearby_atoms)
if(!ismovable(thing))
continue
+ if(isitem(thing))
+ var/obj/item/item = thing
+ if(item.item_flags & ABSTRACT) //bodyparts and stuff will get registered otherwise
+ continue
// There are atoms around the rune still, and there shouldn't be.
// All component atoms were consumed, and all resulting atoms were cleaned up.
diff --git a/code/modules/unit_tests/human_through_recycler.dm b/code/modules/unit_tests/human_through_recycler.dm
index 7d554d72690..c51b9a0e30b 100644
--- a/code/modules/unit_tests/human_through_recycler.dm
+++ b/code/modules/unit_tests/human_through_recycler.dm
@@ -17,8 +17,13 @@
TEST_ASSERT_EQUAL(damage_incurred, chewer.crush_damage, "Assistant did not take the expected amount of brute damage ([chewer.crush_damage]) from the emagged recycler! Took ([damage_incurred]) instead.")
TEST_ASSERT(chewer.bloody, "The emagged recycler did not become bloody after crushing the assistant!")
+ var/list/bad_contents = assistant.contents
+ for(var/obj/item/item in assistant.contents)
+ if(item.item_flags & ABSTRACT)
+ bad_contents -= item
+
// Now, let's test to see if all of their clothing got properly deleted.
- TEST_ASSERT_EQUAL(length(assistant.contents), 0, "Assistant still has items in its contents after being put through an emagged recycler!")
+ TEST_ASSERT_EQUAL(length(bad_contents), 0, "Assistant still has items in its contents after being put through an emagged recycler!")
// Consistent Assistants will always have the following: ID, PDA, backpack, a uniform, a headset, and a pair of shoes. If any of these are still present, then the recycler did not properly delete the assistant's clothing.
// However, let's check for EVERYTHING just in case, because we don't want to miss anything.
// This is just what we expect to be deleted.
diff --git a/code/modules/unit_tests/ling_decap.dm b/code/modules/unit_tests/ling_decap.dm
index 0c964f9a043..5d6f01caf9a 100644
--- a/code/modules/unit_tests/ling_decap.dm
+++ b/code/modules/unit_tests/ling_decap.dm
@@ -9,7 +9,7 @@
var/obj/item/bodypart/head/noggin = ling.get_bodypart(BODY_ZONE_HEAD)
noggin.dismember()
TEST_ASSERT_NULL(ling.get_bodypart(BODY_ZONE_HEAD), "Changeling failed to be decapitated.")
- TEST_ASSERT_NULL(noggin.brainmob.mind, "Changeling's mind was moved to their head after decapitation, but it should have remained in their body.")
+ TEST_ASSERT_NULL(noggin.brain.brainmob.mind, "Changeling's mind was moved to their brain after decapitation, but it should have remained in their body.")
var/obj/item/organ/internal/brain/oldbrain = noggin.brain
noggin.drop_organs()
@@ -33,7 +33,7 @@
var/obj/item/bodypart/head/noggin = normal_guy.get_bodypart(BODY_ZONE_HEAD)
noggin.dismember()
- TEST_ASSERT_EQUAL(noggin.brainmob.mind, my_guys_mind, "Dummy's mind was not moved to their head after decapitation.")
+ TEST_ASSERT_EQUAL(noggin.brain.brainmob.mind, my_guys_mind, "Dummy's mind was not moved to their brain after decapitation.")
var/obj/item/organ/internal/brain/oldbrain = noggin.brain
noggin.drop_organs()
diff --git a/code/modules/unit_tests/organ_bodypart_shuffle.dm b/code/modules/unit_tests/organ_bodypart_shuffle.dm
new file mode 100644
index 00000000000..842dd1c6c13
--- /dev/null
+++ b/code/modules/unit_tests/organ_bodypart_shuffle.dm
@@ -0,0 +1,34 @@
+/// Moves organs in and out of bodyparts, and moves the bodyparts around to see if someone didn't fuck up their movement
+/datum/unit_test/organ_bodypart_shuffle
+
+/datum/unit_test/organ_bodypart_shuffle/Run()
+ var/mob/living/carbon/human/hollow_boy = allocate(/mob/living/carbon/human/consistent)
+
+ // Test if organs are all properly updating when forcefully removed
+ var/list/removed_organs = list()
+
+ for(var/obj/item/organ/organ as anything in hollow_boy.organs)
+ organ.moveToNullspace()
+ removed_organs += organ
+
+ for(var/obj/item/organ/organ as anything in removed_organs)
+ TEST_ASSERT(!(organ in hollow_boy.organs), "Organ '[organ.name] remained inside human after forceMove into nullspace.")
+ TEST_ASSERT(organ.loc == null, "Organ '[organ.name] did not move to nullspace after being forced to.")
+ TEST_ASSERT(!(organ.owner), "Organ '[organ.name] kept reference to human after forceMove into nullspace.")
+ TEST_ASSERT(!(organ.bodypart_owner), "Organ '[organ.name] kept reference to bodypart after forceMove into nullspace.")
+
+ for(var/obj/item/bodypart/bodypart as anything in hollow_boy.bodyparts)
+ bodypart = new bodypart.type() //fresh, duplice bodypart with no insides
+ for(var/obj/item/organ/organ as anything in removed_organs)
+ if(bodypart.body_zone != deprecise_zone(organ.zone))
+ continue
+ organ.bodypart_insert(bodypart) // Put all the old organs back in
+ bodypart.replace_limb(hollow_boy) //so stick new bodyparts on them with their old organs
+ // Check if, after we put the old organs in a new limb, and after we put that new limb on the mob, if the organs came with
+ for(var/obj/item/organ/organ as anything in removed_organs) //technically readded organ now
+ if(bodypart.body_zone != deprecise_zone(organ.zone))
+ continue
+ TEST_ASSERT(organ in hollow_boy.organs, "Organ '[organ.name] was put in an empty bodypart that replaced a humans, but the organ did not come with.")
+
+ // Test if bodyparts are all properly updating when forcefully removed
+ hollow_boy = allocate(/mob/living/carbon/human/consistent) //freshly filled with wet insides
diff --git a/code/modules/unit_tests/organ_set_bonus.dm b/code/modules/unit_tests/organ_set_bonus.dm
index a5a31fc6bb4..b1a314ccda7 100644
--- a/code/modules/unit_tests/organ_set_bonus.dm
+++ b/code/modules/unit_tests/organ_set_bonus.dm
@@ -31,7 +31,7 @@
// Attempt to insert entire list of mutant organs for the given infusion_entry.
for(var/obj/item/organ/organ as anything in output_organs)
organ = new organ()
- TEST_ASSERT(organ.Insert(lab_rat, special = TRUE, drop_if_replaced = FALSE), "The organ `[organ.type]` for `[infuser_entry.type]` was not inserted in the mob when expected, Insert() returned falsy when TRUE was expected.")
+ TEST_ASSERT(organ.Insert(lab_rat, special = TRUE, movement_flags = DELETE_IF_REPLACED), "The organ `[organ.type]` for `[infuser_entry.type]` was not inserted in the mob when expected, Insert() returned falsy when TRUE was expected.")
inserted_organs += organ
// Search for added Status Effect.
diff --git a/code/modules/unit_tests/organs.dm b/code/modules/unit_tests/organs.dm
index 4ba51e0870c..1da3808ba39 100644
--- a/code/modules/unit_tests/organs.dm
+++ b/code/modules/unit_tests/organs.dm
@@ -25,14 +25,10 @@
))
/datum/unit_test/organ_sanity/Run()
- for(var/obj/item/organ/organ_type as anything in subtypesof(/obj/item/organ))
+ for(var/obj/item/organ/organ_type as anything in subtypesof(/obj/item/organ) - test_organ_blacklist)
organ_test_insert(organ_type)
/datum/unit_test/organ_sanity/proc/organ_test_insert(obj/item/organ/organ_type)
- // Skip prototypes.
- if(test_organ_blacklist[organ_type])
- return
-
// Appropriate mob (Human) which will receive organ.
var/mob/living/carbon/human/lab_rat = allocate(/mob/living/carbon/human/consistent)
var/obj/item/organ/test_organ = new organ_type()
@@ -41,8 +37,8 @@
var/mob/living/basic/pet/dog/lab_dog = allocate(/mob/living/basic/pet/dog/corgi)
var/obj/item/organ/reject_organ = new organ_type()
- TEST_ASSERT(test_organ.Insert(lab_rat, special = TRUE, drop_if_replaced = FALSE), TEST_ORGAN_INSERT_MESSAGE(test_organ, "should return TRUE to indicate success."))
- TEST_ASSERT(!reject_organ.Insert(lab_dog, special = TRUE, drop_if_replaced = FALSE), TEST_ORGAN_INSERT_MESSAGE(test_organ, "shouldn't return TRUE when inserting into a basic mob (Corgi)."))
+ TEST_ASSERT(test_organ.Insert(lab_rat, special = TRUE, movement_flags = DELETE_IF_REPLACED), TEST_ORGAN_INSERT_MESSAGE(test_organ, "should return TRUE to indicate success."))
+ TEST_ASSERT(!reject_organ.Insert(lab_dog, special = TRUE, movement_flags = DELETE_IF_REPLACED), TEST_ORGAN_INSERT_MESSAGE(test_organ, "shouldn't return TRUE when inserting into a basic mob (Corgi)."))
// Species change swaps out all the organs, making test_organ un-usable by this point.
if(species_changing_organs[test_organ.type])
diff --git a/code/modules/unit_tests/species_change_organs.dm b/code/modules/unit_tests/species_change_organs.dm
index e0555aa89bc..41d55047f03 100644
--- a/code/modules/unit_tests/species_change_organs.dm
+++ b/code/modules/unit_tests/species_change_organs.dm
@@ -12,7 +12,7 @@
dummy.gain_trauma(/datum/brain_trauma/severe/blindness)
// Give a cyber heart
var/obj/item/organ/internal/heart/cybernetic/cyber_heart = allocate(/obj/item/organ/internal/heart/cybernetic)
- cyber_heart.Insert(dummy, special = TRUE, drop_if_replaced = FALSE)
+ cyber_heart.Insert(dummy, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// Give one of their organs a bit of damage
var/obj/item/organ/internal/appendix/existing_appendix = dummy.get_organ_slot(ORGAN_SLOT_APPENDIX)
existing_appendix.set_organ_damage(25)
diff --git a/code/modules/unit_tests/station_trait_tests.dm b/code/modules/unit_tests/station_trait_tests.dm
index 71886d3c0c2..d3daac1acca 100644
--- a/code/modules/unit_tests/station_trait_tests.dm
+++ b/code/modules/unit_tests/station_trait_tests.dm
@@ -7,5 +7,7 @@
for(var/datum/job/job as anything in subtypesof(/datum/job))
if(!(initial(job.job_flags) & JOB_CREW_MEMBER))
continue
- if(!(job in cyber_trait.job_to_cybernetic)) // BUBBER EDIT - I don't CARE we don't USE THIS.
- TEST_FAIL("Job [job] does not have an assigned cybernetic for [cyber_trait.type] station trait.") */
+ if((initial(job.job_flags) & STATION_TRAIT_JOB_FLAGS) == STATION_TRAIT_JOB_FLAGS)
+ continue
+ if(!(job in cyber_trait.job_to_cybernetic))
+ TEST_FAIL("Job [job] does not have an assigned cybernetic for [cyber_trait.type] station trait.")*/
diff --git a/code/modules/unit_tests/tail_wag.dm b/code/modules/unit_tests/tail_wag.dm
index 0d828557953..ceb82e98c0d 100644
--- a/code/modules/unit_tests/tail_wag.dm
+++ b/code/modules/unit_tests/tail_wag.dm
@@ -6,7 +6,7 @@
/datum/unit_test/tail_wag/Run()
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
var/obj/item/organ/external/tail/cat/dummy_tail = allocate(/obj/item/organ/external/tail/cat)
- dummy_tail.Insert(dummy, special = TRUE, drop_if_replaced = FALSE)
+ dummy_tail.Insert(dummy, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// SANITY TEST
@@ -74,7 +74,7 @@
// TESTING MOB DEATH
// put it back and start wagging again
- dummy_tail.Insert(dummy, special = TRUE, drop_if_replaced = FALSE)
+ dummy_tail.Insert(dummy, special = TRUE, movement_flags = DELETE_IF_REPLACED)
SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE)
if(!(dummy_tail.wag_flags & WAG_WAGGING))
TEST_FAIL("Tail did not start wagging when it should have!")
diff --git a/code/modules/unit_tests/~skyrat/loadout_dupes.dm b/code/modules/unit_tests/~skyrat/loadout_dupes.dm
new file mode 100644
index 00000000000..23bfe368905
--- /dev/null
+++ b/code/modules/unit_tests/~skyrat/loadout_dupes.dm
@@ -0,0 +1,14 @@
+///Checks that loadout items' item_paths are unique to the category of loadout item it belongs to
+/datum/unit_test/loadout_dupes
+
+/datum/unit_test/loadout_dupes/Run()
+ var/list/item_paths = list()
+ for(var/datum/loadout_item/item as anything in subtypesof(/datum/loadout_item))
+ var/loadout_item_category = initial(item.category)
+ var/item_path = initial(item.item_path)
+ if(item_paths[item_path] == loadout_item_category)
+ TEST_FAIL("Duplicate loadout item! [item_path] is already being used by [item].")
+ if(isnull(item_path)) // can be null, for subcategories of loadout items
+ continue
+
+ item_paths[item_path] = loadout_item_category
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 2c060b6ff98..32783504fe8 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -137,6 +137,10 @@
to_chat(user, span_boldnotice("[A] materializes onto the floor!"))
return A
+///For special overrides if an item can be bought or not.
+/datum/uplink_item/proc/can_be_bought(datum/uplink_handler/source)
+ return TRUE
+
/datum/uplink_category/discounts
name = "Discounted Gear"
weight = -1
diff --git a/code/modules/uplink/uplink_items/contractor.dm b/code/modules/uplink/uplink_items/contractor.dm
new file mode 100644
index 00000000000..1364f8b088b
--- /dev/null
+++ b/code/modules/uplink/uplink_items/contractor.dm
@@ -0,0 +1,109 @@
+/datum/uplink_category/contractor
+ name = "Contractor"
+ weight = 10
+
+/datum/uplink_item/bundles_tc/contract_kit
+ name = "Contract Kit"
+ desc = "The Syndicate have offered you the chance to become a contractor, take on kidnapping contracts for TC \
+ and cash payouts. Upon purchase, you'll be granted your own contract uplink embedded within the supplied \
+ tablet computer. Additionally, you'll be granted standard contractor gear to help with your mission - \
+ comes supplied with the tablet, specialised space suit, chameleon jumpsuit and mask, agent card, \
+ specialised contractor baton, and three randomly selected low cost items. \
+ Can include otherwise unobtainable items."
+ item = /obj/item/storage/box/syndicate/contract_kit
+ category = /datum/uplink_category/contractor
+ cost = 20
+ purchasable_from = UPLINK_INFILTRATORS
+
+/datum/uplink_item/bundles_tc/contract_kit/purchase(mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
+ . = ..()
+ for(var/uplink_items in subtypesof(/datum/uplink_item/contractor))
+ var/datum/uplink_item/uplink_item = new uplink_items
+ uplink_handler.extra_purchasable += uplink_item
+
+/datum/uplink_item/contractor
+ restricted = TRUE
+ category = /datum/uplink_category/contractor
+ purchasable_from = NONE //they will be added to extra_purchasable
+
+//prevents buying contractor stuff before you make an account.
+/datum/uplink_item/contractor/can_be_bought(datum/uplink_handler/uplink_handler)
+ if(!uplink_handler.contractor_hub)
+ return FALSE
+ return ..()
+
+/datum/uplink_item/contractor/reroll
+ name = "Contract Reroll"
+ desc = "Request a reroll of your current contract list. Will generate a new target, \
+ payment, and dropoff for the contracts you currently have available."
+ item = /obj/effect/gibspawner/generic
+ limited_stock = 2
+ cost = 0
+
+/datum/uplink_item/contractor/reroll/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
+ //We're not regenerating already completed/aborted/extracting contracts, but we don't want to repeat their targets.
+ var/list/new_target_list = list()
+ for(var/datum/syndicate_contract/contract_check in uplink_handler.contractor_hub.assigned_contracts)
+ if (contract_check.status != CONTRACT_STATUS_ACTIVE && contract_check.status != CONTRACT_STATUS_INACTIVE)
+ if (contract_check.contract.target)
+ new_target_list.Add(contract_check.contract.target)
+ continue
+
+ //Reroll contracts without duplicates
+ for(var/datum/syndicate_contract/rerolling_contract in uplink_handler.contractor_hub.assigned_contracts)
+ if (rerolling_contract.status != CONTRACT_STATUS_ACTIVE && rerolling_contract.status != CONTRACT_STATUS_INACTIVE)
+ continue
+
+ rerolling_contract.generate(new_target_list)
+ new_target_list.Add(rerolling_contract.contract.target)
+
+ //Set our target list with the new set we've generated.
+ uplink_handler.contractor_hub.assigned_targets = new_target_list
+ return source //for log icon
+
+/datum/uplink_item/contractor/pinpointer
+ name = "Contractor Pinpointer"
+ desc = "A pinpointer that finds targets even without active suit sensors. \
+ Due to taking advantage of an exploit within the system, it can't pinpoint \
+ to the same accuracy as the traditional models. \
+ Becomes permanently locked to the user that first activates it."
+ item = /obj/item/pinpointer/crew/contractor
+ limited_stock = 2
+ cost = 1
+
+/datum/uplink_item/contractor/extraction_kit
+ name = "Fulton Extraction Kit"
+ desc = "For getting your target across the station to those difficult dropoffs. \
+ Place the beacon somewhere secure, and link the pack. \
+ Activating the pack on your target will send them over to the beacon - \
+ make sure they're not just going to run away though!"
+ item = /obj/item/storage/box/contractor/fulton_extraction
+ limited_stock = 1
+ cost = 1
+
+/datum/uplink_item/contractor/partner
+ name = "Reinforcements"
+ desc = "Upon purchase we'll contact available units in the area. Should there be an agent free, \
+ we'll send them down to assist you immediately. If no units are free, we give a full refund."
+ item = /obj/effect/gibspawner/generic
+ limited_stock = 1
+ cost = 2
+
+/datum/uplink_item/contractor/partner/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
+ to_chat(user, span_notice("The uplink vibrates quietly, connecting to nearby agents..."))
+ var/list/candidates = SSpolling.poll_ghost_candidates(
+ question = "Do you want to play as the Contractor Support Unit for [user.real_name]?",
+ check_jobban = ROLE_TRAITOR,
+ role = ROLE_TRAITOR,
+ poll_time = 10 SECONDS,
+ ignore_category = POLL_IGNORE_CONTRACTOR_SUPPORT,
+ pic_source = /obj/item/modular_computer/pda/syndicate_contract_uplink,
+ role_name_text = "contractor support unit",
+ )
+ if(!LAZYLEN(candidates))
+ to_chat(user, span_notice("No available agents at this time, please try again later."))
+ limited_stock++
+ return //bobux no icon
+ var/mob/dead/observer/selected_player = pick(candidates)
+ uplink_handler.contractor_hub.contractor_teammate = spawn_contractor_partner(user, selected_player.key)
+ return source //for log icon
diff --git a/code/modules/uplink/uplink_items/stealthy.dm b/code/modules/uplink/uplink_items/stealthy.dm
index 36cb47cc28f..6ba88eda43e 100644
--- a/code/modules/uplink/uplink_items/stealthy.dm
+++ b/code/modules/uplink/uplink_items/stealthy.dm
@@ -101,4 +101,5 @@
item = /obj/item/melee/baton/telescopic/contractor_baton
cost = 12
surplus = 50
+ limited_stock = 1
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index fe79feff66b..505e9be772b 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -6,7 +6,6 @@
max_integrity = 300
armor_type = /datum/armor/obj_vehicle
layer = VEHICLE_LAYER
- plane = GAME_PLANE // SKYRAT EDIT - ORIGINAL: plane = GAME_PLANE_FOV_HIDDEN
density = TRUE
anchored = FALSE
blocks_emissive = EMISSIVE_BLOCK_GENERIC
diff --git a/code/modules/vehicles/atv.dm b/code/modules/vehicles/atv.dm
index b72449de091..aa9a963a101 100644
--- a/code/modules/vehicles/atv.dm
+++ b/code/modules/vehicles/atv.dm
@@ -59,22 +59,18 @@
turret.pixel_x = base_pixel_x
turret.pixel_y = base_pixel_y + 4
turret.layer = ABOVE_MOB_LAYER
- SET_PLANE(turret, GAME_PLANE_UPPER, our_turf)
if(EAST)
turret.pixel_x = base_pixel_x - 12
turret.pixel_y = base_pixel_y + 4
turret.layer = OBJ_LAYER
- SET_PLANE(turret, GAME_PLANE, our_turf)
if(SOUTH)
turret.pixel_x = base_pixel_x
turret.pixel_y = base_pixel_y + 4
turret.layer = OBJ_LAYER
- SET_PLANE(turret, GAME_PLANE, our_turf)
if(WEST)
turret.pixel_x = base_pixel_x + 12
turret.pixel_y = base_pixel_y + 4
turret.layer = OBJ_LAYER
- SET_PLANE(turret, GAME_PLANE, our_turf)
/obj/vehicle/ridden/atv/welder_act(mob/living/user, obj/item/W)
if(user.combat_mode)
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 5dcb5e64e06..208bd8d68c2 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -576,7 +576,7 @@
if(!enclosed && occupant?.incapacitated()) //no sides mean it's easy to just sorta fall out if you're incapacitated.
mob_exit(occupant, randomstep = TRUE) //bye bye
continue
- if(cell)
+ if(cell && cell.maxcharge)
var/cellcharge = cell.charge/cell.maxcharge
switch(cellcharge)
if(0.75 to INFINITY)
@@ -589,6 +589,8 @@
occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/lowcell/mech, 3)
else
occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/emptycell/mech)
+ else
+ occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/nocell)
var/integrity = atom_integrity/max_integrity*100
switch(integrity)
if(30 to 45)
diff --git a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
index 1aa4223b98c..c784219e3c3 100644
--- a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
+++ b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
@@ -147,7 +147,6 @@
chassis.movedelay = 1
chassis.density = FALSE
chassis.layer = ABOVE_ALL_MOB_LAYER
- SET_PLANE(chassis, GAME_PLANE_UPPER_FOV_HIDDEN, launch_turf)
animate(chassis, alpha = 0, time = 8, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL)
animate(chassis, pixel_z = 400, time = 10, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL) //Animate our rising mech (just like pods hehe)
addtimer(CALLBACK(src, PROC_REF(begin_landing)), 2 SECONDS)
diff --git a/code/modules/vehicles/mecha/mecha_ai_interaction.dm b/code/modules/vehicles/mecha/mecha_ai_interaction.dm
index 22db0771809..9ae35d8ff4b 100644
--- a/code/modules/vehicles/mecha/mecha_ai_interaction.dm
+++ b/code/modules/vehicles/mecha/mecha_ai_interaction.dm
@@ -72,7 +72,6 @@
to_chat(occupants, span_danger("You have been forcibly ejected!"))
for(var/ejectee in occupants)
mob_exit(ejectee, silent = TRUE, randomstep = TRUE, forced = TRUE) //IT IS MINE, NOW. SUCK IT, RD!
- AI.can_shunt = FALSE //ONE AI ENTERS. NO AI LEAVES.
if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech.
AI = card.AI
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index a6177c0a77d..1b4f81489a3 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -746,7 +746,6 @@
tilted = TRUE
tilted_rotation = picked_rotation
layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
if(get_turf(fatty) != get_turf(src))
throw_at(get_turf(fatty), 1, 1, spin = FALSE, quickstart = FALSE)
@@ -1016,7 +1015,6 @@
tilted = FALSE
layer = initial(layer)
- SET_PLANE_IMPLICIT(src, initial(plane))
var/matrix/to_turn = turn(transform, -tilted_rotation)
animate(src, transform = to_turn, 0.2 SECONDS)
@@ -1041,10 +1039,10 @@
LAZYADD(product_datum.returned_products, inserted_item)
return
- if(vending_machine_input[format_text(inserted_item.name)])
- vending_machine_input[format_text(inserted_item.name)]++
+ if(vending_machine_input[inserted_item.type])
+ vending_machine_input[inserted_item.type]++
else
- vending_machine_input[format_text(inserted_item.name)] = 1
+ vending_machine_input[inserted_item.type] = 1
loaded_items++
/obj/machinery/vending/unbuckle_mob(mob/living/buckled_mob, force = FALSE, can_fall = TRUE)
@@ -1212,7 +1210,7 @@
colorable = product_record.colorable,
)
- .["stock"][product_record.name] = product_data
+ .["stock"] += list(product_data)
.["extended_inventory"] = extended_inventory
@@ -1601,12 +1599,12 @@
. = ..()
.["access"] = compartmentLoadAccessCheck(user)
.["vending_machine_input"] = list()
- for (var/stocked_item in vending_machine_input)
+ for (var/obj/item/stocked_item as anything in vending_machine_input)
if(vending_machine_input[stocked_item] > 0)
var/base64
var/price = 0
for(var/obj/item/stored_item in contents)
- if(format_text(stored_item.name) == stocked_item)
+ if(stored_item.type == stocked_item)
price = stored_item.custom_price
if(!base64) //generate an icon of the item to use in UI
if(base64_cache[stored_item.type])
@@ -1616,7 +1614,8 @@
base64_cache[stored_item.type] = base64
break
var/list/data = list(
- name = stocked_item,
+ path = stocked_item,
+ name = initial(stocked_item.name),
price = price,
img = base64,
amount = vending_machine_input[stocked_item],
@@ -1631,8 +1630,8 @@
switch(action)
if("dispense")
if(isliving(usr))
- vend_act(usr, params["item"])
- vend_ready = TRUE
+ vend_act(usr, params)
+ vend_ready = TRUE
return TRUE
/obj/machinery/vending/custom/attackby(obj/item/attack_item, mob/user, params)
@@ -1670,9 +1669,10 @@
* Updating stock, account transactions, alerting users.
* @return -- TRUE if a valid condition was met, FALSE otherwise.
*/
-/obj/machinery/vending/custom/proc/vend_act(mob/living/user, choice)
+/obj/machinery/vending/custom/proc/vend_act(mob/living/user, list/params)
if(!vend_ready)
return
+ var/obj/item/choice = text2path(params["item"]) // typepath is a string coming from javascript, we need to convert it back
var/obj/item/dispensed_item
var/obj/item/card/id/id_card = user.get_idcard(TRUE)
vend_ready = FALSE
@@ -1681,8 +1681,8 @@
flick(icon_deny, src)
return TRUE
var/datum/bank_account/payee = id_card.registered_account
- for(var/obj/stock in contents)
- if(format_text(stock.name) == choice)
+ for(var/obj/item/stock in contents)
+ if(istype(stock, choice))
dispensed_item = stock
break
if(!dispensed_item)
diff --git a/code/modules/wiremod/components/abstract/equpiment_action.dm b/code/modules/wiremod/components/abstract/equpiment_action.dm
deleted file mode 100644
index 17f931ae5e3..00000000000
--- a/code/modules/wiremod/components/abstract/equpiment_action.dm
+++ /dev/null
@@ -1,65 +0,0 @@
-/obj/item/circuit_component/equipment_action
- display_name = "Abstract Equipment Action"
- desc = "You shouldn't be seeing this."
-
- /// The icon of the button
- var/datum/port/input/option/icon_options
-
- /// The name to use for the button
- var/datum/port/input/button_name
-
- /// Called when the user presses the button
- var/datum/port/output/signal
-
-/obj/item/circuit_component/equipment_action/Initialize(mapload, default_icon)
- . = ..()
-
- if (!isnull(default_icon))
- icon_options.set_input(default_icon)
-
- button_name = add_input_port("Name", PORT_TYPE_STRING)
-
- signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
-
-/obj/item/circuit_component/equipment_action/populate_options()
- var/static/action_options = list(
- "Blank",
-
- "One",
- "Two",
- "Three",
- "Four",
- "Five",
-
- "Blood",
- "Bomb",
- "Brain",
- "Brain Damage",
- "Cross",
- "Electricity",
- "Exclamation",
- "Heart",
- "Id",
- "Info",
- "Injection",
- "Magnetism",
- "Minus",
- "Network",
- "Plus",
- "Power",
- "Question",
- "Radioactive",
- "Reaction",
- "Repair",
- "Say",
- "Scan",
- "Shield",
- "Skull",
- "Sleep",
- "Wireless",
- )
-
- icon_options = add_option_port("Icon", action_options)
-
-/obj/item/circuit_component/equipment_action/proc/update_action()
- return
diff --git a/code/modules/wiremod/components/action/equpiment_action.dm b/code/modules/wiremod/components/action/equpiment_action.dm
new file mode 100644
index 00000000000..54150ca44d6
--- /dev/null
+++ b/code/modules/wiremod/components/action/equpiment_action.dm
@@ -0,0 +1,92 @@
+/obj/item/circuit_component/equipment_action
+ display_name = "Equipment Action"
+ desc = "Represents an action the user can take when using supported shells."
+ required_shells = list(/obj/item/organ/internal/cyberimp/bci, /obj/item/mod/module/circuit)
+
+ /// The icon of the button
+ var/datum/port/input/option/icon_options
+
+ /// The name to use for the button
+ var/datum/port/input/button_name
+
+ /// The mob who activated their granted action
+ var/datum/port/output/user
+
+ /// Called when the user presses the button
+ var/datum/port/output/signal
+
+ /// An assoc list of datum REF()s, linked to the actions granted.
+ var/list/granted_to = list()
+
+/obj/item/circuit_component/equipment_action/Initialize(mapload, default_icon)
+ . = ..()
+
+ if (!isnull(default_icon))
+ icon_options.set_input(default_icon)
+
+ button_name = add_input_port("Name", PORT_TYPE_STRING)
+
+ user = add_output_port("User", PORT_TYPE_USER)
+ signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
+
+/obj/item/circuit_component/equipment_action/Destroy()
+ QDEL_LIST_ASSOC_VAL(granted_to)
+ return ..()
+
+/obj/item/circuit_component/equipment_action/populate_options()
+ var/static/action_options = list(
+ "Blank",
+
+ "One",
+ "Two",
+ "Three",
+ "Four",
+ "Five",
+
+ "Blood",
+ "Bomb",
+ "Brain",
+ "Brain Damage",
+ "Cross",
+ "Electricity",
+ "Exclamation",
+ "Heart",
+ "Id",
+ "Info",
+ "Injection",
+ "Magnetism",
+ "Minus",
+ "Network",
+ "Plus",
+ "Power",
+ "Question",
+ "Radioactive",
+ "Reaction",
+ "Repair",
+ "Say",
+ "Scan",
+ "Shield",
+ "Skull",
+ "Sleep",
+ "Wireless",
+ )
+
+ icon_options = add_option_port("Icon", action_options)
+
+/obj/item/circuit_component/equipment_action/register_shell(atom/movable/shell)
+ . = ..()
+ SEND_SIGNAL(shell, COMSIG_CIRCUIT_ACTION_COMPONENT_REGISTERED, src)
+
+/obj/item/circuit_component/equipment_action/unregister_shell(atom/movable/shell)
+ . = ..()
+ SEND_SIGNAL(shell, COMSIG_CIRCUIT_ACTION_COMPONENT_UNREGISTERED, src)
+
+/obj/item/circuit_component/equipment_action/input_received(datum/port/input/port)
+ if (length(granted_to))
+ update_actions()
+
+/obj/item/circuit_component/equipment_action/proc/update_actions()
+ for(var/ref in granted_to)
+ var/datum/action/granted_action = granted_to[ref]
+ granted_action.name = button_name.value || "Action"
+ granted_action.button_icon_state = "bci_[replacetextEx(lowertext(icon_options.value), " ", "_")]"
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index c3ac3154d2d..f41d4fa8bf1 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -10,18 +10,16 @@
/obj/item/organ/internal/cyberimp/bci/Initialize(mapload)
. = ..()
+ RegisterSignal(src, COMSIG_CIRCUIT_ACTION_COMPONENT_REGISTERED, PROC_REF(action_comp_registered))
+ RegisterSignal(src, COMSIG_CIRCUIT_ACTION_COMPONENT_UNREGISTERED, PROC_REF(action_comp_unregistered))
+
var/obj/item/integrated_circuit/circuit = new(src)
- circuit.add_component(new /obj/item/circuit_component/equipment_action/bci(null, "One"))
+ circuit.add_component(new /obj/item/circuit_component/equipment_action(null, "One"))
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/bci_core,
), SHELL_CAPACITY_SMALL, starting_circuit = circuit)
-/obj/item/organ/internal/cyberimp/bci/on_insert(mob/living/carbon/receiver)
- . = ..()
- // Organs are put in nullspace, but this breaks circuit interactions
- forceMove(receiver)
-
/obj/item/organ/internal/cyberimp/bci/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null)
if (owner)
// Otherwise say_dead will be called.
@@ -33,41 +31,17 @@
else
return ..()
-/obj/item/circuit_component/equipment_action/bci
- display_name = "BCI Action"
- desc = "Represents an action the user can take when implanted with the brain-computer interface."
- required_shells = list(/obj/item/organ/internal/cyberimp/bci)
+/obj/item/organ/internal/cyberimp/bci/proc/action_comp_registered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
+ SIGNAL_HANDLER
+ LAZYADD(actions, new/datum/action/innate/bci_action(src, action_comp))
- /// A reference to the action button itself
- var/datum/action/innate/bci_action/bci_action
-
-/obj/item/circuit_component/equipment_action/bci/Destroy()
- QDEL_NULL(bci_action)
- return ..()
-
-/obj/item/circuit_component/equipment_action/bci/register_shell(atom/movable/shell)
- . = ..()
- var/obj/item/organ/internal/cyberimp/bci/bci = shell
- if(istype(bci))
- bci_action = new(src)
- update_action()
-
- bci.actions += list(bci_action)
-
-/obj/item/circuit_component/equipment_action/bci/unregister_shell(atom/movable/shell)
- var/obj/item/organ/internal/cyberimp/bci/bci = shell
- if(istype(bci))
- bci.actions -= bci_action
- QDEL_NULL(bci_action)
- return ..()
-
-/obj/item/circuit_component/equipment_action/bci/input_received(datum/port/input/port)
- if (!isnull(bci_action))
- update_action()
-
-/obj/item/circuit_component/equipment_action/bci/update_action()
- bci_action.name = button_name.value
- bci_action.button_icon_state = "bci_[replacetextEx(lowertext(icon_options.value), " ", "_")]"
+/obj/item/organ/internal/cyberimp/bci/proc/action_comp_unregistered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
+ SIGNAL_HANDLER
+ var/datum/action/innate/bci_action/action = action_comp.granted_to[REF(src)]
+ if(!istype(action))
+ return
+ LAZYREMOVE(actions, action)
+ QDEL_LIST_ASSOC_VAL(action_comp.granted_to)
/datum/action/innate/bci_action
name = "Action"
@@ -75,20 +49,23 @@
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "bci_power"
- var/obj/item/circuit_component/equipment_action/bci/circuit_component
+ var/obj/item/organ/internal/cyberimp/bci/bci
+ var/obj/item/circuit_component/equipment_action/circuit_component
-/datum/action/innate/bci_action/New(obj/item/circuit_component/equipment_action/bci/circuit_component)
+/datum/action/innate/bci_action/New(obj/item/organ/internal/cyberimp/bci/_bci, obj/item/circuit_component/equipment_action/circuit_component)
..()
-
+ bci = _bci
+ circuit_component.granted_to[REF(_bci)] = src
src.circuit_component = circuit_component
/datum/action/innate/bci_action/Destroy()
- circuit_component.bci_action = null
+ circuit_component.granted_to -= REF(bci)
circuit_component = null
return ..()
/datum/action/innate/bci_action/Activate()
+ circuit_component.user.set_output(owner)
circuit_component.signal.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/bci_core
diff --git a/code/modules/wiremod/shell/module.dm b/code/modules/wiremod/shell/module.dm
index 4201c6afb17..6f9a3dda93b 100644
--- a/code/modules/wiremod/shell/module.dm
+++ b/code/modules/wiremod/shell/module.dm
@@ -10,8 +10,15 @@
/// A reference to the shell component, used to access the shell and its attached circuit
var/datum/component/shell/shell
+ /// List of installed action components
+ var/list/obj/item/circuit_component/equipment_action/action_comps = list()
+
/obj/item/mod/module/circuit/Initialize(mapload)
. = ..()
+
+ RegisterSignal(src, COMSIG_CIRCUIT_ACTION_COMPONENT_REGISTERED, PROC_REF(action_comp_registered))
+ RegisterSignal(src, COMSIG_CIRCUIT_ACTION_COMPONENT_UNREGISTERED, PROC_REF(action_comp_unregistered))
+
shell = AddComponent(/datum/component/shell, \
list(new /obj/item/circuit_component/mod_adapter_core()), \
capacity = SHELL_CAPACITY_LARGE, \
@@ -22,6 +29,17 @@
if(drain_power(amount))
. = COMPONENT_OVERRIDE_POWER_USAGE
+/obj/item/mod/module/circuit/proc/action_comp_registered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
+ SIGNAL_HANDLER
+ action_comps += action_comp
+
+/obj/item/mod/module/circuit/proc/action_comp_unregistered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
+ SIGNAL_HANDLER
+ action_comps -= action_comp
+ for(var/ref in action_comp.granted_to)
+ unpin_action(action_comp, locate(ref))
+ QDEL_LIST_ASSOC_VAL(action_comp.granted_to)
+
/obj/item/mod/module/circuit/on_install()
if(!shell?.attached_circuit)
return
@@ -30,6 +48,9 @@
/obj/item/mod/module/circuit/on_uninstall(deleting = FALSE)
if(!shell?.attached_circuit)
return
+ for(var/obj/item/circuit_component/equipment_action/action_comp in action_comps)
+ for(var/ref in action_comp.granted_to)
+ unpin_action(action_comp, locate(ref))
UnregisterSignal(shell?.attached_circuit, COMSIG_CIRCUIT_PRE_POWER_USAGE)
/obj/item/mod/module/circuit/on_use()
@@ -38,38 +59,79 @@
return
if(!shell.attached_circuit)
return
- var/list/action_components = shell.attached_circuit.get_all_contents_type(/obj/item/circuit_component/equipment_action/mod)
- if(!action_components.len)
- shell.attached_circuit.interact(mod.wearer)
- return
- var/list/repeat_name_counts = list("Access Circuit" = 1)
- var/list/display_names = list()
- var/list/radial_options = list()
- for(var/obj/item/circuit_component/equipment_action/mod/action_component in action_components)
- var/action_name = action_component.button_name.value
- if(!repeat_name_counts[action_name])
- repeat_name_counts[action_name] = 0
- repeat_name_counts[action_name]++
- if(repeat_name_counts[action_name] > 1)
- action_name += " ([repeat_name_counts[action_name]])"
- display_names[action_name] = REF(action_component)
- var/option_icon_state = "bci_[replacetextEx(lowertext(action_component.icon_options.value), " ", "_")]"
- radial_options += list("[action_name]" = image('icons/mob/actions/actions_items.dmi', option_icon_state))
- radial_options += list("Access Circuit" = image(shell.attached_circuit))
- var/selected_option = show_radial_menu(mod.wearer, src, radial_options, custom_check = FALSE, require_near = TRUE)
- if(!selected_option)
- return
- if(!mod || !mod.wearer || !mod.active || mod.activating)
- return
- if(selected_option == "Access Circuit")
- shell.attached_circuit?.interact(mod.wearer)
- else
- var/component_reference = display_names[selected_option]
- var/obj/item/circuit_component/equipment_action/mod/selected_component = locate(component_reference) in shell.attached_circuit.contents
- if(!istype(selected_component))
- return
- selected_component.signal.set_output(COMPONENT_SIGNAL)
+ shell.attached_circuit?.interact(mod.wearer)
+/obj/item/mod/module/circuit/get_configuration(mob/user)
+ . = ..()
+ var/unnamed_action_index = 1
+ for(var/obj/item/circuit_component/equipment_action/action_comp in action_comps)
+ .[REF(action_comp)] = add_ui_configuration(action_comp.button_name.value || "Unnamed Action [unnamed_action_index++]", "pin", !!action_comp.granted_to[REF(user)])
+
+/obj/item/mod/module/circuit/configure_edit(key, value)
+ . = ..()
+ var/obj/item/circuit_component/equipment_action/action_comp = locate(key) in action_comps
+ if(!istype(action_comp))
+ return
+ if(text2num(value))
+ pin_action(action_comp, usr)
+ else
+ unpin_action(action_comp, usr)
+
+/obj/item/mod/module/circuit/proc/pin_action(obj/item/circuit_component/equipment_action/action_comp, mob/user)
+ if(!istype(user))
+ return
+ if(action_comp.granted_to[REF(user)]) // Sanity check - don't pin an action for a mob that has already pinned it
+ return
+ mod.add_item_action(new/datum/action/item_action/mod/pinnable/circuit(mod, user, src, action_comp))
+
+/obj/item/mod/module/circuit/proc/unpin_action(obj/item/circuit_component/equipment_action/action_comp, mob/user)
+ var/datum/action/item_action/mod/pinnable/circuit/action = action_comp.granted_to[REF(user)]
+ if(!istype(action))
+ return
+ qdel(action)
+
+/datum/action/item_action/mod/pinnable/circuit
+ button_icon = 'icons/mob/actions/actions_items.dmi'
+ button_icon_state = "bci_blank"
+
+ /// A reference to the module containing this action's component
+ var/obj/item/mod/module/circuit/module
+
+ /// A reference to the component this action triggers.
+ var/obj/item/circuit_component/equipment_action/circuit_component
+
+/datum/action/item_action/mod/pinnable/circuit/New(Target, mob/user, obj/item/mod/module/circuit/linked_module, obj/item/circuit_component/equipment_action/action_comp)
+ . = ..()
+ module = linked_module
+ action_comp.granted_to[REF(user)] = src
+ circuit_component = action_comp
+ name = action_comp.button_name.value
+ button_icon_state = "bci_[replacetextEx(lowertext(action_comp.icon_options.value), " ", "_")]"
+
+/datum/action/item_action/mod/pinnable/circuit/Destroy()
+ circuit_component.granted_to -= REF(pinner)
+ circuit_component = null
+
+ return ..()
+
+/datum/action/item_action/mod/pinnable/circuit/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/mod/control/mod = module.mod
+ if(!istype(mod))
+ return
+ if(!mod.active || mod.activating)
+ if(mod.wearer)
+ module.balloon_alert(mod.wearer, "not active!")
+ return
+ circuit_component.user.set_output(owner)
+ circuit_component.signal.set_output(COMPONENT_SIGNAL)
+
+/// If the guy whose UI we are pinned to got deleted
+/datum/action/item_action/mod/pinnable/circuit/pinner_deleted()
+ module?.action_comps[circuit_component] -= REF(pinner)
+ . = ..()
/obj/item/circuit_component/mod_adapter_core
display_name = "MOD circuit adapter core"
@@ -237,8 +299,3 @@
if(!attached_module.mod?.wearer)
return
wearer.set_output(attached_module.mod.wearer)
-
-/obj/item/circuit_component/equipment_action/mod
- display_name = "MOD action"
- desc = "Represents an action the user can take when wearing the MODsuit."
- required_shells = list(/obj/item/mod/module/circuit)
diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm
index 7514b6cdc6b..2ed2bf541d8 100644
--- a/code/modules/zombie/organs.dm
+++ b/code/modules/zombie/organs.dm
@@ -23,24 +23,24 @@
GLOB.zombie_infection_list -= src
. = ..()
-/obj/item/organ/internal/zombie_infection/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/zombie_infection/on_mob_insert(mob/living/carbon/M, special = FALSE, movement_flags)
. = ..()
START_PROCESSING(SSobj, src)
-/obj/item/organ/internal/zombie_infection/Remove(mob/living/carbon/M, special = FALSE)
+/obj/item/organ/internal/zombie_infection/on_mob_remove(mob/living/carbon/M, special = FALSE)
. = ..()
STOP_PROCESSING(SSobj, src)
- if(iszombie(M) && old_species && !special && !QDELETED(src))
+ if(iszombie(M) && old_species && !special)
M.set_species(old_species)
if(timer_id)
deltimer(timer_id)
-/obj/item/organ/internal/zombie_infection/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/zombie_infection/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(organ_owner, COMSIG_LIVING_DEATH, PROC_REF(organ_owner_died))
-/obj/item/organ/internal/zombie_infection/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/zombie_infection/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_LIVING_DEATH)
diff --git a/config/skyrat/skyrat_config.txt b/config/skyrat/skyrat_config.txt
index dfda06ae026..e2d82066234 100644
--- a/config/skyrat/skyrat_config.txt
+++ b/config/skyrat/skyrat_config.txt
@@ -121,7 +121,7 @@ LORECASTER_DELAY 18000
# DISABLE_SUICIDE
## The link to the policy wiki page for primitive catgirls
-ICECATS_POLICY_LINK "https://wiki.skyrat13.tk/w/index.php/Ghost_Role_policy#Icewalkers"
+ICECATS_POLICY_LINK "https://wiki.skyrat13.space/index.php/Ghost_Role_policy#Icewalkers"
## What is the maximum size (in percents) we want people to be able to use with the size collar?
SIZE_COLLAR_MAXIMUM 400
diff --git a/html/changelogs/AutoChangeLog-pr-831.yml b/html/changelogs/AutoChangeLog-pr-831.yml
deleted file mode 100644
index 1c9aa9a3c0e..00000000000
--- a/html/changelogs/AutoChangeLog-pr-831.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "ReturnToZender, The Sharkening"
-delete-after: True
-changes:
- - rscadd: "Department ordered SolFed guns"
- - rscadd: "Sol-Fed Firearms now require a weapons permit to order"
- - bugfix: "Adjusted Sol weapon balance to be more fair"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-872.yml b/html/changelogs/AutoChangeLog-pr-872.yml
deleted file mode 100644
index aec0eb0b947..00000000000
--- a/html/changelogs/AutoChangeLog-pr-872.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "BurgerBB"
-delete-after: True
-changes:
- - qol: "Reduces the frequency of miasma notifications/coughing."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-877.yml b/html/changelogs/AutoChangeLog-pr-877.yml
deleted file mode 100644
index 46bb65309a1..00000000000
--- a/html/changelogs/AutoChangeLog-pr-877.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "ReturnToZender"
-delete-after: True
-changes:
- - bugfix: "job estimation actually works again"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-878.yml b/html/changelogs/AutoChangeLog-pr-878.yml
deleted file mode 100644
index 388b4481133..00000000000
--- a/html/changelogs/AutoChangeLog-pr-878.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "ReturnToZender"
-delete-after: True
-changes:
- - bugfix: "Runtime on obscurity examine prefs when the mob in question lacks a client"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-880.yml b/html/changelogs/AutoChangeLog-pr-880.yml
deleted file mode 100644
index c18392e5cf1..00000000000
--- a/html/changelogs/AutoChangeLog-pr-880.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "StrangeWeirdKitten"
-delete-after: True
-changes:
- - bugfix: "Shadekin is fixed"
\ No newline at end of file
diff --git a/html/changelogs/archive/2023-12.yml b/html/changelogs/archive/2023-12.yml
index 87640a84f29..50620b8409e 100644
--- a/html/changelogs/archive/2023-12.yml
+++ b/html/changelogs/archive/2023-12.yml
@@ -776,33 +776,249 @@
zeroisthebiggay:
- rscadd: face scarves for icewalkers
2023-12-20:
- BurgerBB:
- - qol: Reduces the frequency of miasma notifications/coughing.
Hatterhat:
- balance: Rough stones and stone bricks now have 10 force, up from 0, and 15 throwforce,
up from 0/10, respectively.
- Majkl-J:
- - refactor: Refactors modularity significantly
- ReturnToZender, The Sharkening:
- - rscadd: Department ordered SolFed guns
- - rscadd: Sol-Fed Firearms now require a weapons permit to order
- - bugfix: Adjusted Sol weapon balance to be more fair
- ReturnToZender, Waterpig, Shadow-Quill:
- - rscadd: Job estimation statpanel from Daedalus Dock
+ LT3:
+ - admin: Server wide admin announcements now use an alert box like other announcements
+ MrMelbert, Ghommie:
+ - bugfix: Alien nests, and some other stuff, can be physically attacked again.
+ - balance: x-mas trees (the ones with presents), are indestructibles. Truly protected
+ by a yuletide spirit.
+ Rhials:
+ - bugfix: Icebox escape pods will now land randomly on the surface, instead of only
+ in certain ruins.
SkyratBot:
+ - bugfix: Fixes runtime from augments not unregistering a signal.
+ - bugfix: qm's intern id is now "quartermaster-in-training"
+ - image: qm's id and hud icons now use the cargo tech icons (but blue) to be consistent
+ with the other heads of staff
+ - balance: Disallows siphoning credits outside of station
+ - bugfix: Made Syndicate Stormtrooper and Nuclear Operative TGC card holograms visible
+ again
+ - bugfix: Fixed a bluescreen while inputting a global variable in the circuit UI.
+ - image: cyborg tools became animated
- image: the SpacePol bounty hunters have finally had a new run of Uniforms made,
bringing them back to the forefront of enforcement fashion! Wardens will find
their Police Jackets/Hats have been updated match SpacePol's new look as well.
- bugfix: fixed the Mafia Warden having a hat that didn't match his jacket, and
fixed SpacePol masks not covering the nose
- - bugfix: qm's intern id is now "quartermaster-in-training"
- - image: qm's id and hud icons now use the cargo tech icons (but blue) to be consistent
- with the other heads of staff
- - bugfix: Made Syndicate Stormtrooper and Nuclear Operative TGC card holograms visible
- again
- - bugfix: Fixes runtime from augments not unregistering a signal.
- - image: cyborg tools became animated
+ - bugfix: Emojipedia should copy the text on click properly, now
+2023-12-21:
+ KannaLisvern:
+ - rscadd: Added a new ERP emote. *lewdmoan
+ Nerev4r:
+ - rscadd: Due to increased availability of calcium supplements in Sector 13 due
+ to Deep Spacer lobbying, snailpeople have found their shells to be more suited
+ towards protection of themselves and their recently-discovered-to-be-binary
+ hearts in a combat scenario.
+ - rscadd: Snailpeople have been invited to recent EVA training sessions, and have
+ learned to attach themselves to the floors if gravity generators go out; who'd
+ have thunk it?
+ - bugfix: Snailpeople no longer benefit from Settler's equipment load reduction
+ while crawling. I could not think of a funny caption for this.
+ Paxilmaniac:
+ - balance: Shotgun shells, ESPECIALLY skyrat's shotgun shells, have been rebalanced
+ to not either be useless or insane in every situation, see pr for details.
+ - image: New sprites for some projectiles, like flechettes and whatnot
+ - sound: The sound effect that the arc welder uses has been changed to be less quiet
+ and more noticable
+ - balance: SolFed response team equipment has been changed around a bit with the
+ new armor and whatnot.
+ - image: SolFed response teams (and cargo imports) can now see a new set of SolFed
+ uniforms, one for peacekeepers and one for EMTs. No longer do they have to dress
+ as wild west larpers, or half life HECU.
+ - qol: The sol rifles have had their names changed so that they suck less to refer
+ to as characters that don't speak french-afrikaans (coming 2023)
+ RaShCat:
+ - bugfix: offsets of items in teshari's paws and worn head
+ - image: simple redraw of teshari's sabres.
+ SkyratBot:
+ - bugfix: fixes some dropdowns not displaying the right text after selecting something
+ nikothedude:
+ - bugfix: Unspillable organs no longer spill
+ vinylspiders:
+ - bugfix: fixes company imports bluescreen
+ - bugfix: fixed a bug where certain loadout menu items would duplicate themselves
+ across tabs
2023-12-22:
+ LT3:
+ - qol: Roundstart intercept report and security level announcements are combined
+ into a single announcement
+ Melbert:
+ - qol: When you place a beaker in a chem-master, you no longer thwack it.
+ - bugfix: Crewmembers immune to viruses won't be picked by the fake virus event.
+ Paxilmaniac:
+ - rscadd: A small series of items have been added to the construction section of
+ the rapid construction fabricator to allow users to actually... Rapidly construct
+ things.
+ - image: Sprites for all of the new things made by me.
+ Rhials:
+ - bugfix: Disease Outbreak events will only select players on the station/lavaland
+ as patient zero.
+ SkyratBot:
+ - qol: The bitrunning equipment vending machine now has a unique description for
+ each of the bitrunning disks
+ - rscadd: 'Added new quirk: Cyborg Lover!'
+ - bugfix: Name input in character setup should work properly now.
+ - bugfix: Many inputs should feel more responsive.
+ - bugfix: Examine text on disconnected players is no longer accidentally subtle
+2023-12-23:
+ Melbert:
+ - bugfix: Gibbing a bot will no longer spawn bloody gibs.
+ Rhials:
+ - qol: Wizard apprentices now spawn on the same tile as the contract that summoned
+ them.
+ SkyratBot:
+ - balance: Doing experiments AFTER their tech is researched now gives full 100%
+ points instead of 66%.
+ - bugfix: Fixes Holy Water giving you more blood and making you less drunk than
+ water normally does.
+ - bugfix: '''A whole bunch of spiders in a SWAT suit'' to ''A whole bunch of spiders
+ in a MODsuit'''
+ sqnztb:
+ - bugfix: blueshift - corrects some request console names, virology's outlet now
+ sends to space, and R&D now has a proper R&D console instead of a civilian one.
+2023-12-24:
+ A.C.M.O.:
+ - bugfix: Fixed Personal AI cards, allowing them to wake up from sleep.
+ BurgerBB:
+ - balance: Miasma is now filtered through gas mask filters, at medium strength.
+ - qol: Reduces the frequency of miasma notifications/coughing.
+ ChakatStormCloud:
+ - bugfix: Synths now properly regenerate blood volume with oil
+ GreytideSkye:
+ - refactor: LustWish interns, fed up with 5e Grappling Rules, explicitly clarified
+ what is a restraint and what is a structure.
+ Jolly:
+ - image: Colored labcoats have been GAGSed! Please report any weird oddities on
+ Github.
+ - bugfix: The coroners lab coat is no longer offset by one pixel.
+ LemonInTheDark, Donglesplonge:
+ - image: Redoes fov "mask" sprites. They're clean, have a very pleasant dithering
+ effect, and look real fuckin good!
+ - rscdel: Changed FOV, it no longer hides mobs, instead it blurs the hidden area,
+ and makes it a bit darker/oversaturated
ReturnToZender:
- bugfix: job estimation actually works again
- bugfix: Runtime on obscurity examine prefs when the mob in question lacks a client
+ ReturnToZender, The Sharkening:
+ - rscadd: Department ordered SolFed guns
+ - rscadd: Sol-Fed Firearms now require a weapons permit to order
+ - bugfix: Adjusted Sol weapon balance to be more fair
+ SkyratBot:
+ - bugfix: the boozeomat ui will stop duplicating space beer bottles
+ - refactor: refactored vending machine backend to have unique keys for their data
+ structures. should fix bugs related to items that happen to have the same name.
+ - balance: Ai's using the mech domination ability can now eject from the mech
+ - qol: Head of Security beret added to their garmet bag
+ - qol: Head of Security bowman added to their locker
+ - rscadd: Nightmares retain their ability to dodge projectiles for a brief period
+ after leaving a dark area, meaning it is now possible to dodge lasers.
+ - qol: Nightmares and Shadow People now receive a screen alert when they are in
+ a sufficiently dark location that their abilities are active.
+ - bugfix: Reactions whose temps/ph values fall way below their optimal values no
+ longer restart & prevents infinite loops. Made reaction code slightly faster
+ - rscadd: Added new GaGs santahats.
+ - bugfix: Injecting yourself twice with a luxury medipen in pressure doesn't runtime
+ and mechs with a cell with no max charge won't runtime.
+ - refactor: Gives hyposprays a var to check if they're fully used up rather than
+ setting total_volume to 0.
+ - refactor: Ghost roles now offer ghosts a clickable poll button. Ghosts can select
+ a role, deselect it, alt-click it for "Never For This Round", can cancel "Never",
+ can see the countdown, and can see how many other people are signed up for the
+ role poll.
+ - bugfix: AI voice changer UI should show defaults properly
+ - bugfix: NTOS Software Hub should focus on the input immediately now
+ - bugfix: Ore container UI should autofocus on the search bar now
+ - bugfix: Vend-a-tray registration text should display as intended
+ - bugfix: Fixed integrated circuit UI breaking.
+ - bugfix: Camera circuits actually print pictures again!
+ - qol: Camera circuits can now take pictures while on a carbon, whether held, hung
+ around the neck, or sneakily hidden in either pocket. When held they try to
+ deposit their pictures into the holder's hands if possible.
+ - bugfix: Camera circuits should now actually use the camera's x axis picture size,
+ rather than using the camera's y axis picture size setting for both axis.
+ - spellcheck: changes mutiline text to multiline text in vv
+ - rscadd: Canisters now have wires! You can pulse wires to do various canister functions
+ like opening/closing the valve. Make sure it has a cell though.
+ - rscadd: You can rig assembly combos (igniter-timer, prox-signaler, etc.) onto
+ canisters
+ - qol: Canisters now have screentips
+ StrangeWeirdKitten:
+ - config: The headshot is no longer vetted or donor locked
+ - bugfix: Shadekin is fixed
+ vinylspiders:
+ - qol: loadout menu has a search function and is zebra-styled for readability.
+ - code_imp: improved performance of the loadout menu
+ - bugfix: removed nonfunctional tutorial button
+ xXPawnStarrXx:
+ - image: Made Skyrat Christmas clothes GAGS compatable.
+2023-12-25:
+ Gandalf2k15:
+ - rscadd: PDAs with a dead power cell are now limited to using their Messenger app.
+ - bugfix: Microwaves now stop charging PDAs if the cell was removed mid-charge.
+ - bugfix: Microwaves can now charge laptops.
+ - bugfix: 'PDA Flashlights can''t be turned on while the PDA is dead. fix: You can
+ now hold a laptop up to a camera (if it has a notekeeper app installed) like
+ PDAs already could.'
+ Jacquerel and Fikou:
+ - qol: If the station rolls the "Cargo Gorilla" station trait. you will be able
+ to sign up for the role from the game lobby.
+ - qol: If nobody signs up to be the Cargo Gorilla then you can select it from the
+ Late Join menu and arrive on the arrival shuttle.
+ - bugfix: The Cargo Gorilla will actually spawn.
+ KannaLisvern:
+ - qol: Removed Bluespace Crystals and Diamonds from medical emergency bed's recipe.
+ LT3:
+ - qol: TGUI will now wait longer trying to reconnect to a new round
+ Rhials:
+ - qol: Obsessed crewmembers are now displayed in the orbit panel.
+ - qol: The Paradox Clone orbit menu tab is now white. Neat!
+ SkyratBot:
+ - bugfix: Fixed a bug that would give you the chat message "/datum/element/damage_threshold
+ looks unharmed!"
+ - bugfix: bridge assistant no longer passes some head of staff checks
+ - qol: if you steal a command member's liver, you can now sabre better!
+ - qol: NTNet Downloader now has a search bar, and programs are now better sorted.
+ - balance: Cyborg lover has been replaced with Transhumanist. Transhumanists start
+ with a robotic limb and get mood buffs by being near to silicon-based lifeforms.
+ However, they get mood debuffs by being near organics, so there is a tradeoff
+ to taking this quirk. The cost for this quirk has been reduced from 2 to 0.
+ - bugfix: lab coats no longer have directional sprites when thrown
+ - bugfix: Cut down a significant amount of time that caused the start of rounds
+ to lag.
+ - rscadd: Bridge Assistant job accessible from a station trait.
+ - rscadd: Infiltrators (Latejoin/Midround traitors) can now buy and use Contract
+ kits again.
+ - rscdel: Contractor baton can now only be purchased once.
+ - rscadd: The Spectre-Meter modular computer app. A little, amatuerishly coded app
+ that, as the name implies, scan an area for spectral presence. It can be found
+ amongst other apps in maintenance computer disks.
+ - rscadd: An easier, lazier version of the Arcade app, also found in maintenance.
+ - rscadd: Black market computer disks, which contains programs not readily available
+ to the average assistant.
+ - qol: MOD wearers and internal AIs can pin the individual actions in a MOD circuit
+ module in a similar way to how they can pin modules. Circuit module actions
+ can be pinned from the configuration menu of the circuit
+ - refactor: The MOD action and BCI action components have been merged into one component
+ - the Equipment Action component.
+ - code_imp: removed excess calls to `update_total()` making reaction code slightly
+ faster.
+ - code_imp: removed excessive use of chemical constants (quantisation & rounding
+ constants) in places where they were not needed i.e. plumbing buffer, reaction
+ chamber, pyrotechnics & the liver.
+ - rscadd: You can now look up and down through the IC menu
+ - rscadd: Adds a mint condition clear PDA to the Goodies menu for 100,000 credits.
+ - rscadd: Added weapon recharger boards to designs available to print on sec techfab.
+ - bugfix: Camera console search should update automatically
+ - bugfix: Autolathe search should now properly show designs
+ - bugfix: polymorphing into drone no longer gibs
+ jjpark-kb:
+ - balance: (dec) ash combat plates are now to be equipped in the under slot rather
+ than the suit slot
+ theselfish:
+ - rscadd: Multiple titles have been added.
+ - bugfix: You can select Cargo Tech and Shaft Miner, again.
+ zeroisthebiggay:
+ - image: bluesec MODsuits. finally goddamn
diff --git a/icons/effects/fov/field_of_view.dmi b/icons/effects/fov/field_of_view.dmi
index 8086773d140..8a15ae18a92 100644
Binary files a/icons/effects/fov/field_of_view.dmi and b/icons/effects/fov/field_of_view.dmi differ
diff --git a/icons/hud/lobby/signup_button.dmi b/icons/hud/lobby/signup_button.dmi
new file mode 100644
index 00000000000..a67cc558442
Binary files /dev/null and b/icons/hud/lobby/signup_button.dmi differ
diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi
index 593a3878c2e..44ef2fb0787 100755
Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ
diff --git a/icons/mob/clothing/head/costume.dmi b/icons/mob/clothing/head/costume.dmi
index 3c5ea17b19d..06de36df19a 100644
Binary files a/icons/mob/clothing/head/costume.dmi and b/icons/mob/clothing/head/costume.dmi differ
diff --git a/icons/mob/clothing/suits/labcoat.dmi b/icons/mob/clothing/suits/labcoat.dmi
index f499203c95e..37cb0e8696b 100644
Binary files a/icons/mob/clothing/suits/labcoat.dmi and b/icons/mob/clothing/suits/labcoat.dmi differ
diff --git a/icons/mob/huds/hud.dmi b/icons/mob/huds/hud.dmi
index 3e32e17437c..eb7007c9c45 100644
Binary files a/icons/mob/huds/hud.dmi and b/icons/mob/huds/hud.dmi differ
diff --git a/icons/obj/antags/contractor_tablet.dmi b/icons/obj/antags/contractor_tablet.dmi
deleted file mode 100644
index ae3cb579e2b..00000000000
Binary files a/icons/obj/antags/contractor_tablet.dmi and /dev/null differ
diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi
index fdf3657c9d1..da8c2bda657 100644
Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ
diff --git a/icons/obj/clothing/head/costume.dmi b/icons/obj/clothing/head/costume.dmi
index f676b1c6973..3cfbd3d21ef 100644
Binary files a/icons/obj/clothing/head/costume.dmi and b/icons/obj/clothing/head/costume.dmi differ
diff --git a/icons/obj/clothing/suits/labcoat.dmi b/icons/obj/clothing/suits/labcoat.dmi
index fa404813e40..430d11d5f96 100644
Binary files a/icons/obj/clothing/suits/labcoat.dmi and b/icons/obj/clothing/suits/labcoat.dmi differ
diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi
index ee275066d16..c8ad438d1a3 100644
Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ
diff --git a/icons/obj/modular_pda.dmi b/icons/obj/modular_pda.dmi
index b43be78e717..5a39c6cd726 100644
Binary files a/icons/obj/modular_pda.dmi and b/icons/obj/modular_pda.dmi differ
diff --git a/modular_skyrat/master_files/code/datums/quirks/neutral_quirks/lungs.dm b/modular_skyrat/master_files/code/datums/quirks/neutral_quirks/lungs.dm
index 1e1fa2cc5f8..57247a3aa47 100644
--- a/modular_skyrat/master_files/code/datums/quirks/neutral_quirks/lungs.dm
+++ b/modular_skyrat/master_files/code/datums/quirks/neutral_quirks/lungs.dm
@@ -30,7 +30,7 @@
if (lungs != lungs_added && lungs != lungs_holding)
qdel(lungs_holding)
return
- lungs_holding.Insert(carbon_holder, special = TRUE, drop_if_replaced = FALSE)
+ lungs_holding.Insert(carbon_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
lungs_holding.organ_flags &= ~ORGAN_FROZEN
/datum/quirk/equipping/lungs/on_equip_item(obj/item/equipped, success)
diff --git a/modular_skyrat/master_files/code/datums/traits/neutral.dm b/modular_skyrat/master_files/code/datums/traits/neutral.dm
index 432fa728f03..09020832304 100644
--- a/modular_skyrat/master_files/code/datums/traits/neutral.dm
+++ b/modular_skyrat/master_files/code/datums/traits/neutral.dm
@@ -248,7 +248,7 @@ GLOBAL_VAR_INIT(DNR_trait_overlay, generate_DNR_trait_overlay())
var/obj/item/organ/internal/tongue/dog/new_tongue = new(get_turf(human_holder))
new_tongue.copy_traits_from(human_holder.get_organ_slot(ORGAN_SLOT_TONGUE))
- new_tongue.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_tongue.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/item_quirk/avian
name = "Avian Traits"
@@ -263,7 +263,7 @@ GLOBAL_VAR_INIT(DNR_trait_overlay, generate_DNR_trait_overlay())
var/obj/item/organ/internal/tongue/avian/new_tongue = new(get_turf(human_holder))
new_tongue.copy_traits_from(human_holder.get_organ_slot(ORGAN_SLOT_TONGUE))
- new_tongue.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_tongue.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/sensitivesnout
name = "Sensitive Snout"
diff --git a/modular_skyrat/master_files/code/game/gamemodes/dynamic.dm b/modular_skyrat/master_files/code/game/gamemodes/dynamic.dm
index 7f5ad5ea5fe..d5be696cdcc 100644
--- a/modular_skyrat/master_files/code/game/gamemodes/dynamic.dm
+++ b/modular_skyrat/master_files/code/game/gamemodes/dynamic.dm
@@ -3,7 +3,7 @@
#define ALT_MIDROUND_UPPER_TIME 10500
// A lite version of the intercept, which only sends a paper with goals and a trait report (or a lack thereof)
-/datum/game_mode/dynamic/proc/send_trait_report()
+/datum/controller/subsystem/dynamic/proc/send_trait_report()
. = "Central Command Status Summary"
var/greenshift = GLOB.dynamic_forced_extended || (threat_level < MIN_MIDROUND_COST && shown_threat < MIN_MIDROUND_COST) // if both shown and real threat are below any ruleset, its greenshift time
@@ -24,12 +24,44 @@
print_command_report(., "Central Command Status Summary", announce = FALSE)
priority_announce("Hello, crew of [station_name()]. Our intern has finished their shift-start divergency and goals evaluation, which has been sent to your communications console. Have a secure shift!", "Divergency Report", SSstation.announcer.get_rand_report_sound())
-/datum/game_mode/dynamic
+/*
+ * Generate a list of station goals available to purchase to report to the crew.
+ *
+ * Returns a formatted string all station goals that are available to the station.
+ */
+/datum/controller/subsystem/dynamic/proc/generate_station_goal_report()
+ if(!GLOB.station_goals.len)
+ return
+ . = "Special Orders for [station_name()]: "
+ var/list/goal_reports = list()
+ for(var/datum/station_goal/station_goal as anything in GLOB.station_goals)
+ station_goal.on_report()
+ goal_reports += station_goal.get_report()
+
+ . += goal_reports.Join("")
+ return
+
+/*
+ * Generate a list of active station traits to report to the crew.
+ *
+ * Returns a formatted string of all station traits (that are shown) affecting the station.
+ */
+/datum/controller/subsystem/dynamic/proc/generate_station_trait_report()
+ var/trait_list_string = ""
+ for(var/datum/station_trait/station_trait as anything in SSstation.station_traits)
+ if(!station_trait.show_in_report)
+ continue
+ trait_list_string += "[station_trait.get_report()] "
+ if(trait_list_string != "")
+ return "Identified shift divergencies: " + trait_list_string
+ return
+
+/datum/controller/subsystem/dynamic
/// Desired median point for midrounds, plus or minus the midround_roll_distance.
var/midround_median_frequency = 36000
/// Divides threat budget based on the server config
-/datum/game_mode/dynamic/generate_budgets()
+/datum/controller/subsystem/dynamic/generate_budgets()
if(CONFIG_GET(flag/split_threat_budget))
return ..()
@@ -38,7 +70,7 @@
mid_round_budget = threat_level
/// Gets the chance for a heavy ruleset midround injection, the dry_run argument is only used for forced injection.
-/datum/game_mode/dynamic/get_heavy_midround_injection_chance(dry_run)
+/datum/controller/subsystem/dynamic/get_heavy_midround_injection_chance(dry_run)
var/next_midround_roll = next_midround_injection() - SSticker.round_start_time
var/heavy_coefficient = CLAMP01((next_midround_roll - midround_light_upper_bound) / (midround_heavy_lower_bound - midround_light_upper_bound))
@@ -46,7 +78,7 @@
return 100 * heavy_coefficient
/// Determines the next midround injection attempt based on the set median and roll distance.
-/datum/game_mode/dynamic/next_midround_injection()
+/datum/controller/subsystem/dynamic/next_midround_injection()
if(!isnull(next_midround_injection))
return next_midround_injection
@@ -58,7 +90,7 @@
return next_midround_injection
/// If a midround injection fails to run, this can be called by the particular rule (if required) to attempt an alternate.
-/datum/game_mode/dynamic/proc/alternate_midround_injection()
+/datum/controller/subsystem/dynamic/proc/alternate_midround_injection()
next_midround_injection = world.time + rand(ALT_MIDROUND_LOWER_TIME, ALT_MIDROUND_UPPER_TIME)
log_dynamic_and_announce("Alternate midround injection in [DisplayTimeText(next_midround_injection - world.time)]")
diff --git a/modular_skyrat/master_files/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/modular_skyrat/master_files/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index c08780a17a9..8b6e8e2dc49 100644
--- a/modular_skyrat/master_files/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/modular_skyrat/master_files/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -1,5 +1,5 @@
/* /// Fired when there are no valid candidates. Try another roll after a delay. // BUBBER EDIT DISABLE
/datum/dynamic_ruleset/midround/from_ghosts/attempt_replacement()
- mode.alternate_midround_injection()
+ SSdynamic.alternate_midround_injection()
return
*/ // BUBBER EDIT DISABLE
diff --git a/modular_skyrat/master_files/code/game/gamemodes/objective.dm b/modular_skyrat/master_files/code/game/gamemodes/objective.dm
index 53b6dc7e937..a9867d9933f 100644
--- a/modular_skyrat/master_files/code/game/gamemodes/objective.dm
+++ b/modular_skyrat/master_files/code/game/gamemodes/objective.dm
@@ -1,3 +1,7 @@
+/datum/objective
+ /// Should space areas count when finding a target?
+ var/count_space_areas = TRUE
+
// For modularity, we hook into the update_explanation_text to be sure we have a target to register.
/datum/objective/assassinate/update_explanation_text()
RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(register_target_death))
diff --git a/modular_skyrat/master_files/code/modules/client/preferences/brain.dm b/modular_skyrat/master_files/code/modules/client/preferences/brain.dm
index cdf60e3f59a..516e72f6c86 100644
--- a/modular_skyrat/master_files/code/modules/client/preferences/brain.dm
+++ b/modular_skyrat/master_files/code/modules/client/preferences/brain.dm
@@ -27,7 +27,7 @@
new_brain.modular_persistence = old_brain.modular_persistence
old_brain.modular_persistence = null
- new_brain.Insert(target, drop_if_replaced = FALSE)
+ new_brain.on_mob_insert(target, movement_flags = DELETE_IF_REPLACED)
// Prefs can be applied to mindless mobs, let's not try to move the non-existent mind back in!
if(!keep_me_safe)
diff --git a/modular_skyrat/master_files/code/modules/client/preferences/headshot.dm b/modular_skyrat/master_files/code/modules/client/preferences/headshot.dm
index 44351be0fcf..58992869073 100644
--- a/modular_skyrat/master_files/code/modules/client/preferences/headshot.dm
+++ b/modular_skyrat/master_files/code/modules/client/preferences/headshot.dm
@@ -47,10 +47,3 @@
stored_link[usr?.ckey] = value
usr?.client?.prefs.headshot = value
return TRUE
-
-/datum/preference/text/headshot/is_accessible(datum/preferences/preferences)
- if(isnull(usr)) // Joining at roundstart
- return ..()
- if(!SSplayer_ranks.is_veteran(usr?.client) && !GLOB.donator_list[usr?.ckey] && !is_admin(usr?.client))
- return FALSE
- return ..()
diff --git a/modular_skyrat/master_files/code/modules/clothing/head/monkey_magnification_helmet.dm b/modular_skyrat/master_files/code/modules/clothing/head/monkey_magnification_helmet.dm
index 1c4c286fa85..ebe0ff707c3 100644
--- a/modular_skyrat/master_files/code/modules/clothing/head/monkey_magnification_helmet.dm
+++ b/modular_skyrat/master_files/code/modules/clothing/head/monkey_magnification_helmet.dm
@@ -56,7 +56,7 @@
/obj/item/clothing/head/helmet/monkey_sentience/proc/connect(mob/user)
polling = TRUE
- var/list/candidates = poll_candidates_for_mob("Do you want to play as a mind magnified monkey?", ROLE_MONKEY_HELMET, null, 5 SECONDS, magnification, POLL_IGNORE_MONKEY_HELMET)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a mind magnified monkey?", ROLE_MONKEY_HELMET, poll_time = 5 SECONDS, target_mob = magnification, ignore_category = POLL_IGNORE_MONKEY_HELMET)
polling = FALSE
if(!magnification)
return
diff --git a/modular_skyrat/master_files/code/modules/clothing/suits/labcoat.dm b/modular_skyrat/master_files/code/modules/clothing/suits/labcoat.dm
index f55c26d3645..b6ebd71dbd0 100644
--- a/modular_skyrat/master_files/code/modules/clothing/suits/labcoat.dm
+++ b/modular_skyrat/master_files/code/modules/clothing/suits/labcoat.dm
@@ -1,3 +1,6 @@
+/obj/item/clothing/suit/toggle/labcoat
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+
/obj/item/clothing/suit/toggle/labcoat/skyrat
name = "SR LABCOAT SUIT DEBUG"
desc = "REPORT THIS IF FOUND"
@@ -23,31 +26,34 @@
desc = "A high visibility vest for emergency responders, intended to draw attention away from the blood."
icon_state = "labcoat_highvis"
blood_overlay_type = "armor"
- supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
/obj/item/clothing/suit/toggle/labcoat/skyrat/highvis/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
if(!isinhands)
. += emissive_appearance(icon_file, "[icon_state]-emissive", src, alpha = src.alpha)
-/obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown //Intended to keep patients modest while still allowing for surgeries
+/obj/item/clothing/suit/toggle/labcoat/hospitalgown //Intended to keep patients modest while still allowing for surgeries
name = "hospital gown"
desc = "A complicated drapery with an assortment of velcros and strings, designed to keep a patient modest during medical stay and surgeries."
- icon_state = "hgown"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#478294#478294#478294#478294"
toggle_noun = "drapes"
body_parts_covered = NONE //Allows surgeries despite wearing it; hiding genitals is handled in /datum/sprite_accessory/genital/is_hidden() (Only place it'd work sadly)
armor_type = /datum/armor/none
equip_delay_other = 8
/obj/item/clothing/suit/toggle/labcoat/roboticist //Overwrite the TG Roboticist labcoat to Black and Red (not the Interdyne labcoat though)
- icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/labcoat.dmi'
- worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/labcoat.dmi'
- icon_state = "labcoat_robo_sr"
+ greyscale_colors = "#2D2D33#88242D#88242D#88242D"
/obj/item/clothing/suit/toggle/labcoat/medical //Renamed version of the Genetics labcoat for more generic medical purposes; just a subtype of /labcoat/ for the TG files
name = "medical labcoat"
desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder."
- icon_state = "labcoat_gen"
+ icon_state = "labcoat_job"
+ greyscale_config = /datum/greyscale_config/labcoat
+ greyscale_config_worn = /datum/greyscale_config/labcoat/worn
+ greyscale_colors = "#EEEEEE#4A77A1#4A77A1#7095C2"
/obj/item/clothing/suit/toggle/labcoat/Initialize(mapload)
. = ..()
diff --git a/modular_skyrat/master_files/code/modules/clothing/under/costume.dm b/modular_skyrat/master_files/code/modules/clothing/under/costume.dm
index 9e5567e4496..f3d511de144 100644
--- a/modular_skyrat/master_files/code/modules/clothing/under/costume.dm
+++ b/modular_skyrat/master_files/code/modules/clothing/under/costume.dm
@@ -107,21 +107,32 @@
/obj/item/clothing/under/costume/skyrat/christmas
name = "christmas costume"
desc = "Can you believe it guys? Christmas. Just a lightyear away!" //Lightyear is a measure of distance I hate it being used for this joke :(
- icon_state = "christmas"
+ icon_state = "christmas_male"
+ greyscale_colors = "#cc0f0f#c4c2c2"
+ greyscale_config = /datum/greyscale_config/chrimbo
+ greyscale_config_worn = /datum/greyscale_config/chrimbo/worn
+ greyscale_config_worn_digi = /datum/greyscale_config/chrimbo/worn/digi
+ body_parts_covered = CHEST|GROIN|ARMS
+ flags_1 = IS_PLAYER_COLORABLE_1
/obj/item/clothing/under/costume/skyrat/christmas/green
name = "green christmas costume"
desc = "4:00, wallow in self-pity. 4:30, stare into the abyss. 5:00, solve world hunger, tell no one. 5:30, jazzercize; 6:30, dinner with me. I can't cancel that again. 7:00, wrestle with my self-loathing. I'm booked. Of course, if I bump the loathing to 9, I could still be done in time to lay in bed, stare at the ceiling and slip slowly into madness."
- icon_state = "christmas_green"
+ greyscale_colors = "#1a991a#c4c2c2"
/obj/item/clothing/under/costume/skyrat/christmas/croptop
name = "sexy christmas costume"
desc = "About 550 years since the release of Mariah Carey's \"All I Want For Christmas is You\", society has yet to properly recover from its repercussions. Some still keep a gun as their christmas mantlepiece, just in case she's heard singing on their rooftop late in the night..."
- icon_state = "christmas_crop"
+ icon_state = "christmas_female"
+ greyscale_colors = "#cc0f0f#c4c2c2"
+ greyscale_config = /datum/greyscale_config/chrimbo
+ greyscale_config_worn = /datum/greyscale_config/chrimbo/worn
+ greyscale_config_worn_digi = /datum/greyscale_config/chrimbo/worn/digi
body_parts_covered = CHEST|GROIN
female_sprite_flags = FEMALE_UNIFORM_TOP_ONLY
+ flags_1 = IS_PLAYER_COLORABLE_1
/obj/item/clothing/under/costume/skyrat/christmas/croptop/green
name = "sexy green christmas costume"
desc = "Stupid. Ugly. Out of date. If I can't find something nice to wear I'm not going."
- icon_state = "christmas_crop_green"
+ greyscale_colors = "#1a991a#c4c2c2"
diff --git a/modular_skyrat/master_files/code/modules/mob/living/carbon/human/species_type/snail.dm b/modular_skyrat/master_files/code/modules/mob/living/carbon/human/species_type/snail.dm
index d6ae6c5a103..7d145267b94 100644
--- a/modular_skyrat/master_files/code/modules/mob/living/carbon/human/species_type/snail.dm
+++ b/modular_skyrat/master_files/code/modules/mob/living/carbon/human/species_type/snail.dm
@@ -1,9 +1,95 @@
#define SHELL_TRANSPARENCY_ALPHA 90
+/datum/species/snail
+ eyes_icon = 'modular_skyrat/modules/organs/icons/snail_eyes.dmi' //This is to consolidate our icons and prevent future calamity.
+ mutantliver = /obj/item/organ/internal/liver/snail //This is just a better liver to deal with toxins, it's a thematic thing.
+ mutantheart = /obj/item/organ/internal/heart/snail //This gives them the shell buff where they take less damage from behind, and their heart's more durable.
+ exotic_blood = null
+
+ eyes_icon = 'modular_skyrat/modules/organs/icons/snail_eyes.dmi'
+
+/datum/species/snail/on_species_gain(mob/living/carbon/new_snailperson, datum/species/old_species, pref_load)
+ . = ..()
+ new_snailperson.update_icons()
+
+/datum/species/snail/update_quirk_mail_goodies(mob/living/carbon/human/recipient, datum/quirk/quirk, list/mail_goodies = list())
+ return //This is so that they don't get lube sent in the mail - they don't have exotic blood, so there's no need for it.
+
/obj/item/storage/backpack/snail
/// Whether or not a bluespace anomaly core has been inserted
var/storage_core = FALSE
- obj_flags = IMMUTABLE_SLOW
+ slowdown = 6 // The snail's shell is what's making them slow.
+ obj_flags = IMMUTABLE_SLOW //This should hopefully solve other issues involing it as well.
+ alternate_worn_layer = ABOVE_BODY_FRONT_LAYER //This makes them layer over tails like the cult backpack; some tails really shouldn't appear over them!
+ uses_advanced_reskins = TRUE
+ unique_reskin = list(
+ "Conical Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "coneshell",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "coneshell"
+ ),
+ "Round Shell" = list(
+ RESKIN_ICON = 'icons/obj/storage/backpack.dmi',
+ RESKIN_ICON_STATE = "snailshell",
+ RESKIN_WORN_ICON = 'icons/mob/clothing/back/backpack.dmi',
+ RESKIN_WORN_ICON_STATE = "snailshell"
+ ),
+ "Cinnamon Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "cinnamonshell",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "cinnamonshell"
+ ),
+ "Caramel Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "caramelshell",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "caramelshell"
+ ),
+ "Metal Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "mechashell",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "mechashell"
+ ),
+ "Pyramid Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "pyramidshell",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "pyramidshell"
+ ),
+ "Ivory Pyramid Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "pyramidshellwhite",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "pyramidshellwhite"
+ ),
+ "Spiral Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "spiralshell",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "spiralshell"
+ ),
+ "Ivory Spiral Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "spiralshellwhite",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "spiralshellwhite"
+ ),
+ "Rocky Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "rockshell",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "rockshell"
+ ),
+ "Ivory Rocky Shell" = list(
+ RESKIN_ICON = 'modular_skyrat/master_files/icons/obj/clothing/backpacks.dmi',
+ RESKIN_ICON_STATE = "rockshellwhite",
+ RESKIN_WORN_ICON = 'modular_skyrat/master_files/icons/mob/clothing/back.dmi',
+ RESKIN_WORN_ICON_STATE = "rockshellwhite"
+ ),
+ )
/obj/item/storage/backpack/snail/Initialize(mapload)
. = ..()
@@ -52,7 +138,7 @@
SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
SPECIES_PERK_ICON = "home",
SPECIES_PERK_NAME = "Shellback",
- SPECIES_PERK_DESC = "Snails have a shell fused to their back. While it doesn't offer any protection, it offers great storage. Alt click to change the sprite!",
+ SPECIES_PERK_DESC = "Snails have a shell fused to their back. It offers great storage and most importantly gives them 50% brute damage reduction from behind, or while resting. Alt click to change the sprite!",
),
list(
SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
@@ -60,6 +146,12 @@
SPECIES_PERK_NAME = "Poison Resistance",
SPECIES_PERK_DESC = "Snails have a higher tolerance for poison owing to their robust livers.",
),
+ list(
+ SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
+ SPECIES_PERK_ICON = "heart",
+ SPECIES_PERK_NAME = "Double Hearts",
+ SPECIES_PERK_DESC = "Snails have two hearts, meaning it'll take more to break theirs.",
+ ),
list(
SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
SPECIES_PERK_ICON = "water",
@@ -70,13 +162,13 @@
SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
SPECIES_PERK_ICON = "bone",
SPECIES_PERK_NAME = "Boneless",
- SPECIES_PERK_DESC = "Snails are invertebrates.",
+ SPECIES_PERK_DESC = "Snails are invertebrates, meaning they don't take bone wounds, but are easier to delimb.",
),
list(
SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
SPECIES_PERK_ICON = "crutch",
SPECIES_PERK_NAME = "Sheer Mollusk Speed",
- SPECIES_PERK_DESC = "Snails move incredibly slow while standing. They move much faster while crawling.",
+ SPECIES_PERK_DESC = "Snails move incredibly slow while standing. They move much faster while crawling, and can stick to the floors when the gravity is out.",
),
list(
SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
diff --git a/modular_skyrat/modules/modsuit_armour/modsuit_armour.dm b/modular_skyrat/master_files/code/modules/mod/mod_theme.dm
similarity index 68%
rename from modular_skyrat/modules/modsuit_armour/modsuit_armour.dm
rename to modular_skyrat/master_files/code/modules/mod/mod_theme.dm
index ae6a4b4dc45..11cf9fd6ff3 100644
--- a/modular_skyrat/modules/modsuit_armour/modsuit_armour.dm
+++ b/modular_skyrat/master_files/code/modules/mod/mod_theme.dm
@@ -113,7 +113,38 @@
wound = 15
/datum/mod_theme/security // Security Officer
+ default_skin = "security"
armor_type = /datum/armor/mod_theme_security
+ skins = list(
+ "security" = list(
+ MOD_ICON_OVERRIDE = 'modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_clothing.dmi',
+ MOD_WORN_ICON_OVERRIDE = 'modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_clothing.dmi',
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
/datum/armor/mod_theme_security
melee = 30
@@ -128,6 +159,35 @@
/datum/mod_theme/safeguard // Head of Security
armor_type = /datum/armor/mod_theme_safeguard
+ default_skin = "safeguard"
+ skins = list(
+ "safeguard" = list(
+ MOD_ICON_OVERRIDE = 'modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_clothing.dmi',
+ MOD_WORN_ICON_OVERRIDE = 'modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_clothing.dmi',
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
/datum/armor/mod_theme_safeguard
melee = 40
diff --git a/modular_skyrat/master_files/code/modules/mod/modules/modules_security.dm b/modular_skyrat/master_files/code/modules/mod/modules/modules_security.dm
new file mode 100644
index 00000000000..a0e28ff47d3
--- /dev/null
+++ b/modular_skyrat/master_files/code/modules/mod/modules/modules_security.dm
@@ -0,0 +1,24 @@
+/obj/item/mod/module/pepper_shoulders
+ overlay_icon_file = 'modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_modules.dmi'
+ overlay_state_inactive = "module_pepper"
+ overlay_state_use = "module_pepper_used"
+
+/obj/item/mod/module/pepper_shoulders/generate_worn_overlay(mutable_appearance/standing)
+ if(mod.skin == "security")
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ overlay_state_use = "[initial(overlay_state_use)]-[mod.skin]"
+ else
+ overlay_state_inactive = "[initial(overlay_state_inactive)]"
+ overlay_state_use = "[initial(overlay_state_use)]"
+ return ..()
+
+/obj/item/mod/module/dispenser/mirage
+ overlay_icon_file = 'modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_modules.dmi'
+ overlay_state_inactive = "module_mirage_grenade"
+
+/obj/item/mod/module/dispenser/mirage/generate_worn_overlay(mutable_appearance/standing)
+ if(mod.skin == "security")
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ else
+ overlay_state_inactive = "[initial(overlay_state_inactive)]"
+ return ..()
diff --git a/modular_skyrat/master_files/code/modules/modular_computers/file_system/programs/maintenance/camera.dm b/modular_skyrat/master_files/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
index 1fbfdedb519..c262bac9407 100644
--- a/modular_skyrat/master_files/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
+++ b/modular_skyrat/master_files/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
@@ -1,4 +1,4 @@
// Makes camera app readily available to crew
/datum/computer_file/program/maintenance/camera
- available_on_ntnet = TRUE
- unique_copy = FALSE
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
+
diff --git a/modular_skyrat/master_files/code/modules/research/designs/limbgrower_designs.dm b/modular_skyrat/master_files/code/modules/research/designs/limbgrower_designs.dm
index 419041848bb..9c65cf4502a 100644
--- a/modular_skyrat/master_files/code/modules/research/designs/limbgrower_designs.dm
+++ b/modular_skyrat/master_files/code/modules/research/designs/limbgrower_designs.dm
@@ -19,3 +19,15 @@
id = "snailtongue"
build_path = /obj/item/organ/internal/tongue/snail
category = list(SPECIES_SNAIL, RND_CATEGORY_INITIAL)
+
+/datum/design/liver/snail
+ name = "Snail Liver"
+ id = "snailliver"
+ build_path = /obj/item/organ/internal/liver/snail
+ category = list(SPECIES_SNAIL, RND_CATEGORY_INITIAL)
+
+/datum/design/heart/snail
+ name = "Snail Heart"
+ id = "snailheart"
+ build_path = /obj/item/organ/internal/heart/snail
+ category = list(SPECIES_SNAIL, RND_CATEGORY_INITIAL)
diff --git a/modular_skyrat/master_files/icons/donator/mob/clothing/uniform.dmi b/modular_skyrat/master_files/icons/donator/mob/clothing/uniform.dmi
index 7c324c0b8d1..f83de963a1f 100644
Binary files a/modular_skyrat/master_files/icons/donator/mob/clothing/uniform.dmi and b/modular_skyrat/master_files/icons/donator/mob/clothing/uniform.dmi differ
diff --git a/modular_skyrat/master_files/icons/donator/obj/clothing/uniform.dmi b/modular_skyrat/master_files/icons/donator/obj/clothing/uniform.dmi
index 32e425e06ce..8375633d354 100644
Binary files a/modular_skyrat/master_files/icons/donator/obj/clothing/uniform.dmi and b/modular_skyrat/master_files/icons/donator/obj/clothing/uniform.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/feet.dmi b/modular_skyrat/master_files/icons/mob/clothing/feet.dmi
index 88d930bae4d..563d63d374e 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/feet.dmi and b/modular_skyrat/master_files/icons/mob/clothing/feet.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/feet_digi.dmi b/modular_skyrat/master_files/icons/mob/clothing/feet_digi.dmi
index 056216fca2b..2c5f223792d 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/feet_digi.dmi and b/modular_skyrat/master_files/icons/mob/clothing/feet_digi.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_clothing.dmi b/modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_clothing.dmi
new file mode 100644
index 00000000000..2c69f5a53c8
Binary files /dev/null and b/modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_clothing.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_modules.dmi b/modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_modules.dmi
new file mode 100644
index 00000000000..57139e3c2ac
Binary files /dev/null and b/modular_skyrat/master_files/icons/mob/clothing/modsuit/mod_modules.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/species/teshari/belt.dmi b/modular_skyrat/master_files/icons/mob/clothing/species/teshari/belt.dmi
index eadfe01a6e3..c3ee69dd2a3 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/species/teshari/belt.dmi and b/modular_skyrat/master_files/icons/mob/clothing/species/teshari/belt.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/under/costume.dmi b/modular_skyrat/master_files/icons/mob/clothing/under/costume.dmi
index cbc54a934f5..8439d78c6e1 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/under/costume.dmi and b/modular_skyrat/master_files/icons/mob/clothing/under/costume.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/under/costume_digi.dmi b/modular_skyrat/master_files/icons/mob/clothing/under/costume_digi.dmi
index 9a266a9476b..bd1262a82c8 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/under/costume_digi.dmi and b/modular_skyrat/master_files/icons/mob/clothing/under/costume_digi.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/mod.dmi b/modular_skyrat/master_files/icons/mob/mod.dmi
index a1055a38d5e..7c100fd2e1e 100644
Binary files a/modular_skyrat/master_files/icons/mob/mod.dmi and b/modular_skyrat/master_files/icons/mob/mod.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_clothing.dmi b/modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_clothing.dmi
new file mode 100644
index 00000000000..d39564cef13
Binary files /dev/null and b/modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_clothing.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_modules.dmi b/modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_modules.dmi
new file mode 100644
index 00000000000..a4fd55880f5
Binary files /dev/null and b/modular_skyrat/master_files/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/shoes.dmi b/modular_skyrat/master_files/icons/obj/clothing/shoes.dmi
index c7cd72757db..9bf04a5256a 100644
Binary files a/modular_skyrat/master_files/icons/obj/clothing/shoes.dmi and b/modular_skyrat/master_files/icons/obj/clothing/shoes.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/suits/labcoat.dmi b/modular_skyrat/master_files/icons/obj/clothing/suits/labcoat.dmi
index 8126f117ef1..c63b241a799 100644
Binary files a/modular_skyrat/master_files/icons/obj/clothing/suits/labcoat.dmi and b/modular_skyrat/master_files/icons/obj/clothing/suits/labcoat.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/under/costume.dmi b/modular_skyrat/master_files/icons/obj/clothing/under/costume.dmi
index 2624d4d3dfc..9edd0ae2ade 100644
Binary files a/modular_skyrat/master_files/icons/obj/clothing/under/costume.dmi and b/modular_skyrat/master_files/icons/obj/clothing/under/costume.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/surgery.dmi b/modular_skyrat/master_files/icons/obj/surgery.dmi
index 5a3aabe223b..3ba35f9ad5a 100644
Binary files a/modular_skyrat/master_files/icons/obj/surgery.dmi and b/modular_skyrat/master_files/icons/obj/surgery.dmi differ
diff --git a/modular_skyrat/modules/GAGS/greyscale_configs.dm b/modular_skyrat/modules/GAGS/greyscale_configs.dm
index 21d423040ed..62a8744a7dc 100644
--- a/modular_skyrat/modules/GAGS/greyscale_configs.dm
+++ b/modular_skyrat/modules/GAGS/greyscale_configs.dm
@@ -1093,6 +1093,19 @@
name = "Buttondown with Double-breasted Vest (Worn, Digi)"
icon_file = SHORTS_PANTS_SHIRTS_DIGIFILE
+/datum/greyscale_config/chrimbo
+ name = "christmas_male"
+ icon_file = 'modular_skyrat/master_files/icons/obj/clothing/under/costume.dmi'
+ json_config = 'modular_skyrat/modules/GAGS/json_configs/costumes/chrimbo.json'
+
+/datum/greyscale_config/chrimbo/worn
+ name = "christmas_male (Worn)"
+ icon_file = 'modular_skyrat/master_files/icons/mob/clothing/under/costume.dmi'
+
+/datum/greyscale_config/chrimbo/worn/digi
+ name = "christmas_male (Worn, Digi)"
+ icon_file = 'modular_skyrat/master_files/icons/mob/clothing/under/costume_digi.dmi'
+
//GRAYSCALE MAID COSTUME
/datum/greyscale_config/maid_neck_cover
@@ -1192,6 +1205,19 @@ digi
/datum/greyscale_config/boots/worn/oldvox
icon_file = 'modular_skyrat/modules/GAGS/icons/shoes/shoes_oldvox.dmi'
+/datum/greyscale_config/boots/christmasboots
+ name = "christmas_boots"
+ icon_file = 'modular_skyrat/master_files/icons/obj/clothing/shoes.dmi'
+ json_config = 'modular_skyrat/modules/GAGS/json_configs/shoes/boots/christmas_boots.json'
+
+/datum/greyscale_config/boots/christmasboots/worn
+ name = "christmas_boots (Worn)"
+ icon_file = 'modular_skyrat/master_files/icons/mob/clothing/feet.dmi'
+
+/datum/greyscale_config/boots/christmasboots/worn/digi
+ name = "christmas_boots (Worn, Digi)"
+ icon_file = 'modular_skyrat/master_files/icons/mob/clothing/feet_digi.dmi'
+
// Laceups
/datum/greyscale_config/laceup
diff --git a/modular_skyrat/modules/GAGS/json_configs/costumes/chrimbo.json b/modular_skyrat/modules/GAGS/json_configs/costumes/chrimbo.json
new file mode 100644
index 00000000000..046dac4fabe
--- /dev/null
+++ b/modular_skyrat/modules/GAGS/json_configs/costumes/chrimbo.json
@@ -0,0 +1,41 @@
+{
+ "christmas_male": [
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_male_base",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_male_trim",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_male_belt",
+ "blend_mode": "overlay"
+ }
+ ],
+
+ "christmas_female": [
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_female_base",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_female_trim",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_female_belt",
+ "blend_mode": "overlay"
+ }
+ ]
+}
diff --git a/modular_skyrat/modules/GAGS/json_configs/shoes/boots/christmas_boots.json b/modular_skyrat/modules/GAGS/json_configs/shoes/boots/christmas_boots.json
new file mode 100644
index 00000000000..85c261d4fb4
--- /dev/null
+++ b/modular_skyrat/modules/GAGS/json_configs/shoes/boots/christmas_boots.json
@@ -0,0 +1,16 @@
+{
+ "christmas_boots": [
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_boots_base",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "christmas_boots_trim",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ }
+ ]
+}
diff --git a/modular_skyrat/modules/alternative_job_titles/code/alt_job_titles.dm b/modular_skyrat/modules/alternative_job_titles/code/alt_job_titles.dm
index 11de5521903..5917558c519 100644
--- a/modular_skyrat/modules/alternative_job_titles/code/alt_job_titles.dm
+++ b/modular_skyrat/modules/alternative_job_titles/code/alt_job_titles.dm
@@ -29,6 +29,7 @@
"Trader",
"Off-Duty Crew",
"Off-Duty Staff",
+ "Musician",
)
/datum/job/atmospheric_technician
@@ -85,6 +86,10 @@
"Hydroponicist",
"Mycologist",
"Junior Botanist",
+ "Apiarist",
+ "Apiculturist",
+ "Beekeeper",
+ "Farmer",
)
/datum/job/bouncer
@@ -106,10 +111,12 @@
"Commanding Officer",
"Site Manager",
"Station Commander",
+ "Facility Director",
)
/datum/job/cargo_technician
alt_titles = list(
+ "Cargo Technician",
"Warehouse Technician",
"Commodities Trader",
"Deck Worker",
@@ -173,6 +180,9 @@
"Chef",
"Culinary Artist",
"Sous-Chef",
+ "Baker",
+ "Confectionist",
+ "Pastry Chef",
)
/datum/job/coroner
@@ -376,6 +386,7 @@
/datum/job/shaft_miner
alt_titles = list(
+ "Shaft Miner",
"Union Miner",
"Excavator",
"Drill Technician",
diff --git a/modular_skyrat/modules/ashwalkers/code/buildings/railroad.dm b/modular_skyrat/modules/ashwalkers/code/buildings/railroad.dm
index ead76061da9..a0cad153de6 100644
--- a/modular_skyrat/modules/ashwalkers/code/buildings/railroad.dm
+++ b/modular_skyrat/modules/ashwalkers/code/buildings/railroad.dm
@@ -78,7 +78,7 @@
/obj/vehicle/ridden/rail_cart/Initialize(mapload)
. = ..()
attach_trailer()
- railoverlay = mutable_appearance(icon, "railoverlay", ABOVE_MOB_LAYER, src, GAME_PLANE_UPPER)
+ railoverlay = mutable_appearance(icon, "railoverlay", ABOVE_MOB_LAYER, src)
AddElement(/datum/element/ridable, /datum/component/riding/vehicle/rail_cart)
create_storage(max_total_storage = 21, max_slots = 21)
diff --git a/modular_skyrat/modules/ashwalkers/code/clothing/ash_armour.dm b/modular_skyrat/modules/ashwalkers/code/clothing/ash_armour.dm
index 2afd6fba178..84b7b9b9170 100644
--- a/modular_skyrat/modules/ashwalkers/code/clothing/ash_armour.dm
+++ b/modular_skyrat/modules/ashwalkers/code/clothing/ash_armour.dm
@@ -15,14 +15,6 @@
bomb = 20
bio = 10
-/datum/armor/ash_plates
- melee = 15
- bullet = 25
- laser = 15
- energy = 15
- bomb = 20
- bio = 10
-
/obj/item/clothing/head/ash_headdress
name = "ash headdress"
desc = "A headdress that shows the dominance of the walkers of ash."
@@ -78,14 +70,14 @@
. = ..()
AddComponent(/datum/component/armor_plate, 2, /obj/item/stack/sheet/animalhide/goliath_hide, list(MELEE = 5, BULLET = 2, LASER = 2))
-/obj/item/clothing/suit/ash_plates
+/obj/item/clothing/under/costume/gladiator/ash_walker/ash_plates
name = "ash combat plates"
desc = "A combination of bones and hides, strung together by watcher sinew."
icon = 'modular_skyrat/modules/ashwalkers/icons/ashwalker_clothing.dmi'
worn_icon = 'modular_skyrat/modules/ashwalkers/icons/ashwalker_clothing_mob.dmi'
icon_state = "combat_plates"
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
- armor_type = /datum/armor/ash_plates
+ armor_type = /datum/armor/clothing_under/ash_robes
greyscale_colors = null
greyscale_config = null
@@ -95,18 +87,18 @@
/datum/crafting_recipe/ash_recipe/ash_plates
name = "Ash Combat Plates"
- result = /obj/item/clothing/suit/ash_plates
+ result = /obj/item/clothing/under/costume/gladiator/ash_walker/ash_plates
category = CAT_CLOTHING
-/obj/item/clothing/suit/ash_plates/Initialize(mapload)
+/obj/item/clothing/under/costume/gladiator/ash_walker/ash_plates/Initialize(mapload)
. = ..()
AddComponent(/datum/component/armor_plate, 2, /obj/item/stack/sheet/animalhide/goliath_hide, list(MELEE = 5, BULLET = 2, LASER = 2))
-/obj/item/clothing/suit/ash_plates/decorated
+/obj/item/clothing/under/costume/gladiator/ash_walker/ash_plates/decorated
name = "decorated ash combat plates"
icon_state = "dec_breastplate"
/datum/crafting_recipe/ash_recipe/ash_plates/decorated
name = "Decorated Ash Combat Plates"
- result = /obj/item/clothing/suit/ash_plates/decorated
+ result = /obj/item/clothing/under/costume/gladiator/ash_walker/ash_plates/decorated
category = CAT_CLOTHING
diff --git a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm
index a4ca86c4f18..59a52c87335 100644
--- a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm
+++ b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm
@@ -298,7 +298,7 @@
asked_voters += poll_human
- var/list/yes_voters = poll_candidates("Do you wish to banish [find_banished]?", poll_time = 10 SECONDS, group = asked_voters)
+ var/list/yes_voters = SSpolling.poll_candidates("Do you wish to banish [find_banished]?", poll_time = 10 SECONDS, group = asked_voters)
if(length(yes_voters) < length(asked_voters))
find_banished.balloon_alert_to_viewers("banishment failed!")
diff --git a/modular_skyrat/modules/assault_operatives/code/armaments/_armament_primary.dm b/modular_skyrat/modules/assault_operatives/code/armaments/_armament_primary.dm
index e2ef34a2972..26ee34d9018 100644
--- a/modular_skyrat/modules/assault_operatives/code/armaments/_armament_primary.dm
+++ b/modular_skyrat/modules/assault_operatives/code/armaments/_armament_primary.dm
@@ -93,8 +93,8 @@
/datum/armament_entry/assault_operatives/primary/shotgun_ammo/flechette
item_type = /obj/item/ammo_box/advanced/s12gauge/flechette
-/datum/armament_entry/assault_operatives/primary/shotgun_ammo/hollowpoint
- item_type = /obj/item/ammo_box/advanced/s12gauge/hp
+/datum/armament_entry/assault_operatives/primary/shotgun_ammo/magnum
+ item_type = /obj/item/ammo_box/advanced/s12gauge/magnum
/datum/armament_entry/assault_operatives/primary/shotgun_ammo/beehive
item_type = /obj/item/ammo_box/advanced/s12gauge/beehive
diff --git a/modular_skyrat/modules/black_mesa/code/armaments/_armament_primary.dm b/modular_skyrat/modules/black_mesa/code/armaments/_armament_primary.dm
index f32223088d4..49c78d96917 100644
--- a/modular_skyrat/modules/black_mesa/code/armaments/_armament_primary.dm
+++ b/modular_skyrat/modules/black_mesa/code/armaments/_armament_primary.dm
@@ -70,13 +70,13 @@
/obj/item/storage/box/ammo_box/shotgun_12g
/obj/item/storage/box/ammo_box/shotgun_12g/PopulateContents()
- var/funshell = pick(/obj/item/ammo_box/advanced/s12gauge/pt20,
- /obj/item/ammo_box/advanced/s12gauge/incendiary,
- /obj/item/ammo_box/advanced/s12gauge/flechette,
- /obj/item/ammo_box/advanced/s12gauge/beehive,
- /obj/item/ammo_box/advanced/s12gauge/iceblox,
- /obj/item/ammo_box/advanced/s12gauge/rip,
- )
+ var/funshell = pick(
+ /obj/item/ammo_box/advanced/s12gauge/incendiary,
+ /obj/item/ammo_box/advanced/s12gauge/flechette,
+ /obj/item/ammo_box/advanced/s12gauge/beehive,
+ /obj/item/ammo_box/advanced/s12gauge/antitide,
+ /obj/item/ammo_box/advanced/s12gauge/express,
+ )
new /obj/item/ammo_box/advanced/s12gauge/magnum(src)
new /obj/item/ammo_box/advanced/s12gauge(src)
new funshell(src)
diff --git a/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm b/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm
index 3762bf1d0a8..2127353beff 100644
--- a/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm
+++ b/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm
@@ -21,7 +21,7 @@
worn_head_offset = new(
attached_part = src,
feature_key = OFFSET_HEAD,
- offset_x = list("north" = 1, "south" = 1, "east" = 1, "west" = -1, "northwest" = -1, "southwest" = -1),
+ offset_x = list("north" = 1, "south" = 1, "east" = 1, "west" = -1, "northwest" = -1, "southwest" = -1, "northeast" = 1, "southeast" = 1),
offset_y = list("north" = -4, "south" = -4, "east" = -4, "west" = -4),
)
worn_mask_offset = new(
@@ -52,6 +52,23 @@
)
return ..()
+/obj/item/bodypart/arm/left/mutant/teshari/Initialize(mapload)
+ held_hand_offset = new(
+ attached_part = src,
+ feature_key = OFFSET_HELD,
+ offset_x = list("north" = 0, "south" = 0, "east" = 0, "west" = -6, "northwest" = -6, "southwest" = -6, "northeast" = 0, "southeast" = 0),
+ offset_y = list("north" = -3, "south" = -3, "east" = -3, "west" = -3),
+ )
+ return ..()
+
+/obj/item/bodypart/arm/right/mutant/teshari/Initialize(mapload)
+ held_hand_offset = new(
+ attached_part = src,
+ feature_key = OFFSET_HELD,
+ offset_x = list("north" = 0, "south" = 0, "east" = 6, "west" = 0, "northwest" = 0, "southwest" = 0, "northeast" = 6, "southeast" = 6),
+ offset_y = list("north" = -3, "south" = -3, "east" = -3, "west" = -3),
+ )
+ return ..()
/obj/item/bodypart/arm/left/mutant/teshari
icon_greyscale = BODYPART_ICON_TESHARI
diff --git a/modular_skyrat/modules/cellguns/code/medigun_cells.dm b/modular_skyrat/modules/cellguns/code/medigun_cells.dm
index 15e31a7d20b..274a523a7d4 100644
--- a/modular_skyrat/modules/cellguns/code/medigun_cells.dm
+++ b/modular_skyrat/modules/cellguns/code/medigun_cells.dm
@@ -406,7 +406,7 @@
return
var/mob/living/carbon/wearer = target
- var/obj/item/clothing/gown = new /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown/hardlight
+ var/obj/item/clothing/gown = new /obj/item/clothing/suit/toggle/labcoat/hospitalgown/hardlight
if(wearer.equip_to_slot_if_possible(gown, ITEM_SLOT_OCLOTHING, 1, 1, 1))
wearer.visible_message(span_notice("The [gown] covers [wearer] body"), span_notice("The [gown] wraps around your body, covering you"))
@@ -489,12 +489,12 @@
sparks.start()
//Objects Used by medicells.
-/obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown/hardlight
+/obj/item/clothing/suit/toggle/labcoat/hospitalgown/hardlight
name = "hardlight hospital gown"
desc = "A hospital gown made out of hardlight - you can barely feel it on your body, especially with all the anesthetics."
- icon_state = "lgown"
+ greyscale_colors = "#B2D3CA#B2D3CA#B2D3CA#B2D3CA"
-/obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown/hardlight/dropped(mob/user)
+/obj/item/clothing/suit/toggle/labcoat/hospitalgown/hardlight/dropped(mob/user)
. = ..()
var/mob/living/carbon/wearer = user
diff --git a/modular_skyrat/modules/clock_cult/code/actions/_action.dm b/modular_skyrat/modules/clock_cult/code/actions/_action.dm
index da48b3712cf..bbc1347d3d3 100644
--- a/modular_skyrat/modules/clock_cult/code/actions/_action.dm
+++ b/modular_skyrat/modules/clock_cult/code/actions/_action.dm
@@ -20,7 +20,7 @@
return ..()
-/datum/action/innate/clockcult/quick_bind/Grant(mob/living/recieving_mob)
+/datum/action/innate/clockcult/quick_bind/Grant(mob/living/receiving_mob)
name = scripture.name
desc = scripture.tip
button_icon_state = scripture.button_icon_state
@@ -28,7 +28,7 @@
if(scripture.power_cost)
desc += " Draws [scripture.power_cost]W from the ark per use."
- return ..(recieving_mob)
+ return ..(receiving_mob)
/datum/action/innate/clockcult/quick_bind/Remove(mob/losing_mob)
var/obj/item/clockwork/clockwork_slab/activation_slab = slab_weakref.resolve()
diff --git a/modular_skyrat/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm b/modular_skyrat/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm
index fc58ad369b1..475207b5de2 100644
--- a/modular_skyrat/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm
+++ b/modular_skyrat/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm
@@ -22,7 +22,7 @@
/datum/scripture/marauder/invoke()
- var/list/candidates = poll_ghost_candidates("Do you want to play as a Clockwork Marauder?", ROLE_PAI, FALSE, 100, POLL_IGNORE_CONSTRUCT)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Do you want to play as a Clockwork Marauder?", role = ROLE_PAI, check_jobban = FALSE, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_CONSTRUCT)
if(length(candidates))
selected = pick(candidates)
diff --git a/modular_skyrat/modules/colony_fabricator/code/colony_fabricator.dm b/modular_skyrat/modules/colony_fabricator/code/colony_fabricator.dm
index 6ffa9011b2d..c434228a9bb 100644
--- a/modular_skyrat/modules/colony_fabricator/code/colony_fabricator.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/colony_fabricator.dm
@@ -93,14 +93,14 @@
give_deployable_component()
give_manufacturer_examine()
+/// Adds the deployable component, so that it can be overridden in case that's wanted
+/obj/item/flatpacked_machine/proc/give_deployable_component()
+ AddComponent(/datum/component/deployable, deploy_time, type_to_deploy)
+
/// Adds the manufacturer examine element to the flatpack machine, but can be overridden in the future
/obj/item/flatpacked_machine/proc/give_manufacturer_examine()
AddElement(/datum/element/manufacturer_examine, COMPANY_FRONTIER)
-/// Adds the deployable component, in case we want to change this stuff later
-/obj/item/flatpacked_machine/proc/give_deployable_component()
- AddComponent(/datum/component/deployable, deploy_time, type_to_deploy)
-
/obj/item/borg/apparatus/sheet_manipulator/Initialize(mapload)
. = ..()
storable += /obj/item/flatpacked_machine
diff --git a/modular_skyrat/modules/colony_fabricator/code/construction/doors.dm b/modular_skyrat/modules/colony_fabricator/code/construction/doors.dm
new file mode 100644
index 00000000000..fb84fa473bc
--- /dev/null
+++ b/modular_skyrat/modules/colony_fabricator/code/construction/doors.dm
@@ -0,0 +1,45 @@
+// Shutters
+
+/obj/machinery/door/poddoor/shutters/colony_fabricator
+ name = "prefab shutters"
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/doors/shutter.dmi'
+
+/obj/machinery/door/poddoor/shutters/colony_fabricator/preopen
+ icon_state = "open"
+ density = FALSE
+ opacity = FALSE
+
+/obj/item/flatpacked_machine/shutter_kit
+ name = "prefab shutters parts kit"
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/doors/packed.dmi'
+ icon_state = "shutters_parts"
+ type_to_deploy = /obj/machinery/door/poddoor/shutters/colony_fabricator/preopen
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2,
+ )
+
+// Airlocks
+
+/obj/machinery/door/airlock/colony_prefab
+ name = "prefab airlock"
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/doors/airlock.dmi'
+ overlays_file = 'modular_skyrat/modules/colony_fabricator/icons/doors/overlays.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_colony_prefab
+
+/obj/structure/door_assembly/door_assembly_colony_prefab
+ name = "prefab airlock assembly"
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/doors/airlock.dmi'
+ base_name = "prefab airlock"
+ airlock_type = /obj/machinery/door/airlock/colony_prefab
+ noglass = TRUE
+
+/obj/item/flatpacked_machine/airlock_kit
+ name = "prefab airlock parts kit"
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/doors/packed.dmi'
+ icon_state = "airlock_parts"
+ type_to_deploy = /obj/machinery/door/airlock/colony_prefab
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2,
+ )
diff --git a/modular_skyrat/modules/colony_fabricator/code/construction/turfs.dm b/modular_skyrat/modules/colony_fabricator/code/construction/turfs.dm
new file mode 100644
index 00000000000..766a5b9a401
--- /dev/null
+++ b/modular_skyrat/modules/colony_fabricator/code/construction/turfs.dm
@@ -0,0 +1,176 @@
+// Plastic panel walls, how colony of you
+
+/turf/closed/wall/prefab_plastic
+ name = "prefabricated wall"
+ desc = "A conservatively built metal frame with plastic paneling covering a thin air-seal layer. \
+ It's a little unnerving, but its better than nothing at all."
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/prefab_wall.dmi'
+ icon_state = "prefab-0"
+ base_icon_state = "prefab"
+ can_engrave = FALSE
+ girder_type = null
+ hardness = 70
+ slicing_duration = 5 SECONDS
+ sheet_type = /obj/item/stack/sheet/plastic_wall_panel
+ sheet_amount = 1
+
+GLOBAL_LIST_INIT(plastic_wall_panel_recipes, list(
+ new/datum/stack_recipe("prefabricated wall", /turf/closed/wall/prefab_plastic, time = 3 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_STRUCTURE), \
+ new/datum/stack_recipe("prefabricated window", /obj/structure/window/reinforced/colony_fabricator, time = 0.5 SECONDS, on_solid_ground = TRUE, check_direction = TRUE, is_fulltile = TRUE, category = CAT_WINDOWS), \
+ ))
+
+/obj/item/stack/sheet/plastic_wall_panel
+ name = "plastic panels"
+ singular_name = "plastic panel"
+ desc = "What better material to make the walls of your soon to be home out of than sheets of flimsy plastic? \
+ Metal? What are you talking about, metal walls, in this economy? May also be used to make structures other \
+ than walls."
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/tiles_item.dmi'
+ icon_state = "sheet-plastic"
+ inhand_icon_state = "sheet-plastic"
+ mats_per_unit = list(
+ /datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT,
+ /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT,
+ )
+ has_unique_girder = TRUE
+ material_type = /datum/material/plastic
+ merge_type = /obj/item/stack/sheet/plastic_wall_panel
+ walltype = /turf/closed/wall/prefab_plastic
+
+/obj/item/stack/sheet/plastic_wall_panel/examine(mob/user)
+ . = ..()
+ . += span_notice("You can build a prefabricated wall by right clicking on an empty floor.")
+
+/obj/item/stack/sheet/plastic_wall_panel/afterattack_secondary(atom/target, mob/user, proximity_flag, click_parameters)
+ if(!isopenturf(target))
+ return SECONDARY_ATTACK_CONTINUE_CHAIN
+ var/turf/open/build_on = target
+ if(!user.Adjacent(build_on))
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(isgroundlessturf(build_on))
+ user.balloon_alert(user, "can't place it here!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(build_on.is_blocked_turf())
+ user.balloon_alert(user, "something is blocking the tile!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(get_amount() < 1)
+ user.balloon_alert(user, "not enough material!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(!do_after(user, 3 SECONDS, build_on))
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(build_on.is_blocked_turf())
+ user.balloon_alert(user, "something is blocking the tile!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(!use(1))
+ user.balloon_alert(user, "not enough material!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ new walltype(build_on)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/item/stack/sheet/plastic_wall_panel/get_main_recipes()
+ . = ..()
+ . += GLOB.plastic_wall_panel_recipes
+
+/obj/item/stack/sheet/plastic_wall_panel/ten
+ amount = 10
+
+/obj/item/stack/sheet/plastic_wall_panel/fifty
+ amount = 50
+
+// Stacks of floor tiles
+
+/obj/item/stack/tile/catwalk_tile/colony_lathe
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/tiles_item.dmi'
+ icon_state = "prefab_catwalk"
+ mats_per_unit = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT)
+ turf_type = /turf/open/floor/catwalk_floor/colony_fabricator
+ merge_type = /obj/item/stack/tile/catwalk_tile/colony_lathe
+ tile_reskin_types = null
+
+/obj/item/stack/tile/catwalk_tile/colony_lathe/lathe_spawn
+ amount = 4
+
+/obj/item/stack/tile/iron/colony
+ name = "prefab floor tiles"
+ singular_name = "prefab floor tile"
+ desc = "A stack of large floor tiles that are a common sight in frontier colonies and prefab buildings."
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/tiles_item.dmi'
+ icon_state = "colony_grey"
+ turf_type = /turf/open/floor/iron/colony
+ merge_type = /obj/item/stack/tile/iron/colony
+ tile_reskin_types = list(
+ /obj/item/stack/tile/iron/colony,
+ /obj/item/stack/tile/iron/colony/texture,
+ /obj/item/stack/tile/iron/colony/bolts,
+ /obj/item/stack/tile/iron/colony/white,
+ /obj/item/stack/tile/iron/colony/white/texture,
+ /obj/item/stack/tile/iron/colony/white/bolts,
+ )
+
+/obj/item/stack/tile/iron/colony/lathe_spawn
+ amount = 4
+
+// Grated floor tile, for seeing wires under
+
+/turf/open/floor/catwalk_floor/colony_fabricator
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/tiles.dmi'
+ icon_state = "prefab_above"
+ catwalk_type = "prefab"
+ baseturfs = /turf/open/floor/plating
+ floor_tile = /obj/item/stack/tile/catwalk_tile/colony_lathe
+
+// "Normal" floor tiles
+
+/obj/item/stack/tile/iron/colony/texture
+ icon_state = "colony_grey_texture"
+ turf_type = /turf/open/floor/iron/colony/texture
+
+/obj/item/stack/tile/iron/colony/bolts
+ icon_state = "colony_grey_bolts"
+ turf_type = /turf/open/floor/iron/colony/bolts
+
+/turf/open/floor/iron/colony
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/tiles.dmi'
+ icon_state = "colony_grey"
+ base_icon_state = "colony_grey"
+ floor_tile = /obj/item/stack/tile/iron/colony
+ tiled_dirt = FALSE
+
+/turf/open/floor/iron/colony/texture
+ icon_state = "colony_grey_texture"
+ base_icon_state = "colony_grey_texture"
+ floor_tile = /obj/item/stack/tile/iron/colony/texture
+
+/turf/open/floor/iron/colony/bolts
+ icon_state = "colony_grey_bolts"
+ base_icon_state = "colony_grey_bolts"
+ floor_tile = /obj/item/stack/tile/iron/colony/bolts
+
+// White variants of the above tiles
+
+/obj/item/stack/tile/iron/colony/white
+ icon_state = "colony_white"
+ turf_type = /turf/open/floor/iron/colony/white
+
+/obj/item/stack/tile/iron/colony/white/texture
+ icon_state = "colony_white_texture"
+ turf_type = /turf/open/floor/iron/colony/white/texture
+
+/obj/item/stack/tile/iron/colony/white/bolts
+ icon_state = "colony_white_bolts"
+ turf_type = /turf/open/floor/iron/colony/white/bolts
+
+/turf/open/floor/iron/colony/white
+ icon_state = "colony_white"
+ base_icon_state = "colony_white"
+ floor_tile = /obj/item/stack/tile/iron/colony/white
+
+/turf/open/floor/iron/colony/white/texture
+ icon_state = "colony_white_texture"
+ base_icon_state = "colony_white_texture"
+ floor_tile = /obj/item/stack/tile/iron/colony/white/texture
+
+/turf/open/floor/iron/colony/white/bolts
+ icon_state = "colony_white_bolts"
+ base_icon_state = "colony_white_bolts"
+ floor_tile = /obj/item/stack/tile/iron/colony/white/bolts
diff --git a/modular_skyrat/modules/colony_fabricator/code/construction/windows.dm b/modular_skyrat/modules/colony_fabricator/code/construction/windows.dm
new file mode 100644
index 00000000000..e2848ad7961
--- /dev/null
+++ b/modular_skyrat/modules/colony_fabricator/code/construction/windows.dm
@@ -0,0 +1,11 @@
+/obj/structure/window/reinforced/colony_fabricator
+ name = "prefabricated window"
+ desc = "A conservatively built metal frame with a thick sheet of space-grade glass slotted into it."
+ icon = 'modular_skyrat/modules/colony_fabricator/icons/prefab_window.dmi'
+ icon_state = "prefab-0"
+ base_icon_state = "prefab"
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE
+ glass_type = /obj/item/stack/sheet/plastic_wall_panel
+ glass_amount = 1
diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/construction.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/construction.dm
new file mode 100644
index 00000000000..14e26f0e69c
--- /dev/null
+++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/construction.dm
@@ -0,0 +1,106 @@
+// Look, I had to make its name start with A so it'd be top of the list, fight me
+
+#define FABRICATOR_SUBCATEGORY_STRUCTURES "/Autofab Structures"
+
+// Techweb node that shouldnt show up anywhere ever specifically for the fabricator to work with
+
+/datum/techweb_node/colony_fabricator_structures
+ id = "colony_fabricator_structures"
+ display_name = "Colony Fabricator Structure Designs"
+ description = "Contains all of the colony fabricator's structure designs."
+ design_ids = list(
+ "prefab_airlock_kit",
+ "prefab_shutters_kit",
+ "prefab_floor_tile",
+ "prefab_cat_floor_tile",
+ "colony_fab_plastic_wall_panel",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000000000000000) // God save you
+ hidden = TRUE
+ show_on_wiki = FALSE
+ starting_node = TRUE
+
+// Airlock kit
+
+/datum/design/prefab_airlock_kit
+ name = "Prefab Airlock"
+ id = "prefab_airlock_kit"
+ build_type = COLONY_FABRICATOR
+ materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2,
+ )
+ build_path = /obj/item/flatpacked_machine/airlock_kit
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_CONSTRUCTION + FABRICATOR_SUBCATEGORY_STRUCTURES,
+ )
+ construction_time = 30 SECONDS
+
+// Shutters kit
+
+/datum/design/prefab_shutters_kit
+ name = "Prefab Shutters"
+ id = "prefab_shutters_kit"
+ build_type = COLONY_FABRICATOR
+ materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2,
+ )
+ build_path = /obj/item/flatpacked_machine/shutter_kit
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_CONSTRUCTION + FABRICATOR_SUBCATEGORY_STRUCTURES,
+ )
+ construction_time = 30 SECONDS
+
+// Fancy floor tiles
+
+/datum/design/prefab_floor_tile
+ name = "Prefab Floor Tile"
+ id = "prefab_floor_tile"
+ build_type = COLONY_FABRICATOR
+ materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT,
+ )
+ build_path = /obj/item/stack/tile/iron/colony/lathe_spawn
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_CONSTRUCTION + FABRICATOR_SUBCATEGORY_STRUCTURES,
+ )
+ construction_time = 15 SECONDS
+
+// Fancy catwalk floor tiles
+
+/datum/design/prefab_cat_floor_tile
+ name = "Prefab Catwalk Plating"
+ id = "prefab_cat_floor_tile"
+ build_type = COLONY_FABRICATOR
+ materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT,
+ )
+ build_path = /obj/item/stack/tile/catwalk_tile/colony_lathe/lathe_spawn
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_CONSTRUCTION + FABRICATOR_SUBCATEGORY_STRUCTURES,
+ )
+ construction_time = 15 SECONDS
+
+// Plastic wall panels, twice the wall for the same price in plastic, efficient!
+
+/datum/design/colony_fab_plastic_wall_panel
+ name = "Plastic Paneling x10"
+ id = "colony_fab_plastic_wall_panel"
+ build_type = COLONY_FABRICATOR
+ materials = list(
+ /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 5
+ )
+ build_path = /obj/item/stack/sheet/plastic_wall_panel/ten
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_CONSTRUCTION + FABRICATOR_SUBCATEGORY_STRUCTURES,
+ )
+ construction_time = 15 SECONDS
+
+#undef FABRICATOR_SUBCATEGORY_STRUCTURES
diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/construction.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/construction.dm
index 71eed6b71f8..1a83a810cc2 100644
--- a/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/construction.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/construction.dm
@@ -24,18 +24,6 @@
. = ..()
build_type |= COLONY_FABRICATOR
-/datum/design/condenser/New()
- . = ..()
- build_type |= COLONY_FABRICATOR
-
-/datum/design/health_sensor/New()
- . = ..()
- build_type |= COLONY_FABRICATOR
-
-/datum/design/igniter/New()
- . = ..()
- build_type |= COLONY_FABRICATOR
-
/datum/design/infrared_emitter/New()
. = ..()
build_type |= COLONY_FABRICATOR
@@ -52,10 +40,6 @@
. = ..()
build_type |= COLONY_FABRICATOR
-/datum/design/voice_analyzer/New()
- . = ..()
- build_type |= COLONY_FABRICATOR
-
/datum/design/camera_assembly/New()
. = ..()
build_type |= COLONY_FABRICATOR
diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/tools.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/tools.dm
index 64d5e931de7..92a68bade56 100644
--- a/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/tools.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/fabricator_flag_additions/tools.dm
@@ -64,6 +64,10 @@
. = ..()
build_type |= COLONY_FABRICATOR
+/datum/design/rcd_ammo/New()
+ . = ..()
+ build_type |= COLONY_FABRICATOR
+
/datum/design/light_replacer/New()
. = ..()
build_type |= COLONY_FABRICATOR
diff --git a/modular_skyrat/modules/colony_fabricator/code/tools/tools.dm b/modular_skyrat/modules/colony_fabricator/code/tools/tools.dm
index 554b8d3d3ff..604bf109b0e 100644
--- a/modular_skyrat/modules/colony_fabricator/code/tools/tools.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/tools/tools.dm
@@ -134,12 +134,7 @@
but it still gets the job done and chances are you printed this bad boy off for free."
icon = 'modular_skyrat/modules/colony_fabricator/icons/tools.dmi'
icon_state = "arc_welder"
- usesound = list(
- 'modular_skyrat/modules/colony_fabricator/sound/arc_furnace/arc_furnace_mid_1.wav',
- 'modular_skyrat/modules/colony_fabricator/sound/arc_furnace/arc_furnace_mid_2.wav',
- 'modular_skyrat/modules/colony_fabricator/sound/arc_furnace/arc_furnace_mid_3.wav',
- 'modular_skyrat/modules/colony_fabricator/sound/arc_furnace/arc_furnace_mid_4.wav',
- )
+ usesound = 'modular_skyrat/modules/colony_fabricator/sound/arc_welder/arc_welder.ogg'
light_range = 2
light_power = 0.75
toolspeed = 1
diff --git a/modular_skyrat/modules/colony_fabricator/icons/doors/airlock.dmi b/modular_skyrat/modules/colony_fabricator/icons/doors/airlock.dmi
new file mode 100644
index 00000000000..a5afd63d419
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/doors/airlock.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/icons/doors/overlays.dmi b/modular_skyrat/modules/colony_fabricator/icons/doors/overlays.dmi
new file mode 100644
index 00000000000..4a039a486a6
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/doors/overlays.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/icons/doors/packed.dmi b/modular_skyrat/modules/colony_fabricator/icons/doors/packed.dmi
new file mode 100644
index 00000000000..1f3dacc63d9
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/doors/packed.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/icons/doors/shutter.dmi b/modular_skyrat/modules/colony_fabricator/icons/doors/shutter.dmi
new file mode 100644
index 00000000000..f29dc998999
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/doors/shutter.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/icons/prefab_wall.dmi b/modular_skyrat/modules/colony_fabricator/icons/prefab_wall.dmi
new file mode 100644
index 00000000000..e1aec0f273a
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/prefab_wall.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/icons/prefab_window.dmi b/modular_skyrat/modules/colony_fabricator/icons/prefab_window.dmi
new file mode 100644
index 00000000000..7845026875d
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/prefab_window.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/icons/tiles.dmi b/modular_skyrat/modules/colony_fabricator/icons/tiles.dmi
new file mode 100644
index 00000000000..068107aa0fd
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/tiles.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/icons/tiles_item.dmi b/modular_skyrat/modules/colony_fabricator/icons/tiles_item.dmi
new file mode 100644
index 00000000000..0e76bd25d2c
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/icons/tiles_item.dmi differ
diff --git a/modular_skyrat/modules/colony_fabricator/sound/arc_welder/arc_welder.ogg b/modular_skyrat/modules/colony_fabricator/sound/arc_welder/arc_welder.ogg
new file mode 100644
index 00000000000..8c76738f0f4
Binary files /dev/null and b/modular_skyrat/modules/colony_fabricator/sound/arc_welder/arc_welder.ogg differ
diff --git a/modular_skyrat/modules/colony_fabricator/sound/attributions.txt b/modular_skyrat/modules/colony_fabricator/sound/attributions.txt
index e9c05e68c01..41f5a6b8600 100644
--- a/modular_skyrat/modules/colony_fabricator/sound/attributions.txt
+++ b/modular_skyrat/modules/colony_fabricator/sound/attributions.txt
@@ -8,3 +8,7 @@ modular_skyrat/modules/colony_fabricator/sound/fabricator/fabricator_start.wav
The following sounds are from https://pixabay.com/sound-effects/wall-air-conditioner-43901/
modular_skyrat/modules/colony_fabricator/sound/conditioner/conditioner_(1 - 4).wav
+
+The following sounds are from https://pixabay.com/sound-effects/welder-3-54547/
+
+modular_skyrat/modules/colony_fabricator/sound/arc_welder/arc_welder.ogg
diff --git a/modular_skyrat/modules/company_imports/code/armament_datums/sol_defense.dm b/modular_skyrat/modules/company_imports/code/armament_datums/sol_defense.dm
index 9a0e7ed5d41..70ab3d0cf40 100644
--- a/modular_skyrat/modules/company_imports/code/armament_datums/sol_defense.dm
+++ b/modular_skyrat/modules/company_imports/code/armament_datums/sol_defense.dm
@@ -2,6 +2,18 @@
category = SOL_DEFENSE_DEFENSE_NAME
company_bitflag = CARGO_COMPANY_SOL_DEFENSE
+// Beautiful SolFed clothing
+
+/datum/armament_entry/company_import/sol_defense/clothing
+ subcategory = "Surplus Clothing"
+ cost = PAYCHECK_CREW
+
+/datum/armament_entry/company_import/sol_defense/clothing/peacekeeper
+ item_type = /obj/item/clothing/under/sol_peacekeeper
+
+/datum/armament_entry/company_import/sol_defense/clothing/emt
+ item_type = /obj/item/clothing/under/sol_emt
+
// Armor vests for protecting against bullets
/datum/armament_entry/company_import/sol_defense/armor
@@ -20,12 +32,11 @@
/datum/armament_entry/company_import/sol_defense/armor/sf_soft_vest
item_type = /obj/item/clothing/suit/armor/sf_peacekeeper
-/datum/armament_entry/company_import/sol_defense/armor/normal_vest
- name = "type I vest - normal"
- item_type = /obj/item/clothing/suit/armor/vest/alt
+/datum/armament_entry/company_import/sol_defense/armor/flak_jacket
+ item_type = /obj/item/clothing/suit/armor/vest/det_suit/sol
/datum/armament_entry/company_import/sol_defense/armor/slim_vest
- name = "type I vest - slim"
+ name = "type I vest"
item_type = /obj/item/clothing/suit/armor/vest
/datum/armament_entry/company_import/sol_defense/armor_hardened
diff --git a/modular_skyrat/modules/company_imports/code/armament_datums/vitezstvi_ammo.dm b/modular_skyrat/modules/company_imports/code/armament_datums/vitezstvi_ammo.dm
index fc1a43427c2..339cd458073 100644
--- a/modular_skyrat/modules/company_imports/code/armament_datums/vitezstvi_ammo.dm
+++ b/modular_skyrat/modules/company_imports/code/armament_datums/vitezstvi_ammo.dm
@@ -134,32 +134,36 @@
/* //BUBBER EDIT REMOVAL BEGIN
/datum/armament_entry/company_import/vitezstvi/shot_shells/slugs
item_type = /obj/item/ammo_box/advanced/s12gauge
- description = "A box of 15 slug shells, large singular shots that pack a punch."
/datum/armament_entry/company_import/vitezstvi/shot_shells/buckshot
item_type = /obj/item/ammo_box/advanced/s12gauge/buckshot
- description = "A box of 15 buckshot shells, a modest spread of weaker projectiles."
/datum/armament_entry/company_import/vitezstvi/shot_shells/beanbag_slugs
item_type = /obj/item/ammo_box/advanced/s12gauge/bean
- description = "A box of 15 beanbag slug shells, large singular beanbags that pack a less-lethal punch."
/datum/armament_entry/company_import/vitezstvi/shot_shells/rubbershot
item_type = /obj/item/ammo_box/advanced/s12gauge/rubber
- description = "A box of 15 rubbershot shells, a modest spread of weaker less-lethal projectiles."
/datum/armament_entry/company_import/vitezstvi/shot_shells/magnum_buckshot
item_type = /obj/item/ammo_box/advanced/s12gauge/magnum
- description = "A box of 15 magnum buckshot shells, a wider spread of larger projectiles."
/datum/armament_entry/company_import/vitezstvi/shot_shells/express_buckshot
item_type = /obj/item/ammo_box/advanced/s12gauge/express
- description = "A box of 15 express buckshot shells, a tighter spread of smaller projectiles."
-*/ //BUBBER EDIT END
+/datum/armament_entry/company_import/vitezstvi/shot_shells/hunter_slug
+ item_type = /obj/item/ammo_box/advanced/s12gauge/hunter
+
+/datum/armament_entry/company_import/vitezstvi/shot_shells/flechettes
+ item_type = /obj/item/ammo_box/advanced/s12gauge/flechette
+
+/datum/armament_entry/company_import/vitezstvi/shot_shells/hornet_nest
+ item_type = /obj/item/ammo_box/advanced/s12gauge/beehive
+
+/datum/armament_entry/company_import/vitezstvi/shot_shells/stardust
+ item_type = /obj/item/ammo_box/advanced/s12gauge/antitide
+*/ //BUBBER EDIT END
/datum/armament_entry/company_import/vitezstvi/shot_shells/confetti
item_type = /obj/item/ammo_box/advanced/s12gauge/honk
- description = "A box of 35 confetti shells, firing a spread of harmless confetti everywhere, yippie!"
// Boxes of kiboko launcher ammo
diff --git a/modular_skyrat/modules/contractor/code/datums/contract.dm b/modular_skyrat/modules/contractor/code/datums/contract.dm
deleted file mode 100644
index 8a5f702b6c5..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contract.dm
+++ /dev/null
@@ -1,318 +0,0 @@
-#define RANSOM_LOWER 75
-#define RANSOM_UPPER 150
-#define CONTRACTOR_RANSOM_CUT 0.35
-
-/datum/syndicate_contract
- /// Unique ID tied to the contract
- var/id = 0
- /// If the contract is the contractor's current one
- var/status = CONTRACT_STATUS_INACTIVE
- /// Reference to the objective datum
- var/datum/objective/contract/contract = new()
- /// Target's job
- var/target_rank
- /// A number in multiples of 100, anywhere from 7500 credits to 15000, station cost when someone is kidnapped
- var/ransom = 0
- /// TC payout size, will be small, medium, or large.
- var/payout_type
- /// Mad-libs style wanted message, just flavor.
- var/wanted_message
- /// List of the items the victim had on them prior to kidnapping.
- var/list/victim_belongings = list()
-
-/datum/syndicate_contract/New(contract_owner, blacklist, type = CONTRACT_PAYOUT_SMALL)
- contract.owner = contract_owner
- payout_type = type
-
- generate(blacklist)
-
-/// Generation of the contract, called on New()
-/datum/syndicate_contract/proc/generate(blacklist)
- contract.find_target(null, blacklist)
-
- var/datum/record/crew/record
- if (contract.target)
- record = find_record(contract.target.name)
-
- if (record)
- target_rank = record.rank
- else
- target_rank = "Unknown"
-
- if (payout_type == CONTRACT_PAYOUT_LARGE)
- contract.payout_bonus = rand(9,13)
- else if (payout_type == CONTRACT_PAYOUT_MEDIUM)
- contract.payout_bonus = rand(6,8)
- else
- contract.payout_bonus = rand(2,4)
-
- contract.payout = rand(1, 2)
- contract.generate_dropoff()
-
- ransom = 100 * rand(RANSOM_LOWER, RANSOM_UPPER)
-
- var/base = pick_list(WANTED_FILE, "basemessage")
- var/verb_string = pick_list(WANTED_FILE, "verb")
- var/noun = pick_list_weighted(WANTED_FILE, "noun")
- var/location = pick_list_weighted(WANTED_FILE, "location")
- wanted_message = "[base] [verb_string] [noun] [location]."
-
-/// Handler to find a valid turn and launch victim collector
-/datum/syndicate_contract/proc/handle_extraction(mob/living/user)
- if(!(contract.target && contract.dropoff_check(user, contract.target.current)))
- return FALSE
-
- var/turf/free_location = find_obstruction_free_location(3, user, contract.dropoff)
-
- if(!free_location)
- return FALSE
-
- launch_extraction_pod(free_location)
- return TRUE
-
-
-/// Launch the pod to collect our victim.
-/datum/syndicate_contract/proc/launch_extraction_pod(turf/empty_pod_turf)
- var/obj/structure/closet/supplypod/extractionpod/empty_pod = new()
- empty_pod.contract_hub = contract.owner?.opposing_force?.contractor_hub
- empty_pod.tied_contract = src
- empty_pod.recieving = TRUE
-
- RegisterSignal(empty_pod, COMSIG_ATOM_ENTERED, PROC_REF(enter_check))
-
- empty_pod.stay_after_drop = TRUE
- empty_pod.reversing = TRUE
- empty_pod.explosionSize = list(0,0,0,1)
- empty_pod.leavingSound = 'sound/effects/podwoosh.ogg'
-
- new /obj/effect/pod_landingzone(empty_pod_turf, empty_pod)
-
-/datum/syndicate_contract/proc/enter_check(datum/source, mob/living/sent_mob)
- SIGNAL_HANDLER
- if(!istype(source, /obj/structure/closet/supplypod/extractionpod))
- return
- if(!istype(sent_mob))
- return
- var/datum/opposing_force/opfor_data = contract.owner.opposing_force
-
- if (sent_mob == contract.target.current)
- opfor_data.contractor_hub.contract_TC_to_redeem += contract.payout
- opfor_data.contractor_hub.contracts_completed += 1
-
- if (sent_mob.stat != DEAD)
- opfor_data.contractor_hub.contract_TC_to_redeem += contract.payout_bonus
-
- status = CONTRACT_STATUS_COMPLETE
-
- if (opfor_data.contractor_hub.current_contract == src)
- opfor_data.contractor_hub.current_contract = null
-
- opfor_data.contractor_hub.contract_rep += 2
- else
- status = CONTRACT_STATUS_ABORTED // Sending a sent_mob that wasn't even yours is as good as just aborting it
-
- if (opfor_data.contractor_hub.current_contract == src)
- opfor_data.contractor_hub.current_contract = null
-
- if(iscarbon(sent_mob))
- for(var/obj/item/sent_mob_item in sent_mob)
- if (ishuman(sent_mob))
- var/mob/living/carbon/human/sent_mob_human = sent_mob
- if(sent_mob_item == sent_mob_human.w_uniform)
- continue //So all they're left with are shoes and uniform.
- if(sent_mob_item == sent_mob_human.shoes)
- continue
-
-
- sent_mob.transferItemToLoc(sent_mob_item)
- victim_belongings.Add(sent_mob_item)
-
- var/obj/structure/closet/supplypod/extractionpod/pod = source
- pod.recieving = FALSE
-
- // Handle the pod returning
- pod.startExitSequence(pod)
-
- if (ishuman(sent_mob))
- var/mob/living/carbon/human/sent_mob_human = sent_mob
-
- // After we remove items, at least give them what they need to live.
- sent_mob_human.dna.species.give_important_for_life(sent_mob_human)
-
- // After pod is sent we start the victim narrative/heal.
- INVOKE_ASYNC(src, PROC_REF(handle_victim_experience), sent_mob)
-
- // This is slightly delayed because of the sleep calls above to handle the narrative.
- // We don't want to tell the station instantly.
- var/points_to_check
- var/datum/bank_account/bank = SSeconomy.get_dep_account(ACCOUNT_CAR)
- if(bank)
- points_to_check = bank.account_balance
- if(points_to_check >= ransom)
- bank.adjust_money(-ransom)
- else
- bank.adjust_money(-points_to_check)
-
- priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \
- As is policy we've taken a portion of the station's funds to offset the overall cost.", null, null, null, "Nanotrasen Asset Protection")
-
- addtimer(CALLBACK(src, PROC_REF(finish_enter)), 3 SECONDS)
-
-/// Called when person is finished shoving in, awards ransome money
-/datum/syndicate_contract/proc/finish_enter()
- // Pay contractor their portion of ransom
- if(status != CONTRACT_STATUS_COMPLETE)
- return
-
- var/obj/item/card/id/owner_id = contract.owner.current?.get_idcard(TRUE)
-
- if(owner_id?.registered_account.account_id) // why do we check for account id? because apparently unset agent IDs have existing bank accounts that can't be accessed. this is suboptimal
- owner_id.registered_account.adjust_money(ransom * CONTRACTOR_RANSOM_CUT)
-
- owner_id.registered_account.bank_card_talk("We've processed the ransom, agent. Here's your cut - your balance is now \
- [owner_id.registered_account.account_balance] credits.", TRUE)
- else
- to_chat(contract.owner.current, span_notice("A briefcase appears at your feet!"))
- var/obj/item/storage/briefcase/secure/case = new(get_turf(contract.owner.current))
- for(var/i in 1 to (round((ransom * CONTRACTOR_RANSOM_CUT) / 1000))) // Gets slightly less/more but whatever
- new /obj/item/stack/spacecash/c1000(case)
-
-/// They're off to holding - handle the return timer and give some text about what's going on.
-/datum/syndicate_contract/proc/handle_victim_experience(mob/living/target)
- // Ship 'em back - dead or alive, 4 minutes wait.
- // Even if they weren't the target, we're still treating them the same.
- addtimer(CALLBACK(src, PROC_REF(return_victim), target), 4 MINUTES)
-
- if (target.stat == DEAD)
- return
- // Heal them up - gets them out of crit/soft crit. If omnizine is removed in the future, this needs to be replaced with a
- // method of healing them, consequence free, to a reasonable amount of health.
- victim_stage_one(target)
-
-/// Adds omnizine and begins the victim handling
-/datum/syndicate_contract/proc/victim_stage_one(mob/living/target)
- target.reagents.add_reagent(/datum/reagent/medicine/omnizine, 20)
-
- target.flash_act()
- target.adjust_confusion(10 SECONDS)
- target.set_eye_blur_if_lower(10 SECONDS)
- to_chat(target, span_warning("You feel strange..."))
- addtimer(CALLBACK(src, PROC_REF(victim_stage_two), target), 6 SECONDS)
-
-/// Continued victim handling
-/datum/syndicate_contract/proc/victim_stage_two(mob/living/target)
- var/list/parts_to_fuck_up = list(
- BODY_ZONE_L_ARM,
- BODY_ZONE_R_ARM,
- BODY_ZONE_L_LEG,
- BODY_ZONE_R_LEG,
- )
-
- to_chat(target, span_warning("That pod did something to you..."))
- target.set_dizzy(70 SECONDS)
-
- for(var/i in 1 to 2)
- var/obj/item/bodypart/limb = target.get_bodypart(pick_n_take(parts_to_fuck_up))
- var/min_wound = limb.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_SEVERE, return_value_if_no_wound = 40)
- var/max_wound = limb.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_CRITICAL, return_value_if_no_wound = 60)
- limb.receive_damage(brute = WOUND_MINIMUM_DAMAGE, wound_bonus = rand(min_wound, max_wound))
- target.update_damage_overlays()
-
- addtimer(CALLBACK(src, PROC_REF(victim_stage_three), target), 6 SECONDS)
-
-/// Continued victim handling, flashes them as well
-/datum/syndicate_contract/proc/victim_stage_three(mob/living/target)
- to_chat(target, span_warning("Your head pounds... It feels like it's going to burst out your skull!"))
- target.flash_act()
- target.adjust_confusion(20 SECONDS)
- target.set_eye_blur_if_lower(6 SECONDS)
- addtimer(CALLBACK(src, PROC_REF(victim_stage_four), target), 3 SECONDS)
-
-/// Continued victim handling
-/datum/syndicate_contract/proc/victim_stage_four(mob/living/target)
- to_chat(target, span_warning("Your head pounds..."))
-
- if(iscarbon(target))
- var/mob/living/carbon/carbon_target = target
- switch(rand(1, 100))
- if(1 to 45)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
-
- if(46 to 73)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
-
- if(74 to 94)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY)
-
- if(75 to 100)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY)
-
- addtimer(CALLBACK(src, PROC_REF(victim_stage_five), target), 10 SECONDS)
-
-/// Continued victim handling, some unconsciousness
-/datum/syndicate_contract/proc/victim_stage_five(mob/living/target)
- target.flash_act()
- target.Unconscious(200)
- to_chat(target, span_hypnophrase(span_reallybig("A million voices echo in your head... \"Your mind held many valuable secrets - \
- we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
- so it's only a matter of time before we ship you back...\"")))
- target.set_eye_blur_if_lower(20 SECONDS)
- target.set_dizzy_if_lower(30 SECONDS)
- target.adjust_confusion(20 SECONDS)
-
-/// We're returning the victim
-/datum/syndicate_contract/proc/return_victim(mob/living/target)
- var/list/possible_drop_loc = list()
-
- for(var/turf/possible_drop in contract.dropoff.contents)
- if(is_safe_turf(possible_drop))
- possible_drop_loc += possible_drop
- if(!length(possible_drop_loc)) //Prioritize safe tiles first, then unsafe
- for(var/turf/open/possible_unsafe_drop in contract.dropoff.contents)
- possible_drop_loc += possible_unsafe_drop
-
- if (length(possible_drop_loc))
- var/pod_rand_loc = rand(1, length(possible_drop_loc))
-
- var/obj/structure/closet/supplypod/return_pod = new()
- return_pod.bluespace = TRUE
- return_pod.explosionSize = list(0,0,0,0)
- return_pod.style = STYLE_SYNDICATE
-
- do_sparks(8, FALSE, target)
- target.visible_message(span_notice("[target] vanishes..."))
-
- for(var/obj/item/target_item as anything in target)
- if(ishuman(target))
- var/mob/living/carbon/human/human_target = target
- if(target_item == human_target.w_uniform)
- continue //So all they're left with are shoes and uniform.
- if(target_item == human_target.shoes)
- continue
- target.dropItemToGround(target_item)
-
- for(var/obj/item/target_item as anything in victim_belongings)
- target_item.forceMove(return_pod)
-
- target.forceMove(return_pod)
-
- target.flash_act()
- target.set_eye_blur_if_lower(60 SECONDS)
- target.set_dizzy_if_lower(70 SECONDS)
- target.adjust_confusion(20 SECONDS)
-
- new /obj/effect/pod_landingzone(possible_drop_loc[pod_rand_loc], return_pod)
- else
- to_chat(target, span_reallybig(span_hypnophrase("A million voices echo in your head... \"Seems where you got sent here from won't \
- be able to handle our pod... You will die here instead.\"")))
- if(!isliving(target))
- return
- var/mob/living/unlucky_fellow = target
- unlucky_fellow.investigate_log("was returned without a valid drop location by the contractor [contract.owner?.current].", INVESTIGATE_DEATHS)
- unlucky_fellow.death()
-
-#undef RANSOM_LOWER
-#undef RANSOM_UPPER
-#undef CONTRACTOR_RANSOM_CUT
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_datum.dm b/modular_skyrat/modules/contractor/code/datums/contractor_datum.dm
deleted file mode 100644
index 5346d412702..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_datum.dm
+++ /dev/null
@@ -1,34 +0,0 @@
-/// End-round generation proc
-/datum/opposing_force/proc/contractor_round_end()
- var/result = ""
- var/total_spent_rep = 0
-
- var/completed_contracts = contractor_hub.contracts_completed
- var/tc_total = contractor_hub.contract_paid_out + contractor_hub.contract_TC_to_redeem
-
- var/contractor_item_icons = "" // Icons of purchases
- var/contractor_support_unit = "" // Set if they had a support unit - and shows appended to their contracts completed
-
- // Get all the icons/total cost for all our items bought
- for (var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items)
- contractor_item_icons += "\[ [contractor_purchase.name] - [contractor_purchase.cost] Rep
[contractor_purchase.desc] \]"
-
- total_spent_rep += contractor_purchase.cost
-
- // Special case for reinforcements, we want to show their ckey and name on round end.
- if (istype(contractor_purchase, /datum/contractor_item/contractor_partner))
- var/datum/contractor_item/contractor_partner/partner = contractor_purchase
- var/mob/living/carbon/human/partner_mob = partner.partner_mind.current
- contractor_support_unit += " [partner_mob.name] was [partner_mob.p_their()] contractor support unit."
-
- if (length(contractor_hub.purchased_items))
- result += " (used [total_spent_rep] Rep) "
- result += contractor_item_icons
- result += " "
- if (completed_contracts > 0)
- var/plural_check = "contract[completed_contracts > 1 ? "s" : ""]"
-
- result += "Completed [span_greentext("[completed_contracts]")] [plural_check] for a total of \
- [span_greentext("[tc_total] TC")]![contractor_support_unit] "
-
- return result
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_hub.dm b/modular_skyrat/modules/contractor/code/datums/contractor_hub.dm
deleted file mode 100644
index 130cd3d2a4b..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_hub.dm
+++ /dev/null
@@ -1,92 +0,0 @@
-#define LOWEST_TC 30
-
-/datum/contractor_hub
- /// How much reputation the contractor has
- var/contract_rep = 0
- /// What contractor items can be purchased
- var/list/hub_items = list()
- /// List of what the contractor's purchased
- var/list/purchased_items = list()
- /// Static of contractor_item subtypes
- var/static/list/contractor_items = typecacheof(/datum/contractor_item, ignore_root_path = TRUE)
- /// Reference to the current contract datum
- var/datum/syndicate_contract/current_contract
- /// List of all contract datums the contractor has available
- var/list/datum/syndicate_contract/assigned_contracts = list()
- /// used as a blacklist to make sure we're not assigning targets already assigned
- var/list/assigned_targets = list()
- /// NUmber of how many contracts you've done
- var/contracts_completed = 0
- /// How many TC you've paid out in contracts
- var/contract_paid_out = 0
- /// Amount of TC that has yet to be redeemed
- var/contract_TC_to_redeem = 0
- /// Current index number for contract IDs
- var/start_index = 1
-
-/// Generates a list of all valid hub items to set for purchase
-/datum/contractor_hub/proc/create_hub_items()
- for(var/path in contractor_items)
- var/datum/contractor_item/contractor_item = new path
-
- hub_items.Add(contractor_item)
-
-/// Create initial list of contracts
-/datum/contractor_hub/proc/create_contracts(datum/mind/owner)
-
- // 6 initial contracts
- var/list/to_generate = list(
- CONTRACT_PAYOUT_LARGE,
- CONTRACT_PAYOUT_MEDIUM,
- CONTRACT_PAYOUT_MEDIUM,
- CONTRACT_PAYOUT_SMALL,
- CONTRACT_PAYOUT_SMALL,
- CONTRACT_PAYOUT_SMALL
- )
-
- //What the fuck
- if(length(to_generate) > length(GLOB.manifest.locked))
- to_generate.Cut(1, length(GLOB.manifest.locked))
-
- var/total = 0
- var/lowest_paying_sum = 0
- var/datum/syndicate_contract/lowest_paying_contract
-
- // Randomise order, so we don't have contracts always in payout order.
- to_generate = shuffle(to_generate)
-
- // Support contract generation happening multiple times
- if (length(assigned_contracts))
- start_index = length(assigned_contracts) + 1
-
- // Generate contracts, and find the lowest paying.
- for(var/contract_gen in 1 to length(to_generate))
- var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, to_generate[contract_gen])
- var/contract_payout_total = contract_to_add.contract.payout + contract_to_add.contract.payout_bonus
-
- assigned_targets.Add(contract_to_add.contract.target)
-
- if (!lowest_paying_contract || (contract_payout_total < lowest_paying_sum))
- lowest_paying_sum = contract_payout_total
- lowest_paying_contract = contract_to_add
-
- total += contract_payout_total
- contract_to_add.id = start_index
- assigned_contracts.Add(contract_to_add)
-
- start_index++
-
- // If the threshold for TC payouts isn't reached, boost the lowest paying contract
- if ((total < LOWEST_TC) && lowest_paying_contract)
- lowest_paying_contract.contract.payout_bonus += (LOWEST_TC - total)
-
-#undef LOWEST_TC
-
-/datum/contractor_hub/proc/create_single_contract(datum/mind/owner, contract_payout_tier)
- var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, contract_payout_tier)
-
- assigned_targets.Add(contract_to_add.contract.target)
-
- contract_to_add.id = start_index
- assigned_contracts.Add(contract_to_add)
- start_index++
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_items.dm b/modular_skyrat/modules/contractor/code/datums/contractor_items.dm
deleted file mode 100644
index 80013aef23e..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_items.dm
+++ /dev/null
@@ -1,220 +0,0 @@
-/datum/contractor_item
- /// Name of the item datum
- var/name
- /// Description of the item datum
- var/desc
- /// Item path to spawn, no item path means you need to override `handle_purchase()`
- var/item
- /// fontawesome icon to use inside the hub - https://fontawesome.com/icons/
- var/item_icon = "broadcast-tower"
- /// Any number above 0 for how many times it can be bought in a round for a single traitor. -1 is unlimited.
- var/limited = -1
- /// Cost of the item in contract rep.
- var/cost
-
-/// Subtract cost, and spawn if it's an item.
-/datum/contractor_item/proc/handle_purchase(datum/contractor_hub/hub, mob/living/user)
-
- if(hub.contract_rep >= cost)
- hub.contract_rep -= cost
- else
- return FALSE
-
- if(limited >= 1)
- limited -= 1
- else
- return FALSE
-
- hub.purchased_items.Add(src)
-
- user.playsound_local(user, 'sound/machines/uplinkpurchase.ogg', 100)
-
- if(item)
- var/atom/item_to_create = new item(get_turf(user))
- if(user.put_in_hands(item_to_create))
- to_chat(user, span_notice("Your purchase materializes into your hands!"))
- else
- to_chat(user, span_notice("Your purchase materializes onto the floor."))
-
- return TRUE
-
-/datum/contractor_item/contract_reroll
- name = "Contract Reroll"
- desc = "Request a reroll of your current contract list. Will generate a new target, payment, and dropoff for the contracts you currently have available."
- item_icon = "dice"
- limited = 3
- cost = 0
-
-/datum/contractor_item/contract_reroll/handle_purchase(datum/contractor_hub/hub)
- . = ..()
-
- if(!(.))
- return
- // We're not regenerating already completed/aborted/extracting contracts, but we don't want to repeat their targets.
- var/list/new_target_list = list()
- for(var/datum/syndicate_contract/contract_check as anything in hub.assigned_contracts)
- if (contract_check.status != CONTRACT_STATUS_ACTIVE && contract_check.status != CONTRACT_STATUS_INACTIVE)
- if (contract_check.contract.target)
- new_target_list.Add(contract_check.contract.target)
- continue
-
- // Reroll contracts without duplicates
- for(var/datum/syndicate_contract/rerolling_contract as anything in hub.assigned_contracts)
- if (rerolling_contract.status != CONTRACT_STATUS_ACTIVE && rerolling_contract.status != CONTRACT_STATUS_INACTIVE)
- continue
-
- rerolling_contract.generate(new_target_list)
- new_target_list.Add(rerolling_contract.contract.target)
-
- // Set our target list with the new set we've generated.
- hub.assigned_targets = new_target_list
-
-/datum/contractor_item/contractor_pinpointer
- name = "Contractor Pinpointer"
- desc = "A pinpointer that finds targets even without active suit sensors. Due to taking advantage of an exploit within the system, it can't pinpoint to the same accuracy as the traditional models. Becomes permanently locked to the user that first activates it."
- item = /obj/item/pinpointer/crew/contractor
- item_icon = "search-location"
- limited = 2
- cost = 1
-
-/datum/contractor_item/fulton_extraction_kit
- name = "Fulton Extraction Kit"
- desc = "For getting your target across the station to those difficult dropoffs. Place the beacon somewhere secure, and link the pack. Activating the pack on your target will send them over to the beacon - make sure they're not just going to run away though!"
- item = /obj/item/storage/box/contractor/fulton_extraction
- item_icon = "parachute-box"
- limited = 1
- cost = 1
-
-/datum/contractor_item/contractor_partner
- name = "Reinforcements"
- desc = "Upon purchase we'll contact available units in the area. Should there be an agent free, we'll send them down to assist you immediately. If no units are free, we give a full refund."
- item_icon = "user-friends"
- limited = 1
- cost = 2
- var/datum/mind/partner_mind = null
-
-/datum/contractor_item/contractor_partner/handle_purchase(datum/contractor_hub/hub, mob/living/user)
- . = ..()
-
- if(!.)
- return
- to_chat(user, span_notice("The uplink vibrates quietly, connecting to nearby agents..."))
-
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT)
-
- if(LAZYLEN(candidates))
- var/mob/dead/observer/picked_obs = pick(candidates)
- spawn_contractor_partner(user, picked_obs.key)
- else
- to_chat(user, span_notice("No available agents at this time, please try again later."))
-
- // refund and add the limit back.
- limited += 1
- hub.contract_rep += cost
- hub.purchased_items -= src
-
-/datum/contractor_item/contractor_partner/proc/spawn_contractor_partner(mob/living/user, key)
- var/mob/living/carbon/human/partner = new()
- var/datum/outfit/contractor_partner/partner_outfit = new()
-
- partner_outfit.equip(partner)
-
- var/obj/structure/closet/supplypod/arrival_pod = new(null, STYLE_SYNDICATE)
- arrival_pod.explosionSize = list(0,0,0,1)
- arrival_pod.bluespace = TRUE
-
- var/turf/free_location = find_obstruction_free_location(2, user)
-
- // We really want to send them - if we can't find a nice location just land it on top of them.
- if (!free_location)
- free_location = get_turf(user)
-
- partner.forceMove(arrival_pod)
- partner.ckey = key
-
- /// We give a reference to the mind that'll be the support unit
- partner_mind = partner.mind
- partner_mind.make_contractor_support()
-
- to_chat(partner_mind.current, "\n[span_alertwarning("[user.real_name] is your superior. Follow any, and all orders given by them. You're here to support their mission only.")]")
- to_chat(partner_mind.current, "[span_alertwarning("Should they perish, or be otherwise unavailable, you're to assist other active agents in this mission area to the best of your ability.")]\n\n")
-
- new /obj/effect/pod_landingzone(free_location, arrival_pod)
-
-/datum/contractor_item/blackout
- name = "Blackout"
- desc = "Request Syndicate Command to distrupt the station's powernet. Disables power across the station for a short duration."
- item_icon = "bolt"
- limited = 2
- cost = 2
-
-/datum/contractor_item/blackout/handle_purchase(datum/contractor_hub/hub)
- . = ..()
-
- if(!.)
- return
- power_fail(35, 50)
- priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", ANNOUNCER_POWEROFF)
-
-/datum/contractor_item/comms_blackout
- name = "Comms Outage"
- desc = "Request Syndicate Command to disable station Telecommunications. Disables telecommunications across the station for a medium duration."
- item_icon = "phone-slash"
- limited = 2
- cost = 2
-
-/datum/contractor_item/comms_blackout/handle_purchase(datum/contractor_hub/hub)
- . = ..()
-
- if(!.)
- return
- var/datum/round_event_control/event = locate(/datum/round_event_control/communications_blackout) in SSevents.control
- event.run_event()
-
-/datum/contractor_item/mod_baton_holster
- name = "Baton Holster Module"
- desc = "Never worry about dropping your baton again with this holster module! Simply insert your baton into the module, put it in your MODsuit, and the baton will retract whenever dropped."
- item = /obj/item/mod/module/baton_holster
- item_icon = "wrench" //I cannot find anything better, replace if you find something more fitting
- limited = 1
- cost = 2
-
-/datum/contractor_item/baton_upgrade_cuff
- name = "Baton Cuff Upgrade"
- desc = "Using technology reverse-engineered from some alien batons we had lying around, you can now cuff people using your baton with the secondary attack. Due to technical limitations, only cable cuffs and zipties work, and they need to be loaded into the baton manually."
- item = /obj/item/baton_upgrade/cuff
- item_icon = "bacon" //ditto
- limited = 1
- cost = 1
-
-/datum/contractor_item/baton_upgrade_mute
- name = "Baton Mute Upgrade"
- desc = "A relatively new advancement in completely proprietary baton technology, this baton upgrade will mute anyone hit for ten seconds, maximizing at twenty seconds."
- item = /obj/item/baton_upgrade/mute
- item_icon = "comment-slash"
- limited = 1
- cost = 2
-
-/datum/contractor_item/baton_upgrade_focus
- name = "Baton Focus Upgrade"
- desc = "When applied to a baton, it will exhaust the target even more, should they be the target of your current contract."
- item = /obj/item/baton_upgrade/focus
- item_icon = "eye"
- limited = 1
- cost = 2
-
-/datum/contractor_item/mod_magnetic_suit
- name = "Magnetic Deployment Module"
- desc = "A module that utilizes magnets to largely reduce the time needed to deploy and retract your MODsuit."
- item = /obj/item/mod/module/springlock/contractor
- item_icon = "magnet"
- limited = 1
- cost = 2
-
-/datum/contractor_item/mod_scorpion_hook
- name = "SCORPION Hook Module"
- desc = "A module that allows you to launch a hardlight hook from your MODsuit, pulling a target into range of your baton."
- item = /obj/item/mod/module/scorpion_hook
- item_icon = "arrow-left" //replace if fontawesome gets an actual hook icon
- limited = 1
- cost = 3
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_support.dm b/modular_skyrat/modules/contractor/code/datums/contractor_support.dm
deleted file mode 100644
index c758608ee1a..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_support.dm
+++ /dev/null
@@ -1,53 +0,0 @@
-/datum/antagonist/traitor/contractor_support
- name = "Contractor Support Unit"
- antag_moodlet = /datum/mood_event/focused
-
- show_in_roundend = FALSE // We're already adding them in to the contractor's roundend.
- give_objectives = TRUE // We give them their own custom objective.
- give_uplink = FALSE // Don't give them an uplink, they get one in their backpack
- /// Team datum that contains the contractor and the support unit
- var/datum/team/contractor_team/contractor_team
-
-/// Team for storing both the contractor and their support unit - only really for the HUD and admin logging.
-/datum/team/contractor_team
- show_roundend_report = FALSE
-
-/datum/antagonist/traitor/contractor_support/on_gain()
- var/datum/objective/generic_objective = new
-
- generic_objective.name = "Follow the Contractor's Orders"
- generic_objective.explanation_text = "Follow your orders. Assist agents in this mission area."
-
- generic_objective.completed = TRUE
-
- objectives += generic_objective
- return ..()
-
-/datum/outfit/contractor_partner
- name = "Contractor Support Unit"
-
- uniform = /obj/item/clothing/under/chameleon
- suit = /obj/item/clothing/suit/chameleon
- back = /obj/item/storage/backpack
- belt = /obj/item/modular_computer/pda/chameleon
- mask = /obj/item/clothing/mask/cigarette/syndicate
- shoes = /obj/item/clothing/shoes/chameleon/noslip
- ears = /obj/item/radio/headset/chameleon
- id = /obj/item/card/id/advanced/chameleon
- r_hand = /obj/item/storage/toolbox/syndicate
- id_trim = /datum/id_trim/chameleon/operative
-
- backpack_contents = list(
- /obj/item/storage/box/survival,
- /obj/item/implanter/uplink,
- /obj/item/clothing/mask/chameleon,
- /obj/item/storage/fancy/cigarettes/cigpack_syndicate,
- /obj/item/lighter
- )
-
-/datum/outfit/contractor_partner/post_equip(mob/living/carbon/human/partner, visualsOnly)
- . = ..()
- var/obj/item/clothing/mask/cigarette/syndicate/cig = partner.get_item_by_slot(ITEM_SLOT_MASK)
-
- // pre-light their cig
- cig.light()
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_tablet.dm b/modular_skyrat/modules/contractor/code/datums/contractor_tablet.dm
deleted file mode 100644
index 02f5790d5e8..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_tablet.dm
+++ /dev/null
@@ -1,216 +0,0 @@
-/datum/computer_file/program/contract_uplink
- filename = "contractor uplink"
- filedesc = "Syndicate Contractor Uplink"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "assign"
- extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
- size = 10
- requires_ntnet = FALSE
- available_on_ntnet = FALSE
- undeletable = TRUE
- tgui_id = "SyndContractor"
- program_icon = "tasks"
- /// Error message if there is one
- var/error = ""
- /// If the info screen is displayed or not
- var/info_screen = TRUE
- /// If the contract uplink's been assigned to a person yet
- var/assigned = FALSE
- /// If this is the first opening of the tablet
- var/first_load = TRUE
-
-/datum/computer_file/program/contract_uplink/on_start(mob/living/user)
- . = ..(user)
-
-/datum/computer_file/program/contract_uplink/ui_act(action, params)
- . = ..()
- if(.)
- return
-
- var/mob/living/user = usr
- var/obj/item/modular_computer/pda/contractor/uplink_computer = computer
-
- if(!istype(uplink_computer))
- return
-
- switch(action)
- if("PRG_contract-accept")
- var/contract_id = text2num(params["contract_id"])
-
- // Set as the active contract
- uplink_computer.opfor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ACTIVE
- uplink_computer.opfor_data.contractor_hub.current_contract = uplink_computer.opfor_data.contractor_hub.assigned_contracts[contract_id]
-
- program_icon_state = "single_contract"
- return TRUE
- if("PRG_login")
- if(!user.mind.opposing_force)
- var/datum/opposing_force/opposing_force = new(user.mind)
- user.mind.opposing_force = opposing_force
- SSopposing_force.new_opfor(opposing_force)
- var/datum/opposing_force/opfor_data = user.mind.opposing_force
-
- if (!opfor_data) // Just in case
- return FALSE
- // Only play greet sound, and handle contractor hub when assigning for the first time.
- if (!opfor_data.contractor_hub)
- user.playsound_local(user, 'sound/effects/contractstartup.ogg', 100, FALSE)
- opfor_data.contractor_hub = new
- opfor_data.contractor_hub.create_hub_items()
-
- // Stops any topic exploits such as logging in multiple times on a single system.
- if (!assigned)
- opfor_data.contractor_hub.create_contracts(opfor_data.mind_reference)
-
- uplink_computer.opfor_data = opfor_data
-
- program_icon_state = "contracts"
- assigned = TRUE
- return TRUE
- if("PRG_call_extraction")
- if (uplink_computer.opfor_data.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING)
- if (uplink_computer.opfor_data.contractor_hub.current_contract.handle_extraction(user))
- user.playsound_local(user, 'modular_skyrat/modules/contractor/sound/confirmdropoff.ogg', 100, TRUE)
- uplink_computer.opfor_data.contractor_hub.current_contract.status = CONTRACT_STATUS_EXTRACTING
-
- program_icon_state = "extracted"
- else
- user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
- error = "Either both you or your target aren't at the dropoff location, or the pod hasn't got a valid place to land. Clear space, or make sure you're both inside."
- else
- user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
- error = "Already extracting... Place the target into the pod. If the pod was destroyed, this contract is no longer possible."
-
- return TRUE
- if("PRG_contract_abort")
- var/contract_id = uplink_computer.opfor_data.contractor_hub.current_contract.id
-
- uplink_computer.opfor_data.contractor_hub.current_contract = null
- uplink_computer.opfor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED
-
- program_icon_state = "contracts"
-
- return TRUE
- if("PRG_redeem_TC")
- if (uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem)
- var/obj/item/stack/telecrystal/crystals = new /obj/item/stack/telecrystal(get_turf(user),
- uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- if(H.put_in_hands(crystals))
- to_chat(H, span_notice("Your payment materializes into your hands!"))
- else
- to_chat(user, span_notice("Your payment materializes onto the floor."))
-
- uplink_computer.opfor_data.contractor_hub.contract_paid_out += uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem
- uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem = 0
- return TRUE
- else
- user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
- return TRUE
- if ("PRG_clear_error")
- error = ""
- return TRUE
- if("PRG_set_first_load_finished")
- first_load = FALSE
- return TRUE
- if("PRG_toggle_info")
- info_screen = !info_screen
- return TRUE
- if ("buy_hub")
- if (uplink_computer.opfor_data.mind_reference.current == user)
- var/item = params["item"]
-
- for (var/datum/contractor_item/hub_item in uplink_computer.opfor_data.contractor_hub.hub_items)
- if (hub_item.name == item)
- hub_item.handle_purchase(uplink_computer.opfor_data.contractor_hub, user)
- else
- error = "Invalid user... You weren't recognised as the user of this system."
-
-/datum/computer_file/program/contract_uplink/ui_data(mob/user)
- var/list/data = list()
- var/screen_to_be = null
- var/obj/item/modular_computer/pda/contractor/uplink_computer = computer
-
- data["first_load"] = first_load
-
- if (uplink_computer?.opfor_data)
- var/datum/opposing_force/opfor_data = uplink_computer.opfor_data
- data += list()
-
- if (opfor_data.contractor_hub.current_contract)
- data["ongoing_contract"] = TRUE
- screen_to_be = "single_contract"
- if (opfor_data.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING)
- data["extraction_enroute"] = TRUE
- screen_to_be = "extracted"
- else
- data["extraction_enroute"] = FALSE
- else
- data["ongoing_contract"] = FALSE
- data["extraction_enroute"] = FALSE
-
- data["logged_in"] = TRUE
- data["station_name"] = GLOB.station_name
- data["redeemable_tc"] = opfor_data.contractor_hub.contract_TC_to_redeem
- data["earned_tc"] = opfor_data.contractor_hub.contract_paid_out
- data["contracts_completed"] = opfor_data.contractor_hub.contracts_completed
- data["contract_rep"] = opfor_data.contractor_hub.contract_rep
-
- data["info_screen"] = info_screen
-
- data["error"] = error
-
- for (var/datum/contractor_item/hub_item in opfor_data.contractor_hub.hub_items)
- data["contractor_hub_items"] += list(list(
- "name" = hub_item.name,
- "desc" = hub_item.desc,
- "cost" = hub_item.cost,
- "limited" = hub_item.limited,
- "item_icon" = hub_item.item_icon
- ))
-
- for (var/datum/syndicate_contract/contract in opfor_data.contractor_hub.assigned_contracts)
- if(!contract.contract)
- stack_trace("Syndiate contract with null contract objective found in [opfor_data.mind_reference]'s contractor hub!")
- contract.status = CONTRACT_STATUS_ABORTED
- continue
-
- data["contracts"] += list(list(
- "target" = contract.contract.target,
- "target_rank" = contract.target_rank,
- "payout" = contract.contract.payout,
- "payout_bonus" = contract.contract.payout_bonus,
- "dropoff" = contract.contract.dropoff,
- "id" = contract.id,
- "status" = contract.status,
- "message" = contract.wanted_message
- ))
-
- var/direction
- if (opfor_data.contractor_hub.current_contract)
- var/turf/curr = get_turf(user)
- var/turf/dropoff_turf
- data["current_location"] = "[get_area_name(curr, TRUE)]"
-
- for (var/turf/content in opfor_data.contractor_hub.current_contract.contract.dropoff.contents)
- if (isturf(content))
- dropoff_turf = content
- break
-
- if(curr.z == dropoff_turf.z) //Direction calculations for same z-level only
- direction = uppertext(dir2text(get_dir(curr, dropoff_turf))) //Direction text (East, etc). Not as precise, but still helpful.
- if(get_area(user) == opfor_data.contractor_hub.current_contract.contract.dropoff)
- direction = "LOCATION CONFIRMED"
- else
- direction = "???"
-
- data["dropoff_direction"] = direction
-
- else
- data["logged_in"] = FALSE
-
- if(screen_to_be)
- program_icon_state = screen_to_be
- update_computer_icon()
- return data
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/antag_datum.dm b/modular_skyrat/modules/contractor/code/datums/midround/antag_datum.dm
deleted file mode 100644
index 3c7fbeb2a21..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/antag_datum.dm
+++ /dev/null
@@ -1,59 +0,0 @@
-// USED FOR THE MIDROUND
-/datum/antagonist/contractor
- name = "Drifting Contractor"
- antagpanel_category = "DriftingContractor"
- preview_outfit = /datum/outfit/contractor_preview
- job_rank = ROLE_DRIFTING_CONTRACTOR
- hud_icon = 'modular_skyrat/modules/contractor/icons/hud_icon.dmi'
- antag_hud_name = "contractor"
- antagpanel_category = ANTAG_GROUP_SYNDICATE
- antag_moodlet = /datum/mood_event/focused
- show_to_ghosts = TRUE
- suicide_cry = "FOR THE CONTRACTS!!"
- /// The outfit the contractor is equipped with
- var/contractor_outfit = /datum/outfit/contractor
-
-/datum/antagonist/contractor/proc/equip_guy()
- if(!ishuman(owner.current))
- return
- var/mob/living/carbon/human/person = owner.current
- person.equipOutfit(contractor_outfit)
- return TRUE
-
-/datum/antagonist/contractor/on_gain()
- forge_objectives()
- . = ..()
- equip_guy()
-
-/datum/antagonist/contractor/forge_objectives()
- var/datum/objective/contractor_total/contract_objectives = new
- contract_objectives.owner = owner
- objectives += contract_objectives
-
-/datum/antagonist/contractor/roundend_report()
- var/list/report = list()
-
- if(!owner)
- CRASH("antagonist datum without owner")
-
- report += "[printplayer(owner)]"
-
- var/objectives_complete = TRUE
- if(length(objectives))
- report += printobjectives(objectives)
- for(var/datum/objective/objective as anything in objectives)
- if(!objective.check_completion())
- objectives_complete = FALSE
- break
-
- report += owner.opposing_force.contractor_round_end()
-
- if(!length(objectives) || objectives_complete)
- report += "The [name] was successful!"
- else
- report += "The [name] has failed!"
-
- return report.Join(" ")
-
-/datum/job/drifting_contractor
- title = ROLE_DRIFTING_CONTRACTOR
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/event.dm b/modular_skyrat/modules/contractor/code/datums/midround/event.dm
deleted file mode 100644
index 24ac9c1e21c..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/event.dm
+++ /dev/null
@@ -1,40 +0,0 @@
-/*/datum/round_event_control/contractor
- name = "Drifting Contractor"
- typepath = /datum/round_event/ghost_role/contractor
- weight = 8
- max_occurrences = 1
- category = EVENT_CATEGORY_INVASION
- description = "A pre-equipped contractor floats towards the station to fulfill contracts."
-
-/datum/round_event/ghost_role/contractor
- minimum_required = 1
- role_name = "Drifting Contractor"
- fakeable = FALSE
-
-/datum/round_event/ghost_role/contractor/spawn_role()
- var/list/candidates = get_candidates(ROLE_DRIFTING_CONTRACTOR)
- if(!length(candidates))
- return NOT_ENOUGH_PLAYERS
-
- var/mob/dead/selected = pick_n_take(candidates)
-
- var/list/spawn_locs = list()
- for(var/obj/effect/landmark/carpspawn/carp in GLOB.landmarks_list)
- spawn_locs += carp.loc
- if(!length(spawn_locs))
- return MAP_ERROR
-
- var/mob/living/carbon/human/operative = new(pick(spawn_locs))
- operative.dna.update_dna_identity()
- var/datum/mind/mind = new /datum/mind(selected.key)
- selected.client?.prefs?.apply_prefs_to(operative)
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/drifting_contractor))
- mind.special_role = ROLE_DRIFTING_CONTRACTOR
- mind.active = TRUE
- mind.transfer_to(operative)
- mind.add_antag_datum(/datum/antagonist/contractor)
-
- message_admins("[ADMIN_LOOKUPFLW(operative)] has been made into [src] by an event.")
- log_game("[key_name(operative)] was spawned as a [src] by an event.")
- spawned_mobs += operative
- return SUCCESSFUL_SPAWN*/ //BUBBER EDIT: REMOVES THE EXTRA CONTRACTOR EVENT
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/objective.dm b/modular_skyrat/modules/contractor/code/datums/midround/objective.dm
deleted file mode 100644
index 74247f9a9db..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/objective.dm
+++ /dev/null
@@ -1,17 +0,0 @@
-/datum/objective/contractor_total
- name = "contractor"
- explanation_text = "Complete at least %CONTRACTNUM% contract%S%."
- martyr_compatible = TRUE
- /// How many contracts are needed, rand(1, 3)
- var/contracts_needed
-
-/datum/objective/contractor_total/New(text)
- . = ..()
- contracts_needed = rand(1, 3)
- explanation_text = replacetext(explanation_text, "%CONTRACTNUM%", contracts_needed)
- explanation_text = replacetext(explanation_text, "%S%", (contracts_needed > 1 ? "s" : ""))
-
-/datum/objective/contractor_total/check_completion()
- if(owner?.opposing_force?.contractor_hub.contracts_completed >= contracts_needed)
- return TRUE
- return FALSE
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/outfit.dm b/modular_skyrat/modules/contractor/code/datums/midround/outfit.dm
deleted file mode 100644
index c2886de801d..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/outfit.dm
+++ /dev/null
@@ -1,39 +0,0 @@
-/datum/outfit/contractor
- name = "Syndicate Contractor - Full Kit"
-
- glasses = /obj/item/clothing/glasses/night
- mask = /obj/item/clothing/mask/gas/syndicate
- back = /obj/item/mod/control/pre_equipped/contractor/upgraded
- r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
- internals_slot = ITEM_SLOT_RPOCKET
- belt = /obj/item/storage/belt/military
-
- uniform = /obj/item/clothing/under/syndicate/coldres
- shoes = /obj/item/clothing/shoes/combat
- gloves = /obj/item/clothing/gloves/combat
- ears = /obj/item/radio/headset/syndicate/alt
- l_pocket = /obj/item/modular_computer/pda/contractor
- id = /obj/item/card/id/advanced/chameleon
- backpack_contents = list(
- /obj/item/storage/box/survival/syndie,
- /obj/item/storage/box/syndicate/contract_kit/midround,
- /obj/item/knife/combat/survival,
- /obj/item/pinpointer/crew/contractor,
- )
-
- implants = list(
- /obj/item/implant/uplink,
- )
-
- id_trim = /datum/id_trim/chameleon/contractor
-
-/datum/outfit/contractor_preview
- name = "Syndicate Contractor (Preview only)"
-
- back = /obj/item/mod/control/pre_equipped/empty/contractor
- uniform = /obj/item/clothing/under/syndicate
- glasses = /obj/item/clothing/glasses/night
-
-/datum/outfit/contractor/upgraded
- name = "Syndicate Contractor (Upgraded)"
- back = /obj/item/mod/control/pre_equipped/contractor/upgraded/adminbus
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/ruleset.dm b/modular_skyrat/modules/contractor/code/datums/midround/ruleset.dm
deleted file mode 100644
index 4ba0478cda9..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/ruleset.dm
+++ /dev/null
@@ -1,41 +0,0 @@
-/datum/dynamic_ruleset/midround/from_ghosts/contractor
- name = "Drifting Contractors"
- midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
- antag_datum = /datum/antagonist/contractor
- antag_flag = ROLE_DRIFTING_CONTRACTOR
- antag_flag_override = ROLE_DRIFTING_CONTRACTOR
- enemy_roles = list(
- JOB_CAPTAIN,
- JOB_DETECTIVE,
- JOB_HEAD_OF_SECURITY,
- JOB_SECURITY_OFFICER,
- )
- required_enemies = list(2,2,1,1,1,1,1,0,0,0)
- required_candidates = 2
- weight = 6
- cost = 10
- minimum_players = 25
- flags = HIGH_IMPACT_RULESET
- /// valid places to spawn
- var/list/spawn_locs = list()
-
-/datum/dynamic_ruleset/midround/from_ghosts/contractor/ready(forced = FALSE)
- //BUBBER EDIT: LESS CONTRACTORS
- //if(prob(33))
- // required_candidates++
- //BUBBER EDIT: LESS CONTRACTORS
- if (required_candidates > (length(dead_players) + length(list_observers)))
- return FALSE
- for(var/obj/effect/landmark/carpspawn/carp in GLOB.landmarks_list)
- spawn_locs += carp.loc
- if(!length(spawn_locs))
- log_admin("Cannot accept Drifting Contractors ruleset. Couldn't find any carp spawn points.")
- message_admins("Cannot accept Drifting Contractors ruleset. Couldn't find any carp spawn points.")
- return FALSE
- return TRUE
-
-/datum/dynamic_ruleset/midround/from_ghosts/contractor/finish_setup(mob/new_character, index)
- ..()
- new_character.forceMove(pick_n_take(spawn_locs))
- new_character.mind.set_assigned_role(SSjob.GetJobType(/datum/job/drifting_contractor))
- new_character.mind.active = TRUE
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/trim.dm b/modular_skyrat/modules/contractor/code/datums/midround/trim.dm
deleted file mode 100644
index 8fbf98912e2..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/trim.dm
+++ /dev/null
@@ -1,7 +0,0 @@
-/// Trim for Chameleon ID cards. Many outfits, nuke ops and some corpses hold Chameleon ID cards.
-/datum/id_trim/chameleon/contractor
- assignment = "Syndicate Contractor"
- trim_state = "trim_contractor"
- trim_icon = 'modular_skyrat/master_files/icons/obj/card.dmi'
- department_color = COLOR_ORANGE
- subdepartment_color = COLOR_ORANGE
diff --git a/modular_skyrat/modules/contractor/code/datums/mind_datum.dm b/modular_skyrat/modules/contractor/code/datums/mind_datum.dm
deleted file mode 100644
index 91cb9a5ac69..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/mind_datum.dm
+++ /dev/null
@@ -1,4 +0,0 @@
-/datum/mind/proc/make_contractor_support()
- if(has_antag_datum(/datum/antagonist/traitor/contractor_support))
- return
- add_antag_datum(/datum/antagonist/traitor/contractor_support)
diff --git a/modular_skyrat/modules/contractor/code/datums/objective.dm b/modular_skyrat/modules/contractor/code/datums/objective.dm
deleted file mode 100644
index 557bdddfbbf..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/objective.dm
+++ /dev/null
@@ -1,6 +0,0 @@
-/datum/objective
- /// Should space areas count when finding a target?
- var/count_space_areas = TRUE
-
-/datum/objective/contract
- count_space_areas = FALSE
diff --git a/modular_skyrat/modules/contractor/code/items/boxes.dm b/modular_skyrat/modules/contractor/code/items/boxes.dm
deleted file mode 100644
index 99bfa6600fb..00000000000
--- a/modular_skyrat/modules/contractor/code/items/boxes.dm
+++ /dev/null
@@ -1,114 +0,0 @@
-#define SMALL_ITEM_AMOUNT 3
-
-/obj/item/storage/box/syndicate/contract_kit
- name = "contract kit"
- special_desc = "Supplied to Syndicate contractors."
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
- icon_state = "syndiebox"
- illustration = "writing_syndie"
- var/list/item_list = list(
- /obj/item/storage/backpack/duffelbag/syndie/x4,
- /obj/item/storage/box/syndie_kit/throwing_weapons,
- /obj/item/gun/syringe/syndicate,
- /obj/item/pen/edagger,
- /obj/item/pen/sleepy,
- /obj/item/flashlight/emp,
- /obj/item/reagent_containers/syringe/mulligan,
- /obj/item/clothing/shoes/chameleon/noslip,
- /obj/item/storage/medkit/tactical,
- /obj/item/encryptionkey/syndicate,
- /obj/item/clothing/glasses/thermal/syndi,
- /obj/item/slimepotion/slime/sentience/nuclear,
- /obj/item/storage/box/syndie_kit/imp_radio,
- /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot,
- /obj/item/reagent_containers/hypospray/medipen/stimulants,
- /obj/item/storage/box/syndie_kit/imp_freedom,
- /obj/item/crowbar/power/syndicate,
- /obj/item/clothing/gloves/tackler/combat/insulated,
- /obj/item/storage/box/syndie_kit/emp,
- /obj/item/radio/headset/chameleon/advanced,
- /obj/item/shield/energy,
- /obj/item/healthanalyzer/rad_laser
- )
-
-/obj/item/storage/box/syndicate/contract_kit/PopulateContents()
- new /obj/item/modular_computer/pda/contractor(src)
- new /obj/item/storage/box/syndicate/contractor_loadout(src)
- new /obj/item/melee/baton/telescopic/contractor_baton(src)
-
- // All about 4 TC or less - some nukeops only items, but fit nicely to the theme.
- for(var/iteration in 1 to SMALL_ITEM_AMOUNT)
- var/obj/item/small_item = pick_n_take(item_list)
- new small_item(src)
-
- // Paper guide
- new /obj/item/paper/contractor_guide(src)
-
-/obj/item/storage/box/syndicate/contractor_loadout
- name = "standard loadout"
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
- special_desc = "Supplied to Syndicate contractors, providing their specialised MODSuit and chameleon uniform."
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/storage/box/syndicate/contractor_loadout/PopulateContents()
- new /obj/item/mod/control/pre_equipped/contractor(src)
- new /obj/item/clothing/under/chameleon(src)
- new /obj/item/clothing/mask/chameleon(src)
- new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
- new /obj/item/card/id/advanced/chameleon(src)
- new /obj/item/lighter(src)
- new /obj/item/uplink/old_radio(src)
- new /obj/item/jammer(src)
-
-/obj/item/storage/box/contractor/fulton_extraction
- name = "fulton extraction kit"
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/storage/box/contractor/fulton_extraction/PopulateContents()
- new /obj/item/extraction_pack/contractor(src)
- new /obj/item/fulton_core(src)
-
-/obj/item/storage/box/syndicate/contract_kit/midround
- name = "contract kit"
- special_desc = "Supplied to Syndicate contractors."
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
- icon_state = "syndiebox"
- illustration = "writing_syndie"
- item_list = list(
- /obj/item/storage/backpack/duffelbag/syndie/x4,
- /obj/item/storage/box/syndie_kit/throwing_weapons,
- /obj/item/gun/syringe/syndicate,
- /obj/item/pen/edagger,
- /obj/item/pen/sleepy,
- /obj/item/flashlight/emp,
- /obj/item/reagent_containers/syringe/mulligan,
- /obj/item/storage/medkit/tactical,
- /obj/item/clothing/glasses/thermal/syndi,
- /obj/item/slimepotion/slime/sentience/nuclear,
- /obj/item/storage/box/syndie_kit/imp_radio,
- /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot,
- /obj/item/reagent_containers/hypospray/medipen/stimulants,
- /obj/item/storage/box/syndie_kit/imp_freedom,
- /obj/item/crowbar/power/syndicate,
- /obj/item/clothing/gloves/tackler/combat/insulated,
- /obj/item/storage/box/syndie_kit/emp,
- /obj/item/shield/energy,
- /obj/item/healthanalyzer/rad_laser
- )
-
-/obj/item/storage/box/syndicate/contract_kit/midround/PopulateContents()
- // All about 4 TC or less - some nukeops only items, but fit nicely to the theme.
- for(var/iteration in 1 to SMALL_ITEM_AMOUNT)
- var/obj/item/small_item = pick_n_take(item_list)
- new small_item(src)
-
- // Paper guide
- new /obj/item/paper/contractor_guide/midround(src)
- new /obj/item/reagent_containers/hypospray/medipen/atropine(src)
- new /obj/item/jammer(src)
- new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
- new /obj/item/lighter(src)
-
-#undef SMALL_ITEM_AMOUNT
diff --git a/modular_skyrat/modules/contractor/code/items/misc.dm b/modular_skyrat/modules/contractor/code/items/misc.dm
index abadf28d291..f3badcd6902 100644
--- a/modular_skyrat/modules/contractor/code/items/misc.dm
+++ b/modular_skyrat/modules/contractor/code/items/misc.dm
@@ -1,6 +1,3 @@
-/obj/item/paper/contractor_guide
- name = "Contractor Guide"
-
/obj/item/paper/contractor_guide/Initialize(mapload)
default_raw_text = {"
Welcome agent, congratulations on your new position as contractor. On top of your already assigned objectives,
this kit will provide you contracts to take on for TC payments.
@@ -57,7 +54,6 @@
name = "black fulton extraction pack"
icon = 'modular_skyrat/modules/contractor/icons/fulton.dmi'
can_use_indoors = TRUE
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
special_desc = "A modified fulton pack that can be used indoors thanks to Bluespace technology. Favored by Syndicate Contractors."
diff --git a/modular_skyrat/modules/contractor/code/items/tablet.dm b/modular_skyrat/modules/contractor/code/items/tablet.dm
deleted file mode 100644
index 44b28b75c6a..00000000000
--- a/modular_skyrat/modules/contractor/code/items/tablet.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/obj/item/modular_computer/pda/contractor
- name = "contract PDA"
- icon = 'icons/obj/antags/contractor_tablet.dmi'
- icon_state = "tablet"
- icon_state_unpowered = "tablet"
- icon_state_powered = "tablet"
- icon_state_menu = "assign"
- greyscale_config = null
- greyscale_colors = null
- comp_light_luminosity = 6.3
- saved_identification = "John Doe"
- saved_job = "Citizen"
- device_theme = "syndicate"
- starting_programs = list(
- /datum/computer_file/program/contract_uplink,
- /datum/computer_file/program/crew_manifest,
- )
- device_theme = "syndicate"
-
- /// Contractor uplink system board has the user's data baked directly into it on creation
- var/datum/opposing_force/opfor_data
-
-/obj/item/modular_computer/pda/contractor/Initialize(mapload)
- . = ..()
- var/datum/computer_file/program/contract_uplink/uplink = locate() in stored_files
-
- active_program = uplink
-
- var/datum/computer_file/program/messenger/msg = locate() in stored_files
- if(msg)
- msg.invisible = TRUE
-
-/obj/item/modular_computer/pda/contractor/Destroy()
- opfor_data = null
- return ..()
-
-/obj/item/modular_computer/pda/contractor/UpdateDisplay()
- return
diff --git a/modular_skyrat/modules/contractor/code/objects/supplypod.dm b/modular_skyrat/modules/contractor/code/objects/supplypod.dm
index 2cca53e20b2..1f0e1632dd9 100644
--- a/modular_skyrat/modules/contractor/code/objects/supplypod.dm
+++ b/modular_skyrat/modules/contractor/code/objects/supplypod.dm
@@ -1,10 +1,10 @@
/obj/structure/closet/supplypod/extractionpod
var/datum/contractor_hub/contract_hub
var/datum/syndicate_contract/tied_contract
- var/recieving = FALSE
+ var/receiving = FALSE
/obj/structure/closet/supplypod/extractionpod/Destroy()
- if(!recieving)
+ if(!receiving)
return ..()
to_chat(tied_contract.contract.owner, " [span_userdanger("Extraction pod destroyed. Contract aborted.")]")
var/contract_id = tied_contract.id
@@ -18,7 +18,7 @@
return ..()
/obj/structure/closet/supplypod/extractionpod/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
- if(recieving && (atom_integrity <= 0))
+ if(receiving && (atom_integrity <= 0))
to_chat(tied_contract.contract.owner, " [span_userdanger("Extraction pod destroyed. Contract aborted.")]")
var/contract_id = tied_contract.id
if (contract_hub.current_contract == tied_contract)
diff --git a/modular_skyrat/modules/cortical_borer/code/cortical_borer.dm b/modular_skyrat/modules/cortical_borer/code/cortical_borer.dm
index 059a93fcc29..422dc1aa599 100644
--- a/modular_skyrat/modules/cortical_borer/code/cortical_borer.dm
+++ b/modular_skyrat/modules/cortical_borer/code/cortical_borer.dm
@@ -32,7 +32,7 @@ GLOBAL_LIST_EMPTY(cortical_borers)
return FALSE
//so if a person is debrained, the borer is removed
-/obj/item/organ/internal/brain/Remove(mob/living/carbon/target, special = 0, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/Remove(mob/living/carbon/target, special = 0, movement_flags = NO_ID_TRANSFER)
. = ..()
var/mob/living/basic/cortical_borer/cb_inside = target.has_borer()
if(cb_inside)
@@ -50,7 +50,7 @@ GLOBAL_LIST_EMPTY(cortical_borers)
borer = null
return ..()
-/obj/item/organ/internal/borer_body/Insert(mob/living/carbon/carbon_target, special, drop_if_replaced)
+/obj/item/organ/internal/borer_body/Insert(mob/living/carbon/carbon_target, special, movement_flags)
. = ..()
for(var/datum/borer_focus/body_focus as anything in borer.body_focuses)
body_focus.on_add()
diff --git a/modular_skyrat/modules/cortical_borer/code/cortical_borer_antag.dm b/modular_skyrat/modules/cortical_borer/code/cortical_borer_antag.dm
index 5de312eb22a..8d379dc940f 100644
--- a/modular_skyrat/modules/cortical_borer/code/cortical_borer_antag.dm
+++ b/modular_skyrat/modules/cortical_borer/code/cortical_borer_antag.dm
@@ -125,7 +125,7 @@
vents += temp_vent
if(!length(vents))
return MAP_ERROR
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you want to spawn as a cortical borer?", ROLE_PAI, FALSE, 10 SECONDS, POLL_IGNORE_CORTICAL_BORER)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates("Do you want to spawn as a cortical borer?", role = ROLE_PAI, check_jobban = FALSE, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_CORTICAL_BORER)
if(!length(candidates))
return NOT_ENOUGH_PLAYERS
var/living_number = max(length(GLOB.player_list) / POP_PER_BORER, 1)
diff --git a/modular_skyrat/modules/cortical_borer/code/evolution/evolution_general.dm b/modular_skyrat/modules/cortical_borer/code/evolution/evolution_general.dm
index 9a21906dbf3..0e46dd7681b 100644
--- a/modular_skyrat/modules/cortical_borer/code/evolution/evolution_general.dm
+++ b/modular_skyrat/modules/cortical_borer/code/evolution/evolution_general.dm
@@ -54,7 +54,7 @@
/datum/reagent/medicine/system_cleaner,
/datum/reagent/medicine/nanite_slurry,
/datum/reagent/medicine/liquid_solder,
- /datum/reagent/oil,
+ /datum/reagent/fuel/oil,
/datum/reagent/fuel,
)
diff --git a/modular_skyrat/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm b/modular_skyrat/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm
index 5f079c6b9b3..9ba5899ca69 100644
--- a/modular_skyrat/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm
+++ b/modular_skyrat/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm
@@ -19,7 +19,7 @@
if(iscarbon(loc))
Insert(loc)
-/obj/item/organ/internal/empowered_borer_egg/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/empowered_borer_egg/Insert(mob/living/carbon/M, special = FALSE, movement_flags = DELETE_IF_REPLACED)
..()
addtimer(CALLBACK(src, PROC_REF(try_burst)), burst_time)
@@ -36,7 +36,7 @@
if(owner.stat != DEAD)
qdel(src)
return
- var/list/candidates = poll_ghost_candidates("Do you want to spawn as an empowered Cortical Borer bursting from [owner]?", ROLE_PAI, FALSE, 10 SECONDS, POLL_IGNORE_CORTICAL_BORER)
+ var/list/candidates = SSpolling.poll_ghost_candidates("Do you want to spawn as an empowered Cortical Borer bursting from [owner]?", role = ROLE_PAI, check_jobban = FALSE, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_CORTICAL_BORER)
if(!length(candidates))
var/obj/effect/mob_spawn/ghost_role/borer_egg/empowered/borer_egg = new(get_turf(owner))
borer_egg.generation = generation
diff --git a/modular_skyrat/modules/cryosleep/code/cryopod.dm b/modular_skyrat/modules/cryosleep/code/cryopod.dm
index 08a0bf8b8bb..f2d9c4b5bae 100644
--- a/modular_skyrat/modules/cryosleep/code/cryopod.dm
+++ b/modular_skyrat/modules/cryosleep/code/cryopod.dm
@@ -279,11 +279,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/cryopod, 32)
if(!(affected_contract.contract == objective))
continue
var/contract_id = affected_contract.id
- affected_contractor_hub.create_single_contract(objective.owner, affected_contract.payout_type)
affected_contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED
if (affected_contractor_hub.current_contract == objective)
affected_contractor_hub.current_contract = null
- to_chat(objective.owner.current, " [span_userdanger("Contract target out of reach. Contract rerolled.")]")
+ to_chat(objective.owner.current, " [span_userdanger("Contract target out of reach. Contract aborted.")]")
break
else if(istype(objective.target) && objective.target == mob_occupant.mind)
var/old_target = objective.target
diff --git a/modular_skyrat/modules/customization/modules/client/augment/limbs.dm b/modular_skyrat/modules/customization/modules/client/augment/limbs.dm
index 8ae5fed1f04..3d855176497 100644
--- a/modular_skyrat/modules/customization/modules/client/augment/limbs.dm
+++ b/modular_skyrat/modules/customization/modules/client/augment/limbs.dm
@@ -31,8 +31,6 @@
var/chosen_style = GLOB.robotic_styles_list[prefs.augment_limb_styles[slot]]
new_limb.set_icon_static(chosen_style)
new_limb.current_style = prefs.augment_limb_styles[slot]
- for (var/obj/item/organ/external/external_organ as anything in old_limb.external_organs)
- external_organ.transfer_to_limb(new_limb)
new_limb.replace_limb(augmented)
qdel(old_limb)
diff --git a/modular_skyrat/modules/customization/modules/client/augment/organs.dm b/modular_skyrat/modules/customization/modules/client/augment/organs.dm
index 0085f6426c5..9bc2579e378 100644
--- a/modular_skyrat/modules/customization/modules/client/augment/organs.dm
+++ b/modular_skyrat/modules/customization/modules/client/augment/organs.dm
@@ -8,7 +8,7 @@
var/obj/item/organ/organ_path = path // cast this to an organ so we can get the slot from it using initial()
var/obj/item/organ/new_organ = new path()
new_organ.copy_traits_from(human_holder.get_organ_slot(initial(organ_path.slot)))
- new_organ.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_organ.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
//HEARTS
/datum/augment_item/organ/heart
diff --git a/modular_skyrat/modules/customization/modules/clothing/shoes/shoes.dm b/modular_skyrat/modules/customization/modules/clothing/shoes/shoes.dm
index 07d7cc81ea0..5d9e8a27053 100644
--- a/modular_skyrat/modules/customization/modules/clothing/shoes/shoes.dm
+++ b/modular_skyrat/modules/customization/modules/clothing/shoes/shoes.dm
@@ -141,16 +141,17 @@
/obj/item/clothing/shoes/winterboots/christmas
name = "red christmas boots"
desc = "A pair of fluffy red christmas boots!"
- icon = 'modular_skyrat/master_files/icons/obj/clothing/shoes.dmi'
- worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/feet.dmi'
- icon_state = "christmasbootsr"
+ icon_state = "christmas_boots"
+ greyscale_colors = "#cc0f0f#c4c2c2"
+ greyscale_config = /datum/greyscale_config/boots/christmasboots
+ greyscale_config_worn = /datum/greyscale_config/boots/christmasboots/worn
+ greyscale_config_worn_digi = /datum/greyscale_config/boots/christmasboots/worn/digi
+ flags_1 = IS_PLAYER_COLORABLE_1
/obj/item/clothing/shoes/winterboots/christmas/green
name = "green christmas boots"
desc = "A pair of fluffy green christmas boots!"
- icon = 'modular_skyrat/master_files/icons/obj/clothing/shoes.dmi'
- worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/feet.dmi'
- icon_state = "christmasbootsg"
+ greyscale_colors = "#1a991a#c4c2c2"
/obj/item/clothing/shoes/clown_shoes/pink
name = "pink clown shoes"
diff --git a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm
index f66bcab46ca..d02b7e5005b 100644
--- a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm
+++ b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm
@@ -1589,6 +1589,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
supports_variations_flags = NONE
uses_advanced_reskins = TRUE
unique_reskin = list(
+ "Black-Blue" = list(
+ RESKIN_ICON_STATE = "coat_blackblue",
+ RESKIN_WORN_ICON_STATE = "coat_blackblue"
+ ),
"Black-Red" = list(
RESKIN_ICON_STATE = "coat_blackred",
RESKIN_WORN_ICON_STATE = "coat_blackred"
@@ -1607,6 +1611,25 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
. = ..()
. += "It seems particularly soft and has subtle ballistic fibers intwined with the soft fabric that is perfectedly tailored to the body that wears it. Each golden engraving seems to reflect against your eyes with a slightly blinding flare. This is part of a full set of Luna Wolves Legion battle garb."
+
+/obj/item/clothing/head/hats/hos/elofy
+ name = "solar admiral hat"
+ icon ='modular_skyrat/master_files/icons/donator/obj/clothing/hats.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/donator/mob/clothing/head.dmi'
+ icon_state = "hat_black"
+ uses_advanced_reskins = TRUE
+ unique_reskin = list(
+ "White" = list(
+ RESKIN_ICON_STATE = "hat_white",
+ RESKIN_WORN_ICON_STATE = "hat_white"
+ ),
+ "Black" = list(
+ RESKIN_ICON_STATE = "hat_black",
+ RESKIN_WORN_ICON_STATE = "hat_black"
+ )
+ )
+
+
/obj/item/clothing/gloves/elofy
name = "solar admiral gloves"
icon = 'modular_skyrat/master_files/icons/donator/obj/clothing/gloves.dmi'
@@ -1617,6 +1640,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
"White" = list(
RESKIN_ICON_STATE = "gloves_white",
RESKIN_WORN_ICON_STATE = "gloves_white"
+ ),
+ "Black" = list(
+ RESKIN_ICON_STATE = "gloves_black",
+ RESKIN_WORN_ICON_STATE = "gloves_black"
)
)
@@ -1653,7 +1680,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
inhand_icon_state = "archerboots"
// Donation reward for nikotheguydude
-/obj/item/clothing/suit/toggle/labcoat/medical/vic_dresscoat_donator
+/obj/item/clothing/suit/toggle/labcoat/vic_dresscoat_donator
icon = 'modular_skyrat/master_files/icons/donator/obj/clothing/suits.dmi'
worn_icon = 'modular_skyrat/master_files/icons/donator/mob/clothing/suit.dmi'
icon_state = "vickyred"
@@ -1672,7 +1699,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
body_parts_covered = CHEST|ARMS
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
-/obj/item/clothing/suit/toggle/labcoat/medical/vic_dresscoat_donator/Initialize(mapload)
+/obj/item/clothing/suit/toggle/labcoat/vic_dresscoat_donator/Initialize(mapload)
. = ..()
qdel(GetComponent(/datum/component/toggle_icon)) // we dont have a toggle icon
@@ -1680,7 +1707,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
#define NOBILITY_DRESSCOAT_WASHING_CREDITS_NEEDED 2500
// this is based on an in-joke with the character whom inspires this donator item, where they need a fuckton of money to wash their coat. this takes it literally
-/obj/item/clothing/suit/toggle/labcoat/medical/vic_dresscoat_donator/machine_wash(obj/machinery/washing_machine/washer)
+/obj/item/clothing/suit/toggle/labcoat/vic_dresscoat_donator/machine_wash(obj/machinery/washing_machine/washer)
var/total_credits = 0
var/list/obj/item/money_to_delete = list()
@@ -1763,3 +1790,55 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
RESKIN_WORN_ICON_STATE = "trenchcoat_blue"
)
)
+
+//Donation reward for Jasohavents
+/obj/item/clothing/under/rank/cargo/qm/skirt/old
+ name = "quartermaster's jumpskirt"
+ desc = "It seems somebody has kept a couple outdated quartermaster uniforms. They smell of Rum and Smoke."
+ icon = 'modular_skyrat/master_files/icons/donator/obj/clothing/uniform.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/donator/mob/clothing/uniform.dmi'
+
+//Donation reward for Jasohavents
+/obj/item/food/griddle_toast/toaster_implant
+ name = "toast"
+ desc = "Thick cut bread, toasted to perfection."
+ food_reagents = list(/datum/reagent/consumable/nutriment = 1)
+//it's meant to be practically non-functional in terms of giving any ingame advantage, but it seems food needs to have *something* in it to be edible
+//it *can* be a whole thing with a "gridle" that only accepts bread from the user's hand but I can't be assed to implement it for a one-off gimmick donator item
+
+#define TOASTER_IMPLANT_COOLDOWN (3 MINUTES)
+/obj/item/implant/toaster
+ name = "toaster implant"
+ icon = 'icons/obj/food/burgerbread.dmi'
+ icon_state = "griddle_toast"
+ COOLDOWN_DECLARE(toast_cooldown)
+
+/obj/item/implant/toaster/get_data()
+ var/dat = {"Implant Specifications:
+ Name: Toaster-O brand toaster module
+ Implant Details:
+ Function: Serves you a delicious toasted slice of bread."}
+ return dat
+
+/obj/item/implant/toaster/activate()
+ . = ..()
+ if(!COOLDOWN_FINISHED(src, toast_cooldown))
+ imp_in.show_message(span_notice("You're not quite ready to toast another toast yet."))
+ return
+
+ var/obj/item/food/griddle_toast/toaster_implant/toast = new(get_turf(imp_in))
+ var/adjective = pick("crispy", "delicious", "fresh")
+ imp_in.visible_message(span_notice("[imp_in] ejects a [adjective] toast!"), span_notice("With the familiar \"ding\", the toaster ejects a [adjective] toast."))
+
+ playsound(imp_in, 'sound/machines/ding.ogg', vol = 75, vary = FALSE, pressure_affected = FALSE, use_reverb = FALSE)
+ toast.throw_at(get_turf(imp_in), 2, 3)
+ COOLDOWN_START(src, toast_cooldown, TOASTER_IMPLANT_COOLDOWN)
+
+/obj/item/implanter/toaster
+ name = "implanter (toaster)"
+ imp_type = /obj/item/implant/toaster
+
+/obj/item/implantcase/toaster
+ name = "implant case - 'Toaster'"
+ desc = "A glass case containing a toaster implant. Sweet."
+ imp_type = /obj/item/implant/toaster
diff --git a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm
index f5d314f1793..661b3063352 100644
--- a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm
+++ b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm
@@ -22,7 +22,7 @@
if((target_mob.w_uniform && target_mob.w_uniform.body_parts_covered & genital_location) || (target_mob.wear_suit && target_mob.wear_suit.body_parts_covered & genital_location))
return TRUE
//Do they have a Hospital Gown covering them? (The gown has no body_parts_covered so needs its own check)
- if(istype(target_mob.wear_suit, /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown))
+ if(istype(target_mob.wear_suit, /obj/item/clothing/suit/toggle/labcoat/hospitalgown))
return TRUE
//Are they wearing an Undershirt?
diff --git a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm
index ce8dc17424b..1d76a17fba0 100644
--- a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm
+++ b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm
@@ -30,7 +30,7 @@
return !sprite_datum.is_hidden(human)
-/obj/item/organ/external/snout/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/snout/Insert(mob/living/carbon/reciever, special, movement_flags)
if(sprite_accessory_flags & SPRITE_ACCESSORY_USE_MUZZLED_SPRITE)
external_bodytypes |= BODYTYPE_SNOUTED
if(sprite_accessory_flags & SPRITE_ACCESSORY_USE_ALT_FACEWEAR_LAYER)
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
index 9568aec0bd0..31b0dd14e20 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
@@ -277,7 +277,7 @@ GLOBAL_LIST_EMPTY(customizable_races)
replacement.build_from_dna(target.dna, key)
// organ.Insert will qdel any current organs in that slot, so we don't need to.
- replacement.Insert(target, special = TRUE, drop_if_replaced = FALSE)
+ replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// var/obj/item/organ/path = new SA.organ_type
// var/obj/item/organ/oldorgan = C.get_organ_slot(path.slot)
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
index a1574f895a4..bd3c2630927 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
@@ -50,43 +50,6 @@
// Called on Assign, or on Color Change (or any time proof_ghoul_features() is used)
fixed_mut_color = human_ghoul.dna.features["ghoulcolor"]
-/mob/living/carbon/proc/ReassignForeignBodyparts()
- var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD)
- if (head?.type != part_default_head)
- qdel(head)
- var/obj/item/bodypart/limb = new part_default_head
- limb.replace_limb(src, TRUE)
-
- var/obj/item/bodypart/chest = get_bodypart(BODY_ZONE_CHEST)
- if (chest?.type != part_default_chest)
- qdel(chest)
- var/obj/item/bodypart/limb = new part_default_chest
- limb.replace_limb(src, TRUE)
-
- var/obj/item/bodypart/arm/left/left_arm = get_bodypart(BODY_ZONE_L_ARM)
- if (left_arm?.type != part_default_l_arm)
- qdel(left_arm)
- var/obj/item/bodypart/limb = new part_default_l_arm
- limb.replace_limb(src, TRUE)
-
- var/obj/item/bodypart/arm/right/right_arm = get_bodypart(BODY_ZONE_R_ARM)
- if (right_arm?.type != part_default_r_arm)
- qdel(right_arm)
- var/obj/item/bodypart/limb = new part_default_r_arm
- limb.replace_limb(src, TRUE)
-
- var/obj/item/bodypart/leg/left/left_leg = get_bodypart(BODY_ZONE_L_LEG)
- if (left_leg?.type != part_default_l_leg)
- qdel(left_leg)
- var/obj/item/bodypart/limb = new part_default_l_leg
- limb.replace_limb(src, TRUE)
-
- var/obj/item/bodypart/leg/right/right_leg = get_bodypart(BODY_ZONE_R_LEG)
- if (right_leg?.type != part_default_r_leg)
- qdel(right_leg)
- var/obj/item/bodypart/limb = new part_default_r_leg
- limb.replace_limb(src, TRUE)
-
/datum/species/ghoul/on_species_gain(mob/living/carbon/new_ghoul, datum/species/old_species, pref_load)
// Missing Defaults in DNA? Randomize!
proof_ghoul_features(new_ghoul.dna.features)
@@ -106,7 +69,6 @@
human_ghoul.part_default_r_arm = /obj/item/bodypart/arm/right/mutant/ghoul
human_ghoul.part_default_l_leg = /obj/item/bodypart/leg/left/mutant/ghoul
human_ghoul.part_default_r_leg = /obj/item/bodypart/leg/right/mutant/ghoul
- human_ghoul.ReassignForeignBodyparts()
/datum/species/ghoul/on_species_loss(mob/living/carbon/human/former_ghoul, datum/species/new_species, pref_load)
. = ..()
@@ -119,7 +81,6 @@
former_ghoul.part_default_r_arm = /obj/item/bodypart/arm/right
former_ghoul.part_default_l_leg = /obj/item/bodypart/leg/left
former_ghoul.part_default_r_leg = /obj/item/bodypart/leg/right
- former_ghoul.ReassignForeignBodyparts()
/*
* ATTACK PROCS
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm
index a3731443243..ea44c6905be 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm
@@ -25,7 +25,7 @@
var/bloodloss_rate = NORMAL_BLOOD_DRAIN
-/obj/item/organ/internal/heart/hemophage/Insert(mob/living/carbon/tumorful, special, drop_if_replaced)
+/obj/item/organ/internal/heart/hemophage/Insert(mob/living/carbon/tumorful, special, movement_flags)
. = ..()
if(!. || !owner)
return
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
index 5490654ef96..ee33e675692 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
@@ -389,7 +389,7 @@
if(robot_organs)
replacement_organ.organ_flags |= ORGAN_ROBOTIC
replacement_organ.build_from_dna(alterer.dna, chosen_key)
- replacement_organ.Insert(alterer, special = TRUE, drop_if_replaced = FALSE)
+ replacement_organ.Insert(alterer, special = TRUE, movement_flags = DELETE_IF_REPLACED)
else
var/list/new_acc_list = list()
new_acc_list[MUTANT_INDEX_NAME] = selected_sprite_accessory.name
diff --git a/modular_skyrat/modules/customization/modules/reagents/chemistry/reagents/other_reagents.dm b/modular_skyrat/modules/customization/modules/reagents/chemistry/reagents/other_reagents.dm
index 1c03bbf2ca3..521c7e30910 100644
--- a/modular_skyrat/modules/customization/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/modular_skyrat/modules/customization/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1,7 +1,7 @@
/datum/reagent/fuel
process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC
-/datum/reagent/oil
+/datum/reagent/fuel/oil
process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC
/datum/reagent/stable_plasma
@@ -16,9 +16,6 @@
/datum/reagent/hellwater
process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC
-/datum/reagent/syndicateadrenals
- process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC
-
/datum/reagent/carbondioxide
process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC
@@ -45,7 +42,7 @@
C.nutrition = min(C.nutrition + 5, NUTRITION_LEVEL_FULL-1)
..()
-/datum/reagent/oil/on_mob_life(mob/living/carbon/C)
+/datum/reagent/fuel/oil/on_mob_life(mob/living/carbon/C)
if(C.mob_biotypes & MOB_ROBOTIC && C.blood_volume < BLOOD_VOLUME_NORMAL)
C.blood_volume += 0.5
..()
diff --git a/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm b/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm
index e5cc17fc258..ad72df50bac 100644
--- a/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm
+++ b/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm
@@ -50,7 +50,7 @@
return INITIALIZE_HINT_QDEL
//Removes ERP organs depending on config
-/obj/item/organ/external/genital/Insert(mob/living/carbon/M, special, drop_if_replaced)
+/obj/item/organ/external/genital/Insert(mob/living/carbon/M, special, movement_flags)
if(CONFIG_GET(flag/disable_erp_preferences))
return
. = ..()
diff --git a/modular_skyrat/modules/customization/modules/surgery/organs/organ.dm b/modular_skyrat/modules/customization/modules/surgery/organs/organ.dm
index ac69342a036..e1892fc5543 100644
--- a/modular_skyrat/modules/customization/modules/surgery/organs/organ.dm
+++ b/modular_skyrat/modules/customization/modules/surgery/organs/organ.dm
@@ -14,21 +14,23 @@
if(mutantpart_key)
color = mutantpart_info[MUTANT_INDEX_COLOR_LIST][1]
-/obj/item/organ/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/Insert(mob/living/carbon/M, special = FALSE, movement_flags = DELETE_IF_REPLACED)
var/mob/living/carbon/human/H = M
if(mutantpart_key && istype(H))
H.dna.species.mutant_bodyparts[mutantpart_key] = mutantpart_info.Copy()
- H.update_body()
+ if(!special)
+ H.update_body()
. = ..()
-/obj/item/organ/Remove(mob/living/carbon/M, special = FALSE)
+/obj/item/organ/Remove(mob/living/carbon/M, special = FALSE, movement_flags)
var/mob/living/carbon/human/H = M
if(mutantpart_key && istype(H))
if(H.dna.species.mutant_bodyparts[mutantpart_key])
mutantpart_info = H.dna.species.mutant_bodyparts[mutantpart_key].Copy() //Update the info in case it was changed on the person
color = mutantpart_info[MUTANT_INDEX_COLOR_LIST][1]
H.dna.species.mutant_bodyparts -= mutantpart_key
- H.update_body()
+ if(!special)
+ H.update_body()
. = ..()
/obj/item/organ/proc/build_from_dna(datum/dna/DNA, associated_key)
diff --git a/modular_skyrat/modules/customization/modules/surgery/organs/tails.dm b/modular_skyrat/modules/customization/modules/surgery/organs/tails.dm
index 007fcb16dfd..3cafd6852a0 100644
--- a/modular_skyrat/modules/customization/modules/surgery/organs/tails.dm
+++ b/modular_skyrat/modules/customization/modules/surgery/organs/tails.dm
@@ -41,7 +41,7 @@
return TRUE
-/obj/item/organ/external/tail/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/tail/Insert(mob/living/carbon/reciever, special, movement_flags)
if(sprite_accessory_flags & SPRITE_ACCESSORY_WAG_ABLE)
wag_flags |= WAG_ABLE
return ..()
diff --git a/modular_skyrat/modules/customization/modules/surgery/organs/taur_body.dm b/modular_skyrat/modules/customization/modules/surgery/organs/taur_body.dm
index 233b2290b7c..81b842a1af6 100644
--- a/modular_skyrat/modules/customization/modules/surgery/organs/taur_body.dm
+++ b/modular_skyrat/modules/customization/modules/surgery/organs/taur_body.dm
@@ -31,7 +31,7 @@
return GLOB.sprite_accessories["taur"]
-/obj/item/organ/external/taur_body/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/taur_body/Insert(mob/living/carbon/reciever, special, movement_flags)
if(sprite_accessory_flags & SPRITE_ACCESSORY_HIDE_SHOES)
external_bodytypes |= BODYTYPE_HIDE_SHOES
diff --git a/modular_skyrat/modules/deforest_medical_items/code/storage_items.dm b/modular_skyrat/modules/deforest_medical_items/code/storage_items.dm
index d700f5d68b4..6e918bb2fdb 100644
--- a/modular_skyrat/modules/deforest_medical_items/code/storage_items.dm
+++ b/modular_skyrat/modules/deforest_medical_items/code/storage_items.dm
@@ -173,7 +173,7 @@
/obj/item/clothing/mask/breath,
/obj/item/clothing/mask/muzzle,
/obj/item/clothing/mask/surgical,
- /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown,
+ /obj/item/clothing/suit/toggle/labcoat/hospitalgown,
/obj/item/dnainjector,
/obj/item/extinguisher/mini,
/obj/item/flashlight/pen,
@@ -265,7 +265,7 @@
/obj/item/clothing/mask/breath,
/obj/item/clothing/mask/muzzle,
/obj/item/clothing/mask/surgical,
- /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown,
+ /obj/item/clothing/suit/toggle/labcoat/hospitalgown,
/obj/item/construction/plumbing,
/obj/item/dnainjector,
/obj/item/extinguisher/mini,
diff --git a/modular_skyrat/modules/emotes/code/additionalemotes/turf_list.dm b/modular_skyrat/modules/emotes/code/additionalemotes/turf_list.dm
index 13380db04a0..569c1d76403 100644
--- a/modular_skyrat/modules/emotes/code/additionalemotes/turf_list.dm
+++ b/modular_skyrat/modules/emotes/code/additionalemotes/turf_list.dm
@@ -108,7 +108,7 @@
icon = 'modular_skyrat/master_files/icons/effects/turf_effects_64.dmi'
icon_state = "tails"
pixel_x = -16 //correcting the offset for 64
- var/mutable_appearance/overlay = mutable_appearance('modular_skyrat/master_files/icons/effects/turf_effects_64.dmi', "tails_top", EXTRA_ABOVE_MOB_LAYER, src, GAME_PLANE_UPPER)
+ var/mutable_appearance/overlay = mutable_appearance('modular_skyrat/master_files/icons/effects/turf_effects_64.dmi', "tails_top", EXTRA_ABOVE_MOB_LAYER, src)
overlay.appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER
src.add_overlay(overlay)
playsound(get_turf(src), 'sound/weapons/thudswoosh.ogg', 25, TRUE)
@@ -119,7 +119,7 @@
icon = 'modular_skyrat/master_files/icons/effects/turf_effects_64.dmi'
icon_state = "naga"
pixel_x = -16
- var/mutable_appearance/overlay = mutable_appearance('modular_skyrat/master_files/icons/effects/turf_effects_64.dmi', "naga_top", EXTRA_ABOVE_MOB_LAYER, src, GAME_PLANE_UPPER)
+ var/mutable_appearance/overlay = mutable_appearance('modular_skyrat/master_files/icons/effects/turf_effects_64.dmi', "naga_top", EXTRA_ABOVE_MOB_LAYER, src)
overlay.appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER
src.add_overlay(overlay)
playsound(get_turf(src), 'modular_skyrat/modules/emotes/sound/emotes/hiss.ogg', 25, TRUE)
diff --git a/modular_skyrat/modules/examinemore/code/examine_more.dm b/modular_skyrat/modules/examinemore/code/examine_more.dm
index e8500f18cf7..e1581f5a68e 100644
--- a/modular_skyrat/modules/examinemore/code/examine_more.dm
+++ b/modular_skyrat/modules/examinemore/code/examine_more.dm
@@ -103,21 +103,7 @@ would only be recognisable with someone that had the syndicate trait.
composed_message = "You note the following because of your loyalty to [faction_i]: "
composed_message += special_desc
. += composed_message
- //If they are a syndicate contractor or a syndicate
- if(EXAMINE_CHECK_CONTRACTOR)
- var/mob/living/carbon/human/human_user = user
- if(human_user.mind.special_role == ROLE_DRIFTING_CONTRACTOR)
- composed_message = "You note the following because of your [span_red("Contractor Status")]: "
- composed_message += special_desc
- . += composed_message
- else if(HAS_TRAIT(human_user, TRAIT_DETECTIVE)) //Useful detective!
- composed_message = "You note the following because of your brilliant Detective skills: "
- composed_message += special_desc
- . += composed_message
- else if((human_user.mind.special_role == ROLE_TRAITOR) || (ROLE_SYNDICATE in human_user.faction))
- composed_message = "You note the following because of your [span_red("[special_desc_affiliation ? special_desc_affiliation : "Syndicate Affiliation"]")]: "
- composed_message += special_desc
- . += composed_message
+
/*
* EXAMPLES
diff --git a/modular_skyrat/modules/goofsec/code/sol_fed.dm b/modular_skyrat/modules/goofsec/code/sol_fed.dm
index 2e8348b8eba..b525d852d48 100644
--- a/modular_skyrat/modules/goofsec/code/sol_fed.dm
+++ b/modular_skyrat/modules/goofsec/code/sol_fed.dm
@@ -143,7 +143,7 @@ GLOBAL_LIST_INIT(call911_do_and_do_not, list(
cell_phone_number = "Dogginos"
list_to_use = "dogginos"
priority_announce(announcement_message, announcer, 'sound/effects/families_police.ogg', has_important_message=TRUE, color_override = "yellow")
- var/list/candidates = poll_ghost_candidates(poll_question, "deathsquad")
+ var/list/candidates = SSpolling.poll_ghost_candidates(poll_question, ROLE_DEATHSQUAD)
if(candidates.len)
//Pick the (un)lucky players
@@ -321,21 +321,24 @@ GLOBAL_LIST_INIT(call911_do_and_do_not, list(
/datum/outfit/request_911/police
name = "911 Response: Marshal"
- back = /obj/item/storage/backpack/duffelbag/cops
- uniform = /obj/item/clothing/under/rank/security/detective/cowboy
- shoes = /obj/item/clothing/shoes/cowboy
+ back = /obj/item/storage/backpack/satchel
+ uniform = /obj/item/clothing/under/sol_peacekeeper
+ suit = /obj/item/clothing/suit/armor/vest/det_suit/sol
+ shoes = /obj/item/clothing/shoes/jackboots
glasses = /obj/item/clothing/glasses/sunglasses
ears = /obj/item/radio/headset/headset_sec/alt
- head = /obj/item/clothing/head/cowboy
- belt = /obj/item/gun/energy/disabler
- r_pocket = /obj/item/lighter
+ head = /obj/item/clothing/head/soft/black
+ suit_store = /obj/item/gun/energy/disabler
+ belt = /obj/item/melee/baton/security/loaded
+ r_pocket = /obj/item/flashlight/seclite
l_pocket = /obj/item/restraints/handcuffs
id = /obj/item/card/id/advanced/solfed
- backpack_contents = list(/obj/item/storage/box/survival = 1,
+ backpack_contents = list(
+ /obj/item/storage/box/survival = 1,
/obj/item/storage/box/handcuffs = 1,
- /obj/item/melee/baton/security/loaded = 1,
/obj/item/solfed_reporter/swat_caller = 1,
- /obj/item/beamout_tool = 1)
+ /obj/item/beamout_tool = 1,
+ )
id_trim = /datum/id_trim/solfed
@@ -366,7 +369,7 @@ GLOBAL_LIST_INIT(call911_do_and_do_not, list(
/obj/item/rwd/loaded = 1,
/obj/item/beamout_tool = 1,
/obj/item/solfed_reporter/swat_caller = 1,
- )
+ )
id_trim = /datum/id_trim/solfed/atmos
/obj/item/radio/headset/headset_solfed/atmos
@@ -396,21 +399,25 @@ GLOBAL_LIST_INIT(call911_do_and_do_not, list(
/datum/outfit/request_911/emt
name = "911 Response: EMT"
- back = /obj/item/storage/backpack/duffelbag/cops
- uniform = /obj/item/clothing/under/rank/medical/paramedic
- shoes = /obj/item/clothing/shoes/sneakers/white
+ back = /obj/item/storage/backpack/medic
+ uniform = /obj/item/clothing/under/sol_emt
+ shoes = /obj/item/clothing/shoes/jackboots
ears = /obj/item/radio/headset/headset_med
- head = /obj/item/clothing/head/soft/paramedic
+ mask = /obj/item/clothing/mask/gas/alt
+ head = /obj/item/clothing/head/helmet/toggleable/sf_hardened/emt
id = /obj/item/card/id/advanced/solfed
- suit = /obj/item/clothing/suit/toggle/labcoat/paramedic
+ suit = /obj/item/clothing/suit/armor/sf_hardened/emt
gloves = /obj/item/clothing/gloves/latex/nitrile
- belt = /obj/item/storage/belt/medical/paramedic
- suit_store = /obj/item/flashlight/pen/paramedic
- backpack_contents = list(/obj/item/storage/box/survival = 1,
+ belt = /obj/item/storage/backpack/duffelbag/deforest_medkit/stocked
+ suit_store = /obj/item/tank/internals/emergency_oxygen/engi
+ r_pocket = /obj/item/flashlight/seclite
+ l_pocket = /obj/item/storage/medkit/civil_defense
+ backpack_contents = list(
+ /obj/item/storage/box/survival = 1,
/obj/item/emergency_bed = 1,
- /obj/item/storage/medkit/surgery = 1,
/obj/item/solfed_reporter/swat_caller = 1,
- /obj/item/beamout_tool = 1)
+ /obj/item/beamout_tool = 1,
+ )
id_trim = /datum/id_trim/solfed
@@ -438,24 +445,26 @@ GLOBAL_LIST_INIT(call911_do_and_do_not, list(
/datum/outfit/request_911/condom_destroyer
name = "911 Response: Armed S.W.A.T. Officer"
- back = /obj/item/storage/backpack/duffelbag/cops
- uniform = /obj/item/clothing/under/rank/security/officer/beatcop
+ back = /obj/item/storage/backpack
+ uniform = /obj/item/clothing/under/sol_peacekeeper
shoes = /obj/item/clothing/shoes/jackboots
glasses = /obj/item/clothing/glasses/sunglasses
ears = /obj/item/radio/headset/headset_sec/alt
- head = /obj/item/clothing/head/helmet/toggleable/riot
+ head = /obj/item/clothing/head/helmet/sf_peacekeeper
belt = /obj/item/gun/energy/disabler
- suit = /obj/item/clothing/suit/armor/riot
- r_pocket = /obj/item/lighter
+ suit = /obj/item/clothing/suit/armor/sf_peacekeeper
+ r_pocket = /obj/item/flashlight/seclite
l_pocket = /obj/item/restraints/handcuffs
id = /obj/item/card/id/advanced/solfed
- l_hand = /obj/item/gun/ballistic/shotgun/riot
- backpack_contents = list(/obj/item/storage/box/survival = 1,
+ l_hand = /obj/item/gun/ballistic/shotgun/riot/sol
+ backpack_contents = list(
+ /obj/item/storage/box/survival = 1,
/obj/item/storage/box/handcuffs = 1,
/obj/item/melee/baton/security/loaded = 1,
/obj/item/storage/box/lethalshot = 2,
/obj/item/solfed_reporter/treason_reporter = 1,
- /obj/item/beamout_tool = 1)
+ /obj/item/beamout_tool = 1,
+ )
id_trim = /datum/id_trim/solfed
@@ -482,22 +491,25 @@ GLOBAL_LIST_INIT(call911_do_and_do_not, list(
/datum/outfit/request_911/treason_destroyer
name = "911 Response: SolFed Military"
- uniform = /obj/item/clothing/under/rank/security/officer/hecu
- head = /obj/item/clothing/head/helmet
- mask = /obj/item/clothing/mask/gas/hecu2
+ uniform = /obj/item/clothing/under/sol_peacekeeper
+ head = /obj/item/clothing/head/helmet/sf_sacrificial
+ mask = /obj/item/clothing/mask/gas/alt
gloves = /obj/item/clothing/gloves/combat
- suit = /obj/item/clothing/suit/armor/vest
- shoes = /obj/item/clothing/shoes/combat
+ suit = /obj/item/clothing/suit/armor/sf_sacrificial
+ shoes = /obj/item/clothing/shoes/jackboots
- back = /obj/item/storage/backpack/duffelbag/cops
+ back = /obj/item/storage/backpack
glasses = /obj/item/clothing/glasses/sunglasses
ears = /obj/item/radio/headset/headset_sec/alt
l_pocket = /obj/item/restraints/handcuffs
+ r_pocket = /obj/item/flashlight/seclite
id = /obj/item/card/id/advanced/solfed
r_hand = /obj/item/gun/ballistic/automatic/sol_rifle
- backpack_contents = list(/obj/item/storage/box/handcuffs = 1,
+ backpack_contents = list(
+ /obj/item/storage/box/handcuffs = 1,
+ /obj/item/sacrificial_face_shield = 1,
/obj/item/melee/baton/security/loaded = 1,
- /obj/item/ammo_box/magazine/c40sol_rifle/standard = 4
+ /obj/item/ammo_box/magazine/c40sol_rifle/standard = 4,
)
id_trim = /datum/id_trim/solfed
@@ -566,7 +578,7 @@ GLOBAL_LIST_INIT(call911_do_and_do_not, list(
station_balance?.adjust_money(SOLFED_FINE_AMOUNT) // paying for the gas to drive all the fuckin' way out to the frontier
priority_announce(announcement_message, announcement_source, 'sound/effects/families_police.ogg', has_important_message = TRUE, color_override = "yellow")
- var/list/candidates = poll_ghost_candidates(ghost_poll_msg, jobban_to_check)
+ var/list/candidates = SSpolling.poll_ghost_candidates(ghost_poll_msg, jobban_to_check)
if(candidates.len)
//Pick the (un)lucky players
diff --git a/modular_skyrat/modules/goofsec/code/solfed_clothing.dm b/modular_skyrat/modules/goofsec/code/solfed_clothing.dm
new file mode 100644
index 00000000000..ab3bff3e7d2
--- /dev/null
+++ b/modular_skyrat/modules/goofsec/code/solfed_clothing.dm
@@ -0,0 +1,42 @@
+// Peacekeeper jumpsuit
+
+/obj/item/clothing/under/sol_peacekeeper
+ name = "sol peacekeeper uniform"
+ desc = "A military-grade uniform with military grade comfort (none at all), often seen on \
+ SolFed's various peacekeeping forces, and usually alongside a blue helmet."
+ icon = 'modular_skyrat/modules/goofsec/icons/uniforms.dmi'
+ icon_state = "peacekeeper"
+ worn_icon = 'modular_skyrat/modules/goofsec/icons/uniforms_worn.dmi'
+ worn_icon_digi = 'modular_skyrat/modules/goofsec/icons/uniforms_worn_digi.dmi'
+ worn_icon_state = "peacekeeper"
+ armor_type = /datum/armor/clothing_under/rank_security
+ inhand_icon_state = null
+ has_sensor = SENSOR_COORDS
+ random_sensor = FALSE
+
+// EMT jumpsuit
+
+/obj/item/clothing/under/sol_emt
+ name = "sol emergency medical uniform"
+ desc = "A copy of SolFed's peacekeeping uniform, recolored and re-built paramedics in mind."
+ icon = 'modular_skyrat/modules/goofsec/icons/uniforms.dmi'
+ icon_state = "emt"
+ worn_icon = 'modular_skyrat/modules/goofsec/icons/uniforms_worn.dmi'
+ worn_icon_digi = 'modular_skyrat/modules/goofsec/icons/uniforms_worn_digi.dmi'
+ worn_icon_state = "emt"
+ armor_type = /datum/armor/clothing_under/rank_medical
+ inhand_icon_state = null
+ has_sensor = SENSOR_COORDS
+ random_sensor = FALSE
+
+// Solfed flak jacket, for marshals
+
+/obj/item/clothing/suit/armor/vest/det_suit/sol
+ name = "'Gordyn' flak vest"
+ desc = "A light armored jacket common on SolFed personnel who need armor, but find a full vest \
+ too impractical or uneeded."
+ icon = 'modular_skyrat/modules/goofsec/icons/uniforms.dmi'
+ icon_state = "flak"
+ worn_icon = 'modular_skyrat/modules/goofsec/icons/uniforms_worn.dmi'
+ worn_icon_state = "flak"
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
diff --git a/modular_skyrat/modules/goofsec/icons/uniforms.dmi b/modular_skyrat/modules/goofsec/icons/uniforms.dmi
new file mode 100644
index 00000000000..82b6c313b73
Binary files /dev/null and b/modular_skyrat/modules/goofsec/icons/uniforms.dmi differ
diff --git a/modular_skyrat/modules/goofsec/icons/uniforms_worn.dmi b/modular_skyrat/modules/goofsec/icons/uniforms_worn.dmi
new file mode 100644
index 00000000000..5e3700a1505
Binary files /dev/null and b/modular_skyrat/modules/goofsec/icons/uniforms_worn.dmi differ
diff --git a/modular_skyrat/modules/goofsec/icons/uniforms_worn_digi.dmi b/modular_skyrat/modules/goofsec/icons/uniforms_worn_digi.dmi
new file mode 100644
index 00000000000..934a079713a
Binary files /dev/null and b/modular_skyrat/modules/goofsec/icons/uniforms_worn_digi.dmi differ
diff --git a/modular_skyrat/modules/ices_events/code/events/ev_roleplay_check.dm b/modular_skyrat/modules/ices_events/code/events/ev_roleplay_check.dm
index d55fd75aeb3..0def7040812 100644
--- a/modular_skyrat/modules/ices_events/code/events/ev_roleplay_check.dm
+++ b/modular_skyrat/modules/ices_events/code/events/ev_roleplay_check.dm
@@ -4,10 +4,10 @@
/proc/engaged_role_play_check(mob/living/carbon/human/player, station = TRUE, dorms = TRUE)
var/turf/player_turf = get_turf(player)
var/area/player_area = get_area(player_turf)
- if(!is_station_level(player_turf.z) && station)
- return TRUE
- if(istype(player_area, /area/station/commons/dorms) && dorms)
+ if(station && !is_station_level(player_turf.z))
+ return TRUE
+ if(dorms && istype(player_area, /area/station/commons/dorms))
return TRUE
return FALSE
diff --git a/modular_skyrat/modules/liquids/code/liquid_systems/liquid_effect.dm b/modular_skyrat/modules/liquids/code/liquid_systems/liquid_effect.dm
index a019edc69b7..e3bb79a1f1f 100644
--- a/modular_skyrat/modules/liquids/code/liquid_systems/liquid_effect.dm
+++ b/modular_skyrat/modules/liquids/code/liquid_systems/liquid_effect.dm
@@ -224,7 +224,7 @@
/obj/effect/abstract/liquid_turf/proc/make_state_layer(state, has_top)
PRIVATE_PROC(TRUE)
- . = list(make_liquid_overlay("stage[state]_bottom", ABOVE_MOB_LAYER, GAME_PLANE_UPPER))
+ . = list(make_liquid_overlay("stage[state]_bottom", ABOVE_MOB_LAYER))
if(!has_top)
return
diff --git a/modular_skyrat/modules/loadouts/loadout_items/donator/personal/donator_personal.dm b/modular_skyrat/modules/loadouts/loadout_items/donator/personal/donator_personal.dm
index bb234f4f45b..4ee5e79115d 100644
--- a/modular_skyrat/modules/loadouts/loadout_items/donator/personal/donator_personal.dm
+++ b/modular_skyrat/modules/loadouts/loadout_items/donator/personal/donator_personal.dm
@@ -594,6 +594,12 @@
// ckeywhitelist = list("october23")
restricted_roles = list(JOB_HEAD_OF_SECURITY)
+/datum/loadout_item/head/elofy
+ name = "Solar Admiral Hat"
+ item_path = /obj/item/clothing/head/hats/hos/elofy
+ ckeywhitelist = list("october23")
+ restricted_roles = list(JOB_HEAD_OF_SECURITY)
+
/datum/loadout_item/gloves/elofy
name = "Solar Admiral Gloves"
item_path = /obj/item/clothing/gloves/elofy
@@ -727,8 +733,8 @@
/datum/loadout_item/suit/nobility_dresscoat
name = "Nobility Dresscoat"
- item_path = /obj/item/clothing/suit/toggle/labcoat/medical/vic_dresscoat_donator
- //ckeywhitelist = list("nikotheguydude")
+ item_path = /obj/item/clothing/suit/toggle/labcoat/vic_dresscoat_donator
+// ckeywhitelist = list("nikotheguydude")
/datum/loadout_item/suit/anubite_headpiece
name = "Anubite Headpiece"
@@ -749,3 +755,13 @@
name = "Graceful Trenchcoat"
item_path = /obj/item/clothing/neck/trenchcoat
//ckeywhitelist = list("Smol42", "Awers855")
+
+/datum/loadout_item/under/jumpsuit/old_qm_jumpskirt
+ name = "Old Quartermaster's Jumpskirt"
+ item_path = /obj/item/clothing/under/rank/cargo/qm/skirt/old
+// ckeywhitelist = list("jasohavents")
+
+/datum/loadout_item/pocket_items/toaster_implant
+ name = "Toaster Implant"
+ item_path = /obj/item/implanter/toaster
+// ckeywhitelist = list("jasohavents")
diff --git a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm
index 13dd0118858..f8f74889b50 100644
--- a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm
+++ b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm
@@ -144,7 +144,7 @@ GLOBAL_LIST_INIT(loadout_glasses, generate_loadout_items(/datum/loadout_item/gla
*/
/datum/loadout_item/glasses/medicpatch
- name = "Medical Eyepatch"
+ name = "Medical Eyepatch (Skyrat)"
item_path = /obj/item/clothing/glasses/hud/eyepatch/med
restricted_roles = list(JOB_MEDICAL_DOCTOR, JOB_CHIEF_MEDICAL_OFFICER, JOB_GENETICIST, JOB_CHEMIST, JOB_VIROLOGIST, JOB_PARAMEDIC, JOB_ORDERLY, JOB_CORONER)
diff --git a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_suit.dm b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_suit.dm
index 88f2edf5255..d5e638a7e67 100644
--- a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_suit.dm
+++ b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_suit.dm
@@ -49,7 +49,7 @@ GLOBAL_LIST_INIT(loadout_exosuits, generate_loadout_items(/datum/loadout_item/su
item_path = /obj/item/clothing/suit/koreacoat
/datum/loadout_item/suit/czech
- name = "Modern Winter Coat"
+ name = "Czech Winter Coat"
item_path = /obj/item/clothing/suit/modernwintercoatthing
/datum/loadout_item/suit/mantella
diff --git a/modular_skyrat/modules/loadouts/loadout_items/under/loadout_datum_under.dm b/modular_skyrat/modules/loadouts/loadout_items/under/loadout_datum_under.dm
index 39b5bd34567..efbc552fceb 100644
--- a/modular_skyrat/modules/loadouts/loadout_items/under/loadout_datum_under.dm
+++ b/modular_skyrat/modules/loadouts/loadout_items/under/loadout_datum_under.dm
@@ -145,7 +145,7 @@ GLOBAL_LIST_INIT(loadout_miscunders, generate_loadout_items(/datum/loadout_item/
restricted_roles = list(JOB_PARAMEDIC)
/datum/loadout_item/under/jumpsuit/paramed_light_skirt
- name = "Light Paramedic Uniform"
+ name = "Light Paramedic Skirt"
item_path = /obj/item/clothing/under/rank/medical/paramedic/skyrat/light/skirt
restricted_roles = list(JOB_PARAMEDIC)
@@ -179,10 +179,6 @@ GLOBAL_LIST_INIT(loadout_miscunders, generate_loadout_items(/datum/loadout_item/
item_path = /obj/item/clothing/under/rank/cargo/tech
restricted_roles = list(JOB_CARGO_TECHNICIAN, JOB_QUARTERMASTER, JOB_CUSTOMS_AGENT)
-/datum/loadout_item/under/jumpsuit/cargo/shorts
- name = "Cargo Technician's Shorts"
- item_path = /obj/item/clothing/under/rank/cargo/tech/alt
-
/datum/loadout_item/under/jumpsuit/cargo/skirt
name = "Cargo Technician's Skirt"
item_path = /obj/item/clothing/under/rank/cargo/tech/skirt
@@ -387,7 +383,7 @@ GLOBAL_LIST_INIT(loadout_miscunders, generate_loadout_items(/datum/loadout_item/
item_path = /obj/item/clothing/under/dress/skirt/skyrat/red_skirt
/datum/loadout_item/under/miscellaneous/black_skirt
- name = "Black Skirt"
+ name = "Black Skirt (Skyrat)"
item_path = /obj/item/clothing/under/dress/skirt/skyrat/black_skirt
/datum/loadout_item/under/miscellaneous/swept_skirt
@@ -747,10 +743,6 @@ GLOBAL_LIST_INIT(loadout_miscunders, generate_loadout_items(/datum/loadout_item/
name = "Navy Suit"
item_path = /obj/item/clothing/under/suit/navy
-/datum/loadout_item/under/formal/maid_outfit
- name = "Maid Outfit"
- item_path = /obj/item/clothing/under/costume/maid
-
/datum/loadout_item/under/formal/maid_uniform
name = "Maid Uniform"
item_path = /obj/item/clothing/under/rank/civilian/janitor/maid
diff --git a/modular_skyrat/modules/loadouts/loadout_ui/loadout_manager.dm b/modular_skyrat/modules/loadouts/loadout_ui/loadout_manager.dm
index b1edfb7fa3d..3d5c144a69a 100644
--- a/modular_skyrat/modules/loadouts/loadout_ui/loadout_manager.dm
+++ b/modular_skyrat/modules/loadouts/loadout_ui/loadout_manager.dm
@@ -1,8 +1,8 @@
/// -- The loadout manager and UI --
/// Tracking when a client has an open loadout manager, to prevent funky stuff.
/client
- /// A ref to loadout_manager datum.
- var/datum/loadout_manager/open_loadout_ui = null
+ /// A weakref to loadout_manager datum.
+ var/datum/weakref/open_loadout_ui
/// Datum holder for the loadout manager UI.
/datum/loadout_manager
@@ -10,28 +10,22 @@
var/client/owner
/// The current selected loadout list.
var/list/loadout_on_open
- /// The key of the dummy we use to generate sprites
- var/dummy_key
- /// The dir the dummy is facing.
- var/list/dummy_dir = list(SOUTH)
- /// A ref to the dummy outfit we're using
- var/datum/outfit/player_loadout/custom_loadout
- /// Whether we see our favorite job's clothes on the dummy
- var/view_job_clothes = TRUE
/// Our currently open greyscaling menu.
var/datum/greyscale_modify_menu/menu
/datum/loadout_manager/Destroy(force, ...)
+ if(menu)
+ SStgui.close_uis(menu)
+ menu = null
+ owner?.open_loadout_ui = null
owner = null
QDEL_NULL(menu)
- QDEL_NULL(custom_loadout)
return ..()
/datum/loadout_manager/New(user)
owner = CLIENT_FROM_VAR(user)
- owner.open_loadout_ui = src
loadout_on_open = LAZYLISTDUPLICATE(owner.prefs.loadout_list)
- custom_loadout = new()
+ owner.open_loadout_ui = WEAKREF(src)
/datum/loadout_manager/ui_close(mob/user)
owner?.prefs.save_character()
@@ -39,7 +33,6 @@
SStgui.close_uis(menu)
menu = null
owner?.open_loadout_ui = null
- qdel(custom_loadout)
qdel(src)
/datum/loadout_manager/ui_state(mob/user)
@@ -50,6 +43,7 @@
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "LoadoutManager")
+ ui.set_autoupdate(FALSE)
ui.open()
/datum/loadout_manager/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
@@ -104,18 +98,6 @@
LAZYNULL(owner.prefs.loadout_list)
owner?.prefs?.character_preview_view.update_body()
- // Rotates the dummy left or right depending on params["dir"]
- if("rotate_dummy")
- rotate_model_dir(params["dir"])
-
- // Toggles between showing all dirs of the dummy at once.
- if("show_all_dirs")
- toggle_model_dirs()
-
- if("update_preview")
- owner?.prefs.preview_pref = params["updated_preview"]
- owner?.prefs?.character_preview_view.update_body()
-
if("donator_explain")
if(GLOB.donator_list[owner.ckey])
to_chat(owner, examine_block("Thank you for donating, this item is for you <3!"))
@@ -266,20 +248,6 @@
to_chat(owner, examine_block(composed_message))
-/// Rotate the dummy [DIR] direction, or reset it to SOUTH dir if we're showing all dirs at once.
-/datum/loadout_manager/proc/rotate_model_dir(dir)
- if(dir == "left")
- owner?.prefs?.character_preview_view.dir = turn(owner?.prefs?.character_preview_view.dir, 90)
- else
- owner?.prefs?.character_preview_view.dir = turn(owner?.prefs?.character_preview_view.dir, -90)
-
-/// Toggle between showing all the dirs and just the front dir of the dummy.
-/datum/loadout_manager/proc/toggle_model_dirs()
- if(dummy_dir.len > 1)
- dummy_dir = list(SOUTH)
- else
- dummy_dir = GLOB.cardinals
-
/datum/loadout_manager/ui_data(mob/user)
var/list/data = list()
@@ -288,10 +256,6 @@
all_selected_paths += path
data["selected_loadout"] = all_selected_paths
data["user_is_donator"] = !!(GLOB.donator_list[owner.ckey] || is_admin(owner))
- data["mob_name"] = owner.prefs.read_preference(/datum/preference/name/real_name)
- data["ismoth"] = istype(owner.prefs.read_preference(/datum/preference/choiced/species), /datum/species/moth) // Moth's humanflaticcon isn't the same dimensions for some reason
- data["preview_options"] = list(PREVIEW_PREF_JOB, PREVIEW_PREF_LOADOUT, PREVIEW_PREF_NAKED)
- data["preview_selection"] = owner?.prefs.preview_pref
return data
diff --git a/modular_skyrat/modules/lorecaster/code/archive_viewer.dm b/modular_skyrat/modules/lorecaster/code/archive_viewer.dm
index a68478faa66..71df7918bb3 100644
--- a/modular_skyrat/modules/lorecaster/code/archive_viewer.dm
+++ b/modular_skyrat/modules/lorecaster/code/archive_viewer.dm
@@ -1,11 +1,11 @@
/datum/computer_file/program/news_archive
filename = "NewsArchive"
filedesc = "Nanotrasen News Archives"
- category = PROGRAM_CATEGORY_CREW
- program_icon_state = "generic"
+ downloader_category = PROGRAM_CATEGORY_EQUIPMENT
+ program_open_overlay = "generic"
extended_desc = "This program lets you view out-of-circulation articles from the Nanotrasen News Network."
- usage_flags = PROGRAM_ALL
- requires_ntnet = TRUE
+ can_run_on_flags = PROGRAM_ALL
+ program_flags = PROGRAM_REQUIRES_NTNET
size = 6
tgui_id = "NtosNewsArchive"
program_icon = "newspaper"
diff --git a/modular_skyrat/modules/mapping/code/pool.dm b/modular_skyrat/modules/mapping/code/pool.dm
index f50abbabe5a..4d1a7095887 100644
--- a/modular_skyrat/modules/mapping/code/pool.dm
+++ b/modular_skyrat/modules/mapping/code/pool.dm
@@ -15,7 +15,6 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
/obj/effect/overlay/water/top
icon_state = "top"
diff --git a/modular_skyrat/modules/mapping/voidraptor/code/clothing.dm b/modular_skyrat/modules/mapping/voidraptor/code/clothing.dm
index 1d3b995e409..200664b8b70 100644
--- a/modular_skyrat/modules/mapping/voidraptor/code/clothing.dm
+++ b/modular_skyrat/modules/mapping/voidraptor/code/clothing.dm
@@ -11,7 +11,7 @@
new /obj/item/cautery(src)
new /obj/item/bonesetter(src)
new /obj/item/surgical_drapes(src)
- new /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown(src)
+ new /obj/item/clothing/suit/toggle/labcoat/hospitalgown(src)
new /obj/item/clothing/mask/surgical(src)
new /obj/item/razor(src)
new /obj/item/blood_filter(src)
diff --git a/modular_skyrat/modules/medical_designs/medical_designs.dm b/modular_skyrat/modules/medical_designs/medical_designs.dm
index d0360fbfc2b..dc2e28def25 100644
--- a/modular_skyrat/modules/medical_designs/medical_designs.dm
+++ b/modular_skyrat/modules/medical_designs/medical_designs.dm
@@ -3,7 +3,7 @@
id = "hospital_gown"
build_type = PROTOLATHE
materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT)
- build_path = /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown
+ build_path = /obj/item/clothing/suit/toggle/labcoat/hospitalgown
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL
)
diff --git a/modular_skyrat/modules/modular_implants/code/nifs.dm b/modular_skyrat/modules/modular_implants/code/nifs.dm
index 5d2b1aabd4a..a184b4e7ed9 100644
--- a/modular_skyrat/modules/modular_implants/code/nifs.dm
+++ b/modular_skyrat/modules/modular_implants/code/nifs.dm
@@ -126,7 +126,7 @@
QDEL_LIST(loaded_nifsofts)
return ..()
-/obj/item/organ/internal/cyberimp/brain/nif/Insert(mob/living/carbon/human/insertee, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/cyberimp/brain/nif/Insert(mob/living/carbon/human/insertee, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
if(linked_mob && stored_ckey != insertee.ckey && theft_protection)
diff --git a/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm b/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm
index 4cd6beea379..d60dd4ac3f4 100644
--- a/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm
+++ b/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm
@@ -14,11 +14,11 @@ GLOBAL_LIST_INIT(purchasable_nifsofts, list(
filename = "nifsoftcatalog"
filedesc = "NIFSoft Catalog"
extended_desc = "A virtual storefront that allows the user to install NIFSofts and purchase various NIF related products"
- category = PROGRAM_CATEGORY_MISC
+ downloader_category = PROGRAM_CATEGORY_DEVICE
size = 3
tgui_id = "NtosNifsoftCatalog"
program_icon = "bag-shopping"
- usage_flags = PROGRAM_TABLET
+ can_run_on_flags = PROGRAM_PDA
///What bank account is money being drawn out of?
var/datum/bank_account/paying_account
///What NIF are the NIFSofts being sent to?
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/emote.dm b/modular_skyrat/modules/modular_items/lewd_items/code/emote.dm
new file mode 100644
index 00000000000..f7645d92016
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/emote.dm
@@ -0,0 +1,40 @@
+/datum/emote
+ /// If we should check a preference for this emote
+ var/pref_to_check
+
+/datum/emote/living/lewd
+ pref_to_check = /datum/preference/toggle/erp
+
+// Can we play this emote to viewers?
+/datum/emote/proc/pref_check_emote(mob/user)
+ if(isnull(pref_to_check))
+ return TRUE
+ if(!user.client?.prefs.read_preference(pref_to_check))
+ return FALSE
+
+/datum/emote/living/lewd/can_run_emote(mob/living/carbon/user, status_check = TRUE, intentional)
+ return ..() && user.client?.prefs?.read_preference(pref_to_check)
+
+/datum/emote/living/lewd/lewdmoan
+ key = "lewdmoan"
+ key_third_person = "lewdmoans"
+ message = "moans lewdly!"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+ vary = TRUE
+ sound_volume = 35
+
+/datum/emote/living/lewd/lewdmoan/get_sound(mob/living/carbon/user)
+ if(!istype(user))
+ return
+
+ if(user.gender == MALE)
+ return pick('modular_skyrat/modules/modular_items/lewd_items/sounds/final_m1.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_m2.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_m3.ogg',
+
+ )
+ else
+ return pick('modular_skyrat/modules/modular_items/lewd_items/sounds/final_f1.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_f2.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_f3.ogg',
+ )
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
index 59241ed4dc1..81041896a72 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
@@ -12,107 +12,147 @@
vend_reply = "Enjoy!;We're glad to satisfy your desires!"
//STUFF SOLD HERE//
- products = list(//Sex toys
- /obj/item/clothing/sextoy/eggvib/signalvib = 8,
- /obj/item/assembly/signaler = 8,
- /obj/item/clothing/sextoy/eggvib = 8,
- /obj/item/clothing/sextoy/buttplug = 6,
- /obj/item/clothing/sextoy/nipple_clamps = 4,
- /obj/item/clothing/sextoy/dildo/double_dildo = 3,
- /obj/item/clothing/sextoy/vibroring = 6,
- /obj/item/condom_pack = 20,
- /obj/item/clothing/sextoy/dildo = 8,
- /obj/item/clothing/sextoy/dildo/custom_dildo = 8,
- /obj/item/tickle_feather = 8,
- /obj/item/clothing/sextoy/fleshlight = 8,
- /obj/item/kinky_shocker = 4,
- /obj/item/clothing/mask/leatherwhip = 4,
- /obj/item/clothing/sextoy/magic_wand = 4,
- /obj/item/bdsm_candle = 4,
- /obj/item/spanking_pad = 4,
- /obj/item/clothing/sextoy/vibrator = 4,
- /obj/item/serviette_pack = 10,
- /obj/item/restraints/handcuffs/lewd = 8,
- /obj/item/key/collar = 48,
- /obj/item/fancy_pillow = 32,
- /obj/item/stack/shibari_rope/full = 10,
- /obj/item/stack/shibari_rope/glow/full = 10,
+ product_categories = list(
+ list(
+ "name" = "Toys",
+ "icon" = FA_ICON_MAGIC_WAND_SPARKLES,
+ "products" = list(
+ //Sex Toys
+ /obj/item/clothing/sextoy/buttplug = 6,
+ /obj/item/clothing/sextoy/nipple_clamps = 4,
+ /obj/item/clothing/sextoy/eggvib = 8,
+ /obj/item/clothing/sextoy/eggvib/signalvib = 8,
+ /obj/item/assembly/signaler = 8,
+ /obj/item/clothing/sextoy/vibroring = 6,
- //clothing facial/head
- /obj/item/clothing/mask/ballgag = 8,
- /obj/item/clothing/mask/ballgag/choking = 8,
- /obj/item/clothing/mask/muzzle/ring = 4,
- /obj/item/clothing/head/domina_cap = 5,
- /obj/item/clothing/head/deprivation_helmet = 5,
- /obj/item/clothing/head/costume/skyrat/maid = 5,
- /obj/item/clothing/glasses/blindfold/kinky = 5,
- /obj/item/clothing/ears/kinky_headphones = 5,
- /obj/item/clothing/mask/gas/bdsm_mask = 5,
- /obj/item/reagent_containers/cup/lewd_filter = 5,
- /obj/item/clothing/glasses/hypno = 4,
- /obj/item/clothing/head/costume/kitty = 4,
- /obj/item/clothing/head/costume/rabbitears = 4,
+ //Dildo (Use-on-crotch)
+ /obj/item/clothing/sextoy/dildo = 8,
+ /obj/item/clothing/sextoy/dildo/double_dildo = 3,
+ /obj/item/clothing/sextoy/dildo/custom_dildo = 8,
+ /obj/item/clothing/sextoy/fleshlight = 8,
+ /obj/item/clothing/sextoy/magic_wand = 4,
+ /obj/item/clothing/sextoy/vibrator = 4,
+ //belt
+ /obj/item/clothing/strapon = 6,
- //neck
- /obj/item/clothing/neck/kink_collar = 8,
- /obj/item/clothing/neck/human_petcollar = 8,
- /obj/item/clothing/neck/human_petcollar/choker = 8,
- /obj/item/clothing/neck/human_petcollar/thinchoker = 8,
- /obj/item/clothing/neck/human_petcollar/locked/cow = 8,
- /obj/item/clothing/neck/human_petcollar/locked/bell = 8,
- /obj/item/clothing/neck/human_petcollar/locked/cross = 8,
- /obj/item/clothing/neck/human_petcollar/locked/spike = 8,
- /obj/item/clothing/neck/size_collar = 8,
+ //Multi-use
- //torso clothing
- /obj/item/clothing/under/misc/latex_catsuit = 8,
- /obj/item/clothing/suit/straight_jacket/latex_straight_jacket = 5,
- /obj/item/clothing/under/costume/maid = 5,
- /obj/item/clothing/under/rank/civilian/janitor/maid = 5,
- /obj/item/clothing/under/costume/lewdmaid = 5,
- /obj/item/clothing/suit/straight_jacket/shackles = 4,
- /obj/item/clothing/under/stripper_outfit = 5,
- /obj/item/clothing/under/costume/bunnylewd = 5,
- /obj/item/clothing/under/costume/bunnylewd/white = 5,
- /obj/item/clothing/under/misc/skyrat/gear_harness = 4,
+ /obj/item/kinky_shocker = 4,
+ /obj/item/clothing/mask/leatherwhip = 4,
+ /obj/item/bdsm_candle = 4,
+ /obj/item/spanking_pad = 4,
+ /obj/item/tickle_feather = 8,
+ ),
+ ),
+ list(
+ "name" = "Outfits",
+ "icon" = FA_ICON_SHIRT,
+ "products" = list(
+ /obj/item/clothing/under/pants/skyrat/chaps = 4,
+ /obj/item/clothing/under/costume/bunnylewd = 5,
+ /obj/item/clothing/under/costume/bunnylewd/white = 5,
+ /obj/item/clothing/head/costume/rabbitears = 4,//Ears together, right after Bunny Suit.
+ /obj/item/clothing/head/costume/kitty = 4,
- //hands
- /obj/item/clothing/gloves/ball_mittens = 8,
- /obj/item/clothing/gloves/latex_gloves = 8,
- /obj/item/clothing/gloves/evening = 5,
+ /obj/item/clothing/head/domina_cap = 5,
+ /obj/item/clothing/shoes/latex_heels/domina_heels = 4,
+ /obj/item/clothing/gloves/evening = 5,
- //legs
- /obj/item/clothing/shoes/latex_socks = 8,
- /obj/item/clothing/shoes/latex_heels = 4,
- /obj/item/clothing/shoes/latex_heels/domina_heels = 4,
- /obj/item/clothing/shoes/jackboots/knee = 3,
- /obj/item/clothing/under/pants/skyrat/chaps = 4,
+ /obj/item/clothing/under/misc/skyrat/gear_harness = 6,//Important "not-nude" outfit
+ /obj/item/clothing/shoes/jackboots/knee = 3,
- //belt
- /obj/item/clothing/strapon = 6,
- /obj/item/storage/belt/erpbelt = 5,
+ /obj/item/clothing/under/misc/latex_catsuit = 8,
+ /obj/item/clothing/gloves/latex_gloves = 8,
+ /obj/item/clothing/shoes/latex_heels = 4,
+ /obj/item/clothing/shoes/latex_socks = 8,
- //chems
- /obj/item/reagent_containers/pill/crocin = 20,
- /obj/item/reagent_containers/pill/camphor = 10,
- /obj/item/reagent_containers/cup/bottle/crocin = 6,
- /obj/item/reagent_containers/cup/bottle/camphor = 3,
- /obj/item/reagent_containers/cup/bottle/succubus_milk = 6, //Those are legal 'cause you can just turn off prefs in round in "CLOWN SMOKE MACHINE+PENIS ENLARGEMENT CHEMICAL CASE". Yes, i have special code-phrase for this. I've seen some shit.
- /obj/item/reagent_containers/cup/bottle/incubus_draft = 6,
+ /obj/item/storage/belt/erpbelt = 5,//Leather Belt, holds a lot of tools.
- //special
- /obj/item/clothing/glasses/nice_goggles = 1, //easter egg, don't touch plz)
+ /obj/item/clothing/head/costume/skyrat/maid = 5,
+ /obj/item/clothing/under/costume/maid = 5,
+ /obj/item/clothing/under/rank/civilian/janitor/maid = 5,
+ /obj/item/clothing/under/costume/lewdmaid = 5,
- //fur niture //haha you got it
- /obj/item/storage/box/bdsmbed_kit = 4,
- /obj/item/storage/box/strippole_kit = 4,
- /obj/item/storage/box/xstand_kit = 4,
- /obj/item/storage/box/milking_kit = 4,
- /obj/item/storage/box/shibari_stand = 4)
+ /obj/item/clothing/under/stripper_outfit = 5,
+ /obj/item/clothing/glasses/nice_goggles = 1, //easter egg, don't touch plz
+ ),
+ ),
+ list(
+ "name" = "Restraints",
+ "icon" = FA_ICON_HANDCUFFS,
+ "products" = list(
+ //Sex toys
+ /obj/item/restraints/handcuffs/lewd = 8,
+ /obj/item/stack/shibari_rope/full = 10,
+ /obj/item/stack/shibari_rope/glow/full = 10,
+
+ //clothing facial/head
+ /obj/item/clothing/mask/ballgag = 8,
+ /obj/item/clothing/mask/ballgag/choking = 8,
+ /obj/item/clothing/mask/muzzle/ring = 4,
+ /obj/item/clothing/head/deprivation_helmet = 5,
+ /obj/item/clothing/glasses/blindfold/kinky = 5,
+ /obj/item/clothing/ears/kinky_headphones = 5,
+ /obj/item/clothing/mask/gas/bdsm_mask = 5,
+ /obj/item/reagent_containers/cup/lewd_filter = 5,
+ /obj/item/clothing/glasses/hypno = 4,
+
+ //neck
+ /obj/item/key/collar = 48,
+ /obj/item/clothing/neck/kink_collar = 8,
+ /obj/item/clothing/neck/human_petcollar = 8,
+ /obj/item/clothing/neck/human_petcollar/choker = 8,
+ /obj/item/clothing/neck/human_petcollar/thinchoker = 8,
+ /obj/item/clothing/neck/human_petcollar/locked/bell = 8,
+ /obj/item/clothing/neck/human_petcollar/locked/cow = 8,
+ /obj/item/clothing/neck/human_petcollar/locked/cross = 8,
+ /obj/item/clothing/neck/human_petcollar/locked/spike = 8,
+
+ //torso clothing
+ /obj/item/clothing/suit/straight_jacket/latex_straight_jacket = 5,
+ /obj/item/clothing/suit/straight_jacket/shackles = 4,
+
+ //hands
+ /obj/item/clothing/gloves/ball_mittens = 8,
+ ),
+ ),
+ list(
+ "name" = "Consumables",
+ "icon" = FA_ICON_VIAL,
+ "products" = list(
+ //Sex toys
+ /obj/item/condom_pack = 20,
+ /obj/item/serviette_pack = 10,
+ /obj/item/fancy_pillow = 32,
+
+ //chems
+ /obj/item/reagent_containers/pill/crocin = 20,
+ /obj/item/reagent_containers/pill/camphor = 10,
+ /obj/item/reagent_containers/cup/bottle/crocin = 6,
+ /obj/item/reagent_containers/cup/bottle/camphor = 3,
+ /obj/item/reagent_containers/cup/bottle/succubus_milk = 6, //Those are legal 'cause you can just turn off prefs in round in "CLOWN SMOKE MACHINE+PENIS ENLARGEMENT CHEMICAL CASE". Yes, i have special code-phrase for this. I've seen some shit.
+ /obj/item/reagent_containers/cup/bottle/incubus_draft = 6,
+ ),
+ ),
+ list(
+ "name" = "Structures",
+ "icon" = FA_ICON_HAMMER,
+ "products" = list(
+ //fur niture //haha you got it
+ /obj/item/storage/box/bdsmbed_kit = 4,
+ /obj/item/storage/box/milking_kit = 4,
+ /obj/item/storage/box/shibari_stand = 4,
+ /obj/item/storage/box/strippole_kit = 4,
+ /obj/item/storage/box/xstand_kit = 4,
+ ),
+ ),
+ )
premium = list(
- /obj/item/clothing/neck/human_petcollar/locked/holo = 3)
+ /obj/item/clothing/neck/human_petcollar/locked/holo = 3,
+ /obj/item/clothing/neck/size_collar = 8,//It only works in the Interlink anyways
+ )
contraband = list(
/obj/item/electropack/shockcollar = 4,
@@ -129,8 +169,8 @@
refill_canister = /obj/item/vending_refill/lustwish
payment_department = ACCOUNT_SRV
- default_price = 30
- extra_price = 250
+ default_price = PAYCHECK_CREW * 0.6
+ extra_price = PAYCHECK_COMMAND * 2.5
//Changes the settings on the vendor, if the user uses the discount card.
/obj/machinery/vending/dorms/attackby(obj/item/used_item, mob/living/user, params)
diff --git a/modular_skyrat/modules/modular_vending/code/wardrobes.dm b/modular_skyrat/modules/modular_vending/code/wardrobes.dm
index 103bb2ca975..21e2ff5ebc8 100644
--- a/modular_skyrat/modules/modular_vending/code/wardrobes.dm
+++ b/modular_skyrat/modules/modular_vending/code/wardrobes.dm
@@ -2,7 +2,7 @@
skyrat_products = list(
/obj/item/radio/headset/headset_med = 3,
/obj/item/clothing/gloves/latex/nitrile = 2,
- /obj/item/clothing/suit/toggle/labcoat/skyrat/hospitalgown = 5,
+ /obj/item/clothing/suit/toggle/labcoat/hospitalgown = 5,
/obj/item/storage/belt/medbandolier = 2,
/obj/item/clothing/under/rank/engineering/engineer/skyrat/hazard_chem/emt = 2,
/obj/item/clothing/under/rank/medical/scrubs/skyrat/red = 4,
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm
index 5b298fec87c..fbf5f38b6cf 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm
@@ -1,7 +1,7 @@
// Base Sol rifle
/obj/item/gun/ballistic/automatic/sol_rifle
- name = "\improper Carwo 'd'Infanteria' Rifle"
+ name = "\improper Carwo-Cawil Battle Rifle"
desc = "A heavy battle rifle commonly seen in the hands of SolFed military types. Accepts any standard SolFed rifle magazine."
icon = 'modular_skyrat/modules/modular_weapons/icons/obj/company_and_or_faction_based/carwo_defense_systems/guns48x.dmi'
@@ -57,7 +57,7 @@
/obj/item/gun/ballistic/automatic/sol_rifle/examine_more(mob/user)
. = ..()
- . += "The d'Infanterie rifles are, as the name may imply, built by Carwo for \
+ . += "The Carwo-Cawil rifles are built by Carwo for \
use by SolFed's various infantry branches. Following the rather reasonable \
military requirements of using the same few cartridges and magazines, \
the lifespans of logistics coordinators and quartermasters everywhere \
@@ -74,7 +74,7 @@
// Sol marksman rifle
/obj/item/gun/ballistic/automatic/sol_rifle/marksman
- name = "\improper Carwo 'd'Elite' Marksman Rifle"
+ name = "\improper Cawil Marksman Rifle"
desc = "A heavy marksman rifle commonly seen in the hands of SolFed military types. Accepts any standard SolFed rifle magazine."
icon_state = "elite"
@@ -100,7 +100,7 @@
/obj/item/gun/ballistic/automatic/sol_rifle/marksman/examine_more(mob/user)
. = ..()
- . += "This particlar variant, often called 'd'Elite', is a marksman rifle. \
+ . += "This particlar variant is a marksman rifle. \
Automatic fire was forsaken for a semi-automatic setup, a more fitting \
stock, and more often than not a scope. Typically also seen with smaller \
magazines for convenience for the shooter, but as with any other Sol \
@@ -114,7 +114,7 @@
// Machinegun based on the base Sol rifle
/obj/item/gun/ballistic/automatic/sol_rifle/machinegun
- name = "\improper Carwo 'd'Outomaties' Machinegun"
+ name = "\improper Qarad Light Machinegun"
desc = "A hefty machinegun commonly seen in the hands of SolFed military types. Accepts any standard SolFed rifle magazine."
icon_state = "outomaties"
@@ -134,7 +134,7 @@
/obj/item/gun/ballistic/automatic/sol_rifle/machinegun/examine_more(mob/user)
. = ..()
- . += "The d'Outomaties variant of the rifle, what you are looking at now, \
+ . += "The 'Qarad' variant of the rifle, what you are looking at now, \
is a modification to turn the weapon into a passable, if sub-optimal \
light machinegun. To support the machinegun role, the internals were \
converted to make the gun into an open bolt, faster firing machine. These \
diff --git a/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm b/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm
index ef49a87084b..a2d01ca064b 100644
--- a/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm
+++ b/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm
@@ -15,7 +15,6 @@
buckle_lying = 0
SET_BASE_PIXEL(-8, -8)
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
/// The extra range that this turret gives regarding viewrange.
var/view_range = 2.5
/// Sound to play when overheated
@@ -164,7 +163,6 @@
register_user(user_to_buckle)
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
setDir(SOUTH)
playsound(src,'sound/mecha/mechmove01.ogg', 50, TRUE)
set_anchored(TRUE)
@@ -423,7 +421,6 @@
user.pixel_y = -8
if(EAST)
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
user.pixel_x = -22
user.pixel_y = 0
if(SOUTHEAST)
@@ -433,7 +430,6 @@
user.pixel_y = 14
if(SOUTH)
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
user.pixel_x = 0
user.pixel_y = 22
if(SOUTHWEST)
@@ -443,7 +439,6 @@
user.pixel_y = 14
if(WEST)
layer = ABOVE_MOB_LAYER
- plane = GAME_PLANE_UPPER
user.pixel_x = 22
user.pixel_y = 0
if(NORTHWEST)
diff --git a/modular_skyrat/modules/mutants/code/mutant_component.dm b/modular_skyrat/modules/mutants/code/mutant_component.dm
index 4e78065eb37..f8882646df5 100644
--- a/modular_skyrat/modules/mutants/code/mutant_component.dm
+++ b/modular_skyrat/modules/mutants/code/mutant_component.dm
@@ -156,7 +156,7 @@
/datum/component/mutant_infection/proc/regenerate()
if(!host.mind)
- var/list/candidates = poll_candidates_for_mob("Do you want to play as a mutant([host.name])?", target_mob = host)
+ var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a mutant([host.name])?", target_mob = host)
if(!candidates.len)
return
var/client/C = pick_n_take(candidates)
diff --git a/modular_skyrat/modules/oneclickantag/code/oneclickantag.dm b/modular_skyrat/modules/oneclickantag/code/oneclickantag.dm
index 8fd9ff2702b..cfe5386f4a9 100644
--- a/modular_skyrat/modules/oneclickantag/code/oneclickantag.dm
+++ b/modular_skyrat/modules/oneclickantag/code/oneclickantag.dm
@@ -196,7 +196,7 @@ If anyone can figure out how to get Obsessed to work I would be very appreciativ
return FALSE
/datum/admins/proc/make_wizard()
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", ROLE_WIZARD, null)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", role = ROLE_WIZARD)
var/mob/living/carbon/human/target
do
var/mob/dead/observer/selected = pick_n_take(candidates)
@@ -211,7 +211,7 @@ If anyone can figure out how to get Obsessed to work I would be very appreciativ
return TRUE
/datum/admins/proc/make_nukies(maxCount = 5)
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, null)
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a nuke team being sent in?", role = ROLE_OPERATIVE)
var/list/mob/dead/observer/chosen = list()
var/mob/dead/observer/theghost = null
if(candidates.len)
diff --git a/modular_skyrat/modules/opposing_force/code/equipment/ammo.dm b/modular_skyrat/modules/opposing_force/code/equipment/ammo.dm
index 46734bcc952..32ec19d2811 100644
--- a/modular_skyrat/modules/opposing_force/code/equipment/ammo.dm
+++ b/modular_skyrat/modules/opposing_force/code/equipment/ammo.dm
@@ -49,13 +49,13 @@
description = "A box of slug shotgun shells, fires only one big fuck-off bullet that wounds extra hard."
item_type = /obj/item/ammo_box/advanced/s12gauge
-/datum/opposing_force_equipment/ammo_exotic/hp_slug
- description = "A box of hollow point slug shotgun shells, annihilates unarmoured combatants."
- item_type = /obj/item/ammo_box/advanced/s12gauge/hp
+/datum/opposing_force_equipment/ammo_exotic/hornet
+ description = "A box of hornet's nest shotgun shells, a less-lethal pellet spread that bounces towards targets off of walls."
+ item_type = /obj/item/ammo_box/advanced/s12gauge/beehive
-/datum/opposing_force_equipment/ammo_exotic/ap_slug
- description = "A box of armour piercing slug shotgun shells, are armoured targets your number one concern? With these shells you'll hardly remember they're wearing any."
- item_type = /obj/item/ammo_box/advanced/s12gauge/pt20
+/datum/opposing_force_equipment/ammo_exotic/stardust
+ description = "A box of stardust shotgun shells, a less-lethal spread of embedding electrodes that tire targets that move."
+ item_type = /obj/item/ammo_box/advanced/s12gauge/antitide
/datum/opposing_force_equipment/ammo_exotic/a357phasic
description = "A speedloader of .357 that ignores structure collision at the cost of some damage."
diff --git a/modular_skyrat/modules/opposing_force/code/equipment/guns.dm b/modular_skyrat/modules/opposing_force/code/equipment/guns.dm
index b0644c24565..66d34e97fae 100644
--- a/modular_skyrat/modules/opposing_force/code/equipment/guns.dm
+++ b/modular_skyrat/modules/opposing_force/code/equipment/guns.dm
@@ -12,7 +12,7 @@
new /obj/item/ammo_box/advanced/s12gauge/buckshot(src)
/datum/opposing_force_equipment/ranged/infanteria
- name = "'d'Infanteria' Rifle"
+ name = "Carwo-Cawil Battle Rifle"
description = "A heavy battle rifle, this one seems to be painted tacticool black. Accepts any standard SolFed rifle magazine."
item_type = /obj/item/storage/toolbox/guncase/skyrat/opfor/infanteria
diff --git a/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm b/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm
index 322a5a8f47d..d86011d0636 100644
--- a/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm
+++ b/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm
@@ -875,9 +875,6 @@
report += "[opfor_equipment.opposing_force_equipment.name] "
report += " "
- if(contractor_hub)
- report += contractor_round_end()
-
return report.Join("\n")
/// Adds the OPFOR in question to the ticket ping subsystem should it not be approved.
diff --git a/modular_skyrat/modules/organs/code/heart.dm b/modular_skyrat/modules/organs/code/heart.dm
new file mode 100644
index 00000000000..ae5ed164dc6
--- /dev/null
+++ b/modular_skyrat/modules/organs/code/heart.dm
@@ -0,0 +1,75 @@
+/obj/item/organ/internal/heart/snail
+ name = "twin gastropod hearts"
+ desc = "A primary heart normally nestled inside a gastropod's shell, and another in the owner's actual chest; necessary to maintain ample bloodflow through essentially two torsos."
+ icon = 'modular_skyrat/master_files/icons/obj/surgery.dmi'
+ icon_state = "heart-snail-on"
+ base_icon_state = "heart-snail"
+ maxHealth = 2 * STANDARD_ORGAN_THRESHOLD // There's two of them. Also, due to the special interaction below, it's best we make sure these aren't easily lost.
+ now_fixed = span_info("Your hearts begin to beat again.") //For the sake of verisimilitude.
+
+ COOLDOWN_DECLARE(shell_effect_cd)
+
+/obj/item/organ/internal/heart/snail/on_mob_insert(mob/living/carbon/organ_owner, special)
+ . = ..()
+ if(!ishuman(organ_owner))
+ return
+
+ if(!issnail(organ_owner)) //This is cleaner than checking for the shell, because there's not really going to be any non-horribly-bugged situation in which a snail will be lacking a shell.
+ return
+
+ var/mob/living/carbon/human/human_owner = organ_owner
+
+ RegisterSignal(human_owner, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(modify_damage))
+ RegisterSignal(human_owner, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(do_block_effect))
+
+/obj/item/organ/internal/heart/snail/on_mob_remove(mob/living/carbon/organ_owner, special)
+ . = ..()
+ if(!ishuman(organ_owner) || QDELETED(organ_owner))
+ return
+
+ var/mob/living/carbon/human/human_owner = organ_owner
+
+ UnregisterSignal(human_owner, list(COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, COMSIG_MOB_AFTER_APPLY_DAMAGE))
+
+/**
+ * Signal proc for [COMSIG_MOB_APPLY_DAMAGE_MODIFIERS]
+ *
+ * Adds a 0.5 modifier to attacks from the back, code borrowed (wholesale) from the roach heart.
+ */
+/obj/item/organ/internal/heart/snail/proc/modify_damage(mob/living/carbon/human/source, list/damage_mods, damage_amount, damagetype, def_zone, sharpness, attack_direction, obj/item/attacking_item)
+ SIGNAL_HANDLER
+
+ if(!is_blocking(source, damage_amount, damagetype, attack_direction))
+ return
+
+ damage_mods += 0.5
+
+/**
+ * Signal proc for [COMSIG_MOB_AFTER_APPLY_DAMAGE]
+ *
+ * Does a special effect if we blocked damage with our shell.
+ */
+/obj/item/organ/internal/heart/snail/proc/do_block_effect(mob/living/carbon/human/source, damage_dealt, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction, obj/item/attacking_item)
+ SIGNAL_HANDLER
+
+ if(!is_blocking(source, damage_dealt, damagetype, attack_direction))
+ return
+
+ if(COOLDOWN_FINISHED(src, shell_effect_cd))
+ source.visible_message(span_warning("[source]'s shell weathers the blow, absorbing most of the shock!"))
+ playsound(source, 'sound/weapons/parry.ogg', 50, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
+
+ COOLDOWN_START(src, shell_effect_cd, 5 SECONDS) // Cooldown resets EVERY time we get hit
+
+/// Checks if the passed mob is in a valid state to be blocking damage with the snail shell
+/obj/item/organ/internal/heart/snail/proc/is_blocking(mob/living/carbon/human/blocker, damage_amount, damagetype, attack_direction)
+ if(damage_amount < 5 || damagetype != BRUTE || !attack_direction)
+ return
+ if(!ishuman(blocker) || blocker.stat >= UNCONSCIOUS)
+ return FALSE
+ // No tactical spinning
+ if(HAS_TRAIT(blocker, TRAIT_SPINNING))
+ return FALSE
+ if(blocker.body_position == LYING_DOWN || (blocker.dir & attack_direction))
+ return TRUE
+ return FALSE
diff --git a/modular_skyrat/modules/organs/code/liver.dm b/modular_skyrat/modules/organs/code/liver.dm
index 1fe231786ea..8c275530084 100644
--- a/modular_skyrat/modules/organs/code/liver.dm
+++ b/modular_skyrat/modules/organs/code/liver.dm
@@ -1,6 +1,7 @@
/obj/item/organ/internal/liver/snail
name = "gastropod liver"
- icon_state = "liver-x"
+ icon = 'modular_skyrat/master_files/icons/obj/surgery.dmi'
+ icon_state = "liver-snail"
desc = "Due to snailfolk evolving in typically poisonous environments such as bogs, their liver has a higher tolerance for poisons compared to most."
maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD
toxTolerance = 5 //can shrug off up to 5u of toxins
diff --git a/modular_skyrat/modules/organs/code/tongue.dm b/modular_skyrat/modules/organs/code/tongue.dm
index f2a4787ef86..e151840ae48 100644
--- a/modular_skyrat/modules/organs/code/tongue.dm
+++ b/modular_skyrat/modules/organs/code/tongue.dm
@@ -12,7 +12,7 @@
icon_state = "tongue"
modifies_speech = TRUE
-/obj/item/organ/internal/tongue/dog/Insert(mob/living/carbon/signer, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/tongue/dog/Insert(mob/living/carbon/signer, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
signer.verb_ask = "arfs"
signer.verb_exclaim = "wans"
@@ -34,7 +34,7 @@
icon_state = "tongue"
modifies_speech = TRUE
-/obj/item/organ/internal/tongue/avian/Insert(mob/living/carbon/signer, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/tongue/avian/Insert(mob/living/carbon/signer, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
signer.verb_ask = "peeps"
signer.verb_exclaim = "squawks"
diff --git a/modular_skyrat/modules/shotgunrebalance/code/ammobox.dm b/modular_skyrat/modules/shotgunrebalance/code/ammobox.dm
index ab769454bd7..c2d98779313 100644
--- a/modular_skyrat/modules/shotgunrebalance/code/ammobox.dm
+++ b/modular_skyrat/modules/shotgunrebalance/code/ammobox.dm
@@ -1,6 +1,6 @@
/obj/item/ammo_box/advanced/s12gauge
name = "Slug ammo box"
- desc = "A box of shotgun shells."
+ desc = "A box of 15 slug shells. Large, singular shots that pack a punch."
icon = 'modular_skyrat/modules/shotgunrebalance/icons/shotbox.dmi'
icon_state = "slug"
ammo_type = /obj/item/ammo_casing/shotgun
@@ -9,90 +9,77 @@
/obj/item/ammo_box/advanced/s12gauge/buckshot
name = "Buckshot ammo box"
+ desc = "A box of 15 buckshot shells. These have a modest spread of weaker projectiles."
icon_state = "buckshot"
ammo_type = /obj/item/ammo_casing/shotgun/buckshot
max_ammo = 15
/obj/item/ammo_box/advanced/s12gauge/rubber
name = "Rubbershot ammo box"
+ desc = "A box of 15 rubbershot shells. These have a modest spread of weaker, less-lethal projectiles."
icon_state = "rubber"
ammo_type = /obj/item/ammo_casing/shotgun/rubbershot
max_ammo = 15
/obj/item/ammo_box/advanced/s12gauge/bean
name = "Beanbag Slug ammo box"
+ desc = "A box of 15 beanbag slug shells. These are large, singular beanbags that pack a less-lethal punch."
icon_state = "bean"
ammo_type = /obj/item/ammo_casing/shotgun/beanbag
max_ammo = 15
/obj/item/ammo_box/advanced/s12gauge/magnum
- name = "Magnum Buckshot ammo box"
+ name = "Magnum blockshot ammo box"
+ desc = "A box of 15 magnum blockshot shells. The size of the pellet is larger in diameter than the typical shot, but there are less of them inside each shell."
icon_state = "magnum"
ammo_type = /obj/item/ammo_casing/shotgun/magnum
max_ammo = 15
/obj/item/ammo_box/advanced/s12gauge/express
- name = "Express Buckshot ammo box"
+ name = "Express pelletshot ammo box"
+ desc = "A box of 15 express pelletshot shells. The size of the pellet is smaller in diameter than the typical shot, but there are more of them inside each shell."
icon_state = "express"
ammo_type = /obj/item/ammo_casing/shotgun/express
max_ammo = 15
-/obj/item/ammo_box/advanced/s12gauge/hp
- name = "Hollow Point Slug ammo box"
- icon_state = "hollowpoint"
- ammo_type = /obj/item/ammo_casing/shotgun/hp
- max_ammo = 15
-
/obj/item/ammo_box/advanced/s12gauge/hunter
- name = "Hunter Buckshot ammo box"
+ name = "Hunter slug ammo box"
+ desc = "A box of 15 hunter slug shells. These shotgun slugs excel at damaging the local fauna."
icon_state = "hunter"
ammo_type = /obj/item/ammo_casing/shotgun/hunter
max_ammo = 15
-/obj/item/ammo_box/advanced/s12gauge/pt20
- name = "PT-20 Armor Piercing Slug ammo box"
- icon_state = "apshell"
- ammo_type = /obj/item/ammo_casing/shotgun/pt20
- max_ammo = 15
-
-/obj/item/ammo_box/advanced/s12gauge/rip
- name = "RIP Slug ammo box"
- icon_state = "rip"
- ammo_type = /obj/item/ammo_casing/shotgun/rip
- max_ammo = 15
-
/obj/item/ammo_box/advanced/s12gauge/flechette
name = "Flechette ammo box"
+ desc = "A box of 15 flechette shells. Each shell contains a small group of tumbling blades that excel at causing terrible wounds."
icon_state = "flechette"
ammo_type = /obj/item/ammo_casing/shotgun/flechette
max_ammo = 15
/obj/item/ammo_box/advanced/s12gauge/beehive
- name = "B3-HVE 'Beehive' ammo box"
+ name = "Hornet's nest ammo box"
+ desc = "A box of 15 hornet's nest shells. These are less-lethal shells that will bounce off walls and direct themselves toward nearby targets."
icon_state = "beehive"
ammo_type = /obj/item/ammo_casing/shotgun/beehive
max_ammo = 15
/obj/item/ammo_box/advanced/s12gauge/antitide
- name = "4NT1-TD3 'Suppressor' ammo box"
+ name = "Stardust ammo box"
+ desc = "A box of 15 express pelletshot shells. These are less-lethal and will embed in targets, causing pain on movement."
icon_state = "antitide"
ammo_type = /obj/item/ammo_casing/shotgun/antitide
max_ammo = 15
-/obj/item/ammo_box/advanced/s12gauge/iceblox
- name = "Iceshot ammo box"
- icon_state = "iceblox"
- ammo_type = /obj/item/ammo_casing/shotgun/iceblox
- max_ammo = 15
-
/obj/item/ammo_box/advanced/s12gauge/incendiary
name = "Incendiary Slug ammo box"
+ desc = "A box of 15 incendiary slug shells. These will ignite targets and leave a trail of fire behind them."
icon_state = "incendiary"
ammo_type = /obj/item/ammo_casing/shotgun/incendiary
max_ammo = 15
/obj/item/ammo_box/advanced/s12gauge/honk
name = "Confetti ammo box"
+ desc = "A box of 35 shotgun shells. These ones contain harmless confetti, yippie!"
icon_state = "honk"
ammo_type = /obj/item/ammo_casing/shotgun/honk
max_ammo = 35
diff --git a/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm b/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm
index 853ac892aa1..c1ad1e6c082 100644
--- a/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm
+++ b/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm
@@ -1,25 +1,18 @@
#define AMMO_MATS_SHOTGUN list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 4) // not quite as thick as a half-sheet
-#define AMMO_MATS_SHOTGUN_PT20 list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 3,\
- /datum/material/plasma = SMALL_MATERIAL_AMOUNT * 0.5,\
- /datum/material/titanium = SMALL_MATERIAL_AMOUNT * 0.5) // plastitanium slug
-
-#define AMMO_MATS_SHOTGUN_RIP list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 3,\
- /datum/material/bronze = SMALL_MATERIAL_AMOUNT * 1) // the bronze is because real RIP shells are made with copper, apparently
-
#define AMMO_MATS_SHOTGUN_FLECH list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2,\
- /datum/material/titanium = SMALL_MATERIAL_AMOUNT * 2)
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2)
#define AMMO_MATS_SHOTGUN_HIVE list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2,\
/datum/material/plasma = SMALL_MATERIAL_AMOUNT * 1,\
/datum/material/silver = SMALL_MATERIAL_AMOUNT * 1)
#define AMMO_MATS_SHOTGUN_TIDE list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2,\
- /datum/material/gold = SMALL_MATERIAL_AMOUNT * 1,\
- /datum/material/uranium = SMALL_MATERIAL_AMOUNT * 1) // i mean. i - i guess?
+ /datum/material/plasma = SMALL_MATERIAL_AMOUNT * 1,\
+ /datum/material/gold = SMALL_MATERIAL_AMOUNT * 1)
-#define AMMO_MATS_SHOTGUN_TEMP list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2,\
- /datum/material/plasma = SMALL_MATERIAL_AMOUNT * 2) // covers both iceblox and incendiary
+#define AMMO_MATS_SHOTGUN_PLASMA list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2,\
+ /datum/material/plasma = SMALL_MATERIAL_AMOUNT * 2)
/obj/item/ammo_casing/shotgun
icon = 'modular_skyrat/modules/shotgunrebalance/icons/shotshells.dmi'
@@ -43,7 +36,7 @@
\
INCENDIARY: Leaves a trail of fire when shot, sets targets aflame."
advanced_print_req = TRUE
- custom_materials = AMMO_MATS_SHOTGUN_TEMP
+ custom_materials = AMMO_MATS_SHOTGUN_PLASMA
/obj/item/ammo_casing/shotgun/techshell
can_be_printed = FALSE // techshell... casing! so not really usable on its own but if you're gonna make these go raid a seclathe.
@@ -91,105 +84,74 @@
sacrificing individual pellet strength for sheer stopping power in what's best described as \"spitting distance\".\
\
HORNET'S NEST: Fire an overwhelming amount of projectiles in a single shot."
- // ...you know what if you're confident you can get up in there, you might as well get to use it if you're able to print Weird Shells.
-
-/obj/item/ammo_casing/shotgun/hp
- name = "hollow point slug"
- desc = "A 12 gauge hollow point slug purpose built for unarmored targets."
- icon_state = "stunshell"
- projectile_type = /obj/projectile/bullet/shotgun_slug/hp
- advanced_print_req = TRUE
-
-/obj/projectile/bullet/shotgun_slug/hp
- name = "12g hollow point shotgun slug"
- damage = 60
- wound_bonus = 0
- bare_wound_bonus = 40
- weak_against_armour = TRUE
-
-/obj/item/ammo_casing/shotgun/pt20
- name = "PT-20 armor piercing slug"
- desc = "A 12 gauge plastitanium slug purpose built to penetrate armored targets."
- icon_state = "apshell"
- projectile_type = /obj/projectile/bullet/shotgun_slug/pt20
- custom_materials = AMMO_MATS_SHOTGUN_PT20
- advanced_print_req = TRUE
-
-/obj/projectile/bullet/shotgun_slug/pt20
- name = "armor piercing shotgun slug"
- damage = 40
- armour_penetration = 50
-
-/obj/item/ammo_casing/shotgun/rip
- name = "RIP shotgun slug"
- desc = "A Radically Invasive Projectile Slug that is designed to cause massive damage against unarmored targets by embedding inside them."
- icon_state = "ripshell"
- projectile_type = /obj/projectile/bullet/shotgun_slug/rip
- custom_materials = AMMO_MATS_SHOTGUN_RIP
- advanced_print_req = TRUE
-
-/obj/projectile/bullet/shotgun_slug/rip
- name = "RIP shotgun slug"
- damage = 50
- weak_against_armour = TRUE
- embedding = list(embed_chance=80, pain_chance=40, fall_chance=5, jostle_chance=5, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.5, pain_mult=5, rip_time=30)
+ can_be_printed = FALSE
/obj/item/ammo_casing/shotgun/buckshot
name = "buckshot shell"
desc = "A 12 gauge buckshot shell."
icon_state = "gshell"
projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot
- pellets = 8 //Original: 6 || 8 x 8 = 64 Damage Potential, 34 Damage at 4 tile range
+ pellets = 8 // 8 * 6 for 48 damage if every pellet hits, we want to keep lethal shells ~50 damage
variance = 25
/obj/projectile/bullet/pellet/shotgun_buckshot
name = "buckshot pellet"
- damage = 8//7.5
- wound_bonus = 5
- bare_wound_bonus = 5
- wound_falloff_tile = -2.5 // low damage + additional dropoff will already curb wounding potential anything past point blank
- weak_against_armour = TRUE // Did you knew shotguns are actually shit against armor?
+ damage = 6
+ weak_against_armour = TRUE
/obj/item/ammo_casing/shotgun/rubbershot
name = "rubber shot"
desc = "A shotgun casing filled with densely-packed rubber balls, used to incapacitate crowds from a distance."
icon_state = "rshell"
projectile_type = /obj/projectile/bullet/pellet/shotgun_rubbershot
- pellets = 7
+ pellets = 6 // 6 * 10 for 60 stamina damage, + some small amount of brute, we want to keep less lethal shells ~60
variance = 20
harmful = FALSE
+/obj/projectile/bullet/pellet/shotgun_rubbershot
+ weak_against_armour = TRUE
+ stamina = 10
+
/obj/item/ammo_casing/shotgun/magnum
- name = "magnum buckshot shell"
- desc = "A 12 gauge buckshot shell that fires bigger pellets but has more spread. It is able to contend against armored targets."
+ name = "magnum blockshot shell"
+ desc = "A 12 gauge shell that fires fewer, larger pellets than buckshot. A favorite of SolFed anti-piracy enforcers, \
+ especially against the likes of vox."
icon_state = "magshell"
projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot/magnum
- pellets = 6 //6 x 10 = 60 Damage Potential, 27 Damage at 4 tile range
- variance = 30
+ pellets = 4 // Half as many pellets for twice the damage each pellet, same overall damage as buckshot
+ variance = 20
advanced_print_req = TRUE
/obj/projectile/bullet/pellet/shotgun_buckshot/magnum
- name = "magnum buckshot pellet"
- damage = 10
- wound_bonus = 8
- weak_against_armour = FALSE
+ name = "magnum blockshot pellet"
+ damage = 12
+ wound_bonus = 10
+
+/obj/projectile/bullet/pellet/shotgun_buckshot/magnum/Initialize(mapload)
+ . = ..()
+ transform = transform.Scale(1.25, 1.25)
/obj/item/ammo_casing/shotgun/express
- name = "express buckshot shell"
- desc = "A 12 gauge buckshot shell that has tighter spread and smaller projectiles."
+ name = "express pelletshot shell"
+ desc = "A 12 gauge shell that fires more and smaller projectiles than buckshot. Considered taboo to speak about \
+ openly near teshari, for reasons you would be personally blessed to not know at least some of."
icon_state = "expshell"
projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot/express
- pellets = 9 //6 x 9 = 51 Damage Potential, 33 Damage at 4 tile range
- variance = 20 //tighter spread
+ pellets = 12 // 1.3x The pellets for 0.6x the damage, same overall damage as buckshot
+ variance = 30 // Slightly wider spread than buckshot
/obj/projectile/bullet/pellet/shotgun_buckshot/express
name = "express buckshot pellet"
- damage = 6
- speed = 0.6
+ damage = 4
+ wound_bonus = 0
+
+/obj/projectile/bullet/pellet/shotgun_buckshot/express/Initialize(mapload)
+ . = ..()
+ transform = transform.Scale(0.75, 0.75)
/obj/item/ammo_casing/shotgun/flechette
name = "flechette shell"
- desc = "A 12 gauge flechette shell that specializes in ripping through armor."
+ desc = "A 12 gauge flechette shell that specializes in ripping unarmored targets apart."
icon_state = "fshell"
projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot/flechette
pellets = 8 //8 x 6 = 48 Damage Potential
@@ -199,35 +161,41 @@
/obj/projectile/bullet/pellet/shotgun_buckshot/flechette
name = "flechette"
+ icon = 'modular_skyrat/modules/shotgunrebalance/icons/projectiles.dmi'
+ icon_state = "flechette"
damage = 6
- weak_against_armour = FALSE //Were here to rip armor
- armour_penetration = 40
- wound_bonus = 9
- bare_wound_bonus = 0
+ wound_bonus = 10
+ bare_wound_bonus = 20
sharpness = SHARP_EDGED //Did you knew flechettes fly sideways into people
+/obj/projectile/bullet/pellet/shotgun_buckshot/flechette/Initialize(mapload)
+ . = ..()
+ SpinAnimation()
+
/obj/item/ammo_casing/shotgun/beehive
- name = "B3-HVE 'Beehive' shell"
- desc = "A highly experimental non-lethal shell filled with smart nanite pellets that re-aim themselves when bouncing off from surfaces. However they are not able to make out friend from foe."
+ name = "hornet shell"
+ desc = "A less-lethal 12 gauge shell that fires four pellets capable of bouncing off nearly any surface \
+ and re-aiming themselves toward the nearest target. They will, however, go for any target nearby."
icon_state = "cnrshell"
projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot/beehive
- pellets = 6
- variance = 20
+ pellets = 4
+ variance = 15
fire_sound = 'sound/weapons/taser.ogg'
harmful = FALSE
custom_materials = AMMO_MATS_SHOTGUN_HIVE
advanced_print_req = TRUE
/obj/projectile/bullet/pellet/shotgun_buckshot/beehive
- name = "beehive pellet"
+ name = "hornet flechette"
+ icon = 'modular_skyrat/modules/shotgunrebalance/icons/projectiles.dmi'
+ icon_state = "hornet"
damage = 4
- stamina = 10
+ stamina = 15
damage_falloff_tile = 0.1
stamina_falloff_tile = 0.1
wound_bonus = -5
bare_wound_bonus = 5
wound_falloff_tile = 0
- weak_against_armour = TRUE
sharpness = NONE
ricochets_max = 5
ricochet_chance = 200
@@ -238,11 +206,11 @@
ricochet_incidence_leeway = 0 //nanomachines son
/obj/item/ammo_casing/shotgun/antitide
- name = "4NT1-TD3 'Suppressor' shell"
+ name = "stardust shell"
desc = "A highly experimental shell filled with nanite electrodes that will embed themselves in soft targets. The electrodes are charged from kinetic movement which means moving targets will get punished more."
icon_state = "lasershell"
projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot/antitide
- pellets = 5
+ pellets = 8 // 8 * 7 for 56 stamina damage, plus whatever the embedded shells do
variance = 30
harmful = FALSE
fire_sound = 'sound/weapons/taser.ogg'
@@ -251,8 +219,10 @@
/obj/projectile/bullet/pellet/shotgun_buckshot/antitide
name = "electrode"
- damage = 4
- stamina = 6
+ icon = 'modular_skyrat/modules/shotgunrebalance/icons/projectiles.dmi'
+ icon_state = "stardust"
+ damage = 2
+ stamina = 8
damage_falloff_tile = 0.2
stamina_falloff_tile = 0.3
wound_bonus = 0
@@ -260,66 +230,27 @@
stutter = 3 SECONDS
jitter = 5 SECONDS
eyeblur = 1 SECONDS
- weak_against_armour = TRUE
sharpness = NONE
range = 8
- icon_state = "spark"
embedding = list(embed_chance=70, pain_chance=25, fall_chance=15, jostle_chance=80, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.9, pain_mult=2, rip_time=10)
-/obj/projectile/bullet/pellet/shotgun_buckshot/antitide/Initialize(mapload)
- . = ..()
- SpinAnimation()
- transform *= 0.25
-
/obj/projectile/bullet/pellet/shotgun_buckshot/antitide/on_range()
do_sparks(1, TRUE, src)
..()
-/obj/item/ammo_casing/shotgun/iceblox
- name = "Iceshot shell"
- desc = "A highly experimental shell filled with nanites that will lower the body temperature of hit targets."
- icon_state = "tshell"
- projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot/iceblox
- custom_materials = AMMO_MATS_SHOTGUN_TEMP
- pellets = 5
- variance = 20
- advanced_print_req = TRUE
-
-/obj/projectile/bullet/pellet/shotgun_buckshot/iceblox //see /obj/projectile/temp for the original code
- name = "iceblox pellet"
- damage_falloff_tile = 0.35
- damage = 5
- weak_against_armour = TRUE
- var/temperature = 30
-
-/obj/projectile/bullet/pellet/shotgun_buckshot/iceblox/on_hit(atom/target, blocked = 0, pierce_hit)
- . = ..()
- if(isliving(target))
- var/mob/living/UnluckyBastard = target
- UnluckyBastard.adjust_bodytemperature(((100-blocked)/100)*(temperature - UnluckyBastard.bodytemperature))
-
/obj/item/ammo_casing/shotgun/hunter
- name = "hunter buckshot shell"
- desc = "A 12 gauge buckshot shell that fires specially charged pellets that deal extra damage to simpler beings."
+ name = "hunter slug shell"
+ desc = "A 12 gauge slug shell that fires specially designed slugs that deal extra damage to the local planetary fauna"
icon_state = "huntershell"
- projectile_type = /obj/projectile/bullet/pellet/shotgun_buckshot/hunter
- pellets = 6 // 6 x 30 = 180 damage vs simples
- variance = 30
+ projectile_type = /obj/projectile/bullet/shotgun_slug/hunter
-/obj/projectile/bullet/pellet/shotgun_buckshot/hunter
- name = "hunter buckshot pellet"
- damage = 5
- wound_bonus = 0
- weak_against_armour = FALSE
- /// Bonus force dealt against certain factions
- var/faction_bonus_force = 25
- /// Any mob with a faction that exists in this list will take bonus damage/effects
- var/list/nemesis_path = /mob/living/simple_animal
+/obj/projectile/bullet/shotgun_slug/hunter
+ name = "12g hunter slug"
+ damage = 20
-/obj/projectile/bullet/pellet/shotgun_buckshot/hunter/prehit_pierce(mob/living/target, mob/living/carbon/human/user)
- if(istype(target, nemesis_path))
- damage += faction_bonus_force
- .=..()
+/obj/projectile/bullet/shotgun_slug/hunter/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/bane, mob_biotypes = MOB_BEAST, damage_multiplier = 5)
/obj/projectile/bullet/pellet/shotgun_improvised
weak_against_armour = TRUE // We will not have Improvised are Better 2.0
diff --git a/modular_skyrat/modules/shotgunrebalance/icons/projectiles.dmi b/modular_skyrat/modules/shotgunrebalance/icons/projectiles.dmi
new file mode 100644
index 00000000000..be56385841f
Binary files /dev/null and b/modular_skyrat/modules/shotgunrebalance/icons/projectiles.dmi differ
diff --git a/modular_skyrat/modules/synths/code/bodyparts/brain.dm b/modular_skyrat/modules/synths/code/bodyparts/brain.dm
index 9d92635d4d8..e734cb29d57 100644
--- a/modular_skyrat/modules/synths/code/bodyparts/brain.dm
+++ b/modular_skyrat/modules/synths/code/bodyparts/brain.dm
@@ -1,7 +1,7 @@
/obj/item/organ/internal/brain/synth
name = "compact positronic brain"
slot = ORGAN_SLOT_BRAIN
- zone = BODY_ZONE_CHEST
+ zone = BODY_ZONE_HEAD
organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
desc = "A cube of shining metal, four inches to a side and covered in shallow grooves. It has an IPC serial number engraved on the top. It is usually slotted into the chest of synthetic crewmembers."
@@ -10,13 +10,13 @@
/// The last time (in ticks) a message about brain damage was sent. Don't touch.
var/last_message_time = 0
-/obj/item/organ/internal/brain/synth/Insert(mob/living/carbon/user, special = FALSE, drop_if_replaced = TRUE, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/synth/on_mob_insert(mob/living/carbon/brain_owner, movement_flags = NO_ID_TRANSFER)
. = ..()
- if(user.stat != DEAD || !ishuman(user))
+ if(brain_owner.stat != DEAD || !ishuman(brain_owner))
return
- var/mob/living/carbon/human/user_human = user
+ var/mob/living/carbon/human/user_human = brain_owner
if(HAS_TRAIT(user_human, TRAIT_REVIVES_BY_HEALING) && user_human.health > SYNTH_BRAIN_WAKE_THRESHOLD)
user_human.revive(FALSE)
diff --git a/modular_skyrat/modules/synths/code/bodyparts/stomach.dm b/modular_skyrat/modules/synths/code/bodyparts/stomach.dm
index 6b08aa0af45..a8dfb30521e 100644
--- a/modular_skyrat/modules/synths/code/bodyparts/stomach.dm
+++ b/modular_skyrat/modules/synths/code/bodyparts/stomach.dm
@@ -47,7 +47,7 @@
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE
-/obj/item/organ/internal/stomach/synth/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/internal/stomach/synth/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
RegisterSignal(receiver, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge))
diff --git a/modular_skyrat/modules/synths/code/reagents/reagents.dm b/modular_skyrat/modules/synths/code/reagents/reagents.dm
index 54be7648ada..cc668d98cd1 100644
--- a/modular_skyrat/modules/synths/code/reagents/reagents.dm
+++ b/modular_skyrat/modules/synths/code/reagents/reagents.dm
@@ -4,9 +4,6 @@
/datum/reagent/medicine/syndicate_nanites //Used exclusively by Syndicate medical cyborgs
process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC //Let's not cripple synth ops
-/datum/reagent/medicine/lesser_syndicate_nanites
- process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC
-
/datum/reagent/medicine/stimulants
process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC //Syndicate developed 'accelerants' for synths?
diff --git a/modular_skyrat/modules/title_screen/code/job_traits.dm b/modular_skyrat/modules/title_screen/code/job_traits.dm
new file mode 100644
index 00000000000..2bbf74a4081
--- /dev/null
+++ b/modular_skyrat/modules/title_screen/code/job_traits.dm
@@ -0,0 +1,2 @@
+/datum/station_trait/proc/on_lobby_button_click(mob/dead/new_player/user)
+ return
diff --git a/modular_skyrat/modules/title_screen/code/new_player.dm b/modular_skyrat/modules/title_screen/code/new_player.dm
index f43349165fd..27c94319dd1 100644
--- a/modular_skyrat/modules/title_screen/code/new_player.dm
+++ b/modular_skyrat/modules/title_screen/code/new_player.dm
@@ -17,6 +17,11 @@
make_me_an_observer()
return
+ if(href_list["job_traits"])
+ play_lobby_button_sound()
+ show_job_traits()
+ return
+
if(href_list["server_swap"])
play_lobby_button_sound()
server_swap()
@@ -136,6 +141,32 @@
to_chat_immediate(src, "So long, spaceman.")
src.client << link(server_ip)
+/**
+ * Shows currently available job traits
+ */
+/mob/dead/new_player/proc/show_job_traits()
+ if (!client || client.interviewee)
+ return
+ if(!length(GLOB.lobby_station_traits))
+ to_chat(src, span_warning("There are currently no job traits available!"))
+ return
+ var/list/available_lobby_station_traits = list()
+ for (var/datum/station_trait/trait as anything in GLOB.lobby_station_traits)
+ if (!trait.can_display_lobby_button(client))
+ continue
+ available_lobby_station_traits += trait
+
+ if(!LAZYLEN(available_lobby_station_traits))
+ to_chat(src, span_warning("There are currently no job traits available!"))
+ return
+
+ var/datum/station_trait/clicked_trait = tgui_input_list(src, "Select a job trait to sign up for:", "Job Traits", available_lobby_station_traits)
+
+ if(!clicked_trait)
+ return
+
+ clicked_trait.on_lobby_button_click(src)
+
/**
* Shows the player a list of current polls, if any.
*/
diff --git a/modular_skyrat/modules/title_screen/code/station_traits.dm b/modular_skyrat/modules/title_screen/code/station_traits.dm
new file mode 100644
index 00000000000..a42bd11ecd2
--- /dev/null
+++ b/modular_skyrat/modules/title_screen/code/station_traits.dm
@@ -0,0 +1,7 @@
+/datum/station_trait/job/on_lobby_button_click(mob/dead/new_player/user)
+ if (LAZYFIND(lobby_candidates, user))
+ LAZYREMOVE(lobby_candidates, user)
+ to_chat(user, span_redtext("You have been removed from the [name] list of candidates."))
+ else
+ LAZYADD(lobby_candidates, user)
+ to_chat(user, span_greentext("You have been added to the [name] list of candidates."))
diff --git a/modular_skyrat/modules/title_screen/code/title_screen_html.dm b/modular_skyrat/modules/title_screen/code/title_screen_html.dm
index 52785ed1707..ec9250fea04 100644
--- a/modular_skyrat/modules/title_screen/code/title_screen_html.dm
+++ b/modular_skyrat/modules/title_screen/code/title_screen_html.dm
@@ -116,6 +116,9 @@ GLOBAL_LIST_EMPTY(startup_messages)
SWAP SERVERS
"}
+ if(length(GLOB.lobby_station_traits) && !SSticker.HasRoundStarted())
+ dat += {"JOB TRAITS"}
+
if(!is_guest_key(src.key))
dat += playerpolls()
diff --git a/modular_skyrat/modules/title_screen/code/title_screen_subsystem.dm b/modular_skyrat/modules/title_screen/code/title_screen_subsystem.dm
index 8ea5b81bb33..48b8dc72022 100644
--- a/modular_skyrat/modules/title_screen/code/title_screen_subsystem.dm
+++ b/modular_skyrat/modules/title_screen/code/title_screen_subsystem.dm
@@ -23,6 +23,8 @@ SUBSYSTEM_DEF(title)
var/list/progress_json = list()
/// The reference realtime that we're treating as 0 for this run
var/progress_reference_time = 0
+ /// A list of station traits that have lobby buttons
+ var/list/available_lobby_station_traits = list()
/datum/controller/subsystem/title/Initialize()
var/dat
diff --git a/modular_skyrat/modules/xenos_skyrat_redo/code/base_skyrat_xeno.dm b/modular_skyrat/modules/xenos_skyrat_redo/code/base_skyrat_xeno.dm
index 4b1f325c393..9a39d931aa2 100644
--- a/modular_skyrat/modules/xenos_skyrat_redo/code/base_skyrat_xeno.dm
+++ b/modular_skyrat/modules/xenos_skyrat_redo/code/base_skyrat_xeno.dm
@@ -7,7 +7,6 @@
base_pixel_x = -16 //All of the xeno sprites are 64x64, and we want them to be level with the tile they are on, much like oversized quirk users
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //above most mobs, but below speechbubbles
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
maptext_height = 64
maptext_width = 64
pressure_resistance = 200
diff --git a/sound/effects/confirmdropoff.ogg b/sound/effects/confirmdropoff.ogg
new file mode 100644
index 00000000000..835d9319921
Binary files /dev/null and b/sound/effects/confirmdropoff.ogg differ
diff --git a/strings/names/cargorilla.txt b/strings/names/cargorilla.txt
new file mode 100644
index 00000000000..ec135f5ca4d
--- /dev/null
+++ b/strings/names/cargorilla.txt
@@ -0,0 +1,7 @@
+Cala
+Cerchak
+Citrus
+Coco
+Grodd
+Paperwork
+Winston
diff --git a/strings/names/death_commando.txt b/strings/names/death_commando.txt
index d60fb0e5940..cdde4742ac4 100644
--- a/strings/names/death_commando.txt
+++ b/strings/names/death_commando.txt
@@ -1,4 +1,4 @@
-A whole bunch of spiders in a SWAT suit
+A whole bunch of spiders in a MODsuit
Al "Otta" Gore
AMERICA
Beat Punchbeef
diff --git a/tgstation.dme b/tgstation.dme
index 2fbdd518127..536061c8e03 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -159,6 +159,7 @@
#include "code\__DEFINES\nuclear_bomb.dm"
#include "code\__DEFINES\obj_flags.dm"
#include "code\__DEFINES\observers.dm"
+#include "code\__DEFINES\organ_movement.dm"
#include "code\__DEFINES\overlays.dm"
#include "code\__DEFINES\pai.dm"
#include "code\__DEFINES\paintings.dm"
@@ -484,6 +485,7 @@
#include "code\__HELPERS\_string_lists.dm"
#include "code\__HELPERS\admin.dm"
#include "code\__HELPERS\ai.dm"
+#include "code\__HELPERS\announcements.dm"
#include "code\__HELPERS\areas.dm"
#include "code\__HELPERS\atmospherics.dm"
#include "code\__HELPERS\atoms.dm"
@@ -756,6 +758,7 @@
#include "code\controllers\subsystem\persistent_paintings.dm"
#include "code\controllers\subsystem\ping.dm"
#include "code\controllers\subsystem\points_of_interest.dm"
+#include "code\controllers\subsystem\polling.dm"
#include "code\controllers\subsystem\profiler.dm"
#include "code\controllers\subsystem\queuelinks.dm"
#include "code\controllers\subsystem\radiation.dm"
@@ -793,6 +796,16 @@
#include "code\controllers\subsystem\wardrobe.dm"
#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\wiremod_composite.dm"
+#include "code\controllers\subsystem\dynamic\dynamic.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_hijacking.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_logging.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_midround_rolling.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets_latejoin.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets_midround.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets_roundstart.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_unfavorable_situation.dm"
+#include "code\controllers\subsystem\dynamic\ruleset_picking.dm"
#include "code\controllers\subsystem\movement\ai_movement.dm"
#include "code\controllers\subsystem\movement\cliff_falling.dm"
#include "code\controllers\subsystem\movement\hyperspace_drift.dm"
@@ -838,6 +851,7 @@
#include "code\datums\beam.dm"
#include "code\datums\browser.dm"
#include "code\datums\callback.dm"
+#include "code\datums\candidate_poll.dm"
#include "code\datums\chat_payload.dm"
#include "code\datums\chatmessage.dm"
#include "code\datums\dash_weapon.dm"
@@ -1762,6 +1776,7 @@
#include "code\datums\quirks\neutral_quirks\pride_pin.dm"
#include "code\datums\quirks\neutral_quirks\shifty_eyes.dm"
#include "code\datums\quirks\neutral_quirks\snob.dm"
+#include "code\datums\quirks\neutral_quirks\transhumanist.dm"
#include "code\datums\quirks\neutral_quirks\vegetarian.dm"
#include "code\datums\quirks\positive_quirks\alcohol_tolerance.dm"
#include "code\datums\quirks\positive_quirks\apathetic.dm"
@@ -1822,6 +1837,7 @@
#include "code\datums\skills\mining.dm"
#include "code\datums\station_traits\_station_trait.dm"
#include "code\datums\station_traits\admin_panel.dm"
+#include "code\datums\station_traits\job_traits.dm"
#include "code\datums\station_traits\negative_traits.dm"
#include "code\datums\station_traits\neutral_traits.dm"
#include "code\datums\station_traits\positive_traits.dm"
@@ -1899,6 +1915,7 @@
#include "code\datums\wires\airlock.dm"
#include "code\datums\wires\apc.dm"
#include "code\datums\wires\autolathe.dm"
+#include "code\datums\wires\canister.dm"
#include "code\datums\wires\conveyor.dm"
#include "code\datums\wires\ecto_sniffer.dm"
#include "code\datums\wires\emitter.dm"
@@ -1981,20 +1998,8 @@
#include "code\game\atom\atom_vv.dm"
#include "code\game\atom\atoms_initializing_EXPENSIVE.dm"
#include "code\game\gamemodes\events.dm"
-#include "code\game\gamemodes\game_mode.dm"
#include "code\game\gamemodes\objective.dm"
#include "code\game\gamemodes\objective_items.dm"
-#include "code\game\gamemodes\dynamic\dynamic.dm"
-#include "code\game\gamemodes\dynamic\dynamic_hijacking.dm"
-#include "code\game\gamemodes\dynamic\dynamic_logging.dm"
-#include "code\game\gamemodes\dynamic\dynamic_midround_rolling.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets_roundstart.dm"
-#include "code\game\gamemodes\dynamic\dynamic_simulations.dm"
-#include "code\game\gamemodes\dynamic\dynamic_unfavorable_situation.dm"
-#include "code\game\gamemodes\dynamic\ruleset_picking.dm"
#include "code\game\machinery\_machinery.dm"
#include "code\game\machinery\ai_slipper.dm"
#include "code\game\machinery\airlock_control.dm"
@@ -3224,6 +3229,10 @@
#include "code\modules\antagonists\traitor\components\traitor_objective_helpers.dm"
#include "code\modules\antagonists\traitor\components\traitor_objective_limit_per_time.dm"
#include "code\modules\antagonists\traitor\components\traitor_objective_mind_tracker.dm"
+#include "code\modules\antagonists\traitor\contractor\contract_teammate.dm"
+#include "code\modules\antagonists\traitor\contractor\contractor_hub.dm"
+#include "code\modules\antagonists\traitor\contractor\contractor_items.dm"
+#include "code\modules\antagonists\traitor\contractor\syndicate_contract.dm"
#include "code\modules\antagonists\traitor\objectives\assassination.dm"
#include "code\modules\antagonists\traitor\objectives\demoralise_assault.dm"
#include "code\modules\antagonists\traitor\objectives\destroy_heirloom.dm"
@@ -3644,7 +3653,6 @@
#include "code\modules\client\preferences\clothing.dm"
#include "code\modules\client\preferences\darkened_flash.dm"
#include "code\modules\client\preferences\food_allergy.dm"
-#include "code\modules\client\preferences\fov_darkness.dm"
#include "code\modules\client\preferences\fps.dm"
#include "code\modules\client\preferences\gender.dm"
#include "code\modules\client\preferences\ghost.dm"
@@ -4218,6 +4226,7 @@
#include "code\modules\jobs\job_types\bartender.dm"
#include "code\modules\jobs\job_types\botanist.dm"
#include "code\modules\jobs\job_types\captain.dm"
+#include "code\modules\jobs\job_types\cargo_gorilla.dm"
#include "code\modules\jobs\job_types\cargo_technician.dm"
#include "code\modules\jobs\job_types\chemist.dm"
#include "code\modules\jobs\job_types\chief_engineer.dm"
@@ -4236,8 +4245,6 @@
#include "code\modules\jobs\job_types\medical_doctor.dm"
#include "code\modules\jobs\job_types\mime.dm"
#include "code\modules\jobs\job_types\paramedic.dm"
-#include "code\modules\jobs\job_types\personal_ai.dm"
-#include "code\modules\jobs\job_types\positronic_brain.dm"
#include "code\modules\jobs\job_types\prisoner.dm"
#include "code\modules\jobs\job_types\psychologist.dm"
#include "code\modules\jobs\job_types\quartermaster.dm"
@@ -4245,7 +4252,6 @@
#include "code\modules\jobs\job_types\roboticist.dm"
#include "code\modules\jobs\job_types\scientist.dm"
#include "code\modules\jobs\job_types\security_officer.dm"
-#include "code\modules\jobs\job_types\servant_golem.dm"
#include "code\modules\jobs\job_types\shaft_miner.dm"
#include "code\modules\jobs\job_types\station_engineer.dm"
#include "code\modules\jobs\job_types\unassigned.dm"
@@ -4291,6 +4297,9 @@
#include "code\modules\jobs\job_types\spawner\lavaland_syndicate.dm"
#include "code\modules\jobs\job_types\spawner\lifebringer.dm"
#include "code\modules\jobs\job_types\spawner\maintenance_drone.dm"
+#include "code\modules\jobs\job_types\spawner\personal_ai.dm"
+#include "code\modules\jobs\job_types\spawner\positronic_brain.dm"
+#include "code\modules\jobs\job_types\spawner\servant_golem.dm"
#include "code\modules\jobs\job_types\spawner\skeleton.dm"
#include "code\modules\jobs\job_types\spawner\space_bar_patron.dm"
#include "code\modules\jobs\job_types\spawner\space_bartender.dm"
@@ -4302,6 +4311,8 @@
#include "code\modules\jobs\job_types\spawner\syndicate_cybersun_captain.dm"
#include "code\modules\jobs\job_types\spawner\venus_human_trap.dm"
#include "code\modules\jobs\job_types\spawner\zombie.dm"
+#include "code\modules\jobs\job_types\station_trait\bridge_assistant.dm"
+#include "code\modules\jobs\job_types\station_trait\cargo_gorilla.dm"
#include "code\modules\keybindings\bindings_atom.dm"
#include "code\modules\keybindings\bindings_client.dm"
#include "code\modules\keybindings\focus.dm"
@@ -4527,7 +4538,6 @@
#include "code\modules\mob\status_procs.dm"
#include "code\modules\mob\transform_procs.dm"
#include "code\modules\mob\camera\camera.dm"
-#include "code\modules\mob\dead\crew_manifest.dm"
#include "code\modules\mob\dead\dead.dm"
#include "code\modules\mob\dead\new_player\latejoin_menu.dm"
#include "code\modules\mob\dead\new_player\login.dm"
@@ -5120,7 +5130,6 @@
#include "code\modules\modular_computers\file_system\programs\notepad.dm"
#include "code\modules\modular_computers\file_system\programs\nt_pay.dm"
#include "code\modules\modular_computers\file_system\programs\ntdownloader.dm"
-#include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm"
#include "code\modules\modular_computers\file_system\programs\portrait_printer.dm"
#include "code\modules\modular_computers\file_system\programs\powermonitor.dm"
#include "code\modules\modular_computers\file_system\programs\radar.dm"
@@ -5135,16 +5144,19 @@
#include "code\modules\modular_computers\file_system\programs\techweb.dm"
#include "code\modules\modular_computers\file_system\programs\theme_selector.dm"
#include "code\modules\modular_computers\file_system\programs\wirecarp.dm"
+#include "code\modules\modular_computers\file_system\programs\antagonist\contractor_program.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm"
+#include "code\modules\modular_computers\file_system\programs\chatroom\conversation.dm"
+#include "code\modules\modular_computers\file_system\programs\chatroom\ntnrc_client.dm"
#include "code\modules\modular_computers\file_system\programs\maintenance\_maintenance_program.dm"
#include "code\modules\modular_computers\file_system\programs\maintenance\camera.dm"
#include "code\modules\modular_computers\file_system\programs\maintenance\modsuit.dm"
#include "code\modules\modular_computers\file_system\programs\maintenance\phys_scanner.dm"
+#include "code\modules\modular_computers\file_system\programs\maintenance\spectre_meter.dm"
#include "code\modules\modular_computers\file_system\programs\maintenance\themes.dm"
#include "code\modules\modular_computers\file_system\programs\messenger\messenger_data.dm"
#include "code\modules\modular_computers\file_system\programs\messenger\messenger_program.dm"
-#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm"
#include "code\modules\movespeed\_movespeed_modifier.dm"
#include "code\modules\movespeed\modifiers\components.dm"
#include "code\modules\movespeed\modifiers\drugs.dm"
@@ -5707,6 +5719,7 @@
#include "code\modules\spells\spell_types\touch\smite.dm"
#include "code\modules\station_goals\bsa.dm"
#include "code\modules\station_goals\dna_vault.dm"
+#include "code\modules\station_goals\generate_goals.dm"
#include "code\modules\station_goals\meteor_shield.dm"
#include "code\modules\station_goals\station_goal.dm"
#include "code\modules\station_goals\vault_mutation.dm"
@@ -5776,6 +5789,7 @@
#include "code\modules\surgery\organs\_organ.dm"
#include "code\modules\surgery\organs\autosurgeon.dm"
#include "code\modules\surgery\organs\helpers.dm"
+#include "code\modules\surgery\organs\organ_movement.dm"
#include "code\modules\surgery\organs\external\_external_organ.dm"
#include "code\modules\surgery\organs\external\restyling.dm"
#include "code\modules\surgery\organs\external\spines.dm"
@@ -5878,6 +5892,7 @@
#include "code\modules\uplink\uplink_items\badass.dm"
#include "code\modules\uplink\uplink_items\bundle.dm"
#include "code\modules\uplink\uplink_items\clownops.dm"
+#include "code\modules\uplink\uplink_items\contractor.dm"
#include "code\modules\uplink\uplink_items\dangerous.dm"
#include "code\modules\uplink\uplink_items\device_tools.dm"
#include "code\modules\uplink\uplink_items\explosive.dm"
@@ -5976,10 +5991,10 @@
#include "code\modules\visuals\render_steps.dm"
#include "code\modules\wiremod\components\abstract\assoc_list_variable.dm"
#include "code\modules\wiremod\components\abstract\compare.dm"
-#include "code\modules\wiremod\components\abstract\equpiment_action.dm"
#include "code\modules\wiremod\components\abstract\list_variable.dm"
#include "code\modules\wiremod\components\abstract\module.dm"
#include "code\modules\wiremod\components\abstract\variable.dm"
+#include "code\modules\wiremod\components\action\equpiment_action.dm"
#include "code\modules\wiremod\components\action\laserpointer.dm"
#include "code\modules\wiremod\components\action\light.dm"
#include "code\modules\wiremod\components\action\mmi.dm"
@@ -6412,9 +6427,11 @@
#include "modular_skyrat\master_files\code\modules\mob\living\simple_animal\hostile\zombie.dm"
#include "modular_skyrat\master_files\code\modules\mob_spawn\mob_spawn.dm"
#include "modular_skyrat\master_files\code\modules\mod\mod_clothes.dm"
+#include "modular_skyrat\master_files\code\modules\mod\mod_theme.dm"
#include "modular_skyrat\master_files\code\modules\mod\mod_types.dm"
#include "modular_skyrat\master_files\code\modules\mod\modules\_module.dm"
#include "modular_skyrat\master_files\code\modules\mod\modules\modules_antag.dm"
+#include "modular_skyrat\master_files\code\modules\mod\modules\modules_security.dm"
#include "modular_skyrat\master_files\code\modules\mod\modules\modules_supply.dm"
#include "modular_skyrat\master_files\code\modules\modular_computers\computers\item\laptop_presets.dm"
#include "modular_skyrat\master_files\code\modules\modular_computers\file_system\programs\maintenance\camera.dm"
@@ -6813,7 +6830,11 @@
#include "modular_skyrat\modules\colony_fabricator\code\repacking_element.dm"
#include "modular_skyrat\modules\colony_fabricator\code\appliances\chem_machines.dm"
#include "modular_skyrat\modules\colony_fabricator\code\appliances\wall_cell_charger.dm"
+#include "modular_skyrat\modules\colony_fabricator\code\construction\doors.dm"
+#include "modular_skyrat\modules\colony_fabricator\code\construction\turfs.dm"
+#include "modular_skyrat\modules\colony_fabricator\code\construction\windows.dm"
#include "modular_skyrat\modules\colony_fabricator\code\design_datums\appliances.dm"
+#include "modular_skyrat\modules\colony_fabricator\code\design_datums\construction.dm"
#include "modular_skyrat\modules\colony_fabricator\code\design_datums\equipment.dm"
#include "modular_skyrat\modules\colony_fabricator\code\design_datums\flatpack_machines.dm"
#include "modular_skyrat\modules\colony_fabricator\code\design_datums\tools.dm"
@@ -6863,25 +6884,9 @@
#include "modular_skyrat\modules\connecting_computer\code\connectable_component.dm"
#include "modular_skyrat\modules\connecting_computer\code\non_connecting_computers.dm"
#include "modular_skyrat\modules\container_emotes\code\container_emotes.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contract.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_datum.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_hub.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_items.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_support.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_tablet.dm"
-#include "modular_skyrat\modules\contractor\code\datums\mind_datum.dm"
-#include "modular_skyrat\modules\contractor\code\datums\objective.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\antag_datum.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\event.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\objective.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\outfit.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\ruleset.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\trim.dm"
#include "modular_skyrat\modules\contractor\code\items\baton.dm"
-#include "modular_skyrat\modules\contractor\code\items\boxes.dm"
#include "modular_skyrat\modules\contractor\code\items\hook.dm"
#include "modular_skyrat\modules\contractor\code\items\misc.dm"
-#include "modular_skyrat\modules\contractor\code\items\tablet.dm"
#include "modular_skyrat\modules\contractor\code\items\modsuit\modsuit.dm"
#include "modular_skyrat\modules\contractor\code\items\modsuit\modules.dm"
#include "modular_skyrat\modules\contractor\code\items\modsuit\theme.dm"
@@ -7215,6 +7220,7 @@
#include "modular_skyrat\modules\goofsec\code\hud.dm"
#include "modular_skyrat\modules\goofsec\code\sec_clothing_overrides.dm"
#include "modular_skyrat\modules\goofsec\code\sol_fed.dm"
+#include "modular_skyrat\modules\goofsec\code\solfed_clothing.dm"
#include "modular_skyrat\modules\gun_safety\code\keybinding.dm"
#include "modular_skyrat\modules\gun_safety\code\safety_additions.dm"
#include "modular_skyrat\modules\gun_safety\code\safety_component.dm"
@@ -7439,7 +7445,6 @@
#include "modular_skyrat\modules\Midroundtraitor\code\datum_traitor.dm"
#include "modular_skyrat\modules\Midroundtraitor\code\event.dm"
#include "modular_skyrat\modules\mining_vendor_additions\code\order_mining.dm"
-#include "modular_skyrat\modules\modsuit_armour\modsuit_armour.dm"
#include "modular_skyrat\modules\modsuit_pai\code\mod_pai.dm"
#include "modular_skyrat\modules\modular_ert\code\engineer\engineer.dm"
#include "modular_skyrat\modules\modular_ert\code\fumigators\ert.dm"
@@ -7503,6 +7508,7 @@
#include "modular_skyrat\modules\modular_items\code\thieving_gloves.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\clothing_pref_check.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\decals.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\emote.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_quirks.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\screen_icons.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\verbs.dm"
@@ -7762,6 +7768,7 @@
#include "modular_skyrat\modules\opposing_force\code\equipment\modsuit.dm"
#include "modular_skyrat\modules\opposing_force\code\equipment\spells.dm"
#include "modular_skyrat\modules\opposing_force\code\equipment\uplink.dm"
+#include "modular_skyrat\modules\organs\code\heart.dm"
#include "modular_skyrat\modules\organs\code\liver.dm"
#include "modular_skyrat\modules\organs\code\lungs.dm"
#include "modular_skyrat\modules\organs\code\organs.dm"
@@ -7978,7 +7985,9 @@
#include "modular_skyrat\modules\time_clock\code\mind.dm"
#include "modular_skyrat\modules\time_clock\code\off_duty_component.dm"
#include "modular_skyrat\modules\title_screen\code\_title_screen_defines.dm"
+#include "modular_skyrat\modules\title_screen\code\job_traits.dm"
#include "modular_skyrat\modules\title_screen\code\new_player.dm"
+#include "modular_skyrat\modules\title_screen\code\station_traits.dm"
#include "modular_skyrat\modules\title_screen\code\title_screen_controls.dm"
#include "modular_skyrat\modules\title_screen\code\title_screen_html.dm"
#include "modular_skyrat\modules\title_screen\code\title_screen_pref_middleware.dm"
diff --git a/tgui/.eslintignore b/tgui/.eslintignore
index 692ac5f24af..d3c0ac79cd8 100644
--- a/tgui/.eslintignore
+++ b/tgui/.eslintignore
@@ -3,4 +3,14 @@
/**/*.bundle.*
/**/*.chunk.*
/**/*.hot-update.*
-
+**.lock
+**.log
+**.json
+**.svg
+**.scss
+**.md
+**.css
+**.txt
+**.woff2
+**.eot
+**.ttf
diff --git a/tgui/.eslintrc-sonar.yml b/tgui/.eslintrc-sonar.yml
index 3cdd49f889e..ebc57f72c00 100644
--- a/tgui/.eslintrc-sonar.yml
+++ b/tgui/.eslintrc-sonar.yml
@@ -1,26 +1 @@
-rules:
- # radar/cognitive-complexity: error
- radar/max-switch-cases: error
- radar/no-all-duplicated-branches: error
- radar/no-collapsible-if: error
- radar/no-collection-size-mischeck: error
- radar/no-duplicate-string: error
- radar/no-duplicated-branches: error
- radar/no-element-overwrite: error
- radar/no-extra-arguments: error
- radar/no-identical-conditions: error
- radar/no-identical-expressions: error
- radar/no-identical-functions: error
- radar/no-inverted-boolean-check: error
- radar/no-one-iteration-loop: error
- radar/no-redundant-boolean: error
- radar/no-redundant-jump: error
- radar/no-same-line-conditional: error
- radar/no-small-switch: error
- radar/no-unused-collection: error
- radar/no-use-of-empty-return-value: error
- radar/no-useless-catch: error
- radar/prefer-immediate-return: error
- radar/prefer-object-literal: error
- radar/prefer-single-boolean-return: error
- radar/prefer-while: error
+extends: 'plugin:sonarjs/recommended'
diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml
index f7d552d919b..24ba4c47fbd 100644
--- a/tgui/.eslintrc.yml
+++ b/tgui/.eslintrc.yml
@@ -11,9 +11,10 @@ env:
browser: true
node: true
plugins:
- - radar
+ - sonarjs
- react
- unused-imports
+ - simple-import-sort
settings:
react:
version: '16.10'
@@ -337,7 +338,7 @@ rules:
## Require or disallow named function expressions
# func-names: error
## Enforce the consistent use of either function declarations or expressions
- func-style: [error, expression]
+ # func-style: [error, expression]
## Enforce line breaks between arguments of a function call
# function-call-argument-newline: error
## Enforce consistent line breaks inside function parentheses
@@ -764,3 +765,6 @@ rules:
## Prevents the use of unused imports.
## This could be done by enabling no-unused-vars, but we're doing this for now
unused-imports/no-unused-imports: error
+ ## https://github.com/lydell/eslint-plugin-simple-import-sort/
+ simple-import-sort/imports: error
+ simple-import-sort/exports: error
diff --git a/tgui/package.json b/tgui/package.json
index bb95975ef8b..1370aacc48a 100644
--- a/tgui/package.json
+++ b/tgui/package.json
@@ -13,7 +13,7 @@
"tgui:dev": "node --experimental-modules packages/tgui-dev-server/index.js",
"tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx",
"tgui:prettier": "prettier --check .",
- "tgui:sonar": "eslint packages --ext .js,.cjs,.ts,.tsx -c .eslintrc-sonar.yml",
+ "tgui:sonar": "eslint packages -c .eslintrc-sonar.yml",
"tgui:test": "jest --watch",
"tgui:test-simple": "CI=true jest --color",
"tgui:test-ci": "CI=true jest --color --collect-coverage",
@@ -27,14 +27,15 @@
"@types/node": "^14.x",
"@types/webpack": "^5.28.5",
"@types/webpack-env": "^1.18.4",
- "@typescript-eslint/parser": "^5.62.0",
+ "@typescript-eslint/parser": "^6.14.0",
"css-loader": "^6.8.1",
"esbuild-loader": "^4.0.2",
- "eslint": "^7.32.0",
- "eslint-config-prettier": "^9.0.0",
- "eslint-plugin-radar": "^0.2.1",
+ "eslint": "^8.56.0",
+ "eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.33.2",
- "eslint-plugin-unused-imports": "^1.1.5",
+ "eslint-plugin-simple-import-sort": "^10.0.0",
+ "eslint-plugin-sonarjs": "^0.23.0",
+ "eslint-plugin-unused-imports": "^3.0.0",
"file-loader": "^6.2.0",
"jest": "^29.7.0",
"jest-circus": "^29.7.0",
diff --git a/tgui/packages/common/redux.test.ts b/tgui/packages/common/redux.test.ts
index 0a29a92e0ce..d4af99907ce 100644
--- a/tgui/packages/common/redux.test.ts
+++ b/tgui/packages/common/redux.test.ts
@@ -1,10 +1,10 @@
import {
Action,
- Reducer,
applyMiddleware,
combineReducers,
createAction,
createStore,
+ Reducer,
} from './redux';
// Dummy Reducer
diff --git a/tgui/packages/common/redux.ts b/tgui/packages/common/redux.ts
index 997cc1d2d68..c8eb268f5d4 100644
--- a/tgui/packages/common/redux.ts
+++ b/tgui/packages/common/redux.ts
@@ -95,7 +95,7 @@ export const applyMiddleware = (
return (reducer, ...args): Store => {
const store = createStoreFunction(reducer, ...args);
- let dispatch: Dispatch = () => {
+ let dispatch: Dispatch = (action, ...args) => {
throw new Error(
'Dispatching while constructing your middleware is not allowed.',
);
diff --git a/tgui/packages/tgui-bench/entrypoint.tsx b/tgui/packages/tgui-bench/entrypoint.tsx
index 377848fe3ae..48dcd3dcce1 100644
--- a/tgui/packages/tgui-bench/entrypoint.tsx
+++ b/tgui/packages/tgui-bench/entrypoint.tsx
@@ -4,8 +4,10 @@
* @license MIT
*/
-import { setupGlobalEvents } from 'tgui/events';
import 'tgui/styles/main.scss';
+
+import { setupGlobalEvents } from 'tgui/events';
+
import Benchmark from './lib/benchmark';
const sendMessage = (obj: any) => {
diff --git a/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx b/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx
index 757a4291a0b..376d27a9115 100644
--- a/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx
+++ b/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx
@@ -1,8 +1,7 @@
-import { configureStore } from 'tgui/store';
-
-import { DisposalUnit } from 'tgui/interfaces/DisposalUnit';
import { backendUpdate, setGlobalStore } from 'tgui/backend';
+import { DisposalUnit } from 'tgui/interfaces/DisposalUnit';
import { createRenderer } from 'tgui/renderer';
+import { configureStore } from 'tgui/store';
const store = configureStore({ sideEffects: false });
diff --git a/tgui/packages/tgui-dev-server/dreamseeker.js b/tgui/packages/tgui-dev-server/dreamseeker.js
index af4bfe891cb..d1ca2a9ac53 100644
--- a/tgui/packages/tgui-dev-server/dreamseeker.js
+++ b/tgui/packages/tgui-dev-server/dreamseeker.js
@@ -6,6 +6,7 @@
import { exec } from 'child_process';
import { promisify } from 'util';
+
import { createLogger } from './logging.js';
import { require } from './require.js';
diff --git a/tgui/packages/tgui-dev-server/index.js b/tgui/packages/tgui-dev-server/index.js
index 199e93d8363..85489ebb049 100644
--- a/tgui/packages/tgui-dev-server/index.js
+++ b/tgui/packages/tgui-dev-server/index.js
@@ -4,8 +4,8 @@
* @license MIT
*/
-import { createCompiler } from './webpack.js';
import { reloadByondCache } from './reloader.js';
+import { createCompiler } from './webpack.js';
const noHot = process.argv.includes('--no-hot');
const noTmp = process.argv.includes('--no-tmp');
diff --git a/tgui/packages/tgui-dev-server/link/client.cjs b/tgui/packages/tgui-dev-server/link/client.cjs
index 1e21d42ce86..b0e6f7bc9d4 100644
--- a/tgui/packages/tgui-dev-server/link/client.cjs
+++ b/tgui/packages/tgui-dev-server/link/client.cjs
@@ -31,11 +31,9 @@ const ensureConnection = () => {
};
}
}
-};
-if (process.env.NODE_ENV !== 'production') {
window.onunload = () => socket && socket.close();
-}
+};
const subscribe = (fn) => subscribers.push(fn);
@@ -136,38 +134,38 @@ const sendLogEntry = (level, ns, ...args) => {
const setupHotReloading = () => {
if (
- // prettier-ignore
- process.env.NODE_ENV !== 'production'
- && process.env.WEBPACK_HMR_ENABLED
- && window.WebSocket
+ process.env.NODE_ENV === 'production' ||
+ !process.env.WEBPACK_HMR_ENABLED ||
+ !window.WebSocket
) {
- if (module.hot) {
- ensureConnection();
- sendLogEntry(0, null, 'setting up hot reloading');
- subscribe((msg) => {
- const { type } = msg;
- sendLogEntry(0, null, 'received', type);
- if (type === 'hotUpdate') {
- const status = module.hot.status();
- if (status !== 'idle') {
- sendLogEntry(0, null, 'hot reload status:', status);
- return;
- }
- module.hot
- .check({
- ignoreUnaccepted: true,
- ignoreDeclined: true,
- ignoreErrored: true,
- })
- .then((modules) => {
- sendLogEntry(0, null, 'outdated modules', modules);
- })
- .catch((err) => {
- sendLogEntry(0, null, 'reload error', err);
- });
+ return;
+ }
+ if (module.hot) {
+ ensureConnection();
+ sendLogEntry(0, null, 'setting up hot reloading');
+ subscribe((msg) => {
+ const { type } = msg;
+ sendLogEntry(0, null, 'received', type);
+ if (type === 'hotUpdate') {
+ const status = module.hot.status();
+ if (status !== 'idle') {
+ sendLogEntry(0, null, 'hot reload status:', status);
+ return;
}
- });
- }
+ module.hot
+ .check({
+ ignoreUnaccepted: true,
+ ignoreDeclined: true,
+ ignoreErrored: true,
+ })
+ .then((modules) => {
+ sendLogEntry(0, null, 'outdated modules', modules);
+ })
+ .catch((err) => {
+ sendLogEntry(0, null, 'reload error', err);
+ });
+ }
+ });
}
};
diff --git a/tgui/packages/tgui-dev-server/link/retrace.js b/tgui/packages/tgui-dev-server/link/retrace.js
index b7f4d4eacfb..083ddb37d1c 100644
--- a/tgui/packages/tgui-dev-server/link/retrace.js
+++ b/tgui/packages/tgui-dev-server/link/retrace.js
@@ -6,6 +6,7 @@
import fs from 'fs';
import { basename } from 'path';
+
import { createLogger } from '../logging.js';
import { require } from '../require.js';
import { resolveGlob } from '../util.js';
diff --git a/tgui/packages/tgui-dev-server/link/server.js b/tgui/packages/tgui-dev-server/link/server.js
index 60cc78c1bd9..2a1f551bf6e 100644
--- a/tgui/packages/tgui-dev-server/link/server.js
+++ b/tgui/packages/tgui-dev-server/link/server.js
@@ -6,6 +6,7 @@
import http from 'http';
import { inspect } from 'util';
+
import { createLogger, directLog } from '../logging.js';
import { require } from '../require.js';
import { loadSourceMaps, retrace } from './retrace.js';
diff --git a/tgui/packages/tgui-dev-server/reloader.js b/tgui/packages/tgui-dev-server/reloader.js
index 427d1aecbd6..cb477a6523b 100644
--- a/tgui/packages/tgui-dev-server/reloader.js
+++ b/tgui/packages/tgui-dev-server/reloader.js
@@ -7,6 +7,7 @@
import fs from 'fs';
import os from 'os';
import { basename } from 'path';
+
import { DreamSeeker } from './dreamseeker.js';
import { createLogger } from './logging.js';
import { resolveGlob, resolvePath } from './util.js';
diff --git a/tgui/packages/tgui-dev-server/util.js b/tgui/packages/tgui-dev-server/util.js
index 9d07b96c71a..79190fe189a 100644
--- a/tgui/packages/tgui-dev-server/util.js
+++ b/tgui/packages/tgui-dev-server/util.js
@@ -6,6 +6,7 @@
import fs from 'fs';
import path from 'path';
+
import { require } from './require.js';
const globPkg = require('glob');
diff --git a/tgui/packages/tgui-dev-server/webpack.js b/tgui/packages/tgui-dev-server/webpack.js
index 139610b79ce..e4fbdeb9f1e 100644
--- a/tgui/packages/tgui-dev-server/webpack.js
+++ b/tgui/packages/tgui-dev-server/webpack.js
@@ -7,6 +7,7 @@
import fs from 'fs';
import { createRequire } from 'module';
import { dirname } from 'path';
+
import { loadSourceMaps, setupLink } from './link/server.js';
import { createLogger } from './logging.js';
import { reloadByondCache } from './reloader.js';
diff --git a/tgui/packages/tgui-dev-server/winreg.js b/tgui/packages/tgui-dev-server/winreg.js
index b61fddc1a25..43a41701907 100644
--- a/tgui/packages/tgui-dev-server/winreg.js
+++ b/tgui/packages/tgui-dev-server/winreg.js
@@ -8,6 +8,7 @@
import { exec } from 'child_process';
import { promisify } from 'util';
+
import { createLogger } from './logging.js';
const logger = createLogger('winreg');
@@ -35,8 +36,8 @@ export const regQuery = async (path, key) => {
logger.error('could not find the start of the key value');
return null;
}
- const value = stdout.substring(indexOfValue + 4, indexOfEol);
- return value;
+
+ return stdout.substring(indexOfValue + 4, indexOfEol);
} catch (err) {
logger.error(err);
return null;
diff --git a/tgui/packages/tgui-panel/Panel.tsx b/tgui/packages/tgui-panel/Panel.tsx
index 04182f5eb6e..2813b636574 100644
--- a/tgui/packages/tgui-panel/Panel.tsx
+++ b/tgui/packages/tgui-panel/Panel.tsx
@@ -6,6 +6,7 @@
import { Button, Section, Stack } from 'tgui/components';
import { Pane } from 'tgui/layouts';
+
import { NowPlayingWidget, useAudio } from './audio';
import { ChatPanel, ChatTabs } from './chat';
import { useGame } from './game';
diff --git a/tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx b/tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx
index 903dacf284c..f101d7da038 100644
--- a/tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx
+++ b/tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx
@@ -7,6 +7,7 @@
import { toFixed } from 'common/math';
import { useDispatch, useSelector } from 'tgui/backend';
import { Button, Collapsible, Flex, Knob, Section } from 'tgui/components';
+
import { useSettings } from '../settings';
import { selectAudio } from './selectors';
diff --git a/tgui/packages/tgui-panel/audio/hooks.ts b/tgui/packages/tgui-panel/audio/hooks.ts
index 1286754a37d..2e9d830cb77 100644
--- a/tgui/packages/tgui-panel/audio/hooks.ts
+++ b/tgui/packages/tgui-panel/audio/hooks.ts
@@ -4,7 +4,8 @@
* @license MIT
*/
-import { useSelector, useDispatch } from 'tgui/backend';
+import { useDispatch, useSelector } from 'tgui/backend';
+
import { selectAudio } from './selectors';
export const useAudio = () => {
diff --git a/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx b/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx
index d380979dbd3..67028168f8e 100644
--- a/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx
+++ b/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx
@@ -13,6 +13,7 @@ import {
Section,
Stack,
} from 'tgui/components';
+
import { removeChatPage, toggleAcceptedType, updateChatPage } from './actions';
import { MESSAGE_TYPES } from './constants';
import { selectCurrentChatPage } from './selectors';
diff --git a/tgui/packages/tgui-panel/chat/ChatPanel.jsx b/tgui/packages/tgui-panel/chat/ChatPanel.jsx
index 36e86876f50..845c1612756 100644
--- a/tgui/packages/tgui-panel/chat/ChatPanel.jsx
+++ b/tgui/packages/tgui-panel/chat/ChatPanel.jsx
@@ -7,6 +7,7 @@
import { shallowDiffers } from 'common/react';
import { Component, createRef } from 'react';
import { Button } from 'tgui/components';
+
import { chatRenderer } from './renderer';
export class ChatPanel extends Component {
diff --git a/tgui/packages/tgui-panel/chat/ChatTabs.jsx b/tgui/packages/tgui-panel/chat/ChatTabs.jsx
index ed3a285c204..2031f6a255b 100644
--- a/tgui/packages/tgui-panel/chat/ChatTabs.jsx
+++ b/tgui/packages/tgui-panel/chat/ChatTabs.jsx
@@ -5,10 +5,11 @@
*/
import { useDispatch, useSelector } from 'tgui/backend';
-import { Box, Tabs, Flex, Button } from 'tgui/components';
-import { changeChatPage, addChatPage } from './actions';
-import { selectChatPages, selectCurrentChatPage } from './selectors';
+import { Box, Button, Flex, Tabs } from 'tgui/components';
+
import { openChatSettings } from '../settings/actions';
+import { addChatPage, changeChatPage } from './actions';
+import { selectChatPages, selectCurrentChatPage } from './selectors';
const UnreadCountWidget = ({ value }) => (
type.startsWith(MESSAGE_TYPE_INTERNAL) || page.acceptedTypes[type];
diff --git a/tgui/packages/tgui-panel/chat/reducer.js b/tgui/packages/tgui-panel/chat/reducer.js
index e75fbd06e43..7a7681ada74 100644
--- a/tgui/packages/tgui-panel/chat/reducer.js
+++ b/tgui/packages/tgui-panel/chat/reducer.js
@@ -7,12 +7,12 @@
import {
addChatPage,
changeChatPage,
+ changeScrollTracking,
loadChat,
removeChatPage,
toggleAcceptedType,
updateChatPage,
updateMessageCount,
- changeScrollTracking,
} from './actions';
import { canPageAcceptType, createMainPage } from './model';
diff --git a/tgui/packages/tgui-panel/chat/renderer.jsx b/tgui/packages/tgui-panel/chat/renderer.jsx
index e52007c5f72..899839e56cc 100644
--- a/tgui/packages/tgui-panel/chat/renderer.jsx
+++ b/tgui/packages/tgui-panel/chat/renderer.jsx
@@ -6,7 +6,10 @@
import { EventEmitter } from 'common/events';
import { classes } from 'common/react';
+import { render } from 'react-dom';
+import { Tooltip } from 'tgui/components';
import { createLogger } from 'tgui/logging';
+
import {
COMBINE_MAX_MESSAGES,
COMBINE_MAX_TIME_WINDOW,
@@ -16,14 +19,12 @@ import {
MAX_PERSISTED_MESSAGES,
MAX_VISIBLE_MESSAGES,
MESSAGE_PRUNE_INTERVAL,
- MESSAGE_TYPES,
MESSAGE_TYPE_INTERNAL,
MESSAGE_TYPE_UNKNOWN,
+ MESSAGE_TYPES,
} from './constants';
-import { render } from 'react-dom';
import { canPageAcceptType, createMessage, isSameMessage } from './model';
import { highlightNode, linkifyNode } from './replaceInTextNode';
-import { Tooltip } from 'tgui/components';
const logger = createLogger('chatRenderer');
diff --git a/tgui/packages/tgui-panel/game/constants.ts b/tgui/packages/tgui-panel/game/constants.ts
index f40a529a101..d692e26c213 100644
--- a/tgui/packages/tgui-panel/game/constants.ts
+++ b/tgui/packages/tgui-panel/game/constants.ts
@@ -4,4 +4,4 @@
* @license MIT
*/
-export const CONNECTION_LOST_AFTER = 20000;
+export const CONNECTION_LOST_AFTER = 45000;
diff --git a/tgui/packages/tgui-panel/game/hooks.ts b/tgui/packages/tgui-panel/game/hooks.ts
index c3e7c062847..40a74ff44a0 100644
--- a/tgui/packages/tgui-panel/game/hooks.ts
+++ b/tgui/packages/tgui-panel/game/hooks.ts
@@ -5,6 +5,7 @@
*/
import { useSelector } from 'tgui/backend';
+
import { selectGame } from './selectors';
export const useGame = () => {
diff --git a/tgui/packages/tgui-panel/game/middleware.js b/tgui/packages/tgui-panel/game/middleware.js
index 53dd45bb46e..1cf80a7ac63 100644
--- a/tgui/packages/tgui-panel/game/middleware.js
+++ b/tgui/packages/tgui-panel/game/middleware.js
@@ -6,8 +6,8 @@
import { pingSoft, pingSuccess } from '../ping/actions';
import { connectionLost, connectionRestored, roundRestarted } from './actions';
-import { selectGame } from './selectors';
import { CONNECTION_LOST_AFTER } from './constants';
+import { selectGame } from './selectors';
const withTimestamp = (action) => ({
...action,
diff --git a/tgui/packages/tgui-panel/index.tsx b/tgui/packages/tgui-panel/index.tsx
index 32bb8fe6cce..a70a53bac25 100644
--- a/tgui/packages/tgui-panel/index.tsx
+++ b/tgui/packages/tgui-panel/index.tsx
@@ -10,11 +10,13 @@ import './styles/themes/light.scss';
import { perf } from 'common/perf';
import { combineReducers } from 'common/redux';
-import { setupHotReloading } from 'tgui-dev-server/link/client.cjs';
+import { setGlobalStore } from 'tgui/backend';
import { setupGlobalEvents } from 'tgui/events';
import { captureExternalLinks } from 'tgui/links';
import { createRenderer } from 'tgui/renderer';
import { configureStore } from 'tgui/store';
+import { setupHotReloading } from 'tgui-dev-server/link/client.cjs';
+
import { audioMiddleware, audioReducer } from './audio';
import { chatMiddleware, chatReducer } from './chat';
import { gameMiddleware, gameReducer } from './game';
@@ -22,7 +24,6 @@ import { setupPanelFocusHacks } from './panelFocus';
import { pingMiddleware, pingReducer } from './ping';
import { settingsMiddleware, settingsReducer } from './settings';
import { telemetryMiddleware } from './telemetry';
-import { setGlobalStore } from 'tgui/backend';
perf.mark('inception', window.performance?.timing?.navigationStart);
perf.mark('init');
diff --git a/tgui/packages/tgui-panel/ping/PingIndicator.jsx b/tgui/packages/tgui-panel/ping/PingIndicator.jsx
index b2355820e58..549cd09cf74 100644
--- a/tgui/packages/tgui-panel/ping/PingIndicator.jsx
+++ b/tgui/packages/tgui-panel/ping/PingIndicator.jsx
@@ -8,6 +8,7 @@ import { Color } from 'common/color';
import { toFixed } from 'common/math';
import { useSelector } from 'tgui/backend';
import { Box } from 'tgui/components';
+
import { selectPing } from './selectors';
export const PingIndicator = (props) => {
diff --git a/tgui/packages/tgui-panel/ping/reducer.ts b/tgui/packages/tgui-panel/ping/reducer.ts
index 55d40eeb46f..10531d23d84 100644
--- a/tgui/packages/tgui-panel/ping/reducer.ts
+++ b/tgui/packages/tgui-panel/ping/reducer.ts
@@ -5,6 +5,7 @@
*/
import { clamp01, scale } from 'common/math';
+
import { pingFail, pingSuccess } from './actions';
import {
PING_MAX_FAILS,
diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
index d803c63cb16..a7c90af8cc9 100644
--- a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
+++ b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
@@ -22,22 +22,23 @@ import {
Tabs,
TextArea,
} from 'tgui/components';
+
import { ChatPageSettings } from '../chat';
import { rebuildChat, saveChatToDisk } from '../chat/actions';
import { THEMES } from '../themes';
import {
- changeSettingsTab,
- updateSettings,
addHighlightSetting,
+ changeSettingsTab,
removeHighlightSetting,
updateHighlightSetting,
+ updateSettings,
} from './actions';
-import { SETTINGS_TABS, FONTS, MAX_HIGHLIGHT_SETTINGS } from './constants';
+import { FONTS, MAX_HIGHLIGHT_SETTINGS, SETTINGS_TABS } from './constants';
import {
selectActiveTab,
- selectSettings,
- selectHighlightSettings,
selectHighlightSettingById,
+ selectHighlightSettings,
+ selectSettings,
} from './selectors';
export const SettingsPanel = (props) => {
diff --git a/tgui/packages/tgui-panel/settings/actions.ts b/tgui/packages/tgui-panel/settings/actions.ts
index 52febd21c9c..55e5db11261 100644
--- a/tgui/packages/tgui-panel/settings/actions.ts
+++ b/tgui/packages/tgui-panel/settings/actions.ts
@@ -5,6 +5,7 @@
*/
import { createAction } from 'common/redux';
+
import { createHighlightSetting } from './model';
export const updateSettings = createAction('settings/update');
diff --git a/tgui/packages/tgui-panel/settings/hooks.ts b/tgui/packages/tgui-panel/settings/hooks.ts
index 5ea91c86067..b37b49a22c8 100644
--- a/tgui/packages/tgui-panel/settings/hooks.ts
+++ b/tgui/packages/tgui-panel/settings/hooks.ts
@@ -5,7 +5,8 @@
*/
import { useDispatch, useSelector } from 'tgui/backend';
-import { updateSettings, toggleSettings } from './actions';
+
+import { toggleSettings, updateSettings } from './actions';
import { selectSettings } from './selectors';
export const useSettings = () => {
diff --git a/tgui/packages/tgui-panel/settings/middleware.js b/tgui/packages/tgui-panel/settings/middleware.js
index 3d3c7744df4..edb16a51c4c 100644
--- a/tgui/packages/tgui-panel/settings/middleware.js
+++ b/tgui/packages/tgui-panel/settings/middleware.js
@@ -5,16 +5,17 @@
*/
import { storage } from 'common/storage';
+
import { setClientTheme } from '../themes';
import {
- loadSettings,
- updateSettings,
addHighlightSetting,
+ loadSettings,
removeHighlightSetting,
updateHighlightSetting,
+ updateSettings,
} from './actions';
-import { selectSettings } from './selectors';
import { FONTS_DISABLED } from './constants';
+import { selectSettings } from './selectors';
let overrideRule = null;
let overrideFontFamily = null;
diff --git a/tgui/packages/tgui-panel/settings/reducer.js b/tgui/packages/tgui-panel/settings/reducer.js
index 17f2af380e8..1240198df8a 100644
--- a/tgui/packages/tgui-panel/settings/reducer.js
+++ b/tgui/packages/tgui-panel/settings/reducer.js
@@ -5,17 +5,17 @@
*/
import {
+ addHighlightSetting,
changeSettingsTab,
loadSettings,
openChatSettings,
- toggleSettings,
- updateSettings,
- addHighlightSetting,
removeHighlightSetting,
+ toggleSettings,
updateHighlightSetting,
+ updateSettings,
} from './actions';
+import { FONTS, MAX_HIGHLIGHT_SETTINGS, SETTINGS_TABS } from './constants';
import { createDefaultHighlightSetting } from './model';
-import { SETTINGS_TABS, FONTS, MAX_HIGHLIGHT_SETTINGS } from './constants';
const defaultHighlightSetting = createDefaultHighlightSetting();
diff --git a/tgui/packages/tgui-say/TguiSay.tsx b/tgui/packages/tgui-say/TguiSay.tsx
index 9e93b395151..39043a978b8 100644
--- a/tgui/packages/tgui-say/TguiSay.tsx
+++ b/tgui/packages/tgui-say/TguiSay.tsx
@@ -1,12 +1,13 @@
+import { KEY } from 'common/keys';
+import { BooleanLike } from 'common/react';
+import { Component, createRef, RefObject } from 'react';
+import { dragStartHandler } from 'tgui/drag';
+
import { Channel, ChannelIterator } from './ChannelIterator';
import { ChatHistory } from './ChatHistory';
-import { Component, createRef, RefObject } from 'react';
import { LINE_LENGTHS, RADIO_PREFIXES, WINDOW_SIZES } from './constants';
+import { windowClose, windowOpen, windowSet } from './helpers';
import { byondMessages } from './timers';
-import { dragStartHandler } from 'tgui/drag';
-import { windowOpen, windowClose, windowSet } from './helpers';
-import { BooleanLike } from 'common/react';
-import { KEY } from 'common/keys';
type ByondOpen = {
channel: Channel;
diff --git a/tgui/packages/tgui-say/index.tsx b/tgui/packages/tgui-say/index.tsx
index ed512b525ff..d902ae85cdf 100644
--- a/tgui/packages/tgui-say/index.tsx
+++ b/tgui/packages/tgui-say/index.tsx
@@ -1,5 +1,7 @@
import './styles/main.scss';
+
import { createRenderer } from 'tgui/renderer';
+
import { TguiSay } from './TguiSay';
const renderApp = createRenderer(() => {
diff --git a/tgui/packages/tgui/assets.ts b/tgui/packages/tgui/assets.ts
index e519d0c2f76..f62bf652dea 100644
--- a/tgui/packages/tgui/assets.ts
+++ b/tgui/packages/tgui/assets.ts
@@ -4,10 +4,10 @@
* @license MIT
*/
-import { Action, AnyAction, Middleware } from '../common/redux';
-
import { Dispatch } from 'common/redux';
+import { Action, AnyAction, Middleware } from '../common/redux';
+
const EXCLUDED_PATTERNS = [/v4shim/i];
const loadedMappings: Record = {};
diff --git a/tgui/packages/tgui/backend.ts b/tgui/packages/tgui/backend.ts
index 9f865994574..bf75306108a 100644
--- a/tgui/packages/tgui/backend.ts
+++ b/tgui/packages/tgui/backend.ts
@@ -13,6 +13,7 @@
import { perf } from 'common/perf';
import { createAction } from 'common/redux';
+
import { setupDrag } from './drag';
import { globalEvents } from './events';
import { focusMap } from './focus';
diff --git a/tgui/packages/tgui/components/BlockQuote.jsx b/tgui/packages/tgui/components/BlockQuote.jsx
index 8c0f53a46a1..22508d4b885 100644
--- a/tgui/packages/tgui/components/BlockQuote.jsx
+++ b/tgui/packages/tgui/components/BlockQuote.jsx
@@ -5,6 +5,7 @@
*/
import { classes } from 'common/react';
+
import { Box } from './Box';
export const BlockQuote = (props) => {
diff --git a/tgui/packages/tgui/components/BodyZoneSelector.tsx b/tgui/packages/tgui/components/BodyZoneSelector.tsx
index f6918489647..306b7002eb7 100644
--- a/tgui/packages/tgui/components/BodyZoneSelector.tsx
+++ b/tgui/packages/tgui/components/BodyZoneSelector.tsx
@@ -1,4 +1,5 @@
import { Component, createRef } from 'react';
+
import { resolveAsset } from '../assets';
import { Image } from './Image';
@@ -134,7 +135,7 @@ export class BodyZoneSelector extends Component<
>;
+type StringProps = Partial<
+ Record
+>;
+
+export type EventHandlers = Partial<{
+ onClick: MouseEventHandler;
+ onContextMenu: MouseEventHandler;
+ onDoubleClick: MouseEventHandler;
+ onKeyDown: KeyboardEventHandler;
+ onKeyUp: KeyboardEventHandler;
+ onMouseDown: MouseEventHandler;
+ onMouseMove: MouseEventHandler;
+ onMouseOver: MouseEventHandler;
+ onMouseUp: MouseEventHandler;
+ onScroll: UIEventHandler;
+}>;
+
+export type BoxProps = Partial<{
+ as: string;
+ children: ReactNode;
+ className: string | BooleanLike;
+ style: Partial;
+}> &
+ BooleanProps &
+ StringProps &
+ EventHandlers;
+
+// Don't you dare put this elsewhere
+type DangerDoNotUse = {
+ dangerouslySetInnerHTML?: {
+ __html: any;
+ };
};
/**
* Coverts our rem-like spacing unit into a CSS unit.
*/
-export const unit = (value: unknown): string | undefined => {
+export const unit = (value: unknown) => {
if (typeof value === 'string') {
// Transparently convert pixels into rem units
if (value.endsWith('px')) {
@@ -78,7 +70,7 @@ export const unit = (value: unknown): string | undefined => {
/**
* Same as `unit`, but half the size for integers numbers.
*/
-export const halfUnit = (value: unknown): string | undefined => {
+export const halfUnit = (value: unknown) => {
if (typeof value === 'string') {
return unit(value);
}
@@ -127,9 +119,12 @@ const mapColorPropTo = (attrName) => (style, value) => {
// String / number props
const stringStyleMap = {
+ align: mapRawPropTo('textAlign'),
bottom: mapUnitPropTo('bottom', unit),
+ colSpan: mapRawPropTo('colSpan'),
fontFamily: mapRawPropTo('fontFamily'),
fontSize: mapUnitPropTo('fontSize', unit),
+ fontWeight: mapRawPropTo('fontWeight'),
height: mapUnitPropTo('height', unit),
left: mapUnitPropTo('left', unit),
maxHeight: mapUnitPropTo('maxHeight', unit),
@@ -161,12 +156,12 @@ const stringStyleMap = {
'Left',
'Right',
]),
- mx: mapDirectionalUnitPropTo('margin', halfUnit, ['Left', 'Right']),
- my: mapDirectionalUnitPropTo('margin', halfUnit, ['Top', 'Bottom']),
- mt: mapUnitPropTo('marginTop', halfUnit),
mb: mapUnitPropTo('marginBottom', halfUnit),
ml: mapUnitPropTo('marginLeft', halfUnit),
mr: mapUnitPropTo('marginRight', halfUnit),
+ mt: mapUnitPropTo('marginTop', halfUnit),
+ mx: mapDirectionalUnitPropTo('margin', halfUnit, ['Left', 'Right']),
+ my: mapDirectionalUnitPropTo('margin', halfUnit, ['Top', 'Bottom']),
// Padding
p: mapDirectionalUnitPropTo('padding', halfUnit, [
'Top',
@@ -174,18 +169,21 @@ const stringStyleMap = {
'Left',
'Right',
]),
- px: mapDirectionalUnitPropTo('padding', halfUnit, ['Left', 'Right']),
- py: mapDirectionalUnitPropTo('padding', halfUnit, ['Top', 'Bottom']),
- pt: mapUnitPropTo('paddingTop', halfUnit),
pb: mapUnitPropTo('paddingBottom', halfUnit),
pl: mapUnitPropTo('paddingLeft', halfUnit),
pr: mapUnitPropTo('paddingRight', halfUnit),
+ pt: mapUnitPropTo('paddingTop', halfUnit),
+ px: mapDirectionalUnitPropTo('padding', halfUnit, ['Left', 'Right']),
+ py: mapDirectionalUnitPropTo('padding', halfUnit, ['Top', 'Bottom']),
// Color props
color: mapColorPropTo('color'),
textColor: mapColorPropTo('color'),
backgroundColor: mapColorPropTo('backgroundColor'),
+} as const;
- // Utility props
+// Boolean props
+const booleanStyleMap = {
+ bold: mapBooleanPropTo('fontWeight', 'bold'),
fillPositionedParent: (style, value) => {
if (value) {
style['position'] = 'absolute';
@@ -195,11 +193,6 @@ const stringStyleMap = {
style['right'] = 0;
}
},
-} as const;
-
-// Boolean props
-const booleanStyleMap = {
- bold: mapBooleanPropTo('fontWeight', 'bold'),
inline: mapBooleanPropTo('display', 'inline-block'),
italic: mapBooleanPropTo('fontStyle', 'italic'),
nowrap: mapBooleanPropTo('whiteSpace', 'nowrap'),
@@ -243,7 +236,7 @@ export const computeBoxClassName = (props: BoxProps) => {
]);
};
-export const Box = (props: BoxProps) => {
+export const Box = (props: BoxProps & DangerDoNotUse) => {
const { as = 'div', className, children, ...rest } = props;
// Compute class name and styles
diff --git a/tgui/packages/tgui/components/Button.tsx b/tgui/packages/tgui/components/Button.tsx
index 7d71ffc57b6..dc57f5f7e71 100644
--- a/tgui/packages/tgui/components/Button.tsx
+++ b/tgui/packages/tgui/components/Button.tsx
@@ -7,7 +7,8 @@
import { Placement } from '@popperjs/core';
import { KEY } from 'common/keys';
import { BooleanLike, classes } from 'common/react';
-import { createRef, ReactNode, useEffect, useState } from 'react';
+import { createRef, MouseEvent, ReactNode, useEffect, useState } from 'react';
+
import { Box, BoxProps, computeBoxClassName, computeBoxProps } from './Box';
import { Icon } from './Icon';
import { Tooltip } from './Tooltip';
@@ -22,13 +23,13 @@ type EllipsisUnion =
| {
ellipsis: true;
children: string;
- /** @deprecated */
+ /** @deprecated use children instead */
content?: never;
}
| Partial<{
ellipsis: undefined;
children: ReactNode;
- /** @deprecated */
+ /** @deprecated use children instead */
content: ReactNode;
}>;
@@ -46,7 +47,7 @@ type Props = Partial<{
onClick: (e: any) => void;
selected: BooleanLike;
tooltip: ReactNode;
- tooltipPosition: string;
+ tooltipPosition: Placement;
verticalAlignContent: string;
}> &
EllipsisUnion &
@@ -124,7 +125,6 @@ export const Button = (props: Props) => {
// Refocus layout on pressing escape.
if (event.key === KEY.Escape) {
event.preventDefault();
- return;
}
}}
{...computeBoxProps(rest)}
@@ -198,34 +198,31 @@ type ConfirmProps = Partial<{
confirmColor: string;
confirmContent: ReactNode;
confirmIcon: string;
- content: ReactNode;
- icon: string;
- onClick: () => void;
}> &
- Omit;
+ Props;
/** Requires user confirmation before triggering its action. */
const ButtonConfirm = (props: ConfirmProps) => {
const {
+ children,
color,
confirmColor = 'bad',
confirmContent = 'Confirm?',
confirmIcon,
- content,
- children,
+ ellipsis = true,
icon,
onClick,
...rest
} = props;
const [clickedOnce, setClickedOnce] = useState(false);
- const handleClick = () => {
+ const handleClick = (event: MouseEvent) => {
if (!clickedOnce) {
setClickedOnce(true);
return;
}
- onClick?.();
+ onClick?.(event);
setClickedOnce(false);
};
@@ -236,7 +233,7 @@ const ButtonConfirm = (props: ConfirmProps) => {
onClick={handleClick}
{...rest}
>
- {clickedOnce ? confirmContent : content}
+ {clickedOnce ? confirmContent : children}
);
};
@@ -256,10 +253,12 @@ type InputProps = Partial<{
/** Accepts and handles user input. */
const ButtonInput = (props: InputProps) => {
const {
+ children,
color = 'default',
content,
currentValue,
defaultValue,
+ disabled,
fluid,
icon,
iconRotation,
@@ -274,6 +273,8 @@ const ButtonInput = (props: InputProps) => {
const [inInput, setInInput] = useState(false);
const inputRef = createRef();
+ const toDisplay = content || children;
+
const commitResult = (e) => {
const input = inputRef.current;
if (!input) return;
@@ -312,8 +313,9 @@ const ButtonInput = (props: InputProps) => {
onClick={() => setInInput(true)}
>
{icon && }
-