From ed086039a1530f00ff1c12a064ce6f1344619759 Mon Sep 17 00:00:00 2001 From: gechangfu Date: Thu, 21 Aug 2025 17:06:13 +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/Rune.xlsx | Bin 10667 -> 13147 bytes excel/attribute.xlsx | Bin 13408 -> 13942 bytes excel/equipment.xlsx | Bin 15866 -> 15969 bytes .../Data/ServerSignalEnum.luau | 7 + src/ReplicatedStorage/Json/Equipment.json | 32 +-- src/ReplicatedStorage/Json/Rune.json | 51 ++++ .../Modules/EventFilter.luau | 184 ++++++++++++++ .../Modules/EventFilterExample.luau | 230 ++++++++++++++++++ .../Modules/EventFilterQuickStart.luau | 86 +++++++ src/ReplicatedStorage/Modules/Filter.luau | 178 ++++++++++++++ src/ReplicatedStorage/Tools/Rng.luau | 6 + .../Modules/Behaviours/Attack.luau | 30 ++- .../Modules/Runes/RuneBookQualityPurple.luau | 51 ++++ .../Modules/Runes/RuneCritDamageRateFire.luau | 34 +++ .../Modules/Runes/RuneCritDamageRateIce.luau | 34 +++ .../Runes/RuneCritDamageRateLight.luau | 34 +++ .../Runes/RuneCritDamageRateShadow.luau | 34 +++ .../Modules/Runes/RuneFireDamage.luau | 5 +- .../Modules/Runes/RuneIceCoffin.luau | 1 - .../Modules/Runes/RuneIceDamage.luau | 34 +++ .../Modules/Runes/RuneLightDamage.luau | 34 +++ .../Modules/Runes/RuneShadowDamage.luau | 34 +++ .../Modules/Runes/RuneWearElementAttack.luau | 52 ++++ .../Modules/Runes/RuneWearEmptySlot.luau | 70 ++++++ .../Modules/Runes/RuneWearFillSlot.luau | 70 ++++++ .../Modules/Runes/RuneWearHeavySword.luau | 60 +++++ .../Modules/Runes/RuneWearKnife.luau | 60 +++++ .../Modules/Runes/RuneWearStick.luau | 60 +++++ .../Modules/Runes/RuneWearSword.luau | 60 +++++ 29 files changed, 1503 insertions(+), 28 deletions(-) create mode 100644 src/ReplicatedStorage/Data/ServerSignalEnum.luau create mode 100644 src/ReplicatedStorage/Modules/EventFilter.luau create mode 100644 src/ReplicatedStorage/Modules/EventFilterExample.luau create mode 100644 src/ReplicatedStorage/Modules/EventFilterQuickStart.luau create mode 100644 src/ReplicatedStorage/Modules/Filter.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneBookQualityPurple.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneCritDamageRateFire.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneCritDamageRateIce.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneCritDamageRateLight.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneCritDamageRateShadow.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneIceDamage.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneLightDamage.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneShadowDamage.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneWearElementAttack.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneWearEmptySlot.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneWearFillSlot.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneWearHeavySword.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneWearKnife.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneWearStick.luau create mode 100644 src/ServerStorage/Modules/Runes/RuneWearSword.luau diff --git a/excel/Rune.xlsx b/excel/Rune.xlsx index 7bed530a354d02a77022a417b20467209d4c7516..55b536cf13febf63aab06bd9059f16c51ee183bf 100644 GIT binary patch delta 7560 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 delta 5037 zcmaJ_WmJ@1yB-+2V<LG`5Rd_pZiWVt21!9F=?)12X@(Gyk`RWJ z_{8(+d*1Wo>>tm%_p`3+x?`_(?^t|V@#O6=2Kt}{VVx{|9D^2CC`fb@Gd+~tagWx0D`b7IeEQ~$I=huGdx~{dEM;zvS5%bu*stlFhp+xb z-gW>jfmqxtN$wfFa=*D1a`U;XM$$BEw~98dx;0W=1i5i^w!%3h-Od^2R}TpGeluC{V%lyi!HvU-of{O1g(>=bHm^0aCjwWbZf+111cOG3f77}y;| zH*;ZKi|yE|3N?-f^l~hEJ9v4ol8{OqX)~zc0Dm&4(KGnaLz~Bt2FX0yQk<45EJpY2-emXS})+L%zEJjq{2$9q}x_jtF_a z!IUee(!KQRB52=knGj1pT#H_@rd*Am9~v9@5RnAi4#zXb^(o9v4Y;?07`5HKD7AF@ znGc+Kz8f;(-B{CNX4J#DUTiQh*iVag64Ycuz0L^HMeojR7H^6TfN7p;qgd0Zo-l72 z^Tte|6hduMdTe*sf|HIzlD88jM@PsXm$&Z-P*nRExwv$G9;%zIQT%JPKirx30o8Cm9r?G+w7D4vi*M1 z1z2!kTNL+5%<%rr4qWW%PDUxKmKcrtbIwso;YsWEd4x-Kf1Fw>fw~zqeW%qe_vZ0{ z8_{z$4d@u1Iy48?*4!a9aVFdX3mS<_yx}$)(2+@ymlmfQtW;zJk>bP|xT#=Fn zp*K4%zTau`q>bcaItAi4f7*#ku)pTiMq??#*Y}Qhrz{Mf)*)+M-q;#AJF$;PwN^4E z%6CjFgz#=Toc!oHszfg9#Lfe->W8#!_vm97WFX8@*wZ1?hea?QBNANm6HXq?QN^{; zqR3=Hbw(w>xO>ti5nMWkp2KD!F=f3)jDpMu5i-+_i5fq~Id`|wKr0f zqEUPz9o}4fctsx`IF7@*aJ4)H>y4J zpkd#eq3B&)UQjIV#mlIv0%ZVYzoh3G5N#z%<%uLvt%P13VDWwWf{5T#VXbmQR#Oji#4ViP61z84B>Br;dFjsL3Se%n9LB|acy!c!cF&v+c4ORe z-)u#^oqQBI^Z4V1)5uNa+B$s*9qP52`X>BfBk*WAsx$@bwU>HRdwgYz;wuB|?%61O z<^|ze{HQ>jcm6+`O^{y{C@1Y2`+k@{KPMpNoYm;I7@xuqrIbKWR+mEL&r(P>pd0Qr zFy@(zCeog^tho|dp@IPJwTdVd(l2a#TuI~avpfZp9ET6qhQ}`#Z-!mtBRH}WhpFYh zzbgebLl~`$AhDv6dx6TT=gjkpZ`Ja0N=fOCVu=rSa_QyvKf=iZ0!n3jzm{$T&HZvD;c9l%-*OAMUzHCtSq#^M@tcKoFFQ(H}v5YY94wdny7^CQr$yw zHBoEj@~FxHr=`!6UaIV~eypFA3Cj>$>_ySw+!a0UmcK~XO`=D3$&2R(d6ZD3|WFpdHwnZyd zvbk=RU(U;&W4LuKa35iF|6+3dVsf;C{{h4P51I|mAv%YO^$V-oaA@&cmS?^@lp~mk zw9-+nL9hg3{5#Mga*jVR`!4d|>DS5OHURFjg>x9aMJKW<(8+Y3cyY%p9=3@gcSgA@ z3$yY2KdJ@jzohQ+|F2e8`It?N^@qAY)Q~?^fKZ2e7@@PCW^qX92)Ouluw zqWjwGzFIO=7m)u!9U zv!^##uUtL>wvW2F!@~E=b7}+&--YE+w@MvjR8QHTTXjGtbh26#_lOZnBo0sB?N5gc zKTEQiWv~q!jL!JkxKr!4wp&=s%eH-9cZ)zYs2j1$+lsiU@TU9N^4t6d9K z)!OrwAX+&ei1c+%1m8tfSt|dMioVlSEjegI^uy7-#|PJb-u$+uOAawc;#~RZ;L1+? zVdHWl2at^Y@p*$?G#K(B`H`%_2nwSNriQtzqmBUr52u6tdZo?^qQhUj%j+fR&9(+=#QZ4W$8 zhO_9Z$KkRocN^M#V2O9ININ34+#S3+6|I#o(NgZ!i}Va<^ome99zyQcFc&Xr1vYdU z+{=5Bn$(V)cqihTrT7jeQy;wtQM;nN!-R zmCX3V)7XG@92Y#4tjXYOlm8IK_y!(Uj|Pv8a&_yDENa`;Y~y zpWyE(`N)Ttg9Xrb?|mwDJujh~e2e#Zdjwl~Hk5!bmKv-)N5WqRycLXfwN=2(2uizu zruP6LBUum2zkSdMjZRIpemVKPJAkdx(n1*JX`;bfG-3HE@fEVfc|65RbuF=j@Oh^Y zpK1yunzM_$u%Txy>ZEgH)`ITIC#bXsGrcLAq1)$jx*PM&Hw88sqymJB?o)^w!nUWKZq4jDotRjb*%N>HP4hE2qG$)?~r%hf# zo=6{PK^YPH#`MvQhwpW1N~K}^Pt-?f>4K@3q8^>TpVhSnV!>~|gw8FLD6H@d zn(^b_Q`Dnov1}UnNE`4=zm5ZIl{l*_xGB`w=!syW&%tVo=l4CTWz9Z)8?&3E&7X8D zH`hPMHRm03F{VCpH1snwM zzHYc0^iJVHK4dV!#Ek-LK4^%Hb2r00JE&T2LI(}2I%gAfX=0KocGVFlIKWbPP8&h4rUh(xLCW<;43xIQkR-hcRx zQMl74Y*nRJ@)}5*%{d|u%OZt!h3fTrGFW*@KNz{0^ft^QP2$mjYWGuiIb4UJb+Uu0 zjQBX#IuudD%&1TqDRYl~wEmzQ`a;IOA)T3X=AD@xoCUqe$L$M2d&@M z^CK78QD^<$o)cD?5HPDr3VPQZWjZGzp5u{WvfPz1QVw0TW%aC)^CuIN~K}EA{$6Db-eKYM!J_}?SSCo9Ik3}wJtB{M@IAg=i&)+8nT_r)ykNQONBxV zHCX)7g4udug0Vx-o?$C3D{82`7BM=sJrBzVA-0F?6JSEoDZ^N;kSjAe+E$kjPtAr; zsI+HDc$w2>w5Oy+b#L}FTi=`81ZCTmXH@nR0`B%OG{U7`i+7O_78IDdN)H#PZw0+W zW+))L$Qp*_g2{H=7h)mzg9GeqT&pBQvUj?twrD5T)?XxP3$1pyQHDE&wf2O1#%W0^*{GpcOpv6E&=2gV4iYm~9th5NMBES;M&eH29IkDM(h0{mz zyP|LkUV?WdoMOQLzI^F$Yol<$OSw6y|9xx!b@%!^EZ_&+a;Vp&Z~-1Bntu)y00919 z$eq!35AMrDNApi3002P$0%*{|iCM_ugFHkuck8Nz0s!d$>KMfT0`=3wkvw9k@?bbG NF9QbIuO|cm;6LD0P%r=h diff --git a/excel/attribute.xlsx b/excel/attribute.xlsx index 141c8f298a45c93a5f61a56de907e0693c8d9b29..ebf49afe34828b218e31f8a55acd569f7409a9a7 100644 GIT binary patch delta 5754 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 delta 5220 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 D4{6f0 diff --git a/excel/equipment.xlsx b/excel/equipment.xlsx index e1ec9c39c75db4847694d50113cd23b7d673498a..7c21589e9c7237a1a44e52192844f67031c2ae94 100644 GIT binary patch delta 4335 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 M6vZgW@BC5x2ZM*?I{*Lx diff --git a/src/ReplicatedStorage/Data/ServerSignalEnum.luau b/src/ReplicatedStorage/Data/ServerSignalEnum.luau new file mode 100644 index 0000000..25de2ba --- /dev/null +++ b/src/ReplicatedStorage/Data/ServerSignalEnum.luau @@ -0,0 +1,7 @@ +local ServerSignalEnum = {} + +ServerSignalEnum = { + SHOW_ABILITY = "SHOW_ABILITY", +} + +return ServerSignalEnum \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Equipment.json b/src/ReplicatedStorage/Json/Equipment.json index d096c0b..89ce288 100644 --- a/src/ReplicatedStorage/Json/Equipment.json +++ b/src/ReplicatedStorage/Json/Equipment.json @@ -1,18 +1,18 @@ [ -{"id":40000,"type":1,"name":40000,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"MCSword","specialType":2,"specialRequire":20001,"specialActive":[14,25],"recycle":100}, -{"id":40001,"type":1,"name":40001,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, -{"id":40002,"type":1,"name":40002,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, -{"id":40003,"type":1,"name":40003,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, -{"id":40004,"type":1,"name":40004,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, -{"id":40005,"type":1,"name":40005,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, -{"id":40006,"type":1,"name":40006,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, -{"id":40007,"type":1,"name":40007,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100}, -{"id":40008,"type":1,"name":40008,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":6,"specialActive":[14,25],"recycle":100}, -{"id":40009,"type":1,"name":40009,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, -{"id":40010,"type":1,"name":40010,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, -{"id":40011,"type":1,"name":40011,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, -{"id":40012,"type":1,"name":40012,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, -{"id":40013,"type":1,"name":40013,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, -{"id":40014,"type":1,"name":40014,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, -{"id":40015,"type":1,"name":40015,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100} +{"id":40000,"type":1,"subType":1,"name":40000,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"MCSword","specialType":2,"specialRequire":20001,"specialActive":[14,25],"recycle":100}, +{"id":40001,"type":1,"subType":2,"name":40001,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, +{"id":40002,"type":1,"subType":3,"name":40002,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, +{"id":40003,"type":1,"subType":4,"name":40003,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, +{"id":40004,"type":1,"subType":1,"name":40004,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, +{"id":40005,"type":1,"subType":2,"name":40005,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, +{"id":40006,"type":1,"subType":3,"name":40006,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, +{"id":40007,"type":1,"subType":4,"name":40007,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100}, +{"id":40008,"type":1,"subType":1,"name":40008,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":6,"specialActive":[14,25],"recycle":100}, +{"id":40009,"type":1,"subType":2,"name":40009,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":7,"specialActive":[14,25],"recycle":100}, +{"id":40010,"type":1,"subType":3,"name":40010,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":8,"specialActive":[14,25],"recycle":100}, +{"id":40011,"type":1,"subType":4,"name":40011,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":9,"specialActive":[14,25],"recycle":100}, +{"id":40012,"type":1,"subType":1,"name":40012,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":10,"specialActive":[14,25],"recycle":100}, +{"id":40013,"type":1,"subType":2,"name":40013,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":11,"specialActive":[14,25],"recycle":100}, +{"id":40014,"type":1,"subType":3,"name":40014,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":12,"specialActive":[14,25],"recycle":100}, +{"id":40015,"type":1,"subType":4,"name":40015,"attributes":[14,100,0,15,100,0,16,100,0],"modelName":"Zeus","specialType":1,"specialRequire":13,"specialActive":[14,25],"recycle":100} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Json/Rune.json b/src/ReplicatedStorage/Json/Rune.json index 1b012ae..e8e16bd 100644 --- a/src/ReplicatedStorage/Json/Rune.json +++ b/src/ReplicatedStorage/Json/Rune.json @@ -1,5 +1,56 @@ [ {"id":60000,"quality":1,"type":1,"icon":1,"nameId":60000,"runeName":"RuneFireDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60001,"quality":1,"type":2,"icon":1,"nameId":60001,"runeName":"RuneIceDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60002,"quality":1,"type":3,"icon":1,"nameId":60002,"runeName":"RuneLightDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60003,"quality":1,"type":4,"icon":1,"nameId":60003,"runeName":"RuneShadowDamage","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60004,"quality":1,"type":null,"icon":1,"nameId":60004,"runeName":"RuneBookQualityPurple","behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60005,"quality":1,"type":null,"icon":1,"nameId":60005,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60006,"quality":1,"type":null,"icon":1,"nameId":60006,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60007,"quality":1,"type":null,"icon":1,"nameId":60007,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60008,"quality":1,"type":null,"icon":1,"nameId":60008,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60009,"quality":1,"type":null,"icon":1,"nameId":60009,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60010,"quality":1,"type":null,"icon":1,"nameId":60010,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60011,"quality":1,"type":null,"icon":1,"nameId":60011,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60012,"quality":1,"type":null,"icon":1,"nameId":60012,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60013,"quality":1,"type":null,"icon":1,"nameId":60013,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60014,"quality":1,"type":null,"icon":1,"nameId":60014,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60015,"quality":1,"type":null,"icon":1,"nameId":60015,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60016,"quality":1,"type":null,"icon":1,"nameId":60016,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60017,"quality":1,"type":null,"icon":1,"nameId":60017,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60018,"quality":1,"type":null,"icon":1,"nameId":60018,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60019,"quality":1,"type":null,"icon":1,"nameId":60019,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60020,"quality":1,"type":null,"icon":1,"nameId":60020,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60021,"quality":1,"type":null,"icon":1,"nameId":60021,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60022,"quality":1,"type":null,"icon":1,"nameId":60022,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60023,"quality":1,"type":null,"icon":1,"nameId":60023,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":60024,"quality":1,"type":null,"icon":1,"nameId":60024,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, {"id":61000,"quality":2,"type":1,"icon":1,"nameId":61000,"runeName":"RuneIceCoffin","behaviorName":"IceCoffine","recycle":[],"isInPool":1}, +{"id":61001,"quality":2,"type":null,"icon":1,"nameId":61001,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61002,"quality":2,"type":null,"icon":1,"nameId":61002,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61003,"quality":2,"type":null,"icon":1,"nameId":61003,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61004,"quality":2,"type":null,"icon":1,"nameId":61004,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61005,"quality":2,"type":null,"icon":1,"nameId":61005,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61006,"quality":2,"type":null,"icon":1,"nameId":61006,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61007,"quality":2,"type":null,"icon":1,"nameId":61007,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61008,"quality":2,"type":null,"icon":1,"nameId":61008,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61009,"quality":2,"type":null,"icon":1,"nameId":61009,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61010,"quality":2,"type":null,"icon":1,"nameId":61010,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61011,"quality":2,"type":null,"icon":1,"nameId":61011,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61012,"quality":2,"type":null,"icon":1,"nameId":61012,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61013,"quality":2,"type":null,"icon":1,"nameId":61013,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61014,"quality":2,"type":null,"icon":1,"nameId":61014,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61015,"quality":2,"type":null,"icon":1,"nameId":61015,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61016,"quality":2,"type":null,"icon":1,"nameId":61016,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61017,"quality":2,"type":null,"icon":1,"nameId":61017,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61018,"quality":2,"type":null,"icon":1,"nameId":61018,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61019,"quality":2,"type":null,"icon":1,"nameId":61019,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61020,"quality":2,"type":null,"icon":1,"nameId":61020,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61021,"quality":2,"type":null,"icon":1,"nameId":61021,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61022,"quality":2,"type":null,"icon":1,"nameId":61022,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61023,"quality":2,"type":null,"icon":1,"nameId":61023,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61024,"quality":2,"type":null,"icon":1,"nameId":61024,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61025,"quality":2,"type":null,"icon":1,"nameId":61025,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61026,"quality":2,"type":null,"icon":1,"nameId":61026,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, +{"id":61027,"quality":2,"type":null,"icon":1,"nameId":61027,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1}, {"id":62000,"quality":3,"type":1,"icon":1,"nameId":62000,"runeName":null,"behaviorName":null,"recycle":[],"isInPool":1} ] \ No newline at end of file diff --git a/src/ReplicatedStorage/Modules/EventFilter.luau b/src/ReplicatedStorage/Modules/EventFilter.luau new file mode 100644 index 0000000..b662e3e --- /dev/null +++ b/src/ReplicatedStorage/Modules/EventFilter.luau @@ -0,0 +1,184 @@ +--[[ + EventFilter System - 自动回收版本 + 结合Signal和Filter实现事件数据过滤系统 + 支持事件触发时的数据修改和回调处理 + 自动回收机制,无需手动管理生命周期 +]] + +local Signal = require(script.Parent.Signal) +local Filter = require(script.Parent.Filter) + +-- 事件Filter处理器类型 +export type EventFilterHandler = { + handlerFunction: (eventData: any) -> any, + priority: number, + id: string, + eventName: string +} + +-- EventFilter系统类 +local EventFilter = {} +EventFilter.__index = EventFilter +EventFilter.ClassName = "EventFilter" + +export type EventFilter = typeof(EventFilter) + +-- 全局EventFilter实例(使用弱表自动回收) +local GlobalEventFilters = setmetatable({}, {__mode = "k"}) + +function EventFilter.new(name: string?): EventFilter + local eventFilter = setmetatable({ + Name = name or "GlobalEventFilter", + _signals = {}, -- 存储事件信号 + _filters = {}, -- 存储每个事件的Filter + _nextHandlerId = 1, + _weakRefs = setmetatable({}, {__mode = "k"}) -- 弱引用表 + }, EventFilter) + + if name then + GlobalEventFilters[eventFilter] = true + end + + return eventFilter +end + +-- 注册事件(创建Signal和对应的Filter) +function EventFilter:RegisterEvent(eventName: string): Signal + if not self._signals[eventName] then + self._signals[eventName] = Signal.new(eventName) + self._filters[eventName] = Filter.new(eventName .. "_Filter") + end + return self._signals[eventName] +end + +-- 订阅事件Filter(自动回收版本) +function EventFilter:SubscribeFilter(eventName: string, filterFunction: (eventData: any) -> any, priority: number?, owner: any?): string + -- 确保事件已注册 + self:RegisterEvent(eventName) + + local handlerId = tostring(self._nextHandlerId) + self._nextHandlerId = self._nextHandlerId + 1 + + -- 添加到Filter(带所有者引用) + local filterId = self._filters[eventName]:AddHandler(filterFunction, priority, owner) + + -- 如果提供了所有者,创建弱引用 + if owner then + self._weakRefs[owner] = {eventName = eventName, handlerId = handlerId} + end + + return handlerId +end + +-- 取消订阅事件Filter +function EventFilter:UnsubscribeFilter(eventName: string, handlerId: string): boolean + if self._filters[eventName] then + return self._filters[eventName]:RemoveHandler(handlerId) + end + return false +end + +-- 清理无效的处理器(自动回收) +function EventFilter:CleanupFilters() + for eventName, filter in pairs(self._filters) do + filter:CleanupHandlers() + end +end + +-- 发送事件(带Filter处理,自动清理) +function EventFilter:FireWithFilter(eventName: string, eventData: any, callback: (processedData: any) -> ()) + -- 确保事件已注册 + self:RegisterEvent(eventName) + + -- 自动清理无效处理器 + self:CleanupFilters() + + -- 通过Filter处理数据 + self._filters[eventName]:ProcessDataWithCallback(eventData, function(processedData) + -- 触发Signal事件 + self._signals[eventName]:Fire(processedData) + + -- 执行回调 + if callback then + callback(processedData) + end + end) +end + +-- 发送事件(不带Filter,直接触发) +function EventFilter:Fire(eventName: string, eventData: any) + -- 确保事件已注册 + self:RegisterEvent(eventName) + + -- 自动清理无效处理器 + self:CleanupFilters() + + -- 直接触发Signal事件 + self._signals[eventName]:Fire(eventData) +end + +-- 订阅事件(监听处理后的数据) +function EventFilter:Subscribe(eventName: string, callback: (processedData: any) -> ()): any + -- 确保事件已注册 + self:RegisterEvent(eventName) + + -- 订阅Signal事件 + return self._signals[eventName]:Connect(callback) +end + +-- 获取事件Signal +function EventFilter:GetSignal(eventName: string): Signal? + return self._signals[eventName] +end + +-- 获取事件Filter +function EventFilter:GetFilter(eventName: string): Filter? + return self._filters[eventName] +end + +-- 获取所有已注册的事件名称 +function EventFilter:GetRegisteredEvents(): {string} + local events = {} + for eventName, _ in pairs(self._signals) do + table.insert(events, eventName) + end + return events +end + +-- 便捷方法:获取全局EventFilter实例 +function EventFilter.GetGlobal(): EventFilter + for eventFilter, _ in pairs(GlobalEventFilters) do + if eventFilter.Name == "GlobalEventFilter" then + return eventFilter + end + end + + return EventFilter.new("GlobalEventFilter") +end + +-- 便捷方法:全局订阅Filter(带所有者) +function EventFilter.SubscribeGlobalFilter(eventName: string, filterFunction: (eventData: any) -> any, priority: number?, owner: any?): string + return EventFilter.GetGlobal():SubscribeFilter(eventName, filterFunction, priority, owner) +end + +-- 便捷方法:全局取消订阅Filter +function EventFilter.UnsubscribeGlobalFilter(eventName: string, handlerId: string): boolean + return EventFilter.GetGlobal():UnsubscribeFilter(eventName, handlerId) +end + +-- 便捷方法:全局发送事件(带Filter) +function EventFilter.FireGlobalWithFilter(eventName: string, eventData: any, callback: (processedData: any) -> ()) + EventFilter.GetGlobal():FireWithFilter(eventName, eventData, callback) +end + +-- 便捷方法:全局发送事件(不带Filter) +function EventFilter.FireGlobal(eventName: string, eventData: any) + EventFilter.GetGlobal():Fire(eventName, eventData) +end + +-- 便捷方法:全局订阅事件 +function EventFilter.SubscribeGlobal(eventName: string, callback: (processedData: any) -> ()): any + return EventFilter.GetGlobal():Subscribe(eventName, callback) +end + +return EventFilter diff --git a/src/ReplicatedStorage/Modules/EventFilterExample.luau b/src/ReplicatedStorage/Modules/EventFilterExample.luau new file mode 100644 index 0000000..8fc0ddd --- /dev/null +++ b/src/ReplicatedStorage/Modules/EventFilterExample.luau @@ -0,0 +1,230 @@ +--[[ + EventFilter使用例子 + 演示如何使用事件过滤系统 +]] + +local EventFilter = require(script.Parent.EventFilter) + +-- ===== 基础使用示例 ===== + +-- 1. 创建符文对象(作为所有者) +local function createRunes() + local fireRune = { + name = "火焰符文", + level = 5, + element = "fire" + } + + local iceRune = { + name = "冰霜符文", + level = 3, + element = "ice" + } + + local criticalRune = { + name = "暴击符文", + level = 4, + criticalChance = 0.3 + } + + -- 添加Destroy方法(用于自动回收) + function fireRune:Destroy() + print("销毁火焰符文") + for k, v in pairs(self) do self[k] = nil end + self = nil + end + + function iceRune:Destroy() + print("销毁冰霜符文") + for k, v in pairs(self) do self[k] = nil end + self = nil + end + + function criticalRune:Destroy() + print("销毁暴击符文") + for k, v in pairs(self) do self[k] = nil end + self = nil + end + + return fireRune, iceRune, criticalRune +end + +-- 2. 设置符文效果 +local function setupRuneEffects(fireRune, iceRune, criticalRune) + -- 火焰符文:增加50%伤害 + EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + print("🔥 火焰符文生效:伤害 +50%") + eventData.damage = eventData.damage * 1.5 + eventData.element = "fire" + return eventData + end, 10, fireRune) -- 优先级10,所有者是fireRune + + -- 冰霜符文:增加30%伤害,有概率冰冻 + EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + print("❄️ 冰霜符文生效:伤害 +30%") + eventData.damage = eventData.damage * 1.3 + eventData.element = "ice" + + -- 20%概率冰冻 + if math.random() < 0.2 then + eventData.freeze = true + print(" 💎 触发冰冻效果!") + end + + return eventData + end, 8, iceRune) -- 优先级8,所有者是iceRune + + -- 暴击符文:30%概率暴击 + EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + if math.random() < 0.3 then + print("⚡ 暴击符文生效:暴击!") + eventData.damage = eventData.damage * 2 + eventData.isCritical = true + end + return eventData + end, 5, criticalRune) -- 优先级5,所有者是criticalRune +end + +-- 3. 监听最终结果 +local function setupResultListener() + EventFilter.SubscribeGlobal("OnAttack", function(finalData) + print("=== 最终攻击结果 ===") + print("伤害:", finalData.damage) + print("元素:", finalData.element or "无") + print("暴击:", finalData.isCritical and "是" or "否") + print("冰冻:", finalData.freeze and "是" or "否") + print("==================") + end) +end + +-- 4. 发送攻击事件 +local function sendAttackEvent() + local attackData = { + damage = 100, + attacker = "玩家", + target = "敌人" + } + + print("🗡️ 发送攻击事件,基础伤害:", attackData.damage) + + EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(processedData) + print("✅ 攻击事件处理完成!") + -- 这里可以执行实际的攻击逻辑 + -- 比如:DealDamage(processedData.damage, processedData.target) + end) +end + +-- ===== 演示自动回收 ===== + +local function demonstrateAutoCleanup() + print("\n=== 演示自动回收 ===") + + -- 第一次攻击:所有符文生效 + print("\n第一次攻击(所有符文生效):") + sendAttackEvent() + + -- 销毁火焰符文 + print("\n销毁火焰符文...") + fireRune:Destroy() + + -- 第二次攻击:只有冰霜和暴击符文生效 + print("\n第二次攻击(冰霜+暴击符文生效):") + sendAttackEvent() + + -- 销毁冰霜符文 + print("\n销毁冰霜符文...") + iceRune:Destroy() + + -- 第三次攻击:只有暴击符文生效 + print("\n第三次攻击(只有暴击符文生效):") + sendAttackEvent() + + -- 销毁暴击符文 + print("\n销毁暴击符文...") + criticalRune:Destroy() + + -- 第四次攻击:没有符文生效 + print("\n第四次攻击(没有符文生效):") + sendAttackEvent() +end + +-- ===== 实际游戏场景示例 ===== + +local function gameSceneExample() + print("\n=== 实际游戏场景示例 ===") + + -- 模拟玩家攻击 + local function playerAttack() + local attackData = { + damage = 80, + attackType = "melee", + attacker = game.Players.LocalPlayer, + target = nil, -- 目标敌人 + weaponType = "sword" + } + + print("玩家发起攻击...") + + -- 发送攻击事件,让符文系统处理 + EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(finalData) + print("执行攻击:", finalData.damage, "点伤害") + + -- 应用元素效果 + if finalData.element == "fire" then + print("🔥 造成燃烧效果") + elseif finalData.element == "ice" then + print("❄️ 造成冰冻效果") + end + + -- 应用暴击效果 + if finalData.isCritical then + print("⚡ 暴击!") + end + + -- 应用冰冻效果 + if finalData.freeze then + print("💎 目标被冰冻!") + end + end) + end + + -- 执行攻击 + playerAttack() +end + +-- ===== 主函数:完整演示 ===== + +local function runCompleteExample() + print("=== EventFilter使用示例 ===") + + -- 1. 创建符文 + local fireRune, iceRune, criticalRune = createRunes() + print("创建了三个符文") + + -- 2. 设置符文效果 + setupRuneEffects(fireRune, iceRune, criticalRune) + print("设置了符文效果") + + -- 3. 设置结果监听 + setupResultListener() + print("设置了结果监听") + + -- 4. 演示自动回收 + demonstrateAutoCleanup() + + -- 5. 实际游戏场景 + gameSceneExample() + + print("\n=== 示例完成 ===") +end + +-- ===== 导出函数 ===== + +return { + runCompleteExample = runCompleteExample, + createRunes = createRunes, + setupRuneEffects = setupRuneEffects, + sendAttackEvent = sendAttackEvent, + demonstrateAutoCleanup = demonstrateAutoCleanup, + gameSceneExample = gameSceneExample +} diff --git a/src/ReplicatedStorage/Modules/EventFilterQuickStart.luau b/src/ReplicatedStorage/Modules/EventFilterQuickStart.luau new file mode 100644 index 0000000..970887a --- /dev/null +++ b/src/ReplicatedStorage/Modules/EventFilterQuickStart.luau @@ -0,0 +1,86 @@ +--[[ + EventFilter快速入门指南 + 最简单的使用方式 +]] + +local EventFilter = require(script.Parent.EventFilter) + +-- ===== 最简单的使用方式 ===== + +-- 1. 创建符文对象 +local myRune = { + name = "我的符文", + level = 1 +} + +-- 2. 添加Destroy方法(用于自动回收) +function myRune:Destroy() + print("销毁符文:", self.name) + for k, v in pairs(self) do self[k] = nil end + self = nil +end + +-- 3. 让符文订阅事件 +EventFilter.SubscribeGlobalFilter("OnAttack", function(eventData) + print("符文生效:伤害 +50%") + eventData.damage = eventData.damage * 1.5 + return eventData +end, 10, myRune) -- 事件名, 处理函数, 优先级, 所有者 + +-- 4. 发送事件 +local attackData = {damage = 100} +EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(processedData) + print("最终伤害:", processedData.damage) -- 输出:150 +end) + +-- 5. 销毁符文(自动回收) +myRune:Destroy() -- Filter自动失效,不需要手动取消订阅 + +-- ===== 实际游戏中的使用 ===== + +-- 在攻击脚本中: +local function playerAttack() + local attackData = { + damage = 100, + attackType = "melee", + target = enemy + } + + -- 发送攻击事件,让符文系统处理 + EventFilter.FireGlobalWithFilter("OnAttack", attackData, function(finalData) + -- 使用处理后的数据执行实际攻击 + DealDamage(finalData.damage, finalData.target) + end) +end + +-- 在符文脚本中: +local function setupRune(runeInstance) + -- 符文订阅攻击事件 + EventFilter.SubscribeGlobalFilter("OnAttack", function(data) + -- 符文效果:增加伤害 + data.damage = data.damage * 1.2 + return data + end, 5, runeInstance) +end + +-- ===== 常用事件类型 ===== +-- "OnAttack" - 攻击事件 +-- "OnMove" - 移动事件 +-- "OnSkill" - 技能事件 +-- "OnDamage" - 受伤事件 +-- "OnHeal" - 治疗事件 + +-- ===== 优先级说明 ===== +-- 数字越大,优先级越高,越先执行 +-- 10: 最高优先级(比如基础属性修改) +-- 5: 中等优先级(比如元素转换) +-- 1: 最低优先级(比如暴击判定) + +-- ===== 自动回收的好处 ===== +-- 1. 不需要手动管理:符文销毁时Filter自动失效 +-- 2. 内存安全:不会造成内存泄露 +-- 3. 代码简洁:不需要写清理代码 + +return { + -- 这里可以添加一些辅助函数 +} diff --git a/src/ReplicatedStorage/Modules/Filter.luau b/src/ReplicatedStorage/Modules/Filter.luau new file mode 100644 index 0000000..55b862e --- /dev/null +++ b/src/ReplicatedStorage/Modules/Filter.luau @@ -0,0 +1,178 @@ +--[[ + Filter System - 自动回收版本 + 用于实现事件数据的过滤和处理功能 + 支持多个Filter的链式处理、优先级排序和数据回调 + 自动回收机制,无需手动管理生命周期 +]] + +-- Filter处理器类型 +export type FilterHandler = { + handlerFunction: (data: any) -> any, + priority: number, + id: string, + weakRef: any -- 弱引用,用于自动回收 +} + +-- Filter系统类 +local Filter = {} +Filter.__index = Filter +Filter.ClassName = "Filter" + +export type Filter = typeof(Filter) + +-- 全局Filter实例(使用弱表自动回收) +local GlobalFilters = setmetatable({}, {__mode = "k"}) + +function Filter.new(name: string?): Filter + local filter = setmetatable({ + Name = name or "GlobalFilter", + _handlers = {}, -- 存储所有Filter处理器 + _nextHandlerId = 1, + _weakRefs = setmetatable({}, {__mode = "k"}) -- 弱引用表 + }, Filter) + + if name then + GlobalFilters[filter] = true + end + + return filter +end + +-- 添加Filter处理器(自动回收版本) +function Filter:AddHandler(handlerFunction: (data: any) -> any, priority: number?, owner: any?): string + local handlerId = tostring(self._nextHandlerId) + self._nextHandlerId = self._nextHandlerId + 1 + + local handler: FilterHandler = { + handlerFunction = handlerFunction, + priority = priority or 0, + id = handlerId, + weakRef = owner -- 存储所有者引用 + } + + table.insert(self._handlers, handler) + + -- 如果提供了所有者,创建弱引用 + if owner then + self._weakRefs[owner] = handlerId + end + + -- 按优先级排序(优先级高的先执行) + table.sort(self._handlers, function(a, b) + return a.priority > b.priority + end) + + return handlerId +end + +-- 移除Filter处理器 +function Filter:RemoveHandler(handlerId: string): boolean + for i, handler in ipairs(self._handlers) do + if handler.id == handlerId then + table.remove(self._handlers, i) + return true + end + end + return false +end + +-- 清理无效的处理器(自动回收) +function Filter:CleanupHandlers() + local validHandlers = {} + + for _, handler in ipairs(self._handlers) do + local isValid = true + + if handler.weakRef then + -- 检查Instance类型 + if typeof(handler.weakRef) == "Instance" then + -- Instance被销毁时Parent为nil + if handler.weakRef.Parent == nil then + isValid = false + end + else + -- 对于table类型(如Rune类),检查是否有Destroy方法且是否已被销毁 + if type(handler.weakRef) == "table" then + -- 检查是否还在弱引用表中(如果不在,说明已被垃圾回收) + if not self._weakRefs[handler.weakRef] then + isValid = false + end + end + end + end + + if isValid then + table.insert(validHandlers, handler) + end + end + + self._handlers = validHandlers +end + +-- 处理数据(链式Filter,自动清理) +function Filter:ProcessData(data: any): any + -- 自动清理无效处理器 + self:CleanupHandlers() + + local processedData = data + + for _, handler in ipairs(self._handlers) do + local success, result = pcall(handler.handlerFunction, processedData) + if success and result ~= nil then + processedData = result + end + end + + return processedData +end + +-- 带回调的数据处理(用于事件系统) +function Filter:ProcessDataWithCallback(data: any, callback: (processedData: any) -> ()) + local processedData = self:ProcessData(data) + callback(processedData) +end + +-- 获取处理器数量 +function Filter:GetHandlerCount(): number + self:CleanupHandlers() + return #self._handlers +end + +-- 获取所有处理器信息 +function Filter:GetHandlers(): {FilterHandler} + self:CleanupHandlers() + return self._handlers +end + +-- 便捷方法:创建全局Filter实例 +function Filter.GetGlobal(): Filter + for filter, _ in pairs(GlobalFilters) do + if filter.Name == "GlobalFilter" then + return filter + end + end + + return Filter.new("GlobalFilter") +end + +-- 便捷方法:快速添加处理器(带所有者) +function Filter.AddGlobalHandler(handlerFunction: (data: any) -> any, priority: number?, owner: any?): string + return Filter.GetGlobal():AddHandler(handlerFunction, priority, owner) +end + +-- 便捷方法:快速移除处理器 +function Filter.RemoveGlobalHandler(handlerId: string): boolean + return Filter.GetGlobal():RemoveHandler(handlerId) +end + +-- 便捷方法:快速处理数据 +function Filter.ProcessGlobalData(data: any): any + return Filter.GetGlobal():ProcessData(data) +end + +-- 便捷方法:快速处理数据带回调 +function Filter.ProcessGlobalDataWithCallback(data: any, callback: (processedData: any) -> ()) + Filter.GetGlobal():ProcessDataWithCallback(data, callback) +end + +return Filter diff --git a/src/ReplicatedStorage/Tools/Rng.luau b/src/ReplicatedStorage/Tools/Rng.luau index a2f0ccb..f835e42 100644 --- a/src/ReplicatedStorage/Tools/Rng.luau +++ b/src/ReplicatedStorage/Tools/Rng.luau @@ -154,6 +154,12 @@ function Random:GetRandomInt(min: number, max: number) return math.random(min, max) end +-- 百分比概率随机 +function Random:RandomPercent(percent: number) + -- 随机1-100,如果随机值小于等于百分比,则返回true,否则返回false + local randomValue = self:GetRandomInt(1, 100) + return randomValue <= percent +end -- 初始化随机种子 diff --git a/src/ServerStorage/Modules/Behaviours/Attack.luau b/src/ServerStorage/Modules/Behaviours/Attack.luau index 092c555..958edaf 100644 --- a/src/ServerStorage/Modules/Behaviours/Attack.luau +++ b/src/ServerStorage/Modules/Behaviours/Attack.luau @@ -10,6 +10,7 @@ local Behaviour = require(ServerStorage.Base.Behaviour) local MobsProxy = require(ServerStorage.Proxy.MobsProxy) local DamageProxy = require(ServerStorage.Proxy.DamageProxy) local Utils = require(ReplicatedStorage.Tools.Utils) +local Rng = require(ReplicatedStorage.Tools.Rng) -------------------------------------------------------------------------------- @@ -20,11 +21,11 @@ setmetatable(Attack, {__index = Behaviour}) local CAST_DISTANCE = 8 local COOLDOWN = 1 local ATTRIBUTE_LIST = { - {Name = "attack", ElementType = DamageProxy.ElementType.NONE}, - {Name = "fireAtk", ElementType = DamageProxy.ElementType.FIRE}, - {Name = "iceAtk", ElementType = DamageProxy.ElementType.ICE}, - {Name = "lightAtk", ElementType = DamageProxy.ElementType.LIGHT}, - {Name = "shadowAtk", ElementType = DamageProxy.ElementType.SHADOW}, + {Name = "attack", ElementType = DamageProxy.ElementType.NONE, CritCheckRateName = "critRate", CritDamageRateName = "critDamageRate"}, + {Name = "fireAtk", ElementType = DamageProxy.ElementType.FIRE, CritCheckRateName = "critRateFire", CritDamageRateName = "critDamageRateFire"}, + {Name = "iceAtk", ElementType = DamageProxy.ElementType.ICE, CritCheckRateName = "critRateIce", CritDamageRateName = "critDamageRateIce"}, + {Name = "lightAtk", ElementType = DamageProxy.ElementType.LIGHT, CritCheckRateName = "critRateLight", CritDamageRateName = "critDamageRateLight"}, + {Name = "shadowAtk", ElementType = DamageProxy.ElementType.SHADOW, CritCheckRateName = "critRateShadow", CritDamageRateName = "critDamageRateShadow"}, } @@ -95,17 +96,28 @@ function Attack:Execute() }) -- 攻击前摇 task.wait(atkSpeed) - - -- TODO: 之后这里可以提前做暴击判定 - + -- 伤害逻辑计算部分 local damageData = {} for _, attribute in ATTRIBUTE_LIST do local attributeValue = self:GetAttributeValue(attribute.Name) if attributeValue then + + -- 暴击判定 + local DamageType = DamageProxy.DamageType.NORMAL + local critCheckRate = self:GetAttributeValue(attribute.CritCheckRateName) or 0 + local critDamageRate = self:GetAttributeValue(attribute.CritDamageRateName) or 0 + if critCheckRate then + if Rng:RandomPercent(critCheckRate) then + attributeValue = attributeValue * (1 + (200 + critDamageRate) / 100) + DamageType = DamageProxy.DamageType.CRIT + end + end + + -- 记录伤害数据 table.insert(damageData, { Damage = attributeValue, - Type = DamageProxy.DamageType.NORMAL, + Type = DamageType, Tag = DamageProxy.DamageTag.NORMAL, ElementType = attribute.ElementType, }) diff --git a/src/ServerStorage/Modules/Runes/RuneBookQualityPurple.luau b/src/ServerStorage/Modules/Runes/RuneBookQualityPurple.luau new file mode 100644 index 0000000..7068627 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneBookQualityPurple.luau @@ -0,0 +1,51 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneBookQualityPurple = {} +RuneBookQualityPurple.__index = RuneBookQualityPurple +setmetatable(RuneBookQualityPurple, {__index = Rune}) + + +function RuneBookQualityPurple:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneBookQualityPurple) + + return self +end + +function RuneBookQualityPurple:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneBookQualityPurple:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local bookFolder = pData:FindFirstChild("Book") + if not bookFolder then return nil end + local bookList = bookFolder:GetChildren() + if #bookList == 0 then return nil end + + local qualityNumber = 0 + for _, book in bookList do + local bookQuality = book:GetAttribute("quality") + if bookQuality >= 3 then + qualityNumber = qualityNumber + 1 + end + end + local attackRate = math.floor(qualityNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneBookQualityPurple \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateFire.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateFire.luau new file mode 100644 index 0000000..433bf77 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateFire.luau @@ -0,0 +1,34 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneCritDamageRateFire = {} +RuneCritDamageRateFire.__index = RuneCritDamageRateFire +setmetatable(RuneCritDamageRateFire, {__index = Rune}) + + +function RuneCritDamageRateFire:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateFire) + + return self +end + +function RuneCritDamageRateFire:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateFire:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateFire or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateFire", addAttribute) + return nil +end + + +return RuneCritDamageRateFire \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateIce.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateIce.luau new file mode 100644 index 0000000..ee3591b --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateIce.luau @@ -0,0 +1,34 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneCritDamageRateIce = {} +RuneCritDamageRateIce.__index = RuneCritDamageRateIce +setmetatable(RuneCritDamageRateIce, {__index = Rune}) + + +function RuneCritDamageRateIce:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateIce) + + return self +end + +function RuneCritDamageRateIce:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateIce:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateIce or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateIce", addAttribute) + return nil +end + + +return RuneCritDamageRateIce \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateLight.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateLight.luau new file mode 100644 index 0000000..291f0d9 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateLight.luau @@ -0,0 +1,34 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneCritDamageRateLight = {} +RuneCritDamageRateLight.__index = RuneCritDamageRateLight +setmetatable(RuneCritDamageRateLight, {__index = Rune}) + + +function RuneCritDamageRateLight:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateLight) + + return self +end + +function RuneCritDamageRateLight:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateLight:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateLight or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateLight", addAttribute) + return nil +end + + +return RuneCritDamageRateLight \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneCritDamageRateShadow.luau b/src/ServerStorage/Modules/Runes/RuneCritDamageRateShadow.luau new file mode 100644 index 0000000..b13ab42 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneCritDamageRateShadow.luau @@ -0,0 +1,34 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneCritDamageRateShadow = {} +RuneCritDamageRateShadow.__index = RuneCritDamageRateShadow +setmetatable(RuneCritDamageRateShadow, {__index = Rune}) + + +function RuneCritDamageRateShadow:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneCritDamageRateShadow) + + return self +end + +function RuneCritDamageRateShadow:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneCritDamageRateShadow:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local nowAttribute = AttributesData.critDamageRateShadow or 200 + local addAttribute = math.floor(nowAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "critDamageRateShadow", addAttribute) + return nil +end + + +return RuneCritDamageRateShadow \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneFireDamage.luau b/src/ServerStorage/Modules/Runes/RuneFireDamage.luau index a19216f..31aadaa 100644 --- a/src/ServerStorage/Modules/Runes/RuneFireDamage.luau +++ b/src/ServerStorage/Modules/Runes/RuneFireDamage.luau @@ -24,8 +24,9 @@ function RuneFireDamage:Check(index: number, AttributesData: table, BehaviorName end function RuneFireDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) - print("RuneFireDamage:OnExecute", index, AttributesData, BehaviorNameList) - Utils:TableSafeAddValue(AttributesData, "fireAtk", 100) + local baseAttribute = AttributesData.fireAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "fireAtk", addAttribute) return nil end diff --git a/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau b/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau index 4a40c2e..ebf2f9e 100644 --- a/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau +++ b/src/ServerStorage/Modules/Runes/RuneIceCoffin.luau @@ -24,7 +24,6 @@ function RuneIceCoffin:Check(index: number, AttributesData: table, BehaviorNameL end function RuneIceCoffin:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) - print("RuneIceCoffin:OnExecute", index, AttributesData, BehaviorNameList) local maxRecover = self.PlayerAI:GetSharedData("IceCoffin_MaxRecover") if maxRecover then self.PlayerAI:SetSharedData("IceCoffin_MaxRecover", maxRecover + 60) diff --git a/src/ServerStorage/Modules/Runes/RuneIceDamage.luau b/src/ServerStorage/Modules/Runes/RuneIceDamage.luau new file mode 100644 index 0000000..a223c64 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneIceDamage.luau @@ -0,0 +1,34 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneIceDamage = {} +RuneIceDamage.__index = RuneIceDamage +setmetatable(RuneIceDamage, {__index = Rune}) + + +function RuneIceDamage:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneIceDamage) + + return self +end + +function RuneIceDamage:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneIceDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.iceAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "iceAtk", addAttribute) + return nil +end + + +return RuneIceDamage \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneLightDamage.luau b/src/ServerStorage/Modules/Runes/RuneLightDamage.luau new file mode 100644 index 0000000..f3231f9 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneLightDamage.luau @@ -0,0 +1,34 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneLightDamage = {} +RuneLightDamage.__index = RuneLightDamage +setmetatable(RuneLightDamage, {__index = Rune}) + + +function RuneLightDamage:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneLightDamage) + + return self +end + +function RuneLightDamage:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneLightDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.lightAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "lightAtk", addAttribute) + return nil +end + + +return RuneLightDamage \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneShadowDamage.luau b/src/ServerStorage/Modules/Runes/RuneShadowDamage.luau new file mode 100644 index 0000000..6887a46 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneShadowDamage.luau @@ -0,0 +1,34 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneShadowDamage = {} +RuneShadowDamage.__index = RuneShadowDamage +setmetatable(RuneShadowDamage, {__index = Rune}) + + +function RuneShadowDamage:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneShadowDamage) + + return self +end + +function RuneShadowDamage:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneShadowDamage:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local baseAttribute = AttributesData.shadowAtk or 100 + local addAttribute = math.floor(baseAttribute * 50 / 100) + Utils:TableSafeAddValue(AttributesData, "shadowAtk", addAttribute) + return nil +end + + +return RuneShadowDamage \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearElementAttack.luau b/src/ServerStorage/Modules/Runes/RuneWearElementAttack.luau new file mode 100644 index 0000000..6d4252b --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearElementAttack.luau @@ -0,0 +1,52 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneWearElementAttack = {} +RuneWearElementAttack.__index = RuneWearElementAttack +setmetatable(RuneWearElementAttack, {__index = Rune}) + + +function RuneWearElementAttack:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearElementAttack) + + return self +end + +function RuneWearElementAttack:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearElementAttack:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + local elementNumber = 0 + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + elementNumber = elementNumber + #equipment:FindFirstChild("Element"):GetAttributes() + end + end + + local attackRate = math.floor(elementNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearElementAttack \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearEmptySlot.luau b/src/ServerStorage/Modules/Runes/RuneWearEmptySlot.luau new file mode 100644 index 0000000..6c4508a --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearEmptySlot.luau @@ -0,0 +1,70 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneWearEmptySlot = {} +RuneWearEmptySlot.__index = RuneWearEmptySlot +setmetatable(RuneWearEmptySlot, {__index = Rune}) + + +function RuneWearEmptySlot:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearEmptySlot) + + return self +end + +function RuneWearEmptySlot:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearEmptySlot:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + local runeFolder = pData:FindFirstChild("Rune") + if not runeFolder then return nil end + local runeList = runeFolder:GetChildren() + if #runeList == 0 then return nil end + + + local maxRuneNumber = 0 + local hasSlotNumber = 0 + local wearWeaponList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + maxRuneNumber = maxRuneNumber + 1 + table.insert(wearWeaponList, equipment.Name) + end + end + + for _, rune in runeList do + local runeWearing = rune:GetAttribute("wearing") + if runeWearing then + if table.find(wearWeaponList, tostring(runeWearing)) then + hasSlotNumber = hasSlotNumber + 1 + end + end + end + + local attackRate = math.floor((maxRuneNumber - hasSlotNumber) * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearEmptySlot \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearFillSlot.luau b/src/ServerStorage/Modules/Runes/RuneWearFillSlot.luau new file mode 100644 index 0000000..ae8085a --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearFillSlot.luau @@ -0,0 +1,70 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneWearFillSlot = {} +RuneWearFillSlot.__index = RuneWearFillSlot +setmetatable(RuneWearFillSlot, {__index = Rune}) + + +function RuneWearFillSlot:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearFillSlot) + + return self +end + +function RuneWearFillSlot:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearFillSlot:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + local runeFolder = pData:FindFirstChild("Rune") + if not runeFolder then return nil end + local runeList = runeFolder:GetChildren() + if #runeList == 0 then return nil end + + + local maxRuneNumber = 0 + local hasSlotNumber = 0 + local wearWeaponList = {} + for _, equipment in equipmentList do + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentWearing > 0 then + maxRuneNumber = maxRuneNumber + 1 + table.insert(wearWeaponList, equipment.Name) + end + end + + for _, rune in runeList do + local runeWearing = rune:GetAttribute("wearing") + if runeWearing then + if table.find(wearWeaponList, tostring(runeWearing)) then + hasSlotNumber = hasSlotNumber + 1 + end + end + end + + local attackRate = math.floor(hasSlotNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearFillSlot \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearHeavySword.luau b/src/ServerStorage/Modules/Runes/RuneWearHeavySword.luau new file mode 100644 index 0000000..4fe1976 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearHeavySword.luau @@ -0,0 +1,60 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneWearHeavySword = {} +RuneWearHeavySword.__index = RuneWearHeavySword +setmetatable(RuneWearHeavySword, {__index = Rune}) + + +function RuneWearHeavySword:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearHeavySword) + + return self +end + +function RuneWearHeavySword:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearHeavySword:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + + local CheckSubType = 4 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagHeavySword" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearHeavySword \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearKnife.luau b/src/ServerStorage/Modules/Runes/RuneWearKnife.luau new file mode 100644 index 0000000..c930bba --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearKnife.luau @@ -0,0 +1,60 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneWearKnife = {} +RuneWearKnife.__index = RuneWearKnife +setmetatable(RuneWearKnife, {__index = Rune}) + + +function RuneWearKnife:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearKnife) + + return self +end + +function RuneWearKnife:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearKnife:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + + local CheckSubType = 1 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagKnife" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearKnife \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearStick.luau b/src/ServerStorage/Modules/Runes/RuneWearStick.luau new file mode 100644 index 0000000..ecdcf39 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearStick.luau @@ -0,0 +1,60 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneWearStick = {} +RuneWearStick.__index = RuneWearStick +setmetatable(RuneWearStick, {__index = Rune}) + + +function RuneWearStick:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearStick) + + return self +end + +function RuneWearStick:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearStick:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + + local CheckSubType = 3 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagStick" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearStick \ No newline at end of file diff --git a/src/ServerStorage/Modules/Runes/RuneWearSword.luau b/src/ServerStorage/Modules/Runes/RuneWearSword.luau new file mode 100644 index 0000000..7a49424 --- /dev/null +++ b/src/ServerStorage/Modules/Runes/RuneWearSword.luau @@ -0,0 +1,60 @@ +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Dependencies +local Utils = require(ReplicatedStorage.Tools.Utils) +local TypeList = require(ServerStorage.Base.TypeList) +local Rune = require(ServerStorage.Base.Rune) + +local RuneWearSword = {} +RuneWearSword.__index = RuneWearSword +setmetatable(RuneWearSword, {__index = Rune}) + + +function RuneWearSword:Init(PlayerAI, Character: TypeList.Character) + local self = Rune:Init(PlayerAI, Character, script.Name) + setmetatable(self, RuneWearSword) + + return self +end + +function RuneWearSword:Check(index: number, AttributesData: table, BehaviorNameList: table) + return true +end + +function RuneWearSword:OnExecute(index: number, AttributesData: table, BehaviorNameList: table) + local pDataFolder = ReplicatedStorage:FindFirstChild("PlayerData") + if not pDataFolder then return nil end + local pData = pDataFolder:FindFirstChild(self.PlayerAI.Player.UserId) + if not pData then return nil end + + local equipmentFolder = pData:FindFirstChild("Equipment") + if not equipmentFolder then return nil end + local equipmentList = equipmentFolder:GetChildren() + if #equipmentList == 0 then return nil end + + + local CheckSubType = 2 + local subTypeNumber = 0 + for _, equipment in equipmentList do + local equipmentSubType = equipment:GetAttribute("subType") + local equipmentWearing = equipment:GetAttribute("wearing") + if equipmentSubType == CheckSubType and equipmentWearing > 0 then + subTypeNumber = subTypeNumber + 1 + end + end + + local CheckShareName = "RuneTagSword" + if self.PlayerAI:GetSharedData(CheckShareName) then + subTypeNumber = subTypeNumber + self.PlayerAI:GetSharedData(CheckShareName) + end + + local attackRate = math.floor(subTypeNumber * 25 / 100) + Utils:TableSafeAddValue(AttributesData, "attackRate", attackRate) + + return nil +end + + +return RuneWearSword \ No newline at end of file