mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
157 lines
6.4 KiB
Plaintext
157 lines
6.4 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)
|
|
for(var/datum/gm_action/action in available_actions)
|
|
action.gm = src
|
|
|
|
var/config_setup_delay = TRUE
|
|
spawn(0)
|
|
while(config_setup_delay)
|
|
if(config)
|
|
config_setup_delay = FALSE
|
|
if(config.enable_game_master)
|
|
suspended = FALSE
|
|
else
|
|
sleep(30 SECONDS)
|
|
|
|
/datum/game_master/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)
|
|
if(action.chaotic > danger)
|
|
continue // We skip dangerous events when bad stuff is already occuring.
|
|
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.")
|
|
run_action(choice)
|
|
|
|
/datum/game_master/proc/run_action(var/datum/gm_action/action)
|
|
action.set_up()
|
|
action.start()
|
|
action.announce()
|
|
if(action.chaotic)
|
|
danger += action.chaotic
|
|
if(action.length)
|
|
spawn(action.length)
|
|
action.end()
|
|
next_action = world.time + rand(15 MINUTES, 30 MINUTES)
|
|
last_department_used = action.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.")
|
|
|
|
|