Cast a Deadly Spell

Long long hiatus indeed… Life’s been keeping me busy lately, namely a wonderful awesome girl. 🙂 I’m turning my attention back to the CRPG…

When I last left off, I was doing a lot of prep work for spell casting in combat. I had to redesign the damage system to account for non-damage effects, compress effects code, redo a lot of my spell data, plug in test values for every spell to experiment with… you get the idea.

Now, it’s time to start actually writing the interface! For the record, combat in general has NOT been thoroughly tested in all edge cases… There’s going to be a lot of QA work involved there later.

I first fired up the current code and tried casting a spell. I found several glitches in the interface I had to fix, and I changed my basic spell data so I could validate that it was grabbing the right spells and populating the lists correctly. I have to be careful here because in game, there’s a theoretical maximum of 16 spells in a spellbook… so I don’t want to exceed that because it will overwrite other areas of memory. I still have some interface bugs to fix (Fireball wanted to target party members?) but I’m feeling good about the setup.

Naturally, when I looked at my code for spells actually doing something,I found to my amusement that I had NOTHING written in the action section… no wire-up at all. This actually reminds me of a recent interview that Richard Garriott (a.k.a. “Lord British”, the creator of the Ultima CRPG series) conducted where he admitted that he discovered AFTER he had released Ultima III that he had completely forgotten to write the code to wire up weapon damage to weapons equipped. He didn’t notice (as the ONLY tester) because the characters level also affected hit and damage and it seemed balanced enough. So if you play Ultima III, don’t bother equipping anything but slings or bare hands. 🙂

My next task will be making sure spell types and the necessary targeting interfaces are correctly wired, and then start writing the code to actually make spells happen and calculate effects AND display the FX. I probably won’t get this done before the end of the year… another New Year’s Resolution uncompleted! Oh well…

Posted in Blog, Coding, CRPG, Design, Gaming, RPG, TI-99/4a | 1 Comment

Cause And Effect

So, I finished up the changes to my effects code… although it remains to plug it in and integrate it with the main code base! I thought I would take this opportunity to show what was done and why…

Effects in the CRPG are, essentially, things that alter the status of a player, a monster, or the party in itself. This includes dealing wound damage, setting a counter for a spell effect, increasing the party’s rations, and so forth. There are around 40 effects in the game in total.

Originally, the code I wrote to process the effects was linear; each effect was a separate routine, and it used a passed register for the “address” to update, as well as values in a few extra registers. (For party effects, no passed register is needed, since there’s only one party ever.) Here’s a sample of how it looked:

* Effects subroutine
* R0 = Variable one
* R3 = Variable two
* R2 = STATE location pointer (player or monster)
* R1 = Effect
EFFCTS MOV  R11,*R10+
       SLA  R1,1
       MOV  @EFCASE(R1),R4
       B    *R4

* Reduces R0 fatigue
EF1    AI   R2,4
* Reduces R0 wounds
EF0    S    R0,*R2
       JGT  EF0A
       CLR  *R2
EF0A   B    @SUBRET

* Inflicts R0 fatigue
EF3    AI   R2,4
* Inflicts R0 wounds
EF2    A    R0,*R2
       B    @SUBRET

* Effects case array
EFCASE DATA EF0,EF1,EF2,EF3,EF4,EF5,EF6,EF7
       DATA EF8,EF9,EF10,EF11,EF12,EF13,EF14,EF15
       DATA EF16,EF17,EF18,EF19,EF20,EF21,EF22,EF23
       DATA EF24,EF25,EF26,EF27,EF28,EF29,EF30,EF31
       DATA EF32,EF33,EF34

So what’s the problem with that? Nothing… except that by the end, after crunching all the routines together along with the large reference array for each function (EFCASE), the routines take up around 426 bytes of space. Not a HUGE amount of space, even in a 32k system, but fairly significant. It also is hard to expand and maintain; I’ve had to revise effects multiple times and every time has been a hassle.

So, my new approach was to create an effects processing method. First, it’s in a separate workspace so that the registers can’t be corrupted by prior called routines. Second, all of the uniqueness of each effects is defined in a data structure, not as logic checks inside linear code. There’s more overhead writing a more “generic” processing system, but if your data is tight enough you eventually get more out of it.

The new code looks like this:

* Effects subroutine
*
* Inputs
* 
* @WORK - Effect #
* @WORK+2 - Array location for player/monster
* @WORK+4 - Value #1
* @WORK+5 = Value #2 (or #1 overflow)
EFFCTS DATA AWS,EFC0
EFC0   LI   R0,EFCDAT                  * Get effects data into R0
       LI   R9,60
