From 8fae93da030503b6e4ff84ba21d4c5f3a52eba73 Mon Sep 17 00:00:00 2001 From: gechangfu Date: Fri, 22 Aug 2025 16:40:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0-2=E7=BA=A7=E5=89=8D=E6=89=80?= =?UTF-8?q?=E6=9C=89=E8=AF=8D=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- excel/Rune.xlsx | Bin 14707 -> 14753 bytes excel/ability.xlsx | Bin 17557 -> 17631 bytes excel/attribute.xlsx | Bin 13941 -> 14005 bytes excel/item.xlsx | Bin 25369 -> 25294 bytes .../Data/EventFilterEnum.luau | 3 +- src/ReplicatedStorage/Json/Ability.json | 6 +- src/ReplicatedStorage/Json/Attributes.json | 1 + src/ReplicatedStorage/Json/Rune.json | 57 ++++++------ src/ServerStorage/Base/Behaviour.luau | 9 ++ .../Modules/Behaviours/Attack.luau | 9 +- .../Modules/Behaviours/IceCoffine.luau | 1 + .../Modules/Behaviours/SwordWave.luau | 1 + .../Runes/RuneActiveAbilityAttackRate.luau | 83 +++++++++++++++++ .../Modules/Runes/RuneAllActiveAbility.luau | 88 ++++++++++++++++++ .../Modules/Runes/RuneAtkSpeedToAtkRate.luau | 40 ++++++++ .../Modules/Runes/RuneAttack.luau | 33 +++++++ .../Modules/Runes/RuneAttackRecoverHp.luau | 49 ++++++++++ .../Modules/Runes/RuneBanActiveAbility.luau | 84 +++++++++++++++++ .../Modules/Runes/RuneCritRateDouble.luau | 38 ++++++++ .../Modules/Runes/RuneFireDamage.luau | 1 - .../Modules/Runes/RuneFirstElementCopy.luau | 48 ++++++++++ .../Modules/Runes/RuneFirstHpDamage.luau | 50 ++++++++++ .../Modules/Runes/RuneHealCrit.luau | 48 ++++++++++ src/ServerStorage/Modules/Runes/RuneHp.luau | 33 +++++++ .../Modules/Runes/RuneIronBonus.luau | 33 +++++++ .../Modules/Runes/RuneLastElementCopy.luau | 49 ++++++++++ .../Modules/Runes/RuneLeftTrigger.luau | 33 +++++++ .../Modules/Runes/RuneNormalDamageToIce.luau | 74 +++++++++++++++ .../Runes/RunePassiveAbilityAttackRate.luau | 81 ++++++++++++++++ .../Modules/Runes/RuneRightTrigger.luau | 33 +++++++ src/ServerStorage/Proxy/DamageProxy.luau | 15 +++ src/ServerStorage/Proxy/EquipmentProxy.luau | 14 ++- src/ServerStorage/Proxy/LevelProxy.luau | 8 ++ 33 files changed, 984 insertions(+), 38 deletions(-) create mode 100644 src/ServerStorage/Modules/Runes/RuneActiveAbilityAttackRate.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneAllActiveAbility.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneAtkSpeedToAtkRate.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneAttack.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneAttackRecoverHp.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneBanActiveAbility.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneCritRateDouble.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneFirstElementCopy.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneFirstHpDamage.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneHealCrit.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneHp.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneIronBonus.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneLastElementCopy.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneLeftTrigger.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneNormalDamageToIce.luau create mode 100644 src/ServerStorage/Modules/Runes/RunePassiveAbilityAttackRate.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneRightTrigger.luau diff --git a/excel/Rune.xlsx b/excel/Rune.xlsx index 5ef1a1e1dacf53a661f2647fa42dd6df18f78325..70236d806590376d6d119d7792c9f4e05432dff1 100644 GIT binary patch delta 9202 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 delta 9111 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 diff --git a/excel/ability.xlsx b/excel/ability.xlsx index 8e6459d6c3022b291771c40b33764f46e4a1c593..d4a6477e40b3ac2db2015ce771cc223ec457f9d0 100644 GIT binary patch delta 4333 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 delta 4229 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 kNm#k$1DLilU2Hr& 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 Ys{aBI!JkPh<0QaSq&W#KCH^M;5499t+5i9m diff --git a/excel/item.xlsx b/excel/item.xlsx index c4725b8d52224a584ec6e35234a57519cbd91202..2d716dd0ff070f7d96cc9584ea23f48c45e31003 100644 GIT binary patch delta 16022 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 E0smRVEC2ui diff --git a/src/ReplicatedStorage/Data/EventFilterEnum.luau b/src/ReplicatedStorage/Data/EventFilterEnum.luau index dc644cc..647546c 100644 --- a/src/ReplicatedStorage/Data/EventFilterEnum.luau +++ b/src/ReplicatedStorage/Data/EventFilterEnum.luau @@ -2,9 +2,10 @@ local ServerSignalEnum = {} ServerSignalEnum = { BEFORE_ATTACK = "BEFORE_ATTACK", - -- AFTER_ATTACK = "AFTER_ATTACK", + AFTER_ATTACK = "AFTER_ATTACK", -- BEFORE_KILL_MONSTER = "BEFORE_KILL_MONSTER", BEFORE_KILL_MONSTER = "BEFORE_KILL_MONSTER", + BEFORE_HEAL = "BEFORE_HEAL", } return ServerSignalEnum \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Ability.json b/src/ReplicatedStorage/Json/Ability.json index c24fb01..8aff23f 100644 --- a/src/ReplicatedStorage/Json/Ability.json +++ b/src/ReplicatedStorage/Json/Ability.json @@ -1,5 +1,5 @@ [ -{"id":20000,"type":1,"icon":1,"nameId":20000,"behaviourName":"Attack","upgradeCost":[30000,5,0],"upgradeValue":[10,0],"recycle":[30000,0],"isInPool":null}, -{"id":20001,"type":1,"icon":1,"nameId":20001,"behaviourName":"SwordWave","upgradeCost":[30000,5,10],"upgradeValue":[10,200],"recycle":[30000,100],"isInPool":1}, -{"id":61000,"type":1,"icon":1,"nameId":61000,"behaviourName":"IceCoffine","upgradeCost":[30000,5,0],"upgradeValue":[10,0],"recycle":[30000,0],"isInPool":null} +{"id":20000,"type":1,"castType":null,"icon":1,"nameId":20000,"behaviourName":"Attack","upgradeCost":[30000,5,0],"upgradeValue":[10,0],"recycle":[30000,0],"isInPool":null}, +{"id":20001,"type":1,"castType":1,"icon":1,"nameId":20001,"behaviourName":"SwordWave","upgradeCost":[30000,5,10],"upgradeValue":[10,200],"recycle":[30000,100],"isInPool":1}, +{"id":61000,"type":1,"castType":1,"icon":1,"nameId":61000,"behaviourName":"IceCoffine","upgradeCost":[30000,5,0],"upgradeValue":[10,0],"recycle":[30000,0],"isInPool":null} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Attributes.json b/src/ReplicatedStorage/Json/Attributes.json index 4e578ac..f472ebf 100644 --- a/src/ReplicatedStorage/Json/Attributes.json +++ b/src/ReplicatedStorage/Json/Attributes.json @@ -32,6 +32,7 @@ {"id":34,"type":2,"specialType":1,"effectAttribute":"critDamageRateIce","battleValue":[1,10],"iconId":27,"nameId":234}, {"id":35,"type":2,"specialType":1,"effectAttribute":"critDamageRateLight","battleValue":[1,10],"iconId":28,"nameId":235}, {"id":36,"type":2,"specialType":1,"effectAttribute":"critDamageRateDark","battleValue":[1,10],"iconId":29,"nameId":236}, +{"id":37,"type":2,"specialType":null,"effectAttribute":"ironBonus","battleValue":[1,0],"iconId":37,"nameId":237}, {"id":50,"type":1,"specialType":null,"effectAttribute":"wearNumber","battleValue":[1,10],"iconId":50,"nameId":250}, {"id":51,"type":1,"specialType":null,"effectAttribute":"skillNumber","battleValue":[1,10],"iconId":51,"nameId":251}, {"id":52,"type":1,"specialType":null,"effectAttribute":"extraAttributeNumber","battleValue":[1,10],"iconId":52,"nameId":252}, diff --git a/src/ReplicatedStorage/Json/Rune.json b/src/ReplicatedStorage/Json/Rune.json index b1a366c..5dd1789 100644 --- a/src/ReplicatedStorage/Json/Rune.json +++ b/src/ReplicatedStorage/Json/Rune.json @@ -22,35 +22,34 @@ {"id":60020,"quality":1,"type":null,"icon":1,"nameId":60020,"runeName":"RuneBossHp","behaviorName":null,"recycle":[],"isInPool":1}, {"id":60021,"quality":1,"type":null,"icon":1,"nameId":60021,"runeName":"RuneHpToAtk","behaviorName":null,"recycle":[],"isInPool":1}, {"id":60022,"quality":1,"type":null,"icon":1,"nameId":60022,"runeName":"RuneAtkSpeed","behaviorName":null,"recycle":[],"isInPool":1}, -{"id":60023,"quality":1,"type":null,"icon":1,"nameId":60023,"runeName":"RuneThunderDoubleDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60023,"quality":1,"type":null,"icon":1,"nameId":60023,"runeName":"RuneThunderAddDamageTime","behaviorName":null,"recycle":[],"isInPool":1}, {"id":60024,"quality":1,"type":null,"icon":1,"nameId":60024,"runeName":"RuneWearElementAttackSpeed","behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61000,"quality":2,"type":1,"icon":1,"nameId":61000,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61001,"quality":2,"type":null,"icon":1,"nameId":61001,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61002,"quality":2,"type":null,"icon":1,"nameId":61002,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61003,"quality":2,"type":null,"icon":1,"nameId":61003,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61004,"quality":2,"type":null,"icon":1,"nameId":61004,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61005,"quality":2,"type":null,"icon":1,"nameId":61005,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61006,"quality":2,"type":null,"icon":1,"nameId":61006,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61007,"quality":2,"type":null,"icon":1,"nameId":61007,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61008,"quality":2,"type":null,"icon":1,"nameId":61008,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61009,"quality":2,"type":null,"icon":1,"nameId":61009,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61010,"quality":2,"type":null,"icon":1,"nameId":61010,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61011,"quality":2,"type":null,"icon":1,"nameId":61011,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61012,"quality":2,"type":null,"icon":1,"nameId":61012,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61013,"quality":2,"type":null,"icon":1,"nameId":61013,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61014,"quality":2,"type":null,"icon":1,"nameId":61014,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61015,"quality":2,"type":null,"icon":1,"nameId":61015,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61016,"quality":2,"type":null,"icon":1,"nameId":61016,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61017,"quality":2,"type":null,"icon":1,"nameId":61017,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61018,"quality":2,"type":null,"icon":1,"nameId":61018,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61019,"quality":2,"type":null,"icon":1,"nameId":61019,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61020,"quality":2,"type":null,"icon":1,"nameId":61020,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61021,"quality":2,"type":null,"icon":1,"nameId":61021,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61022,"quality":2,"type":null,"icon":1,"nameId":61022,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61023,"quality":2,"type":null,"icon":1,"nameId":61023,"runeName":"RuneIceCoffin","behaviorName":"IceCoffine","recycle":[],"isInPool":1}, -{"id":61024,"quality":2,"type":null,"icon":1,"nameId":61024,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61025,"quality":2,"type":null,"icon":1,"nameId":61025,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61026,"quality":2,"type":null,"icon":1,"nameId":61026,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, -{"id":61027,"quality":2,"type":null,"icon":1,"nameId":61027,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61000,"quality":2,"type":1,"icon":1,"nameId":61000,"runeName":"RuneThunderDoubleDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61001,"quality":2,"type":null,"icon":1,"nameId":61001,"runeName":"RuneBookQualityYellow","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61002,"quality":2,"type":null,"icon":1,"nameId":61002,"runeName":"RuneCritToAttackRate","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61003,"quality":2,"type":null,"icon":1,"nameId":61003,"runeName":"RuneWearGenWeapon","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61004,"quality":2,"type":null,"icon":1,"nameId":61004,"runeName":"RuneNormalCritRate","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61005,"quality":2,"type":null,"icon":1,"nameId":61005,"runeName":"RuneWearSingleRuneSlot","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61006,"quality":2,"type":null,"icon":1,"nameId":61006,"runeName":"RuneSacrifice","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61007,"quality":2,"type":null,"icon":1,"nameId":61007,"runeName":"RuneNormalNoElement","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61008,"quality":2,"type":null,"icon":1,"nameId":61008,"runeName":"RuneKillCoin","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61009,"quality":2,"type":null,"icon":1,"nameId":61009,"runeName":"RuneBanActiveAbility","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61010,"quality":2,"type":null,"icon":1,"nameId":61010,"runeName":"RuneFirstElementCopy","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61011,"quality":2,"type":null,"icon":1,"nameId":61011,"runeName":"RuneLastElementCopy","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61012,"quality":2,"type":null,"icon":1,"nameId":61012,"runeName":"RuneNormalDamageToIce","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61013,"quality":2,"type":null,"icon":1,"nameId":61013,"runeName":"RuneAttackRecoverHp","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61014,"quality":2,"type":null,"icon":1,"nameId":61014,"runeName":"RuneIronBonus","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61015,"quality":2,"type":null,"icon":1,"nameId":61015,"runeName":"RuneActiveAbilityAttackRate","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61016,"quality":2,"type":null,"icon":1,"nameId":61016,"runeName":"RunePassiveAbilityAttackRate","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61017,"quality":2,"type":null,"icon":1,"nameId":61018,"runeName":"RuneLeftTrigger","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61018,"quality":2,"type":null,"icon":1,"nameId":61019,"runeName":"RuneRightTrigger","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61019,"quality":2,"type":null,"icon":1,"nameId":61020,"runeName":"RuneAllActiveAbility","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61020,"quality":2,"type":null,"icon":1,"nameId":61021,"runeName":"RuneAttack","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61021,"quality":2,"type":null,"icon":1,"nameId":61022,"runeName":"RuneHp","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61022,"quality":2,"type":null,"icon":1,"nameId":61023,"runeName":"RuneIceCoffin","behaviorName":"IceCoffine","recycle":[],"isInPool":1}, +{"id":61023,"quality":2,"type":null,"icon":1,"nameId":61024,"runeName":"RuneFirstHpDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61024,"quality":2,"type":null,"icon":1,"nameId":61025,"runeName":"RuneHealCrit","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61025,"quality":2,"type":null,"icon":1,"nameId":61026,"runeName":"RuneCritRateDouble","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61026,"quality":2,"type":null,"icon":1,"nameId":61027,"runeName":"RuneAtkSpeedToAtkRate","behaviorName":null,"recycle":[],"isInPool":1}, {"id":62000,"quality":3,"type":1,"icon":1,"nameId":62000,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1} ] \ No newline at end of file diff --git a/src/ServerStorage/Base/Behaviour.luau b/src/ServerStorage/Base/Behaviour.luau index 45d1d11..fed72d9 100644 --- a/src/ServerStorage/Base/Behaviour.luau +++ b/src/ServerStorage/Base/Behaviour.luau @@ -122,6 +122,15 @@ function Behaviour:CheckStat() return false end +-- 检查特殊状态是否可执行(禁止使用技能) +function Behaviour:CheckSpecialStat() + if not self.Character then return true end + if self.PlayerAI:GetSharedData("BanAbility") then + return true + end + return false +end + -- 改变当前执行状态标记 function Behaviour:ChangeExecutingState(State: boolean) if not self.Character then warn("Behaviour Character not found") return end diff --git a/src/ServerStorage/Modules/Behaviours/Attack.luau b/src/ServerStorage/Modules/Behaviours/Attack.luau index acda124..c637d33 100644 --- a/src/ServerStorage/Modules/Behaviours/Attack.luau +++ b/src/ServerStorage/Modules/Behaviours/Attack.luau @@ -128,7 +128,7 @@ function Attack:Execute() end end - -- 发送事件 + -- 发送攻击前事件 EventFilter.FireGlobalWithFilter(EventFilterEnum.BEFORE_ATTACK, { attacker = self.Character, target = self.CheckData["ClosestCharacter"], @@ -137,6 +137,13 @@ function Attack:Execute() damageData = processedData.damageData end) DamageProxy:TakeDamage(self.Character, self.CheckData["ClosestCharacter"], damageData) + + -- 发送攻击后事件 + EventFilter.FireGlobalWithFilter(EventFilterEnum.AFTER_ATTACK, { + attacker = self.Character, + target = self.CheckData["ClosestCharacter"], + damageData = damageData, + }) -- task.wait(atkSpeed / 2) self:ChangeExecutingState(false) diff --git a/src/ServerStorage/Modules/Behaviours/IceCoffine.luau b/src/ServerStorage/Modules/Behaviours/IceCoffine.luau index 5dccc76..8e7639c 100644 --- a/src/ServerStorage/Modules/Behaviours/IceCoffine.luau +++ b/src/ServerStorage/Modules/Behaviours/IceCoffine.luau @@ -37,6 +37,7 @@ end function IceCoffine:Check(CheckInfo: table) if Behaviour.CheckStat(self) then return -1, self.CheckData end + if self:CheckSpecialStat() then return -1, self.CheckData end self:CheckClean() -- 当前血量<=20%时触发技能 diff --git a/src/ServerStorage/Modules/Behaviours/SwordWave.luau b/src/ServerStorage/Modules/Behaviours/SwordWave.luau index e8b9921..89b6ae2 100644 --- a/src/ServerStorage/Modules/Behaviours/SwordWave.luau +++ b/src/ServerStorage/Modules/Behaviours/SwordWave.luau @@ -37,6 +37,7 @@ end function SwordWave:Check(CheckInfo: table) if Behaviour.CheckStat(self) then return -1, self.CheckData end + if self:CheckSpecialStat() then return -1, self.CheckData end self:CheckClean() self.Mobs = MobsProxy:GetPlayerMobs(self.Player) diff --git a/src/ServerStorage/Modules/Runes/RuneActiveAbilityAttackRate.luau b/src/ServerStorage/Modules/Runes/RuneActiveAbilityAttackRate.luau new file mode 100644 index 0000000..e03688f --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneActiveAbilityAttackRate.luau @@ -0,0 +1,83 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local Rng = require(ReplicatedStorage.Tools.Rng) + +--> Json +local JsonAbility = require(ReplicatedStorage.Json.Ability) + +local RuneActiveAbilityAttackRate = {} +RuneActiveAbilityAttackRate.__index = RuneActiveAbilityAttackRate +setmetatable(RuneActiveAbilityAttackRate, {__index = Rune}) + + +function RuneActiveAbilityAttackRate:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneActiveAbilityAttackRate) + + return self +end + +function RuneActiveAbilityAttackRate:Check(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + local abilityFolder = pData:FindFirstChild("Ability") + if not abilityFolder then return nil end + local abilityList = abilityFolder:GetChildren() + if #abilityList == 0 then return nil end + + local wearingEquipmentList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + table.insert(wearingEquipmentList, equipment.Name) + end + end + + self.Data = nil + self.Data = {} + for _, ability in abilityList do + local wearingEquipment = ability:GetAttribute("wearing") + if wearingEquipment > 0 then + if table.find(wearingEquipmentList, wearingEquipment) then + table.insert(self.Data, ability) + end + end + end + + return true +end + +function RuneActiveAbilityAttackRate:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local banAbilityNumber = 0 + for _, ability in self.Data do + local abilityId = ability:GetAttribute("orgId") + local abilityData = Utils:GetIdDataFromJson(JsonAbility, abilityId) + if abilityData.castType then + if abilityData.castType == 1 then + banAbilityNumber = banAbilityNumber + 1 + end + end + end + + local attackRate = math.floor((AttributesData.attackRate or 100) * banAbilityNumber * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneActiveAbilityAttackRate \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneAllActiveAbility.luau b/src/ServerStorage/Modules/Runes/RuneAllActiveAbility.luau new file mode 100644 index 0000000..6ff469f --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneAllActiveAbility.luau @@ -0,0 +1,88 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local Rng = require(ReplicatedStorage.Tools.Rng) + +--> Json +local JsonAbility = require(ReplicatedStorage.Json.Ability) + +local RuneAllActiveAbility = {} +RuneAllActiveAbility.__index = RuneAllActiveAbility +setmetatable(RuneAllActiveAbility, {__index = Rune}) + + +function RuneAllActiveAbility:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneAllActiveAbility) + + return self +end + +function RuneAllActiveAbility:Check(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + local abilityFolder = pData:FindFirstChild("Ability") + if not abilityFolder then return nil end + local abilityList = abilityFolder:GetChildren() + if #abilityList == 0 then return nil end + + local wearingEquipmentList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + local maxAbilityNumber = equipment:GetAttribute("maxAbilityNumber") + if equipmentWearing > 0 and maxAbilityNumber > 0 then + table.insert(wearingEquipmentList, { + id = equipment.Name, + maxAbilityNumber = maxAbilityNumber, + abilityNumber = 0, + }) + end + end + + for _, ability in abilityList do + local wearingEquipment = ability:GetAttribute("wearing") + local orgId = ability:GetAttribute("orgId") + for _, equipmentData in wearingEquipmentList do + if equipmentData.id == wearingEquipment then + local abilityData = Utils:GetIdDataFromJson(JsonAbility, orgId) + if abilityData.castType then + if abilityData.castType == 1 then + equipmentData.abilityNumber = equipmentData.abilityNumber + 1 + end + end + end + end + end + + self.Data = 0 + for _, equipmentData in wearingEquipmentList do + if equipmentData.abilityNumber >= equipmentData.maxAbilityNumber then + self.Data = self.Data + 1 + end + end + + return true +end + +function RuneAllActiveAbility:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local attackRate = math.floor((AttributesData.attackRate or 100) * self.Data * 150 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneAllActiveAbility \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneAtkSpeedToAtkRate.luau b/src/ServerStorage/Modules/Runes/RuneAtkSpeedToAtkRate.luau new file mode 100644 index 0000000..da34a7d --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneAtkSpeedToAtkRate.luau @@ -0,0 +1,40 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +--> Json +local JsonLevel = require(ReplicatedStorage.Json.Level) + +local RuneAtkSpeedToAtkRate = {} +RuneAtkSpeedToAtkRate.__index = RuneAtkSpeedToAtkRate +setmetatable(RuneAtkSpeedToAtkRate, {__index = Rune}) + + +function RuneAtkSpeedToAtkRate:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneAtkSpeedToAtkRate) + + return self +end + +function RuneAtkSpeedToAtkRate:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneAtkSpeedToAtkRate:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.atkSpeed or 100 + local atkRate = AttributesData.atkRate or 100 + + local addAttribute = math.floor(atkRate * (25 / 100 + math.max(0, (100 - baseAttribute) * 5))) + Utils:TableSafeAddValue(AttributesData, "atkRate", addAttribute) + + return nil +end + + +return RuneAtkSpeedToAtkRate \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneAttack.luau b/src/ServerStorage/Modules/Runes/RuneAttack.luau new file mode 100644 index 0000000..91c1fd8 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneAttack.luau @@ -0,0 +1,33 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneAttack = {} +RuneAttack.__index = RuneAttack +setmetatable(RuneAttack, {__index = Rune}) + +function RuneAttack:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneAttack) + + return self +end + +function RuneAttack:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneAttack:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.attackRate or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", addAttribute) + return nil +end + + +return RuneAttack \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneAttackRecoverHp.luau b/src/ServerStorage/Modules/Runes/RuneAttackRecoverHp.luau new file mode 100644 index 0000000..a93406b --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneAttackRecoverHp.luau @@ -0,0 +1,49 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local DamageProxy = require(ServerStorage.Proxy.DamageProxy) + +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + +local RuneAttackRecoverHp = {} +RuneAttackRecoverHp.__index = RuneAttackRecoverHp +setmetatable(RuneAttackRecoverHp, {__index = Rune}) + + +function RuneAttackRecoverHp:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneAttackRecoverHp) + + return self +end + +function RuneAttackRecoverHp:OnInitFinish() + EventFilter.SubscribeGlobalFilter(EventFilterEnum.AFTER_ATTACK, function(eventData) + local recoverHp = 0 + for _, damageData in eventData.damageData do + recoverHp = recoverHp + damageData.Damage * 0.2 + end + + recoverHp = math.floor(recoverHp) + DamageProxy:Heal(eventData.attacker, eventData.attacker, recoverHp) + return eventData + end, self.TriggerSlot, self) +end + +function RuneAttackRecoverHp:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneAttackRecoverHp:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + return nil +end + + +return RuneAttackRecoverHp \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneBanActiveAbility.luau b/src/ServerStorage/Modules/Runes/RuneBanActiveAbility.luau new file mode 100644 index 0000000..66d19ff --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneBanActiveAbility.luau @@ -0,0 +1,84 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local Rng = require(ReplicatedStorage.Tools.Rng) + +--> Json +local JsonAbility = require(ReplicatedStorage.Json.Ability) + +local RuneBanActiveAbility = {} +RuneBanActiveAbility.__index = RuneBanActiveAbility +setmetatable(RuneBanActiveAbility, {__index = Rune}) + + +function RuneBanActiveAbility:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneBanActiveAbility) + + return self +end + +function RuneBanActiveAbility:Check(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + local abilityFolder = pData:FindFirstChild("Ability") + if not abilityFolder then return nil end + local abilityList = abilityFolder:GetChildren() + if #abilityList == 0 then return nil end + + local wearingEquipmentList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + table.insert(wearingEquipmentList, equipment.Name) + end + end + + self.Data = nil + self.Data = {} + for _, ability in abilityList do + local wearingEquipment = ability:GetAttribute("wearing") + if wearingEquipment > 0 then + if table.find(wearingEquipmentList, wearingEquipment) then + table.insert(self.Data, ability) + end + end + end + + return true +end + +function RuneBanActiveAbility:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local banAbilityNumber = 0 + for _, ability in self.Data do + local abilityId = ability:GetAttribute("orgId") + local abilityData = Utils:GetIdDataFromJson(JsonAbility, abilityId) + if abilityData.castType then + if abilityData.castType == 1 then + banAbilityNumber = banAbilityNumber + 1 + end + end + end + + self.PlayerAI:SetSharedData("BanAbility", 1) + local attackRate = math.floor((AttributesData.attackRate or 100) * banAbilityNumber * 150 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneBanActiveAbility \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritRateDouble.luau b/src/ServerStorage/Modules/Runes/RuneCritRateDouble.luau new file mode 100644 index 0000000..3b88992 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritRateDouble.luau @@ -0,0 +1,38 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local DamageProxy = require(ServerStorage.Proxy.DamageProxy) +local Rng = require(ServerStorage.Base.Rng) + +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + +local RuneCritRateDouble = {} +RuneCritRateDouble.__index = RuneCritRateDouble +setmetatable(RuneCritRateDouble, {__index = Rune}) + + +function RuneCritRateDouble:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritRateDouble) + + return self +end + +function RuneCritRateDouble:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritRateDouble:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local critRate = AttributesData.critRate or 0 + Utils:TableSafeAddValue(AttributesData, "critRate", critRate) + return nil +end + +return RuneCritRateDouble \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneFireDamage.luau b/src/ServerStorage/Modules/Runes/RuneFireDamage.luau index 31aadaa..8ca0567 100644 --- a/src/ServerStorage/Modules/Runes/RuneFireDamage.luau +++ b/src/ServerStorage/Modules/Runes/RuneFireDamage.luau @@ -11,7 +11,6 @@ local RuneFireDamage = {} RuneFireDamage.__index = RuneFireDamage setmetatable(RuneFireDamage, {__index = Rune}) - function RuneFireDamage:Init(PlayerAI, Character: TypeList.Character) local self = Rune:Init(PlayerAI, Character, script.Name) setmetatable(self, RuneFireDamage) diff --git a/src/ServerStorage/Modules/Runes/RuneFirstElementCopy.luau b/src/ServerStorage/Modules/Runes/RuneFirstElementCopy.luau new file mode 100644 index 0000000..eae7f8f --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneFirstElementCopy.luau @@ -0,0 +1,48 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local DamageProxy = require(ServerStorage.Proxy.DamageProxy) + +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + +local RuneFirstElementCopy = {} +RuneFirstElementCopy.__index = RuneFirstElementCopy +setmetatable(RuneFirstElementCopy, {__index = Rune}) + + +function RuneFirstElementCopy:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneFirstElementCopy) + + return self +end + +function RuneFirstElementCopy:OnInitFinish() + EventFilter.SubscribeGlobalFilter(EventFilterEnum.BEFORE_ATTACK, function(eventData) + for _, damageData in eventData.damageData do + if damageData.ElementType ~= DamageProxy.ElementType.NONE then + local newDamageData = Utils:DeepCopyTable(damageData) + table.insert(eventData.damageData, newDamageData) + break + end + end + return eventData + end, self.TriggerSlot, self) +end + +function RuneFirstElementCopy:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneFirstElementCopy:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + return nil +end + +return RuneFirstElementCopy \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneFirstHpDamage.luau b/src/ServerStorage/Modules/Runes/RuneFirstHpDamage.luau new file mode 100644 index 0000000..53e0ead --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneFirstHpDamage.luau @@ -0,0 +1,50 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + +local RuneFirstHpDamage = {} +RuneFirstHpDamage.__index = RuneFirstHpDamage +setmetatable(RuneFirstHpDamage, {__index = Rune}) + + +function RuneFirstHpDamage:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneFirstHpDamage) + self.FirstAttack = true + + return self +end + +function RuneFirstHpDamage:OnInitFinish() + EventFilter.SubscribeGlobalFilter(EventFilterEnum.BEFORE_ATTACK, function(eventData) + if self.FirstAttack then + self.FirstAttack = false + for _, damageData in eventData.damageData do + damageData.Damage = damageData.Damage + math.floor(self.PlayerAI.Config.maxhp * 40 / 100) + break + end + end + return eventData + end, self.TriggerSlot, self) + +end + +function RuneFirstHpDamage:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneFirstHpDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + return nil +end + + +return RuneFirstHpDamage \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneHealCrit.luau b/src/ServerStorage/Modules/Runes/RuneHealCrit.luau new file mode 100644 index 0000000..8d9f22c --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneHealCrit.luau @@ -0,0 +1,48 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local DamageProxy = require(ServerStorage.Proxy.DamageProxy) +local Rng = require(ServerStorage.Base.Rng) + +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + +local RuneHealCrit = {} +RuneHealCrit.__index = RuneHealCrit +setmetatable(RuneHealCrit, {__index = Rune}) + + +function RuneHealCrit:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneHealCrit) + + return self +end + +function RuneHealCrit:OnInitFinish() + EventFilter.SubscribeGlobalFilter(EventFilterEnum.BEFORE_HEAL, function(eventData) + eventData.amount = math.floor(eventData.amount * 40 / 100) + local critRate = self.PlayerAI.Config.critRate or 0 + if Rng:RandomPercent(critRate) then + eventData.amount = math.floor(eventData.amount * 2) + end + + return eventData + end, self.TriggerSlot, self) +end + +function RuneHealCrit:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneHealCrit:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + return nil +end + +return RuneHealCrit \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneHp.luau b/src/ServerStorage/Modules/Runes/RuneHp.luau new file mode 100644 index 0000000..a418e9e --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneHp.luau @@ -0,0 +1,33 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneHp = {} +RuneHp.__index = RuneHp +setmetatable(RuneHp, {__index = Rune}) + +function RuneHp:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneHp) + + return self +end + +function RuneHp:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneHp:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.hpRate or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "hpRate", addAttribute) + return nil +end + + +return RuneHp \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneIronBonus.luau b/src/ServerStorage/Modules/Runes/RuneIronBonus.luau new file mode 100644 index 0000000..a884509 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneIronBonus.luau @@ -0,0 +1,33 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneIronBonus = {} +RuneIronBonus.__index = RuneIronBonus +setmetatable(RuneIronBonus, {__index = Rune}) + + +function RuneIronBonus:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneIronBonus) + + return self +end + +function RuneIronBonus:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneIronBonus:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.iron or 100 + local addAttribute = math.floor(baseAttribute * 20 / 100) + Utils:TableSafeAddValue(AttributesData, "ironBonus", addAttribute) + return nil +end + +return RuneIronBonus \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneLastElementCopy.luau b/src/ServerStorage/Modules/Runes/RuneLastElementCopy.luau new file mode 100644 index 0000000..43ac365 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneLastElementCopy.luau @@ -0,0 +1,49 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local DamageProxy = require(ServerStorage.Proxy.DamageProxy) + +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + +local RuneLastElementCopy = {} +RuneLastElementCopy.__index = RuneLastElementCopy +setmetatable(RuneLastElementCopy, {__index = Rune}) + + +function RuneLastElementCopy:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneLastElementCopy) + + return self +end + +function RuneLastElementCopy:OnInitFinish() + EventFilter.SubscribeGlobalFilter(EventFilterEnum.BEFORE_ATTACK, function(eventData) + for i = #eventData.damageData, 1, -1 do + local damageData = eventData.damageData[i] + if damageData.ElementType ~= DamageProxy.ElementType.NONE then + local newDamageData = Utils:DeepCopyTable(damageData) + table.insert(eventData.damageData, newDamageData) + break + end + end + return eventData + end, self.TriggerSlot, self) +end + +function RuneLastElementCopy:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneLastElementCopy:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + return nil +end + +return RuneLastElementCopy \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneLeftTrigger.luau b/src/ServerStorage/Modules/Runes/RuneLeftTrigger.luau new file mode 100644 index 0000000..3f2dc3a --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneLeftTrigger.luau @@ -0,0 +1,33 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneLeftTrigger = {} +RuneLeftTrigger.__index = RuneLeftTrigger +setmetatable(RuneLeftTrigger, {__index = Rune}) + +function RuneLeftTrigger:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneLeftTrigger) + self.TriggerTime = 0 + + return self +end + +function RuneLeftTrigger:Check(index: number, AttributesData: table, BehaviorNameList: table) + if self.TriggerSlot == 1 then return false end + if self.TriggerTime ~= 0 then return false end + return true +end + +function RuneLeftTrigger:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + self.TriggerTime = self.TriggerTime + 1 + return self.TriggerSlot - 1 +end + +return RuneLeftTrigger \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneNormalDamageToIce.luau b/src/ServerStorage/Modules/Runes/RuneNormalDamageToIce.luau new file mode 100644 index 0000000..6ea79cb --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneNormalDamageToIce.luau @@ -0,0 +1,74 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local DamageProxy = require(ServerStorage.Proxy.DamageProxy) + +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + +local RuneNormalDamageToIce = {} +RuneNormalDamageToIce.__index = RuneNormalDamageToIce +setmetatable(RuneNormalDamageToIce, {__index = Rune}) + + +function RuneNormalDamageToIce:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneNormalDamageToIce) + + return self +end + +function RuneNormalDamageToIce:OnInitFinish() + EventFilter.SubscribeGlobalFilter(EventFilterEnum.BEFORE_ATTACK, function(eventData) + local noneDamageValue = 0 + local hasIceDamage = false + local iceDamageIndex = nil + local lastNoneDamageData = nil + + -- 第一步:找到NONE类型的伤害并计算总值,同时检查是否有ICE伤害 + for i = #eventData.damageData, 1, -1 do + local damageData = eventData.damageData[i] + if damageData.ElementType == DamageProxy.ElementType.NONE then + noneDamageValue = noneDamageValue + damageData.Damage + if not lastNoneDamageData then + lastNoneDamageData = Utils:DeepCopyTable(damageData) + end + table.remove(eventData.damageData, i) + elseif damageData.ElementType == DamageProxy.ElementType.ICE then + hasIceDamage = true + iceDamageIndex = i + end + end + + -- 第二步:处理NONE伤害值 + if noneDamageValue > 0 then + if hasIceDamage then + -- 如果存在ICE伤害,将NONE伤害值加到ICE伤害中 + eventData.damageData[iceDamageIndex].Damage = eventData.damageData[iceDamageIndex].Damage + noneDamageValue + else + -- 如果不存在ICE伤害,复制之前的伤害记录并修改为ICE类型 + if lastNoneDamageData then + lastNoneDamageData.ElementType = DamageProxy.ElementType.ICE + table.insert(eventData.damageData, lastNoneDamageData) + end + end + end + return eventData + end, self.TriggerSlot, self) +end + +function RuneNormalDamageToIce:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneNormalDamageToIce:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + return nil +end + +return RuneNormalDamageToIce \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RunePassiveAbilityAttackRate.luau b/src/ServerStorage/Modules/Runes/RunePassiveAbilityAttackRate.luau new file mode 100644 index 0000000..af58026 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RunePassiveAbilityAttackRate.luau @@ -0,0 +1,81 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) +local Rng = require(ReplicatedStorage.Tools.Rng) + +--> Json +local JsonAbility = require(ReplicatedStorage.Json.Ability) + +local RunePassiveAbilityAttackRate = {} +RunePassiveAbilityAttackRate.__index = RunePassiveAbilityAttackRate +setmetatable(RunePassiveAbilityAttackRate, {__index = Rune}) + + +function RunePassiveAbilityAttackRate:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RunePassiveAbilityAttackRate) + + return self +end + +function RunePassiveAbilityAttackRate:Check(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + local abilityFolder = pData:FindFirstChild("Ability") + if not abilityFolder then return nil end + local abilityList = abilityFolder:GetChildren() + if #abilityList == 0 then return nil end + + local wearingEquipmentList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + table.insert(wearingEquipmentList, equipment.Name) + end + end + + self.Data = nil + self.Data = {} + for _, ability in abilityList do + local wearingEquipment = ability:GetAttribute("wearing") + if wearingEquipment > 0 then + if table.find(wearingEquipmentList, wearingEquipment) then + table.insert(self.Data, ability) + end + end + end + + return true +end + +function RunePassiveAbilityAttackRate:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local banAbilityNumber = 0 + for _, ability in self.Data do + local abilityId = ability:GetAttribute("orgId") + local abilityData = Utils:GetIdDataFromJson(JsonAbility, abilityId) + if not abilityData.castType then + banAbilityNumber = banAbilityNumber + 1 + end + end + + local attackRate = math.floor((AttributesData.attackRate or 100) * banAbilityNumber * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RunePassiveAbilityAttackRate \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneRightTrigger.luau b/src/ServerStorage/Modules/Runes/RuneRightTrigger.luau new file mode 100644 index 0000000..3742825 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneRightTrigger.luau @@ -0,0 +1,33 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneRightTrigger = {} +RuneRightTrigger.__index = RuneRightTrigger +setmetatable(RuneRightTrigger, {__index = Rune}) + +function RuneRightTrigger:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneRightTrigger) + self.TriggerTime = 0 + + return self +end + +function RuneRightTrigger:Check(index: number, AttributesData: table, BehaviorNameList: table) + if self.TriggerSlot == #self.PlayerAI:GetRuneList() then return false end + if self.TriggerTime ~= 0 then return false end + return true +end + +function RuneRightTrigger:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + self.TriggerTime = self.TriggerTime + 1 + return self.TriggerSlot + 1 +end + +return RuneRightTrigger \ No newline at end of file diff --git a/src/ServerStorage/Proxy/DamageProxy.luau b/src/ServerStorage/Proxy/DamageProxy.luau index 318ad07..e286e3c 100644 --- a/src/ServerStorage/Proxy/DamageProxy.luau +++ b/src/ServerStorage/Proxy/DamageProxy.luau @@ -15,6 +15,11 @@ local Utils = require(ReplicatedStorage.Tools.Utils) --> Variables local TypeList = require(ServerStorage.Base.TypeList) +--> EventFilter +local EventFilter = require(ReplicatedStorage.Modules.EventFilter) +local EventFilterEnum = require(ReplicatedStorage.Data.EventFilterEnum) + + -- 伤害类型枚举 local DamageType = { NORMAL = "Normal", -- 普攻 @@ -307,6 +312,16 @@ function DamageProxy:Heal(Caster: TypeList.Character, Victim: TypeList.Character local actualHeal = math.min(Amount, MaxHealth - VictimHealth) local overheal = math.max(0, Amount - actualHeal) + + -- 发送攻击前事件 + EventFilter.FireGlobalWithFilter(EventFilterEnum.BEFORE_HEAL, { + caster = Caster, + victim = Victim, + amount = actualHeal, + }, function(processedData) + actualHeal = processedData.amount + end) + if actualHeal > 0 then Victim:ChangeAttributeValue("hp", VictimHealth + actualHeal) end diff --git a/src/ServerStorage/Proxy/EquipmentProxy.luau b/src/ServerStorage/Proxy/EquipmentProxy.luau index f60c482..c9656f7 100644 --- a/src/ServerStorage/Proxy/EquipmentProxy.luau +++ b/src/ServerStorage/Proxy/EquipmentProxy.luau @@ -341,9 +341,17 @@ function EquipmentProxy:RecycleEquipment(Player: Player, EquipmentId: number) -- GemProxy:RecycleGem(Player, EquipmentData.id) -- 回收装备返回金币 - -- TODO:处理关卡金币产出加成和词条加成 - local rewardData = {["1"] = EquipmentData.recycle} - PlayerInfoProxy:ChangeItemCount(Player, 1, EquipmentData.recycle) + + -- 处理关卡金币产出加成和词条加成 + local getNumber = EquipmentData.recycle + local coinBonus = Player.Character:FindFirstChild("Attributes"):GetAttribute("coinBonus") + if coinBonus then + getNumber = math.floor(getNumber * (1 + coinBonus / 100)) + end + + -- 返回金币 + local rewardData = {["1"] = getNumber} + PlayerInfoProxy:ChangeItemCount(Player, 1, getNumber) ArchiveProxy.pData[Player.UserId][STORE_NAME][EquipmentId] = nil diff --git a/src/ServerStorage/Proxy/LevelProxy.luau b/src/ServerStorage/Proxy/LevelProxy.luau index 9fa1d18..7f5d77a 100644 --- a/src/ServerStorage/Proxy/LevelProxy.luau +++ b/src/ServerStorage/Proxy/LevelProxy.luau @@ -117,6 +117,14 @@ local function OnMobDied(Player: Player, Mob: TypeList.Character) local mob_died_get = Utils:GetIdDataFromJson(JsonParam, 3) local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy) local getNumber = math.floor(mob_died_get["intArray"][2] + LevelProxy:GetLevelGetBonus(Player) / 100) + + -- 铁矿加成,读取属性加成 + local ironBonus = Player.Character:FindFirstChild("Attributes"):GetAttribute("ironBonus") + if ironBonus then + if mob_died_get["intArray"][1] == 2 then + getNumber = math.floor(getNumber * (1 + ironBonus / 100)) + end + end PlayerInfoProxy:ChangeItemCount(Player, mob_died_get["intArray"][1], getNumber) for _, mob in LevelProxy.pData[Player.UserId].Mobs do