Przeglądaj źródła

Merge pull request #129 from RedstoneFuture/feature/move-command

Adding MOVE-Command, Improving messages for team-switch and game-join, Bug-Fixing
RedstoneFuture 1 rok temu
rodzic
commit
f53e8966cd

+ 15 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/MWCommandCompletions.java

@@ -38,6 +38,7 @@ public class MWCommandCompletions {
         registerMissilesResult();
         registerArenasResult();
         registerTeamsResult();
+        registerGamePlayerResult();
     }
 
     private void registerGamesResult() {
@@ -85,5 +86,19 @@ public class MWCommandCompletions {
             return ImmutableList.of("1", "2", "spec");
         });
     }
+    
+    private void registerGamePlayerResult() {
+        commandCompletions.registerCompletion("game-players", c -> {
+            CommandSender sender = c.getSender();
+
+            if (!(sender instanceof Player)) return null;
+            Player player = (Player) sender;
 
+            Game game = GameManager.getInstance().getGame(player.getLocation());
+            if (game == null) return null;
+            
+            return game.getPlayerList();
+        });
+    }
+    
 }

+ 107 - 9
missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/MWCommands.java

@@ -24,16 +24,15 @@ import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.Messages;
-import de.butzlabben.missilewars.game.Arenas;
-import de.butzlabben.missilewars.game.Game;
-import de.butzlabben.missilewars.game.GameManager;
-import de.butzlabben.missilewars.game.TeamManager;
+import de.butzlabben.missilewars.game.*;
 import de.butzlabben.missilewars.game.enums.GameResult;
 import de.butzlabben.missilewars.game.enums.GameState;
+import de.butzlabben.missilewars.game.enums.TeamType;
 import de.butzlabben.missilewars.game.enums.VoteState;
 import de.butzlabben.missilewars.game.schematics.SchematicFacing;
 import de.butzlabben.missilewars.game.schematics.objects.Missile;
 import de.butzlabben.missilewars.game.timer.LobbyTimer;
+import de.butzlabben.missilewars.player.MWPlayer;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 
@@ -47,7 +46,7 @@ public class MWCommands extends BaseCommand {
         sendHelpMessage(sender, "mw.mapmenu", "/mw mapmenu", "Open the map-vote menu.");
         sendHelpMessage(sender, "mw.vote", "/mw vote <arena>", "Vote for a arena.");
         sendHelpMessage(sender, "mw.teammenu", "/mw teammenu", "Open the team-change menu.");
-        sendHelpMessage(sender, "mw.change.use", "/mw change <1|2|spec>", "Changes your team.");
+        sendHelpMessage(sender, "mw.change.use", "/mw change <1|2|spec>", "Change your team.");
         sendHelpMessage(sender, "mw.quit", "/mw quit", "Quit a game.");
 
         sendHelpMessage(sender, "mw.stats", "/mw stats [from] [arena]", "Shows stats.");
@@ -56,6 +55,7 @@ public class MWCommands extends BaseCommand {
         sendHelpMessage(sender, "mw.stats.list", "/mw stats list [from] [arena]", "Lists history of games.");
 
         sendHelpMessage(sender, "mw.listgames", "/mw listgames", "List the active games.");
+        sendHelpMessage(sender, "mw.move", "/mw move <player> <1|2|spec>", "Change the team of a specific player.");
         sendHelpMessage(sender, "mw.paste", "/mw paste <missile>", "Pastes a missile.");
         sendHelpMessage(sender, "mw.start", "/mw start [lobby]", "Starts the game.");
         sendHelpMessage(sender, "mw.stop", "/mw stop [lobby]", "Stops the game.");
@@ -99,9 +99,107 @@ public class MWCommands extends BaseCommand {
         }
 
     }
+    
+    @Subcommand("move")
+    @CommandCompletion("@game-players @teams @nothing")
+    @CommandPermission("mw.move")
+    public void moveCommand(CommandSender sender, String[] args) {
+
+        if (!MWCommands.senderIsPlayer(sender)) return;
+        Player player = (Player) sender;
+
+        if (args.length < 2) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_TEAM_NUMBER_NEEDED));
+            return;
+        }
+
+        if (args.length > 2) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_TO_MANY_ARGUMENTS));
+            return;
+        }
+
+        Game game = GameManager.getInstance().getGame(player.getLocation());
+        if (game == null) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.GAME_NOT_IN_GAME_AREA));
+            return;
+        }
+        
+        Player targetPlayer = MissileWars.getInstance().getServer().getPlayer(args[0]);
+        if (targetPlayer == null) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_INVALID_PLAYER_NOT_ONLINE)
+                    .replace("%input%", args[0]));
+            return;
+        }
+        
+        MWPlayer targetMwPlayer = game.getPlayer(targetPlayer);
+        if (targetMwPlayer == null) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_INVALID_PLAYER_NOT_IN_GAME)
+                    .replace("%input%", args[0]));
+            return;
+        }
+        
+        // The "isTeamchangeOngoingGame()" check is skipped here.
+        
+        
+        Team from = targetMwPlayer.getTeam();
+        Team to;
+        
+        switch (args[1]) {
+            case "1":
+            case "team1":
+                to = game.getTeamManager().getTeam1();
+                break;
+            case "2":
+            case "team2":
+                to = game.getTeamManager().getTeam2();
+                break;
+            case "spec":
+            case "spectator":
+                to = game.getTeamManager().getTeamSpec();
+                break;
+            default:
+                sender.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_INVALID_TEAM));
+                return;
+        }
+        
+        // Is the same team?
+        if (from == to) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_MOVE_ALREADY_IN_TEAM)
+                    .replace("%player%", targetPlayer.getName()));
+            return;
+        }
+        
+        if (game.getState() != GameState.END) {
+            // Is the player the last one from his team?
+            if ((from.getTeamType() == TeamType.PLAYER) && (from.getMembers().size() == 1)) {
+                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_MOVE_IS_LAST_PLAYER)
+                    .replace("%from%", from.getFullname()));
+                return;
+            }
+            
+        } else {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_MOVE_TEAM_NOT_NOW));
+            return;
+            
+        }
+        
+        // The "isValidFairSwitch()" validation and max-user check is skipped here.
+        
+        sender.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_MOVE_MOVED_SENDER)
+                .replace("%player%", targetPlayer.getName())
+                .replace("%from%", from.getFullname())
+                .replace("%to%", to.getFullname()));
+        
+        targetPlayer.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_MOVE_MOVED_TARGET)
+                .replace("%sender%", player.getName())
+                .replace("%from%", from.getFullname())
+                .replace("%to%", to.getFullname()));
+        
+        game.getGameJoinManager().runPlayerTeamSwitch(targetMwPlayer, to);
+    }
 
     @Subcommand("paste")
