1
|
|
|
package de.pewpewproject.lasertag.lasertaggame.state.management.server.implementation; |
2
|
|
|
|
3
|
|
|
import de.pewpewproject.lasertag.LasertagMod; |
4
|
|
|
import de.pewpewproject.lasertag.common.types.Tuple; |
5
|
|
|
import de.pewpewproject.lasertag.lasertaggame.settings.SettingDescription; |
6
|
|
|
import de.pewpewproject.lasertag.lasertaggame.state.management.server.IMusicalChairsManager; |
7
|
|
|
import de.pewpewproject.lasertag.lasertaggame.state.management.server.synced.*; |
8
|
|
|
import de.pewpewproject.lasertag.lasertaggame.state.server.IMusicalChairsState; |
9
|
|
|
import de.pewpewproject.lasertag.lasertaggame.state.server.implementation.MusicalChairsState; |
10
|
|
|
import de.pewpewproject.lasertag.lasertaggame.state.synced.ITeamsConfigState; |
11
|
|
|
import de.pewpewproject.lasertag.lasertaggame.state.synced.implementation.TeamsConfigState; |
12
|
|
|
import de.pewpewproject.lasertag.lasertaggame.team.TeamDto; |
13
|
|
|
import net.minecraft.server.world.ServerWorld; |
14
|
|
|
|
15
|
|
|
import java.util.ArrayDeque; |
16
|
|
|
import java.util.Queue; |
17
|
|
|
import java.util.UUID; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Implementation of IMusicalChairsManager for the server lasertag game |
21
|
|
|
* |
22
|
|
|
* @author Étienne Muser |
23
|
|
|
*/ |
24
|
|
|
public class MusicalChairsManager implements IMusicalChairsManager { |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* The number of lasertag ticks since the last phase change. |
28
|
|
|
* Initialize with -1 as there will be a tick at 0 seconds |
29
|
|
|
* into the game. |
30
|
|
|
*/ |
31
|
|
|
private long ticksSinceLastPhaseChange = -1L; |
32
|
|
|
|
33
|
|
|
private final ISettingsManager settingsManager; |
34
|
|
|
|
35
|
|
|
private final ITeamsManager teamsManager; |
36
|
|
|
|
37
|
|
|
private final IScoreManager scoreManager; |
38
|
|
|
|
39
|
|
|
private final IGameModeManager gameModeManager; |
40
|
|
|
|
41
|
|
|
private final ITeamsConfigState teamsConfigState; |
42
|
|
|
|
43
|
|
|
private final IEliminationManager eliminationManager; |
44
|
|
|
|
45
|
|
|
private final ILasertargetsManager lasertargetsManager; |
46
|
|
|
|
47
|
|
|
private final ServerWorld world; |
48
|
|
|
|
49
|
|
|
private final IMusicalChairsState musicalChairsState; |
50
|
|
|
|
51
|
|
|
public MusicalChairsManager(ISettingsManager settingsManager, |
52
|
|
|
ITeamsManager teamsManager, |
53
|
|
|
IScoreManager scoreManager, |
54
|
|
|
IGameModeManager gameModeManager, |
55
|
|
|
ITeamsConfigState teamsConfigState, |
56
|
|
|
IEliminationManager eliminationManager, |
57
|
|
|
ILasertargetsManager lasertargetsManager, |
58
|
|
|
ServerWorld world |
59
|
|
|
) { |
60
|
|
|
this.settingsManager = settingsManager; |
61
|
|
|
this.teamsManager = teamsManager; |
62
|
|
|
this.scoreManager = scoreManager; |
63
|
|
|
this.gameModeManager = gameModeManager; |
64
|
|
|
this.teamsConfigState = teamsConfigState; |
65
|
|
|
this.eliminationManager = eliminationManager; |
66
|
|
|
this.lasertargetsManager = lasertargetsManager; |
67
|
|
|
this.world = world; |
68
|
|
|
this.musicalChairsState = new MusicalChairsState(); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
|
72
|
|
|
@Override |
73
|
|
|
public synchronized void tick() { |
74
|
|
|
|
75
|
|
|
LasertagMod.LOGGER.info("[MusicalChairsManager] Tick..."); |
76
|
|
|
|
77
|
|
|
// Increment time since last phase change |
78
|
|
|
++ticksSinceLastPhaseChange; |
79
|
|
|
|
80
|
|
|
// If the phase is now over |
81
|
|
|
if (ticksSinceLastPhaseChange >= settingsManager.<Long>get(SettingDescription.PHASE_DURATION)) { |
82
|
|
|
|
83
|
|
|
LasertagMod.LOGGER.info("[MusicalChairsManager] Phase end."); |
84
|
|
|
|
85
|
|
|
// Reset the time since last phase change |
86
|
|
|
ticksSinceLastPhaseChange = 0L; |
87
|
|
|
|
88
|
|
|
// Handle phase change |
89
|
|
|
handlePhaseChange(); |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
@Override |
94
|
|
|
public synchronized long getPlayerTotalScore(UUID playerUuid) { |
95
|
|
|
return musicalChairsState.getPlayerOverallScore(playerUuid); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
@Override |
99
|
|
|
public synchronized void onPlayerScored(UUID playerUuid, long score) { |
100
|
|
|
|
101
|
|
|
// Get the old score |
102
|
|
|
var oldScore = musicalChairsState.getPlayerOverallScore(playerUuid); |
103
|
|
|
|
104
|
|
|
// Calculate the new score |
105
|
|
|
var newScore = oldScore + score; |
106
|
|
|
|
107
|
|
|
// Set the new score |
108
|
|
|
musicalChairsState.setPlayerOverallScore(playerUuid, newScore); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
@Override |
112
|
|
|
public synchronized void reset() { |
113
|
|
|
ticksSinceLastPhaseChange = -1L; |
114
|
|
|
musicalChairsState.reset(); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
private void handlePhaseChange() { |
118
|
|
|
|
119
|
|
|
// Reset the already hit by state of the lasertargets lasertargets |
120
|
|
|
lasertargetsManager.resetAlreadyHitBy(); |
121
|
|
|
|
122
|
|
|
// If the scores should be reset at the end of the phase |
123
|
|
|
if (settingsManager.<Boolean>get(SettingDescription.RESET_SCORES_AT_PHASE_END)) { |
124
|
|
|
|
125
|
|
|
scoreManager.resetScores(); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// Get the non-eliminated teams |
129
|
|
|
var nonEliminatedTeams = teamsConfigState.getTeams().stream() |
130
|
|
|
// Filter out empty teams |
131
|
|
|
.filter(team -> !teamsManager.getPlayersOfTeam(team).isEmpty()) |
132
|
|
|
// Filter out eliminated teams |
133
|
|
|
.filter(eliminationManager::isTeamNotEliminated) |
134
|
|
|
// Filter out the spectators |
135
|
|
|
.filter(team -> !team.equals(TeamsConfigState.SPECTATORS)) |
136
|
|
|
.toList(); |
137
|
|
|
|
138
|
|
|
var sb = new StringBuilder("[MusicalChairsManager] Remaining teams: ["); |
139
|
|
|
nonEliminatedTeams.forEach(t -> sb.append(t.name()).append(", ")); |
140
|
|
|
sb.append("]"); |
141
|
|
|
LasertagMod.LOGGER.info(sb.toString()); |
142
|
|
|
|
143
|
|
|
// Get the teams with the fewest amount of points |
144
|
|
|
var toBeEliminatedTeams = nonEliminatedTeams.stream() |
145
|
|
|
// Map to (Score, Team) tuple |
146
|
|
|
.map(team -> new Tuple<>(teamsManager.getPlayersOfTeam(team).stream() |
147
|
|
|
.map(scoreManager::getScore).mapToLong(a -> a) |
148
|
|
|
.sum(), team)) |
149
|
|
|
// Collect the teams with the least amount of points |
150
|
|
|
.collect(ArrayDeque::new, |
151
|
|
|
(Queue<Tuple<Long, TeamDto>> queue, Tuple<Long, TeamDto> tuple) -> { |
152
|
|
|
if (!queue.isEmpty() && queue.peek().x().compareTo(tuple.x()) > 0) { |
153
|
|
|
queue.clear(); |
154
|
|
|
} |
155
|
|
|
if (queue.isEmpty() || queue.peek().x().equals(tuple.x())) { |
156
|
|
|
queue.offer(tuple); |
157
|
|
|
} |
158
|
|
|
}, |
159
|
|
|
(left, right) -> { |
160
|
|
|
if (left.peek().x().compareTo(right.peek().x()) > 0) { |
161
|
|
|
left.clear(); |
162
|
|
|
} |
163
|
|
|
if (left.isEmpty() || left.peek().x().equals(right.peek().x())) |
164
|
|
|
{ |
165
|
|
|
left.addAll(right); |
166
|
|
|
} |
167
|
|
|
}) |
168
|
|
|
// Map back to stream of teams |
169
|
|
|
.stream() |
170
|
|
|
.map(Tuple::y) |
171
|
|
|
.toList(); |
172
|
|
|
|
173
|
|
|
// If all teams are about to be eliminated |
174
|
|
|
if (nonEliminatedTeams.size() == toBeEliminatedTeams.size()) { |
175
|
|
|
|
176
|
|
|
LasertagMod.LOGGER.info("[MusicalChairsManager] Tie between all teams present. No team gets eliminated"); |
177
|
|
|
LasertagMod.LOGGER.info("Number of remaining teams: " + nonEliminatedTeams.size()); |
178
|
|
|
LasertagMod.LOGGER.info("Number of to be eliminated teams: " + toBeEliminatedTeams.size()); |
179
|
|
|
|
180
|
|
|
// Eliminate no team - tiebreaker |
181
|
|
|
return; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
// Eliminate all the teams that need to be eliminated |
185
|
|
|
toBeEliminatedTeams.forEach(team -> { |
186
|
|
|
|
187
|
|
|
LasertagMod.LOGGER.info("[MusicalChairsManager] Eliminating team '" + team.name() + "'."); |
188
|
|
|
eliminationManager.eliminateTeam(team); |
189
|
|
|
}); |
190
|
|
|
|
191
|
|
|
// Check if game ended |
192
|
|
|
gameModeManager.getGameMode().checkGameOver(world.getServer()); |
193
|
|
|
} |
194
|
|
|
} |