1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-04-18 14:42:27 +00:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
mrcheko 2014-01-17 21:00:55 +02:00
commit a11a6b616b
35 changed files with 493 additions and 123 deletions

View File

@ -186,8 +186,6 @@ if (WIN32)
add_definitions(-DSDL_MAIN_HANDLED) add_definitions(-DSDL_MAIN_HANDLED)
else (WIN32) else (WIN32)
set(PLATFORM_INCLUDE_DIR "") set(PLATFORM_INCLUDE_DIR "")
find_path (UUID_INCLUDE_DIR uuid/uuid.h)
include_directories(${UUID_INCLUDE_DIR})
endif (WIN32) endif (WIN32)
if (MSVC10) if (MSVC10)
set(PLATFORM_INCLUDE_DIR "") set(PLATFORM_INCLUDE_DIR "")
@ -239,7 +237,6 @@ include_directories("."
${MYGUI_INCLUDE_DIRS} ${MYGUI_INCLUDE_DIRS}
${MYGUI_PLATFORM_INCLUDE_DIRS} ${MYGUI_PLATFORM_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR}
${UUID_INCLUDE_DIR}
${LIBDIR} ${LIBDIR}
) )
@ -409,46 +406,6 @@ IF(NOT WIN32 AND NOT APPLE)
# Install resources # Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources") INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
IF (DPKG_PROGRAM)
## Debian Specific
IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git")
EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION )
STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}")
EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME )
EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
ELSE()
SET(VERSION_STRING "${OPENMW_VERSION}")
SET(PACKAGE_MAINTAINER "unknown")
ENDIF()
SET(CPACK_GENERATOR "DEB")
SET(CPACK_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org")
SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}")
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind
OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind.
Data files from the original game is required to run it.")
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
EXECUTE_PROCESS(
COMMAND ${DPKG_PROGRAM} --print-architecture
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
INCLUDE(CPack)
ENDIF(DPKG_PROGRAM)
ENDIF(NOT WIN32 AND NOT APPLE) ENDIF(NOT WIN32 AND NOT APPLE)
if(WIN32) if(WIN32)

View File

@ -680,7 +680,7 @@ std::string creatureFlags(int flags)
if (flags & ESM::Creature::Walks) properties += "Walks "; if (flags & ESM::Creature::Walks) properties += "Walks ";
if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies "; if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Biped) properties += "Biped "; if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Respawn) properties += "Respawn ";
if (flags & ESM::Creature::Weapon) properties += "Weapon "; if (flags & ESM::Creature::Weapon) properties += "Weapon ";
if (flags & ESM::Creature::Skeleton) properties += "Skeleton "; if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
@ -691,7 +691,7 @@ std::string creatureFlags(int flags)
ESM::Creature::Walks| ESM::Creature::Walks|
ESM::Creature::Swims| ESM::Creature::Swims|
ESM::Creature::Flies| ESM::Creature::Flies|
ESM::Creature::Biped| ESM::Creature::Bipedal|
ESM::Creature::Respawn| ESM::Creature::Respawn|
ESM::Creature::Weapon| ESM::Creature::Weapon|
ESM::Creature::Skeleton| ESM::Creature::Skeleton|

View File

@ -623,6 +623,17 @@ MwIniImporter::MwIniImporter()
"Moons:Masser Fade Out Finish", "Moons:Masser Fade Out Finish",
"Moons:Script Color", "Moons:Script Color",
// blood
"Blood:Model 0",
"Blood:Model 1",
"Blood:Model 2",
"Blood:Texture 0",
"Blood:Texture 1",
"Blood:Texture 2",
"Blood:Texture Name 0",
"Blood:Texture Name 1",
"Blood:Texture Name 2",
0 0
}; };

View File

@ -181,7 +181,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
unsigned int mFlag; unsigned int mFlag;
} sCreatureFlagTable[] = } sCreatureFlagTable[] =
{ {
{ Columns::ColumnId_Biped, ESM::Creature::Biped }, { Columns::ColumnId_Biped, ESM::Creature::Bipedal },
{ Columns::ColumnId_HasWeapon, ESM::Creature::Weapon }, { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },
{ Columns::ColumnId_NoMovement, ESM::Creature::None }, { Columns::ColumnId_NoMovement, ESM::Creature::None },
{ Columns::ColumnId_Swims, ESM::Creature::Swims }, { Columns::ColumnId_Swims, ESM::Creature::Swims },

View File

@ -20,7 +20,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction characterpreview globalmap videoplayer ripplesimulation refraction
terrainstorage renderconst terrainstorage renderconst effectmanager
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

View File

@ -463,6 +463,9 @@ namespace MWBase
/// Spawn a random creature from a levelled list next to the player /// Spawn a random creature from a levelled list next to the player
virtual void spawnRandomCreature(const std::string& creatureList) = 0; virtual void spawnRandomCreature(const std::string& creatureList) = 0;
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
}; };
} }

View File

