Skip to content

Nicole Makes Games

a devblog

Menu
Menu

Author: Nicole

How I Made It: A Player Decision Manager

Posted on August 9, 2021January 12, 2022 by Nicole

As I prepare my next update for Fair Weather, I thought it might be nice to dive into one of the features of the game and explain how I brought it together technically. The feature I’ll discuss today is my Player Decision Manager.

What is it?

Well simply put, it’s a multiple choice question presented to the player which will affect the player’s stats. It allows me to represent events aboard the player’s ship without having to build them out mechanically, (because I simply don’t have the resources or time to do so) but also it allows me an opportunity to introduce small snippets of world building. I also use these decisions to inject humor and set the mood of the game. While I aim to build many (hundreds, ideally) of decisions in, I also expect that the player will see these questions multiple times and begin to use them strategically to keep their ship afloat.

The final product

For my next update I decided to add 50 new decisions (a number I selected quite arbitrarily) into the game. It has always been my hope that my game would excel in writing and while I built systems for notes, and quests, and decisions, I haven’t taken a lot of time to sit down and make content, favoring work on features instead. So that’s what I’m doing now. I am inspired by games like Reigns which seem so simple on the surface and still manage to be totally engaging.

Wrangling Data

While on its face this is a relatively straightforward task, a lot of time is lost in the wrangling of data. So what I’m really sharing today is a couple of tools that I’ve made to make the process faster and less error prone. My game expects a json file with decisions laid out. Here’s a sample.

The decisions json file which is interpreted by the game

Initially I used Google Sheets to hold my thoughts for questions and responses and then manually copied the text into the json format that I needed by hand. Of course this lead to a ton of errors due to mistyping. (Shout out to JSONLint. You tha real MVP!)

The Starting Point

So this was my first problem to solve: How to convert the data in my spreadsheet into the format that I needed. I looked into writing a script directly in Google’s scripting ecosystem, but the process of dealing with Google services and scary security messages just to run my own script turned out to be pretty convoluted and not one I wanted to spend more time on. I decided to just export my sheet as a csv and use an online csv to json converter to get my decisions.json. This worked….partially. As I continued to expand the complexity of my decision model I found that the online tool could work for some of the columns, but didn’t map some of the relationships correctly. So I could get part way there and have to manually insert the rest of the data. (Did I mention, I heart JSONLint?)

My next optimization involved writing a python script to read in the csv and spit out a json. It’s been quite a while since I wrote python but after a healthy amount of googling I managed to put together a script that does the job. This is the system currently in use for generating data files for decisions and quests. I feasibly could use this for the ftue (first time user experience) data as well but since that file changes a lot less, I haven’t spent the time to set up the script. There are improvements that I’ll need to address in the near future. The largest being an encoding problem which causes the script to stumble when it encounters smart quotes. Smart quotes are the curly style quotes that some programs like word convert straight quotes to automatically. The default text encoding (ascii) doesn’t know what to do with these characters. Text typed directly into the spreadsheet doesn’t have this problem, but if I copy and paste text from other programs, as I often do, I could inadvertently introduce them. The solution is to change the encoding to UTF-8, but doing so has proved difficult. Using a different library such as pandas to read in my csv could potentially solve this problem, but since it’s a rare issue I’ve delegated it to the backlog for now.

String Replacement

If you’ve scrutinized the screenshots above you’ll notice I developed a simple markup syntax for string replacement. It generally takes the form:

<KEYWORD>

I process all text before it’s displayed through a function that will replace the various keywords with the desired text. This helps with strings that are variable, like your captain’s name which changes each time you die, and also for items which I may change during development of the game, such as your ship name and the name of the company you work for. This is my way of reserving the right to change these items easily before I take the game of this beta-mode that it’s in currently. [Fun fact: The ship name Fair Weather used to be editable much like your crew and captain’s names, but I changed it to be static instead because I think it tells a better story that the Fair Weather is your ship, instead of me telling the story of some arbitrary ship.] Additionally, some of my keywords will automatically generate sector or ship names to vary the text slightly each time you see it.

So I can write statements like:

Singing is against <COMPANY_NAME> policy.

Vacation Request for <CREW_NAME>.

and

Captain, the <RAND_SHIP_NAME> wants to know if we’ll participate in their crew exchange program.

Reward Types

To add consequences, or “rewards” as I call them, for each option I added a few base reward types to specify to the code how handle dishing out rewards. Each option under the decision can have zero to N rewards listed along with associated counts for each reward. These rewards are things like reputation, fuel, and food, but also can be negative stat hits like ship damage and injuries.

A type of ITEM means that the player will get all of the items listed when the choice is selected.

The type RANDOM_SELECT means to choose between the rewards listed. There is an equal chance of each item being selected. In the future I hope to expand this so that the choice can be weighted on an individual basis. This has been a tricky type to navigate, and because of that I use it sparingly. I expect players will encounter a decision more than once as they play, but not knowing that an outcome was random, they could make the incorrect conclusion that their selected outcome is the only option and avoid selecting it if they didn’t get the “good” outcome the first time. To combat this I try to use language like “might” or “could” to signal that there are multiple results.

The final reward type ACTION signals to the code that an in-game event should trigger on selection like a sector jump or an attack. No doubt I’ll add new reward types as I extend the game functionality, but for now these types provide a decent amount of variety.

To test I read the file in and create triggers for each individual decision in my debug menu

Selecting Decisions and their Targets

Decisions are triggered as one of the outcomes when the player hits the scan button. The PlayerDecisionManager then attempts to retrieve a decision from the full set of options. Some decisions have requirements, such as the average ship morale being above or below a certain level, which could cause a decision to be rejected. In cases such as these, to attempt up to four times to retrieve another decision. In the case that one isn’t found (which is pretty rare unless only a small subset of the decisions are loaded) it closes down and proceeds without showing the player anything.

Decisions also might have targets. Targets are one or more Non Player Characters (NPCs). So an outcome in a decision might affect a single crew member, an entire “department”, such as all Biologists, or the entire crew. In the case where everyone is effected, instead of specifying a target the reward type is used to specify this distinction. Instead of rewarding mood I would reward mood_all, for example.

Let’s take a look at some code snippets from the PlayerDecisionManager:

private const string TARGET_SINGLE_CREW = "SINGLE_CREW";
private const string TARGET_RAND_CREW = "RAND_CREW_";
private const string TARGET_DEPARTMENT = "DEPT_";
private const string TARGET_SINGLE_DEPARTMENT = "SINGLE_DEPT_";

public DecisionData? GetDecision()
{
    int reselectAttempts = 0;
    if(String.IsNullOrEmpty(selectedDecision.id))
    { 
        bool success = false;
        while(!success && reselectAttempts < 4)
        {
            selectedDecision = GetDecisionData();
            success = !HasSeenRecently(selectedDecision.id) &&
                      SelectTargets(selectedDecision.target) &&
                      MeetsRequirements(selectedDecision.require);
            reselectAttempts++;
        }

        if(success) 
        {
            MarkDecisionSeen(selectedDecision.id);
            return selectedDecision;
        } 
        else 
        {
            Invoke("CloseDecision", 0.2f);
            return null;
        }
    }
            
    return selectedDecision;
}

