forked from teamnwah/openmw-tes3coop
Merge remote branch 'zini/master' into gui-windows
This commit is contained in:
commit
38b434771a
40 changed files with 2919 additions and 121 deletions
|
@ -38,7 +38,7 @@ PROJECT_NUMBER =
|
||||||
# If a relative path is entered, it will be relative to the location
|
# If a relative path is entered, it will be relative to the location
|
||||||
# where doxygen was started. If left blank the current directory will be used.
|
# where doxygen was started. If left blank the current directory will be used.
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = ../Doxygen
|
OUTPUT_DIRECTORY = Doxygen
|
||||||
|
|
||||||
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
|
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
|
||||||
# 4096 sub-directories (in 2 levels) under the output directory of each output
|
# 4096 sub-directories (in 2 levels) under the output directory of each output
|
||||||
|
@ -573,10 +573,9 @@ WARN_LOGFILE =
|
||||||
# directories like "/usr/src/myproject". Separate the files or directories
|
# directories like "/usr/src/myproject". Separate the files or directories
|
||||||
# with spaces.
|
# with spaces.
|
||||||
|
|
||||||
INPUT = ../apps
|
INPUT = apps \
|
||||||
../components
|
components \
|
||||||
../libs
|
libs \
|
||||||
../docs
|
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||||
|
|
1542
Docs/DoxyfilePages
Normal file
1542
Docs/DoxyfilePages
Normal file
File diff suppressed because it is too large
Load diff
3
apps/doc.hpp
Normal file
3
apps/doc.hpp
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Note: This is not a regular source file.
|
||||||
|
|
||||||
|
/// \defgroup apps Applications
|
|
@ -184,11 +184,14 @@ set(GAMECLASS_HEADER
|
||||||
source_group(apps\\openmw\\mwclass FILES ${GAMECLASS} ${GAMECLASS_HEADER})
|
source_group(apps\\openmw\\mwclass FILES ${GAMECLASS} ${GAMECLASS_HEADER})
|
||||||
|
|
||||||
set(GAMEMECHANICS
|
set(GAMEMECHANICS
|
||||||
mwmechanics/mechanicsmanager.cpp)
|
mwmechanics/mechanicsmanager.cpp
|
||||||
|
mwmechanics/magiceffects.cpp
|
||||||
|
)
|
||||||
set(GAMEMECHANICS_HEADER
|
set(GAMEMECHANICS_HEADER
|
||||||
mwmechanics/mechanicsmanager.hpp
|
mwmechanics/mechanicsmanager.hpp
|
||||||
mwmechanics/stat.hpp
|
mwmechanics/stat.hpp
|
||||||
mwmechanics/creaturestats.hpp
|
mwmechanics/creaturestats.hpp
|
||||||
|
mwmechanics/magiceffects.hpp
|
||||||
)
|
)
|
||||||
source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER})
|
source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER})
|
||||||
|
|
||||||
|
|
44
apps/openmw/doc.hpp
Normal file
44
apps/openmw/doc.hpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Note: This is not a regular source file.
|
||||||
|
|
||||||
|
/// \ingroup apps
|
||||||
|
/// \defgroup openmw OpenMW
|
||||||
|
|
||||||
|
/// \namespace OMW
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief Integration of OpenMW-subsystems
|
||||||
|
|
||||||
|
/// \namespace MWDialogue
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief NPC dialogues
|
||||||
|
|
||||||
|
/// \namespace MWMechanics
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief Game mechanics and NPC-AI
|
||||||
|
|
||||||
|
/// \namespace MWSound
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief Sound & music
|
||||||
|
|
||||||
|
/// \namespace MWGUI
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief HUD and windows
|
||||||
|
|
||||||
|
/// \namespace MWRender
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief Rendering via Ogre
|
||||||
|
|
||||||
|
/// \namespace MWWorld
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief World data
|
||||||
|
|
||||||
|
/// \namespace MWClass
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief Workaround for non-OOP design of the record system
|
||||||
|
|
||||||
|
/// \namespace MWInput
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief User input and character controls
|
||||||
|
|
||||||
|
/// \namespace MWScript
|
||||||
|
/// \ingroup openmw
|
||||||
|
/// \brief MW-specific script extentions and integration of the script system into OpenMW
|
|
@ -117,6 +117,7 @@ OMW::Engine::Engine()
|
||||||
, mVerboseScripts (false)
|
, mVerboseScripts (false)
|
||||||
, mNewGame (false)
|
, mNewGame (false)
|
||||||
, mUseSound (true)
|
, mUseSound (true)
|
||||||
|
, mCompileAll (false)
|
||||||
, mScriptManager (0)
|
, mScriptManager (0)
|
||||||
, mScriptContext (0)
|
, mScriptContext (0)
|
||||||
, mGuiManager (0)
|
, mGuiManager (0)
|
||||||
|
@ -278,9 +279,20 @@ void OMW::Engine::go()
|
||||||
|
|
||||||
// load cell
|
// load cell
|
||||||
ESM::Position pos;
|
ESM::Position pos;
|
||||||
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
|
||||||
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
||||||
|
pos.pos[2] = 0;
|
||||||
|
|
||||||
|
if (const ESM::Cell *exterior = mEnvironment.mWorld->getExterior (mCellName))
|
||||||
|
{
|
||||||
|
mEnvironment.mWorld->indexToPosition (exterior->data.gridX, exterior->data.gridY,
|
||||||
|
pos.pos[0], pos.pos[1], true);
|
||||||
|
mEnvironment.mWorld->changeToExteriorCell (pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos.pos[0] = pos.pos[1] = 0;
|
||||||
mEnvironment.mWorld->changeCell (mCellName, pos);
|
mEnvironment.mWorld->changeCell (mCellName, pos);
|
||||||
|
}
|
||||||
|
|
||||||
// Sets up the input system
|
// Sets up the input system
|
||||||
MWInput::MWInputManager input(mOgre, mEnvironment.mWorld->getPlayerPos(),
|
MWInput::MWInputManager input(mOgre, mEnvironment.mWorld->getPlayerPos(),
|
||||||
|
@ -305,6 +317,29 @@ void OMW::Engine::go()
|
||||||
std::cout << " Music Error: " << e.what() << "\n";
|
std::cout << " Music Error: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scripts
|
||||||
|
if (mCompileAll)
|
||||||
|
{
|
||||||
|
typedef ESMS::ScriptListT<ESM::Script>::MapType Container;
|
||||||
|
|
||||||
|
Container scripts = mEnvironment.mWorld->getStore().scripts.list;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
int success = 0;
|
||||||
|
|
||||||
|
for (Container::const_iterator iter (scripts.begin()); iter!=scripts.end(); ++iter, ++count)
|
||||||
|
if (mScriptManager->compile (iter->first))
|
||||||
|
++success;
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
std::cout
|
||||||
|
<< "compiled " << success << " of " << count << " scripts ("
|
||||||
|
<< 100*static_cast<double> (success)/count
|
||||||
|
<< "%)"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Start the main rendering loop
|
// Start the main rendering loop
|
||||||
mOgre.start();
|
mOgre.start();
|
||||||
|
|
||||||
|
@ -345,3 +380,8 @@ void OMW::Engine::activate()
|
||||||
interpreterContext.executeActivation();
|
interpreterContext.executeActivation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setCompileAll (bool all)
|
||||||
|
{
|
||||||
|
mCompileAll = all;
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ namespace OMW
|
||||||
bool mVerboseScripts;
|
bool mVerboseScripts;
|
||||||
bool mNewGame;
|
bool mNewGame;
|
||||||
bool mUseSound;
|
bool mUseSound;
|
||||||
|
bool mCompileAll;
|
||||||
|
|
||||||
MWWorld::Environment mEnvironment;
|
MWWorld::Environment mEnvironment;
|
||||||
MWScript::ScriptManager *mScriptManager;
|
MWScript::ScriptManager *mScriptManager;
|
||||||
|
@ -127,6 +128,9 @@ namespace OMW
|
||||||
|
|
||||||
/// Activate the focussed object.
|
/// Activate the focussed object.
|
||||||
void activate();
|
void activate();
|
||||||
|
|
||||||
|
/// Compile all scripts (excludign dialogue scripts) at startup?
|
||||||
|
void setCompileAll (bool all);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,13 +43,14 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
("data", bpo::value<std::string>()->default_value ("data"),
|
("data", bpo::value<std::string>()->default_value ("data"),
|
||||||
"set data directory")
|
"set data directory")
|
||||||
("start", bpo::value<std::string>()->default_value ("Beshara"),
|
("start", bpo::value<std::string>()->default_value ("Beshara"),
|
||||||
"set initial cell (only interior cells supported at the moment")
|
"set initial cell")
|
||||||
("master", bpo::value<std::string>()->default_value ("Morrowind"),
|
("master", bpo::value<std::string>()->default_value ("Morrowind"),
|
||||||
"master file")
|
"master file")
|
||||||
( "debug", "debug mode" )
|
( "debug", "debug mode" )
|
||||||
( "nosound", "disable all sound" )
|
( "nosound", "disable all sound" )
|
||||||
( "script-verbose", "verbose script output" )
|
( "script-verbose", "verbose script output" )
|
||||||
( "new-game", "activate char gen/new game mechanics" )
|
( "new-game", "activate char gen/new game mechanics" )
|
||||||
|
( "script-all", "compile all scripts (excluding dialogue scripts) at startup")
|
||||||
;
|
;
|
||||||
|
|
||||||
bpo::variables_map variables;
|
bpo::variables_map variables;
|
||||||
|
@ -91,6 +92,9 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
if (variables.count ("new-game"))
|
if (variables.count ("new-game"))
|
||||||
engine.setNewGame();
|
engine.setNewGame();
|
||||||
|
|
||||||
|
if (variables.count ("script-all"))
|
||||||
|
engine.setCompileAll (true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -560,13 +560,19 @@ void WindowManager::onClassChoice(MyGUI::WidgetPtr, int _index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::showClassQuestionDialog()
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Step
|
struct Step
|
||||||
{
|
{
|
||||||
const char* text;
|
const char* text;
|
||||||
const char* buttons[3];
|
const char* buttons[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::showClassQuestionDialog()
|
||||||
|
{
|
||||||
static boost::array<Step, 2> steps = { {
|
static boost::array<Step, 2> steps = { {
|
||||||
{"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.",
|
{"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.",
|
||||||
{"Use herbs from your pack to put it to sleep?",
|
{"Use herbs from your pack to put it to sleep?",
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#ifndef GAME_MWMECHANICS_CREATURESTATS_H
|
#ifndef GAME_MWMECHANICS_CREATURESTATS_H
|
||||||
#define GAME_MWMECHANICS_CREATURESTATS_H
|
#define GAME_MWMECHANICS_CREATURESTATS_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "stat.hpp"
|
#include "stat.hpp"
|
||||||
|
#include "magiceffects.hpp"
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
@ -10,6 +14,8 @@ namespace MWMechanics
|
||||||
Stat<int> mAttributes[8];
|
Stat<int> mAttributes[8];
|
||||||
DynamicStat<int> mDynamic[3]; // health, magicka, fatigue
|
DynamicStat<int> mDynamic[3]; // health, magicka, fatigue
|
||||||
int mLevel;
|
int mLevel;
|
||||||
|
std::set<std::string> mAbilities;
|
||||||
|
MagicEffects mMagicEffects;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
118
apps/openmw/mwmechanics/magiceffects.cpp
Normal file
118
apps/openmw/mwmechanics/magiceffects.cpp
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
|
||||||
|
#include "magiceffects.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <components/esm/defs.hpp>
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
EffectKey::EffectKey() : mId (0), mArg (-1) {}
|
||||||
|
|
||||||
|
EffectKey::EffectKey (const ESM::ENAMstruct& effect)
|
||||||
|
{
|
||||||
|
mId = effect.effectID;
|
||||||
|
mArg = -1;
|
||||||
|
|
||||||
|
if (effect.skill!=-1)
|
||||||
|
mArg = effect.skill;
|
||||||
|
|
||||||
|
if (effect.attribute!=-1)
|
||||||
|
{
|
||||||
|
if (mArg!=-1)
|
||||||
|
throw std::runtime_error (
|
||||||
|
"magic effect can't have both a skill and an attribute argument");
|
||||||
|
|
||||||
|
mArg = effect.attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator< (const EffectKey& left, const EffectKey& right)
|
||||||
|
{
|
||||||
|
if (left.mId<right.mId)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (left.mId>right.mId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return left.mArg<right.mArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectParam::EffectParam() : mMagnitude (0) {}
|
||||||
|
|
||||||
|
EffectParam& EffectParam::operator+= (const EffectParam& param)
|
||||||
|
{
|
||||||
|
mMagnitude += param.mMagnitude;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectParam& EffectParam::operator-= (const EffectParam& param)
|
||||||
|
{
|
||||||
|
mMagnitude -= param.mMagnitude;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MagicEffects::add (const EffectKey& key, const EffectParam& param)
|
||||||
|
{
|
||||||
|
Collection::iterator iter = mCollection.find (key);
|
||||||
|
|
||||||
|
if (iter==mCollection.end())
|
||||||
|
{
|
||||||
|
mCollection.insert (std::make_pair (key, param));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->second += param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectParam MagicEffects::get (const EffectKey& key) const
|
||||||
|
{
|
||||||
|
Collection::const_iterator iter = mCollection.find (key);
|
||||||
|
|
||||||
|
if (iter==mCollection.end())
|
||||||
|
{
|
||||||
|
return EffectParam();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MagicEffects MagicEffects::diff (const MagicEffects& prev, const MagicEffects& now)
|
||||||
|
{
|
||||||
|
MagicEffects result;
|
||||||
|
|
||||||
|
// adding/changing
|
||||||
|
for (Collection::const_iterator iter (now.Begin()); iter!=now.End(); ++iter)
|
||||||
|
{
|
||||||
|
Collection::const_iterator other = prev.mCollection.find (iter->first);
|
||||||
|
|
||||||
|
if (other==prev.End())
|
||||||
|
{
|
||||||
|
// adding
|
||||||
|
result.add (iter->first, iter->second);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// changing
|
||||||
|
result.add (iter->first, iter->second - other->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removing
|
||||||
|
for (Collection::const_iterator iter (prev.Begin()); iter!=prev.End(); ++iter)
|
||||||
|
{
|
||||||
|
Collection::const_iterator other = now.mCollection.find (iter->first);
|
||||||
|
|
||||||
|
if (other==prev.End())
|
||||||
|
{
|
||||||
|
result.add (iter->first, EffectParam() - iter->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
77
apps/openmw/mwmechanics/magiceffects.hpp
Normal file
77
apps/openmw/mwmechanics/magiceffects.hpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef GAME_MWMECHANICS_MAGICEFFECTS_H
|
||||||
|
#define GAME_MWMECHANICS_MAGICEFFECTS_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct ENAMstruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
struct EffectKey
|
||||||
|
{
|
||||||
|
int mId;
|
||||||
|
int mArg; // skill or ability
|
||||||
|
|
||||||
|
EffectKey();
|
||||||
|
|
||||||
|
EffectKey (int id, int arg = -1) : mId (id), mArg (arg) {}
|
||||||
|
|
||||||
|
EffectKey (const ESM::ENAMstruct& effect);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator< (const EffectKey& left, const EffectKey& right);
|
||||||
|
|
||||||
|
struct EffectParam
|
||||||
|
{
|
||||||
|
int mMagnitude;
|
||||||
|
|
||||||
|
EffectParam();
|
||||||
|
|
||||||
|
EffectParam& operator+= (const EffectParam& param);
|
||||||
|
|
||||||
|
EffectParam& operator-= (const EffectParam& param);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline EffectParam operator+ (const EffectParam& left, const EffectParam& right)
|
||||||
|
{
|
||||||
|
EffectParam param (left);
|
||||||
|
return param += right;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline EffectParam operator- (const EffectParam& left, const EffectParam& right)
|
||||||
|
{
|
||||||
|
EffectParam param (left);
|
||||||
|
return param -= right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Effects currently affecting a NPC or creature
|
||||||
|
class MagicEffects
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef std::map<EffectKey, EffectParam> Collection;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Collection mCollection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Collection::const_iterator Begin() const { return mCollection.begin(); }
|
||||||
|
|
||||||
|
Collection::const_iterator End() const { return mCollection.end(); }
|
||||||
|
|
||||||
|
void add (const EffectKey& key, const EffectParam& param);
|
||||||
|
|
||||||
|
EffectParam get (const EffectKey& key) const;
|
||||||
|
///< This function can safely be used for keys that are not present.
|
||||||
|
|
||||||
|
static MagicEffects diff (const MagicEffects& prev, const MagicEffects& now);
|
||||||
|
///< Return changes from \a prev to \a now.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -22,13 +22,18 @@ namespace MWMechanics
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
creatureStats.mLevel = player->npdt52.level;
|
creatureStats.mLevel = player->npdt52.level;
|
||||||
|
creatureStats.mAbilities.clear();
|
||||||
|
creatureStats.mMagicEffects = MagicEffects();
|
||||||
|
|
||||||
for (int i=0; i<27; ++i)
|
for (int i=0; i<27; ++i)
|
||||||
npcStats.mSkill[i].setBase (player->npdt52.skills[i]);
|
npcStats.mSkill[i].setBase (player->npdt52.skills[i]);
|
||||||
|
|
||||||
// race
|
// race
|
||||||
|
if (mRaceSelected)
|
||||||
|
{
|
||||||
const ESM::Race *race =
|
const ESM::Race *race =
|
||||||
mEnvironment.mWorld->getStore().races.find (mEnvironment.mWorld->getPlayerPos().getRace());
|
mEnvironment.mWorld->getStore().races.find (
|
||||||
|
mEnvironment.mWorld->getPlayerPos().getRace());
|
||||||
|
|
||||||
bool male = mEnvironment.mWorld->getPlayerPos().isMale();
|
bool male = mEnvironment.mWorld->getPlayerPos().isMale();
|
||||||
|
|
||||||
|
@ -57,15 +62,35 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (index>=0 && index<27)
|
if (index>=0 && index<27)
|
||||||
{
|
{
|
||||||
npcStats.mSkill[i].setBase (
|
npcStats.mSkill[index].setBase (
|
||||||
npcStats.mSkill[i].getBase() + race->data.bonus[i].bonus);
|
npcStats.mSkill[index].getBase() + race->data.bonus[i].bonus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (std::vector<std::string>::const_iterator iter (race->powers.list.begin());
|
||||||
|
iter!=race->powers.list.end(); ++iter)
|
||||||
|
{
|
||||||
|
insertSpell (*iter, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// birthsign
|
// birthsign
|
||||||
|
if (!mEnvironment.mWorld->getPlayerPos().getBirthsign().empty())
|
||||||
|
{
|
||||||
|
const ESM::BirthSign *sign =
|
||||||
|
mEnvironment.mWorld->getStore().birthSigns.find (
|
||||||
|
mEnvironment.mWorld->getPlayerPos().getBirthsign());
|
||||||
|
|
||||||
|
for (std::vector<std::string>::const_iterator iter (sign->powers.list.begin());
|
||||||
|
iter!=sign->powers.list.end(); ++iter)
|
||||||
|
{
|
||||||
|
insertSpell (*iter, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// class
|
// class
|
||||||
|
if (mClassSelected)
|
||||||
|
{
|
||||||
const ESM::Class& class_ = mEnvironment.mWorld->getPlayerPos().getClass();
|
const ESM::Class& class_ = mEnvironment.mWorld->getPlayerPos().getClass();
|
||||||
|
|
||||||
for (int i=0; i<2; ++i)
|
for (int i=0; i<2; ++i)
|
||||||
|
@ -78,7 +103,42 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<2; ++i)
|
||||||
|
{
|
||||||
|
int bonus = i==0 ? 10 : 25;
|
||||||
|
|
||||||
|
for (int i2=0; i2<5; ++i2)
|
||||||
|
{
|
||||||
|
int index = class_.data.skills[i2][i];
|
||||||
|
|
||||||
|
if (index>=0 && index<27)
|
||||||
|
{
|
||||||
|
npcStats.mSkill[index].setBase (
|
||||||
|
npcStats.mSkill[index].getBase() + bonus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ESMS::IndexListT<ESM::Skill>::MapType ContainerType;
|
||||||
|
const ContainerType& skills = mEnvironment.mWorld->getStore().skills.list;
|
||||||
|
|
||||||
|
for (ContainerType::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (iter->second.data.specialization==class_.data.specialization)
|
||||||
|
{
|
||||||
|
int index = iter->first;
|
||||||
|
|
||||||
|
if (index>=0 && index<27)
|
||||||
|
{
|
||||||
|
npcStats.mSkill[index].setBase (
|
||||||
|
npcStats.mSkill[index].getBase() + 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// magic effects
|
||||||
|
adjustMagicEffects (ptr);
|
||||||
|
|
||||||
// calculate dynamic stats
|
// calculate dynamic stats
|
||||||
int strength = creatureStats.mAttributes[0].getBase();
|
int strength = creatureStats.mAttributes[0].getBase();
|
||||||
|
@ -87,17 +147,81 @@ namespace MWMechanics
|
||||||
int agility = creatureStats.mAttributes[3].getBase();
|
int agility = creatureStats.mAttributes[3].getBase();
|
||||||
int endurance = creatureStats.mAttributes[5].getBase();
|
int endurance = creatureStats.mAttributes[5].getBase();
|
||||||
|
|
||||||
|
double magickaFactor = creatureStats.mMagicEffects.get (EffectKey (84)).mMagnitude*0.1 + 0.5;
|
||||||
|
|
||||||
creatureStats.mDynamic[0].setBase (static_cast<int> (0.5 * (strength + endurance)));
|
creatureStats.mDynamic[0].setBase (static_cast<int> (0.5 * (strength + endurance)));
|
||||||
// TODO: calculate factor
|
creatureStats.mDynamic[1].setBase (static_cast<int> (intelligence +
|
||||||
creatureStats.mDynamic[1].setBase (static_cast<int> (intelligence + 1 * intelligence));
|
magickaFactor * intelligence));
|
||||||
creatureStats.mDynamic[2].setBase (strength+willpower+agility+endurance);
|
creatureStats.mDynamic[2].setBase (strength+willpower+agility+endurance);
|
||||||
|
|
||||||
for (int i=0; i<3; ++i)
|
for (int i=0; i<3; ++i)
|
||||||
creatureStats.mDynamic[i].setCurrent (creatureStats.mDynamic[i].getModified());
|
creatureStats.mDynamic[i].setCurrent (creatureStats.mDynamic[i].getModified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MechanicsManager::insertSpell (const std::string& id, MWWorld::Ptr& creature)
|
||||||
|
{
|
||||||
|
MWMechanics::CreatureStats& creatureStats =
|
||||||
|
MWWorld::Class::get (creature).getCreatureStats (creature);
|
||||||
|
|
||||||
|
const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (id);
|
||||||
|
|
||||||
|
switch (spell->data.type)
|
||||||
|
{
|
||||||
|
case ESM::Spell::ST_Ability:
|
||||||
|
|
||||||
|
if (creatureStats.mAbilities.find (id)==creatureStats.mAbilities.end())
|
||||||
|
{
|
||||||
|
creatureStats.mAbilities.insert (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO ST_SPELL, ST_Blight, ST_Disease, ST_Curse, ST_Power
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "adding unsupported spell type (" << spell->data.type
|
||||||
|
<< ") to creature: " << id << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MechanicsManager::adjustMagicEffects (MWWorld::Ptr& creature)
|
||||||
|
{
|
||||||
|
MWMechanics::CreatureStats& creatureStats =
|
||||||
|
MWWorld::Class::get (creature).getCreatureStats (creature);
|
||||||
|
|
||||||
|
MagicEffects now;
|
||||||
|
|
||||||
|
for (std::set<std::string>::const_iterator iter (creatureStats.mAbilities.begin());
|
||||||
|
iter!=creatureStats.mAbilities.end(); ++iter)
|
||||||
|
{
|
||||||
|
const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (*iter);
|
||||||
|
|
||||||
|
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->effects.list.begin();
|
||||||
|
iter!=spell->effects.list.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (iter->range==0) // self
|
||||||
|
{
|
||||||
|
EffectParam param;
|
||||||
|
param.mMagnitude = iter->magnMax; // TODO calculate magnitude
|
||||||
|
now.add (EffectKey (*iter), param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add effects from other spell types, active spells and equipment
|
||||||
|
|
||||||
|
MagicEffects diff = MagicEffects::diff (creatureStats.mMagicEffects, now);
|
||||||
|
|
||||||
|
creatureStats.mMagicEffects = now;
|
||||||
|
|
||||||
|
// TODO apply diff to other stats
|
||||||
|
}
|
||||||
|
|
||||||
MechanicsManager::MechanicsManager (MWWorld::Environment& environment)
|
MechanicsManager::MechanicsManager (MWWorld::Environment& environment)
|
||||||
: mEnvironment (environment), mUpdatePlayer (true)
|
: mEnvironment (environment), mUpdatePlayer (true), mClassSelected (false),
|
||||||
|
mRaceSelected (false)
|
||||||
{
|
{
|
||||||
buildPlayer();
|
buildPlayer();
|
||||||
}
|
}
|
||||||
|
@ -237,6 +361,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
mEnvironment.mWorld->getPlayerPos().setGender (male);
|
mEnvironment.mWorld->getPlayerPos().setGender (male);
|
||||||
mEnvironment.mWorld->getPlayerPos().setRace (race);
|
mEnvironment.mWorld->getPlayerPos().setRace (race);
|
||||||
|
mRaceSelected = true;
|
||||||
buildPlayer();
|
buildPlayer();
|
||||||
mUpdatePlayer = true;
|
mUpdatePlayer = true;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +375,7 @@ namespace MWMechanics
|
||||||
void MechanicsManager::setPlayerClass (const std::string& id)
|
void MechanicsManager::setPlayerClass (const std::string& id)
|
||||||
{
|
{
|
||||||
mEnvironment.mWorld->getPlayerPos().setClass (*mEnvironment.mWorld->getStore().classes.find (id));
|
mEnvironment.mWorld->getPlayerPos().setClass (*mEnvironment.mWorld->getStore().classes.find (id));
|
||||||
|
mClassSelected = true;
|
||||||
buildPlayer();
|
buildPlayer();
|
||||||
mUpdatePlayer = true;
|
mUpdatePlayer = true;
|
||||||
}
|
}
|
||||||
|
@ -257,6 +383,7 @@ namespace MWMechanics
|
||||||
void MechanicsManager::setPlayerClass (const ESM::Class& class_)
|
void MechanicsManager::setPlayerClass (const ESM::Class& class_)
|
||||||
{
|
{
|
||||||
mEnvironment.mWorld->getPlayerPos().setClass (class_);
|
mEnvironment.mWorld->getPlayerPos().setClass (class_);
|
||||||
|
mClassSelected = true;
|
||||||
buildPlayer();
|
buildPlayer();
|
||||||
mUpdatePlayer = true;
|
mUpdatePlayer = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,17 @@ namespace MWMechanics
|
||||||
CreatureStats mWatchedCreature;
|
CreatureStats mWatchedCreature;
|
||||||
NpcStats mWatchedNpc;
|
NpcStats mWatchedNpc;
|
||||||
bool mUpdatePlayer;
|
bool mUpdatePlayer;
|
||||||
|
bool mClassSelected;
|
||||||
|
bool mRaceSelected;
|
||||||
|
|
||||||
void buildPlayer();
|
void buildPlayer();
|
||||||
///< build player according to stored class/race/birthsign information. Will
|
///< build player according to stored class/race/birthsign information. Will
|
||||||
/// default to the values of the ESM::NPC object, if no explicit information is given.
|
/// default to the values of the ESM::NPC object, if no explicit information is given.
|
||||||
|
|
||||||
|
void insertSpell (const std::string& id, MWWorld::Ptr& creature);
|
||||||
|
|
||||||
|
void adjustMagicEffects (MWWorld::Ptr& creature);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
MechanicsManager (MWWorld::Environment& environment);
|
MechanicsManager (MWWorld::Environment& environment);
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Interpreter
|
||||||
|
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
{
|
{
|
||||||
/// \brief stats-related script functionality (creatures and NPCs)
|
/// \brief Container-related script functionality (chests, NPCs, creatures)
|
||||||
namespace Container
|
namespace Container
|
||||||
{
|
{
|
||||||
void registerExtensions (Compiler::Extensions& extensions);
|
void registerExtensions (Compiler::Extensions& extensions);
|
||||||
|
|
|
@ -95,4 +95,10 @@ op 0x2000085-0x200008b: Disable Controls
|
||||||
op 0x200008c: Unlock
|
op 0x200008c: Unlock
|
||||||
op 0x200008d: Unlock, explicit reference
|
op 0x200008d: Unlock, explicit reference
|
||||||
op 0x200008e: COE
|
op 0x200008e: COE
|
||||||
opcodes 0x200008f-0x3ffffff unused
|
op 0x200008e-0x20000a8: GetSkill
|
||||||
|
op 0x20000a9-0x20000c3: GetSkill, explicit reference
|
||||||
|
op 0x20000c4-0x20000de: SetSkill
|
||||||
|
op 0x20000df-0x20000f9: SetSkill, explicit reference
|
||||||
|
op 0x20000fa-0x2000114: ModSkill
|
||||||
|
op 0x2000115-0x200012f: ModSKill, explicit reference
|
||||||
|
opcodes 0x2000130-0x3ffffff unused
|
||||||
|
|
|
@ -38,16 +38,18 @@ namespace MWScript
|
||||||
|
|
||||||
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
|
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
|
||||||
|
|
||||||
bool compile (const std::string& name);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
||||||
Compiler::Context& compilerContext);
|
Compiler::Context& compilerContext);
|
||||||
|
|
||||||
void run (const std::string& name, Interpreter::Context& interpreterContext);
|
void run (const std::string& name, Interpreter::Context& interpreterContext);
|
||||||
|
///< Run the script with the given name (compile first, if not compiled yet)
|
||||||
|
|
||||||
|
bool compile (const std::string& name);
|
||||||
|
///< Compile script with the given namen
|
||||||
|
/// \return Success?
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -468,6 +468,160 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OpGetSkill : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OpGetSkill (int index) : mIndex (index) {}
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = context.getReference();
|
||||||
|
|
||||||
|
Interpreter::Type_Integer value =
|
||||||
|
MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
getModified();
|
||||||
|
|
||||||
|
runtime.push (value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpGetSkillExplicit : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OpGetSkillExplicit (int index) : mIndex (index) {}
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
std::string id = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = context.getWorld().getPtr (id, false);
|
||||||
|
|
||||||
|
Interpreter::Type_Integer value =
|
||||||
|
MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
getModified();
|
||||||
|
|
||||||
|
runtime.push (value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpSetSkill : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OpSetSkill (int index) : mIndex (index) {}
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = context.getReference();
|
||||||
|
|
||||||
|
MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
setModified (value, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpSetSkillExplicit : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OpSetSkillExplicit (int index) : mIndex (index) {}
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
std::string id = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = context.getWorld().getPtr (id, false);
|
||||||
|
|
||||||
|
MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
setModified (value, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpModSkill : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OpModSkill (int index) : mIndex (index) {}
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = context.getReference();
|
||||||
|
|
||||||
|
value += MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
getModified();
|
||||||
|
|
||||||
|
MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
setModified (value, 0, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpModSkillExplicit : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OpModSkillExplicit (int index) : mIndex (index) {}
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
std::string id = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = context.getWorld().getPtr (id, false);
|
||||||
|
|
||||||
|
value +=
|
||||||
|
MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
getModified();
|
||||||
|
|
||||||
|
MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex].
|
||||||
|
setModified (value, 0, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const int numberOfAttributes = 8;
|
const int numberOfAttributes = 8;
|
||||||
|
|
||||||
const int opcodeGetAttribute = 0x2000027;
|
const int opcodeGetAttribute = 0x2000027;
|
||||||
|
@ -490,6 +644,15 @@ namespace MWScript
|
||||||
const int opcodeGetDynamicGetRatio = 0x200006f;
|
const int opcodeGetDynamicGetRatio = 0x200006f;
|
||||||
const int opcodeGetDynamicGetRatioExplicit = 0x2000072;
|
const int opcodeGetDynamicGetRatioExplicit = 0x2000072;
|
||||||
|
|
||||||
|
const int numberOfSkills = 27;
|
||||||
|
|
||||||
|
const int opcodeGetSkill = 0x200008e;
|
||||||
|
const int opcodeGetSkillExplicit = 0x20000a9;
|
||||||
|
const int opcodeSetSkill = 0x20000c4;
|
||||||
|
const int opcodeSetSkillExplicit = 0x20000df;
|
||||||
|
const int opcodeModSkill = 0x20000fa;
|
||||||
|
const int opcodeModSkillExplicit = 0x2000115;
|
||||||
|
|
||||||
void registerExtensions (Compiler::Extensions& extensions)
|
void registerExtensions (Compiler::Extensions& extensions)
|
||||||
{
|
{
|
||||||
static const char *attributes[numberOfAttributes] =
|
static const char *attributes[numberOfAttributes] =
|
||||||
|
@ -503,6 +666,16 @@ namespace MWScript
|
||||||
"health", "magicka", "fatigue"
|
"health", "magicka", "fatigue"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *skills[numberOfSkills] =
|
||||||
|
{
|
||||||
|
"block", "armorer", "mediumarmor", "heavyarmor", "bluntweapon",
|
||||||
|
"longblade", "axe", "spear", "athletics", "enchant", "destruction",
|
||||||
|
"alteration", "illusion", "conjuration", "mysticism",
|
||||||
|
"restoration", "alchemy", "unarmored", "security", "sneak",
|
||||||
|
"acrobatics", "lightarmor", "shortblade", "marksman",
|
||||||
|
"merchantile", "speechcraft", "handtohand"
|
||||||
|
};
|
||||||
|
|
||||||
std::string get ("get");
|
std::string get ("get");
|
||||||
std::string set ("set");
|
std::string set ("set");
|
||||||
std::string mod ("mod");
|
std::string mod ("mod");
|
||||||
|
@ -537,7 +710,18 @@ namespace MWScript
|
||||||
|
|
||||||
extensions.registerFunction (get + dynamics[i] + getRatio, 'f', "",
|
extensions.registerFunction (get + dynamics[i] + getRatio, 'f', "",
|
||||||
opcodeGetDynamicGetRatio+i, opcodeGetDynamicGetRatioExplicit+i);
|
opcodeGetDynamicGetRatio+i, opcodeGetDynamicGetRatioExplicit+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<numberOfSkills; ++i)
|
||||||
|
{
|
||||||
|
extensions.registerFunction (get + skills[i], 'l', "",
|
||||||
|
opcodeGetSkill+i, opcodeGetSkillExplicit+i);
|
||||||
|
|
||||||
|
extensions.registerInstruction (set + skills[i], "l",
|
||||||
|
opcodeSetSkill+i, opcodeSetSkillExplicit+i);
|
||||||
|
|
||||||
|
extensions.registerInstruction (mod + skills[i], "l",
|
||||||
|
opcodeModSkill+i, opcodeModSkillExplicit+i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,6 +765,18 @@ namespace MWScript
|
||||||
interpreter.installSegment5 (opcodeGetDynamicGetRatioExplicit+i,
|
interpreter.installSegment5 (opcodeGetDynamicGetRatioExplicit+i,
|
||||||
new OpGetDynamicGetRatioExplicit (i));
|
new OpGetDynamicGetRatioExplicit (i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<numberOfSkills; ++i)
|
||||||
|
{
|
||||||
|
interpreter.installSegment5 (opcodeGetSkill+i, new OpGetSkill (i));
|
||||||
|
interpreter.installSegment5 (opcodeGetSkillExplicit+i, new OpGetSkillExplicit (i));
|
||||||
|
|
||||||
|
interpreter.installSegment5 (opcodeSetSkill+i, new OpSetSkill (i));
|
||||||
|
interpreter.installSegment5 (opcodeSetSkillExplicit+i, new OpSetSkillExplicit (i));
|
||||||
|
|
||||||
|
interpreter.installSegment5 (opcodeModSkill+i, new OpModSkill (i));
|
||||||
|
interpreter.installSegment5 (opcodeModSkillExplicit+i, new OpModSkillExplicit (i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
components/doc.hpp
Normal file
30
components/doc.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Note: This is not a regular source file.
|
||||||
|
|
||||||
|
/// \defgroup components Components
|
||||||
|
|
||||||
|
/// \namespace ESMS
|
||||||
|
/// \ingroup components
|
||||||
|
/// \brief ESM/ESP record store
|
||||||
|
|
||||||
|
/// \namespace ESM
|
||||||
|
/// \ingroup components
|
||||||
|
/// \brief ESM/ESP records
|
||||||
|
|
||||||
|
/// \namespace FileFinder
|
||||||
|
/// \ingroup components
|
||||||
|
/// \brief Linux/Windows-path resolving
|
||||||
|
|
||||||
|
/// \namespace ToUTF
|
||||||
|
/// \ingroup components
|
||||||
|
/// \brief Text encoding
|
||||||
|
|
||||||
|
/// \namespace Compiler
|
||||||
|
/// \ingroup components
|
||||||
|
/// \brief script compiler
|
||||||
|
|
||||||
|
/// \namespace Interpreter
|
||||||
|
/// \ingroup components
|
||||||
|
/// \brief script interpreter
|
||||||
|
|
||||||
|
// TODO put nif and nifogre in different namespaces (or merge them)
|
||||||
|
// TODO put other components into namespaces
|
|
@ -68,7 +68,7 @@ struct ENAMstruct
|
||||||
|
|
||||||
// Which skills/attributes are affected (for restore/drain spells
|
// Which skills/attributes are affected (for restore/drain spells
|
||||||
// etc.)
|
// etc.)
|
||||||
char skill, attribute; // -1 if N/A
|
signed char skill, attribute; // -1 if N/A
|
||||||
|
|
||||||
// Other spell parameters
|
// Other spell parameters
|
||||||
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
||||||
|
|
|
@ -215,6 +215,7 @@ struct Cell
|
||||||
else ref.teleport = false;
|
else ref.teleport = false;
|
||||||
|
|
||||||
// Integer, despite the name suggesting otherwise
|
// Integer, despite the name suggesting otherwise
|
||||||
|
ref.lockLevel = 0;
|
||||||
esm.getHNOT(ref.lockLevel, "FLTV");
|
esm.getHNOT(ref.lockLevel, "FLTV");
|
||||||
ref.key = esm.getHNOString("KNAM");
|
ref.key = esm.getHNOString("KNAM");
|
||||||
ref.trap = esm.getHNOString("TNAM");
|
ref.trap = esm.getHNOString("TNAM");
|
||||||
|
|
|
@ -25,8 +25,9 @@ struct Land
|
||||||
{
|
{
|
||||||
// Get the grid location
|
// Get the grid location
|
||||||
esm.getSubNameIs("INTV");
|
esm.getSubNameIs("INTV");
|
||||||
esm.getT(X);
|
esm.getSubHeaderIs(8);
|
||||||
esm.getT(Y);
|
esm.getT<int>(X);
|
||||||
|
esm.getT<int>(Y);
|
||||||
|
|
||||||
esm.getHNT(flags, "DATA");
|
esm.getHNT(flags, "DATA");
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,11 @@ namespace ESM {
|
||||||
|
|
||||||
struct LandTexture
|
struct LandTexture
|
||||||
{
|
{
|
||||||
std::string name, texture;
|
std::string id, texture;
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
name = esm.getHNString("NAME");
|
|
||||||
esm.getHNT(index, "INTV");
|
esm.getHNT(index, "INTV");
|
||||||
texture = esm.getHNString("DATA");
|
texture = esm.getHNString("DATA");
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,79 @@ namespace ESMS
|
||||||
int getSize() { return list.size(); }
|
int getSize() { return list.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Land textures are indexed by an integer number
|
||||||
|
*/
|
||||||
|
struct LTexList : RecList
|
||||||
|
{
|
||||||
|
// TODO: For multiple ESM/ESP files we need one list per file.
|
||||||
|
std::vector<LandTexture> ltex;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
LTexList() : count(0)
|
||||||
|
{
|
||||||
|
// More than enough to hold Morrowind.esm.
|
||||||
|
ltex.reserve(128);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSize() { return count; }
|
||||||
|
|
||||||
|
void load(ESMReader &esm, const std::string &id)
|
||||||
|
{
|
||||||
|
LandTexture lt;
|
||||||
|
lt.load(esm);
|
||||||
|
lt.id = id;
|
||||||
|
|
||||||
|
// Make sure we have room for the structure
|
||||||
|
if(lt.index + 1 > (int)ltex.size())
|
||||||
|
ltex.resize(lt.index+1);
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
ltex[lt.index] = lt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Landscapes are indexed by the X,Y coordinates of the exterior
|
||||||
|
cell they belong to.
|
||||||
|
*/
|
||||||
|
struct LandList : RecList
|
||||||
|
{
|
||||||
|
// Map containing all landscapes
|
||||||
|
typedef std::map<int, Land*> LandsCol;
|
||||||
|
typedef std::map<int, LandsCol> Lands;
|
||||||
|
Lands lands;
|
||||||
|
|
||||||
|
int count;
|
||||||
|
LandList() : count(0) {}
|
||||||
|
int getSize() { return count; }
|
||||||
|
|
||||||
|
// Find land for the given coordinates. Return null if no data.
|
||||||
|
const Land *search(int x, int y) const
|
||||||
|
{
|
||||||
|
Lands::const_iterator it = lands.find(x);
|
||||||
|
if(it==lands.end())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
LandsCol::const_iterator it2 = it->second.find(y);
|
||||||
|
if(it2 == it->second.end())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return it2->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(ESMReader &esm, const std::string &id)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
|
||||||
|
// Create the structure and load it. This actually skips the
|
||||||
|
// landscape data and remembers the file position for later.
|
||||||
|
Land *land = new Land;
|
||||||
|
land->load(esm);
|
||||||
|
|
||||||
|
// Store the structure
|
||||||
|
lands[land->X][land->Y] = land;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Cells aren't simply indexed by name. Exterior cells are treated
|
// Cells aren't simply indexed by name. Exterior cells are treated
|
||||||
// separately.
|
// separately.
|
||||||
// TODO: case handling (cell names are case-insensitive, but they are also showen to the
|
// TODO: case handling (cell names are case-insensitive, but they are also showen to the
|
||||||
|
@ -254,8 +327,6 @@ namespace ESMS
|
||||||
|
|
||||||
void load(ESMReader &esm, const std::string &id)
|
void load(ESMReader &esm, const std::string &id)
|
||||||
{
|
{
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
// All cells have a name record, even nameless exterior cells.
|
// All cells have a name record, even nameless exterior cells.
|
||||||
|
|
|
@ -69,10 +69,10 @@ namespace ESMS
|
||||||
// Lists that need special rules
|
// Lists that need special rules
|
||||||
CellList cells;
|
CellList cells;
|
||||||
RecIDListT<GameSetting> gameSettings;
|
RecIDListT<GameSetting> gameSettings;
|
||||||
//RecListT<Land> lands;
|
LandList lands;
|
||||||
//RecListT<LandTexture> landTexts;
|
LTexList landTexts;
|
||||||
IndexListT<MagicEffect> magicEffects;
|
|
||||||
ScriptListT<Script> scripts;
|
ScriptListT<Script> scripts;
|
||||||
|
IndexListT<MagicEffect> magicEffects;
|
||||||
IndexListT<Skill> skills;
|
IndexListT<Skill> skills;
|
||||||
//RecListT<PathGrid> pathgrids;
|
//RecListT<PathGrid> pathgrids;
|
||||||
|
|
||||||
|
@ -115,12 +115,13 @@ namespace ESMS
|
||||||
recLists[REC_GLOB] = &globals;
|
recLists[REC_GLOB] = &globals;
|
||||||
recLists[REC_GMST] = &gameSettings;
|
recLists[REC_GMST] = &gameSettings;
|
||||||
recLists[REC_INGR] = &ingreds;
|
recLists[REC_INGR] = &ingreds;
|
||||||
//recLists[REC_LAND] = &lands;
|
recLists[REC_LAND] = &lands;
|
||||||
recLists[REC_LEVC] = &creatureLists;
|
recLists[REC_LEVC] = &creatureLists;
|
||||||
recLists[REC_LEVI] = &itemLists;
|
recLists[REC_LEVI] = &itemLists;
|
||||||
recLists[REC_LIGH] = &lights;
|
recLists[REC_LIGH] = &lights;
|
||||||
recLists[REC_LOCK] = &lockpicks;
|
recLists[REC_LOCK] = &lockpicks;
|
||||||
//recLists[REC_LTEX] = &landTexts;
|
recLists[REC_LTEX] = &landTexts;
|
||||||
|
//recLists[REC_MGEF] = &magicEffects;
|
||||||
recLists[REC_MISC] = &miscItems;
|
recLists[REC_MISC] = &miscItems;
|
||||||
recLists[REC_NPC_] = &npcs;
|
recLists[REC_NPC_] = &npcs;
|
||||||
recLists[REC_NPCC] = &npcChange;
|
recLists[REC_NPCC] = &npcChange;
|
||||||
|
|
1
components/terrain/.gitignore
vendored
Normal file
1
components/terrain/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
old
|
48
components/terrain/esm_land_factory.cpp
Normal file
48
components/terrain/esm_land_factory.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include "esm_land_factory.hpp"
|
||||||
|
|
||||||
|
// The first one already includes the others implicitly, but it
|
||||||
|
// doesn't hurt to be explicit.
|
||||||
|
#include "../esm_store/store.hpp"
|
||||||
|
#include "../esm/esm_reader.hpp"
|
||||||
|
#include "../esm/loadland.hpp"
|
||||||
|
|
||||||
|
using namespace Terrain;
|
||||||
|
|
||||||
|
static class ESMLandStream : public Mangle::Stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ESMLandStream(ESM::Land *l, ESM::ESMReader &r)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ESMLandFactory::has(int x, int y)
|
||||||
|
{
|
||||||
|
return store.landscapes.has(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
LandDataPtr get(int x, int y, LandInfo &info)
|
||||||
|
{
|
||||||
|
assert(has(x,y));
|
||||||
|
|
||||||
|
// Set up the info
|
||||||
|
info.grid = LGT_Quadratic;
|
||||||
|
info.data = LDT_Float;
|
||||||
|
|
||||||
|
const float SIZE = 8192; // CHECK
|
||||||
|
|
||||||
|
info.xsize = SIZE;
|
||||||
|
info.ysize = SIZE;
|
||||||
|
info.numx = 65;
|
||||||
|
info.numy = 65;
|
||||||
|
info.xoffset = SIZE*x;
|
||||||
|
info.yoffset = SIZE*y;
|
||||||
|
|
||||||
|
// Get the Land struct from store
|
||||||
|
ESM::Land* land = store.landscapes.find(x,y);
|
||||||
|
assert(land->hasData);
|
||||||
|
|
||||||
|
// Create a stream for the data and return it.
|
||||||
|
LandDataPtr ptr(new ESMLandStream(land, reader));
|
||||||
|
return ptr;
|
||||||
|
}
|
41
components/terrain/esm_land_factory.hpp
Normal file
41
components/terrain/esm_land_factory.hpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef TERRAIN_ESM_LAND_FACTORY_H
|
||||||
|
#define TERRAIN_ESM_LAND_FACTORY_H
|
||||||
|
|
||||||
|
#include "land_factory.hpp"
|
||||||
|
|
||||||
|
namespace ESMS
|
||||||
|
{
|
||||||
|
struct ESMStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Terrain
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Land factory that loads data from ESM files.
|
||||||
|
*/
|
||||||
|
class ESMLandFactory
|
||||||
|
{
|
||||||
|
ESMS::ESMStore &store;
|
||||||
|
ESM::ESMReader &reader;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Initialize the land factory. Note that refrences to the given
|
||||||
|
// store and reader are stored in the class, so the given objects
|
||||||
|
// must be valid for a long as you plan to use this factory.
|
||||||
|
ESMLandFactory(ESMS::ESMStore &st, ESM::ESMReader &rd)
|
||||||
|
: store(st), reader(rd) {}
|
||||||
|
|
||||||
|
// True if this factory has any data for the given grid cell.
|
||||||
|
bool has(int x, int y);
|
||||||
|
|
||||||
|
// Return stream to data for this cell. Additional data about the
|
||||||
|
// landscape is returned through the LandInfo struct.
|
||||||
|
LandDataPtr get(int x, int y, LandInfo &info);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
38
components/terrain/heightmap.hpp
Normal file
38
components/terrain/heightmap.hpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef TERRAIN_HEIGHTMAP_H
|
||||||
|
#define TERRAIN_HEIGHTMAP_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generic interface for a structure holding heightmap data.
|
||||||
|
|
||||||
|
A HeightMap returns information about landscape data in the form of
|
||||||
|
a regular grid of float heights.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Terrain
|
||||||
|
{
|
||||||
|
struct HeightMap
|
||||||
|
{
|
||||||
|
// Get height from grid position, counted from 0 to getNumX/Y().
|
||||||
|
virtual float getHeight(int x, int y) = 0;
|
||||||
|
|
||||||
|
// Get heigth from vertex index, assumed to be y*getNumX() + x.
|
||||||
|
virtual float getHeight(int index) = 0;
|
||||||
|
|
||||||
|
virtual float getMinX() = 0;
|
||||||
|
virtual float getMaxX() = 0;
|
||||||
|
virtual float getMinY() = 0;
|
||||||
|
virtual float getMaxY() = 0;
|
||||||
|
|
||||||
|
virtual int getNumX() = 0;
|
||||||
|
virtual int getNumY() = 0;
|
||||||
|
|
||||||
|
// True if the given coordinate is within the grid
|
||||||
|
bool isWithin(float x, float y)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
x >= getMinX() && x < getMaxX() &&
|
||||||
|
y >= getMinY() && y < getMaxY();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
72
components/terrain/heightmapbuf.hpp
Normal file
72
components/terrain/heightmapbuf.hpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#ifndef TERRAIN_HEIGHTMAPBUF_H
|
||||||
|
#define TERRAIN_HEIGHTMAPBUF_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
A HeightMap implementation that stores heigths in a buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "heightmap.hpp"
|
||||||
|
#include "land_factory.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace Terrain
|
||||||
|
{
|
||||||
|
class HeightMapBuffer : public HeightMap
|
||||||
|
{
|
||||||
|
std::vector<float> buf;
|
||||||
|
|
||||||
|
float beginX, sizeX, endX;
|
||||||
|
float beginY, sizeY, endY;
|
||||||
|
|
||||||
|
int numX, numY;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void load(LandDataPtr data, const LandInfo &info)
|
||||||
|
{
|
||||||
|
// We don't support other kinds of grid data yet.
|
||||||
|
assert(info.grid == LGT_Quadratic);
|
||||||
|
assert(info.data == LDT_Float);
|
||||||
|
|
||||||
|
// Set up internal data
|
||||||
|
beginX = info.xoffset;
|
||||||
|
sizeX = info.xsize;
|
||||||
|
endX = beginX+sizeX;
|
||||||
|
numX = info.numx;
|
||||||
|
|
||||||
|
beginY = info.yoffset;
|
||||||
|
sizeY = info.ysize;
|
||||||
|
endY = beginY+sizeY;
|
||||||
|
numY = info.numy;
|
||||||
|
|
||||||
|
// Prepare the buffer and load it
|
||||||
|
buf.resize(numX*numY);
|
||||||
|
|
||||||
|
data.read(&buf[0], buf.size()*sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions inherited from HeightMap:
|
||||||
|
|
||||||
|
float getHeight(int x, int y)
|
||||||
|
{
|
||||||
|
assert(x>=0 && x<numX);
|
||||||
|
assert(y>=0 && y<numY);
|
||||||
|
return getHeight(x + y*numX);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getHeight(int index)
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index < buf.size());
|
||||||
|
return buf[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
float getMinX() { return beginX; }
|
||||||
|
float getMaxX() { return endX; }
|
||||||
|
float getMinY() { return beginY; }
|
||||||
|
float getMaxY() { return endY; }
|
||||||
|
|
||||||
|
int getNumX() { return numX; }
|
||||||
|
int getNumY() { return numY; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
55
components/terrain/land_factory.hpp
Normal file
55
components/terrain/land_factory.hpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef TERRAIN_LAND_FACTORY_H
|
||||||
|
#define TERRAIN_LAND_FACTORY_H
|
||||||
|
|
||||||
|
#include <mangle/stream/stream.hpp>
|
||||||
|
|
||||||
|
namespace Terrain
|
||||||
|
{
|
||||||
|
enum LandInfoGridType
|
||||||
|
{
|
||||||
|
LGT_Quadratic
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LandInfoDataType
|
||||||
|
{
|
||||||
|
LDT_Float
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LandInfo
|
||||||
|
{
|
||||||
|
// Type information
|
||||||
|
LandInfoGridType grid;
|
||||||
|
LandInfoDataType data;
|
||||||
|
|
||||||
|
// Landscape size and number of vertices. Note that xsize and
|
||||||
|
// ysize may be negative, signaling a flipped landscape in that
|
||||||
|
// direction.
|
||||||
|
float xsize, ysize;
|
||||||
|
int numx, numy;
|
||||||
|
|
||||||
|
// World offset along the same x/y axes. Whether these are set or
|
||||||
|
// used depends on the client implementation.
|
||||||
|
float xoffset, yoffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* We use normal streams for data. This allows us to just pass (for
|
||||||
|
example) a file stream as height map input later, with no extra
|
||||||
|
fuzz.
|
||||||
|
*/
|
||||||
|
typedef Mangle::Stream::StreamPtr LandDataPtr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Factory class that provides streams to land data cells. Each
|
||||||
|
"cell" has a unique integer coordinate in the plane.
|
||||||
|
*/
|
||||||
|
struct LandFactory
|
||||||
|
{
|
||||||
|
// True if this factory has any data for the given grid cell.
|
||||||
|
virtual bool has(int x, int y) = 0;
|
||||||
|
|
||||||
|
// Return stream to data for this cell. Additional data about the
|
||||||
|
// landscape is returned through the LandInfo struct.
|
||||||
|
virtual LandDataPtr get(int x, int y, LandInfo &info) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
1
components/terrain/tests/.gitignore
vendored
Normal file
1
components/terrain/tests/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*_test
|
14
components/terrain/tests/Makefile
Normal file
14
components/terrain/tests/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
GCC=g++
|
||||||
|
|
||||||
|
all: triangle_test esm_test
|
||||||
|
|
||||||
|
LIB_INC=-I../../../libs/
|
||||||
|
|
||||||
|
triangle_test: triangle_test.cpp
|
||||||
|
$(GCC) $^ -o $@
|
||||||
|
|
||||||
|
esm_test: esm_test.cpp
|
||||||
|
$(GCC) $^ -o $@ $(LIB_INC)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm *_test
|
10
components/terrain/tests/esm_test.cpp
Normal file
10
components/terrain/tests/esm_test.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "../esm_land_factory.hpp"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
cout << "under development\n";
|
||||||
|
return 0;
|
||||||
|
}
|
1
components/terrain/tests/output/esm_test.out
Normal file
1
components/terrain/tests/output/esm_test.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
under development
|
55
components/terrain/tests/output/triangle_test.out
Normal file
55
components/terrain/tests/output/triangle_test.out
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
Cell types:
|
||||||
|
\ / \ /
|
||||||
|
/ \ / \
|
||||||
|
\ / \ /
|
||||||
|
/ \ / \
|
||||||
|
|
||||||
|
Full index list:
|
||||||
|
0
|
||||||
|
6
|
||||||
|
5
|
||||||
|
0
|
||||||
|
1
|
||||||
|
6
|
||||||
|
1
|
||||||
|
2
|
||||||
|
6
|
||||||
|
6
|
||||||
|
2
|
||||||
|
7
|
||||||
|
2
|
||||||
|
8
|
||||||
|
7
|
||||||
|
2
|
||||||
|
3
|
||||||
|
8
|
||||||
|
3
|
||||||
|
4
|
||||||
|
8
|
||||||
|
8
|
||||||
|
4
|
||||||
|
9
|
||||||
|
5
|
||||||
|
6
|
||||||
|
10
|
||||||
|
10
|
||||||
|
6
|
||||||
|
11
|
||||||
|
6
|
||||||
|
12
|
||||||
|
11
|
||||||
|
6
|
||||||
|
7
|
||||||
|
12
|
||||||
|
7
|
||||||
|
8
|
||||||
|
12
|
||||||
|
12
|
||||||
|
8
|
||||||
|
13
|
||||||
|
8
|
||||||
|
14
|
||||||
|
13
|
||||||
|
8
|
||||||
|
9
|
||||||
|
14
|
18
components/terrain/tests/test.sh
Executable file
18
components/terrain/tests/test.sh
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
make || exit
|
||||||
|
|
||||||
|
mkdir -p output
|
||||||
|
|
||||||
|
PROGS=*_test
|
||||||
|
|
||||||
|
for a in $PROGS; do
|
||||||
|
if [ -f "output/$a.out" ]; then
|
||||||
|
echo "Running $a:"
|
||||||
|
./$a | diff output/$a.out -
|
||||||
|
else
|
||||||
|
echo "Creating $a.out"
|
||||||
|
./$a > "output/$a.out"
|
||||||
|
git add "output/$a.out"
|
||||||
|
fi
|
||||||
|
done
|
93
components/terrain/tests/triangle_test.cpp
Normal file
93
components/terrain/tests/triangle_test.cpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "../triangulator.hpp"
|
||||||
|
|
||||||
|
const int X = 4;
|
||||||
|
const int Y = 4;
|
||||||
|
|
||||||
|
typedef Terrain::Triangulator<short,X,Y> Triangles4x4;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Triangles4x4 t;
|
||||||
|
|
||||||
|
cout << "Cell types:\n";
|
||||||
|
for(int y=0;y<Y;y++)
|
||||||
|
{
|
||||||
|
for(int x=0;x<X;x++)
|
||||||
|
{
|
||||||
|
if(t.cellType(x,y)) cout << "/ ";
|
||||||
|
else cout << "\\ ";
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
cout << "Full index list:\n";
|
||||||
|
for(int i=0; i<X*Y*3; i++)
|
||||||
|
cout << t.getData()[i] << endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code we might add later:
|
||||||
|
|
||||||
|
// Get the vertex indices belonging to a given triangle
|
||||||
|
void getTriangle(int trinum, Index &p1, Index &p2, Index &p3)
|
||||||
|
{
|
||||||
|
assert(trinum >= 0 && trinum < TriNum);
|
||||||
|
trinum *= 3;
|
||||||
|
|
||||||
|
p1 = array[trinum++];
|
||||||
|
p2 = array[trinum++];
|
||||||
|
p3 = array[trinum];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get height interpolation weights for a given grid square. The
|
||||||
|
input is the grid square number (x,y) and the relative position
|
||||||
|
within that square (xrel,yrel = [0.0..1.0].) The weights are
|
||||||
|
returned as three vertex index + weight factor pairs.
|
||||||
|
|
||||||
|
A more user-friendly version for HeightMap structs is given
|
||||||
|
below.
|
||||||
|
* /
|
||||||
|
void getWeights(int x, int y, float xrel, float yrel,
|
||||||
|
Index &p1, float w1,
|
||||||
|
Index &p2, float w2,
|
||||||
|
Index &p3, float w3)
|
||||||
|
{
|
||||||
|
// Find cell index
|
||||||
|
int index = y*SizeX + x;
|
||||||
|
|
||||||
|
// First triangle in cell
|
||||||
|
index *= 2;
|
||||||
|
|
||||||
|
// The rest depends on how the cell is triangulated
|
||||||
|
if(cellType(x,y))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Cell is divided as \ from 0,0 to 1,1
|
||||||
|
if(xrel < yrel)
|
||||||
|
{
|
||||||
|
// Bottom left triangle.
|
||||||
|
|
||||||
|
// Order is (0,0),(1,1),(0,1).
|
||||||
|
getTriangle(index, p1,p2,p3);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Top right triangle
|
||||||
|
|
||||||
|
// Order is (0,0),(1,0),(1,1).
|
||||||
|
getTriangle(index+1, p1,p2,p3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
104
components/terrain/triangulator.hpp
Normal file
104
components/terrain/triangulator.hpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#ifndef TERRAIN_TRIANGULATOR_H
|
||||||
|
#define TERRAIN_TRIANGULATOR_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
The triangulator is a simple math helper class, used for dividing a
|
||||||
|
regular square grid into alternating set of triangles. It divides a
|
||||||
|
grid like this:
|
||||||
|
|
||||||
|
+----+----+
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
+----+----+
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
+----+----+
|
||||||
|
|
||||||
|
into this:
|
||||||
|
|
||||||
|
+----+----+
|
||||||
|
| \ 2|3 / |
|
||||||
|
|1 \ | / 4|
|
||||||
|
+----+----+
|
||||||
|
|5 / | \ 8|
|
||||||
|
| / 6|7 \ |
|
||||||
|
+----+----+
|
||||||
|
|
||||||
|
Since the triangulation information is typically the same for all
|
||||||
|
terrains of the same size, once instance can usually be shared.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace Terrain
|
||||||
|
{
|
||||||
|
// Index number type, number of grid cells (not vertices) in X and Y
|
||||||
|
// directions.
|
||||||
|
template <typename Index, int SizeX, int SizeY>
|
||||||
|
class Triangulator
|
||||||
|
{
|
||||||
|
// Number of triangles
|
||||||
|
static const int TriNum = SizeX * SizeY * 2;
|
||||||
|
|
||||||
|
// 3 indices per triangle
|
||||||
|
Index array[TriNum * 3];
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Get raw triangle data pointer. Typically used for creating
|
||||||
|
// meshes.
|
||||||
|
const Index *getData() { return array; }
|
||||||
|
|
||||||
|
// Return whether a given cell is divided as / (true) or \
|
||||||
|
// (false).
|
||||||
|
static bool cellType(int x, int y)
|
||||||
|
{
|
||||||
|
assert(x >= 0 && x < SizeX);
|
||||||
|
assert(y >= 0 && y < SizeY);
|
||||||
|
|
||||||
|
bool even = (x & 1) == 1;
|
||||||
|
if((y & 1) == 1) even = !even;
|
||||||
|
return even;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor sets up the index buffer
|
||||||
|
Triangulator()
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
for ( int y = 0; y < SizeX; y++ )
|
||||||
|
for ( int x = 0; x < SizeY; x++ )
|
||||||
|
{
|
||||||
|
// Get vertex indices
|
||||||
|
Index line1 = y*(SizeX+1) + x;
|
||||||
|
Index line2 = line1 + SizeX+1;
|
||||||
|
|
||||||
|
if(cellType(x,y))
|
||||||
|
{
|
||||||
|
// Top left
|
||||||
|
array[index++] = line1;
|
||||||
|
array[index++] = line1 + 1;
|
||||||
|
array[index++] = line2;
|
||||||
|
|
||||||
|
// Bottom right
|
||||||
|
array[index++] = line2;
|
||||||
|
array[index++] = line1 + 1;
|
||||||
|
array[index++] = line2 + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Bottom left
|
||||||
|
array[index++] = line1;
|
||||||
|
array[index++] = line2 + 1;
|
||||||
|
array[index++] = line2;
|
||||||
|
|
||||||
|
// Top right
|
||||||
|
array[index++] = line1;
|
||||||
|
array[index++] = line1 + 1;
|
||||||
|
array[index++] = line2 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(index == TriNum*3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // Namespace
|
||||||
|
|
||||||
|
#endif
|
|
@ -352,46 +352,7 @@ void genIndexData()
|
||||||
cache.addVertexBuffer(lev,vertList);
|
cache.addVertexBuffer(lev,vertList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pregenerate triangle indices
|
// index stuff already ported
|
||||||
int size = 64*64*6;
|
|
||||||
auto indList = new ushort[size];
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
bool flag = false;
|
|
||||||
for ( int y = 0; y < 64; y++ )
|
|
||||||
{
|
|
||||||
for ( int x = 0; x < 64; x++ )
|
|
||||||
{
|
|
||||||
int line1 = y*65 + x;
|
|
||||||
int line2 = (y+1)*65 + x;
|
|
||||||
|
|
||||||
if ( flag )
|
|
||||||
{
|
|
||||||
indList[index++] = line1;
|
|
||||||
indList[index++] = line1 + 1;
|
|
||||||
indList[index++] = line2;
|
|
||||||
|
|
||||||
indList[index++] = line2;
|
|
||||||
indList[index++] = line1 + 1;
|
|
||||||
indList[index++] = line2 + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
indList[index++] = line1;
|
|
||||||
indList[index++] = line2 + 1;
|
|
||||||
indList[index++] = line2;
|
|
||||||
|
|
||||||
indList[index++] = line1;
|
|
||||||
indList[index++] = line1 + 1;
|
|
||||||
indList[index++] = line2 + 1;
|
|
||||||
}
|
|
||||||
flag = !flag; //flip tris for next time
|
|
||||||
}
|
|
||||||
flag = !flag; //flip tries for next row
|
|
||||||
}
|
|
||||||
assert(index == indList.length);
|
|
||||||
|
|
||||||
cache.setIndexBuffer(indList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void genLevel(int level, int X, int Y, ref GenLevelResult result,
|
void genLevel(int level, int X, int Y, ref GenLevelResult result,
|
||||||
|
|
Loading…
Reference in a new issue