更新
This commit is contained in:
parent
fdc6ee6a6e
commit
2c43aba4bf
147
RUNE_STATE_WINDOW_README.md
Normal file
147
RUNE_STATE_WINDOW_README.md
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# RuneStateWindow 符文状态窗口
|
||||||
|
|
||||||
|
## 📋 功能概述
|
||||||
|
|
||||||
|
RuneStateWindow是一个用于显示当前穿戴符文状态的UI窗口,主要功能包括:
|
||||||
|
|
||||||
|
1. **符文栏显示** - 根据当前穿戴装备显示符文列表
|
||||||
|
2. **小丑牌动画** - 接收服务端符文执行记录后播放类似小丑牌的动画效果
|
||||||
|
|
||||||
|
## 🏗️ 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/StarterPlayerScripts/UI/Windows/RuneStateWindow/
|
||||||
|
├── init.luau # 主窗口文件
|
||||||
|
└── RuneShow.luau # 符文显示组件
|
||||||
|
|
||||||
|
src/StarterPlayerScripts/ClientMain/
|
||||||
|
├── RuneExecutionClient.luau # 客户端接收符文执行记录
|
||||||
|
└── TestRuneStateWindow.luau # 测试脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎮 使用方法
|
||||||
|
|
||||||
|
### 1. 打开符文状态窗口
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- 通过UIManager打开窗口
|
||||||
|
UIManager:OpenWindow("RuneStateWindow", {
|
||||||
|
runeData = {
|
||||||
|
{
|
||||||
|
runeName = "RuneFireDamage",
|
||||||
|
runeUniqueId = 1,
|
||||||
|
level = 5,
|
||||||
|
icon = "fire_icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
runeName = "RuneIceCoffin",
|
||||||
|
runeUniqueId = 2,
|
||||||
|
level = 3,
|
||||||
|
icon = "ice_icon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 接收符文执行记录
|
||||||
|
|
||||||
|
窗口会自动接收服务端发送的符文执行记录,并播放动画:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- 服务端发送的数据格式
|
||||||
|
local executionRecords = {
|
||||||
|
{
|
||||||
|
runeName = "RuneFireDamage",
|
||||||
|
runeUniqueId = 1,
|
||||||
|
index = 1,
|
||||||
|
timestamp = tick(),
|
||||||
|
action = "Execute",
|
||||||
|
attributesBefore = {attack = 100, hp = 1000},
|
||||||
|
attributesAfter = {attack = 150, hp = 1000},
|
||||||
|
behaviorListBefore = {"Attack"},
|
||||||
|
behaviorListAfter = {"Attack", "FireDamage"},
|
||||||
|
nextIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 测试功能
|
||||||
|
|
||||||
|
使用测试脚本可以快速验证功能:
|
||||||
|
|
||||||
|
- **按 R 键** - 打开符文状态窗口
|
||||||
|
- **按 T 键** - 模拟接收符文执行记录
|
||||||
|
- **按 C 键** - 关闭符文状态窗口
|
||||||
|
|
||||||
|
## 🎨 动画效果
|
||||||
|
|
||||||
|
### 小丑牌动画特点
|
||||||
|
|
||||||
|
1. **金色光效** - 符文激活时显示金色发光效果
|
||||||
|
2. **缩放动画** - 符文图标会放大并旋转
|
||||||
|
3. **粒子效果** - 中心产生黄色粒子扩散效果
|
||||||
|
4. **顺序播放** - 多个符文按执行顺序依次播放动画
|
||||||
|
|
||||||
|
### 动画参数
|
||||||
|
|
||||||
|
- **动画时长**: 0.3秒
|
||||||
|
- **缩放比例**: 1.5倍
|
||||||
|
- **旋转角度**: 360度
|
||||||
|
- **颜色**: 金色(#FFD700)和黄色(#FFFF00)
|
||||||
|
|
||||||
|
## 🔧 技术实现
|
||||||
|
|
||||||
|
### 核心组件
|
||||||
|
|
||||||
|
1. **RuneStateWindow** - 主窗口管理
|
||||||
|
- 管理符文执行记录队列
|
||||||
|
- 控制动画播放顺序
|
||||||
|
- 处理窗口生命周期
|
||||||
|
|
||||||
|
2. **RuneShow** - 符文显示组件
|
||||||
|
- 显示符文图标、名称、等级
|
||||||
|
- 处理符文激活动画
|
||||||
|
- 管理组件生命周期
|
||||||
|
|
||||||
|
3. **RuneExecutionClient** - 客户端接收器
|
||||||
|
- 监听服务端符文执行记录
|
||||||
|
- 转发数据到窗口组件
|
||||||
|
|
||||||
|
### 数据流程
|
||||||
|
|
||||||
|
```
|
||||||
|
服务端符文执行 → RemoteEvent发送 → 客户端接收 → 窗口处理 → 动画播放
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 扩展功能
|
||||||
|
|
||||||
|
### 可扩展的动画效果
|
||||||
|
|
||||||
|
1. **音效支持** - 添加符文激活音效
|
||||||
|
2. **特效增强** - 添加更多视觉特效
|
||||||
|
3. **交互功能** - 点击符文查看详情
|
||||||
|
4. **状态显示** - 显示符文冷却时间
|
||||||
|
|
||||||
|
### 自定义动画
|
||||||
|
|
||||||
|
可以通过修改`CreateJokerCardEffect`方法来自定义动画效果:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function RuneStateWindow:CreateJokerCardEffect(runeItem, record)
|
||||||
|
-- 自定义动画逻辑
|
||||||
|
-- 可以添加更多特效、音效等
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐛 注意事项
|
||||||
|
|
||||||
|
1. **UI预制体** - 需要创建对应的UI预制体`ui_w_rune_state`
|
||||||
|
2. **符文数据** - 需要正确配置符文JSON数据
|
||||||
|
3. **图标资源** - 需要上传对应的符文图标资源
|
||||||
|
4. **性能优化** - 大量符文时注意动画性能
|
||||||
|
|
||||||
|
## 📝 更新日志
|
||||||
|
|
||||||
|
- **v1.0.0** - 初始版本,支持基础符文显示和小丑牌动画
|
||||||
|
- 支持符文执行记录接收和播放
|
||||||
|
- 支持测试脚本快速验证功能
|
BIN
excel/Rune.xlsx
BIN
excel/Rune.xlsx
Binary file not shown.
Binary file not shown.
@ -1,4 +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":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":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}
|
||||||
]
|
]
|
@ -30,5 +30,5 @@
|
|||||||
{"id":53,"type":1,"specialType":null,"effectAttribute":"elementNumber","battleValue":[1,10],"iconId":29,"nameId":253},
|
{"id":53,"type":1,"specialType":null,"effectAttribute":"elementNumber","battleValue":[1,10],"iconId":29,"nameId":253},
|
||||||
{"id":54,"type":1,"specialType":null,"effectAttribute":"elementDefNumber","battleValue":[1,10],"iconId":30,"nameId":254},
|
{"id":54,"type":1,"specialType":null,"effectAttribute":"elementDefNumber","battleValue":[1,10],"iconId":30,"nameId":254},
|
||||||
{"id":55,"type":1,"specialType":null,"effectAttribute":"gemNumber","battleValue":[1,10],"iconId":31,"nameId":255},
|
{"id":55,"type":1,"specialType":null,"effectAttribute":"gemNumber","battleValue":[1,10],"iconId":31,"nameId":255},
|
||||||
{"id":56,"type":1,"specialType":null,"effectAttribute":"runeNumber","battleValue":[1,10],"iconId":31,"nameId":256}
|
{"id":56,"type":1,"specialType":null,"effectAttribute":"runeNumber","battleValue":[1,10],"iconId":32,"nameId":256}
|
||||||
]
|
]
|
@ -1,5 +1,5 @@
|
|||||||
[
|
[
|
||||||
{"id":60000,"quality":1,"type":1,"icon":1,"nameId":60000,"runeName":"RuneFireDamage","behaviorName":null,"recycle":[],"isInPool":1},
|
{"id":60000,"quality":1,"type":1,"icon":1,"nameId":60000,"runeName":"RuneFireDamage","behaviorName":null,"recycle":[],"isInPool":1},
|
||||||
{"id":61000,"quality":2,"type":1,"icon":1,"nameId":61000,"runeName":"RuneIceCoffin","behaviorName":"IceCoffin","recycle":[],"isInPool":1},
|
{"id":61000,"quality":2,"type":1,"icon":1,"nameId":61000,"runeName":"RuneIceCoffin","behaviorName":"IceCoffine","recycle":[],"isInPool":1},
|
||||||
{"id":62000,"quality":3,"type":1,"icon":1,"nameId":62000,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}
|
{"id":62000,"quality":3,"type":1,"icon":1,"nameId":62000,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}
|
||||||
]
|
]
|
@ -60,6 +60,14 @@ local Temporary = CreateFolder("Temporary", workspace)
|
|||||||
local ProjectileCache = CreateFolder("ProjectileCache", Temporary)
|
local ProjectileCache = CreateFolder("ProjectileCache", Temporary)
|
||||||
local Characters = CreateFolder("Characters", workspace)
|
local Characters = CreateFolder("Characters", workspace)
|
||||||
|
|
||||||
|
-- 初始化ReplicatedStorage目录
|
||||||
|
local EventsFolder = CreateFolder("Events", ReplicatedStorage)
|
||||||
|
|
||||||
|
-- 创建符文执行记录RemoteEvent
|
||||||
|
local RE_RuneExecutionRecord = Instance.new("RemoteEvent")
|
||||||
|
RE_RuneExecutionRecord.Name = "RE_RuneExecutionRecord"
|
||||||
|
RE_RuneExecutionRecord.Parent = EventsFolder
|
||||||
|
|
||||||
-- Initially require all server-sided & shared modules
|
-- Initially require all server-sided & shared modules
|
||||||
-- 加载模块 ReplicatedStorage.Modules, ServerStorage.Modules
|
-- 加载模块 ReplicatedStorage.Modules, ServerStorage.Modules
|
||||||
for _, Location in {ReplicatedStorage.Modules, ServerStorage.Modules} do
|
for _, Location in {ReplicatedStorage.Modules, ServerStorage.Modules} do
|
||||||
|
@ -7,20 +7,55 @@ local ServerStorage = game:GetService("ServerStorage")
|
|||||||
|
|
||||||
--> Dependencies
|
--> Dependencies
|
||||||
local TypeList = require(ServerStorage.Base.TypeList)
|
local TypeList = require(ServerStorage.Base.TypeList)
|
||||||
local Communicate = require(ServerStorage.Modules.Tools.Communicate)
|
|
||||||
local Utils = require(ReplicatedStorage.Tools.Utils)
|
local Utils = require(ReplicatedStorage.Tools.Utils)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function Rune:Init(PlayerAI: Player, Character: TypeList.Character, ScriptName: string)
|
function Rune:Init(PlayerAI: Player, Character: TypeList.Character, ScriptName: string)
|
||||||
local self = {}
|
local self = {}
|
||||||
setmetatable(self, Rune)
|
setmetatable(self, Rune)
|
||||||
self.PlayerAI = PlayerAI
|
self.PlayerAI = PlayerAI
|
||||||
self.Character = Character
|
self.Character = Character
|
||||||
self.ScriptName = ScriptName
|
self.ScriptName = ScriptName
|
||||||
|
self.Player = self.Character.Player
|
||||||
self.TriggerSlot = 0
|
self.TriggerSlot = 0
|
||||||
self.WearingSlot = 0
|
self.WearingSlot = 0
|
||||||
self.TriggerTime = 0
|
self.TriggerTime = 0
|
||||||
|
self.executionRecords = {}
|
||||||
|
self.isRecording = false -- 记录开关
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 开始记录
|
||||||
|
function Rune:StartRecording()
|
||||||
|
self.isRecording = true
|
||||||
|
self.executionRecords = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 停止记录
|
||||||
|
function Rune:StopRecording()
|
||||||
|
self.isRecording = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 记录执行信息
|
||||||
|
function Rune:RecordExecution(index: number, attributesBefore: table?, attributesAfter: table?, behaviorListBefore: table?, behaviorListAfter: table?, nextIndex: number?)
|
||||||
|
if not self.isRecording then return end
|
||||||
|
|
||||||
|
table.insert(self.executionRecords, {
|
||||||
|
runeName = self.ScriptName,
|
||||||
|
runeUniqueId = self.TriggerSlot,
|
||||||
|
index = index,
|
||||||
|
timestamp = tick(),
|
||||||
|
action = "Execute",
|
||||||
|
attributesBefore = attributesBefore or {},
|
||||||
|
attributesAfter = attributesAfter or {},
|
||||||
|
behaviorListBefore = behaviorListBefore or {},
|
||||||
|
behaviorListAfter = behaviorListAfter or {},
|
||||||
|
nextIndex = nextIndex
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取执行记录
|
||||||
|
function Rune:GetExecutionRecords()
|
||||||
|
return self.executionRecords
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 触发最开始事件
|
-- 触发最开始事件
|
||||||
@ -62,14 +97,25 @@ function Rune:Check(index: number, AttributesData: table?, BehaviorNameList: tab
|
|||||||
-- index: 当前符文在列表中的位置
|
-- index: 当前符文在列表中的位置
|
||||||
-- AttributesData: 属性数据,符文可以修改
|
-- AttributesData: 属性数据,符文可以修改
|
||||||
-- BehaviorNameList: 行为名称列表,符文可以修改
|
-- BehaviorNameList: 行为名称列表,符文可以修改
|
||||||
return false -- 默认返回false,表示不可以执行
|
-- 返回值: true表示可以执行,false表示跳过
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 执行事件
|
-- 执行事件
|
||||||
function Rune:Execute(index: number, AttributesData: table?, BehaviorNameList: table?)
|
function Rune:Execute(index: number, AttributesData: table?, BehaviorNameList: table?)
|
||||||
-- 使用PlayerAI的专用方法,性能更好
|
-- index: 当前符文在列表中的位置
|
||||||
|
-- AttributesData: 属性数据,符文可以修改
|
||||||
|
-- BehaviorNameList: 行为名称列表,符文可以修改
|
||||||
|
-- 返回值: 下一个要执行的index,或者nil表示继续下一个
|
||||||
|
|
||||||
|
-- 记录执行前的状态
|
||||||
|
local attributesBefore = Utils:DeepCopyTable(AttributesData or {})
|
||||||
|
local behaviorListBefore = Utils:DeepCopyTable(BehaviorNameList or {})
|
||||||
|
|
||||||
|
-- 使用PlayerAI的专用方法
|
||||||
local beginNextIndex = self.PlayerAI:TriggerAllRunesExcept(self, "OnTriggerBeginEvent", index, AttributesData, BehaviorNameList)
|
local beginNextIndex = self.PlayerAI:TriggerAllRunesExcept(self, "OnTriggerBeginEvent", index, AttributesData, BehaviorNameList)
|
||||||
if type(beginNextIndex) == "number" then
|
if type(beginNextIndex) == "number" then
|
||||||
|
self:RecordExecution(index, attributesBefore, AttributesData, behaviorListBefore, BehaviorNameList, beginNextIndex)
|
||||||
return beginNextIndex
|
return beginNextIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -77,32 +123,35 @@ function Rune:Execute(index: number, AttributesData: table?, BehaviorNameList: t
|
|||||||
local nextIndex = self:OnExecute(index, AttributesData, BehaviorNameList)
|
local nextIndex = self:OnExecute(index, AttributesData, BehaviorNameList)
|
||||||
if type(nextIndex) == "number" then
|
if type(nextIndex) == "number" then
|
||||||
self.PlayerAI:TriggerAllRunesExcept(self, "OnTriggerEndEvent", index, AttributesData, BehaviorNameList)
|
self.PlayerAI:TriggerAllRunesExcept(self, "OnTriggerEndEvent", index, AttributesData, BehaviorNameList)
|
||||||
|
self:RecordExecution(index, attributesBefore, AttributesData, behaviorListBefore, BehaviorNameList, nextIndex)
|
||||||
return nextIndex
|
return nextIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
local endNextIndex = self.PlayerAI:TriggerAllRunesExcept(self, "OnTriggerEndEvent", index, AttributesData, BehaviorNameList)
|
local endNextIndex = self.PlayerAI:TriggerAllRunesExcept(self, "OnTriggerEndEvent", index, AttributesData, BehaviorNameList)
|
||||||
if type(endNextIndex) == "number" then
|
if type(endNextIndex) == "number" then
|
||||||
|
self:RecordExecution(index, attributesBefore, AttributesData, behaviorListBefore, BehaviorNameList, endNextIndex)
|
||||||
return endNextIndex
|
return endNextIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
self.TriggerTime = self.TriggerTime + 1
|
self.TriggerTime = self.TriggerTime + 1
|
||||||
return nextIndex -- 返回下一个要执行的index
|
self:RecordExecution(index, attributesBefore, AttributesData, behaviorListBefore, BehaviorNameList, nextIndex)
|
||||||
|
|
||||||
|
return nextIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 子类可以重写这个方法
|
-- 子类可以重写这个方法
|
||||||
function Rune:OnExecute(index: number, AttributesData: table?, BehaviorNameList: table?)
|
function Rune:OnExecute(index: number, AttributesData: table?, BehaviorNameList: table?)
|
||||||
-- 默认实现,子类可以重写
|
|
||||||
-- index: 当前符文在列表中的位置
|
-- index: 当前符文在列表中的位置
|
||||||
-- AttributesData: 属性数据,符文可以修改
|
-- AttributesData: 属性数据,符文可以修改
|
||||||
-- BehaviorNameList: 行为名称列表,符文可以修改
|
-- BehaviorNameList: 行为名称列表,符文可以修改
|
||||||
-- 返回值: 下一个要执行的index,或者nil表示继续下一个
|
-- 返回值: 下一个要执行的index,或者nil表示继续下一个
|
||||||
return nil -- 默认继续下一个符文
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 销毁
|
-- 销毁
|
||||||
function Rune:OnDestroy()
|
function Rune:OnDestroy()
|
||||||
|
self.executionRecords = nil
|
||||||
self = nil
|
self = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return Rune
|
return Rune
|
@ -28,7 +28,7 @@ function IceCoffine:Init(PlayerAI, Character: TypeList.Character, Player: Player
|
|||||||
self.Mobs = nil
|
self.Mobs = nil
|
||||||
setmetatable(self, IceCoffine)
|
setmetatable(self, IceCoffine)
|
||||||
self.OrgCooldown = COOLDOWN
|
self.OrgCooldown = COOLDOWN
|
||||||
self:StartCooldownTask()
|
-- self:StartCooldownTask()
|
||||||
|
|
||||||
-- 客户端表现
|
-- 客户端表现
|
||||||
self:SendPerformanceEvent("Init", self.Player, script.Name, true, {})
|
self:SendPerformanceEvent("Init", self.Player, script.Name, true, {})
|
||||||
@ -40,8 +40,8 @@ function IceCoffine:Check(CheckInfo: table)
|
|||||||
self:CheckClean()
|
self:CheckClean()
|
||||||
|
|
||||||
-- 当前血量<=20%时触发技能
|
-- 当前血量<=20%时触发技能
|
||||||
local maxHp = self.Character.Stats.MaxHp
|
local maxHp = self.Character.Config.maxhp
|
||||||
local currentHp = self.Character.Stats.Hp
|
local currentHp = self.Character.Config.hp
|
||||||
local recoverHp = maxHp * 0.2
|
local recoverHp = maxHp * 0.2
|
||||||
if currentHp <= recoverHp then
|
if currentHp <= recoverHp then
|
||||||
self.CheckData = {}
|
self.CheckData = {}
|
||||||
@ -58,7 +58,7 @@ function IceCoffine:Execute()
|
|||||||
-- cd放前面之后发送事件才能正常记录cd
|
-- cd放前面之后发送事件才能正常记录cd
|
||||||
self:StartCooldownTask()
|
self:StartCooldownTask()
|
||||||
|
|
||||||
local maxHp = self.Character.Stats.MaxHp
|
local maxHp = self.Character.Config.MaxHp
|
||||||
local recoverHp = math.floor(maxHp * (self.PlayerAI:GetSharedData("IceCoffin_MaxRecover") / 100))
|
local recoverHp = math.floor(maxHp * (self.PlayerAI:GetSharedData("IceCoffin_MaxRecover") / 100))
|
||||||
DamageProxy:Heal(self.Character, self.Character, recoverHp)
|
DamageProxy:Heal(self.Character, self.Character, recoverHp)
|
||||||
task.wait(0.5)
|
task.wait(0.5)
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
--> Services
|
--> Services
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local ServerStorage = game:GetService("ServerStorage")
|
||||||
|
|
||||||
--> Dependencies
|
--> Dependencies
|
||||||
local Utils = require(ReplicatedStorage.Tools.Utils)
|
local Utils = require(ReplicatedStorage.Tools.Utils)
|
||||||
|
local TypeList = require(ServerStorage.Base.TypeList)
|
||||||
|
local Rune = require(ServerStorage.Base.Rune)
|
||||||
|
|
||||||
local RuneFireDamage = {}
|
local RuneFireDamage = {}
|
||||||
RuneFireDamage.__index = RuneFireDamage
|
RuneFireDamage.__index = RuneFireDamage
|
||||||
setmetatable(RuneFireDamage, {__index = Rune})
|
setmetatable(RuneFireDamage, {__index = Rune})
|
||||||
|
|
||||||
|
|
||||||
function RuneFireDamage:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
function RuneFireDamage:Init(PlayerAI, Character: TypeList.Character)
|
||||||
local self = Rune:Init(PlayerAI, Character, script.Name)
|
local self = Rune:Init(PlayerAI, Character, script.Name)
|
||||||
self.Player = Player
|
|
||||||
setmetatable(self, RuneFireDamage)
|
setmetatable(self, RuneFireDamage)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
--> Services
|
--> Services
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local ServerStorage = game:GetService("ServerStorage")
|
||||||
|
|
||||||
--> Dependencies
|
--> Dependencies
|
||||||
local Utils = require(ReplicatedStorage.Tools.Utils)
|
local Utils = require(ReplicatedStorage.Tools.Utils)
|
||||||
|
local TypeList = require(ServerStorage.Base.TypeList)
|
||||||
|
local Rune = require(ServerStorage.Base.Rune)
|
||||||
|
|
||||||
local RuneIceCoffin = {}
|
local RuneIceCoffin = {}
|
||||||
RuneIceCoffin.__index = RuneIceCoffin
|
RuneIceCoffin.__index = RuneIceCoffin
|
||||||
setmetatable(RuneIceCoffin, {__index = Rune})
|
setmetatable(RuneIceCoffin, {__index = Rune})
|
||||||
|
|
||||||
|
|
||||||
function RuneIceCoffin:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
function RuneIceCoffin:Init(PlayerAI, Character: TypeList.Character)
|
||||||
local self = Rune:Init(PlayerAI, Character, script.Name)
|
local self = Rune:Init(PlayerAI, Character, script.Name)
|
||||||
self.Player = Player
|
|
||||||
setmetatable(self, RuneIceCoffin)
|
setmetatable(self, RuneIceCoffin)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -95,7 +95,7 @@ end
|
|||||||
|
|
||||||
-- 动态添加行为
|
-- 动态添加行为
|
||||||
function PlayerAI:AddBehaviour(BehaviourName: string, WearingSlot: number?)
|
function PlayerAI:AddBehaviour(BehaviourName: string, WearingSlot: number?)
|
||||||
if not Behaviours[BehaviourName] then warn("Behaviour not found") return end
|
if not Behaviours[BehaviourName] then warn("Behaviour not found", BehaviourName) return end
|
||||||
local newBehaviour = Behaviours[BehaviourName]:Init(self, self.Character, self.Player)
|
local newBehaviour = Behaviours[BehaviourName]:Init(self, self.Character, self.Player)
|
||||||
newBehaviour.WearingSlot = WearingSlot or 4
|
newBehaviour.WearingSlot = WearingSlot or 4
|
||||||
self.BehaviourList[BehaviourName] = newBehaviour
|
self.BehaviourList[BehaviourName] = newBehaviour
|
||||||
|
@ -185,7 +185,6 @@ function PlayerFightProxy:UpdatePlayerFightData(Player: Player)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- 更新玩家属性
|
-- 更新玩家属性
|
||||||
-- print(AttributesData)
|
|
||||||
for AttributeName, AttributeValue in AttributesData do
|
for AttributeName, AttributeValue in AttributesData do
|
||||||
-- TODO:这里可能涉及到战斗时更换装备的属性处理,还需要再函数内部再根据剩余百分比数值变化
|
-- TODO:这里可能涉及到战斗时更换装备的属性处理,还需要再函数内部再根据剩余百分比数值变化
|
||||||
PlayerRole:ChangeAttributeValue(AttributeName, AttributeValue)
|
PlayerRole:ChangeAttributeValue(AttributeName, AttributeValue)
|
||||||
@ -205,7 +204,6 @@ function PlayerFightProxy:UpdatePlayerFightData(Player: Player)
|
|||||||
playerAI:AddBehaviour(behaviorName)
|
playerAI:AddBehaviour(behaviorName)
|
||||||
end
|
end
|
||||||
playerAI:AddBehaviour("Move")
|
playerAI:AddBehaviour("Move")
|
||||||
-- playerAI:AddBehaviour("SwordWave")
|
|
||||||
playerAI:AddBehaviour("Attack")
|
playerAI:AddBehaviour("Attack")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
local RuneCalculation = {}
|
local RuneCalculation = {}
|
||||||
|
|
||||||
|
--> Services
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
--> Events
|
||||||
|
local RE_RuneExecutionRecord = ReplicatedStorage.Events:WaitForChild("RE_RuneExecutionRecord")
|
||||||
|
|
||||||
-- 处理符文机制主函数
|
-- 处理符文机制主函数
|
||||||
function RuneCalculation:GetRuneAttributes(Player: Player, PlayerAI: table, AttributesData: table, BehaviorNameList: table)
|
function RuneCalculation:GetRuneAttributes(Player: Player, PlayerAI: table, AttributesData: table, BehaviorNameList: table)
|
||||||
if not Player and not PlayerAI then warn("Player or PlayerAI not found") return end
|
if not Player and not PlayerAI then warn("Player or PlayerAI not found") return end
|
||||||
@ -19,25 +25,52 @@ function RuneCalculation:GetRuneAttributes(Player: Player, PlayerAI: table, Attr
|
|||||||
PlayerAI:ClearSharedData()
|
PlayerAI:ClearSharedData()
|
||||||
PlayerAI:ClearAllRune()
|
PlayerAI:ClearAllRune()
|
||||||
for wearingSlot, runeName in wearingRuneName do PlayerAI:AddRune(runeName, wearingSlot) end
|
for wearingSlot, runeName in wearingRuneName do PlayerAI:AddRune(runeName, wearingSlot) end
|
||||||
|
|
||||||
|
-- 开始记录
|
||||||
|
for runeName, rune in pairs(PlayerAI.RuneList) do
|
||||||
|
rune:StartRecording()
|
||||||
|
end
|
||||||
|
|
||||||
-- 触发开始事件,并检查是否有符文要控制流程
|
-- 触发开始事件,并检查是否有符文要控制流程
|
||||||
local startNextIndex = PlayerAI:ForEachRune(function(runeName, triggerSlot, rune)
|
local startNextIndex = PlayerAI:ForEachRune(function(runeName, triggerSlot, rune)
|
||||||
local result = rune:OnStartEvent(runeName, triggerSlot, AttributesData, BehaviorNameList)
|
local result = rune:OnStartEvent(runeName, triggerSlot, AttributesData, BehaviorNameList)
|
||||||
if type(result) == "number" then
|
if type(result) == "number" then
|
||||||
return result -- 返回要跳转的index
|
return result
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local index = 1
|
local index = 1
|
||||||
local maxSteps = 1000 -- 防止无限循环
|
local maxSteps = 1000
|
||||||
|
|
||||||
-- 如果开始事件指定了起始位置,使用它
|
|
||||||
if type(startNextIndex) == "number" then
|
if type(startNextIndex) == "number" then
|
||||||
index = startNextIndex
|
index = startNextIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 执行符文循环
|
-- 执行符文循环
|
||||||
self:ExecuteRuneLoop(PlayerAI, wearingRuneName, wearingRuneUniqueId, index, maxSteps)
|
self:ExecuteRuneLoop(PlayerAI, wearingRuneName, wearingRuneUniqueId, index, maxSteps, AttributesData, BehaviorNameList)
|
||||||
|
|
||||||
|
-- 收集所有符文的执行记录
|
||||||
|
local allExecutionRecords = {}
|
||||||
|
for runeName, rune in pairs(PlayerAI.RuneList) do
|
||||||
|
local records = rune:GetExecutionRecords()
|
||||||
|
if records and #records > 0 then
|
||||||
|
for _, record in ipairs(records) do
|
||||||
|
table.insert(allExecutionRecords, record)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rune:StopRecording()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 统一发送执行记录到前端
|
||||||
|
if #allExecutionRecords > 0 then
|
||||||
|
local success, error = pcall(function()
|
||||||
|
RE_RuneExecutionRecord:FireClient(Player, allExecutionRecords)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
warn("RuneCalculation: 发送符文执行记录失败:", error)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 执行符文循环的辅助函数
|
-- 执行符文循环的辅助函数
|
||||||
@ -49,31 +82,28 @@ function RuneCalculation:ExecuteRuneLoop(PlayerAI: table, wearingRuneName: table
|
|||||||
if result then
|
if result then
|
||||||
local nextIndex = PlayerAI:TriggerRune(wearingRuneName[index], "Execute", index, AttributesData, BehaviorNameList)
|
local nextIndex = PlayerAI:TriggerRune(wearingRuneName[index], "Execute", index, AttributesData, BehaviorNameList)
|
||||||
|
|
||||||
-- 根据返回值决定下一个index
|
|
||||||
if type(nextIndex) == "number" then
|
if type(nextIndex) == "number" then
|
||||||
index = nextIndex -- 符文指定了下一个位置
|
index = nextIndex
|
||||||
else
|
else
|
||||||
index += 1 -- 默认继续下一个
|
index += 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
index += 1
|
index += 1
|
||||||
end
|
end
|
||||||
maxSteps -= 1 -- 防止无限循环
|
maxSteps -= 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 触发结束事件,并检查是否有符文要控制流程
|
-- 触发结束事件,并检查是否有符文要控制流程
|
||||||
local endNextIndex = PlayerAI:ForEachRune(function(runeName, triggerSlot, rune)
|
local endNextIndex = PlayerAI:ForEachRune(function(runeName, triggerSlot, rune)
|
||||||
local result = rune:OnEndEvent(runeName, triggerSlot, AttributesData, BehaviorNameList)
|
local result = rune:OnEndEvent(runeName, triggerSlot, AttributesData, BehaviorNameList)
|
||||||
if type(result) == "number" then
|
if type(result) == "number" then
|
||||||
return result -- 返回要跳转的index
|
return result
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- 如果结束事件指定了要跳转的位置,重新开始循环
|
|
||||||
if type(endNextIndex) == "number" and endNextIndex <= #wearingRuneUniqueId and maxSteps > 0 then
|
if type(endNextIndex) == "number" and endNextIndex <= #wearingRuneUniqueId and maxSteps > 0 then
|
||||||
self:ExecuteRuneLoop(PlayerAI, wearingRuneName, wearingRuneUniqueId, endNextIndex, maxSteps, AttributesData, BehaviorNameList)
|
self:ExecuteRuneLoop(PlayerAI, wearingRuneName, wearingRuneUniqueId, endNextIndex, maxSteps, AttributesData, BehaviorNameList)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return RuneCalculation
|
return RuneCalculation
|
@ -258,16 +258,16 @@ function RuneProxy:GetPlayerWearingRuneData(Player: Player)
|
|||||||
for _, RuneData in ArchiveProxy.pData[Player.UserId][STORE_NAME] do
|
for _, RuneData in ArchiveProxy.pData[Player.UserId][STORE_NAME] do
|
||||||
if tonumber(RuneData.wearing) > 0 and table.find(wearingEquipments, RuneData.wearing) then
|
if tonumber(RuneData.wearing) > 0 and table.find(wearingEquipments, RuneData.wearing) then
|
||||||
table.insert(wearingRuneUniqueId, RuneData.id)
|
table.insert(wearingRuneUniqueId, RuneData.id)
|
||||||
local RuneData = Utils:GetIdDataFromJson(JsonRune, RuneData.orgId)
|
local JsonRuneData = Utils:GetIdDataFromJson(JsonRune, RuneData.orgId)
|
||||||
if RuneData.runeName then
|
if JsonRuneData and JsonRuneData.runeName then
|
||||||
wearingRuneName[RuneData.wearingSlot] = RuneData.runeName
|
wearingRuneName[RuneData.wearingSlot] = JsonRuneData.runeName
|
||||||
end
|
end
|
||||||
if RuneData.behaviorName then
|
if JsonRuneData and JsonRuneData.behaviorName then
|
||||||
table.insert(wearingRuneBehaviorName, RuneData.behaviorName)
|
table.insert(wearingRuneBehaviorName, JsonRuneData.behaviorName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return wearingRuneUniqueId, wearingRuneName
|
return wearingRuneUniqueId, wearingRuneName, wearingRuneBehaviorName
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 获取对应装备槽位上的技能
|
-- 获取对应装备槽位上的技能
|
||||||
|
@ -123,5 +123,6 @@ end)
|
|||||||
-- 打开默认界面
|
-- 打开默认界面
|
||||||
UIManager:OpenWindow("MainWindow")
|
UIManager:OpenWindow("MainWindow")
|
||||||
UIManager:OpenWindow("TipsWindow")
|
UIManager:OpenWindow("TipsWindow")
|
||||||
|
UIManager:OpenWindow("RuneStateWindow")
|
||||||
|
|
||||||
return PerformanceClient
|
return PerformanceClient
|
70
src/StarterPlayerScripts/ClientMain/TestRuneStateWindow.luau
Normal file
70
src/StarterPlayerScripts/ClientMain/TestRuneStateWindow.luau
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
--> Services
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local UserInputService = game:GetService("UserInputService")
|
||||||
|
|
||||||
|
--> Dependencies
|
||||||
|
local UIManager = require(script.Parent.Parent.UI.UIManager)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local LocalPlayer = Players.LocalPlayer
|
||||||
|
|
||||||
|
-- 测试执行记录
|
||||||
|
local testExecutionRecords = {
|
||||||
|
{
|
||||||
|
runeName = "RuneFireDamage",
|
||||||
|
runeUniqueId = 1,
|
||||||
|
index = 1,
|
||||||
|
timestamp = tick(),
|
||||||
|
action = "Execute",
|
||||||
|
attributesBefore = {attack = 100, hp = 1000, atkSpeed = 100},
|
||||||
|
attributesAfter = {attack = 150, hp = 1000, atkSpeed = 120},
|
||||||
|
behaviorListBefore = {"Attack"},
|
||||||
|
behaviorListAfter = {"Attack", "FireDamage"},
|
||||||
|
nextIndex = 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
runeName = "RuneIceCoffin",
|
||||||
|
runeUniqueId = 2,
|
||||||
|
index = 2,
|
||||||
|
timestamp = tick(),
|
||||||
|
action = "Execute",
|
||||||
|
attributesBefore = {attack = 150, hp = 1000, atkSpeed = 120},
|
||||||
|
attributesAfter = {attack = 150, hp = 1200, atkSpeed = 120, fireAtk = 50},
|
||||||
|
behaviorListBefore = {"Attack", "FireDamage"},
|
||||||
|
behaviorListAfter = {"Attack", "FireDamage", "IceCoffin"},
|
||||||
|
nextIndex = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- 键盘输入测试
|
||||||
|
UserInputService.InputBegan:Connect(function(input, gameProcessed)
|
||||||
|
if gameProcessed then return end
|
||||||
|
|
||||||
|
if input.KeyCode == Enum.KeyCode.R then
|
||||||
|
-- 打开符文状态窗口
|
||||||
|
print("打开符文状态窗口")
|
||||||
|
UIManager:OpenWindow("RuneStateWindow")
|
||||||
|
|
||||||
|
elseif input.KeyCode == Enum.KeyCode.T then
|
||||||
|
-- 模拟接收符文执行记录
|
||||||
|
print("模拟符文执行记录")
|
||||||
|
local runeStateWindow = UIManager:GetWindow("RuneStateWindow")
|
||||||
|
if runeStateWindow then
|
||||||
|
runeStateWindow:OnRuneExecutionRecord(testExecutionRecords)
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif input.KeyCode == Enum.KeyCode.C then
|
||||||
|
-- 关闭符文状态窗口
|
||||||
|
print("关闭符文状态窗口")
|
||||||
|
UIManager:CloseWindow("RuneStateWindow")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
print("符文状态窗口测试脚本已启动")
|
||||||
|
print("按 R 键打开符文状态窗口")
|
||||||
|
print("按 T 键模拟符文执行记录")
|
||||||
|
print("按 C 键关闭符文状态窗口")
|
||||||
|
|
||||||
|
-- 返回一个空表作为模块返回值
|
||||||
|
return {}
|
@ -9,6 +9,7 @@ UIManager.ExcludeFromStack = { -- 不受堆栈影响的窗口列表
|
|||||||
"TipsWindow", -- 主窗口
|
"TipsWindow", -- 主窗口
|
||||||
"AbilityStateWindow",
|
"AbilityStateWindow",
|
||||||
"LevelStageWindow",
|
"LevelStageWindow",
|
||||||
|
"RuneStateWindow", -- 符文状态窗口
|
||||||
-- 可以继续添加其他窗口名称
|
-- 可以继续添加其他窗口名称
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +130,11 @@ function UIManager:IsOpened(WindowName: string)
|
|||||||
return UIManager.Instances[WindowName] ~= nil
|
return UIManager.Instances[WindowName] ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 获取窗口实例
|
||||||
|
function UIManager:GetWindow(WindowName: string)
|
||||||
|
return UIManager.Instances[WindowName]
|
||||||
|
end
|
||||||
|
|
||||||
function UIManager:SetData(WindowName: string, Data: table)
|
function UIManager:SetData(WindowName: string, Data: table)
|
||||||
if not UIManager.Instances[WindowName] then
|
if not UIManager.Instances[WindowName] then
|
||||||
warn("UIManager:SetData() 窗口不存在:" .. WindowName)
|
warn("UIManager:SetData() 窗口不存在:" .. WindowName)
|
||||||
|
@ -0,0 +1,151 @@
|
|||||||
|
local RuneShow = {}
|
||||||
|
RuneShow.__index = RuneShow
|
||||||
|
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
local Utils = require(ReplicatedStorage.Tools.Utils)
|
||||||
|
local Localization = require(ReplicatedStorage.Tools.Localization)
|
||||||
|
local JsonRune = require(ReplicatedStorage.Json.Rune)
|
||||||
|
local Signal = require(ReplicatedStorage.Tools.Signal)
|
||||||
|
|
||||||
|
local showRuneSignal = Signal.new(Signal.ENUM.SHOW_RUNE)
|
||||||
|
|
||||||
|
function RuneShow:Init(data: table)
|
||||||
|
local self = {}
|
||||||
|
self.Data = data
|
||||||
|
self.Variables = {
|
||||||
|
["_imgIcon"] = 0,
|
||||||
|
["_imgMask"] = 0,
|
||||||
|
["_tmpName"] = 0,
|
||||||
|
["_tmpLevel"] = 0,
|
||||||
|
}
|
||||||
|
self.Task = nil
|
||||||
|
self.SignalConnections = {}
|
||||||
|
|
||||||
|
local con = showRuneSignal:Connect(function(RuneName, RuneUniqueId)
|
||||||
|
if RuneName == self.Data.runeName and RuneUniqueId == self.Data.runeUniqueId then
|
||||||
|
self:Refresh()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
table.insert(self.SignalConnections, con)
|
||||||
|
|
||||||
|
setmetatable(self, RuneShow)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 初始化完成后的回调
|
||||||
|
function RuneShow:OnInitFinish()
|
||||||
|
end
|
||||||
|
|
||||||
|
function RuneShow:Refresh()
|
||||||
|
if self.Task then
|
||||||
|
task.cancel(self.Task)
|
||||||
|
self.Task = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.Data.runeName ~= nil then
|
||||||
|
-- 获取符文数据
|
||||||
|
local runeData = Utils:GetSpecialKeyDataFromJson(JsonRune, "runeName", self.Data.runeName)
|
||||||
|
|
||||||
|
-- 设置符文图标
|
||||||
|
if runeData and runeData.icon then
|
||||||
|
self.Variables._imgIcon.Image = Localization:GetImageData(runeData.icon)
|
||||||
|
else
|
||||||
|
-- 使用默认图标
|
||||||
|
self.Variables._imgIcon.Image = "rbxassetid://0"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 设置符文名称
|
||||||
|
if runeData and runeData.nameId then
|
||||||
|
-- 通过nameId获取本地化文本
|
||||||
|
self.Variables._tmpName.Text = Localization:GetLanguageData(runeData.nameId) or "未知符文"
|
||||||
|
else
|
||||||
|
-- 使用符文名称作为显示文本
|
||||||
|
self.Variables._tmpName.Text = self.Data.runeName or "未知符文"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 设置符文等级
|
||||||
|
if self.Data.level then
|
||||||
|
self.Variables._tmpLevel.Text = "Lv." .. tostring(self.Data.level)
|
||||||
|
else
|
||||||
|
self.Variables._tmpLevel.Text = "Lv.1"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 设置符文背景颜色
|
||||||
|
if runeData and runeData.quality then
|
||||||
|
local bgColor = Localization:GetRuneQualityBgColor(runeData.quality)
|
||||||
|
if bgColor and self.Variables._imgIcon then
|
||||||
|
self.Variables._imgIcon.BackgroundColor3 = bgColor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.Variables._imgIcon.Visible = true
|
||||||
|
self.Variables._imgMask.Visible = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 播放激活动画
|
||||||
|
function RuneShow:PlayActivateAnimation()
|
||||||
|
-- 创建激活效果
|
||||||
|
local effect = Instance.new("Frame")
|
||||||
|
effect.Size = UDim2.new(1, 0, 1, 0)
|
||||||
|
effect.Position = UDim2.new(0, 0, 0, 0)
|
||||||
|
effect.BackgroundColor3 = Color3.fromRGB(255, 215, 0) -- 金色
|
||||||
|
effect.BackgroundTransparency = 0.3
|
||||||
|
effect.BorderSizePixel = 0
|
||||||
|
effect.Parent = self.UIRoot
|
||||||
|
|
||||||
|
-- 创建粒子效果
|
||||||
|
local particle = Instance.new("Frame")
|
||||||
|
particle.Size = UDim2.new(0.1, 0, 0.1, 0)
|
||||||
|
particle.Position = UDim2.new(0.45, 0, 0.45, 0)
|
||||||
|
particle.BackgroundColor3 = Color3.fromRGB(255, 255, 0) -- 黄色
|
||||||
|
particle.BackgroundTransparency = 0
|
||||||
|
particle.BorderSizePixel = 0
|
||||||
|
particle.Parent = effect
|
||||||
|
|
||||||
|
-- 动画效果
|
||||||
|
local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
|
||||||
|
|
||||||
|
-- 主效果动画
|
||||||
|
local mainTween = game:GetService("TweenService"):Create(effect, tweenInfo, {
|
||||||
|
Size = UDim2.new(1.5, 0, 1.5, 0),
|
||||||
|
BackgroundTransparency = 1
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 粒子动画
|
||||||
|
local particleTween = game:GetService("TweenService"):Create(particle, tweenInfo, {
|
||||||
|
Size = UDim2.new(0.5, 0, 0.5, 0),
|
||||||
|
BackgroundTransparency = 1
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 播放动画
|
||||||
|
mainTween:Play()
|
||||||
|
particleTween:Play()
|
||||||
|
|
||||||
|
-- 动画结束后清理
|
||||||
|
mainTween.Completed:Connect(function()
|
||||||
|
effect:Destroy()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RuneShow:Destroy()
|
||||||
|
if self.Task then
|
||||||
|
task.cancel(self.Task)
|
||||||
|
self.Task = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 清理信号连接
|
||||||
|
for _, connection in pairs(self.SignalConnections) do
|
||||||
|
if connection then
|
||||||
|
connection:Disconnect()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(self) do
|
||||||
|
self[k] = nil
|
||||||
|
end
|
||||||
|
self = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return RuneShow
|
361
src/StarterPlayerScripts/UI/Windows/RuneStateWindow/init.luau
Normal file
361
src/StarterPlayerScripts/UI/Windows/RuneStateWindow/init.luau
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
--> Services
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
--> Dependencies
|
||||||
|
local UIWindow = require(ReplicatedStorage.Base.UIWindow)
|
||||||
|
local UIEnums = require(ReplicatedStorage.Base.UIEnums)
|
||||||
|
local Localization = require(ReplicatedStorage.Tools.Localization)
|
||||||
|
local Utils = require(ReplicatedStorage.Tools.Utils)
|
||||||
|
|
||||||
|
--> Json
|
||||||
|
local JsonAttributes = require(ReplicatedStorage.Json.Attributes)
|
||||||
|
|
||||||
|
--> Components
|
||||||
|
local RuneShow = require(script.RuneShow)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local RuneStateWindow = {}
|
||||||
|
RuneStateWindow.__index = RuneStateWindow
|
||||||
|
setmetatable(RuneStateWindow, {__index = UIWindow})
|
||||||
|
|
||||||
|
function RuneStateWindow:Init(UIManager: table, Data: table?)
|
||||||
|
local self = UIWindow:Init(UIManager, Data)
|
||||||
|
setmetatable(self, RuneStateWindow)
|
||||||
|
self.Variables = {
|
||||||
|
["__listRune"] = 0,
|
||||||
|
}
|
||||||
|
self.UIRootName = "ui_w_rune_state"
|
||||||
|
self.UIParentName = UIEnums.UIParent.UIRoot
|
||||||
|
|
||||||
|
-- 符文执行记录队列
|
||||||
|
self.runeExecutionQueue = {}
|
||||||
|
self.isPlayingAnimation = false
|
||||||
|
|
||||||
|
-- 事件连接
|
||||||
|
self.runeExecutionConnection = nil
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function RuneStateWindow:OnOpenWindow()
|
||||||
|
UIWindow.OnOpenWindow(self)
|
||||||
|
|
||||||
|
self.Variables["__listRune"]:AddComponent(RuneShow)
|
||||||
|
|
||||||
|
-- 连接符文执行记录事件
|
||||||
|
self:ConnectRuneExecutionEvent()
|
||||||
|
|
||||||
|
-- 初始化符文列表
|
||||||
|
self:RefreshRuneList()
|
||||||
|
end
|
||||||
|
|
||||||
|
function RuneStateWindow:OnCloseWindow()
|
||||||
|
UIWindow.OnCloseWindow(self)
|
||||||
|
|
||||||
|
self.Variables["__listRune"]:Clean()
|
||||||
|
|
||||||
|
-- 断开符文执行记录事件连接
|
||||||
|
self:DisconnectRuneExecutionEvent()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 刷新符文列表
|
||||||
|
function RuneStateWindow:RefreshRuneList()
|
||||||
|
-- 这里需要从服务端获取当前穿戴的符文数据
|
||||||
|
-- 暂时使用模拟数据
|
||||||
|
local runeData = self:GetWearingRuneData()
|
||||||
|
self.Variables["__listRune"]:SetData(runeData)
|
||||||
|
print("刷新符文列表", runeData)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- 获取穿戴的符文数据
|
||||||
|
function RuneStateWindow:GetWearingRuneData()
|
||||||
|
-- 从传入的数据中获取符文信息
|
||||||
|
if self.Data and self.Data.runeData then
|
||||||
|
return self.Data.runeData
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 从ReplicatedStorage获取当前穿戴的符文
|
||||||
|
local runeData = {}
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local LocalPlayer = Players.LocalPlayer
|
||||||
|
|
||||||
|
-- 获取玩家数据文件夹
|
||||||
|
local PlayerDataFolder = ReplicatedStorage:WaitForChild("PlayerData")
|
||||||
|
local PlayerFolder = PlayerDataFolder:FindFirstChild(LocalPlayer.UserId)
|
||||||
|
if not PlayerFolder then return runeData end
|
||||||
|
|
||||||
|
-- 获取符文文件夹
|
||||||
|
local RuneFolder = PlayerFolder:FindFirstChild("Rune")
|
||||||
|
if not RuneFolder then return runeData end
|
||||||
|
|
||||||
|
-- 遍历所有符文,找到穿戴中的
|
||||||
|
for _, runeInstance in pairs(RuneFolder:GetChildren()) do
|
||||||
|
local wearing = runeInstance:GetAttribute("wearing")
|
||||||
|
if wearing and tonumber(wearing) > 0 then
|
||||||
|
local runeInfo = {
|
||||||
|
runeName = runeInstance:GetAttribute("runeName"),
|
||||||
|
runeUniqueId = tonumber(runeInstance.Name),
|
||||||
|
level = runeInstance:GetAttribute("level") or 1,
|
||||||
|
wearingSlot = runeInstance:GetAttribute("wearingSlot") or 1
|
||||||
|
}
|
||||||
|
table.insert(runeData, runeInfo)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 按穿戴槽位排序
|
||||||
|
table.sort(runeData, function(a, b)
|
||||||
|
return a.wearingSlot < b.wearingSlot
|
||||||
|
end)
|
||||||
|
|
||||||
|
return runeData
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 接收符文执行记录
|
||||||
|
function RuneStateWindow:OnRuneExecutionRecord(executionRecords: table)
|
||||||
|
-- 自动刷新符文列表(获取最新的穿戴符文数据)
|
||||||
|
self:RefreshRuneList()
|
||||||
|
|
||||||
|
-- 将执行记录添加到队列
|
||||||
|
for _, record in ipairs(executionRecords) do
|
||||||
|
table.insert(self.runeExecutionQueue, record)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 开始播放动画
|
||||||
|
if not self.isPlayingAnimation then
|
||||||
|
self:PlayRuneAnimation()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 播放符文动画
|
||||||
|
function RuneStateWindow:PlayRuneAnimation()
|
||||||
|
if #self.runeExecutionQueue == 0 then
|
||||||
|
self.isPlayingAnimation = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.isPlayingAnimation = true
|
||||||
|
local record = table.remove(self.runeExecutionQueue, 1)
|
||||||
|
|
||||||
|
-- 播放小丑牌动画
|
||||||
|
self:PlayJokerCardAnimation(record)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 播放小丑牌动画
|
||||||
|
function RuneStateWindow:PlayJokerCardAnimation(record: table)
|
||||||
|
-- 找到对应的符文UI元素
|
||||||
|
local runeItem = self:FindRuneItem(record.runeName, record.runeUniqueId)
|
||||||
|
if not runeItem then
|
||||||
|
-- 如果找不到符文,直接播放下一个
|
||||||
|
task.wait(0.5) -- 短暂延迟
|
||||||
|
self:PlayRuneAnimation()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 创建动画效果
|
||||||
|
self:CreateJokerCardEffect(runeItem, record)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 查找符文UI元素
|
||||||
|
function RuneStateWindow:FindRuneItem(runeName: string, runeUniqueId: number)
|
||||||
|
-- 在符文列表中查找对应的符文
|
||||||
|
local runeList = self.Variables["__listRune"]
|
||||||
|
if runeList and runeList.Instances then
|
||||||
|
for _, component in pairs(runeList.Instances) do
|
||||||
|
if component.Data and component.Data.runeName == runeName and component.Data.runeUniqueId == runeUniqueId then
|
||||||
|
return component
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 创建小丑牌动画效果
|
||||||
|
function RuneStateWindow:CreateJokerCardEffect(runeItem: table, record: table)
|
||||||
|
-- 获取符文UI根节点
|
||||||
|
local runeUI = runeItem.UIRoot
|
||||||
|
if not runeUI then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 创建动画效果
|
||||||
|
local effect = Instance.new("Frame")
|
||||||
|
effect.Size = UDim2.new(1, 0, 1, 0)
|
||||||
|
effect.Position = UDim2.new(0, 0, 0, 0)
|
||||||
|
effect.BackgroundColor3 = Color3.fromRGB(255, 215, 0) -- 金色
|
||||||
|
effect.BackgroundTransparency = 0.5
|
||||||
|
effect.BorderSizePixel = 0
|
||||||
|
effect.Parent = runeUI
|
||||||
|
|
||||||
|
-- 创建发光效果
|
||||||
|
local glow = Instance.new("Frame")
|
||||||
|
glow.Size = UDim2.new(1.2, 0, 1.2, 0)
|
||||||
|
glow.Position = UDim2.new(-0.1, 0, -0.1, 0)
|
||||||
|
glow.BackgroundColor3 = Color3.fromRGB(255, 255, 0) -- 黄色
|
||||||
|
glow.BackgroundTransparency = 0.8
|
||||||
|
glow.BorderSizePixel = 0
|
||||||
|
glow.Parent = effect
|
||||||
|
|
||||||
|
-- 创建属性变化显示
|
||||||
|
self:CreateAttributeChangeDisplay(runeUI, record)
|
||||||
|
|
||||||
|
-- 动画效果
|
||||||
|
local tweenInfo = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
|
||||||
|
|
||||||
|
-- 缩放动画
|
||||||
|
local scaleTween = game:GetService("TweenService"):Create(effect, tweenInfo, {
|
||||||
|
Size = UDim2.new(1.5, 0, 1.5, 0),
|
||||||
|
BackgroundTransparency = 0.9
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 旋转动画
|
||||||
|
local rotationTween = game:GetService("TweenService"):Create(effect, tweenInfo, {
|
||||||
|
Rotation = 360
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 播放动画
|
||||||
|
scaleTween:Play()
|
||||||
|
rotationTween:Play()
|
||||||
|
|
||||||
|
-- 动画结束后清理
|
||||||
|
scaleTween.Completed:Connect(function()
|
||||||
|
effect:Destroy()
|
||||||
|
-- 播放下一个动画
|
||||||
|
task.wait(0.2)
|
||||||
|
self:PlayRuneAnimation()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 创建属性变化显示
|
||||||
|
function RuneStateWindow:CreateAttributeChangeDisplay(runeUI: Instance, record: table)
|
||||||
|
-- 收集所有属性变化
|
||||||
|
local attributeChanges = {}
|
||||||
|
|
||||||
|
-- 检查属性变化
|
||||||
|
if record.attributesBefore and record.attributesAfter then
|
||||||
|
for attrName, afterValue in pairs(record.attributesAfter) do
|
||||||
|
local beforeValue = record.attributesBefore[attrName] or 0
|
||||||
|
local change = afterValue - beforeValue
|
||||||
|
if change ~= 0 then
|
||||||
|
-- 通过effectAttribute查找对应的nameId
|
||||||
|
local attributeData = Utils:GetSpecialKeyDataFromJson(JsonAttributes, "effectAttribute", attrName)
|
||||||
|
local attributeName = attrName -- 默认使用原始名称
|
||||||
|
|
||||||
|
if attributeData and attributeData.nameId then
|
||||||
|
-- 通过nameId获取本地化文本
|
||||||
|
attributeName = Localization:GetLanguageData(attributeData.nameId) or attrName
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(attributeChanges, {
|
||||||
|
name = attributeName,
|
||||||
|
change = change
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 如果没有属性变化,直接返回
|
||||||
|
if #attributeChanges == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 创建统一的属性变化显示
|
||||||
|
self:CreateAttributeChangeContainer(runeUI, attributeChanges)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 创建属性变化容器
|
||||||
|
function RuneStateWindow:CreateAttributeChangeContainer(runeUI: Instance, attributeChanges: table)
|
||||||
|
-- 计算容器高度
|
||||||
|
local containerHeight = #attributeChanges * 25 + 10
|
||||||
|
|
||||||
|
-- 创建属性变化显示容器
|
||||||
|
local changeContainer = Instance.new("Frame")
|
||||||
|
changeContainer.Size = UDim2.new(0, 160, 0, containerHeight)
|
||||||
|
changeContainer.Position = UDim2.new(0.5, -80, 0, -30)
|
||||||
|
changeContainer.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
|
||||||
|
changeContainer.BackgroundTransparency = 0.2
|
||||||
|
changeContainer.BorderSizePixel = 0
|
||||||
|
changeContainer.Parent = runeUI
|
||||||
|
|
||||||
|
-- 创建圆角
|
||||||
|
local corner = Instance.new("UICorner")
|
||||||
|
corner.CornerRadius = UDim.new(0, 8)
|
||||||
|
corner.Parent = changeContainer
|
||||||
|
|
||||||
|
-- 创建边框
|
||||||
|
local stroke = Instance.new("UIStroke")
|
||||||
|
stroke.Color = Color3.fromRGB(255, 215, 0)
|
||||||
|
stroke.Thickness = 1.5
|
||||||
|
stroke.Parent = changeContainer
|
||||||
|
|
||||||
|
-- 创建属性变化文本
|
||||||
|
for i, change in ipairs(attributeChanges) do
|
||||||
|
local changeText = Instance.new("TextLabel")
|
||||||
|
changeText.Size = UDim2.new(1, -10, 0, 20)
|
||||||
|
changeText.Position = UDim2.new(0, 5, 0, 5 + (i-1) * 25)
|
||||||
|
changeText.Text = string.format("%s +%d", change.name, change.change)
|
||||||
|
changeText.TextColor3 = change.change > 0 and Color3.fromRGB(0, 255, 100) or Color3.fromRGB(255, 100, 100)
|
||||||
|
changeText.TextScaled = true
|
||||||
|
changeText.Font = Enum.Font.SourceSansBold
|
||||||
|
changeText.BackgroundTransparency = 1
|
||||||
|
changeText.Parent = changeContainer
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 创建向上移动动画
|
||||||
|
local moveTween = game:GetService("TweenService"):Create(changeContainer, TweenInfo.new(2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {
|
||||||
|
Position = UDim2.new(0.5, -80, 0, -150),
|
||||||
|
BackgroundTransparency = 1
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 播放动画
|
||||||
|
moveTween:Play()
|
||||||
|
|
||||||
|
-- 动画结束后清理
|
||||||
|
moveTween.Completed:Connect(function()
|
||||||
|
changeContainer:Destroy()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 连接符文执行记录事件
|
||||||
|
function RuneStateWindow:ConnectRuneExecutionEvent()
|
||||||
|
-- 确保只连接一次
|
||||||
|
if self.runeExecutionConnection then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 等待RemoteEvent
|
||||||
|
local RE_RuneExecutionRecord = ReplicatedStorage.Events:WaitForChild("RE_RuneExecutionRecord")
|
||||||
|
|
||||||
|
-- 连接事件
|
||||||
|
self.runeExecutionConnection = RE_RuneExecutionRecord.OnClientEvent:Connect(function(executionRecords: table)
|
||||||
|
print("收到符文执行记录:", executionRecords)
|
||||||
|
|
||||||
|
-- 发送执行记录到窗口
|
||||||
|
self:OnRuneExecutionRecord(executionRecords)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 断开符文执行记录事件
|
||||||
|
function RuneStateWindow:DisconnectRuneExecutionEvent()
|
||||||
|
if self.runeExecutionConnection then
|
||||||
|
self.runeExecutionConnection:Disconnect()
|
||||||
|
self.runeExecutionConnection = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 销毁窗口
|
||||||
|
function RuneStateWindow:Destroy()
|
||||||
|
-- 清理动画队列
|
||||||
|
self.runeExecutionQueue = {}
|
||||||
|
self.isPlayingAnimation = false
|
||||||
|
|
||||||
|
-- 断开事件连接
|
||||||
|
self:DisconnectRuneExecutionEvent()
|
||||||
|
|
||||||
|
-- 调用父类销毁方法
|
||||||
|
UIWindow.Destroy(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
return RuneStateWindow
|
Loading…
x
Reference in New Issue
Block a user