1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-05 06:45:34 +00:00

Add a separate on-hit method to handle objects being hit

This commit is contained in:
Chris Robinson 2013-07-26 03:21:54 -07:00
parent aa14656ff2
commit dd6edd21f8
4 changed files with 57 additions and 31 deletions

View file

@ -310,12 +310,13 @@ namespace MWClass
// Get the weapon used (if hand-to-hand, weapon = inv.end()) // Get the weapon used (if hand-to-hand, weapon = inv.end())
MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end() && weapon->getTypeName() != typeid(ESM::Weapon).name()) MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr());
weapon = inv.end(); if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
weapon = MWWorld::Ptr();
float dist = 100.0f * ((weapon != inv.end()) ? float dist = 100.0f * (!weapon.isEmpty() ?
weapon->get<ESM::Weapon>()->mBase->mData.mReach : weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat()); gmst.find("fHandToHandReach")->getFloat());
MWWorld::Ptr victim = world->getFacedObject(ptr, dist); MWWorld::Ptr victim = world->getFacedObject(ptr, dist);
if(victim.isEmpty()) // Didn't hit anything if(victim.isEmpty()) // Didn't hit anything
@ -329,8 +330,8 @@ namespace MWClass
} }
int weapskill = ESM::Skill::HandToHand; int weapskill = ESM::Skill::HandToHand;
if(weapon != inv.end()) if(!weapon.isEmpty())
weapskill = MWWorld::Class::get(*weapon).getEquipmentSkill(*weapon); weapskill = MWWorld::Class::get(weapon).getEquipmentSkill(weapon);
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
MWMechanics::NpcStats &npcstats = getNpcStats(ptr); MWMechanics::NpcStats &npcstats = getNpcStats(ptr);
@ -345,24 +346,23 @@ namespace MWClass
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{ {
// Missed othercls.onHit(victim, 0.0f, weapon, ptr, false);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(victim, "miss", 1.0f, 1.0f);
return; return;
} }
if(weapon != inv.end()) float damage = 0.0f;
if(!weapon.isEmpty())
{ {
const unsigned char *attack = NULL; const unsigned char *attack = NULL;
if(type == MWMechanics::CreatureStats::AT_Chop) if(type == MWMechanics::CreatureStats::AT_Chop)
attack = weapon->get<ESM::Weapon>()->mBase->mData.mChop; attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == MWMechanics::CreatureStats::AT_Slash) else if(type == MWMechanics::CreatureStats::AT_Slash)
attack = weapon->get<ESM::Weapon>()->mBase->mData.mSlash; attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
else if(type == MWMechanics::CreatureStats::AT_Thrust) else if(type == MWMechanics::CreatureStats::AT_Thrust)
attack = weapon->get<ESM::Weapon>()->mBase->mData.mThrust; attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack) if(attack)
{ {
float damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength()); damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength());
damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
//damage *= weapon_current_health / weapon_max_health; //damage *= weapon_current_health / weapon_max_health;
if(!othercls.hasDetected(victim, ptr)) if(!othercls.hasDetected(victim, ptr))
@ -370,30 +370,44 @@ namespace MWClass
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
} }
damage /= std::min(1.0f + othercls.getArmorRating(victim) / std::max(1.0f, damage), 4.0f); damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f);
float health = othercls.getCreatureStats(victim).getHealth().getCurrent() - damage;
othercls.setActorHealth(victim, health, ptr);
} }
} }
skillUsageSucceeded(ptr, weapskill, 0); skillUsageSucceeded(ptr, weapskill, 0);
othercls.onHit(victim, damage, weapon, ptr, true);
}
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
{
// NOTE: 'object' and/or 'attacker' may be empty.
if(!successful)
{
// TODO: Handle HitAttemptOnMe script function
// Missed
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
// TODO: Handle HitOnMe script function and OnPCHitMe script variable.
if(damage > 0.0f)
{
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
// something, alert the character controller, scripts, etc.
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
}
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
} }
void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
{ {
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
float diff = health - crstats.getHealth().getCurrent();
if(diff < 0.0f)
{
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
// something, alert the character controller, scripts, etc.
// NOTE: 'attacker' may be empty.
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
}
bool wasDead = crstats.isDead(); bool wasDead = crstats.isDead();
MWMechanics::DynamicStat<float> stat(crstats.getHealth()); MWMechanics::DynamicStat<float> stat(crstats.getHealth());

View file

@ -70,6 +70,8 @@ namespace MWClass
virtual void hit(const MWWorld::Ptr& ptr, int type) const; 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 setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,

View file

@ -96,6 +96,11 @@ namespace MWWorld
throw std::runtime_error("class cannot hit"); throw std::runtime_error("class cannot hit");
} }
void Class::onHit(const Ptr& ptr, float damage, const Ptr& object, const Ptr& attacker, bool successful) const
{
throw std::runtime_error("class cannot be hit");
}
void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const
{ {
throw std::runtime_error("class does not have actor health"); throw std::runtime_error("class does not have actor health");

View file

@ -115,6 +115,11 @@ namespace MWWorld
/// enums. ignored for creature attacks. /// enums. ignored for creature attacks.
/// (default implementation: throw an exceoption) /// (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 setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; 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 ///< Sets a new current health value for the actor, optionally specifying the object causing
/// the change. Use this instead of using CreatureStats directly as this will make sure the /// the change. Use this instead of using CreatureStats directly as this will make sure the