feat(participation): isolate opted-out combat
Release / release (push) Successful in 2m24s
CI / build (push) Successful in 1m4s

This commit is contained in:
dmg
2026-08-14 23:33:24 -04:00
parent fe07c202f9
commit 24c54a7281
7 changed files with 276 additions and 0 deletions
@@ -0,0 +1,64 @@
package games.dmg.spigottyrant;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
public final class ParticipationCombatListener implements Listener {
public static final String OPTED_OUT_MESSAGE =
"That player has chosen not to take part in the Tyrant event.";
private final TyrantStateManager stateManager;
private final ParticipationCombatService combat;
public ParticipationCombatListener(
TyrantStateManager stateManager,
ParticipationCombatService combat
) {
this.stateManager = stateManager;
this.combat = combat;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof Player target)) {
return;
}
Optional<Player> attacker = attackingPlayer(event);
if (attacker.isEmpty()) {
return;
}
Player source = attacker.orElseThrow();
Map<UUID, PlayerState> players = stateManager.players();
PlayerState attackerState = players.getOrDefault(
source.getUniqueId(),
PlayerState.newPlayer(source.getUniqueId(), source.getName())
);
PlayerState targetState = players.getOrDefault(
target.getUniqueId(),
PlayerState.newPlayer(target.getUniqueId(), target.getName())
);
if (combat.blocks(stateManager.game(), attackerState, targetState)) {
event.setCancelled(true);
source.sendMessage(ChatColor.YELLOW + OPTED_OUT_MESSAGE);
}
}
private static Optional<Player> attackingPlayer(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof Player player) {
return Optional.of(player);
}
if (event.getDamager() instanceof Projectile projectile
&& projectile.getShooter() instanceof Player player) {
return Optional.of(player);
}
return Optional.empty();
}
}
@@ -0,0 +1,24 @@
package games.dmg.spigottyrant;
public final class ParticipationCombatService {
public boolean blocks(
GameState game,
PlayerState attacker,
PlayerState target
) {
if (game.lifecycle() != GameLifecycle.RUNNING) {
return false;
}
boolean attackerOptedOut = attacker.optedOutUntil().isPresent();
boolean targetOptedOut = target.optedOutUntil().isPresent();
boolean attackerTyrantSide = isTyrantSide(game, attacker);
boolean targetTyrantSide = isTyrantSide(game, target);
return (attackerTyrantSide && targetOptedOut)
|| (attackerOptedOut && targetTyrantSide);
}
private static boolean isTyrantSide(GameState game, PlayerState player) {
return game.tyrantId().filter(player.playerId()::equals).isPresent()
|| player.tyrantClass() != TyrantClass.NONE;
}
}
@@ -122,6 +122,12 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
),
this
);
getServer().getPluginManager().registerEvents(
new ParticipationCombatListener(
stateManager, new ParticipationCombatService()
),
this
);
getServer().getPluginManager().registerEvents(
new VigilanteCombatListener(
stateManager, combatTracker, clock, settings.vigilanteCombatDuration()