EFC1   CB   @WORK,*R0                  * Check if current effect is in record
       JNE  EFC4                       * If not, jump to loop
       INC  R0                         * Increment R0 to type byte
       MOVB *R0+,R1                    * Copy type byte to R1
       MOV  R1,R2                      * Copy type byte to R2
       MOV  R1,R3                      * Copy type byte to R3
       ANDI R2,>1000                   * Get player/party bit
       JNE  EFC2
       MOV  @WORK+2,R4                 * Copy player/monster state to R4
       JMP  EFC3
EFC2   LI   R4,PARTY                   * Copy party state to R4
EFC3   AB   *R0+,@>8389                * Add Offset to R4 low-byte
       ANDI R3,>2000                   * Get byte/word bit
       ANDI R1,>0F00
       SRL  R1,8
       MOV  @EFCASE(R1),R2
       B    *R2
       JMP  EFC5
EFC4   AI   R0,4                       * Move to next record
EFC5   DEC  R9                         * Decrease effect count
       JNE  EFC1
EFC6   RTWP

* Effect "is zero"
EFZERO INC  R0                         * Set R0 to next record
       CB   @ZERO,*R4                  * Check if target value is zero
       JEQ  EFC5                       * If so, go to next record
       JMP  EFC4                       * Otherwise skip next record

* Effects add
EFADD  INC  R0                         * Set R0 to next record
       MOV  R3,R3                      * Word or byte operation?
       JEQ  EFADD1
       A    @WORK+4,*R4                * Add word value
       JMP  EFC5
EFADD1 AB   @WORK+4,*R4                * Add byte value
       JMP  EFC5

* Effects subtract
EFSUB  INC  R0                         * Set R0 to next record
       MOV  R3,R3                      * Word or byte operation?
       JEQ  EFSUB1
       S    @WORK+4,*R4                * Subtract word value
       JLT  EFSUB3                     * If negative, clear to 0
       JMP  EFC5
EFSUB1 SB   @WORK+4,*R4                * Subtract byte value
       JLT  EFSUB2                     * If negative, clear to 0
       JMP  EFC5
EFSUB2 MOVB @ZERO,*R4                  * Set to 0
       JMP  EFC5
EFSUB3 CLR  *R4                        * Set to 0
       JMP  EFC5

* Effect set
EFSET  INC  R0                         * Set R0 to next record
       MOV  R3,R3                      * Word or byte operation?
       JEQ  EFADD1
       MOV  @WORK+4,*R4                * Set word value
       JMP  EFC5
EFSET1 MOVB @WORK+4,*R4                * Set byte value
       JMP  EFC5

* Effect clear
EFCLR  MOVB *R0+,R3
       SRL  R3,8                       * Set R3 to count of bytes to clear
EFCLR1 MOVB @ZERO,*R4+                 * Clear location
       DEC  R3
       JNE  EFCLR1
       JMP  EFC5

* Effects cases
EFCASE DATA EFADD,EFSUB,EFSET,EFCLR,EFZERO

* Effects data
EFCDAT DATA >0022,>0000
       DATA >0122,>0400
       DATA >0220,>0000
       DATA >0320,>0400
       DATA >0424,>0000
       DATA >0522,>0000
       DATA >0522,>0400
       DATA >0632,>1C00
       DATA >0730,>1C00
       DATA >0802,>0F00
       DATA >0902,>1300
       DATA >0902,>1400
       DATA >0902,>1600
       DATA >0A06,>1E01
       DATA >0B06,>1F01
       DATA >0C06,>0816
       DATA >0D14,>0700
       DATA >0D10,>1401
       DATA >0E10,>1500
       DATA >0F10,>1600
       DATA >1010,>1700
       DATA >1100,>0800
       DATA >1106,>0901
       DATA >1200,>0900
       DATA >1206,>0801
       DATA >1300,>0A00
       DATA >1306,>0B01
       DATA >1400,>0B00
       DATA >1406,>0A01
       DATA >1500,>0C00
       DATA >1506,>0D01
       DATA >1600,>0D00
       DATA >1606,>0C01
       DATA >1700,>0E00
       DATA >1706,>0F01
       DATA >1800,>0F00
       DATA >1806,>0E01
       DATA >1900,>1200
       DATA >1906,>1301
       DATA >1A00,>1300
       DATA >1A06,>1201
       DATA >1B00,>1400
       DATA >1C08,>1C00
       DATA >1C00,>1500
       DATA >1D08,>1C00
       DATA >1D00,>1600
       DATA >1E00,>1700
       DATA >1F00,>1800
       DATA >2008,>1C00
       DATA >2000,>1900
       DATA >2100,>1A00
       DATA >2200,>1B00
       DATA >2300,>1C00
       DATA >2408,>1000
       DATA >2400,>1100
       DATA >2508,>1100
       DATA >2500,>1000
       DATA >2600,>1D00
       DATA >2700,>1E00
       DATA >2800,>1F00

