Turn based battle

Ok guys I think ive learned enough to try and make my own turn based battle system functions but I don’t know where to start. I do know I need these features.

Monsters must be randomly picked

Monster picks should based on the map area or room in which the player resides

Towns and villages should be battle free zones

I know that first I should build some kind of structure for the monsters with the statisics But should I build several structures with a few monsters in each structure to call randomly or should I give a structure for each monster.

I was thinking of a for loop that checks for room number then increments the monster id number to a random number for the monster Id between 1 and how ever many monsters there are in that structure.

Have you finished your other game yet?

Is this going to be an Arduboy-based game or another ESP32 game?

Its good that you are taking the time to plan your next game properly as it will make things easier in the long run.

With classes or structures, I would personally keep them discrete and simple. I would have a single structure for the player, a single structure for the monsters, a single structure for the rooms, etc. If you need to have variations of monsters, look at building a base class and extend this to incorporate the necessary elements that describe the different monsters.

This way you can right functions that take these classes /structures as parameters and have some nicely encapsulated logic. For functions that need a monster as an input, you can declare the input as a ‘BaseMonster’ and act on all monster types or pass a ‘BigHairyMonster’ and act specifically on it. You might like to read up on inheritance and polymorphism.

2 Likes

This article might be of use when trying to decide how to structure your ‘monsters’:

http://gameprogrammingpatterns.com/type-object.html

Nice reference there @Pharap

1 Like

Believe it not I actually read everything. Even the polymorphism. I haven’t memorized it all yet so don’t test me just now. I need to read over the page that pharap linked me to. I’m not understanding how to use it just now. But ill get there.

thought I would start with something simple then graduate to more efficient. So I wrote a function to pick a random monsters ID…

#define Monster_Cave_spider      1
#define Monster_Desert_bat       2
#define Monster_Desert_hare      3
#define Monster_Desert_scorpion  4
#define Monster_Desert_slig      5
#define Monster_Face_dancer      6
#define Monster_Jacruto_outcast  7
#define Monster_Kangaroo_mouse   8
#define Monster_Laza_tiger       9
#define Monster_Sandworm         10

struct Monster {
  int x;
  int y;
  int w;
  int h;
  uint8_t MonId;
  uint8_t Attack;
  uint8_t Defense;
  uint8_t Health;
}

Monster monsters[] = {
{143, 150, 34, 24, 1,  10, 5,  20},
{143, 150, 48, 40, 2,  10, 5,  20},
{143, 150, 34, 22, 3,  15, 10, 30],
{143, 150, 44, 34, 4   20, 15, 40},
{143, 150, 28, 36, 5,  20, 15, 40},
{143, 150, 34, 40, 6,  20, 20, 50},
[143, 150, 34, 32, 7,  20, 30, 60},
{143, 150, 38, 36, 8,  10, 5,  20},
[143, 150, 44, 32, 9,  30, 40, 70},
{143, 150, 24, 24, 10, 40, 50, 80}, 
};

void pickMonster(uint8_t MonId) {

  for (uint8_t i = 0; i < 0; i += random(1,11)){
   Serial.println(i); 
  }
  return MonId;
}

its untested but it should return a monsters ID.

Right … so in this function, you pass it a monsterId, it prints out some random numbers then returns you the same monster ID you passed it. Doesn’t sound too random to me.

The for loop is just meaningless … first, the i < 0 will not work as it is uint8_t and therefore numbers can not be less than zero. Secondly, the i += random(1,11)) just adds some random number to the variable i. Why?

Couldn’t this all be simplified to:

void pickMonster() {

  return random(1,11);

}

And on that note, I feel I might be bowing out of this thread too.

1 Like

Its my first try at something ive been trying to learn. Sorry you feel that way but thank you for all the info.

the idea was that I was leaving the function at idle or zero then having it pick a random number to represent the monsters id pick. looks like I still need to do some more reading on the subject of integers unsigned and other wise and what they can and cant do.

Inheritance and polymorphism aren’t something you can understand in a few hours. They’re something you need to practise with and play around with until you get a feel for how they work and how they can be used.

