更新
This commit is contained in:
parent
40e1eb3462
commit
42dbc86b3a
BIN
excel/level.xlsx
BIN
excel/level.xlsx
Binary file not shown.
36
export.py
36
export.py
@ -11,16 +11,13 @@ if not os.path.exists(output_dir):
|
|||||||
|
|
||||||
def parse_array_field(value, elem_type):
|
def parse_array_field(value, elem_type):
|
||||||
"""
|
"""
|
||||||
解析数组类型字段,支持[1,2,3]或1,2,3写法,自动去除空格和空字符串。
|
解析一维数组类型字段,支持[1,2,3]或1,2,3写法。
|
||||||
elem_type: 'int', 'float', 'string'等
|
|
||||||
"""
|
"""
|
||||||
if value is None:
|
if value is None:
|
||||||
return []
|
return []
|
||||||
s = str(value).strip()
|
s = str(value).strip()
|
||||||
# 支持带中括号写法
|
|
||||||
if s.startswith("[") and s.endswith("]"):
|
if s.startswith("[") and s.endswith("]"):
|
||||||
s = s[1:-1]
|
s = s[1:-1]
|
||||||
# 支持英文逗号和中文逗号
|
|
||||||
items = re.split(r'[,,]', s)
|
items = re.split(r'[,,]', s)
|
||||||
result = []
|
result = []
|
||||||
for v in items:
|
for v in items:
|
||||||
@ -41,6 +38,32 @@ def parse_array_field(value, elem_type):
|
|||||||
result.append(v)
|
result.append(v)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def parse_2d_array_field(value, elem_type):
|
||||||
|
"""
|
||||||
|
解析二维数组类型字段,支持[1,2],[3,4]或[[1,2],[3,4]]等写法。
|
||||||
|
保证导出始终为双数组结构。
|
||||||
|
"""
|
||||||
|
if value is None or str(value).strip() == "":
|
||||||
|
return []
|
||||||
|
s = str(value).strip()
|
||||||
|
# 去除最外层中括号
|
||||||
|
if s.startswith("[[") and s.endswith("]]"):
|
||||||
|
s = s[1:-1]
|
||||||
|
# 按 '],[' 拆分
|
||||||
|
parts = re.split(r'\]\s*,\s*\[', s)
|
||||||
|
result = []
|
||||||
|
for part in parts:
|
||||||
|
part = part.strip("[] ")
|
||||||
|
arr = parse_array_field(part, elem_type)
|
||||||
|
result.append(arr)
|
||||||
|
# 如果内容其实是一维数组(如单元格内容为1,2,3),也包一层
|
||||||
|
if len(result) == 1 and not isinstance(result[0], list):
|
||||||
|
result = [parse_array_field(s, elem_type)]
|
||||||
|
# 如果内容为空但原始字符串非空,也包一层
|
||||||
|
if not result and s:
|
||||||
|
result = [parse_array_field(s, elem_type)]
|
||||||
|
return result
|
||||||
|
|
||||||
for filename in os.listdir(excel_dir):
|
for filename in os.listdir(excel_dir):
|
||||||
if filename.endswith('.xlsx'):
|
if filename.endswith('.xlsx'):
|
||||||
filepath = os.path.join(excel_dir, filename)
|
filepath = os.path.join(excel_dir, filename)
|
||||||
@ -65,7 +88,10 @@ for filename in os.listdir(excel_dir):
|
|||||||
filtered_row = [row[i] if i < len(row) else None for i in valid_indices]
|
filtered_row = [row[i] if i < len(row) else None for i in valid_indices]
|
||||||
row_dict = {}
|
row_dict = {}
|
||||||
for h, t, v in zip(valid_headers, valid_types, filtered_row):
|
for h, t, v in zip(valid_headers, valid_types, filtered_row):
|
||||||
if t.endswith("[]"):
|
if t.endswith("[][]"):
|
||||||
|
elem_type = t[:-4]
|
||||||
|
row_dict[h] = parse_2d_array_field(v, elem_type)
|
||||||
|
elif t.endswith("[]"):
|
||||||
elem_type = t[:-2]
|
elem_type = t[:-2]
|
||||||
row_dict[h] = parse_array_field(v, elem_type)
|
row_dict[h] = parse_array_field(v, elem_type)
|
||||||
else:
|
else:
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
[
|
[
|
||||||
{"id":1,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":1,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":2,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":2,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":3,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":3,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":4,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":4,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":5,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]},
|
{"id":5,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]},
|
||||||
{"id":6,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":6,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":7,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":7,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":8,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":8,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":9,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":9,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":10,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]},
|
{"id":10,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]},
|
||||||
{"id":11,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":11,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":12,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":12,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":13,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":13,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":14,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":14,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":15,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]},
|
{"id":15,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]},
|
||||||
{"id":16,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":16,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":17,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":17,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":18,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
|
{"id":18,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":19,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
|
{"id":19,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]},
|
||||||
{"id":20,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]}
|
{"id":20,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]}
|
||||||
]
|
]
|
@ -110,3 +110,5 @@ function Character:Died()
|
|||||||
self.Instance:Destroy()
|
self.Instance:Destroy()
|
||||||
self = nil
|
self = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return Character
|
@ -41,9 +41,10 @@ end
|
|||||||
|
|
||||||
function MobLib.new(MobInstance: Model): Mobs.Mob
|
function MobLib.new(MobInstance: Model): Mobs.Mob
|
||||||
local HumanoidRootPart = MobInstance:FindFirstChild("HumanoidRootPart") :: BasePart
|
local HumanoidRootPart = MobInstance:FindFirstChild("HumanoidRootPart") :: BasePart
|
||||||
local Enemy = MobInstance:FindFirstChild("Enemy") :: Humanoid
|
local Enemy = MobInstance:WaitForChild("Enemy") :: Humanoid
|
||||||
local MobConfig = MobInstance:FindFirstChild("MobConfig") and require(MobInstance:FindFirstChild("MobConfig"))
|
local MobConfig = MobInstance:FindFirstChild("MobConfig") and require(MobInstance:FindFirstChild("MobConfig"))
|
||||||
if not HumanoidRootPart or not Enemy or not MobConfig then
|
if not HumanoidRootPart or not Enemy or not MobConfig then
|
||||||
|
print(HumanoidRootPart, Enemy, MobConfig)
|
||||||
error(("MobLib.new: Passed mob '%s' is missing vital components."):format(MobInstance.Name))
|
error(("MobLib.new: Passed mob '%s' is missing vital components."):format(MobInstance.Name))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ local Players = game:GetService("Players")
|
|||||||
--> Variables
|
--> Variables
|
||||||
local Utils = require(ReplicatedStorage.Tools.Utils)
|
local Utils = require(ReplicatedStorage.Tools.Utils)
|
||||||
local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
|
local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
|
||||||
|
local MobsProxy = require(ServerStorage.Proxy.MobsProxy)
|
||||||
|
local TypeList = require(ServerStorage.Base.TypeList)
|
||||||
|
|
||||||
--> Json
|
--> Json
|
||||||
local JsonLevel = require(ReplicatedStorage.Json.Level)
|
local JsonLevel = require(ReplicatedStorage.Json.Level)
|
||||||
@ -60,6 +62,48 @@ local function ExtraAddPlayerLevel(Player: Player, LevelData: table)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local EXCEPT_KEY = { "Task", "Mobs"}
|
||||||
|
local function ChangeValue(Player: Player, Folder: Instance, LevelKey: string, LevelValue: any)
|
||||||
|
if not Player or not Folder or not LevelKey or not LevelValue then return end
|
||||||
|
local ValueInstance = Folder:FindFirstChild(LevelKey)
|
||||||
|
if not ValueInstance then return end
|
||||||
|
|
||||||
|
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
|
||||||
|
if not table.find(EXCEPT_KEY, LevelKey) then ValueInstance.Value = LevelValue end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 怪物死亡,由初始化时传入
|
||||||
|
local function OnMobDied(Player: Player, Mob: TypeList.Character)
|
||||||
|
for _, mob in LevelProxy.pData[Player.UserId].Mobs do
|
||||||
|
if mob ~= Mob then continue end
|
||||||
|
|
||||||
|
table.remove(LevelProxy.pData[Player.UserId].Mobs, mob)
|
||||||
|
|
||||||
|
-- 怪物清除判断
|
||||||
|
local LevelData = Utils:GetJsonData(JsonLevel, LevelProxy.pData[Player.UserId].LevelId)
|
||||||
|
if LevelProxy.pData[Player.UserId].SpawnWaveFinish and #LevelProxy.pData[Player.UserId].Mobs == 0 then
|
||||||
|
if LevelProxy.pData[Player.UserId].NowWave < #LevelData["wave"] then
|
||||||
|
-- 波数增长
|
||||||
|
LevelProxy.pData[Player.UserId].NowWave = LevelProxy.pData[Player.UserId].NowWave + 1
|
||||||
|
-- 新波次重置怪物生成状态标记
|
||||||
|
local ChallengeFolder = LevelFolder:FindFirstChild("Challenge")
|
||||||
|
ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", false)
|
||||||
|
elseif LevelProxy.pData[Player.UserId].NowWave >= #LevelData["wave"] then
|
||||||
|
-- 结束判断
|
||||||
|
LevelProxy:ChallengeEnd(Player, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
function LevelProxy:InitPlayer(Player: Player)
|
function LevelProxy:InitPlayer(Player: Player)
|
||||||
@ -68,6 +112,7 @@ function LevelProxy:InitPlayer(Player: Player)
|
|||||||
local LevelFolder = Utils:CreateFolder(STORE_NAME, pData)
|
local LevelFolder = Utils:CreateFolder(STORE_NAME, pData)
|
||||||
local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder)
|
local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder)
|
||||||
local DungeonFolder = Utils:CreateFolder("Dungeon", LevelFolder)
|
local DungeonFolder = Utils:CreateFolder("Dungeon", LevelFolder)
|
||||||
|
local ChallengeFolder = Utils:CreateFolder("Challenge", LevelFolder)
|
||||||
-- 当前关卡状态
|
-- 当前关卡状态
|
||||||
Utils:CreateFolder("Stats", LevelFolder)
|
Utils:CreateFolder("Stats", LevelFolder)
|
||||||
|
|
||||||
@ -88,29 +133,108 @@ function LevelProxy:InitPlayer(Player: Player)
|
|||||||
for LevelKey, LevelValue in ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress do
|
for LevelKey, LevelValue in ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress do
|
||||||
CreateLevelInstance(Player, ProgressFolder, LevelKey, LevelValue)
|
CreateLevelInstance(Player, ProgressFolder, LevelKey, LevelValue)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 本地内容初始化(关卡挑战信息,不存储)
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 挑战关卡(挑战副本用另一个函数)
|
-- 挑战关卡(挑战副本用另一个函数)
|
||||||
function LevelProxy:ChallengeLevel(Player: Player, LevelId: number)
|
function LevelProxy:ChallengeLevel(Player: Player, LevelId: number)
|
||||||
|
local LevelData = Utils:GetJsonData(JsonLevel, LevelId)
|
||||||
|
if not LevelData then warn("Level Data not found", LevelId) return end
|
||||||
-- 给前端传数据,做表现
|
-- 给前端传数据,做表现
|
||||||
|
|
||||||
-- 场景后端生成
|
-- 场景后端生成
|
||||||
|
|
||||||
-- 后端生成当前关卡状态数据
|
-- 后端生成当前关卡状态数据
|
||||||
|
local LevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId)
|
||||||
|
local ChallengeFolder = LevelFolder:FindFirstChild("Challenge")
|
||||||
|
if not ChallengeFolder then return end
|
||||||
|
local levelTask = task.defer(function()
|
||||||
|
ChangeValue(Player, ChallengeFolder, "IsBoss", LevelData.type == 1 and true or false)
|
||||||
|
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", {})
|
||||||
|
|
||||||
|
if LevelData.timeLimit then
|
||||||
|
while task.wait(1) do
|
||||||
|
ChangeValue(Player, ChallengeFolder, "Time", LevelProxy.pData[Player.UserId].Time + 1)
|
||||||
|
|
||||||
|
-- 关卡生成
|
||||||
|
if LevelProxy.pData[Player.UserId].Wave < #LevelData.wave then
|
||||||
|
ChangeValue(Player, ChallengeFolder, "Wave", LevelProxy.pData[Player.UserId].Wave + 1)
|
||||||
|
end
|
||||||
|
if LevelProxy.pData[Player.UserId].NowWave < LevelProxy.pData[Player.UserId].ShouldWave 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
|
||||||
|
local mob = MobsProxy:CreateMob(Player, mobId, LevelData.atkBonus, LevelData.hpBonus, OnMobDied)
|
||||||
|
table.insert(LevelProxy.pData[Player.UserId].Mobs, mob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", true)
|
||||||
|
end
|
||||||
|
-- 时间结束
|
||||||
|
if LevelProxy.pData[Player.UserId].Time >= LevelData.timeLimit then
|
||||||
|
self:ChallengeEnd(Player, false)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
ChangeValue(Player, ChallengeFolder, "Task", levelTask)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 挑战结束
|
-- 挑战结束
|
||||||
function LevelProxy:ChallengeEnd(Player: Player)
|
function LevelProxy:ChallengeEnd(Player: Player, result: boolean)
|
||||||
|
local pData = Utils:GetPlayerDataFolder(Player)
|
||||||
|
local LevelFolder = Utils:CreateFolder(STORE_NAME, pData)
|
||||||
|
local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder)
|
||||||
|
-- 清除剩余怪物
|
||||||
|
for _, mob in LevelProxy.pData[Player.UserId].Mobs do
|
||||||
|
mob:Died()
|
||||||
|
end
|
||||||
|
LevelProxy.pData[Player.UserId].Mobs = {}
|
||||||
|
|
||||||
-- 判断玩家是否通关
|
-- 判断玩家是否通关
|
||||||
-- 通关后,没到最大关卡,关卡进度+1
|
if result then
|
||||||
-- 到达最大关卡不做处理
|
ChangeValue(Player, ProgressFolder, "LevelId", LevelProxy.pData[Player.UserId].LevelId + 1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function LevelProxy:OnPlayerRemoving(Player: Player)
|
function LevelProxy:OnPlayerRemoving(Player: Player)
|
||||||
|
-- 关卡文件夹清除
|
||||||
local PlayerLevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId)
|
local PlayerLevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId)
|
||||||
if PlayerLevelFolder then
|
if PlayerLevelFolder then
|
||||||
PlayerLevelFolder:Destroy()
|
PlayerLevelFolder:Destroy()
|
||||||
end
|
end
|
||||||
|
-- 关卡存储数据清除
|
||||||
|
if LevelProxy.pData[Player.UserId].Task then
|
||||||
|
LevelProxy.pData[Player.UserId].Task:Cancel()
|
||||||
|
end
|
||||||
|
LevelProxy.pData[Player.UserId] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(PlayerUserId: string)
|
-- ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(PlayerUserId: string)
|
||||||
|
@ -10,22 +10,24 @@
|
|||||||
|
|
||||||
--> Services
|
--> Services
|
||||||
local Players = game:GetService("Players")
|
local Players = game:GetService("Players")
|
||||||
|
local ServerStorage = game:GetService("ServerStorage")
|
||||||
|
|
||||||
--> Dependencies
|
--> Dependencies
|
||||||
local MobList = require(script.Parent.MobList)
|
local TypeList = require(ServerStorage.Base.TypeList)
|
||||||
|
|
||||||
--> Variables
|
--> Variables
|
||||||
|
local DamageProxy = require(ServerStorage.Proxy.DamageProxy)
|
||||||
local ActiveMobs = {}
|
local ActiveMobs = {}
|
||||||
local AI = {}
|
local AI = {}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- 获取两个单位之间的距离
|
-- 获取两个单位之间的距离
|
||||||
function AI:GetModelDistance(Unit1: Model, Unit2: Model): number
|
function AI:GetModelDistance(Unit1: TypeList.Character, Unit2: TypeList.Character): number
|
||||||
return (Unit1:GetPivot().Position - Unit2:GetPivot().Position).Magnitude
|
return (Unit1:GetPivot().Position - Unit2:GetPivot().Position).Magnitude
|
||||||
end
|
end
|
||||||
|
|
||||||
function AI:GetClosestPlayer(Mob: any): (Player?, number?)
|
function AI:GetClosestPlayer(Mob: TypeList.Character): (Player?, number?)
|
||||||
local Closest = {Player = nil, Magnitude = math.huge}
|
local Closest = {Player = nil, Magnitude = math.huge}
|
||||||
|
|
||||||
local ActivePlayer -- We retain a reference to this, so they have to get further away from the mob to stop following, instead of the usual distance.
|
local ActivePlayer -- We retain a reference to this, so they have to get further away from the mob to stop following, instead of the usual distance.
|
||||||
@ -97,7 +99,13 @@ task.defer(function()
|
|||||||
if not Player then return end
|
if not Player then return end
|
||||||
if AI:GetModelDistance(MobInstance, Player.Character) <= 5 then
|
if AI:GetModelDistance(MobInstance, Player.Character) <= 5 then
|
||||||
-- 调用伤害模块
|
-- 调用伤害模块
|
||||||
|
DamageProxy:TakeDamage(MobInstance, Player.Character, {
|
||||||
|
{
|
||||||
|
Damage = 10,
|
||||||
|
DamageType = DamageProxy.DamageType.PHYSICAL,
|
||||||
|
DamageTag = DamageProxy.DamageTag.NORMAL
|
||||||
|
}
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,9 +11,10 @@ local Utils = require(ReplicatedStorage.Tools.Utils)
|
|||||||
local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy)
|
local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy)
|
||||||
local AI = require(script.AI)
|
local AI = require(script.AI)
|
||||||
local Character = require(ServerStorage.Base.Character)
|
local Character = require(ServerStorage.Base.Character)
|
||||||
|
local TypeList = require(ServerStorage.Base.TypeList)
|
||||||
|
|
||||||
--> Json
|
--> Json
|
||||||
local JsonMob = require(ReplicatedStorage.Json.Mob)
|
local JsonMob = require(ReplicatedStorage.Json.Enemy)
|
||||||
|
|
||||||
--> Constants
|
--> Constants
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ end
|
|||||||
local Mob = {}
|
local Mob = {}
|
||||||
Mob.__index = Mob
|
Mob.__index = Mob
|
||||||
|
|
||||||
function Mob.new(Player: Player, MobId: number)
|
function Mob.new(Player: Player, MobId: number, OnMobDied: ((Player: Player, Mob: TypeList.Character) -> ())?)
|
||||||
-- 获取玩家怪物目录
|
-- 获取玩家怪物目录
|
||||||
local playerMobsFolder = GetPlayerMobsFolder(Player)
|
local playerMobsFolder = GetPlayerMobsFolder(Player)
|
||||||
if not playerMobsFolder then return end
|
if not playerMobsFolder then return end
|
||||||
@ -65,6 +66,9 @@ function Mob.new(Player: Player, MobId: number)
|
|||||||
-- 放入关卡中
|
-- 放入关卡中
|
||||||
newMobModel.Parent = playerMobsFolder
|
newMobModel.Parent = playerMobsFolder
|
||||||
|
|
||||||
|
-- 死亡函数
|
||||||
|
if OnMobDied then Mob.OnDied = OnMobDied end
|
||||||
|
|
||||||
-- 接入统一AI
|
-- 接入统一AI
|
||||||
self.Humanoid.MoveToFinished:Connect(function()
|
self.Humanoid.MoveToFinished:Connect(function()
|
||||||
if not AI:GetClosestPlayer(Mob) and not Mob.isDead then
|
if not AI:GetClosestPlayer(Mob) and not Mob.isDead then
|
||||||
@ -78,14 +82,18 @@ end
|
|||||||
|
|
||||||
function Mob:Died()
|
function Mob:Died()
|
||||||
MobsProxy:RemoveMob(self.Player, self.Instance)
|
MobsProxy:RemoveMob(self.Player, self.Instance)
|
||||||
|
if self.OnDied then self.OnDied(self.Player, self) end
|
||||||
Character.Died(self)
|
Character.Died(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- 给玩家创建怪物
|
-- 给玩家创建怪物
|
||||||
function MobsProxy:CreateMob(Player: Player, MobId: number)
|
function MobsProxy:CreateMob(Player: Player, MobId: number, AtkBonus: number?, HpBonus: number?, OnMobDied: ((Player: Player, Mob: TypeList.Character) -> ())?)
|
||||||
local Mob = Mob.new(Player, MobId)
|
local Mob = Mob.new(Player, MobId, OnMobDied)
|
||||||
|
-- 关卡系数
|
||||||
|
if AtkBonus then Mob:ChangeValue("attack", math.floor(Mob.attack * (AtkBonus / 1000))) end
|
||||||
|
if HpBonus then Mob:ChangeValue("hp", math.floor(Mob.hp * (HpBonus / 1000))) end
|
||||||
MobsProxy.pData[Player.UserId][Mob.Instance] = Mob
|
MobsProxy.pData[Player.UserId][Mob.Instance] = Mob
|
||||||
return Mob
|
return Mob
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user