From 94ef6e0e1eee4bcdb4deda38ada2cd555f0e1b92 Mon Sep 17 00:00:00 2001 From: TheDeathlyCow <53499406+TheDeathlyCow@users.noreply.github.com> Date: Mon, 25 Mar 2024 02:18:57 -1000 Subject: [PATCH] Item Attribute Modifiers and Other New APIs (#8) * item attribute modifier codec * implement modifiers on event callback * use a simpler item predicate type and fix encoding * add javadoc * sequence temperature effect * celsius to temp tick api * temperature unit api * add units to checktemperature command * set version 2.2 * fix wrong example slot in javadoc * require checktemperature to be a loaded blockpos * display temperature to 2 decimal places * client api and mixin setup * heart render event for vanilla * add compats for overflowing bars and colorful hearts * common seasons integration events * move common mixins to common mixin package * fire hearts in testmod * add fabric seasons integration * update icon * make heart overlay event class more generic * write clearer javadoc for season events --- build.gradle | 20 ++- docs/requires-thermoo.png | Bin 0 -> 13967 bytes gradle.properties | 15 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../thermoo/api/ThermooCodecs.java | 71 +++++++++ .../thermoo/api/ThermooRegistryKeys.java | 3 + .../api/attribute/ItemAttributeModifier.java | 150 ++++++++++++++++++ .../thermoo/api/attribute/package-info.java | 4 + .../client/StatusBarOverlayRenderEvents.java | 58 +++++++ .../thermoo/api/client/package-info.java | 8 + .../api/command/EnvironmentCommand.java | 77 ++++++++- .../command/TemperatureUnitArgumentType.java | 24 +++ .../api/season/ThermooSeasonEvents.java | 45 ++++++ .../thermoo/api/season/ThermooSeasons.java | 31 ++++ .../thermoo/api/season/package-info.java | 4 + .../effects/SequenceTemperatureEffect.java | 54 +++++++ .../effects/TemperatureEffects.java | 13 +- .../api/util/TemperatureConverter.java | 106 +++++++++++++ .../thermoo/api/util/TemperatureUnit.java | 88 ++++++++++ .../impl/ItemAttributeModifierManager.java | 85 ++++++++++ .../thedeathlycow/thermoo/impl/Thermoo.java | 47 +++++- .../thermoo/impl/ThermooClient.java | 12 ++ .../thermoo/impl/ThermooCommonRegisters.java | 1 + .../thermoo/impl/ThermooIntegrations.java | 18 +++ .../thermoo/impl/client/HeartOverlayImpl.java | 33 ++++ .../thermoo/mixin/client/compat/Plugin.java | 65 ++++++++ .../present/HeartRendererMixin.java | 87 ++++++++++ .../absent/InGameHudMixin.java | 87 ++++++++++ .../present/HealthBarRendererMixin.java | 86 ++++++++++ .../{ => common}/EntityPredicateMixin.java | 4 +- .../EnvironmentAwareEntityMixin.java | 2 +- .../mixin/{ => common}/HotFloorMixin.java | 2 +- .../LivingEntityAttributeMixin.java | 2 +- .../LivingEntityEnvironmentTickMixin.java | 2 +- .../PlayerTemperatureTickMixin.java | 2 +- .../TemperatureEffectTickerMixin.java | 2 +- .../mixin/{ => common}/package-info.java | 2 +- src/main/resources/assets/thermoo/icon.png | Bin 343 -> 3028 bytes .../resources/assets/thermoo/lang/en_us.json | 4 +- src/main/resources/fabric.mod.json | 19 ++- src/main/resources/thermoo.accesswidener | 5 + src/main/resources/thermoo.client.mixins.json | 14 ++ src/main/resources/thermoo.mixins.json | 6 +- .../thermoo/testmod/ThermooTestModClient.java | 62 ++++++++ .../textures/gui/fire_heart_overlay.png | Bin 0 -> 393 bytes .../data/thermoo-test/predicates/is_cold.json | 11 ++ .../data/thermoo-test/predicates/is_warm.json | 11 ++ .../diamond_weapons.json | 16 ++ .../temperature_effects/sequence_test.json | 36 +++++ src/testmod/resources/fabric.mod.json | 3 + 50 files changed, 1467 insertions(+), 32 deletions(-) create mode 100644 docs/requires-thermoo.png create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/ThermooCodecs.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/attribute/ItemAttributeModifier.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/attribute/package-info.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/client/StatusBarOverlayRenderEvents.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/client/package-info.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/command/TemperatureUnitArgumentType.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasonEvents.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasons.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/season/package-info.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/SequenceTemperatureEffect.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureConverter.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureUnit.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/impl/ItemAttributeModifierManager.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooClient.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooIntegrations.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/impl/client/HeartOverlayImpl.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/Plugin.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/colorfulhearts/present/HeartRendererMixin.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/absent/InGameHudMixin.java create mode 100644 src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/present/HealthBarRendererMixin.java rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/EntityPredicateMixin.java (95%) rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/EnvironmentAwareEntityMixin.java (99%) rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/HotFloorMixin.java (96%) rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/LivingEntityAttributeMixin.java (97%) rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/LivingEntityEnvironmentTickMixin.java (95%) rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/PlayerTemperatureTickMixin.java (97%) rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/TemperatureEffectTickerMixin.java (96%) rename src/main/java/com/github/thedeathlycow/thermoo/mixin/{ => common}/package-info.java (73%) create mode 100644 src/main/resources/thermoo.accesswidener create mode 100644 src/main/resources/thermoo.client.mixins.json create mode 100644 src/testmod/java/com/github/thedeathlycow/thermoo/testmod/ThermooTestModClient.java create mode 100644 src/testmod/resources/assets/thermoo/textures/gui/fire_heart_overlay.png create mode 100644 src/testmod/resources/data/thermoo-test/predicates/is_cold.json create mode 100644 src/testmod/resources/data/thermoo-test/predicates/is_warm.json create mode 100644 src/testmod/resources/data/thermoo-test/thermoo/item_attribute_modifier/diamond_weapons.json create mode 100644 src/testmod/resources/data/thermoo-test/thermoo/temperature_effects/sequence_test.json diff --git a/build.gradle b/build.gradle index bc8c7282..20f4a1a3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.2-SNAPSHOT' + id 'fabric-loom' version '1.5-SNAPSHOT' id 'maven-publish' } @@ -23,6 +23,17 @@ repositories { name = "Ladysnake Mods" url = 'https://maven.ladysnake.org/releases' } + exclusiveContent { + forRepository { + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + } + } + filter { + includeGroup "maven.modrinth" + } + } } dependencies { @@ -41,6 +52,10 @@ dependencies { include "dev.onyxstudios.cardinal-components-api:cardinal-components-base:${project.cca_version}" include "dev.onyxstudios.cardinal-components-api:cardinal-components-entity:${project.cca_version}" + modCompileOnly("maven.modrinth:overflowing-bars:${project.overflowing_bars_version}") { transitive = false } + modCompileOnly("maven.modrinth:colorful-hearts:${project.colorful_hearts_version}") { transitive = false } + + modCompileOnly("maven.modrinth:fabric-seasons:${project.fabric_seasons_version}") { transitive = false } // Uncomment the following line to enable the deprecated Fabric API modules. // These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time. @@ -86,6 +101,9 @@ subprojects { } loom { + + accessWidenerPath = file("src/main/resources/thermoo.accesswidener") + interfaceInjection { enableDependencyInterfaceInjection = true } diff --git a/docs/requires-thermoo.png b/docs/requires-thermoo.png new file mode 100644 index 0000000000000000000000000000000000000000..10294d790213f3e25e60fb368e669872f62634e4 GIT binary patch literal 13967 zcmbWebyQW+*DicSFhEL58jk`ZCEXp;Naq3R?rsq2mXdA|;gE+;>5wjI>F(~kx4-xO z?)%R-?znf4L5#iDTshZV&wS?GA)n+vqN5U_LLd-yNeNL!2;{Lg1oDXD*%RPRX0p8+ z1VRCk6cti-P2HPEbH$rzx;XNweJ+Xo==rBd27(mGLdc=0sLQ1AZpB9mnnSb%g^Iip zAL&(=P~hX5W@{8GOsOkce!|72RiQs5M!pE6ynas*BaKYYA=HWd$uD(7U_a~i+t(}> z0}GWGo&rO$AHVL_74vzVUk}vhS#7as3l}E^J7UN1V)tUlSYn&Kh8BcM;9wWhLX-6P z9|1#zq{j#M)IclLMEP-INTG^!&=fJlh~P$QXhXYpu?ZcG?Y$QD(ntGZXQ+K$@M+{7 z;`!JwP%s#+Mj@{}7y;K0(h@T?+ugv!o_^I;L-oS1C@1&@PR#4_Rur&(>QSn57S?kQ zqT2P)9u{Z?KV6<0tTN%PZ`D|&29`thUV|BWM4zW*<-pPtOHeW@Fu1;bN20jDI zDcrX^jziYtu*>IO{-8igEQzm@5KVs>qf{FVPWfy2dK_W-+&9wItZ58w?k10z;M2-R z7}#$TE0mI0{pUwf(9z*hc2J1I6y1Q!Se$N3-RO;N6;@13_q8Ar@LrIa z2)mz+-ioZj?C&-mv|lW@Aa4Z}@D<%6)MBlxgx{qgmpF*6%3_x#lbXG@hub`PNE~5mnZ2j$7OGyVLeJw3eTNj zhh-1~4GaFH;qes3D4u?+ORqusbkZ81CQN#4iXVIHJ+yP;Zj?|OHT6Mtsx6)h%CbUO zJ!(GjE&O0ZGN*qqwtgU`%c2Gx-M47=-?=VaV^_cw$*ztkj1;eu6M|U@ApSx~dhUT2MXa^}%9Qunioaub}CqDf@De-1+c}m%a+n(dPOARs32i zSM`kk%9$ke+|E4?iT39C|L7dDTxF0k)&MkNBIFFCSh2HpLP#|5iBlW5PLbGs)LL$; zs|Hnno;?-EwW1xn_vjJ$bBwY~0fc(|O zUCxd&HX(jT$kd=r;(E;@g;R3+%Z_z$Mw#NQv8*oj1o^XcWqRejPN~NRRo>bCdeh!1 zj<%YU@=wc~6a1=T#XF4z(z*l)6YpHH*BmZZ{AQBUb({{CzLk-119~uL%03cavEni$ zF`d=U?p(yRO@-~2yDSiAvqVJL@tmE^wLCTMu8D#j9UOhLskj;Qp?*pLM|MSv<72gp z?I-S6c{`R&#IJ!;-w)h%HZIha>Z42vMUhKSN`6%jwVcyX_ZPO8EVqHDA4@Y?W1H35 zbmuU#U&Z?Gb=Dq|sr@{qV$m@~780L3N6Si+47(IR`*h-%%nXhT?I#MM%V;z6rTvBg?{>q$NKikrcEYto>(i-qK4aty^ATP9i1B6_}bCV}jiP z+sKNaP{Ymr>bK@!Kp%5*C*TGa4qy?MOR^d3?5jRIU2FQ+c^MOtMOlH9aztx;D5Q-C zIb+e(^kjC+jzkA{ypw}x>dpKMI0D^k6OXG{_*&K%8rRdkAN<%BqIMSADHn7BTNN$J zIvdvrGqi90_&6~QzeLkavBis+HU(S?U(uh7_l{$1b(-})WiM;Dt1PLih{okV$AIJT zCFKs3W{G^%P>DOq(`1D1{ci`p`RjF=t=|8V`>Vf44_2e;_^?8fM%ym+_vdV}-F6%u zDB8pFoAaZIK;_F?a}MiQXBVL63ZjAKwmiJi0wmRSp^+ z$g==M?6wBi3tGP-RWF{Wpd3W+GwsTF;jaVW-^?edx@k7bGUzPKYsU)&isOQ)uWbQ! zS7*K)EEgxPw>peKW}xWAs_oO@F^k)nawK}ly&%0CUO6Z|YYwYTlWx6D4ca(XA*VX$ z#W7l{aN#|;T1c-ew7&)#_j_aeD7aWHp_%4JO8TYyb%7CE;_uS~V|Ob%bCzE7QP#Et z;65x2YDJ-_xUau-aO5YjgrGU=pC8Z{xusEpasR z1c&F3AXxgy3=j!@7nvlPO(Y0K2Pm}4>WHWMeHsg>|9GtJu^{SjFDrYwF+sHL30b(hIX?=`}bIU#zTv zqDG7b-3sT?cSEJUHz!c}mX`IT`}B*Ypectf)_chzk0Wgzfzw8`UgCAQXXi)48@I)~ zID$6$227`(!NL(cZ}rCMtAiWj$^KCl+O(~CE3M0O#jVliH)lUL2{c;i?^54faiDL|qd+L$ZYn;5JkNe4)2&VaoqufRk>wv%x`y-p zrGJH{OGv)R4UHE8a#NydX2XG`8G8xERI(x$)#g~^1`Z9X*0aqpcAtu>(Z#)l6l!!9 zmNddyP4<4NZR>CD3&b_}{RIuq-1Cwb-dAf(5BupmW*P%|7D3v*rqStT1}KUe$_GMu zPtW`Ft+b48_F)yJh>oiUj)lT{=?0Jemph#?N$Gi&OWf)`rqDLjOpFmFrR72 z)rCcAEdRzSCxfTiTAAU4w{G|N^0=|8e{C-^ef4`=Yq3$rStID$`!nwYW7xR*S_3~&$Hpy zYhVAv8ZywfX+4HcIm7*mioI)Xi!D=NPs^mA-aoLfj9l9Tq3^J^nc}5JRE0OO9=8U2 z3fsG>(zL5A|LPY=AG=ZpquP$Z>yOYZ3PaUJT(`c}>CTzD=OJ&TE}DcFo?q8^dB=xr z^GOV8wXfZ=8j+ed;Dsc!DhiY<88Mh_`T z^2p3L`eN4aMi_5zd=fGa89ls`1-3Ai;HrnL>hnxS6(=VW_nf^JF8N0|?yXc-l1UF2 zoyV7R8g^H684|#D9q}F=-sqNQ$p+hZ=U`i(U{K6nFX?6`N3wBm4&QuVzuxBrYU z+OetCydLHR<+{ZCBN>=yYd)6d zgk7qxItP&O+m`WiTYovKSLIF+E-0x!`>H|fAtZ~RY4JzfUFX2ye(*dSF322q%qNkU zUgmT#vb8g0zNo-$xFHdyltQY8e|>1vZPJi-SXYV=U2rwwx`3jAmR69x`^~1(Y-awO zNxvCyGmej?N|Y9qpJ(`R(x$#6Zl~SbiVtF~`>9~RFXu{3q^B_I;>j}7TU5OrEi;@s zys~XE(8%9ZWP4&>XKZAg-DyrK;gA)NWOukPT(1Pmqlal?%;d$~MEig?9t$>Pd>p?nU*&Qqaj4R?19j=L;v5b!K+*2 zb(}1XFnYod(yt*MF@bb@$@xlj7HiY0e@3M|KULyv6BOyr(03!8w)U5D(imL4>0woE zrVs2|HHhUL`7n!@)eukWzKcxR+22#A6>@ojU~@iGycokFkW0c>59@8KNxb&n z1FV_F9w$TLhpew^nr0`nNTxHwROR>*q`{bPd0f6ETGzRaeqcGNO}f{nypdiTgYl$2 zc=)utYt)q&L)W!+PSkK?X0w(vP5!DgZdl-+(|6MjCQ0HdmkUn zjgd~;Pf5;b$}_A?U4)6pKJ9Q~QD|&&{_soGc|FhLbk4>UCE0I1ejvndx)aO)9nJn> z2KD=o6X_@5PAd{sfAKUglsCL{#@?ETH=M1EpvEnhh|N~pT%6TsV@_SU!`!&h9X?fl zL=w`vKGim<_$vCB%rGb+E0S2bU?Zpw#7x{BO+3a``_t1Jq;?YM z)4Ybc@#v5UAaAj)=tl@EmyaA3yJ44SfW003lEmI*{2{?$h#=#}JuSjPLU{)gy<7V< zq>|GP%kkE(lc|TOZIG95_IVMAvz)Cek`yLhf^9w8>$86tI$qIqn-bdh-ctJe0X~wA z7siDte|F0qi<;0u)erfLd69WAxy-WFw<%^6D`*xK`daOUt+2Xo|H^Xq*H#s|g39#M z+vFS#TQ1{6){P-3i?ra)+v-?$ z;zc}3UYQW`d_IyvnCo4uH6;%^QVmcnWPs5s9!NIGI4fBn{#9u zOtJx+r;Oj+q`d!Kzw~232#WLqg{_KUqA(=bzac29nmb)u>vtP3-oZ~N3t?MaW6NT% zYO9;`j&xCpfUOW-MWy$1{^nBtYWK43X?E?>8R@i9t5KELUMunNps>QPz{=Tu_TWt< ziW8t3?sTh?j?^tw%wmTl8c&2`stg`w2YN(vT=V77OA|HgFn*`xd8nRy{&@y|*Bn#R z4k~Z?SzSS7new zKac1n0G_5n@?o@2a|fwG1>7u~*6wC8bO5wXa((jy>s;?>{W8t;<>wb_TxPI=X7!lH zV>0d4y+7oq-tbLXjA6#+*#!;jy{#hEO%Lb&ZQcpfj65_Jx2Y^ z)lt7Vn%b-PUP2$dOfxZ2H&ugy_OAE+BILd~4t$TX+kzIt2UlM7lXQbhi?ms+Sl#Uq z0U#dt&TN+&GV#)>6(Xj40=uHaP482)Llgl*{k++HResCg=SDfB1l3H~iRn|buEb-; z0((17`!~gFq2u^x9?=#fp=+Zz`LCJ1XX0APl{YV93j+z0{KyO{03TEE3+GDR3*)7q z5-rgO>BrF%u_NUl^Zi7+leWe*X6bF$>f%!!p1@Xk(I=%6=sC2{Y`U~YSB|uvXx5EN zCBQo?+eHha&K3Q}k7-@4btn#4+Cn^r%THOxZnJfm*S5a2eo2h5{fTzEqWcd+05jUc zQMntfF+0UN`Se4tY)|D5uJ#gvlsEgEw?_JNd}>&;Pf7fqE6rp0;pQK7^f#Jys4MmC zULG?V8{Kfh?Yix%qWSzeJI3oAp8_N2qeJ4fV`2cKBEy42uK9j2&rqrfaLiaK9N@8Q z9p0ZFhXKoWz&aLCq=a6HizJ9tw^Ao_eW{kKdo5j=td)``t+jkTl(wdcUMV=}$D)7Z=mF#|Jh0uInu39NYc zy)On9+=l1@4rJ*>(*?iVpJL=!sQtPV=$HS~Y*qd24VUO08Llc?{*WVCYoXI zcz>oA**v|C_yxGmHTAX*%x;czU%x7!IyEZ4+PbV3RrYquc)0VOciM>N1+XV7aoc1F zlbJXd4<-q4k*=d}KXnydtb6b$P1kkM-N64vB9jWgQt%n#`F(Se-HJInR0SpGf4=-F zIvb(*uwP#_Iq5jEx0&5Dwx47QZ*}AqQ*e&Fz0JCbb+6_tMfCsfj~a3mw&b0h8?oJR zW<%dW#+w<~Ny7{+9rN>0qBsz6WV-QsX94x)8`4AJC)ZfWkY=VF}`WmgeZ6(k& zHT@GcJ;lT+Wd`N=?2E==?D_}igHZ$2>@zXiSKNEv!}DlWDm(LS1i}J4iOwgZgiI?c zWYv@3MUJI`vkph;vKl+~)|+|Jriwn&2HdfE@lvEiZ2HTLaMDa`TEJ?ap%(F*k=Y6f z{PQ7e^H8K@Yr~Syxe5T{=7=^YOI@Uka^tN0nv4dWVMTl)S$T}MAuH@y5Sj~mwncB9 zb%vU%DHz~ADkFzK!iX?$epSgtE@6YDkPM*F+R=*oVNi=i&RKb?D#fh$$9E%=-1p~E zM$9*PEggBwE2_xHGiijTJwGKbat;)R24SncZCqUz3#Mh402LrTbA(mhFVa9a?{q>R zjwuuY^!nr0&CKOQWoNPnAXE(Zzg*dEP3|rvF1y*tvYBaJzhGqHE7oUkngot! zEd(Mo78-nC3&-WQ3mW`hYc}hmG-0#YBz+#ApzMYV&{cUxm2?|JR$4XB&k|caN@n_2 z(pQ2yQ=)E~#wwDfjorojTw;ofd4IaM@chSR8&P>anRm?B!^e@x!}N_OOQ!c;@yc;( z@V#z^%*B+UI6ydcqx=efpM^ZOuq=7E(_X{(nY404gro&oy@JV?So zZGhipNb1bej!1yykP+M2q3&hC4e0S zST8{Ee~-+OhHfL5O^dfBUtxhTU3aBQT0(QmIsL*8IL7I^IGAoeI=GiQ7-ZbLS8O_q zH>F;UAk!L52JXTCoZ0_j;{SKW<5{0$%$sk|wT4XM){ZXZH7f&lKvI}m6gc6)^Zz@4 zssV^^u!l{%`&F51<$>_#Y)+yfn;FrWJ~AXz5!i%aPIN8*1CpHvECBVs1-Ri)DD}AD zf(Qx%3llx+yf^>GrdU1w*v7s+T&4~n2VZl(gg`!#`Jo9)%^po#=j_|}E?58JcnM7Z z+3Ww7{qNnW$0g!68%SuqP*6pToLG$3SEH?}FAogW`M_$dD>B%DjuesU|DU$J#cX{9E?%?g87}qEcLpDKr{)oM0CTGV?1MT_{_j&qi&!n)CHK!QxB#HzpRZD?wpwIJ5tw%WUE-iw6*Eb%)%Rb7 zCiMsEBEW&^i5zn72aZr0%a2`8*u4EouAXr|>+hbpgn^1@E0ViS=sRf_p1P7D#hNwi z-*N0p4>HMXm0e!!S0(($^g89QFq>87*NPJx1@1NoQXbxyD<54tzzDWFsQunyB&K-G z*dz>4;EbnB(nAja$j`9WXR{Cpz7&`X@E;;{x(xou)T0KyS>gJ8mlAM`o!@UCtv?ZO zLGYAjTohN_nI~$*G7qDg_TJw$>-uDe7CuQ0uwjg+M%@^{^s-sx*0LsKRc7ESt%(n{ zc@_Ek-M)Xakyz0hKz;UR1C-=>?nS)x;Le*mE~~ZM%7&oxMp_Y#w*_EiG%<N0cgYeRfZ?sh|SWoci^nOc%5q6Sio z5QG3Blz=J+uzp5|X$D>`%|9JRLQhzHyma=zGfy8rYbkog|AAfi35>nfRjazG;#Br( zXL%~3G-TT}Z*#Gxh4=ux*NeaKrclH~zI-f2_TCK^l)0tUmBLI@HVNAHch`s+Xp*idlwbVlH`fh0| z5ssyS<8Ct&Bzz4Jv9-ei=zUPO5A?i{Rw6iwHj8mCPUII?)S|4FFBvZi?Zbt6H=BbN zxKvAO&(>>$Q08?HQUZG}&6HAyzgeDsuUgv$qasrh%z^N>KWwJ7mUX8m*;C2@6Kd}G zVt{emsOe=%cji>J*;Genlut*8t}3DHT$#=2 zL?spL<<19YEBSR?l>*mm$>WM2G(t11Sf%>!`p>}E5bNLi_% z)@Sbo1oES&_hQvA<8}ET13sTzk!qtR(R(!BBa9mXpUwN)s%-PtaSJY)sgK#3V>Dso z0NRDT8o~M&j88sq!du^9ny2(QZ;!!{-Sm)zOHbRhfnZz7&WoEYkot@YkpgSfT4Ozw zKx%JRy>v>24YPhWZGB--R4|)NR+=fKk;?##j>2rKgG4A0ox_r{9!<{)q3?MS7bNVW zJ#$ zuMkCAf1cD(@2JB+jD(=Q^twZt=*x>g2e<^33ai;u?&gh=#V7duz&yGUhY;V~bAcfN z;+|_|qR*BHx>~eX9RD#Sk@t7!^9`%9X*`BYv(m-NKdY)33Gsu)?MDi4{M8@iX!m+? zd(K$g*SDsb&19O-td#HzK@vp5{CdsC!-~Oc*^=AXc%fY>5AYaN$PycZ4-WnO(^fcO zN|LmeGep>3omnxB2#cJ^&dRH+_u+6_6aNNv8-T9L6LB6Kp-Td|D>nX`a!vNexjj0QgzufvjmYu;^aa z-eE=xB#v=-5sME%$SCbO9-`*v{j^vGETx(o9g!$PKU3fuq0%t+$F=RIi>Dv% zly@`mo7%HQHG?drnqi*L6Y(4NVe#U7WM0uTZ6UX}KB+GN+^gbTTIA|wAzcU`ep4Pq z=rZpqU3E~(|96=Djzq`fC~vUXK7Yj@5zzkxJ(s<&UApz1e7~&L7nuVt_O1v;{E;@g z00`vG%ugld%j_}AlKL_gLU`PEqPO~{ex;N#)%|QS5KRfZhD^MSatfU zW=#$)H}OK1L-%9siFd`+XWHJfYG?WN4h-NDte!-`dK9wBB%@?qvk?dzru zZJSG5@ou3cxd=H8Q8e%(e*<89^V7W26%2&1*)gt81H}9>TJutIr|Marxf}=PwuSoUDBwJ~|jIqJkun{w1sQYKj z<^xq^blCNW)?79*+6f0b-d7`%#4?*~j(72D#Nl`_;(m$L#1ZbStd*hbj&0Jaen zuA}E>Z_v3q6I~+FE#1`}u>9)%J&=-A1UIlXPk!jBj(dRP!T7XV03DvO_V~%<&YPI5 zBOu5?ZMn#hM$v}QFTHq9jMKJh(tgSZy6dTg;&+*J*+D>b*Y|6`g^O`pl#o`yZFR-j z{yLo2NY<3Hrxe8L0%WBL{Crbpcq7BOX9bIDus;G>HwQDXk5=@p!XH->l6=Hu+S%Tc zOFVdLgv|$zv`uBQif#d+(onT&5l5=K)b zHeBw?>F4cC?Zo0^qCTiQ7}J%g{ov>`b|mb*h5MIVDOUoD28hvj!kI+*XekG@<>bsV zY-ZyuNZ;JDEjkRKn!ODrOR#4y1&#S-)kRdnOt8f0!*RR zsr%h;F#grmJu+oGPtyn01bu=I?S&iR6%^iwQ$PZs>}em56fHl215Dr>&=(Lz`_7Ggs;6g=8*tjqckm(Rp~aPQaVG~=w5f; zYq^>vH*10~_}@l>$V3F@O{kU)pgyiym^7!AIj=j%of7c53h1(Pi7+6R3AJIeKk)9f`D2@q*IGA$iv6nIfm4kGIHGPE-9_fsz&l;^M-Wk z4i_EE7l;9{r)N^7v*MzongTUquE8BsUP|~Sk|xIY=uh7Kma$B#C9je$?)AGV+!+++La4n5u!8B&lTvnRJSn zK=KacZ%noGR(9UWL$f+r4ML*gxr^Q!kQ(xrhV@&rTpAT#(5 zLi>Vca0FpYZELki{}IE}%0LNx8~rKSigo1`XY%AOou_B642JdP_o9DH$*4ocKn!#2 z*q`P5tVqE1dbk&tJeXOST#wF{)2yVdu-OB-LGPA~TmCWwrlm7pptWxi#>65ibBVKN zOQZgpO*(zee9HcdV6L>U?Wj!7bk{KV#Tt+TdG0Xxu<%hEBuXIP5{!w^PLvuq?u8yt zQCFkp7+&2}v;X70>`Y#dnKsXC-A6qsp{$l$Zk>@W?gdLIXw1Mo%gyheMr^i{Nj-_B zjhz$!3br^kx=ihbyGM^7a{Mg(2EM(6)yV+~xZUei;3`mjO4gHFgqau+FeW}^XqMBU zMQrf#{w$U`=WiIESqH~MVVWWEz%f=;^ELqt)($ zo(q+f6mUTxMXZ%1*Pa;>vfl5YwD0eYt^%SmD!dLUA1(!@7T#AhX@HeJ_uWB8a!v#?6j=4j+A{Phr z6%4^z&$Bc{!Fplqp%U@5RBA7@&9m2YC{P7W9{w<`MIE25%h$Gn6*~r|wD_GW0NDK; z7NmbwC>4)Vf(|_v{Y33uyXw2^>R7$&VPN#B0j%qr!|Jhnc}GVbrq2F`ozye_J({-p zml7DsxK^(J*!iG`iZj8j510L43N=u3Av=*ImovR2$$WHj(MzMx-|zE`FisRMA2?-B z%tMb2v8eaod$}}uHmzlhUVbEy7O#mE+6V9!MDXjb?p@Zdmvh!0!`HQWynfjUso7Bz zSCxl!DrlH!t}gFF!jn3#QNzPL8B#ETx0NI9*vn?g!v6E#It7|tdGP5JFqpfx|76A! z)GAj85 z8N*2x(a@qBN)B6?)?l* z1~ohQXGQzR)z)NL#R|u&UENB_+~U3`)G1x5?=0h8vRf(3Rbs2O^r~d_RO91&?=jiA z1*({dB*drA6M_X~0!$9Ttag75ye}(uAy7&Tp#g zM=~-)NR@pWC3LaIy_PK#<#IwxY%nVFebH1!(zwN#;x@a@a`?s1*a=Z{6V_-=bh&Q` zcc&FESD;-8-Kc6&MQMwjF^hoxAu$)?#=%5;vWDlPk3*}bpJUcVjm7@`Ct7{rdy3>I zC8c7kAEC$9!3e28C@NIP0|G8Q0$?Lp)EU7&n7~IDIE?-|j?|AuSEU#JWzAzB8PPrU zTHbJM8WcZj4%U7RrPTiS0g9rwB;7;f!3FD`%tMWgXbIF2TrUYd%JR9&->bNHgEja0 z!P@Vky0!uT3Yc^|jt5in3ydmS^EwQlH(R1GOtC;mWknat{_BgGy$E&hD}zsz7Cg3v15o>z;Bb9>5m4&&sTf_U zHf}$o-*c#KAv1Q;Yp6+Rk0Ul$hYDy6D1Kpd9``1dH!9+vE+OO9YG;J>SV2cMM2*9G zVtoHyJrF!Ij3k_X%9>7lDQ#rZT5a;so(Sluq-gpJ{SQQ7AIi2L5(JJ#R|C}`=-ky(%hz17tR6$1>gu21o zilWX6H)cc3uq3$nS%1>o_qOph&~pb9I$Ib6v`K6g3EK?s%L-T#iy!!qDFs@gEUFWN zQZoEp + * Exposed in API for the convenience of API users. + */ +@ApiStatus.Experimental +public class ThermooCodecs { + + public static final Codec EQUIPMENT_SLOT_CODEC = createEnumCodec(EquipmentSlot.class); + + public static final Codec ENTITY_ATTRIBUTE_OPERATION_CODEC = createEnumCodec(EntityAttributeModifier.Operation.class); + + public static final Codec ATTRIBUTE_MODIFIER_CODEC = RecordCodecBuilder.create( + instance -> instance.group( + Uuids.CODEC + .fieldOf("uuid") + .orElseGet(() -> MathHelper.randomUuid(Random.createLocal())) + .forGetter(EntityAttributeModifier::getId), + Codec.STRING + .fieldOf("name") + .forGetter(EntityAttributeModifier::getName), + Codec.DOUBLE + .fieldOf("value") + .forGetter(EntityAttributeModifier::getValue), + ENTITY_ATTRIBUTE_OPERATION_CODEC + .fieldOf("operation") + .forGetter(EntityAttributeModifier::getOperation) + ).apply(instance, EntityAttributeModifier::new) + ); + + /** + * Creates a codec for an Enum. Either uses the enum ordinal or the name, but prefers the ordinal for more efficient + * storage. + * + * @param clazz The class of the enum. + * @param The enum type + * @return Returns a codec for the enum class + */ + public static > Codec createEnumCodec(Class clazz) { + return Codec.either( + Codec.INT.xmap( + ordinal -> clazz.getEnumConstants()[ordinal], + Enum::ordinal + ), + Codec.STRING.xmap( + name -> Enum.valueOf(clazz, name), + Enum::name + ) + ).xmap( + either -> either.left().orElseGet(() -> either.right().orElseThrow()), + Either::left + ); + } + + private ThermooCodecs() { + + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/ThermooRegistryKeys.java b/src/main/java/com/github/thedeathlycow/thermoo/api/ThermooRegistryKeys.java index dad30d92..471faa52 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/api/ThermooRegistryKeys.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/ThermooRegistryKeys.java @@ -1,5 +1,6 @@ package com.github.thedeathlycow.thermoo.api; +import com.github.thedeathlycow.thermoo.api.attribute.ItemAttributeModifier; import com.github.thedeathlycow.thermoo.api.temperature.effects.TemperatureEffect; import com.github.thedeathlycow.thermoo.impl.Thermoo; import net.minecraft.registry.Registry; @@ -10,6 +11,8 @@ public final class ThermooRegistryKeys { public static final RegistryKey>> TEMPERATURE_EFFECT = createRegistryKey("temperature_effects"); + public static final RegistryKey> ITEM_ATTRIBUTE_MODIFIER = createRegistryKey("item_attribute_modifier"); + private static RegistryKey> createRegistryKey(String registryId) { return RegistryKey.ofRegistry(Thermoo.id(registryId)); } diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/attribute/ItemAttributeModifier.java b/src/main/java/com/github/thedeathlycow/thermoo/api/attribute/ItemAttributeModifier.java new file mode 100644 index 00000000..60f7c167 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/attribute/ItemAttributeModifier.java @@ -0,0 +1,150 @@ +package com.github.thedeathlycow.thermoo.api.attribute; + +import com.github.thedeathlycow.thermoo.api.ThermooCodecs; +import com.google.common.collect.Multimap; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.attribute.EntityAttribute; +import net.minecraft.entity.attribute.EntityAttributeModifier; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.tag.TagKey; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Applies changes to the default attributes of an item. Item attribute modifiers are provided in datapack JSON files at + * {@code data//thermoo/item_attribute_modifier/}. They are synchronized to clients so that + * they will properly show up in the item tooltip. + *

+ * Example JSON file: + *

+ *     {
+ *          "attribute": "thermoo:generic.heat_resistance",
+ *          "modifier": {
+ *              "uuid": "413a10a0-bf0b-47db-a9a9-2eb3dda3bbaf",
+ *              "name": "Test",
+ *              "value": -1.0,
+ *              "operation": "ADDITION"
+ *          },
+ *          "item": {
+ *              "items": [
+ *                  "minecraft:diamond_helmet",
+ *                  "minecraft:iron_helmet",
+ *                  "minecraft:leather_helmet"
+ *              ]
+ *          },
+ *          "slot": "HEAD"
+ *     }
+ * 
+ *

+ * You can also use tags: + *

+ *     {
+ *          "attribute": "thermoo:generic.heat_resistance",
+ *          "modifier": {
+ *              "uuid": "413a10a0-bf0b-47db-a9a9-2eb3dda3bbaf",
+ *              "name": "Test",
+ *              "value": 2.0,
+ *              "operation": "ADDITION"
+ *          },
+ *          "item": {
+ *              "tag": "scorchful:turtle_armor"
+ *          },
+ *          "slot": "HEAD"
+ *     }
+ * 
+ *

+ * This class is experimental and subject to change. Please use the datapack JSON instead of referencing this class directly. + * + * @param attribute The attribute this modifier affects + * @param modifier The modifier that this applies to the attribute + * @param itemPredicate A type predicate for items this should apply to + * @param slot The slot this should apply to + */ +@ApiStatus.Experimental +public record ItemAttributeModifier( + EntityAttribute attribute, + EntityAttributeModifier modifier, + ItemTypePredicate itemPredicate, + EquipmentSlot slot +) { + + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + Registries.ATTRIBUTE.getCodec() + .fieldOf("attribute") + .forGetter(ItemAttributeModifier::attribute), + ThermooCodecs.ATTRIBUTE_MODIFIER_CODEC + .fieldOf("modifier") + .forGetter(ItemAttributeModifier::modifier), + ItemTypePredicate.CODEC + .fieldOf("item") + .forGetter(ItemAttributeModifier::itemPredicate), + ThermooCodecs.EQUIPMENT_SLOT_CODEC + .fieldOf("slot") + .forGetter(ItemAttributeModifier::slot) + ).apply(instance, ItemAttributeModifier::new) + ); + + public void apply( + ItemStack stack, + EquipmentSlot slot, + Multimap attributeModifiers + ) { + if (this.slot == slot && this.itemPredicate.test(stack)) { + attributeModifiers.put(this.attribute, this.modifier); + } + } + + public record ItemTypePredicate( + @Nullable List items, + @Nullable TagKey itemTag + ) implements Predicate { + + public static final Codec CODEC = Codec.either( + RecordCodecBuilder.create( + instance -> instance.group( + Codec.list(Registries.ITEM.getCodec()) + .fieldOf("items") + .forGetter(ItemTypePredicate::items) + ).apply(instance, items -> new ItemTypePredicate(items, null)) + ), + RecordCodecBuilder.create( + instance -> instance.group( + TagKey.codec(RegistryKeys.ITEM) + .fieldOf("tag") + .forGetter(ItemTypePredicate::itemTag) + ).apply(instance, tag -> new ItemTypePredicate(null, tag)) + ) + ).xmap( + either -> either.left().orElseGet(() -> either.right().orElseThrow()), + Either::left + ); + + @Override + public boolean test(ItemStack stack) { + + if (this.items == null && this.itemTag == null) { + return false; + } + + if (this.items != null && !this.items.contains(stack.getItem())) { + return false; + } + + if (this.itemTag != null && !stack.isIn(this.itemTag)) { + return false; + } + + return true; + } + } +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/attribute/package-info.java b/src/main/java/com/github/thedeathlycow/thermoo/api/attribute/package-info.java new file mode 100644 index 00000000..36cfd2b6 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/attribute/package-info.java @@ -0,0 +1,4 @@ +/** + * + */ +package com.github.thedeathlycow.thermoo.api.attribute; \ No newline at end of file diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/client/StatusBarOverlayRenderEvents.java b/src/main/java/com/github/thedeathlycow/thermoo/api/client/StatusBarOverlayRenderEvents.java new file mode 100644 index 00000000..96267484 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/client/StatusBarOverlayRenderEvents.java @@ -0,0 +1,58 @@ +package com.github.thedeathlycow.thermoo.api.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.entity.player.PlayerEntity; +import org.joml.Vector2i; + +/** + * Event for rendering temperature overlays on status bar. + */ +@Environment(EnvType.CLIENT) +public class StatusBarOverlayRenderEvents { + + /** + * Invoked after the player health bar is drawn. Does not include information on the Absorption bar. + *

+ * Integrates with Colorful Hearts and Overflowing Bars for now - but this integration will be removed in the + * future. + *

+ * Custom heart types, like Frozen Hearts, should be handled separately. + */ + public static final Event AFTER_HEALTH_BAR = EventFactory.createArrayBacked( + RenderHealthBarCallback.class, + callbacks -> (context, player, heartPositions, displayHealth, maxDisplayHeath) -> { + for (RenderHealthBarCallback callback : callbacks) { + callback.render(context, player, heartPositions, displayHealth, maxDisplayHeath); + } + } + ); + + @FunctionalInterface + public interface RenderHealthBarCallback { + + /** + * Note that {@code displayHealth} and {@code maxDisplayHealth} are not always the same as health and max health. Mods that + * override the health bar rendering like Colorful Hearts may change these values. + * + * @param context DrawContext for the HUD + * @param player The player rendering hearts for + * @param heartPositions An array of heart positions on the HUD. Elements may be null, indicating that a heart + * should not be rendered at this index. + * @param displayHealth How many half hearts are to be displayed + * @param maxDisplayHealth The maximum number of half hearts to be displayed + */ + void render( + DrawContext context, + PlayerEntity player, + Vector2i[] heartPositions, + int displayHealth, + int maxDisplayHealth + ); + + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/client/package-info.java b/src/main/java/com/github/thedeathlycow/thermoo/api/client/package-info.java new file mode 100644 index 00000000..7853a85d --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/client/package-info.java @@ -0,0 +1,8 @@ +/** + * Relates to any client APIs provided by Thermoo + */ +@Environment(EnvType.CLIENT) +package com.github.thedeathlycow.thermoo.api.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; \ No newline at end of file diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/command/EnvironmentCommand.java b/src/main/java/com/github/thedeathlycow/thermoo/api/command/EnvironmentCommand.java index 8af72fd9..879a53f9 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/api/command/EnvironmentCommand.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/command/EnvironmentCommand.java @@ -1,11 +1,16 @@ package com.github.thedeathlycow.thermoo.api.command; import com.github.thedeathlycow.thermoo.api.temperature.EnvironmentManager; +import com.github.thedeathlycow.thermoo.api.util.TemperatureConverter; +import com.github.thedeathlycow.thermoo.api.util.TemperatureUnit; import com.github.thedeathlycow.thermoo.impl.Thermoo; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.command.argument.BlockPosArgumentType; import net.minecraft.command.argument.EntityArgumentType; +import net.minecraft.server.command.FillCommand; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.command.SetBlockCommand; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import org.jetbrains.annotations.Contract; @@ -64,6 +69,24 @@ private static LiteralArgumentBuilder buildCommand() { ); } ) + .then( + argument("unit", TemperatureUnitArgumentType.temperatureUnit()) + .executes( + context -> { + return executeCheckTemperature( + context.getSource(), + EntityArgumentType.getEntity( + context, + "target" + ).getBlockPos(), + TemperatureUnitArgumentType.getTemperatureUnit( + context, + "unit" + ) + ); + } + ) + ) ) .then( argument("location", BlockPosArgumentType.blockPos()) @@ -71,13 +94,31 @@ private static LiteralArgumentBuilder buildCommand() { context -> { return executeCheckTemperature( context.getSource(), - BlockPosArgumentType.getBlockPos( + BlockPosArgumentType.getLoadedBlockPos( context, "location" ) ); } ) + .then( + argument("unit", TemperatureUnitArgumentType.temperatureUnit()) + .executes( + context -> { + return executeCheckTemperature( + context.getSource(), + BlockPosArgumentType.getLoadedBlockPos( + context, + "location" + ), + TemperatureUnitArgumentType.getTemperatureUnit( + context, + "unit" + ) + ); + } + ) + ) ); return literal("thermoo").then( @@ -111,7 +152,7 @@ private static int executeCheckTemperature(ServerCommandSource source, BlockPos source.sendFeedback( () -> Text.translatableWithFallback( "commands.thermoo.environment.checktemperature.success", - "The passive temperature change at %s, %s, %s (%s) is %d", + "The passive temperature change at %s, %s, %s (%s) is %s", location.getX(), location.getY(), location.getZ(), @@ -123,4 +164,36 @@ private static int executeCheckTemperature(ServerCommandSource source, BlockPos return temperatureChange; } + + private static int executeCheckTemperature(ServerCommandSource source, BlockPos location, TemperatureUnit unit) { + + int temperatureTick = EnvironmentManager.INSTANCE.getController().getLocalTemperatureChange( + source.getWorld(), + location + ); + + + var biome = source.getWorld().getBiome(location).getKey().orElse(null); + + double temperature = TemperatureConverter.temperatureTickToAmbientTemperature( + temperatureTick, + new TemperatureConverter.Settings(unit, 1, 0) + ); + + source.sendFeedback( + () -> Text.translatableWithFallback( + "commands.thermoo.environment.checktemperature.unit.success", + "The temperature at %s, %s, %s (%s) is %s°%s", + location.getX(), + location.getY(), + location.getZ(), + biome == null ? "unknown" : biome.getValue(), + String.format("%.2f", temperature), + unit.getUnitSymbol() + ), + false + ); + + return (int) temperature; + } } diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/command/TemperatureUnitArgumentType.java b/src/main/java/com/github/thedeathlycow/thermoo/api/command/TemperatureUnitArgumentType.java new file mode 100644 index 00000000..0c8382bc --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/command/TemperatureUnitArgumentType.java @@ -0,0 +1,24 @@ +package com.github.thedeathlycow.thermoo.api.command; + +import com.github.thedeathlycow.thermoo.api.util.TemperatureUnit; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.serialization.Codec; +import net.minecraft.command.argument.EnumArgumentType; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.util.StringIdentifiable; + +public class TemperatureUnitArgumentType extends EnumArgumentType { + public static final Codec CODEC = StringIdentifiable.createCodec(TemperatureUnit::values); + + private TemperatureUnitArgumentType() { + super(CODEC, TemperatureUnit::values); + } + + public static TemperatureUnitArgumentType temperatureUnit() { + return new TemperatureUnitArgumentType(); + } + + public static TemperatureUnit getTemperatureUnit(CommandContext context, String id) { + return context.getArgument(id, TemperatureUnit.class); + } +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasonEvents.java b/src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasonEvents.java new file mode 100644 index 00000000..dd3f9cdf --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasonEvents.java @@ -0,0 +1,45 @@ +package com.github.thedeathlycow.thermoo.api.season; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.world.World; + +import java.util.Optional; + +/** + * Events related to Seasons mod integration in Thermoo. Thermoo does not add seasonal functionality by itself, seasons + * must be implemented by another mod like Fabric Seasons or Serene Seasons. This only provides the ability to query + * seasons if you want to use them. + */ +public class ThermooSeasonEvents { + + /** + * Retrieves the current season. This event just places season integration into + * a common source. + *

+ * If any listener returns a non-empty season, then all further processing is cancelled and that season is returned. + *

+ * Returns empty by default. + */ + public static final Event GET_CURRENT_SEASON = EventFactory.createArrayBacked( + CurrentSeasonCallback.class, + callbacks -> world -> { + for (CurrentSeasonCallback callback : callbacks) { + Optional season = callback.getCurrentSeason(world); + if (season.isPresent()) { + return season; + } + } + + return Optional.empty(); + } + ); + + @FunctionalInterface + public interface CurrentSeasonCallback { + + Optional getCurrentSeason(World world); + + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasons.java b/src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasons.java new file mode 100644 index 00000000..cfa6b985 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/season/ThermooSeasons.java @@ -0,0 +1,31 @@ +package com.github.thedeathlycow.thermoo.api.season; + +import net.minecraft.world.World; + +import java.util.Optional; + +/** + * Mod-agnostic Seasons enum. Thermoo does not provide seasons itself, but this can be used to better integrate with any + * mods that do provide seasons. + */ +public enum ThermooSeasons { + SPRING, + SUMMER, + AUTUMN, + WINTER; + + /** + * Shorthand for invoking {@link ThermooSeasonEvents#GET_CURRENT_SEASON}. + *

+ * Retrieves the current season, if a season mod is loaded. Thermoo does not add seasons by itself, seasons must be + * implemented by another mod like Fabric Seasons or Serene Seasons. This event just places season integration into + * a common source. + * + * @param world The current world / level to get the season from. + * @return Returns the current season if a Seasons mod is installed, or empty if no seasons mod is installed. + */ + public static Optional getCurrentSeason(World world) { + return ThermooSeasonEvents.GET_CURRENT_SEASON.invoker().getCurrentSeason(world); + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/season/package-info.java b/src/main/java/com/github/thedeathlycow/thermoo/api/season/package-info.java new file mode 100644 index 00000000..8d1e4cfd --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/season/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides a common source for integrations with seasons mods like Fabric Seasons and Serene Seasons + */ +package com.github.thedeathlycow.thermoo.api.season; \ No newline at end of file diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/SequenceTemperatureEffect.java b/src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/SequenceTemperatureEffect.java new file mode 100644 index 00000000..f428e471 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/SequenceTemperatureEffect.java @@ -0,0 +1,54 @@ +package com.github.thedeathlycow.thermoo.api.temperature.effects; + +import com.google.gson.*; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.server.world.ServerWorld; + +import java.util.ArrayList; +import java.util.List; + +/** + * Applies multiple child temperature effects at once. Useful for when you want to apply several different temperature + * effects under the same base set of conditions, without the overhead of checking those conditions multiple times. + */ +public class SequenceTemperatureEffect extends TemperatureEffect { + + @Override + public void apply(LivingEntity victim, ServerWorld serverWorld, Config config) { + for (ConfiguredTemperatureEffect child : config.children()) { + EntityType childType = child.getEntityType(); + if (childType == null || victim.getType() == childType) { + child.applyIfPossible(victim); + } + } + } + + @Override + public boolean shouldApply(LivingEntity victim, Config config) { + return true; + } + + @Override + public Config configFromJson(JsonElement json, JsonDeserializationContext context) throws JsonSyntaxException { + return Config.fromJson(json, context); + } + + public record Config(List> children) { + + public static Config fromJson(JsonElement json, JsonDeserializationContext context) throws JsonSyntaxException { + JsonObject object = json.getAsJsonObject(); + + JsonArray jsonChildren = object.get("children").getAsJsonArray(); + List> children = new ArrayList<>(jsonChildren.size()); + for (JsonElement jsonChild : jsonChildren) { + ConfiguredTemperatureEffect child = context.deserialize(jsonChild, ConfiguredTemperatureEffect.class); + children.add(child); + } + + return new Config(children); + } + + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/TemperatureEffects.java b/src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/TemperatureEffects.java index 755238f6..8c6ce1f8 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/TemperatureEffects.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/temperature/effects/TemperatureEffects.java @@ -20,14 +20,20 @@ public final class TemperatureEffects { public static final TemperatureEffect EMPTY = new EmptyTemperatureEffect(); /** - * Applies {@link net.minecraft.entity.effect.StatusEffect}s to entities based on their temperature + * A meta temperature effect that allows multiple child effects to be applied under the same conditions. */ - public static final TemperatureEffect STATUS_EFFECT = new StatusEffectTemperatureEffect(); + public static final TemperatureEffect SEQUENCE = new SequenceTemperatureEffect(); /** - * Applies scaled {@link net.minecraft.entity.attribute.EntityAttributeModifier}s to entities based on their + * Applies {@linkplain net.minecraft.entity.effect.StatusEffect status effects} to entities based on their * temperature */ + public static final TemperatureEffect STATUS_EFFECT = new StatusEffectTemperatureEffect(); + + /** + * Applies scaled {@linkplain net.minecraft.entity.attribute.EntityAttributeModifier attribute modifiers} to + * entities based on their temperature + */ public static final TemperatureEffect SCALING_ATTRIBUTE_MODIFIER = new ScalingAttributeModifierTemperatureEffect(); /** @@ -49,6 +55,7 @@ public final class TemperatureEffects { /** * Returns all currently loaded {@link ConfiguredTemperatureEffect}s that are mapped to the {@code entity}'s type. + * * @param entity The entity to fetch the effects for * @return Returns the effects loaded for the entity type */ diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureConverter.java b/src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureConverter.java new file mode 100644 index 00000000..72a6f897 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureConverter.java @@ -0,0 +1,106 @@ +package com.github.thedeathlycow.thermoo.api.util; + +import net.minecraft.util.math.MathHelper; + +/** + * Helper API for conversions between normal temperature units (like Celsius and Fahrenheit) to per-tick temperature + * point changes. + */ +public class TemperatureConverter { + + /** + * Converts an ambient Celsius temperature value to a per tick Thermoo passive temperature point change. + *

+ * The result is based on a linear scale. For example: + *

+ * 5C - 14C => -1 temp/tick + *

+ * 15C - 24C => 0 temp/tick + *

+ * 25C - 34C => +1 temp/tick + *

+ * etc + * + * @param temperatureValue The input temperature value, in Celsius + * @return An equivalent per-tick temperature point change + */ + public static int celsiusToTemperatureTick(double temperatureValue) { + return ambientTemperatureToTemperatureTick(temperatureValue, Settings.DEFAULT); + } + + /** + * Converts an ambient temperature value in Celsius, Kelvin, Fahrenheit or Rankine to a per tick Thermoo temperature + * point change. Using the {@linkplain Settings#DEFAULT default settings}, this converts the temperature value to a + * temperature point change following a linear scale where 15C-24C equates to 0 temp/tick, and then every +10C adds + * +1 temp/tick (and vice versa). + *

+ * With the settings, the scale the effect of changes in ambient temperature. For example a scale of 2 will mean that + * every +5C adds +1 temp/tick. The base shift effects where the 0 base is. For example, a base shift of +5 means that + * the 20C - 29C will be 0 temp/tick. + *

+ * You can also change the unit to other measurements like Fahrenheit or Kelvin, but the scale is still based in Celsius. + * So for example with default settings 59F to 75.2F equates to 0 temp/tick, and increases of 18F will add +1 temp/tick. + * + * @param temperatureValue The ambient temperature, in Celsius. + * @param settings Allows you to adjust the unit, scale, and base of the temperature conversion. + * @see #temperatureTickToAmbientTemperature(int, Settings) + */ + public static int ambientTemperatureToTemperatureTick(double temperatureValue, Settings settings) { + double celsiusTemperature = settings.unit.toCelsius(temperatureValue); + + return MathHelper.floor(settings.scale / 10.0 * (celsiusTemperature - (15.0 + settings.baseShift))); + } + + /** + * Converts a per-tick thermoo temperature point change to an ambient temperature in Celsius. + *

+ * Performs the inverse calculation of {@link #celsiusToTemperatureTick(double)}, but returns the median of the input + * temperature range. + *

+ * So for example: + *

+ * -1 temp/tick => 10C + *

+ * 0 temp/tick => 20C, + *

+ * +1 temp/tick => 30C. + * + * @param temperatureTick The thermoo temperature point change per tick value + * @return Returns the ambient temperature in Celsius for the given temperature point change. + * @see #ambientTemperatureToTemperatureTick(double, Settings) + */ + public static double temperatureTickToCelsius(int temperatureTick) { + return temperatureTickToAmbientTemperature(temperatureTick, Settings.DEFAULT); + } + + + /** + * Converts a per-tick thermoo temperature point change to an ambient temperature in Celsius. + *

+ * Performs the inverse calculation of {@link #ambientTemperatureToTemperatureTick(double, Settings)}, but returns + * the median of the range. + *

+ * So for example, 0 temp/tick => 20C, -1 temp/tick => 10C, and +1 temp/tick => 30C. + * + * @param temperatureTick The thermoo temperature point change per tick value + * @param settings Allows you to adjust the unit, scale, and base of the temperature conversion. + * @return Returns the ambient temperature in Celsius for the given temperature point change. + * @see #ambientTemperatureToTemperatureTick(double, Settings) + */ + public static double temperatureTickToAmbientTemperature(int temperatureTick, Settings settings) { + double celsiusTemperature = (10 * temperatureTick) / settings.scale + 20.0 + settings.baseShift; + return settings.unit.fromCelsius(celsiusTemperature); + } + + public record Settings( + TemperatureUnit unit, + double scale, + double baseShift + ) { + public static final Settings DEFAULT = new Settings(TemperatureUnit.CELSIUS, 1.0, 0); + } + + private TemperatureConverter() { + + } +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureUnit.java b/src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureUnit.java new file mode 100644 index 00000000..00079a9a --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/api/util/TemperatureUnit.java @@ -0,0 +1,88 @@ +package com.github.thedeathlycow.thermoo.api.util; + +import net.minecraft.util.StringIdentifiable; + +import java.util.function.DoubleUnaryOperator; + +/** + * Defines the basic units of temperature and allows for conversions between them. + */ +public enum TemperatureUnit implements StringIdentifiable { + + CELSIUS( + "C", + celsiusValue -> celsiusValue, + celsiusValue -> celsiusValue + ), + KELVIN( + "K", + kelvinValue -> kelvinValue - 273.15, + celsiusValue -> celsiusValue + 273.15 + ), + FAHRENHEIT( + "F", + fahrenheitValue -> (fahrenheitValue - 32.0) * 5.0 / 9.0, + celsiusValue -> (9.0 / 5.0) * celsiusValue + 32.0 + ), + RANKINE( + "R", + rankineValue -> FAHRENHEIT.toCelsius(rankineValue - 459.67), + celsiusValue -> FAHRENHEIT.fromCelsius(celsiusValue) + 459.67 + ); + + private final String unitSymbol; + + private final DoubleUnaryOperator toCelsius; + + private final DoubleUnaryOperator fromCelsius; + + TemperatureUnit(String unitSymbol, DoubleUnaryOperator toCelsius, DoubleUnaryOperator fromCelsius) { + this.unitSymbol = unitSymbol; + this.toCelsius = toCelsius; + this.fromCelsius = fromCelsius; + } + + public String getUnitSymbol() { + return this.unitSymbol; + } + + /** + * Converts a temperature value in this unit to Celsius. + * + * @param value A temperature value in this unit + * @return Returns the equivalent temperature value in Celsius + */ + public double toCelsius(double value) { + return this.toCelsius.applyAsDouble(value); + } + + /** + * Converts a temperature value in Celsius to this unit. + * + * @param celsiusValue A temperature value in Celsius + * @return Returns the equivalent temperature value in this unit + */ + public double fromCelsius(double celsiusValue) { + return this.fromCelsius.applyAsDouble(celsiusValue); + } + + /** + * Converts a temperature in some other unit to this unit. + * + * @param inputValue The input temperature value in the other unit + * @param inputUnit The other unit + * @return Returns the equivalent temperature value in this unit. + */ + public double convertTemperature(double inputValue, TemperatureUnit inputUnit) { + if (this == inputUnit) { + return inputValue; + } + double inputCelsius = inputUnit.toCelsius(inputValue); + return this.fromCelsius(inputCelsius); + } + + @Override + public String asString() { + return this.toString().toLowerCase(); + } +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/impl/ItemAttributeModifierManager.java b/src/main/java/com/github/thedeathlycow/thermoo/impl/ItemAttributeModifierManager.java new file mode 100644 index 00000000..7b5cfb94 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/impl/ItemAttributeModifierManager.java @@ -0,0 +1,85 @@ +package com.github.thedeathlycow.thermoo.impl; + +import com.github.thedeathlycow.thermoo.api.ThermooRegistryKeys; +import com.github.thedeathlycow.thermoo.api.attribute.ItemAttributeModifier; +import com.google.common.collect.Multimap; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.registry.DynamicRegistryView; +import net.fabricmc.fabric.api.item.v1.ModifyItemAttributeModifiersCallback; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.attribute.EntityAttribute; +import net.minecraft.entity.attribute.EntityAttributeModifier; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.Registry; +import org.jetbrains.annotations.Nullable; + +/** + * Applies {@link com.github.thedeathlycow.thermoo.api.attribute.ItemAttributeModifier}s to the default attributes of + * item stacks + */ +public class ItemAttributeModifierManager implements ModifyItemAttributeModifiersCallback { + + public static final ItemAttributeModifierManager INSTANCE = new ItemAttributeModifierManager(); + + @Nullable + private DynamicRegistryManager manager; + + public void registerToEventsCommon() { + ServerLifecycleEvents.SERVER_STARTING.register(server -> { + this.manager = server.getRegistryManager(); + }); + + ServerLifecycleEvents.SERVER_STARTED.register(server -> { + if (this.manager == null) { + return; + } + + Registry registry = this.manager.get(ThermooRegistryKeys.ITEM_ATTRIBUTE_MODIFIER); + Thermoo.LOGGER.info("Loaded {} items attribute modifier(s)", registry != null ? registry.size() : 0); + }); + + ServerLifecycleEvents.SERVER_STOPPING.register(server -> { + this.manager = null; + }); + ModifyItemAttributeModifiersCallback.EVENT.register(this); + } + + public void registerToEventsClient() { + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { + if (this.manager == null) { + this.manager = handler.getRegistryManager(); + } + }); + ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> { + this.manager = null; + }); + } + + @Override + public void modifyAttributeModifiers( + ItemStack stack, + EquipmentSlot slot, + Multimap attributeModifiers + ) { + if (manager != null) { + manager.getOptional(ThermooRegistryKeys.ITEM_ATTRIBUTE_MODIFIER) + .ifPresent( + registry -> { + registry.streamEntries().forEach( + entry -> { + entry.value().apply(stack, slot, attributeModifiers); + } + ); + } + ); + } else { + Thermoo.LOGGER.info("Manager is null"); + } + } + + private ItemAttributeModifierManager() { + + } +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/impl/Thermoo.java b/src/main/java/com/github/thedeathlycow/thermoo/impl/Thermoo.java index e853ecda..7845cef7 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/impl/Thermoo.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/impl/Thermoo.java @@ -1,13 +1,22 @@ package com.github.thedeathlycow.thermoo.impl; +import com.github.thedeathlycow.thermoo.api.ThermooRegistryKeys; +import com.github.thedeathlycow.thermoo.api.attribute.ItemAttributeModifier; import com.github.thedeathlycow.thermoo.api.command.EnvironmentCommand; import com.github.thedeathlycow.thermoo.api.command.HeatingModeArgumentType; import com.github.thedeathlycow.thermoo.api.command.TemperatureCommand; +import com.github.thedeathlycow.thermoo.api.command.TemperatureUnitArgumentType; +import com.github.thedeathlycow.thermoo.api.season.ThermooSeasonEvents; +import com.github.thedeathlycow.thermoo.api.season.ThermooSeasons; import com.github.thedeathlycow.thermoo.api.temperature.EnvironmentManager; +import io.github.lucaargolo.seasons.FabricSeasons; +import io.github.lucaargolo.seasons.utils.Season; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.registry.DynamicRegistries; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.command.argument.serialize.ConstantArgumentSerializer; import net.minecraft.resource.ResourceType; import net.minecraft.util.Identifier; @@ -15,21 +24,25 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Optional; + public class Thermoo implements ModInitializer { public static final String MODID = "thermoo"; public static final Logger LOGGER = LoggerFactory.getLogger(MODID); - public static final int CONFIG_VERSION = 0; - @Override public void onInitialize() { - ArgumentTypeRegistry.registerArgumentType( Thermoo.id("heating_mode"), HeatingModeArgumentType.class, ConstantArgumentSerializer.of(HeatingModeArgumentType::heatingMode) ); + ArgumentTypeRegistry.registerArgumentType( + Thermoo.id("temperature_unit"), + TemperatureUnitArgumentType.class, + ConstantArgumentSerializer.of(TemperatureUnitArgumentType::temperatureUnit) + ); CommandRegistrationCallback.EVENT.register( (dispatcher, registryAccess, environment) -> { @@ -38,16 +51,44 @@ public void onInitialize() { } ); + DynamicRegistries.registerSynced( + ThermooRegistryKeys.ITEM_ATTRIBUTE_MODIFIER, + ItemAttributeModifier.CODEC, + DynamicRegistries.SyncOption.SKIP_WHEN_EMPTY + ); + + ItemAttributeModifierManager.INSTANCE.registerToEventsCommon(); + ThermooCommonRegisters.registerAttributes(); ThermooCommonRegisters.registerTemperatureEffects(); ResourceManagerHelper serverManager = ResourceManagerHelper.get(ResourceType.SERVER_DATA); serverManager.registerReloadListener(TemperatureEffectLoader.INSTANCE); + registerFabricSeasonsIntegration(); LOGGER.info("Creating environment manager {}", EnvironmentManager.INSTANCE); LOGGER.info("Thermoo initialized"); } + private static void registerFabricSeasonsIntegration() { + if (FabricLoader.getInstance().isModLoaded(ThermooIntegrations.FABRIC_SEASONS_ID)) { + LOGGER.warn("Registering builtin Fabric Seasons integration with Thermoo. " + + "Note that this integration will be removed as a builtin feature in the future."); + ThermooSeasonEvents.GET_CURRENT_SEASON.register(world -> { + Season fabricSeason = FabricSeasons.getCurrentSeason(world); + return Optional.ofNullable( + switch (fabricSeason) { + case WINTER -> ThermooSeasons.WINTER; + case SUMMER -> ThermooSeasons.SUMMER; + case FALL -> ThermooSeasons.AUTUMN; + case SPRING -> ThermooSeasons.SPRING; + default -> null; + } + ); + }); + } + } + /** * Creates a new {@link Identifier} under the namespace {@value #MODID} * diff --git a/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooClient.java b/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooClient.java new file mode 100644 index 00000000..88de8d40 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooClient.java @@ -0,0 +1,12 @@ +package com.github.thedeathlycow.thermoo.impl; + +import net.fabricmc.api.ClientModInitializer; + +public class ThermooClient implements ClientModInitializer { + + + @Override + public void onInitializeClient() { + ItemAttributeModifierManager.INSTANCE.registerToEventsClient(); + } +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooCommonRegisters.java b/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooCommonRegisters.java index 0fb6d9fe..2eca7727 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooCommonRegisters.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooCommonRegisters.java @@ -13,6 +13,7 @@ public class ThermooCommonRegisters { @SuppressWarnings("deprecation") public static void registerTemperatureEffects() { registerTemperatureEffect("empty", TemperatureEffects.EMPTY); + registerTemperatureEffect("sequence", TemperatureEffects.SEQUENCE); registerTemperatureEffect("status_effect", TemperatureEffects.STATUS_EFFECT); registerTemperatureEffect("scaling_attribute_modifier", TemperatureEffects.SCALING_ATTRIBUTE_MODIFIER); registerTemperatureEffect("damage", TemperatureEffects.DAMAGE); diff --git a/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooIntegrations.java b/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooIntegrations.java new file mode 100644 index 00000000..12babe39 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/impl/ThermooIntegrations.java @@ -0,0 +1,18 @@ +package com.github.thedeathlycow.thermoo.impl; + +import net.fabricmc.loader.api.FabricLoader; + +public class ThermooIntegrations { + + public static final String COLORFUL_HEARTS_ID = "colorfulhearts"; + + public static final String OVERFLOWING_BARS_ID = "overflowingbars"; + + public static final String FABRIC_SEASONS_ID = "seasons"; + + public static boolean isModLoaded(String id) { + return FabricLoader.getInstance().isModLoaded(id); + } + + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/impl/client/HeartOverlayImpl.java b/src/main/java/com/github/thedeathlycow/thermoo/impl/client/HeartOverlayImpl.java new file mode 100644 index 00000000..cfc3c409 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/impl/client/HeartOverlayImpl.java @@ -0,0 +1,33 @@ +package com.github.thedeathlycow.thermoo.impl.client; + +import net.minecraft.util.Util; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector2i; + +import java.util.Arrays; + +public class HeartOverlayImpl { + + public static final HeartOverlayImpl INSTANCE = new HeartOverlayImpl(); + + private static final int MAX_OVERLAY_HEARTS = 20; + + private final Vector2i[] heartPositions = Util.make(() -> { + var positions = new Vector2i[MAX_OVERLAY_HEARTS]; + Arrays.fill(positions, null); + return positions; + }); + + public void setHeartPosition(int index, int heartX, int heartY) { + heartPositions[index] = new Vector2i(heartX, heartY); + } + + public Vector2i[] getHeartPositions() { + return heartPositions; + } + + private HeartOverlayImpl() { + + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/Plugin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/Plugin.java new file mode 100644 index 00000000..47b5db13 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/Plugin.java @@ -0,0 +1,65 @@ +package com.github.thedeathlycow.thermoo.mixin.client.compat; + +import net.fabricmc.loader.api.FabricLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class Plugin implements IMixinConfigPlugin { + private static final String COMPAT_PACKAGE_ROOT; + private static final String COMPAT_PRESENT_KEY = "present"; + private static final FabricLoader LOADER = FabricLoader.getInstance(); + + static { + // Shorthand getting the plugin package to ensure not making trouble with other mixins + COMPAT_PACKAGE_ROOT = Plugin.class.getPackageName(); + } + + @Override + public void onLoad(String mixinPackage) { + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + if (!mixinClassName.startsWith(COMPAT_PACKAGE_ROOT)) { + return true; // We do not meddle with the others' work + } + String[] compatRoot = COMPAT_PACKAGE_ROOT.split("\\."); + String[] mixinPath = mixinClassName.split("\\."); + // The id of the mod the mixin depends on + String compatModId = mixinPath[compatRoot.length]; + // Whether the mixin is for when the mod is loaded or not + boolean isPresentMixin = mixinPath[compatRoot.length + 1].equals(COMPAT_PRESENT_KEY); + + if (isPresentMixin) { + // We only load the mixin if the mod we want to be present is found + return LOADER.isModLoaded(compatModId); + } + return !LOADER.isModLoaded(compatModId); + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/colorfulhearts/present/HeartRendererMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/colorfulhearts/present/HeartRendererMixin.java new file mode 100644 index 00000000..8a1b079f --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/colorfulhearts/present/HeartRendererMixin.java @@ -0,0 +1,87 @@ +package com.github.thedeathlycow.thermoo.mixin.client.compat.colorfulhearts.present; + +import com.github.thedeathlycow.thermoo.api.client.StatusBarOverlayRenderEvents; +import com.github.thedeathlycow.thermoo.impl.ThermooIntegrations; +import com.github.thedeathlycow.thermoo.impl.client.HeartOverlayImpl; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.entity.player.PlayerEntity; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import terrails.colorfulhearts.heart.Heart; +import terrails.colorfulhearts.heart.HeartType; +import terrails.colorfulhearts.render.HeartRenderer; + +import java.util.Arrays; + +@Environment(EnvType.CLIENT) +@Mixin(value = HeartRenderer.class, remap = false) +public class HeartRendererMixin { + + @Inject(method = "renderPlayerHearts", + at = @At( + value = "INVOKE", + target = "Lterrails/colorfulhearts/heart/Heart;draw(Lnet/minecraft/client/util/math/MatrixStack;IIZZLterrails/colorfulhearts/heart/HeartType;)V", + remap = true, + shift = At.Shift.AFTER + ), + locals = LocalCapture.CAPTURE_FAILEXCEPTION + ) + private void captureHeartPositions( + DrawContext guiGraphics, + PlayerEntity player, + int x, int y, + int maxHealth, int currentHealth, + int displayHealth, int absorption, + boolean renderHighlight, + CallbackInfo ci, + int healthHearts, int displayHealthHearts, + boolean absorptionSameRow, + int regenIndex, + HeartType heartType, + int index, + Heart heart, + int xPos, int yPos, + boolean highlightHeart + ) { + if (ThermooIntegrations.isModLoaded(ThermooIntegrations.OVERFLOWING_BARS_ID)) { + return; + } + HeartOverlayImpl.INSTANCE.setHeartPosition(index, xPos, yPos); + } + + @Inject( + method = "renderPlayerHearts", + at = @At( + value = "INVOKE", + target = "Lcom/mojang/blaze3d/systems/RenderSystem;disableBlend()V", + shift = At.Shift.BEFORE + ) + ) + private void drawColdHeartOverlayBar( + DrawContext drawContext, + PlayerEntity player, + int x, int y, + int maxHealth, int currentHealth, int displayHealth, int absorption, + boolean renderHighlight, + CallbackInfo ci + ) { + if (ThermooIntegrations.isModLoaded(ThermooIntegrations.OVERFLOWING_BARS_ID)) { + return; + } + StatusBarOverlayRenderEvents.AFTER_HEALTH_BAR.invoker() + .render( + drawContext, + player, + HeartOverlayImpl.INSTANCE.getHeartPositions(), + displayHealth, + 20 + ); + Arrays.fill(HeartOverlayImpl.INSTANCE.getHeartPositions(), null); + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/absent/InGameHudMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/absent/InGameHudMixin.java new file mode 100644 index 00000000..4e5da449 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/absent/InGameHudMixin.java @@ -0,0 +1,87 @@ +package com.github.thedeathlycow.thermoo.mixin.client.compat.overflowingbars.absent; + +import com.github.thedeathlycow.thermoo.api.client.StatusBarOverlayRenderEvents; +import com.github.thedeathlycow.thermoo.impl.client.HeartOverlayImpl; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.MathHelper; +import org.joml.Vector2i; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.Arrays; + +@Mixin(InGameHud.class) +public class InGameHudMixin { + @Inject( + method = "renderHealthBar", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/hud/InGameHud;drawHeart(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/gui/hud/InGameHud$HeartType;IIIZZ)V", + ordinal = 0, + shift = At.Shift.AFTER + ), + locals = LocalCapture.CAPTURE_FAILEXCEPTION + ) + private void captureHeartPositions( + DrawContext context, + PlayerEntity player, + int x, int y, + int lines, + int regeneratingHeartIndex, + float maxHealth, + int lastHealth, + int health, + int absorption, + boolean blinking, + CallbackInfo ci, + // local captures + InGameHud.HeartType heartType, + int i, int j, int k, int l, + int m, // index of heart + int n, int o, + int p, int q // position of heart to capture + ) { + HeartOverlayImpl.INSTANCE.setHeartPosition(m, p, q); + } + + @Inject( + method = "renderHealthBar", + at = @At( + value = "TAIL" + ) + ) + private void drawHeartOverlayBar( + DrawContext context, + PlayerEntity player, + int x, int y, + int lines, + int regeneratingHeartIndex, + float maxHealth, + int lastHealth, + int health, + int absorption, + boolean blinking, + CallbackInfo ci + ) { + + Vector2i[] heartPositions = HeartOverlayImpl.INSTANCE.getHeartPositions(); + int displayHealth = Math.min(health, heartPositions.length); + int maxDisplayHealth = Math.min(MathHelper.ceil(maxHealth), heartPositions.length); + + StatusBarOverlayRenderEvents.AFTER_HEALTH_BAR.invoker() + .render( + context, + player, + heartPositions, + displayHealth, + maxDisplayHealth + ); + Arrays.fill(HeartOverlayImpl.INSTANCE.getHeartPositions(), null); + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/present/HealthBarRendererMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/present/HealthBarRendererMixin.java new file mode 100644 index 00000000..5d4aa081 --- /dev/null +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/client/compat/overflowingbars/present/HealthBarRendererMixin.java @@ -0,0 +1,86 @@ +package com.github.thedeathlycow.thermoo.mixin.client.compat.overflowingbars.present; + +import com.github.thedeathlycow.thermoo.api.client.StatusBarOverlayRenderEvents; +import com.github.thedeathlycow.thermoo.impl.client.HeartOverlayImpl; +import fuzs.overflowingbars.client.handler.HealthBarRenderer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.entity.player.PlayerEntity; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.Arrays; + +@Environment(EnvType.CLIENT) +@Mixin(value = HealthBarRenderer.class, remap = false) +public class HealthBarRendererMixin { + + @Inject( + method = "renderHearts", + at = @At( + value = "INVOKE", + target = "Lfuzs/overflowingbars/client/handler/HealthBarRenderer;renderHeart(Lnet/minecraft/client/gui/DrawContext;Lfuzs/overflowingbars/client/handler/HealthBarRenderer$HeartType;IIZZZ)V", + ordinal = 0, + shift = At.Shift.AFTER, + remap = true + ), + locals = LocalCapture.CAPTURE_FAILEXCEPTION + ) + private void captureHeartPosition( + DrawContext guiGraphics, + PlayerEntity player, + int posX, + int posY, + int heartOffsetByRegen, + float maxHealth, + int currentHealth, + int displayHealth, + int currentAbsorptionHealth, + boolean blink, + CallbackInfo ci, + boolean hardcore, + int normalHearts, + int maxAbsorptionHearts, + int absorptionHearts, + int currentHeart, + int currentPosX, + int currentPosY + ) { + HeartOverlayImpl.INSTANCE.setHeartPosition(currentHeart, currentPosX, currentPosY); + } + + @Inject( + method = "renderHearts", + at = @At( + value = "TAIL" + ) + ) + private void renderColdHeartOverlayBar( + DrawContext guiGraphics, + PlayerEntity player, + int posX, + int posY, + int heartOffsetByRegen, + float maxHealth, + int currentHealth, + int displayHealth, + int currentAbsorptionHealth, + boolean blink, + CallbackInfo ci + ) { + StatusBarOverlayRenderEvents.AFTER_HEALTH_BAR.invoker() + .render( + guiGraphics, + player, + HeartOverlayImpl.INSTANCE.getHeartPositions(), + displayHealth, + 20 + ); + Arrays.fill(HeartOverlayImpl.INSTANCE.getHeartPositions(), null); + } + +} diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/EntityPredicateMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/EntityPredicateMixin.java similarity index 95% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/EntityPredicateMixin.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/EntityPredicateMixin.java index ecddf041..a7e3a6ae 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/EntityPredicateMixin.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/EntityPredicateMixin.java @@ -1,4 +1,4 @@ -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import com.github.thedeathlycow.thermoo.api.predicate.TemperaturePredicate; import com.github.thedeathlycow.thermoo.api.temperature.TemperatureAware; @@ -10,6 +10,7 @@ import net.minecraft.util.JsonHelper; import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -18,6 +19,7 @@ @Mixin(EntityPredicate.class) public class EntityPredicateMixin { + @Unique private TemperaturePredicate thermoo$temperaturePredicate = TemperaturePredicate.ANY; @Inject( diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/EnvironmentAwareEntityMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/EnvironmentAwareEntityMixin.java similarity index 99% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/EnvironmentAwareEntityMixin.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/EnvironmentAwareEntityMixin.java index 01023489..e2876415 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/EnvironmentAwareEntityMixin.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/EnvironmentAwareEntityMixin.java @@ -1,4 +1,4 @@ -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import com.github.thedeathlycow.thermoo.api.ThermooAttributes; import com.github.thedeathlycow.thermoo.api.ThermooTags; diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/HotFloorMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/HotFloorMixin.java similarity index 96% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/HotFloorMixin.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/HotFloorMixin.java index 36d62f71..b7ab5ff3 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/HotFloorMixin.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/HotFloorMixin.java @@ -1,4 +1,4 @@ -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import com.github.thedeathlycow.thermoo.api.temperature.EnvironmentManager; import com.github.thedeathlycow.thermoo.api.temperature.HeatingModes; diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/LivingEntityAttributeMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/LivingEntityAttributeMixin.java similarity index 97% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/LivingEntityAttributeMixin.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/LivingEntityAttributeMixin.java index f99791fc..ccd3e0e3 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/LivingEntityAttributeMixin.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/LivingEntityAttributeMixin.java @@ -1,4 +1,4 @@ -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import com.github.thedeathlycow.thermoo.api.ThermooAttributes; import com.github.thedeathlycow.thermoo.api.temperature.EnvironmentController; diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/LivingEntityEnvironmentTickMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/LivingEntityEnvironmentTickMixin.java similarity index 95% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/LivingEntityEnvironmentTickMixin.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/LivingEntityEnvironmentTickMixin.java index 6e0e3ebe..f8d0771f 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/LivingEntityEnvironmentTickMixin.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/LivingEntityEnvironmentTickMixin.java @@ -1,4 +1,4 @@ -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import com.github.thedeathlycow.thermoo.impl.LivingEntityEnvironmentTickImpl; import net.minecraft.entity.LivingEntity; diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/PlayerTemperatureTickMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/PlayerTemperatureTickMixin.java similarity index 97% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/PlayerTemperatureTickMixin.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/PlayerTemperatureTickMixin.java index 7bf08387..863529c0 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/PlayerTemperatureTickMixin.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/PlayerTemperatureTickMixin.java @@ -1,4 +1,4 @@ -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import com.github.thedeathlycow.thermoo.api.temperature.EnvironmentManager; import com.github.thedeathlycow.thermoo.api.temperature.HeatingModes; diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/TemperatureEffectTickerMixin.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/TemperatureEffectTickerMixin.java similarity index 96% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/TemperatureEffectTickerMixin.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/TemperatureEffectTickerMixin.java index 2bb54d5d..eb7eee12 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/TemperatureEffectTickerMixin.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/TemperatureEffectTickerMixin.java @@ -1,4 +1,4 @@ -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import com.github.thedeathlycow.thermoo.api.temperature.effects.TemperatureEffects; import net.minecraft.entity.Entity; diff --git a/src/main/java/com/github/thedeathlycow/thermoo/mixin/package-info.java b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/package-info.java similarity index 73% rename from src/main/java/com/github/thedeathlycow/thermoo/mixin/package-info.java rename to src/main/java/com/github/thedeathlycow/thermoo/mixin/common/package-info.java index 36a6ebac..c7228224 100644 --- a/src/main/java/com/github/thedeathlycow/thermoo/mixin/package-info.java +++ b/src/main/java/com/github/thedeathlycow/thermoo/mixin/common/package-info.java @@ -2,6 +2,6 @@ * Mixin package for Thermoo. Code here should not be relied on by API users. */ @ApiStatus.Internal -package com.github.thedeathlycow.thermoo.mixin; +package com.github.thedeathlycow.thermoo.mixin.common; import org.jetbrains.annotations.ApiStatus; diff --git a/src/main/resources/assets/thermoo/icon.png b/src/main/resources/assets/thermoo/icon.png index 8febce05874f27356d18832cdab4da2fde92e1f4..0db142b5843e64b125cd29ff2116eeb1a77bde19 100644 GIT binary patch literal 3028 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4YzZe*}XM4IhhE&XXd(%F9?sM6O zhl_u4+)gN1eq(|j+hv85M<{)g)M_EvXnqN3l(>7+3@oYI#uXZXUw zz`!BIz#yQ)z@Xs4z~C@}fuTWxk%7Sx=(Z*n1_l;RiWEHKtF?T4|90HUuQ~T>cd*14 z7FpE0?WlisXM*MS`~P0pOrIc5k@-Zs5bSH2KOf)Rzdf(+MD^uu?0_jzFb|Hbblea>=IoQUu`43Dp<)lXOd5|g>#Jo(!}?}Yka z{x|kmKPyk0`sV+;?a5)E|E)dIH%}*yHhB>_7)|6iKH0i@`dwX)+f^Hk|2~rM`8hZ8 zjU3NR=3qJm&4QiI@qY8||0bR&zq_;Oiqb4`AwVkpL{-IcU9#lN%wa#J1tVqY2B>ave{^L<=^?+ouBpm;H@xz@m@Zxl}=^tEbEdN zzrOq1^G5!*W=@-P&sxTSHLRTumY)sJJh}Wk-}DFCb#mQzn-z<%t&5X0YJV(Q`Y-?5 z^|?GQm5fxa-H_u%1u4@m*n0Hc!9xf2=L56Y>*xav-FKO9*Z6$*pF6we*xB=Tvw+pf z&&$8Jx2<{cacl3?o0fD)Ho!PZ5WQnxns-7gukPE#-xB2a3cj5=d3#Pn;B))xZ%L_UpJ(UK=b2etd}7MgPk(-X|Ee3| zMTfEw6muExE=<2{eLjxo=ep_Z_Pwk<8 z8wouX0o`)@-1_zWk^f)rJv{a1bHVCuUcXIGJ4 nw>Gmkh|xaxteAN=?LTYBg%gQ<8RzYRt#t-ZS3j3^P6`!fTk}3j!F)#wJ-DuM5z@k-0BXL@>f|>HX(K8-`6ShJnJo+(a1) zQUg*;&_JGz-@yvey@CvOy!pf+zYlv7#+y@cfw!N(!YwDtK$t>ot^|o;^C}Errb8rv zEyh7&M;LGi2U%Xm2k;sQ3Vd`xCKT_+dr{3=s4($N*Rp0kN^g pEJ+5UyO=0.14.19", - "fabric-api": "*", - "minecraft": ">=1.20", + "fabricloader": ">=0.15.7", + "fabric-api": ">=0.91.0", + "minecraft": "~1.20.1", "java": ">=17" }, "suggests": { - "frostiful": "*" + "frostiful": "*", + "scorchful": "*" }, + "accessWidener": "thermoo.accesswidener", "custom": { "loom:injected_interfaces": { "net/minecraft/class_1309": [ diff --git a/src/main/resources/thermoo.accesswidener b/src/main/resources/thermoo.accesswidener new file mode 100644 index 00000000..d9a4ac53 --- /dev/null +++ b/src/main/resources/thermoo.accesswidener @@ -0,0 +1,5 @@ +accessWidener v1 named +### Classes ### + +# Heart Type needed to mixin to render health bar +accessible class net/minecraft/client/gui/hud/InGameHud$HeartType \ No newline at end of file diff --git a/src/main/resources/thermoo.client.mixins.json b/src/main/resources/thermoo.client.mixins.json new file mode 100644 index 00000000..d7969c8f --- /dev/null +++ b/src/main/resources/thermoo.client.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "com.github.thedeathlycow.thermoo.mixin.client", + "compatibilityLevel": "JAVA_17", + "plugin": "com.github.thedeathlycow.thermoo.mixin.client.compat.Plugin", + "client": [ + "compat.colorfulhearts.present.HeartRendererMixin", + "compat.overflowingbars.absent.InGameHudMixin", + "compat.overflowingbars.present.HealthBarRendererMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/src/main/resources/thermoo.mixins.json b/src/main/resources/thermoo.mixins.json index bc638167..861c8623 100644 --- a/src/main/resources/thermoo.mixins.json +++ b/src/main/resources/thermoo.mixins.json @@ -1,7 +1,7 @@ { "required": true, "minVersion": "0.8", - "package": "com.github.thedeathlycow.thermoo.mixin", + "package": "com.github.thedeathlycow.thermoo.mixin.common", "compatibilityLevel": "JAVA_17", "mixins": [ "EntityPredicateMixin", @@ -12,9 +12,7 @@ "PlayerTemperatureTickMixin", "TemperatureEffectTickerMixin" ], - "client": [ - ], "injectors": { "defaultRequire": 1 - } + } } diff --git a/src/testmod/java/com/github/thedeathlycow/thermoo/testmod/ThermooTestModClient.java b/src/testmod/java/com/github/thedeathlycow/thermoo/testmod/ThermooTestModClient.java new file mode 100644 index 00000000..98e54234 --- /dev/null +++ b/src/testmod/java/com/github/thedeathlycow/thermoo/testmod/ThermooTestModClient.java @@ -0,0 +1,62 @@ +package com.github.thedeathlycow.thermoo.testmod; + +import com.github.thedeathlycow.thermoo.api.client.StatusBarOverlayRenderEvents; +import com.github.thedeathlycow.thermoo.impl.Thermoo; +import net.fabricmc.api.ClientModInitializer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector2i; + +public class ThermooTestModClient implements ClientModInitializer { + + public static final Identifier HEART_OVERLAY_TEXTURE = Thermoo.id("textures/gui/fire_heart_overlay.png"); + + private static final int TEXTURE_WIDTH = 18; + private static final int TEXTURE_HEIGHT = 30; + + @Override + public void onInitializeClient() { + StatusBarOverlayRenderEvents.AFTER_HEALTH_BAR.register(ThermooTestModClient::renderFireHeartBar); + } + + public static void renderFireHeartBar( + DrawContext context, + PlayerEntity player, + Vector2i[] heartPositions, + int displayHealth, + int maxDisplayHealth + ) { + int fireHeartPoints = getNumFirePoints(player, maxDisplayHealth); + int fireHearts = getNumFireHeartsFromPoints(fireHeartPoints, maxDisplayHealth); + + for (int m = 0; m < fireHearts; m++) { + // is half heart if this is the last heart being rendered and we have an odd + // number of frozen health points + int x = heartPositions[m].x; + int y = heartPositions[m].y - 1; + boolean isHalfHeart = m + 1 >= fireHearts && (fireHeartPoints & 1) == 1; // is odd check + + int u = isHalfHeart ? 9 : 0; + + context.drawTexture(HEART_OVERLAY_TEXTURE, x, y, u, 0, 9, 10, TEXTURE_WIDTH, TEXTURE_HEIGHT); + } + } + + private static int getNumFirePoints(@NotNull PlayerEntity player, int maxDisplayHealth) { + float tempScale = player.thermoo$getTemperatureScale(); + if (tempScale <= 0f) { + return 0; + } + return (int) (tempScale * maxDisplayHealth); + } + + private static int getNumFireHeartsFromPoints(int fireHealthPoints, int maxDisplayHealth) { + // number of whole hearts + int frozenHealthHearts = MathHelper.ceil(fireHealthPoints / 2.0f); + + return Math.min(maxDisplayHealth / 2, frozenHealthHearts); + } +} diff --git a/src/testmod/resources/assets/thermoo/textures/gui/fire_heart_overlay.png b/src/testmod/resources/assets/thermoo/textures/gui/fire_heart_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..95e27290328fae3086908349c89573dfd9f05ecd GIT binary patch literal 393 zcmV;40e1e0P)_^k zrg@$x#qIetFA?Wa&RSV+KJE_Zhsy)Sq+R(E?iZ#kH=nMLx^qRR|)^|nAlWdC&Q@}czverQ6td%taM-nn+t$|x1hMwN!Br|8N ztkKl|da_^dPUEb7ZL-bK6O;OUaU5ZgHRfXIi4P}0O@lf2K$4*+L;~Jlb^+vFVG$X6 zBnp`?{CK