public bool SelectTargets(string decisionTarget)
{
    if(!String.IsNullOrEmpty(decisionTarget))
    {
        selectedTargets = new List<NPCModel>();
        if(decisionTarget == TARGET_SINGLE_CREW)
        {
            selectedTargets.Add(Main.Instance.crewCtrl.GetRandomCrew()[0]);
        }
        else if(decisionTarget.Contains(TARGET_RAND_CREW))
        {
            int count = 0;
            Int32.TryParse(decisionTarget.Substring(TARGET_RAND_CREW.Length), out count);

            if(count < 0)
            {
                NLog.LogError("INCORRECT COUNT FOR SELECTED TARGET RAND");
                count = 1;    //fallback in case of error
            }
            count = Math.Min(count, Main.Instance.crewCtrl.CrewCount);
            selectedTargets = Main.Instance.crewCtrl.GetRandomCrew(count);
        }
        else if(decisionTarget.StartsWith(TARGET_DEPARTMENT))
        {
            string departmentType = 
                decisionTarget.Substring(TARGET_DEPARTMENT.Length);
            selectedTargets = Main.Instance.crewCtrl.GetCrewOfType(departmentType);
            if(selectedTargets.Count == 0)
            {
                return false;
            }
        }
        else if(decisionTarget.StartsWith(TARGET_SINGLE_DEPARTMENT))
        { 
            string departmentType =
                decisionTarget.Substring(TARGET_SINGLE_DEPARTMENT.Length);
            selectedTargets = Main.Instance.crewCtrl.GetCrewOfType(departmentType, 1);
            if(selectedTargets.Count == 0)
            {
                return false;
            }
        }
        
        selectedDecision.npcs = selectedTargets;
    }
    
    return true;
}

HasSeenRecently simply checks to see if the selected decision id is present a list recounting the last five decisions seen. While the PlayerDecisionManager selects a decision and processes the outcome, a separate popup view displays the decision and its options to the player. The selectedDecision object also stores selected NPC targets so that outcomes can be applied to them, and also so that string replacements can be done as needed with their names.

This system allows for a variable number of crew to be specified as targets for a decision in the spreadsheet. Take the target type TARGET_RAND_CREW in the code above. In the spreadsheet, I can enter a target like RAND_CREW_2 or RAND_CREW_3. That number is then is parsed out and used to select crew members that meet the desired type via helper functions in my crew controller.

Wrapping Up

That’s essentially it! When one of the multiple options is selected in the view, it calls back to the PlayerDecisionManager to dish out the rewards and complete the process. Like all things code, this will undoubtedly change in the future and if it’s interesting enough I’ll let you know about it. I hope you check out Fair Weather if you haven’t already. That’s all for now.

Hand-picked Mobile Games

Posted on March 15, 2021March 15, 2021 by Nicole

A bit of exciting news: Fair Weather has been included on a list of hand-picked mobile casual games over at GamesKeys.net!

It’s lovely to know someone else sees the potential in my little game baby especially after working on it for so long all by myself. Recognition like this rekindles my fire to keep adding new features and story lines (not to mention balance and bug fixes) to the game! So thanks to the folks at GamesKeys.

Check it out here!

And to any new players: I welcome your kind feedback here.

Chef Shelf

Posted on February 27, 2021 by Nicole

I’m making something and it’s not a game. But it has me excited so I thought I’d share it with you. For work, the frontend team and myself are trying to delve deeper into backend programming land so that we can remove some of the bottlenecks on our very small team. To that end, we’ve been having workshops with our resident guru (shout out to Johanns!) and we’ve been building our own Rails projects to learn on. I used this opportunity to tackle a problem I’ve been thinking about a lot, which is recipe management. 

Some background information first: I love to cook. Cooking is a special kind of magic. The recipes that I use are all over the place. Probably 50% of the YouTube content I watch is cooking videos. I have a ton of cookbooks. Despite not loving the user experience, I use Pinterest which is great for its vision board approach. (I’m definitely a visual learner. And I can’t stand cookbooks without pictures. How do you know if you want to cook it without a picture?) I also have a ton of cooking magazines that I’d really like to scrape for quality recipes and then reclaim my shelf space. And that doesn’t count a barrage of loose handwritten or printed paper recipes and random links that currently I just store in Pocket. I want one source of information. Because everytime I make something bomb and think about making it again I have to remember where the hell it came from. 

So my app is a bit of a recipe catalog, with the ability to group recipes into books for categorization. It’s tentatively called Chef Shelf. (And no, I don’t have the honor of being a trained chef, but who can resist the consonance.) So I’ve been wrangling database tables and trying to get more comfortable with Ruby mainly because I want to be a (more) badass developer, but also because I’ve got this problem that needs solving. My end goal is to put this bad boy on a spare raspberry pi I got for Christmas a couple years back and serve it throughout my apartment, but we’ll see how it goes. Maybe I’ll make it go farther. I haven’t built it for multiple users because I don’t want to put the cart before the horse, and right now I’m having fun! I mean, don’t get me wrong, I’m spending entire evenings scratching my head and shaking my fist at Stack Overflow (note to self: write post about the toxicity of Stack Overflow) just to solve some tiny problem but that’s how you learn, right? I’ll share some photos down the line.

Oh, and fear not, I’m still working on Fair Weather and contemplating my next game project (more on that later). So as usual I’m keeping several plates spinning over here. I’d better get back to it!

↑↑ ↓↓ ←→ ←→ B A

Posted on June 19, 2020August 9, 2021 by Nicole

With Fair Weather in the App Store I thought it might be nice to share a few cheats to help along any players who find themselves stuck.

I built a hidden console into the game inspired by the Sims. You can access it by repeatedly tapping on the app version in the settings menu. (Yeah, Android, I took your thing. What you gonna do about it?) Once there you can enter commands to give you a boost in the game!

Running low on fuel, but you gotta go fast? Try fillherup

Got the munchies? Try nomnomnom

Can’t wait for Trader Ruth to show up? Try ruth

Did you kill your crew and now you’re all alone, huddled around a trash fire with nothing but your thoughts to echo off the cold empty hull? Try friend

These are just a few and surely I’ll be adding more in the future, so stay tuned. 😉

The Road to IndieCade

Posted on June 9, 2020November 16, 2020 by Nicole
Trello Board

Look at that Trello board. It’s beautiful. Clean. Sure, it’s not empty. What you don’t see pictured above is about six more lists filled with ideas, bugs, features and enhancements, but don’t kill my vibe. This post is a celebration and we’re living in times very much in need of some good news. I finished all the tasks I planned for IndieCade and submitted an alpha build to the App Store. Go get it! Get it now!

It’s been a long time since I wrote an update of my progress. I’ve been very busy on the game. I did an overhaul of the interior artwork. I added a bunch of quests. I added some animation. I fixed a lot of bugs–and I mean a LOT of bugs. At the end there, I was finding bugs that had me questioning things like: How LONG has this thing been broken? Has it always been broken? How did I not notice? Am I a ghost? I also started testing with a group a friends, but it’s clear that I need more internal and external testing. (And if you’re interested in helping me test, click here.)

IndieCade is a yearly festival celebrating independent game development. How it works is: you submit your game, it gets judged by a panel and if selected, it gets showcased for the festival. No matter how IndieCade turns out this is a big win for me personally. It’s a milestone that I’ve worked very hard to achieve. I’ve spent practically every weekend and countless nights for several years working on this game. It feels scary showing the game to people. I keep saying “oh it’s not finished”, “I’m still working on getting the perfect balance”. It’s hard not to be intimidated by these other amazing solo dev projects that you see popping up all over the place. I don’t have any answers yet on how to get over that. I’m trying to tackle my insecurities. All I can do is try to get a little better every day.

I got iOS working for IndieCade. I wanted to release Android as well. I got a closed alpha testing track working in the Play Store several months back, but I had to give up on it in the last week or so leading up to the IndieCade deadline. I simply didn’t have enough time to wait on their approval process which was experiencing delays due to COVID-19 whereas the App Store turn around was super fast. But on the positive side, I can do updates for Android while my iOS build is frozen while IndieCade judges look at it.

