mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-14 09:35:30 +01:00
Storyteller port from horizon [DNM] (#456)
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may not be viewable. --> <!-- You can view Contributing.MD for a detailed description of the pull request process. --> ## About The Pull Request Ports storyteller from horizon, who deleted their repo. ### How it works _Written by Majkl-J_ Basically, the storyteller runs several "Tracks" that slowly fill up with points. Upon reaching their set limit, an event is chosen and spawned, and the storyteller takes a somewhat-random amount of points from that track.  In this example, the moderate track will soon spawn an event. Each track has its own sets of events it can spawn, sorted by intensity (Roleset is midround and roundstart antags). Every event also has little tags that the storyteller can be made to prioritize. As with dynamic, events have a certain weight that determines the chance they roll. Prioritized events get a multiplier to this weight.  In this example, we can see some of the tags. Some storytellers can be made to increase/decrease the weight of these events. #### Extra stuff it does Keeps track of med, engineering, and sec players. Whilst this still has no actual usage, it is good to know it is capable of this. ## Why It's Good For The Game Storyteller is an alternate event controller, in my opinion, superior to dynamic. ## Changelog <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Be sure to properly mark your PRs to prevent unnecessary GBP loss. You can read up on GBP and it's effects on PRs in the tgstation guides for contributors. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> 🆑 Nevimer, Majkl-J, BurgerBB, Azarak add: Ported the storyteller event system from horizon /🆑 <!-- Both 🆑's are required for the changelog to work! You can put your name to the right of the first 🆑 if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. --> --------- Co-authored-by: The Sharkening <95130227+StrangeWeirdKitten@users.noreply.github.com> Co-authored-by: iero <> Co-authored-by: Waterpig <wtryoutube@seznam.cz> Co-authored-by: Waterpig <49160555+Majkl-J@users.noreply.github.com> Co-authored-by: Return <donwest947@gmail.com> Co-authored-by: BurgerLUA <8602857+BurgerLUA@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
//Could be bitflags, but that would require a good amount of translations, which eh, either way works for me
|
||||
/// When the event is combat oriented (spawning monsters, inherently hostile antags)
|
||||
#define TAG_COMBAT "combat"
|
||||
/// When the event is spooky (broken lights, some antags)
|
||||
#define TAG_SPOOKY "spooky"
|
||||
/// When the event is destructive in a decent capacity (meteors, blob)
|
||||
#define TAG_DESTRUCTIVE "destructive"
|
||||
/// When the event impacts most of the crewmembers in some capacity (comms blackout)
|
||||
#define TAG_COMMUNAL "communal"
|
||||
/// When the event targets a person for something (appendix, heart attack)
|
||||
#define TAG_TARGETED "targeted"
|
||||
/// When the event is positive and helps the crew, in some capacity (Shuttle Loan, Supply Pod)
|
||||
#define TAG_POSITIVE "positive"
|
||||
/// When one of the crewmembers becomes an antagonist
|
||||
#define TAG_CREW_ANTAG "crew_antag"
|
||||
/// When the antagonist event is focused around team cooperation.
|
||||
#define TAG_TEAM_ANTAG "team_antag"
|
||||
/// When one of the non-crewmember players becomes an antagonist
|
||||
#define TAG_OUTSIDER_ANTAG "away_antag"
|
||||
/// When the event impacts the overmap
|
||||
#define TAG_OVERMAP "overmap"
|
||||
/// When the event requires the station to be in space (meteors, carp)
|
||||
#define TAG_SPACE "space"
|
||||
/// When the event requires the station to be on planetary.
|
||||
#define TAG_PLANETARY "planetary"
|
||||
|
||||
#define EVENT_TRACK_MUNDANE "Mundane"
|
||||
#define EVENT_TRACK_MODERATE "Moderate"
|
||||
#define EVENT_TRACK_MAJOR "Major"
|
||||
#define EVENT_TRACK_ROLESET "Roleset"
|
||||
#define EVENT_TRACK_OBJECTIVES "Objectives"
|
||||
|
||||
#define ALL_EVENTS "All"
|
||||
#define UNCATEGORIZED_EVENTS "Uncategorized"
|
||||
|
||||
#define STORYTELLER_WAIT_TIME 20 SECONDS
|
||||
|
||||
#define EVENT_POINT_GAINED_PER_SECOND 0.05
|
||||
|
||||
#define TRACK_FAIL_POINT_PENALTY_MULTIPLIER 0.5
|
||||
|
||||
#define GAMEMODE_PANEL_MAIN "Main"
|
||||
#define GAMEMODE_PANEL_VARIABLES "Variables"
|
||||
|
||||
#define MUNDANE_POINT_THRESHOLD 40
|
||||
#define MODERATE_POINT_THRESHOLD 70
|
||||
#define MAJOR_POINT_THRESHOLD 130
|
||||
#define ROLESET_POINT_THRESHOLD 150
|
||||
#define OBJECTIVES_POINT_THRESHOLD 170
|
||||
|
||||
#define MUNDANE_MIN_POP 4
|
||||
#define MODERATE_MIN_POP 6
|
||||
#define MAJOR_MIN_POP 20
|
||||
#define ROLESET_MIN_POP 20
|
||||
#define OBJECTIVES_MIN_POP 20
|
||||
|
||||
/// Defines for how much pop do we need to stop applying a pop scalling penalty to event frequency.
|
||||
#define MUNDANE_POP_SCALE_THRESHOLD 25
|
||||
#define MODERATE_POP_SCALE_THRESHOLD 32
|
||||
#define MAJOR_POP_SCALE_THRESHOLD 45
|
||||
#define ROLESET_POP_SCALE_THRESHOLD 45
|
||||
#define OBJECTIVES_POP_SCALE_THRESHOLD 45
|
||||
|
||||
/// The maximum penalty coming from pop scalling, when we're at the most minimum point, easing into 0 as we reach the SCALE_THRESHOLD. This is treated as a percentage.
|
||||
#define MUNDANE_POP_SCALE_PENALTY 35
|
||||
#define MODERATE_POP_SCALE_PENALTY 35
|
||||
#define MAJOR_POP_SCALE_PENALTY 35
|
||||
#define ROLESET_POP_SCALE_PENALTY 35
|
||||
#define OBJECTIVES_POP_SCALE_PENALTY 35
|
||||
|
||||
#define STORYTELLER_VOTE "storyteller"
|
||||
|
||||
#define EVENT_TRACKS list(EVENT_TRACK_MUNDANE, EVENT_TRACK_MODERATE, EVENT_TRACK_MAJOR, EVENT_TRACK_ROLESET, EVENT_TRACK_OBJECTIVES)
|
||||
#define EVENT_PANEL_TRACKS list(EVENT_TRACK_MUNDANE, EVENT_TRACK_MODERATE, EVENT_TRACK_MAJOR, EVENT_TRACK_ROLESET, EVENT_TRACK_OBJECTIVES, UNCATEGORIZED_EVENTS, ALL_EVENTS)
|
||||
|
||||
/// Defines for the antag cap to prevent midround injections.
|
||||
#define ANTAG_CAP_FLAT 1
|
||||
#define ANTAG_CAP_DENOMINATOR 9
|
||||
|
||||
///Below are defines for roundstart point pool. The GAIN ones are multiplied by ready population
|
||||
#define ROUNDSTART_MUNDANE_BASE 20
|
||||
#define ROUNDSTART_MUNDANE_GAIN 0.5
|
||||
|
||||
#define ROUNDSTART_MODERATE_BASE 35
|
||||
#define ROUNDSTART_MODERATE_GAIN 1.2
|
||||
|
||||
#define ROUNDSTART_MAJOR_BASE 40
|
||||
#define ROUNDSTART_MAJOR_GAIN 2
|
||||
|
||||
#define ROUNDSTART_ROLESET_BASE 60
|
||||
#define ROUNDSTART_ROLESET_GAIN 4
|
||||
|
||||
#define ROUNDSTART_OBJECTIVES_BASE 40
|
||||
#define ROUNDSTART_OBJECTIVES_GAIN 2
|
||||
@@ -319,7 +319,7 @@ SUBSYSTEM_DEF(dynamic)
|
||||
SSticker.news_report = SSshuttle.emergency?.is_hijacked() ? SHUTTLE_HIJACK : STATION_EVACUATED
|
||||
|
||||
/datum/controller/subsystem/dynamic/proc/send_intercept()
|
||||
if(GLOB.communications_controller.block_command_report) //If we don't want the report to be printed just yet, we put it off until it's ready
|
||||
/*if(GLOB.communications_controller.block_command_report) //If we don't want the report to be printed just yet, we put it off until it's ready
|
||||
addtimer(CALLBACK(src, PROC_REF(send_intercept)), 10 SECONDS)
|
||||
return
|
||||
|
||||
@@ -369,7 +369,7 @@ SUBSYSTEM_DEF(dynamic)
|
||||
if(SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_BLUE)
|
||||
SSsecurity_level.set_level(SEC_LEVEL_BLUE, announce = FALSE)
|
||||
priority_announce("[SSsecurity_level.current_security_level.elevating_to_announcement]\n\nA summary has been copied and printed to all communications consoles.", "Security level elevated.", ANNOUNCER_INTERCEPT, color_override = SSsecurity_level.current_security_level.announcement_color)
|
||||
#endif
|
||||
#endif*/ // BUBBER EDIT END
|
||||
|
||||
return .
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ SUBSYSTEM_DEF(persistence)
|
||||
var/tram_hits_this_round = 0
|
||||
var/tram_hits_last_round = 0
|
||||
|
||||
var/last_storyteller = "" // BUBBER EDIT ADD: Storyteller votes
|
||||
|
||||
/datum/controller/subsystem/persistence/Initialize()
|
||||
load_poly()
|
||||
load_wall_engravings()
|
||||
@@ -63,6 +65,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
load_panic_bunker() //SKYRAT EDIT ADDITION - PANICBUNKER
|
||||
load_tram_counter()
|
||||
load_adventures()
|
||||
load_storyteller() //BUBBER EDIT ADD - Storyteller
|
||||
return SS_INIT_SUCCESS
|
||||
|
||||
///Collects all data to persist.
|
||||
|
||||
@@ -45,6 +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.config?.map_name || "Loading..."]",
|
||||
cached ? "Next Map: [cached.map_name]" : null,
|
||||
"Storyteller: [SSgamemode.storyteller ? SSgamemode.storyteller.name : "N/A"]", // BUBBER EDIT ADDITION
|
||||
"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
|
||||
" ",
|
||||
|
||||
@@ -169,6 +169,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
send2chat(new /datum/tgs_message_content("<@&[CONFIG_GET(string/game_alert_role_id)]> Round **[GLOB.round_id]** starting on [SSmapping.config.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
|
||||
SSvote.initiate_vote(/datum/vote/storyteller, "Storyteller Vote", forced = TRUE) // BUBBER EDIT ADDITION
|
||||
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
|
||||
@@ -246,7 +247,12 @@ SUBSYSTEM_DEF(ticker)
|
||||
CHECK_TICK
|
||||
//Configure mode and assign player to antagonists
|
||||
var/can_continue = FALSE
|
||||
can_continue = SSdynamic.pre_setup() //Choose antagonists
|
||||
// can_continue = SSdynamic.pre_setup() //Choose antagonists // BUBBER EDIT - STORYTELLER (note: maybe disable)
|
||||
//BUBBER EDIT BEGIN - STORYTELLER
|
||||
SSgamemode.init_storyteller()
|
||||
can_continue = SSgamemode.pre_setup()
|
||||
//BUBBER EDIT END - STORYTELLER
|
||||
|
||||
CHECK_TICK
|
||||
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_JOBS_ASSIGNED, src)
|
||||
can_continue = can_continue && SSjob.DivideOccupations() //Distribute jobs
|
||||
@@ -315,6 +321,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
/datum/controller/subsystem/ticker/proc/PostSetup()
|
||||
set waitfor = FALSE
|
||||
SSdynamic.post_setup()
|
||||
SSgamemode.post_setup() // BUBBER EDIT - Storyteller
|
||||
GLOB.start_state = new /datum/station_state()
|
||||
GLOB.start_state.count()
|
||||
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
|
||||
var/dat = "<center><B>Game Panel</B></center><hr>"
|
||||
if(SSticker.current_state <= GAME_STATE_PREGAME)
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_ruleset_manage=1'>(Manage Dynamic Rulesets)</A><br>"
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart=1'>(Force Roundstart Rulesets)</A><br>"
|
||||
// BUBBER EDIT START - STORYTELLER
|
||||
//dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_ruleset_manage=1'>(Manage Dynamic Rulesets)</A><br>"
|
||||
//dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart=1'>(Force Roundstart Rulesets)</A><br>"
|
||||
dat += "<a href='?src=[REF(src)];[HrefToken()];gamemode_panel=1'>(Game Mode Panel)</a><BR>"
|
||||
// BUBBER EDIT END
|
||||
if (GLOB.dynamic_forced_roundstart_ruleset.len > 0)
|
||||
for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
|
||||
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_remove=[text_ref(rule)]'>-> [rule.name] <-</A><br>"}
|
||||
@@ -31,7 +34,7 @@
|
||||
dat += "<hr/>"
|
||||
if(SSticker.IsRoundInProgress())
|
||||
dat += "<a href='?src=[REF(src)];[HrefToken()];gamemode_panel=1'>(Game Mode Panel)</a><BR>"
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_ruleset_manage=1'>(Manage Dynamic Rulesets)</A><br>"
|
||||
//dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_ruleset_manage=1'>(Manage Dynamic Rulesets)</A><br>" BUBBER EDIT - STORYTELLER
|
||||
dat += {"
|
||||
<BR>
|
||||
<A href='?src=[REF(src)];[HrefToken()];create_object=1'>Create Object</A><br>
|
||||
|
||||
@@ -113,8 +113,8 @@
|
||||
else if(href_list["gamemode_panel"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
SSdynamic.admin_panel()
|
||||
|
||||
//SSdynamic.admin_panel() // BUBBER EDIT - STORYTELLER
|
||||
SSgamemode.admin_panel(usr) // BUBBER EDIT - STORYTELLER
|
||||
else if(href_list["call_shuttle"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME (3 MINUTES) //SKYRAT EDIT CHANGE
|
||||
#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME (2 MINUTES) //SKYRAT EDIT CHANGE
|
||||
|
||||
//this singleton datum is used by the events controller to dictate how it selects events
|
||||
/datum/round_event_control
|
||||
@@ -74,7 +74,7 @@
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
if(occurrences >= max_occurrences)
|
||||
return FALSE
|
||||
if(earliest_start >= world.time-SSticker.round_start_time)
|
||||
if(!roundstart && earliest_start >= world.time-SSticker.round_start_time ) // BUBBER EDIT: Roundstart checks added
|
||||
return FALSE
|
||||
if(!allow_magic && wizardevent != SSevents.wizardmode)
|
||||
return FALSE
|
||||
@@ -115,19 +115,24 @@
|
||||
// SKYRAT EDIT REMOVAL END - Event notification
|
||||
|
||||
// SKYRAT EDIT ADDITION BEGIN - Event notification
|
||||
message_admins("<font color='[COLOR_ADMIN_PINK]'>Random Event triggering in [DisplayTimeText(RANDOM_EVENT_ADMIN_INTERVENTION_TIME)]: [name]. (\
|
||||
<a href='?src=[REF(src)];cancel=1'>CANCEL</a> | \
|
||||
<a href='?src=[REF(src)];something_else=1'>SOMETHING ELSE</a>)</font>")
|
||||
for(var/client/staff as anything in GLOB.admins)
|
||||
if(staff?.prefs.read_preference(/datum/preference/toggle/comms_notification))
|
||||
SEND_SOUND(staff, sound('sound/misc/server-ready.ogg'))
|
||||
sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME * 0.5)
|
||||
|
||||
if(triggering)
|
||||
message_admins("<font color='[COLOR_ADMIN_PINK]'>Random Event triggering in [DisplayTimeText(RANDOM_EVENT_ADMIN_INTERVENTION_TIME * 0.5)]: [name]. (\
|
||||
<a href='?src=[REF(src)];cancel=1'>CANCEL</a> | \
|
||||
<a href='?src=[REF(src)];something_else=1'>SOMETHING ELSE</a>)</font>")
|
||||
// BUBBER EDIT START - Only delay on roundstart
|
||||
if(SSticker.HasRoundStarted())
|
||||
message_admins("<font color='[COLOR_ADMIN_PINK]'>Random Event triggering in [DisplayTimeText(RANDOM_EVENT_ADMIN_INTERVENTION_TIME)]: [name]. (\
|
||||
<a href='?src=[REF(src)];cancel=1'>CANCEL</a> | \
|
||||
<a href='?src=[REF(src)];something_else=1'>SOMETHING ELSE</a>)</font>")
|
||||
for(var/client/staff as anything in GLOB.admins)
|
||||
if(staff?.prefs.read_preference(/datum/preference/toggle/comms_notification))
|
||||
SEND_SOUND(staff, sound('sound/misc/server-ready.ogg'))
|
||||
sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME * 0.5)
|
||||
|
||||
if(triggering)
|
||||
message_admins("<font color='[COLOR_ADMIN_PINK]'>Random Event triggering in [DisplayTimeText(RANDOM_EVENT_ADMIN_INTERVENTION_TIME * 0.5)]: [name]. (\
|
||||
<a href='?src=[REF(src)];cancel=1'>CANCEL</a> | \
|
||||
<a href='?src=[REF(src)];something_else=1'>SOMETHING ELSE</a>)</font>")
|
||||
sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME * 0.5)
|
||||
else
|
||||
message_admins("<font color='[COLOR_ADMIN_PINK]'> Roundstart event chosen: [name].")
|
||||
// BUBBER EDIT END
|
||||
// SKYRAT EDIT ADDITION END - Event notification
|
||||
|
||||
if(!triggering)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
/datum/round_event/bureaucratic_error/start()
|
||||
var/list/jobs = SSjob.get_valid_overflow_jobs()
|
||||
/* SKYRAT EDIT REMOVAL START
|
||||
// BUBBER EDIT BEGIN /* SKYRAT EDIT REMOVAL START
|
||||
if(prob(33)) // Only allows latejoining as a single role.
|
||||
var/datum/job/overflow = pick_n_take(jobs)
|
||||
overflow.spawn_positions = -1
|
||||
@@ -23,7 +23,7 @@
|
||||
var/datum/job/current = job
|
||||
current.total_positions = 0
|
||||
return
|
||||
*/ // SKYRAT EDIT REMOVAL - no more locking off jobs
|
||||
// BUBBER EDIT END */ // SKYRAT EDIT REMOVAL - no more locking off jobs
|
||||
// Adds/removes a random amount of job slots from all jobs.
|
||||
for(var/datum/job/current as anything in jobs)
|
||||
current.total_positions = max(current.total_positions + rand(-2,4), 1) // SKYRAT EDIT CHANGE - No more locking off jobs - ORIGINAL: current.total_positions = max(current.total_positions + rand(-2,4), 0)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"/datum/round_event_control":
|
||||
{
|
||||
"weight" : 10,
|
||||
"min_players" : 0,
|
||||
"max_occurrences" : 100,
|
||||
"earliest_start" : 20,
|
||||
"track" : "Moderate",
|
||||
"cost" : 1,
|
||||
"reoccurence_penalty_multiplier" : 1,
|
||||
"shared_occurence_type" : null
|
||||
}
|
||||
}
|
||||
+47
-1
@@ -141,7 +141,7 @@ ALLOW_RANDOM_EVENTS
|
||||
|
||||
## Multiplier for earliest start time of dangerous events.
|
||||
## Set to 0 to make dangerous events avaliable from round start.
|
||||
EVENTS_MIN_TIME_MUL 5
|
||||
EVENTS_MIN_TIME_MUL 1
|
||||
|
||||
## Multiplier for minimal player count (players = alive non-AFK humans) for dangerous events to start.
|
||||
## Set to 0 to make dangerous events avaliable for all populations.
|
||||
@@ -585,3 +585,49 @@ NEGATIVE_STATION_TRAITS 3 1
|
||||
# If set to 0, then players won't be able to select any positive quirks.
|
||||
# If commented-out or undefined, the maximum default is 6.
|
||||
MAX_POSITIVE_QUIRKS 6
|
||||
## Gamemode configurations
|
||||
|
||||
## Multipliers for points gained over time for event tracks.
|
||||
MUNDANE_POINT_GAIN_MULTIPLIER 1
|
||||
MODERATE_POINT_GAIN_MULTIPLIER 1
|
||||
MAJOR_POINT_GAIN_MULTIPLIER 1
|
||||
ROLESET_POINT_GAIN_MULTIPLIER 1
|
||||
OBJECTIVES_POINT_GAIN_MULTIPLIER 1
|
||||
|
||||
## Multipliers for points to spend on roundstart events.
|
||||
MUNDANE_ROUNDSTART_POINT_MULTIPLIER 1
|
||||
MODERATE_ROUNDSTART_POINT_MULTIPLIER 1
|
||||
MAJOR_ROUNDSTART_POINT_MULTIPLIER 1
|
||||
ROLESET_ROUNDSTART_POINT_MULTIPLIER 1
|
||||
OBJECTIVES_ROUNDSTART_POINT_MULTIPLIER 1
|
||||
|
||||
## Minimum population caps for event tracks to run their events.
|
||||
MUNDANE_MIN_POP 0
|
||||
MODERATE_MIN_POP 0
|
||||
MAJOR_MIN_POP 0
|
||||
ROLESET_MIN_POP 0
|
||||
OBJECTIVES_MIN_POP 0
|
||||
|
||||
## Point thresholds for tracks to run events. The lesser the more frequent events will be.
|
||||
MUNDANE_POINT_THRESHOLD 25
|
||||
MODERATE_POINT_THRESHOLD 50
|
||||
MAJOR_POINT_THRESHOLD 90
|
||||
ROLESET_POINT_THRESHOLD 120
|
||||
OBJECTIVES_POINT_THRESHOLD 130
|
||||
|
||||
## Allows the storyteller to scale event frequencies based on population
|
||||
ALLOW_STORYTELLER_POP_SCALING
|
||||
|
||||
## Thresholds that population frequency scalling penalize up to.
|
||||
MUNDANE_POP_SCALE_THRESHOLD 10
|
||||
MODERATE_POP_SCALE_THRESHOLD 15
|
||||
MAJOR_POP_SCALE_THRESHOLD 40
|
||||
ROLESET_POP_SCALE_THRESHOLD 45
|
||||
OBJECTIVES_POP_SCALE_THRESHOLD 40
|
||||
|
||||
## The maximum penalties population scalling will apply to the tracks for having less pop than POP_SCALE_THRESHOLD. This is treated as percentages
|
||||
MUNDANE_POP_SCALE_PENALTY 30
|
||||
MODERATE_POP_SCALE_PENALTY 30
|
||||
MAJOR_POP_SCALE_PENALTY 30
|
||||
ROLESET_POP_SCALE_PENALTY 30
|
||||
OBJECTIVES_POP_SCALE_PENALTY 30
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/datum/round_event_control/can_spawn_event(players_amt, allow_magic = FALSE)
|
||||
/* /datum/round_event_control/can_spawn_event(players_amt, allow_magic = FALSE) // BUBBER EDIT REMOVAL
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
. = ..()
|
||||
if(intensity_restriction && !GLOB.intense_event_credits)
|
||||
@@ -17,3 +17,4 @@
|
||||
else
|
||||
log_game("ICES: [src.name] does not need an intensity credit. Intensity credit count: [GLOB.intense_event_credits] credits")
|
||||
message_admins("ICES: [src.name] does not need an intensity credit. Intensity credit count: [GLOB.intense_event_credits] credits")
|
||||
*/
|
||||
|
||||
@@ -44,10 +44,10 @@
|
||||
frequency_upper = CONFIG_GET(number/event_frequency_upper)
|
||||
intensity_credit_rate = CONFIG_GET(number/intensity_credit_rate)
|
||||
|
||||
/**
|
||||
/** // BUBBER EDIT BEGIN
|
||||
* Abductors
|
||||
*/
|
||||
/datum/round_event_control/abductor
|
||||
/* /datum/round_event_control/abductor
|
||||
max_occurrences = 1
|
||||
weight = VERY_LOW_EVENT_FREQ
|
||||
|
||||
@@ -603,7 +603,7 @@
|
||||
/datum/round_event_control/wormholes
|
||||
max_occurrences = 2
|
||||
weight = MED_EVENT_FREQ
|
||||
|
||||
*/ // BUBBER EDIT END
|
||||
|
||||
|
||||
#undef VERY_HIGH_EVENT_FREQ
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// BUBBER EDIT - REVERT TO TG
|
||||
/*#define SUPERMATTER_SURGE_BULLET_ENERGY_FACTOR_UPPER 300
|
||||
#define SUPERMATTER_SURGE_BULLET_ENERGY_FACTOR_LOWER 100
|
||||
#define SUPERMATTER_SURGE_TIME_UPPER 360 * 0.5
|
||||
#define SUPERMATTER_SURGE_TIME_LOWER 180 * 0.5
|
||||
#define SUPERMATTER_SURGE_ANNOUNCE_THRESHOLD 25
|
||||
#define LOWER_SURGE_LIMIT 100 to 150
|
||||
#define MIDDLE_SURGE_LIMIT 151 to 200
|
||||
#define UPPER_SURGE_LIMIT 201 to 250
|
||||
/**
|
||||
* Supermatter Surge
|
||||
*
|
||||
* A very simple event designed to give engineering a challenge. It simply increases the supermatters power by a set amount, and announces it.
|
||||
*
|
||||
* This should be entirely fine for a powerful setup, but will require intervention on a lower power setup.
|
||||
*/
|
||||
|
||||
/datum/round_event_control/supermatter_surge
|
||||
name = "Supermatter Surge"
|
||||
typepath = /datum/round_event/supermatter_surge
|
||||
category = EVENT_CATEGORY_ENGINEERING
|
||||
max_occurrences = 4
|
||||
earliest_start = 30 MINUTES
|
||||
description = "The supermatter will increase in power by a random amount, and announce it."
|
||||
weight = 10 // BUBBER EDIT
|
||||
|
||||
/datum/round_event/supermatter_surge
|
||||
announce_when = 1
|
||||
end_when = SUPERMATTER_SURGE_TIME_LOWER
|
||||
/// How powerful is the supermatter surge going to be? Set in setup.
|
||||
var/surge_power = SUPERMATTER_SURGE_BULLET_ENERGY_FACTOR_LOWER
|
||||
var/starting_surge_power = 0
|
||||
/// Typecasted reference to the supermatter chosen at the events start. Prevents the engine from going AWOL if it changes for some reason.
|
||||
var/obj/machinery/power/supermatter_crystal/our_main_engine
|
||||
|
||||
/datum/round_event/supermatter_surge/setup()
|
||||
our_main_engine = GLOB.main_supermatter_engine
|
||||
surge_power = rand(SUPERMATTER_SURGE_BULLET_ENERGY_FACTOR_LOWER, SUPERMATTER_SURGE_BULLET_ENERGY_FACTOR_UPPER)
|
||||
starting_surge_power = our_main_engine.bullet_energy
|
||||
end_when = rand(SUPERMATTER_SURGE_TIME_LOWER, SUPERMATTER_SURGE_TIME_UPPER)
|
||||
|
||||
/datum/round_event/supermatter_surge/announce()
|
||||
if(surge_power > SUPERMATTER_SURGE_ANNOUNCE_THRESHOLD || prob(round(surge_power)))
|
||||
priority_announce("Class [get_surge_level()] supermatter surge detected. Intervention may be required.", "Anomaly Alert", ANNOUNCER_KLAXON)
|
||||
|
||||
/datum/round_event/supermatter_surge/proc/get_surge_level()
|
||||
switch(surge_power)
|
||||
if(LOWER_SURGE_LIMIT)
|
||||
return 4
|
||||
if(MIDDLE_SURGE_LIMIT)
|
||||
return 3
|
||||
if(UPPER_SURGE_LIMIT)
|
||||
return 2
|
||||
else
|
||||
return 1
|
||||
|
||||
/datum/round_event/supermatter_surge/start()
|
||||
our_main_engine?.bullet_energy *= surge_power
|
||||
|
||||
/datum/round_event/supermatter_surge/end()
|
||||
our_main_engine?.bullet_energy = starting_surge_power
|
||||
priority_announce("The supermatter surge has dissipated.", "Anomaly Cleared")
|
||||
our_main_engine = null
|
||||
|
||||
#undef SUPERMATTER_SURGE_BULLET_ENERGY_FACTOR_UPPER
|
||||
#undef SUPERMATTER_SURGE_BULLET_ENERGY_FACTOR_LOWER
|
||||
#undef SUPERMATTER_SURGE_TIME_UPPER
|
||||
#undef SUPERMATTER_SURGE_TIME_LOWER
|
||||
#undef SUPERMATTER_SURGE_ANNOUNCE_THRESHOLD
|
||||
#undef LOWER_SURGE_LIMIT
|
||||
#undef MIDDLE_SURGE_LIMIT
|
||||
#undef UPPER_SURGE_LIMIT
|
||||
*/
|
||||
@@ -0,0 +1,149 @@
|
||||
///Gamemode related configs below
|
||||
// Point Gain Multipliers
|
||||
/datum/config_entry/number/mundane_point_gain_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/moderate_point_gain_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/major_point_gain_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roleset_point_gain_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/objectives_point_gain_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
// Roundstart points Multipliers
|
||||
/datum/config_entry/number/mundane_roundstart_point_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/moderate_roundstart_point_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/major_roundstart_point_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roleset_roundstart_point_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/objectives_roundstart_point_multiplier
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
// Minimum population
|
||||
/datum/config_entry/number/mundane_min_pop
|
||||
config_entry_value = MUNDANE_MIN_POP
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/moderate_min_pop
|
||||
config_entry_value = MODERATE_MIN_POP
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/major_min_pop
|
||||
config_entry_value = MAJOR_MIN_POP
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roleset_min_pop
|
||||
config_entry_value = ROLESET_MIN_POP
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/objectives_min_pop
|
||||
config_entry_value = OBJECTIVES_MIN_POP
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
// Point Thresholds
|
||||
/datum/config_entry/number/mundane_point_threshold
|
||||
config_entry_value = MUNDANE_POINT_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/moderate_point_threshold
|
||||
config_entry_value = MODERATE_POINT_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/major_point_threshold
|
||||
config_entry_value = MAJOR_POINT_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roleset_point_threshold
|
||||
config_entry_value = ROLESET_POINT_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/objectives_point_threshold
|
||||
config_entry_value = OBJECTIVES_POINT_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
|
||||
/datum/config_entry/flag/allow_storyteller_pop_scaling // Allows storyteller to scale down the event frequency by population
|
||||
|
||||
// Pop scalling thresholds
|
||||
/datum/config_entry/number/mundane_pop_scale_threshold
|
||||
config_entry_value = MUNDANE_POP_SCALE_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/moderate_pop_scale_threshold
|
||||
config_entry_value = MODERATE_POP_SCALE_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/major_pop_scale_threshold
|
||||
config_entry_value = MAJOR_POP_SCALE_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roleset_pop_scale_threshold
|
||||
config_entry_value = ROLESET_POP_SCALE_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/objectives_pop_scale_threshold
|
||||
config_entry_value = OBJECTIVES_POP_SCALE_THRESHOLD
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
// Pop scalling penalties
|
||||
/datum/config_entry/number/mundane_pop_scale_penalty
|
||||
config_entry_value = MUNDANE_POP_SCALE_PENALTY
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/moderate_pop_scale_penalty
|
||||
config_entry_value = MODERATE_POP_SCALE_PENALTY
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/major_pop_scale_penalty
|
||||
config_entry_value = MAJOR_POP_SCALE_PENALTY
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roleset_pop_scale_penalty
|
||||
config_entry_value = ROLESET_POP_SCALE_PENALTY
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/objectives_pop_scale_penalty
|
||||
config_entry_value = OBJECTIVES_POP_SCALE_PENALTY
|
||||
integer = TRUE
|
||||
min_val = 0
|
||||
@@ -0,0 +1,60 @@
|
||||
/datum/controller/subsystem/gamemode/proc/send_trait_report()
|
||||
. = "<b><i>Central Command Status Summary</i></b><hr>"
|
||||
SSstation.generate_station_goals(20)
|
||||
|
||||
var/list/station_goals = SSstation.get_station_goals()
|
||||
|
||||
if(!length(station_goals))
|
||||
. = "<hr><b>No assigned goals.</b><BR>"
|
||||
else
|
||||
. += generate_station_goal_report(station_goals)
|
||||
if(!SSstation.station_traits.len)
|
||||
. = "<hr><b>No identified shift divergencies.</b><BR>"
|
||||
else
|
||||
. += generate_station_trait_report()
|
||||
|
||||
. += "<hr>This concludes your shift-start evaluation. Have a secure shift!<hr>\
|
||||
<p style=\"color: grey; text-align: justify;\">This label certifies an Intern has reviewed the above before sending. This document is the property of Nanotrasen Corporation.</p>"
|
||||
|
||||
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/gamemode/proc/generate_station_goal_report(var/list/station_goals)
|
||||
. = "<hr><b>Special Orders for [station_name()]:</b><BR>"
|
||||
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("<hr>")
|
||||
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/gamemode/proc/generate_station_trait_report()
|
||||
if(!SSstation.station_traits.len)
|
||||
return
|
||||
. = "<hr><b>Identified shift divergencies:</b><BR>"
|
||||
for(var/datum/station_trait/station_trait as anything in SSstation.station_traits)
|
||||
if(!station_trait.show_in_report)
|
||||
continue
|
||||
. += "[station_trait.get_report()]<BR>"
|
||||
return
|
||||
|
||||
/*/datum/controller/subsystem/gamemode/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?
|
||||
*/
|
||||
@@ -0,0 +1,77 @@
|
||||
/datum/round_event_control
|
||||
var/roundstart = FALSE
|
||||
var/cost = 1
|
||||
var/reoccurence_penalty_multiplier = 0.75
|
||||
var/shared_occurence_type
|
||||
var/track = EVENT_TRACK_MODERATE
|
||||
/// Last calculated weight that the storyteller assigned this event
|
||||
var/calculated_weight = 0
|
||||
var/tags = list() /// Tags of the event
|
||||
/// List of the shared occurence types.
|
||||
var/static/list/shared_occurences = list()
|
||||
/// Whether a roundstart event can happen post roundstart. Very important for events which override job assignments.
|
||||
var/can_run_post_roundstart = TRUE
|
||||
|
||||
/datum/round_event
|
||||
/// Whether the event called its start() yet or not.
|
||||
var/has_started = FALSE
|
||||
|
||||
/// This section of event processing is in a proc because roundstart events may get their start invoked.
|
||||
/datum/round_event/proc/try_start()
|
||||
if(has_started)
|
||||
return
|
||||
has_started = TRUE
|
||||
processing = FALSE
|
||||
start()
|
||||
processing = TRUE
|
||||
|
||||
/datum/round_event_control/roundstart
|
||||
roundstart = TRUE
|
||||
earliest_start = 0
|
||||
|
||||
///Adds an occurence. Has to use the setter to properly handle shared occurences
|
||||
/datum/round_event_control/proc/add_occurence()
|
||||
if(shared_occurence_type)
|
||||
if(!shared_occurences[shared_occurence_type])
|
||||
shared_occurences[shared_occurence_type] = 0
|
||||
shared_occurences[shared_occurence_type]++
|
||||
occurrences++
|
||||
|
||||
///Subtracts an occurence. Has to use the setter to properly handle shared occurences
|
||||
/datum/round_event_control/proc/subtract_occurence()
|
||||
if(shared_occurence_type)
|
||||
if(!shared_occurences[shared_occurence_type])
|
||||
shared_occurences[shared_occurence_type] = 0
|
||||
shared_occurences[shared_occurence_type]--
|
||||
occurrences--
|
||||
|
||||
///Gets occurences. Has to use the getter to properly handle shared occurences
|
||||
/datum/round_event_control/proc/get_occurences()
|
||||
if(shared_occurence_type)
|
||||
if(!shared_occurences[shared_occurence_type])
|
||||
shared_occurences[shared_occurence_type] = 0
|
||||
return shared_occurences[shared_occurence_type]
|
||||
return occurrences
|
||||
|
||||
/// Prints the action buttons for this event.
|
||||
/datum/round_event_control/proc/get_href_actions()
|
||||
if(SSticker.HasRoundStarted())
|
||||
if(roundstart)
|
||||
if(!can_run_post_roundstart)
|
||||
return "<a class='linkOff'>Fire</a> <a class='linkOff'>Schedule</a>"
|
||||
return "<a href='?src=[REF(src)];action=fire'>Fire</a> <a href='?src=[REF(src)];action=schedule'>Schedule</a>"
|
||||
else
|
||||
return "<a href='?src=[REF(src)];action=fire'>Fire</a> <a href='?src=[REF(src)];action=schedule'>Schedule</a> <a href='?src=[REF(src)];action=force_next'>Force Next</a>"
|
||||
else
|
||||
if(roundstart)
|
||||
return "<a href='?src=[REF(src)];action=force_next'>Force Roundstart</a>"
|
||||
else
|
||||
return "<a class='linkOff'>Fire</a> <a class='linkOff'>Schedule</a> <a class='linkOff'>Force Next</a>"
|
||||
|
||||
/datum/round_event_control/Topic(href, href_list)
|
||||
. = ..()
|
||||
switch(href_list["action"])
|
||||
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)
|
||||
@@ -0,0 +1,3 @@
|
||||
/datum/round_event_control/sentient_disease
|
||||
weight = 0
|
||||
max_occurrences = 0
|
||||
@@ -0,0 +1,54 @@
|
||||
/datum/round_event_control/earthquake
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_DESTRUCTIVE)
|
||||
|
||||
/datum/round_event_control/bureaucratic_error
|
||||
track = EVENT_TRACK_MAJOR // Yes, it's annoying.
|
||||
tags = list(TAG_COMMUNAL)
|
||||
weight = 5
|
||||
|
||||
/datum/round_event_control/blob
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_DESTRUCTIVE, TAG_COMBAT)
|
||||
weight = 10
|
||||
|
||||
/datum/round_event_control/meteor_wave
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_COMMUNAL, TAG_SPACE, TAG_DESTRUCTIVE)
|
||||
weight = 10
|
||||
|
||||
/datum/round_event_control/meteor_wave/meaty
|
||||
weight = 15
|
||||
|
||||
/datum/round_event_control/meteor_wave/ices
|
||||
weight = 0
|
||||
|
||||
/datum/round_event_control/radiation_storm
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/wormholes
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/immovable_rod
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_DESTRUCTIVE)
|
||||
weight = 20
|
||||
|
||||
/datum/round_event_control/stray_meteor
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_DESTRUCTIVE, TAG_SPACE)
|
||||
weight = 25
|
||||
|
||||
/datum/round_event_control/anomaly/anomaly_vortex
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_DESTRUCTIVE)
|
||||
|
||||
/datum/round_event_control/anomaly/anomaly_pyro
|
||||
track = EVENT_TRACK_MAJOR
|
||||
tags = list(TAG_DESTRUCTIVE)
|
||||
|
||||
/datum/round_event_control/spider_infestation
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT)
|
||||
@@ -0,0 +1,55 @@
|
||||
/datum/round_event_control/brand_intelligence
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_DESTRUCTIVE, TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/carp_migration
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/communications_blackout
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL, TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/grid_check
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL, TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/ion_storm
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_TARGETED)
|
||||
|
||||
/datum/round_event_control/processor_overload
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/radiation_storm
|
||||
max_occurrences = 2
|
||||
|
||||
/datum/round_event_control/radiation_leak
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/sandstorm
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_DESTRUCTIVE)
|
||||
|
||||
/datum/round_event_control/shuttle_catastrophe
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/vent_clog
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/anomaly
|
||||
weight = 10 // Lower from original 15 because it KEEPS SPAWNING THEM
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL, TAG_DESTRUCTIVE)
|
||||
|
||||
/datum/round_event_control/spacevine
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMMUNAL, TAG_COMBAT)
|
||||
|
||||
/datum/round_event_control/portal_storm_syndicate
|
||||
track = EVENT_TRACK_MODERATE
|
||||
tags = list(TAG_COMBAT)
|
||||
@@ -0,0 +1,72 @@
|
||||
/datum/round_event_control/aurora_caelus
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL, TAG_POSITIVE, TAG_SPACE)
|
||||
|
||||
/datum/round_event_control/brain_trauma
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_TARGETED)
|
||||
|
||||
/datum/round_event_control/camera_failure
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL, TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/disease_outbreak
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_TARGETED)
|
||||
|
||||
/datum/round_event_control/space_dust
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_DESTRUCTIVE, TAG_SPACE)
|
||||
|
||||
/datum/round_event_control/electrical_storm
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/fake_virus
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_TARGETED)
|
||||
|
||||
/datum/round_event_control/falsealarm
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/market_crash
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/mice_migration
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_DESTRUCTIVE)
|
||||
|
||||
/datum/round_event_control/wisdomcow
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL, TAG_POSITIVE)
|
||||
|
||||
/datum/round_event_control/shuttle_loan
|
||||
var/list/run_situations = list()
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/mass_hallucination
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/stray_cargo
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/grey_tide
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_DESTRUCTIVE, TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/gravity_generator_blackout
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL, TAG_SPACE)
|
||||
|
||||
/datum/round_event_control/shuttle_insurance
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/tram_malfunction
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_DESTRUCTIVE)
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/round_event_control/scrubber_overflow
|
||||
track = EVENT_TRACK_MUNDANE
|
||||
tags = list(TAG_COMMUNAL)
|
||||
|
||||
/datum/round_event_control/scrubber_overflow/threatening
|
||||
weight = 0
|
||||
max_occurrences = 0
|
||||
|
||||
/datum/round_event_control/scrubber_overflow/catastrophic
|
||||
weight = 0
|
||||
max_occurrences = 0
|
||||
|
||||
/datum/round_event_control/scrubber_overflow/every_vent
|
||||
weight = 0
|
||||
max_occurrences = 0
|
||||
|
||||
/datum/round_event_control/scrubber_overflow/ices
|
||||
weight = 0
|
||||
max_occurrences = 0
|
||||
@@ -0,0 +1,11 @@
|
||||
# Storyteller event overrides/defines
|
||||
|
||||
## Existing events
|
||||
All tg/skyrat event categorizations and adjustments are to be done in the correct track folder, in a .dm file whose name specifies it is an override.
|
||||
|
||||
(An exception applies to antag rolls, which are handled independently from any tg or skyrat code)
|
||||
|
||||
Some events might have multiple severities, these are to be put in the "multitrack" folder (If you don't see one that means there are no such events *yet*. Feel free to add it!)
|
||||
|
||||
## New events
|
||||
So you have decided to create a new event. Depending on how much code you might need to write, you either create a single dm file in the relevant folder, or create a subfolder if you need more code!
|
||||
@@ -0,0 +1,158 @@
|
||||
/datum/round_event_control/antagonist
|
||||
reoccurence_penalty_multiplier = 0
|
||||
track = EVENT_TRACK_ROLESET
|
||||
/// Protected roles from the antag roll. People will not get those roles if a config is enabled
|
||||
var/protected_roles = list(
|
||||
JOB_CAPTAIN,
|
||||
JOB_BLUESHIELD,
|
||||
|
||||
// Heads of staff
|
||||
JOB_HEAD_OF_PERSONNEL,
|
||||
JOB_HEAD_OF_SECURITY,
|
||||
JOB_CHIEF_ENGINEER,
|
||||
JOB_CHIEF_MEDICAL_OFFICER,
|
||||
JOB_RESEARCH_DIRECTOR,
|
||||
JOB_QUARTERMASTER,
|
||||
JOB_NT_REP,
|
||||
|
||||
// Seccies
|
||||
JOB_DETECTIVE,
|
||||
JOB_SECURITY_OFFICER,
|
||||
JOB_WARDEN,
|
||||
JOB_CORRECTIONS_OFFICER,
|
||||
JOB_PRISONER,
|
||||
JOB_SECURITY_MEDIC,
|
||||
)
|
||||
/// Restricted roles from the antag roll
|
||||
var/restricted_roles = list(JOB_AI, JOB_CYBORG)
|
||||
|
||||
/// How many baseline antags do we spawn
|
||||
var/base_antags = 2
|
||||
/// How many maximum antags can we spawn
|
||||
var/maximum_antags = 6
|
||||
/// For this many players we'll add 1 up to the maximum antag amount
|
||||
var/denominator = 20
|
||||
/// The antag flag to be used
|
||||
var/antag_flag
|
||||
/// The antag datum to be applied
|
||||
var/antag_datum
|
||||
|
||||
var/minimum_candidate_base = 1
|
||||
|
||||
var/list/ruleset_lazy_templates
|
||||
|
||||
/datum/round_event_control/antagonist/New()
|
||||
. = ..()
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
restricted_roles |= protected_roles
|
||||
restricted_roles |= SSstation.antag_restricted_roles
|
||||
restricted_roles |= SSstation.antag_protected_roles
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
restricted_roles |= JOB_ASSISTANT
|
||||
for(var/datum/job/iterating_job as anything in subtypesof(/datum/job))
|
||||
if(initial(iterating_job.restricted_antagonists))
|
||||
restricted_roles |= initial(iterating_job.title)
|
||||
|
||||
/datum/round_event_control/antagonist/can_spawn_event(popchecks = TRUE, allow_magic)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!roundstart && !SSgamemode.can_inject_antags())
|
||||
return FALSE
|
||||
var/list/candidates = get_candidates()
|
||||
if(candidates.len < get_minimum_candidates())
|
||||
return FALSE
|
||||
|
||||
/datum/round_event_control/antagonist/proc/get_minimum_candidates()
|
||||
return minimum_candidate_base
|
||||
|
||||
/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)
|
||||
return candidates
|
||||
|
||||
/datum/round_event_control/antagonist/solo
|
||||
typepath = /datum/round_event/antagonist/solo
|
||||
|
||||
/datum/round_event_control/antagonist/proc/get_antag_amount()
|
||||
var/people = SSgamemode.get_correct_popcount()
|
||||
var/amount = base_antags + FLOOR(people / denominator, 1)
|
||||
return min(amount, maximum_antags)
|
||||
|
||||
/datum/round_event/antagonist
|
||||
fakeable = FALSE
|
||||
end_when = 60 //This is so prompted picking events have time to run //TODO: refactor events so they can be the masters of themselves, instead of relying on some weirdly timed vars
|
||||
// ALL of those variables are internal. Check the control event to change them
|
||||
/// The antag flag passed from control
|
||||
var/antag_flag
|
||||
/// The antag datum passed from control
|
||||
var/antag_datum
|
||||
/// The antag count passed from control
|
||||
var/antag_count
|
||||
/// The restricted roles (jobs) passed from control
|
||||
var/list/restricted_roles
|
||||
/// The minds we've setup in setup() and need to finalize in start()
|
||||
var/list/setup_minds = list()
|
||||
|
||||
/datum/round_event/antagonist/solo
|
||||
|
||||
/datum/round_event/antagonist/setup()
|
||||
load_vars(control)
|
||||
candidate_setup(control)
|
||||
template_setup(control)
|
||||
|
||||
/datum/round_event/antagonist/proc/load_vars(datum/round_event_control/antagonist/cast_control)
|
||||
// Set up all the different variables on the round_event. God isn't it ugly
|
||||
// Maybe move this to New() if possible? ~Waterpig
|
||||
antag_flag = cast_control.antag_flag
|
||||
antag_datum = cast_control.antag_datum
|
||||
antag_count = cast_control.get_antag_amount()
|
||||
restricted_roles = cast_control.restricted_roles
|
||||
|
||||
/datum/round_event/antagonist/proc/candidate_setup(datum/round_event_control/antagonist/cast_control)
|
||||
var/list/candidates = cast_control.get_candidates()
|
||||
for(var/i in 1 to antag_count)
|
||||
if(!candidates.len)
|
||||
break
|
||||
var/mob/candidate = pick_n_take(candidates)
|
||||
setup_minds += candidate.mind
|
||||
candidate_roles_setup(candidate)
|
||||
|
||||
/datum/round_event/antagonist/proc/candidate_roles_setup(mob/candidate)
|
||||
SHOULD_CALL_PARENT(FALSE)
|
||||
|
||||
candidate.mind.special_role = antag_flag
|
||||
candidate.mind.restricted_roles = restricted_roles
|
||||
|
||||
/datum/round_event/antagonist/proc/template_setup(datum/round_event_control/antagonist/cast_control)
|
||||
for(var/template in cast_control.ruleset_lazy_templates)
|
||||
SSmapping.lazy_load_template(template)
|
||||
|
||||
/datum/round_event/antagonist/solo/start()
|
||||
for(var/datum/mind/antag_mind as anything in setup_minds)
|
||||
add_datum_to_mind(antag_mind)
|
||||
|
||||
/datum/round_event/antagonist/proc/add_datum_to_mind(datum/mind/antag_mind)
|
||||
antag_mind.add_antag_datum(antag_datum)
|
||||
|
||||
/datum/round_event_control/antagonist/team
|
||||
typepath = /datum/round_event/antagonist/team
|
||||
minimum_candidate_base = 1
|
||||
|
||||
var/antag_leader_datum
|
||||
|
||||
/datum/round_event_control/antagonist/team/New()
|
||||
. = ..()
|
||||
if(isnull(antag_leader_datum))
|
||||
antag_leader_datum = antag_datum
|
||||
|
||||
/datum/round_event/antagonist/team
|
||||
var/antag_leader_datum
|
||||
|
||||
/datum/round_event/antagonist/team/start()
|
||||
for(var/datum/mind/antag_mind as anything in setup_minds)
|
||||
add_datum_to_mind(antag_mind)
|
||||
|
||||
/datum/round_event/antagonist/team/load_vars(datum/round_event_control/antagonist/team/cast_control)
|
||||
. = ..()
|
||||
antag_leader_datum = cast_control.antag_leader_datum
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/datum/round_event_control/antagonist/solo/bloodsucker
|
||||
name = "Bloodsuckers"
|
||||
roundstart = TRUE
|
||||
|
||||
antag_flag = ROLE_BLOODSUCKER
|
||||
antag_datum = /datum/antagonist/bloodsucker
|
||||
weight = 2
|
||||
min_players = 20
|
||||
|
||||
base_antags = 2
|
||||
maximum_antags = 3
|
||||
|
||||
tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_CREW_ANTAG)
|
||||
|
||||
/datum/round_event_control/antagonist/solo/bloodsucker/midround
|
||||
name = "Vampiric Accident"
|
||||
roundstart = FALSE
|
||||
weight = 6 // Outweight the other ghost roles slightly
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/datum/round_event_control/antagonist/solo/changeling
|
||||
name = "Changelings"
|
||||
roundstart = TRUE
|
||||
|
||||
antag_flag = ROLE_CHANGELING
|
||||
antag_datum = /datum/antagonist/changeling
|
||||
weight = 4
|
||||
min_players = 20
|
||||
|
||||
tags = list(TAG_COMBAT, TAG_CREW_ANTAG)
|
||||
|
||||
/datum/round_event_control/antagonist/solo/changeling/midround
|
||||
name = "Genome Awakening (Changelings)"
|
||||
roundstart = FALSE
|
||||
weight = 6 // Outweight the other ghost roles slightly
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/datum/round_event_control/antagonist/solo/heretic
|
||||
name = "Heretics"
|
||||
roundstart = TRUE
|
||||
|
||||
antag_flag = ROLE_HERETIC
|
||||
antag_datum = /datum/antagonist/heretic
|
||||
weight = 4
|
||||
min_players = 40
|
||||
|
||||
base_antags = 2
|
||||
maximum_antags = 3
|
||||
|
||||
tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_CREW_ANTAG)
|
||||
|
||||
/datum/round_event_control/antagonist/solo/heretic/New()
|
||||
protected_roles |= JOB_CHAPLAIN // Would be silly to get chaplain heretics
|
||||
. = ..()
|
||||
|
||||
/datum/round_event_control/antagonist/solo/heretic/midround
|
||||
name = "Midround Heretics"
|
||||
roundstart = FALSE
|
||||
weight = 6 // Outweight the other ghost roles slightly
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
|
||||
/datum/round_event_control/antagonist/solo/malf
|
||||
name = "Malfunctioning AI"
|
||||
|
||||
base_antags = 1
|
||||
maximum_antags = 1
|
||||
|
||||
min_players = 20
|
||||
|
||||
antag_datum = /datum/antagonist/malf_ai
|
||||
antag_flag = ROLE_MALF
|
||||
weight = 0
|
||||
tags = list(TAG_CREW_ANTAG, TAG_COMBAT, TAG_DESTRUCTIVE)
|
||||
restricted_roles = list()
|
||||
|
||||
/datum/round_event_control/antagonist/solo/malf/roundstart
|
||||
roundstart = TRUE
|
||||
typepath = /datum/round_event/antagonist/solo/malf_ai/roundstart
|
||||
weight = 2
|
||||
|
||||
// God has abandoned us
|
||||
/datum/round_event_control/antagonist/solo/malf/roundstart/get_candidates()
|
||||
var/list/candidates = ..()
|
||||
. = list()
|
||||
var/datum/job/aijob = SSjob.GetJob(JOB_AI)
|
||||
for(var/mob/candidate as anything in candidates)
|
||||
if(SSjob.check_job_eligibility(candidate, aijob) == JOB_AVAILABLE)
|
||||
. += candidate
|
||||
return .
|
||||
|
||||
/datum/round_event_control/antagonist/solo/malf/roundstart/can_spawn_event(popchecks, allow_magic)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return .
|
||||
|
||||
var/datum/job/ai_job = SSjob.GetJobType(/datum/job/ai)
|
||||
if(!(ai_job.total_positions - ai_job.current_positions && ai_job.spawn_positions))
|
||||
return FALSE
|
||||
else
|
||||
return TRUE
|
||||
|
||||
/datum/round_event/antagonist/solo/malf_ai/roundstart/setup()
|
||||
. = ..()
|
||||
for(var/datum/mind/new_malf in setup_minds)
|
||||
GLOB.pre_setup_antags += new_malf
|
||||
LAZYADDASSOC(SSjob.dynamic_forced_occupations, new_malf.current, "AI")
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/datum/round_event_control/antagonist/team/nuke_ops
|
||||
name = "Nuclear Operatives"
|
||||
roundstart = TRUE
|
||||
|
||||
antag_flag = ROLE_OPERATIVE
|
||||
antag_datum = /datum/antagonist/nukeop
|
||||
antag_leader_datum = /datum/antagonist/nukeop/leader
|
||||
|
||||
weight = 0
|
||||
tags = list(TAG_CREW_ANTAG)
|
||||
|
||||
base_antags = 2
|
||||
maximum_antags = 5
|
||||
|
||||
typepath = /datum/round_event/antagonist/team/nukie
|
||||
|
||||
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_NUKIEBASE)
|
||||
|
||||
/datum/round_event/antagonist/team/nukie
|
||||
var/datum/job/job_type = /datum/job/nuclear_operative
|
||||
var/required_role = ROLE_NUCLEAR_OPERATIVE
|
||||
|
||||
var/datum/team/nuclear/nuke_team
|
||||
|
||||
/datum/round_event/antagonist/team/nukie/candidate_roles_setup(mob/candidate)
|
||||
candidate.mind.set_assigned_role(SSjob.GetJobType(job_type))
|
||||
candidate.mind.special_role = required_role
|
||||
|
||||
/datum/round_event/antagonist/team/nukie/start()
|
||||
// Get our nukie leader
|
||||
var/datum/mind/most_experienced = get_most_experienced(setup_minds, required_role)
|
||||
if(!most_experienced)
|
||||
most_experienced = setup_minds[1]
|
||||
var/datum/antagonist/nukeop/leader/leader = most_experienced.add_antag_datum(antag_leader_datum)
|
||||
nuke_team = leader.nuke_team
|
||||
|
||||
// Setup everyone else
|
||||
for(var/datum/mind/assigned_player in setup_minds)
|
||||
if(assigned_player == most_experienced)
|
||||
continue
|
||||
add_datum_to_mind(assigned_player)
|
||||
return TRUE
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/datum/round_event_control/antagonist/solo/spy
|
||||
name = "Spies"
|
||||
roundstart = TRUE
|
||||
|
||||
antag_flag = ROLE_SPY
|
||||
antag_datum = /datum/antagonist/spy
|
||||
weight = 4
|
||||
|
||||
tags = list(TAG_CREW_ANTAG)
|
||||
|
||||
/datum/round_event_control/antagonist/solo/spy/midround
|
||||
name = "Spies (Midround)"
|
||||
roundstart = FALSE
|
||||
weight = 7
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/datum/round_event_control/antagonist/solo/traitor
|
||||
name = "Traitors"
|
||||
roundstart = TRUE
|
||||
|
||||
antag_flag = ROLE_TRAITOR
|
||||
antag_datum = /datum/antagonist/traitor
|
||||
weight = 6
|
||||
|
||||
tags = list(TAG_CREW_ANTAG)
|
||||
|
||||
/datum/round_event_control/antagonist/solo/traitor/midround
|
||||
name = "Sleeper Agents (Traitors)"
|
||||
roundstart = FALSE
|
||||
weight = 7
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/datum/round_event_control/nightmare
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT, TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/revenant
|
||||
min_players = 20
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_DESTRUCTIVE, TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/space_dragon
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT)
|
||||
|
||||
/datum/round_event_control/space_ninja
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT)
|
||||
|
||||
/datum/round_event_control/changeling
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT, TAG_CREW_ANTAG)
|
||||
|
||||
/datum/round_event_control/fugitives
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT)
|
||||
|
||||
/datum/round_event_control/alien_infestation
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT, TAG_SPOOKY)
|
||||
|
||||
/datum/round_event_control/abductor
|
||||
track = EVENT_TRACK_ROLESET
|
||||
tags = list(TAG_COMBAT, TAG_SPOOKY)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
///Scheduled event datum for SSgamemode to put events into.
|
||||
/datum/scheduled_event
|
||||
/// What event are scheduling.
|
||||
var/datum/round_event_control/event
|
||||
/// When do we start our event
|
||||
var/start_time = 0
|
||||
/// If we were created by a storyteller, here's a cost to refund in case.
|
||||
var/cost
|
||||
/// Whether we alerted admins about this schedule when it's close to being invoked.
|
||||
var/alerted_admins = FALSE
|
||||
/// Whether we are faking an occurence or not
|
||||
var/fakes_occurence = TRUE
|
||||
/// Whether this ignores event can run checks. If bussed by an admin, you want to ignore checks
|
||||
var/ignores_checks
|
||||
/// Whether the scheduled event will override the announcement change. If null it won't. TRUE = force yes. FALSE = force no.
|
||||
var/announce_change
|
||||
|
||||
/datum/scheduled_event/New(datum/round_event_control/passed_event, passed_time, passed_cost, passed_ignore, passed_announce)
|
||||
. = ..()
|
||||
event = passed_event
|
||||
start_time = passed_time
|
||||
cost = passed_cost
|
||||
ignores_checks = passed_ignore
|
||||
announce_change = passed_announce
|
||||
/// Add a fake occurence to make the weightings/checks properly respect the scheduled event.
|
||||
event.add_occurence()
|
||||
fakes_occurence = TRUE
|
||||
|
||||
/datum/scheduled_event/proc/remove_occurence()
|
||||
if(fakes_occurence)
|
||||
/// Remove the fake occurence if we still have it
|
||||
event.subtract_occurence()
|
||||
fakes_occurence = FALSE
|
||||
|
||||
/// For admins who want to reschedule the event.
|
||||
/datum/scheduled_event/proc/reschedule(new_time)
|
||||
start_time = new_time
|
||||
alerted_admins = FALSE
|
||||
|
||||
/datum/scheduled_event/proc/get_href_actions()
|
||||
var/round_started = SSticker.HasRoundStarted()
|
||||
if(round_started)
|
||||
return "<a href='?src=[REF(src)];action=fire'>Fire</a> <a href='?src=[REF(src)];action=reschedule'>Reschedule</a> <a href='?src=[REF(src)];action=cancel'>Cancel</a> <a href='?src=[REF(src)];action=refund'>Refund</a></td>"
|
||||
else
|
||||
return "<a href='?src=[REF(src)];action=cancel'>Cancel</a>"
|
||||
|
||||
/// Try and fire off the scheduled event
|
||||
/datum/scheduled_event/proc/try_fire()
|
||||
/// Remove our fake occurence pre-emptively for the checks.
|
||||
remove_occurence()
|
||||
var/players_amt = get_active_player_count(alive_check = TRUE, afk_check = TRUE, human_check = FALSE)
|
||||
///If we can't spawn the scheduled event, refund it.
|
||||
if(!ignores_checks && !event.can_spawn_event(players_amt)) //FALSE argument to ignore popchecks, to prevent scheduled events from failing from people dying/cryoing etc.
|
||||
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)
|
||||
return
|
||||
|
||||
///Trigger the event and remove the scheduled datum
|
||||
message_admins("Scheduled Event: [event] successfully triggered.")
|
||||
SSgamemode.TriggerEvent(event)
|
||||
SSgamemode.remove_scheduled_event(src)
|
||||
|
||||
/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].")
|
||||
SSgamemode.remove_scheduled_event(src)
|
||||
if("refund")
|
||||
message_admins("[key_name_admin(usr)] refunded scheduled event [event.name].")
|
||||
log_admin_private("[key_name(usr)] refunded scheduled event [event.name].")
|
||||
SSgamemode.refund_scheduled_event(src)
|
||||
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()
|
||||
@@ -0,0 +1,68 @@
|
||||
/datum/vote/var/has_desc = FALSE
|
||||
|
||||
/datum/vote/proc/return_desc(vote_name)
|
||||
return ""
|
||||
|
||||
/datum/vote/storyteller
|
||||
name = "Storyteller"
|
||||
default_message = "Vote for the storyteller!"
|
||||
has_desc = TRUE
|
||||
count_method = VOTE_COUNT_METHOD_MULTI
|
||||
winner_method = VOTE_WINNER_METHOD_SIMPLE
|
||||
|
||||
/datum/vote/storyteller/New()
|
||||
. = ..()
|
||||
default_choices = list()
|
||||
default_choices = SSgamemode.storyteller_vote_choices()
|
||||
|
||||
|
||||
/datum/vote/storyteller/return_desc(vote_name)
|
||||
return SSgamemode.storyteller_desc(vote_name)
|
||||
|
||||
/datum/vote/storyteller/create_vote()
|
||||
. = ..()
|
||||
if((length(choices) == 1)) // Only one choice, no need to vote. Let's just auto-rotate it to the only remaining map because it would just happen anyways.
|
||||
var/de_facto_winner = choices[1]
|
||||
SSgamemode.storyteller_vote_result(de_facto_winner)
|
||||
to_chat(world, span_boldannounce("The storyteller vote has been skipped because there is only one storyteller left to vote for. The storyteller has been changed to [de_facto_winner]."))
|
||||
return FALSE
|
||||
|
||||
/datum/vote/storyteller/can_be_initiated(mob/by_who, forced = FALSE)
|
||||
. = ..()
|
||||
if(forced)
|
||||
return TRUE
|
||||
|
||||
if(SSgamemode.storyteller_voted)
|
||||
default_message = "The next Storyteller has already been selected."
|
||||
return FALSE
|
||||
|
||||
/datum/vote/storyteller/finalize_vote(winning_option)
|
||||
SSgamemode.storyteller_vote_result(winning_option)
|
||||
SSgamemode.storyteller_voted = TRUE
|
||||
|
||||
/*
|
||||
### PERSISTENCE SUBSYSTEM TRACKING BELOW ###
|
||||
Basically, this keeps track of what we voted last time to prevent it being voted on again.
|
||||
For this, we use the SSpersistence.last_storyteller variable
|
||||
|
||||
We then just check what the last one is in SSgamemode.storyteller_vote_choices()
|
||||
*/
|
||||
|
||||
#define STORYTELLER_LAST_FILEPATH "data/storyteller_last_round.txt"
|
||||
|
||||
/// Extends collect_data
|
||||
/datum/controller/subsystem/persistence/collect_data()
|
||||
. = ..()
|
||||
collect_storyteller()
|
||||
|
||||
/// Loads last storyteller into last_storyteller
|
||||
/datum/controller/subsystem/persistence/proc/load_storyteller()
|
||||
if(!fexists(STORYTELLER_LAST_FILEPATH))
|
||||
return
|
||||
last_storyteller = file2text(STORYTELLER_LAST_FILEPATH)
|
||||
|
||||
/// Collects current storyteller and stores it
|
||||
/datum/controller/subsystem/persistence/proc/collect_storyteller()
|
||||
rustg_file_write("[SSgamemode.storyteller.name]", STORYTELLER_LAST_FILEPATH)
|
||||
|
||||
#undef STORYTELLER_LAST_FILEPATH
|
||||
@@ -0,0 +1,145 @@
|
||||
///The storyteller datum. He operates with the SSgamemode data to run events
|
||||
/datum/storyteller
|
||||
/// Name of our storyteller.
|
||||
var/name = "Badly coded storyteller"
|
||||
/// Description of our storyteller.
|
||||
var/desc = "Report this to the coders."
|
||||
/// Text that the players will be greeted with when this storyteller is chosen.
|
||||
var/welcome_text = "The storyteller has been selected. Get ready!"
|
||||
/// This is the multiplier for repetition penalty in event weight. The lower the harsher it is
|
||||
var/event_repetition_multiplier = 0.6
|
||||
/// Multipliers for starting points.
|
||||
var/list/starting_point_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 1,
|
||||
EVENT_TRACK_MODERATE = 1,
|
||||
EVENT_TRACK_MAJOR = 1,
|
||||
EVENT_TRACK_ROLESET = 1,
|
||||
EVENT_TRACK_OBJECTIVES = 1
|
||||
)
|
||||
/// Multipliers for point gains.
|
||||
var/list/point_gains_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 1,
|
||||
EVENT_TRACK_MODERATE = 1,
|
||||
EVENT_TRACK_MAJOR = 1,
|
||||
EVENT_TRACK_ROLESET = 1,
|
||||
EVENT_TRACK_OBJECTIVES = 1
|
||||
)
|
||||
/// Multipliers of weight to apply for each tag of an event.
|
||||
var/list/tag_multipliers
|
||||
|
||||
/// Variance in cost of the purchased events. Effectively affects frequency of events
|
||||
var/cost_variance = 15
|
||||
|
||||
/// Variance in the budget of roundstart points.
|
||||
var/roundstart_points_variance = 15
|
||||
|
||||
/// Whether the storyteller guaranteed a roleset roll (antag) on roundstart. (Still needs to pass pop check)
|
||||
var/guarantees_roundstart_roleset = TRUE
|
||||
|
||||
/// Whether the storyteller has the distributions disabled. Important for ghost storytellers
|
||||
var/disable_distribution = FALSE
|
||||
|
||||
/// Whether people can vote for the storyteller
|
||||
var/votable = TRUE
|
||||
/// If defined, will need a minimum of population to be votable
|
||||
var/population_min
|
||||
/// If defined, it will not be votable if exceeding the population
|
||||
var/population_max
|
||||
/// The antag divisor, the higher it is the lower the antag cap gets.
|
||||
var/antag_divisor = 1
|
||||
|
||||
/datum/storyteller/process(delta_time)
|
||||
if(disable_distribution)
|
||||
return
|
||||
add_points(delta_time)
|
||||
handle_tracks()
|
||||
|
||||
/// Add points to all tracks while respecting the multipliers.
|
||||
/datum/storyteller/proc/add_points(delta_time)
|
||||
var/datum/controller/subsystem/gamemode/mode = SSgamemode
|
||||
var/base_point = EVENT_POINT_GAINED_PER_SECOND * delta_time * mode.event_frequency_multiplier
|
||||
for(var/track in mode.event_track_points)
|
||||
var/point_gain = base_point * point_gains_multipliers[track] * mode.point_gain_multipliers[track]
|
||||
if(mode.allow_pop_scaling)
|
||||
point_gain *= mode.current_pop_scale_multipliers[track]
|
||||
mode.event_track_points[track] += point_gain
|
||||
mode.last_point_gains[track] = point_gain
|
||||
|
||||
/// Goes through every track of the gamemode and checks if it passes a threshold to buy an event, if does, buys one.
|
||||
/datum/storyteller/proc/handle_tracks()
|
||||
. = FALSE //Has return value for the roundstart loop
|
||||
var/datum/controller/subsystem/gamemode/mode = SSgamemode
|
||||
for(var/track in mode.event_track_points)
|
||||
var/points = mode.event_track_points[track]
|
||||
if(points >= mode.point_thresholds[track] && find_and_buy_event_from_track(track))
|
||||
. = TRUE
|
||||
|
||||
/// Find and buy a valid event from a track.
|
||||
/datum/storyteller/proc/find_and_buy_event_from_track(track)
|
||||
. = FALSE
|
||||
var/datum/round_event_control/picked_event
|
||||
if(SSgamemode.forced_next_events[track]) //Forced event by admin
|
||||
/// Dont check any prerequisites, it has been forced by an admin
|
||||
picked_event = SSgamemode.forced_next_events[track]
|
||||
SSgamemode.forced_next_events -= track
|
||||
else
|
||||
var/player_pop = SSgamemode.get_correct_popcount()
|
||||
var/pop_required = SSgamemode.min_pop_thresholds[track]
|
||||
if(player_pop < pop_required)
|
||||
message_admins("Storyteller failed to pick an event for track of [track] due to insufficient population. (required: [pop_required] active pop for [track]. Current: [player_pop])")
|
||||
log_admin("Storyteller failed to pick an event for track of [track] due to insufficient population. (required: [pop_required] active pop for [track]. Current: [player_pop])")
|
||||
SSgamemode.event_track_points[track] *= TRACK_FAIL_POINT_PENALTY_MULTIPLIER
|
||||
return
|
||||
calculate_weights(track)
|
||||
var/list/valid_events = list()
|
||||
// Determine which events are valid to pick
|
||||
for(var/datum/round_event_control/event as anything in SSgamemode.event_pools[track])
|
||||
if(event.can_spawn_event(player_pop))
|
||||
valid_events[event] = event.calculated_weight
|
||||
///If we didn't get any events, remove the points inform admins and dont do anything
|
||||
if(!length(valid_events))
|
||||
message_admins("Storyteller failed to pick an event for track of [track].")
|
||||
log_admin("Storyteller failed to pick an event for track of [track].")
|
||||
SSgamemode.event_track_points[track] *= TRACK_FAIL_POINT_PENALTY_MULTIPLIER
|
||||
return
|
||||
picked_event = pick_weight(valid_events)
|
||||
if(!picked_event)
|
||||
message_admins("WARNING: Storyteller picked a null from event pool. Aborting event roll.")
|
||||
log_admin("WARNING: Storyteller picked a null from event pool. Aborting event roll.")
|
||||
stack_trace("WARNING: Storyteller picked a null from event pool.")
|
||||
return
|
||||
buy_event(picked_event, track)
|
||||
. = TRUE
|
||||
|
||||
/// Find and buy a valid event from a track.
|
||||
/datum/storyteller/proc/buy_event(datum/round_event_control/bought_event, track)
|
||||
var/datum/controller/subsystem/gamemode/mode = SSgamemode
|
||||
// Perhaps use some bell curve instead of a flat variance?
|
||||
var/total_cost = bought_event.cost * mode.point_thresholds[track]
|
||||
if(!bought_event.roundstart)
|
||||
total_cost *= (1 + (rand(-cost_variance, cost_variance)/100)) //Apply cost variance if not roundstart event
|
||||
mode.event_track_points[track] -= total_cost
|
||||
message_admins("Storyteller purchased and triggered [bought_event] event, on [track] track, for [total_cost] cost.")
|
||||
log_admin("Storyteller purchased and triggered [bought_event] event, on [track] track, for [total_cost] cost.")
|
||||
if(bought_event.roundstart)
|
||||
mode.TriggerEvent(bought_event)
|
||||
else
|
||||
mode.schedule_event(bought_event, (rand(3, 4) MINUTES), total_cost)
|
||||
|
||||
/// Calculates the weights of the events from a passed track.
|
||||
/datum/storyteller/proc/calculate_weights(track)
|
||||
var/datum/controller/subsystem/gamemode/mode = SSgamemode
|
||||
for(var/datum/round_event_control/event as anything in mode.event_pools[track])
|
||||
var/weight_total = event.weight
|
||||
/// Apply tag multipliers if able
|
||||
if(tag_multipliers)
|
||||
for(var/tag in tag_multipliers)
|
||||
if(tag in event.tags)
|
||||
weight_total *= tag_multipliers[tag]
|
||||
/// 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 * weight_total * (1 - (event_repetition_multiplier ** occurences))
|
||||
/// Write it
|
||||
event.calculated_weight = weight_total
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
/datum/storyteller/fragile
|
||||
name = "The Fragile"
|
||||
desc = "The Fragile will limit destructive and combat-focused events, and you'll rarely see midround antagonist roles that usually cause it."
|
||||
welcome_text = "Handle with care!"
|
||||
|
||||
starting_point_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 1,
|
||||
EVENT_TRACK_MODERATE = 1,
|
||||
EVENT_TRACK_MAJOR = 1,
|
||||
EVENT_TRACK_ROLESET = 4,
|
||||
EVENT_TRACK_OBJECTIVES = 1
|
||||
)
|
||||
|
||||
point_gains_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 1,
|
||||
EVENT_TRACK_MODERATE = 1,
|
||||
EVENT_TRACK_MAJOR = 1,
|
||||
EVENT_TRACK_ROLESET = 0.1,
|
||||
EVENT_TRACK_OBJECTIVES = 1
|
||||
)
|
||||
|
||||
event_repetition_multiplier = 0.5
|
||||
|
||||
tag_multipliers = list(
|
||||
TAG_COMBAT = 0.5,
|
||||
TAG_SPOOKY = 1,
|
||||
TAG_DESTRUCTIVE = 0.1,
|
||||
TAG_COMMUNAL = 0.5,
|
||||
TAG_TARGETED = 0.5,
|
||||
TAG_POSITIVE = 2,
|
||||
TAG_CREW_ANTAG = 2,
|
||||
TAG_TEAM_ANTAG = 0.5,
|
||||
TAG_OUTSIDER_ANTAG = 0.25
|
||||
)
|
||||
*/
|
||||
@@ -0,0 +1,156 @@
|
||||
/datum/storyteller/predictable
|
||||
name = "The Predictable Chaos"
|
||||
desc = "The Predictable Chaos will attempt to spawn a lot of antagonists relative to the crew population, while also ensuring events roll every set amount of time. Expect minor events every 10 minutes, moderate events every 30 minutes, and major events every hour and a half."
|
||||
welcome_text = "Waiter, more chaos! That's enough. Thank you, waiter."
|
||||
population_min = 35
|
||||
|
||||
var/crew_per_antag = 20 //Basically this means for every 10 crew, spawn 1 antag. REMEMBER: This is CREW pop, NOT server pop
|
||||
|
||||
tag_multipliers = list(
|
||||
|
||||
TAG_COMBAT = 0.25, //Already got this from the constant antag spawns.
|
||||
TAG_POSITIVE = 1.25, //Increase this even more if there is too much chaos in events. This is basically how you balance this storyteller.
|
||||
|
||||
TAG_TARGETED = 0.25, //Let us not waste event rolls on single people every 5 or so minutes.
|
||||
|
||||
TAG_OUTSIDER_ANTAG = 0.25, //BurgerBB was here.
|
||||
|
||||
)
|
||||
|
||||
starting_point_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 2,
|
||||
EVENT_TRACK_MODERATE = 2,
|
||||
EVENT_TRACK_MAJOR = 2,
|
||||
EVENT_TRACK_ROLESET = 2,
|
||||
EVENT_TRACK_OBJECTIVES = 2
|
||||
)
|
||||
|
||||
point_gains_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 2,
|
||||
EVENT_TRACK_MODERATE = 2,
|
||||
EVENT_TRACK_MAJOR = 2,
|
||||
EVENT_TRACK_ROLESET = 2,
|
||||
EVENT_TRACK_OBJECTIVES = 2
|
||||
)
|
||||
|
||||
event_repetition_multiplier = 0.25 //Repeat events are boring.
|
||||
|
||||
var/last_crew_score = 0
|
||||
var/last_antag_score = 0
|
||||
|
||||
var/antag_event_delay = 5 MINUTES
|
||||
var/mundane_event_delay = 10 MINUTES
|
||||
var/moderate_event_delay = 30 MINUTES
|
||||
var/major_event_delay = 90 MINUTES
|
||||
|
||||
COOLDOWN_DECLARE(antag_event_cooldown)
|
||||
COOLDOWN_DECLARE(mundane_event_cooldown)
|
||||
COOLDOWN_DECLARE(moderate_event_cooldown)
|
||||
COOLDOWN_DECLARE(major_event_cooldown)
|
||||
|
||||
|
||||
/datum/storyteller/predictable/New(...)
|
||||
reset_cooldowns()
|
||||
. = ..()
|
||||
|
||||
|
||||
/datum/storyteller/predictable/proc/reset_cooldowns()
|
||||
COOLDOWN_START(src, mundane_event_cooldown, mundane_event_delay)
|
||||
COOLDOWN_START(src, moderate_event_cooldown, moderate_event_delay)
|
||||
COOLDOWN_START(src, major_event_cooldown, major_event_delay)
|
||||
|
||||
|
||||
//IF YOU EDIT THIS PROC, REMEMBER TO ALSO EDIT THE UNIT TESTS IN unit_tests/zubbers/predictable_storyteller.dm
|
||||
/proc/storyteller_get_antag_to_crew_ratio(do_debug=FALSE,minds_to_use_override)
|
||||
|
||||
var/total_crew_score = 0
|
||||
var/total_antagonist_score = 0
|
||||
|
||||
if(!minds_to_use_override)
|
||||
minds_to_use_override = SSticker.minds
|
||||
|
||||
var/medical_count = 0
|
||||
var/engineering_count = 0
|
||||
|
||||
for(var/datum/mind/mob_mind as anything in minds_to_use_override)
|
||||
|
||||
if(do_debug)
|
||||
if(!mob_mind.current)
|
||||
continue
|
||||
else
|
||||
if(!mob_mind.key || !mob_mind.current)
|
||||
continue
|
||||
|
||||
var/antagonist_score = 0
|
||||
for(var/datum/antagonist/antag as anything in mob_mind.antag_datums)
|
||||
if( !antag.show_in_antagpanel || (antag.antag_flags & FLAG_FAKE_ANTAG)) //For unimportant antags, like ashwalkers or valentines. You're not a real antag.
|
||||
continue
|
||||
antagonist_score += 1
|
||||
//We add to the total antagonist score later.
|
||||
|
||||
if(mob_mind.assigned_role)
|
||||
var/datum/job/current_job = mob_mind.assigned_role
|
||||
if(current_job.faction == FACTION_STATION) //This means you're actually crew.
|
||||
var/crew_score = 1 //You count as 1.
|
||||
if(current_job.auto_deadmin_role_flags & DEADMIN_POSITION_SECURITY)
|
||||
crew_score *= 2
|
||||
if(current_job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD)
|
||||
crew_score *= 1.25
|
||||
if(antagonist_score > 0)
|
||||
if(crew_score > 1)
|
||||
antagonist_score *= crew_score //If you're an antagonist as an important role, then you're going to cause some chaos.
|
||||
else
|
||||
if(mob_mind.current && mob_mind.current.stat == DEAD)
|
||||
crew_score *= -0.25 //If you're dead, then usually some chaos must be happening and you in fact are a slight burden towards the crew.
|
||||
total_crew_score += crew_score
|
||||
//Hacky nonsense here. Limits based on support roles.
|
||||
//MEDICAL
|
||||
if(current_job.supervisors == SUPERVISOR_CMO)
|
||||
if(current_job.title == JOB_MEDICAL_DOCTOR)
|
||||
medical_count += 1
|
||||
else
|
||||
medical_count += 0.5
|
||||
//ENGINEERING
|
||||
if(current_job.supervisors == SUPERVISOR_CE)
|
||||
if(current_job.title == JOB_STATION_ENGINEER)
|
||||
engineering_count += 1
|
||||
else
|
||||
engineering_count += 0.5
|
||||
|
||||
if(antagonist_score)
|
||||
total_antagonist_score += antagonist_score
|
||||
|
||||
if(total_crew_score <= 0)
|
||||
return INFINITY //Force infinity.
|
||||
|
||||
total_crew_score = total_crew_score*0.75 + min(total_crew_score*0.25,engineering_count*10) //Up to 25% penalty if there are no engineers.
|
||||
total_crew_score = total_crew_score*0.25 + min(total_crew_score*0.75,medical_count*10) //Up to 75% penalty if there are no medical doctors.
|
||||
|
||||
return round(total_antagonist_score/total_crew_score,0.01)
|
||||
|
||||
|
||||
/datum/storyteller/predictable/handle_tracks()
|
||||
|
||||
if(!COOLDOWN_FINISHED(src,antag_event_cooldown)) //Don't want to run an antag event then suddenly have meteors.
|
||||
return FALSE
|
||||
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_IDLE) //Only do serious shit if the emergency shuttle is at Central Command and not in transit.
|
||||
if(storyteller_get_antag_to_crew_ratio() < (1/crew_per_antag) && find_and_buy_event_from_track(EVENT_TRACK_ROLESET))
|
||||
COOLDOWN_START(src,antag_event_cooldown,antag_event_delay)
|
||||
return TRUE
|
||||
if(COOLDOWN_FINISHED(src,major_event_cooldown) && find_and_buy_event_from_track(EVENT_TRACK_MAJOR))
|
||||
COOLDOWN_START(src, major_event_cooldown, major_event_delay)
|
||||
COOLDOWN_START(src, moderate_event_cooldown, moderate_event_delay)
|
||||
COOLDOWN_START(src, mundane_event_cooldown, mundane_event_delay)
|
||||
return TRUE
|
||||
|
||||
if(COOLDOWN_FINISHED(src,moderate_event_cooldown) && find_and_buy_event_from_track(EVENT_TRACK_MODERATE))
|
||||
COOLDOWN_START(src, moderate_event_cooldown, moderate_event_delay)
|
||||
COOLDOWN_START(src, mundane_event_cooldown, mundane_event_delay)
|
||||
return TRUE
|
||||
|
||||
if(COOLDOWN_FINISHED(src,mundane_event_cooldown) && find_and_buy_event_from_track(EVENT_TRACK_MUNDANE))
|
||||
COOLDOWN_START(src, mundane_event_cooldown, mundane_event_delay)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
@@ -0,0 +1,68 @@
|
||||
/datum/storyteller/guide
|
||||
name = "The Guide"
|
||||
desc = "The Guide is the default Storyteller, and the comparison point for every other Storyteller. Best for an average, varied experience."
|
||||
antag_divisor = 8
|
||||
|
||||
/datum/storyteller/sleeper
|
||||
name = "The Sleeper"
|
||||
desc = "The Sleeper will be light on events compared to the Guide, especially so on ones involving combat or destruction. Best for more chill rounds."
|
||||
point_gains_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 1,
|
||||
EVENT_TRACK_MODERATE = 0.7,
|
||||
EVENT_TRACK_MAJOR = 0.7,
|
||||
EVENT_TRACK_ROLESET = 0.7,
|
||||
EVENT_TRACK_OBJECTIVES = 1
|
||||
)
|
||||
guarantees_roundstart_roleset = FALSE
|
||||
tag_multipliers = list(TAG_COMBAT = 0.1, TAG_DESTRUCTIVE = 0.3)
|
||||
antag_divisor = 32
|
||||
|
||||
/datum/storyteller/jester
|
||||
name = "The Jester"
|
||||
desc = "The Jester will create the most events overall, with higher chances of repeating. Best for the most hectic rounds."
|
||||
event_repetition_multiplier = 0.8
|
||||
point_gains_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 1.2,
|
||||
EVENT_TRACK_MODERATE = 1.4,
|
||||
EVENT_TRACK_MAJOR = 1.4,
|
||||
EVENT_TRACK_ROLESET = 1,
|
||||
EVENT_TRACK_OBJECTIVES = 1
|
||||
)
|
||||
population_min = 35
|
||||
antag_divisor = 8
|
||||
|
||||
/datum/storyteller/warrior
|
||||
name = "The Warrior"
|
||||
desc = "The Warrior will create more antag-focused events than the Guide, but will spawn less events overall than the Jester. Best for more hectic rounds with a dash of combat."
|
||||
point_gains_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 1,
|
||||
EVENT_TRACK_MODERATE = 1.3,
|
||||
EVENT_TRACK_MAJOR = 1.3,
|
||||
EVENT_TRACK_ROLESET = 1,
|
||||
EVENT_TRACK_OBJECTIVES = 1
|
||||
)
|
||||
tag_multipliers = list(TAG_COMBAT = 1.5)
|
||||
population_min = 35
|
||||
antag_divisor = 5
|
||||
|
||||
/datum/storyteller/demoman
|
||||
name = "The DemoMan"
|
||||
desc = "The Demoman will focus on impactful environmental events, best for hectic shifts with relatively normal antagonists."
|
||||
welcome_text = "What makes me a good demoman?"
|
||||
point_gains_multipliers = list(
|
||||
EVENT_TRACK_MUNDANE = 0.5,
|
||||
EVENT_TRACK_MODERATE = 1.4,
|
||||
EVENT_TRACK_MAJOR = 1.5,
|
||||
EVENT_TRACK_ROLESET = 1,
|
||||
EVENT_TRACK_OBJECTIVES = 0.8
|
||||
)
|
||||
tag_multipliers = list(TAG_DESTRUCTIVE = 2.5) // You asked and I delivered. Destructiveness increased
|
||||
population_min = 25
|
||||
antag_divisor = 10
|
||||
|
||||
/datum/storyteller/ghost
|
||||
name = "The Ghost"
|
||||
desc = "The Ghost is the absence of a Storyteller. It will not spawn a single event of any sort, or run any Antagonists. Best for rounds where the population is so low that not even the Sleeper is low enough."
|
||||
disable_distribution = TRUE
|
||||
population_max = 35
|
||||
antag_divisor = 32
|
||||
@@ -2,6 +2,7 @@
|
||||
title = ROLE_DAUNTLESS
|
||||
policy_index = ROLE_DAUNTLESS
|
||||
akula_outfit = /datum/outfit/akula
|
||||
antagonist_restricted = TRUE
|
||||
|
||||
// Dauntless Ghost Spawners (Lava)
|
||||
|
||||
|
||||
@@ -495,6 +495,7 @@
|
||||
#include "code\__DEFINES\~~bubber_defines\signals.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\species.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\status_indicator_defines.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\storyteller_defines.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\traits.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\transport.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\___HELPERS\global_lists.dm"
|
||||
@@ -8254,6 +8255,7 @@
|
||||
#include "modular_skyrat\modules\subsystems\code\ticket_ping\adminhelp.dm"
|
||||
#include "modular_skyrat\modules\subsystems\code\ticket_ping\preference.dm"
|
||||
#include "modular_skyrat\modules\subsystems\code\ticket_ping\ticket_ss.dm"
|
||||
#include "modular_skyrat\modules\supermatter_surge\code\supermatter_surge.dm"
|
||||
#include "modular_skyrat\modules\supersoups\code\soup_mixtures.dm"
|
||||
#include "modular_skyrat\modules\supersoups\code\stove.dm"
|
||||
#include "modular_skyrat\modules\Syndie_edits\code\area.dm"
|
||||
@@ -8755,6 +8757,30 @@
|
||||
#include "modular_zubbers\code\modules\status_effects\buffs\frenzy.dm"
|
||||
#include "modular_zubbers\code\modules\status_indicators\status_indicator.dm"
|
||||
#include "modular_zubbers\code\modules\status_indicators\status_indicator_pref.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\config.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\divergency_report.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\gamemode.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\scheduled_event.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\storyteller_vote.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\_event.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\disabled_event_overrides.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\major\major_overrides.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\moderate\moderate_overrides.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\mundane\mundane_overrides.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\mundane\scrubber_overrides.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\_antagonist_event.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\roleset_ghost_overrides.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\crew_antagonists\bloodsucker.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\crew_antagonists\changeling.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\crew_antagonists\heretic.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\crew_antagonists\malf.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\crew_antagonists\nuke_ops.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\crew_antagonists\spies.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\event_defines\roleset\crew_antagonists\traitor.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\storytellers\_storyteller.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\storytellers\storyteller_fragile.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\storytellers\storyteller_predictable.dm"
|
||||
#include "modular_zubbers\code\modules\storyteller\storytellers\storyteller_tellers.dm"
|
||||
#include "modular_zubbers\code\modules\surgery\tools.dm"
|
||||
#include "modular_zubbers\code\modules\surgery\bodyparts\dismemberment.dm"
|
||||
#include "modular_zubbers\code\modules\surgery\bodyparts\species_parts\misc_bodyparts.dm"
|
||||
|
||||
Reference in New Issue
Block a user