Forensic Friday 61: Storytellers & Facility Raids
Welcome to Forensic Friday 61! Not that you didn’t know this was Forensic Friday, but… we have a lot to show! This week has been lightning-fast for features. This week I’ve been focusing on Facility Raidsm, adding some new sprites to the game, and a lesser extent a water/flooding system and storytellers??? Since I finished an entire feature rather quickly, I thought I should give a random feature further along the line a try, and that feature was flooding (and storytellers). Other than that, that covers most of what we have been up to this week! So all in all, we have tackled 50% of the Storyteller batch, 50% of the Combat & Defence batch, and 25% of the Maintenance System Batch, all while working on the Personnel Intake batch. So a lot of parallelised work has been going on!
Facility Raids #
Facility raids are the biggest feature that I tackled this week. Raids work pretty simply. Raiders spawn at the edge of the map and slowly make their way towards whatever structures you have in your base. Currently, the criteria for raids to start (and their difficulty) depends on how many anomalies you have on your site, and how many prisoners you have. Prisoners are simply raiding party members that have been injured and apprehended. Currently, these prisoners are hauled to medical beds (more about this later), but in the future, we will have a system to imprison these raiders and integrate them into your workforce of Category C. You can see a video of raiders attacking below!
Raiders are somewhat smart, they will only attack things of value and will attack perimeter walls in an attempt to breach them and gain any valuables on the other side. Raiders are a real test of our new AI system, but so far it has been looking good. A lot better than what we would have achieved with the old AI system at least.
Raids are considered complete when over 50% of the raiders have fled or are incapacitated, and each beaten raider yields a 1% increase in reputation score. NPCs fleeing is also a new feature for their AI, which segways nicely to some of the additions and improvements to NPC’s combat AI!
Combat AI improvements #
NPCs can flee when faced with a certain death. This was a pretty easy thing to achieve by utilising a neat trick with our A* pathing system. All we have to do is call a path request to the target we want to flee from, then inverse the A* heuristic (meaning positions that lead the NPC away from the target are favoured), meaning the NPC finds be perfect route that takes them as far away from the target as possible. Neat!
It hasn’t been perfectly tested in all situations, but from our observational tests, it seems to work pretty well. The only issue that sticks out right now is getting NPCs to choose to flee rather than fight the threat head-on. But when we get around to adding a “Fight, Flight or Freeze” system to the combat AI, we can influence what actions NPCs take more easily.
Sadly no footage of NPCs fleeing, but not that exciting, just looks like they are pathing anyway.
Hauling to Medbay #
Another feature I added to combat AI, which is adjacent to the raiding, is the ability for NPCs to haul other injured NPCs to free medical beds in the world, including raiders. However, these medical beds need to be inside infirmaries for them to be valid. It’s a feature that I can certainly imagine will be used in the future for the Doctors and Surgery feature. It was surprisingly easy to implement which makes me optimistic about the turnover rate for some of these NPC-centric systems.
Storyteller Framework #
A very unexpected feature that I implemented this week was storytellers. At least the framework for it, and it works in quite an interesting way. The way we think the storyteller works in Rimworld is by choosing events with a weighted random (depending on the personality of the storyteller) from a pool of events, a pool which grows in size as you become more wealthy. This seems to work pretty well, but we decided to be novel and see whether we could reuse our utility AI system for storytellers. So our storytellers use the same AI as our NPCs for making decisions on events to spawn, rather than things to do.
Below you can read my design philosophy behind the Storyteller, and then beneath that, I’ll show you how that translated to an implementation:
The Storyteller: What do I mean?
The storyteller, in short, would be an AI we program to orchestrate events in the game. Instead of the events happening randomly without any coherency, we create a bot to create events on the fly to keep the game as interesting as possible and leave the player satisfied.
The storyteller aims to keep the player engaged on an in-game daily basis and keep the player wanting more the next day, whilst creating a high-level level long-term coherent story that will leave the player a long-lasting experience to remember and tell their friends about.
“Yeah, seriously. I was playing a game where my facility had a major breach. The surrounding area plunged into an eternal winter and my facility was isolated for more months. My staff members slowly went mad and a cult formed to worship the anomaly that caused the whole ordeal. By the end, a bunch of anomalies and staff members teamed up to defeat the cult and escape the wintry prison, and they succeeded somehow. But just as they were about the get their freedom, the facility got nuked. This was all within the first quarter!”
A player tells his friend, and his friend is intrigued and buys the game.
To keep the player engaged we could define a template of engagement for the AI storyteller to use, for example:
- Rising action
It could then apply this template on an in-game daily basis for example:
- Hook: Head command sends a grant if you contain an anomaly. Will you accept it?!
- Fall: The anomaly arrives and you take the necessary precautions to contain it.
- Rising action: You assign a staff member to research it. Turns out he has a dark history with this particular anomaly.
- Climax: The researcher freaks out in a chaotic outburst, almost causing a breach in the kerfuffle, that ends with him having to be locked up in his office
- Denouement: The researcher calms down, and after a 2-hour therapy session with the onsite psychologist, they can return to work.
This is a basic story structure that could occur during the day. The story was emergent but still coerced by the storyteller. But how? This NPC never had a history with the anomaly, but the storyteller injected this trait into the NPC, in hopes of causing something. The storyteller also created the inciting incident of the anomaly grant being sent to the player.
This example shows how we could program the storyteller. An "AI" that detects lulls in the game, and fabricates events and information to create "stories".
A question comes to mind. Is it worth doing this? ABSOLUTELY. Not only is the structure of hooks and a climb to a climax engaging, but it's also addictive. Players should ask themselves "I wonder what's going to happen tomorrow?"
This is a basic daily application of the storyteller but what about broader terms?
The Wider scale
There is no reason why this storyteller should be restricted to day-to-day gameplay. Ideally, we would want to show wider story structures.
The story of the rise of a new facility, the influx of researchers and investment opportunities, an age of innovation and technology, a "fly too close to the sun" moment and the inevitable total collapse of the facility into disrepair.
Or perhaps, the story of a facility left in ruin, brought back to its former glory, only to fall again into ruin, but this time take the entire world with it.
These stories are all possibilities within the game’s mechanics but are diluted amongst the sea of randomness. By using the storyteller we can nudge the gameplay into making more interesting playthroughs over the many hours of playtime.
A possible implementation
We could simply apply the daily engagement structure to individual months, or perhaps more suitably, each quarter of the year.
So each quarter, the storyteller would try and follow an overall structure of:
- Status quo
- Introducing inciting incident; a challenge
- Players adapting to the challenge
- Players achieving their goals
- The storyteller introduces a twist or a cost to the player's success in overcoming the challenge
- The player has changed their outlook on the game and its mechanics or reassessed how they should play, effectively they have experienced a story arc
Since a year in the game is 608 hours at normal speed or 6 hours full speed, we could expect around 70 to 100 hours for a full year, and a full story with 4 "arcs".
4 arcs give us a lot to play with. We could create full act-like structures, each being a fully contained arc. We could then have the storyteller select act templates such as these 4-act templates below:
A Possible Way to Beat the Game
One thing we could do, which would be an interesting way to actually "beat" the game, is to guarantee players that they will have to face an end-of-world scenario. If they successfully save the world from this scenario, then they have beaten the game.
However, it may be tasteful to count losing the game, to a breach or an end-of-world scenario, as a valid ending. However, this is beside the point.
The point is having a larger scope structure that the storyteller will try to nudge the gameplay towards.
The Many Avenues of Nudging
We need a way to formalise how the storyteller can "nudge" the story along. We already gave an example of the storyteller giving the player grants, but what else can it do?
Storyteller should be able to do 3 things:
- Generate Events
- Log and present information
- Inject New Information
The storyteller most simply should generate events based on their evaluation of the engagement and story curves. In lull points, it should generate exciting events, after a period of excitement it should hold back a bit, and in the perfect moment should generate story beats.
Events are simple in the sense that they can be easily predefined such as:
- Anomaly sighting
- Prison fight
- Malfunction of device
- Natural disaster
The hard thing is that all these events have many variables such as, who is raiding? Why are they raiding? So the job of the storyteller is not only to provide these events at appropriate times but also to make them coherent. For example, if you just mended relations with a group of interest, it wouldn't make sense to be raided by them the next day, unless it was some sort of betrayal etc.
This is quite important as the player needs a way to inspect or find out about all of these story-driven occurrences. Broad events are trivial, we already have a system to display event notifications to the player, and even down individual significant character moments could be displayed this way. But what about character arcs, less significant character moments and overall character progression over time?
The best solution, taken from the pages of Dwarf Fortress, is keeping a log of all events. This log could then be filtered through to see events about specific characters, events that include a group of characters, or events that are broader in scale.
This would give the player a way to get a temporal overview of the overall stories that are happening in more detail.
Injecting New Information
Injecting new information is the hardest thing. How would you know whether to secretly edit the trait of a scientist in an attempt to cause an interesting scenario? There are two things we could do: Forget about the system and just use events or implement the system by giving every actor a secret trait that the storyteller can edit and activate at any moment.
This trait could be a hidden ability for an anomaly that the storyteller activates just after u recovered from a major breach or a mental illness that now randomly affects an NPC.
Though having a broad story is cool, all we have is just a series of interesting, well-paced events. But no story is complete without characters to inhabit it. We want to give players something to remember. A breach is memorable, but will probably be a shared and common experience. But I doubt many will be able to recall a time when Bob the heroic Category C saved the facility from a breach by releasing a bunch of anomalous cats, whilst simultaneously falling in love with their assigned overlooker. But having to ultimately choose between having the cats destroy the world or killing their lover.
That was a bad example but you get what I mean. A better example could be a scientist, who was a wimp, finally becoming strong and being able to run an experiment, but at the cost of losing all their colleagues.
You get the gist.
Characters are interesting because they have:
- Characterisation through strengths and flaws
- They change over time
It would be amazing if Containcorp could generate character arcs, as this would catapult it to an unforgettably amazing story engine.
The characterisation is simple, we already do it in some aspects. "Characters" would only need a few attributes
- Influence- this would affect how often the AI storyteller interacts with this character. High influence is more akin to the main character.
- Traits - These would be various strengths and flaws that make the characters unique. They would need to balance each other out.
- Backstory - We already have this. Every character has a backstory that influences the pool of traits they can select from/guarantees they will get certain traits
This is all you would need, the physical attributes such as age, gender, and ethnicity are already handled.
Arcs are considerably harder to formalise. The best way is to think about the different arcs characters could theoretically go through, and think of ways to represent these more programmatically.
Arcs can simply be described as a series of rises and falls, the most common are:
- 'Rags to riches' (rise)
- 'Tragedy', or 'Riches to rags' (fall)
- 'Man in a hole' (fall-rise)
- 'Icarus' (rise-fall)
- 'Cinderella' (rise-fall-rise)
- 'Oedipus' (fall-rise-fall)
We could assign each character an arc, and then also decide what a rise or fall looks like in the context of the character.
The best way to do this would be to perhaps assign a "value" to each character; decide what each character values, whether that's money, fame, freedom, bloodshed etc.
A rise would be a path to attaining this value in its purest form, whereas a fall would be losing this value and being thrown as far away as possible from it.
Regardless, at the end of these arcs, characters will change. We can change their value based on their arc to align with its end. I.e. if a character had a fall from grace, from high reputation to low, then they stop valuing reputation and instead value [Insert something opposite]
All of this is a top-down approach to generating interesting stories, but a laid-back approach. Ideally, we would want the game to create emergent stories from just the mechanics interacting, but not every story is going to be coherent or interesting. By using the AI storyteller, we can filter through the randomness and ensure the player is always presented with interesting scenarios and events to not only deal with but take part in, and observe how other characters fit in.
A lot of these ideas could be implemented in a bottom-up approach, but that would require a lot more deep simulation and complexity. The AI storyteller, though complex itself, offloads the complexity and eliminates the unavoidable noise that a bottom-up approach would create.
All of that long rabbling over, onto the actual implementation. Initially, it seemed like a huge task needing a complex algorithm, or even a neural network. But no, utility AI covered it perfectly. In short, we have various attributes which we call “story goals”. These story goals are categories which the storyteller aims to satisfy (to create their intended experience). The story goals we have currently are:
Every event in the game satisfies one of these story goals. For instance, the example used above where “a researcher had a dark history with a recently contained anomaly, which caused them to freak out in a chaotic outburst during an experiment, almost causing a break and forcing them to be locked in their office” would satisfy the Drama story goal. Specifically the inciting incident “a researcher had a dark history with a recently contained anomaly”, which is the spark which leads to the emergent results of the rest of the story.
Each storyteller has a value for each of these storygoals, each ranging between -100 and 100, which tick down at a predetermined rate. The lower the number is, the more starved they are of that story goal. Each story goal also has a curve, which dictates how the storyteller prioritises said goal depending on its value.
there are also engagement curves which vary over a daily, monthly and yearly basis. This creates a resultant intensity value which dictates the maximum intensity of an event that can happen at that time.
Storyteller Data showing curves for each attribute
Combining these nets a resultant urgency for each storygoal, which can be used to rank a list of events.
This list of events is scattered across the codebase, so instead of the storyteller injecting information, events are broadcast from various points in the codebase, and the storyteller can then rank and choose the events. It allows us to have hundreds of events unique to every NPC, anomaly, entity and feature in the game, which effectively solves the issue of the storyteller being able to nudge and change the game state.
So the storyteller is more like a custodian who orders and chooses events that should happen, instead of creating the events themselves. Akin to a librarian compiling books in a library best suited for the player to read!
The best thing about the way I’ve set up the framework is that it is incredibly easy to tweak for different storyteller personalities.
Flooding System #
Another thing I tackled briefly this week, is a flooding system. A feature completely out of left field, but it’ll be needed eventually so no harm right? So what is it? It is (a currently rudimentary) system for fluid volumes to wreak havoc across your facility. It is a direct continuation of the pipe & fluid system and bridges the gap between the mysterious fluids inside pipes, and the very annoying fluids outside of them. Right now the only thing that interacts with the system is shower heads (and pushing NPCs around), but soon we will feature burst pipes, rainwater, and drowning NPCs!
NPC taking shower. Note: Shower makes reliable water, so many showers would be needed to make a noticeable impact
You know the game is getting complex when you can’t figure out why NPCs aren’t showering, and it’s because the shower water wasn’t filtered for pollutants, and only then you build this monstrosity that works…
New Assets #
The last thing I spent doing this week was adding new assets to the game and improving the look of certain things.
Some new floors and walls! Also, the research desk finally has a sprite (guess which one). Some of the old sprites need redoing, as we start to settle on an art style for tile assets!
Case Closed #
That’s all for this Forensic Friday! Unfortunately delayed, but that’s become pretty common for the past 2! Expect one of the feature batches to be done as we pick up the lightning pace! Keeping this closer short and sweet (mostly because it’s late and going to bed seems like an amazing idea). Anyway, thanks to all who read! Ciao!