From 67e4fc9136ce37081faad12b9bbc910494271bdc Mon Sep 17 00:00:00 2001 From: fcelsa Date: Sat, 10 Apr 2021 22:21:14 +0200 Subject: [PATCH] Update RoyalMod lib --- src/lib/rmod/Main.lua | 2 +- src/lib/rmod/RoyalMod.lua | 152 ++++++++++++++--- src/lib/utility/Array.lua | 48 ++++++ .../utility/{UtilityDebug.lua => Debug.lua} | 121 +++++++------- src/lib/utility/DelayedCallBack.lua | 63 ++++++++ .../utility/{UtilityEntity.lua => Entity.lua} | 27 ++-- src/lib/utility/FadeEffect.lua | 153 ++++++++++++++++++ .../{UtilityGameplay.lua => Gameplay.lua} | 17 +- ...ilityInterpolator.lua => Interpolator.lua} | 2 +- src/lib/utility/Main.lua | 17 +- .../utility/{UtilityString.lua => String.lua} | 2 +- .../utility/{UtilityTable.lua => Table.lua} | 16 +- src/lib/utility/Utility.lua | 101 +++++++++++- 13 files changed, 611 insertions(+), 110 deletions(-) create mode 100644 src/lib/utility/Array.lua rename src/lib/utility/{UtilityDebug.lua => Debug.lua} (67%) create mode 100644 src/lib/utility/DelayedCallBack.lua rename src/lib/utility/{UtilityEntity.lua => Entity.lua} (87%) create mode 100644 src/lib/utility/FadeEffect.lua rename src/lib/utility/{UtilityGameplay.lua => Gameplay.lua} (85%) rename src/lib/utility/{UtilityInterpolator.lua => Interpolator.lua} (99%) rename src/lib/utility/{UtilityString.lua => String.lua} (98%) rename src/lib/utility/{UtilityTable.lua => Table.lua} (91%) diff --git a/src/lib/rmod/Main.lua b/src/lib/rmod/Main.lua index 87e11cb..bc6a6b5 100644 --- a/src/lib/rmod/Main.lua +++ b/src/lib/rmod/Main.lua @@ -1,7 +1,7 @@ --- Royal Mod ---@author Royal Modding ----@version 1.4.0.0 +---@version 1.5.0.0 ---@date 03/12/2020 --- Initialize RoyalMod diff --git a/src/lib/rmod/RoyalMod.lua b/src/lib/rmod/RoyalMod.lua index f411588..902b23c 100644 --- a/src/lib/rmod/RoyalMod.lua +++ b/src/lib/rmod/RoyalMod.lua @@ -1,33 +1,64 @@ --- Royal Mod ---@author Royal Modding ----@version 1.4.0.0 +---@version 1.5.0.0 ---@date 03/12/2020 ---@class RoyalMod ----@field directory string mod directory ----@field userProfileDirectory string user profile directory ----@field name string mod name ----@field mod table g_modManager mod object ----@field version string mod version ----@field author string mod author ----@field modEnv table mod scripting environment ----@field gameEnv table game scripting environment ----@field super table mod super class ----@field debug boolean mod debug state +---@field onWriteStream fun(self: RoyalMod, streamId: integer) +---@field onReadStream fun(self: RoyalMod, streamId: integer) +---@field onUpdateTick fun(self: RoyalMod, dt: number) +---@field onWriteUpdateStream fun(self: RoyalMod, streamId: integer, connection: Connection, dirtyMask: integer) +---@field onReadUpdateStream fun(self: RoyalMod, streamId: integer, timestamp: number, connection: Connection) +---@field onLoadMap fun(self: RoyalMod, mapNode: integer, mapFile: string) +---@field onDeleteMap fun(self: RoyalMod) +---@field onDraw fun(self: RoyalMod) +---@field onUpdate fun(self: RoyalMod, dt: number) +---@field onMouseEvent fun(self: RoyalMod, posX: number, posY: number, isDown: boolean, isUp: boolean, button: integer) +---@field onKeyEvent fun(self: RoyalMod, unicode: integer, sym: integer, modifier: integer, isDown: boolean) +---@field initialize fun(self: RoyalMod) +---@field onValidateVehicleTypes fun(self: RoyalMod, vtm: VehicleTypeManager, addSpecialization: fun(specName: string), addSpecializationBySpecialization: fun(specName: string, requiredSpecName: string), addSpecializationByVehicleType: fun(specName: string, requiredVehicleTypeName: string), addSpecializationByFunction: fun(specName: string, func: function)) +---@field onMissionInitialize fun(self: RoyalMod, baseDirectory: string, missionCollaborators: MissionCollaborators) +---@field onSetMissionInfo fun(self: RoyalMod, missionInfo: MissionInfo, missionDynamicInfo: table) +---@field onLoad fun(self: RoyalMod) +---@field onPreLoadMap fun(self: RoyalMod, mapFile: string) +---@field onCreateStartPoint fun(self: RoyalMod, startPointNode: integer) +---@field onPostLoadMap fun(self: RoyalMod, mapNode: integer, mapFile: string) +---@field onLoadSavegame fun(self: RoyalMod, savegameDirectory: string, savegameIndex: integer) +---@field onPreLoadVehicles fun(self: RoyalMod, xmlFile: integer, resetVehicles: boolean) +---@field onPreLoadItems fun(self: RoyalMod, xmlFile: integer) +---@field onPreLoadOnCreateLoadedObjects fun(self: RoyalMod, xmlFile: integer) +---@field onLoadFinished fun(self: RoyalMod) +---@field onStartMission fun(self: RoyalMod) +---@field onMissionStarted fun(self: RoyalMod) +---@field onPreDeleteMap fun(self: RoyalMod) +---@field onPreSaveSavegame fun(self: RoyalMod, savegameDirectory: string, savegameIndex: integer) +---@field onPostSaveSavegame fun(self: RoyalMod, savegameDirectory: string, savegameIndex: integer) +---@field onLoadHelpLine fun(self: RoyalMod): string +--@field directory string mod directory +--@field userProfileDirectory string user profile directory +--@field name string mod name +--@field mod table g_modManager mod object +--@field version string mod version +--@field author string mod author +--@field modEnv table mod scripting environment +--@field gameEnv table game scripting environment +--@field super table mod super class +--@field debug boolean mod debug state RoyalMod = {} ---@param debug boolean defines if debug is enabled ---@param mpSync boolean defines if mp sync is enabled ---@return RoyalMod function RoyalMod.new(debug, mpSync) + ---@type RoyalMod local mod = {} mod.directory = g_currentModDirectory mod.userProfileDirectory = getUserProfileAppPath() mod.name = g_currentModName - mod.mod = g_modManager:getModByName(mod.name) - mod.version = mod.mod.version - mod.author = mod.mod.author + mod.modManagerMod = g_modManager:getModByName(mod.name) + mod.version = mod.modManagerMod.version + mod.author = mod.modManagerMod.author mod.modEnv = getfenv() mod.gameEnv = getfenv(0) mod.super = {} @@ -35,11 +66,13 @@ function RoyalMod.new(debug, mpSync) if mod.debug then mod.gameEnv["g_showDevelopmentWarnings"] = true + mod.gameEnv["g_addTestCommands"] = true --mod.gameEnv["g_isDevelopmentConsoleScriptModTesting"] = true end mod.super.oldFunctions = {} + ---@param error string mod.super.errorHandle = function(error) g_logManager:devError("RoyalMod caught error from %s (%s)", mod.name, mod.version) g_logManager:error(error) @@ -61,6 +94,8 @@ function RoyalMod.new(debug, mpSync) if mpSync then mod.super.sync = Object:new(g_server ~= nil, g_client ~= nil, Class(nil, Object)) + ---@param self Object + ---@param streamId integer mod.super.sync.writeStream = function(self, streamId) self:superClass().writeStream(self, streamId) if mod.onWriteStream ~= nil then @@ -72,6 +107,8 @@ function RoyalMod.new(debug, mpSync) end end + ---@param self Object + ---@param streamId integer mod.super.sync.readStream = function(self, streamId) self:superClass().readStream(self, streamId) if mod.onReadStream ~= nil then @@ -83,6 +120,8 @@ function RoyalMod.new(debug, mpSync) end end + ---@param self Object + ---@param dt number mod.super.sync.updateTick = function(self, dt) self:superClass().updateTick(self, dt) if mod.onUpdateTick ~= nil then @@ -90,6 +129,10 @@ function RoyalMod.new(debug, mpSync) end end + ---@param self Object + ---@param streamId integer + ---@param connection Connection + ---@param dirtyMask integer mod.super.sync.writeUpdateStream = function(self, streamId, connection, dirtyMask) self:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) if mod.onWriteUpdateStream ~= nil then @@ -97,6 +140,10 @@ function RoyalMod.new(debug, mpSync) end end + ---@param self Object + ---@param streamId integer + ---@param timestamp number + ---@param connection Connection mod.super.sync.readUpdateStream = function(self, streamId, timestamp, connection) self:superClass().readUpdateStream(self, streamId, timestamp, connection) if mod.onReadUpdateStream ~= nil then @@ -105,36 +152,53 @@ function RoyalMod.new(debug, mpSync) end end + ---@param _ table + ---@param mapFile string mod.super.loadMap = function(_, mapFile) if mod.onLoadMap ~= nil then xpcall(mod.onLoadMap, mod.super.errorHandle, mod, mod.mapNode, mapFile) end end + ---@param _ table mod.super.deleteMap = function(_) if mod.onDeleteMap ~= nil then xpcall(mod.onDeleteMap, mod.super.errorHandle, mod) end end + ---@param _ table mod.super.draw = function(_) if mod.onDraw ~= nil then xpcall(mod.onDraw, mod.super.errorHandle, mod) end end + ---@param _ table + ---@param dt number mod.super.update = function(_, dt) if mod.onUpdate ~= nil then xpcall(mod.onUpdate, mod.super.errorHandle, mod, dt) end end + ---@param _ table + ---@param posX number + ---@param posY number + ---@param isDown boolean + ---@param isUp boolean + ---@param button integer mod.super.mouseEvent = function(_, posX, posY, isDown, isUp, button) if mod.onMouseEvent ~= nil then xpcall(mod.onMouseEvent, mod.super.errorHandle, mod, posX, posY, isDown, isUp, button) end end + ---@param _ table + ---@param unicode integer + ---@param sym integer + ---@param modifier integer + ---@param isDown boolean mod.super.keyEvent = function(_, unicode, sym, modifier, isDown) if mod.onKeyEvent ~= nil then xpcall(mod.onKeyEvent, mod.super.errorHandle, mod, unicode, sym, modifier, isDown) @@ -143,6 +207,14 @@ function RoyalMod.new(debug, mpSync) mod.super.oldFunctions.VehicleTypeManagervalidateVehicleTypes = VehicleTypeManager.validateVehicleTypes VehicleTypeManager.validateVehicleTypes = function(self, ...) + ---@type table + local global = mod.gameEnv["g_i18n"].texts + ---@type string + for key, text in pairs(g_i18n.texts) do + if global[key] == nil then + global[key] = text + end + end if mod.initialize ~= nil then --- g_currentMission is still nil here --- All mods are loaded here @@ -167,6 +239,7 @@ function RoyalMod.new(debug, mpSync) mod.super.errorHandle, mod, self, + ---@param specName string function(specName) if not specAddAllowed then g_logManager:devError("[%s] addSpecialization is no more allowed", mod.name) @@ -174,6 +247,8 @@ function RoyalMod.new(debug, mpSync) end table.insert(specs, {name = string.format("%s.%s", mod.name, specName), addedTo = {}}) end, + ---@param specName string + ---@param requiredSpecName string function(specName, requiredSpecName) if not specAddAllowed then g_logManager:devError("[%s] addSpecializationBySpecialization is no more allowed", mod.name) @@ -181,6 +256,8 @@ function RoyalMod.new(debug, mpSync) end table.insert(specsBySpec, {name = string.format("%s.%s", mod.name, specName), requiredSpecName = requiredSpecName, addedTo = {}}) end, + ---@param specName string + ---@param requiredVehicleTypeName string function(specName, requiredVehicleTypeName) if not specAddAllowed then g_logManager:devError("[%s] addSpecializationByVehicleType is no more allowed", mod.name) @@ -188,6 +265,8 @@ function RoyalMod.new(debug, mpSync) end table.insert(specsByType, {name = string.format("%s.%s", mod.name, specName), requiredVehicleTypeName = requiredVehicleTypeName, addedTo = {}}) end, + ---@param specName string + ---@param func function function(specName, func) if not specAddAllowed then g_logManager:devError("[%s] addSpecializationByFunction is no more allowed", mod.name) @@ -201,6 +280,7 @@ function RoyalMod.new(debug, mpSync) specAddAllowed = false -- remove invalid specs + for i, spec in pairs(specs) do if g_specializationManager:getSpecializationByName(spec.name) == nil then g_logManager:devError("[%s] Can't find specialization %s", mod.name, spec.name) @@ -327,13 +407,12 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00new = Mission00.new + ---@param self Mission00 + ---@param baseDirectory string + ---@param customMt? table + ---@param missionCollaborators MissionCollaborators + ---@return Mission00 Mission00.new = function(self, baseDirectory, customMt, missionCollaborators, ...) - local global = mod.gameEnv["g_i18n"].texts - for key, text in pairs(g_i18n.texts) do - if global[key] == nil then - global[key] = text - end - end if mod.onMissionInitialize ~= nil then --- g_currentMission is still nil here xpcall(mod.onMissionInitialize, mod.super.errorHandle, mod, baseDirectory, missionCollaborators) @@ -342,6 +421,9 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00setMissionInfo = Mission00.setMissionInfo + ---@param self Mission00 + ---@param missionInfo FSCareerMissionInfo + ---@param missionDynamicInfo table Mission00.setMissionInfo = function(self, missionInfo, missionDynamicInfo, ...) g_currentMission:addLoadFinishedListener(mod.super) g_currentMission:registerObjectToCallOnMissionStart(mod.super) @@ -353,6 +435,7 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00load = Mission00.load + ---@param self Mission00 Mission00.load = function(self, ...) if mod.onLoad ~= nil then xpcall(mod.onLoad, mod.super.errorHandle, mod) @@ -361,6 +444,12 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.FSBaseMissionloadMap = FSBaseMission.loadMap + ---@param self FSBaseMission + ---@param mapFile string + ---@param addPhysics boolean + ---@param asyncCallbackFunction function + ---@param asyncCallbackObject table + ---@param asyncCallbackArguments table FSBaseMission.loadMap = function(self, mapFile, addPhysics, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments, ...) if mod.onPreLoadMap ~= nil then xpcall(mod.onPreLoadMap, mod.super.errorHandle, mod, mapFile) @@ -369,6 +458,8 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00onCreateStartPoint = Mission00.onCreateStartPoint + ---@param self Mission00 + ---@param startPointNode integer Mission00.onCreateStartPoint = function(self, startPointNode, ...) mod.super.oldFunctions.Mission00onCreateStartPoint(self, startPointNode, ...) if mod.super.sync ~= nil then @@ -381,6 +472,10 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.BaseMissionloadMapFinished = BaseMission.loadMapFinished + ---@param self BaseMission + ---@param mapNode integer + ---@param arguments table + ---@param callAsyncCallback boolean BaseMission.loadMapFinished = function(self, mapNode, arguments, callAsyncCallback, ...) mod.mapNode = mapNode local mapFile, _, _, _ = unpack(arguments) @@ -391,6 +486,9 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00loadMission00Finished = Mission00.loadMission00Finished + ---@param self Mission00 + ---@param mapNode integer + ---@param arguments table Mission00.loadMission00Finished = function(self, mapNode, arguments, ...) if mod.onLoadSavegame ~= nil then xpcall(mod.onLoadSavegame, mod.super.errorHandle, mod, mod.super.getSavegameDirectory(), g_currentMission.missionInfo.savegameIndex) @@ -399,6 +497,9 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00loadVehicles = Mission00.loadVehicles + ---@param self Mission00 + ---@param xmlFile integer + ---@param resetVehicles boolean Mission00.loadVehicles = function(self, xmlFile, resetVehicles, ...) if mod.onPreLoadVehicles ~= nil then xpcall(mod.onPreLoadVehicles, mod.super.errorHandle, mod, xmlFile, resetVehicles) @@ -407,6 +508,8 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00loadItems = Mission00.loadItems + ---@param self Mission00 + ---@param xmlFile integer Mission00.loadItems = function(self, xmlFile, ...) if mod.onPreLoadItems ~= nil then xpcall(mod.onPreLoadItems, mod.super.errorHandle, mod, xmlFile) @@ -415,6 +518,8 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00loadOnCreateLoadedObjects = Mission00.loadOnCreateLoadedObjects + ---@param self Mission00 + ---@param xmlFile integer Mission00.loadOnCreateLoadedObjects = function(self, xmlFile, ...) if mod.onPreLoadOnCreateLoadedObjects ~= nil then xpcall(mod.onPreLoadOnCreateLoadedObjects, mod.super.errorHandle, mod, xmlFile) @@ -430,6 +535,7 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00onStartMission = Mission00.onStartMission + ---@param self Mission00 Mission00.onStartMission = function(self, ...) if mod.onStartMission ~= nil then xpcall(mod.onStartMission, mod.super.errorHandle, mod) @@ -444,6 +550,7 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.Mission00delete = Mission00.delete + ---@param self Mission00 Mission00.delete = function(self, ...) if mod.onPreDeleteMap ~= nil then xpcall(mod.onPreDeleteMap, mod.super.errorHandle, mod) @@ -452,6 +559,7 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.FSBaseMissionsaveSavegame = FSBaseMission.saveSavegame + ---@param self FSBaseMission FSBaseMission.saveSavegame = function(self, ...) if mod.onPreSaveSavegame ~= nil then -- before all vhicles, items and onCreateObjects are saved @@ -465,6 +573,10 @@ function RoyalMod.new(debug, mpSync) end mod.super.oldFunctions.HelpLineManagerloadMapData = HelpLineManager.loadMapData + ---@param self HelpLineManager + ---@param xmlFile integer + ---@param missionInfo FSCareerMissionInfo + ---@return boolean HelpLineManager.loadMapData = function(self, xmlFile, missionInfo) if mod.super.oldFunctions.HelpLineManagerloadMapData(self, xmlFile, missionInfo) then if mod.onLoadHelpLine ~= nil then diff --git a/src/lib/utility/Array.lua b/src/lib/utility/Array.lua new file mode 100644 index 0000000..e8a9d6d --- /dev/null +++ b/src/lib/utility/Array.lua @@ -0,0 +1,48 @@ +--- Royal Utility + +---@author Royal Modding +---@version 2.1.0.0 +---@date 26/02/2021 + +---@alias Array table Table with numeric indexes only, always ordered and sequential + +--- Array utilities class built with performances in mind (with 'array' we mean tables with numeric indexes only, always ordered and sequential) +---@class ArrayUtility +ArrayUtility = ArrayUtility or {} + +--- Remove matching elements from an array +---@param array Array +---@param removeFunc fun(array: Array, index: number, moveAt: number): boolean | "function(array, index, moveAt) local element = array[index] return true end" +---@return number removedCount count of removed elements +function ArrayUtility.remove(array, removeFunc) + local removedCount = 0 + local moveAt, length = 1, #array + for index = 1, length do + if removeFunc(array, index, moveAt) then + array[index] = nil + removedCount = removedCount + 1 + else + -- move kept element's value to moveAt's position, if it's not already there + if (index ~= moveAt) then + array[moveAt] = array[index] + array[index] = nil + end + -- increment position of where we'll place the next kept value + moveAt = moveAt + 1 + end + end + return removedCount +end + +--- Remove element at the given index from an array +---@param array Array +---@param index number +---@return Array +function ArrayUtility.removeAt(array, index) + ArrayUtility.remove( + array, + function(_, i) + return index == i + end + ) +end diff --git a/src/lib/utility/UtilityDebug.lua b/src/lib/utility/Debug.lua similarity index 67% rename from src/lib/utility/UtilityDebug.lua rename to src/lib/utility/Debug.lua index fbade21..b6e763b 100644 --- a/src/lib/utility/UtilityDebug.lua +++ b/src/lib/utility/Debug.lua @@ -1,9 +1,12 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 05/01/2021 +---@class DebugUtility +DebugUtility = DebugUtility or {} + --- Render a table (for debugging purpose) ---@param posX number ---@param posY number @@ -11,7 +14,7 @@ ---@param inputTable table ---@param maxDepth integer|nil ---@param hideFunc boolean|nil -function Utility.renderTable(posX, posY, textSize, inputTable, maxDepth, hideFunc) +function DebugUtility.renderTable(posX, posY, textSize, inputTable, maxDepth, hideFunc) inputTable = inputTable or {tableIs = "nil"} hideFunc = hideFunc or false maxDepth = maxDepth or 2 @@ -67,7 +70,7 @@ end ---@param textSize number ---@param inputNode integer ---@param maxDepth integer|nil -function Utility.renderNodeHierarchy(posX, posY, textSize, inputNode, maxDepth) +function DebugUtility.renderNodeHierarchy(posX, posY, textSize, inputNode, maxDepth) if inputNode == nil or inputNode == 0 then return end @@ -79,7 +82,7 @@ function Utility.renderNodeHierarchy(posX, posY, textSize, inputNode, maxDepth) return i end local offset = i * textSize * 1.05 - local _, className = Utility.getObjectClass(node) + local _, className = EntityUtility.getObjectClass(node) renderText(x, posY - offset, textSize, string.format("%s (%s)", getName(node), className)) i = i + 1 for ni = 0, getNumOfChildren(node) - 1 do @@ -92,7 +95,7 @@ function Utility.renderNodeHierarchy(posX, posY, textSize, inputNode, maxDepth) setTextColor(1, 1, 1, 1) setTextBold(false) textSize = getCorrectTextSize(textSize) - local _, className = Utility.getObjectClass(inputNode) + local _, className = EntityUtility.getObjectClass(inputNode) renderText(posX, posY, textSize, string.format("%s (%s)", getName(inputNode), className)) for ni = 0, getNumOfChildren(inputNode) - 1 do i = renderNodeHierarchyRecursively(posX + textSize * 1.8, getChildAt(inputNode, ni), 1, i) @@ -115,7 +118,7 @@ end ---@param ag number g color if active ---@param ab number b color if active ---@param active boolean active? -function Utility.drawDebugRectangle(node, minX, maxX, minZ, maxZ, yOffset, alignToGround, r, g, b, ar, ag, ab, active) +function DebugUtility.drawDebugRectangle(node, minX, maxX, minZ, maxZ, yOffset, alignToGround, r, g, b, ar, ag, ab, active) if active then r, g, b = ar, ag, ab end @@ -149,7 +152,7 @@ end ---@param ag number g color if active ---@param ab number b color if active ---@param active boolean active? -function Utility.drawDebugCube(node, size, r, g, b, ar, ag, ab, active) +function DebugUtility.drawDebugCube(node, size, r, g, b, ar, ag, ab, active) if active then r, g, b = ar, ag, ab end @@ -174,31 +177,31 @@ function Utility.drawDebugCube(node, size, r, g, b, ar, ag, ab, active) corners[7] = {x - offsets, y - offsets, z - offsets} corners[8] = {x - offsets, y - offsets, z + offsets} - Utility.drawDebugLine(corners[1], corners[2], 0, 0, 0) - Utility.drawDebugLine(corners[2], corners[3], 0, 0, 0) - Utility.drawDebugLine(corners[3], corners[4], 0, 0, 0) - Utility.drawDebugLine(corners[4], corners[1], 0, 0, 0) - Utility.drawDebugLine(corners[1], corners[5], 0, 0, 0) - Utility.drawDebugLine(corners[2], corners[6], 0, 0, 0) - Utility.drawDebugLine(corners[3], corners[7], 0, 0, 0) - Utility.drawDebugLine(corners[4], corners[8], 0, 0, 0) - Utility.drawDebugLine(corners[5], corners[6], 0, 0, 0) - Utility.drawDebugLine(corners[6], corners[7], 0, 0, 0) - Utility.drawDebugLine(corners[7], corners[8], 0, 0, 0) - Utility.drawDebugLine(corners[8], corners[5], 0, 0, 0) + DebugUtility.drawDebugLine(corners[1], corners[2], 0, 0, 0) + DebugUtility.drawDebugLine(corners[2], corners[3], 0, 0, 0) + DebugUtility.drawDebugLine(corners[3], corners[4], 0, 0, 0) + DebugUtility.drawDebugLine(corners[4], corners[1], 0, 0, 0) + DebugUtility.drawDebugLine(corners[1], corners[5], 0, 0, 0) + DebugUtility.drawDebugLine(corners[2], corners[6], 0, 0, 0) + DebugUtility.drawDebugLine(corners[3], corners[7], 0, 0, 0) + DebugUtility.drawDebugLine(corners[4], corners[8], 0, 0, 0) + DebugUtility.drawDebugLine(corners[5], corners[6], 0, 0, 0) + DebugUtility.drawDebugLine(corners[6], corners[7], 0, 0, 0) + DebugUtility.drawDebugLine(corners[7], corners[8], 0, 0, 0) + DebugUtility.drawDebugLine(corners[8], corners[5], 0, 0, 0) - Utility.drawDebugTriangle(corners[3], corners[2], corners[1], r, g, b) - Utility.drawDebugTriangle(corners[1], corners[4], corners[3], r, g, b) - Utility.drawDebugTriangle(corners[4], corners[1], corners[5], r, g, b) - Utility.drawDebugTriangle(corners[5], corners[8], corners[4], r, g, b) - Utility.drawDebugTriangle(corners[8], corners[5], corners[6], r, g, b) - Utility.drawDebugTriangle(corners[6], corners[7], corners[8], r, g, b) - Utility.drawDebugTriangle(corners[7], corners[6], corners[2], r, g, b) - Utility.drawDebugTriangle(corners[2], corners[3], corners[7], r, g, b) - Utility.drawDebugTriangle(corners[2], corners[6], corners[5], r, g, b) - Utility.drawDebugTriangle(corners[5], corners[1], corners[2], r, g, b) - Utility.drawDebugTriangle(corners[4], corners[8], corners[7], r, g, b) - Utility.drawDebugTriangle(corners[7], corners[3], corners[4], r, g, b) + DebugUtility.drawDebugTriangle(corners[3], corners[2], corners[1], r, g, b) + DebugUtility.drawDebugTriangle(corners[1], corners[4], corners[3], r, g, b) + DebugUtility.drawDebugTriangle(corners[4], corners[1], corners[5], r, g, b) + DebugUtility.drawDebugTriangle(corners[5], corners[8], corners[4], r, g, b) + DebugUtility.drawDebugTriangle(corners[8], corners[5], corners[6], r, g, b) + DebugUtility.drawDebugTriangle(corners[6], corners[7], corners[8], r, g, b) + DebugUtility.drawDebugTriangle(corners[7], corners[6], corners[2], r, g, b) + DebugUtility.drawDebugTriangle(corners[2], corners[3], corners[7], r, g, b) + DebugUtility.drawDebugTriangle(corners[2], corners[6], corners[5], r, g, b) + DebugUtility.drawDebugTriangle(corners[5], corners[1], corners[2], r, g, b) + DebugUtility.drawDebugTriangle(corners[4], corners[8], corners[7], r, g, b) + DebugUtility.drawDebugTriangle(corners[7], corners[3], corners[4], r, g, b) end --- Draw a triangle (for debugging purpose) @@ -208,7 +211,7 @@ end ---@param r number r ---@param g number g ---@param b number b -function Utility.drawDebugTriangle(c1, c2, c3, r, g, b) +function DebugUtility.drawDebugTriangle(c1, c2, c3, r, g, b) drawDebugTriangle(c1[1], c1[2], c1[3], c2[1], c2[2], c2[3], c3[1], c3[2], c3[3], r, g, b, 1, false) end @@ -218,7 +221,7 @@ end ---@param r number r ---@param g number g ---@param b number b -function Utility.drawDebugLine(p1, p2, r, g, b) +function DebugUtility.drawDebugLine(p1, p2, r, g, b) drawDebugLine(p1[1], p1[2], p1[3], r, g, b, p2[1], p2[2], p2[3], r, g, b) end @@ -229,7 +232,7 @@ end ---@param h number height ---@param curve table AnimCurve object ---@param numPointsToShow? integer number of points to render -function Utility.renderAnimCurve(x, y, w, h, curve, numPointsToShow) +function DebugUtility.renderAnimCurve(x, y, w, h, curve, numPointsToShow) local graph = curve.debugGraph local numPoints = numPointsToShow or #curve.keyframes local minTime = 0 @@ -261,16 +264,16 @@ end --- Get the loading speed meter object ---@return LoadingSpeedMeter loadingSpeedMeter -function Utility.getVehicleLoadingSpeedMeter() - if Utility.loadingSpeedMeter == nil then +function DebugUtility.getVehicleLoadingSpeedMeter() + if DebugUtility.loadingSpeedMeter == nil then ---@class LoadingSpeedMeter - Utility.loadingSpeedMeter = {} - Utility.loadingSpeedMeter.vehicles = {} - Utility.loadingSpeedMeter.filters = {} + DebugUtility.loadingSpeedMeter = {} + DebugUtility.loadingSpeedMeter.vehicles = {} + DebugUtility.loadingSpeedMeter.filters = {} --- Add a new filter ---@param filterFunction function | 'function(vehicleData) return true, "meter name" end' - Utility.loadingSpeedMeter.addFilter = function(filterFunction) - table.insert(Utility.loadingSpeedMeter.filters, filterFunction) + DebugUtility.loadingSpeedMeter.addFilter = function(filterFunction) + table.insert(DebugUtility.loadingSpeedMeter.filters, filterFunction) end Utility.overwrittenFunction( Vehicle, @@ -278,7 +281,7 @@ function Utility.getVehicleLoadingSpeedMeter() function(self, superFunc, vehicleData, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments) local smEnabled = false local smName = "" - for _, filter in ipairs(Utility.loadingSpeedMeter.filters) do + for _, filter in ipairs(DebugUtility.loadingSpeedMeter.filters) do smEnabled, smName = filter(vehicleData) if smEnabled then break @@ -286,20 +289,20 @@ function Utility.getVehicleLoadingSpeedMeter() end if smEnabled then - Utility.loadingSpeedMeter.vehicles[self] = {} - Utility.loadingSpeedMeter.vehicles[self].smName = smName - Utility.loadingSpeedMeter.vehicles[self].totalStartTime = getTimeSec() + DebugUtility.loadingSpeedMeter.vehicles[self] = {} + DebugUtility.loadingSpeedMeter.vehicles[self].smName = smName + DebugUtility.loadingSpeedMeter.vehicles[self].totalStartTime = getTimeSec() end local state = superFunc(self, vehicleData, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments) if smEnabled then - Utility.loadingSpeedMeter.vehicles[self].totalTime = getTimeSec() - Utility.loadingSpeedMeter.vehicles[self].totalStartTime - print(string.format("[%s] Pre time: %.4f ms", Utility.loadingSpeedMeter.vehicles[self].smName, (Utility.loadingSpeedMeter.vehicles[self].preLoadTime or 0) * 1000)) - print(string.format("[%s] Load time: %.4f ms", Utility.loadingSpeedMeter.vehicles[self].smName, (Utility.loadingSpeedMeter.vehicles[self].loadTime or 0) * 1000)) - print(string.format("[%s] Post time: %.4f ms", Utility.loadingSpeedMeter.vehicles[self].smName, (Utility.loadingSpeedMeter.vehicles[self].postLoadTime or 0) * 1000)) - print(string.format("[%s] Total time: %.4f ms", Utility.loadingSpeedMeter.vehicles[self].smName, (Utility.loadingSpeedMeter.vehicles[self].totalTime or 0) * 1000)) - Utility.loadingSpeedMeter.vehicles[self] = nil + DebugUtility.loadingSpeedMeter.vehicles[self].totalTime = getTimeSec() - DebugUtility.loadingSpeedMeter.vehicles[self].totalStartTime + print(string.format("[%s] Pre time: %.4f ms", DebugUtility.loadingSpeedMeter.vehicles[self].smName, (DebugUtility.loadingSpeedMeter.vehicles[self].preLoadTime or 0) * 1000)) + print(string.format("[%s] Load time: %.4f ms", DebugUtility.loadingSpeedMeter.vehicles[self].smName, (DebugUtility.loadingSpeedMeter.vehicles[self].loadTime or 0) * 1000)) + print(string.format("[%s] Post time: %.4f ms", DebugUtility.loadingSpeedMeter.vehicles[self].smName, (DebugUtility.loadingSpeedMeter.vehicles[self].postLoadTime or 0) * 1000)) + print(string.format("[%s] Total time: %.4f ms", DebugUtility.loadingSpeedMeter.vehicles[self].smName, (DebugUtility.loadingSpeedMeter.vehicles[self].totalTime or 0) * 1000)) + DebugUtility.loadingSpeedMeter.vehicles[self] = nil end return state end @@ -308,21 +311,21 @@ function Utility.getVehicleLoadingSpeedMeter() SpecializationUtil, "raiseEvent", function(superFunc, vehicle, eventName, ...) - if Utility.loadingSpeedMeter.vehicles[vehicle] ~= nil then + if DebugUtility.loadingSpeedMeter.vehicles[vehicle] ~= nil then if eventName == "onPreLoad" then - Utility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime = getTimeSec() + DebugUtility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime = getTimeSec() superFunc(vehicle, eventName, ...) - Utility.loadingSpeedMeter.vehicles[vehicle].preLoadTime = getTimeSec() - Utility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime + DebugUtility.loadingSpeedMeter.vehicles[vehicle].preLoadTime = getTimeSec() - DebugUtility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime end if eventName == "onLoad" then - Utility.loadingSpeedMeter.vehicles[vehicle].loadStartTime = getTimeSec() + DebugUtility.loadingSpeedMeter.vehicles[vehicle].loadStartTime = getTimeSec() superFunc(vehicle, eventName, ...) - Utility.loadingSpeedMeter.vehicles[vehicle].loadTime = getTimeSec() - Utility.loadingSpeedMeter.vehicles[vehicle].loadStartTime + DebugUtility.loadingSpeedMeter.vehicles[vehicle].loadTime = getTimeSec() - DebugUtility.loadingSpeedMeter.vehicles[vehicle].loadStartTime end if eventName == "onPostLoad" then - Utility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime = getTimeSec() + DebugUtility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime = getTimeSec() superFunc(vehicle, eventName, ...) - Utility.loadingSpeedMeter.vehicles[vehicle].postLoadTime = getTimeSec() - Utility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime + DebugUtility.loadingSpeedMeter.vehicles[vehicle].postLoadTime = getTimeSec() - DebugUtility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime end else superFunc(vehicle, eventName, ...) @@ -330,5 +333,5 @@ function Utility.getVehicleLoadingSpeedMeter() end ) end - return Utility.loadingSpeedMeter + return DebugUtility.loadingSpeedMeter end diff --git a/src/lib/utility/DelayedCallBack.lua b/src/lib/utility/DelayedCallBack.lua new file mode 100644 index 0000000..ce6ef6b --- /dev/null +++ b/src/lib/utility/DelayedCallBack.lua @@ -0,0 +1,63 @@ +--- Royal Utility + +---@author Royal Modding +---@version 2.1.0.0 +---@date 08/03/17 + +---@class DelayedCallBack +DelayedCallBack = {} + +---@param callback function +---@param callbackObject any +---@return DelayedCallBack +function DelayedCallBack:new(callback, callbackObject) + if DelayedCallBack_mt == nil then + DelayedCallBack_mt = Class(DelayedCallBack) + end + + ---@type DelayedCallBack + local dcb = setmetatable({}, DelayedCallBack_mt) + dcb.callBack = callback + dcb.callbackObject = callbackObject + dcb.callbackCalled = true + dcb.delay = 0 + dcb.timer = 0 + dcb.skipOneFrame = false + return dcb +end + +---@param dt number +function DelayedCallBack:update(dt) + if not self.callbackCalled then + if not self.skipOneFrame then + self.timer = self.timer + dt + end + if self.timer >= self.delay then + self:callCallBack() + end + if self.skipOneFrame then + self.timer = self.timer + dt + end + end +end + +---@param delay number +function DelayedCallBack:call(delay, ...) + self.callbackCalled = false + self.callbackParams = {...} + if delay == nil or delay == 0 then + self:callCallBack() + else + self.delay = delay + self.timer = 0 + end +end + +function DelayedCallBack:callCallBack() + if self.callbackObject ~= nil then + self.callBack(self.callbackObject, unpack(self.callbackParams)) + else + self.callBack(unpack(self.callbackParams)) + end + self.callbackCalled = true +end diff --git a/src/lib/utility/UtilityEntity.lua b/src/lib/utility/Entity.lua similarity index 87% rename from src/lib/utility/UtilityEntity.lua rename to src/lib/utility/Entity.lua index bd9891d..9c3977d 100644 --- a/src/lib/utility/UtilityEntity.lua +++ b/src/lib/utility/Entity.lua @@ -1,14 +1,17 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 05/01/2021 +---@class EntityUtility +EntityUtility = EntityUtility or {} + --- Get the class id and name of an onject ---@param objectId integer ---@return integer classId class id ---@return string className class name -function Utility.getObjectClass(objectId) +function EntityUtility.getObjectClass(objectId) if objectId == nil then return nil, nil end @@ -23,7 +26,7 @@ end ---@param childNode integer ---@param parentNode integer ---@return boolean -function Utility.isChildOf(childNode, parentNode) +function EntityUtility.isChildOf(childNode, parentNode) if childNode == nil or childNode == 0 or parentNode == nil or parentNode == 0 then return false end @@ -41,9 +44,9 @@ end ---@param nodeId integer id of node ---@param rootId integer id of root node ---@return string nodeIndex index of node -function Utility.nodeToIndex(nodeId, rootId) +function EntityUtility.nodeToIndex(nodeId, rootId) local index = "" - if nodeId ~= nil and entityExists(nodeId) and rootId ~= nil and entityExists(rootId) and Utility.isChildOf(nodeId, rootId) then + if nodeId ~= nil and entityExists(nodeId) and rootId ~= nil and entityExists(rootId) and EntityUtility.isChildOf(nodeId, rootId) then index = tostring(getChildIndex(nodeId)) local pNode = getParent(nodeId) while pNode ~= rootId and pNode ~= 0 do @@ -58,7 +61,7 @@ end ---@param nodeIndex string index of node ---@param rootId integer id of root node ---@return integer nodeId id of node -function Utility.indexToNode(nodeIndex, rootId) +function EntityUtility.indexToNode(nodeIndex, rootId) if nodeIndex == nil or rootId == nil or not entityExists(rootId) then return nil end @@ -81,8 +84,8 @@ end --- Queries a node hierarchy ---@param inputNode integer ----@param func function | "function(node, name, depth) end" -function Utility.queryNodeHierarchy(inputNode, func) +---@param func fun(node: integer, name: string, depth: integer) +function EntityUtility.queryNodeHierarchy(inputNode, func) if not type(inputNode) == "number" or not entityExists(inputNode) or func == nil then return end @@ -104,7 +107,7 @@ end ---@param parent integer ---@param md5 boolean ---@return string hash hash of the node hierarchy -function Utility.getNodeHierarchyHash(node, parent, md5) +function EntityUtility.getNodeHierarchyHash(node, parent, md5) if not type(node) == "number" or not entityExists(node) or not type(parent) == "number" or not entityExists(parent) then return string.format("Invalid hash node:%s parent:%s", node, parent) end @@ -125,7 +128,7 @@ function Utility.getNodeHierarchyHash(node, parent, md5) local isDyna = false - Utility.queryNodeHierarchy( + EntityUtility.queryNodeHierarchy( node, function(n, name) local rbt = getRigidBodyType(n) @@ -139,7 +142,7 @@ function Utility.getNodeHierarchyHash(node, parent, md5) rot = floatsToString(getWorldRotation(n)) end local sca = floatsToString(getScale(n)) - local index = Utility.nodeToIndex(node, parent) + local index = EntityUtility.nodeToIndex(node, parent) local vis = getVisibility(n) hash = string.format("%s>->%s->%s->%s->%s->%s->%s->%s", hash, name, pos, rot, sca, index, rbt, vis) nodeCount = nodeCount + 1 @@ -155,7 +158,7 @@ end --- Queries node parents (return false to break the loop) ---@param inputNode integer ---@param func function | "function(node, name, depth) return true end" -function Utility.queryNodeParents(inputNode, func) +function EntityUtility.queryNodeParents(inputNode, func) if not type(inputNode) == "number" or not entityExists(inputNode) or func == nil then return end diff --git a/src/lib/utility/FadeEffect.lua b/src/lib/utility/FadeEffect.lua new file mode 100644 index 0000000..391765c --- /dev/null +++ b/src/lib/utility/FadeEffect.lua @@ -0,0 +1,153 @@ +--- Royal Utility + +---@author Royal Modding +---@version 2.1.0.0 +---@date 17/02/2017 + +---@class FadeEffect +FadeEffect = {} + +FadeEffect.STATES = {} +FadeEffect.STATES.FADEIN = 1 +FadeEffect.STATES.STAY = 2 +FadeEffect.STATES.FADEOUT = 3 +FadeEffect.STATES.IDLE = 4 + +FadeEffect.ALIGNS = {} +FadeEffect.ALIGNS.LEFT = 0 +FadeEffect.ALIGNS.TOP = 0 +FadeEffect.ALIGNS.CENTER = 1 +FadeEffect.ALIGNS.RIGHT = 2 +FadeEffect.ALIGNS.BOTTOM = 2 + +---@class FadeEffectSettings +FadeEffect.defaultSettings = { + color = { + r = 1, + g = 1, + b = 1 + }, + position = { + x = 0.5, + y = 0.5 + }, + align = { + x = FadeEffect.ALIGNS.CENTER, + y = FadeEffect.ALIGNS.CENTER + }, + bold = true, + size = 0.025, + text = "Fade Effect", + shadow = false, + shadowPosition = { + x = 0, + y = 0 + }, + initialAlpha = 0, + statesTime = {1, 1, 1}, + statesAlpha = {1, 1, 0}, + loop = false +} + +---@param settings FadeEffectSettings +---@return FadeEffect +function FadeEffect:new(settings) + if FadeEffect_mt == nil then + FadeEffect_mt = Class(FadeEffect) + end + + ---@type FadeEffect + local fe = setmetatable({}, FadeEffect_mt) + + ---@type FadeEffectSettings + fe.settings = {} + + for k, v in pairs(self.defaultSettings) do + fe.settings[k] = v + end + + for k, v in pairs(settings) do + fe.settings[k] = v + end + + fe:play(fe.settings.text) + fe.state = FadeEffect.STATES.IDLE + + return fe +end + +function FadeEffect:alignText() + self.settings.position.alignedX = self.settings.position.x + self.settings.position.alignedY = self.settings.position.y + if self.settings.align.x == FadeEffect.ALIGNS.CENTER then + self.settings.position.alignedX = self.settings.position.x - (getTextWidth(self.settings.size, self.settings.text) / 2) + end + if self.settings.align.x == FadeEffect.ALIGNS.RIGHT then + self.settings.position.alignedX = self.settings.position.x - getTextWidth(self.settings.size, self.settings.text) + end + if self.settings.align.y == FadeEffect.ALIGNS.CENTER then + self.settings.position.alignedY = self.settings.position.y - (getTextHeight(self.settings.size, self.settings.text) / 2) + end + if self.settings.align.y == FadeEffect.ALIGNS.TOP then + self.settings.position.alignedY = self.settings.position.y - getTextHeight(self.settings.size, self.settings.text) + end +end + +---@param text string +function FadeEffect:setText(text) + self.settings.text = text + self:alignText() +end + +---@param text string +function FadeEffect:play(text) + if text ~= nil then + self.settings.text = text + self:alignText() + end + self.alpha = self.settings.initialAlpha + self.initialAlpha = self.settings.initialAlpha + self.state = FadeEffect.STATES.FADEIN + self.tmpStateTime = 0 +end + +function FadeEffect:stop() + self.state = FadeEffect.STATES.IDLE +end + +function FadeEffect:draw() + if self.state ~= FadeEffect.STATES.IDLE then + setTextBold(self.settings.bold) + if self.settings.shadow then + setTextColor(0, 0, 0, self.alpha) + renderText(self.settings.position.alignedX + self.settings.shadowPosition.x, self.settings.position.alignedY - self.settings.shadowPosition.y, self.settings.size, self.settings.text) + end + setTextColor(self.settings.color.r, self.settings.color.g, self.settings.color.b, self.alpha) + renderText(self.settings.position.alignedX, self.settings.position.alignedY, self.settings.size, self.settings.text) + setTextBold(false) + setTextColor(1, 1, 1, 1) + end +end + +---@param dt number +function FadeEffect:update(dt) + if self.state ~= FadeEffect.STATES.IDLE then + local stateTime = self.settings.statesTime[self.state] * 1000 + if (self.tmpStateTime + dt) >= stateTime then + self.tmpStateTime = (self.tmpStateTime + dt - stateTime) + self.alpha = self.settings.statesAlpha[self.state] + self.initialAlpha = self.alpha + self.state = self.state + 1 + if self.settings.loop and self.state == FadeEffect.STATES.IDLE then + self.state = 1 + end + else + self.tmpStateTime = self.tmpStateTime + dt + end + if self.initialAlpha == self.settings.statesAlpha[self.state] then + self.alpha = self.settings.statesAlpha[self.state] + else + self.alpha = math.abs(self.initialAlpha - (1 / stateTime) * self.tmpStateTime) + end + end +end diff --git a/src/lib/utility/UtilityGameplay.lua b/src/lib/utility/Gameplay.lua similarity index 85% rename from src/lib/utility/UtilityGameplay.lua rename to src/lib/utility/Gameplay.lua index 2ebd446..59a015c 100644 --- a/src/lib/utility/UtilityGameplay.lua +++ b/src/lib/utility/Gameplay.lua @@ -1,14 +1,17 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 05/01/2021 +---@class GameplayUtility +GameplayUtility = GameplayUtility or {} + --- Get value of a trunk (splitshape) ---@param id integer ---@param splitType table ---@return number, number, number, number -function Utility.getTrunkValue(id, splitType) +function GameplayUtility.getTrunkValue(id, splitType) if splitType == nil then splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(id)) end @@ -50,13 +53,15 @@ function Utility.getTrunkValue(id, splitType) defoliageScale = MathUtil.lerp(1, defoliageScale, g_currentMission.missionInfo.economicDifficulty / 3) - return volume * 1000 * splitType.pricePerLiter * qualityScale * defoliageScale * lengthScale, qualityScale, defoliageScale, lengthScale + local sellPriceMultiplier = g_currentMission.missionInfo.sellPriceMultiplier + + return volume * 1000 * splitType.pricePerLiter * qualityScale * defoliageScale * lengthScale * sellPriceMultiplier, qualityScale, defoliageScale, lengthScale end --- Get the farm color ---@param farmId number ---@return number[] -function Utility.getFarmColor(farmId) +function GameplayUtility.getFarmColor(farmId) local farm = g_farmManager:getFarmById(farmId) if farm ~= nil then local color = Farm.COLORS[farm.color] @@ -70,10 +75,10 @@ end --- Get the farm name ---@param farmId number ---@return string -function Utility.getFarmName(farmId) +function GameplayUtility.getFarmName(farmId) local farm = g_farmManager:getFarmById(farmId) if farm ~= nil then return farm.name end - return "N/D" + return nil end diff --git a/src/lib/utility/UtilityInterpolator.lua b/src/lib/utility/Interpolator.lua similarity index 99% rename from src/lib/utility/UtilityInterpolator.lua rename to src/lib/utility/Interpolator.lua index fb04e4a..781d6bb 100644 --- a/src/lib/utility/UtilityInterpolator.lua +++ b/src/lib/utility/Interpolator.lua @@ -1,7 +1,7 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 11/01/2021 --- Interpolators utilities class diff --git a/src/lib/utility/Main.lua b/src/lib/utility/Main.lua index 2357e2a..5126668 100644 --- a/src/lib/utility/Main.lua +++ b/src/lib/utility/Main.lua @@ -1,19 +1,22 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 21/11/2020 --- Initialize RoyalUtility ---@param utilityDirectory string function InitRoyalUtility(utilityDirectory) source(Utils.getFilename("Utility.lua", utilityDirectory)) - source(Utils.getFilename("UtilityDebug.lua", utilityDirectory)) - source(Utils.getFilename("UtilityEntity.lua", utilityDirectory)) - source(Utils.getFilename("UtilityGameplay.lua", utilityDirectory)) - source(Utils.getFilename("UtilityString.lua", utilityDirectory)) - source(Utils.getFilename("UtilityTable.lua", utilityDirectory)) - source(Utils.getFilename("UtilityInterpolator.lua", utilityDirectory)) + source(Utils.getFilename("Debug.lua", utilityDirectory)) + source(Utils.getFilename("Entity.lua", utilityDirectory)) + source(Utils.getFilename("Gameplay.lua", utilityDirectory)) + source(Utils.getFilename("String.lua", utilityDirectory)) + source(Utils.getFilename("Table.lua", utilityDirectory)) + source(Utils.getFilename("Interpolator.lua", utilityDirectory)) + source(Utils.getFilename("Array.lua", utilityDirectory)) + source(Utils.getFilename("FadeEffect.lua", utilityDirectory)) + source(Utils.getFilename("DelayedCallBack.lua", utilityDirectory)) g_logManager:devInfo("Royal Utility loaded successfully by " .. g_currentModName) return true end diff --git a/src/lib/utility/UtilityString.lua b/src/lib/utility/String.lua similarity index 98% rename from src/lib/utility/UtilityString.lua rename to src/lib/utility/String.lua index 0759525..2bc9fda 100644 --- a/src/lib/utility/UtilityString.lua +++ b/src/lib/utility/String.lua @@ -1,7 +1,7 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 05/01/2021 --- String utilities class diff --git a/src/lib/utility/UtilityTable.lua b/src/lib/utility/Table.lua similarity index 91% rename from src/lib/utility/UtilityTable.lua rename to src/lib/utility/Table.lua index 6482312..901d380 100644 --- a/src/lib/utility/UtilityTable.lua +++ b/src/lib/utility/Table.lua @@ -1,7 +1,7 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 05/01/2021 --- Table utilities class @@ -37,7 +37,7 @@ function TableUtility.overwrite(t, newTable) end end ---- Get if a the element exists +--- Get if an element with the given value exists ---@param t table ---@param value any ---@return boolean @@ -50,6 +50,18 @@ function TableUtility.contains(t, value) return false end +--- Map a table to a new table +---@param t table source table +---@param func function | "function(e) return { f1 = e.f1, f2 = e.f2 } end" mapping function +---@return table mapped mapped table +function TableUtility.map(t, func) + local mapped = {} + for k, v in pairs(t) do + mapped[k] = func(v) + end + return mapped +end + --- Get if a matching element exists ---@param t table ---@param func function | "function(e) return true end" diff --git a/src/lib/utility/Utility.lua b/src/lib/utility/Utility.lua index c2e2d83..2caa695 100644 --- a/src/lib/utility/Utility.lua +++ b/src/lib/utility/Utility.lua @@ -1,13 +1,56 @@ --- Royal Utility ---@author Royal Modding ----@version 1.8.0.0 +---@version 2.1.0.0 ---@date 09/11/2020 +---@class RandomInterval +---@field min integer +---@field max integer + --- Utilities class ---@class Utility Utility = Utility or {} +---@overload fun(fieldPartition: FieldPartition) : number, number, number, number, number, number +--- Converts from point-vertex-vertex to point-point-point (pvv to ppp) +---@param startX number | FieldPartition +---@param startZ number +---@param widthX number +---@param widthZ number +---@param heightX number +---@param heightZ number +---@return number +---@return number +---@return number +---@return number +---@return number +---@return number +function Utility.getPPP(startX, startZ, widthX, widthZ, heightX, heightZ) + if type(startX) == "table" then + return startX.x0, startX.z0, startX.widthX + startX.x0, startX.widthZ + startX.z0, startX.heightX + startX.x0, startX.heightZ + startX.z0 + else + return startX, startZ, widthX + startX, widthZ + startZ, heightX + startX, heightZ + startZ + end +end + +--- Converts from point-point-point to point-vertex-vertex (ppp to pvv) +---@param startWorldX number +---@param startWorldZ number +---@param widthWorldX number +---@param widthWorldZ number +---@param heightWorldX number +---@param heightWorldZ number +---@return number +---@return number +---@return number +---@return number +---@return number +---@return number +function Utility.getPVV(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ) + return startWorldX, startWorldZ, widthWorldX - startWorldX, widthWorldZ - startWorldZ, heightWorldX - startWorldX, heightWorldZ - startWorldZ +end + --- Clamp between the given maximum and minimum ---@param minValue number ---@param value number @@ -20,6 +63,36 @@ function Utility.clamp(minValue, value, maxValue) return math.max(minValue, math.min(maxValue, value)) end +--- Get random number sign (1 or -1) +---@return integer +function Utility.randomSign() + if math.random(2) > 1 then + return -1 + else + return 1 + end +end + +--- Pick one random between the **batches** and return a random integer in it's interval +---@param batches RandomInterval[] +---@return integer +function Utility.randomFromBatches(batches) + local batch = batches[math.random(1, #batches)] + return math.random(batch.min, batch.max) +end + +--- Normalize value by given maximum and minimum +---@param minValue number +---@param value number +---@param maxValue number +---@return number +function Utility.normalize(minValue, value, maxValue) + minValue = minValue or 0 + maxValue = maxValue or 1 + value = value or 0.5 + return (value - minValue) / (maxValue - minValue) +end + ---@param target table ---@param name string ---@param newFunc function @@ -50,3 +123,29 @@ end function Utility.prependedFunction(target, name, newFunc) target[name] = Utils.prependedFunction(target[name], newFunc) end + +--- Get elapsed seconds between given date and FS19 release date +---@param year? integer +---@param month? integer +---@param day? integer +---@param hour? integer +---@param minute? integer +---@param second? integer +---@return integer +function Utility.getTimestampAt(year, month, day, hour, minute, second) + year = year or 0 + month = month or 0 + day = day or 0 + hour = hour or 0 + minute = minute or 0 + second = second or 0 + return getDateDiffSeconds(year, month, day, hour, minute, second, 2018, 11, 20, 0, 0, 0) +end + +--- Get elapsed seconds since FS19 release date +---@return integer +function Utility.getTimestamp() + local date = getDate("%Y-%m-%d_%H-%M-%S") + local year, month, day, hour, minute, second = date:match("(%d%d%d%d)-(%d%d)-(%d%d)_(%d%d)-(%d%d)-(%d%d)") + return Utility.getTimestampAt(tonumber(year), tonumber(month), tonumber(day), tonumber(hour), tonumber(minute), tonumber(second)) +end