mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-05 08:45:33 +00:00
Add a separate on-hit method to handle objects being hit
This commit is contained in:
parent
aa14656ff2
commit
dd6edd21f8
4 changed files with 57 additions and 31 deletions
|
@ -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))
|
||||||
|
@ -371,29 +371,43 @@ namespace MWClass
|
||||||
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());
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue