1 /*
2  * Luola - 2D multiplayer cave-flying game
3  * Copyright (C) 2001-2006 Calle Laakkonen
4  *
5  * File        : animation.c
6  * Description : This module handles all the animation and redraw timings
7  * Author(s)   : Calle Laakkonen
8  *
9  * Luola is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Luola is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  */
23 
24 #include <stdlib.h>
25 #include "SDL.h"
26 
27 #include "fs.h"
28 #include "console.h"
29 #include "level.h"
30 #include "player.h"
31 #include "projectile.h"
32 #include "animation.h"
33 #include "particle.h"
34 #include "special.h"
35 #include "critter.h"
36 #include "decor.h"
37 #include "ship.h"
38 
39 /* Internally used globals */
40 static SDL_Rect anim_update_rects[2];
41 static int anim_rects;
42 static char anim_gamepaused;
43 static enum {SCR_UNDEF,SCR_QUARTER,SCR_HALF,SCR_FULL} screen_geometry;
44 static Uint32 anim_fadescr;     /* Fade dead player screens */
45 
46 /* Exported globals */
47 int endgame;
48 
49 /* Set quarter screens */
set_quarter_geom(void)50 static void set_quarter_geom(void)
51 {
52     int p;
53     for(p=0;p<4;p++) {
54         cam_rects[p].w = screen->w/2;
55         cam_rects[p].h = screen->h/2;
56         viewport_rects[p].x = (screen->w/2) * (p%2);
57         viewport_rects[p].y = (screen->h/2) * (p/2);
58     }
59     screen_geometry = SCR_QUARTER;
60 }
61 
62 /* Set half screens for two players */
set_half_geom(int p1,int p2)63 static void set_half_geom(int p1,int p2)
64 {
65     cam_rects[p1].w=screen->w;
66     cam_rects[p1].h=screen->h/2;
67     cam_rects[p2] = cam_rects[p1];
68     viewport_rects[p1].x=0;
69     viewport_rects[p1].y=0;
70 
71     viewport_rects[p2].x=0;
72     viewport_rects[p2].y=screen->h/2;
73 
74     screen_geometry = SCR_HALF;
75 }
76 
77 /* Set single player as fullscreen */
set_full_geom(int p)78 static void set_full_geom(int p) {
79     cam_rects[p].w=screen->w;
80     cam_rects[p].h=screen->h;
81     viewport_rects[p].x=0;
82     viewport_rects[p].y=0;
83     screen_geometry = SCR_FULL;
84 }
85 
86 /* Recalculate player screen geometry */
recalc_geometry(void)87 void recalc_geometry(void)
88 {
89     int oldgeom=screen_geometry;
90     if(game_settings.bigscreens==0) {
91         /* Always use quarter screens */
92         set_quarter_geom();
93     } else {
94         /* Maximize available screen estate */
95         int r,numplayers=0;
96         int my_players[4] = {0};
97 
98         for (r = 0; r < 4; r++) {
99             if (players[r].state==ALIVE) {
100                 my_players[r] = 1;
101                 numplayers++;
102             }
103         }
104         if( numplayers==1 ) {
105             for(r=0;r<4;r++) {
106                 if(my_players[r]) {
107                     set_full_geom(r);
108                     break;
109                 }
110             }
111         } else if( numplayers==2 ) {
112             int p1=-1,p2=-1;
113             for(r=0;r<4;r++) {
114                 if(my_players[r]) {
115                     if(p1==-1) p1=r; else p2=r;
116                 }
117             }
118             if(p1<0 || p2<0) {
119                 printf("Bug! recalc_geometry(): p1==%d, p2==%d\n",p1,p2);
120                 abort();
121             }
122             set_half_geom(p1,p2);
123         } else {
124             set_quarter_geom();
125         }
126     }
127 
128     /* Stars must be recalculated after geometry change */
129     if(oldgeom != screen_geometry)
130         reinit_stars();
131 }
132 
133 /* Get viewport size */
get_viewport_size(void)134 SDL_Rect get_viewport_size(void) {
135     SDL_Rect size;
136     if(game_settings.bigscreens) {
137         int r,num=0;
138         for(r=0;r<4;r++)
139             if(players[r].state!=INACTIVE) num++;
140         if(num==1) {
141             size.w = screen->w;
142             size.h = screen->h;
143         } else if(num==2) {
144             size.w = screen->w;
145             size.h = screen->h/2;
146         } else {
147             size.w = screen->w/2;
148             size.h = screen->h/2;
149         }
150     } else {
151         size.w = screen->w/2;
152         size.h = screen->h/2;
153     }
154 
155     return size;
156 }
157 
158 /* Arrange screen update rectangles for quarter screens */
rearrange_quarter(int numplayers,int my_players[4])159 static void rearrange_quarter(int numplayers, int my_players[4])
160 {
161     /* What should we update on frame redraw? */
162     anim_update_rects[0].x = 0;
163     anim_update_rects[0].y = 0;
164     anim_update_rects[0].w = screen->w/2;
165     anim_update_rects[0].h = screen->h/2;
166     anim_update_rects[1].x = 0;
167     anim_update_rects[1].y = screen->h/2;
168     anim_update_rects[1].w = screen->w/2;
169     anim_update_rects[1].h = screen->h/2;
170     anim_rects = 1;
171 
172     if (numplayers == 4) {
173         /* 4 players, update whole screen */
174         anim_update_rects[0].w = screen->w;
175         anim_update_rects[0].h = screen->h;
176     } else {
177         if(numplayers==2 && my_players[0] && my_players[2]) {
178             /* Special case, player 1 & 3 */
179             anim_update_rects[0].h = screen->h;
180         } else if(numplayers==2 && my_players[1] && my_players[3]) {
181             /* Special case, player 2 & 4 */
182             anim_update_rects[0].x = screen->w/2;
183             anim_update_rects[0].h = screen->h;
184         } else {
185             /* Players 1 & 2  and 3 & 4*/
186             int r;
187             for(r=0;r<4;r+=2) {
188                 if(my_players[r+1]) {
189                     if(my_players[r]) {
190                         anim_update_rects[r/2].w = screen->w;
191                     } else {
192                         anim_update_rects[r/2].x += screen->w/2;
193                     }
194                 } else if(my_players[r]==0) {
195                     anim_update_rects[r/2].w = 0;
196                 }
197             }
198             anim_rects = (anim_update_rects[0].w != 0) + (anim_update_rects[1].w != 0);
199             if(anim_update_rects[0].w==0 && anim_update_rects[1].w!=0)
200                 anim_update_rects[0] = anim_update_rects[1];
201         }
202     }
203 }
204 
205 /* Arrange screen update rectangles for half screens */
rearrange_half(int numplayers,int my_players[4])206 static void rearrange_half(int numplayers, int my_players[4])
207 {
208     anim_rects = 1;
209     if(numplayers==2) {
210         anim_update_rects[0].x = 0;
211         anim_update_rects[0].y = 0;
212         anim_update_rects[0].w = screen->w;
213         anim_update_rects[0].h = screen->h;
214     } else {
215         int r,deadplr=-1,aliveplr=-1;
216         for(r=0;r<4;r++) {
217             if(players[r].state == BURIED)
218                 deadplr = r;
219             else if(my_players[r])
220                 aliveplr = r;
221         }
222         if(deadplr<0 || aliveplr<0) {
223             fprintf(stderr,"%s: BUG: deadplr==%d, aliveplr==%d\n",
224                     __func__,deadplr,aliveplr);
225         }
226         anim_update_rects[0].x = 0;
227         anim_update_rects[0].y = (screen->h/2) * (aliveplr>deadplr);
228         anim_update_rects[0].w = screen->w;
229         anim_update_rects[0].h = screen->h/2;
230     }
231 }
232 
233 /* Reinitialize animation */
reinit_animation(void)234 void reinit_animation (void)
235 {
236     anim_gamepaused = 0;
237     anim_fadescr = 0;
238     endgame = -1;
239 }
240 
241 /* Arrange screen update rectangles */
rearrange_animation(void)242 void rearrange_animation (void)
243 {
244     int r, numplayers=0;
245     int my_players[4] = {0};
246     /* Which players are to be updated */
247     for (r = 0; r < 4; r++) {
248         if (players[r].state==ALIVE || players[r].state==DEAD) {
249             my_players[r] = 1;
250             numplayers++;
251         }
252     }
253 
254     if(numplayers==0) {
255         anim_rects = 0;
256     } else {
257         switch(screen_geometry) {
258             case SCR_UNDEF:
259                 fputs("rearrange_animation(): BUG: screen geometry in undefined!\n",stderr);
260                 abort();
261             case SCR_QUARTER:
262                 rearrange_quarter(numplayers,my_players);
263                 break;
264             case SCR_HALF:
265                 rearrange_half(numplayers,my_players);
266                 break;
267             case SCR_FULL:
268                 anim_update_rects[0].x = 0;
269                 anim_update_rects[0].y = 0;
270                 anim_update_rects[0].w = screen->w;
271                 anim_update_rects[0].h = screen->h;
272                 anim_rects = 1;
273                 break;
274         }
275     }
276 }
277 
278 /* Fade out a player viewport */
fade_plr_screen(int plr,Uint8 opacity)279 static void fade_plr_screen(int plr,Uint8 opacity)
280 {
281     SDL_Rect msg;
282 #ifdef HAVE_LIBSDL_GFX
283     boxRGBA(screen,viewport_rects[plr].x,viewport_rects[plr].y,
284             viewport_rects[plr].x+cam_rects[plr].w,
285             viewport_rects[plr].y+cam_rects[plr].h,
286             0,0,0,opacity);
287 #else
288     SDL_Rect rect;
289     rect.x = viewport_rects[plr].x;
290     rect.y = viewport_rects[plr].y;
291     rect.w = cam_rects[plr].w;
292     rect.h = cam_rects[plr].h*opacity/510;
293 
294     SDL_FillRect(screen,&rect,0);
295     rect.y+=cam_rects[plr].h-rect.h;
296     SDL_FillRect(screen,&rect,0);
297 
298     rect.y = viewport_rects[plr].y+rect.h;
299     rect.h = cam_rects[plr].h-rect.h*2;
300     rect.w = cam_rects[plr].w*opacity/510;
301 
302     SDL_FillRect(screen,&rect,0);
303     rect.x+=cam_rects[plr].w-rect.w;
304     SDL_FillRect(screen,&rect,0);
305 #endif
306     msg.x = viewport_rects[plr].x + cam_rects[plr].w/2 - plr_messages[plr]->w/2;
307     msg.y = viewport_rects[plr].y + cam_rects[plr].h/2 - plr_messages[plr]->h/2;
308     if(plr_messages[plr])
309         SDL_BlitSurface (plr_messages[plr], NULL, screen, &msg);
310     else
311         printf("Bug! fade_plr_screen(%d,%d): plr_messages[%d] is NULL!\n",plr,opacity,plr);
312 }
313 
kill_plr_screen(int plr)314 void kill_plr_screen (int plr)
315 {
316     anim_fadescr |= FADE_STEP << (plr*8);
317 }
318 
pause_game(void)319 int pause_game (void)
320 {
321     int p;
322     SDL_Rect rect;
323     if (anim_gamepaused) {
324         anim_gamepaused = 0;
325     } else {
326         SDL_Surface *pause_msg;
327         SDL_Rect msg_rect;
328         if ((game_settings.endmode == 0 && plr_teams_left <= 1)
329             || (game_settings.endmode == 1 && plr_teams_left < 1))
330             return 1;  /* Dont bother pausing the game, its already over */
331         anim_gamepaused = 1;
332 
333         /* Render the message string */
334         pause_msg = renderstring(Bigfont,"Paused",font_color_red);
335 
336         /* Draw pause messages */
337         for (p = 0; p < 4; p++)
338             if (players[p].state==ALIVE) {
339                 rect.x = viewport_rects[p].x;
340                 rect.y = viewport_rects[p].y;
341                 rect.w = cam_rects[p].w;
342                 rect.h = cam_rects[p].h;
343                 fill_box(screen, viewport_rects[p].x, viewport_rects[p].y, viewport_rects[p].w,
344                         viewport_rects[p].h, col_pause_backg);
345                 msg_rect.x = rect.x + rect.w/2 - pause_msg->w/2;
346                 msg_rect.y = rect.y + rect.h/2 - pause_msg->h/2;
347                 SDL_BlitSurface(pause_msg,NULL,screen,&msg_rect);
348             }
349         SDL_UpdateRects (screen, anim_rects, anim_update_rects);
350         SDL_FreeSurface(pause_msg);
351     }
352     return !anim_gamepaused;
353 }
354 
355 /**** Redraw frame ****/
animate_frame(void)356 void animate_frame (void)
357 {
358     /* Delay after game ends */
359     if (endgame > 0)
360         endgame--;
361     else if (endgame == 0)
362         game_loop = 0;
363 
364     /* Do animations */
365     animate_players ();
366     animate_ships ();
367     animate_pilots ();
368     animate_level ();
369     animate_specials ();
370     animate_critters ();
371     animate_decorations ();
372     /* Draw */
373     draw_ships ();
374     draw_pilots ();
375     animate_projectiles ();
376     animate_particles ();
377     draw_bat_attack ();
378     draw_player_hud ();
379 
380     /* Fade dead player screens to black */
381     if(anim_fadescr) {
382         Uint8 fades[4];
383         int r;
384         for(r=0;r<4;r++) {
385             fades[r] = (anim_fadescr >> (r*8)) & 0xff;
386             if(fades[r]) {
387                 fades[r]--;
388                 fade_plr_screen(r,(FADE_STEP-fades[r])/(double)FADE_STEP*255);
389                 if(fades[r]==0) {
390                     players[r].state = BURIED;
391                     rearrange_animation();
392                 }
393             }
394         }
395         anim_fadescr = fades[0] | (fades[1] << 8) | (fades[2] << 16) | (fades[3] << 24);
396     }
397 
398     /* Update screen */
399     SDL_UpdateRects (screen, anim_rects, anim_update_rects);
400 
401     /* End the level if there are less than two teams left
402      * and endmode is last player wins or if there are less than 10
403      * pixels of base terrain left or all players are dead. */
404     if (endgame == -1) {
405         if (plr_teams_left < 2) {
406             if (game_settings.endmode == 0 || lev_level.base_area < 10
407                     || plr_teams_left == 0)
408                 endgame = 30;
409         }
410     }
411 }
412 
413