From 77c3f126340a459d7e611e565e4a5a2b70831f30 Mon Sep 17 00:00:00 2001 From: Conan Date: Mon, 11 Nov 2024 18:52:35 +0100 Subject: [PATCH 01/33] Stops rewarding Pokemon EXP upon forfeiting NPC battles. --- .../mod/common/api/battles/model/PokemonBattle.kt | 11 ++++++++++- .../com/cobblemon/mod/common/api/npc/NPCClass.kt | 2 +- .../cobblemon/mod/common/entity/npc/NPCBattleActor.kt | 6 ------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt index 3fc37c722d..f28f16058e 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt @@ -271,12 +271,13 @@ open class PokemonBattle( else -> continue } val experience = Cobblemon.experienceCalculator.calculate(opponentPokemon, faintedPokemon, multiplier) - if (experience > 0) { + if (experience > 0 && actor.pokemonList.all { it.health <= 0 }) { opponent.awardExperience(opponentPokemon, experience) } Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) -> pokemon.evs.add(stat, amount) } + } } } @@ -287,6 +288,14 @@ open class PokemonBattle( .forEach { player?.giveOrDropItemStack(ItemStack(it))} } } + + // Heal NPC's team if enabled + if (actor is NPCBattleActor) { + if (actor.npc.npc.autoHealParty) { + actor.npc.party?.heal() + } + } + } // Heal Mon if wild actors.filter { it.type == ActorType.WILD } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt index 076b41c7ae..9834fdcda2 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt @@ -47,7 +47,7 @@ class NPCClass { var variables = mutableMapOf() // Questionable whether this should be here. var party: NPCPartyProvider? = null var skill: Int = 0 - var autoHealParty: Boolean = false + var autoHealParty: Boolean = true var battleTheme: ResourceLocation? = null var ai: MutableList = mutableListOf() diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCBattleActor.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCBattleActor.kt index ae1ed831ba..1d593d07e9 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCBattleActor.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCBattleActor.kt @@ -79,17 +79,11 @@ class NPCBattleActor( override fun win(otherWinners: List, losers: List) { super.win(otherWinners, losers) npc.playAnimation(NPCEntity.WIN_ANIMATION) - if (npc.npc.autoHealParty) { - npc.party?.heal() - } } override fun lose(winners: List, otherLosers: List) { super.lose(winners, otherLosers) npc.playAnimation(NPCEntity.LOSE_ANIMATION) - if (npc.npc.autoHealParty) { - npc.party?.heal() - } // winners.forEach { // rewards.forEach { // winner give reward :)) From 9e22b662a415fcfa3e9fe6f1182baf6ed409e36a Mon Sep 17 00:00:00 2001 From: mia <15349355-miasmusa@users.noreply.gitlab.com> Date: Tue, 12 Nov 2024 15:23:22 -0500 Subject: [PATCH 02/33] remove duplicate sand_attack sounds and re-add to particles (fixes #1156) --- .../sand_attack/sandattack_actor.particle.json | 10 ++++++++++ .../sand_attack/sandattack_impact.particle.json | 10 ++++++++++ .../main/resources/assets/cobblemon/sounds.json | 4 ++-- .../move/sandattack/sand_attack_actor.ogg | Bin 27715 -> 0 bytes .../move/sandattack/sand_attack_target.ogg | Bin 20122 -> 0 bytes 5 files changed, 22 insertions(+), 2 deletions(-) delete mode 100644 common/src/main/resources/assets/cobblemon/sounds/move/sandattack/sand_attack_actor.ogg delete mode 100644 common/src/main/resources/assets/cobblemon/sounds/move/sandattack/sand_attack_target.ogg diff --git a/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_actor.particle.json b/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_actor.particle.json index 301d9fb649..6131c92ae1 100644 --- a/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_actor.particle.json +++ b/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_actor.particle.json @@ -8,6 +8,13 @@ "texture": "textures/particles/powder" } }, + "events": { + "event_sound": { + "sound_effect": { + "event_name": "cobblemon:move.sandattack.actor" + } + } + }, "components": { "minecraft:emitter_rate_steady": { "spawn_rate": 500, @@ -16,6 +23,9 @@ "minecraft:emitter_lifetime_looping": { "active_time": 1 }, + "minecraft:emitter_lifetime_events": { + "creation_event": "event_sound" + }, "minecraft:emitter_shape_point": { "offset": [0, 0.05, "-(q.entity_radius*0.7))"], "direction": ["math.random(-0.81,0.81)", "math.random(0.95,1.55)", "math.random(-4,-1)"] diff --git a/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_impact.particle.json b/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_impact.particle.json index a7fa0d17e8..23327cde36 100644 --- a/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_impact.particle.json +++ b/common/src/main/resources/assets/cobblemon/bedrock/particles/moves/sand_attack/sandattack_impact.particle.json @@ -8,7 +8,17 @@ "texture": "textures/particles/powder" } }, + "events": { + "event_sound": { + "sound_effect": { + "event_name": "cobblemon:move.sandattack.target" + } + } + }, "components": { + "minecraft:emitter_lifetime_events": { + "creation_event": "event_sound" + }, "minecraft:emitter_rate_steady": { "spawn_rate": 500, "max_particles": 25 diff --git a/common/src/main/resources/assets/cobblemon/sounds.json b/common/src/main/resources/assets/cobblemon/sounds.json index f7a4d6af76..8cdb7d2d54 100644 --- a/common/src/main/resources/assets/cobblemon/sounds.json +++ b/common/src/main/resources/assets/cobblemon/sounds.json @@ -555,10 +555,10 @@ "sounds": [ { "name": "cobblemon:move/flamethrower/flamethrower_target", "volume": 0.8 } ] }, "move.sandattack.actor": { - "sounds": [ { "name": "cobblemon:move/sandattack/sand_attack_actor", "volume": 0.8 } ] + "sounds": [ { "name": "cobblemon:move/sand_attack/sand_attack_actor", "volume": 0.8 } ] }, "move.sandattack.target": { - "sounds": [ { "name": "cobblemon:move/sandattack/sand_attack_target", "volume": 0.8 } ] + "sounds": [ { "name": "cobblemon:move/sand_attack/sand_attack_target", "volume": 0.8 } ] }, "move.confusion.actor": { "sounds": [ { "name": "cobblemon:move/confusion/confusion_actor", "volume": 0.9 } ] diff --git a/common/src/main/resources/assets/cobblemon/sounds/move/sandattack/sand_attack_actor.ogg b/common/src/main/resources/assets/cobblemon/sounds/move/sandattack/sand_attack_actor.ogg deleted file mode 100644 index e5a2cbbae247583011c9e207e219c93d69fe9b45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27715 zcmb4q1y~);vf$tvAi)V5f_rc$5Q4jVaMy!-g1fsXNbulpA-KB*2@VH$KYPf3?|b)q z`|a+xO;6KIO;>eUcU2FgU~aAgzykj&AJ0Q!p1YY8aX2ufFs}AaMwX!G8!#WrpzQ?| z_^+!GM&Wtnf0gHv&qF_rBBo%{LjJSt!u^8>5eilof3GO6{QMvW^b(gBQ}`gREcmax z2>(1sCn}~610X};Y_jA%{^PM~0DuL>{FV+Q!Ay#-ATf{DBQ;U-x!1!QmYNvWLtq@q z*8A^HoWmSoG5aB2ZcOITH6d zz7bdHhJVH44@FCmhb)Hv4<+aW zp?RC+>Er3=BfJ>tTe-zJ8B<+hsu?s*#i<#8HE1(RxB=BA-C$ZzZrXS{XjBS4z2Kjo z$Eysa^5RM8J;I$#j9x-R=yR5n=}5&3n$WRYon&EdtjL%J3_b4Cu zM>m!jIvdoA89AX=996fS4XWUkv@WQd)n+J2RFivC=ClXq|Y+q{1$!rmCjlYO9svveXcyH}A4K=d(H&Wq=?2 zKMm`@JO_X}P2gXjOf-sQ>&py~7e)C;!TP>YTe6;HSMV}`p9%ng&RBGr zzw3ySGSiGQ=ZrFwnyTRc-D9B884kHA4ya(00DuqxOuDYg1F8R>o8E@YR$Jy$o2%zka@JE)Gu2X6(^5BGz*ERpb_M)zp&J7A|eol6_X4_4G7M7p~Q`u3VPzgSW1HzI`vLw;QaeEUiAcueUq5 zUFDWeH%m{aZ7!h>FR?BuvCAm2UP`7dDfwPfeUMhdP+HA+P{Od(423e3lrTn=&<~Xy z+_lpWH8YizltZzA3mNX)kM8HIk6ss*>^IXdwO0*RSAyFsYh9MF1*a0BPT{kxs%DVl zvP!$u%o1ZN^2-HApYX)?hv=>&Jb2)o}AjqmKcvoqRC3@ex&9p+>h>8+$;r{;5GH|c`eKT8R$vzb6xthimlqO2c z(d+KrlI99niX!MybUx5>zQhUa-6`;Rp=dY6%WuI zfxrQ}cd+K6;}BK90Yb5XPgqX!YzTSE=%Ppz1sI}IxW-uTcJft+-=h@N;)zO?8=;GC zr3Mc3(&Ysvi_$TeR*lqGF`$nKvq}>rpp`GN>qR4W*%M%~?2WgbpRH{Fit{)xU|; zS0%-3%T~?IYnO4>;Tc<1&EgrCv&}9Vm$w!DofQ=Wb`No)vNkv~VNy9VD?s^~fobi? zy5vM{C>PM+6O|Ymf7y&DY7*x@uR;I#y=dfni&&*WHsq-H(ohX4hqLYEe~@O|&P*I; z+j*Ox$hJeAm?(-|kUy!6oR^rW%m58s{`x(lTv!hbbV4tmCj|78_hK0%^C3|ryyET8 zb;cq>rbv>P{#cNxu052Wm@IAmGEiPxlJ1+bHbb##ytH*e5-iY(K@7bZ1EDJ!EZ_l+ z6xg2Wf)FVRh@m@lK&=rr^mC9bN%uEW&VSxedsCkq0+9UQ=$W^Vz!)mp%;#Vl z8i_mupy3EKtbUG3BB222pHOC>LzMpm6IgGa0|v|dzfl9nbLjB5_y5ySL4^d}^RZ9? z$5sd6BJDq!D1P2kPL2<8xMl1qZOg1Mg|mVE}&=XaWPH{)3^W*6LehaYE~E zKqrm!FEf+=0@dNo595@a(?(SDdAPsV5I*HmFu(xsppIfST-T>b33UrqZDw3$ROzrb zRcSh;ysdQT4(F6C(+5?DQW=%ABq4lx;5c1S=+|UzhCH!;sX?~hSvqL?$H6tp%$z#};5$3cea8BN6-bVUq$mQ5A>a5Xj9cIjBF!#N65l@&I6UAE0Fqq( zp`jMrJbVH|BBCEYN=nPhEB=!!A_GDI5RXIt=1n?pG)4?&ELPmh_*V(oiO?wfpF&Pf z{KAfmu+hWBzE@pwied55Pt{(gaz^rA_x(Na6$MX+z<|k zP~v4ZoL!z>Ff94_>cCuAX<22sPo|$II7Z`qNvGoR?)Ii;0L*80+|W=)A_1-oQ%8@3 z+bh4(561=_ah(AcZ#jV4WdRHJ+o=Re$&@HACgKR%I+laD*tdY6@lLvbnoA~ z5Qtw}dN>@M+SVdgkuPqQg?%X<^Ujo+-C;)6Qgom6D+`Rhwhv`CwvP!|bYxy#F0xj02 zs~@KeV~}))PtF!r-R!d}&6LCtA8>ii=sRInM@_|}{m^kWG%mrm8h#vpq?KO3G2z9AMokFC*!t_DXV@G_Ng+!_{cW-L2%&aV8{uR;OyQOrM%Fx zlH=GR8g@RZr~N!m+7I0a^5lxYY%eHJ0!u8K2Ky!BQ=dVH`Y**t+ltw#N7cmJylnRh zgP0^GTcMpdXM^39mu;I#mXmfKio^VIuNLd87?;t!I*fFj>+@LO9sfM)O}e&EmOp(;K0^u>o#UMTu}|iUl6n#4!THzC2nvpI;ET2znY^%(3Vm=A16M zAD&I|H$R!A;KwEGDdAeroO{kx@*2Sj;o&D8otxG$%8u*hq?w$Z(eo%0=fUmt6*sx% zE7+NjxEXdoZ2NAng4+G=1{xISknKNN~+WrU(E6@B;-H_gywJb$(od zMd*}s2ewT103HCjL>ZBGC(~BBP!P@^j?islgdv9{Gg1;n|&BxXH z5*7Ox+j!^dwHq?stL0St2e=yH5xO6ptwXbD71^mPux)VX^DtwjZ(o{Iuv5RsfMCzR z%uk-)T}_Z@Va+or$W2F;n=Ap(CiRESB2SgC@#pdOFp}0W=EN{>W@uMF6p~d^TF+;E zNUJpZ{`9S_Co(O5*A;QWZghaK2*1LQ~}aSJ}Z-^N)zB z!pohS+E7lE`48G|(SPfFkQmGFFQ597v33Veh?HIkek zd$vy-+qg&aia$}cS^})D{^fu#f6_}h7=TreFOYMWclT8%@ErylF4qWuel+dNg#2t| zX4<~qVAShsfjb(#Lmg869GICn`fX z|(sd{H^Af zPQ=`k3`tjNRw6Tx5hI%X28U>lt`WVKrH+-48B#}W`mXsTiD*Nb4Fx;o;jhx*`W&v~ ziQE|MRY3~U#uocqg|-yb_2lLeKRiVozdu|F6-!2W$t*k6)c8Cax0ADl861nXTDwzQ zs*~QgQtTbnl8_D}dd7p5AYPB=9)jEVXJxrxgIi+*U&TbuMX)@k9H!=yy9p6Evy7P^Ep=jm;oD-EAbTkIPy z^xa+%h1zP2vmCxs88#<$bC^!0%W{4BhBf3+xT;OTAx;U+YJH4f2#8YaOAV~S+d&hX^Le?9Ppw4 zaqTwYB^=ZGaKfnGOp|_nJrcTPT@*oe9erTzDXr}~cNwglQ>77UwiGOH9)EX)!*42O zgcJk6($R)u1z+Sl%egS!x1;x}crU%8jvB_416}yG)Z)PZt9gU)*Q)5Nv=7E*LTcw6 zEMtY)q%#l6J6}yA^ukIV`E^F!<#2j&qGFV;gan*UUv{v{riE?HIx^E1U6X4_y+Dr< zINK{_fP*nt@?~H))$Y*#S@1bh0EE5lI3n^rPouc;SpPR6m6O&vt_8sq&fJ0Y*B{Z5 zEDML?)>UPcw=m(Q6-hz+i6H+%{FJd|)}4sqqCG(#cGG^4XrV`jphvbRcU77ekJPb6 z=$6%#QAPU$K)O#!0vG`>Xss_K6qsDeirwIHOG=D(7%4ci_&ZJ)JWQ1&&qXfM(X(PIvuA z{M~-HN3J*K+rLLx)0eQ&t(K!@j#Mj@P~>(>Z_~V8XfAhp{Dk^$WwX2>zWb5LWP#j= zH)DLZ!n|HDc~C1<{OWt`$ULP5axzM=!hDm~r?4XH)ouu+p~7}9m}ofc@?ayqIj#Pc z8wlk~lcl!$8&?`J^Q40Bk$#FZuhi&pzC7f9Cou`c5i~sMP>8gDT{ozLH6Tn@u+IHP z<5z&>=uuLCQjx$8Kwkjp6K=wzSRm2|f4ZAMQFr^nOhaJLzLED%M2&Yx_A3@1zqwl$ zEMF0#U-jO`&%)jzwE9eZ7Bo}5$feA@smdj90SH8NOpB>a18sX#bi3bEmTJ{A&_gw( znZ-9VLdU9gzB0aj-6vf+*b#H)bsO{6!a=aNrc9(%C;X9j^4~pjF7xCgnQwN+P=+9uQj`@Qr=0OzhgUmsX#Ly7WFijF09}0N7nuhe3PmZ zv0U+DO0#crTw%Wqi}2HL%!mv1+aRXVde!0cKF5)v0F9j=yA9lb<_6d$vtH|j?tE%N z8GJ2EbGMU!YP(;<`TZM$H#+rA9qK|16QEXNusom)lA{Xu|WQoFJ?KqeTZi2{%kZn9z$@&b`XAsDr?*0lV)rRQBHz$Vgn7{w|y*%^_y zlh!Gmb$28Zkph$qZ|ra+l1@98>wMgz3`XkP&-&!I?ahVj*UzrzO=@Vnwx=)D)s$;-Bqvxi^598s8~ zn`ZKT2)>^)f1i*uKRH}yVk*CL@*Qy)=Hr+_DPm@TPgPRIyb9uYSN06%v>4wPVR_y@ zdwYKdzy0^|2S}COUam0sjoKTvlX4sS^^IcJ8DnXxUKTvXLI~GH0$G`>LS-=qn$iov zK5cR`tMDAY?x&=#koJ%3UlH1oYjF_D`m4qPjpde|)j^lJR~Ud|5{zcq-RBl60IHXgsxDbsHXEIseA0>Ee&^y<{^}JTb6h?ecW22wN>X zrRi> zf+fzlzxbemO(9~}k*(DMea2-X1linUSrYSI*gohxH7|bU8O!!KZ+*fant6-T&q6QG zf)myWa(7JH4)Ec355_in+G$NYBJ}e*TW_yJdZdvsN0o9~Nh?1Y(_oydm4RI*WMN=$ z8ya+nt;`>kJc1Xg0~>0?!df;6k6z8TsyVpkeIJ{DeC+%X#$kMGn!;fl_q#>3G1}ZM zJzPhZ#8+#`vL&D+l&9#(9|QQ;p@r6eA6mdcyFHY_W_S>Nh#5o&q74a#ctHXn36L+4 zSV;U+UkxtqI683i+kh~;1;>>!I%Ajq54Mhqjjt^53xCW^`c9q>vqY9&^dz^*H*gP`sCcjJ{3wXY ziP4L|eK)F$k{5|vvgmGiVO#!G^p#BEn^)$Bm2`m?nQcq`3H4hBv#rSr6_?6!+VV$J zjn7MX_FPI?nA7MQLk%tJ!sIq>g7}T}n;{*`VctB)>xK5enfVh}Q%X?yM$fnN=fKNt zQKE?|UZrjQ@=O?C+1lO0-j1UxmLMhwYdL(c{b`$GLZICpOZlqaMoHe+7wBaVg9K348zyUXnSfmw+Jfb2%f&!`p{u#!zp z4L%J^OIDHG5fuJ#05J(rf+&&%DggtTFoHU#hB}y@4Z=+Jt65`hHl9!S%Q-DHF-2{V z_DcQ!dtn{-i77YbtcWIwhIrE_x@Z~Is4kn<%#f^Ambm1Eowku$2cp+X%r_<#zvGX8eD86uE5^z#~h%9>(|TmYu_v(ZKL}R=wgv@)Ni6IiSsyu!j$TS)K{r+DqSz| zIt*!dAM%2IyYgL8rHhOZ`DTtdHKF;_ovRf9u z#W>NmK_Jse)xczdJKk{a{@s(;R$04P>-&ViW(@V}NTp-RdAa zfGC2<13(kKO^?saO(x?}BR=&$PCoP+G;E%>o?3)x=?@Hx(gI-s{p5rXPX?+6zu{evei@kSuOk-Y%VT{caIzC^cR} z#u-ty@{ThFsW);KgEvmu^(9!+ry@2!g7w``?vAG6Nt8yq)p2zL;&_CvhJFYJ9r@ku z0}6k7o>Um)URm^7t@bjeaYp%@;#Q9(74HWperC4LNdH;Q;0u>!wK42Xi9HwUIPRf( z5?sAO8?QuwcBJB7(ZDd=!845l$MVj`QWH=D5MS~MO+XaIFT^Q8ntzk0XHi>*T z4}bcx7IZIEbz%he+c{5~su5+ma-FBeJLydtGZ+>X-v6yFRfYbZTHxyw)yIWTN1~?z zKgs84_9mJ4$F^|e1$65#?fxjp&EXns^X@789#fdqJsF9Koy@5ZM?&x^TH&|JCRxZ#bC|!rYch4@nM_^>s<7^ztc+iOorZQBjyLe(9-JB-np4lu1oP@ zZ{HSEA@f>~YcZ-TvYFbFmqr*o)aPa;rzuo&#*1D!eO|*`b%N>f&7a7)yTMJ5;-`a^ zBeezZ8v0=+tj&%7fKBw5`>0M0@6fI;qfx_AW!BE$MwPv4D#rSQ4QG*MZzm$Cz}FRO z9CQ_o_39hZTc$)2>@&>W-=I(FuHmF@E|!@468S#{zNR_l?33}i8BDG(yeS9fz3Bn{ zkQ!u!ahSLM1D;pZyP@sJEQ2A8qxvw|o9~fgF+<4ncmM%n0E-iN$1|7~&+vw2eP{$1 zlORUZDomZMvg5)|R!HE9Rkz~SZP$?4qf5wVk>6nTpsL3)O4)ZFY)CSO;n}7a$by!& zf)}AlUnks06+Cv`wvedgd|+G*#(tzfL-fA6-CM>oxVyPJ{-&0e9w0h-g@1h4|9EWR zAUUGjmofVD+{>I;%7@E4%Fp@5v5t@4s3g|*NY@i6J;`{j{~%$PDWHPm(nZAiZjRLa z-8ww;{wS>BN-O)s&>IQkn1)+!++N%^IP=k|5lTp^ z`m*%?N0y(K#PZ@s%r!T}Wl`RoM|t44(EWwZ%gDEL+QxS_*TwKp%8C=bJYH+Zic6KN zmSLX-;iaPdA}hK@W2zGNpST;BNk=u>ib(0wCe^Q(D=nMv*@)>r$`Kp&1?i^E4QLtB z5F?V?^7YMZbcy-=GVp+RY+>XkVJUA7owi#vE6Xj}J*d9;mI7S#$@P#39NSL%1Qji8 z>XVm$HJIB@yDR?l9m$|@^s5U-8js))nZ)l`WwD&)NPpmIwpCy38;z(%NOzAv1YIOn zRL8s+SzmnbTGF%+|4YwVT{EQG!l6&2nTcZ`?qlr8ubGnqX+ON5sWJwFT zg0q++nO+L<<-=M`7g_D9yhSR-i~g2ZMi>cBIvd7ehlV~i9cR(Vz>{l@D%zLNi2h^T z4ojM!x!1=`crbCq(D0+jb=t-9*9uY0Iw-NnGKI@Af2%64hr1~2G{hVW7IJ<2e@%9T|TT(~G-qg69^a?G(RjjNjFR zk>px%PjpMNYqP_Zfi5@a(PKo1`u2Ttql^|NKXu$y)b36zvJ0rAOy?)tI{2aeC@9CF zi8^&gC65xG+m@{UDN|;|TAiaJD2wIQcdn%705LCqS=SA-DWl%yZ@gpQMu<^n-s#G> zs4HKx5BJq*k@DimDU>`SQ9MmGj=E^ke>PozGbz2YR5F1zGIIA$BkT=alT0`cEnS`v zc5^)@oX|=fr&5ji*kqTs{(1DxXT}chGePl;f(edofPl6BTG&#M^qfDL7To3b`@zI3 z_k2D>yFl=o8yx_IQYb^Qa8TZTWM!KAv^V-GgKTPr^KlcV^K=bH9vY@3?buJC$t@mZ z%80h)-fTIhqqnMdOCagT#s-W^U_*1UhKz{*Sm7tnTP22z^o(pTG}H_`jWY7+l3QN) zUy16ZF-p@N{kLlT?xjNfggzU%U)UU%{q7m<(s`O@%V`!-zkUvZ)&1zsFz#~f&sAZo zmQ)*#`Z!Z|?!cMJ%jhfkcpjA}%%6l&iOZ2u z&q>eY&%N>1!-C1bPw@UO5;lyMQaT63$Pjvl^OpPV=Q)IVJM2YaEf`cPmnBWHa{$mc z7t=+SM+AN+pZT^3R)`$s!e#t(VF8VKUtX;r&2P$?CQ=HBFhOrs(Bo)23 z#xs!Zy^ULsy#{zLJG4QAd?>@MGFwNe&D{xhZFCz`Haao zb5Y-I(N7q6ti$v&2v!aU1!Lyd!1c0Q*a921%>X+NTiceIyp=XCGnwL+O*`K{0(xV4lg=S`)wPB>6yI2npdHwj z7fb1unI23R)UFD8aMh-5o-R^WKS4(0DyVA`TRnI5_BP6+Y(b6k9~<4mN5Z>W6onAg z{298LH@&|SeIwEm-AjYrX|#oFH&ekuVZm1kK1uFu!6uze53K{iNeTQEq1OUdkFGO44Bbem9dEs{%-bGcH__PM zf7++vC02D}MATY1dN5mnvn*TMND*A#m^~&2W4QUSeq-AsRb=bX_b$eFUK%qT?jaZvG(=XL|m zm#timRzH2AY&H4}xeNVEE~6638h5USWLxd}USA1}FpV7ZpLmpIZ&b!Nwe6rok&Ie0%L+W;vbo`@Q+j3MCiShl!yWZR|ip za8xObP|Ais0`xze6TCPqv3{uYmxmbJ18dX(4iZl%Kkx}Kc{L3X;IKNA6EiO%e}wdf z{Glx}&6P*&AjumUnQ?1FBem0AK0DgBgm_1S+^X!({Vs)1MS8i~G#@^_L6*IZKYN&x zeweYu6&|qR{#eAKX~@yec!@f0NdP-B>j; zllYGH7T_7vWQljmE;X@yo_pozr_VNGXk4CS5%oq+F`6~p7qqWe*cO`dB?xcloL556 zTxA6$w_;a7m1Sh;*Uh?C0DX(h*LOH10r%$dtwpO^r6y1~I{D+zM!t3U|G4#JuVG>_ze>uF>DsG==a| zwq{6@v2L?va0>za7m7b|k_0Kl2lTpH4LoNuMGci|oQfQ^2o(*{|9!F$^xr27@X+q= zqANNcBnWzv5CUvd;V}pYa*_lFp>_j zvYjxPp8(Yb<`d$O<)29R_h2$fPh$@&tP0Gjk|hTDD)7@1*x9RjZl{Ao10vm*k`i+s zG}gApn=^Fmc%q4W$7Oc0J1cy`{TlnbMp)f-yOM{ zOlQ7|RSJ8%kw`Wp<;*a{sTT34kf$lNu4#mY)DZuV~r1^_0SxMp`ml{UOzK0%n zJ?ke9fpe;Fj->2Cb;rXsuS4y+*xnPnF1z&Pr;f+YZ|K>CxP&b1p0eK*{M zJ52R`ye=*(E5)jAx4*8-9JG61c(f-}LP8@Re7*6)#$ z@Oo-!PK(aHc6&OT)pM{|(ePLiV)rxqhF&g|%_(bI>nv3%iP68>cd?rPRaC%&Ord=% zBQ2#TCXhzXAf`LQR)wj#d$`id_+aA*6koBkcSUI{J3`2uGvFAx&J-8nF2@#jxS z#jqykd%2p;^^C^H8e}42@gLSCA=ScOER_82-eQ=6X zel7&3b0NTzq4zhPZ+rZUGe!?fMFAGA%!)*Q!skt15kV{(zO($dy3Xa`Xe(Q$KDEPV z6@WBNViK1e18~3wcwRxjGl_+v-_%8Yy{`T)?TBx=_~O#qdU~4X9b*b+jVM9uGWK+X##p_AYGY#y{uWaCL&YcU0(zb=;}^#Q z2AA`-lWwztdWS{3I!+5n8!MeCerJukcGhCRi$%v?efH`5OUYobhyx}2Kv)-|5Z*5y zIWe-M0WURiRy+sQLxyXYAZUDq^mk z6X8jyR-4pdMhdHg66$irbZ{PLu(SkB1g+%w4`K~;Ok(W^6F#hE!{?aKd4+0EMcX6P zh6v0_ndmBeeB|lp42JhUc{pVyQ{2fE!91Z*h~No%*py1f^?_BdZ|Hkv^(b`3q}V`xrz z>&FO@uWJ;1NmDlKFI>-WmYR|BM?Af{KlJEAzGZ0nc9>=Lqi&y`F$|VfoV%=FIT#9E z74e9>wSAYUT^?{^?m%xlzZSkM_(iCCpxoy5=a-TBlu!)F#FFM%TZem6dI>od^>@lD zd~t(pE60kMA?>Lk!#e(*6`5kBu)Mld`PBE@X`;4IhRP+>ORTBlrn0z}=%T%L$G)vx zjq7ji?}9JUkNc3U!Rd_!`+0-Cu4mgQ0;&n6@qJlf1z%D6^)f8Z&8QDAO>4!RlyRiI z-DFe_5TtP4FHw!)SGqTS10{Cys9j{SBa?~s1eD4m*BZ`oQ z25OyZTt^|5{EQs7TNPDeBNaKmz7M}p-exYOL6L^uO%a>hPbT{5?^ln9Gv0hvC_vL|5P1+-gd`@6m}mH= zrwWo5ww&+jT>fkk9+mQl%>dkFQsT`+B_lhed2jO^!GFukw`jhwSq}(Y`N-&Q@L1f*7gu!cFJnOzMf?G zxln{Oad5!nm_Qpy{if)-OX=ZnqoX@r$eyv3HfnYjDAGJ3l z=5IF-okn-9xooDAx_FwI#o1}d%t>1dwe3s;dr&KHH*5T#BFL-XV!@HVD2pJfJud9*BjZT-HZnx7kd zzbYPjCP#@9W31Vsel}kZQFb*^50ZEr_RSXUPle>j_Mhq@hOZ)L{HsDwuxiwm)@zHB7POnymZ*Wyc&gW#Ccv|@2| zbr+Njmf@AD@g?4lwm^`{nr$!+6qoi9^VNv6_UCOq0~_ATsn?T<7TaMEVtIe2NchU? zj_cIup+nKp{GNH{z_nsoKGSZ6n!%s@0n~M)Iiow6cNJiL;V?$C+1Wj2CdkBHXNi((X!`hal+>-hugJJ!RkFYMxfgu{vq)3 zzHspNbWdJr|I`ux(gruOFQRdCs5&FWZ1=#ljJSth+v2BKX=ubw*YFc+BjDq5dmtQ_NmnyVO*L(XI(tQ9*lvdz zozU$CdwTcTx0&S92F&9@@+p>{Ly{^jtdr+ao*bS}C`jlQq`)+29+~@bKsmYuVHec? zF`?1*GNwr(h8@s1+mJ#NQHBM$c~Waa%C$Q*9C`thKeBJ1-;iM`LXV#W%$IyIwhTF{ zSvq#`4&_G*6)H{z9`7iHEV#-1Vs#qg1nW!uFt!+CxV?r~x$SNgeA7Lr7d>kvACif# zi;MzS5biO&RQDIki;JQ-YRop#ItXQYpBxuKXK$hnuJs^a7Y21|4J!l^=d0Mp=eVU_ z(VXv)hHNi=8*~XVtFLUY+h~xJU^s%f=>!ksZJ-rLb9x#=02(3{$#h% zxAuwNfu}UkLVZ0cA-r@n%67bAo8|QP#aL4A6@T+NcecdX;J}#qVSNk(lZ=r=4EwqE z@Ij6iR`xF;59~J1jZGp7VYl$p=!sv152-y9v^;D`=)+%=)GNe0e4V|D&{HYV((P%5 z<$wDeC6&p~UtV#}mawvUy-TLc0Z6%2Byq`M06!(fGW}hz+_gx!+s7b3cxcP(p}ClcI{ z))R+}$X>iDMOTo5 z&XY!6okNG2c#pF~do1s(n^_w38tGMfk8Q(-bb)~JJSD#dlS{Yo<{w8^l{AJ7A!L#v zHd$^7O8(Z*$wFp)yyhKMNbkPzr;dJ22^g~WyUt!s-a4@6KLdSuB2yy}#F1gLcIjyu zFK!onS%)@;+128LLg-y%TubjbYKTr$^MuM2igWiv*lM7cr>_QG-CzCK>5=jHO+CH` z*6S^hg!KN|xVMimQ?)_(K?8_cG{vZnzU1p9V;Z|UzDTqDA1&P5T5+Gl8wnXKt`yRj z)Sa`lu(ADmd~!ETjfq8i1*ITv1#sdn@}CbK^`R&@AO^@)M3OWdv3T@M5#zFrSD#uRlob1es_(cklfo85ooBgw!ll`k z1a~1nQdc=Ve7Wn9_A6+t+v-ADnC^RTyT*msFk3^GB#vnZ)O_x)2@@|g|3oI-Z_Aa^ z@H=6zs{4+*OWaXgu5m$A&NSlfFJ*=qy_d%mr{>gIaT{yhs&i+WxK1DCbSMQ@+w47b zjw-;Hs3=!cpGKp5hv&v=><6>`RcUEsPWN5aKUo&5tc9gJ=6AA=ab)+rejF4t>+2B z?Y5WdE8D6w(bm?vH1|!z*W2>)FYDeXU zv+?0#_pZp?F$_<2K=A9}DcNdGpE6r394X7xGH1h6#F$V@MPpAnw$r5Z1o-$5@}4PG z5-zWCOmg_e;&E$>sgm1n%33Da{Yb8R`|{_=;+Rg;_1)d{omVIgCQIg45BSVRFrl@) zBxHeU#a641Q|{DnWj~`?PgXv2vVun-k492lWy#G4o(oBs85WbppaDw1{3^B z^v?UxBJQy_b@L;!+hXWX*6$Yz_lQgquBOOa4&>U`NN!C6=bzzg*X|lR_!DvVKY^=$ zD->IttZJ-&c@cAl_av+>n$HD!HH?#qt2fj>SV3_^Oe8zVO%`sZGbCo>Xcf`!&=Z&+ zT?fMlAA(aH;J_6zl@w`^2`QFKo}1;(2mtH$4R{5QIr|XzfPPqV-C+!A(kt!FUr2z8 z&7;kdaWa%@QBM4BMI2kq$c41zM)Ht-TQV+$Rj-yYp2SsxOa3M113$8C_co`!YQLQ^ zO7nZs$0WDP2Q_6`9p2OHJZD`eO0Oz`m&LkiEXI%QrzxrUQ(LFkXAGO(t`gB>6`px> z+%C$;zwF$-_1b0U8+j;(DI=xvG12(hU-c|TRq+=~2KKPyNRdQski9~z%xGRE_4vq) z{(9V)oulcy!ahf&3*mKqg2|iFjvzF9^P>z~H1zlQ{ZAT#D4OAAPHvQA2#-<*(9NYi$>G}(XkPUIs5HL znJ7DbKTpAgq%>;-hzH~-aC{lj$!-$w~M*aNJvCF{-ZpAGkd53 zZe)b+A)dQ!ek;{j{^lb0O$;W z0{~Qo%ek=0^jGalKkx35?C#fpi~F^GzGqMLU;lo8?$^RRNo3O~o*L?ZzVC;=g*>)P zZgsiy88Ew(8I}$6VrQU@GK%J2W)z;wH~q$H|DMCXTgGNk59)X3+Z;!nzP5AtS2kDp z^1+>Z**t0!E;~vW#uHMj**6xGep_dhgA?C2Rinh6uX`rb%g#PQ{K*FF_&*YfYOeweyMS)TFiQ1ru_xDBR_ zWnDav2EQ6dFm%Sfjvj~RvXP21917e@z-ba*8J6>`qEvVV_v-C&-*gu zCQ=QN3}Lf7_=p-vAq2v7Mr(lEOLo`9i63wP-c?hO3Mv5&gohio?Uoo~0Jn}Io4~;X z;79^M)c~kUF65kGsDAzB`|{tfV_%~9S8^a$f0B9JD_p;S{%`S;r=7Cud;wk5dboX^ z7>0<8V(09I*^KMzoa3)u<+My>#+y13#_MHA28!5l7ldfVBwuBPO_{Bvp2e^3WN_x) zdTG>!`Nes--CCdV?l>q4Tk)=FEkA!fH?@_q3{A0goc|{CRC$&ZW%ETZS@`jXSEYxQ z`atF^zqi+2Oi3Q93^Xt!oCX%1f|5rZNLEF>KgbL1L8Ku7+Pv+2!KgahN%{EHtFdQ} zbBdy7(vGIt)-o_N#StV~Eybr%Gur(3p_5yT@n(uk@`g|M1S5P&e6VJ$b#DGp&cXPJ zz61y!IOpvrNe!56ed)xeQ1r{L8i4o|o}1 z86K@#IU+}-gO5MDi$1@GHZj8bOUBdnrLCM=%`IO~2Yf>NVWzyE)GN`AG#S3n7gTms zM|X0~`sVBFg^wwhvJ@d?Y27K6$W9Kv>2qc+B~xMNL_3nVlpi?EfiO6SS_ktdRM)|3 zcN4OWxc%=ZRrb%NfbLSIev&`lZ>jt*#pj)#Z%ZdB!R>F3U&7=@LetyH5T?DvCw{Dw zDHqPuUItF2`5yXI-dE|Vk}88$d*Vi&I9t1ratKgEi!WIp-sznOM1Kos7%Ah%u)9W2 zbGa1^sr*_dycAi`y=2RMDxUQFjz>isrCBiDn$twdqs!nZ#cPE54I2isT4Go6@{J=rlYnT}_=E zUgLU$vit3GIn&lYl+`=#5x+GZE9Yf?UWc!lx>xDl`xyOgfBlRv^~t&v?dtV%N(kAl zc>Mf9S|UpPzPfthr0A_Ce!h4>J+@2w+|SINIyW< zuX4_?P)BI@l_irVGrr$T1q%Qk1_Mk51pwgc<%WWoUE2+C=DsZGU><Ex&O)!V;nEA5YJ{I{=Z^Ibogji>3{--(N^oTR_p?;oNQrCaw{ zaC**e3VCD4Nz?W1B(HSH*T2T>`~H|Xdp*B;;oVeq-Yc$(0{B)~kqLcpcB(WRN8^Qe zMOtuU_$dFH3hvycj%@BjErfn6Gc=kuz2MApC;T)!yff<|5nYr(y8QygRqTx6WxNi& z*2ihen>}kiRR6Y>e!Ts}70=J#X7%{ZeZ@bG7Qb-aehU8l;lC{W^IQij#?9G}Ny{&% zzTM?{@$3C4^_#D8q|gnI)YBqVN?iehs%0@9k*y8`=)Y_5ZJt*~CnsH^ZN=ftwit&s zH+$hir!apeouwI2v`|;4T^hQey86L!)w5%bHo+Ja$n@X$+8~u-(rZbM4VKBtuPg`_LpY_T0(u(A|&M zEE>cUFYo=Ip3S$isLIL^`U>TA82etfLB~G&eIWVk|9yEcTO0|O%X(vEZYyRgb5NPO za8AYMZjjJix_Vm1qj_ihFOMX0;;W=eGBmhw847VIfEAm&wK_bez-t}VVAn(TP)>F@ZIMW#f3_tB8O2qZ_bh_;i z(&3lL5+U6!WNnJ%5l}AV%1687q6?=soUo(wY$nSu7V3@=9i*d0t)a$;JZ?TsX*y@y z*K(7nK;=e_do!O_xq@`s_}8}%C^G5u{7jejNlc+7u1@ef1%%cd+{?J{%uPO7u3ZCt zYuWXvQ3zl~>y66iR6X44QYVh%TK0OJez0CdORJ0i&~huW0J#9(R8tfc1Ofo=MA3oN zfdR0pIe?RJQJ@G=P9z|CZUW$O0D!6pzyL@(d4WPBV`7{qKB5X*=*`@zTvp-}|p?CTrblgD)RtaiR41ef((y8;kY!;ew)e z(cs*>w3i{ENUinaiN2YI`!X(GH!X>_UEAa!u3B3a(FfR!&poB*j>tI1`gaN3D>w zaQPRKK>g>w_>A6uyhJgPg@OBu*W#yu|D&K@T7P@_WEkh0E2&X1KZBZslSsEo-gz-V z?VW7MooBJdo>H08wt-FaIngj!)Og-;8eNQ)oW(Kp)4^Wc&NN80@bj81$X3RhS!jI` zh#2)YbkNpVYXJ>Jd)GrMo|skpYNh|HK)3T*)>E?L zJdi9ZzRSZ)gQrM~Hc1a3O2E!m2i|kJrb7JU)`%DPSISg%_`K$8@x)(uSD#3FhOcU~ zZDHV05b~1U2gT|@%SKrHFZ%ZSx;N$hr9a=d`&DN1d?&fUreA*+^s4lL|E=s$>GnRk zB7IKAz;y}=mVG4D{qKa&j|O&zx@$-L)YpHPrnG{;H<=+9|6c!j|62cn{!}B83KD??aDShkTlA${*Qu^o9YDo{ zOW>tGX=eg}%LLFc00ux+97tiuQGMLaa{U*1j~`BX{!{;lZ(pm@+2_KhXO~wV){OIZ z>|ETfhrb`)4@c{jGka24@PFUL!be-Ww;vvCaer-!s`t0c@yHwFFW3L~e~aErq*cf7 z{Z4oS_Omyq%FrZvp1{zGD@p7XrB}ADSo<(O@tP$*$zz->ntP-k#zPc4d!7GFkQsXA zjeb~^0Wy&2pK=y(rzuvPzlSP!(Zih{cbK}@^f<_}kJKaFRG5=8?e7XNNPAwD!8@=& z(Ow${%lZXeCc>syW6?kA}a+q8L8~|XzAp=x}oo505+g?B0=(`lJ!gKMk zyYYaxo(X*R!YZ+lF>r>y-T~O|ywV9ULp=@JPlr+vXsQayqyFy!KsYp1Km53K!xjAu>*dt!C-f|XaB!!7+aFghW>3<|KhNj z)>9=AKHp&;_U$A8y`-;?p(FZqt?z&kuF+=In?7lK+ijn79PmjGQ*^_EEN}buvE2M; znVD<>47Mmo)9lCjjR#kT`G(Fu5V$QLJ4a-0LgHX>>`IS3q6B;%9S$uRwD|%b?_i*G z8;*496!BadJR1sjQo<+Brf2!dOje7X>vIuab%U4#2mrYE)0Cc9%XGNcI(VSJ)+wZ?&^iso0ago%g;OhR6i-+jE>t~ z>A&mI?qo02{V}t*AtzV=mY?kYdMe2|nNs4-K?QrA&W^rt2L7b-?|ZZrdt;6vEewZJ!|U6J<$b|Suq zX6OhyXp#BFV>gYC=&}K3VF(r9G(}B7PGuK&R44B+QHN3&&*#{ zt4SDy{EzvWk?!;vO#0J~U;nf-fJT`fEXA3j`q!@9(<60$@X^_fJl}HL;`R#!lXC~= zx02B}O~QIazRzhH$(VqGV18mgndE4PjdLd1jwZTzXlfLr7J!fAC$r{RpZs+-J7(CM zzTNojS6@U25)J#6DyY!9aOnX4WP=0=jRJsw67!r`$~l(ttX9G$8o??! z&;VQqK!i^LpyR+vzyW|T;Ew??CZKGW6zFHb75$@H=k9^h|8`%9ns`#$9zm0qNp=o2 zw@vIGd`s){yY(dOxUvZ??>=s&P}E-!B=v`eO<|%}6m=F=y>V|RopRrCt$EYOX=s(S zMKSV8FJN{NsZPr9)C;4ps{&?ScpXV6&z8$tOI>&fc8b|SY|veE%5~QsF%W_SB8p?C z@QVV@Lmxlw9r5VIpQd=rpjJ8BkAKipo>-}yM>wG{<3E^R+9N*6#WJd;4t{=6W5eYALstEd=g1 zS^JbB()UFhb311mB)yPjOWDnOkNQ#7FBxF9=aStrd(K5Ox;%W2NX0oq zKPcBiEg$uB#k^!&fh8xc{d(}Lkw#c~hF=-wLlg09^(cC>oV@pklw@r+|y|%~7(uzee0$bTju8FV9xR zH@W|CVkds|;FQ#x{cooy<`$eT{A-*8XI|XtsnyKF3TO}~efku9{w6eaOjDP=Z)@Af z>Ghec9<5%vMVUJxSGq!Hv+Qr#+~4E!*A9nwwi0svdejj-3SE_?B1Q4L-MJmQ($}*% z-D!C40HEj`)Qgm#pABbIEhL@qHpaJAadT@zkgXl4KBU;xZpxiT<&&;EK`PZzd*b+~ zI+F6g=L!((K(6#{z5nGk^nO|czD#tR<}|rx%`_gt%V%U;x;Aa;NLH9N*eq6mnAQic zy50GDpBG4UQcLFHbQP+NTd)ahPdy`Y8}W5d=;2^w=`yZxP&Pg}S>CX;+phsU)P1MO zcGqoyNX-xxX<=@szs-{=5dma)-7O)>c=ZX)jdNpu2zS^B3BGj0i~#6BgwuD!Rd4k3 zEJHrMH`ApH$b&q|^I+lt2kqSK0N@$`G=~EP<^aerQ7(w181(L={&&*PPuQn^9{=2D z@+Xd0m2a1!$JTxNkk7iAid*z)pP%f16bB1^(n`H@XyQJs^!k;R_f0n9#?DZ8O&+877@TynE3IrXxTfWY z_szvVy_DXRUyB3D-s;SiyFIt7tGwf-YvjcH{an@NR=jd`aSLuIjP+Dt?iQz7s&9Qd zJhY>6-g`=jape73t5itpRBFXHPP5MR0MAeep`nN(4wtnIl#!Pr#r78M7LjA(IwKB_ z$1h~-txlGr2qe(9-ksDtH>l}tJ`nl!*ctDE5w3~*Wo5IrVO}D8yVUs#%+^G@{>17< z@I;}pPLFY7ix+>LDX;rL0^Xzp0s*=MSo8O4-eNWv0Ey*TR+xa(fq>@#C~yE041fX9 z0%U-)T{0=q&R(kJu6#K19el2Ce3<@j`zG1yzBYwJ8M!g)2nlh# za?&2v*j8Y#3U<{H+UA|hq(Y%`MpI0J+(IZv8MKv^{5-e!_`Yco!PS;@>8XY#?c)rT z7b1ptoQ|SL9;RD~bq-=)wn%!Z9XVz2jn(PcfTrF-1IF zcsG3VEY{n$9`AGP!PUpQnCJ=g>(+9aA-b^Ra{c%4s@x|3jh?@@7>>%Gnv))f7rw`i zcB3#HRPK75?)v%U!G_QD)&F?%%N}OZnk|yPLy=xm5|dQCIOweR_0)GGn&#(NG1M>n z-+dgiW0L;*Y^mLaaenSBu>PKHmT6qKN_yW-8yKe>{F&p-gdQrL-Ku}rbrObd#n~qO zmX1P9FD{t{R43aa;xKt{0m^@G=_Q&)LtQ5QGe zIq9n|U??6I`tfx$A&A*&ReN)j=R$KTBFrO#$n%?0v39w9yS_#zn&yL><@WFp7KI*g z=}&%5OgF8JxSM^x)r5sU(`HLrEozt$c=57ZW0fU$Kj^gqTbeRXcO8^eNF9#Q6S z#DfZudCsNGBkBpfTWL1JZVIcqHH<<0mt*OiRxD^H#DDlj6{cBb-na!?F9iTTl#>{7 zgnU4c0pGpPT0Q6R_VH%pAwSiT2-@k;fXsUZR49i*0SBOfg95+-0IHG;d=&BUfTR;+NyeG-MzP0WAU9{+R~<#)s?SL?o?;}(k4mof$+0X$gRxP z7m;sQ_xDdl6;bqei|1XV^`qEWeZKBSeUa^FuU(P-aLmKo=Pzxh_Q5gx|FA)FkB(yf z!?wP#(t3=}qCZ1ynQuZmI@-)Xe3f9sX31|uDUNn3y_p$j?sMrZXU#^8A)RNt#VRI1LO3hB9I;q**)jnCVY|F7_l`8(v}Gxzt9 zE`Q1|K5@J*R_6KancBfSJmeZhla$0W?8lp4efy%V**qvuXJ=CY1gQJ~0000007mnO0S5p80K4RwR1W`P|9$^<|84(t z|7QP$UbK@8&;<`0K>PRU-WqMddoCyQ0E!4)peOM_2F@J-Z~)-R00SU*fad~dpuqWR zzsH~dsoH#hS=#rj&y}|Htyqzc{q}rWJ$dwO(RemB8+1~Bv-#Af-Q60s4_BtgH#gTV zsqJfy_3d@K+ViRL+kAfDmm9dt+y2Y=(6e%UU;Rib<0q~w@4Q9#sj#0re~3G=&DS)8 zyptkuP^80@4T7WYQHb37T95v=#xx$iqJtCn+{t}n*Yoh!T36?(H14m<(>gv(>QZ4C zy3|N^BD6w0X}b3P`c_9FQJZTAD#9vz=orS|Mxu?8PaLuS?bX{{0}G#(!5^QVMxK-s zv+(FP3+Ch(PEoAa5PLC}+h3Mm2r03>8a@7z!S1w@ABm5&m|Kc0P_3uqFhmA6G( zbc}4vk&V+Nr)9ibO0E(C^Gw38X_Wf>y)Z~Kj9&Gs@d|i(vG!_0l8gfYK9q}$AcTEM z^uRx<6Z14d{O+uXbX^wU;D9IK95@BhI0aC&&OrxY01SW(Z~%Z5ID-s;cDAKb9E$wNG;l-)yEYhtY1dadUcYb{^%6j;Q2ABc_>ptLw16a-uz9o*Raz> z`JI0wD`sPj6`~)$dwB8cvNc?F#f!Zvy z`yT5}@g@)e{*yBr5by)Qcdb5fOZDF3luzbGEie=CmyU-AwL8$jD?TRxa18)BXaHn@ zssZ3RBMLN?^y2^d@%!JOcRjnp_B!=NTkAA;yQ?3~Hvjr~;yy&L`=KYwi*?n-hPJm?b6qFJ7`XrPSsLw7(YdzmtZv0rTp((ZQZd@*CD(p*_SQMKJp+sqC zRt2^EEjZjJf7wNeo!$Ox zBp27;HlZWw@0c%hV(!0OKlkgH`znf#D>Etk*ZS|{SR8qFi+5G-@1n@>)+^pg&t$g`Y0|0O$paz~30GLhy zfDCW|a2&t{szN3MFmdXl{r#Yy*YorLE9YB3-TFN6>GAu>-|4;k+l&y7_h*l}pSAqq z%m28vTe{Qz$<9V^-yUHt&bIj+^lkp|ZhvXJ+M>?4onq-BYorf5+kNcNJ(5{&_IOYq z#eH@0>)0@@_@9~Ud4Vv#{2PunkA4a@&9uYKDkucWuE-{3Z)d{^D}!=_CX)oPI|xBu#|dN-f@$JErr z4?0qctpc|{@oC-EfFZ}Ktzg9dM^(u{Sey@;6@_)kCcF9AM4aT+$|!axd;+Rnxldc2 zY||-9MtZD3JfenhGLX*YE-2c2XQ%`KK9pNLej}9)V7(>##%#NB-b$2XRRTj^^<8>~LD+J9{TzY)}37{o=q`Xm-Ei zWS?8f?b*%ifX`!ytL0MWt8|}L+|QR~pT~WVHb3#XDqFvE(sc97-(y!MiF>5)U%I30 zLCsoBzYi9@zL!q8Uor53BhzVnX8@X*Kz(aID(1A$GNp5s8He?%(Ss8s?RY^W|4#N% zku&z2j`u$pBwS#+GV4q#0-oB^h9o^YP>GuaK@{YN+m?ox&A(=%w)xm&61X{V$HUDJ z<5G>a6Wxr)o&74-Bu2FJI1bnNcBpv8^SO%V>ZT1E;Wu&`T(2DGx_i6d+MID5Xf@q@ zwkk>N@#3i^)QAVut@Y(fVe(+h(iE7FFD}R@_{?4f= zmCrX|1mp6*zTI+L?dS5_K2*k!KFNIWITYfb$D;S$r+*H%_g-E-R(rE%L;Q1UN*mHg z=TG{#n@`n|k=&uuPUeo^%@$nvJa->AVp`9(%botHx4Y5HWNqtOJ3LxI)?ySaC(;l9`E2gm z58LnlpF6DlKDKQTY{l+QExm{J>*AE4ZP(qO5G=H{>uVp~ynfxw09rRjYVF2n;-e@3 zP8kkF0#YKi_VQ=F-OYJQ)^^4H9D!yv=JsWJvb0~7ANrAxFZ!um3(qpg9PTw0G3Mo@ r@clJ<*LW)7drj{A@ugsk5SSOCJw9as^C#-`tdT8?@V~^AY#>7dDML{y diff --git a/common/src/main/resources/assets/cobblemon/sounds/move/sandattack/sand_attack_target.ogg b/common/src/main/resources/assets/cobblemon/sounds/move/sandattack/sand_attack_target.ogg deleted file mode 100644 index eadd3ce876aaf2b6600234af56e64cef5440ee15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20122 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 H00RIHaP}3+ From 8a5279741d9909be3bacf41a4e9bd36d6bdf8ee8 Mon Sep 17 00:00:00 2001 From: mia <15349355-miasmusa@users.noreply.gitlab.com> Date: Tue, 12 Nov 2024 17:02:21 -0500 Subject: [PATCH 03/33] fix bobber line being drawn incorrectly sometimes (fixes #1099) --- .../mod/common/client/render/entity/PokeBobberEntityRenderer.kt | 2 +- common/src/main/resources/mixins.cobblemon-common.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt index 99b739ca6c..d5316a4038 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt @@ -180,7 +180,7 @@ class PokeBobberEntityRenderer(context: EntityRendererProvider.Context?) : Entit var armOffset = if (playerEntity.mainArm == HumanoidArm.RIGHT) 1 else -1 val itemStack = playerEntity.mainHandItem - if (itemStack.item !is PokerodItem) { + if ((itemStack.item !is PokerodItem) || (itemStack.item as PokerodItem).pokeRodId != pokeRodId) { armOffset = -armOffset } val handSwingProgress = playerEntity.getAttackAnim(tickDelta) diff --git a/common/src/main/resources/mixins.cobblemon-common.json b/common/src/main/resources/mixins.cobblemon-common.json index a62da26d7a..643bd3bb01 100644 --- a/common/src/main/resources/mixins.cobblemon-common.json +++ b/common/src/main/resources/mixins.cobblemon-common.json @@ -11,9 +11,9 @@ "ItemRendererMixin", "ModelLoaderMixin", "ModelPartMixin", + "MouseHandlerMixin", "MusicManagerMixin", "ParticleManagerMixin", - "MouseHandlerMixin", "PlayerRendererMixin", "SoundEngineMixin", "SoundManagerMixin", From bbb06677b9dbbb18722a738d0cdf426b2244b3a1 Mon Sep 17 00:00:00 2001 From: mia <15349355-miasmusa@users.noreply.gitlab.com> Date: Tue, 12 Nov 2024 19:26:43 -0500 Subject: [PATCH 04/33] fix: uncast rods (both pokerods and vanilla) appearing as cast when dual-wielding --- .../mod/common/mixin/ItemPropertiesMixin.java | 25 +++++++++++++++++++ .../item/CobblemonModelPredicateRegistry.kt | 23 ++++++++++------- .../resources/mixins.cobblemon-common.json | 1 + 3 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java diff --git a/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java new file mode 100644 index 0000000000..2f5648eb6d --- /dev/null +++ b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java @@ -0,0 +1,25 @@ +package com.cobblemon.mod.common.mixin; + +import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity; +import com.cobblemon.mod.common.item.interactive.PokerodItem; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.item.ItemProperties; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ItemProperties.class) +public class ItemPropertiesMixin { + + // Without this, the fishing rod item model appears cast when using a PokeRod in the other hand + @Inject(method = "method_27883", at = @At(value = "RETURN", ordinal = 1), cancellable = true) + private static void cobblemon$preventRodMixup(ItemStack itemStack, ClientLevel clientLevel, LivingEntity livingEntity, int i, CallbackInfoReturnable cir) { + Player player = (Player) livingEntity; + if (player.fishing instanceof PokeRodFishingBobberEntity) cir.setReturnValue(0.0f); + } +} diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/item/CobblemonModelPredicateRegistry.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/item/CobblemonModelPredicateRegistry.kt index 7d9a86771d..e9ada0b8d8 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/item/CobblemonModelPredicateRegistry.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/item/CobblemonModelPredicateRegistry.kt @@ -9,6 +9,7 @@ package com.cobblemon.mod.common.client.render.item import com.cobblemon.mod.common.CobblemonItems +import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity import com.cobblemon.mod.common.item.interactive.PokerodItem import net.minecraft.client.renderer.item.ItemProperties import net.minecraft.resources.ResourceLocation @@ -71,17 +72,21 @@ object CobblemonModelPredicateRegistry { rods.forEach { rod -> ItemProperties.register(rod, ResourceLocation.parse("cast")) { stack, world, entity, seed -> - if (entity == null) { - 0.0f - } else { - val isMainHand = entity.mainHandItem == stack - var isOffHand = entity.offhandItem == stack - if (entity.mainHandItem.item is PokerodItem) { - isOffHand = false - } + if (entity !is Player || entity.fishing !is PokeRodFishingBobberEntity) return@register 0.0f - if ((isMainHand || isOffHand) && entity is Player && entity.fishing != null) 1.0f else 0.0f + val rodId = entity.fishing!!.entityData.get(PokeRodFishingBobberEntity.POKEROD_ID) + + val isMainHand = stack == entity.mainHandItem + var isOffHand = stack == entity.offhandItem + + var mainHandItem = entity.mainHandItem.item + val isFishingWithMainHand = mainHandItem is PokerodItem && rodId == mainHandItem.pokeRodId.toString() + + if (isFishingWithMainHand) { + isOffHand = false } + + if (isMainHand && isFishingWithMainHand || isOffHand) 1.0f else 0.0f } } diff --git a/common/src/main/resources/mixins.cobblemon-common.json b/common/src/main/resources/mixins.cobblemon-common.json index 643bd3bb01..0cb67b6d29 100644 --- a/common/src/main/resources/mixins.cobblemon-common.json +++ b/common/src/main/resources/mixins.cobblemon-common.json @@ -8,6 +8,7 @@ "EntityRenderDispatcherMixin", "GameRendererMixin", "ItemInHandRendererMixin", + "ItemPropertiesMixin", "ItemRendererMixin", "ModelLoaderMixin", "ModelPartMixin", From 973e730b4868aa9d0fad078f7b2510d9d97872a2 Mon Sep 17 00:00:00 2001 From: mia <15349355-miasmusa@users.noreply.gitlab.com> Date: Thu, 14 Nov 2024 08:41:05 -0500 Subject: [PATCH 05/33] license + remove unused imports --- .../mod/common/mixin/ItemPropertiesMixin.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java index 2f5648eb6d..b854f784b5 100644 --- a/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java +++ b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java @@ -1,12 +1,18 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + package com.cobblemon.mod.common.mixin; import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity; -import com.cobblemon.mod.common.item.interactive.PokerodItem; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.item.ItemProperties; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; From 6dfb1323b7df2c9c62cfb9ae51099292b7be83c1 Mon Sep 17 00:00:00 2001 From: mia <15349355-miasmusa@users.noreply.gitlab.com> Date: Thu, 14 Nov 2024 08:49:01 -0500 Subject: [PATCH 06/33] cleanup if statement logic --- .../common/client/render/entity/PokeBobberEntityRenderer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt index d5316a4038..b074535cba 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt @@ -179,8 +179,8 @@ class PokeBobberEntityRenderer(context: EntityRendererProvider.Context?) : Entit matrixStack.popPose() // close bobber rendering transforms var armOffset = if (playerEntity.mainArm == HumanoidArm.RIGHT) 1 else -1 - val itemStack = playerEntity.mainHandItem - if ((itemStack.item !is PokerodItem) || (itemStack.item as PokerodItem).pokeRodId != pokeRodId) { + val item = playerEntity.mainHandItem.item + if ((item !is PokerodItem) || item.pokeRodId != pokeRodId) { armOffset = -armOffset } val handSwingProgress = playerEntity.getAttackAnim(tickDelta) From 73209ab6b596fde61a2c17ad26cb1211001d62b4 Mon Sep 17 00:00:00 2001 From: Sterrezo Date: Sat, 16 Nov 2024 02:35:09 +0100 Subject: [PATCH 07/33] Shiny notification and owned ambient particles no longer play in spectator --- .../kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt | 2 +- .../cobblemon/mod/common/client/entity/PokemonClientDelegate.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt index e232b56fc3..3bddfe2b9b 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt @@ -156,7 +156,7 @@ object CobblemonClient { return@subscribe } val nearbyShinies = player?.level()?.getEntities(player, AABB.ofSize(player.position(), 16.0, 16.0, 16.0)) { (it is PokemonEntity) && it.pokemon.shiny } - nearbyShinies?.firstOrNull { player.isLookingAt(it) }.let { + nearbyShinies?.firstOrNull { player.isLookingAt(it) && !player.isSpectator }.let { if(it is PokemonEntity) it.delegate.spawnShinyParticle(player!!) } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt index 0b5c3b2073..9d89249e70 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt @@ -379,7 +379,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate { val player = Minecraft.getInstance().player ?: return val isWithinRange = player.position().distanceTo(currentEntity.position()) <= Cobblemon.config.shinyNoticeParticlesDistance - if (currentEntity.pokemon.shiny && currentEntity.ownerUUID == null) { + if (currentEntity.pokemon.shiny && currentEntity.ownerUUID == null && !player.isSpectator ) { if (isWithinRange && !shined) { playShinyEffect("cobblemon:wild_shiny_ring") shined = true From 4c8c0fd0633860c8bf8204b05f86b42275ffdffe Mon Sep 17 00:00:00 2001 From: Sterrezo Date: Sat, 16 Nov 2024 03:01:07 +0100 Subject: [PATCH 08/33] Shiny Pokemon now play the shiny sendout sound when breaking out of a Pokeball instead of the notification --- .../cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt index d412201415..9787bdce0c 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt @@ -380,7 +380,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod after(seconds = 0.8F) { if (pokemon.pokemon.shiny) { - SpawnSnowstormEntityParticlePacket(cobblemonResource("wild_shiny_ring"), pokemon.id, listOf("shiny_particles", "middle")) + SpawnSnowstormEntityParticlePacket(cobblemonResource("shiny_ring"), pokemon.id, listOf("shiny_particles", "middle")) .sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, 32.0, pokemon.level().dimension()) } discard() From 5a2f948acb0f801e3b67eb4d70c15c53c40f9210 Mon Sep 17 00:00:00 2001 From: Sterrezo Date: Sun, 17 Nov 2024 03:04:00 +0100 Subject: [PATCH 09/33] Fixed music not playing when fishing or in npc battles Doozy. Basically, using forceBattle from PokemonEntity.kt makes InitializeInstruction.kt send a packet with battle music before battleTheme is assigned in PlayerBattleActor.kt. There's likely a more sophisticated way to fix this, but this works. Closes #1172 and #1161 --- .../interpreter/instructions/InitializeInstruction.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt b/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt index 3ad6cd08f7..f09df9d766 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt @@ -52,7 +52,10 @@ class InitializeInstruction(val instructionSet: InstructionSet, val message: Bat battle.actors.filterIsInstance().forEach { actor -> val initializePacket = BattleInitializePacket(battle, actor.getSide()) actor.sendUpdate(initializePacket) - actor.sendUpdate(BattleMusicPacket(actor.battleTheme)) + // Delay accounts for some cases when the packet is sent before the battleTheme is known, this happens with forceBattle + afterOnServer(0.1F) { + actor.sendUpdate(BattleMusicPacket(actor.battleTheme)) + } } battle.actors.forEach { actor -> From e20728faa0b8cbdabe351ab30396a8a2f0ee094d Mon Sep 17 00:00:00 2001 From: Sterrezo Date: Sun, 17 Nov 2024 03:05:28 +0100 Subject: [PATCH 10/33] Made owned shiny ambient sounds actually audible --- common/src/main/resources/assets/cobblemon/sounds.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/resources/assets/cobblemon/sounds.json b/common/src/main/resources/assets/cobblemon/sounds.json index 6db590b12c..ed8bc3d10a 100644 --- a/common/src/main/resources/assets/cobblemon/sounds.json +++ b/common/src/main/resources/assets/cobblemon/sounds.json @@ -924,10 +924,10 @@ }, "particle.shiny_ambient_chime": { "sounds": [ - { "name": "cobblemon:shiny/shiny_ambient_chime_1", "volume": 0.05 }, - { "name": "cobblemon:shiny/shiny_ambient_chime_2", "volume": 0.05 }, - { "name": "cobblemon:shiny/shiny_ambient_chime_3", "volume": 0.05 }, - { "name": "cobblemon:shiny/shiny_ambient_chime_4", "volume": 0.05 } + { "name": "cobblemon:shiny/shiny_ambient_chime_1", "volume": 0.3 }, + { "name": "cobblemon:shiny/shiny_ambient_chime_2", "volume": 0.3 }, + { "name": "cobblemon:shiny/shiny_ambient_chime_3", "volume": 0.3 }, + { "name": "cobblemon:shiny/shiny_ambient_chime_4", "volume": 0.3 } ] }, "particle.wild_shiny_ambient_chime": { From f706dcda2659094ba43bc6389b3d5f1ce90252a1 Mon Sep 17 00:00:00 2001 From: mia <15349355-miasmusa@users.noreply.gitlab.com> Date: Sun, 17 Nov 2024 20:45:59 -0500 Subject: [PATCH 11/33] clarify ItemPropertiesMixin inject location --- .../java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java index b854f784b5..983a99eaa6 100644 --- a/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java +++ b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java @@ -22,6 +22,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(ItemProperties.class) public class ItemPropertiesMixin { + // Mixins into the fishing rod's register() lambda expression // Without this, the fishing rod item model appears cast when using a PokeRod in the other hand @Inject(method = "method_27883", at = @At(value = "RETURN", ordinal = 1), cancellable = true) private static void cobblemon$preventRodMixup(ItemStack itemStack, ClientLevel clientLevel, LivingEntity livingEntity, int i, CallbackInfoReturnable cir) { From ce6087f36805b4f3cb91a41f20037913e94a50ee Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 18 Nov 2024 13:28:50 +1000 Subject: [PATCH 12/33] Added panic AI for NPCs and much more molang hooking --- .../api/ai/config/task/CalmDownTaskConfig.kt | 31 +++ .../task/ExitBattleWhenHurtTaskConfig.kt | 75 ++++++++ .../ai/config/task/FleeAttackerTaskConfig.kt | 44 +++++ .../task/FleeNearestHostileTaskConfig.kt | 44 +++++ .../mod/common/api/ai/config/task/README.md | 43 ++++- .../task/SwitchFromActionEffectTaskConfig.kt | 17 +- .../task/SwitchFromChattingTaskConfig.kt | 6 +- .../config/task/SwitchFromFightTaskConfig.kt | 6 +- .../task/SwitchFromNPCBattleTaskConfig.kt | 15 +- ...itchToPanicWhenHostilesNearbyTaskConfig.kt | 35 ++++ .../task/SwitchToPanicWhenHurtTaskConfig.kt | 59 ++++++ .../common/api/ai/config/task/TaskConfig.kt | 6 + .../common/api/battles/model/PokemonBattle.kt | 9 +- .../mod/common/api/molang/MoLangFunctions.kt | 180 +++++++++++++++++- .../api/permission/PermissionValidator.kt | 18 +- .../common/api/storage/party/PartyStore.kt | 7 +- .../mod/common/api/storage/pc/PCStore.kt | 8 +- .../common/battles/pokemon/BattlePokemon.kt | 5 - .../mod/common/entity/npc/NPCEntity.kt | 13 +- .../npc/ai/SwitchFromActionEffectTask.kt | 32 ---- .../entity/npc/ai/SwitchFromBattleTask.kt | 35 ---- .../permission/LaxPermissionValidator.kt | 2 + .../cobblemon/mod/common/pokemon/Pokemon.kt | 63 +++--- .../mod/common/util/MoLangExtensions.kt | 10 +- .../cancels_battle_when_hurt_by_anything.json | 14 ++ .../data/cobblemon/brain_presets/panics.json | 20 ++ .../panics_when_hurt_by_anything.json | 14 ++ .../data/cobblemon/dialogues/npc-example.json | 2 +- .../data/cobblemon/npcs/ai_test.json | 5 +- .../permission/FabricPermissionValidator.kt | 4 + .../permission/ForgePermissionValidator.kt | 11 ++ 31 files changed, 682 insertions(+), 151 deletions(-) create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/CalmDownTaskConfig.kt create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/ExitBattleWhenHurtTaskConfig.kt create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeAttackerTaskConfig.kt create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeNearestHostileTaskConfig.kt create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHostilesNearbyTaskConfig.kt create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHurtTaskConfig.kt delete mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromActionEffectTask.kt delete mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromBattleTask.kt create mode 100644 common/src/main/resources/data/cobblemon/brain_presets/cancels_battle_when_hurt_by_anything.json create mode 100644 common/src/main/resources/data/cobblemon/brain_presets/panics.json create mode 100644 common/src/main/resources/data/cobblemon/brain_presets/panics_when_hurt_by_anything.json diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/CalmDownTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/CalmDownTaskConfig.kt new file mode 100644 index 0000000000..dd493fc1f7 --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/CalmDownTaskConfig.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.api.ai.config.task + +import com.bedrockk.molang.runtime.struct.QueryStruct +import com.cobblemon.mod.common.api.ai.BrainConfigurationContext +import com.cobblemon.mod.common.entity.PosableEntity +import com.cobblemon.mod.common.util.asExpression +import com.cobblemon.mod.common.util.resolveBoolean +import com.cobblemon.mod.common.util.withQueryValue +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.ai.behavior.BehaviorControl +import net.minecraft.world.entity.ai.behavior.VillagerCalmDown + +class CalmDownTaskConfig : SingleTaskConfig { + var condition = "true".asExpression() + override fun createTask( + entity: LivingEntity, + brainConfigurationContext: BrainConfigurationContext + ): BehaviorControl? { + runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf())) + if (!runtime.resolveBoolean(condition)) return null + return VillagerCalmDown.create() + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/ExitBattleWhenHurtTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/ExitBattleWhenHurtTaskConfig.kt new file mode 100644 index 0000000000..38a7300980 --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/ExitBattleWhenHurtTaskConfig.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.api.ai.config.task + +import com.bedrockk.molang.runtime.struct.QueryStruct +import com.cobblemon.mod.common.CobblemonMemories +import com.cobblemon.mod.common.api.ai.BrainConfigurationContext +import com.cobblemon.mod.common.battles.BattleRegistry +import com.cobblemon.mod.common.entity.PosableEntity +import com.cobblemon.mod.common.entity.npc.NPCEntity +import com.cobblemon.mod.common.util.asExpression +import com.cobblemon.mod.common.util.resolveBoolean +import com.cobblemon.mod.common.util.withQueryValue +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.ai.behavior.BehaviorControl +import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder +import net.minecraft.world.entity.ai.behavior.declarative.Trigger +import net.minecraft.world.entity.ai.memory.MemoryModuleType + +class ExitBattleWhenHurtTaskConfig : SingleTaskConfig { + var condition = "true".asExpression() + var includePassiveDamage = true + + override fun createTask( + entity: LivingEntity, + brainConfigurationContext: BrainConfigurationContext + ): BehaviorControl? { + runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf())) + if (!runtime.resolveBoolean(condition)) return null + + if (entity is NPCEntity) { + fun cancelNPCBattles(npcEntity: NPCEntity): Boolean { + val battles = npcEntity.battleIds.mapNotNull(BattleRegistry::getBattle) + battles.forEach { it.end() } + return battles.isNotEmpty() + } + + return if (includePassiveDamage) { + BehaviorBuilder.create { + it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.NPC_BATTLING)) + .apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelNPCBattles(entity as NPCEntity) } } + } + } else { + BehaviorBuilder.create { + it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.NPC_BATTLING)) + .apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelNPCBattles(entity as NPCEntity) } } + } + } + } else { return null }/* else if (entity is PokemonEntity) { TODO uncomment when pokemon have brain :) + fun cancelPokemonBattle(pokemonEntity: PokemonEntity): Boolean { + val battle = BattleRegistry.getBattle(pokemonEntity.battleId ?: return false) + battle?.end() + return battle != null + } + + return if (includePassiveDamage) { + BehaviorBuilder.create { + it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.POKEMON_BATTLING)) + .apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } } + } + } else { + BehaviorBuilder.create { + it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.POKEMON_BATTLING)) + .apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } } + } + } + }*/ + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeAttackerTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeAttackerTaskConfig.kt new file mode 100644 index 0000000000..89677f676b --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeAttackerTaskConfig.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.api.ai.config.task + +import com.bedrockk.molang.runtime.struct.QueryStruct +import com.cobblemon.mod.common.api.ai.BrainConfigurationContext +import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask +import com.cobblemon.mod.common.entity.PosableEntity +import com.cobblemon.mod.common.util.asExpression +import com.cobblemon.mod.common.util.resolveBoolean +import com.cobblemon.mod.common.util.resolveFloat +import com.cobblemon.mod.common.util.resolveInt +import com.cobblemon.mod.common.util.withQueryValue +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.PathfinderMob +import net.minecraft.world.entity.ai.behavior.BehaviorControl +import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom +import net.minecraft.world.entity.ai.memory.MemoryModuleType + +class FleeAttackerTaskConfig : SingleTaskConfig { + var condition = "true".asExpression() + var speedMultiplier = "0.5".asExpression() + var desiredDistance = "9".asExpression() + + override fun createTask( + entity: LivingEntity, + brainConfigurationContext: BrainConfigurationContext + ): BehaviorControl? { + runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf())) + if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null + val speedMultiplier = runtime.resolveFloat(speedMultiplier) + val desiredDistance = runtime.resolveInt(desiredDistance) + return WrapperLivingEntityTask( + SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false), + PathfinderMob::class.java + ) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeNearestHostileTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeNearestHostileTaskConfig.kt new file mode 100644 index 0000000000..bf38e6c75c --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/FleeNearestHostileTaskConfig.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.api.ai.config.task + +import com.bedrockk.molang.runtime.struct.QueryStruct +import com.cobblemon.mod.common.api.ai.BrainConfigurationContext +import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask +import com.cobblemon.mod.common.entity.PosableEntity +import com.cobblemon.mod.common.util.asExpression +import com.cobblemon.mod.common.util.resolveBoolean +import com.cobblemon.mod.common.util.resolveFloat +import com.cobblemon.mod.common.util.resolveInt +import com.cobblemon.mod.common.util.withQueryValue +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.PathfinderMob +import net.minecraft.world.entity.ai.behavior.BehaviorControl +import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom +import net.minecraft.world.entity.ai.memory.MemoryModuleType + +class FleeNearestHostileTaskConfig : SingleTaskConfig { + var condition = "true".asExpression() + var speedMultiplier = "0.5".asExpression() + var desiredDistance = "9".asExpression() + + override fun createTask( + entity: LivingEntity, + brainConfigurationContext: BrainConfigurationContext + ): BehaviorControl? { + runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf())) + if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null + val speedMultiplier = runtime.resolveFloat(speedMultiplier) + val desiredDistance = runtime.resolveInt(desiredDistance) + return WrapperLivingEntityTask( + SetWalkTargetAwayFrom.entity(MemoryModuleType.NEAREST_HOSTILE, speedMultiplier, desiredDistance, false), + PathfinderMob::class.java + ) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/README.md b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/README.md index a09a207b09..ccea004781 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/README.md +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/README.md @@ -89,20 +89,24 @@ on the idle activity so that it automatically transitions activities when a batt ### switch_npc_from_battle The 'switch_npc_from_battle' type creates a task that will switch NPC entities from the battle activity when it is no longer in a battle. You should put this in the battle activity so that it automatically transitions activities when a battle ends. If the NPC is in multiple battles, this -will only activate when no battles remain. -- `activity`: The activity to switch to when the NPC is no longer in a battle. Defaults to `minecraft:idle`. +will only activate when no battles remain. It will rely on the brain schedule to decide what activity to switch back to (usually idle). ### look_at_battling_pokemon The 'look_at_battling_pokemon' type creates a task that will set the entity's look target to switch between the battling Pokémon. - `minDurationTicks`: A MoLang expression that determines the minimum number of ticks that the entity will look at a specific battling Pokémon. Defaults to 40. - `maxDurationTicks`: A MoLang expression that determines the maximum number of ticks that the entity will look at a specific battling Pokémon. Defaults to 80. +### exit_battle_when_hurt +The 'exit_battle_when_hurt' type creates a task that will make the entity exit the battle when it is hurt. +- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick. +- `includePassiveDamage`: A boolean that determines if passive damage (like poison or cacti) should trigger this task. Defaults to true. + ### switch_to_chatting The 'switch_to_chatting' type creates a task that will switch the entity to the chatting activity when a dialogue has been opened with that NPC. ### switch_from_chatting The 'switch_from_chatting' type creates a task that will switch the entity from the chatting activity when the dialogue has been closed with that NPC. -- `activity`: The activity to switch to when the NPC is no longer chatting. Defaults to `minecraft:idle`. +It will rely on the brain schedule to decide what activity to switch back to (usually idle). ### look_at_speaker The 'look_at_speaker' type creates a task that will set the entity's look target to the speaker of the dialogue. @@ -112,7 +116,7 @@ The 'switch_to_action_effect' type creates a task that will switch the entity to ### switch_from_action_effect The 'switch_from_action_effect' type creates a task that will switch the entity from the action effect activity when it is no longer performing an action effect. -- `activity`: The activity to switch to when the NPC is no longer performing an action effect. Defaults to `minecraft:idle`. +It will rely on the brain schedule to decide what activity to switch back to (usually idle). ### switch_to_fight The 'switch_to_fight' type creates a task that will switch the entity to the fight activity when it has an attack target. @@ -120,8 +124,7 @@ The 'switch_to_fight' type creates a task that will switch the entity to the fig - `activity`: The activity to switch to when the NPC is in a fight. Defaults to `minecraft:fight`. ### switch_from_fight -The 'switch_from_fight' type creates a task that will switch the entity from the fight activity when it no longer has an attack target. -- `activity`: The activity to switch to when the NPC is no longer in a fight. Defaults to `minecraft:idle`. +The 'switch_from_fight' type creates a task that will switch the entity from the fight activity when it no longer has an attack target. It will rely on the brain schedule to decide what activity to switch back to (usually idle). ### get_angry_at_attacker The 'get_angry_at_attacker' type creates a task that will make the entity get angry at the attacker when it is attacked. @@ -143,4 +146,30 @@ The 'move_to_attack_target' type creates a task that will make the entity move t The 'melee_attack' type creates a task that will make the entity perform a melee attack on its attack target when it is close enough. The damage dealt is determined by entity properties, which cannot be easily configured at time of writing. - `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick. - `range`: A MoLang expression that determines the maximum distance from the attack target that the entity can be to perform the attack. Defaults to 1.5. -- `cooldownTicks`: A MoLang expression that determines the number of ticks to wait before performing another attack. Defaults to 30. \ No newline at end of file +- `cooldownTicks`: A MoLang expression that determines the number of ticks to wait before performing another attack. Defaults to 30. + +### switch_to_panic_when_hurt +The 'switch_to_panic_when_hurt' type creates a task that will switch the entity to the panic activity when it is hurt. +- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick. +- `includePassiveDamage`: A boolean that determines if passive damage (like poison or cacti) should trigger this task. Defaults to false. + +### switch_to_panic_when_hostiles_nearby +The 'switch_to_panic_when_hostiles_nearby' type creates a task that will switch the entity to the panic activity when hostiles are nearby. The definition of nearby depends +on the entity type and this piggy-backs off the Minecraft list of aggressive mobs and the right distances. +- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick. + +### calm_down +The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker. +- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick. + +### flee_attacker +The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt. +- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick. +- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual. +- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9. + +### flee_nearest_hostile +The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt. +- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick. +- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the nearest hostile. Defaults to 0.5 which is an elevated speed to usual. +- `desiredDistance`: A MoLang expression that determines the distance from the nearest hostile that the entity should try to maintain. Defaults to 9. \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromActionEffectTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromActionEffectTaskConfig.kt index b72b82bdd8..4a57031db7 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromActionEffectTaskConfig.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromActionEffectTaskConfig.kt @@ -8,15 +8,24 @@ package com.cobblemon.mod.common.api.ai.config.task +import com.cobblemon.mod.common.CobblemonMemories import com.cobblemon.mod.common.api.ai.BrainConfigurationContext -import com.cobblemon.mod.common.entity.npc.ai.SwitchFromActionEffectTask import net.minecraft.world.entity.LivingEntity -import net.minecraft.world.entity.schedule.Activity +import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder +import net.minecraft.world.entity.ai.behavior.declarative.Trigger class SwitchFromActionEffectTaskConfig : SingleTaskConfig { - val activity = Activity.IDLE override fun createTask( entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext - ) = SwitchFromActionEffectTask.create(activity) + ) = BehaviorBuilder.create { + it.group( + it.absent(CobblemonMemories.ACTIVE_ACTION_EFFECT) + ).apply(it) { _ -> + Trigger { level, entity, _ -> + entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime) + return@Trigger true + } + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromChattingTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromChattingTaskConfig.kt index f7468b4a3b..a6042f5f6b 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromChattingTaskConfig.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromChattingTaskConfig.kt @@ -13,10 +13,8 @@ import com.cobblemon.mod.common.api.ai.BrainConfigurationContext import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder import net.minecraft.world.entity.ai.behavior.declarative.Trigger -import net.minecraft.world.entity.schedule.Activity class SwitchFromChattingTaskConfig : SingleTaskConfig { - val activity = Activity.IDLE override fun createTask( entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext @@ -24,8 +22,8 @@ class SwitchFromChattingTaskConfig : SingleTaskConfig { it.group( it.absent(CobblemonMemories.DIALOGUES) ).apply(it) { _ -> - Trigger { world, entity, _ -> - entity.brain.setActiveActivityIfPossible(activity) + Trigger { level, entity, _ -> + entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime) return@Trigger true } } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromFightTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromFightTaskConfig.kt index 391e6c41e9..8e93613062 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromFightTaskConfig.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromFightTaskConfig.kt @@ -13,10 +13,8 @@ import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder import net.minecraft.world.entity.ai.behavior.declarative.Trigger import net.minecraft.world.entity.ai.memory.MemoryModuleType -import net.minecraft.world.entity.schedule.Activity class SwitchFromFightTaskConfig : SingleTaskConfig { - val activity = Activity.IDLE override fun createTask( entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext @@ -24,8 +22,8 @@ class SwitchFromFightTaskConfig : SingleTaskConfig { it.group( it.absent(MemoryModuleType.ATTACK_TARGET) ).apply(it) { _ -> - Trigger { world, entity, _ -> - entity.brain.setActiveActivityIfPossible(activity) + Trigger { level, entity, _ -> + entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime) return@Trigger true } } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromNPCBattleTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromNPCBattleTaskConfig.kt index c8df3b155f..13e5ca4282 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromNPCBattleTaskConfig.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchFromNPCBattleTaskConfig.kt @@ -8,18 +8,25 @@ package com.cobblemon.mod.common.api.ai.config.task +import com.cobblemon.mod.common.CobblemonMemories import com.cobblemon.mod.common.api.ai.BrainConfigurationContext -import com.cobblemon.mod.common.entity.npc.ai.SwitchFromBattleTask import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.ai.behavior.BehaviorControl -import net.minecraft.world.entity.schedule.Activity +import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder +import net.minecraft.world.entity.ai.behavior.declarative.Trigger class SwitchFromNPCBattleTaskConfig : SingleTaskConfig { - val activity: Activity = Activity.IDLE override fun createTask( entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext ): BehaviorControl? { - return SwitchFromBattleTask.create(activity) + return BehaviorBuilder.create { + it.group(it.absent(CobblemonMemories.NPC_BATTLING)).apply(it) { _ -> + Trigger { level, entity, _ -> + entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime) + true + } + } + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHostilesNearbyTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHostilesNearbyTaskConfig.kt new file mode 100644 index 0000000000..959947045f --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHostilesNearbyTaskConfig.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.api.ai.config.task + +import com.cobblemon.mod.common.api.ai.BrainConfigurationContext +import com.cobblemon.mod.common.util.asExpression +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.PathfinderMob +import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder +import net.minecraft.world.entity.ai.behavior.declarative.Trigger +import net.minecraft.world.entity.ai.memory.MemoryModuleType +import net.minecraft.world.entity.schedule.Activity + +class SwitchToPanicWhenHostilesNearbyTaskConfig : SingleTaskConfig { + var condition = "true".asExpression() + override fun createTask( + entity: LivingEntity, + brainConfigurationContext: BrainConfigurationContext + ) = BehaviorBuilder.create { + it.group(it.present(MemoryModuleType.NEAREST_HOSTILE)) + .apply(it) { nearestHostile -> + Trigger { world, entity, _ -> + (entity as? PathfinderMob)?.navigation?.stop() + entity.brain.setActiveActivityIfPossible(Activity.PANIC) + return@Trigger true + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHurtTaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHurtTaskConfig.kt new file mode 100644 index 0000000000..700dab9748 --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/SwitchToPanicWhenHurtTaskConfig.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.api.ai.config.task + +import com.bedrockk.molang.runtime.struct.QueryStruct +import com.cobblemon.mod.common.api.ai.BrainConfigurationContext +import com.cobblemon.mod.common.entity.PosableEntity +import com.cobblemon.mod.common.util.asExpression +import com.cobblemon.mod.common.util.resolveBoolean +import com.cobblemon.mod.common.util.withQueryValue +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.PathfinderMob +import net.minecraft.world.entity.ai.behavior.BehaviorControl +import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder +import net.minecraft.world.entity.ai.behavior.declarative.Trigger +import net.minecraft.world.entity.ai.memory.MemoryModuleType +import net.minecraft.world.entity.schedule.Activity + +class SwitchToPanicWhenHurtTaskConfig : SingleTaskConfig { + var condition = "true".asExpression() + var includePassiveDamage = false + + override fun createTask( + entity: LivingEntity, + brainConfigurationContext: BrainConfigurationContext + ): BehaviorControl? { + runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf())) + if (!runtime.resolveBoolean(condition)) return null + return if (includePassiveDamage) { + BehaviorBuilder.create { + it.group(it.present(MemoryModuleType.HURT_BY)) + .apply(it) { _ -> + Trigger { world, entity, _ -> + (entity as? PathfinderMob)?.navigation?.stop() + entity.brain.setActiveActivityIfPossible(Activity.PANIC) + return@Trigger true + } + } + } + } else { + BehaviorBuilder.create { + it.group(it.present(MemoryModuleType.HURT_BY_ENTITY)) + .apply(it) { _ -> + Trigger { world, entity, _ -> + (entity as? PathfinderMob)?.navigation?.stop() + entity.brain.setActiveActivityIfPossible(Activity.PANIC) + return@Trigger true + } + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/TaskConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/TaskConfig.kt index bb3e9b38ed..d6048713c9 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/TaskConfig.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/TaskConfig.kt @@ -52,6 +52,12 @@ fun interface TaskConfig { "look_at_speaker" to LookAtSpeakerTaskConfig::class.java, "switch_to_action_effect" to SwitchToActionEffectTaskConfig::class.java, "switch_from_action_effect" to SwitchFromActionEffectTaskConfig::class.java, + "exit_battle_when_hurt" to ExitBattleWhenHurtTaskConfig::class.java, + "switch_to_panic_when_hurt" to SwitchToPanicWhenHurtTaskConfig::class.java, + "switch_to_panic_when_hostiles_nearby" to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java, + "calm_down" to CalmDownTaskConfig::class.java, + "flee_attacker" to FleeAttackerTaskConfig::class.java, + "flee_nearest_hostile" to FleeNearestHostileTaskConfig::class.java, ) val runtime = MoLangRuntime().setup() diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt index 3fc37c722d..ba402864ed 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt @@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.battles.model import com.bedrockk.molang.runtime.MoLangRuntime import com.bedrockk.molang.runtime.struct.QueryStruct import com.bedrockk.molang.runtime.value.DoubleValue -import com.bedrockk.molang.runtime.value.MoValue import com.cobblemon.mod.common.Cobblemon import com.cobblemon.mod.common.Cobblemon.LOGGER import com.cobblemon.mod.common.CobblemonNetwork @@ -22,7 +21,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor import com.cobblemon.mod.common.api.events.CobblemonEvents import com.cobblemon.mod.common.api.events.battles.BattleFledEvent -import com.cobblemon.mod.common.api.moves.Moves import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue import com.cobblemon.mod.common.api.net.NetworkPacket import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore @@ -51,17 +49,16 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement -import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager import com.cobblemon.mod.common.util.battleLang import com.cobblemon.mod.common.util.getPlayer -import net.minecraft.network.chat.Component import com.cobblemon.mod.common.util.giveOrDropItemStack import com.cobblemon.mod.common.util.itemRegistry -import net.minecraft.resources.ResourceLocation import java.io.File import java.util.UUID import java.util.concurrent.CompletableFuture import java.util.concurrent.ConcurrentLinkedDeque +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerPlayer import net.minecraft.world.item.ItemStack @@ -280,7 +277,7 @@ open class PokemonBattle( } } } - if(actor.itemsUsed.isNotEmpty() && actor.getPlayerUUIDs().count() > 0) { + if (actor.itemsUsed.isNotEmpty() && actor.getPlayerUUIDs().count() > 0) { val player = actor.getPlayerUUIDs().first().getPlayer() player?.level()?.itemRegistry.let { registry -> actor.itemsUsed.mapNotNull { registry?.get(ResourceLocation.tryBySeparator(it.itemName.substringAfter('.'), '.')) } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt index 74cb1c66f3..af394d9133 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt @@ -27,9 +27,15 @@ import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext import com.cobblemon.mod.common.api.moves.animations.ActionEffects import com.cobblemon.mod.common.api.moves.animations.NPCProvider +import com.cobblemon.mod.common.api.pokemon.stats.Stats import com.cobblemon.mod.common.api.scripting.CobblemonScripts +import com.cobblemon.mod.common.api.storage.PokemonStore import com.cobblemon.mod.common.api.storage.party.PartyStore +import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore +import com.cobblemon.mod.common.api.storage.pc.PCPosition +import com.cobblemon.mod.common.api.storage.pc.PCStore import com.cobblemon.mod.common.api.text.text +import com.cobblemon.mod.common.battles.BattleRegistry import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions import com.cobblemon.mod.common.entity.npc.NPCEntity import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag @@ -43,6 +49,7 @@ import java.util.UUID import net.minecraft.commands.arguments.EntityAnchorArgument import net.minecraft.core.Holder import net.minecraft.core.Registry +import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.core.registries.Registries import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.DoubleTag @@ -125,15 +132,12 @@ object MoLangFunctions { val map = hashMapOf>() map.put("username") { _ -> StringValue(player.gameProfile.name) } map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) } - map.put("data") { _ -> return@put if (player is ServerPlayer) Cobblemon.molangData.load(player.uuid) else DoubleValue(0) } - map.put("save_data") { _ -> if (player is ServerPlayer) Cobblemon.molangData.save(player.uuid) else DoubleValue(0) } map.put("main_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.mainHandItem.item).asMoLangValue(Registries.ITEM) } map.put("off_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.offhandItem.item).asMoLangValue(Registries.ITEM) } map.put("face") { params -> ObjectValue(PlayerDialogueFaceProvider(player.uuid, params.getBooleanOrNull(0) != false)) } map.put("swing_hand") { _ -> player.swing(player.usedItemHand) } map.put("food_level") { _ -> DoubleValue(player.foodData.foodLevel) } map.put("saturation_level") { _ -> DoubleValue(player.foodData.saturationLevel) } -// map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, Permission)) } map.put("tell") { params -> val message = params.getString(0).text() val overlay = params.getBooleanOrNull(1) ?: false @@ -176,6 +180,13 @@ object MoLangFunctions { return@put DoubleValue.ZERO } } + map.put("party") { player.party().struct } + map.put("pc") { player.pc().struct } + map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) } + map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) } + map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) } + map.put("in_battle") { DoubleValue(player.isInBattle()) } + map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO } } map } @@ -255,6 +266,17 @@ object MoLangFunctions { map.put("has_aspect") { params -> DoubleValue(npc.aspects.contains(params.getString(0))) } map.put("face") { params -> ObjectValue(ReferenceDialogueFaceProvider(npc.id, params.getBooleanOrNull(0) != false)) } map.put("in_battle") { DoubleValue(npc.isInBattle()) } + map.put("battles") { ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }.mapIndexed { index, value -> "$index" to value }.toMap()) } + map.put("stop_battles") { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } } + map.put("is_doing_activity") { params -> + val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") } + val activities = identifiers.mapNotNull { identifier -> BuiltInRegistries.ACTIVITY.get(identifier) } + if (activities.isNotEmpty()) { + return@put DoubleValue(activities.any { activity -> npc.brain.isActive(activity) }) + } else { + return@put DoubleValue.ZERO + } + } map.put("run_script_on_client") { params -> val world = npc.level() if (world is ServerLevel) { @@ -311,6 +333,8 @@ object MoLangFunctions { npc.lookAt(EntityAnchorArgument.Anchor.EYES, pos) } map.put("environment") { _ -> npc.runtime.environment } + map.put("party") { npc.party?.struct ?: DoubleValue.ZERO } + map.put("has_party") { DoubleValue(npc.party != null) } map } ) @@ -329,6 +353,7 @@ object MoLangFunctions { val actor = battle.actors.find { it.uuid == uuid } ?: return@put DoubleValue.ZERO return@put actor.struct } + map.put("stop") { _ -> battle.stop() } map } ) @@ -336,10 +361,30 @@ object MoLangFunctions { val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap>>( { pokemon -> val map = hashMapOf>() + map.put("id") { StringValue(pokemon.uuid.toString()) } + map.put("level") { DoubleValue(pokemon.level.toDouble()) } + map.put("max_hp") { DoubleValue(pokemon.maxHealth.toDouble()) } + map.put("current_hp") { DoubleValue(pokemon.currentHealth.toDouble()) } + map.put("friendship") { DoubleValue(pokemon.friendship.toDouble()) } + map.put("evs") { + val struct = QueryStruct(hashMapOf()) + for (stat in Stats.PERMANENT) { + struct.addFunction(stat.showdownId) { DoubleValue(pokemon.evs.getOrDefault(stat).toDouble()) } + } + struct + } + map.put("ivs") { + val struct = QueryStruct(hashMapOf()) + for (stat in Stats.PERMANENT) { + struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.getOrDefault(stat).toDouble()) } + } + struct + } map.put("is_wild") { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) } map.put("is_shiny") { DoubleValue(pokemon.shiny) } map.put("form") { StringValue(pokemon.form.name) } map.put("weight") { DoubleValue(pokemon.species.weight.toDouble()) } + map.put("matches") { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) } map } ) @@ -355,21 +400,113 @@ object MoLangFunctions { } ) + val pokemonStoreFunctions = mutableListOf<(PokemonStore<*>) -> HashMap>>( + { store -> + val map = hashMapOf>() + map.put("add") { params -> + val pokemon = params.get>(0) + return@put DoubleValue(store.add(pokemon.obj)) + } + map.put("add_by_properties") { params -> + val props = params.getString(0).toProperties() + val player = (store as? PlayerPartyStore)?.playerUUID?.getPlayer() // Only really know for sure when it's a player party store + val pokemon = props.create(player = player) + return@put DoubleValue(store.add(pokemon)) + } + map.put("find_by_properties") { params -> + val props = params.getString(0).toProperties() + val pokemon = store.find { props.matches(it) } + return@put pokemon?.asStruct() ?: DoubleValue.ZERO + } + map.put("find_all_by_properties") { params -> + val props = params.getString(0).toProperties() + val pokemon = store.filter { props.matches(it) } + return@put ArrayStruct(pokemon.mapIndexed { index, value -> "$index" to value.asStruct() }.toMap()) + } + map.put("find_by_id") { params -> + val id = params.getString(0).asUUID + val pokemon = store.find { it.uuid == id } + return@put pokemon?.asStruct() ?: DoubleValue.ZERO + } + map.put("remove_by_id") { params -> + val id = params.getString(0).asUUID + val pokemon = store.find { it.uuid == id } ?: return@put DoubleValue.ZERO + return@put DoubleValue(store.remove(pokemon)) + } + map.put("average_level") { _ -> + var numberOfPokemon = 0 + var totalLevel = 0 + for (pokemon in store) { + totalLevel += pokemon.level + numberOfPokemon++ + } + if (numberOfPokemon == 0) { + return@put DoubleValue.ZERO + } + return@put DoubleValue(totalLevel.toDouble() / numberOfPokemon) + } + map.put("count") { _ -> DoubleValue(store.count()) } + map.put("count_by_properties") { params -> + val props = params.getString(0).toProperties() + return@put DoubleValue(store.count { props.matches(it) }) + } + map.put("highest_level") { + val highest = store.maxByOrNull { it.level } + return@put highest?.asStruct() ?: DoubleValue.ZERO + } + map.put("lowest_level") { + val lowest = store.minByOrNull { it.level } + return@put lowest?.asStruct() ?: DoubleValue.ZERO + } + map.put("heal") { + for (pokemon in store) { + pokemon.heal() + } + return@put DoubleValue.ONE + } + map.put("healing_remainder_percent") { _ -> + var totalPercent = 0.0f + for (pokemon in store) { + totalPercent += (1.0f - (pokemon.currentHealth.toFloat() / pokemon.maxHealth)) + } + DoubleValue(totalPercent) + } + map.put("has_usable_pokemon") { _ -> DoubleValue(store.any { !it.isFainted() }) } + map + } + ) + val partyFunctions = mutableListOf<(PartyStore) -> HashMap>>( { party -> val map = hashMapOf>() map.put("get_pokemon") { params -> val index = params.getInt(0) val pokemon = party.get(index) ?: return@put DoubleValue.ZERO - val struct = VariableStruct() - pokemon.writeVariables(struct) - return@put struct + return@put pokemon.struct } - map.put("healing_remainder_percent") { _ -> DoubleValue(party.getHealingRemainderPercent()) } - return@mutableListOf map }) + val pcFunctions = mutableListOf<(PCStore) -> HashMap>>( + { pc -> + val map = hashMapOf>() + map.put("get_pokemon") { params -> + val box = params.getInt(0) + val slot = params.getInt(1) + val pokemon = pc[PCPosition(box, slot)] ?: return@put DoubleValue.ZERO + return@put pokemon.struct + } + map.put("resize") { params -> + val newSize = params.getInt(0) + val lockNewSize = params.getBooleanOrNull(1) == true + pc.resize(newSize, lockNewSize) + return@put DoubleValue.ONE + } + map.put("get_box_count") { _ -> DoubleValue(pc.boxes.size.toDouble()) } + return@mutableListOf map + } + ) + fun Holder.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions) fun Holder.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions) fun Holder.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions) @@ -384,6 +521,33 @@ object MoLangFunctions { return value } + // We need to migrate the writeVariables thing to be all about query structs, variable doesn't make sense and I don't want to break fringe 1.6 compatibility issues close to release + fun Pokemon.asStruct(): ObjectValue { + val queryStruct = ObjectValue(this) + queryStruct.addFunctions(pokemonFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) + return queryStruct + } + + fun PartyStore.asMoLangValue(): ObjectValue { + val value = ObjectValue( + obj = this, + stringify = { it.toString() } + ) + value.addFunctions(pokemonStoreFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) + value.addFunctions(partyFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) + return value + } + + fun PCStore.asMoLangValue(): ObjectValue { + val value = ObjectValue( + obj = this, + stringify = { it.toString() } + ) + value.addFunctions(pokemonStoreFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) + value.addFunctions(pcFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) + return value + } + fun NPCEntity.asMoLangValue(): ObjectValue { val value = ObjectValue( obj = this, diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt index 6d494e4185..43fbdd14f5 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt @@ -24,7 +24,6 @@ interface PermissionValidator { /** * Invoked when the validator replaces the existing one in [Cobblemon.permissionValidator]. - * */ fun initialize() @@ -36,6 +35,14 @@ interface PermissionValidator { * @return If the [player] has the [permission]. */ fun hasPermission(player: ServerPlayer, permission: Permission): Boolean + /** + * Validates a permission for [ServerPlayer] based only on a permission string and a permission level. + * + * @param player The target [ServerPlayer]. + * @param permission The permission string being queried such as cobblemon.command.giveallpokemon. + * @param level The permission level being queried. 4 is generally used for cheats. + */ + fun hasPermission(player: ServerPlayer, permission: String, level: Int): Boolean /** * Validates a permission for [CommandSource]. @@ -45,5 +52,12 @@ interface PermissionValidator { * @return If the [source] has the [permission]. */ fun hasPermission(source: CommandSourceStack, permission: Permission): Boolean - + /** + * Validates a permission for [CommandSourceStack] based only on a permission string and a permission level. + * + * @param source The target [CommandSourceStack]. + * @param permission The permission string being queried such as cobblemon.command.giveallpokemon. + * @param level The permission level being queried. 4 is generally used for cheats. + */ + fun hasPermission(source: CommandSourceStack, permission: String, level: Int): Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/party/PartyStore.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/party/PartyStore.kt index b51f025893..057ba938b0 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/party/PartyStore.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/party/PartyStore.kt @@ -9,6 +9,7 @@ package com.cobblemon.mod.common.api.storage.party import com.cobblemon.mod.common.CobblemonNetwork.sendPacket +import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue import com.cobblemon.mod.common.api.reactive.Observable import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter import com.cobblemon.mod.common.api.reactive.SimpleObservable @@ -25,11 +26,11 @@ import com.cobblemon.mod.common.pokemon.Pokemon import com.cobblemon.mod.common.util.DataKeys import com.cobblemon.mod.common.util.server import com.google.gson.JsonObject +import java.util.Collections import java.util.UUID +import net.minecraft.core.RegistryAccess import net.minecraft.nbt.CompoundTag import net.minecraft.server.level.ServerPlayer -import java.util.Collections -import net.minecraft.core.RegistryAccess /** * A [PokemonStore] for a party of Pokémon. This is a simple structure that by default will hold 6 nullable slots of Pokémon. @@ -47,6 +48,8 @@ open class PartyStore(override val uuid: UUID) : PokemonStore() { /** A list of player UUIDs representing players that are observing this store. This is NOT serialized/deserialized. */ var observerUUIDs = mutableListOf() + val struct = asMoLangValue() + override fun iterator() = slots.filterNotNull().iterator() /** Gets the Pokémon at the specified slot. It will return null if the slot is empty or the given slot is out of bounds. */ fun get(slot: Int) = slot.takeIf { it < slots.size && it >= 0 }?.let { slots[it] } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt index 2f364f177d..b0ddb3a19a 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt @@ -9,6 +9,7 @@ package com.cobblemon.mod.common.api.storage.pc import com.cobblemon.mod.common.Cobblemon +import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue import com.cobblemon.mod.common.api.reactive.SimpleObservable import com.cobblemon.mod.common.api.storage.BottomlessStore import com.cobblemon.mod.common.api.storage.PokemonStore @@ -23,11 +24,11 @@ import com.cobblemon.mod.common.util.DataKeys import com.cobblemon.mod.common.util.getPlayer import com.cobblemon.mod.common.util.lang import com.google.gson.JsonObject +import java.util.UUID +import net.minecraft.core.RegistryAccess import net.minecraft.nbt.CompoundTag import net.minecraft.network.chat.MutableComponent import net.minecraft.server.level.ServerPlayer -import java.util.* -import net.minecraft.core.RegistryAccess /** * The store used for PCs. It is divided into some number of [PCBox]es, and can @@ -54,6 +55,9 @@ open class PCStore( override fun iterator() = boxes.flatMap { it.toList() }.iterator() override fun getObservingPlayers() = observingUUIDs.mapNotNull { it.getPlayer() } + + val struct = asMoLangValue() + fun addObserver(player: ServerPlayer) { observingUUIDs.add(player.uuid) sendTo(player) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/battles/pokemon/BattlePokemon.kt b/common/src/main/kotlin/com/cobblemon/mod/common/battles/pokemon/BattlePokemon.kt index 00bea5495d..d15419d610 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/battles/pokemon/BattlePokemon.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/battles/pokemon/BattlePokemon.kt @@ -8,7 +8,6 @@ package com.cobblemon.mod.common.battles.pokemon -import com.bedrockk.molang.runtime.struct.VariableStruct import com.cobblemon.mod.common.api.battles.model.actor.BattleActor import com.cobblemon.mod.common.api.moves.MoveSet import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager @@ -107,9 +106,5 @@ open class BattlePokemon( !isSentOut() && !willBeSwitchedIn && health > 0 } - fun writeVariables(struct: VariableStruct) { - effectedPokemon.writeVariables(struct) - } - fun getIllusion(): BattlePokemon? = this.actor.activePokemon.find { it.battlePokemon == this }?.illusion } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt index ad3556d6ff..499a2e124a 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt @@ -52,7 +52,6 @@ import com.google.common.collect.ImmutableList import com.mojang.authlib.GameProfile import com.mojang.authlib.ProfileLookupCallback import com.mojang.serialization.Dynamic -import java.lang.Exception import java.net.URI import java.util.UUID import java.util.concurrent.CompletableFuture @@ -66,6 +65,7 @@ import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket import net.minecraft.network.protocol.game.ClientGamePacketListener import net.minecraft.network.protocol.game.ClientboundAddEntityPacket +import net.minecraft.network.protocol.game.DebugPackets import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.network.syncher.EntityDataSerializers import net.minecraft.network.syncher.SynchedEntityData @@ -203,7 +203,8 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P SensorType.HURT_BY, SensorType.NEAREST_PLAYERS, CobblemonSensors.BATTLING_POKEMON, - CobblemonSensors.NPC_BATTLING + CobblemonSensors.NPC_BATTLING, + SensorType.VILLAGER_HOSTILES ) val MEMORY_MODULES: List> = ImmutableList.of( @@ -223,6 +224,7 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P MemoryModuleType.ATTACK_COOLING_DOWN, CobblemonMemories.DIALOGUES, CobblemonMemories.ACTIVE_ACTION_EFFECT, + MemoryModuleType.NEAREST_HOSTILE ) const val SEND_OUT_ANIMATION = "send_out" @@ -294,6 +296,13 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P getBrain().tick(level() as ServerLevel, this) } + override fun sendDebugPackets() { + super.sendDebugPackets() + DebugPackets.sendEntityBrain(this) + DebugPackets.sendGoalSelector(level(), this, this.goalSelector) + DebugPackets.sendPathFindingPacket(level(), this, this.navigation.path, this.navigation.path?.distToTarget ?: 0F) + } + override fun saveWithoutId(nbt: CompoundTag): CompoundTag { super.saveWithoutId(nbt) nbt.put(DataKeys.NPC_DATA, MoLangFunctions.writeMoValueToNBT(data)) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromActionEffectTask.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromActionEffectTask.kt deleted file mode 100644 index d95f9ab1b1..0000000000 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromActionEffectTask.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2023 Cobblemon Contributors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package com.cobblemon.mod.common.entity.npc.ai - -import com.cobblemon.mod.common.CobblemonMemories -import com.cobblemon.mod.common.entity.npc.NPCEntity -import net.minecraft.world.entity.LivingEntity -import net.minecraft.world.entity.ai.behavior.OneShot -import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder -import net.minecraft.world.entity.ai.behavior.declarative.Trigger -import net.minecraft.world.entity.schedule.Activity - -object SwitchFromActionEffectTask { - fun create(activity: Activity = Activity.IDLE): OneShot { - return BehaviorBuilder.create { - it.group( - it.absent(CobblemonMemories.ACTIVE_ACTION_EFFECT) - ).apply(it) { _ -> - Trigger { world, entity, time -> - entity.brain.setActiveActivityIfPossible(activity) - return@Trigger true - } - } - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromBattleTask.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromBattleTask.kt deleted file mode 100644 index 87a25e857e..0000000000 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/ai/SwitchFromBattleTask.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Cobblemon Contributors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package com.cobblemon.mod.common.entity.npc.ai - -import com.cobblemon.mod.common.CobblemonMemories -import net.minecraft.world.entity.LivingEntity -import net.minecraft.world.entity.ai.behavior.OneShot -import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder -import net.minecraft.world.entity.ai.behavior.declarative.Trigger -import net.minecraft.world.entity.schedule.Activity - -/** - * When NPC battling memory is removed, this task swaps the NPC's activity over to idle. - * - * @author Hiroku - * @since February 24th, 2024 - */ -object SwitchFromBattleTask { - fun create(activity: Activity): OneShot { - return BehaviorBuilder.create { - it.group(it.absent(CobblemonMemories.NPC_BATTLING)).apply(it) { _ -> - Trigger { _, entity, _ -> - entity.brain.setActiveActivityIfPossible(activity) - true - } - } - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/permission/LaxPermissionValidator.kt b/common/src/main/kotlin/com/cobblemon/mod/common/permission/LaxPermissionValidator.kt index 50aa3be806..b21a672e58 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/permission/LaxPermissionValidator.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/permission/LaxPermissionValidator.kt @@ -26,4 +26,6 @@ class LaxPermissionValidator : PermissionValidator { override fun hasPermission(player: ServerPlayer, permission: Permission) = player.hasPermissions(permission.level.numericalValue) override fun hasPermission(source: CommandSourceStack, permission: Permission) = source.hasPermission(permission.level.numericalValue) + override fun hasPermission(player: ServerPlayer, permission: String, level: Int) = player.hasPermissions(level) + override fun hasPermission(source: CommandSourceStack, permission: String, level: Int) = source.hasPermission(level) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt b/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt index c1d7f4c510..0f996d0c76 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt @@ -8,8 +8,6 @@ package com.cobblemon.mod.common.pokemon -import com.bedrockk.molang.runtime.struct.VariableStruct -import com.bedrockk.molang.runtime.value.DoubleValue import com.cobblemon.mod.common.Cobblemon import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers import com.cobblemon.mod.common.CobblemonSounds @@ -23,14 +21,25 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents.FRIENDSHIP_UPDATED import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_FAINTED import com.cobblemon.mod.common.api.events.pokemon.* import com.cobblemon.mod.common.api.events.pokemon.healing.PokemonHealedEvent -import com.cobblemon.mod.common.api.moves.* +import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonFunctions +import com.cobblemon.mod.common.api.molang.ObjectValue +import com.cobblemon.mod.common.api.moves.BenchedMove +import com.cobblemon.mod.common.api.moves.BenchedMoves +import com.cobblemon.mod.common.api.moves.Move +import com.cobblemon.mod.common.api.moves.MoveSet +import com.cobblemon.mod.common.api.moves.MoveTemplate +import com.cobblemon.mod.common.api.moves.Moves import com.cobblemon.mod.common.api.pokeball.PokeBalls import com.cobblemon.mod.common.api.pokemon.Natures import com.cobblemon.mod.common.api.pokemon.PokemonProperties import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor import com.cobblemon.mod.common.api.pokemon.PokemonSpecies import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider -import com.cobblemon.mod.common.api.pokemon.evolution.* +import com.cobblemon.mod.common.api.pokemon.evolution.Evolution +import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionController +import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay +import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionProxy +import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup import com.cobblemon.mod.common.api.pokemon.experience.ExperienceSource import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature @@ -55,7 +64,6 @@ import com.cobblemon.mod.common.api.types.ElementalType import com.cobblemon.mod.common.api.types.ElementalTypes import com.cobblemon.mod.common.api.types.tera.TeraType import com.cobblemon.mod.common.api.types.tera.TeraTypes -import com.cobblemon.mod.common.client.entity.PokemonClientDelegate import com.cobblemon.mod.common.config.CobblemonConfig import com.cobblemon.mod.common.datafixer.CobblemonSchemas import com.cobblemon.mod.common.datafixer.CobblemonTypeReferences @@ -68,7 +76,11 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.* import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_DURATION import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.THROW_DURATION import com.cobblemon.mod.common.pokeball.PokeBall -import com.cobblemon.mod.common.pokemon.activestate.* +import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState +import com.cobblemon.mod.common.pokemon.activestate.InactivePokemonState +import com.cobblemon.mod.common.pokemon.activestate.PokemonState +import com.cobblemon.mod.common.pokemon.activestate.SentOutState +import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState import com.cobblemon.mod.common.pokemon.evolution.CobblemonEvolutionProxy import com.cobblemon.mod.common.pokemon.evolution.controller.ClientEvolutionController import com.cobblemon.mod.common.pokemon.evolution.controller.ServerEvolutionController @@ -80,12 +92,19 @@ import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty import com.cobblemon.mod.common.pokemon.status.PersistentStatus import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer -import com.cobblemon.mod.common.util.* -import com.cobblemon.mod.common.util.codec.internal.* +import com.cobblemon.mod.common.util.DataKeys +import com.cobblemon.mod.common.util.cobblemonResource import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1 +import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP2 +import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP3 import com.cobblemon.mod.common.util.codec.internal.PokemonP1 import com.cobblemon.mod.common.util.codec.internal.PokemonP2 import com.cobblemon.mod.common.util.codec.internal.PokemonP3 +import com.cobblemon.mod.common.util.lang +import com.cobblemon.mod.common.util.playSoundServer +import com.cobblemon.mod.common.util.server +import com.cobblemon.mod.common.util.setPositionSafely +import com.cobblemon.mod.common.util.toBlockPos import com.google.gson.JsonObject import com.mojang.datafixers.util.Pair import com.mojang.serialization.Codec @@ -102,7 +121,9 @@ import kotlin.math.roundToInt import kotlin.random.Random import net.minecraft.core.BlockPos import net.minecraft.core.RegistryAccess -import net.minecraft.nbt.* +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtOps +import net.minecraft.nbt.StringTag import net.minecraft.network.RegistryFriendlyByteBuf import net.minecraft.network.chat.MutableComponent import net.minecraft.network.chat.contents.PlainTextContents @@ -117,10 +138,14 @@ import net.minecraft.util.StringRepresentable import net.minecraft.world.InteractionHand import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.player.Player -import net.minecraft.world.entity.vehicle.Boat import net.minecraft.world.item.ItemStack import net.minecraft.world.level.Level -import net.minecraft.world.level.block.* +import net.minecraft.world.level.block.CactusBlock +import net.minecraft.world.level.block.CampfireBlock +import net.minecraft.world.level.block.FireBlock +import net.minecraft.world.level.block.MagmaBlock +import net.minecraft.world.level.block.SweetBerryBushBlock +import net.minecraft.world.level.block.WitherRoseBlock import net.minecraft.world.phys.Vec3 enum class OriginalTrainerType : StringRepresentable { @@ -1497,6 +1522,9 @@ open class Pokemon : ShowdownIdentifiable { private val observables = mutableListOf>() val anyChangeObservable = SimpleObservable() + val struct = ObjectValue(this) + .addPokemonFunctions(this) + fun markFeatureDirty(feature: SpeciesFeature) { _features.emit(feature) } @@ -1505,19 +1533,6 @@ open class Pokemon : ShowdownIdentifiable { /** Returns an [Observable] that emits Unit whenever any change is made to this Pokémon. The change itself is not included. */ fun getChangeObservable(): Observable = anyChangeObservable - fun writeVariables(struct: VariableStruct) { - struct.setDirectly("level", DoubleValue(level.toDouble())) - struct.setDirectly("max_hp", DoubleValue(maxHealth.toDouble())) - struct.setDirectly("current_hp", DoubleValue(currentHealth.toDouble())) - struct.setDirectly("friendship", DoubleValue(friendship.toDouble())) - struct.setDirectly("shiny", DoubleValue(shiny)) - for (stat in Stats.PERMANENT) { - struct.setDirectly("ev_${stat.showdownId}", DoubleValue(evs.getOrDefault(stat).toDouble())) - struct.setDirectly("iv_${stat.showdownId}", DoubleValue(ivs.getOrDefault(stat).toDouble())) - struct.setDirectly("stat_${stat.showdownId}", DoubleValue(getStat(stat).toDouble())) - } - } - /** * Used for when 'this' would be called in leaking context. * diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt index 63cfedae01..89eabf7053 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt @@ -16,7 +16,6 @@ import com.bedrockk.molang.runtime.MoLangRuntime import com.bedrockk.molang.runtime.MoParams import com.bedrockk.molang.runtime.struct.ArrayStruct import com.bedrockk.molang.runtime.struct.ContextStruct -import com.bedrockk.molang.runtime.struct.VariableStruct import com.bedrockk.molang.runtime.value.DoubleValue import com.bedrockk.molang.runtime.value.MoValue import com.cobblemon.mod.common.Cobblemon @@ -161,15 +160,11 @@ fun String.asExpressionLike() = try { fun Double.asExpression() = toString().asExpression() // Use the string route because it remembers the original string value for serialization fun MoLangEnvironment.writePokemon(pokemon: Pokemon) { - val pokemonStruct = VariableStruct() - pokemon.writeVariables(pokemonStruct) - setSimpleVariable("pokemon", pokemonStruct) + setSimpleVariable("pokemon", pokemon.struct) } fun MoLangEnvironment.writePokemon(pokemon: BattlePokemon) { - val pokemonStruct = VariableStruct() - pokemon.writeVariables(pokemonStruct) - setSimpleVariable("pokemon", pokemonStruct) + setSimpleVariable("pokemon", pokemon.effectedPokemon.struct) } fun List.asExpressionLike() = joinToString(separator = "\n").asExpressionLike() @@ -182,6 +177,7 @@ fun List.resolveObject(runtime: MoLangRuntime, context: Map index) getString(index) else null fun MoParams.getDoubleOrNull(index: Int) = if (params.size > index) getDouble(index) else null fun MoParams.getBooleanOrNull(index: Int) = if (params.size > index) getDouble(index) == 1.0 else null +fun MoParams.getIntOrNull(index: Int) = if (params.size > index) getDouble(index).toInt() else null fun MoLangRuntime.withQueryValue(name: String, value: MoValue): MoLangRuntime { environment.query.functions.put(name) { value } diff --git a/common/src/main/resources/data/cobblemon/brain_presets/cancels_battle_when_hurt_by_anything.json b/common/src/main/resources/data/cobblemon/brain_presets/cancels_battle_when_hurt_by_anything.json new file mode 100644 index 0000000000..edd56be067 --- /dev/null +++ b/common/src/main/resources/data/cobblemon/brain_presets/cancels_battle_when_hurt_by_anything.json @@ -0,0 +1,14 @@ +[ + { + "type": "add_tasks_to_activity", + "activity": "cobblemon:battling", + "tasksByPriority": { + "0": [ + { + "type": "exit_battle_when_hurt", + "includePassiveDamage": true + } + ] + } + } +] \ No newline at end of file diff --git a/common/src/main/resources/data/cobblemon/brain_presets/panics.json b/common/src/main/resources/data/cobblemon/brain_presets/panics.json new file mode 100644 index 0000000000..0e8279e4dd --- /dev/null +++ b/common/src/main/resources/data/cobblemon/brain_presets/panics.json @@ -0,0 +1,20 @@ +[ + { + "type": "add_tasks_to_activity", + "activity": "panic", + "tasksByPriority": { + "0": ["calm_down", "follow_walk_target"], + "1": ["flee_nearest_hostile"], + "2": ["flee_attacker"], + "3": [ + { + "type": "wander", + "speedMultiplier": 0.5, + "horizontalRange": 3, + "verticalRange": 3, + "shouldWander": true + } + ] + } + } +] \ No newline at end of file diff --git a/common/src/main/resources/data/cobblemon/brain_presets/panics_when_hurt_by_anything.json b/common/src/main/resources/data/cobblemon/brain_presets/panics_when_hurt_by_anything.json new file mode 100644 index 0000000000..5c5b6343fd --- /dev/null +++ b/common/src/main/resources/data/cobblemon/brain_presets/panics_when_hurt_by_anything.json @@ -0,0 +1,14 @@ +[ + { + "type": "add_tasks_to_activity", + "activity": "idle", + "tasksByPriority": { + "0": [ + { + "type": "switch_to_panic_when_hurt", + "includePassiveDamage": true + } + ] + } + } +] \ No newline at end of file diff --git a/common/src/main/resources/data/cobblemon/dialogues/npc-example.json b/common/src/main/resources/data/cobblemon/dialogues/npc-example.json index 2830d8644e..9dd050689a 100644 --- a/common/src/main/resources/data/cobblemon/dialogues/npc-example.json +++ b/common/src/main/resources/data/cobblemon/dialogues/npc-example.json @@ -1,5 +1,5 @@ { - "initializationAction": "q.npc.is_in_battle_with(q.player) ? q.dialogue.close()", + "initializationAction": "(q.npc.is_in_battle_with(q.player) || !q.npc.is_doing_activity('minecraft:idle', 'cobblemon:battling')) ? q.dialogue.close()", "speakers": { "npc": { "name": { diff --git a/common/src/main/resources/data/cobblemon/npcs/ai_test.json b/common/src/main/resources/data/cobblemon/npcs/ai_test.json index d94ff10541..2385c37a4e 100644 --- a/common/src/main/resources/data/cobblemon/npcs/ai_test.json +++ b/common/src/main/resources/data/cobblemon/npcs/ai_test.json @@ -14,10 +14,11 @@ "cobblemon:looks", "cobblemon:floats", "cobblemon:pauses_in_battle", + "cobblemon:cancels_battle_when_hurt_by_anything", "cobblemon:uses_healing_machine", "cobblemon:chats", - "cobblemon:retaliates", - "cobblemon:fights_melee" + "cobblemon:panics_when_hurt_by_anything", + "cobblemon:panics" ] }, { diff --git a/fabric/src/main/kotlin/com/cobblemon/mod/fabric/permission/FabricPermissionValidator.kt b/fabric/src/main/kotlin/com/cobblemon/mod/fabric/permission/FabricPermissionValidator.kt index 469a62bd9d..282bf9b996 100644 --- a/fabric/src/main/kotlin/com/cobblemon/mod/fabric/permission/FabricPermissionValidator.kt +++ b/fabric/src/main/kotlin/com/cobblemon/mod/fabric/permission/FabricPermissionValidator.kt @@ -23,4 +23,8 @@ class FabricPermissionValidator : PermissionValidator { override fun hasPermission(player: ServerPlayer, permission: Permission) = Permissions.check(player, permission.literal, permission.level.numericalValue) override fun hasPermission(source: CommandSourceStack, permission: Permission) = Permissions.check(source, permission.literal, permission.level.numericalValue) + + override fun hasPermission(player: ServerPlayer, permission: String, level: Int) = Permissions.check(player, permission, level) + + override fun hasPermission(source: CommandSourceStack, permission: String, level: Int) = Permissions.check(source, permission, level) } \ No newline at end of file diff --git a/neoforge/src/main/kotlin/com/cobblemon/mod/neoforge/permission/ForgePermissionValidator.kt b/neoforge/src/main/kotlin/com/cobblemon/mod/neoforge/permission/ForgePermissionValidator.kt index c3a4942e35..70c97ee422 100644 --- a/neoforge/src/main/kotlin/com/cobblemon/mod/neoforge/permission/ForgePermissionValidator.kt +++ b/neoforge/src/main/kotlin/com/cobblemon/mod/neoforge/permission/ForgePermissionValidator.kt @@ -42,12 +42,23 @@ object ForgePermissionValidator : PermissionValidator { return PermissionAPI.getPermission(player, node) } + override fun hasPermission(player: ServerPlayer, permission: String, level: Int): Boolean { + val node = PermissionNode(permission.split(".").first(), permission.substringAfter("."), PermissionTypes.BOOLEAN, { player, _, _ -> player?.hasPermissions(level) == true }) + return PermissionAPI.getPermission(player, node) + } + override fun hasPermission(source: CommandSourceStack, permission: Permission): Boolean { val player = this.extractPlayerFromSource(source) ?: return source.hasPermission(permission.level.numericalValue) val node = this.findNode(permission) ?: return source.hasPermission(permission.level.numericalValue) return PermissionAPI.getPermission(player, node) } + override fun hasPermission(source: CommandSourceStack, permission: String, level: Int): Boolean { + val player = this.extractPlayerFromSource(source) ?: return source.hasPermission(4) + val node = PermissionNode(permission.split(".").first(), permission.substringAfter("."), PermissionTypes.BOOLEAN, { player, _, _ -> player?.hasPermissions(level) == true }) + return PermissionAPI.getPermission(player, node) + } + private fun createNodes() = CobblemonPermissions.all().map { permission -> // 3rd arg is default value if no implementation is present essentially val node = PermissionNode(permission.identifier, PermissionTypes.BOOLEAN, { player, _, _ -> player?.hasPermissions(permission.level.numericalValue) == true }) From dd516c153e111560d03f457f8d5827ee69b69bbe Mon Sep 17 00:00:00 2001 From: Conan Date: Tue, 19 Nov 2024 08:39:14 +0100 Subject: [PATCH 13/33] Added NPC properties: isInvulnerable, isLeashable, and allowProjectileHits. --- .../cobblemon/mod/common/api/npc/NPCClass.kt | 9 +++++ .../cobblemon/mod/common/api/npc/NPCPreset.kt | 6 ++++ .../mod/common/entity/npc/NPCEntity.kt | 35 +++++++++++++++++++ .../com/cobblemon/mod/common/util/DataKeys.kt | 3 ++ .../common/util/adapters/NPCClassAdapter.kt | 3 ++ 5 files changed, 56 insertions(+) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt index 7a2456d739..de18e7ec8f 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt @@ -51,6 +51,9 @@ class NPCClass { var battleTheme: ResourceLocation? = null var ai: MutableList = mutableListOf() var isMovable: Boolean = true + var isInvulnerable = true + var isLeashable = true + var allowProjectileHits = true // If you're adding stuff here, add it to NPCPreset and NPCClassAdapter too @@ -86,6 +89,9 @@ class NPCClass { } buffer.writeNullable(battleTheme) { _, v -> buffer.writeIdentifier(v) } buffer.writeBoolean(isMovable) + buffer.writeBoolean(isInvulnerable) + buffer.writeBoolean(isLeashable) + buffer.writeBoolean(allowProjectileHits) } fun decode(buffer: RegistryFriendlyByteBuf) { @@ -132,5 +138,8 @@ class NPCClass { } battleTheme = buffer.readNullable { buffer.readIdentifier() } isMovable = buffer.readBoolean() + isInvulnerable = buffer.readBoolean() + isLeashable = buffer.readBoolean() + allowProjectileHits = buffer.readBoolean() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCPreset.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCPreset.kt index a67d3c262d..0e0e41f79d 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCPreset.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCPreset.kt @@ -32,6 +32,9 @@ class NPCPreset { var autoHealParty: Boolean? = null var battleTheme: ResourceLocation? = null var isMovable: Boolean? = null + var isInvulnerable: Boolean? = null + var isLeashable: Boolean? = null + var allowProjectileHits: Boolean? = null fun applyTo(npcClass: NPCClass) { resourceIdentifier?.let { npcClass.resourceIdentifier = it } @@ -53,5 +56,8 @@ class NPCPreset { battleTheme?.let { npcClass.battleTheme = it } ai?.let { npcClass.ai.addAll(it) } isMovable?.let { npcClass.isMovable = it } + isInvulnerable?.let { npcClass.isInvulnerable = it } + isLeashable?.let { npcClass.isLeashable = it } + allowProjectileHits?.let { npcClass.allowProjectileHits = it } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt index ad3556d6ff..500ec844e7 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/entity/npc/NPCEntity.kt @@ -75,6 +75,7 @@ import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerPlayer import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionResult +import net.minecraft.world.damagesource.DamageSource import net.minecraft.world.entity.AgeableMob import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Pose @@ -119,6 +120,12 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P var isMovable: Boolean? = null + var isInvulnerable: Boolean? = null + + var isLeashable: Boolean? = null + + var allowProjectileHits: Boolean? = null + fun getPartyForChallenge(player: ServerPlayer): NPCPartyStore? { val party = this.party return if (party != null) { @@ -333,6 +340,18 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P if (isMovable != null) { nbt.putBoolean(DataKeys.NPC_IS_MOVABLE, isMovable) } + val isInvulnerable = isInvulnerable + if (isInvulnerable != null) { + nbt.putBoolean(DataKeys.NPC_IS_INVULNERABLE, isInvulnerable) + } + val isLeashable = isLeashable + if (isLeashable != null) { + nbt.putBoolean(DataKeys.NPC_IS_LEASHABLE, isLeashable) + } + val allowProjectileHits = allowProjectileHits + if (allowProjectileHits != null) { + nbt.putBoolean(DataKeys.NPC_ALLOW_PROJECTILE_HITS, allowProjectileHits) + } return nbt } @@ -367,6 +386,10 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P entityData.set(NPC_PLAYER_TEXTURE, NPCPlayerTexture(texture, model)) } this.isMovable = if (nbt.contains(DataKeys.NPC_IS_MOVABLE)) nbt.getBoolean(DataKeys.NPC_IS_MOVABLE) else null + this.isInvulnerable = if (nbt.contains(DataKeys.NPC_IS_INVULNERABLE)) nbt.getBoolean(DataKeys.NPC_IS_INVULNERABLE) else null + this.isLeashable = if (nbt.contains(DataKeys.NPC_IS_LEASHABLE)) nbt.getBoolean(DataKeys.NPC_IS_LEASHABLE) else null + this.allowProjectileHits = if (nbt.contains(DataKeys.NPC_ALLOW_PROJECTILE_HITS)) nbt.getBoolean(DataKeys.NPC_ALLOW_PROJECTILE_HITS) else null + updateAspects() } @@ -404,6 +427,18 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P return isMovable ?: npc.isMovable } + override fun isInvulnerableTo(source: DamageSource): Boolean { + return isInvulnerable ?: npc.isInvulnerable + } + + override fun canBeLeashed(): Boolean { + return isLeashable ?: npc.isLeashable + } + + override fun canBeHitByProjectile(): Boolean { + return allowProjectileHits ?: npc.allowProjectileHits + } + fun initialize(level: Int) { appliedAspects.clear() entityData.set(LEVEL, level) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt index 8c5834b0e0..c641b11f6b 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt @@ -213,6 +213,9 @@ object DataKeys { const val NPC_LEVEL = "Level" const val NPC_IS_MOVABLE = "IsMovable" const val NPC_SKILL = "Skill" + const val NPC_IS_INVULNERABLE = "IsInvulnerable" + const val NPC_IS_LEASHABLE = "IsLeashable" + const val NPC_ALLOW_PROJECTILE_HITS = "AllowProjectileHits" // PokemonProperties const val ELEMENTAL_TYPE = "ElementalType" diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassAdapter.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassAdapter.kt index 702b634443..124d6ff95f 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassAdapter.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassAdapter.kt @@ -73,6 +73,9 @@ object NPCClassAdapter : JsonDeserializer { obj.get("battleTheme")?.let { npcClass.battleTheme = it.asString.asIdentifierDefaultingNamespace() } obj.get("ai")?.let { npcClass.ai.addAll(it.asJsonArray.map { ctx.deserialize(it, BrainConfig::class.java) }.toMutableList()) } obj.get("isMovable")?.let { npcClass.isMovable = it.asBoolean } + obj.get("isInvulnerable")?.let { npcClass.isInvulnerable = it.asBoolean } + obj.get("isLeashable")?.let { npcClass.isLeashable = it.asBoolean } + obj.get("allowProjectileHits")?.let { npcClass.allowProjectileHits = it.asBoolean } return npcClass } From fb39ff8657696dd9143cf35b8f2d5c82732682fb Mon Sep 17 00:00:00 2001 From: Conan Date: Tue, 19 Nov 2024 08:41:10 +0100 Subject: [PATCH 14/33] Added NPC properties: isInvulnerable, isLeashable, and allowProjectileHits. --- .../main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt index de18e7ec8f..028a448476 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/NPCClass.kt @@ -51,7 +51,7 @@ class NPCClass { var battleTheme: ResourceLocation? = null var ai: MutableList = mutableListOf() var isMovable: Boolean = true - var isInvulnerable = true + var isInvulnerable = false var isLeashable = true var allowProjectileHits = true From ddbf6d7345e9e81f0a814572d9ffed03788cf625 Mon Sep 17 00:00:00 2001 From: Guilherme Pereira Date: Tue, 19 Nov 2024 09:51:58 -0300 Subject: [PATCH 15/33] add support for feeding display case items from underneath --- .../mod/common/block/entity/DisplayCaseBlockEntity.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/DisplayCaseBlockEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/DisplayCaseBlockEntity.kt index 7c3c33633c..f912bbe0bd 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/DisplayCaseBlockEntity.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/DisplayCaseBlockEntity.kt @@ -173,10 +173,6 @@ class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cob override fun getMaxStackSize() = 1 - override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, dir: Direction?): Boolean { - if(dir == Direction.DOWN) return false - return getStack().isEmpty - } - + override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, dir: Direction?): Boolean = (getStack().isEmpty) override fun canTakeItemThroughFace(slot: Int, stack: ItemStack, direction: Direction): Boolean = (direction == Direction.DOWN) } \ No newline at end of file From 6c9c5a93f1cc8ee2b528db0fd7420122f04af5f0 Mon Sep 17 00:00:00 2001 From: Guilherme Pereira Date: Tue, 19 Nov 2024 11:43:35 -0300 Subject: [PATCH 16/33] add tooltips for pomeg, tamato, grepa, kelpsy, hondew and qualot berries --- .../main/resources/assets/cobblemon/lang/en_us.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/main/resources/assets/cobblemon/lang/en_us.json b/common/src/main/resources/assets/cobblemon/lang/en_us.json index bc64d25336..0aede1aa0c 100644 --- a/common/src/main/resources/assets/cobblemon/lang/en_us.json +++ b/common/src/main/resources/assets/cobblemon/lang/en_us.json @@ -1101,11 +1101,11 @@ "item.cobblemon.ganlon_berry": "Ganlon Berry", "item.cobblemon.ganlon_berry.tooltip": "Raises holder's Defense by 1 stage when at 1/4 max HP or less", "item.cobblemon.grepa_berry": "Grepa Berry", - "item.cobblemon.grepa_berry.tooltip": "Cannot be eaten by the holder", + "item.cobblemon.grepa_berry.tooltip": "Lowers the pokémon's Defense EVs by 10, while raising friendship", "item.cobblemon.haban_berry": "Haban Berry", "item.cobblemon.haban_berry.tooltip": "Halves damage taken from a supereffective Dragon-type attack", "item.cobblemon.hondew_berry": "Hondew Berry", - "item.cobblemon.hondew_berry.tooltip": "Cannot be eaten by the holder", + "item.cobblemon.hondew_berry.tooltip": "Lowers the pokémon's Special Defense EVs by 10, while raising friendship", "item.cobblemon.hopo_berry": "Hopo Berry", "item.cobblemon.hopo_berry.tooltip": "Cannot be eaten by the holder", "item.cobblemon.iapapa_berry": "Iapapa Berry", @@ -1119,7 +1119,7 @@ "item.cobblemon.kee_berry": "Kee Berry", "item.cobblemon.kee_berry.tooltip": "Raises holder's Defense by 1 stage after it is hit by a physical attack", "item.cobblemon.kelpsy_berry": "Kelpsy Berry", - "item.cobblemon.kelpsy_berry.tooltip": "Cannot be eaten by the holder", + "item.cobblemon.kelpsy_berry.tooltip": "Lowers the pokémon's Special Attack EVs by 10, while raising friendship", "item.cobblemon.lansat_berry": "Lansat Berry", "item.cobblemon.lansat_berry.tooltip": "Holder gains the Focus Energy effect when at 1/4 max HP or less", "item.cobblemon.leppa_berry": "Leppa Berry", @@ -1161,9 +1161,9 @@ "item.cobblemon.pinap_berry": "Pinap Berry", "item.cobblemon.pinap_berry.tooltip": "Cannot be eaten by the holder", "item.cobblemon.pomeg_berry": "Pomeg Berry", - "item.cobblemon.pomeg_berry.tooltip": "Cannot be eaten by the holder", + "item.cobblemon.pomeg_berry.tooltip": "Lowers the pokémon's HP EVs by 10, while raising friendship", "item.cobblemon.qualot_berry": "Qualot Berry", - "item.cobblemon.qualot_berry.tooltip": "Cannot be eaten by the holder", + "item.cobblemon.qualot_berry.tooltip": "Lowers the pokémon's Speed EVs by 10, while raising friendship", "item.cobblemon.rabuta_berry": "Rabuta Berry", "item.cobblemon.rabuta_berry.tooltip": "Cannot be eaten by the holder", "item.cobblemon.rawst_berry": "Rawst Berry", @@ -1187,7 +1187,7 @@ "item.cobblemon.starf_berry": "Starf Berry", "item.cobblemon.starf_berry.tooltip": "Raises a random stat by 2 when at 1/4 max HP or less", "item.cobblemon.tamato_berry": "Tamato Berry", - "item.cobblemon.tamato_berry.tooltip": "Cannot be eaten by the holder", + "item.cobblemon.tamato_berry.tooltip": "Lowers the pokémon's Attack EVs by 10, while raising friendship", "item.cobblemon.tanga_berry": "Tanga Berry", "item.cobblemon.tanga_berry.tooltip": "Halves damage taken from a supereffective Bug-type attack", "item.cobblemon.wacan_berry": "Wacan Berry", From b881c966306050b483c4aa29cd7c18ca43c7ffee Mon Sep 17 00:00:00 2001 From: Guilherme Pereira Date: Tue, 19 Nov 2024 20:09:25 -0300 Subject: [PATCH 17/33] add changelog for feeding display case items from underneath fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1a3f2247a..e58803d924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -405,6 +405,7 @@ - Fixed aspect tracking for Advancements. - Fixed illusion not copying aspects or caught ball. - Prevent summary stats tab from making sounds when clicking on an already open tab. +- Fixed display case not being able to be fed items from underneath. ### Developer - `SpawnCause` is now an implementation of `SpawningInfluence`. From 5ff5de8d14efff6c2ea266120546fb24cecd8b2b Mon Sep 17 00:00:00 2001 From: Guilherme Pereira Date: Wed, 20 Nov 2024 07:58:41 -0300 Subject: [PATCH 18/33] re-add tooltip 'Cannot be eaten by holder' on another line for pomeg, tamato, grepa, kelpsy, hondew and qualot berries --- .../resources/assets/cobblemon/lang/en_us.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/common/src/main/resources/assets/cobblemon/lang/en_us.json b/common/src/main/resources/assets/cobblemon/lang/en_us.json index 0aede1aa0c..499cca8b4f 100644 --- a/common/src/main/resources/assets/cobblemon/lang/en_us.json +++ b/common/src/main/resources/assets/cobblemon/lang/en_us.json @@ -1101,11 +1101,13 @@ "item.cobblemon.ganlon_berry": "Ganlon Berry", "item.cobblemon.ganlon_berry.tooltip": "Raises holder's Defense by 1 stage when at 1/4 max HP or less", "item.cobblemon.grepa_berry": "Grepa Berry", - "item.cobblemon.grepa_berry.tooltip": "Lowers the pokémon's Defense EVs by 10, while raising friendship", + "item.cobblemon.grepa_berry.tooltip_1": "Lowers the pokémon's Defense EVs by 10, while raising friendship", + "item.cobblemon.grepa_berry.tooltip_2": "Cannot be eaten by the holder", "item.cobblemon.haban_berry": "Haban Berry", "item.cobblemon.haban_berry.tooltip": "Halves damage taken from a supereffective Dragon-type attack", "item.cobblemon.hondew_berry": "Hondew Berry", - "item.cobblemon.hondew_berry.tooltip": "Lowers the pokémon's Special Defense EVs by 10, while raising friendship", + "item.cobblemon.hondew_berry.tooltip_1": "Lowers the pokémon's Special Defense EVs by 10, while raising friendship", + "item.cobblemon.hondew_berry.tooltip_2": "Cannot be eaten by the holder", "item.cobblemon.hopo_berry": "Hopo Berry", "item.cobblemon.hopo_berry.tooltip": "Cannot be eaten by the holder", "item.cobblemon.iapapa_berry": "Iapapa Berry", @@ -1119,7 +1121,8 @@ "item.cobblemon.kee_berry": "Kee Berry", "item.cobblemon.kee_berry.tooltip": "Raises holder's Defense by 1 stage after it is hit by a physical attack", "item.cobblemon.kelpsy_berry": "Kelpsy Berry", - "item.cobblemon.kelpsy_berry.tooltip": "Lowers the pokémon's Special Attack EVs by 10, while raising friendship", + "item.cobblemon.kelpsy_berry.tooltip_1": "Lowers the pokémon's Special Attack EVs by 10, while raising friendship", + "item.cobblemon.kelpsy_berry.tooltip_2": "Cannot be eaten by the holder", "item.cobblemon.lansat_berry": "Lansat Berry", "item.cobblemon.lansat_berry.tooltip": "Holder gains the Focus Energy effect when at 1/4 max HP or less", "item.cobblemon.leppa_berry": "Leppa Berry", @@ -1161,9 +1164,11 @@ "item.cobblemon.pinap_berry": "Pinap Berry", "item.cobblemon.pinap_berry.tooltip": "Cannot be eaten by the holder", "item.cobblemon.pomeg_berry": "Pomeg Berry", - "item.cobblemon.pomeg_berry.tooltip": "Lowers the pokémon's HP EVs by 10, while raising friendship", + "item.cobblemon.pomeg_berry.tooltip_1": "Lowers the pokémon's HP EVs by 10, while raising friendship", + "item.cobblemon.pomeg_berry.tooltip_2": "Cannot be eaten by the holder", "item.cobblemon.qualot_berry": "Qualot Berry", - "item.cobblemon.qualot_berry.tooltip": "Lowers the pokémon's Speed EVs by 10, while raising friendship", + "item.cobblemon.qualot_berry.tooltip_1": "Lowers the pokémon's Speed EVs by 10, while raising friendship", + "item.cobblemon.qualot_berry.tooltip_2": "Cannot be eaten by the holder", "item.cobblemon.rabuta_berry": "Rabuta Berry", "item.cobblemon.rabuta_berry.tooltip": "Cannot be eaten by the holder", "item.cobblemon.rawst_berry": "Rawst Berry", @@ -1187,7 +1192,8 @@ "item.cobblemon.starf_berry": "Starf Berry", "item.cobblemon.starf_berry.tooltip": "Raises a random stat by 2 when at 1/4 max HP or less", "item.cobblemon.tamato_berry": "Tamato Berry", - "item.cobblemon.tamato_berry.tooltip": "Lowers the pokémon's Attack EVs by 10, while raising friendship", + "item.cobblemon.tamato_berry.tooltip_1": "Lowers the pokémon's Attack EVs by 10, while raising friendship", + "item.cobblemon.tamato_berry.tooltip_2": "Cannot be eaten by the holder", "item.cobblemon.tanga_berry": "Tanga Berry", "item.cobblemon.tanga_berry.tooltip": "Halves damage taken from a supereffective Bug-type attack", "item.cobblemon.wacan_berry": "Wacan Berry", From 1c569ac5cf53312579ab610871f5a9fc98bb4a8e Mon Sep 17 00:00:00 2001 From: Conan Date: Wed, 20 Nov 2024 17:01:14 +0100 Subject: [PATCH 19/33] Added property descriptions --- .../com/cobblemon/mod/common/api/npc/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md index f01cd50c65..1abfea78b8 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md @@ -349,6 +349,21 @@ Whether or not the NPC's party will be healed between battles. If the NPC has a do nothing. If the NPC has a static party and this option is false, their Pokémon will not be healed and the NPC will have to use healing machines to heal them if they have that enabled in their AI. +### isMovable +Controls whether the NPC can be moved by players or other entities. If disabled, the NPC will remain +stationary. + +### isInvincible +Controls whether the NPC is immune to all damage sources. + +### isLeashable +Controls whether players can attach a lead to a NPC. If disabled, players will be unable to leash +the NPC and move them using leads. + +### allowProjectileHits +Controls whether the NPC can be hit by projectiles. Disabling this option prevents unwanted +interactions, such as players moving the NPC with fishing rods or other projectiles. + ### ai The AI property is an array of brain configurations. This is used to configure the behaviours of the NPC. See the documentation for brain configurations at [AI Configuration](../ai/config/README.md). \ No newline at end of file From b0e0a06e9a85a38a12be1ebf0f4261683d45b21e Mon Sep 17 00:00:00 2001 From: Segfault Guy Date: Wed, 20 Nov 2024 21:00:16 -0500 Subject: [PATCH 20/33] Fix battle music race condition on forceBattle. --- .../api/battles/model/actor/BattleActor.kt | 1 + .../mod/common/battles/BattleBuilder.kt | 24 ++++++++++--------- .../common/battles/actor/PlayerBattleActor.kt | 6 ++--- .../instructions/InitializeInstruction.kt | 6 ++--- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/actor/BattleActor.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/actor/BattleActor.kt index 2b0c33067a..dc18137570 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/actor/BattleActor.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/actor/BattleActor.kt @@ -40,6 +40,7 @@ abstract class BattleActor( lateinit var showdownId: String lateinit var battle: PokemonBattle + fun isInitialized() = this::battle.isInitialized && this::showdownId.isInitialized val activePokemon = mutableListOf() var canDynamax = false diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt b/common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt index 483b6470a0..edd7242c56 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt @@ -105,6 +105,9 @@ object BattleBuilder { } } + player1Actor.battleTheme = player2.getBattleTheme() + player2Actor.battleTheme = player1.getBattleTheme() + return if (errors.isEmpty) { var result: BattleStartResult = errors BattleRegistry.startBattle( @@ -112,8 +115,6 @@ object BattleBuilder { side1 = BattleSide(player1Actor), side2 = BattleSide(player2Actor) ).ifSuccessful { - player1Actor.battleTheme = player2.getBattleTheme() - player2Actor.battleTheme = player1.getBattleTheme() it.battlePartyStores.addAll(battlePartyStores) result = SuccessfulBattleStart(it) } @@ -197,19 +198,18 @@ object BattleBuilder { playerActors.swap(2,3) } + // TODO: less hard coding + playerActors[0].battleTheme = players[2].getBattleTheme() + playerActors[1].battleTheme = players[2].getBattleTheme() + playerActors[2].battleTheme = players[0].getBattleTheme() + playerActors[3].battleTheme = players[0].getBattleTheme() + return if (errors.isEmpty) { BattleRegistry.startBattle( battleFormat = battleFormat, side1 = BattleSide(playerActors[0], playerActors[1]), side2 = BattleSide(playerActors[2], playerActors[3]) ).ifSuccessful { - // TODO: less hard coding - playerActors[0].battleTheme = players[2].getBattleTheme() - playerActors[1].battleTheme = players[2].getBattleTheme() - - playerActors[2].battleTheme = players[0].getBattleTheme() - playerActors[3].battleTheme = players[0].getBattleTheme() - it.battlePartyStores.addAll(battlePartyStores) } errors @@ -272,6 +272,8 @@ object BattleBuilder { errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor) } + playerActor.battleTheme = pokemonEntity.getBattleTheme() + return if (errors.isEmpty) { var result: BattleStartResult = errors BattleRegistry.startBattle( @@ -282,7 +284,6 @@ object BattleBuilder { if (!cloneParties) { pokemonEntity.battleId = it.battleId } - playerActor.battleTheme = pokemonEntity.getBattleTheme() result = SuccessfulBattleStart(it) } result @@ -347,6 +348,8 @@ object BattleBuilder { ) } + playerActor.battleTheme = npcEntity.getBattleTheme() + return if (errors.isEmpty) { var result: BattleStartResult = errors BattleRegistry.startBattle( @@ -354,7 +357,6 @@ object BattleBuilder { side1 = BattleSide(playerActor), side2 = BattleSide(npcActor) ).ifSuccessful { battle -> - playerActor.battleTheme = npcEntity.getBattleTheme() npcEntity.entityData.update(NPCEntity.BATTLE_IDS) { it + battle.battleId } result = SuccessfulBattleStart(battle) } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/battles/actor/PlayerBattleActor.kt b/common/src/main/kotlin/com/cobblemon/mod/common/battles/actor/PlayerBattleActor.kt index 0efcc1dd00..98f8ef9316 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/battles/actor/PlayerBattleActor.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/battles/actor/PlayerBattleActor.kt @@ -24,11 +24,12 @@ import java.util.UUID import net.minecraft.network.chat.MutableComponent import net.minecraft.server.level.ServerPlayer import net.minecraft.sounds.SoundEvent +import net.minecraft.sounds.SoundEvents import net.minecraft.world.phys.Vec3 class PlayerBattleActor( uuid: UUID, - pokemonList: List + pokemonList: List, ) : BattleActor(uuid, pokemonList.toMutableList()), EntityBackedBattleActor { override val initialPos: Vec3? @@ -41,8 +42,7 @@ class PlayerBattleActor( /** The [SoundEvent] to play to the player during a battle. Will start playing as soon as the battle starts. */ var battleTheme: SoundEvent? = null set(value) { - if (field != value && this.battle.started) - this.sendUpdate(BattleMusicPacket(value)) + if (this.isInitialized() && this.battle.started) this.sendUpdate(BattleMusicPacket(value)) field = value } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt b/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt index f09df9d766..dd62ff0c14 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/InitializeInstruction.kt @@ -8,6 +8,7 @@ package com.cobblemon.mod.common.battles.interpreter.instructions +import com.cobblemon.mod.common.Cobblemon import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage import com.cobblemon.mod.common.api.battles.model.PokemonBattle import com.cobblemon.mod.common.api.scheduling.afterOnServer @@ -52,10 +53,7 @@ class InitializeInstruction(val instructionSet: InstructionSet, val message: Bat battle.actors.filterIsInstance().forEach { actor -> val initializePacket = BattleInitializePacket(battle, actor.getSide()) actor.sendUpdate(initializePacket) - // Delay accounts for some cases when the packet is sent before the battleTheme is known, this happens with forceBattle - afterOnServer(0.1F) { - actor.sendUpdate(BattleMusicPacket(actor.battleTheme)) - } + actor.sendUpdate(BattleMusicPacket(actor.battleTheme)) } battle.actors.forEach { actor -> From fd0e741a2585b7e36be2e742d9d6ef057e659b68 Mon Sep 17 00:00:00 2001 From: Test Date: Wed, 20 Nov 2024 22:39:50 -0500 Subject: [PATCH 21/33] update ui, fix renders in dex from clipping into background --- .../subscreen/BattleSwitchPokemonSelection.kt | 4 ++-- .../battle/subscreen/BattleTargetSelection.kt | 5 +++-- .../gui/pokedex/widgets/PokemonInfoWidget.kt | 4 ++-- .../gui/summary/widgets/screens/SummaryTab.kt | 4 ++-- .../gui/pokedex/pokedex_screen_scan.png | Bin 3549 -> 2725 bytes .../gui/pokedex/scan/overlay_notch.png | Bin 1057 -> 204 bytes .../gui/pokedex/scan/overlay_scanlines.png | Bin 935 -> 117 bytes .../gui/summary/summary_tab_icon_info.png | Bin 189 -> 223 bytes .../gui/summary/summary_tab_icon_moves.png | Bin 150 -> 186 bytes .../gui/summary/summary_tab_icon_stats.png | Bin 152 -> 208 bytes 10 files changed, 9 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleSwitchPokemonSelection.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleSwitchPokemonSelection.kt index 957d91d846..efff9394d7 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleSwitchPokemonSelection.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleSwitchPokemonSelection.kt @@ -47,8 +47,8 @@ class BattleSwitchPokemonSelection( battleGUI, request, x = 0, - y = if (Minecraft.getInstance().window.guiScaledHeight > 630) Minecraft.getInstance().window.guiScaledHeight / 2 - 148 / 2 - else Minecraft.getInstance().window.guiScaledHeight - 226, + y = if (Minecraft.getInstance().window.guiScaledHeight > 304) (Minecraft.getInstance().window.guiScaledHeight / 2) - (BACKGROUND_HEIGHT / 2) + else Minecraft.getInstance().window.guiScaledHeight - (BACKGROUND_HEIGHT + 78), width = Minecraft.getInstance().window.guiScaledWidth, height = Minecraft.getInstance().window.guiScaledHeight, battleLang("switch_pokemon") diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleTargetSelection.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleTargetSelection.kt index 6cfb863877..c73d349fc9 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleTargetSelection.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleTargetSelection.kt @@ -22,6 +22,7 @@ import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon import com.cobblemon.mod.common.client.battle.SingleActionRequest import com.cobblemon.mod.common.client.gui.battle.BattleGUI import com.cobblemon.mod.common.client.gui.battle.BattleOverlay +import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleSwitchPokemonSelection.Companion import com.cobblemon.mod.common.client.gui.drawProfilePokemon import com.cobblemon.mod.common.client.render.drawScaledText import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState @@ -47,8 +48,8 @@ class BattleTargetSelection( battleGUI = battleGUI, request = request, x = 0, - y = if (Minecraft.getInstance().window.guiScaledHeight > 630) Minecraft.getInstance().window.guiScaledHeight / 2 - 148 / 2 - else Minecraft.getInstance().window.guiScaledHeight - 226, + y = if (Minecraft.getInstance().window.guiScaledHeight > 304) (Minecraft.getInstance().window.guiScaledHeight / 2) - (BattleSwitchPokemonSelection.BACKGROUND_HEIGHT / 2) + else Minecraft.getInstance().window.guiScaledHeight - (BattleSwitchPokemonSelection.BACKGROUND_HEIGHT + 78), width = 100, height = 100, battleLang("ui.select_move") diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt index f368addf74..0db3dfbd2b 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt @@ -368,7 +368,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm) matrices.translate( pX.toDouble() + (POKEMON_PORTRAIT_WIDTH.toDouble() + 2)/2, pY.toDouble() + portraitStartY - 12, - 0.0 + 1000.0 // Prevent model from clipping into background ) matrices.scale(scaleAmount, scaleAmount, scaleAmount) val rotationVector = Vector3f(13F, rotationY, 0F) @@ -398,7 +398,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm) // Ensure elements are not hidden behind Pokémon render matrices.pushPose() - matrices.translate(0.0, 0.0, 1000.0) + matrices.translate(0.0, 0.0, 2000.0) if (isSelectedPokemonOwned()) { val primaryType = type[0] diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/SummaryTab.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/SummaryTab.kt index 1f115c5cdc..775ee0edfe 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/SummaryTab.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/SummaryTab.kt @@ -46,8 +46,8 @@ class SummaryTab( texture = icon, x = (x + 21) / 0.5F, y = (y + 3.5) / 0.5F, - width = 16, - height = 16, + width = 17, + height = 17, scale = 0.5F ) } diff --git a/common/src/main/resources/assets/cobblemon/textures/gui/pokedex/pokedex_screen_scan.png b/common/src/main/resources/assets/cobblemon/textures/gui/pokedex/pokedex_screen_scan.png index aa3599cfdaa1c0916307c0aed665dbef3e63672b..422c4de61435a1d17881fb3461bf3fc2a3654569 100644 GIT binary patch literal 2725 zcmeHJ`&SYO7DkI~C9^$K*yQ-ybq{TcPUEY@!AE+GGbvm7CP5QJ2hl`95tSbHP}fIV zPKqe&lvaoj2z;ZXX`&gHDIh9aCN>Htnkb^UoU{MMocZP6@0@#oy65}8`{n&BD8S70 zm?;1NFhiX4y9fXnMHsMX|2{*-bkv?Qv~Sb=!_zJ$UrkGoA;tlGu*p~AY!QSQT-?RD z7%U}480QH9m zbd=0RZh7|SIDgrBO~utH{4Zbu-xXgTGS2d`(@h4X^S7$2Cx6QTzD%+BuY{^orZO;= zHsgH?y`8e_k^if;e`KUg13ff@d!WQwqF3t>$|-|4JeV7l31EI4~_a;@v;JkG)`%$F$G0Hr(FE1oh)5B zZ&eIi=EF4k)k3mr?poK}l?7fjp|b`gD@6OaON00Qen(kH=L$#rNRz}KXtdaLN(}~8 zk@n)VM^H)KoW`CpZjnq-a-{9|;Jt)BZ=T|YQl{BnlGn#jq0}r^EJ21KzyE8a3AC6U znFgA%YG(C?w$;G~iad%21YCrjEJry}+?1e_&>d@W&D8j@!ax!~4Rb`erZKF(P4ayjmD zr~H8IJ`h9AXSjm9p^2S0%XiFV0=xPphj%VAnv!RgyhbP`VIZh4v0_u18eHJHxL=|r zKUXgZn1NC9!!UwFrrZyoAILg!biE4U*3ZXRlBTou87z}TR1mwP3txiZYwNg6s=Q?OAplAVL3hr^@+m!=u4)3Lv9(OTnOG+ zV4;)z;gPeB)(#wL@*r}z&zVoZ^hL0?JutE$;+=m!`cucGAQ#V&M~#J<2ahj#MkD3h zJ2uHPyPBVD9&98>G|ptxh#L!QVN+Mmw3pwNHkiKmAF`ftXDXA2zyUGT!)@k$q!$OK z2U4^wb%hOuzfaw_Imr73P08 z@TKLeMjC{Cz8-&96l;jVyvgdo2DM?@x)tv2 zT?{2QY)%I}w2|7&H1gton{WUmPz(v7<2GPOj+X0{z510am9yO2z&0L3Xj*M$IP0ZY zX#92pCMD(E{=_U(di2;Hjm8`(O`h{f_k)05hJL|z-We{0-Hm!Tvlpb)xpbDPufzUR z2CQYJb8cM=0rZ|aX1>{-#_>*D51O8V=b`(fS^aUkTq+my>7+a zpsyB#zSy@&G6X-o#Yic$?@Uz`ino2Ty|Vbz|5pxwm@kNq`j$^8a|j7&)&5?10!UgY znii|D^km{hKS>bi%y2k_=`Lfq5gVZ<9312?U3F+04S}45k)zyN9Moc$Rw^HaH*y4SUzJQU3_V*NIop)PdS(7IjrM*M4JeKjH=kxRDv&=L zQNyWST#pPxRDn$ZN39wf^Vs~OuRO)wbQKyyVoC}0czPK@HUW%qTd~9tVqi-WwIalX5)f#My;1i4D3}Le zxYkW3jJ%*ah=r5#?dNo!GSRjDjYnOW9jNL>2{iLWZp7d3x$9k<=B&s+ux=-CPlO+7 z^aXF$p5-^ekB8`lNtJ*aP)y84vAQ{vlfZ1|xO$VoG_#fzQmU9q^;BE}(pFTzY5Wo_e$)#XIP kzaBKMVghC*yTGG>1gJ`msMcRKKgh4qH~QxP02}6NIRF3v literal 3549 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 diff --git a/common/src/main/resources/assets/cobblemon/textures/gui/pokedex/scan/overlay_notch.png b/common/src/main/resources/assets/cobblemon/textures/gui/pokedex/scan/overlay_notch.png index 1fd4470f43e5f5cfa88efa09e4eb0ca583e4dab0..62a15e80daa667ec286d03600683119f4f6f6bf9 100644 GIT binary patch delta 140 zcmV;70CWGL2+RSHNPhrsNkl<6lzyPFE3Dgds&QkyY002ovP6b4+LSTX%sW;33 literal 1057 zcmaJ=OK1~87~Ud?v<9RqR6(3>MXi|ZBah9ljhgHxZJ;S7O)-jKH@nkjZFgt8J2l&S zuqY~`2;Mw+5X6&@QlTdm!Cv%G!Gj{=q2d#i3L=W&!PzuvJ=8AD&dksE{r~qr=0a~z zXP|LwBSlexY?qQJ`xCO(uUkw0yIQ|@kZlW27jYjN#1++nR8mJJ2(qR+1oKeU$48$+ z0$Q`vC=_uq*DYzt45>aQ~issmyEyJ?W zHQ~U%iJpQsF|3I?ePBQ6@Fe2EgjfZhIbykzm!PYDC35!73=OIfJe;5xMHO?sKt>J( z(NHX?u`vM%Vu*_hLPXdLc$SMYY?ukNK|U-+SSiAT+DDUUjy@>ml}s%bxh3c_#RV<)AaMIqFtPa%Q99(yM=KZGI{8tF-If)7;LMPN$pk-L z_Mmw-PPN}Vc!oL^NL{4h$v5NIlatnw{GRPo4}SfgPdCjzf62~m{xWmCp?s$}v!mh0 cOy`M4mb$cqzC8M3-DCgJX45^&-PFM8zrQ(FwZAL8eAGIBT^;Wyw`IKZHDo!K<}jcNr05O})!xvXuHBYP#+qc-d zwR4C4pMQOU*tSpY9z7)kI`vY_)dSMUpc#2X+`(Qjd;J;LIBxqe?DlBSeTIAz30{E_ z@+e_!j;kH#iRX_o1${gWV}t*B`GE&vVDM*(E4qn^N8!ml#i#RD*PoAlJ>ZWYgIbQ5 zg9uX(tBn1Cyi}C_=U5i&Yhq$Fpc@FhCv4S;k^FgC9|vAPA?^sW2@IBppIY*L5h$P?k#!QOag9 z_3~1j9jrW9IP=pmp&^Mu;nC}p2{m}8^eTlYaox4!I9nEql?>)y0wqC&QB>r$Kxedr zuVrjRXWdzXVFzbqlKQM4gM$^B)$Zm%1(2yh7V#$$j%m{}cxDlTFhEMVtT~$IR4hf7 zB-_z7+p?;T+E8TKu|;XcZN?e(Vjr)#;WgL3k&Da}dz7SILe5v+YmW#e*@z^-G#A|h z56{9lAk*woQQoCmoQ7|3;G`r1i~68&g9c5kR~5;wR~njH=+|VHfm&~vbyb%|rNS?{ z!G_$I+{S;okcojsbN^}f%ETt9IIc&RUDm^oV>a`HeOi=&; delta 158 zcmcc5xR+6}Gr-TCmrII^fq{Y7)59eQNDF{42OE%-|NK93qM}Agyr+v}NX4zBKmY&R zAHKi8{=e}A#u+nbN=p9!^V4}+nnZ$=8Lx+6>%y!+UIpeKF<>~x zvWfSH0qZj1k_rbH%&&?Duzp<83AB>I)78&q Iol`;+08-&MjsO4v diff --git a/common/src/main/resources/assets/cobblemon/textures/gui/summary/summary_tab_icon_moves.png b/common/src/main/resources/assets/cobblemon/textures/gui/summary/summary_tab_icon_moves.png index 1625cfa83833258ad8487917f4fcabb6030741bd..a5fd4a1a9b41750a385c3687531f267d0db2a3c5 100644 GIT binary patch delta 155 zcmbQnxQkJ-Gr-TCmrII^fq{Y7)59eQNDG262OE&I&UODaQBk8X#?!?yq~cc6AAS~| z6%B%3i@P5)_4q`v{9i2DCN3c%@hAO|>OlvlghNRT%$@fV6BsR}?>^%e*E5*(L@8iL zP}A41Uk~a@{=zt3m}KnvkVznc!O5~Ke?4zr4A4FXPgg&ebxsLQ E08f`SF8}}l delta 119 zcmdnRIE_)UGr-TCmrII^fq{Y7)59eQNDF{42OE%-|NK93qN0Ycg{O;SNX4zBKm05_ zD;fm77I!DJ`a~T0w^-6;6Qc@a4%>t$tYS8POZ&wmHdvIJh`A*<{@chPkihWi6u%nt T%b%%0vlu*G{an^LB{Ts5YK9}@ diff --git a/common/src/main/resources/assets/cobblemon/textures/gui/summary/summary_tab_icon_stats.png b/common/src/main/resources/assets/cobblemon/textures/gui/summary/summary_tab_icon_stats.png index 07b42403d4393aa2f2c7eb0212a867b645757885..7b05fa52d1a3f14338e403baa4805682e6daa3fb 100644 GIT binary patch delta 178 zcmbQic!5!|Gr-TCmrII^fq{Y7)59eQNDG262OE&I&UODaQBkA5$kW9!q~g|*DS=!J ziX5!98`(eoTmHdqnoiRO`7UYG_`ok04mRn%V7>C>Bdc+=gp14JIL!)`=ev}bu-rMR zWn0xLy5P-`PQOjl?%cdz_GQInlf{o(Htm#@WxxLHNwNCe$>)+?Dvs*F=gYN@_y#6i;VW2A*I6PhbT-G@yGywpw14OC- delta 121 zcmcb>ID=8KGr-TCmrII^fq{Y7)59eQNDF{42OE%-|NK93qN0Ysm8XkiNX4zBKm05_ z0tZewuq7N+`X=A$dm%uLjf?RpTf%21Wz)kA0tqsWi3corE;EM4eyDdi#Gr71K`)j= U_iyH>QlM!Jp00i_>zopr0IcaGLjV8( From 77490b5084f3caefa9f1bbaa1ba8bf0d6c07996c Mon Sep 17 00:00:00 2001 From: amo Date: Thu, 21 Nov 2024 14:15:44 +0000 Subject: [PATCH 22/33] =?UTF-8?q?=F0=9F=8D=8B=E2=80=8D=F0=9F=9F=A9=20Chore?= =?UTF-8?q?:=20Oops!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cobblemon/mod/common/api/events/CobblemonEvents.kt | 2 +- .../common/battles/interpreter/instructions/MegaInstruction.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt index 0aa09d0986..44bf291e53 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt @@ -100,7 +100,7 @@ object CobblemonEvents { // instructions @JvmField - val MEGA_EVOLUTON = EventObservable() + val MEGA_EVOLUTION = EventObservable() @JvmField val TERASTALLIZATION = EventObservable() @JvmField diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/MegaInstruction.kt b/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/MegaInstruction.kt index c1a92f4488..3157a3316b 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/MegaInstruction.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/MegaInstruction.kt @@ -30,7 +30,7 @@ class MegaInstruction(val message: BattleMessage): InterpreterInstruction { battle.dispatchWaiting { val pokemonName = battlePokemon.getName() battle.broadcastChatMessage(battleLang("mega", pokemonName).yellow()) - CobblemonEvents.MEGA_EVOLUTON.post(MegaEvolutionEvent(battle, battlePokemon)) + CobblemonEvents.MEGA_EVOLUTION.post(MegaEvolutionEvent(battle, battlePokemon)) battle.minorBattleActions[battlePokemon.uuid] = message } } From 6a076d36f9f9f0eb5290ee1a40bb2e71a01ea787 Mon Sep 17 00:00:00 2001 From: Conan Date: Thu, 21 Nov 2024 20:28:54 +0100 Subject: [PATCH 23/33] "isInvincible" != "isInvulnerable" --- .../src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md index 1abfea78b8..ceb6bdf247 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/npc/README.md @@ -353,7 +353,7 @@ NPC will have to use healing machines to heal them if they have that enabled in Controls whether the NPC can be moved by players or other entities. If disabled, the NPC will remain stationary. -### isInvincible +### isInvulnerable Controls whether the NPC is immune to all damage sources. ### isLeashable From 67ae5ff1557ac49e673c8296e86838899e57a831 Mon Sep 17 00:00:00 2001 From: Conan Date: Fri, 22 Nov 2024 00:06:07 +0100 Subject: [PATCH 24/33] Added a command to delete NPCs --- .../cobblemon/mod/common/CobblemonCommands.kt | 1 + .../api/permission/CobblemonPermissions.kt | 1 + .../mod/common/command/NPCDeleteCommand.kt | 42 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/command/NPCDeleteCommand.kt diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt index 7799a58e6f..f649dc0a23 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt @@ -52,6 +52,7 @@ object CobblemonCommands { BedrockParticleCommand.register(dispatcher) OpenDialogueCommand.register(dispatcher) NPCEditCommand.register(dispatcher) + NPCDeleteCommand.register(dispatcher) AbandonMultiTeam.register(dispatcher) FreezePokemonCommand.register(dispatcher) ApplyPlayerTextureCommand.register(dispatcher) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt index 76a2b4a364..10dfe6f637 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt @@ -76,6 +76,7 @@ object CobblemonPermissions { val POKEDEX = this.create("${COMMAND_PREFIX}pokedex", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS) val NPC_EDIT = this.create("${COMMAND_PREFIX}npcedit", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS) + val NPC_DELETE = this.create("${COMMAND_PREFIX}npcdelete", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS) val FREEZE_POKEMON = this.create("${COMMAND_PREFIX}freezepokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS) val APPLY_PLAYER_TEXTURE = this.create("${COMMAND_PREFIX}applyplayertexture", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/command/NPCDeleteCommand.kt b/common/src/main/kotlin/com/cobblemon/mod/common/command/NPCDeleteCommand.kt new file mode 100644 index 0000000000..2a6483895e --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/command/NPCDeleteCommand.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.command + +import com.cobblemon.mod.common.api.permission.CobblemonPermissions +import com.cobblemon.mod.common.api.text.red +import com.cobblemon.mod.common.entity.npc.NPCEntity +import com.cobblemon.mod.common.util.commandLang +import com.cobblemon.mod.common.util.requiresWithPermission +import com.cobblemon.mod.common.util.traceFirstEntityCollision +import com.mojang.brigadier.Command +import com.mojang.brigadier.CommandDispatcher +import com.mojang.brigadier.context.CommandContext +import net.minecraft.commands.CommandSourceStack +import net.minecraft.commands.Commands +import net.minecraft.server.level.ServerPlayer + +object NPCDeleteCommand { + fun register(dispatcher : CommandDispatcher) { + dispatcher.register( + Commands.literal("npcdelete") + .requiresWithPermission(CobblemonPermissions.NPC_DELETE) { it.player != null } + .executes { execute(it, it.source.playerOrException) }) + } + + private fun execute(context: CommandContext, player: ServerPlayer) : Int { + val targetEntity = player.traceFirstEntityCollision(entityClass = NPCEntity::class.java) + if (targetEntity == null) { + player.sendSystemMessage(commandLang("npcedit.non_npc").red()) + return 0 + } + + targetEntity.discard() + return Command.SINGLE_SUCCESS + } +} \ No newline at end of file From 40957b6eac322606b03ae191741c3bf2df4ea636 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 22 Nov 2024 09:37:12 +1000 Subject: [PATCH 25/33] Added defaults to new PermissionValidator functions --- .../cobblemon/mod/common/api/permission/PermissionLevel.kt | 7 ++++++- .../mod/common/api/permission/PermissionValidator.kt | 4 ++-- .../api/pokeball/catching/modifiers/CatchRateModifiers.kt | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionLevel.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionLevel.kt index 14f51ea3b1..94e726f6aa 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionLevel.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionLevel.kt @@ -22,6 +22,11 @@ enum class PermissionLevel(val numericalValue: Int) { SPAWN_PROTECTION_BYPASS(1), CHEAT_COMMANDS_AND_COMMAND_BLOCKS(2), MULTIPLAYER_MANAGEMENT(3), - ALL_COMMANDS(4) + ALL_COMMANDS(4); + companion object { + fun byNumericValue(value: Int): PermissionLevel { + return entries.first { it.numericalValue == value } + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt index 43fbdd14f5..76756e2ddf 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/PermissionValidator.kt @@ -42,7 +42,7 @@ interface PermissionValidator { * @param permission The permission string being queried such as cobblemon.command.giveallpokemon. * @param level The permission level being queried. 4 is generally used for cheats. */ - fun hasPermission(player: ServerPlayer, permission: String, level: Int): Boolean + fun hasPermission(player: ServerPlayer, permission: String, level: Int): Boolean = hasPermission(player, CobblemonPermission(permission, PermissionLevel.byNumericValue(level))) /** * Validates a permission for [CommandSource]. @@ -59,5 +59,5 @@ interface PermissionValidator { * @param permission The permission string being queried such as cobblemon.command.giveallpokemon. * @param level The permission level being queried. 4 is generally used for cheats. */ - fun hasPermission(source: CommandSourceStack, permission: String, level: Int): Boolean + fun hasPermission(source: CommandSourceStack, permission: String, level: Int): Boolean = hasPermission(source, CobblemonPermission(permission, PermissionLevel.byNumericValue(level))) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/modifiers/CatchRateModifiers.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/modifiers/CatchRateModifiers.kt index 757ac82c74..2b03c8c753 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/modifiers/CatchRateModifiers.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/modifiers/CatchRateModifiers.kt @@ -133,7 +133,7 @@ object CatchRateModifiers { */ val LURE = WorldStateModifier { _, entity -> if (FishingSpawnCause.FISHED_ASPECT in entity.aspects) - 4f + 4F else 1F } From 3322f3ffb60e0624035b388fb1add4b1e4ddb783 Mon Sep 17 00:00:00 2001 From: Conan Date: Fri, 22 Nov 2024 01:05:00 +0100 Subject: [PATCH 26/33] Changelog addition --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4836dd47d..577b8d5f50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -320,6 +320,7 @@ - Improved the performance of display cases that contain Pokémon photos. ### Fixes +- Fixed awarding Pokémon experience upon forfeiting battles. - Scaled down Amaura's fetus model to avoid clipping through the tank while animating. - Fixed Cubone's cry not having a sound. - Fixed the sendout sound erroneously playing when a wild Pokémon breaks out of a Poké ball. From 78ac8542f615332f10419e8b95f035d05a0b8f2e Mon Sep 17 00:00:00 2001 From: Tyzillion Date: Fri, 22 Nov 2024 00:51:55 -0500 Subject: [PATCH 27/33] Added new particles to Poke fishing. --- .../fishing/PokeRodFishingBobberEntity.kt | 19 ++++- .../fishing_bobber_big_ripple.particle.json | 67 +++++++++++++++ .../fishing_bobber_ripple.particle.json | 65 +++++++++++++++ .../fishing_surface_ripple.particle.json | 76 ++++++++++++++++++ .../fishing_surface_wake.particle.json | 65 +++++++++++++++ .../particles/fishing/splash.particle.json | 44 ++++++++++ .../assets/cobblemon/particles/snowstorm.json | 4 +- .../textures/particle/rain_splash.png | Bin 0 -> 157 bytes .../textures/particle/water_ripple.png | Bin 0 -> 479 bytes 9 files changed, 336 insertions(+), 4 deletions(-) create mode 100644 common/src/main/resources/assets/cobblemon/bedrock/particles/fishing/fishing_bobber_big_ripple.particle.json create mode 100644 common/src/main/resources/assets/cobblemon/bedrock/particles/fishing/fishing_bobber_ripple.particle.json create mode 100644 common/src/main/resources/assets/cobblemon/bedrock/particles/fishing/fishing_surface_ripple.particle.json create mode 100644 common/src/main/resources/assets/cobblemon/bedrock/particles/fishing/fishing_surface_wake.particle.json create mode 100644 common/src/main/resources/assets/cobblemon/bedrock/particles/fishing/splash.particle.json create mode 100644 common/src/main/resources/assets/cobblemon/textures/particle/rain_splash.png create mode 100644 common/src/main/resources/assets/cobblemon/textures/particle/water_ripple.png diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt index f6d6244b76..0966efaaec 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt @@ -72,6 +72,8 @@ import net.minecraft.world.phys.Vec3 class PokeRodFishingBobberEntity(type: EntityType, world: Level) : FishingHook(type, world) { + private var lastRippleSpawnTime: Long = 0 + private val rippleCooldown: Long = 1000 private val velocityRandom = RandomSource.create() private var caughtFish = false private var outOfOpenWaterTicks = 0 @@ -306,8 +308,7 @@ class PokeRodFishingBobberEntity(type: EntityType= rippleCooldown) { + particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_bobber_ripple")) + lastRippleSpawnTime = currentTime + } + particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_surface_ripple")) } } if (this.waitCountdown <= 0) { @@ -778,6 +785,12 @@ class PokeRodFishingBobberEntity(type: EntityTypeQjEnx?oJHr&dIz4a>6}b978Pp zlT#EJB-kf2eP3hzzueC_amoja&;O6|3GcWaAe&kC%>J-ui^>V+=2PN3oTgN`A3DT% zSA|FT1G|V=yTE(4Q?-t&!fo{>!aN#>UNU+s2u{^!kY|%sZ1WCU2eg90)78&qol`;+ E0GBT?VE_OC literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/cobblemon/textures/particle/water_ripple.png b/common/src/main/resources/assets/cobblemon/textures/particle/water_ripple.png new file mode 100644 index 0000000000000000000000000000000000000000..b1fb3b7c4cda166777c8feb0e0541996f996a993 GIT binary patch literal 479 zcmV<50U-W~P)1njshtcN6_vw;)U zlVsLn8RxC<{Y8)SPa-Dfn3zQX4b#Fr&KpO5mhsCr5w68S+O^nAo^`I;++fJW5wS%; zE3Xt*SI4X9p&$}CH{b<}CeYAyBa?ssZjj431 z1%Zm)sRU{-zII712a>WGbsPqqbCkUY)@0@LLKCcMc@(D#^iD;K0na^%-ot2x z>EU?vu^z!>8rk<=RlefyPDL8|o{?;DzF VAsdWb^h*E$002ovPDHLkV1imN Date: Fri, 22 Nov 2024 01:15:41 -0500 Subject: [PATCH 28/33] System time? More like fishing time. Amiright? --- .../mod/common/entity/fishing/PokeRodFishingBobberEntity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt index 0966efaaec..46e236fb64 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt @@ -68,6 +68,7 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets import net.minecraft.world.level.storage.loot.parameters.LootContextParams import net.minecraft.world.phys.EntityHitResult import net.minecraft.world.phys.Vec3 +import kotlin.time.Duration.Companion.milliseconds class PokeRodFishingBobberEntity(type: EntityType, world: Level) : FishingHook(type, world) { @@ -367,7 +368,7 @@ class PokeRodFishingBobberEntity(type: EntityType= rippleCooldown) { particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_bobber_ripple")) lastRippleSpawnTime = currentTime From f5766cdad4d8481e361594dd49c17a774794920c Mon Sep 17 00:00:00 2001 From: Tyzillion Date: Fri, 22 Nov 2024 01:29:17 -0500 Subject: [PATCH 29/33] Milliseconds? More like LITERAL TICKS. Lime disease... --- .../mod/common/entity/fishing/PokeRodFishingBobberEntity.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt index 46e236fb64..7c6969c9ef 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/entity/fishing/PokeRodFishingBobberEntity.kt @@ -68,13 +68,12 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets import net.minecraft.world.level.storage.loot.parameters.LootContextParams import net.minecraft.world.phys.EntityHitResult import net.minecraft.world.phys.Vec3 -import kotlin.time.Duration.Companion.milliseconds class PokeRodFishingBobberEntity(type: EntityType, world: Level) : FishingHook(type, world) { private var lastRippleSpawnTime: Long = 0 - private val rippleCooldown: Long = 1000 + private val rippleCooldown: Long = 20 private val velocityRandom = RandomSource.create() private var caughtFish = false private var outOfOpenWaterTicks = 0 @@ -368,7 +367,7 @@ class PokeRodFishingBobberEntity(type: EntityType= rippleCooldown) { particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_bobber_ripple")) lastRippleSpawnTime = currentTime From 2f2fd3b17e0cb8ed05c12b5895f3a4457e1e031a Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 22 Nov 2024 17:21:20 +1000 Subject: [PATCH 30/33] Fixed a couple of molang functions, added level support and fixes to NPC spawn files --- .../mod/common/api/molang/MoLangFunctions.kt | 45 +++++++++++++++++-- .../common/api/spawning/SpawnDetailPresets.kt | 6 +++ .../mod/common/api/spawning/SpawnLoader.kt | 6 +++ .../api/spawning/context/SpawningContext.kt | 25 +++++------ .../api/spawning/detail/NPCSpawnAction.kt | 14 +++--- .../api/spawning/detail/NPCSpawnDetail.kt | 6 ++- .../util/adapters/NPCClassReferenceAdapter.kt | 25 +++++++++++ 7 files changed, 101 insertions(+), 26 deletions(-) create mode 100644 common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassReferenceAdapter.kt diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt index af394d9133..07f1759a4a 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt @@ -29,6 +29,7 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffects import com.cobblemon.mod.common.api.moves.animations.NPCProvider import com.cobblemon.mod.common.api.pokemon.stats.Stats import com.cobblemon.mod.common.api.scripting.CobblemonScripts +import com.cobblemon.mod.common.api.spawning.context.SpawningContext import com.cobblemon.mod.common.api.storage.PokemonStore import com.cobblemon.mod.common.api.storage.party.PartyStore import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore @@ -451,12 +452,12 @@ object MoLangFunctions { return@put DoubleValue(store.count { props.matches(it) }) } map.put("highest_level") { - val highest = store.maxByOrNull { it.level } - return@put highest?.asStruct() ?: DoubleValue.ZERO + val highest = store.maxOfOrNull { it.level } ?: 0 + return@put DoubleValue(highest) } map.put("lowest_level") { - val lowest = store.minByOrNull { it.level } - return@put lowest?.asStruct() ?: DoubleValue.ZERO + val lowest = store.minOfOrNull { it.level } ?: 0 + return@put DoubleValue(lowest) } map.put("heal") { for (pokemon in store) { @@ -507,6 +508,33 @@ object MoLangFunctions { } ) + val spawningContextFunctions = mutableListOf<(SpawningContext) -> HashMap>>( + { spawningContext -> + val map = hashMapOf>() + val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION).wrapAsHolder(spawningContext.world).asWorldMoLangValue() + val biomeValue = spawningContext.biomeRegistry.wrapAsHolder(spawningContext.biome).asBiomeMoLangValue() + map.put("biome") { _ -> biomeValue } + map.put("world") { _ -> worldValue } + map.put("light") { _ -> DoubleValue(spawningContext.light.toDouble()) } + map.put("x") { _ -> DoubleValue(spawningContext.position.x.toDouble()) } + map.put("y") { _ -> DoubleValue(spawningContext.position.y.toDouble()) } + map.put("z") { _ -> DoubleValue(spawningContext.position.z.toDouble()) } + map.put("moon_phase") { _ -> DoubleValue(spawningContext.moonPhase.toDouble()) } + map.put("can_see_sky") { _ -> DoubleValue(spawningContext.canSeeSky) } + map.put("sky_light") { _ -> DoubleValue(spawningContext.skyLight.toDouble()) } + map.put("bucket") { _ -> StringValue(spawningContext.cause.bucket.name) } + map.put("player") { _ -> + val causeEntity = spawningContext.cause.entity ?: return@put DoubleValue.ZERO + if (causeEntity is ServerPlayer) { + return@put causeEntity.asMoLangValue() + } else { + return@put DoubleValue.ZERO + } + } + map + } + ) + fun Holder.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions) fun Holder.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions) fun Holder.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions) @@ -567,6 +595,15 @@ object MoLangFunctions { return value } + fun SpawningContext.asMoLangValue(): ObjectValue { + val value = ObjectValue( + obj = this, + stringify = { it.toString() } + ) + value.addFunctions(spawningContextFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) + return value + } + fun Holder.asMoLangValue(key: ResourceKey>): ObjectValue> { val value = ObjectValue( obj = this, diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnDetailPresets.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnDetailPresets.kt index 94b149e5a1..066c1d7210 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnDetailPresets.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnDetailPresets.kt @@ -8,9 +8,12 @@ package com.cobblemon.mod.common.api.spawning +import com.bedrockk.molang.Expression import com.cobblemon.mod.common.Cobblemon import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition import com.cobblemon.mod.common.api.data.JsonDataRegistry +import com.cobblemon.mod.common.api.molang.ExpressionLike +import com.cobblemon.mod.common.api.npc.NPCClass import com.cobblemon.mod.common.api.pokemon.PokemonProperties import com.cobblemon.mod.common.api.reactive.SimpleObservable import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition @@ -69,6 +72,9 @@ object SpawnDetailPresets : JsonDataRegistry { .registerTypeAdapter(MoonPhaseRange::class.java, IntRangesAdapter(MoonPhaseRange.moonPhaseRanges) { MoonPhaseRange(*it) }) .registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter) .registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter) + .registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter) + .registerTypeAdapter(Expression::class.java, ExpressionAdapter) + .registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter) .create() val presetTypes = mutableMapOf>() diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnLoader.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnLoader.kt index c06209e127..4fc09defff 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnLoader.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawnLoader.kt @@ -8,9 +8,12 @@ package com.cobblemon.mod.common.api.spawning +import com.bedrockk.molang.Expression import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition import com.cobblemon.mod.common.api.drop.DropEntry import com.cobblemon.mod.common.api.drop.ItemDropMethod +import com.cobblemon.mod.common.api.molang.ExpressionLike +import com.cobblemon.mod.common.api.npc.NPCClass import com.cobblemon.mod.common.api.pokemon.PokemonProperties import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext @@ -68,6 +71,9 @@ object SpawnLoader { .registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter) .registerTypeAdapter(IntRange::class.java, IntRangeAdapter) .registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter) + .registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter) + .registerTypeAdapter(Expression::class.java, ExpressionAdapter) + .registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter) .create() var deserializingConditionClass: Class>? = null diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/context/SpawningContext.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/context/SpawningContext.kt index 1e8403b37c..209332c739 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/context/SpawningContext.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/context/SpawningContext.kt @@ -8,9 +8,10 @@ package com.cobblemon.mod.common.api.spawning.context -import com.bedrockk.molang.runtime.struct.VariableStruct -import com.bedrockk.molang.runtime.value.DoubleValue -import com.cobblemon.mod.common.api.molang.ObjectValue +import com.bedrockk.molang.runtime.MoLangRuntime +import com.bedrockk.molang.runtime.struct.QueryStruct +import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue +import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup import com.cobblemon.mod.common.api.spawning.SpawnCause import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail @@ -55,6 +56,10 @@ abstract class SpawningContext { } } + @Transient + val runtime = MoLangRuntime().setup() + .also { runtime -> runtime.environment.query.addFunction("context") { getOrSetupStruct() } } + /** What caused the spawn context, as a [SpawnCause]. */ abstract val cause: SpawnCause val spawner: Spawner @@ -87,7 +92,7 @@ abstract class SpawningContext { val biomeName: ResourceLocation get() = this.biomeRegistry.getKey(biome)!! - private val struct = VariableStruct() + private var struct = QueryStruct(hashMapOf()) private var structCompiled = false class StructureChunkCache { @@ -168,19 +173,11 @@ abstract class SpawningContext { return weight } - fun getOrSetupStruct(): VariableStruct { + fun getOrSetupStruct(): QueryStruct { if (structCompiled) { return struct } - - struct.setDirectly("light", DoubleValue(light.toDouble())) - struct.setDirectly("x", DoubleValue(position.x.toDouble())) - struct.setDirectly("y", DoubleValue(position.y.toDouble())) - struct.setDirectly("z", DoubleValue(position.z.toDouble())) - struct.setDirectly("moon_phase", DoubleValue(moonPhase.toDouble())) - struct.setDirectly("world", ObjectValue(world.registryAccess().registryOrThrow(Registries.DIMENSION).wrapAsHolder(world))) - struct.setDirectly("biome", ObjectValue(biomeRegistry.wrapAsHolder(biome))) - + struct = this.asMoLangValue() structCompiled = true return struct } diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnAction.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnAction.kt index 218235f7cb..0c1b9f6991 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnAction.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnAction.kt @@ -8,10 +8,10 @@ package com.cobblemon.mod.common.api.spawning.detail +import com.cobblemon.mod.common.Cobblemon import com.cobblemon.mod.common.api.spawning.context.SpawningContext import com.cobblemon.mod.common.entity.npc.NPCEntity -import com.cobblemon.mod.common.util.party -import net.minecraft.server.level.ServerPlayer +import com.cobblemon.mod.common.util.resolveInt /** * A [SpawnAction] for creating [NPCEntity]s. @@ -19,13 +19,15 @@ import net.minecraft.server.level.ServerPlayer * @author Hiroku * @since October 8th, 2023 */ -class NPCSpawnAction(ctx: SpawningContext, override val detail: NPCSpawnDetail) : SpawnAction(ctx, detail) { - override fun run(): EntitySpawnResult { +class NPCSpawnAction(ctx: SpawningContext, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction(ctx, detail) { + override fun createEntity(): NPCEntity { val npc = NPCEntity(ctx.world) npc.npc = detail.npcClass npc.appliedAspects.addAll(detail.aspects) - val seedLevel = (ctx.cause.entity as? ServerPlayer)?.let { it.party().maxOfOrNull { it.level } } ?: 10 + val minLevel = ctx.runtime.resolveInt(detail.minLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel) + val maxLevel = ctx.runtime.resolveInt(detail.maxLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel) + val seedLevel = (minLevel..maxLevel).random() npc.initialize(seedLevel) - return EntitySpawnResult(listOf(npc)) + return npc } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnDetail.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnDetail.kt index 1972cb2831..15e237f06d 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnDetail.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/detail/NPCSpawnDetail.kt @@ -8,10 +8,11 @@ package com.cobblemon.mod.common.api.spawning.detail +import com.bedrockk.molang.Expression import com.cobblemon.mod.common.api.npc.NPCClass -import com.cobblemon.mod.common.api.npc.NPCConfiguration import com.cobblemon.mod.common.api.spawning.context.SpawningContext import com.cobblemon.mod.common.entity.npc.NPCEntity +import com.cobblemon.mod.common.util.asExpression import com.google.gson.annotations.SerializedName /** @@ -30,8 +31,9 @@ class NPCSpawnDetail : SpawnDetail() { @SerializedName(value = "npcClass", alternate = ["class", "npc"]) val npcClass: NPCClass = blankClass - val configuration: NPCConfiguration? = null val aspects: Set = emptySet() + val minLevel: Expression = "1".asExpression() + val maxLevel: Expression = "100".asExpression() override fun doSpawn(ctx: SpawningContext) = NPCSpawnAction(ctx, this) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassReferenceAdapter.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassReferenceAdapter.kt new file mode 100644 index 0000000000..c76d6bf895 --- /dev/null +++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/NPCClassReferenceAdapter.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.util.adapters + +import com.cobblemon.mod.common.api.npc.NPCClass +import com.cobblemon.mod.common.api.npc.NPCClasses +import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import java.lang.reflect.Type + +object NPCClassReferenceAdapter : JsonDeserializer { + override fun deserialize(json: JsonElement, typeOfT: Type, ctx: JsonDeserializationContext): NPCClass { + val resourceLocation = json.asString.asIdentifierDefaultingNamespace() + return NPCClasses.getByIdentifier(resourceLocation) + ?: throw IllegalArgumentException("No such NPC class: $resourceLocation") + } +} \ No newline at end of file From 6eb814a7f0363cac04ae1a35af0e1db8e913ba81 Mon Sep 17 00:00:00 2001 From: Test <12199724-nlclient108@users.noreply.gitlab.com> Date: Fri, 22 Nov 2024 08:59:37 +0000 Subject: [PATCH 31/33] Fix dex search crash + add text cursor functionality --- .../gui/pokedex/widgets/SearchWidget.kt | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/SearchWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/SearchWidget.kt index 30be7fcd59..fd3c3da67b 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/SearchWidget.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/SearchWidget.kt @@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets import com.cobblemon.mod.common.api.gui.blitk import com.cobblemon.mod.common.api.text.bold +import com.cobblemon.mod.common.api.text.font import com.cobblemon.mod.common.api.text.text import com.cobblemon.mod.common.client.CobblemonResources import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT @@ -17,9 +18,11 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVER import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE import com.cobblemon.mod.common.client.render.drawScaledText import com.cobblemon.mod.common.util.cobblemonResource +import net.minecraft.Util import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.components.EditBox +import net.minecraft.client.renderer.RenderType import net.minecraft.network.chat.Component class SearchWidget( @@ -36,20 +39,24 @@ class SearchWidget( private val searchIcon = cobblemonResource("textures/gui/pokedex/search_icon.png") } + var focusedTime: Long = 0 + init { this.setMaxLength(24) this.setResponder { update.invoke() } + focusedTime = Util.getMillis() + } + + override fun setFocused(focused: Boolean) { + if (focused) focusedTime = Util.getMillis() + super.setFocused(focused) } override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - return if (mouseX.toInt() in x..(x + width) && mouseY.toInt() in y..(y + height)) { - isFocused = true - true - } else { - false - } + if (mouseX.toInt() in x..(x + width) && mouseY.toInt() in y..(y + height)) isFocused = true + return super.mouseClicked(mouseX, mouseY, button) } override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) { @@ -73,29 +80,30 @@ class SearchWidget( scale = SCALE ) -// if(text.isEmpty() && !isFocused) { -// drawScaledText( -// context = context, -// font = CobblemonResources.DEFAULT_LARGE, -// text = Component.translatable("cobblemon.ui.pokedex.search").bold(), -// x = posX.toInt() + 13, -// y = posY.toInt() + 1, -// shadow = true -// ) -// } - if (cursorPosition != value.length) moveCursorToEnd(false) - - val input = if (isFocused) "${value}_".text() + val showCursor = isFocused && ((Util.getMillis() - this.focusedTime) / 300L % 2L == 0L) + val input = if (isFocused) "${value}${if ((cursorPosition == value.length) && showCursor) "_" else ""}".text() else (if(value.isEmpty()) Component.translatable("cobblemon.ui.pokedex.search") else value.text()) - + val startX = posX.toInt() + 13 + val startY = posY.toInt() + 1 drawScaledText( context = context, font = CobblemonResources.DEFAULT_LARGE, text = input.bold(), - // text = Component.translatable(if (isFocused) "${text}_" else text).bold(), - x = posX.toInt() + 13, - y = posY.toInt() + 1, + x = startX, + y = startY, shadow = true ) + + if (showCursor && !value.isEmpty() && cursorPosition != value.length) { + val startToCursorWidth = Minecraft.getInstance().font.width((input.getString(cursorPosition).text().bold()).font(CobblemonResources.DEFAULT_LARGE)) + context.fill( + RenderType.guiTextHighlight(), + startX + startToCursorWidth - 1, + startY, + startX + startToCursorWidth, + startY + 9, + -3092272 + ) + } } -} \ No newline at end of file +} From 0cdeab0800d7ea17b7be9c79e2381b17dacf16b3 Mon Sep 17 00:00:00 2001 From: mia <15349355-miasmusa@users.noreply.gitlab.com> Date: Fri, 22 Nov 2024 20:14:29 -0500 Subject: [PATCH 32/33] check livingEntity instanceof Player before casting --- .../com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java index 983a99eaa6..71f3e3b31d 100644 --- a/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java +++ b/common/src/main/java/com/cobblemon/mod/common/mixin/ItemPropertiesMixin.java @@ -26,7 +26,8 @@ public class ItemPropertiesMixin { // Without this, the fishing rod item model appears cast when using a PokeRod in the other hand @Inject(method = "method_27883", at = @At(value = "RETURN", ordinal = 1), cancellable = true) private static void cobblemon$preventRodMixup(ItemStack itemStack, ClientLevel clientLevel, LivingEntity livingEntity, int i, CallbackInfoReturnable cir) { - Player player = (Player) livingEntity; - if (player.fishing instanceof PokeRodFishingBobberEntity) cir.setReturnValue(0.0f); + if (!(livingEntity instanceof Player) || ((Player) livingEntity).fishing instanceof PokeRodFishingBobberEntity) { + cir.setReturnValue(0.0f); + } } } From e8dcd994be391bac6657042fdde9d7a3f8585f3e Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 23 Nov 2024 16:27:33 +1000 Subject: [PATCH 33/33] Fixed fish baits. Fished fix baits? Bished fix fates --- .../com/cobblemon/mod/common/api/fishing/FishingBait.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/fishing/FishingBait.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/fishing/FishingBait.kt index a3418baaa0..d62eff606c 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/api/fishing/FishingBait.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/fishing/FishingBait.kt @@ -10,16 +10,14 @@ package com.cobblemon.mod.common.api.fishing import com.cobblemon.mod.common.api.events.CobblemonEvents import com.cobblemon.mod.common.api.events.fishing.BaitEffectFunctionRegistryEvent -import com.cobblemon.mod.common.api.fishing.FishingBait.Effect import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause import com.cobblemon.mod.common.entity.pokemon.PokemonEntity import com.cobblemon.mod.common.util.cobblemonResource -import com.cobblemon.mod.common.util.codec.kotlinOptionalFieldOf import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder +import java.util.Optional import net.minecraft.core.Registry import net.minecraft.network.codec.ByteBufCodecs -import net.minecraft.nbt.StringTag import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack @@ -36,11 +34,13 @@ data class FishingBait( val chance: Double = 0.0, val value: Double = 0.0 ) { + constructor(type: ResourceLocation, subcategory: Optional, chance: Double, value: Double) : this(type, subcategory.orElse(null), chance, value) + companion object { val CODEC = RecordCodecBuilder.create { instance -> instance.group( ResourceLocation.CODEC.fieldOf("type").forGetter { it.type }, - ResourceLocation.CODEC.kotlinOptionalFieldOf("subcategory").forGetter { it.subcategory }, + ResourceLocation.CODEC.optionalFieldOf("subcategory").forGetter { Optional.ofNullable(it.subcategory) }, Codec.DOUBLE.fieldOf("chance").forGetter { it.chance }, Codec.DOUBLE.fieldOf("value").forGetter { it.value } ).apply(instance, ::Effect)