@ -69,6 +69,9 @@ namespace MWClass
fMaxFlySpeed = gmst.find("fMaxFlySpeed"); fMaxFlySpeed = gmst.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase"); fSwimRunBase = gmst.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
inited = true; inited = true;
} }
@ -173,6 +176,62 @@ namespace MWClass
void Creature::hit(const MWWorld::Ptr& ptr, int type) const void Creature::hit(const MWWorld::Ptr& ptr, int type) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
// TODO: where is the distance defined?
std::pair<MWWorld::Ptr, Ogre::Vector3> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 100);
if (result.first.isEmpty())
return; // Didn't hit anything
MWWorld::Ptr victim = result.first;
if (!victim.getClass().isActor())
return; // Can't hit non-actors
Ogre::Vector3 hitPosition = result.second;
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
float hitchance = ref->mBase->mData.mCombat +
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= stats.getFatigueTerm();
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
hitchance -= otherstats.getEvasion();
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
return;
}
int min,max;
switch (type)
{
case 0:
min = ref->mBase->mData.mAttack[0];
max = ref->mBase->mData.mAttack[1];
break;
case 1:
min = ref->mBase->mData.mAttack[2];
max = ref->mBase->mData.mAttack[3];
break;
case 2:
default:
min = ref->mBase->mData.mAttack[4];
max = ref->mBase->mData.mAttack[5];
break;
}
float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0);
// TODO: do not do this if the attack is blocked
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true);
} }
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
@ -199,6 +258,19 @@ namespace MWClass
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
} }
// Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{
getCreatureStats(ptr).setKnockedDown(true);
}
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(ishealth) if(ishealth)
{ {
if(damage > 0.0f) if(damage > 0.0f)
@ -301,7 +373,7 @@ namespace MWClass
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
const float normalizedEncumbrance = 0; //getEncumbrance(ptr) / getCapacity(ptr); const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
@ -530,6 +602,17 @@ namespace MWClass
} }
} }
int Creature::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::Creature::Metal)
return 2;
return 0;
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect; const ESM::GameSetting *Creature::fEncumberedMoveEffect;
@ -540,5 +623,8 @@ namespace MWClass
const ESM::GameSetting *Creature::fMaxFlySpeed; const ESM::GameSetting *Creature::fMaxFlySpeed;
const ESM::GameSetting *Creature::fSwimRunBase; const ESM::GameSetting *Creature::fSwimRunBase;
const ESM::GameSetting *Creature::fSwimRunAthleticsMult; const ESM::GameSetting *Creature::fSwimRunAthleticsMult;
const ESM::GameSetting *Creature::fKnockDownMult;
const ESM::GameSetting *Creature::iKnockDownOddsMult;
const ESM::GameSetting *Creature::iKnockDownOddsBase;
} }

View File

@ -24,6 +24,10 @@ namespace MWClass
static const ESM::GameSetting *fMaxFlySpeed; static const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fSwimRunBase; static const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fSwimRunAthleticsMult; static const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase;
public: public:
@ -111,6 +115,9 @@ namespace MWClass
virtual bool isFlying (const MWWorld::Ptr &ptr) const; virtual bool isFlying (const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
}; };
} }

View File

@ -454,8 +454,10 @@ namespace MWClass
float dist = 100.0f * (!weapon.isEmpty() ? float dist = 100.0f * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach : weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat()); gmst.find("fHandToHandReach")->getFloat());
// TODO: Use second to work out the hit angle and where to spawn the blood effect // TODO: Use second to work out the hit angle
MWWorld::Ptr victim = world->getHitContact(ptr, dist).first; std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
MWWorld::Ptr victim = result.first;
Ogre::Vector3 hitPosition = result.second;
if(victim.isEmpty()) // Didn't hit anything if(victim.isEmpty()) // Didn't hit anything
return; return;
@ -602,6 +604,10 @@ namespace MWClass
} }
} }
// TODO: do not do this if the attack is blocked
if (healthdmg)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
} }
@ -775,10 +781,10 @@ namespace MWClass
} }
if(getCreatureStats(ptr).isDead()) if(getCreatureStats(ptr).isDead())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true)); return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak))
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
if(get(ptr).getCreatureStats(ptr).isHostile()) if(get(ptr).getCreatureStats(ptr).isHostile())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}")); return boost::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}"));
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak))
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }
@ -1216,6 +1222,17 @@ namespace MWClass
return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified();
} }
int Npc::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
}
const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect; const ESM::GameSetting *Npc::fEncumberedMoveEffect;

View File

@ -142,6 +142,9 @@ namespace MWClass
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual bool isActor() const { virtual bool isActor() const {
return true; return true;
} }

View File

@ -267,7 +267,8 @@ namespace MWGui
else if ((mode == GM_Container) || (mode == GM_Inventory)) else if ((mode == GM_Container) || (mode == GM_Inventory))
{ {
// pick up object // pick up object
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); if (!object.isEmpty())
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
} }
} }
} }

View File

@ -301,6 +301,12 @@ namespace MWGui
if (type == Type_Magic) if (type == Type_Magic)
{ {
std::string spellId = button->getChildAt(0)->getUserString("Spell"); std::string spellId = button->getChildAt(0)->getUserString("Spell");
// Make sure the player still has this spell
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
if (!spells.hasSpell(spellId))
return;
store.setSelectedEnchantItem(store.end()); store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
} }

View File

@ -109,7 +109,10 @@ void MWMechanics::AiSequence::stack (const AiPackage& package)
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); it++) for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); it++)
{ {
if(mPackages.front()->getPriority() <= package.getPriority()) if(mPackages.front()->getPriority() <= package.getPriority())
{
mPackages.insert(it,package.clone()); mPackages.insert(it,package.clone());
return;
}
} }
if(mPackages.empty()) if(mPackages.empty())

View File

@ -6,6 +6,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include <OgreVector3.h> #include <OgreVector3.h>
@ -185,6 +186,14 @@ namespace MWMechanics
playIdle(actor, mPlayedIdle); playIdle(actor, mPlayedIdle);
mChooseAction = false; mChooseAction = false;
mIdleNow = true; mIdleNow = true;
// Play idle voiced dialogue entries randomly
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
float chance = store.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
// TODO: do not show subtitle messagebox if player is too far away? or do not say at all?
if (roll < chance)
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
} }
} }

View File

@ -479,9 +479,47 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
mPtr = ptr; mPtr = ptr;
} }
bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) bool CharacterController::updateCreatureState()
{ {
const MWWorld::Class &cls = MWWorld::Class::get(mPtr); const MWWorld::Class &cls = mPtr.getClass();
CreatureStats &stats = cls.getCreatureStats(mPtr);
if(stats.getAttackingOrSpell())
{
if(mUpperBodyState == UpperCharState_Nothing && mHitState == CharState_None)
{
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
switch (stats.getAttackType())
{
case CreatureStats::AT_Chop:
mCurrentWeapon = "attack1";
break;
case CreatureStats::AT_Slash:
mCurrentWeapon = "attack2";
break;
case CreatureStats::AT_Thrust:
mCurrentWeapon = "attack3";
break;
}
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1, "start", "stop",
0.0f, 0);
mUpperBodyState = UpperCharState_StartToMinAttack;
}
}
bool animPlaying = mAnimation->getInfo(mCurrentWeapon);
if (!animPlaying)
mUpperBodyState = UpperCharState_Nothing;
return false;
}
bool CharacterController::updateNpcState(bool inwater, bool isrunning)
{
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);
@ -583,8 +621,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
// This has to be done at the start of the casting animation, // This has to be done at the start of the casting animation,
// *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)
if (mPtr.getRefData().getHandle() == "player") if (mPtr.getRefData().getHandle() == "player")
stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell()); {
std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();
stats.getSpells().setSelectedSpell(selectedSpell);
}
std::string spellid = stats.getSpells().getSelectedSpell(); std::string spellid = stats.getSpells().getSelectedSpell();
if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr))
@ -1089,7 +1129,9 @@ void CharacterController::update(float duration)
} }
if(cls.isNpc()) if(cls.isNpc())
forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate;
else
forcestateupdate = updateCreatureState() || forcestateupdate;
refreshCurrentAnims(idlestate, movestate, forcestateupdate); refreshCurrentAnims(idlestate, movestate, forcestateupdate);

View File

@ -170,7 +170,8 @@ class CharacterController
void clearAnimQueue(); void clearAnimQueue();
bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); bool updateNpcState(bool inwater, bool isrunning);
bool updateCreatureState();
void updateVisibility(); void updateVisibility();

View File

@ -116,9 +116,9 @@ namespace MWMechanics
enum AttackType enum AttackType
{ {
AT_Chop,
AT_Slash, AT_Slash,
AT_Thrust, AT_Thrust
AT_Chop
}; };
void setAttackType(int attackType) { mAttackType = attackType; } void setAttackType(int attackType) { mAttackType = attackType; }
int getAttackType() { return mAttackType; } int getAttackType() { return mAttackType; }

View File

@ -44,6 +44,8 @@ namespace MWMechanics
TIterator end() const; TIterator end() const;
bool hasSpell(const std::string& spell) { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); }
void add (const std::string& spell); void add (const std::string& spell);
///< Adding a spell that is already listed in *this is a no-op. ///< Adding a spell that is already listed in *this is a no-op.

View File

