From 54f91d4b3a365c9eb8e9196b2512db6601873c47 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 06:48:25 -0700 Subject: [PATCH] Implement hand-to-hand attacks --- apps/openmw/mwclass/creature.cpp | 18 ++++++---- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/npc.cpp | 61 ++++++++++++++++++++++++++------ apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 9 ++--- 6 files changed, 71 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 43d6c6159..960f57430 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -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 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 diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 452a0054f..0d8694ff8 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -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; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e1e4e7856..c0d42ec3d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -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 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 diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index a527740de..3d429c982 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -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; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 9bcfaa026..123027c9c 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -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"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 9755686c9..bfeeaba9f 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -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