package com.projectsacrifice.game; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** Immutable puzzle-critical state used by versioned activity restoration. */ public final class GameSessionSnapshot { public static final class PlayerData { private final float x, y, velocityX, velocityY; private final boolean alive, onGround; private final float coyoteSeconds, jumpBufferSeconds; private final int supportIndex, facing; public PlayerData(float x, float y, float velocityX, float velocityY, boolean alive, boolean onGround, float coyoteSeconds, float jumpBufferSeconds, int supportIndex, int facing) { this.x = x; this.y = y; this.velocityX = velocityX; this.velocityY = velocityY; this.alive = alive; this.onGround = onGround; this.coyoteSeconds = coyoteSeconds; this.jumpBufferSeconds = jumpBufferSeconds; this.supportIndex = supportIndex; this.facing = facing < 0 ? -1 : 1; } public float x() { return x; } public float y() { return y; } public float velocityX() { return velocityX; } public float velocityY() { return velocityY; } public boolean alive() { return alive; } public boolean onGround() { return onGround; } public float coyoteSeconds() { return coyoteSeconds; } public float jumpBufferSeconds() { return jumpBufferSeconds; } public int supportIndex() { return supportIndex; } public int facing() { return facing; } } public static final class BodyData { private final float x, y, width, height, velocityX, velocityY; private final boolean frozen, conductive, pinned, grounded, crushed, burned; private final int supportIndex; private final String deathType; public BodyData(float x, float y, float width, float height, float velocityX, float velocityY, boolean frozen, boolean conductive, boolean pinned, boolean grounded, int supportIndex, String deathType) { this(x, y, width, height, velocityX, velocityY, frozen, conductive, pinned, grounded, false, false, supportIndex, deathType); } public BodyData(float x, float y, float width, float height, float velocityX, float velocityY, boolean frozen, boolean conductive, boolean pinned, boolean grounded, boolean crushed, boolean burned, int supportIndex, String deathType) { this.x = x; this.y = y; this.width = width; this.height = height; this.velocityX = velocityX; this.velocityY = velocityY; this.frozen = frozen; this.conductive = conductive; this.pinned = pinned; this.grounded = grounded; this.crushed = crushed; this.burned = burned; this.supportIndex = supportIndex; this.deathType = deathType == null ? "SPIKE" : deathType; } public float x() { return x; } public float y() { return y; } public float width() { return width; } public float height() { return height; } public float velocityX() { return velocityX; } public float velocityY() { return velocityY; } public boolean frozen() { return frozen; } public boolean conductive() { return conductive; } public boolean pinned() { return pinned; } public boolean grounded() { return grounded; } public boolean crushed() { return crushed; } public boolean burned() { return burned; } public int supportIndex() { return supportIndex; } public String deathType() { return deathType; } } public static final class ObjectData { private final LevelDefinition.PhysicalObjectType type; private final float x, y, width, height, velocityX, velocityY; private final int mass; private final boolean grounded; private final int supportIndex; public ObjectData(LevelDefinition.PhysicalObjectType type, float x, float y, float width, float height, float velocityX, float velocityY, int mass, boolean grounded, int supportIndex) { this.type = type; this.x = x; this.y = y; this.width = width; this.height = height; this.velocityX = velocityX; this.velocityY = velocityY; this.mass = mass; this.grounded = grounded; this.supportIndex = supportIndex; } public LevelDefinition.PhysicalObjectType type() { return type; } public float x() { return x; } public float y() { return y; } public float width() { return width; } public float height() { return height; } public float velocityX() { return velocityX; } public float velocityY() { return velocityY; } public int mass() { return mass; } public boolean grounded() { return grounded; } public int supportIndex() { return supportIndex; } } public static final class Builder { private int saveVersion = VersionedSessionCodec.CURRENT_VERSION; private final int levelNumber; private float elapsedSeconds; private int deaths; private float respawnSeconds; private boolean collectibleFound; private PlayerData player = new PlayerData( 0f, 0f, 0f, 0f, true, false, 0f, 0f, -1, 1); private final ArrayList bodies = new ArrayList<>(); private final ArrayList objects = new ArrayList<>(); private final ArrayList platformMotionTimes = new ArrayList<>(); private final LinkedHashMap toggleSignals = new LinkedHashMap<>(); private final LinkedHashMap timedSignals = new LinkedHashMap<>(); private final LinkedHashMap previousSwitchContacts = new LinkedHashMap<>(); private Builder(int levelNumber) { this.levelNumber = levelNumber; } Builder saveVersion(int version) { saveVersion = version; return this; } public Builder elapsedSeconds(float value) { elapsedSeconds = value; return this; } public Builder deaths(int value) { deaths = value; return this; } public Builder respawnSeconds(float value) { respawnSeconds = value; return this; } public Builder collectibleFound(boolean value) { collectibleFound = value; return this; } public Builder player(PlayerData value) { player = value; return this; } public Builder addBody(BodyData value) { bodies.add(value); return this; } public Builder addObject(ObjectData value) { objects.add(value); return this; } public Builder platformMotionTimes(List values) { platformMotionTimes.clear(); if (values != null) platformMotionTimes.addAll(values); return this; } public Builder toggleSignal(String key, boolean value) { toggleSignals.put(key, value); return this; } public Builder timedSignal(String key, float value) { timedSignals.put(key, value); return this; } public Builder previousSwitchContact(String key, boolean value) { previousSwitchContacts.put(key, value); return this; } public GameSessionSnapshot build() { return new GameSessionSnapshot(this); } } private final int saveVersion; private final int levelNumber; private final float elapsedSeconds; private final int deaths; private final float respawnSeconds; private final boolean collectibleFound; private final PlayerData player; private final List bodies; private final List objects; private final List platformMotionTimes; private final Map toggleSignals; private final Map timedSignals; private final Map previousSwitchContacts; private GameSessionSnapshot(Builder builder) { saveVersion = builder.saveVersion; levelNumber = builder.levelNumber; elapsedSeconds = builder.elapsedSeconds; deaths = builder.deaths; respawnSeconds = builder.respawnSeconds; collectibleFound = builder.collectibleFound; player = builder.player; bodies = Collections.unmodifiableList(new ArrayList<>(builder.bodies)); objects = Collections.unmodifiableList(new ArrayList<>(builder.objects)); platformMotionTimes = Collections.unmodifiableList( new ArrayList<>(builder.platformMotionTimes)); toggleSignals = Collections.unmodifiableMap( new LinkedHashMap<>(builder.toggleSignals)); timedSignals = Collections.unmodifiableMap(new LinkedHashMap<>(builder.timedSignals)); previousSwitchContacts = Collections.unmodifiableMap( new LinkedHashMap<>(builder.previousSwitchContacts)); } public static Builder builder(int levelNumber) { return new Builder(levelNumber); } public int saveVersion() { return saveVersion; } public int levelNumber() { return levelNumber; } public float elapsedSeconds() { return elapsedSeconds; } public int deaths() { return deaths; } public float respawnSeconds() { return respawnSeconds; } public boolean collectibleFound() { return collectibleFound; } public PlayerData player() { return player; } public List bodies() { return bodies; } public List objects() { return objects; } public List platformMotionTimes() { return platformMotionTimes; } public Map toggleSignals() { return toggleSignals; } public Map timedSignals() { return timedSignals; } public Map previousSwitchContacts() { return previousSwitchContacts; } }