Implement hand-to-hand attacks

This commit is contained in:
Chris Robinson 2013-07-28 06:48:25 -07:00
parent b7e81dbc5b
commit 54f91d4b3a
6 changed files with 71 additions and 23 deletions

View file

@ -154,7 +154,7 @@ namespace MWClass
{
}
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
{
// NOTE: 'object' and/or 'attacker' may be empty.
@ -178,13 +178,19 @@ namespace MWClass
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
}
if(damage > 0.0f)
if(ishealth)
{
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
if(damage > 0.0f)
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
}
else
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage);
getCreatureStats(ptr).setFatigue(fatigue);
}
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
}
void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const

View file

@ -44,7 +44,7 @@ namespace MWClass
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;

View file

@ -346,10 +346,11 @@ namespace MWClass
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
othercls.onHit(victim, 0.0f, weapon, ptr, false);
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
return;
}
bool healthdmg;
float damage = 0.0f;
if(!weapon.isEmpty())
{
@ -373,15 +374,43 @@ namespace MWClass
}
damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f);
}
healthdmg = true;
}
else
{
// Note: MCP contains an option to include Strength in hand-to-hand damage
// calculations. Some mods recommend using it, so we may want to include am
// option for it.
float minstrike = gmst.find("fMinHandToHandMult")->getFloat();
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat();
damage = npcstats.getSkill(weapskill).getModified();
damage *= minstrike + ((maxstrike-minstrike)*npcstats.getAttackStrength());
if(!othercls.hasDetected(victim, ptr))
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
healthdmg = (othercls.getCreatureStats(victim).getFatigue().getCurrent() < 1.0f ||
npcstats.isWerewolf());
if(healthdmg)
{
// Not sure this is right...
damage *= gmst.find("fHandtoHandHealthPer")->getFloat() * 1.5f;
damage /= othercls.getArmorRating(victim);
}
}
if(ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, weapskill, 0);
othercls.onHit(victim, damage, weapon, ptr, true);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
}
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
// NOTE: 'object' and/or 'attacker' may be empty.
if(!successful)
@ -389,7 +418,7 @@ namespace MWClass
// TODO: Handle HitAttemptOnMe script function
// Missed
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
@ -429,13 +458,14 @@ namespace MWClass
};
int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)];
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
{
switch(get(armor).getEquipmentSkill(armor))
if(object.isEmpty())
sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f);
else switch(get(armor).getEquipmentSkill(armor))
{
case ESM::Skill::LightArmor:
sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f);
@ -448,12 +478,23 @@ namespace MWClass
break;
}
}
sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
else if(object.isEmpty())
sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f);
}
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
if(ishealth)
{
if(damage > 0.0f)
sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
}
else
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage);
getCreatureStats(ptr).setFatigue(fatigue);
}
}
void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const

View file

@ -70,7 +70,7 @@ namespace MWClass
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void onHit(const MWWorld::Ptr& ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;

View file

@ -96,7 +96,7 @@ namespace MWWorld
throw std::runtime_error("class cannot hit");
}
void Class::onHit(const Ptr& ptr, float damage, const Ptr& object, const Ptr& attacker, bool successful) const
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const
{
throw std::runtime_error("class cannot be hit");
}

View file

@ -115,10 +115,11 @@ namespace MWWorld
/// enums. ignored for creature attacks.
/// (default implementation: throw an exceoption)
virtual void onHit(const Ptr& ptr, float damage, const Ptr &object, const Ptr &attacker, bool successful) const;
///< Alerts \a ptr that it's being hit for \a damage health by \a object (sword, arrow,
/// etc). \a attacker specifies the actor responsible for the attack, and \a successful
/// specifies if the hit is successful or not.
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
/// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the
/// actor responsible for the attack, and \a successful specifies if the hit is
/// successful or not.
virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const;
///< Sets a new current health value for the actor, optionally specifying the object causing