Skip to content

Commit fecc0d0

Browse files
committed
Merge branch 'dev' of github.com:Mere-Solace/Sword-Combat-Plugin into dev
2 parents 47942bc + fda7a6f commit fecc0d0

36 files changed

Lines changed: 1581 additions & 266 deletions

src/main/java/btm/sword/Sword.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import btm.sword.commands.SwordCommands;
1414
import btm.sword.config.ConfigManager;
1515
import btm.sword.listeners.EntityListener;
16+
import btm.sword.listeners.ErrorListener;
1617
import btm.sword.listeners.InputListener;
1718
import btm.sword.listeners.PlayerListener;
1819
import btm.sword.listeners.SystemListener;
@@ -68,6 +69,7 @@ public void onEnable() {
6869

6970
InvUI.getInstance().setPlugin(this);
7071

72+
getServer().getPluginManager().registerEvents(new ErrorListener(), this);
7173
getServer().getPluginManager().registerEvents(new InputListener(), this);
7274
getServer().getPluginManager().registerEvents(new PlayerListener(), this);
7375
getServer().getPluginManager().registerEvents(new ServerJoinArbiter(), this);

src/main/java/btm/sword/config/Config.java

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4258,4 +4258,144 @@ public static class MenuGrid {
42584258
}
42594259
}
42604260
//endregion
4261+
4262+
// ==============================================================================
4263+
//region ROGUELIKE - Wave enemy run configuration
4264+
// ==============================================================================
4265+
/**
4266+
* Configuration for the roguelike wave-run gamemode.
4267+
* <p>
4268+
* Controls the spawn center location, spawn radius, and per-wave enemy counts.
4269+
* All values are hot-reloadable via {@code /sword reload}.
4270+
* </p>
4271+
*/
4272+
public static class Roguelike {
4273+
4274+
/** World name for the roguelike spawn center. */
4275+
public static String SPAWN_WORLD = "world";
4276+
4277+
/** X coordinate of the spawn center. */
4278+
public static double SPAWN_X = 0.0;
4279+
4280+
/** Y coordinate of the spawn center. */
4281+
public static double SPAWN_Y = 64.0;
4282+
4283+
/** Z coordinate of the spawn center. */
4284+
public static double SPAWN_Z = 0.0;
4285+
4286+
/** Radius within which enemies spawn around the center, in blocks. */
4287+
public static double SPAWN_RADIUS = 8.0;
4288+
4289+
/** Number of Pillagers spawned in wave 1. */
4290+
public static int WAVE_1_PILLAGERS = 3;
4291+
4292+
/** Number of Wither Skeletons spawned in wave 2. */
4293+
public static int WAVE_2_WITHER_SKELETONS = 3;
4294+
4295+
/** Number of Pillagers spawned in wave 3. */
4296+
public static int WAVE_3_PILLAGERS = 2;
4297+
4298+
/** Number of Wither Skeletons spawned in wave 3. */
4299+
public static int WAVE_3_WITHER_SKELETONS = 3;
4300+
4301+
static {
4302+
register("roguelike.spawn_world", SPAWN_WORLD, String.class,
4303+
v -> SPAWN_WORLD = v, ConfigurationSection::getString);
4304+
register("roguelike.spawn_x", SPAWN_X, Double.class,
4305+
v -> SPAWN_X = v, ConfigurationSection::getDouble);
4306+
register("roguelike.spawn_y", SPAWN_Y, Double.class,
4307+
v -> SPAWN_Y = v, ConfigurationSection::getDouble);
4308+
register("roguelike.spawn_z", SPAWN_Z, Double.class,
4309+
v -> SPAWN_Z = v, ConfigurationSection::getDouble);
4310+
register("roguelike.spawn_radius", SPAWN_RADIUS, Double.class,
4311+
v -> SPAWN_RADIUS = v, ConfigurationSection::getDouble);
4312+
register("roguelike.wave_1_pillagers", WAVE_1_PILLAGERS, Integer.class,
4313+
v -> WAVE_1_PILLAGERS = v, ConfigurationSection::getInt);
4314+
register("roguelike.wave_2_wither_skeletons", WAVE_2_WITHER_SKELETONS, Integer.class,
4315+
v -> WAVE_2_WITHER_SKELETONS = v, ConfigurationSection::getInt);
4316+
register("roguelike.wave_3_pillagers", WAVE_3_PILLAGERS, Integer.class,
4317+
v -> WAVE_3_PILLAGERS = v, ConfigurationSection::getInt);
4318+
register("roguelike.wave_3_wither_skeletons", WAVE_3_WITHER_SKELETONS, Integer.class,
4319+
v -> WAVE_3_WITHER_SKELETONS = v, ConfigurationSection::getInt);
4320+
}
4321+
}
4322+
//region Ctf
4323+
/**
4324+
* Configuration for the Capture the Flag gamemode.
4325+
* <p>
4326+
* Controls team spawn locations, flag return timer, respawn delay, win threshold, and
4327+
* the speed debuff applied to flag carriers.
4328+
* All values are hot-reloadable via {@code /sword reload}.
4329+
* </p>
4330+
*/
4331+
public static class Ctf {
4332+
4333+
/** World name shared by both team spawns. */
4334+
public static String SPAWN_WORLD = "world";
4335+
4336+
/** X coordinate of the RED team spawn (also the RED flag spawn). */
4337+
public static double RED_SPAWN_X = 15.0;
4338+
/** Y coordinate of the RED team spawn. */
4339+
public static double RED_SPAWN_Y = 64.0;
4340+
/** Z coordinate of the RED team spawn. */
4341+
public static double RED_SPAWN_Z = 0.0;
4342+
4343+
/** X coordinate of the BLUE team spawn (also the BLUE flag spawn). */
4344+
public static double BLUE_SPAWN_X = -15.0;
4345+
/** Y coordinate of the BLUE team spawn. */
4346+
public static double BLUE_SPAWN_Y = 64.0;
4347+
/** Z coordinate of the BLUE team spawn. */
4348+
public static double BLUE_SPAWN_Z = 0.0;
4349+
4350+
/** Seconds before a dropped flag automatically returns to its base. */
4351+
public static int FLAG_RETURN_TIMER_SECONDS = 30;
4352+
4353+
/** Seconds before a dead player respawns at their team spawn. */
4354+
public static int RESPAWN_DELAY_SECONDS = 5;
4355+
4356+
/**
4357+
* Number of flag captures required to win.
4358+
* Set to 0 for a timer-only match (most captures at expiry wins).
4359+
*/
4360+
public static int CAPTURES_TO_WIN = 3;
4361+
4362+
/** Radius in blocks within which a flag carrier scores a capture when near their own base. */
4363+
public static double CAPTURE_RADIUS = 3.0;
4364+
4365+
/** Duration in ticks of the Slowness effect applied to flag carriers. Use a large value for a persistent effect. */
4366+
public static int FLAG_CARRIER_SLOW_DURATION = 999999;
4367+
4368+
/** Amplifier (0-based) of the Slowness effect on flag carriers. 0 = Slowness I, 1 = Slowness II. */
4369+
public static int FLAG_CARRIER_SLOW_AMPLIFIER = 1;
4370+
4371+
static {
4372+
register("ctf.spawn_world", SPAWN_WORLD, String.class,
4373+
v -> SPAWN_WORLD = v, ConfigurationSection::getString);
4374+
register("ctf.red_spawn_x", RED_SPAWN_X, Double.class,
4375+
v -> RED_SPAWN_X = v, ConfigurationSection::getDouble);
4376+
register("ctf.red_spawn_y", RED_SPAWN_Y, Double.class,
4377+
v -> RED_SPAWN_Y = v, ConfigurationSection::getDouble);
4378+
register("ctf.red_spawn_z", RED_SPAWN_Z, Double.class,
4379+
v -> RED_SPAWN_Z = v, ConfigurationSection::getDouble);
4380+
register("ctf.blue_spawn_x", BLUE_SPAWN_X, Double.class,
4381+
v -> BLUE_SPAWN_X = v, ConfigurationSection::getDouble);
4382+
register("ctf.blue_spawn_y", BLUE_SPAWN_Y, Double.class,
4383+
v -> BLUE_SPAWN_Y = v, ConfigurationSection::getDouble);
4384+
register("ctf.blue_spawn_z", BLUE_SPAWN_Z, Double.class,
4385+
v -> BLUE_SPAWN_Z = v, ConfigurationSection::getDouble);
4386+
register("ctf.flag_return_timer_seconds", FLAG_RETURN_TIMER_SECONDS, Integer.class,
4387+
v -> FLAG_RETURN_TIMER_SECONDS = v, ConfigurationSection::getInt);
4388+
register("ctf.respawn_delay_seconds", RESPAWN_DELAY_SECONDS, Integer.class,
4389+
v -> RESPAWN_DELAY_SECONDS = v, ConfigurationSection::getInt);
4390+
register("ctf.captures_to_win", CAPTURES_TO_WIN, Integer.class,
4391+
v -> CAPTURES_TO_WIN = v, ConfigurationSection::getInt);
4392+
register("ctf.capture_radius", CAPTURE_RADIUS, Double.class,
4393+
v -> CAPTURE_RADIUS = v, ConfigurationSection::getDouble);
4394+
register("ctf.flag_carrier_slow_duration", FLAG_CARRIER_SLOW_DURATION, Integer.class,
4395+
v -> FLAG_CARRIER_SLOW_DURATION = v, ConfigurationSection::getInt);
4396+
register("ctf.flag_carrier_slow_amplifier", FLAG_CARRIER_SLOW_AMPLIFIER, Integer.class,
4397+
v -> FLAG_CARRIER_SLOW_AMPLIFIER = v, ConfigurationSection::getInt);
4398+
}
4399+
}
4400+
//endregion
42614401
}
Lines changed: 105 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,57 @@
11
package btm.sword.gamemode;
22

