Fixing some stuff related to attacks

This commit is contained in:
Vladislav Khorev 2026-07-12 16:55:23 +03:00
parent 9d03b52c2a
commit 92929118d5
7 changed files with 32 additions and 7 deletions

View File

@ -330,6 +330,7 @@ void Character::update(int64_t deltaMs, const CharacterResolver& resolver) {
state.currentState != AnimationState::DEATH_IDLE) { state.currentState != AnimationState::DEATH_IDLE) {
state.currentState = AnimationState::ACTION_TO_DEATH; state.currentState = AnimationState::ACTION_TO_DEATH;
state.resetAnim = true; state.resetAnim = true;
state.attackTargetIndex = CharacterState::kNoTarget;
} }
} else { } else {
if (state.battle_state == 1) { if (state.battle_state == 1) {
@ -433,7 +434,7 @@ void Character::update(int64_t deltaMs, const CharacterResolver& resolver) {
(state.currentState == AnimationState::ACTION_ATTACK || (state.currentState == AnimationState::ACTION_ATTACK ||
state.currentState == AnimationState::ACTION_ATTACK_2)) { state.currentState == AnimationState::ACTION_ATTACK_2)) {
if (attackTarget != nullptr) { if (attackTarget != nullptr) {
attackTarget->applyDamage(10.f, attackDirectionToTarget()); attackTarget->applyDamage(10.f, attackDirectionToTarget(), index);
if (audioPlayer) { if (audioPlayer) {
if (state.isPlayer) if (state.isPlayer)
@ -1214,12 +1215,12 @@ void Character::setupHitSparks(std::shared_ptr<Texture> sparkTexture) {
hitSparkEmitter.markConfigured(); hitSparkEmitter.markConfigured();
} }
void Character::applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection) { void Character::applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection, int attackerIndex) {
std::cout << "Character::applyDamage" << std::endl; std::cout << "Character::applyDamage" << std::endl;
state.hp -= damageAmount; state.hp -= damageAmount;
if (state.hp < 0) state.hp = 0; if (state.hp < 0) state.hp = 0;
if (state.onHpChanged) state.onHpChanged(state.hp, state.initialHp); if (state.onHpChanged) state.onHpChanged(state.hp, state.initialHp, attackerIndex);
if (!hitSparkEmitter.isConfigured()) return; if (!hitSparkEmitter.isConfigured()) return;

View File

@ -31,6 +31,8 @@ public:
// All serializable game state (position, hp, flags, pathfinding, etc.) // All serializable game state (position, hp, flags, pathfinding, etc.)
CharacterState state; CharacterState state;
int index = CharacterState::kNoTarget;
// ---- Asset loading ---- // ---- Asset loading ----
void loadAnimation(AnimationState animState, const std::string& filename, const std::string& zipFile = ""); void loadAnimation(AnimationState animState, const std::string& filename, const std::string& zipFile = "");
void loadBinaryAnimation(AnimationState animState, const std::string& filename, const std::string& zipFile = ""); void loadBinaryAnimation(AnimationState animState, const std::string& filename, const std::string& zipFile = "");
@ -64,7 +66,7 @@ public:
void resetPlayerAfterDeath(); void resetPlayerAfterDeath();
// ---- Damage ---- // ---- Damage ----
void applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection = Eigen::Vector3f::Zero()); void applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection = Eigen::Vector3f::Zero(), int attackerIndex = CharacterState::kNoTarget);
// ---- Spark setup ---- // ---- Spark setup ----
void setupHitSparks(std::shared_ptr<Texture> sparkTexture); void setupHitSparks(std::shared_ptr<Texture> sparkTexture);

View File

@ -27,7 +27,7 @@ void CharacterState::stopInPlace() {
void CharacterState::setHp(float newHp) { void CharacterState::setHp(float newHp) {
hp = newHp; hp = newHp;
if (hp < 0.f) hp = 0.f; if (hp < 0.f) hp = 0.f;
if (onHpChanged) onHpChanged(hp, initialHp); if (onHpChanged) onHpChanged(hp, initialHp, CharacterState::kNoTarget);
} }
void CharacterState::save(nlohmann::json& out) const void CharacterState::save(nlohmann::json& out) const

View File

@ -114,7 +114,7 @@ public:
// --- Non-serializable callbacks (re-wired after each load) --- // --- Non-serializable callbacks (re-wired after each load) ---
std::function<void()> onArrivedCallback; std::function<void()> onArrivedCallback;
std::function<void()> onDeathAnimComplete; std::function<void()> onDeathAnimComplete;
std::function<void(float, float)> onHpChanged; std::function<void(float, float, int)> onHpChanged;
// Global Lua function name backing onArrivedCallback, if it was sourced from a // Global Lua function name backing onArrivedCallback, if it was sourced from a
// script (see ScriptEngine::rehydrateNamedCallback). Serialized so the callback // script (see ScriptEngine::rehydrateNamedCallback). Serialized so the callback

View File

@ -744,8 +744,25 @@ namespace ZL
// Wire HP-change callbacks so all player instances update the health bar HUD. // Wire HP-change callbacks so all player instances update the health bar HUD.
for (auto& [name, loc] : gameState.locations) { for (auto& [name, loc] : gameState.locations) {
if (loc->player) { if (loc->player) {
loc->player->state.onHpChanged = [this](float hp, float maxHp) { loc->player->state.onHpChanged = [this, loc](float hp, float maxHp, int attackerIndex) {
menuManager.updateHealthBar(hp, maxHp); menuManager.updateHealthBar(hp, maxHp);
if (attackerIndex != CharacterState::kNoTarget) {
//Someone attacked, cancel dialogue and agro the player
if (loc->dialogueSystem.isDialogueActive()) {
loc->dialogueSystem.stopDialogue();
}
if ((loc->player->state.attackTargetIndex == CharacterState::kNoTarget ||
((loc->player->state.attackTargetIndex >= 0) &&
(loc->npcs[loc->player->state.attackTargetIndex]->state.hp <= 0)))
&&
loc->player->isMoving() == false) {
loc->player->state.attackTargetIndex = attackerIndex;
}
}
}; };
} }
} }

View File

@ -52,6 +52,7 @@ public:
void load(const nlohmann::json& in) override { loadDialogueState(in); } void load(const nlohmann::json& in) override { loadDialogueState(in); }
bool isActive() const { return dialogueRuntime.isActive() || cutsceneRuntime.isActive(); } bool isActive() const { return dialogueRuntime.isActive() || cutsceneRuntime.isActive(); }
bool isDialogueActive() const { return dialogueRuntime.isActive(); }
bool blocksGameplayInput() const { return isActive(); } bool blocksGameplayInput() const { return isActive(); }
void setFlag(const std::string& name, int value) { dialogueRuntime.setFlag(name, value); } void setFlag(const std::string& name, int value) { dialogueRuntime.setFlag(name, value); }

View File

@ -363,6 +363,8 @@ namespace ZL {
{ {
std::vector<std::unique_ptr<Character>> npcs; std::vector<std::unique_ptr<Character>> npcs;
int index = 0;
for (const auto& npcData : loadNpcsFromJson(jsonPath, zipPath)) { for (const auto& npcData : loadNpcsFromJson(jsonPath, zipPath)) {
SDL_PumpEvents(); SDL_PumpEvents();
std::cout << "Loading NPC: " << npcData.name << std::endl; std::cout << "Loading NPC: " << npcData.name << std::endl;
@ -371,7 +373,9 @@ namespace ZL {
if (npc) { if (npc) {
std::cout << "Successfully loaded NPC: " << npcData.name << " at (" std::cout << "Successfully loaded NPC: " << npcData.name << " at ("
<< npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl; << npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl;
npc->index = index;
npcs.push_back(std::move(npc)); npcs.push_back(std::move(npc));
index = index + 1;
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "GameObjectLoader: Failed to load NPC '" << npcData.name << "': " << e.what() << std::endl; std::cerr << "GameObjectLoader: Failed to load NPC '" << npcData.name << "': " << e.what() << std::endl;