Each effect is defined with a one or more 4-byte records. The first byte indicates which effect it applies to. The second is the type (adding, subtracting, checking for zero, etc.), whether to do word or byte operations, and whether it’s a player/monster or party change. The last two bytes are used for values or ranges to change, depending on the type.

The entire effects array is parsed each time a new effect is done. That is so every case is checked and every record potentially ran. In BASIC this would be a terrible waste, but for assembly language this is fairly fast. Given that the most effects calculated at any time is under a dozen, even a human user wouldn’t notice it taking much longer than a split second.

I haven’t crunched the numbers yet, but my initial calculations were very favorable for the size of the new Effects system… The data table is 240 bytes, and the instructions add around another 128 or so… It may not be much smaller than the original, but it’s VERY easy for me to maintain and add new records.

I also need to integrate it better into the combat system itself. My attack routines for melee and ranged are adding wounds directly; they should use the effects system instead. That will allow me to substitute in different effects, such as spells, within the same framework.

Posted in Coding, CRPG, TI-99/4a | 1 Comment

Hiatus Hell

Been quiet awhile… I’ve had a lot of stuff going on in my real life, so the game has dropped by the wayside.

In particular, I’m going through a divorce. It was a short marriage, and quite frankly a serious mistake, but dealing with the legal and bureaucratic nonsense is enough to drain anyone of their energy. On the plus side, the house is much more quiet and easy to work on vintage gaming in. 🙂

About picking back up on the game… I was in the midst of getting the spell FX off the ground and re-writing the effects code. I also got back into World of Warcraft, though, and it’s a definite truth you can’t both play and write games at the same time.

I think I was in the middle of trying to convert the effects system to a data-driven routine… Time to blow off the dust and get the gears grinding again!

Posted in Blog, CRPG, Personal | 6 Comments

Elevated Expectations

And back to CRPG stuff…

I was at a remote forested area which had a beautiful viewpoint from a forested hill of a waterfall across from it. I realized such an awesome view would be impossible to do in my current engine because the forests would automatically block your sight. That got me thinking about the concept of elevation in my CRPG.

If every tile on a given map had an elevation value, you could automatically consider anything lower than the current location’s elevation as view-able, and anything higher as not view-able. That way you could create canyons and depressions that limit visibility while hills or lookout points improve it! I’d only need to use about 3-bits (0-7) to get a good amount of range too. Tiles like forests would continue to block line of sight at the same elevation, unless you were above it.

As I pondered the implementations of it, I realized I had one small problem; I’d need more memory to support an elevation map in the game. Right now I have a 4k buffer for maps; I would need to either shrink it to support an elevation map, or find more memory somewhere. (I’m thinking I could use about 3k for tiles and 1.5k for elevation/light mapping… I’m sure I can dig up 512 bytes somewhere.)

Why don’t I compress the maps with something like RLE (Run Length Encoding)? Well, that’s mainly to save disk space. You still have to decompress into your local memory. And disk space isn’t an issue with this game; a primary difference from developing vintage games in the modern era is that you don’t have the concerns of manufacturing and packaging floppy disks. So you can use the biggest disks available without fear.

For now, I’m marking this idea as a P2 (Priority Two, nice to have but not strictly necessary) and shelving it. I really want to finish the combat system before I return to travel mode. And I should get the quest system and other elements done before I go around adding new things to other modes.

Combat engine work is still mostly idle; I’ve been trying to complete my spreadsheet of FX data before I go any further. This is so every spell has a combat effect that I can test and make adjustments to, as needed.

The way I handle FX in the game is essentially an engine that processes commands in 8-byte segments. There are commands to create sprites of a given pattern/color and animation style, move sprites from one point to another, create a circle, cone, or line effect, and so forth. But I have to have data to test.

One big element that will require a lot of testing is the timing. For several of my effects, I provide for a delay count so that the engine knows when to stop and move on to the next effect (if any). Sound effects are handled by the interrupts, which means they can’t really be used to time things. So I’ll need to try effects, make a judgement call on how well they look, and make adjustments as needed.

On the subject of sound effects, I’ve been doing a little redesign there too. I went back to the Tunnels of Doom sound effects to borrow a few (the monster ones are particularly good for negative effects) and I’m always impressed by them. The sounds are VERY small, only taking around 19-21 bytes of space each. Each seems to follow a pattern of three different tones, but also use the noise generator and different duration to achieve an effect. I’d like to go back and redo some of my positive sound effects (healing, curing, etc.) to match the same pattern, but that will take awhile and I have stock sounds I can use in the meantime.

Right now, my list of tasks is as follows:

  1. Fill in FX for all combat-oriented spells. Use placeholders if need be for some.
  2. Expand the action array to include direction of attack and other elements. I originally was trying to squeeze all action data into a single word (16 bytes), but now that I’m not processing sets of actions anymore, I can make this more informative and easier to use
  3. Fully split damage out from attack. My initial code had them closely tied together; this won’t work for area attacks, which ignore defense rolls. It also needs to consider effects, like positive/negative states.
  4. Make sure that area attacks that affect multiple units will process against all affected units and locations. I’m pretty sure this is set up, but it hasn’t been tested, and I may have biased it towards one unit while doing ranged attacks.
  5. Look into whether or not I can condense and data-orient the effects routine. Right now it’s 426 bytes of code that just changes a state array directly with addresses and instructions. Writing a data array structure that can do everything could be tricky though.

Plus I have two bugs to fix:

  • When the wizard uses a wand, the wand effect pattern doesn’t appear to be changing orientation, as opposed to the arrows/quarrels used by the other party members. Could be a data issue?
  • Some shots at particular angles are traveling well past screen boundaries and looping around. Smells like an overflow math issue with calculating the slope.
Posted in Coding, CRPG, Design, TI-99/4a | 8 Comments

Review: When a Star Falls

Time to take a short side trip in my blog to review a product…

“When a Star Falls”, UK4, was the first Dungeons & Dragons module I ever purchased. It was back around 1984, when book stores were selling D&D products and occasionally dice.

I bought it because it was for levels 3-5, a low-level module. I was still playing the box set of D&D at the time, and I didn’t realize the module was for Advanced Dungeons & Dragons. Given that at age 9 I could barely follow the regular D&D rules, Advanced was quite out of my means.

As I got older, though, I began to realize what a treasure I had accidentally purchased. The UK series of D&D modules were some of the best ever written. Most of them featured intricate plots and clever adventures that were far more than just a dungeon map with a list of encounters. It’s unfortunate that TSR UK was eventually closed down in the mid-80’s. The lead writer, Graeme Morris, actually left the gaming hobby entirely and his whereabouts are unknown.

So, on to the module…

The first bit of awesomeness about this module is how it begins. While traveling across a moor, the party comes across several dead monks, encased in a strange web. The web is a living entity that absorbs memories. After killing it, the memory web releases all its stored memories, which imprint themselves into the characters. There are over a dozen memories, described both with a few lines of dialogue AND an illustration.

Oh yeah, the illustrations are incredible as well. The artist, Jeremy Goodwin, creates some incredible scenery and sinister characters with an asymmetrical style that really stands out from the U.S. D&D modules. Even his black and white illustrations rock. If I could find him and he was still a commercial artist, I would SERIOUSLY consider hiring him to do illustrations for my CRPG’s manuals.

Anyway, the memories give the players the information they need. A star fell from the skies (a meteor) in the region, and the monks were on a journey to take a valuable tome to a sage, who would be able to tell them where it fell. The monks were going to retrieve the star for their master, the sage and oracle Shalfey, who resides at the Tower of the Heavens.

During their travels, the players also have an unwelcome visitor, the monk Sion, who serves Shalfey’s principal apprentice, Piyarz. (I also LOVE the names…) Sion has undergone a terrible ritual to become a shade, a half-shadow man that gains strength in shadows and is effectively immortal. He stalks the party, attempting to learn their intentions and, if need be, slay them.

The sage Derwyth tells the party that the star has fallen to the north along the river. You eventually discover that it has fallen directly on the lair of a tribe of derro, small blue-skinned creatures of the Underdark known for their sadistic cruelty and insanity. (The Underdark was still two years away from being introduced into D&D at this point.) Most of the derro were killed in the blast, but the survivors excavated the fallen meteor to examine it. The party must enter what remains of their lair and retrieve it.

