1 #include "cellextensions.hpp" 2 3 #include <limits> 4 5 #include "../mwworld/esmstore.hpp" 6 7 #include <components/compiler/opcodes.hpp> 8 9 #include <components/interpreter/interpreter.hpp> 10 #include <components/interpreter/runtime.hpp> 11 #include <components/interpreter/opcodes.hpp> 12 13 #include "../mwworld/actionteleport.hpp" 14 #include "../mwworld/cellstore.hpp" 15 #include "../mwbase/environment.hpp" 16 #include "../mwbase/statemanager.hpp" 17 #include "../mwbase/windowmanager.hpp" 18 #include "../mwbase/world.hpp" 19 20 #include "../mwmechanics/actorutil.hpp" 21 22 #include "interpretercontext.hpp" 23 24 namespace MWScript 25 { 26 namespace Cell 27 { 28 class OpCellChanged : public Interpreter::Opcode0 29 { 30 public: 31 execute(Interpreter::Runtime & runtime)32 void execute (Interpreter::Runtime& runtime) override 33 { 34 runtime.push (MWBase::Environment::get().getWorld()->hasCellChanged() ? 1 : 0); 35 } 36 }; 37 38 class OpTestCells : public Interpreter::Opcode0 39 { 40 public: 41 execute(Interpreter::Runtime & runtime)42 void execute (Interpreter::Runtime& runtime) override 43 { 44 if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame) 45 { 46 runtime.getContext().report("Use TestCells from the main menu, when there is no active game session."); 47 return; 48 } 49 50 bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode(); 51 if (wasConsole) 52 MWBase::Environment::get().getWindowManager()->toggleConsole(); 53 54 MWBase::Environment::get().getWorld()->testExteriorCells(); 55 56 if (wasConsole) 57 MWBase::Environment::get().getWindowManager()->toggleConsole(); 58 } 59 }; 60 61 class OpTestInteriorCells : public Interpreter::Opcode0 62 { 63 public: 64 execute(Interpreter::Runtime & runtime)65 void execute (Interpreter::Runtime& runtime) override 66 { 67 if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame) 68 { 69 runtime.getContext().report("Use TestInteriorCells from the main menu, when there is no active game session."); 70 return; 71 } 72 73 bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode(); 74 if (wasConsole) 75 MWBase::Environment::get().getWindowManager()->toggleConsole(); 76 77 MWBase::Environment::get().getWorld()->testInteriorCells(); 78 79 if (wasConsole) 80 MWBase::Environment::get().getWindowManager()->toggleConsole(); 81 } 82 }; 83 84 class OpCOC : public Interpreter::Opcode0 85 { 86 public: 87 execute(Interpreter::Runtime & runtime)88 void execute (Interpreter::Runtime& runtime) override 89 { 90 std::string cell = runtime.getStringLiteral (runtime[0].mInteger); 91 runtime.pop(); 92 93 ESM::Position pos; 94 MWBase::World *world = MWBase::Environment::get().getWorld(); 95 const MWWorld::Ptr playerPtr = world->getPlayerPtr(); 96 97 if (world->findExteriorPosition(cell, pos)) 98 { 99 MWWorld::ActionTeleport("", pos, false).execute(playerPtr); 100 world->adjustPosition(playerPtr, false); 101 } 102 else 103 { 104 // Change to interior even if findInteriorPosition() 105 // yields false. In this case position will be zero-point. 106 world->findInteriorPosition(cell, pos); 107 MWWorld::ActionTeleport(cell, pos, false).execute(playerPtr); 108 } 109 } 110 }; 111 112 class OpCOE : public Interpreter::Opcode0 113 { 114 public: 115 execute(Interpreter::Runtime & runtime)116 void execute (Interpreter::Runtime& runtime) override 117 { 118 Interpreter::Type_Integer x = runtime[0].mInteger; 119 runtime.pop(); 120 121 Interpreter::Type_Integer y = runtime[0].mInteger; 122 runtime.pop(); 123 124 ESM::Position pos; 125 MWBase::World *world = MWBase::Environment::get().getWorld(); 126 const MWWorld::Ptr playerPtr = world->getPlayerPtr(); 127 128 world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); 129 pos.pos[2] = 0; 130 131 pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; 132 133 MWWorld::ActionTeleport("", pos, false).execute(playerPtr); 134 world->adjustPosition(playerPtr, false); 135 } 136 }; 137 138 class OpGetInterior : public Interpreter::Opcode0 139 { 140 public: 141 execute(Interpreter::Runtime & runtime)142 void execute (Interpreter::Runtime& runtime) override 143 { 144 if (!MWMechanics::getPlayer().isInCell()) 145 { 146 runtime.push (0); 147 return; 148 } 149 150 bool interior = 151 !MWMechanics::getPlayer().getCell()->getCell()->isExterior(); 152 153 runtime.push (interior ? 1 : 0); 154 } 155 }; 156 157 class OpGetPCCell : public Interpreter::Opcode0 158 { 159 public: 160 execute(Interpreter::Runtime & runtime)161 void execute (Interpreter::Runtime& runtime) override 162 { 163 std::string name = runtime.getStringLiteral (runtime[0].mInteger); 164 runtime.pop(); 165 166 if (!MWMechanics::getPlayer().isInCell()) 167 { 168 runtime.push(0); 169 return; 170 } 171 const MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); 172 173 std::string current = MWBase::Environment::get().getWorld()->getCellName(cell); 174 Misc::StringUtils::lowerCaseInPlace(current); 175 176 bool match = current.length()>=name.length() && 177 current.substr (0, name.length())==name; 178 179 runtime.push (match ? 1 : 0); 180 } 181 }; 182 183 class OpGetWaterLevel : public Interpreter::Opcode0 184 { 185 public: 186 execute(Interpreter::Runtime & runtime)187 void execute (Interpreter::Runtime& runtime) override 188 { 189 if (!MWMechanics::getPlayer().isInCell()) 190 { 191 runtime.push(0.f); 192 return; 193 } 194 MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); 195 if (cell->isExterior()) 196 runtime.push(0.f); // vanilla oddity, return 0 even though water is actually at -1 197 else if (cell->getCell()->hasWater()) 198 runtime.push (cell->getWaterLevel()); 199 else 200 runtime.push (-std::numeric_limits<float>::max()); 201 } 202 }; 203 204 class OpSetWaterLevel : public Interpreter::Opcode0 205 { 206 public: 207 execute(Interpreter::Runtime & runtime)208 void execute (Interpreter::Runtime& runtime) override 209 { 210 Interpreter::Type_Float level = runtime[0].mFloat; 211 212 if (!MWMechanics::getPlayer().isInCell()) 213 { 214 return; 215 } 216 217 MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); 218 219 if (cell->getCell()->isExterior()) 220 throw std::runtime_error("Can't set water level in exterior cell"); 221 222 cell->setWaterLevel (level); 223 MWBase::Environment::get().getWorld()->setWaterHeight (cell->getWaterLevel()); 224 } 225 }; 226 227 class OpModWaterLevel : public Interpreter::Opcode0 228 { 229 public: 230 execute(Interpreter::Runtime & runtime)231 void execute (Interpreter::Runtime& runtime) override 232 { 233 Interpreter::Type_Float level = runtime[0].mFloat; 234 235 if (!MWMechanics::getPlayer().isInCell()) 236 { 237 return; 238 } 239 240 MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); 241 242 if (cell->getCell()->isExterior()) 243 throw std::runtime_error("Can't set water level in exterior cell"); 244 245 cell->setWaterLevel (cell->getWaterLevel()+level); 246 MWBase::Environment::get().getWorld()->setWaterHeight(cell->getWaterLevel()); 247 } 248 }; 249 250 installOpcodes(Interpreter::Interpreter & interpreter)251 void installOpcodes (Interpreter::Interpreter& interpreter) 252 { 253 interpreter.installSegment5 (Compiler::Cell::opcodeCellChanged, new OpCellChanged); 254 interpreter.installSegment5 (Compiler::Cell::opcodeTestCells, new OpTestCells); 255 interpreter.installSegment5 (Compiler::Cell::opcodeTestInteriorCells, new OpTestInteriorCells); 256 interpreter.installSegment5 (Compiler::Cell::opcodeCOC, new OpCOC); 257 interpreter.installSegment5 (Compiler::Cell::opcodeCOE, new OpCOE); 258 interpreter.installSegment5 (Compiler::Cell::opcodeGetInterior, new OpGetInterior); 259 interpreter.installSegment5 (Compiler::Cell::opcodeGetPCCell, new OpGetPCCell); 260 interpreter.installSegment5 (Compiler::Cell::opcodeGetWaterLevel, new OpGetWaterLevel); 261 interpreter.installSegment5 (Compiler::Cell::opcodeSetWaterLevel, new OpSetWaterLevel); 262 interpreter.installSegment5 (Compiler::Cell::opcodeModWaterLevel, new OpModWaterLevel); 263 } 264 } 265 } 266