Commit b3a057d6 authored by Chris Robinson's avatar Chris Robinson

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
parent 1dcc8937
......@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment