From d4435197ca4324be8a3f1a60166ed53e734cd884 Mon Sep 17 00:00:00 2001 From: Ggafrik <906823881@qq.com> Date: Mon, 7 Jul 2025 00:45:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ServerStorage/Modules/MobLib/init.luau | 364 ++++++++++----------- src/ServerStorage/Proxy/LevelProxy.luau | 62 ++-- 2 files changed, 214 insertions(+), 212 deletions(-) diff --git a/src/ServerStorage/Modules/MobLib/init.luau b/src/ServerStorage/Modules/MobLib/init.luau index 3521715..3b5d03a 100644 --- a/src/ServerStorage/Modules/MobLib/init.luau +++ b/src/ServerStorage/Modules/MobLib/init.luau @@ -29,222 +29,222 @@ local Random = Random.new() local MobLib = {} -- Mirror table with the Mob constructor function -local Mob = {} -- Syntax sugar for mob-related functions -Mob.__index = Mob +-- local Mob = {} -- Syntax sugar for mob-related functions +-- Mob.__index = Mob -local MobsFolder = workspace:FindFirstChild("Mobs") -if not MobsFolder then - MobsFolder = Instance.new("Folder") - MobsFolder.Name = "Mobs" - MobsFolder.Parent = workspace -end +-- local MobsFolder = workspace:FindFirstChild("Mobs") +-- if not MobsFolder then +-- MobsFolder = Instance.new("Folder") +-- MobsFolder.Name = "Mobs" +-- MobsFolder.Parent = workspace +-- end -function MobLib.new(MobInstance: Model): Mobs.Mob - local HumanoidRootPart = MobInstance:FindFirstChild("HumanoidRootPart") :: BasePart - local Enemy = MobInstance:WaitForChild("Enemy") :: Humanoid - local MobConfig = MobInstance:FindFirstChild("MobConfig") and require(MobInstance:FindFirstChild("MobConfig")) - 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)) - end +-- function MobLib.new(MobInstance: Model): Mobs.Mob +-- local HumanoidRootPart = MobInstance:FindFirstChild("HumanoidRootPart") :: BasePart +-- local Enemy = MobInstance:WaitForChild("Enemy") :: Humanoid +-- local MobConfig = MobInstance:FindFirstChild("MobConfig") and require(MobInstance:FindFirstChild("MobConfig")) +-- 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)) +-- end - local Mob = setmetatable({}, Mob) - Mob.Instance = MobInstance - Mob.Config = MobConfig - Mob.Root = HumanoidRootPart - Mob.Enemy = Enemy - Mob.Origin = HumanoidRootPart:GetPivot() - Mob._Copy = MobInstance:Clone() +-- local Mob = setmetatable({}, Mob) +-- Mob.Instance = MobInstance +-- Mob.Config = MobConfig +-- Mob.Root = HumanoidRootPart +-- Mob.Enemy = Enemy +-- Mob.Origin = HumanoidRootPart:GetPivot() +-- Mob._Copy = MobInstance:Clone() - -- Initialize - Enemy.MaxHealth = MobConfig.Health - Enemy.Health = MobConfig.Health - Enemy.WalkSpeed = MobConfig.WalkSpeed - Enemy.JumpPower = MobConfig.JumpPower - HumanoidRootPart.Anchored = true - MobInstance.Parent = MobsFolder +-- -- Initialize +-- Enemy.MaxHealth = MobConfig.Health +-- Enemy.Health = MobConfig.Health +-- Enemy.WalkSpeed = MobConfig.WalkSpeed +-- Enemy.JumpPower = MobConfig.JumpPower +-- HumanoidRootPart.Anchored = true +-- MobInstance.Parent = MobsFolder - -- Set humanoid states (helps prevent falling down & useless calculations - you're unlikely to have an enemy climbing without pathfinding) - for _, EnumName in {"FallingDown", "Seated", "Flying", "Swimming", "Climbing"} do - local HumanoidStateType = Enum.HumanoidStateType[EnumName] - Enemy:SetStateEnabled(HumanoidStateType, false) - if Enemy:GetState() == HumanoidStateType then - Enemy:ChangeState(Enum.HumanoidStateType.Running) - end - end +-- -- Set humanoid states (helps prevent falling down & useless calculations - you're unlikely to have an enemy climbing without pathfinding) +-- for _, EnumName in {"FallingDown", "Seated", "Flying", "Swimming", "Climbing"} do +-- local HumanoidStateType = Enum.HumanoidStateType[EnumName] +-- Enemy:SetStateEnabled(HumanoidStateType, false) +-- if Enemy:GetState() == HumanoidStateType then +-- Enemy:ChangeState(Enum.HumanoidStateType.Running) +-- end +-- end - -- Damage - local function OnTouched(BasePart) - local Player = Players:GetPlayerFromCharacter(BasePart.Parent) - local Humanoid = Player and Player.Character:FindFirstChild("Humanoid") +-- -- Damage +-- local function OnTouched(BasePart) +-- local Player = Players:GetPlayerFromCharacter(BasePart.Parent) +-- local Humanoid = Player and Player.Character:FindFirstChild("Humanoid") - if Humanoid and Enemy.Health > 0 then - if DamageCooldown[Player.UserId] then - return - end - DamageCooldown[Player.UserId] = true - task.delay(0.5, function() - DamageCooldown[Player.UserId] = nil - end) +-- if Humanoid and Enemy.Health > 0 then +-- if DamageCooldown[Player.UserId] then +-- return +-- end +-- DamageCooldown[Player.UserId] = true +-- task.delay(0.5, function() +-- DamageCooldown[Player.UserId] = nil +-- end) - Humanoid.Health = math.clamp(Humanoid.Health - MobConfig.Damage, 0, Humanoid.MaxHealth) - ReplicatedStorage.Remotes.MobDamagedPlayer:FireClient(Player, MobInstance, MobConfig.Damage) - end - end +-- Humanoid.Health = math.clamp(Humanoid.Health - MobConfig.Damage, 0, Humanoid.MaxHealth) +-- ReplicatedStorage.Remotes.MobDamagedPlayer:FireClient(Player, MobInstance, MobConfig.Damage) +-- end +-- end - (MobInstance:WaitForChild("Hitbox") :: BasePart).Touched:Connect(OnTouched) +-- (MobInstance:WaitForChild("Hitbox") :: BasePart).Touched:Connect(OnTouched) - -- Respawn - Enemy.Died:Once(function() - if not Mob.isDead then - Mob.isDead = true - Mob:ActivateRagdoll() - Mob:AwardDrops() - task.wait(MobConfig.RespawnTime or 5) - Mob:Respawn() - end - end) +-- -- Respawn +-- Enemy.Died:Once(function() +-- if not Mob.isDead then +-- Mob.isDead = true +-- Mob:ActivateRagdoll() +-- Mob:AwardDrops() +-- task.wait(MobConfig.RespawnTime or 5) +-- Mob:Respawn() +-- end +-- end) - -- Following has finished. Anchor assembly to optimize. - Enemy.MoveToFinished:Connect(function() - if not AI:GetClosestPlayer(Mob) and not Mob.isDead then - HumanoidRootPart.Anchored = true - else - AI:StartTracking(Mob) - end - end) +-- -- Following has finished. Anchor assembly to optimize. +-- Enemy.MoveToFinished:Connect(function() +-- if not AI:GetClosestPlayer(Mob) and not Mob.isDead then +-- HumanoidRootPart.Anchored = true +-- else +-- AI:StartTracking(Mob) +-- end +-- end) - Mobs[MobInstance] = Mob +-- Mobs[MobInstance] = Mob - return Mob -end +-- return Mob +-- end -function Mob:Destroy() - if not self.Destroyed then - self.Destroyed = true +-- function Mob:Destroy() +-- if not self.Destroyed then +-- self.Destroyed = true - Mobs[self.Instance] = nil - self.Instance:Destroy() +-- Mobs[self.Instance] = nil +-- self.Instance:Destroy() - -- Remove instance references - self.Instance = nil - self.Root = nil - self.Enemy = nil - self._Copy = nil - end -end +-- -- Remove instance references +-- self.Instance = nil +-- self.Root = nil +-- self.Enemy = nil +-- self._Copy = nil +-- end +-- end -function Mob:TakeDamage(Damage: number) - local Enemy = self.Enemy - if not self.isDead then - Enemy.Health = math.clamp(Enemy.Health - Damage, 0, Enemy.MaxHealth) - end -end +-- function Mob:TakeDamage(Damage: number) +-- local Enemy = self.Enemy +-- if not self.isDead then +-- Enemy.Health = math.clamp(Enemy.Health - Damage, 0, Enemy.MaxHealth) +-- end +-- end -function Mob:Respawn() - if not self.Destroyed then - local NewMob = self._Copy - self:Destroy() - NewMob.Parent = MobsFolder - end -end +-- function Mob:Respawn() +-- if not self.Destroyed then +-- local NewMob = self._Copy +-- self:Destroy() +-- NewMob.Parent = MobsFolder +-- end +-- end -function Mob:ActivateRagdoll() - for _, Item in self.Instance:GetDescendants() do - if Item:IsA("Motor6D") then - local Attachment0 = Instance.new("Attachment") - Attachment0.CFrame = Item.C0 - Attachment0.Parent = Item.Part0 +-- function Mob:ActivateRagdoll() +-- for _, Item in self.Instance:GetDescendants() do +-- if Item:IsA("Motor6D") then +-- local Attachment0 = Instance.new("Attachment") +-- Attachment0.CFrame = Item.C0 +-- Attachment0.Parent = Item.Part0 - local Attachment1 = Instance.new("Attachment") - Attachment1.CFrame = Item.C1 - Attachment1.Parent = Item.Part1 +-- local Attachment1 = Instance.new("Attachment") +-- Attachment1.CFrame = Item.C1 +-- Attachment1.Parent = Item.Part1 - local Constraint = Instance.new("BallSocketConstraint") - Constraint.Attachment0 = Attachment0 - Constraint.Attachment1 = Attachment1 - Constraint.LimitsEnabled = true - Constraint.TwistLimitsEnabled = true - Constraint.Parent = Item.Parent +-- local Constraint = Instance.new("BallSocketConstraint") +-- Constraint.Attachment0 = Attachment0 +-- Constraint.Attachment1 = Attachment1 +-- Constraint.LimitsEnabled = true +-- Constraint.TwistLimitsEnabled = true +-- Constraint.Parent = Item.Parent - Item.Enabled = false - end - end -end +-- Item.Enabled = false +-- end +-- end +-- end -function Mob:AwardDrops() - local PlayerTags = self.Instance:FindFirstChild("PlayerTags") :: Configuration - if not PlayerTags then return end +-- function Mob:AwardDrops() +-- local PlayerTags = self.Instance:FindFirstChild("PlayerTags") :: Configuration +-- if not PlayerTags then return end - for UserId, Damage: number in PlayerTags:GetAttributes() do - UserId = tonumber(UserId) +-- for UserId, Damage: number in PlayerTags:GetAttributes() do +-- UserId = tonumber(UserId) - local Player = Players:GetPlayerByUserId(UserId) - if not Player then continue end +-- local Player = Players:GetPlayerByUserId(UserId) +-- if not Player then continue end - local Percent = Damage / self.Enemy.MaxHealth +-- local Percent = Damage / self.Enemy.MaxHealth - if Percent >= 0.25 then - local pData = PlayerData:FindFirstChild(Player.UserId) - local Statistics = pData:FindFirstChild("Stats") - local Items = pData:FindFirstChild("Items") +-- if Percent >= 0.25 then +-- local pData = PlayerData:FindFirstChild(Player.UserId) +-- local Statistics = pData:FindFirstChild("Stats") +-- local Items = pData:FindFirstChild("Items") - -- Stats - if Statistics then - for _, StatInfo in self.Config.Drops.Statistics do - local StatName: string = StatInfo[1] - local StatCount: number = StatInfo[2] +-- -- Stats +-- if Statistics then +-- for _, StatInfo in self.Config.Drops.Statistics do +-- local StatName: string = StatInfo[1] +-- local StatCount: number = StatInfo[2] - local Stat = Statistics:FindFirstChild(StatName) - if Stat then - Stat.Value += StatCount - end - end - end +-- local Stat = Statistics:FindFirstChild(StatName) +-- if Stat then +-- Stat.Value += StatCount +-- end +-- end +-- end - -- Items - if Items then - for _, ItemInfo in self.Config.Drops.Items do - local ItemType: string = ItemInfo[1] - local ItemName: string = ItemInfo[2] - local DropChance: number = ItemInfo[3] +-- -- Items +-- if Items then +-- for _, ItemInfo in self.Config.Drops.Items do +-- local ItemType: string = ItemInfo[1] +-- local ItemName: string = ItemInfo[2] +-- local DropChance: number = ItemInfo[3] - local Item = ContentLibrary[ItemType] and ContentLibrary[ItemType][ItemName] - if Item then - local isLucky = Random:NextInteger(1, DropChance) == 1 - if isLucky then - require(ServerStorage.Modules[ItemType .."Lib"]):Give(Player, Item) - ReplicatedStorage.Remotes.SendNotification:FireClient(Player, - "Item Dropped", - self.Config.Name .." dropped ".. Item.Name .." at a 1/".. FormatNumber(DropChance, "Suffix") .." chance.", - Item.Config.IconId - ) - end - else - warn("[Kit/MobLib/AwardDrops]: Item doesn't exist: '".. ItemType .."/".. ItemName ..".") - end - end - end +-- local Item = ContentLibrary[ItemType] and ContentLibrary[ItemType][ItemName] +-- if Item then +-- local isLucky = Random:NextInteger(1, DropChance) == 1 +-- if isLucky then +-- require(ServerStorage.Modules[ItemType .."Lib"]):Give(Player, Item) +-- ReplicatedStorage.Remotes.SendNotification:FireClient(Player, +-- "Item Dropped", +-- self.Config.Name .." dropped ".. Item.Name .." at a 1/".. FormatNumber(DropChance, "Suffix") .." chance.", +-- Item.Config.IconId +-- ) +-- end +-- else +-- warn("[Kit/MobLib/AwardDrops]: Item doesn't exist: '".. ItemType .."/".. ItemName ..".") +-- end +-- end +-- end - -- TeleportLocation (TP) - local TP = self.Config.TeleportLocation and workspace.TP:FindFirstChild(self.Config.TeleportLocation) - local Character = Player.Character - if TP and Character then - Character:PivotTo(TP.CFrame + Vector3.yAxis*4) - end - end - end -end +-- -- TeleportLocation (TP) +-- local TP = self.Config.TeleportLocation and workspace.TP:FindFirstChild(self.Config.TeleportLocation) +-- local Character = Player.Character +-- if TP and Character then +-- Character:PivotTo(TP.CFrame + Vector3.yAxis*4) +-- end +-- end +-- end +-- end -CollectionService:GetInstanceAddedSignal("Mob"):Connect(function(MobInstance) - if MobInstance:IsDescendantOf(workspace) then -- For some reason, HD Admin saves a copy of the map under ServerStorage (if you happen to use that), and the MobLib will attempt to clone its copy into workspace.Mobs. - MobLib.new(MobInstance) - end -end) +-- CollectionService:GetInstanceAddedSignal("Mob"):Connect(function(MobInstance) +-- if MobInstance:IsDescendantOf(workspace) then -- For some reason, HD Admin saves a copy of the map under ServerStorage (if you happen to use that), and the MobLib will attempt to clone its copy into workspace.Mobs. +-- MobLib.new(MobInstance) +-- end +-- end) -for _, MobInstance in CollectionService:GetTagged("Mob") do - task.defer(MobLib.new, MobInstance) -end +-- for _, MobInstance in CollectionService:GetTagged("Mob") do +-- task.defer(MobLib.new, MobInstance) +-- end return MobLib \ No newline at end of file diff --git a/src/ServerStorage/Proxy/LevelProxy.luau b/src/ServerStorage/Proxy/LevelProxy.luau index 27f55a4..d261ed2 100644 --- a/src/ServerStorage/Proxy/LevelProxy.luau +++ b/src/ServerStorage/Proxy/LevelProxy.luau @@ -108,16 +108,13 @@ end function LevelProxy:InitPlayer(Player: Player) local pData = Utils:GetPlayerDataFolder(Player) - if not pData then return end - local LevelFolder = Utils:CreateFolder(STORE_NAME, pData) - local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder) - local DungeonFolder = Utils:CreateFolder("Dungeon", LevelFolder) - local ChallengeFolder = Utils:CreateFolder("Challenge", LevelFolder) + if not pData then warn("Level pData not found", Player.UserId) return end + local PlayerLevelFolder = Utils:CreateFolder(Player.UserId, LevelFolder) + local ProgressFolder = Utils:CreateFolder("Progress", PlayerLevelFolder) + local DungeonFolder = Utils:CreateFolder("Dungeon", PlayerLevelFolder) + local ChallengeFolder = Utils:CreateFolder("Challenge", PlayerLevelFolder) -- 当前关卡状态 - Utils:CreateFolder("Stats", LevelFolder) - - -- 关卡目录下生成玩家目录 - local spawnFloder = Utils:CreateFolder(Player.UserId, game.Workspace:FindFirstChild(STORE_NAME)) + Utils:CreateFolder("Stats", PlayerLevelFolder) -- 新玩家数据初始化 if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then @@ -152,11 +149,13 @@ function LevelProxy:InitPlayer(Player: Player) if key == "Task" or key == "Mobs" then continue end CreateLevelInstance(Player, ChallengeFolder, key, value) end + + self:ChallengeLevel(Player, 1) end -- 挑战关卡(挑战副本用另一个函数) function LevelProxy:ChallengeLevel(Player: Player, LevelId: number) - local LevelData = Utils:GetJsonData(JsonLevel, LevelId) + local LevelData = Utils:GetIdDataFromJson(JsonLevel, LevelId) if not LevelData then warn("Level Data not found", LevelId) return end -- 给前端传数据,做表现 @@ -165,7 +164,7 @@ function LevelProxy:ChallengeLevel(Player: Player, LevelId: number) -- 后端生成当前关卡状态数据 local LevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId) local ChallengeFolder = LevelFolder:FindFirstChild("Challenge") - if not ChallengeFolder then return end + if not ChallengeFolder then warn("ChallengeFolder not found") return end local levelTask = task.defer(function() ChangeValue(Player, ChallengeFolder, "IsBoss", LevelData.type == 1 and true or false) ChangeValue(Player, ChallengeFolder, "LevelId", LevelId) @@ -174,32 +173,35 @@ function LevelProxy:ChallengeLevel(Player: Player, LevelId: number) ChangeValue(Player, ChallengeFolder, "ShouldWave", 1) ChangeValue(Player, ChallengeFolder, "MaxTime", LevelData.timeLimit or 0) ChangeValue(Player, ChallengeFolder, "Mobs", {}) + ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", false) - if LevelData.timeLimit then - while task.wait(1) do - ChangeValue(Player, ChallengeFolder, "Time", LevelProxy.pData[Player.UserId].Time + 1) + 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 + -- 关卡生成 + 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) + ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", true) + + 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) + print(mob) end - ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", true) end + ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", true) + end + if LevelData.timeLimit then -- 时间结束 if LevelProxy.pData[Player.UserId].Time >= LevelData.timeLimit then self:ChallengeEnd(Player, false) - break end end end