Most places use the clichéd ‘mammal inherits animal’ approach, which I think isn’t very representative of their actual usage.
A better example is a data stream (e.g. C++'s std::istream and std::ostream, or C#'s Stream class).

A good example of real-world polymorphism is Arduino’s Print class.

It specifies the abstract virtual member function size_t write(uint8_t), which its child classes must override.
write's only argument is a byte that represents an ascii character.

Print defines several print functions that use write to print different values. For example, print(int, int = DEC) will turn the first argument (an integer) into a sequence of characters representing that number in a human-readable format, and write those characters using write.

How write actually behaves is defined by the child class.
For example, Arduino’s HardwareSerial class (the class that Serial is an instance of) overrides write to write the provided character out over the serial communication line (typically a USB connection).

In contrast, Arduboy2’s implementation of write prints the provided character to the screen (and handles '\n' and '\r' specially).

So if you had a variable Print * printer (a pointer to print), and then called printer.print(45);, the actual behaviour would depend on whether printer pointed to an instance of Arduboy2 or to Serial (or to some other class inheriting Print).


Even if you had used int8_t (a signed integer) instead of uint8_t (an unsigned integer), your loop would still be useless because you initialise i to 0, and 0 is evidently not less than 0 so the i < 0 condition is immediately false so the body of the loop is never entered.

And then, even if you initialised i to something negative, e.g. int8_t i = -1;, then the loop body would just print -1 out to serial, add a random number between 1 and 10 to -1 (effectively subtracting 1 from the random number) and breaking out if it’s 0 or larger (i.e. if it’s positive).

And even if there wasn’t an issue with the loop’s settings, you’re still only printing the loop variable over serial, you’re not actually using it for anything.

Like I said ill have to read over every thing a few more times.

on the for loop… For the sake of learning, how would I write it so it would be true and work the way I want?

Ive been looking at dark and under for an example of what looks like a simpler (than arduventure) battle system. I would understand it more if I could find the structures that have the player and monster info. I see functions like getMaxattack but where does it get the value for it. The same with hp and so on

What is it that you want it to be doing?

If you just want to pick a random value then what @filmote posted will work. If you also want to print that value, you’d need to save it to a variable, print the variable and then return the variable.

If you want to do something else, it’ll depend on what your objective is.

Good choice.

(Though it’s a bit messy in places. Cramming something that complex into the Arduboy’s memory took a lot of work and there’s a lot of code involved.)

The monster stats are stored in MapData.h:
https://github.com/Garage-Collective/Dark-And-Under/blob/master/src/levels/MapData.h

They can be modified with the level designer.

Remember back along when I was talking about making your engine depend upon world data? This is exactly what I meant.

Dark & Under’s levels are heavily modable because the system has all the logic in the code and then all the level-specific info (or ‘room-specific info’ depending on your outlook) is stored as if it were an external data file.


Incidentally we used an entity ID system using enum class (that other thing I mentioned ages ago), but in a way that’s similar to the type object pattern that I linked to earlier.

The end result is that the system requires more effort to add new monsters, but it uses less RAM because the ID enum is 1 byte whereas a pointer would have been 2 bytes.

In your case you wouldn’t have that worry because you’ve got much more RAM at your disposal, but an ID might still be useful if you need some kind of bestiary.

looks like Mapdata.h also holds the player starting data here…

#define MAX_LEVEL_COUNT 3
#define START_HP 20
#define START_AP 2
#define START_DF 2
#define MAX_HP 50
#define LEVEL_UP 50

Aso I loaded up d and u to my arduboy .In d and u initialize.h there is a function called load enemies. Am I to assume that this function places the enemies physically in the dungeon in various spots?

next… in this function it calls hp loss twice. Why? And where does it initialize the player stats in order to be able to deduct the health loss.

I can tell you for a fact there’s no initialize.h without even looking.
(I’m English, @filmote’s Australian, neither of us follow Oxford style.)

I think you mean DarkUnder_Initialise.ino.

No need to assume, most functions are qute self-descriptive.

