From d00ce590066329cacfd0ff0a4b0fdf9f320bbd1d Mon Sep 17 00:00:00 2001 From: Ggafrik <906823881@qq.com> Date: Wed, 30 Jul 2025 00:37:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- excel/cha.xlsx | Bin 13773 -> 13796 bytes excel/enemy.xlsx | Bin 9025 -> 9060 bytes excel/equipment.xlsx | Bin 9768 -> 9767 bytes excel/level.xlsx | Bin 9405 -> 10188 bytes src/ReplicatedStorage/Base/UIList.luau | 31 ++- .../Json/AttributesUpgrade.json | 4 +- src/ReplicatedStorage/Json/Character.json | 2 +- src/ReplicatedStorage/Json/Enemy.json | 4 +- src/ReplicatedStorage/Json/Forge.json | 30 +-- src/ReplicatedStorage/Json/Level.json | 70 +++-- src/ServerStorage/Proxy/BookProxy.luau | 7 +- src/ServerStorage/Proxy/EquipmentProxy.luau | 5 +- src/ServerStorage/Proxy/PlayerInfoProxy.luau | 51 ++-- .../ClientMain/HealthBar.luau | 69 +++++ .../ClientMain/Helper.luau | 2 +- .../ClientMain/PerformanceClient/init.luau | 8 + src/StarterPlayerScripts/UI/UIManager.luau | 149 ++++++++++- .../UI/Windows/CreateWindow/init.luau | 11 +- .../GetEquipmentsWindow/AttributeShow.luau | 43 +++ .../GetEquipmentsWindow/EquipmentShow.luau | 77 ++++++ .../UI/Windows/GetEquipmentsWindow/init.luau | 252 ++++++++++++++++++ .../UI/Windows/MainWindow/init.luau | 30 +++ 22 files changed, 763 insertions(+), 82 deletions(-) create mode 100644 src/StarterPlayerScripts/ClientMain/HealthBar.luau create mode 100644 src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/AttributeShow.luau create mode 100644 src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/EquipmentShow.luau create mode 100644 src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/init.luau diff --git a/excel/cha.xlsx b/excel/cha.xlsx index 5f61c455e563daf06542c7561790c7797d2aa0ef..aa490c160311063715753aa9f76bdf7e5efbbbdb 100644 GIT binary patch delta 6540 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 diff --git a/excel/enemy.xlsx b/excel/enemy.xlsx index 6332a8c9f21efdbf77a5b0d536eebfcccf4e80c1..32f8ea287375ecc2e41b5b38ba001013942acbe7 100644 GIT binary patch delta 2124 zcmV-S2($OWM&w4Y$_9VBz4qqq0ssKi1^@sL0001ZY%h0ja%*C5Z)+}iZEUQSVNatl z7{}jVa^HdUJwh2{8xf6em|NmqjOV#GCZ;Q$^ad^07Kbr@_j?M%se9w;k*%e){P{n9 zer*qro3e23nbKT}1mOc8IZRNQadDTRUsq@D7&+Pyk&!|QmY{zJrqS`y*Dr?~sn$y= z*A55>ouJ&9D)u~0b5;_4AS))IXC+lh3^di9rz^!srgO$jS$Ki(M_x&|K%GLY-lHK` zD^A%=(z;~AbRvos#DIOB^Gf$>C4G-vNz}ToTq?^7B$mA3=AjiuPD$hWosfzw3kbWx zL#I*ji{OuaD1v|fOVG9VsghGAwOpA4koG#$d+}r6!}!${H2ZV#KBdr8?4H{kKD$!U z2VF(aTG37n`=3_BfL6;&43qakwc)e30398!c)_kabRAMvKS*gaRUoGz+N2pbEKAS` znsUQ-40_I|!IKDGXDB7&eu{0D{|) zK8stcJ= z4QnVXZeFlS_7}oG&`GF`+k?P6bKU*hNdS*3?g_jw%7Od&>>RT2liWj2AoJOF$oO)hW@y zQx^;n_yrt$*IxhD(O&?QkpmR7*9kKM37W$%#?}J>0MU~Z3@Ly09T4|1K4cHE;;gE! zqNW#B)$QRL<8h1v#s!>%s=oUUj+3mEn`kW?FlN4g27b?<_F9oGSSL+e&?Jf}fmWDG zw(Ela`njAuQsO*sE3Qln1wDYHPm7Nq<~w6It^x1_39Tz=<9(Mi=0pRUyU29V;+NW3 z%{{)_HFKQ>UJZXiTCp^a@0sSZrAI<;Z(*3amI9uQ=ry$d2(h5J$NXI*J9naKaSKj! zyXm`$FuFrTDV6kx0ZNG$`D)!7%S(lI?~^GPWN-0K@ySjy!~B(!mcklmY9Aq)9do`e zf5v8vi!d)_6NU=`9DlW7ZYa zyjT8**?oTjxo$iPPjM51i}LF53{IeZQ98;3R2YQ>Y>Afm8$O`>_m5HQQGe~afi^>vM34KA z@ra2{jQ+d1_3Yc_OpIUKYud}#AMN_KyA3h1K3tMSEk{2&8xw~ZEbm$ROsvR!CXv}r zM7k#&naK8pClho}*uIS9dnAyFLQw?HDHRA*sT2rwT+fKfG%%%$S7zyLubh>=+Tc}7 zWLkfMYKdG+6k4Lx5|x60cI!Q|PJm?Dl`UV{U^*ww6!SgYHoLKLtWy!N&tY&)>W_~dhfj-7%tZ;*$fVt2u4s7a8Id7!B3`SsYqRDEuUjFF_lIW$`dQr z$!ina#gp1Zc)&y`X2Qa|OT_~I?!Ut6gnKAt%6IjPc|m@o<#d3YR;;7fMz)J*Fk-HM zmHz;ZNmnf@7#af^rsm5o13A(-Z=IsIi0MwH{Ax;6- zlZ_!Q9Gb%~#?}J>0MQEo02lxO00000000000000943pL&L;@8MlN=&H0m_qHB02&J z8k3kJCL7I_>ybqP00374000pH000000000000000gp=7KJ^}z9lNlo-2F@S=0000* CobAd0 delta 2107 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 diff --git a/excel/equipment.xlsx b/excel/equipment.xlsx index 8488c3a0aac4e65990f6412e38d202c8b66db9f2..4b91c8ead4751d2e2b2e6a92b19f6f86d3abc4f9 100644 GIT binary patch delta 2417 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 F_+Kh3AeR6D diff --git a/src/ReplicatedStorage/Base/UIList.luau b/src/ReplicatedStorage/Base/UIList.luau index d0d2719..bac759e 100644 --- a/src/ReplicatedStorage/Base/UIList.luau +++ b/src/ReplicatedStorage/Base/UIList.luau @@ -41,6 +41,7 @@ function UIList:Init(Prefab: Instance) self.Instances = {} self.Connections = {} self.LayoutOrderKey = nil + self.reverse = false self.Component = nil self.UIRoot = Prefab self.Org = Utils:FindInDescendantsUI(Prefab, "__org") @@ -89,10 +90,23 @@ function UIList:RemoveData(key) end end -function UIList:SetLayoutOrder(keyName: string) - self.LayoutOrderKey = keyName +function UIList:FindSameDataInstance(data) for _, ui in pairs(self.Instances) do - ui.UIRoot.LayoutOrder = tonumber(ui.Data[keyName]) + if ui.Data == data then + return ui + end + end +end + +function UIList:SetLayoutOrder(keyName: string, reverse: boolean?) + self.LayoutOrderKey = keyName + if reverse then self.reverse = true else self.reverse = false end + for _, ui in pairs(self.Instances) do + if self.reverse then + ui.UIRoot.LayoutOrder = 1000 - tonumber(ui.Data[keyName]) + else + ui.UIRoot.LayoutOrder = tonumber(ui.Data[keyName]) + end end end @@ -106,7 +120,11 @@ function UIList:SetSingleInstance(index: number, data: table) uiInstance.Name = index uiInstance.Parent = self.Org.Parent if self.LayoutOrderKey then - uiInstance.LayoutOrder = tonumber(child.Data[self.LayoutOrderKey]) + if self.reverse then + uiInstance.LayoutOrder = 1000 - tonumber(child.Data[self.LayoutOrderKey]) + else + uiInstance.LayoutOrder = tonumber(child.Data[self.LayoutOrderKey]) + end end self.Instances[index] = child @@ -129,7 +147,10 @@ function UIList:GetMinLayoutOrderInstance() end function UIList:Refresh() - for _, ui in pairs(self.Instances) do ui:Destroy() end + for _, ui in pairs(self.Instances) do + if ui.UIRoot then ui.UIRoot:Destroy() end + ui:Destroy() + end self.Instances = {} if not self.Component then warn("UIList:Refresh() Component未设置") return end diff --git a/src/ReplicatedStorage/Json/AttributesUpgrade.json b/src/ReplicatedStorage/Json/AttributesUpgrade.json index 6510e37..c238de5 100644 --- a/src/ReplicatedStorage/Json/AttributesUpgrade.json +++ b/src/ReplicatedStorage/Json/AttributesUpgrade.json @@ -1,6 +1,6 @@ [ -{"id":1,"type":1,"effectAttribute":"attack","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null}, -{"id":2,"type":1,"effectAttribute":"hp","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null}, +{"id":1,"type":1,"effectAttribute":"attack","cost":[1,10,20],"lvAdd":[100,845],"battleValueLimit":[5,20],"maxLv":null}, +{"id":2,"type":1,"effectAttribute":"hp","cost":[1,10,20],"lvAdd":[2000,845],"battleValueLimit":[5,20],"maxLv":null}, {"id":3,"type":1,"effectAttribute":"swordAtk","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null}, {"id":4,"type":1,"effectAttribute":"swordWearBase","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null}, {"id":5,"type":1,"effectAttribute":"swordWearSpe","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null}, diff --git a/src/ReplicatedStorage/Json/Character.json b/src/ReplicatedStorage/Json/Character.json index 92d553a..dfbc59d 100644 --- a/src/ReplicatedStorage/Json/Character.json +++ b/src/ReplicatedStorage/Json/Character.json @@ -1,3 +1,3 @@ [ -{"id":1,"name":1,"attack":10,"hp":100,"walkSpeed":10,"atkSpeed":1} +{"id":1,"name":1,"attack":100,"hp":2000,"walkSpeed":10,"atkSpeed":1} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Enemy.json b/src/ReplicatedStorage/Json/Enemy.json index 05a2143..e29a0d2 100644 --- a/src/ReplicatedStorage/Json/Enemy.json +++ b/src/ReplicatedStorage/Json/Enemy.json @@ -1,5 +1,5 @@ [ -{"id":1,"type":1,"name":1,"attack":10,"hp":100,"walkSpeed":10,"attackSpeed":2,"model":"Thief"}, +{"id":1,"type":1,"name":1,"attack":83,"hp":400,"walkSpeed":10,"attackSpeed":1,"model":"Thief"}, {"id":2,"type":1,"name":2,"attack":30,"hp":300,"walkSpeed":10,"attackSpeed":1,"model":"Thief"}, -{"id":1000,"type":2,"name":1000,"attack":50,"hp":1000,"walkSpeed":20,"attackSpeed":1,"model":"Thief"} +{"id":1000,"type":2,"name":1000,"attack":240,"hp":2000,"walkSpeed":20,"attackSpeed":1,"model":"Thief"} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Forge.json b/src/ReplicatedStorage/Json/Forge.json index 1a4963c..e3cc33b 100644 --- a/src/ReplicatedStorage/Json/Forge.json +++ b/src/ReplicatedStorage/Json/Forge.json @@ -1,17 +1,17 @@ [ -{"id":1,"cost":[2,20]}, -{"id":2,"cost":[2,30]}, -{"id":3,"cost":[2,40]}, -{"id":4,"cost":[2,50]}, -{"id":5,"cost":[2,60]}, -{"id":6,"cost":[2,70]}, -{"id":7,"cost":[2,80]}, -{"id":8,"cost":[2,90]}, -{"id":9,"cost":[2,100]}, -{"id":10,"cost":[2,110]}, -{"id":11,"cost":[2,120]}, -{"id":12,"cost":[2,130]}, -{"id":13,"cost":[2,140]}, -{"id":14,"cost":[2,150]}, -{"id":15,"cost":[2,160]} +{"id":1,"cost":[1,500]}, +{"id":2,"cost":[1,500]}, +{"id":3,"cost":[1,500]}, +{"id":4,"cost":[1,500]}, +{"id":5,"cost":[1,500]}, +{"id":6,"cost":[1,500]}, +{"id":7,"cost":[1,500]}, +{"id":8,"cost":[1,500]}, +{"id":9,"cost":[1,500]}, +{"id":10,"cost":[1,500]}, +{"id":11,"cost":[1,500]}, +{"id":12,"cost":[1,500]}, +{"id":13,"cost":[1,500]}, +{"id":14,"cost":[1,500]}, +{"id":15,"cost":[1,500]} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Level.json b/src/ReplicatedStorage/Json/Level.json index bc036d9..5dc7665 100644 --- a/src/ReplicatedStorage/Json/Level.json +++ b/src/ReplicatedStorage/Json/Level.json @@ -1,22 +1,52 @@ [ -{"id":1,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1]]}, -{"id":2,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":3,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":4,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":5,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]}, -{"id":6,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":7,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":8,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":9,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":10,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]}, -{"id":11,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":12,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":13,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":14,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":15,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]}, -{"id":16,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":17,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":18,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":19,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1],[10,1,1,10,2,1]]}, -{"id":20,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1000,1]]} +{"id":1,"type":1,"timeLimit":null,"atkBonus":500,"hpBonus":500,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":2,"type":1,"timeLimit":null,"atkBonus":520,"hpBonus":520,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":3,"type":1,"timeLimit":null,"atkBonus":540,"hpBonus":540,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":4,"type":1,"timeLimit":null,"atkBonus":560,"hpBonus":560,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":5,"type":2,"timeLimit":60,"atkBonus":1050,"hpBonus":1050,"wave":[[10,1000,1]]}, +{"id":6,"type":1,"timeLimit":null,"atkBonus":600,"hpBonus":600,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":7,"type":1,"timeLimit":null,"atkBonus":620,"hpBonus":620,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":8,"type":1,"timeLimit":null,"atkBonus":640,"hpBonus":640,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":9,"type":1,"timeLimit":null,"atkBonus":660,"hpBonus":660,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":10,"type":2,"timeLimit":60,"atkBonus":1100,"hpBonus":1100,"wave":[[10,1000,1]]}, +{"id":11,"type":1,"timeLimit":null,"atkBonus":700,"hpBonus":700,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":12,"type":1,"timeLimit":null,"atkBonus":720,"hpBonus":720,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":13,"type":1,"timeLimit":null,"atkBonus":740,"hpBonus":740,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":14,"type":1,"timeLimit":null,"atkBonus":760,"hpBonus":760,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":15,"type":2,"timeLimit":60,"atkBonus":1150,"hpBonus":1150,"wave":[[10,1000,1]]}, +{"id":16,"type":1,"timeLimit":null,"atkBonus":800,"hpBonus":800,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":17,"type":1,"timeLimit":null,"atkBonus":820,"hpBonus":820,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":18,"type":1,"timeLimit":null,"atkBonus":840,"hpBonus":840,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":19,"type":1,"timeLimit":null,"atkBonus":860,"hpBonus":860,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":20,"type":2,"timeLimit":60,"atkBonus":1250,"hpBonus":1250,"wave":[[10,1000,1]]}, +{"id":21,"type":1,"timeLimit":null,"atkBonus":900,"hpBonus":900,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":22,"type":1,"timeLimit":null,"atkBonus":920,"hpBonus":920,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":23,"type":1,"timeLimit":null,"atkBonus":940,"hpBonus":940,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":24,"type":1,"timeLimit":null,"atkBonus":960,"hpBonus":960,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":25,"type":2,"timeLimit":60,"atkBonus":1350,"hpBonus":1350,"wave":[[10,1000,1]]}, +{"id":26,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":27,"type":1,"timeLimit":null,"atkBonus":1020,"hpBonus":1020,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":28,"type":1,"timeLimit":null,"atkBonus":1040,"hpBonus":1040,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":29,"type":1,"timeLimit":null,"atkBonus":1060,"hpBonus":1060,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":30,"type":2,"timeLimit":60,"atkBonus":1500,"hpBonus":1500,"wave":[[10,1000,1]]}, +{"id":31,"type":1,"timeLimit":null,"atkBonus":1100,"hpBonus":1100,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":32,"type":1,"timeLimit":null,"atkBonus":1120,"hpBonus":1120,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":33,"type":1,"timeLimit":null,"atkBonus":1140,"hpBonus":1140,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":34,"type":1,"timeLimit":null,"atkBonus":1160,"hpBonus":1160,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":35,"type":2,"timeLimit":60,"atkBonus":2000,"hpBonus":2000,"wave":[[10,1000,1]]}, +{"id":36,"type":1,"timeLimit":null,"atkBonus":1200,"hpBonus":1200,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":37,"type":1,"timeLimit":null,"atkBonus":1220,"hpBonus":1220,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":38,"type":1,"timeLimit":null,"atkBonus":1240,"hpBonus":1240,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":39,"type":1,"timeLimit":null,"atkBonus":1260,"hpBonus":1260,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":40,"type":2,"timeLimit":60,"atkBonus":2500,"hpBonus":2500,"wave":[[10,1000,1]]}, +{"id":41,"type":1,"timeLimit":null,"atkBonus":1300,"hpBonus":1300,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":42,"type":1,"timeLimit":null,"atkBonus":1320,"hpBonus":1320,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":43,"type":1,"timeLimit":null,"atkBonus":1340,"hpBonus":1340,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":44,"type":1,"timeLimit":null,"atkBonus":1360,"hpBonus":1360,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":45,"type":2,"timeLimit":60,"atkBonus":3000,"hpBonus":3000,"wave":[[10,1000,1]]}, +{"id":46,"type":1,"timeLimit":null,"atkBonus":1400,"hpBonus":1400,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":47,"type":1,"timeLimit":null,"atkBonus":1420,"hpBonus":1420,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":48,"type":1,"timeLimit":null,"atkBonus":1440,"hpBonus":1440,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":49,"type":1,"timeLimit":null,"atkBonus":1460,"hpBonus":1460,"wave":[[10,1,1,50,1,1,100,1,1,150,1,1,200,1,1,250,1,1]]}, +{"id":50,"type":2,"timeLimit":60,"atkBonus":3500,"hpBonus":3500,"wave":[[10,1000,1]]} ] \ No newline at end of file diff --git a/src/ServerStorage/Proxy/BookProxy.luau b/src/ServerStorage/Proxy/BookProxy.luau index 0befcbc..6d7a335 100644 --- a/src/ServerStorage/Proxy/BookProxy.luau +++ b/src/ServerStorage/Proxy/BookProxy.luau @@ -64,7 +64,11 @@ function BookProxy:AddBook(Player: Player, BookId: number, UniqueEquipmentId: nu local EquipmentData = EquipmentProxy:GetEquipmentData(Player, UniqueEquipmentId) if not EquipmentData then return end + + -- 变成字符串,因为在次读取后,是字典 + local BookId = tostring(BookId) local orgBookData = ArchiveProxy.pData[Player.UserId][STORE_NAME].Books[BookId] + -- 检查是否已经存在 if orgBookData then -- 图鉴没原先品质高就返回 @@ -86,6 +90,7 @@ end -- 解锁图鉴 function BookProxy:UnlockBook(Player: Player, BookId: number) + local BookId = tostring(BookId) local pData = Utils:GetPlayerDataFolder(Player) if not pData then return end @@ -102,7 +107,7 @@ end -- 检查是否解锁对应图鉴 function BookProxy:IsBookUnlocked(Player: Player, BookId: number) - return ArchiveProxy.pData[Player.UserId][STORE_NAME].Books[BookId] ~= nil + return ArchiveProxy.pData[Player.UserId][STORE_NAME].Books[tostring(BookId)] end -------------------------------------------------------------------------------- diff --git a/src/ServerStorage/Proxy/EquipmentProxy.luau b/src/ServerStorage/Proxy/EquipmentProxy.luau index a0b63c4..a0696cc 100644 --- a/src/ServerStorage/Proxy/EquipmentProxy.luau +++ b/src/ServerStorage/Proxy/EquipmentProxy.luau @@ -21,7 +21,6 @@ local JsonParam = require(ReplicatedStorage.Json.Param) local RE_PlayerTip = ReplicatedStorage.Events.RE_PlayerTip local RE_WearEquipment = ReplicatedStorage.Events.RE_WearEquipment - --> Constants local STORE_NAME = "Equipment" @@ -219,12 +218,12 @@ function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number) ------------------------------------------------------------ ArchiveProxy.pData[Player.UserId][STORE_NAME][UniqueId] = ResultData - print(ArchiveProxy.pData[Player.UserId][STORE_NAME]) - Utils:CreateDataInstance(Player, UniqueId, ResultData, GetPlayerEquipmentFolder(Player)) + local equipmentInstance = Utils:CreateDataInstance(Player, UniqueId, ResultData, GetPlayerEquipmentFolder(Player)) -- 添加图鉴记录 local BookProxy = require(ServerStorage.Proxy.BookProxy) BookProxy:AddBook(Player, EquipmentId, UniqueId) + return equipmentInstance end -- 回收装备 diff --git a/src/ServerStorage/Proxy/PlayerInfoProxy.luau b/src/ServerStorage/Proxy/PlayerInfoProxy.luau index 3d2a261..4959fdc 100644 --- a/src/ServerStorage/Proxy/PlayerInfoProxy.luau +++ b/src/ServerStorage/Proxy/PlayerInfoProxy.luau @@ -20,6 +20,7 @@ local JsonForge = require(ReplicatedStorage.Json.Forge) local RE_PlayerTip = ReplicatedStorage.Events.RE_PlayerTip local RE_Forge = ReplicatedStorage.Events.RE_Forge local RE_UpgradeAttributes = ReplicatedStorage.Events.RE_UpgradeAttributes +local RE_ShowGetEquipments = ReplicatedStorage.Events.RE_ShowGetEquipments --> Constants local STORE_NAME = "PlayerInfo" @@ -67,6 +68,7 @@ local STATS_DATA = { name = {type = ENUM_STATE_TYPE.String, value = "PlayerName"}, level = {type = ENUM_STATE_TYPE.Number, value = 1}, exp = {type = ENUM_STATE_TYPE.Number, value = 0}, + forge = {type = ENUM_STATE_TYPE.Number, value = 1}, } -- 初始化玩家状态信息(采用额外添加的模式,如果没有写值,就覆盖写入) @@ -75,7 +77,7 @@ local function ExtraAddPlayerStats(Player: Player, StatsData: table) -- 如果列表中不包含信息就添加到表中 for StateKey, StateValue in STATS_DATA do if not StatsData[StateKey] then - StatsData[StateKey] = StateValue + StatsData[StateKey] = StateValue.value end end end @@ -97,7 +99,6 @@ function PlayerInfoProxy:InitPlayer(Player: Player) ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats = {} ArchiveProxy.pData[Player.UserId][STORE_NAME].Items = {} ArchiveProxy.pData[Player.UserId][STORE_NAME].AttributesUpgrade = {} - ArchiveProxy.pData[Player.UserId][STORE_NAME].Forge = 1 end -- 放在外面是为了以后系统新增内容方便(同时不用在初始化数据是做写入了) @@ -108,7 +109,7 @@ function PlayerInfoProxy:InitPlayer(Player: Player) CreateInfoInstance(Player, ItemsFolder, ItemId, ENUM_STATE_TYPE.Number, ItemValue) end for StateKey, StateData in ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats do - CreateInfoInstance(Player, StatsFolder, StateKey, StateData.type, StateData.value) + CreateInfoInstance(Player, StatsFolder, StateKey, STATS_DATA[StateKey].type, StateData) end for AttributeId, AttributeLv in ArchiveProxy.pData[Player.UserId][STORE_NAME].AttributesUpgrade do CreateInfoInstance(Player, AttributesUpgradeFolder, AttributeId, "NumberValue", AttributeLv) @@ -127,7 +128,7 @@ end function PlayerInfoProxy:GetPlayerLevel(Player: Player) if not Player then warn('获取玩家等级失败: ', Player.Name) return end local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME] - return playerInfoData.Stats.level.value + return playerInfoData.Stats.level end function PlayerInfoProxy:GetPlayerAttributesUpgrade(Player: Player, AttributeName: string) @@ -153,7 +154,7 @@ function PlayerInfoProxy:UpgradeAttribute(Player: Player, AttributeId: number) requireMoney = attributeData["cost"][2] requireBattleValue = attributeData["battleValueLimit"][1] else - requireMoney = attributeData["cost"][2] + (nowLv - 1) * attributeData["cost"][3] + requireMoney = math.floor(attributeData["cost"][2] ^ (nowLv - 1) * attributeData["cost"][3] / 100) requireBattleValue = attributeData["battleValueLimit"][1] + (nowLv - 1) * attributeData["battleValueLimit"][2] end @@ -255,6 +256,7 @@ end -- 打造装备 function PlayerInfoProxy:MakeForge(Player: Player, EquipmentId: number, Count: number) if not Player or not EquipmentId then warn('打造装备失败: ', Player.Name,EquipmentId) return end + local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME] local PlayerInfoFolder = GetPlayerInfoFolder(Player) if not PlayerInfoFolder then return end @@ -266,18 +268,23 @@ function PlayerInfoProxy:MakeForge(Player: Player, EquipmentId: number, Count: n return end - -- 锻造数量>1,判断是否到达最高锻造等级 - local ForgeLv = PlayerInfoProxy:GetPlayerInfo(Player).Forge - if Count > 1 then - local MaxForgeLv = Utils:GetMaxIdFromJson(JsonForge, ForgeLv) - if ForgeLv < MaxForgeLv then - RE_PlayerTip:FireClient(Player, "锻造等级已到达最高等级") - return + -- 判断金钱是否足够 + local MaxForgeLv = Utils:GetMaxIdFromJson(JsonForge) + local ShouldCostMoney = 0 + for i = 1, Count do + -- 读取等级并且做最大值限制 + local ForgeLv = ArchiveProxy.pData[Player.UserId][STORE_NAME]["Stats"].forge + ForgeLv = ForgeLv > MaxForgeLv and MaxForgeLv or ForgeLv + + local ForgeData = Utils:GetIdDataFromJson(JsonForge, ForgeLv) + ShouldCostMoney = ShouldCostMoney + ForgeData["cost"][2] + + if ForgeLv > MaxForgeLv then + ForgeLv = MaxForgeLv + else + ForgeLv = ForgeLv + 1 end end - - -- 判断金钱是否足够 - local ShouldCostMoney = Utils:GetIdDataFromJson(JsonForge, ForgeLv)["cost"][2] * Count if not self:HasEnoughItem(Player, 1, ShouldCostMoney) then RE_PlayerTip:FireClient(Player, "金钱不足") return @@ -286,16 +293,19 @@ function PlayerInfoProxy:MakeForge(Player: Player, EquipmentId: number, Count: n self:ChangeItemCount(Player, 1, -ShouldCostMoney) -- 生成对应装备 + local ResultData = {} local EquipmentProxy = require(ServerStorage.Proxy.EquipmentProxy) for i = 1, Count do - EquipmentProxy:AddEquipment(Player, EquipmentId) + local EquipmentInstance = EquipmentProxy:AddEquipment(Player, EquipmentId) + table.insert(ResultData, EquipmentInstance) end -- 锻造升级 - ForgeLv = ForgeLv + Count - ChangeInfoInstance(Player, PlayerInfoFolder, "Forge", ForgeLv) + ArchiveProxy.pData[Player.UserId][STORE_NAME]["Stats"].forge = ArchiveProxy.pData[Player.UserId][STORE_NAME]["Stats"].forge + Count + ChangeInfoInstance(Player, PlayerInfoFolder:FindFirstChild("Stats"), "forge", ArchiveProxy.pData[Player.UserId][STORE_NAME]["Stats"].forge) - -- TODO: 添加对应奖励弹窗 + -- 装备奖励弹窗 + RE_ShowGetEquipments:FireClient(Player, ResultData) end -------------------------------------------------------------------------------- @@ -307,7 +317,8 @@ function PlayerInfoProxy:GetPlayerUpgradeAttributes(Player: Player) local attributes = {} for AttributeId, AttributeLv in playerInfoData.AttributesUpgrade do local attributeData = Utils:GetIdDataFromJson(JsonAttributesUpgrade, AttributeId) - attributes[attributeData["effectAttribute"]] = attributeData["lvAdd"][1] + (AttributeLv - 1) * attributeData["lvAdd"][2] + -- attributes[attributeData["effectAttribute"]] = attributeData["lvAdd"][1] + (AttributeLv - 1) * attributeData["lvAdd"][2] + attributes[attributeData["effectAttribute"]] = math.floor(attributeData["lvAdd"][1] ^ (AttributeLv - 1) * attributeData["lvAdd"][2] / 10000) end return attributes end diff --git a/src/StarterPlayerScripts/ClientMain/HealthBar.luau b/src/StarterPlayerScripts/ClientMain/HealthBar.luau new file mode 100644 index 0000000..3fab74c --- /dev/null +++ b/src/StarterPlayerScripts/ClientMain/HealthBar.luau @@ -0,0 +1,69 @@ +local HealthBar = {} + +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local Workspace = game:GetService("Workspace") +local Players = game:GetService("Players") + +local BoardFolder = ReplicatedStorage:WaitForChild("UI"):WaitForChild("Board") +local HeadBar = BoardFolder:WaitForChild("HeadBar") + +local LocalPlayer = Players.LocalPlayer +local MobsFolder = Workspace:WaitForChild("Mobs"):WaitForChild(LocalPlayer.UserId) +local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait() + +local Connections = {} + +local function SetHp(child, hpFill, Attributes) + local hpValue = Attributes:GetAttribute("hp") + local maxHpValue = Attributes:GetAttribute("maxhp") + hpFill.Size = UDim2.new(hpValue / maxHpValue, 0, 1, 0) +end + +local function SetHealthBar(child, prefab) + local HealthBarInstance = prefab:Clone() + + local CoordinateFrame: CFrame, Size: Vector3 = child:GetBoundingBox() + HealthBarInstance.Adornee = child.HumanoidRootPart + HealthBarInstance.StudsOffsetWorldSpace = Vector3.new(0, (CoordinateFrame.Position.Y+Size.Y/2) - child.HumanoidRootPart.Position.Y+2, 0) + HealthBarInstance.Enabled = true + local hpFill = HealthBarInstance:WaitForChild("Canvas"):WaitForChild("HealthBar"):WaitForChild("Fill") + HealthBarInstance.Parent = child + + local Attributes = child:WaitForChild("Attributes") + local attributeCon = Attributes.AttributeChanged:Connect(function(attribute) + if attribute == "hp" then + SetHp(child, hpFill, Attributes) + end + end) + Connections[child] = attributeCon + + -- 初始化设置血量 + SetHp(child, hpFill, Attributes) +end + +-- 玩家自己特殊监听 +SetHealthBar(Character, HeadBar) + +-- 监听玩家角色重新生成(暂时没放在Connections中) +LocalPlayer.CharacterAdded:Connect(function(newCharacter) + Character = newCharacter + SetHealthBar(Character, HeadBar) +end) + + + +-- TODO: 监听玩家怪物目录下的怪物增减,处理对应血条 +MobsFolder.ChildAdded:Connect(function(child) + if child:IsA("Model") then + SetHealthBar(child, HeadBar) + end +end) + +MobsFolder.ChildRemoved:Connect(function(child) + if Connections[child] then + Connections[child]:Disconnect() + Connections[child] = nil + end +end) + +return HealthBar \ No newline at end of file diff --git a/src/StarterPlayerScripts/ClientMain/Helper.luau b/src/StarterPlayerScripts/ClientMain/Helper.luau index 1168257..9350299 100644 --- a/src/StarterPlayerScripts/ClientMain/Helper.luau +++ b/src/StarterPlayerScripts/ClientMain/Helper.luau @@ -15,7 +15,7 @@ UserInputService.InputBegan:Connect(function(input, gameProcessed) if input.KeyCode == Enum.KeyCode.H then RE_PlayerHelper:FireServer("CleanPlayerData") elseif input.KeyCode == Enum.KeyCode.J then - RE_PlayerHelper:FireServer("AddItem", {1, 100}) + RE_PlayerHelper:FireServer("AddItem", {1, 1000}) elseif input.KeyCode == Enum.KeyCode.K then RE_UpgradeAttributes:FireServer(1) elseif input.KeyCode == Enum.KeyCode.L then diff --git a/src/StarterPlayerScripts/ClientMain/PerformanceClient/init.luau b/src/StarterPlayerScripts/ClientMain/PerformanceClient/init.luau index a526288..7ddccf5 100644 --- a/src/StarterPlayerScripts/ClientMain/PerformanceClient/init.luau +++ b/src/StarterPlayerScripts/ClientMain/PerformanceClient/init.luau @@ -12,6 +12,7 @@ local RE_PerformanceEvent = EventsFolder:FindFirstChild("RE_PerformanceEvent") local RE_CleanPlayerPerformance = EventsFolder:FindFirstChild("RE_CleanPlayerPerformance") local RE_DamagePerformance = EventsFolder:FindFirstChild("RE_DamagePerformance") local RE_AbilityPerformance = EventsFolder:FindFirstChild("RE_AbilityPerformance") +local RE_ShowGetEquipments = EventsFolder:FindFirstChild("RE_ShowGetEquipments") --> Dependencies local UIManager = require(script.Parent.Parent.UI.UIManager) @@ -113,6 +114,13 @@ RE_CleanPlayerPerformance.OnClientEvent:Connect(function(CleanedPlayer: Player) PerformanceClient.pData[UserId] = nil end) +-- 监听获取装备事件调用 +RE_ShowGetEquipments.OnClientEvent:Connect(function(EquipmentDetail: table) + if not UIManager:IsOpened("GetEquipmentsWindow") then + UIManager:OpenWindow("GetEquipmentsWindow", EquipmentDetail) + end +end) + -- 打开默认界面 UIManager:OpenWindow("MainWindow") UIManager:OpenWindow("TipsWindow") diff --git a/src/StarterPlayerScripts/UI/UIManager.luau b/src/StarterPlayerScripts/UI/UIManager.luau index 4f513d5..0fb6a88 100644 --- a/src/StarterPlayerScripts/UI/UIManager.luau +++ b/src/StarterPlayerScripts/UI/UIManager.luau @@ -4,10 +4,98 @@ local FolderWindows = script.Parent.Windows UIManager.Instances = {} UIManager.Windows = {} +UIManager.WindowStack = {} -- 窗口堆栈 +UIManager.ExcludeFromStack = { -- 不受堆栈影响的窗口列表 + "TipsWindow", -- 主窗口 + "AbilityStateWindow", + -- 可以继续添加其他窗口名称 +} + for _, child in FolderWindows:GetChildren() do UIManager.Windows[child.Name] = require(child) end +-- 获取窗口的ZIndex +local function getWindowZIndex(windowInstance) + if not windowInstance or not windowInstance.UIRoot then + return 0 + end + return windowInstance.UIRoot.ZIndex or 0 +end + +-- 设置窗口的Interactable +local function setWindowInteractable(windowInstance, interactable) + if not windowInstance or not windowInstance.UIRoot then + return + end + + -- 根窗口本身就是CanvasGroup + if windowInstance.UIRoot:IsA("CanvasGroup") then + windowInstance.UIRoot.Interactable = interactable + end +end + +-- 检查窗口是否在排除列表中 +local function isExcludedFromStack(windowName) + for _, excludedName in ipairs(UIManager.ExcludeFromStack) do + if windowName == excludedName then + return true + end + end + return false +end + +-- 管理窗口堆栈 +local function manageWindowStack(newWindowName, newWindowInstance) + -- 检查是否在排除列表中 + if isExcludedFromStack(newWindowName) then + return + end + + local newZIndex = getWindowZIndex(newWindowInstance) + + -- 检查堆栈中所有窗口,设置ZIndex较低的窗口为false + for i = #UIManager.WindowStack, 1, -1 do + local stackWindow = UIManager.WindowStack[i] + local stackZIndex = getWindowZIndex(stackWindow.instance) + + -- 如果堆栈中窗口的ZIndex小于等于新窗口,设置为false + if stackZIndex <= newZIndex then + -- 设置之前窗口的Interactable为false + setWindowInteractable(stackWindow.instance, false) + end + end + + -- 将新窗口添加到堆栈顶部 + table.insert(UIManager.WindowStack, { + name = newWindowName, + instance = newWindowInstance, + zIndex = newZIndex + }) +end + +-- 移除窗口堆栈中的窗口 +local function removeFromWindowStack(windowName) + -- 检查是否在排除列表中 + if isExcludedFromStack(windowName) then + return + end + + for i = #UIManager.WindowStack, 1, -1 do + if UIManager.WindowStack[i].name == windowName then + table.remove(UIManager.WindowStack, i) + break + end + end + + -- 如果堆栈不为空,设置顶部窗口的Interactable为true + if #UIManager.WindowStack > 0 then + local topWindow = UIManager.WindowStack[#UIManager.WindowStack] + setWindowInteractable(topWindow.instance, true) + end +end + +-- 打开窗口 function UIManager:OpenWindow(WindowName: string, Data: table?) if not UIManager.Windows[WindowName] then warn("UIManager:OpenWindow() 窗口不存在:" .. WindowName) @@ -16,22 +104,21 @@ function UIManager:OpenWindow(WindowName: string, Data: table?) UIManager.Instances[WindowName] = UIManager.Windows[WindowName]:Init(self, Data) UIManager.Instances[WindowName]:OnOpenWindow() + + -- 管理窗口堆栈 + manageWindowStack(WindowName, UIManager.Instances[WindowName]) end -function UIManager:SetData(WindowName: string, Data: table) - if not UIManager.Instances[WindowName] then - warn("UIManager:SetData() 窗口不存在:" .. WindowName) - return - end - UIManager.Instances[WindowName]:SetData(Data) -end - +-- 关闭窗口 function UIManager:CloseWindow(WindowName: string) if not UIManager.Instances[WindowName] then warn("UIManager:CloseWindow() 窗口不存在:" .. WindowName) return end + -- 从堆栈中移除窗口 + removeFromWindowStack(WindowName) + UIManager.Instances[WindowName]:OnCloseWindow() UIManager.Instances[WindowName]:Destroy() UIManager.Instances[WindowName] = nil @@ -41,5 +128,51 @@ function UIManager:IsOpened(WindowName: string) return UIManager.Instances[WindowName] ~= nil end +-- 获取当前窗口堆栈信息(用于调试) +function UIManager:GetWindowStackInfo() + local info = {} + for i, window in ipairs(UIManager.WindowStack) do + table.insert(info, { + name = window.name, + zIndex = window.zIndex, + index = i + }) + end + return info +end + +-- 打印窗口堆栈(用于调试) +function UIManager:PrintWindowStack() + print("=== 窗口堆栈信息 ===") + for i, window in ipairs(UIManager.WindowStack) do + print(string.format("[%d] %s (ZIndex: %d)", i, window.name, window.zIndex)) + end + print("==================") +end + +-- 添加窗口到排除列表 +function UIManager:AddToExcludeList(windowName) + for _, excludedName in ipairs(UIManager.ExcludeFromStack) do + if windowName == excludedName then + return + end + end + table.insert(UIManager.ExcludeFromStack, windowName) +end + +-- 从排除列表中移除窗口 +function UIManager:RemoveFromExcludeList(windowName) + for i, excludedName in ipairs(UIManager.ExcludeFromStack) do + if windowName == excludedName then + table.remove(UIManager.ExcludeFromStack, i) + return + end + end +end + +-- 获取排除列表 +function UIManager:GetExcludeList() + return UIManager.ExcludeFromStack +end return UIManager \ No newline at end of file diff --git a/src/StarterPlayerScripts/UI/Windows/CreateWindow/init.luau b/src/StarterPlayerScripts/UI/Windows/CreateWindow/init.luau index d3036b6..c901c2e 100644 --- a/src/StarterPlayerScripts/UI/Windows/CreateWindow/init.luau +++ b/src/StarterPlayerScripts/UI/Windows/CreateWindow/init.luau @@ -44,6 +44,7 @@ function CreateWindow:Init(UIManager: table, Data: table?) ["_tmpQuality"] = 0, ["_toggleAutoRecycle"] = 0, ["_imgAutoRecycleActive"] = 0, + ["__moneyCoin"] = 0, } self.MultNumber = 1 self.AutoRecycle = false @@ -107,7 +108,7 @@ function CreateWindow:OnOpenWindow() Quality = child:GetAttribute("quality"), Timestamp = child:GetAttribute("timestamp"), } - self.Variables["__listWeaponPackage"]:AddData(self.Data[child.Name]) + self.Variables["__listWeaponPackage"]:AddData(child.Name, self.Data[child.Name]) end) table.insert(self.Connections, childAddCon) @@ -140,10 +141,12 @@ function CreateWindow:OnOpenWindow() if self.Data then local minInstance = self.Variables["__listWeaponPackage"]:GetMinLayoutOrderInstance() - self:ShowDetailInfo(minInstance.Data) + if minInstance then + self:ShowDetailInfo(minInstance.Data) - self.LastActiveItem = minInstance - minInstance:SetSelected(true) + self.LastActiveItem = minInstance + minInstance:SetSelected(true) + end else self:ShowDetailInfo() end diff --git a/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/AttributeShow.luau b/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/AttributeShow.luau new file mode 100644 index 0000000..4f41d03 --- /dev/null +++ b/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/AttributeShow.luau @@ -0,0 +1,43 @@ +local AttributeShow = {} +AttributeShow.__index = AttributeShow + +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Utils = require(ReplicatedStorage.Tools.Utils) +local Localization = require(ReplicatedStorage.Tools.Localization) +local JsonEquipment = require(ReplicatedStorage.Json.Equipment) +local JsonItemProp = require(ReplicatedStorage.Json.ItemProp) +local JsonAttributes = require(ReplicatedStorage.Json.Attributes) + +function AttributeShow:Init(data: table) + local self = {} + self.Data = data + self.Variables = { + ["_tmpAttributeName"] = 0, + ["_tmpValue"] = 0, + } + + setmetatable(self, AttributeShow) + return self +end + +function AttributeShow:Refresh() + local attributeData = Utils:GetSpecialKeyDataFromJson(JsonAttributes, "effectAttribute", self.Data.attribute) + self.Variables._tmpAttributeName.Text = self.Data.attribute + self.UIRoot.LayoutOrder = 1000 - attributeData.id + + if attributeData.type == 2 then + self.Variables._tmpValue.Text = string.format("%.2f%%", self.Data.value / 100) + else + self.Variables._tmpValue.Text = self.Data.value + end +end + +function AttributeShow:Destroy() + for k, v in pairs(self) do + self[k] = nil + end + self = nil +end + +return AttributeShow \ No newline at end of file diff --git a/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/EquipmentShow.luau b/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/EquipmentShow.luau new file mode 100644 index 0000000..511dd7a --- /dev/null +++ b/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/EquipmentShow.luau @@ -0,0 +1,77 @@ +local EquipmentShow = {} +EquipmentShow.__index = EquipmentShow + +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Utils = require(ReplicatedStorage.Tools.Utils) +local Localization = require(ReplicatedStorage.Tools.Localization) +local JsonEquipment = require(ReplicatedStorage.Json.Equipment) +local JsonItemProp = require(ReplicatedStorage.Json.ItemProp) +local JsonAttributes = require(ReplicatedStorage.Json.Attributes) + +local FolderEquipment = ReplicatedStorage:WaitForChild("Prefabs"):WaitForChild("Equipments") + +function EquipmentShow:Init(data: table) + local self = {} + self.Data = data + self.Variables = { + ["_imgView"] = 0, + ["_btnSelect"] = 0, + } + self.Connections = {} + + setmetatable(self, EquipmentShow) + return self +end + +function EquipmentShow:Refresh() + local equipmentData = Utils:GetIdDataFromJson(JsonEquipment, self.Data["orgId"]) + + -- 模型 + local part = FolderEquipment:FindFirstChild(equipmentData.modelName):Clone() + part.Handle.Position = Vector3.new(0, 0, 0) + part.Handle.CFrame = CFrame.new(0, 0, 0) * CFrame.Angles(math.rad(90), 0, 0) + part.Parent = self.Variables["_imgView"] + self.Prefab = part + + -- 相机 + local viewportCamera = Instance.new("Camera") + self.Variables["_imgView"].CurrentCamera = viewportCamera + viewportCamera.Parent = self.Variables["_imgView"] + viewportCamera.CFrame = CFrame.new(Vector3.new(0, 0, 6), part.Handle.Position) + self.ViewCamera = viewportCamera + + -- 旋转 + self.taskRotation = task.spawn(function() + while true do + self.Prefab.Handle.CFrame = self.Prefab.Handle.CFrame * CFrame.Angles(0, 0, 0.01) + task.wait(0.01) + end + end) +end + +function EquipmentShow:OnInitFinish() + -- 点击事件 + local con = self.Variables["_btnSelect"].Activated:Connect(function() + self.TopUI:ReShowDetail(self.Data) + end) + table.insert(self.Connections, con) +end + +function EquipmentShow:Destroy() + if self.Connections then + for k, v in pairs(self.Connections) do + v:Disconnect() + end + end + if self.taskRotation then + task.cancel(self.taskRotation) + self.taskRotation = nil + end + for k, v in pairs(self) do + self[k] = nil + end + self = nil +end + +return EquipmentShow \ No newline at end of file diff --git a/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/init.luau b/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/init.luau new file mode 100644 index 0000000..09a9b93 --- /dev/null +++ b/src/StarterPlayerScripts/UI/Windows/GetEquipmentsWindow/init.luau @@ -0,0 +1,252 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +--> Dependencies +local UIWindow = require(ReplicatedStorage.Base.UIWindow) +local UIEnums = require(ReplicatedStorage.Base.UIEnums) + +--> Json +local JsonItemProp = require(ReplicatedStorage.Json.ItemProp) +local JsonAttributes = require(ReplicatedStorage.Json.Attributes) + +--> Modules +local Utils = require(ReplicatedStorage.Tools.Utils) +local Localization = require(ReplicatedStorage.Tools.Localization) +local AttributeShow = require(script.AttributeShow) +local EquipmentShow = require(script.EquipmentShow) + +local LocalPlayer = game:GetService("Players").LocalPlayer + +-------------------------------------------------------------------------------- + +local function centerSpecificListItem(scrollingFrame, targetItem) + -- 检查参数是否有效 + if not scrollingFrame or not targetItem then + warn("centerSpecificListItem: 参数无效") + return + end + + -- 等待UI完全加载 + task.wait() + + local canvasSize = scrollingFrame.CanvasSize + local absoluteSize = scrollingFrame.AbsoluteSize + + -- 检查尺寸是否有效 + if not canvasSize or not absoluteSize then + warn("centerSpecificListItem: 尺寸无效", scrollingFrame.Name) + return + end + + -- 获取目标项的位置和尺寸 + local itemPosition = targetItem.Position + local itemSize = targetItem.AbsoluteSize + + -- 检查位置和尺寸是否有效 + if not itemPosition or not itemSize then + warn("centerSpecificListItem: 目标项位置或尺寸无效", targetItem.Name) + return + end + + -- 计算目标项在ScrollingFrame中的绝对位置 + local itemAbsolutePosition = targetItem.AbsolutePosition + + -- 计算目标项的中心位置(相对于ScrollingFrame) + local itemCenterX = itemAbsolutePosition.X + (itemSize.X / 2) + + -- 计算容器中心位置 + local containerCenterX = absoluteSize.X / 2 + + -- 计算目标项在ScrollingFrame内容中的位置 + local currentCanvasPosition = scrollingFrame.CanvasPosition + + -- 计算需要的偏移量,使目标项居中 + -- 目标项中心位置 - 容器中心位置 = 需要的偏移量 + local offsetX = itemCenterX - containerCenterX + + -- 计算新的CanvasPosition(当前位置 + 需要的偏移量) + local newCanvasPositionX = currentCanvasPosition.X + offsetX + + -- 确保不超出边界 + -- CanvasSize是UDim2,需要计算实际的像素宽度 + local contentWidth = canvasSize.X.Offset + (canvasSize.X.Scale * absoluteSize.X) + local maxOffset = contentWidth - absoluteSize.X + + newCanvasPositionX = math.clamp(newCanvasPositionX, 0, math.max(0, maxOffset)) + + -- 设置canvasPosition + scrollingFrame.CanvasPosition = Vector2.new(newCanvasPositionX, 0) +end + +-------------------------------------------------------------------------------- + +local GetEquipmentsWindow = {} +GetEquipmentsWindow.__index = GetEquipmentsWindow +setmetatable(GetEquipmentsWindow, {__index = UIWindow}) + +function GetEquipmentsWindow:Init(UIManager: table, Data: table?) + local self = UIWindow:Init(UIManager, Data) + setmetatable(self, GetEquipmentsWindow) + self.Variables = { + ["_goRewardsPanel"] = 0, + ["_goDetailPanel"] = 0, + + ["_goBase"] = 0, + ["_goExAttributesPanel"] = 0, + ["_goElementPanel"] = 0, + ["_goElementDefPanel"] = 0, + + ["__listBaseAttributes"] = 0, + ["__listExAttributes"] = 0, + ["__listElement"] = 0, + ["__listElementDef"] = 0, + + ["_tmpName"] = 0, + ["_tmpQuality"] = 0, + + ["__listEquipments"] = 0, + ["_btnLeft"] = 0, + ["_btnRight"] = 0, + + ["_btnClose"] = 0, + } + self.UIRootName = "ui_w_get_equipments" + self.UIParentName = UIEnums.UIParent.UIRoot + + return self +end + +function GetEquipmentsWindow:TransformKeyTable(data: table) + local newData = {} + local index = 1 + for k, v in pairs(data) do + newData[index] = {} + newData[index]["attribute"] = k + newData[index]["value"] = v + index = index + 1 + end + return newData +end + +function GetEquipmentsWindow:ShowDetail(equipmentInstance: Instance) + local equipmentData = Utils:GetIdDataFromJson(JsonItemProp, equipmentInstance:GetAttribute("orgId")) + self.Variables["_tmpName"].Text = Localization:GetLanguageData(equipmentData.textId) + self.Variables["_tmpQuality"].Text = equipmentInstance:GetAttribute("quality") + + local baseAttributes = equipmentInstance:FindFirstChild("attributes"):GetAttributes() + local exAttributes = equipmentInstance:FindFirstChild("exAttributes"):GetAttributes() + local elements = equipmentInstance:FindFirstChild("elements"):GetAttributes() + local elementDef = equipmentInstance:FindFirstChild("elementDef"):GetAttributes() + + self.Variables["__listBaseAttributes"]:SetData(self:TransformKeyTable(baseAttributes)) + self.Variables["__listExAttributes"]:SetData(self:TransformKeyTable(exAttributes)) + self.Variables["__listElement"]:SetData(self:TransformKeyTable(elements)) + self.Variables["__listElementDef"]:SetData(self:TransformKeyTable(elementDef)) + + -- 额外属性 + if #exAttributes > 0 then + self.Variables["_goExAttributesPanel"].Visible = true + self.Variables["__listExAttributes"]:SetData(self:TransformKeyTable(exAttributes)) + else + self.Variables["_goExAttributesPanel"].Visible = false + end + + -- 元素属性 + if #elements > 0 then + self.Variables["_goElementPanel"].Visible = true + self.Variables["__listElement"]:SetData(self:TransformKeyTable(elements)) + else + self.Variables["_goElementPanel"].Visible = false + end + + -- 元素定义属性 + if #elementDef > 0 then + self.Variables["_goElementDefPanel"].Visible = true + self.Variables["__listElementDef"]:SetData(self:TransformKeyTable(elementDef)) + else + self.Variables["_goElementDefPanel"].Visible = false + end +end + +function GetEquipmentsWindow:SetCenter(listName, equipmentData) + -- 等待一帧确保UI已加载,然后设置居中 + task.spawn(function() + task.wait() -- 等待一帧 + local targetInstance = self.Variables[listName]:FindSameDataInstance(equipmentData) + if targetInstance and targetInstance.UIRoot then + -- 查找ScrollingFrame + local scrollingFrame = self.Variables[listName].UIRoot + if scrollingFrame:IsA("ScrollingFrame") then + centerSpecificListItem(scrollingFrame, targetInstance.UIRoot) + else + -- 如果不是ScrollingFrame,查找子级中的ScrollingFrame + local scroller = scrollingFrame:FindFirstChildWhichIsA("ScrollingFrame") + if scroller then + centerSpecificListItem(scroller, targetInstance.UIRoot) + end + end + end + end) +end + +function GetEquipmentsWindow:ReShowDetail(equipmentData) + local equipmentInstance + for _, instance in self.Data do + if instance.Name == tostring(equipmentData.id) then + equipmentInstance = instance + break + end + end + self:ShowDetail(equipmentInstance) + self:SetCenter("__listEquipments", equipmentData) +end + +function GetEquipmentsWindow:OnOpenWindow() + UIWindow.OnOpenWindow(self) + + local equipmentsData = {} + local maxQuality = 0 + local showDetailData = nil + + for _, equipmentInstance in self.Data do + local attributes = equipmentInstance:GetAttributes() + equipmentsData[equipmentInstance.Name] = attributes + + -- 同时找到最高品质的装备 + if attributes.quality and attributes.quality > maxQuality then + maxQuality = attributes.quality + showDetailData = attributes + end + end + + self.Variables["__listEquipments"]:AddComponent(EquipmentShow) + self.Variables["__listEquipments"]:SetData(equipmentsData) + self.Variables["__listEquipments"]:SetLayoutOrder("quality", true) + + if not showDetailData then warn("ShowDetail Is Null") return end + + self:SetCenter("__listEquipments", showDetailData) + + -- 基础属性 + self.Variables["__listBaseAttributes"]:AddComponent(AttributeShow) + self.Variables["__listExAttributes"]:AddComponent(AttributeShow) + self.Variables["__listElement"]:AddComponent(AttributeShow) + self.Variables["__listElementDef"]:AddComponent(AttributeShow) + + -- 找到对应的原始equipmentInstance来显示详情 + for _, equipmentInstance in self.Data do + if equipmentInstance:GetAttribute("quality") == showDetailData.quality then + self:ShowDetail(equipmentInstance) + break + end + end + + local closeCon = self.Variables["_btnClose"].Activated:Connect(function() + self.UIManager:CloseWindow(script.Name) + end) + table.insert(self.Connections, closeCon) +end + + + +return GetEquipmentsWindow \ No newline at end of file diff --git a/src/StarterPlayerScripts/UI/Windows/MainWindow/init.luau b/src/StarterPlayerScripts/UI/Windows/MainWindow/init.luau index 0e1b309..050451d 100644 --- a/src/StarterPlayerScripts/UI/Windows/MainWindow/init.luau +++ b/src/StarterPlayerScripts/UI/Windows/MainWindow/init.luau @@ -5,6 +5,13 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") local UIWindow = require(ReplicatedStorage.Base.UIWindow) local UIEnums = require(ReplicatedStorage.Base.UIEnums) +--> Json +local JsonLevel = require(ReplicatedStorage.Json.Level) + +local Utils = require(ReplicatedStorage.Tools.Utils) + +local LocalPlayer = game:GetService("Players").LocalPlayer + -------------------------------------------------------------------------------- local MainWindow = {} @@ -18,6 +25,9 @@ function MainWindow:Init(UIManager: table, Data: table?) ["_btnMainCreate"] = 0, ["_btnMainCha"] = 0, ["_btnMainAttributeUpgrade"] = 0, + + ["_tmpNowLevel"] = 0, + ["_imgBoss"] = 0, } self.UIRootName = "ui_w_main" self.UIParentName = UIEnums.UIParent.UIRoot @@ -25,6 +35,16 @@ function MainWindow:Init(UIManager: table, Data: table?) return self end +function MainWindow:SetShowLevel(level: number) + self.Variables["_tmpNowLevel"].Text = string.format("第%d关", level) + local levelData = Utils:GetIdDataFromJson(JsonLevel, level) + if levelData.type == 2 then + self.Variables["_imgBoss"].Visible = true + else + self.Variables["_imgBoss"].Visible = false + end +end + function MainWindow:OnOpenWindow() UIWindow.OnOpenWindow(self) @@ -41,6 +61,16 @@ function MainWindow:OnOpenWindow() table.insert(self.Connections, createCon) table.insert(self.Connections, chaCon) table.insert(self.Connections, attributeUpgradeCon) + + local playerDataFolder = Utils:WaitPlayerDataFolder(LocalPlayer) + local StatsFolder = playerDataFolder:WaitForChild("PlayerInfo"):WaitForChild("Stats") + local levelCon = StatsFolder.level.Changed:Connect(function(newValue) + self:SetShowLevel(newValue) + end) + + -- 初始值设置 + self:SetShowLevel(StatsFolder.level.Value) + table.insert(self.Connections, levelCon) end