140 lines
4.9 KiB
Java
140 lines
4.9 KiB
Java
package games.dmg.spigottyrant;
|
|
|
|
import java.time.Instant;
|
|
import java.util.EnumMap;
|
|
import java.util.EnumSet;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
|
|
public final class ClassAssignmentService {
|
|
public ClassAssignmentResult assign(
|
|
GameState game,
|
|
Map<UUID, PlayerState> players,
|
|
UUID assignerId,
|
|
UUID targetId,
|
|
TyrantClass tyrantClass,
|
|
Instant now
|
|
) {
|
|
ClassAssignmentStatus rejected = rejection(
|
|
game, players, assignerId, targetId, tyrantClass, now
|
|
);
|
|
if (rejected != null) {
|
|
return new ClassAssignmentResult(players, rejected, Optional.empty());
|
|
}
|
|
|
|
Map<UUID, PlayerState> updated = new HashMap<>(players);
|
|
Optional<UUID> previousHolder = players.values().stream()
|
|
.filter(player -> player.tyrantClass() == tyrantClass)
|
|
.map(PlayerState::playerId)
|
|
.filter(playerId -> !playerId.equals(targetId))
|
|
.findFirst();
|
|
previousHolder.ifPresent(playerId -> updated.put(
|
|
playerId,
|
|
withClass(updated.get(playerId), TyrantClass.NONE)
|
|
));
|
|
updated.put(targetId, withClass(updated.get(targetId), tyrantClass));
|
|
return new ClassAssignmentResult(
|
|
updated,
|
|
ClassAssignmentStatus.ASSIGNED,
|
|
previousHolder
|
|
);
|
|
}
|
|
|
|
public Map<UUID, PlayerState> clearAll(Map<UUID, PlayerState> players) {
|
|
Map<UUID, PlayerState> updated = new HashMap<>();
|
|
for (PlayerState player : players.values()) {
|
|
updated.put(player.playerId(), withClass(player, TyrantClass.NONE));
|
|
}
|
|
return Map.copyOf(updated);
|
|
}
|
|
|
|
private static ClassAssignmentStatus rejection(
|
|
GameState game,
|
|
Map<UUID, PlayerState> players,
|
|
UUID assignerId,
|
|
UUID targetId,
|
|
TyrantClass tyrantClass,
|
|
Instant now
|
|
) {
|
|
if (game.lifecycle() != GameLifecycle.RUNNING) {
|
|
return ClassAssignmentStatus.GAME_NOT_RUNNING;
|
|
}
|
|
if (game.tyrantId().isEmpty() || !game.tyrantId().orElseThrow().equals(assignerId)) {
|
|
return ClassAssignmentStatus.NOT_TYRANT;
|
|
}
|
|
if (tyrantClass == null || tyrantClass == TyrantClass.NONE) {
|
|
return ClassAssignmentStatus.INVALID_CLASS;
|
|
}
|
|
if (!game.purchases().contains(unlockFor(tyrantClass))) {
|
|
return ClassAssignmentStatus.CLASS_NOT_UNLOCKED;
|
|
}
|
|
PlayerState target = players.get(targetId);
|
|
if (target == null) {
|
|
return ClassAssignmentStatus.UNKNOWN_PLAYER;
|
|
}
|
|
if (targetId.equals(assignerId)) {
|
|
return ClassAssignmentStatus.TARGET_IS_TYRANT;
|
|
}
|
|
if (target.optedOutUntil().isPresent()) {
|
|
return ClassAssignmentStatus.TARGET_OPTED_OUT;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static TyrantUnlock unlockFor(TyrantClass tyrantClass) {
|
|
return switch (tyrantClass) {
|
|
case ASSASSIN -> TyrantUnlock.ASSASSIN;
|
|
case FIXER -> TyrantUnlock.FIXER;
|
|
case TAMER -> TyrantUnlock.TAMER;
|
|
case NONE -> throw new IllegalArgumentException("NONE is not assignable");
|
|
};
|
|
}
|
|
|
|
private static PlayerState withClass(PlayerState player, TyrantClass tyrantClass) {
|
|
Map<Ability, Instant> cooldowns = new EnumMap<>(Ability.class);
|
|
cooldowns.putAll(player.cooldownEnds());
|
|
Set<Ability> readyItems = EnumSet.noneOf(Ability.class);
|
|
readyItems.addAll(player.readyAbilityItems());
|
|
for (Ability ability : abilitiesFor(player.tyrantClass())) {
|
|
cooldowns.remove(ability);
|
|
readyItems.remove(ability);
|
|
}
|
|
readyItemFor(tyrantClass).ifPresent(readyItems::add);
|
|
return new PlayerState(
|
|
player.playerId(),
|
|
player.latestName(),
|
|
player.lastLogin(),
|
|
player.optedOutUntil(),
|
|
tyrantClass,
|
|
player.followerOf(),
|
|
cooldowns,
|
|
readyItems,
|
|
player.capturedMobs()
|
|
);
|
|
}
|
|
|
|
private static Optional<Ability> readyItemFor(TyrantClass tyrantClass) {
|
|
return switch (tyrantClass) {
|
|
case ASSASSIN -> Optional.of(Ability.ASSASSIN_INVISIBILITY);
|
|
case FIXER -> Optional.of(Ability.FIXER_BOOST);
|
|
case TAMER -> Optional.of(Ability.TAMER_CAPTURE);
|
|
case NONE -> Optional.empty();
|
|
};
|
|
}
|
|
|
|
private static Set<Ability> abilitiesFor(TyrantClass tyrantClass) {
|
|
return switch (tyrantClass) {
|
|
case ASSASSIN -> Set.of(
|
|
Ability.ASSASSIN_INVISIBILITY,
|
|
Ability.ASSASSIN_DOUBLE_JUMP
|
|
);
|
|
case FIXER -> Set.of(Ability.FIXER_BOOST);
|
|
case TAMER -> Set.of(Ability.TAMER_CAPTURE);
|
|
case NONE -> Set.of();
|
|
};
|
|
}
|
|
}
|