After this, the party travels to the Tower of the Heavens. If they head north up-river, they have an interesting non-combat encounter. Tribal hunters and fishermen live on a lake there, but they’re isolationist and paranoid. Numerous derro raids have left them suspicious and hostile towards strangers. The party has to be diplomatic and careful to convince them to allow them to cross the lake. At one point in the village, while being hustled through by spearmen, a child screams from inside one of the huts, causing the party’s escort to panic and attack them. If the party manages to keep their temper, the mistake is discovered and all is well. These kind of quandaries for players to solve (preferably with no violence) are a welcome change from dungeon crawls filled with no ethical decisions to make.

At the Tower of the Heavens, the players discover the truth. Shalfey is being held prisoner by Piyarz and the other lesser sages. None of the other tower residents, including the gnomish mercenaries who guard it, know this. Piyarz desires to kill Shalfey and take over the tower, but Shalfey locked himself into the upper tower and threatened to destroy the books of prophecy, which are needed to see the future.

Again, the players face an ethical dilemma. While the lesser sages, Piyarz, and their monk guards are all fair game, the gnomish guard and the regular tower residents are all innocent and unaware. A party who charges in, slaughtering everything, will neither get very far (they’re SORELY out-numbered) or accomplish anything heroic. Pretending to be supplicants for an oracle reading and then sneaking in is the best route.

When the players deal with Piyarz and his minions, they learn the truth from Shalfey. The books of prophecy (which he just burned prior to the players coming in) were blank! Their knowledge of the future ran out, and the last clue given was that a fallen star must be taken to the nearby hills where a group of “dark smiths with a fabled library” live and exchange the meteor for a new set of books. So one final mission remains.

The dark smiths are a group of Svirfnebli, underground gnomes, who found a prismatic sphere guarding a set of books and decided to build their forge and base around it. While not evil, the inference is that the smiths are anything BUT good. They have built a number of automated minions called Maschin-I-Bozorgs that look like giant  frisbees that fire darts and jets of steam. The party has to fight some before the Kagu-Svirfnebli will talk to them, and take the fallen star in exchange for the new books of prophecy. The star dispels the prismatic sphere guarding the books.

One last trick remains, though. The Svirfnebli leave the forge to return to their home in the Underdark, setting off the “self-destruct” button on the way out. The party must flee the cave before it collapses. When they reach outside, they find two red dragons waiting for them, allies of the Svirfnebli who are suspicious at the destruction of their home and angry at losing a source of treasure and food. This is the one part of the adventure I don’t like; aside from the meanness of dropping two major opponents on the party at a vulnerable time, and feeling like a cliche ending (you have to drop dragons into it, seriously?), the worst is that dragons are SOLITARY creatures and would never pair up for any long period of time.

The ending of the adventure leaves the party with questions. What was the fallen star, exactly? What are the intentions of the Kagu-Svirfnebli? Will they make a weapon of evil destruction from it? The great thing about this adventure is how it leaves an open thread for future endeavors.

The adventure recently became available on D&D Classics as a PDF. You can download it here: http://www.dndclassics.com/product/17075/UK4-When-a-Star-Falls-%281e%29?it=1

Posted in Gaming, Review, RPG | 1 Comment

Animated Changes

I’m still working on content changes right now… graphics, sound effects, actual FX for all the spells… a lot of work to do before I go back to coding up things.

One aside was to update my animation code. I’ve had the same block of code in place for several years, and it’s functioned adequately. But it’s hard to expand, as it’s really just a set of linear instructions for each character block. Some sample code is this:

* Tile animation
ANIME  DATA AWS,AM1
AM1    MOV  @CLOCK,R0
       ANDI R0,>0003
       JEQ  AM15
       RTWP
AM15   LI   R0,>2468
       LI   R1,HIBUFF
       LI   R2,40
       BLWP @VMBR
       LI   R1,32
       BL   @ANORTH
       C    @CHAR,@W1
       JLE  AM16
       CLR  R1
       BL   @ANORTH
       LI   R1,8
       BL   @ANORTH
       LI   R1,16
       BL   @ANORTH
       LI   R1,24
       BL   @ANORTH
AM16   LI   R0,>2468
       LI   R1,HIBUFF
       LI   R2,40
       BLWP @VMBW
       LI   R0,>0400
       LI   R1,HIBUFF
       LI   R2,104
       BLWP @VMBR
       LI   R1,64
       BL   @ASOUTH
       LI   R1,72
       BL   @AWEST
       LI   R1,80
       BL   @ANORTH
       LI   R1,88
       BL   @AEAST
       MOV  @CLOCK,R1
       CLR  R0
       DIV  @W16,R0
       MOV  R1,R1
       JEQ  AM2
