diff --git a/aurorastation.dme b/aurorastation.dme
index 20a689da4a0..4bc3635faad 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -306,6 +306,7 @@
#include "code\datums\looping_sounds\_looping_sound.dm"
#include "code\datums\looping_sounds\machinery_sounds.dm"
#include "code\datums\looping_sounds\thermal_drill.dm"
+#include "code\datums\looping_sounds\revenant_rift.dm"
#include "code\datums\observation\_debug.dm"
#include "code\datums\observation\destroyed.dm"
#include "code\datums\observation\equipped.dm"
@@ -895,6 +896,7 @@
#include "code\game\objects\items\weapons\landmines.dm"
#include "code\game\objects\items\weapons\manuals.dm"
#include "code\game\objects\items\weapons\mop.dm"
+#include "code\game\objects\items\weapons\neutralizer.dm"
#include "code\game\objects\items\weapons\paint.dm"
#include "code\game\objects\items\weapons\paiwire.dm"
#include "code\game\objects\items\weapons\policetape.dm"
diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm
index df03ed65000..32350fcba62 100644
--- a/code/__defines/species_languages.dm
+++ b/code/__defines/species_languages.dm
@@ -59,6 +59,7 @@
#define LANGUAGE_CULT "Cult" // NOT CULTISTS!
#define LANGUAGE_OCCULT "Occult"
#define LANGUAGE_REVENANT "Revenant"
+#define LANGUAGE_REVENANT_RIFTSPEAK "Riftspeak"
#define LANGUAGE_TERMINATOR "Hephaestus Darkcomms" // HKs.
// Lesser-form Languages
diff --git a/code/controllers/subsystems/statistics.dm b/code/controllers/subsystems/statistics.dm
index 222d2235cfc..7a372a17955 100644
--- a/code/controllers/subsystems/statistics.dm
+++ b/code/controllers/subsystems/statistics.dm
@@ -53,7 +53,7 @@
if(!isobserver(C.mob) && !C.holder)
if(C.is_afk(inactivity_threshold))
log_access("AFK: [key_name(C)]")
- to_chat(C, SPAN_WARNING("You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected."))
+ to_chat_immediate(C, SPAN_WARNING("You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected."))
qdel(C)
kicked_clients++
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 36741ff7a79..a68e2aa461d 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -90,7 +90,7 @@
return ..()
/datum/beam/proc/Draw()
- var/Angle = round(Get_Angle(origin.x ? origin : get_turf(origin), target.x ? target : get_turf(target)))
+ var/Angle = round(Get_Angle(origin.z ? origin : get_turf(origin), target.z ? target : get_turf(target)))
var/matrix/rot_matrix = matrix()
rot_matrix.Turn(Angle)
@@ -183,6 +183,13 @@
var/obj/effect/ebeam/B = beam
B.color = COLOR_GRAY40
+// this simplified datum will work with timed beams that are held
+/datum/beam/held/get_x_translation_vector()
+ return (world.icon_size * target_oldloc.x) - (world.icon_size * origin_oldloc.x)
+
+/datum/beam/held/get_y_translation_vector()
+ return (world.icon_size * target_oldloc.y) - (world.icon_size * origin_oldloc.y)
+
/obj/effect/ebeam
mouse_opacity = 0
anchored = 1
@@ -197,10 +204,10 @@
owner = null
return ..()
-/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time = 3)
+/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time = 3, beam_datum_type=/datum/beam)
if(time >= INFINITY)
crash_with("Tried to create beam with infinite time!")
return null
- var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time)
+ var/datum/beam/newbeam = new beam_datum_type(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time)
INVOKE_ASYNC(newbeam, /datum/beam/.proc/Start)
- return newbeam
+ return newbeam
\ No newline at end of file
diff --git a/code/datums/looping_sounds/revenant_rift.dm b/code/datums/looping_sounds/revenant_rift.dm
new file mode 100644
index 00000000000..98458f8d222
--- /dev/null
+++ b/code/datums/looping_sounds/revenant_rift.dm
@@ -0,0 +1,7 @@
+/datum/looping_sound/revenant_rift
+ start_sound = list('sound/effects/psi/power_feedback.ogg' = 1)
+ start_length = 8
+ mid_sounds = list('sound/ambience/tension/horror.ogg' = 1)
+ mid_length = 260
+ end_sound = list('sound/effects/psi/power_feedback.ogg' = 1)
+ volume = 80
\ No newline at end of file
diff --git a/code/game/antagonist/outsider/revenant.dm b/code/game/antagonist/outsider/revenant.dm
index 8391cb89e92..6d1e35e931e 100644
--- a/code/game/antagonist/outsider/revenant.dm
+++ b/code/game/antagonist/outsider/revenant.dm
@@ -12,7 +12,22 @@ var/datum/antagonist/revenant/revenants = null
hard_cap = 12
hard_cap_round = 12
+ var/rifts_left = 3
+ var/kill_count = 0
+ var/obj/effect/portal/revenant/revenant_rift
+
/datum/antagonist/revenant/New()
..()
- revenants = src
\ No newline at end of file
+ revenants = src
+
+/datum/antagonist/revenant/proc/destroyed_rift()
+ revenants.revenant_rift = null
+ revenants.rifts_left--
+ if(revenants.rifts_left <= 0)
+ command_announcement.Announce("Aurora, we aren't detecting any more rift energy signatures. Mop up the rest of the invaders. Good work.", "Bluespace Breach Alert")
+
+/proc/message_all_revenants(var/message)
+ for(var/thing in human_mob_list)
+ if(isrevenant(thing))
+ to_chat(thing, message)
\ No newline at end of file
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index 9a7760bf224..866dec788a9 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -40,6 +40,13 @@
if(does_teleport)
teleport(AM)
+/obj/effect/portal/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/bluespace_neutralizer))
+ user.visible_message("[user] collapses \the [src] with \the [I].", SPAN_NOTICE("You collapse \the [src] with \the [I]."))
+ qdel(src)
+ return
+ return ..()
+
/obj/effect/portal/attack_hand(mob/user)
set waitfor = FALSE
@@ -92,7 +99,7 @@
if(next_spawn < world.time)
visible_message(SPAN_WARNING("\The [src] spits something out!"))
for(var/thing in spawn_things)
- var/spawn_path = text2path(thing)
+ var/spawn_path = thing
var/atom/spawned_thing = new spawn_path(get_turf(src))
if(istype(spawned_thing, /obj/item/stack))
@@ -113,33 +120,137 @@
/obj/effect/portal/spawner/metal
num_of_spawns = 3
spawn_things = list(
- "/obj/item/stack/material/steel" = 10,
- "/obj/item/stack/material/plasteel" = 5
- )
+ /obj/item/stack/material/steel = 10,
+ /obj/item/stack/material/plasteel = 5
+ )
/obj/effect/portal/spawner/rare_metal
num_of_spawns = 4
spawn_things = list(
- "/obj/item/stack/material/gold" = 2,
- "/obj/item/stack/material/silver" = 2,
- "/obj/item/stack/material/uranium" = 2,
- "/obj/item/stack/material/diamond" = 1
- )
+ /obj/item/stack/material/gold = 2,
+ /obj/item/stack/material/silver = 2,
+ /obj/item/stack/material/uranium = 2,
+ /obj/item/stack/material/diamond = 1
+ )
+
+/obj/effect/portal/spawner/silver
+ num_of_spawns = 3
+ spawn_things = list(
+ /obj/item/stack/material/silver = 3
+ )
+
+/obj/effect/portal/spawner/gold
+ num_of_spawns = 3
+ spawn_things = list(
+ /obj/item/stack/material/gold = 3
+ )
/obj/effect/portal/spawner/phoron
num_of_spawns = 3
spawn_things = list(
- "/obj/item/stack/material/phoron" = 3
- )
+ /obj/item/stack/material/phoron = 3
+ )
/obj/effect/portal/spawner/monkey_cube
num_of_spawns = 1
spawn_things = list(
- "/obj/item/reagent_containers/food/snacks/monkeycube" = 4
- )
+ /obj/item/reagent_containers/food/snacks/monkeycube = 4
+ )
/obj/effect/portal/spawner/cow // debug but funny so im keeping it
num_of_spawns = 1
spawn_things = list(
- "/mob/living/simple_animal/cow" = 1
+ /mob/living/simple_animal/cow = 1
)
+
+#define COLOR_STAGE_FIVE "#163DFF"
+#define COLOR_STAGE_FOUR "#0099ff"
+#define COLOR_STAGE_THREE "#33eb33"
+#define COLOR_STAGE_TWO "#F0FF2B"
+#define COLOR_STAGE_ONE "#FF8D23"
+#define COLOR_STAGE_ZERO "#FF0000"
+
+/obj/effect/portal/revenant
+ name = "bluespace rift"
+ desc = "A bluespace tear in space, reaching directly to another point within this region. This one looks like a one-way portal to here, don't come too close."
+ desc_info = "This is a bluespace rift. It is a node wherein revenants can seep into this locale. To destroy it, you must bring a bluespace neutralizer near it."
+ icon_state = "portal_g"
+
+ does_teleport = FALSE
+ has_lifespan = FALSE
+
+ color = COLOR_STAGE_FIVE
+ light_color = COLOR_STAGE_FIVE
+ light_power = 6
+ light_range = 8
+
+ var/last_color_level = 5
+ var/health_timer = 10 MINUTES // you need to reduce the health by standing near it with a neutralizer
+ var/datum/looping_sound/revenant_rift/soundloop
+
+/obj/effect/portal/revenant/Initialize(mapload)
+ . = ..()
+ if(revenants.revenant_rift)
+ return INITIALIZE_HINT_QDEL
+ var/turf/T = get_turf(src)
+ log_and_message_admins("Revenant Bluespace Rift spawned at \the [get_area(T)]", null, T)
+ revenants.revenant_rift = src
+ soundloop = new(list(src), FALSE)
+ soundloop.start()
+
+/obj/effect/portal/revenant/Destroy()
+ revenants.destroyed_rift()
+ visible_message(FONT_LARGE(SPAN_DANGER("\The [src] collapses!")))
+ new /obj/random/highvalue/no_crystal(src)
+ new /obj/random/highvalue/no_crystal(src)
+ for(var/thing in contents)
+ var/obj/O = thing
+ O.forceMove(get_turf(src))
+ O.throw_at_random(FALSE, 3, THROWNOBJ_KNOCKBACK_SPEED)
+ var/area/A = get_area(src)
+ message_all_revenants(FONT_LARGE(SPAN_WARNING("The rift keeping us here has been destroyed in [A.name]!")))
+ QDEL_NULL(soundloop)
+ return ..()
+
+/obj/effect/portal/revenant/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/bluespace_neutralizer))
+ to_chat(user, SPAN_WARNING("You need to activate \the [I] and keep it near \the [src] to collapse it."))
+ return
+ return ..()
+
+/obj/effect/portal/revenant/proc/reduce_health(var/amount = 1)
+ health_timer -= amount
+ if(health_timer <= 0)
+ qdel(src)
+ return
+ update_icon()
+
+/obj/effect/portal/revenant/update_icon()
+ var/color_level = round((health_timer / 600) / 2) // this should give a value from 0 - 5
+ if(color_level == last_color_level)
+ return
+ var/area/A = get_area(src)
+ message_all_revenants(FONT_LARGE(SPAN_WARNING("The rift keeping us here is being attacked in [A.name]!")))
+ last_color_level = color_level
+ switch(color_level)
+ if(0)
+ color = COLOR_STAGE_ZERO
+ if(1)
+ color = COLOR_STAGE_ONE
+ if(2)
+ color = COLOR_STAGE_TWO
+ if(3)
+ color = COLOR_STAGE_THREE
+ if(4)
+ color = COLOR_STAGE_FOUR
+ if(5)
+ color = COLOR_STAGE_FIVE
+ light_color = color
+ update_light()
+
+#undef COLOR_STAGE_FIVE
+#undef COLOR_STAGE_FOUR
+#undef COLOR_STAGE_THREE
+#undef COLOR_STAGE_TWO
+#undef COLOR_STAGE_ONE
+#undef COLOR_STAGE_ZERO
\ No newline at end of file
diff --git a/code/game/objects/items/devices/hearing_aid.dm b/code/game/objects/items/devices/hearing_aid.dm
index 0d879cfa0bc..6b6f1364331 100644
--- a/code/game/objects/items/devices/hearing_aid.dm
+++ b/code/game/objects/items/devices/hearing_aid.dm
@@ -1,6 +1,6 @@
/obj/item/device/hearing_aid
name = "hearing aid"
- desc = "A device of Skrellian design. It allows the naturally deaf to hear, to an extent."
+ desc = "A device that allows the naturally deaf to hear, to an extent."
icon = 'icons/obj/hearing_aid.dmi'
icon_state = "hearing_aid"
item_state = "hearing_aid"
@@ -8,7 +8,19 @@
slot_flags = SLOT_EARS
w_class = ITEMSIZE_SMALL
-/obj/item/device/hearing_aid/human
- desc = "A device that allows the naturally deaf to hear, to an extent."
- icon_state = "hearing_aid_human"
- item_state = "hearing_aid_human"
\ No newline at end of file
+/obj/item/device/hearing_aid/black
+ icon_state = "hearing_aid-b"
+ item_state = "hearing_aid-b"
+
+/obj/item/device/hearing_aid/silver
+ icon_state = "hearing_aid-s"
+ item_state = "hearing_aid-s"
+
+/obj/item/device/hearing_aid/white
+ icon_state = "hearing_aid-w"
+ item_state = "hearing_aid-w"
+
+/obj/item/device/hearing_aid/skrell
+ desc = "A device that allows the naturally deaf to hear, to an extent. It seems to be Skrellian in design."
+ icon_state = "hearing_aid_skrell"
+ item_state = "hearing_aid_skrell"
diff --git a/code/game/objects/items/weapons/neutralizer.dm b/code/game/objects/items/weapons/neutralizer.dm
new file mode 100644
index 00000000000..6231f91709f
--- /dev/null
+++ b/code/game/objects/items/weapons/neutralizer.dm
@@ -0,0 +1,55 @@
+/obj/item/bluespace_neutralizer
+ name = "bluespace neutralizer"
+ desc = "A strange device, supposedly capable of pre-emptively shutting down bluespace portals."
+ desc_info = "Click on it, or use it in-hand to activate it. Click on any portal-like structure to instantly close it. Stand near a bluespace rift while it's active to start the closing process."
+ icon = 'icons/obj/contained_items/tools/neutralizer.dmi'
+ icon_state = "neutralizer"
+ contained_sprite = TRUE
+
+ var/tethered = FALSE // it needs to tether with the portal before it can start zapping
+ var/last_zap = 0
+ var/active = FALSE
+
+/obj/item/bluespace_neutralizer/update_icon()
+ icon_state = "neutralizer[active ? "-a" : ""]"
+
+/obj/item/bluespace_neutralizer/attack_self(mob/user)
+ if(!active && !revenants.revenant_rift)
+ to_chat(user, SPAN_WARNING("\The [src] doesn't detect any large bluespace rifts in the region."))
+ return
+ toggle(user)
+
+/obj/item/bluespace_neutralizer/proc/toggle(var/mob/user)
+ active = !active
+ if(active)
+ START_PROCESSING(SSprocessing, src)
+ else
+ STOP_PROCESSING(SSprocessing, src)
+ if(user)
+ to_chat(user, SPAN_NOTICE("You turn \the [src] [active ? "on" : "off"]."))
+ update_icon()
+
+/obj/item/bluespace_neutralizer/process()
+ if(active && !revenants.revenant_rift)
+ toggle()
+ return
+ var/turf/our_turf = get_turf(src)
+ if(!(our_turf.z == revenants.revenant_rift.z && get_dist(src, revenants.revenant_rift) < 5 && isInSight(src, revenants.revenant_rift)))
+ tethered = FALSE
+ return
+ if(!tethered)
+ visible_message(SPAN_DANGER("\The [src] tethers with \the [revenants.revenant_rift]!"))
+ last_zap = world.time
+ tethered = TRUE
+ Beam(revenants.revenant_rift, icon_state="n_beam", icon = 'icons/effects/beam.dmi', time=2, maxdistance=5, beam_datum_type=/datum/beam/held)
+ playsound(get_turf(src), 'sound/magic/Charge.ogg', 70, TRUE, extrarange = 30)
+ return
+ revenants.revenant_rift.reduce_health(world.time - last_zap)
+ last_zap = world.time
+ Beam(revenants.revenant_rift, icon_state="lightning[rand(1,12)]", icon = 'icons/effects/effects.dmi', time=2, maxdistance=5, beam_datum_type=/datum/beam/held)
+ playsound(get_turf(src), 'sound/magic/LightningShock.ogg', 50, TRUE, extrarange = 30)
+
+/obj/item/bluespace_neutralizer/Destroy()
+ if(active)
+ STOP_PROCESSING(SSprocessing, src)
+ return ..()
\ No newline at end of file
diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm
index da41c535b69..ded0cdca330 100644
--- a/code/game/objects/random/random.dm
+++ b/code/game/objects/random/random.dm
@@ -1108,6 +1108,21 @@
/obj/item/anomaly_core = 0.5
)
+/obj/random/highvalue/no_crystal
+ problist = list(
+ /obj/item/clothing/suit/armor/reactive = 0.5,
+ /obj/item/clothing/glasses/thermal = 0.5,
+ /obj/item/gun/projectile/automatic/rifle/shotgun = 0.5,
+ /obj/random/sword = 0.5,
+ /obj/item/gun/energy/lawgiver = 0.5,
+ /obj/item/melee/energy/axe = 0.5,
+ /obj/item/gun/projectile/automatic/terminator = 0.5,
+ /obj/item/rig/military = 0.5,
+ /obj/item/rig/unathi/fancy = 0.5,
+ /obj/item/rig/vaurca/minimal = 0.5,
+ /obj/item/anomaly_core = 0.5
+ )
+
/obj/random/junk
name = "random trash"
desc = "This is toss."
@@ -1731,3 +1746,35 @@
/obj/item/pizzabox/meat,
/obj/item/pizzabox/pineapple
)
+
+/obj/random/seed
+ name = "random seed"
+ desc = "This is a random normal seed."
+ icon = 'icons/obj/seeds.dmi'
+ icon_state = "random"
+ spawnlist = list(
+ /obj/item/seeds/limeseed,
+ /obj/item/seeds/lemonseed,
+ /obj/item/seeds/orangeseed,
+ /obj/item/seeds/grapeseed,
+ /obj/item/seeds/berryseed,
+ /obj/item/seeds/appleseed,
+ /obj/item/seeds/bananaseed,
+ /obj/item/seeds/watermelonseed,
+ /obj/item/seeds/pumpkinseed,
+ /obj/item/seeds/wheatseed,
+ /obj/item/seeds/cornseed,
+ /obj/item/seeds/riceseed,
+ /obj/item/seeds/sugarcaneseed,
+ /obj/item/seeds/carrotseed,
+ /obj/item/seeds/garlicseed,
+ /obj/item/seeds/onionseed,
+ /obj/item/seeds/potatoseed,
+ /obj/item/seeds/whitebeetseed,
+ /obj/item/seeds/tomatoseed,
+ /obj/item/seeds/chiliseed,
+ /obj/item/seeds/eggplantseed,
+ /obj/item/seeds/peanutseed,
+ /obj/item/seeds/soyaseed,
+ /obj/item/seeds/cabbageseed
+ )
\ No newline at end of file
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 683ff801313..9fdfc2e0ae4 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -327,9 +327,9 @@
return
var/reason = sanitize(input("Please enter reason"))
if(!reason)
- to_chat(M, "You have been kicked from the server")
+ to_chat_immediate(M, "You have been kicked from the server")
else
- to_chat(M, "You have been kicked from the server: [reason]")
+ to_chat_immediate(M, "You have been kicked from the server: [reason]")
log_admin("[key_name(usr)] booted [key_name(M)].",admin_key=key_name(usr),ckey=key_name(M))
message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1)
//M.client = null
@@ -367,15 +367,15 @@
notes_add(M.ckey,"[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.",usr)
else
notes_add_sql(M.ckey, "[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.", usr, M.lastKnownIP, M.computer_id)
- to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].")
- to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.")
+ to_chat_immediate(M, "You have been banned by [usr.client.ckey].\nReason: [reason].")
+ to_chat_immediate(M, "This is a temporary ban, it will be removed in [mins] minutes.")
feedback_inc("ban_tmp",1)
DB_ban_record(BANTYPE_TEMP, M, mins, reason)
feedback_inc("ban_tmp_mins",mins)
if(config.banappeals)
- to_chat(M, "To try to resolve this matter head to [config.banappeals]")
+ to_chat_immediate(M, "To try to resolve this matter head to [config.banappeals]")
else
- to_chat(M, "No ban appeals URL has been set.")
+ to_chat_immediate(M, "No ban appeals URL has been set.")
log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.",admin_key=key_name(usr),ckey=key_name(M))
message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")
diff --git a/code/modules/admin/verbs/warning.dm b/code/modules/admin/verbs/warning.dm
index dde65366889..965450eb349 100644
--- a/code/modules/admin/verbs/warning.dm
+++ b/code/modules/admin/verbs/warning.dm
@@ -79,7 +79,7 @@
ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.")
if(C)
message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban.")
- to_chat(C, "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.")
+ to_chat_immediate(C, "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.")
qdel(C)
else
message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban.")
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index 34f6b679158..2e176a842d1 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -444,6 +444,10 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
var/list/valid_hairstyles = list()
for(var/hairstyle in hair_styles_list)
var/datum/sprite_accessory/S = hair_styles_list[hairstyle]
+ if(pref.gender == MALE && S.gender == FEMALE)
+ continue
+ if(pref.gender == FEMALE && S.gender == MALE)
+ continue
if(!(mob_species.type in S.species_allowed))
continue
@@ -555,7 +559,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
pref.body_markings[new_marking] = "#000000" //New markings start black
return TOPIC_REFRESH_UPDATE_PREVIEW
-
+
else if(href_list["marking_up"])
var/M = href_list["marking_up"]
var/start = pref.body_markings.Find(M)
diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
index 57cebe09e31..0f3a4e5adee 100644
--- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
@@ -73,27 +73,22 @@
gear_tweaks += new/datum/gear_tweak/path(holsters)
/datum/gear/accessory/tie
- display_name = "tie selection"
- path = /obj/item/clothing/accessory/blue
+ display_name = "tie selection (colourable)"
+ path = /obj/item/clothing/accessory/tie/colourable
+ flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION | GEAR_HAS_COLOR_SELECTION
/datum/gear/accessory/tie/New()
..()
var/ties = list()
- ties["red tie"] = /obj/item/clothing/accessory/red
- ties["red tie with a clip"] = /obj/item/clothing/accessory/tie/red_clip
- ties["orange tie"] = /obj/item/clothing/accessory/tie/orange
- ties["yellow tie"] = /obj/item/clothing/accessory/tie/yellow
- ties["horrible tie"] = /obj/item/clothing/accessory/horrible
- ties["green tie"] = /obj/item/clothing/accessory/tie/green
- ties["dark green tie"] = /obj/item/clothing/accessory/tie/darkgreen
- ties["blue tie"] = /obj/item/clothing/accessory/blue
- ties["blue tie with a clip"] = /obj/item/clothing/accessory/tie/blue_clip
- ties["navy tie"] = /obj/item/clothing/accessory/tie/navy
- ties["purple tie"] = /obj/item/clothing/accessory/tie/purple
- ties["black tie"] = /obj/item/clothing/accessory/tie/black
- ties["white tie"] = /obj/item/clothing/accessory/tie/white
+ ties["tie"] = /obj/item/clothing/accessory/tie/colourable
+ ties["tie, gold clip"] = /obj/item/clothing/accessory/tie/colourable/clip
+ ties["tie, silver clip"] = /obj/item/clothing/accessory/tie/colourable/clip/silver
gear_tweaks += new/datum/gear_tweak/path(ties)
+/datum/gear/accessory/horrible_tie
+ display_name = "horrible tie"
+ path = /obj/item/clothing/accessory/horrible
+
/datum/gear/accessory/bowtie
display_name = "bowtie"
path = /obj/item/clothing/accessory/tie/bowtie
diff --git a/code/modules/client/preference_setup/loadout/loadout_ears.dm b/code/modules/client/preference_setup/loadout/loadout_ears.dm
index b5e03e15c0b..eedcdf8f1cf 100644
--- a/code/modules/client/preference_setup/loadout/loadout_ears.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_ears.dm
@@ -23,7 +23,7 @@
/datum/gear/ears/circuitry
display_name = "earwear, circuitry (empty)"
path = /obj/item/clothing/ears/circuitry
-
+
/datum/gear/ears/earrings
display_name = "earring selection"
description = "A selection of eye-catching earrings."
@@ -36,3 +36,18 @@
earrings["stud earrings"] = /obj/item/clothing/ears/earring/stud
earrings["dangle earrings"] = /obj/item/clothing/ears/earring/dangle
gear_tweaks += new/datum/gear_tweak/path(earrings)
+
+/datum/gear/ears/hearing_aid
+ display_name = "hearing aid selection"
+ path = /obj/item/device/hearing_aid
+ cost = 1
+
+/datum/gear/ears/hearing_aid/New()
+ ..()
+ var/hearingaids = list()
+ hearingaids["hearing aid, black"] = /obj/item/device/hearing_aid/black
+ hearingaids["hearing aid, grey"] = /obj/item/device/hearing_aid
+ hearingaids["hearing aid, silver"] = /obj/item/device/hearing_aid/silver
+ hearingaids["hearing aid, white"] = /obj/item/device/hearing_aid/white
+ hearingaids["hearing aid, skrellian"] = /obj/item/device/hearing_aid/skrell
+ gear_tweaks += new/datum/gear_tweak/path(hearingaids)
diff --git a/code/modules/client/preference_setup/loadout/loadout_utility.dm b/code/modules/client/preference_setup/loadout/loadout_utility.dm
index 8ab7cc9796b..ce89460ef7b 100644
--- a/code/modules/client/preference_setup/loadout/loadout_utility.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_utility.dm
@@ -50,19 +50,6 @@
fountainpens["silver fountain pen"] = /obj/item/pen/fountain/silver
fountainpens["white fountain pen"] = /obj/item/pen/fountain/white
gear_tweaks += new/datum/gear_tweak/path(fountainpens)
-
-/datum/gear/utility/hearing_aid
- display_name = "hearing aid selection"
- path = /obj/item/device/hearing_aid
- cost = 1
-
-/datum/gear/utility/hearing_aid/New()
- ..()
- var/hearingaids = list()
- hearingaids["hearing aid, skrell design"] = /obj/item/device/hearing_aid
- hearingaids["hearing aid, human design"] = /obj/item/device/hearing_aid/human
- gear_tweaks += new/datum/gear_tweak/path(hearingaids)
-
/datum/gear/utility/paicard
display_name = "personal AI device"
path = /obj/item/device/paicard
diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm
index 7e8e7cf2e20..67108b8c637 100644
--- a/code/modules/clothing/under/accessories/accessory.dm
+++ b/code/modules/clothing/under/accessories/accessory.dm
@@ -18,37 +18,50 @@
on_removed()
return ..()
-/obj/item/clothing/accessory/proc/get_inv_overlay()
- if(!inv_overlay)
- if(!mob_overlay)
- get_mob_overlay()
+/obj/item/clothing/accessory/Initialize()
+ . = ..()
+ update_icon()
+/obj/item/clothing/accessory/update_icon()
+ . = ..()
+ if(build_from_parts)
+ cut_overlays()
+ add_overlay(overlay_image(icon, "[icon_state]_[worn_overlay]", flags=RESET_COLOR)) //add the overlay w/o coloration of the original sprite
+
+/obj/item/clothing/accessory/proc/get_inv_overlay()
+ if(!mob_overlay)
+ get_mob_overlay()
+ var/I = mob_overlay.icon
+ if(!inv_overlay)
var/tmp_icon_state = "[overlay_state? "[overlay_state]" : "[icon_state]"]"
if(icon_override)
if("[tmp_icon_state]_tie" in icon_states(icon_override))
tmp_icon_state = "[tmp_icon_state]_tie"
- inv_overlay = image(icon = mob_overlay.icon, icon_state = tmp_icon_state, dir = SOUTH)
- if(contained_sprite)
- tmp_icon_state = "[tmp_icon_state]"
- inv_overlay = image("icon" = icon, "icon_state" = "[tmp_icon_state]_w", dir = SOUTH)
+ else if(contained_sprite)
+ tmp_icon_state = "[tmp_icon_state]_w"
+ inv_overlay = image(icon = I, icon_state = tmp_icon_state, dir = SOUTH)
if(color)
inv_overlay.color = color
+ if(build_from_parts)
+ inv_overlay.cut_overlays()
+ inv_overlay.add_overlay(overlay_image(I, "[icon_state]_[worn_overlay]", flags=RESET_COLOR)) //add the overlay w/o coloration of the original sprite
return inv_overlay
/obj/item/clothing/accessory/proc/get_mob_overlay()
+ var/I = icon_override ? icon_override : contained_sprite ? icon : INV_ACCESSORIES_DEF_ICON
if(!mob_overlay)
var/tmp_icon_state = "[overlay_state? "[overlay_state]" : "[icon_state]"]"
if(icon_override)
- if("[tmp_icon_state]_mob" in icon_states(icon_override))
+ if("[tmp_icon_state]_mob" in icon_states(I))
tmp_icon_state = "[tmp_icon_state]_mob"
- mob_overlay = image("icon" = icon_override, "icon_state" = "[tmp_icon_state]")
else if(contained_sprite)
tmp_icon_state = "[src.item_state][WORN_UNDER]"
- mob_overlay = image("icon" = icon, "icon_state" = "[tmp_icon_state]")
- else
- mob_overlay = image("icon" = INV_ACCESSORIES_DEF_ICON, "icon_state" = "[tmp_icon_state]")
+ mob_overlay = image("icon" = I, "icon_state" = "[tmp_icon_state]")
if(color)
mob_overlay.color = color
+ if(build_from_parts)
+ mob_overlay.cut_overlays()
+ mob_overlay.add_overlay(overlay_image(I, "[icon_state]_[worn_overlay]", flags=RESET_COLOR)) //add the overlay w/o coloration of the original sprite
mob_overlay.appearance_flags = RESET_ALPHA
return mob_overlay
@@ -59,7 +72,6 @@
has_suit = S
loc = has_suit
has_suit.add_overlay(get_inv_overlay())
-
if(user)
to_chat(user, "You attach \the [src] to \the [has_suit].")
src.add_fingerprint(user)
@@ -164,11 +176,23 @@
name = "white tie"
icon_state = "whitetie"
+/obj/item/clothing/accessory/tie/colourable
+ name = "tie"
+ icon_state = "whitetie"
+
+/obj/item/clothing/accessory/tie/colourable/clip
+ name = "tie with a gold clip"
+ build_from_parts = TRUE
+ worn_overlay = "clip"
+
+/obj/item/clothing/accessory/tie/colourable/clip/silver
+ name = "tie with a silver clip"
+ worn_overlay = "sclip"
+
/obj/item/clothing/accessory/tie/bowtie
name = "bowtie"
desc = "Snazzy!"
icon_state = "bowtie"
-
/obj/item/clothing/accessory/stethoscope
name = "stethoscope"
desc = "An outdated medical apparatus for listening to the sounds of the human body. It also makes you look like you know what you're doing."
diff --git a/code/modules/ghostroles/spawner/antagonist/revenant.dm b/code/modules/ghostroles/spawner/antagonist/revenant.dm
index f3b7f7b9531..9e7e76bb279 100644
--- a/code/modules/ghostroles/spawner/antagonist/revenant.dm
+++ b/code/modules/ghostroles/spawner/antagonist/revenant.dm
@@ -17,6 +17,13 @@
var/spawn_index = 1 // used to add a numerical identifier to the revenant. increases by 1 per spawn
var/has_fired = FALSE
+ var/first_rift_done = FALSE
+
+/datum/ghostspawner/revenant/cant_spawn(mob/user)
+ . = ..()
+ if(!. && revenants.rifts_left <= 0)
+ return "The final rift has been closed."
+
/datum/ghostspawner/revenant/spawn_mob(mob/user)
var/turf/T = select_spawnlocation()
var/mob/living/carbon/human/revenant/R
@@ -34,8 +41,11 @@
R.ckey = user.ckey
revenants.add_antagonist(R.mind, TRUE, TRUE, FALSE, TRUE, TRUE)
+ if(R.client)
+ to_chat(R, FONT_LARGE(SPAN_CULT("You can now speak with all revenants in the game world by using \"[R.client.prefs.language_prefixes[1]]rs\" before a message.")))
if(!has_fired)
INVOKE_ASYNC(src, .proc/play_ambience, R)
+ INVOKE_ASYNC(src, .proc/check_rift)
return R
@@ -46,4 +56,30 @@
continue
M.playsound_simple(get_turf(M), 'sound/ambience/tension/tension.ogg', 75, FALSE)
to_chat(M, FONT_LARGE(SPAN_CULT("A faint hum coming from the station walls fills your ears...")))
- has_fired = TRUE
\ No newline at end of file
+ has_fired = TRUE
+
+/datum/ghostspawner/revenant/proc/check_rift()
+ if(revenants.revenant_rift || revenants.rifts_left <= 0)
+ return
+ var/kills_needed = ((initial(revenants.rifts_left) - revenants.rifts_left) + 1) * 5
+ if(revenants.kill_count > kills_needed)
+ var/turf/rift_turf
+ var/list/possible_landmarks = list()
+ for(var/thing in landmarks_list)
+ var/obj/effect/landmark/landmark = thing
+ if(landmark.name == "RevenantRift")
+ possible_landmarks += landmark
+ if(length(possible_landmarks))
+ var/obj/effect/landmark/L = pick(possible_landmarks)
+ rift_turf = get_turf(L)
+ if(rift_turf)
+ new /obj/effect/portal/revenant(rift_turf)
+ if(!first_rift_done)
+ command_announcement.Announce("Aurora, we're detecting energy signatures eerily similar to a bluespace rift breach inside your hull. [SScargo.shuttle ? "We're sending you a bluespace neutralizer via the cargo shuttle. " : ""]Locate the rift and shut it down.", "Bluespace Breach Alert")
+ first_rift_done = TRUE
+ if(SScargo.shuttle)
+ var/turf/T = pick_area_turf(pick(SScargo.shuttle.shuttle_area), list(/proc/not_turf_contains_dense_objects))
+ if(T)
+ var/obj/structure/closet/crate/C = new /obj/structure/closet/crate(T)
+ C.name += " (Neutralizer)"
+ new /obj/item/bluespace_neutralizer(C)
\ No newline at end of file
diff --git a/code/modules/ghostroles/spawner/human/responseteams/mercenary.dm b/code/modules/ghostroles/spawner/human/responseteams/mercenary.dm
index 6f6f38c0c0f..673781d3956 100644
--- a/code/modules/ghostroles/spawner/human/responseteams/mercenary.dm
+++ b/code/modules/ghostroles/spawner/human/responseteams/mercenary.dm
@@ -3,7 +3,7 @@
short_name = "mercr"
max_count = 2
desc = "Rank and file of a freelancer mercenary team."
- welcome_message = "You're part of a freelancing mercenary team who just picked up a distress beacon coming from the Aurora. You have no affiliation to anyone, but you sure do want a quick buck."
+ welcome_message = "Your crew has been hired by Nanotrasen to sort out a distress signal. While you wouldn't do anything violent to the crew, maybe they won't miss that one fancy artifact..."
outfit = /datum/outfit/admin/ert/mercenary
possible_species = list(SPECIES_HUMAN, SPECIES_HUMAN_OFFWORLD, SPECIES_TAJARA, SPECIES_TAJARA_MSAI, SPECIES_TAJARA_ZHAN, SPECIES_SKRELL, SPECIES_DIONA, SPECIES_UNATHI, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_WORKER, SPECIES_IPC, SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_ZENGHU, SPECIES_IPC_BISHOP, SPECIES_IPC_SHELL)
diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm
index ff89bef271b..d93b9a29ea1 100644
--- a/code/modules/holodeck/HolodeckObjects.dm
+++ b/code/modules/holodeck/HolodeckObjects.dm
@@ -424,9 +424,9 @@
/mob/living/simple_animal/hostile/carp/holodeck
icon = 'icons/mob/AI.dmi'
- icon_state = "holo4"
- icon_living = "holo4"
- icon_dead = "holo4"
+ icon_state = "carp"
+ icon_living = "carp"
+ icon_dead = "carp"
alpha = 127
icon_gib = null
meat_amount = 0
diff --git a/code/modules/mob/abstract/new_player/sprite_accessories.dm b/code/modules/mob/abstract/new_player/sprite_accessories.dm
index 40781255cf9..5b3bca9f191 100644
--- a/code/modules/mob/abstract/new_player/sprite_accessories.dm
+++ b/code/modules/mob/abstract/new_player/sprite_accessories.dm
@@ -68,7 +68,6 @@ Follow by example and make good judgement based on length which list to include
bald
name = "Bald" // try to capitalize the names please~
icon_state = "bald" // you do not need to define _s or _l sub-states, game automatically does this for you
- gender = MALE
species_allowed = list(/datum/species/human,/datum/species/human/offworlder,/datum/species/machine/shell,/datum/species/machine/shell/rogue,/datum/species/zombie,/datum/species/unathi,/datum/species/zombie/unathi)
length = 0
chatname = "bald head" //aim to keep these lowercase so they fit into the hair tugging message
@@ -106,7 +105,6 @@ Follow by example and make good judgement based on length which list to include
afro3
name = "Afro, Big"
icon_state = "hair_afrobig"
- gender = MALE
length = 4
chatname = "big afro"
@@ -130,41 +128,35 @@ Follow by example and make good judgement based on length which list to include
amazon
name = "Amazon"
icon_state = "hair_amazon"
- gender = FEMALE
length = 2
chatname = "long hair"
averagejoe
name = "Average Joe"
icon_state = "hair_averagejoe"
- gender = MALE
chatname = "short hair"
baldingfade
name = "Balding Fade"
icon_state = "hair_baldingfade"
- gender = MALE
length = 0
chatname = "bald head"
baldinghair
name = "Balding Hair"
icon_state = "hair_baldinghair" //hair_e
- gender = MALE // turnoff!
length = 0
chatname = "balding hair"
bangs
name = "Bangs"
icon_state = "hair_bangs"
- gender = FEMALE
length = 2
chatname = "fringe"
bangs_short
name = "Bangs, Short"
icon_state = "hair_bangs_short"
- gender = FEMALE
chatname = "fringe"
bangs_veryshort
@@ -190,28 +182,24 @@ Follow by example and make good judgement based on length which list to include
bedhead4
name = "Bedhead 4"
icon_state = "hair_bedhead4"
- gender = FEMALE
length = 3
chatname = "messy locks"
beehive
name = "Beehive"
icon_state = "hair_beehive"
- gender = FEMALE
length = 2
chatname = "beehive hairdo"
beehive2
name = "Beehive 2"
icon_state = "hair_beehive2"
- gender = FEMALE
length = 2
chatname = "beehive hairdo"
beehive3
name = "Beehive 3"
icon_state = "hair_beehive3"
- gender = FEMALE
length = 2
chatname = "beehive hairdo"
@@ -230,14 +218,12 @@ Follow by example and make good judgement based on length which list to include
bob
name = "Bob"
icon_state = "hair_bob"
- gender = FEMALE
species_allowed = list(/datum/species/human,/datum/species/human/offworlder,/datum/species/machine/shell,/datum/species/machine/shell/rogue,/datum/species/zombie,/datum/species/unathi,/datum/species/zombie/unathi)
chatname = "short hair"
bob_chin
name = "Bob, Chin Length"
icon_state = "hair_bob_chin"
- gender = FEMALE
chatname = "short hair"
bob_kusanagi
@@ -248,66 +234,56 @@ Follow by example and make good judgement based on length which list to include
bob_shoulder
name = "Bob, Shoulder Length"
icon_state = "hair_bob_shoulder"
- gender = FEMALE
chatname = "short hair"
bobcurl
name = "Bobcurl"
icon_state = "hair_bobcurl"
- gender = FEMALE
species_allowed = list(/datum/species/human,/datum/species/human/offworlder,/datum/species/machine/shell,/datum/species/machine/shell/rogue,/datum/species/zombie,/datum/species/unathi,/datum/species/zombie/unathi)
chatname = "curls"
bobcurl2
name = "Bobcurl 2"
icon_state = "hair_bobcurl2"
- gender = FEMALE
chatname = "curls"
bowl
name = "Bowl"
icon_state = "hair_bowlcut"
- gender = MALE
chatname = "bowl cut"
bowlcut2
name = "Bowl 2"
icon_state = "hair_bowlcut2"
- gender = MALE
chatname = "bowl cut"
bowlcut_birdnest
name = "Bowl, Birdnest"
icon_state = "hair_bowlcut_birdnest"
- gender = MALE
length = 4
chatname = "bowl cut"
braid_grande
name = "Braid, Grande"
icon_state = "hair_braid_grande"
- gender = FEMALE
length = 3
chatname = "braid"
braid_medium
name = "Braid, Medium"
icon_state = "hair_braid_medium"
- gender = FEMALE
length = 2
chatname = "braid"
braided
name = "Braided"
icon_state = "hair_braided"
- gender = FEMALE
length = 3
chatname = "braids"
braided_alt
name = "Braided, Alt"
icon_state = "hair_braided_alt"
- gender = FEMALE
length = 3
chatname = "braids"
@@ -320,14 +296,12 @@ Follow by example and make good judgement based on length which list to include
bun
name = "Bun"
icon_state = "hair_bun"
- gender = FEMALE
length = 2
chatname = "hair bun"
bun_casual
name = "Bun, Casual"
icon_state = "hair_bun_casual"
- gender = FEMALE
length = 2
chatname = "hair bun"
@@ -340,7 +314,6 @@ Follow by example and make good judgement based on length which list to include
bun_double
name = "Bun, Double"
icon_state = "hair_bun_double"
- gender = FEMALE
length = 3
chatname = "hair buns"
@@ -353,35 +326,30 @@ Follow by example and make good judgement based on length which list to include
bun_manbun
name = "Bun, Manbun"
icon_state = "hair_bun_manbun"
- gender = MALE
length = 2
chatname = "manbun"
bun_odango
name = "Bun, Odango"
icon_state = "hair_bun_odango"
- gender = FEMALE
length = 2
chatname = "hair buns"
bun_odango2
name = "Bun, Odango 2"
icon_state = "hair_bun_odango2"
- gender = FEMALE
length = 2
chatname = "hair buns"
bun_odango3
name = "Bun, Odango 3"
icon_state = "hair_bun_odango3"
- gender = FEMALE
length = 3
chatname = "hair buns"
bun_overeye
name = "Bun, Overeye"
icon_state = "hair_bun_overeye"
- gender = FEMALE
length = 2
chatname = "hair bun"
@@ -394,72 +362,61 @@ Follow by example and make good judgement based on length which list to include
bun_tight
name = "Bun, Tight"
icon_state = "hair_bun_tight"
- gender = FEMALE
length = 2
chatname = "hair bun"
bun_topknot
name = "Bun, Topknot"
icon_state = "hair_bun_topknot"
- gender = MALE
length = 2
chatname = "hair bun"
buzzcut
name = "Buzzcut"
icon_state = "hair_buzzcut"
- gender = MALE
species_allowed = list(/datum/species/human,/datum/species/human/offworlder,/datum/species/machine/shell,/datum/species/machine/shell/rogue,/datum/species/zombie,/datum/species/unathi,/datum/species/zombie/unathi)
chatname = "unbuzzed hair" //grabbing the grabbable hair
buzzcut2
name = "Buzzcut 2"
icon_state = "hair_buzzcut2"
- gender = MALE
species_allowed = list(/datum/species/human,/datum/species/human/offworlder,/datum/species/machine/shell,/datum/species/machine/shell/rogue,/datum/species/zombie,/datum/species/unathi,/datum/species/zombie/unathi)
chatname = "unbuzzed hair"
chrono
name = "Chrono"
icon_state = "hair_chrono"
- gender = MALE
length = 4
chatname = "spiked hair"
cia
name = "CIA"
icon_state = "hair_cia"
- gender = MALE
chatname = "short hair"
coffeehouse
name = "Coffee House Cut"
icon_state = "hair_coffeehouse"
- gender = MALE
chatname = "coffee house haircut"
coffeehouse_shave
name = "Coffee House Shave"
icon_state = "hair_coffeehouse_shave"
- gender = MALE
chatname = "coffee house haircut"
combover
name = "Combover"
icon_state = "hair_combover"
- gender = MALE
chatname = "groomed hair"
country
name = "Country"
icon_state = "hair_country"
- gender = FEMALE
chatname = "ponytail"
crew
name = "Crewcut"
icon_state = "hair_crewcut"
- gender = MALE
chatname = "short hair"
curls
@@ -486,14 +443,12 @@ Follow by example and make good judgement based on length which list to include
drills_drillruru
name = "Drills, Drillruru"
icon_state = "hair_drills_drillruru"
- gender = FEMALE
length = 2
chatname = "hair drills"
drills_drillruru_long
name = "Drills, Drillruru Long"
icon_state = "hair_drills_drillruru_long"
- gender = FEMALE
length = 3
chatname = "hair drills"
@@ -521,38 +476,32 @@ Follow by example and make good judgement based on length which list to include
emofringe_long
name = "Emo Fringe Long"
icon_state = "hair_emofringe_long"
- gender = FEMALE
length = 3
chatname = "long fringe"
fade_high
name = "Fade, High"
icon_state = "hair_fade_high"
- gender = MALE
chatname = "unshaved hair"
fade_low
name = "Fade, Low"
icon_state = "hair_fade_low"
- gender = MALE
chatname = "short hair"
fade_medium
name = "Fade, Medium"
icon_state = "hair_fade_medium"
- gender = MALE
chatname = "unshaved hair"
fade_none
name = "Fade, None"
icon_state = "hair_fade_none"
- gender = MALE
chatname = "short hair"
father
name = "Father"
icon_state = "hair_father"
- gender = MALE
chatname = "short hair"
feather
@@ -564,7 +513,6 @@ Follow by example and make good judgement based on length which list to include
flat_top
name = "Flat Top"
icon_state = "hair_flattop"
- gender = MALE
chatname = "flat top hair"
flair
@@ -581,14 +529,12 @@ Follow by example and make good judgement based on length which list to include
gelled
name = "Gelled Back"
icon_state = "hair_gelled"
- gender = FEMALE
length = 2
chatname = "gelled-back hair"
gentle
name = "Gentle"
icon_state = "hair_gentle"
- gender = FEMALE
length = 3
chatname = "long hair"
@@ -635,62 +581,53 @@ Follow by example and make good judgement based on length which list to include
himecut
name = "Hime Cut"
icon_state = "hair_himecut"
- gender = FEMALE
length = 3
chatname = "long hair"
himecut_alt
name = "Hime Cut, Alt"
icon_state = "hair_himecut_alt"
- gender = FEMALE
length = 3
chatname = "long hair"
himecut_alt2
name = "Hime Cut, Alt 2"
icon_state = "hair_himecut_alt2"
- gender = FEMALE
length = 3
chatname = "long hair"
himecut_long
name = "Hime Cut, Long"
icon_state = "hair_himecut_long"
- gender = FEMALE
length = 3
chatname = "long hair"
himecut_long_ponytail
name = "Hime Cut, Long Ponytail"
icon_state = "hair_himecut_long_ponytail"
- gender = FEMALE
length = 3
chatname = "long hair"
himecut_ponytail
name = "Hime Cut, Ponytail"
icon_state = "hair_himecut_ponytail"
- gender = FEMALE
length = 3
chatname = "long hair"
himecut_ponytail_up
name = "Hime Cut, Ponytail Up"
icon_state = "hair_himecut_ponytail_up"
- gender = FEMALE
length = 2
chatname = "long hair"
himecut_short
name = "Hime Cut, Short"
icon_state = "hair_himecut_short"
- gender = FEMALE
chatname = "short hair"
hitop
name = "Hitop"
icon_state = "hair_hitop"
- gender = MALE
chatname = "hitop"
jade
@@ -702,13 +639,11 @@ Follow by example and make good judgement based on length which list to include
jensen
name = "Jensen Hair" // Removing Videogame References
icon_state = "hair_jensen"
- gender = MALE
chatname = "short hair"
joestar
name = "Joestar"
icon_state = "hair_joestar"
- gender = MALE
chatname = "short hair"
longfringe
@@ -792,7 +727,6 @@ Follow by example and make good judgement based on length which list to include
mohawk_hightight
name = "Mohawk, High and Tight"
icon_state = "hair_mohawk_hightight"
- gender = MALE
chatname = "mohawk"
mohawk_naomi
@@ -803,7 +737,6 @@ Follow by example and make good judgement based on length which list to include
mohawk_reverse
name = "Mohawk, Reverse"
icon_state = "hair_mohawk_reverse"
- gender = MALE
chatname = "short hair"
mohawk_shaved
@@ -834,47 +767,40 @@ Follow by example and make good judgement based on length which list to include
mulder
name = "Mulder"
icon_state = "hair_mulder"
- gender = MALE
chatname = "short hair"
neat
name = "Neat"
icon_state = "hair_neat"
- gender = FEMALE
chatname = "groomed hair"
neatlong
name = "Neat (Long)"
icon_state = "hair_neatlong"
- gender = FEMALE
length = 2
chatname = "long hair"
newyou
name = "New You"
icon_state = "hair_newyou"
- gender = FEMALE
length = 3
chatname = "ponytail"
nia
name = "Nia"
icon_state = "hair_nia"
- gender = FEMALE
length = 3
chatname = "long hair"
ombre
name = "Ombre"
icon_state = "hair_ombre"
- gender = FEMALE
length = 2
chatname = "short hair"
oneshoulder
name = "One Shoulder"
icon_state = "hair_oneshoulder"
- gender = FEMALE
length = 2
chatname = "one shoulder hairstyle"
@@ -936,56 +862,48 @@ Follow by example and make good judgement based on length which list to include
pigtails_kagami
name = "Pigtails, Kagami"
icon_state = "hair_pigtails_kagami"
- gender = FEMALE
length = 2
chatname = "pigtails"
pigtails_low
name = "Pigtails, Low"
icon_state = "hair_pigtails_low"
- gender = FEMALE
length = 2
chatname = "pigtails"
pigtails_nitori
name = "Pigtails, Nitori"
icon_state = "hair_pigtails_nitori"
- gender = FEMALE
length = 2
chatname = "pigtails"
pigtails_twintail
name = "Pigtails, Twintail"
icon_state = "hair_pigtails_twintail"
- gender = FEMALE
length = 2
chatname = "pigtails"
pigtails_twintail_ombre
name = "Pigtails, Twintail Ombre"
icon_state = "hair_pigtails_twintail_ombre"
- gender = FEMALE
length = 2
chatname = "pigtails"
pigtails_twintail_ombre_alt
name = "Pigtails, Twintail Ombre Alt"
icon_state = "hair_pigtails_twintail_ombre_alt"
- gender = FEMALE
length = 3
chatname = "pigtails"
pompadour
name = "Pompadour"
icon_state = "hair_pompadour"
- gender = MALE
length = 3
chatname = "pompadour"
pompadour_dandy
name = "Pompadour, Dandy"
icon_state = "hair_pompadour_dandy"
- gender = MALE
length = 3
chatname = "pompadour"
@@ -998,7 +916,6 @@ Follow by example and make good judgement based on length which list to include
ponytail2
name = "Ponytail 2"
icon_state = "hair_ponytail2" //hair_pa
- gender = FEMALE
length = 2
chatname = "ponytail"
@@ -1011,7 +928,6 @@ Follow by example and make good judgement based on length which list to include
ponytail4
name = "Ponytail 4"
icon_state = "hair_ponytail4"
- gender = FEMALE
length = 2
chatname = "ponytail"
@@ -1024,21 +940,18 @@ Follow by example and make good judgement based on length which list to include
ponytail6
name = "Ponytail 6"
icon_state = "hair_ponytail6"
- gender = FEMALE
length = 2
chatname = "ponytail"
ponytail7
name = "Ponytail 7"
icon_state = "hair_ponytail7"
- gender = FEMALE
length = 2
chatname = "ponytail"
ponytail8
name = "Ponytail 8"
icon_state = "hair_ponytail8"
- gender = FEMALE
length = 2
chatname = "ponytail"
@@ -1051,28 +964,24 @@ Follow by example and make good judgement based on length which list to include
ponytail_fringetail
name = "Ponytail, Fringetail"
icon_state = "hair_ponytail_fringetail"
- gender = FEMALE
length = 2
chatname = "ponytail"
ponytail_high
name = "Ponytail, High"
icon_state = "hair_ponytail_high"
- gender = FEMALE
length = 2
chatname = "ponytail"
ponytail_side
name = "Ponytail, Side"
icon_state = "hair_ponytail_side"
- gender = FEMALE
length = 2
chatname = "ponytail"
ponytail_side2
name = "Ponytail, Side 2"
icon_state = "hair_ponytail_side2"
- gender = FEMALE
length = 2
chatname = "ponytail"
@@ -1091,35 +1000,30 @@ Follow by example and make good judgement based on length which list to include
ponytail_spiky
name = "Ponytail, Spiky"
icon_state = "hair_ponytail_spiky"
- gender = FEMALE
length = 4
chatname = "ponytail"
ponytail_wisp
name = "Ponytail, Wisp"
icon_state = "hair_ponytail_wisp"
- gender = FEMALE
length = 3
chatname = "ponytail"
ponytail_zieglertail
name = "Ponytail, Zieglertail"
icon_state = "hair_ponytail_ziegler"
- gender = FEMALE
length = 2
chatname = "ponytail"
poofy
name = "Poofy"
icon_state = "hair_poofy"
- gender = FEMALE
length = 2
chatname = "poofy hair"
poofy2
name = "Poofy 2"
icon_state = "hair_poofy2"
- gender = FEMALE
length = 2
chatname = "poofy hair"
@@ -1195,14 +1099,12 @@ Follow by example and make good judgement based on length which list to include
quiff
name = "Quiff"
icon_state = "hair_quiff"
- gender = MALE
length = 2
chatname = "quiff"
ronin
name = "Ronin"
icon_state = "hair_ronin"
- gender = MALE
length = 2
chatname = "long hair"
@@ -1244,13 +1146,11 @@ Follow by example and make good judgement based on length which list to include
scully
name = "Scully"
icon_state = "hair_scully"
- gender = FEMALE
chatname = "short hair"
shaved
name = "Shaved"
icon_state = "hair_shaved"
- gender = MALE
length = 0
chatname = "shaved head"
@@ -1309,83 +1209,70 @@ Follow by example and make good judgement based on length which list to include
thinning
name = "Thinning"
icon_state = "hair_thinning"
- gender = MALE
chatname = "short hair"
thinningback
name = "Thinning Back"
icon_state = "hair_thinningback"
- gender = MALE
chatname = "short hair"
thinningfront
name = "Thinning Front"
icon_state = "hair_thinningfront"
- gender = MALE
chatname = "short hair"
tresshoulder
name = "Tress Shoulder"
icon_state = "hair_tressshoulder"
- gender = FEMALE
length = 2
chatname = "curls"
trimmed
name = "Trimmed"
icon_state = "hair_trimmed"
- gender = MALE
chatname = "trimmed hair"
trimmedflat
name = "Trimmed Flat Top"
icon_state = "hair_trimmedflat"
- gender = MALE
chatname = "trimmed hair"
twincurls
name = "Twincurls"
icon_state = "hair_twincurls"
- gender = FEMALE
length = 2
chatname = "curls"
twincurls2
name = "Twincurls 2"
icon_state = "hair_twincurls2"
- gender = FEMALE
length = 2
chatname = "curls"
undercut
name = "Undercut"
icon_state = "hair_undercut"
- gender = MALE
chatname = "unshaved hair"
undercut2
name = "Undercut 2"
icon_state = "hair_undercut2"
- gender = MALE
chatname = "undercut"
undercut3
chatname = "unshaved hair"
name = "Undercut 3"
icon_state = "hair_undercut3"
- gender = MALE
chatname = "unshaved hair"
undercut4
name = "Undercut 4"
icon_state = "hair_undercut4"
- gender = MALE
chatname = "unshaved hair"
undercut5
name = "Undercut 5"
icon_state = "hair_undercut5"
- gender = MALE
chatname = "unshaved hair"
unkept
@@ -1397,14 +1284,12 @@ Follow by example and make good judgement based on length which list to include
updo
name = "Updo"
icon_state = "hair_updo"
- gender = FEMALE
length = 2
chatname = "updo"
vegeta
name = "Vegeta"
icon_state = "hair_vegeta"
- gender = MALE
length = 4
chatname = "mighty spikes"
@@ -1417,7 +1302,6 @@ Follow by example and make good judgement based on length which list to include
wheeler
name = "Wheeler"
icon_state = "hair_wheeler"
- gender = FEMALE
chatname = "short hair"
// TG-format hair - uses ICON_MULTIPLY instead of ICON_ADD
@@ -1570,14 +1454,12 @@ Follow by example and make good judgement based on length which list to include
nia2
name = "Nia 2"
icon_state = "hair_nia2"
- gender = FEMALE
length = 3
chatname = "long hair"
nia3
name = "Nia 3"
icon_state = "hair_nia3"
- gender = FEMALE
length = 3
chatname = "long hair"
@@ -2321,6 +2203,12 @@ Follow by example and make good judgement based on length which list to include
icon_state = "hair_victory"
chatname = "curls"
+ taj_ears_mane
+ name = "Tajara Mane"
+ icon_state = "hair_mane"
+ gender = MALE
+ chatname = "long mane"
+
//msai hair, longer ears
msai_ears
icon = 'icons/mob/human_face/msai_hair.dmi'
@@ -2423,6 +2311,12 @@ Follow by example and make good judgement based on length which list to include
icon_state = "msai_victory"
chatname = "curls"
+ msai_ears_mane
+ name = "M'sai Mane"
+ icon_state = "msai_mane"
+ gender = MALE
+ chatname = "long mane"
+
//vaurca antennae
vaurca_classic
icon = 'icons/mob/human_face/vaurca_hair.dmi'
diff --git a/code/modules/mob/abstract/unauthed/login.dm b/code/modules/mob/abstract/unauthed/login.dm
index f9a22c2826d..020340dcb06 100644
--- a/code/modules/mob/abstract/unauthed/login.dm
+++ b/code/modules/mob/abstract/unauthed/login.dm
@@ -28,7 +28,7 @@
/mob/abstract/unauthed/proc/timeout()
if (client)
- to_chat(client, "Your login time has expired. Please relog and try again.")
+ to_chat_immediate(client, "Your login time has expired. Please relog and try again.")
qdel(client)
qdel(src)
@@ -45,9 +45,9 @@
// Check for bans
var/list/ban_data = world.IsBanned(ckey(newkey), c.address, c.computer_id, 1, TRUE)
if(ban_data)
- to_chat(c, "You are banned for this server.")
- to_chat(c, "Reason: [ban_data["reason"]]")
- to_chat(c, "Description: [ban_data["desc"]]")
+ to_chat_immediate(c, "You are banned for this server.")
+ to_chat_immediate(c, "Reason: [ban_data["reason"]]")
+ to_chat_immediate(c, "Description: [ban_data["desc"]]")
del(c)
return
diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm
index b090e13af08..8ea6c062da7 100644
--- a/code/modules/mob/language/language.dm
+++ b/code/modules/mob/language/language.dm
@@ -22,6 +22,7 @@
var/list/partial_understanding // List of languages that can /somehwat/ understand it, format is: name = chance of understanding a word
var/machine_understands = TRUE // Whether machines can parse and understand this language
var/allow_accents = FALSE
+ var/always_parse_language = FALSE // forces the language to parse for language keys even when a default is set
/datum/language/proc/get_random_name(var/gender, name_count=2, syllable_count=4, syllable_divisor=2)
if(!syllables || !syllables.len)
diff --git a/code/modules/mob/language/outsider.dm b/code/modules/mob/language/outsider.dm
index dba7d88c4e9..b060f549d3d 100644
--- a/code/modules/mob/language/outsider.dm
+++ b/code/modules/mob/language/outsider.dm
@@ -104,4 +104,11 @@
key = "c"
syllables = list("grhhg", "ghrohg", "grgugh", "grrhh", "hghh", "rghghh", "gghhh", "ggrh", "aghrh")
flags = RESTRICTED
- partial_understanding = list(LANGUAGE_TCB = 80)
\ No newline at end of file
+ partial_understanding = list(LANGUAGE_TCB = 80)
+ always_parse_language = TRUE
+
+/datum/language/revenant/hivemind
+ name = LANGUAGE_REVENANT_RIFTSPEAK
+ desc = "A manner of speaking that allows revenants to talk to eachother no matter the distance."
+ key = "rs"
+ flags = RESTRICTED | HIVEMIND
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index 76c7165548f..333f1a18918 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -105,57 +105,11 @@
switch(M.a_intent)
if(I_HELP)
if(H != src && istype(H) && (is_asystole() || (status_flags & FAKEDEATH) || failed_last_breath))
- if (!cpr_time)
- return 0
-
- cpr_time = 0
-
- if(!do_after(H, rand(3, 5), src))
- cpr_time = 1
+ if (cpr)
+ cpr = FALSE
return
- cpr_time = 1
-
- H.do_attack_animation(src, null, image('icons/mob/screen/generic.dmi', src, "cpr", src.layer + 1))
- var/starting_pixel_y = pixel_y
- animate(src, pixel_y = starting_pixel_y + 4, time = 2)
- animate(src, pixel_y = starting_pixel_y, time = 2)
-
- if(is_asystole())
- if(prob(5 * rand(2, 3)))
- var/obj/item/organ/external/chest = get_organ(BP_CHEST)
- if(chest)
- chest.fracture()
-
- var/obj/item/organ/internal/heart/heart = internal_organs_by_name[BP_HEART]
- if(heart)
- heart.external_pump = list(world.time, 0.4 + 0.1 + rand(-0.1,0.1))
-
- if(stat != DEAD && prob(10 * rand(0.5, 1)))
- resuscitate()
-
- if(!H.check_has_mouth())
- to_chat(H, "You don't have a mouth, you cannot do mouth-to-mouth resuscitation!")
- return
- if(!check_has_mouth())
- to_chat(H, "They don't have a mouth, you cannot do mouth-to-mouth resuscitation!")
- return
- if((H.head && (H.head.body_parts_covered & FACE)) || (H.wear_mask && (H.wear_mask.body_parts_covered & FACE)))
- to_chat(H, "You need to remove your mouth covering for mouth-to-mouth resuscitation!")
- return 0
- if((head && (head.body_parts_covered & FACE)) || (wear_mask && (wear_mask.body_parts_covered & FACE)))
- to_chat(H, "You need to remove \the [src]'s mouth covering for mouth-to-mouth resuscitation!")
- return 0
- if (!H.internal_organs_by_name[H.species.breathing_organ])
- to_chat(H, "You need lungs for mouth-to-mouth resuscitation!")
- return
- if(!need_breathe())
- return
- var/obj/item/organ/internal/lungs/L = internal_organs_by_name[species.breathing_organ]
- if(L)
- var/datum/gas_mixture/breath = H.get_breath_from_environment()
- var/fail = L.handle_breath(breath, 1)
- if(!fail)
- to_chat(src, "You feel a breath of fresh air enter your lungs. It feels good.")
+ cpr = TRUE
+ cpr(H, TRUE)
else if(!(M == src && apply_pressure(M, M.zone_sel.selecting)))
help_shake_act(M)
@@ -471,6 +425,68 @@
visible_message("[M] attempted to disarm [src]!")
return
+/mob/living/carbon/human/proc/cpr(mob/living/carbon/human/H, var/starting = FALSE)
+ var/obj/item/main_hand = H.get_active_hand()
+ var/obj/item/off_hand = H.get_inactive_hand()
+ if (istype(main_hand) || istype(off_hand))
+ cpr = FALSE
+ to_chat(H, SPAN_NOTICE("You cannot perform CPR with anything in your hands."))
+ return
+ if(!(cpr && H.Adjacent(src) && (is_asystole() || (status_flags & FAKEDEATH) || failed_last_breath))) //Keeps doing CPR unless cancelled, or the target recovers
+ cpr = FALSE
+ to_chat(H, SPAN_NOTICE("You stop performing CPR on \the [src]."))
+ return
+ else if (starting)
+ to_chat(H, SPAN_NOTICE("You begin performing CPR on \the [src]."))
+
+ H.do_attack_animation(src, null, image('icons/mob/screen/generic.dmi', src, "cpr", src.layer + 1))
+ var/starting_pixel_y = pixel_y
+ animate(src, pixel_y = starting_pixel_y + 4, time = 2)
+ animate(src, pixel_y = starting_pixel_y, time = 2)
+
+ if(is_asystole())
+ if(prob(5 * rand(2, 3)))
+ var/obj/item/organ/external/chest = get_organ(BP_CHEST)
+ if(chest)
+ chest.fracture()
+
+ var/obj/item/organ/internal/heart/heart = internal_organs_by_name[BP_HEART]
+ if(heart)
+ heart.external_pump = list(world.time, 0.4 + 0.1 + rand(-0.1,0.1))
+
+ if(stat != DEAD && prob(10 * rand(0.5, 1)))
+ resuscitate()
+
+ if(!do_after(H, 3, FALSE)) //Chest compresssions are fast, need to wait for the loading bar to do mouth to mouth
+ to_chat(H, SPAN_NOTICE("You stop performing CPR on \the [src]."))
+ cpr = FALSE //If it cancelled, cancel it. Simple.
+
+ if(!H.check_has_mouth())
+ to_chat(H, "You don't have a mouth, you cannot do mouth-to-mouth resuscitation!")
+ return
+ if(!check_has_mouth())
+ to_chat(H, "They don't have a mouth, you cannot do mouth-to-mouth resuscitation!")
+ return
+ if((H.head && (H.head.body_parts_covered & FACE)) || (H.wear_mask && (H.wear_mask.body_parts_covered & FACE)))
+ to_chat(H, "You need to remove your mouth covering for mouth-to-mouth resuscitation!")
+ return 0
+ if((head && (head.body_parts_covered & FACE)) || (wear_mask && (wear_mask.body_parts_covered & FACE)))
+ to_chat(H, "You need to remove \the [src]'s mouth covering for mouth-to-mouth resuscitation!")
+ return 0
+ if (!H.internal_organs_by_name[H.species.breathing_organ])
+ to_chat(H, "You need lungs for mouth-to-mouth resuscitation!")
+ return
+ if(!need_breathe())
+ return
+ var/obj/item/organ/internal/lungs/L = internal_organs_by_name[species.breathing_organ]
+ if(L)
+ var/datum/gas_mixture/breath = H.get_breath_from_environment()
+ var/fail = L.handle_breath(breath, 1)
+ if(!fail)
+ to_chat(src, "You feel a breath of fresh air enter your lungs. It feels good.")
+
+ cpr(H) //Again.
+
/mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params)
return
@@ -625,4 +641,4 @@
return ..()
/mob/living/carbon/human/proc/set_default_attack(var/datum/unarmed_attack/u_attack)
- default_attack = u_attack
+ default_attack = u_attack
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index d4f7d3d7a0b..a2a8e0721e8 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -70,6 +70,8 @@
handle_medical_side_effects()
+ handle_fever()
+
//Handles regenerating stamina if we have sufficient air and no oxyloss
handle_stamina()
@@ -639,23 +641,6 @@
stamina_recovery *= 1 + 0.3 * chem_effects[CE_SPEEDBOOST]
move_delay_mod += -1.5 * chem_effects[CE_SPEEDBOOST]
- if(CE_FEVER in chem_effects)
- var/normal_temp = species?.body_temperature || (T0C+37)
- var/fever = chem_effects[CE_FEVER]
- if(CE_NOFEVER in chem_effects)
- fever -= chem_effects[CE_NOFEVER] // a dose of 16u Perconol should offset a stage 4 virus
- bodytemperature = Clamp(bodytemperature+fever, normal_temp, normal_temp + 9) // temperature should range from 37C to 46C, 98.6F to 115F
- if(fever > 1)
- if(prob(20/3)) // every 30 seconds, roughly
- to_chat(src, SPAN_WARNING(pick("You feel cold and clammy...", "You shiver as if a breeze has passed through.", "Your muscles ache.", "You feel tired and fatigued.")))
- if(prob(20)) // once every 10 seconds, roughly
- drowsyness += 4
- if(prob(20))
- adjustHalLoss(15) // muscle pain from fever
- if(fever >= 5) // your organs are boiling, figuratively speaking
- var/obj/item/organ/internal/IO = pick(internal_organs)
- IO.take_internal_damage(1)
-
var/total_phoronloss = 0
for(var/obj/item/I in src)
if(I.contaminated && !(isvaurca(src) && src.species.has_organ[BP_FILTRATION_BIT]))
@@ -1406,3 +1391,51 @@
damageoverlay.cut_overlay(last_oxy_overlay)
last_oxy_overlay = null
+//Fevers
+//This handles infection fevers as well as fevers caused by chem effects
+/mob/living/carbon/human/proc/handle_fever()
+ var/normal_temp = species?.body_temperature || (T0C+37)
+ //If we have infections, they give us a fever. Get all of their germ levels and find what our bodytemp will raise by.
+ var/fever = get_infection_germ_level() / INFECTION_LEVEL_ONE
+ //See what chemicals in our body affect our fever for better or worse
+ if(CE_FEVER in chem_effects)
+ fever += chem_effects[CE_FEVER]
+ if(CE_NOFEVER in chem_effects)
+ fever -= chem_effects[CE_NOFEVER]
+
+ //Apply changes to body temp. I absolutely hate body temp code -Doxx
+ if(fever < 0) //If we have enough anti-fever meds to bring us back towards normal temperature, do so.
+ if(bodytemperature >= normal_temp) //We don't have any effect if we're colder than normal.
+ bodytemperature = max(bodytemperature + fever, normal_temp)
+ return
+ if(fever > 0) //We're getting a fever, raise body temp. 10C above normal is our max for fevers.
+ if(bodytemperature < normal_temp) //If we're colder than usual, we'll slowly raise to normal temperature
+ bodytemperature = min(bodytemperature + fever, normal_temp)
+ else if(bodytemperature <= normal_temp + 10) //If we're hotter than max due to like, being on fire, don't keep increasing.
+ bodytemperature = normal_temp + min(fever, 10) //We use normal_temp here to maintain a steady temperature, otherwise even a small infection steadily increases bodytemp to max. This way it's easier to diagnose the intensity of an infection based on how bad the fever is.
+ //Apply side effects for having a fever. Separate from body temp changes.
+ if(fever >= 2)
+ do_fever_effects(fever)
+
+
+//Getting the total germ level for all infected organs, affects fever
+/mob/living/carbon/human/proc/get_infection_germ_level()
+ var/germs
+ for(var/obj/item/organ/I in internal_organs)
+ if(I.is_infected())
+ germs += I.germ_level
+ for(var/obj/item/organ/external/E in organs)
+ if(E.is_infected())
+ germs += E.germ_level
+ return germs
+
+/mob/living/carbon/human/proc/do_fever_effects(var/fever)
+ if(prob(20/3)) // every 30 seconds, roughly
+ to_chat(src, SPAN_WARNING(pick("You feel cold and clammy...", "You shiver as if a breeze has passed through.", "Your muscles ache.", "You feel tired and fatigued.")))
+ if(prob(20)) // once every 10 seconds, roughly
+ drowsyness += 4
+ if(prob(20))
+ adjustHalLoss(5 * min(fever, 5)) // muscle pain from fever
+ if(fever >= 7 && prob(10)) // your organs are boiling, figuratively speaking
+ var/obj/item/organ/internal/IO = pick(internal_organs)
+ IO.take_internal_damage(1)
diff --git a/code/modules/mob/living/carbon/human/species/outsider/revenant.dm b/code/modules/mob/living/carbon/human/species/outsider/revenant.dm
index 9b4c21a9fc7..b1f9d0746ef 100644
--- a/code/modules/mob/living/carbon/human/species/outsider/revenant.dm
+++ b/code/modules/mob/living/carbon/human/species/outsider/revenant.dm
@@ -101,6 +101,7 @@
if(player_is_antag(H.mind))
var/datum/ghostspawner/revenant/R = SSghostroles.get_spawner(MODE_REVENANT)
R.count = max(R.count - 1, 0)
+ revenants.kill_count++
INVOKE_ASYNC(src, .proc/spawn_gore, get_turf(H))
H.set_death_time(ANIMAL, world.time)
for(var/obj/item/I in H)
@@ -108,6 +109,8 @@
qdel(H)
/datum/species/revenant/proc/spawn_gore(var/turf/T)
+ var/portal_type = pick(/obj/effect/portal/spawner/silver, /obj/effect/portal/spawner/gold, /obj/effect/portal/spawner/phoron)
+ new portal_type(T)
var/obj/effect/decal/cleanable/blood/gibs/G = new /obj/effect/decal/cleanable/blood/gibs(T)
G.basecolor = blood_color
G.fleshcolor = flesh_color
@@ -119,6 +122,7 @@
..()
H.gender = NEUTER
H.universal_understand = TRUE
+ H.add_language(LANGUAGE_REVENANT_RIFTSPEAK)
/datum/species/revenant/get_random_name()
return "Revenant"
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 990265b527f..c34a0a6c3e0 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -184,7 +184,7 @@ proc/get_radio_key_from_channel(var/channel)
message = formalize_text(message)
//parse the language code and consume it
- if(!speaking)
+ if(!speaking || speaking.always_parse_language)
speaking = parse_language(message)
if(speaking)
message = copytext(message,2+length(speaking.key))
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 5e8904fa0c2..76cfe5c268d 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -107,7 +107,7 @@
var/name_archive //For admin things like possession
var/timeofdeath = 0.0//Living
- var/cpr_time = 1.0//Carbon
+ var/cpr = FALSE //Whether the mob is performing cpr or not
var/bodytemperature = 310.055 //98.7 F
var/old_x = 0
diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm
index 25bba3d9308..033304c9684 100644
--- a/code/modules/mob/mob_grab_specials.dm
+++ b/code/modules/mob/mob_grab_specials.dm
@@ -35,6 +35,10 @@
if(H.getOxyLoss() >= 20)
to_chat(user, "[H]'s skin is unusually pale.")
bad = 1
+ if(E.is_infected())
+ var/severity = E.germ_level < INFECTION_LEVEL_TWO ? "slightly" : E.germ_level < INFECTION_LEVEL_THREE ? "moderately" : "extremely"
+ to_chat(user, SPAN_WARNING("[H]'s skin is [severity] warm and reddened."))
+ bad = 1
if(E.status & ORGAN_DEAD)
to_chat(user, "[E] is decaying!")
bad = 1
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index c8eb4c0ff3f..609463baaba 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -140,6 +140,14 @@
return TRUE
return FALSE
+/proc/isrevenant(A)
+ if(ishuman(A))
+ var/mob/living/carbon/human/H = A
+ switch(H.get_species())
+ if(SPECIES_REVENANT)
+ return TRUE
+ return FALSE
+
/proc/islesserform(A)
if(istype(A, /mob/living/carbon/human))
switch(A:get_species())
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 84a6799e34d..c463b6d33d1 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -159,9 +159,12 @@
return all_languages["Noise"]
if(length(message) >= 2 && is_language_prefix(prefix))
- var/language_prefix = lowertext(copytext(message, 2 ,3))
+ var/language_prefix = lowertext(copytext(message, 2, 4))
var/datum/language/L = language_keys[language_prefix]
- if (can_speak(L))
+ if(!L)
+ language_prefix = lowertext(copytext(message, 2, 3))
+ L = language_keys[language_prefix]
+ if(can_speak(L))
return L
return null
diff --git a/code/modules/nano/modules/human_appearance.dm b/code/modules/nano/modules/human_appearance.dm
index a00c80e17cc..9482f943397 100644
--- a/code/modules/nano/modules/human_appearance.dm
+++ b/code/modules/nano/modules/human_appearance.dm
@@ -209,7 +209,7 @@
if(!length(valid_species))
valid_species = owner.generate_valid_species(check_whitelist, whitelist, blacklist)
if(!length(valid_hairstyles) || !length(valid_facial_hairstyles))
- valid_hairstyles = owner.generate_valid_hairstyles(check_gender = 0)
+ valid_hairstyles = owner.generate_valid_hairstyles(check_gender = 1)
valid_facial_hairstyles = owner.generate_valid_facial_hairstyles()
if(!length(valid_accents))
valid_accents = owner.generate_valid_accent()
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index 7eb20dd21b3..003ab6444ff 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -235,9 +235,6 @@
if(antibiotics < 5 && prob(round(germ_level/6)))
germ_level++
- if(germ_level >= INFECTION_LEVEL_ONE)
- owner.add_chemical_effect(CE_FEVER, germ_level/INFECTION_LEVEL_ONE) //10u of Perconol minimum for a level 3 infection
-
if (germ_level >= INFECTION_LEVEL_TWO)
var/obj/item/organ/external/parent = owner.get_organ(parent_organ)
//spread germs
@@ -284,6 +281,9 @@
/obj/item/organ/proc/is_usable()
return !(status & (ORGAN_CUT_AWAY|ORGAN_MUTATED|ORGAN_DEAD))
+/obj/item/organ/proc/is_infected()
+ return (germ_level >= INFECTION_LEVEL_ONE)
+
//Germs
/obj/item/organ/proc/handle_antibiotics()
if(!owner || !(CE_ANTIBIOTIC in owner.chem_effects))
diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm
index b7b4b87c55e..8a641191da8 100644
--- a/code/modules/organs/organ_external.dm
+++ b/code/modules/organs/organ_external.dm
@@ -90,6 +90,9 @@
var/augment_limit //how many augments you can fit inside this limb
+ var/obj/item/organ/infect_target_internal //make internal organs become infected one at a time instead of all at once
+ var/obj/item/organ/infect_target_external //make child and parent organs become infected one at a time instead of all at once
+
/obj/item/organ/external/proc/invalidate_marking_cache()
cached_markings = null
@@ -105,6 +108,9 @@
for(var/obj/item/organ/O in internal_organs)
qdel(O)
+ infect_target_internal = null
+ infect_target_external = null
+
applied_pressure = null
return ..()
@@ -642,46 +648,65 @@ Note that amputating the affected organ does in fact remove the infection from t
germ_level++
break //limit increase to a maximum of one per second
-/obj/item/organ/external/handle_germ_effects()
+/obj/item/organ/external/proc/get_infect_target(var/list/infect_candidates = list())
+ var/obj/item/organ/temp_target
+ shuffle(infect_candidates) //Slightly randomizes since if all germ levels are zero, it'll always be the first pick of the list
- if(germ_level < INFECTION_LEVEL_TWO)
- return ..()
+ //figure out which organs we can spread germs to
+ for (var/obj/item/organ/I in infect_candidates)
+ if(I.germ_level < min(germ_level, INFECTION_LEVEL_TWO)) //Only choose organs that have less germs than us AND are below level two
+ //The below will always be the organ with the highest germ level. It picks a temp_target first then cycles through to find which, if any, has more germs.
+ if(!temp_target || I.germ_level > temp_target.germ_level)
+ temp_target = I //This will always be the organ with the highest germ level
+
+ return temp_target
+
+/obj/item/organ/external/handle_germ_effects()
var/antibiotics = 0
if(CE_ANTIBIOTIC in owner.chem_effects)
antibiotics = owner.chem_effects[CE_ANTIBIOTIC]
- if(germ_level >= INFECTION_LEVEL_TWO)
- //spread the infection to internal organs
- var/obj/item/organ/target_organ = null //make internal organs become infected one at a time instead of all at once
- for (var/obj/item/organ/I in internal_organs)
- if (I.germ_level > 0 && I.germ_level < min(germ_level, INFECTION_LEVEL_TWO)) //once the organ reaches whatever we can give it, or level two, switch to a different one
- if (!target_organ || I.germ_level > target_organ.germ_level) //choose the organ with the highest germ_level
- target_organ = I
+ if(germ_level < INFECTION_LEVEL_TWO)
+ //null out the infect targets since at this point we're not in danger of spreading our infection.
+ infect_target_internal = null
+ infect_target_external = null
+ return ..()
- if (!target_organ)
- //figure out which organs we can spread germs to and pick one at random
- var/list/candidate_organs = list()
- for (var/obj/item/organ/I in internal_organs)
- if (I.germ_level < germ_level)
- candidate_organs |= I
- if (candidate_organs.len)
- target_organ = pick(candidate_organs)
+ germ_level++
- if (target_organ)
- target_organ.germ_level++
+ if(germ_level >= INFECTION_LEVEL_TWO && REAGENT_VOLUME(owner.reagents, /decl/reagent/thetamycin) < 5) //The presence of 5 units of thetamycin will stop infections spreading
+ //SPREADING TO INTERNAL ORGANS
+ if(isnull(infect_target_internal) || QDELETED(infect_target_internal))
+ infect_target_internal = get_infect_target(internal_organs)
- //spread the infection to child and parent organs
- if (children)
- for (var/obj/item/organ/external/child in children)
- if (child.germ_level < germ_level && !(child.status & ORGAN_ROBOT))
- if (child.germ_level < INFECTION_LEVEL_ONE*2 || prob(30))
- child.germ_level++
+ else
+ if(prob(25) || !infect_target_internal.is_infected()) //Increase steadily until infection_level_one, then only bump the germ level now and then
+ infect_target_internal.germ_level++ //slowly increase the infection level
+ //Check to see if the other organ is as infected or more infected than us. If so, we need to get a new target on the next process
+ if(infect_target_internal.germ_level >= min(germ_level, INFECTION_LEVEL_TWO))
+ infect_target_internal = null
- if (parent)
- if (parent.germ_level < germ_level && !(parent.status & ORGAN_ROBOT))
- if (parent.germ_level < INFECTION_LEVEL_ONE*2 || prob(30))
- parent.germ_level++
+ //SPREADING TO CHILD AND PARENT EXTERNAL ORGANS
+ if(isnull(infect_target_external) || QDELETED(infect_target_internal))
+ var/list/temp_targets = list()
+ if(children)
+ for (var/obj/item/organ/external/child in children)
+ if (child.germ_level < germ_level && !(child.status & ORGAN_ROBOT))
+ temp_targets += child
+
+ if(parent)
+ if(parent.germ_level < germ_level && !(parent.status & ORGAN_ROBOT))
+ temp_targets += parent
+ if(length(temp_targets))
+ infect_target_external = get_infect_target(temp_targets)
+
+ else
+ if(prob(25) || !infect_target_external.is_infected()) //Increase steadily until sufficiently infected, then only bump the germ level now and then
+ infect_target_external.germ_level++ //slowly increase the infection level
+ //Check to see if the other organ is as infected or more infected than us. If so, we need to get a new target on the next process
+ if(infect_target_external.germ_level >= min(germ_level, INFECTION_LEVEL_TWO))
+ infect_target_external = null
if(germ_level >= INFECTION_LEVEL_THREE && antibiotics < 20) //overdosing is necessary to stop severe infections
if (!(status & ORGAN_DEAD))
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
index 3362c462d9e..2df507a4f04 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
@@ -264,7 +264,7 @@
/decl/reagent/perconol/affect_blood(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder)
M.add_chemical_effect(CE_PAINKILLER, 50)
- M.add_chemical_effect(CE_NOFEVER, ((M.chem_doses[type]/2)^2-(REAGENT_VOLUME(holder, type)-M.chem_doses[type]/2))/(M.chem_doses[type]/4)) // creates a smooth curve peaking at half the dose metabolised
+ M.add_up_to_chemical_effect(CE_NOFEVER, 5) //Good enough to handle fevers for a few light infections or one bad one.
/decl/reagent/perconol/overdose(var/mob/living/carbon/M, var/alien, var/datum/reagents/holder)
..()
@@ -824,6 +824,7 @@
taste_description = "bitterness"
/decl/reagent/leporazine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder)
+ M.add_up_to_chemical_effect(CE_NOFEVER, 5) //Also handles the effects of fevers
if(!(REAGENT_VOLUME(holder, type) > 20))
if(M.bodytemperature > 310)
M.bodytemperature = max(310, M.bodytemperature - (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
diff --git a/code/modules/research/designs/protolathe/tool_designs.dm b/code/modules/research/designs/protolathe/tool_designs.dm
index 3cc1067955f..1ba935a8dce 100644
--- a/code/modules/research/designs/protolathe/tool_designs.dm
+++ b/code/modules/research/designs/protolathe/tool_designs.dm
@@ -143,4 +143,10 @@ datum/design/item/tool/advanced_light_replacer
desc = "The infamously Idris Service Standard refers to this monstrous, self-stabilizing back-mounted utensil and service item holder, not anything professional."
req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
materials = list(DEFAULT_WALL_MATERIAL = 1500, MATERIAL_GLASS = 1500)
- build_path = /obj/item/storage/backpack/service
\ No newline at end of file
+ build_path = /obj/item/storage/backpack/service
+
+/datum/design/item/tool/bluespace_neutralizer
+ desc = "A state of the art bluespace neutralizer, capable of shutting down any bluespace portal it gets used on. A special mode exists for severe interdimensional breaches, but this is highly unlikely to be necessary."
+ req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 4)
+ materials = list(DEFAULT_WALL_MATERIAL = 20000, MATERIAL_GLASS = 20000, MATERIAL_SILVER = 20000, MATERIAL_GOLD = 20000, MATERIAL_PHORON = 10000)
+ build_path = /obj/item/bluespace_neutralizer
\ No newline at end of file
diff --git a/html/changelog.html b/html/changelog.html
index 9fbc5038426..0562f3cd569 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -35,6 +35,55 @@
-->