From 25c64dbb0f2051e1e17752d1dd2b9068fd554872 Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 2 Feb 2017 02:15:10 +0900 Subject: [PATCH 01/10] Make combat engagement logic more like vanilla (Fixes #2678, Fixes #3705) --- apps/openmw/mwclass/creature.cpp | 47 +++-- apps/openmw/mwclass/npc.cpp | 51 +++-- apps/openmw/mwmechanics/actors.cpp | 192 ++++++++++++------ apps/openmw/mwmechanics/aicombat.cpp | 10 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 + apps/openmw/mwmechanics/creaturestats.hpp | 7 +- .../mwmechanics/mechanicsmanagerimp.cpp | 37 ++-- files/settings-default.cfg | 4 + 8 files changed, 238 insertions(+), 120 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9e5afb66a..fa38949d8 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -325,22 +325,38 @@ namespace MWClass void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const { - // NOTE: 'object' and/or 'attacker' may be empty. + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); - if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)) - getCreatureStats(ptr).setAttacked(true); + // NOTE: 'object' and/or 'attacker' may be empty. + if (!attacker.isEmpty() && !stats.getAiSequence().isInCombat(attacker)) + stats.setAttacked(true); // Self defense bool setOnPcHitMe = true; // Note OnPcHitMe is not set for friendly hits. // No retaliation for totally static creatures (they have no movement or attacks anyway) if (isMobile(ptr) && !attacker.isEmpty()) - { setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); + + // Attacker and target store each other as hitattemptactor if they have no one stored yet + if (!attacker.isEmpty() && !ptr.isEmpty()) + { + MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker); + // First handle the attacked actor + if (stats.getHitAttemptActor().isEmpty() + && (statsAttacker.getAiSequence().isInCombat(ptr) + || attacker == MWMechanics::getPlayer())) + stats.setHitAttemptActor(attacker); + + // Next handle the attacking actor + if (statsAttacker.getHitAttemptActor().isEmpty() + && (statsAttacker.getAiSequence().isInCombat(ptr) + || attacker == MWMechanics::getPlayer())) + statsAttacker.setHitAttemptActor(ptr); } if (!object.isEmpty()) - getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); + stats.setLastHitAttemptObject(object.getCellRef().getRefId()); if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { @@ -358,7 +374,7 @@ namespace MWClass } if (!object.isEmpty()) - getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); + stats.setLastHitObject(object.getCellRef().getRefId()); if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); @@ -371,16 +387,13 @@ namespace MWClass if (!attacker.isEmpty()) { // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); - float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); + float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().iKnockDownOddsMult->getInt() * 0.01f + getGmst().iKnockDownOddsBase->getInt(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) - { - getCreatureStats(ptr).setKnockedDown(true); - - } + stats.setKnockedDown(true); else - getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + stats.setHitRecovery(true); // Is this supposed to always occur? } damage = std::max(1.f, damage); @@ -395,15 +408,15 @@ namespace MWClass MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); - MWMechanics::DynamicStat health(getCreatureStats(ptr).getHealth()); + MWMechanics::DynamicStat health(stats.getHealth()); health.setCurrent(health.getCurrent() - damage); - getCreatureStats(ptr).setHealth(health); + stats.setHealth(health); } else { - MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); + MWMechanics::DynamicStat fatigue(stats.getFatigue()); fatigue.setCurrent(fatigue.getCurrent() - damage, true); - getCreatureStats(ptr).setFatigue(fatigue); + stats.setFatigue(fatigue); } } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9e7b568fa..bc5a2fec5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -654,22 +654,38 @@ namespace MWClass void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - - // NOTE: 'object' and/or 'attacker' may be empty. - - bool wasDead = getCreatureStats(ptr).isDead(); + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + bool wasDead = stats.isDead(); // Note OnPcHitMe is not set for friendly hits. bool setOnPcHitMe = true; - if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)) - { - getCreatureStats(ptr).setAttacked(true); + // NOTE: 'object' and/or 'attacker' may be empty. + if (!attacker.isEmpty() && !stats.getAiSequence().isInCombat(attacker)) + { + stats.setAttacked(true); setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } + // Attacker and target store each other as hitattemptactor if they have no one stored yet + if (!attacker.isEmpty() && !ptr.isEmpty()) + { + MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker); + // First handle the attacked actor + if (stats.getHitAttemptActor().isEmpty() + && (statsAttacker.getAiSequence().isInCombat(ptr) + || attacker == MWMechanics::getPlayer())) + stats.setHitAttemptActor(attacker); + + // Next handle the attacking actor + if (statsAttacker.getHitAttemptActor().isEmpty() + && (statsAttacker.getAiSequence().isInCombat(ptr) + || attacker == MWMechanics::getPlayer())) + statsAttacker.setHitAttemptActor(ptr); + } + if (!object.isEmpty()) - getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); + stats.setLastHitAttemptObject(object.getCellRef().getRefId()); if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { @@ -687,7 +703,7 @@ namespace MWClass } if (!object.isEmpty()) - getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); + stats.setLastHitObject(object.getCellRef().getRefId()); if (damage > 0.0f && !object.isEmpty()) @@ -706,21 +722,16 @@ namespace MWClass int chance = store.get().find("iVoiceHitOdds")->getInt(); if (Misc::Rng::roll0to99() < chance) - { MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); - } // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); - float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); + float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.iKnockDownOddsMult->getInt() * 0.01f + gmst.iKnockDownOddsBase->getInt(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) - { - getCreatureStats(ptr).setKnockedDown(true); - - } + stats.setKnockedDown(true); else - getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + stats.setHitRecovery(true); // Is this supposed to always occur? if (damage > 0 && ishealth) { @@ -799,13 +810,13 @@ namespace MWClass } MWMechanics::DynamicStat health(getCreatureStats(ptr).getHealth()); health.setCurrent(health.getCurrent() - damage); - getCreatureStats(ptr).setHealth(health); + stats.setHealth(health); } else { MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); fatigue.setCurrent(fatigue.getCurrent() - damage, true); - getCreatureStats(ptr).setFatigue(fatigue); + stats.setFatigue(fatigue); } if (!wasDead && getCreatureStats(ptr).isDead()) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5668832f7..a6380608a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -9,6 +9,8 @@ #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -280,7 +282,7 @@ namespace MWMechanics void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) { - const CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1); + CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1); if (creatureStats1.getAiSequence().isInCombat(actor2)) return; @@ -298,97 +300,125 @@ namespace MWMechanics if (!actor1.getClass().isMobile(actor1)) return; - // Start combat if target actor is in combat with one of our followers or escorters - const std::list& followersAndEscorters = getActorsSidingWith(actor1); - for (std::list::const_iterator it = followersAndEscorters.begin(); it != followersAndEscorters.end(); ++it) + // If this is set to true, actor1 will start combat with actor2 if the awareness check at the end of the method returns true + bool aggressive = false; + + // Get actors allied with actor1. Includes those following or escorting actor1, actors following or escorting those actors, (recursive) + // and any actor currently being followed or escorted by actor1 + std::set allies1; + getActorsSidingWith(actor1, allies1); + + // If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and actor2 + for (std::set::const_iterator it = allies1.begin(); it != allies1.end(); ++it) { - // Need to check both ways since player doesn't use AI packages - if ((creatureStats2.getAiSequence().isInCombat(*it) - || it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) - && !creatureStats1.getAiSequence().isInCombat(*it)) + if (creatureStats1.getAiSequence().isInCombat(*it)) + continue; + + if (!it->getClass().getCreatureStats(*it).getHitAttemptActor().isEmpty() + && it->getClass().getCreatureStats(*it).getHitAttemptActor() == actor2) { MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); - return; + // Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat + // if the player gets out of reach, while the ally would continue combat with the player + creatureStats1.setHitAttemptActor(actor2); + return; } - } - // Start combat if target actor is in combat with someone we are following through a follow package - for (std::list::const_iterator it = creatureStats1.getAiSequence().begin(); it != creatureStats1.getAiSequence().end(); ++it) - { - if (!(*it)->sideWithTarget()) - continue; - - MWWorld::Ptr followTarget = (*it)->getTarget(); + // If there's been no attack attempt yet but an ally of actor1 is in combat with actor2, become aggressive to actor2 + if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) + aggressive = true; + } - if (followTarget.isEmpty()) - continue; + std::set playerFollowersAndEscorters; + getActorsSidingWith(MWMechanics::getPlayer(), playerFollowersAndEscorters); - if (creatureStats1.getAiSequence().isInCombat(followTarget)) - continue; + bool isPlayerFollowerOrEscorter = std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end(); - // Need to check both ways since player doesn't use AI packages - if (creatureStats2.getAiSequence().isInCombat(followTarget) - || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) + // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them + // Doesn't apply for player followers/escorters + if (!aggressive && !isPlayerFollowerOrEscorter) + { + // Check that actor2 is in combat with actor1 + if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1)) { - MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); - return; + std::set allies2; + getActorsSidingWith(actor2, allies2); + // Check that an ally of actor2 is also in combat with actor1 + for (std::set::const_iterator it = allies2.begin(); it != allies2.end(); ++it) + { + if ((it)->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor1)) + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + // Also have actor1's allies start combat + for (std::set::const_iterator it2 = allies1.begin(); it2 != allies1.end(); ++it2) + MWBase::Environment::get().getMechanicsManager()->startCombat(*it2, actor2); + return; + } + } } } - - // Start combat with the player if we are already in combat with a player follower or escorter - const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); - if (againstPlayer) + + // If set in the settings file, player followers and escorters will become aggressive toward enemies in combat with them or the player + if (!aggressive && isPlayerFollowerOrEscorter && Settings::Manager::getBool("followers attack on sight", "Game")) { - for (std::list::const_iterator it = playerFollowersAndEscorters.begin(); it != playerFollowersAndEscorters.end(); ++it) + if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1)) + aggressive = true; + else { - if (creatureStats1.getAiSequence().isInCombat(*it)) + for (std::set::const_iterator it = allies1.begin(); it != allies1.end(); ++it) { - MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); - return; + if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(*it)) + { + aggressive = true; + break; + } } } } - // Otherwise, don't initiate combat with an unreachable target - if (!MWMechanics::canFight(actor1,actor2)) + // Stop here if target is unreachable + if (!MWMechanics::canFight(actor1, actor2)) return; - bool aggressive = false; - - if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + // Do aggression check if actor2 is the player or a player follower or escorter + if (!aggressive) { - // Player followers and escorters with high fight should not initiate combat here with the player or with - // other player followers or escorters - if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) - return; - - aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); + if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + { + // Player followers and escorters with high fight should not initiate combat with the player or with + // other player followers or escorters + if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) == playerFollowersAndEscorters.end()) + aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); + } } - else + + // Make guards go aggressive with creatures that are in combat, unless the creature is a follower or escorter + if (actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc()) { - // Make guards fight aggressive creatures - if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) + bool followerOrEscorter = false; + for (std::list::const_iterator it = creatureStats2.getAiSequence().begin(); it != creatureStats2.getAiSequence().end(); ++it) { - if (creatureStats1.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) - aggressive = true; + // The follow package must be first or have nothing but combat before it + if ((*it)->sideWithTarget()) + { + followerOrEscorter = true; + break; + } + else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + break; } + if (!followerOrEscorter && creatureStats2.getAiSequence().isInCombat()) + aggressive = true; } + // If any of the above conditions turned actor1 aggressive towards actor2, do an awareness check. If it passes, start combat with actor2. if (aggressive) { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); - - if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) - LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); + LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); if (LOS) - { MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); - if (!againstPlayer) // start combat between each other - { - MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1); - } - } } } @@ -931,7 +961,10 @@ namespace MWMechanics { static const int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) + { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); + creatureStats.setHitAttemptActor(player); // Stops the guard from quitting combat if player is unreachable + } else creatureStats.getAiSequence().stack(AiPursue(player), ptr); creatureStats.setAlarmed(true); @@ -1054,10 +1087,25 @@ namespace MWMechanics if (iter->first == player) iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); + // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player. + if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead() + || !iter->first.getClass().getCreatureStats(iter->first).getAiSequence().isInCombat() + || !inProcessingRange)) + { + iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActor(NULL); + if (player.getClass().getCreatureStats(player).getHitAttemptActor() == iter->first) + player.getClass().getCreatureStats(player).setHitAttemptActor(NULL); + } + + const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(player.getClass().getCreatureStats(player).getHitAttemptActorId()); + + if (!playerHitAttemptActor.isInCell()) + player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); + if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { - MWWorld::Ptr actor = iter->first; // make a copy of the map key to avoid it being invalidated when the player teleports bool cellChanged = MWBase::Environment::get().getWorld()->hasCellChanged(); + MWWorld::Ptr actor = iter->first; // make a copy of the map key to avoid it being invalidated when the player teleports updateActor(actor, duration); if (!cellChanged && MWBase::Environment::get().getWorld()->hasCellChanged()) { @@ -1470,18 +1518,36 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Class &cls = iter->first.getClass(); - CreatureStats &stats = cls.getCreatureStats(iter->first); + const CreatureStats &stats = cls.getCreatureStats(iter->first); if (stats.isDead()) continue; - // An actor counts as following if AiFollow or AiEscort is the current AiPackage, or there are only Combat packages before the AiFollow/AiEscort package + // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat packages before the Follow/Escort package for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) { if ((*it)->sideWithTarget() && (*it)->getTarget() == actor) + { list.push_back(iter->first); + break; + } else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) break; } + // Actors that are targeted by this actor's Follow or Escort packages also side with them + if (actor != getPlayer()) + { + const CreatureStats &stats2 = actor.getClass().getCreatureStats(actor); + for (std::list::const_iterator it2 = stats2.getAiSequence().begin(); it2 != stats2.getAiSequence().end(); ++it2) + { + if ((*it2)->sideWithTarget() && !(*it2)->getTarget().isEmpty()) + { + list.push_back((*it2)->getTarget()); + break; + } + else if ((*it2)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + break; + } + } } return list; } @@ -1551,8 +1617,6 @@ namespace MWMechanics return list; } - - std::list Actors::getActorsFighting(const MWWorld::Ptr& actor) { std::list list; std::vector neighbors; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index fbd3819e2..96f3ef73d 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -20,6 +20,7 @@ #include "combat.hpp" #include "coordinateconverter.hpp" #include "actorutil.hpp" +#include "mechanicsmanagerimp.hpp" namespace { @@ -233,9 +234,14 @@ namespace MWMechanics storage.stopAttack(); characterController.setAttackingOrSpell(false); storage.mActionCooldown = 0.f; - if (target == MWMechanics::getPlayer()) + // Continue combat if target is player or player follower/escorter and an attack has been attempted + const std::list& playerFollowersAndEscorters = MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(MWMechanics::getPlayer()); + bool targetSidesWithPlayer = (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), target) != playerFollowersAndEscorters.end()); + if ((target == MWMechanics::getPlayer() || targetSidesWithPlayer) + && ((actor.getClass().getCreatureStats(actor).getHitAttemptActor() == target) + || (target.getClass().getCreatureStats(target).getHitAttemptActor() == actor))) forceFlee = true; - else + else // Otherwise end combat return true; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index be51cea25..1fce2f8f8 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -371,6 +371,16 @@ namespace MWMechanics return mLastHitAttemptObject; } + void CreatureStats::setHitAttemptActor(const MWWorld::Ptr& actor) + { + mHitAttemptActor = actor; + } + + const MWWorld::Ptr &CreatureStats::getHitAttemptActor() const + { + return mHitAttemptActor; + } + void CreatureStats::addToFallHeight(float height) { mFallHeight += height; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 171857603..6e47a9278 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -53,6 +53,9 @@ namespace MWMechanics std::string mLastHitObject; // The last object to hit this actor std::string mLastHitAttemptObject; // The last object to attempt to hit this actor + MWWorld::Ptr mHitAttemptActor; // Stores an actor that attacked this actor. Only one is stored at a time, + // and it is not changed if a different actor attacks. It is cleared when combat ends. + bool mRecalcMagicka; // For merchants: the last time items were restocked and gold pool refilled. @@ -241,9 +244,11 @@ namespace MWMechanics bool getStance (Stance flag) const; void setLastHitObject(const std::string &objectid); - void setLastHitAttemptObject(const std::string &objectid); const std::string &getLastHitObject() const; + void setLastHitAttemptObject(const std::string &objectid); const std::string &getLastHitAttemptObject() const; + void setHitAttemptActor(const MWWorld::Ptr &actor); + const MWWorld::Ptr &getHitAttemptActor() const; // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves. // TODO: Put it somewhere else? diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index ad1ca9bb9..c04fcefae 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1214,48 +1214,51 @@ namespace MWMechanics } } - bool MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker) + bool MechanicsManager::actorAttacked(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker) { - if (ptr == getPlayer()) + std::list followersAttacker = getActorsSidingWith(attacker); + std::list followersTarget = getActorsSidingWith(target); + + MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target); + + if (target == getPlayer()) return false; - std::list followers = getActorsSidingWith(attacker); - MWMechanics::CreatureStats& targetStats = ptr.getClass().getCreatureStats(ptr); - if (std::find(followers.begin(), followers.end(), ptr) != followers.end()) + if (std::find(followersAttacker.begin(), followersAttacker.end(), target) != followersAttacker.end()) { - targetStats.friendlyHit(); + statsTarget.friendlyHit(); - if (targetStats.getFriendlyHits() < 4) + if (statsTarget.getFriendlyHits() < 4) { - MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + MWBase::Environment::get().getDialogueManager()->say(target, "hit"); return false; } } // Attacking an NPC that is already in combat with any other NPC is not a crime - AiSequence& seq = targetStats.getAiSequence(); + AiSequence& seq = statsTarget.getAiSequence(); bool isFightingNpc = false; for (std::list::const_iterator it = seq.begin(); it != seq.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { - MWWorld::Ptr target = (*it)->getTarget(); - if (!target.isEmpty() && target.getClass().isNpc()) + MWWorld::Ptr target2 = (*it)->getTarget(); + if (!target2.isEmpty() && target2.getClass().isNpc()) isFightingNpc = true; } } - if (ptr.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) - && !isAggressive(ptr, attacker) && !isFightingNpc) - commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + if (target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) + && !isAggressive(target, attacker) && !isFightingNpc) + commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault); - if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr) + if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) || attacker == getPlayer()) && !seq.isInCombat(attacker)) { // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Note: accidental or collateral damage attacks are ignored. - startCombat(ptr, attacker); + startCombat(target, attacker); } return true; @@ -1365,6 +1368,7 @@ namespace MWMechanics // if guard starts combat with player, guards pursuing player should do the same if (ptr.getClass().isClass(ptr, "Guard")) { + ptr.getClass().getCreatureStats(ptr).setHitAttemptActor(target); // Stops guard from ending combat if player is unreachable for (Actors::PtrActorMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { if (iter->first.getClass().isClass(iter->first, "Guard")) @@ -1374,6 +1378,7 @@ namespace MWMechanics { aiSeq.stopPursuit(); aiSeq.stack(MWMechanics::AiCombat(target), ptr); + iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActor(target); // Stops guard from ending combat if player is unreachable } } } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 770a9674d..91329b8ca 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -153,6 +153,10 @@ show effect duration = false # Prevents merchants from equipping items that are sold to them. prevent merchant equipping = false +# Makes player followers and escorters start combat with enemies who have started combat with them +# or the player. Otherwise they wait for the enemies or the player to do an attack first. +followers attack on sight = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). From 5d2090684a89aae5da55b9c0160a61cf32fbf496 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 6 Feb 2017 21:32:36 +0900 Subject: [PATCH 02/10] Store mHitAttemptActorId in save files --- apps/openmw/mwclass/creature.cpp | 8 ++++---- apps/openmw/mwclass/npc.cpp | 8 ++++---- apps/openmw/mwmechanics/actors.cpp | 13 ++++++------- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- apps/openmw/mwmechanics/creaturestats.cpp | 12 +++++++----- apps/openmw/mwmechanics/creaturestats.hpp | 9 ++++----- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- components/esm/creaturestats.cpp | 7 +++++++ components/esm/creaturestats.hpp | 1 + 9 files changed, 37 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index fa38949d8..dea078701 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -343,16 +343,16 @@ namespace MWClass { MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker); // First handle the attacked actor - if (stats.getHitAttemptActor().isEmpty() + if ((stats.getHitAttemptActorId() == -1) && (statsAttacker.getAiSequence().isInCombat(ptr) || attacker == MWMechanics::getPlayer())) - stats.setHitAttemptActor(attacker); + stats.setHitAttemptActorId(statsAttacker.getActorId()); // Next handle the attacking actor - if (statsAttacker.getHitAttemptActor().isEmpty() + if ((statsAttacker.getHitAttemptActorId() == -1) && (statsAttacker.getAiSequence().isInCombat(ptr) || attacker == MWMechanics::getPlayer())) - statsAttacker.setHitAttemptActor(ptr); + statsAttacker.setHitAttemptActorId(stats.getActorId()); } if (!object.isEmpty()) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index bc5a2fec5..645cd799c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -672,16 +672,16 @@ namespace MWClass { MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker); // First handle the attacked actor - if (stats.getHitAttemptActor().isEmpty() + if ((stats.getHitAttemptActorId() == -1) && (statsAttacker.getAiSequence().isInCombat(ptr) || attacker == MWMechanics::getPlayer())) - stats.setHitAttemptActor(attacker); + stats.setHitAttemptActorId(statsAttacker.getActorId()); // Next handle the attacking actor - if (statsAttacker.getHitAttemptActor().isEmpty() + if ((statsAttacker.getHitAttemptActorId() == -1) && (statsAttacker.getAiSequence().isInCombat(ptr) || attacker == MWMechanics::getPlayer())) - statsAttacker.setHitAttemptActor(ptr); + statsAttacker.setHitAttemptActorId(stats.getActorId()); } if (!object.isEmpty()) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a6380608a..418f53422 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -314,13 +314,12 @@ namespace MWMechanics if (creatureStats1.getAiSequence().isInCombat(*it)) continue; - if (!it->getClass().getCreatureStats(*it).getHitAttemptActor().isEmpty() - && it->getClass().getCreatureStats(*it).getHitAttemptActor() == actor2) + if (creatureStats2.matchesActorId(it->getClass().getCreatureStats(*it).getHitAttemptActorId())) { MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); // Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat // if the player gets out of reach, while the ally would continue combat with the player - creatureStats1.setHitAttemptActor(actor2); + creatureStats1.setHitAttemptActorId(it->getClass().getCreatureStats(*it).getHitAttemptActorId()); return; } @@ -963,7 +962,7 @@ namespace MWMechanics if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); - creatureStats.setHitAttemptActor(player); // Stops the guard from quitting combat if player is unreachable + creatureStats.setHitAttemptActorId(player.getClass().getCreatureStats(player).getActorId()); // Stops the guard from quitting combat if player is unreachable } else creatureStats.getAiSequence().stack(AiPursue(player), ptr); @@ -1092,9 +1091,9 @@ namespace MWMechanics || !iter->first.getClass().getCreatureStats(iter->first).getAiSequence().isInCombat() || !inProcessingRange)) { - iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActor(NULL); - if (player.getClass().getCreatureStats(player).getHitAttemptActor() == iter->first) - player.getClass().getCreatureStats(player).setHitAttemptActor(NULL); + iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActorId(-1); + if (player.getClass().getCreatureStats(player).getHitAttemptActorId() == iter->first.getClass().getCreatureStats(iter->first).getActorId()) + player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); } const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(player.getClass().getCreatureStats(player).getHitAttemptActorId()); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 96f3ef73d..5b48a0ace 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -238,8 +238,8 @@ namespace MWMechanics const std::list& playerFollowersAndEscorters = MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(MWMechanics::getPlayer()); bool targetSidesWithPlayer = (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), target) != playerFollowersAndEscorters.end()); if ((target == MWMechanics::getPlayer() || targetSidesWithPlayer) - && ((actor.getClass().getCreatureStats(actor).getHitAttemptActor() == target) - || (target.getClass().getCreatureStats(target).getHitAttemptActor() == actor))) + && ((actor.getClass().getCreatureStats(actor).getHitAttemptActorId() == target.getClass().getCreatureStats(target).getActorId()) + || (target.getClass().getCreatureStats(target).getHitAttemptActorId() == actor.getClass().getCreatureStats(actor).getActorId()))) forceFlee = true; else // Otherwise end combat return true; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 1fce2f8f8..89406e883 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -21,7 +21,7 @@ namespace MWMechanics mTalkedTo (false), mAlarmed (false), mAttacked (false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), - mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), + mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1), mDeathAnimation(-1), mTimeOfDeath(), mLevel (0) { for (int i=0; i<4; ++i) @@ -371,14 +371,14 @@ namespace MWMechanics return mLastHitAttemptObject; } - void CreatureStats::setHitAttemptActor(const MWWorld::Ptr& actor) + void CreatureStats::setHitAttemptActorId(int actorId) { - mHitAttemptActor = actor; + mHitAttemptActorId = actorId; } - const MWWorld::Ptr &CreatureStats::getHitAttemptActor() const + int CreatureStats::getHitAttemptActorId() const { - return mHitAttemptActor; + return mHitAttemptActorId; } void CreatureStats::addToFallHeight(float height) @@ -531,6 +531,7 @@ namespace MWMechanics state.mActorId = mActorId; state.mDeathAnimation = mDeathAnimation; state.mTimeOfDeath = mTimeOfDeath.toEsm(); + state.mHitAttemptActorId = mHitAttemptActorId; mSpells.writeState(state.mSpells); mActiveSpells.writeState(state.mActiveSpells); @@ -579,6 +580,7 @@ namespace MWMechanics mActorId = state.mActorId; mDeathAnimation = state.mDeathAnimation; mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath); + mHitAttemptActorId = state.mHitAttemptActorId; mSpells.readState(state.mSpells); mActiveSpells.readState(state.mActiveSpells); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 6e47a9278..057a6f602 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -53,9 +53,6 @@ namespace MWMechanics std::string mLastHitObject; // The last object to hit this actor std::string mLastHitAttemptObject; // The last object to attempt to hit this actor - MWWorld::Ptr mHitAttemptActor; // Stores an actor that attacked this actor. Only one is stored at a time, - // and it is not changed if a different actor attacks. It is cleared when combat ends. - bool mRecalcMagicka; // For merchants: the last time items were restocked and gold pool refilled. @@ -65,6 +62,8 @@ namespace MWMechanics int mGoldPool; int mActorId; + int mHitAttemptActorId; // Stores an actor that attacked this actor. Only one is stored at a time, + // and it is not changed if a different actor attacks. It is cleared when combat ends. // The index of the death animation that was played, or -1 if none played signed char mDeathAnimation; @@ -247,8 +246,8 @@ namespace MWMechanics const std::string &getLastHitObject() const; void setLastHitAttemptObject(const std::string &objectid); const std::string &getLastHitAttemptObject() const; - void setHitAttemptActor(const MWWorld::Ptr &actor); - const MWWorld::Ptr &getHitAttemptActor() const; + void setHitAttemptActorId(const int actorId); + int getHitAttemptActorId() const; // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves. // TODO: Put it somewhere else? diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c04fcefae..735e7d267 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1368,7 +1368,7 @@ namespace MWMechanics // if guard starts combat with player, guards pursuing player should do the same if (ptr.getClass().isClass(ptr, "Guard")) { - ptr.getClass().getCreatureStats(ptr).setHitAttemptActor(target); // Stops guard from ending combat if player is unreachable + ptr.getClass().getCreatureStats(ptr).setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable for (Actors::PtrActorMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { if (iter->first.getClass().isClass(iter->first, "Guard")) @@ -1378,7 +1378,7 @@ namespace MWMechanics { aiSeq.stopPursuit(); aiSeq.stack(MWMechanics::AiCombat(target), ptr); - iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActor(target); // Stops guard from ending combat if player is unreachable + iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable } } } diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index c19dfcf02..92f1c3025 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -90,6 +90,9 @@ void ESM::CreatureStats::load (ESMReader &esm) mActorId = -1; esm.getHNOT (mActorId, "ACID"); + mHitAttemptActorId = -1; + esm.getHNOT(mHitAttemptActorId, "HAID"); + mDeathAnimation = -1; esm.getHNOT (mDeathAnimation, "DANM"); @@ -203,6 +206,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mActorId != -1) esm.writeHNT ("ACID", mActorId); + if (mHitAttemptActorId != -1) + esm.writeHNT("HAID", mHitAttemptActorId); + if (mDeathAnimation != -1) esm.writeHNT ("DANM", mDeathAnimation); @@ -240,6 +246,7 @@ void ESM::CreatureStats::blank() mTradeTime.mDay = 0; mGoldPool = 0; mActorId = -1; + mHitAttemptActorId = -1; mHasAiSettings = false; mDead = false; mDeathAnimationFinished = false; diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 55af9d509..cbb70535f 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -38,6 +38,7 @@ namespace ESM ESM::TimeStamp mTradeTime; int mGoldPool; int mActorId; + int mHitAttemptActorId; bool mDead; bool mDeathAnimationFinished; From b3d5c2bd7ff3633685518dc98d34cad17a76acf9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Feb 2017 01:58:27 +0100 Subject: [PATCH 03/10] Use the actor's collision shape in findGround() The cylinder base is no longer appropriate as of the change to capsules. This also works around a bug when tracing a small cylinder/box shape apparently introduced with bullet 2.86. --- apps/openmw/mwphysics/actor.cpp | 1 + apps/openmw/mwphysics/actor.hpp | 4 ++++ apps/openmw/mwphysics/physicssystem.cpp | 2 +- apps/openmw/mwphysics/trace.cpp | 8 +------- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 5167f6ef0..e0edd515d 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -35,6 +35,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr } else mShape.reset(new btBoxShape(toBullet(mHalfExtents))); + mConvexShape = static_cast(mShape.get()); mCollisionObject.reset(new btCollisionObject); mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 9c0144b9d..a640640e2 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -12,6 +12,7 @@ class btCollisionWorld; class btCollisionShape; class btCollisionObject; +class btConvexShape; namespace Resource { @@ -61,6 +62,8 @@ namespace MWPhysics return mInternalCollisionMode; } + btConvexShape* getConvexShape() const { return mConvexShape; } + /** * Enables or disables the *external* collision body. If disabled, other actors will not collide with this actor. */ @@ -153,6 +156,7 @@ namespace MWPhysics bool mWalkingOnWater; std::auto_ptr mShape; + btConvexShape* mConvexShape; std::auto_ptr mCollisionObject; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 6d7daa3b8..0884798df 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -239,7 +239,7 @@ namespace MWPhysics public: static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight) { - osg::Vec3f position(ptr.getRefData().getPosition().asVec3()); + osg::Vec3f position(actor->getCollisionObjectPosition()); ActorTracer tracer; tracer.findGround(actor, position, position-osg::Vec3f(0,0,maxHeight), collisionWorld); diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index feda68ca5..10d22ec7f 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -4,7 +4,6 @@ #include #include -#include #include "collisiontype.hpp" #include "actor.hpp" @@ -106,12 +105,7 @@ void ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask; newTraceCallback.m_collisionFilterMask &= ~CollisionType_Actor; - btVector3 halfExtents = toBullet(actor->getHalfExtents()); - - halfExtents[2] = 1.0f; - btCylinderShapeZ base(halfExtents); - - world->convexSweepTest(&base, from, to, newTraceCallback); + world->convexSweepTest(actor->getConvexShape(), from, to, newTraceCallback); if(newTraceCallback.hasHit()) { const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; From cf7b0098ed397452d2107565f305bd2a5c440537 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Feb 2017 02:05:12 +0100 Subject: [PATCH 04/10] Slightly increase backoff value --- apps/openmw/mwphysics/physicssystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 0884798df..dd5a7e826 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -401,7 +401,7 @@ namespace MWPhysics if (tracer.mFraction < 1E-9f) { // Try to separate by backing off slighly to unstuck the solver - const osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-3f; + osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-2f; newPosition += backOff; } From 6ecc008813d945ddbc2cff9e0c2b6d98376ed47f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Feb 2017 02:43:49 +0100 Subject: [PATCH 05/10] Fix an issue uncovered by the last commit related to changing actor position without properly moving the actor --- apps/openmw/mwphysics/physicssystem.cpp | 9 ++++---- apps/openmw/mwphysics/physicssystem.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 29 +++++++++++-------------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index dd5a7e826..7c8342707 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -237,9 +237,10 @@ namespace MWPhysics public: - static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight) + static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, osg::Vec3f position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight) { - osg::Vec3f position(actor->getCollisionObjectPosition()); + osg::Vec3f offset = actor->getCollisionObjectPosition() - ptr.getRefData().getPosition().asVec3(); + position += offset; ActorTracer tracer; tracer.findGround(actor, position, position-osg::Vec3f(0,0,maxHeight), collisionWorld); @@ -1085,13 +1086,13 @@ namespace MWPhysics return resultCallback.mResult; } - osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, float maxHeight) + osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight) { ActorMap::iterator found = mActors.find(ptr); if (found == mActors.end()) return ptr.getRefData().getPosition().asVec3(); else - return MovementSolver::traceDown(ptr, found->second, mCollisionWorld, maxHeight); + return MovementSolver::traceDown(ptr, position, found->second, mCollisionWorld, maxHeight); } void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts) diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index d74631845..ec12f7681 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -90,7 +90,7 @@ namespace MWPhysics void debugDraw(); std::vector getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with - osg::Vec3f traceDown(const MWWorld::Ptr &ptr, float maxHeight); + osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight); std::pair getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0229f1f93..dcec8ea85 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1296,7 +1296,7 @@ namespace MWWorld void World::adjustPosition(const Ptr &ptr, bool force) { - ESM::Position pos (ptr.getRefData().getPosition()); + osg::Vec3f pos (ptr.getRefData().getPosition().asVec3()); if(!ptr.getRefData().getBaseNode()) { @@ -1306,34 +1306,31 @@ namespace MWWorld float terrainHeight = -std::numeric_limits::max(); if (ptr.getCell()->isExterior()) - terrainHeight = getTerrainHeightAt(pos.asVec3()); + terrainHeight = getTerrainHeightAt(pos); - if (pos.pos[2] < terrainHeight) - pos.pos[2] = terrainHeight; + if (pos.z() < terrainHeight) + pos.z() = terrainHeight; - pos.pos[2] += 20; // place slightly above. will snap down to ground with code below - - ptr.getRefData().setPosition(pos); + pos.z() += 20; // place slightly above. will snap down to ground with code below if (force || !isFlying(ptr)) { - osg::Vec3f traced = mPhysics->traceDown(ptr, 500); - if (traced.z() < pos.pos[2]) - pos.pos[2] = traced.z(); + osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500); + if (traced.z() < pos.z()) + pos.z() = traced.z(); } - moveObject(ptr, ptr.getCell(), pos.pos[0], pos.pos[1], pos.pos[2]); + moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z()); } void World::fixPosition(const Ptr &actor) { const float dist = 8000; - ESM::Position pos (actor.getRefData().getPosition()); - pos.pos[2] += dist; - actor.getRefData().setPosition(pos); + osg::Vec3f pos (actor.getRefData().getPosition().asVec3()); + pos.z() += dist; - osg::Vec3f traced = mPhysics->traceDown(actor, dist*1.1f); - if (traced != pos.asVec3()) + osg::Vec3f traced = mPhysics->traceDown(actor, pos, dist*1.1f); + if (traced != pos) moveObject(actor, actor.getCell(), traced.x(), traced.y(), traced.z()); } From dd54887783d8295aa13a3004748c0a0c07048e7b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Feb 2017 03:31:37 +0100 Subject: [PATCH 06/10] Fix node mask of CharacterPreview being reset --- apps/openmw/mwrender/characterpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index b177645b1..0d848bb11 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -184,7 +184,7 @@ namespace MWRender void CharacterPreview::redraw() { - mCamera->setNodeMask(~0); + mCamera->setNodeMask(Mask_RenderToTexture); mDrawOnceCallback->redrawNextFrame(); } From bbcba57495751ffe8a21735dec485939ca264a4a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Feb 2017 06:26:32 +0100 Subject: [PATCH 07/10] Disable hitAttemptActorId saving code for now --- apps/openmw/mwmechanics/creaturestats.cpp | 4 ++-- components/esm/creaturestats.cpp | 10 +++++----- components/esm/creaturestats.hpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 89406e883..31724bcbc 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -531,7 +531,7 @@ namespace MWMechanics state.mActorId = mActorId; state.mDeathAnimation = mDeathAnimation; state.mTimeOfDeath = mTimeOfDeath.toEsm(); - state.mHitAttemptActorId = mHitAttemptActorId; + //state.mHitAttemptActorId = mHitAttemptActorId; mSpells.writeState(state.mSpells); mActiveSpells.writeState(state.mActiveSpells); @@ -580,7 +580,7 @@ namespace MWMechanics mActorId = state.mActorId; mDeathAnimation = state.mDeathAnimation; mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath); - mHitAttemptActorId = state.mHitAttemptActorId; + //mHitAttemptActorId = state.mHitAttemptActorId; mSpells.readState(state.mSpells); mActiveSpells.readState(state.mActiveSpells); diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 92f1c3025..beb2c4c64 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -90,8 +90,8 @@ void ESM::CreatureStats::load (ESMReader &esm) mActorId = -1; esm.getHNOT (mActorId, "ACID"); - mHitAttemptActorId = -1; - esm.getHNOT(mHitAttemptActorId, "HAID"); + //mHitAttemptActorId = -1; + //esm.getHNOT(mHitAttemptActorId, "HAID"); mDeathAnimation = -1; esm.getHNOT (mDeathAnimation, "DANM"); @@ -206,8 +206,8 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mActorId != -1) esm.writeHNT ("ACID", mActorId); - if (mHitAttemptActorId != -1) - esm.writeHNT("HAID", mHitAttemptActorId); + //if (mHitAttemptActorId != -1) + // esm.writeHNT("HAID", mHitAttemptActorId); if (mDeathAnimation != -1) esm.writeHNT ("DANM", mDeathAnimation); @@ -246,7 +246,7 @@ void ESM::CreatureStats::blank() mTradeTime.mDay = 0; mGoldPool = 0; mActorId = -1; - mHitAttemptActorId = -1; + //mHitAttemptActorId = -1; mHasAiSettings = false; mDead = false; mDeathAnimationFinished = false; diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index cbb70535f..7e7e5dac3 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -38,7 +38,7 @@ namespace ESM ESM::TimeStamp mTradeTime; int mGoldPool; int mActorId; - int mHitAttemptActorId; + //int mHitAttemptActorId; bool mDead; bool mDeathAnimationFinished; From 8752ae9c6a98828d97a8d714cd0162892b359c39 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Feb 2017 06:28:23 +0100 Subject: [PATCH 08/10] Fix include --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 5b48a0ace..17005f235 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -9,6 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwrender/animation.hpp" @@ -20,7 +21,6 @@ #include "combat.hpp" #include "coordinateconverter.hpp" #include "actorutil.hpp" -#include "mechanicsmanagerimp.hpp" namespace { From 332ceb51a25c40caba4444174216794652094be8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Feb 2017 06:48:03 +0100 Subject: [PATCH 09/10] Rearranged check for better performance --- apps/openmw/mwmechanics/actors.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 418f53422..9376beb2d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -300,6 +300,10 @@ namespace MWMechanics if (!actor1.getClass().isMobile(actor1)) return; + // Stop here if target is unreachable + if (!MWMechanics::canFight(actor1, actor2)) + return; + // If this is set to true, actor1 will start combat with actor2 if the awareness check at the end of the method returns true bool aggressive = false; @@ -375,10 +379,6 @@ namespace MWMechanics } } - // Stop here if target is unreachable - if (!MWMechanics::canFight(actor1, actor2)) - return; - // Do aggression check if actor2 is the player or a player follower or escorter if (!aggressive) { From cda83559aa046bac6d379f4c454cd650f450215e Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 10 Feb 2017 11:37:36 +0100 Subject: [PATCH 10/10] Update Bullet to 2.86 --- CI/before_script.msvc.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 98c1984f4..aa8c565ba 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -300,9 +300,9 @@ if [ -z $SKIP_DOWNLOAD ]; then fi # Bullet - download "Bullet 2.83.7" \ - "http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.83.7-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "Bullet-2.83.7-msvc${MSVC_YEAR}-win${BITS}.7z" + download "Bullet 2.86" \ + "http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \ + "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" # FFmpeg download "FFmpeg 3.0.1" \ @@ -414,7 +414,7 @@ cd $DEPS echo # Bullet -printf "Bullet 2.83.7... " +printf "Bullet 2.86... " { cd $DEPS_INSTALL @@ -422,8 +422,8 @@ printf "Bullet 2.83.7... " printf -- "Exists. (No version checking) " elif [ -z $SKIP_EXTRACT ]; then rm -rf Bullet - eval 7z x -y "${DEPS}/Bullet-2.83.7-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "Bullet-2.83.7-msvc${MSVC_YEAR}-win${BITS}" Bullet + eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP + mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet fi export BULLET_ROOT="$(real_pwd)/Bullet"