diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm index df225210f02e..fb8d74ac0a6f 100644 --- a/code/controllers/subsystem/persistence/_persistence.dm +++ b/code/controllers/subsystem/persistence/_persistence.dm @@ -73,7 +73,7 @@ SUBSYSTEM_DEF(persistence) load_panic_bunker() //SKYRAT EDIT ADDITION - PANICBUNKER load_tram_counter() load_adventures() - load_storyteller_type() //BUBBER EDIT ADD - Storyteller + // load_storyteller_type() //BUBBER EDIT ADD - Storyteller // BUG EDIT - Unified return SS_INIT_SUCCESS ///Collects all data to persist. diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index 535cbd33503a..92d9ab778676 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -45,7 +45,7 @@ SUBSYSTEM_DEF(statpanels) "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)", "Map: [SSmapping.current_map?.map_name || "Loading..."]", cached ? "Next Map: [cached.map_name]" : null, - "Storyteller: [SSgamemode.storyteller ? SSgamemode.storyteller.name : "N/A"]", // BUBBER EDIT ADDITION + // "Storyteller: [SSgamemode.storyteller ? SSgamemode.storyteller.name : "N/A"]", // BUBBER EDIT ADDITION // BUG REMOVAL "Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]", "Connected: [GLOB.clients.len] | Active: [active_players]/[CONFIG_GET(number/hard_popcap)] | Observing: [observing_players]", //BUBBER EDIT: ACTIVE AND OBSERVING PLAYERS " ", diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 7c3ba10474d3..a6f96aa0cd8e 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -172,6 +172,7 @@ SUBSYSTEM_DEF(ticker) send2chat(new /datum/tgs_message_content("<@&[CONFIG_GET(string/game_alert_role_id)]> Round **[GLOB.round_id]** starting on [SSmapping.current_map.map_name], [CONFIG_GET(string/servername)]! \nIf you wish to be pinged for game related stuff, go to <#[CONFIG_GET(string/role_assign_channel_id)]> and assign yourself the roles."), CONFIG_GET(string/channel_announce_new_game)) // SKYRAT EDIT - Role ping and round ID in game-alert // SKYRAT EDIT END current_state = GAME_STATE_PREGAME + /* BUGSTATION REMOVAL START // BUBBERSTATION EDIT START var/storyteller = CONFIG_GET(string/default_storyteller) if(storyteller) @@ -179,6 +180,7 @@ SUBSYSTEM_DEF(ticker) else SSvote.initiate_vote(/datum/vote/storyteller, "Storyteller Vote", forced = TRUE) // BUBBERSTATION EDIT END + BUGSTATION REMOVAL END */ SStitle.change_title_screen() //SKYRAT EDIT ADDITION - Title screen addtimer(CALLBACK(SStitle, TYPE_PROC_REF(/datum/controller/subsystem/title, change_title_screen)), 1 SECONDS) //SKYRAT EDIT ADDITION - Title screen //Everyone who wants to be an observer is now spawned @@ -257,10 +259,15 @@ SUBSYSTEM_DEF(ticker) //Configure mode and assign player to antagonists var/can_continue = FALSE // can_continue = SSdynamic.pre_setup() //Choose antagonists // BUBBER EDIT - STORYTELLER (note: maybe disable) + /* BUG REMOVAL START //BUBBER EDIT BEGIN - STORYTELLER SSgamemode.init_storyteller() can_continue = SSgamemode.pre_setup() //BUBBER EDIT END - STORYTELLER + BUG REMOVAL END */ + // BUG ADDITION START + can_continue = SSunified.pre_setup() + // BUG ADDITION END CHECK_TICK SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_JOBS_ASSIGNED, src) @@ -330,7 +337,7 @@ SUBSYSTEM_DEF(ticker) /datum/controller/subsystem/ticker/proc/PostSetup() set waitfor = FALSE SSdynamic.post_setup() - SSgamemode.post_setup() // BUBBER EDIT - Storyteller + SSunified.post_setup() // BUBBER EDIT - Storyteller // BUG EDIT - Unified GLOB.start_state = new /datum/station_state() GLOB.start_state.count() diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 6ada66d3199f..4754e46f2813 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -114,7 +114,8 @@ if(!check_rights(R_ADMIN)) return //SSdynamic.admin_panel() // BUBBER EDIT - STORYTELLER - SSgamemode.ui_interact(usr) // BUBBER EDIT - STORYTELLER + //SSgamemode.ui_interact(usr) // BUBBER EDIT - STORYTELLER // BUG EDIT - Unified + SSunified.admin_panel(usr) // BUG EDIT - Unified else if(href_list["call_shuttle"]) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm index 0e2edb42f620..37f6f0c5609e 100644 --- a/code/modules/events/_event.dm +++ b/code/modules/events/_event.dm @@ -42,9 +42,11 @@ var/map_flags = NONE /datum/round_event_control/New() + SHOULD_CALL_PARENT(TRUE) // BUG EDIT + . = ..() // BUG EDIT if(config && !wizardevent) // Magic is unaffected by configs earliest_start = CEILING(earliest_start * CONFIG_GET(number/events_min_time_mul), 1) - min_players = CEILING(min_players * CONFIG_GET(number/events_min_players_mul), 1) + // min_players = CEILING(min_players * CONFIG_GET(number/events_min_players_mul), 1) // BUG EDIT if(!length(admin_setup)) return var/list/admin_setup_types = admin_setup.Copy() @@ -74,13 +76,13 @@ SHOULD_CALL_PARENT(TRUE) if(occurrences >= max_occurrences) return FALSE - if(!(roundstart ^ SSticker.HasRoundStarted())) // BUBBER EDIT: Roundstart checks added + if(roundstart && !SSticker.HasRoundStarted()) // BUBBER EDIT: Roundstart checks added // BUG EDIT return FALSE - if(weight == 0) // BUBBER EDIT: Weight check added + if(calculated_weight == 0 && unified_cost != 0) // BUBBER EDIT: Weight check added // BUG EDIT return FALSE if(!allow_magic && wizardevent != SSevents.wizardmode) return FALSE - if(players_amt < min_players) + if(players_amt < CEILING(min_players * CONFIG_GET(number/events_min_players_mul), 1)) // BUG EDIT return FALSE if(holidayID && !check_holidays(holidayID)) return FALSE @@ -92,6 +94,11 @@ if (dynamic_should_hijack && SSdynamic.random_event_hijacked != HIJACKED_NOTHING) return FALSE + // BUG ADDITION START + if(calculated_cost > SSunified.points) + return FALSE + // BUG ADDITION END + return TRUE /datum/round_event_control/proc/preRunEvent() diff --git a/code/modules/events/ghost_role/_ghost_role.dm b/code/modules/events/ghost_role/_ghost_role.dm index 336fb4c03bbd..a77f2bfd17e8 100644 --- a/code/modules/events/ghost_role/_ghost_role.dm +++ b/code/modules/events/ghost_role/_ghost_role.dm @@ -55,11 +55,13 @@ if(MAP_ERROR) message_admins("[role_name] cannot be spawned due to a map error.") kill() + SSunified.refund_failed_event(control) // BUG ADDITION return if(NOT_ENOUGH_PLAYERS) message_admins("[role_name] cannot be spawned due to lack of players signing up.") deadchat_broadcast(" did not get enough candidates ([minimum_required]) to spawn.", "[role_name]", message_type=DEADCHAT_ANNOUNCEMENT) kill() + SSunified.refund_failed_event(control) // BUG ADDITION return if(SUCCESSFUL_SPAWN) message_admins("[role_name] spawned successfully.") diff --git a/modular_zubbers/code/modules/storyteller/_events/_event.dm b/modular_zubbers/code/modules/storyteller/_events/_event.dm index 232381eddb3b..2bbc83f7fd2b 100644 --- a/modular_zubbers/code/modules/storyteller/_events/_event.dm +++ b/modular_zubbers/code/modules/storyteller/_events/_event.dm @@ -58,20 +58,31 @@ if(SSticker.HasRoundStarted()) if(roundstart) if(!can_run_post_roundstart) - return "Fire Schedule" + return "Fire" // BUG EDIT return "Fire Schedule" else - return "Fire Schedule Force Next" + return "Fire Schedule" // BUG EDIT else if(roundstart) - return "Force Roundstart" + return "Fire" // BUG EDIT else - return "Fire Schedule Force Next" + return "Schedule" // BUG EDIT /datum/round_event_control/Topic(href, href_list) . = ..() switch(href_list["action"]) + // BUG EDIT START if("force_next") message_admins("[key_name_admin(usr)] has forced scheduled event [src.name].") log_admin_private("[key_name(usr)] has forced scheduled event [src.name].") SSgamemode.force_event(src) + if("fire") + message_admins("[key_name_admin(usr)] has fired event [src.name].") + log_admin_private("[key_name(usr)] has fired event [src.name].") + run_event(admin_forced = TRUE) + if("schedule") + var/delay = input(usr, "Enter the time in seconds to run the event in.", "Schedule Event") as null|num + message_admins("[key_name_admin(usr)] has scheduled event [src.name] to run in [delay] seconds.") + log_admin_private("[key_name(usr)] has scheduled event [src.name] to run in [delay] seconds.") + SSunified.schedule_event(src, delay SECONDS, calculated_cost, TRUE, FALSE) + // BUG EDIT END diff --git a/modular_zubbers/code/modules/storyteller/event_defines/crewset/_antagonist_event.dm b/modular_zubbers/code/modules/storyteller/event_defines/crewset/_antagonist_event.dm index ab2959cb3e76..0bfaeaedc655 100644 --- a/modular_zubbers/code/modules/storyteller/event_defines/crewset/_antagonist_event.dm +++ b/modular_zubbers/code/modules/storyteller/event_defines/crewset/_antagonist_event.dm @@ -67,7 +67,7 @@ . = ..() if(!.) return - if(!roundstart && !SSgamemode.can_inject_antags()) + if(!roundstart && !SSunified.can_inject_antags()) // BUG EDIT return FALSE if(!get_antag_amount()) return FALSE @@ -80,7 +80,7 @@ /datum/round_event_control/antagonist/proc/get_candidates() var/round_started = SSticker.HasRoundStarted() - var/list/candidates = SSgamemode.get_candidates(antag_flag, pick_roundstart_players = !round_started, restricted_roles = restricted_roles) + var/list/candidates = SSunified.get_candidates(antag_flag, pick_roundstart_players = !round_started, restricted_roles = restricted_roles) // BUG EDIT return candidates /datum/round_event_control/antagonist/solo @@ -88,7 +88,7 @@ /datum/round_event_control/antagonist/proc/get_antag_amount() - var/people = SSgamemode.get_correct_popcount() + var/people = SSunified.get_correct_popcount() // BUG EDIT var/amount = base_antags + FLOOR(people / denominator, 1) if(antag_datum && maximum_antags_global > 0) diff --git a/modular_zubbers/code/modules/storyteller/gamemode.dm b/modular_zubbers/code/modules/storyteller/gamemode.dm index d8e7170da439..7334f6d531c9 100644 --- a/modular_zubbers/code/modules/storyteller/gamemode.dm +++ b/modular_zubbers/code/modules/storyteller/gamemode.dm @@ -688,9 +688,11 @@ SUBSYSTEM_DEF(gamemode) if((storyboy.population_min && storyboy.population_min > client_amount) || (storyboy.population_max && storyboy.population_max < client_amount)) continue choices += storyboy.name + /* BUG REMOVAL START ///Because the vote subsystem is dumb and does not support any descriptions, we dump them into world. to_chat(world, span_notice("[storyboy.name]")) to_chat(world, span_notice("[storyboy.desc]")) + BUG REMOVAL END */ return choices /datum/controller/subsystem/gamemode/proc/storyteller_vote_result(winner_name) diff --git a/modular_zubbers/code/modules/storyteller/scheduled_event.dm b/modular_zubbers/code/modules/storyteller/scheduled_event.dm index ab196d7baf47..f0a38eb01b08 100644 --- a/modular_zubbers/code/modules/storyteller/scheduled_event.dm +++ b/modular_zubbers/code/modules/storyteller/scheduled_event.dm @@ -1,4 +1,4 @@ -///Scheduled event datum for SSgamemode to put events into. +///Scheduled event datum for SSunified to put events into. // BUG EDIT /datum/scheduled_event /// What event are scheduling. var/datum/round_event_control/event @@ -35,7 +35,7 @@ /// For admins who want to reschedule the event. /datum/scheduled_event/proc/reschedule(new_time) start_time = new_time - alerted_admins = FALSE + // alerted_admins = FALSE // BUG EDIT /datum/scheduled_event/proc/get_href_actions() var/round_started = SSticker.HasRoundStarted() @@ -54,15 +54,43 @@ message_admins("Scheduled Event: [event] was unable to run and has been refunded.") log_admin("Scheduled Event: [event] was unable to run and has been refunded.") - SSgamemode.refund_scheduled_event(src) + SSunified.refund_scheduled_event(src) // BUG EDIT return ///Trigger the event and remove the scheduled datum message_admins("Scheduled Event: [event] successfully triggered.") - SSgamemode.TriggerEvent(event) - SSgamemode.remove_scheduled_event(src) + SSunified.TriggerEvent(event) // BUG EDIT + SSunified.remove_scheduled_event(src) // BUG EDIT /datum/scheduled_event/Destroy() remove_occurence() event = null return ..() + +/datum/scheduled_event/Topic(href, href_list) + . = ..() + if(QDELETED(src)) + return + var/round_started = SSticker.HasRoundStarted() + switch(href_list["action"]) + if("cancel") + message_admins("[key_name_admin(usr)] cancelled scheduled event [event.name].") + log_admin_private("[key_name(usr)] cancelled scheduled event [event.name].") + SSunified.remove_scheduled_event(src) // BUG EDIT + if("refund") + message_admins("[key_name_admin(usr)] refunded scheduled event [event.name].") + log_admin_private("[key_name(usr)] refunded scheduled event [event.name].") + SSunified.refund_scheduled_event(src) // BUG EDIT + if("reschedule") + var/new_schedule = input(usr, "New schedule time (in seconds):", "Reschedule Event") as num|null + if(isnull(new_schedule) || QDELETED(src)) + return + start_time = world.time + new_schedule * 1 SECONDS + message_admins("[key_name_admin(usr)] rescheduled event [event.name] to [new_schedule] seconds.") + log_admin_private("[key_name(usr)] rescheduled event [event.name] to [new_schedule] seconds.") + if("fire") + if(!round_started) + return + message_admins("[key_name_admin(usr)] has fired scheduled event [event.name].") + log_admin_private("[key_name(usr)] has fired scheduled event [event.name].") + try_fire() diff --git a/modular_zubbers/code/modules/storyteller/storyteller_vote.dm b/modular_zubbers/code/modules/storyteller/storyteller_vote.dm index 42a4ca665e32..85606cec71a0 100644 --- a/modular_zubbers/code/modules/storyteller/storyteller_vote.dm +++ b/modular_zubbers/code/modules/storyteller/storyteller_vote.dm @@ -51,10 +51,12 @@ We then just check what the last one is in SSgamemode.storyteller_vote_choices() #define STORYTELLER_LAST_FILEPATH "data/storyteller_last_round.txt" +/* BUG REMOVAL START - unified /// Extends collect_data /datum/controller/subsystem/persistence/collect_data() . = ..() collect_storyteller_type() +BUG REMOVAL END */ /// Loads last storyteller into last_storyteller_type /datum/controller/subsystem/persistence/proc/load_storyteller_type() diff --git a/modular_zzbug/code/__DEFINES/unified_defines.dm b/modular_zzbug/code/__DEFINES/unified_defines.dm new file mode 100644 index 000000000000..4e004757387d --- /dev/null +++ b/modular_zzbug/code/__DEFINES/unified_defines.dm @@ -0,0 +1,29 @@ +#define TAG_ENGINEERING "engineering" +#define TAG_MEDICAL "medical" +#define TAG_SECURITY "security" +#define TAG_SCIENCE "science" +#define TAG_WIZARD "wizard" + +#define UNIFIED_WAIT_TIME 20 SECONDS + +#define UNIFIED_PANEL_MAIN "Main" +#define UNIFIED_PANEL_VARIABLES "Variables" + +#define COST_VERY_MINOR 5 +#define COST_MINOR 10 +#define COST_MODERATE 20 +#define COST_SEMIMAJOR 30 +#define COST_MAJOR 60 +#define COST_SUPERMAJOR BASE_POINTS * 1.25 + +#define WEIGHT_NORMAL 10 +#define WEIGHT_LESS_LIKELY 7.5 +#define WEIGHT_UNLIKELY 5 + +// TODO add to config +#define BASE_POINTS 120 + +#define STARTING_DELAY 10 * BASE_POINTS/starting_points MINUTES +#define SCHEDULE_DELAY 3 MINUTES + +#define COOLDOWN_MULT (120/BASE_POINTS) * (BASE_POINTS/starting_points) diff --git a/modular_zzbug/code/modules/unified/_event.dm b/modular_zzbug/code/modules/unified/_event.dm new file mode 100644 index 000000000000..753bf3b2a2f2 --- /dev/null +++ b/modular_zzbug/code/modules/unified/_event.dm @@ -0,0 +1,9 @@ +/datum/round_event_control + /// This event's cost in the Unified system. Equates to a cooldown in minutes for the event. + var/unified_cost = 0 + + /// The cooldown in minutes to use instead of the cost. + var/cooldown_override = 0 + + /// Last calculated weight that Unified assigned this event + var/calculated_cost = 0 diff --git a/modular_zzbug/code/modules/unified/divergency_report.dm b/modular_zzbug/code/modules/unified/divergency_report.dm new file mode 100644 index 000000000000..f85f038eda5d --- /dev/null +++ b/modular_zzbug/code/modules/unified/divergency_report.dm @@ -0,0 +1,70 @@ +/datum/controller/subsystem/unified/proc/send_trait_report() + . = "Central Command Status Summary
" + + . += "" + if(starting_points < BASE_POINTS * 0.75) + . += "Low" + else if(starting_points < BASE_POINTS * 1.25) + . += "Moderate" + else + . += "High" + . += " station threat detected." + + SSstation.generate_station_goals(20) + + var/list/station_goals = SSstation.get_station_goals() + + if(!length(station_goals)) + . += "
No assigned goals.
" + else + . += generate_station_goal_report(station_goals) + if(!SSstation.station_traits.len) + . += "
No identified shift divergencies.
" + else + . += generate_station_trait_report() + + . += "
This concludes your shift-start evaluation. Have a secure shift!
\ +

