feat(vf): extend Block with BossBar and BossState
This commit is contained in:
1 parent
b14c722431
commit
76f2be284b
2 files changed
+169
-1
No files matched your search
@@ -490,6 +490,13 @@ inline int blockLightEmission(Block b) {
|
|||||||
case Block::FurnaceLit: return 13;
|
case Block::FurnaceLit: return 13;
|
||||||
case Block::RedstoneTorch: return 7;
|
case Block::RedstoneTorch: return 7;
|
||||||
case Block::RedstoneOre: return 0; // lit only on touch, handled by gameplay
|
case Block::RedstoneOre: return 0; // lit only on touch, handled by gameplay
|
||||||
|
case Block::EndPortal: return 15;
|
||||||
|
case Block::SeaLantern: return 15;
|
||||||
|
case Block::Lantern: return 15;
|
||||||
|
case Block::NetherPortal: return 11;
|
||||||
|
case Block::CryingObsidian: return 10;
|
||||||
|
case Block::EnchantingTable: return 7;
|
||||||
|
case Block::Magma: return 3;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,7 +506,8 @@ inline bool occludesLight(Block b) { return isOpaque(b); }
|
|||||||
|
|
||||||
// Meshed in the transparent pass (depth write off, back-to-front): fluids + ice.
|
// Meshed in the transparent pass (depth write off, back-to-front): fluids + ice.
|
||||||
inline bool isTransparent(Block b) {
|
inline bool isTransparent(Block b) {
|
||||||
return b == Block::Water || b == Block::Ice;
|
return b == Block::Water || b == Block::Ice || b == Block::Glass ||
|
||||||
|
b == Block::NetherPortal || b == Block::EndPortal;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace vf
|
} // namespace vf
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Behavior.h"
|
||||||
|
#include "Block.h"
|
||||||
|
#include "EndGen.h"
|
||||||
|
#include "Math.h"
|
||||||
|
#include "World.h"
|
||||||
|
|
||||||
|
namespace vf {
|
||||||
|
|
||||||
|
// Two staged boss fights driven by the behaviour trees from Behavior.h. Both
|
||||||
|
// are pure tick machines: feed the same seed and the same sequence of player
|
||||||
|
// actions and the fight replays move for move.
|
||||||
|
enum class BossKind : uint8_t {
|
||||||
|
EnderDragon = 0,
|
||||||
|
Wither = 1,
|
||||||
|
Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* bossKindName(BossKind kind);
|
||||||
|
|
||||||
|
enum class BossPhase : uint8_t {
|
||||||
|
Dormant = 0,
|
||||||
|
Circling,
|
||||||
|
Charging,
|
||||||
|
Breathing,
|
||||||
|
Perching,
|
||||||
|
Summoning,
|
||||||
|
Explosive,
|
||||||
|
Ranged,
|
||||||
|
Armored,
|
||||||
|
Dying,
|
||||||
|
Defeated,
|
||||||
|
Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* bossPhaseName(BossPhase phase);
|
||||||
|
|
||||||
|
enum class BossBarColor : uint8_t { Pink = 0, Blue, Red, Green, Yellow, Purple, White, Count };
|
||||||
|
enum class BossBarStyle : uint8_t { Solid = 0, Notched6, Notched10, Notched12, Notched20, Count };
|
||||||
|
|
||||||
|
struct BossBar {
|
||||||
|
std::string title;
|
||||||
|
float progress = 1.0f;
|
||||||
|
BossBarColor color = BossBarColor::Purple;
|
||||||
|
BossBarStyle style = BossBarStyle::Notched10;
|
||||||
|
bool visible = false;
|
||||||
|
|
||||||
|
int notchCount() const;
|
||||||
|
// Whole segments currently lit; a partially drained segment reads as empty.
|
||||||
|
int filledNotches() const;
|
||||||
|
int percent() const;
|
||||||
|
void setFromHealth(float health, float maxHealth);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BossState {
|
||||||
|
BossKind kind = BossKind::EnderDragon;
|
||||||
|
BossPhase phase = BossPhase::Dormant;
|
||||||
|
float health = 200.0f;
|
||||||
|
float maxHealth = 200.0f;
|
||||||
|
Vec3 position;
|
||||||
|
Vec3 heading{1.0f, 0.0f, 0.0f};
|
||||||
|
float orbitAngle = 0.0f;
|
||||||
|
int targetIndex = -1;
|
||||||
|
long long tick = 0;
|
||||||
|
int phaseTicks = 0;
|
||||||
|
int phaseChanges = 0;
|
||||||
|
int attacksLaunched = 0;
|
||||||
|
int minionsSummoned = 0;
|
||||||
|
float healingReceived = 0.0f;
|
||||||
|
float damageTaken = 0.0f;
|
||||||
|
bool alive = true;
|
||||||
|
uint32_t rng = 0x51ed270bu;
|
||||||
|
BossBar bar;
|
||||||
|
|
||||||
|
void enterPhase(BossPhase next);
|
||||||
|
float healthFraction() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// One end crystal on top of an obsidian pillar from EndGen.h. The taller
|
||||||
|
// pillars carry a cage that has to come down before the crystal can be shot.
|
||||||
|
struct CrystalNode {
|
||||||
|
Vec3i pillarBase;
|
||||||
|
Vec3i position;
|
||||||
|
int pillarHeight = 0;
|
||||||
|
bool caged = false;
|
||||||
|
bool cageIntact = false;
|
||||||
|
bool alive = true;
|
||||||
|
float health = 5.0f;
|
||||||
|
long long destroyedTick = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<CrystalNode> crystalsFromPillars(const std::vector<ObsidianPillar>& pillars);
|
||||||
|
|
||||||
|
struct BossTickReport {
|
||||||
|
BossPhase phase = BossPhase::Dormant;
|
||||||
|
BossPhase previousPhase = BossPhase::Dormant;
|
||||||
|
bool phaseChanged = false;
|
||||||
|
float bossHealth = 0.0f;
|
||||||
|
float healedThisTick = 0.0f;
|
||||||
|
float damageToTargets = 0.0f;
|
||||||
|
int crystalsDestroyed = 0;
|
||||||
|
int minionsSpawned = 0;
|
||||||
|
bool defeated = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DragonFightConfig {
|
||||||
|
float maxHealth = 200.0f;
|
||||||
|
float orbitRadius = 44.0f;
|
||||||
|
float orbitHeight = 26.0f;
|
||||||
|
float orbitStep = 0.06f;
|
||||||
|
float chargeTrigger = 48.0f;
|
||||||
|
float chargeSpeed = 1.4f;
|
||||||
|
float chargeContactRange = 3.0f;
|
||||||
|
float chargeDamage = 10.0f;
|
||||||
|
int chargeCooldownTicks = 60;
|
||||||
|
int chargeWindowTicks = 40;
|
||||||
|
float breathRange = 14.0f;
|
||||||
|
float breathDamage = 3.0f;
|
||||||
|
int breathCooldownTicks = 40;
|
||||||
|
int swipeCooldownTicks = 25;
|
||||||
|
float swipeDamage = 6.0f;
|
||||||
|
float swipeRange = 8.0f;
|
||||||
|
float crystalHealPerTick = 0.4f;
|
||||||
|
float crystalHealRange = 128.0f;
|
||||||
|
float crystalBlastRange = 12.0f;
|
||||||
|
float crystalBlastDamage = 6.0f;
|
||||||
|
float perchHeight = 3.0f;
|
||||||
|
float perchDescentStep = 0.9f;
|
||||||
|
float meleeReach = 7.0f;
|
||||||
|
float flyingDamageFactor = 0.25f;
|
||||||
|
int deathTicks = 40;
|
||||||
|
};
|
||||||
|
|
||||||
|
// What the player did on one tick. `shootCrystal` indexes crystals(); a caged
|
||||||
|
// crystal needs `breakCage` on an earlier tick before a shot lands.
|
||||||
|
struct DragonPlayerInput {
|
||||||
|
Vec3 position;
|
||||||
|
bool present = true;
|
||||||
|
int shootCrystal = -1;
|
||||||
|
bool breakCage = false;
|
||||||
|
float meleeDamage = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Crystals heal the dragon while they stand, so the fight cannot be won by
|
||||||
|
// damage alone. Killing the last one drops it onto the portal, where melee
|
||||||
|
// lands at full strength; the kill opens the exit portal and leaves the egg.
|
||||||
|
class DragonFight {
|
||||||
|
public:
|
||||||
|
DragonFight(const EndGenerator& end, uint32_t seed, const DragonFightConfig& config = {});
|
||||||
|
|
||||||
|
DragonFight(const DragonFight&) = delete;
|
||||||
|
DragonFight& operator=(const DragonFight&) = delete;
|
||||||
|
DragonFight(DragonFight&&) = delete;
|
||||||
|
DragonFight& operator=(DragonFight&&) = delete;
|
||||||
|
|
||||||
Reference in new issue
Block a user