Working on health bar
This commit is contained in:
parent
8552725dfc
commit
22b0e1992f
@ -89,15 +89,21 @@
|
||||
"texture": "resources/w/ui/img/HealthBarBack001.png"
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "healthBarBorder",
|
||||
"type": "Slider",
|
||||
"name": "healthBarFill",
|
||||
"width": 151.2,
|
||||
"height": 22.8,
|
||||
"y": 49.8,
|
||||
"x": 100.6,
|
||||
"horizontal_gravity": "left",
|
||||
"vertical_align": "top",
|
||||
"texture": "resources/w/ui/img/HealthBarFill001.png"
|
||||
"vertical_gravity": "top",
|
||||
"orientation": "horizontal",
|
||||
"value": 1.0,
|
||||
"fillMode": true,
|
||||
"interactive": false,
|
||||
"textures": {
|
||||
"track": "resources/w/ui/img/HealthBarFill001.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
|
||||
@ -89,15 +89,21 @@
|
||||
"texture": "resources/w/ui/img/HealthBarBack001.png"
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "healthBarBorder",
|
||||
"type": "Slider",
|
||||
"name": "healthBarFill",
|
||||
"width": 151.2,
|
||||
"height": 22.8,
|
||||
"y": 49.8,
|
||||
"x": 100.6,
|
||||
"horizontal_gravity": "left",
|
||||
"vertical_align": "top",
|
||||
"texture": "resources/w/ui/img/HealthBarFill001.png"
|
||||
"vertical_gravity": "top",
|
||||
"orientation": "horizontal",
|
||||
"value": 1.0,
|
||||
"fillMode": true,
|
||||
"interactive": false,
|
||||
"textures": {
|
||||
"track": "resources/w/ui/img/HealthBarFill001.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
|
||||
@ -99,32 +99,6 @@
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_align": "top",
|
||||
"texture": "resources/w/ui/img/Location_uni_int.png"
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "hint_darklands001",
|
||||
"width": 470,
|
||||
"height": 156,
|
||||
"x": 630,
|
||||
"y": 200,
|
||||
"texture": "resources/w/ui/img/Hint_darklands001.png",
|
||||
"pulse": {
|
||||
"minScale": 0.92,
|
||||
"maxScale": 1.08,
|
||||
"periodMs": 1500
|
||||
},
|
||||
"fadeIn": {
|
||||
"durationMs": 600
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "hint_darklands001_arrow",
|
||||
"width": 24,
|
||||
"height": 118,
|
||||
"x": 860,
|
||||
"y": 110,
|
||||
"texture": "resources/w/ui/img/hint_arrow_up.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -115,15 +115,21 @@
|
||||
"texture": "resources/w/ui/img/HealthBarBack001.png"
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"type": "Slider",
|
||||
"name": "healthBarFill",
|
||||
"width": 151.2,
|
||||
"height": 22.8,
|
||||
"y": 49.8,
|
||||
"x": 100.6,
|
||||
"horizontal_gravity": "left",
|
||||
"vertical_align": "top",
|
||||
"texture": "resources/w/ui/img/HealthBarFill001.png"
|
||||
"vertical_gravity": "top",
|
||||
"orientation": "horizontal",
|
||||
"value": 1.0,
|
||||
"fillMode": true,
|
||||
"interactive": false,
|
||||
"textures": {
|
||||
"track": "resources/w/ui/img/HealthBarFill001.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
|
||||
@ -98,15 +98,21 @@
|
||||
"texture": "resources/w/ui/img/HealthBarBack001.png"
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"type": "Slider",
|
||||
"name": "healthBarFill",
|
||||
"width": 151.2,
|
||||
"height": 22.8,
|
||||
"y": 49.8,
|
||||
"x": 100.6,
|
||||
"horizontal_gravity": "left",
|
||||
"vertical_align": "top",
|
||||
"texture": "resources/w/ui/img/HealthBarFill001.png"
|
||||
"vertical_gravity": "top",
|
||||
"orientation": "horizontal",
|
||||
"value": 1.0,
|
||||
"fillMode": true,
|
||||
"interactive": false,
|
||||
"textures": {
|
||||
"track": "resources/w/ui/img/HealthBarFill001.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
|
||||
@ -922,6 +922,8 @@ void Character::applyDamage(float damageAmount, const Eigen::Vector3f& attackDir
|
||||
hp = hp - damageAmount;
|
||||
if (hp < 0) hp = 0;
|
||||
|
||||
if (onHpChanged) onHpChanged(hp, initialHp);
|
||||
|
||||
if (!hitSparkEmitter.isConfigured()) return;
|
||||
|
||||
Eigen::Vector3f emitPos = position + Eigen::Vector3f(0.f, 1.f, 0.f);
|
||||
@ -973,7 +975,7 @@ void Character::drawHealthBar(Renderer& renderer,
|
||||
const Eigen::Matrix4f& cameraViewMatrix,
|
||||
const Eigen::Matrix4f& projectionMatrix)
|
||||
{
|
||||
if (!isPlayer && !canAttack) return;
|
||||
if (isPlayer || !canAttack) return;
|
||||
if (!enabled) return;
|
||||
if (hp <= 0.f) return;
|
||||
if (initialHp <= 0.f) return;
|
||||
|
||||
@ -160,6 +160,9 @@ public:
|
||||
// Called once when the death animation finishes and the character enters DEATH_IDLE.
|
||||
std::function<void()> onDeathAnimComplete;
|
||||
|
||||
// Called whenever hp changes (e.g. on damage). Arguments: current hp, max hp.
|
||||
std::function<void(float hp, float maxHp)> onHpChanged;
|
||||
|
||||
private:
|
||||
|
||||
std::map<AnimationState, BoneAnimationDataNew> animations;
|
||||
|
||||
10
src/Game.cpp
10
src/Game.cpp
@ -131,7 +131,6 @@ namespace ZL
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Game::setupPart2()
|
||||
{
|
||||
|
||||
@ -461,6 +460,15 @@ namespace ZL
|
||||
std::cerr << "Failed to load UI: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
// Wire HP-change callbacks so all player instances update the health bar HUD.
|
||||
for (auto& [name, loc] : locations) {
|
||||
if (loc->player) {
|
||||
loc->player->onHpChanged = [this](float hp, float maxHp) {
|
||||
menuManager.updateHealthBar(hp, maxHp);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
loadingCompleted = true;
|
||||
|
||||
if (audioPlayer->init()) {
|
||||
|
||||
@ -110,6 +110,7 @@ namespace ZL {
|
||||
void MenuManager::enterGameplay() {
|
||||
state = GameState::Gameplay;
|
||||
uiManager.replaceRoot(hudRoot);
|
||||
applyCurrentHealthBar();
|
||||
/*
|
||||
uiManager.setTextButtonCallback("inventory_button", [this](const std::string&) {
|
||||
openInventory();
|
||||
@ -366,12 +367,14 @@ namespace ZL {
|
||||
|
||||
if (locationName == "uni_exterior") {
|
||||
uiManager.replaceRoot(currentIsDarklands_ ? hudUniExtDarkRoot : hudUniExtRoot);
|
||||
applyCurrentHealthBar();
|
||||
setupGameplayHudCallbacks();
|
||||
} else if (locationName == "uni_interior") {
|
||||
applyUniIntHud();
|
||||
} else {
|
||||
// Returning to dorm: reuse step5ab, suppress already-completed hints
|
||||
uiManager.replaceRoot(hudStep5abRoot);
|
||||
applyCurrentHealthBar();
|
||||
setupStep5Callbacks();
|
||||
}
|
||||
}
|
||||
@ -402,6 +405,7 @@ namespace ZL {
|
||||
|
||||
if (state == GameState::Gameplay && nextRoot) {
|
||||
uiManager.replaceRoot(nextRoot);
|
||||
applyCurrentHealthBar();
|
||||
|
||||
uiManager.setButtonCallback("inventoryButton", [this](const std::string&) {
|
||||
openInventory();
|
||||
@ -450,6 +454,7 @@ namespace ZL {
|
||||
|
||||
if (nextRoot) {
|
||||
uiManager.replaceRoot(nextRoot);
|
||||
applyCurrentHealthBar();
|
||||
// Register item-screen buttons and re-apply any already-completed hint visibility.
|
||||
setupStep5Callbacks();
|
||||
}
|
||||
@ -628,6 +633,7 @@ namespace ZL {
|
||||
} else if (currentLocationName_ == "uni_exterior") {
|
||||
if (state == GameState::Gameplay) {
|
||||
uiManager.replaceRoot(enabled ? hudUniExtDarkRoot : hudUniExtRoot);
|
||||
applyCurrentHealthBar();
|
||||
setupGameplayHudCallbacks();
|
||||
}
|
||||
} else {
|
||||
@ -654,6 +660,7 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
uiManager.replaceRoot(root);
|
||||
applyCurrentHealthBar();
|
||||
setupGameplayHudCallbacks();
|
||||
}
|
||||
|
||||
@ -684,4 +691,20 @@ namespace ZL {
|
||||
applyUniIntHud();
|
||||
}
|
||||
|
||||
void MenuManager::updateHealthBar(float hp, float maxHp) {
|
||||
currentPlayerHp_ = hp;
|
||||
currentPlayerMaxHp_ = maxHp;
|
||||
applyCurrentHealthBar();
|
||||
}
|
||||
|
||||
void MenuManager::applyCurrentHealthBar() {
|
||||
if (currentPlayerMaxHp_ <= 0.f) return;
|
||||
const float fraction = std::clamp(currentPlayerHp_ / currentPlayerMaxHp_, 0.f, 1.f);
|
||||
uiManager.setSliderValue("healthBarFill", fraction);
|
||||
std::string hpText = std::to_string(static_cast<int>(currentPlayerHp_)) + "/" +
|
||||
std::to_string(static_cast<int>(currentPlayerMaxHp_));
|
||||
if (hpText.size() < 7) hpText.insert(0, 7 - hpText.size(), ' ');
|
||||
uiManager.setText("healthValue", hpText);
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
|
||||
@ -67,6 +67,9 @@ namespace ZL {
|
||||
void advanceUniIntDarklandsHud();
|
||||
void onPlayerStartedWalking();
|
||||
|
||||
// Called by Game when the player's HP changes. Stores values and updates HUD.
|
||||
void updateHealthBar(float hp, float maxHp);
|
||||
|
||||
TutorialStep tutorialStep = TutorialStep::Step0;
|
||||
|
||||
protected:
|
||||
@ -130,6 +133,10 @@ namespace ZL {
|
||||
std::shared_ptr<Texture> texItemSelected_;
|
||||
std::shared_ptr<Texture> texItemTransparent_;
|
||||
|
||||
float currentPlayerHp_ = 200.f;
|
||||
float currentPlayerMaxHp_ = 200.f;
|
||||
void applyCurrentHealthBar();
|
||||
|
||||
int selectedQuestIndex = -1;
|
||||
std::vector<std::string> visibleQuestIds;
|
||||
|
||||
|
||||
@ -284,9 +284,22 @@ namespace ZL {
|
||||
|
||||
float x0 = rect.x;
|
||||
float y0 = rect.y;
|
||||
float x1 = rect.x + rect.w;
|
||||
float y1 = rect.y + rect.h;
|
||||
|
||||
float x1, u1;
|
||||
if (fillMode) {
|
||||
const float clamped = std::clamp(value, 0.0f, 1.0f);
|
||||
if (clamped <= 0.0f) {
|
||||
trackMesh.RefreshVBO();
|
||||
return;
|
||||
}
|
||||
x1 = rect.x + rect.w * clamped;
|
||||
u1 = clamped;
|
||||
} else {
|
||||
x1 = rect.x + rect.w;
|
||||
u1 = 1.0f;
|
||||
}
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
@ -294,16 +307,16 @@ namespace ZL {
|
||||
trackMesh.data.TexCoordData.push_back({ 0, 1 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
trackMesh.data.TexCoordData.push_back({ u1, 1 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
trackMesh.data.TexCoordData.push_back({ u1, 1 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 1, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ u1, 0 });
|
||||
|
||||
trackMesh.RefreshVBO();
|
||||
}
|
||||
@ -566,6 +579,8 @@ namespace ZL {
|
||||
std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
|
||||
s->vertical = (orient != "horizontal");
|
||||
}
|
||||
if (j.contains("fillMode")) s->fillMode = j["fillMode"].get<bool>();
|
||||
if (j.contains("interactive")) s->interactive = j["interactive"].get<bool>();
|
||||
|
||||
node->slider = s;
|
||||
}
|
||||
@ -1162,6 +1177,7 @@ namespace ZL {
|
||||
value = std::clamp(value, 0.0f, 1.0f);
|
||||
if (fabs(s->value - value) < 1e-6f) return true;
|
||||
s->value = value;
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
return true;
|
||||
@ -1634,18 +1650,21 @@ namespace ZL {
|
||||
auto it = pressedSliders.find(fingerId);
|
||||
if (it != pressedSliders.end()) {
|
||||
auto s = it->second;
|
||||
float t;
|
||||
if (s->vertical) {
|
||||
t = (y - s->rect.y) / s->rect.h;
|
||||
if (s->interactive) {
|
||||
float t;
|
||||
if (s->vertical) {
|
||||
t = (y - s->rect.y) / s->rect.h;
|
||||
}
|
||||
else {
|
||||
t = (x - s->rect.x) / s->rect.w;
|
||||
}
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
s->value = t;
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
}
|
||||
else {
|
||||
t = (x - s->rect.x) / s->rect.w;
|
||||
}
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
s->value = t;
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1676,6 +1695,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
for (auto& s : sliders) {
|
||||
if (!s->interactive) continue;
|
||||
if (s->rect.contains((float)x, (float)y)) {
|
||||
pressedSliders[fingerId] = s;
|
||||
float t;
|
||||
@ -1688,6 +1708,7 @@ namespace ZL {
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
s->value = t;
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
break;
|
||||
|
||||
@ -129,6 +129,8 @@ namespace ZL {
|
||||
|
||||
float value = 0.0f;
|
||||
bool vertical = true;
|
||||
bool fillMode = false; // track width = value * rect.w (display-only bar)
|
||||
bool interactive = true; // false = ignore touch events
|
||||
|
||||
std::function<void(const std::string&, float)> onValueChanged;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user