This label certifies an Intern has reviewed the above before sending. This document is the property of Nanotrasen Corporation.

" + + print_command_report(., "Central Command Status Summary", announce = FALSE) + priority_announce("Hello, crew of [station_name()]. Our intern has finished their shift-start divergency and goals evaluation, which has been sent to your communications console. Have a secure shift!", "Divergency Report", SSstation.announcer.get_rand_report_sound()) + + + +/* + * Generate a list of station goals available to purchase to report to the crew. + * + * Returns a formatted string all station goals that are available to the station. + */ +/datum/controller/subsystem/unified/proc/generate_station_goal_report(var/list/station_goals) + . = "
Special Orders for [station_name()]:
" + var/list/goal_reports = list() + for(var/datum/station_goal/station_goal as anything in station_goals) + station_goal.on_report() + goal_reports += station_goal.get_report() + + . += goal_reports.Join("
") + return +/* + * Generate a list of active station traits to report to the crew. + * + * Returns a formatted string of all station traits (that are shown) affecting the station. + */ +/datum/controller/subsystem/unified/proc/generate_station_trait_report() + if(!SSstation.station_traits.len) + return + . = "
Identified shift divergencies:
" + for(var/datum/station_trait/station_trait as anything in SSstation.station_traits) + if(!station_trait.show_in_report) + continue + . += "[station_trait.get_report()]
" + return + +/*/datum/controller/subsystem/unified/proc/generate_station_goals() + var/list/possible = subtypesof(/datum/station_goal) + var/goal_weights = 0 + while(possible.len && goal_weights < 1) // station goal budget is 1 + var/datum/station_goal/picked = pick_n_take(possible) + goal_weights += initial(picked.weight) + SSstation.goals_by_type += new picked // does this still work? +*/ diff --git a/modular_zzbug/code/modules/unified/event_overrides.dm b/modular_zzbug/code/modules/unified/event_overrides.dm new file mode 100644 index 000000000000..2268de5b00e1 --- /dev/null +++ b/modular_zzbug/code/modules/unified/event_overrides.dm @@ -0,0 +1,650 @@ +// SCRUBBERS EVENTS + +/datum/round_event_control/scrubber_overflow/threatening + max_occurrences = 0 + +/datum/round_event_control/scrubber_overflow/catastrophic + max_occurrences = 0 + +/datum/round_event_control/scrubber_overflow/every_vent + max_occurrences = 0 + +/datum/round_event_control/scrubber_overflow/ices + max_occurrences = 0 + +// MUNDANE EVENTS + +/datum/round_event_control/bitrunning_glitch + unified_cost = COST_VERY_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/sentience + unified_cost = COST_VERY_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/aurora_caelus + unified_cost = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/camera_failure + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/space_dust + unified_cost = COST_MINOR + max_occurrences = 0 // space dust is a nothing event + weight = WEIGHT_NORMAL + +/datum/round_event_control/space_dust/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/electrical_storm + unified_cost = COST_MINOR + weight = WEIGHT_UNLIKELY // it's annoying + weight = WEIGHT_NORMAL + +/datum/round_event_control/fake_virus + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/falsealarm + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/market_crash + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/mice_migration + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/mice_migration/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/wisdomcow + unified_cost = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/shuttle_loan + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/mass_hallucination + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/stray_cargo + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/stray_cargo/syndicate + weight = WEIGHT_LESS_LIKELY + +/datum/round_event_control/grey_tide + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/grey_tide/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/gravity_generator_blackout + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/gravity_generator_blackout/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/shuttle_insurance + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/tram_malfunction + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/tram_malfunction/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/grid_check + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/bureaucratic_error + unified_cost = COST_MINOR + weight = WEIGHT_UNLIKELY // It's annoying + weight = WEIGHT_NORMAL + +/datum/round_event_control/vent_clog + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +// /datum/round_event_control/anomaly + +/datum/round_event_control/anomaly/New() + . = ..() + tags |= list(TAG_SCIENCE) + +/datum/round_event_control/anomaly/anomaly_hallucination + unified_cost = COST_VERY_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_grav + unified_cost = COST_VERY_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_grav/high + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_grav/high/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/anomaly/anomaly_bioscrambler + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_bioscrambler/New() + . = ..() + tags |= list(TAG_MEDICAL) + +/datum/round_event_control/anomaly/anomaly_bluespace + unified_cost = COST_MINOR + weight = WEIGHT_LESS_LIKELY // Our stuff being teleported away is kinda annoying but not too bad + +/datum/round_event_control/vent_clog/strange + unified_cost = COST_MINOR + max_occurrences = 0 // none of this for now, not sure about the animals in it + weight = WEIGHT_NORMAL + +/datum/round_event_control/scrubber_overflow + unified_cost = COST_MINOR + weight = WEIGHT_NORMAL + +// MODERATE EVENTS + +/datum/round_event_control/meteor_wave/dust_storm + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/brain_trauma + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/brain_trauma/New() + . = ..() + tags |= list(TAG_MEDICAL) + +/datum/round_event_control/supermatter_surge + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/supermatter_surge/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/brand_intelligence + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/brand_intelligence/New() + . = ..() + tags |= list(TAG_ENGINEERING, TAG_MEDICAL) + +/datum/round_event_control/carp_migration + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/carp_migration/New() + . = ..() + tags |= list(TAG_SECURITY, TAG_MEDICAL) + +/datum/round_event_control/communications_blackout + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/ion_storm + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/processor_overload + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/processor_overload/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/radiation_leak + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/radiation_leak/New() + . = ..() + tags |= list(TAG_ENGINEERING, TAG_MEDICAL) + +/datum/round_event_control/sandstorm + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/sandstorm/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/shuttle_catastrophe + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/spacevine + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/portal_storm_syndicate + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/portal_storm_syndicate/New() + . = ..() + tags |= list(TAG_SECURITY, TAG_MEDICAL) + +// BUG EDIT START +/datum/round_event_control/radiation_storm + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + max_occurrences = 2 + +/datum/round_event_control/radiation_storm/New() + . = ..() + tags |= list(TAG_MEDICAL) + +/datum/round_event_control/wormholes + unified_cost = COST_MODERATE + max_occurrences = 2 // BUG: more than two wormholes would be pretty annoying, like the radstorm + weight = WEIGHT_NORMAL + +/datum/round_event_control/wormholes/New() + . = ..() + tags |= list(TAG_MEDICAL) + +/datum/round_event_control/heart_attack + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/heart_attack/New() + . = ..() + tags |= list(TAG_MEDICAL) + +/datum/round_event_control/appendicitis + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/appendicitis/New() + . = ..() + tags |= list(TAG_MEDICAL) + +/datum/round_event_control/disease_outbreak + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/disease_outbreak/New() + . = ..() + tags |= list(TAG_MEDICAL) + +/datum/round_event_control/disease_outbreak/advanced + unified_cost = COST_MODERATE + weight = WEIGHT_UNLIKELY + +/datum/round_event_control/anomaly/anomaly_dimensional + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_dimensional/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/anomaly/anomaly_ectoplasm + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_flux + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_flux/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/vent_clog/major + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/vent_clog/major/New() + . = ..() + tags |= list(TAG_SECURITY) + +/datum/round_event_control/vent_clog/critical + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +/datum/round_event_control/vent_clog/critical/New() + . = ..() + tags |= list(TAG_SECURITY) + +/datum/round_event_control/mold + unified_cost = COST_MAJOR + weight = WEIGHT_UNLIKELY + +/datum/round_event_control/mold/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +// MAJOR EVENTS + +/datum/round_event_control/pirates + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/pirates/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +/datum/round_event_control/alien_infestation + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/alien_infestation/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/space_dragon + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/space_dragon/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +/datum/round_event_control/cortical_borer + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/cortical_borer/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/earthquake + unified_cost = COST_SEMIMAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/earthquake/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/blob + unified_cost = COST_SUPERMAJOR + weight = WEIGHT_UNLIKELY + +/datum/round_event_control/blob/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING) + +/datum/round_event_control/meteor_wave + unified_cost = COST_SEMIMAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/meteor_wave/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING) + +/datum/round_event_control/meteor_wave/meaty + unified_cost = COST_SEMIMAJOR + weight = WEIGHT_UNLIKELY // meat meteors?? how queer + +/datum/round_event_control/meteor_wave/ices + max_occurrences = 0 + +/datum/round_event_control/immovable_rod + unified_cost = COST_SEMIMAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/immovable_rod/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING) + +/datum/round_event_control/stray_meteor + unified_cost = COST_SEMIMAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/stray_meteor/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/anomaly/anomaly_vortex + unified_cost = COST_SEMIMAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_vortex/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/anomaly/anomaly_pyro + unified_cost = COST_SEMIMAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/anomaly/anomaly_pyro/New() + . = ..() + tags |= list(TAG_ENGINEERING) + +/datum/round_event_control/spider_infestation + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/spider_infestation/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/revenant + min_players = 20 + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/abductor + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/abductor/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/fugitives + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/fugitives/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/voidwalker + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/voidwalker/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/cme + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/cme/New() + . = ..() + tags |= list(TAG_ENGINEERING, TAG_SCIENCE) + +/datum/round_event_control/stray_cargo/changeling_zombie + unified_cost = COST_MAJOR + weight = WEIGHT_NORMAL + +/datum/round_event_control/stray_cargo/changeling_zombie/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +// ANTAGS + +/datum/round_event_control/antagonist + unified_cost = COST_SEMIMAJOR + cooldown_override = 10 + +/datum/round_event_control/antagonist/solo/bloodsucker + min_players = 0 + base_antags = 1 + maximum_antags = INFINITY + maximum_antags_global = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/antagonist/solo/bloodsucker/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/antagonist/solo/changeling + min_players = 0 + base_antags = 1 + maximum_antags = INFINITY + maximum_antags_global = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/antagonist/solo/changeling/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/antagonist/solo/heretic + min_players = 0 + base_antags = 1 + maximum_antags = INFINITY + maximum_antags_global = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/antagonist/solo/heretic/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +/datum/round_event_control/antagonist/solo/malf + min_players = 0 + base_antags = 1 + maximum_antags = INFINITY + maximum_antags_global = 0 + weight = WEIGHT_NORMAL + + min_players = 20 + +/datum/round_event_control/antagonist/solo/malf/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +/datum/round_event_control/antagonist/team/nuke_ops + unified_cost = COST_SUPERMAJOR + weight = WEIGHT_UNLIKELY + min_players = 0 + base_antags = 2 + maximum_antags = INFINITY + maximum_antags_global = 5 + weight = WEIGHT_NORMAL + +/datum/round_event_control/antagonist/team/nuke_ops/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +/datum/round_event_control/antagonist/obsessed + unified_cost = COST_MODERATE + min_players = 0 + base_antags = 1 + maximum_antags = INFINITY + maximum_antags_global = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/antagonist/obsessed/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_SECURITY) + +/datum/round_event_control/antagonist/obsessed/midround + name = "Obsessed (Midround)" + roundstart = FALSE + +/datum/round_event_control/antagonist/solo/spy + min_players = 0 + base_antags = 1 + maximum_antags = INFINITY + maximum_antags_global = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/antagonist/solo/spy/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +/datum/round_event_control/antagonist/solo/traitor + min_players = 0 + base_antags = 1 + maximum_antags = INFINITY + maximum_antags_global = 0 + weight = WEIGHT_NORMAL + +/datum/round_event_control/antagonist/solo/traitor/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +// WIZARD EVENTS + +// /datum/round_event_control/wizard + +/datum/round_event_control/wizard/New() + . = ..() + tags = list(TAG_WIZARD) + +// MISC + +/datum/round_event_control/operative + // Don't override weight for operative! + unified_cost = COST_MAJOR + +/datum/round_event_control/operative/New() + . = ..() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) + +/datum/round_event_control/changeling + max_occurrences = 0 // TODO add an antag version of this + //unified_cost = COST_MAJOR + //cooldown_override = 5 +/* +/datum/round_event_control/changeling/New() + tags |= list(TAG_MEDICAL, TAG_SECURITY) +*/ +/datum/round_event_control/nightmare + max_occurrences = 0 // TODO add an antag version of this + //unified_cost = COST_MAJOR +/* +/datum/round_event_control/nightmare/New() + tags |= list(TAG_MEDICAL, TAG_ENGINEERING, TAG_SECURITY) +*/ +/datum/round_event_control/wizard_dice + unified_cost = COST_MODERATE + weight = WEIGHT_NORMAL + +// /datum/round_event_control/wizard_dice/New() + +/datum/round_event_control/space_ninja + max_occurrences = 0 // TODO add an antag version of this + unified_cost = COST_MAJOR +/* +/datum/round_event_control/space_ninja/New() + tags |= list(TAG_MEDICAL, TAG_SECURITY) +*/ +/datum/round_event_control/obsessed + max_occurrences = 0 // We already have an antag version of this event + +/datum/round_event_control/morph + max_occurrences = 0 // TODO add an antag version of this diff --git a/modular_zzbug/code/modules/unified/unified.dm b/modular_zzbug/code/modules/unified/unified.dm new file mode 100644 index 000000000000..860ec6ae4443 --- /dev/null +++ b/modular_zzbug/code/modules/unified/unified.dm @@ -0,0 +1,887 @@ +#define INIT_ORDER_UNIFIED 70 + +SUBSYSTEM_DEF(unified) + name = "Unified" + init_order = INIT_ORDER_UNIFIED + runlevels = RUNLEVEL_GAME + flags = SS_BACKGROUND | SS_KEEP_TIMING + wait = 2 SECONDS + + /// Next process for our storyteller. The wait time is STORYTELLER_WAIT_TIME + var/next_process = 0 + + /// Our total event point budget for the shift. Initialized in pre_setup() + var/points = 0 + /// Our starting event point budget for the shift. Initialized in pre_setup() + var/starting_points = 0 + + /// The times after which we can schedule another event + var/list/cooldown_dates = list(0, 0) + + /// Whether we allow pop scaling. This is configured by config, or the storyteller UI + var/allow_pop_scaling = TRUE + + /// Events that we have scheduled to run in the nearby future + var/list/scheduled_events = list() + + /// For admins to force events (though they can still invoke them freely outside of the track system) + var/datum/round_event_control/forced_next_event + + var/list/control = list() //list of all datum/round_event_control. Used for selecting events based on weight and occurrences. + var/list/running = list() //list of all existing /datum/round_event + var/list/currentrun = list() + + /// List of all uncategorized events, because they were wizard or holiday events + var/list/uncategorized = list() + + var/list/holidays //List of all holidays occuring today or null if no holidays + + /// Event frequency multiplier, it exists because wizard, eugh. + var/event_frequency_multiplier = 1 + + /// Current preview page for the statistics UI. + var/statistics_track_page = EVENT_TRACK_MUNDANE + /// Page of the UI panel. + var/panel_page = UNIFIED_PANEL_MAIN + /// Whether we are viewing the roundstart events or not + var/roundstart_event_view = TRUE + + /// Whether the gamemode has been halted + var/halted = FALSE + + /// Ready players for roundstart events. + var/ready_players = 0 + var/active_players = 0 + var/head_crew = 0 + var/eng_crew = 0 + var/sec_crew = 0 + var/med_crew = 0 + var/sci_crew = 0 + + var/wizardmode = FALSE + + var/storyteller_voted = FALSE + + /// Whether we account for currently filled jobs when scheduling events TODO add to config + var/allow_job_weighting = TRUE + + // TODO add to config + var/antag_divisor = 8 + + /// % chance of having an antag created at roundstart + var/roundstart_event_chance = 40 + + /// List of all datum/round_event_control with roundstart=true. + var/list/roundstart_control = list() + +/datum/controller/subsystem/unified/Initialize(time, zlevel) + for(var/type in typesof(/datum/round_event_control)) + var/datum/round_event_control/event = new type() + if(!event.typepath || !event.name || !event.valid_for_map()) + continue //don't want this one! leave it for the garbage collector + if(event.roundstart) + roundstart_control += event + else + control += event //add it to the list of all events (controls) + getHoliday() + + load_config_vars() + load_event_config_vars() + +/datum/controller/subsystem/unified/fire(resumed = FALSE) + if(!resumed) + src.currentrun = running.Copy() + + ///Handle scheduled events + for(var/datum/scheduled_event/sch_event in scheduled_events) + if(world.time >= sch_event.start_time) + sch_event.try_fire() + else if(!sch_event.alerted_admins && world.time >= sch_event.start_time - 1 MINUTES) + ///Alert admins 1 minute before running and allow them to cancel or refund the event, once again. + sch_event.alerted_admins = TRUE + message_admins("Scheduled Event: [sch_event.event] will run in [(sch_event.start_time - world.time) / 10] seconds. (CANCEL) (REFUND)") + + if(!halted) + for(var/i in 1 to cooldown_dates.len) + if(cooldown_dates[i] <= world.time) + buy_event(control, i) + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + + while(currentrun.len) + var/datum/thing = currentrun[currentrun.len] + currentrun.len-- + if(thing) + thing.process(wait * 0.1) + else + running.Remove(thing) + if (MC_TICK_CHECK) + return + +/datum/controller/subsystem/unified/proc/buy_event(list/events, cooldown_num = 0, run_now = FALSE) + . = FALSE + + var/datum/round_event_control/picked_event + var/candidate_events = events + var/player_pop = SSunified.get_correct_popcount() + calculate_weights(candidate_events) + calculate_costs(candidate_events) + var/list/valid_events = list() + // Determine which events are valid to pick + + for(var/datum/round_event_control/event as anything in candidate_events) + if(isnull(event)) + continue + if(event.unified_cost == 0) // free events are handled in handle_free_events + continue + if(event.can_spawn_event(player_pop)) + valid_events[event] = event.calculated_weight + if(!length(valid_events)) + if(SSticker.HasRoundStarted()) + message_admins("Unified failed to pick an event. Suspending.") + log_admin("Unified failed to pick an event. Suspending.") + halted = TRUE; + return + picked_event = pick_weight(valid_events) + if(!picked_event) + message_admins("WARNING: Unified picked a null from event pool. Aborting event roll.") + log_admin("WARNING: Unified picked a null from event pool. Aborting event roll.") + stack_trace("WARNING: Unified picked a null from event pool.") + return + + points -= picked_event.calculated_cost // we already know that valid events cost less than our current points + message_admins("Unified purchased and triggered [picked_event] event for [picked_event.calculated_cost] cost.") + log_admin("Unified purchased and triggered [picked_event] event for [picked_event.calculated_cost] cost.") + if(picked_event.roundstart || run_now) + TriggerEvent(picked_event) + else + schedule_event(picked_event, 3 MINUTES, picked_event.calculated_cost) // We already randomize cooldown, we don't need to randomize this + if(cooldown_num) + var/cooldown // in minutes + if(picked_event.cooldown_override) + cooldown = rand(picked_event.cooldown_override*0.5, picked_event.cooldown_override*1.5) + else + cooldown = rand(picked_event.calculated_cost*0.5, picked_event.calculated_cost*1.5) + cooldown *= COOLDOWN_MULT // the higher the starting points, the lower the cooldown + cooldown_dates[cooldown_num] = world.time + cooldown MINUTES // convert cooldown to deciseconds + + . = TRUE + +/datum/controller/subsystem/unified/proc/calculate_costs(list/events) + for(var/datum/round_event_control/event in events) + var/total_cost = event.unified_cost + /* + if(allow_job_weighting) + var/job_cost = 1 + if((TAG_ENGINEERING in event.tags) && eng_crew == 0) + job_cost *= 2 + if((TAG_MEDICAL in event.tags) && med_crew == 0) + job_cost *= 2 + if((TAG_SECURITY in event.tags) && sec_crew == 0) + job_cost *= 2 + if((TAG_SCIENCE in event.tags) && sci_crew == 0) + job_cost *= 2 + total_cost *= job_cost + */ + if(istype(event, /datum/round_event_control/antagonist)) + total_cost /= get_antag_cap() + event.calculated_cost = total_cost + +/datum/controller/subsystem/unified/proc/calculate_weights(list/events) + for(var/datum/round_event_control/event in events) + var/weight_total = event.weight + if(allow_job_weighting) + if((TAG_ENGINEERING in event.tags) && eng_crew == 0) + weight_total = 0 + else if((TAG_MEDICAL in event.tags) && med_crew == 0) + weight_total = 0 + else if((TAG_SECURITY in event.tags) && sec_crew == 0) + weight_total = 0 + else if((TAG_SCIENCE in event.tags) && sci_crew == 0) + weight_total = 0 + /// Apply occurence multipliers if able + var/occurences = event.get_occurences() + if(occurences) + ///If the event has occured already, apply a penalty multiplier based on amount of occurences + weight_total *= event.reoccurence_penalty_multiplier ** occurences + /// Write it + event.calculated_weight = weight_total + +/// Gets the number of antagonists the antagonist injection events will stop rolling after. +/datum/controller/subsystem/unified/proc/get_antag_cap() + return round(max(min(get_correct_popcount() / antag_divisor + sec_crew,sec_crew*1.5),ANTAG_CAP_FLAT)) + +/// Whether events can inject more antagonists into the round +/datum/controller/subsystem/unified/proc/can_inject_antags() + return (get_antag_cap() > length(GLOB.antagonists)) + +/// Gets candidates for antagonist roles. + +/// Todo: Split into get_candidates and post_get_candidates +/datum/controller/subsystem/unified/proc/get_candidates( + special_role_flag, + pick_observers, + pick_roundstart_players, + required_time, + inherit_required_time = TRUE, + no_antags = TRUE, + list/restricted_roles, + ) + + + var/list/candidates = list() + var/list/candidate_candidates = list() //lol + if(pick_roundstart_players) + for(var/mob/dead/new_player/player in GLOB.new_player_list) + if(player.ready == PLAYER_READY_TO_PLAY && player.mind && player.check_preferences()) + candidate_candidates += player + else if(pick_observers) + for(var/mob/player as anything in GLOB.dead_mob_list) + candidate_candidates += player + else + for(var/datum/record/locked/manifest_log as anything in GLOB.manifest.locked) + var/datum/mind/player_mind = manifest_log.mind_ref.resolve() + var/mob/living/player = player_mind.current + if(isnull(player)) + continue + candidate_candidates += player + + + for(var/mob/candidate as anything in candidate_candidates) + if(QDELETED(candidate) || !candidate.key || !candidate.client || !candidate.mind) + continue + if(no_antags && candidate.mind.special_role) + continue + if(restricted_roles && (candidate.mind.assigned_role.title in restricted_roles)) + continue + if(special_role_flag) + if(!(candidate.client.prefs) || !(special_role_flag in candidate.client.prefs.be_special)) + continue + + var/time_to_check + if(required_time) + time_to_check = required_time + else if (inherit_required_time) + time_to_check = GLOB.special_roles[special_role_flag] + + if(time_to_check && candidate.client.get_remaining_days(time_to_check) > 0) + continue + + if(special_role_flag && is_banned_from(candidate.ckey, list(special_role_flag, ROLE_SYNDICATE))) + continue + if(is_banned_from(candidate.client.ckey, BAN_ANTAGONIST)) + continue + if(!candidate.client?.prefs?.read_preference(/datum/preference/toggle/be_antag)) + continue + candidates += candidate + return candidates + +/// Gets the correct popcount, returning READY people if roundstart, and active people if not. +/datum/controller/subsystem/unified/proc/get_correct_popcount() + if(SSticker.HasRoundStarted()) + update_crew_infos() + return active_players + else + update_ready_crew_infos() + return ready_players + +/// Refunds and removes a scheduled event +/datum/controller/subsystem/unified/proc/refund_scheduled_event(datum/scheduled_event/refunded) + points += refunded.cost + remove_scheduled_event(refunded) + +/// Refunds a failed event +/datum/controller/subsystem/unified/proc/refund_failed_event(datum/round_event_control/failed) + points += failed.calculated_cost + +/// Schedules an event. +/datum/controller/subsystem/unified/proc/force_event(datum/round_event_control/event) + forced_next_event = event + +/// Removes a scheduled event. +/datum/controller/subsystem/unified/proc/remove_scheduled_event(datum/scheduled_event/removed) + scheduled_events -= removed + qdel(removed) + +/// Because roundstart events need 2 steps of firing for purposes of antags, here is the first step handled, happening before occupation division. +/datum/controller/subsystem/unified/proc/handle_pre_setup_roundstart_events() + if(halted) + message_admins("WARNING: Didn't roll any events (including antagonists) due to Unified being halted.") + return + for(var/i in 1 to get_antag_cap()) + if(prob(roundstart_event_chance)) + buy_event(roundstart_control) + +/// Second step of handlind roundstart events, happening after people spawn. +/datum/controller/subsystem/unified/proc/handle_post_setup_roundstart_events() + /// Start all roundstart events on post_setup immediately + for(var/datum/round_event/event as anything in running) + if(!event.control.roundstart) + continue + ASYNC + event.try_start() + +/// Schedules an event to run later. +/datum/controller/subsystem/unified/proc/schedule_event(datum/round_event_control/passed_event, passed_time, passed_cost, passed_ignore, passed_announce) + var/datum/scheduled_event/scheduled = new (passed_event, world.time + passed_time, passed_cost, passed_ignore, passed_announce) + message_admins("Event: [passed_event] has been scheduled to run in [passed_time / 10] seconds. (CANCEL) (REFUND)") + scheduled_events += scheduled + +/datum/controller/subsystem/unified/proc/update_crew_infos() + // Very similar logic to `get_active_player_count()` + active_players = 0 + head_crew = 0 + eng_crew = 0 + med_crew = 0 + sec_crew = 0 + sci_crew = 0 + for(var/mob/player_mob as anything in GLOB.player_list) + if(!player_mob.client) + continue + if(player_mob.stat) //If they're alive + continue + if(player_mob.client.is_afk()) //If afk + continue + if(!ishuman(player_mob)) + continue + active_players++ + if(player_mob.mind?.assigned_role) + var/datum/job/player_role = player_mob.mind.assigned_role + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) + head_crew++ + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_ENGINEERING) + eng_crew++ + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_MEDICAL) + med_crew++ + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY) + sec_crew++ + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_SCIENCE) + sci_crew++ + // update_pop_scaling() TODO FIX + +/datum/controller/subsystem/unified/proc/update_ready_crew_infos() + // Very similar logic to `get_active_player_count()` + ready_players = 0 + head_crew = 0 + eng_crew = 0 + med_crew = 0 + sec_crew = 0 + sci_crew = 0 + var/list/players = list() + + //This fills the readied players list that the job estimation panel uses. + for(var/mob/dead/new_player/player as anything in GLOB.new_player_list) + if(player.ready == PLAYER_READY_TO_PLAY) + players[player.key] = player + ready_players++ + ready_players++ + sortTim(players, GLOBAL_PROC_REF(cmp_text_asc)) + + for(var/ckey in players) + var/mob/dead/new_player/player = players[ckey] + var/datum/preferences/prefs = player.client?.prefs + var/datum/job/J = prefs?.get_highest_priority_job() + if(J.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) + head_crew++ + if(J.departments_bitflags & DEPARTMENT_BITFLAG_ENGINEERING) + eng_crew++ + if(J.departments_bitflags & DEPARTMENT_BITFLAG_MEDICAL) + med_crew++ + if(J.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY) + sec_crew++ + if(J.departments_bitflags & DEPARTMENT_BITFLAG_SCIENCE) + sci_crew++ + +/* TODO FIX +/datum/controller/subsystem/unified/proc/update_pop_scaling() + for(var/track in event_tracks) + var/low_pop_bound = min_pop_thresholds[track] + var/high_pop_bound = pop_scale_thresholds[track] + var/scale_penalty = pop_scale_penalties[track] + + var/perceived_pop = min(max(low_pop_bound, active_players), high_pop_bound) + + var/divisor = high_pop_bound - low_pop_bound + /// If the bounds are equal, we'd be dividing by zero or worse, if upper is smaller than lower, we'd be increasing the factor, just make it 1 and continue. + /// this is only a problem for bad configs + if(divisor <= 0) + current_pop_scale_multipliers[track] = 1 + continue + var/scalar = (perceived_pop - low_pop_bound) / divisor + var/penalty = scale_penalty - (scale_penalty * scalar) + var/calculated_multiplier = 1 - (penalty / 100) + + current_pop_scale_multipliers[track] = calculated_multiplier +*/ + +/datum/controller/subsystem/unified/proc/TriggerEvent(datum/round_event_control/event) + . = event.preRunEvent() + if(. == EVENT_CANT_RUN)//we couldn't run this event for some reason, set its max_occurrences to 0 + event.max_occurrences = 0 + else if(. == EVENT_READY) + event.run_event(random = TRUE) // fallback to dynamic + +///Resets frequency multiplier. +/datum/controller/subsystem/unified/proc/resetFrequency() + event_frequency_multiplier = 1 + + +////////////// +// HOLIDAYS // +////////////// +//Uncommenting ALLOW_HOLIDAYS in config.txt will enable holidays + +//It's easy to add stuff. Just add a holiday datum in code/modules/holiday/holidays.dm +//You can then check if it's a special day in any code in the game by doing if(SSunified.holidays["Groundhog Day"]) + +//You can also make holiday random events easily thanks to Pete/Gia's system. +//simply make a random event normally, then assign it a holidayID string which matches the holiday's name. +//Anything with a holidayID, which isn't in the holidays list, will never occur. + +//Please, Don't spam stuff up with stupid stuff (key example being april-fools Pooh/ERP/etc), +//And don't forget: CHECK YOUR CODE!!!! We don't want any zero-day bugs which happen only on holidays and never get found/fixed! + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +//sets up the holidays and holidays list +/datum/controller/subsystem/unified/proc/getHoliday() + if(!CONFIG_GET(flag/allow_holidays)) + return // Holiday stuff was not enabled in the config! + for(var/H in subtypesof(/datum/holiday)) + var/datum/holiday/holiday = new H() + var/delete_holiday = TRUE + for(var/timezone in holiday.timezones) + var/time_in_timezone = world.realtime + timezone HOURS + + var/YYYY = text2num(time2text(time_in_timezone, "YYYY")) // get the current year + var/MM = text2num(time2text(time_in_timezone, "MM")) // get the current month + var/DD = text2num(time2text(time_in_timezone, "DD")) // get the current day + var/DDD = time2text(time_in_timezone, "DDD") // get the current weekday + + if(holiday.shouldCelebrate(DD, MM, YYYY, DDD)) + holiday.celebrate() + LAZYSET(holidays, holiday.name, holiday) + delete_holiday = FALSE + break + if(delete_holiday) + qdel(holiday) + + if(holidays) + holidays = shuffle(holidays) + // regenerate station name because holiday prefixes. + set_station_name(new_station_name()) + world.update_status() + +/datum/controller/subsystem/unified/proc/toggleWizardmode() + wizardmode = !wizardmode //TODO: decide what to do with wiz events + message_admins("Summon Events has been [wizardmode ? "enabled, events will occur [SSunified.event_frequency_multiplier] times as fast" : "disabled"]!") + log_game("Summon Events was [wizardmode ? "enabled" : "disabled"]!") + +///Attempts to select players for special roles the mode might have. +/datum/controller/subsystem/unified/proc/pre_setup() + // We need to do this to prevent some niche fuckery... and make dep. orders work. Lol + SSjob.reset_occupations() + handle_pre_setup_roundstart_events() + starting_points = rand(BASE_POINTS*0.5, BASE_POINTS*1.5) + points = starting_points + cooldown_dates[1] = world.time + STARTING_DELAY - SCHEDULE_DELAY + for(var/i in 2 to cooldown_dates.len) + cooldown_dates[i] = cooldown_dates[i-1] + rand(0, STARTING_DELAY) + log_game("Unified: Point budget is [starting_points], starting cooldown is [round((STARTING_DELAY - SCHEDULE_DELAY) / (1 MINUTES), 0.01)] minutes.") + message_admins("Unified: Point budget is [starting_points], starting cooldown is [round((STARTING_DELAY - SCHEDULE_DELAY) / (1 MINUTES), 0.01)] minutes.") + return TRUE + +///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things +/datum/controller/subsystem/unified/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report. + if(!report) + report = !CONFIG_GET(flag/no_intercept_report) + addtimer(CALLBACK(src, PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME) + + if(SSdbcore.Connect()) + var/list/to_set = list() + var/arguments = list() + to_set += "game_mode = :game_mode" + arguments["game_mode"] = "Unified" + if(GLOB.revdata.originmastercommit) + to_set += "commit_hash = :commit_hash" + arguments["commit_hash"] = GLOB.revdata.originmastercommit + if(to_set.len) + arguments["round_id"] = GLOB.round_id + var/datum/db_query/query_round_game_mode = SSdbcore.NewQuery( + "UPDATE [format_table_name("round")] SET [to_set.Join(", ")] WHERE id = :round_id", + arguments + ) + query_round_game_mode.Execute() + qdel(query_round_game_mode) + addtimer(CALLBACK(src, PROC_REF(send_trait_report)), rand(1 MINUTES, 5 MINUTES)) + handle_post_setup_roundstart_events() + roundstart_event_view = FALSE + return TRUE + + +///Handles late-join antag assignments +/datum/controller/subsystem/unified/proc/make_antag_chance(mob/living/carbon/human/character) + return + +/datum/controller/subsystem/unified/proc/check_finished(force_ending) //to be called by SSticker + if(!SSticker.setup_done) + return FALSE + if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME)) + return TRUE + if(GLOB.station_was_nuked) + return TRUE + if(force_ending) + return TRUE + +////////////////////////// +//Reports player logouts// +////////////////////////// +/datum/controller/subsystem/unified/proc/display_roundstart_logout_report() + var/list/msg = list("[span_boldnotice("Roundstart logout report")]\n\n") + for(var/i in GLOB.mob_living_list) + var/mob/living/L = i + var/mob/living/carbon/C = L + var/mob/living/carbon/human/H = C + if (istype(C) && !C.last_mind) + continue // never had a client + + if(L.ckey && !GLOB.directory[L.ckey]) + msg += "[L.name] ([L.key]), the [L.job] (Disconnected)\n" + + + if(L.ckey && L.client) + var/failed = FALSE + if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) + msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n" + failed = TRUE //AFK client + if(!failed && L.stat) + if(H.get_dnr()) //Suicider + msg += "[L.name] ([L.key]), the [L.job] ([span_boldannounce("Suicide")])\n" + failed = TRUE //Disconnected client + if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT)) + msg += "[L.name] ([L.key]), the [L.job] (Dying)\n" + failed = TRUE //Unconscious + if(!failed && L.stat == DEAD) + msg += "[L.name] ([L.key]), the [L.job] (Dead)\n" + failed = TRUE //Dead + + continue //Happy connected client + for(var/mob/dead/observer/D in GLOB.dead_mob_list) + if(D.mind && D.mind.current == L) + if(L.stat == DEAD) + if(H.get_dnr()) //Suicider + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Suicide")])\n" + continue //Disconnected client + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" + continue //Dead mob, ghost abandoned + else + if(D.can_reenter_corpse) + continue //Adminghost, or cult/wizard ghost + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Ghosted")])\n" + continue //Ghosted while alive + + + for (var/C in GLOB.admins) + to_chat(C, msg.Join()) + +//Set result and news report here +/datum/controller/subsystem/unified/proc/set_round_result() + SSticker.mode_result = "undefined" + if(GLOB.station_was_nuked) + SSticker.news_report = STATION_DESTROYED_NUKE + if(EMERGENCY_ESCAPED_OR_ENDGAMED) + SSticker.news_report = STATION_EVACUATED + if(SSshuttle.emergency.is_hijacked()) + SSticker.news_report = SHUTTLE_HIJACK + +/// Loads json event config values from events.txt +/datum/controller/subsystem/unified/proc/load_event_config_vars() + var/json_file = file("[global.config.directory]/events.json") + if(!fexists(json_file)) + return + var/list/decoded = json_decode(file2text(json_file)) + for(var/event_text_path in decoded) + var/event_path = text2path(event_text_path) + var/datum/round_event_control/event + for(var/datum/round_event_control/iterated_event as anything in control) + if(iterated_event.type == event_path) + event = iterated_event + break + if(!event) + continue + var/list/var_list = decoded[event_text_path] + for(var/variable in var_list) + var/value = var_list[variable] + switch(variable) + if("weight") + event.weight = value + if("min_players") + event.min_players = value + if("max_occurrences") + event.max_occurrences = value + if("earliest_start") + event.earliest_start = value * (1 MINUTES) + if("cost") + event.cost = value + if("reoccurence_penalty_multiplier") + event.reoccurence_penalty_multiplier = value + if("shared_occurence_type") + if(!isnull(value)) + value = text2path(value) + event.shared_occurence_type = value + +/// Loads config values from game_options.txt +/datum/controller/subsystem/unified/proc/load_config_vars() + allow_pop_scaling = CONFIG_GET(flag/allow_storyteller_pop_scaling) + +/// Panel containing information, variables and controls about the gamemode and scheduled event +/datum/controller/subsystem/unified/proc/admin_panel(mob/user) + update_crew_infos() + var/list/dat = list() + var/active_pop = get_correct_popcount() + dat += " HALT Unified Event Panel Refresh" + dat += "
Active Players: [active_pop] (Head: [head_crew], Sec: [sec_crew], Eng: [eng_crew], Med: [med_crew]) - Antag Cap: [get_antag_cap()]" + dat += "
" + dat += "Main" + dat += " Variables" + dat += "
" + switch(panel_page) + if(UNIFIED_PANEL_VARIABLES) + /* + dat += "Reload Config Vars Configs located in game_options.txt." + dat += "
Point Gains Multipliers (only over time):" + dat += "
This affects points gained over time towards scheduling new events of the tracks." + for(var/track in event_tracks) + dat += "
[track]: [point_gain_multipliers[track]]" + dat += "
" + + dat += "Roundstart Points Multipliers:" + dat += "
This affects points generated for roundstart events and antagonists." + for(var/track in event_tracks) + dat += "
[track]: [roundstart_point_multipliers[track]]" + dat += "
" + */ + /* TODO FIX + dat += "Minimum Population for Tracks:" + dat += "
This are the minimum population caps for events to be able to run." + for(var/track in event_tracks) + dat += "
[track]: [min_pop_thresholds[track]]" + dat += "
" + + dat += "Point Thresholds:" + dat += "
Those are thresholds the tracks require to reach with points to make an event." + for(var/track in event_tracks) + dat += "
[track]: [point_thresholds[track]]" + */ + if(UNIFIED_PANEL_MAIN) + var/background_cl = "#23273C" + dat += "

