1 /***************************************************************************
2                           bricks.c  -  description
3                              -------------------
4     begin                : Thu Sep 6 2001
5     copyright            : (C) 2001 by Michael Speck
6     email                : kulkanie@gmx.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <math.h>
19 #include "../game/game.h"
20 #include "lbreakout.h"
21 #include "shrapnells.h"
22 #include "bricks.h"
23 
24 extern SDL_Surface *stk_display;
25 extern SDL_Surface *offscreen; /* offscreen with bricks, background, frame */
26 extern SDL_Surface *bkgnd; /* background picture (includes frame) */
27 extern SDL_Surface *warp_pic;
28 extern SDL_Surface *brick_pic; /* brick graphics in a horizontal order */
29 extern int shadow_size;
30 extern SDL_Surface *brick_shadow; /* shadow mask */
31 extern int shine_x, shine_y, shine_recreate;
32 #ifdef AUDIO_ENABLED
33 extern StkSound *wav_boom;
34 #endif
35 extern Game *game;
36 
37 /*
38 ====================================================================
39 Locals
40 ====================================================================
41 */
42 
43 /* remove a brick and release the extra (which will have no effect
44  * on collection) update the graphics. no growth or explosions
45  * of other bricks are initiated as these are send by network */
client_brick_remove(int mx,int my,int type,Vector imp,Paddle * paddle,int no_sound)46 void client_brick_remove( int mx, int my, int type, Vector imp, Paddle *paddle, int no_sound )
47 {
48   int px, py, w, h, i, j, shadow, anim_brick_id;
49   int dir;
50 
51   /* add explosion animation */
52   if ( type == SHR_BY_EXPL_WITH_EXPL ) {
53     exp_create( mx * BRICK_WIDTH + ( BRICK_WIDTH >> 1 ),
54 		my * BRICK_HEIGHT + ( BRICK_HEIGHT >> 1 ) );
55 #ifdef AUDIO_ENABLED
56   if (!no_sound)
57     stk_sound_play_x( mx * BRICK_WIDTH + ( BRICK_WIDTH >> 1 ), wav_boom );
58 #endif
59     type = SHR_BY_EXPL; /* WITH_EXPL is not known */
60   }
61 
62 	/* decrease brick count if no indestructible brick was destroyed */
63 	if ( game->bricks[mx][my].dur != -1 ) {
64 		game->bricks_left--;
65 
66 		/* adjust warp limit if this was a grown brick; then limit
67 		 * has to decrease again */
68 		if (IS_GROWN_BRICK_CHAR(game->bricks[mx][my].brick_c))
69 			game->warp_limit--;
70 	}
71 
72   /* before removing the brick, store the brick id for animation */
73   anim_brick_id = game->bricks[mx][my].id;
74 
75   /* remove brick from map */
76   game->bricks[mx][my].brick_c = ' ';
77   game->bricks[mx][my].id = -1;
78   game->bricks[mx][my].dur = -1;
79   game->bricks[mx][my].exp_time = -1;
80   game->bricks[mx][my].heal_time = -1;
81   game->bricks[mx][my].type = MAP_EMPTY;
82 
83   /* get screen position */
84   px = mx * BRICK_WIDTH;
85   py = my * BRICK_HEIGHT;
86 
87   /* release extra if one exists. in opposite to the server the goldshower
88    * 1000P is not released here but in the hit handle function if the gold_shower
89    * flag is set to avoid releasing 'ghost' extras due to latency. */
90   dir = ( paddle->type == PADDLE_TOP ) ? -1 : 1;
91   if ( game->bricks[mx][my].extra != EX_NONE ) {
92     if ( game->diff->allow_maluses || !extra_is_malus( game->bricks[mx][my].extra ) )
93       list_add( game->extras, extra_create( game->bricks[mx][my].extra, px, py, dir ) );
94   }
95   game->bricks[mx][my].extra = EX_NONE;
96   game->bricks[mx][my].extra_c = ' ';
97 
98   /* in case of darkness no (graphical) remove nescessary */
99   if ( game->extra_active[EX_DARKNESS] ) {
100 #ifdef AUDIO_ENABLED
101   if (!no_sound)
102     stk_sound_play_x( px, wav_boom );
103 #endif
104     return;
105   }
106 
107   shrapnells_create( brick_pic, anim_brick_id * BRICK_WIDTH, 0,
108 		     BRICK_WIDTH, BRICK_HEIGHT,
109 		     px, py, type, imp );
110   /* recreate shine if needed */
111   if (px == shine_x && py == shine_y) shine_recreate = 1;
112   /* clear offscreen */
113   w = BRICK_WIDTH + shadow_size; h = BRICK_HEIGHT + shadow_size;
114   if ( px + w > stk_display->w - BRICK_WIDTH )
115     w = stk_display->w - BRICK_WIDTH - px;
116   stk_surface_clip( offscreen, px, py, w, h );
117   stk_surface_blit( bkgnd, px, py, w, h, offscreen, px, py );
118   /* if shadow redraw close bricks */
119   for ( i = mx - 1; i <= mx + 1; i++ )
120     for ( j = my - 1; j <= my + 1; j++ ) {
121       if ( i > 0 && j > 0 && i < MAP_WIDTH - 1 ) {
122 	if ( game->bricks[i][j].type != MAP_EMPTY ) {
123 	  if ( i <= mx && j <= my ) shadow = 1; else shadow = 0;
124 	  brick_draw( offscreen, i, j, shadow );
125 	}
126       }
127     }
128   stk_surface_clip( offscreen, 0, 0, 0, 0 );
129   /* update screen */
130   stk_surface_blit( offscreen, px, py, w, h, stk_display, px, py );
131   stk_display_store_drect();
132 #ifdef AUDIO_ENABLED
133   if (!no_sound)
134     stk_sound_play_x( px, wav_boom );
135 #endif
136 }
137 
client_brick_heal(int x,int y)138 static void client_brick_heal( int x, int y )
139 {
140 	game->bricks[x][y].dur++;
141 	game->bricks[x][y].id++;
142 	if ( !game->extra_active[EX_DARKNESS] ) {
143 		brick_draw( offscreen, x, y, 0 );
144 		brick_draw( stk_display, x, y, 0 );
145 		stk_display_store_drect();
146 	}
147 }
148 
client_brick_grow(int x,int y,int id)149 static void client_brick_grow( int x, int y, int id )
150 {
151 	Brick *brick = &game->bricks[x][y];
152 	int px, py;
153 	int isReplace = 0;
154 
155 	if (game->bricks[x][y].type!=MAP_EMPTY)
156 		isReplace = 1;
157 
158 	/* add brick */
159 	//brick->id = RANDOM( BRICK_GROW_FIRST, BRICK_GROW_LAST );
160 	brick->id = id;
161 	brick->brick_c = brick_get_char( brick->id );
162 	brick->type = MAP_BRICK;
163 	brick->score = game->diff->score_mod * BRICK_SCORE / 10;
164 	brick->dur = 1;
165 	/* keep the extra that is already assigned to this position */
166 	brick->exp_time = -1;
167 	brick->heal_time = -1;
168 
169 	/* XXX mark grown bricks by upper case. with this trick we can store
170 	 * this information in the level snapshot. */
171 	brick->brick_c -= 32; /* f->F, ... */
172 
173 	if (!isReplace) {
174 		game->bricks_left++;
175 
176 		/* adjust warp limit (grown bricks don't help hitting limit) */
177 		game->warp_limit++;
178 	}
179 
180 	if ( !game->extra_active[EX_DARKNESS] ) {
181 		px = x * BRICK_WIDTH;
182 		py = y * BRICK_HEIGHT;
183 		if (isReplace)
184 			brick_draw( offscreen, x, y, 0 );
185 		else
186 			brick_draw_complex( x, y, px, py );
187 		stk_surface_blit( offscreen, px, py,
188 				BRICK_WIDTH + shadow_size,
189 				BRICK_HEIGHT + shadow_size,
190 				stk_display, px, py );
191 		stk_display_store_drect();
192 	}
193 }
194 
195 /*
196 ====================================================================
197 Publics
198 ====================================================================
199 */
200 
201 /*
202 ====================================================================
203 Draw all bricks to offscreen surface.
204 ====================================================================
205 */
bricks_draw()206 void bricks_draw()
207 {
208 	int i, j;
209 	if ( offscreen == 0 ) return;
210 	stk_surface_clip( offscreen, 0, 0, stk_display->w - BRICK_WIDTH, stk_display->h );
211 	for ( j = 1; j < MAP_HEIGHT - 1; j++ )
212 		for ( i = 1; i < MAP_WIDTH - 1; i++ )
213 			if ( game->bricks[i][j].id >= 0 )
214 				brick_draw( offscreen, i, j, 1 );
215 	stk_surface_clip( offscreen, 0,0,0,0 );
216 }
217 /*
218 ====================================================================
219 Draw brick to passed surface
220 ====================================================================
221 */
brick_draw(SDL_Surface * surf,int map_x,int map_y,int shadow)222 void brick_draw( SDL_Surface *surf, int map_x, int map_y, int shadow )
223 {
224 	int x = map_x * BRICK_WIDTH, y = map_y * BRICK_HEIGHT;
225 	/* dont draw invisible bricks */
226 	if ( game->bricks[map_x][map_y].id == INVIS_BRICK_ID ) return;
227 	/* add shadow */
228 	if ( shadow ) {
229 		stk_surface_alpha_blit( brick_shadow,
230 				game->bricks[map_x][map_y].id * BRICK_WIDTH, 0,
231 				BRICK_WIDTH, BRICK_HEIGHT,
232 				surf, x + shadow_size, y + shadow_size, SHADOW_ALPHA );
233 	}
234 	/* brick if not frame brick */
235 	if ( map_x == 0 || map_y == 0 || map_x == MAP_WIDTH - 1 ) return;
236 	stk_surface_blit( brick_pic,
237 			game->bricks[map_x][map_y].id * BRICK_WIDTH, 0,
238 			BRICK_WIDTH, BRICK_HEIGHT, surf, x,y );
239 }
240 /*
241 ====================================================================
242 Add brick with clipped shadow to offscreen. To draw a brick without
243 shadow check use brick_draw().
244 ====================================================================
245 */
brick_draw_complex(int mx,int my,int px,int py)246 void brick_draw_complex( int mx, int my, int px, int py )
247 {
248 	brick_draw( offscreen, mx, my, 1 );
249 	/* redraw surrounding bricks */
250 	stk_surface_clip( offscreen, px + shadow_size, py + shadow_size, BRICK_WIDTH, BRICK_HEIGHT );
251 	if ( mx + 1 == MAP_WIDTH - 1 ) {
252 		/* right frame part */
253 		stk_surface_blit( bkgnd, px + BRICK_WIDTH, py,
254 				BRICK_WIDTH, ( BRICK_HEIGHT << 1 ),
255 				offscreen, px + BRICK_WIDTH, py );
256 	}
257 	else {
258 		brick_draw( offscreen, mx + 1, my, 0 );
259 		brick_draw( offscreen, mx + 1, my + 1, 0 );
260 	}
261 	if ( game->bricks[mx][my + 1].type != MAP_EMPTY )
262 		brick_draw( offscreen, mx, my + 1, 0 );
263 	stk_surface_clip( offscreen, 0, 0, 0, 0 );
264 }
265 /*
266 ====================================================================
267 Make brick at mx,my loose 'points' duration. It must have been
268 previously checked that this operation is completely valid.
269 It does not update net_bricks or the player's duration reference.
270 ====================================================================
271 */
client_brick_loose_dur(int mx,int my,int points)272 void client_brick_loose_dur( int mx, int my, int points )
273 {
274 	int px, py;
275 	int refresh_h, refresh_w;
276 	while ( points-- > 0 ) {
277 		game->bricks[mx][my].dur--;
278 		game->bricks[mx][my].id--;
279 		/* adjust brick character:
280 		 * a,b,c - multiple hits
281 		 * v - invisible */
282 		if ( game->bricks[mx][my].brick_c == 'v' )
283 			game->bricks[mx][my].brick_c = 'c';
284 		else
285 			game->bricks[mx][my].brick_c--; /* successive order */
286 		if ( !game->extra_active[EX_DARKNESS]) {
287 			px = mx * BRICK_WIDTH;
288 			py = my * BRICK_HEIGHT;
289 			refresh_w = BRICK_WIDTH+shadow_size;
290 			refresh_h = BRICK_HEIGHT+shadow_size;
291 			/* copy background as old brick may have different transparency
292 			   do this in three parts to save computation */
293 			stk_surface_blit( bkgnd, px, py,
294 					shadow_size, BRICK_HEIGHT,
295 					offscreen, px, py );
296 			stk_surface_blit( bkgnd, px + shadow_size, py,
297 					BRICK_WIDTH - shadow_size, BRICK_HEIGHT + shadow_size,
298 					offscreen, px + shadow_size, py );
299 			stk_surface_blit( bkgnd, px + BRICK_WIDTH, py + shadow_size,
300 					shadow_size, BRICK_HEIGHT,
301 					offscreen, px + BRICK_WIDTH, py + shadow_size );
302 			/* draw brick + surrounding */
303 			brick_draw_complex( mx, my, px, py );
304 			/* udpate screen */
305 			stk_surface_blit( offscreen, px, py,
306 					refresh_w, refresh_h, stk_display, px, py );
307 			stk_display_store_drect();
308 		}
309 	}
310 }
311 
312 /* handle a received brick action */
client_handle_brick_hit(BrickHit * hit)313 void client_handle_brick_hit( BrickHit *hit )
314 {
315   Paddle *paddle;
316   Vector imp;
317 
318   paddle = (hit->paddle==PADDLE_BOTTOM)?game->paddles[PADDLE_BOTTOM]:game->paddles[PADDLE_TOP];
319   angle2vec( hit->degrees, &imp );
320 
321   switch ( hit->type )
322     {
323     case HT_HIT:
324       client_brick_loose_dur( hit->x, hit->y, 1 );
325       break;
326     case HT_REMOVE:
327       if (hit->draw_explosion)
328 	hit->dest_type = SHR_BY_EXPL_WITH_EXPL;
329       client_brick_remove( hit->x, hit->y, hit->dest_type, imp, paddle, hit->no_sound );
330       /* gold shower extra */
331       if ( hit->gold_shower )
332 	list_add( game->extras,
333 		  extra_create( EX_SCORE1000,
334 				hit->x*BRICK_WIDTH, hit->y*BRICK_HEIGHT,
335 				(hit->paddle==PADDLE_TOP)?-1:1 ) );
336       break;
337     case HT_HEAL:
338       client_brick_heal( hit->x, hit->y );
339       break;
340     case HT_GROW:
341       client_brick_grow( hit->x, hit->y, hit->brick_id );
342       break;
343     }
344 }
345 
346 /* redraw all bricks (leaving shadows in transparent frame parts) */
client_redraw_all_bricks()347 void client_redraw_all_bricks()
348 {
349     stk_surface_blit( bkgnd, BRICK_WIDTH, BRICK_HEIGHT,
350                       stk_display->w - BRICK_WIDTH*2,stk_display->h - BRICK_HEIGHT,
351                       offscreen, BRICK_WIDTH, BRICK_HEIGHT );
352     bricks_draw();
353     stk_surface_blit( offscreen, BRICK_WIDTH, BRICK_HEIGHT,
354                       stk_display->w - BRICK_WIDTH*2,stk_display->h - BRICK_HEIGHT,
355                       stk_display, BRICK_WIDTH, BRICK_HEIGHT );
356 //    stk_display_update( STK_UPDATE_ALL );
357     stk_display_store_drect();
358 }
359