ticks
This commit is contained in:
@@ -104,6 +104,30 @@ To implement the RTS Civilization game, develop modular subsystems decoupled fro
|
|||||||
+-----------------+
|
+-----------------+
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3.1 Simulation Time & Tick System
|
||||||
|
|
||||||
|
The game combines continuous real-time rendering and interpolation (`deltaTime`) with a configurable **Simulation Tick** (`movement_tick_duration` in `config/game.yaml`, defaulting to `30.0` seconds at $1\times$ simulation speed):
|
||||||
|
|
||||||
|
1. **Tick Timer & Speed Multiplier:**
|
||||||
|
- `GameManager::update` advances an internal tick timer by `deltaTime * simSpeedMultiplier`.
|
||||||
|
- When the timer reaches `movement_tick_duration`, a simulation tick fires (`processTickYields`).
|
||||||
|
2. **Discrete Per-Tick Economy & Progression (`processTickYields`):**
|
||||||
|
- **Food & Population:** City food yield is converted into inhabitants (`food * foodToInhabitantsRatio`).
|
||||||
|
- **Labor (Production):** City labor yield is added to the active construction project (`currentLabor`), completing units or buildings when `laborCost` is reached, or accumulating locally if idle.
|
||||||
|
- **National Stockpiles:** Culture, Faith, and Gold yields from all owned cities are added to the player's national treasury.
|
||||||
|
- **Science & Research:** Total science generated per tick across a player's cities is applied directly to the active technology in the player's `researchQueue`.
|
||||||
|
3. **Continuous Real-Time Unit Movement (`movement_speed`, Scaled by Tick Duration):**
|
||||||
|
- Units move smoothly every frame, and their movement rate is defined in **movement points (base hexes) per tick** (`movement_speed` in `config/units/*.yaml`, stored in `UnitConfig::movementSpeed` — higher values mean faster movement, e.g., `2.0` for infantry/civilian, `3.0` for galley, `4.0` for cavalry).
|
||||||
|
- Effective speed in hexes per second is calculated dynamically per hex segment as:
|
||||||
|
$$\text{hexesPerSec} = \frac{\text{movementSpeed}}{\text{tickDuration} \times \text{stepCost}} \times \text{simSpeedMultiplier}$$
|
||||||
|
- On flat terrain (`stepCost = 1.0`), a unit with `movementSpeed = 2.0` traverses $2.0$ hexes during one full simulation tick ($0.5$ ticks per hex).
|
||||||
|
4. **Unit Construction & Action Durations (`action_durations`):**
|
||||||
|
- Unlike `movement_speed` (which is a rate per tick), unit build actions (such as `boerderij_bouwen` and `build_road` under `action_durations` in `config/units/builder.yaml`) are configured as **durations in simulation ticks** (ticks per action, accessed via `UnitConfig::getActionDuration`).
|
||||||
|
- Total real-time build duration in seconds is calculated as:
|
||||||
|
$$\text{duration} = \text{ticks} \times \text{movement\_tick\_duration}$$
|
||||||
|
- While building, `buildProgress` interpolates continuously from $0.0$ to $1.0$ over `duration` seconds (scaled by `simSpeedMultiplier`).
|
||||||
|
- Similarly, city production (`labor_cost` in `config/units/*.yaml` and `config/buildings.yaml`) and technology research (`science_cost` in `config/technologies.yaml`) progress discretely each tick based on the city/empire's per-tick yield.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Agents & Unit Specifications
|
## 4. Agents & Unit Specifications
|
||||||
@@ -124,7 +148,7 @@ Units are autonomous or player-directed entities traversing the hex map in real
|
|||||||
- **Siege:** Catapult $\rightarrow$ Trebuchet $\rightarrow$ Bombard (high bonus damage against city walls).
|
- **Siege:** Catapult $\rightarrow$ Trebuchet $\rightarrow$ Bombard (high bonus damage against city walls).
|
||||||
- **Naval:** Galley $\rightarrow$ Caravel $\rightarrow$ Frigate $\rightarrow$ Battleship (operates only on water hexes).
|
- **Naval:** Galley $\rightarrow$ Caravel $\rightarrow$ Frigate $\rightarrow$ Battleship (operates only on water hexes).
|
||||||
|
|
||||||
### 4.2 Unit Data Model (`Unit.h`)
|
### 4.2 Unit Data Model (`unit.h` / `unitconfig.h`)
|
||||||
```cpp
|
```cpp
|
||||||
enum class UnitType { Settler, Worker, Warrior, Archer, Horseman, Galley };
|
enum class UnitType { Settler, Worker, Warrior, Archer, Horseman, Galley };
|
||||||
enum class UnitState { Idle, Moving, Attacking, Harvesting, Building, Fortified };
|
enum class UnitState { Idle, Moving, Attacking, Harvesting, Building, Fortified };
|
||||||
@@ -146,14 +170,14 @@ public:
|
|||||||
float health, maxHealth;
|
float health, maxHealth;
|
||||||
float attackDamage;
|
float attackDamage;
|
||||||
float defense;
|
float defense;
|
||||||
float movementSpeed; // hexes per second
|
float movementSpeed; // movement points (base hexes) per simulation tick
|
||||||
float visionRange; // in hex radius
|
float visionRange; // in hex radius
|
||||||
|
|
||||||
// Path & Target
|
// Path & Target
|
||||||
std::vector<std::pair<int, int>> currentPath;
|
std::vector<std::pair<int, int>> currentPath;
|
||||||
int targetEntityId; // target unit or city
|
int targetEntityId; // target unit or city
|
||||||
|
|
||||||
void update(float deltaTime);
|
void update(float deltaTime, float tickDuration, float simSpeedMultiplier);
|
||||||
void commandMoveTo(int destX, int destY);
|
void commandMoveTo(int destX, int destY);
|
||||||
void commandAttack(int targetId);
|
void commandAttack(int targetId);
|
||||||
void commandBuildImprovement(int improvementType);
|
void commandBuildImprovement(int improvementType);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ ui:
|
|||||||
action_scheduled_after_current: "%1 scheduled after current action"
|
action_scheduled_after_current: "%1 scheduled after current action"
|
||||||
action_cancelled: "%1 cancelled"
|
action_cancelled: "%1 cancelled"
|
||||||
farm_queued: "Build Farm queued"
|
farm_queued: "Build Farm queued"
|
||||||
farm_started: "Constructing Farm..."
|
farm_started: "Constructing Farm... (%1 ticks)"
|
||||||
farm_completed: "Farm completed!"
|
farm_completed: "Farm completed!"
|
||||||
farm_completed_consumed: "Farm completed! (Builder consumed)"
|
farm_completed_consumed: "Farm completed! (Builder consumed)"
|
||||||
farm_completed_charges: "Farm completed! (%1 %2 remaining)"
|
farm_completed_charges: "Farm completed! (%1 %2 remaining)"
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 2
|
|||||||
required_tech: "archery"
|
required_tech: "archery"
|
||||||
obsolete_tech: "machinery"
|
obsolete_tech: "machinery"
|
||||||
icon: images/units/slinger.png
|
icon: images/units/slinger.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ actions:
|
|||||||
- boerderij_bouwen
|
- boerderij_bouwen
|
||||||
- build_road
|
- build_road
|
||||||
recharge_time: 500
|
recharge_time: 500
|
||||||
recharge_duration: 300
|
|
||||||
attack_flash_duration: 400
|
attack_flash_duration: 400
|
||||||
action_speeds:
|
action_durations:
|
||||||
boerderij_bouwen: 10.0
|
boerderij_bouwen: 2.0
|
||||||
build_road: 2
|
build_road: 2.0
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 2
|
|||||||
required_tech: "machinery"
|
required_tech: "machinery"
|
||||||
obsolete_tech: ""
|
obsolete_tech: ""
|
||||||
icon: images/units/slinger.png
|
icon: images/units/slinger.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 1
|
|||||||
required_tech: "sailing"
|
required_tech: "sailing"
|
||||||
obsolete_tech: ""
|
obsolete_tech: ""
|
||||||
icon: images/units/galley.png
|
icon: images/units/galley.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 1
|
|||||||
required_tech: "horseback_riding"
|
required_tech: "horseback_riding"
|
||||||
obsolete_tech: "stirrups"
|
obsolete_tech: "stirrups"
|
||||||
icon: images/units/warrior.png
|
icon: images/units/warrior.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 1
|
|||||||
required_tech: "stirrups"
|
required_tech: "stirrups"
|
||||||
obsolete_tech: ""
|
obsolete_tech: ""
|
||||||
icon: images/units/warrior.png
|
icon: images/units/warrior.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 1
|
|||||||
required_tech: "gunpowder"
|
required_tech: "gunpowder"
|
||||||
obsolete_tech: ""
|
obsolete_tech: ""
|
||||||
icon: images/units/warrior.png
|
icon: images/units/warrior.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ labor_cost: 2000
|
|||||||
required_tech: ""
|
required_tech: ""
|
||||||
obsolete_tech: ""
|
obsolete_tech: ""
|
||||||
icon: images/units/settler.png
|
icon: images/units/settler.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- steden_bouwen
|
- steden_bouwen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 2
|
|||||||
required_tech: ""
|
required_tech: ""
|
||||||
obsolete_tech: "archery"
|
obsolete_tech: "archery"
|
||||||
icon: images/units/slinger.png
|
icon: images/units/slinger.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 1
|
|||||||
required_tech: "bronze_working"
|
required_tech: "bronze_working"
|
||||||
obsolete_tech: "gunpowder"
|
obsolete_tech: "gunpowder"
|
||||||
icon: images/units/warrior.png
|
icon: images/units/warrior.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 1
|
|||||||
required_tech: "iron_working"
|
required_tech: "iron_working"
|
||||||
obsolete_tech: "gunpowder"
|
obsolete_tech: "gunpowder"
|
||||||
icon: images/units/warrior.png
|
icon: images/units/warrior.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ attack_range: 1
|
|||||||
required_tech: ""
|
required_tech: ""
|
||||||
obsolete_tech: "iron_working"
|
obsolete_tech: "iron_working"
|
||||||
icon: images/units/warrior.png
|
icon: images/units/warrior.png
|
||||||
recharge_time: 500
|
recharge_ticks: 2
|
||||||
recharge_duration: 300
|
attack_flash_ticks: 1
|
||||||
attack_flash_duration: 400
|
|
||||||
actions:
|
actions:
|
||||||
- verplaatsen
|
- verplaatsen
|
||||||
- aanvallen
|
- aanvallen
|
||||||
|
|||||||
+6
-5
@@ -1265,9 +1265,10 @@ void GameManager::continueCurrentUnitAction(Unit &unit, Matrix2D<TerrainPoint> *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unit is on the target tile: start constructing the improvement
|
// Unit is on the target tile: start constructing the improvement
|
||||||
float duration = unit.config.getActionDuration("boerderij_bouwen", 10.0f);
|
float ticks = unit.config.getActionDuration("boerderij_bouwen", 2.0f);
|
||||||
|
float duration = ticks * m_movementTickDuration;
|
||||||
unit.startBuilding(impId, duration);
|
unit.startBuilding(impId, duration);
|
||||||
addFloatingText(Localization::instance().get("ui.unit.farm_started", "Constructing Farm..."),
|
addFloatingText(Localization::instance().get("ui.unit.farm_started", "Constructing Farm... (%1 ticks)").arg(ticks, 0, 'g', 3),
|
||||||
unit.worldPos + QVector3D(0, 0, 0.8f), QColor(255, 220, 80), 2.0f);
|
unit.worldPos + QVector3D(0, 0, 0.8f), QColor(255, 220, 80), 2.0f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1789,10 +1790,10 @@ void GameManager::advanceRoadRoute(Unit &unit, Matrix2D<TerrainPoint> *mapPoints
|
|||||||
QString defaultType = m_roadConfigManager.defaultRoadType();
|
QString defaultType = m_roadConfigManager.defaultRoadType();
|
||||||
const RoadTypeConfig *rCfg = m_roadConfigManager.getRoadType(defaultType);
|
const RoadTypeConfig *rCfg = m_roadConfigManager.getRoadType(defaultType);
|
||||||
QString roadName = rCfg ? rCfg->name : "Road";
|
QString roadName = rCfg ? rCfg->name : "Road";
|
||||||
int ticks = m_roadConfigManager.buildDurationTicks();
|
float ticks = unit.config.getActionDuration("build_road", (float)m_roadConfigManager.buildDurationTicks());
|
||||||
float duration = (float)ticks * m_movementTickDuration;
|
float duration = ticks * m_movementTickDuration;
|
||||||
unit.startBuilding(QString("road:%1").arg(defaultType), duration);
|
unit.startBuilding(QString("road:%1").arg(defaultType), duration);
|
||||||
addFloatingText(Localization::instance().get("ui.unit.road_started", "Constructing %1... (%2 ticks)").arg(roadName).arg(ticks),
|
addFloatingText(Localization::instance().get("ui.unit.road_started", "Constructing %1... (%2 ticks)").arg(roadName).arg(ticks, 0, 'g', 3),
|
||||||
unit.worldPos + QVector3D(0, 0, 0.8f), QColor(255, 220, 80), 2.0f);
|
unit.worldPos + QVector3D(0, 0, 0.8f), QColor(255, 220, 80), 2.0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -770,8 +770,8 @@ void QSceneRenderer::drawUnitsOverlay(QPainter &painter, const QMatrix4x4 &vMatr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recharge bar below military units when recharging
|
// Recharge bar below military units when recharging
|
||||||
if (unit->config.canAttack() && unit->isRecharging()) {
|
if (unit->isRecharging()) {
|
||||||
float rechargePercent = std::max(0.0f, std::min(1.0f, unit->rechargeTimer / unit->config.rechargeTime));
|
float rechargePercent = std::max(0.0f, std::min(1.0f, unit->rechargeTimer / unit->config.rechargeTicks));
|
||||||
QRectF recBg(headCenter.x() - 14.0f, headCenter.y() - R - 5.5f, 28.0f, 3.5f);
|
QRectF recBg(headCenter.x() - 14.0f, headCenter.y() - R - 5.5f, 28.0f, 3.5f);
|
||||||
painter.setBrush(QColor(30, 30, 30, 220));
|
painter.setBrush(QColor(30, 30, 30, 220));
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ Unit::Unit(int uId, int pId, const UnitConfig &cfg, int startX, int startY, floa
|
|||||||
buildCharges(cfg.buildCharges),
|
buildCharges(cfg.buildCharges),
|
||||||
state(UnitState::Idle), pathIndex(0), moveProgress(0.0f),
|
state(UnitState::Idle), pathIndex(0), moveProgress(0.0f),
|
||||||
targetTileX(startX), targetTileY(startY), attackFlashTimer(0.0f), rechargeTimer(0.0f),
|
targetTileX(startX), targetTileY(startY), attackFlashTimer(0.0f), rechargeTimer(0.0f),
|
||||||
buildProgress(0.0f), buildDuration(0.0f), buildingImprovementId("")
|
buildProgress(0.0f), buildDuration(0.0f), buildingImprovementId(""),
|
||||||
|
movement_tick_duration(30.0f) // Default: 30 seconds per tick at 1x speed
|
||||||
{
|
{
|
||||||
QVector2D w = Pathfinder::hexToWorld(startX, startY);
|
QVector2D w = Pathfinder::hexToWorld(startX, startY);
|
||||||
worldPos = QVector3D(w.x(), w.y(), groundZ);
|
worldPos = QVector3D(w.x(), w.y(), groundZ);
|
||||||
@@ -43,8 +44,8 @@ void Unit::performAttack(Unit &target)
|
|||||||
{
|
{
|
||||||
if (!canAttack(target)) return;
|
if (!canAttack(target)) return;
|
||||||
|
|
||||||
attackFlashTimer = config.attackFlashDuration / 1000.0f; // Convert ms to seconds
|
attackFlashTimer = config.attackFlashTicks * movement_tick_duration; // Convert ticks to seconds
|
||||||
rechargeTimer = config.rechargeTime / 1000.0f; // Convert ms to seconds
|
rechargeTimer = config.rechargeTicks * movement_tick_duration; // Convert ticks to seconds
|
||||||
|
|
||||||
target.takeDamage((float)config.attackDamage);
|
target.takeDamage((float)config.attackDamage);
|
||||||
|
|
||||||
@@ -60,8 +61,8 @@ void Unit::performAttack(Unit &target)
|
|||||||
void Unit::takeDamage(float damage)
|
void Unit::takeDamage(float damage)
|
||||||
{
|
{
|
||||||
health = std::max(0.0f, health - damage);
|
health = std::max(0.0f, health - damage);
|
||||||
attackFlashTimer = 0.4f;
|
attackFlashTimer = movement_tick_duration; // Flash lasts 1 tick
|
||||||
rechargeTimer = config.rechargeTime / 1000.0f; // Reset recharge on taking damage
|
rechargeTimer = config.rechargeTicks * movement_tick_duration; // Reset recharge to full duration
|
||||||
}
|
}
|
||||||
|
|
||||||
float Unit::getTerrainHeight(float worldX, float worldY, Matrix2D<TerrainPoint> *mapPoints, int numPoints05)
|
float Unit::getTerrainHeight(float worldX, float worldY, Matrix2D<TerrainPoint> *mapPoints, int numPoints05)
|
||||||
@@ -247,8 +248,10 @@ void Unit::update(float deltaTime, float tickDuration, float simSpeedMultiplier,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update recharge timer (cooldown between attacks)
|
// Update recharge timer (cooldown between attacks)
|
||||||
|
// Recharge decays based on simulation ticks, not real-time multiplier
|
||||||
if (rechargeTimer > 0.0f) {
|
if (rechargeTimer > 0.0f) {
|
||||||
rechargeTimer = std::max(0.0f, rechargeTimer - deltaTime * simSpeedMultiplier);
|
float tickDurationSec = deltaTime / movement_tick_duration;
|
||||||
|
rechargeTimer = std::max(0.0f, rechargeTimer - tickDurationSec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == UnitState::Building) {
|
if (state == UnitState::Building) {
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ public:
|
|||||||
float health;
|
float health;
|
||||||
float maxHealth;
|
float maxHealth;
|
||||||
int buildCharges;
|
int buildCharges;
|
||||||
|
|
||||||
|
// Simulation parameters (set during unit creation or via GameManager)
|
||||||
|
float movement_tick_duration; // Seconds per simulation tick
|
||||||
UnitState state;
|
UnitState state;
|
||||||
|
|
||||||
// Movement Path
|
// Movement Path
|
||||||
|
|||||||
+2
-2
@@ -82,11 +82,11 @@ bool UnitConfigManager::loadConfigFile(const QString &filePath)
|
|||||||
if (root.hasChild(key)) {
|
if (root.hasChild(key)) {
|
||||||
const YamlNode &sNode = root[key];
|
const YamlNode &sNode = root[key];
|
||||||
for (auto it = sNode.children.begin(); it != sNode.children.end(); ++it) {
|
for (auto it = sNode.children.begin(); it != sNode.children.end(); ++it) {
|
||||||
cfg.actionSpeeds[it.key()] = it.value().asFloat(10.0f);
|
cfg.actionSpeeds[it.key()] = it.value().asFloat(2.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
parseActionMap("action_speeds");
|
parseActionMap("action_durations");
|
||||||
parseActionMap("action_durations");
|
parseActionMap("action_durations");
|
||||||
|
|
||||||
m_configs[cfg.id] = cfg;
|
m_configs[cfg.id] = cfg;
|
||||||
|
|||||||
+8
-10
@@ -48,23 +48,21 @@ struct UnitConfig {
|
|||||||
QString obsoleteTech;
|
QString obsoleteTech;
|
||||||
QString iconPath;
|
QString iconPath;
|
||||||
QStringList actions;
|
QStringList actions;
|
||||||
QMap<QString, float> actionSpeeds; // Duration in seconds per action (e.g. "boerderij_bouwen": 10.0f)
|
QMap<QString, float> actionSpeeds; // Duration in simulation ticks per action (e.g. "boerderij_bouwen": 2.0f)
|
||||||
QStringList impassableTerrains;
|
QStringList impassableTerrains;
|
||||||
QStringList passableTerrains;
|
QStringList passableTerrains;
|
||||||
|
|
||||||
// Recharge / Cooldown configuration for military units
|
// Recharge / Cooldown configuration for military units (in simulation ticks)
|
||||||
int rechargeTime; // Time in milliseconds between attacks
|
int rechargeTicks; // Ticks between attacks for military units
|
||||||
float rechargeDuration; // Duration of recharge state (optional, for visual indication)
|
int attackFlashTicks; // Ticks for visual attack flash effect
|
||||||
int attackFlashDuration; // How long the flash effect lasts (in ms)
|
|
||||||
|
|
||||||
UnitConfig()
|
UnitConfig()
|
||||||
: id(""), name(""), category(UnitCategory::Builder),
|
: id(""), name(""), category(UnitCategory::Builder),
|
||||||
movementDomain(MovementDomain::Land), health(100),
|
movementDomain(MovementDomain::Land), health(100),
|
||||||
movementSpeed(2.0f), cost(10), laborCost(1000), attackDamage(0), attackRange(1),
|
movementSpeed(2.0f), cost(10), laborCost(1000), attackDamage(0), attackRange(1),
|
||||||
buildCharges(-1), infiniteCharges(true), requiredTech(""), obsoleteTech(""), iconPath(""),
|
buildCharges(-1), infiniteCharges(true), requiredTech(""), obsoleteTech(""), iconPath(""),
|
||||||
rechargeTime(500), // Default recharge time: 500ms (0.5 seconds)
|
rechargeTicks(2), // Default recharge: 2 simulation ticks
|
||||||
rechargeDuration(300), // Visual cooldown bar duration: 300ms
|
attackFlashTicks(1) // Attack flash effect: 1 tick
|
||||||
attackFlashDuration(400) // Attack flash effect duration: 400ms
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool isRanged() const { return attackRange > 1; }
|
bool isRanged() const { return attackRange > 1; }
|
||||||
@@ -77,11 +75,11 @@ struct UnitConfig {
|
|||||||
bool canBuildRoad() const { return actions.contains("build_road") || actions.contains("weg_bouwen"); }
|
bool canBuildRoad() const { return actions.contains("build_road") || actions.contains("weg_bouwen"); }
|
||||||
bool isBuilder() const { return canBuildFarm() || canBuildRoad(); }
|
bool isBuilder() const { return canBuildFarm() || canBuildRoad(); }
|
||||||
|
|
||||||
float getActionDuration(const QString &actionName, float fallback = 10.0f) const {
|
float getActionDuration(const QString &actionName, float fallbackTicks = 2.0f) const {
|
||||||
if (actionSpeeds.contains(actionName)) {
|
if (actionSpeeds.contains(actionName)) {
|
||||||
return actionSpeeds.value(actionName);
|
return actionSpeeds.value(actionName);
|
||||||
}
|
}
|
||||||
return fallback;
|
return fallbackTicks;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user