battle engine

If you have questions about any aspect of QBasic programming, or would like to help fellow programmers solve their problems, check out this board!

Moderators: Pete, Mods

captain_squirrely

battle engine

Post by captain_squirrely » Tue Aug 03, 2004 11:06 pm

Where can i find a good rpg battle tutorial?

User avatar
Nodtveidt
Veteran
Posts: 826
Joined: Sun Jul 25, 2004 4:24 am
Location: Quebradillas, PR
Contact:

Post by Nodtveidt » Wed Aug 04, 2004 4:07 am

DarkDread wrote a pretty good one. Search google.com.

User avatar
Pete
Site Admin
Posts: 882
Joined: Sun Dec 07, 2003 9:10 pm
Location: Candor, NY
Contact:

Post by Pete » Wed Aug 04, 2004 9:14 am

DarkDread's RPG Battle Engine tutorial is actually on my site in the Tutorials section.

Anyway, I've reprinted it here for you:

Code: Select all

+------------------------------------------+
|  RPG Programming Tutorial                |
|                                          |
|  June/July 1997                          |
|  Issue #3                                |
|  Released: July 16th, 1997               |
|  Enemy Encounter Engine                  |
+------------------------------------------+

At last, at last.  Here is the enemy encounter tutorial.  Make a note, this
is part 1 of the tutorial.  If all things go according to plan, part 2 should
be out in two weeks (Assuming of course, that it's before July 30th when you
are reading this).  Also, the tutorials will be coming out every two months,
beginning with this issue.

3.1 INTRODUCTION
---------------->>>>

        So, you want to program some enemies into your new RPG.  Well, here's
one way to do it.  For this tutorial, I'll focus mostly on setting up the
enemy encounters.  The second part of this will focus on more detailed aspects
of the enemy encounter engine.

3.2 SETTING UP
---------------->>>>

        Compared to level maps and tile graphics and story boards, an enemy
encounter routine is, for the most part, rather simple.  First, it is useful
to make a list of all of the variables you'll be using in the fight sequences
and write them down on paper so that you know what each does.  For the sake
of simplicity, I'll only use some essential variables for this lesson.  Oh,
and this lesson will only include one player vs. one enemy fights.  Part 2 of
the tutorial will expand on multiple characters/enemies.

So we would make our list like so:

+---------------------------+-------------------------------------+----------+
| Variable name             | Variable description                | Type     |
+---------------------------+-------------------------------------+----------+ 
| HitPoints                 | The player's hit points             | Integer  | 
| MaxHitPoints              | The player's max hit points         | Integer  |
| MagicPoints               | The player's magic points           | Integer  |
| MaxMagicPoints            | The player's max magic points       | Integer  |
| Strength                  | The player's strength               | Integer  |
| Defense                   | The player's defense                | Integer  |
| Experience                | The player's experience             | Long     |
| Gold                      | The player's gold/money/etc.        | Long     |
| Level                     | The player's level                  | Integer  |
| Weapon$                   | The player's weapon                 | String   |
| EnemyName$                | Name of the enemy                   | String   |
| EnemyHP                   | The enemy's hit points              | Integer  |
| EnemyStrength             | The enemy's strength                | Integer  |
| EnemyDefense              | The enemy's defense                 | Integer  |
| EnemyExperience           | Amount of experience gained for     |          |
|                           | defeating the enemy                 | Integer  |
| EnemyGold                 | Amount of money gained for defeating|          |
|                           | the enemy                           |          |
+---------------------------+-------------------------------------+----------+

        In a nutshell, those are the basic variables you will need when making
an enemy encounter routine.  Now we will put our variables to use.

3.3 THE ENCOUNTER
---------------->>>>

        Instead of blathering on and on about what you need to do, I'll just
give you some sample Qbasic code which you can use and optimize.

<<-------------- Qbasic code, begin cutting here.
' For the sake of this tutorial, I'll just put in some stats for the player.
' You should have your character's stats stored in memory or wherever so just
' disregard the next few lines.
HitPoints = 50
MaxHitPoints = 50
MagicPoints = 25
MaxMagicPoints = 25
Strength = 3
Defense = 2
Experience = 0
Gold = 0
Level = 1
Weapon$ = "short sword"

' The next few lines create a random enemy and input the enemy's stats into
' the variables accordingly.
RANDOMIZE TIMER                                 ' This will give us a random
                                                ' number seed.

enemy = INT(RND * 3) + 1                        ' Will produce a random number
                                                ' between 1 and 3.

IF enemy = 1 THEN                               ' This IF block will fill in
        EnemyName$ = "Slime"                    ' the enemy stats based on
        EnemyHP = 10                            ' what number was chosen.
        EnemyStrength = 2                       ' Feel free to experiment by
        EnemyDefense = 2                        ' changing these stats around
        EnemyExperience = 10                    ' and adding more enemies.
        EnemyGold = 8                           ' Just don't forget to add
    ELSEIF enemy = 2 THEN                       ' to the 3 in Enemy = INT(RND
        EnemyName$ = "Skeleton"                 ' * 3) + 1 when you are adding
        EnemyHP = 24                            ' more bad guys.
        EnemyStrength = 4
        EnemyDefense = 1
        EnemyExperience = 20
        EnemyGold = 16
    ELSEIF enemy = 3 THEN
        EnemyName$ = "Warrior"
        EnemyHP = 40
        EnemyStrength = 4
        EnemyDefense = 2
        EnemyExperience = 40
        EnemyGold = 35
END IF

' Now that we have our enemy, let's begin the fight.  For this fight, we'll be
' nice and always let the player go first.

CLS
PRINT "Oh, oh.  Looks like you are being attacked by a "; EnemyName$

DO
    PRINT "What do you want to do? (F)ight, (R)un"
    Choice$ = LCASE$(INPUT$(1))                 ' This line scans the keyboard
                                                ' for player input.

    IF Choice$ = "r" THEN
            RunAway = INT(RND * 2) + 1          ' Give the player a 50/50
                                                ' chance of running away.
            IF RunAway = 1 THEN
                    PRINT "You got away!"
                    done = 1                    ' This is used so that the
                                                ' program will know when the
                                                ' fight is over and exit the
                                                ' loop.
                ELSE
                    PRINT "You couldn't run!"
            END IF
        ELSEIF Choice$ = "f" THEN
            ' Below is a simple equation which will create a random attack
            ' strength based in part on the player's Strength.
            attack = ((INT(RND * 3) + 2) * Strength) - ((INT(RND * 3) + 2) * EnemyDefense)
            IF attack > 0 THEN
                    PRINT "You attack the "; EnemyName$; " with your "; Weapon$; " and do"; attack; "damage!"
                    EnemyHP = EnemyHP - attack  ' This will decrease the
                                                ' enemy's HP by the amount of
                                                ' the player's attack.
                ELSE
                    PRINT "You did no damage!"  ' If the player's attack is
                                                ' less than 1 then no damage
                                                ' will be taken from the
                                                ' enemy.
            END IF
            IF EnemyHP < 1 THEN                 ' This IF block checks to see
                                                ' if the player has defeated
                                                ' the enemy and increases the
                                                ' player's gold/experience if
                                                ' they have.
                    Gold = Gold + EnemyGold
                    Experience = Experience + EnemyExperience
                    PRINT "Yeah! You beat the "; EnemyName$; " and got"; EnemyGold; "gold and"; EnemyExperience; "experience!"
                    done = 1
                ELSE                            ' If the player did not beat
                                                ' the enemy, then the enemy
                                                ' gets to attack.
                    attack = ((INT(RND * 3) + 2) * EnemyStrength) - ((INT(RND * 3) + 2) * Defense)
                    IF attack > 0 THEN
                            PRINT EnemyName$; " attacks and does"; attack; "damage!"
                            HitPoints = HitPoints - attack ' This will decrease the
                                                ' player's HP by the amount of
                                                ' the enemy's attack.
                        ELSE
                            PRINT EnemyName$; " did no damage!"' If the enemy's attack is
                                                ' less than 1 then no damage
                                                ' will be taken from the
                                                ' player.
                    END IF
                    IF HitPoints < 1 THEN       ' This IF block checks to see if the
                                                ' player has no hit points left and
                                                ' exits the LOOP if so.
                        PRINT "You have been defeated by the "; EnemyName$
                        done = 1
                    END IF
            END IF
        ELSE
            PRINT "That is not a valid choice!" ' If the player has selected
                                                ' a choice other than "r" or
                                                ' "f" then they will get a
                                                ' message telling them so.
    END IF
    ' The next line will show the player's hit points after each attack.
    PRINT "HP"; HitPoints; "/"; MaxHitPoints
