This commit is contained in:
Ggafrik 2025-07-05 00:20:53 +08:00
parent fc39c8c3b3
commit a01f2ebcad
4 changed files with 168 additions and 108 deletions

View File

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

View File

@ -0,0 +1,12 @@
export type Character = {
Instance: Model,
Config: any,
Root: BasePart,
Humanoid: Humanoid,
Origin: CFrame,
TargetPlayerUserID: string,
Connections: table,
Stats: table,
}
return {}

View File

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

View File

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