[General] Always use correct ranged weapon & ammo for ranged attack sync

Previously, the player's currently selected weapon was being used in ranged attacks as in the original melee-oriented attack sync, which meant that shooting one type of projectile and then equipping another while the old projectile was still in the air turned the old projectile into the new projectile upon impact.

Additionally, avoid running most of the code in MechanicsHelper::assignAttackTarget() for non-hitting melee and ranged attacks.
pull/475/head
David Cernat 6 years ago
parent db39c62e89
commit 995d20348f

@ -307,6 +307,7 @@ namespace MWClass
if (localAttack) if (localAttack)
{ {
localAttack->isHit = true;
localAttack->success = true; localAttack->success = true;
localAttack->hitPosition = MechanicsHelper::getPositionFromVector(hitPosition); localAttack->hitPosition = MechanicsHelper::getPositionFromVector(hitPosition);
MechanicsHelper::assignAttackTarget(localAttack, victim); MechanicsHelper::assignAttackTarget(localAttack, victim);

@ -635,6 +635,7 @@ namespace MWClass
if (localAttack) if (localAttack)
{ {
localAttack->isHit = true;
localAttack->success = true; localAttack->success = true;
localAttack->hitPosition = MechanicsHelper::getPositionFromVector(hitPosition); localAttack->hitPosition = MechanicsHelper::getPositionFromVector(hitPosition);
MechanicsHelper::assignAttackTarget(localAttack, victim); MechanicsHelper::assignAttackTarget(localAttack, victim);

@ -220,7 +220,7 @@ namespace MWMechanics
Ignore projectiles fired by DedicatedPlayers and DedicatedActors Ignore projectiles fired by DedicatedPlayers and DedicatedActors
If fired by LocalPlayers and LocalActors, get the associated LocalAttack and set its type If fired by LocalPlayers and LocalActors, get the associated LocalAttack and set its type
to RANGED to RANGED while also marking it as a hit
*/ */
if (mwmp::PlayerList::isDedicatedPlayer(attacker) || mwmp::Main::get().getCellController()->isDedicatedActor(attacker)) if (mwmp::PlayerList::isDedicatedPlayer(attacker) || mwmp::Main::get().getCellController()->isDedicatedActor(attacker))
return; return;
@ -228,7 +228,10 @@ namespace MWMechanics
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker); mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);
if (localAttack) if (localAttack)
{
localAttack->type = mwmp::Attack::RANGED; localAttack->type = mwmp::Attack::RANGED;
localAttack->isHit = true;
}
/* /*
End of tes3mp addition End of tes3mp addition
*/ */
@ -329,7 +332,7 @@ namespace MWMechanics
appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition, true); appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition, true);
if (localAttack) if (localAttack)
localAttack->applyProjectileEnchantment = appliedEnchantment; localAttack->applyAmmoEnchantment = appliedEnchantment;
} }
/* /*
End of tes3mp change (minor) End of tes3mp change (minor)

@ -203,11 +203,12 @@ void MechanicsHelper::assignAttackTarget(Attack* attack, const MWWorld::Ptr& tar
void MechanicsHelper::resetAttack(Attack* attack) void MechanicsHelper::resetAttack(Attack* attack)
{ {
attack->isHit = false;
attack->success = false; attack->success = false;
attack->knockdown = false; attack->knockdown = false;
attack->block = false; attack->block = false;
attack->applyWeaponEnchantment = false; attack->applyWeaponEnchantment = false;
attack->applyProjectileEnchantment = false; attack->applyAmmoEnchantment = false;
attack->hitPosition.pos[0] = attack->hitPosition.pos[1] = attack->hitPosition.pos[2] = 0; attack->hitPosition.pos[0] = attack->hitPosition.pos[1] = attack->hitPosition.pos[2] = 0;
attack->target.guid = RakNet::RakNetGUID(); attack->target.guid = RakNet::RakNetGUID();
attack->target.refId.clear(); attack->target.refId.clear();
@ -254,53 +255,85 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
victim = controller->getDedicatedActor(attack.target.refNum, attack.target.mpNum)->getPtr(); victim = controller->getDedicatedActor(attack.target.refNum, attack.target.mpNum)->getPtr();
} }
if (attack.type == attack.MELEE || attack.type == attack.RANGED) if (attack.isHit && (attack.type == attack.MELEE || attack.type == attack.RANGED))
{ {
bool isRanged = attack.type == attack.RANGED; bool isRanged = attack.type == attack.RANGED;
MWWorld::Ptr weapon; MWWorld::Ptr weaponPtr;
MWWorld::Ptr projectile; MWWorld::Ptr ammoPtr;
// Get the weapon used; if using hand-to-hand, the weapon is equal to inv.end() bool usedTempRangedWeapon = false;
bool usedTempRangedAmmo = false;
// Get the attacker's current weapon
//
// Note: if using hand-to-hand, the weapon is equal to inv.end()
if (attacker.getClass().hasInventoryStore(attacker)) if (attacker.getClass().hasInventoryStore(attacker))
{ {
MWWorld::InventoryStore &inventoryStore = attacker.getClass().getInventoryStore(attacker); MWWorld::InventoryStore &inventoryStore = attacker.getClass().getInventoryStore(attacker);
MWWorld::ContainerStoreIterator weaponSlot = inventoryStore.getSlot( MWWorld::ContainerStoreIterator weaponSlot = inventoryStore.getSlot(
MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::InventoryStore::Slot_CarriedRight);
weapon = weaponSlot != inventoryStore.end() ? *weaponSlot : MWWorld::Ptr(); weaponPtr = weaponSlot != inventoryStore.end() ? *weaponSlot : MWWorld::Ptr();
// Is the currently selected weapon different from the one recorded for this ranged attack?
// If so, try to find the correct one in the attacker's inventory and use it here. If it
// no longer exists, add it back temporarily.
if (isRanged) if (isRanged)
{ {
// TODO: Fix for when arrows, bolts and throwing weapons have just run out if (!weaponPtr || !Misc::StringUtils::ciEqual(weaponPtr.getCellRef().getRefId(), attack.rangedWeaponId))
MWWorld::ContainerStoreIterator projectileSlot = inventoryStore.getSlot( {
MWWorld::InventoryStore::Slot_Ammunition); weaponPtr = inventoryStore.search(attack.rangedWeaponId);
projectile = projectileSlot != inventoryStore.end() ? *projectileSlot : MWWorld::Ptr();
if (!weaponPtr)
{
weaponPtr = *attacker.getClass().getContainerStore(attacker).add(attack.rangedWeaponId, 1, attacker);
usedTempRangedWeapon = true;
}
}
if (!attack.rangedAmmoId.empty())
{
MWWorld::ContainerStoreIterator ammoSlot = inventoryStore.getSlot(
MWWorld::InventoryStore::Slot_Ammunition);
ammoPtr = ammoSlot != inventoryStore.end() ? *ammoSlot : MWWorld::Ptr();
if (!ammoPtr || !Misc::StringUtils::ciEqual(ammoPtr.getCellRef().getRefId(), attack.rangedAmmoId))
{
ammoPtr = inventoryStore.search(attack.rangedAmmoId);
if (!ammoPtr)
{
ammoPtr = *attacker.getClass().getContainerStore(attacker).add(attack.rangedAmmoId, 1, attacker);
usedTempRangedAmmo = true;
}
}
}
} }
if (!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) if (!weaponPtr.isEmpty() && weaponPtr.getTypeName() != typeid(ESM::Weapon).name())
weapon = MWWorld::Ptr(); weaponPtr = MWWorld::Ptr();
} }
if (!weapon.isEmpty()) if (!weaponPtr.isEmpty())
{ {
LOG_APPEND(Log::LOG_VERBOSE, "- weapon: %s\n- isRanged: %s\n- applyWeaponEnchantment: %s\n- applyProjectileEnchantment: %s", LOG_APPEND(Log::LOG_VERBOSE, "- weapon: %s\n- isRanged: %s\n- applyWeaponEnchantment: %s\n- applyAmmoEnchantment: %s",
weapon.getCellRef().getRefId().c_str(), isRanged ? "true" : "false", attack.applyWeaponEnchantment ? "true" : "false", weaponPtr.getCellRef().getRefId().c_str(), isRanged ? "true" : "false", attack.applyWeaponEnchantment ? "true" : "false",
attack.applyProjectileEnchantment ? "true" : "false"); attack.applyAmmoEnchantment ? "true" : "false");
if (attack.applyWeaponEnchantment) if (attack.applyWeaponEnchantment)
{ {
MWMechanics::CastSpell cast(attacker, victim, isRanged); MWMechanics::CastSpell cast(attacker, victim, isRanged);
cast.mHitPosition = attack.hitPosition.asVec3(); cast.mHitPosition = attack.hitPosition.asVec3();
cast.cast(weapon, false); cast.cast(weaponPtr, false);
} }
if (isRanged && !projectile.isEmpty() && attack.applyProjectileEnchantment) if (isRanged && !ammoPtr.isEmpty() && attack.applyAmmoEnchantment)
{ {
MWMechanics::CastSpell cast(attacker, victim, isRanged); MWMechanics::CastSpell cast(attacker, victim, isRanged);
cast.mHitPosition = attack.hitPosition.asVec3(); cast.mHitPosition = attack.hitPosition.asVec3();
cast.cast(projectile, false); cast.cast(ammoPtr, false);
} }
} }
@ -308,7 +341,7 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
{ {
bool isHealthDamage = true; bool isHealthDamage = true;
if (weapon.isEmpty()) if (weaponPtr.isEmpty())
{ {
if (attacker.getClass().isBipedal(attacker)) if (attacker.getClass().isBipedal(attacker))
{ {
@ -318,11 +351,23 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
} }
if (!isRanged) if (!isRanged)
MWMechanics::blockMeleeAttack(attacker, victim, weapon, attack.damage, 1); MWMechanics::blockMeleeAttack(attacker, victim, weaponPtr, attack.damage, 1);
victim.getClass().onHit(victim, attack.damage, isHealthDamage, weapon, attacker, attack.hitPosition.asVec3(), victim.getClass().onHit(victim, attack.damage, isHealthDamage, weaponPtr, attacker, attack.hitPosition.asVec3(),
attack.success); attack.success);
} }
// Remove temporary items that may have been added above for ranged attacks
if (isRanged && attacker.getClass().hasInventoryStore(attacker))
{
MWWorld::InventoryStore &inventoryStore = attacker.getClass().getInventoryStore(attacker);
if (usedTempRangedWeapon)
inventoryStore.remove(weaponPtr, 1, attacker);
if (usedTempRangedAmmo)
inventoryStore.remove(ammoPtr, 1, attacker);
}
} }
else if (attack.type == attack.MAGIC) else if (attack.type == attack.MAGIC)
{ {

@ -102,26 +102,37 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
{ {
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weapon == inv.end())
return;
if (weapon->getTypeName() != typeid(ESM::Weapon).name())
return;
/* /*
Start of tes3mp addition Start of tes3mp addition
If this is an attack by a LocalPlayer or LocalActor, record its attackStrength and If this is an attack by a LocalPlayer or LocalActor, record its attackStrength and
prepare an attack packet for sending rangedWeaponId and prepare an attack packet for sending.
Unlike melee attacks, ranged attacks require the weapon and ammo IDs to be recorded
because players and actors can have multiple projectiles in the air at the same time.
If it's an attack by a DedicatedPlayer or DedicatedActor, apply the attackStrength If it's an attack by a DedicatedPlayer or DedicatedActor, apply the attackStrength
from their latest attack packet from their latest attack packet.
*/ */
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor); mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);
if (localAttack) if (localAttack)
{ {
localAttack->attackStrength = attackStrength; localAttack->attackStrength = attackStrength;
localAttack->rangedWeaponId = weapon->getCellRef().getRefId();
localAttack->shouldSend = true; localAttack->shouldSend = true;
} }
else else
{ {
mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor); mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor);
if (dedicatedAttack) if (dedicatedAttack)
attackStrength = dedicatedAttack->attackStrength; attackStrength = dedicatedAttack->attackStrength;
} }
@ -129,13 +140,6 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
End of tes3mp addition End of tes3mp addition
*/ */
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weapon == inv.end())
return;
if (weapon->getTypeName() != typeid(ESM::Weapon).name())
return;
// The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise. // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
@ -147,6 +151,17 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
{ {
/*
Start of tes3mp addition
If this is a local attack, clear the rangedAmmoId used for it
*/
if (localAttack)
localAttack->rangedAmmoId = "";
/*
End of tes3mp addition
*/
// Thrown weapons get detached now // Thrown weapons get detached now
osg::Node* weaponNode = getWeaponNode(); osg::Node* weaponNode = getWeaponNode();
if (!weaponNode) if (!weaponNode)
@ -176,6 +191,17 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
if (!mAmmunition) if (!mAmmunition)
return; return;
/*
Start of tes3mp addition
If this is a local attack, record the rangedAmmoId used for it
*/
if (localAttack)
localAttack->rangedAmmoId = ammo->getCellRef().getRefId();
/*
End of tes3mp addition
*/
osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode(); osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode();
osg::NodePathList nodepaths = ammoNode->getParentalNodePaths(); osg::NodePathList nodepaths = ammoNode->getParentalNodePaths();
if (nodepaths.empty()) if (nodepaths.empty())

@ -78,11 +78,15 @@ namespace mwmp
std::string spellId; // id of spell (e.g. "fireball") std::string spellId; // id of spell (e.g. "fireball")
std::string itemId; std::string itemId;
std::string rangedWeaponId;
std::string rangedAmmoId;
ESM::Position hitPosition; ESM::Position hitPosition;
float damage; float damage;
float attackStrength; float attackStrength;
bool isHit;
bool success; bool success;
bool block; bool block;
@ -90,7 +94,7 @@ namespace mwmp
bool instant; bool instant;
bool knockdown; bool knockdown;
bool applyWeaponEnchantment; bool applyWeaponEnchantment;
bool applyProjectileEnchantment; bool applyAmmoEnchantment;
bool shouldSend; bool shouldSend;
}; };

@ -40,19 +40,25 @@ void PacketActorAttack::Actor(BaseActor &actor, bool send)
} }
else else
{ {
RW(actor.attack.damage, send); RW(actor.attack.isHit, send);
RW(actor.attack.block, send);
RW(actor.attack.knockdown, send);
RW(actor.attack.applyWeaponEnchantment, send);
if (actor.attack.type == mwmp::Attack::RANGED) if (actor.attack.type == mwmp::Attack::RANGED)
{
RW(actor.attack.applyProjectileEnchantment, send);
RW(actor.attack.attackStrength, send); RW(actor.attack.attackStrength, send);
}
if (actor.attack.success || actor.attack.applyWeaponEnchantment || actor.attack.applyProjectileEnchantment) if (actor.attack.isHit)
{ {
RW(actor.attack.damage, send);
RW(actor.attack.block, send);
RW(actor.attack.knockdown, send);
RW(actor.attack.applyWeaponEnchantment, send);
if (actor.attack.type == mwmp::Attack::RANGED)
{
RW(actor.attack.applyAmmoEnchantment, send);
RW(actor.attack.rangedWeaponId, send);
RW(actor.attack.rangedAmmoId, send);
}
RW(actor.attack.hitPosition.pos[0], send); RW(actor.attack.hitPosition.pos[0], send);
RW(actor.attack.hitPosition.pos[1], send); RW(actor.attack.hitPosition.pos[1], send);
RW(actor.attack.hitPosition.pos[2], send); RW(actor.attack.hitPosition.pos[2], send);

@ -41,19 +41,25 @@ void PacketPlayerAttack::Packet(RakNet::BitStream *bs, bool send)
} }
else else
{ {
RW(player->attack.damage, send); RW(player->attack.isHit, send);
RW(player->attack.block, send);
RW(player->attack.knockdown, send);
RW(player->attack.applyWeaponEnchantment, send);
if (player->attack.type == mwmp::Attack::RANGED) if (player->attack.type == mwmp::Attack::RANGED)
{
RW(player->attack.applyProjectileEnchantment, send);
RW(player->attack.attackStrength, send); RW(player->attack.attackStrength, send);
}
if (player->attack.success || player->attack.applyWeaponEnchantment || player->attack.applyProjectileEnchantment) if (player->attack.isHit)
{ {
RW(player->attack.damage, send);
RW(player->attack.block, send);
RW(player->attack.knockdown, send);
RW(player->attack.applyWeaponEnchantment, send);
if (player->attack.type == mwmp::Attack::RANGED)
{
RW(player->attack.applyAmmoEnchantment, send);
RW(player->attack.rangedWeaponId, send);
RW(player->attack.rangedAmmoId, send);
}
RW(player->attack.hitPosition.pos[0], send); RW(player->attack.hitPosition.pos[0], send);
RW(player->attack.hitPosition.pos[1], send); RW(player->attack.hitPosition.pos[1], send);
RW(player->attack.hitPosition.pos[2], send); RW(player->attack.hitPosition.pos[2], send);

Loading…
Cancel
Save