Combat in action! I still need to re-implement ranged weapons, thrown weapons, and test all spells but looking good…
Combat in action! I still need to re-implement ranged weapons, thrown weapons, and test all spells but looking good…
A very happy Thanksgiving to everyone! I am always thankful that I have at least a few people paying attention to my project. Despite taking WAY too long on it. 🙂
I’ve been working on an FX tester recently. FX is a huge part of spellcasting in combat, and I need to be able to fine tune that code specifically in isolation so that I can make it perfect without having to hack the game.
There is a lot of aesthetics going into the design, so I need to be able to play with graphics, colors, and other factors quickly. My original “move sprite from A to B” code wasn’t quite perfect either, often over-shooting the mark. I’ve come up with a design that both allows different speeds by only opening interrupts to display sprite position changes at given intervals, and is also completely deterministic so they won’t “shoot past” their mark.
Hopefully I can get most of the engine work done and focus on content… I may have to start beta-testing with only the first 25% of the content complete.
So I’m definitely in alpha stage with the game engine.
For those of you unfamiliar with software, alpha testing is typically testing of the project with internal testers, and using both white and blackbox testing. At the moment, the sole project employee is me, so I’m doing all of it. 🙂
In the near future, I will have a beta test with a select group of people. I haven’t decided yet on who; feel free to post on this thread if you are interested!
Bear in mind though that being in a beta test means actual testing work, not just playing the game. I would expect bug reports and lots of constant feedback. If you’d rather just wait for the official release to enjoy the game, there’s nothing wrong with that.
Today, I had the unique pleasure of fixing a bug and then seeing several features I hadn’t tested yet work perfectly!
This particular combat, I can see my multiple monster generation system worked like a charm! The rats poisoned occasionally on melee attacks, and the monster AI was fairly good too.
I still have several combat bugs to fix. The fighter, for example, should not be able to cast spells… In fact, the whole spell system has to be fully tested.
I may have to write another side-tool engine to test the FX effects as well. I noticed while debugging the trap ones that there’s a LOT of “artistic adjustment” to make them look just right, and the best place to do that is in a separate tool so I can just cycle through FX and fine-tune them.
Despite not having a post for awhile, I’ve been very busy on the game! I’ve been updating the progress page with various bugs and issues. There’s a LOT of code to test and fix; in particular I know the FX code has a lot of issues to deal with.
It’s early to start on it, but I already made some adjustments to game mechanics. I found my first set of encounters the monsters were just a little TOO hard to drop quickly, and they’re supposed to be a nuisance not a full on battle. Hit ratio seems fairly low too; my fighter has swung and missed three times in a row, he should be a bit better than that.
One thing I ended up adding to the game because I had a new module for combat encounter generation and resolution is a complete “you found a chest” system for post-combat. I’m still working through bugs, but you can see it in action here:
I designed it so if a given character is chosen to open the chest, all the others take a step away from it. (Like ANY good adventuring party does.) I need to make sure they don’t move over obstacles like trees though. 🙂
I still need to test the item distribution system as well. The game checks if the player who you try to give the item to has a full backpack or not, offers the option to trade to another player, or discard an item from your backpack to make room. Lots of logic to debug!
Quick share of some screenshots of where I’m at…
So initially I got a nice video garbage screen when going to combat. Lots of bugs with functions not returning to the caller, workspace overlaps, and video buffer overruns. After getting a lot of that sorted out, I finally have the display showing up and it reaching a point of “user input”:
The initial encounter screen. This is mostly good. There is the lack of a plural on the monster name. (Yes, it was supposed to add an ‘s’ on the end.) And the position of the units isn’t correct; my party came from the left so they should be on the left and the monsters on the right.
Hmm, it’s missing the “Your turn guy, this weapon ready” line. And the commands look a little squished. And his actual CHARACTER is gone from the map and the sprite “blink” effect looks corrupted. But at least it’s at a stage where the character can do something.
One step (and debug) at a time…
Despite the lack of posts, I have not been idle…
After a lot of testing, I’m satisfied with my battlemap generator. I’ve populated the data table with some entries that are undefined (graphics sets in later parts of the game) but all the core graphic work is done. I still have other portions of the encounter engine to test, but I can do that in live running rather than in a test program.
The next step? Running the full game and getting into a fight. Then putting a breakpoint in the debugger to try and figure out where it crashed and why. Repeat repeat repeat… 🙂
Playing World of Warcraft, while a bit of a time-consumer, is also useful for thinking about game mechanics. A bit of a side bar on that…
Since I haven’t played since the Pandaria expansion, I’m enjoying a lot of the new ideas they’ve introduced, like garrison campaigns. The stat/level squishing that occurred shortly after Pandaria and more recently before Battle for Azeroth also helped to make the game stats less crazy; before your hit points were up in the 500k range! They actually ran into integer overflow problems with raid bosses; because the base code was still using 32-bit integers and the raid bosses hit points were set high enough to exceed the max positive. I’m noticing though in Azeroth that item levels and stat bonuses are getting out of control again…
I was worried about the level scaling at first, but it works pretty well. You can now do either Northrend or the Outlands from 60-80, and Pandaria or Cataclysm areas from 80-90. I’m certain they’ll be doing something similar for Draenor and Legion soon so you can play either area from 90-110, there’s more than enough content to do it and stick to one. Funny how Draenor suffers the same issues that Burning Crusade did; it’s a bit of a “side trek” to visit and players get bored and resentful having to play through the content with their alts. Legion content is VERY desirable to play through, as it’s the only way to unlock the new races to play.
However, I do NOT like the dungeons, and I haven’t for awhile. The original classic game tried to introduce some interesting story points and offered very non-linear designs. Modern dungeons (pretty much since the first expansion) are extremely linear and dull. Contrast the original Blackrock Depths instance, with a colorful and huge dwarven city, to the dull linear design of Grim Batol and you can see the difference. My favorite thing is to out-level the dungeons so I can go in and just solo them if I need to. Otherwise, I have to just run the dungeons with a bunch of strangers using the finder who are ONLY interested in clearing it as quick as possible and getting gear.
Anyway, back to my CRPG…
I originally had my experience levels going up exponentially; I decided to relax that a bit and try and make it less step at the higher levels. Time and play-testing will also help adjust these values.
The most important often-overlooked aspect of CRPG design is pacing. You want to make sure your world is not just large but also rich in content. You want to avoid the following:
I’ve no doubt that my present material for the game will go through some serious revisions as I go along. I’ve already noticed places where I’ve repeated myself a bit, and I want to make sure I make everything fresh and interesting.
And now a side-trek on random number generation…
Pseudo-random numbers have been a part of software design for awhile. It’s a field that is still very heavy in the mathematics area, because a large part of it is about trying to design an algorithm to create a random value quickly but also predictably.
However, if you started with BASIC in the old days, all that was hidden from you. You just called a RAND or RND function and got a number. The better BASIC languages offered a way to change or update the random seed, but otherwise you just used what they gave you and didn’t know what it did under the hood.
For most 99’ers, the earliest best example of an assembly random number generator came from the source code for Tombstone City, a cartridge game from Texas Instruments. The source code was provided with the Editor/Assembler package as a learning tool. (Sadly, as a teenager, I wasn’t aware of this. Studying source code may have made my early frustrations with assembly programming easier. Ah well…)
Here is the routine from it:
RANDNO LI R4,28645 MPY @RAND16,R4 AI R5,31417 MOV R5,@RAND16 RT
As you can see, not a lot to this one. Let’s say you started with a random seed of 0 and went a few cycles:
This is pretty much what every pseudo-random algorithm does. It generates a sequence of values using the prior value as a key to generate the new value. It also creates a predictable sequence, which can be useful in situations where you want the randomness to be more controlled. It also produces an exact sequence of 65536 different values; changing the seed value just changes where you start in the sequence.
However, it consistently produces an even/odd pattern. In some cases, this may be desirable! But for most random routines you want the even/oddness to be variant.
A simple fix, which is what I introduced and it did the trick at the time, was to throw in a bit shift of an odd value on the random seed, as follows:
RANDNO LI R4,28645 MPY @RAND16,R4 AI R5,31417 SRC R5,5 MOV R5,@RAND16 RT
By shifting the bits an odd number of times it broke up the even/odd cycle. It however still creates a predictable sequence since you are always shifting by a predictable amount.
So my next solution was to have the bit shift be determined by an outside factor.
The TI-99/4a has a vertical sync clock which ticks at 1/60 of a second. By using that value to determine the bit shift, I was able to have it shift to a completely different place in the sequence in an unpredictable fashion:
RANDNO LI R4,28645 MPY @RAND16,R4 AI R5,31417 MOV @CLOCK,R0 ANDI R0,>000F SRC R5,0 MOV R5,@RAND16 RT
@CLOCK in this instance is a word that I increment every time there is a vertical sync.
But even THIS one is not perfect… because when I tried to use it to generate random map content, which involved consecutive calls to the random function in a tight time frame when the vertical clock did not actually change, it created strange symmetric patterns every 4th or 5th iteration.
So I decided to change the routine, for the map generation at least, to a different approach:
MASK DATA >B400 RANDNO MOV @RAND16,R0 SRL R0,1 JNC NOCARR XOR @MASK,R0 NOCARR MOV R0,@RAND16
This rather clever routine avoids the use of multiplication (which is unavailable in many assembly languages of the era, and is cycle-costly) and uses XOR instead to invert the bits when there is no carry. This creates a predictable sequence that varies high and low.
Let’s step through it a bit, starting with 0:
So this one worked better, since it wasn’t relying upon the clock value. Until I reached a particular map where I was placing random tiles in a smaller area. Suddenly every single map produced the EXACT SAME pattern of tiles in an odd diagonal line. I realized then that I had to go back to the drawing board.
The solution (at present anyway) is to use a different clock. I needed one that counted faster than 1/60 of a second, and wasn’t reliant upon the vertical sync, which requires you to have interrupts enabled to update the value.
I found one in the interface chip the TMS9901, which has a 14-bit counter value you can set. It decrements it at clock-cycle speed so it’s VERY fast. It requires a bit of setup in your program to initialize with a value, but after that it’s readily accessible. After I put this in, my random values are EXTREMELY random and satisfying.
* Somewhere at program start * Set up 9901 counter CLR R12 * CRU base of the TMS9901 SBO 0 * Enter timer mode LI R1,>000F * Set counter to 15 INCT R12 * Address of bit 1 LDCR R1,14 * Load value DECT R12 SBZ 0 * Exit clock mode, start decrementer * Random function RANDNO LI R4,23729 * Load with 15-bit value MPY @RSEED,R4 * Multiply by the random seed AI R5,31871 * Add a 15-bit value to the lower word CLR R12 * CRU base of the TMS9901 SBO 0 * Enter timer mode STCR R0,15 * Read current value (plus mode bit) SRL R0,1 * Get rid of mode bit SBZ 0 * Exit clock mode, decrementer continues SRC R5,0 * Rotate seed based on clock value MOV R5,@RSEED * Copy back to RSEED RT
Alternatively, if I was coding in a system that lacked such a handy counter, I would try and create some kind of clock that is incremented by other parts of my program that I could use in it’s place. The most important aspect of it is that it can’t be in the random function itself!
One other option, that occurred to me afterwards, is that I could have opened the interrupts on the TI during the random function so that the clock value would get incremented. I’m reluctant to do this, though, because in many places I’m doing more during interrupts than just a clock, and they would steal resources away from map generation.
So for now, I seem to have enough randomness to use everywhere in the CRPG. 🙂
Okay, a couple new things…
One, after all the excitement and talk of the latest expansion for World of Warcraft, I decided to renew my subscription after over four years just to play a bit. I have two and a half expansions of material to enjoy before I even buy the latest one, so it’s slow going but fun! You can criticize Blizzard on a lot of things, but they are damn good at making a fun game.
All of that doesn’t mean the CRPG has dropped off, far from it! I needed something to get through the myriad of frustrating bugs in my battlemap generator system. I decided I didn’t want to risk testing the code in the game itself, but instead write a prototype to create the maps so I could observe how they looked. After a LOT of assembler debugging to find video over-writes and other problems, it’s done and working well! This will allow me to test different graphics combinations, and add new ones as needed, to keep the maps looking fresh and interesting. Here’s a small sampling of some:
Lastly, I had a very unexpected and welcome gift come my way…
When I was at Fest West 18′ a few months back, we had a contest and the prize I won was a TIPI sidecar controller. I graciously turned it down and took an alternate gift because I am using my Peripheral expansion system and the sidecar isn’t advantageous for it.
So the creator of the TIPI system contacted me a week ago to say he’d finished the PEB card version of it, and he was going to send me the card! I had to purchase my own Raspberry PI unit and associated components (power supply, Micro SDHC card) but that was not expensive or difficult on Amazon.
After figuring out how to put it together and get it in, I discovered it’s a fantastic device. I am able to access the “file system” from my local PC laptop and can quickly and easily copy TI files from there right to the drive. This allows for VERY fast turn-around in testing the game on the hardware, and it’s a perfect replacement for the drive system, given that the disk controllers are getting scarce, and these days, you WANT some compatibility with modern devices.
You can find all the information on TIPI you need right here!
As part of my work coding up traps for the encounter module, I realized I had to make my damage approach consistent. There’s effectively two kinds of damage reduction, one for wounding effects and one for every other kind of effect such as poison, charm, and so forth. So I centralized damage determination in the various modules to make it so the same rules apply everywhere.
I also realized my statistic screens for players need some serious revisions. To make it similar to the monster stat screens, I’m restructuring one so you are told explicitly your stats for melee, ranged, and defense. I won’t throw away the work I did designing the item stat generation with the little blocks indicating special qualities and such. Instead I’ll move it into the examine option for inventory.
I also need to write up trading code for the encounter module. Items in combat are randomly determined and can’t be recovered later if you turn them down. So I need to make sure you can trade off items to different players and, if need be, discard an item from your backpack to make room. (No, I can’t have chests of items generate in travel mode where the combat occurred. There’s a multitude of technical reasons why that’s impossible.)
I may need to go back to the travel module and make some changes there too. My original design was built around the idea of an “active” player. Whoever was active would be the one who picked up items, used his skills for trap detection, locks, and other things. But I’m wondering if this is going to cause trouble and confusion later. I originally extended it into the manager module but I’m going to change that so it prompts you to select a player for given operations instead.
The combat module is finished! I ended up moving combat end determinations into the Encounter module, so that work will be completed there.
Now that I got some code space to spare, I may have a complex “You found a chest, open it?” event at the end of combat. Another opportunity to throw in some traps… 😉