Category: Roguelike


I spent my spare coding hours over the last few days investigating installers for Windows and Mac.  On Windows I leveraged WiX which is simultaneously a life safer and a leading cause of developer suicides; but it works.  Mac is a bit more esoteric as my Xamarin license doesn’t currently include Mac (and for $249 it’ll be a while), which means I have to deal with including Mono myself, and my non-Windows build experience is non-existent.  So: I now have a Windows installer, but Mac will have to wait a bit longer.

Here’s what you can do in the alpha:

  • Precious little.  This isn’t intended to be playable or fun – it’s just testing out infrastructure.  You can move around, single-shot mobs, see the pretty shadows, pick and and equip items, and ascend/descend levels.  All the trademarks of any AAA game!
  • You can move using arrow keys, tap to move, or clicking on the primary CAB button (upper right corner) to Explore (which currently ignores mobs and items and just explores until the map is full).  You can also use the CAB buttons to do other things like pick up items, equip them, and ascend/descend stairs.
  • Main keys: arrow keys to move, enter to perform primary cab action, 1 or 2 to perform skills 1 or 2. I, T, S, C to bring up various panes.  “-” and “+” zoom in and out a bit.

Here’s what you can’t do in the alpha:

  • Pretty much anything.  But why would you want to?

Hopefully that’ll change in time…

Here’s a link to the alpha demo. Run it like any other MSI and it’ll install into prog files with a desktop icon to launch it (and you can uninstall it using the standard Windows uninstall programs UI). Note that it may pop the Smartscreen warning when you try to run it; click ‘more info’ and then ‘run anyways’ to bypass it.

7YRL (Seven Year Roguelike) pre-pre-pre-alpha demo