For more info on Fair Weather, hit up this link right here.

Oh and there’s a Facebook Page now too!

WTF America?

Posted on June 8, 2020November 16, 2020 by Nicole

A lot has happened in the last couple weeks. It’s hard to function. It’s hard to think about anything else. It’s hard to read the news without crying. George Floyd was murdered and I don’t have enough words for how terrible it is–for how terrible it is EVERY SINGLE TIME this shit happens. Seeing the large amount of people unabashedly okay with the cops’ constant unjust persecution of people of color in our country is sickening. It’s all too much–the division, the excuses, the lack of leadership, those who would distract us from the real issues plaguing this country.

Oh and coronavirus is killing us too.

I released my app in the App Store. Which is fucking huge. I’ve been working on Fair Weather for years, but it just doesn’t seem important right now. I’ll write about it later. For now…I don’t know. Breathe in. Breathe out. Stay strong. Fight the good fight. Black Lives Matter.

I love deadlines.

Posted on October 1, 2019November 16, 2020 by Nicole

“I love deadlines. I love the whooshing noise they make as they go by.”
― Douglas Adams

IGF deadline was yesterday! Did I make it in time? No. Obviously. Come on people, context clues!

So this is the second deadline I missed this year. (The first one was for Indiecade, whose submission window had been moved up.) I don’t feel that bad about it though because I got really far in the submission process. My app is up in the google play store awaiting approval. A few days before the deadline I realized that the chance I’d finish in time was very very low, so I chilled out a bit. I don’t want to stress out for no reason. Additionally I’m going on a trip this week and I’ve done next to zero preparation for it, so I knew I needed to carve out a large chunk of my weekend to devote to that.

Where I went wrong:

  • I didn’t code freeze early enough. I was still making tweaks and bug fixes up until about a week ago.
  • I underestimated how long it would take me to figure out the Play Store’s upload process and decipher error messages.
  • I didn’t think I needed a privacy policy because I don’t track anything. This was wrong. So I had to figure all that out and add one at the last minute.
  • I didn’t think I’d need app approval for an internal release (but I suppose it makes sense).
  • I didn’t plan ahead far enough for my vacation which mean running out at the last minute trying to buy the right clothes and items which if I had done earlier wouldn’t have impacted my pre-release weekend.
  • The IGF deadline wasn’t on my radar until submissions were already open. It was pretty low stakes target. I basically used it to push myself because someone else’s deadline seems so much realer than your own.

Where I went right:

  • I didn’t kill myself trying to do it. This is my hobby and something I’m doing for fun. I’m usually working on it after a full day of work so I’m already 70% exhausted when I come to the table. I kept thinking to myself, am I trying hard enough? I could stay up til the wee hours of the morning and bang this thing out. I’ve done it plenty back in school, and even at work to hit deadlines. But over the years I’ve learned that it takes so much longer to complete any task, even a simple one, when you’re super tired, and the work isn’t your best, AND you’re gonna be super cranky tomorrow at work. So will I ever push myself that hard for Fair Weather? Sure, probably. But it’s not necessary right now. So I’m going to choose the healthy and sane route as long as I can.

I made a thing!

Posted on September 18, 2019November 16, 2020 by Nicole

I made a trailer for Fair Weather!

I used Divinci Resolve for the editing and plain old Quicktime for screen recording.

I LOVE the song I found from the site Nihilore.com. It’s called As Nihilism Gives Way To Existentialism.

I’ll probably do another cut in the future with more details on where and when the game will be available and also hiding some things (like the debug menu up there in the top left corner…oops!), but I’m liking how it turned out. Hooray!

So close and yet so far

Posted on September 18, 2019November 16, 2020 by Nicole

I’m getting the app ready for the Play Store! I heard the Independent Games Festival, or IGF, submissions were open and thought, why not go for it? I missed the IndieCade deadline earlier this year, and while I wasn’t planning on submitting to IGF, I don’t see why I shouldn’t try.