AMEND  B    @AM7
AM2    MOV  R0,R7
       ANDI R7,>0003
       LI   R1,24
       BL   @AROT
       LI   R1,32
       BL   @ASOUTH
       LI   R1,40
       BL   @AWEST
       LI   R1,48
       BL   @ANORTH
       LI   R1,56
       BL   @AEAST
       CI   R7,3
       JNE  AM25
       CLR  R1
       BL   @ASOUTH
       BL   @AEAST
       LI   R1,8
       BL   @ASOUTH
       BL   @AEAST
       JMP  AM3

This kind of imperative linear approach is all right, if you don’t have a lot of things to do. Truthfully, in assembly language, you often have to balance whether or not a more generic data-driven approach wouldn’t just end up costing you more memory than its worth.

In my case, my animation requirements DID get up complicated enough I wanted to get away from this approach. I also wanted it to be easy to add new patterns or alter exiting ones (and their animation speed) with a simple change.

Here’s some of the new code, which uses a data array to store the VDP address, size, and speed and type of animation. It’s then processed in a short loop:

* Tile animation
ANIME  DATA AWS,AM1
AM1    MOV  @>8314,R10                 * Copy stack address to R10
       MOV  @CLOCK,R0
       ANDI R0,>0003                   * Check clock value for 1/15 interval
       JEQ  AM2                        * If not, skip animation
       RTWP
AM2    LI   R8,ANDAT0
       BL   @ANIMAT
       C    @GSTATE,@W2                * In combat mode?
       JNE  AM4
       LI   R8,ANDAT2
       BL   @ANIMAT
AM3    RTWP
AM4    C    @CHAR,@W2                  * Is character set 3-7?
       JLE  AM3
       LI   R8,ANDAT1
       BL   @ANIMAT
       JMP  AM3       

ANIMAT MOV  R11,*R10+                  * Animation looping for blocks
       MOV  R8,R9
       MOV  *R9+,R0                    * Load character data 
       LI   R1,HIBUFF
       MOV  *R9+,R2
       BLWP @VMBR
       CLR  R1                         * Clear pattern index
       SRL  R2,3                       * Get proper count into R2
       MOV  R2,R7
ANM1   MOV  @CLOCK,R3                  * Get clock value into R3
       CLR  R2
       DIV  *R9+,R2                    * Divide by speed factor
       MOV  *R9+,R0                    * Get animation routine into R0
       MOV  R3,R3
       JNE  ANM2                       * If not at interval, skip
       BL   *R0                        * Branch to animation routine
ANM2   AI   R1,8                       * Increment to next character
       DEC  R7
       JNE  ANM1
       MOV  *R8+,R0                    * Write character data 
       LI   R1,HIBUFF
       MOV  *R8+,R2
       BLWP @VMBW
       B    @SUBRET

* Pattern animation data for travel/combat mode 0-6 (56 bytes)
ANDAT0 DATA >0400,104
       DATA 16,ACIRC
       DATA 16,ACIRC
       DATA 16,AUPDW
       DATA 8,AROT
       DATA 8,ASOUTH
       DATA 8,AWEST
       DATA 8,ANORTH
       DATA 8,AEAST
       DATA 4,ASOUTH
       DATA 4,AWEST
       DATA 4,ANORTH
       DATA 4,AEAST
       DATA 32,ACIRC

My old approach was using around 436 bytes of code space. With the data arrays and the smaller routines, my new system uses about 100 less bytes, and is easier to use. On screen, speed seems unimpaired as well. Huzzah!

Posted in Coding, CRPG, Design, TI-99/4a | Leave a comment

From my Video to my Radio…

Bonus points if you can identify the source of today’s blog entry title…

Camtasia is a FANTASTIC video recording software tool. My trial period will be up at the end of the month, but I’m definitely going to go ahead and buy the full version. (No, I don’t hack trials. If you like something, give the creators their fair due so they’ll make more.)

I’m still a bit off from creating spell effects, which should be pretty cool to see and make changes and adjustments to. For now, here’s a video of the dynamic battlemap loading and random placement of units on opposing sides:

Posted in Blog, CRPG, TI-99/4a, Video | 6 Comments

Tactical Position

