This commit is contained in:
gechangfu 2025-07-30 19:46:01 +08:00
parent 678c6035a9
commit 11ddf9fe1f
25 changed files with 495 additions and 83 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +1,4 @@
[
{"id":20000,"type":1,"icon":1,"behaviourName":"SwordWave","upgradeCost":[30000,5,10],"upgradeValue":[10,200],"recycle":[30000,100]}
{"id":20000,"type":1,"icon":1,"behaviourName":"Attack","upgradeCost":[30000,5,0],"upgradeValue":[10,0],"recycle":[30000,0]},
{"id":20001,"type":1,"icon":1,"behaviourName":"SwordWave","upgradeCost":[30000,5,10],"upgradeValue":[10,200],"recycle":[30000,100]}
]

View File

@ -1,3 +1,4 @@
[
{"id":1,"name":"SwordWave","rbxId":"133335766636767","priority":"Action"}
{"id":1,"name":"SwordWave","rbxId":"133335766636767","priority":"Action"},
{"id":2,"name":"Attack","rbxId":"17283773476","priority":"Action"}
]

View File

@ -14,7 +14,7 @@
{"id":13,"type":1,"effectAttribute":"shadowDef","battleValue":[1,10],"iconId":13},
{"id":14,"type":2,"effectAttribute":"attackRate","battleValue":[1,10],"iconId":14},
{"id":15,"type":2,"effectAttribute":"hpRate","battleValue":[1,10],"iconId":15},
{"id":16,"type":2,"effectAttribute":"atkSpeed","battleValue":[1,10],"iconId":16},
{"id":16,"type":1,"effectAttribute":"atkSpeed","battleValue":[1,10],"iconId":16},
{"id":20,"type":2,"effectAttribute":"critRate","battleValue":[1,10],"iconId":17},
{"id":21,"type":2,"effectAttribute":"critDamageRate","battleValue":[1,10],"iconId":18},
{"id":22,"type":2,"effectAttribute":"atkSpeedRate","battleValue":[1,10],"iconId":19},

View File

@ -1,5 +1,5 @@
[
{"id":1,"type":1,"name":1,"attack":83,"hp":400,"walkSpeed":10,"attackSpeed":1,"model":"Thief"},
{"id":2,"type":1,"name":2,"attack":30,"hp":300,"walkSpeed":10,"attackSpeed":1,"model":"Thief"},
{"id":1000,"type":2,"name":1000,"attack":240,"hp":2000,"walkSpeed":20,"attackSpeed":1,"model":"Thief"}
{"id":1,"type":1,"name":1,"attack":83,"hp":400,"walkSpeed":8,"attackSpeed":1,"model":"Thief"},
{"id":2,"type":1,"name":2,"attack":30,"hp":300,"walkSpeed":8,"attackSpeed":1,"model":"Thief"},
{"id":1000,"type":2,"name":1000,"attack":240,"hp":2000,"walkSpeed":4,"attackSpeed":1,"model":"Thief"}
]

View File

@ -1,52 +1,52 @@
[
{"id":1,"type":1,"timeLimit":null,"atkBonus":500,"hpBonus":500,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":2,"type":1,"timeLimit":null,"atkBonus":520,"hpBonus":520,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":3,"type":1,"timeLimit":null,"atkBonus":540,"hpBonus":540,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":4,"type":1,"timeLimit":null,"atkBonus":560,"hpBonus":560,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":1,"type":1,"timeLimit":null,"atkBonus":500,"hpBonus":500,"wave":[[10,1,1,50,1,1]]},
{"id":2,"type":1,"timeLimit":null,"atkBonus":520,"hpBonus":520,"wave":[[10,1,1,50,1,1]]},
{"id":3,"type":1,"timeLimit":null,"atkBonus":540,"hpBonus":540,"wave":[[10,1,1,50,1,1]]},
{"id":4,"type":1,"timeLimit":null,"atkBonus":560,"hpBonus":560,"wave":[[10,1,1,50,1,1]]},
{"id":5,"type":2,"timeLimit":60,"atkBonus":1050,"hpBonus":1050,"wave":[[10,1000,1]]},
{"id":6,"type":1,"timeLimit":null,"atkBonus":600,"hpBonus":600,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":7,"type":1,"timeLimit":null,"atkBonus":620,"hpBonus":620,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":8,"type":1,"timeLimit":null,"atkBonus":640,"hpBonus":640,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":9,"type":1,"timeLimit":null,"atkBonus":660,"hpBonus":660,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":6,"type":1,"timeLimit":null,"atkBonus":600,"hpBonus":600,"wave":[[10,1,1,50,1,1]]},
{"id":7,"type":1,"timeLimit":null,"atkBonus":620,"hpBonus":620,"wave":[[10,1,1,50,1,1]]},
{"id":8,"type":1,"timeLimit":null,"atkBonus":640,"hpBonus":640,"wave":[[10,1,1,50,1,1]]},
{"id":9,"type":1,"timeLimit":null,"atkBonus":660,"hpBonus":660,"wave":[[10,1,1,50,1,1]]},
{"id":10,"type":2,"timeLimit":60,"atkBonus":1100,"hpBonus":1100,"wave":[[10,1000,1]]},
{"id":11,"type":1,"timeLimit":null,"atkBonus":700,"hpBonus":700,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":12,"type":1,"timeLimit":null,"atkBonus":720,"hpBonus":720,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":13,"type":1,"timeLimit":null,"atkBonus":740,"hpBonus":740,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":14,"type":1,"timeLimit":null,"atkBonus":760,"hpBonus":760,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":11,"type":1,"timeLimit":null,"atkBonus":700,"hpBonus":700,"wave":[[10,1,1,50,1,1]]},
{"id":12,"type":1,"timeLimit":null,"atkBonus":720,"hpBonus":720,"wave":[[10,1,1,50,1,1]]},
{"id":13,"type":1,"timeLimit":null,"atkBonus":740,"hpBonus":740,"wave":[[10,1,1,50,1,1]]},
{"id":14,"type":1,"timeLimit":null,"atkBonus":760,"hpBonus":760,"wave":[[10,1,1,50,1,1]]},
{"id":15,"type":2,"timeLimit":60,"atkBonus":1150,"hpBonus":1150,"wave":[[10,1000,1]]},
{"id":16,"type":1,"timeLimit":null,"atkBonus":800,"hpBonus":800,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":17,"type":1,"timeLimit":null,"atkBonus":820,"hpBonus":820,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":18,"type":1,"timeLimit":null,"atkBonus":840,"hpBonus":840,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":19,"type":1,"timeLimit":null,"atkBonus":860,"hpBonus":860,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":16,"type":1,"timeLimit":null,"atkBonus":800,"hpBonus":800,"wave":[[10,1,1,50,1,1]]},
{"id":17,"type":1,"timeLimit":null,"atkBonus":820,"hpBonus":820,"wave":[[10,1,1,50,1,1]]},
{"id":18,"type":1,"timeLimit":null,"atkBonus":840,"hpBonus":840,"wave":[[10,1,1,50,1,1]]},
{"id":19,"type":1,"timeLimit":null,"atkBonus":860,"hpBonus":860,"wave":[[10,1,1,50,1,1]]},
{"id":20,"type":2,"timeLimit":60,"atkBonus":1250,"hpBonus":1250,"wave":[[10,1000,1]]},
{"id":21,"type":1,"timeLimit":null,"atkBonus":900,"hpBonus":900,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":22,"type":1,"timeLimit":null,"atkBonus":920,"hpBonus":920,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":23,"type":1,"timeLimit":null,"atkBonus":940,"hpBonus":940,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":24,"type":1,"timeLimit":null,"atkBonus":960,"hpBonus":960,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":21,"type":1,"timeLimit":null,"atkBonus":900,"hpBonus":900,"wave":[[10,1,1,50,1,1]]},
{"id":22,"type":1,"timeLimit":null,"atkBonus":920,"hpBonus":920,"wave":[[10,1,1,50,1,1]]},
{"id":23,"type":1,"timeLimit":null,"atkBonus":940,"hpBonus":940,"wave":[[10,1,1,50,1,1]]},
{"id":24,"type":1,"timeLimit":null,"atkBonus":960,"hpBonus":960,"wave":[[10,1,1,50,1,1]]},
{"id":25,"type":2,"timeLimit":60,"atkBonus":1350,"hpBonus":1350,"wave":[[10,1000,1]]},
{"id":26,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":27,"type":1,"timeLimit":null,"atkBonus":1020,"hpBonus":1020,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":28,"type":1,"timeLimit":null,"atkBonus":1040,"hpBonus":1040,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":29,"type":1,"timeLimit":null,"atkBonus":1060,"hpBonus":1060,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":26,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,50,1,1]]},
{"id":27,"type":1,"timeLimit":null,"atkBonus":1020,"hpBonus":1020,"wave":[[10,1,1,50,1,1]]},
{"id":28,"type":1,"timeLimit":null,"atkBonus":1040,"hpBonus":1040,"wave":[[10,1,1,50,1,1]]},
{"id":29,"type":1,"timeLimit":null,"atkBonus":1060,"hpBonus":1060,"wave":[[10,1,1,50,1,1]]},
{"id":30,"type":2,"timeLimit":60,"atkBonus":1500,"hpBonus":1500,"wave":[[10,1000,1]]},
{"id":31,"type":1,"timeLimit":null,"atkBonus":1100,"hpBonus":1100,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":32,"type":1,"timeLimit":null,"atkBonus":1120,"hpBonus":1120,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":33,"type":1,"timeLimit":null,"atkBonus":1140,"hpBonus":1140,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":34,"type":1,"timeLimit":null,"atkBonus":1160,"hpBonus":1160,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":31,"type":1,"timeLimit":null,"atkBonus":1100,"hpBonus":1100,"wave":[[10,1,1,50,1,1]]},
{"id":32,"type":1,"timeLimit":null,"atkBonus":1120,"hpBonus":1120,"wave":[[10,1,1,50,1,1]]},
{"id":33,"type":1,"timeLimit":null,"atkBonus":1140,"hpBonus":1140,"wave":[[10,1,1,50,1,1]]},
{"id":34,"type":1,"timeLimit":null,"atkBonus":1160,"hpBonus":1160,"wave":[[10,1,1,50,1,1]]},
{"id":35,"type":2,"timeLimit":60,"atkBonus":2000,"hpBonus":2000,"wave":[[10,1000,1]]},
{"id":36,"type":1,"timeLimit":null,"atkBonus":1200,"hpBonus":1200,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":37,"type":1,"timeLimit":null,"atkBonus":1220,"hpBonus":1220,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":38,"type":1,"timeLimit":null,"atkBonus":1240,"hpBonus":1240,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":39,"type":1,"timeLimit":null,"atkBonus":1260,"hpBonus":1260,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":36,"type":1,"timeLimit":null,"atkBonus":1200,"hpBonus":1200,"wave":[[10,1,1,50,1,1]]},
{"id":37,"type":1,"timeLimit":null,"atkBonus":1220,"hpBonus":1220,"wave":[[10,1,1,50,1,1]]},
{"id":38,"type":1,"timeLimit":null,"atkBonus":1240,"hpBonus":1240,"wave":[[10,1,1,50,1,1]]},
{"id":39,"type":1,"timeLimit":null,"atkBonus":1260,"hpBonus":1260,"wave":[[10,1,1,50,1,1]]},
{"id":40,"type":2,"timeLimit":60,"atkBonus":2500,"hpBonus":2500,"wave":[[10,1000,1]]},
{"id":41,"type":1,"timeLimit":null,"atkBonus":1300,"hpBonus":1300,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":42,"type":1,"timeLimit":null,"atkBonus":1320,"hpBonus":1320,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":43,"type":1,"timeLimit":null,"atkBonus":1340,"hpBonus":1340,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":44,"type":1,"timeLimit":null,"atkBonus":1360,"hpBonus":1360,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":41,"type":1,"timeLimit":null,"atkBonus":1300,"hpBonus":1300,"wave":[[10,1,1,50,1,1]]},
{"id":42,"type":1,"timeLimit":null,"atkBonus":1320,"hpBonus":1320,"wave":[[10,1,1,50,1,1]]},
{"id":43,"type":1,"timeLimit":null,"atkBonus":1340,"hpBonus":1340,"wave":[[10,1,1,50,1,1]]},
{"id":44,"type":1,"timeLimit":null,"atkBonus":1360,"hpBonus":1360,"wave":[[10,1,1,50,1,1]]},
{"id":45,"type":2,"timeLimit":60,"atkBonus":3000,"hpBonus":3000,"wave":[[10,1000,1]]},
{"id":46,"type":1,"timeLimit":null,"atkBonus":1400,"hpBonus":1400,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":47,"type":1,"timeLimit":null,"atkBonus":1420,"hpBonus":1420,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":48,"type":1,"timeLimit":null,"atkBonus":1440,"hpBonus":1440,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":49,"type":1,"timeLimit":null,"atkBonus":1460,"hpBonus":1460,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]},
{"id":46,"type":1,"timeLimit":null,"atkBonus":1400,"hpBonus":1400,"wave":[[10,1,1,50,1,1]]},
{"id":47,"type":1,"timeLimit":null,"atkBonus":1420,"hpBonus":1420,"wave":[[10,1,1,50,1,1]]},
{"id":48,"type":1,"timeLimit":null,"atkBonus":1440,"hpBonus":1440,"wave":[[10,1,1,50,1,1]]},
{"id":49,"type":1,"timeLimit":null,"atkBonus":1460,"hpBonus":1460,"wave":[[10,1,1,50,1,1]]},
{"id":50,"type":2,"timeLimit":60,"atkBonus":3500,"hpBonus":3500,"wave":[[10,1000,1]]}
]

View File

@ -0,0 +1,48 @@
-- 剑气
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Dependencies
local BehaviourClient = require(ReplicatedStorage.Base.BehaviourClient)
--> Variables
local PrefabFolder = ReplicatedStorage:WaitForChild("Prefabs")
-- local Prefab_Attack = PrefabFolder:WaitForChild("Projectiles"):WaitForChild("Attack")
--------------------------------------------------------------------------------
local Attack = {}
Attack.__index = Attack
setmetatable(Attack, {__index = BehaviourClient})
function Attack:Init(CasterPlayer: Player, CastInfo: table, DelayTime: number, CastState: boolean)
local self = BehaviourClient:Init(CasterPlayer, CastInfo, DelayTime, CastState)
setmetatable(self, Attack)
-- 加载动画
self:LoadAnimationByName("Attack")
return self
end
function Attack:Show(CasterPlayer: Player, CastInfo: table, DelayTime: number, CastState: boolean)
self.EffectDispatcher:ShowAnimation(self.Player, DelayTime, self:GetAnimationByName("Attack"))
end
function Attack:Destroy()
if self.ShowTask then
task.cancel(self.ShowTask)
self.ShowTask = nil
end
if self.Tween then
self.Tween:Cancel()
self.Tween = nil
end
if self.Projectile then
self.Projectile:Destroy()
self.Projectile = nil
end
BehaviourClient.Destroy(self)
end
return Attack

View File

@ -12,6 +12,16 @@ local RE_CleanPlayerPerformance = ReplicatedStorage:FindFirstChild("Events"):Fin
--> Dependencies
local TypeList = require(ServerStorage.Base.TypeList)
local Communicate = require(ServerStorage.Modules.Tools.Communicate)
local Utils = require(ReplicatedStorage.Tools.Utils)
--> Json
local JsonAttributes = require(ReplicatedStorage.Json.Attributes)
--> 临时维护一个属性数据表,用于记录属性类型
local AttributesNameData = {}
for _, Attribute in JsonAttributes do
AttributesNameData[Attribute.effectAttribute] = Utils:DeepCopyTable(Attribute)
end
--------------------------------------------------------------------------------
@ -54,7 +64,6 @@ end
-- 检查行为前先清理之前遗留的引用数据
function Behaviour:CheckClean()
if self.Mobs then
for _, Mob in self.Mobs do
self.Mobs[Mob] = nil
@ -69,6 +78,21 @@ function Behaviour:CheckClean()
end
end
function Behaviour:GetAttributeValue(AttributeName: string)
local AttributeValue = self.Character.Instance:FindFirstChild("Attributes"):GetAttribute(AttributeName)
if not AttributeValue then return nil end
-- 处理对应的值
local AttributeData = AttributesNameData[AttributeName]
if not AttributeData then return nil end
local typeValue = AttributeData.type
if typeValue == 1 then
return AttributeValue
elseif typeValue == 2 then
return AttributeValue / 100
end
end
-- 启动冷却时间清除计时
function Behaviour:StartCooldownTask()
self.Cooldown = self.OrgCooldown

View File

