diff --git a/_maps/shuttles/emergency_arena.dmm b/_maps/shuttles/emergency_arena.dmm
index 1314b8a732..2ba9206830 100644
--- a/_maps/shuttles/emergency_arena.dmm
+++ b/_maps/shuttles/emergency_arena.dmm
@@ -43,7 +43,7 @@
},
/area/shuttle/escape/arena)
"l" = (
-/obj/structure/closet/crate/necropolis/tendril,
+/obj/structure/closet/crate/necropolis/tendril/magic,
/turf/open/indestructible/necropolis/air,
/area/shuttle/escape/arena)
"m" = (
@@ -65,10 +65,18 @@
/obj/structure/healingfountain,
/turf/open/indestructible/necropolis/air,
/area/shuttle/escape/arena)
+"t" = (
+/obj/structure/closet/crate/necropolis/tendril/misc,
+/turf/open/indestructible/necropolis/air,
+/area/shuttle/escape/arena)
"z" = (
/obj/effect/landmark/shuttle_arena_safe,
/turf/open/indestructible/necropolis/air,
/area/shuttle/escape/arena)
+"H" = (
+/obj/structure/closet/crate/necropolis/tendril/weapon_armor,
+/turf/open/indestructible/necropolis/air,
+/area/shuttle/escape/arena)
(1,1,1) = {"
a
@@ -264,16 +272,16 @@ m
l
j
m
-l
+H
j
k
-l
+H
j
m
-l
+t
j
m
-j
+t
j
p
g
@@ -524,16 +532,16 @@ m
l
j
m
-l
+H
j
m
-l
+H
j
m
-l
+t
j
m
-j
+t
j
p
g
diff --git a/byond-extools.dll b/byond-extools.dll
index bd6b34c48e..718b475f9a 100644
Binary files a/byond-extools.dll and b/byond-extools.dll differ
diff --git a/code/__DEFINES/_extools.dm b/code/__DEFINES/_extools.dm
new file mode 100644
index 0000000000..4513243aae
--- /dev/null
+++ b/code/__DEFINES/_extools.dm
@@ -0,0 +1 @@
+#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so")
diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index 621af2c811..dad2a38afd 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -281,8 +281,6 @@ GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
#define CALCULATE_ADJACENT_TURFS(T) SSadjacent_air.queue[T] = 1
#endif
-#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so")
-
GLOBAL_VAR(atmos_extools_initialized) // this must be an uninitialized (null) one or init_monstermos will be called twice because reasons
#define ATMOS_EXTOOLS_CHECK if(!GLOB.atmos_extools_initialized){\
GLOB.atmos_extools_initialized=TRUE;\
diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm
new file mode 100644
index 0000000000..35d99a982d
--- /dev/null
+++ b/code/__DEFINES/research/anomalies.dm
@@ -0,0 +1,21 @@
+// Anomaly core types
+/// Bluespace cores
+#define ANOMALY_CORE_BLUESPACE /obj/item/assembly/signaler/anomaly/bluespace
+/// Gravitational cores
+#define ANOMALY_CORE_GRAVITATIONAL /obj/item/assembly/signaler/anomaly/grav
+/// Flux
+#define ANOMALY_CORE_FLUX /obj/item/assembly/signaler/anomaly/flux
+/// Vortex
+#define ANOMALY_CORE_VORTEX /obj/item/assembly/signaler/anomaly/vortex
+/// Pyro
+#define ANOMALY_CORE_PYRO /obj/item/assembly/signaler/anomaly/pyro
+
+// Max amounts of cores you can make
+#define MAX_CORES_BLUESPACE 8
+#define MAX_CORES_GRAVITATIONAL 8
+#define MAX_CORES_FLUX 8
+#define MAX_CORES_VORTEX 8
+#define MAX_CORES_PYRO 8
+
+/// chance supermatter anomalies drop real cores
+#define SUPERMATTER_ANOMALY_DROP_CHANCE 20
diff --git a/code/__HELPERS/_extools_api.dm b/code/__HELPERS/_extools_api.dm
new file mode 100644
index 0000000000..af348dc939
--- /dev/null
+++ b/code/__HELPERS/_extools_api.dm
@@ -0,0 +1,5 @@
+#define EXTOOLS_LOGGING // rust_g is used as a fallback if this is undefined
+
+/proc/extools_log_write()
+
+/proc/extools_finalize_logging()
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 62e6c4daf9..888c20003f 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -4,10 +4,15 @@
#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound)
#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text)
#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text)
+#ifdef EXTOOLS_LOGGING
+// proc hooked, so we can just put in standard TRUE and FALSE
+#define WRITE_LOG(log, text) extools_log_write(log,text,TRUE)
+#define WRITE_LOG_NO_FORMAT(log, text) extools_log_write(log,text,FALSE)
+#else
//This is an external call, "true" and "false" are how rust parses out booleans
#define WRITE_LOG(log, text) rustg_log_write(log, text, "true")
#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false")
-
+#endif
//print a warning message to world.log
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].")
/proc/warning(msg)
@@ -216,7 +221,11 @@
/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */
/proc/shutdown_logging()
+#ifdef EXTOOLS_LOGGING
+ extools_finalize_logging()
+#else
rustg_log_close_all()
+#endif
/* Helper procs for building detailed log lines */
diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm
index 8b4f8d5b54..aad5b4cab1 100644
--- a/code/__HELPERS/files.dm
+++ b/code/__HELPERS/files.dm
@@ -71,3 +71,18 @@
/proc/pathflatten(path)
return replacetext(path, "/", "_")
+
+/// Returns the md5 of a file at a given path.
+/proc/md5filepath(path)
+ . = md5(file(path))
+
+/// Save file as an external file then md5 it.
+/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results.
+/proc/md5asfile(file)
+ var/static/notch = 0
+ // its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g.
+ var/filename = "tmp/md5asfile.[world.realtime].[world.timeofday].[world.time].[world.tick_usage].[notch]"
+ notch = WRAP(notch+1, 0, 2^15)
+ fcopy(file, filename)
+ . = md5filepath(filename)
+ fdel(filename)
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 11332863f5..2bd477d98c 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1126,12 +1126,13 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
var/list/partial = splittext(iconData, "{")
return replacetext(copytext_char(partial[2], 3, -5), "\n", "")
-/proc/icon2html(thing, target, icon_state, dir, frame = 1, moving = FALSE)
+/proc/icon2html(thing, target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE)
if (!thing)
return
var/key
var/icon/I = thing
+
if (!target)
return
if (target == world)
@@ -1147,17 +1148,26 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
if (!isicon(I))
if (isfile(thing)) //special snowflake
var/name = sanitize_filename("[generate_asset_name(thing)].png")
- if(!SSassets.cache[name])
+ if (!SSassets.cache[name])
SSassets.transport.register_asset(name, thing)
for (var/thing2 in targets)
SSassets.transport.send_assets(thing2, name)
+ if(sourceonly)
+ return SSassets.transport.get_asset_url(name)
return "
"
var/atom/A = thing
- if (isnull(dir))
- dir = A.dir
+
+ I = A.icon
if (isnull(icon_state))
icon_state = A.icon_state
- I = A.icon
+ if (!(icon_state in icon_states(I, 1)))
+ icon_state = initial(A.icon_state)
+ if (isnull(dir))
+ dir = initial(A.dir)
+
+ if (isnull(dir))
+ dir = A.dir
+
if (ishuman(thing)) // Shitty workaround for a BYOND issue.
var/icon/temp = I
I = icon()
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 6dc1433bc8..980ec38909 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -184,14 +184,23 @@
/obj/screen/alert/hot
name = "Too Hot"
- desc = "You're flaming hot! Get somewhere cooler and take off any insulating clothing like a fire suit."
+ desc = "The air around you is pretty toasty! Consider putting on some insulating clothing, or moving to a cooler area."
icon_state = "hot"
/obj/screen/alert/cold
name = "Too Cold"
- desc = "You're freezing cold! Get somewhere warmer and take off any insulating clothing like a space suit."
+ desc = "The air around you is pretty cold! Consider wearing a coat, or moving to a warmer area."
icon_state = "cold"
+/obj/screen/alert/sweat
+ name = "Sweating"
+ desc = "You're sweating! Get somewhere cooler and take off any insulating clothing like a fire suit."
+ icon_state = "sweat"
+
+/obj/screen/alert/shiver
+ name = "Shivering"
+ desc = "You're shivering! Get somewhere warmer and take off any insulating clothing like a space suit."
+
/obj/screen/alert/lowpressure
name = "Low Pressure"
desc = "The air around you is hazardously thin. A space suit would protect you."
diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm
index 60c5dd5b99..ac9db60ccc 100644
--- a/code/controllers/subsystem/research.dm
+++ b/code/controllers/subsystem/research.dm
@@ -294,6 +294,17 @@ SUBSYSTEM_DEF(research)
//[88nodes * 5000points/node] / [1.5hr * 90min/hr * 60s/min]
//Around 450000 points max???
+ /// The global list of raw anomaly types that have been refined, for hard limits.
+ var/list/created_anomaly_types = list()
+ /// The hard limits of cores created for each anomaly type. For faster code lookup without switch statements.
+ var/list/anomaly_hard_limit_by_type = list(
+ ANOMALY_CORE_BLUESPACE = MAX_CORES_BLUESPACE,
+ ANOMALY_CORE_PYRO = MAX_CORES_PYRO,
+ ANOMALY_CORE_GRAVITATIONAL = MAX_CORES_GRAVITATIONAL,
+ ANOMALY_CORE_VORTEX = MAX_CORES_VORTEX,
+ ANOMALY_CORE_FLUX = MAX_CORES_FLUX
+ )
+
/datum/controller/subsystem/research/Initialize()
point_types = TECHWEB_POINT_TYPE_LIST_ASSOCIATIVE_NAMES
initialize_all_techweb_designs()
diff --git a/code/datums/accents.dm b/code/datums/accents.dm
index 9baba90c32..ce32aa0884 100644
--- a/code/datums/accents.dm
+++ b/code/datums/accents.dm
@@ -78,23 +78,22 @@
speech_args[SPEECH_MESSAGE] = lowertext(message)
return speech_args
-/datum/accent/bone
+/datum/accent/span
var/span_flag
-/datum/accent/bone/modify_speech(list/speech_args)
- speech_args[SPEECH_SPANS] = span_flag
+/datum/accent/span/modify_speech(list/speech_args)
+ speech_args[SPEECH_SPANS] |= span_flag
return speech_args
//bone tongues either have the sans accent or the papyrus accent
-/datum/accent/bone/sans
+/datum/accent/span/sans
span_flag = SPAN_SANS
-/datum/accent/bone/papyrus
+/datum/accent/span/papyrus
span_flag = SPAN_PAPYRUS
-/datum/accent/robot/modify_speech(list/speech_args)
- speech_args[SPEECH_SPANS] = SPAN_ROBOT
- return speech_args
+/datum/accent/span/robot
+ span_flag = SPAN_ROBOT
/datum/accent/dullahan/modify_speech(list/speech_args, datum/source, mob/living/carbon/owner)
if(owner)
diff --git a/code/datums/components/crafting/recipes/recipes_clothing.dm b/code/datums/components/crafting/recipes/recipes_clothing.dm
index 717e99e8b2..df0a2a091a 100644
--- a/code/datums/components/crafting/recipes/recipes_clothing.dm
+++ b/code/datums/components/crafting/recipes/recipes_clothing.dm
@@ -273,6 +273,16 @@
time = 30
category = CAT_CLOTHING
+/datum/crafting_recipe/twinsheath
+ name = "Twin Sword Sheath"
+ result = /obj/item/storage/belt/sabre/twin
+ reqs = list(/obj/item/stack/sheet/mineral/wood = 3,
+ /obj/item/stack/sheet/leather = 8)
+ tools = list(TOOL_WIRECUTTER)
+ time = 70
+ category = CAT_CLOTHING
+
+
/datum/crafting_recipe/durathread_reinforcement_kit
name = "Durathread Reinforcement Kit"
result = /obj/item/armorkit
diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm
index f113be728d..14987bac44 100644
--- a/code/datums/components/crafting/recipes/recipes_misc.dm
+++ b/code/datums/components/crafting/recipes/recipes_misc.dm
@@ -120,6 +120,53 @@
category = CAT_MISC
always_availible = FALSE // Disabled til learned
+/datum/crafting_recipe/furnace
+ name = "Sandstone Furnace"
+ result = /obj/structure/furnace
+ time = 300
+ reqs = list(/obj/item/stack/sheet/mineral/sandstone = 15,
+ /obj/item/stack/sheet/metal = 4,
+ /obj/item/stack/rods = 2)
+ tools = list(TOOL_CROWBAR)
+ subcategory = CAT_MISCELLANEOUS
+ category = CAT_MISC
+
+/datum/crafting_recipe/tableanvil
+ name = "Table Anvil"
+ result = /obj/structure/anvil/obtainable/table
+ time = 300
+ reqs = list(/obj/item/stack/sheet/metal = 4,
+ /obj/item/stack/rods = 2)
+ tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER)
+ subcategory = CAT_MISCELLANEOUS
+ category = CAT_MISC
+
+/datum/crafting_recipe/sandvil
+ name = "Sandstone Anvil"
+ result = /obj/structure/anvil/obtainable/sandstone
+ time = 300
+ reqs = list(/obj/item/stack/sheet/mineral/sandstone = 24)
+ tools = list(TOOL_CROWBAR)
+ subcategory = CAT_MISCELLANEOUS
+ category = CAT_MISC
+
+/datum/crafting_recipe/basaltblock
+ name = "Sintered Basalt Block"
+ result = /obj/item/basaltblock
+ time = 200
+ reqs = list(/obj/item/stack/ore/glass/basalt = 50)
+ tools = list(TOOL_WELDER)
+ subcategory = CAT_MISCELLANEOUS
+ category = CAT_MISC
+
+/datum/crafting_recipe/basaltanvil
+ name = "Basalt Anvil"
+ result = /obj/structure/anvil/obtainable/basalt
+ time = 200
+ reqs = list(/obj/item/basaltblock = 5)
+ tools = list(TOOL_CROWBAR)
+ subcategory = CAT_MISCELLANEOUS
+ category = CAT_MISC
///////////////////
//Tools & Storage//
///////////////////
@@ -175,6 +222,17 @@
subcategory = CAT_TOOL
category = CAT_MISC
+/datum/crafting_recipe/toolboxhammer
+ name = "Toolbox Hammer"
+ result = /obj/item/melee/smith/hammer/toolbox
+ tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER)
+ reqs = list(/obj/item/storage/toolbox = 1,
+ /obj/item/stack/sheet/metal = 4,
+ /obj/item/stack/rods = 2)
+ time = 40
+ subcategory = CAT_TOOL
+ category = CAT_MISC
+
/datum/crafting_recipe/papersack
name = "Paper Sack"
result = /obj/item/storage/box/papersack
@@ -358,6 +416,25 @@
//Unsorted//
////////////
+
+
+/datum/crafting_recipe/stick
+ name = "Stick"
+ time = 30
+ reqs = list(/obj/item/stack/sheet/mineral/wood = 1)
+ result = /obj/item/stick
+ subcategory = CAT_MISCELLANEOUS
+ category = CAT_MISC
+
+
+/datum/crafting_recipe/swordhilt
+ name = "Sword Hilt"
+ time = 30
+ reqs = list(/obj/item/stack/sheet/mineral/wood = 2)
+ result = /obj/item/swordhandle
+ subcategory = CAT_MISCELLANEOUS
+ category = CAT_MISC
+
/datum/crafting_recipe/blackcarpet
name = "Black Carpet"
reqs = list(/obj/item/stack/tile/carpet = 50, /obj/item/toy/crayon/black = 1)
diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm
index 1469965b57..30a04d7382 100644
--- a/code/datums/materials/basemats.dm
+++ b/code/datums/materials/basemats.dm
@@ -35,23 +35,24 @@ Unless you know what you're doing, only use the first three numbers. They're in
value_per_unit = 0.025
beauty_modifier = 0.075
-///Slight force increase
+///Slight force decrease. It's gold, it's soft as fuck.
/datum/material/gold
name = "gold"
desc = "Gold"
color = list(340/255, 240/255, 50/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //gold is shiny, but not as bright as bananium
- strength_modifier = 1.2
+ strength_modifier = 0.8
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/gold
value_per_unit = 0.0625
beauty_modifier = 0.15
armor_modifiers = list("melee" = 1.1, "bullet" = 1.1, "laser" = 1.15, "energy" = 1.15, "bomb" = 1, "bio" = 1, "rad" = 1, "fire" = 0.7, "acid" = 1.1)
-///Has no special properties
+///Small force increase, for diamond swords
/datum/material/diamond
name = "diamond"
desc = "Highly pressurized carbon"
color = list(48/255, 272/255, 301/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
+ strength_modifier = 1.1
alpha = 132
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/diamond
@@ -106,6 +107,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "bluespace crystal"
desc = "Crystals with bluespace properties"
color = list(119/255, 217/255, 396/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
+ integrity_modifier = 0.2 //these things shatter when thrown.
alpha = 200
categories = list(MAT_CATEGORY_ORE = TRUE)
beauty_modifier = 0.5
@@ -139,7 +141,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "titanium"
desc = "Titanium"
color = "#b3c0c7"
- strength_modifier = 1.3
+ strength_modifier = 1.1
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/titanium
value_per_unit = 0.0625
@@ -203,7 +205,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "adamantine"
desc = "A powerful material made out of magic, I mean science!"
color = "#6d7e8e"
- strength_modifier = 1.5
+ strength_modifier = 1.3
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/adamantine
value_per_unit = 0.25
@@ -276,6 +278,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
desc = "Mir'ntrath barhah Nar'sie."
color = "#3C3434"
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ strength_modifier = 1.2
sheet_type = /obj/item/stack/sheet/runed_metal
value_per_unit = 0.75
armor_modifiers = list("melee" = 1.2, "bullet" = 1.2, "laser" = 1, "energy" = 1, "bomb" = 1.2, "bio" = 1.2, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5)
@@ -286,6 +289,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "bronze"
desc = "Clock Cult? Never heard of it."
color = "#92661A"
+ strength_modifier = 1.1
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/bronze
value_per_unit = 0.025
diff --git a/code/datums/skills/_skill_holder.dm b/code/datums/skills/_skill_holder.dm
index 73748417c3..83c95c6439 100644
--- a/code/datums/skills/_skill_holder.dm
+++ b/code/datums/skills/_skill_holder.dm
@@ -110,7 +110,7 @@
/**
* Automatic skill increase, multiplied by skill affinity if existing.
- * Only works if skill is numerical.
+ * Only works if skill is numerical or levelled..
*/
/datum/mind/proc/auto_gain_experience(skill, value, maximum, silent = FALSE)
if(!ispath(skill, /datum/skill))
diff --git a/code/datums/skills/blacksmithing.dm b/code/datums/skills/blacksmithing.dm
new file mode 100644
index 0000000000..0bddae5562
--- /dev/null
+++ b/code/datums/skills/blacksmithing.dm
@@ -0,0 +1,6 @@
+/datum/skill/level/dorfy/blacksmithing
+ name = "Blacksmithing"
+ desc = "Making metal into fancy shapes using heat and force. Higher levels increase both your working speed at an anvil as well as the quality of your works."
+ name_color = COLOR_FLOORTILE_GRAY
+ skill_traits = list(SKILL_SANITY, SKILL_INTELLIGENCE, SKILL_USE_TOOL, SKILL_TRAINING_TOOL)
+ ui_category = SKILL_UI_CAT_MISC
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 9cd47403d1..a2f534115a 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -533,6 +533,38 @@
if(100)
H.adjustOrganLoss(ORGAN_SLOT_BRAIN,20)
+/datum/status_effect/corrosion_curse/lesser
+ id = "corrosion_curse_lesser"
+ duration = 20 SECONDS
+
+/datum/status_effect/corrosion_curse/lesser/tick()
+ . = ..()
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/H = owner
+ var/chance = rand(0,100)
+ switch(chance)
+ if(0 to 19)
+ H.adjustBruteLoss(6)
+ if(20 to 29)
+ H.Dizzy(10)
+ if(30 to 39)
+ H.adjustOrganLoss(ORGAN_SLOT_LIVER,2)
+ if(40 to 49)
+ H.adjustOrganLoss(ORGAN_SLOT_HEART,2)
+ if(50 to 59)
+ H.adjustOrganLoss(ORGAN_SLOT_STOMACH,2)
+ if(60 to 69)
+ H.adjustOrganLoss(ORGAN_SLOT_EYES,5)
+ if(70 to 79)
+ H.adjustOrganLoss(ORGAN_SLOT_EARS,5)
+ if(80 to 89)
+ H.adjustOrganLoss(ORGAN_SLOT_LUNGS,5)
+ if(90 to 99)
+ H.adjustOrganLoss(ORGAN_SLOT_TONGUE,5)
+ if(100)
+ H.adjustOrganLoss(ORGAN_SLOT_BRAIN,10)
+
/datum/status_effect/amok
id = "amok"
status_type = STATUS_EFFECT_REPLACE
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 659d50c077..eac7347479 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -6,10 +6,6 @@ GLOBAL_VAR_INIT(dynamic_latejoin_delay_max, (30 MINUTES))
GLOBAL_VAR_INIT(dynamic_midround_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_midround_delay_max, (30 MINUTES))
-GLOBAL_VAR_INIT(dynamic_event_delay_min, (10 MINUTES))
-GLOBAL_VAR_INIT(dynamic_event_delay_max, (30 MINUTES)) // this is on top of regular events, so can't be quite as often
-
-
// -- Roundstart injection delays
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_min, (2 MINUTES))
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_max, (30 MINUTES))
@@ -58,7 +54,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
// Threat logging vars
/// Starting threat level, for things that increase it but can bring it back down.
var/initial_threat_level = 0
- /// Target threat level right now. Events and antags will try to keep the round at this level.
+ /// Target threat level right now. Antags will try to keep the round at this level.
var/threat_level = 0
/// The current antag threat. Recalculated every time a ruletype starts or ends.
var/threat = 0
@@ -80,8 +76,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/list/latejoin_rules = list()
/// List of midround rules used for selecting the rules.
var/list/midround_rules = list()
- /// List of events used for reducing threat without causing antag injection (necessarily).
- var/list/events = list()
/** # Pop range per requirement.
* If the value is five the range is:
* 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+
@@ -119,8 +113,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/latejoin_injection_cooldown = 0
/// When world.time is over this number the mode tries to inject a midround ruleset.
var/midround_injection_cooldown = 0
- /// When wor.dtime is over this number the mode tries to do an event.
- var/event_injection_cooldown = 0
/// When TRUE GetInjectionChance returns 100.
var/forced_injection = FALSE
/// Forced ruleset to be executed for the next latejoin.
@@ -184,7 +176,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
dat += "
Injection Timers: ([storyteller.get_injection_chance(TRUE)]% chance)
"
dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)/10] seconds"] \[Now!\]
"
dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)/10] seconds"] \[Now!\]
"
- dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)/10] seconds"] \[Now!\]
"
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
/datum/game_mode/dynamic/Topic(href, href_list)
@@ -204,10 +195,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/threatadd = input("Specify how much threat to add (negative to subtract). This can inflate the threat level.", "Adjust Threat", 0) as null|num
if(!threatadd)
return
- if(threatadd > 0)
- create_threat(threatadd)
- else
- remove_threat(threatadd)
+ create_threat(threatadd)
else if (href_list["injectlate"])
latejoin_injection_cooldown = 0
forced_injection = TRUE
@@ -216,10 +204,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
midround_injection_cooldown = 0
forced_injection = TRUE
message_admins("[key_name(usr)] forced a midround injection.", 1)
- else if (href_list["forceevent"])
- event_injection_cooldown = 0
- // events always happen anyway
- message_admins("[key_name(usr)] forced an event.", 1)
else if (href_list["threatlog"])
show_threatlog(usr)
else if (href_list["stacking_limit"])
@@ -377,8 +361,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
generate_threat()
storyteller.start_injection_cooldowns()
- SSevents.frequency_lower = storyteller.event_frequency_lower // 6 minutes by default
- SSevents.frequency_upper = storyteller.event_frequency_upper // 20 minutes by default
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
initial_threat_level = threat_level
return TRUE
@@ -397,9 +379,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
if ("Midround")
if (ruleset.weight)
midround_rules += ruleset
- if("Event")
- if(ruleset.weight)
- events += ruleset
for(var/mob/dead/new_player/player in GLOB.player_list)
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
roundstart_pop_ready++
@@ -596,8 +575,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
latejoin_rules = remove_from_list(latejoin_rules, rule.type)
else if(rule.ruletype == "Midround")
midround_rules = remove_from_list(midround_rules, rule.type)
- else if(rule.ruletype == "Event")
- events = remove_from_list(events,rule.type)
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_midround_latejoin_rule, rule), rule.delay)
return TRUE
@@ -706,17 +683,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
picking_midround_latejoin_rule(drafted_rules)
// get_injection_chance can do things on fail
- if(event_injection_cooldown < world.time)
- SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections")
- event_injection_cooldown = storyteller.get_event_cooldown() + world.time
- message_admins("DYNAMIC: Doing event injection.")
- log_game("DYNAMIC: Doing event injection.")
- update_playercounts()
- var/list/drafted_rules = storyteller.event_draft()
- if(drafted_rules.len > 0)
- SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections")
- picking_midround_latejoin_rule(drafted_rules)
-
/// Updates current_players.
/datum/game_mode/dynamic/proc/update_playercounts()
current_players[CURRENT_LIVING_PLAYERS] = list()
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm
deleted file mode 100644
index 1ee226875b..0000000000
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm
+++ /dev/null
@@ -1,454 +0,0 @@
-/datum/dynamic_ruleset/event
- ruletype = "Event"
- var/typepath // typepath of the event
- var/triggering
- var/earliest_start = 20 MINUTES
-
-/datum/dynamic_ruleset/event/get_blackbox_info()
- var/list/ruleset_data = list()
- ruleset_data["name"] = name
- ruleset_data["rule_type"] = ruletype
- ruleset_data["cost"] = total_cost
- ruleset_data["weight"] = weight
- ruleset_data["scaled_times"] = scaled_times
- ruleset_data["event_type"] = typepath
- ruleset_data["population_tier"] = indice_pop
- return ruleset_data
-
-/datum/dynamic_ruleset/event/execute()
- var/datum/round_event/E = new typepath()
- E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
- // E.control = src // can't be done! we just don't use events that require these, those can be from_ghost almost always
-
- testing("[time2text(world.time, "hh:mm:ss")] [E.type]")
- deadchat_broadcast("[name] has just been triggered by dynamic!")
- log_game("Random Event triggering: [name] ([typepath])")
-
- return E
-
-/datum/dynamic_ruleset/event/ready(forced = FALSE)
- if (!forced)
- if(earliest_start >= world.time-SSticker.round_start_time)
- return FALSE
- var/job_check = 0
- if (enemy_roles.len > 0)
- for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
- if (M.stat == DEAD)
- continue // Dead players cannot count as opponents
- if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles))
- job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
-
- var/threat = round(mode.threat_level/10)
- if (job_check < required_enemies[threat])
- SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
- return FALSE
- return TRUE
-
-//////////////////////////////////////////////
-// //
-// PIRATES //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/event/pirates
- name = "Space Pirates"
- config_tag = "pirates"
- typepath = /datum/round_event/pirates
- antag_flag = ROLE_TRAITOR
- enemy_roles = list("AI","Security Officer","Head of Security","Captain")
- required_enemies = list(2,2,1,1,0,0,0,0,0,0)
- weight = 5
- cost = 10
- earliest_start = 30 MINUTES
- blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
- requirements = list(70,60,50,50,40,40,40,30,20,15)
- property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
- high_population_requirement = 15
-
-/datum/dynamic_ruleset/event/pirates/ready(forced = FALSE)
- if (!SSmapping.empty_space)
- return FALSE
- return ..()
-
-//////////////////////////////////////////////
-// //
-// SPIDERS //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/event/spiders
- name = "Spider Infestation"
- config_tag = "spiders"
- typepath = /datum/round_event/spider_infestation
- enemy_roles = list("AI","Security Officer","Head of Security","Captain")
- required_enemies = list(2,2,1,1,0,0,0,0,0,0)
- weight = 5
- cost = 10
- requirements = list(70,60,50,50,40,40,40,30,20,15)
- high_population_requirement = 15
- property_weights = list("chaos" = 1, "valid" = 1)
-
-//////////////////////////////////////////////
-// //
-// CLOGGED VENTS //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/event/ventclog
- name = "Clogged Vents"
- config_tag = "ventclog"
- typepath = /datum/round_event/vent_clog
- enemy_roles = list("Chemist","Medical Doctor","Chief Medical Officer")
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- cost = 2
- weight = 4
- repeatable_weight_decrease = 3
- requirements = list(5,5,5,5,5,5,5,5,5,5) // yes, can happen on fake-extended
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("chaos" = 1, "extended" = 2)
-
-/datum/dynamic_ruleset/event/ventclog/ready()
- if(mode.threat_level > 30 && mode.threat >= 5 && prob(20))
- name = "Clogged Vents: Threatening"
- cost = 5
- required_enemies = list(3,3,3,2,2,2,1,1,1,1)
- typepath = /datum/round_event/vent_clog/threatening
- else if(mode.threat_level > 15 && mode.threat > 15 && prob(30))
- name = "Clogged Vents: Catastrophic"
- cost = 15
- required_enemies = list(2,2,1,1,1,1,0,0,0,0)
- typepath = /datum/round_event/vent_clog/catastrophic
- else
- cost = 2
- name = "Clogged Vents: Normal"
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- typepath = /datum/round_event/vent_clog
- return ..()
-
-//////////////////////////////////////////////
-// //
-// ION STORM //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/event/ion_storm
- name = "Ion Storm"
- config_tag = "ion_storm"
- typepath = /datum/round_event/ion_storm
- enemy_roles = list("Research Director","Captain","Chief Engineer")
- required_enemies = list(1,1,0,0,0,0,0,0,0,0)
- weight = 4
- // no repeatable weight decrease. too variable to be unfun multiple times in one round
- cost = 1
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("story_potential" = 1, "extended" = 1)
- always_max_weight = TRUE
-
-//////////////////////////////////////////////
-// //
-// METEORS //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/event/meteor_wave
- name = "Meteor Wave"
- config_tag = "meteor_wave"
- typepath = /datum/round_event/meteor_wave
- enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Captain","Cyborg")
- required_enemies = list(3,3,3,3,3,3,3,3,3,3)
- cost = 15
- weight = 3
- earliest_start = 25 MINUTES
- repeatable_weight_decrease = 2
- requirements = list(60,50,40,30,30,30,30,30,30,30)
- high_population_requirement = 30
- property_weights = list("extended" = -2)
-
-/datum/dynamic_ruleset/event/meteor_wave/ready()
- if(world.time-SSticker.round_start_time > 35 MINUTES && mode.threat_level > 40 && mode.threat >= 25 && prob(30))
- name = "Meteor Wave: Threatening"
- cost = 25
- typepath = /datum/round_event/meteor_wave/threatening
- else if(world.time-SSticker.round_start_time > 45 MINUTES && mode.threat_level > 50 && mode.threat >= 40 && prob(30))
- name = "Meteor Wave: Catastrophic"
- cost = 40
- typepath = /datum/round_event/meteor_wave/catastrophic
- else
- name = "Meteor Wave: Normal"
- cost = 15
- typepath = /datum/round_event/meteor_wave
- return ..()
-
-//////////////////////////////////////////////
-// //
-// ANOMALIES //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/event/anomaly_bluespace
- name = "Anomaly: Bluespace"
- config_tag = "anomaly_bluespace"
- typepath = /datum/round_event/anomaly/anomaly_bluespace
- enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain")
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- weight = 2
- repeatable_weight_decrease = 1
- cost = 3
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/anomaly_flux
- name = "Anomaly: Hyper-Energetic Flux"
- config_tag = "anomaly_flux"
- typepath = /datum/round_event/anomaly/anomaly_flux
- enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain")
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- weight = 2
- repeatable_weight_decrease = 1
- cost = 5
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 10
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/anomaly_gravitational
- name = "Anomaly: Gravitational"
- config_tag = "anomaly_gravitational"
- typepath = /datum/round_event/anomaly/anomaly_grav
- weight = 2
- repeatable_weight_decrease = 1
- cost = 3
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/anomaly_pyroclastic
- name = "Anomaly: Pyroclastic"
- config_tag = "anomaly_pyroclastic"
- typepath = /datum/round_event/anomaly/anomaly_pyro
- weight = 2
- repeatable_weight_decrease = 1
- cost = 5
- enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain","Cyborg")
- required_enemies = list(1,1,1,1,1,1,1,1,1,1)
- requirements = list(10,10,10,10,10,10,10,10,10,10)
- high_population_requirement = 10
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/anomaly_vortex
- name = "Anomaly: Vortex"
- config_tag = "anomaly_vortex"
- typepath = /datum/round_event/anomaly/anomaly_vortex
- weight = 2
- repeatable_weight_decrease = 1
- cost = 5
- enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain","Cyborg")
- required_enemies = list(1,1,1,1,1,1,1,1,1,1)
- requirements = list(10,10,10,10,10,10,10,10,10,10)
- high_population_requirement = 10
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-//////////////////////////////////////////////
-// //
-// WOW THAT'S A LOT OF EVENTS //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/event/brand_intelligence
- name = "Brand Intelligence"
- config_tag = "brand_intelligence"
- typepath = /datum/round_event/brand_intelligence
- weight = 1
- repeatable_weight_decrease = 1
- cost = 2
- enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain","Cyborg")
- required_enemies = list(1,1,1,1,0,0,0,0,0,0)
- requirements = list(10,10,10,10,10,10,10,10,10,10)
- high_population_requirement = 10
- repeatable = TRUE
- property_weights = list("extended" = -1, "chaos" = 1)
-
-/datum/dynamic_ruleset/event/carp_migration
- name = "Carp Migration"
- config_tag = "carp_migration"
- typepath = /datum/round_event/carp_migration
- weight = 7
- repeatable_weight_decrease = 3
- cost = 4
- requirements = list(10,10,10,10,10,10,10,10,10,10)
- high_population_requirement = 10
- earliest_start = 10 MINUTES
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/communications_blackout
- name = "Communications Blackout"
- config_tag = "communications_blackout"
- typepath = /datum/round_event/communications_blackout
- cost = 4
- weight = 2
- repeatable_weight_decrease = 3
- enemy_roles = list("Chief Engineer","Station Engineer")
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("extended" = 1, "chaos" = 1)
-
-/datum/dynamic_ruleset/event/processor_overload
- name = "Processor Overload"
- config_tag = "processor_overload"
- typepath = /datum/round_event/processor_overload
- cost = 4
- weight = 2
- repeatable_weight_decrease = 3
- enemy_roles = list("Chief Engineer","Station Engineer")
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("extended" = 1, "chaos" = 1)
- always_max_weight = TRUE
-
-/datum/dynamic_ruleset/event/space_dust
- name = "Minor Space Dust"
- config_tag = "space_dust"
- typepath = /datum/round_event/space_dust
- cost = 2
- weight = 2
- repeatable_weight_decrease = 1
- enemy_roles = list("Chief Engineer","Station Engineer")
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- earliest_start = 0 MINUTES
- property_weights = list("extended" = 1)
- always_max_weight = TRUE
-
-/datum/dynamic_ruleset/event/major_dust
- name = "Major Space Dust"
- config_tag = "major_dust"
- typepath = /datum/round_event/meteor_wave/major_dust
- cost = 4
- weight = 2
- repeatable_weight_decrease = 1
- enemy_roles = list("Chief Engineer","Station Engineer")
- required_enemies = list(2,2,2,2,2,2,2,2,2,2)
- requirements = list(10,10,10,10,10,10,10,10,10,10)
- high_population_requirement = 10
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/electrical_storm
- name = "Electrical Storm"
- config_tag = "electrical_storm"
- typepath = /datum/round_event/electrical_storm
- cost = 1
- weight = 2
- repeatable_weight_decrease = 1
- enemy_roles = list("Chief Engineer","Station Engineer")
- required_enemies = list(1,1,1,0,0,0,0,0,0,0)
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/heart_attack
- name = "Random Heart Attack"
- config_tag = "heart_attack"
- typepath = /datum/round_event/heart_attack
- cost = 3
- weight = 2
- repeatable_weight_decrease = 1
- enemy_roles = list("Medical Doctor","Chief Medical Officer")
- required_enemies = list(2,2,2,2,2,2,2,2,2,2)
- requirements = list(101,101,101,5,5,5,5,5,5,5)
- high_population_requirement = 5
- repeatable = TRUE
- property_weights = list("extended" = 1)
- always_max_weight = TRUE
-
-/datum/dynamic_ruleset/event/radiation_storm
- name = "Radiation Storm"
- config_tag = "radiation_storm"
- typepath = /datum/round_event/radiation_storm
- cost = 3
- weight = 1
- enemy_roles = list("Chemist","Chief Medical Officer","Geneticist","Medical Doctor","AI","Captain")
- required_enemies = list(1,1,1,1,1,1,1,1,1,1)
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- property_weights = list("extended" = 1,"chaos" = 1)
-
-/datum/dynamic_ruleset/event/portal_storm_syndicate
- name = "Portal Storm"
- config_tag = "portal_storm"
- typepath = /datum/round_event/portal_storm/syndicate_shocktroop
- cost = 10
- weight = 1
- enemy_roles = list("Head of Security","Security Officer","AI","Captain","Shaft Miner")
- required_enemies = list(2,2,2,2,2,2,2,2,2,2)
- requirements = list(101,101,101,30,30,30,30,30,30,30)
- high_population_requirement = 30
- earliest_start = 30 MINUTES
- property_weights = list("teamwork" = 1,"chaos" = 1, "extended" = -1)
-
-/datum/dynamic_ruleset/event/wormholes
- name = "Wormholes"
- config_tag = "wormhole"
- typepath = /datum/round_event/wormholes
- cost = 3
- weight = 4
- enemy_roles = list("AI","Medical Doctor","Station Engineer","Head of Personnel","Captain")
- required_enemies = list(2,2,2,2,2,2,2,2,2,2)
- requirements = list(5,5,5,5,5,5,5,5,5,5)
- high_population_requirement = 5
- property_weights = list("extended" = 1)
-
-/datum/dynamic_ruleset/event/swarmers
- name = "Swarmers"
- config_tag = "swarmer"
- typepath = /datum/round_event/spawn_swarmer
- cost = 10
- weight = 1
- earliest_start = 30 MINUTES
- enemy_roles = list("AI","Security Officer","Head of Security","Captain","Station Engineer","Atmos Technician","Chief Engineer")
- required_enemies = list(4,4,4,4,3,3,2,2,1,1)
- requirements = list(101,101,101,101,101,101,101,101,101,101)
- high_population_requirement = 5
- property_weights = list("extended" = -2)
-
-/datum/dynamic_ruleset/event/sentient_disease
- name = "Sentient Disease"
- config_tag = "sentient_disease"
- typepath = /datum/round_event/ghost_role/sentient_disease
- enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
- required_enemies = list(2,1,1,1,0,0,0,0,0,0)
- required_candidates = 1
- weight = 4
- cost = 5
- requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
- property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
- high_population_requirement = 5
-
-/datum/dynamic_ruleset/event/revenant
- name = "Revenant"
- config_tag = "revenant"
- typepath = /datum/round_event/ghost_role/revenant
- enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
- required_enemies = list(2,1,1,1,0,0,0,0,0,0)
- required_candidates = 1
- weight = 4
- cost = 5
- requirements = list(30,30,30,30,20,15,15,15,15,15)
- high_population_requirement = 15
- property_weights = list("story_potential" = -2, "extended" = -1)
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index dd281c456f..ed6cb0ba88 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -39,9 +39,6 @@ Property weights are added to the config weight of the ruleset. They are:
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
mode.midround_injection_cooldown = round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
- var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
- mode.event_injection_cooldown = (round(clamp(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
-
/datum/dynamic_storyteller/proc/calculate_threat()
var/threat = 0
for(var/datum/antagonist/A in GLOB.antagonists)
@@ -99,10 +96,6 @@ Property weights are added to the config weight of the ruleset. They are:
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
return round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max))
-/datum/dynamic_storyteller/proc/get_event_cooldown()
- var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
- return round(clamp(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max))
-
/datum/dynamic_storyteller/proc/get_latejoin_cooldown()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
return round(clamp(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max))
@@ -195,20 +188,6 @@ Property weights are added to the config weight of the ruleset. They are:
drafted_rules[rule] = calced_weight
return drafted_rules
-/datum/dynamic_storyteller/proc/event_draft()
- var/list/drafted_rules = list()
- for(var/datum/dynamic_ruleset/event/rule in mode.events)
- if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && (mode.threat_level + 20 - mode.threat) >= rule.cost && rule.ready())
- var/property_weight = 0
- for(var/property in property_weights)
- if(property in rule.property_weights)
- property_weight += rule.property_weights[property] * property_weights[property]
- var/calced_weight = (rule.get_weight() + property_weight) * rule.weight_mult
- if(calced_weight > 0)
- drafted_rules[rule] = calced_weight
- return drafted_rules
-
-
/datum/dynamic_storyteller/chaotic
name = "Chaotic"
config_tag = "chaotic"
@@ -271,9 +250,6 @@ Property weights are added to the config weight of the ruleset. They are:
/datum/dynamic_storyteller/random/get_midround_cooldown()
return rand(GLOB.dynamic_midround_delay_min/2, GLOB.dynamic_midround_delay_max*2)
-/datum/dynamic_storyteller/random/get_event_cooldown()
- return rand(GLOB.dynamic_event_delay_min/2, GLOB.dynamic_event_delay_max*2)
-
/datum/dynamic_storyteller/random/get_latejoin_cooldown()
return rand(GLOB.dynamic_latejoin_delay_min/2, GLOB.dynamic_latejoin_delay_max*2)
@@ -319,13 +295,6 @@ Property weights are added to the config weight of the ruleset. They are:
drafted_rules[rule] = 1
return drafted_rules
-/datum/dynamic_storyteller/random/event_draft()
- var/list/drafted_rules = list()
- for(var/datum/dynamic_ruleset/event/rule in mode.events)
- if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && rule.ready())
- drafted_rules[rule] = 1
- return drafted_rules
-
/datum/dynamic_storyteller/story
name = "Story"
config_tag = "story"
@@ -365,7 +334,7 @@ Property weights are added to the config weight of the ruleset. They are:
/datum/dynamic_storyteller/no_antag
name = "Extended"
config_tag = "semiextended"
- desc = "No standard antags. Threatening events may still spawn."
+ desc = "No standard antags."
curve_centre = -5
curve_width = 0.5
flags = NO_ASSASSIN | FORCE_IF_WON
@@ -377,15 +346,3 @@ Property weights are added to the config weight of the ruleset. They are:
/datum/dynamic_storyteller/no_antag/get_injection_chance(dry_run)
return 0
-
-/datum/dynamic_storyteller/extended
- name = "Super Extended"
- config_tag = "extended"
- desc = "No antags. No dangerous events."
- curve_centre = -20
- weight = 0
- curve_width = 0.5
-
-/datum/dynamic_storyteller/extended/on_start()
- ..()
- GLOB.dynamic_forced_extended = TRUE
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index cfb4c9a120..795508320b 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -112,7 +112,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/turf/T = get_turf(loc)
ram_turf(T)
- if(prob(10) && !isspaceturf(T))//randomly takes a 'hit' from ramming
+ if(prob(10) && !isspaceturf(T) && !istype(T, /turf/closed/mineral) && !istype(T, /turf/open/floor/plating/asteroid))//randomly takes a 'hit' from ramming
get_hit()
/obj/effect/meteor/Destroy()
@@ -136,7 +136,8 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
if(A)
ram_turf(get_turf(A))
playsound(src.loc, meteorsound, 40, 1)
- get_hit()
+ if(!istype(A, /turf/closed/mineral) && !istype(A, /turf/open/floor/plating/asteroid))
+ get_hit()
/obj/effect/meteor/proc/ram_turf(turf/T)
//first bust whatever is in the turf
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 2b7411d03f..b82d932e6d 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -551,4 +551,4 @@ Class Procs:
AM.pixel_y = -8 + (round( . / 3)*8)
/obj/machinery/rust_heretic_act()
- take_damage(500, BRUTE, "melee", 1)
+ take_damage(500, BRUTE, "melee", 1)
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 871fe838d7..7e3a5205de 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -17,11 +17,15 @@
var/countdown_colour
var/obj/effect/countdown/anomaly/countdown
-/obj/effect/anomaly/Initialize(mapload, new_lifespan)
+ /// chance we drop a core when neutralized
+ var/core_drop_chance = 100
+
+/obj/effect/anomaly/Initialize(mapload, new_lifespan, core_drop_chance = 100)
. = ..()
GLOB.poi_list |= src
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
+ src.core_drop_chance = core_drop_chance
if (!impact_area)
return INITIALIZE_HINT_QDEL
@@ -54,6 +58,8 @@
GLOB.poi_list.Remove(src)
STOP_PROCESSING(SSobj, src)
qdel(countdown)
+ if(aSignal)
+ QDEL_NULL(aSignal)
return ..()
/obj/effect/anomaly/proc/anomalyEffect()
@@ -70,12 +76,12 @@
/obj/effect/anomaly/proc/anomalyNeutralize()
new /obj/effect/particle_effect/smoke/bad(loc)
- for(var/atom/movable/O in src)
- O.forceMove(drop_location())
+ if(prob(core_drop_chance))
+ aSignal.forceMove(drop_location())
+ aSignal = null
qdel(src)
-
/obj/effect/anomaly/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_ANALYZER) //revert if runtimed
to_chat(user, "Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].")
diff --git a/code/game/objects/items/charter.dm b/code/game/objects/items/charter.dm
index 5ed719ff94..9b658bc5f1 100644
--- a/code/game/objects/items/charter.dm
+++ b/code/game/objects/items/charter.dm
@@ -13,6 +13,7 @@
var/ignores_timeout = FALSE
var/response_timer_id = null
var/approval_time = 600
+ var/allow_unicode = FALSE
var/static/regex/standard_station_regex
@@ -48,6 +49,9 @@
if(!new_name)
return
+ if(!allow_unicode && (length(new_name) != length_char(new_name)))
+ to_chat(user, "Unicode is not allowed. Adminhelp if you want to use it so badly.")
+ return
log_game("[key_name(user)] has proposed to name the station as \
[new_name]")
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index 37d19783b5..8e59e91e38 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -390,7 +390,7 @@
/obj/item/circuitboard/machine/thermomachine/examine()
. = ..()
- . += "It is set to layer [pipe_layer]."
+ . += "It is set to layer [pipe_layer]. Use a Multitool on the circuit to change this."
/obj/item/circuitboard/machine/thermomachine/heater
name = "Heater (Machine Board)"
@@ -1146,3 +1146,8 @@
build_path = /obj/machinery/atmospherics/components/unary/shuttle/heater
req_components = list(/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/matter_bin = 1)
+
+/obj/item/circuitboard/machine/explosive_compressor
+ name = "Explosive Compressor (Machine Board)"
+ build_path = /obj/machinery/research/explosive_compressor
+ req_components = list(/obj/item/stock_parts/matter_bin = 3)
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 767f8fc395..6396a4fe3d 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -394,8 +394,6 @@
to_chat(user, "[src] are recharging!")
return
- user.stop_pulling() //User has hands full, and we don't care about anyone else pulling on it, their problem. CLEAR!!
-
if(user.a_intent == INTENT_DISARM)
do_disarm(M, user)
return
@@ -447,8 +445,7 @@
if(do_after(user, isnull(defib?.disarm_shock_time)? disarm_shock_time : defib.disarm_shock_time, target = M))
M.visible_message("[user] zaps [M] with [src]!", \
"[user] zaps [M] with [src]!")
- M.adjustStaminaLoss(50)
- M.DefaultCombatKnockdown(100)
+ M.DefaultCombatKnockdown(140)
M.updatehealth() //forces health update before next life tick
playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1)
M.emote("gasp")
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 32d9c02a27..8b0d46be44 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -289,3 +289,9 @@
. = TRUE
update_icon()
+
+/**
+ * Returns if this is ready to be detonated. Checks if both tanks are in place.
+ */
+/obj/item/transfer_valve/proc/ready()
+ return tank_one && tank_two
diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm
index cf5c3d4fc5..6cf6a524de 100644
--- a/code/game/objects/items/dualsaber.dm
+++ b/code/game/objects/items/dualsaber.dm
@@ -26,6 +26,7 @@
wound_bonus = -110
bare_wound_bonus = 20
block_parry_data = /datum/block_parry_data/dual_esword
+ block_chance = 60
var/hacked = FALSE
/// Can this reflect all energy projectiles?
var/can_reflect = TRUE
@@ -38,7 +39,8 @@
var/wielded = FALSE // track wielded status on item
var/slowdown_wielded = 0
-/datum/block_parry_data/dual_esword
+/datum/block_parry_data/dual_esword // please run at the man going apeshit with his funny doublesword
+ can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST | BLOCK_DIR_WEST | BLOCK_DIR_EAST
block_damage_absorption = 2
block_damage_multiplier = 0.15
block_damage_multiplier_override = list(
@@ -50,10 +52,10 @@
block_lock_sprinting = TRUE
// no attacking while blocking
block_lock_attacking = TRUE
- block_projectile_mitigation = 75
+ block_projectile_mitigation = 85
// more efficient vs projectiles
block_stamina_efficiency_override = list(
- TEXT_ATTACK_TYPE_PROJECTILE = 4
+ TEXT_ATTACK_TYPE_PROJECTILE = 6
)
parry_time_windup = 0
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 7692278ba3..426958b99a 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -117,6 +117,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \
new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \
+// new/datum/stack_recipe("diamond brick", /obj/item/ingot/diamond, 6, time = 100), \ not yet
))
/obj/item/stack/sheet/mineral/diamond/get_main_recipes()
@@ -145,6 +146,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \
new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \
new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("uranium ingot", /obj/item/ingot/uranium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/uranium/get_main_recipes()
@@ -177,6 +179,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \
new/datum/stack_recipe("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \
new/datum/stack_recipe("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \
+// new/datum/stack_recipe("plasma ingot", /obj/item/ingot/plasma, 6, time = 100), \ no
))
/obj/item/stack/sheet/mineral/plasma/get_main_recipes()
@@ -221,6 +224,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \
new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Simple Crown", /obj/item/clothing/head/crown, 5), \
new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("gold ingot", /obj/item/ingot/gold, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/gold/get_main_recipes()
@@ -252,6 +256,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("silver ingot", /obj/item/ingot/silver, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/silver/get_main_recipes()
@@ -278,6 +283,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
GLOBAL_LIST_INIT(bananium_recipes, list ( \
new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \
new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("hilarious ingot", /obj/item/ingot/bananium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/bananium/get_main_recipes()
@@ -306,6 +312,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \
GLOBAL_LIST_INIT(titanium_recipes, list ( \
new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), \
+ new/datum/stack_recipe("titanic ingot", /obj/item/ingot/titanium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/titanium/get_main_recipes()
@@ -353,6 +360,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list ( \
*/
GLOBAL_LIST_INIT(adamantine_recipes, list(
new /datum/stack_recipe("incomplete servant golem shell", /obj/item/golem_shell/servant, req_amount=1, res_amount=1),
+ new/datum/stack_recipe("adamant ingot", /obj/item/ingot/adamantine, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/adamantine
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 645051b7c2..9ed930b8c7 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -121,6 +121,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("iron door", /obj/structure/mineral_door/iron, 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("pestle", /obj/item/pestle, 1, time = 50), \
new/datum/stack_recipe("floodlight frame", /obj/structure/floodlight_frame, 5, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("iron ingot", /obj/item/ingot/iron, 6, time = 100), \
))
/obj/item/stack/sheet/metal
@@ -556,6 +557,9 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
new/datum/stack_recipe("forge", /obj/structure/destructible/cult/forge, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("archives", /obj/structure/destructible/cult/tome, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("altar", /obj/structure/destructible/cult/talisman, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("anvil", /obj/structure/anvil/obtainable/narsie, 4, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("runic ingot", /obj/item/ingot/cult, 2, time = 100), \
+ new/datum/stack_recipe("rune smith's hammer", /obj/item/melee/smith/hammer/narsie, 6), \
))
/obj/item/stack/sheet/runed_metal
@@ -618,6 +622,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("brass bar stool", /obj/structure/chair/stool/bar/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass stool", /obj/structure/chair/stool/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass table frame", /obj/structure/table_frame/brass, 1, time = 5, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("brass anvil", /obj/structure/anvil/obtainable/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("brass furnace", /obj/structure/furnace/infinite/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
null, \
new/datum/stack_recipe("sender - pressure sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("sender - mech sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/mech, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
@@ -629,6 +635,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("receiver - power nullifier", /obj/structure/destructible/clockwork/trap/power_nullifier, 5, time = 20, one_per_turf = TRUE, on_floor = TRUE, placement_checks = STACK_CHECK_CARDINALS), \
null, \
new/datum/stack_recipe("brass flask", /obj/item/reagent_containers/food/drinks/bottle/holyoil/empty), \
+ new/datum/stack_recipe("brass smith's hammer", /obj/item/melee/smith/hammer/ratvar, 6), \
+ new/datum/stack_recipe("brass ingot", /obj/item/ingot/bronze/ratvar, 6, time = 100), \
))
/obj/item/stack/tile/brass
@@ -684,7 +692,10 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
new/datum/stack_recipe("bronze chair", /obj/structure/chair/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze bar stool", /obj/structure/chair/stool/bar/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze stool", /obj/structure/chair/stool/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
- new /datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
+ new/datum/stack_recipe("bronze anvil",/obj/structure/anvil/obtainable/bronze, 20, time = 110, one_per_turf = TRUE, on_floor = TRUE), \
+ null,
+ new/datum/stack_recipe("bronze ingot", /obj/item/ingot/bronze, 6, time = 100), \
+ new/datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
))
/obj/item/stack/sheet/bronze
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 0001494fdd..590df34cde 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -815,3 +815,18 @@
attack_verb = list("bashed", "slashes", "prods", "pokes")
fitting_swords = list(/obj/item/melee/rapier)
starting_sword = /obj/item/melee/rapier
+
+/obj/item/storage/belt/sabre/twin
+ name = "twin sheath"
+ desc = "Two sheaths. One is capable of holding a katana (or bokken) and the other a wakizashi. You could put two wakizashis in if you really wanted to. Now you can really roleplay as a samurai."
+ icon_state = "twinsheath"
+ item_state = "quiver" //this'll do.
+ w_class = WEIGHT_CLASS_BULKY
+ fitting_swords = list(/obj/item/melee/smith/wakizashi, /obj/item/melee/smith/twohand/katana, /obj/item/melee/bokken)
+ starting_sword = null
+
+/obj/item/storage/belt/sabre/twin/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 2
+ STR.max_w_class = WEIGHT_CLASS_BULKY + WEIGHT_CLASS_NORMAL //katana and waki.
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 653e95c21e..3f2dc2506a 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -237,6 +237,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
resistance_flags = FIRE_PROOF
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+/obj/item/katana/lavaland
+ desc = "Woefully underpowered in Lavaland."
+ block_chance = 30
+ force = 25 //Like a fireaxe but one handed and can block!
+
/obj/item/katana/cursed
slot_flags = null
diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm
index 2911babd83..edc4f0c91f 100644
--- a/code/game/objects/structures/lavaland/necropolis_tendril.dm
+++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm
@@ -10,6 +10,7 @@
max_mobs = 3
max_integrity = 250
mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril)
+ var/loot_type = /obj/structure/closet/crate/necropolis/tendril/all
move_resist=INFINITY // just killing it tears a massive hole in the ground, let's not move it
anchored = TRUE
@@ -41,7 +42,7 @@ GLOBAL_LIST_INIT(tendrils, list())
/obj/structure/spawner/lavaland/deconstruct(disassembled)
new /obj/effect/collapse(loc)
- new /obj/structure/closet/crate/necropolis/tendril(loc)
+ new loot_type(loc)
return ..()
diff --git a/code/game/world.dm b/code/game/world.dm
index 2d174c86e5..121d51136d 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -11,6 +11,11 @@ GLOBAL_LIST(topic_status_cache)
/world/New()
if (fexists(EXTOOLS))
call(EXTOOLS, "maptick_initialize")()
+ #ifdef EXTOOLS_LOGGING
+ call(EXTOOLS, "init_logging")()
+ else
+ CRASH("[EXTOOLS] does not exist!")
+ #endif
enable_debugger()
#ifdef REFERENCE_TRACKING
enable_reference_tracking()
@@ -274,6 +279,7 @@ GLOBAL_LIST(topic_status_cache)
GM.__gasmixture_unregister()
num_deleted++
log_world("Deallocated [num_deleted] gas mixtures")
+ shutdown_logging() // makes sure the thread is closed before end, else we terminate
..()
/world/proc/update_status()
diff --git a/code/modules/antagonists/cult/cult_structures.dm b/code/modules/antagonists/cult/cult_structures.dm
index 5803941f36..a388621c92 100644
--- a/code/modules/antagonists/cult/cult_structures.dm
+++ b/code/modules/antagonists/cult/cult_structures.dm
@@ -174,7 +174,14 @@
new reward(get_turf(src))
to_chat(user, "You work the forge as dark knowledge guides your hands, creating the [choice]!")
-
+/obj/structure/destructible/cult/forge/attackby(obj/item/I, mob/user)
+ if(!iscultist(user))
+ to_chat(user, "The heat radiating from [src] pushes you back.")
+ return
+ if(istype(I, /obj/item/ingot))
+ var/obj/item/ingot/notsword = I
+ to_chat(user, "You heat the [notsword] in the [src].")
+ notsword.workability = "shapeable"
/obj/structure/destructible/cult/pylon
name = "pylon"
diff --git a/code/modules/antagonists/disease/disease_event.dm b/code/modules/antagonists/disease/disease_event.dm
index 4365fd7538..385cee998b 100644
--- a/code/modules/antagonists/disease/disease_event.dm
+++ b/code/modules/antagonists/disease/disease_event.dm
@@ -3,7 +3,6 @@
name = "Spawn Sentient Disease"
typepath = /datum/round_event/ghost_role/sentient_disease
weight = 7
- gamemode_blacklist = list("dynamic")
max_occurrences = 1
min_players = 5
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
index 79dfbdd7c3..81bc090460 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
@@ -30,6 +30,7 @@
/datum/antagonist/heretic/on_gain()
var/mob/living/current = owner.current
+ owner.teach_crafting_recipe(/datum/crafting_recipe/heretic/codex)
if(ishuman(current))
forge_primary_objectives()
gain_knowledge(/datum/eldritch_knowledge/spell/basic)
@@ -41,7 +42,6 @@
START_PROCESSING(SSprocessing,src)
if(give_equipment)
equip_cultist()
- owner.teach_crafting_recipe(/datum/crafting_recipe/heretic/codex)
return ..()
/datum/antagonist/heretic/on_removal()
@@ -111,17 +111,12 @@
P.find_target(owners,assasination)
protection += P.target
objectives += P
-
var/datum/objective/sacrifice_ecult/SE = new
SE.owner = owner
SE.update_explanation_text()
objectives += SE
- var/datum/objective/escape/escape_objective = new
- escape_objective.owner = owner
- objectives += escape_objective
-
/datum/antagonist/heretic/apply_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm
index 899e588bda..99794024f1 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm
@@ -20,9 +20,11 @@
if(!is_in_use)
INVOKE_ASYNC(src, .proc/activate , user)
-/obj/effect/eldritch/attacked_by(obj/item/I, mob/living/user)
+/obj/effect/eldritch/attackby(obj/item/I, mob/living/user)
. = ..()
- if(istype(I,/obj/item/nullrod))
+ if(istype(I, /obj/item/storage/book/bible) || istype(I, /obj/item/nullrod))
+ user.say("BEGONE FOUL MAGICKS!!", forced = "bible")
+ to_chat(user, "You disrupt the magic of [src] with [I].")
qdel(src)
/obj/effect/eldritch/proc/activate(mob/living/user)
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm
index 4dab6d789d..2052a2fbb3 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_items.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm
@@ -16,16 +16,19 @@
return
var/dist = get_dist(user.loc,target.loc)
var/dir = get_dir(user.loc,target.loc)
-
- switch(dist)
- if(0 to 15)
- to_chat(user,"[target.real_name] is near you. They are to the [dir2text(dir)] of you!")
- if(16 to 31)
- to_chat(user,"[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!")
- if(32 to 127)
- to_chat(user,"[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!")
- else
- to_chat(user,"[target.real_name] is beyond our reach.")
+
+ if(user.z != target.z)
+ to_chat(user,"[target.real_name] is beyond our reach.")
+ else
+ switch(dist)
+ if(0 to 15)
+ to_chat(user,"[target.real_name] is near you. They are to the [dir2text(dir)] of you!")
+ if(16 to 31)
+ to_chat(user,"[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!")
+ if(32 to 127)
+ to_chat(user,"[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!")
+ else
+ to_chat(user,"[target.real_name] is beyond our reach.")
if(target.stat == DEAD)
to_chat(user,"[target.real_name] is dead. Bring them onto a transmutation rune!")
@@ -86,8 +89,8 @@
desc = "A crescent blade born from a fleshwarped creature. Keenly aware, it seeks to spread to others the excruciations it has endured from dead origins."
icon_state = "flesh_blade"
item_state = "flesh_blade"
- wound_bonus = 5
- bare_wound_bonus = 15
+ wound_bonus = 10
+ bare_wound_bonus = 20
/obj/item/clothing/neck/eldritch_amulet
name = "warm eldritch medallion"
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
index 065844bedf..27868a3e3e 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
@@ -224,7 +224,7 @@
name = "Break of Dawn"
desc = "Starts your journey in the mansus. Allows you to select a target using a living heart on a transmutation rune."
gain_text = "Gates of Mansus open up to your mind."
- next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh)
+ next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/spell/silence)
cost = 0
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
required_atoms = list(/obj/item/living_heart)
@@ -301,3 +301,11 @@
required_atoms = list(/obj/item/shard,/obj/item/stack/rods)
result_atoms = list(/obj/item/melee/sickly_blade)
route = "Start"
+
+/datum/eldritch_knowledge/spell/silence
+ name = "Silence"
+ desc = "Allows you to use the power of the Mansus to force an individual's tongue to be held down for up to twenty seconds. They'll notice quickly, however."
+ gain_text = "They must hold their tongues, for they do not understand."
+ cost = 1
+ spell_to_add = /obj/effect/proc_holder/spell/pointed/trigger/mute/eldritch
+ route = PATH_SIDE
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_magic.dm b/code/modules/antagonists/eldritch_cult/eldritch_magic.dm
index d84a997e28..799667999e 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_magic.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_magic.dm
@@ -43,8 +43,9 @@
/obj/item/melee/touch_attack/mansus_fist
name = "Mansus Grasp"
desc = "A sinister looking aura that distorts the flow of reality around it. Causes knockdown, major stamina damage aswell as some Brute. It gains additional beneficial effects with certain knowledges you can research."
- icon_state = "disintegrate"
- item_state = "disintegrate"
+ icon = 'icons/obj/eldritch.dmi'
+ icon_state = "mansus_grasp"
+ item_state = "mansus"
catchphrase = "T'IESA SIE'KTI VISATA"
/obj/item/melee/touch_attack/mansus_fist/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
@@ -118,6 +119,7 @@
/obj/item/melee/touch_attack/blood_siphon
name = "Blood Siphon"
desc = "A sinister looking aura that distorts the flow of reality around it."
+ color = RUNE_COLOR_RED
icon_state = "disintegrate"
item_state = "disintegrate"
catchphrase = "SUN'AI'KINI'MAS"
@@ -260,41 +262,82 @@
/obj/effect/proc_holder/spell/pointed/cleave/long
charge_max = 650
-/obj/effect/proc_holder/spell/pointed/touch/mad_touch
+/obj/effect/proc_holder/spell/targeted/touch/mad_touch
name = "Touch of Madness"
- desc = "Touch spell that drains your enemies sanity."
- school = "transmutation"
- charge_max = 150
+ desc = "Touch spell that allows you to force the knowledge of the mansus upon your foes."
+ hand_path = /obj/item/melee/touch_attack/mad_touch
+ school = "evocation"
+ charge_max = 1800
clothes_req = FALSE
- invocation_type = "none"
- range = 2
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "mad_touch"
action_background_icon_state = "bg_ecult"
-/obj/effect/proc_holder/spell/pointed/touch/mad_touch/can_target(atom/target, mob/user, silent)
- . = ..()
- if(!.)
- return FALSE
- if(!istype(target,/mob/living/carbon/human))
- if(!silent)
- to_chat(user, "You are unable to touch [target]!")
- return FALSE
- return TRUE
+/obj/item/melee/touch_attack/mad_touch
+ name = "Touch of Madness"
+ desc = "A sinister looking aura that shatters your enemies minds."
+ icon = 'icons/obj/eldritch.dmi'
+ icon_state = "mad_touch"
+ item_state = "madness"
+ catchphrase = "SUNA'IKINTI PROTA"
-/obj/effect/proc_holder/spell/pointed/touch/mad_touch/cast(list/targets, mob/user)
- . = ..()
- for(var/mob/living/carbon/target in targets)
- if(ishuman(targets))
- var/mob/living/carbon/human/tar = target
- if(tar.anti_magic_check())
- tar.visible_message("Spell bounces off of [target]!","The spell bounces off of you!")
- return
- if(target.mind && !target.mind.has_antag_datum(/datum/antagonist/heretic))
- to_chat(user,"[target.name] has been cursed!")
- SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
+/obj/item/melee/touch_attack/mad_touch/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
-/obj/effect/proc_holder/spell/pointed/ash_final
+ if(!proximity_flag || target == user)
+ return
+ if(ishuman(target))
+ var/mob/living/carbon/human/tar = target
+ if(tar.anti_magic_check())
+ tar.visible_message("Spell bounces off of [target]!","The spell bounces off of you!")
+ return ..()
+
+ if(iscarbon(target))
+ playsound(user, 'sound/effects/curseattack.ogg', 75, TRUE)
+ var/mob/living/carbon/C = target
+ C.adjustOrganLoss(ORGAN_SLOT_BRAIN,35)
+ C.DefaultCombatKnockdown(50, override_stamdmg = 0)
+ C.gain_trauma(/datum/brain_trauma/mild/phobia)
+ to_chat(user,"[target.name] has been cursed!")
+ SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
+ return ..()
+
+/obj/effect/proc_holder/spell/targeted/touch/grasp_of_decay
+ name = "Grasp of Decay"
+ desc = "A sinister looking touch that rots your foes from the inside out for twenty seconds."
+ hand_path = /obj/item/melee/touch_attack/grasp_of_decay
+ school = "evocation"
+ charge_max = 1200
+ clothes_req = FALSE
+ action_icon = 'icons/mob/actions/actions_ecult.dmi'
+ action_icon_state = "mansus_grasp"
+ action_background_icon_state = "bg_ecult"
+
+/obj/item/melee/touch_attack/grasp_of_decay
+ name = "Grasp of Decay"
+ desc = "A sinister looking aura that rots your foes from the inside out."
+ icon = 'icons/obj/eldritch.dmi'
+ icon_state = "mansus_grasp"
+ item_state = "mansus"
+ catchphrase = "SKILI'EDUONIS"
+
+/obj/item/melee/touch_attack/grasp_of_decay/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+
+ if(!proximity_flag || target == user)
+ return
+ if(ishuman(target))
+ var/mob/living/carbon/human/tar = target
+ if(tar.anti_magic_check())
+ tar.visible_message("Spell bounces off of [target]!","The spell bounces off of you!")
+ return ..()
+
+ if(iscarbon(target))
+ playsound(user, 'sound/effects/curseattack.ogg', 75, TRUE)
+ var/mob/living/carbon/C = target
+ C.DefaultCombatKnockdown(50, override_stamdmg = 0)
+ C.apply_status_effect(/datum/status_effect/corrosion_curse/lesser)
+ return ..()
+
+/obj/effect/proc_holder/spell/pointed/nightwatchers_rite
name = "Nightwatcher's Rite"
desc = "Powerful spell that releases 5 streams of fire away from you."
school = "transmutation"
@@ -307,7 +350,7 @@
action_icon_state = "flames"
action_background_icon_state = "bg_ecult"
-/obj/effect/proc_holder/spell/pointed/ash_final/cast(list/targets, mob/user)
+/obj/effect/proc_holder/spell/pointed/nightwatchers_rite/cast(list/targets, mob/user)
for(var/X in targets)
var/T
T = line_target(-25, range, X, user)
@@ -322,11 +365,12 @@
INVOKE_ASYNC(src, .proc/fire_line, user,T)
return ..()
-/obj/effect/proc_holder/spell/pointed/ash_final/proc/line_target(offset, range, atom/at , atom/user)
+/obj/effect/proc_holder/spell/pointed/nightwatchers_rite/proc/line_target(offset, range, atom/at , atom/user)
if(!at)
return
var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset
var/turf/T = get_turf(user)
+ playsound(user,'sound/magic/fireball.ogg', 200, 1)
for(var/i in 1 to range)
var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z)
if(!check)
@@ -334,7 +378,7 @@
T = check
return (getline(user, T) - get_turf(user))
-/obj/effect/proc_holder/spell/pointed/ash_final/proc/fire_line(atom/source, list/turfs)
+/obj/effect/proc_holder/spell/pointed/nightwatchers_rite/proc/fire_line(atom/source, list/turfs)
var/list/hit_list = list()
for(var/turf/T in turfs)
if(istype(T, /turf/closed))
@@ -347,8 +391,8 @@
if(L in hit_list || L == source)
continue
hit_list += L
- L.adjustFireLoss(20)
- to_chat(L, "You're hit by [source]'s fire breath!")
+ L.adjustFireLoss(15)
+ to_chat(L, "You're hit by a blast of fire!")
new /obj/effect/hotspot(T)
T.hotspot_expose(700,50,1)
@@ -368,7 +412,7 @@
possible_shapes = list(/mob/living/simple_animal/mouse,\
/mob/living/simple_animal/pet/dog/corgi,\
/mob/living/simple_animal/hostile/carp,\
- /mob/living/simple_animal/bot/secbot, \
+ /mob/living/simple_animal/bot/secbot,\
/mob/living/simple_animal/pet/fox,\
/mob/living/simple_animal/pet/cat )
@@ -430,7 +474,7 @@
action_background_icon_state = "bg_ecult"
range = -1
include_user = TRUE
- charge_max = 700
+ charge_max = 1200
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "fire_ring"
///how long it lasts
@@ -595,6 +639,39 @@
invocation = "AK'LIS"
action_background_icon_state = "bg_ecult"
+/obj/effect/proc_holder/spell/pointed/trigger/mute/eldritch
+ name = "Silence"
+ desc = "Using the power of the mansus, silences a selected unbeliever for twenty seconds."
+ school = "transmutation"
+ charge_max = 1800
+ clothes_req = FALSE
+ invocation = "VIS'TIEK TAVO'LIZUVIS"
+ invocation_type = "whisper"
+ message = "It feels as if your tongue is being held down by an unseen force!"
+ starting_spells = list("/obj/effect/proc_holder/spell/targeted/genetic/mute")
+ ranged_mousepointer = 'icons/effects/mouse_pointers/mute_target.dmi'
+ action_background_icon_state = "bg_ecult"
+ action_icon = 'icons/mob/actions/actions_ecult.dmi'
+ action_icon_state = "mute"
+ active_msg = "You prepare to silence a target..."
+
+/obj/effect/proc_holder/spell/targeted/genetic/mute
+ mutations = list(MUT_MUTE)
+ duration = 200
+ charge_max = 1200 // needs to be higher than the duration or it'll be permanent
+ sound = 'sound/magic/blind.ogg'
+
+/obj/effect/proc_holder/spell/pointed/trigger/mute/can_target(atom/target, mob/user, silent)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(!isliving(target))
+ if(!silent)
+ to_chat(user, "You can only silence living beings!")
+ return FALSE
+ return TRUE
+
+
/obj/effect/temp_visual/dir_setting/entropic
icon = 'icons/effects/160x160.dmi'
icon_state = "entropic_plume"
diff --git a/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm
index 8efd9837a9..425052de2a 100644
--- a/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm
+++ b/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm
@@ -95,7 +95,30 @@
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/targeted/fiery_rebirth
- next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/final/ash_final)
+ next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/flame_immunity)
+ route = PATH_ASH
+
+/datum/eldritch_knowledge/flame_immunity
+ name = "Nightwatcher's Blessing"
+ gain_text = "The True Light will destroy and make something anew of any individual. If only they accepted it."
+ desc = "Becoming one with the ash, you become immune to fire and heat, allowing you to thrive in a more extreme environment.."
+ cost = 2
+ next_knowledge = list(/datum/eldritch_knowledge/spell/nightwatchers_rite)
+ route = PATH_ASH
+ var/list/trait_list = list(TRAIT_RESISTHEAT,TRAIT_NOFIRE)
+
+/datum/eldritch_knowledge/flame_immunity/on_gain(mob/living/user)
+ to_chat(user, "[gain_text]")
+ for(var/X in trait_list)
+ ADD_TRAIT(user,X,MAGIC_TRAIT)
+
+/datum/eldritch_knowledge/spell/nightwatchers_rite
+ name = "Nightwatcher's Rite"
+ gain_text = "When the Glory of the Lantern scorches and sears their skin, nothing will protect them from the ashes."
+ desc = "Fire off five streams of fire from your hand, each setting ablaze targets hit and scorching them upon contact."
+ cost = 2
+ spell_to_add = /obj/effect/proc_holder/spell/pointed/nightwatchers_rite
+ next_knowledge = list(/datum/eldritch_knowledge/final/ash_final)
route = PATH_ASH
/datum/eldritch_knowledge/ash_blade_upgrade
@@ -167,7 +190,7 @@
required_atoms = list(/mob/living/carbon/human)
cost = 5
route = PATH_ASH
- var/list/trait_list = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_BOMBIMMUNE)
+ var/list/trait_list = list(TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_BOMBIMMUNE)
/datum/eldritch_knowledge/final/ash_final/on_finished_recipe(mob/living/user, list/atoms, loc)
priority_announce("$^@*$^@(#&$(@^$^@# Fear the blaze, for Ashbringer [user.real_name] has come! $^@*$^@(#&$(@^$^@#","#$^@*$^@(#&$(@^$^@#", 'sound/announcer/classic/spanomalies.ogg')
diff --git a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm
index 9684e1fa0c..5e32cf1b4e 100644
--- a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm
@@ -70,7 +70,7 @@
desc = "Empowers your Mansus Grasp to be able to create a single ghoul out of a dead player. You cannot raise the same person twice. Ghouls have only 50 HP and look like husks."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/flesh_ghoul)
- var/ghoul_amt = 6
+ var/ghoul_amt = 4
var/list/spooky_scaries
route = PATH_FLESH
@@ -177,7 +177,7 @@
cost = 1
required_atoms = list(/obj/item/kitchen/knife,/obj/item/reagent_containers/food/snacks/grown/poppy,/obj/item/pen,/obj/item/paper)
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/stalker
- next_knowledge = list(/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/summon/rusty,/datum/eldritch_knowledge/final/flesh_final)
+ next_knowledge = list(/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/summon/rusty,/datum/eldritch_knowledge/flesh_blade_upgrade_2)
route = PATH_FLESH
/datum/eldritch_knowledge/summon/ashy
@@ -250,3 +250,28 @@
carbon_user.gib()
return ..()
+
+/datum/eldritch_knowledge/flesh_blade_upgrade_2
+ name = "Remembrance"
+ gain_text = "Pain isn't something easily forgotten."
+ desc = "Your blade remembers more, and remembers how easily bones broke just as its flesh did, guaranteeing dislocated, or broken bones."
+ cost = 2
+ next_knowledge = list(/datum/eldritch_knowledge/spell/touch_of_madness)
+ route = PATH_FLESH
+
+/datum/eldritch_knowledge/flesh_blade_upgrade_2/on_eldritch_blade(target,user,proximity_flag,click_parameters)
+ . = ..()
+ if(iscarbon(target))
+ var/mob/living/carbon/carbon_target = target
+ var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
+ var/datum/wound/blunt/moderate/moderate_wound = new
+ moderate_wound.apply_wound(bodypart)
+
+/datum/eldritch_knowledge/spell/touch_of_madness
+ name = "Touch of Madness"
+ gain_text = "The ignorant mind that inhabits their feeble bodies will crumble when they acknowledge - willingly or not, the truth."
+ desc = "By forcing the knowledge of the Mansus upon my foes, I can show them things that would drive any normal man insane."
+ cost = 2
+ spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mad_touch
+ next_knowledge = list(/datum/eldritch_knowledge/final/flesh_final)
+ route = PATH_FLESH
diff --git a/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm
index 74b3753b69..f5c54dd7bf 100644
--- a/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm
@@ -27,8 +27,13 @@
if(E)
E.on_effect()
H.adjustOrganLoss(pick(ORGAN_SLOT_BRAIN,ORGAN_SLOT_EARS,ORGAN_SLOT_EYES,ORGAN_SLOT_LIVER,ORGAN_SLOT_LUNGS,ORGAN_SLOT_STOMACH,ORGAN_SLOT_HEART),25)
+ else
+ for(var/X in user.mind.spell_list)
+ if(!istype(X,/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp))
+ continue
+ var/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp/MG = X
+ MG.charge_counter = min(round(MG.charge_counter + MG.charge_max * 0.75),MG.charge_max)
target.rust_heretic_act()
- target.emp_act(EMP_HEAVY)
return TRUE
/datum/eldritch_knowledge/spell/area_conversion
@@ -43,7 +48,7 @@
/datum/eldritch_knowledge/spell/rust_wave
name = "Patron's Reach"
desc = "You can now send a bolt of rust that corrupts the immediate area, and poisons the first target hit."
- gain_text = "Messengers of hope fear the rustbringer."
+ gain_text = "Messengers of hope fear the Rustbringer."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/aimed/rust_wave
route = PATH_RUST
@@ -92,19 +97,20 @@
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
route = PATH_RUST
-/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
+/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(mob/target,user,proximity_flag,click_parameters)
. = ..()
- if(iscarbon(target))
- var/mob/living/carbon/carbon_target = target
- carbon_target.reagents.add_reagent(/datum/reagent/eldritch, 5)
+ if(!IS_HERETIC(target))
+ if(iscarbon(target))
+ var/mob/living/carbon/carbon_target = target
+ carbon_target.reagents.add_reagent(/datum/reagent/eldritch, 5)
/datum/eldritch_knowledge/spell/entropic_plume
name = "Entropic Plume"
desc = "You can now send a befuddling plume that blinds, poisons and makes enemies strike each other, while also converting the immediate area into rust."
- gain_text = "Messengers of hope fear the rustbringer."
+ gain_text = "If they knew, the truth would turn them against eachother."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/cone/staggered/entropic_plume
- next_knowledge = list(/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty)
+ next_knowledge = list(/datum/eldritch_knowledge/rust_fist_upgrade,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty)
route = PATH_RUST
/datum/eldritch_knowledge/armor
@@ -119,12 +125,38 @@
/datum/eldritch_knowledge/essence
name = "Priest's Ritual"
desc = "You can now transmute a tank of water into a bottle of eldritch fluid."
- gain_text = "This is an old recipe, i got it from an owl."
+ gain_text = "This is an old recipe, I got it from an owl."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/spell/ashen_shift)
required_atoms = list(/obj/structure/reagent_dispensers/watertank)
result_atoms = list(/obj/item/reagent_containers/glass/beaker/eldritch)
+/datum/eldritch_knowledge/rust_fist_upgrade
+ name = "Vile Grip"
+ desc = "Empowers your Mansus Grasp further, sickening your foes and making them vomit, while also strengthening the rate at which your hand decays objects."
+ gain_text = "A sickly diseased touch that was, yet, so welcoming."
+ cost = 2
+ next_knowledge = list(/datum/eldritch_knowledge/spell/grasp_of_decay)
+ var/rust_force = 750
+ var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava,/turf/open/chasm,/turf/open/floor/plating/rust))
+ route = PATH_RUST
+
+/datum/eldritch_knowledge/rust_fist_upgrade/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.set_disgust(75)
+ return TRUE
+
+/datum/eldritch_knowledge/spell/grasp_of_decay
+ name = "Grasp of Decay"
+ desc = "Applying your knowledge of rust to the human body, a knowledge that could decay your foes from the inside out, resulting in organ failure, vomiting, or eventual death through peeling flesh."
+ gain_text = "Decay, similar to Rust, yet so much more terribly uninviting."
+ cost = 2
+ spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/grasp_of_decay
+ next_knowledge = list(/datum/eldritch_knowledge/final/rust_final)
+ route = PATH_RUST
+
/datum/eldritch_knowledge/final/rust_final
name = "Rustbringer's Oath"
desc = "Bring three corpses onto a transmutation rune. After you finish the ritual, rust will now automatically spread from the rune. Your healing on rust is also tripled, while you become more resilient overall."
diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm
index 7bb7f1aa76..cb534b6613 100644
--- a/code/modules/antagonists/revenant/revenant_spawn_event.dm
+++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm
@@ -4,7 +4,6 @@
name = "Spawn Revenant" // Did you mean 'griefghost'?
typepath = /datum/round_event/ghost_role/revenant
weight = 7
- gamemode_blacklist = list("dynamic")
max_occurrences = 1
min_players = 5
diff --git a/code/modules/antagonists/swarmer/swarmer_event.dm b/code/modules/antagonists/swarmer/swarmer_event.dm
index c626799a53..43ac07cba9 100644
--- a/code/modules/antagonists/swarmer/swarmer_event.dm
+++ b/code/modules/antagonists/swarmer/swarmer_event.dm
@@ -5,7 +5,6 @@
max_occurrences = 1 //Only once okay fam
earliest_start = 30 MINUTES
min_players = 35
- gamemode_blacklist = list("dynamic")
/datum/round_event/spawn_swarmer
diff --git a/code/modules/asset_cache/asset_cache_item.dm b/code/modules/asset_cache/asset_cache_item.dm
index 41cee06db1..72d976bf11 100644
--- a/code/modules/asset_cache/asset_cache_item.dm
+++ b/code/modules/asset_cache/asset_cache_item.dm
@@ -24,12 +24,10 @@
/datum/asset_cache_item/New(name, file)
if (!isfile(file))
file = fcopy_rsc(file)
- hash = md5(file)
+
+ hash = md5asfile(file) //icons sent to the rsc sometimes md5 incorrectly
if (!hash)
- hash = md5(fcopy_rsc(file))
- if (!hash)
- CRASH("invalid asset sent to asset cache")
- debug_world_log("asset cache unexpected success of second fcopy_rsc")
+ CRASH("invalid asset sent to asset cache")
src.name = name
var/extstart = findlasttext(name, ".")
if (extstart)
diff --git a/code/modules/cargo/packs/misc.dm b/code/modules/cargo/packs/misc.dm
index c6728831eb..394b86bb81 100644
--- a/code/modules/cargo/packs/misc.dm
+++ b/code/modules/cargo/packs/misc.dm
@@ -13,6 +13,13 @@
//////////////////// Paperwork and Writing Supplies //////////////////////////
//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_pack/misc/anvil
+ name = "Anvil Crate"
+ desc = "An anvil in a crate, we had to dig this out of the old warehouse. It's got wheels on it so you can move it."
+ cost = 7500
+ contains = list(/obj/structure/anvil/obtainable/basic)
+
/datum/supply_pack/misc/artsupply
name = "Art Supplies"
desc = "Make some happy little accidents with six canvasses, two easels, two boxes of crayons, and a rainbow crayon!"
diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm
index fd6fee362d..125bfe2034 100644
--- a/code/modules/cargo/packs/science.dm
+++ b/code/modules/cargo/packs/science.dm
@@ -192,3 +192,49 @@
crate_type = /obj/structure/closet/crate/secure/science
dangerous = TRUE
+//////// RAW ANOMALY CORES
+
+/datum/supply_pack/science/raw_flux_anomaly
+ name = "Raw Flux Anomaly"
+ desc = "The raw core of a flux anomaly, ready to be implosion-compressed into a powerful artifact."
+ cost = 5000
+ access = ACCESS_TOX
+ contains = list(/obj/item/raw_anomaly_core/flux)
+ crate_name = "raw flux anomaly"
+ crate_type = /obj/structure/closet/crate/secure/science
+
+/datum/supply_pack/science/raw_grav_anomaly
+ name = "Raw Gravitational Anomaly"
+ desc = "The raw core of a gravitational anomaly, ready to be implosion-compressed into a powerful artifact."
+ cost = 5000
+ access = ACCESS_TOX
+ contains = list(/obj/item/raw_anomaly_core/grav)
+ crate_name = "raw pyro anomaly"
+ crate_type = /obj/structure/closet/crate/secure/science
+
+/datum/supply_pack/science/raw_vortex_anomaly
+ name = "Raw Vortex Anomaly"
+ desc = "The raw core of a vortex anomaly, ready to be implosion-compressed into a powerful artifact."
+ cost = 5000
+ access = ACCESS_TOX
+ contains = list(/obj/item/raw_anomaly_core/vortex)
+ crate_name = "raw vortex anomaly"
+ crate_type = /obj/structure/closet/crate/secure/science
+
+/datum/supply_pack/science/raw_bluespace_anomaly
+ name = "Raw Bluespace Anomaly"
+ desc = "The raw core of a bluespace anomaly, ready to be implosion-compressed into a powerful artifact."
+ cost = 5000
+ access = ACCESS_TOX
+ contains = list(/obj/item/raw_anomaly_core/bluespace)
+ crate_name = "raw bluespace anomaly"
+ crate_type = /obj/structure/closet/crate/secure/science
+
+/datum/supply_pack/science/raw_pyro_anomaly
+ name = "Raw Pyro Anomaly"
+ desc = "The raw core of a pyro anomaly, ready to be implosion-compressed into a powerful artifact."
+ cost = 5000
+ access = ACCESS_TOX
+ contains = list(/obj/item/raw_anomaly_core/pyro)
+ crate_name = "raw pyro anomaly"
+ crate_type = /obj/structure/closet/crate/secure/science
diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm
index 395b3b88a5..7f0dedaab6 100644
--- a/code/modules/events/anomaly_bluespace.dm
+++ b/code/modules/events/anomaly_bluespace.dm
@@ -4,7 +4,6 @@
max_occurrences = 1
weight = 5
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_bluespace
startWhen = 3
diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm
index a9a7ed50b9..8047976330 100644
--- a/code/modules/events/anomaly_flux.dm
+++ b/code/modules/events/anomaly_flux.dm
@@ -5,7 +5,6 @@
min_players = 10
max_occurrences = 5
weight = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_flux
startWhen = 10
diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm
index cabd7face8..7d2bb33889 100644
--- a/code/modules/events/anomaly_grav.dm
+++ b/code/modules/events/anomaly_grav.dm
@@ -4,7 +4,6 @@
max_occurrences = 5
weight = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_grav
diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm
index 8c8fbd6d36..350c8fc946 100644
--- a/code/modules/events/anomaly_pyro.dm
+++ b/code/modules/events/anomaly_pyro.dm
@@ -4,7 +4,6 @@
max_occurrences = 5
weight = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_pyro
startWhen = 3
diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm
index 96d084873d..e2a4ceadf3 100644
--- a/code/modules/events/anomaly_vortex.dm
+++ b/code/modules/events/anomaly_vortex.dm
@@ -5,7 +5,6 @@
min_players = 20
max_occurrences = 2
weight = 5
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_vortex
startWhen = 10
diff --git a/code/modules/events/brain_trauma.dm b/code/modules/events/brain_trauma.dm
index 0a07e2a693..75c514774c 100644
--- a/code/modules/events/brain_trauma.dm
+++ b/code/modules/events/brain_trauma.dm
@@ -3,6 +3,13 @@
typepath = /datum/round_event/brain_trauma
weight = 25
+/datum/round_event_control/brain_trauma/canSpawnEvent(var/players_amt, var/gamemode)
+ var/list/enemy_roles = list("Medical Doctor","Chief Medical Officer","Paramedic")
+ for (var/mob/M in GLOB.alive_mob_list)
+ if(M.stat != DEAD && (M.mind?.assigned_role in enemy_roles))
+ return TRUE
+ return FALSE
+
/datum/round_event/brain_trauma
fakeable = FALSE
@@ -16,18 +23,19 @@
continue
if(HAS_TRAIT(H,TRAIT_EXEMPT_HEALTH_EVENTS))
continue
+ if(!is_station_level(H.z))
+ continue
traumatize(H)
break
/datum/round_event/brain_trauma/proc/traumatize(mob/living/carbon/human/H)
var/resistance = pick(
65;TRAUMA_RESILIENCE_BASIC,
- 30;TRAUMA_RESILIENCE_SURGERY,
- 5;TRAUMA_RESILIENCE_LOBOTOMY)
+ 35;TRAUMA_RESILIENCE_SURGERY)
var/trauma_type = pickweight(list(
- BRAIN_TRAUMA_MILD = 60,
- BRAIN_TRAUMA_SEVERE = 30,
+ BRAIN_TRAUMA_MILD = 80,
+ BRAIN_TRAUMA_SEVERE = 10,
BRAIN_TRAUMA_SPECIAL = 10
))
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 1c88e68377..f0e4bd4a53 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -5,7 +5,6 @@
min_players = 15
max_occurrences = 1
- gamemode_blacklist = list("dynamic")
/datum/round_event/brand_intelligence
announceWhen = 21
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index 2c553fc8a7..d08e6267a0 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -5,7 +5,6 @@
min_players = 2
earliest_start = 10 MINUTES
max_occurrences = 6
- gamemode_blacklist = list("dynamic")
/datum/round_event/carp_migration
announceWhen = 3
diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm
index 45fa1c8a01..cb62e0df22 100644
--- a/code/modules/events/communications_blackout.dm
+++ b/code/modules/events/communications_blackout.dm
@@ -2,7 +2,6 @@
name = "Communications Blackout"
typepath = /datum/round_event/communications_blackout
weight = 30
- gamemode_blacklist = list("dynamic")
/datum/round_event/communications_blackout
announceWhen = 1
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 860685c787..eb7edcafbf 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -5,7 +5,6 @@
max_occurrences = 1000
earliest_start = 0 MINUTES
alert_observers = FALSE
- gamemode_blacklist = list("dynamic")
/datum/round_event/space_dust
startWhen = 1
@@ -29,4 +28,4 @@
fakeable = FALSE
/datum/round_event/sandstorm/tick()
- spawn_meteors(10, GLOB.meteorsC)
\ No newline at end of file
+ spawn_meteors(10, GLOB.meteorsC)
diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm
index 5e5e318e3c..b850b4db62 100644
--- a/code/modules/events/electrical_storm.dm
+++ b/code/modules/events/electrical_storm.dm
@@ -5,7 +5,6 @@
min_players = 5
weight = 40
alert_observers = FALSE
- gamemode_blacklist = list("dynamic")
/datum/round_event/electrical_storm
var/lightsoutAmount = 1
diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm
index b3bc571a4a..8c33e69107 100644
--- a/code/modules/events/heart_attack.dm
+++ b/code/modules/events/heart_attack.dm
@@ -4,7 +4,6 @@
weight = 20
max_occurrences = 2
min_players = 40 // To avoid shafting lowpop
- gamemode_blacklist = list("dynamic")
/datum/round_event/heart_attack/start()
var/list/heart_attack_contestants = list()
@@ -20,4 +19,4 @@
var/mob/living/carbon/human/winner = pickweight(heart_attack_contestants)
var/datum/disease/D = new /datum/disease/heart_failure()
winner.ForceContractDisease(D, FALSE, TRUE)
- announce_to_ghosts(winner)
\ No newline at end of file
+ announce_to_ghosts(winner)
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index b15e9d1f99..c75aff0c4a 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -3,7 +3,6 @@
/datum/round_event_control/ion_storm
name = "Ion Storm"
typepath = /datum/round_event/ion_storm
- gamemode_blacklist = list("dynamic")
weight = 15
min_players = 2
diff --git a/code/modules/events/major_dust.dm b/code/modules/events/major_dust.dm
index c594d7b3c0..d7d8f1aec8 100644
--- a/code/modules/events/major_dust.dm
+++ b/code/modules/events/major_dust.dm
@@ -2,7 +2,6 @@
name = "Major Space Dust"
typepath = /datum/round_event/meteor_wave/major_dust
weight = 8
- gamemode_blacklist = list("dynamic")
/datum/round_event/meteor_wave/major_dust
wave_name = "space dust"
diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm
index 7763f9950d..515e0fe1b3 100644
--- a/code/modules/events/meteor_wave.dm
+++ b/code/modules/events/meteor_wave.dm
@@ -10,7 +10,6 @@
min_players = 15
max_occurrences = 3
earliest_start = 25 MINUTES
- gamemode_blacklist = list("dynamic")
/datum/round_event/meteor_wave
startWhen = 6
diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm
index 6e5512a617..698f5130f1 100644
--- a/code/modules/events/nightmare.dm
+++ b/code/modules/events/nightmare.dm
@@ -2,7 +2,6 @@
name = "Spawn Nightmare"
typepath = /datum/round_event/ghost_role/nightmare
max_occurrences = 1
- gamemode_blacklist = list("dynamic")
min_players = 20
/datum/round_event/ghost_role/nightmare
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 9ab5e8d517..4cbfb8ae9a 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -5,7 +5,7 @@
max_occurrences = 1
min_players = 10
earliest_start = 30 MINUTES
- gamemode_blacklist = list("nuclear","dynamic")
+ gamemode_blacklist = list("nuclear")
/datum/round_event_control/pirates/preRunEvent()
if (!SSmapping.empty_space)
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 457b5bd4ec..5ef30d0030 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -4,7 +4,6 @@
weight = 2
min_players = 15
earliest_start = 30 MINUTES
- gamemode_blacklist = list("dynamic")
/datum/round_event/portal_storm/syndicate_shocktroop
boss_types = list(/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper = 2)
diff --git a/code/modules/events/processor_overload.dm b/code/modules/events/processor_overload.dm
index 22e475a8ef..6bedce6b4b 100644
--- a/code/modules/events/processor_overload.dm
+++ b/code/modules/events/processor_overload.dm
@@ -3,7 +3,6 @@
typepath = /datum/round_event/processor_overload
weight = 15
min_players = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/processor_overload
announceWhen = 1
diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm
index 0a5bedb464..36e16bb2c9 100644
--- a/code/modules/events/radiation_storm.dm
+++ b/code/modules/events/radiation_storm.dm
@@ -2,7 +2,6 @@
name = "Radiation Storm"
typepath = /datum/round_event/radiation_storm
max_occurrences = 1
- gamemode_blacklist = list("dynamic")
/datum/round_event/radiation_storm
diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm
index d1c327e0f7..23ce6ce730 100644
--- a/code/modules/events/spider_infestation.dm
+++ b/code/modules/events/spider_infestation.dm
@@ -2,7 +2,6 @@
name = "Spider Infestation"
typepath = /datum/round_event/spider_infestation
weight = 5
- gamemode_blacklist = list("dynamic")
max_occurrences = 1
min_players = 15
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index dc672cec45..cd7b23a577 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -3,7 +3,6 @@
typepath = /datum/round_event/vent_clog
weight = 10
max_occurrences = 3
- gamemode_blacklist = list("dynamic")
min_players = 25
/datum/round_event/vent_clog
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index 1b4716b407..0920c9ccb6 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -4,7 +4,6 @@
max_occurrences = 3
weight = 2
min_players = 2
- gamemode_blacklist = list("dynamic")
/datum/round_event/wormholes
diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm
index 80ce2522ff..a4d88158e2 100644
--- a/code/modules/library/lib_items.dm
+++ b/code/modules/library/lib_items.dm
@@ -22,7 +22,7 @@
max_integrity = 200
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
var/state = 0
- var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/book) //Things allowed in the bookcase
+ var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/book, /obj/item/gun/magic/wand/book) //Things allowed in the bookcase
/obj/structure/bookcase/examine(mob/user)
. = ..()
@@ -192,7 +192,7 @@
desc = "Crack it open, inhale the musk of its pages, and learn something new."
throw_speed = 1
throw_range = 5
- w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever)
+ w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever)
attack_verb = list("bashed", "whacked", "educated")
resistance_flags = FLAMMABLE
var/dat //Actual page content
diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm
index 9ccb7c0efc..b2cd840468 100644
--- a/code/modules/mining/equipment/mineral_scanner.dm
+++ b/code/modules/mining/equipment/mineral_scanner.dm
@@ -12,13 +12,14 @@
slot_flags = ITEM_SLOT_BELT
var/cooldown = 35
var/current_cooldown = 0
+ var/range = 7
/obj/item/mining_scanner/attack_self(mob/user)
if(!user.client)
return
if(current_cooldown <= world.time)
current_cooldown = world.time + cooldown
- mineral_scan_pulse(get_turf(user))
+ mineral_scan_pulse(get_turf(user), range)
//Debug item to identify all ore spread quickly
/obj/item/mining_scanner/admin
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index 27259ce812..31ee37c3b0 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -66,6 +66,16 @@
force = 19
custom_materials = list(/datum/material/diamond=4000)
+/obj/item/pickaxe/rosegold
+ name = "rose gold pickaxe"
+ icon_state = "rgpickaxe"
+ item_state = "rgpickaxe"
+ toolspeed = 0.1
+ desc = "A pickaxe with a light rose gold head and some red glowing runes. Extremely robust at cracking rock walls and digging up dirt."
+ force = 19
+ custom_materials = list(/datum/material/gold=4000)
+ digrange = 3
+
/obj/item/pickaxe/plasteel
name = "plasteel-tipped pickaxe"
icon_state = "titaxe"
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 819b005b43..72e52176bb 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -7,78 +7,202 @@
icon_state = "necrocrate"
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+/obj/structure/closet/crate/necropolis/tendril/random
+ name = "necropolis crate"
+ desc = "A chest for a chest, a head for a head."
+
+/obj/structure/closet/crate/necropolis/tendril/random/PopulateContents()
+ var/loot = rand(1,3)
+ switch(loot)
+ if(1)
+ new /obj/structure/closet/crate/necropolis/tendril/magic(src)
+ if(2)
+ new /obj/structure/closet/crate/necropolis/tendril/weapon_armor(src)
+ if(3)
+ new /obj/structure/closet/crate/necropolis/tendril/misc(src)
+
/obj/structure/closet/crate/necropolis/tendril
desc = "It's watching you suspiciously."
-/obj/structure/closet/crate/necropolis/tendril/PopulateContents()
- var/loot = rand(1,29)
+/obj/structure/closet/crate/necropolis/tendril/magic
+ name = "relic necropolis chest"
+
+/obj/structure/closet/crate/necropolis/tendril/weapon_armor
+ name = "armament necropolis chest"
+
+/obj/structure/closet/crate/necropolis/tendril/misc
+
+/obj/structure/closet/crate/necropolis/tendril/all
+ desc = "It's watching you suspiciously."
+
+/obj/structure/closet/crate/necropolis/tendril/magic/PopulateContents()
+ var/loot = rand(1,10)
switch(loot)
if(1)
- new /obj/item/shared_storage/red(src)
- if(2)
- new /obj/item/clothing/suit/space/hardsuit/cult(src)
- if(3)
new /obj/item/soulstone/anybody(src)
+ if(2)
+ new /obj/item/rod_of_asclepius(src)
+ if(3)
+ new /obj/item/organ/heart/cursed/wizard(src)
if(4)
- new /obj/item/katana/cursed(src)
+ new /obj/item/book/granter/spell/summonitem(src)
if(5)
- new /obj/item/clothing/glasses/godeye(src)
+ new /obj/item/borg/upgrade/modkit/lifesteal(src)
+ new /obj/item/bedsheet/cult(src)
if(6)
- new /obj/item/reagent_containers/glass/bottle/potion/flight(src)
+ new /obj/item/clothing/neck/necklace/memento_mori(src)
if(7)
- new /obj/item/pickaxe/diamond(src)
+ new /obj/item/warp_cube/red(src)
if(8)
+ new /obj/item/immortality_talisman(src)
+ if(9)
+ new /obj/item/gun/magic/wand/book/healing(src)
+ if(10)
+ new /obj/item/reagent_containers/glass/bottle/ichor/red(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/blue(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/green(src)
+
+/obj/structure/closet/crate/necropolis/tendril/weapon_armor/PopulateContents()
+ var/loot = rand(1,11)
+ switch(loot)
+ if(1)
+ new /obj/item/clothing/suit/space/hardsuit/cult(src)
+ if(2)
+ new /obj/item/katana/lavaland(src)
+ if(3)
if(prob(50))
new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src)
else
new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
- if(9)
- new /obj/item/rod_of_asclepius(src)
- if(10)
- new /obj/item/organ/heart/cursed/wizard(src)
- if(11)
- new /obj/item/ship_in_a_bottle(src)
- if(12)
+ if(4)
new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/beserker/old(src)
- if(13)
- new /obj/item/jacobs_ladder(src)
- if(14)
+ if(5)
new /obj/item/nullrod/scythe/talking(src)
- if(15)
+ if(6)
new /obj/item/nullrod/armblade(src)
- if(16)
- new /obj/item/guardiancreator(src)
- if(17)
+ if(7)
+ new /obj/item/reagent_containers/food/drinks/bottle/holywater/hell(src)
+ new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor/old(src)
+ if(8)
+ new /obj/item/grenade/clusterbuster/inferno(src)
+ if(9)
+ new /obj/item/gun/magic/wand/book/shock(src)
+ if(10)
+ new /obj/item/gun/magic/wand/book/page(src)
+ if(11)
+ new /obj/item/gun/magic/wand/book/spark(src)
+
+/obj/structure/closet/crate/necropolis/tendril/misc/PopulateContents()
+ var/loot = rand(1,11)
+ switch(loot)
+ if(1)
+ new /obj/item/shared_storage/red(src)
+ if(2)
+ new /obj/item/reagent_containers/glass/bottle/potion/flight(src)
+ if(3)
+ new /obj/item/ship_in_a_bottle(src)
+ if(4)
+ new /obj/item/voodoo(src)
+ if(5)
+ new /obj/item/book_of_babel(src)
+ if(6)
+ new /obj/item/jacobs_ladder(src)
+ if(7)
if(prob(50))
new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src)
else
new /obj/item/disk/design_disk/modkit_disc/bounty(src)
- if(18)
- new /obj/item/warp_cube/red(src)
- if(19)
+ if(8)
new /obj/item/wisp_lantern(src)
- if(20)
- new /obj/item/immortality_talisman(src)
- if(21)
- new /obj/item/gun/magic/hook(src)
- if(22)
+ if(9)
+ new /obj/item/pickaxe/rosegold(src)
+ if(10)
+ new /obj/item/bedsheet/cosmos(src)
+ new /obj/item/melee/skateboard/hoverboard(src)
+ if(11)
+ if(prob(50))
+ new /obj/item/malf_upgrade
+ else
+ new /obj/item/disk/tech_disk/illegal
+ if(12)
+ new /obj/item/clothing/suit/space/hardsuit/cult(src)
+ if(13)
+ new /obj/item/katana/lavaland(src)
+ if(14)
+ if(prob(50))
+ new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src)
+ else
+ new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
+
+/obj/structure/closet/crate/necropolis/tendril/all/PopulateContents()
+ var/loot = rand(1,31)
+ switch(loot)
+ if(1)
+ new /obj/item/shared_storage/red(src)
+ if(2)
+ new /obj/item/reagent_containers/glass/bottle/potion/flight(src)
+ if(3)
+ new /obj/item/ship_in_a_bottle(src)
+ if(4)
new /obj/item/voodoo(src)
- if(23)
- new /obj/item/grenade/clusterbuster/inferno(src)
- if(24)
+ if(5)
+ new /obj/item/book_of_babel(src)
+ if(6)
+ new /obj/item/jacobs_ladder(src)
+ if(7)
+ if(prob(50))
+ new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src)
+ else
+ new /obj/item/disk/design_disk/modkit_disc/bounty(src)
+ if(8)
+ new /obj/item/wisp_lantern(src)
+ if(9)
+ new /obj/item/pickaxe/rosegold(src)
+ if(10)
+ new /obj/item/bedsheet/cosmos(src)
+ new /obj/item/melee/skateboard/hoverboard(src)
+ if(11)
+ new /obj/item/disk/tech_disk/illegal
+ if(15)
+ new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/beserker/old(src)
+ if(16)
+ new /obj/item/nullrod/scythe/talking(src)
+ if(17)
+ new /obj/item/nullrod/armblade(src)
+ if(18)
new /obj/item/reagent_containers/food/drinks/bottle/holywater/hell(src)
new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor/old(src)
+ if(19)
+ new /obj/item/grenade/clusterbuster/inferno(src)
+ if(20)
+ new /obj/item/gun/magic/wand/book/shock(src)
+ if(21)
+ new /obj/item/gun/magic/wand/book/page(src)
+ if(22)
+ new /obj/item/gun/magic/wand/book/spark(src)
+ if(23)
+ new /obj/item/soulstone/anybody(src)
+ if(24)
+ new /obj/item/rod_of_asclepius(src)
if(25)
- new /obj/item/book/granter/spell/summonitem(src)
+ new /obj/item/organ/heart/cursed/wizard(src)
if(26)
- new /obj/item/book_of_babel(src)
+ new /obj/item/book/granter/spell/summonitem(src)
if(27)
new /obj/item/borg/upgrade/modkit/lifesteal(src)
new /obj/item/bedsheet/cult(src)
if(28)
new /obj/item/clothing/neck/necklace/memento_mori(src)
+ if(28)
+ new /obj/item/warp_cube/red(src)
if(29)
- new /obj/item/disk/tech_disk/illegal(src)
+ new /obj/item/immortality_talisman(src)
+ if(30)
+ new /obj/item/gun/magic/wand/book/healing(src)
+ if(31)
+ new /obj/item/reagent_containers/glass/bottle/ichor/red(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/blue(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/green(src)
//KA modkit design discs
/obj/item/disk/design_disk/modkit_disc
@@ -744,7 +868,7 @@
new /obj/item/lava_staff(src)
if(3)
new /obj/item/book/granter/spell/sacredflame(src)
- new /obj/item/gun/magic/wand/fireball(src)
+ new /obj/item/gun/magic/hook(src)
if(4)
new /obj/item/dragons_blood(src)
@@ -983,7 +1107,7 @@
if(2)
new /obj/item/gun/ballistic/revolver/doublebarrel/super(src)
if(3)
- new /obj/item/gun/magic/staff/spellblade(src)
+ new /obj/item/guardiancreator(src)
/obj/structure/closet/crate/necropolis/bubblegum/crusher
name = "bloody bubblegum chest"
@@ -1072,6 +1196,7 @@
var/random_crystal = pick(choices)
new random_crystal(src)
new /obj/item/organ/vocal_cords/colossus(src)
+ new /obj/item/clothing/glasses/godeye(src)
/obj/structure/closet/crate/necropolis/colossus/crusher
name = "angelic colossus chest"
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index ed509f900a..89f5783124 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1956,19 +1956,19 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + min(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
switch((loc_temp - H.bodytemperature)*thermal_protection)
if(-INFINITY to -50)
- H.throw_alert("temp", /obj/screen/alert/cold, 3)
+ H.throw_alert("tempfeel", /obj/screen/alert/cold, 3)
if(-50 to -35)
- H.throw_alert("temp", /obj/screen/alert/cold, 2)
+ H.throw_alert("tempfeel", /obj/screen/alert/cold, 2)
if(-35 to -20)
- H.throw_alert("temp", /obj/screen/alert/cold, 1)
+ H.throw_alert("tempfeel", /obj/screen/alert/cold, 1)
if(-20 to 0) //This is the sweet spot where air is considered normal
- H.clear_alert("temp")
+ H.clear_alert("tempfeel")
if(0 to 15) //When the air around you matches your body's temperature, you'll start to feel warm.
- H.throw_alert("temp", /obj/screen/alert/hot, 1)
+ H.throw_alert("tempfeel", /obj/screen/alert/hot, 1)
if(15 to 30)
- H.throw_alert("temp", /obj/screen/alert/hot, 2)
+ H.throw_alert("tempfeel", /obj/screen/alert/hot, 2)
if(30 to INFINITY)
- H.throw_alert("temp", /obj/screen/alert/hot, 3)
+ H.throw_alert("tempfeel", /obj/screen/alert/hot, 3)
// +/- 50 degrees from 310K is the 'safe' zone, where no damage is dealt.
if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !HAS_TRAIT(H, TRAIT_RESISTHEAT))
@@ -1986,6 +1986,14 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else
firemodifier = min(firemodifier, 0)
burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0) // this can go below 5 at log 2.5
+ if (burn_damage)
+ switch(burn_damage)
+ if(0 to 2)
+ H.throw_alert("temp", /obj/screen/alert/sweat, 1)
+ if(2 to 4)
+ H.throw_alert("temp", /obj/screen/alert/sweat, 2)
+ else
+ H.throw_alert("temp", /obj/screen/alert/sweat, 3)
burn_damage = burn_damage * heatmod * H.physiology.heat_mod
if (H.stat < UNCONSCIOUS && (prob(burn_damage) * 10) / 4) //40% for level 3 damage on humans
H.emote("scream")
@@ -1998,14 +2006,18 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = ((BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
switch(H.bodytemperature)
if(200 to BODYTEMP_COLD_DAMAGE_LIMIT)
+ H.throw_alert("temp", /obj/screen/alert/shiver, 1)
H.apply_damage(COLD_DAMAGE_LEVEL_1*coldmod*H.physiology.cold_mod, BURN)
if(120 to 200)
+ H.throw_alert("temp", /obj/screen/alert/shiver, 2)
H.apply_damage(COLD_DAMAGE_LEVEL_2*coldmod*H.physiology.cold_mod, BURN)
else
+ H.throw_alert("temp", /obj/screen/alert/shiver, 3)
H.apply_damage(COLD_DAMAGE_LEVEL_3*coldmod*H.physiology.cold_mod, BURN)
else
H.remove_movespeed_modifier(/datum/movespeed_modifier/cold)
+ H.clear_alert("temp")
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
diff --git a/code/modules/mob/living/simple_animal/astral.dm b/code/modules/mob/living/simple_animal/astral.dm
index f79a2b5b3e..ec2180ccb3 100644
--- a/code/modules/mob/living/simple_animal/astral.dm
+++ b/code/modules/mob/living/simple_animal/astral.dm
@@ -24,6 +24,7 @@
incorporeal_move = 1
alpha = 50
speak_emote = list("echos")
+ rad_flags = RAD_NO_CONTAMINATE
movement_type = FLYING
var/pseudo_death = FALSE
var/posses_safe = FALSE
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index 795184bad5..3de4c8b41b 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -141,7 +141,7 @@ Difficulty: Medium
loot = list(/obj/item/staff/storm)
elimination = 0
else if(prob(20))
- loot = list(/obj/structure/closet/crate/necropolis/tendril)
+ loot = list(/obj/structure/closet/crate/necropolis/tendril/random) //This one spawns a chest that could be any of the three types
..()
/obj/item/gps/internal/legion
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index e3edd171c0..e347056924 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -25,6 +25,7 @@
var/list/attack_action_types = list()
var/can_talk = FALSE
var/obj/loot_drop = null
+ var/crate_type = /obj/structure/closet/crate/necropolis/tendril
var/owner
//Gives player-controlled variants the ability to swap attacks
@@ -182,7 +183,7 @@ While using this makes the system rely on OnFire, it still gives options for tim
activator = null
-obj/structure/elite_tumor/proc/spawn_elite(var/mob/dead/observer/elitemind)
+/obj/structure/elite_tumor/proc/spawn_elite(var/mob/dead/observer/elitemind)
var/selectedspawn = pick(potentialspawns)
mychild = new selectedspawn(loc)
visible_message("[mychild] emerges from [src]!")
@@ -193,7 +194,7 @@ obj/structure/elite_tumor/proc/spawn_elite(var/mob/dead/observer/elitemind)
icon_state = "tumor_popped"
INVOKE_ASYNC(src, .proc/arena_checks)
-obj/structure/elite_tumor/proc/return_elite()
+/obj/structure/elite_tumor/proc/return_elite()
mychild.forceMove(loc)
visible_message("[mychild] emerges from [src]!")
playsound(loc,'sound/effects/phasein.ogg', 200, 0, 50, TRUE, TRUE)
@@ -271,11 +272,11 @@ obj/structure/elite_tumor/proc/return_elite()
visible_message("[mychild] suddenly reappears above [src]!")
playsound(loc,'sound/effects/phasein.ogg', 200, 0, 50, TRUE, TRUE)
-obj/structure/elite_tumor/proc/onEliteLoss()
+/obj/structure/elite_tumor/proc/onEliteLoss()
playsound(loc,'sound/effects/tendril_destroyed.ogg', 200, 0, 50, TRUE, TRUE)
visible_message("[src] begins to convulse violently before beginning to dissipate.")
visible_message("As [src] closes, something is forced up from down below.")
- var/obj/structure/closet/crate/necropolis/tendril/lootbox = new /obj/structure/closet/crate/necropolis/tendril(loc)
+ var/obj/structure/closet/crate/necropolis/tendril/lootbox = new mychild.crate_type(loc)
if(!boosted)
mychild = null
activator = null
@@ -290,7 +291,7 @@ obj/structure/elite_tumor/proc/onEliteLoss()
activator = null
qdel(src)
-obj/structure/elite_tumor/proc/onEliteWon()
+/obj/structure/elite_tumor/proc/onEliteWon()
activity = TUMOR_PASSIVE
activator = null
mychild.revive(full_heal = TRUE, admin_revive = TRUE)
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
index d9f3cfba3d..4e8f4f6160 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
@@ -40,6 +40,7 @@
mouse_opacity = MOUSE_OPACITY_ICON
deathmessage = "explodes into gore!"
loot_drop = /obj/item/crusher_trophy/broodmother_tongue
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/weapon_armor
attack_action_types = list(/datum/action/innate/elite_attack/tentacle_patch,
/datum/action/innate/elite_attack/spawn_children,
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
index fce5b636e4..032bc2d60d 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
@@ -38,7 +38,7 @@
deathsound = 'sound/magic/demon_dies.ogg'
deathmessage = "begins to shudder as it becomes transparent..."
loot_drop = /obj/item/clothing/neck/cloak/herald_cloak
-
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/magic
can_talk = 1
attack_action_types = list(/datum/action/innate/elite_attack/herald_trishot,
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
index 15ed0135fe..6c13bb903e 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
@@ -38,7 +38,7 @@
deathsound = 'sound/magic/curse.ogg'
deathmessage = "'s arms reach out before it falls apart onto the floor, lifeless."
loot_drop = /obj/item/crusher_trophy/legionnaire_spine
-
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/misc
attack_action_types = list(/datum/action/innate/elite_attack/legionnaire_charge,
/datum/action/innate/elite_attack/head_detach,
/datum/action/innate/elite_attack/bonfire_teleport,
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
index 7995e4d20f..b3300a86bb 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
@@ -38,7 +38,7 @@
deathsound = 'sound/magic/repulse.ogg'
deathmessage = "'s lights flicker, before its top part falls down."
loot_drop = /obj/item/clothing/accessory/pandora_hope
-
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/magic
attack_action_types = list(/datum/action/innate/elite_attack/singular_shot,
/datum/action/innate/elite_attack/magic_box,
/datum/action/innate/elite_attack/pandora_teleport,
@@ -190,4 +190,4 @@
/obj/item/clothing/accessory/pandora_hope/on_uniform_dropped(obj/item/clothing/under/U, user)
var/mob/living/L = user
if(L && L.mind)
- SEND_SIGNAL(L, COMSIG_CLEAR_MOOD_EVENT, "hope_lavaland")
+ SEND_SIGNAL(L, COMSIG_CLEAR_MOOD_EVENT, "hope_lavaland")
\ No newline at end of file
diff --git a/code/modules/pool/pool_controller.dm b/code/modules/pool/pool_controller.dm
index c9bc6be3e5..17faa7a3e3 100644
--- a/code/modules/pool/pool_controller.dm
+++ b/code/modules/pool/pool_controller.dm
@@ -210,7 +210,7 @@
for(var/datum/reagent/R in reagents.reagent_list)
if(R.reagent_state == SOLID)
R.reagent_state = LIQUID
- if(!swimee.reagents.has_reagent(POOL_NO_OVERDOSE_MEDICINE_MAX))
+ if(!swimee.reagents.has_reagent(R.type,POOL_NO_OVERDOSE_MEDICINE_MAX))
swimee.reagents.add_reagent(R.type, 0.5) //osmosis
reagents.reaction(swimee, VAPOR, 0.03) //3 percent. Need to find a way to prevent this from stacking chems at some point like the above.
for(var/obj/objects in W)
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index fd8f900552..e3fdbd3654 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -639,7 +639,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(produces_gas)
env.merge(removed)
air_update_turf()
-
+
/*********
END CITADEL CHANGES
*********/
@@ -985,6 +985,13 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
layer = ABOVE_MOB_LAYER
moveable = TRUE
+/obj/machinery/power/supermatter_crystal/shard/examine(mob/user)
+ . = ..()
+ if(anchored)
+ . += "[src] is anchored to the floor."
+ else
+ . += "[src] is unanchored, but can be bolted down."
+
/obj/machinery/power/supermatter_crystal/shard/engine
name = "anchored supermatter shard"
is_main_engine = TRUE
@@ -1026,12 +1033,12 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(L)
switch(type)
if(FLUX_ANOMALY)
- var/obj/effect/anomaly/flux/A = new(L, 300, FALSE)
+ var/obj/effect/anomaly/flux/A = new(L, 300, SUPERMATTER_ANOMALY_DROP_CHANCE)
A.explosive = FALSE
if(GRAVITATIONAL_ANOMALY)
- new /obj/effect/anomaly/grav(L, 250, FALSE)
+ new /obj/effect/anomaly/grav(L, 250, SUPERMATTER_ANOMALY_DROP_CHANCE)
if(PYRO_ANOMALY)
- new /obj/effect/anomaly/pyro(L, 200, FALSE)
+ new /obj/effect/anomaly/pyro(L, 200, SUPERMATTER_ANOMALY_DROP_CHANCE)
/obj/machinery/power/supermatter_crystal/proc/supermatter_zap(atom/zapstart = src, range = 5, zap_str = 4000, zap_flags = ZAP_SUPERMATTER_FLAGS, list/targets_hit = list())
if(QDELETED(zapstart))
diff --git a/code/modules/projectiles/ammunition/special/magic.dm b/code/modules/projectiles/ammunition/special/magic.dm
index cc039c5f2b..8380bf82e9 100644
--- a/code/modules/projectiles/ammunition/special/magic.dm
+++ b/code/modules/projectiles/ammunition/special/magic.dm
@@ -43,3 +43,17 @@
/obj/item/ammo_casing/magic/locker
projectile_type = /obj/item/projectile/magic/locker
+
+//Spell book ammo casing
+/obj/item/ammo_casing/magic/book
+ projectile_type = /obj/item/projectile/magic/spellcard/book
+
+/obj/item/ammo_casing/magic/book/spark
+ projectile_type = /obj/item/projectile/magic/spellcard/book/spark
+
+/obj/item/ammo_casing/magic/book/heal
+ projectile_type = /obj/item/projectile/magic/spellcard/book/heal
+ harmful = FALSE
+
+/obj/item/ammo_casing/magic/book/shock
+ projectile_type = /obj/item/projectile/magic/spellcard/book/shock
diff --git a/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
index 78ca6e9280..9ea030da99 100644
--- a/code/modules/projectiles/boxes_magazines/_box_magazine.dm
+++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
@@ -20,7 +20,6 @@
var/caliber
var/multiload = 1
var/start_empty = 0
- var/load_delay = 0 //how long do we take to load (deciseconds)
var/list/bullet_cost
var/list/base_cost// override this one as well if you override bullet_cost
@@ -76,19 +75,12 @@
return 1
/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = FALSE, replace_spent = 0)
- if(INTERACTING_WITH(user, src) || INTERACTING_WITH(user, A))
- to_chat(user, "You're already doing that!")
- return FALSE
var/num_loaded = 0
if(!can_load(user))
return
if(istype(A, /obj/item/ammo_box))
var/obj/item/ammo_box/AM = A
for(var/obj/item/ammo_casing/AC in AM.stored_ammo)
- if(load_delay || AM.load_delay)
- var/loadtime = max(AM.load_delay, load_delay)
- if(!do_after(user, loadtime, target = src))
- return FALSE
var/did_load = give_round(AC, replace_spent)
if(did_load)
AM.stored_ammo -= AC
diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
index 8cd49bdf16..70058801f8 100644
--- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
+++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
@@ -156,8 +156,6 @@
max_ammo = 4
var/pixeloffsetx = 4
start_empty = TRUE
- multiload = FALSE
- load_delay = 6 //6ds
/obj/item/ammo_box/shotgun/update_overlays()
. = ..()
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 1aefa51a51..fa8099a257 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -53,8 +53,6 @@
..()
if (istype(A, /obj/item/ammo_box/magazine))
var/obj/item/ammo_box/magazine/AM = A
- if(AM.load_delay && !do_after(user, AM.load_delay, target = src))
- return FALSE
if (!magazine && istype(AM, mag_type))
if(user.transferItemToLoc(AM, src))
magazine = AM
diff --git a/code/modules/projectiles/guns/ballistic/magweapon.dm b/code/modules/projectiles/guns/ballistic/magweapon.dm
index 4e27a73300..c01a2fb74a 100644
--- a/code/modules/projectiles/guns/ballistic/magweapon.dm
+++ b/code/modules/projectiles/guns/ballistic/magweapon.dm
@@ -51,11 +51,6 @@
cell.use(shot.energy_cost)
. = ..()
-/obj/item/gun/ballistic/automatic/magrifle/emp_act(severity)
- . = ..()
- if(!(. & EMP_PROTECT_CONTENTS))
- cell.use(round(cell.charge / severity))
-
/obj/item/gun/ballistic/automatic/magrifle/get_cell()
return cell
diff --git a/code/modules/projectiles/guns/magic/spell_book.dm b/code/modules/projectiles/guns/magic/spell_book.dm
new file mode 100644
index 0000000000..752a6ae5ac
--- /dev/null
+++ b/code/modules/projectiles/guns/magic/spell_book.dm
@@ -0,0 +1,66 @@
+/obj/item/gun/magic/wand/book
+ name = "blank spellbook"
+ desc = "It's not just a book, it's a SPELL book!"
+ ammo_type = /obj/item/ammo_casing/magic
+ icon = 'icons/obj/library.dmi'
+ icon_state = "book"
+ w_class = WEIGHT_CLASS_NORMAL
+ charges = 10 //We start with max pages
+ max_charges = 10
+ variable_charges = FALSE
+
+/obj/item/gun/magic/wand/book/zap_self(mob/living/user)
+ to_chat(user, "The book has [charges] pages\s remaining.")
+
+/obj/item/gun/magic/wand/book/attackby(obj/item/S, mob/living/user, params)
+ if(!istype(S, /obj/item/paper))
+ return ..()
+ if(charges < max_charges)
+ charges++
+ recharge_newshot()
+ to_chat(user, "You add a new page to [src].")
+ qdel(S)
+ update_icon()
+ process()
+ else
+ to_chat(user, "The [src] has no more room for pages!")
+
+//////////////////////
+//Spell Book - SPARK//
+//////////////////////
+
+/obj/item/gun/magic/wand/book/spark
+ name = "Spell Book of Spark"
+ desc = "A spell book that fires burn pages to set the target ablaze!"
+ ammo_type = /obj/item/ammo_casing/magic/book/spark
+ icon_state = "spellbook_spark"
+
+//////////////////////
+//Spell Book - PAGE///
+//////////////////////
+
+/obj/item/gun/magic/wand/book/page
+ name = "Spell Book of Throw"
+ desc = "A spell book that throws pages at its target!"
+ ammo_type = /obj/item/ammo_casing/magic/book
+ icon_state = "spellbook_page"
+
+//////////////////////
+//Spell Book - SHOCK//
+//////////////////////
+
+/obj/item/gun/magic/wand/book/shock
+ name = "Spell Book of Shock"
+ desc = "A spell book that uses its pages to capture energy in the air and send it in a bolt at its target!"
+ ammo_type = /obj/item/ammo_casing/magic/book/shock
+ icon_state = "spellbook_shock"
+
+////////////////////////
+//Spell Book - HEALING//
+////////////////////////
+
+/obj/item/gun/magic/wand/book/healing
+ name = "Spell Book of Mending"
+ desc = "A spell book that uses its pages to heal and repair the target! The back of the book lists what it works on, and it seems to only be of flesh and of metal beings..."
+ ammo_type = /obj/item/ammo_casing/magic/book/heal
+ icon_state = "spellbook_healing"
diff --git a/code/modules/projectiles/projectile/magic/spellcard.dm b/code/modules/projectiles/projectile/magic/spellcard.dm
index 1b8f326573..5015a97e78 100644
--- a/code/modules/projectiles/projectile/magic/spellcard.dm
+++ b/code/modules/projectiles/projectile/magic/spellcard.dm
@@ -4,3 +4,60 @@
icon_state = "spellcard"
damage_type = BURN
damage = 2
+
+/obj/item/projectile/magic/spellcard/book
+ nodamage = FALSE
+ name = "enchanted page"
+ desc = "A piece of paper enchanted to give it extreme durability and stiffness, along with a very hot burn to anyone unfortunate enough to get hit by a charged one."
+ icon_state = "spellcard"
+ damage_type = BURN
+ damage = 12
+ flag = "magic"
+
+/obj/item/projectile/magic/spellcard/book/spark
+ damage = 4
+ var/fire_stacks = 4
+
+/obj/item/projectile/magic/spellcard/book/spark/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ var/mob/living/carbon/M = target
+ if(ismob(target))
+ if(M.anti_magic_check())
+ M.visible_message("[src] vanishes on contact with [target]!")
+ return BULLET_ACT_BLOCK
+
+ if(iscarbon(target))
+ M.adjust_fire_stacks(fire_stacks)
+ M.IgniteMob()
+ return
+ else
+ damage = 20 //If we are a simplemob we deal 5x damage
+
+/obj/item/projectile/magic/spellcard/book/shock
+ damage = 0
+ stamina = 11
+ stutter = 5
+ jitter = 20
+ knockdown = 10
+
+/obj/item/projectile/magic/spellcard/book/heal
+ damage = 0
+ nodamage = TRUE
+
+/obj/item/projectile/magic/spellcard/book/heal/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ var/mob/living/carbon/M = target
+ if(ismob(target))
+ if(M.anti_magic_check())
+ M.visible_message("[src] vanishes on contact with [target]!")
+ return BULLET_ACT_BLOCK
+ if(iscarbon(target))
+ M.visible_message("[src] mends [target]!")
+ M.adjustBruteLoss(-5) //HEALS
+ M.adjustOxyLoss(-5)
+ M.adjustBruteLoss(-5)
+ M.adjustFireLoss(-5)
+ M.adjustToxLoss(-5, TRUE) //heals TOXINLOVERs
+ M.adjustCloneLoss(-5)
+ M.adjustStaminaLoss(-5)
+ return
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 5c01fd8cf6..86f8b341d9 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2220,6 +2220,7 @@
color = "#f7685e"
metabolization_rate = REAGENTS_METABOLISM * 0.25
+
/datum/reagent/wittel
name = "Wittel"
description = "An extremely rare metallic-white substance only found on demon-class planets."
@@ -2281,6 +2282,7 @@
/datum/reagent/gravitum/on_mob_end_metabolize(mob/living/L)
L.RemoveElement(/datum/element/forced_gravity, 0)
+
//body bluids
/datum/reagent/consumable/semen
name = "Semen"
@@ -2457,3 +2459,56 @@ datum/reagent/eldritch
return
..()
+/datum/reagent/red_ichor
+ name = "Red Ichor"
+ can_synth = FALSE
+ description = "A unknown red liquid, linked to healing of most moral wounds."
+ color = "#c10000"
+ metabolization_rate = REAGENTS_METABOLISM * 2.5
+
+/datum/reagent/red_ichor/on_mob_life(mob/living/carbon/M)
+ M.adjustBruteLoss(-50)
+ M.adjustOxyLoss(-50)
+ M.adjustBruteLoss(-50)
+ M.adjustFireLoss(-50)
+ M.adjustToxLoss(-50, TRUE) //heals TOXINLOVERs
+ M.adjustCloneLoss(-50)
+ M.adjustStaminaLoss(-50)
+ ..()
+
+/datum/reagent/green_ichor
+ name = "Green Ichor"
+ can_synth = FALSE
+ description = "A unknown green liquid, linked to healing of most internal wounds."
+ color = "#158c00"
+ metabolization_rate = REAGENTS_METABOLISM * 2.5
+
+/datum/reagent/green_ichor/on_mob_life(mob/living/carbon/M)
+ M.adjustOrganLoss(ORGAN_SLOT_LUNGS, -100)
+ M.adjustOrganLoss(ORGAN_SLOT_HEART, -100)
+ M.adjustOrganLoss(ORGAN_SLOT_LIVER, -100)
+ M.adjustOrganLoss(ORGAN_SLOT_EARS, -100)
+ M.adjustOrganLoss(ORGAN_SLOT_STOMACH, -100)
+ M.adjustOrganLoss(ORGAN_SLOT_TONGUE, -100)
+ M.adjustOrganLoss(ORGAN_SLOT_EYES, -100)
+ ..()
+
+/datum/reagent/blue_ichor
+ name = "Blue Ichor"
+ can_synth = FALSE
+ description = "A unknown blue liquid, linked to healing the mind."
+ color = "#0914e0"
+ metabolization_rate = REAGENTS_METABOLISM * 2.5
+
+/datum/reagent/blue_ichor/on_mob_life(mob/living/carbon/M)
+ M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -100)
+ M.cure_all_traumas(TRAUMA_RESILIENCE_MAGIC)
+ M.hallucination = 0
+ M.dizziness = 0
+ M.disgust = 0
+ M.drowsyness = 0
+ M.stuttering = 0
+ M.confused = 0
+ M.SetSleeping(0, 0)
+ ..()
+
diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm
index 20abcf847f..2cd0a76a4b 100644
--- a/code/modules/reagents/reagent_containers/bottle.dm
+++ b/code/modules/reagents/reagent_containers/bottle.dm
@@ -416,3 +416,20 @@
/obj/item/reagent_containers/glass/bottle/bromine
name = "bromine bottle"
list_reagents = list(/datum/reagent/bromine = 30)
+
+//Ichors
+/obj/item/reagent_containers/glass/bottle/ichor
+ possible_transfer_amounts = list(1)
+ volume = 1
+
+/obj/item/reagent_containers/glass/bottle/ichor/red
+ name = "healing potion"
+ list_reagents = list(/datum/reagent/red_ichor = 1)
+
+/obj/item/reagent_containers/glass/bottle/ichor/blue
+ name = "blue potion"
+ list_reagents = list(/datum/reagent/blue_ichor = 1)
+
+/obj/item/reagent_containers/glass/bottle/ichor/green
+ name = "green potion"
+ list_reagents = list(/datum/reagent/green_ichor = 1)
\ No newline at end of file
diff --git a/code/modules/research/anomaly/explosive_compressor.dm b/code/modules/research/anomaly/explosive_compressor.dm
new file mode 100644
index 0000000000..23983e31dc
--- /dev/null
+++ b/code/modules/research/anomaly/explosive_compressor.dm
@@ -0,0 +1,152 @@
+#define MAX_RADIUS_REQUIRED 8000 //tritbomb
+#define MIN_RADIUS_REQUIRED 20 //maxcap
+/**
+ * # Explosive compressor machines
+ *
+ * The explosive compressor machine used in anomaly core production.
+ *
+ * Uses the standard toxins/tank explosion scaling to compress raw anomaly cores into completed ones. The required explosion radius increases as more cores of that type are created.
+ */
+/obj/machinery/research/explosive_compressor
+ name = "implosion compressor"
+ desc = "An advanced machine capable of implosion-compressing raw anomaly cores into finished artifacts."
+ icon = 'icons/obj/machines/research.dmi'
+ icon_state = "explosive_compressor"
+ density = TRUE
+ circuit = /obj/item/circuitboard/machine/explosive_compressor
+
+ /// The raw core inserted in the machine.
+ var/obj/item/raw_anomaly_core/inserted_core
+ /// The TTV inserted in the machine.
+ var/obj/item/transfer_valve/inserted_bomb
+ /// The last time we did say_requirements(), because someone will inevitably click spam this.
+ var/last_requirements_say = 0
+
+/obj/machinery/research/explosive_compressor/examine(mob/user)
+ . = ..()
+ . += "Ctrl-Click to remove an inserted core."
+ . += "Click with an empty hand to gather information about the required radius of an inserted core. Insert a ready TTV to start the implosion process if a core is inserted."
+
+/obj/machinery/research/explosive_compressor/attack_hand(mob/living/user)
+ . = ..()
+ if(.)
+ return
+ if(!inserted_core)
+ to_chat(user, "There is no core inserted.")
+ return
+ if(last_requirements_say + 3 SECONDS > world.time)
+ return
+ last_requirements_say = world.time
+ say_requirements(inserted_core)
+
+/obj/machinery/research/explosive_compressor/CtrlClick(mob/living/user)
+ . = ..()
+ if(!istype(user) || !user.Adjacent(src) || !(user.mobility_flags & MOBILITY_USE))
+ return
+ if(!inserted_core)
+ to_chat(user, "There is no core inserted.")
+ return
+ inserted_core.forceMove(get_turf(user))
+ to_chat(user, "You remove [inserted_core] from [src].")
+ user.put_in_hands(inserted_core)
+ inserted_core = null
+
+/**
+ * Says (no, literally) the data of required explosive power for a certain anomaly type.
+ */
+/obj/machinery/research/explosive_compressor/proc/say_requirements(obj/item/raw_anomaly_core/C)
+ var/required = get_required_radius(C.anomaly_type)
+ if(isnull(required))
+ say("Unfortunately, due to diminishing supplies of condensed anomalous matter, [C] and any cores of its type are no longer of a sufficient quality level to be compressed into a working core.")
+ else
+ say("[C] requires a minimum of a theoretical radius of [required] to successfully implode into a charged anomaly core.")
+
+/**
+ * Determines how much explosive power (last value, so light impact theoretical radius) is required to make a certain anomaly type.
+ *
+ * Returns null if the max amount has already been reached.
+ *
+ * Arguments:
+ * * anomaly_type - anomaly type define
+ */
+/obj/machinery/research/explosive_compressor/proc/get_required_radius(anomaly_type)
+ var/already_made = SSresearch.created_anomaly_types[anomaly_type]
+ var/hard_limit = SSresearch.anomaly_hard_limit_by_type[anomaly_type]
+ if(already_made >= hard_limit)
+ return //return null
+ // my crappy autoscale formula
+ // linear scaling.
+ var/radius_span = MAX_RADIUS_REQUIRED - MIN_RADIUS_REQUIRED
+ var/radius_increase_per_core = radius_span / hard_limit
+ var/radius = clamp(round(MIN_RADIUS_REQUIRED + radius_increase_per_core * already_made, 1), MIN_RADIUS_REQUIRED, MAX_RADIUS_REQUIRED)
+ return radius
+
+/obj/machinery/research/explosive_compressor/attackby(obj/item/I, mob/living/user, params)
+ if(default_unfasten_wrench(user, I))
+ return
+ if(default_deconstruction_screwdriver(user, "explosive_compressor", "explosive_compressor", I))
+ return
+ if(default_deconstruction_crowbar(I))
+ return
+ . = ..()
+ if(istype(I, /obj/item/raw_anomaly_core))
+ if(inserted_core)
+ to_chat(user, "There is already a core in [src].")
+ return
+ if(!user.transferItemToLoc(I, src))
+ to_chat(user, "[I] is stuck to your hand.")
+ return
+ inserted_core = I
+ to_chat(user, "You insert [I] into [src].")
+ return
+ if(istype(I, /obj/item/transfer_valve))
+ // If they don't have a bomb core inserted, don't let them insert this. If they do, insert and do implosion.
+ if(!inserted_core)
+ to_chat(user, "There is no core inserted in [src]. What would be the point of detonating an implosion without a core?")
+ return
+ var/obj/item/transfer_valve/valve = I
+ if(!valve.ready())
+ to_chat(user, "[valve] is incomplete.")
+ return
+ if(!user.transferItemToLoc(I, src))
+ to_chat(user, "[I] is stuck to your hand.")
+ return
+ inserted_bomb = I
+ to_chat(user, "You insert [I] and press the start button.")
+ do_implosion()
+
+/**
+ * The ""explosion"" proc.
+ */
+/obj/machinery/research/explosive_compressor/proc/do_implosion()
+ var/required_radius = get_required_radius(inserted_core.anomaly_type)
+ // By now, we should be sure that we have a core, a TTV, and that the TTV has both tanks in place.
+ var/datum/gas_mixture/mix1 = inserted_bomb.tank_one.air_contents
+ var/datum/gas_mixture/mix2 = inserted_bomb.tank_two.air_contents
+ // Snowflaked tank explosion
+ var/datum/gas_mixture/mix = new(70) // Standard tank volume, 70L
+ mix.merge(mix1)
+ mix.merge(mix2)
+ mix.react()
+ if(mix.return_pressure() < TANK_FRAGMENT_PRESSURE)
+ // They failed so miserably we're going to give them their bomb back.
+ inserted_bomb.forceMove(drop_location())
+ inserted_bomb = null
+ inserted_core.forceMove(drop_location())
+ inserted_core = null
+ say("Transfer valve resulted in negligible explosive power. Items ejected.")
+ return
+ mix.react() // build more pressure
+ var/pressure = mix.return_pressure()
+ var/range = (pressure - TANK_FRAGMENT_PRESSURE) / TANK_FRAGMENT_SCALE
+ QDEL_NULL(inserted_bomb) // bomb goes poof
+ if(range < required_radius)
+ inserted_bomb.forceMove(src)
+ say("Resultant detonation failed to produce enough implosive power to compress [inserted_core]. Core ejected.")
+ return
+ inserted_core.create_core(drop_location(), TRUE, TRUE)
+ inserted_core = null
+ say("Success. Resultant detonation has theoretical range of [range]. Required radius was [required_radius]. Core production complete.")
+
+#undef MAX_RADIUS_REQUIRED
+#undef MIN_RADIUS_REQUIRED
diff --git a/code/modules/research/anomaly/raw_anomaly.dm b/code/modules/research/anomaly/raw_anomaly.dm
new file mode 100644
index 0000000000..2342edd28f
--- /dev/null
+++ b/code/modules/research/anomaly/raw_anomaly.dm
@@ -0,0 +1,73 @@
+/**
+ * # Raw Anomaly Cores
+ *
+ * The current precursor to anomaly cores, these are manufactured into 'finished' anomaly cores for use in research, items, and more.
+ *
+ * The current amounts created is stored in SSresearch.created_anomaly_types[ANOMALY_CORE_TYPE_DEFINE] = amount
+ * The hard limits are in code/__DEFINES/anomalies.dm
+ */
+/obj/item/raw_anomaly_core
+ name = "raw anomaly core"
+ desc = "You shouldn't be seeing this. Someone screwed up."
+ icon = 'icons/obj/assemblies/new_assemblies.dmi'
+ icon_state = "broken_state"
+
+ /// Anomaly type
+ var/anomaly_type
+
+/obj/item/raw_anomaly_core/bluespace
+ name = "raw bluespace core"
+ desc = "The raw core of a bluespace anomaly, glowing and full of potential."
+ anomaly_type = /obj/item/assembly/signaler/anomaly/bluespace
+ icon_state = "rawcore_bluespace"
+
+/obj/item/raw_anomaly_core/vortex
+ name = "raw vortex core"
+ desc = "The raw core of a vortex anomaly. Feels heavy to the touch."
+ anomaly_type = /obj/item/assembly/signaler/anomaly/vortex
+ icon_state = "rawcore_vortex"
+
+/obj/item/raw_anomaly_core/grav
+ name = "raw gravity core"
+ desc = "The raw core of a gravity anomaly. The air seems attracted to it."
+ anomaly_type = /obj/item/assembly/signaler/anomaly/grav
+ icon_state = "rawcore_grav"
+
+/obj/item/raw_anomaly_core/pyro
+ desc = "The raw core of a pyro anomaly. It is warm to the touch."
+ name = "raw pyro core"
+ anomaly_type = /obj/item/assembly/signaler/anomaly/pyro
+ icon_state = "rawcore_pyro"
+
+/obj/item/raw_anomaly_core/flux
+ name = "raw flux core"
+ desc = "The raw core of a flux anomaly, faintly crackling with energy."
+ anomaly_type = /obj/item/assembly/signaler/anomaly/flux
+ icon_state = "rawcore_flux"
+
+/obj/item/raw_anomaly_core/random
+ name = "random raw core"
+ desc = "You should not see this!"
+ icon_state = "rawcore_bluespace"
+
+/obj/item/raw_anomaly_core/random/Initialize(mapload)
+ . = ..()
+ var/path = pick(subtypesof(/obj/item/raw_anomaly_core))
+ new path(loc)
+ return INITIALIZE_HINT_QDEL
+
+/**
+ * Created the resulting core after being "made" into it.
+ *
+ * Arguments:
+ * * newloc - Where the new core will be created
+ * * del_self - should we qdel(src)
+ * * count_towards_limit - should we increment the amount of created cores on SSresearch
+ */
+/obj/item/raw_anomaly_core/proc/create_core(newloc, del_self = FALSE, count_towards_limit = FALSE)
+ . = new anomaly_type(newloc)
+ if(count_towards_limit)
+ var/existing = SSresearch.created_anomaly_types[anomaly_type] || 0
+ SSresearch.created_anomaly_types[anomaly_type] = existing + 1
+ if(del_self)
+ qdel(src)
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index 1b608060ce..75e862778a 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -469,7 +469,7 @@
build_type = PROTOLATHE
materials = list(/datum/material/iron = 5000, /datum/material/glass = 1000, /datum/material/gold = 1000, /datum/material/silver = 1000)
build_path = /obj/item/holosign_creator/engineering
- category = list("Equipment")
+ category = list("Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/holosignatmos
@@ -479,7 +479,7 @@
build_type = PROTOLATHE
materials = list(/datum/material/iron = 5000, /datum/material/glass = 1000, /datum/material/gold = 1000, /datum/material/silver = 1000)
build_path = /obj/item/holosign_creator/atmos
- category = list("Equipment")
+ category = list("Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/holosignfirelock
@@ -489,7 +489,7 @@
build_type = PROTOLATHE
materials = list(/datum/material/iron = 5000, /datum/material/glass = 1000, /datum/material/gold = 1000, /datum/material/silver = 1000)
build_path = /obj/item/holosign_creator/firelock
- category = list("Equipment")
+ category = list("Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/holosigncombifan
@@ -499,7 +499,7 @@
build_type = PROTOLATHE
materials = list(/datum/material/iron = 7500, /datum/material/glass = 2500, /datum/material/silver = 2500, /datum/material/gold = 2500, /datum/material/titanium = 1750)
build_path = /obj/item/holosign_creator/combifan
- category = list("Equipment")
+ category = list("Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/forcefield_projector
@@ -509,7 +509,7 @@
build_type = PROTOLATHE
materials = list(/datum/material/iron = 2500, /datum/material/glass = 1000)
build_path = /obj/item/forcefield_projector
- category = list("Equipment")
+ category = list("Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/holobarrier_med
diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
index d97eae5766..4859fcfca0 100644
--- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm
+++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
@@ -183,6 +183,13 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337)
. = ..()
promptAndCheckIn(user)
+/obj/item/hilbertshotel/ghostdojo/linkTurfs(datum/turf_reservation/currentReservation, currentRoomnumber)
+ . = ..()
+ var/area/hilbertshotel/currentArea = get_area(locate(currentReservation.bottom_left_coords[1], currentReservation.bottom_left_coords[2], currentReservation.bottom_left_coords[3]))
+ for(var/turf/closed/indestructible/hoteldoor/door in currentArea)
+ door.parentSphere = src
+ door.desc = "The door to this hotel room. Strange, this door doesnt even seem openable. The doorknob, however, seems to buzz with unusual energy...
Alt-Click to look through the peephole."
+
//Template Stuff
/datum/map_template/hilbertshotel
name = "Hilbert's Hotel Room"
diff --git a/code/modules/smithing/anvil.dm b/code/modules/smithing/anvil.dm
new file mode 100644
index 0000000000..88711aec37
--- /dev/null
+++ b/code/modules/smithing/anvil.dm
@@ -0,0 +1,315 @@
+#define WORKPIECE_PRESENT 1
+#define WORKPIECE_INPROGRESS 2
+#define WORKPIECE_FINISHED 3
+#define WORKPIECE_SLAG 5
+
+#define RECIPE_SMALLPICK "dbp" //draw bend punch
+#define RECIPE_LARGEPICK "ddbp" //draw draw bend punch
+#define RECIPE_SHOVEL "dfup" //draw fold upset punch
+#define RECIPE_HAMMER "sfp" //shrink fold punch
+
+
+#define RECIPE_SMALLKNIFE "sdd" //shrink draw draw
+#define RECIPE_SHORTSWORD "dff" //draw fold fold
+#define RECIPE_WAKI "dfsf" //draw fold shrink fold
+#define RECIPE_SCIMITAR "dfb" //draw fold bend
+#define RECIPE_SABRE "ddsf" //draw draw shrink fold
+#define RECIPE_RAPIER "sdfd" //shrink draw fold draw
+#define RECIPE_BROADSWORD "dfuf" //draw fold upset fold
+#define RECIPE_ZWEIHANDER "udfsf" //upset draw fold shrink fold
+#define RECIPE_KATANA "fffff" //fold fold fold fold fold
+
+
+#define RECIPE_SCYTHE "bdf" //bend draw fold
+#define RECIPE_COGHEAD "bsf" //bend shrink fold.
+
+
+#define RECIPE_JAVELIN "dbf" //draw bend fold
+#define RECIPE_HALBERD "duffp" //draw upset fold fold punch
+#define RECIPE_GLAIVE "usfp" //upset shrink fold punch
+#define RECIPE_PIKE "ddbf" //draw draw bend fold
+
+/obj/structure/anvil
+ name = "anvil"
+ desc = "Base class of anvil. This shouldn't exist, but is useable."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "anvil"
+ density = TRUE
+ anchored = TRUE
+ var/workpiece_state = FALSE
+ var/datum/material/workpiece_material
+ var/anvilquality = 0
+ var/currentquality = 0 //lolman? what the fuck do these vars do?
+ var/currentsteps = 0 //even i don't know
+ var/outrightfailchance = 1 //todo: document this shit
+ var/stepsdone = ""
+ var/rng = FALSE
+ var/debug = FALSE //vv this if you want an artifact
+ var/artifactrolled = FALSE
+ var/itemqualitymax = 20
+ var/list/smithrecipes = list(RECIPE_HAMMER = /obj/item/smithing/hammerhead,
+ RECIPE_SCYTHE = /obj/item/smithing/scytheblade,
+ RECIPE_SHOVEL = /obj/item/smithing/shovelhead,
+ RECIPE_COGHEAD = /obj/item/smithing/cogheadclubhead,
+ RECIPE_JAVELIN = /obj/item/smithing/javelinhead,
+ RECIPE_LARGEPICK = /obj/item/smithing/pickaxehead,
+ RECIPE_SMALLPICK = /obj/item/smithing/prospectingpickhead,
+ RECIPE_SHORTSWORD = /obj/item/smithing/shortswordblade,
+ RECIPE_SCIMITAR = /obj/item/smithing/scimitarblade,
+ RECIPE_WAKI = /obj/item/smithing/wakiblade,
+ RECIPE_RAPIER = /obj/item/smithing/rapierblade,
+ RECIPE_SABRE = /obj/item/smithing/sabreblade,
+ RECIPE_SMALLKNIFE = /obj/item/smithing/knifeblade,
+ RECIPE_BROADSWORD = /obj/item/smithing/broadblade,
+ RECIPE_ZWEIHANDER = /obj/item/smithing/zweiblade,
+ RECIPE_KATANA = /obj/item/smithing/katanablade,
+ RECIPE_HALBERD = /obj/item/smithing/halberdhead,
+ RECIPE_GLAIVE = /obj/item/smithing/glaivehead,
+ RECIPE_PIKE = /obj/item/smithing/pikehead)
+
+/obj/structure/anvil/Initialize()
+ ..()
+ currentquality = anvilquality
+
+/obj/structure/anvil/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/ingot))
+ var/obj/item/ingot/notsword = I
+ if(workpiece_state)
+ to_chat(user, "There's already a workpiece! Finish it or take it off.")
+ return FALSE
+ if(notsword.workability == "shapeable")
+ workpiece_state = WORKPIECE_PRESENT
+ workpiece_material = notsword.custom_materials
+ to_chat(user, "You place the [notsword] on the [src].")
+ currentquality = anvilquality
+ var/skillmod = 0
+ if(user.mind.skill_holder)
+ skillmod = user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/2
+ currentquality += skillmod
+ qdel(notsword)
+ else
+ to_chat(user, "The ingot isn't workable yet!")
+ return FALSE
+ return
+ else if(istype(I, /obj/item/melee/smith/hammer))
+ var/obj/item/melee/smith/hammer/hammertime = I
+ if(workpiece_state == WORKPIECE_PRESENT || workpiece_state == WORKPIECE_INPROGRESS)
+ do_shaping(user, hammertime.qualitymod)
+ return
+ else
+ to_chat(user, "You can't work an empty anvil!")
+ return FALSE
+ return ..()
+
+/obj/structure/anvil/wrench_act(mob/living/user, obj/item/I)
+ ..()
+ default_unfasten_wrench(user, I, 5)
+ return TRUE
+
+
+/obj/structure/anvil/proc/do_shaping(mob/user, var/qualitychange)
+ currentquality += qualitychange
+ var/list/shapingsteps = list("weak hit", "strong hit", "heavy hit", "fold", "draw", "shrink", "bend", "punch", "upset") //weak/strong/heavy hit affect strength. All the other steps shape.
+ workpiece_state = WORKPIECE_INPROGRESS
+ var/stepdone = input(user, "How would you like to work the metal?") in shapingsteps
+ var/steptime = 50
+ if(user.mind.skill_holder)
+ var/skillmod = user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/10 + 1
+ steptime = 50 / skillmod
+ playsound(src, 'sound/effects/clang2.ogg',40, 2)
+ if(!do_after(user, steptime, target = src))
+ return FALSE
+ switch(stepdone)
+ if("weak hit")
+ currentsteps += 1
+ outrightfailchance += 5
+ currentquality += 1
+ if("strong hit")
+ currentsteps += 2
+ outrightfailchance += 9.5
+ currentquality += 2
+ if("heavy hit")
+ currentsteps += 3
+ outrightfailchance += 12.5
+ currentquality += 3
+ if("fold")
+ stepsdone += "f"
+ currentsteps += 1
+ currentquality -= 1
+ if("draw")
+ stepsdone += "d"
+ currentsteps += 1
+ currentquality -= 1
+ if("shrink")
+ stepsdone += "s"
+ currentsteps += 1
+ currentquality -= 1
+ if("bend")
+ stepsdone += "b"
+ currentsteps += 1
+ currentquality -= 1
+ if("punch")
+ stepsdone += "p"
+ currentsteps += 1
+ currentquality -= 1
+ if("upset")
+ stepsdone += "u"
+ currentsteps += 1
+ currentquality -= 1
+ user.visible_message("[user] works the metal on the anvil with their hammer with a loud clang!", \
+ "You [stepdone] the metal with a loud clang!")
+ playsound(src, 'sound/effects/clang2.ogg',40, 2)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, src, 'sound/effects/clang2.ogg', 40, 2), 15)
+ if(length(stepsdone) >= 3)
+ tryfinish(user)
+
+/obj/structure/anvil/proc/tryfinish(mob/user)
+ var/artifactchance = 0
+ if(!artifactrolled)
+ artifactchance = (1+(user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/4))/2500
+ artifactrolled = TRUE
+ var/artifact = max(prob(artifactchance), debug)
+ var/finalfailchance = outrightfailchance
+ if(user.mind.skill_holder)
+ var/skillmod = user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/10 + 1
+ finalfailchance = max(0, finalfailchance / skillmod) //lv 2 gives 20% less to fail, 3 30%, etc
+ if((currentsteps > 10 || (rng && prob(finalfailchance))) && !artifact)
+ to_chat(user, "")
+ var/turf/T = get_turf(user)
+ workpiece_state = FALSE
+ new /obj/item/stack/ore/slag(T)
+ currentquality = anvilquality
+ stepsdone = ""
+ currentsteps = 0
+ outrightfailchance = 1
+ artifactrolled = FALSE
+ if(user.mind.skill_holder)
+ user.mind.auto_gain_experience(/datum/skill/level/dorfy/blacksmithing, 25, 400, silent = FALSE)
+ for(var/i in smithrecipes)
+ if(i == stepsdone)
+ var/turf/T = get_turf(user)
+ var/obj/item/smithing/create = smithrecipes[stepsdone]
+ var/obj/item/smithing/finisheditem = new create(T)
+ to_chat(user, "You finish your [finisheditem]!")
+ if(artifact)
+ to_chat(user, "It is an artifact, a creation whose legacy shall live on forevermore.") //todo: SSblackbox
+ currentquality = max(currentquality, 2)
+ finisheditem.quality = currentquality * 3//this is insane i know it's 1/2500 for most of the time and 0.8% at best
+ finisheditem.artifact = TRUE
+ else
+ finisheditem.quality = min(currentquality, itemqualitymax)
+ switch(finisheditem.quality)
+ if(-1000 to -8)
+ finisheditem.desc = "It looks to be the most awfully made object you've ever seen."
+ if(-8)
+ finisheditem.desc = "It looks to be the second most awfully made object you've ever seen."
+ if(-8 to 0)
+ finisheditem.desc = "It looks to be barely passable as... whatever it's trying to pass for."
+ if(0)
+ finisheditem.desc = "It looks to be totally average."
+ if(0 to INFINITY)
+ finisheditem.desc = "It looks to be better than average."
+ workpiece_state = FALSE
+ finisheditem.set_custom_materials(workpiece_material)
+ currentquality = anvilquality
+ stepsdone = ""
+ currentsteps = 0
+ outrightfailchance = 1
+ artifactrolled = FALSE
+ if(user.mind.skill_holder)
+ user.mind.auto_gain_experience(/datum/skill/level/dorfy/blacksmithing, 50, 10000000, silent = FALSE)
+ break
+
+/obj/structure/anvil/debugsuper
+ name = "super ultra epic anvil of debugging."
+ desc = "WOW. A DEBUG ITEM STRUCTURE. EPIC."
+ icon_state = "anvil"
+ anvilquality = 10
+ itemqualitymax = 9001
+ outrightfailchance = 0
+
+/obj/structure/anvil/obtainable
+ name = "anvil"
+ desc = "Base class of anvil. This shouldn't exist, but is useable."
+ anvilquality = 0
+ outrightfailchance = 5
+ rng = TRUE
+
+/obj/structure/anvil/obtainable/table
+ name = "table anvil"
+ desc = "A slightly reinforced table. Good luck."
+ icon_state = "tablevil"
+ anvilquality = -2
+ itemqualitymax = 0
+
+
+/obj/structure/anvil/obtainable/table/do_shaping(mob/user, var/qualitychange)
+ if(prob(5))
+ to_chat(user, "The [src] breaks under the strain!")
+ take_damage(max_integrity)
+ return FALSE
+ else
+ ..()
+
+/obj/structure/anvil/obtainable/bronze
+ name = "slab of bronze"
+ desc = "A big block of bronze. Useable as an anvil."
+ custom_materials = list(/datum/material/bronze=8000)
+ icon_state = "ratvaranvil"
+ anvilquality = -0.5
+ itemqualitymax = 2
+
+/obj/structure/anvil/obtainable/sandstone
+ name = "sandstone brick anvil"
+ desc = "A big block of sandstone. Useable as an anvil."
+ custom_materials = list(/datum/material/sandstone=8000)
+ icon_state = "sandvil"
+ anvilquality = -1
+ itemqualitymax = 2
+
+/obj/structure/anvil/obtainable/basalt
+ name = "basalt brick anvil"
+ desc = "A big block of basalt. Useable as an anvil, better than sandstone. Igneous!"
+ icon_state = "sandvilnoir"
+ anvilquality = -0.5
+ itemqualitymax = 4
+
+/obj/structure/anvil/obtainable/basic
+ name = "anvil"
+ desc = "An anvil. It's got wheels bolted to the bottom."
+ anvilquality = 0
+ itemqualitymax = 6
+
+/obj/structure/anvil/obtainable/ratvar
+ name = "brass anvil"
+ desc = "A big block of what appears to be brass. Useable as an anvil, if whatever's holding the brass together lets you."
+ custom_materials = list(/datum/material/bronze=8000)
+ icon_state = "ratvaranvil"
+ anvilquality = 1
+ itemqualitymax = 8
+
+/obj/structure/anvil/obtainable/ratvar/attackby(obj/item/I, mob/user)
+ if(is_servant_of_ratvar(user))
+ return ..()
+ else
+ to_chat(user, "KNPXWN, QNJCQNW!") //rot13 then rot22 if anyone wants to decode
+
+/obj/structure/anvil/obtainable/narsie
+ name = "runic anvil"
+ desc = "An anvil made of a strange, runic metal."
+ custom_materials = list(/datum/material/runedmetal=8000)
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "evil"
+ anvilquality = 1
+ itemqualitymax = 8
+
+/obj/structure/anvil/obtainable/narsie/attackby(obj/item/I, mob/user)
+ if(iscultist(user))
+ return ..()
+ else
+ to_chat(user, "That is not yours to use!")
+
+#undef WORKPIECE_PRESENT
+#undef WORKPIECE_INPROGRESS
+#undef WORKPIECE_FINISHED
+#undef WORKPIECE_SLAG
diff --git a/code/modules/smithing/finished_items.dm b/code/modules/smithing/finished_items.dm
new file mode 100644
index 0000000000..5db6b09452
--- /dev/null
+++ b/code/modules/smithing/finished_items.dm
@@ -0,0 +1,329 @@
+
+/obj/item/melee/smith
+ name = "base class obj/item/melee/smith" //tin. handles overlay and quality and shit.
+ desc = "cringe"
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "claymore"
+ item_state = "claymore"
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON //yeah ok
+ slot_flags = ITEM_SLOT_BELT
+ w_class = WEIGHT_CLASS_NORMAL
+ force = 6
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ var/quality
+ var/overlay_state = "stick"
+ var/mutable_appearance/overlay
+ var/wielded_mult = 1
+ var/wield_force = 15
+
+/obj/item/melee/smith/Initialize()
+ ..()
+ if(desc == "cringe")
+ desc = "A handmade [name]."
+ overlay = mutable_appearance(icon, overlay_state)
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+
+/obj/item/melee/smith/twohand
+ item_flags = NEEDS_PERMIT //it's a bigass sword/spear. beepsky is going to give you shit for it.
+ sharpness = SHARP_EDGED
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ force = 10
+ wielded_mult = 1.75
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/melee/smith/twohand/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 100, 70) //decent in a pinch, but pretty bad.
+ AddElement(/datum/element/sword_point)
+
+
+
+///////////////////////////
+// Mining //
+///////////////////////////
+/obj/item/mining_scanner/prospector
+ name = "prospector's pickaxe"
+ desc = "A pickaxe that can sound rocks to find mineral deposits and stop gibtonite detonations."
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "minipick" //todo:sprite
+ sharpness = SHARP_POINTY//it doesnt have a blade it has a point
+
+/obj/item/mining_scanner/prospector/Initialize()
+ ..()
+ var/mutable_appearance/overlay
+ desc = "A handmade [name]."
+ overlay = mutable_appearance(icon, "minihandle")
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+/obj/item/pickaxe/smithed
+ name = "pickaxe"
+ desc = "A pickaxe."
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "pickaxe"
+ sharpness = SHARP_POINTY
+
+/obj/item/pickaxe/smithed/Initialize()
+ ..()
+ desc = "A handmade [name]."
+ var/mutable_appearance/overlay
+ overlay = mutable_appearance(icon, "stick")
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+/obj/item/pickaxe/smithed/attack_self(mob/user)
+ to_chat(user, "Tool does not have a configureable dig range.")
+
+/obj/item/shovel/smithed
+ name = "shovel"
+ desc = "A shovel."
+ icon = 'icons/obj/smith.dmi'
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ icon_state = "shovel"
+ sharpness = SHARP_EDGED //it cuts through the earth
+
+/obj/item/shovel/smithed/Initialize()
+ ..()
+ desc = "A handmade [name]."
+ var/mutable_appearance/overlay
+ overlay = mutable_appearance(icon, "shovelhandle")
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+
+///////////////////////////
+// Spears //
+///////////////////////////
+
+
+/obj/item/melee/smith/twohand/halberd
+ name = "halberd"
+ icon_state = "halberd"
+ w_class = WEIGHT_CLASS_HUGE
+ overlay_state = "spearhandle"
+ slot_flags = ITEM_SLOT_BACK
+ wielded_mult = 2.5
+
+/obj/item/melee/smith/twohand/halberd/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/jousting)
+
+/obj/item/melee/smith/twohand/javelin
+ name = "javelin"
+ icon_state = "javelin"
+ overlay_state = "longhandle"
+ wielded_mult = 1.5
+ slot_flags = ITEM_SLOT_BACK
+ sharpness = SHARP_POINTY
+
+
+/obj/item/melee/smith/twohand/javelin/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/jousting)
+
+/obj/item/melee/smith/twohand/glaive
+ name = "glaive"
+ icon_state = "glaive"
+ overlay_state = "longhandle"
+ slot_flags = ITEM_SLOT_BACK
+ wielded_mult = 2
+
+/obj/item/melee/smith/twohand/glaive/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/jousting)
+
+
+/obj/item/melee/smith/twohand/pike
+ name = "pike"
+ icon_state = "pike"
+ w_class = WEIGHT_CLASS_HUGE
+ overlay_state = "longhandle"
+ reach = 2 //yeah ok
+ slot_flags = ITEM_SLOT_BACK
+ sharpness = SHARP_POINTY
+
+//////////////////////////
+// Other Melee //
+///////////////////////////
+
+
+/obj/item/melee/smith/hammer//blacksmithing, not warhammer.
+ name = "hammer"
+ icon_state = "hammer"
+ overlay_state = "hammerhandle"
+ var/qualitymod = 0
+
+/obj/item/scythe/smithed //we need to inherit scythecode, but that's about it.
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+
+
+/obj/item/melee/smith/cogheadclub
+ name = "coghead club"
+ icon_state = "coghead"
+ item_flags = NEEDS_PERMIT
+ overlay_state = "stick"
+
+/obj/item/melee/smith/shortsword
+ name = "gladius"
+ force = 9
+ item_flags = NEEDS_PERMIT
+ sharpness = SHARP_EDGED
+ icon_state = "gladius"
+ overlay_state = "gladiushilt"
+
+/obj/item/melee/smith/shortsword/scimitar
+ name = "scimitar"
+ sharpness = SHARP_EDGED
+ icon_state = "scimitar"
+ overlay_state = "scimitarhilt"
+
+/obj/item/melee/smith/wakizashi
+ name = "wakizashi"
+ sharpness = SHARP_EDGED
+ force = 7
+ item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY
+ obj_flags = UNIQUE_RENAME
+ icon_state = "waki"
+ overlay_state = "wakihilt"
+ block_parry_data = /datum/block_parry_data/waki
+
+/datum/block_parry_data/waki //like longbokken but worse reflect
+ parry_stamina_cost = 6
+ parry_time_windup = 0
+ parry_time_active = 15 //decent window
+ parry_time_spindown = 0
+ parry_time_perfect = 2
+ parry_time_perfect_leeway = 0.75
+ parry_imperfect_falloff_percent = 7.5
+ parry_efficiency_to_counterattack = 100
+ parry_efficiency_considered_successful = 80
+ parry_efficiency_perfect = 120
+ parry_failed_stagger_duration = 3 SECONDS
+ parry_data = list(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 1.9)
+
+/obj/item/melee/smith/twohand/broadsword
+ name = "broadsword"
+ icon_state = "broadsword"
+ overlay_state = "broadhilt"
+ wielded_mult = 1.8
+
+/obj/item/melee/smith/twohand/zweihander
+ name = "zweihander"
+ icon_state = "zwei"
+ overlay_state = "zweihilt"
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON * 2
+ force = 4
+ wielded_mult = 3 //affected more by quality. a -1 is 25% less damage, a +1 is 25% more. These bonuses are tripled when wielded.
+
+/obj/item/melee/smith/twohand/katana
+ name = "katana"
+ icon_state = "katana"
+ overlay_state = "katanahilt"
+ force = 7
+ wielded_mult = 2
+ item_flags = ITEM_CAN_PARRY | NEEDS_PERMIT //want to name your katana "DEMON BLADE" or some shit? go ahead, idiot.
+ obj_flags = UNIQUE_RENAME
+ block_parry_data = /datum/block_parry_data/captain_saber //todo
+
+/obj/item/melee/smith/sabre
+ name = "sabre"
+ icon_state = "sabre"
+ sharpness = SHARP_EDGED
+ overlay_state = "sabrehilt"
+ armour_penetration = 15
+ force = 9
+ hitsound = 'sound/weapons/rapierhit.ogg'
+ item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY
+ block_parry_data = /datum/block_parry_data/captain_saber //yeah this is fine i guess
+
+/obj/item/melee/smith/sabre/rapier
+ name = "rapier"
+ icon_state = "rapier"
+ sharpness = SHARP_EDGED
+ overlay_state = "rapierhilt"
+ force = 6 //less force, stronger parry
+ sharpness = SHARP_POINTY
+ armour_penetration = 30
+ block_parry_data = /datum/block_parry_data/smithrapier
+
+/datum/block_parry_data/smithrapier //parry into riposte. i am pretty sure this is going to be nearly fucking impossible to land.
+ parry_stamina_cost = 12 //dont miss
+ parry_time_active = 4
+ parry_time_perfect = 2
+ parry_time_perfect_leeway = 2
+ parry_failed_stagger_duration = 3 SECONDS
+ parry_failed_clickcd_duration = 3 SECONDS
+ parry_time_windup = 0
+ parry_time_spindown = 0
+ parry_imperfect_falloff_percent = 0
+ parry_efficiency_to_counterattack = 100
+ parry_efficiency_considered_successful = 120
+ parry_efficiency_perfect = 120
+ parry_data = list(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 4)
+
+//unique hammers
+/obj/item/melee/smith/hammer/toolbox
+ name = "toolbox hammer"
+ desc = "A metal filled toolbox on a stick. Useable as a really shitty hammer."
+ w_class = WEIGHT_CLASS_BULKY
+ icon_state = "toolbox"
+ overlay_state = "hammerhandle"
+ qualitymod = -2
+
+/obj/item/melee/smith/hammer/narsie
+ name = "runemetal hammer"
+ custom_materials = list(/datum/material/runedmetal = 12000)
+ desc = "A metal hammer inscribed with geometeric runes."
+ qualitymod = 1
+
+/obj/item/melee/smith/hammer/narsie/attack(mob/living/target, mob/living/carbon/human/user)
+ if(!iscultist(user))
+ user.DefaultCombatKnockdown(100)
+ user.dropItemToGround(src, TRUE)
+ user.visible_message("A powerful force shoves [user] away from [target]!", \
+ "\"You shouldn't be touching tools that aren't yours.\"")
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.apply_damage(rand(force/2, force), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ else
+ user.adjustBruteLoss(rand(force/2,force))
+ return
+ ..()
+
+/obj/item/melee/smith/hammer/ratvar
+ name = "brass hammer"
+ custom_materials = list(/datum/material/bronze = 12000)
+ desc = "A brass hammer inscribed with... writing? You can't read it."
+ qualitymod = 1
+
+/obj/item/melee/smith/hammer/ratvar/attack(mob/living/target, mob/living/carbon/human/user)
+ if(!is_servant_of_ratvar(user))
+ user.DefaultCombatKnockdown(100)
+ user.dropItemToGround(src, TRUE)
+ user.visible_message("A powerful force shoves [user] away from [target]!", "\"You shouldn't be touching tools that aren't yours.\"")
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.apply_damage(rand(force/2, force), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ else
+ user.adjustBruteLoss(rand(force/2,force))
+ return
+ ..()
+
+/obj/item/melee/smith/hammer/debug
+ name = "debugging hammer"
+ desc = "A DEBUGGING HAMMER!! EPIC!!."
+ qualitymod = 10
diff --git a/code/modules/smithing/furnace.dm b/code/modules/smithing/furnace.dm
new file mode 100644
index 0000000000..ee1d6a1b93
--- /dev/null
+++ b/code/modules/smithing/furnace.dm
@@ -0,0 +1,75 @@
+/obj/structure/furnace
+ name = "furnace"
+ desc = "A furnace."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "furnace0"
+ density = TRUE
+ anchored = TRUE
+ var/debug = FALSE //debugging only
+ var/working = TRUE
+ var/fueluse = 1
+
+
+/obj/structure/furnace/Initialize()
+ ..()
+ create_reagents(250, TRANSPARENT)
+ START_PROCESSING(SSobj, src)
+
+/obj/structure/furnace/Destroy()
+ ..()
+ STOP_PROCESSING(SSobj, src)
+
+/obj/structure/furnace/process()
+ if(debug)
+ reagents.add_reagent(/datum/reagent/fuel, 1)
+ return TRUE
+ if(reagents.remove_reagent(/datum/reagent/fuel, fueluse))
+ working = TRUE
+ if(icon_state == "furnace0")
+ icon_state = "furnace1"
+ else
+ working = FALSE
+ icon_state = "furnace0"
+
+/obj/structure/furnace/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/ingot))
+ var/obj/item/ingot/notsword = I
+ if(working)
+ to_chat(user, "You heat the [notsword] in the [src].")
+ notsword.workability = "shapeable"
+ else
+ to_chat(user, "The furnace isn't working!.")
+ else
+ ..()
+
+/obj/structure/furnace/wrench_act(mob/living/user, obj/item/I)
+ ..()
+ default_unfasten_wrench(user, I, 5)
+ return TRUE
+
+/obj/structure/furnace/attackby(obj/item/W, mob/user, params)
+ if(W.reagents)
+ W.reagents.trans_to(src, 250)
+ else
+ return ..()
+
+/obj/structure/furnace/plunger_act(obj/item/plunger/P, mob/living/user, reinforced)
+ to_chat(user, "You start furiously plunging [name].")
+ if(do_after(user, 30, target = src))
+ to_chat(user, "You finish plunging the [name].")
+ reagents.reaction(get_turf(src), TOUCH) //splash on the floor
+ reagents.clear_reagents()
+
+/obj/structure/furnace/infinite
+ name = "fuelless furnace"
+ debug = TRUE
+ icon_state = "ratfurnace"
+
+
+/obj/structure/furnace/infinite/ratvar
+ name = "brass furnace"
+ desc = "A brass furnace. Powered by... something, but seems otherwise safe." //todo:sprites they're safe for noncultists because you're just putting ingots in them. also there';s a reason to steal them ig
+
+/obj/structure/furnace/infinite/narsie
+ name = "rune furnace"
+ desc = "A runed furnace. Powered by... something, but seems otherwise safe."
diff --git a/code/modules/smithing/smithed_items.dm b/code/modules/smithing/smithed_items.dm
new file mode 100644
index 0000000000..df30a01f8c
--- /dev/null
+++ b/code/modules/smithing/smithed_items.dm
@@ -0,0 +1,420 @@
+/obj/item/basaltblock
+ name = "basalt block"
+ desc = "A block of basalt."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "sandvilnoir"
+
+
+/obj/item/smithing
+ name = "base class /obj/item/smithing"
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "unfinished"
+ material_flags = MATERIAL_COLOR | MATERIAL_ADD_PREFIX
+ var/quality = 0 //quality. Changed by the smithing process.
+ var/obj/item/finishingitem = /obj/item/stick //What this item needs to be hit by to create finalitem
+ var/obj/item/finalitem
+ var/artifact = FALSE
+
+/obj/item/ingot
+ name = "ingot"
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "ingot"
+ material_flags = MATERIAL_COLOR | MATERIAL_ADD_PREFIX
+ var/workability = 0
+
+
+/obj/item/ingot/on_attack_hand(mob/user)
+ var/mob/living/carbon/human/H
+ if(workability != "shapeable")
+ return ..()
+ var/prot = 0
+ if(ishuman(user))
+ H = user
+ if(H.gloves)
+ var/obj/item/clothing/gloves/G = H.gloves
+ if(G.max_heat_protection_temperature)
+ prot = (G.max_heat_protection_temperature > 360)
+ else
+ prot = 1
+ if(prot > 0 || HAS_TRAIT(user, TRAIT_RESISTHEAT) || HAS_TRAIT(user, TRAIT_RESISTHEATHANDS))
+ to_chat(user, "You pick up the [src].")
+ return ..()
+ else
+ to_chat(user, "You try to move the [src], but you burn your hand on it!")
+ if(H)
+ var/obj/item/bodypart/affecting = H.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
+ if(affecting && affecting.receive_damage( 0, 5 )) // 5 burn damage
+ H.update_damage_overlays()
+
+/obj/item/ingot/iron
+ custom_materials = list(/datum/material/iron=12000)
+
+/obj/item/ingot/diamond
+ custom_materials = list(/datum/material/diamond=12000) //yeah ok
+
+/obj/item/ingot/uranium
+ custom_materials = list(/datum/material/uranium=12000)
+
+/obj/item/ingot/plasma
+ custom_materials = list(/datum/material/plasma=12000)//yeah ok
+
+/obj/item/ingot/gold
+ custom_materials = list(/datum/material/gold=12000)
+
+/obj/item/ingot/silver
+ custom_materials = list(/datum/material/silver=12000)
+
+/obj/item/ingot/bananium
+ custom_materials = list(/datum/material/bananium=12000)
+
+/obj/item/ingot/titanium
+ custom_materials = list(/datum/material/titanium=12000)
+
+/obj/item/ingot/adamantine
+ custom_materials = list(/datum/material/adamantine=12000)
+
+/obj/item/ingot/cult
+ custom_materials = list(/datum/material/runedmetal=12000)
+
+/obj/item/ingot/bronze
+ custom_materials = list(/datum/material/bronze=12000)
+
+/obj/item/ingot/bronze/ratvar
+ material_flags = MATERIAL_COLOR
+ name = "brass ingnot"
+ desc = "On closer inspection, what appears to be wholly-unsuitable-for-smithing brass is actually more structurally stable bronze. Ratvar must have transformed the brass into bronze. Somehow."
+
+
+/obj/item/smithing/attackby(obj/item/I, mob/user)
+ if(istype(I, finishingitem))
+ qdel(I)
+ startfinish()
+ else
+ return ..()
+
+/obj/item/smithing/proc/startfinish()
+ dofinish()
+
+/obj/item/smithing/proc/dofinish()
+ var/qualname
+ switch(quality)
+ if(-1000 to -5)
+ qualname = "awful"
+ if(-1000 to -2)
+ qualname = "shoddy"
+ if(-1000 to -1)
+ qualname = "poor"
+ if(-1 to 1)
+ qualname = "normal"
+ if(10 to INFINITY)
+ qualname = "legendary"
+ if(7.5 to 10)
+ qualname = "masterwork"
+ if(5.5 to 7.5)
+ qualname = "excellent"
+ if(3.5 to 5.5)
+ qualname = "good"
+ if(0 to 3.5)
+ qualname = "above-average"
+ var/datum/material/mat = custom_materials[1]
+ finalitem.set_custom_materials(custom_materials)
+ mat = mat.name
+ if(artifact)
+ dwarfyartifact(finalitem, mat)
+ else
+ finalitem.name = "[qualname] [mat] [initial(finalitem.name)]"
+ finalitem.desc = "A [qualname] [initial(finalitem.name)]. Its quality is [quality]."
+ finalitem.forceMove(get_turf(src))
+ qdel(src)
+
+
+/obj/item/smithing/proc/dwarfyartifact(var/obj/item/finalitem, var/mat)
+ var/finaldesc = "A [initial(finalitem.name)] made of [mat], all craftsmanship is of the highest quality. It "
+ switch(pick(1,2,3,4,5))
+ if(1)
+ finaldesc += "is encrusted with [pick("","synthetic ","multi-faceted ","magical ","sparkling ") + pick("rubies","emeralds","jade","opals","lapiz lazuli")]."
+ if(2)
+ finaldesc += "is laced with studs of [pick("gold","silver","aluminium","titanium")]."
+ if(3)
+ finaldesc += "is encircled with bands of [pick("durasteel","metallic hydrogen","ferritic-alloy","plasteel","duranium")]."
+ if(4)
+ finaldesc += "menaces with spikes of [pick("ytterbium","uranium","white pearl","black steel")]."
+ if(5)
+ finaldesc += "is encrusted with [pick("","synthetic ","multi-faceted ","magical ","sparkling ") + pick("rubies","emeralds","jade","opals","lapis lazuli")],laced with studs of [pick("gold","silver","aluminium","titanium")], encircled with bands of [pick("durasteel","metallic hydrogen","ferritic-alloy","plasteel","duranium")] and menaces with spikes of [pick("ytterbium","uranium","white pearl","black steel")]."
+ finalitem.desc = finaldesc
+ finalitem.name = pick("Delersibnir", "Nekolangrir", "Zanoreshik","Öntakrítin", "Nogzatan", "Vunomam", "Nazushagsaldôbar", "Sergeb", "Zafaldastot", "Vudnis", "Dostust", "Shotom", "Mugshith", "Angzak", "Oltud", "Deleratîs", "Nökornomal") //one of these is literally BLOOD POOL CREATE.iirc its Nazushagsaldôbar.
+
+/obj/item/smithing/hammerhead
+ name = "smithed hammer head"
+ finalitem = /obj/item/melee/smith/hammer
+ icon_state = "hammer"
+
+/obj/item/smithing/hammerhead/startfinish()
+ var/obj/item/melee/smith/hammer/finalforreal = new /obj/item/melee/smith/hammer(src)
+ finalforreal.force += quality/2
+ finalforreal.qualitymod = quality/4
+ finalitem = finalforreal
+ ..()
+
+
+
+/obj/item/smithing/scytheblade
+ name = "smithed scythe head"
+ finalitem = /obj/item/scythe/smithed
+ icon_state = "scythe"
+
+/obj/item/smithing/scytheblade/startfinish()
+ finalitem = new /obj/item/scythe/smithed(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/shovelhead
+ name = "smithed shovel head"
+ finalitem = /obj/item/shovel/smithed
+ icon_state = "shovel"
+
+/obj/item/smithing/shovelhead/startfinish()
+ finalitem = new /obj/item/shovel/smithed(src)
+ finalitem.force += quality/2
+ if(quality > 0)
+ finalitem.toolspeed = max(0.05,(1-(quality/10)))
+ else
+ finalitem.toolspeed *= max(1, (quality * -1))
+ ..()
+
+/obj/item/smithing/cogheadclubhead
+ name = "smithed coghead club head"
+ finalitem = /obj/item/melee/smith/cogheadclub
+ icon_state = "coghead"
+
+/obj/item/smithing/cogheadclubhead/startfinish()
+ finalitem = new /obj/item/melee/smith/cogheadclub(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/javelinhead
+ name = "smithed javelin head"
+ finalitem = /obj/item/melee/smith/twohand/javelin
+ icon_state = "javelin"
+
+/obj/item/smithing/javelinhead/startfinish()
+ var/obj/item/melee/smith/twohand/javelin/finalforreal = new /obj/item/melee/smith/twohand/javelin(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalforreal.throwforce = finalforreal.force*2
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/pikehead
+ name = "smithed pike head"
+ finalitem = /obj/item/melee/smith/twohand/pike
+ icon_state = "pike"
+
+/obj/item/smithing/pikehead/startfinish()
+ var/obj/item/melee/smith/twohand/pike/finalforreal = new /obj/item/melee/smith/twohand/pike(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalforreal.throwforce = finalforreal.force/10 //its a pike not a javelin
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/pickaxehead
+ name = "smithed pickaxe head"
+ finalitem = /obj/item/pickaxe/smithed
+ icon_state = "pickaxe"
+
+/obj/item/smithing/pickaxehead/startfinish()
+ var/obj/item/pickaxe/smithed/finalforreal = new /obj/item/pickaxe/smithed(src)
+ finalforreal.force += quality/2
+ if(quality > 0)
+ finalforreal.toolspeed = max(0.05,(1-(quality/10)))
+ else
+ finalforreal.toolspeed *= max(1, (quality * -1))
+ switch(quality)
+ if(10 to INFINITY)
+ finalforreal.digrange = 4
+ if(5 to 9)
+ finalforreal.digrange = 3
+ if(3,4)
+ finalforreal.digrange = 2
+ else
+ finalforreal.digrange = 1
+ finalitem = finalforreal
+ ..()
+
+
+/obj/item/smithing/prospectingpickhead
+ name = "smithed prospector's pickaxe head"
+ finalitem = /obj/item/mining_scanner/prospector
+ icon_state = "minipick"
+
+/obj/item/smithing/prospectingpickhead/startfinish()
+ var/obj/item/mining_scanner/prospector/finalforreal = new /obj/item/mining_scanner/prospector(src)
+ finalforreal.range = 2 + quality
+ if(quality)
+ finalforreal.cooldown = 100/quality
+ finalitem = finalforreal
+ ..()
+
+
+/obj/item/smithing/shortswordblade
+ name = "smithed gladius blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/shortsword
+ icon_state = "gladius"
+
+/obj/item/smithing/shortswordblade/startfinish()
+ finalitem = new /obj/item/melee/smith/shortsword(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/scimitarblade
+ name = "smithed scimitar blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/shortsword/scimitar
+ icon_state = "scimitar"
+
+/obj/item/smithing/scimitarblade/startfinish()
+ finalitem = new /obj/item/melee/smith/shortsword/scimitar(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/wakiblade
+ name = "smithed wakizashi blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/wakizashi
+ icon_state = "waki"
+
+/obj/item/smithing/wakiblade/startfinish()
+ finalitem = new /obj/item/melee/smith/wakizashi(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/sabreblade
+ name = "smithed sabre blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/sabre
+ icon_state = "sabre"
+
+/obj/item/smithing/sabreblade/startfinish()
+ finalitem = new /obj/item/melee/smith/sabre(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/rapierblade
+ name = "smithed rapier blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/sabre/rapier
+ icon_state = "rapier"
+
+/obj/item/smithing/rapierblade/startfinish()
+ finalitem = new /obj/item/melee/smith/sabre/rapier(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/knifeblade
+ name = "smithed knife blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/kitchen/knife
+ icon_state = "dagger"
+
+/obj/item/smithing/knifeblade/startfinish()
+ finalitem = new /obj/item/kitchen/knife(src)
+ finalitem.force = 4 + quality/2
+ finalitem.icon = 'icons/obj/smith.dmi'
+ finalitem.icon_state = "dagger"
+ finalitem.name = "dagger"
+ finalitem.desc = "A dagger."
+ var/mutable_appearance/overlay = mutable_appearance('icons/obj/smith.dmi', "daggerhilt")
+ overlay.appearance_flags = RESET_COLOR
+ finalitem.add_overlay(overlay)
+ if(finalitem.force < 0)
+ finalitem.force = 0
+ finalitem.material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ ..()
+
+/obj/item/smithing/broadblade
+ name = "smithed broadsword blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/twohand/broadsword
+ icon_state = "broadsword"
+
+/obj/item/smithing/broadblade/startfinish()
+ var/obj/item/melee/smith/twohand/broadsword/finalforreal = new /obj/item/melee/smith/twohand/broadsword(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/zweiblade
+ name = "smithed zweihander blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/twohand/zweihander
+ icon_state = "zwei"
+
+/obj/item/smithing/zweiblade/startfinish()
+ var/obj/item/melee/smith/twohand/zweihander/finalforreal = new /obj/item/melee/smith/twohand/zweihander(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/halberdhead
+ name = "smithed halberd head"
+ finalitem = /obj/item/melee/smith/twohand/halberd
+ icon_state = "halberd"
+
+/obj/item/smithing/halberdhead/startfinish()
+ var/obj/item/melee/smith/twohand/halberd/finalforreal = new /obj/item/melee/smith/twohand/halberd(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.throwforce = finalforreal.force/3
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/glaivehead
+ name = "smithed glaive head"
+ finalitem = /obj/item/melee/smith/twohand/glaive
+ icon_state = "glaive"
+
+/obj/item/smithing/glaive/startfinish()
+ var/obj/item/melee/smith/twohand/glaive/finalforreal = new /obj/item/melee/smith/twohand/glaive(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.throwforce = finalforreal.force
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/katanablade
+ name = "smithed katana blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/twohand/katana
+ icon_state = "katana"
+
+
+/obj/item/smithing/katanablade/startfinish()
+ var/obj/item/melee/smith/twohand/katana/finalforreal = new /obj/item/melee/smith/twohand/katana(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/stick
+ name = "wooden rod"
+ desc = "It's a rod, suitable for use of a handle of a tool. Also could serve as a weapon, in a pinch."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "stick"
+ force = 7
+
+/obj/item/swordhandle
+ name = "sword handle"
+ desc = "It's a crudlely shaped wooden sword hilt."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "shorthilt"
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 33d143d400..09e6f0ef42 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -163,7 +163,7 @@
maxHealth = 75 //Take brute damage instead
var/chattering = FALSE
var/phomeme_type = "sans"
- var/list/phomeme_types = list(/datum/accent/bone/sans, /datum/accent/bone/papyrus)
+ var/list/phomeme_types = list(/datum/accent/span/sans, /datum/accent/span/papyrus)
/obj/item/organ/tongue/bone/Initialize()
. = ..()
@@ -197,7 +197,7 @@
icon_state = "tonguerobot"
say_mod = "states"
attack_verb = list("beeped", "booped")
- initial_accents = list(/datum/accent/robot)
+ initial_accents = list(/datum/accent/span/robot)
taste_sensitivity = 25 // not as good as an organic tongue
maxHealth = 100 //RoboTongue!
var/electronics_magic = TRUE
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index c4ded65e9c..0f1004221c 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -10,6 +10,12 @@
tool_behaviour = TOOL_RETRACTOR
toolspeed = 1
+/obj/item/retractor/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/retractor/advanced
name = "mechanical pinches"
desc = "An agglomerate of rods and gears."
@@ -55,6 +61,12 @@
tool_behaviour = TOOL_HEMOSTAT
toolspeed = 1
+/obj/item/hemostat/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/hemostat/augment
name = "hemostat"
desc = "Tiny servos power a pair of pincers to stop bleeding."
@@ -79,6 +91,12 @@
tool_behaviour = TOOL_CAUTERY
toolspeed = 1
+/obj/item/cautery/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/cautery/augment
name = "cautery"
desc = "A heated element that cauterizes wounds."
@@ -107,6 +125,12 @@
tool_behaviour = TOOL_DRILL
toolspeed = 1
+/obj/item/surgicaldrill/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/surgicaldrill/advanced
name = "searing tool"
desc = "It projects a high power laser used for medical application."
@@ -172,6 +196,12 @@
. = ..()
AddComponent(/datum/component/butchering, 80 * toolspeed, 100, 0)
+/obj/item/scalpel/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/scalpel/advanced
name = "laser scalpel"
desc = "An advanced scalpel which uses laser technology to cut."
@@ -255,6 +285,11 @@
. = ..()
AddComponent(/datum/component/butchering, 40 * toolspeed, 100, 5, 'sound/weapons/circsawhit.ogg') //saws are very accurate and fast at butchering
+/obj/item/circular_saw/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
/obj/item/circular_saw/augment
name = "circular saw"
@@ -392,3 +427,9 @@
attack_verb = list("corrected", "properly set")
tool_behaviour = TOOL_BONESET
toolspeed = 1
+
+/obj/item/bonesetter/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
diff --git a/code/modules/unit_tests/character_saving.dm b/code/modules/unit_tests/character_saving.dm
index 6c83d4142a..bdcb0f0276 100644
--- a/code/modules/unit_tests/character_saving.dm
+++ b/code/modules/unit_tests/character_saving.dm
@@ -11,4 +11,4 @@
if(P.features["ooc_notes"] != "Bar")
Fail("OOC text is failing to save.")
catch(var/exception/e)
- Fail("Failed to save and load character due to exception [e.name]")
+ Fail("Failed to save and load character due to exception [e.file]:[e.line], [e.name]")
diff --git a/html/changelog.html b/html/changelog.html
index 4b79940945..75939ac553 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,41 @@
-->
+
06 September 2020
+
Putnam3145 updated:
+
+ - Dynamic no longer pushes events.
+ - Made spontaneous brain trauma a good deal less annoying.
+
+
lolman360, NecromancerAnne updated:
+
+ - The ancient art of blacksmithing, now in pixels.
+ - cool swords and shit (thanks anne)
+
+
raspy-on-osu updated:
+
+ - thermomachine examine text
+
+
+
05 September 2020
+
Bhijn updated:
+
+ - Readded the old method of temperature notifications in the form of a new pair of shivering/sweating notifications.
+
+
Putnam3145 updated:
+
+ - Hilbert hotel flavor text for one particular snowflake hotel changed.
+ - admins can now actually reduce threat level in dynamic
+ - Made owo.ogg smaller.
+ - Character saving unit test is now more verbose on failure.
+ - Added an extools proc hook alternative to rust_g logging.
+
+
raspy-on-osu updated:
+
+ - supermatter shard examine text
+ - protolathe item categories
+
+
04 September 2020
timothyteakettle updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 134fadc0c5..b1683cdf5f 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -27215,3 +27215,25 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
2020-09-04:
timothyteakettle:
- bugfix: ipcs can speak
+2020-09-05:
+ Bhijn:
+ - rscadd: Readded the old method of temperature notifications in the form of a new
+ pair of shivering/sweating notifications.
+ Putnam3145:
+ - tweak: Hilbert hotel flavor text for one particular snowflake hotel changed.
+ - admin: admins can now actually reduce threat level in dynamic
+ - tweak: Made owo.ogg smaller.
+ - tweak: Character saving unit test is now more verbose on failure.
+ - refactor: Added an extools proc hook alternative to rust_g logging.
+ raspy-on-osu:
+ - tweak: supermatter shard examine text
+ - tweak: protolathe item categories
+2020-09-06:
+ Putnam3145:
+ - rscdel: Dynamic no longer pushes events.
+ - balance: Made spontaneous brain trauma a good deal less annoying.
+ lolman360, NecromancerAnne:
+ - rscadd: The ancient art of blacksmithing, now in pixels.
+ - imageadd: cool swords and shit (thanks anne)
+ raspy-on-osu:
+ - tweak: thermomachine examine text
diff --git a/html/changelogs/AutoChangeLog-pr-12452.yml b/html/changelogs/AutoChangeLog-pr-12452.yml
new file mode 100644
index 0000000000..d9806ec781
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12452.yml
@@ -0,0 +1,14 @@
+author: "Putnam for debugging ammo loading! Thx!!"
+delete-after: True
+changes:
+ - rscdel: "Removed spell blade, diamond picaxe, and fire wand from lava land loot tables"
+ - tweak: "Each mini boss now has its own type of crate."
+ - tweak: "Each spike has its own type of crate that it pulls its now smaller loot table from"
+ - balance: "Moved god eye from spike loot table to hard spawn collosses fight"
+ - balance: "Moved holoparasight from spike loot table to bubble gum"
+ - balance: "Moved magic meat hook from spike loot table to drake"
+ - rscadd: "2 more crates to Arena Shuttle as well as 4-4-4 of each new type of chest"
+ - balance: "Replaced the diamond pick loot with a better one"
+ - tweak: "Replaced the cursted katana with a non cursted verson that deals half the damage and has less block!"
+ - rscadd: "Three new potions, blue heals the mind like a mama potion, Green heals the soul aka the organs! Lastly Red heals the body, by 100 damage of each main types. Best not to waste them!"
+ - rscadd: "Four more \"wands\" Spellbooks! These fun little guys shoot out its own pages to do the affect, one will set the target on fire like a bullet, one will harm them a bit, one will heal the target a small bit - How nice! Last one will give them a few statis affects like a taser bolt but without as much power or tasing affect"
diff --git a/icons/effects/mouse_pointers/mute_target.dmi b/icons/effects/mouse_pointers/mute_target.dmi
new file mode 100644
index 0000000000..8e3c278d3b
Binary files /dev/null and b/icons/effects/mouse_pointers/mute_target.dmi differ
diff --git a/icons/mob/actions/actions_ecult.dmi b/icons/mob/actions/actions_ecult.dmi
index 0a130f006e..d083206454 100644
Binary files a/icons/mob/actions/actions_ecult.dmi and b/icons/mob/actions/actions_ecult.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index 315ca5e924..0a82845910 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 6af883f2e8..abfbb18f84 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi
index 4ced910193..e64c037cf8 100644
Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ
diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi
index c106bd12fc..1a98872463 100644
Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ
diff --git a/icons/obj/eldritch.dmi b/icons/obj/eldritch.dmi
index 50c2913708..294deda628 100644
Binary files a/icons/obj/eldritch.dmi and b/icons/obj/eldritch.dmi differ
diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi
index 727d69b1e3..6e14c4c6ca 100644
Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ
diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi
index e7063b71db..400c5bdfa0 100644
Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ
diff --git a/icons/obj/smith.dmi b/icons/obj/smith.dmi
new file mode 100644
index 0000000000..a40b9a2b19
Binary files /dev/null and b/icons/obj/smith.dmi differ
diff --git a/libbyond-extools.so b/libbyond-extools.so
index 8e17f952f2..052a8cb037 100644
Binary files a/libbyond-extools.so and b/libbyond-extools.so differ
diff --git a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
index 39ba69bd61..aea156a8f5 100644
--- a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
@@ -253,6 +253,7 @@
holder.remove_reagent(type, cached_volume)
holder.add_reagent(/datum/reagent/fermi/PEsmaller, cached_volume)
+/*
/datum/chemical_reaction/fermi/astral
name = "Astrogen"
id = /datum/reagent/fermi/astral
@@ -274,7 +275,7 @@
FermiChem = TRUE
FermiExplode = TRUE
PurityMin = 0.25
-
+*/
/datum/chemical_reaction/fermi/enthrall //check this
name = "MKUltra"
diff --git a/sound/voice/medbot/owo.ogg b/sound/voice/medbot/owo.ogg
index 0fdaa9d483..5eeb502c88 100644
Binary files a/sound/voice/medbot/owo.ogg and b/sound/voice/medbot/owo.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index e742498527..c3e51d49cc 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -17,6 +17,7 @@
#include "_maps\_basemap.dm"
#include "code\_compile_options.dm"
#include "code\world.dm"
+#include "code\__DEFINES\_extools.dm"
#include "code\__DEFINES\_globals.dm"
#include "code\__DEFINES\_protect.dm"
#include "code\__DEFINES\_tick.dm"
@@ -141,12 +142,14 @@
#include "code\__DEFINES\mapping\maploader.dm"
#include "code\__DEFINES\material\worth.dm"
#include "code\__DEFINES\mobs\slowdowns.dm"
+#include "code\__DEFINES\research\anomalies.dm"
#include "code\__DEFINES\research\stock_parts.dm"
#include "code\__DEFINES\skills\defines.dm"
#include "code\__DEFINES\skills\helpers.dm"
#include "code\__DEFINES\storage\_storage.dm"
#include "code\__DEFINES\storage\volumetrics.dm"
#include "code\__HELPERS\_cit_helpers.dm"
+#include "code\__HELPERS\_extools_api.dm"
#include "code\__HELPERS\_lists.dm"
#include "code\__HELPERS\_logging.dm"
#include "code\__HELPERS\_string_lists.dm"
@@ -646,6 +649,7 @@
#include "code\datums\skills\_skill.dm"
#include "code\datums\skills\_skill_holder.dm"
#include "code\datums\skills\_skill_modifier.dm"
+#include "code\datums\skills\blacksmithing.dm"
#include "code\datums\skills\engineering.dm"
#include "code\datums\skills\medical.dm"
#include "code\datums\skills\modifiers\job.dm"
@@ -735,7 +739,6 @@
#include "code\game\gamemodes\devil\devil agent\devil_agent.dm"
#include "code\game\gamemodes\dynamic\dynamic.dm"
#include "code\game\gamemodes\dynamic\dynamic_rulesets.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets_events.dm"
#include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm"
#include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm"
#include "code\game\gamemodes\dynamic\dynamic_rulesets_roundstart.dm"
@@ -3023,6 +3026,7 @@
#include "code\modules\projectiles\guns\energy\pulse.dm"
#include "code\modules\projectiles\guns\energy\special.dm"
#include "code\modules\projectiles\guns\energy\stun.dm"
+#include "code\modules\projectiles\guns\magic\spell_book.dm"
#include "code\modules\projectiles\guns\magic\staff.dm"
#include "code\modules\projectiles\guns\magic\wand.dm"
#include "code\modules\projectiles\guns\misc\beam_rifle.dm"
@@ -3141,6 +3145,8 @@
#include "code\modules\research\server.dm"
#include "code\modules\research\stock_parts.dm"
#include "code\modules\research\anomaly\anomaly_core.dm"
+#include "code\modules\research\anomaly\explosive_compressor.dm"
+#include "code\modules\research\anomaly\raw_anomaly.dm"
#include "code\modules\research\designs\AI_module_designs.dm"
#include "code\modules\research\designs\autobotter_designs.dm"
#include "code\modules\research\designs\autoylathe_designs.dm"
@@ -3307,6 +3313,10 @@
#include "code\modules\shuttle\shuttle_creation\shuttle_creator_eye.dm"
#include "code\modules\shuttle\shuttle_creation\shuttle_creator_overlay.dm"
#include "code\modules\shuttle\shuttle_creation\shuttle_upgrades.dm"
+#include "code\modules\smithing\anvil.dm"
+#include "code\modules\smithing\finished_items.dm"
+#include "code\modules\smithing\furnace.dm"
+#include "code\modules\smithing\smithed_items.dm"
#include "code\modules\spells\spell.dm"
#include "code\modules\spells\spell_types\aimed.dm"
#include "code\modules\spells\spell_types\area_teleport.dm"