LOOP UNTIL done = 1                             ' The fight will keep looping
                                                ' until the player has won,
                                                ' or lost, or run away.
------------>>>> End cutting here.

        There you go, a simple enemy encounter routine.  It should be fairly
simple to optimize and add things to it such as spells and multiple hits, etc.


3.4 HOW DO I PUT THIS IN MY PROGRAM?
---------------->>>>

        This part shouldn't be too hard to figure out, but hey, if you want
me to do everything for you then here's one way to do it.  You should make the
routine above into a SUB.  To do this, simply run Qbasic, copy the code, load
up your program, go to the edit menu, and go to the new sub menu.  When it
asks you for a name, you can use whatever you want, as long as it isn't a
Qbasic command, number, etc.  Once you have done this, go to the main loop of
your program (Such as the one where the player walks around on the map) and
insert the following lines (Just don't forget to change CALL Battle to what-
ever name you gave the enemy encounter engine, so if you called it Fight,
then it would be CALL Fight):

EncounterChance = INT(RND * 5) + 1
IF EncounterChance = 1 THEN CALL Battle

As you can see, in these lines, the player has a 1 in 5 chance of meeting an
enemy whenever the program goes through the loop.  The best place to put the
above lines is right after your code for moving the player.  This way, enemies
will only attack when the player moves.

3.5 END NOTES
---------------->>>>

        Well, this concludes another lesson in RPG programming.  I hope that
you have found this enlightening.  Remember, there will be a part 2 to this
tutorial so please don't ask me 'how do I?' questions.  You can give me some
ideas as to what you would like to see in part 2 that is related to enemy
encounters, if I use your suggestion, you will get credit.

3.6 NEXT ISSUE
---------------->>>>

        To tell you the truth, I really don't have too many ideas for the next
issue.  I am thinking about explaining how to put shops and inns and other
town things into it.  If you have any better ideas then feel free to e-mail me
with your suggestions.  Good luck with your RPG programming.  That's it for
now.

                                                                   - DarkDread

+----------------------------------------------------------------------------+
| RPG Programming Tutorials (Programming RPG's in Qbasic)                    |
| Issue #3, June/July 1997                                                   |
| Copyright (c) 1997 DarkDreams                                              |
| Written by: DarkDread                                                      |
|                                                                            |
| This tutorial may be posted anywhere as long as it is left unmodified and  |
| the author is given credit.  The author is not responsible for the use of  |
| these tutorials.                                                           |
+----------------------------------------------------------------------------+

captain_squirrely

Post by captain_squirrely » Wed Aug 04, 2004 10:11 am

Are there any other ones?...I'm trying to find as many as i can...and I can't find any besides darkdreads.

User avatar
Nodtveidt
Veteran
Posts: 826
Joined: Sun Jul 25, 2004 4:24 am
Location: Quebradillas, PR
Contact:

Post by Nodtveidt » Wed Aug 04, 2004 11:24 am

Not a lot of people have written them due to their sheer simplicity.

User avatar
Pete
Site Admin
Posts: 882
Joined: Sun Dec 07, 2003 9:10 pm
Location: Candor, NY
Contact:

Post by Pete » Wed Aug 04, 2004 5:06 pm

Tsugumo's partner in crime, Frozen Emu, wrote an interesting article on creating RPGs once... It's called "How To Make Your RPG Not Suck," and it's available on QBasicNews (http://www.qbasicnews.com/articles.php?id=7).

It's not so much a tutorial, but more advice and commentary. You might find it a little helpful though.

Here's the section on Battle Engines.

Code: Select all

6) Game System Design
or: not every RPG has to be like FF4.

    <Bob, The Warrior?> Alright.. it's finally time I put this sword I've been lugging around since the begining of time to some good use! GERANIMO!! <screen zooms in, battle starts>

Here we see the classic battle screen. As you can see, our friend Bob, The Warrior? has the option of (A)ttackig, Casting a (M)agic spell, using his special (S)kill, or using an (I)tem.


There are two things wrong with the above picture, design-wise. No, the lousy graphics isn't one of them. See if you can guess.

If you guessed "Generic System" and "(A)ttack", you'd be right. Let's hit the "Generic" part first...

    <Bob, The Warrior?> Generic? no, no, my friend, you're wrong! For you see, instead of the usual spells of "Fire" and "Ice" and whatnot, MY spells are all based on poison! Therefore, the system is original!

No, Bob, it's not. Look at this side-by side $$$.

(FF4 VS Generic Battle Engine Image)

Note the similarities? I've said it before and I'll say it again (in boldface this time!) Not every game has to be Final Fantasy 4! There's nothing really wrong with the system, but if you want to set your game apart, it might be a good idea to try something new, or at least a little different. Here are some characteristics of the FF4 engine that you can try to modify:

   1. Menu: Attack, Magic, Skill, Item, Run, Block
   2. Each character is given a specific set of commands. (i.e. Paladin Cecil has "Attack", "White Magic", "Cover", and "Item"... Kain has "Attack", "Jump" and "Item", and so on)... maybe you could assign skills yourself, make customized characters? Learn new command abilities by completing quests?
   3. Characters learn increasingly powerfull magic at level up... all kinds of possibilities with this one.
   4. Character sprites on the right, monsters on the left. Maybe randomly throw sprites around the screen at the start of the battle, and do different amounts of damange based on distance? Have characters move around the screen as you fight?
   5. Numerical Spell Numbering (i.e. "Fire 2", "Ice 3", etc)
   6.

          <Bob, The Warrior?> I am truely the worlds finest warior! I can slash my sword at thin air, and a few seconds later, the enemy is hurt! HAHA!

      Ah, thank you bob.... another good example.
   7. "Active" battle system based on character speed.... maybe use speed to determine how far you can move before attacking? Make the system truely active, allowing to quickly select and use characters as you see fit?
   8. Stats: Strength, Agility, Intelligence, Wisdom, Defense, all increase at level up.... stats are easy to rearange, change, add too, or remove from.. you don't nessisarily have to base everything on stats.. maybe give stats through items (i.e. your defense is entirely determined by your armor, etc)?
   9. Weapons and armor are purchaced through stores at progressively better power.... maybe allow for upgrading weapons? Or make a lot of different weapons that aren't really any better than others, but have different stats (i.e. a katana can be used faster than a broadsword, but the broadsword does more damage)
  10. Each character can equip one/a few specific type(s) of weapon (i.e. Kain can equip swords, axes, and spears, FuSoYa can only equip staffs)
  11. You Die/Faint/Retreat when you run out of "Hit Points"... Maybe have damage to specific parts of body.. attacking arms lowers attack power, attacking legs lowers speed, attacking head causes death, etc... maybe have "hit points" in armor you're wearing, and once the armor breaks, next hit kills...
  12. Magic is cast by consuming a set # of "Magic Points"... maybe have magic consume itself.. (i.e. you can only cast Spell X Y times before you have to go to Z to get more... maybe have magic use spell components that you have to purchace, etc

    <Bob, The Warrior?> b-b-b-but... that's the very stuff that MAKES an RPG!!

