add "Help Up" feature for knocked players, improve execution handling, and update mod version to 1.6.0

This commit is contained in:
2026-05-23 19:37:58 +02:00
parent 25d0ae5229
commit 332b6552cf
30 changed files with 956 additions and 42 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ mod_name=Knockedback
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=MIT mod_license=MIT
# The mod version. See https://semver.org/ # The mod version. See https://semver.org/
mod_version=1.5.3 mod_version=1.6.0
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources. # This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html # See https://maven.apache.org/guides/mini/guide-naming-conventions.html
@@ -43,7 +43,8 @@ public class CarryManager {
.filter(p -> KnockedManager.isKnocked(p) .filter(p -> KnockedManager.isKnocked(p)
&& !p.getUUID().equals(carrier.getUUID()) && !p.getUUID().equals(carrier.getUUID())
&& !isBeingCarried(p.getUUID()) && !isBeingCarried(p.getUUID())
&& !PlayerExecutionHandler.isBeingPlayerExecuted(p.getUUID())) && !PlayerExecutionHandler.isBeingPlayerExecuted(p.getUUID())
&& !HelpUpHandler.isBeingHelped(p.getUUID()))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
} }
@@ -56,6 +57,7 @@ public class CarryManager {
if (knocked instanceof ServerPlayer spKnocked) { if (knocked instanceof ServerPlayer spKnocked) {
spKnocked.connection.send(mountPacket); spKnocked.connection.send(mountPacket);
} }
KnockedbackAdvancements.recordCarry(carrier);
carrier.sendSystemMessage(Component.translatable("message.knockedback.picked_up", knocked.getDisplayName())); carrier.sendSystemMessage(Component.translatable("message.knockedback.picked_up", knocked.getDisplayName()));
knocked.sendSystemMessage(Component.translatable("message.knockedback.being_carried", carrier.getDisplayName())); knocked.sendSystemMessage(Component.translatable("message.knockedback.being_carried", carrier.getDisplayName()));
} }
@@ -8,17 +8,40 @@ import java.util.UUID;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ExecutionProgressPacket { public class ExecutionProgressPacket {
public enum Action {
NONE,
EXECUTION,
HELP_UP
}
private final int timeLeft; private final int timeLeft;
private final int totalTicks;
private final Action action;
private final String targetName;
@Nullable @Nullable
private final UUID executorId; private final UUID executorId;
public ExecutionProgressPacket(int timeLeft, @Nullable UUID executorId) { public ExecutionProgressPacket(int timeLeft, @Nullable UUID executorId) {
this(timeLeft, PlayerExecutionHandler.getExecutionTime(), timeLeft > 0 ? Action.EXECUTION : Action.NONE, "", executorId);
}
public ExecutionProgressPacket(int timeLeft, int totalTicks, Action action, @Nullable UUID executorId) {
this(timeLeft, totalTicks, action, "", executorId);
}
public ExecutionProgressPacket(int timeLeft, int totalTicks, Action action, String targetName, @Nullable UUID executorId) {
this.timeLeft = timeLeft; this.timeLeft = timeLeft;
this.totalTicks = totalTicks;
this.action = timeLeft > 0 ? action : Action.NONE;
this.targetName = timeLeft > 0 ? targetName : "";
this.executorId = executorId; this.executorId = executorId;
} }
public static void encode(ExecutionProgressPacket msg, FriendlyByteBuf buf) { public static void encode(ExecutionProgressPacket msg, FriendlyByteBuf buf) {
buf.writeInt(msg.timeLeft); buf.writeInt(msg.timeLeft);
buf.writeInt(msg.totalTicks);
buf.writeEnum(msg.action);
buf.writeUtf(msg.targetName);
buf.writeBoolean(msg.executorId != null); buf.writeBoolean(msg.executorId != null);
if (msg.executorId != null) { if (msg.executorId != null) {
buf.writeUUID(msg.executorId); buf.writeUUID(msg.executorId);
@@ -27,13 +50,16 @@ public class ExecutionProgressPacket {
public static ExecutionProgressPacket decode(FriendlyByteBuf buf) { public static ExecutionProgressPacket decode(FriendlyByteBuf buf) {
int timeLeft = buf.readInt(); int timeLeft = buf.readInt();
int totalTicks = buf.readInt();
Action action = buf.readEnum(Action.class);
String targetName = buf.readUtf();
UUID executorId = buf.readBoolean() ? buf.readUUID() : null; UUID executorId = buf.readBoolean() ? buf.readUUID() : null;
return new ExecutionProgressPacket(timeLeft, executorId); return new ExecutionProgressPacket(timeLeft, totalTicks, action, targetName, executorId);
} }
public static void handle(ExecutionProgressPacket msg, Supplier<NetworkEvent.Context> ctx) { public static void handle(ExecutionProgressPacket msg, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> { ctx.get().enqueueWork(() -> {
net.fretux.knockedback.client.ClientExecutionState.setExecution(msg.timeLeft, msg.executorId); net.fretux.knockedback.client.ClientExecutionState.setProgress(msg.timeLeft, msg.totalTicks, msg.action, msg.targetName, msg.executorId);
}); });
ctx.get().setPacketHandled(true); ctx.get().setPacketHandled(true);
} }
@@ -0,0 +1,211 @@
package net.fretux.knockedback;
import net.fretux.knockedback.config.Config;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.PacketDistributor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Mod.EventBusSubscriber(modid = KnockedBack.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class HelpUpHandler {
public static final double HELP_UP_RANGE = 2.0;
private static final Map<UUID, HelpUpAttempt> helpUpAttempts = new HashMap<>();
public static int getHelpUpTime() {
return Config.COMMON.helpUpTime.get();
}
public static boolean isBeingHelped(UUID knockedId) {
return helpUpAttempts.containsKey(knockedId);
}
public static boolean isHelping(UUID helperId) {
return helpUpAttempts.values().stream().anyMatch(attempt -> attempt.helperId.equals(helperId));
}
public static void startHelpUp(ServerPlayer helper) {
if (!Config.COMMON.helpUpEnabled.get()) return;
if (KnockedManager.isKnocked(helper)) {
helper.sendSystemMessage(Component.translatable("message.knockedback.cannot_help_while_knocked"));
return;
}
if (PlayerExecutionHandler.isExecuting(helper.getUUID())) return;
if (isHelping(helper.getUUID())) return;
Player target = findNearestKnocked(helper);
if (target == null) return;
UUID knockedId = target.getUUID();
if (CarryManager.isBeingCarried(knockedId)) {
helper.sendSystemMessage(Component.translatable("message.knockedback.cannot_help_carried_player"));
return;
}
if (helpUpAttempts.containsKey(knockedId)) return;
helpUpAttempts.put(knockedId, new HelpUpAttempt(helper.getUUID()));
helper.sendSystemMessage(Component.translatable("message.knockedback.help_up_started", target.getDisplayName()));
target.sendSystemMessage(Component.translatable("message.knockedback.being_helped", helper.getDisplayName()));
}
public static void cancelHelpUp(UUID knockedId) {
HelpUpAttempt attempt = helpUpAttempts.remove(knockedId);
if (attempt == null) return;
ServerPlayer knocked = getPlayerByUuid(knockedId);
if (knocked != null) {
knocked.sendSystemMessage(Component.translatable("message.knockedback.help_up_interrupted"));
clearProgress(knocked);
}
ServerPlayer helper = getPlayerByUuid(attempt.helperId);
if (helper != null) {
helper.sendSystemMessage(Component.translatable("message.knockedback.help_up_interrupted_helper"));
clearProgress(helper);
}
}
public static void cancelHelpUpByHelper(UUID helperId) {
List<UUID> toCancel = helpUpAttempts.entrySet().stream()
.filter(entry -> entry.getValue().helperId.equals(helperId))
.map(Map.Entry::getKey)
.toList();
for (UUID knockedId : toCancel) {
cancelHelpUp(knockedId);
}
}
@SubscribeEvent
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
if (event.side.isServer() && event.phase == TickEvent.Phase.END && event.player instanceof ServerPlayer sp && isHelping(sp.getUUID())) {
sp.setDeltaMovement(0, 0, 0);
sp.setSprinting(false);
}
}
@SubscribeEvent
public static void onAttackEntity(AttackEntityEvent event) {
if (event.getEntity() instanceof ServerPlayer sp && isHelping(sp.getUUID())) {
event.setCanceled(true);
}
}
@SubscribeEvent
public static void onLivingHurt(LivingHurtEvent event) {
if (!(event.getEntity() instanceof ServerPlayer hurtPlayer)) return;
cancelHelpUpByHelper(hurtPlayer.getUUID());
}
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase != TickEvent.Phase.END || !event.side.isServer()) return;
Map<UUID, HelpUpAttempt> updated = new HashMap<>();
List<UUID> toCancel = new ArrayList<>();
List<UUID> toComplete = new ArrayList<>();
for (Map.Entry<UUID, HelpUpAttempt> entry : helpUpAttempts.entrySet()) {
UUID knockedId = entry.getKey();
HelpUpAttempt attempt = entry.getValue();
ServerPlayer knocked = getPlayerByUuid(knockedId);
ServerPlayer helper = getPlayerByUuid(attempt.helperId);
if (knocked == null || helper == null || !KnockedManager.isKnocked(knocked)) {
toCancel.add(knockedId);
continue;
}
if (CarryManager.isBeingCarried(knockedId)
|| PlayerExecutionHandler.isBeingPlayerExecuted(knockedId)
|| MobKillHandler.isBeingMobExecuted(knockedId)
|| helper.distanceTo(knocked) > HELP_UP_RANGE) {
toCancel.add(knockedId);
continue;
}
attempt.timeLeft--;
sendProgress(helper, knocked, attempt.timeLeft);
sendProgress(knocked, knocked, attempt.timeLeft);
if (attempt.timeLeft <= 0) {
toComplete.add(knockedId);
} else {
updated.put(knockedId, attempt);
}
}
for (UUID knockedId : toCancel) {
cancelHelpUp(knockedId);
}
for (UUID knockedId : toComplete) {
HelpUpAttempt attempt = helpUpAttempts.get(knockedId);
if (attempt == null) continue;
ServerPlayer knocked = getPlayerByUuid(knockedId);
ServerPlayer helper = getPlayerByUuid(attempt.helperId);
if (knocked == null || helper == null) {
cancelHelpUp(knockedId);
continue;
}
completeHelpUp(helper, knocked);
}
helpUpAttempts.clear();
helpUpAttempts.putAll(updated);
}
private static void completeHelpUp(ServerPlayer helper, ServerPlayer knocked) {
helpUpAttempts.remove(knocked.getUUID());
KnockedManager.removeKnockedState(knocked);
knocked.heal(Config.COMMON.helpUpHealAmount.get().floatValue());
clearProgress(helper);
clearProgress(knocked);
KnockedbackAdvancements.recordHelpUp(helper);
KnockedbackAdvancements.award(knocked, KnockedbackAdvancements.BE_HELPED_UP);
helper.sendSystemMessage(Component.translatable("message.knockedback.you_helped_up", knocked.getDisplayName()));
knocked.sendSystemMessage(Component.translatable("message.knockedback.you_were_helped_up", helper.getDisplayName()));
}
private static void sendProgress(ServerPlayer player, ServerPlayer target, int timeLeft) {
NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> player),
new ExecutionProgressPacket(timeLeft, getHelpUpTime(), ExecutionProgressPacket.Action.HELP_UP, target.getDisplayName().getString(), null)
);
}
private static void clearProgress(ServerPlayer player) {
NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> player),
new ExecutionProgressPacket(0, 1, ExecutionProgressPacket.Action.NONE, null)
);
}
private static Player findNearestKnocked(ServerPlayer helper) {
AABB box = helper.getBoundingBox().inflate(HELP_UP_RANGE);
return helper
.getCommandSenderWorld()
.getEntitiesOfClass(Player.class, box).stream()
.filter(player -> KnockedManager.isKnocked(player)
&& !player.getUUID().equals(helper.getUUID())
&& !CarryManager.isBeingCarried(player.getUUID())
&& !PlayerExecutionHandler.isBeingPlayerExecuted(player.getUUID())
&& !MobKillHandler.isBeingMobExecuted(player.getUUID())
&& !isBeingHelped(player.getUUID()))
.findFirst()
.orElse(null);
}
private static ServerPlayer getPlayerByUuid(UUID uuid) {
MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer();
return NetworkHandlerHelper.getPlayerByUuid(server, uuid);
}
private static class HelpUpAttempt {
private final UUID helperId;
private int timeLeft;
private HelpUpAttempt(UUID helperId) {
this.helperId = helperId;
this.timeLeft = getHelpUpTime();
}
}
}
@@ -0,0 +1,36 @@
package net.fretux.knockedback;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public class HelpUpKnockedPacket {
private final boolean helping;
public HelpUpKnockedPacket(boolean helping) {
this.helping = helping;
}
public static void encode(HelpUpKnockedPacket msg, FriendlyByteBuf buf) {
buf.writeBoolean(msg.helping);
}
public static HelpUpKnockedPacket decode(FriendlyByteBuf buf) {
return new HelpUpKnockedPacket(buf.readBoolean());
}
public static void handle(HelpUpKnockedPacket msg, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ServerPlayer helper = ctx.get().getSender();
if (helper == null) return;
if (msg.helping) {
HelpUpHandler.startHelpUp(helper);
} else {
HelpUpHandler.cancelHelpUpByHelper(helper.getUUID());
}
});
ctx.get().setPacketHandled(true);
}
}
@@ -43,6 +43,9 @@ public class KnockedManager {
knockedEntities.put(entity.getUUID(), getKnockedDuration()); knockedEntities.put(entity.getUUID(), getKnockedDuration());
entity.setHealth(1.0F); entity.setHealth(1.0F);
applyKnockedEffects((Player) entity); applyKnockedEffects((Player) entity);
if (entity instanceof ServerPlayer sp) {
KnockedbackAdvancements.recordKnocked(sp);
}
} }
} }
@@ -69,6 +72,7 @@ public class KnockedManager {
knockedEntities.remove(entity.getUUID()); knockedEntities.remove(entity.getUUID());
setGripped(entity, false); setGripped(entity, false);
MobKillHandler.clearKillAttempt(entity.getUUID()); MobKillHandler.clearKillAttempt(entity.getUUID());
HelpUpHandler.cancelHelpUp(entity.getUUID());
entity.setPose(Pose.STANDING); entity.setPose(Pose.STANDING);
if (entity instanceof ServerPlayer sp) { if (entity instanceof ServerPlayer sp) {
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
@@ -118,7 +122,8 @@ public class KnockedManager {
continue; continue;
} }
if (MobKillHandler.isBeingMobExecuted(playerId) if (MobKillHandler.isBeingMobExecuted(playerId)
|| PlayerExecutionHandler.isBeingPlayerExecuted(playerId)) { || PlayerExecutionHandler.isBeingPlayerExecuted(playerId)
|| HelpUpHandler.isBeingHelped(playerId)) {
ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId);
if (p != null) { if (p != null) {
applyKnockedEffects(p); applyKnockedEffects(p);
@@ -206,6 +211,8 @@ public class KnockedManager {
knockedEntities.remove(playerId); knockedEntities.remove(playerId);
grippedEntities.remove(playerId); grippedEntities.remove(playerId);
MobKillHandler.clearKillAttempt(playerId); MobKillHandler.clearKillAttempt(playerId);
HelpUpHandler.cancelHelpUp(playerId);
HelpUpHandler.cancelHelpUpByHelper(playerId);
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> sp), PacketDistributor.PLAYER.with(() -> sp),
new KnockedTimePacket(0) new KnockedTimePacket(0)
@@ -220,11 +227,17 @@ public class KnockedManager {
); );
} }
@SubscribeEvent
public static void onPlayerClone(PlayerEvent.Clone event) {
KnockedbackAdvancements.copyCounts(event.getOriginal(), event.getEntity());
}
@SubscribeEvent @SubscribeEvent
public static void onPlayerLoggedOut(PlayerLoggedOutEvent event) { public static void onPlayerLoggedOut(PlayerLoggedOutEvent event) {
if (!(event.getEntity() instanceof ServerPlayer sp)) { if (!(event.getEntity() instanceof ServerPlayer sp)) {
return; return;
} }
HelpUpHandler.cancelHelpUpByHelper(sp.getUUID());
if (!isKnocked(sp)) { if (!isKnocked(sp)) {
return; return;
} }
@@ -0,0 +1,98 @@
package net.fretux.knockedback;
import net.minecraft.advancements.Advancement;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
public class KnockedbackAdvancements {
private static final String CRITERION = "triggered";
private static final String DATA_KEY = KnockedBack.MOD_ID + ".advancement_counts";
public static final ResourceLocation KNOCKED =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "knocked");
public static final ResourceLocation KNOCKED_10 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "knocked_10");
public static final ResourceLocation KNOCKED_50 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "knocked_50");
public static final ResourceLocation EXECUTE_PLAYER =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "execute_player");
public static final ResourceLocation EXECUTE_PLAYER_10 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "execute_player_10");
public static final ResourceLocation EXECUTE_PLAYER_100 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "execute_player_100");
public static final ResourceLocation HELP_UP =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "help_up");
public static final ResourceLocation HELP_UP_10 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "help_up_10");
public static final ResourceLocation HELP_UP_50 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "help_up_50");
public static final ResourceLocation BE_HELPED_UP =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "be_helped_up");
public static final ResourceLocation CARRY_PLAYER =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "carry_player");
public static final ResourceLocation CARRY_PLAYER_10 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "carry_player_10");
public static final ResourceLocation CARRY_PLAYER_50 =
ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "carry_player_50");
public static void award(ServerPlayer player, ResourceLocation advancementId) {
MinecraftServer server = player.getServer();
if (server == null) return;
Advancement advancement = server.getAdvancements().getAdvancement(advancementId);
if (advancement == null) return;
player.getAdvancements().award(advancement, CRITERION);
}
public static void recordKnocked(ServerPlayer player) {
award(player, KNOCKED);
int count = increment(player, "knocked");
awardAt(player, count, 10, KNOCKED_10);
awardAt(player, count, 50, KNOCKED_50);
}
public static void recordExecution(ServerPlayer player) {
award(player, EXECUTE_PLAYER);
int count = increment(player, "executions");
awardAt(player, count, 10, EXECUTE_PLAYER_10);
awardAt(player, count, 100, EXECUTE_PLAYER_100);
}
public static void recordHelpUp(ServerPlayer player) {
award(player, HELP_UP);
int count = increment(player, "help_ups");
awardAt(player, count, 10, HELP_UP_10);
awardAt(player, count, 50, HELP_UP_50);
}
public static void recordCarry(ServerPlayer player) {
award(player, CARRY_PLAYER);
int count = increment(player, "carries");
awardAt(player, count, 10, CARRY_PLAYER_10);
awardAt(player, count, 50, CARRY_PLAYER_50);
}
private static void awardAt(ServerPlayer player, int count, int threshold, ResourceLocation advancementId) {
if (count >= threshold) {
award(player, advancementId);
}
}
private static int increment(ServerPlayer player, String key) {
CompoundTag persistentData = player.getPersistentData();
CompoundTag counts = persistentData.getCompound(DATA_KEY);
int count = counts.getInt(key) + 1;
counts.putInt(key, count);
persistentData.put(DATA_KEY, counts);
return count;
}
public static void copyCounts(Player original, Player replacement) {
CompoundTag originalData = original.getPersistentData();
if (originalData.contains(DATA_KEY)) {
replacement.getPersistentData().put(DATA_KEY, originalData.getCompound(DATA_KEY).copy());
}
}
}
@@ -132,7 +132,7 @@ public class MobKillHandler {
if (knocked instanceof ServerPlayer sp) { if (knocked instanceof ServerPlayer sp) {
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> sp), PacketDistributor.PLAYER.with(() -> sp),
new ExecutionProgressPacket(attempt.timeLeft, null) new ExecutionProgressPacket(attempt.timeLeft, getExecutionTime(), ExecutionProgressPacket.Action.EXECUTION, knocked.getDisplayName().getString(), null)
); );
} }
if (attempt.timeLeft <= 0) { if (attempt.timeLeft <= 0) {
@@ -5,7 +5,7 @@ import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.simple.SimpleChannel;
public class NetworkHandler { public class NetworkHandler {
public static final String PROTOCOL_VERSION = "1"; public static final String PROTOCOL_VERSION = "4";
public static SimpleChannel CHANNEL; public static SimpleChannel CHANNEL;
public static void register() { public static void register() {
CHANNEL = NetworkRegistry.newSimpleChannel(ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); CHANNEL = NetworkRegistry.newSimpleChannel(ResourceLocation.fromNamespaceAndPath(KnockedBack.MOD_ID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals);
@@ -15,5 +15,6 @@ public class NetworkHandler {
CHANNEL.registerMessage(id++, ExecutionProgressPacket.class, ExecutionProgressPacket::encode, ExecutionProgressPacket::decode, ExecutionProgressPacket::handle); CHANNEL.registerMessage(id++, ExecutionProgressPacket.class, ExecutionProgressPacket::encode, ExecutionProgressPacket::decode, ExecutionProgressPacket::handle);
CHANNEL.registerMessage(id++, CarryTogglePacket.class, CarryTogglePacket::encode, CarryTogglePacket::decode, CarryTogglePacket::handle); CHANNEL.registerMessage(id++, CarryTogglePacket.class, CarryTogglePacket::encode, CarryTogglePacket::decode, CarryTogglePacket::handle);
CHANNEL.registerMessage(id++, GrippedStatePacket.class, GrippedStatePacket::encode, GrippedStatePacket::decode, GrippedStatePacket::handle); CHANNEL.registerMessage(id++, GrippedStatePacket.class, GrippedStatePacket::encode, GrippedStatePacket::decode, GrippedStatePacket::handle);
CHANNEL.registerMessage(id++, HelpUpKnockedPacket.class, HelpUpKnockedPacket::encode, HelpUpKnockedPacket::decode, HelpUpKnockedPacket::handle);
} }
} }
@@ -76,6 +76,7 @@ public class PlayerExecutionHandler {
executor.sendSystemMessage(Component.translatable("message.knockedback.cannot_execute_carried_player")); executor.sendSystemMessage(Component.translatable("message.knockedback.cannot_execute_carried_player"));
return; return;
} }
if (HelpUpHandler.isBeingHelped(knockedId)) return;
if (executionAttempts.containsKey(knockedId)) return; if (executionAttempts.containsKey(knockedId)) return;
executionAttempts.put(knockedId, new PlayerExecutionAttempt(executor.getUUID())); executionAttempts.put(knockedId, new PlayerExecutionAttempt(executor.getUUID()));
executor.sendSystemMessage(Component.translatable("message.knockedback.execution_started")); executor.sendSystemMessage(Component.translatable("message.knockedback.execution_started"));
@@ -139,6 +140,7 @@ public class PlayerExecutionHandler {
new ExecutionProgressPacket(0, null) new ExecutionProgressPacket(0, null)
); );
} }
KnockedbackAdvancements.recordExecution(executor);
executor.sendSystemMessage(Component.translatable("message.knockedback.you_executed", knockedPlayer.getDisplayName())); executor.sendSystemMessage(Component.translatable("message.knockedback.you_executed", knockedPlayer.getDisplayName()));
if (knockedPlayer instanceof ServerPlayer serverPlayer) { if (knockedPlayer instanceof ServerPlayer serverPlayer) {
serverPlayer.sendSystemMessage(Component.translatable("message.knockedback.you_were_executed", executor.getDisplayName())); serverPlayer.sendSystemMessage(Component.translatable("message.knockedback.you_were_executed", executor.getDisplayName()));
@@ -168,11 +170,11 @@ public class PlayerExecutionHandler {
attempt.timeLeft--; attempt.timeLeft--;
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> executor), PacketDistributor.PLAYER.with(() -> executor),
new ExecutionProgressPacket(attempt.timeLeft, attempt.executorUuid) new ExecutionProgressPacket(attempt.timeLeft, getExecutionTime(), ExecutionProgressPacket.Action.EXECUTION, knocked.getDisplayName().getString(), attempt.executorUuid)
); );
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> knocked), PacketDistributor.PLAYER.with(() -> knocked),
new ExecutionProgressPacket(attempt.timeLeft, attempt.executorUuid) new ExecutionProgressPacket(attempt.timeLeft, getExecutionTime(), ExecutionProgressPacket.Action.EXECUTION, knocked.getDisplayName().getString(), attempt.executorUuid)
); );
if (attempt.timeLeft <= 0) { if (attempt.timeLeft <= 0) {
toExecute.add(knockedId); toExecute.add(knockedId);
@@ -1,27 +1,79 @@
package net.fretux.knockedback.client; package net.fretux.knockedback.client;
import net.fretux.knockedback.ExecutionProgressPacket;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.UUID; import java.util.UUID;
public class ClientExecutionState { public class ClientExecutionState {
private static int timeLeft = 0; private static int timeLeft = 0;
private static int totalTicks = 1;
private static ExecutionProgressPacket.Action action = ExecutionProgressPacket.Action.NONE;
private static String targetName = "";
@Nullable @Nullable
private static UUID executorId; private static UUID executorId;
public static void setExecution(int ticks, @Nullable UUID executor) { public static void setExecution(int ticks, @Nullable UUID executor) {
timeLeft = ticks; setProgress(ticks, net.fretux.knockedback.PlayerExecutionHandler.getExecutionTime(), ExecutionProgressPacket.Action.EXECUTION, "", executor);
executorId = ticks > 0 ? executor : null; }
public static void setProgress(int ticks, int total, ExecutionProgressPacket.Action progressAction, @Nullable UUID actor) {
setProgress(ticks, total, progressAction, "", actor);
}
public static void setProgress(int ticks, int total, ExecutionProgressPacket.Action progressAction, String target, @Nullable UUID actor) {
timeLeft = Math.max(0, ticks);
totalTicks = Math.max(1, total);
action = timeLeft > 0 ? progressAction : ExecutionProgressPacket.Action.NONE;
targetName = timeLeft > 0 ? target : "";
executorId = timeLeft > 0 ? actor : null;
}
public static int getTotalTicks() {
return totalTicks;
}
public static ExecutionProgressPacket.Action getAction() {
return action;
}
public static String getTargetName() {
return targetName;
}
public static boolean isHelpUp() {
return isActive() && action == ExecutionProgressPacket.Action.HELP_UP;
}
public static boolean isExecution() {
return isActive() && action == ExecutionProgressPacket.Action.EXECUTION;
}
public static boolean isActive() {
return timeLeft > 0;
}
public static boolean isExecuting() {
return isExecution();
}
public static boolean hasProgress() {
return isActive();
}
public static void clear() {
timeLeft = 0;
totalTicks = 1;
action = ExecutionProgressPacket.Action.NONE;
targetName = "";
executorId = null;
} }
public static int getTimeLeft() { public static int getTimeLeft() {
return timeLeft; return timeLeft;
} }
public static boolean isExecuting() {
return timeLeft > 0;
}
public static boolean isExecutor(UUID playerId) { public static boolean isExecutor(UUID playerId) {
return timeLeft > 0 && executorId != null && executorId.equals(playerId); return isExecution() && executorId != null && executorId.equals(playerId);
} }
} }
@@ -2,10 +2,10 @@ package net.fretux.knockedback.client;
import net.fretux.knockedback.CarryTogglePacket; import net.fretux.knockedback.CarryTogglePacket;
import net.fretux.knockedback.ExecuteKnockedPacket; import net.fretux.knockedback.ExecuteKnockedPacket;
import net.fretux.knockedback.HelpUpKnockedPacket;
import net.fretux.knockedback.NetworkHandler; import net.fretux.knockedback.NetworkHandler;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
@@ -27,6 +27,14 @@ public class ClientKeyHandler {
"key.categories.knockedback" "key.categories.knockedback"
); );
public static final KeyMapping HELP_UP_KEY = new KeyMapping(
"key.knockedback.help_up",
GLFW.GLFW_KEY_H,
"key.categories.knockedback"
);
private static boolean wasHelping;
@Mod.EventBusSubscriber(modid = "knockedback", value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) @Mod.EventBusSubscriber(modid = "knockedback", value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
public static class KeyMappingRegistration { public static class KeyMappingRegistration {
@SubscribeEvent @SubscribeEvent
@@ -37,21 +45,32 @@ public class ClientKeyHandler {
public static void registerCarryKey(RegisterKeyMappingsEvent e) { public static void registerCarryKey(RegisterKeyMappingsEvent e) {
e.register(CARRY_KEY); e.register(CARRY_KEY);
} }
@SubscribeEvent
public static void registerHelpUpKey(RegisterKeyMappingsEvent e) {
e.register(HELP_UP_KEY);
}
} }
@SubscribeEvent @SubscribeEvent
public static void onClientTick(TickEvent.ClientTickEvent event) { public static void onClientTick(TickEvent.ClientTickEvent event) {
if (Minecraft.getInstance().screen != null) return; Minecraft mc = Minecraft.getInstance();
if (event.phase == TickEvent.Phase.END) {
boolean isHelping = mc.player != null && mc.screen == null && HELP_UP_KEY.isDown();
if (isHelping != wasHelping) {
NetworkHandler.CHANNEL.sendToServer(new HelpUpKnockedPacket(isHelping));
wasHelping = isHelping;
}
}
if (mc.screen != null) return;
if (EXECUTE_KEY.consumeClick()) { if (EXECUTE_KEY.consumeClick()) {
if (Minecraft.getInstance().player != null) { if (mc.player != null) {
NetworkHandler.CHANNEL.sendToServer(new ExecuteKnockedPacket()); NetworkHandler.CHANNEL.sendToServer(new ExecuteKnockedPacket());
} }
} }
if (event.phase == TickEvent.Phase.END && CARRY_KEY.consumeClick()) { if (event.phase == TickEvent.Phase.END && CARRY_KEY.consumeClick()) {
Minecraft mc = Minecraft.getInstance();
if (mc.player != null && mc.screen == null) { if (mc.player != null && mc.screen == null) {
NetworkHandler.CHANNEL.sendToServer(new CarryTogglePacket()); NetworkHandler.CHANNEL.sendToServer(new CarryTogglePacket());
} }
} }
} }
} }
@@ -3,6 +3,7 @@ package net.fretux.knockedback.client;
import net.fretux.knockedback.KnockedBack; import net.fretux.knockedback.KnockedBack;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.resources.language.I18n;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderGuiOverlayEvent; import net.minecraftforge.client.event.RenderGuiOverlayEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -10,22 +11,46 @@ import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(modid = KnockedBack.MOD_ID, value = Dist.CLIENT) @Mod.EventBusSubscriber(modid = KnockedBack.MOD_ID, value = Dist.CLIENT)
public class ExecutionProgressOverlay { public class ExecutionProgressOverlay {
private static final int BAR_WIDTH = 100; private static final int BAR_WIDTH = 132;
private static final int BAR_HEIGHT = 8; private static final int BAR_HEIGHT = 9;
@SubscribeEvent @SubscribeEvent
public static void onRender(RenderGuiOverlayEvent.Post event) { public static void onRender(RenderGuiOverlayEvent.Post event) {
if (!ClientExecutionState.isExecuting()) return; if (!ClientExecutionState.hasProgress()) return;
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
GuiGraphics gui = event.getGuiGraphics(); GuiGraphics gui = event.getGuiGraphics();
int sw = mc.getWindow().getGuiScaledWidth(); int sw = mc.getWindow().getGuiScaledWidth();
int sh = mc.getWindow().getGuiScaledHeight(); int sh = mc.getWindow().getGuiScaledHeight();
int x = (sw - BAR_WIDTH) / 2; int x = (sw - BAR_WIDTH) / 2;
int y = (sh - BAR_HEIGHT) / 2; int y = (sh / 2) + 24;
int totalTicks = net.fretux.knockedback.PlayerExecutionHandler.getExecutionTime(); float progress = 1.0f - (ClientExecutionState.getTimeLeft() / (float) ClientExecutionState.getTotalTicks());
float progress = 1.0f - (ClientExecutionState.getTimeLeft() / (float) totalTicks); progress = Math.max(0.0f, Math.min(1.0f, progress));
int fillWidth = (int) (BAR_WIDTH * progress); int fillWidth = (int) (BAR_WIDTH * progress);
gui.fill(x, y, x + BAR_WIDTH, y + BAR_HEIGHT, 0x80000000); int fillColor = ClientExecutionState.isHelpUp() ? 0xFF55D987 : 0xFFE05252;
gui.fill(x, y, x + fillWidth, y + BAR_HEIGHT, 0x80FFFFFF); int fillHighlight = ClientExecutionState.isHelpUp() ? 0xFF8EF0B0 : 0xFFFF8A78;
String targetName = ClientExecutionState.getTargetName();
String label = targetName.isBlank()
? I18n.get(ClientExecutionState.isHelpUp()
? "overlay.knockedback.help_up_progress"
: "overlay.knockedback.execution_progress")
: I18n.get(ClientExecutionState.isHelpUp()
? "overlay.knockedback.helping_up_target"
: "overlay.knockedback.executing_target", targetName);
String percent = Math.round(progress * 100.0f) + "%";
int maxLabelWidth = BAR_WIDTH - mc.font.width(percent) - 6;
if (mc.font.width(label) > maxLabelWidth) {
label = mc.font.plainSubstrByWidth(label, Math.max(0, maxLabelWidth - 3)) + "...";
}
int labelY = y - 12;
gui.drawString(mc.font, label, x, labelY, 0xFFFFFFFF, true);
gui.drawString(mc.font, percent, x + BAR_WIDTH - mc.font.width(percent), labelY, 0xFFE8E8E8, true);
gui.fill(x - 2, y - 2, x + BAR_WIDTH + 2, y + BAR_HEIGHT + 2, 0xCC000000);
gui.fill(x - 1, y - 1, x + BAR_WIDTH + 1, y + BAR_HEIGHT + 1, 0x99FFFFFF);
gui.fill(x, y, x + BAR_WIDTH, y + BAR_HEIGHT, 0xDD101010);
if (fillWidth > 0) {
gui.fill(x, y, x + fillWidth, y + BAR_HEIGHT, fillColor);
gui.fill(x, y, x + fillWidth, y + 2, fillHighlight);
}
} }
} }
@@ -17,6 +17,9 @@ public class Config {
public static class Common { public static class Common {
public final ForgeConfigSpec.IntValue knockedDuration; public final ForgeConfigSpec.IntValue knockedDuration;
public final ForgeConfigSpec.IntValue executionTime; public final ForgeConfigSpec.IntValue executionTime;
public final ForgeConfigSpec.IntValue helpUpTime;
public final ForgeConfigSpec.DoubleValue helpUpHealAmount;
public final ForgeConfigSpec.BooleanValue helpUpEnabled;
public final ForgeConfigSpec.BooleanValue mobExecutionEnabled; public final ForgeConfigSpec.BooleanValue mobExecutionEnabled;
public final ForgeConfigSpec.BooleanValue totemPreventsKnockdown; public final ForgeConfigSpec.BooleanValue totemPreventsKnockdown;
public final ForgeConfigSpec.ConfigValue<List<? extends String>> mobExecutionAllowlist; public final ForgeConfigSpec.ConfigValue<List<? extends String>> mobExecutionAllowlist;
@@ -34,6 +37,18 @@ public class Config {
.comment("How long (in ticks) it takes to execute a knocked player. Default: 60 (3 seconds).") .comment("How long (in ticks) it takes to execute a knocked player. Default: 60 (3 seconds).")
.defineInRange("executionTime", 60, 20, 200); .defineInRange("executionTime", 60, 20, 200);
helpUpEnabled = builder
.comment("If true, players can help knocked players recover by holding the help-up key. Default: true.")
.define("helpUpEnabled", true);
helpUpTime = builder
.comment("How long (in ticks) it takes to help up a knocked player. Default: 60 (3 seconds).")
.defineInRange("helpUpTime", 60, 20, 200);
helpUpHealAmount = builder
.comment("How much health a helped-up player receives after recovery. Default: 4.0 (2 hearts).")
.defineInRange("helpUpHealAmount", 4.0D, 1.0D, 20.0D);
mobExecutionEnabled = builder mobExecutionEnabled = builder
.comment("If true, mobs can execute knocked players. Default: true.") .comment("If true, mobs can execute knocked players. Default: true.")
.define("mobExecutionEnabled", true); .define("mobExecutionEnabled", true);
@@ -1,23 +1,61 @@
{ {
"key.knockedback.carry": "Tragen", "key.knockedback.carry": "Tragen",
"key.knockedback.execute": "Hinrichten", "key.knockedback.execute": "Hinrichten",
"key.knockedback.help_up": "Aufhelfen",
"key.categories.knockedback": "Knockedback-Steuerung", "key.categories.knockedback": "Knockedback-Steuerung",
"message.knockedback.cannot_carry_while_knocked": "Du kannst niemanden tragen, w\u00e4hrend du bet\u00e4ubt bist!",
"message.knockedback.cannot_carry_while_knocked": "Du kannst niemanden tragen, während du betäubt bist!", "message.knockedback.cannot_execute_while_knocked": "Du kannst niemanden hinrichten, w\u00e4hrend du bet\u00e4ubt bist!",
"message.knockedback.cannot_execute_while_knocked": "Du kannst niemanden hinrichten, während du betäubt bist!",
"message.knockedback.cannot_execute_carried_player": "Du kannst keinen Spieler hinrichten, der getragen wird!", "message.knockedback.cannot_execute_carried_player": "Du kannst keinen Spieler hinrichten, der getragen wird!",
"message.knockedback.cannot_help_while_knocked": "Du kannst niemandem aufhelfen, w\u00e4hrend du bet\u00e4ubt bist!",
"message.knockedback.cannot_help_carried_player": "Du kannst keinem Spieler aufhelfen, der getragen wird!",
"message.knockedback.execution_started": "Hinrichtung gestartet...", "message.knockedback.execution_started": "Hinrichtung gestartet...",
"message.knockedback.being_executed": "Du wirst hingerichtet!", "message.knockedback.being_executed": "Du wirst hingerichtet!",
"message.knockedback.execution_interrupted": "Hinrichtung abgebrochen!", "message.knockedback.execution_interrupted": "Hinrichtung abgebrochen!",
"message.knockedback.execution_interrupted_executor": "Deine Hinrichtung wurde abgebrochen!", "message.knockedback.execution_interrupted_executor": "Deine Hinrichtung wurde abgebrochen!",
"message.knockedback.picked_up": "%1$s wurde Aufgehoben", "message.knockedback.help_up_started": "Du hilfst %1$s auf...",
"message.knockedback.being_carried": "Du wirst von %1$s getragen", "message.knockedback.being_helped": "%1$s hilft dir auf!",
"message.knockedback.dropped": "%1$s wurde Abgesetzt", "message.knockedback.help_up_interrupted": "Aufhelfen abgebrochen!",
"message.knockedback.been_dropped": "Du wurdest von %1$s abgesetzt", "message.knockedback.help_up_interrupted_helper": "Dein Aufhelfen wurde abgebrochen!",
"message.knockedback.you_executed": "Du hast %1$s hingerichtet!", "message.knockedback.you_helped_up": "Du hast %1$s aufgeholfen!",
"message.knockedback.you_were_helped_up": "Dir wurde von %1$s aufgeholfen!",
"message.knockedback.picked_up": "%1$s wurde aufgehoben",
"message.knockedback.being_carried": "Du wirst von %1$s getragen",
"message.knockedback.dropped": "%1$s wurde abgesetzt",
"message.knockedback.been_dropped": "Du wurdest von %1$s abgesetzt",
"message.knockedback.you_executed": "Du hast %1$s hingerichtet!",
"message.knockedback.you_were_executed": "Du wurdest von %1$s hingerichtet!", "message.knockedback.you_were_executed": "Du wurdest von %1$s hingerichtet!",
"overlay.knockedback.execution_progress": "Hinrichten",
"overlay.knockedback.help_up_progress": "Aufhelfen",
"overlay.knockedback.executing_target": "%s hinrichten...",
"overlay.knockedback.helping_up_target": "%s aufhelfen...",
"overlay.knockedback.recovery_time": "Zeit bis Erholung: %s s", "overlay.knockedback.recovery_time": "Zeit bis Erholung: %s s",
"death.attack.knockedback_execution": "%1$s wurde gnadenlos hingerichtet!" "death.attack.knockedback_execution": "%1$s wurde gnadenlos hingerichtet!",
"advancements.knockedback.root.title": "Knocked Back",
"advancements.knockedback.root.description": "\u00dcberlebe, rette andere oder beende den Kampf, w\u00e4hrend der Tod wartet.",
"advancements.knockedback.knocked.title": "Noch nicht tot",
"advancements.knockedback.knocked.description": "Werde bet\u00e4ubt.",
"advancements.knockedback.knocked_10.title": "Hart im Nehmen",
"advancements.knockedback.knocked_10.description": "Werde 10-mal bet\u00e4ubt.",
"advancements.knockedback.knocked_50.title": "Bleibt nicht liegen",
"advancements.knockedback.knocked_50.description": "Werde 50-mal bet\u00e4ubt.",
"advancements.knockedback.execute_player.title": "Gnadensto\u00df",
"advancements.knockedback.execute_player.description": "Richte einen bet\u00e4ubten Spieler hin.",
"advancements.knockedback.execute_player_10.title": "Keine offenen Enden",
"advancements.knockedback.execute_player_10.description": "Richte 10 bet\u00e4ubte Spieler hin.",
"advancements.knockedback.execute_player_100.title": "Gnadenlos",
"advancements.knockedback.execute_player_100.description": "Richte 100 bet\u00e4ubte Spieler hin.",
"advancements.knockedback.help_up.title": "Feldsanit\u00e4ter",
"advancements.knockedback.help_up.description": "Hilf einem bet\u00e4ubten Spieler wieder auf die Beine.",
"advancements.knockedback.help_up_10.title": "Kampfsanit\u00e4ter",
"advancements.knockedback.help_up_10.description": "Hilf 10 bet\u00e4ubten Spielern auf.",
"advancements.knockedback.help_up_50.title": "Schutzengel",
"advancements.knockedback.help_up_50.description": "Hilf 50 bet\u00e4ubten Spielern auf.",
"advancements.knockedback.be_helped_up.title": "Zweiter Atem",
"advancements.knockedback.be_helped_up.description": "Lass dir von einem anderen Spieler aufhelfen.",
"advancements.knockedback.carry_player.title": "Niemand bleibt zur\u00fcck",
"advancements.knockedback.carry_player.description": "Trage einen bet\u00e4ubten Spieler aus der Gefahr.",
"advancements.knockedback.carry_player_10.title": "Evakuierungsteam",
"advancements.knockedback.carry_player_10.description": "Trage 10 bet\u00e4ubte Spieler.",
"advancements.knockedback.carry_player_50.title": "Bergungsexperte",
"advancements.knockedback.carry_player_50.description": "Trage 50 bet\u00e4ubte Spieler."
} }
@@ -1,10 +1,13 @@
{ {
"key.knockedback.carry": "Carry", "key.knockedback.carry": "Carry",
"key.knockedback.execute": "Execute", "key.knockedback.execute": "Execute",
"key.knockedback.help_up": "Help Up",
"key.categories.knockedback": "Knockedback Controls", "key.categories.knockedback": "Knockedback Controls",
"message.knockedback.cannot_carry_while_knocked": "You cannot carry others while you are knocked!", "message.knockedback.cannot_carry_while_knocked": "You cannot carry others while you are knocked!",
"message.knockedback.cannot_execute_while_knocked": "You cannot execute others while you are knocked!", "message.knockedback.cannot_execute_while_knocked": "You cannot execute others while you are knocked!",
"message.knockedback.cannot_execute_carried_player": "You cannot execute a player who is being carried!", "message.knockedback.cannot_execute_carried_player": "You cannot execute a player who is being carried!",
"message.knockedback.cannot_help_while_knocked": "You cannot help others while you are knocked!",
"message.knockedback.cannot_help_carried_player": "You cannot help up a player who is being carried!",
"message.knockedback.picked_up": "Picked up %s", "message.knockedback.picked_up": "Picked up %s",
"message.knockedback.being_carried": "You are being carried by %s", "message.knockedback.being_carried": "You are being carried by %s",
"message.knockedback.dropped": "Dropped %s", "message.knockedback.dropped": "Dropped %s",
@@ -13,8 +16,46 @@
"message.knockedback.being_executed": "You're being executed!", "message.knockedback.being_executed": "You're being executed!",
"message.knockedback.execution_interrupted": "Execution interrupted!", "message.knockedback.execution_interrupted": "Execution interrupted!",
"message.knockedback.execution_interrupted_executor": "Your execution was interrupted!", "message.knockedback.execution_interrupted_executor": "Your execution was interrupted!",
"message.knockedback.help_up_started": "Helping up %s...",
"message.knockedback.being_helped": "%s is helping you up!",
"message.knockedback.help_up_interrupted": "Help interrupted!",
"message.knockedback.help_up_interrupted_helper": "Your help-up was interrupted!",
"message.knockedback.you_helped_up": "You helped up %s!",
"message.knockedback.you_were_helped_up": "You were helped up by %s!",
"message.knockedback.you_executed": "You executed %s!", "message.knockedback.you_executed": "You executed %s!",
"message.knockedback.you_were_executed": "You were executed by %s!", "message.knockedback.you_were_executed": "You were executed by %s!",
"overlay.knockedback.execution_progress": "Executing",
"overlay.knockedback.help_up_progress": "Helping up",
"overlay.knockedback.executing_target": "Executing %s...",
"overlay.knockedback.helping_up_target": "Helping up %s...",
"overlay.knockedback.recovery_time": "Time until recovery: %s s", "overlay.knockedback.recovery_time": "Time until recovery: %s s",
"death.attack.knockedback_execution": "%1$s was executed mercilessly." "death.attack.knockedback_execution": "%1$s was executed mercilessly.",
"advancements.knockedback.root.title": "Knocked Back",
"advancements.knockedback.root.description": "Survive, rescue, or finish the fight while death is waiting.",
"advancements.knockedback.knocked.title": "Not Dead Yet",
"advancements.knockedback.knocked.description": "Enter the knocked state.",
"advancements.knockedback.knocked_10.title": "Built Different",
"advancements.knockedback.knocked_10.description": "Enter the knocked state 10 times.",
"advancements.knockedback.knocked_50.title": "Refuses to Stay Down",
"advancements.knockedback.knocked_50.description": "Enter the knocked state 50 times.",
"advancements.knockedback.execute_player.title": "Coup de Grace",
"advancements.knockedback.execute_player.description": "Execute a knocked player.",
"advancements.knockedback.execute_player_10.title": "No Loose Ends",
"advancements.knockedback.execute_player_10.description": "Execute 10 knocked players.",
"advancements.knockedback.execute_player_100.title": "Merciless",
"advancements.knockedback.execute_player_100.description": "Execute 100 knocked players.",
"advancements.knockedback.help_up.title": "Field Medic",
"advancements.knockedback.help_up.description": "Help a knocked player back onto their feet.",
"advancements.knockedback.help_up_10.title": "Combat Medic",
"advancements.knockedback.help_up_10.description": "Help up 10 knocked players.",
"advancements.knockedback.help_up_50.title": "Guardian Angel",
"advancements.knockedback.help_up_50.description": "Help up 50 knocked players.",
"advancements.knockedback.be_helped_up.title": "Second Wind",
"advancements.knockedback.be_helped_up.description": "Get helped up by another player.",
"advancements.knockedback.carry_player.title": "No One Left Behind",
"advancements.knockedback.carry_player.description": "Carry a knocked player out of danger.",
"advancements.knockedback.carry_player_10.title": "Evac Team",
"advancements.knockedback.carry_player_10.description": "Carry 10 knocked players.",
"advancements.knockedback.carry_player_50.title": "Extraction Expert",
"advancements.knockedback.carry_player_50.description": "Carry 50 knocked players."
} }
@@ -0,0 +1,22 @@
{
"parent": "knockedback:knocked",
"display": {
"icon": {
"item": "minecraft:honey_bottle"
},
"title": {
"translate": "advancements.knockedback.be_helped_up.title"
},
"description": {
"translate": "advancements.knockedback.be_helped_up.description"
},
"frame": "task",
"show_toast": true,
"announce_to_chat": false
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
}
}
@@ -0,0 +1,22 @@
{
"parent": "knockedback:knocked",
"display": {
"icon": {
"item": "minecraft:lead"
},
"title": {
"translate": "advancements.knockedback.carry_player.title"
},
"description": {
"translate": "advancements.knockedback.carry_player.description"
},
"frame": "task",
"show_toast": true,
"announce_to_chat": false
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:carry_player",
"display": {
"icon": {
"item": "minecraft:saddle"
},
"title": {
"translate": "advancements.knockedback.carry_player_10.title"
},
"description": {
"translate": "advancements.knockedback.carry_player_10.description"
},
"frame": "goal",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 50
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:carry_player_10",
"display": {
"icon": {
"item": "minecraft:elytra"
},
"title": {
"translate": "advancements.knockedback.carry_player_50.title"
},
"description": {
"translate": "advancements.knockedback.carry_player_50.description"
},
"frame": "challenge",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 150
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:knocked",
"display": {
"icon": {
"item": "minecraft:iron_sword"
},
"title": {
"translate": "advancements.knockedback.execute_player.title"
},
"description": {
"translate": "advancements.knockedback.execute_player.description"
},
"frame": "challenge",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 25
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:execute_player",
"display": {
"icon": {
"item": "minecraft:diamond_sword"
},
"title": {
"translate": "advancements.knockedback.execute_player_10.title"
},
"description": {
"translate": "advancements.knockedback.execute_player_10.description"
},
"frame": "goal",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 50
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:execute_player_10",
"display": {
"icon": {
"item": "minecraft:netherite_sword"
},
"title": {
"translate": "advancements.knockedback.execute_player_100.title"
},
"description": {
"translate": "advancements.knockedback.execute_player_100.description"
},
"frame": "challenge",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 250
}
}
@@ -0,0 +1,22 @@
{
"parent": "knockedback:knocked",
"display": {
"icon": {
"item": "minecraft:golden_apple"
},
"title": {
"translate": "advancements.knockedback.help_up.title"
},
"description": {
"translate": "advancements.knockedback.help_up.description"
},
"frame": "goal",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:help_up",
"display": {
"icon": {
"item": "minecraft:glistering_melon_slice"
},
"title": {
"translate": "advancements.knockedback.help_up_10.title"
},
"description": {
"translate": "advancements.knockedback.help_up_10.description"
},
"frame": "goal",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 50
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:help_up_10",
"display": {
"icon": {
"item": "minecraft:enchanted_golden_apple"
},
"title": {
"translate": "advancements.knockedback.help_up_50.title"
},
"description": {
"translate": "advancements.knockedback.help_up_50.description"
},
"frame": "challenge",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 150
}
}
@@ -0,0 +1,22 @@
{
"parent": "knockedback:root",
"display": {
"icon": {
"item": "minecraft:redstone"
},
"title": {
"translate": "advancements.knockedback.knocked.title"
},
"description": {
"translate": "advancements.knockedback.knocked.description"
},
"frame": "task",
"show_toast": true,
"announce_to_chat": false
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:knocked",
"display": {
"icon": {
"item": "minecraft:shield"
},
"title": {
"translate": "advancements.knockedback.knocked_10.title"
},
"description": {
"translate": "advancements.knockedback.knocked_10.description"
},
"frame": "goal",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 50
}
}
@@ -0,0 +1,25 @@
{
"parent": "knockedback:knocked_10",
"display": {
"icon": {
"item": "minecraft:netherite_chestplate"
},
"title": {
"translate": "advancements.knockedback.knocked_50.title"
},
"description": {
"translate": "advancements.knockedback.knocked_50.description"
},
"frame": "challenge",
"show_toast": true,
"announce_to_chat": true
},
"criteria": {
"triggered": {
"trigger": "minecraft:impossible"
}
},
"rewards": {
"experience": 150
}
}
@@ -0,0 +1,22 @@
{
"display": {
"icon": {
"item": "minecraft:totem_of_undying"
},
"title": {
"translate": "advancements.knockedback.root.title"
},
"description": {
"translate": "advancements.knockedback.root.description"
},
"background": "minecraft:textures/gui/advancements/backgrounds/adventure.png",
"frame": "task",
"show_toast": false,
"announce_to_chat": false
},
"criteria": {
"triggered": {
"trigger": "minecraft:tick"
}
}
}