Productivity

I’m finding it helpful to have a variety of tasks available to work on each day. Adding content is the top priority, but the odd bug fix or feature addition goes a long way toward keeping my productivity up. Otherwise I tend to get quickly burnt out.

Full changelog will be tomorrow during the Weekly Update, but yesterday I added a few content items, expanded the music feature, and made a bunch of small tweaks (like adding a command to switch visual themes).

StupidRPG: Litterbug Quest

One of the first quests in StupidRPG. It includes mandatory and optional objectives, as well as physical and intangible rewards. Spoilers below.

Quest: Litterbugs


Ranger Bob: Somebody’s been leaving trash all over the forest. I suspect youths. I can’t leave the watch tower, so if you can do something about this I’d surely appreciate it. In return, I can probably help you out with…whatever it is you’re doing here.

Ranger Bob: Oh, and one more thing…if you happen to find out who’s responsible…I don’t need the details. Use your best judgement.


Objectives

  • Find and dispose of the trash scattered around the forest
  • (Optional) Find out who’s been littering
  • (Optional) Deal with the litterbug(s)

Rewards

  • Mystery Item
  • New Friend: Ranger Bob

Nothing is a Part of Something

Yesterday I added two new Components to the game: Part and Nothing. These are intended to help simplify a couple object interactions I ran into.

Part

A Part is a regular object in most respects, but it’s directly connected to another object. A button on a shirt, a branch on a tree, etc. Parts don’t have much in the way of unique behavior (yet). For now it allows me to easily restrict objects from being taken when they’re part of a larger object.

Part Component


1
2
3
4
5
6
parts.c('part', {
'dependencies': [],
'onAction.TAKE':function(){
queueGMOutput("That's part of the "+getNameTag(this.parent)+".");
}
});

Later on, parts might interact with their parent objects in more complex ways.

Nothing

A Nothing is a thing that isn’t really a thing. The specific use case was creating a hole in something. You can look at a hole, and possibly put things in a hole, but some activities don’t make sense with an abstract object like a hole. For example, you can’t take a hole. The response to a player trying to take a Nothing is slightly different than a Part. You can’t take a Part because it’s attached to something; you can’t take a Nothing because it’s fundamentally not a thing that can be taken.

Nothing Component


1
2
3
4
5
6
coreModule.c('nothing', {
'dependencies': [],
'onAction.TAKE':function(){
queueGMOutput("That's too abstract to be taken.");
}
});

As part of building the bird feeder item, I added a hole (nothing) that is a part of the feeder. The feeder also has a part called a post (which is not nothing), and together it all makes a whole.

StupidRPG: There is no Time

Yet. Some interactive fiction tracks turns, some track time (where 1 turn often equals 1 minute), some track neither. StupidRPG currently tracks turns (internally) but not time. I don’t currently have any plans to add a dynamic day-night cycle (though it might show up on the Wish List), and time of day is not generally relevant to any of the mechanics.

That said, time (tracked or not) will pass for story reasons. In Act II, the player returns to a familiar region, now at night. Since this is mostly a descriptive element, I’d also like to tie the background art into the change to help sell the feeling. I’m tempted to experiment with a larger style overhaul (text/background colors), but I don’t want to disrupt the player too much.

If I do decide to pursue a day-night cycle, Act III (which contains a ‘living’ town) will be the testing ground. That’s a major undertaking, however. I’m not making Majora’s Mask here.

Web Apps for the Desktop

This is a weird one. SRPG is a website (web app?). But what if…what if you could run web apps directly on your desktop?

Well, as it turns out, technology has come full circle. A few months back I looked into a system called TideSDK, which acted as a native wrapper around WebKit (the backend for browsers like Chrome) and allowed you to distribute websites/web-apps as native applications. On the face of it, this is slightly ridiculous, and I don’t have much of an argument for why such a thing should exist (beyond “lots of people have experience building software with web tools these days”).

TideSDK died under mysterious circumstances, but the semi-recent release of the Atom.io IDE (built on NodeJS) led to the creation of Electron, which performs similar functions but has the advantages of being 1) not dead, and 2) open source. It took about 3 minutes to install it and get StupidRPG running:

Electron Screenshot

So far so good. Supports full screen, seems to be using a recent (if not the latest) version of Chromium (which means that dumb rainbow text works, unlike in TideSDK), and gives access to the standard debug tools. Nifty.

On to the tougher bit: finding a good reason to view this as anything other than a novelty. It does open up a few interesting features / conveniences. For example:

  • Helpers for native file handling (handy for save files/etc, but you can accomplish this without much trouble in a standard website)
  • Can add user tasks (basically a context menu on your application icon). Don’t have an immediate use for this.
  • Can easily register global keyboard shortcuts.
  • Can create native menus and native context menus.

Super exciting, I know. Not a compelling case yet, but it’s been fun to play with. Now that I think of it, Steam doesn’t have nearly enough text adventures…

StupidRPG: The Big Bad

Spoilers incoming.

I’ll do a proper bio of the Big Bad later. For now I want to talk about the concept. Most RPG stories have a final boss, primary threat, etc. The final (usually) challenge for the players to overcome on their way to heroic victory. This usually involves a lot of setup, sometimes early in the campaign (as a distant threat the players can’t/shouldn’t address immediately).

In the case of StupidRPG, the Big Bad is:


The mysterious Baron, actual name unknown. A relative newcomer to the local territories, the Baron has established a significant powerbase through various means. It’s not all bad, in the same way that the Industrial Revolution wasn’t all bad. The Baron’s motives are unclear, and though violence doesn’t seem to be off the table, outright slaughter doesn’t seem to be on it.

In short, it might take a while before it becomes apparent whether the Baron is a real threat, or if this is one of those stories where the real challenge was overcoming inner conflict and achieving your full potential.

Could be a bit of both.

Weekly Update #5

More progress!

Changelog:

  • Added a bird. It flies around the map looking for food. Once it finds (and eats) something it likes, it will go find the Ranger and hang out with him.
  • Worked on some AI handling style to support the bird behavior in a reasonably clean way.
  • Added a bird feeder for the bird.
  • Moved all BlackBox module content into sub-files broken down by type. Creatures, locations, items get their own files (module file was getting huge).
  • Added a ‘local output’ function to supplement the output and GM output functions. Local output only displays if the originating entity is in the same location as the player. Lets me skip redundant code (i.e. having the entity itself check for locality before printing something).
  • Added ECS.moveEntity function to simplify NPC movement.
  • Added locationIs function to Thing component to simplify NPC location checking.
  • Fixed NPCs executing during non-tick turns.

Known Issues:

  • Nothing new

Next Up:

  • Fill out Ranger Bob’s dialogue tree.
  • Add The Twins.
  • Litterbug quest.
  • Feed the bird quest.

Feature Creep

Many (most?) of my projects are marred by scope/feature creep. That is, the tendency to continually add features or expand the scope of the project after starting it. It’s a dangerous game. For my hobby projects, the size of the project tends to be inversely proportional to the likelihood of completion.

For recent projects I’ve been vigilant about creep, as I’d like to actually finish a few things for once. My side project (Numenera power cards) was successfully finished and test printed, which is a nice morale boost. I have a couple tweaks to make on the coloring and font sizes, but the test went very well.

SRPG is particularly open to creep. It’s a big project, has a lot of interlocking pieces, and is taking place over a long period of time. As such, I have to be very strict about not adding anything (or doing refactoring) that doesn’t absolutely need to be there to meet my milestone goals. Structured AI, callback improvements, more content, etc.

So far, so good.

StupidRPG: Bird is the Word

Today I’m working on the first indepently-acting NPC: a bird. Pretty exciting, yeah? It flies around the map looking for food, avoiding unwelcome contact from the player, and (when no longer hungry), paying a visit to Ranger Bob.

Its behavior will likely grow more complex over time, but as a proof of concept I’m pretty happy with how it’s working. I started with a basic outline of the bird’s possible actions and logic.

Behaviors
1
The bird roams the Forest, chattering away and looking for food.

Starting (Max) hunger is 100.
    Reduced by 60 when eating.
    Can go below 0.
    Increased by 1 per turn.
    
General Behaviors
    If the player tries to touch/take/hurt/socialize me, flee along loop

Hungry Behaviors
    Every turn, if the player is in the location, chirp
    Every turn, if there is something edible in the location, try to eat it
    Every 2nd turn, if there are no edibles in the location, move along loop
    If the bird feeder is here, check it for food

Full Behaviors (Hunger <= 20)
    Every turn, head toward ranger station
    If I'm at ranger station, sing

Next, I started putting together a set of functions to handle each of the bird’s actions: look for food, move along path, etc.

Actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
actions = {
'_lookForFood':function() {
var things = this.location().children;
for(var f in things) {
console.log("BIRD EYEBALLING "+things[f].name);
if(things[f].hasComponent('edible')) {
if(things[f].nutrition > 0) {
queueLocalOutput(this, "The bird pecks at the " + things[f].name + " eagerly.");
this.hunger -= things[f].nutrition;
return true;
} else {
queueLocalOutput(this, "The bird pecks at the " + things[f].name + " half-heartedly.");
return false;
}
}
}

return false;
},
'_moveAlongPath':function() {
var prev = this.location().key;
var next = this.path[prev];

this._alertPlayerToMovement(prev,next);

ECS.moveEntity(this, next);
},
'_alertPlayerToMovement':function(prev,next) {
if(player.locationIs(prev)) {
queueGMOutput("The bird flits away toward " + ECS.getEntity(next).name + ".");
} else if(player.locationIs(next)) {
queueGMOutput("The bird flits in from " + this.location().name + ".");
}
}
}

Finally, I put it together into some simple branching logic, executed on each game tick.

Bird Brain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function(){
// Look for food when hungry
if(this._hungry())
{
if(!this._lookForFood()) {
this._moveAlongPath();
}
return;
}

// Head toward ranger station when full
if(!this.locationIs('ranger-station'))
{
// Head to ranger station
if(this.locationIs('north-trail')) {
this._alertPlayerToMovement('north-trail', 'ranger-station');
ECS.moveEntity(this, 'ranger-station');
} else {
this._moveAlongPath();
}

return;
}

// Sing at random
if(random() > 0.75) {
queueLocalOutput("The bird chirps a happy tune.");
}

// Update hunger
this.hunger++;
}

With these simple behaviors and extremely simple logic, the bird will roam the map, interacting dynamically with any food it encounters (stopping to sample it, and continuing to eat if it’s still hungry and the food is nutritious). Once full, it will make its way to see Ranger Bob, and serenade him until it gets hungry again.

The code structure is a little clunky, and I’d like to turn it into something closer to a state machine, where the bird starts in one of a few states (LOOKING_FOR_FOOD, LOOKING_FOR_BOB, SINGING) and can then transition into other states. Managing AI via a state machine is a more scalable solution and will make it easier for me to assemble complex NPC behaviors.

The Grind

Computer RPGs have a bad habit of padding their content to increase the game’s playtime. They have a bit of incentive to do so, as many players measure the game’s worth in part based on the number of hours they can sink into it. Some games justify the length, but more often in my experience, the game becomes a grind. Hack-and-slash or action RPGs are particularly common examples, but even more plot-heavy RPGs like Final Fantasy VII or Mass Effect are guilty of this.

Random encounters, same-y enemies, and arbitrary objectives (Collect 30 Wolf Pelts) tend to leave me feeling burnt out (YMMV). Action RPGs try to keep things fresh by varying up the locations, periodically introducing new enemies (often location-themed), and tossing in the occasional puzzle or unique side quest. It’s a distraction (a welcome one) from the otherwise repetitive gameplay.

For StupidRPG, I’m aiming for roughly a 4-hour playtime. Even that length may be excessive; I’m not confident in my ability to keep a modern gaming audience interested in a text-based game for several hours, at least not without moving in more of a MUD direction. The exact amount of time isn’t important to me right now, however. What’s more important is the amount of unique, quality content. SRPG is more about interesting text than increasingly large numbers.

I have a lot of work to do.