2025-07-03 01:48:06 +08:00
|
|
|
|
-- 关卡代理
|
|
|
|
|
local LevelProxy = {}
|
|
|
|
|
|
|
|
|
|
--> Services
|
|
|
|
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
|
|
|
local ServerStorage = game:GetService("ServerStorage")
|
|
|
|
|
local Players = game:GetService("Players")
|
|
|
|
|
|
|
|
|
|
--> Variables
|
|
|
|
|
local Utils = require(ReplicatedStorage.Tools.Utils)
|
|
|
|
|
local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
|
2025-07-07 00:03:47 +08:00
|
|
|
|
local MobsProxy = require(ServerStorage.Proxy.MobsProxy)
|
|
|
|
|
local TypeList = require(ServerStorage.Base.TypeList)
|
2025-07-03 01:48:06 +08:00
|
|
|
|
|
2025-07-12 05:06:14 +08:00
|
|
|
|
--> Dependencies
|
|
|
|
|
local Communicate = require(ServerStorage.Modules.Tools.Communicate)
|
|
|
|
|
|
2025-07-03 01:48:06 +08:00
|
|
|
|
--> Json
|
|
|
|
|
local JsonLevel = require(ReplicatedStorage.Json.Level)
|
2025-07-16 00:45:20 +08:00
|
|
|
|
local JsonParam = require(ReplicatedStorage.Json.Param)
|
2025-07-03 01:48:06 +08:00
|
|
|
|
|
2025-07-09 23:59:56 +08:00
|
|
|
|
--> Events
|
|
|
|
|
local BD_ChallengeEnd = ReplicatedStorage.Events.BD_ChallengeEnd
|
2025-07-12 05:06:14 +08:00
|
|
|
|
local RE_CleanPlayerPerformance = ReplicatedStorage.Events.RE_CleanPlayerPerformance
|
|
|
|
|
|
2025-07-09 23:59:56 +08:00
|
|
|
|
|
2025-07-03 01:48:06 +08:00
|
|
|
|
--> Constants
|
|
|
|
|
local STORE_NAME = "Level"
|
|
|
|
|
local ENUM_LEVEL_TYPE = {
|
|
|
|
|
Main = 1,
|
2025-07-07 23:52:31 +08:00
|
|
|
|
BossFail = 0,
|
2025-07-03 01:48:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- 初始化生成关卡目录
|
|
|
|
|
local LevelFolder = Utils:CreateFolder(STORE_NAME, game.Workspace)
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- 获取玩家关卡文件夹
|
2025-07-12 05:06:14 +08:00
|
|
|
|
local function GetPlayerLevelFolder(Player: Player, ChildFolderName: string?)
|
|
|
|
|
local PlayerFolder = LevelFolder:FindFirstChild(Player.UserId)
|
|
|
|
|
if not PlayerFolder then warn("PlayerFolder not found", Player.UserId) return end
|
|
|
|
|
if ChildFolderName then
|
|
|
|
|
local ChildFolder = PlayerFolder:FindFirstChild(ChildFolderName)
|
|
|
|
|
if not ChildFolder then warn("ChildFolder not found", ChildFolderName) return end
|
|
|
|
|
return ChildFolder
|
|
|
|
|
end
|
|
|
|
|
return PlayerFolder
|
2025-07-03 01:48:06 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 获取玩家关卡Workspace目录
|
|
|
|
|
local function GetPlayerLevelWorkspaceFolder(PlayerUserId: string)
|
|
|
|
|
return LevelFolder:FindFirstChild(PlayerUserId)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 创建关卡信息实例
|
2025-07-12 05:06:14 +08:00
|
|
|
|
local function CreateLevelInstance(Player: Player, Folder: Instance, LevelKey: string, LevelValue: any)
|
|
|
|
|
if not Player or not Folder or not LevelKey then return end
|
|
|
|
|
local InstanceType
|
|
|
|
|
if type(LevelValue) == "number" then
|
|
|
|
|
InstanceType = "NumberValue"
|
|
|
|
|
elseif type(LevelValue) == "boolean" then
|
|
|
|
|
InstanceType = "BoolValue"
|
|
|
|
|
elseif type(LevelValue) == "string" then
|
|
|
|
|
InstanceType = "StringValue"
|
|
|
|
|
else
|
|
|
|
|
InstanceType = "NumberValue"
|
|
|
|
|
end
|
|
|
|
|
local LevelInstance = Instance.new(InstanceType)
|
2025-07-03 01:48:06 +08:00
|
|
|
|
LevelInstance.Name = LevelKey
|
|
|
|
|
LevelInstance.Parent = Folder
|
|
|
|
|
LevelInstance.Value = LevelValue
|
|
|
|
|
return LevelInstance
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 初始化玩家关卡信息
|
|
|
|
|
local function ExtraAddPlayerLevel(Player: Player, LevelData: table)
|
|
|
|
|
if not Player or not LevelData then return end
|
|
|
|
|
-- 如果列表中不包含信息就添加到表中
|
|
|
|
|
for LevelKey, LevelValue in ENUM_LEVEL_TYPE do
|
|
|
|
|
if not LevelData[LevelKey] then
|
|
|
|
|
LevelData[LevelKey] = LevelValue
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2025-07-07 00:03:47 +08:00
|
|
|
|
local EXCEPT_KEY = { "Task", "Mobs"}
|
|
|
|
|
local function ChangeValue(Player: Player, Folder: Instance, LevelKey: string, LevelValue: any)
|
2025-07-12 05:06:14 +08:00
|
|
|
|
if not Player or not Folder or not LevelKey then warn("LevelProxy ChangeValue", Player.UserId, Folder.Name, LevelKey, LevelValue) return end
|
2025-07-15 01:12:05 +08:00
|
|
|
|
if table.find(EXCEPT_KEY, LevelKey) then return end
|
|
|
|
|
|
2025-07-07 00:03:47 +08:00
|
|
|
|
local ValueInstance = Folder:FindFirstChild(LevelKey)
|
2025-07-12 05:06:14 +08:00
|
|
|
|
if not ValueInstance then warn("ValueInstance not found", LevelKey) return end
|
2025-07-07 00:03:47 +08:00
|
|
|
|
|
|
|
|
|
local storeTable
|
|
|
|
|
if Folder.Name == "Challenge" then
|
|
|
|
|
storeTable = LevelProxy.pData[Player.UserId]
|
|
|
|
|
else
|
|
|
|
|
storeTable = ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
storeTable[LevelKey] = LevelValue
|
2025-07-15 01:12:05 +08:00
|
|
|
|
ValueInstance.Value = LevelValue
|
2025-07-07 00:03:47 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 怪物死亡,由初始化时传入
|
|
|
|
|
local function OnMobDied(Player: Player, Mob: TypeList.Character)
|
2025-07-16 00:45:20 +08:00
|
|
|
|
-- 怪物死亡后增加玩家货币
|
|
|
|
|
-- TODO:这里暂时没做对应玩家属性处理
|
|
|
|
|
local mob_died_get = Utils:GetIdDataFromJson(JsonParam, 3)
|
|
|
|
|
local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy)
|
|
|
|
|
local getNumber = math.floor(mob_died_get["intArray"][2] + LevelProxy:GetLevelGetBonus(Player) / 100)
|
|
|
|
|
PlayerInfoProxy:ChangeItemCount(Player, mob_died_get["intArray"][1], getNumber)
|
|
|
|
|
|
2025-07-07 00:03:47 +08:00
|
|
|
|
for _, mob in LevelProxy.pData[Player.UserId].Mobs do
|
2025-07-12 05:06:14 +08:00
|
|
|
|
if mob.Instance ~= Mob.Instance then continue end
|
2025-07-09 23:59:56 +08:00
|
|
|
|
table.remove(LevelProxy.pData[Player.UserId].Mobs, table.find(LevelProxy.pData[Player.UserId].Mobs, mob))
|
2025-07-07 00:03:47 +08:00
|
|
|
|
|
2025-07-12 05:06:14 +08:00
|
|
|
|
-- 怪物被击杀时做关卡数据处理
|
|
|
|
|
local LevelData = Utils:GetIdDataFromJson(JsonLevel, LevelProxy.pData[Player.UserId].LevelId)
|
2025-07-07 00:03:47 +08:00
|
|
|
|
if LevelProxy.pData[Player.UserId].SpawnWaveFinish and #LevelProxy.pData[Player.UserId].Mobs == 0 then
|
|
|
|
|
if LevelProxy.pData[Player.UserId].NowWave < #LevelData["wave"] then
|
2025-07-12 05:06:14 +08:00
|
|
|
|
-- -- 波数增长
|
|
|
|
|
LevelProxy.pData[Player.UserId].ShouldWave = LevelProxy.pData[Player.UserId].ShouldWave + 1
|
2025-07-07 00:03:47 +08:00
|
|
|
|
-- 新波次重置怪物生成状态标记
|
2025-07-12 05:06:14 +08:00
|
|
|
|
local ChallengeFolder = GetPlayerLevelFolder(Player, "Challenge")
|
2025-07-07 00:03:47 +08:00
|
|
|
|
ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", false)
|
|
|
|
|
elseif LevelProxy.pData[Player.UserId].NowWave >= #LevelData["wave"] then
|
|
|
|
|
-- 结束判断
|
|
|
|
|
LevelProxy:ChallengeEnd(Player, true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2025-07-03 01:48:06 +08:00
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function LevelProxy:InitPlayer(Player: Player)
|
2025-07-07 00:45:26 +08:00
|
|
|
|
local PlayerLevelFolder = Utils:CreateFolder(Player.UserId, LevelFolder)
|
|
|
|
|
local ProgressFolder = Utils:CreateFolder("Progress", PlayerLevelFolder)
|
|
|
|
|
local DungeonFolder = Utils:CreateFolder("Dungeon", PlayerLevelFolder)
|
2025-07-07 23:52:31 +08:00
|
|
|
|
-- 当前关卡状态 不存储
|
2025-07-07 00:45:26 +08:00
|
|
|
|
local ChallengeFolder = Utils:CreateFolder("Challenge", PlayerLevelFolder)
|
2025-07-07 23:52:31 +08:00
|
|
|
|
-- Utils:CreateFolder("Stats", PlayerLevelFolder)
|
2025-07-03 01:48:06 +08:00
|
|
|
|
|
|
|
|
|
-- 新玩家数据初始化
|
|
|
|
|
if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then
|
|
|
|
|
ArchiveProxy.pData[Player.UserId][STORE_NAME] = {}
|
|
|
|
|
ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress = {}
|
|
|
|
|
-- 副本之后再做
|
|
|
|
|
ArchiveProxy.pData[Player.UserId][STORE_NAME].Dungeon = {}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ExtraAddPlayerLevel(Player, ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress)
|
|
|
|
|
|
|
|
|
|
-- 前端变化
|
|
|
|
|
for LevelKey, LevelValue in ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress do
|
|
|
|
|
CreateLevelInstance(Player, ProgressFolder, LevelKey, LevelValue)
|
|
|
|
|
end
|
2025-07-07 00:03:47 +08:00
|
|
|
|
|
|
|
|
|
-- 本地内容初始化(关卡挑战信息,不存储)
|
|
|
|
|
if not LevelProxy.pData then LevelProxy.pData = {} end
|
|
|
|
|
if not LevelProxy.pData[Player.UserId] then LevelProxy.pData[Player.UserId] = {} end
|
|
|
|
|
LevelProxy.pData[Player.UserId].Task = nil
|
|
|
|
|
LevelProxy.pData[Player.UserId].Time = 0
|
|
|
|
|
LevelProxy.pData[Player.UserId].LevelId = 0
|
|
|
|
|
LevelProxy.pData[Player.UserId].MaxTime = 0
|
|
|
|
|
LevelProxy.pData[Player.UserId].IsBoss = false
|
|
|
|
|
LevelProxy.pData[Player.UserId].NowWave = 0
|
|
|
|
|
LevelProxy.pData[Player.UserId].ShouldWave = 0
|
|
|
|
|
LevelProxy.pData[Player.UserId].SpawnWaveFinish = false
|
|
|
|
|
LevelProxy.pData[Player.UserId].Mobs = {}
|
|
|
|
|
|
|
|
|
|
-- 关卡挑战信息前端
|
|
|
|
|
for key, value in LevelProxy.pData[Player.UserId] do
|
|
|
|
|
if key == "Task" or key == "Mobs" then continue end
|
|
|
|
|
CreateLevelInstance(Player, ChallengeFolder, key, value)
|
|
|
|
|
end
|
2025-07-03 01:48:06 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 挑战关卡(挑战副本用另一个函数)
|
|
|
|
|
function LevelProxy:ChallengeLevel(Player: Player, LevelId: number)
|
2025-07-31 19:36:32 +08:00
|
|
|
|
print("挑战关卡", LevelId)
|
2025-07-07 00:45:26 +08:00
|
|
|
|
local LevelData = Utils:GetIdDataFromJson(JsonLevel, LevelId)
|
2025-07-07 00:03:47 +08:00
|
|
|
|
if not LevelData then warn("Level Data not found", LevelId) return end
|
2025-07-03 01:48:06 +08:00
|
|
|
|
-- 给前端传数据,做表现
|
|
|
|
|
|
|
|
|
|
-- 场景后端生成
|
|
|
|
|
|
|
|
|
|
-- 后端生成当前关卡状态数据
|
2025-07-12 05:06:14 +08:00
|
|
|
|
local ChallengeFolder = GetPlayerLevelFolder(Player,"Challenge")
|
2025-07-07 00:45:26 +08:00
|
|
|
|
if not ChallengeFolder then warn("ChallengeFolder not found") return end
|
2025-07-09 23:59:56 +08:00
|
|
|
|
local levelTask = task.spawn(function()
|
2025-07-07 23:52:31 +08:00
|
|
|
|
ChangeValue(Player, ChallengeFolder, "IsBoss", LevelData.type == 2 and true or false)
|
2025-07-07 00:03:47 +08:00
|
|
|
|
ChangeValue(Player, ChallengeFolder, "LevelId", LevelId)
|
|
|
|
|
ChangeValue(Player, ChallengeFolder, "Time", 0)
|
|
|
|
|
ChangeValue(Player, ChallengeFolder, "NowWave", 0)
|
|
|
|
|
ChangeValue(Player, ChallengeFolder, "ShouldWave", 1)
|
|
|
|
|
ChangeValue(Player, ChallengeFolder, "MaxTime", LevelData.timeLimit or 0)
|
|
|
|
|
ChangeValue(Player, ChallengeFolder, "Mobs", {})
|
2025-07-07 00:45:26 +08:00
|
|
|
|
ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", false)
|
|
|
|
|
|
|
|
|
|
while task.wait(1) do
|
|
|
|
|
ChangeValue(Player, ChallengeFolder, "Time", LevelProxy.pData[Player.UserId].Time + 1)
|
|
|
|
|
|
|
|
|
|
-- 关卡生成
|
|
|
|
|
if LevelProxy.pData[Player.UserId].NowWave < #LevelData.wave and
|
|
|
|
|
LevelProxy.pData[Player.UserId].NowWave < LevelProxy.pData[Player.UserId].ShouldWave and
|
|
|
|
|
LevelProxy.pData[Player.UserId].SpawnWaveFinish == false then
|
|
|
|
|
ChangeValue(Player, ChallengeFolder, "NowWave", LevelProxy.pData[Player.UserId].NowWave + 1)
|
|
|
|
|
|
|
|
|
|
local waveData = LevelData.wave[LevelProxy.pData[Player.UserId].NowWave]
|
|
|
|
|
for i = 1, #waveData, 3 do
|
|
|
|
|
local mobId = waveData[i + 1]
|
|
|
|
|
local mobCount = waveData[i + 2]
|
|
|
|
|
for _ = 1, mobCount do
|
2025-07-12 05:06:14 +08:00
|
|
|
|
print("怪物增益", LevelData.atkBonus, LevelData.hpBonus)
|
2025-07-07 00:45:26 +08:00
|
|
|
|
local mob = MobsProxy:CreateMob(Player, mobId, LevelData.atkBonus, LevelData.hpBonus, OnMobDied)
|
|
|
|
|
table.insert(LevelProxy.pData[Player.UserId].Mobs, mob)
|
2025-07-07 00:03:47 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2025-07-07 00:45:26 +08:00
|
|
|
|
ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", true)
|
|
|
|
|
end
|
2025-07-07 23:52:31 +08:00
|
|
|
|
|
|
|
|
|
if LevelProxy.pData[Player.UserId].NowWave >= LevelProxy.pData[Player.UserId].ShouldWave and
|
|
|
|
|
LevelProxy.pData[Player.UserId].SpawnWaveFinish == true and
|
|
|
|
|
#LevelProxy.pData[Player.UserId].Mobs == 0 then
|
|
|
|
|
local maxWave = #LevelData.wave
|
|
|
|
|
if LevelProxy.pData[Player.UserId].NowWave < maxWave then
|
|
|
|
|
-- 下一波
|
2025-07-12 05:06:14 +08:00
|
|
|
|
ChangeValue(Player, ChallengeFolder, "ShouldWave", LevelProxy.pData[Player.UserId].ShouldWave)
|
2025-07-07 23:52:31 +08:00
|
|
|
|
else
|
|
|
|
|
-- 挑战胜利
|
|
|
|
|
self:ChallengeEnd(Player, true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 时间结束
|
2025-07-07 00:45:26 +08:00
|
|
|
|
if LevelData.timeLimit then
|
2025-07-07 00:03:47 +08:00
|
|
|
|
if LevelProxy.pData[Player.UserId].Time >= LevelData.timeLimit then
|
|
|
|
|
self:ChallengeEnd(Player, false)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end)
|
2025-07-09 23:59:56 +08:00
|
|
|
|
LevelProxy.pData[Player.UserId].Task = levelTask
|
2025-07-03 01:48:06 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 挑战结束
|
2025-07-07 00:03:47 +08:00
|
|
|
|
function LevelProxy:ChallengeEnd(Player: Player, result: boolean)
|
2025-07-12 05:06:14 +08:00
|
|
|
|
local ProgressFolder = GetPlayerLevelFolder(Player, "Progress")
|
2025-07-09 23:59:56 +08:00
|
|
|
|
|
|
|
|
|
-- 停止关卡循环
|
|
|
|
|
if LevelProxy.pData[Player.UserId].Task then
|
|
|
|
|
task.cancel(LevelProxy.pData[Player.UserId].Task)
|
|
|
|
|
LevelProxy.pData[Player.UserId].Task = nil
|
|
|
|
|
end
|
|
|
|
|
|
2025-07-07 00:03:47 +08:00
|
|
|
|
-- 清除剩余怪物
|
2025-07-31 19:36:32 +08:00
|
|
|
|
for _, mob in pairs(LevelProxy.pData[Player.UserId].Mobs) do mob:Died(true) end
|
2025-07-07 00:03:47 +08:00
|
|
|
|
LevelProxy.pData[Player.UserId].Mobs = {}
|
|
|
|
|
|
2025-07-12 05:06:14 +08:00
|
|
|
|
-- 清除玩家表现
|
|
|
|
|
Communicate:SendToClientFree(RE_CleanPlayerPerformance, Player)
|
|
|
|
|
|
2025-07-03 01:48:06 +08:00
|
|
|
|
-- 判断玩家是否通关
|
2025-07-07 00:03:47 +08:00
|
|
|
|
if result then
|
2025-07-12 05:06:14 +08:00
|
|
|
|
ChangeValue(Player, ProgressFolder, "Main", LevelProxy.pData[Player.UserId].LevelId + 1)
|
2025-07-07 23:52:31 +08:00
|
|
|
|
else
|
|
|
|
|
local IsBoss = LevelProxy.pData[Player.UserId].IsBoss
|
|
|
|
|
if IsBoss then
|
|
|
|
|
ChangeValue(Player, ProgressFolder, "BossFail", LevelProxy.pData[Player.UserId].LevelId)
|
|
|
|
|
end
|
2025-07-07 00:03:47 +08:00
|
|
|
|
end
|
2025-07-09 23:59:56 +08:00
|
|
|
|
BD_ChallengeEnd:Fire(Player, LevelProxy.pData[Player.UserId].LevelId, result)
|
2025-07-03 01:48:06 +08:00
|
|
|
|
end
|
|
|
|
|
|
2025-07-16 00:45:20 +08:00
|
|
|
|
-- 获得关卡产出货币百分比
|
|
|
|
|
function LevelProxy:GetLevelGetBonus(Player: Player)
|
|
|
|
|
if not Player then warn('获取关卡产出货币百分比失败: ', Player.Name) return end
|
|
|
|
|
local level_get_bonus = Utils:GetIdDataFromJson(JsonParam, 2)
|
|
|
|
|
local level = ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress.Main
|
|
|
|
|
return level_get_bonus["intArray"][1] + (level - 1) * level_get_bonus["intArray"][2]
|
|
|
|
|
end
|
|
|
|
|
|
2025-07-03 01:48:06 +08:00
|
|
|
|
function LevelProxy:OnPlayerRemoving(Player: Player)
|
2025-07-07 00:03:47 +08:00
|
|
|
|
-- 关卡文件夹清除
|
2025-07-03 01:48:06 +08:00
|
|
|
|
local PlayerLevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId)
|
|
|
|
|
if PlayerLevelFolder then
|
|
|
|
|
PlayerLevelFolder:Destroy()
|
|
|
|
|
end
|
2025-07-07 00:03:47 +08:00
|
|
|
|
-- 关卡存储数据清除
|
|
|
|
|
if LevelProxy.pData[Player.UserId].Task then
|
2025-07-09 23:59:56 +08:00
|
|
|
|
task.cancel(LevelProxy.pData[Player.UserId].Task)
|
2025-07-07 00:03:47 +08:00
|
|
|
|
end
|
|
|
|
|
LevelProxy.pData[Player.UserId] = nil
|
2025-07-03 01:48:06 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(PlayerUserId: string)
|
|
|
|
|
-- LevelProxy:OnPlayerRemoving(PlayerUserId)
|
|
|
|
|
-- end)
|
|
|
|
|
|
|
|
|
|
Players.PlayerRemoving:Connect(function(Player: Player)
|
|
|
|
|
LevelProxy:OnPlayerRemoving(Player)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
return LevelProxy
|