Change terminology of gameSecond/gameHour to simulationTime/gameTime

psi29a-master-patch-54550
Petr Mikheev 2 years ago
parent 8ee8f81619
commit 2d1b100239

@ -19,36 +19,36 @@ namespace MWLua
sol::function getAsyncPackageInitializer(const Context& context)
{
using TimeUnit = LuaUtil::ScriptsContainer::TimeUnit;
using TimerType = LuaUtil::ScriptsContainer::TimerType;
sol::usertype<AsyncPackageId> api = context.mLua->sol().new_usertype<AsyncPackageId>("AsyncPackage");
api["registerTimerCallback"] = [](const AsyncPackageId& asyncId, std::string_view name, sol::function callback)
{
asyncId.mContainer->registerTimerCallback(asyncId.mScriptId, name, std::move(callback));
return TimerCallback{asyncId, std::string(name)};
};
api["newTimerInSeconds"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
const TimerCallback& callback, sol::object callbackArg)
api["newSimulationTimer"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
const TimerCallback& callback, sol::object callbackArg)
{
callback.mAsyncId.mContainer->setupSerializableTimer(
TimeUnit::SECONDS, world->getGameTimeInSeconds() + delay,
TimerType::SIMULATION_TIME, world->getSimulationTime() + delay,
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
};
api["newTimerInHours"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
const TimerCallback& callback, sol::object callbackArg)
api["newGameTimer"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
const TimerCallback& callback, sol::object callbackArg)
{
callback.mAsyncId.mContainer->setupSerializableTimer(
TimeUnit::HOURS, world->getGameTimeInHours() + delay,
TimerType::GAME_TIME, world->getGameTime() + delay,
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
};
api["newUnsavableTimerInSeconds"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
api["newUnsavableSimulationTimer"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
{
asyncId.mContainer->setupUnsavableTimer(
TimeUnit::SECONDS, world->getGameTimeInSeconds() + delay, asyncId.mScriptId, std::move(callback));
TimerType::SIMULATION_TIME, world->getSimulationTime() + delay, asyncId.mScriptId, std::move(callback));
};
api["newUnsavableTimerInHours"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
api["newUnsavableGameTimer"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
{
asyncId.mContainer->setupUnsavableTimer(
TimeUnit::HOURS, world->getGameTimeInHours() + delay, asyncId.mScriptId, std::move(callback));
TimerType::GAME_TIME, world->getGameTime() + delay, asyncId.mScriptId, std::move(callback));
};
api["callback"] = [](const AsyncPackageId& asyncId, sol::function fn)
{

@ -22,11 +22,32 @@ namespace MWLua
return LuaUtil::makeReadOnly(res);
}
static void addTimeBindings(sol::table& api, const Context& context, bool global)
{
api["getSimulationTime"] = [world=context.mWorldView]() { return world->getSimulationTime(); };
api["getSimulationTimeScale"] = [world=context.mWorldView]() { return world->getSimulationTimeScale(); };
api["getGameTime"] = [world=context.mWorldView]() { return world->getGameTime(); };
api["getGameTimeScale"] = [world=context.mWorldView]() { return world->getGameTimeScale(); };
api["isWorldPaused"] = [world=context.mWorldView]() { return world->isPaused(); };
if (!global)
return;
api["setGameTimeScale"] = [world=context.mWorldView](double scale) { world->setGameTimeScale(scale); };
// TODO: Ability to make game time slower or faster than real time (needed for example for mechanics like VATS)
// api["setSimulationTimeScale"] = [](double scale) {};
// TODO: Ability to pause/resume world from Lua (needed for UI dehardcoding)
// api["pause"] = []() {};
// api["resume"] = []() {};
}
sol::table initCorePackage(const Context& context)
{
auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 12;
api["API_REVISION"] = 13;
api["quit"] = [lua]()
{
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
@ -36,9 +57,7 @@ namespace MWLua
{
context.mGlobalEventQueue->push_back({std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
};
api["getGameTimeInSeconds"] = [world=context.mWorldView]() { return world->getGameTimeInSeconds(); };
api["getGameTimeInHours"] = [world=context.mWorldView]() { return world->getGameTimeInHours(); };
api["isWorldPaused"] = [world=context.mWorldView]() { return world->isPaused(); };
addTimeBindings(api, context, false);
api["OBJECT_TYPE"] = definitionList(*lua,
{
"Activator", "Armor", "Book", "Clothing", "Creature", "Door", "Ingredient",
@ -73,6 +92,7 @@ namespace MWLua
{
sol::table api(context.mLua->sol(), sol::create);
WorldView* worldView = context.mWorldView;
addTimeBindings(api, context, true);
api["getCellByName"] = [worldView=context.mWorldView](const std::string& name) -> sol::optional<GCell>
{
MWWorld::CellStore* cell = worldView->findNamedCell(name);

@ -113,13 +113,13 @@ namespace MWLua
if (!mWorldView.isPaused())
{ // Update time and process timers
double seconds = mWorldView.getGameTimeInSeconds() + frameDuration;
mWorldView.setGameTimeInSeconds(seconds);
double hours = mWorldView.getGameTimeInHours();
double simulationTime = mWorldView.getSimulationTime() + frameDuration;
mWorldView.setSimulationTime(simulationTime);
double gameTime = mWorldView.getGameTime();
mGlobalScripts.processTimers(seconds, hours);
mGlobalScripts.processTimers(simulationTime, gameTime);
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->processTimers(seconds, hours);
scripts->processTimers(simulationTime, gameTime);
}
// Receive events

@ -70,16 +70,16 @@ namespace MWLua
removeFromGroup(*group, ptr);
}
double WorldView::getGameTimeInHours() const
double WorldView::getGameTime() const
{
MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::TimeStamp timeStamp = world->getTimeStamp();
return static_cast<double>(timeStamp.getDay()) * 24 + timeStamp.getHour();
return (static_cast<double>(timeStamp.getDay()) * 24 + timeStamp.getHour()) * 3600.0;
}
void WorldView::load(ESM::ESMReader& esm)
{
esm.getHNT(mGameSeconds, "LUAW");
esm.getHNT(mSimulationTime, "LUAW");
ObjectId lastAssignedId;
lastAssignedId.load(esm, true);
mObjectRegistry.setLastAssignedId(lastAssignedId);
@ -87,7 +87,7 @@ namespace MWLua
void WorldView::save(ESM::ESMWriter& esm) const
{
esm.writeHNT("LUAW", mGameSeconds);
esm.writeHNT("LUAW", mSimulationTime);
mObjectRegistry.getLastAssignedId().save(esm, true);
}

@ -22,13 +22,16 @@ namespace MWLua
// Whether the world is paused (i.e. game time is not changing and actors don't move).
bool isPaused() const { return mPaused; }
// Returns the number of seconds passed from the beginning of the game.
double getGameTimeInSeconds() const { return mGameSeconds; }
void setGameTimeInSeconds(double t) { mGameSeconds = t; }
// The number of seconds passed from the beginning of the game.
double getSimulationTime() const { return mSimulationTime; }
void setSimulationTime(double t) { mSimulationTime = t; }
double getSimulationTimeScale() const { return 1.0; }
// Returns the number of game hours passed from the beginning of the game.
// Note that the number of seconds in a game hour is not fixed.
double getGameTimeInHours() const;
// The game time (in game seconds) passed from the beginning of the game.
// Note that game time generally goes faster than the simulation time.
double getGameTime() const;
double getGameTimeScale() const { return MWBase::Environment::get().getWorld()->getTimeScaleFactor(); }
void setGameTimeScale(double s) { MWBase::Environment::get().getWorld()->setGlobalFloat("timescale", s); }
ObjectIdList getActivatorsInScene() const { return mActivatorsInScene.mList; }
ObjectIdList getActorsInScene() const { return mActorsInScene.mList; }
@ -76,7 +79,7 @@ namespace MWLua
ObjectGroup mDoorsInScene;
ObjectGroup mItemsInScene;
double mGameSeconds = 0;
double mSimulationTime = 0;
bool mPaused = false;
};

@ -364,7 +364,7 @@ return {
TEST_F(LuaScriptsContainerTest, Timers)
{
using TimeUnit = LuaUtil::ScriptsContainer::TimeUnit;
using TimerType = LuaUtil::ScriptsContainer::TimerType;
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
int test1Id = *mCfg.findId("test1.lua");
int test2Id = *mCfg.findId("test2.lua");
@ -387,18 +387,18 @@ return {
scripts.processTimers(1, 2);
scripts.setupSerializableTimer(TimeUnit::SECONDS, 10, test1Id, "B", sol::make_object(mLua.sol(), 3));
scripts.setupSerializableTimer(TimeUnit::HOURS, 10, test2Id, "B", sol::make_object(mLua.sol(), 4));
scripts.setupSerializableTimer(TimeUnit::SECONDS, 5, test1Id, "A", sol::make_object(mLua.sol(), 1));
scripts.setupSerializableTimer(TimeUnit::HOURS, 5, test2Id, "A", sol::make_object(mLua.sol(), 2));
scripts.setupSerializableTimer(TimeUnit::SECONDS, 15, test1Id, "A", sol::make_object(mLua.sol(), 10));
scripts.setupSerializableTimer(TimeUnit::SECONDS, 15, test1Id, "B", sol::make_object(mLua.sol(), 20));
scripts.setupUnsavableTimer(TimeUnit::SECONDS, 10, test2Id, fn2);
scripts.setupUnsavableTimer(TimeUnit::HOURS, 10, test1Id, fn2);
scripts.setupUnsavableTimer(TimeUnit::SECONDS, 5, test2Id, fn1);
scripts.setupUnsavableTimer(TimeUnit::HOURS, 5, test1Id, fn1);
scripts.setupUnsavableTimer(TimeUnit::SECONDS, 15, test2Id, fn1);
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 10, test1Id, "B", sol::make_object(mLua.sol(), 3));
scripts.setupSerializableTimer(TimerType::GAME_TIME, 10, test2Id, "B", sol::make_object(mLua.sol(), 4));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 5, test1Id, "A", sol::make_object(mLua.sol(), 1));
scripts.setupSerializableTimer(TimerType::GAME_TIME, 5, test2Id, "A", sol::make_object(mLua.sol(), 2));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 15, test1Id, "A", sol::make_object(mLua.sol(), 10));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 15, test1Id, "B", sol::make_object(mLua.sol(), 20));
scripts.setupUnsavableTimer(TimerType::SIMULATION_TIME, 10, test2Id, fn2);
scripts.setupUnsavableTimer(TimerType::GAME_TIME, 10, test1Id, fn2);
scripts.setupUnsavableTimer(TimerType::SIMULATION_TIME, 5, test2Id, fn1);
scripts.setupUnsavableTimer(TimerType::GAME_TIME, 5, test1Id, fn1);
scripts.setupUnsavableTimer(TimerType::SIMULATION_TIME, 15, test2Id, fn1);
EXPECT_EQ(counter1, 0);
EXPECT_EQ(counter3, 0);

@ -72,7 +72,7 @@ void ESM::LuaScripts::load(ESMReader& esm)
{
esm.getSubHeader();
LuaTimer timer;
esm.getT(timer.mUnit);
esm.getT(timer.mType);
esm.getT(timer.mTime);
timer.mCallbackName = esm.getHNString("LUAC");
timer.mCallbackArgument = loadLuaBinaryData(esm);
@ -91,7 +91,7 @@ void ESM::LuaScripts::save(ESMWriter& esm) const
for (const LuaTimer& timer : script.mTimers)
{
esm.startSubRecord("LUAT");
esm.writeT(timer.mUnit);
esm.writeT(timer.mType);
esm.writeT(timer.mTime);
esm.endRecord("LUAT");
esm.writeHNString("LUAC", timer.mCallbackName);

@ -51,13 +51,13 @@ namespace ESM
struct LuaTimer
{
enum class TimeUnit : bool
enum class Type : bool
{
SECONDS = 0,
HOURS = 1,
SIMULATION_TIME = 0,
GAME_TIME = 1,
};
TimeUnit mUnit;
Type mType;
double mTime;
std::string mCallbackName;
std::string mCallbackArgument; // Serialized Lua table. It is a binary data. Can contain '\0'.

@ -52,7 +52,7 @@ namespace LuaUtil
LuaState::LuaState(const VFS::Manager* vfs, const ScriptsConfiguration* conf) : mConf(conf), mVFS(vfs)
{
mLua.open_libraries(sol::lib::base, sol::lib::coroutine, sol::lib::math,
sol::lib::string, sol::lib::table, sol::lib::debug);
sol::lib::string, sol::lib::table, sol::lib::os, sol::lib::debug);
mLua["math"]["randomseed"](static_cast<unsigned>(std::time(nullptr)));
mLua["math"]["randomseed"] = []{};
@ -85,6 +85,11 @@ namespace LuaUtil
if (mLua[s] == sol::nil) throw std::logic_error("Lua package not found: " + s);
mCommonPackages[s] = mSandboxEnv[s] = makeReadOnly(mLua[s]);
}
mCommonPackages["os"] = mSandboxEnv["os"] = makeReadOnly(tableFromPairs<std::string_view, sol::function>({
{"date", mLua["os"]["date"]},
{"difftime", mLua["os"]["difftime"]},
{"time", mLua["os"]["time"]}
}));
}
LuaState::~LuaState()

@ -309,21 +309,21 @@ namespace LuaUtil
void ScriptsContainer::save(ESM::LuaScripts& data)
{
std::map<int, std::vector<ESM::LuaTimer>> timers;
auto saveTimerFn = [&](const Timer& timer, TimeUnit timeUnit)
auto saveTimerFn = [&](const Timer& timer, TimerType timerType)
{
if (!timer.mSerializable)
return;
ESM::LuaTimer savedTimer;
savedTimer.mTime = timer.mTime;
savedTimer.mUnit = timeUnit;
savedTimer.mType = timerType;
savedTimer.mCallbackName = std::get<std::string>(timer.mCallback);
savedTimer.mCallbackArgument = timer.mSerializedArg;
timers[timer.mScriptId].push_back(std::move(savedTimer));
};
for (const Timer& timer : mSecondsTimersQueue)
saveTimerFn(timer, TimeUnit::SECONDS);
for (const Timer& timer : mHoursTimersQueue)
saveTimerFn(timer, TimeUnit::HOURS);
for (const Timer& timer : mSimulationTimersQueue)
saveTimerFn(timer, TimerType::SIMULATION_TIME);
for (const Timer& timer : mGameTimersQueue)
saveTimerFn(timer, TimerType::GAME_TIME);
data.mScripts.clear();
for (auto& [scriptId, script] : mScripts)
{
@ -408,17 +408,17 @@ namespace LuaUtil
// updates refnums, so timer.mSerializedArg may be not equal to savedTimer.mCallbackArgument.
timer.mSerializedArg = serialize(timer.mArg, mSerializer);
if (savedTimer.mUnit == TimeUnit::HOURS)
mHoursTimersQueue.push_back(std::move(timer));
if (savedTimer.mType == TimerType::GAME_TIME)
mGameTimersQueue.push_back(std::move(timer));
else
mSecondsTimersQueue.push_back(std::move(timer));
mSimulationTimersQueue.push_back(std::move(timer));
}
catch (std::exception& e) { printError(scriptId, "can not load timer", e); }
}
}
std::make_heap(mSecondsTimersQueue.begin(), mSecondsTimersQueue.end());
std::make_heap(mHoursTimersQueue.begin(), mHoursTimersQueue.end());
std::make_heap(mSimulationTimersQueue.begin(), mSimulationTimersQueue.end());
std::make_heap(mGameTimersQueue.begin(), mGameTimersQueue.end());
}
ScriptsContainer::~ScriptsContainer()
@ -437,8 +437,8 @@ namespace LuaUtil
for (auto& [_, handlers] : mEngineHandlers)
handlers->mList.clear();
mEventHandlers.clear();
mSecondsTimersQueue.clear();
mHoursTimersQueue.clear();
mSimulationTimersQueue.clear();
mGameTimersQueue.clear();
mPublicInterfaces.clear();
// Assigned by LuaUtil::makeReadOnly, but `clear` removes it, so we need to assign it again.
@ -464,7 +464,7 @@ namespace LuaUtil
std::push_heap(timerQueue.begin(), timerQueue.end());
}
void ScriptsContainer::setupSerializableTimer(TimeUnit timeUnit, double time, int scriptId,
void ScriptsContainer::setupSerializableTimer(TimerType type, double time, int scriptId,
std::string_view callbackName, sol::object callbackArg)
{
Timer t;
@ -474,10 +474,10 @@ namespace LuaUtil
t.mTime = time;
t.mArg = callbackArg;
t.mSerializedArg = serialize(t.mArg, mSerializer);
insertTimer(timeUnit == TimeUnit::HOURS ? mHoursTimersQueue : mSecondsTimersQueue, std::move(t));
insertTimer(type == TimerType::GAME_TIME ? mGameTimersQueue : mSimulationTimersQueue, std::move(t));
}
void ScriptsContainer::setupUnsavableTimer(TimeUnit timeUnit, double time, int scriptId, sol::function callback)
void ScriptsContainer::setupUnsavableTimer(TimerType type, double time, int scriptId, sol::function callback)
{
Timer t;
t.mScriptId = scriptId;
@ -488,7 +488,7 @@ namespace LuaUtil
getScript(t.mScriptId).mTemporaryCallbacks.emplace(mTemporaryCallbackCounter, std::move(callback));
mTemporaryCallbackCounter++;
insertTimer(timeUnit == TimeUnit::HOURS ? mHoursTimersQueue : mSecondsTimersQueue, std::move(t));
insertTimer(type == TimerType::GAME_TIME ? mGameTimersQueue : mSimulationTimersQueue, std::move(t));
}
void ScriptsContainer::callTimer(const Timer& t)
@ -524,10 +524,10 @@ namespace LuaUtil
}
}
void ScriptsContainer::processTimers(double gameSeconds, double gameHours)
void ScriptsContainer::processTimers(double simulationTime, double gameTime)
{
updateTimerQueue(mSecondsTimersQueue, gameSeconds);
updateTimerQueue(mHoursTimersQueue, gameHours);
updateTimerQueue(mSimulationTimersQueue, simulationTime);
updateTimerQueue(mGameTimersQueue, gameTime);
}
}

@ -73,7 +73,7 @@ namespace LuaUtil
ScriptsContainer* mContainer;
int mIndex; // index in LuaUtil::ScriptsConfiguration
};
using TimeUnit = ESM::LuaTimer::TimeUnit;
using TimerType = ESM::LuaTimer::Type;
// `namePrefix` is a common prefix for all scripts in the container. Used in logs for error messages and `print` output.
// `autoStartMode` specifies the list of scripts that should be autostarted in this container; the list itself is
@ -99,8 +99,7 @@ namespace LuaUtil
bool hasScript(int scriptId) const { return mScripts.count(scriptId) != 0; }
void removeScript(int scriptId);
// Processes timers. gameSeconds and gameHours are time (in seconds and in game hours) passed from the game start.
void processTimers(double gameSeconds, double gameHours);
void processTimers(double simulationTime, double gameTime);
// Calls `onUpdate` (if present) for every script in the container.
// Handlers are called in the same order as scripts were added.
@ -134,17 +133,17 @@ namespace LuaUtil
void registerTimerCallback(int scriptId, std::string_view callbackName, sol::function callback);
// Sets up a timer, that can be automatically saved and loaded.
// timeUnit - game seconds (TimeUnit::Seconds) or game hours (TimeUnit::Hours).
// type - the type of timer, either SIMULATION_TIME or GAME_TIME.
// time - the absolute game time (in seconds or in hours) when the timer should be executed.
// scriptPath - script path in VFS is used as script id. The script with the given path should already present in the container.
// callbackName - callback (should be registered in advance) for this timer.
// callbackArg - parameter for the callback (should be serializable).
void setupSerializableTimer(TimeUnit timeUnit, double time, int scriptId,
void setupSerializableTimer(TimerType type, double time, int scriptId,
std::string_view callbackName, sol::object callbackArg);
// Creates a timer. `callback` is an arbitrary Lua function. This type of timers is called "unsavable"
// because it can not be stored in saves. I.e. loading a saved game will not fully restore the state.
void setupUnsavableTimer(TimeUnit timeUnit, double time, int scriptId, sol::function callback);
// Creates a timer. `callback` is an arbitrary Lua function. These timers are called "unsavable"
// because they can not be stored in saves. I.e. loading a saved game will not fully restore the state.
void setupUnsavableTimer(TimerType type, double time, int scriptId, sol::function callback);
protected:
struct Handler
@ -237,8 +236,8 @@ namespace LuaUtil
std::map<std::string_view, EngineHandlerList*> mEngineHandlers;
std::map<std::string, EventHandlerList, std::less<>> mEventHandlers;
std::vector<Timer> mSecondsTimersQueue;
std::vector<Timer> mHoursTimersQueue;
std::vector<Timer> mSimulationTimersQueue;
std::vector<Timer> mGameTimersQueue;
int64_t mTemporaryCallbackCounter = 0;
};

@ -19,6 +19,7 @@ Lua API reference
openmw_ui
openmw_camera
openmw_aux_util
openmw_aux_time
interface_camera
@ -75,6 +76,8 @@ Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can overrid
+=========================================================+====================+===============================================================+
|:ref:`openmw_aux.util <Package openmw_aux.util>` | everywhere | | Miscellaneous utils |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw_aux.time <Package openmw_aux.time>` | everywhere | | Timers and game time utils |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
**Interfaces of built-in scripts**

@ -0,0 +1,5 @@
Package openmw_aux.time
=======================
.. raw:: html
:file: generated_html/openmw_aux_time.html

@ -15,10 +15,11 @@ Here are starting points for learning Lua:
Each script works in a separate sandbox and doesn't have any access to the underlying operating system.
Only a limited list of allowed standard libraries can be used:
`coroutine <https://www.lua.org/manual/5.1/manual.html#5.2>`__,
`math <https://www.lua.org/manual/5.1/manual.html#5.6>`__,
`math <https://www.lua.org/manual/5.1/manual.html#5.6>`__ (except `math.randomseed` -- it is called by the engine on startup and not available from scripts),
`string <https://www.lua.org/manual/5.1/manual.html#5.4>`__,
`table <https://www.lua.org/manual/5.1/manual.html#5.5>`__.
These libraries are loaded automatically and are always available (except the function `math.randomseed` -- it is called by the engine on startup and not available from scripts).
`table <https://www.lua.org/manual/5.1/manual.html#5.5>`__,
`os <https://www.lua.org/manual/5.1/manual.html#5.8>`__ (only `os.date`, `os.difftime`, `os.time`).
These libraries are loaded automatically and are always available.
Allowed `basic functions <https://www.lua.org/manual/5.1/manual.html#5.1>`__:
``assert``, ``error``, ``ipairs``, ``next``, ``pairs``, ``pcall``, ``print``, ``select``, ``tonumber``, ``tostring``, ``type``, ``unpack``, ``xpcall``, ``rawequal``, ``rawget``, ``rawset``, ``getmetatable``, ``setmetatable``.
@ -366,6 +367,8 @@ Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can overrid
+=========================================================+====================+===============================================================+
|:ref:`openmw_aux.util <Package openmw_aux.util>` | everywhere | | Miscellaneous utils |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw_aux.time <Package openmw_aux.time>` | everywhere | | Timers and game time utils |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
They can be loaded with ``require`` the same as API packages. For example:

@ -0,0 +1,104 @@
---
-- `openmw_aux.time` defines utility functions for timers.
-- Implementation can be found in `resources/vfs/openmw_aux/time.lua`.
-- @module time
-- @usage local time = require('openmw_aux.time')
local time = {
second = 1,
minute = 60,
hour = 3600,
day = 3600 * 24,
GameTime = 'GameTime',
SimulationTime = 'SimulationTime',
}
---
-- Alias of async:registerTimerCallback ; register a function as a timer callback.
-- @function [parent=#time] registerTimerCallback
-- @param #string name
-- @param #function func
-- @return openmw.async#TimerCallback
function time.registerTimerCallback(name, fn)
local async = require('openmw.async')
return async:registerTimerCallback(name, fn)
end
---
-- Alias of async:newSimulationTimer ; call callback(arg) in `delay` game seconds.
-- Callback must be registered in advance.
-- @function [parent=#time] newGameTimer
-- @param #number delay
-- @param openmw.async#TimerCallback callback A callback returned by `registerTimerCallback`
-- @param arg An argument for `callback`; can be `nil`.
function time.newGameTimer(delay, callback, callbackArg)
local async = require('openmw.async')
return async:newGameTimer(delay, callback, callbackArg)
end
---
-- Alias of async:newSimulationTimer ; call callback(arg) in `delay` simulation seconds.
-- Callback must be registered in advance.
-- @function [parent=#time] newSimulationTimer
-- @param #number delay
-- @param openmw.async#TimerCallback callback A callback returned by `registerTimerCallback`
-- @param arg An argument for `callback`; can be `nil`.
function time.newSimulationTimer(delay, callback, callbackArg)
local async = require('openmw.async')
return async:newSimulationTimer(delay, callback, callbackArg)
end
---
-- Run given function repeatedly.
-- Note that loading a save stops the evaluation. If it should work always, call it during initialization of the script (i.e. not in a handler)
-- @function [parent=#time] runRepeatedly
-- @param #function fn the function that should be called
-- @param #number period interval
-- @param #table options additional options `initialDelay` and `type`.
-- `initialDelay` - delay before the first call. If missed then the delay is a random number in range [0, N]. Randomization is used for performance reasons -- to prevent all scripts from doing time consuming operations at the same time.
-- `type` - either `time.SimulationTime` (by default, timer uses simulation time) or `time.GameTime` (timer uses game time).
-- @return #function a function without arguments that can be used to stop the periodical evaluation.
-- @usage
-- local stopFn = time.runRepeatedly(function() print('Test') end,
-- 5 * time.second) -- print 'Test' every 5 seconds
-- stopFn() -- stop printing 'Test'
-- time.runRepeatedly( -- print 'Test' every 5 minutes with initial 30 second delay
-- function() print('Test2') end, 5 * time.minute,
-- { initialDelay = 30 * time.second })
-- @usage
-- local timeBeforeMidnight = time.day - time.gameTime() % time.day
-- time.runRepeatedly(doSomething, time.day, {
-- initialDelay = timeBeforeMidnight,
-- type = time.GameTime,
-- }) -- call `doSomething` at the end of every game day.
function time.runRepeatedly(fn, period, options)
if period <= 0 then
error('Period must be positive. If you want it to be as small '..
'as possible, use the engine handler `onUpdate` instead', 2)
end
local async = require('openmw.async')
local core = require('openmw.core')
local initialDelay = (options and options.initialDelay) or math.random() * period
local getTimeFn, newTimerFn
if (options and options.type) == time.GameTime then
getTimeFn = core.getGameTime
newTimerFn = async.newUnsavableGameTimer
else
getTimeFn = core.getSimulationTime
newTimerFn = async.newUnsavableSimulationTimer
end
local baseTime = getTimeFn() + initialDelay
local breakFlag = false
local wrappedFn
wrappedFn = function()
if breakFlag then return end
fn()
local nextDelay = 1.5 * period - math.fmod(getTimeFn() - baseTime + period / 2, period)
newTimerFn(async, nextDelay, wrappedFn)
end
newTimerFn(async, initialDelay, wrappedFn)
return function() breakFlag = true end
end
return time

@ -28,72 +28,5 @@ function aux_util.findNearestTo(point, objectList)
return res, resDist
end
-------------------------------------------------------------------------------
-- Runs given function every N game seconds (seconds when the game is not paused).
-- Note that loading a save stops the evaluation. If it should work always, call it in 2 places --
-- when a script starts and in the engine handler `onLoad`.
-- @function [parent=#util] runEveryNSeconds
-- @param #number N interval in seconds
-- @param #function fn the function that should be called every N seconds
-- @param #number initialDelay optional argument -- delay in seconds before the first call. If missed then the delay is a random number in range [0, N]. Randomization is used for performance reasons -- to prevent all scripts from doing time consuming operations at the same time.
-- @return #function a function without arguments that can be used to stop the periodical evaluation.
-- @usage
-- local stopFn = aux_util.runEveryNSeconds(5, function() print('Test') end) -- print 'Test' every 5 seconds
-- stopFn() -- stop printing 'Test'
-- aux_util.runEveryNSeconds(5, function() print('Test2') end, 1) -- print 'Test' every 5 seconds starting from the next second
function aux_util.runEveryNSeconds(N, fn, initialDelay)
if N <= 0 then
error('Interval must be positive. If you want it to be as small '..
'as possible, use the engine handler `onUpdate` instead', 2)
end
local async = require('openmw.async')
local core = require('openmw.core')
local breakFlag = false
local initialDelay = initialDelay or math.random() * N
local baseTime = core.getGameTimeInSeconds() + initialDelay
local wrappedFn
wrappedFn = function()
if breakFlag then return end
fn()
local nextDelay = 1.5 * N - math.fmod(core.getGameTimeInSeconds() - baseTime + N / 2, N)
async:newUnsavableTimerInSeconds(nextDelay, wrappedFn)
end
async:newUnsavableTimerInSeconds(initialDelay, wrappedFn)
return function() breakFlag = true end
end
-------------------------------------------------------------------------------
-- Runs given function every N game hours.
-- Note that loading a save stops the evaluation. If it should work always, call it in 2 places --
-- when a script starts and in the engine handler `onLoad`.
-- @function [parent=#util] runEveryNHours
-- @param #number N interval in game hours
-- @param #function fn the function that should be called every N game hours
-- @param #number initialDelay optional argument -- delay in game hours before the first call. If missed then the delay is a random number in range [0, N]. Randomization is used for performance reasons -- to prevent all scripts from doing time consuming operations at the same time.
-- @return #function a function without arguments that can be used to stop the periodical evaluation.
-- @usage
-- local timeBeforeMidnight = 24 - math.fmod(core.getGameTimeInHours(), 24)
-- aux_util.runEveryNHours(24, doSomething, timeBeforeMidnight) -- call `doSomething` at the end of every game day.
function aux_util.runEveryNHours(N, fn, initialDelay)
if N <= 0 then
error('Interval must be positive. If you want it to be as small '..
'as possible, use the engine handler `onUpdate` instead', 2)
end
local async = require('openmw.async')
local core = require('openmw.core')
local breakFlag = false
local initialDelay = initialDelay or math.random() * N
local baseTime = core.getGameTimeInHours() + initialDelay
local wrappedFn
wrappedFn = function()
if breakFlag then return end
fn()
local nextDelay = 1.5 * N - math.fmod(core.getGameTimeInHours() - baseTime + N / 2, N)
async:newUnsavableTimerInHours(nextDelay, wrappedFn)
end
async:newUnsavableTimerInHours(initialDelay, wrappedFn)
return function() breakFlag = true end
end
return aux_util

@ -15,35 +15,35 @@
-- @return #TimerCallback
-------------------------------------------------------------------------------
-- Calls callback(arg) in `delay` seconds.
-- Calls callback(arg) in `delay` simulation seconds.
-- Callback must be registered in advance.
-- @function [parent=#async] newTimerInSeconds
-- @function [parent=#async] newSimulationTimer
-- @param self
-- @param #number delay
-- @param #TimerCallback callback A callback returned by `registerTimerCallback`
-- @param arg An argument for `callback`; can be `nil`.
-------------------------------------------------------------------------------
-- Calls callback(arg) in `delay` game hours.
-- Calls callback(arg) in `delay` game seconds.
-- Callback must be registered in advance.
-- @function [parent=#async] newTimerInHours
-- @function [parent=#async] newGameTimer
-- @param self
-- @param #number delay
-- @param #TimerCallback callback A callback returned by `registerTimerCallback`
-- @param arg An argument for `callback`; can be `nil`.
-------------------------------------------------------------------------------
-- Calls `func()` in `delay` seconds.
-- Calls `func()` in `delay` simulation seconds.
-- The timer will be lost if the game is saved and loaded.
-- @function [parent=#async] newUnsavableTimerInSeconds
-- @function [parent=#async] newUnsavableSimulationTimer
-- @param self
-- @param #number delay
-- @param #function func
-------------------------------------------------------------------------------
-- Calls `func()` in `delay` game hours.
-- Calls `func()` in `delay` game seconds.
-- The timer will be lost if the game is saved and loaded.
-- @function [parent=#async] newUnsavableTimerInHours
-- @function [parent=#async] newUnsavableGameTimer
-- @param self
-- @param #number delay
-- @param #function func

@ -20,16 +20,25 @@
-- @param #string eventName
-- @param eventData
-------------------------------------------------------------------------------
-- Simulation time in seconds.
-- The number of simulation seconds passed in the game world since starting a new game.
-- @function [parent=#core] getSimulationTime
-- @return #number
-------------------------------------------------------------------------------
-- The scale of simulation time relative to real time.
-- @function [parent=#core] getSimulationTimeScale
-- @return #number
-------------------------------------------------------------------------------
-- Game time in seconds.
-- The number of seconds passed in the game world since starting a new game.
-- @function [parent=#core] getGameTimeInSeconds
-- @function [parent=#core] getGameTime
-- @return #number
-------------------------------------------------------------------------------
-- Current time of the game world in hours.
-- Note that the number of game seconds in a game hour is not guaranteed to be fixed.
-- @function [parent=#core] getGameTimeInHours
-- The scale of game time relative to simulation time.
-- @function [parent=#core] getGameTimeScale
-- @return #number
-------------------------------------------------------------------------------

@ -29,6 +29,36 @@
-- @param #number gridY
-- @return openmw.core#Cell
-------------------------------------------------------------------------------
-- Simulation time in seconds.
-- The number of simulation seconds passed in the game world since starting a new game.
-- @function [parent=#core] getSimulationTime
-- @return #number
-------------------------------------------------------------------------------
-- The scale of simulation time relative to real time.
-- @function [parent=#core] getSimulationTimeScale
-- @return #number
-------------------------------------------------------------------------------
-- Game time in seconds.
-- @function [parent=#core] getGameTime
-- @return #number
-------------------------------------------------------------------------------
-- The scale of game time relative to simulation time.
-- @function [parent=#core] getGameTimeScale
-- @return #number
-------------------------------------------------------------------------------
-- Set the ratio of game time speed to simulation time speed.
-- @function [parent=#world] setGameTimeScale
-- @param #number ratio
-------------------------------------------------------------------------------
-- Whether the world is paused (onUpdate doesn't work when the world is paused).
-- @function [parent=#world] isWorldPaused
-- @return #boolean
return nil

@ -0,0 +1,64 @@
-------------------------------------------------------------------------------
-- Operating System Facilities.
-- This library is implemented through table os.
-- @module os
-------------------------------------------------------------------------------
-- Returns a string or a table containing date and time, formatted according
-- to the given string `format`.
--
-- If the `time` argument is present, this is the time to be formatted
-- (see the `os.time` function for a description of this value). Otherwise,
-- `date` formats the current time.
--
-- If `format` starts with '`!`', then the date is formatted in Coordinated
-- Universal Time. After this optional character, if `format` is the string
-- "`*t`", then `date` returns a table with the following fields:
--
-- * `year` (four digits)
-- * `month` (1--12)
-- * `day` (1--31)
-- * `hour` (0--23)
-- * `min` (0--59)
-- * `sec` (0--61)
-- * `wday` (weekday, Sunday is 1)
-- * `yday` (day of the year)
-- * `isdst` (daylight saving flag, a boolean).
--
-- If `format` is not "`*t`", then `date` returns the date as a string,
-- formatted according to the same rules as the C function `strftime`.
-- When called without arguments, `date` returns a reasonable date and time
-- representation that depends on the host system and on the current locale
-- (that is, `os.date()` is equivalent to `os.date("%c")`).
-- @function [parent=#os] date
-- @param #string format format of date. (optional)
-- @param #number time time to format. (default value is current time)
-- @return #string a formatted string representation of `time`.
-------------------------------------------------------------------------------
-- Returns the number of seconds from time `t1` to time `t2`. In POSIX,
-- Windows, and some other systems, this value is exactly `t2`*-*`t1`.
-- @function [parent=#os] difftime
-- @param #number t2
-- @param #number t1
-- @return #number the number of seconds from time `t1` to time `t2`.
-------------------------------------------------------------------------------
-- Returns the current time when called without arguments, or a time
-- representing the date and time specified by the given table. This table
-- must have fields `year`, `month`, and `day`, and may have fields `hour`,
-- `min`, `sec`, and `isdst` (for a description of these fields, see the
-- `os.date` function).
--
-- The returned value is a number, whose meaning depends on your system. In
-- POSIX, Windows, and some other systems, this number counts the number
-- of seconds since some given start time (the "epoch"). In other systems,
-- the meaning is not specified, and the number returned by `time` can be
-- used only as an argument to `date` and `difftime`.
-- @function [parent=#os] time
-- @param #table table a table which describes a date.
-- @return #number a number meaning a date.
return nil
Loading…
Cancel
Save