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