mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-13 03:33:21 +00:00
Merge things
This commit is contained in:
@@ -167,3 +167,15 @@
|
|||||||
#define ANTAG_HIDDEN "Hidden"
|
#define ANTAG_HIDDEN "Hidden"
|
||||||
#define ANTAG_SHARED "Shared"
|
#define ANTAG_SHARED "Shared"
|
||||||
#define ANTAG_KNOWN "Known"
|
#define ANTAG_KNOWN "Known"
|
||||||
|
|
||||||
|
// Job groups
|
||||||
|
#define ROLE_COMMAND "command"
|
||||||
|
#define ROLE_SECURITY "security"
|
||||||
|
#define ROLE_ENGINEERING "engineering"
|
||||||
|
#define ROLE_MEDICAL "medical"
|
||||||
|
#define ROLE_RESEARCH "research"
|
||||||
|
#define ROLE_CARGO "cargo"
|
||||||
|
#define ROLE_CIVILIAN "civilian"
|
||||||
|
#define ROLE_SYNTHETIC "synthetic"
|
||||||
|
#define ROLE_UNKNOWN "unknown"
|
||||||
|
#define ROLE_EVERYONE "everyone"
|
||||||
|
|||||||
66
code/datums/wires/grid_checker.dm
Normal file
66
code/datums/wires/grid_checker.dm
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/datum/wires/grid_checker
|
||||||
|
holder_type = /obj/machinery/power/grid_checker
|
||||||
|
wire_count = 8
|
||||||
|
|
||||||
|
var/const/GRID_CHECKER_WIRE_REBOOT = 1 // This wire causes the grid-check to end, if pulsed.
|
||||||
|
var/const/GRID_CHECKER_WIRE_LOCKOUT = 2 // If cut or pulsed, locks the user out for half a minute.
|
||||||
|
var/const/GRID_CHECKER_WIRE_ALLOW_MANUAL_1 = 4 // Needs to be cut for REBOOT to be possible.
|
||||||
|
var/const/GRID_CHECKER_WIRE_ALLOW_MANUAL_2 = 8 // Needs to be cut for REBOOT to be possible.
|
||||||
|
var/const/GRID_CHECKER_WIRE_ALLOW_MANUAL_3 = 16 // Needs to be cut for REBOOT to be possible.
|
||||||
|
var/const/GRID_CHECKER_WIRE_SHOCK = 32 // Shocks the user if not wearing gloves.
|
||||||
|
var/const/GRID_CHECKER_WIRE_NOTHING_1 = 64 // Does nothing, but makes it a bit harder.
|
||||||
|
var/const/GRID_CHECKER_WIRE_NOTHING_2 = 128 // Does nothing, but makes it a bit harder.
|
||||||
|
|
||||||
|
|
||||||
|
/datum/wires/grid_checker/CanUse(var/mob/living/L)
|
||||||
|
var/obj/machinery/power/grid_checker/G = holder
|
||||||
|
if(G.opened)
|
||||||
|
return TRUE
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
|
||||||
|
/datum/wires/grid_checker/GetInteractWindow()
|
||||||
|
var/obj/machinery/power/grid_checker/G = holder
|
||||||
|
. += ..()
|
||||||
|
. += "The green light is [G.power_failing ? "off" : "on"].<br>"
|
||||||
|
. += "The red light is [G.wire_locked_out ? "on" : "off"].<br>"
|
||||||
|
. += "The blue light is [G.wire_allow_manual_1 && G.wire_allow_manual_2 && G.wire_allow_manual_3 ? "on" : "off"]."
|
||||||
|
|
||||||
|
|
||||||
|
/datum/wires/grid_checker/UpdateCut(var/index, var/mended)
|
||||||
|
var/obj/machinery/power/grid_checker/G = holder
|
||||||
|
switch(index)
|
||||||
|
if(GRID_CHECKER_WIRE_LOCKOUT)
|
||||||
|
G.wire_locked_out = !mended
|
||||||
|
if(GRID_CHECKER_WIRE_ALLOW_MANUAL_1)
|
||||||
|
G.wire_allow_manual_1 = !mended
|
||||||
|
if(GRID_CHECKER_WIRE_ALLOW_MANUAL_2)
|
||||||
|
G.wire_allow_manual_2 = !mended
|
||||||
|
if(GRID_CHECKER_WIRE_ALLOW_MANUAL_3)
|
||||||
|
G.wire_allow_manual_3 = !mended
|
||||||
|
if(GRID_CHECKER_WIRE_SHOCK)
|
||||||
|
if(G.wire_locked_out)
|
||||||
|
return
|
||||||
|
G.shock(usr, 70)
|
||||||
|
|
||||||
|
|
||||||
|
/datum/wires/grid_checker/UpdatePulsed(var/index)
|
||||||
|
var/obj/machinery/power/grid_checker/G = holder
|
||||||
|
switch(index)
|
||||||
|
if(GRID_CHECKER_WIRE_REBOOT)
|
||||||
|
if(G.wire_locked_out)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(G.power_failing && G.wire_allow_manual_1 && G.wire_allow_manual_2 && G.wire_allow_manual_3)
|
||||||
|
G.end_power_failure(TRUE)
|
||||||
|
if(GRID_CHECKER_WIRE_LOCKOUT)
|
||||||
|
if(G.wire_locked_out)
|
||||||
|
return
|
||||||
|
|
||||||
|
G.wire_locked_out = TRUE
|
||||||
|
spawn(30 SECONDS)
|
||||||
|
G.wire_locked_out = FALSE
|
||||||
|
if(GRID_CHECKER_WIRE_SHOCK)
|
||||||
|
if(G.wire_locked_out)
|
||||||
|
return
|
||||||
|
G.shock(usr, 70)
|
||||||
@@ -22,3 +22,10 @@
|
|||||||
build_path = /obj/machinery/power/smes/batteryrack/makeshift
|
build_path = /obj/machinery/power/smes/batteryrack/makeshift
|
||||||
board_type = new /datum/frame/frame_types/machine
|
board_type = new /datum/frame/frame_types/machine
|
||||||
req_components = list(/obj/item/weapon/cell = 3)
|
req_components = list(/obj/item/weapon/cell = 3)
|
||||||
|
|
||||||
|
/obj/item/weapon/circuitboard/grid_checker
|
||||||
|
name = T_BOARD("power grid checker")
|
||||||
|
build_path = /obj/machinery/power/grid_checker
|
||||||
|
board_type = new /datum/frame/frame_types/machine
|
||||||
|
origin_tech = list(TECH_POWER = 4, TECH_ENGINEERING = 3)
|
||||||
|
req_components = list(/obj/item/weapon/stock_parts/capacitor = 3, /obj/item/stack/cable_coil = 10)
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ var/join_motd = null
|
|||||||
var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs.
|
var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs.
|
||||||
var/datum/event_manager/event_manager = new() // Event Manager, the manager for events.
|
var/datum/event_manager/event_manager = new() // Event Manager, the manager for events.
|
||||||
var/datum/game_master/game_master = new() // Game Master, an AI for choosing events.
|
var/datum/game_master/game_master = new() // Game Master, an AI for choosing events.
|
||||||
|
var/datum/metric/metric = new() // Metric datum, used to keep track of the round.
|
||||||
|
|
||||||
var/list/awaydestinations = list() // Away missions. A list of landmarks that the warpgate can take you to.
|
var/list/awaydestinations = list() // Away missions. A list of landmarks that the warpgate can take you to.
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,7 @@
|
|||||||
/datum/gm_action/comms_blackout
|
/datum/gm_action/comms_blackout
|
||||||
name = "communications blackout"
|
name = "communications blackout"
|
||||||
departments = list(ROLE_ENGINEERING, ROLE_EVERYONE)
|
departments = list(ROLE_ENGINEERING, ROLE_EVERYONE)
|
||||||
chaotic = 35
|
chaotic = 35
|
||||||
|
|
||||||
|
/datum/gm_action/comms_blackout/get_weight()
|
||||||
|
return 50 + (metric.count_people_in_department(ROLE_ENGINEERING) * 40)
|
||||||
@@ -1,9 +1,22 @@
|
|||||||
// New grid check event:
|
// New grid check event:
|
||||||
// Very similar to the old one, power goes out in most of the colony, however the new feature is the ability for engineering to
|
// Very similar to the old one, power goes out in most of the colony, however the new feature is the ability for engineering to
|
||||||
// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so,
|
// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so,
|
||||||
// it will reboot itself after a few minutes, just like the old one.
|
// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting
|
||||||
|
// the powernet when this event fires.
|
||||||
|
|
||||||
/datum/gm_action/grid_check
|
/datum/gm_action/grid_check
|
||||||
name = "grid check"
|
name = "grid check"
|
||||||
departments = list(ROLE_ENGINEERING, ROLE_EVERYONE)
|
departments = list(ROLE_ENGINEERING, ROLE_EVERYONE)
|
||||||
chaotic = 20
|
chaotic = 20
|
||||||
|
|
||||||
|
/datum/gm_action/grid_check/get_weight()
|
||||||
|
return 50 + (metric.count_people_in_department(ROLE_ENGINEERING) * 30)
|
||||||
|
|
||||||
|
/datum/gm_action/grid_check/start()
|
||||||
|
// This sets off a chain of events that lead to the actual grid check (or perhaps worse).
|
||||||
|
// First, the Supermatter engine makes a power spike.
|
||||||
|
for(var/obj/machinery/power/generator/engine in machines)
|
||||||
|
engine.power_spike()
|
||||||
|
break // Just one engine, please.
|
||||||
|
// After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout.
|
||||||
|
// If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code.
|
||||||
@@ -3,4 +3,7 @@
|
|||||||
/datum/gm_action/waste_disposal
|
/datum/gm_action/waste_disposal
|
||||||
name = "waste disposal"
|
name = "waste disposal"
|
||||||
departments = list(ROLE_CARGO)
|
departments = list(ROLE_CARGO)
|
||||||
chaotic = 0
|
chaotic = 0
|
||||||
|
|
||||||
|
/datum/gm_action/waste_disposal/get_weight()
|
||||||
|
return metric.count_people_in_department(ROLE_CARGO) * 50
|
||||||
@@ -11,30 +11,70 @@
|
|||||||
|
|
||||||
var/HTML = "<html><head><title>Game Master AI</title></head><body>"
|
var/HTML = "<html><head><title>Game Master AI</title></head><body>"
|
||||||
|
|
||||||
HTML += "Staleness: [staleness]<br>"
|
HTML += "<a href='?src=\ref[src];toggle_time_restrictions=1'>\[Toggle Time Restrictions\]</a> | \
|
||||||
HTML += "Danger: [danger]<br><br>"
|
<a href='?src=\ref[src];suspend=1'>\[Toggle GM\]</a> | \
|
||||||
|
<a href='?src=\ref[src];force_choose_event=1'>\[Force Event Decision\]</a><br>"
|
||||||
|
|
||||||
|
HTML += "Status: [pre_action_checks() ? "Ready" : "Suppressed"]<br><br>"
|
||||||
|
|
||||||
|
HTML += "Staleness: [staleness] <a href='?src=\ref[src];adjust_staleness=1'>\[Adjust\]</a><br>"
|
||||||
|
HTML += "Danger: [danger] <a href='?src=\ref[src];adjust_danger=1'>\[Adjust\]</a><br><br>"
|
||||||
|
|
||||||
HTML += "Actions available;<br>"
|
HTML += "Actions available;<br>"
|
||||||
for(var/datum/gm_action/action in available_actions)
|
for(var/datum/gm_action/action in available_actions)
|
||||||
if(action.enabled == FALSE)
|
if(action.enabled == FALSE)
|
||||||
continue
|
continue
|
||||||
HTML += "[action.name] ([english_list(action.departments)])<br>"
|
HTML += "[action.name] ([english_list(action.departments)]) (weight: [action.get_weight()])<br>"
|
||||||
|
|
||||||
HTML += "<br>"
|
HTML += "<br>"
|
||||||
HTML += "All living mobs activity: [assess_all_living_mobs()]<br>"
|
HTML += "All living mobs activity: [metric.assess_all_living_mobs()]%<br>"
|
||||||
|
HTML += "All ghost activity: [metric.assess_all_dead_mobs()]%<br>"
|
||||||
|
|
||||||
HTML += "<br>"
|
HTML += "<br>"
|
||||||
HTML += "Departmental activity;<br>"
|
HTML += "Departmental activity;<br>"
|
||||||
for(var/department in departments)
|
for(var/department in metric.departments)
|
||||||
var/number_of_people = count_people_in_department(department)
|
HTML += " [department] : [metric.assess_department(department)]%<br>"
|
||||||
HTML += " [department] : [assess_department(department)] / [number_of_people * 100]<br>"
|
|
||||||
|
|
||||||
HTML += "<br>"
|
HTML += "<br>"
|
||||||
HTML += "Activity of players;<br>"
|
HTML += "Activity of players;<br>"
|
||||||
for(var/mob/player in player_list)
|
for(var/mob/player in player_list)
|
||||||
HTML += " [player] : [assess_player_activity(player)]<br>"
|
HTML += " [player] ([player.key]) : [metric.assess_player_activity(player)]%<br>"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
HTML +="</body></html>"
|
HTML +="</body></html>"
|
||||||
user << browse(HTML, "window=log;size=400x450;border=1;can_resize=1;can_close=1;can_minimize=1")
|
user << browse(HTML, "window=log;size=400x450;border=1;can_resize=1;can_close=1;can_minimize=1")
|
||||||
|
|
||||||
|
/datum/game_master/Topic(href, href_list)
|
||||||
|
if(..())
|
||||||
|
return
|
||||||
|
|
||||||
|
if(!is_admin(usr))
|
||||||
|
message_admins("[usr] has attempted to modify the Game Master values without being an admin.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if(href_list["toggle_time_restrictions"])
|
||||||
|
ignore_time_restrictions = !ignore_time_restrictions
|
||||||
|
message_admins("GM event time restrictions was [ignore_time_restrictions ? "dis" : "en"]abled by [usr.key].")
|
||||||
|
|
||||||
|
if(href_list["force_choose_event"])
|
||||||
|
start_action()
|
||||||
|
message_admins("[usr.key] forced the Game Master to choose an event immediately.")
|
||||||
|
|
||||||
|
if(href_list["suspend"])
|
||||||
|
suspended = !suspended
|
||||||
|
message_admins("GM was [suspended ? "dis" : "en"]abled by [usr.key].")
|
||||||
|
|
||||||
|
if(href_list["adjust_staleness"])
|
||||||
|
var/amount = input(usr, "How much staleness should be added or subtracted?", "Game Master") as null|num
|
||||||
|
if(amount)
|
||||||
|
adjust_staleness(amount)
|
||||||
|
message_admins("GM staleness was adjusted by [amount] by [usr.key].")
|
||||||
|
|
||||||
|
if(href_list["adjust_danger"])
|
||||||
|
var/amount = input(usr, "How much danger should be added or subtracted?", "Game Master") as null|num
|
||||||
|
if(amount)
|
||||||
|
adjust_danger(amount)
|
||||||
|
message_admins("GM danger was adjusted by [amount] by [usr.key].")
|
||||||
|
|
||||||
|
interact(usr) // To refresh the UI.
|
||||||
@@ -1,10 +1 @@
|
|||||||
#define ROLE_COMMAND "command"
|
#define EVENT_BASELINE_WEIGHT 200
|
||||||
#define ROLE_SECURITY "security"
|
|
||||||
#define ROLE_ENGINEERING "engineering"
|
|
||||||
#define ROLE_MEDICAL "medical"
|
|
||||||
#define ROLE_RESEARCH "research"
|
|
||||||
#define ROLE_CARGO "cargo"
|
|
||||||
#define ROLE_CIVILIAN "civilian"
|
|
||||||
#define ROLE_SYNTHETIC "synthetic"
|
|
||||||
#define ROLE_UNKNOWN "unknown"
|
|
||||||
#define ROLE_EVERYONE "everyone"
|
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
// the round.
|
// the round.
|
||||||
|
|
||||||
/datum/game_master
|
/datum/game_master
|
||||||
var/suspended = FALSE // If true, it will not do anything.
|
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/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/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/staleness = -20 // Determines liklihood of the GM doing something, increases over time.
|
||||||
@@ -12,31 +13,20 @@
|
|||||||
var/staleness_modifier = 1 // Ditto. Higher numbers generally result in more events occuring in a round.
|
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/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/next_action = 0 // Minimum amount of time of nothingness until the GM can pick something again.
|
||||||
var/departments = list( // List of departments the GM considers for choosing events for.
|
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.
|
||||||
ROLE_COMMAND,
|
|
||||||
ROLE_SECURITY,
|
|
||||||
ROLE_ENGINEERING,
|
|
||||||
ROLE_MEDICAL,
|
|
||||||
ROLE_RESEARCH,
|
|
||||||
ROLE_CARGO,
|
|
||||||
ROLE_CIVILIAN,
|
|
||||||
ROLE_SYNTHETIC
|
|
||||||
)
|
|
||||||
|
|
||||||
/datum/game_master/New()
|
/datum/game_master/New()
|
||||||
..()
|
..()
|
||||||
available_actions = init_subtypes(/datum/gm_action)
|
available_actions = init_subtypes(/datum/gm_action)
|
||||||
// var/actions = typesof(/datum/gm_actions)
|
|
||||||
// for(var/type in actions)
|
|
||||||
// available_actions.Add(new type)
|
|
||||||
|
|
||||||
/datum/game_master/proc/process()
|
/datum/game_master/proc/process()
|
||||||
if(ticker && ticker.current_state == GAME_STATE_PLAYING)
|
if(ticker && ticker.current_state == GAME_STATE_PLAYING && !suspended)
|
||||||
adjust_staleness(1)
|
adjust_staleness(1)
|
||||||
adjust_danger(-1)
|
adjust_danger(-1)
|
||||||
ticks_completed++
|
ticks_completed++
|
||||||
|
|
||||||
var/global_afk = assess_all_living_mobs()
|
var/global_afk = metric.assess_all_living_mobs()
|
||||||
global_afk -= 100
|
global_afk -= 100
|
||||||
global_afk = abs(global_afk)
|
global_afk = abs(global_afk)
|
||||||
global_afk = round(global_afk / 100, 0.1)
|
global_afk = round(global_afk / 100, 0.1)
|
||||||
@@ -46,19 +36,15 @@
|
|||||||
log_debug("Game Master going to start something.")
|
log_debug("Game Master going to start something.")
|
||||||
start_action()
|
start_action()
|
||||||
|
|
||||||
/datum/game_master/proc/assess_all_living_mobs()
|
|
||||||
var/num = 0
|
|
||||||
for(var/mob/living/L in player_list) // Ghosts being AFK isn't that much of a concern.
|
|
||||||
. += assess_player_activity(L)
|
|
||||||
num++
|
|
||||||
if(num)
|
|
||||||
. = round(. / num, 0.1)
|
|
||||||
|
|
||||||
// This is run before committing to an action/event.
|
// This is run before committing to an action/event.
|
||||||
/datum/game_master/proc/pre_action_checks()
|
/datum/game_master/proc/pre_action_checks()
|
||||||
if(!ticker || ticker.current_state != GAME_STATE_PLAYING)
|
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.")
|
log_debug("Game Master unable to start event: Ticker is nonexistant, or the game is not ongoing.")
|
||||||
return FALSE
|
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.
|
// 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/mills = round_duration_in_ticks
|
||||||
var/mins = round((mills % 36000) / 600)
|
var/mins = round((mills % 36000) / 600)
|
||||||
@@ -76,43 +62,21 @@
|
|||||||
if(!pre_action_checks()) // Make sure we're not doing last minute events, or early events.
|
if(!pre_action_checks()) // Make sure we're not doing last minute events, or early events.
|
||||||
return
|
return
|
||||||
log_debug("Game Master now starting action decision.")
|
log_debug("Game Master now starting action decision.")
|
||||||
var/list/best_actions = assess_round() // Checks the whole round for active people, and returns a list of the most activie departments.
|
var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used))
|
||||||
if(best_actions && best_actions.len)
|
|
||||||
var/datum/gm_action/choice = pick(best_actions)
|
|
||||||
if(choice)
|
|
||||||
// log_debug("[choice.name] was chosen by the Game Master, and is now being ran.")
|
|
||||||
// choice.set_up()
|
|
||||||
// choice.start()
|
|
||||||
// choice.annnounce()
|
|
||||||
next_action = world.time + rand(15 MINUTES, 30 MINUTES)
|
|
||||||
|
|
||||||
/datum/game_master/proc/assess_round()
|
|
||||||
var/list/activity = list()
|
|
||||||
for(var/department in departments)
|
|
||||||
activity[department] = assess_department(department)
|
|
||||||
log_debug("Assessing department [department]. They have activity of [activity[department]].")
|
|
||||||
|
|
||||||
var/list/most_active_departments = list() // List of winners.
|
|
||||||
var/highest_activity = null // Department who is leading in activity, if one exists.
|
|
||||||
var/highest_number = 0 // Activity score needed to beat to be the most active department.
|
|
||||||
for(var/i = 1, i <= 3, i++)
|
|
||||||
log_debug("Doing [i]\th round of counting.")
|
|
||||||
for(var/department in activity)
|
|
||||||
if(activity[department] > highest_number && activity[department] > 0) // More active than the current highest department?
|
|
||||||
highest_activity = department
|
|
||||||
highest_number = activity[department]
|
|
||||||
|
|
||||||
if(highest_activity) // Someone's a winner.
|
|
||||||
most_active_departments.Add(highest_activity) // Add to the list of most active.
|
|
||||||
activity.Remove(highest_activity) // Remove them from the other list so they don't win more than once.
|
|
||||||
log_debug("[highest_activity] has won the [i]\th round of activity counting.")
|
|
||||||
highest_activity = null // Now reset for the next round.
|
|
||||||
highest_number = 0
|
|
||||||
//todo: finish
|
|
||||||
var/list/best_actions = decide_best_action(most_active_departments)
|
var/list/best_actions = decide_best_action(most_active_departments)
|
||||||
return best_actions
|
|
||||||
|
|
||||||
// By now, we should have a list of departments populated. The GM will prefer events tailored to these 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]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -169,39 +133,4 @@
|
|||||||
else
|
else
|
||||||
log_debug("Game Master failed to find a suitable event, something very wrong is going on.")
|
log_debug("Game Master failed to find a suitable event, something very wrong is going on.")
|
||||||
|
|
||||||
// This checks a whole department's viability to receive an event.
|
|
||||||
/datum/game_master/proc/assess_department(var/department)
|
|
||||||
if(!department)
|
|
||||||
return
|
|
||||||
var/departmental_activitiy = 0
|
|
||||||
for(var/mob/M in player_list)
|
|
||||||
if(guess_department(M) != department) // Ignore people outside the department we're assessing.
|
|
||||||
continue
|
|
||||||
departmental_activitiy += assess_player_activity(M)
|
|
||||||
return departmental_activitiy
|
|
||||||
|
|
||||||
// This checks an individual player's activity level. People who have been afk for a few minutes aren't punished as much as those
|
|
||||||
// who were afk for hours, as they're most likely gone for good.
|
|
||||||
/datum/game_master/proc/assess_player_activity(var/mob/M)
|
|
||||||
. = 100
|
|
||||||
if(!M)
|
|
||||||
. = 0
|
|
||||||
return
|
|
||||||
|
|
||||||
if(!M.mind || !M.client) // Logged out. They might come back but we can't do any meaningful assessments for now.
|
|
||||||
. = 0
|
|
||||||
return
|
|
||||||
|
|
||||||
var/afk = M.client.is_afk(1 MINUTE)
|
|
||||||
if(afk) // Deduct points based on length of AFK-ness.
|
|
||||||
switch(afk) // One minute is equal to 600, for reference.
|
|
||||||
if(1 MINUTE to 10 MINUTES) // People gone for this emough of time hopefully will come back soon.
|
|
||||||
. -= round( (afk / 200), 1)
|
|
||||||
// . -= 30
|
|
||||||
if(10 MINUTES to 30 MINUTES)
|
|
||||||
. -= round( (afk / 150), 1)
|
|
||||||
// . -= 70
|
|
||||||
if(30 MINUTES to INFINITY) // They're probably not coming back if it's been 30 minutes.
|
|
||||||
. -= 100
|
|
||||||
. = max(. , 0) // No negative numbers, or else people could drag other, non-afk players down.
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,67 +6,4 @@
|
|||||||
// Tell the game master that something interesting happened.
|
// Tell the game master that something interesting happened.
|
||||||
/datum/game_master/proc/adjust_staleness(var/amt)
|
/datum/game_master/proc/adjust_staleness(var/amt)
|
||||||
amt = amt * staleness_modifier
|
amt = amt * staleness_modifier
|
||||||
staleness = round( Clamp(staleness + amt, -50, 200), 0.1)
|
staleness = round( Clamp(staleness + amt, -50, 200), 0.1)
|
||||||
|
|
||||||
// This proc tries to find the department of an arbitrary mob.
|
|
||||||
/datum/game_master/proc/guess_department(var/mob/M)
|
|
||||||
var/datum/data/record/R = find_general_record("name", M.real_name)
|
|
||||||
. = ROLE_UNKNOWN
|
|
||||||
if(R) // We found someone with a record.
|
|
||||||
var/recorded_rank = R.fields["real_rank"]
|
|
||||||
. = role_name_to_department(recorded_rank)
|
|
||||||
if(. != ROLE_UNKNOWN) // We found the correct department, so we can stop now.
|
|
||||||
return
|
|
||||||
|
|
||||||
// They have a custom title, aren't crew, or someone deleted their record, so we need a fallback method.
|
|
||||||
// Let's check the mind.
|
|
||||||
if(M.mind)
|
|
||||||
. = role_name_to_department(M.mind.assigned_role)
|
|
||||||
if(. != ROLE_UNKNOWN)
|
|
||||||
return
|
|
||||||
|
|
||||||
// At this point, they don't have a mind, or for some reason assigned_role didn't work.
|
|
||||||
if(ishuman(M))
|
|
||||||
var/mob/living/carbon/human/H = M
|
|
||||||
. = role_name_to_department(H.job)
|
|
||||||
if(. != ROLE_UNKNOWN)
|
|
||||||
return
|
|
||||||
|
|
||||||
return ROLE_UNKNOWN // Welp.
|
|
||||||
|
|
||||||
|
|
||||||
// Feed this proc the name of a job, and it will try to figure out what department they are apart of.
|
|
||||||
/datum/game_master/proc/role_name_to_department(var/role_name)
|
|
||||||
if(role_name in security_positions)
|
|
||||||
return ROLE_SECURITY
|
|
||||||
|
|
||||||
if(role_name in engineering_positions)
|
|
||||||
return ROLE_ENGINEERING
|
|
||||||
|
|
||||||
if(role_name in medical_positions)
|
|
||||||
return ROLE_MEDICAL
|
|
||||||
|
|
||||||
if(role_name in science_positions)
|
|
||||||
return ROLE_RESEARCH
|
|
||||||
|
|
||||||
if(role_name in cargo_positions)
|
|
||||||
return ROLE_CARGO
|
|
||||||
|
|
||||||
if(role_name in civilian_positions)
|
|
||||||
return ROLE_CIVILIAN
|
|
||||||
|
|
||||||
if(role_name in nonhuman_positions)
|
|
||||||
return ROLE_SYNTHETIC
|
|
||||||
|
|
||||||
if(role_name in command_positions) // We do command last, so that only the Captain and command secretaries get caught.
|
|
||||||
return ROLE_COMMAND
|
|
||||||
|
|
||||||
return ROLE_UNKNOWN
|
|
||||||
|
|
||||||
/datum/game_master/proc/count_people_in_department(var/department)
|
|
||||||
if(!department)
|
|
||||||
return
|
|
||||||
for(var/mob/M in player_list)
|
|
||||||
if(guess_department(M) != department) // Ignore people outside the department we're counting.
|
|
||||||
continue
|
|
||||||
. += 1
|
|
||||||
82
code/modules/metric/activity.dm
Normal file
82
code/modules/metric/activity.dm
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// This checks an individual player's activity level. People who have been afk for a few minutes aren't punished as much as those
|
||||||
|
// who were afk for hours, as they're most likely gone for good.
|
||||||
|
/datum/metric/proc/assess_player_activity(var/mob/M)
|
||||||
|
. = 100
|
||||||
|
if(!M)
|
||||||
|
. = 0
|
||||||
|
return
|
||||||
|
|
||||||
|
if(!M.mind || !M.client) // Logged out. They might come back but we can't do any meaningful assessments for now.
|
||||||
|
. = 0
|
||||||
|
return
|
||||||
|
|
||||||
|
var/afk = M.client.is_afk(1 MINUTE)
|
||||||
|
if(afk) // Deduct points based on length of AFK-ness.
|
||||||
|
switch(afk) // One minute is equal to 600, for reference.
|
||||||
|
if(1 MINUTE to 10 MINUTES) // People gone for this emough of time hopefully will come back soon.
|
||||||
|
. -= round( (afk / 200), 1)
|
||||||
|
if(10 MINUTES to 30 MINUTES)
|
||||||
|
. -= round( (afk / 150), 1)
|
||||||
|
if(30 MINUTES to INFINITY) // They're probably not coming back if it's been 30 minutes.
|
||||||
|
. -= 100
|
||||||
|
. = max(. , 0) // No negative numbers, or else people could drag other, non-afk players down.
|
||||||
|
|
||||||
|
// This checks a whole department's collective activity.
|
||||||
|
/datum/metric/proc/assess_department(var/department)
|
||||||
|
if(!department)
|
||||||
|
return
|
||||||
|
var/departmental_activity = 0
|
||||||
|
var/departmental_size = 0
|
||||||
|
for(var/mob/M in player_list)
|
||||||
|
if(guess_department(M) != department) // Ignore people outside the department we're assessing.
|
||||||
|
continue
|
||||||
|
departmental_activity += assess_player_activity(M)
|
||||||
|
departmental_size++
|
||||||
|
if(departmental_size)
|
||||||
|
departmental_activity = departmental_activity / departmental_size // Average it out.
|
||||||
|
return departmental_activity
|
||||||
|
|
||||||
|
/datum/metric/proc/assess_all_departments(var/cutoff_number = 3, var/list/department_blacklist = list())
|
||||||
|
var/list/activity = list()
|
||||||
|
for(var/department in departments)
|
||||||
|
activity[department] = assess_department(department)
|
||||||
|
log_debug("Assessing department [department]. They have activity of [activity[department]].")
|
||||||
|
|
||||||
|
var/list/most_active_departments = list() // List of winners.
|
||||||
|
var/highest_activity = null // Department who is leading in activity, if one exists.
|
||||||
|
var/highest_number = 0 // Activity score needed to beat to be the most active department.
|
||||||
|
for(var/i = 1, i <= cutoff_number, i++)
|
||||||
|
log_debug("Doing [i]\th round of counting.")
|
||||||
|
for(var/department in activity)
|
||||||
|
if(department in department_blacklist) // Blacklisted?
|
||||||
|
continue
|
||||||
|
if(activity[department] > highest_number && activity[department] > 0) // More active than the current highest department?
|
||||||
|
highest_activity = department
|
||||||
|
highest_number = activity[department]
|
||||||
|
|
||||||
|
if(highest_activity) // Someone's a winner.
|
||||||
|
most_active_departments.Add(highest_activity) // Add to the list of most active.
|
||||||
|
activity.Remove(highest_activity) // Remove them from the other list so they don't win more than once.
|
||||||
|
log_debug("[highest_activity] has won the [i]\th round of activity counting.")
|
||||||
|
highest_activity = null // Now reset for the next round.
|
||||||
|
highest_number = 0
|
||||||
|
//todo: finish
|
||||||
|
return most_active_departments
|
||||||
|
|
||||||
|
/datum/metric/proc/assess_all_living_mobs() // Living refers to the type, not the stat variable.
|
||||||
|
. = 0
|
||||||
|
var/num = 0
|
||||||
|
for(var/mob/living/L in player_list)
|
||||||
|
. += assess_player_activity(L)
|
||||||
|
num++
|
||||||
|
if(num)
|
||||||
|
. = round(. / num, 0.1)
|
||||||
|
|
||||||
|
/datum/metric/proc/assess_all_dead_mobs() // Ditto.
|
||||||
|
. = 0
|
||||||
|
var/num = 0
|
||||||
|
for(var/mob/observer/dead/O in player_list)
|
||||||
|
. += assess_player_activity(O)
|
||||||
|
num++
|
||||||
|
if(num)
|
||||||
|
. = round(. / num, 0.1)
|
||||||
72
code/modules/metric/department.dm
Normal file
72
code/modules/metric/department.dm
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
// This proc tries to find the department of an arbitrary mob.
|
||||||
|
/datum/metric/proc/guess_department(var/mob/M)
|
||||||
|
var/list/found_roles = list()
|
||||||
|
. = ROLE_UNKNOWN
|
||||||
|
|
||||||
|
// Records are usually the most reliable way to get what job someone is.
|
||||||
|
var/datum/data/record/R = find_general_record("name", M.real_name)
|
||||||
|
if(R) // We found someone with a record.
|
||||||
|
var/recorded_rank = R.fields["real_rank"]
|
||||||
|
found_roles = role_name_to_department(recorded_rank)
|
||||||
|
. = found_roles[1]
|
||||||
|
if(. != ROLE_UNKNOWN) // We found the correct department, so we can stop now.
|
||||||
|
return
|
||||||
|
|
||||||
|
// They have a custom title, aren't crew, or someone deleted their record, so we need a fallback method.
|
||||||
|
// Let's check the mind.
|
||||||
|
if(M.mind)
|
||||||
|
found_roles = role_name_to_department(M.mind.assigned_role)
|
||||||
|
. = found_roles[1]
|
||||||
|
if(. != ROLE_UNKNOWN)
|
||||||
|
return
|
||||||
|
|
||||||
|
// At this point, they don't have a mind, or for some reason assigned_role didn't work.
|
||||||
|
found_roles = role_name_to_department(M.job)
|
||||||
|
. = found_roles[1]
|
||||||
|
if(. != ROLE_UNKNOWN)
|
||||||
|
return
|
||||||
|
|
||||||
|
return ROLE_UNKNOWN // Welp.
|
||||||
|
|
||||||
|
// Feed this proc the name of a job, and it will try to figure out what department they are apart of.
|
||||||
|
// Note that this returns a list, as some jobs are in more than one department, like Command. The 'primary' department is the first
|
||||||
|
// in the list, e.g. a HoS has Security as first, Command as second in the returned list.
|
||||||
|
/datum/metric/proc/role_name_to_department(var/role_name)
|
||||||
|
var/list/result = list()
|
||||||
|
|
||||||
|
if(role_name in security_positions)
|
||||||
|
result += ROLE_SECURITY
|
||||||
|
|
||||||
|
if(role_name in engineering_positions)
|
||||||
|
result += ROLE_ENGINEERING
|
||||||
|
|
||||||
|
if(role_name in medical_positions)
|
||||||
|
result += ROLE_MEDICAL
|
||||||
|
|
||||||
|
if(role_name in science_positions)
|
||||||
|
result += ROLE_RESEARCH
|
||||||
|
|
||||||
|
if(role_name in cargo_positions)
|
||||||
|
result += ROLE_CARGO
|
||||||
|
|
||||||
|
if(role_name in civilian_positions)
|
||||||
|
result += ROLE_CIVILIAN
|
||||||
|
|
||||||
|
if(role_name in nonhuman_positions)
|
||||||
|
result += ROLE_SYNTHETIC
|
||||||
|
|
||||||
|
if(role_name in command_positions) // We do Command last, since we consider command to only be a primary department for hop/admin.
|
||||||
|
result += ROLE_COMMAND
|
||||||
|
|
||||||
|
if(!result.len) // No department was found.
|
||||||
|
result += ROLE_UNKNOWN
|
||||||
|
return result
|
||||||
|
|
||||||
|
/datum/metric/proc/count_people_in_department(var/department)
|
||||||
|
if(!department)
|
||||||
|
return
|
||||||
|
for(var/mob/M in player_list)
|
||||||
|
if(guess_department(M) != department) // Ignore people outside the department we're counting.
|
||||||
|
continue
|
||||||
|
. += 1
|
||||||
15
code/modules/metric/metric.dm
Normal file
15
code/modules/metric/metric.dm
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// This is a global datum used to retrieve certain information about the round, such as activity of a department or a specific
|
||||||
|
// player.
|
||||||
|
|
||||||
|
/datum/metric
|
||||||
|
var/departments = list(
|
||||||
|
ROLE_COMMAND,
|
||||||
|
ROLE_SECURITY,
|
||||||
|
ROLE_ENGINEERING,
|
||||||
|
ROLE_MEDICAL,
|
||||||
|
ROLE_RESEARCH,
|
||||||
|
ROLE_CARGO,
|
||||||
|
ROLE_CIVILIAN,
|
||||||
|
ROLE_SYNTHETIC
|
||||||
|
)
|
||||||
|
|
||||||
@@ -68,6 +68,7 @@
|
|||||||
var/cell_type = /obj/item/weapon/cell/apc
|
var/cell_type = /obj/item/weapon/cell/apc
|
||||||
var/opened = 0 //0=closed, 1=opened, 2=cover removed
|
var/opened = 0 //0=closed, 1=opened, 2=cover removed
|
||||||
var/shorted = 0
|
var/shorted = 0
|
||||||
|
var/grid_check = FALSE
|
||||||
var/lighting = 3
|
var/lighting = 3
|
||||||
var/equipment = 3
|
var/equipment = 3
|
||||||
var/environ = 3
|
var/environ = 3
|
||||||
@@ -796,7 +797,7 @@
|
|||||||
return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])"
|
return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])"
|
||||||
|
|
||||||
/obj/machinery/power/apc/proc/update()
|
/obj/machinery/power/apc/proc/update()
|
||||||
if(operating && !shorted)
|
if(operating && !shorted && !grid_check)
|
||||||
area.power_light = (lighting > 1)
|
area.power_light = (lighting > 1)
|
||||||
area.power_equip = (equipment > 1)
|
area.power_equip = (equipment > 1)
|
||||||
area.power_environ = (environ > 1)
|
area.power_environ = (environ > 1)
|
||||||
@@ -1001,7 +1002,7 @@
|
|||||||
if(debug)
|
if(debug)
|
||||||
log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]")
|
log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]")
|
||||||
|
|
||||||
if(cell && !shorted)
|
if(cell && !shorted && !grid_check)
|
||||||
// draw power from cell as before to power the area
|
// draw power from cell as before to power the area
|
||||||
var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
|
var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
|
||||||
cell.use(cellused)
|
cell.use(cellused)
|
||||||
@@ -1196,7 +1197,7 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on)
|
|||||||
// overload the lights in this APC area
|
// overload the lights in this APC area
|
||||||
|
|
||||||
/obj/machinery/power/apc/proc/overload_lighting(var/chance = 100)
|
/obj/machinery/power/apc/proc/overload_lighting(var/chance = 100)
|
||||||
if(/* !get_connection() || */ !operating || shorted)
|
if(/* !get_connection() || */ !operating || shorted || grid_check)
|
||||||
return
|
return
|
||||||
if( cell && cell.charge>=20)
|
if( cell && cell.charge>=20)
|
||||||
cell.use(20);
|
cell.use(20);
|
||||||
@@ -1225,4 +1226,34 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on)
|
|||||||
update_icon()
|
update_icon()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
/obj/machinery/power/apc/overload(var/obj/machinery/power/source)
|
||||||
|
if(is_critical)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(prob(30)) // Nothing happens.
|
||||||
|
return
|
||||||
|
|
||||||
|
if(prob(40)) // Lights blow.
|
||||||
|
overload_lighting()
|
||||||
|
|
||||||
|
if(prob(40)) // Spooky flickers.
|
||||||
|
for(var/obj/machinery/light/L in area)
|
||||||
|
L.flicker(20)
|
||||||
|
|
||||||
|
if(prob(25)) // Bluescreens.
|
||||||
|
emagged = 1
|
||||||
|
locked = 0
|
||||||
|
update_icon()
|
||||||
|
|
||||||
|
if(prob(25)) // Cell gets damaged.
|
||||||
|
if(cell)
|
||||||
|
cell.corrupt()
|
||||||
|
|
||||||
|
if(prob(10)) // Computers get broken.
|
||||||
|
for(var/obj/machinery/computer/comp in area)
|
||||||
|
comp.ex_act(3)
|
||||||
|
|
||||||
|
if(prob(5)) // APC completely ruined.
|
||||||
|
set_broken()
|
||||||
|
|
||||||
#undef APC_UPDATE_ICON_COOLDOWN
|
#undef APC_UPDATE_ICON_COOLDOWN
|
||||||
|
|||||||
@@ -234,3 +234,8 @@
|
|||||||
return
|
return
|
||||||
|
|
||||||
src.set_dir(turn(src.dir, -90))
|
src.set_dir(turn(src.dir, -90))
|
||||||
|
|
||||||
|
/obj/machinery/power/generator/power_spike()
|
||||||
|
if(effective_gen >= max_power / 2 && powernet) // Don't make a spike if we're not making a whole lot of power.
|
||||||
|
..()
|
||||||
|
|
||||||
|
|||||||
125
code/modules/power/grid_checker.dm
Normal file
125
code/modules/power/grid_checker.dm
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/obj/machinery/power/grid_checker
|
||||||
|
name = "grid checker"
|
||||||
|
desc = "A machine that reacts to unstable conditions in the powernet, by safely shutting everything down. Probably better \
|
||||||
|
than the alternative."
|
||||||
|
icon_state = "gridchecker_on"
|
||||||
|
circuit = /obj/item/weapon/circuitboard/grid_checker
|
||||||
|
var/power_failing = FALSE // Turns to TRUE when the grid check event is fired by the Game Master, or perhaps a cheeky antag.
|
||||||
|
// Wire stuff below.
|
||||||
|
var/datum/wires/grid_checker/wires
|
||||||
|
var/wire_locked_out = FALSE
|
||||||
|
var/wire_allow_manual_1 = FALSE
|
||||||
|
var/wire_allow_manual_2 = FALSE
|
||||||
|
var/wire_allow_manual_3 = FALSE
|
||||||
|
var/opened = FALSE
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/New()
|
||||||
|
..()
|
||||||
|
connect_to_network()
|
||||||
|
update_icon()
|
||||||
|
wires = new(src)
|
||||||
|
component_parts = list()
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
|
||||||
|
component_parts += new /obj/item/stack/cable_coil(src, 10)
|
||||||
|
RefreshParts()
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/Destroy()
|
||||||
|
qdel(wires)
|
||||||
|
wires = null
|
||||||
|
..()
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/update_icon()
|
||||||
|
if(power_failing)
|
||||||
|
icon_state = "gridchecker_off"
|
||||||
|
set_light(2, 2, "#F86060")
|
||||||
|
else
|
||||||
|
icon_state = "gridchecker_on"
|
||||||
|
set_light(2, 2, "#A8B0F8")
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/attackby(obj/item/W, mob/user)
|
||||||
|
if(!user)
|
||||||
|
return
|
||||||
|
if(istype(W, /obj/item/weapon/screwdriver))
|
||||||
|
default_deconstruction_screwdriver(user, W)
|
||||||
|
opened = !opened
|
||||||
|
else if(istype(W, /obj/item/weapon/crowbar))
|
||||||
|
default_deconstruction_crowbar(user, W)
|
||||||
|
else if(istype(W, /obj/item/device/multitool) || istype(W, /obj/item/weapon/wirecutters) )
|
||||||
|
attack_hand(user)
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/attack_hand(mob/user)
|
||||||
|
if(!user)
|
||||||
|
return
|
||||||
|
add_fingerprint(user)
|
||||||
|
interact(user)
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/interact(mob/user)
|
||||||
|
if(!user)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(opened)
|
||||||
|
wires.Interact(user)
|
||||||
|
|
||||||
|
return ui_interact(user)
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/proc/power_failure(var/announce = TRUE)
|
||||||
|
if(announce)
|
||||||
|
command_announcement.Announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, \
|
||||||
|
the colony's power will be shut off for an indeterminate duration while the powernet monitor restarts automatically, or \
|
||||||
|
when Engineering can manually resolve the issue.",
|
||||||
|
"Critical Power Failure",
|
||||||
|
new_sound = 'sound/AI/poweroff.ogg')
|
||||||
|
power_failing = TRUE
|
||||||
|
if(powernet)
|
||||||
|
for(var/obj/machinery/power/terminal/T in powernet.nodes) // SMESes that are "downstream" of the powernet.
|
||||||
|
|
||||||
|
if(istype(T.master, /obj/machinery/power/apc))
|
||||||
|
var/obj/machinery/power/apc/A = T.master
|
||||||
|
if(A.is_critical)
|
||||||
|
continue
|
||||||
|
A.grid_check = TRUE
|
||||||
|
|
||||||
|
for(var/obj/machinery/power/smes/smes in powernet.nodes) // These are "upstream"
|
||||||
|
smes.grid_check = TRUE
|
||||||
|
/*
|
||||||
|
smes.last_charge = smes.charge
|
||||||
|
smes.last_output_attempt = smes.output_attempt
|
||||||
|
smes.last_input_attempt = smes.input_attempt
|
||||||
|
smes.charge = 0
|
||||||
|
smes.inputting(FALSE)
|
||||||
|
smes.outputting(FALSE)
|
||||||
|
smes.update_icon()
|
||||||
|
smes.power_change()
|
||||||
|
*/
|
||||||
|
update_icon()
|
||||||
|
|
||||||
|
spawn(rand(4 MINUTES, 10 MINUTES) )
|
||||||
|
if(power_failing) // Check to see if engineering didn't beat us to it.
|
||||||
|
end_power_failure(TRUE)
|
||||||
|
|
||||||
|
/obj/machinery/power/grid_checker/proc/end_power_failure(var/announce = TRUE)
|
||||||
|
if(announce)
|
||||||
|
command_announcement.Announce("Power has been restored to [station_name()]. We apologize for the inconvenience.",
|
||||||
|
"Power Systems Nominal",
|
||||||
|
new_sound = 'sound/AI/poweron.ogg')
|
||||||
|
power_failing = FALSE
|
||||||
|
update_icon()
|
||||||
|
|
||||||
|
for(var/obj/machinery/power/terminal/T in powernet.nodes)
|
||||||
|
if(istype(T.master, /obj/machinery/power/apc))
|
||||||
|
var/obj/machinery/power/apc/A = T.master
|
||||||
|
if(A.is_critical)
|
||||||
|
continue
|
||||||
|
A.grid_check = FALSE
|
||||||
|
|
||||||
|
for(var/obj/machinery/power/smes/smes in powernet.nodes) // These are "upstream"
|
||||||
|
smes.grid_check = FALSE
|
||||||
|
/*
|
||||||
|
smes.charge = smes.last_charge
|
||||||
|
smes.output_attempt = smes.last_output_attempt
|
||||||
|
smes.input_attempt = smes.last_input_attempt
|
||||||
|
smes.update_icon()
|
||||||
|
smes.power_change()
|
||||||
|
*/
|
||||||
@@ -138,6 +138,29 @@
|
|||||||
..()
|
..()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// Used for power spikes by the engine, has specific effects on different machines.
|
||||||
|
/obj/machinery/power/proc/overload(var/obj/machinery/power/source)
|
||||||
|
return
|
||||||
|
|
||||||
|
/obj/machinery/power/proc/power_spike()
|
||||||
|
var/obj/machinery/power/grid_checker/G = locate() in powernet.nodes
|
||||||
|
if(G) // If we found a grid checker, then all is well.
|
||||||
|
G.power_failure(prob(30))
|
||||||
|
else // Otherwise lets break some stuff.
|
||||||
|
spawn(1)
|
||||||
|
command_announcement.Announce("Dangerous power spike detected in the power network. Please check machinery \
|
||||||
|
for electrical damage.",
|
||||||
|
"Critical Power Overload")
|
||||||
|
var/i = 0
|
||||||
|
var/limit = rand(30, 50)
|
||||||
|
for(var/obj/machinery/power/P in powernet.nodes)
|
||||||
|
P.overload(src)
|
||||||
|
i++
|
||||||
|
if(i % 5)
|
||||||
|
sleep(1)
|
||||||
|
if(i >= limit)
|
||||||
|
break
|
||||||
|
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
// Powernet handling helpers
|
// Powernet handling helpers
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
var/building_terminal = 0 //Suggestions about how to avoid clickspam building several terminals accepted!
|
var/building_terminal = 0 //Suggestions about how to avoid clickspam building several terminals accepted!
|
||||||
var/obj/machinery/power/terminal/terminal = null
|
var/obj/machinery/power/terminal/terminal = null
|
||||||
var/should_be_mapped = 0 // If this is set to 0 it will send out warning on New()
|
var/should_be_mapped = 0 // If this is set to 0 it will send out warning on New()
|
||||||
|
var/grid_check = FALSE // If true, suspends all I/O.
|
||||||
|
|
||||||
/obj/machinery/power/smes/drain_power(var/drain_check, var/surge, var/amount = 0)
|
/obj/machinery/power/smes/drain_power(var/drain_check, var/surge, var/amount = 0)
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@
|
|||||||
var/last_onln = outputting
|
var/last_onln = outputting
|
||||||
|
|
||||||
//inputting
|
//inputting
|
||||||
if(input_attempt && (!input_pulsed && !input_cut))
|
if(input_attempt && (!input_pulsed && !input_cut) && !grid_check)
|
||||||
var/target_load = min((capacity-charge)/SMESRATE, input_level) // charge at set rate, limited to spare capacity
|
var/target_load = min((capacity-charge)/SMESRATE, input_level) // charge at set rate, limited to spare capacity
|
||||||
var/actual_load = draw_power(target_load) // add the load to the terminal side network
|
var/actual_load = draw_power(target_load) // add the load to the terminal side network
|
||||||
charge += actual_load * SMESRATE // increase the charge
|
charge += actual_load * SMESRATE // increase the charge
|
||||||
@@ -137,7 +138,7 @@
|
|||||||
inputting = 0
|
inputting = 0
|
||||||
|
|
||||||
//outputting
|
//outputting
|
||||||
if(outputting && (!output_pulsed && !output_cut))
|
if(outputting && (!output_pulsed && !output_cut) && !grid_check)
|
||||||
output_used = min( charge/SMESRATE, output_level) //limit output to that stored
|
output_used = min( charge/SMESRATE, output_level) //limit output to that stored
|
||||||
|
|
||||||
charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive)
|
charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive)
|
||||||
@@ -420,6 +421,11 @@
|
|||||||
update_icon()
|
update_icon()
|
||||||
..()
|
..()
|
||||||
|
|
||||||
|
/obj/machinery/power/smes/overload(var/obj/machinery/power/source) // This propagates the power spike down the powernet.
|
||||||
|
if(istype(source, /obj/machinery/power/smes)) // Prevent infinite loops if two SMESes are hooked up to each other.
|
||||||
|
return
|
||||||
|
power_spike()
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/power/smes/magical
|
/obj/machinery/power/smes/magical
|
||||||
name = "magical power storage unit"
|
name = "magical power storage unit"
|
||||||
|
|||||||
@@ -37,3 +37,7 @@
|
|||||||
// Powernet rebuilds need this to work properly.
|
// Powernet rebuilds need this to work properly.
|
||||||
/obj/machinery/power/terminal/process()
|
/obj/machinery/power/terminal/process()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
/obj/machinery/power/terminal/overload(var/obj/machinery/power/source)
|
||||||
|
if(master)
|
||||||
|
master.overload(source)
|
||||||
|
|||||||
@@ -1047,6 +1047,14 @@ CIRCUITS BELOW
|
|||||||
build_path = /obj/item/weapon/circuitboard/smes
|
build_path = /obj/item/weapon/circuitboard/smes
|
||||||
sort_string = "JBABB"
|
sort_string = "JBABB"
|
||||||
|
|
||||||
|
/datum/design/circuit/grid_checker
|
||||||
|
name = "power grid checker"
|
||||||
|
desc = "Allows for the construction of circuit boards used to build a grid checker."
|
||||||
|
id = "grid_checker"
|
||||||
|
req_tech = list(TECH_POWER = 4, TECH_ENGINEERING = 3)
|
||||||
|
build_path = /obj/item/weapon/circuitboard/grid_checker
|
||||||
|
sort_string = "JBABC"
|
||||||
|
|
||||||
/datum/design/circuit/gas_heater
|
/datum/design/circuit/gas_heater
|
||||||
name = "gas heating system"
|
name = "gas heating system"
|
||||||
id = "gasheater"
|
id = "gasheater"
|
||||||
|
|||||||
@@ -253,6 +253,7 @@
|
|||||||
#include "code\datums\wires\autolathe.dm"
|
#include "code\datums\wires\autolathe.dm"
|
||||||
#include "code\datums\wires\camera.dm"
|
#include "code\datums\wires\camera.dm"
|
||||||
#include "code\datums\wires\explosive.dm"
|
#include "code\datums\wires\explosive.dm"
|
||||||
|
#include "code\datums\wires\grid_checker.dm"
|
||||||
#include "code\datums\wires\particle_accelerator.dm"
|
#include "code\datums\wires\particle_accelerator.dm"
|
||||||
#include "code\datums\wires\radio.dm"
|
#include "code\datums\wires\radio.dm"
|
||||||
#include "code\datums\wires\robot.dm"
|
#include "code\datums\wires\robot.dm"
|
||||||
@@ -1409,6 +1410,9 @@
|
|||||||
#include "code\modules\materials\material_sheets.dm"
|
#include "code\modules\materials\material_sheets.dm"
|
||||||
#include "code\modules\materials\material_synth.dm"
|
#include "code\modules\materials\material_synth.dm"
|
||||||
#include "code\modules\materials\materials.dm"
|
#include "code\modules\materials\materials.dm"
|
||||||
|
#include "code\modules\metric\activity.dm"
|
||||||
|
#include "code\modules\metric\department.dm"
|
||||||
|
#include "code\modules\metric\metric.dm"
|
||||||
#include "code\modules\mining\abandonedcrates.dm"
|
#include "code\modules\mining\abandonedcrates.dm"
|
||||||
#include "code\modules\mining\alloys.dm"
|
#include "code\modules\mining\alloys.dm"
|
||||||
#include "code\modules\mining\coins.dm"
|
#include "code\modules\mining\coins.dm"
|
||||||
@@ -1762,6 +1766,7 @@
|
|||||||
#include "code\modules\power\generator.dm"
|
#include "code\modules\power\generator.dm"
|
||||||
#include "code\modules\power\generator_type2.dm"
|
#include "code\modules\power\generator_type2.dm"
|
||||||
#include "code\modules\power\gravitygenerator.dm"
|
#include "code\modules\power\gravitygenerator.dm"
|
||||||
|
#include "code\modules\power\grid_checker.dm"
|
||||||
#include "code\modules\power\lighting.dm"
|
#include "code\modules\power\lighting.dm"
|
||||||
#include "code\modules\power\port_gen.dm"
|
#include "code\modules\power\port_gen.dm"
|
||||||
#include "code\modules\power\power.dm"
|
#include "code\modules\power\power.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user