Добавить production-каталог опасностей

This commit is contained in:
2026-08-19 05:30:56 +03:00
parent cc7f487cf7
commit dc2ebcbf8c
33 changed files with 1160 additions and 37 deletions
@@ -6,7 +6,9 @@ import java.util.List;
/** Immutable, Android-independent level data. */
public final class LevelDefinition {
public enum HazardType { SPIKES, SAW, FREEZER, ELECTRIC, PIT }
public enum HazardType {
SPIKES, SAW, FREEZER, ELECTRIC, PIT, CRUSHER, FIRE, PROJECTILE, EXPLOSION
}
public enum SwitchType { PRESSURE, TOGGLE, TIMED }
public static final class Box {
@@ -54,17 +56,36 @@ public final class LevelDefinition {
public final String relayId;
public final float impulseX;
public final float impulseY;
public final float cycleSeconds;
public final float activeSeconds;
public final float speedX;
public final float speedY;
public final String visualStyle;
public final String audioCue;
public Hazard(HazardType type, Box box) {
this(type, box, null, 0f, 0f);
}
public Hazard(HazardType type, Box box, String relayId, float impulseX, float impulseY) {
this(type, box, relayId, impulseX, impulseY,
0f, 0f, 0f, 0f, null, null);
}
public Hazard(HazardType type, Box box, String relayId, float impulseX, float impulseY,
float cycleSeconds, float activeSeconds, float speedX, float speedY,
String visualStyle, String audioCue) {
this.type = type;
this.box = box;
this.relayId = relayId;
this.impulseX = impulseX;
this.impulseY = impulseY;
this.cycleSeconds = cycleSeconds;
this.activeSeconds = activeSeconds;
this.speedX = speedX;
this.speedY = speedY;
this.visualStyle = visualStyle;
this.audioCue = audioCue;
}
}
@@ -106,7 +106,13 @@ public final class LevelJsonLoader {
box(required(value, "box", path), path + ".box"),
optionalText(value, "relayId", path),
optionalDecimal(value, "impulseX", path, 0f),
optionalDecimal(value, "impulseY", path, 0f)));
optionalDecimal(value, "impulseY", path, 0f),
optionalDecimal(value, "cycleSeconds", path, 0f),
optionalDecimal(value, "activeSeconds", path, 0f),
optionalDecimal(value, "speedX", path, 0f),
optionalDecimal(value, "speedY", path, 0f),
optionalText(value, "visualStyle", path),
optionalText(value, "audioCue", path)));
}
return result;
}
@@ -40,7 +40,47 @@ public final class LevelValidator {
}
}
for (int i = 0; i < level.hazards.size(); i++) {
box(level.hazards.get(i).box, level, source + ".hazards[" + i + "].box");
LevelDefinition.Hazard hazard = level.hazards.get(i);
String path = source + ".hazards[" + i + "]";
box(hazard.box, level, path + ".box");
if (hazard.type == LevelDefinition.HazardType.CRUSHER) {
positive(hazard.cycleSeconds, path + ".cycleSeconds");
positive(hazard.activeSeconds, path + ".activeSeconds");
if (hazard.activeSeconds > hazard.cycleSeconds) {
throw invalid(path + ".activeSeconds", "must not exceed cycleSeconds");
}
requiredText(hazard.visualStyle, path + ".visualStyle");
requiredText(hazard.audioCue, path + ".audioCue");
}
if (hazard.type == LevelDefinition.HazardType.FIRE) {
requiredText(hazard.visualStyle, path + ".visualStyle");
requiredText(hazard.audioCue, path + ".audioCue");
}
if (hazard.type == LevelDefinition.HazardType.PROJECTILE) {
positive(hazard.cycleSeconds, path + ".cycleSeconds");
if (hazard.speedX == 0f && hazard.speedY == 0f) {
throw invalid(path + ".speedX", "or speedY must be non-zero");
}
LevelDefinition.Box destination = new LevelDefinition.Box(
hazard.box.x + hazard.speedX * hazard.cycleSeconds,
hazard.box.y + hazard.speedY * hazard.cycleSeconds,
hazard.box.width, hazard.box.height);
box(destination, level, path + ".destinationBox");
requiredText(hazard.visualStyle, path + ".visualStyle");
requiredText(hazard.audioCue, path + ".audioCue");
}
if (hazard.type == LevelDefinition.HazardType.EXPLOSION) {
positive(hazard.cycleSeconds, path + ".cycleSeconds");
positive(hazard.activeSeconds, path + ".activeSeconds");
if (hazard.activeSeconds > hazard.cycleSeconds) {
throw invalid(path + ".activeSeconds", "must not exceed cycleSeconds");
}
if (hazard.impulseX == 0f && hazard.impulseY == 0f) {
throw invalid(path + ".impulseX", "or impulseY must be non-zero");
}
requiredText(hazard.visualStyle, path + ".visualStyle");
requiredText(hazard.audioCue, path + ".audioCue");
}
}
for (int i = 0; i < level.switches.size(); i++) {
LevelDefinition.Switch item = level.switches.get(i);
@@ -112,6 +152,10 @@ public final class LevelValidator {
if (!Float.isFinite(value) || value <= 0f) throw invalid(path, "must be positive and finite");
}
private static void requiredText(String value, String path) {
if (value == null || value.trim().isEmpty()) throw invalid(path, "must not be blank");
}
private static IllegalArgumentException invalid(String path, String message) {
return new IllegalArgumentException(path + " " + message);
}