@ -0,0 +1,121 @@
-- 移动行为
--> Services
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Dependencies
local TypeList = require(ServerStorage.Base.TypeList)
local Behaviour = require(ServerStorage.Base.Behaviour)
local MobsProxy = require(ServerStorage.Proxy.MobsProxy)
local DamageProxy = require(ServerStorage.Proxy.DamageProxy)
local Utils = require(ReplicatedStorage.Tools.Utils)
--------------------------------------------------------------------------------
local Attack = {}
Attack.__index = Attack
setmetatable(Attack, {__index = Behaviour})
local CAST_DISTANCE = 8
local COOLDOWN = 1
local ATTRIBUTE_LIST = {
{Name = "attack", ElementType = DamageProxy.ElementType.NONE},
{Name = "fireAtk", ElementType = DamageProxy.ElementType.FIRE},
{Name = "iceAtk", ElementType = DamageProxy.ElementType.ICE},
{Name = "lightAtk", ElementType = DamageProxy.ElementType.LIGHT},
{Name = "shadowAtk", ElementType = DamageProxy.ElementType.SHADOW},
}
function Attack:Init(PlayerAI, Character: TypeList.Character, Player: Player)
local self = Behaviour:Init(PlayerAI, Character, script.Name)
self.Player = Player
self.Mobs = nil
setmetatable(self, Attack)
self.OrgCooldown = COOLDOWN
-- self:StartCooldownTask()
-- 客户端表现
self:SendPerformanceEvent("Init", self.Player, script.Name, true, {})
return self
end
function Attack:Check(CheckInfo: table)
if Behaviour.CheckStat(self) then return -1, self.CheckData end
self:CheckClean()
self.Mobs = MobsProxy:GetPlayerMobs(self.Player)
if not self.Mobs then return end
local closestMob, minDistance = nil, CAST_DISTANCE
for _, Mob in self.Mobs do
if Mob.Instance and Mob.Instance.PrimaryPart then
local dist = (Mob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
if dist < minDistance then
minDistance = dist
closestMob = Mob
end
end
end
self.CheckData = {}
if closestMob then
self.CheckData["ClosestCharacter"] = closestMob
return 50, self.CheckData
end
-- 返回优先级,执行数据
return -1, self.CheckData
end
function Attack:Execute()
self.ExeTask = task.spawn(function ()
self:ChangeExecutingState(true)
-- cd放前面之后发送事件才能正常记录cd
-- self:StartCooldownTask()
-- 停止移动
self.Character.Humanoid:MoveTo(self.Character.Root.Position)
-- 朝向目标
local HumanoidRootPart = self.Character.Root
local TargetPosition = self.CheckData["ClosestCharacter"].Instance.PrimaryPart.Position
if HumanoidRootPart then
HumanoidRootPart.CFrame = CFrame.lookAt(HumanoidRootPart.Position, TargetPosition)
end
local atkSpeed = self:GetAttributeValue("atkSpeed")
if not atkSpeed then warn("atkSpeed not found") return end
-- 表现部分
self:SendPerformanceEvent("Show", self.Player, self.ScriptName, true, {
{ UniqueId = self.PlayerAI:GetBehaviourUniqueId() }
})
-- 攻击前摇
task.wait(atkSpeed)
-- TODO: 之后这里可以提前做暴击判定
-- 伤害逻辑计算部分
local damageData = {}
for _, attribute in ATTRIBUTE_LIST do
local attributeValue = self:GetAttributeValue(attribute.Name)
if attributeValue then
table.insert(damageData, {
Damage = attributeValue,
Type = DamageProxy.DamageType.NORMAL,
Tag = DamageProxy.DamageTag.NORMAL,
ElementType = attribute.ElementType,
})
end
end
DamageProxy:TakeDamage(self.Character, self.CheckData["ClosestCharacter"], damageData)
-- task.wait(atkSpeed / 2)
self:ChangeExecutingState(false)
end)
end
return Attack

View File

@ -14,6 +14,8 @@ local Move = {}
Move.__index = Move
setmetatable(Move, {__index = Behaviour})
local ATTACK_DISTANCE = 8
function Move:Init(PlayerAI, Character: TypeList.Character, Player: Player)
local self = Behaviour:Init(PlayerAI, Character, script.Name)
self.Player = Player
@ -41,8 +43,11 @@ function Move:Check(CheckInfo: table)
self.CheckData = {}
if closestMob then
self.CheckData["ClosestCharacter"] = closestMob
return 10, self.CheckData
local distance = (closestMob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
if distance > ATTACK_DISTANCE then
self.CheckData["ClosestCharacter"] = closestMob
return 10, self.CheckData
end
end
-- 返回优先级,执行数据
@ -50,10 +55,22 @@ function Move:Check(CheckInfo: table)
end
function Move:Execute()
if self.ExeTask then task.cancel(self.ExeTask) end
self.ExeTask = task.spawn(function ()
self:ChangeExecutingState(true)
self.Character.Humanoid:MoveTo(self.CheckData["ClosestCharacter"].Instance:GetPivot().Position)
task.wait(0.5)
-- 新移动朝向
local TargetCharacter = self.CheckData["ClosestCharacter"].Instance
local TargetPosition = TargetCharacter:GetPivot().Position
local CharacterPosition = self.Character.Instance:GetPivot().Position
local Direction = (TargetPosition - CharacterPosition).Unit
local MoveToPosition = TargetPosition - (Direction * ATTACK_DISTANCE)
self.Character.Humanoid:MoveTo(MoveToPosition)
-- 旧内容
-- self.Character.Humanoid:MoveTo(self.CheckData["ClosestCharacter"].Instance:GetPivot().Position)
task.wait(0.2)
self:ChangeExecutingState(false)
end)
end

View File

@ -86,7 +86,6 @@ local function LoadData(Player: Player): (boolean, any)
if Success and Response then
print(("DataManager: User %s's data loaded into the game."):format(Player.Name))
print(Response)
else
print(("DataManager: User %s had no data to load from."):format(Player.Name))
end

View File

@ -67,9 +67,7 @@ task.defer(function()
local Enemy = Mob.Humanoid
-- Simulation
if Mob.Root.Anchored then
Mob.Root.Anchored = false
end
if Mob.Root.Anchored then Mob.Root.Anchored = false end
if not Mob.Root:GetNetworkOwner() then
Mob.Root:SetNetworkOwner(Player)
task.wait(0.05) -- Give physics more time so it doesn't appear as choppy
@ -78,9 +76,12 @@ task.defer(function()
-- Tracking
if Player and Enemy and PlayerRole then
if PlayerRole.Stats.Died then return end
if distance > 5 then
if distance > 8 then
Enemy:SetAttribute("AttackState", "Idle")
Enemy:MoveTo(Player.Character:GetPivot().Position, Player.Character.PrimaryPart)
else
Enemy:SetAttribute("AttackState", "Hit")
Mob.Root.Anchored = true
Mob.ExecutingState = true
-- 停止移动
Enemy:MoveTo(MobInstance:GetPivot().Position)
@ -92,7 +93,7 @@ task.defer(function()
if not Player then return end
if PlayerRole.Stats.Died then return end
if AI:GetModelDistance(MobInstance, Player.Character) <= 5 then
if AI:GetModelDistance(MobInstance, Player.Character) <= 8 then
-- 调用伤害模块
DamageProxy:TakeDamage(Mob, Mob.PlayerRole, {
{
@ -104,6 +105,7 @@ task.defer(function()
})
end
Mob.ExecutingState = false
Enemy:SetAttribute("AttackState", "Idle")
end
end
else

View File

@ -99,7 +99,10 @@ function MobsProxy:CreateMob(Player: Player, MobId: number, AtkBonus: number?, H
AI:StartTracking(Mob)
-- 关卡系数
if AtkBonus then Mob:ChangeAttributeValue("attack", math.floor(Mob.Config.attack * (AtkBonus / 1000))) end
if HpBonus then Mob:ChangeAttributeValue("hp", math.floor(Mob.Config.hp * (HpBonus / 1000))) end
if HpBonus then
Mob:ChangeAttributeValue("hp", math.floor(Mob.Config.hp * (HpBonus / 1000)))
Mob:ChangeAttributeValue("maxhp", math.floor(Mob.Config.maxhp * (HpBonus / 1000)))
end
MobsProxy.pData[Player.UserId][Mob.Instance] = Mob
return Mob
end

View File

@ -65,6 +65,7 @@ function PlayerRole.new(Player: Player, CharacterId: number)
return self
end
function PlayerRole:Died()
self:ChangeState("Died", true)
self.Humanoid.WalkSpeed = 0
@ -119,7 +120,6 @@ function PlayerFightProxy:UpdatePlayerFightData(Player: Player)
local AbilityProxy = require(ServerStorage.Proxy.AbilityProxy)
local GemProxy = require(ServerStorage.Proxy.GemProxy)
local AttributesData = {}
-- 计算角色基础属性值 + 装备属性值 + 宝石属性值,赋值属性
@ -178,8 +178,9 @@ function PlayerFightProxy:UpdatePlayerFightData(Player: Player)
for _, behaviourName in behaviourNameList do
playerAI:AddBehaviour(behaviourName)
end
-- playerAI:AddBehaviour("Move")
playerAI:AddBehaviour("SwordWave")
playerAI:AddBehaviour("Move")
-- playerAI:AddBehaviour("SwordWave")
playerAI:AddBehaviour("Attack")
-- 给前端发送技能信息

View File

@ -5,7 +5,7 @@ local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
local CAMERA_DISTANCE = 20
local CAMERA_DISTANCE = 30
local CAMERA_HEIGHT = 40
local CAMERA_ANGLE = math.rad(-45)
local SCREEN_OFFSET = 5

View File

@ -0,0 +1,159 @@
--[[
Evercyan @ March 2023
MobClient
Unlike MobLib which handles server-sided code, and most of it in general, we run
some code on the client for things such as custom health overlays & overwriting humanoid state types.
If you want to edit mob code to add new behavior or edit existing behavior, you likely want to refer
to the server-sided code under ServerStorage.
]]
--> Services
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
--> Player
local Player = Players.LocalPlayer
--> Dependencies
local Tween = require(ReplicatedStorage.Modules.Tween)
local Maid = require(ReplicatedStorage.Modules.Maid)
--> Variables
local Mobs = {}
-- Folder
local ClientMainPrefabs = Player.PlayerScripts.ClientMainPrefabs
--------------------------------------------------------------------------------
-- WaitForChild keeps yielding, even if the Instance is removed.
-- Using this for safe yielding with StreamingEnabled!
local function safeWait(Item: Instance, Name: string): Instance?
if not Item then
return
elseif Item:FindFirstChild(Name) then
return Item:FindFirstChild(Name)
end
local ItemAdded = Instance.new("BindableEvent")
local Maid = Maid.new()
Maid:Add(Item.ChildAdded:Connect(function(Child)
if Child.Name == Name then
ItemAdded:Fire(Child)
ItemAdded:Destroy()
Maid:Destroy()
end
end))
Maid:Add(Item.Destroying:Connect(function()
ItemAdded:Fire()
ItemAdded:Destroy()
Maid:Destroy()
end))
return ItemAdded.Event:Wait()
end
-- Creates an "AnimationTrack" instance which is stored on the client for playing
local function LoadAnimationTrack(MobInstance: Model, Name: string, Priority: string) : AnimationTrack?
local Humanoid = MobInstance:FindFirstChild("Humanoid") :: Humanoid
local Animator = Humanoid and Humanoid:FindFirstChild("Animator") :: Animator
if not Animator then return nil end
local Animation
Animation = ClientMainPrefabs.MobClient.DefaultAnimations[Name]
local AnimationTrack = Animator:LoadAnimation(Animation)
AnimationTrack.Priority = Enum.AnimationPriority[Priority or "Core"]
return AnimationTrack
end
local function PerMob(MobInstance: Model)
if Mobs[MobInstance] then return end
local Humanoid = safeWait(MobInstance, "Humanoid") :: Humanoid
local Root = safeWait(MobInstance, "HumanoidRootPart") :: BasePart
if not Humanoid or not Root then return end
local Maid = Maid.new()
-- 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]
Humanoid:SetStateEnabled(HumanoidStateType, false)
if Humanoid:GetState() == HumanoidStateType then
Humanoid:ChangeState(Enum.HumanoidStateType.Running)
end
end
-- Animations / Behavior ---------------------------------------------------
local AnimationTracks = {
Running = LoadAnimationTrack(MobInstance, "Running", "Core"),
Jumping = LoadAnimationTrack(MobInstance, "Jumping", "Movement"),
Hit = LoadAnimationTrack(MobInstance, "Hit", "Action")
}
Maid:Add(Humanoid.Running:Connect(function(Speed)
if Speed > 0.01 then
local Percent = Speed/Humanoid.WalkSpeed
if not AnimationTracks.Running.IsPlaying then
AnimationTracks.Running:Play()
end
AnimationTracks.Running:AdjustSpeed(Percent)
else
AnimationTracks.Running:Stop()
end
end))
Maid:Add(Humanoid.Jumping:Connect(function()
AnimationTracks.Jumping.TimePosition = 0
if not AnimationTracks.Jumping.IsPlaying then
AnimationTracks.Jumping:Play()
end
end))
-- Maid:Add(Humanoid.Died:Once(function()
-- Root:ApplyImpulse(-Root.CFrame.LookVector * Root.AssemblyMass*50) -- Ragdoll Impulse
-- end))
Maid:Add(Root:GetPropertyChangedSignal("Anchored"):Connect(function()
if Root.Anchored then
AnimationTracks.Running:Stop()
end
end))
-- TODO: 攻击动画之后要做延迟处理和攻速处理
Maid:Add(Humanoid:GetAttributeChangedSignal("AttackState"):Connect(function()
if Humanoid:GetAttribute("AttackState") == "Hit" then
if AnimationTracks.Hit.IsPlaying then
AnimationTracks.Hit.TimePosition = 0
else
AnimationTracks.Hit:Play()
end
else
AnimationTracks.Hit:Stop()
end
end))
local Mob = {}
Mob.Instance = MobInstance
Mob.AnimationTracks = AnimationTracks
Mobs[MobInstance] = Mob
Maid:Add(MobInstance.Destroying:Connect(function()
Mobs[MobInstance] = nil
Maid:Destroy()
end))
end
CollectionService:GetInstanceAddedSignal("Mob"):Connect(PerMob)
for _, MobInstance in CollectionService:GetTagged("Mob") do
task.spawn(PerMob, MobInstance)
end
return {}

View File

@ -35,8 +35,8 @@ end
function DamageBoard:CreateNormalDamageBoard(DamageDetail: table)
local DamageInitPos = DamageDetail.DamagePosition
local BiasPos = Vector3.new(0, 4, 0)
local MultPos = Vector3.new(0, 1.5, 0)
local BiasPos = Vector3.new(0, 6, 0)
local MultPos = Vector3.new(0, 1, 0)
-- 根据元素类型排序
local DamageInfos = DamageDetail["DamageInfos"]

View File

@ -56,7 +56,6 @@ RE_PerformanceEvent.OnClientEvent:Connect(function(ServerTime: number, CastTag:
local delayTime = ServerTime - tick()
if CastTag == "Init" then
-- print("Init", BehaviourName)
-- 暂时就新增一个表不调用初始化因为服务端不是多个new做的而是多次调用
if not PerformanceClient.pData[UserId][BehaviourName] then
PerformanceClient.pData[UserId][BehaviourName] = {}

View File

@ -2,30 +2,57 @@ local UserInputService = game:GetService("UserInputService")
local PlayerControl = {}
-- 监听按键按下
function PlayerControl.ListenMoveKeyDown(onKeyDown)
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
local key = input.KeyCode
if key == Enum.KeyCode.W or key == Enum.KeyCode.A or key == Enum.KeyCode.S or key == Enum.KeyCode.D then
onKeyDown(key)
end
end
end)
end
-- 添加按键状态跟踪
local pressedKeys = {}
-- 监听按键松开
function PlayerControl.ListenMoveKeyUp(onKeyUp)
UserInputService.InputEnded:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
local key = input.KeyCode
if key == Enum.KeyCode.W or key == Enum.KeyCode.A or key == Enum.KeyCode.S or key == Enum.KeyCode.D then
onKeyUp(key)
pressedKeys[key] = nil -- 移除当前按键状态
-- 检查是否还有其他移动按键在按下
local hasOtherKeysPressed = false
for k, v in pairs(pressedKeys) do
if k == Enum.KeyCode.W or k == Enum.KeyCode.A or k == Enum.KeyCode.S or k == Enum.KeyCode.D then
hasOtherKeysPressed = true
break
end
end
-- 只有当没有其他移动按键按下时才调用onKeyUp
if not hasOtherKeysPressed then
onKeyUp(key)
end
end
end
end)
end
-- 添加按键按下监听
function PlayerControl.ListenMoveKeyDown(onKeyDown)
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
local key = input.KeyCode
if key == Enum.KeyCode.W or key == Enum.KeyCode.A or key == Enum.KeyCode.S or key == Enum.KeyCode.D then
pressedKeys[key] = true
onKeyDown(key)
end
end
end)
end
-- 添加获取当前按下按键的函数
function PlayerControl.GetPressedKeys()
return pressedKeys
end
-- 添加检查特定按键是否按下的函数
function PlayerControl.IsKeyPressed(keyCode)
return pressedKeys[keyCode] == true
end
return PlayerControl

View File

@ -128,6 +128,14 @@ function UIManager:IsOpened(WindowName: string)
return UIManager.Instances[WindowName] ~= nil
end
function UIManager:SetData(WindowName: string, Data: table)
if not UIManager.Instances[WindowName] then
warn("UIManager:SetData() 窗口不存在:" .. WindowName)
return
end
UIManager.Instances[WindowName]:SetData(Data)
end
-- 获取当前窗口堆栈信息(用于调试)
function UIManager:GetWindowStackInfo()
local info = {}

View File

@ -62,14 +62,16 @@ function MainWindow:OnOpenWindow()
table.insert(self.Connections, chaCon)
table.insert(self.Connections, attributeUpgradeCon)
local playerDataFolder = Utils:WaitPlayerDataFolder(LocalPlayer)
local StatsFolder = playerDataFolder:WaitForChild("PlayerInfo"):WaitForChild("Stats")
local levelCon = StatsFolder.level.Changed:Connect(function(newValue)
-- TODO: 暂时用主关卡数显示我记得之前这里主要是记录的Challenge中才是正在挑战的内容
-- TODO: 之后LevelProxy也应该挪到ReplicatedStorage下之前可能是因为觉得Challenge是临时的内容所以放在workspace下但是逻辑做的不统一
local playerDataFolder = game.Workspace:WaitForChild("Level"):WaitForChild(LocalPlayer.UserId)
local StatsFolder = playerDataFolder:WaitForChild("Progress")
local levelCon = StatsFolder.Main.Changed:Connect(function(newValue)
self:SetShowLevel(newValue)
end)
-- 初始值设置
self:SetShowLevel(StatsFolder.level.Value)
self:SetShowLevel(StatsFolder.Main.Value)
table.insert(self.Connections, levelCon)
end