This repository has been archived by the owner on Jul 29, 2022. It is now read-only.
/
LevelScene.java
380 lines (329 loc) · 10 KB
/
LevelScene.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
/**
*
*/
package rekit.logic.scene;
import java.awt.Font;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import rekit.config.GameConf;
import rekit.core.CameraTarget;
import rekit.core.ShutdownManager;
import rekit.logic.GameModel;
import rekit.logic.ILevelScene;
import rekit.logic.filters.GrayScaleMode;
import rekit.logic.gameelements.GameElement;
import rekit.logic.gameelements.GameElementFactory;
import rekit.logic.gameelements.entities.Player;
import rekit.logic.gui.LifeGui;
import rekit.logic.gui.ScoreGui;
import rekit.logic.gui.Text;
import rekit.logic.gui.TimeDecorator;
import rekit.logic.gui.menu.MenuActionItem;
import rekit.logic.gui.menu.MenuItem;
import rekit.logic.gui.menu.MenuList;
import rekit.logic.gui.menu.SubMenu;
import rekit.logic.gui.parallax.HeapElementCloud;
import rekit.logic.gui.parallax.HeapElementMountain;
import rekit.logic.gui.parallax.HeapLayer;
import rekit.logic.gui.parallax.ParallaxContainer;
import rekit.logic.gui.parallax.TriangulationLayer;
import rekit.logic.level.Level;
import rekit.persistence.level.LevelType;
import rekit.primitives.TextOptions;
import rekit.primitives.geometry.Vec;
import rekit.primitives.time.Timer;
import rekit.util.CalcUtil;
/**
* Scene that holds a playable Level created by a LevelCreator. Different Levels
* are possible by changing the LevelCreator in the constructor.
*
* @author Matthias Schmitt
*
*/
public abstract class LevelScene extends Scene implements ILevelScene {
/**
* Menu than will be displayed when the game is paused.
*/
protected SubMenu pauseMenu;
/**
* Menu that will be displayed when the game has ended. Shows options
* dependent on whether the player won or lost and whether its an arcade
* level or not.
*/
protected SubMenu endMenu;
/**
* The player in the scene.
*/
private final Player player;
/**
* The level in the scene.
*/
private final Level level;
/**
* The current camera target.
*/
private CameraTarget cameraTarget;
/**
* The score gui element.
*/
private ScoreGui scoreGui;
/**
* The life gui element.
*/
private LifeGui lifeGui;
/**
* The ParallaxContainer for the background.
*/
protected ParallaxContainer parallax;
/**
* Indicates whether the level has ended.
*/
private boolean ended;
/**
* Return value of {@link #isOffsetWildCard()}.
*/
private boolean offsetWildCard;
/**
* The handler for {@link #attack(boolean)} of the user.
*/
private Consumer<Boolean> attackHandler = LevelScene.DEFAULT_ATTACK;
private static final Consumer<Boolean> DEFAULT_ATTACK = (a) -> System.out.println("Great Attack! (" + a + ")");
/**
* Create a new LevelScene.
*
* @param model
* the model
* @param level
* the level
*/
public LevelScene(GameModel model, Level level) {
super(model);
this.level = level;
this.player = this.level.getLp().getPlayer();
this.ended = true;
}
@Override
public void init() {
super.init();
this.level.reset();
this.offsetWildCard = false;
// Create Player and add him to game
this.cameraTarget = this.player;
this.addGameElement(this.player);
// Init EnemyFactory with model
GameElementFactory.setScene(this);
this.level.reset();
this.createBackground();
this.createGui();
TextOptions op = new TextOptions(new Vec(-0.5f, -0.5f), 40, GameConf.GAME_TEXT_COLOR, GameConf.GAME_TEXT_FONT, Font.BOLD);
Text levelText = new Text(this, op).setText(this.level.getName());
levelText.setPos(CalcUtil.units2pixel(new Vec(GameConf.GRID_W / 2f, GameConf.GRID_H / 2f)));
this.addGuiElement(new TimeDecorator(this, levelText, new Timer(5000)));
this.createPauseMenu();
}
private void createPauseMenu() {
this.pauseMenu = new MenuList(this, "Pause Menu");
this.pauseMenu.setPos(new Vec(GameConf.PIXEL_W / 2f, GameConf.PIXEL_H / 2f));
MenuActionItem resume = new MenuActionItem(this, "Resume", () -> this.togglePause());
MenuActionItem restart = new MenuActionItem(this, "Restart", () -> this.restart());
MenuActionItem back = new MenuActionItem(this, "Main Menu", () -> this.getModel().switchScene(Scenes.MAIN_MENU, "0.0"));
MenuActionItem desktop = new MenuActionItem(this, "Exit Game", () -> ShutdownManager.shutdown());
this.pauseMenu.addItem(resume, restart, back, desktop);
this.pauseMenu.setVisible(false);
this.pauseMenu.select();
this.addGuiElement(this.pauseMenu);
}
private void createGui() {
// Create Gui
this.scoreGui = new ScoreGui(this);
this.scoreGui.setPos(new Vec(10, 10));
this.lifeGui = new LifeGui(this);
this.lifeGui.setPos(new Vec(10));
this.addGuiElement(this.scoreGui);
this.addGuiElement(this.lifeGui);
}
private void createBackground() {
// Create parallax background
this.parallax = new ParallaxContainer(this);
this.parallax.addLayer(new TriangulationLayer(1.5f));
this.parallax.addLayer(new HeapLayer(new HeapElementCloud(null, new Vec(), null, null), 1.1f));
this.parallax.addLayer(new HeapLayer(new HeapElementMountain(null, new Vec(), null, null), 1.3f));
}
@Override
public void start() {
this.ended = false;
this.getModel().removeFilter();
}
@Override
public final void end(boolean won) {
if (this.ended) {
return;
}
this.ended = true;
this.level.end(won);
// create the end menu before actually populating and showing it
this.endMenu = new MenuList(this, "End Menu");
this.endMenu.setPos(new Vec(GameConf.PIXEL_W / 2f, GameConf.PIXEL_H / 2f));
this.endMenu.setVisible(false);
this.addGuiElement(this.endMenu);
int delay = this.performEndTasks(won);
// show end menu after the specified time
if (delay >= 0) {
Timer.execute(delay, () -> this.showEndMenu(won));
}
}
/**
* Populate the end menu and show it.
*
* @param won
* Indicates whether the game was won. This has an effect on the
* created items of the menu
*/
private void showEndMenu(boolean won) {
// TODO definitely not the proper place to do this
// do this in an FinitLevelScene and InifinitLevelScene (not to current
// one a new one)
MenuActionItem endBack;
MenuActionItem endExit = new MenuActionItem(this, "Exit Game", () -> ShutdownManager.shutdown());
if (won || this.level.isInfinite()) {
String nextLevel = this.level.getLp().getNextLevel();
if (nextLevel != null) {
MenuActionItem endNext = new MenuActionItem(this, "Next Level", () -> this.getModel().switchScene(Scenes.ARCADE, nextLevel));
this.endMenu.addItem(endNext);
}
}
MenuActionItem endRestart = new MenuActionItem(this, "Restart", () -> this.restart());
this.endMenu.addItem(endRestart);
if (this.level.getDefinition().getType() == LevelType.Arcade) {
endBack = new MenuActionItem(this, "Level selection", () -> this.getModel().switchScene(Scenes.MAIN_MENU, "0.0.0"));
} else {
endBack = new MenuActionItem(this, "Main Menu", () -> this.getModel().switchScene(Scenes.MAIN_MENU));
}
this.endMenu.addItem(endBack, endExit);
this.endMenu.select();
this.endMenu.setVisible(true);
}
@Override
public boolean hasEnded() {
return this.ended;
}
/**
* Perform tasks on the end of the game (level).
*
* @param won
* indicates whether successful or died
*
* @return delay delay (in ms) when to show the end menu. on -1 the endMenu
* will not be shown.
*
*/
protected int performEndTasks(boolean won) {
TextOptions op = new TextOptions(new Vec(-0.5f, -0.5f), 50, GameConf.GAME_TEXT_COLOR, GameConf.GAME_TEXT_FONT, Font.BOLD, false);
Text levelText = new Text(this, op).setText(won ? "You won the game!" : "You lost the game!");
levelText.setPos(CalcUtil.units2pixel(new Vec(GameConf.GRID_W / 2f, GameConf.GRID_H / 2f)));
this.addGuiElement(new TimeDecorator(this, levelText, new Timer(2000)));
if (won) {
this.getModel().removeFilter();
} else {
this.getModel().setFilter(new GrayScaleMode());
}
// show menu short after the winning text has faded out
return 2500;
}
@Override
public boolean togglePause() {
if (!super.togglePause()) {
return false;
}
// toggle visibility of pause menu
this.pauseMenu.setVisible(!this.pauseMenu.isVisible());
this.pauseMenu.setIndex(0);
return true;
}
@Override
public void restart() {
// reset all data structures
this.init();
// restart logic thread
this.start();
}
@Override
protected void logicLoopPre() {
this.level.getSp().generate((int) (this.getCameraOffset() + GameConf.GRID_W + 1));
// dont allow player to go behind currentOffset
float minX = this.getCameraOffset() + this.player.getSize().x / 2f;
if (this.player.getPos().x < minX) {
this.player.setPos(this.player.getPos().setX(minX));
}
this.parallax.logicLoop(this.getCameraOffset());
}
@Override
protected void logicLoopAfter() {
if (this.isPaused()) {
return;
}
this.checkCollisions();
if (this.player.getDeleteMe()) {
this.end(false);
}
}
/**
* Check and Threat collisions.
*/
private void checkCollisions() {
Set<GameElement> elements = new HashSet<>();
this.applyToNonNeutralGameElements(elements::add);
for (GameElement e1 : elements) {
for (GameElement e2 : elements) {
e1.checkCollision(e2);
}
}
}
@Override
public Player getPlayer() {
return this.player;
}
@Override
public float getCameraOffset() {
return this.cameraTarget.getCameraOffset();
}
@Override
public void setCameraTarget(CameraTarget cameraTarget) {
this.cameraTarget = cameraTarget;
}
@Override
public final MenuItem getMenu() {
if (this.isPaused()) {
return this.pauseMenu;
}
if (this.hasEnded()) {
return this.endMenu;
}
return null;
}
@Override
public final boolean isLevelScene() {
return true;
}
@Override
public final Level getLevel() {
return this.level;
}
@Override
public final boolean isOffsetWildCard() {
return this.offsetWildCard;
}
@Override
public final void setOffsetWildCard(boolean wildcard) {
this.offsetWildCard = wildcard;
}
@Override
public final void attack(boolean active) {
this.attackHandler.accept(active);
}
@Override
public final void setAttackHandler(Consumer<Boolean> handler) {
this.attackHandler = handler == null ? LevelScene.DEFAULT_ATTACK : handler;
}
}