Add next death text

This commit is contained in:
2026-04-10 09:41:43 +03:00
parent fb0f7fe358
commit 3660c4dac7
6 changed files with 33 additions and 5 deletions

View File

@@ -11,6 +11,7 @@ import net.respawnbackoff.CooldownSyncPayload;
public class RespawnBackoffClient implements ClientModInitializer { public class RespawnBackoffClient implements ClientModInitializer {
private static volatile boolean overlayActive; private static volatile boolean overlayActive;
private static volatile long cooldownEndEpochMs; private static volatile long cooldownEndEpochMs;
private static volatile long nextDeathWaitMinutes;
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
@@ -20,6 +21,7 @@ public class RespawnBackoffClient implements ClientModInitializer {
context.client().execute(() -> { context.client().execute(() -> {
overlayActive = payload.active(); overlayActive = payload.active();
cooldownEndEpochMs = payload.cooldownEndEpochMs(); cooldownEndEpochMs = payload.cooldownEndEpochMs();
nextDeathWaitMinutes = payload.nextDeathWaitMinutes();
}); });
}); });
@@ -44,13 +46,23 @@ public class RespawnBackoffClient implements ClientModInitializer {
int seconds = totalSeconds % 60; int seconds = totalSeconds % 60;
String time = String.format("%02d:%02d", minutes, seconds); String time = String.format("%02d:%02d", minutes, seconds);
Component line = Component.translatable("respawn_backoff.hud.countdown", time); Component line = Component.translatable("respawn_backoff.hud.countdown", time);
int lineHeight = client.font.lineHeight;
int centerY = h / 2;
int mainY = centerY - lineHeight - 2;
int textWidth = client.font.width(line); int textWidth = client.font.width(line);
graphics.drawString(client.font, line, (w - textWidth) / 2, h / 2, 0xFFFFFF, false); graphics.drawString(client.font, line, (w - textWidth) / 2, mainY, 0xFFFFFF, false);
long nextMin = nextDeathWaitMinutes;
if (nextMin > 0L) {
Component sub = Component.translatable("respawn_backoff.hud.next_death", nextMin);
int subW = client.font.width(sub);
graphics.drawString(client.font, sub, (w - subW) / 2, centerY + 4, 0xA0A0A0, false);
}
} }
public static void clearForDisconnect() { public static void clearForDisconnect() {
overlayActive = false; overlayActive = false;
cooldownEndEpochMs = 0L; cooldownEndEpochMs = 0L;
nextDeathWaitMinutes = 0L;
} }
} }

View File

@@ -5,7 +5,7 @@ import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public record CooldownSyncPayload(boolean active, long cooldownEndEpochMs) implements CustomPacketPayload { public record CooldownSyncPayload(boolean active, long cooldownEndEpochMs, long nextDeathWaitMinutes) implements CustomPacketPayload {
public static final CustomPacketPayload.Type<CooldownSyncPayload> TYPE = new CustomPacketPayload.Type<>( public static final CustomPacketPayload.Type<CooldownSyncPayload> TYPE = new CustomPacketPayload.Type<>(
ResourceLocation.fromNamespaceAndPath(RespawnBackoffMod.MOD_ID, "cooldown_sync") ResourceLocation.fromNamespaceAndPath(RespawnBackoffMod.MOD_ID, "cooldown_sync")
); );
@@ -14,8 +14,9 @@ public record CooldownSyncPayload(boolean active, long cooldownEndEpochMs) imple
(RegistryFriendlyByteBuf buf, CooldownSyncPayload payload) -> { (RegistryFriendlyByteBuf buf, CooldownSyncPayload payload) -> {
buf.writeBoolean(payload.active()); buf.writeBoolean(payload.active());
buf.writeLong(payload.cooldownEndEpochMs()); buf.writeLong(payload.cooldownEndEpochMs());
buf.writeLong(payload.nextDeathWaitMinutes());
}, },
buf -> new CooldownSyncPayload(buf.readBoolean(), buf.readLong()) buf -> new CooldownSyncPayload(buf.readBoolean(), buf.readLong(), buf.readLong())
); );
@Override @Override

View File

@@ -30,4 +30,12 @@ public record RespawnBackoffData(
public boolean isCooldownFinished(long nowMs) { public boolean isCooldownFinished(long nowMs) {
return cooldownEndEpochMs > 0L && nowMs >= cooldownEndEpochMs; return cooldownEndEpochMs > 0L && nowMs >= cooldownEndEpochMs;
} }
/**
* Spectator wait length in whole minutes if the player dies again now (same formula as the next death).
*/
public long nextDeathWaitMinutes() {
int cappedExp = Math.min(exponent, 6);
return Math.min(1L << cappedExp, 64L);
}
} }

View File

@@ -8,6 +8,11 @@ public final class RespawnBackoffNetworking {
} }
public static void sendCooldown(ServerPlayer player, boolean active, long cooldownEndEpochMs) { public static void sendCooldown(ServerPlayer player, boolean active, long cooldownEndEpochMs) {
ServerPlayNetworking.send(player, new CooldownSyncPayload(active, cooldownEndEpochMs)); long nextMinutes = 0L;
if (active) {
RespawnBackoffData data = player.getAttachedOrElse(RespawnBackoffMod.RESPAWN_BACKOFF, RespawnBackoffData.DEFAULT);
nextMinutes = data.nextDeathWaitMinutes();
}
ServerPlayNetworking.send(player, new CooldownSyncPayload(active, cooldownEndEpochMs, nextMinutes));
} }
} }

View File

@@ -1,5 +1,6 @@
{ {
"respawn_backoff.hud.countdown": "Respawn in %s", "respawn_backoff.hud.countdown": "Respawn in %s",
"respawn_backoff.hud.next_death": "If you die again: %s min",
"respawn_backoff.command.reset.single": "Reset respawn backoff for %s to the minimum (next death: 1 minute).", "respawn_backoff.command.reset.single": "Reset respawn backoff for %s to the minimum (next death: 1 minute).",
"respawn_backoff.command.reset.multiple": "Reset respawn backoff to the minimum for %s players.", "respawn_backoff.command.reset.multiple": "Reset respawn backoff to the minimum for %s players.",
"respawn_backoff.command.skip.single": "Skipped respawn wait for %s.", "respawn_backoff.command.skip.single": "Skipped respawn wait for %s.",

View File

@@ -1,5 +1,6 @@
{ {
"respawn_backoff.hud.countdown": "Возрождение через %s", "respawn_backoff.hud.countdown": "Возрождение через %s",
"respawn_backoff.hud.next_death": "Если умрёте снова: %s мин.",
"respawn_backoff.command.reset.single": "Откат респавна для %s сброшен к минимуму (после следующей смерти: 1 минута).", "respawn_backoff.command.reset.single": "Откат респавна для %s сброшен к минимуму (после следующей смерти: 1 минута).",
"respawn_backoff.command.reset.multiple": "Откат респавна сброшен к минимуму для %s игроков.", "respawn_backoff.command.reset.multiple": "Откат респавна сброшен к минимуму для %s игроков.",
"respawn_backoff.command.skip.single": "Ожидание респавна для %s пропущено.", "respawn_backoff.command.skip.single": "Ожидание респавна для %s пропущено.",