The most difficult building to program in Stranded is this:
Players can concoct various drinks at a bar, such as Fruit Juice or Alcohol, once you unlocked their recipes. NPCs can then come to the bar, sit down and consume them.
One day, players suggested that it would be good if you can change the recipe of the bar without having to dismantle and rebuild the bar, as recipe selection for choosing what you want the bar to concoct is done during construction.
Sounds easy doesn’t it? Just add a reset button!
The Reset Button
So I coded it and it works…98% of the time.
When you hit the reset button, the player is now prompted to re-select a new recipe. It works, so what’s wrong?
Well here’s the thing I just found out.
If you open a bar to make Fruit Juice, NPCs sit in the bar and take about 5 seconds to finish drinking it. Yes, there is a delay. In the process, they gain ‘Thirst’ gradually over those 5 seconds.
However, if in those 5 seconds, you decided to change the recipe while they were drinking, to ‘Alcohol’, which provides ‘Happiness’ instead of ‘Thirst’, then would the NPCs suddenly switch?
Well, no problem I just set the coroutine to specify the drinkType at the start of the interaction. That way if they started with juice, they’d continue to drink juice even if the bar now produces Alcohol.
But wait, what if the player decides that before those 5 seconds are up, he decides to PAUSE the game, and hit SAVE. Then exit the game.
The game has to be persistent, thankfully I already save all building states and recipes to a savefile, as well as a full list of NPCs who happen to be interacting or using each building. And yes I also save their ‘drink progress’, so if an NPC had 2 seconds out of 5 seconds of drinking done, they’d reload and only gain a remaining of 3 seconds worth of stats. That took some work but at least I got it. Haha, how smart, I thought. Then as I read my code, I realise something.
When the player reloads the game, the game initializes the buildings’ recipes, and guess what? All the NPCs at that bar are now drinking alcohol even though they started with Fruit Juice! And they are now gaining ‘happiness’ instead of ‘thirst’, even though they consumed the Juice items.
So what I’d have to do now is to create a new save variable for every character to store what drink they are drinking so that if the recipe changes, they still remember what they are drinking after quitting and reloading.
This is getting to become a lot of work.
But that’s not difficult because this is do-able and isn’t what makes the bar difficult. Here’s where I almost had a meltdown.
Stranded was coded to have buildings be interacted with in only one way. See, some buildings in Stranded can be ‘interacted’ with to either produce resources or consume resources.
Example of Production buildings:
There is a Water Pump. You pump water. You get water, done.
There is a Cookhouse. You cook food. You get Food, done.
Example of Consumption buildings:
There is a Water Tank. You drink from it, you gain Thirst, done.
There is a Dining Table. You eat on it, you fulfill Hunger, done.
Then…there is the Bar. You make juices. You get juices. done? No wait. The bar is BOTH a production building and a consumption building. You also have NPCs consuming juices at the bar!
For a long while in Stranded, I made NPCs take juices from the bar, and go to a separate facility to consume the drink. But the logic always bothered me because then settlers could collect a drink and then have to travel across the map to a ‘Dining Table’ to consume the measely drink.
I had a long think about whether to begin the daunting task of refactoring the code to allow the Bar to execute both and eventually attempted it. I don’t regret it but darn, it was very annoying. See the thing about interaction is there is a list of Characters to store so buildings have a reference to who is currently using it.
That character list exists for every building and cannot be shared. If you assign a settler to produce, it goes to that building and it must know that it was set to ‘Produce’ role instead of ‘Consume’ role.
Call it a stupid implementation but I set up interaction to be versatile enough that each building would default to execute a certain code depending on whether I set it to be ‘Production’ or ‘Consumption’, so there was no way to merge the code as buildings were either one or the other. A settler would go to a building knowing its role but the building would handle the interaction. Combining the list would result in something like a character going to the Bar to drink, and end up bartending instead of drinking, and the second character who was going to go there, realise that the ‘Bartender’ slot is taken up, thus assigns himself to drink instead! But because of the failsafe I added that he only accepts the ‘Bartender’ role, he won’t drink but he ends up defaulting to wandering around looking for another Bar. So while some parts may appear to not break the game, a lot of hell will break lose. And hence the game is actually breaking silently and running quite inefficiently.
Beginning to rewrite my code
I began to refactor the block of code containing building interactions, separating the logic into ‘interact’ and ‘work’. Working for production, Interacting for consumption.
I had double the amount of code and functions. But things got really messy when I began getting involved with the UI bits. I had to complicate the saving code, the UI code, the character code.
Every time I refactored one function, I had two other functions invoked by it that needed refactoring. Soon it was just a never-ending growing list of functions that needed to be refactored.
The 90/10 Solution
After about a full night of work, I decided to discontinue the fixes.
Yes, it was only one night, but I could see this taking up a lot of time and this was an experiment. Every feature I add to the game is an experiment. If it’s something great, I keep on it and if it’s not, I have to resist falling in love with something unworkable and trash it. In this case, I had deemed it unworthy to continue on this.
For every problem we face, there is something called the “90/10 solution”.
A solution where you spend 10% of the effort to achieve 90% of the result.
I decided that refactoring a whole manager class was not ideal to support just ONE measly building. So what I did was just to make the bar ‘automated’. So the bar produces drinks by itself! It does not hurt the gameplay that much, and I can balance around that quite easily.
I kept the reset recipe button.
I added the ability to go drinking at bars.
I was considering to take out bartending, but since the solution works 98% of the time, I think I am leaving it as an experimental feature for now. It’s cool and I can always take it out in future if it is too troubling! I know 98% is not 100%, but hey this is a prototype after all and I have room for experimentation.
Problem solved! And now I can spend all this extra time working on stuff that actually matters.
The thing about games is there is a lot of things running in parallel. Unlike say developing an app, where things may somewhat run linearly, in Stranded your character may be running about, taking resources out from a building, moving the building while NPCs are trying to reach it, or taking resources from a building that NPCs are interacting with. All while in the background, animations are playing, the weather is changing, the lighting is dimming as rain falls, and the rain then waters the farms, which causes their production to increase, etc. And then realise all this code has to also be somewhat duplicated to work in multiplayer.
There’s like a bunch of different system managers I have in my code, sometimes I wonder if I am programming them as efficiently as I should. Every new mechanic that I add that requires its own gameplay manager has to work with every single previous manager and not cause bugs in the other systems. If it’s an independent system that is easy, but if it’s something that depends or links with other managers, it gets tricky.
A player (PlayerManager) in a multiplayer room (MultiplayerManager) presses the arrow keys (InputManager) to build something (BuildingManager). This updates the walkable areas for the AI (NavMeshManager) and takes resources (InventoryManager) to put into the settlement (ResourceManager), to advance his technology (ResearchManager), where he can assign (JobManager) settler NPCs (CharacterManager) to work while looking out for potential new settlers arriving by sea (BoatManager) to exchange items with (TradeManager). When that happens, there is a notification (NotificationManager) and since this is his first time trading, there is a brief tutorial (TutorialManager), but it can be disabled through settings (SettingsManager). And when it rains (WeatherManager), it plays sounds (AudioManager) and maybe cause some story events (StoryManager)… Okay imma stop.
Well, welcome to game development. That’s all for now. Until next time!