mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:23:52 +00:00
Merge branch 'dialogue'
This commit is contained in:
commit
cbb0fd5792
19 changed files with 1209 additions and 729 deletions
|
@ -34,7 +34,7 @@ add_openmw_dir (mwgui
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwdialogue
|
add_openmw_dir (mwdialogue
|
||||||
dialoguemanagerimp journalimp journalentry quest topic
|
dialoguemanagerimp journalimp journalentry quest topic filter selectwrapper
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwscript
|
add_openmw_dir (mwscript
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "mwscript/scriptmanagerimp.hpp"
|
#include "mwscript/scriptmanagerimp.hpp"
|
||||||
#include "mwscript/extensions.hpp"
|
#include "mwscript/extensions.hpp"
|
||||||
|
#include "mwscript/interpretercontext.hpp"
|
||||||
|
|
||||||
#include "mwsound/soundmanagerimp.hpp"
|
#include "mwsound/soundmanagerimp.hpp"
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void goodbye() = 0;
|
virtual void goodbye() = 0;
|
||||||
|
|
||||||
///get the faction of the actor you are talking with
|
virtual MWWorld::Ptr getActor() const = 0;
|
||||||
virtual std::string getFaction() const = 0;
|
///< Return the actor the player is currently talking to.
|
||||||
|
|
||||||
//calbacks for the GUI
|
//calbacks for the GUI
|
||||||
virtual void keywordSelected (const std::string& keyword) = 0;
|
virtual void keywordSelected (const std::string& keyword) = 0;
|
||||||
|
|
|
@ -62,10 +62,10 @@ namespace MWClass
|
||||||
|
|
||||||
data->mCreatureStats.setLevel(ref->mBase->mData.mLevel);
|
data->mCreatureStats.setLevel(ref->mBase->mData.mLevel);
|
||||||
|
|
||||||
data->mCreatureStats.setHello(ref->mBase->mAiData.mHello);
|
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
|
||||||
data->mCreatureStats.setFight(ref->mBase->mAiData.mFight);
|
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
|
||||||
data->mCreatureStats.setFlee(ref->mBase->mAiData.mFlee);
|
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
|
||||||
data->mCreatureStats.setAlarm(ref->mBase->mAiData.mAlarm);
|
data->mCreatureStats.setAiSetting (3, ref->mBase->mAiData.mAlarm);
|
||||||
|
|
||||||
// spells
|
// spells
|
||||||
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
|
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
|
||||||
|
|
|
@ -110,10 +110,10 @@ namespace MWClass
|
||||||
data->mCreatureStats.setLevel (1);
|
data->mCreatureStats.setLevel (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->mCreatureStats.setHello(ref->mBase->mAiData.mHello);
|
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
|
||||||
data->mCreatureStats.setFight(ref->mBase->mAiData.mFight);
|
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
|
||||||
data->mCreatureStats.setFlee(ref->mBase->mAiData.mFlee);
|
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
|
||||||
data->mCreatureStats.setAlarm(ref->mBase->mAiData.mAlarm);
|
data->mCreatureStats.setAiSetting (3, ref->mBase->mAiData.mAlarm);
|
||||||
|
|
||||||
// spells
|
// spells
|
||||||
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
|
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
|
||||||
|
|
|
@ -6,23 +6,7 @@
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
#include <components/esm/loaddial.hpp>
|
#include <components/esm/loaddial.hpp>
|
||||||
|
#include <components/esm/loadinfo.hpp>
|
||||||
#include "../mwbase/environment.hpp"
|
|
||||||
#include "../mwbase/world.hpp"
|
|
||||||
#include "../mwbase/scriptmanager.hpp"
|
|
||||||
#include "../mwbase/journal.hpp"
|
|
||||||
#include "../mwbase/windowmanager.hpp"
|
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
|
||||||
#include "../mwworld/refdata.hpp"
|
|
||||||
#include "../mwworld/player.hpp"
|
|
||||||
#include "../mwworld/containerstore.hpp"
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
|
||||||
|
|
||||||
#include "../mwgui/dialogue.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <components/compiler/exception.hpp>
|
#include <components/compiler/exception.hpp>
|
||||||
#include <components/compiler/errorhandler.hpp>
|
#include <components/compiler/errorhandler.hpp>
|
||||||
|
@ -30,15 +14,31 @@
|
||||||
#include <components/compiler/locals.hpp>
|
#include <components/compiler/locals.hpp>
|
||||||
#include <components/compiler/output.hpp>
|
#include <components/compiler/output.hpp>
|
||||||
#include <components/compiler/scriptparser.hpp>
|
#include <components/compiler/scriptparser.hpp>
|
||||||
|
|
||||||
#include <components/interpreter/interpreter.hpp>
|
#include <components/interpreter/interpreter.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/scriptmanager.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
|
#include "../mwgui/dialogue.hpp"
|
||||||
|
|
||||||
#include "../mwscript/compilercontext.hpp"
|
#include "../mwscript/compilercontext.hpp"
|
||||||
#include "../mwscript/interpretercontext.hpp"
|
#include "../mwscript/interpretercontext.hpp"
|
||||||
#include "../mwscript/extensions.hpp"
|
#include "../mwscript/extensions.hpp"
|
||||||
|
|
||||||
#include "../mwclass/npc.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/npcstats.hpp"
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
|
||||||
|
#include "filter.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::string toLower (const std::string& name)
|
std::string toLower (const std::string& name)
|
||||||
|
@ -66,87 +66,6 @@ namespace
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 MWWorld::ESMStore& store)
|
|
||||||
{
|
|
||||||
std::string scriptName = MWWorld::Class::get (actor).getScript (actor);
|
|
||||||
|
|
||||||
if (scriptName.empty())
|
|
||||||
return false; // no script
|
|
||||||
|
|
||||||
const ESM::Script *script =
|
|
||||||
store.get<ESM::Script>().find (scriptName);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (; i<static_cast<int> (script->mVarNames.size()); ++i)
|
|
||||||
if (script->mVarNames[i]==name)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (i>=static_cast<int> (script->mVarNames.size()))
|
|
||||||
return false; // script does not have a variable of this name
|
|
||||||
|
|
||||||
const MWScript::Locals& locals = actor.getRefData().getLocals();
|
|
||||||
|
|
||||||
if (i<script->mData.mNumShorts)
|
|
||||||
return selectCompare (comp, locals.mShorts[i], value);
|
|
||||||
else
|
|
||||||
i -= script->mData.mNumShorts;
|
|
||||||
|
|
||||||
if (i<script->mData.mNumLongs)
|
|
||||||
return selectCompare (comp, locals.mLongs[i], value);
|
|
||||||
else
|
|
||||||
i -= script->mData.mNumShorts;
|
|
||||||
|
|
||||||
return selectCompare (comp, locals.mFloats.at (i), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
bool checkGlobal (char comp, const std::string& name, T value)
|
|
||||||
{
|
|
||||||
switch (MWBase::Environment::get().getWorld()->getGlobalVariableType (name))
|
|
||||||
{
|
|
||||||
case 's':
|
|
||||||
return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mShort, value);
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
|
|
||||||
return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mLong, value);
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
|
|
||||||
return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat, value);
|
|
||||||
|
|
||||||
case ' ':
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWorld()->getGlobalVariable (name); // trigger exception
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
throw std::runtime_error ("unsupported gobal variable type");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//helper function
|
//helper function
|
||||||
std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
|
std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
|
||||||
{
|
{
|
||||||
|
@ -156,432 +75,6 @@ namespace
|
||||||
|
|
||||||
namespace MWDialogue
|
namespace MWDialogue
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
bool DialogueManager::functionFilter(const MWWorld::Ptr& actor, const ESM::DialInfo& info,bool choice)
|
|
||||||
{
|
|
||||||
bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name());
|
|
||||||
|
|
||||||
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.mSelects.begin());
|
|
||||||
iter != info.mSelects.end(); ++iter)
|
|
||||||
{
|
|
||||||
ESM::DialInfo::SelectStruct select = *iter;
|
|
||||||
char type = select.mSelectRule[1];
|
|
||||||
if(type == '1')
|
|
||||||
{
|
|
||||||
char comp = select.mSelectRule[4];
|
|
||||||
std::string name = select.mSelectRule.substr (5);
|
|
||||||
std::string function = select.mSelectRule.substr(2,2);
|
|
||||||
|
|
||||||
int ifunction;
|
|
||||||
std::istringstream iss(function);
|
|
||||||
iss >> ifunction;
|
|
||||||
switch(ifunction)
|
|
||||||
{
|
|
||||||
case 39://PC Expelled
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 40://PC Common Disease
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 41://PC Blight Disease
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 43://PC Crime level
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 46://Same faction
|
|
||||||
{
|
|
||||||
if (isCreature)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MWMechanics::NpcStats PCstats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
|
||||||
MWMechanics::NpcStats NPCstats = MWWorld::Class::get(actor).getNpcStats(actor);
|
|
||||||
int sameFaction = 0;
|
|
||||||
if(!NPCstats.getFactionRanks().empty())
|
|
||||||
{
|
|
||||||
std::string NPCFaction = NPCstats.getFactionRanks().begin()->first;
|
|
||||||
if(PCstats.getFactionRanks().find(toLower(NPCFaction)) != PCstats.getFactionRanks().end()) sameFaction = 1;
|
|
||||||
}
|
|
||||||
if(!selectCompare<int,int>(comp,sameFaction,select.mI)) return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 48://Detected
|
|
||||||
if(!selectCompare<int,int>(comp,1,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 49://Alarmed
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 50://choice
|
|
||||||
if(choice)
|
|
||||||
{
|
|
||||||
if(!selectCompare<int,int>(comp,mChoice,select.mI)) return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 60://PC Vampire
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 61://Level
|
|
||||||
if(!selectCompare<int,int>(comp,1,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 62://Attacked
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 63://Talked to PC
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 64://PC Health
|
|
||||||
if(!selectCompare<int,int>(comp,50,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 65://Creature target
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 66://Friend hit
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 67://Fight
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 68://Hello????
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 69://Alarm
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 70://Flee
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 71://Should Attack
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DialogueManager::isMatching (const MWWorld::Ptr& actor,
|
|
||||||
const ESM::DialInfo::SelectStruct& select) const
|
|
||||||
{
|
|
||||||
bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name());
|
|
||||||
|
|
||||||
char type = select.mSelectRule[1];
|
|
||||||
|
|
||||||
if (type!='0')
|
|
||||||
{
|
|
||||||
char comp = select.mSelectRule[4];
|
|
||||||
std::string name = select.mSelectRule.substr (5);
|
|
||||||
std::string function = select.mSelectRule.substr(1,2);
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case '1': // function
|
|
||||||
|
|
||||||
return true; // Done elsewhere.
|
|
||||||
|
|
||||||
case '2': // global
|
|
||||||
|
|
||||||
if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int ||
|
|
||||||
select.mType==ESM::VT_Long)
|
|
||||||
{
|
|
||||||
if (!checkGlobal (comp, toLower (name), select.mI))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (select.mType==ESM::VT_Float)
|
|
||||||
{
|
|
||||||
if (!checkGlobal (comp, toLower (name), select.mF))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '3': // local
|
|
||||||
|
|
||||||
if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int ||
|
|
||||||
select.mType==ESM::VT_Long)
|
|
||||||
{
|
|
||||||
if (!checkLocal (comp, toLower (name), select.mI, actor,
|
|
||||||
MWBase::Environment::get().getWorld()->getStore()))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (select.mType==ESM::VT_Float)
|
|
||||||
{
|
|
||||||
if (!checkLocal (comp, toLower (name), select.mF, actor,
|
|
||||||
MWBase::Environment::get().getWorld()->getStore()))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '4'://journal
|
|
||||||
if(select.mType==ESM::VT_Int)
|
|
||||||
{
|
|
||||||
if(!selectCompare<int,int>(comp,MWBase::Environment::get().getJournal()->getJournalIndex(toLower(name)),select.mI)) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '5'://item
|
|
||||||
{
|
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
|
||||||
MWWorld::ContainerStore& store = MWWorld::Class::get (player).getContainerStore (player);
|
|
||||||
|
|
||||||
int sum = 0;
|
|
||||||
|
|
||||||
for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)
|
|
||||||
if (toLower(iter->getCellRef().mRefID) == toLower(name))
|
|
||||||
sum += iter->getRefData().getCount();
|
|
||||||
if(!selectCompare<int,int>(comp,sum,select.mI)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
|
|
||||||
case '6'://dead
|
|
||||||
if(!selectCompare<int,int>(comp,0,select.mI)) return false;
|
|
||||||
|
|
||||||
case '7':// not ID
|
|
||||||
if(select.mType==ESM::VT_String ||select.mType==ESM::VT_Int)//bug in morrowind here? it's not a short, it's a string
|
|
||||||
{
|
|
||||||
int isID = int(toLower(name)==toLower(MWWorld::Class::get (actor).getId (actor)));
|
|
||||||
if (selectCompare<int,int>(comp,!isID,select.mI)) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '8':// not faction
|
|
||||||
if (isCreature)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(select.mType==ESM::VT_Int)
|
|
||||||
{
|
|
||||||
MWWorld::LiveCellRef<ESM::NPC>* npc = actor.get<ESM::NPC>();
|
|
||||||
int isFaction = int(toLower(npc->mBase->mFaction) == toLower(name));
|
|
||||||
if(selectCompare<int,int>(comp,!isFaction,select.mI))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '9':// not class
|
|
||||||
if (isCreature)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(select.mType==ESM::VT_Int)
|
|
||||||
{
|
|
||||||
MWWorld::LiveCellRef<ESM::NPC>* npc = actor.get<ESM::NPC>();
|
|
||||||
int isClass = int(toLower(npc->mBase->mClass) == toLower(name));
|
|
||||||
if(selectCompare<int,int>(comp,!isClass,select.mI))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case 'A'://not Race
|
|
||||||
if (isCreature)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(select.mType==ESM::VT_Int)
|
|
||||||
{
|
|
||||||
MWWorld::LiveCellRef<ESM::NPC>* npc = actor.get<ESM::NPC>();
|
|
||||||
int isRace = int(toLower(npc->mBase->mRace) == toLower(name));
|
|
||||||
if(selectCompare<int,int>(comp,!isRace,select.mI))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case 'B'://not Cell
|
|
||||||
if(select.mType==ESM::VT_Int)
|
|
||||||
{
|
|
||||||
int isCell = int(toLower(actor.getCell()->mCell->mName) == toLower(name));
|
|
||||||
if(selectCompare<int,int>(comp,!isCell,select.mI))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error (
|
|
||||||
"unsupported variable type in dialogue info select");
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case 'C'://not local
|
|
||||||
if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int ||
|
|
||||||
select.mType==ESM::VT_Long)
|
|
||||||
{
|
|
||||||
if (checkLocal (comp, toLower (name), select.mI, actor,
|
|
||||||
MWBase::Environment::get().getWorld()->getStore()))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (select.mType==ESM::VT_Float)
|
|
||||||
{
|
|
||||||
if (checkLocal (comp, toLower (name), select.mF, actor,
|
|
||||||
MWBase::Environment::get().getWorld()->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
|
|
||||||
{
|
|
||||||
bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name());
|
|
||||||
|
|
||||||
// actor id
|
|
||||||
if (!info.mActor.empty())
|
|
||||||
if (toLower (info.mActor)!=MWWorld::Class::get (actor).getId (actor))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//NPC race
|
|
||||||
if (!info.mRace.empty())
|
|
||||||
{
|
|
||||||
if (isCreature)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MWWorld::LiveCellRef<ESM::NPC> *cellRef = actor.get<ESM::NPC>();
|
|
||||||
|
|
||||||
if (!cellRef)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (toLower (info.mRace)!=toLower (cellRef->mBase->mRace))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//NPC class
|
|
||||||
if (!info.mClass.empty())
|
|
||||||
{
|
|
||||||
if (isCreature)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MWWorld::LiveCellRef<ESM::NPC> *cellRef = actor.get<ESM::NPC>();
|
|
||||||
|
|
||||||
if (!cellRef)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (toLower (info.mClass)!=toLower (cellRef->mBase->mClass))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//NPC faction
|
|
||||||
if (!info.mNpcFaction.empty())
|
|
||||||
{
|
|
||||||
if (isCreature)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//MWWorld::Class npcClass = MWWorld::Class::get(actor);
|
|
||||||
MWMechanics::NpcStats stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
|
||||||
std::map<std::string,int>::iterator it = stats.getFactionRanks().find(toLower(info.mNpcFaction));
|
|
||||||
if(it!=stats.getFactionRanks().end())
|
|
||||||
{
|
|
||||||
//check rank
|
|
||||||
if(it->second < (int)info.mData.mRank) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//not in the faction
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO check player faction
|
|
||||||
if(!info.mPcFaction.empty())
|
|
||||||
{
|
|
||||||
MWMechanics::NpcStats stats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
|
||||||
std::map<std::string,int>::iterator it = stats.getFactionRanks().find(toLower(info.mPcFaction));
|
|
||||||
if(it!=stats.getFactionRanks().end())
|
|
||||||
{
|
|
||||||
//check rank
|
|
||||||
if(it->second < (int)info.mData.mPCrank) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//not in the faction
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//check gender
|
|
||||||
if (!isCreature)
|
|
||||||
{
|
|
||||||
MWWorld::LiveCellRef<ESM::NPC>* npc = actor.get<ESM::NPC>();
|
|
||||||
if(npc->mBase->mFlags & npc->mBase->Female)
|
|
||||||
{
|
|
||||||
if(static_cast<int> (info.mData.mGender)==0) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(static_cast<int> (info.mData.mGender)==1) return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check cell
|
|
||||||
if (!info.mCell.empty())
|
|
||||||
if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mName != info.mCell)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// TODO check DATAstruct
|
|
||||||
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.mSelects.begin());
|
|
||||||
iter != info.mSelects.end(); ++iter)
|
|
||||||
if (!isMatching (actor, *iter))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogueManager::DialogueManager (const Compiler::Extensions& extensions) :
|
DialogueManager::DialogueManager (const Compiler::Extensions& extensions) :
|
||||||
mCompilerContext (MWScript::CompilerContext::Type_Dialgoue),
|
mCompilerContext (MWScript::CompilerContext::Type_Dialgoue),
|
||||||
mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream)
|
mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream)
|
||||||
|
@ -609,7 +102,7 @@ namespace MWDialogue
|
||||||
mKnownTopics[toLower(topic)] = true;
|
mKnownTopics[toLower(topic)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueManager::parseText (std::string text)
|
void DialogueManager::parseText (const std::string& text)
|
||||||
{
|
{
|
||||||
std::list<std::string>::iterator it;
|
std::list<std::string>::iterator it;
|
||||||
for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it)
|
for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it)
|
||||||
|
@ -636,6 +129,10 @@ namespace MWDialogue
|
||||||
mIsInChoice = false;
|
mIsInChoice = false;
|
||||||
|
|
||||||
mActor = actor;
|
mActor = actor;
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor);
|
||||||
|
mTalkedTo = creatureStats.hasTalkedToPlayer();
|
||||||
|
creatureStats.talkedToPlayer();
|
||||||
|
|
||||||
mActorKnownTopics.clear();
|
mActorKnownTopics.clear();
|
||||||
|
|
||||||
|
@ -648,35 +145,28 @@ namespace MWDialogue
|
||||||
updateTopics();
|
updateTopics();
|
||||||
|
|
||||||
//greeting
|
//greeting
|
||||||
bool greetingFound = false;
|
|
||||||
const MWWorld::Store<ESM::Dialogue> &dialogs =
|
const MWWorld::Store<ESM::Dialogue> &dialogs =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
||||||
|
|
||||||
MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin();
|
Filter filter (actor, mChoice, mTalkedTo);
|
||||||
for (; it != dialogs.end(); ++it)
|
|
||||||
|
for (MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin(); it != dialogs.end(); ++it)
|
||||||
{
|
{
|
||||||
if(it->mType == ESM::Dialogue::Greeting)
|
if(it->mType == ESM::Dialogue::Greeting)
|
||||||
{
|
{
|
||||||
if (greetingFound) break;
|
if (const ESM::DialInfo *info = filter.search (*it))
|
||||||
for (std::vector<ESM::DialInfo>::const_iterator iter (it->mInfo.begin());
|
|
||||||
iter!=it->mInfo.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
if (isMatching (actor, *iter) && functionFilter(mActor,*iter,true))
|
if (!info->mSound.empty())
|
||||||
{
|
{
|
||||||
if (!iter->mSound.empty())
|
// TODO play sound
|
||||||
{
|
|
||||||
// TODO play sound
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string text = iter->mResponse;
|
|
||||||
parseText(text);
|
|
||||||
win->addText(iter->mResponse);
|
|
||||||
executeScript(iter->mResultScript);
|
|
||||||
greetingFound = true;
|
|
||||||
mLastTopic = it->mId;
|
|
||||||
mLastDialogue = *iter;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseText (info->mResponse);
|
||||||
|
win->addText (info->mResponse);
|
||||||
|
executeScript (info->mResultScript);
|
||||||
|
mLastTopic = it->mId;
|
||||||
|
mLastDialogue = *info;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -724,7 +214,7 @@ namespace MWDialogue
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueManager::executeScript(std::string script)
|
void DialogueManager::executeScript (const std::string& script)
|
||||||
{
|
{
|
||||||
std::vector<Interpreter::Type_Code> code;
|
std::vector<Interpreter::Type_Code> code;
|
||||||
if(compile(script,code))
|
if(compile(script,code))
|
||||||
|
@ -749,29 +239,24 @@ namespace MWDialogue
|
||||||
int choice = mChoice;
|
int choice = mChoice;
|
||||||
mChoice = -1;
|
mChoice = -1;
|
||||||
mActorKnownTopics.clear();
|
mActorKnownTopics.clear();
|
||||||
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
|
||||||
|
|
||||||
const MWWorld::Store<ESM::Dialogue> &dialogs =
|
const MWWorld::Store<ESM::Dialogue> &dialogs =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
||||||
|
|
||||||
|
Filter filter (mActor, mChoice, mTalkedTo);
|
||||||
|
|
||||||
MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin();
|
for (MWWorld::Store<ESM::Dialogue>::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter)
|
||||||
for (; it != dialogs.end(); ++it)
|
|
||||||
{
|
{
|
||||||
if(it->mType == ESM::Dialogue::Topic)
|
if (iter->mType == ESM::Dialogue::Topic)
|
||||||
{
|
{
|
||||||
for (std::vector<ESM::DialInfo>::const_iterator iter (it->mInfo.begin());
|
if (filter.search (*iter))
|
||||||
iter!=it->mInfo.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true))
|
mActorKnownTopics.push_back (toLower (iter->mId));
|
||||||
|
|
||||||
|
//does the player know the topic?
|
||||||
|
if (mKnownTopics.find (toLower (iter->mId)) != mKnownTopics.end())
|
||||||
{
|
{
|
||||||
mActorKnownTopics.push_back(toLower(it->mId));
|
keywordList.push_back (iter->mId);
|
||||||
//does the player know the topic?
|
|
||||||
if(mKnownTopics.find(toLower(it->mId)) != mKnownTopics.end())
|
|
||||||
{
|
|
||||||
keywordList.push_back(it->mId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -822,6 +307,8 @@ namespace MWDialogue
|
||||||
if (services & ESM::NPC::Enchanting)
|
if (services & ESM::NPC::Enchanting)
|
||||||
windowServices |= MWGui::DialogueWindow::Service_Enchant;
|
windowServices |= MWGui::DialogueWindow::Service_Enchant;
|
||||||
|
|
||||||
|
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
||||||
|
|
||||||
win->setServices (windowServices);
|
win->setServices (windowServices);
|
||||||
|
|
||||||
// sort again, because the previous sort was case-sensitive
|
// sort again, because the previous sort was case-sensitive
|
||||||
|
@ -838,28 +325,25 @@ namespace MWDialogue
|
||||||
if(mDialogueMap.find(keyword) != mDialogueMap.end())
|
if(mDialogueMap.find(keyword) != mDialogueMap.end())
|
||||||
{
|
{
|
||||||
ESM::Dialogue ndialogue = mDialogueMap[keyword];
|
ESM::Dialogue ndialogue = mDialogueMap[keyword];
|
||||||
if(ndialogue.mType == ESM::Dialogue::Topic)
|
if (mDialogueMap[keyword].mType == ESM::Dialogue::Topic)
|
||||||
{
|
{
|
||||||
for (std::vector<ESM::DialInfo>::const_iterator iter = ndialogue.mInfo.begin();
|
Filter filter (mActor, mChoice, mTalkedTo);
|
||||||
iter!=ndialogue.mInfo.end(); ++iter)
|
|
||||||
|
if (const ESM::DialInfo *info = filter.search (mDialogueMap[keyword]))
|
||||||
{
|
{
|
||||||
if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true))
|
std::string text = info->mResponse;
|
||||||
{
|
std::string script = info->mResultScript;
|
||||||
std::string text = iter->mResponse;
|
|
||||||
std::string script = iter->mResultScript;
|
|
||||||
|
|
||||||
parseText(text);
|
parseText (text);
|
||||||
|
|
||||||
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
||||||
win->addTitle(keyword);
|
win->addTitle (keyword);
|
||||||
win->addText(iter->mResponse);
|
win->addText (info->mResponse);
|
||||||
|
|
||||||
executeScript(script);
|
executeScript (script);
|
||||||
|
|
||||||
mLastTopic = keyword;
|
mLastTopic = keyword;
|
||||||
mLastDialogue = *iter;
|
mLastDialogue = *info;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -875,7 +359,7 @@ namespace MWDialogue
|
||||||
// Apply disposition change to NPC's base disposition
|
// Apply disposition change to NPC's base disposition
|
||||||
if (mActor.getTypeName() == typeid(ESM::NPC).name())
|
if (mActor.getTypeName() == typeid(ESM::NPC).name())
|
||||||
{
|
{
|
||||||
MWMechanics::NpcStats npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor);
|
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor);
|
||||||
npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange);
|
npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange);
|
||||||
}
|
}
|
||||||
mPermanentDispositionChange = 0;
|
mPermanentDispositionChange = 0;
|
||||||
|
@ -884,41 +368,36 @@ namespace MWDialogue
|
||||||
|
|
||||||
void DialogueManager::questionAnswered (const std::string& answer)
|
void DialogueManager::questionAnswered (const std::string& answer)
|
||||||
{
|
{
|
||||||
if(mChoiceMap.find(answer) != mChoiceMap.end())
|
if (mChoiceMap.find(answer) != mChoiceMap.end())
|
||||||
{
|
{
|
||||||
mChoice = mChoiceMap[answer];
|
mChoice = mChoiceMap[answer];
|
||||||
|
|
||||||
std::vector<ESM::DialInfo>::const_iterator iter;
|
if (mDialogueMap.find(mLastTopic) != mDialogueMap.end())
|
||||||
if(mDialogueMap.find(mLastTopic) != mDialogueMap.end())
|
|
||||||
{
|
{
|
||||||
ESM::Dialogue ndialogue = mDialogueMap[mLastTopic];
|
if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic)
|
||||||
if(ndialogue.mType == ESM::Dialogue::Topic)
|
|
||||||
{
|
{
|
||||||
for (std::vector<ESM::DialInfo>::const_iterator iter = ndialogue.mInfo.begin();
|
Filter filter (mActor, mChoice, mTalkedTo);
|
||||||
iter!=ndialogue.mInfo.end(); ++iter)
|
|
||||||
|
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic]))
|
||||||
{
|
{
|
||||||
if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true))
|
mChoiceMap.clear();
|
||||||
{
|
mChoice = -1;
|
||||||
mChoiceMap.clear();
|
mIsInChoice = false;
|
||||||
mChoice = -1;
|
std::string text = info->mResponse;
|
||||||
mIsInChoice = false;
|
parseText (text);
|
||||||
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (text);
|
||||||
std::string text = iter->mResponse;
|
executeScript (info->mResultScript);
|
||||||
parseText(text);
|
mLastTopic = mLastTopic;
|
||||||
win->addText(text);
|
mLastDialogue = *info;
|
||||||
executeScript(iter->mResultScript);
|
|
||||||
mLastTopic = mLastTopic;
|
|
||||||
mLastDialogue = *iter;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTopics();
|
updateTopics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueManager::printError (std::string error)
|
void DialogueManager::printError (const std::string& error)
|
||||||
{
|
{
|
||||||
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
||||||
win->addText(error);
|
win->addText(error);
|
||||||
|
@ -932,22 +411,9 @@ namespace MWDialogue
|
||||||
mIsInChoice = true;
|
mIsInChoice = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DialogueManager::getFaction() const
|
MWWorld::Ptr DialogueManager::getActor() const
|
||||||
{
|
{
|
||||||
if (mActor.getTypeName() != typeid(ESM::NPC).name())
|
return mActor;
|
||||||
return "";
|
|
||||||
|
|
||||||
std::string factionID("");
|
|
||||||
MWMechanics::NpcStats stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
|
|
||||||
if(stats.getFactionRanks().empty())
|
|
||||||
{
|
|
||||||
std::cout << "No faction for this actor!";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
factionID = stats.getFactionRanks().begin()->first;
|
|
||||||
}
|
|
||||||
return factionID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueManager::goodbye()
|
void DialogueManager::goodbye()
|
||||||
|
@ -980,7 +446,6 @@ namespace MWDialogue
|
||||||
if (success)
|
if (success)
|
||||||
MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0);
|
MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0);
|
||||||
|
|
||||||
|
|
||||||
// add status message to dialogue window
|
// add status message to dialogue window
|
||||||
std::string text;
|
std::string text;
|
||||||
|
|
||||||
|
|
|
@ -3,48 +3,32 @@
|
||||||
|
|
||||||
#include "../mwbase/dialoguemanager.hpp"
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
|
|
||||||
#include <components/esm/loadinfo.hpp>
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include <components/compiler/streamerrorhandler.hpp>
|
#include <components/compiler/streamerrorhandler.hpp>
|
||||||
#include "../mwscript/compilercontext.hpp"
|
|
||||||
#include "../mwscript/interpretercontext.hpp"
|
|
||||||
#include <components/compiler/output.hpp>
|
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include "../mwscript/compilercontext.hpp"
|
||||||
|
|
||||||
namespace MWDialogue
|
namespace MWDialogue
|
||||||
{
|
{
|
||||||
class DialogueManager : public MWBase::DialogueManager
|
class DialogueManager : public MWBase::DialogueManager
|
||||||
{
|
{
|
||||||
bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo::SelectStruct& select) const;
|
std::map<std::string, ESM::Dialogue> mDialogueMap;
|
||||||
|
std::map<std::string, bool> mKnownTopics;// Those are the topics the player knows.
|
||||||
bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const;
|
|
||||||
|
|
||||||
bool functionFilter(const MWWorld::Ptr& actor, const ESM::DialInfo& info,bool choice);
|
|
||||||
|
|
||||||
void parseText(std::string text);
|
|
||||||
|
|
||||||
void updateTopics();
|
|
||||||
|
|
||||||
std::map<std::string,ESM::Dialogue> mDialogueMap;
|
|
||||||
std::map<std::string,bool> mKnownTopics;// Those are the topics the player knows.
|
|
||||||
std::list<std::string> mActorKnownTopics;
|
std::list<std::string> mActorKnownTopics;
|
||||||
|
|
||||||
MWScript::CompilerContext mCompilerContext;
|
MWScript::CompilerContext mCompilerContext;
|
||||||
std::ostream mErrorStream;
|
std::ostream mErrorStream;
|
||||||
Compiler::StreamErrorHandler mErrorHandler;
|
Compiler::StreamErrorHandler mErrorHandler;
|
||||||
|
|
||||||
|
|
||||||
bool compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code);
|
|
||||||
void executeScript(std::string script);
|
|
||||||
MWWorld::Ptr mActor;
|
MWWorld::Ptr mActor;
|
||||||
|
bool mTalkedTo;
|
||||||
void printError(std::string error);
|
|
||||||
|
|
||||||
int mChoice;
|
int mChoice;
|
||||||
std::map<std::string,int> mChoiceMap;
|
std::map<std::string, int> mChoiceMap;
|
||||||
std::string mLastTopic;
|
std::string mLastTopic;
|
||||||
ESM::DialInfo mLastDialogue;
|
ESM::DialInfo mLastDialogue;
|
||||||
bool mIsInChoice;
|
bool mIsInChoice;
|
||||||
|
@ -52,6 +36,15 @@ namespace MWDialogue
|
||||||
float mTemporaryDispositionChange;
|
float mTemporaryDispositionChange;
|
||||||
float mPermanentDispositionChange;
|
float mPermanentDispositionChange;
|
||||||
|
|
||||||
|
void parseText (const std::string& text);
|
||||||
|
|
||||||
|
void updateTopics();
|
||||||
|
|
||||||
|
bool compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code);
|
||||||
|
void executeScript (const std::string& script);
|
||||||
|
|
||||||
|
void printError (const std::string& error);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
DialogueManager (const Compiler::Extensions& extensions);
|
DialogueManager (const Compiler::Extensions& extensions);
|
||||||
|
@ -64,8 +57,8 @@ namespace MWDialogue
|
||||||
|
|
||||||
virtual void goodbye();
|
virtual void goodbye();
|
||||||
|
|
||||||
///get the faction of the actor you are talking with
|
virtual MWWorld::Ptr getActor() const;
|
||||||
virtual std::string getFaction() const;
|
///< Return the actor the player is currently talking to.
|
||||||
|
|
||||||
//calbacks for the GUI
|
//calbacks for the GUI
|
||||||
virtual void keywordSelected (const std::string& keyword);
|
virtual void keywordSelected (const std::string& keyword);
|
||||||
|
|
494
apps/openmw/mwdialogue/filter.cpp
Normal file
494
apps/openmw/mwdialogue/filter.cpp
Normal file
|
@ -0,0 +1,494 @@
|
||||||
|
|
||||||
|
#include "filter.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/journal.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/player.hpp"
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
|
#include "../mwmechanics/magiceffects.hpp"
|
||||||
|
|
||||||
|
#include "selectwrapper.hpp"
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
|
||||||
|
{
|
||||||
|
// actor id
|
||||||
|
if (!info.mActor.empty())
|
||||||
|
if (toLower (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name());
|
||||||
|
|
||||||
|
// NPC race
|
||||||
|
if (!info.mRace.empty())
|
||||||
|
{
|
||||||
|
if (isCreature)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (toLower (info.mRace)!=toLower (cellRef->mBase->mRace))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NPC class
|
||||||
|
if (!info.mClass.empty())
|
||||||
|
{
|
||||||
|
if (isCreature)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (toLower (info.mClass)!=toLower (cellRef->mBase->mClass))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NPC faction
|
||||||
|
if (!info.mNpcFaction.empty())
|
||||||
|
{
|
||||||
|
if (isCreature)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MWMechanics::NpcStats& stats = MWWorld::Class::get (mActor).getNpcStats (mActor);
|
||||||
|
std::map<std::string, int>::iterator iter = stats.getFactionRanks().find (toLower (info.mNpcFaction));
|
||||||
|
|
||||||
|
if (iter==stats.getFactionRanks().end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check rank
|
||||||
|
if (iter->second < info.mData.mRank)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gender
|
||||||
|
if (!isCreature)
|
||||||
|
{
|
||||||
|
MWWorld::LiveCellRef<ESM::NPC>* npc = mActor.get<ESM::NPC>();
|
||||||
|
if (info.mData.mGender==(npc->mBase->mFlags & npc->mBase->Female ? 0 : 1))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
|
||||||
|
{
|
||||||
|
const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
|
|
||||||
|
// check player faction
|
||||||
|
if (!info.mPcFaction.empty())
|
||||||
|
{
|
||||||
|
MWMechanics::NpcStats& stats = MWWorld::Class::get (player).getNpcStats (player);
|
||||||
|
std::map<std::string,int>::iterator iter = stats.getFactionRanks().find (toLower (info.mPcFaction));
|
||||||
|
|
||||||
|
if(iter==stats.getFactionRanks().end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check rank
|
||||||
|
if (iter->second < info.mData.mPCrank)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check cell
|
||||||
|
if (!info.mCell.empty())
|
||||||
|
if (toLower (player.getCell()->mCell->mName) != toLower (info.mCell))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const
|
||||||
|
{
|
||||||
|
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.mSelects.begin());
|
||||||
|
iter != info.mSelects.end(); ++iter)
|
||||||
|
if (!testSelectStruct (*iter))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const
|
||||||
|
{
|
||||||
|
if (select.isNpcOnly() && mActor.getTypeName()!=typeid (ESM::NPC).name())
|
||||||
|
return select.isInverted();
|
||||||
|
|
||||||
|
switch (select.getType())
|
||||||
|
{
|
||||||
|
case SelectWrapper::Type_None: return true;
|
||||||
|
case SelectWrapper::Type_Integer: return select.selectCompare (getSelectStructInteger (select));
|
||||||
|
case SelectWrapper::Type_Numeric: return testSelectStructNumeric (select);
|
||||||
|
case SelectWrapper::Type_Boolean: return select.selectCompare (getSelectStructBoolean (select));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) const
|
||||||
|
{
|
||||||
|
switch (select.getFunction())
|
||||||
|
{
|
||||||
|
case SelectWrapper::Function_Global:
|
||||||
|
|
||||||
|
// internally all globals are float :(
|
||||||
|
return select.selectCompare (
|
||||||
|
MWBase::Environment::get().getWorld()->getGlobalVariable (select.getName()).mFloat);
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Local:
|
||||||
|
{
|
||||||
|
std::string scriptName = MWWorld::Class::get (mActor).getScript (mActor);
|
||||||
|
|
||||||
|
if (scriptName.empty())
|
||||||
|
return false; // no script
|
||||||
|
|
||||||
|
const ESM::Script *script =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptName);
|
||||||
|
|
||||||
|
std::string name = select.getName();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i<static_cast<int> (script->mVarNames.size()); ++i)
|
||||||
|
if (script->mVarNames[i]==name)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i>=static_cast<int> (script->mVarNames.size()))
|
||||||
|
return false; // script does not have a variable of this name
|
||||||
|
|
||||||
|
const MWScript::Locals& locals = mActor.getRefData().getLocals();
|
||||||
|
|
||||||
|
if (i<script->mData.mNumShorts)
|
||||||
|
return select.selectCompare (static_cast<int> (locals.mShorts[i]));
|
||||||
|
|
||||||
|
i -= script->mData.mNumShorts;
|
||||||
|
|
||||||
|
if (i<script->mData.mNumLongs)
|
||||||
|
return select.selectCompare (locals.mLongs[i]);
|
||||||
|
|
||||||
|
i -= script->mData.mNumShorts;
|
||||||
|
|
||||||
|
return select.selectCompare (locals.mFloats.at (i));
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcHealthPercent:
|
||||||
|
{
|
||||||
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
|
|
||||||
|
float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() /
|
||||||
|
MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified();
|
||||||
|
|
||||||
|
return select.selectCompare (ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcDynamicStat:
|
||||||
|
{
|
||||||
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
|
|
||||||
|
float value = MWWorld::Class::get (player).getCreatureStats (player).
|
||||||
|
getDynamic (select.getArgument()).getCurrent();
|
||||||
|
|
||||||
|
return select.selectCompare (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_HealthPercent:
|
||||||
|
{
|
||||||
|
float ratio = MWWorld::Class::get (mActor).getCreatureStats (mActor).getHealth().getCurrent() /
|
||||||
|
MWWorld::Class::get (mActor).getCreatureStats (mActor).getHealth().getModified();
|
||||||
|
|
||||||
|
return select.selectCompare (ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown numeric select function");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) const
|
||||||
|
{
|
||||||
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
|
|
||||||
|
switch (select.getFunction())
|
||||||
|
{
|
||||||
|
case SelectWrapper::Function_Journal:
|
||||||
|
|
||||||
|
return MWBase::Environment::get().getJournal()->getJournalIndex (select.getName());
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Item:
|
||||||
|
{
|
||||||
|
MWWorld::ContainerStore& store = MWWorld::Class::get (player).getContainerStore (player);
|
||||||
|
|
||||||
|
int sum = 0;
|
||||||
|
|
||||||
|
std::string name = select.getName();
|
||||||
|
|
||||||
|
for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)
|
||||||
|
if (toLower(iter->getCellRef().mRefID) == name)
|
||||||
|
sum += iter->getRefData().getCount();
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Dead:
|
||||||
|
|
||||||
|
return MWBase::Environment::get().getMechanicsManager()->countDeaths (select.getName());
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Choice:
|
||||||
|
|
||||||
|
return mChoice;
|
||||||
|
|
||||||
|
case SelectWrapper::Function_AiSetting:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (mActor).getCreatureStats (mActor).getAiSetting (select.getArgument());
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcAttribute:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getCreatureStats (player).
|
||||||
|
getAttribute (select.getArgument()).getModified();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcSkill:
|
||||||
|
|
||||||
|
return static_cast<int> (MWWorld::Class::get (player).
|
||||||
|
getNpcStats (player).getSkill (select.getArgument()).getModified());
|
||||||
|
|
||||||
|
case SelectWrapper::Function_FriendlyHit:
|
||||||
|
{
|
||||||
|
int hits = MWWorld::Class::get (mActor).getCreatureStats (mActor).getFriendlyHits();
|
||||||
|
|
||||||
|
return hits>4 ? 4 : hits;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcLevel:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getCreatureStats (player).getLevel();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcGender:
|
||||||
|
|
||||||
|
return player.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female ? 0 : 1;
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcClothingModifier:
|
||||||
|
{
|
||||||
|
MWWorld::InventoryStore& store = MWWorld::Class::get (player).getInventoryStore (player);
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
for (int i=0; i<=15; ++i) // everything except thigns held in hands and amunition
|
||||||
|
{
|
||||||
|
MWWorld::ContainerStoreIterator slot = store.getSlot (i);
|
||||||
|
|
||||||
|
if (slot!=store.end())
|
||||||
|
value += MWWorld::Class::get (*slot).getValue (*slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcCrimeLevel:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getNpcStats (player).getBounty();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_RankRequirement:
|
||||||
|
{
|
||||||
|
if (MWWorld::Class::get (mActor).getNpcStats (mActor).getFactionRanks().empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
std::string faction =
|
||||||
|
MWWorld::Class::get (mActor).getNpcStats (mActor).getFactionRanks().begin()->first;
|
||||||
|
|
||||||
|
int rank = getFactionRank (player, faction);
|
||||||
|
|
||||||
|
if (rank>=9)
|
||||||
|
return 0; // max rank
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if (hasFactionRankSkillRequirements (player, faction, rank+1))
|
||||||
|
result += 1;
|
||||||
|
|
||||||
|
if (hasFactionRankReputationRequirements (player, faction, rank+1))
|
||||||
|
result += 2;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Level:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (mActor).getCreatureStats (mActor).getLevel();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PCReputation:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getNpcStats (player).getReputation();
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown integer select function");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) const
|
||||||
|
{
|
||||||
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
|
|
||||||
|
switch (select.getFunction())
|
||||||
|
{
|
||||||
|
case SelectWrapper::Function_False:
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Id:
|
||||||
|
|
||||||
|
return select.getName()==toLower (MWWorld::Class::get (mActor).getId (mActor));
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Faction:
|
||||||
|
|
||||||
|
return toLower (mActor.get<ESM::NPC>()->mBase->mFaction)==select.getName();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Class:
|
||||||
|
|
||||||
|
return toLower (mActor.get<ESM::NPC>()->mBase->mClass)==select.getName();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Race:
|
||||||
|
|
||||||
|
return toLower (mActor.get<ESM::NPC>()->mBase->mRace)==select.getName();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_Cell:
|
||||||
|
|
||||||
|
return toLower (mActor.getCell()->mCell->mName)==select.getName();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_SameGender:
|
||||||
|
|
||||||
|
return (player.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female)==
|
||||||
|
(mActor.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female);
|
||||||
|
|
||||||
|
case SelectWrapper::Function_SameRace:
|
||||||
|
|
||||||
|
return toLower (mActor.get<ESM::NPC>()->mBase->mRace)!=
|
||||||
|
toLower (player.get<ESM::NPC>()->mBase->mRace);
|
||||||
|
|
||||||
|
case SelectWrapper::Function_SameFaction:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (mActor).getNpcStats (mActor).isSameFaction (
|
||||||
|
MWWorld::Class::get (player).getNpcStats (player));
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcCommonDisease:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getCreatureStats (player).hasCommonDisease();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcBlightDisease:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getCreatureStats (player).hasBlightDisease();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcCorprus:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getCreatureStats (player).
|
||||||
|
getMagicEffects().get (132).mMagnitude!=0;
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcExpelled:
|
||||||
|
{
|
||||||
|
if (MWWorld::Class::get (mActor).getNpcStats (mActor).getFactionRanks().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string faction =
|
||||||
|
MWWorld::Class::get (mActor).getNpcStats (mActor).getFactionRanks().begin()->first;
|
||||||
|
|
||||||
|
std::set<std::string>& expelled = MWWorld::Class::get (player).getNpcStats (player).getExpelled();
|
||||||
|
|
||||||
|
return expelled.find (faction)!=expelled.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
case SelectWrapper::Function_PcVampire:
|
||||||
|
|
||||||
|
return MWWorld::Class::get (player).getNpcStats (player).isVampire();
|
||||||
|
|
||||||
|
case SelectWrapper::Function_TalkedToPc:
|
||||||
|
|
||||||
|
return mTalkedToPlayer;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown boolean select function");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MWDialogue::Filter::getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const
|
||||||
|
{
|
||||||
|
MWMechanics::NpcStats& stats = MWWorld::Class::get (actor).getNpcStats (actor);
|
||||||
|
|
||||||
|
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (factionId);
|
||||||
|
|
||||||
|
if (iter==stats.getFactionRanks().end())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::hasFactionRankSkillRequirements (const MWWorld::Ptr& actor,
|
||||||
|
const std::string& factionId, int rank) const
|
||||||
|
{
|
||||||
|
if (rank<0 || rank>=10)
|
||||||
|
throw std::runtime_error ("rank index out of range");
|
||||||
|
|
||||||
|
if (!MWWorld::Class::get (actor).getNpcStats (actor).hasSkillsForRank (factionId, rank))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const ESM::Faction& faction =
|
||||||
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& stats = MWWorld::Class::get (actor).getCreatureStats (actor);
|
||||||
|
|
||||||
|
return stats.getAttribute (faction.mData.mAttribute1).getBase()>=faction.mData.mRankData[rank].mAttribute1 &&
|
||||||
|
stats.getAttribute (faction.mData.mAttribute2).getBase()>=faction.mData.mRankData[rank].mAttribute2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::hasFactionRankReputationRequirements (const MWWorld::Ptr& actor,
|
||||||
|
const std::string& factionId, int rank) const
|
||||||
|
{
|
||||||
|
if (rank<0 || rank>=10)
|
||||||
|
throw std::runtime_error ("rank index out of range");
|
||||||
|
|
||||||
|
MWMechanics::NpcStats& stats = MWWorld::Class::get (actor).getNpcStats (actor);
|
||||||
|
|
||||||
|
const ESM::Faction& faction =
|
||||||
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);
|
||||||
|
|
||||||
|
return stats.getFactionReputation (factionId)>=faction.mData.mRankData[rank].mFactReaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer)
|
||||||
|
: mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool MWDialogue::Filter::operator() (const ESM::DialInfo& info) const
|
||||||
|
{
|
||||||
|
return testActor (info) && testPlayer (info) && testSelectStructs (info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue) const
|
||||||
|
{
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter = dialogue.mInfo.begin();
|
||||||
|
iter!=dialogue.mInfo.end(); ++iter)
|
||||||
|
if ((*this) (*iter))
|
||||||
|
return &*iter;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
58
apps/openmw/mwdialogue/filter.hpp
Normal file
58
apps/openmw/mwdialogue/filter.hpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef GAME_MWDIALOGUE_FILTER_H
|
||||||
|
#define GAME_MWDIALOGUE_FILTER_H
|
||||||
|
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct DialInfo;
|
||||||
|
struct Dialogue;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
class SelectWrapper;
|
||||||
|
|
||||||
|
class Filter
|
||||||
|
{
|
||||||
|
MWWorld::Ptr mActor;
|
||||||
|
int mChoice;
|
||||||
|
bool mTalkedToPlayer;
|
||||||
|
|
||||||
|
bool testActor (const ESM::DialInfo& info) const;
|
||||||
|
///< Is this the right actor for this \a info?
|
||||||
|
|
||||||
|
bool testPlayer (const ESM::DialInfo& info) const;
|
||||||
|
///< Do the player and the cell the player is currently in match \a info?
|
||||||
|
|
||||||
|
bool testSelectStructs (const ESM::DialInfo& info) const;
|
||||||
|
///< Are all select structs matching?
|
||||||
|
|
||||||
|
bool testSelectStruct (const SelectWrapper& select) const;
|
||||||
|
|
||||||
|
bool testSelectStructNumeric (const SelectWrapper& select) const;
|
||||||
|
|
||||||
|
int getSelectStructInteger (const SelectWrapper& select) const;
|
||||||
|
|
||||||
|
bool getSelectStructBoolean (const SelectWrapper& select) const;
|
||||||
|
|
||||||
|
int getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const;
|
||||||
|
|
||||||
|
bool hasFactionRankSkillRequirements (const MWWorld::Ptr& actor, const std::string& factionId,
|
||||||
|
int rank) const;
|
||||||
|
|
||||||
|
bool hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, const std::string& factionId,
|
||||||
|
int rank) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer);
|
||||||
|
|
||||||
|
bool operator() (const ESM::DialInfo& info) const;
|
||||||
|
///< \return does the dialogue match?
|
||||||
|
|
||||||
|
const ESM::DialInfo *search (const ESM::Dialogue& dialogue) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
294
apps/openmw/mwdialogue/selectwrapper.cpp
Normal file
294
apps/openmw/mwdialogue/selectwrapper.cpp
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
|
||||||
|
#include "selectwrapper.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
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 selectCompareImp (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 selectCompareImp (const ESM::DialInfo::SelectStruct& select, T value1)
|
||||||
|
{
|
||||||
|
if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int ||
|
||||||
|
select.mType==ESM::VT_Long)
|
||||||
|
{
|
||||||
|
return selectCompareImp (select.mSelectRule[4], value1, select.mI);
|
||||||
|
}
|
||||||
|
else if (select.mType==ESM::VT_Float)
|
||||||
|
{
|
||||||
|
return selectCompareImp (select.mSelectRule[4], value1, select.mF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error (
|
||||||
|
"unsupported variable type in dialogue info select");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() const
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
std::istringstream (mSelect.mSelectRule.substr(2,2)) >> index;
|
||||||
|
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
// 0, 1
|
||||||
|
case 2: return Function_RankRequirement;
|
||||||
|
// 3
|
||||||
|
case 4: return Function_HealthPercent;
|
||||||
|
case 5: return Function_PCReputation;
|
||||||
|
case 6: return Function_PcLevel;
|
||||||
|
case 7: return Function_PcHealthPercent;
|
||||||
|
case 8: case 9: return Function_PcDynamicStat;
|
||||||
|
case 10: return Function_PcAttribute;
|
||||||
|
case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20:
|
||||||
|
case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30:
|
||||||
|
case 31: case 32: case 33: case 34: case 35: case 36: case 37: return Function_PcSkill;
|
||||||
|
case 38: return Function_PcGender;
|
||||||
|
case 39: return Function_PcExpelled;
|
||||||
|
case 40: return Function_PcCommonDisease;
|
||||||
|
case 41: return Function_PcBlightDisease;
|
||||||
|
case 42: return Function_PcClothingModifier;
|
||||||
|
case 43: return Function_PcCrimeLevel;
|
||||||
|
case 44: return Function_SameGender;
|
||||||
|
case 45: return Function_SameRace;
|
||||||
|
case 46: return Function_SameFaction;
|
||||||
|
// 47-49
|
||||||
|
case 50: return Function_Choice;
|
||||||
|
case 51: case 52: case 53: case 54: case 55: case 56: case 57: return Function_PcAttribute;
|
||||||
|
case 58: return Function_PcCorprus;
|
||||||
|
// 59
|
||||||
|
case 60: return Function_PcVampire;
|
||||||
|
case 61: return Function_Level;
|
||||||
|
// 62
|
||||||
|
case 63: return Function_TalkedToPc;
|
||||||
|
case 64: return Function_PcDynamicStat;
|
||||||
|
// 65
|
||||||
|
case 66: return Function_FriendlyHit;
|
||||||
|
case 67: case 68: case 69: case 70: return Function_AiSetting;
|
||||||
|
// 71
|
||||||
|
}
|
||||||
|
|
||||||
|
return Function_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWDialogue::SelectWrapper::SelectWrapper (const ESM::DialInfo::SelectStruct& select) : mSelect (select) {}
|
||||||
|
|
||||||
|
MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() const
|
||||||
|
{
|
||||||
|
char type = mSelect.mSelectRule[1];
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case '1': return decodeFunction();
|
||||||
|
case '2': return Function_Global;
|
||||||
|
case '3': return Function_Local;
|
||||||
|
case '4': return Function_Journal;
|
||||||
|
case '5': return Function_Item;
|
||||||
|
case '6': return Function_Dead;
|
||||||
|
case '7': return Function_Id;
|
||||||
|
case '8': return Function_Faction;
|
||||||
|
case '9': return Function_Class;
|
||||||
|
case 'A': return Function_Race;
|
||||||
|
case 'B': return Function_Cell;
|
||||||
|
case 'C': return Function_Local;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Function_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MWDialogue::SelectWrapper::getArgument() const
|
||||||
|
{
|
||||||
|
if (mSelect.mSelectRule[1]!='1')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
std::istringstream (mSelect.mSelectRule.substr(2,2)) >> index;
|
||||||
|
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
// AI settings
|
||||||
|
case 67: return 1;
|
||||||
|
case 68: return 0;
|
||||||
|
case 69: return 3;
|
||||||
|
case 70: return 2;
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
case 10: return 0;
|
||||||
|
case 51: return 1;
|
||||||
|
case 52: return 2;
|
||||||
|
case 53: return 3;
|
||||||
|
case 54: return 4;
|
||||||
|
case 55: return 5;
|
||||||
|
case 56: return 6;
|
||||||
|
case 57: return 7;
|
||||||
|
|
||||||
|
// skills
|
||||||
|
case 11: return 0;
|
||||||
|
case 12: return 1;
|
||||||
|
case 13: return 2;
|
||||||
|
case 14: return 3;
|
||||||
|
case 15: return 4;
|
||||||
|
case 16: return 5;
|
||||||
|
case 17: return 6;
|
||||||
|
case 18: return 7;
|
||||||
|
case 19: return 8;
|
||||||
|
case 20: return 9;
|
||||||
|
case 21: return 10;
|
||||||
|
case 22: return 11;
|
||||||
|
case 23: return 12;
|
||||||
|
case 24: return 13;
|
||||||
|
case 25: return 14;
|
||||||
|
case 26: return 15;
|
||||||
|
case 27: return 16;
|
||||||
|
case 28: return 17;
|
||||||
|
case 29: return 18;
|
||||||
|
case 30: return 19;
|
||||||
|
case 31: return 20;
|
||||||
|
case 32: return 21;
|
||||||
|
case 33: return 22;
|
||||||
|
case 34: return 23;
|
||||||
|
case 35: return 24;
|
||||||
|
case 36: return 25;
|
||||||
|
case 37: return 26;
|
||||||
|
|
||||||
|
// dynamic stats
|
||||||
|
case 8: return 1;
|
||||||
|
case 9: return 2;
|
||||||
|
case 64: return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const
|
||||||
|
{
|
||||||
|
static const Function integerFunctions[] =
|
||||||
|
{
|
||||||
|
Function_Journal, Function_Item, Function_Dead,
|
||||||
|
Function_Choice,
|
||||||
|
Function_AiSetting,
|
||||||
|
Function_PcAttribute, Function_PcSkill,
|
||||||
|
Function_FriendlyHit,
|
||||||
|
Function_PcLevel, Function_PcGender, Function_PcClothingModifier,
|
||||||
|
Function_PcCrimeLevel,
|
||||||
|
Function_RankRequirement,
|
||||||
|
Function_Level, Function_PCReputation,
|
||||||
|
Function_None // end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Function numericFunctions[] =
|
||||||
|
{
|
||||||
|
Function_Global, Function_Local,
|
||||||
|
Function_PcDynamicStat, Function_PcHealthPercent,
|
||||||
|
Function_HealthPercent,
|
||||||
|
Function_None // end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Function booleanFunctions[] =
|
||||||
|
{
|
||||||
|
Function_False,
|
||||||
|
Function_Id, Function_Faction, Function_Class, Function_Race, Function_Cell,
|
||||||
|
Function_SameGender, Function_SameRace, Function_SameFaction,
|
||||||
|
Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus,
|
||||||
|
Function_PcExpelled,
|
||||||
|
Function_PcVampire, Function_TalkedToPc,
|
||||||
|
Function_None // end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
Function function = getFunction();
|
||||||
|
|
||||||
|
for (int i=0; integerFunctions[i]!=Function_None; ++i)
|
||||||
|
if (integerFunctions[i]==function)
|
||||||
|
return Type_Integer;
|
||||||
|
|
||||||
|
for (int i=0; numericFunctions[i]!=Function_None; ++i)
|
||||||
|
if (numericFunctions[i]==function)
|
||||||
|
return Type_Numeric;
|
||||||
|
|
||||||
|
for (int i=0; booleanFunctions[i]!=Function_None; ++i)
|
||||||
|
if (booleanFunctions[i]==function)
|
||||||
|
return Type_Boolean;
|
||||||
|
|
||||||
|
return Type_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::SelectWrapper::isInverted() const
|
||||||
|
{
|
||||||
|
char type = mSelect.mSelectRule[1];
|
||||||
|
|
||||||
|
return type=='7' || type=='8' || type=='9' || type=='A' || type=='B' || type=='C';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::SelectWrapper::isNpcOnly() const
|
||||||
|
{
|
||||||
|
static const Function functions[] =
|
||||||
|
{
|
||||||
|
Function_Faction, SelectWrapper::Function_Class, SelectWrapper::Function_Race,
|
||||||
|
Function_SameGender, Function_SameRace, Function_SameFaction,
|
||||||
|
Function_PcSkill,
|
||||||
|
Function_PcExpelled,
|
||||||
|
Function_PcVampire,
|
||||||
|
Function_PcCrimeLevel,
|
||||||
|
Function_RankRequirement,
|
||||||
|
Function_None // end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
Function function = getFunction();
|
||||||
|
|
||||||
|
for (int i=0; functions[i]!=Function_None; ++i)
|
||||||
|
if (functions[i]==function)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::SelectWrapper::selectCompare (int value) const
|
||||||
|
{
|
||||||
|
return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::SelectWrapper::selectCompare (float value) const
|
||||||
|
{
|
||||||
|
return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWDialogue::SelectWrapper::selectCompare (bool value) const
|
||||||
|
{
|
||||||
|
return selectCompareImp (mSelect, static_cast<int> (value))!=isInverted(); // logic XOR
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MWDialogue::SelectWrapper::getName() const
|
||||||
|
{
|
||||||
|
return toLower (mSelect.mSelectRule.substr (5));
|
||||||
|
}
|
80
apps/openmw/mwdialogue/selectwrapper.hpp
Normal file
80
apps/openmw/mwdialogue/selectwrapper.hpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#ifndef GAME_MWDIALOGUE_SELECTWRAPPER_H
|
||||||
|
#define GAME_MWDIALOGUE_SELECTWRAPPER_H
|
||||||
|
|
||||||
|
#include <components/esm/loadinfo.hpp>
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
class SelectWrapper
|
||||||
|
{
|
||||||
|
const ESM::DialInfo::SelectStruct& mSelect;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum Function
|
||||||
|
{
|
||||||
|
Function_None, Function_False,
|
||||||
|
Function_Journal,
|
||||||
|
Function_Item,
|
||||||
|
Function_Dead,
|
||||||
|
Function_Id,
|
||||||
|
Function_Faction,
|
||||||
|
Function_Class,
|
||||||
|
Function_Race,
|
||||||
|
Function_Cell,
|
||||||
|
Function_Local,
|
||||||
|
Function_Global,
|
||||||
|
Function_SameGender, Function_SameRace, Function_SameFaction,
|
||||||
|
Function_Choice,
|
||||||
|
Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus,
|
||||||
|
Function_AiSetting,
|
||||||
|
Function_PcAttribute, Function_PcSkill,
|
||||||
|
Function_PcExpelled,
|
||||||
|
Function_PcVampire,
|
||||||
|
Function_FriendlyHit,
|
||||||
|
Function_TalkedToPc,
|
||||||
|
Function_PcLevel, Function_PcHealthPercent, Function_PcDynamicStat,
|
||||||
|
Function_PcGender, Function_PcClothingModifier, Function_PcCrimeLevel,
|
||||||
|
Function_RankRequirement,
|
||||||
|
Function_HealthPercent, Function_Level, Function_PCReputation
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
Type_None,
|
||||||
|
Type_Integer,
|
||||||
|
Type_Numeric,
|
||||||
|
Type_Boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Function decodeFunction() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SelectWrapper (const ESM::DialInfo::SelectStruct& select);
|
||||||
|
|
||||||
|
Function getFunction() const;
|
||||||
|
|
||||||
|
int getArgument() const;
|
||||||
|
|
||||||
|
Type getType() const;
|
||||||
|
|
||||||
|
bool isInverted() const;
|
||||||
|
|
||||||
|
bool isNpcOnly() const;
|
||||||
|
///< \attention Do not call any of the select functions for this select struct!
|
||||||
|
|
||||||
|
bool selectCompare (int value) const;
|
||||||
|
|
||||||
|
bool selectCompare (float value) const;
|
||||||
|
|
||||||
|
bool selectCompare (bool value) const;
|
||||||
|
|
||||||
|
std::string getName() const;
|
||||||
|
///< Return case-smashed name.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -10,8 +10,10 @@
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
CreatureStats::CreatureStats()
|
CreatureStats::CreatureStats()
|
||||||
: mLevel (0), mHello (0), mFight (0), mFlee (0), mAlarm (0), mLevelHealthBonus(0.f), mDead (false)
|
: mLevel (0), mLevelHealthBonus(0.f), mDead (false), mFriendlyHits (0), mTalkedTo (false)
|
||||||
{
|
{
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
mAiSettings[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureStats::increaseLevelHealthBonus (float value)
|
void CreatureStats::increaseLevelHealthBonus (float value)
|
||||||
|
@ -90,27 +92,13 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
return mLevel;
|
return mLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CreatureStats::getAiSetting (int index) const
|
||||||
|
{
|
||||||
|
assert (index>=0 && index<4);
|
||||||
|
return mAiSettings[index];
|
||||||
|
}
|
||||||
|
|
||||||
int CreatureStats::getHello() const
|
|
||||||
{
|
|
||||||
return mHello;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CreatureStats::getFight() const
|
|
||||||
{
|
|
||||||
return mFight;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CreatureStats::getFlee() const
|
|
||||||
{
|
|
||||||
return mFlee;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CreatureStats::getAlarm() const
|
|
||||||
{
|
|
||||||
return mAlarm;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stat<int> &CreatureStats::getAttribute(int index)
|
Stat<int> &CreatureStats::getAttribute(int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index > 7) {
|
if (index < 0 || index > 7) {
|
||||||
|
@ -196,25 +184,11 @@ namespace MWMechanics
|
||||||
mMagicEffects = effects;
|
mMagicEffects = effects;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureStats::setHello(int value)
|
void CreatureStats::setAiSetting (int index, int value)
|
||||||
{
|
{
|
||||||
mHello = value;
|
assert (index>=0 && index<4);
|
||||||
|
mAiSettings[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureStats::setFight(int value)
|
|
||||||
{
|
|
||||||
mFight = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreatureStats::setFlee(int value)
|
|
||||||
{
|
|
||||||
mFlee = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreatureStats::setAlarm(int value)
|
|
||||||
{
|
|
||||||
mAlarm = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreatureStats::isDead() const
|
bool CreatureStats::isDead() const
|
||||||
{
|
{
|
||||||
|
@ -242,4 +216,24 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
return mSpells.hasBlightDisease();
|
return mSpells.hasBlightDisease();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CreatureStats::getFriendlyHits() const
|
||||||
|
{
|
||||||
|
return mFriendlyHits;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureStats::friendlyHit()
|
||||||
|
{
|
||||||
|
++mFriendlyHits;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreatureStats::hasTalkedToPlayer() const
|
||||||
|
{
|
||||||
|
return mTalkedTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureStats::talkedToPlayer()
|
||||||
|
{
|
||||||
|
mTalkedTo = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,12 @@ namespace MWMechanics
|
||||||
Spells mSpells;
|
Spells mSpells;
|
||||||
ActiveSpells mActiveSpells;
|
ActiveSpells mActiveSpells;
|
||||||
MagicEffects mMagicEffects;
|
MagicEffects mMagicEffects;
|
||||||
int mHello;
|
int mAiSettings[4];
|
||||||
int mFight;
|
|
||||||
int mFlee;
|
|
||||||
int mAlarm;
|
|
||||||
AiSequence mAiSequence;
|
AiSequence mAiSequence;
|
||||||
float mLevelHealthBonus;
|
float mLevelHealthBonus;
|
||||||
bool mDead;
|
bool mDead;
|
||||||
|
int mFriendlyHits;
|
||||||
|
bool mTalkedTo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CreatureStats();
|
CreatureStats();
|
||||||
|
@ -53,13 +52,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
int getLevel() const;
|
int getLevel() const;
|
||||||
|
|
||||||
int getHello() const;
|
int getAiSetting (int index) const;
|
||||||
|
///< 0: hello, 1 fight, 2 flee, 3 alarm
|
||||||
int getFight() const;
|
|
||||||
|
|
||||||
int getFlee() const;
|
|
||||||
|
|
||||||
int getAlarm() const;
|
|
||||||
|
|
||||||
Stat<int> & getAttribute(int index);
|
Stat<int> & getAttribute(int index);
|
||||||
|
|
||||||
|
@ -87,14 +81,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
void setLevel(int level);
|
void setLevel(int level);
|
||||||
|
|
||||||
void setHello(int value);
|
void setAiSetting (int index, int value);
|
||||||
|
///< 0: hello, 1 fight, 2 flee, 3 alarm
|
||||||
void setFight(int value);
|
|
||||||
|
|
||||||
void setFlee(int value);
|
|
||||||
|
|
||||||
void setAlarm(int value);
|
|
||||||
|
|
||||||
const AiSequence& getAiSequence() const;
|
const AiSequence& getAiSequence() const;
|
||||||
|
|
||||||
AiSequence& getAiSequence();
|
AiSequence& getAiSequence();
|
||||||
|
@ -112,7 +101,18 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool hasCommonDisease() const;
|
bool hasCommonDisease() const;
|
||||||
|
|
||||||
bool hasBlightDisease() const;
|
bool hasBlightDisease() const;
|
||||||
|
|
||||||
|
int getFriendlyHits() const;
|
||||||
|
///< Number of friendly hits received.
|
||||||
|
|
||||||
|
void friendlyHit();
|
||||||
|
///< Increase number of friendly hits by one.
|
||||||
|
|
||||||
|
bool hasTalkedToPlayer() const;
|
||||||
|
///< Has this creature talked with the player before?
|
||||||
|
|
||||||
|
void talkedToPlayer();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -572,8 +572,8 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
float s = int(r * fPerDieRollMult * fPerTempMult);
|
float s = int(r * fPerDieRollMult * fPerTempMult);
|
||||||
|
|
||||||
npcStats.setFlee ( std::max(0, std::min(100, npcStats.getFlee() + int(std::max(iPerMinChange, s)))));
|
npcStats.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + int(std::max(iPerMinChange, s)))));
|
||||||
npcStats.setFight ( std::max(0, std::min(100, npcStats.getFight() + int(std::min(-iPerMinChange, -s)))));
|
npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + int(std::min(-iPerMinChange, -s)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
float c = -std::abs(int(r * fPerDieRollMult));
|
float c = -std::abs(int(r * fPerDieRollMult));
|
||||||
|
@ -607,8 +607,8 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
float s = c * fPerDieRollMult * fPerTempMult;
|
float s = c * fPerDieRollMult * fPerTempMult;
|
||||||
|
|
||||||
npcStats.setFlee ( std::max(0, std::min(100, npcStats.getFlee() + std::min(-int(iPerMinChange), int(-s)))));
|
npcStats.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + std::min(-int(iPerMinChange), int(-s)))));
|
||||||
npcStats.setFight ( std::max(0, std::min(100, npcStats.getFight() + std::max(int(iPerMinChange), int(s)))));
|
npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + std::max(int(iPerMinChange), int(s)))));
|
||||||
}
|
}
|
||||||
x = int(-c * fPerDieRollMult);
|
x = int(-c * fPerDieRollMult);
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
#include <components/esm/loadskil.hpp>
|
#include <components/esm/loadskil.hpp>
|
||||||
#include <components/esm/loadclas.hpp>
|
#include <components/esm/loadclas.hpp>
|
||||||
#include <components/esm/loadgmst.hpp>
|
#include <components/esm/loadgmst.hpp>
|
||||||
|
#include <components/esm/loadfact.hpp>
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
@ -19,8 +22,7 @@
|
||||||
|
|
||||||
MWMechanics::NpcStats::NpcStats()
|
MWMechanics::NpcStats::NpcStats()
|
||||||
: mMovementFlags (0), mDrawState (DrawState_Nothing), mBounty (0)
|
: mMovementFlags (0), mDrawState (DrawState_Nothing), mBounty (0)
|
||||||
, mLevelProgress(0), mDisposition(0), mReputation(0)
|
, mLevelProgress(0), mDisposition(0), mVampire (0), mReputation(0)
|
||||||
|
|
||||||
{
|
{
|
||||||
mSkillIncreases.resize (ESM::Attribute::Length);
|
mSkillIncreases.resize (ESM::Attribute::Length);
|
||||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||||
|
@ -81,11 +83,26 @@ std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks()
|
||||||
return mFactionRank;
|
return mFactionRank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<std::string>& MWMechanics::NpcStats::getExpelled()
|
||||||
|
{
|
||||||
|
return mExpelled;
|
||||||
|
}
|
||||||
|
|
||||||
const std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() const
|
const std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() const
|
||||||
{
|
{
|
||||||
return mFactionRank;
|
return mFactionRank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MWMechanics::NpcStats::isSameFaction (const NpcStats& npcStats) const
|
||||||
|
{
|
||||||
|
for (std::map<std::string, int>::const_iterator iter (mFactionRank.begin()); iter!=mFactionRank.end();
|
||||||
|
++iter)
|
||||||
|
if (npcStats.mFactionRank.find (iter->first)!=npcStats.mFactionRank.end())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType,
|
float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType,
|
||||||
int level) const
|
int level) const
|
||||||
{
|
{
|
||||||
|
@ -260,6 +277,31 @@ void MWMechanics::NpcStats::setBounty (int bounty)
|
||||||
mBounty = bounty;
|
mBounty = bounty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MWMechanics::NpcStats::getFactionReputation (const std::string& faction) const
|
||||||
|
{
|
||||||
|
std::map<std::string, int>::const_iterator iter = mFactionReputation.find (faction);
|
||||||
|
|
||||||
|
if (iter==mFactionReputation.end())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::NpcStats::setFactionReputation (const std::string& faction, int value)
|
||||||
|
{
|
||||||
|
mFactionReputation[faction] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWMechanics::NpcStats::isVampire() const
|
||||||
|
{
|
||||||
|
return mVampire;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::NpcStats::setVampire (bool set)
|
||||||
|
{
|
||||||
|
mVampire = set;
|
||||||
|
}
|
||||||
|
|
||||||
int MWMechanics::NpcStats::getReputation() const
|
int MWMechanics::NpcStats::getReputation() const
|
||||||
{
|
{
|
||||||
return mReputation;
|
return mReputation;
|
||||||
|
@ -269,3 +311,29 @@ void MWMechanics::NpcStats::setReputation(int reputation)
|
||||||
{
|
{
|
||||||
mReputation = reputation;
|
mReputation = reputation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int rank) const
|
||||||
|
{
|
||||||
|
if (rank<0 || rank>=10)
|
||||||
|
throw std::runtime_error ("rank index out of range");
|
||||||
|
|
||||||
|
const ESM::Faction& faction =
|
||||||
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);
|
||||||
|
|
||||||
|
std::vector<int> skills;
|
||||||
|
|
||||||
|
for (int i=0; i<6; ++i)
|
||||||
|
skills.push_back (static_cast<int> (getSkill (faction.mData.mSkillID[i]).getModified()));
|
||||||
|
|
||||||
|
std::sort (skills.begin(), skills.end());
|
||||||
|
|
||||||
|
std::vector<int>::const_reverse_iterator iter = skills.rbegin();
|
||||||
|
|
||||||
|
const ESM::RankData& rankData = faction.mData.mRankData[rank];
|
||||||
|
|
||||||
|
if (*iter<rankData.mSkill1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return *++iter>=rankData.mSkill2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,9 @@ namespace MWMechanics
|
||||||
unsigned int mMovementFlags;
|
unsigned int mMovementFlags;
|
||||||
Stat<float> mSkill[27];
|
Stat<float> mSkill[27];
|
||||||
int mBounty;
|
int mBounty;
|
||||||
|
std::set<std::string> mExpelled;
|
||||||
|
std::map<std::string, int> mFactionReputation;
|
||||||
|
bool mVampire;
|
||||||
int mReputation;
|
int mReputation;
|
||||||
|
|
||||||
int mLevelProgress; // 0-10
|
int mLevelProgress; // 0-10
|
||||||
|
@ -80,6 +83,11 @@ namespace MWMechanics
|
||||||
Stat<float>& getSkill (int index);
|
Stat<float>& getSkill (int index);
|
||||||
|
|
||||||
std::map<std::string, int>& getFactionRanks();
|
std::map<std::string, int>& getFactionRanks();
|
||||||
|
|
||||||
|
std::set<std::string>& getExpelled();
|
||||||
|
|
||||||
|
bool isSameFaction (const NpcStats& npcStats) const;
|
||||||
|
///< Do *this and \a npcStats share a faction?
|
||||||
|
|
||||||
const std::map<std::string, int>& getFactionRanks() const;
|
const std::map<std::string, int>& getFactionRanks() const;
|
||||||
|
|
||||||
|
@ -107,6 +115,16 @@ namespace MWMechanics
|
||||||
int getBounty() const;
|
int getBounty() const;
|
||||||
|
|
||||||
void setBounty (int bounty);
|
void setBounty (int bounty);
|
||||||
|
|
||||||
|
int getFactionReputation (const std::string& faction) const;
|
||||||
|
|
||||||
|
void setFactionReputation (const std::string& faction, int value);
|
||||||
|
|
||||||
|
bool isVampire() const;
|
||||||
|
|
||||||
|
void setVampire (bool set);
|
||||||
|
|
||||||
|
bool hasSkillsForRank (const std::string& factionId, int rank) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ namespace MWScript
|
||||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).setHello(value);
|
MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (0, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ namespace MWScript
|
||||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).setFight(value);
|
MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (1, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ namespace MWScript
|
||||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).setFlee(value);
|
MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (2, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ namespace MWScript
|
||||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).setAlarm(value);
|
MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (3, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,21 @@
|
||||||
#include "interpretercontext.hpp"
|
#include "interpretercontext.hpp"
|
||||||
#include "ref.hpp"
|
#include "ref.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string getDialogueActorFaction()
|
||||||
|
{
|
||||||
|
MWWorld::Ptr actor = MWBase::Environment::get().getDialogueManager()->getActor();
|
||||||
|
|
||||||
|
MWMechanics::NpcStats stats = MWWorld::Class::get (actor).getNpcStats (actor);
|
||||||
|
|
||||||
|
if (stats.getFactionRanks().empty())
|
||||||
|
throw std::runtime_error (
|
||||||
|
"failed to determine dialogue actors faction (because actor is factionless)");
|
||||||
|
|
||||||
|
return stats.getFactionRanks().begin()->first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
{
|
{
|
||||||
|
@ -450,7 +465,7 @@ namespace MWScript
|
||||||
|
|
||||||
if(arg0==0)
|
if(arg0==0)
|
||||||
{
|
{
|
||||||
factionID = MWBase::Environment::get().getDialogueManager()->getFaction();
|
factionID = getDialogueActorFaction();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -479,7 +494,7 @@ namespace MWScript
|
||||||
|
|
||||||
if(arg0==0)
|
if(arg0==0)
|
||||||
{
|
{
|
||||||
factionID = MWBase::Environment::get().getDialogueManager()->getFaction();
|
factionID = getDialogueActorFaction();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -512,7 +527,7 @@ namespace MWScript
|
||||||
|
|
||||||
if(arg0==0)
|
if(arg0==0)
|
||||||
{
|
{
|
||||||
factionID = MWBase::Environment::get().getDialogueManager()->getFaction();
|
factionID = getDialogueActorFaction();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,10 +32,10 @@ struct DialInfo
|
||||||
{
|
{
|
||||||
int mUnknown1;
|
int mUnknown1;
|
||||||
int mDisposition;
|
int mDisposition;
|
||||||
char mRank; // Rank of NPC
|
signed char mRank; // Rank of NPC
|
||||||
char mGender; // See Gender enum
|
signed char mGender; // See Gender enum
|
||||||
char mPCrank; // Player rank
|
signed char mPCrank; // Player rank
|
||||||
char mUnknown2;
|
signed char mUnknown2;
|
||||||
}; // 12 bytes
|
}; // 12 bytes
|
||||||
DATAstruct mData;
|
DATAstruct mData;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue