Files
purpur-tyrant/src/main/java/games/dmg/spigottyrant/ClassAssignmentService.java
T
dmg 1642d46f1b
Release / release (push) Successful in 2m17s
CI / build (push) Successful in 1m1s
feat(assassin): add stealth and double jump
2026-08-14 23:08:18 -04:00

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();
};
}
}