From 5d7081ff32f57fd0043e805cb655e9ecb208b0b2 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 1 Sep 2026 17:36:12 +0200 Subject: [PATCH] feat(vf): add WitherFightConfig and WitherTarget to Boss --- core/include/vf/Boss.h | 128 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/core/include/vf/Boss.h b/core/include/vf/Boss.h index 0e8d12f..6cdbb68 100644 --- a/core/include/vf/Boss.h +++ b/core/include/vf/Boss.h @@ -158,3 +158,131 @@ public: DragonFight(DragonFight&&) = delete; DragonFight& operator=(DragonFight&&) = delete; + BossTickReport tick(const DragonPlayerInput& input); + + const BossState& state() const { return boss; } + const DragonFightConfig& settings() const { return cfg; } + const std::vector& crystals() const { return pillarCrystals; } + int aliveCrystalCount() const; + // Taken from the End generator: the fight orbits and perches on the real + // exit portal column, not on an invented origin. + Vec3 portalCenter() const { return portal; } + + bool exitPortalOpen() const { return portalOpen; } + bool dragonEggDropped() const { return eggDropped; } + Vec3i eggPosition() const { return eggCell; } + + const BehaviorTree& brain() const { return tree; } + const BehaviorContext& context() const { return ctx; } + + // Writes the reward the fight earned: the real End exit portal from + // EndGen. Refuses while the dragon still lives. + bool placeVictoryStructures(World& world, const EndGenerator& end) const; + +private: + void buildTree(); + int resolvePlayerShot(const DragonPlayerInput& input); + void applyPlayerMelee(const DragonPlayerInput& input); + float applyCrystalHealing(); + + DragonFightConfig cfg; + BossState boss; + std::vector pillarCrystals; + BehaviorTree tree; + BehaviorContext ctx; + DragonPlayerInput lastInput; + Vec3 portal; + Vec3i eggCell; + bool portalOpen = false; + bool eggDropped = false; + float damageThisTick = 0.0f; +}; + +struct WitherFightConfig { + float maxHealth = 300.0f; + int summonTicks = 220; + float summonBlastRadius = 8.0f; + float summonBlastDamage = 12.0f; + float armorThreshold = 0.5f; + float projectileDamage = 5.0f; + float projectileRange = 32.0f; + int projectileCooldownTicks = 20; + int minionCooldownTicks = 100; + int maxMinions = 3; + float minionSpawnRange = 6.0f; + float armoredRegenPerTick = 0.06f; + float moveStep = 0.35f; + float minTargetDistance = 6.0f; +}; + +struct WitherTarget { + Vec3 position; + float health = 20.0f; + bool alive = true; +}; + +struct BossMinion { + Vec3 position; + float health = 6.0f; + bool alive = true; + long long spawnedTick = 0; +}; + +struct WitherHit { + float amount = 0.0f; + bool projectile = false; +}; + +// The block pattern that calls the wither: a soul sand T topped with three +// heads, readable along either horizontal axis. +Block witherBaseBlock(); +Block witherHeadBlock(); +bool witherPatternAt(const World& world, const Vec3i& baseCell, bool alongX); +bool findWitherPattern(const World& world, const Vec3i& baseCell, bool& outAlongX); +void consumeWitherPattern(World& world, const Vec3i& baseCell, bool alongX); + +// Summons out of a block pattern, blasts everything around it on arrival, then +// hunts the nearest living target. Below half health it armours up: projectiles +// stop landing entirely and it starts regenerating and calling minions. +class WitherFight { +public: + explicit WitherFight(uint32_t seed, const WitherFightConfig& config = {}); + + WitherFight(const WitherFight&) = delete; + WitherFight& operator=(const WitherFight&) = delete; + WitherFight(WitherFight&&) = delete; + WitherFight& operator=(WitherFight&&) = delete; + + void begin(const Vec3& spawnPosition); + // Consumes the pattern in the world and starts the fight at its centre. + bool summonFromWorld(World& world, const Vec3i& baseCell); + + BossTickReport tick(const std::vector& incoming, + std::vector& targets); + + const BossState& state() const { return boss; } + const WitherFightConfig& settings() const { return cfg; } + const std::vector& minions() const { return summoned; } + bool armored() const { return boss.phase == BossPhase::Armored; } + int aliveMinionCount() const; + + const BehaviorTree& brain() const { return tree; } + const BehaviorContext& context() const { return ctx; } + +private: + void buildTree(); + float applyIncoming(const std::vector& incoming); + int nearestAliveTarget() const; + + WitherFightConfig cfg; + BossState boss; + std::vector summoned; + BehaviorTree tree; + BehaviorContext ctx; + std::vector* activeTargets = nullptr; + float damageThisTick = 0.0f; + float healedThisTick = 0.0f; + int minionsThisTick = 0; +}; + +} // namespace vf