Handle hit chance and damage calculation

Math is based on what the UESP describes, with some tweaks (using
fatigue term, and the fCombatCriticalStrikeMult GMST):
http://www.uesp.net/wiki/Morrowind:Combat
actorid
Chris Robinson 12 years ago
parent 1dcc893703
commit b3a057d679

@ -14,6 +14,7 @@
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
@ -304,6 +305,12 @@ namespace MWClass
void Npc::hit(const MWWorld::Ptr& ptr, int type) const
{
// Get the weapon used (if hand-to-hand, weapon = inv.end())
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end() && weapon->getTypeName() != typeid(ESM::Weapon).name())
weapon = inv.end();
// FIXME: Detect what was hit
MWWorld::Ptr victim;
if(victim.isEmpty()) // Didn't hit anything
@ -316,34 +323,58 @@ namespace MWClass
return;
}
// Get the weapon used
MWWorld::LiveCellRef<ESM::Weapon> *weapon = NULL;
MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(iter != inv.end() && iter->getTypeName() == typeid(ESM::Weapon).name())
weapon = iter->get<ESM::Weapon>();
int weapskill = ESM::Skill::HandToHand;
if(weapon != inv.end())
weapskill = MWWorld::Class::get(*weapon).getEquipmentSkill(*weapon);
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
MWMechanics::NpcStats &npcstats = getNpcStats(ptr);
const MWMechanics::MagicEffects &mageffects = crstats.getMagicEffects();
float hitchance = npcstats.getSkill(weapskill).getModified() +
(crstats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(crstats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= crstats.getFatigueTerm();
hitchance += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyAttack)).mMagnitude -
mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
hitchance -= othercls.getEvasion(victim);
if((::rand()/(RAND_MAX+1.0)) > hitchance)
{
// Missed
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(victim, "miss", 1.0f, 1.0f);
return;
}
// TODO: Check weapon skill against victim's armor skill (if !weapon, attacker is using
// hand-to-hand, which damages fatique unless in werewolf form).
if(weapon)
if(weapon != inv.end())
{
const unsigned char *att = NULL;
const unsigned char *attack = NULL;
if(type == MWMechanics::CreatureStats::AT_Chop)
att = weapon->mBase->mData.mChop;
attack = weapon->get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == MWMechanics::CreatureStats::AT_Slash)
att = weapon->mBase->mData.mSlash;
attack = weapon->get<ESM::Weapon>()->mBase->mData.mSlash;
else if(type == MWMechanics::CreatureStats::AT_Thrust)
att = weapon->mBase->mData.mThrust;
if(att)
attack = weapon->get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack)
{
float health = othercls.getCreatureStats(victim).getHealth().getCurrent();
// FIXME: Modify damage based on strength attribute?
health -= att[0] + ((att[1]-att[0])*Npc::getNpcStats(ptr).getAttackStrength());
float damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength());
damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
//damage *= weapon_current_health / weapon_max_health;
if(!othercls.hasDetected(victim, ptr))
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
}
damage /= std::min(1.0f + othercls.getArmorRating(victim) / std::max(1.0f, damage), 4.0f);
othercls.setActorHealth(victim, std::max(health, 0.0f), ptr);
float health = othercls.getCreatureStats(victim).getHealth().getCurrent() - damage;
othercls.setActorHealth(victim, health, ptr);
}
}
skillUsageSucceeded(ptr, weapskill, 0);
}
void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const

@ -13,6 +13,8 @@
#include "containerstore.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp"
namespace MWWorld
{
@ -77,6 +79,18 @@ namespace MWWorld
throw std::runtime_error ("class does not have item health");
}
float Class::getEvasion(const Ptr& ptr) const
{
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
const MWMechanics::MagicEffects &mageffects = crstats.getMagicEffects();
float evasion = (crstats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(crstats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
evasion *= crstats.getFatigueTerm();
evasion += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Sanctuary)).mMagnitude;
return evasion;
}
void Class::hit(const Ptr& ptr, int type) const
{
throw std::runtime_error("class cannot hit");

@ -105,6 +105,9 @@ namespace MWWorld
///< Return item max health or throw an exception, if class does not have item health
/// (default implementation: throw an exceoption)
virtual float getEvasion(const Ptr& ptr) const;
///< Gets the chance the given object can evade an attack
virtual void hit(const Ptr& ptr, int type=-1) const;
///< Execute a melee hit, using the current weapon. This will check the relevant skills
/// of the given attacker, and whoever is hit.

Loading…
Cancel
Save