int maxHP;\r
int currentMP;\r
int maxMP;\r
- \r
+ int attack;\r
float agility; // to determine move order\r
float moveDist;\r
\r
\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
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
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
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
\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
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
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
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
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
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
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
// 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
\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
\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