de.pewpewproject.lasertag.lasertaggame.gamemode.GameMode   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 479
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 167
dl 0
loc 479
rs 9.76
c 0
b 0
f 0
wmc 33

24 Methods

Rating   Name   Duplication   Size   Complexity  
getTeamScoreText(TeamDto) 0 1 ?
A onPlayerHitPlayer(MinecraftServer,ServerPlayerEntity,ServerPlayerEntity) 0 2 1
A canLasertargetsBeHitMutlipleTimes() 0 2 1
A sendPlayersToSpawnpoints(MinecraftServer) 0 22 3
A getRelevantSettings() 0 21 1
getTeamFinalScore(TeamDto,IServerLasertagManager) 0 1 ?
getPlayerScoreText(UUID) 0 1 ?
A GameMode(String,boolean,boolean,boolean) 0 8 1
A hasInfiniteTime() 0 2 1
A onGameStart(MinecraftServer) 0 8 1
onTick(MinecraftServer) 0 1 ?
B checkStartingConditions(MinecraftServer) 0 62 8
A getTranslatableName() 0 2 1
onPlayerDeath(MinecraftServer,ServerPlayerEntity,DamageSource) 0 1 ?
A onPreGameStart(MinecraftServer) 0 16 2
A checkGameOver(MinecraftServer) 0 2 1
getWinnerTeamId() 0 1 ?
A sendSpectatorsToSpawnpoints(MinecraftServer,List,List) 0 24 3
A areTeamsActive() 0 2 1
getPlayerFinalScore(UUID,IServerLasertagManager) 0 1 ?
A onGameEnd(MinecraftServer) 0 30 2
A createDefaultSettings() 0 2 1
A onPlayerHitLasertarget(MinecraftServer,ServerPlayerEntity,LaserTargetBlockEntity) 0 4 1
A sendTeamToSpawnpoints(MinecraftServer,TeamDto,List) 0 46 4
1
package de.pewpewproject.lasertag.lasertaggame.gamemode;
2
3
import de.pewpewproject.lasertag.LasertagMod;
4
import de.pewpewproject.lasertag.block.entity.LaserTargetBlockEntity;
5
import de.pewpewproject.lasertag.common.types.ScoreHolding;
6
import de.pewpewproject.lasertag.lasertaggame.settings.SettingDescription;
7
import de.pewpewproject.lasertag.lasertaggame.state.management.server.IServerLasertagManager;
8
import de.pewpewproject.lasertag.lasertaggame.state.synced.implementation.SettingsState;
9
import de.pewpewproject.lasertag.lasertaggame.state.synced.implementation.TeamsConfigState;
10
import de.pewpewproject.lasertag.lasertaggame.team.TeamDto;
11
import de.pewpewproject.lasertag.networking.NetworkingConstants;
12
import de.pewpewproject.lasertag.networking.server.ServerEventSending;
13
import net.minecraft.entity.damage.DamageSource;
14
import net.minecraft.server.MinecraftServer;
15
import net.minecraft.server.network.ServerPlayerEntity;
16
import net.minecraft.text.Text;
17
import net.minecraft.util.math.BlockPos;
18
import net.minecraft.world.GameRules;
19
import net.minecraft.world.World;
20
21
import java.util.ArrayList;
22
import java.util.List;
23
import java.util.Optional;
24
import java.util.UUID;
25
26
/**
27
 * Base class for all game modes. Defines the interface for the game modes.
28
 *
29
 * @author Étienne Muser
30
 */
