138 lines
4.8 KiB
Java
138 lines
4.8 KiB
Java
package games.dmg.spigottyrant;
|
|
|
|
import java.time.Duration;
|
|
import java.time.Instant;
|
|
import java.util.EnumMap;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
|
|
public final class GameLifecycleService {
|
|
public LifecycleState start(
|
|
PersistentState state,
|
|
UUID tyrantId,
|
|
Optional<UUID> vigilanteId,
|
|
Instant now
|
|
) {
|
|
if (state.game().lifecycle() != GameLifecycle.UNSTARTED) {
|
|
throw new IllegalStateException("game has already been started");
|
|
}
|
|
if (vigilanteId.filter(tyrantId::equals).isPresent()) {
|
|
throw new IllegalArgumentException("Tyrant and Vigilante must be different players");
|
|
}
|
|
GameState game = new GameState(
|
|
GameLifecycle.RUNNING,
|
|
Optional.of(tyrantId),
|
|
vigilanteId,
|
|
Optional.empty(),
|
|
Optional.empty(),
|
|
Optional.empty(),
|
|
Duration.ZERO,
|
|
0,
|
|
1,
|
|
Set.of()
|
|
);
|
|
return new LifecycleState(game, state.players());
|
|
}
|
|
|
|
public LifecycleState pause(PersistentState state, Instant now) {
|
|
if (state.game().lifecycle() != GameLifecycle.RUNNING) {
|
|
throw new IllegalStateException("only a running game can be paused");
|
|
}
|
|
GameState current = state.game();
|
|
GameState paused = new GameState(
|
|
GameLifecycle.PAUSED,
|
|
current.tyrantId(),
|
|
current.vigilanteId(),
|
|
current.pendingTyrant(),
|
|
current.pendingVigilante(),
|
|
Optional.of(now),
|
|
current.accumulatedPausedTime(),
|
|
current.tyrantLevel(),
|
|
current.unspentChoices(),
|
|
current.purchases()
|
|
);
|
|
return new LifecycleState(paused, state.players());
|
|
}
|
|
|
|
public LifecycleState resume(PersistentState state, Instant now) {
|
|
GameState current = state.game();
|
|
if (current.lifecycle() != GameLifecycle.PAUSED || current.pausedAt().isEmpty()) {
|
|
throw new IllegalStateException("only a paused game can be resumed");
|
|
}
|
|
Instant pausedAt = current.pausedAt().orElseThrow();
|
|
Duration pausedFor = Duration.between(pausedAt, now);
|
|
if (pausedFor.isNegative()) {
|
|
throw new IllegalArgumentException("resume time must not precede pause time");
|
|
}
|
|
GameState resumed = new GameState(
|
|
GameLifecycle.RUNNING,
|
|
current.tyrantId(),
|
|
current.vigilanteId(),
|
|
shiftSelection(current.pendingTyrant(), pausedFor),
|
|
shiftSelection(current.pendingVigilante(), pausedFor),
|
|
Optional.empty(),
|
|
current.accumulatedPausedTime().plus(pausedFor),
|
|
current.tyrantLevel(),
|
|
current.unspentChoices(),
|
|
current.purchases()
|
|
);
|
|
Map<UUID, PlayerState> players = new HashMap<>();
|
|
for (PlayerState player : state.players().values()) {
|
|
players.put(player.playerId(), shift(player, pausedFor));
|
|
}
|
|
return new LifecycleState(resumed, players);
|
|
}
|
|
|
|
public LifecycleState reset(PersistentState state) {
|
|
Map<UUID, PlayerState> players = new HashMap<>();
|
|
for (PlayerState player : state.players().values()) {
|
|
players.put(player.playerId(), new PlayerState(
|
|
player.playerId(),
|
|
player.latestName(),
|
|
player.lastLogin(),
|
|
player.optedOutUntil(),
|
|
TyrantClass.NONE,
|
|
Optional.empty(),
|
|
Map.of(),
|
|
Set.of(),
|
|
player.capturedMobs()
|
|
));
|
|
}
|
|
return new LifecycleState(GameState.empty(), players);
|
|
}
|
|
|
|
private static Optional<PendingSelection> shiftSelection(
|
|
Optional<PendingSelection> selection,
|
|
Duration pausedFor
|
|
) {
|
|
return selection.map(value -> new PendingSelection(
|
|
value.candidateId(), value.expiresAt().plus(pausedFor)
|
|
));
|
|
}
|
|
|
|
private static PlayerState shift(PlayerState player, Duration pausedFor) {
|
|
Map<Ability, Instant> cooldowns = new EnumMap<>(Ability.class);
|
|
player.cooldownEnds().forEach((ability, deadline) ->
|
|
cooldowns.put(ability, deadline.plus(pausedFor))
|
|
);
|
|
return new PlayerState(
|
|
player.playerId(),
|
|
player.latestName(),
|
|
shiftInstant(player.lastLogin(), pausedFor),
|
|
shiftInstant(player.optedOutUntil(), pausedFor),
|
|
player.tyrantClass(),
|
|
player.followerOf(),
|
|
cooldowns,
|
|
player.readyAbilityItems(),
|
|
player.capturedMobs()
|
|
);
|
|
}
|
|
|
|
private static Optional<Instant> shiftInstant(Optional<Instant> value, Duration duration) {
|
|
return value.map(instant -> instant.plus(duration));
|
|
}
|
|
}
|