Skip to content

Nicole Makes Games

a devblog

Menu
Menu

Category: Fair Weather

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.

↑↑ ↓↓ ←→ ←→ 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!

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.

I’m not dead yet!

Posted on February 7, 2019November 16, 2020 by Nicole

Holy Hiatus Batman!

Ok I haven’t updated the blog in a while, but rest assured, dear reader things have been moving along at, I dunno…let’s call it lilting speed.

Let’s see what’s happened:

new laptop
What’s a Computer?

I got a new computer back in August and it is sooooo much better than my old Sony Viao. I mean that thing was Bad with a capital B.

halloween costume
We are made o-of
l-o-o-o-ove.

I took off some of October to deal with Halloween costume preparations (It’s my favorite holiday, and it’s kind of a big deal) and most of November to participate in Nanowrimo.

I’ve started incorporating audio into the game. I haven’t quite pinned down a musical style. I thought of having jazzy rifts play, after finding a baritone sax sound pack that I liked, but I don’t want to be accused of trying to be a Cowboy Bebop rip off. I’m not sure if this is a valid concern, but I have it none the less.

Unbeknownst to me, IndieCade moved up their submission this year to March 15/April 15 (late submission date) rather than the summer so I’ve advanced to full on panic mode, cutting features and trying to figure out art.

I did a reworking of the Mining minigame just to make it a bit more complex. Perhaps I’ll make a separate post about that.

Speaking of art, I’ve been experimenting with pixel art a bit more because I need to start replacing all the temp art in the game with assets I can actually use.  And while pixel art in games is, if not already, quickly becoming played out, it’s an art style I, as a programmer, can create on my own. My pixel art is pretty basic, but I can improve on it as I go.

pixel gun
Pew Pew

And that’s pretty much it…well, it’s not…but I gotta get back in the game.

Dev Update #1

Posted on July 27, 2018November 16, 2020 by Nicole

I upgraded Unity recently. I guess time got away from me because I was seeeeveral versions behind.

The reason for my upgrade was stupid. I’ll admit that. I ran into a problem. A stupid problem. I accidentally hit the close program button instead of the stop running in editor button (because I was distracted) and it borked my project so horribly that I couldn’t recover. I tried throwing away my changes in source control and restarting. That didn’t work. I googled the errors I was getting and tried all the suggestions there but that didn’t work either. Then I decided to reinstall unity. And while I was at it I was like, yeah, let’s go ahead and upgrade.

(more…)

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