No, that's the stuff that makes FF4. Think about some other RPGs out there... FF8 doesn't have MP.. you draw magic from monsters. In Secret of Mana, you don't buy new weapons, you find orbs to upgrade your current ones. In WildARMS, you learn magic by finding crests and going to special shops to have magic placed on them. You can see where I'm going with this.

Now.... no system is going to be different on ALL of those counts, but if you find your engine has most or all of those elements, try picking one and trying to revise it. Change the way you learn magic.. change the way magic is consumed... whatever it takes, just remember you don't have to make it exactly the same as Final Fantasy 4.

OK, now for problem #2... "(A)ttack, (M)agic, etc". This is a system for controlling battle that I've seen more times than I care to. This may just be a personal thing, but it just seems amature-ish to me. Ideally, your game should be able to be operated with the keyboard or a gamepad... and there's certinly not an "M" key on your controller (unless you're using a Jaguar one, but let's not go there.. heh). Basicly, you should only need to ever use 2 keys during battle: "Select" and "Cancel". These could be something like "A" and "S" on your keyboard, or Button A and B on the gamepad... or even better, allow the people to customize them... but anyway, that comes later.. let's focus on controll for right now. Basicly, to keep your game feeling like a game, and not a typing lesson, you have to keep people's eyes off thier keyboard. If they have to search around for keys and numbers all the time, it will eventually get annoying.

Placing in key controll isn't difficult by any means.. all you have to do is determine the keybaord scan codes for them, and set up a simple routine for placing a little arrow/finger/sword/whatever to point at the current choice. This doesn't just apply to battle either.. you should also apply the same to your menu system, shopping, and you should never have to type "Y" or "N" to respond to a question from an NPC. This shouldn't be a problem for you, you've programed a tile engine by yourself, right? Good. Go program it. I'll wait here. 

User avatar
Pete
Site Admin
Posts: 882
Joined: Sun Dec 07, 2003 9:10 pm
Location: Candor, NY
Contact:

Post by Pete » Wed Aug 04, 2004 5:13 pm

I found a second DarkDread battle engine tutorial that's better than the first one I got for you. It was originally printed in QB:TM.

It makes a battle engine that looks like this:

Image

You can find it here: http://hammer.prohosting.com/~coderz/71499.htm

Code: Select all

Hail!

Welcome to the second installment of the new RPG tutorials. Hopefully by now, most of the rust has worn off... and I'll be able to provide you all with a much better tutorial.

If you missed the previous one... It showed you how to write a scripting engine for your RPG. If you don't see it around here somewhere, ask zkman, I'm sure that he still has it. :)

This time around, I'll show you how to write a semi-active battle engine!

Before we start, let me point out that all of the code used in this tutorial is available in a zip file, along with some graphics, which demonstrate the engine in action. You can download it now, or read the tutorial first. If you're new to this, I would suggest reading the tutorial, as the code is not commented.

NOTE: This tutorial is aimed at a novice QB programmer. If you're a beginner, it shouldn't be too tough to figure out, But you may wish to brush up on your coding skills first.

Setting it all up.
First, we need to set up our variables, and the sub routines which we will use. Remember, always start with the two lines below. The $DYNAMIC meta- command is used to store arrays dynamically; This means that your array space is much more flexible. DEFINT A-Z defines all arrays as integers. This basically speeds up execution of the program.

'$DYNAMIC
DEFINT A-Z
Next, we delcare our sub routines. I'll explain what each of these is for once we get to them, so don't worry about it for now. You may also wish to know that this step is optional, as QB will automatically create these lines when you write the actual subroutines.

DECLARE SUB GetHandLocation ()
DECLARE SUB TimerDelay (Seconds!)
DECLARE SUB Battle ()
DECLARE SUB StatsBox ()
DECLARE SUB ChoiceBox (BoxType%)
DECLARE SUB DrawBattleScreen (ScreenType%)
DECLARE SUB InitBattle ()
DECLARE SUB InitRandomStats ()
DECLARE SUB InitSprites ()

Next, we must dimension our arrays, set up our global variables, and our constants as well. The first constants, are true and false. We will be using these for our 'flag' variables, to see check for certain happenings and tell the program to comtinue doing something if they are false, or to do something else if they are true.

CONST True = -1, False = NOT True

Next, we dimension the arrays we will use in the engine. These are needed to store our graphics. For this engine, there a four different enemies, and all are 32x32 in size. The two player characters are also 32x32, there are also three frames of animation for them. Next, our hand pointer is one 16x16 sprite. Finally, we need to allocate two arrays to hold parts of the graphic background which our sprites might be put over. This way, we can get what's behind the sprites, put or sprite on the screen, then, restore the background when we're done. Finally, we will also allocate space for masks in our sprite arrays.

Basically, This is the calculation I used to determine how big each array would have to be:

((SpriteXSize * SpriteYSize / 2) * NumberOfSprites) - 1

If you are not familiar how this all works, I would suggest reading a tutorial about sprites and graphics in SCREEN 13. It would help you understand more of what we're doing here.

DIM SHARED Hand%(258)
DIM SHARED Players%(4626)
DIM SHARED Enemies%(2570)
DIM SHARED BackSprite%(1028)
DIM SHARED BackHand%(129)

Next, we need to declare all the variables and dimension all of the arrays, which we will be using for our data. Basically, we need space to hold the players stats and enemy stats. Below, are the stats I deemed necessary for this engine to work. It's a good idea to give everything a name which will tell you what each variable is for; Ie. PlayerHP% is a much better variable name than, say A1% to hold the player's Hit Points.

DIM SHARED PlayerName$(1 TO 2), PlayerAlive%(1 TO 2), PlayerType%(1 TO 2)
DIM SHARED PlayerHP%(1 TO 2), PlayerMaxHP%(1 TO 2), PlayerMP%(1 TO 2), PlayerMaxMP%(1 TO 2)
DIM SHARED PlayerST%(1 TO 2), PlayerDF%(1 TO 2), PlayerAG%(1 TO 2)
DIM SHARED PlayerMS%(1 TO 2), PlayerMD%(1 TO 2)
DIM SHARED PlayerEXP&(1 TO 2), PlayerGold&
DIM SHARED PlayerX%(1 TO 2), PlayerY%(1 TO 2), PlayerGo%(1 TO 2)


DIM SHARED EnemyName$(1 TO 4), EnemyAlive%(1 TO 4), EnemyType%(1 TO 4)
DIM SHARED EnemyHP%(1 TO 4), EnemyMaxHP%(1 TO 4), EnemyMP%(1 TO 4), EnemyMaxMP%(1 TO 4)
DIM SHARED EnemyST%(1 TO 4), EnemyDF%(1 TO 4), EnemyAG%(1 TO 4)
DIM SHARED EnemyMS%(1 TO 4), EnemyMD%(1 TO 4)
DIM SHARED EnemyEXP%(1 TO 4), EnemyGold%(1 TO 4)
DIM SHARED EnemyX%(1 TO 4), EnemyY%(1 TO 4), EnemyGo%(1 TO 4), EnemyThere%

 

	
R
P
G
 
B
A
T

T
L
E
S
 	Next, we'll declare arrays to hold the X and Y location of our hand pointer. This will come in handy (No pun intended... really...) later.

DIM SHARED HandX%, HandY%

Now, we will initialize the random number seed. What is this? Well, we need the ability to generate random numbers, so that player and enemy damage isn't always the same. This line tells QB that we will be doing so later.

RANDOMIZE TIMER

Okay... Now we're more or less set up. The next parts of this tutorial will deal with individual parts of the engine. It is recommended that you read all of them carefully, as they are all intertwined closley together.

The rest of the main module.
This is how the rest of our main module would look. Note that some of this refers to subroutines I have not talked about yet... You may scroll down and read about these subroutines first... Then come back here.

