Pass enemy type into beginCombat function. Improved overworld entity collision detect... master
authorStan_Lewry <stanley.jml@gmail.com>
Tue, 1 Oct 2024 20:03:39 +0000 (21:03 +0100)
committerStan_Lewry <stanley.jml@gmail.com>
Tue, 1 Oct 2024 20:03:39 +0000 (21:03 +0100)
combat.cpp
combat.h
globals.h
main.cpp
main_menu.cpp

index 68e4263..2c1d627 100644 (file)
@@ -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
index 6f3a723..48673d6 100644 (file)
--- 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
index 25aa7ab..f60972f 100644 (file)
--- 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
index 9ac80ea..b363469 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -53,6 +53,7 @@ struct OverworldEntity {
        bool hostile;
        S2DE::Vec2<float> position;
        SDL_Texture* texture;
+       EnemyType type; // used to set up combat
 };
 
 std::vector<OverworldEntity> overworldEntities;
@@ -60,35 +61,38 @@ std::vector<OverworldEntity> 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);
index 8dbacf5..ae84046 100644 (file)
@@ -53,7 +53,7 @@ void mainMenu(double deltaTime) {
                        gameState = OVERWORLD;
                        break;
                case 1:
-                       beginCombat();
+                       beginCombat(PRACTICE_DUMMY);
                        gameState = COMBAT;
                        break;
                default: