[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.
remotes/1728160796594174844/tmp_0.7.0-alpha
David Cernat 6 years ago
parent db39c62e89
commit 995d20348f

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

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

@ -220,7 +220,7 @@ namespace MWMechanics
Ignore projectiles fired by DedicatedPlayers and DedicatedActors
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))
return;
@ -228,7 +228,10 @@ namespace MWMechanics
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);
if (localAttack)
{
localAttack->type = mwmp::Attack::RANGED;
localAttack->isHit = true;
}
/*
End of tes3mp addition
*/
@ -329,7 +332,7 @@ namespace MWMechanics
appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition, true);
if (localAttack)
localAttack->applyProjectileEnchantment = appliedEnchantment;
localAttack->applyAmmoEnchantment = appliedEnchantment;
}
/*
End of tes3mp change (minor)

@ -203,11 +203,12 @@ void MechanicsHelper::assignAttackTarget(Attack* attack, const MWWorld::Ptr& tar
void MechanicsHelper::resetAttack(Attack* attack)
{
attack->isHit = false;
attack->success = false;
attack->knockdown = false;
attack->block = false;
attack->applyWeaponEnchantment = false;
attack->applyProjectileEnchantment = false;
attack->applyAmmoEnchantment = false;
attack->hitPosition.pos[0] = attack->hitPosition.pos[1] = attack->hitPosition.pos[2] = 0;
attack->target.guid = RakNet::RakNetGUID();
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();
}
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;
MWWorld::Ptr weapon;
MWWorld::Ptr projectile;
MWWorld::Ptr weaponPtr;
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))
{
MWWorld::InventoryStore &inventoryStore = attacker.getClass().getInventoryStore(attacker);
MWWorld::ContainerStoreIterator weaponSlot = inventoryStore.getSlot(
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)
{
// TODO: Fix for when arrows, bolts and throwing weapons have just run out
MWWorld::ContainerStoreIterator projectileSlot = inventoryStore.getSlot(
MWWorld::InventoryStore::Slot_Ammunition);
projectile = projectileSlot != inventoryStore.end() ? *projectileSlot : MWWorld::Ptr();
if (!weaponPtr || !Misc::StringUtils::ciEqual(weaponPtr.getCellRef().getRefId(), attack.rangedWeaponId))
{
weaponPtr = inventoryStore.search(attack.rangedWeaponId);
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())
weapon = MWWorld::Ptr();
if (!weaponPtr.isEmpty() && weaponPtr.getTypeName() != typeid(ESM::Weapon).name())
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",
weapon.getCellRef().getRefId().c_str(), isRanged ? "true" : "false", attack.applyWeaponEnchantment ? "true" : "false",
attack.applyProjectileEnchantment ? "true" : "false");
LOG_APPEND(Log::LOG_VERBOSE, "- weapon: %s\n- isRanged: %s\n- applyWeaponEnchantment: %s\n- applyAmmoEnchantment: %s",
weaponPtr.getCellRef().getRefId().c_str(), isRanged ? "true" : "false", attack.applyWeaponEnchantment ? "true" : "false",
attack.applyAmmoEnchantment ? "true" : "false");
if (attack.applyWeaponEnchantment)
{
MWMechanics::CastSpell cast(attacker, victim, isRanged);
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);
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;
if (weapon.isEmpty())
if (weaponPtr.isEmpty())
{
if (attacker.getClass().isBipedal(attacker))
{
@ -318,11 +351,23 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
}
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);
}
// 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)
{

@ -102,26 +102,37 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
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
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
from their latest attack packet
from their latest attack packet.
*/
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);
if (localAttack)
{
localAttack->attackStrength = attackStrength;
localAttack->rangedWeaponId = weapon->getCellRef().getRefId();
localAttack->shouldSend = true;
}
else
{
mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor);
if (dedicatedAttack)
attackStrength = dedicatedAttack->attackStrength;
}
@ -129,13 +140,6 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
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.
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));
@ -147,6 +151,17 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
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
osg::Node* weaponNode = getWeaponNode();
if (!weaponNode)
@ -176,6 +191,17 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
if (!mAmmunition)
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::NodePathList nodepaths = ammoNode->getParentalNodePaths();
if (nodepaths.empty())

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

@ -40,19 +40,25 @@ void PacketActorAttack::Actor(BaseActor &actor, bool send)
}
else
{
RW(actor.attack.damage, send);
RW(actor.attack.block, send);
RW(actor.attack.knockdown, send);
RW(actor.attack.applyWeaponEnchantment, send);
RW(actor.attack.isHit, send);
if (actor.attack.type == mwmp::Attack::RANGED)
{
RW(actor.attack.applyProjectileEnchantment, 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[1], send);
RW(actor.attack.hitPosition.pos[2], send);

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

Loading…
Cancel
Save