[General] Include death animations in ActorDeath packets

pull/556/head
David Cernat 5 years ago
parent ecf00af548
commit a383b7b612

@ -212,6 +212,11 @@ const char *ActorFunctions::GetActorKillerName(unsigned int index) noexcept
return readActorList->baseActors.at(index).killer.name.c_str();
}
unsigned int ActorFunctions::GetActorDeathState(unsigned int index) noexcept
{
return readActorList->baseActors.at(index).deathState;
}
bool ActorFunctions::DoesActorHavePosition(unsigned int index) noexcept
{
return readActorList->baseActors.at(index).hasPositionData;
@ -316,6 +321,16 @@ void ActorFunctions::SetActorSound(const char* sound) noexcept
tempActor.sound = sound;
}
void ActorFunctions::SetActorDeathState(unsigned int deathState) noexcept
{
tempActor.deathState = deathState;
}
void ActorFunctions::SetActorDeathInstant(bool isInstant) noexcept
{
tempActor.isInstantDeath = isInstant;
}
void ActorFunctions::SetActorAIAction(unsigned int action) noexcept
{
tempActor.aiAction = action;
@ -483,6 +498,25 @@ void ActorFunctions::SendActorSpeech(bool sendToOtherVisitors, bool skipAttached
}
}
void ActorFunctions::SendActorDeath(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept
{
mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_DEATH);
actorPacket->setActorList(&writeActorList);
if (!skipAttachedPlayer)
actorPacket->Send(writeActorList.guid);
if (sendToOtherVisitors)
{
Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);
if (serverCell != nullptr)
{
serverCell->sendToLoaded(actorPacket, &writeActorList);
}
}
}
void ActorFunctions::SendActorAI(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept
{
mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI);

@ -46,6 +46,7 @@
{"GetActorKillerRefNum", ActorFunctions::GetActorKillerRefNum},\
{"GetActorKillerMpNum", ActorFunctions::GetActorKillerMpNum},\
{"GetActorKillerName", ActorFunctions::GetActorKillerName},\
{"GetActorDeathState", ActorFunctions::GetActorDeathState},\
\
{"DoesActorHavePosition", ActorFunctions::DoesActorHavePosition},\
{"DoesActorHaveStatsDynamic", ActorFunctions::DoesActorHaveStatsDynamic},\
@ -71,6 +72,8 @@
{"SetActorFatigueCurrent", ActorFunctions::SetActorFatigueCurrent},\
{"SetActorFatigueModified", ActorFunctions::SetActorFatigueModified},\
\
{"SetActorDeathState", ActorFunctions::SetActorDeathState},\
{"SetActorDeathInstant", ActorFunctions::SetActorDeathInstant},\
{"SetActorSound", ActorFunctions::SetActorSound},\
\
{"SetActorAIAction", ActorFunctions::SetActorAIAction},\
@ -92,6 +95,7 @@
{"SendActorStatsDynamic", ActorFunctions::SendActorStatsDynamic},\
{"SendActorEquipment", ActorFunctions::SendActorEquipment},\
{"SendActorSpeech", ActorFunctions::SendActorSpeech},\
{"SendActorDeath", ActorFunctions::SendActorDeath},\
{"SendActorAI", ActorFunctions::SendActorAI},\
{"SendActorCellChange", ActorFunctions::SendActorCellChange},\
\
@ -402,6 +406,14 @@ public:
*/
static const char *GetActorKillerName(unsigned int index) noexcept;
/**
* \brief Get the deathState of the actor at a certain index in the read actor list.
*
* \param index The index of the actor.
* \return The deathState.
*/
static unsigned int GetActorDeathState(unsigned int index) noexcept;
/**
* \brief Check whether there is any positional data for the actor at a certain index in
* the read actor list.
@ -581,6 +593,23 @@ public:
*/
static void SetActorSound(const char* sound) noexcept;
/**
* \brief Set the deathState of the temporary actor stored on the server.
*
* \param deathState The deathState.
* \return void
*/
static void SetActorDeathState(unsigned int deathState) noexcept;
/**
* \brief Set whether the death of the temporary actor stored on the server should
* be instant or not.
*
* \param isInstant Whether the death should be instant.
* \return void
*/
static void SetActorDeathInstant(bool isInstant) noexcept;
/**
* \brief Set the AI action of the temporary actor stored on the server.
*
@ -742,6 +771,17 @@ public:
*/
static void SendActorSpeech(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;
/**
* \brief Send an ActorDeath packet.
*
* \param sendToOtherVisitors Whether this packet should be sent to cell visitors other
* than the player attached to the packet (false by default).
* \param skipAttachedPlayer Whether the packet should skip being sent to the player attached
* to the packet (false by default).
* \return void
*/
static void SendActorDeath(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;
/**
* \brief Send an ActorAI packet.
*

@ -813,11 +813,18 @@ void CharacterController::playRandomDeath(float startpoint)
Start tes3mp change (major)
If this is a DedicatedPlayer, use the deathState received from their PlayerDeath packet
If this is a DedicatedActor, use the deathState from their ActorDeath packet
*/
if (mwmp::PlayerList::isDedicatedPlayer(mPtr))
{
mDeathState = static_cast<CharacterState>(mwmp::PlayerList::getPlayer(mPtr)->deathState);
}
else if (mwmp::Main::get().getCellController()->hasQueuedDeathState(mPtr))
{
mDeathState = static_cast<CharacterState>(mwmp::Main::get().getCellController()->getQueuedDeathState(mPtr));
mwmp::Main::get().getCellController()->clearQueuedDeathState(mPtr);
}
else if(mHitState == CharState_SwimKnockDown && mAnimation->hasAnimation("swimdeathknockdown"))
/*
End of tes3mp change (major)
@ -849,12 +856,19 @@ void CharacterController::playRandomDeath(float startpoint)
/*
Start of tes3mp addition
Send a PlayerDeath packet with the decided-upon death animation
If this is the local player, send a PlayerDeath packet with the decided-upon
death animation
If this is a local actor, send an ActorDeath packet with the animation
*/
if (mPtr == getPlayer())
{
mwmp::Main::get().getLocalPlayer()->sendDeath(mDeathState);
}
else if (!mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() && mwmp::Main::get().getCellController()->isLocalActor(mPtr))
{
mwmp::Main::get().getCellController()->getLocalActor(mPtr)->sendDeath(mDeathState);
}
/*
End of tes3mp addition
*/

@ -203,14 +203,37 @@ void Cell::readStatsDynamic(ActorList& actorList)
// That way, if this actor is about to become a LocalActor, initial data about it
// received from the server still gets set
actor->setStatsDynamic();
}
}
}
}
// Actors loaded as dead from the server need special handling to skip their death animations
// and disable their collision
if (actor->creatureStats.mDynamic[0].mCurrent < 1)
{
actor->getPtr().getClass().getCreatureStats(actor->getPtr()).setDeathAnimationFinished(true);
MWBase::Environment::get().getWorld()->enableActorCollision(actor->getPtr(), false);
}
void Cell::readDeath(ActorList& actorList)
{
initializeDedicatedActors(actorList);
if (dedicatedActors.empty()) return;
for (const auto &baseActor : actorList.baseActors)
{
std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);
if (dedicatedActors.count(mapIndex) > 0)
{
DedicatedActor *actor = dedicatedActors[mapIndex];
actor->creatureStats.mDead = true;
actor->creatureStats.mDynamic[0].mCurrent = 0;
Main::get().getCellController()->setQueuedDeathState(actor->getPtr(), baseActor.deathState);
LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, "In Cell::readDeath, deathState is %i and isInstantDeath is %s",
baseActor.deathState,
baseActor.isInstantDeath ? "true" : "false");
if (baseActor.isInstantDeath)
{
actor->getPtr().getClass().getCreatureStats(actor->getPtr()).setDeathAnimationFinished(true);
MWBase::Environment::get().getWorld()->enableActorCollision(actor->getPtr(), false);
}
}
}
@ -419,11 +442,6 @@ void Cell::initializeLocalActor(const MWWorld::Ptr& ptr)
actor->cell = *store->getCell();
actor->setPtr(ptr);
// Note that this actor was already dead when we were given control over it,
// to avoid sending an ActorDeath packet
if (ptr.getClass().getCreatureStats(ptr).isDead())
actor->wasDead = true;
localActors[mapIndex] = actor;
Main::get().getCellController()->setLocalActorRecord(mapIndex, getDescription());

@ -22,6 +22,7 @@ namespace mwmp
void readAnimFlags(ActorList& actorList);
void readAnimPlay(ActorList& actorList);
void readStatsDynamic(ActorList& actorList);
void readDeath(ActorList& actorList);
void readEquipment(ActorList& actorList);
void readSpeech(ActorList& actorList);
void readAi(ActorList& actorList);

@ -17,6 +17,7 @@ using namespace mwmp;
std::map<std::string, mwmp::Cell *> CellController::cellsInitialized;
std::map<std::string, std::string> CellController::localActorsToCells;
std::map<std::string, std::string> CellController::dedicatedActorsToCells;
std::map<std::string, unsigned int> CellController::queuedDeathStates;
mwmp::CellController::CellController()
{
@ -134,6 +135,17 @@ void CellController::readStatsDynamic(ActorList& actorList)
cellsInitialized[mapIndex]->readStatsDynamic(actorList);
}
void CellController::readDeath(ActorList& actorList)
{
std::string mapIndex = actorList.cell.getDescription();
initializeCell(actorList.cell);
// If this now exists, send it the data
if (cellsInitialized.count(mapIndex) > 0)
cellsInitialized[mapIndex]->readDeath(actorList);
}
void CellController::readEquipment(ActorList& actorList)
{
std::string mapIndex = actorList.cell.getDescription();
@ -200,6 +212,34 @@ void CellController::readCellChange(ActorList& actorList)
cellsInitialized[mapIndex]->readCellChange(actorList);
}
bool CellController::hasQueuedDeathState(MWWorld::Ptr ptr)
{
std::string actorIndex = generateMapIndex(ptr);
return queuedDeathStates.count(actorIndex) > 0;
}
unsigned int CellController::getQueuedDeathState(MWWorld::Ptr ptr)
{
std::string actorIndex = generateMapIndex(ptr);
return queuedDeathStates[actorIndex];
}
void CellController::clearQueuedDeathState(MWWorld::Ptr ptr)
{
std::string actorIndex = generateMapIndex(ptr);
queuedDeathStates.erase(actorIndex);
}
void CellController::setQueuedDeathState(MWWorld::Ptr ptr, unsigned int deathState)
{
std::string actorIndex = generateMapIndex(ptr);
queuedDeathStates[actorIndex] = deathState;
}
void CellController::setLocalActorRecord(std::string actorIndex, std::string cellIndex)
{
localActorsToCells[actorIndex] = cellIndex;

@ -25,6 +25,7 @@ namespace mwmp
void readAnimFlags(mwmp::ActorList& actorList);
void readAnimPlay(mwmp::ActorList& actorList);
void readStatsDynamic(mwmp::ActorList& actorList);
void readDeath(mwmp::ActorList& actorList);
void readEquipment(mwmp::ActorList& actorList);
void readSpeech(mwmp::ActorList& actorList);
void readAi(mwmp::ActorList& actorList);
@ -32,6 +33,11 @@ namespace mwmp
void readCast(mwmp::ActorList& actorList);
void readCellChange(mwmp::ActorList& actorList);
bool hasQueuedDeathState(MWWorld::Ptr ptr);
unsigned int getQueuedDeathState(MWWorld::Ptr ptr);
void clearQueuedDeathState(MWWorld::Ptr ptr);
void setQueuedDeathState(MWWorld::Ptr ptr, unsigned int deathState);
void setLocalActorRecord(std::string actorIndex, std::string cellIndex);
void removeLocalActorRecord(std::string actorIndex);
@ -68,6 +74,7 @@ namespace mwmp
static std::map<std::string, mwmp::Cell *> cellsInitialized;
static std::map<std::string, std::string> localActorsToCells;
static std::map<std::string, std::string> dedicatedActorsToCells;
static std::map<std::string, unsigned int> queuedDeathStates;
};
}

