diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 13e6c5772a..981007eb8b 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -24,6 +24,10 @@ // signals from globally accessible objects /// from SSsun when the sun changes position : (primary_sun, suns) #define COMSIG_SUN_MOVED "sun_moved" + +/// from SSactivity for things that add threat but aren't "global" (e.g. phylacteries) +#define COMSIG_THREAT_CALC "threat_calculation" + ////////////////////////////////////////////////////////////////// // /datum signals diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 9c67a6b36c..deb578464f 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -152,6 +152,7 @@ // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) #define FIRE_PRIORITY_VORE 5 +#define FIRE_PRIORITY_ACTIVITY 10 #define FIRE_PRIORITY_IDLE_NPC 10 #define FIRE_PRIORITY_SERVER_MAINT 10 #define FIRE_PRIORITY_RESEARCH 10 diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index afd8a7c223..75ce77302b 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -386,12 +386,15 @@ //ignore this comment, it fixes the broken sytax parsing caused by the " above else parts += "[FOURSPACES]Nobody died this shift!" + var/avg_threat = SSactivity.get_average_threat() + var/max_threat = SSactivity.get_max_threat() + parts += "[FOURSPACES]Threat at round end: [SSactivity.current_threat]" + parts += "[FOURSPACES]Average threat: [avg_threat]" + parts += "[FOURSPACES]Max threat: [max_threat]" if(istype(SSticker.mode, /datum/game_mode/dynamic)) var/datum/game_mode/dynamic/mode = SSticker.mode mode.update_playercounts() // ? - parts += "[FOURSPACES]Threat level: [mode.threat_level]" - parts += "[FOURSPACES]Threat left: [mode.threat]" - parts += "[FOURSPACES]Average threat: [mode.threat_average]" + parts += "[FOURSPACES]Target threat: [mode.threat_level]" parts += "[FOURSPACES]Executed rules:" for(var/datum/dynamic_ruleset/rule in mode.executed_rules) parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost + rule.scaled_times * rule.scaling_cost] threat" @@ -400,8 +403,10 @@ parts += "[FOURSPACES][FOURSPACES][str]" for(var/entry in mode.threat_tallies) parts += "[FOURSPACES][FOURSPACES][entry] added [mode.threat_tallies[entry]]" - SSblackbox.record_feedback("tally","dynamic_threat",mode.threat_level,"Final threat level") - SSblackbox.record_feedback("tally","dynamic_threat",mode.threat,"Final Threat") + SSblackbox.record_feedback("tally","threat",mode.threat_level,"Target threat") + SSblackbox.record_feedback("tally","threat",SSactivity.current_threat,"Final Threat") + SSblackbox.record_feedback("tally","threat",avg_threat,"Average Threat") + SSblackbox.record_feedback("tally","threat",max_threat,"Max Threat") return parts.Join("
") /client/proc/roundend_report_file() diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index 5d7de05598..6a5a4c4610 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -404,9 +404,9 @@ Example config: if(!Get(/datum/config_entry/flag/no_storyteller_threat_removal)) var/min_chaos = (probabilities in storyteller_min_chaos) ? storyteller_min_chaos[config_tag] : initial(S.min_chaos) var/max_chaos = (probabilities in storyteller_max_chaos) ? storyteller_max_chaos[config_tag] : initial(S.max_chaos) - if(SSpersistence.average_dynamic_threat < min_chaos) + if(SSpersistence.average_threat + 50 < min_chaos) continue - if(SSpersistence.average_dynamic_threat > max_chaos) + if(SSpersistence.average_threat + 50 > max_chaos) continue if(SSpersistence.saved_storytellers.len == repeated_mode_adjust.len) var/name = initial(S.name) diff --git a/code/controllers/subsystem/activity.dm b/code/controllers/subsystem/activity.dm new file mode 100644 index 0000000000..9c7492f83f --- /dev/null +++ b/code/controllers/subsystem/activity.dm @@ -0,0 +1,75 @@ +SUBSYSTEM_DEF(activity) + name = "Activity tracking" + flags = SS_BACKGROUND | SS_NO_TICK_CHECK + priority = FIRE_PRIORITY_ACTIVITY + wait = 1 MINUTES + var/list/deferred_threats = list() + var/current_threat = 0 + var/list/threat_history = list() + var/list/threats = list() + +/datum/controller/subsystem/activity/Initialize(timeofday) + RegisterSignal(SSdcs,COMSIG_GLOB_EXPLOSION,.proc/on_explosion) + RegisterSignal(SSdcs,COMSIG_GLOB_MOB_DEATH,.proc/on_death) + +/datum/controller/subsystem/activity/fire(resumed = 0) + calculate_threat() + +/datum/controller/subsystem/activity/proc/calculate_threat() + threats = deferred_threats.Copy() + deferred_threats.Cut() + threats["antagonists"] = 0 + for(var/datum/antagonist/A in GLOB.antagonists) + if(A?.owner?.current && A.owner.current.stat != DEAD) + threats["antagonists"] += A.threat() + threats["events"] = 0 + for(var/r in SSevents.running) + var/datum/round_event/R = r + threats["events"] += R.threat() + threats["players"] = 0 + SEND_SIGNAL(src, COMSIG_THREAT_CALC, threats) + for(var/m in GLOB.player_list) + var/mob/M = m + if (M?.mind?.assigned_role && M.stat != DEAD) + var/datum/job/J = SSjob.GetJob(M.mind.assigned_role) + if(J) + if(length(M.mind.antag_datums)) + threats["players"] += J.GetThreat() + else + threats["players"] -= J.GetThreat() + else if(M?.stat == DEAD && !M.voluntary_ghosted) + threats["dead_players"] += 1 + current_threat = 0 + for(var/threat_type in threats) + current_threat += threats[threat_type] + threat_history += "[world.time]" + threat_history["[world.time]"] = current_threat + +/datum/controller/subsystem/activity/proc/get_average_threat() + if(!length(threat_history)) + return 0 + var/total_weight = 0 + var/total_amt = 0 + for(var/i in 1 to threat_history.len-1) + var/weight = (text2num(threat_history[i+1])-text2num(threat_history[i])) + total_weight += weight + total_amt += weight * (threat_history[threat_history[i]]) + return round(total_amt / total_weight,0.1) + +/datum/controller/subsystem/activity/proc/get_max_threat() + . = 0 + for(var/threat in threat_history) + . = max(threat_history[threat], .) + +/datum/controller/subsystem/activity/proc/on_explosion(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) + if(!("explosions" in deferred_threats)) + deferred_threats["explosions"] = 0 + var/area/A = get_area(epicenter) + if(is_station_level(epicenter.z) && A.blob_allowed && !istype(A, /area/asteroid)) + deferred_threats["explosions"] += devastation_range**2 + heavy_impact_range**2 / 4 + light_impact_range**2 / 8 // 75 for a maxcap + +/datum/controller/subsystem/activity/proc/on_death(mob/M, gibbed) + if(!("crew_deaths" in deferred_threats)) + deferred_threats["crew_deaths"] = 0 + if(M?.mind && SSjob.GetJob(M.mind.assigned_role)) + deferred_threats["crew_deaths"] += 1 diff --git a/code/controllers/subsystem/persistence/recent_votes_etc.dm b/code/controllers/subsystem/persistence/recent_votes_etc.dm index 87f1ec0d4f..45b866b1c6 100644 --- a/code/controllers/subsystem/persistence/recent_votes_etc.dm +++ b/code/controllers/subsystem/persistence/recent_votes_etc.dm @@ -6,7 +6,7 @@ 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 + var/average_threat = 50 var/list/saved_maps /datum/controller/subsystem/persistence/SaveServerPersistence() @@ -38,9 +38,10 @@ saved_chaos[3] = saved_chaos[2] saved_chaos[2] = saved_chaos[1] saved_chaos[1] = SSticker.mode.get_chaos() + average_threat = (SSactivity.get_average_threat() + average_threat) / 2 json_file = file("data/RecentChaos.json") file_data = list() - file_data["data"] = saved_chaos + file_data["data"] = saved_chaos + average_threat fdel(json_file) WRITE_FILE(json_file, json_encode(file_data)) @@ -49,10 +50,9 @@ saved_storytellers[3] = saved_storytellers[2] saved_storytellers[2] = saved_storytellers[1] saved_storytellers[1] = mode.storyteller.name - average_dynamic_threat = (mode.max_threat + average_dynamic_threat) / 2 var/json_file = file("data/RecentStorytellers.json") var/list/file_data = list() - file_data["data"] = saved_storytellers + average_dynamic_threat + file_data["data"] = saved_storytellers fdel(json_file) WRITE_FILE(json_file, json_encode(file_data)) @@ -94,6 +94,9 @@ if(!json) return saved_chaos = json["data"] + if(saved_chaos.len > 3) + average_threat = saved_chaos[4] + saved_chaos.len = 3 /datum/controller/subsystem/persistence/proc/LoadRecentRulesets() var/json_file = file("data/RecentRulesets.json") @@ -112,9 +115,6 @@ if(!json) return saved_storytellers = json["data"] - if(saved_storytellers.len > 3) - average_dynamic_threat = saved_storytellers[4] - saved_storytellers.len = 3 /datum/controller/subsystem/persistence/proc/LoadRecentMaps() var/json_file = file("data/RecentMaps.json") diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm index 9a29158b33..8cdfaa4308 100644 --- a/code/datums/explosion.dm +++ b/code/datums/explosion.dm @@ -317,6 +317,8 @@ GLOBAL_LIST_EMPTY(explosions) var/took = (REALTIMEOFDAY - started_at) / 10 + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPLOSION,epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) + //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare if(GLOB.Debug2) log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 6a16b62643..55f91ba3d3 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -58,14 +58,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null) var/threat_level = 0 /// The current antag threat. Recalculated every time a ruletype starts or ends. var/threat = 0 - /// Threat average over the course of the round, for endgame logs. - var/threat_average = 0 - /// Number of times threat average has been calculated, for calculating above. - var/threat_average_weight = 0 - /// Last time a threat average sample was taken. Used for weighting the rolling average. - var/last_threat_sample_time = 0 - /// Maximum threat recorded so far, for cross-round chaos adjustment. - var/max_threat = 0 /// Things that cause a rolling threat adjustment to be displayed at roundend. var/list/threat_tallies = list() /// Running information about the threat. Can store text or datum entries. @@ -745,17 +737,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null) continue if(!M.voluntary_ghosted) current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow) - threat = storyteller.calculate_threat() + added_threat - max_threat = max(max_threat,threat) - if(threat_average_weight) - var/cur_sample_weight = world.time - last_threat_sample_time - threat_average = ((threat_average * threat_average_weight) + (threat * cur_sample_weight)) / (threat_average_weight + cur_sample_weight) - threat_average_weight += cur_sample_weight - last_threat_sample_time = world.time - else - threat_average = threat - threat_average_weight++ - last_threat_sample_time = world.time + threat = (SSactivity.current_threat * 0.6 + SSactivity.get_max_threat() * 0.2 + SSactivity.get_average_threat() * 0.2) + added_threat /// Removes type from the list /datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type) diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm index 03aa9d174a..927ab7796b 100644 --- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm +++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm @@ -45,27 +45,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 -/datum/dynamic_storyteller/proc/calculate_threat() - var/threat = 0 - for(var/datum/antagonist/A in GLOB.antagonists) - if(A?.owner?.current && A.owner.current.stat != DEAD) - threat += A.threat() - for(var/r in SSevents.running) - var/datum/round_event/R = r - threat += R.threat() - for(var/obj/item/phylactery/P in GLOB.poi_list) - threat += 25 // can't be giving them too much of a break - for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS]) - if (M?.mind?.assigned_role && M.stat != DEAD) - var/datum/job/J = SSjob.GetJob(M.mind.assigned_role) - if(J) - if(length(M.mind.antag_datums)) - threat += J.GetThreat() - else - threat -= J.GetThreat() - threat += (mode.current_players[CURRENT_DEAD_PLAYERS].len)*dead_player_weight - return round(threat,0.1) - /datum/dynamic_storyteller/proc/do_process() return @@ -95,7 +74,7 @@ Property weights are added to the config weight of the ruleset. They are: if(voters) GLOB.dynamic_curve_centre += (mean/voters) if(flags & USE_PREV_ROUND_WEIGHTS) - GLOB.dynamic_curve_centre += (50 - SSpersistence.average_dynamic_threat) / 10 + GLOB.dynamic_curve_centre += (SSpersistence.average_threat) / 10 GLOB.dynamic_forced_threat_level = forced_threat_level /datum/dynamic_storyteller/proc/get_midround_cooldown() diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 5be3e2a3a5..f2f8c20776 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -409,7 +409,10 @@ visible_message("\The [src] hums and hisses as it moves [mob_occupant.real_name] into storage.") // Ghost and delete the mob. - if(!mob_occupant.get_ghost(1)) + var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE) + if(G) + G.voluntary_ghosted = TRUE + else mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE) QDEL_NULL(occupant) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index be9cd0aabb..5fd52975e0 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -87,7 +87,7 @@ addtimer(CALLBACK(src, .proc/med_hud_set_status), (DEFIB_TIME_LIMIT * 10) + 1) stop_pulling() - var/signal = SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed) + var/signal = SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed) | SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MOB_DEATH, src, gibbed) var/turf/T = get_turf(src) if(mind && mind.name && mind.active && !istype(T.loc, /area/ctf) && !(signal & COMPONENT_BLOCK_DEATH_BROADCAST)) diff --git a/code/modules/spells/spell_types/lichdom.dm b/code/modules/spells/spell_types/lichdom.dm index 3d54c7130c..fbe7a0ea40 100644 --- a/code/modules/spells/spell_types/lichdom.dm +++ b/code/modules/spells/spell_types/lichdom.dm @@ -93,12 +93,14 @@ active_phylacteries++ GLOB.poi_list |= src START_PROCESSING(SSobj, src) + RegisterSignal(SSactivity, COMSIG_THREAT_CALC, .proc/get_threat) set_light(lon_range) if(initial(SSticker.mode.round_ends_with_antag_death)) SSticker.mode.round_ends_with_antag_death = FALSE /obj/item/phylactery/Destroy(force=FALSE) STOP_PROCESSING(SSobj, src) + UnregisterSignal(SSactivity, COMSIG_THREAT_CALC) active_phylacteries-- GLOB.poi_list -= src if(!active_phylacteries) @@ -113,6 +115,12 @@ if(!mind.current || (mind.current && mind.current.stat == DEAD)) addtimer(CALLBACK(src, .proc/rise), respawn_time, TIMER_UNIQUE) +/obj/item/phylactery/proc/get_threat(list/threat_list) + if(mind?.current?.stat == DEAD) + if(!("phylactery" in threat_list)) + threat_list["phylactery"] = 0 + threat_list["phylactery"] += 25 + /obj/item/phylactery/proc/rise() if(mind.current && mind.current.stat != DEAD) return "[mind] already has a living body: [mind.current]" diff --git a/tgstation.dme b/tgstation.dme index 03410ba0da..db49b62864 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -303,6 +303,7 @@ #include "code\controllers\configuration\entries\stamina_combat.dm" #include "code\controllers\subsystem\achievements.dm" #include "code\controllers\subsystem\acid.dm" +#include "code\controllers\subsystem\activity.dm" #include "code\controllers\subsystem\adjacent_air.dm" #include "code\controllers\subsystem\air.dm" #include "code\controllers\subsystem\air_turfs.dm"