5--! \brief
Utility module for FrameworkZ. Contains utility functions and classes.
6--! \class FrameworkZ.Utility
7FrameworkZ.Utilities = {}
8FrameworkZ.Utilities.__index = FrameworkZ.Utilities
9FrameworkZ.Utilities = FrameworkZ.Foundation:NewModule(FrameworkZ.Utilities, "Utilities")
11function FrameworkZ.Utilities:Pack(...)
12 return {n = select("#", ...), ...}
15function FrameworkZ.Utilities:Unpack(t)
16 return unpack(t, 1, t.n)
19--! \brief Copies a table.
20--! \param originalTable \table The table to copy.
21--! \param tableCopies \table (Internal) The table of copies used internally by the function.
22--! \return \table The copied table.
23function FrameworkZ.Utilities:CopyTable(originalTable, tableCopies, shouldCopyMetatable)
24 tableCopies = tableCopies or {}
26 local originalType = type(originalTable)
29 if originalType == "table" then
30 if tableCopies[originalTable] then
31 copy = tableCopies[originalTable]
34 tableCopies[originalTable] = copy
36 for originalKey, originalValue in pairs(originalTable) do
37 copy[self:CopyTable(originalKey, tableCopies)] = self:CopyTable(originalValue, tableCopies)
40 local mt = getmetatable(originalTable)
42 if mt and type(mt) == "table" then
43 setmetatable(copy, self:CopyTable(mt, tableCopies))
46 else -- number, string, boolean, etc
53function FrameworkZ.Utilities:MergeTables(t1, t2)
54 for k, v in pairs(t2) do
55 if type(v) == "table" and type(t1[k]) == "table" then
56 self:MergeTables(t1[k], v)
62 -- Handle metatable merging
63 local mt1 = getmetatable(t1)
64 local mt2 = getmetatable(t2)
68 -- Both tables have metatables, merge them recursively
69 if type(mt1) == "table" and type(mt2) == "table" then
70 self:MergeTables(mt1, mt2)
72 -- If metatables aren't tables, use t2's metatable
76 -- Only t2 has a metatable, apply it to t1
84function FrameworkZ.Utilities:DumpTable(tbl)
85 if type(tbl) == 'table' then
87 for k,v in pairs(tbl) do
88 if type(k) ~= 'number' then k = '"'..k..'"' end
89 s = s .. '['..k..'] = ' .. self:DumpTable(v) .. ','
97function FrameworkZ.Utilities:PrintTable(tbl)
98 print(self:DumpTable(tbl))
101function FrameworkZ.Utilities:TableIsEmpty(t)
102 if type(t) ~= "table" then
108 for _, _ in pairs(t) do
116function FrameworkZ.Utilities:TableContainsKey(t, key)
118 for k, _ in pairs(t) do
128function FrameworkZ.Utilities:TableContainsValue(t, value)
130 for _, v in pairs(t) do
140function FrameworkZ.Utilities:RemoveContextDuplicates(worldObjects)
141 local newObjects = {}
143 for _, v1 in ipairs(worldObjects) do
144 local isInList = false
146 for _2, v2 in ipairs(newObjects) do
154 table.insert(newObjects, v1)
161function FrameworkZ.Utilities:__GenOrderedIndex( t )
162 local orderedIndex = {}
164 for key in pairs(t) do
165 table.insert(orderedIndex, key)
168 table.sort(orderedIndex)
173function FrameworkZ.Utilities:OrderedNext(t, state)
177 t.__orderedIndex = self:__GenOrderedIndex(t)
178 key = t.__orderedIndex[1]
180 for i = 1, #t.__orderedIndex do
181 if t.__orderedIndex[i] == state then
182 key = t.__orderedIndex[i + 1]
191 t.__orderedIndex = nil
196function FrameworkZ.Utilities:OrderedPairs(t)
197 return self.OrderedNext, t, nil
200--! \brief Word wraps text to a specified length.
201--! \param text \string The text to word wrap.
202--! \param maxLength \int The maximum length of a line (default: 28).
203--! \param eolDelimiter \string (Optional) The end of line delimiter. Returns a \table of lines if not supplied.
204--! \return \mixed The word wrapped text as a \string or a \table of lines of text if no eolDelimiter was supplied as an argument.
205function FrameworkZ.Utilities:WordWrapText(text, maxLength, eolDelimiter)
206 local maxLineLength = maxLength or 28
212 for word in string.gmatch(text, "%S+") do
213 table.insert(words, word)
217 local word = words[i]
218 local wordLength = string.len(word)
220 if lineLength + wordLength <= maxLineLength then
221 line = line .. " " .. word
222 lineLength = lineLength + wordLength
224 table.insert(lines, line)
226 lineLength = wordLength
230 table.insert(lines, line)
233 local delimitedText = ""
236 delimitedText = delimitedText .. lines[i] .. (i ~= #lines and eolDelimiter or "")
245function FrameworkZ.Utilities:GetRandomNumber(min, max, keepLeadingZeros)
250 local maxDigits = math.max(#tostring(min), #tostring(max))
252 return keepLeadingZeros and string.format("%0" .. maxDigits .. "d", ZombRandBetween(min, max)) or ZombRandBetween(min, max)
255FrameworkZ.Utilities.Directions = {
256 { dx = 1, dy = 0, wallFlag = IsoFlagType.collideW, doorFlag = IsoFlagType.doorW, windowFlag = IsoFlagType.windowW },
257 { dx = -1, dy = 0, wallFlag = IsoFlagType.collideW, doorFlag = IsoFlagType.doorW, windowFlag = IsoFlagType.windowW },
258 { dx = 0, dy = 1, wallFlag = IsoFlagType.collideN, doorFlag = IsoFlagType.doorN, windowFlag = IsoFlagType.windowN },
259 { dx = 0, dy = -1, wallFlag = IsoFlagType.collideN, doorFlag = IsoFlagType.doorN, windowFlag = IsoFlagType.windowN },
262function FrameworkZ.Utilities:IsExterior(square)
263 return not square or square:getRoom() == nil
266function FrameworkZ.Utilities:IsTrulyInterior(square)
267 if not square then return false end
268 local room = square:getRoom()
269 if not room then return false end
272 local queue = { square }
276 local currentSquare = table.remove(queue, 1)
278 for _, dir in ipairs(self.Directions) do
279 local nx, ny, nz = currentSquare:getX() + dir.dx, currentSquare:getY() + dir.dy, currentSquare:getZ()
280 local nextSquare = getCell():getGridSquare(nx, ny, nz)
282 if nextSquare and not seen[nextSquare] then
283 local blocked = nextSquare:Is(dir.wallFlag) or nextSquare:Is(dir.doorFlag) or nextSquare:Is(dir.windowFlag)
286 if not nextSquare:getRoom() then
290 if nextSquare:getRoom() == room then
291 seen[nextSquare] = true
292 table.insert(queue, nextSquare)
302function FrameworkZ.Utilities:IsSemiExterior(square)
303 if not square or not square:getRoom() then return false end
304 return not FrameworkZ.Utilities:IsTrulyInterior(square)
307function FrameworkZ.Utilities:GetPrettyDuration(timeInSeconds)
308 local seconds = math.floor(tonumber(timeInSeconds) or 0)
309 local days = math.floor(seconds / 86400)
310 seconds = seconds % 86400
311 local hours = math.floor(seconds / 3600)
312 seconds = seconds % 3600
313 local minutes = math.floor(seconds / 60)
314 seconds = seconds % 60
319 table.insert(parts, days .. (days == 1 and " day" or " days"))
321 table.insert(parts, hours .. (hours == 1 and " hour" or " hours"))
323 elseif hours > 0 then
324 table.insert(parts, hours .. (hours == 1 and " hour" or " hours"))
326 table.insert(parts, minutes .. (minutes == 1 and " minute" or " minutes"))
328 elseif minutes > 0 then
329 table.insert(parts, minutes .. (minutes == 1 and " minute" or " minutes"))
331 table.insert(parts, seconds .. (seconds == 1 and " second" or " seconds"))
334 table.insert(parts, seconds .. (seconds == 1 and " second" or " seconds"))
337 return table.concat(parts, " and ")
340--! \brief Trims a string to a maximum length, optionally adding ellipsis.
341--! \param str \string The string to trim.
342--! \param maxLength \int The maximum allowed length.
343--! \param addEllipsis \boolean (Optional) If true, appends "..." if trimmed.
344--! \return \string The trimmed string.
345function FrameworkZ.Utilities:TrimString(str, maxLength, addEllipsis)
346 if type(str) ~= "string" or type(maxLength) ~= "number" then
350 if #str <= maxLength then
355 local ellipsis = "..."
356 if maxLength > #ellipsis then
357 local trimmed = string.sub(str, 1, maxLength - #ellipsis)
358 trimmed = trimmed:match("^(.-)%s*$")
359 return trimmed .. ellipsis
361 return string.sub(str, 1, maxLength)
364 return string.sub(str, 1, maxLength)
Utility module for FrameworkZ. Contains utility functions and classes.