Well, we have to tell QB to load our initialization routines next. To do this, we just call them by name like so:

InitSprites
InitRandomStats

Once we're initialized... Let's switch to screen 13 (320x200 resolution video mode, with 256 colours if you don't know) and call our main Battle sub to begin the fight:

SCREEN 13 Battle

Well... The battle's done. Now, the engine reverts to text mode and displays a short message and waits for a key press. After this, it exits. This last bit of code, you should leave out.

SCREEN 0: WIDTH 80
PRINT "Semi-Active Battle Engine. Created by DarkDread, 1999"
PRINT "You may use this in your programs... Just give me credit :)"
PRINT "Press any key to exit."
WHILE INKEY$ = "": WEND
END

The main battle sub.
This is the biggest sub routine in the engine, as it controls the flow of the battle. You may split this up into smaller routines, but for the sake of simplicity, I kept it this way for the tutorial.

Our subroutine starting code... QB will automatically do this for you, when you create a new sub, so you may skip this part if you wish.

REM $STATIC
SUB Battle

First, we must initialize our battle stats, then tell the program to draw the battle screen. It is done by calling the DrawBattleScreen. Note that we also pass a value of 1 to it. This way, the sub will know to draw a starting battle screen, and not an ending one.

InitBattle
DrawBattleScreen 1

Now, we begin our main battle loops.

DO

DO

These next lines, will calculate the agility of player and enemy characters that are still alive. This sub will take the agility of each character, and add it to a total every time it loops. Once a character's total is greater than 99... That character will be allowed a turn. Also, some of the player part of these lines, draws the little red and yellow status bar, which shows how much longer a player has to wait until their next turn.

FOR I = 1 TO 4
IF EnemyAlive%(I) THEN EnemyGo%(I) = EnemyGo%(I) + EnemyAG%(I)
IF EnemyGo%(I) > 99 THEN
EnemyGo%(I) = -1: GoThere% = True
END IF
IF EnemyGo%(I) = -1 THEN EXIT FOR
NEXT I
FOR I = 1 TO 2
IF PlayerAlive%(I) THEN PlayerGo%(I) = PlayerGo%(I) + PlayerAG%(I)
IF I = 1 THEN
Yellow = PlayerGo%(I) + 128
IF Yellow% > 228 THEN Yellow% = 228
IF Yellow% > 128 THEN LINE (128, 16)-(Yellow%, 18), 44, BF
IF Yellow% < 228 THEN LINE (Yellow% + 1, 16)-(228, 18), 40, BF
ELSEIF I = 2 THEN
Yellow = PlayerGo%(I) + 128
IF Yellow% > 228 THEN Yellow% = 228
IF Yellow% > 128 THEN LINE (128, 32)-(Yellow%, 34), 44, BF
IF Yellow% < 228 THEN LINE (Yellow% + 1, 32)-(228, 34), 40, BF
END IF
IF PlayerGo%(I) > 99 THEN
GoGo% = True
END IF
IF PlayerGo%(I) > 99 THEN EXIT FOR
NEXT I

Here, we have a little timer delay. This is to slow the battles down to a playable speed. If the delay wasn't here, you would notice that everyone attacks almost at once! The battles wouldn't be much fun then. You can raise this number to slow a battle down, and lower the number to speed it up.

TimerDelay .1

LOOP UNTIL GoThere% OR GoGo%

A character can now take a turn... If GoThere% is true, this means that it's an enemy's turn.

IF GoThere% THEN

Next, we check which enemy is allowed a turn, and if they are still alive or not... Just in case. Once we have found out, we draw the enemy mask on it's location. This will draw the enemy in all black, so the player knows which enemy is about to attack them.

FOR I = 1 TO 4
IF EnemyGo%(I) = -1 AND EnemyAlive%(I) THEN
PUT (EnemyX%(I), EnemyY%(I)), Enemies%(514 * 4), AND
This next line randomly selects which player the enemy will attack. This is where our random seed generator is put to work.

HitPlayer% = INT(RND * 2) + 1

After a player has been selected, these next lines check if that player is alive. Then a calculation is made based on the enemy's strength, the player's defense, and the help of a few random numbers, to determine how much damage the enemy has done. The random numbers are used just to add some variety to the damage. Instead of an enemy doing, say, 5 damage on a player all the time, they may do 3 sometimes, and 6 some other time.

Finally, we must take the damage done off of the player's HP. If the player's HP is less than one after this, it means they are dead. We then change the player's HP to 0 (So you don't see negative numbers in their stats) and make the PlayerAlive% variable of that player false. This way, the engine will know not to let that player have a turn, or to show their picture on the screen.

IF PlayerAlive%(HitPlayer%) THEN
Damage% = (EnemyST%(I) * 2 + (INT(RND * 4))) - PlayerDF%(HitPlayer%)
IF Damage% < 1 THEN Damage% = INT(RND * 2) + 1
PlayerHP%(HitPlayer%) = PlayerHP%(HitPlayer%) - Damage%
IF PlayerHP%(HitPlayer%) < 1 THEN
PlayerHP%(HitPlayer%) = 0: PlayerAlive%(HitPlayer%) = False
END IF
ELSE
IF HitPlayer% = 1 THEN HitPlayer% = 2 ELSE HitPlayer% = 1
Damage% = (EnemyST%(I) * 2 + (INT(RND * 4))) - PlayerDF%(HitPlayer%)
IF Damage% < 1 THEN Damage% = INT(RND * 2) + 1
PlayerHP%(HitPlayer%) = PlayerHP%(HitPlayer%) - Damage%
IF PlayerHP%(HitPlayer%) < 1 THEN
PlayerHP%(HitPlayer%) = 0: PlayerAlive%(HitPlayer%) = False
END IF
END IF

Next, we show the damage done on the appropriate player, and delay for 3 seconds. The delay is necessary to allow us to see how much damamge the enemy did, and on who.

IF HitPlayer% = 1 THEN
LOCATE 12, 36: PRINT Damage%
TimerDelay 3
ELSEIF HitPlayer% = 2 THEN
LOCATE 18, 36: PRINT Damage%
TimerDelay 3
END IF

Now that the enemy's turn is done, their agility meter must be set back to 0. We must also make GoThere% false, so the engine knows to return to the pervious loop. Finally, we must make our DrawNeed% variable equal to 1. We will use this variable later when calling the DrawBattleScreen so we know what parts of the battle we will need to redraw.

EnemyGo%(I) = 0: GoThere% = False: DrawNeed% = 1
END IF
NEXT I
END IF

Next, our routine checks if it was a player's turn right after the enemy's. If so, we must redraw the screen, using our DrawNeed% variable from before.

IF DrawNeed% = 1 AND GoGo% THEN DrawBattleScreen DrawNeed%

Now, we check if it's the player's turn to go. If so, we check which player will be going. Then, we draw a choice box for them which will give the player their options (RUN, ATTACK) and set up our hand pointer X and Y values. We then draw the player's mask over the player, this way, we will know which character's turn it is. We also draw our hand pointer next to the first choice in our choice box.

IF GoGo% THEN
FOR I = 1 TO 2
IF PlayerGo%(I) > 99 AND PlayerAlive%(I) THEN
ChoiceBox 1
HandY% = 5: HandX% = 10
PUT (PlayerX%(I), PlayerY%(I)), Players%(514 * 6), AND
PUT (HandX%, HandY%), Hand%(0), PSET
ChoiceMade% = False

Now, we have to create a loop. Then, we trap the keyboard buffer using INKEY$ to check which keys the player has pressed. This is all used so that the routine knows to move the hand pointer up or down, and which choice the player has selected when they hit enter.

