mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 07:23:51 +00:00
Merge remote branch 'upstream/master'
This commit is contained in:
commit
927879d23e
22 changed files with 482 additions and 40 deletions
|
@ -41,6 +41,14 @@ set(GAMEGUI
|
||||||
)
|
)
|
||||||
source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
|
source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
|
||||||
|
|
||||||
|
set(GAMEDIALOGUE_HEADER
|
||||||
|
mwdialogue/dialoguemanager.hpp
|
||||||
|
)
|
||||||
|
set(GAMEDIALOGUE
|
||||||
|
mwdialogue/dialoguemanager.cpp
|
||||||
|
)
|
||||||
|
source_group(apps\\openmw\\mwdialogue FILES ${GAMEDIALOGUE_HEADER} ${GAMEDIALOGUE})
|
||||||
|
|
||||||
set(GAMESCRIPT
|
set(GAMESCRIPT
|
||||||
mwscript/scriptmanager.cpp
|
mwscript/scriptmanager.cpp
|
||||||
mwscript/compilercontext.cpp
|
mwscript/compilercontext.cpp
|
||||||
|
@ -97,6 +105,7 @@ set(GAMEWORLD_HEADER
|
||||||
mwworld/action.hpp
|
mwworld/action.hpp
|
||||||
mwworld/nullaction.hpp
|
mwworld/nullaction.hpp
|
||||||
mwworld/actionteleport.hpp
|
mwworld/actionteleport.hpp
|
||||||
|
mwworld/containerstore.hpp
|
||||||
mwworld/actiontalk.hpp
|
mwworld/actiontalk.hpp
|
||||||
mwworld/actiontake.hpp
|
mwworld/actiontake.hpp
|
||||||
mwworld/containerstore.hpp
|
mwworld/containerstore.hpp
|
||||||
|
@ -164,11 +173,11 @@ set(GAMEMECHANICS_HEADER
|
||||||
source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER})
|
source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER})
|
||||||
|
|
||||||
set(OPENMW_CPP ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND} ${GAMEGUI} ${GAMEWORLD}
|
set(OPENMW_CPP ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND} ${GAMEGUI} ${GAMEWORLD}
|
||||||
${GAMECLASS} ${GAMEMECHANICS}
|
${GAMECLASS} ${GAMEMECHANICS} ${GAMEDIALOGUE}
|
||||||
)
|
)
|
||||||
set(OPENMW_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER}
|
set(OPENMW_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER}
|
||||||
${GAMESOUND_HEADER} ${GAMEGUI_HEADER} ${GAMEWORLD_HEADER} ${GAMECLASS_HEADER}
|
${GAMESOUND_HEADER} ${GAMEGUI_HEADER} ${GAMEWORLD_HEADER} ${GAMECLASS_HEADER}
|
||||||
${GAMEMECHANICS_HEADER}
|
${GAMEMECHANICS_HEADER} ${GAMEDIALOG_HEADERUE}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include "mwclass/classes.hpp"
|
#include "mwclass/classes.hpp"
|
||||||
|
|
||||||
|
#include "mwdialogue/dialoguemanager.hpp"
|
||||||
|
|
||||||
#include "mwmechanics/mechanicsmanager.hpp"
|
#include "mwmechanics/mechanicsmanager.hpp"
|
||||||
|
|
||||||
#include <OgreRoot.h>
|
#include <OgreRoot.h>
|
||||||
|
@ -104,6 +106,7 @@ OMW::Engine::Engine()
|
||||||
: mDebug (false)
|
: mDebug (false)
|
||||||
, mVerboseScripts (false)
|
, mVerboseScripts (false)
|
||||||
, mNewGame (false)
|
, mNewGame (false)
|
||||||
|
, mUseSound (true)
|
||||||
, mScriptManager (0)
|
, mScriptManager (0)
|
||||||
, mScriptContext (0)
|
, mScriptContext (0)
|
||||||
{
|
{
|
||||||
|
@ -117,6 +120,7 @@ OMW::Engine::~Engine()
|
||||||
delete mEnvironment.mSoundManager;
|
delete mEnvironment.mSoundManager;
|
||||||
delete mEnvironment.mGlobalScripts;
|
delete mEnvironment.mGlobalScripts;
|
||||||
delete mEnvironment.mMechanicsManager;
|
delete mEnvironment.mMechanicsManager;
|
||||||
|
delete mEnvironment.mDialogueManager;
|
||||||
delete mScriptManager;
|
delete mScriptManager;
|
||||||
delete mScriptContext;
|
delete mScriptContext;
|
||||||
}
|
}
|
||||||
|
@ -236,7 +240,8 @@ void OMW::Engine::go()
|
||||||
mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre.getRoot(),
|
mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre.getRoot(),
|
||||||
mOgre.getCamera(),
|
mOgre.getCamera(),
|
||||||
mEnvironment.mWorld->getStore(),
|
mEnvironment.mWorld->getStore(),
|
||||||
(mDataDir / "Sound").file_string());
|
(mDataDir / "Sound").file_string(),
|
||||||
|
mUseSound);
|
||||||
|
|
||||||
// Create script system
|
// Create script system
|
||||||
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
|
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
|
||||||
|
@ -253,6 +258,9 @@ void OMW::Engine::go()
|
||||||
mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (
|
mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (
|
||||||
mEnvironment.mWorld->getStore(), *mEnvironment.mWindowManager);
|
mEnvironment.mWorld->getStore(), *mEnvironment.mWindowManager);
|
||||||
|
|
||||||
|
// Create dialog system
|
||||||
|
mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment);
|
||||||
|
|
||||||
// load cell
|
// load cell
|
||||||
ESM::Position pos;
|
ESM::Position pos;
|
||||||
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
||||||
|
|
|
@ -59,6 +59,7 @@ namespace OMW
|
||||||
bool mDebug;
|
bool mDebug;
|
||||||
bool mVerboseScripts;
|
bool mVerboseScripts;
|
||||||
bool mNewGame;
|
bool mNewGame;
|
||||||
|
bool mUseSound;
|
||||||
|
|
||||||
MWWorld::Environment mEnvironment;
|
MWWorld::Environment mEnvironment;
|
||||||
MWScript::ScriptManager *mScriptManager;
|
MWScript::ScriptManager *mScriptManager;
|
||||||
|
@ -115,6 +116,9 @@ namespace OMW
|
||||||
/// Enable verbose script output
|
/// Enable verbose script output
|
||||||
void enableVerboseScripts();
|
void enableVerboseScripts();
|
||||||
|
|
||||||
|
/// Disable all sound
|
||||||
|
void disableSound() { mUseSound = false; }
|
||||||
|
|
||||||
/// Start as a new game.
|
/// Start as a new game.
|
||||||
void setNewGame();
|
void setNewGame();
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
("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" )
|
||||||
( "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" )
|
||||||
;
|
;
|
||||||
|
@ -65,6 +66,9 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
if (variables.count ("debug"))
|
if (variables.count ("debug"))
|
||||||
engine.enableDebugMode();
|
engine.enableDebugMode();
|
||||||
|
|
||||||
|
if (variables.count ("nosound"))
|
||||||
|
engine.disableSound();
|
||||||
|
|
||||||
if (variables.count ("script-verbose"))
|
if (variables.count ("script-verbose"))
|
||||||
engine.enableVerboseScripts();
|
engine.enableVerboseScripts();
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,14 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
|
std::string Creature::getId (const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::Creature, MWWorld::RefData> *ref =
|
||||||
|
ptr.get<ESM::Creature>();
|
||||||
|
|
||||||
|
return ref->base->mId;
|
||||||
|
}
|
||||||
|
|
||||||
void Creature::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
void Creature::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const
|
MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,9 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
virtual std::string getId (const MWWorld::Ptr& ptr) const;
|
||||||
|
///< Return ID of \a ptr
|
||||||
|
|
||||||
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const;
|
MWWorld::Environment& environment) const;
|
||||||
///< Add reference into a cell for rendering
|
///< Add reference into a cell for rendering
|
||||||
|
|
|
@ -16,6 +16,14 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
|
std::string Npc::getId (const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
|
||||||
|
ptr.get<ESM::NPC>();
|
||||||
|
|
||||||
|
return ref->base->mId;
|
||||||
|
}
|
||||||
|
|
||||||
void Npc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
void Npc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const
|
MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,9 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
virtual std::string getId (const MWWorld::Ptr& ptr) const;
|
||||||
|
///< Return ID of \a ptr
|
||||||
|
|
||||||
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const;
|
MWWorld::Environment& environment) const;
|
||||||
///< Add reference into a cell for rendering
|
///< Add reference into a cell for rendering
|
||||||
|
@ -26,14 +29,14 @@ namespace MWClass
|
||||||
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
|
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
|
||||||
///< Return creature stats
|
///< Return creature stats
|
||||||
|
|
||||||
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const;
|
|
||||||
///< Generate action for activation
|
|
||||||
|
|
||||||
virtual MWWorld::ContainerStore<MWWorld::RefData>& getContainerStore (
|
virtual MWWorld::ContainerStore<MWWorld::RefData>& getContainerStore (
|
||||||
const MWWorld::Ptr& ptr) const;
|
const MWWorld::Ptr& ptr) const;
|
||||||
///< Return container store
|
///< Return container store
|
||||||
|
|
||||||
|
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
||||||
|
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const;
|
||||||
|
///< Generate action for activation
|
||||||
|
|
||||||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||||
///< Return name of the script attached to ptr
|
///< Return name of the script attached to ptr
|
||||||
|
|
||||||
|
|
281
apps/openmw/mwdialogue/dialoguemanager.cpp
Normal file
281
apps/openmw/mwdialogue/dialoguemanager.cpp
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
|
||||||
|
#include "dialoguemanager.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include <components/esm/loaddial.hpp>
|
||||||
|
|
||||||
|
#include <components/esm_store/store.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/environment.hpp"
|
||||||
|
#include "../mwworld/world.hpp"
|
||||||
|
#include "../mwworld/refdata.hpp"
|
||||||
|
|
||||||
|
#include "../mwgui/window_manager.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string toLower (const std::string& name)
|
||||||
|
{
|
||||||
|
std::string lowerCase;
|
||||||
|
|
||||||
|
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
|
||||||
|
(int(*)(int)) std::tolower);
|
||||||
|
|
||||||
|
return lowerCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
bool selectCompare (char comp, T1 value1, T2 value2)
|
||||||
|
{
|
||||||
|
switch (comp)
|
||||||
|
{
|
||||||
|
case '0': return value1==value2;
|
||||||
|
case '1': return value1!=value2;
|
||||||
|
case '2': return value1>value2;
|
||||||
|
case '3': return value1>=value2;
|
||||||
|
case '4': return value1<value2;
|
||||||
|
case '5': return value1<=value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown compare type in dialogue info select");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool checkLocal (char comp, const std::string& name, T value, const MWWorld::Ptr& actor,
|
||||||
|
const ESMS::ESMStore& store)
|
||||||
|
{
|
||||||
|
std::string scriptName = MWWorld::Class::get (actor).getScript (actor);
|
||||||
|
|
||||||
|
if (scriptName.empty())
|
||||||
|
return false; // no script
|
||||||
|
|
||||||
|
const ESM::Script *script = store.scripts.find (scriptName);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i<static_cast<int> (script->varNames.size()); ++i)
|
||||||
|
if (script->varNames[i]==name)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i>=static_cast<int> (script->varNames.size()))
|
||||||
|
return false; // script does not have a variable of this name
|
||||||
|
|
||||||
|
const MWScript::Locals& locals = actor.getRefData().getLocals();
|
||||||
|
|
||||||
|
if (i<script->data.numShorts)
|
||||||
|
return selectCompare (comp, locals.mShorts[i], value);
|
||||||
|
else
|
||||||
|
i -= script->data.numShorts;
|
||||||
|
|
||||||
|
if (i<script->data.numLongs)
|
||||||
|
return selectCompare (comp, locals.mLongs[i], value);
|
||||||
|
else
|
||||||
|
i -= script->data.numShorts;
|
||||||
|
|
||||||
|
return selectCompare (comp, locals.mFloats.at (i), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool checkGlobal (char comp, const std::string& name, T value, MWWorld::World& world)
|
||||||
|
{
|
||||||
|
switch (world.getGlobalVariableType (name))
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
|
||||||
|
return selectCompare (comp, value, world.getGlobalVariable (name).mShort);
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
|
||||||
|
return selectCompare (comp, value, world.getGlobalVariable (name).mLong);
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
|
||||||
|
return selectCompare (comp, value, world.getGlobalVariable (name).mFloat);
|
||||||
|
|
||||||
|
case ' ':
|
||||||
|
|
||||||
|
world.getGlobalVariable (name); // trigger exception
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
throw std::runtime_error ("unsupported gobal variable type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
bool DialogueManager::isMatching (const MWWorld::Ptr& actor,
|
||||||
|
const ESM::DialInfo::SelectStruct& select) const
|
||||||
|
{
|
||||||
|
char type = select.selectRule[1];
|
||||||
|
|
||||||
|
if (type!='0')
|
||||||
|
{
|
||||||
|
char comp = select.selectRule[4];
|
||||||
|
std::string name = select.selectRule.substr (5);
|
||||||
|
|
||||||
|
// TODO types 4, 5, 6, 7, 8, 9, A, B, C
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case '1': // function
|
||||||
|
|
||||||
|
return false; // TODO implement functions
|
||||||
|
|
||||||
|
case '2': // global
|
||||||
|
|
||||||
|
if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
|
||||||
|
select.type==ESM::VT_Long)
|
||||||
|
{
|
||||||
|
if (!checkGlobal (comp, toLower (name), select.i, *mEnvironment.mWorld))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (select.type==ESM::VT_Float)
|
||||||
|
{
|
||||||
|
if (!checkGlobal (comp, toLower (name), select.f, *mEnvironment.mWorld))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error (
|
||||||
|
"unsupported variable type in dialogue info select");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case '3': // local
|
||||||
|
|
||||||
|
if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
|
||||||
|
select.type==ESM::VT_Long)
|
||||||
|
{
|
||||||
|
if (!checkLocal (comp, toLower (name), select.i, actor,
|
||||||
|
mEnvironment.mWorld->getStore()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (select.type==ESM::VT_Float)
|
||||||
|
{
|
||||||
|
if (!checkLocal (comp, toLower (name), select.f, actor,
|
||||||
|
mEnvironment.mWorld->getStore()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error (
|
||||||
|
"unsupported variable type in dialogue info select");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
std::cout << "unchecked select: " << type << " " << comp << " " << name << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DialogueManager::isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const
|
||||||
|
{
|
||||||
|
// actor id
|
||||||
|
if (!info.actor.empty())
|
||||||
|
if (toLower (info.actor)!=MWWorld::Class::get (actor).getId (actor))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!info.race.empty())
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (!cellRef)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toLower (info.race)!=toLower (cellRef->base->race))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.clas.empty())
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (!cellRef)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toLower (info.clas)!=toLower (cellRef->base->cls))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.npcFaction.empty())
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (!cellRef)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toLower (info.npcFaction)!=toLower (cellRef->base->faction))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check player faction
|
||||||
|
|
||||||
|
// check cell
|
||||||
|
if (!info.cell.empty())
|
||||||
|
if (mEnvironment.mWorld->getPlayerPos().getPlayer().getCell()->cell->name != info.cell)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// TODO check DATAstruct
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.selects.begin());
|
||||||
|
iter != info.selects.end(); ++iter)
|
||||||
|
if (!isMatching (actor, *iter))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "unchecked entries:" << std::endl
|
||||||
|
<< " player faction: " << info.pcFaction << std::endl
|
||||||
|
<< " DATAstruct" << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogueManager::DialogueManager (MWWorld::Environment& environment) : mEnvironment (environment) {}
|
||||||
|
|
||||||
|
void DialogueManager::startDialogue (const MWWorld::Ptr& actor)
|
||||||
|
{
|
||||||
|
std::cout << "talking with " << MWWorld::Class::get (actor).getName (actor) << std::endl;
|
||||||
|
|
||||||
|
const ESM::Dialogue *dialogue = mEnvironment.mWorld->getStore().dialogs.find ("hello");
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||||
|
iter!=dialogue->mInfo.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (isMatching (actor, *iter))
|
||||||
|
{
|
||||||
|
// start dialogue
|
||||||
|
std::cout << "found matching info record" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "response: " << iter->response << std::endl;
|
||||||
|
|
||||||
|
if (!iter->sound.empty())
|
||||||
|
{
|
||||||
|
// TODO play sound
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iter->resultScript.empty())
|
||||||
|
{
|
||||||
|
std::cout << "script: " << iter->resultScript << std::endl;
|
||||||
|
// TODO execute script
|
||||||
|
}
|
||||||
|
|
||||||
|
mEnvironment.mWindowManager->setMode (MWGui::GM_Dialogue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
apps/openmw/mwdialogue/dialoguemanager.hpp
Normal file
32
apps/openmw/mwdialogue/dialoguemanager.hpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef GAME_MMDIALOG_DIALOGUEMANAGER_H
|
||||||
|
#define GAME_MWDIALOG_DIALOGUEMANAGER_H
|
||||||
|
|
||||||
|
#include <components/esm/loadinfo.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class Environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
class DialogueManager
|
||||||
|
{
|
||||||
|
MWWorld::Environment& mEnvironment;
|
||||||
|
|
||||||
|
bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo::SelectStruct& select) const;
|
||||||
|
|
||||||
|
bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DialogueManager (MWWorld::Environment& environment);
|
||||||
|
|
||||||
|
void startDialogue (const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,16 +20,22 @@ using namespace std;
|
||||||
#ifdef OPENMW_USE_AUDIERE
|
#ifdef OPENMW_USE_AUDIERE
|
||||||
#include <mangle/sound/filters/openal_audiere.hpp>
|
#include <mangle/sound/filters/openal_audiere.hpp>
|
||||||
#define SOUND_FACTORY OpenAL_Audiere_Factory
|
#define SOUND_FACTORY OpenAL_Audiere_Factory
|
||||||
|
#define SOUND_OUT "OpenAL"
|
||||||
|
#define SOUND_IN "Audiere"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OPENMW_USE_FFMPEG
|
#ifdef OPENMW_USE_FFMPEG
|
||||||
#include <mangle/sound/filters/openal_ffmpeg.hpp>
|
#include <mangle/sound/filters/openal_ffmpeg.hpp>
|
||||||
#define SOUND_FACTORY OpenAL_FFMpeg_Factory
|
#define SOUND_FACTORY OpenAL_FFMpeg_Factory
|
||||||
|
#define SOUND_OUT "OpenAL"
|
||||||
|
#define SOUND_IN "FFmpeg"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OPENMW_USE_MPG123
|
#ifdef OPENMW_USE_MPG123
|
||||||
#include <mangle/sound/filters/openal_sndfile_mpg123.hpp>
|
#include <mangle/sound/filters/openal_sndfile_mpg123.hpp>
|
||||||
#define SOUND_FACTORY OpenAL_SndFile_Mpg123_Factory
|
#define SOUND_FACTORY OpenAL_SndFile_Mpg123_Factory
|
||||||
|
#define SOUND_OUT "OpenAL"
|
||||||
|
#define SOUND_IN "mpg123,sndfile"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace Mangle::Sound;
|
using namespace Mangle::Sound;
|
||||||
|
@ -77,6 +83,9 @@ namespace MWSound
|
||||||
, cameraTracker(mgr)
|
, cameraTracker(mgr)
|
||||||
, store(str)
|
, store(str)
|
||||||
{
|
{
|
||||||
|
cout << "Sound output: " << SOUND_OUT << endl;
|
||||||
|
cout << "Sound decoder: " << SOUND_IN << endl;
|
||||||
|
|
||||||
// Attach the camera to the camera tracker
|
// Attach the camera to the camera tracker
|
||||||
cameraTracker.followCamera(camera);
|
cameraTracker.followCamera(camera);
|
||||||
|
|
||||||
|
@ -150,29 +159,37 @@ namespace MWSound
|
||||||
|
|
||||||
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
|
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
|
||||||
const ESMS::ESMStore &store,
|
const ESMS::ESMStore &store,
|
||||||
const std::string &soundDir)
|
const std::string &soundDir,
|
||||||
|
bool useSound)
|
||||||
|
: mData(NULL)
|
||||||
{
|
{
|
||||||
|
if(useSound)
|
||||||
mData = new SoundImpl(root, camera, store, soundDir);
|
mData = new SoundImpl(root, camera, store, soundDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundManager::~SoundManager()
|
SoundManager::~SoundManager()
|
||||||
{
|
{
|
||||||
|
if(mData)
|
||||||
delete mData;
|
delete mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename)
|
void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename)
|
||||||
{
|
{
|
||||||
// The range values are not tested
|
// The range values are not tested
|
||||||
|
if(!mData) return;
|
||||||
mData->add(filename, ptr, "_say_sound", 1, 1, 100, 10000, false);
|
mData->add(filename, ptr, "_say_sound", 1, 1, 100, 10000, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SoundManager::sayDone (MWWorld::Ptr ptr) const
|
bool SoundManager::sayDone (MWWorld::Ptr ptr) const
|
||||||
{
|
{
|
||||||
|
if(!mData) return false;
|
||||||
return !mData->isPlaying(ptr, "_say_sound");
|
return !mData->isPlaying(ptr, "_say_sound");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::streamMusic (const std::string& filename)
|
void SoundManager::streamMusic (const std::string& filename)
|
||||||
{
|
{
|
||||||
|
if(!mData) return;
|
||||||
|
|
||||||
// Play the sound and tell it to stream, if possible. TODO:
|
// Play the sound and tell it to stream, if possible. TODO:
|
||||||
// Store the reference, the jukebox will need to check status,
|
// Store the reference, the jukebox will need to check status,
|
||||||
// control volume etc.
|
// control volume etc.
|
||||||
|
@ -184,6 +201,8 @@ namespace MWSound
|
||||||
|
|
||||||
void SoundManager::playSound (const std::string& soundId, float volume, float pitch)
|
void SoundManager::playSound (const std::string& soundId, float volume, float pitch)
|
||||||
{
|
{
|
||||||
|
if(!mData) return;
|
||||||
|
|
||||||
// Play and forget
|
// Play and forget
|
||||||
float min, max;
|
float min, max;
|
||||||
const std::string &file = mData->lookup(soundId, volume, min, max);
|
const std::string &file = mData->lookup(soundId, volume, min, max);
|
||||||
|
@ -199,6 +218,8 @@ namespace MWSound
|
||||||
void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId,
|
void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId,
|
||||||
float volume, float pitch, bool loop)
|
float volume, float pitch, bool loop)
|
||||||
{
|
{
|
||||||
|
if(!mData) return;
|
||||||
|
|
||||||
// Look up the sound in the ESM data
|
// Look up the sound in the ESM data
|
||||||
float min, max;
|
float min, max;
|
||||||
const std::string &file = mData->lookup(soundId, volume, min, max);
|
const std::string &file = mData->lookup(soundId, volume, min, max);
|
||||||
|
@ -208,21 +229,28 @@ namespace MWSound
|
||||||
|
|
||||||
void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId)
|
void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId)
|
||||||
{
|
{
|
||||||
|
if(!mData) return;
|
||||||
mData->remove(ptr, soundId);
|
mData->remove(ptr, soundId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell)
|
void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell)
|
||||||
{
|
{
|
||||||
|
if(!mData) return;
|
||||||
mData->removeCell(cell);
|
mData->removeCell(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const
|
bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const
|
||||||
{
|
{
|
||||||
|
// Mark all sounds as playing, otherwise the scripts will just
|
||||||
|
// keep trying to play them every frame.
|
||||||
|
if(!mData) return true;
|
||||||
|
|
||||||
return mData->isPlaying(ptr, soundId);
|
return mData->isPlaying(ptr, soundId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::updateObject(MWWorld::Ptr ptr)
|
void SoundManager::updateObject(MWWorld::Ptr ptr)
|
||||||
{
|
{
|
||||||
|
if(!mData) return;
|
||||||
mData->updatePositions(ptr);
|
mData->updatePositions(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace MWSound
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
|
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
|
||||||
const std::string &soundDir);
|
const std::string &soundDir, bool useSound);
|
||||||
~SoundManager();
|
~SoundManager();
|
||||||
|
|
||||||
void say (MWWorld::Ptr reference, const std::string& filename);
|
void say (MWWorld::Ptr reference, const std::string& filename);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "environment.hpp"
|
#include "environment.hpp"
|
||||||
|
|
||||||
#include "../mwgui/window_manager.hpp"
|
#include "../mwdialogue/dialoguemanager.hpp"
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,6 @@ namespace MWWorld
|
||||||
|
|
||||||
void ActionTalk::execute (Environment& environment)
|
void ActionTalk::execute (Environment& environment)
|
||||||
{
|
{
|
||||||
environment.mWindowManager->setMode (MWGui::GM_Dialogue);
|
environment.mDialogueManager->startDialogue (mActor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace MWWorld
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ActionTalk (const Ptr& actor);
|
ActionTalk (const Ptr& actor);
|
||||||
|
///< \param actor The actor the player is talking to
|
||||||
|
|
||||||
virtual void execute (Environment& environment);
|
virtual void execute (Environment& environment);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,11 @@ namespace MWWorld
|
||||||
|
|
||||||
Class::~Class() {}
|
Class::~Class() {}
|
||||||
|
|
||||||
|
std::string Class::getId (const Ptr& ptr) const
|
||||||
|
{
|
||||||
|
throw std::runtime_error ("class does not support ID retrieval");
|
||||||
|
}
|
||||||
|
|
||||||
void Class::insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
void Class::insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const
|
MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,10 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual ~Class();
|
virtual ~Class();
|
||||||
|
|
||||||
|
virtual std::string getId (const Ptr& ptr) const;
|
||||||
|
///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval
|
||||||
|
/// (default implementation: throw an exception)
|
||||||
|
|
||||||
virtual void insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
virtual void insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const;
|
MWWorld::Environment& environment) const;
|
||||||
///< Add reference into a cell for rendering (default implementation: don't render anything).
|
///< Add reference into a cell for rendering (default implementation: don't render anything).
|
||||||
|
|
|
@ -21,6 +21,11 @@ namespace MWMechanics
|
||||||
class MechanicsManager;
|
class MechanicsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
class DialogueManager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class World;
|
class World;
|
||||||
|
@ -31,7 +36,7 @@ namespace MWWorld
|
||||||
public:
|
public:
|
||||||
Environment()
|
Environment()
|
||||||
: mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0),
|
: mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0),
|
||||||
mMechanicsManager (0), mFrameDuration (0)
|
mMechanicsManager (0), mDialogueManager (0), mFrameDuration (0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
World *mWorld;
|
World *mWorld;
|
||||||
|
@ -39,9 +44,9 @@ namespace MWWorld
|
||||||
MWScript::GlobalScripts *mGlobalScripts;
|
MWScript::GlobalScripts *mGlobalScripts;
|
||||||
MWGui::WindowManager *mWindowManager;
|
MWGui::WindowManager *mWindowManager;
|
||||||
MWMechanics::MechanicsManager *mMechanicsManager;
|
MWMechanics::MechanicsManager *mMechanicsManager;
|
||||||
|
MWDialogue::DialogueManager *mDialogueManager;
|
||||||
float mFrameDuration;
|
float mFrameDuration;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#ifndef _ESM_DIAL_H
|
#ifndef _ESM_DIAL_H
|
||||||
#define _ESM_DIAL_H
|
#define _ESM_DIAL_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
#include "loadinfo.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM {
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ struct Dialogue
|
||||||
};
|
};
|
||||||
|
|
||||||
char type;
|
char type;
|
||||||
|
std::vector<DialInfo> mInfo;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
#include "loadcont.hpp"
|
#include "loadcont.hpp"
|
||||||
#include "loadcrea.hpp"
|
#include "loadcrea.hpp"
|
||||||
#include "loadcrec.hpp"
|
#include "loadcrec.hpp"
|
||||||
|
#include "loadinfo.hpp"
|
||||||
#include "loaddial.hpp"
|
#include "loaddial.hpp"
|
||||||
#include "loaddoor.hpp"
|
#include "loaddoor.hpp"
|
||||||
#include "loadench.hpp"
|
#include "loadench.hpp"
|
||||||
#include "loadfact.hpp"
|
#include "loadfact.hpp"
|
||||||
#include "loadglob.hpp"
|
#include "loadglob.hpp"
|
||||||
#include "loadgmst.hpp"
|
#include "loadgmst.hpp"
|
||||||
#include "loadinfo.hpp"
|
|
||||||
#include "loadingr.hpp"
|
#include "loadingr.hpp"
|
||||||
#include "loadland.hpp"
|
#include "loadland.hpp"
|
||||||
#include "loadlevlist.hpp"
|
#include "loadlevlist.hpp"
|
||||||
|
|
|
@ -20,6 +20,8 @@ void ESMStore::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
set<string> missing;
|
set<string> missing;
|
||||||
|
|
||||||
|
ESM::Dialogue *dialogue = 0;
|
||||||
|
|
||||||
// Loop through all records
|
// Loop through all records
|
||||||
while(esm.hasMoreRecs())
|
while(esm.hasMoreRecs())
|
||||||
{
|
{
|
||||||
|
@ -30,21 +32,57 @@ void ESMStore::load(ESMReader &esm)
|
||||||
RecListList::iterator it = recLists.find(n.val);
|
RecListList::iterator it = recLists.find(n.val);
|
||||||
|
|
||||||
if(it == recLists.end())
|
if(it == recLists.end())
|
||||||
|
{
|
||||||
|
if (n.val==ESM::REC_INFO)
|
||||||
|
{
|
||||||
|
if (dialogue)
|
||||||
|
{
|
||||||
|
ESM::DialInfo info;
|
||||||
|
info.load (esm);
|
||||||
|
|
||||||
|
dialogue->mInfo.push_back (info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "error: info record without dialog" << std::endl;
|
||||||
|
esm.skipRecord();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Not found (this would be an error later)
|
// Not found (this would be an error later)
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
missing.insert(n.toString());
|
missing.insert(n.toString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Load it
|
// Load it
|
||||||
std::string id = esm.getHNOString("NAME");
|
std::string id = esm.getHNOString("NAME");
|
||||||
it->second->load(esm, id);
|
it->second->load(esm, id);
|
||||||
|
|
||||||
|
if (n.val==ESM::REC_DIAL)
|
||||||
|
{
|
||||||
|
RecListT<Dialogue>& recList = static_cast<RecListT<Dialogue>& > (*it->second);
|
||||||
|
|
||||||
|
id = recList.toLower (id);
|
||||||
|
|
||||||
|
RecListT<Dialogue>::MapType::iterator iter = recList.list.find (id);
|
||||||
|
|
||||||
|
assert (iter!=recList.list.end());
|
||||||
|
|
||||||
|
dialogue = &iter->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dialogue = 0;
|
||||||
|
|
||||||
// Insert the reference into the global lookup
|
// Insert the reference into the global lookup
|
||||||
if(!id.empty())
|
if(!id.empty())
|
||||||
all[id] = n.val;
|
all[id] = n.val;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* This information isn't needed on screen. But keep the code around
|
/* This information isn't needed on screen. But keep the code around
|
||||||
for debugging purposes later.
|
for debugging purposes later.
|
||||||
|
|
|
@ -69,7 +69,6 @@ namespace ESMS
|
||||||
// Lists that need special rules
|
// Lists that need special rules
|
||||||
CellList cells;
|
CellList cells;
|
||||||
RecIDListT<GameSetting> gameSettings;
|
RecIDListT<GameSetting> gameSettings;
|
||||||
//RecListT<DialInfo> dialInfos;
|
|
||||||
//RecListT<Land> lands;
|
//RecListT<Land> lands;
|
||||||
//RecListT<LandTexture> landTexts;
|
//RecListT<LandTexture> landTexts;
|
||||||
//RecListT<MagicEffect> magicEffects;
|
//RecListT<MagicEffect> magicEffects;
|
||||||
|
@ -92,7 +91,6 @@ namespace ESMS
|
||||||
|
|
||||||
ESMStore()
|
ESMStore()
|
||||||
{
|
{
|
||||||
recLists[REC_ACTI] = &activators;
|
|
||||||
recLists[REC_ACTI] = &activators;
|
recLists[REC_ACTI] = &activators;
|
||||||
recLists[REC_ALCH] = &potions;
|
recLists[REC_ALCH] = &potions;
|
||||||
recLists[REC_APPA] = &appas;
|
recLists[REC_APPA] = &appas;
|
||||||
|
@ -113,7 +111,6 @@ namespace ESMS
|
||||||
recLists[REC_FACT] = &factions;
|
recLists[REC_FACT] = &factions;
|
||||||
recLists[REC_GLOB] = &globals;
|
recLists[REC_GLOB] = &globals;
|
||||||
recLists[REC_GMST] = &gameSettings;
|
recLists[REC_GMST] = &gameSettings;
|
||||||
//recLists[REC_INFO] = &dialInfos;
|
|
||||||
recLists[REC_INGR] = &ingreds;
|
recLists[REC_INGR] = &ingreds;
|
||||||
//recLists[REC_LAND] = &lands;
|
//recLists[REC_LAND] = &lands;
|
||||||
recLists[REC_LEVC] = &creatureLists;
|
recLists[REC_LEVC] = &creatureLists;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit cd4ed4e6bfb23d736c4b1f30d6096ec164ba937b
|
Subproject commit 200fab03efaa2cbe1b8fce4a742b0195f8912295
|
Loading…
Reference in a new issue