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!");
|
||||
}
|
||||
}
|
||||
|
||||
while (UltimateKeybinding.switchUltimateMenuKey.consumeClick()) {
|
||||
client.setScreen(new com.ottohg.ultcraft.client.UltimateSelectionScreen());
|
||||
}
|
||||
});
|
||||
|
||||
Ultcraft.LOGGER.info("Ultcraft Client initialized successfully!");
|
||||
|
||||
@@ -7,8 +7,12 @@ package com.ottohg.ultcraft.client;
|
||||
public class ClientUltimateData {
|
||||
private static float targetCharge = 0.0f;
|
||||
private static float displayedCharge = 0.0f;
|
||||
|
||||
// Animation state
|
||||
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
|
||||
|
||||
public static float getCharge() {
|
||||
@@ -16,17 +20,17 @@ public class ClientUltimateData {
|
||||
}
|
||||
|
||||
public static float getDisplayCharge() {
|
||||
if (isAnimatingReset) {
|
||||
if (isAnimating) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
float timeDelta = currentTime - lastAnimationTime;
|
||||
float progress = timeDelta / RESET_DURATION;
|
||||
|
||||
if (progress >= 1.0f) {
|
||||
displayedCharge = targetCharge;
|
||||
isAnimatingReset = false;
|
||||
displayedCharge = animationTargetCharge;
|
||||
isAnimating = false;
|
||||
} else {
|
||||
// Lerp from 100 to 0
|
||||
displayedCharge = 100.0f * (1.0f - progress);
|
||||
// Lerp from start to target
|
||||
displayedCharge = animationStartCharge + (animationTargetCharge - animationStartCharge) * progress;
|
||||
}
|
||||
} else {
|
||||
displayedCharge = targetCharge;
|
||||
@@ -37,11 +41,16 @@ public class ClientUltimateData {
|
||||
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;
|
||||
// Check for significant drops that should be animated
|
||||
// 1. Reset: > 90 -> < 10
|
||||
// 2. Switch penalty: > 15 -> ~15
|
||||
if ((targetCharge > 90.0f && newCharge < 10.0f) ||
|
||||
(targetCharge > 15.1f && Math.abs(newCharge - 15.0f) < 0.1f)) {
|
||||
|
||||
isAnimating = true;
|
||||
lastAnimationTime = System.currentTimeMillis();
|
||||
animationStartCharge = displayedCharge; // Start from current visual position (usually 100 or current)
|
||||
animationTargetCharge = newCharge;
|
||||
}
|
||||
|
||||
targetCharge = newCharge;
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.lwjgl.glfw.GLFW;
|
||||
*/
|
||||
public class UltimateKeybinding {
|
||||
public static KeyMapping activateUltimateKey;
|
||||
public static KeyMapping switchUltimateMenuKey;
|
||||
public static final KeyMapping.Category ULTIMATE_CATEGORY = KeyMapping.Category.register(Identifier.fromNamespaceAndPath("ultcraft", "ultimate"));
|
||||
|
||||
public static void register() {
|
||||
@@ -20,5 +21,12 @@ public class UltimateKeybinding {
|
||||
GLFW.GLFW_KEY_Z, // Default to 'Z' key
|
||||
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;
|
||||
|
||||
import com.ottohg.ultcraft.network.UltimateActivatePacket;
|
||||
import com.ottohg.ultcraft.network.UltimateSwitchPacket;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
@@ -20,10 +21,12 @@ public class Ultcraft implements ModInitializer {
|
||||
|
||||
// Register network packets
|
||||
PayloadTypeRegistry.playC2S().register(UltimateActivatePacket.TYPE, UltimateActivatePacket.CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(UltimateSwitchPacket.TYPE, UltimateSwitchPacket.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(com.ottohg.ultcraft.network.UltimateSyncPacket.TYPE,
|
||||
com.ottohg.ultcraft.network.UltimateSyncPacket.CODEC);
|
||||
|
||||
UltimateActivatePacket.register();
|
||||
ServerPlayNetworking.registerGlobalReceiver(UltimateSwitchPacket.TYPE, UltimateSwitchPacket::handle);
|
||||
|
||||
// Register server tick event for passive charging
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.UUID;
|
||||
public class UltimateData {
|
||||
private static final Map<UUID, Float> ultimateCharges = new HashMap<>();
|
||||
private static final Map<UUID, Long> lastTickTimes = new HashMap<>();
|
||||
private static final Map<UUID, UltimateType> selectedUltimates = new HashMap<>();
|
||||
|
||||
// Constants
|
||||
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;
|
||||
}
|
||||
|
||||
// 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(
|
||||
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(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
|
||||
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;
|
||||
}
|
||||
|
||||
public static void removePlayer(UUID playerId) {
|
||||
ultimateCharges.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) {
|
||||
|
||||
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.switch_ultimate_menu": "Switch Ultimate Menu",
|
||||
"category.ultcraft.ultimate": "Ultcraft"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user