mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-26 10:03:45 +00:00
Separates the 'count and assess everything' stuff to it's own datum, called the metric datum, which I plan to add on to in the future to make counting and metrics easier. Makes decision process a bit more weight-based, will probably continue tweaking later. Makes the admin debug UI have links to change settings easily. Adds replacement for grid check event, which works similar to the old one, but is now based on a physical machine in the game world, that Engineering can hack to make the event end faster, if so desired. Note that the machine is not mapped in, and won't be mapped in until the event system is ready for launch. Adds grid_check variables to SMESes and APCs to make them stop doing work without draining the battery. Grid checks in the new system are caused by a "power spike" which originates from the engine and will cause bad things, should no grid checker machine be connected to the power-net. These power spikes occur when the GM decides that a grid check is a good event to have. The grid checker can be built and deconstructed using the standard machine construction methods.
137 lines
5.8 KiB
Plaintext
137 lines
5.8 KiB
Plaintext
// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the
|
|
// previous ones, checking various things like player count, department size and composition, individual player activity,
|
|
// individual player (IC) skill, and such, in order to try to choose the best actions to take in order to add spice or variety to
|
|
// the round.
|
|
|
|
/datum/game_master
|
|
var/suspended = TRUE // If true, it will not do anything.
|
|
var/ignore_time_restrictions = FALSE// Useful for debugging without needing to wait 20 minutes each time.
|
|
var/list/available_actions = list() // A list of 'actions' that the GM has access to, to spice up a round, such as events.
|
|
var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back.
|
|
var/staleness = -20 // Determines liklihood of the GM doing something, increases over time.
|
|
var/danger_modifier = 1 // Multiplier for how much 'danger' is accumulated.
|
|
var/staleness_modifier = 1 // Ditto. Higher numbers generally result in more events occuring in a round.
|
|
var/ticks_completed = 0 // Counts amount of ticks completed. Note that this ticks once a minute.
|
|
var/next_action = 0 // Minimum amount of time of nothingness until the GM can pick something again.
|
|
var/last_department_used = null // If an event was done for a specific department, it is written here, so it doesn't do it again.
|
|
|
|
|
|
/datum/game_master/New()
|
|
..()
|
|
available_actions = init_subtypes(/datum/gm_action)
|
|
|
|
/datum/game_master/proc/process()
|
|
if(ticker && ticker.current_state == GAME_STATE_PLAYING && !suspended)
|
|
adjust_staleness(1)
|
|
adjust_danger(-1)
|
|
ticks_completed++
|
|
|
|
var/global_afk = metric.assess_all_living_mobs()
|
|
global_afk -= 100
|
|
global_afk = abs(global_afk)
|
|
global_afk = round(global_afk / 100, 0.1)
|
|
adjust_staleness(global_afk) // Staleness increases faster if more people are less active.
|
|
|
|
if(world.time < next_action && prob(staleness * 2) )
|
|
log_debug("Game Master going to start something.")
|
|
start_action()
|
|
|
|
// This is run before committing to an action/event.
|
|
/datum/game_master/proc/pre_action_checks()
|
|
if(!ticker || ticker.current_state != GAME_STATE_PLAYING)
|
|
log_debug("Game Master unable to start event: Ticker is nonexistant, or the game is not ongoing.")
|
|
return FALSE
|
|
if(suspended)
|
|
return FALSE
|
|
if(ignore_time_restrictions)
|
|
return TRUE
|
|
// Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round.
|
|
var/mills = round_duration_in_ticks
|
|
var/mins = round((mills % 36000) / 600)
|
|
var/hours = round(mills / 36000)
|
|
|
|
if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round.
|
|
log_debug("Game Master unable to start event: It is too early.")
|
|
return FALSE
|
|
if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well.
|
|
log_debug("Game Master unable to start event: It is too late.")
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/datum/game_master/proc/start_action()
|
|
if(!pre_action_checks()) // Make sure we're not doing last minute events, or early events.
|
|
return
|
|
log_debug("Game Master now starting action decision.")
|
|
var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used))
|
|
var/list/best_actions = decide_best_action(most_active_departments)
|
|
|
|
if(best_actions && best_actions.len)
|
|
var/list/weighted_actions = list()
|
|
for(var/datum/gm_action/action in best_actions)
|
|
weighted_actions[action] = action.get_weight()
|
|
|
|
var/datum/gm_action/choice = pickweight(weighted_actions)
|
|
if(choice)
|
|
log_debug("[choice.name] was chosen by the Game Master, and is now being ran.")
|
|
choice.set_up()
|
|
choice.start()
|
|
next_action = world.time + rand(15 MINUTES, 30 MINUTES)
|
|
last_department_used = choice.departments[1]
|
|
|
|
|
|
|
|
|
|
/datum/game_master/proc/decide_best_action(var/list/most_active_departments)
|
|
if(!most_active_departments.len) // Server's empty?
|
|
log_debug("Game Master failed to find any active departments.")
|
|
return list()
|
|
|
|
var/list/best_actions = list() // List of actions which involve the most active departments.
|
|
if(most_active_departments.len >= 2)
|
|
for(var/datum/gm_action/action in available_actions)
|
|
if(!action.enabled)
|
|
continue
|
|
// Try to incorporate an action with the top two departments first.
|
|
if(most_active_departments[1] in action.departments && most_active_departments[2] in action.departments)
|
|
best_actions.Add(action)
|
|
log_debug("[action.name] is being considered because both most active departments are involved.")
|
|
|
|
if(best_actions.len) // We found something for those two, let's do it.
|
|
return best_actions
|
|
|
|
// Otherwise we probably couldn't find something for the second highest group, so let's ignore them.
|
|
for(var/datum/gm_action/action in available_actions)
|
|
if(!action.enabled)
|
|
continue
|
|
if(most_active_departments[1] in action.departments)
|
|
best_actions.Add(action)
|
|
log_debug("[action.name] is being considered because the most active department is involved.")
|
|
|
|
if(best_actions.len) // Found something for the one guy.
|
|
return best_actions
|
|
|
|
// At this point we should expand our horizons.
|
|
for(var/datum/gm_action/action in available_actions)
|
|
if(!action.enabled)
|
|
continue
|
|
if(ROLE_EVERYONE in action.departments)
|
|
best_actions.Add(action)
|
|
log_debug("[action.name] is being considered because it involves everyone.")
|
|
|
|
if(best_actions.len) // Finally, perhaps?
|
|
return best_actions
|
|
|
|
// Just give a random event if for some reason it still can't make up its mind.
|
|
for(var/datum/gm_action/action in available_actions)
|
|
if(!action.enabled)
|
|
continue
|
|
best_actions.Add(action)
|
|
log_debug("[action.name] is being considered because everything else failed.")
|
|
|
|
if(best_actions.len) // Finally, perhaps?
|
|
return best_actions
|
|
else
|
|
log_debug("Game Master failed to find a suitable event, something very wrong is going on.")
|
|
|
|
|