So, what is IGF? IGF is a yearly competition of Indie games for sweet sweet glory (and money if you win big). They also have a huge Indie Megabooth set up at the Game Developer’s Conference which is one of the best booths each year. Submitting the game for others to check out is a big milestone for me.

The game has come really far in the past few months. I added some polish and tuned the tutorial based on the limited play test feedback I’ve conducted.

I do have tons of concerns still:

All the art is my own shitty pixel art (with the exception of a few assets such as the mining minigame’s monsters which I purchased from an itch.io artist). I know my game will be compared to games that are extremely pretty. I’ve tried to keep the aesthetic “minimalist-ish pixel” but there’s tons of room for improvement.

I am concerned that I haven’t tested it with a wide enough audience. One of my playtesters throughly broke the tutorial within the first two minutes. I think I’ve patched up those holes but I know there’s more. There are always more.

I’m scared by all the horror stories I’ve heard of people’s apps being plagiarized off the Google Play Store. This might be a premature concern, but the idea that someone could take what I’ve spent years working on and pimp it out makes me sick.

There’s a million things I want to add that I’ve had to delegate to future updates. Oh and there’s also..Am I a huge idiot for thinking anyone would want to play this? Could I have spent the last few years becoming fluent in Spanish or working through my bookshelf’s backlog instead? Have I done everything wrong? I’ve done everything wrong! I waited too long to release it! They’re all gonna laugh at me! You know, all the usual self doubt suspects.

So that’s where I’m at. But I’m still going. I gotta put those fears behind me and soldier onward. To Valhalla!

Dev Update #2

Posted on June 20, 2019November 16, 2020 by Nicole

#GameGoals

A lot of time has passed since my last update. The IndieCade submission deadline has come and gone. I decided about a month before that happened that I wasn’t in a great position to show the game and while I still used that date as a personal milestone, I just didn’t want to kill myself getting a demo done that wasn’t going to stand up to the competition and that I wouldn’t even get any feedback on (as indicated by the IndieCade documentation). I still had a lot of temporary art that needed to be replaced, barely any sound integrated and I haven’t had a chance to throughly playtest. So my current goal is to get playtest ready. I’ve done a few quick tests with friends from work, but I’ve got a ways to go.

So where does that leave me?

Feature wise the game is in a good position. I’m tweaking things now for playability, but trying not to give in to the feature creep demons. There are currently two minigames in the game which correspond to the mining signals and the ambush signals. I have plans for a third minigame for the rescue signal but I’m thinking it would be integrated as a later update so that I can stay on track. I may start slowly building it in my free time (my what?) in isolation of the rest of the game, but my main goal is polishing and playtesting what I already have now and getting it in the Google Play store.

Process Updates

I’ve started using Trello for task management! Wunderlist was working alright, but it wasn’t good for tracking tasks that were in progress. Tasks were either done or not done, and since I sometimes dabble in one task then dabble in another, that wasn’t working out for me. I still use lists for things like keeping track of what actions I need sounds for, or high level goals but I’m really liking Trello.

I promise I’ll update more often. But, you know, don’t hold me to that.

Posts navigation

  • 1
  • 2
  • Next

About Me

Oh hello there! I’m Nicole and I’m a software engineer. This blog documents the development of the game projects that I work on in my spare time. My current project is a game called Fair Weather.

Latest Posts

  • How I Made It: A Player Decision Manager
  • Hand-picked Mobile Games
  • Chef Shelf

Archives

  • August 2021
  • March 2021
  • February 2021
  • June 2020
  • October 2019
  • September 2019
  • June 2019
  • February 2019
  • July 2018
  • June 2018
  • May 2018

More From Me

  • Get Fair Weather on iOS
  • More about Fair Weather
  • Email me!
  • More Me
  • LinkedIn

“If you can’t make a mistake, you can’t make anything.”

– Marva Collins