Files
Ascent-of-Man/unit.h
T
2026-11-10 20:31:33 +01:00

141 lines
4.3 KiB
C++

#ifndef UNIT_H
#define UNIT_H
#include <QString>
#include <QVector3D>
#include <vector>
#include <deque>
#include <utility>
#include "unitconfig.h"
#include "terrain.h"
#include "matrix2d.h"
enum class UnitState {
Idle,
Moving,
Attacking,
Building
};
enum class UnitActionType {
None,
Move,
BuildImprovement,
BuildCity,
BuildRoad,
Attack
};
struct UnitAction {
UnitActionType type = UnitActionType::None;
int targetX = -1;
int targetY = -1;
int targetUnitId = -1;
QString extraData = ""; // e.g. improvementId ("farm")
UnitAction() = default;
UnitAction(UnitActionType t) : type(t) {}
UnitAction(UnitActionType t, int x, int y, const QString &data = "", int uId = -1)
: type(t), targetX(x), targetY(y), targetUnitId(uId), extraData(data) {}
UnitAction(UnitActionType t, int targetId)
: type(t), targetUnitId(targetId) {}
bool isValid() const { return type != UnitActionType::None; }
};
class Unit {
public:
Unit(int uId, int pId, const UnitConfig &cfg, int startX, int startY, float groundZ);
int id;
int playerId;
UnitConfig config;
int tileX;
int tileY;
QVector3D worldPos;
float health;
float maxHealth;
int buildCharges;
// Simulation parameters (set during unit creation or via GameManager)
float movement_tick_duration; // Seconds per simulation tick
UnitState state;
// Movement Path
std::vector<std::pair<int, int>> path;
size_t pathIndex;
float moveProgress; // 0.0 to 1.0 between hexes
int targetTileX;
int targetTileY;
// Currently executing action & subsequent Action Queue
UnitAction currentAction;
std::deque<UnitAction> actionQueue;
int maxQueueSize = 15;
// Road Construction Route (tile-by-tile road paving)
std::vector<std::pair<int, int>> roadRoute;
size_t roadRouteIndex = 0;
bool hasRoadRoute() const { return !roadRoute.empty() && roadRouteIndex < roadRoute.size(); }
void startRoadRoute(const std::vector<std::pair<int, int>> &route) {
roadRoute = route;
roadRouteIndex = 0;
}
void clearRoadRoute() {
roadRoute.clear();
roadRouteIndex = 0;
}
void advanceRoadRouteStep() {
if (!roadRoute.empty()) {
roadRouteIndex++;
}
}
bool hasActiveAction() const {
return currentAction.isValid() || state != UnitState::Idle || hasRoadRoute();
}
bool queueAction(const UnitAction &action);
void clearActionQueue();
bool hasQueuedAction() const { return !actionQueue.empty(); }
bool hasQueuedActionType(UnitActionType type) const;
bool hasQueuedImprovementAt(int tx, int ty) const;
bool hasQueuedCityAt(int tx, int ty) const;
void removeQueuedActionsOfType(UnitActionType type);
UnitAction popNextQueuedAction();
const UnitAction* peekNextQueuedAction() const;
QString getActionSummary(const UnitAction &act) const;
QString getQueuedActionSummary() const;
// Attack cooldown / flash
float attackFlashTimer; // Flash effect timer (visual feedback after attack or taking damage)
float rechargeTimer; // Recharge/cooldown timer (time remaining until unit can attack again)
bool isRecharging() const; // Returns true if unit is currently on cooldown
// Building State
float buildProgress; // 0.0 to 1.0
float buildDuration;
QString buildingImprovementId;
void update(float deltaTime, float tickDuration, float simSpeedMultiplier,
Matrix2D<TerrainPoint> *mapPoints, int numPoints05);
void setPath(const std::vector<std::pair<int, int>> &newPath);
void stopMovement();
void startBuilding(const QString &impId, float duration);
void cancelBuilding();
bool isBuilding() const { return state == UnitState::Building; }
bool isBusy() const { return state == UnitState::Building; }
bool canAttack(const Unit &other) const;
void performAttack(Unit &target);
void takeDamage(float damage);
bool isDead() const { return health <= 0.001f; }
bool isNaval() const { return config.isNaval(); }
bool isLand() const { return config.isLand(); }
static float getTerrainHeight(float worldX, float worldY, Matrix2D<TerrainPoint> *mapPoints, int numPoints05);
};
#endif // UNIT_H