From a01f2ebcadf70eb6577eedd2ca231a460576f26d Mon Sep 17 00:00:00 2001 From: Ggafrik <906823881@qq.com> Date: Sat, 5 Jul 2025 00:20:53 +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/Base/Character.luau | 112 ++++++++++++++++++ src/ServerStorage/Base/TypeList.luau | 12 ++ src/ServerStorage/Proxy/DamageProxy.luau | 31 ++++- src/ServerStorage/Proxy/MobsProxy/init.luau | 121 +++----------------- 4 files changed, 168 insertions(+), 108 deletions(-) create mode 100644 src/ServerStorage/Base/Character.luau create mode 100644 src/ServerStorage/Base/TypeList.luau diff --git a/src/ServerStorage/Base/Character.luau b/src/ServerStorage/Base/Character.luau new file mode 100644 index 0000000..27ed32c --- /dev/null +++ b/src/ServerStorage/Base/Character.luau @@ -0,0 +1,112 @@ + +local Character = {} +Character.__index = Character + +local TypeList = require(script.Parent.TypeList) + +local LIMIT_ATTRIBUTE = { + "health" +} + +function Character.new(Player: Player, CharacterModel: Model, CharacterData: table): TypeList.Character + local newMobModel = CharacterModel + local HumanoidRootPart = newMobModel:FindFirstChild("HumanoidRootPart") :: BasePart + local mobHumanoid = newMobModel:FindFirstChild("Humanoid") :: Humanoid + + -- 生成表格数据 + local self = {} + self.Instance = newMobModel + self.Config = CharacterData + self.Root = HumanoidRootPart + self.Humanoid = mobHumanoid + self.Origin = HumanoidRootPart:GetPivot() + self.TargetPlayerUserID = Player.UserId + self.Connections = {} + self.Stats = {} + + -- 生成实例身上的配置数据 + local Attributes = Instance.new("Configuration") + Attributes.Name = "Attributes" + Attributes.Parent = self.Instance + for attributeKey, attributeValue in self.Config do + Attributes:SetAttribute(attributeKey, attributeValue) + -- 设置限制值 + if table.find(LIMIT_ATTRIBUTE, attributeKey) then + self.Config["max" .. attributeKey] = attributeValue + Attributes:SetAttribute("max" .. attributeKey, attributeValue) + end + + local conAttribute = self.AttributeChanged:Connect(function(attributeKey: string, attributeValue: number) + self:ChangeAttribute(attributeKey, attributeValue) + end) + table.insert(self.Connections, conAttribute) + end + + -- 配置角色状态数据 + local statsData = { + Died = false + } + local Stats = Instance.new("Configuration") + Stats.Name = "Stats" + Stats.Parent = self.Instance + for statKey, statValue in statsData do + self.Stats[statKey] = statValue + Stats:SetAttribute(statKey, statValue) + end + + -- 初始化原有功能数值 + mobHumanoid.WalkSpeed = CharacterData.walkSpeed + + -- 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] + mobHumanoid:SetStateEnabled(HumanoidStateType, false) + if mobHumanoid:GetState() == HumanoidStateType then + mobHumanoid:ChangeState(Enum.HumanoidStateType.Running) + end + end + return self +end + +function Character:GetAttribute(attributeKey: string) + return self.Config[attributeKey], self.Instance.Attributes:GetAttribute(attributeKey) +end + +function Character:ChangeAttribute(attributeKey: string, value: any) + local newValue = value + -- 限制最大值 + if table.find(LIMIT_ATTRIBUTE, attributeKey) then + if newValue > self.Config["max" .. attributeKey] then + newValue = self.Config["max" .. attributeKey] + end + end + -- 改变值 + self.Config[attributeKey] = newValue + self.Instance.Attributes:SetAttribute(attributeKey, newValue) + + -- 死亡判断 + if attributeKey == "health" and self.Stats.Died == false then + if self.Config[attributeKey] <= 0 then + self:Died() + end + end +end + +function Character:GetState(state: string) + return self.Stats[state], self.Instance.Stats:GetAttribute(state) +end + +function Character:ChangeState(state: string, value: any) + self.Stats[state] = value + self.Instance.Stats:SetAttribute(state, value) +end + +function Character:Died() + self:ChangeState("Died", true) + for _, connection in self.Connections do + connection:Disconnect() + end + self.Connections = nil + self.Instance:Destroy() + self = nil +end diff --git a/src/ServerStorage/Base/TypeList.luau b/src/ServerStorage/Base/TypeList.luau new file mode 100644 index 0000000..0bb7234 --- /dev/null +++ b/src/ServerStorage/Base/TypeList.luau @@ -0,0 +1,12 @@ +export type Character = { + Instance: Model, + Config: any, + Root: BasePart, + Humanoid: Humanoid, + Origin: CFrame, + TargetPlayerUserID: string, + Connections: table, + Stats: table, +} + +return {} \ No newline at end of file diff --git a/src/ServerStorage/Proxy/DamageProxy.luau b/src/ServerStorage/Proxy/DamageProxy.luau index 834f0fa..4d91da4 100644 --- a/src/ServerStorage/Proxy/DamageProxy.luau +++ b/src/ServerStorage/Proxy/DamageProxy.luau @@ -1,6 +1,12 @@ -- 伤害代理 local DamageProxy = {} +--> Services +local ServerStorage = game:GetService("ServerStorage") + +--> Variables +local TypeList = require(ServerStorage.Base.TypeList) + -- 伤害类型枚举 local DamageType = { NORMAL = "Normal", -- 普攻 @@ -14,22 +20,41 @@ local DamageTag = { export type DamageTag = "Normal" | "Critical" export type DamageType = "Normal" | "Skill" - export type DamageInfo = { Damage: number, Type: DamageType, Tag: DamageTag, } -function DamageProxy:TakeDamage(Caster: Model, Victim: Model, DamageInfos: {DamageInfo}) +-------------------------------------------------------------------------------- + +function DamageProxy:GetHumanoid(Target: Model) + if not Target:IsA("Model") then + warn("Target is not a Model") + return nil + end + local Humanoid = Target:FindFirstChild("Humanoid") + if not Humanoid then + warn("Humanoid not found", Target) + return nil + end + return Humanoid +end + +function DamageProxy:IsDied(Target: Model) + local Humanoid = self:GetHumanoid(Target) + return Humanoid:GetAttribute("Health") <= 0 +end + +function DamageProxy:TakeDamage(Caster: TypeList.Character, Victim: TypeList.Character, DamageInfos: {DamageInfo}) for _, DamageInfo in DamageInfos do local Damage = DamageInfo.Damage local DamageType = DamageInfo.DamageType local DamageTag = DamageInfo.DamageTag -- 伤害计算 + end - end diff --git a/src/ServerStorage/Proxy/MobsProxy/init.luau b/src/ServerStorage/Proxy/MobsProxy/init.luau index 0814387..e86ab1c 100644 --- a/src/ServerStorage/Proxy/MobsProxy/init.luau +++ b/src/ServerStorage/Proxy/MobsProxy/init.luau @@ -10,6 +10,7 @@ local Players = game:GetService("Players") local Utils = require(ReplicatedStorage.Tools.Utils) local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy) local AI = require(script.AI) +local Character = require(ServerStorage.Base.Character) --> Json local JsonMob = require(ReplicatedStorage.Json.Mob) @@ -41,133 +42,43 @@ end local Mob = {} Mob.__index = Mob -local LIMIT_ATTRIBUTE = { - "health" -} - function Mob.new(Player: Player, MobId: number) - -- 怪物实例目录判断 + -- 获取玩家怪物目录 local playerMobsFolder = GetPlayerMobsFolder(Player) if not playerMobsFolder then return end - -- 怪物数据获取 + + -- 获取怪物数据 local MobData = Utils:GetJsonData(JsonMob, MobId) if not MobData then warn("Mob Data not found", MobId) return end - -- 怪物模型获取 + + -- 获取怪物模型 local MobInstance = FindMobPrefab(MobData.Model) if not MobInstance then warn("Mob Prefab not found", MobData.Model) return end - -- 生成对应实例 + + -- 克隆怪物模型 local newMobModel = MobInstance:Clone() - local HumanoidRootPart = newMobModel:FindFirstChild("HumanoidRootPart") :: BasePart - local mobHumanoid = newMobModel:FindFirstChild("Humanoid") :: Humanoid - -- 生成表格数据 - local newMob = setmetatable({}, Mob) - newMob.Instance = newMobModel - newMob.Config = MobData - newMob.Root = HumanoidRootPart - newMob.Humanoid = mobHumanoid - newMob.Origin = HumanoidRootPart:GetPivot() - newMob.TargetPlayerUserID = Player.UserId - newMob.Connections = {} - newMob.Stats = {} + -- 调用父类Character的new方法,初始化通用属性 + local self = Character.new(Player, newMobModel, MobData) + setmetatable(self, Mob) -- 继承Mob方法 - -- 生成实例身上的配置数据 - local Attributes = Instance.new("Configuration") - Attributes.Name = "Attributes" - Attributes.Parent = newMob - for attributeKey, attributeValue in newMob.Config do - Attributes:SetAttribute(attributeKey, attributeValue) - -- 设置限制值 - if table.find(LIMIT_ATTRIBUTE, attributeKey) then - newMob.Config["max" .. attributeKey] = attributeValue - Attributes:SetAttribute("max" .. attributeKey, attributeValue) - end - - local conAttribute = newMob.AttributeChanged:Connect(function(attributeKey: string, attributeValue: number) - newMob:ChangeAttribute(attributeKey, attributeValue) - end) - table.insert(newMob.Connections, conAttribute) - end - - -- 配置角色状态数据 - local statsData = { - Died = false - } - local Stats = Instance.new("Configuration") - Stats.Name = "Stats" - Stats.Parent = newMob - for statKey, statValue in statsData do - newMob.Stats[statKey] = statValue - Stats:SetAttribute(statKey, statValue) - end - - -- 初始化原有功能数值 - mobHumanoid.WalkSpeed = MobData.walkSpeed -- 放入关卡中 newMobModel.Parent = playerMobsFolder - -- 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] - mobHumanoid:SetStateEnabled(HumanoidStateType, false) - if mobHumanoid:GetState() == HumanoidStateType then - mobHumanoid:ChangeState(Enum.HumanoidStateType.Running) - end - end - + -- 接入统一AI - -- Following has finished. Anchor assembly to optimize. - mobHumanoid.MoveToFinished:Connect(function() + self.Humanoid.MoveToFinished:Connect(function() if not AI:GetClosestPlayer(Mob) and not Mob.isDead then - HumanoidRootPart.Anchored = true + self.Root.Anchored = true else AI:StartTracking(Mob) end end) - return newMob -end - -function Mob:GetAttribute(attributeKey: string) - return self.Config[attributeKey], self.Instance.Attributes:GetAttribute(attributeKey) -end - -function Mob:ChangeAttribute(attributeKey: string, value: any) - local newValue = value - -- 限制最大值 - if table.find(LIMIT_ATTRIBUTE, attributeKey) then - if newValue > self.Config["max" .. attributeKey] then - newValue = self.Config["max" .. attributeKey] - end - end - -- 改变值 - self.Config[attributeKey] = newValue - self.Instance.Attributes:SetAttribute(attributeKey, newValue) - - -- 死亡判断 - if attributeKey == "health" and self.Stats.Died == false then - if self.Config[attributeKey] <= 0 then - self:Died() - end - end -end - -function Mob:GetState(state: string) - return self.Stats[state], self.Instance.Stats:GetAttribute(state) -end - -function Mob:ChangeState(state: string, value: any) - self.Stats[state] = value - self.Instance.Stats:SetAttribute(state, value) + return self end function Mob:Died() - self:ChangeState("Died", true) MobsProxy:RemoveMob(self.Player, self.Instance) - for _, connection in self.Connections do - connection:Disconnect() - end - self.Connections = nil - self.Instance:Destroy() - self = nil + Character.Died(self) end --------------------------------------------------------------------------------