1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-29 17:15:32 +00:00

[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.
This commit is contained in:
David Cernat 2018-09-23 02:30:31 +03:00
parent db39c62e89
commit 995d20348f
8 changed files with 163 additions and 71 deletions

View file

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

View file

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

View file

@ -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)

View file

@ -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)
{

View file

@ -102,33 +102,6 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
{
/*
Start of tes3mp addition
If this is an attack by a LocalPlayer or LocalActor, record its attackStrength and
prepare an attack packet for sending
If it's an attack by a DedicatedPlayer or DedicatedActor, apply the attackStrength
from their latest attack packet
*/
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);
if (localAttack)
{
localAttack->attackStrength = attackStrength;
localAttack->shouldSend = true;
}
else
{
mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor);
if (dedicatedAttack)
attackStrength = dedicatedAttack->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())
@ -136,6 +109,37 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
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
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.
*/
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;
}
/*
End of tes3mp addition
*/
// 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())

View file

@ -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;
};

View file

@ -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);
if (actor.attack.type == mwmp::Attack::RANGED)
{
RW(actor.attack.applyProjectileEnchantment, send);
RW(actor.attack.attackStrength, send);
}
RW(actor.attack.isHit, send);
if (actor.attack.success || actor.attack.applyWeaponEnchantment || actor.attack.applyProjectileEnchantment)
if (actor.attack.type == mwmp::Attack::RANGED)
RW(actor.attack.attackStrength, send);
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);

View file

@ -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);