1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-06 00:55:50 +00:00
OpenMW/apps/openmw/mwscript/cellextensions.cpp
elsid 83e60fef4e
Avoid using findCellPosition for coc command implementation
It breaks teleport to interior cells and in general is very fragile because
of using exception for common logic path. Remove the function since it's not
used anywhere else.
2023-04-09 13:39:29 +02:00

256 lines
9.2 KiB
C++

#include "cellextensions.hpp"
#include <limits>
#include "../mwworld/esmstore.hpp"
#include <components/compiler/opcodes.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/opcodes.hpp>
#include <components/interpreter/runtime.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/actionteleport.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/scene.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "interpretercontext.hpp"
namespace MWScript
{
namespace Cell
{
class OpCellChanged : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
runtime.push(MWBase::Environment::get().getWorldScene()->hasCellChanged() ? 1 : 0);
}
};
class OpTestCells : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame)
{
runtime.getContext().report(
"Use TestCells from the main menu, when there is no active game session.");
return;
}
bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
MWBase::Environment::get().getWorldScene()->testExteriorCells();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
}
};
class OpTestInteriorCells : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame)
{
runtime.getContext().report(
"Use TestInteriorCells from the main menu, when there is no active game session.");
return;
}
bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
MWBase::Environment::get().getWorldScene()->testInteriorCells();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
}
};
class OpCOC : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
std::string_view cell = runtime.getStringLiteral(runtime[0].mInteger);
runtime.pop();
ESM::Position pos;
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Ptr playerPtr = world->getPlayerPtr();
if (const ESM::RefId refId = world->findExteriorPosition(cell, pos); !refId.empty())
{
MWWorld::ActionTeleport(refId, pos, false).execute(playerPtr);
world->adjustPosition(playerPtr, false);
return;
}
if (const ESM::RefId refId = world->findInteriorPosition(cell, pos); !refId.empty())
{
MWWorld::ActionTeleport(refId, pos, false).execute(playerPtr);
return;
}
throw std::runtime_error("Cell " + std::string(cell) + " is not found");
}
};
class OpCOE : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
Interpreter::Type_Integer x = runtime[0].mInteger;
runtime.pop();
Interpreter::Type_Integer y = runtime[0].mInteger;
runtime.pop();
ESM::Position pos;
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Ptr playerPtr = world->getPlayerPtr();
world->indexToPosition(x, y, pos.pos[0], pos.pos[1], true);
pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
MWWorld::ActionTeleport(ESM::RefId::esm3ExteriorCell(x, y), pos, false).execute(playerPtr);
world->adjustPosition(playerPtr, false);
}
};
class OpGetInterior : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
if (!MWMechanics::getPlayer().isInCell())
{
runtime.push(0);
return;
}
bool interior = !MWMechanics::getPlayer().getCell()->getCell()->isExterior();
runtime.push(interior ? 1 : 0);
}
};
class OpGetPCCell : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
std::string_view name = runtime.getStringLiteral(runtime[0].mInteger);
runtime.pop();
if (!MWMechanics::getPlayer().isInCell())
{
runtime.push(0);
return;
}
const MWWorld::CellStore* cell = MWMechanics::getPlayer().getCell();
std::string_view current = MWBase::Environment::get().getWorld()->getCellName(cell);
bool match = Misc::StringUtils::ciCompareLen(name, current, name.length()) == 0;
runtime.push(match ? 1 : 0);
}
};
class OpGetWaterLevel : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
if (!MWMechanics::getPlayer().isInCell())
{
runtime.push(0.f);
return;
}
MWWorld::CellStore* cell = MWMechanics::getPlayer().getCell();
if (cell->isExterior())
runtime.push(0.f); // vanilla oddity, return 0 even though water is actually at -1
else if (cell->getCell()->hasWater())
runtime.push(cell->getWaterLevel());
else
runtime.push(-std::numeric_limits<float>::max());
}
};
class OpSetWaterLevel : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
Interpreter::Type_Float level = runtime[0].mFloat;
runtime.pop();
if (!MWMechanics::getPlayer().isInCell())
{
return;
}
MWWorld::CellStore* cell = MWMechanics::getPlayer().getCell();
if (cell->getCell()->isExterior())
throw std::runtime_error("Can't set water level in exterior cell");
cell->setWaterLevel(level);
MWBase::Environment::get().getWorld()->setWaterHeight(cell->getWaterLevel());
}
};
class OpModWaterLevel : public Interpreter::Opcode0
{
public:
void execute(Interpreter::Runtime& runtime) override
{
Interpreter::Type_Float level = runtime[0].mFloat;
runtime.pop();
if (!MWMechanics::getPlayer().isInCell())
{
return;
}
MWWorld::CellStore* cell = MWMechanics::getPlayer().getCell();
if (cell->getCell()->isExterior())
throw std::runtime_error("Can't set water level in exterior cell");
cell->setWaterLevel(cell->getWaterLevel() + level);
MWBase::Environment::get().getWorld()->setWaterHeight(cell->getWaterLevel());
}
};
void installOpcodes(Interpreter::Interpreter& interpreter)
{
interpreter.installSegment5<OpCellChanged>(Compiler::Cell::opcodeCellChanged);
interpreter.installSegment5<OpTestCells>(Compiler::Cell::opcodeTestCells);
interpreter.installSegment5<OpTestInteriorCells>(Compiler::Cell::opcodeTestInteriorCells);
interpreter.installSegment5<OpCOC>(Compiler::Cell::opcodeCOC);
interpreter.installSegment5<OpCOE>(Compiler::Cell::opcodeCOE);
interpreter.installSegment5<OpGetInterior>(Compiler::Cell::opcodeGetInterior);
interpreter.installSegment5<OpGetPCCell>(Compiler::Cell::opcodeGetPCCell);
interpreter.installSegment5<OpGetWaterLevel>(Compiler::Cell::opcodeGetWaterLevel);
interpreter.installSegment5<OpSetWaterLevel>(Compiler::Cell::opcodeSetWaterLevel);
interpreter.installSegment5<OpModWaterLevel>(Compiler::Cell::opcodeModWaterLevel);
}
}
}