Merge remote-tracking branch 'mrcheko/master'

actorid
Marc Zinnschlag 11 years ago
commit a7be755db0

@ -616,6 +616,7 @@ namespace MWClass
// something, alert the character controller, scripts, etc. // something, alert the character controller, scripts, etc.
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
getCreatureStats(ptr).setAttacked(true);//used in CharacterController
if(object.isEmpty()) if(object.isEmpty())
{ {

@ -63,15 +63,25 @@ struct StateInfo {
const char groupname[32]; const char groupname[32];
}; };
static const StateInfo sDeathList[] = { static const std::string sDeathList[] = {
{ CharState_Death1, "death1" }, "death1" ,
{ CharState_Death2, "death2" }, "death2" ,
{ CharState_Death3, "death3" }, "death3" ,
{ CharState_Death4, "death4" }, "death4" ,
{ CharState_Death5, "death5" }, "death5" ,
{ CharState_SwimDeath, "swimdeath" }, "swimdeath",
}; };
static const StateInfo *sDeathListEnd = &sDeathList[sizeof(sDeathList)/sizeof(sDeathList[0])]; static const int sDeathListSize = sizeof(sDeathList)/sizeof(sDeathList[0]);
static const std::string sHitList[] = {
"hit1" ,
"hit2" ,
"hit3" ,
"hit4" ,
"hit5" ,
"knockdown" ,
};
static const int sHitListSize = sizeof(sHitList)/sizeof(sHitList[0]);
static const StateInfo sMovementList[] = { static const StateInfo sMovementList[] = {
{ CharState_WalkForward, "walkforward" }, { CharState_WalkForward, "walkforward" },
@ -148,6 +158,38 @@ public:
void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force)
{ {
//hit recoils/knockdown animations handling
if(MWWorld::Class::get(mPtr).isActor())
{
if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked())
{
MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false);
if(mHitState == CharState_None)
{
if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr)
&& !MWBase::Environment::get().getWorld()->isSwimming(mPtr) )
{
mHitState = CharState_KnockDown;
mCurrentHit = sHitList[sHitListSize-1];
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
}
else
{
mHitState = CharState_Hit;
int iHit = rand() % (sHitListSize-1);
mCurrentHit = sHitList[iHit];
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
}
}
}
else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit))
{
mCurrentHit.erase();
mHitState = CharState_None;
}
}
const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType));
if(force || idle != mIdleState) if(force || idle != mIdleState)
@ -336,6 +378,31 @@ MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &s
return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
} }
void CharacterController::playRandomDeath(float startpoint)
{
if(MWWorld::Class::get(mPtr).isNpc())
{
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr))
{
mDeathState = CharState_SwimDeath;
mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath'
}
else
{
int num = rand() % (sDeathListSize-1);
mDeathState = static_cast<CharacterState>(CharState_Death1 + num);
mCurrentDeath = sDeathList[num];
}
}
else
{
mDeathState = CharState_Death1;
mCurrentDeath = "death1";
}
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", 0.0f, 0);
}
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
: mPtr(ptr) : mPtr(ptr)
@ -344,6 +411,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mMovementState(CharState_None) , mMovementState(CharState_None)
, mMovementSpeed(0.0f) , mMovementSpeed(0.0f)
, mDeathState(CharState_None) , mDeathState(CharState_None)
, mHitState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing) , mUpperBodyState(UpperCharState_Nothing)
, mJumpState(JumpState_None) , mJumpState(JumpState_None)
, mWeaponType(WeapType_None) , mWeaponType(WeapType_None)
@ -388,15 +456,10 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
} }
refreshCurrentAnims(mIdleState, mMovementState, true); refreshCurrentAnims(mIdleState, mMovementState, true);
if(mDeathState != CharState_None) if(mDeathState != CharState_None)
{ {
const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); playRandomDeath(1.0f);
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
mCurrentDeath = state->groupname;
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", 1.0f, 0);
} }
} }
@ -410,10 +473,9 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
mPtr = ptr; mPtr = ptr;
} }
bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak)
{ {
const MWWorld::Class &cls = MWWorld::Class::get(mPtr); const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
NpcStats &stats = cls.getNpcStats(mPtr); NpcStats &stats = cls.getNpcStats(mPtr);
WeaponType weaptype = WeapType_None; WeaponType weaptype = WeapType_None;
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
@ -443,6 +505,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
{ {
getWeaponGroup(weaptype, weapgroup); getWeaponGroup(weaptype, weapgroup);
mAnimation->showWeapons(false); mAnimation->showWeapons(false);
mAnimation->play(weapgroup, Priority_Weapon, mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true, MWRender::Animation::Group_UpperBody, true,
1.0f, "equip start", "equip stop", 0.0f, 0); 1.0f, "equip start", "equip stop", 0.0f, 0);
@ -498,7 +561,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
bool animPlaying; bool animPlaying;
if(stats.getAttackingOrSpell()) if(stats.getAttackingOrSpell())
{ {
if(mUpperBodyState == UpperCharState_WeapEquiped) if(mUpperBodyState == UpperCharState_WeapEquiped && mHitState == CharState_None)
{ {
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
mAttackType.clear(); mAttackType.clear();
@ -614,15 +677,16 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
MWRender::Animation::Group_UpperBody, false, MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" start", mAttackType+" min attack", weapSpeed, mAttackType+" start", mAttackType+" min attack",
0.0f, 0); 0.0f, 0);
mUpperBodyState = UpperCharState_StartToMinAttack; mUpperBodyState = UpperCharState_StartToMinAttack;
} }
} }
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
} }
else else
{ {
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown)
{ {
if(mAttackType != "shoot") if(mAttackType != "shoot")
{ {
@ -655,6 +719,11 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
1.0f-complete, 0); 1.0f-complete, 0);
mUpperBodyState = UpperCharState_MaxAttackToMinHit; mUpperBodyState = UpperCharState_MaxAttackToMinHit;
} }
else if (mHitState == CharState_KnockDown)
{
mUpperBodyState = UpperCharState_WeapEquiped;
mAnimation->disable(mCurrentWeapon);
}
} }
if(!animPlaying) if(!animPlaying)
@ -662,60 +731,79 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
if(mUpperBodyState == UpperCharState_EquipingWeap || if(mUpperBodyState == UpperCharState_EquipingWeap ||
mUpperBodyState == UpperCharState_FollowStartToFollowStop || mUpperBodyState == UpperCharState_FollowStartToFollowStop ||
mUpperBodyState == UpperCharState_CastingSpell) mUpperBodyState == UpperCharState_CastingSpell)
{
mUpperBodyState = UpperCharState_WeapEquiped; mUpperBodyState = UpperCharState_WeapEquiped;
//don't allow to continue playing hit animation on UpperBody after actor had attacked during it
if(mHitState == CharState_Hit)
{
mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody);
//commenting out following 2 lines will give a bit different combat dynamics(slower)
mHitState = CharState_None;
mCurrentHit.clear();
}
}
else if(mUpperBodyState == UpperCharState_UnEquipingWeap) else if(mUpperBodyState == UpperCharState_UnEquipingWeap)
mUpperBodyState = UpperCharState_Nothing; mUpperBodyState = UpperCharState_Nothing;
} }
else if(complete >= 1.0f) else if(complete >= 1.0f)
{ {
if(mUpperBodyState == UpperCharState_StartToMinAttack) std::string start, stop;
{ switch(mUpperBodyState)
mAnimation->disable(mCurrentWeapon);
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" min attack", mAttackType+" max attack",
0.0f, 0);
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
}
else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit)
{ {
mAnimation->disable(mCurrentWeapon); case UpperCharState_StartToMinAttack:
if(mAttackType == "shoot") start = mAttackType+" min attack";
mAnimation->play(mCurrentWeapon, Priority_Weapon, stop = mAttackType+" max attack";
MWRender::Animation::Group_UpperBody, false, mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
weapSpeed, mAttackType+" min hit", mAttackType+" follow start", break;
0.0f, 0); case UpperCharState_MaxAttackToMinHit:
else if(mAttackType == "shoot")
mAnimation->play(mCurrentWeapon, Priority_Weapon, {
MWRender::Animation::Group_UpperBody, false, start = mAttackType+" min hit";
weapSpeed, mAttackType+" min hit", mAttackType+" hit", stop = mAttackType+" release";
0.0f, 0); }
mUpperBodyState = UpperCharState_MinHitToHit; else
{
start = mAttackType+" min hit";
stop = mAttackType+" hit";
}
mUpperBodyState = UpperCharState_MinHitToHit;
break;
case UpperCharState_MinHitToHit:
if(mAttackType == "shoot")
{
start = mAttackType+" follow start";
stop = mAttackType+" follow stop";
}
else
{
float str = stats.getAttackStrength();
start = mAttackType+((str < 0.5f) ? " small follow start"
: (str < 1.0f) ? " medium follow start"
: " large follow start");
stop = mAttackType+((str < 0.5f) ? " small follow stop"
: (str < 1.0f) ? " medium follow stop"
: " large follow stop");
}
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
break;
default:
break;
} }
else if(mUpperBodyState == UpperCharState_MinHitToHit)
if(!start.empty())
{ {
mAnimation->disable(mCurrentWeapon); mAnimation->disable(mCurrentWeapon);
if(mAttackType == "shoot") if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
mAnimation->play(mCurrentWeapon, Priority_Weapon, mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true, MWRender::Animation::Group_UpperBody, true,
weapSpeed, mAttackType+" follow start", mAttackType+" follow stop", weapSpeed, start, stop, 0.0f, 0);
0.0f, 0);
else else
{
float str = stats.getAttackStrength();
std::string start = mAttackType+((str < 0.5f) ? " small follow start"
: (str < 1.0f) ? " medium follow start"
: " large follow start");
std::string stop = mAttackType+((str < 0.5f) ? " small follow stop"
: (str < 1.0f) ? " medium follow stop"
: " large follow stop");
mAnimation->play(mCurrentWeapon, Priority_Weapon, mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true, MWRender::Animation::Group_UpperBody, false,
weapSpeed, start, stop, 0.0f, 0); weapSpeed, start, stop, 0.0f, 0);
}
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
} }
} }
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()
@ -764,6 +852,8 @@ void CharacterController::update(float duration)
bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak);
bool flying = world->isFlying(mPtr); bool flying = world->isFlying(mPtr);
Ogre::Vector3 vec = cls.getMovementVector(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr);
if(mHitState != CharState_None && mJumpState == JumpState_None)
vec = Ogre::Vector3(0.0f);
Ogre::Vector3 rot = cls.getRotationVector(mPtr); Ogre::Vector3 rot = cls.getRotationVector(mPtr);
mMovementSpeed = cls.getSpeed(mPtr); mMovementSpeed = cls.getSpeed(mPtr);
@ -776,6 +866,7 @@ void CharacterController::update(float duration)
isrunning = isrunning && std::abs(vec[0])+std::abs(vec[1]) > 0.0f; isrunning = isrunning && std::abs(vec[0])+std::abs(vec[1]) > 0.0f;
// advance athletics // advance athletics
if(std::abs(vec[0])+std::abs(vec[1]) > 0.0f && mPtr.getRefData().getHandle() == "player") if(std::abs(vec[0])+std::abs(vec[1]) > 0.0f && mPtr.getRefData().getHandle() == "player")
{ {
@ -879,6 +970,7 @@ void CharacterController::update(float duration)
int realHealthLost = healthLost * (1.0f - 0.25 * fatigueTerm); int realHealthLost = healthLost * (1.0f - 0.25 * fatigueTerm);
health.setCurrent(health.getCurrent() - realHealthLost); health.setCurrent(health.getCurrent() - realHealthLost);
cls.getCreatureStats(mPtr).setHealth(health); cls.getCreatureStats(mPtr).setHealth(health);
cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true);
// report acrobatics progression // report acrobatics progression
if (mPtr.getRefData().getHandle() == "player") if (mPtr.getRefData().getHandle() == "player")
@ -893,7 +985,7 @@ void CharacterController::update(float duration)
} }
else else
{ {
if(!(vec.z > 0.0f)) if(!(vec.z > 0.0f))
mJumpState = JumpState_None; mJumpState = JumpState_None;
vec.z = 0.0f; vec.z = 0.0f;
@ -954,13 +1046,19 @@ void CharacterController::update(float duration)
refreshCurrentAnims(idlestate, movestate, forcestateupdate); refreshCurrentAnims(idlestate, movestate, forcestateupdate);
rot *= duration * Ogre::Math::RadiansToDegrees(1.0f);
if (!mSkipAnim) if (!mSkipAnim)
{ {
world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); if(mHitState != CharState_KnockDown)
{
rot *= duration * Ogre::Math::RadiansToDegrees(1.0f);
world->rotateObject(mPtr, rot.x, rot.y, rot.z, true);
}
else //avoid z-rotating for knockdown
world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true);
world->queueMovement(mPtr, vec); world->queueMovement(mPtr, vec);
} }
movement = vec; movement = vec;
} }
else if(cls.getCreatureStats(mPtr).isDead()) else if(cls.getCreatureStats(mPtr).isDead())
@ -1056,14 +1154,7 @@ void CharacterController::forceStateUpdate()
refreshCurrentAnims(mIdleState, mMovementState, true); refreshCurrentAnims(mIdleState, mMovementState, true);
if(mDeathState != CharState_None) if(mDeathState != CharState_None)
{ {
const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); playRandomDeath();
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
mCurrentDeath = state->groupname;
if(!mAnimation->getInfo(mCurrentDeath))
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", 0.0f, 0);
} }
} }
@ -1072,44 +1163,10 @@ void CharacterController::kill()
if(mDeathState != CharState_None) if(mDeathState != CharState_None)
return; return;
if(mPtr.getTypeName() == typeid(ESM::NPC).name()) playRandomDeath();
{
const StateInfo *state = NULL;
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr))
{
mDeathState = CharState_SwimDeath;
state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
}
static const CharacterState deathstates[5] = {
CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5
};
std::vector<CharacterState> states(&deathstates[0], &deathstates[5]);
while(states.size() > 1 && (!state || !mAnimation->hasAnimation(state->groupname)))
{
int pos = (int)(rand()/((double)RAND_MAX+1.0)*states.size());
mDeathState = states[pos];
states.erase(states.begin()+pos);
state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
}
mCurrentDeath = state->groupname;
}
else
{
mDeathState = CharState_Death1;
mCurrentDeath = "death1";
}
if(mAnimation) if(mAnimation)
{ {
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", 0.0f, 0);
mAnimation->disable(mCurrentIdle); mAnimation->disable(mCurrentIdle);
} }