DO
SELECT CASE INKEY$
CASE CHR$(0) + CHR$(72)
IF HandY% = 5 THEN
HandY% = 29
ELSEIF HandY% = 29 THEN
HandY% = 5
END IF
LINE (5, 7)-(24, 45), 0, BF
PUT (HandX%, HandY%), Hand%, PSET
CASE CHR$(0) + CHR$(80)
IF HandY% = 5 THEN
HandY% = 29
ELSEIF HandY% = 29 THEN
HandY% = 5
END IF
LINE (5, 7)-(24, 45), 0, BF
PUT (HandX%, HandY%), Hand%, PSET
CASE CHR$(13)

The player has hit enter. Now, we check where the hand pointer was when they pressed the enter key. If it was at Attack (HandY% = 5), then we allow the player to choose which enemy they wish to attack. This is done with the hand pointer, and another loop while checking the keyboard. You will also notice that before the handpointer is moved, the routine checks if which enemy is still alive and which one isn't. This is so that the hand pointer is never pointing to an enemy that is no longer there.

You will also notice that this is where we use our array to hold the back- ground graphics were the hand pointer is put. This is necessary because once the player moves the hand pointer, we need to restore the graphics that were there before the pointer was drawn.

After the player hits enter here... We determine which enemy they have decided to attack. Then, we make a calculation based on the player's strength and the enemy's defense... As usual, we throw in a few random numbers to add variety.

Finally, we show an animation of the player attacking (We use our second array to hold background graphics here) and display the damage done on the enemy they've attacked. Again, we pause after this for 3 seconds, so that we may see who was attacked by whom, and how much damage was done.

IF HandY% = 5 THEN
ChoiceBox 0
GetHandLocation
GET (HandX%, HandY%)-(HandX% + 15, HandY% + 15), BackHand%
PUT (HandX%, HandY%), Hand%(130), AND
PUT (HandX%, HandY%), Hand%(0), XOR
ChoiceMade2% = False
DO

SELECT CASE INKEY$
CASE CHR$(0) + CHR$(72), CHR$(0) + CHR$(80)
PUT (HandX%, HandY%), BackHand%, PSET
IF HandX% = 8 AND HandY% = 85 THEN
IF EnemyAlive%(3) THEN
HandX% = 8: HandY% = 135
ELSEIF EnemyAlive%(4) THEN
HandX% = 114: HandY% = 135
ELSEIF EnemyAlive%(2) THEN
HandX% = 114: HandY% = 85
END IF
ELSEIF HandX% = 114 AND HandY% = 85 THEN
IF EnemyAlive%(4) THEN
HandX% = 114: HandY% = 135
ELSEIF EnemyAlive%(3) THEN
HandX% = 8: HandY% = 135
ELSEIF EnemyAlive%(1) THEN
HandX% = 8: HandY% = 85
END IF
ELSEIF HandX% = 8 AND HandY% = 135 THEN
IF EnemyAlive%(1) THEN
HandX% = 8: HandY% = 85
ELSEIF EnemyAlive%(2) THEN
HandX% = 114: HandY% = 85
ELSEIF EnemyAlive%(4) THEN
HandX% = 114: HandY% = 135
END IF
ELSEIF HandX% = 114 AND HandY% = 135 THEN
IF EnemyAlive%(2) THEN
HandX% = 114: HandY% = 85
ELSEIF EnemyAlive%(1) THEN
HandX% = 8: HandY% = 85
ELSEIF EnemyAlive%(3) THEN
HandX% = 8: HandY% = 135
END IF
END IF
GET (HandX%, HandY%)-(HandX% + 15, HandY% + 15), BackHand%
PUT (HandX%, HandY%), Hand%(130), AND
PUT (HandX%, HandY%), Hand%(0), XOR
CASE CHR$(0) + CHR$(75), CHR$(0) + CHR$(77)
PUT (HandX%, HandY%), BackHand%, PSET
IF HandX% = 8 AND HandY% = 85 THEN
IF EnemyAlive%(2) THEN
HandX% = 114: HandY% = 85
ELSEIF EnemyAlive%(4) THEN
HandX% = 114: HandY% = 135
ELSEIF EnemyAlive%(3) THEN
HandX% = 8: HandY% = 135
END IF
ELSEIF HandX% = 114 AND HandY% = 85 THEN
IF EnemyAlive%(1) THEN
HandX% = 8: HandY% = 85
ELSEIF EnemyAlive%(3) THEN
HandX% = 8: HandY% = 135
ELSEIF EnemyAlive%(4) THEN
HandX% = 114: HandY% = 135
END IF
ELSEIF HandX% = 8 AND HandY% = 135 THEN
IF EnemyAlive%(4) THEN
HandX% = 114: HandY% = 135
ELSEIF EnemyAlive%(2) THEN
HandX% = 114: HandY% = 85
ELSEIF EnemyAlive%(1) THEN
HandX% = 8: HandY% = 85
END IF
ELSEIF HandX% = 114 AND HandY% = 135 THEN
IF EnemyAlive%(3) THEN
HandX% = 8: HandY% = 135
ELSEIF EnemyAlive%(1) THEN
HandX% = 8: HandY% = 85
ELSEIF EnemyAlive%(2) THEN
HandX% = 114: HandY% = 85
END IF
END IF
GET (HandX%, HandY%)-(HandX% + 15, HandY% + 15), BackHand%
PUT (HandX%, HandY%), Hand%(130), AND
PUT (HandX%, HandY%), Hand%(0), XOR
CASE CHR$(13)
IF HandX% = 8 AND HandY% = 85 THEN
EnemySelect% = 1
ELSEIF HandX% = 114 AND HandY% = 85 THEN
EnemySelect% = 2
ELSEIF HandX% = 8 AND HandY% = 135 THEN
EnemySelect% = 3
ELSEIF HandX% = 114 AND HandY% = 135 THEN
EnemySelect% = 4
END IF
ChoiceMade2% = True
END SELECT
LOOP UNTIL ChoiceMade2%

Damage% = (PlayerST%(I) * 2 + INT(RND * 4) + 1) - EnemyDF%(EnemySelect%)
IF Damage% = 0 THEN Damage% = INT(RND * 2) + 1
EnemyHP%(EnemySelect%) = EnemyHP%(EnemySelect%) - Damage%
IF EnemyHP%(EnemySelect%) < 1 THEN EnemyAlive%(EnemySelect%) = False
FOR J = 7 TO 8
PUT (PlayerX%(I), PlayerY%(I)), BackSprite%(514 * (I - 1)), PSET
PUT (PlayerX%(I), PlayerY%(I)), Players%(514 * J), AND
IF J = 7 THEN
PUT (PlayerX%(I), PlayerY%(I)), Players%(514 * (PlayerType%(I) + 1)), XOR
ELSEIF J = 8 THEN
PUT (PlayerX%(I), PlayerY%(I)), Players%(514 * (PlayerType%(I) + 2)), XOR
END IF
TimerDelay .5
NEXT J
IF EnemySelect% = 1 THEN
LOCATE 12, 4: PRINT USING "###"; Damage%
ELSEIF EnemySelect% = 2 THEN
LOCATE 12, 17: PRINT USING "###"; Damage%
ELSEIF EnemySelect% = 3 THEN
LOCATE 18, 4: PRINT USING "###"; Damage%
ELSEIF EnemySelect% = 4 THEN
LOCATE 18, 17: PRINT USING "###"; Damage%
END IF
TimerDelay 3
ChoiceMade% = True: DrawNeed% = 1

	 

A pic of the engine
	

	 
 	This is the rest of our first choice box. Here, if the player has chosen to run away (HandY% = 29) then we do a calculation based on the player's agility and the agility of a random enemy to detrmine if the player can succesfully run, or not. Why a random enemy? Simple, we want to allow the player to have a fair chance from running from any group of enemies that isn't based on the agility of the quickest or slowest enemy in the group. If the player can run, then RanAway is made true.