3+
import java.util.ArrayList;
4+
import java.util.Collections;
35
import java.util.HashMap;
6+
import java.util.List;
47
import java.util.Map;
58
import java.util.Queue;
9+
import java.util.UUID;
610
import java.util.concurrent.ConcurrentLinkedQueue;
711

812
import btm.sword.gamemode.type.CaptureTheFlag1v1;
913
import btm.sword.gamemode.type.Gamemode;
14+
import btm.sword.gamemode.type.RoguelikeRun;
1015
import btm.sword.system.entity.impl.SwordPlayer;
1116

1217
/**
13-
* Manages per-{@link Gamemode} matchmaking queues.
18+
* Manages per-{@link Gamemode} matchmaking queues and tracks active matches.
1419
* <p>
15-
* Players are enqueued via {@link #enqueue} and matched when the arena is available and
16-
* the queue has at least two players. Arena integration is not yet implemented — see
17-
* {@link #tryStartNextMatch()}.
20+
* Players are enqueued via {@link #enqueue} and matched when the queue has enough players.
21+
* Roguelike runs start solo (1 player); CTF requires 2.
1822
* </p>
23+
*
24+
* <p>Active CTF matches are registered in {@link #activeMatches} so that
25+
* {@link btm.sword.listeners.PlayerListener} can route death events to the correct match.</p>
1926
*/
2027
public class QueueManager {
2128

2229
private static final Map<Class<? extends Gamemode>, Queue<SwordPlayer>> queueMap;
2330

31+
/**
32+
* Maps each participating player's UUID to their active CTF match.
33+
* Populated on match start, cleaned up on match stop.
34+
*/
35+
private static final Map<UUID, CaptureTheFlag1v1> activeMatches = new HashMap<>();
36+
37+
/**
38+
* All currently active roguelike runs.
39+
* Populated when a run starts, removed when it stops.
40+
*/
41+
private static final List<RoguelikeRun> activeRoguelikeRuns = new ArrayList<>();
42+
2443
static {
2544
queueMap = new HashMap<>();
2645
queueMap.put(CaptureTheFlag1v1.class, new ConcurrentLinkedQueue<>());
46+
queueMap.put(RoguelikeRun.class, new ConcurrentLinkedQueue<>());
2747
}
2848

2949
/**
3050
* Adds the given player to the queue for the specified game mode.
3151
* No-ops (with a message) if the player is already queued.
3252
*
33-
* @param gamemode the game mode class to queue for
34-
* @param swordPlayer the player requesting to join the queue
53+
* @param gamemode the game mode class to queue for
54+
* @param swordPlayer the player requesting to join the queue
3555
*/
3656
public static void enqueue(Class<? extends Gamemode> gamemode, SwordPlayer swordPlayer) {
3757
Queue<SwordPlayer> currentPlayerQueue = queueMap.get(gamemode);
@@ -47,10 +67,86 @@ public static void enqueue(Class<? extends Gamemode> gamemode, SwordPlayer sword
4767
}
4868

4969
/**
50-
* Attempts to start the next match if the arena is free and at least two players are queued.
51-
* Arena integration is pending — this method is currently a no-op.
70+
* Immediately launches a CTF match for the given player without going through the queue.
71+
* Intended for developer testing and single-player debug sessions.
72+
*
73+
* @param swordPlayer the player to start the match for
74+
*/
75+
public static void startCtfDebug(SwordPlayer swordPlayer) {
76+
startCtfMatch(List.of(swordPlayer));
77+
}
78+
79+
/**
80+
* Attempts to start any matches where the queue meets the player threshold.
81+
* <p>
82+
* Roguelike runs start immediately with 1 player. CTF requires 2.
83+
* </p>
5284
*/
5385
public static void tryStartNextMatch() {
54-
// TODO: integrate ArenaManager once arena lifecycle is implemented
86+
Queue<SwordPlayer> roguelikeQueue = queueMap.get(RoguelikeRun.class);
87+
if (roguelikeQueue != null && !roguelikeQueue.isEmpty()) {
88+
SwordPlayer player = roguelikeQueue.poll();
89+
startRoguelikeRun(List.of(player));
90+
}
91+
92+
Queue<SwordPlayer> ctfQueue = queueMap.get(CaptureTheFlag1v1.class);
93+
if (ctfQueue != null && ctfQueue.size() >= 2) {
94+
SwordPlayer p1 = ctfQueue.poll();
95+
SwordPlayer p2 = ctfQueue.poll();
96+
startCtfMatch(List.of(p1, p2));
97+
}
98+
}
99+
100+
/**
101+
* Returns the active CTF match for the given player UUID, or {@code null} if they are not in one.
102+
*
103+
* @param uuid the player's UUID
104+
* @return the active {@link CaptureTheFlag1v1}, or {@code null}
105+
*/
106+
public static CaptureTheFlag1v1 getActiveCtfMatch(UUID uuid) {
107+
return activeMatches.get(uuid);
108+
}
109+
110+
/**
111+
* Removes the given player from all active match registrations.
112+
* Called automatically when a match ends.
113+
*
114+
* @param uuid the UUID to deregister
115+
*/
116+
public static void deregisterFromMatch(UUID uuid) {
117+
activeMatches.remove(uuid);
118+
}
119+
120+
/**
121+
* Returns an unmodifiable view of all currently active roguelike runs.
122+
*
123+
* @return list of active {@link RoguelikeRun} instances
124+
*/
125+
public static List<RoguelikeRun> getActiveRoguelikeRuns() {
126+
return Collections.unmodifiableList(activeRoguelikeRuns);
127+
}
128+
129+
/**
130+
* Removes the given roguelike run from the active run registry.
131+
* Should be called when the run ends.
132+
*
133+
* @param run the run to deregister
134+
*/
135+
public static void deregisterRoguelikeRun(RoguelikeRun run) {
136+
activeRoguelikeRuns.remove(run);
137+
}
138+
139+
private static void startRoguelikeRun(List<SwordPlayer> players) {
140+
RoguelikeRun run = new RoguelikeRun(players);
141+
activeRoguelikeRuns.add(run);
142+
run.start();
143+
}
144+
145+
private static void startCtfMatch(List<SwordPlayer> players) {
146+
CaptureTheFlag1v1 match = new CaptureTheFlag1v1(players);
147+
for (SwordPlayer sp : players) {
148+
activeMatches.put(sp.player().getUniqueId(), match);
149+
}
150+
match.start();
55151
}
56152
}

0 commit comments

Comments
 (0)