1 /*
2  * Biloba
3  * Copyright (C) 2004-2008 Guillaume Demougeot, Colin Leroy
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 /**
21  * Biloba - Q1 2005
22 	* Game by Guillaume Demougeot <dmgt@wanadoo.fr>
23 	* Code by Colin Leroy <colin@colino.net>
24 	*
25 	* This file contains all functions related to tile
26 	* management.
27 	*/
28 
29 #include <SDL.h>
30 #include <SDL_image.h>
31 #include <stdlib.h>
32 
33 #include "utils.h"
34 #include "tile.h"
35 #include "pawn.h"
36 #include "arrow.h"
37 #include "logic.h"
38 
39 #ifdef DEBUG
tile_type_str(TileType type)40 static char *tile_type_str(TileType type)
41 {
42 	switch (type) {
43 	case TILE_NO_TILE:
44 		return "TILE_NO_TILE";
45 	case TILE_NORMAL:
46 		return "TILE_NORMAL";
47 	case TILE_CENTER:
48 		return "TILE_CENTER";
49 	case TILE_UP_LEFT:
50 		return "TILE_UP_LEFT";
51 	case TILE_UP_RIGHT:
52 		return "TILE_UP_RIGHT";
53 	case TILE_DOWN_LEFT:
54 		return "TILE_DOWN_LEFT";
55 	case TILE_DOWN_RIGHT:
56 		return "TILE_DOWN_RIGHT";
57 	default:
58 		assert(FALSE);
59 		return "TILE_ERROR";
60 	}
61 }
62 #endif
tile_get_type(int x,int y)63 static TileType tile_get_type(int x, int y)
64 {
65 	if (x == 0 || x == MAX_TILES_X-1) {
66 		switch (y) {
67 		case 0:
68 		case MAX_TILES_Y-1:
69 			return TILE_NO_TILE;
70 		case 1:
71 			return (x==0 ? TILE_UP_LEFT:TILE_UP_RIGHT);
72 		case MAX_TILES_Y-2:
73 			return (x==0 ? TILE_DOWN_LEFT:TILE_DOWN_RIGHT);
74 		}
75 	} else if (x == 1 || x == MAX_TILES_X-2) {
76 		switch (y) {
77 		case 0:
78 			return (x==1 ? TILE_UP_LEFT:TILE_UP_RIGHT);
79 		case MAX_TILES_Y-1:
80 			return (x==1 ? TILE_DOWN_LEFT:TILE_DOWN_RIGHT);
81 		}
82 	} else if (x == (MAX_TILES_X-1)/2 && y == (MAX_TILES_Y-1)/2)
83 		return TILE_CENTER;
84 
85 	return TILE_NORMAL;
86 }
87 
88 static SDL_Surface *tiles_models[NUM_TILE_TYPES] = {NULL};
89 static LList *my_tiles = NULL;
90 
init_tiles_models(void)91 static void init_tiles_models(void)
92 {
93 	int i = 0;
94 	assert(tiles_models[TILE_NO_TILE] == NULL);
95 
96 	tiles_models[TILE_NO_TILE]    = biloba_load_image("tile-no-tile.png");
97 	tiles_models[TILE_NORMAL]     = biloba_load_image("tile.png");
98 	tiles_models[TILE_CENTER]     = biloba_load_image("tile-center.png");
99 	tiles_models[TILE_UP_LEFT]    = biloba_load_image("tile-up-left.png");
100 	tiles_models[TILE_UP_RIGHT]   = biloba_load_image("tile-up-right.png");
101 	tiles_models[TILE_DOWN_LEFT]  = biloba_load_image("tile-down-left.png");
102 	tiles_models[TILE_DOWN_RIGHT] = biloba_load_image("tile-down-right.png");
103 
104 	for (i = 0; i < NUM_TILE_TYPES; i++)
105 		assert(tiles_models[i] != NULL);
106 }
107 
tile_find(int x,int y)108 static Tile *tile_find(int x, int y)
109 {
110 	LList *cur = my_tiles;
111 
112 	while (cur) {
113 		Tile *tile = (Tile *)cur->data;
114 		if (tile->pos_x == x && tile->pos_y == y)
115 			return tile;
116 		cur = cur->next;
117 	}
118 	return NULL;
119 }
120 
tile_get(int x,int y)121 Tile *tile_get(int x, int y)
122 {
123 	Tile *new_tile = NULL;
124 
125 	assert(x >= 0 && x < MAX_TILES_X);
126 	assert(y >= 0 && y < MAX_TILES_Y);
127 #ifdef DEBUG
128 	printf("tile %d,%d: %s\n", x, y, tile_type_str(tile_get_type(x,y)));
129 #endif
130 	if (tiles_models[TILE_NO_TILE] == NULL)
131 		init_tiles_models();
132 
133 	if (tile_find(x, y))
134 		return tile_find(x, y);
135 
136 	new_tile = malloc(sizeof(Tile));
137 
138 	new_tile->type 		= tile_get_type(x, y);
139 	new_tile->pos_x		= x;
140 	new_tile->pos_y		= y;
141 	new_tile->coord_x	= get_x(x);
142 	new_tile->coord_y	= get_y(y);
143 	new_tile->surface	= tiles_models[new_tile->type];
144 	new_tile->pawn		= NULL;
145 
146 	my_tiles = llist_append(my_tiles, new_tile);
147 
148 	return new_tile;
149 }
150 
tile_free_all(void)151 void tile_free_all(void)
152 {
153 	LList *tiles = my_tiles;
154 	for(; tiles; tiles = tiles->next) {
155 		Tile *tile = (Tile *)tiles->data;
156 		free(tile);
157 		tile = NULL;
158 	}
159 	llist_free(my_tiles);
160 	my_tiles = NULL;
161 }
162 
tile_get_by_coords(int x,int y)163 Tile *tile_get_by_coords (int x, int y)
164 {
165 	int nx = (x - X_OFFSET)/50;
166 	int ny = (y - Y_OFFSET)/50;
167 
168 #ifdef DEBUG
169 	printf("tile_get_by_coords: %d,%d -> %d,%d\n", x, y, nx, ny);
170 #endif
171 	if (nx < 0 || nx >= MAX_TILES_X)
172 		return NULL;
173 	if (ny < 0 || ny >= MAX_TILES_Y)
174 		return NULL;
175 
176 	return tile_get(nx, ny);
177 }
178 
tile_draw_fast(Tile * tile,int fast)179 static void tile_draw_fast (Tile *tile, int fast)
180 {
181 	assert(tile != NULL);
182 	put_image(tile->surface, tile->coord_x, tile->coord_y);
183 
184 	if (tile->pawn)
185 		pawn_draw(tile->pawn);
186 	if (!fast)
187 		SDL_UpdateRect(screen, tile->coord_x, tile->coord_y, 50, 50);
188 }
189 
tile_draw(Tile * tile)190 void tile_draw (Tile *tile)
191 {
192 	tile_draw_fast(tile, !is_playing());
193 }
194 
tile_is_forward(Tile * tile,Tile * third)195 static int tile_is_forward(Tile *tile, Tile *third)
196 {
197 	if (!tile->pawn || third->pawn)
198 		return FALSE;
199 
200 	if (game_num_players() > 2) {
201 		switch (tile->pawn->color) {
202 		case PAWN_ORANGE:
203 			return (third->pos_y >= tile->pos_y);
204 		case PAWN_BLUE:
205 			return (third->pos_x <= tile->pos_x);
206 		case PAWN_RED:
207 			return (third->pos_y <= tile->pos_y);
208 		case PAWN_GREEN:
209 			return (third->pos_x >= tile->pos_x);
210 		default:
211 			assert(FALSE);
212 		}
213 	} else {
214 		switch (tile->pawn->color) {
215 		case PAWN_ORANGE:
216 			return (third->pos_y >= tile->pos_y);
217 		case PAWN_BLUE:
218 			return (third->pos_y <= tile->pos_y);
219 		default:
220 			assert(FALSE);
221 		}
222 	}
223 	return FALSE;
224 }
225 
tile_get_accessible_surroundings(Tile * tile)226 LList *tile_get_accessible_surroundings (Tile *tile)
227 {
228 	LList *results = NULL;
229 	int ox, x = tile->pos_x - 1;
230 	int oy, y = tile->pos_y - 1;
231 
232 	if (x < 0)
233 		x = 0;
234 	if (y < 0)
235 		y = 0;
236 
237 	ox = x; oy = y;
238 #ifdef DEBUG
239 	printf("getting surroundings from %d, %d to %d, %d\n",
240 			x, y, min(tile->pos_x + 1, MAX_TILES_X),
241 			min(tile->pos_y + 1, MAX_TILES_Y));
242 #endif
243 	for (x = ox; x <= min(tile->pos_x + 1, MAX_TILES_X - 1); x++) {
244 		for (y = oy; y <= min(tile->pos_y + 1, MAX_TILES_Y - 1); y++) {
245 			Tile *ctile = tile_get(x, y);
246 #ifdef DEBUG
247 			printf("tile_get_accessible_surroundings: tile %d,%d (%s): has_pawn %d.\n",
248 					ctile->pos_x, ctile->pos_y,
249 					tile_type_str(ctile->type),
250 					ctile->pawn != NULL);
251 #endif
252 			if (ctile == tile)
253 				continue;
254 			if (ctile->type == TILE_NO_TILE   ||
255 			    ctile->type == TILE_UP_LEFT   ||
256 			    ctile->type == TILE_UP_RIGHT  ||
257 			    ctile->type == TILE_DOWN_LEFT ||
258 			    ctile->type == TILE_DOWN_RIGHT)
259 				continue;
260 
261 			if (ctile->pawn == NULL) {
262 				if (x == 4 && y == 4 && !can_move_on_center())
263 					continue;
264 				results = llist_append(results, ctile);
265 			} else {
266 				Tile *third = tile_get_next_in_row(tile, ctile);
267 
268 				if (third == NULL || third->pawn != NULL)
269 					continue;
270 
271 				if (third->type == TILE_NO_TILE   ||
272 				    third->type == TILE_UP_LEFT   ||
273 				    third->type == TILE_UP_RIGHT  ||
274 				    third->type == TILE_DOWN_LEFT ||
275 				    third->type == TILE_DOWN_RIGHT)
276 					continue;
277 
278 				/* can't jump backward */
279 				if (!tile_is_forward(tile, third))
280 					continue;
281 
282 				/* can't jump to center */
283 				if (third->type == TILE_CENTER)
284 					continue;
285 
286 				/* can't jump from center either */
287 				if (tile->type == TILE_CENTER)
288 					continue;
289 
290 				results = llist_append(results, ctile); /* just for repaint */
291 				results = llist_append(results, third);
292 			}
293 		}
294 	}
295 
296 	return results;
297 }
298 
tile_highlight(Tile * tile,int value)299 void tile_highlight(Tile *tile, int value)
300 {
301 	static SDL_Surface *highlighter = NULL;
302 
303 	if (!highlighter) {
304 		highlighter = biloba_load_image("highlighter.png");
305 		assert(highlighter != NULL);
306 	}
307 	SDL_SetAlpha(highlighter, SDL_SRCALPHA, value);
308 
309 	assert(tile != NULL);
310 
311 	tile_draw_fast(tile, TRUE);
312 
313 	if (value)
314 		put_image(highlighter,
315 				tile->coord_x,
316 				tile->coord_y);
317 
318 	if (is_playing()) {
319 		SDL_UpdateRect(screen, tile->coord_x,
320 				tile->coord_y, 50, 50);
321 	}
322 }
323 
tile_draw_arrow(Tile * tile,Tile * to)324 void tile_draw_arrow(Tile *tile, Tile *to)
325 {
326 	int start_x = tile->coord_x + 25;
327 	int start_y = tile->coord_y + 25;
328 	int offset_x = -10;
329 	int offset_y = -10;
330 	int left_to_right = 0;
331 	int up_to_down = 0;
332 	ArrowType arrow_type = -1;
333 	if (tile->coord_x < to->coord_x) {
334 		offset_x = +ARROW_OFFSET_FROM_CENTER;
335 		left_to_right = +1;
336 	}
337 	if (tile->coord_x > to->coord_x) {
338 		offset_x = -ARROW_OFFSET_FROM_CENTER - 20;
339 		left_to_right = -1;
340 	}
341 	if (tile->coord_y < to->coord_y) {
342 		offset_y = +ARROW_OFFSET_FROM_CENTER;
343 		up_to_down = -1;
344 	}
345 	if (tile->coord_y > to->coord_y) {
346 		offset_y = -ARROW_OFFSET_FROM_CENTER - 20;
347 		up_to_down = +1;
348 	}
349 
350 	switch (left_to_right) {
351 	case +1: /* -> */
352 		switch(up_to_down) {
353 			case +1: /* ^ */
354 				arrow_type = ARROW_UP_RIGHT;
355 				break;
356 			case 0: /* . */
357 				arrow_type = ARROW_RIGHT;
358 				break;
359 			case -1: /* v */
360 				arrow_type = ARROW_DOWN_RIGHT;
361 				break;
362 		}
363 		break;
364 	case 0:  /* . */
365 		switch(up_to_down) {
366 			case +1: /* ^ */
367 				arrow_type = ARROW_UP;
368 				break;
369 			case 0: /* . */
370 				assert(FALSE);
371 				break;
372 			case -1: /* v */
373 				arrow_type = ARROW_DOWN;
374 				break;
375 		}
376 		break;
377 	case -1: /* <- */
378 		switch(up_to_down) {
379 			case +1: /* ^ */
380 				arrow_type = ARROW_UP_LEFT;
381 				break;
382 			case 0: /* . */
383 				arrow_type = ARROW_LEFT;
384 				break;
385 			case -1: /* v */
386 				arrow_type = ARROW_DOWN_LEFT;
387 				break;
388 		}
389 		break;
390 	}
391 	assert(arrow_type != -1);
392 
393 	arrow_draw(arrow_type, start_x + offset_x, start_y + offset_y);
394 }
395 
tile_get_next_in_row(Tile * start,Tile * second)396 Tile *tile_get_next_in_row(Tile *start, Tile *second)
397 {
398 	int diff_x = second->pos_x - start->pos_x;
399 	int diff_y = second->pos_y - start->pos_y;
400 	int third_x = second->pos_x + diff_x;
401 	int third_y = second->pos_y + diff_y;
402 
403 	if (third_x < 0 || third_x >= MAX_TILES_X)
404 		return NULL;
405 	if (third_y < 0 || third_y >= MAX_TILES_Y)
406 		return NULL;
407 	return tile_get(third_x, third_y);
408 }
409 
tile_is_near_center(Tile * tile)410 int tile_is_near_center(Tile *tile)
411 {
412 	LList *surr = tile_get_accessible_surroundings(tile);
413 	LList *cur = surr;
414 	while (cur) {
415 		Tile *t = (Tile *)cur->data;
416 		if (t->type == TILE_CENTER) {
417 			llist_free(surr);
418 			return TRUE;
419 		}
420 		cur = cur->next;
421 	}
422 	llist_free(surr);
423 	return FALSE;
424 }
425