diff --git a/betterContracts.lua b/betterContracts.lua
index b3b33bd..ffa59d0 100644
--- a/betterContracts.lua
+++ b/betterContracts.lua
@@ -20,9 +20,10 @@
-- v1.2.4.0 26.08.2022 allow for other (future) mission types,
-- fix distorted menu page for different screen aspect ratios,
-- show fruit type to harvest in contracts list
+-- v.1.2.4.1 05.09.2022 indicate leased equipment for active missions
+-- allow clear/new contracts button only for master user
+-- lazyNPC / maxActive contracts now configurable
--=======================================================================================================
-InitRoyalUtility(Utils.getFilename("lib/utility/", g_currentModDirectory))
-InitRoyalMod(Utils.getFilename("lib/rmod/", g_currentModDirectory))
SC = {
FERTILIZER = 1, -- prices index
LIQUIDFERT = 2,
@@ -35,7 +36,7 @@ SC = {
BALING = 4,
TRANSP = 5,
SUPPLY = 6,
- OTHER = 7,
+ OTHER = 7,
-- Gui controls:
CONTROLS = {
npcbox = "npcbox",
@@ -64,23 +65,21 @@ SC = {
helpsort = "helpsort"
}
}
----@class BetterContracts : RoyalMod
-BetterContracts = RoyalMod.new(false, false) --params bool debug, bool sync
-
function debugPrint(text, ...)
if BetterContracts.debug then
Logging.info(text,...)
end
end
+source(Utils.getFilename("RoyalMod.lua", g_currentModDirectory.."scripts/")) -- RoyalMod support functions
+source(Utils.getFilename("Utility.lua", g_currentModDirectory.."scripts/")) -- RoyalMod utility functions
+source(Utils.getFilename("TableUtility.lua", g_currentModDirectory.."scripts/"))-- RoyalMod table utility functions
+---@class BetterContracts : RoyalMod
+BetterContracts = RoyalMod.new(false, true) --params bool debug, bool sync
+
function BetterContracts:initialize()
debugPrint("[%s] initialize(): %s", self.name,self.initialized)
if self.initialized ~= nil then return end -- run only once
- -- check for debug switch in modSettings/
self.modSettings= getUserProfileAppPath().."modSettings/"
- if not self.debug and
- fileExists(self.modSettings.."BetterContracts.debug") then
- self.debug = true
- end
g_missionManager.missionMapNumChannels = 6
self.missionUpdTimeout = 15000
self.missionUpdTimer = 0 -- will also update on frame open of contracts page
@@ -105,32 +104,32 @@ function BetterContracts:initialize()
]]
-- mission.type to BC category: harvest, spread, simple, mow, transp, supply
-- self.typeToCat = {4, 3, 3, 2, 1, 3, 2, 2, 5, 6}
- self.typeToCat = {}
- local function addMapping(name, category)
- local missionType = g_missionManager:getMissionType(name)
- if missionType ~= nil then
- self.typeToCat[missionType.typeId] = category
- end
- end
- addMapping("mow_bale", SC.BALING)
- addMapping("plow", SC.SIMPLE)
- addMapping("cultivate", SC.SIMPLE)
- addMapping("sow", SC.SPREAD)
- addMapping("harvest", SC.HARVEST)
- addMapping("weed", SC.SIMPLE)
- addMapping("spray", SC.SPREAD)
- addMapping("fertilize", SC.SPREAD)
- addMapping("transport", SC.TRANSP)
- addMapping("supplyTransport", SC.SUPPLY)
- addMapping("deadwood", SC.OTHER)
- addMapping("treeTransport", SC.OTHER)
+ self.typeToCat = {}
+ local function addMapping(name, category)
+ local missionType = g_missionManager:getMissionType(name)
+ if missionType ~= nil then
+ self.typeToCat[missionType.typeId] = category
+ end
+ end
+ addMapping("mow_bale", SC.BALING)
+ addMapping("plow", SC.SIMPLE)
+ addMapping("cultivate", SC.SIMPLE)
+ addMapping("sow", SC.SPREAD)
+ addMapping("harvest", SC.HARVEST)
+ addMapping("weed", SC.SIMPLE)
+ addMapping("spray", SC.SPREAD)
+ addMapping("fertilize", SC.SPREAD)
+ addMapping("transport", SC.TRANSP)
+ addMapping("supplyTransport", SC.SUPPLY)
+ addMapping("deadwood", SC.OTHER)
+ addMapping("treeTransport", SC.OTHER)
self.harvest = {} -- harvest missions 1
self.spread = {} -- sow, spray, fertilize 2
self.simple = {} -- plow, cultivate, weed 3
self.mow_bale = {} -- mow/ bale 4
self.transp = {} -- transport 5
self.supply = {} -- supplyTransport mod 6
- self.other = {} -- deadwood, treeTrans 7
+ self.other = {} -- deadwood, treeTrans 7
self.IdToCont = {} -- to find a contract from its mission id
self.fieldToMission = {} -- to find a contract from its field number
self.catHarvest = "BEETHARVESTING BEETVEHICLES CORNHEADERS COTTONVEHICLES CUTTERS POTATOHARVESTING POTATOVEHICLES SUGARCANEHARVESTING SUGARCANEVEHICLES"
@@ -147,6 +146,27 @@ function BetterContracts:initialize()
{"sortprof", g_i18n:getText("SC_sortProf")},
{"sortpmin", g_i18n:getText("SC_sortpMin")}
}
+ self.npcProb = {
+ harvest = 1.0,
+ plowCultivate = 0.3,
+ sow = 0.3,
+ fertilize = 0.3
+ }
+ self.npcType = {}
+ self.lazyNPC = false -- adjust NPC field work activity
+ self.maxActive = 3 -- max active contracts
+
+ if g_server ~= nil then
+ readconfig(self)
+ debugPrint("%s read config: maxActive %d, lazyNPC %s",self.name, self.maxActive, self.lazyNPC)
+ -- to allow multiple missions:
+ if self.maxActive > 0 then
+ MissionManager.ACTIVE_CONTRACT_LIMIT = self.maxActive
+ else -- allow unlimited active missions
+ MissionManager.hasFarmReachedMissionLimit =
+ Utils.overwrittenFunction(nil, function() return false end)
+ end
+ end
local mods = {"FS22_RefreshContracts","FS22_Contracts_Plus"}
if g_modIsLoaded["FS22_RefreshContracts"] then
self.needsRefreshContractsConflictsPrevention = true
@@ -157,14 +177,16 @@ function BetterContracts:initialize()
if g_modIsLoaded["FS22_SupplyTransportContracts"] then
self.supplyTransport = true
end
+
-- to load own mission vehicles:
Utility.overwrittenFunction(MissionManager, "loadMissionVehicles", BetterContracts.loadMissionVehicles)
-- fix AbstractMission:
Utility.overwrittenFunction(AbstractMission, "new", abstractMissionNew)
- -- fix Harvest NPC Mission:
- Utility.overwrittenFunction(FieldManager, "updateNPCField", NPCHarvest)
-
+ -- adjust NPC activity for missions:
+ if self.lazyNPC then -- always false on an MP client
+ Utility.overwrittenFunction(FieldManager, "updateNPCField", NPCHarvest)
+ end
-- get addtnl mission values from server:
Utility.appendedFunction(HarvestMission, "writeStream", BetterContracts.writeStream)
Utility.appendedFunction(HarvestMission, "readStream", BetterContracts.readStream)
@@ -173,17 +195,14 @@ function BetterContracts:initialize()
Utility.appendedFunction(AbstractMission, "writeUpdateStream", BetterContracts.writeUpdateStream)
Utility.appendedFunction(AbstractMission, "readUpdateStream", BetterContracts.readUpdateStream)
-- functions for ingame menu contracts frame:
- InGameMenuContractsFrame.onFrameOpen = Utils.overwrittenFunction(InGameMenuContractsFrame.onFrameOpen, onFrameOpen)
- InGameMenuContractsFrame.onFrameClose = Utils.appendedFunction(InGameMenuContractsFrame.onFrameClose, onFrameClose)
- InGameMenuContractsFrame.updateFarmersBox = Utils.appendedFunction(InGameMenuContractsFrame.updateFarmersBox, updateFarmersBox)
- InGameMenuContractsFrame.populateCellForItemInSection = Utils.appendedFunction(InGameMenuContractsFrame.populateCellForItemInSection, populateCell)
- InGameMenuContractsFrame.updateList = Utils.overwrittenFunction(InGameMenuContractsFrame.updateList, updateList)
- InGameMenuContractsFrame.sortList = Utils.overwrittenFunction(InGameMenuContractsFrame.sortList, sortList)
- -- to allow multiple missions:
- MissionManager.hasFarmReachedMissionLimit =
- Utils.overwrittenFunction(nil,
- function() return false end
- )
+ Utility.overwrittenFunction(InGameMenuContractsFrame, "onFrameOpen", onFrameOpen)
+ Utility.appendedFunction(InGameMenuContractsFrame, "onFrameClose", onFrameClose)
+ Utility.appendedFunction(InGameMenuContractsFrame, "updateFarmersBox", updateFarmersBox)
+ Utility.appendedFunction(InGameMenuContractsFrame, "populateCellForItemInSection", populateCell)
+ Utility.overwrittenFunction(InGameMenuContractsFrame, "updateList", updateList)
+ Utility.overwrittenFunction(InGameMenuContractsFrame, "sortList", sortList)
+ Utility.appendedFunction(InGameMenuContractsFrame, "startContract", startContract)
+ Utility.appendedFunction(InGameMenu, "updateButtonsPanel", updateButtonsPanel)
if self.debug then
addConsoleCommand("printBetterContracts", "Print detail stats for all available missions.", "consoleCommandPrint", self)
addConsoleCommand("gsFieldGenerateMission", "Force generating a new mission for given field", "consoleGenerateFieldMission", g_missionManager)
@@ -193,6 +212,31 @@ function BetterContracts:initialize()
end
end
+function readconfig(self)
+ -- check for config file in modSettings/
+ self.configFile = self.modSettings .. self.name..'.xml'
+ if not fileExists(self.configFile) then -- copy initial config file to /modSettings
+ copyFile(self.directory.."config.xml", self.configFile, true)
+ Logging.info("[%s] wrote initial config file %s", self.name, self.configFile)
+ end
+ -- read config parms:
+ local xmlFile = loadXMLFile("conf", self.configFile)
+ local key = self.name
+ if not self.debug then
+ self.debug = Utils.getNoNil(getXMLBool(xmlFile, key.."#debug"), false)
+ end
+ self.maxActive = Utils.getNoNil(getXMLInt(xmlFile, key.."#maxActive"), 3)
+ self.lazyNPC = Utils.getNoNil(getXMLBool(xmlFile, key.."#lazyNPC"), false)
+ if self.lazyNPC then
+ key = key..".lazyNPC"
+ self.npcType.harvest = Utils.getNoNil(getXMLBool(xmlFile, key.."#harvest"), false)
+ self.npcType.plowCultivate =Utils.getNoNil(getXMLBool(xmlFile, key.."#plowCultivate"), false)
+ self.npcType.sow = Utils.getNoNil(getXMLBool(xmlFile, key.."#sow"), false)
+ self.npcType.fertilize = Utils.getNoNil(getXMLBool(xmlFile, key.."#fertilize"), false)
+ end
+ delete(xmlFile)
+end
+
function BetterContracts:onMissionInitialize(baseDirectory, missionCollaborators)
MissionManager.AI_PRICE_MULTIPLIER = 1.5
MissionManager.MISSION_GENERATION_INTERVAL = 3600000 -- every 1 game hour
@@ -265,7 +309,7 @@ function BetterContracts:onPostLoadMap(mapNode, mapFile)
local rewd = self.frCon.contractsList.cellDatabase.autoCell1:getDescendantByName("reward")
local profit = rewd:clone(self.frCon.contractsList.cellDatabase.autoCell1)
profit.name = "profit"
- profit:setPosition(-110/1920, -12/1080 *g_aspectScaleY) --
+ profit:setPosition(-110/1920 *g_aspectScaleX, -12/1080 *g_aspectScaleY) --
--profit:setTextColor(1, 1, 1, 1)
profit.textBold = false
profit:setVisible(false)
@@ -335,6 +379,26 @@ function BetterContracts:onPostLoadMap(mapNode, mapFile)
self.initialized = true
end
+function BetterContracts:onWriteStream(streamId)
+ -- write to a client when it joins
+ local num = g_missionManager.numTransportTriggers
+ debugPrint("** writing maxActive %d ", self.maxActive)
+ streamWriteUInt8(streamId, self.maxActive)
+ streamWriteBool(streamId, self.debug)
+end
+function BetterContracts:onReadStream(streamId)
+ -- client reads our config when it joins
+ self.maxActive = streamReadUInt8(streamId)
+ self.debug = streamReadBool(streamId)
+ debugPrint("** read maxActive %d, debug %s", self.maxActive, self.debug)
+ -- to allow multiple missions:
+ if self.maxActive > 0 then
+ MissionManager.ACTIVE_CONTRACT_LIMIT = self.maxActive
+ else -- allow unlimited active missions
+ MissionManager.hasFarmReachedMissionLimit =
+ Utils.overwrittenFunction(nil, function() return false end)
+ end
+end
function BetterContracts:onUpdate(dt)
local self = BetterContracts
self.missionUpdTimer = self.missionUpdTimer + dt
@@ -482,18 +546,18 @@ function BetterContracts:addMission(m)
reward = rew
}
table.insert(self.transp, cont)
- elseif cat == SC.OTHER then
- cont = {
- miss = m,
- worktime = 0,
- profit = rew,
- permin = 0,
- reward = rew
- }
- table.insert(self.other, cont)
+ elseif cat == SC.OTHER then
+ cont = {
+ miss = m,
+ worktime = 0,
+ profit = rew,
+ permin = 0,
+ reward = rew
+ }
+ table.insert(self.other, cont)
else
Logging.warning("[%s]: Unknown cat %s in addMission(m)", self.name, cat)
- end
+ end
return {cat, cont}
end
function getPalletType(m)
@@ -514,6 +578,9 @@ function BetterContracts.readStream(self, streamId, connection)
self.depositedLiters = streamReadFloat32(streamId)
end
function BetterContracts.writeUpdateStream(self, streamId, connection, dirtyMask)
+ if self.status == AbstractMission.STATUS_RUNNING then
+ streamWriteBool(streamId, self.spawnedVehicles)
+ end
local fieldPercent, depo = 0., 0.
if self.fieldPercentageDone then fieldPercent = self.fieldPercentageDone end
if self.depositedLiters then depo = self.depositedLiters end
@@ -521,6 +588,9 @@ function BetterContracts.writeUpdateStream(self, streamId, connection, dirtyMask
streamWriteFloat32(streamId, depo)
end
function BetterContracts.readUpdateStream(self, streamId, timestamp, connection)
+ if self.status == AbstractMission.STATUS_RUNNING then
+ self.spawnedVehicles = streamReadBool(streamId)
+ end
self.fieldPercentageDone = streamReadFloat32(streamId)
self.depositedLiters = streamReadFloat32(streamId)
end
@@ -535,32 +605,65 @@ function NPCHarvest(self, superf, field, allowUpdates)
superf(self, field, allowUpdates)
return
end
+ local npc = BetterContracts.npcType
+ local prob= BetterContracts.npcProb
local fruitDesc, harvestReadyState, maxHarvestState, area, total
local x, z = FieldUtil.getMeasurementPositionOfField(field)
if field.fruitType ~= nil then
- -- leave a withered field for plow/ grubber missions
fruitDesc = g_fruitTypeManager:getFruitTypeByIndex(field.fruitType)
+ -- leave a withered field for plow/ grubber missions
+ --[[
local withered = fruitDesc.witheredState
if withered ~= nil then
area, total = FieldUtil.getFruitArea(x - 1, z - 1, x + 1, z - 1, x - 1, z + 1, FieldUtil.FILTER_EMPTY, FieldUtil.FILTER_EMPTY, field.fruitType, withered, withered, 0, 0, 0, false)
- if area > 0.5*total and math.random() < 0.5 then return end
+ if area > 0.5*total and math.random() < 0.3 then return end
end
-
- -- don't let NPCs harvest
- harvestReadyState = fruitDesc.maxHarvestingGrowthState
- if fruitDesc.maxPreparingGrowthState > -1 then
- harvestReadyState = fruitDesc.maxPreparingGrowthState
+ ]]
+ if npc.harvest then
+ -- don't let NPCs harvest
+ harvestReadyState = fruitDesc.maxHarvestingGrowthState
+ if fruitDesc.maxPreparingGrowthState > -1 then
+ harvestReadyState = fruitDesc.maxPreparingGrowthState
+ end
+ maxHarvestState = FieldUtil.getMaxHarvestState(field, field.fruitType)
+ if maxHarvestState == harvestReadyState then return end
end
- maxHarvestState = FieldUtil.getMaxHarvestState(field, field.fruitType)
- if maxHarvestState == harvestReadyState then return end
- -- leave a cut field for plow/ grubber mission
- area, total = FieldUtil.getFruitArea(x - 1, z - 1, x + 1, z - 1, x - 1, z + 1, FieldUtil.FILTER_EMPTY, FieldUtil.FILTER_EMPTY, field.fruitType, fruitDesc.cutState, fruitDesc.cutState, 0, 0, 0, false)
- if area > 0.5 * total and g_currentMission.snowSystem.height < SnowSystem.MIN_LAYER_HEIGHT and math.random() < 0.3 then return end
- else
+ if npc.plowCultivate then
+ -- leave a cut field for plow/ grubber mission
+ area, total = FieldUtil.getFruitArea(x - 1, z - 1, x + 1, z - 1, x - 1, z + 1, FieldUtil.FILTER_EMPTY, FieldUtil.FILTER_EMPTY, field.fruitType, fruitDesc.cutState, fruitDesc.cutState, 0, 0, 0, false)
+ if area > 0.5 * total and
+ g_currentMission.snowSystem.height < SnowSystem.MIN_LAYER_HEIGHT and
+ math.random() < prob.plowCultivate then return end
+ end
+ elseif npc.sow then
-- leave empty (plowed/grubbered) field for sow mission
- if self:getFruitIndexForField(field) ~= nil and math.random() < 0.5 then return end
+ if self:getFruitIndexForField(field) ~= nil and
+ math.random() < prob.sow then return end
end
superf(self, field, allowUpdates)
end
+function updateButtonsPanel(menu, page)
+ -- called by TabbedMenu.onPageChange(), after page:onFrameOpen()
+ local inGameMenu = BetterContracts.gameMenu
+ if page.id == "pageContracts" and inGameMenu.newButton ~= nil then
+ local disable = g_currentMission.missionDynamicInfo.isMultiplayer and
+ not g_currentMission.isMasterUser
+ -- disable if MP and not masterUser:
+ inGameMenu.newButton:setDisabled(disable)
+ inGameMenu.clearButton:setDisabled(disable)
+ end
+end
+function startContract()
+ -- overwrite dialog info box
+ local farmId = g_currentMission:getFarmId()
+ if g_missionManager:hasFarmReachedMissionLimit(farmId)
+ and BetterContracts.maxActive ~= 3 then
+ g_gui:showInfoDialog({
+ visible = true,
+ text = g_i18n:getText("bc_enoughMissions"),
+ dialogType = DialogElement.TYPE_INFO
+ })
+ end
+end
\ No newline at end of file
diff --git a/config.xml b/config.xml
new file mode 100644
index 0000000..3712716
--- /dev/null
+++ b/config.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/lib/rmod/Main.lua b/lib/rmod/Main.lua
deleted file mode 100644
index 23bc8f0..0000000
--- a/lib/rmod/Main.lua
+++ /dev/null
@@ -1,13 +0,0 @@
---- Royal Mod
-
----@author Royal Modding
----@version 1.5.0.0
----@date 03/12/2020
-
---- Initialize RoyalMod
----@param rmodDirectory string
-function InitRoyalMod(rmodDirectory)
- source(Utils.getFilename("RoyalMod.lua", rmodDirectory))
- Logging.devInfo("Royal Mod loaded successfully by " .. g_currentModName)
- return true
-end
diff --git a/lib/rmod/RoyalMod.lua b/lib/rmod/RoyalMod.lua
deleted file mode 100644
index f5668f9..0000000
--- a/lib/rmod/RoyalMod.lua
+++ /dev/null
@@ -1,615 +0,0 @@
---- Royal Mod
-
----@author Royal Modding
----@version 1.5.0.0
----@date 03/12/2020
--- Changelog:
--- v1.0.0.0 03.12.2020 initial by Royal-Modding
--- v1.1.0.0 30.12.2021: (Mmtrx) commented out vehicleTypeManagerFinalizeTypes, different in FS22
-
----@class RoyalMod
----@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.modManagerMod = g_modManager:getModByName(mod.name)
- mod.version = mod.modManagerMod.version
- mod.author = mod.modManagerMod.author
- mod.modEnv = getfenv()
- mod.gameEnv = getfenv(0) -- is redirected to modEnv in FS22
- mod.super = {}
- mod.debug = debug
-
- if mod.debug then
- g_showDevelopmentWarnings = true
- g_addTestCommands = true
- --mod.gameEnv["g_isDevelopmentConsoleScriptModTesting"] = true
- end
-
- mod.super.oldFunctions = {}
-
- ---@param error string
- mod.super.errorHandle = function(error)
- Logging.error("RoyalMod caught error from %s (%s)", mod.name, mod.version)
- Logging.error(error)
- end
-
- mod.super.getSavegameDirectory = function()
- if g_currentMission ~= nil and g_currentMission.missionInfo ~= nil then
- if g_currentMission.missionInfo.savegameDirectory ~= nil then
- return string.format("%s/", g_currentMission.missionInfo.savegameDirectory)
- end
-
- if g_currentMission.missionInfo.savegameIndex ~= nil then
- return string.format("%ssavegame%d/", mod.userProfileDirectory, g_currentMission.missionInfo.savegameIndex)
- end
- end
- return mod.userProfileDirectory
- end
-
- 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
- local time = netGetTime()
- local offset = streamGetWriteOffset(streamId)
- xpcall(mod.onWriteStream, mod.super.errorHandle, mod, streamId)
- offset = streamGetWriteOffset(streamId) - offset
- Logging.devInfo("[%s] Written %.0f bits (%.0f bytes) in %s ms", mod.name, offset, offset / 8, netGetTime() - time)
- 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
- local time = netGetTime()
- local offset = streamGetReadOffset(streamId)
- xpcall(mod.onReadStream, mod.super.errorHandle, mod, streamId)
- offset = streamGetReadOffset(streamId) - offset
- Logging.devInfo("[%s] Read %.0f bits (%.0f bytes) in %s ms", mod.name, offset, offset / 8, netGetTime() - time)
- 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
- xpcall(mod.onUpdateTick, mod.super.errorHandle, mod, dt)
- 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
- xpcall(mod.onWriteUpdateStream, mod.super.errorHandle, mod, streamId, connection, dirtyMask)
- 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
- xpcall(mod.onReadUpdateStream, mod.super.errorHandle, mod, streamId, timestamp, connection)
- end
- 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)
- end
- end
-
- --g_vehicleTypeManager = TypeManager.new("vehicle", "vehicleTypes", "dataS/vehicleTypes.xml", g_specializationManager)
-
- mod.super.oldFunctions.VehicleTypeManagerValidateTypes = TypeManager.validateTypes
- TypeManager.validateTypes = 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
- xpcall(mod.initialize, mod.super.errorHandle, mod)
- end
- mod.super.oldFunctions.VehicleTypeManagerValidateTypes(self, ...)
- end
-
- --[[
- mod.super.oldFunctions.VehicleTypeManagerfinalizeTypes = g_vehicleTypeManager.finalizeTypes
- g_vehicleTypeManager.finalizeTypes = function(self, ...)
- local specs = {}
- local specsBySpec = {}
- local specsByType = {}
- local specsByFunc = {}
-
- local specAddAllowed = true
-
- if mod.onValidateVehicleTypes ~= nil then
- --- Add your specialization to vehicle types here
- xpcall(
- mod.onValidateVehicleTypes,
- mod.super.errorHandle,
- mod,
- self,
- ---@param specName string
- function(specName)
- if not specAddAllowed then
- Logging.devError("[%s] addSpecialization is no more allowed", mod.name)
- return
- 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
- Logging.devError("[%s] addSpecializationBySpecialization is no more allowed", mod.name)
- return
- 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
- Logging.devError("[%s] addSpecializationByVehicleType is no more allowed", mod.name)
- return
- 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
- Logging.devError("[%s] addSpecializationByFunction is no more allowed", mod.name)
- return
- end
- table.insert(specsByFunc, {name = string.format("%s.%s", mod.name, specName), func = func, addedTo = {}})
- end
- )
- end
-
- specAddAllowed = false
-
- -- remove invalid specs
-
- for i, spec in pairs(specs) do
- if g_specializationManager:getSpecializationByName(spec.name) == nil then
- Logging.devError("[%s] Can't find specialization %s", mod.name, spec.name)
- table.remove(specs, i)
- end
- end
-
- for i, spec in pairs(specsBySpec) do
- if g_specializationManager:getSpecializationByName(spec.name) == nil then
- Logging.devError("[%s] Can't find specialization %s", mod.name, spec.name)
- table.remove(specsBySpec, i)
- end
- end
-
- for i, spec in pairs(specsByType) do
- if g_specializationManager:getSpecializationByName(spec.name) == nil then
- Logging.devError("[%s] Can't find specialization %s", mod.name, spec.name)
- table.remove(specsByType, i)
- end
- end
-
- for i, spec in pairs(specsByFunc) do
- if g_specializationManager:getSpecializationByName(spec.name) == nil then
- Logging.devError("[%s] Can't find specialization %s", mod.name, spec.name)
- table.remove(specsByFunc, i)
- end
- end
-
- local vehicleTypesCount = 0
-
- for typeName, typeEntry in pairs(self:getVehicleTypes()) do
- vehicleTypesCount = vehicleTypesCount + 1
-
- -- add "global" specializations
- for _, spec in pairs(specs) do
- if typeEntry.specializationsByName[spec.name] == nil then
- if g_specializationManager:getSpecializationObjectByName(spec.name).prerequisitesPresent(typeEntry.specializations) then
- self:addSpecialization(typeName, spec.name)
- table.insert(spec.addedTo, typeName)
- else
- Logging.devError("[%s] Not all prerequisites of specialization %s are fulfilled by %s", mod.name, spec.name, typeName)
- end
- end
- end
-
- -- add specializations by function
- for _, spec in pairs(specsByFunc) do
- if spec.func(typeEntry) then
- if typeEntry.specializationsByName[spec.name] == nil then
- if g_specializationManager:getSpecializationObjectByName(spec.name).prerequisitesPresent(typeEntry.specializations) then
- self:addSpecialization(typeName, spec.name)
- table.insert(spec.addedTo, typeName)
- else
- Logging.devError("[%s] Not all prerequisites of specialization %s are fulfilled by %s", mod.name, spec.name, typeName)
- end
- end
- end
- end
-
- -- add specializations by required specialization
- for name, _ in pairs(typeEntry.specializationsByName) do
- for _, spec in pairs(specsBySpec) do
- if name == spec.requiredSpecName then
- if typeEntry.specializationsByName[spec.name] == nil then
- if g_specializationManager:getSpecializationObjectByName(spec.name).prerequisitesPresent(typeEntry.specializations) then
- self:addSpecialization(typeName, spec.name)
- table.insert(spec.addedTo, typeName)
- else
- Logging.devError("[%s] Not all prerequisites of specialization %s are fulfilled by %s", mod.name, spec.name, typeName)
- end
- end
- end
- end
- end
-
- -- add specializations by required vehicle type
- for _, spec in pairs(specsByType) do
- if typeName == spec.requiredVehicleTypeName then
- if typeEntry.specializationsByName[spec.name] == nil then
- if g_specializationManager:getSpecializationObjectByName(spec.name).prerequisitesPresent(typeEntry.specializations) then
- self:addSpecialization(typeName, spec.name)
- table.insert(spec.addedTo, typeName)
- else
- Logging.devError("[%s] Not all prerequisites of specialization %s are fulfilled by %s", mod.name, spec.name, typeName)
- end
- end
- end
- end
- end
-
- for _, spec in pairs(specs) do
- if #spec.addedTo <= 25 then
- Logging.devInfo("[%s] %s added to [%s]", mod.name, spec.name, table.concat(spec.addedTo, ", "))
- else
- Logging.devInfo("[%s] %s added to %s vehicle types out of %s", mod.name, spec.name, #spec.addedTo, vehicleTypesCount)
- end
- end
-
- for _, spec in pairs(specsBySpec) do
- if #spec.addedTo <= 25 then
- Logging.devInfo("[%s] %s added to [%s]", mod.name, spec.name, table.concat(spec.addedTo, ", "))
- else
- Logging.devInfo("[%s] %s added to %s vehicle types out of %s", mod.name, spec.name, #spec.addedTo, vehicleTypesCount)
- end
- end
-
- for _, spec in pairs(specsByType) do
- if #spec.addedTo <= 25 then
- Logging.devInfo("[%s] %s added to [%s]", mod.name, spec.name, table.concat(spec.addedTo, ", "))
- else
- Logging.devInfo("[%s] %s added to %s vehicle types out of %s", mod.name, spec.name, #spec.addedTo, vehicleTypesCount)
- end
- end
-
- for _, spec in pairs(specsByFunc) do
- if #spec.addedTo <= 25 then
- Logging.devInfo("[%s] %s added to [%s]", mod.name, spec.name, table.concat(spec.addedTo, ", "))
- else
- Logging.devInfo("[%s] %s added to %s vehicle types out of %s", mod.name, spec.name, #spec.addedTo, vehicleTypesCount)
- end
- end
-
- mod.super.oldFunctions.VehicleTypeManagerfinalizeTypes(self, ...)
- 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, ...)
- if mod.onMissionInitialize ~= nil then
- --- g_currentMission is still nil here
- xpcall(mod.onMissionInitialize, mod.super.errorHandle, mod, baseDirectory, missionCollaborators)
- end
- return mod.super.oldFunctions.Mission00new(self, baseDirectory, customMt, missionCollaborators, ...)
- 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)
- if mod.onSetMissionInfo ~= nil then
- --- g_currentMission is no more nil here
- xpcall(mod.onSetMissionInfo, mod.super.errorHandle, mod, missionInfo, missionDynamicInfo)
- end
- mod.super.oldFunctions.Mission00setMissionInfo(self, missionInfo, missionDynamicInfo, ...)
- 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)
- end
- mod.super.oldFunctions.Mission00load(self, ...)
- 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)
- end
- mod.super.oldFunctions.FSBaseMissionloadMap(self, mapFile, addPhysics, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments, ...)
- 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
- g_currentMission:addOnCreateLoadedObject(mod.super.sync)
- mod.super.sync:register(true)
- end
- if mod.onCreateStartPoint ~= nil then
- xpcall(mod.onCreateStartPoint, mod.super.errorHandle, mod, startPointNode)
- end
- end
-
- mod.super.oldFunctions.BaseMissionloadMapFinished = BaseMission.loadMapFinished
- ---@param self BaseMission
- ---@param mapNode integer
- ---@param arguments table
- ---@param callAsyncCallback boolean
- BaseMission.loadMapFinished = function(self, mapNode, failedReason, arguments, callAsyncCallback, ...)
- mod.mapNode = mapNode
- local mapFile, _, _, _ = unpack(arguments)
- mod.super.oldFunctions.BaseMissionloadMapFinished(self, mapNode, failedReason, arguments, callAsyncCallback, ...)
- if mod.onPostLoadMap ~= nil then
- xpcall(mod.onPostLoadMap, mod.super.errorHandle, mod, mapNode, mapFile)
- end
- 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)
- end
- mod.super.oldFunctions.Mission00loadMission00Finished(self, mapNode, arguments, ...)
- 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)
- end
- mod.super.oldFunctions.Mission00loadVehicles(self, xmlFile, resetVehicles, ...)
- 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)
- end
- mod.super.oldFunctions.Mission00loadItems(self, xmlFile, ...)
- 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)
- end
- mod.super.oldFunctions.Mission00loadOnCreateLoadedObjects(self, xmlFile, ...)
- end
-
- -- not called on clients in MP games
- mod.super.onLoadFinished = function()
- if mod.onLoadFinished ~= nil then
- xpcall(mod.onLoadFinished, mod.super.errorHandle, mod)
- end
- 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)
- end
- mod.super.oldFunctions.Mission00onStartMission(self, ...)
- end
-
- mod.super.onMissionStarted = function(...)
- if mod.onMissionStarted ~= nil then
- xpcall(mod.onMissionStarted, mod.super.errorHandle, mod)
- end
- 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)
- end
- mod.super.oldFunctions.Mission00delete(self, ...)
- 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
- xpcall(mod.onPreSaveSavegame, mod.super.errorHandle, mod, mod.super.getSavegameDirectory(), g_currentMission.missionInfo.savegameIndex)
- end
- mod.super.oldFunctions.FSBaseMissionsaveSavegame(self, ...)
- if mod.onPostSaveSavegame ~= nil then
- -- after all vhicles, items and onCreateObjects are saved
- xpcall(mod.onPostSaveSavegame, mod.super.errorHandle, mod, mod.super.getSavegameDirectory(), g_currentMission.missionInfo.savegameIndex)
- end
- 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
- local success, hlFilename = xpcall(mod.onLoadHelpLine, mod.super.errorHandle, mod)
- if success and hlFilename ~= nil and type(hlFilename) == "string" and hlFilename ~= "" then
- self:loadFromXML(hlFilename)
- for ci = 1, #self.categories do
- local category = self.categories[ci]
- for pi = 1, #category.pages do
- local page = category.pages[pi]
- for ii = 1, #page.items do
- local item = page.items[ii]
- if item.type == HelpLineManager.ITEM_TYPE.IMAGE then
- if item.value:sub(1, 10) == "$rmModDir/" then
- item.value = "$" .. mod.directory .. item.value:sub(11)
- end
- end
- end
- end
- end
- end
- end
- return true
- end
- end
-
- addModEventListener(mod.super)
- return mod
-end
diff --git a/lib/utility/Array.lua b/lib/utility/Array.lua
deleted file mode 100644
index 2174e63..0000000
--- a/lib/utility/Array.lua
+++ /dev/null
@@ -1,48 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.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/lib/utility/Debug.lua b/lib/utility/Debug.lua
deleted file mode 100644
index 27a8f19..0000000
--- a/lib/utility/Debug.lua
+++ /dev/null
@@ -1,337 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.0
----@date 05/01/2021
-
----@class DebugUtility
-DebugUtility = DebugUtility or {}
-
---- Render a table (for debugging purpose)
----@param posX number
----@param posY number
----@param textSize number
----@param inputTable table
----@param maxDepth integer|nil
----@param hideFunc boolean|nil
-function DebugUtility.renderTable(posX, posY, textSize, inputTable, maxDepth, hideFunc)
- inputTable = inputTable or {tableIs = "nil"}
- hideFunc = hideFunc or false
- maxDepth = maxDepth or 2
-
- local function renderTableRecursively(x, t, depth, i)
- if depth >= maxDepth then
- return i
- end
- for k, v in pairs(t) do
- local vType = type(v)
- if not hideFunc or vType ~= "function" then
- local offset = i * textSize * 1.05
- setTextAlignment(RenderText.ALIGN_RIGHT)
- renderText(x, posY - offset, textSize, tostring(k) .. " :")
- setTextAlignment(RenderText.ALIGN_LEFT)
- if vType ~= "table" then
- renderText(x, posY - offset, textSize, " " .. tostring(v))
- end
- i = i + 1
- if vType == "table" then
- i = renderTableRecursively(x + textSize * 1.8, v, depth + 1, i)
- end
- end
- end
- return i
- end
-
- local i = 0
- setTextColor(1, 1, 1, 1)
- setTextBold(false)
- textSize = getCorrectTextSize(textSize)
- for k, v in pairs(inputTable) do
- local vType = type(v)
- if not hideFunc or vType ~= "function" then
- local offset = i * textSize * 1.05
- setTextAlignment(RenderText.ALIGN_RIGHT)
- renderText(posX, posY - offset, textSize, tostring(k) .. " :")
- setTextAlignment(RenderText.ALIGN_LEFT)
- if vType ~= "table" then
- renderText(posX, posY - offset, textSize, " " .. tostring(v))
- end
- i = i + 1
- if vType == "table" then
- i = renderTableRecursively(posX + textSize * 1.8, v, 1, i)
- end
- end
- end
-end
-
---- Render a node hierarchy (for debugging purpose)
----@param posX number
----@param posY number
----@param textSize number
----@param inputNode integer
----@param maxDepth integer|nil
-function DebugUtility.renderNodeHierarchy(posX, posY, textSize, inputNode, maxDepth)
- if inputNode == nil or inputNode == 0 then
- return
- end
- if type(inputNode) == "number" and entityExists(inputNode) then
- maxDepth = maxDepth or math.huge
-
- local function renderNodeHierarchyRecursively(x, node, depth, i)
- if depth >= maxDepth then
- return i
- end
- local offset = i * textSize * 1.05
- 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
- i = renderNodeHierarchyRecursively(x + textSize * 1.8, getChildAt(node, ni), depth + 1, i)
- end
- return i
- end
-
- local i = 1
- setTextColor(1, 1, 1, 1)
- setTextBold(false)
- textSize = getCorrectTextSize(textSize)
- 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)
- end
- end
-end
-
---- Draw a rectangle (for debugging purpose)
----@param node integer ref node
----@param minX number minX
----@param maxX number maxX
----@param minZ number minZ
----@param maxZ number maxZ
----@param yOffset number height offset
----@param alignToGround boolean alignToGround
----@param r number r
----@param g number g
----@param b number b
----@param ar number r color if active
----@param ag number g color if active
----@param ab number b color if active
----@param active boolean 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
-
- local leftFrontX, leftFrontY, leftFrontZ = localToWorld(node, minX, yOffset, maxZ)
- local rightFrontX, rightFrontY, rightFrontZ = localToWorld(node, maxX, yOffset, maxZ)
-
- local leftBackX, leftBackY, leftBackZ = localToWorld(node, minX, yOffset, minZ)
- local rightBackX, rightBackY, rightBackZ = localToWorld(node, maxX, yOffset, minZ)
-
- if alignToGround then
- leftFrontY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, leftFrontX, 0, leftFrontZ) + yOffset + 0.1
- rightFrontY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rightFrontX, 0, rightFrontZ) + yOffset + 0.1
- leftBackY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, leftBackX, 0, leftBackZ) + yOffset + 0.1
- rightBackY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rightBackX, 0, rightBackZ) + yOffset + 0.1
- end
-
- drawDebugLine(leftFrontX, leftFrontY, leftFrontZ, r, g, b, rightFrontX, rightFrontY, rightFrontZ, r, g, b)
- drawDebugLine(rightFrontX, rightFrontY, rightFrontZ, r, g, b, rightBackX, rightBackY, rightBackZ, r, g, b)
- drawDebugLine(rightBackX, rightBackY, rightBackZ, r, g, b, leftBackX, leftBackY, leftBackZ, r, g, b)
- drawDebugLine(leftBackX, leftBackY, leftBackZ, r, g, b, leftFrontX, leftFrontY, leftFrontZ, r, g, b)
-end
-
---- Draw a cube (for debugging purpose)
----@param node integer|table ref node or ref position
----@param size number size
----@param r number r
----@param g number g
----@param b number b
----@param ar number r color if active
----@param ag number g color if active
----@param ab number b color if active
----@param active boolean active?
-function DebugUtility.drawDebugCube(node, size, r, g, b, ar, ag, ab, active)
- if active then
- r, g, b = ar, ag, ab
- end
-
- local x, y, z = 0, 0, 0
- if type(node) == "table" then
- x = node[1]
- y = node[2]
- z = node[3]
- else
- x, y, z = getWorldTranslation(node)
- end
-
- local offsets = size / 2
- local corners = {}
- corners[1] = {x + offsets, y + offsets, z + offsets}
- corners[2] = {x + offsets, y + offsets, z - offsets}
- corners[3] = {x + offsets, y - offsets, z - offsets}
- corners[4] = {x + offsets, y - offsets, z + offsets}
- corners[5] = {x - offsets, y + offsets, z + offsets}
- corners[6] = {x - offsets, y + offsets, z - offsets}
- corners[7] = {x - offsets, y - offsets, z - offsets}
- corners[8] = {x - offsets, y - offsets, z + offsets}
-
- 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)
-
- 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)
----@param c1 table first corner position {x, y, z}
----@param c2 table second corner position {x, y, z}
----@param c3 table third corner position {x, y, z}
----@param r number r
----@param g number g
----@param b number 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
-
---- Draw a triangle (for debugging purpose)
----@param p1 table first point position {x, y, z}
----@param p2 table second point position {x, y, z}
----@param r number r
----@param g number g
----@param b number 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
-
---- Render an AnimCurve (for debugging purpose)
----@param x number x position
----@param y number y position
----@param w number width
----@param h number height
----@param curve table AnimCurve object
----@param numPointsToShow? integer number of points to render
-function DebugUtility.renderAnimCurve(x, y, w, h, curve, numPointsToShow)
- local graph = curve.debugGraph
- local numPoints = numPointsToShow or #curve.keyframes
- local minTime = 0
- local maxTime = curve.maxTime
- if graph == nil then
- if numPointsToShow == nil then
- graph = Graph:new(numPoints, x, y, w, h, 0, 0.0001, true, "", Graph.STYLE_LINES)
- graph:setColor(1, 0, 0, 1)
- for i, kf in ipairs(curve.keyframes) do
- local v = curve:get(kf.time)
- graph:setValue(i, v)
- graph:setXPosition(i, (kf.time - minTime) / (maxTime - minTime))
- graph.maxValue = math.max(graph.maxValue, v)
- end
- else
- graph = Graph:new(numPoints + 1, x, y, w, h, 0, 0.0001, true, "", Graph.STYLE_LINES)
- graph:setColor(1, 0, 0, 1)
- for s = 1, numPoints + 1 do
- local i = s - 1
- local v = curve:get(minTime + (maxTime - minTime) * (i / numPoints))
- graph:setValue(s, v)
- graph.maxValue = math.max(graph.maxValue, v)
- end
- end
- curve.debugGraph = graph
- end
- graph:draw()
-end
-
---- Get the loading speed meter object
----@return LoadingSpeedMeter loadingSpeedMeter
-function DebugUtility.getVehicleLoadingSpeedMeter()
- if DebugUtility.loadingSpeedMeter == nil then
- ---@class LoadingSpeedMeter
- DebugUtility.loadingSpeedMeter = {}
- DebugUtility.loadingSpeedMeter.vehicles = {}
- DebugUtility.loadingSpeedMeter.filters = {}
- --- Add a new filter
- ---@param filterFunction function | 'function(vehicleData) return true, "meter name" end'
- DebugUtility.loadingSpeedMeter.addFilter = function(filterFunction)
- table.insert(DebugUtility.loadingSpeedMeter.filters, filterFunction)
- end
- Utility.overwrittenFunction(
- Vehicle,
- "load",
- function(self, superFunc, vehicleData, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments)
- local smEnabled = false
- local smName = ""
- for _, filter in ipairs(DebugUtility.loadingSpeedMeter.filters) do
- smEnabled, smName = filter(vehicleData)
- if smEnabled then
- break
- end
- end
-
- if smEnabled then
- 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
- 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
- )
- Utility.overwrittenStaticFunction(
- SpecializationUtil,
- "raiseEvent",
- function(superFunc, vehicle, eventName, ...)
- if DebugUtility.loadingSpeedMeter.vehicles[vehicle] ~= nil then
- if eventName == "onPreLoad" then
- DebugUtility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime = getTimeSec()
- superFunc(vehicle, eventName, ...)
- DebugUtility.loadingSpeedMeter.vehicles[vehicle].preLoadTime = getTimeSec() - DebugUtility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime
- end
- if eventName == "onLoad" then
- DebugUtility.loadingSpeedMeter.vehicles[vehicle].loadStartTime = getTimeSec()
- superFunc(vehicle, eventName, ...)
- DebugUtility.loadingSpeedMeter.vehicles[vehicle].loadTime = getTimeSec() - DebugUtility.loadingSpeedMeter.vehicles[vehicle].loadStartTime
- end
- if eventName == "onPostLoad" then
- DebugUtility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime = getTimeSec()
- superFunc(vehicle, eventName, ...)
- DebugUtility.loadingSpeedMeter.vehicles[vehicle].postLoadTime = getTimeSec() - DebugUtility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime
- end
- else
- superFunc(vehicle, eventName, ...)
- end
- end
- )
- end
- return DebugUtility.loadingSpeedMeter
-end
diff --git a/lib/utility/DelayedCallBack.lua b/lib/utility/DelayedCallBack.lua
deleted file mode 100644
index 6353b9d..0000000
--- a/lib/utility/DelayedCallBack.lua
+++ /dev/null
@@ -1,63 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.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/lib/utility/Entity.lua b/lib/utility/Entity.lua
deleted file mode 100644
index 66d7ca0..0000000
--- a/lib/utility/Entity.lua
+++ /dev/null
@@ -1,174 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.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 EntityUtility.getObjectClass(objectId)
- if objectId == nil then
- return nil, nil
- end
- for name, id in pairs(ClassIds) do
- if getHasClassId(objectId, id) then
- return id, name
- end
- end
-end
-
---- Determines whether a node is a child of a given node
----@param childNode integer
----@param parentNode integer
----@return boolean
-function EntityUtility.isChildOf(childNode, parentNode)
- if childNode == nil or childNode == 0 or parentNode == nil or parentNode == 0 then
- return false
- end
- local pNode = getParent(childNode)
- while pNode ~= 0 do
- if pNode == parentNode then
- return true
- end
- pNode = getParent(pNode)
- end
- return false
-end
-
---- Get the node index relative to root node
----@param nodeId integer id of node
----@param rootId integer id of root node
----@return string nodeIndex index of node
-function EntityUtility.nodeToIndex(nodeId, rootId)
- local index = ""
- 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
- index = string.format("%s|%s", getChildIndex(pNode), index)
- pNode = getParent(pNode)
- end
- end
- return index
-end
-
---- Get a node id by an index
----@param nodeIndex string index of node
----@param rootId integer id of root node
----@return integer nodeId id of node
-function EntityUtility.indexToNode(nodeIndex, rootId)
- if nodeIndex == nil or rootId == nil or not entityExists(rootId) then
- return nil
- end
- local objectId = rootId
- local indexes = StringUtility.split(nodeIndex, "|")
- for _, index in pairs(indexes) do
- index = tonumber(index)
- if type(index) == "number" then
- if getNumOfChildren(objectId) >= index then
- objectId = getChildAt(objectId, index)
- else
- return nil
- end
- else
- return nil
- end
- end
- return objectId
-end
-
---- Queries a node hierarchy
----@param inputNode integer
----@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
- local function queryNodeHierarchyRecursively(node, depth)
- func(node, getName(node), depth)
- for i = 0, getNumOfChildren(node) - 1 do
- queryNodeHierarchyRecursively(getChildAt(node, i), depth + 1)
- end
- end
- local depth = 1
- func(inputNode, getName(inputNode), depth)
- for i = 0, getNumOfChildren(inputNode) - 1 do
- queryNodeHierarchyRecursively(getChildAt(inputNode, i), depth + 1)
- end
-end
-
---- Get the hash of a node hierarchy
----@param node integer
----@param parent integer
----@param md5 boolean
----@return string hash hash of the node hierarchy
-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
- local hash = ""
- local nodeCount = 0
-
- local floatsToString = function(...)
- local ret = {}
- for i, v in ipairs({...}) do
- local tV = string.format("%.1f", v)
- if tV == "-0.0" then
- tV = "0.0"
- end
- ret[i] = tV
- end
- return table.concat(ret, "|")
- end
-
- local isDyna = false
-
- EntityUtility.queryNodeHierarchy(
- node,
- function(n, name)
- local rbt = getRigidBodyType(n)
- if rbt == "Dynamic" then
- isDyna = true
- end
- local pos = ""
- local rot = ""
- if not isDyna then
- pos = floatsToString(getWorldTranslation(n))
- rot = floatsToString(getWorldRotation(n))
- end
- local sca = floatsToString(getScale(n))
- 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
- end
- )
- if md5 then
- return getMD5(string.format("%s%s_dMs5AsHZWy", hash, nodeCount))
- else
- return string.format("%s___%s", hash, nodeCount)
- end
-end
-
---- Queries node parents (return false to break the loop)
----@param inputNode integer
----@param func function | "function(node, name, depth) return true end"
-function EntityUtility.queryNodeParents(inputNode, func)
- if not type(inputNode) == "number" or not entityExists(inputNode) or func == nil then
- return
- end
- local depth = 1
- local pNode = inputNode
- while pNode ~= 0 do
- if not func(pNode, getName(pNode), depth) then
- break
- end
- pNode = getParent(pNode)
- depth = depth + 1
- end
-end
diff --git a/lib/utility/FadeEffect.lua b/lib/utility/FadeEffect.lua
deleted file mode 100644
index 79cf574..0000000
--- a/lib/utility/FadeEffect.lua
+++ /dev/null
@@ -1,153 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.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/lib/utility/Gameplay.lua b/lib/utility/Gameplay.lua
deleted file mode 100644
index 21fc540..0000000
--- a/lib/utility/Gameplay.lua
+++ /dev/null
@@ -1,84 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.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 GameplayUtility.getTrunkValue(id, splitType)
- if splitType == nil then
- splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(id))
- end
-
- if splitType == nil or splitType.pricePerLiter <= 0 then
- return 0
- end
-
- local volume = getVolume(id)
- local qualityScale = 1
- local lengthScale = 1
- local defoliageScale = 1
- local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(id)
-
- if sizeX ~= nil and volume > 0 then
- local bvVolume = sizeX * sizeY * sizeZ
- local volumeRatio = bvVolume / volume
- local volumeQuality = 1 - math.sqrt(MathUtil.clamp((volumeRatio - 3) / 7, 0, 1)) * 0.95 -- ratio <= 3: 100%, ratio >= 10: 5%
- local convexityQuality = 1 - MathUtil.clamp((numConvexes - 2) / (6 - 2), 0, 1) * 0.95
- -- 0-2: 100%:, >= 6: 5%
-
- local maxSize = math.max(sizeX, math.max(sizeY, sizeZ))
- -- 1m: 60%, 6-11m: 120%, 19m: 60%
- if maxSize < 11 then
- lengthScale = 0.6 + math.min(math.max((maxSize - 1) / 5, 0), 1) * 0.6
- else
- lengthScale = 1.2 - math.min(math.max((maxSize - 11) / 8, 0), 1) * 0.6
- end
-
- local minQuality = math.min(convexityQuality, volumeQuality)
- local maxQuality = math.max(convexityQuality, volumeQuality)
- qualityScale = minQuality + (maxQuality - minQuality) * 0.3 -- use 70% of min quality
-
- defoliageScale = 1 - math.min(numAttachments / 15, 1) * 0.8 -- #attachments 0: 100%, >=15: 20%
- end
-
- -- Only take 33% into account of the quality criteria on easy difficulty
- qualityScale = MathUtil.lerp(1, qualityScale, g_currentMission.missionInfo.economicDifficulty / 3)
-
- defoliageScale = MathUtil.lerp(1, defoliageScale, g_currentMission.missionInfo.economicDifficulty / 3)
-
- 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 GameplayUtility.getFarmColor(farmId)
- local farm = g_farmManager:getFarmById(farmId)
- if farm ~= nil then
- local color = Farm.COLORS[farm.color]
- if color ~= nil then
- return color
- end
- end
- return {1, 1, 1, 1}
-end
-
---- Get the farm name
----@param farmId number
----@return string
-function GameplayUtility.getFarmName(farmId)
- local farm = g_farmManager:getFarmById(farmId)
- if farm ~= nil then
- return farm.name
- end
- return nil
-end
diff --git a/lib/utility/Interpolator.lua b/lib/utility/Interpolator.lua
deleted file mode 100644
index 7a1923a..0000000
--- a/lib/utility/Interpolator.lua
+++ /dev/null
@@ -1,113 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.0
----@date 11/01/2021
-
---- Interpolators utilities class
----@class InterpolatorUtility
-InterpolatorUtility = InterpolatorUtility or {}
-
-function InterpolatorUtility.exponential11Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 2
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential11InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 2
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential12Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 3
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential12InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 3
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential13Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 4
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential13InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 4
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential14Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 8
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential14InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 8
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential21Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = (2 ^ alpha) - 1
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential21InvertedInterpolator1(first, second, alpha)
- alpha = (2 ^ alpha) - 1
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential22Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = ((3 ^ alpha) - 1) / 2
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential22InvertedInterpolator1(first, second, alpha)
- alpha = ((3 ^ alpha) - 1) / 2
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential23Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = ((4 ^ alpha) - 1) / 3
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential23InvertedInterpolator1(first, second, alpha)
- alpha = ((4 ^ alpha) - 1) / 3
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential24Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = ((8 ^ alpha) - 1) / 7
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential24InvertedInterpolator1(first, second, alpha)
- alpha = ((8 ^ alpha) - 1) / 7
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.catmullRomInterpolator1(first, second, beforeFirst, afterSecond, alpha)
- alpha = 1 - alpha
- local alpha2 = alpha * alpha
- local alpha3 = alpha2 * alpha
-
- if beforeFirst == nil then
- beforeFirst = {2 * first[1] - second[1]}
- end
-
- if afterSecond == nil then
- afterSecond = {2 * second[1] - first[1]}
- end
-
- return 0.5 * ((2 * first[1]) + (-beforeFirst[1] + second[1]) * alpha + (2 * beforeFirst[1] - 5 * first[1] + 4 * second[1] - afterSecond[1]) * alpha2 + (-beforeFirst[1] + 3 * first[1] - 3 * second[1] + afterSecond[1]) * alpha3)
-end
diff --git a/lib/utility/Main.lua b/lib/utility/Main.lua
deleted file mode 100644
index 2fb791d..0000000
--- a/lib/utility/Main.lua
+++ /dev/null
@@ -1,22 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.0
----@date 21/11/2020
-
---- Initialize RoyalUtility
----@param utilityDirectory string
-function InitRoyalUtility(utilityDirectory)
- source(Utils.getFilename("Utility.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))
- Logging.devInfo("Royal Utility loaded successfully by " .. g_currentModName)
- return true
-end
diff --git a/lib/utility/String.lua b/lib/utility/String.lua
deleted file mode 100644
index 9fc16a9..0000000
--- a/lib/utility/String.lua
+++ /dev/null
@@ -1,114 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.0
----@date 05/01/2021
-
---- String utilities class
----@class StringUtility
-StringUtility = StringUtility or {}
-
---- Chars available for randomizing
----@type string[]
-StringUtility.randomCharset = {
- "0",
- "1",
- "2",
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z"
-}
-
---- Get random string
----@param length number
----@return string
-function StringUtility.random(length)
- length = length or 1
- if length <= 0 then
- return ""
- end
- return StringUtility.random(length - 1) .. StringUtility.randomCharset[math.random(1, #StringUtility.randomCharset)]
-end
-
---- Split a string
----@param s string
----@param sep string
----@return string[]
-function StringUtility.split(s, sep)
- sep = sep or ":"
- local fields = {}
- local pattern = string.format("([^%s]+)", sep)
- s:gsub(
- pattern,
- function(c)
- fields[#fields + 1] = c
- end
- )
- return fields
-end
-
---- Get translated text
----@param key string text key prefixed with "$l10n_"
----@return string text translated text
-function StringUtility.parseI18NText(key)
- if key == nil then
- return ""
- end
- return g_i18n:convertText(key)
-end
diff --git a/lib/utility/Table.lua b/lib/utility/Table.lua
deleted file mode 100644
index dbdaad0..0000000
--- a/lib/utility/Table.lua
+++ /dev/null
@@ -1,236 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 2.1.1.0
----@date 05/01/2021
-
---- Table utilities class
----@class TableUtility
-TableUtility = TableUtility or {}
-
---- Clone a table
----@param t table
----@return table
-function TableUtility.clone(t)
- local copy = {}
- for k, v in pairs(t) do
- if type(v) == "table" then
- v = TableUtility.clone(v)
- end
- copy[k] = v
- end
- return copy
-end
-
---- Overwrite a table
----@param t table
----@param newTable table
----@return table
-function TableUtility.overwrite(t, newTable)
- t = t or {}
- for k, v in pairs(newTable) do
- if type(v) == "table" then
- TableUtility.overwrite(t[k], v)
- else
- t[k] = v
- end
- end
-end
-
---- Get if an element with the given value exists
----@param t table
----@param value any
----@return boolean
-function TableUtility.contains(t, value)
- for _, v in pairs(t) do
- if v == value then
- return true
- end
- end
- 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"
----@return boolean
-function TableUtility.f_contains(t, func)
- for _, v in pairs(t) do
- if func(v) then
- return true
- end
- end
- return false
-end
-
---- Get the index of element
----@param t table
----@param value any
----@return integer|nil
-function TableUtility.indexOf(t, value)
- for k, v in pairs(t) do
- if v == value then
- return k
- end
- end
- return nil
-end
-
---- Get the index of matching element
----@param t table
----@param func function | "function(e) return true end"
----@return integer|nil
-function TableUtility.f_indexOf(t, func)
- for k, v in pairs(t) do
- if func(v) then
- return k
- end
- end
- return nil
-end
-
---- Get the matching element
----@param t table
----@param func function | "function(e) return true end"
----@return any|nil
-function TableUtility.f_find(t, func)
- for _, v in pairs(t) do
- if func(v) then
- return v
- end
- end
- return nil
-end
-
---- Get a new table with matching elements
----@param t table
----@param func function | "function(e) return true end"
----@return table
-function TableUtility.f_filter(t, func)
- local new = {}
- for _, v in pairs(t) do
- if func(v) then
- table.insert(new, v)
- end
- end
- return new
-end
-
---- Remove matching element
----@param t table
----@param value any
----@return boolean
-function TableUtility.removeValue(t, value)
- for k, v in pairs(t) do
- if v == value then
- table.remove(t, k)
- return true
- end
- end
- return false
-end
-
---- Remove matching elements
----@param t table
----@param func function | "function(e) return true end"
-function TableUtility.f_remove(t, func)
- for k, v in pairs(t) do
- if func(v) then
- table.remove(t, k)
- end
- end
-end
-
---- Count occurrences
----@param t table
----@return integer
-function TableUtility.count(t)
- local c = 0
- if t ~= nil then
- for _ in pairs(t) do
- c = c + 1
- end
- end
- return c
-end
-
---- Count occurrences
----@param t table
----@param func function | "function(e) return true end"
----@return integer
-function TableUtility.f_count(t, func)
- local c = 0
- if t ~= nil then
- for _, v in pairs(t) do
- if func(v) then
- c = c + 1
- end
- end
- end
- return c
-end
-
---- Concat and return nil if the sring is empty
----@param t table
----@param sep string
----@param i integer
----@param j integer
----@return string|nil
-function TableUtility.concatNil(t, sep, i, j)
- local res = table.concat(t, sep, i, j)
- if res == "" then
- res = nil
- end
- return res
-end
-
----@param table1 table
----@param table2 table
----@return boolean
-function TableUtility.equals(table1, table2)
- if table1 == table2 then
- return true
- end
-
- local table1Type = type(table1)
-
- local table2Type = type(table2)
-
- if table1Type ~= table2Type then
- return false
- end
-
- if table1Type ~= "table" then
- return false
- end
-
- local keySet = {}
-
- for key1, value1 in pairs(table1) do
- local value2 = table2[key1]
- if value2 == nil or TableUtility.equals(value1, value2) == false then
- return false
- end
- keySet[key1] = true
- end
-
- for key2, _ in pairs(table2) do
- if not keySet[key2] then
- return false
- end
- end
-
- return true
-end
diff --git a/lib/utility/UtilityArray.lua b/lib/utility/UtilityArray.lua
deleted file mode 100644
index 1662ea8..0000000
--- a/lib/utility/UtilityArray.lua
+++ /dev/null
@@ -1,46 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 1.8.1.0
----@date 26/02/2021
-
----@alias Array table tables 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 Array
-function ArrayUtility.remove(array, removeFunc)
- local moveAt, length = 1, #array
- for index = 1, length do
- if removeFunc(array, index, moveAt) then
- array[index] = nil
- 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 array
-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/lib/utility/UtilityDebug.lua b/lib/utility/UtilityDebug.lua
deleted file mode 100644
index 7b7282c..0000000
--- a/lib/utility/UtilityDebug.lua
+++ /dev/null
@@ -1,334 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 1.8.1.0
----@date 05/01/2021
-
---- Render a table (for debugging purpose)
----@param posX number
----@param posY number
----@param textSize number
----@param inputTable table
----@param maxDepth integer|nil
----@param hideFunc boolean|nil
-function Utility.renderTable(posX, posY, textSize, inputTable, maxDepth, hideFunc)
- inputTable = inputTable or {tableIs = "nil"}
- hideFunc = hideFunc or false
- maxDepth = maxDepth or 2
-
- local function renderTableRecursively(x, t, depth, i)
- if depth >= maxDepth then
- return i
- end
- for k, v in pairs(t) do
- local vType = type(v)
- if not hideFunc or vType ~= "function" then
- local offset = i * textSize * 1.05
- setTextAlignment(RenderText.ALIGN_RIGHT)
- renderText(x, posY - offset, textSize, tostring(k) .. " :")
- setTextAlignment(RenderText.ALIGN_LEFT)
- if vType ~= "table" then
- renderText(x, posY - offset, textSize, " " .. tostring(v))
- end
- i = i + 1
- if vType == "table" then
- i = renderTableRecursively(x + textSize * 1.8, v, depth + 1, i)
- end
- end
- end
- return i
- end
-
- local i = 0
- setTextColor(1, 1, 1, 1)
- setTextBold(false)
- textSize = getCorrectTextSize(textSize)
- for k, v in pairs(inputTable) do
- local vType = type(v)
- if not hideFunc or vType ~= "function" then
- local offset = i * textSize * 1.05
- setTextAlignment(RenderText.ALIGN_RIGHT)
- renderText(posX, posY - offset, textSize, tostring(k) .. " :")
- setTextAlignment(RenderText.ALIGN_LEFT)
- if vType ~= "table" then
- renderText(posX, posY - offset, textSize, " " .. tostring(v))
- end
- i = i + 1
- if vType == "table" then
- i = renderTableRecursively(posX + textSize * 1.8, v, 1, i)
- end
- end
- end
-end
-
---- Render a node hierarchy (for debugging purpose)
----@param posX number
----@param posY number
----@param textSize number
----@param inputNode integer
----@param maxDepth integer|nil
-function Utility.renderNodeHierarchy(posX, posY, textSize, inputNode, maxDepth)
- if inputNode == nil or inputNode == 0 then
- return
- end
- if type(inputNode) == "number" and entityExists(inputNode) then
- maxDepth = maxDepth or math.huge
-
- local function renderNodeHierarchyRecursively(x, node, depth, i)
- if depth >= maxDepth then
- return i
- end
- local offset = i * textSize * 1.05
- local _, className = Utility.getObjectClass(node)
- renderText(x, posY - offset, textSize, string.format("%s (%s)", getName(node), className))
- i = i + 1
- for ni = 0, getNumOfChildren(node) - 1 do
- i = renderNodeHierarchyRecursively(x + textSize * 1.8, getChildAt(node, ni), depth + 1, i)
- end
- return i
- end
-
- local i = 1
- setTextColor(1, 1, 1, 1)
- setTextBold(false)
- textSize = getCorrectTextSize(textSize)
- local _, className = Utility.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)
- end
- end
-end
-
---- Draw a rectangle (for debugging purpose)
----@param node integer ref node
----@param minX number minX
----@param maxX number maxX
----@param minZ number minZ
----@param maxZ number maxZ
----@param yOffset number height offset
----@param alignToGround boolean alignToGround
----@param r number r
----@param g number g
----@param b number b
----@param ar number r color if active
----@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)
- if active then
- r, g, b = ar, ag, ab
- end
-
- local leftFrontX, leftFrontY, leftFrontZ = localToWorld(node, minX, yOffset, maxZ)
- local rightFrontX, rightFrontY, rightFrontZ = localToWorld(node, maxX, yOffset, maxZ)
-
- local leftBackX, leftBackY, leftBackZ = localToWorld(node, minX, yOffset, minZ)
- local rightBackX, rightBackY, rightBackZ = localToWorld(node, maxX, yOffset, minZ)
-
- if alignToGround then
- leftFrontY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, leftFrontX, 0, leftFrontZ) + yOffset + 0.1
- rightFrontY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rightFrontX, 0, rightFrontZ) + yOffset + 0.1
- leftBackY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, leftBackX, 0, leftBackZ) + yOffset + 0.1
- rightBackY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rightBackX, 0, rightBackZ) + yOffset + 0.1
- end
-
- drawDebugLine(leftFrontX, leftFrontY, leftFrontZ, r, g, b, rightFrontX, rightFrontY, rightFrontZ, r, g, b)
- drawDebugLine(rightFrontX, rightFrontY, rightFrontZ, r, g, b, rightBackX, rightBackY, rightBackZ, r, g, b)
- drawDebugLine(rightBackX, rightBackY, rightBackZ, r, g, b, leftBackX, leftBackY, leftBackZ, r, g, b)
- drawDebugLine(leftBackX, leftBackY, leftBackZ, r, g, b, leftFrontX, leftFrontY, leftFrontZ, r, g, b)
-end
-
---- Draw a cube (for debugging purpose)
----@param node integer|table ref node or ref position
----@param size number size
----@param r number r
----@param g number g
----@param b number b
----@param ar number r color if active
----@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)
- if active then
- r, g, b = ar, ag, ab
- end
-
- local x, y, z = 0, 0, 0
- if type(node) == "table" then
- x = node[1]
- y = node[2]
- z = node[3]
- else
- x, y, z = getWorldTranslation(node)
- end
-
- local offsets = size / 2
- local corners = {}
- corners[1] = {x + offsets, y + offsets, z + offsets}
- corners[2] = {x + offsets, y + offsets, z - offsets}
- corners[3] = {x + offsets, y - offsets, z - offsets}
- corners[4] = {x + offsets, y - offsets, z + offsets}
- corners[5] = {x - offsets, y + offsets, z + offsets}
- corners[6] = {x - offsets, y + offsets, z - offsets}
- 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)
-
- 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)
-end
-
---- Draw a triangle (for debugging purpose)
----@param c1 table first corner position {x, y, z}
----@param c2 table second corner position {x, y, z}
----@param c3 table third corner position {x, y, z}
----@param r number r
----@param g number g
----@param b number b
-function Utility.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
-
---- Draw a triangle (for debugging purpose)
----@param p1 table first point position {x, y, z}
----@param p2 table second point position {x, y, z}
----@param r number r
----@param g number g
----@param b number b
-function Utility.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
-
---- Render an AnimCurve (for debugging purpose)
----@param x number x position
----@param y number y position
----@param w number width
----@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)
- local graph = curve.debugGraph
- local numPoints = numPointsToShow or #curve.keyframes
- local minTime = 0
- local maxTime = curve.maxTime
- if graph == nil then
- if numPointsToShow == nil then
- graph = Graph:new(numPoints, x, y, w, h, 0, 0.0001, true, "", Graph.STYLE_LINES)
- graph:setColor(1, 0, 0, 1)
- for i, kf in ipairs(curve.keyframes) do
- local v = curve:get(kf.time)
- graph:setValue(i, v)
- graph:setXPosition(i, (kf.time - minTime) / (maxTime - minTime))
- graph.maxValue = math.max(graph.maxValue, v)
- end
- else
- graph = Graph:new(numPoints + 1, x, y, w, h, 0, 0.0001, true, "", Graph.STYLE_LINES)
- graph:setColor(1, 0, 0, 1)
- for s = 1, numPoints + 1 do
- local i = s - 1
- local v = curve:get(minTime + (maxTime - minTime) * (i / numPoints))
- graph:setValue(s, v)
- graph.maxValue = math.max(graph.maxValue, v)
- end
- end
- curve.debugGraph = graph
- end
- graph:draw()
-end
-
---- Get the loading speed meter object
----@return LoadingSpeedMeter loadingSpeedMeter
-function Utility.getVehicleLoadingSpeedMeter()
- if Utility.loadingSpeedMeter == nil then
- ---@class LoadingSpeedMeter
- Utility.loadingSpeedMeter = {}
- Utility.loadingSpeedMeter.vehicles = {}
- Utility.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)
- end
- Utility.overwrittenFunction(
- Vehicle,
- "load",
- function(self, superFunc, vehicleData, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments)
- local smEnabled = false
- local smName = ""
- for _, filter in ipairs(Utility.loadingSpeedMeter.filters) do
- smEnabled, smName = filter(vehicleData)
- if smEnabled then
- break
- end
- end
-
- if smEnabled then
- Utility.loadingSpeedMeter.vehicles[self] = {}
- Utility.loadingSpeedMeter.vehicles[self].smName = smName
- Utility.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
- end
- return state
- end
- )
- Utility.overwrittenStaticFunction(
- SpecializationUtil,
- "raiseEvent",
- function(superFunc, vehicle, eventName, ...)
- if Utility.loadingSpeedMeter.vehicles[vehicle] ~= nil then
- if eventName == "onPreLoad" then
- Utility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime = getTimeSec()
- superFunc(vehicle, eventName, ...)
- Utility.loadingSpeedMeter.vehicles[vehicle].preLoadTime = getTimeSec() - Utility.loadingSpeedMeter.vehicles[vehicle].preLoadStartTime
- end
- if eventName == "onLoad" then
- Utility.loadingSpeedMeter.vehicles[vehicle].loadStartTime = getTimeSec()
- superFunc(vehicle, eventName, ...)
- Utility.loadingSpeedMeter.vehicles[vehicle].loadTime = getTimeSec() - Utility.loadingSpeedMeter.vehicles[vehicle].loadStartTime
- end
- if eventName == "onPostLoad" then
- Utility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime = getTimeSec()
- superFunc(vehicle, eventName, ...)
- Utility.loadingSpeedMeter.vehicles[vehicle].postLoadTime = getTimeSec() - Utility.loadingSpeedMeter.vehicles[vehicle].postLoadStartTime
- end
- else
- superFunc(vehicle, eventName, ...)
- end
- end
- )
- end
- return Utility.loadingSpeedMeter
-end
diff --git a/lib/utility/UtilityEntity.lua b/lib/utility/UtilityEntity.lua
deleted file mode 100644
index 58de44f..0000000
--- a/lib/utility/UtilityEntity.lua
+++ /dev/null
@@ -1,171 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 1.8.1.0
----@date 05/01/2021
-
---- 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)
- if objectId == nil then
- return nil, nil
- end
- for name, id in pairs(ClassIds) do
- if getHasClassId(objectId, id) then
- return id, name
- end
- end
-end
-
---- Determines whether a node is a child of a given node
----@param childNode integer
----@param parentNode integer
----@return boolean
-function Utility.isChildOf(childNode, parentNode)
- if childNode == nil or childNode == 0 or parentNode == nil or parentNode == 0 then
- return false
- end
- local pNode = getParent(childNode)
- while pNode ~= 0 do
- if pNode == parentNode then
- return true
- end
- pNode = getParent(pNode)
- end
- return false
-end
-
---- Get the node index relative to root node
----@param nodeId integer id of node
----@param rootId integer id of root node
----@return string nodeIndex index of node
-function Utility.nodeToIndex(nodeId, rootId)
- local index = ""
- if nodeId ~= nil and entityExists(nodeId) and rootId ~= nil and entityExists(rootId) and Utility.isChildOf(nodeId, rootId) then
- index = tostring(getChildIndex(nodeId))
- local pNode = getParent(nodeId)
- while pNode ~= rootId and pNode ~= 0 do
- index = string.format("%s|%s", getChildIndex(pNode), index)
- pNode = getParent(pNode)
- end
- end
- return index
-end
-
---- Get a node id by an index
----@param nodeIndex string index of node
----@param rootId integer id of root node
----@return integer nodeId id of node
-function Utility.indexToNode(nodeIndex, rootId)
- if nodeIndex == nil or rootId == nil or not entityExists(rootId) then
- return nil
- end
- local objectId = rootId
- local indexes = StringUtility.split(nodeIndex, "|")
- for _, index in pairs(indexes) do
- index = tonumber(index)
- if type(index) == "number" then
- if getNumOfChildren(objectId) >= index then
- objectId = getChildAt(objectId, index)
- else
- return nil
- end
- else
- return nil
- end
- end
- return objectId
-end
-
---- Queries a node hierarchy
----@param inputNode integer
----@param func function | "function(node, name, depth) end"
-function Utility.queryNodeHierarchy(inputNode, func)
- if not type(inputNode) == "number" or not entityExists(inputNode) or func == nil then
- return
- end
- local function queryNodeHierarchyRecursively(node, depth)
- func(node, getName(node), depth)
- for i = 0, getNumOfChildren(node) - 1 do
- queryNodeHierarchyRecursively(getChildAt(node, i), depth + 1)
- end
- end
- local depth = 1
- func(inputNode, getName(inputNode), depth)
- for i = 0, getNumOfChildren(inputNode) - 1 do
- queryNodeHierarchyRecursively(getChildAt(inputNode, i), depth + 1)
- end
-end
-
---- Get the hash of a node hierarchy
----@param node integer
----@param parent integer
----@param md5 boolean
----@return string hash hash of the node hierarchy
-function Utility.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
- local hash = ""
- local nodeCount = 0
-
- local floatsToString = function(...)
- local ret = {}
- for i, v in ipairs({...}) do
- local tV = string.format("%.1f", v)
- if tV == "-0.0" then
- tV = "0.0"
- end
- ret[i] = tV
- end
- return table.concat(ret, "|")
- end
-
- local isDyna = false
-
- Utility.queryNodeHierarchy(
- node,
- function(n, name)
- local rbt = getRigidBodyType(n)
- if rbt == "Dynamic" then
- isDyna = true
- end
- local pos = ""
- local rot = ""
- if not isDyna then
- pos = floatsToString(getWorldTranslation(n))
- rot = floatsToString(getWorldRotation(n))
- end
- local sca = floatsToString(getScale(n))
- local index = Utility.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
- end
- )
- if md5 then
- return getMD5(string.format("%s%s_dMs5AsHZWy", hash, nodeCount))
- else
- return string.format("%s___%s", hash, nodeCount)
- end
-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)
- if not type(inputNode) == "number" or not entityExists(inputNode) or func == nil then
- return
- end
- local depth = 1
- local pNode = inputNode
- while pNode ~= 0 do
- if not func(pNode, getName(pNode), depth) then
- break
- end
- pNode = getParent(pNode)
- depth = depth + 1
- end
-end
diff --git a/lib/utility/UtilityGameplay.lua b/lib/utility/UtilityGameplay.lua
deleted file mode 100644
index aa70658..0000000
--- a/lib/utility/UtilityGameplay.lua
+++ /dev/null
@@ -1,79 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 1.8.1.0
----@date 05/01/2021
-
---- Get value of a trunk (splitshape)
----@param id integer
----@param splitType table
----@return number, number, number, number
-function Utility.getTrunkValue(id, splitType)
- if splitType == nil then
- splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(id))
- end
-
- if splitType == nil or splitType.pricePerLiter <= 0 then
- return 0
- end
-
- local volume = getVolume(id)
- local qualityScale = 1
- local lengthScale = 1
- local defoliageScale = 1
- local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(id)
-
- if sizeX ~= nil and volume > 0 then
- local bvVolume = sizeX * sizeY * sizeZ
- local volumeRatio = bvVolume / volume
- local volumeQuality = 1 - math.sqrt(MathUtil.clamp((volumeRatio - 3) / 7, 0, 1)) * 0.95 -- ratio <= 3: 100%, ratio >= 10: 5%
- local convexityQuality = 1 - MathUtil.clamp((numConvexes - 2) / (6 - 2), 0, 1) * 0.95
- -- 0-2: 100%:, >= 6: 5%
-
- local maxSize = math.max(sizeX, math.max(sizeY, sizeZ))
- -- 1m: 60%, 6-11m: 120%, 19m: 60%
- if maxSize < 11 then
- lengthScale = 0.6 + math.min(math.max((maxSize - 1) / 5, 0), 1) * 0.6
- else
- lengthScale = 1.2 - math.min(math.max((maxSize - 11) / 8, 0), 1) * 0.6
- end
-
- local minQuality = math.min(convexityQuality, volumeQuality)
- local maxQuality = math.max(convexityQuality, volumeQuality)
- qualityScale = minQuality + (maxQuality - minQuality) * 0.3 -- use 70% of min quality
-
- defoliageScale = 1 - math.min(numAttachments / 15, 1) * 0.8 -- #attachments 0: 100%, >=15: 20%
- end
-
- -- Only take 33% into account of the quality criteria on easy difficulty
- qualityScale = MathUtil.lerp(1, qualityScale, g_currentMission.missionInfo.economicDifficulty / 3)
-
- defoliageScale = MathUtil.lerp(1, defoliageScale, g_currentMission.missionInfo.economicDifficulty / 3)
-
- return volume * 1000 * splitType.pricePerLiter * qualityScale * defoliageScale * lengthScale, qualityScale, defoliageScale, lengthScale
-end
-
---- Get the farm color
----@param farmId number
----@return number[]
-function Utility.getFarmColor(farmId)
- local farm = g_farmManager:getFarmById(farmId)
- if farm ~= nil then
- local color = Farm.COLORS[farm.color]
- if color ~= nil then
- return color
- end
- end
- return {1, 1, 1, 1}
-end
-
---- Get the farm name
----@param farmId number
----@return string
-function Utility.getFarmName(farmId)
- local farm = g_farmManager:getFarmById(farmId)
- if farm ~= nil then
- return farm.name
- end
- return nil
-end
diff --git a/lib/utility/UtilityInterpolator.lua b/lib/utility/UtilityInterpolator.lua
deleted file mode 100644
index 9a15203..0000000
--- a/lib/utility/UtilityInterpolator.lua
+++ /dev/null
@@ -1,113 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 1.8.1.0
----@date 11/01/2021
-
---- Interpolators utilities class
----@class InterpolatorUtility
-InterpolatorUtility = InterpolatorUtility or {}
-
-function InterpolatorUtility.exponential11Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 2
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential11InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 2
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential12Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 3
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential12InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 3
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential13Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 4
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential13InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 4
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential14Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = alpha ^ 8
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential14InvertedInterpolator1(first, second, alpha)
- alpha = alpha ^ 8
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential21Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = (2 ^ alpha) - 1
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential21InvertedInterpolator1(first, second, alpha)
- alpha = (2 ^ alpha) - 1
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential22Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = ((3 ^ alpha) - 1) / 2
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential22InvertedInterpolator1(first, second, alpha)
- alpha = ((3 ^ alpha) - 1) / 2
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential23Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = ((4 ^ alpha) - 1) / 3
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential23InvertedInterpolator1(first, second, alpha)
- alpha = ((4 ^ alpha) - 1) / 3
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.exponential24Interpolator1(first, second, alpha)
- alpha = 1 - alpha
- alpha = ((8 ^ alpha) - 1) / 7
- return (first[1] + alpha * (second[1] - first[1]))
-end
-
-function InterpolatorUtility.exponential24InvertedInterpolator1(first, second, alpha)
- alpha = ((8 ^ alpha) - 1) / 7
- return (second[1] + alpha * (first[1] - second[1]))
-end
-
-function InterpolatorUtility.catmullRomInterpolator1(first, second, beforeFirst, afterSecond, alpha)
- alpha = 1 - alpha
- local alpha2 = alpha * alpha
- local alpha3 = alpha2 * alpha
-
- if beforeFirst == nil then
- beforeFirst = {2 * first[1] - second[1]}
- end
-
- if afterSecond == nil then
- afterSecond = {2 * second[1] - first[1]}
- end
-
- return 0.5 * ((2 * first[1]) + (-beforeFirst[1] + second[1]) * alpha + (2 * beforeFirst[1] - 5 * first[1] + 4 * second[1] - afterSecond[1]) * alpha2 + (-beforeFirst[1] + 3 * first[1] - 3 * second[1] + afterSecond[1]) * alpha3)
-end
diff --git a/lib/utility/UtilityString.lua b/lib/utility/UtilityString.lua
deleted file mode 100644
index 6eb09e1..0000000
--- a/lib/utility/UtilityString.lua
+++ /dev/null
@@ -1,114 +0,0 @@
---- Royal Utility
-
----@author Royal Modding
----@version 1.8.1.0
----@date 05/01/2021
-
---- String utilities class
----@class StringUtility
-StringUtility = StringUtility or {}
-
---- Chars available for randomizing
----@type string[]
-StringUtility.randomCharset = {
- "0",
- "1",
- "2",
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z"
-}
-
---- Get random string
----@param length number
----@return string
-function StringUtility.random(length)
- length = length or 1
- if length <= 0 then
- return ""
- end
- return StringUtility.random(length - 1) .. StringUtility.randomCharset[math.random(1, #StringUtility.randomCharset)]
-end
-
---- Split a string
----@param s string
----@param sep string
----@return string[]
-function StringUtility.split(s, sep)
- sep = sep or ":"
- local fields = {}
- local pattern = string.format("([^%s]+)", sep)
- s:gsub(
- pattern,
- function(c)
- fields[#fields + 1] = c
- end
- )
- return fields
-end
-
---- Get translated text
----@param key string text key prefixed with "$l10n_"
----@return string text translated text
-function StringUtility.parseI18NText(key)
- if key == nil then
- return ""
- end
- return g_i18n:convertText(key)
-end
diff --git a/missionVehicles/newTest.xml b/missionVehicles/newTest.xml
new file mode 100644
index 0000000..57e4b5a
--- /dev/null
+++ b/missionVehicles/newTest.xml
@@ -0,0 +1,505 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modDesc.xml b/modDesc.xml
index 8f427a5..4090c27 100644
--- a/modDesc.xml
+++ b/modDesc.xml
@@ -1,7 +1,7 @@
Mmtrx
- 1.2.4.0
+ 1.2.4.1
Better Contracts
@@ -24,6 +24,12 @@
Disclaimer: All values shown in details display are ESTIMATES. You should not take them absolutely, but rather as an indication of what contracts to prefer among others.
+Changelog v1.2.4.1:
+- "lazy NPCs" (leave more work for contracts) can be configured on/ off
+- maximum number of active contracts configurable
+- indicator for active contracts with borrowed equipment
+- clear / new contracts buttons in MP games only work for master user
+
Changelog v1.2.4.0:
- Added (interim) fix for "lazy NPCs": leave more work for contracts
- Allow for (future) other contract types
@@ -134,8 +140,6 @@ Changelog v1.2.2.0:
icon.dds
-
-
diff --git a/scripts/RoyalMod.lua b/scripts/RoyalMod.lua
new file mode 100644
index 0000000..81fc94f
--- /dev/null
+++ b/scripts/RoyalMod.lua
@@ -0,0 +1,439 @@
+--- Royal Mod
+
+---@author Royal Modding
+---@version 1.5.0.0
+---@date 03/12/2020
+-- Changelog:
+-- v1.0.0.0 03.12.2020 initial by Royal-Modding
+-- v1.1.0.0 30.12.2021: (Mmtrx) commented out vehicleTypeManagerFinalizeTypes, different in FS22
+-- v1.1.0.1 27.02.2022: (Mmtrx) delete vehicleTypeManagerFinalizeTypes, different in FS22
+-- remove mod.gameEnv, no longer reachable in FS22
+-- change addOnCreateLoadedObjects -> OnCreateObjectSystem:add() for mpSync
+-- delete Mission00.loadOnCreateLoadedObjects, no more in FS22
+-- add BaseMission.onFinishedLoading
+-- 13.04.2022 add check on g_vehicleTypeManager when calling mod.initialize
+-- 15.05.2022 call mod.onLoad with "mission" param
+-- 20.06.2022 check on TypeManager.typeName when calling mod.initialize
+
+--------- possible functions that mod can specify: ------------------------------------
+--[[
+ -- for MP syncing:
+ ---@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)
+
+ -- standard listeners:
+ ---@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)
+
+ -- additional Royal Mod entry points:
+ ---@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 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
+
+
+ --------- predefined fields of the mod: ------------------------------------
+ --@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 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.modManagerMod = g_modManager:getModByName(mod.name)
+ mod.version = mod.modManagerMod.version
+ mod.author = mod.modManagerMod.author
+ mod.modEnv = getfenv()
+ mod.super = {}
+ mod.debug = debug
+ function debugPrint(text, ...)
+ if mod.debug then
+ Logging.info(text,...)
+ end
+ end
+
+ if mod.debug then
+ g_showDevelopmentWarnings = true
+ g_addTestCommands = true
+ end
+
+ mod.super.oldFunctions = {}
+
+ ---@param error string
+ mod.super.errorHandle = function(error)
+ Logging.error("RoyalMod caught error from %s (%s)", mod.name, mod.version)
+ Logging.error(error)
+ end
+
+ mod.super.getSavegameDirectory = function()
+ if g_currentMission ~= nil and g_currentMission.missionInfo ~= nil then
+ if g_currentMission.missionInfo.savegameDirectory ~= nil then
+ return string.format("%s/", g_currentMission.missionInfo.savegameDirectory)
+ end
+
+ if g_currentMission.missionInfo.savegameIndex ~= nil then
+ return string.format("%ssavegame%d/", mod.userProfileDirectory, g_currentMission.missionInfo.savegameIndex)
+ end
+ end
+ return mod.userProfileDirectory
+ end
+ -- ------------- for MP sync handling ----------------------------------------
+ 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
+ local time = netGetTime()
+ local offset = streamGetWriteOffset(streamId)
+ xpcall(mod.onWriteStream, mod.super.errorHandle, mod, streamId)
+ offset = streamGetWriteOffset(streamId) - offset
+ debugPrint("[%s] Written %.0f bits (%.0f bytes) in %s ms", mod.name, offset, offset / 8, netGetTime() - time)
+ 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
+ local time = netGetTime()
+ local offset = streamGetReadOffset(streamId)
+ xpcall(mod.onReadStream, mod.super.errorHandle, mod, streamId)
+ offset = streamGetReadOffset(streamId) - offset
+ debugPrint("[%s] Read %.0f bits (%.0f bytes) in %s ms", mod.name, offset, offset / 8, netGetTime() - time)
+ 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
+ xpcall(mod.onUpdateTick, mod.super.errorHandle, mod, dt)
+ 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
+ xpcall(mod.onWriteUpdateStream, mod.super.errorHandle, mod, streamId, connection, dirtyMask)
+ 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
+ xpcall(mod.onReadUpdateStream, mod.super.errorHandle, mod, streamId, timestamp, connection)
+ end
+ 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)
+ end
+ end
+
+ --g_vehicleTypeManager = TypeManager.new("vehicle", "vehicleTypes", "dataS/vehicleTypes.xml", g_specializationManager)
+
+ mod.super.oldFunctions.TypeManagerValidateTypes = TypeManager.validateTypes
+ TypeManager.validateTypes = function(self, ...)
+ if mod.initialize ~= nil and self.typeName == "placeable" then
+ --- validateTypes will be called twice:
+ --- for vehicles and for placeables
+ --- g_currentMission is still nil here. All mods are loaded here
+ xpcall(mod.initialize, mod.super.errorHandle, mod)
+ end
+ mod.super.oldFunctions.TypeManagerValidateTypes(self, ...)
+ 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, ...)
+ if mod.onMissionInitialize ~= nil then
+ --- g_currentMission is still nil here
+ xpcall(mod.onMissionInitialize, mod.super.errorHandle, mod, baseDirectory, missionCollaborators)
+ end
+ return mod.super.oldFunctions.Mission00new(self, baseDirectory, customMt, missionCollaborators, ...)
+ 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)
+ mod.super.oldFunctions.Mission00setMissionInfo(self, missionInfo, missionDynamicInfo, ...)
+ if mod.onSetMissionInfo ~= nil then
+ --- g_currentMission is no more nil here
+ xpcall(mod.onSetMissionInfo, mod.super.errorHandle, mod, missionInfo, missionDynamicInfo)
+ end
+ 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, self)
+ end
+ mod.super.oldFunctions.Mission00load(self, ...)
+ 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)
+ end
+ mod.super.oldFunctions.FSBaseMissionloadMap(self, mapFile, addPhysics, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments, ...)
+ end
+
+ -- apparently called when careerStartPoint is created from map
+ 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.onCreateStartPoint ~= nil then
+ xpcall(mod.onCreateStartPoint, mod.super.errorHandle, mod, startPointNode)
+ end
+ end
+
+ mod.super.oldFunctions.BaseMissionloadMapFinished = BaseMission.loadMapFinished
+ ---@param self BaseMission
+ ---@param mapNode integer
+ ---@param arguments table
+ ---@param callAsyncCallback boolean
+ BaseMission.loadMapFinished = function(self, mapNode, failedReason, arguments, callAsyncCallback, ...)
+ if mod.super.sync ~= nil then
+ --g_currentMission:addOnCreateLoadedObject(mod.super.sync)
+ g_currentMission.onCreateObjectSystem:add(mod.super.sync)
+ mod.super.sync:register(true)
+ end
+ mod.mapNode = mapNode
+ local mapFile, _, _, _ = unpack(arguments)
+ mod.super.oldFunctions.BaseMissionloadMapFinished(self, mapNode, failedReason, arguments, callAsyncCallback, ...)
+ if mod.onPostLoadMap ~= nil then
+ xpcall(mod.onPostLoadMap, mod.super.errorHandle, mod, mapNode, mapFile)
+ end
+ 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)
+ end
+ mod.super.oldFunctions.Mission00loadMission00Finished(self, mapNode, arguments, ...)
+ 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)
+ end
+ mod.super.oldFunctions.Mission00loadVehicles(self, xmlFile, resetVehicles, ...)
+ 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)
+ end
+ mod.super.oldFunctions.Mission00loadItems(self, xmlFile, ...)
+ end
+
+ -- not called on clients in MP games
+ mod.super.onLoadFinished = function()
+ if mod.onLoadFinished ~= nil then
+ xpcall(mod.onLoadFinished, mod.super.errorHandle, mod)
+ end
+ end
+
+ mod.super.oldFunctions.BaseMissionOnFinishedLoading = BaseMission.onFinishedLoading
+ ---@param self BaseMission
+ BaseMission.onFinishedLoading = function(self, ...)
+ mod.super.oldFunctions.BaseMissionOnFinishedLoading(self, ...)
+ if mod.onFinishedLoading ~= nil then
+ xpcall(mod.onFinishedLoading, mod.super.errorHandle, mod)
+ end
+ 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)
+ end
+ mod.super.oldFunctions.Mission00onStartMission(self, ...)
+ end
+
+ mod.super.onMissionStarted = function(...)
+ if mod.onMissionStarted ~= nil then
+ xpcall(mod.onMissionStarted, mod.super.errorHandle, mod)
+ end
+ 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)
+ end
+ mod.super.oldFunctions.Mission00delete(self, ...)
+ end
+
+ mod.super.oldFunctions.FSBaseMissionsaveSavegame = FSBaseMission.saveSavegame
+ ---@param self FSBaseMission
+ FSBaseMission.saveSavegame = function(self, ...)
+ if mod.onPreSaveSavegame ~= nil then
+ -- before all vehicles, items and onCreateObjects are saved
+ xpcall(mod.onPreSaveSavegame, mod.super.errorHandle, mod, mod.super.getSavegameDirectory(), g_currentMission.missionInfo.savegameIndex)
+ end
+ mod.super.oldFunctions.FSBaseMissionsaveSavegame(self, ...)
+ if mod.onPostSaveSavegame ~= nil then
+ -- after all vhicles, items and onCreateObjects are saved
+ xpcall(mod.onPostSaveSavegame, mod.super.errorHandle, mod, mod.super.getSavegameDirectory(), g_currentMission.missionInfo.savegameIndex)
+ end
+ 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
+ local success, hlFilename = xpcall(mod.onLoadHelpLine, mod.super.errorHandle, mod)
+ if success and hlFilename ~= nil and type(hlFilename) == "string" and hlFilename ~= "" then
+ self:loadFromXML(hlFilename)
+ for ci = 1, #self.categories do
+ local category = self.categories[ci]
+ for pi = 1, #category.pages do
+ local page = category.pages[pi]
+ for ii = 1, #page.items do
+ local item = page.items[ii]
+ if item.type == HelpLineManager.ITEM_TYPE.IMAGE then
+ if item.value:sub(1, 10) == "$rmModDir/" then
+ item.value = "$" .. mod.directory .. item.value:sub(11)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return true
+ end
+ end
+
+ addModEventListener(mod.super)
+ return mod
+end
diff --git a/lib/utility/UtilityTable.lua b/scripts/TableUtility.lua
similarity index 100%
rename from lib/utility/UtilityTable.lua
rename to scripts/TableUtility.lua
diff --git a/lib/utility/Utility.lua b/scripts/Utility.lua
similarity index 100%
rename from lib/utility/Utility.lua
rename to scripts/Utility.lua
diff --git a/scripts/gui.lua b/scripts/gui.lua
index f1cf015..3a763ef 100644
--- a/scripts/gui.lua
+++ b/scripts/gui.lua
@@ -16,6 +16,8 @@
-- v1.2.4.0 26.08.2022 allow for other (future) mission types,
-- fix distorted menu page for different screen aspect ratios,
-- show fruit type to harvest in contracts list
+-- v.1.2.4.1 05.09.2022 indicate leased equipment for active missions
+-- allow clear/new contracts button only for master user
--=======================================================================================================
function BetterContracts:loadGUI(canLoad, guiPath)
@@ -314,6 +316,16 @@ function populateCell(frCon, list, sect, index, cell)
end
end
profit:setVisible(showProf)
+ -- indicate leased equipment for active missions
+ if cont and cont.miss.status == AbstractMission.STATUS_RUNNING then
+ local indicator = cell:getAttribute("indicatorActive")
+ local txt = ""
+ if cont.miss.spawnedVehicles then
+ txt = g_i18n:getText("bc_leased")
+ end
+ indicator:setText(g_i18n:getText("fieldJob_active")..txt)
+ indicator:setVisible(true)
+ end
end
function sortList(frCon, superfunc)
-- sort frCon.contracts according to sort button clicked