Skip to content

Commit

Permalink
misc: Reworked Controllers (#101)
Browse files Browse the repository at this point in the history
* wip: Process in Controller rework

* feat: Added the foundation for AI and MP controllers

* fix: Resolved build problems, more progress on controllers

* lint: The Fallen Green Tick

* fix: First working version with team-scoped controllers

* fix: Cells not rendering after the first turn

Turns out I forgot that cells are drawn as soon as finish is called

* fix: Resolved crash when all the units from a team die

* fix: Resolved some critical bugs
  • Loading branch information
kyranet committed Apr 19, 2019
1 parent c23c090 commit 7500005
Show file tree
Hide file tree
Showing 15 changed files with 316 additions and 187 deletions.
13 changes: 3 additions & 10 deletions Drakhtar/Controllers/AIController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
#include "AIController.h"
#include "GameObjects/TurnBar.h"

AIController::AIController(Board* board, TurnBar* turnBar, GameScene* scene)
: UnitsController(board, turnBar, scene) {}

void AIController::run() {}

void AIController::advanceTurn() {
hasMoved_ = hasAttacked_ = false;
turnBar_->next();
activeUnit_ = turnBar_->getTurnFor();
}
AIController::AIController(Board* board, TurnBar* turnBar, GameScene* scene,
Team* team, Team* oppositeTeam)
: UnitsController(board, turnBar, scene, team, oppositeTeam) {}
14 changes: 2 additions & 12 deletions Drakhtar/Controllers/AIController.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@

class AIController final : public UnitsController {
public:
AIController(Board* board, TurnBar* turnBar, GameScene* scene);

/**
* \brief Is called every time an event is capture to process it.
* \param event: The event to be processed.
*/
void run() override;

/**
* \brief Ends unit's turn and updates active unit.
*/
void advanceTurn();
AIController(Board* board, TurnBar* turnBar, GameScene* scene, Team* team,
Team* oppositeTeam);
};
33 changes: 33 additions & 0 deletions Drakhtar/Controllers/Handlers/PlayerHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2019 the Drakhtar authors. All rights reserved. MIT license.

#include "PlayerHandler.h"
#include "Controllers/PlayerController.h"
#include "GameObjects/Board.h"
#include "GameObjects/Box.h"
#include "GameObjects/Unit.h"
#include "Managers/Input.h"

PlayerHandler::PlayerHandler(PlayerController* controller)
: EventListener(nullptr), controller_(controller) {}

void PlayerHandler::run(const SDL_Event) {
if (controller_->getLocked() || !Input::isMouseButtonUp(MouseKey::LEFT))
return;

const auto gameObject = Input::screenMouseToRay();
if (!gameObject) return;

// Ignore Unit's "hitboxes" and assume it's a click to the board, so get the
// box at the mouse's coordinates
const auto unit = dynamic_cast<Unit*>(gameObject);
const auto box = unit ? controller_->getBoard()->getBoxAtCoordinates(
Input::getMousePosition())
: dynamic_cast<Box*>(gameObject);
if (!box) return;

if (!controller_->hasMoved() && box->isEmpty()) {
controller_->onClickMove(box);
} else if (!controller_->hasAttacked()) {
controller_->onClickAttack(box);
}
}
15 changes: 15 additions & 0 deletions Drakhtar/Controllers/Handlers/PlayerHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2019 the Drakhtar authors. All rights reserved. MIT license.

#pragma once
#include "EventListeners/EventListener.h"

class PlayerController;

class PlayerHandler : public EventListener {
private:
PlayerController* controller_;

public:
explicit PlayerHandler(PlayerController* controller);
void run(SDL_Event event) override;
};
213 changes: 99 additions & 114 deletions Drakhtar/Controllers/PlayerController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <iostream>

#include "Controllers/Handlers/PlayerHandler.h"
#include "GameObjects/Board.h"
#include "GameObjects/Box.h"
#include "GameObjects/TurnBar.h"
Expand All @@ -18,137 +19,121 @@
#include "Utils/Constants.h"

PlayerController::PlayerController(Board* board, TurnBar* turnBar,
GameScene* scene)
: UnitsController(board, turnBar, scene), ListenerOnClick(board) {
activeUnit_->getBox()->setCurrentTexture(TextureInd::ACTIVE);
board_->highlightCellsInRange(activeUnit_->getBox(),
activeUnit_->getStats().moveRange);
board_->highlightEnemiesInRange(activeUnit_->getBox(),
activeUnit_->getStats().attackRange);
}

void PlayerController::run(const SDL_Event) {
if (!Input::isMouseButtonDown(MouseKey::LEFT)) return;

const auto gameObject = Input::screenMouseToRay();
if (!gameObject) return;

// Ignore Unit's "hitboxes" and assume it's a click to the board, so get the
// box at the mouse's coordinates
const auto unit = dynamic_cast<Unit*>(gameObject);
const auto box = unit ? board_->getBoxAtCoordinates(Input::getMousePosition())
: dynamic_cast<Box*>(gameObject);
if (!box) return;

if (!hasMoved_ && box->isEmpty()) {
onClickMove(box);
} else if (!hasAttacked_) {
onClickAttack(box);
}
GameScene* scene, Team* team,
Team* oppositeTeam)
: UnitsController(board, turnBar, scene, team, oppositeTeam) {
const auto handler = new PlayerHandler(this);
handler->setActive(false);
listeners_.push_back(handler);
board->addEventListener(handler);
}

void PlayerController::onClickMove(Box* boxClicked) {
if (locked_) return;
// Checks if the box clicked is within movement range
if (board_->isInMoveRange(activeUnit_->getBox(), boxClicked,
activeUnit_->getStats().moveRange)) {
const auto path = board_->findPath(activeUnit_->getBox()->getIndex(),
boxClicked->getIndex());

setActive(false);
SDLAudioManager::getInstance()->playChannel(0, 0, 0);
const auto unit = activeUnit_;
scene_->getTweenManager()
->create()
->setRoute(board_->pathToRoute(path))
->setDuration(static_cast<int>(
floor(static_cast<double>(path.size()) * GAME_FRAMERATE * 0.25)))
->setOnUpdate([unit](Vector2D<double> updated) {
unit->setPosition({static_cast<int>(std::floor(updated.getX())),
static_cast<int>(std::floor(updated.getY()))});
})
->setOnComplete([this, unit, boxClicked]() {
unit->moveToBox(boxClicked);
hasMoved_ = true;
setActive(true);
// If there are enemies in range, highlight them, otherwise skip turn
if (board_->isEnemyInRange(boxClicked,
unit->getStats().attackRange)) {
board_->resetCellsToBase();
unit->getBox()->setCurrentTexture(TextureInd::ACTIVE);
board_->highlightEnemiesInRange(unit->getBox(),
unit->getStats().attackRange);
SDLAudioManager::getInstance()->setChannelVolume(30, 0);
SDLAudioManager::getInstance()->playChannel(4, 0, 0);
} else {
advanceTurn();
}

// If no actions left, reset and skip turn
if (hasMoved_ && hasAttacked_) {
advanceTurn();
}
});
} else {
std::cout << "Out of movement range!\n";
if (!board_->isInMoveRange(activeUnit_->getBox(), boxClicked,
activeUnit_->getStats().moveRange)) {
SDLAudioManager::getInstance()->playChannel(3, 0, 0);
return;
}

const auto path = board_->findPath(activeUnit_->getBox()->getIndex(),
boxClicked->getIndex());

locked_ = true;
SDLAudioManager::getInstance()->playChannel(0, 0, 0);
const auto unit = activeUnit_;
unit->moveToBox(boxClicked);

scene_->getTweenManager()
->create()
->setRoute(board_->pathToRoute(path))
->setDuration(static_cast<int>(
floor(static_cast<double>(path.size()) * GAME_FRAMERATE * 0.25)))
->setOnUpdate([unit](Vector2D<double> updated) {
unit->setPosition({static_cast<int>(std::floor(updated.getX())),
static_cast<int>(std::floor(updated.getY()))});
})
->setOnComplete([this, unit, boxClicked]() {
hasMoved_ = true;
locked_ = false;
// If there are enemies in range, highlight them, otherwise skip turn
if (!hasAttacked_ &&
board_->isEnemyInRange(boxClicked, unit->getStats().attackRange)) {
board_->resetCellsToBase();
unit->getBox()->setCurrentTexture(TextureInd::ACTIVE);
board_->highlightEnemiesInRange(unit->getBox(),
unit->getStats().attackRange);
SDLAudioManager::getInstance()->setChannelVolume(30, 0);
SDLAudioManager::getInstance()->playChannel(4, 0, 0);
} else {
finish();
}
});
}

void PlayerController::onClickAttack(Box* boxClicked) {
Unit* enemyUnit = boxClicked->getContent();
if (enemyUnit != nullptr) {
// Unit clicked if from a different team and in range
if (enemyUnit->getTeam() != activeUnit_->getTeam() &&
board_->isInRange(activeUnit_->getBox(), boxClicked,
activeUnit_->getStats().attackRange)) {
activeUnit_->getTexture()->setAnimationOnce("attack");
activeUnit_->attack(enemyUnit, false);
SDLAudioManager::getInstance()->playChannel(5, 0, 0);

// Enemy dies
if (enemyUnit->getStats().health <= 0) {
if (enemyUnit->getTeam()->getColor() == Color::RED) {
GameManager::getInstance()->addMoney(enemyUnit->getStats().prize);
}
boxClicked->setContent(nullptr);
enemyUnit->kill();
turnBar_->remove(enemyUnit);
scene_->removeGameObject(enemyUnit);
}

// Re-highlight board
board_->resetCellsToBase();
activeUnit_->getBox()->setCurrentTexture(TextureInd::ACTIVE);
board_->highlightCellsInRange(activeUnit_->getBox(),
activeUnit_->getStats().moveRange);
hasAttacked_ = true;

// Unit dies to counter-attack
if (activeUnit_->getStats().health <= 0) {
activeUnit_->getBox()->setContent(nullptr);
activeUnit_->kill();
turnBar_->remove(activeUnit_);
scene_->removeGameObject(activeUnit_);
advanceTurn();
}

// If no actions left, reset and skip turn
if (hasMoved_ && hasAttacked_) {
advanceTurn();
}
auto unit = boxClicked->getContent();
// If the box clicked was empty, skip
if (!unit) return;

// If the unit is from the same team, skip
if (unit->getTeam() == team_) return;

const auto currentStats = activeUnit_->getStats();
const auto currentBox = activeUnit_->getBox();

// If the selected unit is not in the attack range, skip
if (!board_->isInRange(currentBox, boxClicked, currentStats.attackRange))
return;

hasAttacked_ = true;

activeUnit_->getTexture()->setAnimationOnce("attack");
activeUnit_->attack(unit, false);
SDLAudioManager::getInstance()->playChannel(5, 0, 0);

const auto unitStats = unit->getStats();

// Enemy dies
if (unitStats.health <= 0) {
// Unit dies to attack
if (unit->getTeam()->getColor() == Color::RED) {
GameManager::getInstance()->addMoney(unitStats.prize);
}
boxClicked->destroyContent();
} else if (activeUnit_->getStats().health <= 0) {
// Unit dies to counter-attack
currentBox->destroyContent();
finish();
return;
}

if (hasMoved_) {
// If no actions left, reset and skip turn
finish();
} else {
// Re-highlight board
board_->resetCellsToBase();
currentBox->setCurrentTexture(TextureInd::ACTIVE);
board_->highlightCellsInRange(currentBox, currentStats.moveRange);
}
}

void PlayerController::advanceTurn() {
board_->resetCellsToBase();
hasMoved_ = hasAttacked_ = false;
turnBar_->next();
activeUnit_ = turnBar_->getTurnFor();
void PlayerController::start() {
UnitsController::start();
if (!activeUnit_) return UnitsController::finish();

activeUnit_->getBox()->setCurrentTexture(TextureInd::ACTIVE);
board_->highlightCellsInRange(activeUnit_->getBox(),
activeUnit_->getStats().moveRange);
board_->highlightEnemiesInRange(activeUnit_->getBox(),
activeUnit_->getStats().attackRange);
}

void PlayerController::finish() {
board_->resetCellsToBase();
UnitsController::finish();
}

bool PlayerController::getLocked() const { return locked_; }
27 changes: 12 additions & 15 deletions Drakhtar/Controllers/PlayerController.h
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
// Copyright 2019 the Drakhtar authors. All rights reserved. MIT license.

#pragma once
#include "UnitsController.h"
#include "EventListeners/ListenerOnClick.h"
#include "UnitsController.h"

class PlayerController final : public UnitsController, public ListenerOnClick {
public:
PlayerController(Board *board, TurnBar *turnBar, GameScene *scene);
class PlayerController final : public UnitsController {
bool locked_ = false;

/**
* \brief Is called every time an event is capture to process it.
* \param event: The event to be processed.
*/
void run(SDL_Event event) override;
public:
PlayerController(Board* board, TurnBar* turnBar, GameScene* scene, Team* team,
Team* oppositeTeam);

/**
* \brief Moves active unit to an empty cell within range.
* \param boxClicked: The box where the unit should move.
*/
void onClickMove(Box *boxClicked);
void onClickMove(Box* boxClicked);

/**
* \brief Makes active unit attack another unit clicked
* \param boxClicked: The box containing the unit that will receive the
* attack.
*/
void onClickAttack(Box *boxClicked);
void onClickAttack(Box* boxClicked);

/**
* \brief Resets board textures, ends unit's turn and updates active unit.
*/
void advanceTurn();
void start() override;
void finish() override;

bool getLocked() const;
};
Loading

0 comments on commit 7500005

Please sign in to comment.