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