https://github.com/Garage-Collective/Dark-And-Under/blob/master/DarkUnder_Initialise.ino#L141

If you’re still talking about loadEnemies then no, it doesn’t.
If you’re talking about some other function, which function?

https://github.com/Garage-Collective/Dark-And-Under/blob/master/DarkUnder_Initialise.ino#L49

(The player is called myHero for historical reasons.)

ooops sorry I meant d and u initialize.ino. I use header files for my source so I’m used to saying .h. I started out in the beginning using .ino files but I never could figure out how to include them properly and my questions were ignored on the Arduino site so I worked around the problem.

and this function in d and u battle.ino

    case GameState::Battle_EnemyAttacks:  // ---------------------------------------------------------------------------------------------------------------------------------------
      {
        arduboy.drawCompressed(12, 12, fight_scratch_Mask, BLACK);
        arduboy.drawCompressed(12, 12, fight_scratch, WHITE);

        uint8_t hpLoss = random(0, ENEMY_MAX_ATTACK);
        font3x5.print(F("YOU TAKE\n"));
        font3x5.print(hpLoss);
        font3x5.print(F(" DAMAGE!"));
        font3x5.setCursor(33, 26);
        font3x5.print(hpLoss);
        myHero.setHitPoints(myHero.getHitPoints() > hpLoss ? myHero.getHitPoints() - hpLoss : 0);
        gameState = GameState::Battle_PlayerDecides;

        if (myHero.getHitPoints() == 0)  gameState = GameState::Battle_PlayerDies;
      }

      break;

if its a serial print I still don’t understand why its listed twice

DarkUnder_Initialise.ino

Specifics are important.

You don’t include them, the IDE works out how to include them (somehow).

Personally I hate .ino files because of the special processing that the Arduino IDE does.

It’s not a serial print, it prints to the screen.
Like I said earlier, lots of different classes inherit Print.
Serial prints out over the serial connection, normally an instance of Arduboy2 (typically named arduboy) would print to the screen, but in Dark & Under’s case we employed a special 3x5 font made by filmote which is in its own custom class (viewable in /src/fonts) and font3x5 is the instance of that class.

The first case prints to the text box on the right hand side, the second case prints on top of the monster sprite.

ok on this line of code …

        uint8_t hpLoss = random(0, ENEMY_MAX_ATTACK);

Could it be made to say…

        player.health - random(0, ENEMY_MAX_ATTACK);

It could be made to say:

player.health = player.health - random(0, ENEMY_MAX_ATTACK);

As long as you did not want to display to the player that they lost X HP points.

Hmmm hadnt really thought that far ahead. Would be useful to be able to print the value deducted.

I was thinking it would be easier for ME to use structures for the monsters then have the game pick a random monster. Then Deducting the data from the structures.

But as said at the top the printing the deduction would be nice.

Gonna keep digging. I want to write the code myself for this one But im open to suggestions of course

You can.

In fact the code already does that, twice.

font3x5.print(F("YOU TAKE\n"));
font3x5.print(hpLoss);
font3x5.print(F(" DAMAGE!"));
font3x5.setCursor(33, 26);
font3x5.print(hpLoss);

Which could also be:

player.health -= random(0, ENEMY_MAX_ATTACK);

(But if we did either of those we wouldn’t be able to tell the player how much HP they’d lost.)

The reason

doesn’t work is because that is just an expression, not a statement.
It creates a value, but it doesn’t do anything with that value - no variables are changed.

Whereas with player.health = player.health - random(0, ENEMY_MAX_ATTACK); or player.health -= random(0, ENEMY_MAX_ATTACK); both are statements because there’s an assignment involved.
The former case is the verbose way, the second case uses the compound assignment operator -= which effectively means the same thing.

1 Like

ok but filmotes answer in post #16.

I get what you mean about expression verses statement. But I’m gonna go ahead and do some reading

ok I think I see how to print it now

        uint8 t hploss = player.health = player.health - random(0, 20);
        Serial.println(hpLoss);
        etc.....................

that cant be right though

ok so the above compiles and I put a serial print to print the hploss but it spits out a string of integers. The integers it prints are way above 20.