ELSEIF HandY% = 29 THEN
RandomEnemy% = INT(RND * 4) + 1
IF INT(RND * PlayerAG%(I)) + PlayerAG%(I) / 2 > INT(RND * EnemyAG%(RandomEnemy%)) + EnemyAG%(RandomEnemy%) / 2 THEN RanAway = True
ChoiceMade% = True
END IF
END SELECT
LOOP UNTIL ChoiceMade%

Well... A player's turn has occured, so now we must reset their agility meter to 0 and make GoGo% false so that our program knows to return to the previous loop.

PlayerGo%(I) = 0: GoGo% = False
END IF
NEXT I
END IF

After a player or an enemy has had their turn, this routine checks to see if either the enemies or the party have been wiped out. If the players are dead, then Lost is made true. If the enemies are dead, then Won is made true.

IF EnemyAlive%(1) = False AND EnemyAlive%(2) = False AND EnemyAlive%(3) = False AND EnemyAlive%(4) = False THEN
Won = True
ELSEIF PlayerAlive%(1) = False AND PlayerAlive%(2) = False THEN
Lost = True
END IF

Next, we check the situation (Players won, lost, ran away, or still fighting) and redraw the battle screen based on what has happened. After this is done, or DrawNeed% variable is reset to 0 again.

IF Lost = False AND Won = False AND RanAway = False THEN
DrawBattleScreen DrawNeed%
ELSE
DrawBattleScreen 2
END IF
DrawNeed% = 0

Finally... This whole battle loop continues until something occurs to end the battle. This will be the players winning, losing, or running away. If any of these happen. The engine exits the loop.

LOOP UNTIL Lost OR Won OR RanAway

Once the loop is exited, the engine determines what has happened. If the players lost, a losing message is displayed. If they ran away, a message is displayed saying so. If they won, a message is displayed saying so, then the total gold and experience they won is calculated, displayed, and added to the living characters totals.

IF Lost THEN
LOCATE 2, 2: PRINT "Annhiliated..."
ELSEIF RanAway THEN
LOCATE 2, 2: PRINT "You got away!"
ELSEIF Won THEN
LOCATE 2, 2: PRINT "Victory!"
WHILE INKEY$ = "": WEND
TotalEXP% = EnemyEXP%(1) + EnemyEXP%(2) + EnemyEXP%(3) + EnemyEXP%(4)
FOR I = 1 TO 2
IF PlayerAlive%(I) THEN PlayerEXP&(I) = PlayerEXP&(I) + TotalEXP%
NEXT I
DrawBattleScreen 2
LOCATE 2, 2: PRINT "Gained"; TotalEXP%; "experience."
WHILE INKEY$ = "": WEND
TotalGold% = EnemyGold%(1) + EnemyGold%(2) + EnemyGold%(3) + EnemyGold%(4)
DrawBattleScreen 2
LOCATE 2, 2: PRINT "Gained"; TotalGold%; "gold."
END IF
WHILE INKEY$ = "": WEND
END SUB

The ChoiceBox sub.
This sub is used to draw the box in the upper left corner of the battle screen. It is used to show the names of the enemies that are alive and also used to create the menu for choosing a character's course of action.

Notice that a BoxType% variable is passed to it. This tells the sub which type of box to draw. If a box with the enemy names is needed, it is called like this: ChoiceBox 0 If a box with the player's choices is needed, it is called like this: ChoiceBox 1

SUB ChoiceBox (BoxType%)

LINE (0, 0)-(120, 50), 23, B
LINE (1, 1)-(119, 49), 25, B
LINE (2, 2)-(118, 48), 24, B
LINE (3, 3)-(117, 47), 0, BF

IF BoxType% = 0 THEN
IF EnemyAlive%(1) THEN
LOCATE 2, 2: PRINT EnemyName$(1)
END IF
IF EnemyAlive%(2) THEN
LOCATE 3, 2: PRINT EnemyName$(2)
END IF
IF EnemyAlive%(3) THEN
LOCATE 4, 2: PRINT EnemyName$(3)
END IF
IF EnemyAlive%(4) THEN
LOCATE 5, 2: PRINT EnemyName$(4)
END IF
ELSEIF BoxType% = 1 THEN
LOCATE 2, 5: PRINT "ATTACK"
LOCATE 5, 5: PRINT "RUN"
END IF

END SUB

The DrawBattleScreen sub.
This sub is used to draw the battle screen, and all of the characters which are currently on it.

Notice that the ScreenType% variable is passed to it. This is used to determine what parts of the screen to draw.

SUB DrawBattleScreen (ScreenType%)

DEF SEG = &HA000
IF ScreenType% = 0 THEN
ChoiceBox 0
StatsBox
ELSEIF ScreenType% = 1 THEN
BLOAD "back1.bsv"
GET (PlayerX%(1), PlayerY%(1))-(PlayerX%(1) + 31, PlayerY%(1) + 31), BackSprite%(0)
GET (PlayerX%(2), PlayerY%(2))-(PlayerX%(2) + 31, PlayerY%(2) + 31), BackSprite%(514)
ChoiceBox 0
StatsBox
ELSEIF ScreenType% = 2 THEN
BLOAD "back2.bsv"
END IF
DEF SEG

IF ScreenType% < 2 THEN
Yellow = PlayerGo%(1) + 128
IF Yellow% > 228 THEN Yellow% = 228
IF Yellow% > 128 THEN LINE (128, 16)-(Yellow%, 18), 44, BF
IF Yellow% < 228 THEN LINE (Yellow% + 1, 16)-(228, 18), 40, BF
Yellow = PlayerGo%(2) + 128
IF Yellow% > 228 THEN Yellow% = 228
IF Yellow% > 128 THEN LINE (128, 32)-(Yellow%, 34), 44, BF
IF Yellow% < 228 THEN LINE (Yellow% + 1, 32)-(228, 34), 40, BF
END IF

FOR I = 1 TO 4
IF EnemyAlive%(I) THEN
PUT (EnemyX%(I), EnemyY%(I)), Enemies%(514 * 4), AND
PUT (EnemyX%(I), EnemyY%(I)), Enemies%(514 * EnemyType%(I)), XOR
END IF
NEXT I

FOR I = 1 TO 2
IF PlayerAlive%(I) THEN
PUT (PlayerX%(I), PlayerY%(I)), Players%(514 * 6), AND
PUT (PlayerX%(I), PlayerY%(I)), Players%(514 * PlayerType%(I)), XOR
END IF
NEXT I

END SUB

The GetHandLocation sub.
This sub is used to determine where to draw the hand pointer when the player chooses to attack an enemy. Notice that, it will check to see which enemy is alive before giving values to HandX% and HandY%. This is done so that the hand is never pointing to an enemy which is no longer there.

SUB GetHandLocation

IF EnemyAlive%(1) THEN
HandX% = 8: HandY% = 85
ELSEIF EnemyAlive%(2) THEN
HandX% = 114: HandY% = 85
ELSEIF EnemyAlive%(3) THEN
HandX% = 8: HandY% = 135
ELSEIF EnemyAlive%(4) THEN
HandX% = 114: HandY% = 135
END IF

END SUB

The InitBattle sub.
This sub is used to determine four enemies for the battle and to load their stats into the respective variables. Here, you can change the enemy names, as well as their statistics. You can make tough enemies, or weak enemies using this sub.

REM $DYNAMIC
SUB InitBattle

