From c76df7f37ad3166867ccd4d6b69c48333e5a67d0 Mon Sep 17 00:00:00 2001
From: Zephyr <12817816+ZephyrTFA@users.noreply.github.com>
Date: Wed, 31 Jan 2024 14:47:09 -0500
Subject: [PATCH] Station Goals are now handled by SSstation instead of a
global list (#81177)
## About The Pull Request
You can now get station goals in a slightly better way over using a
`locate() in` call on a global list.
The Meteor Satellite goal no longer stores a giant list of ALL OBJECTS
in view. And now correctly only counts turfs.
## Changelog
:cl:
fix: Meteor Satellites no longer erroneously count every piece of paper
as a protected turf.
fix: As a result the station goal is slightly more difficult
/:cl:
---------
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
---
code/__HELPERS/roundend.dm | 11 +++--
code/controllers/subsystem/dynamic/dynamic.dm | 12 +++---
.../subsystem/processing/station.dm | 43 ++++++++++++++++++-
code/controllers/subsystem/ticker.dm | 1 -
.../machinery/satellite/satellite_control.dm | 7 ++-
code/modules/admin/topic.dm | 1 -
code/modules/admin/verbs/debug.dm | 4 +-
code/modules/station_goals/dna_vault.dm | 4 +-
code/modules/station_goals/generate_goals.dm | 12 ------
code/modules/station_goals/meteor_shield.dm | 21 ++++++---
code/modules/station_goals/station_goal.dm | 20 +++++----
11 files changed, 90 insertions(+), 46 deletions(-)
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 93ba8bb2f1c..a3734cfd7a6 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -491,11 +491,14 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
return ""
/datum/controller/subsystem/ticker/proc/goal_report()
+ var/list/goals = SSstation.get_station_goals()
+ if(!length(goals))
+ return null
+
var/list/parts = list()
- if(GLOB.station_goals.len)
- for(var/datum/station_goal/goal as anything in GLOB.station_goals)
- parts += goal.get_result()
- return "
"
+ for(var/datum/station_goal/goal as anything in SSstation.get_station_goals())
+ parts += goal.get_result()
+ return ""
///Generate a report for how much money is on station, as well as the richest crewmember on the station.
/datum/controller/subsystem/ticker/proc/market_report()
diff --git a/code/controllers/subsystem/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm
index 5024194412d..f7c37cb41e3 100644
--- a/code/controllers/subsystem/dynamic/dynamic.dm
+++ b/code/controllers/subsystem/dynamic/dynamic.dm
@@ -329,16 +329,16 @@ SUBSYSTEM_DEF(dynamic)
if(ruleset.weight <= 0 || ruleset.cost <= 0)
continue
min_threat = min(ruleset.cost, min_threat)
+
var/greenshift = GLOB.dynamic_forced_extended || (threat_level < min_threat && shown_threat < min_threat) //if both shown and real threat are below any ruleset, its extended time
+ SSstation.generate_station_goals(greenshift ? INFINITY : CONFIG_GET(number/station_goal_budget))
- generate_station_goals(greenshift ? INFINITY : CONFIG_GET(number/station_goal_budget))
-
- if (GLOB.station_goals.len > 0)
- var/list/texts = list("
Special Orders for [station_name()]:
")
- for(var/datum/station_goal/station_goal as anything in GLOB.station_goals)
+ var/list/datum/station_goal/goals = SSstation.get_station_goals()
+ if(length(goals))
+ var/list/texts = list("
Special Orders for [station_name()]:
")
+ for(var/datum/station_goal/station_goal as anything in goals)
station_goal.on_report()
texts += station_goal.get_report()
-
. += texts.Join("
")
var/list/trait_list_strings = list()
diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm
index 99af6d867f1..34c4a00f81d 100644
--- a/code/controllers/subsystem/processing/station.dm
+++ b/code/controllers/subsystem/processing/station.dm
@@ -16,8 +16,10 @@ PROCESSING_SUBSYSTEM_DEF(station)
///A list of trait roles that should never be able to roll antag
var/list/antag_restricted_roles = list()
-/datum/controller/subsystem/processing/station/Initialize()
+ /// Assosciative list of station goal type -> goal instance
+ var/list/datum/station_goal/goals_by_type = list()
+/datum/controller/subsystem/processing/station/Initialize()
//If doing unit tests we don't do none of that trait shit ya know?
// Autowiki also wants consistent outputs, for example making sure the vending machine page always reports the normal products
#if !defined(UNIT_TESTS) && !defined(AUTOWIKI)
@@ -30,6 +32,45 @@ PROCESSING_SUBSYSTEM_DEF(station)
return SS_INIT_SUCCESS
+/datum/controller/subsystem/processing/station/Recover()
+ station_traits = SSstation.station_traits
+ selectable_traits_by_types = SSstation.selectable_traits_by_types
+ announcer = SSstation.announcer
+ antag_protected_roles = SSstation.antag_protected_roles
+ antag_restricted_roles = SSstation.antag_restricted_roles
+ goals_by_type = SSstation.goals_by_type
+ ..()
+
+/// This gets called by SSdynamic during initial gamemode setup.
+/// This is done because for a greenshift we want all goals to be generated
+/datum/controller/subsystem/processing/station/proc/generate_station_goals(goal_budget)
+ var/list/possible = subtypesof(/datum/station_goal)
+
+ var/goal_weights = 0
+ var/chosen_goals = list()
+ var/is_planetary = SSmapping.is_planetary()
+ while(possible.len && goal_weights < goal_budget)
+ var/datum/station_goal/picked = pick_n_take(possible)
+ if(picked::requires_space && is_planetary)
+ continue
+
+ goal_weights += initial(picked.weight)
+ chosen_goals += picked
+
+ for(var/chosen in chosen_goals)
+ new chosen()
+
+/// Returns all station goals that are currently active
+/datum/controller/subsystem/processing/station/proc/get_station_goals()
+ var/list/goals = list()
+ for(var/goal_type in goals_by_type)
+ goals += goals_by_type[goal_type]
+ return goals
+
+/// Returns a specific station goal by type
+/datum/controller/subsystem/processing/station/proc/get_station_goal(goal_type)
+ return goals_by_type[goal_type]
+
///Rolls for the amount of traits and adds them to the traits list
/datum/controller/subsystem/processing/station/proc/SetupTraits()
if (CONFIG_GET(flag/forbid_station_traits))
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 9d2fd01732e..e3d9be937e2 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -226,7 +226,6 @@ SUBSYSTEM_DEF(ticker)
return TRUE
return FALSE
-
/datum/controller/subsystem/ticker/proc/setup()
to_chat(world, span_boldannounce("Starting game..."))
var/init_start = world.timeofday
diff --git a/code/game/machinery/satellite/satellite_control.dm b/code/game/machinery/satellite/satellite_control.dm
index e8482fe91df..c0875d9e26a 100644
--- a/code/game/machinery/satellite/satellite_control.dm
+++ b/code/game/machinery/satellite/satellite_control.dm
@@ -44,10 +44,9 @@
))
data["notice"] = notice
-
- var/datum/station_goal/station_shield/goal = locate() in GLOB.station_goals
- if(goal)
- data["meteor_shield"] = 1
+ var/datum/station_goal/station_shield/goal = SSstation.get_station_goal(/datum/station_goal/station_shield)
+ if(!isnull(goal))
+ data["meteor_shield"] = TRUE
data["meteor_shield_coverage"] = goal.get_coverage()
data["meteor_shield_coverage_max"] = goal.coverage_goal
return data
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 6196e2c94b6..9c826dc216c 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1350,7 +1350,6 @@
return
G.report_message = description
message_admins("[key_name(usr)] created \"[G.name]\" station goal.")
- GLOB.station_goals += G
modify_goals()
else if(href_list["change_lag_switch"])
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 260bfb16bf3..126d8756762 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -546,8 +546,8 @@
/datum/admins/proc/modify_goals()
var/dat = ""
- for(var/datum/station_goal/S in GLOB.station_goals)
- dat += "[S.name] - Announce | Remove
"
+ for(var/datum/station_goal/goal as anything in SSstation.get_station_goals())
+ dat += "[goal.name] - Announce | Remove
"
dat += "
Add New Goal"
usr << browse(dat, "window=goals;size=400x400")
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index 83fb1f0ea7f..5c146758d12 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -101,8 +101,8 @@
F.parent = src
fillers += F
- var/datum/station_goal/dna_vault/dna_vault_goal = locate() in GLOB.station_goals
- if (!isnull(dna_vault_goal))
+ var/datum/station_goal/dna_vault/dna_vault_goal = SSstation.get_station_goal(/datum/station_goal/dna_vault)
+ if(!isnull(dna_vault_goal))
animals_max = dna_vault_goal.animal_count
plants_max = dna_vault_goal.plant_count
dna_max = dna_vault_goal.human_count
diff --git a/code/modules/station_goals/generate_goals.dm b/code/modules/station_goals/generate_goals.dm
index 102212c1212..b37543d6de8 100644
--- a/code/modules/station_goals/generate_goals.dm
+++ b/code/modules/station_goals/generate_goals.dm
@@ -1,13 +1 @@
/// Creates the initial station goals.
-/proc/generate_station_goals(goal_budget)
- var/list/possible = subtypesof(/datum/station_goal)
- // Remove all goals that require space if space is not present
- if(SSmapping.is_planetary())
- for(var/datum/station_goal/goal as anything in possible)
- if(initial(goal.requires_space))
- possible -= goal
- var/goal_weights = 0
- while(possible.len && goal_weights < goal_budget)
- var/datum/station_goal/picked = pick_n_take(possible)
- goal_weights += initial(picked.weight)
- GLOB.station_goals += new picked
diff --git a/code/modules/station_goals/meteor_shield.dm b/code/modules/station_goals/meteor_shield.dm
index 3b87ef74578..84a61395a4b 100644
--- a/code/modules/station_goals/meteor_shield.dm
+++ b/code/modules/station_goals/meteor_shield.dm
@@ -14,8 +14,9 @@
// Satellites be actived to generate a shield that will block unorganic matter from passing it.
/datum/station_goal/station_shield
name = "Station Shield"
- var/coverage_goal = 500
requires_space = TRUE
+ var/coverage_goal = 500
+ VAR_PRIVATE/cached_coverage_length
/datum/station_goal/station_shield/get_report()
return list(
@@ -37,17 +38,24 @@
/datum/station_goal/station_shield/check_completion()
if(..())
return TRUE
- if(get_coverage() >= coverage_goal)
+ update_coverage()
+ if(cached_coverage_length >= coverage_goal)
return TRUE
return FALSE
-/datum/station_goal/proc/get_coverage()
+/datum/station_goal/station_shield/proc/get_coverage()
+ return cached_coverage_length
+
+/// Gets the coverage of all active meteor shield satellites
+/// Can be expensive, ensure you need this before calling it
+/datum/station_goal/station_shield/proc/update_coverage()
var/list/coverage = list()
for(var/obj/machinery/satellite/meteor_shield/shield_satt as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/satellite/meteor_shield))
if(!shield_satt.active || !is_station_level(shield_satt.z))
continue
- coverage |= view(shield_satt.kill_range, shield_satt)
- return coverage.len
+ for(var/turf/covered in view(shield_satt.kill_range, shield_satt))
+ coverage |= covered
+ cached_coverage_length = length(coverage)
/obj/machinery/satellite/meteor_shield
name = "\improper Meteor Shield Satellite"
@@ -109,6 +117,9 @@
if(obj_flags & EMAGGED)
update_emagged_meteor_sat(user)
+ var/datum/station_goal/station_shield/goal = SSstation.get_station_goal(/datum/station_goal/station_shield)
+ goal?.update_coverage()
+
/obj/machinery/satellite/meteor_shield/Destroy()
. = ..()
if(obj_flags & EMAGGED)
diff --git a/code/modules/station_goals/station_goal.dm b/code/modules/station_goals/station_goal.dm
index 30ed0b78b6c..d7fd5e7f752 100644
--- a/code/modules/station_goals/station_goal.dm
+++ b/code/modules/station_goals/station_goal.dm
@@ -1,6 +1,3 @@
-/// List of available station goals for the crew to be working on
-GLOBAL_LIST_EMPTY_TYPED(station_goals, /datum/station_goal)
-
/datum/station_goal
var/name = "Generic Goal"
var/weight = 1 //In case of multiple goals later.
@@ -30,13 +27,8 @@ GLOBAL_LIST_EMPTY_TYPED(station_goals, /datum/station_goal)
else
return "[name] : [span_redtext("Failed!")]"
-/datum/station_goal/Destroy()
- GLOB.station_goals -= src
- return ..()
-
/datum/station_goal/Topic(href, href_list)
..()
-
if(!check_rights(R_ADMIN) || !usr.client.holder.CheckAdminHref(href, href_list))
return
@@ -45,3 +37,15 @@ GLOBAL_LIST_EMPTY_TYPED(station_goals, /datum/station_goal)
send_report()
else if(href_list["remove"])
qdel(src)
+
+/datum/station_goal/New()
+ if(type in SSstation.goals_by_type)
+ stack_trace("Creating a new station_goal of type [type] when one already exists in SSstation.goals_by_type this is not supported anywhere. I trust you tho")
+ else
+ SSstation.goals_by_type[type] = src
+ return ..()
+
+/datum/station_goal/Destroy(force)
+ if(SSstation.goals_by_type[type] == src)
+ SSstation.goals_by_type -= type
+ return ..()