It was a bit more work than expected, but battlemaps are now loading dynamically AND units are being placed dynamically as well.

The main problem I encountered was that my trigonometric functions were, well, not working right. I dug around and found a test program I’d written for them and discovered I had fixed the issues there, so I just had to port the code over into the CRPG source.

Experimentation with positions was a bit of work too. I wanted it to be intelligent when placing units. Your top two units are your “front-line” characters, so they should usually be the ones closest to your foes, while the others hang back. So I included a starting angle and two different coordinates for each side of the map. Interestingly enough, the second set of coordinates end up trying to place units off-map, which kicks in the directional placement, that scoots them back in on the back line. Wow, it’s like I designed it that way. 🙂

For ambushes, there’s only one coordinate at the center, and both sets of units are placed around it at a different radius. I haven’t written this code yet, since it requires monster A.I. to make sure that they get the first turn.

Now I’ll move on to the battle’s end, when treasure is determined and clean-up and updates are done prior to returning to travel mode. Eventually this will also involve updating data on disk; I do not want the game to have the same encounters (and treasure) in the same place. If there’s one thing I’ve learned from reading up on the CRPG Addict’s blog, it’s that CRPG’s should make you feel like your actions CHANGE the world.

Posted in Coding, CRPG, Design, Screenshots, TI-99/4a | Leave a comment

Battlemap Rebuild

As I continue working on the combat engine, finishing up other portions of “temporary” code has come up. One particular one is the use of battlemaps.

Most of the 2D CRPG tactical combat games feature some kind of varied terrain to conduct battles on. Ultima’s III-V all had them, one for each different terrain type. So obviously I intended to follow suit here.

But, since I have the disk space for it, I intended to go much further than any of those games. Ultima V only features 16 unique battle maps. My CRPG? Oh, probably around 300+.

I started battlemap design several years ago, when I was still working in the MESS emulator. I actually had to port my work off an old work disk to continue it, and update the tiles, which had changed a few times since I made the original file. Now I’ve gone one step further and made a separate battlemap set for each tile set. That way I can have as many as I need to make combats always potentially interesting.

One complication I had to correct was the loading of different tile sets. Originally I had only a single set for combat. Now I’ve expanded it so that there’s a tied-in tile set for each of the major travel types. That lets me have maximum character space to do interesting features. If you’re in a town battle, lamp posts, crates, barrels, may show up on the map. A dungeon has manacles, skeletons, and other macabre items. All fun!

An added bonus with my approach is that battlemaps will be alterable by player action. Spells exist that let you create barriers, area effects of damage spells can destroy the landscape, and so forth. I don’t think any classic CRPG offered this kind of feature, keeping most of the terrain fairly static.

Battlemaps are stored in two 128 byte records. The first 8 bytes are a compressed mini-map that indicates if the spaces are empty or occupied. The rest of the space stores the maps in RLE compression, using values lower than 128 to indicate a count of the next tile. My battlemap editor warns me if I exceed the record size. This usually happens if I get overzealous with spreading unique tiles around. (I don’t use RLE for the travel maps, because the high-byte is used to indicate lit/unlit squares on dark maps. No real need to compress those files anyway.)

One particular problem to solve is the placement of units on the battle map. I considered at first an array of common positions to just iterate through, but it was quickly adding up to a lot of bytes of data to store. So then I realized I could do this algorithmically with tools the code already has: the trigonometric functions.

Using that code, all I have to supply for the map is two things: a set of coordinates and a radius. Because I want units to spread out a bit, I actually supply two sets. The number of units is divided in half, and then each half is placed in a circle around that coordinate, with the angle being based on the number of units. (4 = 90 degrees, 3=120 degrees, and so forth.) For an ambush scenario, a single set of coordinates is used for both sides, with different radius’s, so one is surrounding the other.

What to do if the chosen space is occupied? Well, first it will check all adjacent squares to that square for any open spots. If it finds one it takes it. If all four cardinal directions have nothing, then I just default to selecting completely random coordinates until an empty space is found. This means there’s a potential for a stray unit to end up in some bizarre place, but hey, that’s the fog of war…

I wrote up the necessary code to do the positioning this afternoon on paper, now I just need to get it written in code and incorporated into the source. I may also need to refactor my trigonometric functions a bit; they were assuming whole word values and I’m pretty much exclusively in bytes, for sprites and coordinates.

