package com.projectsacrifice.game; /** Deterministic per-level runtime clock for one immutable hazard definition. */ public final class HazardRuntime { private final LevelDefinition.Hazard definition; private float elapsedSeconds; public HazardRuntime(LevelDefinition.Hazard definition) { if (definition == null) throw new IllegalArgumentException("definition is required"); this.definition = definition; } public LevelDefinition.Hazard definition() { return definition; } public float elapsedSeconds() { return elapsedSeconds; } public void update(float deltaSeconds) { if (!Float.isFinite(deltaSeconds) || deltaSeconds < 0f) { throw new IllegalArgumentException("deltaSeconds must be finite and non-negative"); } elapsedSeconds += deltaSeconds; if (definition.cycleSeconds > 0f && elapsedSeconds >= definition.cycleSeconds) { elapsedSeconds %= definition.cycleSeconds; } } public boolean active() { boolean cyclicWindow = definition.type == LevelDefinition.HazardType.CRUSHER || definition.type == LevelDefinition.HazardType.EXPLOSION; if (!cyclicWindow || definition.cycleSeconds <= 0f || definition.activeSeconds <= 0f) return true; return elapsedSeconds < definition.activeSeconds; } public LevelDefinition.Box currentBox() { if (definition.type != LevelDefinition.HazardType.PROJECTILE) { return definition.box; } return new LevelDefinition.Box( definition.box.x + definition.speedX * elapsedSeconds, definition.box.y + definition.speedY * elapsedSeconds, definition.box.width, definition.box.height); } public void reset() { elapsedSeconds = 0f; } }