@ -30,7 +30,7 @@
namespace MWRender namespace MWRender
{ {
Ogre::Real Animation::AnimationValue::getValue() const Ogre::Real Animation::AnimationTime::getValue() const
{ {
AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName); AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName);
if(iter != mAnimation->mStates.end()) if(iter != mAnimation->mStates.end())
@ -38,16 +38,16 @@ Ogre::Real Animation::AnimationValue::getValue() const
return 0.0f; return 0.0f;
} }
void Animation::AnimationValue::setValue(Ogre::Real) void Animation::AnimationTime::setValue(Ogre::Real)
{ {
} }
Ogre::Real Animation::EffectAnimationValue::getValue() const Ogre::Real Animation::EffectAnimationTime::getValue() const
{ {
return mTime; return mTime;
} }
void Animation::EffectAnimationValue::setValue(Ogre::Real) void Animation::EffectAnimationTime::setValue(Ogre::Real)
{ {
} }
@ -60,10 +60,10 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node)
, mNonAccumRoot(NULL) , mNonAccumRoot(NULL)
, mNonAccumCtrl(NULL) , mNonAccumCtrl(NULL)
, mAccumulate(0.0f) , mAccumulate(0.0f)
, mNullAnimationValuePtr(OGRE_NEW NullAnimationValue) , mNullAnimationTimePtr(OGRE_NEW NullAnimationTime)
{ {
for(size_t i = 0;i < sNumGroups;i++) for(size_t i = 0;i < sNumGroups;i++)
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this));
} }
Animation::~Animation() Animation::~Animation()
@ -139,7 +139,7 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly)
for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) for(size_t i = 0;i < mObjectRoot->mControllers.size();i++)
{ {
if(mObjectRoot->mControllers[i].getSource().isNull()) if(mObjectRoot->mControllers[i].getSource().isNull())
mObjectRoot->mControllers[i].setSource(mAnimationValuePtr[0]); mObjectRoot->mControllers[i].setSource(mAnimationTimePtr[0]);
} }
} }
@ -286,7 +286,7 @@ void Animation::addAnimSource(const std::string &model)
} }
} }
ctrls[i].setSource(mAnimationValuePtr[grp]); ctrls[i].setSource(mAnimationTimePtr[grp]);
grpctrls[grp].push_back(ctrls[i]); grpctrls[grp].push_back(ctrls[i]);
} }
} }
@ -296,7 +296,7 @@ void Animation::clearAnimSources()
mStates.clear(); mStates.clear();
for(size_t i = 0;i < sNumGroups;i++) for(size_t i = 0;i < sNumGroups;i++)
mAnimationValuePtr[i]->setAnimName(std::string()); mAnimationTimePtr[i]->setAnimName(std::string());
mNonAccumCtrl = NULL; mNonAccumCtrl = NULL;
@ -660,13 +660,22 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co
else if(evt.compare(off, len, "unequip detach") == 0) else if(evt.compare(off, len, "unequip detach") == 0)
showWeapons(false); showWeapons(false);
else if(evt.compare(off, len, "chop hit") == 0) else if(evt.compare(off, len, "chop hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Chop); mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop);
else if(evt.compare(off, len, "slash hit") == 0) else if(evt.compare(off, len, "slash hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash); mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash);
else if(evt.compare(off, len, "thrust hit") == 0) else if(evt.compare(off, len, "thrust hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
else if(evt.compare(off, len, "hit") == 0) else if(evt.compare(off, len, "hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr); {
if (groupname == "attack1")
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop);
else if (groupname == "attack2")
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash);
else if (groupname == "attack3")
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
else
mPtr.getClass().hit(mPtr);
}
else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release")
MWBase::Environment::get().getWorld()->castSpell(mPtr); MWBase::Environment::get().getWorld()->castSpell(mPtr);
@ -789,7 +798,7 @@ void Animation::resetActiveGroups()
active = state; active = state;
} }
mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ? mAnimationTimePtr[grp]->setAnimName((active == mStates.end()) ?
std::string() : active->first); std::string() : active->first);
} }
mNonAccumCtrl = NULL; mNonAccumCtrl = NULL;
@ -797,7 +806,7 @@ void Animation::resetActiveGroups()
if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f))
return; return;
AnimStateMap::const_iterator state = mStates.find(mAnimationValuePtr[0]->getAnimName()); AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName());
if(state == mStates.end()) if(state == mStates.end())
return; return;
@ -869,13 +878,13 @@ Ogre::Vector3 Animation::runAnimation(float duration)
targetTime = state.mTime + timepassed; targetTime = state.mTime + timepassed;
if(textkey == textkeys.end() || textkey->first > targetTime) if(textkey == textkeys.end() || textkey->first > targetTime)
{ {
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName())
updatePosition(state.mTime, targetTime, movement); updatePosition(state.mTime, targetTime, movement);
state.mTime = std::min(targetTime, state.mStopTime); state.mTime = std::min(targetTime, state.mStopTime);
} }
else else
{ {
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName())
updatePosition(state.mTime, textkey->first, movement); updatePosition(state.mTime, textkey->first, movement);
state.mTime = textkey->first; state.mTime = textkey->first;
} }
@ -926,7 +935,7 @@ Ogre::Vector3 Animation::runAnimation(float duration)
// Apply group controllers // Apply group controllers
for(size_t grp = 0;grp < sNumGroups;grp++) for(size_t grp = 0;grp < sNumGroups;grp++)
{ {
const std::string &name = mAnimationValuePtr[grp]->getAnimName(); const std::string &name = mAnimationTimePtr[grp]->getAnimName();
if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end()) if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end())
{ {
const Ogre::SharedPtr<AnimSource> &src = stateiter->second.mSource; const Ogre::SharedPtr<AnimSource> &src = stateiter->second.mSource;
@ -1042,6 +1051,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
else else
params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
// TODO: turn off shadow casting
setRenderProperties(params.mObjects, RV_Misc, setRenderProperties(params.mObjects, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL); RQG_Main, RQG_Alpha, 0.f, false, NULL);
@ -1052,7 +1062,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
for(size_t i = 0;i < params.mObjects->mControllers.size();i++) for(size_t i = 0;i < params.mObjects->mControllers.size();i++)
{ {
if(params.mObjects->mControllers[i].getSource().isNull()) if(params.mObjects->mControllers[i].getSource().isNull())
params.mObjects->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationValue> (new EffectAnimationValue())); params.mObjects->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationTime> (new EffectAnimationTime()));
} }
if (!texture.empty()) if (!texture.empty())
@ -1110,7 +1120,7 @@ void Animation::updateEffects(float duration)
NifOgre::ObjectScenePtr objects = it->mObjects; NifOgre::ObjectScenePtr objects = it->mObjects;
for(size_t i = 0; i < objects->mControllers.size() ;i++) for(size_t i = 0; i < objects->mControllers.size() ;i++)
{ {
EffectAnimationValue* value = dynamic_cast<EffectAnimationValue*>(objects->mControllers[i].getSource().get()); EffectAnimationTime* value = dynamic_cast<EffectAnimationTime*>(objects->mControllers[i].getSource().get());
if (value) if (value)
value->addTime(duration); value->addTime(duration);
@ -1125,7 +1135,7 @@ void Animation::updateEffects(float duration)
float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength; float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength;
for(size_t i = 0; i < objects->mControllers.size() ;i++) for(size_t i = 0; i < objects->mControllers.size() ;i++)
{ {
EffectAnimationValue* value = dynamic_cast<EffectAnimationValue*>(objects->mControllers[i].getSource().get()); EffectAnimationTime* value = dynamic_cast<EffectAnimationTime*>(objects->mControllers[i].getSource().get());
if (value) if (value)
value->resetTime(remainder); value->resetTime(remainder);
} }

View File

@ -32,14 +32,14 @@ protected:
/* This is the number of *discrete* groups. */ /* This is the number of *discrete* groups. */
static const size_t sNumGroups = 4; static const size_t sNumGroups = 4;
class AnimationValue : public Ogre::ControllerValue<Ogre::Real> class AnimationTime : public Ogre::ControllerValue<Ogre::Real>
{ {
private: private:
Animation *mAnimation; Animation *mAnimation;
std::string mAnimationName; std::string mAnimationName;
public: public:
AnimationValue(Animation *anim) AnimationTime(Animation *anim)
: mAnimation(anim) : mAnimation(anim)
{ } { }
@ -52,12 +52,12 @@ protected:
virtual void setValue(Ogre::Real value); virtual void setValue(Ogre::Real value);
}; };
class EffectAnimationValue : public Ogre::ControllerValue<Ogre::Real> class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{ {
private: private:
float mTime; float mTime;
public: public:
EffectAnimationValue() : mTime(0) { } EffectAnimationTime() : mTime(0) { }
void addTime(float time) { mTime += time; } void addTime(float time) { mTime += time; }
void resetTime(float value) { mTime = value; } void resetTime(float value) { mTime = value; }
@ -67,7 +67,7 @@ protected:
class NullAnimationValue : public Ogre::ControllerValue<Ogre::Real> class NullAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{ {
public: public:
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const
@ -134,8 +134,8 @@ protected:
AnimStateMap mStates; AnimStateMap mStates;
Ogre::SharedPtr<AnimationValue> mAnimationValuePtr[sNumGroups]; Ogre::SharedPtr<AnimationTime> mAnimationTimePtr[sNumGroups];
Ogre::SharedPtr<NullAnimationValue> mNullAnimationValuePtr; Ogre::SharedPtr<NullAnimationTime> mNullAnimationTimePtr;
ObjectAttachMap mAttachedObjects; ObjectAttachMap mAttachedObjects;
@ -189,16 +189,18 @@ protected:
/** Adds an additional light to the given object list using the specified ESM record. */ /** Adds an additional light to the given object list using the specified ESM record. */
void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light); void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light);
static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue,
Ogre::uint8 transqueue, Ogre::Real dist=0.0f,
bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL);
void clearAnimSources(); void clearAnimSources();
// TODO: Should not be here // TODO: Should not be here
Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item); Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item);
public: public:
// FIXME: Move outside of this class
static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue,
Ogre::uint8 transqueue, Ogre::Real dist=0.0f,
bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL);
Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node);
virtual ~Animation(); virtual ~Animation();

