mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-22 03:09:39 +00:00
Implement weapon condition damage for ranged weapons (Fixes #1746)
This commit is contained in:
parent
eb1888a540
commit
216ebac2e9
5 changed files with 76 additions and 100 deletions
|
@ -255,23 +255,7 @@ namespace MWClass
|
||||||
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
||||||
{
|
{
|
||||||
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
|
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
|
||||||
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
// Weapon health is reduced by 1 even if the attack misses
|
|
||||||
const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
|
|
||||||
if(weaphashealth)
|
|
||||||
{
|
|
||||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
|
||||||
|
|
||||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
|
||||||
{
|
|
||||||
weaphealth -= std::min(1, weaphealth);
|
|
||||||
weapon.getCellRef().setCharge(weaphealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weapon broken? unequip it
|
|
||||||
if (weapon.getCellRef().getCharge() == 0)
|
|
||||||
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +282,6 @@ namespace MWClass
|
||||||
|
|
||||||
if (!weapon.isEmpty())
|
if (!weapon.isEmpty())
|
||||||
{
|
{
|
||||||
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
|
|
||||||
const unsigned char *attack = NULL;
|
const unsigned char *attack = NULL;
|
||||||
if(type == ESM::Weapon::AT_Chop)
|
if(type == ESM::Weapon::AT_Chop)
|
||||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||||
|
@ -309,26 +292,10 @@ namespace MWClass
|
||||||
if(attack)
|
if(attack)
|
||||||
{
|
{
|
||||||
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
||||||
damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
|
damage *= gmst.find("fDamageStrengthBase")->getFloat() +
|
||||||
if(weaphashealth)
|
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.find("fDamageStrengthMult")->getFloat() * 0.1);
|
||||||
{
|
MWMechanics::adjustWeaponDamage(damage, weapon);
|
||||||
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
|
||||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
|
||||||
damage *= float(weaphealth) / weapmaxhealth;
|
|
||||||
|
|
||||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
|
||||||
{
|
|
||||||
// Reduce weapon charge by at least one, but cap at 0
|
|
||||||
weaphealth -= std::min(std::max(1,
|
|
||||||
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
|
||||||
|
|
||||||
weapon.getCellRef().setCharge(weaphealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weapon broken? unequip it
|
|
||||||
if (weapon.getCellRef().getCharge() == 0)
|
|
||||||
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply "On hit" enchanted weapons
|
// Apply "On hit" enchanted weapons
|
||||||
|
|
|
@ -529,24 +529,7 @@ namespace MWClass
|
||||||
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
||||||
{
|
{
|
||||||
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
|
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
|
||||||
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
// Weapon health is reduced by 1 even if the attack misses
|
|
||||||
const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
|
|
||||||
if(weaphashealth)
|
|
||||||
{
|
|
||||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
|
||||||
|
|
||||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
|
||||||
{
|
|
||||||
weaphealth -= std::min(1, weaphealth);
|
|
||||||
weapon.getCellRef().setCharge(weaphealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weapon broken? unequip it
|
|
||||||
if (weaphealth == 0)
|
|
||||||
weapon = *inv.unequipItem(weapon, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,7 +538,6 @@ namespace MWClass
|
||||||
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
||||||
if(!weapon.isEmpty())
|
if(!weapon.isEmpty())
|
||||||
{
|
{
|
||||||
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
|
|
||||||
const unsigned char *attack = NULL;
|
const unsigned char *attack = NULL;
|
||||||
if(type == ESM::Weapon::AT_Chop)
|
if(type == ESM::Weapon::AT_Chop)
|
||||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||||
|
@ -568,27 +550,9 @@ namespace MWClass
|
||||||
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
||||||
damage *= gmst.fDamageStrengthBase->getFloat() +
|
damage *= gmst.fDamageStrengthBase->getFloat() +
|
||||||
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
|
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
|
||||||
if(weaphashealth)
|
|
||||||
{
|
|
||||||
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
|
||||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
|
||||||
|
|
||||||
damage *= float(weaphealth) / weapmaxhealth;
|
|
||||||
|
|
||||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
|
||||||
{
|
|
||||||
// Reduce weapon charge by at least one, but cap at 0
|
|
||||||
weaphealth -= std::min(std::max(1,
|
|
||||||
(int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
|
||||||
|
|
||||||
weapon.getCellRef().setCharge(weaphealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weapon broken? unequip it
|
|
||||||
if (weaphealth == 0)
|
|
||||||
weapon = *inv.unequipItem(weapon, ptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
MWMechanics::adjustWeaponDamage(damage, weapon);
|
||||||
|
MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
|
||||||
healthdmg = true;
|
healthdmg = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -169,12 +169,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
|
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
|
||||||
|
|
||||||
const MWWorld::Class &othercls = victim.getClass();
|
if(victim.isEmpty() || !victim.getClass().isActor() || victim.getClass().getCreatureStats(victim).isDead())
|
||||||
if(!othercls.isActor()) // Can't hit non-actors
|
// Can't hit non-actors or dead actors
|
||||||
return;
|
{
|
||||||
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
|
reduceWeaponCondition(0.f, false, weapon, attacker);
|
||||||
if(otherstats.isDead()) // Can't hit dead actors
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(attacker.getRefData().getHandle() == "player")
|
if(attacker.getRefData().getHandle() == "player")
|
||||||
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
||||||
|
@ -189,6 +189,7 @@ namespace MWMechanics
|
||||||
if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
|
if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
|
||||||
{
|
{
|
||||||
victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
|
victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
|
||||||
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +210,8 @@ namespace MWMechanics
|
||||||
damage *= fDamageStrengthBase +
|
damage *= fDamageStrengthBase +
|
||||||
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
|
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
|
||||||
|
|
||||||
|
adjustWeaponDamage(damage, weapon);
|
||||||
|
reduceWeaponCondition(damage, true, weapon, attacker);
|
||||||
|
|
||||||
if(attacker.getRefData().getHandle() == "player")
|
if(attacker.getRefData().getHandle() == "player")
|
||||||
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);
|
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);
|
||||||
|
@ -295,4 +298,42 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reduceWeaponCondition(float damage, bool hit, MWWorld::Ptr &weapon, const MWWorld::Ptr &attacker)
|
||||||
|
{
|
||||||
|
if (weapon.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!hit)
|
||||||
|
damage = 0.f;
|
||||||
|
|
||||||
|
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
|
||||||
|
if(weaphashealth)
|
||||||
|
{
|
||||||
|
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
||||||
|
|
||||||
|
const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWeaponDamageMult")->getFloat();
|
||||||
|
float x = std::max(1.f, fWeaponDamageMult * damage);
|
||||||
|
|
||||||
|
weaphealth -= std::min(int(x), weaphealth);
|
||||||
|
weapon.getCellRef().setCharge(weaphealth);
|
||||||
|
|
||||||
|
// Weapon broken? unequip it
|
||||||
|
if (weaphealth == 0)
|
||||||
|
weapon = *attacker.getClass().getInventoryStore(attacker).unequipItem(weapon, attacker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void adjustWeaponDamage(float &damage, const MWWorld::Ptr &weapon)
|
||||||
|
{
|
||||||
|
if (weapon.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
|
||||||
|
if(weaphashealth)
|
||||||
|
{
|
||||||
|
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
||||||
|
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
||||||
|
damage *= (float(weaphealth) / weapmaxhealth);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker
|
||||||
void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage);
|
void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage);
|
||||||
|
|
||||||
/// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt
|
/// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt
|
||||||
|
/// @note \a victim may be empty (e.g. for a hit on terrain), a non-actor (environment objects) or an actor
|
||||||
void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,
|
void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,
|
||||||
const Ogre::Vector3& hitPosition);
|
const Ogre::Vector3& hitPosition);
|
||||||
|
|
||||||
|
@ -22,6 +23,15 @@ float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, in
|
||||||
/// Applies damage to attacker based on the victim's elemental shields.
|
/// Applies damage to attacker based on the victim's elemental shields.
|
||||||
void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
|
void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
|
||||||
|
|
||||||
|
/// @param damage Unmitigated weapon damage of the attack
|
||||||
|
/// @param hit Was the attack successful?
|
||||||
|
/// @param weapon The weapon used.
|
||||||
|
/// @note if the weapon is unequipped as result of condition damage, a new Ptr will be assigned to \a weapon.
|
||||||
|
void reduceWeaponCondition (float damage, bool hit, MWWorld::Ptr& weapon, const MWWorld::Ptr& attacker);
|
||||||
|
|
||||||
|
/// Adjust weapon damage based on its condition. A used weapon will be less effective.
|
||||||
|
void adjustWeaponDamage (float& damage, const MWWorld::Ptr& weapon);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -247,12 +247,6 @@ namespace MWWorld
|
||||||
if (obstacle == caster)
|
if (obstacle == caster)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (obstacle.isEmpty())
|
|
||||||
{
|
|
||||||
// Terrain
|
|
||||||
}
|
|
||||||
else if (obstacle.getClass().isActor())
|
|
||||||
{
|
|
||||||
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId);
|
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId);
|
||||||
|
|
||||||
// Try to get a Ptr to the bow that was used. It might no longer exist.
|
// Try to get a Ptr to the bow that was used. It might no longer exist.
|
||||||
|
@ -269,7 +263,7 @@ namespace MWWorld
|
||||||
caster = obstacle;
|
caster = obstacle;
|
||||||
|
|
||||||
MWMechanics::projectileHit(caster, obstacle, bow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first);
|
MWMechanics::projectileHit(caster, obstacle, bow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first);
|
||||||
}
|
|
||||||
hit = true;
|
hit = true;
|
||||||
}
|
}
|
||||||
if (hit)
|
if (hit)
|
||||||
|
|
Loading…
Reference in a new issue