diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index a0f60711f5..13e6c5772a 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -22,7 +22,7 @@
#define COMPONENT_GLOB_BLOCK_CINEMATIC 1
// signals from globally accessible objects
-/// from SSsun when the sun changes position : (azimuth)
+/// from SSsun when the sun changes position : (primary_sun, suns)
#define COMSIG_SUN_MOVED "sun_moved"
//////////////////////////////////////////////////////////////////
diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index 53fbd15651..32da3b5938 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -425,6 +425,7 @@ Example config:
var/list/min_pop = Get(/datum/config_entry/keyed_list/min_pop)
var/list/max_pop = Get(/datum/config_entry/keyed_list/max_pop)
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
+ var/desired_chaos_level = 9 - SSpersistence.get_recent_chaos()
for(var/T in gamemode_cache)
var/datum/game_mode/M = new T()
if(!(M.config_tag in modes))
@@ -449,6 +450,17 @@ Example config:
adjustment += repeated_mode_adjust[recent_round]
recent_round = SSpersistence.saved_modes.Find(M.config_tag,recent_round+1,0)
final_weight *= max(0,((100-adjustment)/100))
+ if(Get(/datum/config_entry/flag/weigh_by_recent_chaos))
+ var/chaos_level = M.get_chaos()
+ var/exponent = Get(/datum/config_entry/number/chaos_exponent)
+ var/delta = chaos_level - desired_chaos_level
+ if(desired_chaos_level > 5)
+ delta = abs(min(delta, 0))
+ else if(desired_chaos_level < 5)
+ delta = max(delta, 0)
+ else
+ delta = abs(delta)
+ final_weight /= (delta + 1) ** exponent
runnable_modes[M] = final_weight
return runnable_modes
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 4034722417..76a5f5060c 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -7,6 +7,13 @@
/datum/config_entry/keyed_list/probability/ValidateListEntry(key_name)
return key_name in config.modes
+/datum/config_entry/keyed_list/chaos_level
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_NUM
+
+/datum/config_entry/keyed_list/chaos_level/ValidateListEntry(key_name)
+ return key_name in config.modes
+
/datum/config_entry/keyed_list/max_pop
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_NUM
@@ -596,3 +603,8 @@
/// Dirtyness multiplier for making turfs dirty
/datum/config_entry/number/turf_dirty_multiplier
config_entry_value = 1
+
+/datum/config_entry/flag/weigh_by_recent_chaos
+
+/datum/config_entry/number/chaos_exponent
+ config_entry_value = 1
diff --git a/code/controllers/subsystem/persistence/recent_votes_etc.dm b/code/controllers/subsystem/persistence/recent_votes_etc.dm
index f1b902d6ab..3c36c7581e 100644
--- a/code/controllers/subsystem/persistence/recent_votes_etc.dm
+++ b/code/controllers/subsystem/persistence/recent_votes_etc.dm
@@ -3,6 +3,7 @@
*/
/datum/controller/subsystem/persistence
var/list/saved_modes = list(1,2,3)
+ var/list/saved_chaos = list(5,5,5)
var/list/saved_dynamic_rules = list(list(),list(),list())
var/list/saved_storytellers = list("foo","bar","baz")
var/list/average_dynamic_threat = 50
@@ -33,6 +34,14 @@
file_data["data"] = saved_modes
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
+ saved_chaos[3] = saved_chaos[2]
+ saved_chaos[2] = saved_chaos[1]
+ saved_chaos[1] = SSticker.mode.get_chaos()
+ json_file = file("data/RecentChaos.json")
+ file_data = list()
+ file_data["data"] = saved_chaos
+ fdel(json_file)
+ WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode)
saved_storytellers.len = 3
@@ -105,3 +114,9 @@
if(!json)
return
saved_maps = json["maps"]
+
+/datum/controller/subsystem/persistence/proc/get_recent_chaos()
+ var/sum = 0
+ for(var/n in saved_chaos)
+ sum += n
+ return sum/length(saved_chaos)
diff --git a/code/controllers/subsystem/processing/weather.dm b/code/controllers/subsystem/processing/weather.dm
index ca067953cc..4035149ef2 100644
--- a/code/controllers/subsystem/processing/weather.dm
+++ b/code/controllers/subsystem/processing/weather.dm
@@ -70,3 +70,8 @@ PROCESSING_SUBSYSTEM_DEF(weather)
A = W
break
return A
+
+/datum/controller/subsystem/processing/weather/proc/get_weather_by_type(datum/weather/weather_datum_type)
+ for(var/V in processing)
+ if(istype(V,weather_datum_type))
+ return V
diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm
index 442329cf46..746b1be7a9 100644
--- a/code/controllers/subsystem/sun.dm
+++ b/code/controllers/subsystem/sun.dm
@@ -1,32 +1,63 @@
+#define OCCLUSION_DISTANCE 20
+
+/datum/sun
+ var/azimuth = 0 // clockwise, top-down rotation from 0 (north) to 359
+ var/power_mod = 1 // how much power this sun is outputting relative to standard
+
+
+/datum/sun/vv_edit_var(var_name, var_value)
+ . = ..()
+ if(var_name == NAMEOF(src, azimuth))
+ SSsun.complete_movement()
+
+/atom/proc/check_obscured(datum/sun/sun, distance = OCCLUSION_DISTANCE)
+ var/target_x = round(sin(sun.azimuth), 0.01)
+ var/target_y = round(cos(sun.azimuth), 0.01)
+ var/x_hit = x
+ var/y_hit = y
+ var/turf/hit
+
+ for(var/run in 1 to distance)
+ x_hit += target_x
+ y_hit += target_y
+ hit = locate(round(x_hit, 1), round(y_hit, 1), z)
+ if(hit.opacity)
+ return TRUE
+ if(hit.x == 1 || hit.x == world.maxx || hit.y == 1 || hit.y == world.maxy) //edge of the map
+ break
+ return FALSE
+
SUBSYSTEM_DEF(sun)
name = "Sun"
wait = 1 MINUTES
flags = SS_NO_TICK_CHECK
- var/azimuth = 0 ///clockwise, top-down rotation from 0 (north) to 359
+ var/list/datum/sun/suns = list()
+ var/datum/sun/primary_sun
var/azimuth_mod = 1 ///multiplier against base_rotation
var/base_rotation = 6 ///base rotation in degrees per fire
/datum/controller/subsystem/sun/Initialize(start_timeofday)
- azimuth = rand(0, 359)
+ primary_sun = new
+ suns += primary_sun
+ primary_sun.azimuth = rand(0, 359)
azimuth_mod = round(rand(50, 200)/100, 0.01) // 50% - 200% of standard rotation
if(prob(50))
azimuth_mod *= -1
return ..()
/datum/controller/subsystem/sun/fire(resumed = FALSE)
- azimuth += azimuth_mod * base_rotation
- azimuth = round(azimuth, 0.01)
- if(azimuth >= 360)
- azimuth -= 360
- if(azimuth < 0)
- azimuth += 360
+ for(var/S in suns)
+ var/datum/sun/sun = S
+ sun.azimuth += azimuth_mod * base_rotation
+ sun.azimuth = round(sun.azimuth, 0.01)
+ if(sun.azimuth >= 360)
+ sun.azimuth -= 360
+ if(sun.azimuth < 0)
+ sun.azimuth += 360
complete_movement()
/datum/controller/subsystem/sun/proc/complete_movement()
- SEND_SIGNAL(src, COMSIG_SUN_MOVED, azimuth)
+ SEND_SIGNAL(src, COMSIG_SUN_MOVED, primary_sun, suns)
-/datum/controller/subsystem/sun/vv_edit_var(var_name, var_value)
- . = ..()
- if(var_name == NAMEOF(src, azimuth))
- complete_movement()
+#undef OCCLUSION_DISTANCE
diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm
index baf6d81e0f..cc988544b8 100644
--- a/code/datums/components/material_container.dm
+++ b/code/datums/components/material_container.dm
@@ -112,32 +112,87 @@
user_insert(I, user) //, mat_container_flags)
/// Proc used for when player inserts materials
-/datum/component/material_container/proc/user_insert(obj/item/I, mob/living/user)
+/datum/component/material_container/proc/user_insert(obj/item/I, mob/living/user, datum/component/remote_materials/remote = null)
set waitfor = FALSE
- var/requested_amount
var/active_held = user.get_active_held_item() // differs from I when using TK
- if(istype(I, /obj/item/stack) && precise_insertion)
- var/atom/current_parent = parent
+ var/inserted = 0
+
+ //handle stacks specially
+ if(istype(I, /obj/item/stack))
+ var/atom/current_parent = remote ? remote.parent : parent //is the user using a remote materials component?
var/obj/item/stack/S = I
- requested_amount = input(user, "How much do you want to insert?", "Inserting [S.singular_name]s") as num|null
+
+ //try to get ammount to use
+ var/requested_amount
+ if(precise_insertion)
+ requested_amount = input(user, "How much do you want to insert?", "Inserting [S.singular_name]s") as num|null
+ else
+ requested_amount= S.amount
+
if(isnull(requested_amount) || (requested_amount <= 0))
return
- if(QDELETED(I) || QDELETED(user) || QDELETED(src) || parent != current_parent || user.physical_can_use_topic(current_parent) < UI_INTERACTIVE || user.get_active_held_item() != active_held)
+ if(QDELETED(I) || QDELETED(user) || QDELETED(src) || user.get_active_held_item() != active_held)
return
- if(!user.temporarilyRemoveItemFromInventory(I))
- to_chat(user, "[I] is stuck to you and cannot be placed into [parent].")
- return
- var/inserted = insert_item(I, stack_amt = requested_amount)//, breakdown_flags= mat_container_flags)
+ //are we still in range after the user input?
+ if((remote ? remote.parent : parent) != current_parent || user.physical_can_use_topic(current_parent) < UI_INTERACTIVE)
+ return
+ inserted = insert_stack(S, requested_amount)
+ else
+ if(!user.temporarilyRemoveItemFromInventory(I))
+ to_chat(user, "[I] is stuck to you and cannot be placed into [parent].")
+ return
+ inserted = insert_item(I)
+ qdel(I)
+
if(inserted)
to_chat(user, "You insert a material total of [inserted] into [parent].")
- qdel(I)
if(after_insert)
after_insert.Invoke(I, last_inserted_id, inserted)
- else if(I == active_held)
- user.put_in_active_hand(I)
+ if(remote && remote.after_insert)
+ remote.after_insert.Invoke(I, last_inserted_id, inserted)
+
+//Inserts a number of sheets from a stack, returns the amount of sheets used.
+/datum/component/material_container/proc/insert_stack(obj/item/stack/S, amt, multiplier = 1)
+ if(isnull(amt))
+ amt = S.amount
+
+ if(amt <= 0)
+ return FALSE
+
+ if(amt > S.amount)
+ amt = S.amount
+
+ var/material_amt = get_item_material_amount(S)
+ if(!material_amt)
+ return FALSE
+
+ //get max number of sheets we have room to add
+ var/mat_per_sheet = material_amt/S.amount
+ amt = min(amt, round((max_amount - total_amount) / (mat_per_sheet)))
+ if(!amt)
+ return FALSE
+
+ //add the mats and keep track of how much was added
+ var/starting_total = total_amount
+ for(var/MAT in materials)
+ materials[MAT] += S.mats_per_unit[MAT] * amt * multiplier
+ total_amount += S.mats_per_unit[MAT] * amt * multiplier
+ var/total_added = total_amount - starting_total
+
+ //update last_inserted_id with mat making up majority of the stack
+ var/primary_mat
+ var/max_mat_value = 0
+ for(var/MAT in materials)
+ if(S.mats_per_unit[MAT] > max_mat_value)
+ max_mat_value = S.mats_per_unit[MAT]
+ primary_mat = MAT
+ last_inserted_id = primary_mat
+
+ S.use(amt)
+ return total_added
/// Proc specifically for inserting items, returns the amount of materials entered.
-/datum/component/material_container/proc/insert_item(obj/item/I, var/multiplier = 1, stack_amt)
+/datum/component/material_container/proc/insert_item(obj/item/I, var/multiplier = 1)
if(QDELETED(I))
return FALSE
@@ -198,6 +253,7 @@
var/total_amount_saved = total_amount
if(mat)
materials[mat] += amt
+ total_amount += amt
else
for(var/i in materials)
materials[i] += amt
diff --git a/code/datums/components/remote_materials.dm b/code/datums/components/remote_materials.dm
index 01038c11d3..b1d23ea3a8 100644
--- a/code/datums/components/remote_materials.dm
+++ b/code/datums/components/remote_materials.dm
@@ -15,13 +15,15 @@ handles linking back and forth.
var/category
var/allow_standalone
var/local_size = INFINITY
+ var/datum/callback/after_insert
-/datum/component/remote_materials/Initialize(category, mapload, allow_standalone = TRUE, force_connect = FALSE)
+/datum/component/remote_materials/Initialize(category, mapload, allow_standalone = TRUE, force_connect = FALSE, _after_insert)
if (!isatom(parent))
return COMPONENT_INCOMPATIBLE
src.category = category
src.allow_standalone = allow_standalone
+ after_insert = _after_insert
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
@@ -67,7 +69,7 @@ handles linking back and forth.
/datum/material/plastic,
)
- mat_container = parent.AddComponent(/datum/component/material_container, allowed_mats, local_size, allowed_types=/obj/item/stack)
+ mat_container = parent.AddComponent(/datum/component/material_container, allowed_mats, local_size, allowed_types=/obj/item/stack, _after_insert = after_insert)
/datum/component/remote_materials/proc/set_local_size(size)
local_size = size
@@ -103,7 +105,7 @@ handles linking back and forth.
return COMPONENT_NO_AFTERATTACK
else if(silo && istype(I, /obj/item/stack))
- if(silo.remote_attackby(parent, user, I))
+ if(silo.remote_attackby(parent, user, I, src))
return COMPONENT_NO_AFTERATTACK
/datum/component/remote_materials/proc/on_hold()
diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm
index 96ba7bf965..4277cb1316 100644
--- a/code/datums/martial/cqc.dm
+++ b/code/datums/martial/cqc.dm
@@ -97,7 +97,7 @@
D.visible_message("[A] locks [D] into a restraining position!", \
"[A] locks you into a restraining position!")
D.apply_damage(damage, STAMINA)
- D.Stun(100)
+ D.Stun(10)
restraining = TRUE
addtimer(VARSET_CALLBACK(src, restraining, FALSE), 50, TIMER_UNIQUE)
return TRUE
@@ -175,7 +175,7 @@
return TRUE
if(CHECK_MOBILITY(D, MOBILITY_MOVE) || !restraining)
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
- if(damage >= stunthreshold)
+ if(damage >= stunthreshold)
I = D.get_active_held_item()
D.visible_message("[A] strikes [D]'s jaw with their hand!", \
"[A] strikes your jaw, disorienting you!")
@@ -196,7 +196,8 @@
log_combat(A, D, "knocked out (Chokehold)(CQC)")
D.visible_message("[A] puts [D] into a chokehold!", \
"[A] puts you into a chokehold!")
- D.SetSleeping(400)
+ if(D.silent <= 10)
+ D.silent = clamp(D.silent + 10, 0, 10)
restraining = FALSE
if(A.grab_state < GRAB_NECK)
A.setGrabState(GRAB_NECK)
@@ -213,7 +214,7 @@
to_chat(usr, "Slam: Grab Harm. Slam opponent into the ground, knocking them down.")
to_chat(usr, "CQC Kick: Harm Harm. Knocks opponent away. Knocks out stunned or knocked down opponents.")
- to_chat(usr, "Restrain: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a chokehold.")
+ to_chat(usr, "Restrain: Grab Grab. Locks opponents into a restraining position, disarm to mute them with a chokehold.")
to_chat(usr, "Pressure: Disarm Grab. Decent stamina damage.")
to_chat(usr, "Consecutive CQC: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.")
diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm
index 4638508d1c..337be3e005 100644
--- a/code/datums/weather/weather_types/radiation_storm.dm
+++ b/code/datums/weather/weather_types/radiation_storm.dm
@@ -22,7 +22,7 @@
target_trait = ZTRAIT_STATION
immunity_type = "rad"
-
+
var/radiation_intensity = 100
/datum/weather/rad_storm/telegraph()
diff --git a/code/game/gamemodes/bloodsucker/bloodsucker.dm b/code/game/gamemodes/bloodsucker/bloodsucker.dm
index c54de16e2e..65321f5820 100644
--- a/code/game/gamemodes/bloodsucker/bloodsucker.dm
+++ b/code/game/gamemodes/bloodsucker/bloodsucker.dm
@@ -24,6 +24,7 @@
traitor_name = "Bloodsucker"
antag_flag = ROLE_BLOODSUCKER
false_report_weight = 1
+ chaos = 4
restricted_jobs = list("AI","Cyborg")
protected_jobs = list("Chaplain", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 20
@@ -87,7 +88,7 @@
// Init Sunlight (called from datum_bloodsucker.on_gain(), in case game mode isn't even Bloodsucker
/datum/game_mode/proc/check_start_sunlight()
// Already Sunlight (and not about to cancel)
- if(istype(bloodsucker_sunlight) && !bloodsucker_sunlight.cancel_me)
+ if(istype(bloodsucker_sunlight))
return
bloodsucker_sunlight = new ()
@@ -97,7 +98,6 @@
if(!istype(bloodsucker_sunlight))
return
if(bloodsuckers.len <= 0)
- bloodsucker_sunlight.cancel_me = TRUE
qdel(bloodsucker_sunlight)
bloodsucker_sunlight = null
diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm
index 718ed2c103..eda6b5f9e2 100644
--- a/code/game/gamemodes/brother/traitor_bro.dm
+++ b/code/game/gamemodes/brother/traitor_bro.dm
@@ -6,6 +6,7 @@
name = "traitor+brothers"
config_tag = "traitorbro"
required_players = 25
+ chaos = 5
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 8029685bf7..20ef83a54c 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -10,6 +10,7 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th
config_tag = "changeling"
antag_flag = ROLE_CHANGELING
false_report_weight = 10
+ chaos = 5
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") //citadel change - adds HoP, CE, CMO, and RD to ling role blacklist
required_players = 15
diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm
index b010b08bc3..88a1cde8ce 100644
--- a/code/game/gamemodes/changeling/traitor_chan.dm
+++ b/code/game/gamemodes/changeling/traitor_chan.dm
@@ -2,6 +2,7 @@
name = "traitor+changeling"
config_tag = "traitorchan"
false_report_weight = 10
+ chaos = 6
traitors_possible = 3 //hard limit on traitors if scaling is turned off
restricted_jobs = list("AI", "Cyborg")
required_players = 25
diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm
index 29455afe56..d8ebf6f20c 100644
--- a/code/game/gamemodes/clock_cult/clock_cult.dm
+++ b/code/game/gamemodes/clock_cult/clock_cult.dm
@@ -134,6 +134,7 @@ Credit where due:
config_tag = "clockwork_cult"
antag_flag = ROLE_SERVANT_OF_RATVAR
false_report_weight = 10
+ chaos = 8
required_players = 24 //Fixing this directly for now since apparently config machine for forcing modes broke.
required_enemies = 3
recommended_enemies = 5
diff --git a/code/game/gamemodes/clown_ops/clown_ops.dm b/code/game/gamemodes/clown_ops/clown_ops.dm
index 108c67ad27..659d2de105 100644
--- a/code/game/gamemodes/clown_ops/clown_ops.dm
+++ b/code/game/gamemodes/clown_ops/clown_ops.dm
@@ -1,7 +1,7 @@
/datum/game_mode/nuclear/clown_ops
name = "clown ops"
config_tag = "clownops"
-
+ chaos = 8
announce_span = "danger"
announce_text = "Clown empire forces are approaching the station in an attempt to HONK it!\n\
Operatives: Secure the nuclear authentication disk and use your bananium fission explosive to HONK the station.\n\
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 8ec4123201..ba9fad7a84 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -38,6 +38,7 @@
config_tag = "cult"
antag_flag = ROLE_CULTIST
false_report_weight = 10
+ chaos = 8
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 30
diff --git a/code/game/gamemodes/devil/devil agent/devil_agent.dm b/code/game/gamemodes/devil/devil agent/devil_agent.dm
index 789cff5c8f..ccfd6f1dd5 100644
--- a/code/game/gamemodes/devil/devil agent/devil_agent.dm
+++ b/code/game/gamemodes/devil/devil agent/devil_agent.dm
@@ -1,6 +1,7 @@
/datum/game_mode/devil/devil_agents
name = "Devil Agents"
config_tag = "devil_agents"
+ chaos = 5
required_players = 25
required_enemies = 3
recommended_enemies = 8
diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm
index 0f2e8f7858..9bf7fc0e82 100644
--- a/code/game/gamemodes/devil/devil_game_mode.dm
+++ b/code/game/gamemodes/devil/devil_game_mode.dm
@@ -3,6 +3,7 @@
config_tag = "devil"
antag_flag = ROLE_DEVIL
false_report_weight = 1
+ chaos = 3
protected_jobs = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI")
required_players = 0
required_enemies = 1
diff --git a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm
index a3e3c54dce..1693163fa2 100644
--- a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm
+++ b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm
@@ -3,6 +3,7 @@
config_tag = "heresy"
antag_flag = ROLE_HERETIC
false_report_weight = 5
+ chaos = 5
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") //citadel change - adds HoP, CE, CMO, and RD to heretic role blacklist
required_players = 15
diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm
index 976f3e3eca..a6abcbefbd 100644
--- a/code/game/gamemodes/extended/extended.dm
+++ b/code/game/gamemodes/extended/extended.dm
@@ -3,6 +3,7 @@
config_tag = "secret_extended"
false_report_weight = 5
required_players = 0
+ chaos = 0
announce_span = "notice"
announce_text = "Just have fun and enjoy the game!"
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 8bef236c5e..10c0154412 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -17,6 +17,7 @@
var/config_tag = null
var/votable = 1
var/probability = 0
+ var/chaos = 5 // 0-9, used for weighting round-to-round
var/false_report_weight = 0 //How often will this show up incorrectly in a centcom report?
var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm
var/nuke_off_station = 0 //Used for tracking where the nuke hit
@@ -623,3 +624,10 @@
/// Mode specific info for ghost game_info
/datum/game_mode/proc/ghost_info()
return
+
+/datum/game_mode/proc/get_chaos()
+ var/chaos_levels = CONFIG_GET(keyed_list/chaos_level)
+ if(config_tag in chaos_levels)
+ return chaos_levels[config_tag]
+ else
+ return chaos
diff --git a/code/game/gamemodes/gangs/gangs.dm b/code/game/gamemodes/gangs/gangs.dm
index 0dc4a520ef..100669c487 100644
--- a/code/game/gamemodes/gangs/gangs.dm
+++ b/code/game/gamemodes/gangs/gangs.dm
@@ -6,6 +6,7 @@ GLOBAL_LIST_EMPTY(gangs)
name = "gang war"
config_tag = "gang"
antag_flag = ROLE_GANG
+ chaos = 9
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 15
diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm
index afeebb770b..78fe6d9324 100644
--- a/code/game/gamemodes/meteor/meteor.dm
+++ b/code/game/gamemodes/meteor/meteor.dm
@@ -2,6 +2,7 @@
name = "meteor"
config_tag = "meteor"
false_report_weight = 1
+ chaos = 9
var/meteordelay = 2000
var/nometeors = 0
var/rampupdelta = 5
diff --git a/code/game/gamemodes/monkey/monkey.dm b/code/game/gamemodes/monkey/monkey.dm
index 76460ffbb8..c91252258a 100644
--- a/code/game/gamemodes/monkey/monkey.dm
+++ b/code/game/gamemodes/monkey/monkey.dm
@@ -11,6 +11,7 @@
required_players = 20
required_enemies = 1
recommended_enemies = 1
+ chaos = 9
restricted_jobs = list("Cyborg", "AI")
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 48a298984c..dcf84e84db 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -2,6 +2,7 @@
name = "nuclear emergency"
config_tag = "nuclear"
false_report_weight = 10
+ chaos = 9
required_players = 28 // 30 players - 3 players to be the nuke ops = 25 players remaining
required_enemies = 2
recommended_enemies = 5
diff --git a/code/game/gamemodes/overthrow/overthrow.dm b/code/game/gamemodes/overthrow/overthrow.dm
index dca0c1ade1..6a118567df 100644
--- a/code/game/gamemodes/overthrow/overthrow.dm
+++ b/code/game/gamemodes/overthrow/overthrow.dm
@@ -3,6 +3,7 @@
name = "overthrow"
config_tag = "overthrow"
antag_flag = ROLE_OVERTHROW
+ chaos = 5
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 20 // the core idea is of a swift, bloodless coup, so it shouldn't be as chaotic as revs.
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 9eed18d906..419a74d616 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -12,6 +12,7 @@
config_tag = "revolution"
antag_flag = ROLE_REV
false_report_weight = 10
+ chaos = 8
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 20
diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm
index fc669d4855..c64e508cef 100644
--- a/code/game/gamemodes/traitor/double_agents.dm
+++ b/code/game/gamemodes/traitor/double_agents.dm
@@ -10,6 +10,7 @@
required_enemies = 5
recommended_enemies = 8
reroll_friendly = 0
+ chaos = 7
traitor_name = "Nanotrasen Internal Affairs Agent"
antag_flag = ROLE_INTERNAL_AFFAIRS
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index 8b1b18660a..d42fe615cd 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -17,6 +17,7 @@
recommended_enemies = 4
reroll_friendly = 1
enemy_minimum_age = 0
+ chaos = 2
announce_span = "danger"
announce_text = "There are Syndicate agents on the station!\n\
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index 23f065318e..d8cb851aad 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -12,6 +12,7 @@
recommended_enemies = 1
enemy_minimum_age = 7
round_ends_with_antag_death = 1
+ chaos = 9
announce_span = "danger"
announce_text = "There is a space wizard attacking the station!\n\
Wizard: Accomplish your objectives and cause mayhem on the station.\n\
diff --git a/code/game/machinery/aug_manipulator.dm b/code/game/machinery/aug_manipulator.dm
index 6fdeae61a1..e496973d75 100644
--- a/code/game/machinery/aug_manipulator.dm
+++ b/code/game/machinery/aug_manipulator.dm
@@ -8,7 +8,19 @@
max_integrity = 200
var/obj/item/bodypart/storedpart
var/initial_icon_state
- var/static/list/style_list_icons = list("standard" = 'icons/mob/augmentation/augments.dmi', "engineer" = 'icons/mob/augmentation/augments_engineer.dmi', "security" = 'icons/mob/augmentation/augments_security.dmi', "mining" = 'icons/mob/augmentation/augments_mining.dmi')
+ var/static/list/style_list_icons = list("standard" = 'icons/mob/augmentation/augments.dmi',
+ "engineer" = 'icons/mob/augmentation/augments_engineer.dmi',
+ "security" = 'icons/mob/augmentation/augments_security.dmi',
+ "mining" = 'icons/mob/augmentation/augments_mining.dmi',
+ "Talon" = 'icons/mob/augmentation/cosmetic_prosthetic/talon.dmi',
+ "Nanotrasen" = 'icons/mob/augmentation/cosmetic_prosthetic/nanotrasen.dmi',
+ "Hephaesthus" = 'icons/mob/augmentation/cosmetic_prosthetic/hephaestus.dmi',
+ "Bishop" = 'icons/mob/augmentation/cosmetic_prosthetic/bishop.dmi',
+ "Xion" = 'icons/mob/augmentation/cosmetic_prosthetic/xion.dmi',
+ "Grayson" = 'icons/mob/augmentation/cosmetic_prosthetic/grayson.dmi',
+ "Cybersolutions" = 'icons/mob/augmentation/cosmetic_prosthetic/cybersolutions.dmi',
+ "Ward" = 'icons/mob/augmentation/cosmetic_prosthetic/ward.dmi'
+ )
/obj/machinery/aug_manipulator/examine(mob/user)
. = ..()
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 0303117b96..bbbfa7f1bd 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -65,7 +65,7 @@
/obj/machinery/mecha_part_fabricator/Initialize(mapload)
stored_research = new
- rmat = AddComponent(/datum/component/remote_materials, "mechfab", mapload && link_on_init)
+ rmat = AddComponent(/datum/component/remote_materials, "mechfab", mapload && link_on_init, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
RefreshParts() //Recalculating local material sizes if the fab isn't linked
return ..()
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 25a5bcc800..90912f7409 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -721,24 +721,24 @@
/obj/item/disk/medical/defib_heal
name = "Defibrillator Healing Disk"
- desc = "An upgrade which increases the healing power of the defibrillator"
+ desc = "An upgrade which increases the healing power of the defibrillator."
icon_state = "heal_disk"
custom_materials = list(/datum/material/iron=16000, /datum/material/glass = 18000, /datum/material/gold = 6000, /datum/material/silver = 6000)
/obj/item/disk/medical/defib_shock
name = "Defibrillator Anti-Shock Disk"
- desc = "A safety upgrade that guarantees only the patient will get shocked"
+ desc = "A safety upgrade that guarantees only the patient will get shocked."
icon_state = "zap_disk"
custom_materials = list(/datum/material/iron=16000, /datum/material/glass = 18000, /datum/material/gold = 6000, /datum/material/silver = 6000)
/obj/item/disk/medical/defib_decay
name = "Defibrillator Body-Decay Extender Disk"
- desc = "An upgrade allowing the defibrillator to work on more decayed bodies"
+ desc = "An upgrade allowing the defibrillator to work on bodies that have decayed further."
icon_state = "body_disk"
custom_materials = list(/datum/material/iron=16000, /datum/material/glass = 18000, /datum/material/gold = 16000, /datum/material/silver = 6000, /datum/material/titanium = 2000)
/obj/item/disk/medical/defib_speed
name = "Defibrillator Fast Charge Disk"
- desc = "An upgrade to the defibrillator capacitors, which let it charge faster"
+ desc = "An upgrade to the defibrillator capacitors, which lets it charge faster."
icon_state = "fast_disk"
custom_materials = list(/datum/material/iron=16000, /datum/material/glass = 8000, /datum/material/gold = 26000, /datum/material/silver = 26000)
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 15d10c4d11..1e2ae93df9 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -3,9 +3,8 @@
/obj/item/melee/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user)
if(target.check_martial_melee_block())
- target.visible_message("[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!",
+ target.visible_message("[target.name] blocks your attack!",
"You block the attack!")
- user.Stun(40)
return TRUE
/obj/item/melee/chainofcommand
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
index 2cb50c6b3b..f26152f90e 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
@@ -7,98 +7,79 @@
// Over Time, tick down toward a "Solar Flare" of UV buffeting the station. This period is harmful to vamps.
/obj/effect/sunlight
//var/amDay = FALSE
- var/cancel_me = FALSE
var/amDay = FALSE
var/time_til_cycle = 0
- var/nightime_duration = 900 //15 Minutes
+ var/nighttime_duration = 900 //15 Minutes
+ var/issued_XP = FALSE
/obj/effect/sunlight/Initialize()
. = ..()
- INVOKE_ASYNC(src, .proc/countdown)
- INVOKE_ASYNC(src, .proc/hud_tick)
-/obj/effect/sunlight/proc/countdown()
- set waitfor = FALSE
+/obj/effect/sunlight/proc/start_countdown()
+ START_PROCESSING(SSweather, src) //it counts as weather right
+ time_til_cycle = nighttime_duration
- while(!cancel_me)
-
- time_til_cycle = nightime_duration
-
- // Part 1: Night (all is well)
- while(time_til_cycle > TIME_BLOODSUCKER_DAY_WARN)
- sleep(10)
- if(cancel_me)
- return
- //sleep(TIME_BLOODSUCKER_NIGHT - TIME_BLOODSUCKER_DAY_WARN)
- warn_daylight(1,"Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. Prepare to seek cover in a coffin or closet.") // time2text <-- use Help On
- give_home_power() // Give VANISHING ACT power to all vamps with a lair!
-
- // Part 2: Night Ending
- while(time_til_cycle > TIME_BLOODSUCKER_DAY_FINAL_WARN)
- sleep(10)
- if(cancel_me)
- return
- //sleep(TIME_BLOODSUCKER_DAY_WARN - TIME_BLOODSUCKER_DAY_FINAL_WARN)
- message_admins("BLOODSUCKER NOTICE: Daylight beginning in [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds.)")
- warn_daylight(2,"Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!",\
- "In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!")
-
- // (FINAL LIL WARNING)
- while(time_til_cycle > 5)
- sleep(10)
- if(cancel_me)
- return
- //sleep(TIME_BLOODSUCKER_DAY_FINAL_WARN - 50)
- warn_daylight(3,"Seek cover, for Sol rises!")
-
- // Part 3: Night Ending
- while(time_til_cycle > 0)
- sleep(10)
- if(cancel_me)
- return
- //sleep(50)
- warn_daylight(4,"Solar flares bombard the station with deadly UV light!
Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!",\
- "Solar flares bombard the station with UV light!")
-
- // Part 4: Day
- amDay = TRUE
- message_admins("BLOODSUCKER NOTICE: Daylight Beginning (Lasts for [TIME_BLOODSUCKER_DAY / 60] minutes.)")
- time_til_cycle = TIME_BLOODSUCKER_DAY
- sleep(10) // One second grace period.
- //var/daylight_time = TIME_BLOODSUCKER_DAY
- var/issued_XP = FALSE
- while(time_til_cycle > 0)
+/obj/effect/sunlight/process()
+ // Update all Bloodsucker sunlight huds
+ for(var/datum/mind/M in SSticker.mode.bloodsuckers)
+ if(!istype(M) || !istype(M.current))
+ continue
+ var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
+ if(istype(bloodsuckerdatum))
+ bloodsuckerdatum.update_sunlight(max(0, time_til_cycle), amDay) // This pings all HUDs
+ time_til_cycle--
+ if(amDay)
+ if(time_til_cycle > 0 && time_til_cycle % 4 == 0)
punish_vamps()
- sleep(TIME_BLOODSUCKER_BURN_INTERVAL)
- if(cancel_me)
- return
- //daylight_time -= TIME_BLOODSUCKER_BURN_INTERVAL
- // Issue Level Up!
if(!issued_XP && time_til_cycle <= 5)
issued_XP = TRUE
- vamps_rank_up()
+ // Cycle through all vamp antags and check if they're inside a closet.
+ for(var/datum/mind/M in SSticker.mode.bloodsuckers)
+ if(!istype(M) || !istype(M.current))
+ continue
+ var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
+ if(istype(bloodsuckerdatum))
+ bloodsuckerdatum.RankUp() // Rank up! Must still be in a coffin to level!
warn_daylight(5,"The solar flare has ended, and the daylight danger has passed...for now.",\
"The solar flare has ended, and the daylight danger has passed...for now.")
amDay = FALSE
- day_end() // Remove VANISHING ACT power from all vamps who have it! Clear Warnings (sunlight, locker protection)
- nightime_duration += 100 //Each day makes the night a minute longer.
- message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nightime_duration / 60] minutes.)")
-
-
-
-/obj/effect/sunlight/proc/hud_tick()
- set waitfor = FALSE
- while(!cancel_me)
- // Update all Bloodsucker sunlight huds
+ issued_XP = FALSE
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M) || !istype(M.current))
continue
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
- if(istype(bloodsuckerdatum))
- bloodsuckerdatum.update_sunlight(max(0, time_til_cycle), amDay) // This pings all HUDs
- sleep(10)
- time_til_cycle --
+ if(!istype(bloodsuckerdatum))
+ continue
+ // Reset Warnings
+ bloodsuckerdatum.warn_sun_locker = FALSE
+ bloodsuckerdatum.warn_sun_burn = FALSE
+ // Remove Dawn Powers
+ for(var/datum/action/bloodsucker/P in bloodsuckerdatum.powers)
+ if(istype(P, /datum/action/bloodsucker/gohome))
+ bloodsuckerdatum.powers -= P
+ P.Remove(M.current)
+ nighttime_duration += 100 //Each day makes the night a minute longer.
+ time_til_cycle = nighttime_duration
+ message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nighttime_duration / 60] minutes.)")
+ else
+ switch(time_til_cycle)
+ if(TIME_BLOODSUCKER_DAY_WARN)
+ //sleep(TIME_BLOODSUCKER_NIGHT - TIME_BLOODSUCKER_DAY_WARN)
+ warn_daylight(1,"Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. Prepare to seek cover in a coffin or closet.") // time2text <-- use Help On
+ give_home_power() // Give VANISHING ACT power to all vamps with a lair!
+ if(TIME_BLOODSUCKER_DAY_FINAL_WARN)
+ message_admins("BLOODSUCKER NOTICE: Daylight beginning in [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds.)")
+ warn_daylight(2,"Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!",\
+ "In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!")
+ if(5)
+ warn_daylight(3,"Seek cover, for Sol rises!")
+ if(0)
+ warn_daylight(4,"Solar flares bombard the station with deadly UV light!
Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!",\
+ "Solar flares bombard the station with UV light!")
+ amDay = TRUE
+ message_admins("BLOODSUCKER NOTICE: Daylight Beginning (Lasts for [TIME_BLOODSUCKER_DAY / 60] minutes.)")
+ time_til_cycle = TIME_BLOODSUCKER_DAY
/obj/effect/sunlight/proc/warn_daylight(danger_level =0, vampwarn = "", vassalwarn = "")
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
@@ -162,32 +143,6 @@
M.current.updatehealth()
SEND_SIGNAL(M.current, COMSIG_ADD_MOOD_EVENT, "vampsleep", /datum/mood_event/daylight_2)
-/obj/effect/sunlight/proc/day_end()
- for(var/datum/mind/M in SSticker.mode.bloodsuckers)
- if(!istype(M) || !istype(M.current))
- continue
- var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
- if(!istype(bloodsuckerdatum))
- continue
- // Reset Warnings
- bloodsuckerdatum.warn_sun_locker = FALSE
- bloodsuckerdatum.warn_sun_burn = FALSE
- // Remove Dawn Powers
- for(var/datum/action/bloodsucker/P in bloodsuckerdatum.powers)
- if(istype(P, /datum/action/bloodsucker/gohome))
- bloodsuckerdatum.powers -= P
- P.Remove(M.current)
-
-/obj/effect/sunlight/proc/vamps_rank_up()
- set waitfor = FALSE
- // Cycle through all vamp antags and check if they're inside a closet.
- for(var/datum/mind/M in SSticker.mode.bloodsuckers)
- if(!istype(M) || !istype(M.current))
- continue
- var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
- if(istype(bloodsuckerdatum))
- bloodsuckerdatum.RankUp() // Rank up! Must still be in a coffin to level!
-
/obj/effect/sunlight/proc/give_home_power()
// It's late...! Give the "Vanishing Act" gohome power to bloodsuckers.
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 9afc852f6b..16e1b45843 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -1626,7 +1626,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
for(var/modified_limb in modified_limbs)
if(modified_limbs[modified_limb][1] == LOADOUT_LIMB_PROSTHETIC && modified_limb != limb_type)
number_of_prosthetics += 1
- if(number_of_prosthetics > MAXIMUM_LOADOUT_PROSTHETICS)
+ if(number_of_prosthetics == MAXIMUM_LOADOUT_PROSTHETICS)
to_chat(user, "You can only have up to two prosthetic limbs!")
else
//save the actual prosthetic data
diff --git a/code/modules/events/supernova.dm b/code/modules/events/supernova.dm
new file mode 100644
index 0000000000..9766ab6212
--- /dev/null
+++ b/code/modules/events/supernova.dm
@@ -0,0 +1,67 @@
+/datum/round_event_control/supernova
+ name = "Supernova"
+ typepath = /datum/round_event/supernova
+ weight = 10
+ max_occurrences = 2
+ min_players = 2
+
+/datum/round_event/supernova
+ announceWhen = 40
+ startWhen = 1
+ endWhen = 300
+ var/power = 1
+ var/datum/sun/supernova
+ var/storm_count = 0
+
+/datum/round_event/supernova/setup()
+ announceWhen = rand(4, 60)
+ supernova = new
+ SSsun.suns += supernova
+ if(prob(50))
+ power = rand(5,100) / 100
+ else
+ power = rand(5,5000) / 100
+ supernova.azimuth = rand(0, 359)
+ supernova.power_mod = 0
+
+/datum/round_event/supernova/announce()
+ var/message = "Our tachyon-doppler array has detected a supernova in your vicinity. Peak flux from the supernova estimated to be [round(power,0.1)] times current solar flux. [power > 4 ? "Short burts of radiation may be possible, so please prepare accordingly." : ""]"
+ if(prob(power * 25))
+ priority_announce(message)
+ else
+ print_command_report(message)
+
+
+/datum/round_event/supernova/start()
+ supernova.power_mod = 0.00000002 * power
+ var/explosion_size = rand(1000000000, 999999999)
+ var/turf/epicenter = get_turf_in_angle(supernova.azimuth, SSmapping.get_station_center(), world.maxx / 2)
+ for(var/array in GLOB.doppler_arrays)
+ var/obj/machinery/doppler_array/A = array
+ A.sense_explosion(epicenter, explosion_size/2, explosion_size, 0, 107000000 / power, explosion_size/2, explosion_size, 0)
+ if(power > 1 && SSticker.mode.bloodsucker_sunlight?.time_til_cycle > 90)
+ var/obj/effect/sunlight/sucker_light = SSticker.mode.bloodsucker_sunlight
+ sucker_light.time_til_cycle = 90
+ sucker_light.warn_daylight(1,"A supernova will bombard the station with dangerous UV in [90 / 60] minutes. Prepare to seek cover in a coffin or closet.")
+ sucker_light.give_home_power()
+
+/datum/round_event/supernova/tick()
+ var/midpoint = (endWhen-startWhen)/2
+ switch(activeFor)
+ if(startWhen to midpoint)
+ supernova.power_mod = min(supernova.power_mod*1.2, power)
+ if(endWhen-10 to endWhen)
+ supernova.power_mod /= 4
+ if(prob(round(supernova.power_mod / 2)) && storm_count < 3 && !SSweather.get_weather_by_type(/datum/weather/rad_storm))
+ SSweather.run_weather(/datum/weather/rad_storm/supernova)
+ storm_count++
+
+/datum/round_event/supernova/end()
+ SSsun.suns -= supernova
+ qdel(supernova)
+
+/datum/weather/rad_storm/supernova
+ weather_duration_lower = 50
+ weather_duration_lower = 100
+ telegraph_duration = 100
+ radiation_intensity = 50
diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm
index c8188cae8a..331bad9bfe 100644
--- a/code/modules/jobs/job_types/paramedic.dm
+++ b/code/modules/jobs/job_types/paramedic.dm
@@ -4,7 +4,7 @@
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"
- total_positions = 3
+ total_positions = 2
spawn_positions = 2
supervisors = "the chief medical officer"
selection_color = "#74b5e0"
diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm
index b0055204f9..34b349b198 100644
--- a/code/modules/mining/machine_silo.dm
+++ b/code/modules/mining/machine_silo.dm
@@ -47,7 +47,7 @@ GLOBAL_LIST_EMPTY(silo_access_logs)
return ..()
-/obj/machinery/ore_silo/proc/remote_attackby(obj/machinery/M, mob/user, obj/item/stack/I)
+/obj/machinery/ore_silo/proc/remote_attackby(obj/machinery/M, mob/user, obj/item/stack/I, remote = null)
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
// stolen from /datum/component/material_container/proc/OnAttackBy
if(user.a_intent != INTENT_HELP)
@@ -63,7 +63,7 @@ GLOBAL_LIST_EMPTY(silo_access_logs)
return
// assumes unlimited space...
var/amount = I.amount
- materials.user_insert(I, user)
+ materials.user_insert(I, user, remote)
silo_log(M, "deposited", amount, "sheets", item_mats)
return TRUE
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 0baa78f0ea..3eaefebc56 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -334,7 +334,9 @@
I.moveToNullspace()
else
I.forceMove(newloc)
- I.dropped(src, silent)
+ on_item_dropped(I)
+ if(I.dropped(src) == ITEM_RELOCATED_BY_DROPPED)
+ return FALSE
return TRUE
//This is a SAFE proc. Use this instead of equip_to_slot()!
diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm
index 855c579926..92aa95bfb7 100644
--- a/code/modules/power/solar.dm
+++ b/code/modules/power/solar.dm
@@ -1,5 +1,4 @@
#define SOLAR_GEN_RATE 1500
-#define OCCLUSION_DISTANCE 20
/obj/machinery/power/solar
name = "solar panel"
@@ -14,8 +13,8 @@
integrity_failure = 0.33
var/id
- var/obscured = FALSE
- var/sunfrac = 0 //[0-1] measure of obscuration -- multipllier against power generation
+ var/list/obscured = list()
+ var/total_flux = 0 // multipllier against power generation -- measured by obscuration of all suns
var/azimuth_current = 0 //[0-360) degrees, which direction are we facing?
var/azimuth_target = 0 //same but what way we're going to face next time we turn
var/obj/machinery/power/solar_control/control
@@ -133,40 +132,28 @@
///trace towards sun to see if we're in shadow
/obj/machinery/power/solar/proc/occlusion_setup()
- obscured = TRUE
-
- var/distance = OCCLUSION_DISTANCE
- var/target_x = round(sin(SSsun.azimuth), 0.01)
- var/target_y = round(cos(SSsun.azimuth), 0.01)
- var/x_hit = x
- var/y_hit = y
- var/turf/hit
-
- for(var/run in 1 to distance)
- x_hit += target_x
- y_hit += target_y
- hit = locate(round(x_hit, 1), round(y_hit, 1), z)
- if(hit.opacity)
- return
- if(hit.x == 1 || hit.x == world.maxx || hit.y == 1 || hit.y == world.maxy) //edge of the map
- break
- obscured = FALSE
+ obscured = list()
+ for(var/S in SSsun.suns)
+ if(check_obscured(S))
+ obscured |= S
///calculates the fraction of the sunlight that the panel receives
/obj/machinery/power/solar/proc/update_solar_exposure()
needs_to_update_solar_exposure = FALSE
- sunfrac = 0
- if(obscured)
- return 0
-
- var/sun_azimuth = SSsun.azimuth
- if(azimuth_current == sun_azimuth) //just a quick optimization for the most frequent case
- . = 1
- else
- //dot product of sun and panel -- Lambert's Cosine Law
- . = cos(azimuth_current - sun_azimuth)
- . = clamp(round(., 0.01), 0, 1)
- sunfrac = .
+ total_flux = 0
+ for(var/S in SSsun.suns)
+ if(S in obscured)
+ continue
+ var/datum/sun/sun = S
+ var/sun_azimuth = sun.azimuth
+ var/cur_pow = 0
+ if(azimuth_current == sun_azimuth) //just a quick optimization for the most frequent case
+ cur_pow = sun.power_mod
+ else
+ //dot product of sun and panel -- Lambert's Cosine Law
+ cur_pow = cos(azimuth_current - sun_azimuth) * sun.power_mod
+ cur_pow = clamp(round(cur_pow, 0.01), 0, 1)
+ total_flux += cur_pow
/obj/machinery/power/solar/process()
if(stat & BROKEN)
@@ -177,10 +164,10 @@
update_turn()
if(needs_to_update_solar_exposure)
update_solar_exposure()
- if(sunfrac <= 0)
+ if(total_flux <= 0)
return
- var/sgen = SOLAR_GEN_RATE * sunfrac * efficiency
+ var/sgen = SOLAR_GEN_RATE * total_flux * efficiency
add_avail(sgen)
if(control)
control.gen += sgen
@@ -385,7 +372,7 @@
track = mode
if(mode == SOLAR_TRACK_AUTO)
if(connected_tracker)
- connected_tracker.sun_update(SSsun, SSsun.azimuth)
+ connected_tracker.sun_update(SSsun, SSsun.primary_sun, SSsun.suns)
else
track = SOLAR_TRACK_OFF
return TRUE
@@ -483,4 +470,3 @@ Congratulations, you should have a working solar array. If you are having troubl
"}
#undef SOLAR_GEN_RATE
-#undef OCCLUSION_DISTANCE
diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm
index 0627a55de0..86168979c2 100644
--- a/code/modules/power/tracker.dm
+++ b/code/modules/power/tracker.dm
@@ -41,10 +41,10 @@
control = null
///Tell the controller to turn the solar panels
-/obj/machinery/power/tracker/proc/sun_update(datum/source, azimuth)
- setDir(angle2dir(azimuth))
+/obj/machinery/power/tracker/proc/sun_update(datum/source, datum/sun/primary_sun, list/datum/sun/suns)
+ setDir(angle2dir(primary_sun.azimuth))
if(control && control.track == SOLAR_TRACK_AUTO)
- control.set_panels(azimuth)
+ control.set_panels(primary_sun.azimuth)
/obj/machinery/power/tracker/proc/Make(obj/item/solar_assembly/S)
if(!S)
diff --git a/code/modules/reagents/chemistry/recipes/drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm
index 8de4b1248f..cd55d00cf1 100644
--- a/code/modules/reagents/chemistry/recipes/drugs.dm
+++ b/code/modules/reagents/chemistry/recipes/drugs.dm
@@ -88,9 +88,9 @@
mix_message = "The mixture boils off a yellow, smelly vapor..."//Sulfur burns off, leaving the camphor
/datum/chemical_reaction/anaphroplus
- name = "pentacamphor"
+ name = "hexacamphor"
id = /datum/reagent/drug/anaphrodisiacplus
results = list(/datum/reagent/drug/anaphrodisiacplus = 1)
- required_reagents = list(/datum/reagent/drug/aphrodisiac = 5, /datum/reagent/acetone = 1)
- required_temp = 300
+ required_reagents = list(/datum/reagent/drug/anaphrodisiac = 6, /datum/reagent/acetone = 1)
+ required_temp = 400
mix_message = "The mixture thickens and heats up slighty..."
diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm
index 072fa54c6f..319b4a2199 100644
--- a/code/modules/research/machinery/_production.dm
+++ b/code/modules/research/machinery/_production.dm
@@ -30,7 +30,7 @@
stored_research = new
host_research = SSresearch.science_tech
update_research()
- materials = AddComponent(/datum/component/remote_materials, "lathe", mapload)
+ materials = AddComponent(/datum/component/remote_materials, "lathe", mapload, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
RefreshParts()
/obj/machinery/rnd/production/Destroy()
diff --git a/code/modules/research/machinery/circuit_imprinter.dm b/code/modules/research/machinery/circuit_imprinter.dm
index c1c386cb31..a8ce7305a3 100644
--- a/code/modules/research/machinery/circuit_imprinter.dm
+++ b/code/modules/research/machinery/circuit_imprinter.dm
@@ -22,3 +22,6 @@
/obj/machinery/rnd/production/circuit_imprinter/disconnect_console()
linked_console.linked_imprinter = null
..()
+
+/obj/machinery/rnd/production/circuit_imprinter/AfterMaterialInsert() //doesnt use have an animation like lathes do
+ return
diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm
index 2237284a64..f39f5b1584 100644
--- a/code/modules/research/rdmachines.dm
+++ b/code/modules/research/rdmachines.dm
@@ -94,13 +94,13 @@
..()
/obj/machinery/rnd/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted)
- var/stack_name
+ var/mat_name
if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
- stack_name = "bluespace"
+ mat_name = "bluespace"
use_power(MINERAL_MATERIAL_AMOUNT / 10)
else
- var/obj/item/stack/S = item_inserted
- stack_name = S.name
+ var/datum/material/M = id_inserted
+ mat_name = M.name
use_power(min(1000, (amount_inserted / 100)))
- add_overlay("protolathe_[stack_name]")
- addtimer(CALLBACK(src, /atom/proc/cut_overlay, "protolathe_[stack_name]"), 10)
+ add_overlay("protolathe_[mat_name]")
+ addtimer(CALLBACK(src, /atom/proc/cut_overlay, "protolathe_[mat_name]"), 10)
diff --git a/config/game_options.txt b/config/game_options.txt
index 7776d87d4e..b9d763e5b0 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -72,6 +72,14 @@ ALERT_DELTA Destruction of the station is imminent. All crew are instructed to o
## Uncomment to not send a roundstart intercept report. Gamemodes may override this.
#NO_INTERCEPT_REPORT
+## Comment to disable weighting modes by how chaotic recent mode rolls were.
+WEIGH_BY_RECENT_CHAOS
+
+## The weight adjustment will be proportional to this power relative to the "ideal" weight range.
+## e.g. if we have a weight range of 0-5, and an exponent of 1, 6 will be weighted 1/2, 7 1/3 etc.
+## if exponent is 2, it'll be 1/4, 1/9 etc.
+CHAOS_EXPONENT 1
+
## Probablities for game modes chosen in 'secret' and 'random' modes.
## Default probablity is 1, increase to make that mode more likely to be picked.
## Set to 0 to disable that mode.
@@ -154,6 +162,11 @@ FORCE_ANTAG_COUNT CLOCKWORK_CULT
#FORCE_ANTAG_COUNT WIZARD
#FORCE_ANTAG_COUNT MONKEY
+## A config for how much each game mode's chaos level is.
+## All of them have reasonable defaults, but this can be used to adjust them.
+## 0-9, where 0 is lowest chaos (should only be extended) and 9 is highest (wizard? nukies?)
+#CHAOS_LEVEL EXTENDED 0
+
## Uncomment these for overrides of the minimum / maximum number of players in a round type.
## If you set any of these occasionally check to see if you still need them as the modes
## will still be actively rebalanced around the SUGGESTED populations, not your overrides.
diff --git a/html/changelog.html b/html/changelog.html
index 5d6b9c63c8..68b5c86a75 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,22 @@
-->