Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions src/ai.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "ai.h"

#include "game.h"
#include "helper.h"
#include "map.h"
#include "res.h"
Expand All @@ -13,13 +14,10 @@ extern std::array<std::array<Item, MAP_SIZE>, MAP_SIZE> itemMap;
extern bool hasMap[MAP_SIZE][MAP_SIZE];
extern std::array<std::array<bool, MAP_SIZE>, MAP_SIZE> hasEnemy;
extern int spikeDamage;
extern int playersCount;
extern const int n, m;
extern const int SCALE_FACTOR;

// Sprite
extern std::shared_ptr<Snake> spriteSnake[SPRITES_MAX_NUM];
extern int spritesCount;
// Use GameContext for entity access
double AI_LOCK_LIMIT;

int trapVerdict(const std::shared_ptr<Sprite>& sprite) {
Expand Down Expand Up @@ -47,14 +45,18 @@ int trapVerdict(const std::shared_ptr<Sprite>& sprite) {
}

int getPowerfulPlayer() {
GameContext& ctx = getGameContext();
int maxNum = 0;
int mxCount = 0;
int id = -1;
for (int i = 0; i < playersCount; i++) {
if (!spriteSnake[i]) {
const int playerCount = ctx.entityManager.playerCount();
const int snakeCount = ctx.entityManager.snakeCount();
for (int i = 0; i < playerCount; i++) {
const auto& snake = ctx.entityManager.getSnake(i);
if (!snake) {
continue;
}
const int num = spriteSnake[i]->num();
const int num = snake->num();
if (num > maxNum) {
maxNum = num;
mxCount = 1;
Expand All @@ -63,17 +65,20 @@ int getPowerfulPlayer() {
mxCount++;
}
}
if (id == -1 || mxCount != 1 || !spriteSnake[id]) {
if (id == -1 || mxCount != 1) {
return -1;
}
return spriteSnake[id]->num() >= AI_LOCK_LIMIT ? id : -1;
const auto& snake = ctx.entityManager.getSnake(id);
return (snake && snake->num() >= AI_LOCK_LIMIT) ? id : -1;
}

int balanceVerdict(const std::shared_ptr<Sprite>& sprite, int id) {
if (id == -1 || !spriteSnake[id] || spriteSnake[id]->sprites().empty()) {
GameContext& ctx = getGameContext();
const auto& playerSnake = ctx.entityManager.getSnake(id);
if (id == -1 || !playerSnake || playerSnake->sprites().empty()) {
return 0;
}
const auto player = spriteSnake[id]->sprites().front();
const auto player = playerSnake->sprites().front();
if (!player) {
return 0;
}
Expand Down Expand Up @@ -127,7 +132,8 @@ int compareChoiceByValue(const void* x, const void* y) {
return b->value - a->value;
}

void AiInput(const std::shared_ptr<Snake>& snake) {
void DefaultAIBehavior::updateInput(
const std::shared_ptr<Snake>& snake) const {
if (!snake || snake->sprites().empty()) {
return;
}
Expand Down Expand Up @@ -175,3 +181,11 @@ void AiInput(const std::shared_ptr<Snake>& snake) {
}
}
}

void AiInput(const std::shared_ptr<Snake>& snake) {
GameContext& ctx = getGameContext();
if (!ctx.aiBehavior) {
ctx.aiBehavior = std::make_shared<DefaultAIBehavior>();
}
ctx.aiBehavior->updateInput(snake);
}
11 changes: 11 additions & 0 deletions src/ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ struct Choice {
Direction direction = Direction::Right;
};

class AIBehavior {
public:
virtual ~AIBehavior() = default;
virtual void updateInput(const std::shared_ptr<Snake>& snake) const = 0;
};

class DefaultAIBehavior final : public AIBehavior {
public:
void updateInput(const std::shared_ptr<Snake>& snake) const override;
};

void AiInput(const std::shared_ptr<Snake>& snake);
int getPowerfulPlayer();

Expand Down
197 changes: 197 additions & 0 deletions src/component.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#include "component.h"

#include "sprite.h"

// ============================================================================
// TransformComponent Implementation
// ============================================================================

TransformComponent::TransformComponent(int x, int y, Direction direction)
: x_(x), y_(y), direction_(direction), face_(direction) {}

int TransformComponent::x() const noexcept { return x_; }
int TransformComponent::y() const noexcept { return y_; }
Direction TransformComponent::face() const noexcept { return face_; }
Direction TransformComponent::direction() const noexcept { return direction_; }

void TransformComponent::setPosition(int x, int y) noexcept {
x_ = x;
y_ = y;
}

void TransformComponent::setX(int x) noexcept { x_ = x; }
void TransformComponent::setY(int y) noexcept { y_ = y; }

void TransformComponent::setFace(Direction face) noexcept { face_ = face; }

void TransformComponent::setDirection(Direction direction) noexcept {
direction_ = direction;
}

void TransformComponent::enqueueDirectionChange(
Direction newDirection, PositionBuffer& buffer) noexcept {
if (direction_ == newDirection) {
return;
}
direction_ = newDirection;
if (newDirection == Direction::Left || newDirection == Direction::Right) {
face_ = newDirection;
}
// Buffer position change for chained sprites
PositionBufferSlot slot{x_, y_, direction_};
buffer.push(slot);
}

// ============================================================================
// HealthComponent Implementation
// ============================================================================

HealthComponent::HealthComponent(int hp, int totalHp)
: hp_(hp), totalHp_(totalHp) {}

int HealthComponent::hp() const noexcept { return hp_; }
int HealthComponent::totalHp() const noexcept { return totalHp_; }
bool HealthComponent::isDead() const noexcept { return hp_ <= 0; }
bool HealthComponent::isAlive() const noexcept { return hp_ > 0; }

void HealthComponent::setHp(int hp) noexcept { hp_ = hp; }
void HealthComponent::setTotalHp(int totalHp) noexcept { totalHp_ = totalHp; }

void HealthComponent::takeDamage(int damage) noexcept {
hp_ -= damage;
if (hp_ < 0) {
hp_ = 0;
}
}

void HealthComponent::heal(int amount) noexcept {
hp_ += amount;
if (hp_ > totalHp_) {
hp_ = totalHp_;
}
}

void HealthComponent::reset() noexcept { hp_ = totalHp_; }

// ============================================================================
// RenderComponent Implementation
// ============================================================================

RenderComponent::RenderComponent(
const std::shared_ptr<Animation>& animation)
: animation_(animation) {}

std::shared_ptr<Animation> RenderComponent::animation() const noexcept {
return animation_;
}

bool RenderComponent::hasAnimation() const noexcept {
return animation_ != nullptr;
}

void RenderComponent::setAnimation(
const std::shared_ptr<Animation>& animation) noexcept {
animation_ = animation;
}

void RenderComponent::clearAnimation() noexcept { animation_.reset(); }

void RenderComponent::updatePosition(int x, int y) noexcept {
if (animation_) {
animation_->setPosition(x, y);
}
}

// ============================================================================
// CombatComponent Implementation
// ============================================================================

CombatComponent::CombatComponent(Weapon* weapon) : weapon_(weapon) {}

Weapon* CombatComponent::weapon() const noexcept { return weapon_; }
bool CombatComponent::hasWeapon() const noexcept { return weapon_ != nullptr; }
int CombatComponent::lastAttack() const noexcept { return lastAttack_; }
double CombatComponent::dropRate() const noexcept { return dropRate_; }

void CombatComponent::setWeapon(Weapon* weapon) noexcept { weapon_ = weapon; }
void CombatComponent::setLastAttack(int lastAttack) noexcept {
lastAttack_ = lastAttack;
}
void CombatComponent::setDropRate(double dropRate) noexcept {
dropRate_ = dropRate;
}
void CombatComponent::recordAttack() noexcept { ++lastAttack_; }

// ============================================================================
// BuffComponent Implementation
// ============================================================================

const std::array<int, BUFF_END>& BuffComponent::buffs() const noexcept {
return buffs_;
}

std::array<int, BUFF_END>& BuffComponent::buffs() noexcept { return buffs_; }

int BuffComponent::buff(int index) const noexcept { return buffs_[index]; }

void BuffComponent::setBuff(int index, int value) noexcept {
buffs_[index] = value;
}

void BuffComponent::decrementBuff(int index) noexcept {
if (buffs_[index] > 0) {
--buffs_[index];
}
}

void BuffComponent::clearBuffs() noexcept { buffs_.fill(0); }

bool BuffComponent::isFrozen() const noexcept {
return buffs_[BUFF_FROZEN] > 0;
}

bool BuffComponent::isSlowed() const noexcept {
return buffs_[BUFF_SLOWDOWN] > 0;
}

bool BuffComponent::hasDefense() const noexcept {
return buffs_[BUFF_DEFFENCE] > 0;
}

bool BuffComponent::hasAttackUp() const noexcept {
return buffs_[BUFF_ATTACK] > 0;
}

// ============================================================================
// AIComponent Implementation
// ============================================================================

AIComponent::AIComponent(std::shared_ptr<AIBehavior> behavior)
: behavior_(std::move(behavior)) {}

AIBehavior* AIComponent::behavior() const noexcept { return behavior_.get(); }

bool AIComponent::hasBehavior() const noexcept { return behavior_ != nullptr; }

void AIComponent::setBehavior(
std::shared_ptr<AIBehavior> behavior) noexcept {
behavior_ = std::move(behavior);
}

void AIComponent::clearBehavior() noexcept { behavior_.reset(); }

// ============================================================================
// ScoreComponent Implementation
// ============================================================================

const std::shared_ptr<Score>& ScoreComponent::score() const noexcept {
return score_;
}

std::shared_ptr<Score>& ScoreComponent::score() noexcept { return score_; }

void ScoreComponent::setScore(const std::shared_ptr<Score>& score) noexcept {
score_ = score;
}

void ScoreComponent::reset() noexcept { score_.reset(); }
Loading
Loading