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