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:
parent
db39c62e89
commit
995d20348f
8 changed files with 163 additions and 71 deletions
|
@ -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,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())
|
||||
|
|
|
@ -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);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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…
Reference in a new issue