forked from teamnwah/openmw-tes3coop
Merged pull request #1477
This commit is contained in:
commit
7d9de93fd3
11 changed files with 98 additions and 62 deletions
|
@ -1,16 +1,20 @@
|
|||
0.45.0
|
||||
------
|
||||
|
||||
Bug #2835: Player able to slowly move when overencumbered
|
||||
Bug #3374: Touch spells not hitting kwama foragers
|
||||
Bug #3591: Angled hit distance too low
|
||||
Bug #3897: Have Goodbye give all choices the effects of Goodbye
|
||||
Bug #3997: Almalexia doesn't pace
|
||||
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
|
||||
Bug #4221: Characters get stuck in V-shaped terrain
|
||||
Bug #4251: Stationary NPCs do not return to their position after combat
|
||||
Bug #4293: Faction members are not aware of faction ownerships in barter
|
||||
Bug #4327: Missing animations during spell/weapon stance switching
|
||||
Bug #4419: MRK NiStringExtraData is handled incorrectly
|
||||
Bug #4426: RotateWorld behavior is incorrect
|
||||
Bug #4429: [Windows] Error on build INSTALL.vcxproj project (debug) with cmake 3.7.2
|
||||
Bug #4432: Guards behaviour is incorrect if they do not have AI packages
|
||||
Bug #4433: Guard behaviour is incorrect with Alarm = 0
|
||||
Feature #4444: Per-group KF-animation files support
|
||||
|
||||
|
|
|
@ -48,7 +48,8 @@ namespace MWMechanics
|
|||
TypeIdPursue = 6,
|
||||
TypeIdAvoidDoor = 7,
|
||||
TypeIdFace = 8,
|
||||
TypeIdBreathe = 9
|
||||
TypeIdBreathe = 9,
|
||||
TypeIdInternalTravel = 10
|
||||
};
|
||||
|
||||
///Default constructor
|
||||
|
@ -79,6 +80,9 @@ namespace MWMechanics
|
|||
/// Get the target actor the AI is targeted at (not applicable to all AI packages, default return empty Ptr)
|
||||
virtual MWWorld::Ptr getTarget() const;
|
||||
|
||||
/// Get the destination point of the AI package (not applicable to all AI packages, default return (0, 0, 0))
|
||||
virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); };
|
||||
|
||||
/// Return true if having this AiPackage makes the actor side with the target in fights (default false)
|
||||
virtual bool sideWithTarget() const;
|
||||
|
||||
|
|
|
@ -184,7 +184,8 @@ bool isActualAiPackage(int packageTypeId)
|
|||
&& packageTypeId != AiPackage::TypeIdPursue
|
||||
&& packageTypeId != AiPackage::TypeIdAvoidDoor
|
||||
&& packageTypeId != AiPackage::TypeIdFace
|
||||
&& packageTypeId != AiPackage::TypeIdBreathe);
|
||||
&& packageTypeId != AiPackage::TypeIdBreathe
|
||||
&& packageTypeId != AiPackage::TypeIdInternalTravel);
|
||||
}
|
||||
|
||||
void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||
|
@ -298,7 +299,7 @@ void AiSequence::clear()
|
|||
mPackages.clear();
|
||||
}
|
||||
|
||||
void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||
void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, bool cancelOther)
|
||||
{
|
||||
if (actor == getPlayer())
|
||||
throw std::runtime_error("Can't add AI packages to player");
|
||||
|
@ -307,8 +308,33 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
|||
if (isActualAiPackage(package.getTypeId()))
|
||||
stopCombat();
|
||||
|
||||
// We should return a wandering actor back after combat or pursuit.
|
||||
// The same thing for actors without AI packages.
|
||||
// Also there is no point to stack return packages.
|
||||
int currentTypeId = getTypeId();
|
||||
int newTypeId = package.getTypeId();
|
||||
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander
|
||||
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel)
|
||||
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue))
|
||||
{
|
||||
osg::Vec3f dest;
|
||||
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander)
|
||||
{
|
||||
AiPackage* activePackage = getActivePackage();
|
||||
dest = activePackage->getDestination(actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = actor.getRefData().getPosition().asVec3();
|
||||
}
|
||||
|
||||
MWMechanics::AiTravel travelPackage(dest.x(), dest.y(), dest.z(), true);
|
||||
stack(travelPackage, actor, false);
|
||||
}
|
||||
|
||||
// remove previous packages if required
|
||||
if (package.shouldCancelPreviousAi())
|
||||
if (cancelOther && package.shouldCancelPreviousAi())
|
||||
{
|
||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end();)
|
||||
{
|
||||
|
@ -392,6 +418,8 @@ void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const
|
|||
{
|
||||
(*iter)->writeState(sequence);
|
||||
}
|
||||
|
||||
sequence.mLastAiPackage = mLastAiPackage;
|
||||
}
|
||||
|
||||
void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
||||
|
@ -403,7 +431,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
int count = 0;
|
||||
for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin();
|
||||
it != sequence.mPackages.end(); ++it)
|
||||
{
|
||||
{
|
||||
if (isActualAiPackage(it->mType))
|
||||
count++;
|
||||
}
|
||||
|
@ -462,6 +490,8 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
|
||||
mPackages.push_back(package.release());
|
||||
}
|
||||
|
||||
mLastAiPackage = sequence.mLastAiPackage;
|
||||
}
|
||||
|
||||
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state)
|
||||
|
|
|
@ -115,7 +115,7 @@ namespace MWMechanics
|
|||
///< Add \a package to the front of the sequence
|
||||
/** Suspends current package
|
||||
@param actor The actor that owns this AiSequence **/
|
||||
void stack (const AiPackage& package, const MWWorld::Ptr& actor);
|
||||
void stack (const AiPackage& package, const MWWorld::Ptr& actor, bool cancelOther=true);
|
||||
|
||||
/// Return the current active package.
|
||||
/** If there is no active package, it will throw an exception **/
|
||||
|
|
|
@ -28,15 +28,14 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
AiTravel::AiTravel(float x, float y, float z)
|
||||
: mX(x),mY(y),mZ(z)
|
||||
AiTravel::AiTravel(float x, float y, float z, bool hidden)
|
||||
: mX(x),mY(y),mZ(z),mHidden(hidden)
|
||||
{
|
||||
}
|
||||
|
||||
AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
|
||||
: mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ)
|
||||
: mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(travel->mHidden)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AiTravel *MWMechanics::AiTravel::clone() const
|
||||
|
@ -64,7 +63,7 @@ namespace MWMechanics
|
|||
|
||||
int AiTravel::getTypeId() const
|
||||
{
|
||||
return TypeIdTravel;
|
||||
return mHidden ? TypeIdInternalTravel : TypeIdTravel;
|
||||
}
|
||||
|
||||
void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)
|
||||
|
@ -83,6 +82,7 @@ namespace MWMechanics
|
|||
travel->mData.mX = mX;
|
||||
travel->mData.mY = mY;
|
||||
travel->mData.mZ = mZ;
|
||||
travel->mHidden = mHidden;
|
||||
|
||||
ESM::AiSequence::AiPackageContainer package;
|
||||
package.mType = ESM::AiSequence::Ai_Travel;
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace MWMechanics
|
|||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
AiTravel(float x, float y, float z);
|
||||
AiTravel(float x, float y, float z, bool hidden = false);
|
||||
AiTravel(const ESM::AiSequence::AiTravel* travel);
|
||||
|
||||
/// Simulates the passing of time
|
||||
|
@ -38,6 +38,8 @@ namespace MWMechanics
|
|||
float mX;
|
||||
float mY;
|
||||
float mZ;
|
||||
|
||||
bool mHidden;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace MWMechanics
|
|||
float mTargetAngleRadians;
|
||||
bool mTurnActorGivingGreetingToFacePlayer;
|
||||
float mReaction; // update some actions infrequently
|
||||
|
||||
|
||||
AiWander::GreetingState mSaidGreeting;
|
||||
int mGreetingTimer;
|
||||
|
||||
|
@ -70,7 +70,7 @@ namespace MWMechanics
|
|||
|
||||
bool mIsWanderingManually;
|
||||
bool mCanWanderAlongPathGrid;
|
||||
|
||||
|
||||
unsigned short mIdleAnimation;
|
||||
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
|
||||
|
||||
|
@ -86,7 +86,7 @@ namespace MWMechanics
|
|||
|
||||
float mDoorCheckDuration;
|
||||
int mStuckCount;
|
||||
|
||||
|
||||
AiWanderStorage():
|
||||
mTargetAngleRadians(0),
|
||||
mTurnActorGivingGreetingToFacePlayer(false),
|
||||
|
@ -111,7 +111,7 @@ namespace MWMechanics
|
|||
mIsWanderingManually = isManualWander;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
|
||||
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0))
|
||||
|
@ -223,7 +223,7 @@ namespace MWMechanics
|
|||
if (mPathFinder.isPathConstructed())
|
||||
storage.setState(Wander_Walking);
|
||||
}
|
||||
|
||||
|
||||
doPerFrameActionsForState(actor, duration, storage, pos);
|
||||
|
||||
playIdleDialogueRandomly(actor);
|
||||
|
@ -298,13 +298,6 @@ namespace MWMechanics
|
|||
if(mDistance && cellChange)
|
||||
mDistance = 0;
|
||||
|
||||
// For stationary NPCs, move back to the starting location if another AiPackage moved us elsewhere
|
||||
if (mDistance == 0 && !cellChange
|
||||
&& (pos.asVec3() - mInitialActorPosition).length2() > (DESTINATION_TOLERANCE * DESTINATION_TOLERANCE))
|
||||
{
|
||||
returnToStartLocation(actor, storage, pos);
|
||||
}
|
||||
|
||||
// Allow interrupting a walking actor to trigger a greeting
|
||||
WanderState& wanderState = storage.mState;
|
||||
if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking))
|
||||
|
@ -321,7 +314,7 @@ namespace MWMechanics
|
|||
{
|
||||
setPathToAnAllowedNode(actor, storage, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (storage.mIsWanderingManually && mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE)) {
|
||||
completeManualWalking(actor, storage);
|
||||
}
|
||||
|
@ -330,10 +323,19 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
bool AiWander::getRepeat() const
|
||||
{
|
||||
return mRepeat;
|
||||
{
|
||||
return mRepeat;
|
||||
}
|
||||
|
||||
osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const
|
||||
{
|
||||
if (mHasDestination)
|
||||
return mDestination;
|
||||
|
||||
const ESM::Pathgrid::Point currentPosition = actor.getRefData().getPosition().pos;
|
||||
const osg::Vec3f currentPositionVec3f = osg::Vec3f(currentPosition.mX, currentPosition.mY, currentPosition.mZ);
|
||||
return currentPositionVec3f;
|
||||
}
|
||||
|
||||
bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||
{
|
||||
|
@ -342,35 +344,14 @@ namespace MWMechanics
|
|||
// End package if duration is complete
|
||||
if (mRemainingDuration <= 0)
|
||||
{
|
||||
stopWalking(actor, storage);
|
||||
return true;
|
||||
stopWalking(actor, storage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if get here, not yet completed
|
||||
return false;
|
||||
}
|
||||
|
||||
void AiWander::returnToStartLocation(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)
|
||||
{
|
||||
if (!mPathFinder.isPathConstructed())
|
||||
{
|
||||
mDestination = mInitialActorPosition;
|
||||
ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination));
|
||||
|
||||
// actor position is already in world coordinates
|
||||
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
|
||||
|
||||
// don't take shortcuts for wandering
|
||||
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
|
||||
|
||||
if (mPathFinder.isPathConstructed())
|
||||
{
|
||||
storage.setState(Wander_Walking);
|
||||
mHasDestination = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Commands actor to walk to a random location near original spawn location.
|
||||
*/
|
||||
|
@ -497,7 +478,7 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor,
|
||||
void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor,
|
||||
float duration, AiWanderStorage& storage, ESM::Position& pos)
|
||||
{
|
||||
// Is there no destination or are we there yet?
|
||||
|
@ -873,7 +854,7 @@ namespace MWMechanics
|
|||
|
||||
state.moveIn(new AiWanderStorage());
|
||||
|
||||
MWBase::Environment::get().getWorld()->moveObject(actor, static_cast<float>(dest.mX),
|
||||
MWBase::Environment::get().getWorld()->moveObject(actor, static_cast<float>(dest.mX),
|
||||
static_cast<float>(dest.mY), static_cast<float>(dest.mZ));
|
||||
actor.getClass().adjustPosition(actor, false);
|
||||
}
|
||||
|
@ -914,7 +895,7 @@ namespace MWMechanics
|
|||
// get NPC's position in local (i.e. cell) coordinates
|
||||
osg::Vec3f npcPos(mInitialActorPosition);
|
||||
CoordinateConverter(cell).toLocal(npcPos);
|
||||
|
||||
|
||||
// Find closest pathgrid point
|
||||
int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, npcPos);
|
||||
|
||||
|
@ -945,7 +926,7 @@ namespace MWMechanics
|
|||
storage.mPopulateAvailableNodes = false;
|
||||
}
|
||||
|
||||
// When only one path grid point in wander distance,
|
||||
// When only one path grid point in wander distance,
|
||||
// additional points for NPC to wander to are:
|
||||
// 1. NPC's initial location
|
||||
// 2. Partway along the path between the point and its connected points.
|
||||
|
@ -969,7 +950,7 @@ namespace MWMechanics
|
|||
delta.normalize();
|
||||
|
||||
int distance = std::max(mDistance / 2, MINIMUM_WANDER_DISTANCE);
|
||||
|
||||
|
||||
// must not travel longer than distance between waypoints or NPC goes past waypoint
|
||||
distance = std::min(distance, static_cast<int>(length));
|
||||
delta *= distance;
|
||||
|
@ -1041,4 +1022,3 @@ namespace MWMechanics
|
|||
init();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,11 @@ namespace MWMechanics
|
|||
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
|
||||
|
||||
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state);
|
||||
|
||||
|
||||
bool getRepeat() const;
|
||||
|
||||
|
||||
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const;
|
||||
|
||||
enum GreetingState {
|
||||
Greet_None,
|
||||
Greet_InProgress,
|
||||
|
@ -85,7 +87,6 @@ namespace MWMechanics
|
|||
bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage,
|
||||
const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos, float duration);
|
||||
bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||
void returnToStartLocation(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos);
|
||||
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
|
||||
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
||||
bool destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "aicombat.hpp"
|
||||
#include "aipursue.hpp"
|
||||
#include "aitravel.hpp"
|
||||
#include "spellcasting.hpp"
|
||||
#include "autocalcspell.hpp"
|
||||
#include "npcstats.hpp"
|
||||
|
@ -1598,9 +1599,12 @@ namespace MWMechanics
|
|||
|
||||
void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
||||
{
|
||||
if (ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(target))
|
||||
MWMechanics::AiSequence& aiSequence = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
|
||||
if (aiSequence.isInCombat(target))
|
||||
return;
|
||||
ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr);
|
||||
|
||||
aiSequence.stack(MWMechanics::AiCombat(target), ptr);
|
||||
if (target == getPlayer())
|
||||
{
|
||||
// if guard starts combat with player, guards pursuing player should do the same
|
||||
|
|
|
@ -35,11 +35,13 @@ namespace AiSequence
|
|||
void AiTravel::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT (mData, "DATA");
|
||||
esm.getHNOT (mHidden, "HIDD");
|
||||
}
|
||||
|
||||
void AiTravel::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT ("DATA", mData);
|
||||
esm.writeHNT ("HIDD", mHidden);
|
||||
}
|
||||
|
||||
void AiEscort::load(ESMReader &esm)
|
||||
|
@ -158,6 +160,8 @@ namespace AiSequence
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esm.writeHNT ("LAST", mLastAiPackage);
|
||||
}
|
||||
|
||||
void AiSequence::load(ESMReader &esm)
|
||||
|
@ -225,6 +229,8 @@ namespace AiSequence
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
esm.getHNOT (mLastAiPackage, "LAST");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ namespace ESM
|
|||
struct AiTravel : AiPackage
|
||||
{
|
||||
AiTravelData mData;
|
||||
bool mHidden;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
@ -149,10 +150,14 @@ namespace ESM
|
|||
|
||||
struct AiSequence
|
||||
{
|
||||
AiSequence() {}
|
||||
AiSequence()
|
||||
{
|
||||
mLastAiPackage = -1;
|
||||
}
|
||||
~AiSequence();
|
||||
|
||||
std::vector<AiPackageContainer> mPackages;
|
||||
int mLastAiPackage;
|
||||
|
||||
void load (ESMReader &esm);
|
||||
void save (ESMWriter &esm) const;
|
||||
|
|
Loading…
Reference in a new issue