diff --git a/excel/Rune.xlsx b/excel/Rune.xlsx index 7bed530..55b536c 100644 Binary files a/excel/Rune.xlsx and b/excel/Rune.xlsx differ diff --git a/excel/attribute.xlsx b/excel/attribute.xlsx index 141c8f2..ebf49af 100644 Binary files a/excel/attribute.xlsx and b/excel/attribute.xlsx differ diff --git a/excel/equipment.xlsx b/excel/equipment.xlsx index e1ec9c3..7c21589 100644 Binary files a/excel/equipment.xlsx and b/excel/equipment.xlsx differ diff --git a/src/ReplicatedStorage/Data/ServerSignalEnum.luau b/src/ReplicatedStorage/Data/ServerSignalEnum.luau new file mode 100644 index 0000000..25de2ba --- /dev/null +++ b/src/ReplicatedStorage/Data/ServerSignalEnum.luau @@ -0,0 +1,7 @@ +local ServerSignalEnum = {} + +ServerSignalEnum = { + SHOW_ABILITY = "SHOW_ABILITY", +} + +return ServerSignalEnum \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Equipment.json b/src/ReplicatedStorage/Json/Equipment.json index d096c0b..89ce288 100644 --- a/src/ReplicatedStorage/Json/Equipment.json +++ b/src/ReplicatedStorage/Json/Equipment.json @@ -1,18 +1,18 @@ [ -{"id":40000,"type":1,"name":40000,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"MCSword","specialType":2,"specialRequire":20001,"specialActive":[14,25],"recycle":100}, -{"id":40001,"type":1,"name":40001,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, -{"id":40002,"type":1,"name":40002,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, -{"id":40003,"type":1,"name":40003,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, -{"id":40004,"type":1,"name":40004,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, -{"id":40005,"type":1,"name":40005,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, -{"id":40006,"type":1,"name":40006,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, -{"id":40007,"type":1,"name":40007,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100}, -{"id":40008,"type":1,"name":40008,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":6,"specialActive":[14,25],"recycle":100}, -{"id":40009,"type":1,"name":40009,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, -{"id":40010,"type":1,"name":40010,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, -{"id":40011,"type":1,"name":40011,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, -{"id":40012,"type":1,"name":40012,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, -{"id":40013,"type":1,"name":40013,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, -{"id":40014,"type":1,"name":40014,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, -{"id":40015,"type":1,"name":40015,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100} +{"id":40000,"type":1,"subType":1,"name":40000,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"MCSword","specialType":2,"specialRequire":20001,"specialActive":[14,25],"recycle":100}, +{"id":40001,"type":1,"subType":2,"name":40001,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, +{"id":40002,"type":1,"subType":3,"name":40002,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, +{"id":40003,"type":1,"subType":4,"name":40003,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, +{"id":40004,"type":1,"subType":1,"name":40004,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, +{"id":40005,"type":1,"subType":2,"name":40005,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, +{"id":40006,"type":1,"subType":3,"name":40006,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, +{"id":40007,"type":1,"subType":4,"name":40007,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100}, +{"id":40008,"type":1,"subType":1,"name":40008,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":6,"specialActive":[14,25],"recycle":100}, +{"id":40009,"type":1,"subType":2,"name":40009,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, +{"id":40010,"type":1,"subType":3,"name":40010,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, +{"id":40011,"type":1,"subType":4,"name":40011,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, +{"id":40012,"type":1,"subType":1,"name":40012,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, +{"id":40013,"type":1,"subType":2,"name":40013,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, +{"id":40014,"type":1,"subType":3,"name":40014,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, +{"id":40015,"type":1,"subType":4,"name":40015,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Rune.json b/src/ReplicatedStorage/Json/Rune.json index 1b012ae..e8e16bd 100644 --- a/src/ReplicatedStorage/Json/Rune.json +++ b/src/ReplicatedStorage/Json/Rune.json @@ -1,5 +1,56 @@ [ {"id":60000,"quality":1,"type":1,"icon":1,"nameId":60000,"runeName":"RuneFireDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60001,"quality":1,"type":2,"icon":1,"nameId":60001,"runeName":"RuneIceDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60002,"quality":1,"type":3,"icon":1,"nameId":60002,"runeName":"RuneLightDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60003,"quality":1,"type":4,"icon":1,"nameId":60003,"runeName":"RuneShadowDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60004,"quality":1,"type":null,"icon":1,"nameId":60004,"runeName":"RuneBookQualityPurple","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60005,"quality":1,"type":null,"icon":1,"nameId":60005,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60006,"quality":1,"type":null,"icon":1,"nameId":60006,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60007,"quality":1,"type":null,"icon":1,"nameId":60007,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60008,"quality":1,"type":null,"icon":1,"nameId":60008,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60009,"quality":1,"type":null,"icon":1,"nameId":60009,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60010,"quality":1,"type":null,"icon":1,"nameId":60010,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60011,"quality":1,"type":null,"icon":1,"nameId":60011,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60012,"quality":1,"type":null,"icon":1,"nameId":60012,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60013,"quality":1,"type":null,"icon":1,"nameId":60013,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60014,"quality":1,"type":null,"icon":1,"nameId":60014,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60015,"quality":1,"type":null,"icon":1,"nameId":60015,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60016,"quality":1,"type":null,"icon":1,"nameId":60016,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60017,"quality":1,"type":null,"icon":1,"nameId":60017,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60018,"quality":1,"type":null,"icon":1,"nameId":60018,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60019,"quality":1,"type":null,"icon":1,"nameId":60019,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60020,"quality":1,"type":null,"icon":1,"nameId":60020,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60021,"quality":1,"type":null,"icon":1,"nameId":60021,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60022,"quality":1,"type":null,"icon":1,"nameId":60022,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60023,"quality":1,"type":null,"icon":1,"nameId":60023,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60024,"quality":1,"type":null,"icon":1,"nameId":60024,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, {"id":61000,"quality":2,"type":1,"icon":1,"nameId":61000,"runeName":"RuneIceCoffin","behaviorName":"IceCoffine","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":null,"behaviorName":null,"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":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/ReplicatedStorage/Modules/EventFilter.luau b/src/ReplicatedStorage/Modules/EventFilter.luau new file mode 100644 index 0000000..b662e3e --- /dev/null +++ b/src/ReplicatedStorage/Modules/EventFilter.luau @@ -0,0 +1,184 @@ +--[[ + EventFilter System - 自动回收版本 + 结合Signal和Filter实现事件数据过滤系统 + 支持事件触发时的数据修改和回调处理 + 自动回收机制,无需手动管理生命周期 +]] + +local Signal = require(script.Parent.Signal) +local Filter = require(script.Parent.Filter) + +-- 事件Filter处理器类型 +export type EventFilterHandler = { + handlerFunction: (eventData: any) -> any, + priority: number, + id: string, + eventName: string +} + +-- EventFilter系统类 +local EventFilter = {} +EventFilter.__index = EventFilter +EventFilter.ClassName = "EventFilter" + +export type EventFilter = typeof(EventFilter) + +-- 全局EventFilter实例(使用弱表自动回收) +local GlobalEventFilters = setmetatable({}, {__mode = "k"}) + +function EventFilter.new(name: string?): EventFilter + local eventFilter = setmetatable({ + Name = name or "GlobalEventFilter", + _signals = {}, -- 存储事件信号 + _filters = {}, -- 存储每个事件的Filter + _nextHandlerId = 1, + _weakRefs = setmetatable({}, {__mode = "k"}) -- 弱引用表 + }, EventFilter) + + if name then + GlobalEventFilters[eventFilter] = true + end + + return eventFilter +end + +-- 注册事件(创建Signal和对应的Filter) +function EventFilter:RegisterEvent(eventName: string): Signal + if not self._signals[eventName] then + self._signals[eventName] = Signal.new(eventName) + self._filters[eventName] = Filter.new(eventName .. "_Filter") + end + return self._signals[eventName] +end + +-- 订阅事件Filter(自动回收版本) +function EventFilter:SubscribeFilter(eventName: string, filterFunction: (eventData: any) -> any, priority: number?, owner: any?): string + -- 确保事件已注册 + self:RegisterEvent(eventName) + + local handlerId = tostring(self._nextHandlerId) + self._nextHandlerId = self._nextHandlerId + 1 + + -- 添加到Filter(带所有者引用) + local filterId = self._filters[eventName]:AddHandler(filterFunction, priority, owner) + + -- 如果提供了所有者,创建弱引用 + if owner then + self._weakRefs[owner] = {eventName = eventName, handlerId = handlerId} + end + + return handlerId +end + +-- 取消订阅事件Filter +function EventFilter:UnsubscribeFilter(eventName: string, handlerId: string): boolean + if self._filters[eventName] then + return self._filters[eventName]:RemoveHandler(handlerId) + end + return false +end + +-- 清理无效的处理器(自动回收) +function EventFilter:CleanupFilters() + for eventName, filter in pairs(self._filters) do + filter:CleanupHandlers() + end +end + +-- 发送事件(带Filter处理,自动清理) +function EventFilter:FireWithFilter(eventName: string, eventData: any, callback: (processedData: any) -> ()) + -- 确保事件已注册 + self:RegisterEvent(eventName) + + -- 自动清理无效处理器 + self:CleanupFilters() + + -- 通过Filter处理数据 + self._filters[eventName]:ProcessDataWithCallback(eventData, function(processedData) + -- 触发Signal事件 + self._signals[eventName]:Fire(processedData) + + -- 执行回调 + if callback then + callback(processedData) + end + end) +end + +-- 发送事件(不带Filter,直接触发) +function EventFilter:Fire(eventName: string, eventData: any) + -- 确保事件已注册 + self:RegisterEvent(eventName) + + -- 自动清理无效处理器 + self:CleanupFilters() + + -- 直接触发Signal事件 + self._signals[eventName]:Fire(eventData) +end + +-- 订阅事件(监听处理后的数据) +function EventFilter:Subscribe(eventName: string, callback: (processedData: any) -> ()): any + -- 确保事件已注册 + self:RegisterEvent(eventName) + + -- 订阅Signal事件 + return self._signals[eventName]:Connect(callback) +end + +-- 获取事件Signal +function EventFilter:GetSignal(eventName: string): Signal? + return self._signals[eventName] +end + +-- 获取事件Filter +function EventFilter:GetFilter(eventName: string): Filter? + return self._filters[eventName] +end + +-- 获取所有已注册的事件名称 +function EventFilter:GetRegisteredEvents(): {string} + local events = {} + for eventName, _ in pairs(self._signals) do + table.insert(events, eventName) + end + return events +end + +-- 便捷方法:获取全局EventFilter实例 +function EventFilter.GetGlobal(): EventFilter + for eventFilter, _ in pairs(GlobalEventFilters) do + if eventFilter.Name == "GlobalEventFilter" then + return eventFilter + end + end + + return EventFilter.new("GlobalEventFilter") +end + +-- 便捷方法:全局订阅Filter(带所有者) +function EventFilter.SubscribeGlobalFilter(eventName: string, filterFunction: (eventData: any) -> any, priority: number?, owner: any?): string + return EventFilter.GetGlobal():SubscribeFilter(eventName, filterFunction, priority, owner) +end + +-- 便捷方法:全局取消订阅Filter +function EventFilter.UnsubscribeGlobalFilter(eventName: string, handlerId: string): boolean + return EventFilter.GetGlobal():UnsubscribeFilter(eventName, handlerId) +end + +-- 便捷方法:全局发送事件(带Filter) +function EventFilter.FireGlobalWithFilter(eventName: string, eventData: any, callback: (processedData: any) -> ()) + EventFilter.GetGlobal():FireWithFilter(eventName, eventData, callback) +end + +-- 便捷方法:全局发送事件(不带Filter) +function EventFilter.FireGlobal(eventName: string, eventData: any) + EventFilter.GetGlobal():Fire(eventName, eventData) +end + +-- 便捷方法:全局订阅事件 +function EventFilter.SubscribeGlobal(eventName: string, callback: (processedData: any) -> ()): any + return EventFilter.GetGlobal():Subscribe(eventName, callback) +end + +return EventFilter diff --git a/src/ReplicatedStorage/Modules/EventFilterExample.luau b/src/ReplicatedStorage/Modules/EventFilterExample.luau new file mode 100644 index 0000000..8fc0ddd --- /dev/null +++ b/src/ReplicatedStorage/Modules/EventFilterExample.luau @@ -0,0 +1,230 @@ +--[[ + EventFilter使用例子 + 演示如何使用事件过滤系统 +]] + +local EventFilter = require(script.Parent.EventFilter) + +-- ===== 基础使用示例 ===== + +-- 1. 创建符文对象(作为所有者) +local function createRunes() + local fireRune = { + name = "火焰符文", + level = 5, + element = "fire" + } + + local iceRune = { + name = "冰霜符文", + level = 3, + element = "ice" + } + + local criticalRune = { + name = "暴击符文", + level = 4, + criticalChance = 0.3 + } + + -- 添加Destroy方法(用于自动回收) + function fireRune:Destroy() + print("销毁火焰符文") + for k, v in pairs(self) do self[k] = nil end + self = nil + end + + function iceRune:Destroy() + print("销毁冰霜符文") + for k, v in pairs(self) do self[k] = nil end + self = nil + end + + function criticalRune:Destroy() + print("销毁暴击符文") + for k, v in pairs(self) do self[k] = nil end + self = nil + end + + return fireRune, iceRune, criticalRune +end + +-- 2. 设置符文效果 +local function setupRuneEffects(fireRune, iceRune, criticalRune) + -- 火焰符文:增加50%伤害 + EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + print("🔥 火焰符文生效:伤害 +50%") + eventData.damage = eventData.damage * 1.5 + eventData.element = "fire" + return eventData + end, 10, fireRune) -- 优先级10,所有者是fireRune + + -- 冰霜符文:增加30%伤害,有概率冰冻 + EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + print("❄️ 冰霜符文生效:伤害 +30%") + eventData.damage = eventData.damage * 1.3 + eventData.element = "ice" + + -- 20%概率冰冻 + if math.random() < 0.2 then + eventData.freeze = true + print(" 💎 触发冰冻效果!") + end + + return eventData + end, 8, iceRune) -- 优先级8,所有者是iceRune + + -- 暴击符文:30%概率暴击 + EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + if math.random() < 0.3 then + print("⚡ 暴击符文生效:暴击!") + eventData.damage = eventData.damage * 2 + eventData.isCritical = true + end + return eventData + end, 5, criticalRune) -- 优先级5,所有者是criticalRune +end + +-- 3. 监听最终结果 +local function setupResultListener() + EventFilter.SubscribeGlobal("OnAttack", function(finalData) + print("=== 最终攻击结果 ===") + print("伤害:", finalData.damage) + print("元素:", finalData.element or "无") + print("暴击:", finalData.isCritical and "是" or "否") + print("冰冻:", finalData.freeze and "是" or "否") + print("==================") + end) +end + +-- 4. 发送攻击事件 +local function sendAttackEvent() + local attackData = { + damage = 100, + attacker = "玩家", + target = "敌人" + } + + print("🗡️ 发送攻击事件,基础伤害:", attackData.damage) + + EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(processedData) + print("✅ 攻击事件处理完成!") + -- 这里可以执行实际的攻击逻辑 + -- 比如:DealDamage(processedData.damage, processedData.target) + end) +end + +-- ===== 演示自动回收 ===== + +local function demonstrateAutoCleanup() + print("\n=== 演示自动回收 ===") + + -- 第一次攻击:所有符文生效 + print("\n第一次攻击(所有符文生效):") + sendAttackEvent() + + -- 销毁火焰符文 + print("\n销毁火焰符文...") + fireRune:Destroy() + + -- 第二次攻击:只有冰霜和暴击符文生效 + print("\n第二次攻击(冰霜+暴击符文生效):") + sendAttackEvent() + + -- 销毁冰霜符文 + print("\n销毁冰霜符文...") + iceRune:Destroy() + + -- 第三次攻击:只有暴击符文生效 + print("\n第三次攻击(只有暴击符文生效):") + sendAttackEvent() + + -- 销毁暴击符文 + print("\n销毁暴击符文...") + criticalRune:Destroy() + + -- 第四次攻击:没有符文生效 + print("\n第四次攻击(没有符文生效):") + sendAttackEvent() +end + +-- ===== 实际游戏场景示例 ===== + +local function gameSceneExample() + print("\n=== 实际游戏场景示例 ===") + + -- 模拟玩家攻击 + local function playerAttack() + local attackData = { + damage = 80, + attackType = "melee", + attacker = game.Players.LocalPlayer, + target = nil, -- 目标敌人 + weaponType = "sword" + } + + print("玩家发起攻击...") + + -- 发送攻击事件,让符文系统处理 + EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(finalData) + print("执行攻击:", finalData.damage, "点伤害") + + -- 应用元素效果 + if finalData.element == "fire" then + print("🔥 造成燃烧效果") + elseif finalData.element == "ice" then + print("❄️ 造成冰冻效果") + end + + -- 应用暴击效果 + if finalData.isCritical then + print("⚡ 暴击!") + end + + -- 应用冰冻效果 + if finalData.freeze then + print("💎 目标被冰冻!") + end + end) + end + + -- 执行攻击 + playerAttack() +end + +-- ===== 主函数:完整演示 ===== + +local function runCompleteExample() + print("=== EventFilter使用示例 ===") + + -- 1. 创建符文 + local fireRune, iceRune, criticalRune = createRunes() + print("创建了三个符文") + + -- 2. 设置符文效果 + setupRuneEffects(fireRune, iceRune, criticalRune) + print("设置了符文效果") + + -- 3. 设置结果监听 + setupResultListener() + print("设置了结果监听") + + -- 4. 演示自动回收 + demonstrateAutoCleanup() + + -- 5. 实际游戏场景 + gameSceneExample() + + print("\n=== 示例完成 ===") +end + +-- ===== 导出函数 ===== + +return { + runCompleteExample = runCompleteExample, + createRunes = createRunes, + setupRuneEffects = setupRuneEffects, + sendAttackEvent = sendAttackEvent, + demonstrateAutoCleanup = demonstrateAutoCleanup, + gameSceneExample = gameSceneExample +} diff --git a/src/ReplicatedStorage/Modules/EventFilterQuickStart.luau b/src/ReplicatedStorage/Modules/EventFilterQuickStart.luau new file mode 100644 index 0000000..970887a --- /dev/null +++ b/src/ReplicatedStorage/Modules/EventFilterQuickStart.luau @@ -0,0 +1,86 @@ +--[[ + EventFilter快速入门指南 + 最简单的使用方式 +]] + +local EventFilter = require(script.Parent.EventFilter) + +-- ===== 最简单的使用方式 ===== + +-- 1. 创建符文对象 +local myRune = { + name = "我的符文", + level = 1 +} + +-- 2. 添加Destroy方法(用于自动回收) +function myRune:Destroy() + print("销毁符文:", self.name) + for k, v in pairs(self) do self[k] = nil end + self = nil +end + +-- 3. 让符文订阅事件 +EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + print("符文生效:伤害 +50%") + eventData.damage = eventData.damage * 1.5 + return eventData +end, 10, myRune) -- 事件名, 处理函数, 优先级, 所有者 + +-- 4. 发送事件 +local attackData = {damage = 100} +EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(processedData) + print("最终伤害:", processedData.damage) -- 输出:150 +end) + +-- 5. 销毁符文(自动回收) +myRune:Destroy() -- Filter自动失效,不需要手动取消订阅 + +-- ===== 实际游戏中的使用 ===== + +-- 在攻击脚本中: +local function playerAttack() + local attackData = { + damage = 100, + attackType = "melee", + target = enemy + } + + -- 发送攻击事件,让符文系统处理 + EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(finalData) + -- 使用处理后的数据执行实际攻击 + DealDamage(finalData.damage, finalData.target) + end) +end + +-- 在符文脚本中: +local function setupRune(runeInstance) + -- 符文订阅攻击事件 + EventFilter.SubscribeGlobalFilter("OnAttack", function(data) + -- 符文效果:增加伤害 + data.damage = data.damage * 1.2 + return data + end, 5, runeInstance) +end + +-- ===== 常用事件类型 ===== +-- "OnAttack" - 攻击事件 +-- "OnMove" - 移动事件 +-- "OnSkill" - 技能事件 +-- "OnDamage" - 受伤事件 +-- "OnHeal" - 治疗事件 + +-- ===== 优先级说明 ===== +-- 数字越大,优先级越高,越先执行 +-- 10: 最高优先级(比如基础属性修改) +-- 5: 中等优先级(比如元素转换) +-- 1: 最低优先级(比如暴击判定) + +-- ===== 自动回收的好处 ===== +-- 1. 不需要手动管理:符文销毁时Filter自动失效 +-- 2. 内存安全:不会造成内存泄露 +-- 3. 代码简洁:不需要写清理代码 + +return { + -- 这里可以添加一些辅助函数 +} diff --git a/src/ReplicatedStorage/Modules/Filter.luau b/src/ReplicatedStorage/Modules/Filter.luau new file mode 100644 index 0000000..55b862e --- /dev/null +++ b/src/ReplicatedStorage/Modules/Filter.luau @@ -0,0 +1,178 @@ +--[[ + Filter System - 自动回收版本 + 用于实现事件数据的过滤和处理功能 + 支持多个Filter的链式处理、优先级排序和数据回调 + 自动回收机制,无需手动管理生命周期 +]] + +-- Filter处理器类型 +export type FilterHandler = { + handlerFunction: (data: any) -> any, + priority: number, + id: string, + weakRef: any -- 弱引用,用于自动回收 +} + +-- Filter系统类 +local Filter = {} +Filter.__index = Filter +Filter.ClassName = "Filter" + +export type Filter = typeof(Filter) + +-- 全局Filter实例(使用弱表自动回收) +local GlobalFilters = setmetatable({}, {__mode = "k"}) + +function Filter.new(name: string?): Filter + local filter = setmetatable({ + Name = name or "GlobalFilter", + _handlers = {}, -- 存储所有Filter处理器 + _nextHandlerId = 1, + _weakRefs = setmetatable({}, {__mode = "k"}) -- 弱引用表 + }, Filter) + + if name then + GlobalFilters[filter] = true + end + + return filter +end + +-- 添加Filter处理器(自动回收版本) +function Filter:AddHandler(handlerFunction: (data: any) -> any, priority: number?, owner: any?): string + local handlerId = tostring(self._nextHandlerId) + self._nextHandlerId = self._nextHandlerId + 1 + + local handler: FilterHandler = { + handlerFunction = handlerFunction, + priority = priority or 0, + id = handlerId, + weakRef = owner -- 存储所有者引用 + } + + table.insert(self._handlers, handler) + + -- 如果提供了所有者,创建弱引用 + if owner then + self._weakRefs[owner] = handlerId + end + + -- 按优先级排序(优先级高的先执行) + table.sort(self._handlers, function(a, b) + return a.priority > b.priority + end) + + return handlerId +end + +-- 移除Filter处理器 +function Filter:RemoveHandler(handlerId: string): boolean + for i, handler in ipairs(self._handlers) do + if handler.id == handlerId then + table.remove(self._handlers, i) + return true + end + end + return false +end + +-- 清理无效的处理器(自动回收) +function Filter:CleanupHandlers() + local validHandlers = {} + + for _, handler in ipairs(self._handlers) do + local isValid = true + + if handler.weakRef then + -- 检查Instance类型 + if typeof(handler.weakRef) == "Instance" then + -- Instance被销毁时Parent为nil + if handler.weakRef.Parent == nil then + isValid = false + end + else + -- 对于table类型(如Rune类),检查是否有Destroy方法且是否已被销毁 + if type(handler.weakRef) == "table" then + -- 检查是否还在弱引用表中(如果不在,说明已被垃圾回收) + if not self._weakRefs[handler.weakRef] then + isValid = false + end + end + end + end + + if isValid then + table.insert(validHandlers, handler) + end + end + + self._handlers = validHandlers +end + +-- 处理数据(链式Filter,自动清理) +function Filter:ProcessData(data: any): any + -- 自动清理无效处理器 + self:CleanupHandlers() + + local processedData = data + + for _, handler in ipairs(self._handlers) do + local success, result = pcall(handler.handlerFunction, processedData) + if success and result ~= nil then + processedData = result + end + end + + return processedData +end + +-- 带回调的数据处理(用于事件系统) +function Filter:ProcessDataWithCallback(data: any, callback: (processedData: any) -> ()) + local processedData = self:ProcessData(data) + callback(processedData) +end + +-- 获取处理器数量 +function Filter:GetHandlerCount(): number + self:CleanupHandlers() + return #self._handlers +end + +-- 获取所有处理器信息 +function Filter:GetHandlers(): {FilterHandler} + self:CleanupHandlers() + return self._handlers +end + +-- 便捷方法:创建全局Filter实例 +function Filter.GetGlobal(): Filter + for filter, _ in pairs(GlobalFilters) do + if filter.Name == "GlobalFilter" then + return filter + end + end + + return Filter.new("GlobalFilter") +end + +-- 便捷方法:快速添加处理器(带所有者) +function Filter.AddGlobalHandler(handlerFunction: (data: any) -> any, priority: number?, owner: any?): string + return Filter.GetGlobal():AddHandler(handlerFunction, priority, owner) +end + +-- 便捷方法:快速移除处理器 +function Filter.RemoveGlobalHandler(handlerId: string): boolean + return Filter.GetGlobal():RemoveHandler(handlerId) +end + +-- 便捷方法:快速处理数据 +function Filter.ProcessGlobalData(data: any): any + return Filter.GetGlobal():ProcessData(data) +end + +-- 便捷方法:快速处理数据带回调 +function Filter.ProcessGlobalDataWithCallback(data: any, callback: (processedData: any) -> ()) + Filter.GetGlobal():ProcessDataWithCallback(data, callback) +end + +return Filter diff --git a/src/ReplicatedStorage/Tools/Rng.luau b/src/ReplicatedStorage/Tools/Rng.luau index a2f0ccb..f835e42 100644 --- a/src/ReplicatedStorage/Tools/Rng.luau +++ b/src/ReplicatedStorage/Tools/Rng.luau @@ -154,6 +154,12 @@ function Random:GetRandomInt(min: number, max: number) return math.random(min, max) end +-- 百分比概率随机 +function Random:RandomPercent(percent: number) + -- 随机1-100,如果随机值小于等于百分比,则返回true,否则返回false + local randomValue = self:GetRandomInt(1, 100) + return randomValue <= percent +end -- 初始化随机种子 diff --git a/src/ServerStorage/Modules/Behaviours/Attack.luau b/src/ServerStorage/Modules/Behaviours/Attack.luau index 092c555..958edaf 100644 --- a/src/ServerStorage/Modules/Behaviours/Attack.luau +++ b/src/ServerStorage/Modules/Behaviours/Attack.luau @@ -10,6 +10,7 @@ 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 Rng = require(ReplicatedStorage.Tools.Rng) -------------------------------------------------------------------------------- @@ -20,11 +21,11 @@ setmetatable(Attack, {__index = Behaviour}) local CAST_DISTANCE = 8 local COOLDOWN = 1 local ATTRIBUTE_LIST = { - {Name = "attack", ElementType = DamageProxy.ElementType.NONE}, - {Name = "fireAtk", ElementType = DamageProxy.ElementType.FIRE}, - {Name = "iceAtk", ElementType = DamageProxy.ElementType.ICE}, - {Name = "lightAtk", ElementType = DamageProxy.ElementType.LIGHT}, - {Name = "shadowAtk", ElementType = DamageProxy.ElementType.SHADOW}, + {Name = "attack", ElementType = DamageProxy.ElementType.NONE, CritCheckRateName = "critRate", CritDamageRateName = "critDamageRate"}, + {Name = "fireAtk", ElementType = DamageProxy.ElementType.FIRE, CritCheckRateName = "critRateFire", CritDamageRateName = "critDamageRateFire"}, + {Name = "iceAtk", ElementType = DamageProxy.ElementType.ICE, CritCheckRateName = "critRateIce", CritDamageRateName = "critDamageRateIce"}, + {Name = "lightAtk", ElementType = DamageProxy.ElementType.LIGHT, CritCheckRateName = "critRateLight", CritDamageRateName = "critDamageRateLight"}, + {Name = "shadowAtk", ElementType = DamageProxy.ElementType.SHADOW, CritCheckRateName = "critRateShadow", CritDamageRateName = "critDamageRateShadow"}, } @@ -95,17 +96,28 @@ function Attack:Execute() }) -- 攻击前摇 task.wait(atkSpeed) - - -- TODO: 之后这里可以提前做暴击判定 - + -- 伤害逻辑计算部分 local damageData = {} for _, attribute in ATTRIBUTE_LIST do local attributeValue = self:GetAttributeValue(attribute.Name) if attributeValue then + + -- 暴击判定 + local DamageType = DamageProxy.DamageType.NORMAL + local critCheckRate = self:GetAttributeValue(attribute.CritCheckRateName) or 0 + local critDamageRate = self:GetAttributeValue(attribute.CritDamageRateName) or 0 + if critCheckRate then + if Rng:RandomPercent(critCheckRate) then + attributeValue = attributeValue * (1 + (200 + critDamageRate) / 100) + DamageType = DamageProxy.DamageType.CRIT + end + end + + -- 记录伤害数据 table.insert(damageData, { Damage = attributeValue, - Type = DamageProxy.DamageType.NORMAL, + Type = DamageType, Tag = DamageProxy.DamageTag.NORMAL, ElementType = attribute.ElementType, }) diff --git a/src/ServerStorage/Modules/Runes/RuneBookQualityPurple.luau b/src/ServerStorage/Modules/Runes/RuneBookQualityPurple.luau new file mode 100644 index 0000000..7068627 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneBookQualityPurple.luau @@ -0,0 +1,51 @@ +--> 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 RuneBookQualityPurple = {} +RuneBookQualityPurple.__index = RuneBookQualityPurple +setmetatable(RuneBookQualityPurple, {__index = Rune}) + + +function RuneBookQualityPurple:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneBookQualityPurple) + + return self +end + +function RuneBookQualityPurple:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneBookQualityPurple:OnExecute(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 bookFolder = pData:FindFirstChild("Book") + if not bookFolder then return nil end + local bookList = bookFolder:GetChildren() + if #bookList == 0 then return nil end + + local qualityNumber = 0 + for _, book in bookList do + local bookQuality = book:GetAttribute("quality") + if bookQuality >= 3 then + qualityNumber = qualityNumber + 1 + end + end + local attackRate = math.floor(qualityNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneBookQualityPurple \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateFire.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateFire.luau new file mode 100644 index 0000000..433bf77 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateFire.luau @@ -0,0 +1,34 @@ +--> 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 RuneCritDamageRateFire = {} +RuneCritDamageRateFire.__index = RuneCritDamageRateFire +setmetatable(RuneCritDamageRateFire, {__index = Rune}) + + +function RuneCritDamageRateFire:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateFire) + + return self +end + +function RuneCritDamageRateFire:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateFire:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateFire or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateFire", addAttribute) + return nil +end + + +return RuneCritDamageRateFire \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateIce.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateIce.luau new file mode 100644 index 0000000..ee3591b --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateIce.luau @@ -0,0 +1,34 @@ +--> 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 RuneCritDamageRateIce = {} +RuneCritDamageRateIce.__index = RuneCritDamageRateIce +setmetatable(RuneCritDamageRateIce, {__index = Rune}) + + +function RuneCritDamageRateIce:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateIce) + + return self +end + +function RuneCritDamageRateIce:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateIce:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateIce or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateIce", addAttribute) + return nil +end + + +return RuneCritDamageRateIce \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateLight.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateLight.luau new file mode 100644 index 0000000..291f0d9 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateLight.luau @@ -0,0 +1,34 @@ +--> 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 RuneCritDamageRateLight = {} +RuneCritDamageRateLight.__index = RuneCritDamageRateLight +setmetatable(RuneCritDamageRateLight, {__index = Rune}) + + +function RuneCritDamageRateLight:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateLight) + + return self +end + +function RuneCritDamageRateLight:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateLight:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateLight or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateLight", addAttribute) + return nil +end + + +return RuneCritDamageRateLight \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateShadow.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateShadow.luau new file mode 100644 index 0000000..b13ab42 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateShadow.luau @@ -0,0 +1,34 @@ +--> 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 RuneCritDamageRateShadow = {} +RuneCritDamageRateShadow.__index = RuneCritDamageRateShadow +setmetatable(RuneCritDamageRateShadow, {__index = Rune}) + + +function RuneCritDamageRateShadow:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateShadow) + + return self +end + +function RuneCritDamageRateShadow:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateShadow:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateShadow or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateShadow", addAttribute) + return nil +end + + +return RuneCritDamageRateShadow \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneFireDamage.luau b/src/ServerStorage/Modules/Runes/RuneFireDamage.luau index a19216f..31aadaa 100644 --- a/src/ServerStorage/Modules/Runes/RuneFireDamage.luau +++ b/src/ServerStorage/Modules/Runes/RuneFireDamage.luau @@ -24,8 +24,9 @@ function RuneFireDamage:Check(index: number, AttributesData: table, BehaviorName end function RuneFireDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) - print("RuneFireDamage:OnExecute", index, AttributesData, BehaviorNameList) - Utils:TableSafeAddValue(AttributesData, "fireAtk", 100) + local baseAttribute = AttributesData.fireAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "fireAtk", addAttribute) return nil end diff --git a/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau b/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau index 4a40c2e..ebf2f9e 100644 --- a/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau +++ b/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau @@ -24,7 +24,6 @@ function RuneIceCoffin:Check(index: number, AttributesData: table, BehaviorNameL end function RuneIceCoffin:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) - print("RuneIceCoffin:OnExecute", index, AttributesData, BehaviorNameList) local maxRecover = self.PlayerAI:GetSharedData("IceCoffin_MaxRecover") if maxRecover then self.PlayerAI:SetSharedData("IceCoffin_MaxRecover", maxRecover + 60) diff --git a/src/ServerStorage/Modules/Runes/RuneIceDamage.luau b/src/ServerStorage/Modules/Runes/RuneIceDamage.luau new file mode 100644 index 0000000..a223c64 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneIceDamage.luau @@ -0,0 +1,34 @@ +--> 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 RuneIceDamage = {} +RuneIceDamage.__index = RuneIceDamage +setmetatable(RuneIceDamage, {__index = Rune}) + + +function RuneIceDamage:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneIceDamage) + + return self +end + +function RuneIceDamage:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneIceDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.iceAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "iceAtk", addAttribute) + return nil +end + + +return RuneIceDamage \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneLightDamage.luau b/src/ServerStorage/Modules/Runes/RuneLightDamage.luau new file mode 100644 index 0000000..f3231f9 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneLightDamage.luau @@ -0,0 +1,34 @@ +--> 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 RuneLightDamage = {} +RuneLightDamage.__index = RuneLightDamage +setmetatable(RuneLightDamage, {__index = Rune}) + + +function RuneLightDamage:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneLightDamage) + + return self +end + +function RuneLightDamage:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneLightDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.lightAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "lightAtk", addAttribute) + return nil +end + + +return RuneLightDamage \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneShadowDamage.luau b/src/ServerStorage/Modules/Runes/RuneShadowDamage.luau new file mode 100644 index 0000000..6887a46 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneShadowDamage.luau @@ -0,0 +1,34 @@ +--> 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 RuneShadowDamage = {} +RuneShadowDamage.__index = RuneShadowDamage +setmetatable(RuneShadowDamage, {__index = Rune}) + + +function RuneShadowDamage:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneShadowDamage) + + return self +end + +function RuneShadowDamage:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneShadowDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.shadowAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "shadowAtk", addAttribute) + return nil +end + + +return RuneShadowDamage \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearElementAttack.luau b/src/ServerStorage/Modules/Runes/RuneWearElementAttack.luau new file mode 100644 index 0000000..6d4252b --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearElementAttack.luau @@ -0,0 +1,52 @@ +--> 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 RuneWearElementAttack = {} +RuneWearElementAttack.__index = RuneWearElementAttack +setmetatable(RuneWearElementAttack, {__index = Rune}) + + +function RuneWearElementAttack:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearElementAttack) + + return self +end + +function RuneWearElementAttack:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearElementAttack:OnExecute(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 elementNumber = 0 + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + elementNumber = elementNumber + #equipment:FindFirstChild("Element"):GetAttributes() + end + end + + local attackRate = math.floor(elementNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearElementAttack \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearEmptySlot.luau b/src/ServerStorage/Modules/Runes/RuneWearEmptySlot.luau new file mode 100644 index 0000000..6c4508a --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearEmptySlot.luau @@ -0,0 +1,70 @@ +--> 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 RuneWearEmptySlot = {} +RuneWearEmptySlot.__index = RuneWearEmptySlot +setmetatable(RuneWearEmptySlot, {__index = Rune}) + + +function RuneWearEmptySlot:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearEmptySlot) + + return self +end + +function RuneWearEmptySlot:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearEmptySlot:OnExecute(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 runeFolder = pData:FindFirstChild("Rune") + if not runeFolder then return nil end + local runeList = runeFolder:GetChildren() + if #runeList == 0 then return nil end + + + local maxRuneNumber = 0 + local hasSlotNumber = 0 + local wearWeaponList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + maxRuneNumber = maxRuneNumber + 1 + table.insert(wearWeaponList, equipment.Name) + end + end + + for _, rune in runeList do + local runeWearing = rune:GetAttribute("wearing") + if runeWearing then + if table.find(wearWeaponList, tostring(runeWearing)) then + hasSlotNumber = hasSlotNumber + 1 + end + end + end + + local attackRate = math.floor((maxRuneNumber - hasSlotNumber) * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearEmptySlot \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearFillSlot.luau b/src/ServerStorage/Modules/Runes/RuneWearFillSlot.luau new file mode 100644 index 0000000..ae8085a --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearFillSlot.luau @@ -0,0 +1,70 @@ +--> 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 RuneWearFillSlot = {} +RuneWearFillSlot.__index = RuneWearFillSlot +setmetatable(RuneWearFillSlot, {__index = Rune}) + + +function RuneWearFillSlot:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearFillSlot) + + return self +end + +function RuneWearFillSlot:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearFillSlot:OnExecute(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 runeFolder = pData:FindFirstChild("Rune") + if not runeFolder then return nil end + local runeList = runeFolder:GetChildren() + if #runeList == 0 then return nil end + + + local maxRuneNumber = 0 + local hasSlotNumber = 0 + local wearWeaponList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + maxRuneNumber = maxRuneNumber + 1 + table.insert(wearWeaponList, equipment.Name) + end + end + + for _, rune in runeList do + local runeWearing = rune:GetAttribute("wearing") + if runeWearing then + if table.find(wearWeaponList, tostring(runeWearing)) then + hasSlotNumber = hasSlotNumber + 1 + end + end + end + + local attackRate = math.floor(hasSlotNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearFillSlot \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearHeavySword.luau b/src/ServerStorage/Modules/Runes/RuneWearHeavySword.luau new file mode 100644 index 0000000..4fe1976 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearHeavySword.luau @@ -0,0 +1,60 @@ +--> 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 RuneWearHeavySword = {} +RuneWearHeavySword.__index = RuneWearHeavySword +setmetatable(RuneWearHeavySword, {__index = Rune}) + + +function RuneWearHeavySword:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearHeavySword) + + return self +end + +function RuneWearHeavySword:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearHeavySword:OnExecute(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 CheckSubType = 4 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagHeavySword" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearHeavySword \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearKnife.luau b/src/ServerStorage/Modules/Runes/RuneWearKnife.luau new file mode 100644 index 0000000..c930bba --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearKnife.luau @@ -0,0 +1,60 @@ +--> 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 RuneWearKnife = {} +RuneWearKnife.__index = RuneWearKnife +setmetatable(RuneWearKnife, {__index = Rune}) + + +function RuneWearKnife:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearKnife) + + return self +end + +function RuneWearKnife:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearKnife:OnExecute(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 CheckSubType = 1 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagKnife" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearKnife \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearStick.luau b/src/ServerStorage/Modules/Runes/RuneWearStick.luau new file mode 100644 index 0000000..ecdcf39 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearStick.luau @@ -0,0 +1,60 @@ +--> 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 RuneWearStick = {} +RuneWearStick.__index = RuneWearStick +setmetatable(RuneWearStick, {__index = Rune}) + + +function RuneWearStick:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearStick) + + return self +end + +function RuneWearStick:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearStick:OnExecute(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 CheckSubType = 3 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagStick" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearStick \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearSword.luau b/src/ServerStorage/Modules/Runes/RuneWearSword.luau new file mode 100644 index 0000000..7a49424 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearSword.luau @@ -0,0 +1,60 @@ +--> 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 RuneWearSword = {} +RuneWearSword.__index = RuneWearSword +setmetatable(RuneWearSword, {__index = Rune}) + + +function RuneWearSword:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearSword) + + return self +end + +function RuneWearSword:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearSword:OnExecute(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 CheckSubType = 2 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagSword" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearSword \ No newline at end of file