View File

@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
setObjectRoot(model, false); setObjectRoot(model, false);
setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha);
if((ref->mBase->mFlags&ESM::Creature::Biped)) if((ref->mBase->mFlags&ESM::Creature::Bipedal))
addAnimSource("meshes\\base_anim.nif"); addAnimSource("meshes\\base_anim.nif");
addAnimSource(model); addAnimSource(model);
} }

View File

@ -0,0 +1,117 @@
#include "effectmanager.hpp"
#include <OgreSceneManager.h>
#include <OgreParticleSystem.h>
#include "animation.hpp"
#include "renderconst.hpp"
namespace MWRender
{
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
float mTime;
public:
EffectAnimationTime() : mTime(0) { }
void addTime(float time) { mTime += time; }
virtual Ogre::Real getValue() const { return mTime; }
virtual void setValue(Ogre::Real value) {}
};
EffectManager::EffectManager(Ogre::SceneManager *sceneMgr)
: mSceneMgr(sceneMgr)
{
}
void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition)
{
Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition);
// fix texture extension to .dds
if (textureOverride.size() > 4)
{
textureOverride[textureOverride.size()-3] = 'd';
textureOverride[textureOverride.size()-2] = 'd';
textureOverride[textureOverride.size()-1] = 's';
}
NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model);
// TODO: turn off shadow casting
MWRender::Animation::setRenderProperties(scene, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL);
for(size_t i = 0;i < scene->mControllers.size();i++)
{
if(scene->mControllers[i].getSource().isNull())
scene->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationTime> (new EffectAnimationTime()));
}
if (!textureOverride.empty())
{
for(size_t i = 0;i < scene->mParticles.size(); ++i)
{
Ogre::ParticleSystem* partSys = scene->mParticles[i];
Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(partSys);
for (int t=0; t<mat->getNumTechniques(); ++t)
{
Ogre::Technique* tech = mat->getTechnique(t);
for (int p=0; p<tech->getNumPasses(); ++p)
{
Ogre::Pass* pass = tech->getPass(p);
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
tus->setTextureName("textures\\" + textureOverride);
}
}
}
}
}
mEffects.push_back(std::make_pair(sceneNode, scene));
}
void EffectManager::update(float dt)
{
for (std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> >::iterator it = mEffects.begin(); it != mEffects.end(); )
{
NifOgre::ObjectScenePtr objects = it->second;
for(size_t i = 0; i < objects->mControllers.size() ;i++)
{
EffectAnimationTime* value = dynamic_cast<EffectAnimationTime*>(objects->mControllers[i].getSource().get());
if (value)
value->addTime(dt);
objects->mControllers[i].update();
}
// Finished playing?
if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength)
{
Ogre::SceneNode* node = it->first;
it = mEffects.erase(it);
mSceneMgr->destroySceneNode(node);
continue;
}
++it;
}
}
void EffectManager::clear()
{
for (std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> >::iterator it = mEffects.begin(); it != mEffects.end(); )
{
Ogre::SceneNode* node = it->first;
it = mEffects.erase(it);
mSceneMgr->destroySceneNode(node);
}
}
}

View File

@ -0,0 +1,31 @@
#ifndef OPENMW_MWRENDER_EFFECTMANAGER_H
#define OPENMW_MWRENDER_EFFECTMANAGER_H
#include <components/nifogre/ogrenifloader.hpp>
namespace MWRender
{
// Note: effects attached to another object should be managed by MWRender::Animation::addEffect.
// This class manages "free" effects, i.e. attached to a dedicated scene node in the world.
class EffectManager
{
public:
EffectManager(Ogre::SceneManager* sceneMgr);
~EffectManager() { clear(); }
/// Add an effect. When it's finished playing, it will be removed automatically.
void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition);
void update(float dt);
/// Remove all effects
void clear();
private:
std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> > mEffects;
Ogre::SceneManager* mSceneMgr;
};
}
#endif

View File

@ -61,8 +61,9 @@ std::string getVampireHead(const std::string& race, bool female)
namespace MWRender namespace MWRender
{ {
float SayAnimationValue::getValue() const float HeadAnimationTime::getValue() const
{ {
// TODO: Handle eye blinking (time is in the text keys)
if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) if (MWBase::Environment::get().getSoundManager()->sayDone(mReference))
return 0; return 0;
else else
@ -124,7 +125,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
{ {
mNpc = mPtr.get<ESM::NPC>()->mBase; mNpc = mPtr.get<ESM::NPC>()->mBase;
mSayAnimationValue = Ogre::SharedPtr<SayAnimationValue>(new SayAnimationValue(mPtr)); mHeadAnimationTime = Ogre::SharedPtr<HeadAnimationTime>(new HeadAnimationTime(mPtr));
for(size_t i = 0;i < ESM::PRT_Count;i++) for(size_t i = 0;i < ESM::PRT_Count;i++)
{ {
@ -595,10 +596,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
{ {
if(ctrl->getSource().isNull()) if(ctrl->getSource().isNull())
{ {
ctrl->setSource(mNullAnimationValuePtr); ctrl->setSource(mNullAnimationTimePtr);
if (type == ESM::PRT_Head) if (type == ESM::PRT_Head)
ctrl->setSource(mSayAnimationValue); ctrl->setSource(mHeadAnimationTime);
} }
} }

View File

@ -13,12 +13,12 @@ namespace ESM
namespace MWRender namespace MWRender
{ {
class SayAnimationValue : public Ogre::ControllerValue<Ogre::Real> class HeadAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{ {
private: private:
MWWorld::Ptr mReference; MWWorld::Ptr mReference;
public: public:
SayAnimationValue(MWWorld::Ptr reference) : mReference(reference) {} HeadAnimationTime(MWWorld::Ptr reference) : mReference(reference) {}
virtual Ogre::Real getValue() const; virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value) virtual void setValue(Ogre::Real value)
@ -70,7 +70,7 @@ private:
Ogre::Vector3 mFirstPersonOffset; Ogre::Vector3 mFirstPersonOffset;
Ogre::SharedPtr<SayAnimationValue> mSayAnimationValue; Ogre::SharedPtr<HeadAnimationTime> mHeadAnimationTime;
float mAlpha; float mAlpha;

View File

@ -41,6 +41,7 @@
#include "globalmap.hpp" #include "globalmap.hpp"
#include "videoplayer.hpp" #include "videoplayer.hpp"
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "effectmanager.hpp"
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -57,9 +58,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
, mSunEnabled(0) , mSunEnabled(0)
, mPhysicsEngine(engine) , mPhysicsEngine(engine)
, mTerrain(NULL) , mTerrain(NULL)
, mEffectManager(NULL)
{ {
mActors = new MWRender::Actors(mRendering, this); mActors = new MWRender::Actors(mRendering, this);
mObjects = new MWRender::Objects(mRendering); mObjects = new MWRender::Objects(mRendering);
mEffectManager = new EffectManager(mRendering.getScene());
// select best shader mode // select best shader mode
bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos);
bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos); bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos);
@ -193,6 +196,7 @@ RenderingManager::~RenderingManager ()
delete mVideoPlayer; delete mVideoPlayer;
delete mActors; delete mActors;
delete mObjects; delete mObjects;
delete mEffectManager;
delete mFactory; delete mFactory;
} }
@ -374,6 +378,8 @@ void RenderingManager::update (float duration, bool paused)
if(paused) if(paused)
return; return;
mEffectManager->update(duration);
mActors->update (mRendering.getCamera()); mActors->update (mRendering.getCamera());
mPlayerAnimation->preRender(mRendering.getCamera()); mPlayerAnimation->preRender(mRendering.getCamera());
mObjects->update (duration, mRendering.getCamera()); mObjects->update (duration, mRendering.getCamera());
@ -675,14 +681,14 @@ Shadows* RenderingManager::getShadows()
void RenderingManager::switchToInterior() void RenderingManager::switchToInterior()
{ {
// causes light flicker in opengl when moving.. // TODO: also do this when switching worldspace
//mRendering.getScene()->setCameraRelativeRendering(false); mEffectManager->clear();
} }
void RenderingManager::switchToExterior() void RenderingManager::switchToExterior()
{ {
// causes light flicker in opengl when moving.. // TODO: also do this when switching worldspace
//mRendering.getScene()->setCameraRelativeRendering(true); mEffectManager->clear();
} }
Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds) Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds)
@ -1020,4 +1026,9 @@ float RenderingManager::getCameraDistance() const
return mCamera->getCameraDistance(); return mCamera->getCameraDistance();
} }
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition)
{
mEffectManager->addEffect(model, texture, worldPosition);
}
} // namespace } // namespace

View File

@ -21,10 +21,7 @@
namespace Ogre namespace Ogre
{ {
class SceneManager;
class SceneNode; class SceneNode;
class Quaternion;
class Vector3;
} }
namespace MWWorld namespace MWWorld
@ -51,6 +48,7 @@ namespace MWRender
class GlobalMap; class GlobalMap;
class VideoPlayer; class VideoPlayer;
class Animation; class Animation;
class EffectManager;
class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener
{ {
@ -209,6 +207,8 @@ public:
void stopVideo(); void stopVideo();
void frameStarted(float dt, bool paused); void frameStarted(float dt, bool paused);
void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition);
protected: protected:
virtual void windowResized(int x, int y); virtual void windowResized(int x, int y);
@ -239,6 +239,8 @@ private:
MWRender::Objects* mObjects; MWRender::Objects* mObjects;
MWRender::Actors* mActors; MWRender::Actors* mActors;
MWRender::EffectManager* mEffectManager;
MWRender::NpcAnimation *mPlayerAnimation; MWRender::NpcAnimation *mPlayerAnimation;
// 0 normal, 1 more bright, 2 max // 0 normal, 1 more bright, 2 max

View File

@ -248,14 +248,13 @@ namespace MWSound
return; return;
try try
{ {
// The range values are not tested
float basevol = volumeFromType(Play_TypeVoice); float basevol = volumeFromType(Play_TypeVoice);
std::string filePath = "Sound/"+filename; std::string filePath = "Sound/"+filename;
const ESM::Position &pos = ptr.getRefData().getPosition(); const ESM::Position &pos = ptr.getRefData().getPosition();
const Ogre::Vector3 objpos(pos.pos); const Ogre::Vector3 objpos(pos.pos);
MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f,
20.0f, 12750.0f, Play_Normal|Play_TypeVoice, 0); 20.0f, 1500.0f, Play_Normal|Play_TypeVoice, 0);
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
} }
catch(std::exception &e) catch(std::exception &e)

