1 /*
2 * GNUjump
3 * =======
4 *
5 * Copyright (C) 2005-2008, Juan Pedro Bolivar Puente
6 *
7 * GNUjump is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * GNUjump is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "gnujump.h"
22 #include "replay.h"
23 #include "game.h"
24 #include "tools.h"
25 #include "effects.h"
26
27 extern L_gblOptions gblOps;
28
29 const int REPFPS[NREPSPEEDS] = { 5, 10, 20, 40, 80, 160, 320, 640 };
30
31 void
repPushUInt32(replay_t * rep,Uint32 data)32 repPushUInt32 (replay_t * rep, Uint32 data)
33 {
34 *(Uint32 *) (rep->buf) = data;
35 rep->buf += sizeof (Uint32);
36 }
37
38 void
repPushUInt16(replay_t * rep,Uint16 data)39 repPushUInt16 (replay_t * rep, Uint16 data)
40 {
41 *(Uint16 *) (rep->buf) = data;
42 rep->buf += sizeof (Uint16);
43 }
44
45 void
repPushUInt8(replay_t * rep,Uint8 data)46 repPushUInt8 (replay_t * rep, Uint8 data)
47 {
48 *(Uint8 *) (rep->buf) = data;
49 rep->buf += sizeof (Uint8);
50 }
51
52 Uint32
repGetUInt32(replay_t * rep)53 repGetUInt32 (replay_t * rep)
54 {
55 rep->buf += sizeof (Uint32);
56 return *(Uint32 *) (rep->buf - sizeof (Uint32));
57 }
58
59 Uint16
repGetUInt16(replay_t * rep)60 repGetUInt16 (replay_t * rep)
61 {
62 rep->buf += sizeof (Uint16);
63 return *(Uint16 *) (rep->buf - sizeof (Uint16));
64 }
65
66 Uint8
repGetUInt8(replay_t * rep)67 repGetUInt8 (replay_t * rep)
68 {
69 rep->buf += sizeof (Uint8);
70 return *(Uint8 *) (rep->buf - sizeof (Uint8));
71 }
72
73 void
endReplay(game_t * game,int totalms)74 endReplay (game_t * game, int totalms)
75 {
76 int i;
77
78 updateReplay (game, 0);
79 game->replay.bodysize = game->replay.buf - game->replay.bst;
80 game->replay.totalms = totalms;
81 game->replay.record = 0;
82
83 for (i = 0; i < game->numHeros; i++)
84 {
85 if (game->heros[i].floor > game->replay.record)
86 game->replay.record = game->heros[i].floor;
87 }
88
89 if (game->numHeros > 1)
90 game->heros[i].floor /= gblOps.mpLives;
91 }
92
93 void
initReplay(game_t * game)94 initReplay (game_t * game)
95 {
96 int i;
97
98 game->replay.timer = 0;
99 game->replay.scrolls = 0;
100 game->replay.sounds = 0;
101 game->replay.bufsize = REP_BUFFER_SIZE;
102 game->replay.bst = malloc (game->replay.bufsize);
103 game->replay.buf = game->replay.bst;
104 game->replay.nframes = 0;
105 game->replay.fps = gblOps.repFps;
106 game->replay.mspf = 1000.0 / gblOps.repFps;
107
108 for (i = 0; i < game->numHeros; i++)
109 game->replay.deadHero[i] = 0;
110
111 for (i = 0; i < GRIDHEIGHT; i++)
112 {
113 repPushUInt8 (&(game->replay), game->floor_l[i]);
114 repPushUInt8 (&(game->replay), game->floor_r[i]);
115 }
116 repPushUInt8 (&(game->replay), game->numHeros);
117 repPushHeros (game);
118 }
119
120 void
updateReplay(game_t * game,float ms)121 updateReplay (game_t * game, float ms)
122 {
123 int d = game->replay.buf - game->replay.bst;
124
125 if (game->replay.bufsize - d < REP_MIN_FREE)
126 {
127 game->replay.bufsize += REP_BUFFER_SIZE;
128 game->replay.bst = realloc (game->replay.bst, game->replay.bufsize);
129 game->replay.buf = game->replay.bst + d;
130 }
131
132 game->replay.timer += ms;
133
134 if (game->replay.timer >= game->replay.mspf)
135 {
136 game->replay.timer -= game->replay.mspf;
137 game->replay.nframes++;
138 repPushUInt8 (&(game->replay), game->replay.sounds);
139 game->replay.sounds = 0;
140 repPushGrid (game);
141 repPushHeros (game);
142 }
143 }
144
145 void
repPushGrid(game_t * game)146 repPushGrid (game_t * game)
147 {
148 int i, j;
149
150 repPushUInt8 (&(game->replay), game->scrollCount);
151 repPushUInt8 (&(game->replay), game->replay.scrolls);
152 for (i = 0, j = game->mapIndex;
153 i < game->replay.scrolls; i++, j = (j + 1) % GRIDHEIGHT)
154 {
155 repPushUInt8 (&(game->replay), game->floor_l[j]);
156 repPushUInt8 (&(game->replay), game->floor_r[j]);
157 }
158
159 game->replay.scrolls = 0;
160 }
161
162 void
repPushHeros(game_t * game)163 repPushHeros (game_t * game)
164 {
165 int i;
166 for (i = 0; i < game->numHeros; i++)
167 {
168 if (!game->replay.deadHero[i])
169 {
170 repPushUInt16 (&(game->replay), game->heros[i].x);
171 repPushUInt16 (&(game->replay), game->heros[i].y);
172 repPushUInt8 (&(game->replay), game->heros[i].dir);
173 repPushUInt16 (&(game->replay), game->heros[i].angle);
174 repPushUInt8 (&(game->replay), game->heros[i].id);
175 repPushUInt8 (&(game->replay), game->heros[i].dead);
176 repPushUInt8 (&(game->replay), game->heros[i].lives);
177 repPushUInt16 (&(game->replay), game->heros[i].floor);
178
179 game->replay.deadHero[i] = game->heros[i].dead;
180 }
181 }
182 }
183
184 void
freeReplay(replay_t * rep)185 freeReplay (replay_t * rep)
186 {
187 free (rep->bst);
188 }
189
190 void
getPlayerReplay(hero_t * hero,replay_t * rep)191 getPlayerReplay (hero_t * hero, replay_t * rep)
192 {
193 int newid;
194
195 hero->x = repGetUInt16 (rep);
196 hero->y = repGetUInt16 (rep);
197 hero->dir = repGetUInt8 (rep);
198 hero->angle = repGetUInt16 (rep);
199 newid = repGetUInt8 (rep);
200 hero->previd = hero->id;
201 if (newid != hero->id)
202 {
203 hero->sprite[newid].elpTime = hero->sprite[newid].frame = 0;
204 }
205 hero->id = newid;
206 hero->dead = repGetUInt8 (rep);
207 hero->prevLives = hero->lives;
208 hero->lives = repGetUInt8 (rep);
209 if (hero->lives > 4)
210 hero->lives = -1;
211 hero->prevFloor = hero->floor;
212 hero->floor = repGetUInt16 (rep);
213
214 if (gblOps.trailMode != NOTRAIL)
215 updateTrails (hero, RECMS);
216 if (gblOps.useGL && gblOps.blur)
217 updateBlurs (hero, RECMS);
218 }
219
220 void
initGameReplay(game_t * game,data_t * gfx,replay_t * rep)221 initGameReplay (game_t * game, data_t * gfx, replay_t * rep)
222 {
223 int i, j;
224
225 rep->buf = rep->bst;
226 rep->speed = REP_1X;
227
228 game->floorTop = GRIDHEIGHT - 4;
229 game->mapIndex = 0;
230 game->scrollCount = 0;
231 game->scrollTotal = 0;
232
233 for (i = 0; i < GRIDHEIGHT; i++)
234 {
235 game->floor_l[i] = repGetUInt8 (rep);
236 game->floor_r[i] = repGetUInt8 (rep);
237 }
238 game->numHeros = repGetUInt8 (rep);
239 game->heros = malloc (sizeof (hero_t) * game->numHeros);
240 for (i = 0; i < game->numHeros; i++)
241 {
242 for (j = 0; j < HEROANIMS; j++)
243 {
244 initializeSpriteCtlRot (&game->heros[i].sprite[j],
245 gfx->heroSprite[i][j]);
246 }
247 game->heros[i].trail = NULL;
248 game->heros[i].blur = NULL;
249 getPlayerReplay (&(game->heros[i]), rep);
250 game->heros[i].prevLives = -1;
251 }
252 }
253
254 void
scrollReplay(game_t * game,data_t * gfx,replay_t * rep)255 scrollReplay (game_t * game, data_t * gfx, replay_t * rep)
256 {
257 int scrolls;
258 int i, j;
259 int ds;
260
261 ds = game->scrollCount;
262 game->scrollCount = repGetUInt8 (rep);
263 ds = game->scrollCount - ds;
264
265 scrolls = repGetUInt8 (rep);
266 if (scrolls > 0)
267 ds += BLOCKSIZE * (scrolls);
268
269 game->mapIndex -= scrolls;
270 game->floorTop += scrolls;
271
272 game->scrollTotal += ds;
273
274 if (game->mapIndex < 0)
275 game->mapIndex += GRIDHEIGHT;
276 for (i = game->mapIndex, j = 0; j < scrolls; j++, i = (i + 1) % GRIDHEIGHT)
277 {
278 game->floor_l[i] = repGetUInt8 (rep);
279 game->floor_r[i] = repGetUInt8 (rep);
280 }
281
282 for (i = 0; i < game->numHeros; i++)
283 {
284 scrollTrails (&(game->heros[i]), ds);
285 scrollBlurs (&(game->heros[i]), ds);
286 }
287 }
288
289 void
playRepSounds(data_t * gfx,replay_t * rep,int mute)290 playRepSounds (data_t * gfx, replay_t * rep, int mute)
291 {
292 int sounds = repGetUInt8 (rep);
293 if (!mute)
294 {
295 if ((sounds & S_JUMP) == S_JUMP && gfx->gjump)
296 Mix_PlayChannel (-1, gfx->gjump, 0);
297 if ((sounds & S_FALL) == S_FALL && gfx->gfall)
298 Mix_PlayChannel (-1, gfx->gfall, 0);
299 if ((sounds & S_DIE) == S_DIE && gfx->gdie)
300 Mix_PlayChannel (-1, gfx->gdie, 0);
301 }
302 }
303
304 void
drawRepHud(data_t * gfx,replay_t * rep)305 drawRepHud (data_t * gfx, replay_t * rep)
306 {
307 char *str;
308 str = malloc (sizeof (char) * strlen (_("Speed: ")) + 8);
309
310 strcpy (str, _(" Speed: "));
311 switch (rep->speed)
312 {
313 case REP_OX:
314 strcat (str, "1/8 X ");
315 break;
316 case REP_QX:
317 strcat (str, "1/4 X ");
318 break;
319 case REP_HX:
320 strcat (str, "1/2 X ");
321 break;
322 case REP_1X:
323 strcat (str, " 1 X ");
324 break;
325 case REP_2X:
326 strcat (str, " 2 X ");
327 break;
328 case REP_4X:
329 strcat (str, " 4 X ");
330 break;
331 case REP_8X:
332 strcat (str, " 8 X ");
333 break;
334 case REP_16X:
335 strcat (str, " 16 X ");
336 break;
337 default:
338 break;
339 }
340
341 JPB_drawSquare (gfx->gcolor, gfx->galpha,
342 gfx->gameX + BLOCKSIZE,
343 gfx->gameY,
344 SFont_TextWidth (gfx->textfont, str),
345 SFont_TextHeight (gfx->textfont));
346
347 SFont_Write (gfx->textfont, gfx->gameX + BLOCKSIZE, gfx->gameY, str);
348
349 free (str);
350 }
351
352 void
updateGameReplay(game_t * game,data_t * gfx,replay_t * rep,float ms,int mute)353 updateGameReplay (game_t * game, data_t * gfx, replay_t * rep, float ms,
354 int mute)
355 {
356 int i;
357
358 playRepSounds (gfx, rep, mute);
359 scrollReplay (game, gfx, rep);
360 for (i = 0; i < game->numHeros; i++)
361 {
362 if (game->heros[i].dead == FALSE)
363 {
364 getPlayerReplay (&(game->heros[i]), rep);
365 animateSpriteRot (&(game->heros[i].sprite[game->heros[i].id]), ms);
366 }
367 }
368 }
369
370 int
updateInputReplay(replay_t * rep,L_timer * time)371 updateInputReplay (replay_t * rep, L_timer * time)
372 {
373 int ret = 0;
374 SDL_Event event;
375
376 while (SDL_PollEvent (&event))
377 {
378 switch (event.type)
379 {
380 /* A key is pressed */
381 case SDL_KEYDOWN:
382 if (event.key.keysym.sym == SDLK_RIGHT)
383 {
384 rep->speed++;
385 if (rep->speed >= NREPSPEEDS)
386 rep->speed--;
387 setFpsTimer (time, REPFPS[rep->speed]);
388 }
389 if (event.key.keysym.sym == SDLK_LEFT)
390 {
391 rep->speed--;
392 if (rep->speed < 0)
393 rep->speed++;
394 setFpsTimer (time, REPFPS[rep->speed]);
395 }
396 if (event.key.keysym.sym == KEY_QUIT)
397 {
398 ret = TRUE;
399 }
400 if (event.key.keysym.sym == SDLK_p
401 || event.key.keysym.sym == SDLK_PAUSE)
402 {
403 ret = PAUSED;
404 }
405 break;
406 /* Quit: */
407 case SDL_QUIT:
408 ret = TRUE;
409 break;
410 /* Default */
411 default:
412 break;
413 }
414 }
415
416 return ret;
417 }
418
419 int
playReplay(data_t * gfx,replay_t * rep)420 playReplay (data_t * gfx, replay_t * rep)
421 {
422 L_timer timer;
423 game_t game;
424 int done = FALSE;
425 int r, i = 0, j;
426 /* int timegap; */
427 int skipf = 0;
428 int lskipf = 0;
429 /*int lskipf2 = 0; */
430 Uint32 mymscount = 0;
431 float tdelay, adelay, ladelay = 0;
432
433 if (gfx->musgame)
434 Mix_PlayMusic (gfx->musgame, -1);
435
436 drawBg (gfx->gameBg, 0, 0, gblOps.w, gblOps.h);
437 initGameReplay (&game, gfx, rep);
438 initTimer (&timer, rep->fps);
439 FlipScreen ();
440
441 updateTimer (&timer);
442 while (!done)
443 {
444 if ((r = updateInputReplay (rep, &timer)))
445 {
446 if (r == PAUSED)
447 {
448 done = pauseGame (gfx, &game, _("PAUSE"));
449 }
450 else
451 {
452 done =
453 yesNoQuestion (gfx, &game,
454 _
455 ("Are you sure you want to stop playing this replay? (Y/n)"));
456 }
457 continueTimer (&timer);
458 }
459 /*timegap = */
460 updateTimer (&timer);
461
462 tdelay = (1000.0 / (float) REPFPS[rep->speed]);
463 adelay = (float) timer.ms / (skipf + 1);
464 if (lskipf <= skipf && ladelay < adelay)
465 skipf--;
466 if (tdelay < adelay)
467 skipf++;
468 else
469 skipf--;
470
471 if (skipf < 0)
472 skipf = 0;
473 if (skipf > REPFPS[rep->speed] / REPFPS[REP_1X])
474 skipf = REPFPS[rep->speed] / REPFPS[REP_1X];
475 ladelay = adelay;
476 lskipf = skipf;
477 /* Cutremode on */
478 //skipf = ceil((float)timegap/(1000.0/(float)REPFPS[rep->speed]));
479
480 updateGameReplay (&game, gfx, rep, timer.ms, 0);
481
482 /* Be careful when it takes longer than what we want to reach... */
483 /*if (lskipf < skipf && lskipf2 < lskipf) {
484 skipf = lskipf;//-1; if (skipf < 0) skipf = 0;
485 } else {
486 lskipf2 = lskipf;
487 lskipf = skipf;
488 } */
489
490 //printf("skipping %d frames | %f, %f\n", skipf, adelay, tdelay);
491 mymscount += (skipf + 1) * (1000.0 / (float) REPFPS[REP_1X]);
492 for (j = 0; j < skipf && i < rep->nframes; i++, j++)
493 {
494 updateGameReplay (&game, gfx, rep, 0, 1);
495 }
496
497 drawGame (gfx, &game);
498 drawRepHud (gfx, rep);
499 updateScore (gfx, &game, mymscount);
500 FlipScreen ();
501
502 if (++i >= rep->nframes)
503 done = ENDMATCH;
504 }
505 if (done == ENDMATCH)
506 {
507 r =
508 yesNoQuestion (gfx, &game,
509 _("Do you want to watch this replay again? (Y/n)"));
510 }
511 else
512 {
513 r = FALSE;
514 }
515
516 freeGameReplay (&game);
517
518 return r;
519 }
520
521 void
freeGameReplay(game_t * game)522 freeGameReplay (game_t * game)
523 {
524 int i;
525 for (i = 0; i < game->numHeros; i++)
526 free (game->heros[i].trail);
527 free (game->heros);
528 }
529
530 void
fputUint32(Uint32 data,FILE * fh)531 fputUint32 (Uint32 data, FILE * fh)
532 {
533 fputc (data >> 24, fh);
534 fputc ((data << 8) >> 24, fh);
535 fputc ((data << 16) >> 24, fh);
536 fputc ((data << 24) >> 24, fh);
537 }
538
539 int
saveReplay(replay_t * rep,char * fname,char * comment)540 saveReplay (replay_t * rep, char *fname, char *comment)
541 {
542 char *fullname = NULL;
543 char buf[64];
544 time_t timt;
545 struct tm *tims;
546 FILE *fh = NULL;
547 int i;
548
549 fullname =
550 malloc (sizeof (char *) *
551 (strlen (fname) + strlen (gblOps.repDir) + strlen (REPEXT) + 2));
552 sprintf (fullname, "%s/%s%s", gblOps.repDir, fname, REPEXT);
553
554 if ((fh = fopen (fullname, "wb")) == NULL)
555 {
556 fprintf (stderr, _("ERROR: Could not save replay to %s\n"), fullname);
557 free (fullname);
558 return FALSE;
559 }
560
561 timt = time (0);
562 tims = localtime (&timt);
563
564 /* Header */
565 fputUint32 (REP_VERS, fh);
566
567 /* Comment */
568 fputs (comment, fh);
569 sprintf (buf, "Floor: %d", rep->record);
570 fputs (" | ", fh);
571 fputs (buf, fh);
572 sprintf (buf, "Time: %d", rep->totalms / 1000);
573 fputs (" | ", fh);
574 fputs (buf, fh);
575 strftime (buf, 64, "%H:%M %d/%m/%y", tims);
576 fputs (" | ", fh);
577 fputs (buf, fh);
578
579 fputc ('\0', fh);
580
581 /* The rest of the header */
582 fputUint32 (rep->record, fh);
583 fputUint32 (rep->totalms, fh);
584 fputUint32 (rep->fps, fh);
585 fputUint32 (rep->nframes, fh);
586
587 /* Body */
588 for (i = 0; i < rep->bodysize; i++)
589 {
590 fputc (((char *) (rep->bst))[i], fh);
591 }
592
593 free (fullname);
594
595 fclose (fh);
596
597 return TRUE;
598 }
599
600 Uint32
fgetUint32(FILE * fh)601 fgetUint32 (FILE * fh)
602 {
603 Uint32 data = 0;
604 data |= fgetc (fh) << 24;
605 data |= fgetc (fh) << 16;
606 data |= fgetc (fh) << 8;
607 data |= fgetc (fh);
608 return data;
609 }
610
611 int
loadReplay(data_t * gfx,char * file)612 loadReplay (data_t * gfx, char *file)
613 {
614 FILE *fh = NULL;
615 int version;
616 int size;
617 replay_t rep;
618 char *comment;
619 int i;
620
621 size = getFileSize (file);
622
623 if ((fh = fopen (file, "rb")) == NULL)
624 {
625 fprintf (stderr, _("ERROR: Could not open replay %s\n"), file);
626 return FALSE;
627 }
628
629 version = fgetUint32 (fh);
630 if (version != REP_VERS)
631 {
632 fprintf (stderr, _("ERROR: Replay %s is of a different version.\n"),
633 file);
634 return FALSE;
635 }
636
637 i = 0;
638 comment = malloc (sizeof (char) * MAX_CHAR);
639 while ((comment[i++] = fgetc (fh)) != '\0');
640
641 rep.record = fgetUint32 (fh);
642 rep.totalms = fgetUint32 (fh);
643 rep.fps = fgetUint32 (fh);
644 rep.nframes = fgetUint32 (fh);
645 rep.buf = rep.bst = malloc (size);
646 rep.bufsize = size;
647
648 for (rep.bodysize = 0; !feof (fh); rep.bodysize++)
649 {
650 ((char *) (rep.buf))[rep.bodysize] = fgetc (fh);
651 }
652
653 fclose (fh);
654
655 printf (_
656 ("Replay loaded: Size: %d FPS: %d Frames: %d Record: %d Time: %d Comment: %s\n"),
657 size, rep.fps, rep.nframes, rep.record, rep.totalms / 1000,
658 comment);
659 while (playReplay (gfx, &rep));
660
661 freeReplay (&rep);
662 free (comment);
663
664 return TRUE;
665 }
666
667 char *
getReplayComment(char * file)668 getReplayComment (char *file)
669 {
670 FILE *fh = NULL;
671 int version;
672 char *comment = NULL;
673 int i;
674
675 if ((fh = fopen (file, "rb")) == NULL)
676 {
677 fprintf (stderr, _("WARNING: Could not open replay %s\n"), file);
678 return NULL;
679 }
680
681 version = fgetUint32 (fh);
682 if (version != REP_VERS)
683 {
684 fprintf (stderr, _("WARNING: Replay %s is of a different version.\n"),
685 file);
686 return NULL;
687 }
688
689 i = 0;
690 comment = malloc (sizeof (char) * MAX_CHAR);
691 while ((comment[i++] = fgetc (fh)) != '\0');
692
693 return comment;
694 }
695