diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 269b9e43..b68feb73 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -28566,7 +28566,6 @@
/obj/structure/disposalpipe/segment{
dir = 6
},
-/obj/machinery/bloodbankgen,
/turf/open/floor/plasteel/white,
/area/medical/chemistry)
"blu" = (
@@ -39058,20 +39057,10 @@
/turf/closed/wall,
/area/medical/sleeper)
"bHB" = (
-/obj/structure/table,
-/obj/item/storage/belt/medical{
- pixel_y = 2
- },
-/obj/item/storage/belt/medical{
- pixel_y = 2
- },
-/obj/item/storage/belt/medical{
- pixel_y = 2
- },
-/obj/item/clothing/neck/stethoscope,
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
+/obj/machinery/bloodbankgen,
/turf/open/floor/plasteel/white,
/area/medical/sleeper)
"bHC" = (
@@ -40205,6 +40194,16 @@
/obj/item/clothing/glasses/hud/health,
/obj/item/clothing/glasses/hud/health,
/obj/item/clothing/glasses/hud/health,
+/obj/item/clothing/neck/stethoscope,
+/obj/item/storage/belt/medical{
+ pixel_y = 2
+ },
+/obj/item/storage/belt/medical{
+ pixel_y = 2
+ },
+/obj/item/storage/belt/medical{
+ pixel_y = 2
+ },
/turf/open/floor/plasteel/white,
/area/medical/sleeper)
"bKm" = (
@@ -62815,9 +62814,9 @@
/turf/open/floor/plating,
/area/maintenance/port/fore)
"xNJ" = (
-/obj/machinery/bloodbankgen,
-/turf/open/floor/plasteel/white,
-/area/medical/sleeper)
+/obj/effect/landmark/barthpot,
+/turf/open/floor/carpet,
+/area/library)
"xSM" = (
/mob/living/simple_animal/hostile/carp/cayenne/lia,
/turf/open/floor/carpet/red,
@@ -99734,7 +99733,7 @@ bCs
bxa
bER
bFV
-xNJ
+byu
byu
bKn
bLF
@@ -106129,7 +106128,7 @@ aMp
btJ
aOM
aOM
-aOM
+xNJ
aOM
aOM
aOM
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index d402846b..58d03c76 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -126929,6 +126929,10 @@
},
/turf/open/floor/plasteel/white,
/area/science/mixing)
+"kZu" = (
+/obj/effect/landmark/barthpot,
+/turf/open/floor/wood,
+/area/library)
"lak" = (
/turf/open/floor/plasteel/white/side{
dir = 10
@@ -160252,7 +160256,7 @@ caG
chU
cjt
ckR
-cmt
+kZu
cnR
cpv
cqQ
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index c0a456c8..f05e204a 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -2419,7 +2419,7 @@
"aUC" = (/obj/structure/cable{icon_state = "1-2"},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/supply/hidden,/turf/open/floor/plating,/area/maintenance/department/cargo)
"aUD" = (/obj/structure/lattice/catwalk,/obj/structure/cable{icon_state = "2-8"},/obj/structure/cable{icon_state = "2-4"},/obj/structure/cable{icon_state = "1-2"},/turf/open/space,/area/solar/starboard)
"aUE" = (/turf/open/floor/wood,/area/maintenance/department/chapel/monastery)
-"aUF" = (/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/structure/cable{icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/open/floor/carpet,/area/library/lounge)
+"aUF" = (/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/structure/cable{icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/obj/effect/landmark/barthpot,/turf/open/floor/carpet,/area/library/lounge)
"aUG" = (/obj/machinery/atmospherics/pipe/manifold/supply/hidden,/obj/structure/cable{icon_state = "4-8"},/obj/effect/turf_decal/tile/red{dir = 4},/turf/open/floor/plasteel/white/corner{dir = 1},/area/hallway/secondary/exit/departure_lounge)
"aUH" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/obj/structure/sign/directions/evac{dir = 1; pixel_y = 32},/obj/structure/cable{icon_state = "4-8"},/obj/effect/turf_decal/tile/red{dir = 4},/turf/open/floor/plasteel/white/corner{dir = 1},/area/hallway/secondary/exit/departure_lounge)
"aUI" = (/obj/structure/table/wood,/obj/machinery/computer/libraryconsole,/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/open/floor/plasteel/dark,/area/library/lounge)
@@ -4028,7 +4028,7 @@
"bAW" = (/turf/closed/wall,/area/medical/virology)
"bAX" = (/obj/machinery/door/firedoor,/obj/effect/mapping_helpers/airlock/locked,/obj/machinery/door/airlock/virology{autoclose = 0; frequency = 1449; id_tag = "virology_airlock_exterior"; name = "Virology Exterior Airlock"; req_access_txt = "39"},/obj/structure/cable{icon_state = "1-2"},/obj/machinery/doorButtons/access_button{idDoor = "virology_airlock_exterior"; idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_x = -24; req_access_txt = "39"},/obj/effect/mapping_helpers/airlock/cyclelink_helper,/turf/open/floor/plasteel/white,/area/medical/virology)
"bAY" = (/obj/structure/sign/warning/biohazard,/turf/closed/wall,/area/medical/virology)
-"bBb" = (/obj/effect/turf_decal/stripes/line,/turf/open/floor/plasteel/white,/area/medical/sleeper)
+"bBb" = (/obj/effect/turf_decal/stripes/line,/obj/machinery/bloodbankgen,/turf/open/floor/plasteel/white,/area/medical/sleeper)
"bBc" = (/obj/structure/disposalpipe/segment{dir = 6},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/open/floor/plasteel/white,/area/medical/sleeper)
"bBd" = (/obj/machinery/door/firedoor,/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 9},/turf/open/floor/plasteel/white,/area/medical/sleeper)
"bBe" = (/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{dir = 8},/obj/structure/disposalpipe/segment{dir = 4},/obj/effect/turf_decal/tile/blue{dir = 1},/obj/effect/turf_decal/tile/blue{dir = 8},/turf/open/floor/plasteel/white,/area/medical/medbay/central)
@@ -6449,7 +6449,6 @@
"mES" = (/obj/machinery/door/airlock/maintenance/abandoned{name = "Surgical Room"},/turf/open/floor/plating,/area/maintenance/department/science)
"mHy" = (/obj/item/storage/fancy/cigarettes/cigpack_shadyjims,/turf/open/floor/wood,/area/maintenance/department/engine)
"mIa" = (/obj/structure/disposalpipe/segment{dir = 10},/obj/machinery/light/small{dir = 8},/turf/open/floor/plating,/area/maintenance/department/engine)
-"mKw" = (/obj/machinery/bloodbankgen,/turf/open/floor/plasteel/white,/area/medical/medbay/central)
"mLc" = (/obj/structure/sign/warning/electricshock,/turf/closed/wall/r_wall,/area/engine/supermatter)
"mLB" = (/obj/structure/cable{icon_state = "1-4"},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 5},/turf/open/floor/plating{icon_state = "platingdmg1"},/area/maintenance/department/security/brig)
"mMz" = (/obj/effect/spawner/structure/window/reinforced,/obj/structure/cable,/obj/machinery/door/poddoor/preopen{id = "misclab"; name = "test chamber blast door"},/turf/open/floor/plating,/area/science/xenobiology)
@@ -7144,7 +7143,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabVpaZxaLtaLJaZxahtbBVabIbBXbBXbBXbBXbBXbBYbvbbnvbnvbnvbnvbBZbCabCbbCcbCdbCebvcbCfbCgbChbsAbCiiojbBbbClbCmbyobCnbCobCpabhbCrbCsbCtbCubCvabibCxbCybCzbCAbCBafYrGzdggfdQnkkbBpbCEbCFbCGbCHbCIbAoaLPaMdaMebAtcqkcqvbkobCNbCOahtbCPbBCbBDbCQbCRbCRbCSbCTbCUbBJbCVbCWbCWbCXbBNbCZbCWbDabDbbDcbDdbHybwmbwmbwmhgDbwmlWyhxnrFqlWybkFbkFbkFbkFbkFbkFbkFbkFbkFbkFbkFbkFbkFaadjRGeuQvtljEXhSMahiaaaaaaaaaahtaaaaaaaaaaaaahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaagLnaZxaLtaMfaZxahtbDfabIbBXbDgbDgbDhbvabTlbDjibUbDibDkbnvbvcbvcbvcbDlbDmbDnbvcbDobDpbDqbsAbDrbsAbDrbsAbDrbsAbDsbkhbpPwLobDtbDubDvbDwbDxbDybDybDybDybDybDzbvarPdphJbmDbDAbBpbDBbDCbDDbDEbDFbAoaMIaMJaMKbAtbsUbBzbkobDKbBoahtbAAbDLbDMbDNbDObDPabkbDRbDSbDTablbDVbDWbDXbDYbDXbDXbEabEbbEcbEdbHyglfpNyveMbEfbAFtSLuvqobPlWybwmkgRdMOhTlbwmqIOdmPlWysZuwfcbwmueVdoodtmgmpvuQbwmruibwmahiaaaaaaaaaaedahtahtabyabyaedahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahtsTgaZxaKXaMMaZxahtbDfahtbBXbEjbEkbElacbbEnbEobmfbEpbmfbEqbDibErbvcbvcbvcbvcbvcbEsbEtbEubjcbEvbEwbExbEybEzbkbbEAbEBbECbzYbEDbEEbEFbEGbEHbDybEIbEJbEKatebEMbvaxDjphJbmDbEQbBpbERbESbETbEUbEVfRsaMNaMOaMPbAubsUbBzbkobEZbCOaaabCPbFabFbbFcbCRbFdbCSbCTbwbbFfbCVbFhkmnbFibFjbFkbFlbFmbFnbGubFpbHybFrbFsbFtbFubAFlWybFxbFybFzbwmbFBlWybFCbwmjXAbFDlWylWysAKbwmheCefUbwmbwmbwmbwmbwmbwmahtahtabyaaaaaaaaaaaaahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabFEaaaaaaaaaaaaaaaaaaahtahtgLnaZxaLtaMMaZxahtbDfabIbBXbFFbvabvabvabvabvabsnbsnbvabFGbEnbFHbFIbFJbFKbFLbAWbErbFMbFNbFObFPmKwbkhbFQbnFacpbFSbkhbFTbFUbFUbFUbFUbFUbFUbDybFVbFWbFXbDybFYbvabltlCYjrbbGabBpbBpbBpbBpbBpbBpbBpbAtbGbbGcbAubGdbBzbkobGebBoahtbAAbGfbGgbGhbCRbGibAAbGjbGkbGlbAFbGmbGnbGobGpbGqbGrbGsbGtbGubGvabmqtAbGxbGynoMabniVJtSLbGBlWybwmbwmrxQbwmbwmbwmbwmfKjbwmbwmbwmuugeMCjFwrNBaaaaaaahtaaaahtaaaahtaaaaaaaaaaaaahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabFEaaaaaaaaaaaaaaaaaaahtahtgLnaZxaLtaMMaZxahtbDfabIbBXbFFbvabvabvabvabvabsnbsnbvabFGbEnbFHbFIbFJbFKbFLbAWbErbFMbFNbFObFPbkhbkhbFQbnFacpbFSbkhbFTbFUbFUbFUbFUbFUbFUbDybFVbFWbFXbDybFYbvabltlCYjrbbGabBpbBpbBpbBpbBpbBpbBpbAtbGbbGcbAubGdbBzbkobGebBoahtbAAbGfbGgbGhbCRbGibAAbGjbGkbGlbAFbGmbGnbGobGpbGqbGrbGsbGtbGubGvabmqtAbGxbGynoMabniVJtSLbGBlWybwmbwmrxQbwmbwmbwmbwmfKjbwmbwmbwmuugeMCjFwrNBaaaaaaahtaaaahtaaaahtaaaaaaaaaaaaahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabGDbGEbGFbGEaMQbGHbGIaaaahtahtahtgLnaZxaNkaMMaZxahtagSaaabvabvabvabGKbGLbGMbvabvabvabvabGNbDibErbGObGPbGQbGRbAWbGSbGTbGUbFObGVbkhbGXbuhbuhactbGZbuhbHabFUbHbbHcbHdbFUbFUbFUbFUbHebFUbFUbDzbvajCvbAkbAlbHgbvDbHhbHibHjbHkbHlbHmaIAbHnbHobHgbHqbHrbHsbHtbCOahtbCPbCRbHubCRbCRbHvbHwbuzbuzbuzbHybHybHybHybHybHybHyubWbHzbGubHAnsDkhkbHCbHDbHEbAGbwmbwmbwmxlAbwmsvNlWynzDrosrosgxKlWybwmhXtbwmiVJiVJxpDbwmahtabyabyabyabyaedabyabyaedabyahtahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabHIbGDbGEbHJbHKbHLbGEbHMbHNaqGaqGaqGeOAaZxaNlaMMaZxahtagSbvabvabHPbHQbHQbHQbHRbHQbHSbvabHTbGNbDibErbHUbHVbHXbHXbHYbHZbIabIbbFObIcbIdbIebIfbIgbkbbCnbIhbIibIjbIkbIlbImbInbIobIpbIqbIrbIsbFUbDzbvabOKbItbIumVjbIvbIvbIvbIxbIvbIvbIycqwbIAbRXcqxbICbIDbBAbIEbBoaaabAAbIFbIGbIHbIIbIJbHwabIabIabIaaaaaaaaaaaaabIaaaaaaubWbIKbILbIMrPWbINcxbfBzbJVfwrrBhbIPbIQbIRlQnbIQrosuojbwmvpzpWFbIQbIQbIQbIQbIQdVJjQhbwmaaaahtahtaaaahtaaaahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabybITbIUpbmbIVbIWbIXbHMbGEbGEbGEbGEaZxaZxaNnaMMaZxahtagSbIZbJabJbbJcbJdbJebJfbJgbJhbvabJibGNbJjpQwbJksENbJmbJlbGWbGWcBKbJnbFObFObFObjcbjcbjcbjcbJobkhbJpabobJrbJsbJrabpbJubJvbJwbJxbJyabqbJAbvabENbJBbJCbJDcqzcqzcqzcqzbJEbJFbJGcqDbJHcqEbJJbJKbJLbJMbJNbJNbJNbHwbHwbJObHwbHwbHwbHwbJPbJPbJPbJPbJPahtabyabyabyahtxIxbJQbJRbJSkfhdAFbJTbCVbLhgMmbwmbwmbwmbwmbwmuekizFbwmbwmbwmbwmbwmbwmrNBbwmbwmjDAbwmbwmaaaaedabyabyaedabyabyaedabyahtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 1c05b2e2..72ad9ac5 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -99,3 +99,7 @@
*/
#define ELEMENT_BESPOKE (1 << 1)
#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" //Called when the movable's glide size is updated: (new_glide_size)
+
+#define COMSIG_LIVING_FULLY_HEAL "living_fully_healed" //from base of /mob/living/fully_heal(): (admin_revive)
+#define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
+
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 7d4dc1ca..2dbc63aa 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -76,6 +76,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define ishumanbasic(A) (is_species(A, /datum/species/human))
#define iscatperson(A) (ishumanbasic(A) && istype(A.dna.species, /datum/species/human/felinid) )
#define isvampire(A) (is_species(A,/datum/species/vampire))
+#define isdwarf(A) (is_species(A, /datum/species/dwarf))
+#define isdullahan(A) (is_species(A, /datum/species/dullahan))
// Citadel specific species
#define isipcperson(A) (is_species(A, /datum/species/ipc))
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 8ef1c3f3..0d64297b 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -263,6 +263,7 @@
var/atom/A = processing_list[1]
if(A.flags_1 & HEAR_1)
. += A
+ SEND_SIGNAL(A, COMSIG_ATOM_HEARER_IN_VIEW, processing_list, .)
processing_list.Cut(1, 2)
processing_list += A.contents
diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm
index 30a03449..c7ff1a2e 100644
--- a/code/datums/components/mood.dm
+++ b/code/datums/components/mood.dm
@@ -127,6 +127,9 @@
screen_obj.icon_state = "mood[mood_level]"
/datum/component/mood/process() //Called on SSmood process
+ if(QDELETED(parent)) // workaround to an obnoxious sneaky periodical runtime.
+ qdel(src)
+ return
var/mob/living/owner = parent
switch(mood_level)
@@ -284,7 +287,7 @@
RegisterSignal(screen_obj, COMSIG_CLICK, .proc/hud_click)
/datum/component/mood/proc/unmodify_hud(datum/source)
- if(!screen_obj)
+ if(!screen_obj || !parent)
return
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index 345212cc..3247b890 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -80,7 +80,7 @@
return TRUE
if(ishuman(L)) //Are you immune?
var/mob/living/carbon/human/H = L
- var/thermal_protection = H.get_thermal_protection()
+ var/thermal_protection = H.easy_thermal_protection()
if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
return TRUE
if(isliving(L))// if we're a non immune mob inside an immune mob we have to reconsider if that mob is immune to protect ourselves
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 684988a5..75adef3d 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -107,7 +107,7 @@
var/atom/movable/AM = i
var/obj/item/bodypart/head/as_head = AM
var/obj/item/mmi/as_mmi = AM
- var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM)
+ var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM) || istype(AM, /obj/item/dullahan_relay)
if(brain_holder)
emergency_stop(AM)
else if(isliving(AM))
diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm
index 23867d14..40452f68 100644
--- a/code/game/objects/effects/effect_system/effects_smoke.dm
+++ b/code/game/objects/effects/effect_system/effects_smoke.dm
@@ -288,7 +288,7 @@
contained = "\[[contained]\]"
var/where = "[AREACOORD(location)]"
- if(carry.my_atom.fingerprintslast)
+ if(carry.my_atom && carry.my_atom.fingerprintslast)
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
var/more = ""
if(M)
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 2ec683e9..78f1af4e 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -209,7 +209,7 @@
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
- cold_protection = H.get_cold_protection(air1.temperature)
+ cold_protection = H.get_thermal_protection(air1.temperature, TRUE)
if(abs(temperature_delta) > 1)
var/air_heat_capacity = air1.heat_capacity()
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 18ca55fe..07097fa8 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -335,6 +335,23 @@
item_state = "officertanjacket"
body_parts_covered = CHEST|ARMS
+/obj/item/clothing/suit/ran
+ name = "Shikigami costume"
+ desc = "A costume that looks like a certain shikigami, is super fluffy."
+ icon_state = "ran_suit"
+ item_state = "ran_suit"
+ body_parts_covered = CHEST|GROIN|LEGS
+ flags_inv = HIDEJUMPSUIT|HIDETAUR
+ heat_protection = CHEST|GROIN|LEGS //fluffy tails!
+//2061
+
+/obj/item/clothing/head/ran
+ name = "Shikigami hat"
+ desc = "A hat that looks like it keeps any fluffy ears contained super warm, has little charms over it."
+ icon_state = "ran_hat"
+ item_state = "ran_hat"
+ flags_inv = HIDEEARS
+
/*
* Misc
*/
diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css
index cca54377..2ca28d99 100644
--- a/code/modules/goonchat/browserassets/css/browserOutput.css
+++ b/code/modules/goonchat/browserassets/css/browserOutput.css
@@ -401,6 +401,7 @@ h1.alert, h2.alert {color: #000000;}
.redtext {color: #FF0000; font-size: 24px;}
.clown {color: #FF69Bf; font-size: 24px; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;}
.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
+.spooky {color: #FF6100;}
.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;}
@keyframes velvet {
0% { color: #400020; }
diff --git a/code/modules/holiday/halloween/bartholomew.dm b/code/modules/holiday/halloween/bartholomew.dm
new file mode 100644
index 00000000..aa119dca
--- /dev/null
+++ b/code/modules/holiday/halloween/bartholomew.dm
@@ -0,0 +1,158 @@
+/obj/effect/landmark/barthpot
+ name = "barthpot"
+
+/obj/item/barthpot
+ name = "Bartholomew"
+ icon = 'icons/obj/halloween_items.dmi'
+ icon_state = "barthpot"
+ anchored = TRUE
+ var/items_list = list()
+ speech_span = "spooky"
+ var/active = TRUE
+
+/obj/item/barthpot/Destroy()
+ var/obj/item/barthpot/n = new src(loc)
+ n.items_list = items_list
+ ..()
+
+
+/obj/item/barthpot/attackby(obj/item/I, mob/user, params)
+ if(!active)
+ say("Meow!")
+ return
+
+ for(var/I2 in items_list)
+ if(istype(I, I2))
+ qdel(I)
+ new /obj/item/reagent_containers/food/snacks/special_candy(loc)
+ to_chat(user, "You add the [I.name] to the pot and watch as it melts into the mixture, a candy crystalising in it's wake.")
+ say("Hooray! Thank you!")
+ items_list -= I2
+ return
+ say("It doesn't seem like that's magical enough!")
+
+/obj/item/barthpot/attack_hand(mob/user)
+ if(!active)
+ say("Meow!")
+ return
+ say("Hello there, I'm Bartholomew, Jacqueline's Familiar.")
+ sleep(20)
+
+ say("I'm currently seeking items to put into my pot, if we get the right items, it should crystalise into a magic candy!")
+ if(!iscarbon(user))
+ say("Though... I'm not sure you can help me.")
+
+ var/message = "From what I can tell, "
+ if(LAZYLEN(items_list) < 5)
+ generate_items()
+ for(var/I2 in items_list)
+ if(!I2)
+ items_list -= I2
+ continue
+ var/obj/item/I3 = new I2
+ message += "a [I3.name], "
+ message += "currently seem to have the most magic potential."
+ sleep(15)
+ say("[message]")
+ sleep(15)
+ //To help people find her
+ for(var/mob/living/simple_animal/jacq/J in GLOB.simple_animals[1])
+ var/turf/L1 = J.loc
+ if(!L1) //Incase someone uh.. puts her in a locker
+ return
+ var/area/L2 = L1.loc
+ if(L2)
+ say("Also, it seems that Jacqueline is currently at the [L2], if you're looking for her too.")
+
+/obj/item/barthpot/proc/generate_items()
+ var/length = LAZYLEN(items_list)
+ var/rand_items = list(/obj/item/bodybag = 1,
+ /obj/item/clothing/glasses/meson = 2,
+ /obj/item/clothing/glasses/sunglasses = 1,
+ /obj/item/clothing/gloves/color/fyellow = 1,
+ /obj/item/clothing/head/hardhat = 1,
+ /obj/item/clothing/head/hardhat/red = 1,
+ /obj/item/clothing/head/that = 1,
+ /obj/item/clothing/head/ushanka = 1,
+ /obj/item/clothing/head/welding = 1,
+ /obj/item/clothing/mask/gas = 10,
+ /obj/item/clothing/suit/hazardvest = 1,
+ /obj/item/clothing/suit/hooded/flashsuit = 1,
+ /obj/item/assembly/prox_sensor = 4,
+ /obj/item/assembly/timer = 3,
+ /obj/item/flashlight = 6,
+ /obj/item/flashlight/pen = 1,
+ /obj/item/flashlight/glowstick = 4,
+ /obj/item/multitool = 2,
+ /obj/item/radio = 2,
+ /obj/item/t_scanner = 5,
+ /obj/item/airlock_painter = 1,
+ /obj/item/stack/cable_coil = 6,
+ /obj/item/stack/medical/bruise_pack = 1,
+ /obj/item/stack/rods = 3,
+ /obj/item/stack/sheet/cardboard = 2,
+ /obj/item/stack/sheet/metal = 1,
+ /obj/item/stack/sheet/mineral/plasma = 1,
+ /obj/item/stack/sheet/rglass = 1,
+ /obj/item/coin = 1,
+ /obj/item/crowbar = 4,
+ /obj/item/extinguisher = 3,
+ /obj/item/hand_labeler = 1,
+ /obj/item/paper = 6,
+ /obj/item/pen = 5,
+ /obj/item/reagent_containers/spray/pestspray = 1,
+ /obj/item/reagent_containers/rag = 3,
+ /obj/item/stock_parts/cell = 3,
+ /obj/item/storage/belt/utility = 2,
+ /obj/item/storage/box = 4,
+ /obj/item/reagent_containers/food/drinks/sillycup = 1,
+ /obj/item/storage/box/donkpockets = 1,
+ /obj/item/storage/box/lights/mixed = 1,
+ /obj/item/storage/box/hug/medical = 1,
+ /obj/item/storage/fancy/cigarettes = 1,
+ /obj/item/storage/toolbox = 1,
+ /obj/item/screwdriver = 3,
+ /obj/item/tank/internals/emergency_oxygen = 2,
+ /obj/item/vending_refill/cola = 1,
+ /obj/item/weldingtool = 3,
+ /obj/item/wirecutters = 2,
+ /obj/item/wrench = 4,
+ /obj/item/weaponcrafting/receiver = 1,
+ /obj/item/geiger_counter = 3,
+ /obj/item/reagent_containers/food/snacks/grown/citrus/orange = 5,
+ /obj/item/assembly/infra = 1,
+ /obj/item/assembly/igniter = 2,
+ /obj/item/assembly/signaler = 2,
+ /obj/item/assembly/mousetrap = 5,
+ /obj/item/reagent_containers/syringe = 5,
+ /obj/item/clothing/gloves = 8,
+ /obj/item/storage/toolbox = 2,
+ /obj/item/reagent_containers/pill = 2,
+ /obj/item/clothing/shoes = 8,
+ /obj/item/clothing/head = 3,
+ /obj/item/reagent_containers/food/snacks = 3,
+ /obj/item/reagent_containers/syringe/dart = 2,
+ /obj/item/reagent_containers/food/drinks/soda_cans = 5)
+ if(length >= 5)
+ return TRUE
+ //var/metalist = pickweight(GLOB.maintenance_loot)
+ for(var/i = length, i <= 5, i+=1)
+ var/obj/item = pickweight(rand_items)
+ if(!item)
+ i-=1
+ continue
+ for(var/obj/item_dupe in items_list) //No duplicates
+ if(item_dupe == item)
+ i-=1
+ continue
+ items_list += item
+ return TRUE
+
+/obj/item/pinpointer/jacq
+ name = "The Jacq-Tracq"
+ desc = "A handheld tracking device that locks onto witchy signals."
+
+/obj/item/pinpointer/jacq/attack_self(mob/living/user)
+ for(var/mob/living/simple_animal/jacq/J in GLOB.simple_animals[1])
+ target = J
+ ..()
diff --git a/code/modules/holiday/halloween.dm b/code/modules/holiday/halloween/halloween.dm
similarity index 92%
rename from code/modules/holiday/halloween.dm
rename to code/modules/holiday/halloween/halloween.dm
index 53608d29..75bb9974 100644
--- a/code/modules/holiday/halloween.dm
+++ b/code/modules/holiday/halloween/halloween.dm
@@ -5,19 +5,27 @@
//spooky recipes
-/datum/recipe/sugarcookie/spookyskull
- reagents = list("flour" = 5, "sugar" = 5, "milk" = 5)
- items = list(
- /obj/item/reagent_containers/food/snacks/egg,
+/datum/crafting_recipe/food/sugarcookie/spookyskull
+ time = 15
+ name = "Sugar cookie"
+ reqs = list(
+ /datum/reagent/consumable/sugar = 5,
+ /obj/item/reagent_containers/food/snacks/pastrybase = 1
)
result = /obj/item/reagent_containers/food/snacks/sugarcookie/spookyskull
+ subcategory = CAT_PASTRY
-/datum/recipe/sugarcookie/spookycoffin
- reagents = list("flour" = 5, "sugar" = 5, "coffee" = 5)
- items = list(
- /obj/item/reagent_containers/food/snacks/egg,
+/datum/crafting_recipe/food/sugarcookie/spookycoffin
+ time = 15
+ name = "Sugar cookie"
+ reqs = list(
+ /datum/reagent/consumable/sugar = 5,
+ /datum/reagent/consumable/coffee = 5,
+ /obj/item/reagent_containers/food/snacks/pastrybase = 1
)
result = /obj/item/reagent_containers/food/snacks/sugarcookie/spookycoffin
+ subcategory = CAT_PASTRY
+
//////////////////////////////
//Spookoween trapped closets//
@@ -34,12 +42,12 @@
var/trapped = 0
var/mob/trapped_mob
-/obj/structure/closet/initialize()
+/obj/structure/closet/Initialize()
..()
if(prob(30))
set_spooky_trap()
-/obj/structure/closet/dump_contents()
+/obj/structure/closet/dump_contents(var/override = TRUE)
..()
trigger_spooky_trap()
diff --git a/code/modules/holiday/halloween/jacqueen.dm b/code/modules/holiday/halloween/jacqueen.dm
new file mode 100644
index 00000000..ed9254c2
--- /dev/null
+++ b/code/modules/holiday/halloween/jacqueen.dm
@@ -0,0 +1,482 @@
+//Conversation
+#define JACQ_HELLO (1<<0)
+#define JACQ_CANDIES (1<<1)
+#define JACQ_HEAD (1<<2)
+#define JACQ_FAR (1<<3)
+#define JACQ_WITCH (1<<4)
+#define JACQ_EXPELL (1<<5)
+#define JACQ_DATE (1<<6)
+
+/////// EVENT
+/datum/round_event_control/jacqueen
+ name = "Jacqueline's visit"
+ holidayID = "jacqueen"
+ typepath = /datum/round_event/jacqueen
+ weight = -1 //forces it to be called, regardless of weight
+ max_occurrences = 1
+ earliest_start = 0 MINUTES
+
+/datum/round_event/jacqueen/start()
+ ..()
+
+ for(var/mob/living/carbon/human/H in GLOB.carbon_list)
+ playsound(H, 'sound/spookoween/ahaha.ogg', 100, 0.25)
+
+ for(var/obj/effect/landmark/barthpot/bp in GLOB.landmarks_list)
+ new /obj/item/barthpot(bp.loc)
+ new /mob/living/simple_animal/jacq(bp.loc)
+
+/////// MOBS
+
+//Whacha doing in here like? Yae wan tae ruin ta magicks?
+/mob/living/simple_animal/jacq
+ name = "Jacqueline the Pumpqueen"
+ real_name = "Jacqueline"
+ icon = 'icons/obj/halloween_items.dmi'
+ icon_state = "jacqueline"
+ maxHealth = 25
+ health = 25
+ density = FALSE
+ speech_span = "spooky"
+ friendly = "pets"
+ response_help = "chats with"
+ light_range = 3
+ light_color = "#ff9842"
+ var/last_poof
+ var/progression = list() //Keep track of where people are in the story.
+ var/active = TRUE //Turn this to false to keep normal mob behavour
+ var/cached_z
+
+/mob/living/simple_animal/jacq/Initialize()
+ ..()
+ cached_z = z
+ poof()
+
+/mob/living/simple_animal/jacq/Life()
+ ..()
+ if(!ckey)
+ if((last_poof+3 MINUTES) < world.realtime)
+ poof()
+
+/mob/living/simple_animal/jacq/Destroy() //I.e invincible
+ visible_message("[src] cackles, \"You'll nae get rid a me that easily!\"")
+ playsound(loc, 'sound/spookoween/ahaha.ogg', 100, 0.25)
+ var/mob/living/simple_animal/jacq/Jacq = new src.type(loc)
+ Jacq.progression = progression
+ ..()
+
+/mob/living/simple_animal/jacq/death() //What is alive may never die
+ visible_message("[src] cackles, \"You'll nae get rid a me that easily!\"")
+ playsound(loc, 'sound/spookoween/ahaha.ogg', 100, 0.25)
+ health = 25
+ poof()
+
+/mob/living/simple_animal/jacq/attack_hand(mob/living/carbon/human/M)
+ if(!active)
+ say("Hello there [gender_check(M)]!")
+ return ..()
+ if(!ckey)
+ canmove = FALSE
+ chit_chat(M)
+ canmove = TRUE
+ ..()
+
+/mob/living/simple_animal/jacq/attack_paw(mob/living/carbon/monkey/M)
+ if(!active)
+ say("Hello there [gender_check(M)]!")
+ return ..()
+ if(!ckey)
+ canmove = FALSE
+ chit_chat(M)
+ canmove = TRUE
+ ..()
+
+/mob/living/simple_animal/jacq/proc/poof()
+ last_poof = world.realtime
+ var/datum/reagents/R = new/datum/reagents(100)//Hey, just in case.
+ var/datum/effect_system/smoke_spread/chem/s = new()
+ R.add_reagent("secretcatchem", (10))
+ s.set_up(R, 0, loc)
+ s.start()
+ visible_message("[src] disappears in a puff of smoke!")
+ canmove = TRUE
+ health = 25
+
+ var/hp_list = list()
+ for(var/obj/machinery/holopad/hp in world)
+ hp_list += hp
+
+ var/nono_areas = list("AI ")
+
+ for(var/i = 0, i <= 6, i+=1) //Attempts a jump 6 times.
+ var/obj/machinery/holopad/hp = pick(hp_list)
+ if(forceMove(pick(hp.loc)))
+
+ var/jacq_please_no = FALSE
+ for(var/no_area in nono_areas)
+ var/turf/L1 = hp.loc
+ if(!L1) //Incase the area isn't a turf (i.e. in a locker)
+ continue
+ var/area/L2 = L1.loc
+ if(L2)
+ if(findtext(L2.name, no_area))
+ jacq_please_no = TRUE
+
+ if(jacq_please_no)
+ i-=1
+ continue
+
+ //Try to go to populated areas
+ var/list/seen = viewers(8, get_turf(src))
+ for(var/victim in seen)
+ if(ishuman(victim))
+ if(z == cached_z)
+ return TRUE
+
+
+ return FALSE
+
+/mob/living/simple_animal/jacq/proc/gender_check(mob/living/carbon/C)
+ var/gender = "lamb"
+ if(C)
+ if(C.gender == MALE)
+ gender = "laddie"
+ if(C.gender == FEMALE)
+ gender = "lassie"
+ return gender
+
+//Ye wee bugger, gerrout of it. Ye've nae tae enjoy reading the code fer mae secrets like.
+/mob/living/simple_animal/jacq/proc/chit_chat(mob/living/carbon/C)
+ //Very important
+ var/gender = gender_check(C)
+ if(C)
+ if(C.gender == MALE)
+ gender = "laddie"
+ if(C.gender == FEMALE)
+ gender = "lassie"
+
+ if(!progression["[C.real_name]"] || !(progression["[C.real_name]"] & JACQ_HELLO))
+ visible_message("[src] smiles ominously at [C], \"Well halo there [gender]! Ah'm Jacqueline, tae great Pumpqueen, great tae meet ye.\"")
+ sleep(20)
+ visible_message("[src] continues, \"Ah'm sure yae well stunned, but ah've got nae taem fer that. Ah'm after the candies around this station. If yae get mae enoof o the wee buggers, Ah'll give ye a treat, or if yae feeling bold, Ah ken trick ye instead.\" giving [C] a wide grin.")
+ if(!progression["[C.real_name]"])
+ progression["[C.real_name]"] = NONE //TO MAKE SURE THAT THE LIST ENTRY EXISTS.
+
+ progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_HELLO
+
+ var/choices = list("Trick", "Treat", "How do I get candies?")
+ var/choice = input(C, "Trick or Treat?", "Trick or Treat?") in choices
+ switch(choice)
+ if("Trick")
+ trick(C)
+ return
+ if("Treat")
+ if(check_candies(C))
+ treat(C, gender)
+ else
+ visible_message("[src] raises an eyebrow, \"You've nae got any candies Ah want! They're the orange round ones, now bugger off an go get em first.\"")
+ return
+ if("How do I get candies?")
+ visible_message("[src] says, \"Gae find my familiar; Bartholomew. Ee's tendin the cauldron which ken bring oot t' magic energy in items scattered aroond. Knowing him, ee's probably gone tae somewhere with books.\"")
+ return
+
+/mob/living/simple_animal/jacq/proc/treat(mob/living/carbon/C, gender)
+ visible_message("[src] gives off a glowing smile, \"What ken Ah offer ye? I can magic up an object, a potion or a plushie fer ye.\"")
+ var/choices_reward = list("Object - 3 candies", "Potion - 2 candies", "Jacqueline Tracker - 2 candies", "Plushie - 1 candy", "Can I get to know you instead?", "Become a pumpkinhead dullahan (perma) - 4 candies")
+ var/choice_reward = input(usr, "Trick or Treat?", "Trick or Treat?") in choices_reward
+
+ //rewards
+ switch(choice_reward)
+ if("Become a pumpkinhead dullahan (perma) - 4 candies")
+ if(!take_candies(C, 4))
+ visible_message("[src] raises an eyebrown, \"It's 4 candies for that [gender]! Thems the rules!\"")
+ return
+ visible_message("[src] waves their arms around, \"Off comes your head, a pumpkin taking it's stead!\"")
+ C.reagents.add_reagent("pumpkinmutationtoxin", 5)
+ sleep(20)
+ poof()
+ return
+
+ if("Object - 3 candies")
+ if(!take_candies(C, 3))
+ visible_message("[src] raises an eyebrown, \"It's 3 candies per trinket [gender]! Thems the rules!\"")
+ return
+
+ var/new_obj = pick(subtypesof(/obj))
+ //for(var/item in blacklist)
+ // if(new_obj == item)
+ // panic()
+ var/reward = new new_obj(C.loc)
+ C.put_in_hands(reward)
+ visible_message("[src] waves her hands, magicking up a [reward] from thin air, \"There ye are [gender], enjoy! \"")
+ sleep(20)
+ poof()
+ return
+ if("Potion - 2 candies")
+ if(!take_candies(C, 2))
+ visible_message("[src] raises an eyebrow, \"It's 2 candies per potion [gender]! Thems the rules!\"")
+ return
+
+ var/reward = new /obj/item/reagent_containers/potion_container(C.loc)
+ C.put_in_hands(reward)
+ visible_message("[src] waves her hands, magicking up a [reward] from thin air, \"There ye are [gender], enjoy! \"")
+ sleep(20)
+ poof()
+ return
+ if("Plushie - 1 candy")
+ if(!take_candies(C, 1))
+ visible_message("[src] raises an eyebrow, \"It's 1 candy per plushie [gender]! Thems the rules!\"")
+ return
+
+ new /obj/item/toy/plush/random(C.loc)
+ visible_message("[src] waves her hands, magicking up a plushie from thin air, \"There ye are [gender], enjoy! \"")
+ sleep(20)
+ poof()
+ return
+ if("Jacqueline Tracker - 2 candies")
+ if(!take_candies(C, 2))
+ visible_message("[src] raises an eyebrow, \"It's 1 candy per plushie [gender]! Thems the rules!\"")
+ return
+ new /obj/item/pinpointer/jacq(C.loc)
+ visible_message("[src] waves her hands, magicking up a tracker from thin air, \"Feels weird to magic up a tracker fer meself but, here ye are [gender], enjoy! \"")
+ sleep(20)
+ poof()
+ return
+
+ //chitchats!
+ if("Can I get to know you instead?")
+ var/choices = list()
+ //Figure out where the C is in the story
+ if(!progression["[C.real_name]"]) //I really don't want to get here withoot a hello, but just to be safe
+ progression["[C.real_name]"] = NONE
+ if(!(progression["[C.real_name]"] & JACQ_FAR))
+ if(progression["[C.real_name]"] & JACQ_CANDIES)
+ choices += "You really came all this way for candy?"
+ else
+ choices += "Why do you want the candies?"
+ if(!(progression["[C.real_name]"] & JACQ_HEAD))
+ choices += "What is that on your head?"
+ if(!(progression["[C.real_name]"] & JACQ_EXPELL))
+ if(progression["[C.real_name]"] & JACQ_WITCH)
+ choices += "So you got ex-spell-ed?"
+ else
+ choices += "Are you a witch?"
+
+ //for Kepler, delete this, or just delete the whole story aspect if you want.
+ //If fully completed
+ /*
+ if(progression["[C.real_name]"] & JACQ_FAR)//Damnit this is a pain
+ if(progression["[C.real_name]"] & JACQ_EXPELL) //I give up
+ if(progression["[C.real_name]"] & JACQ_HEAD) //This is only an event thing
+ choices += "Can I take you out on a date?"
+ */
+ if(progression["[C.real_name]"] == 63)//Damnit this is a pain
+ choices += "Can I take you out on a date?"
+
+ //If you've nothing to ask
+ if(!LAZYLEN(choices))
+ visible_message("[src] sighs, \"Ah'm all questioned oot fer noo, [gender].\"")
+ return
+ //Otherwise, lets go!
+ visible_message("[src] says, \"A question? Sure, it'll cost you a candy though!\"")
+ choices += "Nevermind"
+ //Candies for chitchats
+ var/choice = input(C, "What do you want to ask?", "What do you want to ask?") in choices
+ if(!take_candies(C, 1))
+ visible_message("[src] raises an eyebrow, \"It's a candy per question [gender]! Thems the rules!\"")
+ return
+ //Talking
+ switch(choice)
+ if("Why do you want the candies?")
+ visible_message("[src] says, \"Ave ye tried them? They're full of all sorts of reagents. Ah'm after them so ah ken magic em up an hopefully find rare stuff fer me brews. Honestly it's a lot easier magicking up tatt fer ye lot than runnin aroond on me own like. I'd ask me familiars but most a my familiars are funny fellows 'n constantly bugger off on adventures when given simple objectives like; Go grab me a tea cake or watch over me cauldron. Ah mean, ye might run into Bartholomew my cat. Ee's supposed tae be tending my cauldron, but I've nae idea where ee's got tae.\"")
+ progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_CANDIES
+ sleep(30)
+
+ if("You really came all this way for candy?")
+ visible_message("[src] l ooks tae the side sheepishly, \"Aye, well, tae be honest, Ah'm here tae see me sis, but dunnae let her knew that. She's an alchemist too like, but she dunnae use a caldron like mae, she buggered off like tae her posh ivory tower tae learn bloody chemistry instead!\" [src] scowls, \"She's tae black sheep o' the family too, so we dunnae see eye tae eye sometimes on alchemy. Ah mean, she puts moles in her brews! Ye dunnae put moles in yer brews! Yae threw your brews at tae wee bastards an blew em up!\" [src] sighs, \"But she's a heart o gold so.. Ah wanted tae see her an check up oon her, make sure she's okay.\"")
+ progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_FAR
+ sleep(30)
+
+ if("What is that on your head?")
+ visible_message("[src] pats the pumpkin atop her head, \"This thing? This ain't nae ordinary pumpkin! Me Ma grew this monster ooer a year o love, dedication an hard work. Honestly it felt like she loved this thing more than any of us, which Ah knew ain't true an it's not like she was hartless or anything but.. well, we had a falling oot when Ah got back home with all me stuff in tow. An all she had done is sent me owl after owl over t' last year aboot this bloody pumpkin and ah had enough. So ah took it, an put it on me head. You know, as ye do. Ah am the great Pumpqueen after all, Ah deserve this.\"")
+ progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_HEAD
+ sleep(30)
+
+ if("Are you a witch?")
+ visible_message("[src] grumbles, \"If ye must know, Ah got kicked oot of the witch academy fer being too much of a \"loose cannon\". A bloody loose cannon? Nae they were just pissed off Ah had the brass tae proclaim myself as the Pumpqueen! And also maybe the time Ah went and blew up one of the towers by trying tae make a huge batch of astrogen might've had something tae do with it. Ah mean it would've worked fine if the cauldrons weren't so shite and were actually upgraded by the faculty. So technically no, I'm not a witch.\"")
+ progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_WITCH
+ sleep(30)
+
+ if("So you got ex-spell-ed?")
+ visible_message("[src] Gives you a blank look at the pun, before continuing, \"Not quite, Ah know Ah ken get back into the academy, it's only an explosion, they happen all the time, but, tae be fair it's my fault that things came tae their explosive climax. You don't know what it's like when you're after a witch doctorate, everyone else is doing well, everyone's making new spells and the like, and I'm just good at making explosions really, or fireworks. So, Ah did something Ah knew was dangerous, because Ah had tae do something tae stand oot, but Ah know this life ain't fer me, Ah don't want tae be locked up in dusty towers, grinding reagent after reagent together, trying tae find new reactions, some of the wizards in there haven't left fer years. Ah want tae live, Ah want tae fly around on a broom, turn people into cats fer a day and disappear cackling! That's what got me into witchcraft!\" she throws her arms up in the arm, spinning the pumpkin upon her head slightly. She carefully spins it back to face you, giving oot a soft sigh, \"Ah know my mother's obsession with this dumb thing on my head is just her trying tae fill the void of me and my sis moving oot, and it really shouldn't be on my head. And Ah know that I'm really here tae get help from my sis.. She's the sensible one, and she gives good hugs.\"")
+ sleep(30)
+ visible_message("[src] says, \"Thanks [C], Ah guess Ah didn't realise Ah needed someone tae talk tae but, I'm glad ye spent all your candies talking tae me. Funny how things seem much worse in yer head.\"")
+ progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_EXPELL
+ sleep(30)
+
+ if("Can I take you out on a date?")
+ visible_message("[src] blushes, \"...You want tae ask me oot on a date? Me? After all that nonsense Ah just said? It seems a waste of a candy honestly.\"")
+ //progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_DATE
+ visible_message("[src] looks to the side, deep in thought.")
+ dating_start(C, gender)
+
+ if("Nevermind")
+ visible_message("[src] shrugs, \"Suit yerself then, here's your candy back.\"")
+ new /obj/item/reagent_containers/food/snacks/special_candy(loc)
+
+
+/mob/living/simple_animal/jacq/proc/trick(mob/living/carbon/C, gender)
+ var/option
+ if(ishuman(C))
+ option = rand(1,6)
+ else
+ option = rand(1,5)
+ switch(option)
+ if(1)
+ visible_message("[src] waves their arms around, \"Hocus pocus, making friends is now your focus!\"")
+ var/message = pick("make a tasty sandwich for", "compose a poem for", "aquire a nice outfit to give to", "strike up a conversation about pumpkins with", "write a letter and deliver it to", "give a nice hat to")
+ var/mob/living/L2 = pick(GLOB.player_list)
+ message += " [L2.name]."
+ to_chat(C, " You feel an overwhelming desire to [message]")
+ if(2)
+ visible_message("[src] waves their arms around, \"If only you had a better upbringing, your ears are now full of my singing!\"")
+ var/client/C2 = C.client
+ C2.chatOutput.sendMusic("https://puu.sh/ExBbv.mp4", 1)//I hope this works!
+ if(3)
+ visible_message("[src] waves their arms around, \"You're cute little bumpkin, On your head is a pumpkin!\"")
+ if(C.head)
+ var/obj/item/W = C.head
+ C.dropItemToGround(W, TRUE)
+ var/jaqc_latern = new /obj/item/clothing/head/hardhat/pumpkinhead/jaqc
+ C.equip_to_slot(jaqc_latern, SLOT_HEAD, 1, 1)
+ if(4)
+ visible_message("[src] waves their arms around, \"In your body there's something amiss, you'll find it's a chem made by my sis!\"")
+ C.reagents.add_reagent("eigenstate", 30)
+ if(5)
+ visible_message("[src] waves their arms around, \"A new familiar for me, and you'll see it's thee!\"")
+ C.reagents.add_reagent("secretcatchem", 30)
+ if(6)
+ visible_message("[src] waves their arms around, \"While you may not be a ghost, for this sheet you'll always be it's host.\"")
+ var/mob/living/carbon/human/H = C
+ if(H.wear_suit)
+ var/obj/item/W = H.wear_suit
+ H.dropItemToGround(W, TRUE)
+ var/ghost = new /obj/item/clothing/suit/ghost_sheet/sticky
+ H.equip_to_slot(ghost, SLOT_WEAR_SUIT, 1, 1)
+ poof()
+
+//Blame Fel
+/mob/living/simple_animal/jacq/proc/dating_start(mob/living/carbon/C, gender)
+ var/candies = pollGhostCandidates("Do you want to go on a date with [C] as Jacqueline the great pumpqueen?")
+ //sleep(30) //If the poll doesn't autopause.
+ if(candies)
+ candies = shuffle(candies)//Shake those ghosts up!
+ for(var/mob/dead/observer/C2 in candies)
+ if(C2.key && C2)
+ key = C2.key
+ message_admins("[C2]/[C2.key] has agreed to go on a date with [C] as Jacqueline.")
+ log_game("HALLOWEEN: [C2]/[C2.key] has agreed to go on a date with [C] as Jacqueline")
+ to_chat(src, "You are Jacqueline the great pumpqueen, witch Extraordinaire! You're a very Scottish lass with a kind heart, but also a little crazy. You also blew up the wizarding school and you're suspended for a while, so you visited the station before heading home. On your head lies the prize pumpkin of your Mother's pumpkin patch. You're currently on a date with [C] and well, I didn't think anyone would get this far. Please be good so I can do events like this in the future. ")
+ return
+ else
+ candies =- C2
+ visible_message("[src] looks to the side, \"Look, Ah like ye but, Ah don't think Ah can right now. If ye can't tell, the stations covered in volatile candies, I've a few other laddies and lassies running after me treats, and tae top it all off, I've the gods breathing down me neck, watching every treat Ah make fer the lot of yous.\" she sighs, \"But that's not a no, right? That's.. just a nae right noo.\"")
+ sleep(20)
+ visible_message("[src] takes off the pumpkin on her head, a rich blush on her cheeks. She leans over planting a kiss upon your forehead quickly befere popping the pumpkin back on her head.")
+ sleep(10)
+ visible_message("[src] waves their arms around, \"There, that aught tae be worth a candy.\"")
+ sleep(20)
+ poof()
+
+/obj/item/clothing/head/hardhat/pumpkinhead/jaqc
+ name = "Jacq o' latern"
+ desc = "A jacqueline o' lantern! You can't seem to get rid of it."
+ icon_state = "hardhat0_pumpkin_j"
+ item_state = "hardhat0_pumpkin_j"
+ item_color = "pumpkin_j"
+ brightness_on = 4
+
+/obj/item/clothing/head/hardhat/pumpkinhead/jaqc/Initialize()
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, GLUED_ITEM_TRAIT)
+
+/obj/item/clothing/suit/ghost_sheet/sticky
+
+/obj/item/clothing/suit/ghost_sheet/sticky/Initialize()
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, GLUED_ITEM_TRAIT)
+
+/obj/item/clothing/suit/ghost_sheet/sticky/attack_hand(mob/user)
+ if(iscarbon(user))
+ to_chat(user, "Boooooo~!")
+ return
+ else
+ ..()
+
+/obj/item/clothing/suit/ghost_sheet/sticky/attack_hand(mob/user)
+ if(iscarbon(user))
+ to_chat(user, "Boooooo~!")
+ return
+ else
+ ..()
+
+/datum/reagent/mutationtoxin/pumpkinhead
+ name = "Pumpkin head mutation toxin"
+ id = "pumpkinmutationtoxin"
+ race = /datum/species/dullahan/pumpkin
+ mutationtext = "The pain subsides. You feel your head roll off your shoulders... and you smell pumpkin."
+ //I couldn't get the replace head sprite with a pumpkin to work so, it is what it is.
+
+/mob/living/simple_animal/jacq/proc/check_candies(mob/living/carbon/C)
+ var/invs = C.get_contents()
+ var/candy_count = 0
+ for(var/item in invs)
+ if(istype(item, /obj/item/reagent_containers/food/snacks/special_candy))
+ candy_count++
+ return candy_count
+
+/mob/living/simple_animal/jacq/proc/take_candies(mob/living/carbon/C, candy_amount = 1)
+ var/inv = C.get_contents()
+ var/candies = list()
+ for(var/item in inv)
+ if(istype(item, /obj/item/reagent_containers/food/snacks/special_candy))
+ candies += item
+ if(LAZYLEN(candies) == candy_amount)
+ break
+ if(LAZYLEN(candies) == candy_amount) //I know it's a double check but eh, to be safe.
+ for(var/candy in candies)
+ qdel(candy)
+ return TRUE
+ return FALSE
+
+//Potions
+/obj/item/reagent_containers/potion_container
+ name = "potion"
+ icon = 'icons/obj/halloween_items.dmi'
+ icon_state = "jacq_potion"
+ desc = "A potion with a strange concoction within. Be careful, as if it's thrown it explodes in a puff of smoke like Jacqueline."
+
+/obj/item/reagent_containers/potion_container/Initialize()
+ .=..()
+ var/R = get_random_reagent_id()
+ reagents.add_reagent(R, 30)
+ name = "[R] Potion"
+
+/obj/item/reagent_containers/potion_container/throw_impact(atom/target)
+ ..()
+ sleep(20)
+ var/datum/effect_system/smoke_spread/chem/s = new()
+ s.set_up(src.reagents, 3, src.loc)
+ s.start()
+ qdel(src)
+
+//Candies
+/obj/item/reagent_containers/food/snacks/special_candy
+ name = "Magic candy"
+ icon = 'icons/obj/halloween_items.dmi'
+ icon_state = "jacq_candy"
+ desc = "A candy with strange magic within. Be careful, as the magic isn't always helpful."
+
+/obj/item/reagent_containers/food/snacks/special_candy/Initialize()
+ .=..()
+ reagents.add_reagent(get_random_reagent_id(), 5)
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 7d1e2523..9d075857 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -333,6 +333,16 @@
/datum/holiday/halloween/getStationPrefix()
return pick("Bone-Rattling","Mr. Bones' Own","2SPOOKY","Spooky","Scary","Skeletons")
+/datum/holiday/jacqueen //Subset of halloween
+ name = "jacqueen"
+ begin_day = 27
+ begin_month = OCTOBER
+ end_day = 2
+ end_month = NOVEMBER
+
+/datum/holiday/jacqueen/greet()
+ return "Jacqueline the great Pumpqueen has come to visit!"
+
/datum/holiday/vegan
name = "Vegan Day"
begin_day = 1
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 5a0dce99..139ab861 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -3,7 +3,7 @@
//NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick!
// bitflags for the percentual amount of protection a piece of clothing which covers the body part offers.
-// Used with human/proc/get_heat_protection() and human/proc/get_cold_protection()
+// Used with human/proc/get_thermal_protection()
// The values here should add up to 1.
// Hands and feet have 2.5%, arms and legs 7.5%, each of the torso parts has 15% and the head has 30%
#define THERMAL_PROTECTION_HEAD 0.3
@@ -50,16 +50,17 @@
/mob/living/carbon/human/calculate_affecting_pressure(pressure)
- if (wear_suit && head && istype(wear_suit, /obj/item/clothing) && istype(head, /obj/item/clothing))
+ var/headless = !get_bodypart(BODY_ZONE_HEAD) //should the mob be perennially headless (see dullahans), we only take the suit into account, so they can into space.
+ if (wear_suit && istype(wear_suit, /obj/item/clothing) && (headless || (head && istype(head, /obj/item/clothing))))
var/obj/item/clothing/CS = wear_suit
var/obj/item/clothing/CH = head
- if (CS.clothing_flags & CH.clothing_flags & STOPSPRESSUREDAMAGE)
+ if (CS.clothing_flags & STOPSPRESSUREDAMAGE && (headless || (CH.clothing_flags & STOPSPRESSUREDAMAGE)))
return ONE_ATMOSPHERE
if(istype(loc, /obj/belly)) //START OF CIT CHANGES - Makes it so you don't suffocate while inside vore organs. Remind me to modularize this some time - Bhijn
return ONE_ATMOSPHERE
if(istype(loc, /obj/item/dogborg/sleeper))
return ONE_ATMOSPHERE //END OF CIT CHANGES
- return pressure
+ return ..()
/mob/living/carbon/human/handle_traits()
@@ -133,7 +134,7 @@
if(dna)
dna.species.handle_fire(src)
-/mob/living/carbon/human/proc/get_thermal_protection()
+/mob/living/carbon/human/proc/easy_thermal_protection()
var/thermal_protection = 0 //Simple check to estimate how protected we are against multiple temperatures
//CITADEL EDIT Vore code required overrides
if(istype(loc, /obj/item/dogborg/sleeper))
@@ -165,7 +166,6 @@
..()
//END FIRE CODE
-
//This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, CHEST, GROIN, etc. See setup.dm for the full list)
/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to.
var/thermal_protection_flags = 0
@@ -191,37 +191,6 @@
return thermal_protection_flags
-/mob/living/carbon/human/proc/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to.
- var/thermal_protection_flags = get_heat_protection_flags(temperature)
-
- var/thermal_protection = 0
- if(thermal_protection_flags)
- if(thermal_protection_flags & HEAD)
- thermal_protection += THERMAL_PROTECTION_HEAD
- if(thermal_protection_flags & CHEST)
- thermal_protection += THERMAL_PROTECTION_CHEST
- if(thermal_protection_flags & GROIN)
- thermal_protection += THERMAL_PROTECTION_GROIN
- if(thermal_protection_flags & LEG_LEFT)
- thermal_protection += THERMAL_PROTECTION_LEG_LEFT
- if(thermal_protection_flags & LEG_RIGHT)
- thermal_protection += THERMAL_PROTECTION_LEG_RIGHT
- if(thermal_protection_flags & FOOT_LEFT)
- thermal_protection += THERMAL_PROTECTION_FOOT_LEFT
- if(thermal_protection_flags & FOOT_RIGHT)
- thermal_protection += THERMAL_PROTECTION_FOOT_RIGHT
- if(thermal_protection_flags & ARM_LEFT)
- thermal_protection += THERMAL_PROTECTION_ARM_LEFT
- if(thermal_protection_flags & ARM_RIGHT)
- thermal_protection += THERMAL_PROTECTION_ARM_RIGHT
- if(thermal_protection_flags & HAND_LEFT)
- thermal_protection += THERMAL_PROTECTION_HAND_LEFT
- if(thermal_protection_flags & HAND_RIGHT)
- thermal_protection += THERMAL_PROTECTION_HAND_RIGHT
-
-
- return min(1,thermal_protection)
-
//See proc/get_heat_protection_flags(temperature) for the description of this proc.
/mob/living/carbon/human/proc/get_cold_protection_flags(temperature)
var/thermal_protection_flags = 0
@@ -248,17 +217,42 @@
return thermal_protection_flags
-/mob/living/carbon/human/proc/get_cold_protection(temperature)
-//CITADEL EDIT Mandatory for vore code.
- if(istype(loc, /obj/item/dogborg/sleeper))
- return TRUE //freezing to death in sleepers ruins fun.
- if(isbelly(loc))
- return TRUE
- if(ismob(loc))
- return TRUE //because lazy and being inside somemone insulates you from space
-//END EDIT
- temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K.
- var/thermal_protection_flags = get_cold_protection_flags(temperature)
+/mob/living/carbon/human/proc/get_thermal_protection(temperature, cold = FALSE)
+ if(cold)
+ //CITADEL EDIT Mandatory for vore code.
+ if(istype(loc, /obj/item/dogborg/sleeper) || isbelly(loc) || ismob(loc))
+ return 1 //freezing to death in sleepers ruins fun.
+ //END EDIT
+ temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K.
+ var/thermal_protection_flags = cold ? get_cold_protection_flags(temperature) : get_heat_protection_flags(temperature)
+ var/missing_body_parts_flags = ~get_body_parts_flags()
+ var/max_protection = 1
+ if(missing_body_parts_flags) //I don't like copypasta as much as proc overhead. Do you want me to make these into a macro?
+ DISABLE_BITFIELD(thermal_protection_flags, missing_body_parts_flags)
+ if(missing_body_parts_flags & HEAD)
+ max_protection -= THERMAL_PROTECTION_HEAD
+ if(missing_body_parts_flags & CHEST)
+ max_protection -= THERMAL_PROTECTION_CHEST
+ if(missing_body_parts_flags & GROIN)
+ max_protection -= THERMAL_PROTECTION_GROIN
+ if(missing_body_parts_flags & LEG_LEFT)
+ max_protection -= THERMAL_PROTECTION_LEG_LEFT
+ if(missing_body_parts_flags & LEG_RIGHT)
+ max_protection -= THERMAL_PROTECTION_LEG_RIGHT
+ if(missing_body_parts_flags & FOOT_LEFT)
+ max_protection -= THERMAL_PROTECTION_FOOT_LEFT
+ if(missing_body_parts_flags & FOOT_RIGHT)
+ max_protection -= THERMAL_PROTECTION_FOOT_RIGHT
+ if(missing_body_parts_flags & ARM_LEFT)
+ max_protection -= THERMAL_PROTECTION_ARM_LEFT
+ if(missing_body_parts_flags & ARM_RIGHT)
+ max_protection -= THERMAL_PROTECTION_ARM_RIGHT
+ if(missing_body_parts_flags & HAND_LEFT)
+ max_protection -= THERMAL_PROTECTION_HAND_LEFT
+ if(missing_body_parts_flags & HAND_RIGHT)
+ max_protection -= THERMAL_PROTECTION_HAND_RIGHT
+ if(max_protection == 0) //Is it even a man if it doesn't have a body at all? Early return to avoid division by zero.
+ return 1
var/thermal_protection = 0
if(thermal_protection_flags)
@@ -285,7 +279,7 @@
if(thermal_protection_flags & HAND_RIGHT)
thermal_protection += THERMAL_PROTECTION_HAND_RIGHT
- return min(1,thermal_protection)
+ return round(thermal_protection/max_protection, 0.001)
/mob/living/carbon/human/handle_random_events()
//Puke if toxloss is too high
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index d0f017a4..39868088 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1877,13 +1877,13 @@ GLOBAL_LIST_EMPTY(roundstart_races)
natural = H.natural_bodytemperature_stabilization()
var/thermal_protection = 1
if(loc_temp < H.bodytemperature) //Place is colder than we are
- thermal_protection -= H.get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
+ thermal_protection -= H.get_thermal_protection(loc_temp, TRUE) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(H.bodytemperature < BODYTEMP_NORMAL) //we're cold, insulation helps us retain body heat and will reduce the heat we lose to the environment
H.adjust_bodytemperature((thermal_protection+1)*natural + max(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_COLD_DIVISOR, BODYTEMP_COOLING_MAX))
else //we're sweating, insulation hinders our ability to reduce heat - and it will reduce the amount of cooling you get from the environment
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + max((thermal_protection * (loc_temp - H.bodytemperature) + BODYTEMP_NORMAL - H.bodytemperature) / BODYTEMP_COLD_DIVISOR , BODYTEMP_COOLING_MAX)) //Extra calculation for hardsuits to bleed off heat
else //Place is hotter than we are
- thermal_protection -= H.get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
+ thermal_protection -= H.get_thermal_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(H.bodytemperature < BODYTEMP_NORMAL) //and we're cold, insulation enhances our ability to retain body heat but reduces the heat we get from the environment
H.adjust_bodytemperature((thermal_protection+1)*natural + min(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
else //we're sweating, insulation hinders out ability to reduce heat - but will reduce the amount of heat we get from the environment
@@ -2022,7 +2022,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(!(I.resistance_flags & FIRE_PROOF))
I.take_damage(H.fire_stacks, BURN, "fire", 0)
- var/thermal_protection = H.get_thermal_protection()
+ var/thermal_protection = H.easy_thermal_protection()
if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT && !no_protection)
return
diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
index 9d641b57..5b2ad6e5 100644
--- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm
+++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
@@ -13,9 +13,14 @@
blacklisted = TRUE
limbs_id = "human"
skinned_type = /obj/item/stack/sheet/animalhide/human
+ var/pumpkin = FALSE
var/obj/item/dullahan_relay/myhead
+/datum/species/dullahan/pumpkin
+ name = "Pumpkin Head Dullahan"
+ id = "pumpkindullahan"
+ pumpkin = TRUE
/datum/species/dullahan/check_roundstart_eligible()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
@@ -24,17 +29,25 @@
/datum/species/dullahan/on_species_gain(mob/living/carbon/human/H, datum/species/old_species)
. = ..()
- H.flags_1 &= ~HEAR_1
+ DISABLE_BITFIELD(H.flags_1, HEAR_1)
var/obj/item/bodypart/head/head = H.get_bodypart(BODY_ZONE_HEAD)
if(head)
+ if(pumpkin)//Pumpkinhead!
+ head.animal_origin = 100
+ head.icon = 'icons/obj/clothing/hats.dmi'
+ head.icon_state = "hardhat1_pumpkin_j"
+ head.custom_head = TRUE
head.drop_limb()
- head.flags_1 = HEAR_1
- head.throwforce = 25
- myhead = new /obj/item/dullahan_relay (head, H)
- H.put_in_hands(head)
+ if(!QDELETED(head)) //drop_limb() deletes the limb if it's no drop location and dummy humans used for rendering icons are located in nullspace. Do the math.
+ head.throwforce = 25
+ myhead = new /obj/item/dullahan_relay (head, H)
+ H.put_in_hands(head)
+ var/obj/item/organ/eyes/E = H.getorganslot(ORGAN_SLOT_EYES)
+ for(var/datum/action/item_action/organ_action/OA in E.actions)
+ OA.Trigger()
/datum/species/dullahan/on_species_loss(mob/living/carbon/human/H)
- H.flags_1 |= ~HEAR_1
+ ENABLE_BITFIELD(H.flags_1, HEAR_1)
H.reset_perspective(H)
if(myhead)
var/obj/item/dullahan_relay/DR = myhead
@@ -73,7 +86,7 @@
/obj/item/organ/tongue/dullahan/handle_speech(datum/source, list/speech_args)
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- if(H.dna.species.id == "dullahan")
+ if(isdullahan(H))
var/datum/species/dullahan/D = H.dna.species
if(isobj(D.myhead.loc))
var/obj/O = D.myhead.loc
@@ -88,6 +101,7 @@
desc = "An abstraction."
actions_types = list(/datum/action/item_action/organ_action/dullahan)
zone = "abstract"
+ tint = INFINITY // used to switch the vision perspective to the head on species_gain().
/datum/action/item_action/organ_action/dullahan
name = "Toggle Perspective"
@@ -103,39 +117,55 @@
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- if(H.dna.species.id == "dullahan")
+ if(isdullahan(H))
var/datum/species/dullahan/D = H.dna.species
D.update_vision_perspective(H)
/obj/item/dullahan_relay
+ name = "dullahan relay"
var/mob/living/owner
flags_1 = HEAR_1
-/obj/item/dullahan_relay/Initialize(mapload,new_owner)
+/obj/item/dullahan_relay/Initialize(mapload, mob/living/carbon/human/new_owner)
. = ..()
+ if(!new_owner)
+ return INITIALIZE_HINT_QDEL
owner = new_owner
START_PROCESSING(SSobj, src)
+ RegisterSignal(owner, COMSIG_CLICK_SHIFT, .proc/examinate_check)
+ RegisterSignal(src, COMSIG_ATOM_HEARER_IN_VIEW, .proc/include_owner)
+ RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head)
+ RegisterSignal(owner, COMSIG_LIVING_FULLY_HEAL, .proc/retrieve_head)
+
+/obj/item/dullahan_relay/proc/examinate_check(atom/source, mob/user)
+ if(user.client.eye == src)
+ return COMPONENT_ALLOW_EXAMINATE
+
+/obj/item/dullahan_relay/proc/include_owner(datum/source, list/processing_list, list/hearers)
+ if(!QDELETED(owner))
+ hearers += owner
+
+/obj/item/dullahan_relay/proc/unlist_head(datum/source, noheal = FALSE, list/excluded_limbs)
+ excluded_limbs |= BODY_ZONE_HEAD // So we don't gib when regenerating limbs.
+
+/obj/item/dullahan_relay/proc/retrieve_head(datum/source, admin_revive = FALSE)
+ if(admin_revive) //retrieving the owner's head for ahealing purposes.
+ var/obj/item/bodypart/head/H = loc
+ var/turf/T = get_turf(owner)
+ if(H && istype(H) && T && !(H in owner.GetAllContents()))
+ H.forceMove(T)
/obj/item/dullahan_relay/process()
if(!istype(loc, /obj/item/bodypart/head) || QDELETED(owner))
. = PROCESS_KILL
qdel(src)
-/obj/item/dullahan_relay/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
- . = ..()
- if(!QDELETED(owner))
- message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
- to_chat(owner,message)
- else
- qdel(src)
-
-
/obj/item/dullahan_relay/Destroy()
if(!QDELETED(owner))
var/mob/living/carbon/human/H = owner
- if(H.dna.species.id == "dullahan")
+ if(isdullahan(H))
var/datum/species/dullahan/D = H.dna.species
D.myhead = null
owner.gib()
owner = null
- ..()
\ No newline at end of file
+ ..()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index a61f86b6..bd88a3c8 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -536,6 +536,7 @@
for(var/organ in C.internal_organs)
var/obj/item/organ/O = organ
O.setOrganDamage(0)
+ SEND_SIGNAL(src, COMSIG_LIVING_FULLY_HEAL, admin_revive)
//proc called by revive(), to check if we can actually ressuscitate the mob (we don't want to revive him and have him instantly die again)
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 32d790d8..4d681c59 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -334,12 +334,13 @@
//Regenerates all limbs. Returns amount of limbs regenerated
-/mob/living/proc/regenerate_limbs(noheal, excluded_limbs)
- return 0
+/mob/living/proc/regenerate_limbs(noheal = FALSE, list/excluded_limbs = list())
+ SEND_SIGNAL(src, COMSIG_LIVING_REGENERATE_LIMBS, noheal, excluded_limbs)
-/mob/living/carbon/regenerate_limbs(noheal, list/excluded_limbs)
+/mob/living/carbon/regenerate_limbs(noheal = FALSE, list/excluded_limbs = list())
+ . = ..()
var/list/limb_list = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
- if(excluded_limbs)
+ if(excluded_limbs.len)
limb_list -= excluded_limbs
for(var/Z in limb_list)
. += regenerate_limb(Z, noheal)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 9057ceca..da9ec462 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -32,6 +32,8 @@
var/lip_style = null
var/lip_color = "white"
+ //If the head is a special sprite
+ var/custom_head
/obj/item/bodypart/head/can_dismember(obj/item/I)
if(!((owner.stat == DEAD) || owner.InFullCritical()))
@@ -118,6 +120,8 @@
..()
/obj/item/bodypart/head/update_icon_dropped()
+ if(custom_head)
+ return
var/list/standing = get_limb_icon(1)
if(!standing.len)
icon_state = initial(icon_state)//no overlays found, we default back to initial icon.
@@ -128,6 +132,8 @@
add_overlay(standing)
/obj/item/bodypart/head/get_limb_icon(dropped)
+ if(custom_head)
+ return
cut_overlays()
. = ..()
if(dropped) //certain overlays only appear when the limb is being detached from its owner.
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index d33d0167..37b6ecc1 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -18,8 +18,6 @@
return FALSE
-
-
/mob/proc/has_left_hand(check_disabled = TRUE)
return TRUE
@@ -335,3 +333,19 @@
else
S.adjusted = ALT_STYLE
H.update_inv_wear_suit()
+
+/mob/living/carbon/proc/get_body_parts_flags()
+ for(var/X in bodyparts)
+ var/obj/item/bodypart/L = X
+ switch(L.body_part)
+ if(CHEST)
+ . |= GROIN
+ if(LEG_LEFT)
+ . |= FOOT_LEFT
+ if(LEG_RIGHT)
+ . |= FOOT_RIGHT
+ if(ARM_LEFT)
+ . |= HAND_LEFT
+ if(ARM_RIGHT)
+ . |= HAND_RIGHT
+ . |= L.body_part
\ No newline at end of file
diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm
index 1e235f7f..aaa78d27 100644
--- a/code/modules/vending/autodrobe.dm
+++ b/code/modules/vending/autodrobe.dm
@@ -107,7 +107,9 @@
/obj/item/clothing/under/scratch = 1,
/obj/item/clothing/under/sailor = 1,
/obj/item/clothing/ears/headphones = 2,
- /obj/item/clothing/head/wig/random = 3)
+ /obj/item/clothing/head/wig/random = 3,
+ /obj/item/clothing/suit/ran = 2,
+ /obj/item/clothing/head/ran = 2)
contraband = list(/obj/item/clothing/suit/judgerobe = 1,
/obj/item/clothing/head/powdered_wig = 1,
/obj/item/gun/magic/wand = 2,
diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css
index f907fcfc..c2518bbb 100644
--- a/goon/browserassets/css/browserOutput.css
+++ b/goon/browserassets/css/browserOutput.css
@@ -393,6 +393,7 @@ h1.alert, h2.alert {color: #000000;}
.redtext {color: #FF0000; font-size: 24px;}
.clown {color: #FF69Bf; font-size: 24px; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;}
.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
+.spooky {color: #FF6100;}
.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;}
@keyframes velvet {
0% { color: #400020; }
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index 9d333444..ec18d6ad 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi
index 876f0631..c5546367 100644
Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index 94bcf443..28bef5da 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi
index cbb1b334..2a0fada5 100644
Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ
diff --git a/icons/obj/halloween_items.dmi b/icons/obj/halloween_items.dmi
index c08ea711..b4f11165 100644
Binary files a/icons/obj/halloween_items.dmi and b/icons/obj/halloween_items.dmi differ
diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm
index eb943521..b70a41e4 100644
--- a/interface/stylesheet.dm
+++ b/interface/stylesheet.dm
@@ -154,6 +154,7 @@ h1.alert, h2.alert {color: #000000;}
.clown {color: #FF69Bf; font-size: 3; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;}
.singing {font-family: "Trebuchet MS", cursive, sans-serif; font-style: italic;}
.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
+.spooky {color: #FF9100;}
.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;}
@keyframes velvet {
0% { color: #400020; }
diff --git a/sound/spookoween/ahaha.ogg b/sound/spookoween/ahaha.ogg
new file mode 100644
index 00000000..2e830ad6
Binary files /dev/null and b/sound/spookoween/ahaha.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 9277ae81..24006262 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1772,6 +1772,9 @@
#include "code\modules\goonchat\jsErrorHandler.dm"
#include "code\modules\holiday\easter.dm"
#include "code\modules\holiday\holidays.dm"
+#include "code\modules\holiday\halloween\bartholomew.dm"
+#include "code\modules\holiday\halloween\halloween.dm"
+#include "code\modules\holiday\halloween\jacqueen.dm"
#include "code\modules\holodeck\area_copy.dm"
#include "code\modules\holodeck\computer.dm"
#include "code\modules\holodeck\holo_effect.dm"