Adapt for web, make CharacterState class

This commit is contained in:
Vladislav Khorev committed 2026-06-18 20:02:59 +03:00
1 parent c891161d69
commit 0eb9d0be25
88 files changed
+810 -102277

No files matched your search

+92 -93
View File
@@ -87,20 +87,20 @@ namespace ZL
player->weaponInitialPosition = Eigen::Vector3f(0, 0.09, 0.016);
player->setTexture(playerTexture);
player->walkSpeed = 3.0f;
player->rotationSpeed = 8.0f;
player->state.walkSpeed = 3.0f;
player->state.rotationSpeed = 8.0f;
player->modelScale = 1.f;
player->modelCorrectionRotation = Eigen::Quaternionf(
player->state.modelScale = 1.f;
player->state.modelCorrectionRotation = Eigen::Quaternionf(
Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())
*
Eigen::AngleAxisf(-M_PI*0.5, Eigen::Vector3f::UnitX())
);
player->canAttack = true;
player->isPlayer = true;
player->position = params.playerPosition;
player->state.canAttack = true;
player->state.isPlayer = true;
player->state.position = params.playerPosition;
player->setTarget(params.playerPosition);
player->setupHitSparks(sparkTexture);
std::cout << "Load resurces step 9" << std::endl;
@@ -111,10 +111,10 @@ namespace ZL
npcs = GameObjectLoader::loadAndCreate_Npcs(params.npcsJsonPath, CONST_ZIP_FILE);
for (auto& npc : npcs) {
if (!npc) continue;
npc->homePosition = Eigen::Vector3f(npc->position.x(), 0.f, npc->position.z());
if (npc->canAttack) {
npc->state.homePosition = Eigen::Vector3f(npc->state.position.x(), 0.f, npc->state.position.z());
if (npc->state.canAttack) {
npc->setupHitSparks(sparkTexture);
npc->attackTarget = player.get();
npc->state.attackTargetIndex = CharacterState::kPlayerIndex;
}
}
@@ -385,16 +385,16 @@ namespace ZL
const auto addCharacter = [&](const Character* other) {
if (!other || other == self) return;
if (other->getHp() <= 0.f || !other->enabled) return;
if (other->getHp() <= 0.f || !other->state.enabled) return;
if (other->isMoving()) return;
if (distancePointToSegmentXZ(other->position, start, end) > kDynamicObstacleInfluenceDist) {
if (distancePointToSegmentXZ(other->state.position, start, end) > kDynamicObstacleInfluenceDist) {
return;
}
PathFinder::DynamicObstacle obs;
obs.position = Eigen::Vector3f(other->position.x(), navigation->getFloorY(), other->position.z());
obs.radius = (std::max)(0.0f, other->collisionRadius * 0.6f);
obs.position = Eigen::Vector3f(other->state.position.x(), navigation->getFloorY(), other->state.position.z());
obs.radius = (std::max)(0.0f, other->state.collisionRadius * 0.6f);
dynamicObstacles.push_back(obs);
};
@@ -540,12 +540,12 @@ namespace ZL
for (auto& npc : npcs) {
if (npc->getHp() <= 0.f) {
//std::cout << "[RAYCAST_NPC] " << npc->npcId << " is dead, skipping" << std::endl;
//std::cout << "[RAYCAST_NPC] " << npc->state.npcId << " is dead, skipping" << std::endl;
continue;
}
if (!npc->enabled) continue;
const float dx = rayOrigin.x() - npc->position.x();
const float dz = rayOrigin.z() - npc->position.z();
if (!npc->state.enabled) continue;
const float dx = rayOrigin.x() - npc->state.position.x();
const float dz = rayOrigin.z() - npc->state.position.z();
const float a = rayDir.x() * rayDir.x() + rayDir.z() * rayDir.z();
if (a < 1e-6f) continue; // purely-vertical ray; not a real click case
@@ -562,7 +562,7 @@ namespace ZL
float exitT = tFar;
// Clip against the cylinder's vertical extent.
const float yMin = npc->position.y();
const float yMin = npc->state.position.y();
const float yMax = yMin + NPC_CLICK_HEIGHT;
if (std::abs(rayDir.y()) > 1e-6f) {
const float tYa = (yMin - rayOrigin.y()) / rayDir.y();
@@ -617,7 +617,7 @@ namespace ZL
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix());
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix());
const Eigen::Vector3f& camTarget = player ? player->position : Eigen::Vector3f::Zero();
const Eigen::Vector3f& camTarget = player ? player->state.position : Eigen::Vector3f::Zero();
renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() });
renderer.TranslateMatrix({ 0, -1.f, 0 });
@@ -679,7 +679,7 @@ namespace ZL
{
if (!shadowMap) return;
const Eigen::Vector3f& sceneCenter = player ? player->position : Eigen::Vector3f::Zero();
const Eigen::Vector3f& sceneCenter = player ? player->state.position : Eigen::Vector3f::Zero();
shadowMap->updateLightSpaceMatrix(sceneCenter);
shadowMap->bind();
@@ -788,7 +788,7 @@ namespace ZL
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix());
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix());
const Eigen::Vector3f& camTarget = player ? player->position : Eigen::Vector3f::Zero();
const Eigen::Vector3f& camTarget = player ? player->state.position : Eigen::Vector3f::Zero();
renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() });
renderer.TranslateMatrix({ 0, -1.f, 0 });
@@ -899,7 +899,7 @@ namespace ZL
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix());
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix());
const Eigen::Vector3f& camTarget = player ? player->position : Eigen::Vector3f::Zero();
const Eigen::Vector3f& camTarget = player ? player->state.position : Eigen::Vector3f::Zero();
renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() });
renderer.TranslateMatrix({ 0, -1.f, 0 });
@@ -998,7 +998,7 @@ namespace ZL
if (player && !pointLights.empty()) {
float minDist = FLT_MAX;
for (int i = 0; i < static_cast<int>(pointLights.size()); ++i) {
const float d = (pointLights[i].position - player->position).norm();
const float d = (pointLights[i].position - player->state.position).norm();
if (d < minDist) { minDist = d; nearestLightIdx = i; }
}
}
@@ -1012,12 +1012,12 @@ namespace ZL
if (!active && player) {
if (pl.autoLightLimitX > 0.0f && pl.autoLightLimitY > 0.0f) {
// Rectangular activation zone — independent of nearest-light heuristic
const float dx = std::abs(player->position.x() - pl.position.x());
const float dz = std::abs(player->position.z() - pl.position.z());
const float dx = std::abs(player->state.position.x() - pl.position.x());
const float dz = std::abs(player->state.position.z() - pl.position.z());
active = (dx <= pl.autoLightLimitX && dz <= pl.autoLightLimitY);
} else {
// Legacy: only the nearest autoLight activates, with optional distance cap
const float dist = (pl.position - player->position).norm();
const float dist = (pl.position - player->state.position).norm();
active = (i == nearestLightIdx) &&
(pl.autoLightDistance <= 0.0f || dist <= pl.autoLightDistance);
}
@@ -1033,7 +1033,7 @@ namespace ZL
if (player && !activeLightIndices.empty()) {
float minDist = FLT_MAX;
for (int idx : activeLightIndices) {
const float d = (pointLights[idx].position - player->position).norm();
const float d = (pointLights[idx].position - player->state.position).norm();
if (d < minDist) { minDist = d; shadowLight = &pointLights[idx]; }
}
}
@@ -1081,7 +1081,7 @@ namespace ZL
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix());
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix());
const Eigen::Vector3f& camTarget = player ? player->position : Eigen::Vector3f::Zero();
const Eigen::Vector3f& camTarget = player ? player->state.position : Eigen::Vector3f::Zero();
renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() });
renderer.TranslateMatrix({ 0, -1.f, 0 });
@@ -1212,7 +1212,7 @@ namespace ZL
void Location::fireBumpCallbacks(Character* mover, Character* standing)
{
if (mover->isPlayer) {
if (mover->state.isPlayer) {
// Player walked into a standing NPC
const int idx = findNpcIndex(standing);
if (idx < 0) return;
@@ -1221,7 +1221,7 @@ namespace ZL
cooldown = NPC_BUMP_CALLBACK_COOLDOWN;
scriptEngine.callNpcBumpedByPlayerCallback(idx);
}
} else if (standing->isPlayer) {
} else if (standing->state.isPlayer) {
// Moving NPC walked into the standing player
const int idx = findNpcIndex(mover);
if (idx < 0) return;
@@ -1235,12 +1235,12 @@ namespace ZL
void Location::nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom)
{
if (!standing || standing->isPlayer) return;
if (!standing || standing->state.isPlayer) return;
if (!navigation || !navigation->isReady()) return;
static constexpr float kNudgeDist = 1.2f;
Eigen::Vector3f dir = standing->position - awayFrom;
Eigen::Vector3f dir = standing->state.position - awayFrom;
dir.y() = 0.f;
if (dir.norm() < 1e-3f) dir = Eigen::Vector3f(1.f, 0.f, 0.f);
else dir.normalize();
@@ -1248,7 +1248,7 @@ namespace ZL
const float angles[] = { 0.f, static_cast<float>(M_PI * 0.5), static_cast<float>(-M_PI * 0.5), static_cast<float>(M_PI) };
for (float angle : angles) {
Eigen::Vector3f rotated = Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitY()) * dir;
Eigen::Vector3f candidate = standing->position + rotated * kNudgeDist;
Eigen::Vector3f candidate = standing->state.position + rotated * kNudgeDist;
candidate.y() = 0.f;
if (navigation->isWalkable(candidate)) {
standing->setTarget(candidate);
@@ -1284,13 +1284,13 @@ namespace ZL
Character* a = characters[i];
Character* b = characters[j];
if (!a || !b) continue;
if (a->getHp() <= 0.f || b->getHp() <= 0.f || !a->enabled || !b->enabled) continue;
if (a->getHp() <= 0.f || b->getHp() <= 0.f || !a->state.enabled || !b->state.enabled) continue;
const float minDist = a->collisionRadius + b->collisionRadius;
const float minDist = a->state.collisionRadius + b->state.collisionRadius;
if (minDist <= 0.f) continue;
const Eigen::Vector2f pa(a->position.x(), a->position.z());
const Eigen::Vector2f pb(b->position.x(), b->position.z());
const Eigen::Vector2f pa(a->state.position.x(), a->state.position.z());
const Eigen::Vector2f pb(b->state.position.x(), b->state.position.z());
const Eigen::Vector2f delta = pb - pa;
const float dist = delta.norm();
@@ -1306,8 +1306,8 @@ namespace ZL
const float penetration = (minDist - dist);
const float push = penetration * 0.5f;
Eigen::Vector3f newA = a->position;
Eigen::Vector3f newB = b->position;
Eigen::Vector3f newA = a->state.position;
Eigen::Vector3f newB = b->state.position;
newA.x() -= normal.x() * push;
newA.z() -= normal.y() * push;
newA.y() = 0.f;
@@ -1323,29 +1323,29 @@ namespace ZL
const bool bOk = navigation->isWalkable(newB);
if (aOk && bOk) {
a->position = newA;
b->position = newB;
a->state.position = newA;
b->state.position = newB;
}
else if (aOk && !bOk) {
a->position = newA;
a->state.position = newA;
}
else if (!aOk && bOk) {
b->position = newB;
b->state.position = newB;
}
}
else {
a->position = newA;
b->position = newB;
a->state.position = newA;
b->state.position = newB;
}
if (a->isPlayer && !aWasMoving) a->stopInPlace();
if (b->isPlayer && !bWasMoving) b->stopInPlace();
if (a->state.isPlayer && !aWasMoving) a->stopInPlace();
if (b->state.isPlayer && !bWasMoving) b->stopInPlace();
if (aWasMoving && !bWasMoving) {
nudgeCharacterAside(b, a->position);
nudgeCharacterAside(b, a->state.position);
fireBumpCallbacks(a, b);
} else if (bWasMoving && !aWasMoving) {
nudgeCharacterAside(a, b->position);
nudgeCharacterAside(a, b->state.position);
fireBumpCallbacks(b, a);
}
}
@@ -1369,7 +1369,7 @@ namespace ZL
for (auto it = stuckPauseRemainingS.begin(); it != stuckPauseRemainingS.end();) {
it->second -= deltaS;
if (it->second <= 0.f) {
if (!it->first->isPlayer) {
if (!it->first->state.isPlayer) {
it->first->forceReplan();
}
it = stuckPauseRemainingS.erase(it);
@@ -1399,13 +1399,13 @@ namespace ZL
if (!c) continue;
auto it = lastCharacterPositions.find(c);
if (it == lastCharacterPositions.end()) {
lastCharacterPositions[c] = c->position;
lastCharacterPositions[c] = c->state.position;
continue;
}
const Eigen::Vector3f prev = it->second;
const float moved = (c->position - prev).norm();
it->second = c->position;
const float moved = (c->state.position - prev).norm();
it->second = c->state.position;
if (moved > kMovedEps) {
movers.push_back(c);
@@ -1417,7 +1417,7 @@ namespace ZL
c->stopInPlace();
stuckFrameCounts[c] = 0;
if (stuckPauseRemainingS.find(c) == stuckPauseRemainingS.end()) {
if (!c->isPlayer) {
if (!c->state.isPlayer) {
static std::mt19937 rng(std::random_device{}());
std::uniform_real_distribution<float> pauseDist(kStuckPauseMin, kStuckPauseMax);
stuckPauseRemainingS[c] = pauseDist(rng);
@@ -1442,7 +1442,7 @@ namespace ZL
for (Character* walker : characters) {
if (!walker || walker == mover) continue;
if (walker->getHp() <= 0.f || !walker->enabled) continue;
if (walker->getHp() <= 0.f || !walker->state.enabled) continue;
if (!walker->isMoving()) continue;
if (replanCooldownRemainingMs.find(walker) != replanCooldownRemainingMs.end()) {
@@ -1450,7 +1450,7 @@ namespace ZL
}
const Eigen::Vector3f nextTarget = walker->getCurrentNavigationTarget();
const float distToSegment = distancePointToSegmentXZ(mover->position, walker->position, nextTarget);
const float distToSegment = distancePointToSegmentXZ(mover->state.position, walker->state.position, nextTarget);
if (distToSegment > kReplanTriggerDist) {
continue;
}
@@ -1467,14 +1467,21 @@ namespace ZL
intObj.update(delta);
}
auto resolver = [&](int idx) -> Character* {
if (idx == CharacterState::kNoTarget) return nullptr;
if (idx == CharacterState::kPlayerIndex) return player.get();
if (idx >= 0 && idx < static_cast<int>(npcs.size())) return npcs[idx].get();
return nullptr;
};
if (player) {
player->update(delta);
dialogueSystem.update(static_cast<int>(delta));
updateTriggerZones(player->position);
player->update(delta, resolver);
dialogueSystem.update(static_cast<int>(delta));
updateTriggerZones(player->state.position);
}
for (auto& npc : npcs)
{
npc->update(delta);
npc->update(delta, resolver);
}
resolveCharacterCollisions();
@@ -1489,7 +1496,7 @@ namespace ZL
const Eigen::Vector3f& approachTarget = targetInteractiveObject->hasInteractionPosition
? targetInteractiveObject->interactionPosition
: targetInteractiveObject->position;
float distToObject = (player->position - approachTarget).norm();
float distToObject = (player->state.position - approachTarget).norm();
// If player is close enough to pick up the item
if (distToObject <= targetInteractiveObject->approachRadius) {
@@ -1516,21 +1523,20 @@ namespace ZL
}
// Check if player reached target NPC for interaction.
if (targetInteractNpc && targetInteractNpcIndex >= 0 && player) {
float distToNpc = (player->position - targetInteractNpc->position).norm();
if (targetInteractNpcIndex >= 0 && player) {
float distToNpc = (player->state.position - npcs[targetInteractNpcIndex]->state.position).norm();
if (distToNpc <= NPC_TALK_DISTANCE) {
std::cout << "[NPC] Player reached NPC index " << targetInteractNpcIndex
<< " (distance " << distToNpc << "); firing on_npc_interact" << std::endl;
// Stop the player at the talk distance and have the NPC turn to face them.
player->setTarget(player->position);
targetInteractNpc->faceTarget = player.get();
player->setTarget(player->state.position);
npcs[targetInteractNpcIndex]->state.faceTargetIndex = CharacterState::kPlayerIndex;
try {
scriptEngine.callNpcInteractCallback(targetInteractNpcIndex);
}
catch (const std::exception& e) {
std::cerr << "[NPC] callback error: " << e.what() << std::endl;
}
targetInteractNpc = nullptr;
targetInteractNpcIndex = -1;
}
}
@@ -1538,7 +1544,7 @@ namespace ZL
for (auto& tz : teleportZones) tz.update(static_cast<float>(delta));
if (targetTeleportZone && player) {
float dist = (player->position - targetTeleportZone->position).norm();
float dist = (player->state.position - targetTeleportZone->position).norm();
if (dist <= targetTeleportZone->radius) {
std::cout << "[TELEPORT] Player reached teleport zone '" << targetTeleportZone->id << "'" << std::endl;
if (onTeleport) onTeleport(targetTeleportZone->destinationLocation, targetTeleportZone->destinationPosition, targetTeleportZone->destinationRotationY);
@@ -1551,7 +1557,7 @@ namespace ZL
for (auto& tz : teleportZones) {
if (!tz.active || !tz.automatic) continue;
const float autoR = tz.automaticRadius > 0.0f ? tz.automaticRadius : tz.radius;
const float dist = (player->position - tz.position).norm();
const float dist = (player->state.position - tz.position).norm();
if (!tz.automaticInitialized) {
// If the player starts inside the zone (e.g. just arrived here via teleport),
@@ -1582,7 +1588,7 @@ namespace ZL
return;
}
player->attackTarget = nullptr;
player->state.attackTargetIndex = CharacterState::kNoTarget;
// Unproject click to ground plane (y=0) for Viola's walk target
float ndcX = 2.0f * eventX / Environment::width - 1.0f;
@@ -1596,7 +1602,7 @@ namespace ZL
Eigen::Vector3f camRight(cosAzim, 0.f, sinAzim);
Eigen::Vector3f camForward(sinAzim * cosIncl, -sinIncl, -cosAzim * cosIncl);
Eigen::Vector3f camUp(sinAzim * sinIncl, cosIncl, -cosAzim * sinIncl);
const Eigen::Vector3f& playerPos = player ? player->position : Eigen::Vector3f::Zero();
const Eigen::Vector3f& playerPos = player ? player->state.position : Eigen::Vector3f::Zero();
Eigen::Vector3f camPos = playerPos + Eigen::Vector3f(-sinAzim * cosIncl, sinIncl, cosAzim * cosIncl) * Environment::zoom + Eigen::Vector3f(0,1,0);
Eigen::Vector3f rayDir = (camForward + camRight * (ndcX * aspect * tanHalfFov) + camUp * (ndcY * tanHalfFov)).normalized();
@@ -1630,17 +1636,16 @@ namespace ZL
std::cout << "[CLICK] *** SUCCESS: Clicked on interactive object: " << clickedObject->loadedObject.name << " ***" << std::endl;
std::cout << "[CLICK] Object position: (" << clickedObject->position.x() << ", "
<< clickedObject->position.y() << ", " << clickedObject->position.z() << ")" << std::endl;
std::cout << "[CLICK] Player position: (" << player->position.x() << ", "
<< player->position.y() << ", " << player->position.z() << ")" << std::endl;
std::cout << "[CLICK] Player position: (" << player->state.position.x() << ", "
<< player->state.position.y() << ", " << player->state.position.z() << ")" << std::endl;
targetInteractiveObject = clickedObject;
targetInteractNpc = nullptr;
targetInteractNpcIndex = -1;
targetTeleportZone = nullptr;
player->setTarget(clickedObject->hasInteractionPosition
? clickedObject->interactionPosition
: clickedObject->position);
player->attackTarget = nullptr;
player->state.attackTargetIndex = CharacterState::kNoTarget;
std::cout << "[CLICK] Player moving to object..." << std::endl;
}
else {
@@ -1648,7 +1653,7 @@ namespace ZL
// Check if we clicked on an NPC
Character* clickedNpc = raycastNpcs(camPos, rayDir);
if (clickedNpc && player) {
float distance = (player->position - clickedNpc->position).norm();
float distance = (player->state.position - clickedNpc->state.position).norm();
int npcIndex = -1;
for (size_t i = 0; i < npcs.size(); ++i) {
if (npcs[i].get() == clickedNpc) {
@@ -1660,36 +1665,32 @@ namespace ZL
targetInteractiveObject = nullptr;
targetTeleportZone = nullptr;
if (clickedNpc->canAttack) {
// Hostile NPC: combat logic walks the player in via attackTarget;
// don't queue a Lua interaction during a fight.
player->attackTarget = clickedNpc;
targetInteractNpc = nullptr;
if (clickedNpc->state.canAttack) {
// Hostile NPC: combat logic walks the player in via attackTargetIndex.
player->state.attackTargetIndex = npcIndex;
targetInteractNpcIndex = -1;
if (distance <= clickedNpc->interactionRadius) {
if (distance <= clickedNpc->state.interactionRadius) {
std::cout << "[CLICK] Hostile NPC " << npcIndex << " in range; firing on_npc_interact" << std::endl;
scriptEngine.callNpcInteractCallback(npcIndex);
}
}
else {
// Peaceful NPC: walk to them, fire on_npc_interact when within talk distance.
player->attackTarget = nullptr;
player->state.attackTargetIndex = CharacterState::kNoTarget;
if (distance <= NPC_TALK_DISTANCE) {
// Already in talk range — fire immediately, stop, and face the player.
std::cout << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " (in range) ***" << std::endl;
player->setTarget(player->position);
clickedNpc->faceTarget = player.get();
player->setTarget(player->state.position);
npcs[npcIndex]->state.faceTargetIndex = CharacterState::kPlayerIndex;
scriptEngine.callNpcInteractCallback(npcIndex);
targetInteractNpc = nullptr;
targetInteractNpcIndex = -1;
}
else {
std::cout << "[CLICK] NPC " << npcIndex << " out of talk range (distance " << distance
<< " > " << NPC_TALK_DISTANCE << "); walking to NPC..." << std::endl;
player->setTarget(clickedNpc->position);
targetInteractNpc = clickedNpc;
player->setTarget(clickedNpc->state.position);
targetInteractNpcIndex = npcIndex;
}
}
@@ -1713,14 +1714,12 @@ namespace ZL
std::cout << "[CLICK] Clicked teleport zone '" << clickedTeleport->id << "'" << std::endl;
targetTeleportZone = clickedTeleport;
targetInteractiveObject = nullptr;
targetInteractNpc = nullptr;
targetInteractNpcIndex = -1;
player->setTarget(clickedTeleport->position);
player->attackTarget = nullptr;
player->state.attackTargetIndex = CharacterState::kNoTarget;
} else {
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
player->attackTarget = nullptr;
targetInteractNpc = nullptr;
player->state.attackTargetIndex = CharacterState::kNoTarget;
targetInteractNpcIndex = -1;
targetTeleportZone = nullptr;
if (onPlayerFloorWalk) onPlayerFloorWalk();