Developing Multiplatform Game with LibGDX, part 23: stat logic and display

Assigning meaning to stats

So far in our game, we can upgrade the character and unlock new ones. But the upgrades do nothing. Let’s actually make them affect the game.

Player’s hp, hp regen, damage from attack and bonus spawn time is going to depend on character level. Go to our CharacterRecord class. Introduce four functions to get each value. We are going to pass the level of the character, the function is going to return the value.

For HP, damage and regen – let’s make linear functions. First, get rid of maxPlayerLives and playerDamage in GameProgress. While you’re there, also get rid of playerLives = 3 in our „Reset” function. Then, go back to CharacterRecord and implement the three functions.

public int getMaxHp(int level)
{
    return 3 + level / levelsForHpUpgrade;
}

public int getDmg(int level)
{
    return 1 + level / levelsForAttackUpgrade;
}

public int getHpRestored(int level)
{
    return 1 + level / levelsForHpRegenUpgrade;
}

Pretty straightforward. Take the base value, add level divided by the levels necessary for upgrade, get result. Looks good. What about bonus spawning? We don’t want the bonus time to go down drastically, so it makes sense to introduce diminishing returns.

public float getBonusSpawnReduction(int level)
{
    int bonusSpawnLvl = level / levelsForBonusSpawnUpgrade;
    return bonusSpawnLvl / (30 + bonusSpawnLvl); // 30 enables diminishing returns, x / ( x + 30)
}

30 is just a magical number. You can make it any value you want. All we have to do is put it into necessary places now.

Go to our Player class. Change the lines

max_lives = GameProgress.maxPlayerLives;
set(res.playerSprites.get(CharacterRecord.CHARACTERS[GameProgress.currentCharacter].name));

To

max_lives = GameProgress.getPlayerMaxHp();
set(res.playerSprites.get(CharacterRecord.CHARACTERS[GameProgress.currentCharacter].name));

In our GameProgress class, define the following function:

public static int getPlayerMaxHp() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getMaxHp(levels[currentCharacter]);
}

We just lookup the current character record, then try to get max hp depending on the level (we’ve also made the sprite setting line a bit more readable. Now, let’s modify GameLogic for our new character stats.

First, go to our AssignPlayerPosition function. There are two places that we should change. First,

else if (currentBonus.getBonusType() == Bonus.BONUS_TYPE_ATTACK)
{
    enemy.takeDamage(GameProgress.playerDamage);

Let’s replace GameProgress.playerDamage with static function of the similar name.

else if (currentBonus.getBonusType() == Bonus.BONUS_TYPE_ATTACK)
{
    enemy.takeDamage(GameProgress.getPlayerDamage());

In our GameProgress, create a function getPlayerDamage():

public static int getPlayerDamage() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getDmg(levels[currentCharacter]);
}

While you’re still there, do the same for hp restored per bonus:

public static int getPlayerHealthRestored() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getHpRestored(levels[currentCharacter]);
}

Go back to logic. In BONUS_TYPE_HEALTH check, replace player.addLives(1)  with

player.addLives(GameProgress.getPlayerHealthRestored());

Now, the only thing left to modify is our BONUS_SPAWN_INTERVAL. We’re going to set it’s value during GameLogic construction (since player cannot levelup during the battles).

Remove the default value of 2.0f from BONUS_SPAWN_INTERVAL and remove keyword static.

private final float BONUS_SPAWN_INTERVAL;

Instead, initialize BONUS_SPAWN_INTERVAL in GameLogic constructor.

BONUS_SPAWN_INTERVAL = 2.0f * (1 - GameProgress.getPlayerBonusReduction());

The function itself:

public static float getPlayerBonusReduction() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getBonusSpawnReduction(levels[currentCharacter]);
}

 

Displaying Stat Information

 

That’s it. The changes should affect the game now. But we cannot really see them in character selection scree. Let’s fix that! First, move the hero sprite to the left by adjusting its X position:

heroSprite.setPosition((uiStage.getWidth() - heroSprite.getWidth()) / 4,
                       (uiStage.getHeight() - heroSprite.getHeight()) / 2);

Then, move the arrow button to the top of the screen (otherwise they just start getting in the way).

prevBtn.setPosition(uiStage.getWidth() / 6 - prevBtn.getWidth() / 2, uiStage.getHeight() * 5 / 6);
//...
nextBtn.setPosition(uiStage.getWidth() * 5 / 6 - nextBtn.getWidth() / 2,
        uiStage.getHeight() * 5 / 6);

Then, let’s actually work on adding the labels with stats. Since we have 4 stats which will involve repetitive label creation, make a function to do that:

private Label prepareStatLabel(String text, float x, float y, Label.LabelStyle textStyle)
{
    Label lbl = new Label(text, textStyle);
    lbl.setAlignment(Align.left);
    lbl.setPosition(x, y);
    uiStage.addActor(lbl);
    return lbl;
}

It will return the newly created label, so that it can be used to properly figure out the coordinates of the next one below it. I add the stats after I add heroSprite to uiStage:

uiStage.addActor(heroSprite);

Label stat = prepareStatLabel("DMG:" + GameProgress.getPlayerDamage(),
        uiStage.getWidth() / 2,
        heroSprite.getY() + heroSprite.getHeight(),
        textStyle);

stat = prepareStatLabel("HP:" + GameProgress.getPlayerMaxHp(),
        uiStage.getWidth() / 2,
        stat.getY() - 10,
        textStyle);

stat = prepareStatLabel("HEAL:" + GameProgress.getPlayerHealthRestored(),
        uiStage.getWidth() / 2,
        stat.getY() - 10,
        textStyle);

prepareStatLabel("BNS:" + GameProgress.getBonusReductionValue(),
        uiStage.getWidth() / 2,
        stat.getY() - 10,
        textStyle);
;

//Bonus function for reference in GameProgress:
public static int getBonusReductionValue() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return levels[currentCharacter] / currentChar.levelsForBonusSpawnUpgrade;
}

This should do it! Not only stats are working, but they are also displayed properly. Here’s what I have now:

Relevant git commit: https://github.com/vladimirslav/dodginghero/commit/c867d7ca47e0ea721353d03107ee7f9e160d32aa

Leave a Reply

Your email address will not be published. Required fields are marked *