This commit is contained in:
Ggafrik 2025-07-07 00:45:26 +08:00
parent 42dbc86b3a
commit d4435197ca
2 changed files with 214 additions and 212 deletions

View File

@ -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

View File

@ -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