(If that isn’t working yet, you can also find it here: http://www.wanderlinggames.com/7yrl/7yrlalpha.msi. )

If you try it out, let me know how it works!

Cheers,
Jeff

Lot of progress since my last post, although little of it shows up directly to the user.  The following is now possible:

cells

Yay! green and orange squares! What more can you want from a game?

Granted, it doesn’t look like three days worth of work, but here’s what that’s showing:

  • The green cells are “poisonpit” cells, and the orange ones are ‘firepit’ cells.  Both are defined completely in the defn xlsx, including new tiledefns (for visuals) that points into the tile spritesheets.  It’s cool to be able to add new cells without touching code
  • The poison pit cell is defined to have a CellEffect on it which triggers when an actor enters it.  There are many other events that it could trigger off of – wielding a weapon, moving, taking damage, etc; but for CellEffects the HasBeenEntered event is the most common.
  • When the CellEffect’s HasBeenEntered event has been triggered, it fires a TakeDamage action on the entering action and subtracts health.  The cell effect also has a OnWorldTurn event which triggers, firing the same action as long as the actor is in the cell.
  • The firepit cells are similar, except instead of firing a TakeDamage action on enter, they fire an ApplyBuff action to the action, which applies a “burning” Buff which lasts 5 turns, triggering TakeDamage actions on each OnWorldTurn Event.

I’ve also added AoE actions, modifier action, and a slew of other components to the system. The cool thing about this is that it’s all using the same event/action system, which means that I can now easily (e.g. without code) create:

  • A sword that when equipped gives a +damage-dealt modifier
  • A shield that, when its equipper takes damage, gives a Renew buff (+1 health per turn for 5 turns) to the equipper
  • A “freeze” celleffect that when stepped in, disallows the enterer from moving for 5 turns, does 2 damage to them each turn, and negates any incoming damage from attacks.
  • A mob that when hit has a chance to proc an ‘enrage’ buff which gives them 2x damage for 3 turns but also takes 25% of their health
  • A skill that when used gives any of the above effects as buff to the caster, to an enemy, to a group, …
  • etc

A varied and unique skill system is now going to be easy and fun to create…  I can also easily add other new gameplay mechanisms using this; imagine adding “Traps” – place a trap in a cell, and if a mob steps in it it freezes them in place for a 5 turns.  Sound familiar?  it’s just adding a celleffect to the cell which the Trap was placed in.

All actions are (or will be) implemented as three phases:

  • Phase 1: Send notification events, allowing cancellation (EventType.BeginMoving, BeginEnteringCell, BeginAttacking, BeginOpeningDoor, etc).  Here’s phase 1 for the Action_Move action:
// ==== PHASE 1: Notify listeners of the intent to perform the Action, allowing them to modify or cancel the action ====
//      Important: These listeners must not modify gamestate as subsequent listeners in the chain may cancel the action.

// If the Actor was previously in a Cell then trigger ExitingCell, Moving, and EnteringCell events; otherwise just trigger EnteringCell events
bool wasInCell = oldLocation != null;
if (wasInCell)
{
// Trigger any "BeingExited" events on the actor's previous location, and any "ExitingCell" events on the actor that is moving
if (EventMgr.FireTwoWayEvent(EventType.BeginBeingExited, EventType.BeginExitingCell, movingActor, oldLocation) == EventResult.Cancelled)
return;

// Trigger moving events on the moving Actor.
if (EventMgr.FireEvent(EventType.BeginMoving, movingActor, movingActor, movingActor) == EventResult.Cancelled)
return;
}

// Trigger any "BeingEntered" events on the destination cell and any "EnteringCell" events on the actor that is moving
if (EventMgr.FireTwoWayEvent(EventType.BeginBeingEntered, EventType.BeginEnteringCell, movingActor, newLocation) == EventResult.Cancelled)
return;

// If here, then the Action has not been cancelled and will be performed.  At this point the Move cannot be cancelled or modified further.
  • Phase 2: At this point, the event can no longer be cancelled.  Modifications to the game state are performed during this phase (e.g. moving the actor to the destination cell), but other event/actions can modify some of the changes; e.g. the Action_MeleeAttack flow fires events that allow modifier actions (e.g. “+10% damage” buff) to modify the amount of damage dealt or received.  Here’s phase 2 for Action_MeleeAttack:
// Calculate damage
float damageAmount = attacker.RawDamage;//.WieldedWeapon.BaseDamage;

// TODO: Go through flow - block, dodge, etc
// TODO: Give damage modifiers a chance.  Armor, Buffs, etc

// Give the attacker's Damage modifiers a chance to modify the amount of damage dealt
if (attacker.Events.ContainsKey(EventType.CalculatingDamageDealt))
{
foreach (EventModel evt in attacker.Events[EventType.CalculatingDamageDealt])
{
// The CalculatingDamageDealt Modifier event will modify DamageAmount
evt.ActionInfo.DamageAmount = damageAmount;
EventMgr.FireEvent(EventType.CalculatingDamageDealt, attacker, attacker, target);
damageAmount = evt.ActionInfo.DamageAmount;
}
}

// Give the target's Damage modifiers a chance to modify the amount of damage taken
if (target.Events.ContainsKey(EventType.CalculatingDamageTaken))
{
foreach (EventModel evt in target.Events[EventType.CalculatingDamageTaken])
{
// The CalculatingDamageTaken Modifier event will modify DamageAmount
evt.ActionInfo.DamageAmount = damageAmount;
EventMgr.FireEvent(EventType.CalculatingDamageTaken, target, target, attacker);
damageAmount = evt.ActionInfo.DamageAmount;
}
}

// Do the deed
target.Health -= (int)damageAmount;
  • Phase 3: Send notification events that the action occurred (EventType.Moved, EnteredCell, Attacked, OpenedDoor, …).  This allows chaining of events; e.g. a buff on a player that gives them +1 HP every time they open a door.  Here’s phase 3 for Action_Move:
// ==== PHASE 3: Notify listeners that the Action was performed, allowing chaining ====

// If user was in a cell then trigger ExitedCell and Moved events; otherwise, just trigger an EnteredCell event
if (wasInCell)
{
// Trigger any "HasBeenExited" events on the actor's previous location, and any "HasExited" events on the actor that moved
EventMgr.FireTwoWayEvent(EventType.HasExitedCell, EventType.HasBeenExited, movingActor, oldLocation);

// Trigger Moved event on the actor
EventMgr.FireEvent(EventType.Moved, movingActor, movingActor, movingActor);
}

// Trigger any "HasBeenEntered" events on the destination cell and any "HasEntered" events on the actor that is moving
EventMgr.FireTwoWayEvent(EventType.HasEnteredCell, EventType.HasBeenEntered, movingActor, newLocation);

There are a few things that I had initially implemented but pulled out when the system got too complex for this game.  A couple of those:

  • EventFilters: only trigger the event when a particular object (by TypeId or InstanceId) is used.  This would allow for data-driven generation of event-based puzzles as well as interesting weapons (a sword that gives a buff when used against an undead mob).
  • While modifiers: events only trigger WHILE a condition is met; e.g. equipped or acquired.  This allowed more complex event/action combos like a sword that gives one buff while in your inventory and another while equipped.  This really complicated some of the lifetime code and enables some interesting but frankly unnecessary scenarios, so I axed it.  ‘while equipped’ modifiers on items (very common) are just implemented as two events – HasBeenEquipped (typically adds a buff) and HasBeenUnEquipped (typically removes the same buff).

ProtoBuf made persistence of the events and actions surprisingly easy (given ample use of the AsReference modifier on ProtoMember).  Persisting pointers to objects (e.g. the source of an event or target of an action) can be a royal pain, but ProtoBuf made quick work of it.

A large part of the last few days work was building up tests for this.  Events and Actions are ripe for errors that are hard to track down or reproduce, and that gets even worse when persistence gets thrown into the mix.  To help combat that, I wrote about 2500 lines of test code to test many aspects of the system.  To test persistence, 500 lines of that is a fuzzing test which creates a 100×100 map and throws a bunch of mobs, celleffects, and weapons (most with onwielded events) into it.  It then spins for 1000 turns, picking random actions (move mob, mob1 attacks mob2, wield weapon).  Every 100 turns it persists the gamestate and then rehydrates it, and does a complete deep-comparison between the two, asserting that they match perfectly.  The eventing system is definitely not totally bug-free, but my confidence in it is much higher given this test.

btw, here’s all the code that’s required in order to create the celleffects listed above:

</pre>
// Poison pit
CellEffectEventDefn eventDefn = new CellEffectEventDefn("poisonPit1", true);
ActionInfo effectAction = Action_TakeDamage.GenerateActionInfo(1);
cell.Effects.Add(new CellEffectDefn(eventDefn, effectAction));

// Firepit
CellEffectEventDefn eventDefn = new CellEffectEventDefn("firePit1", true);
BuffDefn buffDefn1 = new BuffDefn()
{
    ActionInfo = Action_TakeDamage.GenerateActionInfo(2),
    InitialDuration = 4,
    HasLimitedLifetime = true,
    Name = "Burning",
    Id = "burning1",
    BuffTileId = "burning1",
};
ActionInfo effectAction = Action_ApplyBuff.GenerateActionInfo(buffDefn1);
cell.Effects.Add(new CellEffectDefn(eventDefn, effectAction));

All of the above was first done in a standalone command-line event/action prototype before being merged back into the actual app.  Here’s a link to the source code of the event/action prototype as a zip file for those masochistic enough to look: www.wanderlinggames.com/7yrl/eventandactionprototype.zip

Cheers,
Jeff

2D Shadows using 3D

Part of my latest roguelike project started with my playing around with 2D shadows for the fun of it.  There  are some great resources out there for examples of how to do 2D shadows; Catalin in particular did a bunch of the heavy lifting to figure out how to do soft shadows in 2D using Shaders.  I got a version of that up an running, but ultimately couldn’t get the performance I wanted out of the algorithm.  In particular older iOS devices and their lack of MRTs and painful fill rate absolutely killed my implementation of Catalin’s approach, which is very much shader and fill bound.

After squeezing every bit of perf I could out of that approach and not getting there, I gave up and took a few steps back.  I drew countless mockups of 2D shadows up on my whiteboard, convinced there must be a better way;

I drew many, many of these little guys

I drew many, many of these little guys

Then, epiphany time: staring at one of those mockups, I realized that it had the same general structure as a 3d overhead view with an extremely high fov.  Picture looking down at a city street, surrounded by skyscrapers looming up and out of your field of vision.

Here’s what I mean – take the screenshot below:

Shadows

And now imagine that each wall cell was actually a 3D object that stretched up and outside your field of view.  It’s easier to show than describe:

Like Tron, but with flaming skulls

At least, it’d be easier to show if I could draw projected lines to save my life

Notice how the shape of the lines in the lower picture directly matches the shape of the shadows in the upper picture?

To get that to work, I oriented a projection matrix on the 2D display, cranked the FOV way up, and placed 3D cubes to line up with the 2D wall tiles.  Set up a shader to just paint black (play with blending a bit) and et voila, you have 2D shadows with just a few 3D shapes.  I did some work to combine nearby cubes to reduce the total number of surfaces drawn, and now the actual casting of shadows is taking a negligible amount of time in my render loop.  I built a ton on top of that, including bump mapping, separating mob and wall shadows, orthographic projection “up” mobs and walls (so that the shadows can be cast up them correctly), multi-phase particle rendering and more – more on all that later.

At some point I’ll write up a more technical walkthrough of the above algorithm.  I’ll also have to dig up an older copy of the Catalin implementation (I did versions in both XNA and OGL) and a prototype build.

Cheers,
Jeff

Day 1007: *Blink*

So anyway; as I was saying a brief 1000 days ago…

Okay, maybe I’m taking the whole “seven year” part of 7yrl too literally ;-).  I’m working at a different company, still in the same house, still coding games at night to maintain sanity.  I took about 18 months off and spun up a few different projects (wanderlinggames.com and bluesky.io, primarily), but my life of an indie developer recently gave way to my life as an individual with a paycheck.  Which isn’t actually a bad thing.

I also recently started in on my roguelike coding efforts again.  I’ve started over from scratch again; one of the joys of not having a deadline imposed (well, other than the self-imposed 7 year one 😉 ) is that I can start anew knowing a bit more than last time.  I’m taking a few things from the previous attempt and will take more (primarily the dungeon generation code), but by and large it’s a completely different project.

The main thing that drove me back to coding up a roguelike is Pixoban, of all things.  It’s a pretty standard Sokoban, but with graphics that simply scream “make a roguelike with me!”  Check this out to see what I mean: http://www.148apps.com/wp-content/uploads/2013/08/pixoban5.jpg.

However, no one has made one (slackers, all of you!).  In the spirit of making the game you want to play, I’m, well, making the game I want to play.  As you can tell from the image in my previous post, I’m going for a bit of a darker feeling and playing a lot with shadows (lots more on that in future posts).

Here’s the short list of core pillars driving the game:

  • Old school 8- and 16-bit graphics with new school flair; real time shadows, particle effects, SSAO(-ish), normal lighting on mobs, bump mapping, ortho projection, and animated tiles.
  • Roguelike permadeath but with progression; when your character dies, he dies; but you maintain knowledge that helps you get farther next time – weapon proficiencies, monster proficiencies, skills, Professions, and more.
  • Heavy crafting influence; discover ingredients and recipes to decipher in order to make items – once you’ve made an item you can use it, but it also can then appear as a drop in subsequent plays.  Skills are also crafted, as are runes to augment weapons.
  • Targeting both Mobile and PC.  I’m building screens and controls from the start to support arbitrary resolutions as well as both portrait and landscape modes
  • One-finger play on mobile; I want to hold my phone like I was browsing the web when I play the game.  I’ll talk more about the Contextual Action Bar (CAB) later.

Current status:

  • All of the graphic systems listed above are coded and fairly production-ready. I’m getting acceptable performance on my 2011 macbook air, and an iPad 2 is acceptable; anything less isn’t.
  • State management (with transitions) is done.  Async loading is also almost done – some issues with OGL and TPL interaction to work through still.
  • You can move around using keyboard or tap-to-move or A* explore.
  • Persistence model is in place and functioning for multiple games and multiple levels
  • Works on both Mac and Windows now, and iOS builds work except for some layout issues.

I still need to finalize how much to open source and how to deal with it.  e.g. I picked up a license for Oryx’s great 8bit and 16bit sprites and am using both, so just posting stuff up on github may not be an option.  I need to read up a bit on how best to handle that situation (e.g. is a note in the license.txt sufficient?).

One more in-game shot:

Game

If you zoom in you can see the bump mapping on the floors as well as the walls above the player.  Up near the top is a mob with a colored light, and the flaming skulls all have particle systems attached to themselves.  A bit harder to see (but present) is that all of the mobs and items have normal maps and light correctly relative to the light; a subtle but nice effect.  The skeleton to the left of the player shows it best in this shot.  You can also see the SSAO-esque effect best in the space to the right of the stairs going down (upper left of the image).

It’s good to be back,
Jeff

… but nothing to announce yet. Taking a queue from animaleante, I’m moving to a model where I go from two distinct update steps (“do animations” and “update turns”) into one combined update step (“do animations if there are any, update turns if not”). This’ll require replacing the Actor list with a priority queue (to eventually support an Energy-based system where Actors are not locked to one-turn-per).

Jeff

Day 40: Miscellaneous cleanup

I’m still pondering the right way to incorporate real-time Animations in with a turn-based system.  While that’s percolating, I did a bit of miscellaneous cleanup:

  • When the player targets an empty cell for an AoE skill, display the Cell background and Adornment (if any) in the targeting dialog
  • Slight refactoring to Cell.Render to handle how AoE range is rendered over out-of range cells (thanks animaleante!)
  • Actors regen health each turn (‘n’ turns after performing the last action).  This was just adding some health directly each turn; I refactored that to use the Event (and eventually Buff) system.  This will allow things like Levels that disallow any healing (including regen).
  • I cleaned up the DecideNextAction/PerformNextAction stuff a bit in anticipation of figuring out the Turn/Animation issue.

Here’s what the AoE targeting UX looks like when the area of effect falls outside the range of the Skill:

I’ve got vacation coming up next week (sunny Cabo San Lucas, hallelujah! 🙂 ) –  I’m hoping to resolve the Turn/Animation issue this week so that I can focus on more fun aspects of the Dungeon :).

Here’s the code and exe:

Updated source: http://wanderlinggames.com/files/dungeon/dungeon-4-19-11.zip

Updated executable: http://wanderlinggames.com/files/dungeon/dungeonexe-4-19-11.zip

Some more improvements made to AoE targeting today:

  • When AoE targets a cell with a monster in it, CAB-select the monster
  • Support targeting valid empty cells for AoE skills

I’m still wondering what to do about Animation timing ><…

Quick update today on the Dungeon; I fixed the issue where firing the same Animation would cause it to not remove the emitter from the system (the result being that the particles wouldn’t stop emitting even after the Animation ended).  In order to reduce memory allocations, I don’t put instance-specific variables in the derived keyframe classes.  Or rather, I tried not to 🙂 – I was allocating the particle emitter in the AnimationKeyFrame_Particle.Start function and deallocating it in AnimationKeyFrame_Particle.End.  So two calls to Start would result in two allocations and only one deallocation.  This was fixed by making the abstract AnimationKeyFrame.Start return a derived AnimationKeyFrameInstanceData object, which is then passed to Render and End.  Most keyframe classes ignore this, but the particle one uses it to store the emitter (and system) and delete them in End.

Using that, the AnimationKeyFrame_Particle class is updated as follows:

 public override KeyFrame_InstanceData Start(GameObject performer, GameObject target)
        {
            // Create the per-instance data object to store our particle emitter nad system.
            AnimationKeyFrame_Particle_InstanceData data = new AnimationKeyFrame_Particle_InstanceData();

            // Create the particle system for this keyframe if not already created.
            data.ParticleSystem = new ParticleSystem(this, 10);
            ParticleMgr.Systems.Add(data.ParticleSystem);

            // Spin up a new particle emitter for this instance
            data.ParticleEmitter = new ParticleEmitter(data.ParticleSystem, 10, GameState_InDungeon.MapCellToScreenCoords(performer.Location));
            ParticleMgr.Emitters.Add(data.ParticleEmitter);

            return data;
        }

        public override void End(KeyFrame_InstanceData data, GameObject performer, GameObject target)
        {
            AnimationKeyFrame_Particle_InstanceData myData = data as AnimationKeyFrame_Particle_InstanceData;

            // Tear down our particle emitter
            myData.ParticleSystem.RemoveWhenDone();
            ParticleMgr.RemoveEmitter(myData.ParticleEmitter);
        }

… and the end result is a correctly working Animation system (whew).

Here’s a pic of AoE in action:

No code drop today though; still some bugs in the AoE targeting system.  More impactfully, it has raised some questions about turn timing and animations that I need to think through (specifically: user uses a skill like ice storm (in the picture above) – do the monsters move at the same time as the skill animation fires? after? before? automatically? etc…

Day 37: AoE selection

Not much time to work on the Dungeon today, but was able to make some progress on AoE target (cell) selection for AoE skills. This is requiring some nontrivial (but not unexpected) tweaking to specifying Skill targets – before I was passing a GameObject, but now an AoE  Skill can impact multiple targets.  This has also caused a bit of refactoring in event Animations, causing me to move the animation into the Action definitions (in addition to the higher level Events being able to specify animations), and adding support for both success and failure animations.

Here’s what the definition for an AoE “Ice Storm” skill looks like:

    <Skill Id="Priest3" Level="1" Name="Ice Storm" Tile="Skill_IceStorm" Description="Blast a group of targets with ice">
      <Event Type="UseSkill" SelfSuccessAnimation="Rainbow">
        <DoDamage Target="Cell" Amount="4+1d2" Range="3" AoERange="2"
                  OnSuccessText="A bolt of ice hits the {target} for {amount}"
                  TargetSuccessAnimation="IceBolt" TargetFailureAnimation="IceBoltFail" />
      </Event>
    </Skill>

Note that an AoE skill has both Range (how far away the Skill can be applied) and AoERange (the range of impact of the Skilll when used).

Here’s what the AoE selection currently looks like.  It highlights the AoE range of impact in yellow:

No code as there remain many issues with the Animation refactoring and target refactoring…

I’ve worked out the last few kinks in single-mob target selection, although I’ve turned up a bug in the Animations that I need to track down (specifically: if you fire off the same animation twice in quick succession, it fails to remove the emitter from the system; some ref-counting issue somewhere…).

In order to minimize the amount of thumb movement while playing, the ContextualActionBar and SkillsBar work together:

  • Performing a Skill on a target mob selects that mob into the ContextualActionBar
  • Performing a skill from the SkillBar now sets the secondary skill in the ContextualActionBar to the same skill
  • Selecting a Skill from the SkillBar when there’s a target in the ContextualActionBar automatically selects that target for offensive skills (i.e. doesn’t open the targeting dialog

Here’s what it looks like after clicking on the skill in the skillbar and selecting the target:

Here’s the code and exe:

Updated source: http://wanderlinggames.com/files/dungeon/dungeon-4-15-11.zip

Updated executable: http://wanderlinggames.com/files/dungeon/dungeonexe-4-15-11.zip