-    @CommandCompletion("@missiles")
+    @CommandCompletion("@missiles @nothing")
     @CommandPermission("mw.paste")
     public void pasteCommand(CommandSender sender, String[] args) {
 
@@ -136,7 +234,7 @@ public class MWCommands extends BaseCommand {
     }
 
     @Subcommand("start")
-    @CommandCompletion("@games")
+    @CommandCompletion("@games @nothing")
     @CommandPermission("mw.start")
     public void startCommand(CommandSender sender, String[] args) {
 
@@ -184,7 +282,7 @@ public class MWCommands extends BaseCommand {
     }
 
     @Subcommand("stop")
-    @CommandCompletion("@games")
+    @CommandCompletion("@games @nothing")
     @CommandPermission("mw.stop")
     public void stopCommand(CommandSender sender, String[] args) {
 
@@ -219,7 +317,7 @@ public class MWCommands extends BaseCommand {
     }
 
     @Subcommand("appendrestart")
-    @CommandCompletion("@games")
+    @CommandCompletion("@games @nothing")
     @CommandPermission("mw.appendrestart")
     public void appendrestartCommand(CommandSender sender, String[] args) {
 

+ 18 - 18
missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/SetupCommands.java

@@ -95,7 +95,7 @@ public class SetupCommands extends BaseCommand {
         public class SpectatorspawnSetup extends BaseCommand {
 
             @Subcommand("set")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void set(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -106,7 +106,7 @@ public class SetupCommands extends BaseCommand {
             }
 
             @Subcommand("teleport|tp")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void teleport(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -121,7 +121,7 @@ public class SetupCommands extends BaseCommand {
         public class Team1spawnSetup extends BaseCommand {
 
             @Subcommand("set")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void set(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -132,7 +132,7 @@ public class SetupCommands extends BaseCommand {
             }
 
             @Subcommand("teleport|tp")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void teleport(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -147,7 +147,7 @@ public class SetupCommands extends BaseCommand {
         public class Team2spawnSetup extends BaseCommand {
 
             @Subcommand("set")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void set(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -158,7 +158,7 @@ public class SetupCommands extends BaseCommand {
             }
 
             @Subcommand("teleport|tp")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void teleport(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -176,7 +176,7 @@ public class SetupCommands extends BaseCommand {
             public class Pos1Setup extends BaseCommand {
 
                 @Subcommand("set")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void set(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;
@@ -188,7 +188,7 @@ public class SetupCommands extends BaseCommand {
                 }
 
                 @Subcommand("teleport|tp")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void teleport(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;
@@ -203,7 +203,7 @@ public class SetupCommands extends BaseCommand {
             public class Pos2Setup extends BaseCommand {
 
                 @Subcommand("set")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void set(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;
@@ -215,7 +215,7 @@ public class SetupCommands extends BaseCommand {
                 }
 
                 @Subcommand("teleport|tp")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void teleport(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;
@@ -236,7 +236,7 @@ public class SetupCommands extends BaseCommand {
         public class SpawnpointSetup extends BaseCommand {
 
             @Subcommand("set")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void set(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -247,7 +247,7 @@ public class SetupCommands extends BaseCommand {
             }
 
             @Subcommand("teleport|tp")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void teleport(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -262,7 +262,7 @@ public class SetupCommands extends BaseCommand {
         public class AftergamespawnSetup extends BaseCommand {
 
             @Subcommand("set")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void set(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -273,7 +273,7 @@ public class SetupCommands extends BaseCommand {
             }
 
             @Subcommand("teleport|tp")
-            @CommandCompletion("@games")
+            @CommandCompletion("@games @nothing")
             public void teleport(CommandSender sender, String[] args) {
                 if (!senderIsPlayer(sender)) return;
                 if (!isValidGame(args)) return;
@@ -291,7 +291,7 @@ public class SetupCommands extends BaseCommand {
             public class Pos1Setup extends BaseCommand {
 
                 @Subcommand("set")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void set(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;
@@ -303,7 +303,7 @@ public class SetupCommands extends BaseCommand {
                 }
 
                 @Subcommand("teleport|tp")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void teleport(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;
@@ -318,7 +318,7 @@ public class SetupCommands extends BaseCommand {
             public class Pos2Setup extends BaseCommand {
 
                 @Subcommand("set")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void set(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;
@@ -330,7 +330,7 @@ public class SetupCommands extends BaseCommand {
                 }
 
                 @Subcommand("teleport|tp")
-                @CommandCompletion("@games")
+                @CommandCompletion("@games @nothing")
                 public void teleport(CommandSender sender, String[] args) {
                     if (!senderIsPlayer(sender)) return;
                     if (!isValidGame(args)) return;

+ 6 - 6
missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/UserCommands.java

@@ -38,7 +38,7 @@ import org.bukkit.entity.Player;
 public class UserCommands extends BaseCommand {
     
     @Subcommand("vote")
-    @CommandCompletion("@arenas")
+    @CommandCompletion("@arenas @nothing")
     @CommandPermission("mw.vote")
     public void voteCommand(CommandSender sender, String[] args) {
 
@@ -106,7 +106,7 @@ public class UserCommands extends BaseCommand {
     }
 
     @Subcommand("change|switch|team")
-    @CommandCompletion("@teams")
+    @CommandCompletion("@teams @nothing")
     @CommandPermission("mw.change.use")
     public void changeCommand(CommandSender sender, String[] args) {
 
@@ -199,14 +199,14 @@ public class UserCommands extends BaseCommand {
         
         // Is the same team?
         if (from == to) {
-            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_ALREADY_IN_TEAM));
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_CHANGE_ALREADY_IN_TEAM));
             return;
         }
         
         // Would the number of team members be too far apart?
         if (game.getState() != GameState.LOBBY) {
             if (!game.getTeamManager().isValidFairSwitch(from, to)) {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_UNFAIR_TEAM_SIZE));
+                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_CHANGE_UNFAIR_TEAM_SIZE));
                 return;
             }
         }
@@ -214,12 +214,12 @@ public class UserCommands extends BaseCommand {
         // Checking max-user values:
         if (to == game.getTeamManager().getTeamSpec()) {
             if (game.areTooManySpectators()) {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_MAX_REACHED));
+                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_MAX_REACHED));
                 return;
             }
         } else {
             if (game.areTooManyPlayers()) {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_MAX_REACHED));
+                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_TEAM_MAX_REACHED));
                 return;
             }
         }

+ 7 - 7
missilewars-plugin/src/main/java/de/butzlabben/missilewars/configuration/Config.java

@@ -101,13 +101,13 @@ public class Config {
         cfg.addDefault("fightstats.enable", false);
         cfg.addDefault("fightstats.show_real_skins", true);
 
-        Location spawnLocation = Bukkit.getWorlds().get(0).getSpawnLocation().add(25, 0, 25);
-        cfg.addDefault("fallback_spawn.world", spawnLocation.getWorld().getName());
-        cfg.addDefault("fallback_spawn.x", spawnLocation.getX());
-        cfg.addDefault("fallback_spawn.y", spawnLocation.getY());
-        cfg.addDefault("fallback_spawn.z", spawnLocation.getZ());
-        cfg.addDefault("fallback_spawn.yaw", spawnLocation.getYaw());
-        cfg.addDefault("fallback_spawn.pitch", spawnLocation.getPitch());
+        Location worldSpawnLoc = Bukkit.getWorlds().get(0).getSpawnLocation();
+        cfg.addDefault("fallback_spawn.world", worldSpawnLoc.getWorld().getName());
+        cfg.addDefault("fallback_spawn.x", worldSpawnLoc.getX());
+        cfg.addDefault("fallback_spawn.y", worldSpawnLoc.getY());
+        cfg.addDefault("fallback_spawn.z", worldSpawnLoc.getZ());
+        cfg.addDefault("fallback_spawn.yaw", worldSpawnLoc.getYaw());
+        cfg.addDefault("fallback_spawn.pitch", worldSpawnLoc.getPitch());
 
         cfg.addDefault("mysql.host", "localhost");
         cfg.addDefault("mysql.database", "db");

+ 22 - 15
missilewars-plugin/src/main/java/de/butzlabben/missilewars/configuration/Messages.java

@@ -106,14 +106,17 @@ public class Messages {
         COMMAND_MISSILE_NEEDED("command.missile_needed", "&cPlease specify the missile."),
         COMMAND_MAP_NEEDED("command.map_needed", "&cPlease specify the map."),
         COMMAND_TEAM_NUMBER_NEEDED("command.team_number_needed", "&cPlease specify the team number."),
-        COMMAND_INVALID_MISSILE("command.invalid_missile", "&cThe specified missile %input% was not found."),
-        COMMAND_INVALID_SHIELD("command.invalid_shield", "&cThe specified shield %input% was not found."),
+        COMMAND_INVALID_MISSILE("command.invalid_missile", "&cThe specified missile %input% was not found for this MissileWars game."),
+        COMMAND_INVALID_SHIELD("command.invalid_shield", "&cThe specified shield %input% was not found for this MissileWars game."),
         COMMAND_INVALID_GAME("command.invalid_game", "&cThe specified game %input% was not found."),
-        COMMAND_INVALID_MAP("command.invalid_map", "&cThe specified map %input% was not found."),
+        COMMAND_INVALID_MAP("command.invalid_map", "&cThe specified map %input% was not found for this MissileWars game."),
         COMMAND_INVALID_TEAM("command.invalid_team", "&cThe team selection is invalid. Use \"1\" or \"2\" to join on of the player-teams or use \"spec\" to enter the game as spectator."),
+        COMMAND_INVALID_PLAYER_NOT_ONLINE("command.invalid_player.not_online", "&cThe specified player %input% is not online on this server."),
+        COMMAND_INVALID_PLAYER_NOT_IN_GAME("command.invalid_player.not_in_game", "&cThe specified player %input% is not in this MissileWars game."),
         COMMAND_ANTISPAM_TEAM_CHANGE("command.antispam.team_change", "&cYou have to wait %seconds% seconds before being able to change the team again."),
         
         GAME_PLAYER_JOINED("game.player_joined", "&e%player% &7joined the game (%team%&7)."),
+        GAME_PLAYER_SWITCHED("game.player_switched", "&e%player% &7switched the team (%from%&7 → %to%&7)."),
         GAME_PLAYER_LEFT("game.player_left", "&e%player% &7left the game (%team%&7)."),
         GAME_LEFT("game.left", "&7You left the running MissileWars game."),
         GAME_NOT_IN_GAME_AREA("game.not_in_game_area", "&cYou are not in an arena right now."),
@@ -132,25 +135,29 @@ public class Messages {
         ENDGAME_TIMER_GAME_STARTS_NEW_IN("endgame_timer.game_starts_new_in", "&7Game starts new in &e%seconds% &7seconds."),
 
         LOBBY_PLAYER_JOINED("lobby.player_joined", "&e%player% &7joined the game &8(&7%players%&8/&7%max_players%&8)"),
+        LOBBY_PLAYER_SWITCHED("lobby.player_switched", "&e%player% &7rejoined the game &8(&7%players%&8/&7%max_players%&8)"),
         LOBBY_PLAYER_LEFT("lobby.player_left", "&e%player% &7left the game &8(&7%players%&8/&7%max_players%&8)"),
         LOBBY_LEFT("lobby.left", "&7You left the MissileWars lobby."),
         LOBBY_NOT_ENOUGH_PLAYERS("lobby.not_enough_players", "&cThere are not enough players online."),
         LOBBY_TEAMS_UNEQUAL("lobby.teams_unequal", "&cThe teams are unequal distributed."),
         
-        TEAM_CHANGE_TEAM_NOT_NOW("team.change_team_not_now", "&cThe game is not in the right state to change your team right now."),
-        TEAM_CHANGE_TEAM_NO_LONGER_NOW("team.change_team_no_longer_now", "&cNow you cannot change your team anymore."),
-        TEAM_ALREADY_IN_TEAM("team.already_in_team", "&cYou are already in this team."),
-        TEAM_UNFAIR_TEAM_SIZE("team.unfair_team_size", "&cChanging the team would make the number of team members more uneven."),
-        TEAM_PLAYER_TEAM_CHANGED("team.player.team_changed", "&7You are now in %team%&7."),
-        TEAM_SPECTATOR_TEAM_CHANGED("team.spectator.team_changed", "&7You are now a %team%&7."),
-        TEAM_PLAYER_TEAM_ASSIGNED("team.player.team_assigned", "&7You have been assigned to %team%&7."),
-        TEAM_SPECTATOR_TEAM_ASSIGNED("team.spectator.team_assigned", "&7You have been assigned to spectator."),
-        TEAM_PLAYER_MAX_REACHED("team.player.max_reached", "&cThe maximum number of players has been reached."),
-        TEAM_SPECTATOR_MAX_REACHED("team.spectator.max_reached", "&cThe maximum number of spectators has been reached."),
+        TEAM_CHANGE_TEAM_NOT_NOW("team.change.team_not_now", "&cThe game is not in the right state to change your team right now."),
+        TEAM_MOVE_TEAM_NOT_NOW("team.move.team_not_now", "&cThe game is not in the right state to change your team right now."),
+        TEAM_CHANGE_TEAM_NO_LONGER_NOW("team.change.team_no_longer_now", "&cNow you cannot change your team anymore."),
+        TEAM_CHANGE_ALREADY_IN_TEAM("team.change.already_in_team", "&cYou are already in this team."),
+        TEAM_MOVE_ALREADY_IN_TEAM("team.move.already_in_team", "&cThe specified player %player% is already in this team."),
+        TEAM_CHANGE_UNFAIR_TEAM_SIZE("team.change.unfair_team_size", "&cChanging the team would make the number of team members more uneven."),
+        TEAM_MOVE_IS_LAST_PLAYER("team.move.is_last_player", "&cChanging the team cancelled, because there are no more players in the team %from%&7."),
+        TEAM_PLAYER_TEAM_SWITCH("team.player_team.switch", "&7You are now in %team%&7."),
+        TEAM_SPECTATOR_TEAM_SWITCH("team.spectator_team.switch", "&7You are now a %team%&7."),
+        TEAM_PLAYER_TEAM_ASSIGNED("team.player_team.assigned", "&7You have been assigned to %team%&7."),
+        TEAM_SPECTATOR_TEAM_ASSIGNED("team.spectator_team.assigned", "&7You have been assigned to spectator."),
+        TEAM_PLAYER_TEAM_MAX_REACHED("team.player_team.max_reached", "&cThe maximum number of players has been reached."),
+        TEAM_SPECTATOR_TEAM_MAX_REACHED("team.spectator_team.max_reached", "&cThe maximum number of spectators has been reached."),
         TEAM_ALL_TEAMMATES_OFFLINE("team.all_teammates_offline", "&7Everyone from %team% &7is offline."),
-        TEAM_TEAM_BUFFED("team.team_buffed", "%team% &7was buffed as one player left the team."),
-        TEAM_TEAM_NERVED("team.team_nerved", "%team% &7was nerved as one player joined the team."),
         TEAM_HURT_TEAMMATES("team.hurt_teammates", "&cYou must not hurt your teammates."),
+        TEAM_MOVE_MOVED_SENDER("team.move.moved.sender", "&7You moved %player% in another team."),
+        TEAM_MOVE_MOVED_TARGET("team.move.moved.target", "&cYou was moved from %sender% to another team."),
 
         ARENA_ARENA_LEAVE("arena.arena_leave", "&cYou are not allowed to leave the arena."),
         ARENA_MISSILE_PLACE_DENY("arena.missile_place_deny", "&cYou are not allowed to place a missile here."),

+ 4 - 3
missilewars-plugin/src/main/java/de/butzlabben/missilewars/configuration/lobby/Lobby.java

@@ -20,6 +20,7 @@ package de.butzlabben.missilewars.configuration.lobby;
 
 import com.google.gson.annotations.SerializedName;
 import de.butzlabben.missilewars.Logger;
+import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.arena.AreaConfiguration;
 import de.butzlabben.missilewars.configuration.arena.Arena;
 import de.butzlabben.missilewars.game.Arenas;
@@ -59,9 +60,9 @@ public class Lobby {
     @SerializedName("team_1") private GameTeamConfiguration team1Config = new GameTeamConfiguration("Team1", "&c");
     @SerializedName("team_2") private GameTeamConfiguration team2Config = new GameTeamConfiguration("Team2", "&a");
     @SerializedName("team_spectator") private GameTeamConfiguration teamConfigSpec = new GameTeamConfiguration("Spectator", "&f");
-    @Setter @SerializedName("spawn_point") private Location spawnPoint = getBukkitDefaultWorld().getSpawnLocation();
-    @Setter @SerializedName("after_game_spawn") private Location afterGameSpawn = getBukkitDefaultWorld().getSpawnLocation();
-    @Setter @SerializedName("area") private AreaConfiguration areaConfig = AreaConfiguration.aroundLocation(getBukkitDefaultWorld().getSpawnLocation(), 30);
+    @Setter @SerializedName("spawn_point") private Location spawnPoint = Config.getFallbackSpawn().add(40, 0, 0);
+    @Setter @SerializedName("after_game_spawn") private Location afterGameSpawn = Config.getFallbackSpawn();
+    @Setter @SerializedName("area") private AreaConfiguration areaConfig = AreaConfiguration.aroundLocation(spawnPoint, 20);
     @SerializedName("map_choose_procedure") private MapChooseProcedure mapChooseProcedure = MapChooseProcedure.FIRST;
     @SerializedName("join_ongoing_game") private JoinIngameBehavior joinIngameBehavior = JoinIngameBehavior.SPECTATOR;
     @SerializedName("rejoin_ongoing_game") private RejoinIngameBehavior rejoinIngameBehavior = RejoinIngameBehavior.LAST_TEAM;

+ 8 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Game.java

@@ -701,6 +701,14 @@ public class Game {
         if ((teamManager.getTeam1() == null) || (teamManager.getTeam2() == null)) return 0;
         return teamManager.getTeam1().getMembers().size() + teamManager.getTeam2().getMembers().size();
     }
+    
+    public List<String> getPlayerList() {
+        List<String> playerList = new ArrayList<>();
+        
+        players.values().forEach(mwPlayer -> playerList.add(mwPlayer.getPlayer().getName()));
+        
+        return playerList;
+    }
 
     public static void knockbackEffect(Player player, Location from, Location to) {
         Vector addTo = from.toVector().subtract(to.toVector()).multiply(3);

+ 37 - 18
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/GameJoinManager.java

@@ -91,6 +91,7 @@ public class GameJoinManager {
         
         sendJoinBroadcastMsg(mwPlayer);
         sendJoinPrivateMsg(mwPlayer, false);
+        
         player.setScoreboard(game.getScoreboardManager().getBoard());
         
         if (game.getState() == GameState.LOBBY) {
@@ -110,6 +111,7 @@ public class GameJoinManager {
     
     public void runPlayerTeamSwitch(MWPlayer mwPlayer, Team targetTeam) {
         Player player = mwPlayer.getPlayer();
+        Team oldTeam = mwPlayer.getTeam();
         
         setDefaultPlayerData(player);
         
@@ -117,8 +119,9 @@ public class GameJoinManager {
         game.getGameLeaveManager().playerLeaveFromTeam(mwPlayer);
         targetTeam.addMember(mwPlayer);
         
-        sendJoinBroadcastMsg(mwPlayer);
-        sendJoinPrivateMsg(mwPlayer, true);
+        sendTeamSwitchBroadcastMsg(mwPlayer, oldTeam);
+        // Sending the private info message is skipped here.
+        
         // Manual update of the scoreboard because the event listener was not addressed.
         game.getScoreboardManager().updateScoreboard();
         
@@ -200,43 +203,59 @@ public class GameJoinManager {
     private void sendJoinBroadcastMsg(MWPlayer mwPlayer) {
         Player player = mwPlayer.getPlayer();
         
-        String broadcastMsg = null;
+        String broadcastMsg;
         if (game.getState() == GameState.LOBBY) {
             broadcastMsg = Messages.getMessage(true, Messages.MessageEnum.LOBBY_PLAYER_JOINED);
-        } else if ((game.getState() == GameState.INGAME) || (game.getState() == GameState.END)) {
+        } else {
             broadcastMsg = Messages.getMessage(true, Messages.MessageEnum.GAME_PLAYER_JOINED);
         }
         
-        if (broadcastMsg != null) {
-            game.broadcast(broadcastMsg.replace("%max_players%", Integer.toString(game.getLobby().getMaxPlayers()))
-                    .replace("%players%", Integer.toString(game.getPlayerAmount()))
-                    .replace("%player%", player.getName())
-                    .replace("%team%", (mwPlayer.getTeam() != null) ? mwPlayer.getTeam().getFullname() : "?"));
+        game.broadcast(broadcastMsg.replace("%max_players%", Integer.toString(game.getLobby().getMaxPlayers()))
+                .replace("%players%", Integer.toString(game.getPlayerAmount()))
+                .replace("%player%", player.getName())
+                .replace("%team%", (mwPlayer.getTeam() != null) ? mwPlayer.getTeam().getFullname() : "?"));
+    }
+    
+    private void sendTeamSwitchBroadcastMsg(MWPlayer mwPlayer, Team oldTeam) {
+        Player player = mwPlayer.getPlayer();
+        
+        String broadcastMsg;
+        if (game.getState() == GameState.LOBBY) {
+            broadcastMsg = Messages.getMessage(true, Messages.MessageEnum.LOBBY_PLAYER_SWITCHED);
+        } else {
+            broadcastMsg = Messages.getMessage(true, Messages.MessageEnum.GAME_PLAYER_SWITCHED);
         }
+        
+        game.broadcast(broadcastMsg.replace("%max_players%", Integer.toString(game.getLobby().getMaxPlayers()))
+                .replace("%players%", Integer.toString(game.getPlayerAmount()))
+                .replace("%player%", player.getName())
+                .replace("%from%", oldTeam.getFullname())
+                .replace("%to%", mwPlayer.getTeam().getFullname()));
     }
     
     public void sendJoinPrivateMsg(MWPlayer mwPlayer, boolean isTeamSwitch) {
         Player player = mwPlayer.getPlayer();
         
+        String privateMsg;
         if (mwPlayer.getTeam() == teamManager.getTeamSpec()) {
-
             if (isTeamSwitch) {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_CHANGED)
-                        .replace("%team%", mwPlayer.getTeam().getFullname()));
+                privateMsg = Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_SWITCH);
             } else {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_ASSIGNED)
-                        .replace("%team%", mwPlayer.getTeam().getFullname()));
+                privateMsg = Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_ASSIGNED);
             }
             
         } else {
             if (isTeamSwitch) {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_TEAM_CHANGED)
-                        .replace("%team%", mwPlayer.getTeam().getFullname()));
+                privateMsg = Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_TEAM_SWITCH);
             } else {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_TEAM_ASSIGNED)
-                        .replace("%team%", mwPlayer.getTeam().getFullname()));
+                privateMsg = Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_TEAM_ASSIGNED);
             }
         }
+        
+        player.sendMessage(privateMsg.replace("%max_players%", Integer.toString(game.getLobby().getMaxPlayers()))
+                .replace("%players%", Integer.toString(game.getPlayerAmount()))
+                .replace("%player%", player.getName())
+                .replace("%team%", (mwPlayer.getTeam() != null) ? mwPlayer.getTeam().getFullname() : "?"));
     }
 
     private void getGameJoinMenu(MWPlayer mwPlayer) {

+ 0 - 10
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Team.java

@@ -249,15 +249,5 @@ public class Team {
                 break;
         }
     }
-
-    // TODO Add new team buffer
-    public void updateIntervals(int newInterval) {
-        if (newInterval < currentInterval && currentInterval != 0) {
-            getGame().broadcast(Messages.getMessage(true, Messages.MessageEnum.TEAM_TEAM_BUFFED).replace("%team%", getFullname()));
-        }
-        if (newInterval > currentInterval && currentInterval != 0) {
-            getGame().broadcast(Messages.getMessage(true, Messages.MessageEnum.TEAM_TEAM_NERVED).replace("%team%", getFullname()));
-        }
-    }
     
 }

+ 15 - 3
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/EndListener.java

@@ -23,7 +23,6 @@ import de.butzlabben.missilewars.configuration.Messages;
 import de.butzlabben.missilewars.event.PlayerArenaJoinEvent;
 import de.butzlabben.missilewars.event.PlayerArenaLeaveEvent;
 import de.butzlabben.missilewars.game.Game;
-import de.butzlabben.missilewars.game.GameLeaveManager;
 import de.butzlabben.missilewars.game.Team;
 import de.butzlabben.missilewars.game.enums.JoinIngameBehavior;
 import de.butzlabben.missilewars.game.enums.RejoinIngameBehavior;
@@ -77,6 +76,19 @@ public class EndListener extends GameBoundListener {
         
         if (player.getGameMode() != GameMode.CREATIVE) event.setCancelled(true);
     }
+    
+    @EventHandler(priority = EventPriority.LOWEST)
+    public void onInventoryClickAsSpectator(InventoryClickEvent event) {
+        
+        if (!(event.getWhoClicked() instanceof Player)) return;
+
+        Player player = (Player) event.getWhoClicked();
+        if (player.getGameMode() != GameMode.SPECTATOR) return;
+        
+        // In Vanilla, the click actions are completely ignored. However, CraftBukkit 
+        // will continue to call the events, but it will be canceled by default.
+        event.setCancelled(false);
+    }
 
     @EventHandler
     public void onInventoryClick(InventoryClickEvent event) {
@@ -120,7 +132,7 @@ public class EndListener extends GameBoundListener {
                 getGame().getGameJoinManager().runPlayerJoin(player, TeamType.SPECTATOR);
                 
             } else {
-                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_MAX_REACHED));
+                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_MAX_REACHED));
                 event.setCancelled(true);
                 
             }
@@ -135,7 +147,7 @@ public class EndListener extends GameBoundListener {
                 getGame().getGameJoinManager().runPlayerJoin(player, TeamType.SPECTATOR);
                 
             } else {
-                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_MAX_REACHED));
+                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_MAX_REACHED));
                 event.setCancelled(true);
                 
             }

+ 15 - 2
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/GameListener.java

@@ -244,6 +244,19 @@ public class GameListener extends GameBoundListener {
         Inventory clickedInventory = event.getInventory();
         if (clickedInventory.getType() != InventoryType.PLAYER) event.setCancelled(true);
     }
+    
+    @EventHandler(priority = EventPriority.LOWEST)
+    public void onInventoryClickAsSpectator(InventoryClickEvent event) {
+        
+        if (!(event.getWhoClicked() instanceof Player)) return;
+
+        Player player = (Player) event.getWhoClicked();
+        if (player.getGameMode() != GameMode.SPECTATOR) return;
+        
+        // In Vanilla, the click actions are completely ignored. However, CraftBukkit 
+        // will continue to call the events, but it will be canceled by default.
+        event.setCancelled(false);
+    }
 
     @EventHandler
     public void onInventoryClick(InventoryClickEvent event) {
@@ -334,7 +347,7 @@ public class GameListener extends GameBoundListener {
                 
             } else if (isKnownPlayer && rejoinBehavior == RejoinIngameBehavior.LAST_TEAM && lastTeam.getTeamType() == TeamType.PLAYER 
                     && !getGame().areTooManySpectators()) {
-                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_MAX_REACHED));
+                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_TEAM_MAX_REACHED));
                 getGame().getGameJoinManager().runPlayerJoin(player, TeamType.SPECTATOR);
                 
             } else {
@@ -356,7 +369,7 @@ public class GameListener extends GameBoundListener {
                 getGame().getGameJoinManager().runPlayerJoin(player, TeamType.PLAYER);
                 
             } else {
-                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_MAX_REACHED));
+                event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_SPECTATOR_TEAM_MAX_REACHED));
                 event.setCancelled(true);
                 
             }

+ 14 - 1
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/LobbyListener.java

@@ -116,6 +116,19 @@ public class LobbyListener extends GameBoundListener {
         
         if (player.getGameMode() != GameMode.CREATIVE) event.setCancelled(true);
     }
+    
+    @EventHandler(priority = EventPriority.LOWEST)
+    public void onInventoryClickAsSpectator(InventoryClickEvent event) {
+        
+        if (!(event.getWhoClicked() instanceof Player)) return;
+
+        Player player = (Player) event.getWhoClicked();
+        if (player.getGameMode() != GameMode.SPECTATOR) return;
+        
+        // In Vanilla, the click actions are completely ignored. However, CraftBukkit 
+        // will continue to call the events, but it will be canceled by default.
+        event.setCancelled(false);
+    }
 
     @EventHandler
     public void onInventoryClick(InventoryClickEvent event) {
@@ -155,7 +168,7 @@ public class LobbyListener extends GameBoundListener {
             getGame().getGameJoinManager().runPlayerJoin(player, TeamType.PLAYER);
             
         } else if (!getGame().areTooManySpectators()) {
-            event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_MAX_REACHED));
+            event.getPlayer().sendMessage(Messages.getMessage(true, Messages.MessageEnum.TEAM_PLAYER_TEAM_MAX_REACHED));
             getGame().getGameJoinManager().runPlayerJoin(player, TeamType.SPECTATOR);
             
         } else {