View File

@ -363,4 +363,8 @@ namespace MWWorld
throw std::runtime_error("class does not support skills"); throw std::runtime_error("class does not support skills");
} }
int Class::getBloodTexture (const MWWorld::Ptr& ptr) const
{
throw std::runtime_error("class does not support gore");
}
} }

View File

@ -278,6 +278,9 @@ namespace MWWorld
virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; }
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual Ptr virtual Ptr
copyToCell(const Ptr &ptr, CellStore &cell) const; copyToCell(const Ptr &ptr, CellStore &cell) const;

View File

@ -149,7 +149,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
item.getCellRef().mPos.pos[1] = 0; item.getCellRef().mPos.pos[1] = 0;
item.getCellRef().mPos.pos[2] = 0; item.getCellRef().mPos.pos[2] = 0;
if (setOwner) if (setOwner && actorPtr.getClass().isActor())
item.getCellRef().mOwner = actorPtr.getCellRef().mRefID; item.getCellRef().mOwner = actorPtr.getCellRef().mRefID;
std::string script = MWWorld::Class::get(item).getScript(item); std::string script = MWWorld::Class::get(item).getScript(item);

View File

@ -108,7 +108,7 @@ namespace MWWorld
} }
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
bool isFlying, float waterlevel, OEngine::Physic::PhysicEngine *engine) bool isFlying, float waterlevel, float slowFall, OEngine::Physic::PhysicEngine *engine)
{ {
const ESM::Position &refpos = ptr.getRefData().getPosition(); const ESM::Position &refpos = ptr.getRefData().getPosition();
Ogre::Vector3 position(refpos.pos); Ogre::Vector3 position(refpos.pos);
@ -229,7 +229,10 @@ namespace MWWorld
physicActor->setInertialForce(Ogre::Vector3(0.0f)); physicActor->setInertialForce(Ogre::Vector3(0.0f));
else else
{ {
inertia.z += time*-627.2f; float diff = time*-627.2f;
if (inertia.z < 0)
diff *= slowFall;
inertia.z += diff;
physicActor->setInertialForce(inertia); physicActor->setInertialForce(inertia);
} }
physicActor->setOnGround(isOnGround); physicActor->setOnGround(isOnGround);
@ -577,9 +580,10 @@ namespace MWWorld
float oldHeight = iter->first.getRefData().getPosition().pos[2]; float oldHeight = iter->first.getRefData().getPosition().pos[2];
const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects();
bool waterCollision = false; bool waterCollision = false;
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects() if (effects.get(ESM::MagicEffect::WaterWalking).mMagnitude
.get(ESM::MagicEffect::WaterWalking).mMagnitude
&& cell->hasWater() && cell->hasWater()
&& !world->isUnderwater(iter->first.getCell(), && !world->isUnderwater(iter->first.getCell(),
Ogre::Vector3(iter->first.getRefData().getPosition().pos))) Ogre::Vector3(iter->first.getRefData().getPosition().pos)))
@ -592,9 +596,12 @@ namespace MWWorld
if (waterCollision) if (waterCollision)
mEngine->dynamicsWorld->addCollisionObject(&object); mEngine->dynamicsWorld->addCollisionObject(&object);
// 100 points of slowfall reduce gravity by 90% (this is just a guess)
float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f);
Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum,
world->isFlying(iter->first), world->isFlying(iter->first),
waterlevel, mEngine); waterlevel, slowFall, mEngine);
if (waterCollision) if (waterCollision)
mEngine->dynamicsWorld->removeCollisionObject(&object); mEngine->dynamicsWorld->removeCollisionObject(&object);