FOR I = 1 TO 4
EnemyType%(I) = INT(RND * 4)
IF EnemyType%(I) = 0 THEN
EnemyName$(I) = "Green Goblin"
EnemyMaxHP%(I) = 20: EnemyMaxMP%(I) = 5
EnemyST%(I) = 3: EnemyDF%(I) = 2: EnemyAG%(I) = 3
EnemyMS%(I) = 1: EnemyMD%(I) = 3
EnemyEXP%(I) = 10: EnemyGold%(I) = 4
EnemyAlive%(I) = True
ELSEIF EnemyType%(I) = 1 THEN
EnemyName$(I) = "Blue Goblin"
EnemyMaxHP%(I) = 20: EnemyMaxMP%(I) = 25
EnemyST%(I) = 2: EnemyDF%(I) = 1: EnemyAG%(I) = 4
EnemyMS%(I) = 5: EnemyMD%(I) = 4
EnemyEXP%(I) = 14: EnemyGold%(I) = 6
EnemyAlive%(I) = True
ELSEIF EnemyType%(I) = 2 THE

Quick! Spot the stoner rock referance in the next line! :)

EnemyName$(I) = "Orange Goblin"
EnemyMaxHP%(I) = 40: EnemyMaxMP%(I) = 0
EnemyST%(I) = 5: EnemyDF%(I) = 3: EnemyAG%(I) = 2
EnemyMS%(I) = 1: EnemyMD%(I) = 1
EnemyEXP%(I) = 18: EnemyGold%(I) = 10
EnemyAlive%(I) = True
ELSEIF EnemyType%(I) = 3 THEN
EnemyName$(I) = "Purple Goblin"
EnemyMaxHP%(I) = 25: EnemyMaxMP%(I) = 15
EnemyST%(I) = 3: EnemyDF%(I) = 3: EnemyAG%(I) = 3
EnemyMS%(I) = 3: EnemyMD%(I) = 3
EnemyEXP%(I) = 16: EnemyGold%(I) = 8
EnemyAlive%(I) = True
END IF
EnemyHP%(I) = EnemyMaxHP%(I): EnemyMP%(I) = EnemyMaxMP%(I)
NEXT I

END SUB

The InitRandomStats sub.
This sub is used to create random stats for the players just for the puprose of this engine. Most of these stats you will not need to use for your battle engine. They are there simply to give some usable character statistics in battle. You will want to replace these with your own character stats.

SUB InitRandomStats

PlayerName$(1) = "DarkDread": PlayerName$(2) = "Tyranny"
PlayerAlive%(1) = True: PlayerAlive%(2) = True

PlayerMaxHP%(1) = 52: PlayerMaxHP%(2) = 37
PlayerMaxMP%(1) = 11: PlayerMaxMP%(2) = 31

PlayerHP%(1) = PlayerMaxHP%(1): PlayerHP%(2) = PlayerMaxHP%(2)
PlayerMP%(1) = PlayerMaxMP%(1): PlayerMP%(2) = PlayerMaxMP%(2)

PlayerST%(1) = 7: PlayerST%(2) = 5
PlayerDF%(1) = 4: PlayerDF%(2) = 0
PlayerAG%(1) = 4: PlayerAG%(2) = 3
PlayerMS%(1) = 4: PlayerMS%(2) = 8
PlayerMD%(1) = 5: PlayerMD%(2) = 9

PlayerType%(1) = 0: PlayerType%(2) = 3

These next lines, you may wish to keep. They tell the engine where to put each enemy and character when drawing them.

EnemyX%(1) = 25: EnemyY%(1) = 75
EnemyX%(2) = 125: EnemyY%(2) = 75
EnemyX%(3) = 25: EnemyY%(3) = 125
EnemyX%(4) = 125: EnemyY%(4) = 125

PlayerX%(1) = 275: PlayerY%(1) = 75
PlayerX%(2) = 275: PlayerY%(2) = 125

END SUB

The InitSprites sub.
This sub is used to load the enemy, player, and hand pointer graphics into the arrays. These arrays are then used to display the necessary graphics by the battle engine.

SUB InitSprites

DEF SEG = VARSEG(Players%(0)): BLOAD "players.spr", VARPTR(Players%(0)): DEF SEG
DEF SEG = VARSEG(Enemies%(0)): BLOAD "enemies.spr", VARPTR(Enemies%(0)): DEF SEG
DEF SEG = VARSEG(Hand%(0)): BLOAD "handy.spr", VARPTR(Hand%(0)): DEF SEG

END SUB

The StatsBox sub.
This sub draws the player statistic box in the upper right corner of the screen. It is called from the DrawBattleScreen sub when the player's HP/MP have changed and need to be displayed. You'll also notice a calculation which checks to see if a player has 25% or less HP and changes the colour of the HP/MaxHP to a bright red if this is so. It is done this way to give the player a warning when the HP of one of the characters is falling to dangerous levels. You will also notice that the MP is displayed in a bright green colour. This is to easily differentiate between the HP and MP. Handy no?

REM $STATIC
SUB StatsBox

LINE (121, 0)-(319, 50), 23, B
LINE (122, 1)-(318, 49), 25, B
LINE (123, 2)-(317, 48), 24, B
LINE (124, 3)-(316, 47), 0, BF

LOCATE 2, 17: PRINT PlayerName$(1)
IF PlayerHP%(1) / PlayerMaxHP%(1) < .25 THEN COLOR 40
LOCATE 2, 30: PRINT USING "HP ###/"; PlayerHP%(1)
LOCATE 2, 37: PRINT USING "###"; PlayerMaxHP%(1)
COLOR 47
LOCATE 3, 30: PRINT USING "MP ###/"; PlayerMP%(1)
LOCATE 3, 37: PRINT USING "###"; PlayerMaxMP%(1)

COLOR 15
LOCATE 4, 17: PRINT PlayerName$(2)
IF PlayerHP%(2) / PlayerMaxHP%(2) < .25 THEN COLOR 40
LOCATE 4, 30: PRINT USING "HP ###/"; PlayerHP%(2)
LOCATE 4, 37: PRINT USING "###"; PlayerMaxHP%(2)
COLOR 47
LOCATE 5, 30: PRINT USING "MP ###/"; PlayerMP%(2)
LOCATE 5, 37: PRINT USING "###"; PlayerMaxMP%(2)
COLOR 15

END SUB

The TimerDelay sub.
This sub is used as a delay sub. It is much more precise than the SLEEP command, as is not system dependant like a FOR...NEXT delay. Of course, there are much more precise delay routines which you can do, but this one is good enough for the battle. One thing you may wish to add to this, is a small routine which checks for midnight rollover. I've left it up to you if you wish to include the midnight rollover check.

SUB TimerDelay (Seconds!)

CurrentTime! = TIMER
WHILE CurrentTime! + Seconds! > TIMER: WEND

END SUB

Putting it all together.
Well... Now that you have an idea of how this engine works, it's time to put it all together. If you look at the battle.bas file from the zip included with this routine, you'll see how it all looks in QB.

Basically, what you'll want to do, is take the stuff that's in the main part of the battle.bas file, and copy it over to your RPG. Of course, don't forget to change anything you may need to change. Then, copy all of the subs to your RPG as well. Now you're one step away from having semi-active battles in your RPG!

This is the easy part... Below, I've given you an example of how to make the random battles work in your RPG. You will likely have to change some lines, but this is the basic idea. Just put this code in your main loop:

RANDOMIZE TIMER
Fight% = INT(RND * 10) + 1
IF Fight% = 5 THEN
Battle
END IF

There you go. That's all you need to get started. Should you have any problems with the engine, feel free to e-mail me at darkdread@hotmail.com and I'll be happy to help you out. Good luck with your RPG!

Next time (and there WILL be a next time, mwuahahah!).

Well... These tutorials are written for YOU. So tell me what you want to read about next. Do you want to know how to create an Eye of the Beholder/ Legend of Lith 2 style 3D engine... or something else?

Incidentally, if you wish to check out the Serenity homepage for some free RPGs written in QB, you can go here

Cheers! 

User avatar
Pete
Site Admin
Posts: 882
Joined: Sun Dec 07, 2003 9:10 pm
Location: Candor, NY
Contact:

Post by Pete » Wed Aug 04, 2004 5:17 pm

