First beta build

Most basic features work
Support for some mods
This commit is contained in:
Motschen
2021-04-25 21:47:51 +02:00
parent 3bafb61f32
commit b167872a6b
32 changed files with 1373 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
package eu.midnightdust.puzzle;
import eu.midnightdust.puzzle.screen.widget.PuzzleWidget;
import net.minecraft.text.Text;
import java.util.ArrayList;
import java.util.List;
public class PuzzleApi {
public static List<PuzzleWidget> GRAPHICS_OPTIONS = new ArrayList<>();
public static List<PuzzleWidget> MISC_OPTIONS = new ArrayList<>();
public static List<PuzzleWidget> PERFORMANCE_OPTIONS = new ArrayList<>();
public static List<PuzzleWidget> TEXTURE_OPTIONS = new ArrayList<>();
public static void addToGraphicsOptions(PuzzleWidget button) {GRAPHICS_OPTIONS.add(button);}
public static void addToMiscOptions(PuzzleWidget button) {MISC_OPTIONS.add(button);}
public static void addToPerformanceOptions(PuzzleWidget button) {PERFORMANCE_OPTIONS.add(button);}
public static void addToTextureOptions(PuzzleWidget button) {TEXTURE_OPTIONS.add(button);}
}

View File

@@ -0,0 +1,59 @@
package eu.midnightdust.puzzle;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import eu.midnightdust.puzzle.screen.widget.PuzzleWidget;
import me.lambdaurora.lambdabettergrass.LBGConfig;
import me.lambdaurora.lambdabettergrass.LambdaBetterGrass;
import me.lambdaurora.lambdynlights.DynamicLightsConfig;
import me.lambdaurora.lambdynlights.LambDynLights;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
import team.chisel.ctm.client.CTMClient;
import team.chisel.ctm.client.config.ConfigManager;
public class PuzzleClient implements ClientModInitializer {
public static final Text YES = new TranslatableText("gui.yes").formatted(Formatting.GREEN);
public static final Text NO = new TranslatableText("gui.no").formatted(Formatting.RED);
@Override
public void onInitializeClient() {
if (FabricLoader.getInstance().isModLoaded("cullleaves")) {
PuzzleApi.addToPerformanceOptions(new PuzzleWidget(Text.of("Cull Leaves"), (button) -> button.setMessage(CullLeavesConfig.enabled ? YES : NO), (button) -> {
CullLeavesConfig.enabled = !CullLeavesConfig.enabled;
CullLeavesConfig.write();
MinecraftClient.getInstance().worldRenderer.reload();
}));
}
if (FabricLoader.getInstance().isModLoaded("lambdynlights")) {
DynamicLightsConfig ldlConfig = LambDynLights.get().config;
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(new TranslatableText("lambdynlights.option.mode"), (button) -> button.setMessage(ldlConfig.getDynamicLightsMode().getTranslatedText()), (button) -> ldlConfig.setDynamicLightsMode(ldlConfig.getDynamicLightsMode().next())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(new TranslatableText("lambdynlights.option.mode").append(": ").append(new TranslatableText("lambdynlights.option.entities")), (button) -> button.setMessage(ldlConfig.hasEntitiesLightSource() ? YES : NO), (button) -> ldlConfig.setEntitiesLightSource(!ldlConfig.hasEntitiesLightSource())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(new TranslatableText("lambdynlights.option.mode").append(": ").append(new TranslatableText("lambdynlights.option.block_entities")), (button) -> button.setMessage(ldlConfig.hasBlockEntitiesLightSource() ? YES : NO), (button) -> ldlConfig.setBlockEntitiesLightSource(!ldlConfig.hasBlockEntitiesLightSource())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(new TranslatableText("lambdynlights.option.mode").append(": ").append(new TranslatableText("entity.minecraft.creeper")), (button) -> button.setMessage(ldlConfig.getCreeperLightingMode().getTranslatedText()), (button) -> ldlConfig.setCreeperLightingMode(ldlConfig.getCreeperLightingMode().next())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(new TranslatableText("lambdynlights.option.mode").append(": ").append(new TranslatableText("block.minecraft.tnt")), (button) -> button.setMessage(ldlConfig.getTntLightingMode().getTranslatedText()), (button) -> ldlConfig.setTntLightingMode(ldlConfig.getTntLightingMode().next())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(new TranslatableText("lambdynlights.option.mode").append(": ").append(new TranslatableText("lambdynlights.option.water_sensitive")), (button) -> button.setMessage(ldlConfig.hasWaterSensitiveCheck() ? YES : NO), (button) -> ldlConfig.setWaterSensitiveCheck(!ldlConfig.hasWaterSensitiveCheck())));
}
if (FabricLoader.getInstance().isModLoaded("ctm")) {
ConfigManager ctmfConfigManager = CTMClient.getConfigManager();
ConfigManager.Config ctmfConfig = CTMClient.getConfigManager().getConfig();
PuzzleApi.addToTextureOptions(new PuzzleWidget(new TranslatableText("puzzle.option.ctm"), (button) -> button.setMessage(ctmfConfig.disableCTM ? NO : YES), (button) -> {
ctmfConfig.disableCTM = !ctmfConfig.disableCTM;
ctmfConfigManager.onConfigChange();
}));
PuzzleApi.addToTextureOptions(new PuzzleWidget(new TranslatableText("puzzle.option.inside_ctm"), (button) -> button.setMessage(ctmfConfig.connectInsideCTM ? YES : NO), (button) -> {
ctmfConfig.connectInsideCTM = !ctmfConfig.connectInsideCTM;
ctmfConfigManager.onConfigChange();
}));
}
if (FabricLoader.getInstance().isModLoaded("lambdabettergrass")) {
LBGConfig lbgConfig = LambdaBetterGrass.get().config;
PuzzleApi.addToTextureOptions(new PuzzleWidget(new TranslatableText("lambdabettergrass.option.mode"), (button) -> button.setMessage(lbgConfig.getMode().getTranslatedText()), (button) -> lbgConfig.setMode(lbgConfig.getMode().next())));
PuzzleApi.addToTextureOptions(new PuzzleWidget(new TranslatableText("lambdabettergrass.option.better_snow"), (button) -> button.setMessage(lbgConfig.hasBetterLayer() ? YES : NO), (button) -> lbgConfig.setBetterLayer(!lbgConfig.hasBetterLayer())));
}
}
}

View File

@@ -0,0 +1,340 @@
package eu.midnightdust.puzzle.config;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ScreenTexts;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.*;
import net.minecraft.util.Formatting;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
// MidnightConfig v0.1.2
// Changelog:
// - The config screen no longer shows the entries of all instances of MidnightConfig
// - Compatible with servers!
/** Based on https://github.com/Minenash/TinyConfig
* Credits to Minenash - CC0-1.0 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class MidnightConfig {
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
private static final List<EntryInfo> entries = new ArrayList<>();
protected static class EntryInfo {
Field field;
Object widget;
int width;
Method dynamicTooltip;
Map.Entry<TextFieldWidget,Text> error;
Object defaultValue;
Object value;
String tempValue;
boolean inLimits = true;
String id;
}
private static final Map<String,Class> configClass = new HashMap();
private static String translationPrefix;
private static Path path;
private static final Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
.excludeFieldsWithModifiers(Modifier.PRIVATE)
.setPrettyPrinting()
.create();
public static void init(String modid, Class<?> config) {
translationPrefix = modid + ".midnightconfig.";
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
configClass.put(modid, config);
for (Field field : config.getFields()) {
Class<?> type = field.getType();
EntryInfo info = new EntryInfo();
Entry e;
try { e = field.getAnnotation(Entry.class); }
catch (Exception ignored) { continue; }
info.width = e.width();
info.field = field;
info.id = modid;
if (type == int.class) textField(info, Integer::parseInt, INTEGER_ONLY, e.min(), e.max(), true);
else if (type == double.class) textField(info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(),false);
else if (type == String.class) textField(info, String::length, null, Math.min(e.min(),0), Math.max(e.max(),1),true);
else if (type == boolean.class) {
Function<Object,Text> func = value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED);
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object, Text>>(button -> {
info.value = !(Boolean) info.value;
button.setMessage(func.apply(info.value));
}, func);
}
else if (type.isEnum()) {
List<?> values = Arrays.asList(field.getType().getEnumConstants());
Function<Object,Text> func = value -> new TranslatableText(translationPrefix + "enum." + type.getSimpleName() + "." + info.value.toString());
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object,Text>>( button -> {
int index = values.indexOf(info.value) + 1;
info.value = values.get(index >= values.size()? 0 : index);
button.setMessage(func.apply(info.value));
}, func);
}
else
continue;
entries.add(info);
try { info.defaultValue = field.get(null); }
catch (IllegalAccessException ignored) {}
try {
info.dynamicTooltip = config.getMethod(e.dynamicTooltip());
info.dynamicTooltip.setAccessible(true);
} catch (Exception ignored) {}
}
try { gson.fromJson(Files.newBufferedReader(path), config); }
catch (Exception e) { write(modid); }
for (EntryInfo info : entries) {
try {
info.value = info.field.get(null);
info.tempValue = info.value.toString();
}
catch (IllegalAccessException ignored) {}
}
}
public static void initServer(String modid, Class<?> config) {
translationPrefix = modid + ".midnightconfig.";
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
configClass.put(modid,config);
try { gson.fromJson(Files.newBufferedReader(path), config); }
catch (Exception e) { write(modid); }
for (EntryInfo info : entries) {
try {
info.value = info.field.get(null);
info.tempValue = info.value.toString();
}
catch (IllegalAccessException ignored) {}
}
}
private static void textField(EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
boolean isNumber = pattern != null;
info.widget = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
s = s.trim();
if (!(s.isEmpty() || !isNumber || pattern.matcher(s).matches()))
return false;
Number value = 0;
boolean inLimits = false;
System.out.println(((isNumber ^ s.isEmpty())));
System.out.println(!s.equals("-") && !s.equals("."));
info.error = null;
if (!(isNumber && s.isEmpty()) && !s.equals("-") && !s.equals(".")) {
value = f.apply(s);
inLimits = value.doubleValue() >= min && value.doubleValue() <= max;
info.error = inLimits? null : new AbstractMap.SimpleEntry<>(t, new LiteralText(value.doubleValue() < min ?
"§cMinimum " + (isNumber? "value" : "length") + (cast? " is " + (int)min : " is " + min) :
"§cMaximum " + (isNumber? "value" : "length") + (cast? " is " + (int)max : " is " + max)));
}
info.tempValue = s;
t.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
info.inLimits = inLimits;
b.active = entries.stream().allMatch(e -> e.inLimits);
if (inLimits)
info.value = isNumber? value : s;
return true;
};
}
public static void write(String modid) {
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
try {
if (!Files.exists(path)) Files.createFile(path);
Files.write(path, gson.toJson(configClass.get(modid).newInstance()).getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void update(String modid, Class<?> config) {
write(modid);
try { gson.fromJson(Files.newBufferedReader(path), config); }
catch (Exception e) { write(modid); }
for (EntryInfo info : entries) {
try {
info.value = info.field.get(null);
info.tempValue = info.value.toString();
}
catch (IllegalAccessException ignored) {}
}
}
@Environment(EnvType.CLIENT)
public static Screen getScreen(Screen parent, String modid) {
return new TinyConfigScreen(parent, modid);
}
@Environment(EnvType.CLIENT)
private static class TinyConfigScreen extends Screen {
protected TinyConfigScreen(Screen parent, String modid) {
super(new TranslatableText(modid + ".midnightconfig." + "title"));
this.parent = parent;
this.modid = modid;
this.translationPrefix = modid + ".midnightconfig.";
}
private final Screen parent;
private final String modid;
private final String translationPrefix;
// Real Time config update //
@Override
public void tick() {
for (EntryInfo info : entries)
try { info.field.set(null, info.value); }
catch (IllegalAccessException ignore) {}
}
@Override
protected void init() {
super.init();
this.addButton(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> {
try { gson.fromJson(Files.newBufferedReader(path), configClass.get(modid)); }
catch (Exception e) { write(modid); }
for (EntryInfo info : entries) {
try {
info.value = info.field.get(null);
info.tempValue = info.value.toString();
}
catch (IllegalAccessException ignored) {}
}
Objects.requireNonNull(client).openScreen(parent);
}));
ButtonWidget done = this.addButton(new ButtonWidget(this.width / 2 + 4, this.height - 28, 150, 20, ScreenTexts.DONE, (button) -> {
for (EntryInfo info : entries)
if (info.id.equals(modid)) {
try {
info.field.set(null, info.value);
} catch (IllegalAccessException ignore) {
}
}
write(modid);
Objects.requireNonNull(client).openScreen(parent);
}));
int y = 45;
for (EntryInfo info : entries) {
if (info.id.equals(modid)) {
addButton(new ButtonWidget(width - 155, y, 40, 20, new LiteralText("Reset").formatted(Formatting.RED), (button -> {
info.value = info.defaultValue;
info.tempValue = info.value.toString();
Objects.requireNonNull(client).openScreen(this);
})));
if (info.widget instanceof Map.Entry) {
Map.Entry<ButtonWidget.PressAction, Function<Object, Text>> widget = (Map.Entry<ButtonWidget.PressAction, Function<Object, Text>>) info.widget;
addButton(new ButtonWidget(width - 110, y, info.width, 20, widget.getValue().apply(info.value), widget.getKey()));
} else {
TextFieldWidget widget = addButton(new TextFieldWidget(textRenderer, width - 110, y, info.width, 20, null));
widget.setText(info.tempValue);
Predicate<String> processor = ((BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) info.widget).apply(widget, done);
widget.setTextPredicate(processor);
children.add(widget);
}
y += 25;
}
}
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices);
int stringWidth = (int) (title.getString().length() * 2.75f);
this.fillGradient(matrices, this.width / 2 - stringWidth, 10, this.width /2 + stringWidth, 29, -1072689136, -804253680);
this.fillGradient(matrices, 0, 35, width, this.height - 40, -1072689136, -804253680);
super.render(matrices, mouseX, mouseY, delta);
//renderTooltip(matrices, title, width/2 - stringWidth, 27);
drawCenteredText(matrices, textRenderer, title, width/2, 15, 0xFFFFFF);
int y = 40;
for (EntryInfo info : entries) {
if (info.id.equals(modid)) {
drawTextWithShadow(matrices, textRenderer, new TranslatableText(translationPrefix + info.field.getName()), 12, y + 10, 0xFFFFFF);
if (info.error != null && info.error.getKey().isMouseOver(mouseX, mouseY))
renderTooltip(matrices, info.error.getValue(), mouseX, mouseY);
else if (mouseY >= y && mouseY < (y + 25)) {
if (info.dynamicTooltip != null) {
try {
renderTooltip(matrices, (List<Text>) info.dynamicTooltip.invoke(null, entries), mouseX, mouseY);
y += 25;
continue;
} catch (Exception e) {
e.printStackTrace();
}
}
String key = translationPrefix + info.field.getName() + ".tooltip";
if (I18n.hasTranslation(key)) {
List<Text> list = new ArrayList<>();
for (String str : I18n.translate(key).split("\n"))
list.add(new LiteralText(str));
renderTooltip(matrices, list, mouseX, mouseY);
}
}
y += 25;
}
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Entry {
String dynamicTooltip() default "";
int width() default 100;
double min() default Double.MIN_NORMAL;
double max() default Double.MAX_VALUE;
}
}

View File

@@ -0,0 +1,16 @@
package eu.midnightdust.puzzle.config;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import eu.midnightdust.puzzle.screen.PuzzleOptionsScreen;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class ModMenuIntegration implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return PuzzleOptionsScreen::new;
}
}

View File

@@ -0,0 +1,29 @@
package eu.midnightdust.puzzle.mixin;
import eu.midnightdust.puzzle.screen.PuzzleOptionsScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.OptionsScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(OptionsScreen.class)
public class MixinOptionsScreen extends Screen {
protected MixinOptionsScreen(Text title) {
super(title);
}
@Inject(at = @At("TAIL"),method = "init")
public void init(CallbackInfo ci) {
PuzzleOptionsScreen puzzleScreen = new PuzzleOptionsScreen(this);
this.addButton(new ButtonWidget(this.width / 2 - 155, this.height / 6 + 144 - 6, 150, 20, new TranslatableText("puzzle.screen.title").append("..."), (button) -> {
this.client.openScreen(puzzleScreen);
}));
}
}

View File

@@ -0,0 +1,70 @@
package eu.midnightdust.puzzle.screen;
import eu.midnightdust.puzzle.screen.page.GraphicsPage;
import eu.midnightdust.puzzle.screen.page.MiscPage;
import eu.midnightdust.puzzle.screen.page.PerformancePage;
import eu.midnightdust.puzzle.screen.page.TexturesPage;
import net.coderbot.iris.gui.ShaderPackScreen;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ScreenTexts;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.TranslatableText;
import org.apache.logging.log4j.LogManager;
import java.util.Objects;
public class PuzzleOptionsScreen extends Screen {
public PuzzleOptionsScreen(Screen parent) {
super(new TranslatableText("puzzle.screen.title"));
this.parent = parent;
}
private final Screen parent;
@Override
protected void init() {
super.init();
GraphicsPage graphicsPage = new GraphicsPage(this);
MiscPage miscPage = new MiscPage(this);
PerformancePage performancePage = new PerformancePage(this);
TexturesPage texturesPage = new TexturesPage(this);
this.addButton(new ButtonWidget(this.width / 2 - 155, this.height / 6 + 48 - 6, 150, 20, graphicsPage.getTitle().copy().append("..."), (button) -> {
Objects.requireNonNull(client).openScreen(graphicsPage);
}));
this.addButton(new ButtonWidget(this.width / 2 + 5, this.height / 6 + 48 - 6, 150, 20, texturesPage.getTitle().copy().append("..."), (button) -> {
Objects.requireNonNull(client).openScreen(texturesPage);
}));
this.addButton(new ButtonWidget(this.width / 2 - 155, this.height / 6 + 72 - 6, 150, 20, performancePage.getTitle().copy().append("..."), (button) -> {
Objects.requireNonNull(client).openScreen(performancePage);
}));
this.addButton(new ButtonWidget(this.width / 2 + 5, this.height / 6 + 72 - 6, 150, 20, miscPage.getTitle().copy().append("..."), (button) -> {
Objects.requireNonNull(client).openScreen(miscPage);
}));
if (FabricLoader.getInstance().isModLoaded("iris")) {
try {
ShaderPackScreen shaderPackPage = new ShaderPackScreen(this);
this.addButton(new ButtonWidget(this.width / 2 - 155, this.height / 6 + 96 - 6, 150, 20, shaderPackPage.getTitle().copy().append("..."), (button) -> {
Objects.requireNonNull(client).openScreen(shaderPackPage);
}));
}
catch (NoClassDefFoundError e) {
LogManager.getLogger("Puzzle").info("The shaderpack selection screen is not present, not adding it.");
}
}
this.addButton(new ButtonWidget(this.width / 2 - 100, this.height / 6 + 168, 200, 20, ScreenTexts.DONE, (button) -> {
Objects.requireNonNull(client).openScreen(parent);
}));
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices);
super.render(matrices, mouseX, mouseY, delta);
drawCenteredText(matrices, textRenderer, title, width/2, 15, 0xFFFFFF);
}
}

View File

@@ -0,0 +1,46 @@
package eu.midnightdust.puzzle.screen.page;
import eu.midnightdust.puzzle.screen.widget.PuzzleOptionListWidget;
import eu.midnightdust.puzzle.screen.widget.PuzzleWidget;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ScreenTexts;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.TranslatableText;
import java.util.List;
import java.util.Objects;
public abstract class AbstractPuzzleOptionsPage extends Screen {
private PuzzleOptionListWidget list;
private final List<PuzzleWidget> options;
public AbstractPuzzleOptionsPage(Screen parent, TranslatableText title, List<PuzzleWidget> options) {
super(title);
this.parent = parent;
this.options = options;
}
private final Screen parent;
@Override
protected void init() {
this.list = new PuzzleOptionListWidget(this.client, this.width, this.height, 32, this.height - 32, 25);
list.addAll(options);
this.children.add(this.list);
super.init();
this.addButton(new ButtonWidget(this.width / 2 - 100, this.height - 28, 200, 20, ScreenTexts.DONE, (button) -> {
Objects.requireNonNull(client).openScreen(parent);
}));
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices);
this.list.render(matrices, mouseX, mouseY, delta);
drawCenteredText(matrices, textRenderer, title, width/2, 15, 0xFFFFFF);
super.render(matrices, mouseX, mouseY, delta);
}
}

View File

@@ -0,0 +1,13 @@
package eu.midnightdust.puzzle.screen.page;
import eu.midnightdust.puzzle.PuzzleApi;
import eu.midnightdust.puzzle.screen.page.AbstractPuzzleOptionsPage;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.TranslatableText;
public class GraphicsPage extends AbstractPuzzleOptionsPage {
public GraphicsPage(Screen parent) {
super(parent, new TranslatableText("puzzle.page.graphics"), PuzzleApi.GRAPHICS_OPTIONS);
}
}

View File

@@ -0,0 +1,12 @@
package eu.midnightdust.puzzle.screen.page;
import eu.midnightdust.puzzle.PuzzleApi;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.TranslatableText;
public class MiscPage extends AbstractPuzzleOptionsPage {
public MiscPage(Screen parent) {
super(parent, new TranslatableText("puzzle.page.misc"), PuzzleApi.MISC_OPTIONS);
}
}

View File

@@ -0,0 +1,12 @@
package eu.midnightdust.puzzle.screen.page;
import eu.midnightdust.puzzle.PuzzleApi;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.TranslatableText;
public class PerformancePage extends AbstractPuzzleOptionsPage {
public PerformancePage(Screen parent) {
super(parent, new TranslatableText("puzzle.page.performance"), PuzzleApi.PERFORMANCE_OPTIONS);
}
}

View File

@@ -0,0 +1,13 @@
package eu.midnightdust.puzzle.screen.page;
import eu.midnightdust.puzzle.PuzzleApi;
import eu.midnightdust.puzzle.screen.page.AbstractPuzzleOptionsPage;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.TranslatableText;
public class TexturesPage extends AbstractPuzzleOptionsPage {
public TexturesPage(Screen parent) {
super(parent, new TranslatableText("puzzle.page.textures"), PuzzleApi.TEXTURE_OPTIONS);
}
}

View File

@@ -0,0 +1,5 @@
package eu.midnightdust.puzzle.screen.widget;
public enum ButtonType {
BUTTON, SLIDER, TEXT_FIELD
}

View File

@@ -0,0 +1,19 @@
package eu.midnightdust.puzzle.screen.widget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
public class PuzzleButtonWidget extends ButtonWidget {
private final PuzzleWidget.TextAction title;
public PuzzleButtonWidget(int x, int y, int width, int height, PuzzleWidget.TextAction title, PressAction onPress) {
super(x, y, width, height, Text.of(""), onPress);
this.title = title;
}
@Override
public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) {
title.setTitle(this);
super.renderButton(matrices, mouseX, mouseY, delta);
}
}

View File

@@ -0,0 +1,94 @@
package eu.midnightdust.puzzle.screen.widget;
import com.google.common.collect.ImmutableMap;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ElementListWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import org.apache.logging.log4j.LogManager;
import java.util.*;
@Environment(EnvType.CLIENT)
public class PuzzleOptionListWidget extends ElementListWidget<PuzzleOptionListWidget.ButtonEntry> {
TextRenderer textRenderer;
public PuzzleOptionListWidget(MinecraftClient minecraftClient, int i, int j, int k, int l, int m) {
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
textRenderer = minecraftClient.textRenderer;
}
public void addButton(AbstractButtonWidget button, Text text) {
this.addEntry(ButtonEntry.create(button, text));
}
public void addAll(List<PuzzleWidget> buttons) {
for (int i = 0; i < buttons.size(); ++i) {
PuzzleWidget button = buttons.get(i);
if (button.buttonType == ButtonType.BUTTON) {
this.addButton(new PuzzleButtonWidget(this.width / 2 - 155 + 160, 0, 150, 20, button.buttonTextAction, button.onPress), button.descriptionText);
}
else if (button.buttonType == ButtonType.SLIDER) {
this.addButton(new PuzzleSliderWidget(button.min,button.max,this.width / 2 - 155 + 160, 0, 150, 20, ((TranslatableText) button.buttonText),1), button.descriptionText);
}
else if (button.buttonType == ButtonType.TEXT_FIELD) {
this.addButton(new PuzzleTextFieldWidget(textRenderer,this.width / 2 - 155 + 160, 0, 150, 20,null, button.buttonText), button.descriptionText);
}
else LogManager.getLogger("Puzzle").warn("Button " + button + " is missing the buttonType variable. This shouldn't happen!");
}
}
public int getRowWidth() {
return 400;
}
protected int getScrollbarPositionX() {
return super.getScrollbarPositionX() + 32;
}
public Optional<AbstractButtonWidget> getHoveredButton(double mouseX, double mouseY) {
for (ButtonEntry buttonEntry : this.children()) {
for (AbstractButtonWidget abstractButtonWidget : buttonEntry.buttons) {
if (abstractButtonWidget.isMouseOver(mouseX, mouseY)) {
return Optional.of(abstractButtonWidget);
}
}
}
return Optional.empty();
}
public static class ButtonEntry extends ElementListWidget.Entry<ButtonEntry> {
private static final TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
private final List<AbstractButtonWidget> buttons;
private final Map<AbstractButtonWidget, Text> buttonsWithText;
private ButtonEntry(ImmutableMap<AbstractButtonWidget, Text> buttons) {
this.buttons = buttons.keySet().asList();
this.buttonsWithText = buttons;
}
public static ButtonEntry create(AbstractButtonWidget button, Text text) {
return new ButtonEntry(ImmutableMap.of(button, text));
}
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
this.buttonsWithText.forEach((button, text) -> {
button.y = y;
button.render(matrices, mouseX, mouseY, tickDelta);
drawTextWithShadow(matrices,textRenderer, text,x+15,y+5,0xFFFFFF);
});
}
public List<? extends Element> children() {
return buttons;
}
}
}

View File

@@ -0,0 +1,29 @@
package eu.midnightdust.puzzle.screen.widget;
import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
public class PuzzleSliderWidget extends SliderWidget {
private final int min;
private final double difference;
public PuzzleSliderWidget(int min, int max, int x, int y, int width, int height, TranslatableText label, double value) {
super(x,y,width,height,label,value);
this.updateMessage();
this.min = min;
this.difference = max - min;
}
@Override
protected void updateMessage() {
Text text = new LiteralText((int) (min + this.value * difference) + "");
this.setMessage(new TranslatableText("label").append(": ").append((Text) text));
}
@Override
protected void applyValue() {
}
}

View File

@@ -0,0 +1,17 @@
package eu.midnightdust.puzzle.screen.widget;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import org.jetbrains.annotations.Nullable;
public class PuzzleTextFieldWidget extends TextFieldWidget {
private TranslatableText label;
public PuzzleTextFieldWidget(TextRenderer textRenderer, int x, int y, int width, int height, @Nullable TextFieldWidget copyFrom, Text text) {
super(textRenderer, x, y, width, height, text);
}
}

View File

@@ -0,0 +1,58 @@
package eu.midnightdust.puzzle.screen.widget;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
public class PuzzleWidget {
public ButtonType buttonType;
public int min;
public int max;
public Text descriptionText;
public Text buttonText;
public TextAction buttonTextAction;
public ButtonWidget.PressAction onPress;
public PuzzleWidget.SaveAction onSave;
/**
* Puzzle Button Widget Container
* @param descriptionText Tells the user what the option does.
* @param getTitle Function to set the text on the button.
* @param onPress Function to call when the user presses the button.
*/
public PuzzleWidget(Text descriptionText, PuzzleWidget.TextAction getTitle, ButtonWidget.PressAction onPress) {
this.buttonType = ButtonType.BUTTON;
this.descriptionText = descriptionText;
this.buttonTextAction = getTitle;
this.onPress = onPress;
}
/**
* Puzzle Slider Widget Container (WIP - Doesn't work)
*/
public PuzzleWidget(int min, int max, Text descriptionText, TranslatableText buttonText) {
this.buttonType = ButtonType.SLIDER;
this.min = min;
this.max = max;
this.descriptionText = descriptionText;
this.buttonText = buttonText;
}
/**
* Puzzle Text Field Widget Container (WIP - Doesn't work)
*/
public PuzzleWidget(Text descriptionText, Text buttonText) {
this.buttonType = ButtonType.TEXT_FIELD;
this.descriptionText = descriptionText;
this.buttonText = buttonText;
}
@Environment(EnvType.CLIENT)
public interface SaveAction {
void onSave(AbstractButtonWidget button);
}
@Environment(EnvType.CLIENT)
public interface TextAction {
void setTitle(AbstractButtonWidget button);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -0,0 +1,9 @@
{
"puzzle.screen.title":"Puzzle Settings",
"puzzle.page.graphics":"Graphics Settings",
"puzzle.page.textures":"Texture Settings",
"puzzle.page.performance":"Performance Settings",
"puzzle.page.misc":"Miscellaneous Settings",
"puzzle.option.ctm":"Connected Textures",
"puzzle.option.inside_ctm":"Connect Inside Textures"
}

Binary file not shown.

View File

@@ -0,0 +1,38 @@
{
"schemaVersion": 1,
"id": "puzzle",
"version": "${version}",
"name": "Puzzle",
"description": "Unites optifine replacement mods in a clean & vanilla-style gui",
"authors": [
"Motschen",
"TeamMidnightDust"
],
"contact": {
"homepage": "https://www.midnightdust.eu/",
"sources": "https://github.com/TeamMidnightDust/Puzzle",
"issues": "https://github.com/TeamMidnightDust/Puzzle/issues"
},
"license": "MIT",
"icon": "assets/puzzle/icon.png",
"environment": "*",
"entrypoints": {
"client": [
"eu.midnightdust.puzzle.PuzzleClient"
],
"modmenu": [
"eu.midnightdust.puzzle.config.ModMenuIntegration"
]
},
"mixins": [
"puzzle.mixins.json"
],
"depends": {
"fabric": "*"
}
}

View File

@@ -0,0 +1,11 @@
{
"required": true,
"package": "eu.midnightdust.puzzle.mixin",
"compatibilityLevel": "JAVA_8",
"client": [
"MixinOptionsScreen"
],
"injectors": {
"defaultRequire": 1
}
}