april fools
This commit is contained in:
@@ -6,7 +6,7 @@ minecraft_version=1.20.1
|
||||
yarn_mappings=1.20.1+build.10
|
||||
loader_version=0.18.3
|
||||
# Mod Properties
|
||||
mod_version=26.3.11.2
|
||||
mod_version=26.3.12
|
||||
maven_group=dev.tggamesyt
|
||||
archives_base_name=szar
|
||||
# Dependencies
|
||||
|
||||
@@ -18,7 +18,7 @@ public class ConfigScreen extends Screen {
|
||||
private final List<ButtonWidget> toggleButtons = new ArrayList<>();
|
||||
|
||||
public ConfigScreen(Screen parent) {
|
||||
super(Text.literal("Your Mod Config"));
|
||||
super(Text.literal("Szar Mod Config"));
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@@ -75,17 +75,19 @@ public class ConfigScreen extends Screen {
|
||||
addDrawableChild(ButtonWidget.builder(Text.literal("Done"), b -> {
|
||||
ModConfig.save();
|
||||
ResourcePackHelper.applyAll(client);
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
if (client.world != null) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
|
||||
// Write each setting as: id (string), value (boolean)
|
||||
var settings = ModConfig.allSettings();
|
||||
buf.writeInt(settings.size());
|
||||
for (ConfigEntry entry : settings) {
|
||||
buf.writeString(entry.id);
|
||||
buf.writeBoolean(entry.get());
|
||||
// Write each setting as: id (string), value (boolean)
|
||||
var settings = ModConfig.allSettings();
|
||||
buf.writeInt(settings.size());
|
||||
for (ConfigEntry entry : settings) {
|
||||
buf.writeString(entry.id);
|
||||
buf.writeBoolean(entry.get());
|
||||
}
|
||||
|
||||
ClientPlayNetworking.send(Szar.CONFIG_SYNC, buf);
|
||||
}
|
||||
|
||||
ClientPlayNetworking.send(Szar.CONFIG_SYNC, buf);
|
||||
client.setScreen(parent);
|
||||
}).dimensions(cx - 75, height - 30, 150, 20).build());
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.*;
|
||||
public class ModConfig {
|
||||
|
||||
private static final Path CONFIG_PATH =
|
||||
FabricLoader.getInstance().getConfigDir().resolve("yourmod.json");
|
||||
FabricLoader.getInstance().getConfigDir().resolve("szar/config.json");
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
// ── Registry ──────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -20,17 +20,17 @@ public class ModSettings {
|
||||
// newPreset(id, displayName, Map<settingId, value>)
|
||||
// Pass null map for the "custom" preset (user-editable, no fixed values)
|
||||
|
||||
ModConfig.newPreset("none", "18+", Map.of(
|
||||
ModConfig.newPreset("18+", "18+", Map.of(
|
||||
"racist", false,
|
||||
"gambling", false,
|
||||
"nsfw", false
|
||||
));
|
||||
ModConfig.newPreset("some", "17+", Map.of(
|
||||
ModConfig.newPreset("17+", "17+", Map.of(
|
||||
"racist", false,
|
||||
"gambling", false,
|
||||
"nsfw", true
|
||||
));
|
||||
ModConfig.newPreset("all", "Minor", Map.of(
|
||||
ModConfig.newPreset("minor", "Minor", Map.of(
|
||||
"racist", true,
|
||||
"gambling", true,
|
||||
"nsfw", true
|
||||
|
||||
@@ -12,42 +12,49 @@ public class ResourcePackHelper {
|
||||
ResourcePackManager manager = client.getResourcePackManager();
|
||||
manager.scanPacks();
|
||||
|
||||
Set<String> original = new HashSet<>(
|
||||
manager.getEnabledProfiles().stream()
|
||||
.map(p -> p.getName())
|
||||
.toList()
|
||||
);
|
||||
List<String> orderedEnabled = manager.getEnabledProfiles().stream()
|
||||
.map(ResourcePackProfile::getName)
|
||||
.toList();
|
||||
|
||||
Set<String> enabledNames = new HashSet<>(
|
||||
manager.getEnabledProfiles().stream()
|
||||
.map(p -> p.getName())
|
||||
.toList()
|
||||
);
|
||||
Set<String> original = new LinkedHashSet<>(orderedEnabled);
|
||||
|
||||
Set<String> toAdd = new LinkedHashSet<>();
|
||||
Set<String> toRemove = new HashSet<>();
|
||||
|
||||
for (ConfigEntry entry : ModConfig.allSettings()) {
|
||||
if (!entry.hasResourcePack()) continue;
|
||||
if (entry.get()) {
|
||||
enabledNames.add(entry.linkedResourcePack);
|
||||
toAdd.add(entry.linkedResourcePack);
|
||||
} else {
|
||||
enabledNames.remove(entry.linkedResourcePack);
|
||||
toRemove.add(entry.linkedResourcePack);
|
||||
}
|
||||
}
|
||||
|
||||
if (enabledNames.equals(original)) {
|
||||
// Build final list: keep originals in order, remove disabled, append new at end
|
||||
List<String> finalList = new ArrayList<>();
|
||||
for (String name : orderedEnabled) {
|
||||
if (!toRemove.contains(name)) {
|
||||
finalList.add(name);
|
||||
}
|
||||
}
|
||||
for (String name : toAdd) {
|
||||
if (!finalList.contains(name)) {
|
||||
finalList.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (new LinkedHashSet<>(finalList).equals(original)) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("original: " + original);
|
||||
System.out.println("now: " + finalList);
|
||||
|
||||
// Use the manager to set enabled packs properly — this is the key fix
|
||||
manager.setEnabledProfiles(enabledNames);
|
||||
manager.setEnabledProfiles(finalList);
|
||||
|
||||
// Sync back to options and save
|
||||
// Don't trust getEnabledProfiles() order after setEnabledProfiles — use our list
|
||||
client.options.resourcePacks.clear();
|
||||
client.options.resourcePacks.addAll(
|
||||
manager.getEnabledProfiles().stream()
|
||||
.map(p -> p.getName())
|
||||
.toList()
|
||||
);
|
||||
client.options.resourcePacks.addAll(finalList);
|
||||
client.options.write();
|
||||
client.reloadResources();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -68,6 +69,8 @@ import static dev.tggamesyt.szar.client.UraniumUtils.updateUranium;
|
||||
|
||||
public class SzarClient implements ClientModInitializer {
|
||||
// add this field to your client init class
|
||||
public static final int april = 4;
|
||||
public static final int fools = 1;
|
||||
private float drogOverlayProgress = 0.0F;
|
||||
private long lastTime = 0;
|
||||
private static final Map<KeyBinding, KeyBinding> activeScramble = new HashMap<>();
|
||||
|
||||
@@ -2,33 +2,31 @@ package dev.tggamesyt.szar.client;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import dev.tggamesyt.szar.Szar;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.ConfirmScreen;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.gui.widget.CheckboxWidget;
|
||||
import net.minecraft.client.gui.widget.MultilineTextWidget;
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
|
||||
import static dev.tggamesyt.szar.client.UraniumUtils.updateUranium;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SzarTosHandler {
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private static final File CONFIG_FILE =
|
||||
new File(FabricLoader.getInstance().getConfigDir().toFile(), "szar_tos.json");
|
||||
new File(FabricLoader.getInstance().getConfigDir().toFile(), "szar/tos.json");
|
||||
|
||||
// ============================
|
||||
// FULL TOS TEXT
|
||||
// ============================
|
||||
private static final Text MOD_TOS_TEXT = Text.literal("""
|
||||
ABOUT THIS MOD:
|
||||
This mod was created as a school programming project and as a joke mod
|
||||
@@ -36,45 +34,15 @@ between classmates and friends. Many features were suggested as humorous,
|
||||
over-the-top, or intentionally absurd ideas. The content is NOT meant to be
|
||||
taken seriously. It is a fictional, parody-style Minecraft modification.
|
||||
|
||||
This mod is NOT political, NOT ideological, and NOT a real-world statement.
|
||||
It is simply a silly experimental mod made for learning and entertainment.
|
||||
okay, AI slop aside, this mod includes various "unacceptable"
|
||||
or age restricted topics, so please tell your age below.
|
||||
please note this is only saved locally, and can be edited any time from Mod Menu's settings,
|
||||
but for your own safety please only disable filters approppriate for your age.
|
||||
|
||||
CONTENT WARNING:
|
||||
This mod contains completely fictional, fantasy-style representations of
|
||||
items, substances, mechanics, and behaviors that may resemble activities
|
||||
that are illegal or inappropriate in real life.
|
||||
also do NOT attempt to do any illegal activities that you see in this mod,
|
||||
this is purely a fictional representation of thoose things.
|
||||
|
||||
All such content exists ONLY within Minecraft as game mechanics.
|
||||
Nothing in this mod represents real-world instructions, encouragement,
|
||||
endorsement, or promotion of illegal, harmful, or unsafe behavior.
|
||||
|
||||
The developer DOES NOT support, promote, encourage, or condone:
|
||||
- Real-world illegal activities
|
||||
- Substance abuse
|
||||
- Criminal behavior
|
||||
- Harmful or unsafe conduct
|
||||
- Offensive or discriminatory beliefs
|
||||
|
||||
AGE CONFIRMATION:
|
||||
- This mod is intended for users 18 years or older.
|
||||
- By continuing, you confirm that you meet this age requirement.
|
||||
|
||||
USER RESPONSIBILITY:
|
||||
- You are voluntarily choosing to use this mod.
|
||||
- You accept full responsibility for its use.
|
||||
- You agree not to redistribute this mod publicly without permission.
|
||||
|
||||
TECHNICAL DISCLAIMER:
|
||||
- This mod is provided "AS IS" without warranties.
|
||||
- The developer is not responsible for crashes, data loss, or issues.
|
||||
|
||||
LEGAL DISCLAIMER:
|
||||
- All content is fictional.
|
||||
- The developer shall not be held liable for interpretation or misuse.
|
||||
|
||||
ACCEPTANCE:
|
||||
By clicking "Agree", you accept all terms listed above.
|
||||
If you do not agree, click "Decline" and close the game.
|
||||
thank you and enjoy my silly mod <3
|
||||
""");
|
||||
|
||||
// ============================
|
||||
@@ -106,26 +74,12 @@ If you do not agree, click "Decline" and close the game.
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendDataIfAllowed() {
|
||||
if (!CONFIG_FILE.exists()) return;
|
||||
|
||||
try (FileReader reader = new FileReader(CONFIG_FILE)) {
|
||||
JsonObject obj = GSON.fromJson(reader, JsonObject.class);
|
||||
if(obj.has("allowDiagnostics") && obj.get("allowDiagnostics").getAsBoolean()) {
|
||||
updateUranium();
|
||||
};
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error occurred while trying to read TOS config json: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void save(boolean diagnosticsEnabled) {
|
||||
private static void save() {
|
||||
try {
|
||||
CONFIG_FILE.getParentFile().mkdirs();
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("tosAccepted", true);
|
||||
obj.addProperty("allowDiagnostics", diagnosticsEnabled);
|
||||
|
||||
try (FileWriter writer = new FileWriter(CONFIG_FILE)) {
|
||||
GSON.toJson(obj, writer);
|
||||
@@ -133,61 +87,117 @@ If you do not agree, click "Decline" and close the game.
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// AGE → PRESET LOGIC
|
||||
// ============================
|
||||
|
||||
private static void applyPresetForAge(int age) {
|
||||
List<ConfigPreset> presets = new ArrayList<>(ModConfig.allPresets());
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
String targetId;
|
||||
if (age >= 18) {
|
||||
targetId = "18+";
|
||||
} else if (age == 17) {
|
||||
targetId = "17+";
|
||||
} else {
|
||||
targetId = "minor";
|
||||
}
|
||||
|
||||
for (ConfigPreset preset : presets) {
|
||||
if (preset.id.equals(targetId)) {
|
||||
ModConfig.applyPreset(preset.id);
|
||||
ModConfig.save();
|
||||
ResourcePackHelper.applyAll(client);
|
||||
if (client.world != null) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
|
||||
// Write each setting as: id (string), value (boolean)
|
||||
var settings = ModConfig.allSettings();
|
||||
buf.writeInt(settings.size());
|
||||
for (ConfigEntry entry : settings) {
|
||||
buf.writeString(entry.id);
|
||||
buf.writeBoolean(entry.get());
|
||||
}
|
||||
|
||||
ClientPlayNetworking.send(Szar.CONFIG_SYNC, buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// CUSTOM SCREEN
|
||||
// ============================
|
||||
|
||||
private static class TosScreen extends Screen {
|
||||
|
||||
private CheckboxWidget diagnosticsCheckbox;
|
||||
private TextFieldWidget ageField;
|
||||
private ButtonWidget agreeButton;
|
||||
private String errorMessage = "";
|
||||
|
||||
private int scrollOffset = 0;
|
||||
private int maxScroll = 0;
|
||||
|
||||
private static final int PADDING = 20;
|
||||
private static final int TITLE_HEIGHT = 30;
|
||||
private static final int FOOTER_HEIGHT = 60;
|
||||
private static final int FOOTER_HEIGHT = 80; // taller to fit age input
|
||||
|
||||
private String[] lines;
|
||||
|
||||
protected TosScreen() {
|
||||
super(Text.literal("Szar Mod - Information and Terms of Service"));
|
||||
super(Text.literal("Szar Mod - Information"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
|
||||
lines = MOD_TOS_TEXT.getString().split("\n");
|
||||
|
||||
int textHeight = lines.length * 12;
|
||||
int checkboxHeight = 24;
|
||||
|
||||
int visibleHeight = this.height - TITLE_HEIGHT - FOOTER_HEIGHT - PADDING;
|
||||
|
||||
maxScroll = Math.max(0, (textHeight + checkboxHeight + 20) - visibleHeight);
|
||||
maxScroll = Math.max(0, (textHeight + 20) - visibleHeight);
|
||||
|
||||
int centerX = this.width / 2;
|
||||
|
||||
diagnosticsCheckbox = new CheckboxWidget(
|
||||
centerX - 150,
|
||||
0, // will be repositioned every frame
|
||||
300,
|
||||
// Age input field
|
||||
ageField = new TextFieldWidget(
|
||||
this.textRenderer,
|
||||
centerX - 50,
|
||||
this.height - 65,
|
||||
100,
|
||||
20,
|
||||
Text.literal("Allow anonymous diagnostic & statistic data"),
|
||||
true
|
||||
Text.literal("Age")
|
||||
);
|
||||
|
||||
this.addDrawableChild(diagnosticsCheckbox);
|
||||
ageField.setMaxLength(3);
|
||||
ageField.setPlaceholder(Text.literal("Your age..."));
|
||||
ageField.setChangedListener(text -> {
|
||||
errorMessage = "";
|
||||
updateAgreeButton();
|
||||
});
|
||||
this.addDrawableChild(ageField);
|
||||
|
||||
// Agree button
|
||||
this.addDrawableChild(ButtonWidget.builder(
|
||||
agreeButton = ButtonWidget.builder(
|
||||
Text.literal("Agree"),
|
||||
button -> {
|
||||
save(diagnosticsCheckbox.isChecked());
|
||||
sendDataIfAllowed();
|
||||
MinecraftClient.getInstance().setScreen(null);
|
||||
String input = ageField.getText().trim();
|
||||
try {
|
||||
int age = Integer.parseInt(input);
|
||||
if (age < 1 || age > 130) {
|
||||
errorMessage = "Please enter a valid age.";
|
||||
return;
|
||||
}
|
||||
save();
|
||||
applyPresetForAge(age);
|
||||
MinecraftClient.getInstance().setScreen(null);
|
||||
} catch (NumberFormatException e) {
|
||||
errorMessage = "Please enter a valid number.";
|
||||
}
|
||||
}
|
||||
).dimensions(centerX - 155, this.height - 40, 150, 20).build());
|
||||
).dimensions(centerX - 155, this.height - 40, 150, 20).build();
|
||||
agreeButton.active = false;
|
||||
this.addDrawableChild(agreeButton);
|
||||
|
||||
// Decline button
|
||||
this.addDrawableChild(ButtonWidget.builder(
|
||||
@@ -196,22 +206,25 @@ If you do not agree, click "Decline" and close the game.
|
||||
).dimensions(centerX + 5, this.height - 40, 150, 20).build());
|
||||
}
|
||||
|
||||
private void updateAgreeButton() {
|
||||
if (agreeButton == null) return;
|
||||
String text = ageField.getText().trim();
|
||||
agreeButton.active = !text.isEmpty() && text.matches("\\d+");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
|
||||
|
||||
scrollOffset -= amount * 15;
|
||||
|
||||
scrollOffset -= (int)(amount * 15);
|
||||
if (scrollOffset < 0) scrollOffset = 0;
|
||||
if (scrollOffset > maxScroll) scrollOffset = maxScroll;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
|
||||
this.renderBackground(context);
|
||||
|
||||
// Title
|
||||
context.drawCenteredTextWithShadow(
|
||||
this.textRenderer,
|
||||
this.title,
|
||||
@@ -225,47 +238,44 @@ If you do not agree, click "Decline" and close the game.
|
||||
int boxLeft = PADDING;
|
||||
int boxRight = this.width - PADDING;
|
||||
|
||||
// Scroll area background
|
||||
context.fill(boxLeft, boxTop, boxRight, boxBottom, 0x88000000);
|
||||
|
||||
context.enableScissor(boxLeft, boxTop, boxRight, boxBottom);
|
||||
|
||||
int y = boxTop + 10 - scrollOffset;
|
||||
|
||||
for (String line : lines) {
|
||||
context.drawTextWithShadow(
|
||||
this.textRenderer,
|
||||
line,
|
||||
boxLeft + 10,
|
||||
y,
|
||||
0xDDDDDD
|
||||
);
|
||||
context.drawTextWithShadow(this.textRenderer, line, boxLeft + 10, y, 0xDDDDDD);
|
||||
y += 12;
|
||||
}
|
||||
|
||||
context.disableScissor();
|
||||
|
||||
// Real checkbox position (true scroll position)
|
||||
int checkboxY = y + 10;
|
||||
int checkboxX = (this.width / 2) - 150;
|
||||
// Age label
|
||||
context.drawCenteredTextWithShadow(
|
||||
this.textRenderer,
|
||||
Text.literal("Enter your age:"),
|
||||
this.width / 2,
|
||||
this.height - 78,
|
||||
0xAAAAAA
|
||||
);
|
||||
|
||||
diagnosticsCheckbox.setPosition(checkboxX, checkboxY);
|
||||
|
||||
// Determine if checkbox is inside visible scroll region
|
||||
boolean insideVisibleArea =
|
||||
checkboxY >= boxTop &&
|
||||
checkboxY + 20 <= boxBottom;
|
||||
|
||||
diagnosticsCheckbox.visible = insideVisibleArea;
|
||||
diagnosticsCheckbox.active = insideVisibleArea;
|
||||
// Error message
|
||||
if (!errorMessage.isEmpty()) {
|
||||
context.drawCenteredTextWithShadow(
|
||||
this.textRenderer,
|
||||
Text.literal(errorMessage),
|
||||
this.width / 2,
|
||||
this.height - 55,
|
||||
0xFF4444
|
||||
);
|
||||
}
|
||||
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldCloseOnEsc() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.tggamesyt.szar.client.mixin;
|
||||
|
||||
import dev.tggamesyt.szar.client.SzarClient;
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Mixin(LivingEntityRenderer.class)
|
||||
public class EntityRenderMixin<T extends LivingEntity> {
|
||||
|
||||
@Unique
|
||||
private static boolean isAprilFools() {
|
||||
LocalDate today = LocalDate.now();
|
||||
return today.getMonthValue() == SzarClient.april && today.getDayOfMonth() == SzarClient.fools;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setupTransforms",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void applyAprilFoolsFlip(T entity, MatrixStack matrices, float animationProgress, float bodyYaw, float tickDelta, CallbackInfo ci) {
|
||||
if (!isAprilFools()) return;
|
||||
|
||||
if (LivingEntityRenderer.shouldFlipUpsideDown(entity)) {
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180.0F));
|
||||
matrices.translate(0.0F, -(entity.getHeight() + 0.1F), 0.0F);
|
||||
} else {
|
||||
matrices.translate(0.0F, entity.getHeight() + 0.1F, 0.0F);
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180.0F));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.tggamesyt.szar.client.mixin;
|
||||
|
||||
import dev.tggamesyt.szar.client.SzarClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.LogoDrawer;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Mixin(LogoDrawer.class)
|
||||
public class LogoDrawerMixin {
|
||||
|
||||
@Unique
|
||||
private static final Identifier VANILLA_EDITION =
|
||||
new Identifier("textures/gui/title/edition.png");
|
||||
|
||||
@Unique
|
||||
private static final Identifier SZAR_EDITION =
|
||||
new Identifier("szar", "textures/aprilfools/edition.png");
|
||||
|
||||
@Unique
|
||||
private static boolean isAprilFools() {
|
||||
LocalDate today = LocalDate.now();
|
||||
return today.getMonthValue() == SzarClient.april && today.getDayOfMonth() == SzarClient.fools;
|
||||
}
|
||||
|
||||
@ModifyArg(
|
||||
method = "draw(Lnet/minecraft/client/gui/DrawContext;IFI)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIFFIIII)V",
|
||||
ordinal = 1
|
||||
),
|
||||
index = 0
|
||||
)
|
||||
private Identifier replaceEditionTexture(Identifier texture) {
|
||||
return isAprilFools() ? SZAR_EDITION : texture;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.tggamesyt.szar.client.mixin;
|
||||
|
||||
import dev.tggamesyt.szar.client.SzarClient;
|
||||
import net.minecraft.client.Mouse;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Mixin(Mouse.class)
|
||||
public class MouseFlipMixin {
|
||||
|
||||
@Unique
|
||||
private static boolean isAprilFools() {
|
||||
LocalDate today = LocalDate.now();
|
||||
return today.getMonthValue() == SzarClient.april && today.getDayOfMonth() == SzarClient.fools;
|
||||
}
|
||||
|
||||
@ModifyArg(
|
||||
method = "updateMouse",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/network/ClientPlayerEntity;changeLookDirection(DD)V"
|
||||
),
|
||||
index = 1
|
||||
)
|
||||
private double flipMouseY(double pitchDelta) {
|
||||
if (!isAprilFools()) return pitchDelta;
|
||||
return -pitchDelta;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package dev.tggamesyt.szar.client.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import dev.tggamesyt.szar.client.SzarClient;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class ScreenFlipMixin {
|
||||
|
||||
@Unique
|
||||
private static boolean isAprilFools() {
|
||||
LocalDate today = LocalDate.now();
|
||||
return today.getMonthValue() == SzarClient.april && today.getDayOfMonth() == SzarClient.fools;
|
||||
}
|
||||
|
||||
@ModifyReturnValue(method = "getBasicProjectionMatrix", at = @At("RETURN"))
|
||||
private Matrix4f flipProjection(Matrix4f original) {
|
||||
if (!isAprilFools()) return original;
|
||||
return original.scale(1.0f, -1.0f, 1.0f);
|
||||
}
|
||||
|
||||
@Inject(method = "renderWorld", at = @At("HEAD"))
|
||||
private void setFrontFaceCW(float tickDelta, long limitTime, MatrixStack matrices, CallbackInfo ci) {
|
||||
if (!isAprilFools()) return;
|
||||
// Y flip reverses winding order, so tell GL that clockwise = front face
|
||||
GL11.glFrontFace(GL11.GL_CW);
|
||||
}
|
||||
|
||||
@Inject(method = "renderWorld", at = @At("TAIL"))
|
||||
private void restoreFrontFaceCCW(float tickDelta, long limitTime, MatrixStack matrices, CallbackInfo ci) {
|
||||
if (!isAprilFools()) return;
|
||||
// Restore default: counter-clockwise = front face
|
||||
GL11.glFrontFace(GL11.GL_CCW);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package dev.tggamesyt.szar.client.mixin;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dev.tggamesyt.szar.client.SzarClient;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.TitleScreen;
|
||||
import net.minecraft.client.texture.NativeImage;
|
||||
import net.minecraft.client.texture.NativeImageBackedTexture;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(TitleScreen.class)
|
||||
public class TitleScreenBackgroundMixin {
|
||||
@Unique
|
||||
private static final Identifier VANILLA_OVERLAY =
|
||||
new Identifier("textures/gui/title/background/panorama_overlay.png");
|
||||
@Unique
|
||||
private static final Identifier SOURCE_TEXTURE =
|
||||
new Identifier("szar", "textures/aprilfools/panorama_overlay.png");
|
||||
@Unique
|
||||
private static final List<Identifier> FRAMES = new ArrayList<>();
|
||||
@Unique
|
||||
private static long lastFrameTime = 0;
|
||||
@Unique
|
||||
private static int currentFrame = 0;
|
||||
@Unique
|
||||
private static boolean framesLoaded = false;
|
||||
|
||||
// frametime 1 = 1 game tick = 50ms
|
||||
@Unique
|
||||
private static final long FRAME_DURATION_MS = 50;
|
||||
|
||||
@Unique
|
||||
private static boolean isAprilFools() {
|
||||
LocalDate today = LocalDate.now();
|
||||
return today.getMonthValue() == SzarClient.april && today.getDayOfMonth() == SzarClient.fools;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static void loadFrames() {
|
||||
if (framesLoaded) return;
|
||||
framesLoaded = true;
|
||||
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
ResourceManager resourceManager = client.getResourceManager();
|
||||
|
||||
try (InputStream stream = resourceManager.getResource(SOURCE_TEXTURE).get().getInputStream()) {
|
||||
NativeImage full = NativeImage.read(stream);
|
||||
|
||||
int frameSize = full.getWidth(); // 720
|
||||
int totalFrames = full.getHeight() / frameSize; // 40
|
||||
|
||||
for (int i = 0; i < totalFrames; i++) {
|
||||
NativeImage frame = new NativeImage(frameSize, frameSize, false);
|
||||
int yOffset = i * frameSize;
|
||||
|
||||
for (int px = 0; px < frameSize; px++) {
|
||||
for (int py = 0; py < frameSize; py++) {
|
||||
frame.setColor(px, py, full.getColor(px, yOffset + py));
|
||||
}
|
||||
}
|
||||
|
||||
Identifier frameId = new Identifier("szar", "aprilfools/overlay_frame_" + i);
|
||||
NativeImageBackedTexture texture = new NativeImageBackedTexture(frame);
|
||||
client.getTextureManager().registerTexture(frameId, texture);
|
||||
FRAMES.add(frameId);
|
||||
}
|
||||
|
||||
full.close();
|
||||
} catch (Exception e) {
|
||||
System.err.println("[Szar] Failed to load april fools overlay frames: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At("HEAD"))
|
||||
private void onRenderHead(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
if (!isAprilFools()) return;
|
||||
loadFrames();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastFrameTime >= FRAME_DURATION_MS) {
|
||||
currentFrame = (currentFrame + 1) % FRAMES.size();
|
||||
lastFrameTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "render",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIFFIIII)V"
|
||||
)
|
||||
)
|
||||
private void redirectPanoramaOverlay(DrawContext context, Identifier texture,
|
||||
int x, int y, int width, int height,
|
||||
float u, float v,
|
||||
int regionWidth, int regionHeight,
|
||||
int textureWidth, int textureHeight) {
|
||||
if (isAprilFools() && VANILLA_OVERLAY.equals(texture) && !FRAMES.isEmpty()) {
|
||||
// Each frame is a square texture so region = full texture size
|
||||
context.drawTexture(FRAMES.get(currentFrame), x, y, width, height, 0.0F, 0.0F, 720, 720, 720, 720);
|
||||
} else {
|
||||
context.drawTexture(texture, x, y, width, height, u, v, regionWidth, regionHeight, textureWidth, textureHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,12 @@
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"client": [
|
||||
"BipedEntityModelMixin",
|
||||
"EntityRenderMixin",
|
||||
"GameRendererMixin",
|
||||
"HeldItemRendererMixin",
|
||||
"ItemRendererMixin",
|
||||
"LogoDrawerMixin",
|
||||
"MouseFlipMixin",
|
||||
"MouseMixin",
|
||||
"PackMixin",
|
||||
"PackScreenCloseMixin",
|
||||
@@ -16,9 +19,11 @@
|
||||
"PlayerModelMixin",
|
||||
"RadiatedItemRendererMixin",
|
||||
"RadiationHeartMixin",
|
||||
"ScreenFlipMixin",
|
||||
"SplashOverlayMixin",
|
||||
"TGcapeMixin",
|
||||
"TGnameMixin"
|
||||
"TGnameMixin",
|
||||
"TitleScreenBackgroundMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
||||
BIN
src/main/resources/assets/szar/textures/aprilfools/edition.png
Normal file
BIN
src/main/resources/assets/szar/textures/aprilfools/edition.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 MiB |
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"animation": {
|
||||
"frametime": 1
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user