Major refactoring - internal server state machine implemented
This commit is contained in:
1 parent
71c654e5b6
commit
66e93d46f4
48 files changed
+3948
-3696
No files matched your search
+116
-450
@@ -9,10 +9,11 @@
|
||||
#include "render/TextureManager.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "TextModel.h"
|
||||
#include "game_server/IGameServer.h"
|
||||
#include "navigation/PathFinder.h"
|
||||
#include "Location.h"
|
||||
|
||||
namespace FRG {
|
||||
|
||||
const float ATTACK_COOLDOWN_TIME = 1.6f;
|
||||
extern float x;
|
||||
extern float y;
|
||||
|
||||
@@ -26,6 +27,8 @@ void Character::loadAnimation(AnimationState animState, const std::string& filen
|
||||
} else {
|
||||
data.totalFrames = 1;
|
||||
}
|
||||
gameServer_.submit(SetCharacterAnimationMetadataCommand{
|
||||
locationId_, entityId_, animState, data.model.startingFrame, data.totalFrames});
|
||||
}
|
||||
|
||||
void Character::loadBinaryAnimation(AnimationState animState, const std::string& filename, const std::string& zipFile) {
|
||||
@@ -36,11 +39,14 @@ void Character::loadBinaryAnimation(AnimationState animState, const std::string&
|
||||
} else {
|
||||
data.totalFrames = 1;
|
||||
}
|
||||
gameServer_.submit(SetCharacterAnimationMetadataCommand{
|
||||
locationId_, entityId_, animState, data.model.startingFrame, data.totalFrames});
|
||||
}
|
||||
|
||||
std::unique_ptr<Character> Character::createFromState(CharacterState st, Renderer& renderer, const char* zipFile) {
|
||||
auto c = std::make_unique<Character>();
|
||||
c->state = std::move(st);
|
||||
std::unique_ptr<Character> Character::createFromState(const CharacterState& st, IGameServer& server,
|
||||
const std::string& locationId, const std::string& entityId,
|
||||
Renderer& renderer, const char* zipFile) {
|
||||
auto c = std::make_unique<Character>(st, server, locationId, entityId);
|
||||
|
||||
const std::string zip = zipFile ? zipFile : "";
|
||||
|
||||
@@ -56,7 +62,7 @@ std::unique_ptr<Character> Character::createFromState(CharacterState st, Rendere
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& [animState, path] : c->state.creationInfo.animationPaths)
|
||||
for (const auto& [animState, path] : c->characterState.creationInfo.animationPaths)
|
||||
loadAnim(animState, path);
|
||||
|
||||
auto loadTex = [&](const std::string& path) -> std::shared_ptr<Texture> {
|
||||
@@ -70,27 +76,27 @@ std::unique_ptr<Character> Character::createFromState(CharacterState st, Rendere
|
||||
}
|
||||
};
|
||||
|
||||
if (!c->state.creationInfo.singleTexturePath.empty()) {
|
||||
auto tex = loadTex(c->state.creationInfo.singleTexturePath);
|
||||
if (!c->characterState.creationInfo.singleTexturePath.empty()) {
|
||||
auto tex = loadTex(c->characterState.creationInfo.singleTexturePath);
|
||||
if (tex) c->setTexture(tex);
|
||||
}
|
||||
for (const auto& [meshName, texPath] : c->state.creationInfo.meshTexturePaths) {
|
||||
for (const auto& [meshName, texPath] : c->characterState.creationInfo.meshTexturePaths) {
|
||||
auto tex = loadTex(texPath);
|
||||
if (tex) c->setTexture(meshName, tex);
|
||||
}
|
||||
|
||||
if (!c->state.creationInfo.weaponMeshPath.empty()) {
|
||||
const std::string& wp = c->state.creationInfo.weaponMeshPath;
|
||||
if (!c->characterState.creationInfo.weaponMeshPath.empty()) {
|
||||
const std::string& wp = c->characterState.creationInfo.weaponMeshPath;
|
||||
try {
|
||||
if (wp.size() >= 4 && wp.substr(wp.size() - 4) == ".bin")
|
||||
c->weaponMesh.data = LoadModelFromBinFile(wp, zip);
|
||||
else
|
||||
c->weaponMesh.data = LoadFromTextFile02(wp, zip);
|
||||
|
||||
if (c->state.creationInfo.weaponMeshMove != Eigen::Vector3f::Zero())
|
||||
c->weaponMesh.data.Move(c->state.creationInfo.weaponMeshMove);
|
||||
if (c->state.creationInfo.weaponMeshScale != 1.0f)
|
||||
c->weaponMesh.data.Scale(c->state.creationInfo.weaponMeshScale);
|
||||
if (c->characterState.creationInfo.weaponMeshMove != Eigen::Vector3f::Zero())
|
||||
c->weaponMesh.data.Move(c->characterState.creationInfo.weaponMeshMove);
|
||||
if (c->characterState.creationInfo.weaponMeshScale != 1.0f)
|
||||
c->weaponMesh.data.Scale(c->characterState.creationInfo.weaponMeshScale);
|
||||
|
||||
c->weaponMesh.RefreshVBO();
|
||||
} catch (const std::exception& e) {
|
||||
@@ -98,100 +104,14 @@ std::unique_ptr<Character> Character::createFromState(CharacterState st, Rendere
|
||||
}
|
||||
}
|
||||
|
||||
c->weaponTexture = loadTex(c->state.creationInfo.weaponTexturePath);
|
||||
c->weaponAttachBoneName = c->state.creationInfo.weaponAttachBoneName;
|
||||
c->weaponInitialPosition = c->state.creationInfo.weaponInitialPosition;
|
||||
c->weaponInitialRotation = c->state.creationInfo.weaponInitialRotation.toRotationMatrix();
|
||||
c->weaponTexture = loadTex(c->characterState.creationInfo.weaponTexturePath);
|
||||
c->weaponAttachBoneName = c->characterState.creationInfo.weaponAttachBoneName;
|
||||
c->weaponInitialPosition = c->characterState.creationInfo.weaponInitialPosition;
|
||||
c->weaponInitialRotation = c->characterState.creationInfo.weaponInitialRotation.toRotationMatrix();
|
||||
|
||||
c->setTarget(c->state.position);
|
||||
return c;
|
||||
}
|
||||
|
||||
void Character::setTarget(const Eigen::Vector3f& target, std::function<void()> onArrived, std::string callbackName) {
|
||||
Eigen::Vector3f normalizedTarget(target.x(), 0.f, target.z());
|
||||
|
||||
const bool sameRequestedTarget =
|
||||
(normalizedTarget - state.requestedWalkTarget).norm() <= TARGET_REPLAN_THRESHOLD;
|
||||
const bool alreadyMovingToTarget =
|
||||
!state.pathWaypoints.empty() || (state.walkTarget - normalizedTarget).norm() <= TARGET_REPLAN_THRESHOLD;
|
||||
const bool stoppedAfterFailedPath =
|
||||
state.pathWaypoints.empty() && (state.walkTarget - state.position).norm() <= TARGET_REPLAN_THRESHOLD;
|
||||
|
||||
if (!onArrived && sameRequestedTarget && (alreadyMovingToTarget || stoppedAfterFailedPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.requestedWalkTarget = normalizedTarget;
|
||||
state.onArrivedCallback = std::move(onArrived);
|
||||
state.onArrivedCallbackName = std::move(callbackName);
|
||||
|
||||
if (pathPlanner) {
|
||||
state.pathWaypoints = pathPlanner(state.position, normalizedTarget);
|
||||
state.currentWaypointIndex = 0;
|
||||
|
||||
if (!state.pathWaypoints.empty()) {
|
||||
for (Eigen::Vector3f& waypoint : state.pathWaypoints) {
|
||||
waypoint.y() = 0.f;
|
||||
}
|
||||
state.walkTarget = state.pathWaypoints.back();
|
||||
return;
|
||||
}
|
||||
|
||||
state.walkTarget = Eigen::Vector3f(state.position.x(), 0.f, state.position.z());
|
||||
state.onArrivedCallback = nullptr;
|
||||
state.onArrivedCallbackName.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
state.pathWaypoints.clear();
|
||||
state.currentWaypointIndex = 0;
|
||||
state.walkTarget = normalizedTarget;
|
||||
}
|
||||
|
||||
void Character::stopAndRotateToTarget(int npcId)
|
||||
{
|
||||
state.onArrivedCallback = nullptr;
|
||||
state.onArrivedCallbackName = "";
|
||||
state.walkTarget = state.position;
|
||||
state.requestedWalkTarget = state.position;
|
||||
state.faceTargetIndex = npcId;
|
||||
state.pathWaypoints.clear();
|
||||
state.currentWaypointIndex = 0;
|
||||
}
|
||||
|
||||
void Character::setPathPlanner(PathPlanner planner) {
|
||||
pathPlanner = std::move(planner);
|
||||
}
|
||||
|
||||
void Character::resetPlayerAfterDeath() {
|
||||
if (!state.isPlayer) return;
|
||||
state.currentState = AnimationState::STAND;
|
||||
state.resetAnim = true;
|
||||
state.showWeapon = false;
|
||||
state.battle_state = 0;
|
||||
state.attack = 0;
|
||||
}
|
||||
|
||||
void Character::forceReplan() {
|
||||
if (!pathPlanner) return;
|
||||
|
||||
const Eigen::Vector3f normalizedTarget(state.requestedWalkTarget.x(), 0.f, state.requestedWalkTarget.z());
|
||||
state.pathWaypoints = pathPlanner(state.position, normalizedTarget);
|
||||
state.currentWaypointIndex = 0;
|
||||
|
||||
if (!state.pathWaypoints.empty()) {
|
||||
for (Eigen::Vector3f& waypoint : state.pathWaypoints) {
|
||||
waypoint.y() = 0.f;
|
||||
}
|
||||
state.walkTarget = state.pathWaypoints.back();
|
||||
return;
|
||||
}
|
||||
|
||||
state.walkTarget = Eigen::Vector3f(state.position.x(), 0.f, state.position.z());
|
||||
state.onArrivedCallback = nullptr;
|
||||
state.onArrivedCallbackName.clear();
|
||||
}
|
||||
|
||||
void Character::setTexture(std::shared_ptr<FRG::Texture> texture) {
|
||||
for (auto& animEntry : animations) {
|
||||
for (const auto& name : animEntry.second.model.meshNamesOrdered) {
|
||||
@@ -205,275 +125,38 @@ void Character::setTexture(const std::string& meshName, std::shared_ptr<Texture>
|
||||
}
|
||||
|
||||
AnimationState Character::resolveActiveState() const {
|
||||
if (animations.count(state.currentState)) return state.currentState;
|
||||
if (animations.count(characterState.currentState)) return characterState.currentState;
|
||||
return AnimationState::STAND;
|
||||
}
|
||||
|
||||
float Character::getSpeedMultiplier(float transitionProgress) const {
|
||||
if (state.isPlayer) {
|
||||
return 1.0f + (1.5f - 1.0f) * transitionProgress;
|
||||
} else {
|
||||
return 1.0f - (1.0f - 0.1f) * transitionProgress;
|
||||
}
|
||||
}
|
||||
|
||||
void Character::update(int64_t deltaMs, const CharacterResolver& resolver) {
|
||||
if (!state.enabled) return;
|
||||
|
||||
if (state.slowMoTimeRemaining > 0.0f) {
|
||||
state.slowMoTimeRemaining -= static_cast<float>(deltaMs) / 1000.0f;
|
||||
|
||||
float transitionProgress;
|
||||
if (state.slowMoTimeRemaining > state.slowMoActiveTime - state.slowMoTransitionTime) {
|
||||
float timeInTransition = state.slowMoActiveTime - state.slowMoTimeRemaining;
|
||||
transitionProgress = (std::min)(1.0f, timeInTransition / state.slowMoTransitionTime);
|
||||
} else if (state.slowMoTimeRemaining < state.slowMoTransitionTime) {
|
||||
transitionProgress = (std::max)(0.0f, state.slowMoTimeRemaining / state.slowMoTransitionTime);
|
||||
} else {
|
||||
transitionProgress = 1.0f;
|
||||
}
|
||||
|
||||
float speedMultiplier = getSpeedMultiplier(transitionProgress);
|
||||
deltaMs = static_cast<int64_t>(static_cast<float>(deltaMs) * speedMultiplier);
|
||||
}
|
||||
|
||||
Character* attackTarget = resolver ? resolver(state.attackTargetIndex) : nullptr;
|
||||
Character* faceTarget = resolver ? resolver(state.faceTargetIndex) : nullptr;
|
||||
|
||||
Eigen::Vector3f activeTarget;
|
||||
Eigen::Vector3f lookTarget;
|
||||
|
||||
if (attackTarget == nullptr) {
|
||||
state.battle_state = 0;
|
||||
state.attack = 0;
|
||||
}
|
||||
|
||||
if (attackTarget != nullptr) {
|
||||
float distToGhost = (attackTarget->state.position - state.position).norm();
|
||||
if (distToGhost >= state.visibleRange) {
|
||||
if (state.isPlayer) {
|
||||
setTarget(attackTarget->state.position);
|
||||
}
|
||||
state.battle_state = 0;
|
||||
} else if (distToGhost < state.visibleRange && distToGhost >= kAttackRange) {
|
||||
setTarget(attackTarget->state.position);
|
||||
state.battle_state = 0;
|
||||
} else {
|
||||
state.battle_state = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.pathWaypoints.empty() && state.currentWaypointIndex < state.pathWaypoints.size()) {
|
||||
activeTarget = state.pathWaypoints[state.currentWaypointIndex];
|
||||
} else {
|
||||
activeTarget = state.walkTarget;
|
||||
}
|
||||
|
||||
if (state.battle_state == 0) {
|
||||
lookTarget = activeTarget;
|
||||
} else {
|
||||
lookTarget = attackTarget->state.position;
|
||||
}
|
||||
|
||||
if (state.currentState == AnimationState::WALK || state.currentState == AnimationState::STAND) {
|
||||
Eigen::Vector3f toTarget = activeTarget - state.position;
|
||||
toTarget.y() = 0.f;
|
||||
float dist = toTarget.norm();
|
||||
|
||||
if (dist > CharacterState::kWalkThreshold) {
|
||||
Eigen::Vector3f dir = toTarget / dist;
|
||||
float moveAmount = state.walkSpeed * static_cast<float>(deltaMs) / 1000.f;
|
||||
if (moveAmount >= dist) {
|
||||
state.position = activeTarget;
|
||||
state.position.y() = 0.f;
|
||||
} else {
|
||||
state.position += dir * moveAmount;
|
||||
}
|
||||
state.targetFacingAngle = atan2(dir.x(), -dir.z());
|
||||
state.currentState = AnimationState::WALK;
|
||||
} else {
|
||||
state.currentState = AnimationState::STAND;
|
||||
|
||||
const bool hasNextWaypoint = !state.pathWaypoints.empty() &&
|
||||
state.currentWaypointIndex + 1 < state.pathWaypoints.size();
|
||||
if (hasNextWaypoint) {
|
||||
++state.currentWaypointIndex;
|
||||
} else {
|
||||
state.pathWaypoints.clear();
|
||||
state.currentWaypointIndex = 0;
|
||||
}
|
||||
|
||||
if (!hasNextWaypoint && state.onArrivedCallback) {
|
||||
auto cb = std::move(state.onArrivedCallback);
|
||||
state.onArrivedCallback = nullptr;
|
||||
state.onArrivedCallbackName.clear();
|
||||
cb();
|
||||
}
|
||||
|
||||
if (faceTarget) {
|
||||
Eigen::Vector3f toFace = faceTarget->state.position - state.position;
|
||||
toFace.y() = 0.f;
|
||||
float d = toFace.norm();
|
||||
if (d > 1e-3f) {
|
||||
state.targetFacingAngle = atan2(toFace.x() / d, -toFace.z() / d);
|
||||
} else {
|
||||
state.targetFacingAngle = state.facingAngle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.hp <= 0) {
|
||||
if (state.currentState != AnimationState::ACTION_TO_DEATH &&
|
||||
state.currentState != AnimationState::DEATH_IDLE) {
|
||||
state.currentState = AnimationState::ACTION_TO_DEATH;
|
||||
state.resetAnim = true;
|
||||
state.attackTargetIndex = CharacterState::kNoTarget;
|
||||
}
|
||||
} else {
|
||||
if (state.battle_state == 1) {
|
||||
state.targetFacingAngle = atan2(lookTarget.x() - state.position.x(),
|
||||
-(lookTarget.z() - state.position.z()));
|
||||
|
||||
if (state.currentState == AnimationState::STAND || state.currentState == AnimationState::WALK) {
|
||||
state.currentState = AnimationState::STAND_TO_ACTION;
|
||||
state.resetAnim = true;
|
||||
}
|
||||
|
||||
if (state.canAttack && state.attack == 0 && state.attack_cooldown < 0.f &&
|
||||
state.currentState == AnimationState::ACTION_IDLE) {
|
||||
state.attack = 1;
|
||||
state.attack_cooldown = ATTACK_COOLDOWN_TIME;
|
||||
}
|
||||
if (state.canAttack && state.attack_cooldown >= 0.f) {
|
||||
state.attack_cooldown -= deltaMs / 1000.f;
|
||||
}
|
||||
|
||||
if (state.attack == 1 && state.currentState == AnimationState::ACTION_IDLE) {
|
||||
if (attackTarget != nullptr && attackTarget->state.hp > 0) {
|
||||
state.currentState = AnimationState::ACTION_ATTACK;
|
||||
state.resetAnim = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (state.currentState == AnimationState::STAND_TO_ACTION ||
|
||||
state.currentState == AnimationState::ACTION_IDLE ||
|
||||
state.currentState == AnimationState::ACTION_ATTACK) {
|
||||
state.currentState = AnimationState::ACTION_TO_STAND;
|
||||
state.resetAnim = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate toward target facing angle at constant angular speed
|
||||
float angleDiff = state.targetFacingAngle - state.facingAngle;
|
||||
while (angleDiff > static_cast<float>(M_PI)) angleDiff -= 2.f * static_cast<float>(M_PI);
|
||||
while (angleDiff < -static_cast<float>(M_PI)) angleDiff += 2.f * static_cast<float>(M_PI);
|
||||
float rotStep = state.rotationSpeed * static_cast<float>(deltaMs) / 1000.f;
|
||||
|
||||
if (std::fabs(angleDiff) <= rotStep) {
|
||||
state.facingAngle = state.targetFacingAngle;
|
||||
// One-shot: once aligned, clear the face target so the NPC stops tracking.
|
||||
state.faceTargetIndex = CharacterState::kNoTarget;
|
||||
} else {
|
||||
state.facingAngle += (angleDiff > 0.f ? rotStep : -rotStep);
|
||||
}
|
||||
|
||||
if (!state.isPlayer && state.hp > 0.f && state.battle_state == 0 && state.maxHomeDrift > 0.f) {
|
||||
state.homeDriftCheckTimer -= static_cast<float>(deltaMs) / 1000.f;
|
||||
if (state.homeDriftCheckTimer <= 0.f) {
|
||||
state.homeDriftCheckTimer = HOME_DRIFT_CHECK_INTERVAL;
|
||||
Eigen::Vector3f toHome = state.homePosition - state.position;
|
||||
toHome.y() = 0.f;
|
||||
if (!isMoving() && toHome.norm() > state.maxHomeDrift) {
|
||||
setTarget(state.homePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Character::update(int64_t deltaMs, const CharacterUpdateEvents* events) {
|
||||
if (hitSparkEmitter.isConfigured()) {
|
||||
hitSparkEmitter.update(static_cast<float>(deltaMs));
|
||||
}
|
||||
|
||||
auto it = animations.find(state.currentState);
|
||||
if (events) {
|
||||
if (events->deathAnimationComplete && onDeathAnimComplete)
|
||||
onDeathAnimComplete();
|
||||
if (events->attackImpact && audioPlayer) {
|
||||
audioPlayer->playSoundAsync(characterState.isPlayer
|
||||
? "resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg"
|
||||
: "resources/audio/13925__adcbicycle__21.ogg");
|
||||
}
|
||||
}
|
||||
|
||||
if (!characterState.enabled) return;
|
||||
auto it = animations.find(characterState.currentState);
|
||||
if (it == animations.end()) return;
|
||||
|
||||
auto& anim = it->second;
|
||||
|
||||
if (state.resetAnim) {
|
||||
state.resetAnim = false;
|
||||
anim.currentFrame = 0;
|
||||
}
|
||||
int prevFrame = anim.currentFrame;
|
||||
anim.currentFrame += static_cast<float>(deltaMs) / 24.f;
|
||||
|
||||
if (static_cast<int>(anim.currentFrame) >= 20 && state.currentState == AnimationState::STAND_TO_ACTION) {
|
||||
state.showWeapon = true;
|
||||
} else if (static_cast<int>(anim.currentFrame) <= 32 && state.currentState == AnimationState::ACTION_TO_STAND) {
|
||||
state.showWeapon = true;
|
||||
} else if (state.currentState == AnimationState::ACTION_ATTACK_2 ||
|
||||
state.currentState == AnimationState::ACTION_ATTACK ||
|
||||
state.currentState == AnimationState::ACTION_IDLE) {
|
||||
state.showWeapon = true;
|
||||
} else {
|
||||
state.showWeapon = false;
|
||||
}
|
||||
|
||||
auto attackDirectionToTarget = [&]() {
|
||||
Eigen::Vector3f dir = attackTarget->state.position - state.position;
|
||||
dir.y() = 0.f;
|
||||
float n = dir.norm();
|
||||
if (n > 1e-3f) dir /= n;
|
||||
else dir = Eigen::Vector3f::Zero();
|
||||
return dir;
|
||||
};
|
||||
|
||||
if (prevFrame < 19 && static_cast<int>(anim.currentFrame) >= 19 &&
|
||||
(state.currentState == AnimationState::ACTION_ATTACK ||
|
||||
state.currentState == AnimationState::ACTION_ATTACK_2)) {
|
||||
if (attackTarget != nullptr) {
|
||||
attackTarget->applyDamage(10.f, attackDirectionToTarget(), index);
|
||||
|
||||
if (audioPlayer) {
|
||||
if (state.isPlayer)
|
||||
{
|
||||
audioPlayer->playSoundAsync("resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioPlayer->playSoundAsync("resources/audio/13925__adcbicycle__21.ogg");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int frms = anim.model.animations[0].keyFrames[anim.model.animations[0].keyFrames.size() - 1].frame;
|
||||
if (static_cast<int>(anim.currentFrame) >= frms) {
|
||||
anim.currentFrame = anim.model.startingFrame;
|
||||
|
||||
if (state.currentState == AnimationState::STAND_TO_ACTION) {
|
||||
state.currentState = AnimationState::ACTION_IDLE;
|
||||
}
|
||||
if (state.currentState == AnimationState::ACTION_TO_STAND) {
|
||||
state.currentState = AnimationState::STAND;
|
||||
}
|
||||
if (state.currentState == AnimationState::ACTION_ATTACK) {
|
||||
state.currentState = AnimationState::ACTION_IDLE;
|
||||
state.attack = 0;
|
||||
}
|
||||
if (state.currentState == AnimationState::ACTION_TO_DEATH) {
|
||||
state.currentState = AnimationState::DEATH_IDLE;
|
||||
if (state.onDeathAnimComplete) {
|
||||
state.onDeathAnimComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
anim.currentFrame = (std::min)(
|
||||
characterState.animationFrame, static_cast<float>((std::max)(0, anim.totalFrames - 1)));
|
||||
|
||||
if (static_cast<int>(anim.currentFrame) != anim.lastFrame) {
|
||||
anim.gpuSkinningShaderData.ComputeSkinningMatrices(
|
||||
anim.model.startBones, anim.model.animations[0].keyFrames,
|
||||
static_cast<int>(anim.currentFrame));
|
||||
if (!state.useGpuSkinning) {
|
||||
if (!characterState.useGpuSkinning) {
|
||||
anim.model.Interpolate(static_cast<int>(anim.currentFrame));
|
||||
}
|
||||
anim.lastFrame = static_cast<int>(anim.currentFrame);
|
||||
@@ -488,9 +171,9 @@ void Character::draw(Renderer& renderer) {
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (!state.enabled) return;
|
||||
if (!characterState.enabled) return;
|
||||
|
||||
if (state.useGpuSkinning) {
|
||||
if (characterState.useGpuSkinning) {
|
||||
drawGpuSkinning(renderer);
|
||||
return;
|
||||
}
|
||||
@@ -509,10 +192,10 @@ void Character::draw(Renderer& renderer) {
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
auto& anim = it->second;
|
||||
for (const auto& name : anim.model.meshNamesOrdered) {
|
||||
@@ -563,10 +246,10 @@ void Character::drawGpuSkinning(Renderer& renderer) {
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||
static_cast<int>(anim.gpuSkinningShaderData.skinningMatrices.size()), false,
|
||||
@@ -610,24 +293,8 @@ bool Character::prepareGpuSkinning() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Character::tryAttackCombo()
|
||||
{
|
||||
logger() << "Try Attack Combo" << std::endl;
|
||||
auto it = animations.find(state.currentState);
|
||||
if (it == animations.end()) return;
|
||||
|
||||
auto& anim = it->second;
|
||||
|
||||
if (static_cast<int>(anim.currentFrame) >= 20 &&
|
||||
(state.currentState == AnimationState::ACTION_ATTACK ||
|
||||
state.currentState == AnimationState::ACTION_ATTACK_2)) {
|
||||
logger() << "Try Attack Combo Success" << std::endl;
|
||||
anim.currentFrame = anim.model.startingFrame;
|
||||
}
|
||||
}
|
||||
|
||||
void Character::drawAttachedWeapon(Renderer& renderer) {
|
||||
if (!state.showWeapon) return;
|
||||
if (!characterState.showWeapon) return;
|
||||
if (weaponMesh.data.PositionData.size() == 0 || !weaponTexture) return;
|
||||
|
||||
auto it = animations.find(resolveActiveState());
|
||||
@@ -643,10 +310,10 @@ void Character::drawAttachedWeapon(Renderer& renderer) {
|
||||
Eigen::Matrix4f currentBone = anim.gpuSkinningShaderData.skinningMatrices[boneIdx] * bindBone;
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix(Eigen::Vector3f(currentBone.block<3, 1>(0, 3)));
|
||||
renderer.RotateMatrix(Eigen::Matrix3f(currentBone.block<3, 3>(0, 0)));
|
||||
renderer.TranslateMatrix(weaponInitialPosition);
|
||||
@@ -660,8 +327,8 @@ void Character::drawAttachedWeapon(Renderer& renderer) {
|
||||
// ==================== Shadow depth pass ====================
|
||||
|
||||
void Character::drawShadowDepth(Renderer& renderer) {
|
||||
if (!state.enabled) return;
|
||||
if (state.useGpuSkinning) {
|
||||
if (!characterState.enabled) return;
|
||||
if (characterState.useGpuSkinning) {
|
||||
drawShadowDepthGpuSkinning(renderer);
|
||||
} else {
|
||||
drawShadowDepthCpu(renderer);
|
||||
@@ -674,10 +341,10 @@ void Character::drawShadowDepthCpu(Renderer& renderer) {
|
||||
if (it == animations.end()) return;
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
auto& anim = it->second;
|
||||
for (const auto& name : anim.model.meshNamesOrdered) {
|
||||
@@ -710,10 +377,10 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
||||
renderer.shaderManager.PushShader(shadowSkinningShader);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||
@@ -740,7 +407,7 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
||||
// ==================== Main pass with shadows ====================
|
||||
|
||||
void Character::drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
|
||||
if (!state.enabled) return;
|
||||
if (!characterState.enabled) return;
|
||||
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||
}
|
||||
@@ -748,7 +415,7 @@ void Character::drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightF
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (state.useGpuSkinning) {
|
||||
if (characterState.useGpuSkinning) {
|
||||
drawGpuSkinningWithShadow(renderer, lightFromCamera, shadowMapTex, lightDirCamera);
|
||||
} else {
|
||||
drawCpuWithShadow(renderer, lightFromCamera, shadowMapTex, lightDirCamera);
|
||||
@@ -779,10 +446,10 @@ void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lig
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
auto& anim = it->second;
|
||||
for (const auto& name : anim.model.meshNamesOrdered) {
|
||||
@@ -841,10 +508,10 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||
@@ -887,7 +554,7 @@ void Character::drawNight(Renderer& renderer,
|
||||
const Eigen::Matrix4f& cameraViewInverse,
|
||||
const float* ambientColor, const float* fogColor)
|
||||
{
|
||||
if (!state.enabled) return;
|
||||
if (!characterState.enabled) return;
|
||||
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||
}
|
||||
@@ -895,7 +562,7 @@ void Character::drawNight(Renderer& renderer,
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (state.useGpuSkinning) {
|
||||
if (characterState.useGpuSkinning) {
|
||||
drawNightGpuSkinning(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount,
|
||||
pointLightWorldXZ, pointLightLimitXZ, cameraViewInverse, ambientColor, fogColor);
|
||||
} else {
|
||||
@@ -939,10 +606,10 @@ void Character::drawNightGpuSkinning(Renderer& renderer,
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
||||
@@ -1010,10 +677,10 @@ void Character::drawNightCpu(Renderer& renderer,
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
auto& anim = it->second;
|
||||
for (const auto& name : anim.model.meshNamesOrdered) {
|
||||
@@ -1045,14 +712,14 @@ void Character::drawNightWithShadow(Renderer& renderer,
|
||||
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex,
|
||||
const float* ambientColor, const float* fogColor)
|
||||
{
|
||||
if (!state.enabled) return;
|
||||
if (!characterState.enabled) return;
|
||||
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||
}
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (state.useGpuSkinning) {
|
||||
if (characterState.useGpuSkinning) {
|
||||
drawNightGpuSkinningWithShadow(renderer, plPosEye, plDirEye, plColors, plCount,
|
||||
plWorldXZ, plLimitXZ, cameraViewInverse,
|
||||
lightFromCamera, shadowMapTex, ambientColor, fogColor);
|
||||
@@ -1107,10 +774,10 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer,
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
||||
@@ -1191,10 +858,10 @@ void Character::drawNightCpuWithShadow(Renderer& renderer,
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(state.modelScale);
|
||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(characterState.modelScale);
|
||||
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
auto& anim = it->second;
|
||||
for (const auto& name : anim.model.meshNamesOrdered) {
|
||||
@@ -1232,16 +899,15 @@ void Character::setupHitSparks(std::shared_ptr<Texture> sparkTexture) {
|
||||
hitSparkEmitter.markConfigured();
|
||||
}
|
||||
|
||||
void Character::applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection, int attackerIndex) {
|
||||
logger() << "Character::applyDamage" << std::endl;
|
||||
state.hp -= damageAmount;
|
||||
if (state.hp < 0) state.hp = 0;
|
||||
|
||||
if (state.onHpChanged) state.onHpChanged(state.hp, state.initialHp, attackerIndex);
|
||||
void Character::presentDamage(float hp, float maxHp,
|
||||
const Eigen::Vector3f& attackDirection,
|
||||
const std::string& attackerId) {
|
||||
if (onHpChanged)
|
||||
onHpChanged(hp, maxHp, attackerId);
|
||||
|
||||
if (!hitSparkEmitter.isConfigured()) return;
|
||||
|
||||
Eigen::Vector3f emitPos = state.position + Eigen::Vector3f(0.f, 1.f, 0.f);
|
||||
Eigen::Vector3f emitPos = characterState.position + Eigen::Vector3f(0.f, 1.f, 0.f);
|
||||
hitSparkEmitter.resetEmissionPoints({ emitPos });
|
||||
hitSparkEmitter.setEmissionDirection(attackDirection);
|
||||
|
||||
@@ -1262,12 +928,12 @@ void Character::drawName(TextRenderer& textRenderer,
|
||||
const Eigen::Matrix4f& cameraViewMatrix,
|
||||
const Eigen::Matrix4f& projectionMatrix)
|
||||
{
|
||||
if (state.isPlayer) return;
|
||||
if (!state.enabled) return;
|
||||
if (state.hp <= 0.f) return;
|
||||
if (state.npcName.empty()) return;
|
||||
if (characterState.isPlayer) return;
|
||||
if (!characterState.enabled) return;
|
||||
if (characterState.hp <= 0.f) return;
|
||||
if (characterState.npcName.empty()) return;
|
||||
|
||||
Eigen::Vector4f worldPos(state.position.x(), state.position.y() + 1.83f, state.position.z(), 1.f);
|
||||
Eigen::Vector4f worldPos(characterState.position.x(), characterState.position.y() + 1.83f, characterState.position.z(), 1.f);
|
||||
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
|
||||
if (clip.w() <= 0.f) return;
|
||||
|
||||
@@ -1278,11 +944,11 @@ void Character::drawName(TextRenderer& textRenderer,
|
||||
float screenX = (ndcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||
float screenY = (ndcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||
|
||||
std::array<float, 4> color = state.canAttack
|
||||
std::array<float, 4> color = characterState.canAttack
|
||||
? std::array<float, 4>{ 1.f, 0.f, 0.f, 1.f }
|
||||
: std::array<float, 4>{ 0.f, 1.f, 0.f, 1.f };
|
||||
|
||||
const std::string& translatedName = Character::nameTranslations.translateRef(state.npcName, g_currentLanguage);
|
||||
const std::string& translatedName = Character::nameTranslations.translateRef(characterState.npcName, g_currentLanguage);
|
||||
|
||||
textRenderer.drawText(translatedName, screenX, screenY, 1.0f, true, color);
|
||||
}
|
||||
@@ -1291,12 +957,12 @@ void Character::drawHealthBar(Renderer& renderer,
|
||||
const Eigen::Matrix4f& cameraViewMatrix,
|
||||
const Eigen::Matrix4f& projectionMatrix)
|
||||
{
|
||||
if (state.isPlayer || !state.canAttack) return;
|
||||
if (!state.enabled) return;
|
||||
if (state.hp <= 0.f) return;
|
||||
if (state.initialHp <= 0.f) return;
|
||||
if (characterState.isPlayer || !characterState.canAttack) return;
|
||||
if (!characterState.enabled) return;
|
||||
if (characterState.hp <= 0.f) return;
|
||||
if (characterState.initialHp <= 0.f) return;
|
||||
|
||||
Eigen::Vector4f worldPos(state.position.x(), state.position.y() + 1.75f, state.position.z(), 1.f);
|
||||
Eigen::Vector4f worldPos(characterState.position.x(), characterState.position.y() + 1.75f, characterState.position.z(), 1.f);
|
||||
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
|
||||
if (clip.w() <= 0.f) return;
|
||||
float ndcX = clip.x() / clip.w();
|
||||
@@ -1314,7 +980,7 @@ void Character::drawHealthBar(Renderer& renderer,
|
||||
const float bottom = sy - barH * 0.5f;
|
||||
const float top = sy + barH * 0.5f;
|
||||
|
||||
float frac = state.hp / state.initialHp;
|
||||
float frac = characterState.hp / characterState.initialHp;
|
||||
if (frac < 0.f) frac = 0.f;
|
||||
if (frac > 1.f) frac = 1.f;
|
||||
const float split = left + barW * frac;
|
||||
|
||||
Reference in new issue
Block a user