feat: add nextRandom and cellCentre
This commit is contained in:
1 parent
395ba54d52
commit
0bb172d35b
1 file changed
+160
@@ -0,0 +1,160 @@
|
|||||||
|
#include "vf/Raid.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace vf {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr float raidTickSeconds = 1.0f / static_cast<float>(ticksPerSecond);
|
||||||
|
|
||||||
|
uint32_t nextRandom(uint32_t& state) {
|
||||||
|
state += 0x9e3779b9u;
|
||||||
|
uint32_t z = state;
|
||||||
|
z = (z ^ (z >> 16)) * 0x85ebca6bu;
|
||||||
|
z = (z ^ (z >> 13)) * 0xc2b2ae35u;
|
||||||
|
return z ^ (z >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 cellCentre(const Vec3i& cell) {
|
||||||
|
return {static_cast<float>(cell.x) + 0.5f, static_cast<float>(cell.y),
|
||||||
|
static_cast<float>(cell.z) + 0.5f};
|
||||||
|
}
|
||||||
|
|
||||||
|
float horizontalDistance(const Vec3& a, const Vec3& b) {
|
||||||
|
float dx = a.x - b.x;
|
||||||
|
float dz = a.z - b.z;
|
||||||
|
return std::sqrt(dx * dx + dz * dz);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Raider::Stats statsTable[static_cast<int>(RaiderKind::Count)] = {
|
||||||
|
// maxHealth, damage, speed, attackRange, attackIntervalTicks
|
||||||
|
{24.0f, 3.0f, 2.6f, 1.8f, 20},
|
||||||
|
{24.0f, 6.0f, 2.9f, 1.8f, 18},
|
||||||
|
{26.0f, 4.0f, 2.4f, 6.0f, 30},
|
||||||
|
{100.0f, 12.0f, 3.1f, 2.4f, 30},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Walks a body along a cell path, returning true once the final cell is under
|
||||||
|
// its feet.
|
||||||
|
bool advanceAlong(Vec3& position, const std::vector<Vec3i>& path, int& cursor, float speed,
|
||||||
|
float dt) {
|
||||||
|
if (path.empty()) return false;
|
||||||
|
int last = static_cast<int>(path.size()) - 1;
|
||||||
|
float budget = speed * dt;
|
||||||
|
while (budget > 0.0f) {
|
||||||
|
Vec3 target = cellCentre(path[cursor]);
|
||||||
|
Vec3 delta = target - position;
|
||||||
|
float remaining = length(delta);
|
||||||
|
if (remaining <= budget) {
|
||||||
|
position = target;
|
||||||
|
budget -= remaining;
|
||||||
|
if (cursor >= last) return true;
|
||||||
|
++cursor;
|
||||||
|
} else {
|
||||||
|
position += normalize(delta) * budget;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nearest cell at or around `y` that a walker can stand on, searched outward so
|
||||||
|
// the result does not depend on world height.
|
||||||
|
bool standableNear(const World& world, const Vec3i& column, int verticalSearch,
|
||||||
|
const PathConfig& config, Vec3i& outCell) {
|
||||||
|
for (int offset = 0; offset <= verticalSearch; ++offset) {
|
||||||
|
Vec3i up{column.x, column.y + offset, column.z};
|
||||||
|
if (isStandable(world, up, config)) {
|
||||||
|
outCell = up;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (offset == 0) continue;
|
||||||
|
Vec3i down{column.x, column.y - offset, column.z};
|
||||||
|
if (isStandable(world, down, config)) {
|
||||||
|
outCell = down;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outCell = column;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiderKind kindForSlot(const RaidWave& plan, int slot) {
|
||||||
|
if (slot < plan.raiders) return RaiderKind::Raider;
|
||||||
|
slot -= plan.raiders;
|
||||||
|
if (slot < plan.vindicators) return RaiderKind::Vindicator;
|
||||||
|
slot -= plan.vindicators;
|
||||||
|
if (slot < plan.witches) return RaiderKind::Witch;
|
||||||
|
return RaiderKind::Ravager;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const char* raiderKindName(RaiderKind kind) {
|
||||||
|
switch (kind) {
|
||||||
|
case RaiderKind::Raider: return "raider";
|
||||||
|
case RaiderKind::Vindicator: return "vindicator";
|
||||||
|
case RaiderKind::Witch: return "witch";
|
||||||
|
case RaiderKind::Ravager: return "ravager";
|
||||||
|
case RaiderKind::Count: break;
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
const Raider::Stats& raiderStats(RaiderKind kind) { return statsTable[static_cast<int>(kind)]; }
|
||||||
|
|
||||||
|
Vec3i Raider::cell() const {
|
||||||
|
return {static_cast<int>(std::floor(position.x)), static_cast<int>(std::floor(position.y)),
|
||||||
|
static_cast<int>(std::floor(position.z))};
|
||||||
|
}
|
||||||
|
|
||||||
|
int RaidWave::countOf(RaiderKind kind) const {
|
||||||
|
switch (kind) {
|
||||||
|
case RaiderKind::Raider: return raiders;
|
||||||
|
case RaiderKind::Vindicator: return vindicators;
|
||||||
|
case RaiderKind::Witch: return witches;
|
||||||
|
case RaiderKind::Ravager: return ravagers;
|
||||||
|
case RaiderKind::Count: break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int raidWaveCount(int difficulty) {
|
||||||
|
if (difficulty <= raidDifficultyEasy) return 3;
|
||||||
|
if (difficulty == raidDifficultyNormal) return 5;
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaidWave planRaidWave(int waveIndex, int badOmenLevel, int difficulty) {
|
||||||
|
RaidWave plan;
|
||||||
|
plan.index = waveIndex < 0 ? 0 : waveIndex;
|
||||||
|
int omen = badOmenLevel < 1 ? 1 : badOmenLevel;
|
||||||
|
|
||||||
|
plan.raiders = 2 + plan.index + (difficulty >= raidDifficultyHard ? 1 : 0);
|
||||||
|
if (plan.index >= 1)
|
||||||
|
plan.vindicators = plan.index / 2 + (difficulty >= raidDifficultyNormal ? 1 : 0);
|
||||||
|
if (plan.index >= 2) plan.witches = 1 + (difficulty >= raidDifficultyHard ? 1 : 0);
|
||||||
|
if (plan.index >= 3) plan.ravagers = 1;
|
||||||
|
if (omen >= 3) plan.vindicators += 1;
|
||||||
|
if (omen >= 4 && plan.index >= 2) plan.ravagers += 1;
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyBadOmen(OmenState& omen, int level, int durationTicks) {
|
||||||
|
int capped = level > badOmenMaxLevel ? badOmenMaxLevel : level;
|
||||||
|
if (capped < 1) return;
|
||||||
|
if (capped >= omen.level) {
|
||||||
|
omen.level = capped;
|
||||||
|
omen.ticksRemaining = durationTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void grantHeroOfTheVillage(OmenState& omen, int level, int durationTicks) {
|
||||||
|
int granted = level < 1 ? 1 : level;
|
||||||
|
if (granted >= omen.heroLevel) {
|
||||||
|
omen.heroLevel = granted;
|
||||||
|
omen.heroTicksRemaining = durationTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user