feat: Update respawn delay to support decimal values for items and NPCs

This commit is contained in:
2026-06-21 18:53:11 -07:00
parent ca11bbd4c0
commit e992f18b8e
3 changed files with 36 additions and 18 deletions
@@ -31,8 +31,8 @@ public final class SpawnItemEffect extends MinigameRuntimeEffect {
.add() .add()
.<Integer>append(new KeyedCodec<>("Amount", Codec.INTEGER, false), (effect, value) -> effect.amount = value, effect -> effect.amount) .<Integer>append(new KeyedCodec<>("Amount", Codec.INTEGER, false), (effect, value) -> effect.amount = value, effect -> effect.amount)
.add() .add()
.<Integer>append( .<Double>append(
new KeyedCodec<>("RespawnDelaySeconds", Codec.INTEGER, false), new KeyedCodec<>("RespawnDelaySeconds", Codec.DOUBLE, false),
(effect, value) -> effect.respawnDelaySeconds = value, (effect, value) -> effect.respawnDelaySeconds = value,
effect -> effect.respawnDelaySeconds effect -> effect.respawnDelaySeconds
) )
@@ -51,7 +51,7 @@ public final class SpawnItemEffect extends MinigameRuntimeEffect {
private String itemId = ""; private String itemId = "";
private int amount = 1; private int amount = 1;
private int respawnDelaySeconds = 0; private double respawnDelaySeconds = 0.0D;
private String spawnKey = ""; private String spawnKey = "";
private double offsetX = 0.0D; private double offsetX = 0.0D;
private double offsetY = 0.0D; private double offsetY = 0.0D;
@@ -87,9 +87,9 @@ public final class SpawnItemEffect extends MinigameRuntimeEffect {
if (active != null) { if (active != null) {
activeItems.remove(key); activeItems.remove(key);
int delay = Math.max(0, respawnDelaySeconds); Duration delay = respawnDelay();
if (delay > 0 && !nextSpawnTimes.containsKey(key)) { if (!delay.isZero() && !nextSpawnTimes.containsKey(key)) {
nextSpawnTimes.put(key, Instant.now().plus(Duration.ofSeconds(delay))); nextSpawnTimes.put(key, Instant.now().plus(delay));
return; return;
} }
} }
@@ -128,9 +128,9 @@ public final class SpawnItemEffect extends MinigameRuntimeEffect {
activeItems.put(key, ref); activeItems.put(key, ref);
nextSpawnTimes.remove(key); nextSpawnTimes.remove(key);
if (ignoreRespawnTimer) { if (ignoreRespawnTimer) {
int delay = Math.max(0, respawnDelaySeconds); Duration delay = respawnDelay();
if (delay > 0) { if (!delay.isZero()) {
nextSpawnTimes.put(key, Instant.now().plus(Duration.ofSeconds(delay))); nextSpawnTimes.put(key, Instant.now().plus(delay));
} }
} }
runtime(context).ifPresent(runtime -> { runtime(context).ifPresent(runtime -> {
@@ -142,6 +142,15 @@ public final class SpawnItemEffect extends MinigameRuntimeEffect {
debugMessage(context, TYPE_ID, "FIRED spawned item='" + itemId + "' amount=" + Math.max(1, amount)); debugMessage(context, TYPE_ID, "FIRED spawned item='" + itemId + "' amount=" + Math.max(1, amount));
} }
@Nonnull
private Duration respawnDelay() {
double seconds = Math.max(0.0D, respawnDelaySeconds);
if (seconds <= 0.0D) {
return Duration.ZERO;
}
return Duration.ofNanos(Math.max(1L, Math.round(seconds * 1_000_000_000.0D)));
}
@Nonnull @Nonnull
private String key(@Nonnull TriggerContext context) { private String key(@Nonnull TriggerContext context) {
String configured = spawnKey != null ? spawnKey.trim() : ""; String configured = spawnKey != null ? spawnKey.trim() : "";
@@ -29,8 +29,8 @@ public final class SpawnNpcEffect extends MinigameRuntimeEffect {
.add() .add()
.<Integer>append(new KeyedCodec<>("Count", Codec.INTEGER, false), (effect, value) -> effect.count = value, effect -> effect.count) .<Integer>append(new KeyedCodec<>("Count", Codec.INTEGER, false), (effect, value) -> effect.count = value, effect -> effect.count)
.add() .add()
.<Integer>append( .<Double>append(
new KeyedCodec<>("RespawnDelaySeconds", Codec.INTEGER, false), new KeyedCodec<>("RespawnDelaySeconds", Codec.DOUBLE, false),
(effect, value) -> effect.respawnDelaySeconds = value, (effect, value) -> effect.respawnDelaySeconds = value,
effect -> effect.respawnDelaySeconds effect -> effect.respawnDelaySeconds
) )
@@ -61,7 +61,7 @@ public final class SpawnNpcEffect extends MinigameRuntimeEffect {
private String roleId = ""; private String roleId = "";
private int count = 1; private int count = 1;
private int respawnDelaySeconds = 0; private double respawnDelaySeconds = 0.0D;
private String spawnKey = ""; private String spawnKey = "";
private double offsetX = 0.0D; private double offsetX = 0.0D;
private double offsetY = 0.0D; private double offsetY = 0.0D;
@@ -100,9 +100,9 @@ public final class SpawnNpcEffect extends MinigameRuntimeEffect {
} }
if (nextSpawn == null && activeNpcs.containsKey(key)) { if (nextSpawn == null && activeNpcs.containsKey(key)) {
int delay = Math.max(0, respawnDelaySeconds); Duration delay = respawnDelay();
if (delay > 0) { if (!delay.isZero()) {
nextSpawnTimes.put(key, Instant.now().plus(Duration.ofSeconds(delay))); nextSpawnTimes.put(key, Instant.now().plus(delay));
return; return;
} }
} }
@@ -199,6 +199,15 @@ public final class SpawnNpcEffect extends MinigameRuntimeEffect {
return runtimeSegment + "|" + volumeId + "|" + (!configured.isBlank() ? configured : roleId); return runtimeSegment + "|" + volumeId + "|" + (!configured.isBlank() ? configured : roleId);
} }
@Nonnull
private Duration respawnDelay() {
double seconds = Math.max(0.0D, respawnDelaySeconds);
if (seconds <= 0.0D) {
return Duration.ZERO;
}
return Duration.ofNanos(Math.max(1L, Math.round(seconds * 1_000_000_000.0D)));
}
/** Drops state belonging to runtimes that no longer exist. */ /** Drops state belonging to runtimes that no longer exist. */
static void pruneStaleRuntimeKeys(Map<String, ?>... maps) { static void pruneStaleRuntimeKeys(Map<String, ?>... maps) {
var services = services(); var services = services();
@@ -27,7 +27,7 @@ customUI.triggerVolumeEffectEditor.field.common.MapId.tooltip = The map id to vo
customUI.triggerVolumeEffectEditor.field.common.ItemId = Item customUI.triggerVolumeEffectEditor.field.common.ItemId = Item
customUI.triggerVolumeEffectEditor.field.common.ItemId.tooltip = The item asset id to spawn on the ground. customUI.triggerVolumeEffectEditor.field.common.ItemId.tooltip = The item asset id to spawn on the ground.
customUI.triggerVolumeEffectEditor.field.common.RespawnDelaySeconds = Respawn Delay customUI.triggerVolumeEffectEditor.field.common.RespawnDelaySeconds = Respawn Delay
customUI.triggerVolumeEffectEditor.field.common.RespawnDelaySeconds.tooltip = Seconds to wait after the spawned item is picked up or despawns before spawning again. customUI.triggerVolumeEffectEditor.field.common.RespawnDelaySeconds.tooltip = Seconds to wait before spawning again. Decimals are supported, e.g. 0.5.
customUI.triggerVolumeEffectEditor.field.common.SpawnKey = Spawn Key customUI.triggerVolumeEffectEditor.field.common.SpawnKey = Spawn Key
customUI.triggerVolumeEffectEditor.field.common.SpawnKey.tooltip = Optional unique id for this spawn point when one volume has multiple item spawners. customUI.triggerVolumeEffectEditor.field.common.SpawnKey.tooltip = Optional unique id for this spawn point when one volume has multiple item spawners.
customUI.triggerVolumeEffectEditor.field.common.OffsetX = Offset X customUI.triggerVolumeEffectEditor.field.common.OffsetX = Offset X
@@ -64,7 +64,7 @@ customUI.triggerVolumeEffectEditor.field.StartQueuedMinigame.MinigameId.tooltip
customUI.triggerVolumeEffectEditor.field.SpawnItem.MinigameId.tooltip = Optional minigame runtime to associate this spawned pickup with. customUI.triggerVolumeEffectEditor.field.SpawnItem.MinigameId.tooltip = Optional minigame runtime to associate this spawned pickup with.
customUI.triggerVolumeEffectEditor.field.SpawnItem.ItemId.tooltip = The item asset id to spawn at this trigger volume. customUI.triggerVolumeEffectEditor.field.SpawnItem.ItemId.tooltip = The item asset id to spawn at this trigger volume.
customUI.triggerVolumeEffectEditor.field.SpawnItem.Amount.tooltip = Number of items in the spawned stack. customUI.triggerVolumeEffectEditor.field.SpawnItem.Amount.tooltip = Number of items in the spawned stack.
customUI.triggerVolumeEffectEditor.field.SpawnItem.RespawnDelaySeconds.tooltip = Seconds to wait before respawning after the previous item is picked up or despawns. customUI.triggerVolumeEffectEditor.field.SpawnItem.RespawnDelaySeconds.tooltip = Seconds to wait before respawning after the previous item is picked up or despawns. Decimals are supported.
customUI.triggerVolumeEffectEditor.field.SpawnItem.SpawnKey.tooltip = Optional unique id for this item spawner. customUI.triggerVolumeEffectEditor.field.SpawnItem.SpawnKey.tooltip = Optional unique id for this item spawner.
customUI.triggerVolumeEffectEditor.field.SpawnItem.OffsetX.tooltip = Spawn position offset on the X axis. customUI.triggerVolumeEffectEditor.field.SpawnItem.OffsetX.tooltip = Spawn position offset on the X axis.
customUI.triggerVolumeEffectEditor.field.SpawnItem.OffsetY.tooltip = Spawn position offset on the Y axis. customUI.triggerVolumeEffectEditor.field.SpawnItem.OffsetY.tooltip = Spawn position offset on the Y axis.
@@ -72,7 +72,7 @@ customUI.triggerVolumeEffectEditor.field.SpawnItem.OffsetZ.tooltip = Spawn posit
customUI.triggerVolumeEffectEditor.field.SpawnNpc.MinigameId.tooltip = Optional minigame runtime to associate this spawned NPC with. customUI.triggerVolumeEffectEditor.field.SpawnNpc.MinigameId.tooltip = Optional minigame runtime to associate this spawned NPC with.
customUI.triggerVolumeEffectEditor.field.SpawnNpc.RoleId.tooltip = The spawnable NPC role id to spawn. customUI.triggerVolumeEffectEditor.field.SpawnNpc.RoleId.tooltip = The spawnable NPC role id to spawn.
customUI.triggerVolumeEffectEditor.field.SpawnNpc.Count.tooltip = Number of NPCs to keep active for this spawn key. customUI.triggerVolumeEffectEditor.field.SpawnNpc.Count.tooltip = Number of NPCs to keep active for this spawn key.
customUI.triggerVolumeEffectEditor.field.SpawnNpc.RespawnDelaySeconds.tooltip = Seconds to wait before respawning after all active NPCs for this spawn key are gone. customUI.triggerVolumeEffectEditor.field.SpawnNpc.RespawnDelaySeconds.tooltip = Seconds to wait before respawning after all active NPCs for this spawn key are gone. Decimals are supported.
customUI.triggerVolumeEffectEditor.field.SpawnNpc.SpawnKey.tooltip = Optional unique id for this NPC spawner. customUI.triggerVolumeEffectEditor.field.SpawnNpc.SpawnKey.tooltip = Optional unique id for this NPC spawner.
customUI.triggerVolumeEffectEditor.field.SpawnNpc.OffsetX.tooltip = Spawn position offset on the X axis. customUI.triggerVolumeEffectEditor.field.SpawnNpc.OffsetX.tooltip = Spawn position offset on the X axis.
customUI.triggerVolumeEffectEditor.field.SpawnNpc.OffsetY.tooltip = Spawn position offset on the Y axis. customUI.triggerVolumeEffectEditor.field.SpawnNpc.OffsetY.tooltip = Spawn position offset on the Y axis.