diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e5fede1a61..5abefd9d47 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -310,12 +310,13 @@ namespace MWClass // 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(); + MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr()); + if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) + weapon = MWWorld::Ptr(); - float dist = 100.0f * ((weapon != inv.end()) ? - weapon->get()->mBase->mData.mReach : + float dist = 100.0f * (!weapon.isEmpty() ? + weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); MWWorld::Ptr victim = world->getFacedObject(ptr, dist); if(victim.isEmpty()) // Didn't hit anything @@ -329,8 +330,8 @@ namespace MWClass } int weapskill = ESM::Skill::HandToHand; - if(weapon != inv.end()) - weapskill = MWWorld::Class::get(*weapon).getEquipmentSkill(*weapon); + if(!weapon.isEmpty()) + weapskill = MWWorld::Class::get(weapon).getEquipmentSkill(weapon); MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); MWMechanics::NpcStats &npcstats = getNpcStats(ptr); @@ -345,24 +346,23 @@ namespace MWClass if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { - // Missed - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(victim, "miss", 1.0f, 1.0f); + othercls.onHit(victim, 0.0f, weapon, ptr, false); return; } - if(weapon != inv.end()) + float damage = 0.0f; + if(!weapon.isEmpty()) { const unsigned char *attack = NULL; if(type == MWMechanics::CreatureStats::AT_Chop) - attack = weapon->get()->mBase->mData.mChop; + attack = weapon.get()->mBase->mData.mChop; else if(type == MWMechanics::CreatureStats::AT_Slash) - attack = weapon->get()->mBase->mData.mSlash; + attack = weapon.get()->mBase->mData.mSlash; else if(type == MWMechanics::CreatureStats::AT_Thrust) - attack = weapon->get()->mBase->mData.mThrust; + attack = weapon.get()->mBase->mData.mThrust; 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 *= weapon_current_health / weapon_max_health; if(!othercls.hasDetected(victim, ptr)) @@ -370,30 +370,44 @@ namespace MWClass 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); - - float health = othercls.getCreatureStats(victim).getHealth().getCurrent() - damage; - othercls.setActorHealth(victim, health, ptr); + damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f); } } - 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 { 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(); MWMechanics::DynamicStat stat(crstats.getHealth()); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 05c4b1e8f0..a527740dee 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -70,6 +70,8 @@ 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 setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 8a2b87b803..9bcfaa026e 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -96,6 +96,11 @@ 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 + { + throw std::runtime_error("class cannot be hit"); + } + void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const { throw std::runtime_error("class does not have actor health"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 89d914746b..9755686c9c 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -115,6 +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 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 /// the change. Use this instead of using CreatureStats directly as this will make sure the