Is It Wrong to Ask My Son to Play His Own Game?

We got my son The Legend of Zelda: Link’s Awakening for the Nintendo Switch for his birthday. He has never played a Zelda game before, and I had fond memories of playing it on my Game Boy when I was roughly his age.

In fact, I pulled out my Game Boy and played the original Link’s Awakening while he played on the Switch, partly so I could revisit Koholint Island while he saw it with fresh eyes (and in 3D rendered color).

Links Awakening - Switch and Game Boy

I also wanted to try to get ahead of where he was so that when he had questions about how to proceed, I could be a bit more knowledgeable about what he was dealing with.

But I also believe that a big part of the entire point of playing a Zelda game is to solve the puzzles and overcome the challenges.

Now, my son fancies himself a gamer, but he does not have the same internalized language of games that I grew up on. Specifically, Link’s Awakening very much builds upon the language of the SNES game The Legend of Zelda: A Link to the Past. For example, I know that there were some boss battles that mimicked boss battles from the SNES game in that you had to deflect projectiles back at the enemy, but nothing in Link’s Awakening tells you this fact. You just have to try things and figure it out. In fact, that’s probably how the SNES game was, too, and I’m just aware of the clues and norms in hindsight.

So my son struggled on parts of the game because he did not see the opportunities to do Zelda-like things because he didn’t know what Zelda-like things were possible in the first place.

Which is fine. He’s solving puzzles and overcoming challenges.

Except when he wasn’t.

He would ask for help, and I’d try to give him clues or hints without giving away the solution. And most of the time, it worked, and most of the time, I’d hear a report from him that he actually figured out quite a bit of the game on his own.

But multiple times he asked if he could look up the solution to a puzzle on the Internet. And I told him no.

My reasoning: “What’s the point of letting someone else play the game for you?”

I grew up as one of those kids who had Nintendo Power subscription. I knew details about games I didn’t own. I got the free strategy guides they sent out.

But I never liked the idea of playing a game by following a walkthrough or using a strategy guide to tell me how to play a game. Yeah, I could advance faster and have an easier time, but to me, it would feel like cheating.

So I insisted to my son that he play the game and figure it out. I figure he would feel a sense of accomplishment if he did it all by himself that he would never get from doing what he was told by some Internet stranger.

But am I wrong to do so?

Is it wrong for me to insist he plays the game the way I want him to?

I never want to be a gatekeeper and say that Real Gamers ™ play a certain way and everyone else is just pretending. People play games for all sorts of reasons, and who am I to tell them how to enjoy them?

Some people play games for the entertaining story, and I have no problems with them using a walkthrough, strategy guide, or even cheat codes to do so.

In fact, I’ll admit that I got stuck in Link’s Awakening in my replay, and I looked up how to get through a certain dungeon because I’m a grown-up with limited play time and didn’t want to spend the time doing trial and error to figure out which way I was supposed to go. Don’t tell my son I’m a hypocrite.

My intent was for my son to use the game as practice for perseverance. He struggles with challenges, whether in games or not, and often gives up and even says he no longer wants something if he discovers it takes some non-trivial effort to acquire it.

Games are all about learning, and I felt that looking up the answers not only gave him a shortcut in the game but would also make him less self-sufficient when it comes to problems in real life that you can’t take shortcuts to get past.

But we also gave him a game, and then I insisted that he play it differently from how he wanted to. No one did that to me when I was younger.

I suppose I could have told him my reasoning, that I wanted him to want to figure things out on his own for the benefits and practice it gives him, and then let him decide if he wanted those benefits. But based upon previous opportunities to let him decide whether to take the easy route or the more rewarding yet challenging route, I figured he would not care and would simply look up the answers.

So I struggle still. Was I parenting well by preventing my son from what I would consider cheating himself out of the experience and growth? Or was I parenting poorly, forcing him to play a game the way I wanted him to play, taking away his enjoyment and agency?

Maybe it worked out?

As I was writing this post, I asked him about his time with the game, which he has now played through twice. He told me he really liked it. He remembers the game fondly, and he even mentions figuring out how to “get past it” in a positive light. At the very least, it seems he hasn’t been turned off of games, and in fact is now playing his way through Pokemon Sword.

So I hope his experience with the game has prepared him for the future just a bit more. Maybe he’ll be less likely to give up on a math problem that troubles him. Perhaps he’ll stick with a craft project of his own making for longer. It’s possible he’ll be more capable the next time we play Mighty No. 9, a game which provoked so much frustration from him that he quit playing before finishing the first level.

He’s already asked about other Zelda games in my collection. I’m trying to decide if I should introduce the original NES game first, or if I should show him Wind Waker.

I never did finish either game myself. It might be a good challenge for both of us.

Toytles: Leaf Raking Progress Report – Streamlining and Planning

In the late report from last week, I went into detail about some changes I made to improve the quality of the codebase, then ended by saying that I wasn’t going to do it again unless it was related to a feature in development. That is, no cleaning up for the sake of cleaning up, as it is premature and potentially wasteful when there is more pressing work to do.

In fact, both stories meant to be completed in sprint 5 had to move to sprint 6.

Sprint 6: More personality injections; streamline waiting for rain

Last week’s sprint plan was:

  • Give each neighbor unique text as unhappy clients
  • Streamline “Wait for Rain” option

I did 7 hours of game development, which is amazing considering I was on vacation for part of this week and did more hours of work than most of the weeks since I started working on these updates.

I finished streamlining the “Wait for Rain” option. It took only 30 minutes, and yet I think it makes a big difference.

Before, if you tried to rake leaves while it was raining, you would get a message informing you that the rain was preventing you from working and asking if you wanted to wait it out.

Your options were to wait 10 minutes or to wait an hour.

Light rain is like a drizzle, and every 10 minutes, which is the smallest unit of time in the game, there is a chance that the rain will start or stop. So it makes sense to want to wait 10 minutes in this case.

But heavier rain will rain for the full hour, so you’ll want to wait that long.

If you were at the top of the hour, this is fine. But the problem is that you could try to rake at any time within an hour, so waiting an hour means that you lose out on anywhere from 10 to 50 minutes depending on when you were trying to start.

The original solution was tedious and frankly non-obvious as an optimal move: just wait 10 minutes over and over until you get to the top of the hour.

Now, you get presented with waiting for 10 minutes and waiting until the end of the hour.

Wait for Rain options

It’s what you would want to do anyway, so now the option is there and easier to execute.

More code changes

Ok, so that was half an hour. What was I doing for the remainder of the time?

Well, I didn’t get the unhappy client text in, but I did a bunch of work to make it possible.

So wait, did I make a bunch of irrelevant code changes again?

No. This time the code changes were paving the way for this feature and not just random opportunities to make my inner technician happy.

Here’s your warning that the rest of this post will get kind of technical!

Currently, when you visit a neighbor, they have one unique thing to say to you which is based upon whether they are a prospective client, a current client, or an ex-client.

I knew the current implementation would be limiting. It’s literally a map of neighbor IDs to a map of client statuses to pieces of text.

typedef std::map<int, std::string> NeighborStatusToGreeting;

It’s a bit unwieldy, but it works for now. When it comes to adding new dialog options based on other things, such as the happiness of the client or the time of day or which date it is, well, I knew this solution wouldn’t have a long life in this codebase.

So I spent a majority of the time planning and researching a better solution. I didn’t need it to do everything possible, but I did need to make something more flexible for my immediate need.

At a high level, I need to take some criteria, then filter out any dialog text that wouldn’t match that criteria.

The current implementation is just hardcoded to pay attention to the visited neighbor’s client status, and so I need to abstract it a bit to make it handle other pieces of data.

In this case, I need to know if the client is unhappy, which isn’t actually a piece of state in the Neighbor struct. It’s related to whether or not the the yard is dangerously close to being overfilled with leaves in the case of a night of incredibly heavy leaf fall.

That is, if you are potentially one turn away from losing the client, the client is nervous, and they should say something about it when you visit them rather than pretend nothing is wrong. Which means that I need to know not only whether the neighbor is a client or not but also the amount of leaves in their yard to determine which dialog text to present to the player. Or more generally, I need more data to act as criteria to validate in order to create a filter that gives me the text I want and leaves out the text I don’t want.

I ended up moving a bunch of code into separate files, partly to make it easier to test drive my new code. As I said before, I wish my Past Self had done more of this quality work, and I don’t want to let Future Self down the way I’ve been let down.

Then I created what I called Validators, and so far I have:

    struct Validator
    {
        virtual bool validate(const ValidatorArgs & args) const = 0;
    };

    struct IsProspect : public Validator
    {
        virtual bool validate(const ValidatorArgs & args) const;
    };

    struct IsClient: public Validator
    {
        virtual bool validate(const ValidatorArgs & args) const;
    };

    struct IsExClient: public Validator
    {
        virtual bool validate(const ValidatorArgs & args) const;
    };

It should be clear that each one will return true or false based on whether or not a neighbor is a prospect, a client, or an ex-client.

Initially, each validator took a Neighbor object as a parameter, since that object had a clientStatus which was the only piece of data I needed.

In preparation for the next validator, IsUnhappyClient, I knew I needed more data to provide to it, but it felt wrong to append more arguments to the validate() function, especially if it ends up being irrelevant for most functions.

So I created ValidatorArgs, which will take all the arguments needed, and each validator can look at the piece that is relevant to it. It means that each time I add new data to be used in future validators, I don’t need to change the signature of all of the validate() functions.

For the above code, this implementation will suffice:

 struct ValidatorArgs
    {
        Neighbor * neighbor;
    };

As an example, here’s IsProspect’s validate():

bool IsProspect::validate(const ValidatorArgs & args) const
{
    return PROSPECT_STATUS == args.neighbor->clientStatus;
}

And if you are wondering what one unit test looks like:

class NeighborDialogCriteriaFixture : public Test
{
    public:  
        NeighborDialogCriteriaFixture() {}
        ~NeighborDialogCriteriaFixture() {}
    
    public: 
        NeighborDialogCriteria::ValidatorArgs args; 
};
    
TEST_F(NeighborDialogCriteriaFixture, CanValidateIfNeighborIsAProspect)
{
    Neighbor prospect(MR_CARDINAL, PROSPECT_STATUS, 0, 5, 0);
    args.neighbor = &prospect;
    EXPECT_TRUE(NeighborDialogCriteria::IsProspect().validate(args));
}

So I create a prospect, then pass it into validate() as part of ValidatorArgs, then test that it does in fact identify this neighbor as a prospect.

There’s also a couple of negative tests that check that it won’t validate if the neighbor is a client and if the neighbor is an ex-client.

What’s next? Well, I didn’t get to it yet, but the plan is to add a new member to ValidatorArgs representing yard data, which will be used in the IsUnhappyClient validator.

And ultimately, all of these validators will get used to determine which filters I should use to identify the appropriate dialog text.

So I envision each greeting text being tagged in some way, such as by client status, and those validators being used to create a list of tags to filter on, then one piece of text is selected and presented in the dialog box.

This also sets up the possibility of having more than one piece of text for a given criteria, so when you visit a neighbor, you’ll get a different greeting each day.

Normally I would love to keep things simple until they need to be more complicated. This solution feels like it is getting more complicated than I wanted it to be at this point, but I wasn’t sure how to take a smaller step to accomplish what I am setting out to do.

The downside is that this work wasn’t something I identified needing to do this sprint, and it meant that I didn’t get the grumpy text into the game yet.

Validating Personality

This coming week, I plan to finish this validator/filter work, and I expect it will open the way for me to add lots of greetings for each neighbor.

In future sprints, I’d like to add some graphical enhancements, including dynamic facial expressions. If a neighbor is greeting you grumpily, I want you to see that they are not smiling.

I’ve also been toying with the idea of enhancing the greetings so they are more like actual back-and-forth dialog with the player. That is, perhaps the player gets asked a question, then has different answers as options, and each one takes you to a different branch of dialog. Perhaps entire transactions can occur, such as giving or receiving gifts, or getting tagged in a way that opens up or closes off certain dialog options with other neighbors.

But, let’s not get ahead of ourselves. I still need to get grumpy neighbor text into the game.

Thanks for following along!

Toytles: Leaf Raking Player's Guide

Want to learn when I release updates to Toytles: Leaf Raking or about future games I am creating? Sign up for the GBGames Curiosities newsletter, and get the 24-page, full color PDF of the Toytles: Leaf Raking Player’s Guide for free!

Toytles: Leaf Raking Progress Report – Refactoring, Testing, and Vacationing

Last week’s report indicated that I didn’t get as much done as I would have liked. I managed to add a bunch of new random dialogs for weekends, but I didn’t get to grumpy client dialogs, which I pushed into the next sprint.

Sprint 5: More personality injections; streamline waiting for rain

Last week’s sprint plan was:

  • Give each neighbor unique text as unhappy clients
  • Streamline “Wait for Rain” option

In the last report, I said:

So this week’s sprint I will definitely get to the unhappy client dialog. I have some marketing screenshots to update, partly due to the copyright date, and I want to streamline the way you wait for rain to end so you can get back to raking as it is currently a bit clunky.

I did 2.25 hours of game development, but I did not actually work on any of the above.

So what did I do? Well, last week’s report also said:

I am kicking my Past Self for not spending more time on automated tests so I didn’t have to verify that I didn’t break anything each time I make a change. I’m slowly improving the code as I go along so that it is easier for Future Self, but Past Self is a short-sighted jerk.

This codebase is a few years old, and it has gotten fairly unwieldy. When I work on a 48-hour game development competition such as Ludum Dare, it’s fine for almost all of my game code to be in a single file. It is quick and dirty, and so long as I am mindful and focused, as I would be in a concentrated weekend development effort, I can make a game and then be done with it.

But when I have a commercial project that I am maintaining for years, something I come back to once in awhile, I need to break it up into well-tested pieces so that I don’t need to worry about forgetting how it works.

So I invested in my code quality last week. The rest of this report gets fairly technical, so feel free to skip to the end for my plans for the next sprint.

Code quality and its improvement

To give an idea of what I am dealing with, I have a class called InGameState which handles all of the game play logic and rendering. The class itself only has six functions:

virtual void onEnter();
virtual void update(int timeElapsed);
virtual GBLib::IRenderCommand * draw(); 
virtual void onExit(); 
virtual void onSuspend();
virtual void onUnsuspend();

This class is the workhorse of pretty much all of my games. That is, when you start a new game, the Main Menu state changes to the In Game state. It calls onEnter(), which is where things get initialized, including loading saved games or creating default values for variables.

Then update() and draw() get called over and over until the player exits, which calls onExit(), and the state machine calls onEnter() on whatever other state, either the main menu state again or the exit state in preparation for shutting down the game.

But as I said, I like to throw as much as I can into a single file when I work on a quick game project. If you looked at InGameState.cpp from nine months ago, it was 4,148 lines of code, and my work in the last month has added another 100 lines of code or so.

In a well-written codebase, I’d argue that most class implementations shouldn’t be more than a few hundred lines of code.

Now onSuspend() and onUnsuspend() are meant to help with pausing and unpausing, usually by delegating to a different state, but in this game, they merely set a boolean value to true and false respectively, so they aren’t very interesting. So how are over 4,000 lines of code distributed among four functions?

Well, they aren’t really. There may be four major class member functions, but each one is implemented using inline code as well as non-member functions strewn throughout the file.

I don’t know offhand how many of those non-member functions are in the code base. There’s a lot. But I know my bias is towards lots of small, well-named functions. But this codebase is also a few years old, so some of those functions are not as small as I would like.

So why didn’t I add them to InGameState.h and make them part of the class? Well, as with my protoytpe projects, it is a lot easier to delete all of the .cpp file and copy the header into a new project than it is to remember which functions to keep and which ones are project-specific.

Why didn’t I create a project-specific class and leave InGameState as a generic class to be used across projects? Because I didn’t.

In any case, there is so much code in this class that handles everything from initializing variables, dialog management, time and date changes, weather updates, input processing, screen updates and changes, and more.

An example: generateNeighborhoodScreenBG()

There’s an entire function dedicated to figuring out how to render a particular part of the neighborhood.

This function is over 50 lines long. What may not be obvious is that it takes what is called a NeighborhoodScreen, iterates over the tiles at the x and y positions, and adds the sprite that corresponds with the tile to the spriteWidget.

Ostensibly, that is ALL it does. But when you look at the code, it looks like it is doing a lot more. In fact, most of the code was actually figuring out how to put sidewalks down based on where streets are located.

void generateNeighborhoodScreenBG(SpriteWidget * spriteWidget, NeighborhoodScreen * screen)
{
	for (int x = 0; x < MAX_NEIGHBORHOOD_TILE_WIDTH; ++x)
	{
		for (int y = 0; y < MAX_NEIGHBORHOOD_TILE_HEIGHT; ++y)
		{
			int tileType(screen->tiles.at(x).at(y));
			SpriteWidget * tileWidget = new SpriteWidget(TileTypesToSprites[tileType], Point(x * TILE_WIDTH, y * TILE_HEIGHT, ZORDER::BG));
			if (STREET_TILE == tileType)
			{
				if (y != 0 && screen->tiles.at(x).at(y-1) != STREET_TILE)
				{
					tileWidget->add(new SpriteWidget(SideWalkHorizontalSprite, Point(0, 0, ZORDER::FG)));
				}
				else 
				{
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(0, 0, ZORDER::FG)));
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(TILE_WIDTH - 27, 0, ZORDER::FG)));
				}

				if (y != MAX_NEIGHBORHOOD_TILE_HEIGHT - 1 && screen->tiles.at(x).at(y + 1) != STREET_TILE)
				{
					tileWidget->add(new SpriteWidget(SideWalkHorizontalSprite, Point(0, TILE_HEIGHT - 27, ZORDER::FG)));
				}
				else
				{
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(0, TILE_HEIGHT - 27, ZORDER::FG)));
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(TILE_WIDTH - 27, TILE_HEIGHT - 27, ZORDER::FG)));
				}

				if (x != 0 && screen->tiles.at(x - 1).at(y) != STREET_TILE)
				{
					tileWidget->add(new SpriteWidget(SideWalkVerticalSprite, Point(0, 0, ZORDER::FG)));
				}
				else
				{
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(0, 0, ZORDER::FG)));
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(0, TILE_HEIGHT - 27, ZORDER::FG)));
				}

				if (x != MAX_NEIGHBORHOOD_TILE_WIDTH - 1 && screen->tiles.at(x + 1).at(y) != STREET_TILE)
				{
					tileWidget->add(new SpriteWidget(SideWalkVerticalSprite, Point(TILE_WIDTH - 27, 0, ZORDER::FG)));
				}
				else
				{
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(TILE_WIDTH - 27, 0, ZORDER::FG)));
					tileWidget->add(new SpriteWidget(SideWalkCornerSprite, Point(TILE_WIDTH - 27, TILE_HEIGHT - 27, ZORDER::FG)));
				}
			}
			spriteWidget->add(tileWidget);
		}
	}
}

There isn’t anything wrong with this function. It does what is intended. Well, presumably. Since I have no tests around it, who knows what that intention was originally or if it continues to work when I’m changing code or even sneezing near it. But let’s assume with maybe only a little anxiety that the code works.

What I hate about it is that this code mixes levels of abstraction. I’m iterating through tile types and adding corresponding tile sprites to some parent sprite. But in one special case, I suddenly need to figure out how to add child sprites to the tile sprite. It’s important, but not so important that it takes up the lion’s share of the function! It just makes the entire thing harder to comprehend. So, a quick “refactor” changes the above code to:

void generateNeighborhoodScreenBG(SpriteWidget * spriteWidget, NeighborhoodScreen * screen)
{
	for (int x = 0; x < MAX_NEIGHBORHOOD_TILE_WIDTH; ++x)
	{
		for (int y = 0; y < MAX_NEIGHBORHOOD_TILE_HEIGHT; ++y)
		{
			int tileType(screen->tiles.at(x).at(y));
			SpriteWidget * tileWidget = new SpriteWidget(TileTypesToSprites[tileType], Point(x * TILE_WIDTH, y * TILE_HEIGHT, ZORDER::BG));
			if (STREET_TILE == tileType)
			{
				addSidewalksToTileWidget(tileWidget, screen, x, y);
			}
			spriteWidget->add(tileWidget);
		}
	}
}

I simply extracted that giant collection of if/else chunks into a separate function called addSidewalksToTileWidget().

Sidewalks on streets

Now, you’ll note I said “refactor” above. Why the quotes? Well, as said, there are no tests around most of these non-member functions. I have no idea if I subtly broke something with this change. I think it looks fine, and when I play test the game, it still seems to be rendering sidewalks on streets as expected, but does it do the exact same thing as it did before? I have no way of verifying it. So it’s not really refactoring, because refactoring requires that I know with some confidence that I didn’t change the behavior when I changed the structure of the code.

You might also wonder what the above change bought me. Did I not just add more lines of code to InGameState.cpp?

Yes, yes I did. But as I said, the original function mixed layers of abstraction and was hard to read. It is now easier to read and understand. Future Me will be so pleased that Current Me didn’t get in his way when it comes to future updates.

Another example: getTimeText()

Ok, how about one that actually shrunk InGameState AND resulted in an increase in testing, giving me more confidence that the code works and will continue to work correctly in the future?

getTimeText() takes a TimeOfDay object and produces a string that I can render in the clock at the top left of the screen.

TimeOfDay handles dates, but for the purposes of this function, know that it has two important members: hour and minute. TimeOfDay uses 24 hours, and so based on the hour, getTimeText() needs to show a 12-hour clock and let you know if it is AM or PM.

The minute isn’t an actual count of minutes. Time passes in Toytles: Leaf Raking in 10 minute increments, so TimeOfDay’s minute can have values 0 through 5. So getTimeText() will need to convert minute to one of the 10 minute increments to render.

For example, if getTimeText() takes a TimeOfDay object with an hour of 14 and a minute of 3, it will return the string “2:30 PM”.

The TimeOfDay struct was already in a separate file called Gamedata.h, mixed with other miscellaneous classes. But the following code was in InGameState.cpp:

std::string getTimeText(const TimeOfDay & timeOfDay)
{
	std::stringstream timeText;
	int currentHour = (timeOfDay.hour == 0) ? 12 : timeOfDay.hour;
	std::string periodString = (timeOfDay.hour >= 12 ) ? "PM" : "AM";
	
	if (currentHour > 12)
	{
		timeText << currentHour - 12;
	}
	else
	{
		timeText << currentHour;
	}

	if (timeOfDay.minute == 0)
	{
		timeText << ":00";
	}
	else
	{
		timeText << ":" << timeOfDay.minute * 10;
	}

	timeText << " " << periodString;

	return timeText.str();
}

Since it is so intimately tied to TimeOfDay, I decided to make it a member, and I wanted to be able to test this class, so I put it in its own .h and .cpp files.

There was a bit of hesitation on my part to add this function as a member of TimeOfDay. The struct is just a value, and getTimeText() outputs a particular rendering of that value in a certain way. How it gets rendered should be independent of what it represents, much like how CSS can let you style the same HTML differently based on your needs. What if a future project needs a 24-hour clock rendered? Or what if I don’t want to render text but will instead render an analog clock?

So good design tells me that getTimeText() is more of a rendering helper than an intrinsic part of the TimeOfDay struct, and therefore it should be separate.

But incremental and iterative change tells me that I can move it into the struct, then add tests to it, as well as other TimeOfDay functionality that wasn’t related to rendering, and I can worry about creating separate renderer utilities in the future. Future Me might wish he had them, but he’ll be appreciative that the improved test quality allows him the option to create them much more easily.

I have a function that generates the store’s hours that makes use of getTimeText(). It changed from

hoursOfOperationText << getTimeText(openingTime) << " - " << getTimeText(closingTime);

to

hoursOfOperationText << openingTime.getTimeText() << " - " << closingTime.getTimeText();

It’s not a big difference, which is part of the point. I wanted it to be an easy change that improved things, a simple step forward.

I don’t like that TimeOfDay is in charge of figuring out how to render its time, but for now, I’ll take well-tested, refactorable code over trying to fix all of the design problems at once without a test suite to catch breaking changes.

Oh, and TimeofDay.cpp is 63 lines of code. It’s super easy to read and understand.

I frankly don’t have the hours to spend trying to remember everything I might break while making a large change, so I make smaller changes that I can handle in a single session between the day job and taking care of the kids.

Also, I went on vacation

I meant to work on actual features to continue to inject personality into the project, but I spent almost 2 hours writing a blog post and an email newsletter to announce the most recent update to Toytles: Leaf Raking, and the remainder of my time on changes like the ones above to make future changes easier and safer.

And then I left town for a birthday vacation at a log cabin in northeastern Iowa. It’s easy to social distance when you are living in a forest with no AC. I had a great time with my family.

But that vacation is also why I haven’t written up this report for Monday like I planned. Next week’s report should be on the regularly scheduled day.

Next time, less random work

So my previous sprint got cut short, and my current sprint started late. I should probably commit to less getting done as a result.

I should ALSO stop myself from working on random “improvements” unrelated to what I committed to working on. I want more automated tests to give me confidence in what I am changing. I want to make the code easier to work within. I want to make Future Me happy.

But I also need to recognize that I didn’t get anything player-facing accomplished. I didn’t even address any bugs. I just restructured some code. There’s no actual value in doing so. In fact, Future Me might not even find any improvement in his situation from the changes I did make since I made them without any thought toward what he was going to work on next.

On the one hand, I’m the boss. I can give myself time to work on nothing but code quality improvement. No one is going to give me a hard time about slowing down my next release. On a whim, I can decide to work on a completely different project if I wanted, or not work on a project at all if I need the break.

But on the other hand, I’m the boss, and I need to keep my eye on continually adding value for players. No one else is responsible for ensuring that what I’m working on is the highest priority, and I need to hold myself to a higher standard. I need to be more deliberate.

You might think that I am chastising myself, but I’m not. I’m just taking this opportunity to analyze how the past sprint went and how I would like to change things going forward to improve future sprints. Future Me is going to have the benefit of hindsight, as well as a project that has steadily been improving in quality both from a game play perspective as well as a codebase perspective.

So going forward, code quality improvements should always correlate to work that adds value to the game in the eyes of my customers.

InGameState.cpp has a lot of low-hanging fruit along the lines of long functions I can shorten and classes I can pull out into their own files, so the temptation is going to be to tackle these all at once. I love refactoring code, even if I am just “refactoring” code carefully without the benefit of tests. And ultimately I’d like to get most of the code covered by tests, which requires pulling out more and more into separate modules.

But I’m also running an indie game development business. No one is going to be convinced to purchase a game because its internal code is easier for the developer to work with. There is value in Future Me having an easier time supporting and updating this and other projects, but Future Me is smart and capable enough to make the changes he needs when he needs them. Current Me will make the changes I need as I need them today.

I’ll waste less time on guessing what might be useful and actually work on what I see would be useful now.

Toytles: Leaf Raking Player's Guide

Want to learn when I release updates to Toytles: Leaf Raking or about future games I am creating? Sign up for the GBGames Curiosities newsletter, and get the 24-page, full color PDF of the Toytles: Leaf Raking Player’s Guide for free!

Play Toytles: Leaf Raking v1.4.1 – Now with More Personality!

I’ve published another update for Toytles: Leaf Raking, my family-friendly leaf-raking business simulation available for iPhones, iPads, and Android devices.

Learn how to get it at the Toytles: Leaf Raking page.

Version 1.4.1 is part of what I am calling the “Personality Injection” updates. It builds upon the previous update to give the neighbors even more things to say when you visit them.

Your neighbors are now starting to show their individual personalities. For example, Pierre seems friendly when you first meet him:

Pierre's initial greeting

Once he becomes a client, you learn that he is a birdwatcher!

Pierre's a birdwatcher!

Eventually future updates will allow Pierre to demonstrate his love of birds as well as potentially a few of his other interests.

Of course, if you neglect his lawn and get fired, Pierre will find that he wishes he had his free time back:

Pierre's disappointment

Your neighbors are film fans, students, painters, animal lovers, day dreamers, and more.

I hope you enjoy getting to know the neighbors as much as I have been. I can’t wait to find out what Pierre discovers.

Toytles: Leaf Raking Player's Guide

Get the 24-page, full color PDF of the Toytles: Leaf Raking Player’s Guide for free by signing up for the GBGames Curiosities newsletter!

Toytles: Leaf Raking Progress Report – A Little More Conversation

Last week, I reported on the progress of my first few sprints to create updates for my leaf-raking business simulation Toytles: Leaf Raking.

Sprint 4: More personality injections

Last week’s sprint plan included:

  • Give each neighbor unique text as unhappy clients
  • Add variation to weekend flavor text

I was able to work 5.25 hours, but unfortunately part of that time was used to create the v1.4.1 release for both the App Store and Google Play.

Unfortunately, it’s not yet an automated process. Well, the Android build is mostly automated. I can run a single command and have a release APK ready to install on my own devices to test with or to upload.

But due to Apple changing requirements every so often, breaking old scripts and how-tos, I unfortunately can only get so far before I need to fiddle with things in Xcode in order to create a publishable release for iOS.

I have it mostly down to only a few manual steps related to getting the app icons setup at this point, but it’s still tedious and annoying that I need to do so.

I decided to work on the weekend flavor text, which is all I finished for this sprint unfortunately.

There were originally two random dialog sequences that occurred when you woke up on a weekend, telling you about a dream you had. Now there are 11 more.

New dream

Part of the time was rewriting some of the code so it was easier for me to add a dialog sequence. I think ultimately it would be best if I could change the code to load the scripts from a text file. For now, I’m adding the scripts in code, which requires a recompile to test each time. But it works.

None of these events make a difference to the game play. That is, they don’t help or hinder your progress towards earning enough money to purchase the Ultimate Item(tm).

So why do it? It’s all part of my attempt to inject some personality into the game.

And eventually I would like to add weekend events that allow the player to make a decision that could, in fact, make a difference. So for now, I’m laying the groundwork.

Until next sprint

So this week’s sprint I will definitely get to the unhappy client dialog. I have some marketing screenshots to update, partly due to the copyright date, and I want to streamline the way you wait for rain to end so you can get back to raking as it is currently a bit clunky.

I’d also like to invest in getting the release builds a bit more automated, and I am kicking my Past Self for not spending more time on automated tests so I didn’t have to verify that I didn’t break anything each time I make a change. I’m slowly improving the code as I go along so that it is easier for Future Self, but Past Self is a short-sighted jerk.

Toytles: Leaf Raking Player's Guide

Want to learn when I release updates to Toytles: Leaf Raking or about future games I am creating? Sign up for the GBGames Curiosities newsletter, and get the 24-page, full color PDF of the Toytles: Leaf Raking Player’s Guide for free!

Twitter: gbgames

  • I found some caramel popcorn purchased from my son's Cub Scout troop last year. It turns out that caramel popcorn can go stale. B-(
  • Uh, oh. My cucumber plants aren't looking so great anymore. Any green thumbs know if this is more than a nutrient… https://t.co/R3gLPoALOf
  • RT : Podcasters and streamers out there: I'm trying up up my streaming game. When I'm listening to playback I realize I can he…