diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 35b70bdb84..d33eaa35da 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -6,7 +6,8 @@
#define PLANE_SPACE -95
#define PLANE_SPACE_PARALLAX -90
-#define GAME_PLANE 0
+#define GAME_PLANE -1
+#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
#define SPACE_LAYER 1.8
#define ABOVE_SPACE_LAYER 1.9
//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define
diff --git a/code/__DEFINES/server_tools.dm b/code/__DEFINES/server_tools.dm
index b86a54959f..2773965d9c 100644
--- a/code/__DEFINES/server_tools.dm
+++ b/code/__DEFINES/server_tools.dm
@@ -1,4 +1,4 @@
-// /tg/station 13 server tools API v3.1
+// /tg/station 13 server tools API v3.1.0.1
//CONFIGURATION
//use this define if you want to do configuration outside of this file
@@ -21,6 +21,8 @@
#define SERVER_TOOLS_LOG(message) log_world("SERVICE: [##message]")
//Notify current in-game administrators of a string `event`
#define SERVER_TOOLS_NOTIFY_ADMINS(event) message_admins(##event)
+//The current amount of connected clients
+#define SERVER_TOOLS_CLIENT_COUNT GLOB.clients.len
#endif
//Required hooks:
@@ -62,7 +64,7 @@
//IMPLEMENTATION
-#define SERVICE_API_VERSION_STRING "3.1.0.0"
+#define SERVICE_API_VERSION_STRING "3.1.0.1"
#define REBOOT_MODE_NORMAL 0
#define REBOOT_MODE_HARD 1
@@ -78,12 +80,13 @@
#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands"
+#define SERVICE_CMD_API_COMPATIBLE "api_compat"
+#define SERVICE_CMD_PLAYER_COUNT "client_count"
#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
#define SERVICE_CMD_PARAM_COMMAND "command"
#define SERVICE_CMD_PARAM_SENDER "sender"
#define SERVICE_CMD_PARAM_CUSTOM "custom"
-#define SERVICE_CMD_API_COMPATIBLE "api_compat"
#define SERVICE_JSON_PARAM_HELPTEXT "help_text"
#define SERVICE_JSON_PARAM_ADMINONLY "admin_only"
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 1d36f7d31e..4d1ca2c778 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -313,11 +313,15 @@
inv.hud = src
inv_slots[inv.slot_id] = inv
inv.update_icon()
+
+ update_locked_slots()
/datum/hud/human/update_locked_slots()
if(!mymob)
return
var/mob/living/carbon/human/H = mymob
+ if(!istype(H) || !H.dna.species)
+ return
var/datum/species/S = H.dna.species
for(var/obj/screen/inventory/inv in (static_inventory + toggleable_inventory))
if(inv.slot_id)
diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index 49ccb411fc..a9807a599c 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -74,6 +74,10 @@ GLOBAL_PROTECT(config_dir)
if(copytext(L, 1, 2) == "#")
continue
+ var/lockthis = copytext(L, 1, 2) == "@"
+ if(lockthis)
+ L = copytext(L, 2)
+
var/pos = findtext(L, " ")
var/entry = null
var/value = null
@@ -96,6 +100,9 @@ GLOBAL_PROTECT(config_dir)
log_config("Found [entry] in [filename] when it should have been in [E.resident_file]! Ignoring.")
continue
+ if(lockthis)
+ E.protection |= CONFIG_ENTRY_LOCKED
+
var/validated = E.ValidateAndSet(value)
if(!validated)
log_config("Failed to validate setting \"[value]\" for [entry]")
diff --git a/code/datums/action.dm b/code/datums/action.dm
index a65294fa40..cf1046ca8f 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -591,3 +591,50 @@
var/mob/M = owner
var/datum/language_holder/H = M.get_language_holder()
H.open_language_menu(usr)
+
+/datum/action/innate/dash
+ name = "Dash"
+ desc = "Teleport to the targeted location."
+ icon_icon = 'icons/mob/actions/actions_items.dmi'
+ button_icon_state = "jetboot"
+ var/charged = TRUE
+ var/charge_rate = 250
+ var/mob/living/carbon/human/holder
+ var/obj/item/dashing_item
+ var/dash_sound = 'sound/magic/blink.ogg'
+ var/recharge_sound = 'sound/magic/charge.ogg'
+ var/beam_effect = "blur"
+ var/phasein = /obj/effect/temp_visual/dir_setting/ninja/phase
+ var/phaseout = /obj/effect/temp_visual/dir_setting/ninja/phase/out
+
+/datum/action/innate/dash/Grant(mob/user, obj/dasher)
+ . = ..()
+ dashing_item = dasher
+ holder = user
+
+/datum/action/innate/dash/IsAvailable()
+ if(charged)
+ return TRUE
+ else
+ return FALSE
+
+/datum/action/innate/dash/Activate()
+ dashing_item.attack_self(holder) //Used to toggle dash behavior in the dashing item
+
+/datum/action/innate/dash/proc/Teleport(mob/user, atom/target)
+ var/turf/T = get_turf(target)
+ if(target in view(user.client.view, get_turf(user)))
+ var/obj/spot1 = new phaseout(get_turf(user), user.dir)
+ user.forceMove(T)
+ playsound(T, dash_sound, 25, 1)
+ var/obj/spot2 = new phasein(get_turf(user), user.dir)
+ spot1.Beam(spot2,beam_effect,time=20)
+ charged = FALSE
+ holder.update_action_buttons_icon()
+ addtimer(CALLBACK(src, .proc/charge), charge_rate)
+
+/datum/action/innate/dash/proc/charge()
+ charged = TRUE
+ holder.update_action_buttons_icon()
+ playsound(dashing_item, recharge_sound, 50, 1)
+ to_chat(holder, "[dashing_item] is ready for another jaunt.")
\ No newline at end of file
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 25b04b4312..20d218f767 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -384,3 +384,31 @@
if(islist(owner.stun_absorption) && owner.stun_absorption["blooddrunk"])
owner.stun_absorption -= "blooddrunk"
+/datum/status_effect/sword_spin
+ id = "Bastard Sword Spin"
+ duration = 50
+ tick_interval = 8
+ alert_type = null
+
+
+/datum/status_effect/sword_spin/on_apply()
+ owner.visible_message("[owner] begins swinging the sword with inhuman strength!")
+ var/oldcolor = owner.color
+ owner.color = "#ff0000"
+ owner.add_stun_absorption("bloody bastard sword", duration, 2, "doesn't even flinch as the sword's power courses through them!", "You shrug off the stun!", " glowing with a blazing red aura!")
+ owner.spin(duration,1)
+ animate(owner, color = oldcolor, time = duration, easing = EASE_IN)
+ addtimer(CALLBACK(owner, /atom/proc/update_atom_colour), duration)
+ playsound(owner, 'sound/weapons/fwoosh.wav', 75, 0)
+ return ..()
+
+
+/datum/status_effect/sword_spin/tick()
+ playsound(owner, 'sound/weapons/fwoosh.wav', 75, 0)
+ var/obj/item/slashy
+ slashy = owner.get_active_held_item()
+ for(var/mob/living/M in orange(1,owner))
+ slashy.attack(M, owner)
+
+/datum/status_effect/sword_spin/on_remove()
+ owner.visible_message("[owner]'s inhuman strength dissipates and the sword's runes grow cold!")
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 55f8ece5e6..876cace14d 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -72,6 +72,170 @@
if(is_servant_of_ratvar(C) && C.reagents)
C.reagents.add_reagent("heparin", 1)
+/obj/item/twohanded/required/cult_bastard
+ name = "bloody bastard sword"
+ desc = "An enormous sword used by Nar-Sien cultists to rapidly harvest the souls of non-believers."
+ w_class = WEIGHT_CLASS_HUGE
+ block_chance = 50
+ throwforce = 20
+ force = 35
+ armour_penetration = 45
+ throw_speed = 1
+ throw_range = 3
+ sharpness = IS_SHARP
+ light_color = "#ff0000"
+ attack_verb = list("cleaved", "slashed", "torn", "hacked", "ripped", "diced", "carved")
+ icon_state = "cultbastard"
+ item_state = "cultbastard"
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
+ inhand_x_dimension = 64
+ inhand_y_dimension = 64
+ actions_types = list()
+ flags_2 = SLOWS_WHILE_IN_HAND_2
+ var/datum/action/innate/dash/cult/jaunt
+ var/datum/action/innate/cult/spin2win/linked_action
+ var/spinning = FALSE
+ var/spin_cooldown = 250
+ var/list/shards = list()
+ var/dash_toggled = TRUE
+
+/obj/item/twohanded/required/cult_bastard/Initialize()
+ . = ..()
+ set_light(4)
+ jaunt = new(src)
+ linked_action = new(src)
+
+/obj/item/twohanded/required/cult_bastard/can_be_pulled(user)
+ return FALSE
+
+/obj/item/twohanded/required/cult_bastard/attack_self(mob/user)
+ dash_toggled = !dash_toggled
+ if(dash_toggled)
+ to_chat(loc, "You raise the [src] and prepare to jaunt with it.")
+ else
+ to_chat(loc, "You lower the [src] and prepare to swing it normally.")
+
+/obj/item/twohanded/required/cult_bastard/pickup(mob/living/user)
+ . = ..()
+ if(!iscultist(user))
+ if(!is_servant_of_ratvar(user))
+ to_chat(user, "\"I wouldn't advise that.\"")
+ to_chat(user, "An overwhelming sense of nausea overpowers you!")
+ user.Dizzy(80)
+ user.Knockdown(30)
+ return
+ else
+ to_chat(user, "\"One of Ratvar's toys is trying to play with things [user.p_they()] shouldn't. Cute.\"")
+ to_chat(user, "A horrible force yanks at your arm!")
+ user.emote("scream")
+ user.apply_damage(30, BRUTE, pick("l_arm", "r_arm"))
+ user.Knockdown(50)
+ return
+ jaunt.Grant(user, src)
+ linked_action.Grant(user, src)
+ user.update_icons()
+
+/obj/item/twohanded/required/cult_bastard/dropped(mob/user)
+ . = ..()
+ linked_action.Remove(user)
+ jaunt.Remove(user)
+ user.update_icons()
+
+/obj/item/twohanded/required/cult_bastard/IsReflect()
+ if(spinning)
+ playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
+ return TRUE
+ else
+ ..()
+
+/obj/item/twohanded/required/cult_bastard/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(prob(final_block_chance))
+ if(attack_type == PROJECTILE_ATTACK)
+ owner.visible_message("[owner] deflects [attack_text] with [src]!")
+ playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 100, 1)
+ return TRUE
+ else
+ playsound(src, 'sound/weapons/parry.ogg', 75, 1)
+ owner.visible_message("[owner] parries [attack_text] with [src]!")
+ return TRUE
+ return FALSE
+
+/obj/item/twohanded/required/cult_bastard/afterattack(atom/target, mob/user, proximity, click_parameters)
+ . = ..()
+ if(dash_toggled && jaunt.IsAvailable())
+ jaunt.Teleport(user, target)
+ return
+ if(!proximity)
+ return
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ if(H.stat != CONSCIOUS)
+ var/obj/item/device/soulstone/SS = new /obj/item/device/soulstone(src)
+ SS.attack(H, user)
+ shards += SS
+ return
+ if(istype(target, /obj/structure/constructshell) && contents.len)
+ var/obj/item/device/soulstone/SS = contents[1]
+ if(istype(SS) && SS.transfer_soul("CONSTRUCT",target,user))
+ qdel(SS)
+
+/datum/action/innate/dash/cult
+ name = "Rend the Veil"
+ desc = "Use the sword to shear open the flimsy fabric of this reality and teleport to your target."
+ icon_icon = 'icons/mob/actions/actions_cult.dmi'
+ button_icon_state = "phaseshift"
+ dash_sound = 'sound/magic/enter_blood.ogg'
+ recharge_sound = 'sound/magic/exit_blood.ogg'
+ beam_effect = "sendbeam"
+ phasein = /obj/effect/temp_visual/dir_setting/cult/phase
+ phaseout = /obj/effect/temp_visual/dir_setting/cult/phase/out
+
+/datum/action/innate/dash/cult/IsAvailable()
+ if(iscultist(holder) && charged)
+ return TRUE
+ else
+ return FALSE
+
+
+
+/datum/action/innate/cult/spin2win
+ name = "Geometer's Fury"
+ desc = "You draw on the power of the sword's ancient runes, spinning it wildly around you as you become immune to most attacks."
+ background_icon_state = "bg_demon"
+ button_icon_state = "sintouch"
+ var/cooldown = 0
+ var/mob/living/carbon/human/holder
+ var/obj/item/twohanded/required/cult_bastard/sword
+
+/datum/action/innate/cult/spin2win/Grant(mob/user, obj/bastard)
+ . = ..()
+ sword = bastard
+ holder = user
+
+/datum/action/innate/cult/spin2win/IsAvailable()
+ if(iscultist(holder) && cooldown <= world.time)
+ return TRUE
+ else
+ return FALSE
+
+/datum/action/innate/cult/spin2win/Activate()
+ cooldown = world.time + sword.spin_cooldown
+ holder.changeNext_move(50)
+ holder.apply_status_effect(/datum/status_effect/sword_spin)
+ sword.spinning = TRUE
+ sword.block_chance = 100
+ sword.slowdown += 1.5
+ addtimer(CALLBACK(src, .proc/stop_spinning), 50)
+ holder.update_action_buttons_icon()
+
+/datum/action/innate/cult/spin2win/proc/stop_spinning()
+ sword.spinning = FALSE
+ sword.block_chance = 50
+ sword.slowdown -= 1.5
+ sleep(sword.spin_cooldown)
+ holder.update_action_buttons_icon()
/obj/item/restraints/legcuffs/bola/cult
name = "nar'sien bola"
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index c1ccf9a368..0144872c17 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -100,15 +100,21 @@
if(cooldowntime > world.time)
to_chat(user, "The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].")
return
- var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Nar-Sien Hardsuit")
+ var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Bastard Sword")
var/pickedtype
switch(choice)
if("Shielded Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/cult_shield
if("Flagellant's Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/berserker
- if("Nar-Sien Hardsuit")
- pickedtype = /obj/item/clothing/suit/space/hardsuit/cult
+ if("Bastard Sword")
+ if((world.time - SSticker.round_start_time) >= 12000)
+ pickedtype = /obj/item/twohanded/required/cult_bastard
+ else
+ cooldowntime = 12000 - (world.time - SSticker.round_start_time)
+ to_chat(user, "The forge fires are not yet hot enough for this weapon, give it another [DisplayTimeText(cooldowntime - world.time)].")
+ cooldowntime = 0
+ return
if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index df57ded774..149c2ae912 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -199,7 +199,6 @@
SSticker.mode.cult -= A.mind
SSticker.mode.update_cult_icons_removed(A.mind)
qdel(T)
- user.drop_item()
qdel(src)
else
to_chat(user, "Creation failed!: The soul stone is empty! Go kill someone!")
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index e9a622365a..e55c728121 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -73,6 +73,7 @@ MASS SPECTROMETER
origin_tech = "magnets=1;biotech=1"
var/mode = 1
var/scanmode = 0
+ var/advanced = FALSE
/obj/item/device/healthanalyzer/attack_self(mob/user)
if(!scanmode)
@@ -97,7 +98,7 @@ MASS SPECTROMETER
user.visible_message("[user] has analyzed [M]'s vitals.")
if(scanmode == 0)
- healthscan(user, M, mode)
+ healthscan(user, M, mode, advanced)
else if(scanmode == 1)
chemscan(user, M)
@@ -105,7 +106,7 @@ MASS SPECTROMETER
// Used by the PDA medical scanner too
-/proc/healthscan(mob/living/user, mob/living/M, mode = 1)
+/proc/healthscan(mob/living/user, mob/living/M, mode = 1, advanced = FALSE)
if(user.incapacitated() || user.eye_blind)
return
//Damage specifics
@@ -115,8 +116,8 @@ MASS SPECTROMETER
var/brute_loss = M.getBruteLoss()
var/mob_status = (M.stat == DEAD ? "Deceased" : "[round(M.health/M.maxHealth,0.01)*100] % healthy")
- if(M.status_flags & FAKEDEATH)
- mob_status = "Deceased"
+ if(M.status_flags & FAKEDEATH && !advanced)
+ mob_status = "Deceased"
oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss
if(ishuman(M))
@@ -141,19 +142,77 @@ MASS SPECTROMETER
if(oxy_loss > 10)
to_chat(user, "\t[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.")
if(tox_loss > 10)
- to_chat(user, "\t[tox_loss > 50 ? "Critical" : "Dangerous"] amount of toxins detected.")
+ to_chat(user, "\t[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.")
if(M.getStaminaLoss())
to_chat(user, "\tSubject appears to be suffering from fatigue.")
+ if(advanced)
+ to_chat(user, "\tFatigue Level: [M.getStaminaLoss()]%.")
if (M.getCloneLoss())
to_chat(user, "\tSubject appears to have [M.getCloneLoss() > 30 ? "severe" : "minor"] cellular damage.")
- if (M.reagents && M.reagents.get_reagent_amount("epinephrine"))
- to_chat(user, "\tBloodstream analysis located [M.reagents.get_reagent_amount("epinephrine")] units of rejuvenation chemicals.")
+ if(advanced)
+ to_chat(user, "\tCellular Damage Level: [M.getCloneLoss()].")
if (M.getBrainLoss() >= 100 || !M.getorgan(/obj/item/organ/brain))
to_chat(user, "\tSubject brain function is non-existent.")
else if (M.getBrainLoss() >= 60)
to_chat(user, "\tSevere brain damage detected. Subject likely to have mental retardation.")
else if (M.getBrainLoss() >= 10)
to_chat(user, "\tBrain damage detected. Subject may have had a concussion.")
+ if(advanced)
+ to_chat(user, "\tBrain Activity Level: [100 - M.getBrainLoss()]%.")
+ if (M.radiation)
+ to_chat(user, "\tSubject is irradiated.")
+ if(advanced)
+ to_chat(user, "\tRadiation Level: [M.radiation]%.")
+
+ if(advanced && M.hallucinating())
+ to_chat(user, "\tSubject is hallucinating.")
+
+ //Eyes and ears
+ if(advanced)
+ if(iscarbon(M))
+ var/mob/living/carbon/C = M
+ var/obj/item/organ/ears/ears = C.getorganslot("ears")
+ to_chat(user, "\t==EAR STATUS==")
+ if(istype(ears))
+ var/healthy = TRUE
+ if(C.disabilities & DEAF)
+ healthy = FALSE
+ to_chat(user, "\tSubject is genetically deaf.")
+ else
+ if(ears.ear_damage)
+ to_chat(user, "\tSubject has [ears.ear_damage > UNHEALING_EAR_DAMAGE? "permanent ": "temporary "]hearing damage.")
+ healthy = FALSE
+ if(ears.deaf)
+ to_chat(user, "\tSubject is [ears.ear_damage > UNHEALING_EAR_DAMAGE ? "permanently ": "temporarily "] deaf.")
+ healthy = FALSE
+ if(healthy)
+ to_chat(user, "\tHealthy.")
+ else
+ to_chat(user, "\tSubject does not have ears.")
+ var/obj/item/organ/eyes/eyes = C.getorganslot("eye_sight")
+ to_chat(user, "\t==EYE STATUS==")
+ if(istype(eyes))
+ var/healthy = TRUE
+ if(C.disabilities & BLIND)
+ to_chat(user, "\tSubject is blind.")
+ healthy = FALSE
+ if(C.disabilities & NEARSIGHT)
+ to_chat(user, "\tSubject is nearsighted.")
+ healthy = FALSE
+ if(eyes.eye_damage > 30)
+ to_chat(user, "\tSubject has severe eye damage.")
+ healthy = FALSE
+ else if(eyes.eye_damage > 20)
+ to_chat(user, "\tSubject has significant eye damage.")
+ healthy = FALSE
+ else if(eyes.eye_damage)
+ to_chat(user, "\tSubject has minor eye damage.")
+ healthy = FALSE
+ if(healthy)
+ to_chat(user, "\tHealthy.")
+ else
+ to_chat(user, "\tSubject does not have eyes.")
+
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -177,7 +236,7 @@ MASS SPECTROMETER
to_chat(user, "Body temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)")
// Time of death
- if(M.tod && (M.stat == DEAD || (M.status_flags & FAKEDEATH)))
+ if(M.tod && (M.stat == DEAD || ((M.status_flags & FAKEDEATH) && !advanced)))
to_chat(user, "Time of Death: [M.tod]")
var/tdelta = round(world.time - M.timeofdeath)
if(tdelta < (DEFIB_TIME_LIMIT * 10))
@@ -251,6 +310,12 @@ MASS SPECTROMETER
if(0)
to_chat(usr, "The scanner no longer shows limb damage.")
+/obj/item/device/healthanalyzer/advanced
+ name = "advanced health analyzer"
+ icon_state = "health_adv"
+ desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy."
+ origin_tech = "magnets=3;biotech=3"
+ advanced = TRUE
/obj/item/device/analyzer
desc = "A hand-held environmental scanner which reports current gas levels."
diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm
index 1badef90cf..634fbee9d2 100644
--- a/code/game/objects/items/holy_weapons.dm
+++ b/code/game/objects/items/holy_weapons.dm
@@ -335,8 +335,8 @@
/obj/item/nullrod/carp
name = "carp-sie plushie"
desc = "An adorable stuffed toy that resembles the god of all carp. The teeth look pretty sharp. Activate it to receive the blessing of Carp-Sie."
- icon = 'icons/obj/toy.dmi'
- icon_state = "carpplushie"
+ icon = 'icons/obj/plushes.dmi'
+ icon_state = "carpplush"
item_state = "carp_plushie"
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm
index dc6993cdbe..28856e17d8 100644
--- a/code/game/objects/items/storage/book.dm
+++ b/code/game/objects/items/storage/book.dm
@@ -156,6 +156,24 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
var/unholy2clean = A.reagents.get_reagent_amount("unholywater")
A.reagents.del_reagent("unholywater")
A.reagents.add_reagent("holywater",unholy2clean)
+ if(istype(A, /obj/item/twohanded/required/cult_bastard))
+ var/obj/item/twohanded/required/cult_bastard/sword = A
+ to_chat(user, "You begin to exorcise [sword].")
+ playsound(src,'sound/hallucinations/veryfar_noise.ogg',40,1)
+ if(do_after(user, 40, target = sword))
+ playsound(src,'sound/effects/pray_chaplain.ogg',60,1)
+ for(var/obj/item/device/soulstone/SS in sword.shards)
+ SS.usability = TRUE
+ for(var/mob/living/simple_animal/shade/EX in SS)
+ SSticker.mode.remove_cultist(EX.mind, 1, 0)
+ EX.icon_state = "ghost1"
+ EX.name = "Purified [EX.name]"
+ SS.release_shades(user)
+ qdel(SS)
+ new /obj/item/nullrod/claymore(get_turf(sword))
+ user.visible_message("[user] has purified the [sword]!!")
+ qdel(sword)
+
/obj/item/storage/book/bible/booze
desc = "To be applied to the head repeatedly."
diff --git a/code/game/objects/structures/beds_chairs/bed.dm b/code/game/objects/structures/beds_chairs/bed.dm
index 935b62f46a..5c36c5a6ba 100644
--- a/code/game/objects/structures/beds_chairs/bed.dm
+++ b/code/game/objects/structures/beds_chairs/bed.dm
@@ -21,6 +21,10 @@
var/buildstacktype = /obj/item/stack/sheet/metal
var/buildstackamount = 2
+/obj/structure/bed/examine(mob/user)
+ ..()
+ to_chat(user, "It's held together by a couple of bolts.")
+
/obj/structure/bed/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
if(buildstacktype)
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index aed1679322..9d6985c620 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -1,6 +1,6 @@
/obj/structure/chair
name = "chair"
- desc = "You sit in this. Either by will or force.\nDrag your sprite to sit in the chair. Alt-click to rotate it clockwise."
+ desc = "You sit in this. Either by will or force."
icon = 'icons/obj/chairs.dmi'
icon_state = "chair"
anchored = TRUE
@@ -14,6 +14,14 @@
var/item_chair = /obj/item/chair // if null it can't be picked up
layer = OBJ_LAYER
+/obj/structure/chair/examine(mob/user)
+ ..()
+ to_chat(user, "It's held together by a couple of bolts.")
+ if(!has_buckled_mobs())
+ to_chat(user, "Drag your sprite to sit in it. Alt-click to rotate.")
+ else
+ to_chat(user, "Alt-click to rotate.")
+
/obj/structure/chair/Initialize()
. = ..()
if(!anchored) //why would you put these on the shuttle?
@@ -140,7 +148,7 @@
/obj/structure/chair/comfy
name = "comfy chair"
- desc = "It looks comfy.\nAlt-click to rotate it clockwise."
+ desc = "It looks comfy."
icon_state = "comfychair"
color = rgb(255,255,255)
resistance_flags = FLAMMABLE
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index c7b63be32b..3d77be1bb5 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -76,8 +76,12 @@
/obj/structure/closet/examine(mob/user)
..()
+ if(welded)
+ to_chat(user, "It's welded shut.")
if(anchored)
- to_chat(user, "It is anchored to the ground.")
+ to_chat(user, "It is bolted to the ground.")
+ if(opened)
+ to_chat(user, "The parts are welded together.")
else if(secure && !opened)
to_chat(user, "Alt-click to [locked ? "unlock" : "lock"].")
@@ -453,4 +457,4 @@
/obj/structure/closet/return_temperature()
- return
\ No newline at end of file
+ return
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
index a2a055f464..ee9ecfc908 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
@@ -1,93 +1,94 @@
-/obj/structure/closet/secure_closet/medical1
- name = "medicine closet"
- desc = "Filled to the brim with medical junk."
- icon_state = "med"
+/obj/structure/closet/secure_closet/medical1
+ name = "medicine closet"
+ desc = "Filled to the brim with medical junk."
+ icon_state = "med"
req_access = list(ACCESS_MEDICAL)
-
-/obj/structure/closet/secure_closet/medical1/PopulateContents()
- ..()
- new /obj/item/reagent_containers/glass/beaker(src)
- new /obj/item/reagent_containers/glass/beaker(src)
- new /obj/item/reagent_containers/dropper(src)
- new /obj/item/reagent_containers/dropper(src)
- new /obj/item/storage/belt/medical(src)
- new /obj/item/storage/box/syringes(src)
- new /obj/item/reagent_containers/glass/bottle/toxin(src)
- new /obj/item/reagent_containers/glass/bottle/morphine(src)
- new /obj/item/reagent_containers/glass/bottle/morphine(src)
- for(var/i in 1 to 3)
- new /obj/item/reagent_containers/glass/bottle/epinephrine(src)
- for(var/i in 1 to 3)
- new /obj/item/reagent_containers/glass/bottle/charcoal(src)
- new /obj/item/storage/box/rxglasses(src)
-
-/obj/structure/closet/secure_closet/medical2
- name = "anesthetic closet"
- desc = "Used to knock people out."
+
+/obj/structure/closet/secure_closet/medical1/PopulateContents()
+ ..()
+ new /obj/item/reagent_containers/glass/beaker(src)
+ new /obj/item/reagent_containers/glass/beaker(src)
+ new /obj/item/reagent_containers/dropper(src)
+ new /obj/item/reagent_containers/dropper(src)
+ new /obj/item/storage/belt/medical(src)
+ new /obj/item/storage/box/syringes(src)
+ new /obj/item/reagent_containers/glass/bottle/toxin(src)
+ new /obj/item/reagent_containers/glass/bottle/morphine(src)
+ new /obj/item/reagent_containers/glass/bottle/morphine(src)
+ for(var/i in 1 to 3)
+ new /obj/item/reagent_containers/glass/bottle/epinephrine(src)
+ for(var/i in 1 to 3)
+ new /obj/item/reagent_containers/glass/bottle/charcoal(src)
+ new /obj/item/storage/box/rxglasses(src)
+
+/obj/structure/closet/secure_closet/medical2
+ name = "anesthetic closet"
+ desc = "Used to knock people out."
req_access = list(ACCESS_SURGERY)
-
-/obj/structure/closet/secure_closet/medical2/PopulateContents()
- ..()
- for(var/i in 1 to 3)
- new /obj/item/tank/internals/anesthetic(src)
- for(var/i in 1 to 3)
- new /obj/item/clothing/mask/breath/medical(src)
-
-/obj/structure/closet/secure_closet/medical3
- name = "medical doctor's locker"
+
+/obj/structure/closet/secure_closet/medical2/PopulateContents()
+ ..()
+ for(var/i in 1 to 3)
+ new /obj/item/tank/internals/anesthetic(src)
+ for(var/i in 1 to 3)
+ new /obj/item/clothing/mask/breath/medical(src)
+
+/obj/structure/closet/secure_closet/medical3
+ name = "medical doctor's locker"
req_access = list(ACCESS_SURGERY)
- icon_state = "med_secure"
-
-/obj/structure/closet/secure_closet/medical3/PopulateContents()
- ..()
- new /obj/item/device/radio/headset/headset_med(src)
- new /obj/item/defibrillator/loaded(src)
- new /obj/item/clothing/gloves/color/latex/nitrile(src)
- new /obj/item/storage/belt/medical(src)
- new /obj/item/clothing/glasses/hud/health(src)
- return
-
-/obj/structure/closet/secure_closet/CMO
- name = "\proper chief medical officer's locker"
+ icon_state = "med_secure"
+
+/obj/structure/closet/secure_closet/medical3/PopulateContents()
+ ..()
+ new /obj/item/device/radio/headset/headset_med(src)
+ new /obj/item/defibrillator/loaded(src)
+ new /obj/item/clothing/gloves/color/latex/nitrile(src)
+ new /obj/item/storage/belt/medical(src)
+ new /obj/item/clothing/glasses/hud/health(src)
+ return
+
+/obj/structure/closet/secure_closet/CMO
+ name = "\proper chief medical officer's locker"
req_access = list(ACCESS_CMO)
- icon_state = "cmo"
-
-/obj/structure/closet/secure_closet/CMO/PopulateContents()
- ..()
- new /obj/item/clothing/neck/cloak/cmo(src)
- new /obj/item/storage/backpack/duffelbag/med(src)
- new /obj/item/clothing/suit/bio_suit/cmo(src)
- new /obj/item/clothing/head/bio_hood/cmo(src)
- new /obj/item/clothing/suit/toggle/labcoat/cmo(src)
- new /obj/item/clothing/under/rank/chief_medical_officer(src)
- new /obj/item/clothing/shoes/sneakers/brown (src)
- new /obj/item/cartridge/cmo(src)
- new /obj/item/device/radio/headset/heads/cmo(src)
- new /obj/item/device/megaphone/command(src)
- new /obj/item/defibrillator/compact/loaded(src)
- new /obj/item/clothing/gloves/color/latex/nitrile(src)
- new /obj/item/storage/belt/medical(src)
- new /obj/item/device/assembly/flash/handheld(src)
- new /obj/item/reagent_containers/hypospray/CMO(src)
- new /obj/item/device/autosurgeon/cmo(src)
- new /obj/item/door_remote/chief_medical_officer(src)
-
-/obj/structure/closet/secure_closet/animal
- name = "animal control"
+ icon_state = "cmo"
+
+/obj/structure/closet/secure_closet/CMO/PopulateContents()
+ ..()
+ new /obj/item/clothing/neck/cloak/cmo(src)
+ new /obj/item/storage/backpack/duffelbag/med(src)
+ new /obj/item/clothing/suit/bio_suit/cmo(src)
+ new /obj/item/clothing/head/bio_hood/cmo(src)
+ new /obj/item/clothing/suit/toggle/labcoat/cmo(src)
+ new /obj/item/clothing/under/rank/chief_medical_officer(src)
+ new /obj/item/clothing/shoes/sneakers/brown (src)
+ new /obj/item/cartridge/cmo(src)
+ new /obj/item/device/radio/headset/heads/cmo(src)
+ new /obj/item/device/megaphone/command(src)
+ new /obj/item/defibrillator/compact/loaded(src)
+ new /obj/item/clothing/gloves/color/latex/nitrile(src)
+ new /obj/item/storage/belt/medical(src)
+ new /obj/item/device/healthanalyzer/advanced(src)
+ new /obj/item/device/assembly/flash/handheld(src)
+ new /obj/item/reagent_containers/hypospray/CMO(src)
+ new /obj/item/device/autosurgeon/cmo(src)
+ new /obj/item/door_remote/chief_medical_officer(src)
+
+/obj/structure/closet/secure_closet/animal
+ name = "animal control"
req_access = list(ACCESS_SURGERY)
-
-/obj/structure/closet/secure_closet/animal/PopulateContents()
- ..()
- new /obj/item/device/assembly/signaler(src)
- for(var/i in 1 to 3)
- new /obj/item/device/electropack(src)
-
-/obj/structure/closet/secure_closet/chemical
- name = "chemical closet"
- desc = "Store dangerous chemicals in here."
- icon_door = "chemical"
-
-/obj/structure/closet/secure_closet/chemical/PopulateContents()
- ..()
- new /obj/item/storage/box/pillbottles(src)
- new /obj/item/storage/box/pillbottles(src)
+
+/obj/structure/closet/secure_closet/animal/PopulateContents()
+ ..()
+ new /obj/item/device/assembly/signaler(src)
+ for(var/i in 1 to 3)
+ new /obj/item/device/electropack(src)
+
+/obj/structure/closet/secure_closet/chemical
+ name = "chemical closet"
+ desc = "Store dangerous chemicals in here."
+ icon_door = "chemical"
+
+/obj/structure/closet/secure_closet/chemical/PopulateContents()
+ ..()
+ new /obj/item/storage/box/pillbottles(src)
+ new /obj/item/storage/box/pillbottles(src)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 914be24cfd..1683ac00e6 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -1,5 +1,5 @@
/obj/structure/grille
- desc = "A flimsy lattice of metal rods, with screws to secure it to the floor."
+ desc = "A flimsy framework of metal rods."
name = "grille"
icon = 'icons/obj/structures.dmi'
icon_state = "grille"
@@ -17,6 +17,13 @@
var/grille_type = null
var/broken_type = /obj/structure/grille/broken
+/obj/structure/grille/examine(mob/user)
+ ..()
+ if(anchored)
+ to_chat(user, "It's secured in place with screws. The rods look like they could be cut through.")
+ if(!anchored)
+ to_chat(user, "The anchoring screws are unscrewed. The rods look like they could be cut through.")
+
/obj/structure/grille/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
if(RCD_DECONSTRUCT)
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index d578b307db..c0dcd866d8 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -16,6 +16,13 @@
smooth = SMOOTH_MORE
// flags_1 = CONDUCT_1
+/obj/structure/lattice/examine(mob/user)
+ ..()
+ deconstruction_hints(user)
+
+/obj/structure/lattice/proc/deconstruction_hints(mob/user)
+ to_chat(user, "The rods look like they could be cut. There's space for more rods or a tile.")
+
/obj/structure/lattice/Initialize(mapload)
. = ..()
for(var/obj/structure/lattice/LAT in loc)
@@ -79,6 +86,9 @@
smooth = SMOOTH_TRUE
canSmoothWith = null
+/obj/structure/lattice/catwalk/deconstruction_hints(mob/user)
+ to_chat(user, "The supporting rods look like they could be cut.")
+
/obj/structure/lattice/catwalk/ratvar_act()
new /obj/structure/lattice/catwalk/clockwork(loc)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 116ced183f..c54661066a 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -34,6 +34,13 @@
smooth = SMOOTH_TRUE
canSmoothWith = list(/obj/structure/table, /obj/structure/table/reinforced)
+/obj/structure/table/examine(mob/user)
+ ..()
+ deconstruction_hints(user)
+
+/obj/structure/table/proc/deconstruction_hints(mob/user)
+ to_chat(user, "The top is screwed on, but the main bolts are also visible.")
+
/obj/structure/table/Initialize()
. = ..()
for(var/obj/structure/table/T in src.loc)
@@ -285,7 +292,7 @@
*/
/obj/structure/table/reinforced
name = "reinforced table"
- desc = "A reinforced version of the four legged table, much harder to simply deconstruct."
+ desc = "A reinforced version of the four legged table."
icon = 'icons/obj/smooth_structures/reinforced_table.dmi'
icon_state = "r_table"
deconstruction_ready = 0
@@ -295,6 +302,12 @@
integrity_failure = 50
armor = list(melee = 10, bullet = 30, laser = 30, energy = 100, bomb = 20, bio = 0, rad = 0, fire = 80, acid = 70)
+/obj/structure/table/reinforced/deconstruction_hints(mob/user)
+ if(deconstruction_ready)
+ to_chat(user, "The top cover has been welded loose and the main frame's bolts are exposed.")
+ else
+ to_chat(user, "The top cover is firmly welded on.")
+
/obj/structure/table/reinforced/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weldingtool))
var/obj/item/weldingtool/WT = W
@@ -405,6 +418,10 @@
pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.
max_integrity = 20
+/obj/structure/rack/examine(mob/user)
+ ..()
+ to_chat(user, "It's held together by a couple of bolts.")
+
/obj/structure/rack/CanPass(atom/movable/mover, turf/target)
if(src.density == 0) //Because broken racks -Agouri |TODO: SPRITE!|
return 1
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 02206646d2..97634f7629 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -161,10 +161,10 @@
broken = 0
burnt = 0
if(user && !silent)
- to_chat(user, "You remove the broken plating.")
+ to_chat(user, "You remove the broken plating.")
else
if(user && !silent)
- to_chat(user, "You remove the floor tile.")
+ to_chat(user, "You remove the floor tile.")
if(floor_tile && make_tile)
new floor_tile(src)
return make_plating()
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index a24642c735..428c965197 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -8,10 +8,15 @@
*/
/turf/open/floor/wood
+ desc = "Stylish dark wood."
icon_state = "wood"
floor_tile = /obj/item/stack/tile/wood
broken_states = list("wood-broken", "wood-broken2", "wood-broken3", "wood-broken4", "wood-broken5", "wood-broken6", "wood-broken7")
+/turf/open/floor/wood/examine(mob/user)
+ ..()
+ to_chat(user, "There's a few screws and a small crack visible.")
+
/turf/open/floor/wood/attackby(obj/item/C, mob/user, params)
if(..())
return
@@ -42,16 +47,16 @@
broken = 0
burnt = 0
if(user && !silent)
- to_chat(user, "You remove the broken planks.")
+ to_chat(user, "You remove the broken planks.")
else
if(make_tile)
if(user && !silent)
- to_chat(user, "You unscrew the planks.")
+ to_chat(user, "You unscrew the planks.")
if(floor_tile)
new floor_tile(src)
else
if(user && !silent)
- to_chat(user, "You forcefully pry off the planks, destroying them in the process.")
+ to_chat(user, "You forcefully pry off the planks, destroying them in the process.")
return make_plating()
/turf/open/floor/wood/cold
@@ -78,7 +83,7 @@
if(istype(C, /obj/item/shovel) && params)
new ore_type(src)
new ore_type(src) //Make some sand if you shovel grass
- user.visible_message("[user] digs up [src].", "You [src.turfverb] [src].")
+ user.visible_message("[user] digs up [src].", "You [src.turfverb] [src].")
playsound(src, 'sound/effects/shovel_dig.ogg', 50, 1)
make_plating()
if(..())
@@ -143,6 +148,10 @@
canSmoothWith = list(/turf/open/floor/carpet)
flags_1 = NONE
+/turf/open/floor/carpet/examine(mob/user)
+ ..()
+ to_chat(user, "There's a small crack on the edge of it.")
+
/turf/open/floor/carpet/Initialize()
. = ..()
update_icon()
diff --git a/code/game/turfs/simulated/floor/light_floor.dm b/code/game/turfs/simulated/floor/light_floor.dm
index 247382d03c..0dd3673f6a 100644
--- a/code/game/turfs/simulated/floor/light_floor.dm
+++ b/code/game/turfs/simulated/floor/light_floor.dm
@@ -12,6 +12,10 @@
var/can_modify_colour = TRUE
+/turf/open/floor/light/examine(mob/user)
+ ..()
+ to_chat(user, "There's a small crack on the edge of it.")
+
/turf/open/floor/light/Initialize()
. = ..()
update_icon()
diff --git a/code/game/turfs/simulated/floor/plasteel_floor.dm b/code/game/turfs/simulated/floor/plasteel_floor.dm
index 63c883f88c..b194127de8 100644
--- a/code/game/turfs/simulated/floor/plasteel_floor.dm
+++ b/code/game/turfs/simulated/floor/plasteel_floor.dm
@@ -4,6 +4,10 @@
broken_states = list("damaged1", "damaged2", "damaged3", "damaged4", "damaged5")
burnt_states = list("floorscorched1", "floorscorched2")
+/turf/open/floor/plasteel/examine(mob/user)
+ ..()
+ to_chat(user, "There's a small crack on the edge of it.")
+
/turf/open/floor/plasteel/update_icon()
if(!..())
return 0
diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm
index 8ad6657784..3e16da8b9f 100644
--- a/code/game/turfs/simulated/floor/plating.dm
+++ b/code/game/turfs/simulated/floor/plating.dm
@@ -12,6 +12,13 @@
icon_state = "plating"
intact = FALSE
+/turf/open/floor/plating/examine(mob/user)
+ ..()
+ if(broken || burnt)
+ to_chat(user, "It looks like the dents could be welded smooth.")
+ return
+ to_chat(user, "There are few attachment holes for a new tile or reinforcement rods.")
+
/turf/open/floor/plating/Initialize()
if (!broken_states)
broken_states = list("platingdmg1", "platingdmg2", "platingdmg3")
diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm
index 2649a3a14d..848a7e8219 100644
--- a/code/game/turfs/simulated/floor/plating/asteroid.dm
+++ b/code/game/turfs/simulated/floor/plating/asteroid.dm
@@ -193,7 +193,7 @@
var/list/L = list(45)
if(IsOdd(dir2angle(dir))) // We're going at an angle and we want thick angled tunnels.
- L += -45
+ L -= 45
// Expand the edges of our tunnel
for(var/edge_angle in L)
diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm
index 04c97aa7d5..74354e78c7 100644
--- a/code/game/turfs/simulated/floor/reinf_floor.dm
+++ b/code/game/turfs/simulated/floor/reinf_floor.dm
@@ -1,11 +1,16 @@
/turf/open/floor/engine
name = "reinforced floor"
+ desc = "Extremely sturdy."
icon_state = "engine"
thermal_conductivity = 0.025
heat_capacity = INFINITY
floor_tile = /obj/item/stack/rods
+/turf/open/floor/engine/examine(mob/user)
+ ..()
+ to_chat(user, "The reinforcement rods are wrenched firmly in place.")
+
/turf/open/floor/engine/airless
initial_gas_mix = "TEMP=2.7"
diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm
index ae5f5c2fbb..33f02bc759 100644
--- a/code/modules/library/lib_items.dm
+++ b/code/modules/library/lib_items.dm
@@ -25,6 +25,21 @@
var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/book) //Things allowed in the bookcase
+/obj/structure/bookcase/examine(mob/user)
+ ..()
+ if(!anchored)
+ to_chat(user, "The bolts on the bottom are unsecured.")
+ if(anchored)
+ to_chat(user, "It's secured in place with bolts.")
+ switch(state)
+ if(0)
+ to_chat(user, "There's a small crack visible on the back panel.")
+ if(1)
+ to_chat(user, "There's space inside for a wooden shelf.")
+ if(2)
+ to_chat(user, "There's a small crack visible on the shelf.")
+
+
/obj/structure/bookcase/Initialize(mapload)
. = ..()
if(!mapload)
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index cc98fd827e..831fde141e 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -59,6 +59,7 @@
if(previewJob && !nude)
mannequin.job = previewJob.title
previewJob.equip(mannequin, TRUE)
+ mannequin.compile_overlays()
CHECK_TICK
preview_icon = icon('icons/effects/effects.dmi', "nothing")
preview_icon.Scale(48+32, 16+32)
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index fd7b8a8db3..aa15e28e66 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -44,7 +44,7 @@
//Update the body's icon so it doesnt appear debrained anymore
C.update_hair()
-/obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0,no_id_transfer = FALSE)
+/obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0, no_id_transfer = FALSE)
..()
if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !no_id_transfer)
transfer_identity(C)
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 6068ef2d66..d90db23679 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -250,6 +250,9 @@
if(health >= 0 && !(status_flags & FAKEDEATH))
if(lying)
+ if(buckled)
+ to_chat(M, "You need to unbuckle [src] first to do that!")
+ return
M.visible_message("[M] shakes [src] trying to get [p_them()] up!", \
"You shake [src] trying to get [p_them()] up!")
else if(check_zone(M.zone_selected) == "head")
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 70a5f5225c..63b84b9cf3 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -4,6 +4,7 @@
voice_name = "Unknown"
icon = 'icons/mob/human.dmi'
icon_state = "caucasian_m"
+ appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE
/mob/living/carbon/human/Initialize()
verbs += /mob/living/proc/mob_sleep
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index ef044e462b..84bf5bdf80 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -37,7 +37,7 @@
if(H.nutrition > NUTRITION_LEVEL_FULL)
H.nutrition = NUTRITION_LEVEL_FULL
if(light_amount > 0.2) //if there's enough light, heal
- H.heal_overall_damage(0.05,0)
+ H.heal_overall_damage(0.75,0)
H.adjustOxyLoss(-0.5)
if(H.nutrition < NUTRITION_LEVEL_STARVING + 55)
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index de61ef7220..4347a8331c 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -1,3 +1,5 @@
+#define HEART_RESPAWN_THRESHHOLD 40
+
/datum/species/shadow
// Humans cursed to stay in the darkness, lest their life forces drain. They regain health in shadow and die in light.
name = "???"
@@ -32,19 +34,14 @@
no_equip = list(slot_wear_mask, slot_wear_suit, slot_gloves, slot_shoes, slot_w_uniform, slot_s_store)
species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,NO_UNDERWEAR,NOHUNGER,NO_DNA_COPY,NOTRANSSTING)
mutanteyes = /obj/item/organ/eyes/night_vision/nightmare
- var/obj/effect/proc_holder/spell/targeted/shadowwalk/shadowwalk
+ mutant_organs = list(/obj/item/organ/heart/nightmare)
+ mutant_brain = /obj/item/organ/brain/nightmare
- var/info_text = "You are a Nightmare. The ability shadow walk allows unlimited, unrestricted movement in the dark using. \
- Your light eater will destroy any light producing objects you attack, as well as destroy any lights a living creature may be holding. You will automatically dodge gunfire and melee attacks when on a dark tile."
+ var/info_text = "You are a Nightmare. The ability shadow walk allows unlimited, unrestricted movement in the dark while activated. \
+ Your light eater will destroy any light producing objects you attack, as well as destroy any lights a living creature may be holding. You will automatically dodge gunfire and melee attacks when on a dark tile. If killed, you will eventually revive if left in darkness."
/datum/species/shadow/nightmare/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
- var/obj/effect/proc_holder/spell/targeted/shadowwalk/SW = new
- C.AddSpell(SW)
- shadowwalk = SW
- var/obj/item/light_eater/blade = new
- C.put_in_hands(blade)
-
to_chat(C, "[info_text]")
C.real_name = "[pick(GLOB.nightmare_names)]"
@@ -53,11 +50,6 @@
C.mind.name = C.real_name
C.dna.real_name = C.real_name
-/datum/species/shadow/nightmare/on_species_loss(mob/living/carbon/C)
- . = ..()
- if(shadowwalk)
- C.RemoveSpell(shadowwalk)
-
/datum/species/shadow/nightmare/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H)
var/turf/T = H.loc
if(istype(T))
@@ -68,6 +60,93 @@
return -1
return 0
+
+
+//Organs
+
+/obj/item/organ/brain/nightmare
+ name = "tumorous mass"
+ desc = "A fleshy growth that was dug out of the skull of a Nightmare."
+ icon_state = "brain-x-d"
+ var/obj/effect/proc_holder/spell/targeted/shadowwalk/shadowwalk
+
+/obj/item/organ/brain/nightmare/Insert(mob/living/carbon/M, special = 0)
+ ..()
+ if(M.dna.species.id != "nightmare")
+ M.set_species(/datum/species/shadow/nightmare)
+ visible_message("[M] thrashes as [src] takes root in their body!")
+ var/obj/effect/proc_holder/spell/targeted/shadowwalk/SW = new
+ M.AddSpell(SW)
+ shadowwalk = SW
+
+
+/obj/item/organ/brain/nightmare/Remove(mob/living/carbon/M, special = 0)
+ if(shadowwalk)
+ M.RemoveSpell(shadowwalk)
+ ..()
+
+
+/obj/item/organ/heart/nightmare
+ name = "heart of darkness"
+ desc = "An alien organ that twists and writhes when exposed to light."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "demon_heart-on"
+ color = "#1C1C1C"
+ var/respawn_progress = 0
+ var/obj/item/light_eater/blade
+
+
+/obj/item/organ/heart/nightmare/attack(mob/M, mob/living/carbon/user, obj/target)
+ if(M != user)
+ return ..()
+ user.visible_message("[user] raises [src] to their mouth and tears into it with their teeth!", \
+ "[src] feels unnaturally cold in your hands. You raise [src] your mouth and devour it!")
+ playsound(user, 'sound/magic/demon_consume.ogg', 50, 1)
+
+
+ user.visible_message("Blood erupts from [user]'s arm as it reforms into a weapon!", \
+ "Icy blood pumps through your veins as your arm reforms itself!")
+ user.temporarilyRemoveItemFromInventory(src, TRUE)
+ Insert(user)
+
+/obj/item/organ/heart/nightmare/Insert(mob/living/carbon/M, special = 0)
+ ..()
+ blade = new/obj/item/light_eater
+ M.put_in_hands(blade)
+ START_PROCESSING(SSobj, src)
+
+/obj/item/organ/heart/nightmare/Remove(mob/living/carbon/M, special = 0)
+ STOP_PROCESSING(SSobj, src)
+ respawn_progress = 0
+ if(blade)
+ QDEL_NULL(blade)
+ M.visible_message("\The [blade] disintegrates!")
+ ..()
+
+/obj/item/organ/heart/nightmare/Stop()
+ return 0
+
+/obj/item/organ/heart/nightmare/update_icon()
+ return //always beating visually
+
+/obj/item/organ/heart/nightmare/process()
+ if(QDELETED(owner) || owner.stat != DEAD)
+ respawn_progress = 0
+ return
+ var/turf/T = get_turf(owner)
+ if(istype(T))
+ var/light_amount = T.get_lumcount()
+ if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD)
+ respawn_progress++
+ playsound(owner,'sound/effects/singlebeat.ogg',40,1)
+ if(respawn_progress >= HEART_RESPAWN_THRESHHOLD)
+ owner.revive(full_heal = TRUE)
+ owner.visible_message("[owner] staggers to their feet!")
+ playsound(owner, 'sound/hallucinations/far_noise.ogg', 50, 1)
+ respawn_progress = 0
+
+//Weapon
+
/obj/item/light_eater
name = "light eater"
icon_state = "arm_blade"
@@ -113,3 +192,5 @@
visible_message("[O] is disintegrated by [src]!")
O.burn()
playsound(src, 'sound/items/welder.ogg', 50, 1)
+
+#undef HEART_RESPAWN_THRESHHOLD
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm
index 4ce8570c2b..016a3635ef 100644
--- a/code/modules/mob/living/carbon/human/species_types/zombies.dm
+++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm
@@ -37,6 +37,7 @@
C.a_intent = INTENT_HARM // THE SUFFERING MUST FLOW
if(regen_cooldown < world.time)
C.heal_overall_damage(4,4)
+ C.adjustToxLoss(-4)
if(prob(4))
playsound(C, pick(spooks), 50, TRUE, 10)
if(C.InCritical())
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/visuals_icons.dm b/code/modules/mob/living/simple_animal/friendly/drone/visuals_icons.dm
index 259ca4327d..325b971171 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/visuals_icons.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/visuals_icons.dm
@@ -86,7 +86,7 @@
if(istype(head, /obj/item/clothing/mask))
used_head_icon = 'icons/mob/mask.dmi'
var/mutable_appearance/head_overlay = head.build_worn_icon(state = head.icon_state, default_layer = DRONE_HEAD_LAYER, default_icon_file = used_head_icon)
- head_overlay.pixel_y += -15
+ head_overlay.pixel_y -= 15
drone_overlays[DRONE_HEAD_LAYER] = head_overlay
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm
index dda0b3abad..04282d12fd 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm
@@ -62,6 +62,9 @@
return TRUE
+ if(isobj(the_target) && is_type_in_typecache(the_target, wanted_objects))
+ return TRUE
+
return FALSE
/mob/living/simple_animal/hostile/asteroid/gutlunch/Destroy()
diff --git a/code/modules/ninja/suit/gloves.dm b/code/modules/ninja/suit/gloves.dm
index 62b8cee415..8eaaa40097 100644
--- a/code/modules/ninja/suit/gloves.dm
+++ b/code/modules/ninja/suit/gloves.dm
@@ -63,7 +63,7 @@
if(isnum(.)) //Numerical values of drained handle their feedback here, Alpha values handle it themselves (Research hacking)
if(.)
- to_chat(H, "Gained [.] energy from \the [A].")
+ to_chat(H, "Gained [DisplayPower(.)] of energy from [A].")
else
to_chat(H, "\The [A] has run dry of power, you must find another source!")
else
diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm
index 4519835c1f..f04c61426b 100644
--- a/code/modules/power/singularity/particle_accelerator/particle.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle.dm
@@ -1,62 +1,65 @@
-/obj/effect/accelerated_particle
- name = "Accelerated Particles"
- desc = "Small things moving very fast."
- icon = 'icons/obj/machines/particle_accelerator.dmi'
- icon_state = "particle"
+/obj/effect/accelerated_particle
+ name = "Accelerated Particles"
+ desc = "Small things moving very fast."
+ icon = 'icons/obj/machines/particle_accelerator.dmi'
+ icon_state = "particle"
anchored = TRUE
density = FALSE
- var/movement_range = 10
- var/energy = 10
- var/speed = 1
-
-/obj/effect/accelerated_particle/weak
- movement_range = 8
- energy = 5
-
-/obj/effect/accelerated_particle/strong
- movement_range = 15
- energy = 15
-
-/obj/effect/accelerated_particle/powerful
- movement_range = 20
- energy = 50
-
-
-/obj/effect/accelerated_particle/New(loc)
- ..()
-
- addtimer(CALLBACK(src, .proc/move), 1)
-
-
+ var/movement_range = 10
+ var/energy = 10
+ var/speed = 1
+
+/obj/effect/accelerated_particle/weak
+ movement_range = 8
+ energy = 5
+
+/obj/effect/accelerated_particle/strong
+ movement_range = 15
+ energy = 15
+
+/obj/effect/accelerated_particle/powerful
+ movement_range = 20
+ energy = 50
+
+
+/obj/effect/accelerated_particle/New(loc)
+ ..()
+
+ addtimer(CALLBACK(src, .proc/move), 1)
+
+
/obj/effect/accelerated_particle/Collide(atom/A)
- if(A)
- if(isliving(A))
- toxmob(A)
- else if(istype(A, /obj/machinery/the_singularitygen))
- var/obj/machinery/the_singularitygen/S = A
- S.energy += energy
- else if(istype(A, /obj/singularity))
- var/obj/singularity/S = A
- S.energy += energy
-
-
-/obj/effect/accelerated_particle/Crossed(atom/A)
- if(isliving(A))
- toxmob(A)
-
-
-/obj/effect/accelerated_particle/ex_act(severity, target)
- qdel(src)
-
-/obj/effect/accelerated_particle/proc/toxmob(mob/living/M)
- M.rad_act(energy*6)
-
-/obj/effect/accelerated_particle/proc/move()
- if(!step(src,dir))
- forceMove(get_step(src,dir))
- movement_range--
- if(movement_range == 0)
- qdel(src)
- else
- sleep(speed)
- move()
+ if(A)
+ if(isliving(A))
+ toxmob(A)
+ else if(istype(A, /obj/machinery/the_singularitygen))
+ var/obj/machinery/the_singularitygen/S = A
+ S.energy += energy
+ else if(istype(A, /obj/singularity))
+ var/obj/singularity/S = A
+ S.energy += energy
+ else if(istype(A, /obj/structure/blob))
+ var/obj/structure/blob/B = A
+ B.take_damage(energy*0.6)
+ movement_range = 0
+
+/obj/effect/accelerated_particle/Crossed(atom/A)
+ if(isliving(A))
+ toxmob(A)
+
+
+/obj/effect/accelerated_particle/ex_act(severity, target)
+ qdel(src)
+
+/obj/effect/accelerated_particle/proc/toxmob(mob/living/M)
+ M.rad_act(energy*6)
+
+/obj/effect/accelerated_particle/proc/move()
+ if(!step(src,dir))
+ forceMove(get_step(src,dir))
+ movement_range--
+ if(movement_range == 0)
+ qdel(src)
+ else
+ sleep(speed)
+ move()
diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm
index 603f8843ca..5ee2ecec05 100644
--- a/code/modules/projectiles/guns/misc/blastcannon.dm
+++ b/code/modules/projectiles/guns/misc/blastcannon.dm
@@ -12,21 +12,17 @@
randomspread = FALSE
var/obj/item/device/transfer_valve/bomb
- var/datum/gas_mixture/air1
- var/datum/gas_mixture/air2
/obj/item/gun/blastcannon/New()
if(!pin)
pin = new
- . = ..()
+ return ..()
/obj/item/gun/blastcannon/Destroy()
if(bomb)
qdel(bomb)
bomb = null
- air1 = null
- air2 = null
- . = ..()
+ return ..()
/obj/item/gun/blastcannon/attack_self(mob/user)
if(bomb)
@@ -35,7 +31,7 @@
user.visible_message("[user] detaches the [bomb] from the [src]")
bomb = null
update_icon()
- . = ..(user)
+ return ..()
/obj/item/gun/blastcannon/update_icon()
if(bomb)
@@ -46,7 +42,6 @@
icon_state = initial(icon_state)
name = initial(name)
desc = initial(desc)
- . = ..()
/obj/item/gun/blastcannon/attackby(obj/O, mob/user)
if(istype(O, /obj/item/device/transfer_valve))
@@ -59,20 +54,17 @@
return FALSE
user.visible_message("[user] attaches the [O] to the [src]!")
bomb = O
- O.loc = src
+ O.forceMove(src)
update_icon()
return TRUE
- . = ..()
+ return ..()
/obj/item/gun/blastcannon/proc/calculate_bomb()
if(!istype(bomb)||!istype(bomb.tank_one)||!istype(bomb.tank_two))
return 0
- air1 = bomb.tank_one.air_contents
- air2 = bomb.tank_two.air_contents
- var/datum/gas_mixture/temp
- temp.volume = air1.volume + air2.volume
- temp.merge(air1)
- temp.merge(air2)
+ var/datum/gas_mixture/temp = new(60) //directional buff.
+ temp.merge(bomb.tank_one.air_contents.remove_ratio(1))
+ temp.merge(bomb.tank_two.air_contents.remove_ratio(2))
for(var/i in 1 to 6)
temp.react()
var/pressure = temp.return_pressure()
@@ -82,7 +74,7 @@
return (pressure/TANK_FRAGMENT_SCALE)
/obj/item/gun/blastcannon/afterattack(atom/target, mob/user, flag, params)
- if((!bomb) || (target == user) || (target.loc == user) || (!target) || (target.loc == user.loc) || (target.loc in range(user, 2)) || (target in range(user, 2)))
+ if((!bomb) || (!target) || (get_dist(get_turf(target), get_turf(user)) <= 2))
return ..()
var/power = calculate_bomb()
qdel(bomb)
@@ -90,13 +82,17 @@
var/heavy = power * 0.2
var/medium = power * 0.5
var/light = power
- user.visible_message("[user] opens \the [bomb] on \his [src.name] and fires a blast wave at \the [target]!","You open \the [bomb] on your [src.name] and fire a blast wave at \the [target]!")
+ user.visible_message("[user] opens \the [bomb] on \his [name] and fires a blast wave at \the [target]!","You open \the [bomb] on your [name] and fire a blast wave at \the [target]!")
playsound(user, "explosion", 100, 1)
var/turf/starting = get_turf(user)
+ var/turf/targturf = get_turf(target)
var/area/A = get_area(user)
- var/log_str = "Blast wave fired at [ADMIN_COORDJMP(starting)] ([A.name]) by [user.name]([user.ckey]) with power [heavy]/[medium]/[light]."
+ var/log_str = "Blast wave fired from [ADMIN_COORDJMP(starting)] ([A.name]) at [ADMIN_COORDJMP(targturf)] ([target.name]) by [user.name]([user.ckey]) with power [heavy]/[medium]/[light]."
message_admins(log_str)
log_game(log_str)
+ var/obj/item/projectile/blastwave/BW = new(loc, heavy, medium, light)
+ BW.preparePixelProjectile(target, get_turf(target), user, params, 0)
+ BW.fire()
/obj/item/projectile/blastwave
name = "blast wave"
@@ -109,6 +105,12 @@
var/lightr = 0
range = 150
+/obj/item/projectile/blastwave/Initialize(mapload, _h, _m, _l)
+ heavyr = _h
+ mediumr = _m
+ lightr = _l
+ return ..()
+
/obj/item/projectile/blastwave/Range()
..()
if(heavyr)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 3aba80fe64..937326a60b 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -90,8 +90,6 @@
new impact_effect_type(target_loca, target, src)
return 0
var/mob/living/L = target
- if(L.buckled && ismob(L.buckled))
- L = L.buckled
if(blocked != 100) // not completely blocked
if(damage && L.blood_volume && damage_type == BRUTE)
var/splatter_dir = dir
@@ -178,6 +176,8 @@
var/mob/living/picked_mob = pick(mobs_list)
if(!prehit(picked_mob))
return FALSE
+ if(ismob(picked_mob.buckled))
+ picked_mob = picked_mob.buckled
picked_mob.bullet_act(src, def_zone)
qdel(src)
return TRUE
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 6f4ae50243..8395ef4d83 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -10,7 +10,7 @@
/obj/item/projectile/bullet/pellet/Range()
..()
- damage += -0.75
+ damage -= 0.75
if(damage < 0)
qdel(src)
@@ -66,7 +66,7 @@
/obj/item/projectile/bullet/c38
name = ".38 bullet"
damage = 15
- knockdown = 30
+ knockdown = 60
stamina = 50
// 10mm (Stechkin)
diff --git a/code/modules/server_tools/st_interface.dm b/code/modules/server_tools/st_interface.dm
index 12fb5a85c2..10b7f920e8 100644
--- a/code/modules/server_tools/st_interface.dm
+++ b/code/modules/server_tools/st_interface.dm
@@ -89,6 +89,8 @@ SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(server_tools_api_compatible, FALSE)
return "No message set!"
SERVER_TOOLS_WORLD_ANNOUNCE(msg)
return "SUCCESS"
+ if(SERVICE_CMD_PLAYER_COUNT)
+ return "[SERVER_TOOLS_CLIENT_COUNT]"
if(SERVICE_CMD_LIST_CUSTOM)
return json_encode(ListServiceCustomCommands(FALSE))
else
diff --git a/code/modules/spells/spell_types/shadow_walk.dm b/code/modules/spells/spell_types/shadow_walk.dm
index 45ea521098..9f9b4ac223 100644
--- a/code/modules/spells/spell_types/shadow_walk.dm
+++ b/code/modules/spells/spell_types/shadow_walk.dm
@@ -25,8 +25,9 @@
if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD)
playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1)
visible_message("[user] melts into the shadows!")
- user.AdjustStun(-20, 0)
- user.AdjustKnockdown(-20, 0)
+ user.SetStun(0, FALSE)
+ user.SetKnockdown(0, FALSE)
+ user.setStaminaLoss(0, 0)
var/obj/effect/dummy/shadow/S2 = new(get_turf(user.loc))
user.forceMove(S2)
S2.jaunter = user
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 4b939bf4f1..3b83dd8e62 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -44,9 +44,11 @@
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
actions_types = list(/datum/action/item_action/organ_action/use)
+ sight_flags = SEE_BLACKNESS
var/night_vision = TRUE
/obj/item/organ/eyes/night_vision/ui_action_click()
+ sight_flags = initial(sight_flags)
switch(lighting_alpha)
if (LIGHTING_PLANE_ALPHA_VISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
@@ -56,6 +58,7 @@
lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE
else
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
+ sight_flags &= ~SEE_BLACKNESS
owner.update_sight()
/obj/item/organ/eyes/night_vision/alien
diff --git a/config/config.txt b/config/config.txt
index d5d2725714..72f168dcce 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -1,3 +1,11 @@
+# You can use the @ character at the beginning of a config option to lock it from being edited in-game
+# Example usage:
+# @SERVERNAME tgstation
+# Which sets the SERVERNAME, and disallows admins from being able to change it using View Variables.
+# @LOG_TWITTER 0
+# Which explicitly disables LOG_TWITTER, as well as locking it.
+# There are various options which are hard-locked for security reasons.
+
## Server name: This appears at the top of the screen in-game and in the BYOND hub. Uncomment and replace 'tgstation' with the name of your choice.
# SERVERNAME tgstation
diff --git a/html/changelogs/AutoChangeLog-pr-3041.yml b/html/changelogs/AutoChangeLog-pr-3041.yml
new file mode 100644
index 0000000000..872bbc4b99
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3041.yml
@@ -0,0 +1,5 @@
+author: "Robustin"
+delete-after: True
+changes:
+ - rscadd: "Blood cultists can now create a unique bastard sword at their forge"
+ - rscdel: "Blood cultists can no longer obtain a hardsuit from the forge"
diff --git a/html/changelogs/AutoChangeLog-pr-3104.yml b/html/changelogs/AutoChangeLog-pr-3104.yml
new file mode 100644
index 0000000000..e6f9d22374
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3104.yml
@@ -0,0 +1,4 @@
+author: "More Robust Than You"
+delete-after: True
+changes:
+ - balance: "Blobs now take damage from particle accelerators"
diff --git a/html/changelogs/AutoChangeLog-pr-3105.yml b/html/changelogs/AutoChangeLog-pr-3105.yml
new file mode 100644
index 0000000000..b48644c148
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3105.yml
@@ -0,0 +1,4 @@
+author: "CitadelStationBot"
+delete-after: True
+changes:
+ - balance: "The detectives gun has been restored from a system change that nerfed knockdown in general. We need to have a serious discussion about anti revolver hate in this community."
diff --git a/html/changelogs/AutoChangeLog-pr-3108.yml b/html/changelogs/AutoChangeLog-pr-3108.yml
new file mode 100644
index 0000000000..f1e03db19a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3108.yml
@@ -0,0 +1,4 @@
+author: "XDTM"
+delete-after: True
+changes:
+ - rscadd: "The CMO now has an advanced health analyzer in his closet! It can give more precise readings on the non-standard damage types."
diff --git a/html/changelogs/AutoChangeLog-pr-3110.yml b/html/changelogs/AutoChangeLog-pr-3110.yml
new file mode 100644
index 0000000000..7d06447f99
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3110.yml
@@ -0,0 +1,4 @@
+author: "Kor"
+delete-after: True
+changes:
+ - rscadd: "Nightmares now have mutant hearts and brains, with their own special properties when consumed or implanted in mobs. Experiment!"
diff --git a/html/changelogs/AutoChangeLog-pr-3114.yml b/html/changelogs/AutoChangeLog-pr-3114.yml
new file mode 100644
index 0000000000..abe6380887
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3114.yml
@@ -0,0 +1,4 @@
+author: "Daniel0Mclovin"
+delete-after: True
+changes:
+ - tweak: "Modify health regeneration from 0.05 to 0.75"
diff --git a/html/changelogs/AutoChangeLog-pr-3115.yml b/html/changelogs/AutoChangeLog-pr-3115.yml
new file mode 100644
index 0000000000..c3a194f971
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3115.yml
@@ -0,0 +1,4 @@
+author: "Naksu"
+delete-after: True
+changes:
+ - bugfix: "Gutlunches will now once again look for gibs to eat."
diff --git a/html/changelogs/AutoChangeLog-pr-3127.yml b/html/changelogs/AutoChangeLog-pr-3127.yml
new file mode 100644
index 0000000000..050e9cb4d7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3127.yml
@@ -0,0 +1,4 @@
+author: "Raeschen"
+delete-after: True
+changes:
+ - rscadd: "FTL Milky Way added to music"
diff --git a/html/changelogs/AutoChangeLog-pr-3133.yml b/html/changelogs/AutoChangeLog-pr-3133.yml
new file mode 100644
index 0000000000..ac49ae1821
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3133.yml
@@ -0,0 +1,4 @@
+author: "CitadelStationBot"
+delete-after: True
+changes:
+ - spellcheck: "Beds, chairs, closets, grilles, lattices, catwalks, tables, racks, floors, plating and bookcases now show hints about constructing/deconstructing them."
diff --git a/html/changelogs/AutoChangeLog-pr-3134.yml b/html/changelogs/AutoChangeLog-pr-3134.yml
new file mode 100644
index 0000000000..5b8bcf21b6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3134.yml
@@ -0,0 +1,4 @@
+author: "ShizCalev"
+delete-after: True
+changes:
+ - bugfix: "You now have to unbuckle a player PRIOR to shaking them up off a bed or resin nest."
diff --git a/icons/mob/inhands/64x64_lefthand.dmi b/icons/mob/inhands/64x64_lefthand.dmi
index a9a952733a..1b450ab3e0 100644
Binary files a/icons/mob/inhands/64x64_lefthand.dmi and b/icons/mob/inhands/64x64_lefthand.dmi differ
diff --git a/icons/mob/inhands/64x64_righthand.dmi b/icons/mob/inhands/64x64_righthand.dmi
index bff96a991b..bbeddf9152 100644
Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ
diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi
index 21ab5cf9f5..64a877c2a1 100644
Binary files a/icons/obj/abductor.dmi and b/icons/obj/abductor.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 79f0a207d4..71ce7d6df5 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index 94c0c03835..b5ac65c2a8 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/sound/music/milkyway.ogg b/sound/music/milkyway.ogg
new file mode 100644
index 0000000000..59d9ba63f4
Binary files /dev/null and b/sound/music/milkyway.ogg differ
diff --git a/sound/weapons/fwoosh.wav b/sound/weapons/fwoosh.wav
new file mode 100644
index 0000000000..1723c4e9b8
Binary files /dev/null and b/sound/weapons/fwoosh.wav differ
diff --git a/sound/weapons/parry.ogg b/sound/weapons/parry.ogg
new file mode 100644
index 0000000000..1f86716337
Binary files /dev/null and b/sound/weapons/parry.ogg differ
diff --git a/strings/round_start_sounds.txt b/strings/round_start_sounds.txt
index 01323a6120..177f612ee1 100644
--- a/strings/round_start_sounds.txt
+++ b/strings/round_start_sounds.txt
@@ -20,3 +20,4 @@ sound/music/oceanman.ogg
sound/music/indeep.ogg
sound/music/goodbyemoonmen.ogg
sound/music/flytothemoon_otomatone.ogg
+sound/music/milkyway.ogg
\ No newline at end of file