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