rendering/game: make player model semi-transparent in ghost form

Add GhostStateCallback to GameHandler, fired when PLAYER_FLAGS_GHOST
transitions on or off in UPDATE_OBJECT / login detection. Add
setInstanceOpacity() to CharacterRenderer to directly set opacity
without disturbing fade-in state. Application wires the callback to
set opacity 0.5 on ghost entry and 1.0 on resurrect.
This commit is contained in:
Kelsi 2026-03-10 09:57:24 -07:00
parent 366321042f
commit c8d9d6b792
5 changed files with 28 additions and 0 deletions

View file

@ -2794,6 +2794,16 @@ void Application::setupUICallbacks() {
}
});
// Ghost state callback — make player semi-transparent when in spirit form
gameHandler->setGhostStateCallback([this](bool isGhost) {
if (!renderer) return;
auto* cr = renderer->getCharacterRenderer();
if (!cr) return;
uint32_t charInstId = renderer->getCharacterInstanceId();
if (charInstId == 0) return;
cr->setInstanceOpacity(charInstId, isGhost ? 0.5f : 1.0f);
});
// Stand state animation callback — map server stand state to M2 animation on player
// and sync camera sit flag so movement is blocked while sitting
gameHandler->setStandStateCallback([this](uint8_t standState) {

View file

@ -7749,6 +7749,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
releasedSpirit_ = true;
playerDead_ = true;
LOG_INFO("Player logged in as ghost (PLAYER_FLAGS)");
if (ghostStateCallback_) ghostStateCallback_(true);
}
}
// Determine hostility from faction template for online creatures
@ -8212,12 +8213,14 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
if (!wasGhost && nowGhost) {
releasedSpirit_ = true;
LOG_INFO("Player entered ghost form (PLAYER_FLAGS)");
if (ghostStateCallback_) ghostStateCallback_(true);
} else if (wasGhost && !nowGhost) {
releasedSpirit_ = false;
playerDead_ = false;
repopPending_ = false;
resurrectPending_ = false;
LOG_INFO("Player resurrected (PLAYER_FLAGS ghost cleared)");
if (ghostStateCallback_) ghostStateCallback_(false);
}
}
}

View file

@ -2876,6 +2876,15 @@ void CharacterRenderer::startFadeIn(uint32_t instanceId, float durationSeconds)
it->second.fadeInDuration = durationSeconds;
}
void CharacterRenderer::setInstanceOpacity(uint32_t instanceId, float opacity) {
auto it = instances.find(instanceId);
if (it != instances.end()) {
it->second.opacity = std::clamp(opacity, 0.0f, 1.0f);
// Cancel any fade-in in progress to avoid overwriting the new opacity
it->second.fadeInDuration = 0.0f;
}
}
void CharacterRenderer::setActiveGeosets(uint32_t instanceId, const std::unordered_set<uint16_t>& geosets) {
auto it = instances.find(instanceId);
if (it != instances.end()) {