arena options stay changeable until the countdown and reopen with a right click

This commit is contained in:
MineTec committed 2026-10-15 23:25:21 +02:00
1 parent ae3bde626f
commit 6bda5b6adb
4 files changed
+135 -43

No files matched your search

@@ -101,10 +101,6 @@ final class Lobby implements PadLobby.Listener {
} }
void handleMenuClick(Player player, ModeMenu menu, ModeMenu.Click click) { void handleMenuClick(Player player, ModeMenu menu, ModeMenu.Click click) {
if(!this.pads.canConfigure(player.getUniqueId())) {
player.closeInventory();
return;
}
int ownSize = menu.ownSize; int ownSize = menu.ownSize;
int opponentSize = menu.opponentSize; int opponentSize = menu.opponentSize;
FightMode mode = menu.mode; FightMode mode = menu.mode;
@@ -116,13 +112,27 @@ final class Lobby implements PadLobby.Listener {
InteractSounds.of(player).success(); InteractSounds.of(player).success();
player.closeInventory(); player.closeInventory();
player.sendActionBar(Component.text(this.pads.size(this.pads.challengerPad()) + " gegen " + this.pads.size(1 - this.pads.challengerPad()) player.sendActionBar(Component.text(this.pads.size(this.pads.challengerPad()) + " gegen " + this.pads.size(1 - this.pads.challengerPad())
+ ", Modus: " + this.modeName(), NamedTextColor.GOLD)); + ", Modus: " + this.modeName() + ". Rechtsklick öffnet die Auswahl wieder.", NamedTextColor.GOLD));
return; return;
} }
} }
// switching to teams drops the opponent's inventory mode // switching to teams drops the opponent's inventory mode
if(!mode.isAllowed(ownSize, opponentSize)) mode = FightMode.OWN; if(!mode.isAllowed(ownSize, opponentSize)) mode = FightMode.OWN;
if(!this.pads.configure(player.getUniqueId(), ownSize, opponentSize, mode)) return;
int ownPad = this.pads.challengerPad();
String problem = switch(this.pads.configure(player.getUniqueId(), ownSize, opponentSize, mode)) {
case OK -> null;
case NOT_ALLOWED -> "Der Countdown läuft schon";
case OWN_TOO_SMALL -> "Auf deinem Feld stehen schon " + this.pads.players(ownPad).size();
case OPPONENT_TOO_SMALL -> "Auf dem anderen Feld stehen schon " + this.pads.players(1 - ownPad).size();
case INVALID -> "Das geht so nicht";
};
if(problem != null) {
Style.play(player, Sound.BLOCK_NOTE_BLOCK_BASS, 0.6f);
player.sendActionBar(Component.text(problem, NamedTextColor.RED));
if(!this.pads.canConfigure(player.getUniqueId())) player.closeInventory();
return;
}
menu.ownSize = ownSize; menu.ownSize = ownSize;
menu.opponentSize = opponentSize; menu.opponentSize = opponentSize;
menu.mode = mode; menu.mode = mode;
@@ -130,6 +140,17 @@ final class Lobby implements PadLobby.Listener {
InteractSounds.of(player).click(); InteractSounds.of(player).click();
} }
/**
* Right click on the pad: the challenger opens the options again.
*/
boolean openMenu(Player player) {
if(!this.pads.canConfigure(player.getUniqueId()) || ModeMenu.isOpen(player)) return false;
int pad = this.pads.challengerPad();
InteractSounds.of(player).open();
ModeMenu.open(player, pad, this.pads.size(pad), this.pads.size(1 - pad), this.pads.mode());
return true;
}
private boolean isTeamFight() { private boolean isTeamFight() {
return this.pads.size(0) > 1 || this.pads.size(1) > 1; return this.pads.size(0) > 1 || this.pads.size(1) > 1;
} }
@@ -168,24 +189,29 @@ final class Lobby implements PadLobby.Listener {
} }
@Override @Override
public void challengerSelected(UUID challenger, boolean canConfigure) { public void challengerSelected(UUID challenger, boolean first) {
Player player = Bukkit.getPlayer(challenger); Player player = Bukkit.getPlayer(challenger);
if(player == null) return; if(player == null) return;
if(!canConfigure) { if(!first) {
player.sendMessage(Style.message("Du führst jetzt die Herausforderung an. Teams und Modus bleiben wie gewählt.")); player.sendMessage(Style.message("Du führst jetzt die Herausforderung an. Rechtsklick auf dem Feld öffnet die Auswahl."));
Style.play(player, Sound.BLOCK_NOTE_BLOCK_PLING, 1.8f);
return; return;
} }
player.sendMessage(Style.message("Du forderst heraus. Wähle Teamgröße und Modus. Wer sich auf die Felder stellt, macht mit.")); player.sendMessage(Style.message("Du forderst heraus. Wähle Teamgröße und Modus, Rechtsklick auf dem Feld öffnet die Auswahl wieder. Wer sich auf die Felder stellt, macht mit."));
InteractSounds.of(player).open(); this.openMenu(player);
int pad = this.pads.challengerPad(); }
ModeMenu.open(player, pad, this.pads.size(pad), this.pads.size(1 - pad), this.pads.mode());
@Override
public void countdownStarted() {
UUID challenger = this.pads.challenger();
Player player = challenger == null ? null : Bukkit.getPlayer(challenger);
if(player != null && ModeMenu.isOpen(player)) player.closeInventory();
} }
@Override @Override
public void kitAssigned(UUID uuid, UUID kitOwner) { public void kitAssigned(UUID uuid, UUID kitOwner) {
Player player = Bukkit.getPlayer(uuid); Player player = Bukkit.getPlayer(uuid);
if(player == null) return; if(player == null) return;
if(ModeMenu.isOpen(player)) player.closeInventory();
InventoryStore.apply(player, kitOwner); InventoryStore.apply(player, kitOwner);
if(uuid.equals(kitOwner)) return; if(uuid.equals(kitOwner)) return;
Player owner = Bukkit.getPlayer(kitOwner); Player owner = Bukkit.getPlayer(kitOwner);
@@ -3,6 +3,9 @@ package eu.mhsl.craftattack.spawn.arena.appliances.arena;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
class ModeMenuListener extends ApplianceListener<Arena> { class ModeMenuListener extends ApplianceListener<Arena> {
@@ -17,4 +20,13 @@ class ModeMenuListener extends ApplianceListener<Arena> {
Lobby lobby = this.getAppliance().lobby(); Lobby lobby = this.getAppliance().lobby();
if(click != null && lobby != null) lobby.handleMenuClick(player, menu, click); if(click != null && lobby != null) lobby.handleMenuClick(player, menu, click);
} }
@EventHandler
public void onRightClick(PlayerInteractEvent event) {
if(event.getHand() != EquipmentSlot.HAND) return;
if(event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
if(this.getAppliance().state(event.getPlayer()) != Arena.PlayerState.PAD) return;
Lobby lobby = this.getAppliance().lobby();
if(lobby != null && lobby.openMenu(event.getPlayer())) event.setCancelled(true);
}
} }
@@ -18,12 +18,15 @@ public class PadLobby {
public static final int MAX_TEAM_SIZE = 4; public static final int MAX_TEAM_SIZE = 4;
public interface Listener { public interface Listener {
/** The player is now the challenger. Only when alone on the pads the options can be changed. */ /** The player is now the challenger and may change the options until the countdown runs. */
void challengerSelected(UUID challenger, boolean canConfigure); void challengerSelected(UUID challenger, boolean first);
/** The player should now hold the inventory of {@code kitOwner} (the own one to revert). */ /** The player should now hold the inventory of {@code kitOwner} (the own one to revert). */
void kitAssigned(UUID player, UUID kitOwner); void kitAssigned(UUID player, UUID kitOwner);
/** Both pads are exactly full, the options are fixed now. */
void countdownStarted();
void countdown(int secondsLeft); void countdown(int secondsLeft);
void countdownCancelled(); void countdownCancelled();
@@ -70,7 +73,10 @@ public class PadLobby {
} }
this.refreshKits(); this.refreshKits();
if(this.isFull()) this.ticksLeft = this.countdownTicks; if(this.isFull()) {
this.ticksLeft = this.countdownTicks;
this.listener.countdownStarted();
}
return true; return true;
} }
@@ -93,30 +99,46 @@ public class PadLobby {
this.refreshKits(); this.refreshKits();
// the longest waiting player takes over; team sizes and mode stay as chosen // the longest waiting player takes over; team sizes and mode stay as chosen
if(wasChallenger && !this.order.isEmpty()) { if(wasChallenger && !this.order.isEmpty()) {
this.listener.challengerSelected(this.order.getFirst(), this.order.size() == 1); this.listener.challengerSelected(this.order.getFirst(), false);
} }
} }
public enum ConfigureResult {
OK,
NOT_ALLOWED,
OWN_TOO_SMALL,
OPPONENT_TOO_SMALL,
INVALID
}
/** /**
* Only the challenger can change the options, and only while standing alone on the pads. * Only the challenger can change the options, and only until the countdown runs.
* Teams cannot be smaller than the players already standing there.
* *
* @param ownSize players on the challenger's pad * @param ownSize players on the challenger's pad
* @param opponentSize players on the other pad * @param opponentSize players on the other pad
*/ */
public boolean configure(UUID player, int ownSize, int opponentSize, FightMode mode) { public ConfigureResult configure(UUID player, int ownSize, int opponentSize, FightMode mode) {
if(!this.canConfigure(player)) return false; if(!this.canConfigure(player)) return ConfigureResult.NOT_ALLOWED;
if(ownSize < 1 || ownSize > MAX_TEAM_SIZE || opponentSize < 1 || opponentSize > MAX_TEAM_SIZE) return false; if(ownSize < 1 || ownSize > MAX_TEAM_SIZE || opponentSize < 1 || opponentSize > MAX_TEAM_SIZE) return ConfigureResult.INVALID;
if(!mode.isAllowed(ownSize, opponentSize)) return false; if(!mode.isAllowed(ownSize, opponentSize)) return ConfigureResult.INVALID;
int own = this.challengerPad(); int own = this.challengerPad();
if(ownSize < this.pads.get(own).size()) return ConfigureResult.OWN_TOO_SMALL;
if(opponentSize < this.pads.get(1 - own).size()) return ConfigureResult.OPPONENT_TOO_SMALL;
this.sizes[own] = ownSize; this.sizes[own] = ownSize;
this.sizes[1 - own] = opponentSize; this.sizes[1 - own] = opponentSize;
this.mode = mode; this.mode = mode;
this.refreshKits(); this.refreshKits();
return true; if(this.isFull()) {
this.ticksLeft = this.countdownTicks;
this.listener.countdownStarted();
}
return ConfigureResult.OK;
} }
public boolean canConfigure(UUID player) { public boolean canConfigure(UUID player) {
return player.equals(this.challenger()) && this.order.size() == 1; return player.equals(this.challenger()) && !this.isCountingDown();
} }
/** /**
@@ -24,8 +24,12 @@ class PadLobbyTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
this.lobby = new PadLobby(new PadLobby.Listener() { this.lobby = new PadLobby(new PadLobby.Listener() {
public void challengerSelected(UUID challenger, boolean canConfigure) { public void challengerSelected(UUID challenger, boolean first) {
PadLobbyTest.this.events.add("challenger " + PadLobbyTest.this.name(challenger) + (canConfigure ? " configurable" : "")); PadLobbyTest.this.events.add("challenger " + PadLobbyTest.this.name(challenger) + (first ? " first" : ""));
}
public void countdownStarted() {
PadLobbyTest.this.events.add("countdown started");
} }
public void kitAssigned(UUID player, UUID kitOwner) { public void kitAssigned(UUID player, UUID kitOwner) {
@@ -67,7 +71,7 @@ class PadLobbyTest {
@Test @Test
void firstOnPadBecomesChallengerAndMayConfigure() { void firstOnPadBecomesChallengerAndMayConfigure() {
assertTrue(this.lobby.enter(1, this.a)); assertTrue(this.lobby.enter(1, this.a));
assertEquals(List.of("challenger a configurable"), this.events); assertEquals(List.of("challenger a first"), this.events);
assertEquals(this.a, this.lobby.challenger()); assertEquals(this.a, this.lobby.challenger());
assertTrue(this.lobby.canConfigure(this.a)); assertTrue(this.lobby.canConfigure(this.a));
assertFalse(this.lobby.isCountingDown()); assertFalse(this.lobby.isCountingDown());
@@ -76,14 +80,15 @@ class PadLobbyTest {
@Test @Test
void oneVersusOneStartsAfterCountdown() { void oneVersusOneStartsAfterCountdown() {
this.lobby.enter(0, this.a); this.lobby.enter(0, this.a);
assertTrue(this.lobby.configure(this.a, 1, 1, FightMode.CHALLENGER)); assertEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 1, 1, FightMode.CHALLENGER));
this.lobby.enter(1, this.b); this.lobby.enter(1, this.b);
assertFalse(this.lobby.configure(this.a, 1, 1, FightMode.OWN), "options are fixed once someone else joined"); assertEquals(PadLobby.ConfigureResult.NOT_ALLOWED, this.lobby.configure(this.a, 1, 1, FightMode.OWN), "options are fixed during the countdown");
this.run(41); this.run(41);
assertEquals(List.of( assertEquals(List.of(
"challenger a configurable", "challenger a first",
"kit b<-a", "kit b<-a",
"countdown started",
"countdown 2", "countdown 2",
"countdown 1", "countdown 1",
"start a vs b by a CHALLENGER" "start a vs b by a CHALLENGER"
@@ -103,7 +108,7 @@ class PadLobbyTest {
@Test @Test
void unevenTeamsStartWhenBothPadsAreExactlyFull() { void unevenTeamsStartWhenBothPadsAreExactlyFull() {
this.lobby.enter(0, this.a); this.lobby.enter(0, this.a);
assertTrue(this.lobby.configure(this.a, 2, 3, FightMode.OWN)); assertEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 2, 3, FightMode.OWN));
assertTrue(this.lobby.enter(0, this.b)); assertTrue(this.lobby.enter(0, this.b));
assertFalse(this.lobby.enter(0, this.c), "red is full with 2"); assertFalse(this.lobby.enter(0, this.c), "red is full with 2");
this.lobby.enter(1, this.c); this.lobby.enter(1, this.c);
@@ -122,16 +127,16 @@ class PadLobbyTest {
this.lobby.enter(0, this.b); this.lobby.enter(0, this.b);
this.lobby.enter(1, this.c); this.lobby.enter(1, this.c);
this.lobby.enter(1, this.d); this.lobby.enter(1, this.d);
assertEquals(List.of("challenger a configurable", "kit b<-a", "kit c<-a", "kit d<-a"), this.events); assertEquals(List.of("challenger a first", "kit b<-a", "kit c<-a", "kit d<-a", "countdown started"), this.events);
} }
@Test @Test
void opponentModeOnlyForOneVersusOne() { void opponentModeOnlyForOneVersusOne() {
this.lobby.enter(0, this.a); this.lobby.enter(0, this.a);
assertFalse(this.lobby.configure(this.a, 2, 1, FightMode.OPPONENT)); assertNotEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 2, 1, FightMode.OPPONENT));
assertTrue(this.lobby.configure(this.a, 1, 1, FightMode.OPPONENT)); assertEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 1, 1, FightMode.OPPONENT));
this.lobby.enter(1, this.b); this.lobby.enter(1, this.b);
assertEquals("kit a<-b", this.events.getLast()); assertTrue(this.events.contains("kit a<-b"));
this.lobby.leave(this.b); this.lobby.leave(this.b);
assertTrue(this.events.contains("kit a<-a"), "challenger gets the own inventory back"); assertTrue(this.events.contains("kit a<-a"), "challenger gets the own inventory back");
@@ -141,9 +146,9 @@ class PadLobbyTest {
@Test @Test
void onlyChallengerMayConfigure() { void onlyChallengerMayConfigure() {
this.lobby.enter(0, this.a); this.lobby.enter(0, this.a);
assertFalse(this.lobby.configure(this.b, 2, 2, FightMode.OWN)); assertNotEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.b, 2, 2, FightMode.OWN));
assertFalse(this.lobby.configure(this.a, 5, 1, FightMode.OWN)); assertNotEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 5, 1, FightMode.OWN));
assertFalse(this.lobby.configure(this.a, 0, 1, FightMode.OWN)); assertNotEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 0, 1, FightMode.OWN));
} }
@Test @Test
@@ -184,20 +189,47 @@ class PadLobbyTest {
assertEquals(List.of("kit a<-a", "kit b<-b", "kit c<-b", "challenger b"), this.events); assertEquals(List.of("kit a<-a", "kit b<-b", "kit c<-b", "challenger b"), this.events);
assertEquals(this.b, this.lobby.challenger()); assertEquals(this.b, this.lobby.challenger());
assertFalse(this.lobby.canConfigure(this.b), "others are still there"); assertTrue(this.lobby.canConfigure(this.b), "the new challenger may change the options");
assertEquals(2, this.lobby.size(0)); assertEquals(2, this.lobby.size(0));
assertEquals(2, this.lobby.size(1)); assertEquals(2, this.lobby.size(1));
} }
@Test @Test
void aloneAgainMeansConfigurableAgain() { void handOverWhenAlone() {
this.lobby.enter(0, this.a); this.lobby.enter(0, this.a);
this.lobby.enter(1, this.b); this.lobby.enter(1, this.b);
this.lobby.leave(this.a); this.lobby.leave(this.a);
assertEquals("challenger b configurable", this.events.getLast()); assertEquals("challenger b", this.events.getLast());
assertTrue(this.lobby.canConfigure(this.b)); assertTrue(this.lobby.canConfigure(this.b));
} }
@Test
void challengerMayChangeOptionsWhileOthersWait() {
this.lobby.enter(0, this.a);
this.lobby.configure(this.a, 2, 2, FightMode.OWN);
this.lobby.enter(0, this.b);
this.lobby.enter(1, this.c);
assertEquals(PadLobby.ConfigureResult.OWN_TOO_SMALL, this.lobby.configure(this.a, 1, 2, FightMode.OWN),
"two already stand on the own pad");
assertEquals(2, this.lobby.size(0));
this.events.clear();
assertEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 2, 1, FightMode.CHALLENGER));
assertEquals(List.of("kit b<-a", "kit c<-a", "countdown started"), this.events, "shrinking to the players there starts the countdown");
assertTrue(this.lobby.isCountingDown());
}
@Test
void opponentSideCannotShrinkBelowPlayers() {
this.lobby.enter(0, this.a);
this.lobby.configure(this.a, 1, 3, FightMode.OWN);
this.lobby.enter(1, this.b);
this.lobby.enter(1, this.c);
assertEquals(PadLobby.ConfigureResult.OPPONENT_TOO_SMALL, this.lobby.configure(this.a, 1, 1, FightMode.OWN));
assertEquals(PadLobby.ConfigureResult.OK, this.lobby.configure(this.a, 1, 4, FightMode.OWN));
}
@Test @Test
void lastPlayerLeavingResetsLobby() { void lastPlayerLeavingResetsLobby() {
this.lobby.enter(0, this.a); this.lobby.enter(0, this.a);
@@ -205,6 +237,6 @@ class PadLobbyTest {
assertTrue(this.lobby.isIdle()); assertTrue(this.lobby.isIdle());
assertNull(this.lobby.challenger()); assertNull(this.lobby.challenger());
this.lobby.leave(this.b); this.lobby.leave(this.b);
assertEquals(List.of("challenger a configurable", "kit a<-a"), this.events); assertEquals(List.of("challenger a first", "kit a<-a"), this.events);
} }
} }