feat: Highlight century cake slice players
This commit is contained in:
@@ -6,15 +6,21 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import com.google.common.hash.Hashing
|
||||
import com.google.devtools.ksp.gradle.KspTaskJvm
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import moe.nea.licenseextractificator.LicenseDiscoveryTask
|
||||
import moe.nea.mcautotranslations.gradle.CollectTranslations
|
||||
import net.fabricmc.loom.LoomGradleExtension
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import org.gradle.platform.OperatingSystem
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin.Companion.kotlinNodeJsEnvSpec
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.Base64
|
||||
|
||||
plugins {
|
||||
java
|
||||
@@ -337,9 +343,9 @@ loom {
|
||||
named("client") {
|
||||
property("devauth.enabled", "true")
|
||||
vmArg("-ea")
|
||||
vmArg("-XX:+AllowEnhancedClassRedefinition")
|
||||
vmArg("-XX:HotswapAgent=external")
|
||||
vmArg("-javaagent:${hotswap.resolve().single().absolutePath}")
|
||||
// vmArg("-XX:+AllowEnhancedClassRedefinition")
|
||||
// vmArg("-XX:HotswapAgent=external")
|
||||
// vmArg("-javaagent:${hotswap.resolve().single().absolutePath}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,6 +469,70 @@ tasks.create("printAllLicenses", LicenseDiscoveryTask::class.java, licensing).ap
|
||||
}
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
fun patchRenderDoc(
|
||||
javaLauncher: JavaLauncher,
|
||||
): JavaLauncher {
|
||||
val wrappedJavaExecutable = javaLauncher.executablePath.asFile.absolutePath
|
||||
require("\"" !in wrappedJavaExecutable)
|
||||
val hashBytes = Hashing.sha256().hashString(wrappedJavaExecutable, StandardCharsets.UTF_8)
|
||||
val hash = Base64.getUrlEncoder().encodeToString(hashBytes.asBytes())
|
||||
.replace("=", "")
|
||||
val wrapperJavaRoot = rootProject.layout.buildDirectory
|
||||
.dir("binaries/renderdoc-wrapped-java/$hash/")
|
||||
.get()
|
||||
val isWindows = Os.isFamily(Os.FAMILY_WINDOWS)
|
||||
val wrapperJavaExe =
|
||||
if (isWindows) wrapperJavaRoot.file("java.cmd")
|
||||
else wrapperJavaRoot.file("java")
|
||||
return object : JavaLauncher {
|
||||
override fun getMetadata(): JavaInstallationMetadata {
|
||||
return object : JavaInstallationMetadata by javaLauncher.metadata {
|
||||
override fun isCurrentJvm(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getExecutablePath(): RegularFile {
|
||||
val fileF = wrapperJavaExe.asFile
|
||||
if (!fileF.exists()) {
|
||||
fileF.parentFile.mkdirs()
|
||||
if (isWindows) {
|
||||
fileF.writeText("""
|
||||
setlocal enableextensions
|
||||
start "" renderdoccmd.exe capture --opt-hook-children --wait-for-exit --working-dir . "$wrappedJavaExecutable" %*
|
||||
endlocal
|
||||
""".trimIndent())
|
||||
} else {
|
||||
fileF.writeText("""
|
||||
#!/usr/bin/env bash
|
||||
exec renderdoccmd capture --opt-hook-children --wait-for-exit --working-dir . "$wrappedJavaExecutable" "$@"
|
||||
""".trimIndent())
|
||||
fileF.setExecutable(true)
|
||||
}
|
||||
}
|
||||
return wrapperJavaExe
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.runClient {
|
||||
javaLauncher.set(javaToolchains.launcherFor(java.toolchain).map { patchRenderDoc(it) })
|
||||
}
|
||||
//tasks.register<Exec>("runRenderDoc") {
|
||||
// val runClient = tasks.runClient.get()
|
||||
// commandLine(
|
||||
// "renderdoc",
|
||||
// "capture",
|
||||
// "--opt-hook-children",
|
||||
// "--wait-for-exit",
|
||||
// "--working-dir",
|
||||
// runClient.workingDir,
|
||||
// runClient.javaLauncher.get().executablePath.asFile.absoluteFile,
|
||||
// )
|
||||
// args(runClient.allJvmArgs)
|
||||
// args()
|
||||
//
|
||||
//}
|
||||
|
||||
tasks.withType<AbstractArchiveTask>().configureEach {
|
||||
isPreserveFileTimestamps = false
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.render.entity.model.EntityModel;
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
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.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Applies various rendering modifications from {@link EntityRenderTintEvent}
|
||||
*/
|
||||
@Mixin(LivingEntityRenderer.class)
|
||||
public class ChangeColorOfLivingEntities<T extends LivingEntity, S extends LivingEntityRenderState, M extends EntityModel<? super S>> {
|
||||
@ModifyReturnValue(method = "getMixColor", at = @At("RETURN"))
|
||||
private int changeColor(int original, @Local(argsOnly = true) S state) {
|
||||
var tintState = EntityRenderTintEvent.HasTintRenderState.cast(state);
|
||||
if (tintState.getHasTintOverride_firmament())
|
||||
return tintState.getTint_firmament();
|
||||
return original;
|
||||
}
|
||||
|
||||
@ModifyArg(
|
||||
method = "getOverlay",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/OverlayTexture;getU(F)I"),
|
||||
allow = 1
|
||||
)
|
||||
private static float modifyLightOverlay(float originalWhiteOffset, @Local(argsOnly = true) LivingEntityRenderState state) {
|
||||
var tintState = EntityRenderTintEvent.HasTintRenderState.cast(state);
|
||||
if (tintState.getHasTintOverride_firmament() || tintState.getOverlayTexture_firmament() != null) {
|
||||
return 1F; // TODO: add interpolation percentage to render state extension
|
||||
}
|
||||
return originalWhiteOffset;
|
||||
}
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/client/render/entity/state/LivingEntityRenderState;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"))
|
||||
private void afterRender(S livingEntityRenderState, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
|
||||
var tintState = EntityRenderTintEvent.HasTintRenderState.cast(livingEntityRenderState);
|
||||
var overlayTexture = tintState.getOverlayTexture_firmament();
|
||||
if (overlayTexture != null && vertexConsumerProvider instanceof VertexConsumerProvider.Immediate imm) {
|
||||
imm.drawCurrentLayer();
|
||||
}
|
||||
EntityRenderTintEvent.overlayOverride = null;
|
||||
}
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/client/render/entity/state/LivingEntityRenderState;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;push()V"))
|
||||
private void beforeRender(S livingEntityRenderState, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
|
||||
var tintState = EntityRenderTintEvent.HasTintRenderState.cast(livingEntityRenderState);
|
||||
var overlayTexture = tintState.getOverlayTexture_firmament();
|
||||
if (overlayTexture != null) {
|
||||
EntityRenderTintEvent.overlayOverride = overlayTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import moe.nea.firmament.util.render.TintedOverlayTexture;
|
||||
import net.minecraft.client.render.entity.state.EntityRenderState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(EntityRenderState.class)
|
||||
public class EntityRenderStateTint implements EntityRenderTintEvent.HasTintRenderState {
|
||||
@Unique
|
||||
int tint = -1;
|
||||
@Unique
|
||||
TintedOverlayTexture overlayTexture;
|
||||
@Unique
|
||||
boolean hasTintOverride = false;
|
||||
|
||||
@Override
|
||||
public int getTint_firmament() {
|
||||
return tint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTint_firmament(int i) {
|
||||
tint = i;
|
||||
hasTintOverride = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getHasTintOverride_firmament() {
|
||||
return hasTintOverride;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHasTintOverride_firmament(boolean b) {
|
||||
hasTintOverride = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset_firmament() {
|
||||
hasTintOverride = false;
|
||||
overlayTexture = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TintedOverlayTexture getOverlayTexture_firmament() {
|
||||
return overlayTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverlayTexture_firmament(@Nullable TintedOverlayTexture tintedOverlayTexture) {
|
||||
this.overlayTexture = tintedOverlayTexture;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import net.minecraft.client.render.entity.EntityRenderer;
|
||||
import net.minecraft.client.render.entity.state.EntityRenderState;
|
||||
import net.minecraft.entity.Entity;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Dispatches {@link EntityRenderTintEvent} to collect additional render state used by {@link ChangeColorOfLivingEntities}
|
||||
*/
|
||||
@Mixin(EntityRenderer.class)
|
||||
public class InjectIntoRenderState<T extends Entity, S extends EntityRenderState> {
|
||||
|
||||
@Inject(
|
||||
method = "updateRenderState",
|
||||
at = @At("RETURN"))
|
||||
private void onUpdateRenderState(T entity, S state, float tickDelta, CallbackInfo ci) {
|
||||
var renderState = EntityRenderTintEvent.HasTintRenderState.cast(state);
|
||||
renderState.reset_firmament();
|
||||
var tintEvent = new EntityRenderTintEvent(
|
||||
entity,
|
||||
renderState
|
||||
);
|
||||
EntityRenderTintEvent.Companion.publish(tintEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Replaces the overlay texture used by rendering with the override specified in {@link EntityRenderTintEvent#overlayOverride}
|
||||
*/
|
||||
@Mixin(RenderLayer.Overlay.class)
|
||||
public class ReplaceOverlayTexture {
|
||||
@ModifyExpressionValue(
|
||||
method = {"method_23555", "method_23556"},
|
||||
expect = 2,
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;getOverlayTexture()Lnet/minecraft/client/render/OverlayTexture;"))
|
||||
private static OverlayTexture replaceOverlayTexture(OverlayTexture original) {
|
||||
if (EntityRenderTintEvent.overlayOverride != null)
|
||||
return EntityRenderTintEvent.overlayOverride;
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentRenderer;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Patch to make {@link EquipmentRenderer} use a {@link RenderLayer} that allows uses Minecraft's overlay texture, if a {@link EntityRenderTintEvent#overlayOverride} is specified.
|
||||
*/
|
||||
@Mixin(EquipmentRenderer.class)
|
||||
public class UseOverlayableEquipmentRenderer {
|
||||
@WrapOperation(method = "render(Lnet/minecraft/client/render/entity/equipment/EquipmentModel$LayerType;Lnet/minecraft/registry/RegistryKey;Lnet/minecraft/client/model/Model;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/util/Identifier;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayer;getArmorCutoutNoCull(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/RenderLayer;"))
|
||||
private RenderLayer replace(Identifier texture, Operation<RenderLayer> original) {
|
||||
if (EntityRenderTintEvent.overlayOverride != null)
|
||||
return RenderLayer.getEntityTranslucent(texture);
|
||||
return original.call(texture);
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "render(Lnet/minecraft/client/render/entity/equipment/EquipmentModel$LayerType;Lnet/minecraft/registry/RegistryKey;Lnet/minecraft/client/model/Model;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/util/Identifier;)V",
|
||||
at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/OverlayTexture;DEFAULT_UV:I"))
|
||||
private int replaceUvIndex(int original) {
|
||||
if (EntityRenderTintEvent.overlayOverride != null)
|
||||
return OverlayTexture.packUv(15, 10); // TODO: store this info in a global alongside overlayOverride
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.entity.feature.HeadFeatureRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Patch to make {@link HeadFeatureRenderer} use a {@link RenderLayer} that allows uses Minecraft's overlay texture, if a {@link EntityRenderTintEvent#overlayOverride} is specified.
|
||||
* @see UseOverlayableItemRenderer
|
||||
*/
|
||||
@Mixin(HeadFeatureRenderer.class)
|
||||
public class UseOverlayableHeadFeatureRenderer {
|
||||
|
||||
@ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V",
|
||||
at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/OverlayTexture;DEFAULT_UV:I"))
|
||||
private int replaceUvIndex(int original) {
|
||||
if (EntityRenderTintEvent.overlayOverride != null)
|
||||
return OverlayTexture.packUv(15, 10); // TODO: store this info in a global alongside overlayOverride
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.RenderPhase;
|
||||
import net.minecraft.client.render.item.ItemRenderState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Patch to make {@link ItemRenderState} use a {@link RenderLayer} that allows uses Minecraft's overlay texture.
|
||||
*
|
||||
* @see UseOverlayableHeadFeatureRenderer
|
||||
*/
|
||||
@Mixin(ItemRenderState.LayerRenderState.class)
|
||||
public class UseOverlayableItemRenderer {
|
||||
@ModifyExpressionValue(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/item/ItemRenderState$LayerRenderState;renderLayer:Lnet/minecraft/client/render/RenderLayer;"))
|
||||
private RenderLayer replace(RenderLayer original) {
|
||||
if (EntityRenderTintEvent.overlayOverride != null && original instanceof RenderLayer.MultiPhase multiPhase && multiPhase.phases.texture instanceof RenderPhase.Texture texture && texture.getId().isPresent())
|
||||
return RenderLayer.getEntityTranslucent(texture.getId().get());
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package moe.nea.firmament.mixins.render.entitytints;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Patch to make {@link SkullBlockEntityRenderer} use a {@link RenderLayer} that allows uses Minecraft's overlay texture, if a {@link EntityRenderTintEvent#overlayOverride} is specified.
|
||||
*/
|
||||
|
||||
@Mixin(SkullBlockEntityRenderer.class)
|
||||
public class UseOverlayableSkullBlockEntityRenderer {
|
||||
@ModifyExpressionValue(method = "renderSkull",
|
||||
at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/OverlayTexture;DEFAULT_UV:I"))
|
||||
private static int replaceUvIndex(int original) {
|
||||
if (EntityRenderTintEvent.overlayOverride != null)
|
||||
return OverlayTexture.packUv(15, 10); // TODO: store this info in a global alongside overlayOverride
|
||||
return original;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -254,6 +254,7 @@ fun firmamentCommand() = literal("firmament") {
|
||||
val player = MC.player ?: return@thenExecute
|
||||
player.world.getOtherEntities(player, player.boundingBox.expand(12.0))
|
||||
.forEach(PowerUserTools::showEntity)
|
||||
PowerUserTools.showEntity(player)
|
||||
}
|
||||
}
|
||||
thenLiteral("callUrsa") {
|
||||
|
||||
66
src/main/kotlin/events/EntityRenderTintEvent.kt
Normal file
66
src/main/kotlin/events/EntityRenderTintEvent.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import net.minecraft.client.render.GameRenderer
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.entity.state.EntityRenderState
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import moe.nea.firmament.util.render.TintedOverlayTexture
|
||||
|
||||
/**
|
||||
* Change the tint color of a [LivingEntity]
|
||||
*/
|
||||
class EntityRenderTintEvent(
|
||||
val entity: Entity,
|
||||
val renderState: HasTintRenderState
|
||||
) : FirmamentEvent.Cancellable() {
|
||||
init {
|
||||
if (entity !is LivingEntity) {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
companion object : FirmamentEventBus<EntityRenderTintEvent>() {
|
||||
/**
|
||||
* Static variable containing an override for [GameRenderer.getOverlayTexture]. Should be only set briefly.
|
||||
*
|
||||
* This variable only affects render layers that naturally make use of the overlay texture, have proper overlay UVs set (`overlay u != 0`), and have a shader that makes use of the overlay (does not have the `NO_OVERLAY` flag set in its json definition).
|
||||
*
|
||||
* Currently supported layers: [net.minecraft.client.render.entity.equipment.EquipmentRenderer], [net.minecraft.client.render.entity.model.PlayerEntityModel], as well as some others naturally.
|
||||
*
|
||||
* @see moe.nea.firmament.mixins.render.entitytints.ReplaceOverlayTexture
|
||||
* @see TintedOverlayTexture
|
||||
*/
|
||||
@JvmField
|
||||
var overlayOverride: OverlayTexture? = null
|
||||
}
|
||||
|
||||
@Suppress("PropertyName", "FunctionName")
|
||||
interface HasTintRenderState {
|
||||
/**
|
||||
* Multiplicative tint applied before the overlay.
|
||||
*/
|
||||
var tint_firmament: Int
|
||||
|
||||
/**
|
||||
* Must be set for [tint_firmament] to have any effect.
|
||||
*/
|
||||
var hasTintOverride_firmament: Boolean
|
||||
|
||||
// TODO: allow for more specific selection of which layers get tinted
|
||||
/**
|
||||
* Specify a [TintedOverlayTexture] to be used. This does not apply to render layers not using the overlay texture.
|
||||
* @see overlayOverride
|
||||
*/
|
||||
var overlayTexture_firmament: TintedOverlayTexture?
|
||||
fun reset_firmament()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun cast(state: EntityRenderState): HasTintRenderState {
|
||||
return state as HasTintRenderState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +1,31 @@
|
||||
package moe.nea.firmament.features.debug
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import com.mojang.serialization.JsonOps
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.block.SkullBlock
|
||||
import net.minecraft.block.entity.SkullBlockEntity
|
||||
import net.minecraft.component.DataComponentTypes
|
||||
import net.minecraft.component.type.ProfileComponent
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.nbt.NbtString
|
||||
import net.minecraft.predicate.NbtPredicate
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.text.TextCodecs
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.hit.EntityHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
import net.minecraft.util.math.Position
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.CustomItemModelEvent
|
||||
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
||||
@@ -31,6 +40,7 @@ import moe.nea.firmament.util.ClipboardUtils
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.focusedItemStack
|
||||
import moe.nea.firmament.util.mc.IntrospectableItemModelManager
|
||||
import moe.nea.firmament.util.mc.SNbtFormatter
|
||||
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
|
||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||
@@ -88,6 +98,11 @@ object PowerUserTools : FirmamentFeature {
|
||||
}
|
||||
|
||||
fun showEntity(target: Entity) {
|
||||
val nbt = NbtPredicate.entityToNbt(target)
|
||||
nbt.remove("Inventory")
|
||||
nbt.put("StyledName", TextCodecs.CODEC.encodeStart(NbtOps.INSTANCE, target.styledDisplayName).orThrow)
|
||||
println(SNbtFormatter.prettify(nbt))
|
||||
ClipboardUtils.setTextContent(SNbtFormatter.prettify(nbt))
|
||||
MC.sendChat(Text.translatable("firmament.poweruser.entity.type", target.type))
|
||||
MC.sendChat(Text.translatable("firmament.poweruser.entity.name", target.name))
|
||||
MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.position", target.pos))
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package moe.nea.firmament.features.events.anniversity
|
||||
|
||||
import java.util.Optional
|
||||
import me.shedaniel.math.Color
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.text.Style
|
||||
import net.minecraft.util.Formatting
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.EntityRenderTintEvent
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.SkyblockId
|
||||
import moe.nea.firmament.util.render.TintedOverlayTexture
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
import moe.nea.firmament.util.skyblock.SkyBlockItems
|
||||
|
||||
object CenturyRaffleFeatures {
|
||||
object TConfig : ManagedConfig("centuryraffle", Category.EVENTS) {
|
||||
val highlightPlayersForSlice by toggle("highlight-cake-players") { true }
|
||||
// val highlightAllPlayers by toggle("highlight-all-cake-players") { true }
|
||||
}
|
||||
|
||||
val cakeIcon = "⛃"
|
||||
|
||||
val cakeColors = listOf(
|
||||
CakeTeam(SkyBlockItems.SLICE_OF_BLUEBERRY_CAKE, Formatting.BLUE),
|
||||
CakeTeam(SkyBlockItems.SLICE_OF_CHEESECAKE, Formatting.YELLOW),
|
||||
CakeTeam(SkyBlockItems.SLICE_OF_GREEN_VELVET_CAKE, Formatting.GREEN),
|
||||
CakeTeam(SkyBlockItems.SLICE_OF_RED_VELVET_CAKE, Formatting.RED),
|
||||
CakeTeam(SkyBlockItems.SLICE_OF_STRAWBERRY_SHORTCAKE, Formatting.LIGHT_PURPLE),
|
||||
)
|
||||
|
||||
data class CakeTeam(
|
||||
val id: SkyblockId,
|
||||
val formatting: Formatting,
|
||||
) {
|
||||
val searchedTextRgb = formatting.colorValue!!
|
||||
val brightenedRgb = Color.ofOpaque(searchedTextRgb)//.brighter(2.0)
|
||||
val tintOverlay by lazy {
|
||||
TintedOverlayTexture().setColor(brightenedRgb)
|
||||
}
|
||||
}
|
||||
|
||||
val sliceToColor = cakeColors.associateBy { it.id }
|
||||
|
||||
@Subscribe
|
||||
fun onEntityRender(event: EntityRenderTintEvent) {
|
||||
if (!TConfig.highlightPlayersForSlice) return
|
||||
val requestedCakeTeam = sliceToColor[MC.stackInHand?.skyBlockId] ?: return
|
||||
// TODO: cache the requested color
|
||||
val player = event.entity as? PlayerEntity ?: return
|
||||
val cakeColor: Style = player.styledDisplayName.visit(
|
||||
{ style, text ->
|
||||
if (text == cakeIcon) Optional.of(style)
|
||||
else Optional.empty()
|
||||
}, Style.EMPTY).getOrNull() ?: return
|
||||
if (cakeColor.color?.rgb == requestedCakeTeam.searchedTextRgb) {
|
||||
event.renderState.overlayTexture_firmament = requestedCakeTeam.tintOverlay
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,11 +7,13 @@ import net.minecraft.client.gui.hud.InGameHud
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||
import net.minecraft.client.network.ClientPlayerEntity
|
||||
import net.minecraft.client.render.GameRenderer
|
||||
import net.minecraft.client.render.WorldRenderer
|
||||
import net.minecraft.client.render.item.ItemRenderer
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
|
||||
import net.minecraft.registry.BuiltinRegistries
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
@@ -85,6 +87,7 @@ object MC {
|
||||
inline val resourceManager get() = (instance.resourceManager as ReloadableResourceManagerImpl)
|
||||
inline val itemRenderer: ItemRenderer get() = instance.itemRenderer
|
||||
inline val worldRenderer: WorldRenderer get() = instance.worldRenderer
|
||||
inline val gameRenderer: GameRenderer get() = instance.gameRenderer
|
||||
inline val networkHandler get() = player?.networkHandler
|
||||
inline val instance get() = MinecraftClient.getInstance()
|
||||
inline val keyboard get() = instance.keyboard
|
||||
@@ -96,6 +99,7 @@ object MC {
|
||||
inline val soundManager get() = instance.soundManager
|
||||
inline val player: ClientPlayerEntity? get() = TestUtil.unlessTesting { instance.player }
|
||||
inline val camera: Entity? get() = instance.cameraEntity
|
||||
inline val stackInHand: ItemStack? get() = player?.inventory?.mainHandStack
|
||||
inline val guiAtlasManager get() = instance.guiAtlasManager
|
||||
inline val world: ClientWorld? get() = TestUtil.unlessTesting { instance.world }
|
||||
inline val playerName: String? get() = player?.name?.unformattedString
|
||||
|
||||
44
src/main/kotlin/util/render/TintedOverlayTexture.kt
Normal file
44
src/main/kotlin/util/render/TintedOverlayTexture.kt
Normal file
@@ -0,0 +1,44 @@
|
||||
package moe.nea.firmament.util.render
|
||||
|
||||
import com.mojang.blaze3d.platform.GlConst
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import me.shedaniel.math.Color
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.util.math.ColorHelper
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
|
||||
class TintedOverlayTexture : OverlayTexture() {
|
||||
companion object {
|
||||
val size = 16
|
||||
}
|
||||
|
||||
private var lastColor: Color? = null
|
||||
fun setColor(color: Color): TintedOverlayTexture {
|
||||
val image = ErrorUtil.notNullOr(texture.image, "Disposed TintedOverlayTexture written to") { return this }
|
||||
if (color == lastColor) return this
|
||||
lastColor = color
|
||||
|
||||
for (i in 0..<size) {
|
||||
for (j in 0..<size) {
|
||||
if (i < 8) {
|
||||
image.setColorArgb(j, i, 0xB2FF0000.toInt())
|
||||
} else {
|
||||
val k = ((1F - j / 15F * 0.75F) * 255F).toInt()
|
||||
image.setColorArgb(j, i, ColorHelper.withAlpha(k, color.color))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderSystem.activeTexture(GlConst.GL_TEXTURE1)
|
||||
texture.bindTexture()
|
||||
texture.setFilter(false, false)
|
||||
texture.setClamp(true)
|
||||
image.upload(0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
image.width, image.height,
|
||||
false)
|
||||
RenderSystem.activeTexture(GlConst.GL_TEXTURE0)
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,9 @@ object SkyBlockItems {
|
||||
val DIAMOND = SkyblockId("DIAMOND")
|
||||
val ANCESTRAL_SPADE = SkyblockId("ANCESTRAL_SPADE")
|
||||
val REFORGE_ANVIL = SkyblockId("REFORGE_ANVIL")
|
||||
val SLICE_OF_BLUEBERRY_CAKE = SkyblockId("SLICE_OF_BLUEBERRY_CAKE")
|
||||
val SLICE_OF_CHEESECAKE = SkyblockId("SLICE_OF_CHEESECAKE")
|
||||
val SLICE_OF_GREEN_VELVET_CAKE = SkyblockId("SLICE_OF_GREEN_VELVET_CAKE")
|
||||
val SLICE_OF_RED_VELVET_CAKE = SkyblockId("SLICE_OF_RED_VELVET_CAKE")
|
||||
val SLICE_OF_STRAWBERRY_SHORTCAKE = SkyblockId("SLICE_OF_STRAWBERRY_SHORTCAKE")
|
||||
}
|
||||
|
||||
@@ -20,3 +20,8 @@ mutable field net/minecraft/screen/slot/Slot y I
|
||||
|
||||
accessible field net/minecraft/entity/player/PlayerEntity PLAYER_MODEL_PARTS Lnet/minecraft/entity/data/TrackedData;
|
||||
accessible field net/minecraft/client/render/WorldRenderer chunks Lnet/minecraft/client/render/BuiltChunkStorage;
|
||||
accessible field net/minecraft/client/render/OverlayTexture texture Lnet/minecraft/client/texture/NativeImageBackedTexture;
|
||||
|
||||
accessible method net/minecraft/client/render/RenderPhase$Texture getId ()Ljava/util/Optional;
|
||||
accessible field net/minecraft/client/render/RenderLayer$MultiPhase phases Lnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;
|
||||
accessible field net/minecraft/client/render/RenderLayer$MultiPhaseParameters texture Lnet/minecraft/client/render/RenderPhase$TextureBase;
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
"firmament.config.category.mining.description": "Mining related features",
|
||||
"firmament.config.category.misc": "Miscellaneous",
|
||||
"firmament.config.category.misc.description": "Miscellaneous features that don't fit elsewhere",
|
||||
"firmament.config.centuryraffle": "Century Raffle",
|
||||
"firmament.config.centuryraffle.highlight-cake-players": "Highlight Players in Team",
|
||||
"firmament.config.centuryraffle.highlight-cake-players.description": "Highlight the players you can feed while holding a Slice of Century Cake",
|
||||
"firmament.config.chat-links": "Chat Links",
|
||||
"firmament.config.chat-links.allow-all-hosts": "Allow all Image Hosts",
|
||||
"firmament.config.chat-links.allow-all-hosts.description": "Allow displaying images no matter where it is hosted.",
|
||||
|
||||
Reference in New Issue
Block a user