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