670 lines
17 KiB
Lua
670 lines
17 KiB
Lua
GlobalFunctions = GlobalFunctions or {}
|
||
|
||
GlobalFunctions.IsServer = nil
|
||
GlobalFunctions.IsShipping = nil
|
||
|
||
function UE.LogTag()
|
||
if GlobalFunctions.IsServer == nil then
|
||
GlobalFunctions.IsServer = UGCGameSystem.IsServer()
|
||
end
|
||
|
||
local LogStr = "[UGCLog]"
|
||
if GlobalFunctions.IsServer == true then
|
||
LogStr = LogStr .. "[Server]"
|
||
else
|
||
LogStr = LogStr .. "[Client]"
|
||
end
|
||
|
||
if GameDataManager and UE.IsValid(GameDataManager.GetLocalPlayerState()) then
|
||
LogStr = LogStr .. "[" .. GameDataManager.GetLocalPlayerState().PlayerKey .. "]"
|
||
end
|
||
|
||
return LogStr
|
||
end
|
||
|
||
function UE.Print(Log)
|
||
print(Log)
|
||
end
|
||
|
||
function UE.Log(Fmt, ...)
|
||
local LogStr = UE.LogTag()..string.format(Fmt, ...)
|
||
UE.Print(LogStr)
|
||
end
|
||
|
||
function UE.LogError(Fmt, ...)
|
||
local LogStr = UE.LogTag().."[Error]"..string.format(Fmt, ...)
|
||
UE.Print(LogStr)
|
||
end
|
||
|
||
function UE.LogUGC(Fmt, ...)
|
||
local LogStr = UE.LogTag()..string.format(Fmt, ...)
|
||
ugcprint(LogStr)
|
||
end
|
||
|
||
|
||
function GlobalFunctions.GetAttributeDisplayType(InAttributeName)
|
||
for Name, Config in pairs(GlobalConfigs.Attributes) do
|
||
if Name == InAttributeName then
|
||
return Config.Type
|
||
end
|
||
end
|
||
|
||
return EAttributeDisplayType.Int
|
||
end
|
||
|
||
---获取武器蓝图路径
|
||
---@return WeaponClassPath string @蓝图路径
|
||
---@param ID int
|
||
function GlobalFunctions.GetWeaponClassPath(ID)
|
||
local WeaponData = Tables.Weapon[ID]
|
||
if WeaponData ~= nil then
|
||
return WeaponData.ClassPath
|
||
end
|
||
return nil
|
||
end
|
||
|
||
---获取武器名称
|
||
---@return WeaponName string @武器名称
|
||
---@param ID int
|
||
function GlobalFunctions.GetWeaponName(ID)
|
||
local WeaponData = Tables.Weapon[ID]
|
||
if WeaponData ~= nil then
|
||
return WeaponData.WeaponName
|
||
end
|
||
return nil
|
||
end
|
||
|
||
---发送自定义Action
|
||
---生效范围:S
|
||
---@param EventName string @事件名称
|
||
---@param EventWaitTime int @事件等待时间
|
||
function GlobalFunctions.SendEventNextSecond(EventName, EventWaitTime)
|
||
if EventName == "" then
|
||
UE.Log("[GlobalFunctions.SendEventNextSecond] Not SendEvent")
|
||
return
|
||
end
|
||
|
||
local GameState = UGCGameSystem.GameState
|
||
if not GameState then
|
||
return
|
||
end
|
||
if GameState.ActionHandles == nil then
|
||
GameState.ActionHandles = {}
|
||
GameState.ActionHandleIndex = 0
|
||
end
|
||
|
||
GameState.ActionHandleIndex = GameState.ActionHandleIndex + 1
|
||
local HandleIndex = GameState.ActionHandleIndex
|
||
|
||
UE.Log("[GlobalFunctions.SendEventNextSecond] SendEvent: %s, WaitTime: %s", EventName, tostring(EventWaitTime))
|
||
|
||
local ActionHandle = {
|
||
Delegate = nil,
|
||
Timer = nil,
|
||
}
|
||
|
||
ActionHandle.Delegate = ObjectExtend.CreateDelegate(GameState,
|
||
function()
|
||
if type(EventWaitTime) == "number" and EventWaitTime > 0 then
|
||
UGCGameSystem.SendModeCustomEvent(EventName, EventWaitTime)
|
||
else
|
||
UGCGameSystem.SendModeCustomEvent(EventName)
|
||
end
|
||
|
||
if GameState.ActionHandles[HandleIndex] and GameState.ActionHandles[HandleIndex].Delegate then
|
||
ObjectExtend.DestroyDelegate(GameState.ActionHandles[HandleIndex].Delegate)
|
||
GameState.ActionHandles[HandleIndex].Delegate = nil
|
||
end
|
||
|
||
if GameState.ActionHandles[HandleIndex] and GameState.ActionHandles[HandleIndex].Timer then
|
||
KismetSystemLibrary.K2_ClearTimerHandle(GameState, GameState.ActionHandles[HandleIndex].Timer)
|
||
GameState.ActionHandles[HandleIndex].Timer = nil
|
||
end
|
||
|
||
GameState.ActionHandles[HandleIndex] = nil
|
||
end)
|
||
|
||
ActionHandle.Timer = KismetSystemLibrary.K2_SetTimerDelegateForLua(ActionHandle.Delegate, GameState, 1, false)
|
||
GameState.ActionHandles[HandleIndex] = ActionHandle
|
||
end
|
||
|
||
------------------------------------------------------------------------------------
|
||
local RootPackagePath = UGCMapInfoLib.GetRootLongPackagePath()
|
||
|
||
function GetFullPath(InPath)
|
||
if type(InPath) ~= "string" then
|
||
return ""
|
||
end
|
||
|
||
if #RootPackagePath > #InPath or string.sub(InPath, 1, #RootPackagePath) ~= RootPackagePath then
|
||
InPath = RootPackagePath..InPath
|
||
end
|
||
|
||
return InPath
|
||
end
|
||
|
||
|
||
------------------------------------------------------------------------------------
|
||
|
||
---获取表元素个数
|
||
function table.getCount(t)
|
||
if type(t) ~= "table" then
|
||
return -1
|
||
end
|
||
local Length = 0
|
||
for i, v in pairs(t) do
|
||
Length = Length + 1
|
||
end
|
||
return Length
|
||
end
|
||
|
||
---获取表中所有的Key
|
||
function table.getKeys(t)
|
||
if type(t) ~= "table" then
|
||
return {}
|
||
end
|
||
local keys = {}
|
||
for k,v in pairs(t) do
|
||
keys[#keys + 1] = k
|
||
end
|
||
return keys
|
||
end
|
||
|
||
---获取对应元素的Index
|
||
function table.getIndex(t, v)
|
||
if type(t) ~= "table" then
|
||
return nil
|
||
end
|
||
for index, value in pairs(t) do
|
||
if value == v then
|
||
return index
|
||
end
|
||
end
|
||
return nil
|
||
end
|
||
|
||
---获取表中是否有对应Key
|
||
function table.hasKey(t, k)
|
||
if type(t) ~= "table" then
|
||
return false
|
||
end
|
||
for key, value in pairs(t) do
|
||
if k == key then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
---按Key删除表中元素
|
||
function table.removeKey(t, k)
|
||
if t == nil then
|
||
return nil
|
||
end
|
||
local v = t[k]
|
||
t[k] = nil
|
||
return v
|
||
end
|
||
|
||
---获取表中是否有对应的Value
|
||
function table.hasValue(t, value)
|
||
if t == nil then return false end
|
||
for k, v in pairs(t) do
|
||
if v == value then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
---按Value删除表中元素
|
||
function table.removeValue(t, value, removeAll)
|
||
local deleteNum = 0
|
||
local i = 1
|
||
local max = table.getCount(t)
|
||
|
||
while i <= max do
|
||
if t[i] == value then
|
||
table.remove(t,i)
|
||
deleteNum = deleteNum + 1
|
||
i = i - 1
|
||
max = max - 1
|
||
if not removeAll then break end
|
||
end
|
||
i = i + 1
|
||
end
|
||
return deleteNum
|
||
end
|
||
|
||
---返回是否是空表
|
||
function table.isEmpty(t)
|
||
if type(t) ~= "table" then
|
||
return true
|
||
end
|
||
return next(t) == nil
|
||
end
|
||
|
||
function table.Swap(t, i, j)
|
||
if type(t) ~= "table" then
|
||
return
|
||
end
|
||
local temp = t[i]
|
||
t[i] = t[j]
|
||
t[j] = temp
|
||
end
|
||
|
||
-- 针对 t1, t2 做插值,注意:t1 是 t2 的子集
|
||
function table.Diff(t1, t2)
|
||
local val = {}
|
||
for i, v in pairs(t2) do
|
||
if t1[i] == nil then
|
||
val[i] = v
|
||
end
|
||
end
|
||
return val
|
||
end
|
||
|
||
--- table随机(洗牌算法)
|
||
function table.Rand(t)
|
||
if type(t) ~= "table" then
|
||
return false
|
||
end
|
||
for i = 1, #t - 1 do
|
||
local SwapIndex = math.random(i + 1, #t)
|
||
table.Swap(t, i, SwapIndex)
|
||
end
|
||
end
|
||
|
||
---@param header string
|
||
---@param tbl table
|
||
---@param maxIndent integer
|
||
---❉ 递归打印table,默认并且最多支持打印5层Table嵌套
|
||
function table.print(header, tbl, maxIndent)
|
||
local logFunc = UE.Log
|
||
local maxIndent = maxIndent or 5
|
||
maxIndent = math.max(1,maxIndent)
|
||
maxIndent = math.min(5,maxIndent)
|
||
if tbl == nil or type(tbl) ~= 'table' then
|
||
logFunc("table.print input arg is not a table type!")
|
||
return
|
||
end
|
||
|
||
logFunc(header)
|
||
logFunc("========= content of table(\"" .. tostring(tbl) .."\") ===========")
|
||
|
||
local function printInternal(t, prevKey, indent)
|
||
local indent = indent
|
||
if indent > maxIndent then
|
||
logFunc(prevKey .. "Over " .. maxIndent .." indents,omit print...")
|
||
return
|
||
end
|
||
for key, value in pairs(t) do
|
||
local printKey = prevKey .. "." .. tostring(key)
|
||
logFunc(printKey .. " = " .. tostring(value))
|
||
if key ~= "_outer" and type(value) == 'table' then
|
||
if next(value) == nil then
|
||
logFunc(printKey .. " = " .. "{ }")
|
||
else
|
||
printInternal(value, printKey, indent + 1)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
printInternal(tbl, "", 0)
|
||
end
|
||
|
||
|
||
---clamp
|
||
---@param v number @number
|
||
---@param Min number @min
|
||
---@param Max number @max
|
||
---@return number @clamp v between Min and Max
|
||
function math.clamp(v, Min, Max)
|
||
Min = math.min(Min, Max)
|
||
Max = math.max(Min, Max)
|
||
if v < Min then
|
||
return Min
|
||
end
|
||
if v > Max then
|
||
return Max
|
||
end
|
||
return v
|
||
end
|
||
|
||
---clamp
|
||
---@param a number @First number to compare
|
||
---@param b number @Second number to compare
|
||
---@param Tolerance number @Maximum allowed difference for considering them as 'nearly equal'
|
||
---@return boolean @true if a and b are nearly equal
|
||
function math.isNearlyEqual(a, b, Tolerance)
|
||
if Tolerance == nil then
|
||
Tolerance = 0.01
|
||
end
|
||
return math.abs(a - b) <= Tolerance
|
||
end
|
||
|
||
--- @param n integer 正整数
|
||
function math.pow(x, n)
|
||
local val = x
|
||
for i = 1, n do
|
||
val = val * val
|
||
end
|
||
return val
|
||
end
|
||
|
||
---字符串分割
|
||
function string.split(InStr, sep)
|
||
local sep, fields = sep or "\t",{}
|
||
local pattern = string.format("([^%s]+)", sep)
|
||
InStr:gsub(pattern, function(c) fields[#fields+1] = c end)
|
||
return fields
|
||
end
|
||
|
||
---字符串直接转Number
|
||
function string.splitToNumber(InStr, sep)
|
||
local sep, fields = sep or "\t",{}
|
||
local pattern = string.format("([^%s]+)", sep)
|
||
InStr:gsub(pattern, function(c) fields[#fields+1] = tonumber(c) end)
|
||
return fields
|
||
end
|
||
|
||
---------------------------------For Items---------------------------------
|
||
---11111
|
||
---对于武器配件来说
|
||
---第一位:大类 1
|
||
---第二位:物品类型 1~5
|
||
---第三位:所属武器类型 1~6
|
||
---第四位:物品品质 1~4
|
||
---第五位:物品伤害类型 0
|
||
---
|
||
---对于技能书来说
|
||
---第一位:大类 2
|
||
---第二、三位:技能类型 01~99
|
||
---第四位:品质 1~4
|
||
---第五位:伤害类型 0
|
||
---
|
||
---对于石头来说
|
||
---第一位:大类 3
|
||
---第二位:具体分类 1~2
|
||
---第三四五位:保留字段 000
|
||
---
|
||
---以下是武器配件的方法
|
||
---获取品质,对应 Tables.QualityInfo
|
||
function GetItemQualityLevel(InItemId)
|
||
return (InItemId // 10) % 10 - 1
|
||
end
|
||
|
||
---获取物品所属武器类型,注意第一个是手枪,因此不需要 - 1
|
||
function GetItemWeaponTypeByItemId(InItemId)
|
||
return (InItemId // 100) % 10
|
||
end
|
||
|
||
---获取物品类型,此处返回的是 1-5,如果要对应 Enum,需要再 - 1
|
||
function GetItemTypeByItemId(InItemId)
|
||
local val = InItemId // 10000 -- 对应的是 EItemType
|
||
if val == 1 then
|
||
return (InItemId // 1000) % 10 - 1
|
||
elseif val == 2 then
|
||
return EItemType.SkillBook
|
||
elseif val == 3 then
|
||
if InItemId // 1000 == 31 then
|
||
return EItemType.ScouringStone
|
||
elseif InItemId // 1000 == 32 then
|
||
return EItemType.SubstituteStone
|
||
end
|
||
end
|
||
print(string.format("[GetItemTypeByItemId] 此时 ItemId 有问题,ItemId = %d", InItemId))
|
||
end
|
||
|
||
function GetSkillIdByItemId(InItemId)
|
||
if InItemId < 20000 or InItemId > 29999 then
|
||
return nil
|
||
end
|
||
return InItemId % 10000 // 100
|
||
end
|
||
|
||
---获取物品大类,都是对应 Enum: EDropItemSet
|
||
function GetItemGrantTypeById(InItemId)
|
||
return (InItemId // 10000) - 1
|
||
end
|
||
|
||
function GetItemDamageTypeByItemId(InItemId)
|
||
return (InItemId % 10)
|
||
end
|
||
|
||
function IsWeaponPartItem(InItemId)
|
||
if InItemId == nil then
|
||
return false
|
||
end
|
||
local Id = InItemId // 10000
|
||
return Id == EDropItemSet.WeaponParts + 1
|
||
end
|
||
|
||
function IsSkillBookItem(InItemData)
|
||
local Id = InItemData.ItemID // 10000
|
||
return Id == EDropItemSet.SkillBooks + 1
|
||
end
|
||
|
||
function IsStoneItem(InItemData)
|
||
local Id = InItemData.ItemID // 10000
|
||
return Id == EDropItemSet.Stones + 1
|
||
end
|
||
|
||
function GenerateSkillBookData(SkillType)
|
||
local RateTable = {
|
||
[1] = {Min = 1, Max = 85},
|
||
[2] = {Min = 86, Max = 98},
|
||
[3] = {Min = 99, Max = 100},
|
||
}
|
||
local Rand = math.random(1, 100)
|
||
local ResultQuality = -1
|
||
for Quality, Rates in pairs(RateTable) do
|
||
if Rand >= Rates.Min and Rand <= Rates.Max then
|
||
ResultQuality = Quality
|
||
break
|
||
end
|
||
end
|
||
|
||
local StartSkillName = ESkillName.Counter
|
||
local EndSkillName = ESkillName.Bombing
|
||
|
||
if SkillType ~= nil then --刷出制定类型的技能(主动/被动)
|
||
if SkillType == ESkillType.Passive then
|
||
EndSkillName = ESkillName.Scabbing
|
||
elseif SkillType == ESkillType.Active then
|
||
StartSkillName = ESkillName.DeathStrike
|
||
end
|
||
end
|
||
|
||
local SkillNamePool = {}
|
||
for i = StartSkillName, EndSkillName do
|
||
table.insert(SkillNamePool, i)
|
||
end
|
||
|
||
local ResultSkillName = SkillNamePool[math.random(1, #SkillNamePool)]
|
||
|
||
local ItemData = {
|
||
-- 此处需要修改,中间两位是具体生成的
|
||
ItemID = 20000 + ResultQuality * 10 + ResultSkillName * 100,
|
||
ItemType = EItemType.SkillBook,
|
||
Quality = ResultQuality,
|
||
Count = 1,
|
||
}
|
||
UE.Log("DropItem: ItemType = %d, ItemType = %d, Quality = %d", ItemData.ItemID, ItemData.ItemType, ItemData.Quality)
|
||
return ItemData
|
||
end
|
||
|
||
function GenerateWeaponPartData()
|
||
local WeaponPartTypeRateTable = {
|
||
[EItemType.Muzzle] = {Min = 1, Max = 20},
|
||
[EItemType.Grip] = {Min = 21, Max = 40},
|
||
[EItemType.Mag] = {Min = 41, Max = 60},
|
||
[EItemType.Stock] = {Min = 61, Max = 80},
|
||
[EItemType.Scope] = {Min = 81, Max = 100},
|
||
}
|
||
local Rand1 = math.random(1, 100)
|
||
local ResultItemType = -1
|
||
for ItemType, Rates in pairs(WeaponPartTypeRateTable) do
|
||
if Rand1 >= Rates.Min and Rand1 <= Rates.Max then
|
||
ResultItemType = ItemType
|
||
break
|
||
end
|
||
end
|
||
|
||
local QualityRateTable = {
|
||
[1] = {Min = 1, Max = 95},
|
||
[2] = {Min = 96, Max = 99},
|
||
[3] = {Min = 100, Max = 100},
|
||
}
|
||
local Rand2 = math.random(1, 100)
|
||
local ResultQuality = -1
|
||
for Quality, Rates in pairs(QualityRateTable) do
|
||
if Rand2 >= Rates.Min and Rand2 <= Rates.Max then
|
||
ResultQuality = Quality
|
||
break
|
||
end
|
||
end
|
||
|
||
local WeaponClassTypeRateTable = {
|
||
[EWeaponClassType.WT_ShotGun] = {Min = 1, Max = 4},
|
||
[EWeaponClassType.WT_MachineGun] = {Min = 5, Max = 8},
|
||
[EWeaponClassType.WT_SubmachineGun] = {Min = 9, Max = 12},
|
||
[EWeaponClassType.WT_ShooterRifle] = {Min = 13, Max = 16},
|
||
[EWeaponClassType.WT_AssaultRifle] = {Min = 17, Max = 20},
|
||
[EWeaponClassType.WT_Sniper] = {Min = 21, Max = 24},
|
||
}
|
||
local Rand3 = math.random(1, 24)
|
||
local ResultWeaponClassType = -1
|
||
for WeaponClassType, Rates in pairs(WeaponClassTypeRateTable) do
|
||
if Rand3 >= Rates.Min and Rand3 <= Rates.Max then
|
||
ResultWeaponClassType = WeaponClassType
|
||
break
|
||
end
|
||
end
|
||
|
||
local ItemData = {
|
||
ItemID = 10000 + (ResultItemType + 1) * 1000 + ResultWeaponClassType * 100 + ResultQuality * 10,
|
||
ItemType = ResultItemType,
|
||
Quality = ResultQuality,
|
||
Count = 1,
|
||
WeaponClassType = ResultWeaponClassType
|
||
}
|
||
|
||
UE.Log("DropItem: ItemType = %d, ItemType = %d, Quality = %d, WeaponClassType = %d", ItemData.ItemID, ItemData.ItemType, ItemData.Quality, ItemData.WeaponClassType)
|
||
return ItemData
|
||
end
|
||
|
||
function GenerateStoneData()
|
||
local RateTable = {
|
||
[EItemType.SubstituteStone] = {Min = 1, Max = 100},
|
||
[EItemType.ScouringStone] = {Min = -1, Max = -100},
|
||
}
|
||
local Rand = math.random(1, 100)
|
||
local ResultItemType = -1
|
||
for ItemType, Rates in pairs(RateTable) do
|
||
if Rand >= Rates.Min and Rand <= Rates.Max then
|
||
ResultItemType = ItemType
|
||
break
|
||
end
|
||
end
|
||
|
||
local ItemData = {
|
||
ItemID = ResultItemType == EItemType.ScouringStone and 31000 or 32000,
|
||
ItemType = ResultItemType,
|
||
Quality = 1,
|
||
Count = 1,
|
||
}
|
||
|
||
UE.Log("DropItem: ItemType = %d, ItemType = %d, Quality = %d", ItemData.ItemID, ItemData.ItemType, ItemData.Quality)
|
||
return ItemData
|
||
end
|
||
|
||
function GenerateDropItemData()
|
||
local RateTable = {
|
||
[EDropItemSet.SkillBooks] = {Min = 1, Max = 95},
|
||
-- 默认不需要这个
|
||
--[EDropItemSet.WeaponParts] = {Min = -100, Max = -10},
|
||
[EDropItemSet.Stones] = {Min = 96, Max = 100},
|
||
}
|
||
|
||
local Rand = math.random(1, 100)
|
||
|
||
local ResultItemSet = -1
|
||
for ItemSet, Rates in pairs(RateTable) do
|
||
if Rand >= Rates.Min and Rand <= Rates.Max then
|
||
ResultItemSet = ItemSet
|
||
break
|
||
end
|
||
end
|
||
|
||
if ResultItemSet == EDropItemSet.WeaponParts then
|
||
return GenerateWeaponPartData()
|
||
elseif ResultItemSet == EDropItemSet.SkillBooks then
|
||
return GenerateSkillBookData()
|
||
else
|
||
return GenerateStoneData()
|
||
end
|
||
--return GenerateSkillBookData()
|
||
end
|
||
|
||
--创建随机词条
|
||
function RandomWeaponProperty(InVal)
|
||
-- 从200 中进行随机
|
||
math.randomseed(KismetSystemLibrary.GetGameTimeInSeconds(InVal) + math.random())
|
||
--这是计算 200
|
||
local Val = math.random(1,200)
|
||
local WeaponMainPropIndex = 0
|
||
for i = 1, 16 do
|
||
if Val > Tables.WeaponPropertyConfig[i].ProbabilityMin and Val <= Tables.WeaponPropertyConfig[i].ProbabilityMax then
|
||
WeaponMainPropIndex = i
|
||
break
|
||
end
|
||
end
|
||
|
||
local MinVal = Tables.WeaponPropertyConfig[WeaponMainPropIndex].RandomMin
|
||
local MaxVal = Tables.WeaponPropertyConfig[WeaponMainPropIndex].RandomMax
|
||
|
||
math.randomseed(KismetSystemLibrary.GetGameTimeInSeconds(InVal))
|
||
|
||
--返回属性的具体值
|
||
local c = math.random() * (MaxVal - MinVal) + MinVal
|
||
|
||
return WeaponMainPropIndex, c
|
||
end
|
||
|
||
--- @return boolean 是否可以随机到
|
||
function math.RandomValue(InPercent)
|
||
if InPercent > 1 then
|
||
InPercent = InPercent * 0.01
|
||
end
|
||
local RandomNum = math.random()
|
||
if RandomNum < InPercent then
|
||
return true
|
||
end
|
||
return false
|
||
end
|
||
|
||
function table.logTree(InTable, Addtions)
|
||
-- 首先检测一下是否是table
|
||
if Addtions == nil then
|
||
Addtions = ''
|
||
end
|
||
local RetStr = Addtions
|
||
if type(InTable) == 'table' then
|
||
for i, v in pairs(InTable) do
|
||
RetStr = RetStr .. "k: " .. tostring(i) .. '\t';
|
||
RetStr = RetStr .. "v: "
|
||
if type(v) == 'table' then
|
||
RetStr = RetStr .. '\ttype:table\n'
|
||
RetStr = RetStr .. table.logTree(v, "\t" .. Addtions)
|
||
RetStr = RetStr .. '\n'
|
||
else
|
||
RetStr = RetStr .. tostring(v) .. '\t';
|
||
end
|
||
-- RetStr = RetStr ..'\n'
|
||
end
|
||
else
|
||
RetStr = tostring(InTable)
|
||
end
|
||
return RetStr .. '\n';
|
||
end |