View File

@ -2282,7 +2282,8 @@ namespace MWWorld
void World::breakInvisibility(const Ptr &actor) void World::breakInvisibility(const Ptr &actor)
{ {
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility);
actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); if (actor.getClass().isNpc())
actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility);
} }
bool World::isDark() const bool World::isDark() const
@ -2610,4 +2611,31 @@ namespace MWWorld
safePlaceObject(ref.getPtr(),*cell,ipos); safePlaceObject(ref.getPtr(),*cell,ipos);
} }
} }
void World::spawnBloodEffect(const Ptr &ptr, const Vector3 &worldPosition)
{
int type = ptr.getClass().getBloodTexture(ptr);
std::string texture;
switch (type)
{
case 2:
texture = getFallback()->getFallbackString("Blood_Texture_2");
break;
case 1:
texture = getFallback()->getFallbackString("Blood_Texture_1");
break;
case 0:
default:
texture = getFallback()->getFallbackString("Blood_Texture_0");
break;
}
std::stringstream modelName;
modelName << "Blood_Model_";
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 3; // [0, 2]
modelName << roll;
std::string model = "meshes\\" + getFallback()->getFallbackString(modelName.str());
mRendering->spawnEffect(model, texture, worldPosition);
}
} }

View File

@ -549,6 +549,9 @@ namespace MWWorld
/// Spawn a random creature from a levelled list next to the player /// Spawn a random creature from a levelled list next to the player
virtual void spawnRandomCreature(const std::string& creatureList); virtual void spawnRandomCreature(const std::string& creatureList);
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition);
}; };
} }

View File

@ -25,16 +25,20 @@ struct Creature
// Default is 0x48? // Default is 0x48?
enum Flags enum Flags
{ {
Biped = 0x001, // Movement types
Respawn = 0x002, Bipedal = 0x001,
Weapon = 0x004, // Has weapon and shield
None = 0x008, // ??
Swims = 0x010, Swims = 0x010,
Flies = 0x020, // Don't know what happens if several Flies = 0x020, // Don't know what happens if several
Walks = 0x040, // of these are set Walks = 0x040, // of these are set
Respawn = 0x002,
Weapon = 0x004, // Has weapon and shield
None = 0x008, // ??
Essential = 0x080, Essential = 0x080,
Skeleton = 0x400, // Does not have normal blood
Metal = 0x800 // Has 'golden' blood // Blood types
Skeleton = 0x400,
Metal = 0x800
}; };
enum Type enum Type