更新内容
1. 方块掉落战利品表 2. 实体解析Map 3. 客户端转轮组件 4. NBTWriter 改进
This commit is contained in:
parent
4f38923636
commit
19463edd61
120
build.gradle
120
build.gradle
|
|
@ -6,6 +6,7 @@ plugins {
|
|||
id 'maven-publish'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
id 'net.neoforged.moddev.legacyforge' version '2.0.103'
|
||||
id 'com.dorongold.task-tree' version '2.1.1'
|
||||
}
|
||||
|
||||
java {
|
||||
|
|
@ -382,3 +383,122 @@ idea {
|
|||
tasks.withType(GenerateModuleMetadata) {
|
||||
enabled = false
|
||||
}
|
||||
tasks.register('showTaskTree') {
|
||||
doLast {
|
||||
def showTaskDeps
|
||||
showTaskDeps = { task, prefix = '' ->
|
||||
println "${prefix}${task.name}"
|
||||
task.getTaskDependencies().getDependencies(task).each { dep ->
|
||||
showTaskDeps(dep, prefix + ' ')
|
||||
}
|
||||
}
|
||||
|
||||
def targetTask = tasks.findByName('build')
|
||||
if (targetTask) {
|
||||
println "构建任务依赖树:"
|
||||
showTaskDeps(targetTask)
|
||||
} else {
|
||||
println "未找到 build 任务"
|
||||
}
|
||||
}
|
||||
}
|
||||
/**<pre>
|
||||
build
|
||||
├── check
|
||||
│ └── test
|
||||
│ ├── compileTestJava
|
||||
│ │ ├── classes
|
||||
│ │ │ ├── compileJava
|
||||
│ │ │ │ └── createMcpToSrg
|
||||
│ │ │ │ └── extractSrg
|
||||
│ │ │ │ └── downloadMcpConfig
|
||||
│ │ │ └── processResources
|
||||
│ │ └── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── testClasses
|
||||
│ │ ├── processTestResources
|
||||
│ │ └── compileTestJava
|
||||
│ │ ├── classes
|
||||
│ │ │ ├── compileJava
|
||||
│ │ │ │ └── createMcpToSrg
|
||||
│ │ │ │ └── extractSrg
|
||||
│ │ │ │ └── downloadMcpConfig
|
||||
│ │ │ └── processResources
|
||||
│ │ └── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── classes
|
||||
│ │ ├── compileJava
|
||||
│ │ │ └── createMcpToSrg
|
||||
│ │ │ └── extractSrg
|
||||
│ │ │ └── downloadMcpConfig
|
||||
│ │ └── processResources
|
||||
│ └── compileJava
|
||||
│ └── createMcpToSrg
|
||||
│ └── extractSrg
|
||||
│ └── downloadMcpConfig
|
||||
└── assemble
|
||||
├── reobfJarJar
|
||||
│ ├── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── configureReobfTaskForReobfJarJar
|
||||
│ └── proguard
|
||||
│ └── jarJar
|
||||
│ ├── classes
|
||||
│ │ ├── compileJava
|
||||
│ │ │ └── createMcpToSrg
|
||||
│ │ │ └── extractSrg
|
||||
│ │ │ └── downloadMcpConfig
|
||||
│ │ └── processResources
|
||||
│ ├── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ └── addMixinsToJarJar
|
||||
│ └── compileJava
|
||||
│ └── createMcpToSrg
|
||||
│ └── extractSrg
|
||||
│ └── downloadMcpConfig
|
||||
├── reobfJar
|
||||
│ ├── jar
|
||||
│ │ ├── classes
|
||||
│ │ │ ├── compileJava
|
||||
│ │ │ │ └── createMcpToSrg
|
||||
│ │ │ │ └── extractSrg
|
||||
│ │ │ │ └── downloadMcpConfig
|
||||
│ │ │ └── processResources
|
||||
│ │ ├── compileJava
|
||||
│ │ │ └── createMcpToSrg
|
||||
│ │ │ └── extractSrg
|
||||
│ │ │ └── downloadMcpConfig
|
||||
│ │ └── addMixinsToJar
|
||||
│ │ └── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ └── configureReobfTaskForReobfJar
|
||||
└── jar
|
||||
├── classes
|
||||
│ ├── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ └── processResources
|
||||
├── compileJava
|
||||
│ └── createMcpToSrg
|
||||
│ └── extractSrg
|
||||
│ └── downloadMcpConfig
|
||||
└── addMixinsToJar
|
||||
└── compileJava
|
||||
└── createMcpToSrg
|
||||
└── extractSrg
|
||||
└── downloadMcpConfig
|
||||
</pre>
|
||||
*/
|
||||
|
|
@ -33,7 +33,7 @@ mod_name=3944Realms 's Lib Mod
|
|||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||
mod_license=MIT
|
||||
# The mod version. See https://semver.org/
|
||||
mod_version=0.0.17
|
||||
mod_version=0.0.18
|
||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||
# This should match the base package used for the mod sources.
|
||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1829335 Languages: zh_tw
|
||||
4cb94c651f6aa74538a2ab25cb183cffd75be688 assets/lib39/lang/zh_tw.json
|
||||
// 1.20.1 2025-11-22T23:38:13.2517748 Languages: zh_tw
|
||||
84dba66c4c768fd7754eaabe990c96f14a30c1df assets/lib39/lang/zh_tw.json
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1764262 Languages: zh_cn
|
||||
a1db601a0fca923c5434b8b0843773a3115d0b59 assets/lib39/lang/zh_cn.json
|
||||
// 1.20.1 2025-11-22T23:38:13.249775 Languages: zh_cn
|
||||
48655f133966c9a854c57b560d070850af5a289f assets/lib39/lang/zh_cn.json
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// 1.20.1 2025-10-25T18:42:29.3405259 Item Models: lib39
|
||||
// 1.20.1 2025-11-22T23:38:13.2527751 Item Models: lib39
|
||||
14f581c8f8e7f0de004c57a180f371e60e7b12ae assets/lib39/models/item/fabric.json
|
||||
70583055336790fc837836ea6b49d16cfc8b64b8 assets/lib39/models/item/forge.json
|
||||
447b36747d0aa8748dcd86715f4cce2cff19aca7 assets/lib39/models/item/neoforge.json
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1804258 Languages: lzh
|
||||
098df025475d3f4d9d2abefa91a7d38c44644ba4 assets/lib39/lang/lzh.json
|
||||
// 1.20.1 2025-11-22T23:38:13.2517748 Languages: lzh
|
||||
a12c11d89d484a0e4f193ebd11b63722b8c501df assets/lib39/lang/lzh.json
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1784269 Languages: en_us
|
||||
c6b6aadca0a922823a8c949ebde93f8f999737f9 assets/lib39/lang/en_us.json
|
||||
// 1.20.1 2025-11-22T23:38:13.2507757 Languages: en_us
|
||||
65df86271a054138e168311be826408455b3c33a assets/lib39/lang/en_us.json
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"item.lib39.fabric": "Fabric",
|
||||
"item.lib39.forge": "Forge",
|
||||
"item.lib39.neoforge": "NeoForge"
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"item.lib39.fabric": "織",
|
||||
"item.lib39.forge": "砧",
|
||||
"item.lib39.neoforge": "狸"
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"item.lib39.fabric": "织布",
|
||||
"item.lib39.forge": "铁砧",
|
||||
"item.lib39.neoforge": "小狐狸"
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"item.lib39.fabric": "織布",
|
||||
"item.lib39.forge": "铁砧",
|
||||
"item.lib39.neoforge": "狐狸"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "lib39:item/forge"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
package top.r3944realms.lib39;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.r3944realms.lib39.core.network.NetworkHandler;
|
||||
|
|
@ -29,6 +32,11 @@ public class Lib39 {
|
|||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* The constant ENABLE_EXAMPLES_PROPERTY_KEY.
|
||||
*/
|
||||
public static final String ENABLE_EXAMPLES_PROPERTY_KEY = "lib39.enable_examples";
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
|
|
@ -43,6 +51,17 @@ public class Lib39 {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Rl resource location.
|
||||
*
|
||||
* @param path the path
|
||||
* @return the resource location
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ResourceLocation rl(String path) {
|
||||
return new ResourceLocation(MOD_ID, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Mod info.
|
||||
*/
|
||||
|
|
@ -66,7 +85,7 @@ public class Lib39 {
|
|||
* @return the boolean
|
||||
*/
|
||||
static boolean shouldRegisterExamples() {
|
||||
return !FMLEnvironment.production;
|
||||
return !FMLEnvironment.production || Boolean.getBoolean(ENABLE_EXAMPLES_PROPERTY_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.core.sync.ISyncData;
|
||||
import top.r3944realms.lib39.core.sync.ISyncManager;
|
||||
import top.r3944realms.lib39.core.sync.SyncData2Manager;
|
||||
|
|
@ -159,7 +160,7 @@ public class SyncManagerRegisterEvent extends Event {
|
|||
ResourceLocation id,
|
||||
ISyncManager<K, T> syncManager,
|
||||
Function<Entity, Optional<T>> dataProvider,
|
||||
Class<?>... allowedEntityClasses
|
||||
Class<?> @NotNull ... allowedEntityClasses
|
||||
) {
|
||||
registerSyncManager(id, syncManager, dataProvider);
|
||||
if (allowedEntityClasses.length > 0) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,769 @@
|
|||
package top.r3944realms.lib39.client.gui.component;
|
||||
|
||||
import com.mojang.blaze3d.platform.Window;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.*;
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector2f;
|
||||
import top.r3944realms.lib39.client.shader.Lib39Shaders;
|
||||
import top.r3944realms.lib39.util.MathUtil;
|
||||
import top.r3944realms.lib39.util.lang.FourConsumer;
|
||||
import top.r3944realms.lib39.util.lang.Pair;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The type Wheel widget.
|
||||
*
|
||||
* @author QiuShui1012
|
||||
*/
|
||||
@MethodsReturnNonnullByDefault
|
||||
@ParametersAreNonnullByDefault
|
||||
public class WheelWidget extends AbstractWidget {
|
||||
/**
|
||||
* The constant IGNORE_CURSOR_MOVE_LENGTH.
|
||||
*/
|
||||
public static final int IGNORE_CURSOR_MOVE_LENGTH = 15;
|
||||
private static final Vector2f ROTATION_START = new Vector2f(0, 1);
|
||||
private static final int SELECTION_EFFECT_COLOR = 0xddFFFF00;
|
||||
private static final int SELECTION_EFFECT_RADIUS = 20;
|
||||
|
||||
private final Minecraft minecraft = Minecraft.getInstance();
|
||||
private final Vector2f centerPos;
|
||||
private final float ringInnerRadius;
|
||||
private final float ringOuterRadius;
|
||||
private final int delay;
|
||||
private final int animationMs;
|
||||
private final int closingAnimationMs; //ms
|
||||
private final int ringColor;
|
||||
private final int selectionEffectColor;
|
||||
private final int selectionEffectRadius;
|
||||
private final float selectionAnimationSpeedFactor;
|
||||
private final int textColor;
|
||||
private final float textScale;
|
||||
private final List<WheelSection> sections = new ArrayList<>();
|
||||
|
||||
private long displayTime = System.currentTimeMillis();
|
||||
private float currentAngle = 0;
|
||||
|
||||
/**
|
||||
* Gets current section index.
|
||||
*
|
||||
* @return the current section index
|
||||
*/
|
||||
public int getCurrentSectionIndex() {
|
||||
return currentSectionIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is closing animation started boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean isClosingAnimationStarted() {
|
||||
return closingAnimationStarted;
|
||||
}
|
||||
|
||||
private int currentSectionIndex = -1;
|
||||
private Vector2f selectionEffectPos;
|
||||
private boolean animationStarted = false;
|
||||
|
||||
/**
|
||||
* Sets closing animation started.
|
||||
*
|
||||
* @param closingAnimationStarted the closing animation started
|
||||
*/
|
||||
public void setClosingAnimationStarted(boolean closingAnimationStarted) {
|
||||
this.closingAnimationStarted = closingAnimationStarted;
|
||||
}
|
||||
|
||||
private boolean closingAnimationStarted = false;
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param textScale the text scale
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height,
|
||||
float ringInnerRadius, float ringOuterRadius, float textScale,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, textScale, sections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param textScale the text scale
|
||||
* @param degreeOffsetAngle the degree offset angle
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height,
|
||||
float ringInnerRadius, float ringOuterRadius, float textScale, float degreeOffsetAngle,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, textScale, degreeOffsetAngle, sections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height,
|
||||
float ringInnerRadius, float ringOuterRadius,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, sections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param message the message
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param textScale the text scale
|
||||
* @param degreeOffsetAngle the degree offset angle
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height, Component message,
|
||||
float ringInnerRadius, float ringOuterRadius, float textScale, float degreeOffsetAngle,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
this(
|
||||
x, y, width, height, message,
|
||||
ringInnerRadius, ringOuterRadius,
|
||||
150, 300, 150,
|
||||
0x00000000,
|
||||
0xddffff00, 20, 5f,
|
||||
0xfdfdfd, textScale, degreeOffsetAngle,
|
||||
sections
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param message the message
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height, Component message,
|
||||
float ringInnerRadius, float ringOuterRadius,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
this(
|
||||
x, y, width, height, message,
|
||||
ringInnerRadius, ringOuterRadius,
|
||||
150, 300, 150,
|
||||
0x00000000,
|
||||
0xddffff00, 20, 5f,
|
||||
0xfdfdfd, 1f, 0f,
|
||||
sections
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param message the message
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param degreeOffsetAngle the degree offset angle
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height, Component message,
|
||||
float ringInnerRadius, float ringOuterRadius, float degreeOffsetAngle,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
this(
|
||||
x, y, width, height, message,
|
||||
ringInnerRadius, ringOuterRadius,
|
||||
150, 300, 150,
|
||||
0x00000000,
|
||||
0xddffff00, 20, 5f,
|
||||
0xfdfdfd, 1f, degreeOffsetAngle,
|
||||
sections
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param delay the delay
|
||||
* @param animationMs the animation ms
|
||||
* @param closingAnimationMs the closing animation ms
|
||||
* @param ringColor the ring color
|
||||
* @param selectionEffectColor the selection effect color
|
||||
* @param selectionEffectRadius the selection effect radius
|
||||
* @param selectionAnimationSpeedFactor the selection animation speed factor
|
||||
* @param textColor the text color
|
||||
* @param textScale the text scale
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height,
|
||||
float ringInnerRadius, float ringOuterRadius,
|
||||
int delay, int animationMs, int closingAnimationMs,
|
||||
int ringColor,
|
||||
int selectionEffectColor, int selectionEffectRadius, float selectionAnimationSpeedFactor,
|
||||
int textColor, float textScale,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
this(
|
||||
x, y, width, height, Component.empty(),
|
||||
ringInnerRadius, ringOuterRadius,
|
||||
delay, animationMs, closingAnimationMs,
|
||||
ringColor,
|
||||
selectionEffectColor, selectionEffectRadius, selectionAnimationSpeedFactor,
|
||||
textColor, textScale, 0f,
|
||||
sections
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Wheel widget.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param message the message
|
||||
* @param ringInnerRadius the ring inner radius
|
||||
* @param ringOuterRadius the ring outer radius
|
||||
* @param delay the delay
|
||||
* @param animationMs the animation ms
|
||||
* @param closingAnimationMs the closing animation ms
|
||||
* @param ringColor the ring color
|
||||
* @param selectionEffectColor the selection effect color
|
||||
* @param selectionEffectRadius the selection effect radius
|
||||
* @param selectionAnimationSpeedFactor the selection animation speed factor
|
||||
* @param textColor the text color
|
||||
* @param textScale the text scale
|
||||
* @param degreeOffsetAngle the degree offset angle
|
||||
* @param sections the sections
|
||||
*/
|
||||
public WheelWidget(
|
||||
int x, int y, int width, int height, Component message,
|
||||
float ringInnerRadius, float ringOuterRadius,
|
||||
int delay, int animationMs, int closingAnimationMs,
|
||||
int ringColor,
|
||||
int selectionEffectColor, int selectionEffectRadius, float selectionAnimationSpeedFactor,
|
||||
int textColor, float textScale, float degreeOffsetAngle,
|
||||
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
|
||||
) {
|
||||
super(x, y, width, height, message);
|
||||
this.centerPos = new Vector2f(this.getX() + this.getWidth() / 2f, this.getY() + this.getHeight() / 2f);
|
||||
this.ringInnerRadius = Math.max(ringInnerRadius, IGNORE_CURSOR_MOVE_LENGTH);
|
||||
this.ringOuterRadius = ringOuterRadius;
|
||||
this.delay = delay;
|
||||
this.animationMs = animationMs;
|
||||
this.closingAnimationMs = closingAnimationMs;
|
||||
this.ringColor = ringColor;
|
||||
this.selectionEffectColor = selectionEffectColor;
|
||||
this.selectionEffectRadius = selectionEffectRadius;
|
||||
this.selectionAnimationSpeedFactor = selectionAnimationSpeedFactor;
|
||||
this.textColor = textColor;
|
||||
this.textScale = textScale;
|
||||
float degreeEachRotation = 360f / sections.size();
|
||||
for (int i = 0; i < sections.size(); i++) {
|
||||
Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>> section = sections.get(i);
|
||||
float rotation = MathUtil.clampWithProportion((degreeEachRotation * i + degreeOffsetAngle) % 360, 0, 360);
|
||||
Vector2f rotated = MathUtil.rotationDegrees(ROTATION_START, rotation)
|
||||
.mul(1, -1)
|
||||
.mul(this.getSectionCircleDiameter())
|
||||
.add(this.centerPos);
|
||||
float detectionStart = (float) (Math.toRadians(rotation - degreeEachRotation / 2f) + Math.PI * 2);
|
||||
float detectionEnd = (float) (Math.toRadians(rotation + degreeEachRotation / 2f) + Math.PI * 2);
|
||||
detectionStart = detectionStart % (float) (Math.PI * 2);
|
||||
detectionEnd = detectionEnd % (float) (Math.PI * 2);
|
||||
this.sections.add(new WheelSection(
|
||||
rotated,
|
||||
(float) (Math.toRadians(rotation) % (Math.PI * 2)),
|
||||
detectionStart,
|
||||
detectionEnd,
|
||||
section.first,
|
||||
section.second
|
||||
));
|
||||
}
|
||||
this.selectionEffectPos = MathUtil.rotate(
|
||||
MathUtil.copy(ROTATION_START)
|
||||
.mul(this.getSectionCircleDiameter()),
|
||||
this.currentAngle
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets section circle diameter.
|
||||
*
|
||||
* @return the section circle diameter
|
||||
*/
|
||||
// 滚轮选择器中每个扇形的圆形直径
|
||||
public float getSectionCircleDiameter() {
|
||||
return this.ringOuterRadius + this.ringInnerRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets current index.
|
||||
*
|
||||
* @param index the index
|
||||
* @return the current index
|
||||
*/
|
||||
public WheelWidget setCurrentIndex(int index) {
|
||||
this.currentSectionIndex = index;
|
||||
this.currentAngle = this.sections.get(index).angle;
|
||||
this.selectionEffectPos = MathUtil.rotate(
|
||||
MathUtil.copy(ROTATION_START)
|
||||
.mul(this.getSectionCircleDiameter()),
|
||||
this.currentAngle
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets section size.
|
||||
*
|
||||
* @return the section size
|
||||
*/
|
||||
public int getSectionSize() {
|
||||
return this.sections.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
|
||||
if (delta > 0) {
|
||||
if (this.currentSectionIndex == this.getSectionSize() - 1) {
|
||||
this.currentSectionIndex = 0;
|
||||
} else {
|
||||
this.currentSectionIndex++;
|
||||
}
|
||||
} else if (delta < 0) {
|
||||
if (this.currentSectionIndex == 0) {
|
||||
this.currentSectionIndex = this.getSectionSize() - 1;
|
||||
} else {
|
||||
this.currentSectionIndex--;
|
||||
}
|
||||
}
|
||||
for (WheelSection section : this.sections) {
|
||||
if (this.sections.indexOf(section) == this.currentSectionIndex) {
|
||||
this.currentAngle = section.angle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check mouse pos.
|
||||
*
|
||||
* @param mouseX the mouse x
|
||||
* @param mouseY the mouse y
|
||||
*/
|
||||
public void checkMousePos(double mouseX, double mouseY) {
|
||||
if (this.closingAnimationStarted) return;
|
||||
float centerX = this.centerPos.x;
|
||||
float centerY = this.centerPos.y;
|
||||
// 鼠标距离屏幕中心的位置向量
|
||||
Vector2f cursorPos = new Vector2f((float) mouseX - centerX, (float) mouseY - centerY);
|
||||
|
||||
if (cursorPos.length() < IGNORE_CURSOR_MOVE_LENGTH) return;
|
||||
|
||||
Vector2f rotationStart = new Vector2f(0, 1);
|
||||
cursorPos.normalize();
|
||||
// 计算夹角弧度
|
||||
double rot = Math.acos(rotationStart.dot(cursorPos) / (rotationStart.length() * cursorPos.length()));
|
||||
double rotation = cursorPos.x < 0 ? Math.PI - rot : Math.PI + rot;
|
||||
for (WheelSection section : this.sections) {
|
||||
if (section.angleStart > section.angleEnd && rotation >= section.angleStart
|
||||
|| rotation >= section.angleStart && rotation <= section.angleEnd
|
||||
) {
|
||||
this.currentAngle = section.angle;
|
||||
this.currentSectionIndex = this.sections.indexOf(section);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should render boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean shouldRender() {
|
||||
if (this.animationStarted) return true;
|
||||
return (this.displayTime + this.delay) <= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||
this.checkMousePos(mouseX, mouseY);
|
||||
this.renderWidget(guiGraphics, mouseX, mouseY, partialTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderWidget(GuiGraphics guiGraphics, int i, int i1, float v) {
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.enableBlend();
|
||||
this.renderClosingAnimation(guiGraphics);
|
||||
if (!this.shouldRender()) {
|
||||
return;
|
||||
}
|
||||
if (this.closingAnimationStarted) return;
|
||||
if (!this.animationStarted) {
|
||||
this.animationStarted = true;
|
||||
this.displayTime = System.currentTimeMillis();
|
||||
}
|
||||
PoseStack poseStack = guiGraphics.pose();
|
||||
float delta = this.displayTime + this.animationMs - System.currentTimeMillis();
|
||||
if (delta > 0) {
|
||||
float progress = 1 - (delta / this.animationMs);
|
||||
progress = (float) (-Math.pow(progress, 2) + 2 * progress);
|
||||
if (progress == 0) return;
|
||||
this.renderProgressAnimation(guiGraphics, progress);
|
||||
return;
|
||||
}
|
||||
renderRing(
|
||||
guiGraphics,
|
||||
this.centerPos.x,
|
||||
this.centerPos.y,
|
||||
this.ringColor,
|
||||
this.ringInnerRadius * 2,
|
||||
this.ringOuterRadius * 2
|
||||
);
|
||||
this.renderSelection(guiGraphics);
|
||||
for (WheelSection value : this.sections) {
|
||||
float x = value.center.x;
|
||||
float y = value.center.y;
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(x - 10, y - 10, 100);
|
||||
value.renderer.accept(guiGraphics, poseStack, 20, 20);
|
||||
poseStack.popPose();
|
||||
poseStack.pushPose();
|
||||
float coordinateScale = 0.7f;
|
||||
float offsetX = 0.1f * this.width;
|
||||
float offsetY = 0.1f * this.height;
|
||||
float adjustedX = (x - offsetX) / coordinateScale;
|
||||
float adjustedY = (y - offsetY - 20 * this.textScale) / coordinateScale;
|
||||
|
||||
poseStack.translate(offsetX, offsetY, 0);
|
||||
poseStack.scale(coordinateScale, coordinateScale, coordinateScale);
|
||||
poseStack.translate(adjustedX, adjustedY, 0);
|
||||
poseStack.scale(this.textScale / coordinateScale, this.textScale / coordinateScale, this.textScale / coordinateScale);
|
||||
guiGraphics.drawCenteredString(
|
||||
minecraft.font,
|
||||
value.subTitle,
|
||||
0,
|
||||
0,
|
||||
(0xff << 24) | this.textColor
|
||||
);
|
||||
poseStack.popPose();
|
||||
}
|
||||
RenderSystem.disableDepthTest();
|
||||
RenderSystem.disableBlend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render closing animation.
|
||||
*
|
||||
* @param guiGraphics the gui graphics
|
||||
*/
|
||||
public void renderClosingAnimation(GuiGraphics guiGraphics) {
|
||||
if (!this.closingAnimationStarted) return;
|
||||
float delta = this.displayTime + this.closingAnimationMs - System.currentTimeMillis();
|
||||
float progress = delta / this.closingAnimationMs;
|
||||
if(progress >= 1 || progress <= 0) {
|
||||
this.minecraft.setScreen(null);
|
||||
}
|
||||
this.renderProgressAnimation(guiGraphics, progress);
|
||||
}
|
||||
private void renderProgressAnimation(GuiGraphics guiGraphics, float progress) {
|
||||
progress = (float) (-Math.pow(progress, 2) + 2 * progress);
|
||||
if (progress == 0) return;
|
||||
PoseStack poseStack = guiGraphics.pose();
|
||||
poseStack.pushPose();
|
||||
renderRing(
|
||||
guiGraphics,
|
||||
this.centerPos.x,
|
||||
this.centerPos.y,
|
||||
this.ringColor,
|
||||
this.ringInnerRadius * 2 * progress,
|
||||
this.ringOuterRadius * 2 * progress
|
||||
);
|
||||
poseStack.popPose();
|
||||
if(this.currentSectionIndex != -1) {
|
||||
WheelSection section = this.sections.get(this.currentSectionIndex);
|
||||
Vector2f center = new Vector2f(
|
||||
(section.center.x - this.centerPos.x) / this.getSectionCircleDiameter(),
|
||||
(section.center.y - this.centerPos.y) / this.getSectionCircleDiameter()
|
||||
).mul(this.getSectionCircleDiameter() * progress).add(this.centerPos.x, this.centerPos.y);
|
||||
renderSelectionEffect(
|
||||
guiGraphics,
|
||||
center.x,
|
||||
center.y,
|
||||
this.selectionEffectColor,
|
||||
this.selectionEffectRadius
|
||||
);
|
||||
}
|
||||
for (WheelSection value : this.sections) {
|
||||
if (sections.get(0) != value) continue;
|
||||
Vector2f center = new Vector2f(
|
||||
(value.center.x - this.centerPos.x) / this.getSectionCircleDiameter(),
|
||||
(value.center.y - this.centerPos.y) / this.getSectionCircleDiameter()
|
||||
).mul(this.getSectionCircleDiameter() * progress).add(this.centerPos.x, this.centerPos.y);
|
||||
float x = center.x;
|
||||
float y = center.y;
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(x - 10, y - 10, 100);
|
||||
value.renderer.accept(guiGraphics, poseStack, 20, 20);
|
||||
poseStack.pushPose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render ring.
|
||||
*
|
||||
* @param guiGraphics the gui graphics
|
||||
* @param centerX the center x
|
||||
* @param centerY the center y
|
||||
* @param color the color
|
||||
* @param innerRadius the inner radius
|
||||
* @param outerRadius the outer radius
|
||||
*/
|
||||
public static void renderRing(
|
||||
GuiGraphics guiGraphics,
|
||||
float centerX,
|
||||
float centerY,
|
||||
int color,
|
||||
float innerRadius, // 改为半径
|
||||
float outerRadius // 改为半径
|
||||
) {
|
||||
PoseStack poseStack = guiGraphics.pose();
|
||||
poseStack.pushPose();
|
||||
|
||||
Tesselator tesselator = Tesselator.getInstance();
|
||||
BufferBuilder buffer = tesselator.getBuilder();
|
||||
|
||||
// 计算足够大的绘制区域来覆盖整个环形(基于外半径)
|
||||
float margin = outerRadius + 100f; // 使用半径计算边距
|
||||
float x1 = centerX - margin;
|
||||
float y1 = centerY - margin;
|
||||
float x2 = centerX + margin;
|
||||
float y2 = centerY + margin;
|
||||
|
||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||
|
||||
Matrix4f matrix = poseStack.last().pose();
|
||||
buffer.vertex(matrix, x1, y1, -300).color(color).endVertex();
|
||||
buffer.vertex(matrix, x1, y2, -300).color(color).endVertex();
|
||||
buffer.vertex(matrix, x2, y2, -300).color(color).endVertex();
|
||||
buffer.vertex(matrix, x2, y1, -300).color(color).endVertex();
|
||||
|
||||
setupRingShader(centerX, centerY, innerRadius, outerRadius);
|
||||
|
||||
BufferUploader.drawWithShader(buffer.end());
|
||||
poseStack.popPose();
|
||||
}
|
||||
|
||||
private static void setupRingShader(float centerX, float centerY, float innerRadius, float outerRadius) {
|
||||
Window window = Minecraft.getInstance().getWindow();
|
||||
float guiScale = (float) window.getGuiScale();
|
||||
|
||||
RenderSystem.setShader(Lib39Shaders::getRingShader);
|
||||
|
||||
// 转换到像素坐标(考虑GUI缩放)
|
||||
float pixelCenterX = centerX * guiScale;
|
||||
float pixelCenterY = window.getHeight() - (centerY * guiScale); // 翻转Y坐标
|
||||
|
||||
// 半径考虑GUI缩放
|
||||
float pixelInnerRadius = innerRadius * guiScale;
|
||||
float pixelOuterRadius = outerRadius * guiScale;
|
||||
float pixelAntiAliasing = 2.0f * guiScale; // 抗锯齿范围
|
||||
|
||||
System.out.println("Shader Params - Center: (" + pixelCenterX + ", " + pixelCenterY +
|
||||
"), InnerRadius: " + pixelInnerRadius + ", OuterRadius: " + pixelOuterRadius);
|
||||
|
||||
ShaderInstance shader = Lib39Shaders.getRingShader();
|
||||
shader.safeGetUniform("Center").set(pixelCenterX, pixelCenterY);
|
||||
shader.safeGetUniform("InnerRadius").set(pixelInnerRadius);
|
||||
shader.safeGetUniform("OuterRadius").set(pixelOuterRadius);
|
||||
shader.safeGetUniform("AntiAliasing").set(pixelAntiAliasing);
|
||||
shader.safeGetUniform("ColorModulator").set(1.0f, 1.0f, 1.0f, .5f);
|
||||
}
|
||||
|
||||
private void renderSelection(GuiGraphics guiGraphics) {
|
||||
float selectionEffectAngle = MathUtil.angle(
|
||||
MathUtil.copy(ROTATION_START),
|
||||
this.selectionEffectPos
|
||||
);
|
||||
|
||||
float diffAngle = this.currentAngle - selectionEffectAngle;
|
||||
|
||||
if (diffAngle > Math.PI) {
|
||||
diffAngle -= (float) (Math.PI * 2);
|
||||
} else if (diffAngle < -Math.PI) {
|
||||
diffAngle += (float) (Math.PI * 2);
|
||||
}
|
||||
|
||||
this.selectionEffectPos = MathUtil.rotate(
|
||||
this.selectionEffectPos,
|
||||
diffAngle / this.selectionAnimationSpeedFactor
|
||||
);
|
||||
|
||||
Vector2f pos = MathUtil.copy(this.selectionEffectPos)
|
||||
.mul(1, -1)
|
||||
.add(this.centerPos);
|
||||
|
||||
// 调用时使用半径
|
||||
renderSelectionEffect(
|
||||
guiGraphics,
|
||||
pos.x,
|
||||
pos.y,
|
||||
SELECTION_EFFECT_COLOR,
|
||||
SELECTION_EFFECT_RADIUS // 确保这是半径值
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render selection effect.
|
||||
*
|
||||
* @param guiGraphics the gui graphics
|
||||
* @param centerX the center x
|
||||
* @param centerY the center y
|
||||
* @param color the color
|
||||
* @param radius the radius
|
||||
*/
|
||||
public static void renderSelectionEffect(
|
||||
GuiGraphics guiGraphics,
|
||||
float centerX,
|
||||
float centerY,
|
||||
int color,
|
||||
float radius
|
||||
) {
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.disableDepthTest();
|
||||
PoseStack poseStack = guiGraphics.pose();
|
||||
Matrix4f matrix4f = poseStack.last().pose();
|
||||
Tesselator tesselator = Tesselator.getInstance();
|
||||
BufferBuilder buffer = tesselator.getBuilder();
|
||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||
|
||||
float x1 = centerX - radius - 5;
|
||||
float y1 = centerY - radius - 5;
|
||||
float x2 = centerX + radius + 5;
|
||||
float y2 = centerY + radius + 5;
|
||||
buffer.vertex(matrix4f, x1, y1, -200).color(color).endVertex();
|
||||
buffer.vertex(matrix4f, x1, y2, -200).color(color).endVertex();
|
||||
buffer.vertex(matrix4f, x2, y2, -200).color(color).endVertex();
|
||||
buffer.vertex(matrix4f, x2, y1, -200).color(color).endVertex();
|
||||
|
||||
Window window = Minecraft.getInstance().getWindow();
|
||||
float guiScale = (float) window.getGuiScale();
|
||||
RenderSystem.setShader(Lib39Shaders::getSelectionShader);
|
||||
System.out.println("Selection Effect Params:");
|
||||
System.out.println(" Center: " + centerX + ", " + centerY);
|
||||
System.out.println(" Radius: " + radius);
|
||||
System.out.println(" GUI Scale: " + guiScale);
|
||||
System.out.println(" Framebuffer: " + window.getWidth() + "x" + window.getHeight());
|
||||
Lib39Shaders.getSelectionShader()
|
||||
.safeGetUniform("Center")
|
||||
.set(centerX * guiScale, centerY * guiScale);
|
||||
Lib39Shaders.getSelectionShader()
|
||||
.safeGetUniform("FramebufferSize")
|
||||
.set((float) window.getWidth(), (float) window.getHeight());
|
||||
Lib39Shaders.getSelectionShader()
|
||||
.safeGetUniform("Radius")
|
||||
.set(radius * guiScale);
|
||||
Lib39Shaders.getSelectionShader()
|
||||
.safeGetUniform("AntiAliasingRadius")
|
||||
.set(guiScale); // 根据需要调整
|
||||
RenderSystem.setShaderColor(1, 1, 1, 1);
|
||||
BufferUploader.drawWithShader(Objects.requireNonNull(buffer.end()));
|
||||
RenderSystem.enableDepthTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* On closing.
|
||||
*/
|
||||
public void onClosing() {
|
||||
if (this.shouldRender() && !this.closingAnimationStarted) {
|
||||
this.displayTime = System.currentTimeMillis();
|
||||
this.closingAnimationStarted = true;
|
||||
} else {
|
||||
this.minecraft.setScreen(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Wheel section.
|
||||
*/
|
||||
public record WheelSection(
|
||||
Vector2f center,
|
||||
float angle,
|
||||
float angleStart,
|
||||
float angleEnd,
|
||||
Component subTitle,
|
||||
FourConsumer<GuiGraphics, PoseStack, Integer, Integer> renderer
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,381 @@
|
|||
package top.r3944realms.lib39.client.renderer;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.*;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 圆形径向菜单渲染器
|
||||
* 用于创建美观的圆形选择菜单
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
*/
|
||||
public class RadialMenuRenderer<T> {
|
||||
/**
|
||||
* The constant DEFAULT_INNER_RADIUS.
|
||||
*/
|
||||
// 默认配置常量
|
||||
public static final float DEFAULT_INNER_RADIUS = 30f;
|
||||
/**
|
||||
* The constant DEFAULT_OUTER_RADIUS.
|
||||
*/
|
||||
public static final float DEFAULT_OUTER_RADIUS = 80f;
|
||||
/**
|
||||
* The constant DEFAULT_MIDDLE_RADIUS.
|
||||
*/
|
||||
public static final float DEFAULT_MIDDLE_RADIUS = 55f;
|
||||
/**
|
||||
* The constant DEFAULT_SEGMENTS.
|
||||
*/
|
||||
public static final int DEFAULT_SEGMENTS = 64;
|
||||
|
||||
// 配置选项
|
||||
private final float innerRadius;
|
||||
private final float outerRadius;
|
||||
private final float middleRadius;
|
||||
private final int segments;
|
||||
private final boolean enableHoverAnimation;
|
||||
private final ColorScheme colorScheme;
|
||||
|
||||
// 状态
|
||||
private int hoveredIndex = -1;
|
||||
private final float[] hoverAnimations;
|
||||
private long lastAnimationTime = 0;
|
||||
|
||||
/**
|
||||
* The type Color scheme.
|
||||
*/
|
||||
public static class ColorScheme {
|
||||
/**
|
||||
* The Normal color.
|
||||
*/
|
||||
public final float[] normalColor;
|
||||
/**
|
||||
* The Hovered color.
|
||||
*/
|
||||
public final float[] hoveredColor;
|
||||
/**
|
||||
* The Selected color.
|
||||
*/
|
||||
public final float[] selectedColor;
|
||||
/**
|
||||
* The Background color.
|
||||
*/
|
||||
public final float[] backgroundColor;
|
||||
|
||||
/**
|
||||
* Instantiates a new Color scheme.
|
||||
*
|
||||
* @param normalColor the normal color
|
||||
* @param hoveredColor the hovered color
|
||||
* @param selectedColor the selected color
|
||||
* @param backgroundColor the background color
|
||||
*/
|
||||
public ColorScheme(float[] normalColor, float[] hoveredColor, float[] selectedColor, float[] backgroundColor) {
|
||||
this.normalColor = normalColor;
|
||||
this.hoveredColor = hoveredColor;
|
||||
this.selectedColor = selectedColor;
|
||||
this.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The constant DEFAULT.
|
||||
*/
|
||||
// 预定义颜色方案
|
||||
public static final ColorScheme DEFAULT = new ColorScheme(
|
||||
new float[]{0.3f, 0.3f, 0.8f, 0.6f}, // 正常 - 蓝色
|
||||
new float[]{0.9f, 0.7f, 0.1f, 0.8f}, // 悬停 - 金色
|
||||
new float[]{0.2f, 0.8f, 0.2f, 0.9f}, // 选中 - 绿色
|
||||
new float[]{0.1f, 0.1f, 0.1f, 0.7f} // 背景
|
||||
);
|
||||
|
||||
/**
|
||||
* The constant FIRE.
|
||||
*/
|
||||
public static final ColorScheme FIRE = new ColorScheme(
|
||||
new float[]{0.8f, 0.3f, 0.1f, 0.6f}, // 正常 - 红色
|
||||
new float[]{1.0f, 0.5f, 0.0f, 0.8f}, // 悬停 - 橙色
|
||||
new float[]{1.0f, 0.9f, 0.0f, 0.9f}, // 选中 - 黄色
|
||||
new float[]{0.2f, 0.1f, 0.0f, 0.7f} // 背景
|
||||
);
|
||||
|
||||
/**
|
||||
* The constant NATURE.
|
||||
*/
|
||||
public static final ColorScheme NATURE = new ColorScheme(
|
||||
new float[]{0.2f, 0.6f, 0.3f, 0.6f}, // 正常 - 绿色
|
||||
new float[]{0.4f, 0.8f, 0.4f, 0.8f}, // 悬停 - 亮绿
|
||||
new float[]{0.1f, 0.9f, 0.7f, 0.9f}, // 选中 - 青绿
|
||||
new float[]{0.1f, 0.2f, 0.1f, 0.7f} // 背景
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new Radial menu renderer.
|
||||
*/
|
||||
public RadialMenuRenderer() {
|
||||
this(DEFAULT_INNER_RADIUS, DEFAULT_OUTER_RADIUS, ColorScheme.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Radial menu renderer.
|
||||
*
|
||||
* @param innerRadius the inner radius
|
||||
* @param outerRadius the outer radius
|
||||
*/
|
||||
public RadialMenuRenderer(float innerRadius, float outerRadius) {
|
||||
this(innerRadius, outerRadius, ColorScheme.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Radial menu renderer.
|
||||
*
|
||||
* @param innerRadius the inner radius
|
||||
* @param outerRadius the outer radius
|
||||
* @param colorScheme the color scheme
|
||||
*/
|
||||
public RadialMenuRenderer(float innerRadius, float outerRadius, ColorScheme colorScheme) {
|
||||
this(innerRadius, outerRadius, (innerRadius + outerRadius) / 2f, DEFAULT_SEGMENTS, true, colorScheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Radial menu renderer.
|
||||
*
|
||||
* @param innerRadius the inner radius
|
||||
* @param outerRadius the outer radius
|
||||
* @param middleRadius the middle radius
|
||||
* @param segments the segments
|
||||
* @param enableHoverAnimation the enable hover animation
|
||||
* @param colorScheme the color scheme
|
||||
*/
|
||||
public RadialMenuRenderer(float innerRadius, float outerRadius, float middleRadius,
|
||||
int segments, boolean enableHoverAnimation, ColorScheme colorScheme) {
|
||||
this.innerRadius = innerRadius;
|
||||
this.outerRadius = outerRadius;
|
||||
this.middleRadius = middleRadius;
|
||||
this.segments = segments;
|
||||
this.enableHoverAnimation = enableHoverAnimation;
|
||||
this.colorScheme = colorScheme;
|
||||
this.hoverAnimations = new float[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染圆形菜单
|
||||
*
|
||||
* @param guiGraphics the gui graphics
|
||||
* @param entries the entries
|
||||
* @param titleProvider the title provider
|
||||
* @param iconProvider the icon provider
|
||||
* @param selectedIndex the selected index
|
||||
* @param trackMouse the track mouse
|
||||
*/
|
||||
public void render(GuiGraphics guiGraphics, List<T> entries,
|
||||
Function<T, Component> titleProvider,
|
||||
Function<T, ItemStack> iconProvider,
|
||||
int selectedIndex, boolean trackMouse) {
|
||||
if (entries.isEmpty()) return;
|
||||
|
||||
// 更新动画状态
|
||||
updateHoverAnimations(entries.size());
|
||||
|
||||
// 设置渲染状态
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.setShader(GameRenderer::getPositionColorShader);
|
||||
|
||||
float centerX = guiGraphics.guiWidth() / 2f;
|
||||
float centerY = guiGraphics.guiHeight() / 2f;
|
||||
|
||||
guiGraphics.pose().pushPose();
|
||||
guiGraphics.pose().translate(centerX, centerY, 0f);
|
||||
|
||||
// 渲染所有扇形区域
|
||||
renderSectors(guiGraphics, entries, selectedIndex);
|
||||
|
||||
// 渲染图标和文本
|
||||
renderIconsAndText(guiGraphics, entries, titleProvider, iconProvider);
|
||||
|
||||
guiGraphics.pose().popPose();
|
||||
|
||||
RenderSystem.disableBlend();
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染扇形区域
|
||||
*/
|
||||
private void renderSectors(GuiGraphics guiGraphics, List<T> entries, int selectedIndex) {
|
||||
int count = entries.size();
|
||||
float angleSize = 360f / count;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
float startAngle = -90f + i * angleSize;
|
||||
float currentOuterRadius = outerRadius;
|
||||
|
||||
// 悬停动画效果
|
||||
if (enableHoverAnimation && i < hoverAnimations.length) {
|
||||
currentOuterRadius += hoverAnimations[i] * 5f;
|
||||
}
|
||||
|
||||
// 颜色设置
|
||||
float[] color = getSectorColor(i, selectedIndex, entries.get(i));
|
||||
|
||||
// 绘制扇形
|
||||
drawSector(guiGraphics, startAngle, angleSize, innerRadius, currentOuterRadius, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取扇形颜色
|
||||
*/
|
||||
private float[] getSectorColor(int index, int selectedIndex, T entry) {
|
||||
if (index == selectedIndex) {
|
||||
return colorScheme.selectedColor; // 选中状态
|
||||
} else if (index == hoveredIndex) {
|
||||
return colorScheme.hoveredColor; // 悬停状态
|
||||
} else {
|
||||
return colorScheme.normalColor; // 普通状态
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制单个扇形
|
||||
*/
|
||||
private void drawSector(GuiGraphics guiGraphics, float startAngle, float angleSize,
|
||||
float innerRadius, float outerRadius, float[] color) {
|
||||
BufferBuilder buffer = Tesselator.getInstance().getBuilder();
|
||||
buffer.begin(VertexFormat.Mode.TRIANGLE_STRIP, DefaultVertexFormat.POSITION_COLOR);
|
||||
|
||||
Matrix4f matrix = guiGraphics.pose().last().pose();
|
||||
float segments = Math.max(8, this.segments * (angleSize / 360f));
|
||||
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
float progress = i / segments;
|
||||
float angle = startAngle + progress * angleSize;
|
||||
float rad = angle * Mth.DEG_TO_RAD;
|
||||
|
||||
float cos = Mth.cos(rad);
|
||||
float sin = Mth.sin(rad);
|
||||
|
||||
// 外圈顶点
|
||||
buffer.vertex(matrix, outerRadius * cos, outerRadius * sin, 0)
|
||||
.color(color[0], color[1], color[2], color[3]).endVertex();
|
||||
// 内圈顶点
|
||||
buffer.vertex(matrix, innerRadius * cos, innerRadius * sin, 0)
|
||||
.color(color[0], color[1], color[2], color[3] * 0.6f).endVertex();
|
||||
}
|
||||
|
||||
BufferUploader.drawWithShader(buffer.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染图标和文本
|
||||
*/
|
||||
private void renderIconsAndText(GuiGraphics guiGraphics, List<T> entries,
|
||||
Function<T, Component> titleProvider,
|
||||
Function<T, ItemStack> iconProvider) {
|
||||
int count = entries.size();
|
||||
var font = Minecraft.getInstance().font;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
T entry = entries.get(i);
|
||||
float angle = (-90f + 360f * (i + 0.5f) / count) * Mth.DEG_TO_RAD;
|
||||
|
||||
// 计算位置
|
||||
float x = Mth.cos(angle) * middleRadius;
|
||||
float y = Mth.sin(angle) * middleRadius;
|
||||
|
||||
// 渲染图标
|
||||
ItemStack icon = iconProvider.apply(entry);
|
||||
if (!icon.isEmpty()) {
|
||||
guiGraphics.renderItem(icon, (int)(x - 8), (int)(y - 8));
|
||||
}
|
||||
|
||||
// 渲染文本
|
||||
Component title = titleProvider.apply(entry);
|
||||
guiGraphics.pose().pushPose();
|
||||
guiGraphics.pose().translate(x, y + 12, 0);
|
||||
guiGraphics.pose().scale(0.7f, 0.7f, 0.7f);
|
||||
guiGraphics.drawString(font, title, -font.width(title) / 2, 0, 0xFFFFFF, true);
|
||||
guiGraphics.pose().popPose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新悬停动画
|
||||
*/
|
||||
private void updateHoverAnimations(int entryCount) {
|
||||
if (!enableHoverAnimation) return;
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
float deltaTime = Math.min((currentTime - lastAnimationTime) / 1000f, 0.1f);
|
||||
lastAnimationTime = currentTime;
|
||||
|
||||
// 确保数组大小正确
|
||||
if (hoverAnimations.length != entryCount) {
|
||||
// 这里需要重新初始化数组,实际使用时应该处理数组大小变化
|
||||
}
|
||||
|
||||
// 更新动画值
|
||||
for (int i = 0; i < hoverAnimations.length && i < entryCount; i++) {
|
||||
if (i == hoveredIndex) {
|
||||
hoverAnimations[i] = Mth.clamp(hoverAnimations[i] + deltaTime * 2f, 0f, 1f);
|
||||
} else {
|
||||
hoverAnimations[i] = Mth.clamp(hoverAnimations[i] - deltaTime * 3f, 0f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取鼠标下的条目索引
|
||||
*
|
||||
* @param entries the entries
|
||||
* @param mouseX the mouse x
|
||||
* @param mouseY the mouse y
|
||||
* @return the hovered entry
|
||||
*/
|
||||
public int getHoveredEntry(List<T> entries, double mouseX, double mouseY) {
|
||||
float centerX = Minecraft.getInstance().getWindow().getGuiScaledWidth() / 2f;
|
||||
float centerY = Minecraft.getInstance().getWindow().getGuiScaledHeight() / 2f;
|
||||
|
||||
double relX = mouseX - centerX;
|
||||
double relY = mouseY - centerY;
|
||||
double distance = Math.sqrt(relX * relX + relY * relY);
|
||||
|
||||
// 检查是否在有效范围内
|
||||
if (distance < innerRadius || distance > outerRadius) {
|
||||
hoveredIndex = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 计算角度
|
||||
double angle = Math.atan2(relY, relX) * Mth.RAD_TO_DEG;
|
||||
angle = (angle + 450) % 360; // 标准化到 0-360
|
||||
|
||||
int count = entries.size();
|
||||
int index = (int) (angle / (360f / count)) % count;
|
||||
|
||||
hoveredIndex = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除状态
|
||||
*/
|
||||
public void clearState() {
|
||||
hoveredIndex = -1;
|
||||
// 重置动画数组
|
||||
Arrays.fill(hoverAnimations, 0f);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package top.r3944realms.lib39.client.shader;
|
||||
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import net.minecraftforge.client.event.RegisterShadersEvent;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
|
||||
/**
|
||||
* The type Lib 39 shaders.
|
||||
*/
|
||||
public class Lib39Shaders {
|
||||
/**
|
||||
* The Minecraft.
|
||||
*/
|
||||
static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||
|
||||
/**
|
||||
* Gets ring shader.
|
||||
*
|
||||
* @return the ring shader
|
||||
*/
|
||||
public static ShaderInstance getRingShader() {
|
||||
return ringShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Ring shader.
|
||||
*/
|
||||
static ShaderInstance ringShader;
|
||||
|
||||
/**
|
||||
* Gets selection shader.
|
||||
*
|
||||
* @return the selection shader
|
||||
*/
|
||||
public static ShaderInstance getSelectionShader() {
|
||||
return selectionShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Selection shader.
|
||||
*/
|
||||
static ShaderInstance selectionShader;
|
||||
|
||||
/**
|
||||
* Register.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
public static void register(RegisterShadersEvent event) {
|
||||
try {
|
||||
event.registerShader(
|
||||
new ShaderInstance(
|
||||
event.getResourceProvider(),
|
||||
Lib39.rl("ring"),
|
||||
DefaultVertexFormat.POSITION_COLOR
|
||||
),
|
||||
it -> ringShader = it
|
||||
);
|
||||
event.registerShader(
|
||||
new ShaderInstance(
|
||||
event.getResourceProvider(),
|
||||
Lib39.rl("selection"),
|
||||
DefaultVertexFormat.POSITION_COLOR
|
||||
),
|
||||
it -> selectionShader = it
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Lib39.LOGGER.error("Failed to register shader", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
package top.r3944realms.lib39.core.event;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.RegisterShadersEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.client.shader.Lib39Shaders;
|
||||
|
||||
/**
|
||||
* The type Client handler.
|
||||
*/
|
||||
|
|
@ -7,7 +13,18 @@ public class ClientEventHandler {
|
|||
/**
|
||||
* The type Mod.
|
||||
*/
|
||||
public static class Mod extends ClientEventHandler {}
|
||||
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(value = Dist.CLIENT, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD, modid = Lib39.MOD_ID)
|
||||
public static class Mod extends ClientEventHandler {
|
||||
/**
|
||||
* On register shaders.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public static void onRegisterShaders(RegisterShadersEvent event) {
|
||||
Lib39Shaders.register(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Game.
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ public class CommonEventHandler {
|
|||
IEventBus gameBus = MinecraftForge.EVENT_BUS;
|
||||
compatManager = new CompatManager(modBus, gameBus);
|
||||
MinecraftForge.EVENT_BUS.post(new RegisterCompatEvent(compatManager));
|
||||
compatManager.initializeAll();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package top.r3944realms.lib39.datagen.provider;
|
||||
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.loot.LootTableProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
import net.minecraft.world.level.storage.loot.ValidationContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The type Simple loot table provider.
|
||||
*/
|
||||
public class SimpleLootTableProvider extends LootTableProvider {
|
||||
/**
|
||||
* Instantiates a new Simple loot table provider.
|
||||
*
|
||||
* @param output the output
|
||||
* @param subProvidersWrapper the sub providers wrapper
|
||||
*/
|
||||
public SimpleLootTableProvider(PackOutput output, @NotNull SubProvidersWrapper subProvidersWrapper) {
|
||||
super(output, Set.of(), subProvidersWrapper.entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Simple loot table provider.
|
||||
*
|
||||
* @param output the output
|
||||
* @param requiredTables the required tables
|
||||
* @param subProvidersWrapper the sub providers wrapper
|
||||
*/
|
||||
public SimpleLootTableProvider(PackOutput output, Set<ResourceLocation> requiredTables, @NotNull SubProvidersWrapper subProvidersWrapper) {
|
||||
super(output, requiredTables, subProvidersWrapper.entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validate(@NotNull Map<ResourceLocation, LootTable> map, @NotNull ValidationContext validationcontext) {
|
||||
map.forEach((id, table) -> table.validate(validationcontext));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package top.r3944realms.lib39.datagen.provider;
|
||||
|
||||
import net.minecraft.data.loot.LootTableProvider;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
||||
import top.r3944realms.lib39.datagen.provider.subprovider.BlockLootTables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The type Sub providers wrapper.
|
||||
*/
|
||||
public class SubProvidersWrapper {
|
||||
/**
|
||||
* The Entries.
|
||||
*/
|
||||
public List<LootTableProvider.SubProviderEntry> entries = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new Sub providers wrapper.
|
||||
*/
|
||||
public SubProvidersWrapper() {}
|
||||
|
||||
/**
|
||||
* Add entry sub providers wrapper.
|
||||
*
|
||||
* @param entry the entry
|
||||
* @return the sub providers wrapper
|
||||
*/
|
||||
public SubProvidersWrapper addEntry(LootTableProvider.SubProviderEntry entry) {
|
||||
entries.add(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add block entry sub providers wrapper.
|
||||
*
|
||||
* @param blockLootTables the block loot tables
|
||||
* @return the sub providers wrapper
|
||||
*/
|
||||
public SubProvidersWrapper addBlockEntry(BlockLootTables blockLootTables) {
|
||||
entries.add(new LootTableProvider.SubProviderEntry(() -> blockLootTables, LootContextParamSets.BLOCK));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
package top.r3944realms.lib39.datagen.provider.subprovider;
|
||||
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.data.loot.BlockLootSubProvider;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.enchantment.Enchantments;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.FlowerPotBlock;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.storage.loot.LootPool;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
import net.minecraft.world.level.storage.loot.entries.LootItem;
|
||||
import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount;
|
||||
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.MatchTool;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The type Block loot tables.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class BlockLootTables extends BlockLootSubProvider {
|
||||
|
||||
private final List<BlockEntry> blockEntries = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new Block loot tables.
|
||||
*/
|
||||
public BlockLootTables() {
|
||||
super(Set.of(), FeatureFlags.REGISTRY.allFlags());
|
||||
}
|
||||
|
||||
// ==================== 流畅 API 构建方法 ====================
|
||||
|
||||
/**
|
||||
* 添加自掉落的方块
|
||||
*
|
||||
* @param block the block
|
||||
*/
|
||||
public void dropSelf(RegistryObject<Block> block) {
|
||||
addEntry(block, this::createSingleItemTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加自掉落的方块
|
||||
*
|
||||
* @param blocks the blocks
|
||||
* @return the block loot tables
|
||||
*/
|
||||
@Contract("_ -> this")
|
||||
@SafeVarargs
|
||||
public final BlockLootTables dropSelf(RegistryObject<Block> @NotNull ... blocks) {
|
||||
for (RegistryObject<Block> block : blocks) {
|
||||
dropSelf(block);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加需要丝绸之触才掉落的方块
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropWhenSilkTouch(RegistryObject<Block> block) {
|
||||
return addEntry(block, BlockLootSubProvider::createSilkTouchOnlyTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加掉落其他物品的方块
|
||||
*
|
||||
* @param block the block
|
||||
* @param item the item
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropOther(RegistryObject<Block> block, RegistryObject<? extends ItemLike> item) {
|
||||
return addEntry(block, pBlock -> this.createSingleItemTable(item.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加只能被剪子剪下的方块
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropWhenShears(RegistryObject<Block> block) {
|
||||
return addEntry(block, BlockLootSubProvider::createShearsOnlyDrop);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加矿物的掉落表(支持时运附魔)
|
||||
*
|
||||
* @param block the block
|
||||
* @param oreItem the ore item
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropOre(RegistryObject<Block> block, RegistryObject<Item> oreItem) {
|
||||
return addEntry(block, b -> this.createOreDrop(b, oreItem.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加红石矿石掉落表
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropRedstoneOre(RegistryObject<Block> block) {
|
||||
return addEntry(block, this::createRedstoneOreDrops);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加青金石矿石掉落表
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropLapisOre(RegistryObject<Block> block) {
|
||||
return addEntry(block, this::createLapisOreDrops);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加铜矿石掉落表
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropCopperOre(RegistryObject<Block> block) {
|
||||
return addEntry(block, this::createCopperOreDrops);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加地毯类方块的掉落(一次掉落2个)
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropCarpet(RegistryObject<Block> block) {
|
||||
return addEntry(block, b -> LootTable.lootTable()
|
||||
.withPool(LootPool.lootPool()
|
||||
.setRolls(ConstantValue.exactly(1))
|
||||
.add(LootItem.lootTableItem(b)
|
||||
.apply(SetItemCountFunction.setCount(ConstantValue.exactly(2.0F))))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加台阶方块的掉落
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropSlab(RegistryObject<Block> block) {
|
||||
return addEntry(block, this::createSlabItemTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加门方块的掉落(只掉落下半部分)
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropDoor(RegistryObject<Block> block) {
|
||||
return addEntry(block, this::createDoorTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加花盆的掉落
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropFlowerPot(RegistryObject<Block> block) {
|
||||
return addEntry(block, (pBlock) -> this.createPotFlowerItemTable(((FlowerPotBlock)pBlock).getContent()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加树叶的掉落
|
||||
*
|
||||
* @param leavesBlock the leaves block
|
||||
* @param saplingBlock the sapling block
|
||||
* @param chances the chances
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropLeaves(RegistryObject<Block> leavesBlock,
|
||||
RegistryObject<Block> saplingBlock,
|
||||
float... chances) {
|
||||
return addEntry(leavesBlock, b -> this.createLeavesDrops(b, saplingBlock.get(), chances));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加橡树叶的掉落(包含苹果)
|
||||
*
|
||||
* @param leavesBlock the leaves block
|
||||
* @param saplingBlock the sapling block
|
||||
* @param chances the chances
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropOakLeaves(RegistryObject<Block> leavesBlock,
|
||||
RegistryObject<Block> saplingBlock,
|
||||
float... chances) {
|
||||
return addEntry(leavesBlock, b -> this.createOakLeavesDrops(b, saplingBlock.get(), chances));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加农作物的掉落
|
||||
*
|
||||
* @param cropBlock the crop block
|
||||
* @param cropItem the crop item
|
||||
* @param seedsItem the seeds item
|
||||
* @param ageProperty the age property
|
||||
* @param maxAge the max age
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables dropCrop(RegistryObject<Block> cropBlock,
|
||||
RegistryObject<Item> cropItem,
|
||||
RegistryObject<Item> seedsItem,
|
||||
Property<Integer> ageProperty,
|
||||
int maxAge) {
|
||||
return addEntry(cropBlock, b -> this.createCropDrops(
|
||||
b,
|
||||
cropItem.get(),
|
||||
seedsItem.get(),
|
||||
LootItemBlockStatePropertyCondition.hasBlockStateProperties(b)
|
||||
.setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties()
|
||||
.hasProperty(ageProperty, maxAge))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义掉落表
|
||||
*
|
||||
* @param block the block
|
||||
* @param factory the factory
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables custom(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {
|
||||
return addEntry(block, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 没有掉落
|
||||
*
|
||||
* @param block the block
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables noDrop(RegistryObject<Block> block) {
|
||||
return addEntry(block, b -> noDrop());
|
||||
}
|
||||
|
||||
// ==================== 批量操作方法 ====================
|
||||
|
||||
/**
|
||||
* 批量操作一系列方块
|
||||
*
|
||||
* @param blocks the blocks
|
||||
* @param operation the operation
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables batch(@NotNull Iterable<RegistryObject<Block>> blocks,
|
||||
Function<RegistryObject<Block>, BlockLootTables> operation) {
|
||||
for (RegistryObject<Block> block : blocks) {
|
||||
operation.apply(block);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对一组方块应用相同的操作
|
||||
*
|
||||
* @param operation the operation
|
||||
* @param blocks the blocks
|
||||
* @return the block loot tables
|
||||
*/
|
||||
@Contract("_, _ -> this")
|
||||
@SafeVarargs
|
||||
public final BlockLootTables applyToAll(Function<RegistryObject<Block>, BlockLootTables> operation,
|
||||
RegistryObject<Block> @NotNull ... blocks) {
|
||||
for (RegistryObject<Block> block : blocks) {
|
||||
operation.apply(block);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// ==================== 构建方法 ====================
|
||||
|
||||
/**
|
||||
* 构建并返回自身(用于流畅API链式调用)
|
||||
*
|
||||
* @return the block loot tables
|
||||
*/
|
||||
public BlockLootTables build() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generate() {
|
||||
for (BlockEntry entry : blockEntries) {
|
||||
this.add(entry.block.get(), entry.factory);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 内部类和方法 ====================
|
||||
|
||||
private BlockLootTables addEntry(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {
|
||||
blockEntries.add(new BlockEntry(block, factory));
|
||||
return this;
|
||||
}
|
||||
|
||||
private record BlockEntry(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {}
|
||||
|
||||
// ==================== 静态工厂方法 ====================
|
||||
|
||||
/**
|
||||
* 创建新的战利品表生成器
|
||||
*
|
||||
* @return the block loot tables
|
||||
*/
|
||||
@Contract(" -> new")
|
||||
public static @NotNull BlockLootTables create() {
|
||||
return new BlockLootTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速创建基本矿物的掉落表
|
||||
*
|
||||
* @param oreBlock the ore block
|
||||
* @param oreItem the ore item
|
||||
* @return the loot table .@ not null builder
|
||||
*/
|
||||
public static LootTable.@NotNull Builder simpleOreDrop(Block oreBlock, Item oreItem) {
|
||||
return LootTable.lootTable()
|
||||
.withPool(LootPool.lootPool()
|
||||
.setRolls(ConstantValue.exactly(1))
|
||||
.add(LootItem.lootTableItem(oreItem)
|
||||
.when(MatchTool.toolMatches(ItemPredicate.Builder.item()
|
||||
.hasEnchantment(new net.minecraft.advancements.critereon.EnchantmentPredicate(
|
||||
Enchantments.SILK_TOUCH,
|
||||
net.minecraft.advancements.critereon.MinMaxBounds.Ints.atLeast(1)
|
||||
))).invert())
|
||||
.apply(SetItemCountFunction.setCount(UniformGenerator.between(1, 1)))
|
||||
.apply(ApplyBonusCount.addOreBonusCount(Enchantments.BLOCK_FORTUNE))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速创建石质方块的掉落表(需要镐子)
|
||||
*
|
||||
* @param stoneBlock the stone block
|
||||
* @param dropItem the drop item
|
||||
* @return the loot table .@ not null builder
|
||||
*/
|
||||
public static LootTable.@NotNull Builder stoneDrop(Block stoneBlock, Item dropItem) {
|
||||
return LootTable.lootTable()
|
||||
.withPool(LootPool.lootPool()
|
||||
.setRolls(ConstantValue.exactly(1))
|
||||
.add(LootItem.lootTableItem(dropItem)
|
||||
.when(MatchTool.toolMatches(ItemPredicate.Builder.item()
|
||||
.hasEnchantment(new net.minecraft.advancements.critereon.EnchantmentPredicate(
|
||||
Enchantments.SILK_TOUCH,
|
||||
net.minecraft.advancements.critereon.MinMaxBounds.Ints.atLeast(1)
|
||||
))))));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package top.r3944realms.lib39.example.client.screen;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Renderable;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.joml.Vector2f;
|
||||
import top.r3944realms.lib39.client.gui.component.WheelWidget;
|
||||
import top.r3944realms.lib39.util.lang.FourConsumer;
|
||||
import top.r3944realms.lib39.util.lang.Pair;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static top.r3944realms.lib39.client.gui.component.WheelWidget.IGNORE_CURSOR_MOVE_LENGTH;
|
||||
|
||||
/**
|
||||
* The type Forge screen.
|
||||
*/
|
||||
public class ForgeScreen extends Screen {
|
||||
private final LocalPlayer player = Objects.requireNonNull(Minecraft.getInstance().player);
|
||||
private final InteractionHand hand;
|
||||
private final int mode;
|
||||
|
||||
/**
|
||||
* The Wheel.
|
||||
*/
|
||||
public WheelWidget wheel;
|
||||
|
||||
/**
|
||||
* Instantiates a new Forge screen.
|
||||
*
|
||||
* @param hand the hand
|
||||
* @param mode the mode
|
||||
*/
|
||||
public ForgeScreen(InteractionHand hand, int mode) {
|
||||
super(Component.literal("Test"));
|
||||
this.hand = hand;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
int leftPos = (this.width - 75) / 2;
|
||||
int topPos = (this.height - 75) / 2;
|
||||
ItemStack holding = player.getItemInHand(this.hand);
|
||||
WheelWidget wheel = new WheelWidget(
|
||||
leftPos, topPos, 75, 75,
|
||||
12.5f, 32.5f, 0.75f,
|
||||
List.of(
|
||||
Pair.of(
|
||||
Component.literal("auto"),
|
||||
renderItem(holding)),
|
||||
Pair.of(
|
||||
Component.literal("axe"),
|
||||
renderItem(holding)),
|
||||
Pair.of(
|
||||
Component.literal("shovel"),
|
||||
renderItem(holding)),
|
||||
Pair.of(
|
||||
Component.literal("hoe"),
|
||||
renderItem(holding)),
|
||||
Pair.of(
|
||||
Component.literal("pickaxe"),
|
||||
renderItem(holding))
|
||||
)
|
||||
).setCurrentIndex(this.wheel != null ? this.wheel.getCurrentSectionIndex() : this.mode);
|
||||
this.clearWidgets();
|
||||
this.wheel = this.addRenderableWidget(wheel);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private static @NotNull FourConsumer<GuiGraphics, PoseStack, Integer, Integer> renderItem(ItemStack holding) {
|
||||
return (graphics, pose, width, height) -> {
|
||||
ItemStack stack = holding.copy();
|
||||
graphics.renderItem(stack, 2, 2, 9910597);
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) {
|
||||
if (wheel != null && wheel.isClosingAnimationStarted()) return true;
|
||||
float screenCenterX = this.width / 2f;
|
||||
float screenCenterY = this.height / 2f;
|
||||
Vector2f cursorVec2 = new Vector2f(
|
||||
(float) mouseX - screenCenterX,
|
||||
(float) mouseY - screenCenterY
|
||||
);
|
||||
if (cursorVec2.length() < IGNORE_CURSOR_MOVE_LENGTH) {
|
||||
return true;
|
||||
}
|
||||
return super.mouseDragged(mouseX, mouseY, button, dragX, dragY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||
if (wheel != null ) {
|
||||
wheel.onClosing();
|
||||
}
|
||||
return super.mouseReleased(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
super.removed();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void render(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||
for (Renderable renderable : this.renderables) {
|
||||
renderable.render(guiGraphics, mouseX, mouseY, partialTick);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPauseScreen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package top.r3944realms.lib39.example.content.item;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.example.client.screen.ForgeScreen;
|
||||
|
||||
/**
|
||||
* The type Forge item.
|
||||
*/
|
||||
public class ForgeItem extends Item {
|
||||
/**
|
||||
* Instantiates a new Forge item.
|
||||
*
|
||||
* @param properties the properties
|
||||
*/
|
||||
public ForgeItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand usedHand) {
|
||||
if (level.isClientSide() && usedHand == InteractionHand.MAIN_HAND) {
|
||||
Minecraft.getInstance().setScreen(new ForgeScreen(usedHand, 0));
|
||||
}
|
||||
return super.use(level, player, usedHand);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import net.minecraftforge.registries.ForgeRegistries;
|
|||
import net.minecraftforge.registries.RegistryObject;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.example.content.item.FabricItem;
|
||||
import top.r3944realms.lib39.example.content.item.ForgeItem;
|
||||
import top.r3944realms.lib39.example.content.item.NeoForgeItem;
|
||||
|
||||
/**
|
||||
|
|
@ -38,6 +39,16 @@ public class ExLib39Items {
|
|||
.stacksTo(1)
|
||||
.fireResistant()
|
||||
));
|
||||
/**
|
||||
* The constant FORGE.
|
||||
*/
|
||||
public static final RegistryObject<Item> FORGE =
|
||||
ITEMS.register("forge",
|
||||
() -> new ForgeItem(
|
||||
new Item.Properties()
|
||||
.stacksTo(1)
|
||||
.fireResistant()
|
||||
));
|
||||
|
||||
/**
|
||||
* Register.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ public enum ExLib39LangKeys implements ILangKeyValueCollection {
|
|||
ExLib39Items.NEOFORGE, ModPartEnum.ITEM,
|
||||
"NeoForge", "小狐狸", "狐狸", "狸", true
|
||||
));
|
||||
addLang(LangKeyValue.ofSupplier(
|
||||
ExLib39Items.FORGE, ModPartEnum.ITEM,
|
||||
"Forge", "铁砧", "铁砧", "砧", true
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
195
src/main/java/top/r3944realms/lib39/util/MathUtil.java
Normal file
195
src/main/java/top/r3944realms/lib39/util/MathUtil.java
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package top.r3944realms.lib39.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2DoubleMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import org.joml.Vector2f;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
|
||||
/**
|
||||
* 直接拿铁砧工艺的
|
||||
*/
|
||||
public class MathUtil {
|
||||
/**
|
||||
* Calc a vector2 that equals to a vector2 rotated an angle
|
||||
*
|
||||
* @param v origin vector, wont be changed
|
||||
* @param deg angle rotated, in degrees
|
||||
* @return rotated vector2
|
||||
*/
|
||||
public static Vector2f rotationDegrees(Vector2f v, float deg) {
|
||||
return rotate(v, (float) toRadians(deg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calc a vector2 that equals to a vector2 rotated an angle
|
||||
*
|
||||
* @param v origin vector, wont be changed
|
||||
* @param d angle rotated, in radians
|
||||
* @return rotated vector2
|
||||
*/
|
||||
public static Vector2f rotate(Vector2f v, float d) {
|
||||
return new Vector2f(
|
||||
(float) (v.x * cos(d) - v.y * sin(d)),
|
||||
(float) (v.x * sin(d) + v.y * cos(d))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy vector 2 f.
|
||||
*
|
||||
* @param v the v
|
||||
* @return the vector 2 f
|
||||
*/
|
||||
public static Vector2f copy(Vector2f v) {
|
||||
return new Vector2f(v.x, v.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle float.
|
||||
*
|
||||
* @param from the from
|
||||
* @param to the to
|
||||
* @return Angle in radians
|
||||
*/
|
||||
public static float angle(Vector2f from, Vector2f to) {
|
||||
return (float) ((atan2(to.y, to.x) - atan2(from.y, from.x)) % (Math.PI * 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle degrees float.
|
||||
*
|
||||
* @param from the from
|
||||
* @param to the to
|
||||
* @return Angle in degrees
|
||||
*/
|
||||
public static float angleDegrees(Vector2f from, Vector2f to) {
|
||||
return (float) toDegrees(angle(from, to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe divide float.
|
||||
*
|
||||
* @param a the a
|
||||
* @param b the b
|
||||
* @return the float
|
||||
*/
|
||||
public static float safeDivide(float a, float b) {
|
||||
if (a == b) return 1;
|
||||
return a / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is in range boolean.
|
||||
*
|
||||
* @param value the value
|
||||
* @param min the min
|
||||
* @param max the max
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean isInRange(double value, double min, double max) {
|
||||
if (min > max) {
|
||||
double min1 = min;
|
||||
min = max;
|
||||
max = min1;
|
||||
}
|
||||
|
||||
return value > min && value < max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is in range boolean.
|
||||
*
|
||||
* @param valueX the value x
|
||||
* @param valueY the value y
|
||||
* @param minX the min x
|
||||
* @param minY the min y
|
||||
* @param maxX the max x
|
||||
* @param maxY the max y
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean isInRange(double valueX, double valueY, double minX, double minY, double maxX, double maxY) {
|
||||
if (minX > maxX) {
|
||||
double minX1 = minX;
|
||||
minX = maxX;
|
||||
maxX = minX1;
|
||||
}
|
||||
if (minY > maxY) {
|
||||
double minY1 = minY;
|
||||
minY = maxY;
|
||||
maxY = minY1;
|
||||
}
|
||||
|
||||
return valueX > minX && valueX < maxX && valueY > minY && valueY < maxY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dist vec 3 i.
|
||||
*
|
||||
* @param a the a
|
||||
* @param b the b
|
||||
* @return the vec 3 i
|
||||
*/
|
||||
public static Vec3i dist(BlockPos a, BlockPos b) {
|
||||
return new Vec3i(a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets direction.
|
||||
*
|
||||
* @param from the from
|
||||
* @param to the to
|
||||
* @return the direction
|
||||
*/
|
||||
public static Direction getDirection(BlockPos from, BlockPos to) {
|
||||
return Direction.fromDelta(from.getX() - to.getX(), from.getY() - to.getY(), from.getZ() - to.getZ());
|
||||
}
|
||||
|
||||
private static final Int2DoubleMap FACTORIAL_CACHE = new Int2DoubleOpenHashMap();
|
||||
|
||||
/**
|
||||
* Factorial double.
|
||||
*
|
||||
* @param value the value
|
||||
* @return the double
|
||||
*/
|
||||
public static double factorial(int value) {
|
||||
if (value < 1) return 1;
|
||||
if (FACTORIAL_CACHE.containsKey(value)) return FACTORIAL_CACHE.get(value);
|
||||
double result = 1;
|
||||
for (int i = 2; i <= value; i++) {
|
||||
result *= i;
|
||||
}
|
||||
FACTORIAL_CACHE.put(value, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp with proportion float.
|
||||
*
|
||||
* @param value the value
|
||||
* @param min the min
|
||||
* @param max the max
|
||||
* @return the float
|
||||
*/
|
||||
public static float clampWithProportion(float value, float min, float max) {
|
||||
float length = Math.abs(max - min);
|
||||
if (length == 0) throw new IllegalArgumentException("The min value " + min + " cannot be equal to the max value" + max + "!");
|
||||
|
||||
if (value > max) {
|
||||
while (value > max + length) {
|
||||
value -= length;
|
||||
}
|
||||
return max - (max - value);
|
||||
} else if (value < min) {
|
||||
while (value < min + length) {
|
||||
value += length;
|
||||
}
|
||||
return min + (value - min);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package top.r3944realms.lib39.util.command;
|
||||
|
||||
/**
|
||||
* The type Command help helper.
|
||||
*/
|
||||
public class CommandHelpHelper {
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package top.r3944realms.lib39.util.lang;
|
||||
|
||||
/**
|
||||
* The interface Four consumer.
|
||||
*
|
||||
* @param <X> the type parameter
|
||||
* @param <Y> the type parameter
|
||||
* @param <Z> the type parameter
|
||||
* @param <W> the type parameter
|
||||
*/
|
||||
public interface FourConsumer<X, Y, Z, W> {
|
||||
/**
|
||||
* Accept.
|
||||
*
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param z the z
|
||||
* @param w the w
|
||||
*/
|
||||
void accept(X x, Y y, Z z, W w);
|
||||
|
||||
/**
|
||||
* Noop.
|
||||
*
|
||||
* @param <X> the type parameter
|
||||
* @param <Y> the type parameter
|
||||
* @param <Z> the type parameter
|
||||
* @param <W> the type parameter
|
||||
* @param x the x
|
||||
* @param y the y
|
||||
* @param z the z
|
||||
* @param w the w
|
||||
*/
|
||||
static <X, Y, Z, W> void noop(X x, Y y, Z z, W w) {
|
||||
}
|
||||
}
|
||||
|
|
@ -585,6 +585,21 @@ public class NBTWriter {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uuid value if nbt writer.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @param condition the condition
|
||||
* @return the nbt writer
|
||||
*/
|
||||
public NBTWriter uuidValueIf(String key, UUID value, boolean condition) {
|
||||
if (condition && value != null) {
|
||||
root.putUUID(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean value if nbt writer.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
package top.r3944realms.lib39.util.resolve;
|
||||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The type Entity list resolve.
|
||||
*/
|
||||
public abstract class EntityListResolve {
|
||||
/**
|
||||
* The Result.
|
||||
*/
|
||||
protected EntityListResolve.EntityResolveResult result;
|
||||
|
||||
/**
|
||||
* The type Entity resolve result.
|
||||
*/
|
||||
public static class EntityResolveResult {
|
||||
/**
|
||||
* The Entity list.
|
||||
*/
|
||||
protected final List<String> entityList = new ArrayList<>();
|
||||
/**
|
||||
* The Tag list.
|
||||
*/
|
||||
protected final List<String> tagList = new ArrayList<>();
|
||||
/**
|
||||
* The Mod list.
|
||||
*/
|
||||
protected final List<String> modList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The enum Type.
|
||||
*/
|
||||
public enum Type {
|
||||
/**
|
||||
* Entity type.
|
||||
*/
|
||||
ENTITY,
|
||||
/**
|
||||
* Tag type.
|
||||
*/
|
||||
TAG,
|
||||
/**
|
||||
* Mod type.
|
||||
*/
|
||||
MOD
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the map
|
||||
*/
|
||||
public List<String> getMap(@NotNull Type type) {
|
||||
return switch (type) {
|
||||
case ENTITY -> entityList;
|
||||
case TAG -> tagList;
|
||||
case MOD -> modList;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @param tag the tag
|
||||
* @param mod the mod
|
||||
*/
|
||||
public void update(List<String> entity, List<String> tag, List<String> mod) {
|
||||
entityList.clear();
|
||||
entityList.addAll(entity);
|
||||
tagList.clear();
|
||||
tagList.addAll(tag);
|
||||
modList.clear();
|
||||
modList.addAll(mod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve entity resolve result.
|
||||
*
|
||||
* @param configs the configs
|
||||
* @return the entity resolve result
|
||||
*/
|
||||
public EntityResolveResult resolve(@NotNull List<String> configs) {
|
||||
List<String> entityList = new ArrayList<>();
|
||||
List<String> tagList = new ArrayList<>();
|
||||
List<String> modList = new ArrayList<>();
|
||||
for (String config : configs) {
|
||||
if (!isMatch(config)) continue;
|
||||
|
||||
try {
|
||||
String[] entities = resolveEntities(config);
|
||||
for (String e : entities) {
|
||||
String trimmed = e.trim();
|
||||
if (trimmed.equals("*")) modList.add("*");
|
||||
else if (trimmed.startsWith("#")) {
|
||||
String body = trimmed.substring(1).trim();
|
||||
if (body.contains(":")) tagList.add(body);
|
||||
else modList.add(body);
|
||||
} else entityList.add(trimmed);
|
||||
}
|
||||
result.update(entityList, tagList, modList);
|
||||
} catch (NumberFormatException ex) {
|
||||
Lib39.LOGGER.error("Invalid offset config: {}", config);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is match boolean.
|
||||
*
|
||||
* @param input the input
|
||||
* @return the boolean
|
||||
*/
|
||||
protected abstract boolean isMatch(String input);
|
||||
|
||||
/**
|
||||
* Resolve entities string [ ].
|
||||
*
|
||||
* @param input the input
|
||||
* @return the string [ ]
|
||||
*/
|
||||
protected abstract String[] resolveEntities(String input);
|
||||
|
||||
/**
|
||||
* Is entity in list boolean.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the boolean
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean isEntityInList(EntityType<?> type) {
|
||||
String entityId = type.builtInRegistryHolder().key().location().toString();
|
||||
String modId = entityId.split(":")[0];
|
||||
for (String rs : result.entityList) {
|
||||
if (rs.equals(entityId)) return true;
|
||||
}
|
||||
for(String rs : result.tagList) {
|
||||
String body = rs.substring(1);
|
||||
ResourceLocation tagId = new ResourceLocation(body);
|
||||
TagKey<EntityType<?>> tag = TagKey.create(Registries.ENTITY_TYPE, tagId);
|
||||
if (type.builtInRegistryHolder().is(tag)) return true;
|
||||
}
|
||||
for(String rs : result.modList) {
|
||||
String body = rs.substring(1);
|
||||
if (!body.contains(":") && body.equals(modId)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
package top.r3944realms.lib39.util.resolve;
|
||||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The type Entity map resolve.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
*/
|
||||
public abstract class EntityMapResolve<T> {
|
||||
/**
|
||||
* The Result.
|
||||
*/
|
||||
protected EntityResolveResult<T> result;
|
||||
|
||||
/**
|
||||
* The type Entity resolve result.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
*/
|
||||
public static class EntityResolveResult<T> {
|
||||
/**
|
||||
* The Entity map.
|
||||
*/
|
||||
protected final Map<String, T> entityMap = new HashMap<>();
|
||||
/**
|
||||
* The Tag map.
|
||||
*/
|
||||
protected final Map<String, T> tagMap = new HashMap<>();
|
||||
/**
|
||||
* The Mod map.
|
||||
*/
|
||||
protected final Map<String, T> modMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The enum Type.
|
||||
*/
|
||||
public enum Type {
|
||||
/**
|
||||
* Entity type.
|
||||
*/
|
||||
ENTITY,
|
||||
/**
|
||||
* Tag type.
|
||||
*/
|
||||
TAG,
|
||||
/**
|
||||
* Mod type.
|
||||
*/
|
||||
MOD
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the map
|
||||
*/
|
||||
public Map<String, T> getMap(@NotNull Type type) {
|
||||
return switch (type) {
|
||||
case ENTITY -> entityMap;
|
||||
case TAG -> tagMap;
|
||||
case MOD -> modMap;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @param tag the tag
|
||||
* @param mod the mod
|
||||
*/
|
||||
public void update(Map<String, T> entity, Map<String, T> tag, Map<String, T> mod) {
|
||||
entityMap.clear();
|
||||
entityMap.putAll(entity);
|
||||
tagMap.clear();
|
||||
tagMap.putAll(tag);
|
||||
modMap.clear();
|
||||
modMap.putAll(mod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve entity resolve result.
|
||||
*
|
||||
* @param configs the configs
|
||||
* @return the entity resolve result
|
||||
*/
|
||||
public EntityResolveResult<T> resolve(@NotNull List<String> configs) {
|
||||
Map<String, T> entityMap = new HashMap<>();
|
||||
Map<String, T> tagMap = new HashMap<>();
|
||||
Map<String, T> modMap = new HashMap<>();
|
||||
|
||||
for (String config : configs) {
|
||||
if (!isMatch(config)) continue;
|
||||
|
||||
try {
|
||||
T t = resolveT(config);
|
||||
|
||||
String[] entities = resolveEntities(config);
|
||||
for (String e : entities) {
|
||||
String trimmed = e.trim();
|
||||
if (trimmed.equals("*")) modMap.put("*", t);
|
||||
else if (trimmed.startsWith("#")) {
|
||||
String body = trimmed.substring(1).trim();
|
||||
if (body.contains(":")) tagMap.put(body, t);
|
||||
else modMap.put(body, t);
|
||||
} else entityMap.put(trimmed, t);
|
||||
}
|
||||
result.update(entityMap, tagMap, modMap);
|
||||
} catch (NumberFormatException ex) {
|
||||
Lib39.LOGGER.error("Invalid offset config: {}", config);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is match boolean.
|
||||
*
|
||||
* @param input the input
|
||||
* @return the boolean
|
||||
*/
|
||||
protected abstract boolean isMatch(String input);
|
||||
|
||||
/**
|
||||
* Resolve t t.
|
||||
*
|
||||
* @param input the input
|
||||
* @return the t
|
||||
*/
|
||||
protected abstract T resolveT(String input);
|
||||
|
||||
/**
|
||||
* Resolve entities string [ ].
|
||||
*
|
||||
* @param input the input
|
||||
* @return the string [ ]
|
||||
*/
|
||||
protected abstract String[] resolveEntities(String input);
|
||||
/**
|
||||
* 查找实体对应的值,如果找到返回匹配结果,否则返回null
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private EntityMatchResult<T> findEntityMatch(EntityType<?> type) {
|
||||
String entityId = type.builtInRegistryHolder().key().location().toString();
|
||||
String modId = entityId.split(":")[0];
|
||||
|
||||
// 检查实体ID匹配
|
||||
for (String rs : result.entityMap.keySet()) {
|
||||
if (rs.equals(entityId)) {
|
||||
return new EntityMatchResult<>(EntityResolveResult.Type.ENTITY, rs, result.entityMap.get(rs));
|
||||
}
|
||||
}
|
||||
|
||||
// 检查标签匹配
|
||||
for (String rs : result.tagMap.keySet()) {
|
||||
String body = rs.startsWith("#") ? rs.substring(1) : rs;
|
||||
ResourceLocation tagId = new ResourceLocation(body);
|
||||
TagKey<EntityType<?>> tag = TagKey.create(Registries.ENTITY_TYPE, tagId);
|
||||
if (type.builtInRegistryHolder().is(tag)) {
|
||||
return new EntityMatchResult<>(EntityResolveResult.Type.TAG, rs, result.tagMap.get(rs));
|
||||
}
|
||||
}
|
||||
|
||||
// 检查模组匹配
|
||||
for (String rs : result.modMap.keySet()) {
|
||||
String body = rs.startsWith("#") ? rs.substring(1) : rs;
|
||||
if (!body.contains(":") && body.equals(modId)) {
|
||||
return new EntityMatchResult<>(EntityResolveResult.Type.MOD, rs, result.modMap.get(rs));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is entity t in map boolean.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean isEntityTInMap(EntityType<?> type) {
|
||||
return findEntityMatch(type) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity t in map.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the entity t in map
|
||||
*/
|
||||
public T getEntityTInMap(EntityType<?> type) {
|
||||
EntityMatchResult<T> match = findEntityMatch(type);
|
||||
return match != null ? match.value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部类,用于存储实体匹配结果
|
||||
*/
|
||||
private record EntityMatchResult<T>(EntityResolveResult.Type type, String key, T value) {
|
||||
}
|
||||
|
||||
}
|
||||
49
src/main/resources/assets/lib39/shaders/core/ring.fsh
Normal file
49
src/main/resources/assets/lib39/shaders/core/ring.fsh
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#version 150
|
||||
|
||||
in vec4 vertexColor;
|
||||
|
||||
uniform vec4 ColorModulator;
|
||||
uniform vec2 Center;
|
||||
uniform float InnerRadius;
|
||||
uniform float OuterRadius;
|
||||
uniform float AntiAliasing;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
float dist = distance(gl_FragCoord.xy, Center);
|
||||
float alpha = 0.0;
|
||||
|
||||
// 确保内外半径合理
|
||||
if (OuterRadius <= InnerRadius) {
|
||||
vec4 color = vertexColor;
|
||||
color.a = 0;
|
||||
fragColor = color;
|
||||
}
|
||||
|
||||
// 计算环形 alpha
|
||||
if (dist >= InnerRadius && dist <= OuterRadius) {
|
||||
alpha = 1.0;
|
||||
|
||||
// 内边缘抗锯齿
|
||||
if (dist < InnerRadius + AntiAliasing) {
|
||||
float fade = (dist - InnerRadius) / AntiAliasing;
|
||||
alpha *= fade;
|
||||
}
|
||||
|
||||
// 外边缘抗锯齿
|
||||
if (dist > OuterRadius - AntiAliasing) {
|
||||
float fade = 1.0 - (dist - (OuterRadius - AntiAliasing)) / AntiAliasing;
|
||||
alpha *= fade;
|
||||
}
|
||||
}
|
||||
|
||||
vec4 color = vertexColor;
|
||||
color.a *= alpha;
|
||||
|
||||
if (alpha > 0.0) {
|
||||
fragColor = color * ColorModulator;
|
||||
} else {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
44
src/main/resources/assets/lib39/shaders/core/ring.json
Normal file
44
src/main/resources/assets/lib39/shaders/core/ring.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"vertex": "position_color",
|
||||
"fragment": "lib39:ring",
|
||||
"samplers": [
|
||||
],
|
||||
"uniforms": [
|
||||
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
|
||||
{
|
||||
"name": "Center",
|
||||
"type": "float",
|
||||
"count": 2,
|
||||
"values": [
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "InnerRadius",
|
||||
"type": "float",
|
||||
"count": 1,
|
||||
"values": [
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OuterRadius",
|
||||
"type": "float",
|
||||
"count": 1,
|
||||
"values": [
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AntiAliasing",
|
||||
"type": "float",
|
||||
"count": 1,
|
||||
"values": [
|
||||
1.25
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
23
src/main/resources/assets/lib39/shaders/core/selection.fsh
Normal file
23
src/main/resources/assets/lib39/shaders/core/selection.fsh
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#version 150
|
||||
|
||||
in vec4 vertexColor;
|
||||
|
||||
uniform vec4 ColorModulator;
|
||||
uniform vec2 FramebufferSize;
|
||||
uniform vec2 Center;
|
||||
uniform float Radius;
|
||||
uniform float AntiAliasingRadius;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 fragPos = vec2(gl_FragCoord.x, FramebufferSize.y - gl_FragCoord.y);
|
||||
float distance = distance(fragPos, Center);
|
||||
vec4 color = vec4(0, 0, 0, 0);
|
||||
if (distance <= Radius) {
|
||||
color = vertexColor;
|
||||
color.a = smoothstep(Radius, 0.0, distance) * vertexColor.a;
|
||||
}
|
||||
|
||||
fragColor = color * ColorModulator;
|
||||
}
|
||||
45
src/main/resources/assets/lib39/shaders/core/selection.json
Normal file
45
src/main/resources/assets/lib39/shaders/core/selection.json
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"vertex": "position_color",
|
||||
"fragment": "lib39:selection",
|
||||
"samplers": [
|
||||
],
|
||||
"uniforms": [
|
||||
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
|
||||
{
|
||||
"name": "Center",
|
||||
"type": "float",
|
||||
"count": 2,
|
||||
"values": [
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "FramebufferSize",
|
||||
"type": "float",
|
||||
"count": 2,
|
||||
"values": [
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Radius",
|
||||
"type": "float",
|
||||
"count": 1,
|
||||
"values": [
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AntiAliasingRadius",
|
||||
"type": "float",
|
||||
"count": 1,
|
||||
"values": [
|
||||
1.5
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
src/main/resources/assets/lib39/textures/item/forge.png
Normal file
BIN
src/main/resources/assets/lib39/textures/item/forge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Loading…
Reference in New Issue
Block a user