@ -31,7 +31,6 @@ LocalActor::LocalActor()
wasForceJumping = false;
wasForceMoveJumping = false;
wasFlying = false;
wasDead = false;
attack.type = Attack::MELEE;
attack.shouldSend = false;
@ -194,21 +193,6 @@ void LocalActor::updateStatsDynamic(bool forceUpdate)
creatureStats.mDead = ptrCreatureStats->isDead();
mwmp::Main::get().getNetworking()->getActorList()->addStatsDynamicActor(*this);
if (creatureStats.mDead && !wasDead)
{
if (MechanicsHelper::isEmptyTarget(killer))
killer = MechanicsHelper::getTarget(ptr);
LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, "Sending ID_ACTOR_DEATH about %s %i-%i in cell %s to server",
refId.c_str(), refNum, mpNum, cell.getDescription().c_str());
mwmp::Main::get().getNetworking()->getActorList()->addDeathActor(*this);
MechanicsHelper::clearTarget(killer);
}
wasDead = creatureStats.mDead;
}
}
@ -270,6 +254,25 @@ void LocalActor::updateAttackOrCast()
}
}
void LocalActor::sendDeath(char newDeathState)
{
deathState = newDeathState;
if (MechanicsHelper::isEmptyTarget(killer))
killer = MechanicsHelper::getTarget(ptr);
LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, "Sending ID_ACTOR_DEATH about %s %i-%i in cell %s to server\n- deathState: %d",
refId.c_str(), refNum, mpNum, cell.getDescription().c_str(), deathState);
ActorList actorList;
actorList.cell = cell;
actorList.addActor(*this);
Main::get().getNetworking()->getActorPacket(ID_ACTOR_DEATH)->setActorList(&actorList);
Main::get().getNetworking()->getActorPacket(ID_ACTOR_DEATH)->Send();
MechanicsHelper::clearTarget(killer);
}
MWWorld::Ptr LocalActor::getPtr()
{
return ptr;

@ -25,11 +25,12 @@ namespace mwmp
void updateEquipment(bool forceUpdate);
void updateAttackOrCast();
void sendDeath(char newDeathState);
MWWorld::Ptr getPtr();
void setPtr(const MWWorld::Ptr& newPtr);
bool hasSentData;
bool wasDead;
private:
MWWorld::Ptr ptr;

@ -17,7 +17,7 @@ namespace mwmp
virtual void Do(ActorPacket &packet, ActorList &actorList)
{
//Main::get().getCellController()->readDeath(actorList);
Main::get().getCellController()->readDeath(actorList);
}
};
}

@ -37,6 +37,8 @@ namespace mwmp
SimpleCreatureStats creatureStats;
Animation animation;
char deathState;
bool isInstantDeath = false;
Attack attack;
Cast cast;

@ -13,6 +13,8 @@ void PacketActorDeath::Actor(BaseActor &actor, bool send)
{
RW(actor.refId, send);
RW(actor.deathState, send);
RW(actor.isInstantDeath, send);
RW(actor.killer.isPlayer, send);
if (actor.killer.isPlayer)

Loading…
Cancel
Save