first basic attacking implementation done
authorStan_Lewry <stanley.jml@gmail.com>
Tue, 27 Aug 2024 17:42:23 +0000 (18:42 +0100)
committerStan_Lewry <stanley.jml@gmail.com>
Tue, 27 Aug 2024 17:42:23 +0000 (18:42 +0100)
combat.cpp

index 529c159..c2611d6 100644 (file)
@@ -16,7 +16,7 @@ struct Character {
        int maxHP;\r
        int currentMP;\r
        int maxMP;\r
-       \r
+       int attack;\r
        float agility; // to determine move order\r
        float moveDist;\r
 \r
@@ -31,13 +31,15 @@ struct Character {
 \r
        bool canMove = true;\r
        bool canAttack = true;\r
+\r
+       bool alive = true;\r
 };\r
 \r
 std::vector<Character> combatants;\r
 int currentCombatantIndex = -1;\r
 Character* activeCombatant;\r
 \r
-std::vector<Character> targets;\r
+std::vector<Character*> targets;\r
 int currentTargetIdx = 0;\r
 \r
 enum CombatState {\r
@@ -45,20 +47,24 @@ enum CombatState {
        ACTION_SELECT,\r
        PLAYER_MOVE,\r
        SELECT_TARGET,\r
+       DO_ATTACK,\r
        ENEMY_MOVE,\r
        MOVE_TO_POSITION,\r
 };\r
 \r
 CombatState currentCombatState = TURN_ORDER;\r
 \r
+enum Spell {\r
+       BASIC_ATTACK\r
+};\r
+\r
+Spell currentSpell = BASIC_ATTACK;\r
 \r
 static double frameTimer = 0;\r
 static const double frameTime = 1.0f / 60.0f;\r
 \r
-\r
 //bool playerTurn = true;\r
 \r
-\r
 SDL_Texture* arrowSprite = nullptr;\r
 SDL_Texture* indicatorSprite = nullptr;\r
 SDL_Texture* indicatorRedSprite = nullptr;\r
@@ -99,6 +105,7 @@ void enemyMoveActionState(double delta);
 void movingCharacterState(double delta);\r
 void determineNextTurnState(double delta);\r
 void selectAttackTarget(double delta);\r
+void doAttack(double delta); // plays attack animation, resolves and applies damage.\r
 \r
 void runStateFunction(CombatState state, double delta) {\r
        switch (state) {\r
@@ -108,6 +115,7 @@ void runStateFunction(CombatState state, double delta) {
        case SELECT_TARGET: selectAttackTarget(delta);          break;\r
        case ENEMY_MOVE: enemyMoveActionState(delta);           break;\r
        case MOVE_TO_POSITION: movingCharacterState(delta);     break;\r
+       case DO_ATTACK: doAttack(delta);                                        break;\r
        default: assert(false); break;\r
        }\r
 }\r
@@ -163,15 +171,21 @@ void renderStatsPanel() {
 \r
        renderString(portraitX + portraitWidth + innerPad, portraitY, "HP:");\r
        int valPad = 100;\r
-       renderString(portraitX + portraitWidth + innerPad + valPad, portraitY, "50/100");\r
+       char hpStr[10];\r
+       sprintf(hpStr, "%d/%d", activeCombatant->currentHP, activeCombatant->maxHP);\r
+       renderString(portraitX + portraitWidth + innerPad + valPad, portraitY, hpStr);\r
 \r
-       renderMeter(portraitX + portraitWidth + innerPad, portraitY + innerPad, panelWidth - (innerPad * 3) - portraitWidth, 20, 0.5, hpColour, 2);\r
+       float hpLvl = (float)activeCombatant->currentHP / (float)activeCombatant->maxHP;\r
+       renderMeter(portraitX + portraitWidth + innerPad, portraitY + innerPad, panelWidth - (innerPad * 3) - portraitWidth, 20, hpLvl, hpColour, 2);\r
 \r
        renderString(portraitX + portraitWidth + innerPad, portraitY + innerPad + 22, "MP:");\r
 \r
-       renderString(portraitX + portraitWidth + innerPad + valPad, portraitY + innerPad + 22, "50/100");\r
+       char mpStr[10];\r
+       sprintf(mpStr, "%d/%d", activeCombatant->currentMP, activeCombatant->maxMP);\r
+       renderString(portraitX + portraitWidth + innerPad + valPad, portraitY + innerPad + 22, mpStr);\r
        \r
-       renderMeter(portraitX + portraitWidth + innerPad, portraitY + innerPad + 42, panelWidth - (innerPad * 3) - portraitWidth, 20, 0.5, mpColour, 2);\r
+       float mpLvl = (float)activeCombatant->currentMP / (float)activeCombatant->maxMP;\r
+       renderMeter(portraitX + portraitWidth + innerPad, portraitY + innerPad + 42, panelWidth - (innerPad * 3) - portraitWidth, 20, mpLvl, mpColour, 2);\r
        \r
 }\r
 \r
@@ -219,37 +233,41 @@ void renderCombat() {
 void renderCombatants() {\r
 \r
        for (Character c : combatants) {\r
-               S2DE::Vec2<float> pos = c.position;\r
-               S2DE::Rect charDRect = S2DE::worldToScreenRect(&combatCam, &pos, 2, WINDOW_WIDTH, WINDOW_HEIGHT, 32, 32);\r
-               S2DE::Rect charSRect = { 0, 0, 32, 32 };\r
-\r
-               // disabled for now\r
-               //if (c.allied) {\r
-               //      // do the looking logic only for allied characters\r
-               //      int playerFrame = 1;\r
-               //      int playerDir = 0;\r
-\r
-               //      Character enemy = combatants.at(1);\r
-\r
-               //      int lookDeltaX = (enemy.position.x + 0.5) - (c.position.x + 0.5);\r
-               //      int lookDeltaY = (enemy.position.y + 0.5) - (c.position.y + 0.5);\r
-\r
-               //      if (abs(lookDeltaX) > abs(lookDeltaY)) {\r
-               //              if (lookDeltaX > 0) playerDir = 2;\r
-               //              else playerDir = 6;\r
-               //      }\r
-               //      else {\r
-               //              if (lookDeltaY > 0) playerDir = 4;\r
-               //              else playerDir = 0;\r
-               //      }\r
-\r
-               //      charSRect = { playerFrame * 32, playerDir * 32, 32, 32 };\r
-               //}\r
-\r
-               SDL_Texture* texture = c.texture;\r
-               S2DE::renderTexture(&renderer, &texture, &charSRect, &charDRect);\r
-               \r
-               renderMeter(charDRect.x + 6, charDRect.y - 6, charDRect.w - 12, 6, 0.5, hpColour, 1);\r
+\r
+               if (c.alive) {\r
+                       S2DE::Vec2<float> pos = c.position;\r
+                       S2DE::Rect charDRect = S2DE::worldToScreenRect(&combatCam, &pos, 2, WINDOW_WIDTH, WINDOW_HEIGHT, 32, 32);\r
+                       S2DE::Rect charSRect = { 0, 0, 32, 32 };\r
+\r
+                       // disabled for now\r
+                       //if (c.allied) {\r
+                       //      // do the looking logic only for allied characters\r
+                       //      int playerFrame = 1;\r
+                       //      int playerDir = 0;\r
+\r
+                       //      Character enemy = combatants.at(1);\r
+\r
+                       //      int lookDeltaX = (enemy.position.x + 0.5) - (c.position.x + 0.5);\r
+                       //      int lookDeltaY = (enemy.position.y + 0.5) - (c.position.y + 0.5);\r
+\r
+                       //      if (abs(lookDeltaX) > abs(lookDeltaY)) {\r
+                       //              if (lookDeltaX > 0) playerDir = 2;\r
+                       //              else playerDir = 6;\r
+                       //      }\r
+                       //      else {\r
+                       //              if (lookDeltaY > 0) playerDir = 4;\r
+                       //              else playerDir = 0;\r
+                       //      }\r
+\r
+                       //      charSRect = { playerFrame * 32, playerDir * 32, 32, 32 };\r
+                       //}\r
+\r
+                       SDL_Texture* texture = c.texture;\r
+                       S2DE::renderTexture(&renderer, &texture, &charSRect, &charDRect);\r
+\r
+                       float hpLvl = (float)c.currentHP / (float)c.maxHP;\r
+                       renderMeter(charDRect.x + 6, charDRect.y - 6, charDRect.w - 12, 6, hpLvl, hpColour, 1);\r
+               }\r
        }\r
 \r
        // render the little arrow\r
@@ -288,6 +306,8 @@ void initCombatScene()
        player.moveDist = 3.0f;\r
        player.texture = player_forward_texture;\r
        player.position = { arenaCenter.x, arenaCenter.y + 2 };\r
+       player.attack = 5;\r
+\r
 \r
        Character monica;\r
        monica.allied = true;\r
@@ -300,18 +320,20 @@ void initCombatScene()
        monica.moveDist = 6.0f;\r
        monica.texture = monica_texture;\r
        monica.position = { player.position.x - 2, player.position.y };\r
+       monica.attack = 6;\r
 \r
        Character enemy;\r
        enemy.allied = false;\r
        enemy.name = "Bird";\r
        enemy.maxHP = 25;\r
-       enemy.currentHP = player.maxHP;\r
+       enemy.currentHP = enemy.maxHP;\r
        enemy.maxMP = 10;\r
-       enemy.currentMP = player.maxMP;\r
+       enemy.currentMP = enemy.maxMP;\r
        enemy.agility = 5.0f;\r
        enemy.moveDist = 1.5f;\r
        enemy.texture = bird;\r
        enemy.position = { arenaCenter.x, arenaCenter.y - 2 };\r
+       enemy.attack = 2;\r
 \r
        Character frog;\r
        frog.allied = false;\r
@@ -322,6 +344,7 @@ void initCombatScene()
        frog.moveDist = 2.0f;\r
        frog.texture = frog_texture;\r
        frog.position = { arenaCenter.x - 1, arenaCenter.y - 2 };\r
+       frog.attack = 2;\r
 \r
        combatants.push_back(player);\r
        combatants.push_back(monica);\r
@@ -389,17 +412,15 @@ void actionSelectState(double delta) {
                        if (activeCombatant->canAttack) {\r
                                // filter the targets\r
                                targets.clear();\r
-                               //float range = activeCombatant->basicAttackRange;\r
-                               std::copy_if(combatants.begin(), combatants.end(), std::back_inserter(targets),\r
-                                       [](const Character& c) {\r
-                                               if (!c.allied) {\r
-                                                       float dist = distance(activeCombatant->position, c.position);\r
-                                                       if (dist <= activeCombatant->basicAttackRange) {\r
-                                                               return true;\r
-                                                       }\r
+                               for (auto& c : combatants) {\r
+                                       if (!c.allied && c.alive) {\r
+                                               float dist = distance(activeCombatant->position, c.position);\r
+                                               if (dist <= activeCombatant->basicAttackRange) {\r
+                                                       targets.push_back(&c);\r
                                                }\r
-                                               return false;\r
-                                       });\r
+                                       }\r
+                               }\r
+\r
                                currentCombatState = SELECT_TARGET;\r
                        }\r
                        break;\r
@@ -506,7 +527,14 @@ void selectAttackTarget(double delta) {
                frameTimer = 0;\r
                if (inputState.rtrn) {\r
                        inputState.rtrn = false;\r
-                       currentCombatState = ACTION_SELECT;\r
+                       if (targets.size() > 0) {\r
+                               currentSpell = BASIC_ATTACK;\r
+                               currentCombatState = DO_ATTACK;\r
+                       }\r
+                       else {\r
+                               currentCombatState = ACTION_SELECT;\r
+                       }\r
+\r
                }\r
 \r
                if (inputState.right) {\r
@@ -542,7 +570,7 @@ void selectAttackTarget(double delta) {
        // render the little red arrow above all valid targets\r
        //for (auto target : targets) {\r
        if (targets.size() > 0) {\r
-               S2DE::Vec2<float> arrowPos = targets.at(currentTargetIdx).position;\r
+               S2DE::Vec2<float> arrowPos = targets.at(currentTargetIdx)->position;\r
 \r
                arrowPos.y -= 0.7;\r
                S2DE::Rect arrowDRect = S2DE::worldToScreenRect(&combatCam, &arrowPos, 2, WINDOW_WIDTH, WINDOW_HEIGHT, 32, 32);\r
@@ -573,6 +601,31 @@ void selectAttackTarget(double delta) {
 \r
 }\r
 \r
+void doAttack(double delta) {\r
+       // for now just subtract the combatants' dmg from the targets hp and move on\r
+       \r
+\r
+       if (currentTargetIdx == -1) {\r
+               // AOE, apply to all targets in range ?\r
+       }\r
+       else {\r
+               Character* target = targets.at(currentTargetIdx);\r
+               target->currentHP -= activeCombatant->attack;\r
+\r
+               if (target->currentHP <= 0) {\r
+                       target->alive = false;\r
+                       target->currentHP = 0;\r
+               }\r
+       }\r
+\r
+\r
+\r
+       targets.clear();\r
+       currentCombatState = ACTION_SELECT;\r
+       activeCombatant->canAttack = false;\r
+}\r
+\r
+\r
 void enemyMoveActionState(double delta) {\r
        S2DE::updateInputState(&inputState);\r
 \r
@@ -641,15 +694,17 @@ void determineNextTurnState(double delta) {
 \r
        activeCombatant = &combatants.at(currentCombatantIndex);\r
 \r
-       if (activeCombatant->allied) {\r
-               // reset combatants actions\r
-               activeCombatant->canAttack = true;\r
-               activeCombatant->canMove = true;\r
-               currentAction = 0;\r
-               currentCombatState = ACTION_SELECT;\r
-       }\r
-       else {\r
-               currentCombatState = ENEMY_MOVE;\r
+       if (activeCombatant->alive) { // skip dead characters\r
+               if (activeCombatant->allied) {\r
+                       // reset combatants actions\r
+                       activeCombatant->canAttack = true;\r
+                       activeCombatant->canMove = true;\r
+                       currentAction = 0;\r
+                       currentCombatState = ACTION_SELECT;\r
+               }\r
+               else {\r
+                       currentCombatState = ENEMY_MOVE;\r
+               }\r
        }\r
 \r
 }\r