Wednesday, July 11, 2012

EPI #16: Battles, part 2

More about Battles!  Last time I talked about Spells, and while spell-flinging is exciting and accounts for most of the action in battles, there's a lot more that I need to talk about.  So I'll start with Monsters and PlayerCharacters, the two kinds of things that Spells act upon.  class Monster:
  • Monster
    • art - A string containing the path to the sprite to display in battle.
    • statistics - a dictionary. {'stat_1:'val_1', 'stat_2':'val_2', ...}
    • moveset - a list containing the Spell.ids that the Monster is allowed to use.
    • pixbuf - a gtk.gdk.Pixbuf of the image indicated by Monster.art
    • resolution - an OrderedPair
    • pick_move() - chooses a spell to cast (at random, currently)
    • pick_target( targets ) - selects a target for the spell (at random, currently)
Pretty standard stuff.  These are so standard, in fact, that they should probably be related to PlayerCharacters via a common superclass somehow, but they're not.  This is a flaw in my class design, because it means that I'm not reusing as much code as I could be.  class PlayerCharacter:
  • PlayerCharacter(MapEntity)
    • statistics - Same as Monster.statistics.
    • equipment_slots - a dictionary. {'slot_1':Item, 'slot_2':Item, ...}
    • default_equipment - a dictionary {'slot_1':Spell, 'slot_2':Spell, ...}
    • spellbook - Same as Monster.moveset.
    • party - A reference to the Party to which the PlayerCharacter belongs.
    • battle_art - Same as Monster.art.
    • battle_pixbuf - Same as Monster.pixbuf.
    • battle_resolution - Same as Monster.resolution.
    • make_option_list() - Creates a dictionary of dictionaries representing the options a player has to choose from when acting in battle.  Right now, this is constructed from spellbook, equipment_slots, and default_equipment.
The dictionary returned by make_option_list() is passed to a MenuDrawer, which is like a TextDrawer, except it allows the user to select a value.  I'm not going to go into too much detail about MenuDrawers because they're not that interesting.  I haven't implemented Items yet, but the idea is to have players choose a specific Item to use each round in combat rather than generic verbs like "Attack" or "Defend".  make_option_list() loops over the equipment_slots making a list of all the useable Items therein, or, if an equipment slot is None, it uses the same key to see if there's a Spell in default_equipment.  If there is, it adds that to the list, too.  PlayerCharacters are defined in global.xml like so:

<playercharacter id='0' name='Kenfold'>
     <path type='map_sprite' direction='ALL'>./content/test/sprites/kenfold.png</path>
     <path type='battle_sprite'>./content/test/sprites/kenfold_battle.png</path>
     <statistic name='move_power' default='0' />
     <statistic name='attack_power' default='10' />
     <statistic name='hit_points' default='25' />
     ...
     <equipment_slot name='head' />
     <equipment_slot name='left_hand' default='5' />
     ...
     <spellbook>4</spellbook>
</playercharacter>

Looking at this, I realize I've forgotten to specify maximum values for statistics.  That is, Kenfold has default hit_points of 25, but there's no statement about how many maximum hit_points he can have.  This is also an example of how default equipment works.  If the PlayerCharacter has nothing equipped in his left_hand, he can still cast Spell id=5, which is "Unarmed" in my test scenario.  There are no technical barriers preventing me from adding a default to the head equipment_slot, either.  If I wanted to, I could give Kenfold a headbutt Spell there by default.

Monsters look much the same as PlayerCharacters in their XML, except instead of a spellbook, they have a moveset.  They're also defined in bestiary.xml instead of global.xml.  A quick example:

<monster id='1' name='Bearwolf'>
     <path type='battle_sprite'>./content/test/sprites/bearwolf.png</path>
     <statistic attack_power='15' />
     <statistic hit_points='35' />
     ...
     <moveset>1,2,3</moveset>
</monster>

Nothing new here.

BattleWidgets and Battles work together much in the same way that MapWidgets and Maps do.  Maps and Battles are responsible for keeping track of the actual game data, while their MapWidget and BattleWidget counterparts adjust their Maps and Battles according to input and represent them on the screen appropriately.  Here's Kenfold mid-battle:


It's not... riveting.  Not yet, anyways.  You can probably tell that, although the battle algorithm implementation is functional, there's still a lot of work to be done.  One thing that would have an immediate positive effect would be to create battle scene (background) art, and render it to the screen based on where (what part of what Map) the Battle is happening.  It would also be nice to take care of Items, Buffs, and battle rewards (think experience and/or gold).  I also need to implement random encounters, as well as test multi-Monster and multi-PlayerCharacter Battles.  I'll take care of one or more of those things tomorrow.

Hopefully the last couple of blog posts have given you a general idea about how Battles work.  I'll go into more detail next time about the single most important function call in Battles: Spell.eval().

No comments:

Post a Comment