DO_ATTACK,
ENEMY_MOVE,
MOVE_TO_POSITION,
+ WIN,
};
CombatState currentCombatState = TURN_ORDER;
SDL_Colour hpColour = { 136, 8, 8, 255 };
SDL_Colour mpColour = { 0, 150, 255, 255 };
+void exitCombat();
+
void renderCombat();
void renderUIFull();
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) {
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;
}
}
}
-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; });
if (frameTimer > frameTime) {
+
+
+
frameTimer = 0;
// handle inputs
if (inputState.up) {
break;
}
}
+
+
+ // if the player cant move or attack, end the turn
+ if (!activeCombatant->canMove && !activeCombatant->canAttack) {
+ currentCombatState = TURN_ORDER;
+ }
}
if (animTimer > animFramerate) {
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;
runStateFunction(currentCombatState, delta);
+}
+
+void exitCombat() {
+ combatants.clear();
+ currentCombatState = TURN_ORDER;
+ gameState = OVERWORLD;
}
\ No newline at end of file
bool hostile;
S2DE::Vec2<float> position;
SDL_Texture* texture;
+ EnemyType type; // used to set up combat
};
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;
}
camera = playerPos;
- if (checkCollisionOverworldEntities()) {
- beginCombat();
- gameState = COMBAT;
- }
-
if (enemyWalkTimer > enemyWalkTime)
{
// flip the direction and reset the timer
}
}
+ checkHostileEntityCollision(); // see if we're colliding with a hostile entity - transitions state
+
SDL_RenderClear(renderer);
{
renderTilemap(world, WORLD_WIDTH, WORLD_HEIGHT, camera);