I found all of these tutorials with a quick Google search, by the way. I'm sure there are more out there...perhaps you can find a non-QB specific battle engine tutorial that uses pseudo code?

The best suggestion I can give you is to download source code to QB RPGs and study how the battle engine works. This can be the best way to learn, especially if the code is well commented.

Anyway, I hope these help!

captain_squirrely

Post by captain_squirrely » Wed Aug 04, 2004 6:28 pm

AHHHH....THANK YOU SO MUCH, that was EXACTLY what i was looking for...thanx

DarkDread

Post by DarkDread » Wed Sep 22, 2004 9:39 pm

Ah, I remember those things. Nobody ever got my Orange Goblin joke. I was getting into a lot of stoner rock back then, heh.

Anyway, I did one more tutorial, it was on how to make a Legend of Lith 2 style engine, with included flexible source code. I don't think that one ever got published though. Pete, if you'd like, I can dig through my old backups and see if I still have it.

Oh, and cool to see this site again. It's almost made me code in QB... almost... had the IDE open and everything... was about to load my RPG libs (yeah, I made basic tile engine libs, if anyone wants these... pretty flexible, I'd say)... then I decided to write music instead. Oh well. Maybe one of these days... I still want to finish Mysterious Song 2... just need to do a battle engine. :)

DarkDread
Coder
Posts: 11
Joined: Wed Sep 22, 2004 9:40 pm
Location: Behind your bushes.
Contact:

Post by DarkDread » Wed Sep 22, 2004 9:42 pm

...and yeah, I found the motivation to register. Whee!

So yeah... if you need any of my old games/tuts, or whatnot, lemme know, and I'll see what I can dig up. I know I have a lot of stuff archived that isn't really floating around out there.

User avatar
Pete
Site Admin
Posts: 882
Joined: Sun Dec 07, 2003 9:10 pm
Location: Candor, NY
Contact:

Post by Pete » Wed Sep 22, 2004 10:45 pm

Wow, the great DarkDread makes an appearance at my humble little site! I'm glad you like it, even if it wasn't enough to motivate you to code in QB again. :)
DarkDread wrote: Anyway, I did one more tutorial, it was on how to make a Legend of Lith 2 style engine, with included flexible source code. I don't think that one ever got published though. Pete, if you'd like, I can dig through my old backups and see if I still have it.
Would I ever!! If you still have that tutorial, I would absolutely *love* to publish it in QB Express Issue #3.

I'm still working on extracting all the tutorials from the old QB mags and adding them to the Tutorials section, but once that's done, I'll drop you an email and see if there are any tutorials you've written that I haven't managed to find. I have actually gotten several specific requests for your RPG tutorials, so it would be great to have the complete collection.

DarkDread
Coder
Posts: 11
Joined: Wed Sep 22, 2004 9:40 pm
Location: Behind your bushes.
Contact:

Post by DarkDread » Thu Sep 23, 2004 2:27 pm

Allright, I'll have a look, and see what I can dig up, heh.

Oh, and use my gmail addy to email me: mzuchowski at gmail.com

I only use my darkdread at hotmail.com these days for MSN Messenger.

EDIT: Good news, I found it archived on a cd-r... the tutorial explains how to create a legend of lith 2 style engine, and also includes demo code for that (most of the tutorial is in the code actually, lots of comments there on what each part does)... and even better news, on the cd-r was one more tutorial, which I wrote after that one. This one shows how to do a p*t scrolling engine using gslib... and also has demo code which shows the engine in action. I'm not sure if this would be useful to anyone, but if you want, I can send you this one as well.

Oh, both of these are issue 3, and issue 4 of my, at the time, new rpg tutorials... the scripting engine is issue 1, and the battle engine is issue 2.

User avatar
Pete
Site Admin
Posts: 882
Joined: Sun Dec 07, 2003 9:10 pm
Location: Candor, NY
Contact:

Post by Pete » Thu Sep 23, 2004 5:32 pm

Yes, I would definitely like to have both tutorials!

As far as I can tell, you started writing this series for QB:TM in 1999, but only the first two tutorials were ever published. (Your second tutorial, on battle engines, appeared in the final issue of QB:TM. Your next tutorial was supposed to be included in the next issue, but QB:TM unfortunately stopped running before it could be published.)

Were these last two tutorials ever published anywhere else? If not, I would love to run them QB Express.

And if they were already published elsewhere, no harm done. They'd still be a great addition to the Tutorials section.

Anyway, you can email them to me at: pberg1 at gmail.com

Thanks a lot!

DarkDread
Coder
Posts: 11
Joined: Wed Sep 22, 2004 9:40 pm
Location: Behind your bushes.
Contact:

Post by DarkDread » Thu Sep 23, 2004 6:15 pm

Okay, I've sent them off to you. I modifed the text files of both, just to remove dead links to the old Darkness Ethereal site, and changed my contact email... also added a note that they were originally written in 1999, and 2001, so people know I'm not planning to do any new ones.

Anyway, as far as I can remember, neither of these got officially published, although I did have them available for download from the DE site way back when... but yeah, go ahead and publish 'em if you'd like.

EDIT: heh, it was fun digging through all the old DE stuff on that cd-r... even found the last ever build of Mysterious Song DX... that would've been a cool remake, check out the screenshot I took:

Image

...almost makes me want to finish it. :)

User avatar
Pete
Site Admin
Posts: 882
Joined: Sun Dec 07, 2003 9:10 pm
Location: Candor, NY
Contact:

Post by Pete » Fri Sep 24, 2004 2:27 pm

Wonderful!

Great tutorials -- they'll definitely be in QB Express.

That Mysterious Song DX screen looks awesome... even if you don't finish the game, you could always write a tutorial on how to do mock-3D landscapes like that. :)

Anyway, thanks a lot for the tuts!

DarkDread
Coder
Posts: 11
Joined: Wed Sep 22, 2004 9:40 pm
Location: Behind your bushes.
Contact:

Post by DarkDread » Fri Sep 24, 2004 2:34 pm

Glad you think the tutorials will come in handy. :)

As for doing a tut. on floor mapping, I couldn't... the floor mapper for MSDX was actually coded by Eclipzer. I know he wrote a tutorial on how to do it once, but I have no idea if it's still available somewhere.

barok_unlogged

Post by barok_unlogged » Sun Sep 26, 2004 12:15 am

stop torturing us!!! :wink:

So, you got a nearly finished Mysterious song DX and Mysterious song 2 on your hands, huh? Maybe you should upload the source so people could play it, and maybe even make their own ending. ;) j/k.

DarkDread
Coder
Posts: 11
Joined: Wed Sep 22, 2004 9:40 pm
Location: Behind your bushes.
Contact:

Post by DarkDread » Sun Sep 26, 2004 11:40 am

heheh, well, sadly, the main thing they're both missing is a battle engine (usually, that's something I put in last)... so they wouldn't be playable... just walkable... anyway, one of these days, I'll probably finish 'em up.

Nemesis
Coder
Posts: 36
Joined: Mon Aug 16, 2004 12:54 pm
Location: Colorado Springs, Colorado

Post by Nemesis » Mon Sep 27, 2004 4:12 pm

Pete, regarding that floor-mapping tutorial DarkDread mentioned,
that Eclipzer wrote. I beleive I have it somwhere on an old HD.
I'd have to reconnect it, and browse thorough it, but I know it's in there somewhere. So, if you're intrested, I will find the time
to atleast search for it. But I'm not going to bother if you don't
think you'll need it.

P.s...

Me and Eclipzer were pretty close, I even helped him on both his Nexus-13
libraries, and he helped me on some of my projects too. So, I don't think he'd mind if I gave you the tut. (In fact I know he wouln't,
since I beleive the tut was a public release off his old site :D )

Well, let me know,

Cya!

Post Reply