Point Budget:

" + dat += "[points]/[starting_points]" + dat += "

Cooldowns:

" + for(var/i in 1 to cooldown_dates.len) + dat += "

[max(0, round((cooldown_dates[i] - world.time) / (1 MINUTES), 0.01))] minutes Reset Cooldown

" + + dat += "

Scheduled Events:

" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + var/sorted_scheduled = list() + for(var/datum/scheduled_event/scheduled as anything in scheduled_events) + sorted_scheduled[scheduled] = scheduled.start_time + sortTim(sorted_scheduled, cmp=/proc/cmp_numeric_asc, associative = TRUE) + var/even = TRUE + for(var/datum/scheduled_event/scheduled as anything in sorted_scheduled) + even = !even + background_cl = even ? "#17191C" : "#23273C" + dat += "" + dat += "" //Name + dat += "" //Cost + var/time = "[(scheduled.start_time - world.time) / (1 SECONDS)] s." + dat += "" //Time + dat += "" //Actions + dat += "" + dat += "
NameCostTimeActions
[scheduled.event.name][scheduled.event.calculated_cost][time][scheduled.get_href_actions()]
" + + dat += "

Running Events:

" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + even = TRUE + for(var/datum/round_event/event as anything in running) + even = !even + background_cl = even ? "#17191C" : "#23273C" + dat += "" + dat += "" //Name + dat += "" //Actions + dat += "" + dat += "
NameActions
[event.control.name]-TBA-
" + + var/datum/browser/popup = new(user, "unified_admin_panel", "Unified Panel", 670, 650) + popup.set_content(dat.Join()) + popup.open() + + /// Panel containing information and actions regarding events +/datum/controller/subsystem/unified/proc/event_panel(mob/user) + var/list/dat = list() + dat += "
Roundstart Events" + dat += "
Avg. event intervals: " + dat += "
" + /// Create event info and stats table + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + var/even = TRUE + var/total_weight = 0 + var/list/assoc_spawn_weight = list() + var/active_pop = get_correct_popcount() + for(var/datum/round_event_control/event as anything in (control + roundstart_control)) + if(event.roundstart != roundstart_event_view) + continue + if(event.can_spawn_event(active_pop)) + total_weight += event.calculated_weight + assoc_spawn_weight[event] = event.calculated_weight + else + assoc_spawn_weight[event] = 0 + sortTim(assoc_spawn_weight, cmp=/proc/cmp_numeric_dsc, associative = TRUE) + for(var/datum/round_event_control/event as anything in assoc_spawn_weight) + even = !even + var/background_cl = even ? "#17191C" : "#23273C" + dat += "" + dat += "" //Name + dat += "" + dat += "" //Cost + dat += "" //Minimum pop + dat += "" //Minimum time + dat += "" //Can happen? + var/weight_string = "([event.calculated_weight] /raw.[event.weight])" + if(assoc_spawn_weight[event]) + var/percent = round((event.calculated_weight / total_weight) * 100) + weight_string = "[percent]% - [weight_string]" + dat += "" //Weight + dat += "" //Actions + dat += "" + dat += "
NameTagsBase CostM.PopM.TimeCan OccurWeightActions
[event.name]" //Tags + for(var/tag in event.tags) + dat += "[tag] " + dat += "[event.unified_cost][event.min_players][event.earliest_start / (1 MINUTES)] m.[assoc_spawn_weight[event] ? "Yes" : "No"][weight_string][event.get_href_actions()]
" + var/datum/browser/popup = new(user, "unified_event_panel", "Event Panel", 1000, 600) + popup.set_content(dat.Join()) + popup.open() + +/datum/controller/subsystem/unified/Topic(href, href_list) + . = ..() + var/mob/user = usr + if(!check_rights(R_ADMIN)) + return + switch(href_list["panel"]) + if("main") + switch(href_list["action"]) + if("reset_cooldown") + cooldown_dates[text2num(href_list["number"])] = world.time + if("halt_storyteller") + halted = !halted + message_admins("[key_name_admin(usr)] has [halted ? "HALTED" : "un-halted"] Unified.") + /* TODO FIX + if("vars") + var/track = href_list["track"] + switch(href_list["var"]) + /*if("pts_multiplier") + var/new_value = input(usr, "New value:", "Set new value") as num|null + if(isnull(new_value) || new_value < 0) + return + message_admins("[key_name_admin(usr)] set point gain multiplier for [track] track to [new_value].") + point_gain_multipliers[track] = new_value + if("roundstart_pts") + var/new_value = input(usr, "New value:", "Set new value") as num|null + if(isnull(new_value) || new_value < 0) + return + message_admins("[key_name_admin(usr)] set roundstart pts multiplier for [track] track to [new_value].") + roundstart_point_multipliers[track] = new_value + */ + if("min_pop") + var/new_value = input(usr, "New value:", "Set new value") as num|null + if(isnull(new_value) || new_value < 0) + return + message_admins("[key_name_admin(usr)] set minimum population for [track] track to [new_value].") + min_pop_thresholds[track] = new_value + if("pts_threshold") + var/new_value = input(usr, "New value:", "Set new value") as num|null + if(isnull(new_value) || new_value < 0) + return + message_admins("[key_name_admin(usr)] set point threshold of [track] track to [new_value].") + point_thresholds[track] = new_value + */ + if("reload_config_vars") + message_admins("[key_name_admin(usr)] reloaded unified config vars.") + load_config_vars() + if("tab") + var/tab = href_list["tab"] + panel_page = tab + if("open_stats") + event_panel(user) + return + if("change_points") + var/new_points = input(usr, "New point budget:", "Change Points") as num|null + if(isnull(new_points)) + return + points = new_points + if("change_starting_points") + var/new_starting_points = input(usr, "New starting point budget:", "Change Starting Points") as num|null + if(isnull(new_starting_points)) + return + starting_points = new_starting_points + /* TODO FIX + if("track_action") + var/track = href_list["track"] + if(!(track in event_tracks)) + return + switch(href_list["track_action"]) + if("remove_forced") + if(forced_next_events[track]) + var/datum/round_event_control/event = forced_next_events[track] + message_admins("[key_name_admin(usr)] removed forced event [event.name] from track [track].") + forced_next_events -= track + if("set_pts") + var/set_pts = input(usr, "New point amount ([point_thresholds[track]]+ invokes event):", "Set points for [track]") as num|null + if(isnull(set_pts)) + return + event_track_points[track] = set_pts + message_admins("[key_name_admin(usr)] set points of [track] track to [set_pts].") + log_admin_private("[key_name(usr)] set points of [track] track to [set_pts].") + if("next_event") + message_admins("[key_name_admin(usr)] invoked next event for [track] track.") + log_admin_private("[key_name(usr)] invoked next event for [track] track.") + event_track_points[track] = point_thresholds[track] + if(storyteller) + storyteller.handle_tracks() + */ + admin_panel(user) + if("stats") + switch(href_list["action"]) + if("set_roundstart") + roundstart_event_view = !roundstart_event_view + if("set_cat") + var/new_category = href_list["cat"] + if(new_category in EVENT_PANEL_TRACKS) + statistics_track_page = new_category + event_panel(user) diff --git a/tgstation.dme b/tgstation.dme index c8d99a0fbacd..c4d82125eb05 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -9444,12 +9444,17 @@ #include "modular_zubbers\master_files\skyrat\modules\deforest_medical_items\code\cargo_packs.dm" #include "modular_zubbers\master_files\skyrat\modules\opposing_force\code\opposing_force_subsystem.dm" #include "modular_zubbers\master_files\skyrat\modules\verbs\code\subtle.dm" +#include "modular_zzbug\code\__DEFINES\unified_defines.dm" #include "modular_zzbug\code\game\objects\items\plushes.dm" #include "modular_zzbug\code\modules\client\preferences\preferences.dm" #include "modular_zzbug\code\modules\events\appendicitis.dm" #include "modular_zzbug\code\modules\mob\dead\new_player\body_markings.dm" #include "modular_zzbug\code\modules\research\designs\weapon_designs.dm" #include "modular_zzbug\code\modules\research\techweb\all_nodes.dm" +#include "modular_zzbug\code\modules\unified\_event.dm" +#include "modular_zzbug\code\modules\unified\divergency_report.dm" +#include "modular_zzbug\code\modules\unified\event_overrides.dm" +#include "modular_zzbug\code\modules\unified\unified.dm" #include "modular_zzbug\master_files\code\modules\client\examine_tgui.dm" #include "modular_zzbug\master_files\code\modules\research\techweb\all_nodes.dm" // END_INCLUDE