Succor 2025 update postmortem


WARNING! SPOILERS FOR THE GAME INCLUDED IN THIS POST!

I'm quite excited to share this postmortem!

 I've finally returned to Succor, a game initially made in a jam, and given it a major overhaul and update. I feel comfortable with where the game's state is (though there's always going to be a temptation to tinker more) and I'm happy to call it a finished, polished play experience. Most of my game creations are jam experiments, so it feels particularly nice to be able to put a stamp of "finished" on a game - one step further down the road in becoming a proper game dev!

Background

This game in particular feels a bit emotional to finish. Not only does the game cover rather deep and dark topics about trauma and memory, it's my first coding project in nearly a year after struggling with major health setbacks. For many months, I couldn't even sit at a computer, much less wrangle my brain into coding or writing extensively...but the final word count is around 20,000 words! Phew!

The original jam game was a small experiment in how traumatic memories can be sparked by things as mundane as reading a menu and encourages players to battle these demons by making either constructive or destructive choices. The new version has expanded on this concept, with many more additions to content.

Code and Design Updates

As this was a project created fairly early in my game dev journey, a lot of this code was a MESS and I spent a decent chunk of time focusing on behind the scenes things, such as creating widgets (Twine's versions of functions) to streamline creation. Some old code I merely tinkered with a bit, as I didn't want to get caught up in too much refactoring, as I didn't want scope to run away from me. At the end of the day, this is an experimental text game, so "good enough" works for many things.

One major change I added was creating a function to track the game's demons and adjust how demons were assigned to appear though the game. Originally, demons were simply tied to menus: browsing the menu for a bakery spawns a demon for addiction, for example. Our wonderful artist had created several additional pieces of artwork and I wanted to include these as well, so I ended up expanding out the available menus to give each demon one they were tied to. However, I felt simply adding more menus to the table would be a rather dull play experience, so I instead added several "hidden" menus players can find through cleaning the house, as well as some lurking demons triggered by the act of cleaning itself - and then to give the game a bit of replayability (because there are multiple endings and achievements), I decided to shuffle around where they were each new game.

<<set $demonsToDo = Array.from(setup.demons.list)>>
<<set $menusToDo = Array.from(setup.menus.list)>>

This code creates an array of all the potential choices for both demons and menus at the start of the game (storyInit as well as a resetVars widget which runs when the player returns to the main menu at the end of the game). It pulls these values from javascript objects.

setup.demons = {
    list: ["insecurity", "humiliation", "addiction", "loneliness", "abuse", "rage", "regret", "envy", "lethargy", "paranoia"],
...
}
setup.menus = {     list: ["indian", "pizza", "french", "sweets", "bbq", "italian", "chinese", "grill", "turkish", "cajun"],
...
}

It then plucks (randomly removes a value from the array) , creates different menu details and removes the demon associated with that particular menu. So if "sweets" was plucked, it would pick one of the random names for a restaurant (eg "The Sweet Tooth" or "Toothsome Temptations") and also set the associated demon. 

<<set $menu1 = $menusToDo.pluck()>>
<<set $menu1name = setup.menus[$menu1].random()>>
<<set $menu1Demon = setup[$menu1].demon>>
<<run $demonsToDo.delete($menu1Demon)>>

NOTE: This code is one example of the "good enough" type of coding I was talking about above - if I were to continue work on this game, it would definitely be much more efficient to create a loop for this assignment, as well as a much better set of relationships for how I'm handling these values, for example something like an object to store all the different information about each menu. Since this was a continuation of a very old game when I was a lot newer at coding, I decided it was easier to just be a bit sloppy and finish the project using some of the existing framework instead of getting lost in the weeds optimizing.

Once the menus were built, the first 3 were assigned to the main table in the game. 4 more were tucked away to be found when the player finishes cleaning different parts of the house (for example, once a cupboard is fully clean, the player discovers a menu tucked away in the back), which leaves 3 more demons to spawn at random. The following code basically tracks how many actions the player has done and if they are above 20 actions, we spawn a demon:

<<widget "demonspawn>> 
    <<if $demonstodo.length > 0>>     
        <<set $movecount += 1>>     
        <<if $movecount > 20>>         
            <<set $movecount= 0>>         
            <<set $currentdemon= $demonsToDo.pluck()>>            
            <<dialog>>             
                <<print setup.demons[$currentdemon + $currentroom "1"]>> 
                <br><br>
                <<print setup.demons[$currentdemon + $currentroom "2"]>>             
                <<close>>       
                <<onclose>> 
                    <<goto $currentdemon>>    
            <</dialog>>    
        </if>> 
    </if>> 
<</widget>>

I then used this widget in any room/passage for activity where I wanted a demon to potentially spawn. For example, since there's a menu hidden in the cupboard, I didn't use demonspawn in those passages and instead just manually added to movecount. If I were to optimize this, I'd probably split the movecount and the spawning into 2 different widgets or make javascript code to apply to click events/passage navigation and just exclude the places I didn't want it to run.

Some feedback I got from the early version of the game is that people didn't realize there were variants of text for descriptions of items, as I had just been pulling text using .random, so I changed many of these messages to cycle, using the method of creating an array I outlined above. Halfway through changing all this over, I realized I could be a bit lazy and use this process to also cycle through the cleaning process. The code below will check for the size of the array and if it's empty, it will set the bed to cleaned and execute cleaned logic (giving willpower, checking for an achievement for cleaning everything, etc). If there are still values left in the array, it will shift the array to remove the first element and display that.

setup.bed = {
...
clean: ["You begin by stripping the pillows and sheets - judging by the rather...err...ripe smells, it's far past time they were washed. You've just been so exhausted and haven't had the time, but now that you're doing it, you find yourself looking forward to having a chat with neighbors when you bring the laundry down tomorrow.", "You rummage in your tiny closet for spare sheets and pillowcases, dislodging an old box of photos. You spend some time glancing over better days and set aside a few photos from travels with old friends.", "You wrangle with the fitted sheet, starfishing on the mattress until you triumphantly manage to tuck in all four corners.", "You give your pillows a hopeful fluff and toss them atop the made bed. It's not the most luxurious sleeping arrangement, but it definitely looks a lot more inviting and restful than when you started."],
...
}
<<set $msgBedClean = Array.from(setup.bed.clean)>>
<<if $msgBedClean.length == 0>>
<<set $bedClean = 1>>
<<cleanDone>>
    <<dialog>>
<<include bedMenu>>
    <</dialog>>
<<set $msgBed = Array.from(setup.bed.cleanDone)>>
<<else>>
<<clean>>
<<print $msgBedClean.shift()>>

UI Updates

My goal with the UI update was to lean into the hand-drawn art's sketchy style and create the impression of the images and text being words in someone's journal (especially since a journal is an interactable object in the game, where you can even add custom entries!). I browsed the internet and found some useful codepen examples for the stacking pages and tape corners and tweaked those until I was happy. 

Original UI:


Updated UI:

This is another "good enough" moment. I could keep improving the UI, but then I'll end up down the CSS rabbithole for ages, so I basically had to stop myself and say "it looks fine." I might go back and add a color-blind mode as I definitely think that might be a problem :/

Art Updates

The artist for this game had previously sent me some extra art they had done which we didn't have time to add to the project during the jam, due to running out of time. I really wanted to be able to showcase these pieces, so I added in more ways for players to find demons as noted above. 

One issue I ran into is that our format for the menus used a header art image, so creating new menus without those would stand out a bit. 

Example menu page:

I first went through the existing art to determine if I could double-dip on any of the image. For example, the image of an outdoor grill for a bbq restaurant also worked great for a burger joint and by cutting out the distinctive pillars of the Taj Mahal (for our I ndian restaurant) I was able to have a mosque that kinda looked like the Hagia Sofia (for a Turkish restaurant). I began to run out of choices, however, until I realized the French image could make an easy shift to a logo for a cajun restaurant!

All I had to do was crop the fleur de lis, copy it and rotate the copies to flank the main one and ta-da! A quick little logo conjuring up New Orleans:


I also wanted to add some visual progress to the images of the house so players would see the image changing as they cleaned (eg the bed would become made). The artist had originally given me one overall finished image for the main room, but I needed to create steps for each element as well as create updates for the kitchen. For the main room, I copied each side of the room from the finished artwork. I then pasted each on top of the messy room and used smuge, blur and a very diffused paint tool to help make the lighting match. I also created some photographs to paste on the wall around to bed to reflect text about the player hanging them up. I used the blur tool on these to soften them and make them match the sketchy art style of the existing art. I also added a few dots to represent stars in the now-open window.

Original messy room:


Bed made, couch still torn (there's an equivalent for couch repaired and bed still messy):


Final cleaned room:


For the kitchen, it was a lot easier. I just carefully erased away the dishes in the sink and drew in an arc to represent the bottom of the basin, and erased smuges on the stove. I added the same photos that were hung around the bed along with some basic shapes to represent magnets, and tada, fridge was transformed.

Some similar tweaking was done for the final page before the ending, where the player faces the final demon: their own reflection in the mirror. I used the existing image from the TV achievement (which is...a TV screen), filled in the outline around the screen, erased the antennas, and added some parallel diagonal lines to represent light reflecting off the mirror. It's not amazing, but it's functional enough to do the job!


Audio Updates

I added a few more songs to the playlist, retaining the theme of classical piano. Finding these gave me a nice mental break between working on other parts of the project.

I also found several different audio snippets of pages turning, to have the sound match the new "journal" style UI. The code below defines the names of the audio events and randomly shuffles plays one whenever parts of the game are clicked.

setup.audio = {
  pageturn: ["pageturn1", "pageturn2", "pageturn3", "pageturn4"]
}
$(document).on('click', 'button, a, .clickable', function () {
    Wikifier.wikifyEval('<<sf>>');
});
<<widget "sf">>
<<set _click = setup.audio.pageturn.random()>>
<<audio _click volume .3 play>>
<</widget>>

Writing Updates

A large chunk of time was spent on this. Our original game was very black-and-white (teehee) in how we portrayed the player's relationship with their mother. She was basically this flat, one-dimensionally evil character - but that's not how real relationships or people are, so I spent a lot of time fleshing out nuances of the relationship through memories. 

I added a dad and obliquely hinted that he had passed away, which changed the dynamic between mother and player and led to the shift in the mother's behavior. I enhanced this by using seasonal references to indicate what part of the memory timeline the player is recalling, cycling from summer to winter and back into summer as the player left home to try to find their own happiness at culinary school.

  dolmaMemory: ["You remember how one summer all of you took a family cooking course. Dad had roared with laughter as he watched the mess you made trying to roll dolma together with your chubby fingers, before scooping you into his lap to help guide your efforts. Mom had kissed him on the head and tenderly squeezed your shoulder...You suddenly find the thought of the dish unappealing as you imagine some other kid learning to make it, some other child having what you lost."],
 breadMemory: ["You are struck by a bittersweet memory of making bread with your mother. That day was one of the few great ones you can remember with her. It was autumn, the air chill and crisp, before dad's test results kept getting worse. You slathered the crusty slices with butter and dunked them into a hearty chicken soup, a cozy meal against the gathering storm. Was it your fault that everything changed?"],
  beignetMemory: ["Snowflakes mounded soft as sugar outside the hospital that day near the end and the sky was a blueberry bruise. You reflect on how the ugly can nestle among the most beautiful moments - the discordance makes your head spin and you catch yourself nervously glancing towards the window, as if reassuring yourself the day outside is appropriately gloomy."],
lemonTartMemory: ["You’ve always loved lemon desserts. There’s something about the light citrus that is always refreshing. You remember one sun-drenched spring day, not long after dad was gone: your group of friends rode bikes to the store, pooled pocket money, bought a box of cookies and gorged. Powdered sugar smiles beneath cotton-candy clouds - worth the stomach ache that night to forget the feelings for the afternoon."],

  eggrollsMemory: ["You can't help but crack a bittersweet smile, remembering one group outing after culinary class let out for the summer break when a crowd of you went out for dim sum and bonded over boldly trying everything on the menu. Cart after cart rolled by, depositing steaming baskets of dumplings, fried morsels, delicate desserts, and your stomach swelled, aching from overeating...but moreso from laughter. It was a good day."],

The overall goal was to create a deeper, more emotionally rich story with room for sympathy for the maternal figure while enhancing the pathos for the player's character.

Overall

All in all, I'm happy with my updates!

I think one important takeaway is recognizing scope and limiting it where needed to ensure something complete is produced, instead of endlessly tinkering. This is, at the end of the day, an experimental art project - it's not something I'm going to sell so it doesn't need rigorous polish or expansive gameplay. It's updated enough to look slick and the gameplay and story have been expanded enough to tell a well-rounded, self-contained narrative.

Could this be better? Sure, but what I've produced is definitely a clear sign of my progress in both my health/brain recovery and in my growth as a dev - which seems pretty fitting, given it's a game about moving past trauma through constructive choices. I certainly did some constructing! 

Get Succor

Leave a comment

Log in with itch.io to leave a comment.