[WIP] Fight - Turned based arena combat

Fight

splashScreen

This game aims to be a simple turn based player vs cpu fighting game in an “arena” setting.
Currently the demo is playable but not feature complete.

The Source code can be found here: https://github.com/FreezingSnail/Fight

Controls

  • Up/Down to scroll the menus
  • A to select
  • B to go back

I’ve implement these features so far:

  • Player classes
  • Equiptable weapon inventory
  • Cost system for buying weapons and healing
  • Random enemy generation
  • stat and inventory room
  • Store room
  • Battle room
  • option to reset
  • leveling system for the player and enemys
  • Preset “boss” enemies at certain levels i.e. area rank ups
  • magic option for the mage/ boss characters
  • leveling system for enemies
  • Turn screens in the battle scene that describe what happened on the previous turn. I.e. you attacked enemy defended

My goal scope features still include:

  • Ability to save your character
  • Add a special attack for boss characters

Here are a few screen shots:

stats

FightScreen

battleinfo

storescreen

This is the first time I’ve written in c++ and only have minor experience in python and java so I’m aware that there are probably a lot of ways that I could write this better. Currently I’m at 2011 bytes of memory usage so any suggestions on ways I could reduce that would be appreciated.

As for code improvement goals i would like to change how I’ve implemented the scrolling menus, and buffer checks for button inputs. I believe I could move the notPressed checks into the main ino game loop and reduce my if statements in the gameEngine cpp. I would also like to change how they are displayed. I would like all the options in a menu list to be printed at once as you scroll but i’m unsure how to do that efficiently.

The gameEngine.cpp could be simplified and probably split up into 2 different files but I don’t know if that will be in the scope fir this particular project.

Also I would appreciate any comments on my usages of enums, structures, and classes as well. I originally had the player as a structure but found them not flexible enough to modify. The random enemies are constant besides their health so they could potentially be converted into structures. This would also require a rewrite for the battle mechanics however.

A side note, I’m not entirely clear on using fonts in this context. Can SIL Open Font License’d fonts be used freely along an MIT license in bitmaps like the splash screen?

7 Likes

Apparently

The License permits covered fonts to be freely embedded in documents under any terms, but it requires that fonts be packaged with software if they are sold.

So I would assume if you upload the fonts themselves to your github (maybe in their own file) and bundle the appropriate licence with them and make it clear that the fonts themselves follow a separate licence, then the use of the fonts make no impact on the licence used for your game.

(Obviously I’m not a lawyer. It would be really handy if we could attract a lawyer to the forums. :P)

Maybe some pirates or bandits too who have a good understanding of the law.
#equalopportunity

These sprites are pretty neat, I like them

1 Like

Guybrush Threepwood (Mighty Pirate-At-Law™) is never around when you need him.

2 Likes

I think since the software isn’t being sold the font would be fine to use, however I just changed to splash screen to hand drawn letters to avoid any potential conflict. I’ve added a few things including:

  • leveling system for the player and enemys
  • Preset “boss” enemies at certain levels i.e. area rank ups
  • magic option for the mage/ boss characters

As well as inverted the sprites and added new ones for the 3 boss characters. I was at around 93% ram usage but i managed to trim it back to 86%. I’m unsure how much more I can add with how I’ve set up the game so I think I’m going to work on rewriting the engines and classes to reduce the memory usage. I also need to play test and work on the balance . Ideally I envisioned the 3 character classes as a rps type interaction with the different move types to act as a way to for rock to beat paper. Currently the AI is random though so there isn’t much strategy besides attacking or using the special as a mage.

Here’s a gif of a playthrough of the first boss.
gameplay

They would be if you were selling it, you’d just have to pack the fonts with your ‘software’.

it requires that fonts be packaged with software if they are sold

Also from the opensource.org page:

The fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

It wouldn’t be in the open source initiative’s favour if such things weren’t possible. It’s actually a pretty decent licence, effectively all they ask is that the fonts are not sold on their own and that the licence for the fonts is made clear and distinct from the licence for the software.


Regarding space saving, a few tips:

  • If you don’t need negative values, prefer uint16_t over int16_t/int. Unsigned numbers are cheaper to work with.
  • Wrap every string literal that you’re shoving into a print call in the F macro. e.g. arduboy.print("Magic:");arduboy.print(F("Magic:"));
  • Not really a space issue thing but make sure your char * are const char *.
  • You can scrap upbuff, abuff and downbuff in favour of arduboy.justPressed. It returns true when a button is first pressed.
  • You could turn GameStatus into an enum class (scoped enumeration) and specify that it should take the size of a uint8_t. (The compiler might already be backing it with a uint8_t, but just to be sure.)
  • Some of the switch statements might be convertable to progmem array lookups.
  • Maybe explicitly store "null" as a progmem char array since you’re using it a lot.
  • If you’re feeling daring, try using a variation of this to handle your menus.
  • Use more else ifs, instead of just chains of if. The compiler can’t always spot when conditions are mutually exclusive.

I tried the justPressed functions, and they worked well for the menu scrolling but I encounter some ghost inputs for a so I’ve left the a-buffer. as well as switched to the unsigned ints. I’ve trimmed the memory usage about 10% so far.

I found your menu skeleton in another thread and look through it. I feel like I would have to rewrite too much at this point for how my game engine is tied with the actors but its definitely something I’d like to implement if I need menus in another game.

I’ve reworked the stat calculation to thin out my actor’s stored integers how ever it didn’t have as large of an impact on memory usage as I had hope.
However switching the stats to a structure has allowed me to implement an equation for leveling the stats instead using the baseStat as a seed. Though I have a bug now where assigning the weapon structure doesn’t seem to do anything in my pickClass function.

That might be an issue with your Arduboy,
or something related to how the function was being used.

Progmem or RAM?
Either way that’s a decent chunk of memory saved.

Fair enough.

At the moment you don’t seem to actually be doing anything with the weapon in pickClass.
All you’ve got is the line weapon; which effectively does nothing.

The savings is in ram. I peaked at 94% usage and am now at 84%.

I think you were looking at the constructor, Here’s an example from the testing branch:

  case 0:
  statSeed = {10, 10, 5, 5, 1};
  name = "warrior";
  hp = getStat(statSeed.totalHP);
  bmp = warrior_bmp;
  weapon = {1, 0, 0, "Sword", 0};

The statSeed structure assignment works fine however, the only difference is the equipment struct has a char. Originally I had it assign the starter weapon this way but had the same problem I do now. However in the master branch assigning the weapon structure like:

  inventory[0] = {0, 0, 1, "Staff"};
  weapon = inventory[0];

Works fine. Only the player needs an inventory however so I can’t use that method now that I moved the inventory array to to child player class.

I think I know the problem.

Part of the problem is that you’re using char * instead of const char * (which would be an error if the Arduino IDE didn’t use -fpermissive) and the other problem is that you’re not taking progmem into account.

You need to be storing the weapon names (and class/job names) in progmem, e.g.

const char WeaponNameSword[] PROGMEM = "Sword";
const char WeaponNameBow[] PROGMEM = "Bow";
const char WeaponNameStaff[] PROGMEM = "Staff";
const char WeaponNameAxe[] PROGMEM = "Axe";

const char ClassNameWarrior[] PROGMEM = "Warrior";
const char ClassNameMage[] PROGMEM = "Mage";

// elsewhere
case 0:
  statSeed = {10, 10, 5, 5, 1};
  name = ClassNameWarrior;
  hp = getStat(statSeed.totalHP);
  bmp = warrior_bmp;
  weapon = {1, 0, 0, WeaponNameSword, 0};

Then when you come to print them you need to cast them to const __FlashStringHelper * so the Print magic kicks in. (I usually write a helper function for that, in the case of EasterPanic I called it FlashString.)

Using the F macro automatically places strings in progmem and converts them to const __FlashStringHelper *, but there’s no mechanism for preventing duplicates so it can lead to wasted space.
Strings without the F macro (i.e. bare strings) are stored in progmem, then copied into RAM at runtime (as discussed here).

