# Outdated description This document is now historic and a more descriptive version can be found here: https://github.com/space-wizards/docs/blob/076d601f5fcbe88e2e8c38e0e3464f3c617e3796/src/en/proposals/game-director.md # Game Director / Dynamic Event Scheduler Chooses events with intent, rather than randomly. Does this towards a narrative purpose, either to endanger the station or to return order. ## What is our story? Player experience in SS14 should have both its highs and lows. A peaceful extended shift can become boring with no challenges to overcome together. An overly intense battle might kill half the crew and leave the station in disorder that we cannot recover from. What we want is a middle ground with some variation. The ideal story has a mix of both, with order followed by disorder and then a chance to recover and rebuild. ### Example story (aspirational) - Traitors arrive on the station, but cause minor chaos and are rounded up - A number of minor occurences happen, anomolies, gas leaks and whatnot - There is an attack by a band of nukies who kill sec and are threating to destroy the station - A band of security reinforcements arrive (powered by the ghosts of sec and others) - Enemies are killed but the station is in tatters - Engineering staff and supplies arrive and begin repairing the station - Pizza is delivered - Peace resumes (for now?) Broken into "story beats", the above story had these goals: **Traitors -> Peace -> Attackers -> RestoreOrder -> RepairStation -> Peace** We want a round to follow stories we can understand, with each story beat automatically triggering relevant events that follow our goals at that time. The goal might be to bring order to the station, or it might be to sow a very specific sort of Chaos. ## How do we describe what an event does? Events have a metric called "Chaos" which describes different types of negative effects they bring to the station. Good events cause negative chaos. ### Negative events increase chaos ```yml - type: entity id: SpiderSpawn parent: BaseGameRule noSpawn: true components: - type: StationEvent earliestStart: 20 minimumPlayers: 15 weight: 5 duration: 60 chaos: CombatHostiles: 40 # New hostiles are introduced CombatFriends: 20 # Friends are likely to die Medical: 150 # Medical will have wounds to heal - type: VentCrittersRule entries: - id: MobGiantSpiderAngry amount: 4 maxAmount: 8 ``` ### Positive events reduce chaos ```yml - type: entity id: PizzaPartySmall parent: BaseGameRule noSpawn: true components: - type: StationEvent weight: 10 startDelay: 10 duration: 120 chaos: Hunger: -80 # The pizza party satisfies hunger Thirst: -40 # And also thirst - type: CargoGiftsRule descr: A small pizza party sender: NanoTrasen careof: The Bar gifts: FoodPizzaLarge: 1 # 16 pizzas FoodBarSupply: 1 FoodSoftdrinks: 1 CrateVendingMachineRestockRobustSoftdrinks: 1 ``` ## How do we know how bad things are on the station? We want to measure how bad the Chaos is right now. If the station is doing well, the lights are on and the floor is clean, we expect a low chaos score. If the lights are out, the place is spaced and enemies are roaming the station, we want a high chaos score. Because we went events tailored to story requirements and sensitive to the exact situation on the station, chaos is measured on several axes. The solution to hunger is pizzas. The solution to enemies might be a squad of reinforcements. ### Code to measure Chaos A number of systems called "Metrics" are used to infrequently (perhaps every 30 seconds to 4 minutes) summarize the chaos levels. Metrics each stand alone and so it will be easy to add or remove them as the game matures. Each metric is set up like a gamerule - it has a system, a component and an invisible entity. These prototypes are instantiated by the dynamic system when needed. Metrics could subscribe to relevant events and dynamically adjust their scores as events occur on the station. Or they can do a single pass through the component system when run. The single pass approach has been preferred in favor of its stability and simplicity for now. #### Metrics at the moment - **AnomalyMetric** - Are there many? Are they out of control? Writes to "Anomaly" - **CombatMetric** - Who is on the station? How injured are our friends? Writes to "Hostiles", "Friendlies", "Combat", "Death" and "Medical" - **DoorMetric** - Uses doors as a proxy to surveying the ship for danger. Writes to "Emag", "Atmos" and "Power" - **FoodMetric** - How hungry are the friendly crew? Writes to "Hunger" and "Thirst" - **JaniMetric** - How messy is the station (partially as a proxy for safety). Writes to "Jani" I expect that as we describe a situation we want the Director to react to we will introduce further metrics to give us richer insight into the station. We might want trust metrics based on how many traitors there are. Or staff / department metrics based on staffing issues and role deaths. ## How bad do we want things to be? Each of the **story beats** from above has a matching chaos level, specifying factors that we care about at that point in the story along with target values for those **Chaos factors**. **Traitors -> Peace -> Attackers -> RestoreOrder -> RepairStation -> Peace** While each Story beat will stay around for a certian amount of time, it can end early if certian Chaos levels are met, so called `endIfAnyWorse` and `endIfAllBetter`. These are useful if there is too much war, or perhaps too much peace. ```yml - type: entity id: GameDirector parent: BaseGameRule noSpawn: true abstract: true components: - type: GameDirectorSystem storyBeats: Traitors: description: Traitors have infiltrated the station goal: # Some Traitors: 40 Hostile: 60 # Some hostiles (includes traitors) endIfAnyWorse: Atmos: 400 # Severe atmos, power or medical issues should end this beat. Power: 400 Medical: 400 Death: 200 Combat: -100 # Friendly forces should retain the upper hand Peace: description: Life returns to usual, but how usual? goal: # Try to achieve a wide range of different moderate values. Combat: -200 # Friendly forces should have the upper hand Anomaly: 100 Atmos: 200 Power: 100 Jani: 100 Hunger: 100 Thirst: 100 Medical: 100 Attackers: description: Hostiles are attacking the station. goal: Hostile: 100 # Quite a few hostiles endIfAnyWorse: Atmos: 800 # Severe atmos, power or medical issues should end this beat. Power: 800 Medical: 600 Death: 400 Combat: -50 # Friendly forces should just retain the upper hand RestoreOrder: description: Send help to quell disorder on the station # ... - type: entity id: CombatDynamic parent: GameDirector noSpawn: true components: - type: GameDirectorStories Combat: Description: After traitors invade, the station descends into combat and rises from the ashes. MinPlayers: 40 Beats: - Traitors - Peace - Attackers - RestoreOrder - RepairStation - Peace ``` ## Picking the best event Once we know what **Chaos metrics** we currently attempting to achieve, we have a chance to select the correct event. - The **Story Beat** has told us what chaos we want. - The **Metrics** tell us what chaos the station currently has. - Each **StationEvent** has a Chaos field predicting that event's impact So we iterate through all the possible events, choose the one which moves the station chaos nearest to our goal and set that event into action. Simple! The whole process is richly logged into the admin log (under GameDirector) so the admins have insight into what the director is attempting to achieve. # Conclusion The Game Director system will allow us to author specific experiences that are gated on how chaotic the station has become. The more events we introduce to the game with clear chaos outcomes, the better the system will be at guiding the station through a specific narrative experience. The data driven nature of the metrics and story data means that a wide variety of narrative outcomes and station-specific events can all be achieved through the same system.