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 @@ -->
+

29 January 2021

+

Doxxmedearly updated:

+ +

Sparky_hotdog updated:

+ + +

28 January 2021

+

Alberyk updated:

+ +

Geeves updated:

+ +

HappyFox and WickedCybs updated:

+ +

Karolis2011 updated:

+ +

MattAtlas updated:

+ +

Wowzewow (Wezzy) updated:

+ +

27 January 2021

Wowzewow (Wezzy) updated: