2010/09/14

Buffs and Debuffs

These buffs/debuffs are planned for my game:

  • DamageOverTime: lose 1 hp per second for the duration (e.g. bleed, poison)
  • HealingOverTime: gain 1 hp per second for the duration (e.g. food)
  • Stun: unable to move/act for the duration (breaks upon damage)
  • Immobilize: unable to move for the duration
  • Slow: movement speed halved for the duration
  • Shield: absorbs damage up to the shield amount
  • Vengeance: when this ability is available, gained when blocking (up to 3)

These buffs/debuffs have to be stored in StatBlocks. The routine that gathers renderables should check each active StatBlock. Each of these types will have an associated icon. If they do have an animation (e.g. Shield), the animation info is stored in StatBlock. The sprites will be stored in PowerManager.

Other animations happen over the player when casting a new spell (e.g. Heal, Return). These could be simply handled as non-damaging Hazards.

Finally, some animations happen upon impact (e.g. blood spurt, electrical sparks). These can also be non-damaging Hazards created during the TakeHit() routines of Avatar/Enemy.

Actually, all after-effects should be applied by TakeHit(). Usually these will be debuffs set to the StatBlock.

Powers Analysis

Taking a look at each power and how they operate.

  • Shoot creates a Missile hazard. Already implemented.
  • Swing creates a Single hazard. Already implemented.
  • Lore isn’t implemented, not yet determined how this will be handled.
  • Return creates a NonDamage hazard; when the animation is done, set the teleportation variables. Maybe use some kind of return_countdown variable in StatBlock to facilitate this.
  • Bleed upon hit sets a DamageOverTime debuff. Create a blood spurt NonDamage hazard.
  • Block is a special case. It is a unique state. Transition to the state when the block key is pressed. Transition back to Stance when the key is released. Set a “blocking” bool in StatBlock.
  • Shock is a Missile hazard. Upon impact it has to chain to another creature. Not sure the best way to implement this. Perhaps within HazardManager where it has access to enemies and collision data. The routine should be in PowerManager itself; HazardManager should call that routine and pass the needed data (collision info and living enemy positions). This is such a specific implementation that it might slip to another release. Tagged with Air trait.
  • Heal creates a NonDamage hazard and sets hp. Not yet implemented.
  • Multishot creates three Missile Hazards. When I convert to polar coordinates I can offset (+/-) Theta to get the angle of these side missiles. Multi hit is handled by the 5 frame invulnerability.
  • Warcry removes all debuffs from StatBlock. Create a Single hazard that doesn’t do damage but has the Fear after-effect.
  • Quake creates a Single hazard with a Stun after-effect. Already implemented. Tagged with Earth trait.
  • Shield sets Shield in StatBlock. No hazard.
  • Cleave creates a Single multi-target hazard with a very large radius
  • Charge requires a specific implementation. Probably needs to be a distinct Avatar/Enemy state with increased speed at a floating point (x,y) angle. Basically the hero becomes a missile. Stun after-effect.
  • Freeze is a series of Single hazards with execution delays. Care should be taken to not let Freeze travel past walls, so PowerManager might need a copy of the collision grid. Tagged with Ice trait.
  • Teleport creates a NonDamage hazard and sets an intramap teleport at the mouse target location.
  • Piercing Shot creates a multitarget Missile hazard tagged with ArmorPiercing trait.
  • Vengeance causes a stack of Vengeance buff to be created when taking a hit. Casting Vengeance creates a Single hazard with increased accuracy/crit and removes Vengeance stacks.
  • Burn causes a Single hazard with wide radius. Already implemented.
  • Time Stop creates a wide-radius hazard with the Stun aftereffect.

2010/09/13

Missile Accuracy

Missile spells have a radius and speed. Radius is how close a creature has to be to the projectile for an attack roll to take place. Speed is the distance the projectile moves each frame.

Mouse aiming shouldn’t be frustrating. The best way to aid this is to give projectiles a very wide radius. However, make the radius too wide and a stationary enemy might be attacked by a projectile twice on consecutive frames. This messes up accuracy as a stat.

One solution idea: each time a missile attacks an enemy, hit or miss, mark that enemy as Targeted. This enemy is no longer a valid target of this projectile. Targeted can wear off after a very short time (say, 5 frames). Because of the global cooldown (1 second) and because there are not multiple heroes, we can just set Targeted every time an enemy is attacked and do auto-miss if still targeted.

That way we can increase the radius of ranged spells without worrying about overlapping attack rolls. One side effect is that the Multishot skill is fixed and becomes an AOE skill as planned instead of a WeaponX3 skill.

One concern with this solution is firing a new missile while the previous missile is active. I believe this is not possible with current timing.

2010/09/12

AOE Spells

I added a couple AOE spells, as those are fairly easy to implement: Quake and Burn. Note Quake doesn’t have its Stun component yet.

Also note that these spells are massively overpowered at the moment. I will be changing them so they can only hurt creatures if you have line-of-sight on them. Also I’ll change Burn so that you can only cast it on a location if you have line of sight on that location.

Burn will still be overpowered because of its long range + radius. Right now monsters won’t pursue if you are out of their threat range, even if they get hurt. I will have to modify creature behavior in two ways: (1) if an enemy gets hurt but the hero is out of threat range, pursue anyway; (2) if an enemy gets hurt but the hero is not in line-of-sight, use A* to find the hero.

2010/09/11

Projectiles

The latest check-in (revision 128) contains simple support for projectiles: arrows and shock spell. Note that Shock isn’t fully implemented (it doesn’t chain). You can’t use Shoot unless you have a ranged weapon equipped. Normally you wouldn’t be able to use Shock until you unlock the power, but I haven’t finished the changes for the ActionBar yet.

Note that simple hero sword swings are also converted to the new power system. Enemies aren’t converted yet, nor are potions.

2010/09/10

Armory Page

The new Armory page lists all the weapons and armor currently in the game!

Vista Workaround

Using Vista but having problems with OSARE? I’ve uploaded v0.09b which may help.

It looks like when running an .exe from Windows Explorer, the “Working Directory” is not always set to that folder. So when I try to load game assets (images/sounds) it fails. It fails ungracefully because I need to fix error handling in my code. But for now I’ve added Launcher .bat files that should help make sure the local folder is the one used when running the game.

If you have Vista please try it out for me and let me know. Thanks!

2010/09/09

Action Bar and Powers

Time to nail down the logistics of the Action Bar and Powers.

Action Bar already has a place in the dependency hierarchy: GameEngine -> MenuManager -> MenuActionBar. Note that Avatar.logic() will need to read from MenuActionBar to determine actions.

It’s possible that Powers need to be accessible in several places: MenuActionBar, MenuPowers, ItemDatabase, Avatar, Enemies, etc. It probably does not have dependencies of its own. So might create it in GameEngine and pass its pointer to the children who need it.

Action Bars slots (1-0 numbers and M1/M2) are loaded with Powers. Those 12 slots will be integers of the appropriate Power ID.

Powers have ID and Type. Each type (or collection of types) will be handled by its own function. An activate() function in turn calls the appropriate handler function for the type.

Example 1: Drink a Potion. Currently the activate() for this item is in ItemDatabase but it will move. Drinking a potion can be triggered in these ways: (1) right-click on the inventory, (2) hotkey or click the potion on the action bar. Play the potion sound effect. Alter Avatar.StatBlock.hp. Remove the potion from the inventory.

Example 2: Shoot a Bow. Triggered by hotkey or click the potion on the action bar. Create a new missile hazard. Change the Avatar state to Shooting.

Example 3: Cast Shock. Triggered by hotkey or click the potion on the action bar. Create a new missile hazard. Change the Avatar state to Shooting. Alter Avatar.StatBlock.mp.

To have access to activate(), MenuActionBar needs a pointer to Powers. Same with MenuInventory and MenuPowers, to allow right-click use. Powers will have its own set of sound effects. Powers will have a public Hazard (or queue of Hazards) so that the HazardManager can pick those up. Powers needs access to Avatar.StatBlock to change various stats. I think that’s it.

What about Powers generated by enemies? I think this is basically handled similarly. Powers needs a link to the Enemy.StatBlock (in fact, activate() should probably take StatBlock as a parameter). Hazards have damage sources so that’s taken care of.

Where are buff durations, etc. stored? In the StatBlock.

Where are animations and graphics for Powers stored? Probably in the Powers class. When getting renderables for Hazards we’ll need to read some info from Hazard and some from Powers. When getting renderables for Buffs/Debuffs we’ll need some info from StatBlock and some from Powers.

Powers could be stored in a powers.txt file.

[power]
id=1
type=basic_ranged #defines tells activate() which 
name=Shoot
requirement=po,1 #places this power in the skill tree
state=shoot
description=Basic Range Attack

[power]
id=2
type=basic_melee
name=Swing
requirement=pd,1 #places this power in the skill tree
state=swing
description=Basic Melee Attack
value=p # damage is based on the equipped Physical weapon

[power]
id=8
type=cast_heal
name=Heal
requirement=md,3 #places this power in the skill tree
state=cast
description=Restore health
sfx=heal
gfx=heal
value=m # healed amount is based on the equipped Magical weapon

[power]
id=100
type=potion_hp
sfx=potion
name=Health Potion
description=Restore 100% Health
value=max_hp # healed amount is the drinker's max hp

When a power is used that requires the user to change state (e.g. melee swing), the power (often, hazard) activates on a specific frame of that animation. The action frame should be a parameter in the animation part of StatBlock.

Where are the power animations defined? perhaps a power_animations.txt in the powers folder.

2010/09/08

Why?

I want to write a long overdue post about why I’m making OSARE. So many thoughts churning through my head that I need to commit them to paper so I have room to think again.

I turned 30 this year. 30 has this way of making me feel both very old and very young at the same time. I’ve been a gamer for 27 years and a programmer for 18 (professionally for about 10). The peak of my gaming and coding was during the 90s, when I had summers of nothing to do but tinker with code or play RPGs. Also during the 90s was the height of 2D gaming — beautiful pixel art or pre-rendered sprites seemed to push what we thought possible in games. The first generation of 3D games in that era looked primitive by comparison.

Also during the 90s is when I created and finished games. One game had a very primitive Bards-Tale style view and turn-based combat. Another had Gauntlet-style action and a nice map editor. Since then I’ve started and discarded countless new engines.

Typically the engines I create are of two genres: overhead or isometric RPG (action or turn-based), or side-scrolling platformer. These two genres represent my absolute favorite style of games at the height of 2D gaming. Even now when a Final Fantasy Tactics or Castlevania game comes out for the DS I’m there on release day.

One constraint I had for the longest time is creating art. I did poor quality pixel art for most of my projects. I started tinkering with Blender in 2002 but only in the last couple years have I finally learned enough to make game-quality art. I’m still learning about making polished 3D art, but my skills are enough that I can make pre-rendered 2D sprites that look similar quality to games of the 90s.

Of my favorite genre of games, some are well represented in Open Source. There are already plenty of platformers. There are plenty of console-style and turn-based RPGs. There aren’t many prominent action RPGs — surprising given the immense popularity of Diablo.

So I set off to make an isometric action RPG in the spirit of Diablo. I started it as a Java Applet, but quickly realized I didn’t want to be streaming megs of content every time someone launched the game. I chose C++ because it’s still the primary language of non-casual console and PC games. I chose SDL because I’ve worked with it before and found it does what I need it to do.

It’s important for me not to take on a project too big to complete. MMOs are fantastic but take dozens of people years to make. 3D games have their charm but my heart is in the simplicity of 2D gaming. I even cut out networking support for now because a multiplayer game would be a larger undertaking than I’m capable of.

If each game takes 2-5 years to create, a game developer might not finish many games in his/her lifetime. I would love to make a platformer engine but they’ve been done (I’ll probably save my ninja game for later and create it using the Frogatto engine).

It’s an intersection of many opportunities for me. I have the programming and art skill to put together a good 2D game. There isn’t a prominent open source 2D action RPG in existence. I have a good day job that I can leave at the office when I’m done for the day. OSARE begs to be made and I’m having a grand time making it.

2010/09/06

What’s Next for OSARE?

I pushed a lot of new code out this last week. It feels pretty good to sit back and just play the game for a while. I have a hero up to level 6 wearing mostly Warlord (+health) gear. The Skeletal Warriors (level 5) are no longer a real threat. The challenge and fun both feel pretty good so far.

Next up is a major update for gameplay: Action Bar and basic Powers.

The action bar needs to be customizable with Powers (from the Powers menu) and consumables (food/potions from the Powers menu). Essentially all these can be lumped under Powers. Items “effect” entries are essentially item-bound Powers.

So I need a Powers class that handles all the one-off code to deal with all possible powers. Powers each need an icon. Many powers will need data to create Hazards. Instead of planning out all the data I need for these, I probably need to start with simple examples (potions, bow shooting) and figure out what I need just for those.

Some powers will be much harder to implement than others. In particular, the Multishot (fire 3 arrows) and Freeze (sliding ground ice shards) will be tricky. Those are special cases in that they have multiple renderables and need to be balanced so they don’t hurt the same creature multiple times.

When the basic Action Bar, bow/slingshot shooting, and Shock spells are done I will probably pack that up and call it v0.10. At least by then someone who wants to wield Bows and Staffs will have a reason to do so.

2010/09/05

OSARE v0.09 Released

A fast release this time! Less than a week since v0.08, I’ve been having lots of fun coding updates.

  • Games are automatically saved upon exit.
  • Level Up system in place!
  • You must meet requirements to equip items now
  • Now you can respawn after dying
  • Gold added to loot drops! Though nothing to spend it on yet…
  • New map “Averguard Complex” added!

Note that bows and spells still don’t yet work. These will be coming up in v0.10! So for now, you’ll want to upgrade Physical and Defense skills, as the others won’t help you much.