feat: add distanceBetween and crystalCenter

This commit is contained in:
dualfroz committed 2026-09-06 18:05:56 +02:00
1 parent bdad4a344b
commit 1748324d41
1 file changed
+156
+156
View File
@@ -0,0 +1,156 @@
#include "vf/Boss.h"
#include <algorithm>
#include <cmath>
#include <utility>
namespace vf {
namespace {
constexpr float fullTurn = 6.2831853f;
float distanceBetween(const Vec3& a, const Vec3& b) { return length(a - b); }
std::unique_ptr<BehaviorNode> cond(const char* name, BehaviorPredicate predicate) {
return std::unique_ptr<BehaviorNode>(new ConditionNode(name, std::move(predicate)));
}
std::unique_ptr<BehaviorNode> act(const char* name, BehaviorAction fn) {
return std::unique_ptr<BehaviorNode>(new ActionNode(name, std::move(fn)));
}
std::unique_ptr<SequenceNode> seq(const char* name) {
return std::unique_ptr<SequenceNode>(new SequenceNode(name));
}
std::unique_ptr<SelectorNode> sel(const char* name) {
return std::unique_ptr<SelectorNode>(new SelectorNode(name));
}
std::unique_ptr<BehaviorNode> gated(const char* name, std::unique_ptr<BehaviorNode> child,
int ticks) {
return std::unique_ptr<BehaviorNode>(new CooldownNode(name, std::move(child), ticks));
}
std::unique_ptr<BehaviorNode> bounded(const char* name, std::unique_ptr<BehaviorNode> child,
int ticks) {
return std::unique_ptr<BehaviorNode>(new TimeLimitNode(name, std::move(child), ticks));
}
Vec3 crystalCenter(const Vec3i& cell) {
return {static_cast<float>(cell.x) + 0.5f, static_cast<float>(cell.y) + 0.5f,
static_cast<float>(cell.z) + 0.5f};
}
Vec3i witherOffset(const Vec3i& base, int along, int up, bool alongX) {
return alongX ? Vec3i{base.x + along, base.y + up, base.z}
: Vec3i{base.x, base.y + up, base.z + along};
}
} // namespace
const char* bossKindName(BossKind kind) {
switch (kind) {
case BossKind::EnderDragon: return "enderDragon";
case BossKind::Wither: return "wither";
case BossKind::Count: break;
}
return "unknown";
}
const char* bossPhaseName(BossPhase phase) {
switch (phase) {
case BossPhase::Dormant: return "dormant";
case BossPhase::Circling: return "circling";
case BossPhase::Charging: return "charging";
case BossPhase::Breathing: return "breathing";
case BossPhase::Perching: return "perching";
case BossPhase::Summoning: return "summoning";
case BossPhase::Explosive: return "explosive";
case BossPhase::Ranged: return "ranged";
case BossPhase::Armored: return "armored";
case BossPhase::Dying: return "dying";
case BossPhase::Defeated: return "defeated";
case BossPhase::Count: break;
}
return "unknown";
}
int BossBar::notchCount() const {
switch (style) {
case BossBarStyle::Solid: return 1;
case BossBarStyle::Notched6: return 6;
case BossBarStyle::Notched10: return 10;
case BossBarStyle::Notched12: return 12;
case BossBarStyle::Notched20: return 20;
case BossBarStyle::Count: break;
}
return 1;
}
int BossBar::filledNotches() const {
float exact = progress * static_cast<float>(notchCount());
int whole = static_cast<int>(std::floor(exact + 1e-4f));
return std::max(0, std::min(notchCount(), whole));
}
int BossBar::percent() const {
return static_cast<int>(std::floor(progress * 100.0f + 0.5f));
}
void BossBar::setFromHealth(float health, float maxHealth) {
if (maxHealth <= 0.0f) {
progress = 0.0f;
return;
}
float fraction = health / maxHealth;
progress = std::max(0.0f, std::min(1.0f, fraction));
}
void BossState::enterPhase(BossPhase next) {
if (phase == next) return;
phase = next;
phaseTicks = 0;
++phaseChanges;
}
float BossState::healthFraction() const {
if (maxHealth <= 0.0f) return 0.0f;
return health / maxHealth;
}
std::vector<CrystalNode> crystalsFromPillars(const std::vector<ObsidianPillar>& pillars) {
std::vector<CrystalNode> nodes;
nodes.reserve(pillars.size());
for (const ObsidianPillar& pillar : pillars) {
CrystalNode node;
node.pillarBase = pillar.base;
node.pillarHeight = pillar.height;
node.position = {pillar.base.x, pillar.base.y + pillar.height + 1, pillar.base.z};
node.caged = pillar.crowned;
node.cageIntact = pillar.crowned;
nodes.push_back(node);
}
return nodes;
}
DragonFight::DragonFight(const EndGenerator& end, uint32_t seed, const DragonFightConfig& config)
: cfg(config) {
boss.kind = BossKind::EnderDragon;
boss.maxHealth = cfg.maxHealth;
boss.health = cfg.maxHealth;
boss.rng = seed != 0u ? seed : 0x51ed270bu;
boss.phase = BossPhase::Circling;
boss.bar.title = "Ender Dragon";
boss.bar.color = BossBarColor::Pink;
boss.bar.style = BossBarStyle::Notched10;
boss.bar.visible = true;
boss.bar.setFromHealth(boss.health, boss.maxHealth);
pillarCrystals = crystalsFromPillars(end.pillars());
int surface = end.surfaceHeight(0, 0);
portal = {0.5f, static_cast<float>(surface), 0.5f};
eggCell = {0, surface + 1, 0};