From 2485ddca1e67ec0b943541953d2877c4d061854d Mon Sep 17 00:00:00 2001 From: Stan_Lewry Date: Tue, 1 Oct 2024 21:03:39 +0100 Subject: [PATCH] Pass enemy type into beginCombat function. Improved overworld entity collision detection. Implement combat win state and return to overworld --- combat.cpp | 124 ++++++++++++++++++++++++++++++++++++++------------ combat.h | 8 ++-- globals.h | 7 ++- main.cpp | 43 ++++++++--------- main_menu.cpp | 2 +- 5 files changed, 129 insertions(+), 55 deletions(-) diff --git a/combat.cpp b/combat.cpp index 68e4263..2c1d627 100644 --- a/combat.cpp +++ b/combat.cpp @@ -36,6 +36,7 @@ enum CombatState { DO_ATTACK, ENEMY_MOVE, MOVE_TO_POSITION, + WIN, }; CombatState currentCombatState = TURN_ORDER; @@ -65,6 +66,8 @@ int currentAction = 0; SDL_Colour hpColour = { 136, 8, 8, 255 }; SDL_Colour mpColour = { 0, 150, 255, 255 }; +void exitCombat(); + void renderCombat(); void renderUIFull(); @@ -84,6 +87,7 @@ void movingCharacterState(double delta); void determineNextTurnState(double delta); void selectAttackTarget(double delta); void doAttack(double delta); // plays attack animation, resolves and applies damage. +void winState(double delta); void runStateFunction(CombatState state, double delta) { switch (state) { @@ -94,6 +98,7 @@ void runStateFunction(CombatState state, double delta) { case ENEMY_MOVE: enemyMoveActionState(delta); break; case MOVE_TO_POSITION: movingCharacterState(delta); break; case DO_ATTACK: doAttack(delta); break; + case WIN: winState(delta); break; default: assert(false); break; } } @@ -347,25 +352,33 @@ void initCombatScene() } -void beginCombat() +void beginCombat(EnemyType initialEnemy) { - Character dummy; - dummy.allied = false; - dummy.name = "Practice Dummy"; - dummy.currentHP = dummy.maxHP = 25; - dummy.currentMP = dummy.maxMP = 0; - dummy.agility = 0.0f; - dummy.moveDist = 0; - dummy.texture = practice_dummy_texture; - dummy.attack = 0; - dummy.defense = 0; - dummy.position = { arenaCenter.x, arenaCenter.y }; - - combatants.push_back(dummy); - - combatants.push_back(*eric); // push back copies of the players? + // push back the player characters + combatants.push_back(*eric); combatants.push_back(*monica); + // see what type of enemy to spawn. is there a better way of doing this? + // could build some kind of lookup table and copy the stats from there, with some variance? + + if (initialEnemy == PRACTICE_DUMMY) { + Character dummy; + dummy.allied = false; + dummy.name = "Practice Dummy"; + dummy.currentHP = dummy.maxHP = 10; + dummy.currentMP = dummy.maxMP = 0; + dummy.agility = 0.0f; + dummy.moveDist = 0; + dummy.texture = practice_dummy_texture; + dummy.attack = 0; + dummy.defense = 0; + dummy.position = { arenaCenter.x, arenaCenter.y }; + + combatants.push_back(dummy); + } + + + // calculates turn order. Can expand the lambda to support other things std::sort(combatants.begin(), combatants.end(), [](const Character& a, const Character& b) {return a.agility > b.agility; }); @@ -415,6 +428,9 @@ void actionSelectState(double delta) { if (frameTimer > frameTime) { + + + frameTimer = 0; // handle inputs if (inputState.up) { @@ -464,6 +480,12 @@ void actionSelectState(double delta) { break; } } + + + // if the player cant move or attack, end the turn + if (!activeCombatant->canMove && !activeCombatant->canAttack) { + currentCombatState = TURN_ORDER; + } } if (animTimer > animFramerate) { @@ -790,29 +812,67 @@ void movingCharacterState(double delta) { void determineNextTurnState(double delta) { - currentCombatantIndex += 1; - // WARNING: this system does not support the case where agility changes mid-fight. will need to re-sort if that happens - if (currentCombatantIndex > combatants.size() - 1) currentCombatantIndex = 0; + // need to consider what to do if the players die... - activeCombatant = &combatants.at(currentCombatantIndex); + bool livingEnemy = false; - if (activeCombatant->alive) { // skip dead characters - if (activeCombatant->allied) { - // reset combatants actions - activeCombatant->canAttack = true; - activeCombatant->canMove = true; - currentAction = 0; - currentCombatState = ACTION_SELECT; + for (auto c : combatants) { + if (!c.allied && c.alive) { + livingEnemy = true; } - else { - //currentCombatState = ENEMY_MOVE; + } + + if (livingEnemy) { // there is at least one enemy alive, proceed with combat + + + currentCombatantIndex += 1; + + // WARNING: this system does not support the case where agility changes mid-fight. will need to re-sort if that happens + if (currentCombatantIndex > combatants.size() - 1) currentCombatantIndex = 0; + + activeCombatant = &combatants.at(currentCombatantIndex); + + 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; + } } } + else { + // all enemies are dead. + //exitCombat(); + currentCombatState = WIN; + } +} +void winState(double delta) { + S2DE::updateInputState(&inputState); + + if (inputState.rtrn) { + inputState.rtrn = false; + exitCombat(); + } + + SDL_RenderClear(renderer); + renderCombat(); + + renderBasicPanel(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2 - 50, 200, 100); + renderString(WINDOW_WIDTH / 2 - 100 + 20, WINDOW_HEIGHT / 2 - 50 + 40, "VICTORY!"); + + + SDL_RenderPresent(renderer); } + void runCombat(double delta) { static double frameTimer = 0; @@ -820,4 +880,10 @@ void runCombat(double delta) { runStateFunction(currentCombatState, delta); +} + +void exitCombat() { + combatants.clear(); + currentCombatState = TURN_ORDER; + gameState = OVERWORLD; } \ No newline at end of file diff --git a/combat.h b/combat.h index 6f3a723..48673d6 100644 --- a/combat.h +++ b/combat.h @@ -1,15 +1,17 @@ #pragma once +#include "globals.h" + + void initCombatScene(); // call this at program start /* Call this before transitioning into the combat state. Should setup the vector of combatants. -Eventually this will have params allowing the combatants -and the combat arena to be set externally. +Takes the initial enemy - in the future this can be expanded to have some chance of spawning multiple other enemies */ -void beginCombat(); // call this before transitioning into the combat state +void beginCombat(EnemyType initialEnemy); // call this before transitioning into the combat state void runCombat(double deltaTime); \ No newline at end of file diff --git a/globals.h b/globals.h index 25aa7ab..f60972f 100644 --- a/globals.h +++ b/globals.h @@ -111,4 +111,9 @@ struct Character { }; extern Character* eric; -extern Character* monica; \ No newline at end of file +extern Character* monica; + +enum EnemyType { + BIRDFUCKER, + PRACTICE_DUMMY +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index 9ac80ea..b363469 100644 --- a/main.cpp +++ b/main.cpp @@ -53,6 +53,7 @@ struct OverworldEntity { bool hostile; S2DE::Vec2 position; SDL_Texture* texture; + EnemyType type; // used to set up combat }; std::vector overworldEntities; @@ -60,35 +61,38 @@ std::vector overworldEntities; void initOverworldEntities(); /* This function checks collisions against all overworld entities. -Currently returns true if a collision is found. This needs to be changed though depending on what the player collides with. +If the player collides with a hostile entity, initiates combat with that entity. */ -bool checkCollisionOverworldEntities(); +void checkHostileEntityCollision(); void initOverworldEntities() { - overworldEntities.push_back({ true, {29, 22}, practice_dummy_texture }); + overworldEntities.push_back({ true, {27, 21}, practice_dummy_texture, PRACTICE_DUMMY }); + overworldEntities.push_back({ true, {28, 22}, practice_dummy_texture, PRACTICE_DUMMY }); + overworldEntities.push_back({ true, {29, 22}, practice_dummy_texture, PRACTICE_DUMMY }); + overworldEntities.push_back({ true, {30, 22}, practice_dummy_texture, PRACTICE_DUMMY }); } -bool checkCollisionOverworldEntities() { +void checkHostileEntityCollision() { // assumes the enemy has size 1x1; - for (auto oe : overworldEntities) { + for (int i = 0; i < overworldEntities.size(); ++i) { + auto oe = overworldEntities.at(i); + if (oe.hostile) { + float width = 0.5f; + float height = 0.5f; - float minX = playerPos.x; - float minY = playerPos.y; - float maxX = playerPos.x + 1.0f; - float maxy = playerPos.y + 1.0f; - float width = 1.0f; - float height = 1.0f; - if ((playerPos.x > oe.position.x && playerPos.x < oe.position.x + width) - && (playerPos.y > oe.position.y && playerPos.y < oe.position.y + height)){ - return true; + if (((playerPos.x > oe.position.x && playerPos.x < oe.position.x + width) || (playerPos.x + width > oe.position.x && playerPos.x + width < oe.position.x + width)) + && ((playerPos.y > oe.position.y && playerPos.y < oe.position.y + height) || (playerPos.y + height > oe.position.y && playerPos.y + height < oe.position.y + height))) + { + beginCombat(oe.type); + overworldEntities.erase(overworldEntities.begin() + i); + gameState = COMBAT; + } } } - - return false; } @@ -217,11 +221,6 @@ int main(int argc, char** args) camera = playerPos; - if (checkCollisionOverworldEntities()) { - beginCombat(); - gameState = COMBAT; - } - if (enemyWalkTimer > enemyWalkTime) { // flip the direction and reset the timer @@ -265,6 +264,8 @@ int main(int argc, char** args) } } + checkHostileEntityCollision(); // see if we're colliding with a hostile entity - transitions state + SDL_RenderClear(renderer); { renderTilemap(world, WORLD_WIDTH, WORLD_HEIGHT, camera); diff --git a/main_menu.cpp b/main_menu.cpp index 8dbacf5..ae84046 100644 --- a/main_menu.cpp +++ b/main_menu.cpp @@ -53,7 +53,7 @@ void mainMenu(double deltaTime) { gameState = OVERWORLD; break; case 1: - beginCombat(); + beginCombat(PRACTICE_DUMMY); gameState = COMBAT; break; default: -- 2.20.1