I finally got around to changing my print statements to use the F macro. It saved another ~20% of memory. At the time I didn’t know strings were stored in ram. I worked on some of the visuals to make the game more appealing though they’re still very WIP. Fights are a little better passed now that I’ve added a pause after each turn for information on what the opponent did the previous turn but it could be clearer still.

I’ve sorted out most of the bugs though the players wallet occasionally sets to 0. I haven’t looked into yet. I haven’t implement Pharaps flashstringhelper function either yet. I also need to spend some more time with my stat table as the balancing feels terrible. It looks like I’ll have a little space left over once I fix the leveling balance so in the meantime I’ll try and think of another feature or two I could add. The leveling system could use a EXP component as well instead of matching the “Arena Rank”.

2 Likes

I’ve made another commit to the master branch. This includes:

  • Arena level from dictating the player stats to a separate character level.
  • This level also dictates the random mobs levels (+/- 1 of your own) you can fight. The boss characters have a set predetermined set level.
  • Added a consumable potion item. The player can carry 2 at a time and using one counts as a move in a fight.
  • The potions are only represented graphically in the stat screen and in the underused window in the fight screen.
  • I tweaked the damage formulas and stat formula but they still need some work.
  • Added two more normal mobs and a fourth boss.

I added an extra image showing the potions and battle dialog to the op.
There is a bug that using a potion will also carry your previous move that I just noticed while recording a gif for this post as well.

Right now I would say the mechanics of the game and progression are sorted but it doesn’t seem that fun. most of the mobs feel similar as well. Any input on what I could change to make it feel more fun to play would be appreciated.
Perhaps I should try rewriting the fight mechanics again to include more movetypes so I can finally add a class/mob specific move.

3 Likes

Sometime just a little interaction from the player can make it more fun, like a button that has to be pressed at the right timing to do a critical hit maybe? just a small bar with a moving square and you need to press A when it’s in the middle? i don’t know in some turn based RPG i saw that and thought it makes it more interactive. Harder but more powerful moves might have it go faster so that it’s harder to time right… just a thought…

Personally I usually prefer RPGs where I only have to worry about being strategic rather than having to have button mashing skill (mainly because I’m a lazy sod).


That said, one of my favourite RPGs that does this is Paper Mario The Thousand Year Door.
Here’s the first major boss battle as an example.

The better the player is at using the right buttons at the right time the more ‘stylish’ the moves are and the more the audience is impressed.

@Vampirics I was actually toying with the idea of something like that. I was thinking more like how in ff8 when you use a guardian force tapping a increases the damage they do.
At the moment I’m not really happy with how I’ve implement a few things as it is making it difficult to add to the combat so I think I’m going to focus on rewriting a couple parts of the game to make it more expandable. Right now the only difference between magic and normal attacks are the damage formulas and the stat it pulls from. I would like if I could add a gimick for at least each boss (i.e. the slime boss poisons you) and the 3 classes. At the moment though I have more reading to do if I want to add stuff like that.

I decided to have a go at compiling this and the first thing that struck me was the number of warnings, so I’ve decided to systematically eliminate every single warning.
(Except for that annoying one in EEPROM that I can’t do anything about and the Arduino people refuse to fix).

I’ve got through most of the warnings, the only remaining warnings are mis-matched sign comparisons (i.e. signed types are being compared with unsigned types and vice versa), missing field initialisers, a n unhandled case in a switch and a few unused variables (though I expect that’s a side effect of being unfinished rather than a bug).

I’m not sure how you want to handle the remaining ones.
Most of the missing field initialisers are missing weapon names/costs.

The sign issues are:

  • The player’s wallet being compared with inventory cost in the store
  • HP being signed while stats are unsigned

I can see a few places where you could claw back some memory, but to be honest I think changing some of the way your handling certain mechanisms would help the most.

1 Like

I’ve been using the arduino IDE so I never saw those errors. Good to know though, thanks. Tbh I think I’m going to completely scrap the actor classes and fightEngine.cpp to start over with a structure base instead of a class. If I remember right I only switched to creating a class because I couldn’t figure out how to change a variable in a structure with a function. That was before I knew what passing with a pointer was though.

Since I switched the stats to being calculated on the fly from their seed base stat I don’t really feel like it warrants a class anymore when the wallet and exp counters could easily be variables in my global file or something along those lines.

The store needs to be broken up into separate functions as well. I wanted to add item drops from the bosses at some point so it makes sense to make a function to add items to the inventory instead of having the code bare in the store room.

I think changing the way a “player” is created to using a constant structure as a seed and then a few game variables will make it easier to add a save function too. Instead of saving my player object I can just save a variable that can coincide to the class structure in an array of the 3 choices, and an int for exp, wallet, and current health. Generating a mob to fight should be easier too as I could just pull from an array of mob structures instead of how I have it now overwriting an actor object.

I was thinking of doing something similar too with the inventory. If I make a constant array of all my items Instead of duplicating them into an inventory array I could use an array of ints as the inventory and then use them to look up what the modifiers are whenever I need them.

That’s what I’m using to compile.

Most likely they either wizz by so fast you don’t notice or you haven’t got all warnings on:

classes and structures are effectively the same thing.
class and struct are equivalent, the only difference is the members of class are private by default and members of struct are public by default.

I’d advise against that.

If you’re worried about space, classes aren’t heavy,
they take no more memory than the variables that comprise them and their functions take as much space as normal functions.
(In fact member functions are actually normal functions with a hidden implicit argument.)

Also generally it’s cheaper to pass arguments to a function than to make a function refer to a global.

If you find globals easier for now then fair enough,
but classes are what you should be aiming for/working towards.

Better yet, make an Inventory class.
(Or a list class.)

But having functions to access the item storage instead of manual access is a step forwards at least.

While I agree that having a base structure and calculating stats from the base would be good for reducing memory, writing a structure to EEPROM is as easy as EEPROM.put(address, player), so I don’t see how the variable thing (which would require several calls to put or update) would be easier.

Actually, you’d still have to have an overwritable object to be able to change the enemy’s health.
Having an array of monster stats and then copying those stats into an enemy object would be a good way of doing that.

Or having an enemy object that only has a health variable and a monster type id so it can pull stats from the array of monster definitions but still deplete its health.

e.g.

(Essentially a flyweight crossed with a type object.)

Though of course, if you wanted the monster to be able to boost its attack or defence somehow, you’d have to have fields for those in the Monster class.

That one I certainly agree with.
Having ‘item ids’ is quite a common technique.
(Though an enum class would be more useful than int.)


Generally feferences are better.
They’re easier to use and the argument can’t be null.

i.e.

void someFunction(Enemy * enemy)
{
  enemy->doSomething();
}

void otherFunction()
{
  Enemy enemy = Enemy();
  someFunction(&enemy);
}

is equivalent to:

void someFunction(Enemy & enemy)
{
  enemy.doSomething();
}

void otherFunction()
{
  Enemy enemy = Enemy();
  someFunction(enemy);
}

but in the latter case you can’t do this by mistake:

void someFunction(Enemy * enemy)
{
  // bad things happen when enemy is nullptr
  enemy->doSomething();
}

void badFunction()
{
  someFunction(nullptr);
}
1 Like

Quick question, I feel like I’m missing something obvious to get this to work before I go any farther.
I’ve created to arrays to test out the item Idea as it will be the simplest to swap over too vs some of the other changes I mentioned.

const char WpnNmKleaver[] PROGMEM = "Kleaver";
//                             atk, def, spc, name, cost
const weapon PROGMEM kleaver = {5, 2, 0, WpnNmKleaver, 10};
const weapon PROGMEM weaponArray[] = {kleaver};
const weapon PROGMEM weaponArray2[] = { {5, 2, 0, WpnNmKleaver, 10} };

If I attempt to print weaponArray2[0].cost I’ll get the correct value, 10. However weaponArray[0].cost prints 0. Why is this?

Depends how you’re printing.

I’m guessing either the fact kleaver is in progmem is interfering with it being copied, or the print system isn’t correctly pulling it out of progmem.

To print it properly you’d have to be doing something like:

arduboy.print(pgm_read_word(&weaponArray[0].cost));

(Assuming the type of cost is uint16_t.)