@ -28,7 +28,9 @@ enum Priority {
Priority_Default, Priority_Default,
Priority_Jump, Priority_Jump,
Priority_Movement, Priority_Movement,
Priority_Hit,
Priority_Weapon, Priority_Weapon,
Priority_Knockdown,
Priority_Torch, Priority_Torch,
Priority_Death, Priority_Death,
@ -87,7 +89,10 @@ enum CharacterState {
CharState_Death3, CharState_Death3,
CharState_Death4, CharState_Death4,
CharState_Death5, CharState_Death5,
CharState_SwimDeath CharState_SwimDeath,
CharState_Hit,
CharState_KnockDown
}; };
enum WeaponType { enum WeaponType {
@ -142,6 +147,9 @@ class CharacterController
CharacterState mDeathState; CharacterState mDeathState;
std::string mCurrentDeath; std::string mCurrentDeath;
CharacterState mHitState;
std::string mCurrentHit;
UpperBodyCharacterState mUpperBodyState; UpperBodyCharacterState mUpperBodyState;
JumpingState mJumpState; JumpingState mJumpState;
@ -172,6 +180,8 @@ class CharacterController
void updateVisibility(); void updateVisibility();
void playRandomDeath(float startpoint = 0.0f);
public: public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
virtual ~CharacterController(); virtual ~CharacterController();

@ -673,7 +673,20 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co
MWBase::Environment::get().getWorld()->castSpell(mPtr); MWBase::Environment::get().getWorld()->castSpell(mPtr);
} }
void Animation::changeGroups(const std::string &groupname, int groups)
{
AnimStateMap::iterator stateiter = mStates.begin();
stateiter = mStates.find(groupname);
if(stateiter != mStates.end())
{
if(stateiter->second.mGroups != groups)
{
stateiter->second.mGroups = groups;
resetActiveGroups();
}
return;
}
}
void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops) void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops)
{ {
if(!mSkelBase || mAnimSources.empty()) if(!mSkelBase || mAnimSources.empty())
@ -839,7 +852,6 @@ void Animation::disable(const std::string &groupname)
Ogre::Vector3 Animation::runAnimation(float duration) Ogre::Vector3 Animation::runAnimation(float duration)
{ {
Ogre::Vector3 movement(0.0f); Ogre::Vector3 movement(0.0f);
AnimStateMap::iterator stateiter = mStates.begin(); AnimStateMap::iterator stateiter = mStates.begin();
while(stateiter != mStates.end()) while(stateiter != mStates.end())
{ {

@ -272,6 +272,7 @@ public:
* \param groupname Animation group to disable. * \param groupname Animation group to disable.
*/ */
void disable(const std::string &groupname); void disable(const std::string &groupname);
void changeGroups(const std::string &groupname, int group);
/** Retrieves the velocity (in units per second) that the animation will move. */ /** Retrieves the velocity (in units per second) that the animation will move. */
float getVelocity(const std::string &groupname) const; float getVelocity(const std::string &groupname) const;

Loading…
Cancel
Save