Oh yeah, the trigonometric functions. No, I’m not using the ROM routines. They’re difficult to implement and use, plus I’d have to convert everything to Radix 100 floating point. Instead I use whole integers, with a base of 256, and stored the main 90 degree set as values 0-255 in a data table. Ratios are then calculated by multiplying the angle by the data table value and dividing by 256. It’s obviously nowhere near as accurate as a floating-point method, but for what I’m using it for, it works pretty well.

When I get battlemaps loading and positioning of units working, I will probably move on to getting combat ending debugged and sorted out. I’d like to have the end-to-end of it mostly done before I get into the long arduous process of spell FX.

Posted in Coding, CRPG, Design, Screenshots, TI-99/4a | 13 Comments

Checkmate

Let’s take a moment to look at the monster A.I…

Monster A.I. can be as simple as just calculating the deltas of the positions and making them into coordinate change values. My favorite version of this came from an old BASIC program that used the SGN function on the deltas to just make them 1, 0, or -1. That makes the enemy move closer to the target.

For my CRPG, of course, something more complex is needed. I still regret that I can’t have multiple monster types in a single combat… it would be great to have melee types who charge the players while archers and spellcasters rain destruction upon them from afar… but I don’t have the codespace or graphic space to support that. So I definitely want the A.I. to be a bit more deep than “move towards player unit. Attack until dead.”

The first part is how smart the monsters are. I’ve decided on four levels of intelligence:

  1. Dumb – No brain to speak of, always attacks the closest enemy
  2. Animal – As #1, but also considers past attacks for optimal targets
  3. Smart – As #2, but also calculates threat for each movement vector based on closeness of enemy and allied units and obstacles
  4. Really Smart – As #3, but also considers ranged and spellcasting by enemy units as aggressive actions

For each monster, there are a potential four counters for each player unit, which are used to track aggression. These start at 0 for the beginning of combat, and are added to each time a player unit has a successful attack on the monster unit. That weighs the monster in favor of attacking that unit over others.

There are two phases to each action by a monster unit, acquiring a target and determining an action to take.

Targeting uses the distance of each enemy unit as a baseline, subtracting it from a high value so that closer units have higher weight values. If the monster considers aggression as a factor, this is also added. Targets that it can’t see (such as invisible units) are not added to the array at all.

If the monster is confused, ALL units are targetable, and it just picks one at random, skipping over weight calculations. In the case of only a single target (a solo party), it just picks that target. In the case of no targets (a solo invisible target) then a target isn’t factored into movement, and no targeted actions (like ranged weapons or spells) are done.

The next phase is actions. A monster only has a maximum of seven actions:

  • Move up
  • Move down
  • Move left
  • Move right
  • Fire a ranged weapon
  • Use a special attack
  • Guard (ending turn)

Each action is added to a weight array. For movement, distance to the target is added again as a weight, so that the move that gets the monster closer to the target has the highest value. Ranged weapons have a higher weight value if distance is greater, and a random value is added to it just to keep things a little random.

I’m still considering special attacks. They are basically spells, which means they could be buffing spells, area attacks, and so forth. This requires a bit more calculation to determine when the monster uses it.

The guard action will have a higher value if the monster’s action points are low compared to the maximum. That should push the monster into ending his turn unless a particularly valuable action over-rides it. Like players, once action points hits 0 the turn ends, and any negative value is taken as fatigue damage.

For movement, if the monster is smart, he also considers threat levels. This means that the target square’s adjacent squares are considered. Obstacles like trees or boulders are good, they offer cover and reduce chances of being flanked by enemies. Likewise, allied units are good and add to the value. Enemy units, on the other hand, reduce the value. So does screen edges, because the monster doesn’t want to retreat. And magical effects like a fire wall or thorny patch will also reduce the value.

The nice thing with this approach is a simple switch to a different array allows me to simulate a fearful or retreating posture as well. In that instance, screen edges are VERY valuable, and enemy units close by make movement undesirable in that direction. Confusion will just mean the monster picks an action entirely at random.

This same A.I. is also used for players in a negative state, such as confusion, charm, or fear. This means the work I did to encapsulate actions into small consumable data words that could be passed into an engine is still useful, because I’ll need it for both the monsters and players.

The main pain of implementing the monster A.I. will be debugging it. Since a lot of these actions occur offscreen, I’ll have to set up some VERY specific test use cases to try out different scenarios, and then use the debugger to view the resulting action and make sure the logic chains are all working out. I expect the code hit will be hefty as well, to do all these calculations.

But it should be worth it to give the player a worthwhile and challenging tactical battle.

 

Posted in CRPG, Design, TI-99/4a | Leave a comment