mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-01 20:15:33 +00:00
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
|
||||
# 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
|
||||
# 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
|
||||
# with spaces.
|
||||
|
||||
INPUT = ../apps
|
||||
../components
|
||||
../libs
|
||||
../docs
|
||||
INPUT = apps \
|
||||
components \
|
||||
libs \
|
||||
|
||||
# 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
|
||||
|
|
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})
|
||||
|
||||
set(GAMEMECHANICS
|
||||
mwmechanics/mechanicsmanager.cpp)
|
||||
mwmechanics/mechanicsmanager.cpp
|
||||
mwmechanics/magiceffects.cpp
|
||||
)
|
||||
set(GAMEMECHANICS_HEADER
|
||||
mwmechanics/mechanicsmanager.hpp
|
||||
mwmechanics/stat.hpp
|
||||
mwmechanics/creaturestats.hpp
|
||||
mwmechanics/magiceffects.hpp
|
||||
)
|
||||
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)
|
||||
, mNewGame (false)
|
||||
, mUseSound (true)
|
||||
, mCompileAll (false)
|
||||
, mScriptManager (0)
|
||||
, mScriptContext (0)
|
||||
, mGuiManager (0)
|
||||
|
@ -278,9 +279,20 @@ void OMW::Engine::go()
|
|||
|
||||
// load cell
|
||||
ESM::Position pos;
|
||||
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
||||
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
||||
mEnvironment.mWorld->changeCell (mCellName, pos);
|
||||
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);
|
||||
}
|
||||
|
||||
// Sets up the input system
|
||||
MWInput::MWInputManager input(mOgre, mEnvironment.mWorld->getPlayerPos(),
|
||||
|
@ -305,6 +317,29 @@ void OMW::Engine::go()
|
|||
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
|
||||
mOgre.start();
|
||||
|
||||
|
@ -345,3 +380,8 @@ void OMW::Engine::activate()
|
|||
interpreterContext.executeActivation();
|
||||
}
|
||||
}
|
||||
|
||||
void OMW::Engine::setCompileAll (bool all)
|
||||
{
|
||||
mCompileAll = all;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ namespace OMW
|
|||
bool mVerboseScripts;
|
||||
bool mNewGame;
|
||||
bool mUseSound;
|
||||
bool mCompileAll;
|
||||
|
||||
MWWorld::Environment mEnvironment;
|
||||
MWScript::ScriptManager *mScriptManager;
|
||||
|
@ -127,6 +128,9 @@ namespace OMW
|
|||
|
||||
/// Activate the focussed object.
|
||||
void activate();
|
||||
|
||||
/// Compile all scripts (excludign dialogue scripts) at startup?
|
||||
void setCompileAll (bool all);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ using namespace std;
|
|||
bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||
{
|
||||
// Create a local alias for brevity
|
||||
namespace bpo = boost::program_options;
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
bpo::options_description desc (
|
||||
"Syntax: openmw <options>\nAllowed options");
|
||||
|
@ -43,23 +43,24 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
|||
("data", bpo::value<std::string>()->default_value ("data"),
|
||||
"set data directory")
|
||||
("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 file")
|
||||
( "debug", "debug mode" )
|
||||
( "nosound", "disable all sound" )
|
||||
( "script-verbose", "verbose script output" )
|
||||
( "new-game", "activate char gen/new game mechanics" )
|
||||
( "script-all", "compile all scripts (excluding dialogue scripts) at startup")
|
||||
;
|
||||
|
||||
|
||||
bpo::variables_map variables;
|
||||
|
||||
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||
std::string configFilePath(macBundlePath() + "/Contents/MacOS/openmw.cfg");
|
||||
std::ifstream configFile (configFilePath.c_str());
|
||||
#else
|
||||
std::ifstream configFile ("openmw.cfg");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
|
||||
|
||||
|
@ -78,19 +79,22 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
|||
engine.setDataDir (variables["data"].as<std::string>());
|
||||
engine.setCell (variables["start"].as<std::string>());
|
||||
engine.addMaster (variables["master"].as<std::string>());
|
||||
|
||||
|
||||
if (variables.count ("debug"))
|
||||
engine.enableDebugMode();
|
||||
|
||||
if (variables.count ("nosound"))
|
||||
engine.disableSound();
|
||||
|
||||
|
||||
if (variables.count ("script-verbose"))
|
||||
engine.enableVerboseScripts();
|
||||
|
||||
if (variables.count ("new-game"))
|
||||
engine.setNewGame();
|
||||
|
||||
if (variables.count ("script-all"))
|
||||
engine.setCompileAll (true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -100,7 +104,7 @@ int main(int argc, char**argv)
|
|||
try
|
||||
{
|
||||
OMW::Engine engine;
|
||||
|
||||
|
||||
if (parseOptions (argc, argv, engine))
|
||||
{
|
||||
engine.go();
|
||||
|
@ -111,7 +115,7 @@ int main(int argc, char**argv)
|
|||
cout << "\nERROR: " << e.what() << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -560,13 +560,19 @@ void WindowManager::onClassChoice(MyGUI::WidgetPtr, int _index)
|
|||
}
|
||||
}
|
||||
|
||||
void WindowManager::showClassQuestionDialog()
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
struct Step
|
||||
{
|
||||
const char* text;
|
||||
const char* buttons[3];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void WindowManager::showClassQuestionDialog()
|
||||
{
|
||||
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.",
|
||||
{"Use herbs from your pack to put it to sleep?",
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
#ifndef GAME_MWMECHANICS_CREATURESTATS_H
|
||||
#define GAME_MWMECHANICS_CREATURESTATS_H
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "stat.hpp"
|
||||
#include "magiceffects.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
@ -10,6 +14,8 @@ namespace MWMechanics
|
|||
Stat<int> mAttributes[8];
|
||||
DynamicStat<int> mDynamic[3]; // health, magicka, fatigue
|
||||
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,63 +22,123 @@ namespace MWMechanics
|
|||
|
||||
// reset
|
||||
creatureStats.mLevel = player->npdt52.level;
|
||||
creatureStats.mAbilities.clear();
|
||||
creatureStats.mMagicEffects = MagicEffects();
|
||||
|
||||
for (int i=0; i<27; ++i)
|
||||
npcStats.mSkill[i].setBase (player->npdt52.skills[i]);
|
||||
|
||||
// race
|
||||
const ESM::Race *race =
|
||||
mEnvironment.mWorld->getStore().races.find (mEnvironment.mWorld->getPlayerPos().getRace());
|
||||
|
||||
bool male = mEnvironment.mWorld->getPlayerPos().isMale();
|
||||
|
||||
for (int i=0; i<8; ++i)
|
||||
if (mRaceSelected)
|
||||
{
|
||||
const ESM::Race::MaleFemale *attribute = 0;
|
||||
switch (i)
|
||||
const ESM::Race *race =
|
||||
mEnvironment.mWorld->getStore().races.find (
|
||||
mEnvironment.mWorld->getPlayerPos().getRace());
|
||||
|
||||
bool male = mEnvironment.mWorld->getPlayerPos().isMale();
|
||||
|
||||
for (int i=0; i<8; ++i)
|
||||
{
|
||||
case 0: attribute = &race->data.strength; break;
|
||||
case 1: attribute = &race->data.intelligence; break;
|
||||
case 2: attribute = &race->data.willpower; break;
|
||||
case 3: attribute = &race->data.agility; break;
|
||||
case 4: attribute = &race->data.speed; break;
|
||||
case 5: attribute = &race->data.endurance; break;
|
||||
case 6: attribute = &race->data.personality; break;
|
||||
case 7: attribute = &race->data.luck; break;
|
||||
const ESM::Race::MaleFemale *attribute = 0;
|
||||
switch (i)
|
||||
{
|
||||
case 0: attribute = &race->data.strength; break;
|
||||
case 1: attribute = &race->data.intelligence; break;
|
||||
case 2: attribute = &race->data.willpower; break;
|
||||
case 3: attribute = &race->data.agility; break;
|
||||
case 4: attribute = &race->data.speed; break;
|
||||
case 5: attribute = &race->data.endurance; break;
|
||||
case 6: attribute = &race->data.personality; break;
|
||||
case 7: attribute = &race->data.luck; break;
|
||||
}
|
||||
|
||||
creatureStats.mAttributes[i].setBase (
|
||||
static_cast<int> (male ? attribute->male : attribute->female));
|
||||
}
|
||||
|
||||
creatureStats.mAttributes[i].setBase (
|
||||
static_cast<int> (male ? attribute->male : attribute->female));
|
||||
}
|
||||
|
||||
for (int i=0; i<7; ++i)
|
||||
{
|
||||
int index = race->data.bonus[i].skill;
|
||||
|
||||
if (index>=0 && index<27)
|
||||
for (int i=0; i<7; ++i)
|
||||
{
|
||||
npcStats.mSkill[i].setBase (
|
||||
npcStats.mSkill[i].getBase() + race->data.bonus[i].bonus);
|
||||
int index = race->data.bonus[i].skill;
|
||||
|
||||
if (index>=0 && index<27)
|
||||
{
|
||||
npcStats.mSkill[index].setBase (
|
||||
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
|
||||
|
||||
// class
|
||||
const ESM::Class& class_ = mEnvironment.mWorld->getPlayerPos().getClass();
|
||||
|
||||
for (int i=0; i<2; ++i)
|
||||
if (!mEnvironment.mWorld->getPlayerPos().getBirthsign().empty())
|
||||
{
|
||||
int attribute = class_.data.attribute[i];
|
||||
if (attribute>=0 && attribute<8)
|
||||
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)
|
||||
{
|
||||
creatureStats.mAttributes[attribute].setBase (
|
||||
creatureStats.mAttributes[attribute].getBase() + 10);
|
||||
insertSpell (*iter, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// class
|
||||
if (mClassSelected)
|
||||
{
|
||||
const ESM::Class& class_ = mEnvironment.mWorld->getPlayerPos().getClass();
|
||||
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
int attribute = class_.data.attribute[i];
|
||||
if (attribute>=0 && attribute<8)
|
||||
{
|
||||
creatureStats.mAttributes[attribute].setBase (
|
||||
creatureStats.mAttributes[attribute].getBase() + 10);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
int strength = creatureStats.mAttributes[0].getBase();
|
||||
|
@ -87,17 +147,81 @@ namespace MWMechanics
|
|||
int agility = creatureStats.mAttributes[3].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)));
|
||||
// TODO: calculate factor
|
||||
creatureStats.mDynamic[1].setBase (static_cast<int> (intelligence + 1 * intelligence));
|
||||
creatureStats.mDynamic[1].setBase (static_cast<int> (intelligence +
|
||||
magickaFactor * intelligence));
|
||||
creatureStats.mDynamic[2].setBase (strength+willpower+agility+endurance);
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
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)
|
||||
: mEnvironment (environment), mUpdatePlayer (true)
|
||||
: mEnvironment (environment), mUpdatePlayer (true), mClassSelected (false),
|
||||
mRaceSelected (false)
|
||||
{
|
||||
buildPlayer();
|
||||
}
|
||||
|
@ -237,6 +361,7 @@ namespace MWMechanics
|
|||
{
|
||||
mEnvironment.mWorld->getPlayerPos().setGender (male);
|
||||
mEnvironment.mWorld->getPlayerPos().setRace (race);
|
||||
mRaceSelected = true;
|
||||
buildPlayer();
|
||||
mUpdatePlayer = true;
|
||||
}
|
||||
|
@ -250,6 +375,7 @@ namespace MWMechanics
|
|||
void MechanicsManager::setPlayerClass (const std::string& id)
|
||||
{
|
||||
mEnvironment.mWorld->getPlayerPos().setClass (*mEnvironment.mWorld->getStore().classes.find (id));
|
||||
mClassSelected = true;
|
||||
buildPlayer();
|
||||
mUpdatePlayer = true;
|
||||
}
|
||||
|
@ -257,6 +383,7 @@ namespace MWMechanics
|
|||
void MechanicsManager::setPlayerClass (const ESM::Class& class_)
|
||||
{
|
||||
mEnvironment.mWorld->getPlayerPos().setClass (class_);
|
||||
mClassSelected = true;
|
||||
buildPlayer();
|
||||
mUpdatePlayer = true;
|
||||
}
|
||||
|
|
|
@ -23,11 +23,17 @@ namespace MWMechanics
|
|||
CreatureStats mWatchedCreature;
|
||||
NpcStats mWatchedNpc;
|
||||
bool mUpdatePlayer;
|
||||
bool mClassSelected;
|
||||
bool mRaceSelected;
|
||||
|
||||
void buildPlayer();
|
||||
///< 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.
|
||||
|
||||
void insertSpell (const std::string& id, MWWorld::Ptr& creature);
|
||||
|
||||
void adjustMagicEffects (MWWorld::Ptr& creature);
|
||||
|
||||
public:
|
||||
|
||||
MechanicsManager (MWWorld::Environment& environment);
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Interpreter
|
|||
|
||||
namespace MWScript
|
||||
{
|
||||
/// \brief stats-related script functionality (creatures and NPCs)
|
||||
/// \brief Container-related script functionality (chests, NPCs, creatures)
|
||||
namespace Container
|
||||
{
|
||||
void registerExtensions (Compiler::Extensions& extensions);
|
||||
|
|
|
@ -95,4 +95,10 @@ op 0x2000085-0x200008b: Disable Controls
|
|||
op 0x200008c: Unlock
|
||||
op 0x200008d: Unlock, explicit reference
|
||||
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
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Interpreter
|
|||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
{
|
||||
class ScriptManager
|
||||
{
|
||||
Compiler::StreamErrorHandler mErrorHandler;
|
||||
|
@ -35,19 +35,21 @@ namespace MWScript
|
|||
bool mVerbose;
|
||||
Compiler::Context& mCompilerContext;
|
||||
Compiler::FileParser mParser;
|
||||
|
||||
|
||||
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
|
||||
|
||||
bool compile (const std::string& name);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
||||
Compiler::Context& compilerContext);
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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 opcodeGetAttribute = 0x2000027;
|
||||
|
@ -490,6 +644,15 @@ namespace MWScript
|
|||
const int opcodeGetDynamicGetRatio = 0x200006f;
|
||||
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)
|
||||
{
|
||||
static const char *attributes[numberOfAttributes] =
|
||||
|
@ -503,6 +666,16 @@ namespace MWScript
|
|||
"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 set ("set");
|
||||
std::string mod ("mod");
|
||||
|
@ -537,7 +710,18 @@ namespace MWScript
|
|||
|
||||
extensions.registerFunction (get + dynamics[i] + getRatio, 'f', "",
|
||||
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,
|
||||
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
|
||||
// etc.)
|
||||
char skill, attribute; // -1 if N/A
|
||||
signed char skill, attribute; // -1 if N/A
|
||||
|
||||
// Other spell parameters
|
||||
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
||||
|
@ -84,7 +84,7 @@ struct EffectList
|
|||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
ENAMstruct s;
|
||||
ENAMstruct s;
|
||||
while(esm.isNextSub("ENAM"))
|
||||
{
|
||||
esm.getHT(s, 24);
|
||||
|
|
|
@ -215,6 +215,7 @@ struct Cell
|
|||
else ref.teleport = false;
|
||||
|
||||
// Integer, despite the name suggesting otherwise
|
||||
ref.lockLevel = 0;
|
||||
esm.getHNOT(ref.lockLevel, "FLTV");
|
||||
ref.key = esm.getHNOString("KNAM");
|
||||
ref.trap = esm.getHNOString("TNAM");
|
||||
|
|
|
@ -25,8 +25,9 @@ struct Land
|
|||
{
|
||||
// Get the grid location
|
||||
esm.getSubNameIs("INTV");
|
||||
esm.getT(X);
|
||||
esm.getT(Y);
|
||||
esm.getSubHeaderIs(8);
|
||||
esm.getT<int>(X);
|
||||
esm.getT<int>(Y);
|
||||
|
||||
esm.getHNT(flags, "DATA");
|
||||
|
||||
|
|
|
@ -23,12 +23,11 @@ namespace ESM {
|
|||
|
||||
struct LandTexture
|
||||
{
|
||||
std::string name, texture;
|
||||
std::string id, texture;
|
||||
int index;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
name = esm.getHNString("NAME");
|
||||
esm.getHNT(index, "INTV");
|
||||
texture = esm.getHNString("DATA");
|
||||
}
|
||||
|
|
|
@ -160,6 +160,79 @@ namespace ESMS
|
|||
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
|
||||
// separately.
|
||||
// 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)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
count++;
|
||||
|
||||
// All cells have a name record, even nameless exterior cells.
|
||||
|
|
|
@ -69,10 +69,10 @@ namespace ESMS
|
|||
// Lists that need special rules
|
||||
CellList cells;
|
||||
RecIDListT<GameSetting> gameSettings;
|
||||
//RecListT<Land> lands;
|
||||
//RecListT<LandTexture> landTexts;
|
||||
LandList lands;
|
||||
LTexList landTexts;
|
||||
ScriptListT<Script> scripts;
|
||||
IndexListT<MagicEffect> magicEffects;
|
||||
ScriptListT<Script> scripts;
|
||||
IndexListT<Skill> skills;
|
||||
//RecListT<PathGrid> pathgrids;
|
||||
|
||||
|
@ -115,12 +115,13 @@ namespace ESMS
|
|||
recLists[REC_GLOB] = &globals;
|
||||
recLists[REC_GMST] = &gameSettings;
|
||||
recLists[REC_INGR] = &ingreds;
|
||||
//recLists[REC_LAND] = &lands;
|
||||
recLists[REC_LAND] = &lands;
|
||||
recLists[REC_LEVC] = &creatureLists;
|
||||
recLists[REC_LEVI] = &itemLists;
|
||||
recLists[REC_LIGH] = &lights;
|
||||
recLists[REC_LOCK] = &lockpicks;
|
||||
//recLists[REC_LTEX] = &landTexts;
|
||||
recLists[REC_LTEX] = &landTexts;
|
||||
//recLists[REC_MGEF] = &magicEffects;
|
||||
recLists[REC_MISC] = &miscItems;
|
||||
recLists[REC_NPC_] = &npcs;
|
||||
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);
|
||||
}
|
||||
|
||||
// Pregenerate triangle indices
|
||||
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);
|
||||
// index stuff already ported
|
||||
}
|
||||
|
||||
void genLevel(int level, int X, int Y, ref GenLevelResult result,
|
||||
|
|
Loading…
Reference in a new issue