Implement ultimate selection feature with key binding and UI
Some checks failed
build / build (push) Has been cancelled
Some checks failed
build / build (push) Has been cancelled
This commit is contained in:
@@ -38,6 +38,10 @@ public class UltcraftClient implements ClientModInitializer {
|
|||||||
Ultcraft.LOGGER.info("Ultimate activation requested!");
|
Ultcraft.LOGGER.info("Ultimate activation requested!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (UltimateKeybinding.switchUltimateMenuKey.consumeClick()) {
|
||||||
|
client.setScreen(new com.ottohg.ultcraft.client.UltimateSelectionScreen());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ultcraft.LOGGER.info("Ultcraft Client initialized successfully!");
|
Ultcraft.LOGGER.info("Ultcraft Client initialized successfully!");
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ package com.ottohg.ultcraft.client;
|
|||||||
public class ClientUltimateData {
|
public class ClientUltimateData {
|
||||||
private static float targetCharge = 0.0f;
|
private static float targetCharge = 0.0f;
|
||||||
private static float displayedCharge = 0.0f;
|
private static float displayedCharge = 0.0f;
|
||||||
|
|
||||||
|
// Animation state
|
||||||
private static long lastAnimationTime = 0;
|
private static long lastAnimationTime = 0;
|
||||||
private static boolean isAnimatingReset = false;
|
private static boolean isAnimating = false;
|
||||||
|
private static float animationStartCharge = 0.0f;
|
||||||
|
private static float animationTargetCharge = 0.0f;
|
||||||
private static final float RESET_DURATION = 500.0f; // 0.5 seconds
|
private static final float RESET_DURATION = 500.0f; // 0.5 seconds
|
||||||
|
|
||||||
public static float getCharge() {
|
public static float getCharge() {
|
||||||
@@ -16,17 +20,17 @@ public class ClientUltimateData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static float getDisplayCharge() {
|
public static float getDisplayCharge() {
|
||||||
if (isAnimatingReset) {
|
if (isAnimating) {
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
float timeDelta = currentTime - lastAnimationTime;
|
float timeDelta = currentTime - lastAnimationTime;
|
||||||
float progress = timeDelta / RESET_DURATION;
|
float progress = timeDelta / RESET_DURATION;
|
||||||
|
|
||||||
if (progress >= 1.0f) {
|
if (progress >= 1.0f) {
|
||||||
displayedCharge = targetCharge;
|
displayedCharge = animationTargetCharge;
|
||||||
isAnimatingReset = false;
|
isAnimating = false;
|
||||||
} else {
|
} else {
|
||||||
// Lerp from 100 to 0
|
// Lerp from start to target
|
||||||
displayedCharge = 100.0f * (1.0f - progress);
|
displayedCharge = animationStartCharge + (animationTargetCharge - animationStartCharge) * progress;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
displayedCharge = targetCharge;
|
displayedCharge = targetCharge;
|
||||||
@@ -37,11 +41,16 @@ public class ClientUltimateData {
|
|||||||
public static void setCharge(float charge) {
|
public static void setCharge(float charge) {
|
||||||
float newCharge = Math.min(charge, 100.0f);
|
float newCharge = Math.min(charge, 100.0f);
|
||||||
|
|
||||||
// Check if we should trigger reset animation (high charge -> low charge)
|
// Check for significant drops that should be animated
|
||||||
// Relaxed condition to catch cases where passive charge might have ticked once
|
// 1. Reset: > 90 -> < 10
|
||||||
if (targetCharge > 90.0f && newCharge < 10.0f) {
|
// 2. Switch penalty: > 15 -> ~15
|
||||||
isAnimatingReset = true;
|
if ((targetCharge > 90.0f && newCharge < 10.0f) ||
|
||||||
|
(targetCharge > 15.1f && Math.abs(newCharge - 15.0f) < 0.1f)) {
|
||||||
|
|
||||||
|
isAnimating = true;
|
||||||
lastAnimationTime = System.currentTimeMillis();
|
lastAnimationTime = System.currentTimeMillis();
|
||||||
|
animationStartCharge = displayedCharge; // Start from current visual position (usually 100 or current)
|
||||||
|
animationTargetCharge = newCharge;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetCharge = newCharge;
|
targetCharge = newCharge;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.lwjgl.glfw.GLFW;
|
|||||||
*/
|
*/
|
||||||
public class UltimateKeybinding {
|
public class UltimateKeybinding {
|
||||||
public static KeyMapping activateUltimateKey;
|
public static KeyMapping activateUltimateKey;
|
||||||
|
public static KeyMapping switchUltimateMenuKey;
|
||||||
public static final KeyMapping.Category ULTIMATE_CATEGORY = KeyMapping.Category.register(Identifier.fromNamespaceAndPath("ultcraft", "ultimate"));
|
public static final KeyMapping.Category ULTIMATE_CATEGORY = KeyMapping.Category.register(Identifier.fromNamespaceAndPath("ultcraft", "ultimate"));
|
||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
@@ -20,5 +21,12 @@ public class UltimateKeybinding {
|
|||||||
GLFW.GLFW_KEY_Z, // Default to 'Z' key
|
GLFW.GLFW_KEY_Z, // Default to 'Z' key
|
||||||
ULTIMATE_CATEGORY // Category
|
ULTIMATE_CATEGORY // Category
|
||||||
));
|
));
|
||||||
|
|
||||||
|
switchUltimateMenuKey = KeyBindingHelper.registerKeyBinding(new KeyMapping(
|
||||||
|
"key.ultcraft.switch_ultimate_menu",
|
||||||
|
InputConstants.Type.KEYSYM,
|
||||||
|
GLFW.GLFW_KEY_H,
|
||||||
|
ULTIMATE_CATEGORY
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.ottohg.ultcraft.client;
|
||||||
|
|
||||||
|
import com.ottohg.ultcraft.UltimateType;
|
||||||
|
import com.ottohg.ultcraft.network.UltimateSwitchPacket;
|
||||||
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
public class UltimateSelectionScreen extends Screen {
|
||||||
|
public UltimateSelectionScreen() {
|
||||||
|
super(Component.literal("Select Ultimate"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
|
||||||
|
int centerX = this.width / 2;
|
||||||
|
int centerY = this.height / 2;
|
||||||
|
|
||||||
|
this.addRenderableWidget(Button.builder(UltimateType.ARMED_AND_DANGEROUS.getDisplayName(), button -> {
|
||||||
|
selectUltimate(UltimateType.ARMED_AND_DANGEROUS);
|
||||||
|
})
|
||||||
|
.bounds(centerX - 100, centerY - 25, 200, 20).build());
|
||||||
|
|
||||||
|
this.addRenderableWidget(Button.builder(UltimateType.LIKE_A_RABBIT.getDisplayName(), button -> {
|
||||||
|
selectUltimate(UltimateType.LIKE_A_RABBIT);
|
||||||
|
})
|
||||||
|
.bounds(centerX - 100, centerY + 5, 200, 20).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectUltimate(UltimateType type) {
|
||||||
|
ClientPlayNetworking.send(new UltimateSwitchPacket(type.getId()));
|
||||||
|
this.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||||
|
// Draw semi-transparent background manually to avoid overlapping blur issues
|
||||||
|
guiGraphics.fill(0, 0, this.width, this.height, 0x90000000);
|
||||||
|
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, this.height / 2 - 50, 0xFFFFFF);
|
||||||
|
super.render(guiGraphics, mouseX, mouseY, partialTick);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ottohg.ultcraft;
|
package com.ottohg.ultcraft;
|
||||||
|
|
||||||
import com.ottohg.ultcraft.network.UltimateActivatePacket;
|
import com.ottohg.ultcraft.network.UltimateActivatePacket;
|
||||||
|
import com.ottohg.ultcraft.network.UltimateSwitchPacket;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||||
@@ -20,10 +21,12 @@ public class Ultcraft implements ModInitializer {
|
|||||||
|
|
||||||
// Register network packets
|
// Register network packets
|
||||||
PayloadTypeRegistry.playC2S().register(UltimateActivatePacket.TYPE, UltimateActivatePacket.CODEC);
|
PayloadTypeRegistry.playC2S().register(UltimateActivatePacket.TYPE, UltimateActivatePacket.CODEC);
|
||||||
|
PayloadTypeRegistry.playC2S().register(UltimateSwitchPacket.TYPE, UltimateSwitchPacket.CODEC);
|
||||||
PayloadTypeRegistry.playS2C().register(com.ottohg.ultcraft.network.UltimateSyncPacket.TYPE,
|
PayloadTypeRegistry.playS2C().register(com.ottohg.ultcraft.network.UltimateSyncPacket.TYPE,
|
||||||
com.ottohg.ultcraft.network.UltimateSyncPacket.CODEC);
|
com.ottohg.ultcraft.network.UltimateSyncPacket.CODEC);
|
||||||
|
|
||||||
UltimateActivatePacket.register();
|
UltimateActivatePacket.register();
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(UltimateSwitchPacket.TYPE, UltimateSwitchPacket::handle);
|
||||||
|
|
||||||
// Register server tick event for passive charging
|
// Register server tick event for passive charging
|
||||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.util.UUID;
|
|||||||
public class UltimateData {
|
public class UltimateData {
|
||||||
private static final Map<UUID, Float> ultimateCharges = new HashMap<>();
|
private static final Map<UUID, Float> ultimateCharges = new HashMap<>();
|
||||||
private static final Map<UUID, Long> lastTickTimes = new HashMap<>();
|
private static final Map<UUID, Long> lastTickTimes = new HashMap<>();
|
||||||
|
private static final Map<UUID, UltimateType> selectedUltimates = new HashMap<>();
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
private static final float PASSIVE_CHARGE_PER_MINUTE = 100.0f / 15.0f; // 100% over 15 minutes
|
private static final float PASSIVE_CHARGE_PER_MINUTE = 100.0f / 15.0f; // 100% over 15 minutes
|
||||||
@@ -77,7 +78,10 @@ public class UltimateData {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply effects: Strength 2 and Regeneration 2 for 5 seconds
|
UltimateType type = getUltimateType(player.getUUID());
|
||||||
|
|
||||||
|
if (type == UltimateType.ARMED_AND_DANGEROUS) {
|
||||||
|
// Apply effects: Strength 2 and Regeneration 2 for 10 seconds
|
||||||
Holder<MobEffect> strength = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(
|
Holder<MobEffect> strength = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(
|
||||||
BuiltInRegistries.MOB_EFFECT.getValue(Identifier.fromNamespaceAndPath("minecraft", "strength"))
|
BuiltInRegistries.MOB_EFFECT.getValue(Identifier.fromNamespaceAndPath("minecraft", "strength"))
|
||||||
);
|
);
|
||||||
@@ -87,17 +91,52 @@ public class UltimateData {
|
|||||||
|
|
||||||
player.addEffect(new MobEffectInstance(strength, 200, 1));
|
player.addEffect(new MobEffectInstance(strength, 200, 1));
|
||||||
player.addEffect(new MobEffectInstance(regeneration, 200, 1));
|
player.addEffect(new MobEffectInstance(regeneration, 200, 1));
|
||||||
|
} else if (type == UltimateType.LIKE_A_RABBIT) {
|
||||||
|
// Swiftness 2 and Jump Boost 2 for 15 seconds
|
||||||
|
Holder<MobEffect> speed = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(
|
||||||
|
BuiltInRegistries.MOB_EFFECT.getValue(Identifier.fromNamespaceAndPath("minecraft", "speed"))
|
||||||
|
);
|
||||||
|
Holder<MobEffect> jumpBoost = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(
|
||||||
|
BuiltInRegistries.MOB_EFFECT.getValue(Identifier.fromNamespaceAndPath("minecraft", "jump_boost"))
|
||||||
|
);
|
||||||
|
|
||||||
|
player.addEffect(new MobEffectInstance(speed, 300, 1));
|
||||||
|
player.addEffect(new MobEffectInstance(jumpBoost, 300, 1));
|
||||||
|
}
|
||||||
|
|
||||||
// Reset charge
|
// Reset charge
|
||||||
setCharge(player.getUUID(), 0.0f);
|
setCharge(player.getUUID(), 0.0f);
|
||||||
|
|
||||||
Ultcraft.LOGGER.info("Player {} activated ultimate!", player.getName().getString());
|
Ultcraft.LOGGER.info("Player {} activated ultimate: {}", player.getName().getString(), type);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removePlayer(UUID playerId) {
|
public static void removePlayer(UUID playerId) {
|
||||||
ultimateCharges.remove(playerId);
|
ultimateCharges.remove(playerId);
|
||||||
lastTickTimes.remove(playerId);
|
lastTickTimes.remove(playerId);
|
||||||
|
selectedUltimates.remove(playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UltimateType getUltimateType(UUID playerId) {
|
||||||
|
return selectedUltimates.getOrDefault(playerId, UltimateType.ARMED_AND_DANGEROUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void switchUltimate(ServerPlayer player, String ultimateId) {
|
||||||
|
UltimateType newType = UltimateType.fromId(ultimateId);
|
||||||
|
UltimateType currentType = getUltimateType(player.getUUID());
|
||||||
|
|
||||||
|
if (newType != currentType) {
|
||||||
|
selectedUltimates.put(player.getUUID(), newType);
|
||||||
|
float currentCharge = getCharge(player.getUUID());
|
||||||
|
if (currentCharge > 15.0f) {
|
||||||
|
setCharge(player.getUUID(), 15.0f);
|
||||||
|
// Sync charge immediately
|
||||||
|
ServerPlayNetworking.send(player, new UltimateSyncPacket(15.0f));
|
||||||
|
}
|
||||||
|
// You might want to sync the selected ultimate type to the client here as well,
|
||||||
|
// but for now, we'll assume the client knows what it selected locally or we rely on charge sync.
|
||||||
|
// If the UI needs to know the server state (e.g. after relog), we'd need another packet.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveToNBT(UUID playerId, CompoundTag tag) {
|
public static void saveToNBT(UUID playerId, CompoundTag tag) {
|
||||||
|
|||||||
33
src/main/java/com/ottohg/ultcraft/UltimateType.java
Normal file
33
src/main/java/com/ottohg/ultcraft/UltimateType.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ottohg.ultcraft;
|
||||||
|
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
public enum UltimateType {
|
||||||
|
ARMED_AND_DANGEROUS("Armed and Dangerous", Component.literal("Armed and Dangerous")),
|
||||||
|
LIKE_A_RABBIT("Like a Rabbit", Component.literal("Like a Rabbit"));
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final Component displayName;
|
||||||
|
|
||||||
|
UltimateType(String id, Component displayName) {
|
||||||
|
this.id = id;
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UltimateType fromId(String id) {
|
||||||
|
for (UltimateType type : values()) {
|
||||||
|
if (type.id.equals(id)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ARMED_AND_DANGEROUS; // Default
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.ottohg.ultcraft.network;
|
||||||
|
|
||||||
|
import com.ottohg.ultcraft.Ultcraft;
|
||||||
|
import com.ottohg.ultcraft.UltimateData;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.Identifier;
|
||||||
|
|
||||||
|
public record UltimateSwitchPacket(String ultimateId) implements CustomPacketPayload {
|
||||||
|
public static final CustomPacketPayload.Type<UltimateSwitchPacket> TYPE =
|
||||||
|
new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(Ultcraft.MOD_ID, "switch_ultimate"));
|
||||||
|
|
||||||
|
public static final StreamCodec<FriendlyByteBuf, UltimateSwitchPacket> CODEC = StreamCodec.of(
|
||||||
|
(buf, packet) -> buf.writeUtf(packet.ultimateId),
|
||||||
|
(buf) -> new UltimateSwitchPacket(buf.readUtf())
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handle(UltimateSwitchPacket packet, net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.Context context) {
|
||||||
|
context.server().execute(() -> {
|
||||||
|
UltimateData.switchUltimate(context.player(), packet.ultimateId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"key.ultcraft.activate_ultimate": "Activate Ultimate",
|
"key.ultcraft.activate_ultimate": "Activate Ultimate",
|
||||||
|
"key.ultcraft.switch_ultimate_menu": "Switch Ultimate Menu",
|
||||||
"category.ultcraft.ultimate": "Ultcraft"
|
"category.ultcraft.ultimate": "Ultcraft"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user