1 #include "interpretercontext.hpp"
2 
3 #include <cmath>
4 #include <sstream>
5 
6 #include <components/compiler/locals.hpp>
7 
8 #include "../mwworld/esmstore.hpp"
9 
10 #include "../mwbase/environment.hpp"
11 #include "../mwbase/world.hpp"
12 #include "../mwbase/scriptmanager.hpp"
13 #include "../mwbase/windowmanager.hpp"
14 #include "../mwbase/inputmanager.hpp"
15 
16 #include "../mwworld/action.hpp"
17 #include "../mwworld/class.hpp"
18 #include "../mwworld/cellstore.hpp"
19 #include "../mwworld/containerstore.hpp"
20 
21 #include "../mwmechanics/npcstats.hpp"
22 
23 #include "locals.hpp"
24 #include "globalscripts.hpp"
25 
26 namespace MWScript
27 {
getReferenceImp(const std::string & id,bool activeOnly,bool doThrow) const28     const MWWorld::Ptr InterpreterContext::getReferenceImp (
29         const std::string& id, bool activeOnly, bool doThrow) const
30     {
31         if (!id.empty())
32         {
33             return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly);
34         }
35         else
36         {
37             if (mReference.isEmpty() && mGlobalScriptDesc)
38                 mReference = mGlobalScriptDesc->getPtr();
39 
40             if (mReference.isEmpty() && doThrow)
41                 throw MissingImplicitRefError();
42 
43             return mReference;
44         }
45     }
46 
getMemberLocals(std::string & id,bool global) const47     const Locals& InterpreterContext::getMemberLocals (std::string& id, bool global)
48         const
49     {
50         if (global)
51         {
52             return MWBase::Environment::get().getScriptManager()->getGlobalScripts().
53                 getLocals (id);
54         }
55         else
56         {
57             const MWWorld::Ptr ptr = getReferenceImp (id, false);
58 
59             id = ptr.getClass().getScript (ptr);
60 
61             ptr.getRefData().setLocals (
62                 *MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));
63 
64             return ptr.getRefData().getLocals();
65         }
66     }
67 
getMemberLocals(std::string & id,bool global)68     Locals& InterpreterContext::getMemberLocals (std::string& id, bool global)
69     {
70         if (global)
71         {
72             return MWBase::Environment::get().getScriptManager()->getGlobalScripts().
73                 getLocals (id);
74         }
75         else
76         {
77             const MWWorld::Ptr ptr = getReferenceImp (id, false);
78 
79             id = ptr.getClass().getScript (ptr);
80 
81             ptr.getRefData().setLocals (
82                 *MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));
83 
84             return ptr.getRefData().getLocals();
85         }
86     }
87 
MissingImplicitRefError()88     MissingImplicitRefError::MissingImplicitRefError() : std::runtime_error("no implicit reference") {}
89 
findLocalVariableIndex(const std::string & scriptId,const std::string & name,char type) const90     int InterpreterContext::findLocalVariableIndex (const std::string& scriptId,
91         const std::string& name, char type) const
92     {
93         int index = MWBase::Environment::get().getScriptManager()->getLocals (scriptId).
94             searchIndex (type, name);
95 
96         if (index!=-1)
97             return index;
98 
99         std::ostringstream stream;
100 
101         stream << "Failed to access ";
102 
103         switch (type)
104         {
105             case 's': stream << "short"; break;
106             case 'l': stream << "long"; break;
107             case 'f': stream << "float"; break;
108         }
109 
110         stream << " member variable " << name << " in script " << scriptId;
111 
112         throw std::runtime_error (stream.str().c_str());
113     }
114 
InterpreterContext(MWScript::Locals * locals,const MWWorld::Ptr & reference)115     InterpreterContext::InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference)
116     : mLocals (locals), mReference (reference)
117     {}
118 
InterpreterContext(std::shared_ptr<GlobalScriptDesc> globalScriptDesc)119     InterpreterContext::InterpreterContext (std::shared_ptr<GlobalScriptDesc> globalScriptDesc)
120     : mLocals (&(globalScriptDesc->mLocals))
121     {
122         const MWWorld::Ptr* ptr = globalScriptDesc->getPtrIfPresent();
123         // A nullptr here signifies that the script's target has not yet been resolved after loading the game.
124         // Script targets are lazily resolved to MWWorld::Ptrs (which can, upon resolution, be empty)
125         // because scripts started through dialogue often don't use their implicit target.
126         if (ptr)
127             mReference = *ptr;
128         else
129             mGlobalScriptDesc = globalScriptDesc;
130     }
131 
getLocalShort(int index) const132     int InterpreterContext::getLocalShort (int index) const
133     {
134         if (!mLocals)
135             throw std::runtime_error ("local variables not available in this context");
136 
137         return mLocals->mShorts.at (index);
138     }
139 
getLocalLong(int index) const140     int InterpreterContext::getLocalLong (int index) const
141     {
142         if (!mLocals)
143             throw std::runtime_error ("local variables not available in this context");
144 
145         return mLocals->mLongs.at (index);
146     }
147 
getLocalFloat(int index) const148     float InterpreterContext::getLocalFloat (int index) const
149     {
150         if (!mLocals)
151             throw std::runtime_error ("local variables not available in this context");
152 
153         return mLocals->mFloats.at (index);
154     }
155 
setLocalShort(int index,int value)156     void InterpreterContext::setLocalShort (int index, int value)
157     {
158         if (!mLocals)
159             throw std::runtime_error ("local variables not available in this context");
160 
161         mLocals->mShorts.at (index) = value;
162     }
163 
setLocalLong(int index,int value)164     void InterpreterContext::setLocalLong (int index, int value)
165     {
166         if (!mLocals)
167             throw std::runtime_error ("local variables not available in this context");
168 
169         mLocals->mLongs.at (index) = value;
170     }
171 
setLocalFloat(int index,float value)172     void InterpreterContext::setLocalFloat (int index, float value)
173     {
174         if (!mLocals)
175             throw std::runtime_error ("local variables not available in this context");
176 
177         mLocals->mFloats.at (index) = value;
178     }
179 
messageBox(const std::string & message,const std::vector<std::string> & buttons)180     void InterpreterContext::messageBox (const std::string& message,
181         const std::vector<std::string>& buttons)
182     {
183         if (buttons.empty())
184             MWBase::Environment::get().getWindowManager()->messageBox (message);
185         else
186             MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons);
187     }
188 
report(const std::string & message)189     void InterpreterContext::report (const std::string& message)
190     {
191     }
192 
getGlobalShort(const std::string & name) const193     int InterpreterContext::getGlobalShort (const std::string& name) const
194     {
195         return MWBase::Environment::get().getWorld()->getGlobalInt (name);
196     }
197 
getGlobalLong(const std::string & name) const198     int InterpreterContext::getGlobalLong (const std::string& name) const
199     {
200         // a global long is internally a float.
201         return MWBase::Environment::get().getWorld()->getGlobalInt (name);
202     }
203 
getGlobalFloat(const std::string & name) const204     float InterpreterContext::getGlobalFloat (const std::string& name) const
205     {
206         return MWBase::Environment::get().getWorld()->getGlobalFloat (name);
207     }
208 
setGlobalShort(const std::string & name,int value)209     void InterpreterContext::setGlobalShort (const std::string& name, int value)
210     {
211         MWBase::Environment::get().getWorld()->setGlobalInt (name, value);
212     }
213 
setGlobalLong(const std::string & name,int value)214     void InterpreterContext::setGlobalLong (const std::string& name, int value)
215     {
216         MWBase::Environment::get().getWorld()->setGlobalInt (name, value);
217     }
218 
setGlobalFloat(const std::string & name,float value)219     void InterpreterContext::setGlobalFloat (const std::string& name, float value)
220     {
221         MWBase::Environment::get().getWorld()->setGlobalFloat (name, value);
222     }
223 
getGlobals() const224     std::vector<std::string> InterpreterContext::getGlobals() const
225     {
226         const MWWorld::Store<ESM::Global>& globals =
227             MWBase::Environment::get().getWorld()->getStore().get<ESM::Global>();
228 
229         std::vector<std::string> ids;
230         for (auto& globalVariable : globals)
231         {
232             ids.emplace_back(globalVariable.mId);
233         }
234 
235         return ids;
236     }
237 
getGlobalType(const std::string & name) const238     char InterpreterContext::getGlobalType (const std::string& name) const
239     {
240         MWBase::World *world = MWBase::Environment::get().getWorld();
241         return world->getGlobalVariableType(name);
242     }
243 
getActionBinding(const std::string & targetAction) const244     std::string InterpreterContext::getActionBinding(const std::string& targetAction) const
245     {
246         MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
247         std::vector<int> actions = input->getActionKeySorting ();
248         for (const int action : actions)
249         {
250             std::string desc = input->getActionDescription (action);
251             if(desc == "")
252                 continue;
253 
254             if(desc == targetAction)
255             {
256                 if(input->joystickLastUsed())
257                     return input->getActionControllerBindingName(action);
258                 else
259                     return input->getActionKeyBindingName(action);
260             }
261         }
262 
263         return "None";
264     }
265 
getActorName() const266     std::string InterpreterContext::getActorName() const
267     {
268         const MWWorld::Ptr& ptr = getReferenceImp();
269         if (ptr.getClass().isNpc())
270         {
271             const ESM::NPC* npc = ptr.get<ESM::NPC>()->mBase;
272             return npc->mName;
273         }
274 
275         const ESM::Creature* creature = ptr.get<ESM::Creature>()->mBase;
276         return creature->mName;
277     }
278 
getNPCRace() const279     std::string InterpreterContext::getNPCRace() const
280     {
281         ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;
282         const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc.mRace);
283         return race->mName;
284     }
285 
getNPCClass() const286     std::string InterpreterContext::getNPCClass() const
287     {
288         ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;
289         const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc.mClass);
290         return class_->mName;
291     }
292 
getNPCFaction() const293     std::string InterpreterContext::getNPCFaction() const
294     {
295         ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;
296         const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npc.mFaction);
297         return faction->mName;
298     }
299 
getNPCRank() const300     std::string InterpreterContext::getNPCRank() const
301     {
302         const MWWorld::Ptr& ptr = getReferenceImp();
303         std::string faction = ptr.getClass().getPrimaryFaction(ptr);
304         if (faction.empty())
305             throw std::runtime_error("getNPCRank(): NPC is not in a faction");
306 
307         int rank = ptr.getClass().getPrimaryFactionRank(ptr);
308         if (rank < 0 || rank > 9)
309             throw std::runtime_error("getNPCRank(): invalid rank");
310 
311         MWBase::World *world = MWBase::Environment::get().getWorld();
312         const MWWorld::ESMStore &store = world->getStore();
313         const ESM::Faction *fact = store.get<ESM::Faction>().find(faction);
314         return fact->mRanks[rank];
315     }
316 
getPCName() const317     std::string InterpreterContext::getPCName() const
318     {
319         MWBase::World *world = MWBase::Environment::get().getWorld();
320         ESM::NPC player = *world->getPlayerPtr().get<ESM::NPC>()->mBase;
321         return player.mName;
322     }
323 
getPCRace() const324     std::string InterpreterContext::getPCRace() const
325     {
326         MWBase::World *world = MWBase::Environment::get().getWorld();
327         std::string race = world->getPlayerPtr().get<ESM::NPC>()->mBase->mRace;
328         return world->getStore().get<ESM::Race>().find(race)->mName;
329     }
330 
getPCClass() const331     std::string InterpreterContext::getPCClass() const
332     {
333         MWBase::World *world = MWBase::Environment::get().getWorld();
334         std::string class_ = world->getPlayerPtr().get<ESM::NPC>()->mBase->mClass;
335         return world->getStore().get<ESM::Class>().find(class_)->mName;
336     }
337 
getPCRank() const338     std::string InterpreterContext::getPCRank() const
339     {
340         MWBase::World *world = MWBase::Environment::get().getWorld();
341         MWWorld::Ptr player = world->getPlayerPtr();
342 
343         std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
344         if (factionId.empty())
345             throw std::runtime_error("getPCRank(): NPC is not in a faction");
346 
347         const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
348         std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
349         int rank = -1;
350         if (it != ranks.end())
351             rank = it->second;
352 
353         // If you are not in the faction, PcRank returns the first rank, for whatever reason.
354         // This is used by the dialogue when joining the Thieves Guild in Balmora.
355         if (rank == -1)
356             rank = 0;
357 
358         const MWWorld::ESMStore &store = world->getStore();
359         const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);
360 
361         if(rank < 0 || rank > 9) // there are only 10 ranks
362             return "";
363 
364         return faction->mRanks[rank];
365     }
366 
getPCNextRank() const367     std::string InterpreterContext::getPCNextRank() const
368     {
369         MWBase::World *world = MWBase::Environment::get().getWorld();
370         MWWorld::Ptr player = world->getPlayerPtr();
371 
372         std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
373         if (factionId.empty())
374             throw std::runtime_error("getPCNextRank(): NPC is not in a faction");
375 
376         const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
377         std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
378         int rank = -1;
379         if (it != ranks.end())
380             rank = it->second;
381 
382         ++rank; // Next rank
383 
384         // if we are already at max rank, there is no next rank
385         if (rank > 9)
386             rank = 9;
387 
388         const MWWorld::ESMStore &store = world->getStore();
389         const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);
390 
391         if(rank < 0)
392             return "";
393 
394         return faction->mRanks[rank];
395     }
396 
getPCBounty() const397     int InterpreterContext::getPCBounty() const
398     {
399         MWBase::World *world = MWBase::Environment::get().getWorld();
400         MWWorld::Ptr player = world->getPlayerPtr();
401         return player.getClass().getNpcStats (player).getBounty();
402     }
403 
getCurrentCellName() const404     std::string InterpreterContext::getCurrentCellName() const
405     {
406         return  MWBase::Environment::get().getWorld()->getCellName();
407     }
408 
executeActivation(MWWorld::Ptr ptr,MWWorld::Ptr actor)409     void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor)
410     {
411         std::shared_ptr<MWWorld::Action> action = (ptr.getClass().activate(ptr, actor));
412         action->execute (actor);
413         if (action->getTarget() != MWWorld::Ptr() && action->getTarget() != ptr)
414         {
415             updatePtr(ptr, action->getTarget());
416         }
417     }
418 
getMemberShort(const std::string & id,const std::string & name,bool global) const419     int InterpreterContext::getMemberShort (const std::string& id, const std::string& name,
420         bool global) const
421     {
422         std::string scriptId (id);
423 
424         const Locals& locals = getMemberLocals (scriptId, global);
425 
426         return locals.mShorts[findLocalVariableIndex (scriptId, name, 's')];
427     }
428 
getMemberLong(const std::string & id,const std::string & name,bool global) const429     int InterpreterContext::getMemberLong (const std::string& id, const std::string& name,
430         bool global) const
431     {
432         std::string scriptId (id);
433 
434         const Locals& locals = getMemberLocals (scriptId, global);
435 
436         return locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')];
437     }
438 
getMemberFloat(const std::string & id,const std::string & name,bool global) const439     float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name,
440         bool global) const
441     {
442         std::string scriptId (id);
443 
444         const Locals& locals = getMemberLocals (scriptId, global);
445 
446         return locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')];
447     }
448 
setMemberShort(const std::string & id,const std::string & name,int value,bool global)449     void InterpreterContext::setMemberShort (const std::string& id, const std::string& name,
450         int value, bool global)
451     {
452         std::string scriptId (id);
453 
454         Locals& locals = getMemberLocals (scriptId, global);
455 
456         locals.mShorts[findLocalVariableIndex (scriptId, name, 's')] = value;
457     }
458 
setMemberLong(const std::string & id,const std::string & name,int value,bool global)459     void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global)
460     {
461         std::string scriptId (id);
462 
463         Locals& locals = getMemberLocals (scriptId, global);
464 
465         locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')] = value;
466     }
467 
setMemberFloat(const std::string & id,const std::string & name,float value,bool global)468     void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global)
469     {
470         std::string scriptId (id);
471 
472         Locals& locals = getMemberLocals (scriptId, global);
473 
474         locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')] = value;
475     }
476 
getReference(bool required)477     MWWorld::Ptr InterpreterContext::getReference(bool required)
478     {
479         return getReferenceImp ("", true, required);
480     }
481 
updatePtr(const MWWorld::Ptr & base,const MWWorld::Ptr & updated)482     void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)
483     {
484         if (!mReference.isEmpty() && base == mReference)
485         {
486             mReference = updated;
487             if (mLocals == &base.getRefData().getLocals())
488                 mLocals = &mReference.getRefData().getLocals();
489         }
490     }
491 }
492