1 
2 /**
3  *
4  * @file jj2level.cpp
5  *
6  * Part of the OpenJazz project
7  *
8  * @par History:
9  * - 23rd August 2005: Created level.c
10  * - 22nd July 2008: Created levelload.c from parts of level.c
11  * - 3rd February 2009: Renamed level.c to level.cpp
12  * - 19th July 2009: Added parts of levelload.cpp to level.cpp
13  * - 29th June 2010: Created jj2level.cpp from parts of level.cpp
14  * - 2nd July 2010: Created jj2event.cpp from parts of jj2level.cpp
15  * - 2nd July 2010: Created jj2eventframe.cpp from parts of jj2level.cpp
16  *
17  * @par Licence:
18  * Copyright (c) 2005-2012 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 the creating, playing and freeing of JJ2 levels.
29  *
30  */
31 
32 
33 #include "jj2event/jj2event.h"
34 #include "jj2level.h"
35 #include "jj2levelplayer/jj2levelplayer.h"
36 
37 #include "game/game.h"
38 #include "game/gamemode.h"
39 #include "io/controls.h"
40 #include "io/file.h"
41 #include "io/gfx/font.h"
42 #include "io/gfx/sprite.h"
43 #include "io/gfx/video.h"
44 #include "io/sound.h"
45 #include "util.h"
46 
47 #include <string.h>
48 
49 
50 /**
51  * Create a JJ2 level.
52  *
53  * @param owner The current game
54  * @param fileName Name of the file containing the level data.
55  * @param checkpoint Whether or not the player(s) will start at a checkpoint
56  * @param multi Whether or not the level will be multi-player
57  */
JJ2Level(Game * owner,char * fileName,bool checkpoint,bool multi)58 JJ2Level::JJ2Level (Game* owner, char* fileName, bool checkpoint, bool multi) :
59 	Level(owner) {
60 
61 	int ret;
62 
63 	// Load level data
64 
65 	ret = load(fileName, checkpoint);
66 
67 	if (ret < 0) throw ret;
68 
69 	multiplayer = multi;
70 
71 	return;
72 
73 }
74 
75 
76 /**
77  * Delete the JJ2 level.
78  */
~JJ2Level()79 JJ2Level::~JJ2Level () {
80 
81 	int count;
82 
83 	if (events) delete events;
84 	delete[] *mods;
85 	delete[] mods;
86 
87 	for (count = 0; count < LAYERS; count++) delete layers[count];
88 
89 	delete[] flippedMask;
90 	delete[] mask;
91 
92 	delete[] musicFile;
93 	delete[] nextLevel;
94 
95 	for (count = 0; count < nAnimSets; count++) {
96 
97 		if (animSets[count]) delete[] animSets[count];
98 
99 	}
100 
101 	delete[] animSets;
102 	delete[] spriteSet;
103 
104 	SDL_FreeSurface(flippedTileSet);
105 	SDL_FreeSurface(tileSet);
106 
107 	delete font;
108 
109 	// Restore panel font palette
110 	panelBigFont->restorePalette();
111 	panelSmallFont->restorePalette();
112 
113 	return;
114 
115 }
116 
117 
118 /**
119  * Determine whether or not the given point is solid when travelling upwards.
120  *
121  * @param x X-coordinate
122  * @param y Y-coordinate
123  *
124  * @return Solidity
125  */
checkMaskUp(fixed x,fixed y)126 bool JJ2Level::checkMaskUp (fixed x, fixed y) {
127 
128 	int tX, tY;
129 
130 	tX = FTOT(x);
131 	tY = FTOT(y);
132 
133 	// Anything off the edge of the map is solid
134 	if ((x < 0) || (y < 0) || (tX >= layer->getWidth()) || (tY >= layer->getHeight()))
135 		return true;
136 
137 	// Event 1 is one-way
138 	// Event 3 is vine
139 	// Event 4 is hook
140 	if ((mods[tY][tX].type == 1) || (mods[tY][tX].type == 3) || (mods[tY][tX].type == 4)) return false;
141 
142 	// Check the mask in the tile in question
143 	return (layer->getFlipped(tX, tY)? flippedMask: mask)[(layer->getTile(tX, tY) << 10) + ((y >> 5) & 992) + ((x >> 10) & 31)];
144 
145 }
146 
147 
148 /**
149  * Determine whether or not the given point is solid when travelling downwards.
150  *
151  * @param x X-coordinate
152  * @param y Y-coordinate
153  * @param drop Whether or not the player is dropping
154  *
155  * @return Solidity
156  */
checkMaskDown(fixed x,fixed y,bool drop)157 bool JJ2Level::checkMaskDown (fixed x, fixed y, bool drop) {
158 
159 	int tX, tY;
160 
161 	tX = FTOT(x);
162 	tY = FTOT(y);
163 
164 	// Anything off the edge of the map is solid
165 	if ((x < 0) || (y < 0) || (tX >= layer->getWidth()) || (tY >= layer->getHeight()))
166 		return true;
167 
168 	// Event 3 is vine
169 	// Event 4 is hook
170 	if (drop && ((mods[tY][tX].type == 3) || (mods[tY][tX].type == 4))) return false;
171 
172 	// Check the mask in the tile in question
173 	return (layer->getFlipped(tX, tY)? flippedMask: mask)[(layer->getTile(tX, tY) << 10) + ((y >> 5) & 992) + ((x >> 10) & 31)];
174 
175 }
176 
177 
178 /**
179  * Set which level will come next.
180  *
181  * @param fileName Next level's file name
182  */
setNext(char * fileName)183 void JJ2Level::setNext (char* fileName) {
184 
185 	unsigned char buffer[MTL_L_PROP];
186 
187 	delete[] nextLevel;
188 	nextLevel = createString(fileName);
189 
190 	if (multiplayer) {
191 
192 		buffer[0] = MTL_L_PROP;
193 		buffer[1] = MT_L_PROP;
194 		buffer[2] = 0; // set next level
195 		buffer[3] = 0;
196 		buffer[4] = 0;
197 
198 		game->send(buffer);
199 
200 	}
201 
202 	return;
203 
204 }
205 
206 
207 /**
208  * Set the frame of the animated tile at the given location.
209  *
210  * @param gridX X-coordinate of the tile
211  * @param gridY Y-coordinate of the tile
212  * @param frame The new frame
213  */
setFrame(int gridX,int gridY,unsigned char frame)214 void JJ2Level::setFrame (int gridX, int gridY, unsigned char frame) {
215 
216 	unsigned char buffer[MTL_L_GRID];
217 
218 	layer->setFrame(gridX, gridY, frame);
219 
220 	if (multiplayer) {
221 
222 		buffer[0] = MTL_L_GRID;
223 		buffer[1] = MT_L_GRID;
224 		buffer[2] = gridX & 0xFF;
225 		buffer[3] = gridY & 0xFF;
226 		buffer[4] = 0; // tile variable
227 		buffer[5] = frame;
228 		buffer[6] = (gridX >> 8) & 0xFF;
229 		buffer[7] = (gridY >> 8) & 0xFF;
230 
231 		game->send(buffer);
232 
233 	}
234 
235 	return;
236 
237 }
238 
239 
240 /**
241  * Get the modifier event for the given tile.
242  *
243  * @param gridX X-coordinate of the tile
244  * @param gridY Y-coordinate of the tile
245  *
246  * @return Modifier event
247  */
getModifier(int gridX,int gridY)248 JJ2Modifier* JJ2Level::getModifier (int gridX, int gridY) {
249 
250 	return mods[gridY] + gridX;
251 
252 }
253 
254 
255 /**
256  * Get a sprite.
257  *
258  * @param sprite Sprite number
259  *
260  * @return Sprite
261  */
getSprite(unsigned char sprite)262 Sprite* JJ2Level::getSprite (unsigned char sprite) {
263 
264 	return spriteSet + sprite;
265 
266 }
267 
268 
269 /**
270  * Get an animation.
271  *
272  * @param set Animation set number
273  * @param anim Animation number
274  * @param flipped Whether or not the animation should be flipped horizontally
275  *
276  * @return Animation
277  */
getAnim(int set,int anim,bool flipped)278 Anim* JJ2Level::getAnim (int set, int anim, bool flipped) {
279 
280 	return (flipped? flippedAnimSets: animSets)[set] + anim;
281 
282 }
283 
284 
285 /**
286  * Get a player animation.
287  *
288  * @param character Character
289  * @param anim Animation number
290  * @param flipped Whether or not the animation should be flipped horizontally
291  *
292  * @return Animation
293  */
getPlayerAnim(int character,int anim,bool flipped)294 Anim* JJ2Level::getPlayerAnim (int character, int anim, bool flipped) {
295 
296 	int set;
297 
298 	if (TSF) {
299 
300 		if (character == 1) set = 61;
301 		else if (character == 2) set = 89;
302 		else set = 55;
303 
304 	} else {
305 
306 		if (character == 1) set = 85;
307 		else set = 54;
308 
309 	}
310 
311 	return getAnim(set, playerAnims[anim], flipped);
312 
313 }
314 
315 
316 /**
317  * Set the water level.
318  *
319  * @param gridY New water level y-coordinate
320  * @param instant Whether or not the change is instant
321  */
setWaterLevel(int gridY,bool instant)322 void JJ2Level::setWaterLevel (int gridY, bool instant) {
323 
324 	unsigned char buffer[MTL_L_PROP];
325 
326 	waterLevelTarget = TTOF(gridY);
327 
328 	if (instant) waterLevel = waterLevelTarget - F8;
329 
330 	if (multiplayer) {
331 
332 		buffer[0] = MTL_L_PROP;
333 		buffer[1] = MT_L_PROP;
334 		buffer[2] = 1; // set water level
335 		buffer[3] = gridY & 0xFF;
336 		buffer[4] = (gridY >> 8) & 0xFF;
337 
338 		game->send(buffer);
339 
340 	}
341 
342 	return;
343 
344 }
345 
346 
347 /**
348  * Determine the water level.
349  *
350  * @return The y-coordinate of the water level
351  */
getWaterLevel()352 fixed JJ2Level::getWaterLevel () {
353 
354 	return waterLevel;
355 
356 }
357 
358 
359 /**
360  * Move a player to a warp target.
361  *
362  * @param player The player to move
363  * @param id The warp target ID
364  */
warp(JJ2LevelPlayer * player,int id)365 void JJ2Level::warp (JJ2LevelPlayer *player, int id) {
366 
367 	int x, y;
368 
369 	for (y = 0; y < layer->getHeight(); y++) {
370 
371 		for (x = 0; x < layer->getWidth(); x++) {
372 
373 			if ((mods[y][x].type == 240) && ((mods[y][x].properties & 255) == id)) {
374 
375 				player->setPosition(TTOF(x), TTOF(y));
376 
377 				return;
378 
379 			}
380 
381 		}
382 
383 	}
384 
385 }
386 
387 
388 /**
389  * Interpret data received from client/server
390  *
391  * @param buffer Received data
392  */
receive(unsigned char * buffer)393 void JJ2Level::receive (unsigned char* buffer) {
394 
395 	switch (buffer[1]) {
396 
397 		case MT_L_PROP:
398 
399 			if (buffer[2] == 1) {
400 
401 				waterLevelTarget = TTOF(buffer[3] + (buffer[4] << 8));
402 
403 			} else if (buffer[2] == 2) {
404 
405 				if (stage == LS_NORMAL)
406 					endTime += buffer[3] * 1000;
407 
408 			}
409 
410 			break;
411 
412 		case MT_L_GRID:
413 
414 			if (buffer[4] == 0) layer->setFrame(buffer[2] + (buffer[6] << 8), buffer[3] + (buffer[7] << 8), buffer[5]);
415 
416 			break;
417 
418 		case MT_L_STAGE:
419 
420 			stage = LevelStage(buffer[2]);
421 
422 			break;
423 
424 	}
425 
426 	return;
427 
428 }
429 
430 
431 /**
432  * Play the level.
433  *
434  * @return Error code
435  */
play()436 int JJ2Level::play () {
437 
438 	JJ2LevelPlayer* jj2LevelPlayer;
439 	bool pmessage, pmenu;
440 	int option;
441 	unsigned int returnTime;
442 	int count, ret;
443 
444 
445 	jj2LevelPlayer = localPlayer->getJJ2LevelPlayer();
446 
447 	tickOffset = globalTicks;
448 	ticks = T_STEP;
449 	steps = 0;
450 
451 	pmessage = pmenu = false;
452 	option = 0;
453 
454 	returnTime = 0;
455 
456 	video.setPalette(palette);
457 
458 	playMusic(musicFile);
459 
460 	while (true) {
461 
462 		ret = loop(pmenu, option, pmessage);
463 
464 		if (ret < 0) return ret;
465 
466 
467 		// Check if level has been won
468 		if (game && returnTime && (ticks > returnTime)) {
469 
470 			ret = game->setLevel(nextLevel);
471 
472 			if (ret < 0) return ret;
473 
474 			return WON;
475 
476 		}
477 
478 
479 		// Process frame-by-frame activity
480 
481 		while (getTimeChange() >= T_STEP) {
482 
483 			// Apply controls to local player
484 			for (count = 0; count < PCONTROLS; count++)
485 				localPlayer->setControl(count, controls.getState(count));
486 
487 			ret = step();
488 			steps++;
489 
490 			if (ret) return ret;
491 
492 		}
493 
494 
495 		// Draw the graphics
496 
497 		draw();
498 
499 
500 		// If paused, draw "PAUSE"
501 		if (pmessage && !pmenu)
502 			font->showString("pause", (canvasW >> 1) - 44, 32);
503 
504 		// If paused, silence music
505 		pauseMusic(pmessage && !pmenu);
506 
507 		if (stage == LS_END) {
508 
509 			// The level is over, so draw gem counts
510 
511 			if (!returnTime) {
512 
513 				returnTime = ticks + 3000;
514 				playSound(S_UPLOOP);
515 
516 			}
517 
518 			// Display statistics
519 
520 			font->showString("red gems", (canvasW >> 1) - 152, (canvasH >> 1) - 60);
521 			font->showNumber(jj2LevelPlayer->getGems(0), (canvasW >> 1) + 124, (canvasH >> 1) - 60);
522 
523 			font->showString("green gems", (canvasW >> 1) - 152, (canvasH >> 1) - 40);
524 			font->showNumber(jj2LevelPlayer->getGems(1), (canvasW >> 1) + 124, (canvasH >> 1) - 40);
525 
526 			font->showString("blue gems", (canvasW >> 1) - 152, (canvasH >> 1) - 20);
527 			font->showNumber(jj2LevelPlayer->getGems(2), (canvasW >> 1) + 124, (canvasH >> 1) - 20);
528 
529 		}
530 
531 
532 		// Draw statistics, menu etc.
533 		drawOverlay(JJ2_BLACK, pmenu, option, 71, 31, -8);
534 
535 	}
536 
537 	return E_NONE;
538 
539 }
540 
541 
542