forked from mirror/openmw-tes3mp
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
a11a6b616b
35 changed files with 493 additions and 123 deletions
|
@ -186,8 +186,6 @@ if (WIN32)
|
|||
add_definitions(-DSDL_MAIN_HANDLED)
|
||||
else (WIN32)
|
||||
set(PLATFORM_INCLUDE_DIR "")
|
||||
find_path (UUID_INCLUDE_DIR uuid/uuid.h)
|
||||
include_directories(${UUID_INCLUDE_DIR})
|
||||
endif (WIN32)
|
||||
if (MSVC10)
|
||||
set(PLATFORM_INCLUDE_DIR "")
|
||||
|
@ -239,7 +237,6 @@ include_directories("."
|
|||
${MYGUI_INCLUDE_DIRS}
|
||||
${MYGUI_PLATFORM_INCLUDE_DIRS}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${UUID_INCLUDE_DIR}
|
||||
${LIBDIR}
|
||||
)
|
||||
|
||||
|
@ -409,46 +406,6 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
# Install 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")
|
||||
|
||||
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)
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
@ -680,7 +680,7 @@ std::string creatureFlags(int flags)
|
|||
if (flags & ESM::Creature::Walks) properties += "Walks ";
|
||||
if (flags & ESM::Creature::Swims) properties += "Swims ";
|
||||
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::Weapon) properties += "Weapon ";
|
||||
if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
|
||||
|
@ -691,7 +691,7 @@ std::string creatureFlags(int flags)
|
|||
ESM::Creature::Walks|
|
||||
ESM::Creature::Swims|
|
||||
ESM::Creature::Flies|
|
||||
ESM::Creature::Biped|
|
||||
ESM::Creature::Bipedal|
|
||||
ESM::Creature::Respawn|
|
||||
ESM::Creature::Weapon|
|
||||
ESM::Creature::Skeleton|
|
||||
|
|
|
@ -623,6 +623,17 @@ MwIniImporter::MwIniImporter()
|
|||
"Moons:Masser Fade Out Finish",
|
||||
"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
|
||||
};
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
|
|||
unsigned int mFlag;
|
||||
} sCreatureFlagTable[] =
|
||||
{
|
||||
{ Columns::ColumnId_Biped, ESM::Creature::Biped },
|
||||
{ Columns::ColumnId_Biped, ESM::Creature::Bipedal },
|
||||
{ Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },
|
||||
{ Columns::ColumnId_NoMovement, ESM::Creature::None },
|
||||
{ Columns::ColumnId_Swims, ESM::Creature::Swims },
|
||||
|
|
|
@ -20,7 +20,7 @@ add_openmw_dir (mwrender
|
|||
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||
actors objects renderinginterface localmap occlusionquery water shadows
|
||||
characterpreview globalmap videoplayer ripplesimulation refraction
|
||||
terrainstorage renderconst
|
||||
terrainstorage renderconst effectmanager
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
|
|
@ -463,6 +463,9 @@ namespace MWBase
|
|||
|
||||
/// Spawn a random creature from a levelled list next to the player
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,9 @@ namespace MWClass
|
|||
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
|
||||
fSwimRunBase = gmst.find("fSwimRunBase");
|
||||
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
|
||||
fKnockDownMult = gmst.find("fKnockDownMult");
|
||||
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
|
||||
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
|
||||
|
||||
inited = true;
|
||||
}
|
||||
|
@ -173,6 +176,62 @@ namespace MWClass
|
|||
|
||||
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
|
||||
|
@ -199,6 +258,19 @@ namespace MWClass
|
|||
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(damage > 0.0f)
|
||||
|
@ -301,7 +373,7 @@ namespace MWClass
|
|||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
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);
|
||||
|
||||
|
@ -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::fMaxWalkSpeedCreature;
|
||||
const ESM::GameSetting *Creature::fEncumberedMoveEffect;
|
||||
|
@ -540,5 +623,8 @@ namespace MWClass
|
|||
const ESM::GameSetting *Creature::fMaxFlySpeed;
|
||||
const ESM::GameSetting *Creature::fSwimRunBase;
|
||||
const ESM::GameSetting *Creature::fSwimRunAthleticsMult;
|
||||
const ESM::GameSetting *Creature::fKnockDownMult;
|
||||
const ESM::GameSetting *Creature::iKnockDownOddsMult;
|
||||
const ESM::GameSetting *Creature::iKnockDownOddsBase;
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,10 @@ namespace MWClass
|
|||
static const ESM::GameSetting *fMaxFlySpeed;
|
||||
static const ESM::GameSetting *fSwimRunBase;
|
||||
static const ESM::GameSetting *fSwimRunAthleticsMult;
|
||||
static const ESM::GameSetting *fKnockDownMult;
|
||||
static const ESM::GameSetting *iKnockDownOddsMult;
|
||||
static const ESM::GameSetting *iKnockDownOddsBase;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
@ -111,6 +115,9 @@ namespace MWClass
|
|||
virtual bool isFlying (const MWWorld::Ptr &ptr) 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -454,8 +454,10 @@ namespace MWClass
|
|||
float dist = 100.0f * (!weapon.isEmpty() ?
|
||||
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
||||
gmst.find("fHandToHandReach")->getFloat());
|
||||
// TODO: Use second to work out the hit angle and where to spawn the blood effect
|
||||
MWWorld::Ptr victim = world->getHitContact(ptr, dist).first;
|
||||
// TODO: Use second to work out the hit angle
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -775,10 +781,10 @@ namespace MWClass
|
|||
}
|
||||
if(getCreatureStats(ptr).isDead())
|
||||
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())
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -1216,6 +1222,17 @@ namespace MWClass
|
|||
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::fMaxWalkSpeed;
|
||||
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
|
||||
|
|
|
@ -142,6 +142,9 @@ namespace MWClass
|
|||
|
||||
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 {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -267,7 +267,8 @@ namespace MWGui
|
|||
else if ((mode == GM_Container) || (mode == GM_Inventory))
|
||||
{
|
||||
// pick up object
|
||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
|
||||
if (!object.isEmpty())
|
||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -301,6 +301,12 @@ namespace MWGui
|
|||
if (type == Type_Magic)
|
||||
{
|
||||
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());
|
||||
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
|
||||
}
|
||||
|
|
|
@ -109,7 +109,10 @@ void MWMechanics::AiSequence::stack (const AiPackage& package)
|
|||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); it++)
|
||||
{
|
||||
if(mPackages.front()->getPriority() <= package.getPriority())
|
||||
{
|
||||
mPackages.insert(it,package.clone());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(mPackages.empty())
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
#include <OgreVector3.h>
|
||||
|
@ -185,6 +186,14 @@ namespace MWMechanics
|
|||
playIdle(actor, mPlayedIdle);
|
||||
mChooseAction = false;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -479,9 +479,47 @@ void CharacterController::updatePtr(const MWWorld::Ptr &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);
|
||||
WeaponType weaptype = WeapType_None;
|
||||
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,
|
||||
// *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)
|
||||
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();
|
||||
|
||||
if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr))
|
||||
|
@ -1089,7 +1129,9 @@ void CharacterController::update(float duration)
|
|||
}
|
||||
|
||||
if(cls.isNpc())
|
||||
forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate;
|
||||
forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate;
|
||||
else
|
||||
forcestateupdate = updateCreatureState() || forcestateupdate;
|
||||
|
||||
refreshCurrentAnims(idlestate, movestate, forcestateupdate);
|
||||
|
||||
|
|
|
@ -170,7 +170,8 @@ class CharacterController
|
|||
|
||||
void clearAnimQueue();
|
||||
|
||||
bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak);
|
||||
bool updateNpcState(bool inwater, bool isrunning);
|
||||
bool updateCreatureState();
|
||||
|
||||
void updateVisibility();
|
||||
|
||||
|
|
|
@ -116,9 +116,9 @@ namespace MWMechanics
|
|||
|
||||
enum AttackType
|
||||
{
|
||||
AT_Chop,
|
||||
AT_Slash,
|
||||
AT_Thrust,
|
||||
AT_Chop
|
||||
AT_Thrust
|
||||
};
|
||||
void setAttackType(int attackType) { mAttackType = attackType; }
|
||||
int getAttackType() { return mAttackType; }
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace MWMechanics
|
|||
|
||||
TIterator end() const;
|
||||
|
||||
bool hasSpell(const std::string& spell) { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); }
|
||||
|
||||
void add (const std::string& spell);
|
||||
///< Adding a spell that is already listed in *this is a no-op.
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
namespace MWRender
|
||||
{
|
||||
|
||||
Ogre::Real Animation::AnimationValue::getValue() const
|
||||
Ogre::Real Animation::AnimationTime::getValue() const
|
||||
{
|
||||
AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName);
|
||||
if(iter != mAnimation->mStates.end())
|
||||
|
@ -38,16 +38,16 @@ Ogre::Real Animation::AnimationValue::getValue() const
|
|||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
, mNonAccumCtrl(NULL)
|
||||
, mAccumulate(0.0f)
|
||||
, mNullAnimationValuePtr(OGRE_NEW NullAnimationValue)
|
||||
, mNullAnimationTimePtr(OGRE_NEW NullAnimationTime)
|
||||
{
|
||||
for(size_t i = 0;i < sNumGroups;i++)
|
||||
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this));
|
||||
mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this));
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ void Animation::clearAnimSources()
|
|||
mStates.clear();
|
||||
|
||||
for(size_t i = 0;i < sNumGroups;i++)
|
||||
mAnimationValuePtr[i]->setAnimName(std::string());
|
||||
mAnimationTimePtr[i]->setAnimName(std::string());
|
||||
|
||||
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)
|
||||
showWeapons(false);
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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")
|
||||
MWBase::Environment::get().getWorld()->castSpell(mPtr);
|
||||
|
@ -789,7 +798,7 @@ void Animation::resetActiveGroups()
|
|||
active = state;
|
||||
}
|
||||
|
||||
mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ?
|
||||
mAnimationTimePtr[grp]->setAnimName((active == mStates.end()) ?
|
||||
std::string() : active->first);
|
||||
}
|
||||
mNonAccumCtrl = NULL;
|
||||
|
@ -797,7 +806,7 @@ void Animation::resetActiveGroups()
|
|||
if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f))
|
||||
return;
|
||||
|
||||
AnimStateMap::const_iterator state = mStates.find(mAnimationValuePtr[0]->getAnimName());
|
||||
AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName());
|
||||
if(state == mStates.end())
|
||||
return;
|
||||
|
||||
|
@ -869,13 +878,13 @@ Ogre::Vector3 Animation::runAnimation(float duration)
|
|||
targetTime = state.mTime + timepassed;
|
||||
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);
|
||||
state.mTime = std::min(targetTime, state.mStopTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
||||
if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName())
|
||||
updatePosition(state.mTime, textkey->first, movement);
|
||||
state.mTime = textkey->first;
|
||||
}
|
||||
|
@ -926,7 +935,7 @@ Ogre::Vector3 Animation::runAnimation(float duration)
|
|||
// Apply group controllers
|
||||
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())
|
||||
{
|
||||
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
|
||||
params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
|
||||
|
||||
// TODO: turn off shadow casting
|
||||
setRenderProperties(params.mObjects, RV_Misc,
|
||||
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++)
|
||||
{
|
||||
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())
|
||||
|
@ -1110,7 +1120,7 @@ void Animation::updateEffects(float duration)
|
|||
NifOgre::ObjectScenePtr objects = it->mObjects;
|
||||
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)
|
||||
value->addTime(duration);
|
||||
|
||||
|
@ -1125,7 +1135,7 @@ void Animation::updateEffects(float duration)
|
|||
float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength;
|
||||
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)
|
||||
value->resetTime(remainder);
|
||||
}
|
||||
|
|
|
@ -32,14 +32,14 @@ protected:
|
|||
/* This is the number of *discrete* groups. */
|
||||
static const size_t sNumGroups = 4;
|
||||
|
||||
class AnimationValue : public Ogre::ControllerValue<Ogre::Real>
|
||||
class AnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Animation *mAnimation;
|
||||
std::string mAnimationName;
|
||||
|
||||
public:
|
||||
AnimationValue(Animation *anim)
|
||||
AnimationTime(Animation *anim)
|
||||
: mAnimation(anim)
|
||||
{ }
|
||||
|
||||
|
@ -52,12 +52,12 @@ protected:
|
|||
virtual void setValue(Ogre::Real value);
|
||||
};
|
||||
|
||||
class EffectAnimationValue : public Ogre::ControllerValue<Ogre::Real>
|
||||
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
float mTime;
|
||||
public:
|
||||
EffectAnimationValue() : mTime(0) { }
|
||||
EffectAnimationTime() : mTime(0) { }
|
||||
void addTime(float time) { mTime += time; }
|
||||
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:
|
||||
virtual Ogre::Real getValue() const
|
||||
|
@ -134,8 +134,8 @@ protected:
|
|||
|
||||
AnimStateMap mStates;
|
||||
|
||||
Ogre::SharedPtr<AnimationValue> mAnimationValuePtr[sNumGroups];
|
||||
Ogre::SharedPtr<NullAnimationValue> mNullAnimationValuePtr;
|
||||
Ogre::SharedPtr<AnimationTime> mAnimationTimePtr[sNumGroups];
|
||||
Ogre::SharedPtr<NullAnimationTime> mNullAnimationTimePtr;
|
||||
|
||||
ObjectAttachMap mAttachedObjects;
|
||||
|
||||
|
@ -189,16 +189,18 @@ protected:
|
|||
/** 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);
|
||||
|
||||
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();
|
||||
|
||||
// TODO: Should not be here
|
||||
Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item);
|
||||
|
||||
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);
|
||||
virtual ~Animation();
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
|
|||
setObjectRoot(model, false);
|
||||
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(model);
|
||||
}
|
||||
|
|
117
apps/openmw/mwrender/effectmanager.cpp
Normal file
117
apps/openmw/mwrender/effectmanager.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
31
apps/openmw/mwrender/effectmanager.hpp
Normal file
31
apps/openmw/mwrender/effectmanager.hpp
Normal 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
|
|
@ -61,8 +61,9 @@ std::string getVampireHead(const std::string& race, bool female)
|
|||
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))
|
||||
return 0;
|
||||
else
|
||||
|
@ -124,7 +125,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
|
|||
{
|
||||
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++)
|
||||
{
|
||||
|
@ -595,10 +596,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
|
|||
{
|
||||
if(ctrl->getSource().isNull())
|
||||
{
|
||||
ctrl->setSource(mNullAnimationValuePtr);
|
||||
ctrl->setSource(mNullAnimationTimePtr);
|
||||
|
||||
if (type == ESM::PRT_Head)
|
||||
ctrl->setSource(mSayAnimationValue);
|
||||
ctrl->setSource(mHeadAnimationTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ namespace ESM
|
|||
namespace MWRender
|
||||
{
|
||||
|
||||
class SayAnimationValue : public Ogre::ControllerValue<Ogre::Real>
|
||||
class HeadAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
MWWorld::Ptr mReference;
|
||||
public:
|
||||
SayAnimationValue(MWWorld::Ptr reference) : mReference(reference) {}
|
||||
HeadAnimationTime(MWWorld::Ptr reference) : mReference(reference) {}
|
||||
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value)
|
||||
|
@ -70,7 +70,7 @@ private:
|
|||
|
||||
Ogre::Vector3 mFirstPersonOffset;
|
||||
|
||||
Ogre::SharedPtr<SayAnimationValue> mSayAnimationValue;
|
||||
Ogre::SharedPtr<HeadAnimationTime> mHeadAnimationTime;
|
||||
|
||||
float mAlpha;
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "globalmap.hpp"
|
||||
#include "videoplayer.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
#include "effectmanager.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
@ -57,9 +58,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
, mSunEnabled(0)
|
||||
, mPhysicsEngine(engine)
|
||||
, mTerrain(NULL)
|
||||
, mEffectManager(NULL)
|
||||
{
|
||||
mActors = new MWRender::Actors(mRendering, this);
|
||||
mObjects = new MWRender::Objects(mRendering);
|
||||
mEffectManager = new EffectManager(mRendering.getScene());
|
||||
// select best shader mode
|
||||
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);
|
||||
|
@ -193,6 +196,7 @@ RenderingManager::~RenderingManager ()
|
|||
delete mVideoPlayer;
|
||||
delete mActors;
|
||||
delete mObjects;
|
||||
delete mEffectManager;
|
||||
delete mFactory;
|
||||
}
|
||||
|
||||
|
@ -374,6 +378,8 @@ void RenderingManager::update (float duration, bool paused)
|
|||
if(paused)
|
||||
return;
|
||||
|
||||
mEffectManager->update(duration);
|
||||
|
||||
mActors->update (mRendering.getCamera());
|
||||
mPlayerAnimation->preRender(mRendering.getCamera());
|
||||
mObjects->update (duration, mRendering.getCamera());
|
||||
|
@ -675,14 +681,14 @@ Shadows* RenderingManager::getShadows()
|
|||
|
||||
void RenderingManager::switchToInterior()
|
||||
{
|
||||
// causes light flicker in opengl when moving..
|
||||
//mRendering.getScene()->setCameraRelativeRendering(false);
|
||||
// TODO: also do this when switching worldspace
|
||||
mEffectManager->clear();
|
||||
}
|
||||
|
||||
void RenderingManager::switchToExterior()
|
||||
{
|
||||
// causes light flicker in opengl when moving..
|
||||
//mRendering.getScene()->setCameraRelativeRendering(true);
|
||||
// TODO: also do this when switching worldspace
|
||||
mEffectManager->clear();
|
||||
}
|
||||
|
||||
Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds)
|
||||
|
@ -1020,4 +1026,9 @@ float RenderingManager::getCameraDistance() const
|
|||
return mCamera->getCameraDistance();
|
||||
}
|
||||
|
||||
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition)
|
||||
{
|
||||
mEffectManager->addEffect(model, texture, worldPosition);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -21,10 +21,7 @@
|
|||
|
||||
namespace Ogre
|
||||
{
|
||||
class SceneManager;
|
||||
class SceneNode;
|
||||
class Quaternion;
|
||||
class Vector3;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
|
@ -51,6 +48,7 @@ namespace MWRender
|
|||
class GlobalMap;
|
||||
class VideoPlayer;
|
||||
class Animation;
|
||||
class EffectManager;
|
||||
|
||||
class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener
|
||||
{
|
||||
|
@ -209,6 +207,8 @@ public:
|
|||
void stopVideo();
|
||||
void frameStarted(float dt, bool paused);
|
||||
|
||||
void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition);
|
||||
|
||||
protected:
|
||||
virtual void windowResized(int x, int y);
|
||||
|
||||
|
@ -239,6 +239,8 @@ private:
|
|||
MWRender::Objects* mObjects;
|
||||
MWRender::Actors* mActors;
|
||||
|
||||
MWRender::EffectManager* mEffectManager;
|
||||
|
||||
MWRender::NpcAnimation *mPlayerAnimation;
|
||||
|
||||
// 0 normal, 1 more bright, 2 max
|
||||
|
|
|
@ -248,14 +248,13 @@ namespace MWSound
|
|||
return;
|
||||
try
|
||||
{
|
||||
// The range values are not tested
|
||||
float basevol = volumeFromType(Play_TypeVoice);
|
||||
std::string filePath = "Sound/"+filename;
|
||||
const ESM::Position &pos = ptr.getRefData().getPosition();
|
||||
const Ogre::Vector3 objpos(pos.pos);
|
||||
|
||||
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"));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
|
|
|
@ -363,4 +363,8 @@ namespace MWWorld
|
|||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,6 +278,9 @@ namespace MWWorld
|
|||
|
||||
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
|
||||
copyToCell(const Ptr &ptr, CellStore &cell) const;
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
|
|||
item.getCellRef().mPos.pos[1] = 0;
|
||||
item.getCellRef().mPos.pos[2] = 0;
|
||||
|
||||
if (setOwner)
|
||||
if (setOwner && actorPtr.getClass().isActor())
|
||||
item.getCellRef().mOwner = actorPtr.getCellRef().mRefID;
|
||||
|
||||
std::string script = MWWorld::Class::get(item).getScript(item);
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
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();
|
||||
Ogre::Vector3 position(refpos.pos);
|
||||
|
@ -229,7 +229,10 @@ namespace MWWorld
|
|||
physicActor->setInertialForce(Ogre::Vector3(0.0f));
|
||||
else
|
||||
{
|
||||
inertia.z += time*-627.2f;
|
||||
float diff = time*-627.2f;
|
||||
if (inertia.z < 0)
|
||||
diff *= slowFall;
|
||||
inertia.z += diff;
|
||||
physicActor->setInertialForce(inertia);
|
||||
}
|
||||
physicActor->setOnGround(isOnGround);
|
||||
|
@ -577,9 +580,10 @@ namespace MWWorld
|
|||
|
||||
float oldHeight = iter->first.getRefData().getPosition().pos[2];
|
||||
|
||||
const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects();
|
||||
|
||||
bool waterCollision = false;
|
||||
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects()
|
||||
.get(ESM::MagicEffect::WaterWalking).mMagnitude
|
||||
if (effects.get(ESM::MagicEffect::WaterWalking).mMagnitude
|
||||
&& cell->hasWater()
|
||||
&& !world->isUnderwater(iter->first.getCell(),
|
||||
Ogre::Vector3(iter->first.getRefData().getPosition().pos)))
|
||||
|
@ -592,9 +596,12 @@ namespace MWWorld
|
|||
if (waterCollision)
|
||||
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,
|
||||
world->isFlying(iter->first),
|
||||
waterlevel, mEngine);
|
||||
waterlevel, slowFall, mEngine);
|
||||
|
||||
if (waterCollision)
|
||||
mEngine->dynamicsWorld->removeCollisionObject(&object);
|
||||
|
|
|
@ -2282,7 +2282,8 @@ namespace MWWorld
|
|||
void World::breakInvisibility(const Ptr &actor)
|
||||
{
|
||||
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
|
||||
|
@ -2610,4 +2611,31 @@ namespace MWWorld
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -549,6 +549,9 @@ namespace MWWorld
|
|||
|
||||
/// Spawn a random creature from a levelled list next to the player
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -25,16 +25,20 @@ struct Creature
|
|||
// Default is 0x48?
|
||||
enum Flags
|
||||
{
|
||||
Biped = 0x001,
|
||||
Respawn = 0x002,
|
||||
Weapon = 0x004, // Has weapon and shield
|
||||
None = 0x008, // ??
|
||||
// Movement types
|
||||
Bipedal = 0x001,
|
||||
Swims = 0x010,
|
||||
Flies = 0x020, // Don't know what happens if several
|
||||
Walks = 0x040, // of these are set
|
||||
|
||||
Respawn = 0x002,
|
||||
Weapon = 0x004, // Has weapon and shield
|
||||
None = 0x008, // ??
|
||||
Essential = 0x080,
|
||||
Skeleton = 0x400, // Does not have normal blood
|
||||
Metal = 0x800 // Has 'golden' blood
|
||||
|
||||
// Blood types
|
||||
Skeleton = 0x400,
|
||||
Metal = 0x800
|
||||
};
|
||||
|
||||
enum Type
|
||||
|
|
Loading…
Reference in a new issue