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 718b475f9a..ea5491a741 100644
Binary files a/byond-extools.dll and b/byond-extools.dll differ
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 8efb2617bd..2af7ec5c49 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -43,6 +43,8 @@
// /atom signals
//from base of atom/proc/Initialize(): sent any time a new atom is created
#define COMSIG_ATOM_CREATED "atom_created"
+//from SSatoms InitAtom - Only if the atom was not deleted or failed initialization
+#define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE "atom_init_success"
#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called
#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human)
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/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/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm
index db1fced637..b9a4785f49 100644
--- a/code/controllers/subsystem/atoms.dm
+++ b/code/controllers/subsystem/atoms.dm
@@ -90,6 +90,8 @@ SUBSYSTEM_DEF(atoms)
qdeleted = TRUE
else if(!(A.flags_1 & INITIALIZED_1))
BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT
+ else
+ SEND_SIGNAL(A,COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE)
return qdeleted || QDELING(A)
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/components/fantasy/prefixes.dm b/code/datums/components/fantasy/prefixes.dm
index b6de85cab0..0ada00a2e8 100644
--- a/code/datums/components/fantasy/prefixes.dm
+++ b/code/datums/components/fantasy/prefixes.dm
@@ -45,8 +45,9 @@
/datum/fantasy_affix/tactical/apply(datum/component/fantasy/comp, newName)
var/obj/item/master = comp.parent
- master.AddElement(/datum/element/tactical)
- comp.appliedElements += list(/datum/element/tactical)
+ var/list/dat = list(/datum/element/tactical)
+ master._AddElement(dat)
+ comp.appliedElements += list(dat)
return "tactical [newName]"
/datum/fantasy_affix/pyromantic
diff --git a/code/datums/elements/decal.dm b/code/datums/elements/decal.dm
index 5ddd5bd03e..a20d46c813 100644
--- a/code/datums/elements/decal.dm
+++ b/code/datums/elements/decal.dm
@@ -41,19 +41,27 @@
var/atom/A = target
num_decals_per_atom[A]--
if(!num_decals_per_atom[A])
- UnregisterSignal(A, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_UPDATE_OVERLAYS))
+ UnregisterSignal(A, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE,
+ COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_UPDATE_OVERLAYS))
LAZYREMOVE(num_decals_per_atom, A)
apply(A)
return ..()
/datum/element/decal/proc/apply(atom/target)
- target.update_icon()
+ if(target.flags_1 & INITIALIZED_1)
+ target.update_icon() //could use some queuing here now maybe.
+ else if(!QDELETED(target) && num_decals_per_atom[target] == 1)
+ RegisterSignal(target, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE, .proc/late_update_icon)
if(isitem(target))
addtimer(CALLBACK(target, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
+/datum/element/decal/proc/late_update_icon(atom/source)
+ source.update_icon()
+ UnregisterSignal(source,COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE)
+
/datum/element/decal/proc/apply_overlay(atom/source, list/overlay_list)
if(first_dir)
- pic.dir = turn(first_dir, -dir2angle(source.dir))
+ pic.dir = first_dir == SOUTH ? source.dir : turn(first_dir, dir2angle(source.dir)-180) //Never turn a dir by 0.
for(var/i in 1 to num_decals_per_atom[source])
overlay_list += pic
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 2e6353b8a8..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)
@@ -213,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"])
@@ -374,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
@@ -394,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++
@@ -593,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
@@ -703,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/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index b280851888..5f312f2bf3 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -46,4 +46,5 @@
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
- T.AddElement(/datum/element/decal, icon, icon_state, dir, CLEAN_GOD, color, null, null, alpha)
+ var/turn_dir = 180 - dir2angle(T.dir) //Turning a dir by 0 results in a roulette of random dirs.
+ T.AddElement(/datum/element/decal, icon, icon_state, turn_dir ? turn(dir, turn_dir) : dir, CLEAN_GOD, color, null, null, alpha)
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 277d132b12..6396a4fe3d 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -445,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/plushes.dm b/code/game/objects/items/plushes.dm
index a3da49ed89..44205f4884 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -688,18 +688,6 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
icon_state = "scrubpuppy"
item_state = "scrubpuppy"
-/obj/item/toy/plush/borgplushie/meddrake
- name = "MediDrake Plushie"
- desc = "An adorable stuffed toy of a Medidrake."
- icon_state = "meddrake"
- item_state = "meddrake"
-
-/obj/item/toy/plush/borgplushie/secdrake
- name = "SecDrake Plushie"
- desc = "An adorable stuffed toy of a Secdrake."
- icon_state = "secdrake"
- item_state = "secdrake"
-
/obj/item/toy/plush/aiplush
name = "AI plushie"
desc = "A little stuffed toy AI core... it appears to be malfunctioning."
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/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 758f824727..877d236e84 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -13,7 +13,7 @@
var/icon_regular_floor = "floor" //used to remember what icon the tile should have by default
var/icon_plating = "plating"
- thermal_conductivity = 0.040
+ thermal_conductivity = 0.004
heat_capacity = 10000
intact = 1
var/broken = 0
diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm
index 03045674e4..42b4707b50 100644
--- a/code/game/turfs/simulated/floor/reinf_floor.dm
+++ b/code/game/turfs/simulated/floor/reinf_floor.dm
@@ -3,7 +3,7 @@
name = "reinforced floor"
desc = "Extremely sturdy."
icon_state = "engine"
- thermal_conductivity = 0.025
+ thermal_conductivity = 0.0025
heat_capacity = INFINITY
floor_tile = /obj/item/stack/rods
footstep = FOOTSTEP_PLATING
diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm
index ed48c24462..5d928f377e 100644
--- a/code/game/turfs/simulated/wall/mineral_walls.dm
+++ b/code/game/turfs/simulated/wall/mineral_walls.dm
@@ -90,7 +90,7 @@
icon = 'icons/turf/walls/plasma_wall.dmi'
icon_state = "plasma"
sheet_type = /obj/item/stack/sheet/mineral/plasma
- thermal_conductivity = 0.04
+ thermal_conductivity = 0.004
canSmoothWith = list(/turf/closed/wall/mineral/plasma, /obj/structure/falsewall/plasma)
/turf/closed/wall/mineral/plasma/attackby(obj/item/W, mob/user, params)
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/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index 2b43319904..14f480d33a 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -1,6 +1,6 @@
/turf
//used for temperature calculations
- var/thermal_conductivity = 0.05
+ var/thermal_conductivity = 0.005
var/heat_capacity = 1
var/temperature_archived
@@ -270,7 +270,7 @@
/turf/proc/super_conduct()
var/conductivity_directions = conductivity_directions()
-
+ archive()
if(conductivity_directions)
//Conduct with tiles around me
for(var/direction in GLOB.cardinals)
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index e86b249be6..e498ff05c9 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -188,7 +188,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
//Performs air sharing calculations between two gas_mixtures assuming only 1 boundary length
//Returns: amount of gas exchanged (+ if sharer received)
-/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
+/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient,temperature=null,heat_capacity=null)
//Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length
//Returns: new temperature of the sharer
diff --git a/code/modules/cargo/packs/goodies.dm b/code/modules/cargo/packs/goodies.dm
index 5d07e85bac..423d2fc743 100644
--- a/code/modules/cargo/packs/goodies.dm
+++ b/code/modules/cargo/packs/goodies.dm
@@ -52,8 +52,8 @@
cost = 300
contains = list(/obj/item/storage/toolbox/mechanical)
-/datum/supply_pack/goody/electrical_toolbox // mostly just to water down coupon probability
- name = "Mechanical Toolbox"
+/datum/supply_pack/goody/electrical_toolbox
+ name = "Electrical Toolbox"
desc = "A fully stocked electrical toolbox, for when you're too lazy to just print them out."
cost = 300
contains = list(/obj/item/storage/toolbox/electrical)
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/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm
index 18cd104ff8..f8292738ee 100644
--- a/code/modules/clothing/under/costume.dm
+++ b/code/modules/clothing/under/costume.dm
@@ -267,7 +267,7 @@
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON|USE_TAUR_CLIP_MASK
/obj/item/clothing/under/costume/christmas/croptop/green
- name = "green feminine christmas suit"
+ name = "green croptop christmas suit"
desc = "A simple green christmas suit. Smells minty!"
icon_state = "christmasfemaleg"
item_state = "christmasfemaleg"
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..e69af1df13 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
@@ -23,7 +22,7 @@
/datum/round_event/meteor_wave/setup()
announceWhen = 1
- startWhen = rand(90, 180) // Apparently it is by 2 seconds, so 90 is actually 180 seconds, and 180 is 360 seconds. So this is 3-6 minutes
+ startWhen = 150 // 5 minutes
if(GLOB.singularity_counter)
startWhen *= 1 - min(GLOB.singularity_counter * SINGULO_BEACON_DISTURBANCE, SINGULO_BEACON_MAX_DISTURBANCE)
endWhen = startWhen + 60
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/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index aab741edd6..3f11d88668 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -23,6 +23,7 @@
a_intent = INTENT_HARM //so we always get pushed instead of trying to swap
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
see_in_dark = 8
+ deathsound = 'sound/voice/scream/android_scream.ogg'
hud_type = /datum/hud/ai
med_hud = DATA_HUD_MEDICAL_BASIC
sec_hud = DATA_HUD_SECURITY_BASIC
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 6b58988c31..7c4125fbc6 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -361,8 +361,7 @@
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmed"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamed"),
"Eyebot" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "eyebotmed"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakemedbox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed")
)
var/list/L = list("Medihound" = "medihound", "Medihound Dark" = "medihounddark", "Vale" = "valemed")
for(var/a in L)
@@ -423,13 +422,6 @@
moduleselect_icon = "medihound"
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakemed"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakemedsleeper"
- moduleselect_icon = "medihound"
- moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
- dogborg = TRUE
else
return FALSE
return ..()
@@ -486,8 +478,7 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "caneng"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinaeng"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidereng"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeengbox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng")
)
var/list/L = list("Pup Dozer" = "pupdozer", "Vale" = "valeeng")
for(var/a in L)
@@ -545,11 +536,6 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "alinasleeper"
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakeeng"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakesecsleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -588,8 +574,7 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "cansec"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinasec"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidersec"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakesecbox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec")
)
var/list/L = list("K9" = "k9", "Vale" = "valesec", "K9 Dark" = "k9dark")
for(var/a in L)
@@ -645,11 +630,6 @@
sleeper_overlay = "valesecsleeper"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakesec"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakesecsleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -693,8 +673,7 @@
var/static/list/peace_icons = sortList(list(
"Default" = image(icon = 'icons/mob/robots.dmi', icon_state = "peace"),
"Borgi" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "borgi"),
- "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakepeacebox")
+ "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider")
))
var/peace_borg_icon = show_radial_menu(R, R , peace_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE)
switch(peace_borg_icon)
@@ -710,11 +689,6 @@
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
has_snowflake_deadsprite = TRUE
- if("Drake")
- cyborg_base_icon = "drakepeace"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakepeacesleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -853,8 +827,7 @@
"(Janitor) Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinajan"),
"(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"),
"(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"),
- "(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"),
- "(Janitor) Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakejanitbox")
+ "(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan")
)
var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark",
"(Janitor) Scrubpuppy" = "scrubpup")
@@ -926,11 +899,6 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "jsleeper"
dogborg = TRUE
- if("(Janitor) Drake")
- cyborg_base_icon = "drakejanit"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakesecsleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -976,8 +944,7 @@
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmin"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamin"),
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canmin"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeminebox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin")
)
var/list/L = list("Blade" = "blade", "Vale" = "valemine")
for(var/a in L)
@@ -1021,11 +988,6 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "valeminesleeper"
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakemine"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakeminesleeper"
- dogborg = TRUE
else
return FALSE
return ..()
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index c6aee397e4..944b909463 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -13,6 +13,7 @@
mob_biotypes = MOB_ROBOTIC
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
speech_span = SPAN_ROBOT
+ deathsound = 'sound/voice/borg_deathsound.ogg'
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
vore_flags = NO_VORE
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 d4e3f6ad0d..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
*********/
@@ -1033,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.dm b/code/modules/reagents/reagent_containers.dm
index b71584982c..be631513bd 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -70,7 +70,6 @@
return
/obj/item/reagent_containers/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
- . = ..()
if(user.a_intent == INTENT_HARM)
return ..()
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/smithing/anvil.dm b/code/modules/smithing/anvil.dm
new file mode 100644
index 0000000000..19d48119e6
--- /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, "You overwork the metal, causing it to turn into useless slag!")
+ 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/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 9172ce00bc..f23a03da5b 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -265,10 +265,6 @@
var/C = input(owner, "Select Color", "Select color", "#ffffff") as color|null
if(!C || QDELETED(src) || QDELETED(user) || QDELETED(owner) || owner != user)
return
- var/list/hsv = ReadHSV(RGBtoHSV(C))
- if(hsv[2] > 125)
- to_chat(user, "A color that saturated? Surely not!")
- return
var/range = input(user, "Enter range (0 - [max_light_beam_distance])", "Range Select", 0) as null|num
if(!isnum(range))
return
@@ -276,14 +272,24 @@
set_distance(clamp(range, 0, max_light_beam_distance))
assume_rgb(C)
+#define MAX_SATURATION 192
+#define MAX_LIGHTNESS 256
+
/obj/item/organ/eyes/robotic/glow/proc/assume_rgb(newcolor)
- current_color_string = newcolor
- eye_color = RGB2EYECOLORSTRING(current_color_string)
+ eye_color = RGB2EYECOLORSTRING(newcolor)
+ var/list/hsv = ReadHSV(RGBtoHSV(newcolor))
+ hsv[2] = clamp(hsv[2], 0, MAX_SATURATION)
+ hsv[3] = clamp(hsv[3], 0, MAX_LIGHTNESS)
+ var/new_hsv = hsv(hsv[1], hsv[2], hsv[3])
+ current_color_string = HSVtoRGB(new_hsv)
sync_light_effects()
cycle_mob_overlay()
if(!QDELETED(owner) && ishuman(owner)) //Other carbon mobs don't have eye color.
owner.dna.species.handle_body(owner)
+#undef MAX_SATURATION
+#undef MAX_LIGHTNESS
+
/obj/item/organ/eyes/robotic/glow/proc/cycle_mob_overlay()
remove_mob_overlay()
mob_overlay.color = current_color_string
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 33d143d400..929b42f96d 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -12,7 +12,7 @@
var/taste_sensitivity = 15 // lower is more sensitive.
maxHealth = TONGUE_MAX_HEALTH
var/list/initial_accents //the ones the tongue starts with, not what it currently has
- var/list/accents //done in order of priority (please always apply abductor accent and stuttering last)
+ var/list/accents = list() //done in order of priority (please always apply abductor accent and stuttering last)
var/static/list/languages_possible_base = typecacheof(list(
/datum/language/common,
/datum/language/draconic,
@@ -31,6 +31,8 @@
/obj/item/organ/tongue/Initialize(mapload)
. = ..()
+ for(var/accent in initial_accents)
+ accents += new accent
low_threshold_passed = "Your [name] feels a little sore."
low_threshold_cleared = "Your [name] soreness has subsided."
high_threshold_passed = "Your [name] is really starting to hurt."
@@ -38,16 +40,14 @@
now_failing = "Your [name] feels like it's about to fall out!."
now_fixed = "The excruciating pain of your [name] has subsided."
languages_possible = languages_possible_base
- for(var/accent in initial_accents)
- initial_accents += new accent
-/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args) //this wont proc unless there's initial_accents
- for(var/datum/accent/speech_modifier in initial_accents)
+/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args) //this wont proc unless there's initial_accents on the tongue
+ for(var/datum/accent/speech_modifier in accents)
speech_args = speech_modifier.modify_speech(speech_args, source, owner)
/obj/item/organ/tongue/applyOrganDamage(d, maximum = maxHealth)
. = ..()
- if (damage >= maxHealth)
+ if(damage >= maxHealth)
to_chat(owner, "Your tongue is singed beyond recognition, and disintegrates!")
SSblackbox.record_feedback("tally", "fermi_chem", 1, "Tongues lost to Fermi")
qdel(src)
@@ -56,7 +56,7 @@
..()
if(say_mod && M.dna && M.dna.species)
M.dna.species.say_mod = say_mod
- if(initial_accents)
+ if(length(initial_accents) || length(accents))
RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
M.UnregisterSignal(M, COMSIG_MOB_SAY)
@@ -163,11 +163,11 @@
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()
- . = ..()
initial_accents += pick(phomeme_types)
+ . = ..()
/obj/item/organ/tongue/bone/applyOrganDamage(var/d, var/maximum = maxHealth)
if(d < 0)
@@ -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
@@ -205,9 +205,6 @@
/obj/item/organ/tongue/robot/could_speak_language(language)
return ..() || electronics_magic
-/obj/item/organ/tongue/robot/handle_speech(datum/source, list/speech_args)
- ..()
-
/obj/item/organ/tongue/fluffy
name = "fluffy tongue"
desc = "OwO what's this?"
@@ -220,6 +217,7 @@
name = "cybernetic tongue"
desc = "A state of the art robotic tongue that can detect the pH of anything drank."
icon_state = "tonguecybernetic"
+ initial_accents = list(/datum/accent/span/robot)
taste_sensitivity = 10
maxHealth = 60 //It's robotic!
organ_flags = ORGAN_SYNTHETIC
@@ -231,10 +229,6 @@
var/errormessage = list("Runtime in tongue.dm, line 39: Undefined operation \"zapzap ow my tongue\"", "afhsjifksahgjkaslfhashfjsak", "-1.#IND", "Graham's number", "inside you all along", "awaiting at least 1 approving review before merging this taste request")
owner.say("The pH is appropriately [pick(errormessage)].", forced = "EMPed synthetic tongue")
-/obj/item/organ/tongue/cybernetic/handle_speech(datum/source, list/speech_args)
- speech_args[SPEECH_SPANS] |= SPAN_ROBOT
- ..()
-
/obj/item/organ/tongue/robot/ipc
name = "positronic voicebox"
say_mod = "beeps"
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/html/changelog.html b/html/changelog.html
index 864b5d19b0..f440b8c76c 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,86 @@
-->
+
09 September 2020
+
Putnam3145 updated:
+
+ - Made superconductivity work for the first time literally ever.
+
+
timothyteakettle updated:
+
+
+
08 September 2020
+
Ghommie updated:
+
+ - fixed names of the Electrical Toolbox goodie pack and green croptop christmas suit.
+ - Fixed turf visuals for real. Original PR by AnturK on tgstation.
+
+
KeRSedChaplain updated:
+
+ - added borg_deathsound.ogg and android_scream.ogg
+
+
silicons updated:
+
+ - meteor waves now have a static 5 minute timer.
+
+
+
07 September 2020
+
DeltaFire15 updated:
+
+ - fixed a typo causing a span not to show.
+
+
Putnam for debugging ammo loading! Thx!! updated:
+
+ - Removed spell blade, diamond picaxe, and fire wand from lava land loot tables
+ - Each mini boss now has its own type of crate.
+ - Each spike has its own type of crate that it pulls its now smaller loot table from
+ - Moved god eye from spike loot table to hard spawn collosses fight
+ - Moved holoparasight from spike loot table to bubble gum
+ - Moved magic meat hook from spike loot table to drake
+ - 2 more crates to Arena Shuttle as well as 4-4-4 of each new type of chest
+ - Replaced the diamond pick loot with a better one
+ - Replaced the cursted katana with a non cursted verson that deals half the damage and has less block!
+ - 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!
+ - 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
+
+
+
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:
@@ -1362,87 +1442,6 @@
- bluespace tray added, allowing twice as many items as the regular tray, printable at the service lathe, researched through science
- bluespace jar added, a kind of pet carrier that allows human sized mobs inside, and smashes when thrown, researched and printed through science
-
-
08 July 2020
-
DeltaFire15 updated:
-
- - The kill-once objective now works properly.
-
-
EmeraldSundisk updated:
-
- - CogStation now has an apothecary
- - Removes an outdated note on sleepers
- - Readjusts CogStation's chemistry lab
- - Slight area designation adjustments for Robotics
- - The arrivals plaque should be readable now
-
-
Owai-Seek updated:
-
- - Margarine, Chili Cheese Fries.
- - Egg Wraps are now categorized under egg foods.
- - Tuna Sandwich crafting/sprite is now visible.
- - Icons for chicken, cooked chicken, steak, grilled carp, corndogs
- - Icons for chili cheese fries, margarine, BLT sandwich
- - (Unused) icons for raw meatballs, and lard
-
-
-
07 July 2020
-
KasparoVy updated:
-
- - Fixes misaligned south-facing silver legwraps sprite.
-
-
Owai-Seek updated:
-
- - Bee Balm is now visible.
-
-
Weblure updated:
-
- - Fixed the slowdown formula for small character sprites; you guys don't use custom sprite sizes so just ignore these changes.
- - Fixed the "Move it to the threshold" button; it now does what it says.
- - Reworded some text to be clearer.
-
-
-
05 July 2020
-
Ghommie updated:
-
- - You can now actually gain wiring experience from using cable coils.
- - Opening the View Skill Panel shouldn't trigger messages about insufficient admin priviledges anymore.
-
-
Yakumo Chen, kappa-sama updated:
-
- - Removes improvised handguns
- - removed handsaws, improvised gun barrels (you can use atmos pipes again)
- - Guncrafting is less time and resource intensive
- - Item names in guncrafting are user-friendly.
-
-
kappa-sama updated:
-
- - cloth string to replace durathread string
- - durathread string
- - All bows and arrows have had crafting times significantly reduced, coming out at up to 6 times faster crafting speeds. Improvised bows no longer require durathread; instead, they use cloth materials.
-
-
silicons updated:
-
- - active blocking now has a toggle keybind
- - auto bunker override verb has been added
- - shields take 2.5 stam instead of 3.5 stam per second to maintain block
- - Cybernetic implant shields will auto-extend and be used to block if the user has no item to block with
-
-
timothyteakettle updated:
-
- - cooking oil is now far less lethal, requiring a higher volume of the reagent to deal more damage
-
-
-
04 July 2020
-
Sonic121x updated:
-
- - crushed Soldry sodacan
- - digitigrade version of chief medical officer's turtleneck and captain's female formal outfit.
-
-
silicons updated:
-
- - blood_DNA["color"] is now a single variable instead of a list
-
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 134fadc0c5..f8f47e456e 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -27215,3 +27215,63 @@ 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
+2020-09-07:
+ DeltaFire15:
+ - spellcheck: fixed a typo causing a span not to show.
+ Putnam for debugging ammo loading! Thx!!:
+ - 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
+2020-09-08:
+ Ghommie:
+ - spellcheck: fixed names of the Electrical Toolbox goodie pack and green croptop
+ christmas suit.
+ - bugfix: Fixed turf visuals for real. Original PR by AnturK on tgstation.
+ KeRSedChaplain:
+ - soundadd: added borg_deathsound.ogg and android_scream.ogg
+ silicons:
+ - balance: meteor waves now have a static 5 minute timer.
+2020-09-09:
+ Putnam3145:
+ - bugfix: Made superconductivity work for the first time literally ever.
+ timothyteakettle:
+ - bugfix: accents work better
diff --git a/html/changelogs/AutoChangeLog-pr-13321.yml b/html/changelogs/AutoChangeLog-pr-13321.yml
deleted file mode 100644
index 6ef5e6c391..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13321.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - tweak: "Hilbert hotel flavor text for one particular snowflake hotel changed."
diff --git a/html/changelogs/AutoChangeLog-pr-13324.yml b/html/changelogs/AutoChangeLog-pr-13324.yml
deleted file mode 100644
index 0724421ff7..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13324.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - refactor: "Added an extools proc hook alternative to rust_g logging."
diff --git a/html/changelogs/AutoChangeLog-pr-13331.yml b/html/changelogs/AutoChangeLog-pr-13331.yml
deleted file mode 100644
index b75fd5b557..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13331.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "raspy-on-osu"
-delete-after: True
-changes:
- - tweak: "protolathe item categories"
diff --git a/html/changelogs/AutoChangeLog-pr-13333.yml b/html/changelogs/AutoChangeLog-pr-13333.yml
deleted file mode 100644
index a5aba367be..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13333.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "raspy-on-osu"
-delete-after: True
-changes:
- - tweak: "supermatter shard examine text"
diff --git a/html/changelogs/AutoChangeLog-pr-13335.yml b/html/changelogs/AutoChangeLog-pr-13335.yml
deleted file mode 100644
index a216340770..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13335.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Bhijn"
-delete-after: True
-changes:
- - rscadd: "Readded the old method of temperature notifications in the form of a new pair of shivering/sweating notifications."
diff --git a/html/changelogs/AutoChangeLog-pr-13336.yml b/html/changelogs/AutoChangeLog-pr-13336.yml
deleted file mode 100644
index 60bad4e644..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13336.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - admin: "admins can now actually reduce threat level in dynamic"
diff --git a/html/changelogs/AutoChangeLog-pr-13337.yml b/html/changelogs/AutoChangeLog-pr-13337.yml
deleted file mode 100644
index 74f5833824..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13337.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - tweak: "Made owo.ogg smaller."
diff --git a/html/changelogs/AutoChangeLog-pr-13338.yml b/html/changelogs/AutoChangeLog-pr-13338.yml
deleted file mode 100644
index 73b3af15e0..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13338.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - tweak: "Character saving unit test is now more verbose on failure."
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/cyborg/Drakeborg-licensing.txt b/icons/mob/cyborg/Drakeborg-licensing.txt
deleted file mode 100644
index f2d3ca925c..0000000000
--- a/icons/mob/cyborg/Drakeborg-licensing.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-Drakeborg & drakeplushies are created by deviantart.com/mizartz
-
-https://creativecommons.org/licenses/by-nc-sa/3.0/
-Attribution-NonCommercial-ShareAlike 3.0 Unported
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-License
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
-"Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
-"Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(g) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
-"Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
-"License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike.
-"Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
-"Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
-"Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
-"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-"Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-"Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-to Distribute and Publicly Perform Adaptations.
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights described in Section 4(e).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(d), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(d), as requested.
-You may Distribute or Publicly Perform an Adaptation only under: (i) the terms of this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License"). You must include a copy of, or the URI, for Applicable License with every copy of each Adaptation You Distribute or Publicly Perform. You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License. You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
-You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in con-nection with the exchange of copyrighted works.
-If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-For the avoidance of doubt:
-
-Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
-Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c).
-Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
-This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-8. Miscellaneous
-
-Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
-Creative Commons Notice
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
\ No newline at end of file
diff --git a/icons/mob/cyborg/drakemech.dmi b/icons/mob/cyborg/drakemech.dmi
deleted file mode 100644
index 6a4845d983..0000000000
Binary files a/icons/mob/cyborg/drakemech.dmi and /dev/null 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/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/license.txt b/icons/obj/license.txt
deleted file mode 100644
index 2e27ba80f2..0000000000
--- a/icons/obj/license.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-icons/obj/plushies.dmi's icon state of secdrake and meddrake by Mizartz. It has been licensed under the CC BY-NC-SA 3.0 license.
-
-CC BY-NC-SA 3.0 https://creativecommons.org/licenses/by-nc-sa/3.0/
\ No newline at end of file
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/plushes.dmi b/icons/obj/plushes.dmi
index ac0c338016..11d02a46cc 100644
Binary files a/icons/obj/plushes.dmi and b/icons/obj/plushes.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/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/modular_citadel/icons/mob/widerobot.dmi b/modular_citadel/icons/mob/widerobot.dmi
index 29eb35c715..50c29bb75f 100644
Binary files a/modular_citadel/icons/mob/widerobot.dmi and b/modular_citadel/icons/mob/widerobot.dmi differ
diff --git a/sound/voice/borg_deathsound.ogg b/sound/voice/borg_deathsound.ogg
new file mode 100644
index 0000000000..bb11022abe
Binary files /dev/null and b/sound/voice/borg_deathsound.ogg differ
diff --git a/sound/voice/scream/android_scream.ogg b/sound/voice/scream/android_scream.ogg
new file mode 100644
index 0000000000..d3f179a84a
Binary files /dev/null and b/sound/voice/scream/android_scream.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index f0ca748803..c3e51d49cc 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -142,6 +142,7 @@
#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"
@@ -648,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"
@@ -737,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"
@@ -3025,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"
@@ -3143,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"
@@ -3309,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"