-
Posts
1,891 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Gallery
Events
Store
Everything posted by adamantyr
-
One thought, you may want to add the caveat "limit one submission per person". Obviously, it's cool to be able to do 2-3 programs, but for a contest with tangible prizes, I think a 1:1 ratio is best. What you can do is say "Make as many as you want, but when I make the submission thread, you can only post ONE as your submission." Adamantyr
-
Very nice work! You should include a screenshot in your first entry just so people can see what we're talking about. Adamantyr
-
Oh yeah. I wrote my maze generation system based on how Fargoal's worked... albeit in a much simpler fashion. Yeah, I can't do much in 30 lines to make the randomness, er, less random. Maybe give you three lives instead of just one, and you gain one for each successful level? Hm... I also need to work on the maze generator a bit, make it a bit less likely to make impossible mazes. I know WHY it's possible; it's currently able to traverse its own path so it will stop immediately. I may see if I can make larger chambers as starting points as well, although the HCHAR and VCHAR's involved would be pushing it out of 30-line territory. Since I knew CPU Overdrive would be popular, I also had to slow the monster down... he moved WAY too fast before. I used PEEK statements to check the VDP timer and make him wait at least 1/4 of a second between moves. This makes you slightly faster than he is on a straight-way. Adamantyr
-
Hm, actually, I was wrong, but you have to CALL INIT before CALL LOAD works. And it's not much use, since it doesn't have VDP access. (Testing how much of a headache trying to manipulate the VDP ports is. So far, a real skull-cracker.) Oh, I suppose you want your money back now? The random routine was pretty good about not creating trapped conditions when I was testing it, but it appears its possible... Adamantyr
-
Glad you liked it. Yeah, it's all the GCHAR's to uncover the map that really slow it down. And much to my disgust, Extended BASIC doesn't support using LOAD for addresses and values, so I can't cheat and use a bit of machine language know-how to speed it up. Adamantyr
-
Eh, doing regression fixes on my CRPG is bor-ring... I figured, why not do my own entry for the contest? And here it is... DARK MAZE! And now in version 1.1! VERSION 1.1 CHANGES: - Random maze generation improvements (bigger chambers, less likely to make an impossible maze) - The sword now lasts a limited time - Multiple potions/swords possible per level - When the sword is in hand, the monster runs away - Three lives, random placement on same map if monster catches you - Scoring system DARKMAZE_11.zip You are a fearless adventurer, braving the awful depths of a dark and sinister dungeon for precious treasure. But beware! You are not alone in this foul place. You can hear something else moving down here... and it's getting closer... and louder! Unfortunately, it's very difficult to see down in this place. You can scour around you and see everything close, but far away places are a mystery. As you traverse the dungeon, you uncover its secrets and learn its twisting passages. There are a few things to help you. A bubbling potion is here, drinking it causes the monster chasing you to stop for a brief time, letting you catch your breath and get out of his immediate reach. Better still is the mystical sword, a powerful blade that will frighten and drive the monster away from you, for with it you can vanquish him and explore at your leisure... but the sword will disappear after a short time, so move quickly to catch him! To advance to the next level, you must collect all the treasure chests on the dungeon level. The first level had three, and one additional chest is needed for each level, to a maximum of eight. You have three lives at the start of the game. Completing a level awards you one additional life, to a maximum of five. Each time the monster catches you, you reappear in a different portion of the maze, although areas you have already traversed will remain visible. The game ends when your greed leads to your frightful doom... Scoring: When you finally perish, your final score will be displayed. Two values separated by a slash indicate that the point award goes up per level, to the given maximum. Picking up a Chest ........................ 50 points Picking up the Sword ...................... 10 points Picking up the Potion ..................... 10 points Completing a level .................. 100/1000 points Killing the monster ................. 100/1000 points Controls: Joystick - Move any of eight directions Enjoy! Current High (Me): Level 9, 8210 points
-
Interesting, I'll have to remove the NOP and SWPB code from my utilities. It won't make a huge difference in speed, but if it scrapes up some spare bytes, so much the better. Like yourself, I always know where my workspace is as well, so I can replace it with static memory calls. Adamantyr
-
Basically, a sprite management engine that rotates which sprites are in positions 1-4 so that a sprite has at least 1 frame of visibility. You would just swap around the 4-bytes of sprite attribute data for a period of time. The main challenge of such an engine is that it introduces a new layer of abstraction. Your FlyGuy sprite, for example, may not always be sprite #1, you would have to use some kind of ID table to store which sprite is the FlyGuy at any given time. A good engine can even expand the number of sprites. The max number of sprites is 32, but if you rotate sprite data in and out, then it's however many the engine can handle. (Although to be honest, I can't foresee a game where I really need to have more than 32.) Adamantyr
-
Quite true. I only have two workspaces in my CRPG, a primary and an alternate. None of my BLWP calls ever occur on the same layer, so I can reuse the alternate workspace for each one. That's the best way to make use of it, a quick way to get a fresh set of registers, sort of an improved subroutine call. You COULD avoid it by just keeping your data in pre-determined arrays, but that's almost as messy and the trade-off isn't really worth it. Exactly, every situation is different. What worries me is any budding 99'er programmer may just automatically NOT use BLWP because he keeps reading bad things about it with no context as to why it's bad. Yeah, that is a bad clear routine. The one I have in my CRPG is thus: * Clears the entire screen CLRSCR MOV @ACTSCR,R0 ORI R0,>4000 SWPB R0 LI R1,>2000 LI R2,768 MOVB R0,@VDPWA SWPB R0 MOVB R0,@VDPWA CS1 MOVB R1,@VDPWD DEC R2 JNE CS1 RT I have two screens, the address of which is stored in @ACTSCR, so this clears the current active screen. VDPWA and VDPWD are, of course, symbolic addresses to the VDP read/write ports in the scratchpad. I could replace those with indirect registers but this routine is fast enough as is. However, I also have a video routine that just writes the high-byte content of R1 for R2 times at R0 address called VMBS (Video Multiple Byte Stream). So I could drop my clear routine entirely and replace calls to it with this: MOV @ACTSCR,R0 LI R1,>2000 LI R2,768 BLWP @VMBS Not as fast, of course, with the BLWP overhead, but the need for speed here is low, and this routine would save me a couple dozen bytes. Hopefully I don't get to a stage where I'm scrimping bytes THAT closely. Adamantyr
-
Er, how were you going to accomplish that? Replacing them all with sprites? Or using bitmap mode? On the challenging side, if you wrote a sprite rotation system to make sure that every sprite remains visible regardless of the horizontal limitation, that would be quite lovely to see... I don't know of any commercial TI programs that ever tried this, only the Konami MSX platform titles that used the TMS9928A chip. Adamantyr
-
Yes, I found the same problem when I did my conversion of TI-Trek, Tombstone's random number generator always does even/odd patterns. I broke it up by using a SRC with an odd value to throw it off. My current random routines in my CRPG are as follows: * Random number generator (Range in R3, Result in R4) * Returns 0 to R3-1 RNDNUM DATA VWS,RAND2 RAND2 BL @RNDGEN MOV @>0006(R13),R3 INC R3 CLR R4 DIV R3,R4 MOV R4,@>0008(R13) RTWP * Random number generator 16-bit (Result in R4) * returns 0-65535 RANDOM DATA VWS,RAND1 RAND1 BL @RNDGEN MOV R4,@>0008(R13) RTWP * Random number generation RNDGEN LI R4,23729 MPY @>83C0,R4 AI R5,31871 MOV @CLOCK,R0 ANDI R0,>000F SRC R5,0 MOV R5,@>83C0 RT The @CLOCK value is just a word that increments from 0 to 65535. I was using the interrupt counter for this, but you can do it any way you want, so long as a value of some kind that changes periodically is there. I still use @>83C0 for the heck of it, but you could drop that as well if you wanted. The main thing I like is the random SRC which nicely jumbles things. Are you referring to the REF utilities like VMBW, KSCAN, and so forth? Yeah, you can write your own of these... KSCAN still would use the SCAN routine, though, unless you circumvent it. The "BLWP is slow" creed is getting increasingly prolific these days. And not for good reasons. Yes, it's slower than using BL... and it's definitely slower than just hitting the VDP ports directly yourself. But there are times you need speed and times you need convenience of access. Try maintaining register contents between 4-5 levels of subroutine calls! For most of my CRPG display work, I build up everything in CPU memory first and then just blast the whole thing to the screen in one go. That saves a LOT of time. I also don't use any subroutines at all, I just hit the VDP port directly. And fortunately, most of my stuff is just to the screen buffer, not the color or pattern buffers. Adamantyr
-
My secret shame... fellow 99'er and friend calls me up one day and says "Hey.... how were you spelling that coincidence subprogram in Extended BASIC?" "CONIC ... Oh. Crap." Adamantyr
-
There is actually two parts to the player array. The entire thing is stored in VDP. One part is the constantly changing bits, which is status of various effects, health and stamina, wounds and fatigue, and so forth. This is copied down into CPU memory for quick updates and access, and only pushed back to VDP prior to a game save. The second part is the less often changing things, like what equipment the player has, how much ammo of each type, and his defense and miss chances. I don't need them in CPU memory at all times. For now, I'm reading this in as a block from VDP for usage in attack determination, so these values COULD be adjusted directly without harm, since I'm throwing it away afterwards. There is no permanent fatigue statistic; I would have to actually create and carry one in memory all the time. A lot of the "special cases" like various weapons are going to be tricky to do; they don't really fit into an easy categorization that can be made into a pure data form. (More on this below) Fatigue has nothing to do with damage either, incidentally, it's just a measure of how much fatigue the attacker takes on a successful attack or the defender on a successful defense. Multiplying the whole thing like that is something easy to do in a high-level language like C, but EXTREMELY difficult in assembly. Plus, there's a case of "Why am I doing all these calculations when they're pointless anyway? Hey, he's invulnerable, return a code and stop!" Not that's speed an issue; even on a 3mhz machine all this gets done in the blink of an eye. I wrote up my first permutation of the code last night and wow, it was a big chunk of code. At least a kilobyte. Since I still have special effects with sprites with a huge unknown on memory consumption, I definitely want this to be as tight as possible. My current thoughts is to categorize each attack type and the adjustments into a matrix of arrays, which can then be processed. Essentially, I'd write a data-driven statistic crunching engine. Just say "Use the melee array" and it will grab the indexed values from the right places, add the bonuses/penalties specified, and all the determinations are made with the same code. This is a bit of work, and it may mean losing the special cases initially until I find a clever way get them back in, but it should reduce the footprint quite a bit from just straight linear processing. Adamantyr
-
Oh, to continue about the complexities of combat, here's the pseudo-code I have to translate into assembly language in order to determine what happens when one unit attacks another: Attack Check if Defender is Invulernable If so, attack fails, return Invulnerable code Determine attacker and defender base fatigue 1 standard, 0 if empowered, 2 if weakened Determine attacker and defender power bonuses/penalties Determine if attacker is exalted/cursed If exalted, +2 to attacker power. If cursed -2 to attacker power Determine if defender is exalted/cursed If exalted, +2 to defender power. If cursed -2 to defender power Determine if defender is guarding If so, +1 to all defenses, +2 to defender power Goto appropriate attack type Attack Melee Adjust base fatigue If attacker has 2H weapon, +1 fatigue Determine if attacker is enraged If so, add +4 to attacker power Determine if defender is armored If so, add +4 to defender power Determine if defender is enraged If so, subtract -4 to defender power Attack: Determine if attack misses If defender is blurred, miss chance is 50% (0 on a RND of 2) If attack fails, return Miss code Determine if critical hit (0 out of 16) If critical hit, defender cannot avoid attack Set attacker power to 150% Set attacker fatigue to 0 Goto Damage Defense: Determine if defender can parry (Any melee except bare hands and dagger) If so, defender parries, takes fatigue, return parry code Determine if defender can block (Has shield) If so, defender blocks, takes fatigue, return block code Determine if defender can dodge (Open space available to move into, not immobilized) If so, defender dodges, change position, takes fatigue+1, return dodge code Damage: Determine damage (Attacker power + bonus/penalty - defender power + bonus/penalty, minimum of 1) Defender takes {damage} wounds, attacker takes fatigue Return hit code Attack Ranged Adjust base fatigue If attacker has arbalest, -1 fatigue Determine if defender is shielded If so, add +4 to defender power Attack: Determine if attack misses If defender is deflecting, miss chance is 50% (0 on a RND of 2) If attack fails, return Miss code Determine if critical hit (0 out of 16) If critical hit, defender cannot avoid attack Set attacker power to 150% Set attacker fatigue to 0 Goto Damage Defense: Determine if defender can block (Has shield and attacker not using a firearm) If so, defender blocks, takes fatigue, return block code Damage: Determine damage (Attacker power + bonus/penalty - defender power + bonus/penalty, minimum of 1) Defender takes {damage} wounds, attacker takes fatigue Return hit code Attack Sorcery (target) Adjust base fatigue If attacker using affinity spell, -1 fatigue, +2 bonus power If defender targeted by affinity spell, -1 fatigue, +2 bonus power Determine if defender is protected If so, add +4 to defender power Attack: Determine if attack misses If defender is warded, miss chance is 50% (0 on a RND of 2) If attack fails, return Miss code Determine if critical hit (0 out of 16) If critical hit, defender cannot avoid attack Set attacker power to 150% Set attacker fatigue to 0 Goto Effect Defense: Determine if defender can block (Has foci) If so, defender blocks, takes fatigue, return block code Effect: Determine damage (Attacker power + bonus/penalty - defender power + bonus/penalty, minimum of 1) Defender takes {damage} wounds or {damage} duration effect, attacker takes fatigue Return hit code Attack Sorcery (area) Adjust base fatigue If attacker using affinity spell, -1 fatigue, +2 bonus power If defender targeted by affinity spell, -1 fatigue, +2 bonus power Determine if defender is protected If so, add +4 to defender power Defense: If defender is warded, miss chance is 50% (0 on a RND of 2) If attack fails, return Miss code Determine if defender can block (Has shield or foci) If so, defender blocks, takes fatigue, return block code Effect: Determine damage (Attacker power + bonus/penalty - defender power + bonus/penalty, minimum of 1) Defender takes {damage} wounds or {damage} duration effect, attacker takes fatigue Return hit code I also need to determine how this translates into direct screen action... I don't want to build a "cinematic event" array and post-process everything, so I'll probably just have to return codes to indicate what units are doing and go from there. Adamantyr
-
Correct! And they don't necessarily occur in sequential order. I think I may need to add a "flashing arrow" to the direction selection, though. Just pressing a single key, while fast, isn't illustrative of what choice was made, and could be problematic if you're like "Wait, which way did I set it to again?" Also need to change it from SPACE to ENTER to select; it's too easy to press SPACE twice and accidentally end the turn of the character. Adamantyr
-
Well, Excel files are hard to share out. Like any Office file, they can have macros embedded in them that could be a security hazard. And any screen shot or truncated file I attach has to be stored somewhere on the web; it won't let me directly embed them. Basically, I use a spreadsheet to space out the individual bits/bytes with a numeric index. Then I can use comments and a text line underneath it to indicate blocks of array data. I also use a sheet to track what's going on in VDP memory. I make use of the DEC2HEX function in Excel to calculate the hexidecimal values for easier tracking purposes. In the pre-2007 versions of Office, you may need to activate the scientific module to have access to them. Typically, I'll have a few worksheets to store raw data, then I'll have a sheet that compiles it into a series of hex strings. I can then use Hex Workshop (a very useful stand-alone hex editor) to directly insert the data into pre-existing files. I don't use this technique for everything... creating maps, for example, I went to the trouble to write an editor for. Not much fun drawing landscapes abstractly. Adamantyr
-
If you're using Firefox, be sure to download the Firefox Windows Media Player plug-in. Meanwhile, you can download the video directly from here: http://www.adamantyr.com/video/Combat1.avi If the raw file doesn't work, then it's probably some codec issue or something. I recorded it through Classic99, then used AVS4You's Video editor to convert it to MPG4 format. Adamantyr
-
I've put up a video recorded in Classic99 on my blog. However, I've had at least one complaint that nothing could be seen. Is anyone else having difficulty viewing it? It's at http://www.adamantyr.com/blog Thanks, Adamantyr
-
Yeah, World of Warcraft had both the primary attributes (strength, dexterity, etc) and the secondary attributes (attack power, spell power, haste rating, etc.) And of course, what ends up happening with such systems is that the secondaries are the most powerful, and the most desirable. For example, only items give you spell power, which was needed for any serious power as a spellcaster. The next expansion (Cataclysm) is actually doing away with most of the secondary attributes being independent of the primaries. So Intellect directly increases spell power, strength directly increases attack power, etc. And items that add directly to secondaries are going to be changed/removed. So that's going to really change the dynamics in the game. It's good to do research into other RPG's and CRPG's, even board games, to see how they resolve disputes and conflicts, for ideas on how to design your own system. Adamantyr
-
Well, if you look at a lot of old CRPG's from the late 70's/early 80's, many of them are basically just a clone of the Dungeons & Dragons rules systems. For example, nearly every one has ability scores for strength, dexterity, etc. I think a lot of old game designers not only didn't know how to really craft their own systems, they also were afraid if they deviated too much, people would say "that's not an RPG!" and not play it. Early marketing woes, I suppose. So, when I first sat down and sketched out a character system, I had something pretty advanced with ability scores, a complex skill system, and no real classes beyond what was defined in the mechanics. The problems became apparent, when I actually started looking at the code. Combat resolution in almost all CRPG's always boils down to two numbers; an attacker and a defender score. If one is greater than the other, an attack either succeeds or fails. So let's look at a system example: We have a system multiple ability scores like Strength (STR) and Dexterity (DEX). So, let's say you have a melee attack. You decide it's Strength + half the Dexterity score + character level + a class bonus for the attempted hit. In BASIC, this would be calculated like this: HIT = INT(STR + LVL + CLASS_BONUS + (DEX/2)) In assembly, though, things get much more complicated: MOV @STR,R0 A @LVL,R0 A @CBS,R0 MOV @DEX,R1 SLA R1,1 A R1,R0 MOV R0,@HIT Consider that for a single attack type, I just spent about 10 bytes in memory variables and 24 bytes in opcodes. Doesn't seem so bad, right? Wait a moment... how are those memory variables getting filled? Let's say I got all my player data stored in an array that's 80 bytes with static positions: * Level - Offset 2 (byte) * Strength - Offset 6 (byte) * Deterity - Offset 7 (byte) * Class bonus (melee) - Offset 20 (byte) * W80 - Data word, value 80 MOV @PACTIVE,R0 MPY @W80, R0 MOVB @CHRDAT+2(R1),R0 SRL R0,8 MOV R0,@LVL MOVB @CHRDAT+6(R1),R0 SRL R0,8 MOV R0,@STR MOVB @CHRDAT+7(R1),R0 SRL R0,8 MOV R0,@DEX MOVB @CHRDAT+20(R1),R0 SRL R0,8 MOV R0,@CBS Well, that's looking a little more involved. Of course, this is a pretty linear and inefficient way to read static data in. I could make the process simpler by doing it like this: MELEEH DATA 4,2,6,7,20 ... MOV @PACTIVE,R0 MPY @W80, R0 MOV @MELEEH,R3 LI R4,MELEEH+2 CLR R0 MLOOP MOV *R4+,R2 LI R5,CHRDAT A R2,R5 MOVB *R5,R6 SRL R6,8 A R6,R0 DEC R3 JNE MLOOP MOV R0,@HIT This method is data-driven, and while not as easy to read, is better structured for the computer. Ideally, values that have synergistic qualities should be kept close together or in an order that makes sense for the computer, NOT the user. Except... I didn't get my dexterity divided it half! Argh! Now I ask myself, is it REALLY worth the trouble to create this system? Or maybe find something that's more agreeable to the language and platform? More to come... Adamantyr
-
Yeah, combat engines are TOUGH. I've ran the gambit from rough clone of other CRPG's like Avernum to actually crafting my own system from absolute scratch. Mainly because the complexities of assembly language dictated it be so. With such a small memory space, you need to prioritize what you really want. Complicated simulationist mechanics like a lot of tabletop RPG's have is just not worth the hassle. I've got the combat controls done, now it's translating those orders into on-screen action. I'm having to step back and do a little re-design here and there; I hadn't had all the mechanics fully worked out. It will be awhile before I have a full "playable" combat engine. Adamantyr
-
Are you planning on using Extended BASIC for all your file usage? It's not all that efficient to use for files, which is why Old Dark Caves and Legends both avoid it. An assembly routine to handle your file accesses may be the least painful way of doing it. If you're absolutely insistent upon it, I highly recommend using strings for all your data, including numbers. You can use CHR$ and ASC functions to convert from string to numeric as needed, and you can compact it much better in the BASIC file system. Try writing a file out in Classic99 then viewing it in a hex editor. You'll see what I mean. UPDATE and RELATIVE are your methods to access individual records. However, your fine control is still limited to the width of the record. So if you read an 8-byte record in, you have to write an 8-byte record back out. Your first step should be to lay out all the data you want to store externally. Then figure out when and where you need each part, exactly. It's been years since I tried to do anything complicated with the Extended BASIC file system... heck, figuring it out for my CRPG was the first major hurdle, and I'm relieved to be well past it. Adamantyr
-
Hello Opry Well, if I were to do a sound utility, it would probably come out in 2 versions. A light version for Flash and a Java version with whistles and bells. If I were to say what I needed for a sound utility. Well, I should be able to enter, edit, insert and delete notes and play them at any point much like if the notes were coming from a 4A. One should be able to see several notes at the same time, and probably much like 1 of the 4 below. I tried to name them as best as I can. When I save I want the music (or soundeffect) to be an “ordinary” soundlist (data statements) for inclusion directly in assembler source. Very impressive graphics utilities! As for a sound editor, there's two real needs here: the ability to transcribe musical notation easily and create sound effects easily. I'd go for two different utilities for each need. One thing that would be VERY useful for a 99'er is the ability to enter sheet music as is, and have the program "adjust" the octaves so that notes that the TI is unable to play forces it to restructure the music to the higher octave range. Adamantyr
-
When you check status after calling KEY, there are three potential values: 0, no key is pressed 1, a new key is pressed from the last time you called KEY -1 the same key as previous is pressed from when you last called KEY If you wrote something like this, it would prevent holding keys: 100 CALL KEY(0,K,S):: IF S<=0 THEN 100 Adamantyr
-
Loading one piece of code from another
adamantyr replied to InfernalKeith's topic in TI-99/4A Development
True. The only way to transfer values that way is to place them into the low memory using CALL LOAD. No, that would work fine. Any reason it can't be a sequential file of data? Relative is useful for indexed data, but if it's just a stream of values, a sequential save/load is probably the simplest approach. Adamantyr
