Initial commit
This commit is contained in:
45
src/client/java/com/ottohg/ultcraft/UltcraftClient.java
Normal file
45
src/client/java/com/ottohg/ultcraft/UltcraftClient.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.ottohg.ultcraft;
|
||||
|
||||
import com.ottohg.ultcraft.client.ClientUltimateData;
|
||||
import com.ottohg.ultcraft.client.UltimateHudRenderer;
|
||||
import com.ottohg.ultcraft.client.UltimateKeybinding;
|
||||
import com.ottohg.ultcraft.network.UltimateActivatePacket;
|
||||
import com.ottohg.ultcraft.network.UltimateSyncPacket;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
|
||||
public class UltcraftClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
Ultcraft.LOGGER.info("Initializing Ultcraft Client...");
|
||||
|
||||
// Register keybindings
|
||||
UltimateKeybinding.register();
|
||||
|
||||
// Register HUD renderer
|
||||
UltimateHudRenderer.register();
|
||||
|
||||
// Register client packet receiver
|
||||
ClientPlayNetworking.registerGlobalReceiver(UltimateSyncPacket.TYPE, (packet, context) -> {
|
||||
context.client().execute(() -> {
|
||||
if (context.player() != null) {
|
||||
ClientUltimateData.setCharge(packet.charge());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle keybinding presses
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
while (UltimateKeybinding.activateUltimateKey.consumeClick()) {
|
||||
if (ClientUltimateData.isReady()) {
|
||||
// Send activation packet to server
|
||||
ClientPlayNetworking.send(new UltimateActivatePacket());
|
||||
Ultcraft.LOGGER.info("Ultimate activation requested!");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ultcraft.LOGGER.info("Ultcraft Client initialized successfully!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.ottohg.ultcraft.client;
|
||||
|
||||
/**
|
||||
* Client-side storage for the local player's ultimate charge.
|
||||
* This is synced from the server.
|
||||
*/
|
||||
public class ClientUltimateData {
|
||||
private static float targetCharge = 0.0f;
|
||||
private static float displayedCharge = 0.0f;
|
||||
private static long lastAnimationTime = 0;
|
||||
private static boolean isAnimatingReset = false;
|
||||
private static final float RESET_DURATION = 500.0f; // 0.5 seconds
|
||||
|
||||
public static float getCharge() {
|
||||
return targetCharge;
|
||||
}
|
||||
|
||||
public static float getDisplayCharge() {
|
||||
if (isAnimatingReset) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
float timeDelta = currentTime - lastAnimationTime;
|
||||
float progress = timeDelta / RESET_DURATION;
|
||||
|
||||
if (progress >= 1.0f) {
|
||||
displayedCharge = targetCharge;
|
||||
isAnimatingReset = false;
|
||||
} else {
|
||||
// Lerp from 100 to 0
|
||||
displayedCharge = 100.0f * (1.0f - progress);
|
||||
}
|
||||
} else {
|
||||
displayedCharge = targetCharge;
|
||||
}
|
||||
return displayedCharge;
|
||||
}
|
||||
|
||||
public static void setCharge(float charge) {
|
||||
float newCharge = Math.min(charge, 100.0f);
|
||||
|
||||
// Check if we should trigger reset animation (high charge -> low charge)
|
||||
// Relaxed condition to catch cases where passive charge might have ticked once
|
||||
if (targetCharge > 90.0f && newCharge < 10.0f) {
|
||||
isAnimatingReset = true;
|
||||
lastAnimationTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
targetCharge = newCharge;
|
||||
}
|
||||
|
||||
public static boolean isReady() {
|
||||
return targetCharge >= 100.0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.ottohg.ultcraft.client;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
|
||||
/**
|
||||
* Renders the ultimate charge as a circular progress bar on the HUD.
|
||||
* Similar to Overwatch's ultimate indicator.
|
||||
*/
|
||||
public class UltimateHudRenderer implements HudRenderCallback {
|
||||
private static final int CIRCLE_RADIUS = 30;
|
||||
private static final int CIRCLE_THICKNESS = 4;
|
||||
private static final int HUD_X_OFFSET = 50;
|
||||
private static final int HUD_Y_OFFSET = 50;
|
||||
|
||||
// Colors
|
||||
private static final int COLOR_BACKGROUND = 0x80000000; // Semi-transparent black
|
||||
private static final int COLOR_PROGRESS = 0xFFFFD700; // Gold
|
||||
private static final int COLOR_READY = 0xFF00FF00; // Bright green
|
||||
|
||||
@Override
|
||||
public void onHudRender(GuiGraphics guiGraphics, DeltaTracker deltaTracker) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
// Don't render in F3 debug screen
|
||||
if (mc.getDebugOverlay().showDebugScreen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get charge percentage
|
||||
float charge = ClientUltimateData.getDisplayCharge();
|
||||
boolean isReady = ClientUltimateData.isReady();
|
||||
|
||||
// Calculate position (bottom left of screen)
|
||||
int screenWidth = mc.getWindow().getGuiScaledWidth();
|
||||
int screenHeight = mc.getWindow().getGuiScaledHeight();
|
||||
int centerX = HUD_X_OFFSET;
|
||||
int centerY = screenHeight - HUD_Y_OFFSET;
|
||||
|
||||
// Draw background circle
|
||||
drawCircle(guiGraphics, centerX, centerY, CIRCLE_RADIUS, COLOR_BACKGROUND, true);
|
||||
|
||||
// Draw progress arc
|
||||
int progressColor = isReady ? COLOR_READY : COLOR_PROGRESS;
|
||||
drawArc(guiGraphics, centerX, centerY, CIRCLE_RADIUS, CIRCLE_THICKNESS,
|
||||
charge / 100.0f, progressColor);
|
||||
|
||||
// Draw percentage text
|
||||
String text = String.format("%d%%", (int) charge);
|
||||
int textWidth = mc.font.width(text);
|
||||
guiGraphics.drawString(mc.font, text,
|
||||
centerX - textWidth / 2,
|
||||
centerY - mc.font.lineHeight / 2,
|
||||
progressColor, false);
|
||||
|
||||
// Draw "READY!" text if ultimate is charged
|
||||
if (isReady) {
|
||||
String readyText = "READY!";
|
||||
int readyWidth = mc.font.width(readyText);
|
||||
guiGraphics.drawString(mc.font, readyText,
|
||||
centerX - readyWidth / 2,
|
||||
centerY - CIRCLE_RADIUS - mc.font.lineHeight - 5,
|
||||
COLOR_READY, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a filled or outlined circle.
|
||||
*/
|
||||
private void drawCircle(GuiGraphics guiGraphics, int centerX, int centerY, int radius,
|
||||
int color, boolean filled) {
|
||||
float segments = 36;
|
||||
float angleStep = (float) (2 * Math.PI / segments);
|
||||
|
||||
for (int i = 0; i < segments; i++) {
|
||||
float angle1 = i * angleStep;
|
||||
float angle2 = (i + 1) * angleStep;
|
||||
|
||||
int x1 = centerX + (int) (Math.cos(angle1) * radius);
|
||||
int y1 = centerY + (int) (Math.sin(angle1) * radius);
|
||||
int x2 = centerX + (int) (Math.cos(angle2) * radius);
|
||||
int y2 = centerY + (int) (Math.sin(angle2) * radius);
|
||||
|
||||
if (filled) {
|
||||
// Draw triangle from center to edge
|
||||
guiGraphics.fill(centerX, centerY, x1, y1, color);
|
||||
guiGraphics.fill(x1, y1, x2, y2, color);
|
||||
} else {
|
||||
// Draw line segment
|
||||
drawLine(guiGraphics, x1, y1, x2, y2, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a circular arc representing progress.
|
||||
*/
|
||||
private void drawArc(GuiGraphics guiGraphics, int centerX, int centerY, int radius,
|
||||
int thickness, float progress, int color) {
|
||||
float segments = 72;
|
||||
float angleStep = (float) (2 * Math.PI / segments);
|
||||
float startAngle = -(float) Math.PI / 2; // Start at top
|
||||
|
||||
int numSegments = (int) (segments * progress);
|
||||
|
||||
for (int i = 0; i < numSegments; i++) {
|
||||
float angle1 = startAngle + i * angleStep;
|
||||
float angle2 = startAngle + (i + 1) * angleStep;
|
||||
|
||||
// Outer edge
|
||||
int x1Out = centerX + (int) (Math.cos(angle1) * radius);
|
||||
int y1Out = centerY + (int) (Math.sin(angle1) * radius);
|
||||
int x2Out = centerX + (int) (Math.cos(angle2) * radius);
|
||||
int y2Out = centerY + (int) (Math.sin(angle2) * radius);
|
||||
|
||||
// Inner edge
|
||||
int innerRadius = radius - thickness;
|
||||
int x1In = centerX + (int) (Math.cos(angle1) * innerRadius);
|
||||
int y1In = centerY + (int) (Math.sin(angle1) * innerRadius);
|
||||
int x2In = centerX + (int) (Math.cos(angle2) * innerRadius);
|
||||
int y2In = centerY + (int) (Math.sin(angle2) * innerRadius);
|
||||
|
||||
// Draw quad (two triangles)
|
||||
drawQuad(guiGraphics, x1Out, y1Out, x2Out, y2Out, x2In, y2In, x1In, y1In, color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a line between two points.
|
||||
*/
|
||||
private void drawLine(GuiGraphics guiGraphics, int x1, int y1, int x2, int y2, int color) {
|
||||
guiGraphics.fill(x1, y1, x2, y2, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a quadrilateral using two triangles.
|
||||
*/
|
||||
private void drawQuad(GuiGraphics guiGraphics, int x1, int y1, int x2, int y2,
|
||||
int x3, int y3, int x4, int y4, int color) {
|
||||
// Triangle 1
|
||||
fillTriangle(guiGraphics, x1, y1, x2, y2, x3, y3, color);
|
||||
// Triangle 2
|
||||
fillTriangle(guiGraphics, x1, y1, x3, y3, x4, y4, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills a triangle using scanline algorithm.
|
||||
*/
|
||||
private void fillTriangle(GuiGraphics guiGraphics, int x1, int y1, int x2, int y2,
|
||||
int x3, int y3, int color) {
|
||||
// Simple approximation: fill with small rectangles
|
||||
int minX = Math.min(x1, Math.min(x2, x3));
|
||||
int maxX = Math.max(x1, Math.max(x2, x3));
|
||||
int minY = Math.min(y1, Math.min(y2, y3));
|
||||
int maxY = Math.max(y1, Math.max(y2, y3));
|
||||
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
if (isPointInTriangle(x, y, x1, y1, x2, y2, x3, y3)) {
|
||||
guiGraphics.fill(x, y, x + 1, y + 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a point is inside a triangle using barycentric coordinates.
|
||||
*/
|
||||
private boolean isPointInTriangle(int px, int py, int x1, int y1, int x2, int y2,
|
||||
int x3, int y3) {
|
||||
float denominator = ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
|
||||
if (Math.abs(denominator) < 0.001f) return false;
|
||||
|
||||
float a = ((y2 - y3) * (px - x3) + (x3 - x2) * (py - y3)) / denominator;
|
||||
float b = ((y3 - y1) * (px - x3) + (x1 - x3) * (py - y3)) / denominator;
|
||||
float c = 1 - a - b;
|
||||
|
||||
return a >= 0 && a <= 1 && b >= 0 && b <= 1 && c >= 0 && c <= 1;
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
HudRenderCallback.EVENT.register(new UltimateHudRenderer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.ottohg.ultcraft.client;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
/**
|
||||
* Manages the keybinding for activating ultimates.
|
||||
*/
|
||||
public class UltimateKeybinding {
|
||||
public static KeyMapping activateUltimateKey;
|
||||
public static final KeyMapping.Category ULTIMATE_CATEGORY = KeyMapping.Category.register(Identifier.fromNamespaceAndPath("ultcraft", "ultimate"));
|
||||
|
||||
public static void register() {
|
||||
activateUltimateKey = KeyBindingHelper.registerKeyBinding(new KeyMapping(
|
||||
"key.ultcraft.activate_ultimate", // Translation key
|
||||
InputConstants.Type.KEYSYM,
|
||||
GLFW.GLFW_KEY_Z, // Default to 'Z' key
|
||||
ULTIMATE_CATEGORY // Category
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ottohg.ultcraft.mixin.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
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(Minecraft.class)
|
||||
public class ExampleClientMixin {
|
||||
@Inject(at = @At("HEAD"), method = "run")
|
||||
private void init(CallbackInfo info) {
|
||||
// This code is injected into the start of Minecraft.run()V
|
||||
}
|
||||
}
|
||||
12
src/client/resources/ultcraft.client.mixins.json
Normal file
12
src/client/resources/ultcraft.client.mixins.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "com.ottohg.ultcraft.mixin.client",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"refmap": "ultcraft.refmap.json",
|
||||
"client": [
|
||||
"ExampleClientMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
50
src/main/java/com/ottohg/ultcraft/Ultcraft.java
Normal file
50
src/main/java/com/ottohg/ultcraft/Ultcraft.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.ottohg.ultcraft;
|
||||
|
||||
import com.ottohg.ultcraft.network.UltimateActivatePacket;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Ultcraft implements ModInitializer {
|
||||
public static final String MOD_ID = "ultcraft";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
LOGGER.info("Initializing Ultcraft - Ultimate System Loading...");
|
||||
|
||||
// Register network packets
|
||||
PayloadTypeRegistry.playC2S().register(UltimateActivatePacket.TYPE, UltimateActivatePacket.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(com.ottohg.ultcraft.network.UltimateSyncPacket.TYPE,
|
||||
com.ottohg.ultcraft.network.UltimateSyncPacket.CODEC);
|
||||
|
||||
UltimateActivatePacket.register();
|
||||
|
||||
// Register server tick event for passive charging
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
|
||||
// Tick passive charge
|
||||
UltimateData.tickPassiveCharge(player.getUUID());
|
||||
|
||||
// Sync charge to client every second (20 ticks)
|
||||
if (server.getTickCount() % 20 == 0) {
|
||||
float charge = UltimateData.getCharge(player.getUUID());
|
||||
ServerPlayNetworking.send(player,
|
||||
new com.ottohg.ultcraft.network.UltimateSyncPacket(charge));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up data when player disconnects
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
||||
UltimateData.removePlayer(handler.getPlayer().getUUID());
|
||||
});
|
||||
|
||||
LOGGER.info("Ultcraft initialized successfully!");
|
||||
}
|
||||
}
|
||||
112
src/main/java/com/ottohg/ultcraft/UltimateData.java
Normal file
112
src/main/java/com/ottohg/ultcraft/UltimateData.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.ottohg.ultcraft;
|
||||
|
||||
import com.ottohg.ultcraft.network.UltimateSyncPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Manages ultimate charge data for all players.
|
||||
* Handles passive charging, damage-based charging, and ultimate activation.
|
||||
*/
|
||||
public class UltimateData {
|
||||
private static final Map<UUID, Float> ultimateCharges = new HashMap<>();
|
||||
private static final Map<UUID, Long> lastTickTimes = new HashMap<>();
|
||||
|
||||
// Constants
|
||||
private static final float PASSIVE_CHARGE_PER_MINUTE = 100.0f / 15.0f; // 100% over 15 minutes
|
||||
private static final float DAMAGE_CHARGE_RATIO = 3.0f / 6.0f; // 3% per 6 damage points (3 hearts)
|
||||
private static final float MAX_CHARGE = 100.0f;
|
||||
|
||||
public static float getCharge(UUID playerId) {
|
||||
return ultimateCharges.getOrDefault(playerId, 0.0f);
|
||||
}
|
||||
|
||||
public static void setCharge(UUID playerId, float charge) {
|
||||
ultimateCharges.put(playerId, Math.min(charge, MAX_CHARGE));
|
||||
}
|
||||
|
||||
public static void addCharge(UUID playerId, float amount) {
|
||||
float current = getCharge(playerId);
|
||||
setCharge(playerId, current + amount);
|
||||
}
|
||||
|
||||
public static void addChargeFromDamage(ServerPlayer player, float damage) {
|
||||
float chargeGain = damage * DAMAGE_CHARGE_RATIO;
|
||||
addCharge(player.getUUID(), chargeGain);
|
||||
Ultcraft.LOGGER.info("Player {} gained {}% ultimate charge from {} damage",
|
||||
player.getUUID(), String.format("%.2f", chargeGain), damage);
|
||||
|
||||
// Sync to client immediately to reduce UI delay
|
||||
ServerPlayNetworking.send(player, new UltimateSyncPacket(getCharge(player.getUUID())));
|
||||
}
|
||||
|
||||
public static void tickPassiveCharge(UUID playerId) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
Long lastTime = lastTickTimes.get(playerId);
|
||||
|
||||
if (lastTime != null) {
|
||||
long deltaMs = currentTime - lastTime;
|
||||
float deltaMinutes = deltaMs / 60000.0f;
|
||||
float chargeGain = PASSIVE_CHARGE_PER_MINUTE * deltaMinutes;
|
||||
|
||||
float currentCharge = getCharge(playerId);
|
||||
if (currentCharge < MAX_CHARGE) {
|
||||
addCharge(playerId, chargeGain);
|
||||
}
|
||||
}
|
||||
|
||||
lastTickTimes.put(playerId, currentTime);
|
||||
}
|
||||
|
||||
public static boolean isUltimateReady(UUID playerId) {
|
||||
return getCharge(playerId) >= MAX_CHARGE;
|
||||
}
|
||||
|
||||
public static boolean activateUltimate(ServerPlayer player) {
|
||||
if (!isUltimateReady(player.getUUID())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply effects: Strength 2 and Regeneration 2 for 5 seconds
|
||||
Holder<MobEffect> strength = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(
|
||||
BuiltInRegistries.MOB_EFFECT.getValue(Identifier.fromNamespaceAndPath("minecraft", "strength"))
|
||||
);
|
||||
Holder<MobEffect> regeneration = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(
|
||||
BuiltInRegistries.MOB_EFFECT.getValue(Identifier.fromNamespaceAndPath("minecraft", "regeneration"))
|
||||
);
|
||||
|
||||
player.addEffect(new MobEffectInstance(strength, 200, 1));
|
||||
player.addEffect(new MobEffectInstance(regeneration, 200, 1));
|
||||
|
||||
// Reset charge
|
||||
setCharge(player.getUUID(), 0.0f);
|
||||
|
||||
Ultcraft.LOGGER.info("Player {} activated ultimate!", player.getName().getString());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void removePlayer(UUID playerId) {
|
||||
ultimateCharges.remove(playerId);
|
||||
lastTickTimes.remove(playerId);
|
||||
}
|
||||
|
||||
public static void saveToNBT(UUID playerId, CompoundTag tag) {
|
||||
tag.putFloat("UltimateCharge", getCharge(playerId));
|
||||
}
|
||||
|
||||
public static void loadFromNBT(UUID playerId, CompoundTag tag) {
|
||||
if (tag.contains("UltimateCharge")) {
|
||||
setCharge(playerId, tag.getFloat("UltimateCharge").orElse(0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.ottohg.ultcraft.mixin;
|
||||
|
||||
import com.ottohg.ultcraft.UltimateData;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* Mixin to track damage dealt by players and charge their ultimate accordingly.
|
||||
*/
|
||||
@Mixin(LivingEntity.class)
|
||||
public class DamageTrackingMixin {
|
||||
|
||||
@Inject(method = "hurtServer", at = @At("RETURN"))
|
||||
private void onHurt(ServerLevel level, DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
|
||||
// Only process if damage was actually dealt
|
||||
if (!cir.getReturnValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the damage source is a player
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
LivingEntity target = (LivingEntity) (Object) this;
|
||||
|
||||
// Only charge from damage to other entities (not self-damage)
|
||||
if (target != player) {
|
||||
// Add charge based on damage dealt
|
||||
UltimateData.addChargeFromDamage(player, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/main/java/com/ottohg/ultcraft/mixin/ExampleMixin.java
Normal file
15
src/main/java/com/ottohg/ultcraft/mixin/ExampleMixin.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.ottohg.ultcraft.mixin;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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(MinecraftServer.class)
|
||||
public class ExampleMixin {
|
||||
@Inject(at = @At("HEAD"), method = "loadLevel")
|
||||
private void init(CallbackInfo info) {
|
||||
// This code is injected into the start of MinecraftServer.loadLevel()V
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.ottohg.ultcraft.network;
|
||||
|
||||
import com.ottohg.ultcraft.Ultcraft;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
/**
|
||||
* Packet sent from client to server to activate ultimate.
|
||||
*/
|
||||
public record UltimateActivatePacket() implements CustomPacketPayload {
|
||||
public static final CustomPacketPayload.Type<UltimateActivatePacket> TYPE =
|
||||
new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(Ultcraft.MOD_ID, "activate_ultimate"));
|
||||
|
||||
public static final StreamCodec<FriendlyByteBuf, UltimateActivatePacket> CODEC = StreamCodec.of(
|
||||
(buf, packet) -> {}, // Write (nothing to write)
|
||||
(buf) -> new UltimateActivatePacket() // Read
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
ServerPlayNetworking.registerGlobalReceiver(TYPE, (packet, context) -> {
|
||||
context.server().execute(() -> {
|
||||
com.ottohg.ultcraft.UltimateData.activateUltimate(context.player());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ottohg.ultcraft.network;
|
||||
|
||||
import com.ottohg.ultcraft.Ultcraft;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
/**
|
||||
* Packet sent from server to client to sync ultimate charge.
|
||||
*/
|
||||
public record UltimateSyncPacket(float charge) implements CustomPacketPayload {
|
||||
public static final CustomPacketPayload.Type<UltimateSyncPacket> TYPE =
|
||||
new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(Ultcraft.MOD_ID, "sync_ultimate"));
|
||||
|
||||
public static final StreamCodec<FriendlyByteBuf, UltimateSyncPacket> CODEC = StreamCodec.of(
|
||||
(buf, packet) -> buf.writeFloat(packet.charge),
|
||||
(buf) -> new UltimateSyncPacket(buf.readFloat())
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/assets/ultcraft/icon.png
Normal file
BIN
src/main/resources/assets/ultcraft/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
4
src/main/resources/assets/ultcraft/lang/en_us.json
Normal file
4
src/main/resources/assets/ultcraft/lang/en_us.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"key.ultcraft.activate_ultimate": "Activate Ultimate",
|
||||
"category.ultcraft.ultimate": "Ultcraft"
|
||||
}
|
||||
38
src/main/resources/fabric.mod.json
Normal file
38
src/main/resources/fabric.mod.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "ultcraft",
|
||||
"version": "${version}",
|
||||
"name": "Ultcraft",
|
||||
"description": "This is an example description! Tell everyone what your mod is about!",
|
||||
"authors": [
|
||||
"Me!"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://fabricmc.net/",
|
||||
"sources": "https://github.com/FabricMC/fabric-example-mod"
|
||||
},
|
||||
"license": "CC0-1.0",
|
||||
"icon": "assets/ultcraft/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"com.ottohg.ultcraft.Ultcraft"
|
||||
],
|
||||
"client": [
|
||||
"com.ottohg.ultcraft.UltcraftClient"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"ultcraft.mixins.json",
|
||||
{
|
||||
"config": "ultcraft.client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.18.4",
|
||||
"minecraft": "~1.21.11",
|
||||
"java": ">=21",
|
||||
"fabric-api": "*"
|
||||
}
|
||||
}
|
||||
16
src/main/resources/ultcraft.mixins.json
Normal file
16
src/main/resources/ultcraft.mixins.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "com.ottohg.ultcraft.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"refmap": "ultcraft.refmap.json",
|
||||
"mixins": [
|
||||
"ExampleMixin",
|
||||
"DamageTrackingMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"overwrites": {
|
||||
"requireAnnotations": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user