31
public abstract class GameMode {
32
33
    /**
34
     * The translatable name of the game mode. Also acts as an unique identifier for the game mode.
35
     */
36
    private final String translatableName;
37
38
    /**
39
     * Flag whether this game mode has infinite time (Game ends based on other game events)
40
     * or not (Game ends after X minutes)
41
     */
42
    private final boolean infiniteTime;
43
44
    /**
45
     * Flag whether this game mode uses teams or is an 'everyone vs. everyone' game mode
46
     */
47
    private final boolean teamsActive;
48
49
    /**
50
     * Flag to indicate if lasertargets can be hit multiple times by the same player
51
     */
52
    private final boolean lasertargetsCanBeHitMultipleTimes;
53
54
    public GameMode(String translatableName,
55
                    boolean infiniteTime,
56
                    boolean teamsActive,
57
                    boolean lasertargetsCanBeHitMultipleTimes) {
58
        this.translatableName = translatableName;
59
        this.infiniteTime = infiniteTime;
60
        this.teamsActive = teamsActive;
61
        this.lasertargetsCanBeHitMultipleTimes = lasertargetsCanBeHitMultipleTimes;
62
    }
63
64
    //region Interface methods
65
66
    /**
67
     * Creates a settings map containing the default setting values for this game mode.
68
     * The default implementation simply returns the base settings.
69
     * <br>
70
     * Override if the game mode has special default settings.
71
     * <br>
72
     * This method is called when switching the game mode or resetting the settings.
73
     *
74
     * @return The default settings map
75
     */
76
    public SettingsState createDefaultSettings() {
77
        return SettingsState.createBaseSettings();
78
    }
79
80
    /**
81
     * Gets the settings which have an effect in this game mode.
82
     * The default implementation returns the settings that are used by all game modes.
83
     * <br>
84
     * This method gets used in the game managers settings screen.
85
     *
86
     * @return The settings having an effect in this game mode.
87
     */
88
    public List<SettingDescription> getRelevantSettings() {
89
        var list = new ArrayList<SettingDescription>();
90
91
        list.add(SettingDescription.WEAPON_COOLDOWN);
92
        list.add(SettingDescription.WEAPON_REACH);
93
        list.add(SettingDescription.WEAPON_ZOOM);
94
        list.add(SettingDescription.SHOW_LASER_RAYS);
95
        list.add(SettingDescription.MAX_TEAM_SIZE);
96
        list.add(SettingDescription.RENDER_TEAM_LIST);
97
        list.add(SettingDescription.RENDER_TIMER);
98
        list.add(SettingDescription.PREGAME_DURATION);
99
        list.add(SettingDescription.PLAYER_DEACTIVATE_TIME);
100
        list.add(SettingDescription.LASERTARGET_DEACTIVATE_TIME);
101
        list.add(SettingDescription.GEN_STATS_FILE);
102
        list.add(SettingDescription.AUTO_OPEN_STATS_FILE);
103
        list.add(SettingDescription.DO_ORIGIN_SPAWN);
104
        list.add(SettingDescription.RESPAWN_PENALTY);
105
        list.add(SettingDescription.SHOW_NAMETAGS_OF_TEAMMATES);
106
        list.add(SettingDescription.MINING_FATIGUE_ENABLED);
107
108
        return list;
109
    }
110
111
    /**
112
     * Checks if all starting conditions are met. If the game can start, this method returns an empty optional
113
     * Otherwise it returns the reasons why the game can not start as a string.
114
     * <br>
115
     * Override if the game mode has other or additional starting conditions.
116
     * <br>
117
     * This method is called as the first step when starting a lasertag game.
118
     *
119
     * @param server            The server this game runs on
120
     *
121
     * @return Optional containing the abort reasons if there were any. Otherwise Optional.empty()
122
     */
123
    public Optional<String> checkStartingConditions(MinecraftServer server) {
124
        boolean abort = false;
125
        var builder = new StringBuilder();
126
127
        // Get the managers
128
        var gameManager = server.getOverworld().getServerLasertagManager();
129
        var teamsManager = gameManager.getTeamsManager();
130
        var spawnpointManager = gameManager.getSpawnpointManager();
131
        var syncedState = gameManager.getSyncedState();
132
        var teamsConfigState = syncedState.getTeamsConfigState();
133
134
        // For every team
135
        for (var team : teamsConfigState.getTeams()) {
136
137
            // Get the players of the team
138
            var playerUuids = teamsManager.getPlayersOfTeam(team);
139
140
            // Skip spectators
141
            if (team.equals(TeamsConfigState.SPECTATORS)) {
142
143
                continue;
144
            }
145
146
            // Skip empty teams
147
            if (playerUuids.isEmpty()) {
148
149
                continue;
150
            }
151
152
            // Get the spawnpoints for the team
153
            var spawnpoints = spawnpointManager.getSpawnpoints(team);
154
155
            // If the team has no spawnpoints
156
            if (spawnpoints.isEmpty()) {
157
                abort = true;
158
                builder.append("  *No spawnpoints were found for team '");
159
                builder.append(team.name());
160
                builder.append("'\n");
161
            }
162
        }
163
164
        // Get the players without team
165
        var playersWithoutTeam = server.getPlayerManager().getPlayerList().stream()
166
                .filter(playerListEntry -> !teamsManager.isPlayerInTeam(playerListEntry.getUuid()))
167
                .toList();
168
169
        if (!playersWithoutTeam.isEmpty()) {
170
171
            abort = true;
172
            for (var player : playersWithoutTeam) {
173
174
                builder.append("  *Player '");
175
                builder.append(player.getLasertagUsername());
176
                builder.append("' has no team\n");
177
            }
178
        }
179
180
        if (abort) {
181
            return Optional.of(builder.toString());
182
        }
183
184
        return Optional.empty();
185
    }
186
187
    /**
188
     * Sends every player to a spawnpoint that is assigned to him.
189
     * <br>
190
     * Override this method if the game mode has special spawnpoint mechanics.
191
     * <br>
192
     * This method is being called if the starting conditions are met and just before
193
     * executing the <code>onPreGameStart</code> method.
194
     *
195
     * @param server            The server this game runs on
196
     */
197
    public void sendPlayersToSpawnpoints(MinecraftServer server) {
198
199
        // Get the game managers
200
        var gameManager = server.getOverworld().getServerLasertagManager();
201
        var teamsManager = gameManager.getTeamsManager();
202
        var spawnpointManager = gameManager.getSpawnpointManager();
203
        var syncedState = gameManager.getSyncedState();
204
        var teamsConfig = syncedState.getTeamsConfigState();
205
206
        // Teleport players
207
        for (var teamDto : teamsConfig.getTeams()) {
208
            var playerUuids = teamsManager.getPlayersOfTeam(teamDto);
209
210
            // Handle spectators
211
            if (teamDto.equals(TeamsConfigState.SPECTATORS)) {
212
213
                this.sendSpectatorsToSpawnpoints(server, playerUuids, spawnpointManager.getAllSpawnpoints());
214
215
                continue;
216
            }
217
218
            sendTeamToSpawnpoints(server, teamDto, playerUuids);
219
        }
220
    }
221
222
    /**
223
     * Sets the state to be ready for a new lasertag game. Resets scores and states.
224
     * The default implementation sets the game rules keepInventory to true and doImmediateRespawn to true.
225
     * <br>
226
     * Override this method if the game mode needs special preparation.
227
     * <br>
228
     * This method is called just after the players got teleported to their spawnpoints
229
     * and just as the pre game timer starts.
230
     *
231
     * @param server The server this game runs on
232
     */
233
    public void onPreGameStart(MinecraftServer server) {
234
235
        // Get the game managers
236
        var gameManager = server.getOverworld().getServerLasertagManager();
237
        var settingsManager = gameManager.getSettingsManager();
238
239
        // Set gamerules
240
        var gameRules = server.getGameRules();
241
        gameRules.get(GameRules.KEEP_INVENTORY).set(true, server);
242
243
        var doImmediateRespawn = false;
244
        long respawnCooldownSetting = settingsManager.get(SettingDescription.RESPAWN_PENALTY);
245
        if (respawnCooldownSetting == 0L) {
246
            doImmediateRespawn = true;
247
        }
248
        gameRules.get(GameRules.DO_IMMEDIATE_RESPAWN).set(doImmediateRespawn, server);
249
    }
250
251
    /**
252
     * Sets the players to their active state. The default implementation activates every player.
253
     * <br>
254
     * Override this method if the game mode requires special activation methods.
255
     * <br>
256
     * This method is called when the pre game countdown ends.
257
     */
258
    public void onGameStart(MinecraftServer server) {
259
260
        // Get the game managers
261
        var gameManager = server.getOverworld().getServerLasertagManager();
262
        var activationManager = gameManager.getActivationManager();
263
264
        // Activate every player
265
        activationManager.activateAll();
266
    }
267
268
    /**
269
     * Random events can be implemented in this method
270
     * <br>
271
     * This method is being called every minute if the game is running.
272
     *
273
     * @param server The server this game runs on
274
     */
275
    public abstract void onTick(MinecraftServer server);
276
277
    /**
278
     * The game mode's logic for if a player hits a lasertarget. The default implementation triggers the score sound
279
     * event for the shooting player.
280
     *
281
     * @param server  The server this game runs on
282
     * @param shooter The player who fired the laser ray
283
     * @param target  The lasertarget that got hit
284
     */
285
    public void onPlayerHitLasertarget(MinecraftServer server,
286
                                       ServerPlayerEntity shooter,
287
                                       LaserTargetBlockEntity target) {
288
        ServerEventSending.sendPlayerSoundEvent(shooter, NetworkingConstants.PLAY_PLAYER_SCORED_SOUND);
289
    }
290
291
    /**
292
     * The game mode's logic for if a player hits another player. the default implementation triggers the score sound
293
     * event for the shooting player.
294
     *
295
     * @param server  The server this game runs on
296
     * @param shooter The player who fired the laser ray
297
     * @param target  The player who got hit
298
     */
299
    public void onPlayerHitPlayer(MinecraftServer server, ServerPlayerEntity shooter, ServerPlayerEntity target) {
300
        ServerEventSending.sendPlayerSoundEvent(shooter, NetworkingConstants.PLAY_PLAYER_SCORED_SOUND);
301
    }
302
303
    /**
304
     * The game mode's logic for if a player dies. Implement this if the game mode requires custom player death logic.
305
     *
306
     * @param server The server this game runs on
307
     * @param player The player who died
308
     * @param source The damage source which resulted in the player dying
309
     */
310
    public abstract void onPlayerDeath(MinecraftServer server, ServerPlayerEntity player, DamageSource source);
311
312
    /**
313
     * Checks the game over conditions and ends the game if they are met. Only used for game modes that are not
314
     * time-limited. Must be called every time a game over condition changes.
315
     * <br>
316
     * The default implementation just prints an error message as this must be implemented by the concrete game mode
317
     * or should not be used.
318
     *
319
     * @param server The server this game runs on
320
     */
321
    public void checkGameOver(MinecraftServer server) {
322
        LasertagMod.LOGGER.error("checkGameOver not implemented.", new Exception("Not implemented"));
323
    }
324
325
    /**
326
     * Cleans up the game. The default implementation deactivates every player and teleports them back into the lobby.
327
     * <br>
328
     * Override this method if the game mode needs some special game cleanup.
329
     * <br>
330
     * This method is called right when the game ends.
331
     *
332
     * @param server The server this game runs on
333
     */
334
    public void onGameEnd(MinecraftServer server) {
335
336
        // Get the game managers
337
        var gameManager = server.getOverworld().getServerLasertagManager();
338
        var activationManager = gameManager.getActivationManager();
339
        var playerNamesState = gameManager.getSyncedState().getPlayerNamesState();
340
341
        // Deactivate every player
342
        activationManager.deactivateAll();
343
344
        // Teleport players to origin
345
        playerNamesState.forEachPlayer(((playerUuid) -> {
346
            var player = server.getPlayerManager().getPlayer(playerUuid);
347
348
            // Sanity check
349
            if (player == null) {
350
                return;
351
            }
352
353
            player.requestTeleport(0.5F, 1, 0.5F);
354
355
            // Create block pos
356
            var origin = new BlockPos(0, 1, 0);
357
358
            // Set players spawnpoint
359
            player.setSpawnPoint(
360
                    net.minecraft.world.World.OVERWORLD,
361
                    origin, 0.0F, true, false);
362
363
            player.changeGameMode(net.minecraft.world.GameMode.ADVENTURE);
364
        }));
365
    }
366
367
    /**
368
     * Gets the team id of the winning team or -1 if something went wrong.
369
     *
370
     * @return The id of the winning team
371
     */
372
    public abstract int getWinnerTeamId();
373
374
    /**
375
     * Get the team score text for the team list. This will be rendered beside the team name if teams are active
376
     * in the currently selected game mode.
377
     * <br>
378
     * This can for example be a simple text containing the score or a text containing the number of flags a team has
379
     * with an icon.
380
     *
381
     * @param team The team to get the score text of
382
     * @return The text containing the team score
383
     */
384
    public abstract Text getTeamScoreText(TeamDto team);
385
386
    /**
387
     * Get the player score text for the team list. This will be rendered beside the player name.
388
     * <br>
389
     * This can for example be a simple text containing the score of the player of an icon of a flag if the player
390
     * is holding a flag.
391
     *
392
     * @param playerUuid The UUID of the player to get the score text of
393
     * @return The text containing the player score
394
     */
395
    public abstract Text getPlayerScoreText(UUID playerUuid);
396
397
    /**
398
     * Get the final score of a team. Used for statistics calculation.
399
     *
400
     * @param team The team to get the score of
401
     * @return A ScoreHolding object holding the score of the team
402
     */
403
    public abstract ScoreHolding getTeamFinalScore(TeamDto team, IServerLasertagManager gameManager);
404
405
    /**
406
     * Get the final score of a player. Used for statistics calculation.
407
     *
408
     * @param playerUuid The uuid of the player to get the score of
409
     * @return A ScoreHolding object holding the score of the player
410
     */
411
    public abstract ScoreHolding getPlayerFinalScore(UUID playerUuid, IServerLasertagManager gameManager);
412
413
    //endregion
414
415
    //region Public methods
416
417
    public String getTranslatableName() {
418
        return this.translatableName;
419
    }
420
421
    public boolean hasInfiniteTime() {
422
        return this.infiniteTime;
423
    }
424
425
    public boolean areTeamsActive() {
426
        return this.teamsActive;
427
    }
428
429
    public boolean canLasertargetsBeHitMutlipleTimes() {
430
        return this.lasertargetsCanBeHitMultipleTimes;
431
    }
432
433
    //endregion
434
435
    //region Private methods
436
437
    private void sendSpectatorsToSpawnpoints(MinecraftServer server,
438
                                             List<UUID> spectators,
439
                                             List<BlockPos> spawnpoints) {
440
441
        for (var playerUuid : spectators) {
442
443
            // Get the player
444
            var player = server.getPlayerManager().getPlayer(playerUuid);
445
446
            // Sanity check
447
            if (player == null) {
448
                continue;
449
            }
450
451
            // Set player to spectator gamemode
452
            player.changeGameMode(net.minecraft.world.GameMode.SPECTATOR);
453
454
            // Get random index
455
            var idx = server.getOverworld().getRandom().nextInt(spawnpoints.size());
456
457
            var destination = spawnpoints.get(idx);
458
            player.requestTeleport(destination.getX() + 0.5,
459
                    destination.getY() + 1,
460
                    destination.getZ() + 0.5);
461
        }
462
    }
463
464
    private void sendTeamToSpawnpoints(MinecraftServer server,
465
                                       TeamDto teamDto,
466
                                       List<UUID> playerUuids) {
467
468
        // Get the game managers
469
        var gameManager = server.getOverworld().getServerLasertagManager();
470
        var spawnpointManager = gameManager.getSpawnpointManager();
471
        var settingsManager = gameManager.getSettingsManager();
472
473
        for (var playerUuid : playerUuids) {
474
475
            // Get spawnpoints
476
            var spawnpoints = spawnpointManager.getSpawnpoints(teamDto);
477
478
            var player = server.getPlayerManager().getPlayer(playerUuid);
479
480
            // Sanity check
481
            if (player == null) {
482
                continue;
483
            }
484
485
            int idx = server.getOverworld().getRandom().nextInt(spawnpoints.size());
486
487
            var destination = spawnpoints.get(idx);
488
            player.requestTeleport(destination.getX() + 0.5,
489
                    destination.getY() + 1,
490
                    destination.getZ() + 0.5);
491
492
            // Set player to survival by default -> player can break blocks
493
            var newGamemode = net.minecraft.world.GameMode.SURVIVAL;
494
495
            // If setting miningFatigueEnabled is true
496
            if (settingsManager.get(SettingDescription.MINING_FATIGUE_ENABLED)) {
497
                // Set player to adventure game mode -> player can't break blocks
498
                newGamemode = net.minecraft.world.GameMode.ADVENTURE;
499
            }
500
501
            player.changeGameMode(newGamemode);
502
503
            // Get spawn pos
504
            var spawnPos = new BlockPos(destination.getX(), destination.getY() + 1, destination.getZ());
505
506
            // Set players spawnpoint
507
            player.setSpawnPoint(
508
                    World.OVERWORLD,
509
                    spawnPos, 0.0F, true, false);
510
        }
511
    }
512
513
    //endregion
514
}
515