diff --git a/default.project.json b/default.project.json index a69113d..72839bd 100644 --- a/default.project.json +++ b/default.project.json @@ -32,6 +32,9 @@ "ReplicatedStorage": { "$className": "ReplicatedStorage", + "Base": { + "$path": "src/ReplicatedStorage/Base" + }, "Data": { "$path": "src/ReplicatedStorage/Data" }, diff --git a/excel/attribute.xlsx b/excel/attribute.xlsx new file mode 100644 index 0000000..b4f666e Binary files /dev/null and b/excel/attribute.xlsx differ diff --git a/excel/cha.xlsx b/excel/cha.xlsx index cb4048d..521ef7f 100644 Binary files a/excel/cha.xlsx and b/excel/cha.xlsx differ diff --git a/excel/gem.xlsx b/excel/gem.xlsx new file mode 100644 index 0000000..a203561 Binary files /dev/null and b/excel/gem.xlsx differ diff --git a/excel/item.xlsx b/excel/item.xlsx index fbbbaa4..59b092f 100644 Binary files a/excel/item.xlsx and b/excel/item.xlsx differ diff --git a/src/ReplicatedStorage/Base/BehaviourClient.luau b/src/ReplicatedStorage/Base/BehaviourClient.luau new file mode 100644 index 0000000..3fabf58 --- /dev/null +++ b/src/ReplicatedStorage/Base/BehaviourClient.luau @@ -0,0 +1,38 @@ +local BehaviourClient = {} +BehaviourClient.__index = BehaviourClient + +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +--> Dependencies +local EffectDispatcher = require(ReplicatedStorage.Modules.EffectDispatcher) + +-------------------------------------------------------------------------------- + +-- 刷新时,重新载入,暂时不考虑性能 + +-- 初始化内容 +function BehaviourClient:Init(CasterPlayer: Player, CastInfo: table, DelayTime: number, CastState: boolean) + local self = {} + self.Player = CasterPlayer + self.Character = CasterPlayer.Character + self.ShowTask = nil + self.EffectDispatcher = EffectDispatcher + + return self +end + +function BehaviourClient:Show(CasterPlayer: Player, CastInfo: table, DelayTime: number, CastState: boolean) + +end + +-- 销毁 +function BehaviourClient:Destroy() + if self.ShowTask then + task.cancel(self.ShowTask) + self.ShowTask = nil + end + self = nil +end + +return BehaviourClient \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Attributes.json b/src/ReplicatedStorage/Json/Attributes.json new file mode 100644 index 0000000..8867865 --- /dev/null +++ b/src/ReplicatedStorage/Json/Attributes.json @@ -0,0 +1,21 @@ +[ +{"id":1,"type":1,"effectAttribute":"attack","battleValue":[1,10]}, +{"id":2,"type":1,"effectAttribute":"hp","battleValue":[1,10]}, +{"id":3,"type":1,"effectAttribute":"swordAtk","battleValue":[1,10]}, +{"id":4,"type":2,"effectAttribute":"swordWearBase","battleValue":[1,10]}, +{"id":5,"type":2,"effectAttribute":"swordWearSpe","battleValue":[1,10]}, +{"id":6,"type":1,"effectAttribute":"fireAtk","battleValue":[1,10]}, +{"id":7,"type":1,"effectAttribute":"iceAtk","battleValue":[1,10]}, +{"id":8,"type":1,"effectAttribute":"lightAtk","battleValue":[1,10]}, +{"id":9,"type":1,"effectAttribute":"shadowAtk","battleValue":[1,10]}, +{"id":10,"type":1,"effectAttribute":"fireDef","battleValue":[1,10]}, +{"id":11,"type":1,"effectAttribute":"iceDef","battleValue":[1,10]}, +{"id":12,"type":1,"effectAttribute":"lightDef","battleValue":[1,10]}, +{"id":13,"type":1,"effectAttribute":"shadowDef","battleValue":[1,10]}, +{"id":50,"type":1,"effectAttribute":"wearNumber","battleValue":[1,10]}, +{"id":51,"type":1,"effectAttribute":"skillNumber","battleValue":[1,10]}, +{"id":52,"type":1,"effectAttribute":"extraAttributeNumber","battleValue":[1,10]}, +{"id":53,"type":1,"effectAttribute":"elementNumber","battleValue":[1,10]}, +{"id":54,"type":1,"effectAttribute":"elementDefNumber","battleValue":[1,10]}, +{"id":55,"type":1,"effectAttribute":"gemNumber","battleValue":[1,10]} +] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Gem.json b/src/ReplicatedStorage/Json/Gem.json new file mode 100644 index 0000000..e20b740 --- /dev/null +++ b/src/ReplicatedStorage/Json/Gem.json @@ -0,0 +1,58 @@ +[ +{"id":10000,"type":1,"quality":1,"iconId":1,"effectAttribute":"fireAtk","attributeValue":10}, +{"id":10001,"type":1,"quality":2,"iconId":2,"effectAttribute":"fireAtk","attributeValue":20}, +{"id":10002,"type":1,"quality":3,"iconId":3,"effectAttribute":"fireAtk","attributeValue":30}, +{"id":10003,"type":1,"quality":4,"iconId":4,"effectAttribute":"fireAtk","attributeValue":40}, +{"id":10004,"type":1,"quality":5,"iconId":5,"effectAttribute":"fireAtk","attributeValue":50}, +{"id":10005,"type":1,"quality":6,"iconId":6,"effectAttribute":"fireAtk","attributeValue":60}, +{"id":10006,"type":1,"quality":7,"iconId":7,"effectAttribute":"fireAtk","attributeValue":70}, +{"id":10100,"type":1,"quality":1,"iconId":1,"effectAttribute":"fireDef","attributeValue":10}, +{"id":10101,"type":1,"quality":2,"iconId":2,"effectAttribute":"fireDef","attributeValue":20}, +{"id":10102,"type":1,"quality":3,"iconId":3,"effectAttribute":"fireDef","attributeValue":30}, +{"id":10103,"type":1,"quality":4,"iconId":4,"effectAttribute":"fireDef","attributeValue":40}, +{"id":10104,"type":1,"quality":5,"iconId":5,"effectAttribute":"fireDef","attributeValue":50}, +{"id":10105,"type":1,"quality":6,"iconId":6,"effectAttribute":"fireDef","attributeValue":60}, +{"id":10106,"type":1,"quality":7,"iconId":7,"effectAttribute":"fireDef","attributeValue":70}, +{"id":11000,"type":2,"quality":1,"iconId":1,"effectAttribute":"iceAtk","attributeValue":10}, +{"id":11001,"type":2,"quality":2,"iconId":2,"effectAttribute":"iceAtk","attributeValue":20}, +{"id":11002,"type":2,"quality":3,"iconId":3,"effectAttribute":"iceAtk","attributeValue":30}, +{"id":11003,"type":2,"quality":4,"iconId":4,"effectAttribute":"iceAtk","attributeValue":40}, +{"id":11004,"type":2,"quality":5,"iconId":5,"effectAttribute":"iceAtk","attributeValue":50}, +{"id":11005,"type":2,"quality":6,"iconId":6,"effectAttribute":"iceAtk","attributeValue":60}, +{"id":11006,"type":2,"quality":7,"iconId":7,"effectAttribute":"iceAtk","attributeValue":70}, +{"id":11100,"type":2,"quality":1,"iconId":1,"effectAttribute":"iceDef","attributeValue":10}, +{"id":11101,"type":2,"quality":2,"iconId":2,"effectAttribute":"iceDef","attributeValue":20}, +{"id":11102,"type":2,"quality":3,"iconId":3,"effectAttribute":"iceDef","attributeValue":30}, +{"id":11103,"type":2,"quality":4,"iconId":4,"effectAttribute":"iceDef","attributeValue":40}, +{"id":11104,"type":2,"quality":5,"iconId":5,"effectAttribute":"iceDef","attributeValue":50}, +{"id":11105,"type":2,"quality":6,"iconId":6,"effectAttribute":"iceDef","attributeValue":60}, +{"id":11106,"type":2,"quality":7,"iconId":7,"effectAttribute":"iceDef","attributeValue":70}, +{"id":12000,"type":2,"quality":1,"iconId":1,"effectAttribute":"lightAtk","attributeValue":10}, +{"id":12001,"type":2,"quality":2,"iconId":2,"effectAttribute":"lightAtk","attributeValue":20}, +{"id":12002,"type":2,"quality":3,"iconId":3,"effectAttribute":"lightAtk","attributeValue":30}, +{"id":12003,"type":2,"quality":4,"iconId":4,"effectAttribute":"lightAtk","attributeValue":40}, +{"id":12004,"type":2,"quality":5,"iconId":5,"effectAttribute":"lightAtk","attributeValue":50}, +{"id":12005,"type":2,"quality":6,"iconId":6,"effectAttribute":"lightAtk","attributeValue":60}, +{"id":12006,"type":2,"quality":7,"iconId":7,"effectAttribute":"lightAtk","attributeValue":70}, +{"id":12100,"type":2,"quality":1,"iconId":1,"effectAttribute":"lightDef","attributeValue":10}, +{"id":12101,"type":2,"quality":2,"iconId":2,"effectAttribute":"lightDef","attributeValue":20}, +{"id":12102,"type":2,"quality":3,"iconId":3,"effectAttribute":"lightDef","attributeValue":30}, +{"id":12103,"type":2,"quality":4,"iconId":4,"effectAttribute":"lightDef","attributeValue":40}, +{"id":12104,"type":2,"quality":5,"iconId":5,"effectAttribute":"lightDef","attributeValue":50}, +{"id":12105,"type":2,"quality":6,"iconId":6,"effectAttribute":"lightDef","attributeValue":60}, +{"id":12106,"type":2,"quality":7,"iconId":7,"effectAttribute":"lightDef","attributeValue":70}, +{"id":13000,"type":2,"quality":1,"iconId":1,"effectAttribute":"shadowAtk","attributeValue":10}, +{"id":13001,"type":2,"quality":2,"iconId":2,"effectAttribute":"shadowAtk","attributeValue":20}, +{"id":13002,"type":2,"quality":3,"iconId":3,"effectAttribute":"shadowAtk","attributeValue":30}, +{"id":13003,"type":2,"quality":4,"iconId":4,"effectAttribute":"shadowAtk","attributeValue":40}, +{"id":13004,"type":2,"quality":5,"iconId":5,"effectAttribute":"shadowAtk","attributeValue":50}, +{"id":13005,"type":2,"quality":6,"iconId":6,"effectAttribute":"shadowAtk","attributeValue":60}, +{"id":13006,"type":2,"quality":7,"iconId":7,"effectAttribute":"shadowAtk","attributeValue":70}, +{"id":13100,"type":2,"quality":1,"iconId":1,"effectAttribute":"shadowDef","attributeValue":10}, +{"id":13101,"type":2,"quality":2,"iconId":2,"effectAttribute":"shadowDef","attributeValue":20}, +{"id":13102,"type":2,"quality":3,"iconId":3,"effectAttribute":"shadowDef","attributeValue":30}, +{"id":13103,"type":2,"quality":4,"iconId":4,"effectAttribute":"shadowDef","attributeValue":40}, +{"id":13104,"type":2,"quality":5,"iconId":5,"effectAttribute":"shadowDef","attributeValue":50}, +{"id":13105,"type":2,"quality":6,"iconId":6,"effectAttribute":"shadowDef","attributeValue":60}, +{"id":13106,"type":2,"quality":7,"iconId":7,"effectAttribute":"shadowDef","attributeValue":70} +] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/ItemProp.json b/src/ReplicatedStorage/Json/ItemProp.json index 67f4c3c..9d737c6 100644 --- a/src/ReplicatedStorage/Json/ItemProp.json +++ b/src/ReplicatedStorage/Json/ItemProp.json @@ -1,4 +1,61 @@ [ {"id":1,"type":1,"typeArgs":[],"quality":4,"iconId":1,"nameId":10001,"textId":20001,"buyPrice":[],"sellPrice":[],"use":[],"showPackage":null}, -{"id":2,"type":1,"typeArgs":[],"quality":4,"iconId":2,"nameId":10002,"textId":20002,"buyPrice":[],"sellPrice":[],"use":[],"showPackage":null} +{"id":2,"type":1,"typeArgs":[],"quality":4,"iconId":2,"nameId":10002,"textId":20002,"buyPrice":[],"sellPrice":[],"use":[],"showPackage":null}, +{"id":11,"type":1,"typeArgs":[],"quality":1,"iconId":11,"nameId":10011,"textId":20011,"buyPrice":[],"sellPrice":[],"use":[],"showPackage":null}, +{"id":10000,"type":4,"typeArgs":[],"quality":1,"iconId":12,"nameId":20000,"textId":30000,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":10001,"type":4,"typeArgs":[],"quality":2,"iconId":13,"nameId":20001,"textId":30001,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":10002,"type":4,"typeArgs":[],"quality":3,"iconId":14,"nameId":20002,"textId":30002,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":10003,"type":4,"typeArgs":[],"quality":4,"iconId":15,"nameId":20003,"textId":30003,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":10004,"type":4,"typeArgs":[],"quality":5,"iconId":16,"nameId":20004,"textId":30004,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":10005,"type":4,"typeArgs":[],"quality":6,"iconId":17,"nameId":20005,"textId":30005,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":10006,"type":4,"typeArgs":[],"quality":7,"iconId":18,"nameId":20006,"textId":30006,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null}, +{"id":10100,"type":4,"typeArgs":[],"quality":1,"iconId":19,"nameId":20100,"textId":30100,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":10101,"type":4,"typeArgs":[],"quality":2,"iconId":20,"nameId":20101,"textId":30101,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":10102,"type":4,"typeArgs":[],"quality":3,"iconId":21,"nameId":20102,"textId":30102,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":10103,"type":4,"typeArgs":[],"quality":4,"iconId":22,"nameId":20103,"textId":30103,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":10104,"type":4,"typeArgs":[],"quality":5,"iconId":23,"nameId":20104,"textId":30104,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":10105,"type":4,"typeArgs":[],"quality":6,"iconId":24,"nameId":20105,"textId":30105,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":10106,"type":4,"typeArgs":[],"quality":7,"iconId":25,"nameId":20106,"textId":30106,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null}, +{"id":11000,"type":4,"typeArgs":[],"quality":1,"iconId":26,"nameId":21000,"textId":31000,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":11001,"type":4,"typeArgs":[],"quality":2,"iconId":27,"nameId":21001,"textId":31001,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":11002,"type":4,"typeArgs":[],"quality":3,"iconId":28,"nameId":21002,"textId":31002,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":11003,"type":4,"typeArgs":[],"quality":4,"iconId":29,"nameId":21003,"textId":31003,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":11004,"type":4,"typeArgs":[],"quality":5,"iconId":30,"nameId":21004,"textId":31004,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":11005,"type":4,"typeArgs":[],"quality":6,"iconId":31,"nameId":21005,"textId":31005,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":11006,"type":4,"typeArgs":[],"quality":7,"iconId":32,"nameId":21006,"textId":31006,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null}, +{"id":11100,"type":4,"typeArgs":[],"quality":1,"iconId":33,"nameId":21100,"textId":31100,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":11101,"type":4,"typeArgs":[],"quality":2,"iconId":34,"nameId":21101,"textId":31101,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":11102,"type":4,"typeArgs":[],"quality":3,"iconId":35,"nameId":21102,"textId":31102,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":11103,"type":4,"typeArgs":[],"quality":4,"iconId":36,"nameId":21103,"textId":31103,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":11104,"type":4,"typeArgs":[],"quality":5,"iconId":37,"nameId":21104,"textId":31104,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":11105,"type":4,"typeArgs":[],"quality":6,"iconId":38,"nameId":21105,"textId":31105,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":11106,"type":4,"typeArgs":[],"quality":7,"iconId":39,"nameId":21106,"textId":31106,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null}, +{"id":12000,"type":4,"typeArgs":[],"quality":1,"iconId":40,"nameId":22000,"textId":32000,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":12001,"type":4,"typeArgs":[],"quality":2,"iconId":41,"nameId":22001,"textId":32001,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":12002,"type":4,"typeArgs":[],"quality":3,"iconId":42,"nameId":22002,"textId":32002,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":12003,"type":4,"typeArgs":[],"quality":4,"iconId":43,"nameId":22003,"textId":32003,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":12004,"type":4,"typeArgs":[],"quality":5,"iconId":44,"nameId":22004,"textId":32004,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":12005,"type":4,"typeArgs":[],"quality":6,"iconId":45,"nameId":22005,"textId":32005,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":12006,"type":4,"typeArgs":[],"quality":7,"iconId":46,"nameId":22006,"textId":32006,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null}, +{"id":12100,"type":4,"typeArgs":[],"quality":1,"iconId":47,"nameId":22100,"textId":32100,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":12101,"type":4,"typeArgs":[],"quality":2,"iconId":48,"nameId":22101,"textId":32101,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":12102,"type":4,"typeArgs":[],"quality":3,"iconId":49,"nameId":22102,"textId":32102,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":12103,"type":4,"typeArgs":[],"quality":4,"iconId":50,"nameId":22103,"textId":32103,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":12104,"type":4,"typeArgs":[],"quality":5,"iconId":51,"nameId":22104,"textId":32104,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":12105,"type":4,"typeArgs":[],"quality":6,"iconId":52,"nameId":22105,"textId":32105,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":12106,"type":4,"typeArgs":[],"quality":7,"iconId":53,"nameId":22106,"textId":32106,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null}, +{"id":13000,"type":4,"typeArgs":[],"quality":1,"iconId":54,"nameId":23000,"textId":33000,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":13001,"type":4,"typeArgs":[],"quality":2,"iconId":55,"nameId":23001,"textId":33001,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":13002,"type":4,"typeArgs":[],"quality":3,"iconId":56,"nameId":23002,"textId":33002,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":13003,"type":4,"typeArgs":[],"quality":4,"iconId":57,"nameId":23003,"textId":33003,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":13004,"type":4,"typeArgs":[],"quality":5,"iconId":58,"nameId":23004,"textId":33004,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":13005,"type":4,"typeArgs":[],"quality":6,"iconId":59,"nameId":23005,"textId":33005,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":13006,"type":4,"typeArgs":[],"quality":7,"iconId":60,"nameId":23006,"textId":33006,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null}, +{"id":13100,"type":4,"typeArgs":[],"quality":1,"iconId":61,"nameId":23100,"textId":33100,"buyPrice":[11,10],"sellPrice":[11,10],"use":[],"showPackage":null}, +{"id":13101,"type":4,"typeArgs":[],"quality":2,"iconId":62,"nameId":23101,"textId":33101,"buyPrice":[11,20],"sellPrice":[11,20],"use":[],"showPackage":null}, +{"id":13102,"type":4,"typeArgs":[],"quality":3,"iconId":63,"nameId":23102,"textId":33102,"buyPrice":[11,30],"sellPrice":[11,30],"use":[],"showPackage":null}, +{"id":13103,"type":4,"typeArgs":[],"quality":4,"iconId":64,"nameId":23103,"textId":33103,"buyPrice":[11,40],"sellPrice":[11,40],"use":[],"showPackage":null}, +{"id":13104,"type":4,"typeArgs":[],"quality":5,"iconId":65,"nameId":23104,"textId":33104,"buyPrice":[11,50],"sellPrice":[11,50],"use":[],"showPackage":null}, +{"id":13105,"type":4,"typeArgs":[],"quality":6,"iconId":66,"nameId":23105,"textId":33105,"buyPrice":[11,60],"sellPrice":[11,60],"use":[],"showPackage":null}, +{"id":13106,"type":4,"typeArgs":[],"quality":7,"iconId":67,"nameId":23106,"textId":33106,"buyPrice":[11,70],"sellPrice":[11,70],"use":[],"showPackage":null} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Modules/BehavioursClient/SwordWave.luau b/src/ReplicatedStorage/Modules/BehavioursClient/SwordWave.luau new file mode 100644 index 0000000..d10be85 --- /dev/null +++ b/src/ReplicatedStorage/Modules/BehavioursClient/SwordWave.luau @@ -0,0 +1,47 @@ +-- 剑气 + +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +--> Dependencies +local BehaviourClient = require(ReplicatedStorage.Base.BehaviourClient) + +--> Variables +local PrefabFolder = ReplicatedStorage:WaitForChild("Prefabs") +local Prefab_SwordWave = PrefabFolder:WaitForChild("Projectiles"):WaitForChild("SwordWave") + +-------------------------------------------------------------------------------- + +local SwordWave = {} +SwordWave.__index = SwordWave +setmetatable(SwordWave, {__index = BehaviourClient}) + +function SwordWave:Init(CasterPlayer: Player, CastInfo: table, DelayTime: number, CastState: boolean) + local self = BehaviourClient:Init(CasterPlayer, CastInfo, DelayTime, CastState) + setmetatable(self, SwordWave) + + return self +end + +function SwordWave:Show(CasterPlayer: Player, CastInfo: table, DelayTime: number, CastState: boolean) + self.ShowTask, self.Projectile, self.Tween = self.EffectDispatcher:ShowProjectile(self.Player, DelayTime, + 0, CastInfo.StartPos, CastInfo.EndPos, CastInfo.Duration, Prefab_SwordWave, Enum.EasingStyle.Linear) +end + +function SwordWave: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 SwordWave \ No newline at end of file diff --git a/src/ReplicatedStorage/Modules/EffectDispatcher.luau b/src/ReplicatedStorage/Modules/EffectDispatcher.luau new file mode 100644 index 0000000..8f9fcfd --- /dev/null +++ b/src/ReplicatedStorage/Modules/EffectDispatcher.luau @@ -0,0 +1,46 @@ +-- 效果调度器 + +-- 客户端调用表现全部通过这里,预想效果 +-- 1. 方便做功能拓展 +-- 2. 便于统一表现逻辑(如:AOE,弹道。。。) + +local EffectDispatcher = {} + +--> Services +local TweenService = game:GetService("TweenService") + +local function GetPerformanceFolder() + return game.Workspace:FindFirstChild("Performance") +end + +function EffectDispatcher:ShowProjectile(Caster: Player, DelayTime: number, PreTime: number, StartPos: Vector3, EndPos: Vector3, + Duration: number, ProjectilePrefab: Part, EasingStyle: Enum.EasingStyle?) + local projectileTask, Projectile, tween + projectileTask = task.spawn(function () + local PerformanceFolder = GetPerformanceFolder() + if not PerformanceFolder then warn("PerformanceFolder not found") return end + + Projectile = ProjectilePrefab:Clone() + Projectile.Parent = PerformanceFolder + Projectile.CFrame = CFrame.new(StartPos, EndPos) + Projectile.CanCollide = false + Projectile.Anchored = true + + + -- Tween动画 + local tweenInfo = TweenInfo.new(Duration, EasingStyle or Enum.EasingStyle.Linear) + local direction = (EndPos - StartPos).Unit + local lookAt = EndPos + direction + local goal = {CFrame = CFrame.new(EndPos, lookAt)} + tween = TweenService:Create(Projectile, tweenInfo, goal) + + task.wait(PreTime) + tween:Play() + + -- 动画结束后自动销毁 + game.Debris:AddItem(Projectile, Duration + PreTime) + end) + return projectileTask, Projectile, tween +end + +return EffectDispatcher \ No newline at end of file diff --git a/src/ReplicatedStorage/Tools/Utils.luau b/src/ReplicatedStorage/Tools/Utils.luau index 9a0ebc7..0024885 100644 --- a/src/ReplicatedStorage/Tools/Utils.luau +++ b/src/ReplicatedStorage/Tools/Utils.luau @@ -38,6 +38,16 @@ function Utils:GenUniqueId(t: table) return min_id end +function Utils:GenUniqueIdPlayerAI(LastTime: number, Counter: number) + local now = os.time() -- 或 tick(),精度更高 + if now ~= LastTime then + Counter = 0 + LastTime = now + end + Counter = Counter + 1 + return tostring(now) .. "_" .. tostring(Counter) +end + -- function Utils:GetJsonIdData(JsonName: string, id: number) -- local JsonData = require(ReplicatedStorage.Json[JsonName]) -- for _, item in ipairs(JsonData) do @@ -86,6 +96,35 @@ function Utils:StringArrayToTable(StringArray: string) return result end +function Utils:GetFlatDirectionAndEndPos(startPos: Vector3, targetPos: Vector3, length: number) + local flatStartPos = Vector3.new(startPos.X, 0, startPos.Z) + local flatTarget = Vector3.new(targetPos.X, 0, targetPos.Z) + local direction = (flatTarget - flatStartPos).Unit + local endPos = flatStartPos + direction * length + return flatStartPos, endPos, direction +end + +function Utils:CreateDataInstance(Player: Player, UniqueId: number, EquipmentData: table, Folder: Instance) + if Player or UniqueId or EquipmentData or Folder then + warn('创建实例失败: ' , Player.Name, UniqueId, EquipmentData, Folder) + return + end + local Config = Instance.new("Configuration") + Config.Name = UniqueId + Utils:SetAttributesList(Config, EquipmentData) + Config.Parent = Folder + return Config +end + +-- 复制表 +function Utils:CopyTable(t: table) + local newTable = {} + for k, v in pairs(t) do + newTable[k] = v + end + return newTable +end + -------------------------------------------------------------------------------- return Utils \ No newline at end of file diff --git a/src/ServerStorage/Base/Behaviour.luau b/src/ServerStorage/Base/Behaviour.luau index f61b055..aec57cc 100644 --- a/src/ServerStorage/Base/Behaviour.luau +++ b/src/ServerStorage/Base/Behaviour.luau @@ -1,20 +1,34 @@ local Behaviour = {} Behaviour.__index = Behaviour +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Events +local RE_PerformanceEvent = ReplicatedStorage:FindFirstChild("Events"):FindFirstChild("RE_PerformanceEvent") +local RE_CleanPlayerPerformance = ReplicatedStorage:FindFirstChild("Events"):FindFirstChild("RE_CleanPlayerPerformance") + --> Dependencies -local TypeList = require(script.Parent.TypeList) +local TypeList = require(ServerStorage.Base.TypeList) +local Communicate = require(ServerStorage.Modules.Tools.Communicate) -------------------------------------------------------------------------------- -- 刷新时,重新载入,暂时不考虑性能 -- 初始化内容 -function Behaviour:Init(PlayerAI, Character: TypeList.Character) +function Behaviour:Init(PlayerAI, Character: TypeList.Character, ScriptName: string) local self = {} self.PlayerAI = PlayerAI self.Character = Character self.CheckData = nil self.ExeTask = nil + self.UniqueIdList = {} + self.ScriptName = ScriptName + self.Cooldown = 0 + self.OrgCooldown = 0 + self.CooldownTask = nil local Humanoid = self.Character.Humanoid -- 监听属性变化 @@ -38,12 +52,28 @@ function Behaviour:Execute() end +-- 启动冷却时间清除计时 +function Behaviour:StartCooldownTask() + self.Cooldown = self.OrgCooldown + self.CooldownTask = task.spawn(function() + task.wait(self.OrgCooldown) + self.Cooldown = 0 + end) +end + +function Behaviour:SendPerformanceEvent(...) + Communicate:SendToClient(RE_PerformanceEvent, ...) +end + -- 检查当前状态是否可执行 function Behaviour:CheckStat() if not self.Character then return true end -- 死亡检查 if self.Character:GetState("Died") then return true end + -- 冷却中检查 + if self.Cooldown > 0 then return true end + -- 执行状态中检查 local ExecutingState = self.PlayerAI.ExecutingState -- 其他内容执行中,就false @@ -59,6 +89,20 @@ end -- 销毁 function Behaviour:Destroy() + -- 清除客户端对应行为表现 + for _, UniqueId in self.UniqueIdList do + self:SendPerformanceEvent("Destroy", self.Player, self.ScriptName, true, {UniqueId = UniqueId}) + end + if self.CooldownTask then + task.cancel(self.CooldownTask) + self.CooldownTask = nil + end + if self.ExeTask then + task.cancel(self.ExeTask) + self.ExeTask = nil + end + -- 清除数据 + self.UniqueIdList = {} if self.ConAttribtueChanged then self.ConAttribtueChanged:Disconnect() self.ConAttribtueChanged = nil @@ -67,6 +111,10 @@ function Behaviour:Destroy() task.cancel(self.LoopTask) self.LoopTask = nil end + for _, UniqueId in self.UniqueIdList do + self.PlayerAI:RemoveBehaviourUniqueId(UniqueId) + end + self.UniqueIdList = {} self = nil end diff --git a/src/ServerStorage/Base/Character.luau b/src/ServerStorage/Base/Character.luau index 8a10815..57cc4d2 100644 --- a/src/ServerStorage/Base/Character.luau +++ b/src/ServerStorage/Base/Character.luau @@ -2,8 +2,12 @@ local Character = {} Character.__index = Character +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Utils = require(ReplicatedStorage.Tools.Utils) local TypeList = require(script.Parent.TypeList) + local LIMIT_ATTRIBUTE = { "hp" } @@ -16,7 +20,7 @@ function Character.new(Player: Player, CharacterModel: Model, CharacterData: tab -- 生成表格数据 local self = {} self.Instance = newMobModel - self.Config = CharacterData + self.Config = Utils:CopyTable(CharacterData) self.Root = HumanoidRootPart self.Humanoid = mobHumanoid self.Origin = HumanoidRootPart:GetPivot() @@ -90,9 +94,8 @@ function Character:ChangeAttributeValue(attributeKey: string, value: any) -- 死亡判断 if attributeKey == "hp" and self.Stats.Died == false then - if self.Config[attributeKey] <= 0 then - self:Died() - end + + if self.Config[attributeKey] <= 0 then self:Died() end end end @@ -106,6 +109,7 @@ function Character:ChangeState(state: string, value: any) end function Character:Died() + print(debug.traceback("Stack trace:")) self:ChangeState("Died", true) for _, connection in self.Connections do connection:Disconnect() diff --git a/src/ServerStorage/Base/TypeList.luau b/src/ServerStorage/Base/TypeList.luau index 0f7a4ff..bce9ba2 100644 --- a/src/ServerStorage/Base/TypeList.luau +++ b/src/ServerStorage/Base/TypeList.luau @@ -20,4 +20,10 @@ export type Behaviour = { Destroy: () -> (), } +export type CastInfo = { + UniqueId: number, + StartPos: Vector3, + EndPos: Vector3, +} + return {} \ No newline at end of file diff --git a/src/ServerStorage/Modules/Behaviours/Move.luau b/src/ServerStorage/Modules/Behaviours/Move.luau index e30f196..44578e7 100644 --- a/src/ServerStorage/Modules/Behaviours/Move.luau +++ b/src/ServerStorage/Modules/Behaviours/Move.luau @@ -4,9 +4,9 @@ local ServerStorage = game:GetService("ServerStorage") --> Dependencies -local TypeList = require(ServerStorage.Base.TypeList) -local Behaviour = require(ServerStorage.Base.Behaviour) -local MobsProxy = require(ServerStorage.Proxy.MobsProxy) +local TypeList = require(ServerStorage:WaitForChild("Base").TypeList) +local Behaviour = require(ServerStorage:WaitForChild("Base").Behaviour) +local MobsProxy = require(ServerStorage:WaitForChild("Proxy").MobsProxy) -------------------------------------------------------------------------------- @@ -15,7 +15,7 @@ Move.__index = Move setmetatable(Move, {__index = Behaviour}) function Move:Init(PlayerAI, Character: TypeList.Character, Player: Player) - local self = Behaviour:Init(PlayerAI, Character) + local self = Behaviour:Init(PlayerAI, Character, script.Name) self.Player = Player setmetatable(self, Move) return self @@ -57,12 +57,4 @@ function Move:Execute() end) end -function Move:Destroy() - if self.ExeTask then - task.cancel(self.ExeTask) - self.ExeTask = nil - end - Behaviour.Destroy(self) -end - return Move \ No newline at end of file diff --git a/src/ServerStorage/Modules/Behaviours/SwordWave.luau b/src/ServerStorage/Modules/Behaviours/SwordWave.luau new file mode 100644 index 0000000..2ded39c --- /dev/null +++ b/src/ServerStorage/Modules/Behaviours/SwordWave.luau @@ -0,0 +1,102 @@ +-- 移动行为 + +--> 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 SwordWave = {} +SwordWave.__index = SwordWave +setmetatable(SwordWave, {__index = Behaviour}) + +local CAST_DISTANCE = 50 +local PROJECTILE_LENGTH = 50 +local PROJECTILE_DURATION = 3 +local COOLDOWN = 1 + +function SwordWave:Init(PlayerAI, Character: TypeList.Character, Player: Player) + local self = Behaviour:Init(PlayerAI, Character, script.Name) + self.Player = Player + setmetatable(self, SwordWave) + self.OrgCooldown = COOLDOWN + + -- 客户端表现 + self:SendPerformanceEvent("Init", self.Player, script.Name, true, {}) + return self +end + +function SwordWave:Check(CheckInfo: table) + if Behaviour.CheckStat(self) then return -1, self.CheckData end + + local PlayerMobs = MobsProxy:GetPlayerMobs(self.Player) + if not PlayerMobs then return end + + local closestMob, minDistance = nil, CAST_DISTANCE + for _, Mob in PlayerMobs 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 200, self.CheckData + end + + -- 返回优先级,执行数据 + return -1, self.CheckData +end + +function SwordWave:Execute() + self.ExeTask = task.spawn(function () + self:ChangeExecutingState(true) + task.wait(0.5) + -- 伤害逻辑部分 + local StartPos = self.Character.Instance:GetPivot().Position + local EndPos = StartPos + (self.CheckData["ClosestCharacter"].Instance:GetPivot().Position - StartPos).Unit * PROJECTILE_LENGTH + + StartPos, EndPos = Utils:GetFlatDirectionAndEndPos(StartPos, self.CheckData["ClosestCharacter"].Instance:GetPivot().Position, PROJECTILE_LENGTH) + DamageProxy:CastFreeProjectile(self.Character, StartPos, EndPos, PROJECTILE_DURATION, 4, + function (Victim: TypeList.Character) + self:OnHit(Victim) + end) + + -- 表现部分 + self:SendPerformanceEvent("Show", self.Player, self.ScriptName, true, { + { UniqueId = self.PlayerAI:GetBehaviourUniqueId(), + StartPos = StartPos, + EndPos = EndPos, + Duration = PROJECTILE_DURATION, + Cooldown = self.OrgCooldown, + } + }) + self:StartCooldownTask() + self:ChangeExecutingState(false) + end) +end + +function SwordWave:OnHit(Victim: TypeList.Character) + DamageProxy:TakeDamage(self.Character, Victim, { + { + Damage = 30, + Type = DamageProxy.DamageType.SKILL, + Tag = DamageProxy.DamageTag.NORMAL, + } + }) + return false +end + +return SwordWave \ No newline at end of file diff --git a/src/ServerStorage/Modules/Tools/Communicate.luau b/src/ServerStorage/Modules/Tools/Communicate.luau new file mode 100644 index 0000000..961b41f --- /dev/null +++ b/src/ServerStorage/Modules/Tools/Communicate.luau @@ -0,0 +1,11 @@ +local Communicate = {} + +function Communicate:SendToClient(Event: RemoteEvent, CastTag: string, CastPlayer: Player, ...) + Event:FireAllClients(tick(), CastTag, CastPlayer, ...) +end + +function Communicate:SendToClientFree(Event: RemoteEvent, ...) + Event:FireAllClients(...) +end + +return Communicate \ No newline at end of file diff --git a/src/ServerStorage/Proxy/DamageProxy.luau b/src/ServerStorage/Proxy/DamageProxy.luau index 98e0c6c..2de1258 100644 --- a/src/ServerStorage/Proxy/DamageProxy.luau +++ b/src/ServerStorage/Proxy/DamageProxy.luau @@ -4,6 +4,7 @@ local DamageProxy = {} --> Services local ServerStorage = game:GetService("ServerStorage") +--> Modules --> Variables local TypeList = require(ServerStorage.Base.TypeList) @@ -48,6 +49,65 @@ function DamageProxy:IsDied(Target: Model) return Humanoid:GetAttribute("hp") <= 0 end +-- 获取范围内敌人 +function DamageProxy:GetAoeEnemies(Caster: TypeList.Character, Position: Vector3, Radius: number) + local Enemies = {} + local MobsProxy = require(ServerStorage.Proxy.MobsProxy) + for _, enemy in pairs(MobsProxy:GetPlayerMobs(Caster.Player)) do + if enemy ~= Caster and enemy.Instance then + local enemy_pos = enemy.Instance:GetPivot().Position + if (enemy_pos - Position).Magnitude <= Radius then + table.insert(Enemies, enemy) + end + end + end + return Enemies +end + +-- 弹道伤害 +function DamageProxy:CastFreeProjectile(Caster: TypeList.Character, StartPos: Vector3, EndPos: Vector3, Duration: number, Range: number, OnHit: (Target: TypeList.Character) -> (boolean?)) + local projectileTask = nil + local step_time = 0.05 -- 每帧检测间隔 + local elapsed = 0 + local hit_targets = {} + local direction = (EndPos - StartPos).Unit + local distance = (EndPos - StartPos).Magnitude + local speed = distance / Duration + local current_pos = StartPos + local cancelled = false + + local function cancelTask() + cancelled = true + if projectileTask then + task.cancel(projectileTask) + end + end + + projectileTask = task.spawn(function() + while elapsed < Duration and not cancelled do + -- 计算当前位置 + current_pos = StartPos + direction * speed * elapsed + -- 检测范围内敌人 + local MobsProxy = require(ServerStorage.Proxy.MobsProxy) + for _, enemy in pairs(MobsProxy:GetPlayerMobs(Caster.Player)) do + if enemy ~= Caster and enemy.Instance then + local enemy_pos = enemy.Instance:GetPivot().Position + if (enemy_pos - current_pos).Magnitude <= Range then + if not hit_targets[enemy] then + hit_targets[enemy] = true + local stop = OnHit(enemy) + if stop then cancelTask() break end + end + end + end + end + task.wait(step_time) + elapsed = elapsed + step_time + end + end) +end + + function DamageProxy:TakeDamage(Caster: TypeList.Character, Victim: TypeList.Character, DamageInfos: {DamageInfo}) for _, DamageInfo in DamageInfos do local Damage = DamageInfo.Damage diff --git a/src/ServerStorage/Proxy/EquipmentProxy.luau b/src/ServerStorage/Proxy/EquipmentProxy.luau index 42a3b62..adbe6f1 100644 --- a/src/ServerStorage/Proxy/EquipmentProxy.luau +++ b/src/ServerStorage/Proxy/EquipmentProxy.luau @@ -26,22 +26,6 @@ local function GetPlayerEquipmentFolder(Player: Player) return EquipmentFolder end --- 创建装备实例 -local function CreateEquipmentInstance(Player: Player, UniqueId: number, EquipmentData: table) - if Player or UniqueId or EquipmentData then - warn('创建装备实例失败: ' , Player.Name, UniqueId, EquipmentData) - return - end - local PlayerEquipmentFolder = GetPlayerEquipmentFolder(Player) - if not PlayerEquipmentFolder then return end - - local Config = Instance.new("Configuration") - Config.Name = UniqueId - Utils:SetAttributesList(Config, PlayerEquipmentFolder) - Config.Parent = PlayerEquipmentFolder - return Config -end - -------------------------------------------------------------------------------- -- 初始化玩家 @@ -57,10 +41,11 @@ function EquipmentProxy:InitPlayer(Player: Player) -- 初始化装备 for uniqueId, EquipmentData in ArchiveProxy.pData[Player.UserId][STORE_NAME] do - CreateEquipmentInstance(Player, uniqueId, EquipmentData) + Utils:CreateDataInstance(Player, uniqueId, EquipmentData, GetPlayerEquipmentFolder(Player)) end end +-- 一些特殊记录或者不用记录的Key local EXCEPT_KEYS = { "id", "orgId", "name", "attributes"} -- 添加装备到背包 function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number) @@ -81,15 +66,16 @@ function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number) ResultData.id = UniqueId ResultData.orgId = EquipmentId - ResultData.wearing = false + -- 到时候记录穿戴槽位 + ResultData.wearing = 0 - -- 其他随机词条内容添加在下面 - -- 之后回收修改随机生成 + -- TODO: 其他随机词条内容添加在下面 + -- TODO: 之后回收修改随机生成 ------------------------------------------------------------ ArchiveProxy.pData[Player.UserId][UniqueId] = ResultData - CreateEquipmentInstance(Player, UniqueId, ResultData) + Utils:CreateDataInstance(Player, UniqueId, ResultData, GetPlayerEquipmentFolder(Player)) end -- 回收装备 @@ -119,6 +105,12 @@ function EquipmentProxy:WearEquipment(Player: Player, EquipmentId: number) end +-- 获取装备数据 +function EquipmentProxy:GetEquipmentData(Player: Player, EquipmentUniqueId: number) + local EquipmentData = ArchiveProxy.pData[Player.UserId][STORE_NAME][EquipmentUniqueId] + return EquipmentData +end + function EquipmentProxy:OnPlayerRemoving(Player: Player) end diff --git a/src/ServerStorage/Proxy/GemProxy.luau b/src/ServerStorage/Proxy/GemProxy.luau new file mode 100644 index 0000000..062f071 --- /dev/null +++ b/src/ServerStorage/Proxy/GemProxy.luau @@ -0,0 +1,204 @@ +-- 玩家通用信息 +local GemProxy = {} + +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Variables +local Utils = require(ReplicatedStorage.Tools.Utils) +local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy) + +--> Json +local JsonItem = require(ReplicatedStorage.Json.ItemProp) +local JsonGem = require(ReplicatedStorage.Json.Gem) + +--> Events +local RE_PlayerTip = ReplicatedStorage.Events.RE_PlayerTip +local RE_UpgradeAttributes = ReplicatedStorage.Events.RE_UpgradeAttributes + +--> Constants +local STORE_NAME = "Gem" + +-------------------------------------------------------------------------------- + +-- 获取玩家信息文件夹 +local function GetPlayerGemFolder(Player: Player) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + local GemFolder = pData:FindFirstChild("Gem") + return GemFolder +end + +-------------------------------------------------------------------------------- + +-- 初始化玩家 +function GemProxy:InitPlayer(Player: Player) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + local GemFolder = Utils:CreateFolder("Gem", pData) + + -- 新玩家数据初始化 + if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then + ArchiveProxy.pData[Player.UserId][STORE_NAME] = {} + ArchiveProxy.pData[Player.UserId][STORE_NAME].Gems = {} + end + + -- 创建玩家信息实例 + for GemUniqueId, GemData in ArchiveProxy.pData[Player.UserId][STORE_NAME].Gems do + Utils:CreateDataInstance(Player, GemUniqueId, GemData, GemFolder) + end +end + +-------------------------------------------------------------------------------- + +-- 添加宝石 +local EXCEPT_KEYS = {"id", "orgId", "iconId"} +function GemProxy:AddGem(Player: Player, GemId: number) + + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + + local GemData = Utils:GetIdDataFromJson(JsonGem, GemId) + if not GemData then return end + + local UniqueId = Utils:GenUniqueId(ArchiveProxy.pData[Player.UserId]) + -- 配置表内容 + local ResultData = {} + for key, value in pairs(GemData) do + if not table.find(EXCEPT_KEYS, key) then + ResultData[key] = value + end + end + + ResultData.id = UniqueId + ResultData.orgId = GemId + -- 记录穿戴的装备UniqueId + ResultData.wearing = 0 + + + ArchiveProxy.pData[Player.UserId][UniqueId] = ResultData + Utils:CreateDataInstance(Player, UniqueId, ResultData, GetPlayerGemFolder(Player)) +end + +-- 购买宝石 +function GemProxy:BuyGem(Player: Player, GemId: number) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + + local ItemData = Utils:GetIdDataFromJson(JsonItem, GemId) + if not ItemData then warn('无法获取宝石Item数据: ' , Player.Name, GemId) return end + + local GemData = Utils:GetIdDataFromJson(JsonGem, GemId) + if not GemData then warn('无法获取宝石配置数据: ' , Player.Name, GemId) return end + + local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy) + + -- 判断是否花钱 + local buyPrice = ItemData.buyPrice + if buyPrice then + if PlayerInfoProxy:HasEnoughItem(Player, buyPrice[1], buyPrice[2]) then + PlayerInfoProxy:ChangeItemCount(Player, buyPrice[1], -buyPrice[2]) + else + RE_PlayerTip:FireClient(Player, "钱不够") + return + end + end + + -- 添加宝石 + GemProxy:AddGem(Player, GemId) +end + +-- 回收宝石 +function GemProxy:RecycleGem(Player: Player, UniqueId: number) + local GemFolder = GetPlayerGemFolder(Player) + + local GemData = ArchiveProxy.pData[Player.UserId][UniqueId] + if not GemData then warn('无法获取宝石数据: ' , Player.Name, UniqueId) return end + + local GemInstance = GemFolder:FindFirstChild(UniqueId) + if not GemInstance then warn('宝石实例不存在: ' , Player.Name, UniqueId) return end + + local ItemData = Utils:GetIdDataFromJson(JsonItem, GemData.orgId) + if not ItemData then warn('无法获取宝石Item数据: ' , Player.Name, GemData.orgId) return end + + -- 判断是否穿戴 + if GemData.wearing ~= 0 then + RE_PlayerTip:FireClient(Player, "宝石穿戴中") + return + end + + -- 增加货币 + local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy) + PlayerInfoProxy:ChangeItemCount(Player, ItemData.sellPrice[1], ItemData.sellPrice[2]) + + -- 移除内容 + ArchiveProxy.pData[Player.UserId][UniqueId] = nil + GemInstance:Destroy() +end + +-- 穿戴宝石 +function GemProxy:WearGem(Player: Player, GemUniqueId: number, EquipmentUniqueId: number) + -- 检查是否有这个宝石 + local GemData = ArchiveProxy.pData[Player.UserId][GemUniqueId] + if not GemData then warn('无法获取宝石数据: ' , Player.Name, GemUniqueId) return end + + -- 检查是否有宝石实例 + local GemInstance = GetPlayerGemFolder(Player):FindFirstChild(GemUniqueId) + if not GemInstance then warn('宝石实例不存在: ' , Player.Name, GemUniqueId) return end + + -- 检查是否有这个装备 + local EquipmentProxy = require(ServerStorage.Proxy.EquipmentProxy) + local EquipmentData = EquipmentProxy:GetEquipmentData(Player, EquipmentUniqueId) + if not EquipmentData then warn('无法获取装备数据: ' , Player.Name, EquipmentUniqueId) return end + + -- 检查是否正在穿戴中 + if GemData.wearing ~= 0 then + RE_PlayerTip:FireClient(Player, "宝石穿戴中, 请先卸下") + return + end + + -- TODO: 检查对应装备是否有充足的宝石槽位 + + -- 穿戴 + GemData.wearing = EquipmentUniqueId + GemInstance:SetAttribute("Wearing", EquipmentUniqueId) +end + +-------------------------------------------------------------------------------- + +-- 获取升级加点属性 +function GemProxy:GetPlayerGemWearingAttributes(Player: Player) + if not Player then warn('获取玩家属性失败: ', Player.Name) return end + local playerGems = ArchiveProxy.pData[Player.UserId][STORE_NAME].Gems + local attributes = {} + for _, gemData in playerGems do + if gemData.wearing ~= 0 then + if attributes[gemData.effectAttribute] then + attributes[gemData.effectAttribute] = attributes[gemData.effectAttribute] + gemData.attributeValue + else + attributes[gemData.effectAttribute] = gemData.attributeValue + end + end + end + return attributes +end + +-- 获取玩家属性 +function GemProxy:GetPlayerAttributes(Player: Player) + local attributesList = {} + attributesList.GemWearingAttributes = self:GetPlayerGemWearingAttributes(Player) + return attributesList +end + +-------------------------------------------------------------------------------- + +function GemProxy:OnPlayerRemoving(Player: Player) + +end + +ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(Player: Player) + GemProxy:OnPlayerRemoving(Player) +end) + +return GemProxy \ No newline at end of file diff --git a/src/ServerStorage/Proxy/LevelProxy.luau b/src/ServerStorage/Proxy/LevelProxy.luau index c0e96e0..51aca8f 100644 --- a/src/ServerStorage/Proxy/LevelProxy.luau +++ b/src/ServerStorage/Proxy/LevelProxy.luau @@ -12,11 +12,16 @@ local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy) local MobsProxy = require(ServerStorage.Proxy.MobsProxy) local TypeList = require(ServerStorage.Base.TypeList) +--> Dependencies +local Communicate = require(ServerStorage.Modules.Tools.Communicate) + --> Json local JsonLevel = require(ReplicatedStorage.Json.Level) --> Events local BD_ChallengeEnd = ReplicatedStorage.Events.BD_ChallengeEnd +local RE_CleanPlayerPerformance = ReplicatedStorage.Events.RE_CleanPlayerPerformance + --> Constants local STORE_NAME = "Level" @@ -33,11 +38,15 @@ local LevelFolder = Utils:CreateFolder(STORE_NAME, game.Workspace) -------------------------------------------------------------------------------- -- 获取玩家关卡文件夹 -local function GetPlayerLevelFolder(Player: Player) - local pData = Utils:GetPlayerDataFolder(Player) - if not pData then return end - local LevelFolder = pData:FindFirstChild("Level") - return LevelFolder +local function GetPlayerLevelFolder(Player: Player, ChildFolderName: string?) + local PlayerFolder = LevelFolder:FindFirstChild(Player.UserId) + if not PlayerFolder then warn("PlayerFolder not found", Player.UserId) return end + if ChildFolderName then + local ChildFolder = PlayerFolder:FindFirstChild(ChildFolderName) + if not ChildFolder then warn("ChildFolder not found", ChildFolderName) return end + return ChildFolder + end + return PlayerFolder end -- 获取玩家关卡Workspace目录 @@ -46,9 +55,19 @@ local function GetPlayerLevelWorkspaceFolder(PlayerUserId: string) end -- 创建关卡信息实例 -local function CreateLevelInstance(Player: Player, Folder: Instance, LevelKey: string, LevelValue: number) - if not Player or not Folder or not LevelKey or not LevelValue then return end - local LevelInstance = Instance.new("NumberValue") +local function CreateLevelInstance(Player: Player, Folder: Instance, LevelKey: string, LevelValue: any) + if not Player or not Folder or not LevelKey then return end + local InstanceType + if type(LevelValue) == "number" then + InstanceType = "NumberValue" + elseif type(LevelValue) == "boolean" then + InstanceType = "BoolValue" + elseif type(LevelValue) == "string" then + InstanceType = "StringValue" + else + InstanceType = "NumberValue" + end + local LevelInstance = Instance.new(InstanceType) LevelInstance.Name = LevelKey LevelInstance.Parent = Folder LevelInstance.Value = LevelValue @@ -68,9 +87,9 @@ end local EXCEPT_KEY = { "Task", "Mobs"} local function ChangeValue(Player: Player, Folder: Instance, LevelKey: string, LevelValue: any) - if not Player or not Folder or not LevelKey or not LevelValue then return end + if not Player or not Folder or not LevelKey then warn("LevelProxy ChangeValue", Player.UserId, Folder.Name, LevelKey, LevelValue) return end local ValueInstance = Folder:FindFirstChild(LevelKey) - if not ValueInstance then return end + if not ValueInstance then warn("ValueInstance not found", LevelKey) return end local storeTable if Folder.Name == "Challenge" then @@ -86,17 +105,17 @@ end -- 怪物死亡,由初始化时传入 local function OnMobDied(Player: Player, Mob: TypeList.Character) for _, mob in LevelProxy.pData[Player.UserId].Mobs do - if mob ~= Mob then continue end + if mob.Instance ~= Mob.Instance then continue end table.remove(LevelProxy.pData[Player.UserId].Mobs, table.find(LevelProxy.pData[Player.UserId].Mobs, mob)) - -- 怪物清除判断 - local LevelData = Utils:GetJsonData(JsonLevel, LevelProxy.pData[Player.UserId].LevelId) + -- 怪物被击杀时做关卡数据处理 + local LevelData = Utils:GetIdDataFromJson(JsonLevel, LevelProxy.pData[Player.UserId].LevelId) if LevelProxy.pData[Player.UserId].SpawnWaveFinish and #LevelProxy.pData[Player.UserId].Mobs == 0 then if LevelProxy.pData[Player.UserId].NowWave < #LevelData["wave"] then - -- 波数增长 - LevelProxy.pData[Player.UserId].NowWave = LevelProxy.pData[Player.UserId].NowWave + 1 + -- -- 波数增长 + LevelProxy.pData[Player.UserId].ShouldWave = LevelProxy.pData[Player.UserId].ShouldWave + 1 -- 新波次重置怪物生成状态标记 - local ChallengeFolder = LevelFolder:FindFirstChild("Challenge") + local ChallengeFolder = GetPlayerLevelFolder(Player, "Challenge") ChangeValue(Player, ChallengeFolder, "SpawnWaveFinish", false) elseif LevelProxy.pData[Player.UserId].NowWave >= #LevelData["wave"] then -- 结束判断 @@ -110,8 +129,6 @@ end -------------------------------------------------------------------------------- function LevelProxy:InitPlayer(Player: Player) - local pData = Utils:GetPlayerDataFolder(Player) - 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) @@ -163,8 +180,7 @@ function LevelProxy:ChallengeLevel(Player: Player, LevelId: number) -- 场景后端生成 -- 后端生成当前关卡状态数据 - local LevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId) - local ChallengeFolder = LevelFolder:FindFirstChild("Challenge") + local ChallengeFolder = GetPlayerLevelFolder(Player,"Challenge") if not ChallengeFolder then warn("ChallengeFolder not found") return end local levelTask = task.spawn(function() ChangeValue(Player, ChallengeFolder, "IsBoss", LevelData.type == 2 and true or false) @@ -184,13 +200,13 @@ function LevelProxy:ChallengeLevel(Player: Player, LevelId: number) 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) 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 + print("怪物增益", LevelData.atkBonus, LevelData.hpBonus) local mob = MobsProxy:CreateMob(Player, mobId, LevelData.atkBonus, LevelData.hpBonus, OnMobDied) table.insert(LevelProxy.pData[Player.UserId].Mobs, mob) end @@ -204,7 +220,7 @@ function LevelProxy:ChallengeLevel(Player: Player, LevelId: number) local maxWave = #LevelData.wave if LevelProxy.pData[Player.UserId].NowWave < maxWave then -- 下一波 - ChangeValue(Player, ChallengeFolder, "ShouldWave", LevelProxy.pData[Player.UserId].ShouldWave + 1) + ChangeValue(Player, ChallengeFolder, "ShouldWave", LevelProxy.pData[Player.UserId].ShouldWave) else -- 挑战胜利 self:ChallengeEnd(Player, true) @@ -224,9 +240,7 @@ end -- 挑战结束 function LevelProxy:ChallengeEnd(Player: Player, result: boolean) - local pData = Utils:GetPlayerDataFolder(Player) - local LevelFolder = Utils:CreateFolder(STORE_NAME, pData) - local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder) + local ProgressFolder = GetPlayerLevelFolder(Player, "Progress") -- 停止关卡循环 if LevelProxy.pData[Player.UserId].Task then @@ -235,12 +249,16 @@ function LevelProxy:ChallengeEnd(Player: Player, result: boolean) end -- 清除剩余怪物 + print("清除剩余怪物", LevelProxy.pData[Player.UserId].Mobs) for _, mob in LevelProxy.pData[Player.UserId].Mobs do mob:Died(true) end LevelProxy.pData[Player.UserId].Mobs = {} + -- 清除玩家表现 + Communicate:SendToClientFree(RE_CleanPlayerPerformance, Player) + -- 判断玩家是否通关 if result then - ChangeValue(Player, ProgressFolder, "LevelId", LevelProxy.pData[Player.UserId].LevelId + 1) + ChangeValue(Player, ProgressFolder, "Main", LevelProxy.pData[Player.UserId].LevelId + 1) else local IsBoss = LevelProxy.pData[Player.UserId].IsBoss if IsBoss then diff --git a/src/ServerStorage/Proxy/MobsProxy/init.luau b/src/ServerStorage/Proxy/MobsProxy/init.luau index c1b834a..94db091 100644 --- a/src/ServerStorage/Proxy/MobsProxy/init.luau +++ b/src/ServerStorage/Proxy/MobsProxy/init.luau @@ -68,7 +68,7 @@ function Mob.new(Player: Player, MobId: number, OnMobDied: ((Player: Player, Mob newMobModel.Parent = playerMobsFolder -- 死亡函数 - if OnMobDied then Mob.OnDied = OnMobDied end + if OnMobDied then self.OnDied = OnMobDied end -- -- 接入统一AI -- self.Humanoid.MoveToFinished:Connect(function() @@ -81,11 +81,13 @@ function Mob.new(Player: Player, MobId: number, OnMobDied: ((Player: Player, Mob return self end +-- IsSkinOnDied:暂时意义不明,忘了之前为啥写了 function Mob:Died(IsSkinOnDied: boolean?) MobsProxy:RemoveMob(self.Player, self.Instance) - if not IsSkinOnDied then - if self.OnDied then self.OnDied(self.Player, self) end - end + -- if not IsSkinOnDied then + -- if self.OnDied then self.OnDied(self.Player, self) end + -- end + if self.OnDied then self.OnDied(self.Player, self) end Character.Died(self) end diff --git a/src/ServerStorage/Proxy/PlayerFightProxy/LevelLoop.luau b/src/ServerStorage/Proxy/PlayerFightProxy/LevelLoop.luau index 2d739a5..e3d170c 100644 --- a/src/ServerStorage/Proxy/PlayerFightProxy/LevelLoop.luau +++ b/src/ServerStorage/Proxy/PlayerFightProxy/LevelLoop.luau @@ -38,6 +38,9 @@ function LevelLoop.new(Player: Player, PlayerRole: TypeList.Character) end function LevelLoop:AutoChallenge() + print("AutoChallenge") + + -- TODO: 回退有bug,不能一关一关回退 -- 重置玩家状态(先临时调用角色复活,之后复杂的内容再说) self.PlayerRole:Respawn() @@ -50,11 +53,12 @@ function LevelLoop:AutoChallenge() if FailBossId == LevelId then LevelId = LevelId - 1 end + LevelProxy:ChallengeLevel(self.Player, LevelId) end function LevelLoop:OnChallengeEnd(Player: Player, LevelId: number, result: boolean) - self.TaskAutoChallenge = task.defer(function() + self.TaskAutoChallenge = task.spawn(function() task.wait(3) self:AutoChallenge() end) diff --git a/src/ServerStorage/Proxy/PlayerFightProxy/PlayerAI.luau b/src/ServerStorage/Proxy/PlayerFightProxy/PlayerAI.luau index 1389610..54929dc 100644 --- a/src/ServerStorage/Proxy/PlayerFightProxy/PlayerAI.luau +++ b/src/ServerStorage/Proxy/PlayerFightProxy/PlayerAI.luau @@ -9,6 +9,7 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") --> Dependencies local TypeList = require(ServerStorage.Base.TypeList) +local Utils = require(ReplicatedStorage.Tools.Utils) --> Events local RE_PlayerAI = ReplicatedStorage.Events.RE_PlayerAI @@ -17,8 +18,6 @@ local RE_PlayerAI = ReplicatedStorage.Events.RE_PlayerAI local DamageProxy = require(ServerStorage.Proxy.DamageProxy) local ActivePlayers = {} -local BehavioursFolder = ServerStorage:FindFirstChild("Modules"):FindFirstChild("Behaviours") - local Behaviours = {} local BehaviourFolder = ServerStorage:FindFirstChild("Modules"):FindFirstChild("Behaviours") if BehaviourFolder then @@ -36,7 +35,6 @@ if BehaviourFolder then end end - -------------------------------------------------------------------------------- -- 新建玩家AI @@ -48,6 +46,9 @@ function PlayerAI.new(Player: Player, PlayerRole: TypeList.Character) self.ExecutingState = false self.PlayerControling = false + self.LastTime = 0 + self.Counter = 0 + self.BehaviourList = {} self.LoopTask = task.spawn(function() while task.wait(0.25) do @@ -91,6 +92,11 @@ function PlayerAI:AddBehaviour(BehaviourName: string) self.BehaviourList[BehaviourName] = newBehaviour end +-- 获取并记录行为UniqueId +function PlayerAI:GetBehaviourUniqueId() + return Utils:GenUniqueIdPlayerAI(self.LastTime, self.Counter) +end + -- 动态删除行为 function PlayerAI:RemoveBehaviour(BehaviourName: string) if self.BehaviourList[BehaviourName] then diff --git a/src/ServerStorage/Proxy/PlayerFightProxy/init.luau b/src/ServerStorage/Proxy/PlayerFightProxy/init.luau index 3be8a28..9804d7d 100644 --- a/src/ServerStorage/Proxy/PlayerFightProxy/init.luau +++ b/src/ServerStorage/Proxy/PlayerFightProxy/init.luau @@ -17,6 +17,10 @@ local JsonCharacter = require(ReplicatedStorage.Json.Character) --> Dependencies local LevelLoop = require(script.LevelLoop) local PlayerAI = require(script.PlayerAI) +local Communicate = require(ServerStorage.Modules.Tools.Communicate) + +--> Events +local RE_CleanPlayerPerformance = ReplicatedStorage.Events.RE_CleanPlayerPerformance -------------------------------------------------------------------------------- @@ -43,6 +47,9 @@ function PlayerRole.new(Player: Player, CharacterId: number) -- 调用父类Character的new方法,初始化通用属性 local self = Character.new(Player, playerCharacter, CharacterData) setmetatable(self, PlayerRole) + + -- 玩家放到Character目录下 + playerCharacter.Parent = game.Workspace.Characters return self end @@ -80,6 +87,7 @@ function PlayerFightProxy:InitPlayer(Player: Player) local PlayerAI = PlayerAI.new(Player, PlayerRole) PlayerFightProxy.pData[Player.UserId].PlayerAI = PlayerAI PlayerAI:AddBehaviour("Move") + PlayerAI:AddBehaviour("SwordWave") end function PlayerFightProxy:GetPlayerRole(Player: Player) @@ -103,6 +111,9 @@ function PlayerFightProxy:CleanPlayer(Player: Player) end function PlayerFightProxy:OnPlayerRemoving(Player: Player) + -- 玩家离开时,通知其他客户端销毁玩家表现内容 + Communicate:SendToClientFree(RE_CleanPlayerPerformance, Player) + -- 正常清除玩家该模块下数据 if not PlayerFightProxy.pData[Player.UserId] then warn("PlayerFight Remove Data not found", Player.Name) return end PlayerFightProxy.pData[Player.UserId] = nil end diff --git a/src/StarterPlayerScripts/ClientMain/Helper.luau b/src/StarterPlayerScripts/ClientMain/Helper.luau index 8302612..4a17961 100644 --- a/src/StarterPlayerScripts/ClientMain/Helper.luau +++ b/src/StarterPlayerScripts/ClientMain/Helper.luau @@ -15,7 +15,6 @@ UserInputService.InputBegan:Connect(function(input, gameProcessed) if input.KeyCode == Enum.KeyCode.H then RE_PlayerHelper:FireServer("CleanPlayerData") elseif input.KeyCode == Enum.KeyCode.J then - print("添加物品") RE_PlayerHelper:FireServer("AddItem", {1, 100}) elseif input.KeyCode == Enum.KeyCode.K then RE_UpgradeAttributes:FireServer(1) diff --git a/src/StarterPlayerScripts/ClientMain/PerformanceClient.luau b/src/StarterPlayerScripts/ClientMain/PerformanceClient.luau new file mode 100644 index 0000000..078b0a5 --- /dev/null +++ b/src/StarterPlayerScripts/ClientMain/PerformanceClient.luau @@ -0,0 +1,88 @@ +-- 控制客户端表现管理 + +local PerformanceClient = {} +PerformanceClient.pData = {} + +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +--> Events +local EventsFolder = ReplicatedStorage:FindFirstChild("Events") +local RE_PerformanceEvent = EventsFolder:FindFirstChild("RE_PerformanceEvent") +local RE_CleanPlayerPerformance = EventsFolder:FindFirstChild("RE_CleanPlayerPerformance") + +--> Variables +local LocalPlayer = game.Players.LocalPlayer + +-------------------------------------------------------------------------------- +-- 生成本地化表现目录 +local PerformanceFolder = Instance.new("Folder") +PerformanceFolder.Name = "Performance" +PerformanceFolder.Parent = game.Workspace + +-- 加载所有客户端行为 +local Behaviours = {} +local BehaviourFolder = ReplicatedStorage:FindFirstChild("Modules"):FindFirstChild("BehavioursClient") +if BehaviourFolder then + for _, behaviourModule in ipairs(BehaviourFolder:GetChildren()) do + if behaviourModule:IsA("ModuleScript") then + local success, result = pcall(require, behaviourModule) + if success then + -- 去掉文件名后缀 + local name = behaviourModule.Name + Behaviours[name] = result + else + warn("加载代理模块失败: " .. behaviourModule.Name, result) + end + end + end +end + + +-- 监听表现事件调用 +RE_PerformanceEvent.OnClientEvent:Connect(function(ServerTime: number, CastTag: string, CasterPlayer: Player, BehaviourName: string, CastState: boolean, Infos: table) + -- 初始化玩家存储 + local UserId = CasterPlayer.UserId + if not PerformanceClient.pData[UserId] then PerformanceClient.pData[UserId] = {} end + + local delayTime = ServerTime - tick() + if CastTag == "Init" then + print("Init", BehaviourName) + -- 暂时就新增一个表,不调用初始化,因为服务端不是多个new做的,而是多次调用 + if not PerformanceClient.pData[UserId][BehaviourName] then + PerformanceClient.pData[UserId][BehaviourName] = {} + end + elseif CastTag == "Show" then + print("Show", BehaviourName, Behaviours) + -- 直接调用init和对应的show(因为服务端只new一次,客户端多次调用) + if not PerformanceClient.pData[UserId][BehaviourName] then return end + for _, CastInfo in pairs(Infos) do + local BehaviourTable = PerformanceClient.pData[UserId][BehaviourName] + BehaviourTable[CastInfo.UniqueId] = Behaviours[BehaviourName]:Init(CasterPlayer, CastInfo, delayTime, CastState) + BehaviourTable[CastInfo.UniqueId]:Show(CasterPlayer, CastInfo, delayTime, CastState) + end + elseif CastTag == "Destroy" then + if not PerformanceClient.pData[UserId][BehaviourName] then return end + for _, CastInfo in pairs(Infos) do + local Behaviour = PerformanceClient.pData[UserId][BehaviourName][CastInfo.UniqueId] + if Behaviour and Behaviour.Destroy then + Behaviour:Destroy() + end + end + end +end) + +-- 清理玩家所有表现数据 +RE_CleanPlayerPerformance.OnClientEvent:Connect(function(CleanedPlayer: Player) + -- print("CleanPlayerPerformance", CleanedPlayer) + local UserId = CleanedPlayer.UserId + if not PerformanceClient.pData[UserId] then return end + for _, BehaviourList in pairs(PerformanceClient.pData[UserId]) do + for _, Behaviour in pairs(BehaviourList) do + Behaviour:Destroy() + end + end + PerformanceClient.pData[UserId] = nil +end) + +return PerformanceClient \ No newline at end of file