1 
2 /**
3  *
4  * @file level.cpp
5  *
6  * Part of the OpenJazz project
7  *
8  * @par History:
9  * - 23rd August 2005: Created level.c
10  * - 3rd February 2009: Renamed level.c to level.cpp
11  * - 19th July 2009: Created levelframe.cpp from parts of level.cpp
12  * - 19th July 2009: Added parts of levelload.cpp to level.cpp
13  * - 30th March 2010: Created baselevel.cpp from parts of level.cpp and
14  *                  levelframe.cpp
15  * - 1st August 2012: Renamed baselevel.cpp to level.cpp
16  *
17  * @par Licence:
18  * Copyright (c) 2005-2017 Alister Thomson
19  *
20  * OpenJazz is distributed under the terms of
21  * the GNU General Public License, version 2.0
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
26  *
27  * @par Description:
28  * Deals with functionality common to ordinary levels and bonus levels.
29  *
30  */
31 
32 
33 #include "level.h"
34 
35 #include "game/game.h"
36 #include "io/controls.h"
37 #include "io/gfx/font.h"
38 #include "io/gfx/sprite.h"
39 #include "io/gfx/video.h"
40 #include "io/sound.h"
41 #include "player/player.h"
42 #include "jj1scene/jj1scene.h"
43 #include "loop.h"
44 #include "setup.h"
45 
46 
47 /**
48  * Create a new base level
49  */
Level(Game * owner)50 Level::Level (Game* owner) {
51 
52 	game = owner;
53 
54 	menuOptions[0] = "continue game";
55 	menuOptions[2] = "save game";
56 	menuOptions[3] = "load game";
57 	menuOptions[4] = "setup options";
58 	menuOptions[5] = "quit game";
59 
60 	// Arbitrary initial value
61 	smoothfps = 50.0f;
62 
63 	paletteEffects = NULL;
64 
65 	paused = false;
66 
67 	// Set the level stage
68 	stage = LS_NORMAL;
69 
70 	stats = 0;
71 
72 	return;
73 
74 }
75 
76 
77 /**
78  * Destroy base level
79  */
~Level()80 Level::~Level () {
81 
82 	stopMusic();
83 
84 	if (paletteEffects) delete paletteEffects;
85 
86 	return;
87 
88 }
89 
90 
91 /**
92  * Set the players' initial values.
93  *
94  * @param levelType The type of level for which to create a level player
95  * @param anims New level player animations
96  * @param flippedAnims New level player flipped animations
97  * @param checkpoint Whether or not a checkpoint is in use
98  * @param x The level players' new grid x-coordinate
99  * @param y The level players' new grid y-coordinate
100  */
createLevelPlayers(LevelType levelType,Anim ** anims,Anim ** flippedAnims,bool checkpoint,unsigned char x,unsigned char y)101 void Level::createLevelPlayers (LevelType levelType, Anim** anims,
102 	Anim** flippedAnims, bool checkpoint, unsigned char x, unsigned char y) {
103 
104 	int count;
105 
106 	if (!checkpoint) game->setCheckpoint(x, y);
107 
108 	for (count = 0; count < nPlayers; count++) {
109 
110 		players[count].createLevelPlayer(levelType, anims, flippedAnims, x, y);
111 		game->resetPlayer(players + count);
112 
113 	}
114 
115 	return;
116 
117 }
118 
119 
120 /**
121  * Play a cutscene.
122  *
123  * @param file File name of the cutscene to be played
124  *
125  * @return Error code
126  */
playScene(const char * file)127 int Level::playScene (const char* file) {
128 
129 	JJ1Scene* scene;
130 	int ret;
131 
132 	delete paletteEffects;
133 	paletteEffects = NULL;
134 
135 	try {
136 
137 		scene = new JJ1Scene(file);
138 
139 	} catch (int e) {
140 
141 		return e;
142 
143 	}
144 
145 	ret = scene->play();
146 
147 	delete scene;
148 
149 	return ret;
150 
151 }
152 
153 
154 /**
155  * Perform timing calculations.
156  */
timeCalcs()157 void Level::timeCalcs () {
158 
159 	// Calculate smoothed fps
160 	smoothfps = smoothfps + 1.0f -
161 		(smoothfps * ((float)(ticks - prevTicks)) / 1000.0f);
162 	/* This equation is a simplified version of
163 	(fps * c) + (smoothfps * (1 - c))
164 	where c = (1 / fps)
165 	and fps = 1000 / (ticks - prevTicks)
166 	In other words, the response of smoothFPS to changes in FPS decreases as the
167 	framerate increases
168 	The following version is for c = (1 / smoothfps)
169 	*/
170 	// smoothfps = (fps / smoothfps) + smoothfps - 1;
171 
172 	// Ignore outlandish values
173 	if (smoothfps > 9999.0f) smoothfps = 9999.0f;
174 	if (smoothfps < 1.0f) smoothfps = 1.0f;
175 
176 
177 	// Track number of ticks of gameplay since the level started
178 
179 	if (paused) {
180 
181 		tickOffset = globalTicks - ticks;
182 
183 	} else if (globalTicks - tickOffset > ticks + 100) {
184 
185 		prevTicks = ticks;
186 		ticks += 100;
187 
188 		tickOffset = globalTicks - ticks;
189 
190 	} else {
191 
192 		prevTicks = ticks;
193 		ticks = globalTicks - tickOffset;
194 
195 	}
196 
197 	return;
198 
199 }
200 
201 
202 /**
203  * Calculate the amount of time since the last completed step.
204  *
205  * @return Time since last step
206  */
getTimeChange()207 int Level::getTimeChange () {
208 
209 	return paused? 0: ticks - ((steps * (setup.slowMotion? 100: 50)) / 3);
210 
211 }
212 
213 
214 /**
215  * Display menu (if visible) and statistics.
216  *
217  * @param bg Palette index of the box(es)
218  * @param menu Whether or not the level menu should be displayed
219  * @param option Selected menu uption
220  * @param textPalIndex The first palette index for unseleceted text
221  * @param selectedTextPalIndex The first palette index for selected text
222  * @param textPalSpan The number of palette indices for text
223  */
drawOverlay(unsigned char bg,bool menu,int option,unsigned char textPalIndex,unsigned char selectedTextPalIndex,int textPalSpan)224 void Level::drawOverlay (unsigned char bg, bool menu, int option,
225 	unsigned char textPalIndex, unsigned char selectedTextPalIndex,
226 	int textPalSpan) {
227 
228 	const char* difficultyOptions[4] = {"easy", "medium", "hard", "turbo"};
229 	int count, width;
230 
231 	// Draw graphics statistics
232 
233 	if (stats & S_SCREEN) {
234 
235 #ifdef SCALE
236 		if (video.getScaleFactor() > 1)
237 			drawRect(canvasW - 84, 11, 80, 37, bg);
238 		else
239 #endif
240 			drawRect(canvasW - 84, 11, 80, 25, bg);
241 
242 		panelBigFont->showNumber(video.getWidth(), canvasW - 52, 14);
243 		panelBigFont->showString("x", canvasW - 48, 14);
244 		panelBigFont->showNumber(video.getHeight(), canvasW - 12, 14);
245 		panelBigFont->showString("fps", canvasW - 76, 26);
246 		panelBigFont->showNumber((int)smoothfps, canvasW - 12, 26);
247 
248 #ifdef SCALE
249 		if (video.getScaleFactor() > 1) {
250 
251 			panelBigFont->showNumber(canvasW, canvasW - 52, 38);
252 			panelBigFont->showString("x", canvasW - 48, 39);
253 			panelBigFont->showNumber(canvasH, canvasW - 12, 38);
254 
255 		}
256 #endif
257 
258 	}
259 
260 	// Draw player list
261 
262 	if (stats & S_PLAYERS) {
263 
264 		width = 39;
265 
266 		for (count = 0; count < nPlayers; count++)
267 			if (panelBigFont->getStringWidth(players[count].getName()) > width)
268 				width = panelBigFont->getStringWidth(players[count].getName());
269 
270 		drawRect((canvasW >> 1) - 48, 11, width + 57, (nPlayers * 12) + 1, bg);
271 
272 		for (count = 0; count < nPlayers; count++) {
273 
274 			panelBigFont->showNumber(count + 1,
275 				(canvasW >> 1) - 24, 14 + (count * 12));
276 			panelBigFont->showString(players[count].getName(),
277 				(canvasW >> 1) - 16, 14 + (count * 12));
278 			panelBigFont->showNumber(players[count].teamScore,
279 				(canvasW >> 1) + width + 1, 14 + (count * 12));
280 
281 		}
282 
283 	}
284 
285 
286 	if (menu) {
287 
288 		// Draw the menu
289 
290 		drawRect((canvasW >> 2) - 8, (canvasH >> 1) - 54, 144, 108, bg);
291 
292 		menuOptions[1] = difficultyOptions[game->getDifficulty()];
293 
294 		for (count = 0; count < 6; count++) {
295 
296 			if (count == option) fontmn2->mapPalette(240, 8, selectedTextPalIndex, textPalSpan);
297 			else fontmn2->mapPalette(240, 8, textPalIndex, textPalSpan);
298 
299 			fontmn2->showString(menuOptions[count], canvasW >> 2, (canvasH >> 1) + (count << 4) - 46);
300 
301 		}
302 
303 		fontmn2->restorePalette();
304 
305 	}
306 
307 	return;
308 
309 }
310 
311 
312 /**
313  * Process in-game menu selection.
314  *
315  * @param menu Whether or not the level menu should be displayed
316  * @param option Chosen menu option
317  *
318  * @return Error code
319  */
select(bool & menu,int option)320 int Level::select (bool& menu, int option) {
321 
322 	bool wasSlow;
323 
324 	switch (option) {
325 
326 		case 0: // Continue
327 
328 			menu = false;
329 
330 		case 1: // Change difficulty
331 
332 			if (!multiplayer) game->setDifficulty((game->getDifficulty() + 1) & 3);
333 
334 			break;
335 
336 		case 2: // Save
337 
338 			break;
339 
340 		case 3: // Load
341 
342 			break;
343 
344 		case 4: // Setup
345 
346 			if (!multiplayer) {
347 
348 				wasSlow = setup.slowMotion;
349 
350 				if (setupMenu.setupMain() == E_QUIT) return E_QUIT;
351 
352 				if (wasSlow && !setup.slowMotion) steps <<= 1;
353 				else if (!wasSlow && setup.slowMotion) steps >>= 1;
354 
355 				// Restore level palette
356 				video.setPalette(palette);
357 
358 			}
359 
360 			break;
361 
362 		case 5: // Quit game
363 
364 			return E_RETURN;
365 
366 	}
367 
368 	return E_NONE;
369 
370 }
371 
372 /**
373  * Process iteration.
374  *
375  * @param menu Whether or not the level menu should be displayed
376  * @param option Selected menu uption
377  * @param message Whether or not the "paused" message is being displayed
378  *
379  * @return Error code
380  */
loop(bool & menu,int & option,bool & message)381 int Level::loop (bool& menu, int& option, bool& message) {
382 
383 	int ret, x, y;
384 
385 	// Networking
386 	if (multiplayer) {
387 
388 		ret = game->step(ticks);
389 
390 		if (ret < 0) return ret;
391 
392 	}
393 
394 
395 	// Main loop
396 	if (::loop(NORMAL_LOOP, paletteEffects) == E_QUIT) return E_QUIT;
397 
398 
399 	if (controls.release(C_ESCAPE)) {
400 
401 		menu = !menu;
402 		option = 0;
403 
404 	}
405 
406 	if (controls.release(C_PAUSE)) message = !message;
407 
408 	if (controls.release(C_STATS)) {
409 
410 		if (!multiplayer) stats ^= S_SCREEN;
411 		else stats = (stats + 1) & 3;
412 
413 	}
414 
415 	if (menu) {
416 
417 		// Deal with menu controls
418 
419 		if (controls.release(C_UP)) option = (option + 5) % 6;
420 
421 		if (controls.release(C_DOWN)) option = (option + 1) % 6;
422 
423 		if (controls.release(C_ENTER)) {
424 
425 			ret = select(menu, option);
426 
427 			if (ret < 0) return ret;
428 
429 		}
430 
431 		if (controls.getCursor(x, y)) {
432 
433 			x -= canvasW >> 2;
434 			y -= (canvasH >> 1) - 46;
435 
436 			if ((x >= 0) && (x < 128) && (y >= 0) && (y < 96)) {
437 
438 				option = y >> 4;
439 
440 				if (controls.wasCursorReleased()) {
441 
442 					ret = select(menu, option);
443 
444 					if (ret < 0) return ret;
445 
446 				}
447 
448 			} else if (controls.wasCursorReleased()) menu = false;
449 
450 		}
451 
452 	}
453 #if !defined(ANDROID)
454 	else {
455 
456 		if (controls.wasCursorReleased()) menu = true;
457 
458 	}
459 #endif
460 
461 	if (!multiplayer) paused = message || menu;
462 
463 	timeCalcs();
464 
465 	return E_NONE;
466 
467 }
468 
469 
470 /**
471  * Add extra time.
472  *
473  * @param seconds Number of seconds to add
474  */
addTimer(int seconds)475 void Level::addTimer (int seconds) {
476 
477 	unsigned char buffer[MTL_L_PROP];
478 
479 	if (stage != LS_NORMAL) return;
480 
481 	endTime += seconds * 1000;
482 
483 	if (endTime >= ticks + (10 * 60 * 1000))
484 		endTime = ticks + (10 * 60 * 1000) - 1;
485 
486 	if (multiplayer) {
487 
488 		buffer[0] = MTL_L_PROP;
489 		buffer[1] = MT_L_PROP;
490 		buffer[2] = 2; // add timer
491 		buffer[3] = seconds;
492 		buffer[4] = 0; // not used
493 
494 		game->send(buffer);
495 
496 	}
497 
498 	return;
499 
500 }
501 
502 
503 /**
504  * Set the level stage.
505  *
506  * @param newStage New level stage
507  */
setStage(LevelStage newStage)508 void Level::setStage (LevelStage newStage) {
509 
510 	unsigned char buffer[MTL_L_STAGE];
511 
512 	if (stage == newStage) return;
513 
514 	stage = newStage;
515 
516 	if (multiplayer) {
517 
518 		buffer[0] = MTL_L_STAGE;
519 		buffer[1] = MT_L_STAGE;
520 		buffer[2] = stage;
521 		game->send(buffer);
522 
523 	}
524 
525 	return;
526 
527 }
528 
529 
530 /**
531  * Determine the current level stage.
532  *
533  * @return The current level stage.
534  */
getStage()535 LevelStage Level::getStage () {
536 
537 	return stage;
538 
539 }
540 
541