1 
2 /*
3  *   Copyright (c) 2004-2010 Arthur Huillet
4  *   Copyright (c) 2004 Johannes Prix
5  *
6  *
7  *  This file is part of Freedroid
8  *
9  *  Freedroid 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  *  Freedroid 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 Freedroid; see the file COPYING. If not, write to the
21  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  *  MA  02111-1307  USA
23  */
24 
25 /**
26  * This file contains everything that has got to do with the automap.
27  */
28 
29 #include "system.h"
30 
31 #include "defs.h"
32 #include "struct.h"
33 #include "global.h"
34 #include "proto.h"
35 #include "map.h"
36 
37 int AUTOMAP_TEXTURE_WIDTH = 2048;
38 int AUTOMAP_TEXTURE_HEIGHT = 1024;
39 
40 #define AUTOMAP_SQUARE_SIZE 3
41 #define WALL_COLOR 0x00, 0xFF, 0xFF
42 #define EXIT_COLOR 0xFF, 0xFF, 0x00
43 #define TUX_COLOR 0x00, 0x00, 0xFF
44 #define FRIEND_COLOR 0x00, 0xFF, 0x00
45 #define ENEMY_COLOR 0xFF, 0x00, 0x00
46 
47 static struct image compass;
48 
49 /**
50  * This function clears out the Automap data.
51  */
ClearAutomapData(void)52 void ClearAutomapData(void)
53 {
54 	memset(Me.Automap, 0, MAX_LEVELS * 100 * 100);
55 
56 };				// void ClearAutomapData ( void )
57 
58 /**
59  * This function does a special rounding that is used by the automap
60  * to compute the start and end position of an obstacle on the map.
61  * Its key property is that it preserves the distance between the two
62  * numbers, which is important in order for a 1 tile wall to appear
63  * as occupying only one tile on the automap regardless of its exact position.
64  */
round_automap_pos(float a,float b,int * ra,int * rb)65 static void round_automap_pos(float a, float b, int *ra, int *rb)
66 {
67 	float delta;
68 	int dist;
69 #define AUTOMAP_COLLRECT_SHRINK 0.9
70 
71 	if (a > b) {
72 		float c = a;
73 		a = b;
74 		b = c;
75 	}
76 
77 	delta = (b - a) * AUTOMAP_COLLRECT_SHRINK;
78 	dist = floor(delta);
79 
80 	*ra = lrint(a);
81 	*rb = *ra + dist;
82 }
83 
84 /**
85  * This function first erases the automap data on a square,
86  * then it tags each square touching it to redraw the automap.
87  *
88  */
update_automap_square(int z,int x,int y)89 static void update_automap_square(int z, int x, int y)
90 {
91 	int a, b;
92 	level *automap_level = curShip.AllLevels[z];
93 
94 	Me.Automap[z][y][x] &= ~UPDATE_SQUARE_BIT;
95 	Me.Automap[z][y][x] &= ~EW_WALL_BIT;
96 	Me.Automap[z][y][x] &= ~NS_WALL_BIT;
97 
98 	for (a = x - 1 ; a <=  x + 1; a++) {
99 		if ((a < 0) || (a >= automap_level->xlen))
100 			continue;
101 
102 		for (b = y - 1; b <=  y + 1; b++) {
103 			if ((b < 0) || (b >= automap_level->ylen))
104 				continue;
105 
106 			Me.Automap[z][b][a] &= ~SQUARE_SEEN_AT_ALL_BIT;
107 		}
108 	}
109 }
110 
111 /**
112  * This function collects the automap data and stores it in the Me data
113  * structure.
114  */
CollectAutomapData(void)115 void CollectAutomapData(void)
116 {
117 	int x, y;
118 	int start_x, start_y, end_x, end_y;
119 	static int TimePassed;
120 	Level automap_level = curShip.AllLevels[Me.pos.z];
121 	int i;
122 	obstacle *our_obstacle;
123 	int lvl = Me.pos.z;
124 
125 	// Checking the whole map for passability will surely take a lot
126 	// of computation.  Therefore we only do this once every second of
127 	// real time.
128 	//
129 	if (TimePassed == (int)Me.MissionTimeElapsed)
130 		return;
131 	TimePassed = (int)Me.MissionTimeElapsed;
132 
133 	// DebugPrintf ( -3 , "\nCollecting Automap data... " );
134 
135 	// Also if there is no map-maker present in inventory, then we need not
136 	// do a thing here...
137 	//
138 	if (!Me.map_maker_is_present)
139 		return;
140 
141 	// Earlier we had
142 	//
143 	// start_x = 0 ; start_y = 0 ; end_x = automap_level->xlen ; end_y = automap_level->ylen ;
144 	//
145 	// when maximal automap was generated.  Now we only add to the automap what really is on screen...
146 	//
147 	start_x = Me.pos.x - FLOOR_TILES_VISIBLE_AROUND_TUX;
148 	end_x = Me.pos.x + FLOOR_TILES_VISIBLE_AROUND_TUX;
149 	start_y = Me.pos.y - FLOOR_TILES_VISIBLE_AROUND_TUX;
150 	end_y = Me.pos.y + FLOOR_TILES_VISIBLE_AROUND_TUX;
151 
152 	if (start_x < 0)
153 		start_x = 0;
154 	if (end_x >= automap_level->xlen)
155 		end_x = automap_level->xlen;
156 	if (start_y < 0)
157 		start_y = 0;
158 	if (end_y >= automap_level->ylen)
159 		end_y = automap_level->ylen;
160 
161 	int tstamp = next_glue_timestamp();
162 
163 	// Now we do the actual checking for visible wall components.
164 	//
165 	for (y = start_y; y < end_y; y++) {
166 		for (x = start_x; x < end_x; x++) {
167 			if (Me.Automap[lvl][y][x] & UPDATE_SQUARE_BIT)
168 				update_automap_square(lvl, x, y);
169 
170 			if (Me.Automap[lvl][y][x] & SQUARE_SEEN_AT_ALL_BIT)
171 				continue;
172 
173 			for (i = 0; i < automap_level->map[y][x].glued_obstacles.size; i++) {
174 				int idx = ((int *)(automap_level->map[y][x].glued_obstacles.arr))[i];
175 
176 				our_obstacle = &(automap_level->obstacle_list[idx]);
177 
178 				if (our_obstacle->timestamp == tstamp)
179 					continue;
180 
181 				our_obstacle->timestamp = tstamp;
182 
183 				if ((our_obstacle->type >= ISO_H_DOOR_000_OPEN) && (our_obstacle->type <= ISO_V_DOOR_100_OPEN))
184 					continue;
185 				if ((our_obstacle->type >= ISO_DH_DOOR_000_OPEN) && (our_obstacle->type <= ISO_DV_DOOR_100_OPEN))
186 					continue;
187 				if ((our_obstacle->type >= ISO_OUTER_DOOR_V_00) && (our_obstacle->type <= ISO_OUTER_DOOR_H_100))
188 					continue;
189 
190 				int obstacle_start_x;
191 				int obstacle_end_x;
192 				int obstacle_start_y;
193 				int obstacle_end_y;
194 
195 				obstacle_spec *obstacle_spec = get_obstacle_spec(our_obstacle->type);
196 				round_automap_pos(our_obstacle->pos.x + obstacle_spec->left_border, our_obstacle->pos.x + obstacle_spec->right_border,
197 						&obstacle_start_x, &obstacle_end_x);
198 
199 				round_automap_pos(our_obstacle->pos.y + obstacle_spec->upper_border, our_obstacle->pos.y + obstacle_spec->lower_border,
200 						&obstacle_start_y, &obstacle_end_y);
201 
202 				if (obstacle_start_x < 0)
203 					obstacle_start_x = 0;
204 				if (obstacle_start_y < 0)
205 					obstacle_start_y = 0;
206 				if (obstacle_end_x < 0)
207 					obstacle_end_x = 0;
208 				if (obstacle_end_y < 0)
209 					obstacle_end_y = 0;
210 				if (obstacle_start_x >= automap_level->xlen)
211 					obstacle_start_x = automap_level->xlen - 1;
212 				if (obstacle_start_y >= automap_level->ylen)
213 					obstacle_start_y = automap_level->ylen - 1;
214 				if (obstacle_end_x >= automap_level->xlen)
215 					obstacle_end_x = automap_level->xlen - 1;
216 				if (obstacle_end_y >= automap_level->ylen)
217 					obstacle_end_y = automap_level->ylen - 1;
218 
219 				//printf("pos %f %f - border %f %f to %f %f - xstart %d xend %d ystart %d yend %d\n", our_obstacle->pos.x, our_obstacle->pos.y, our_obstacle->pos.x + obstacle_map [ our_obstacle -> type ] . left_border, our_obstacle->pos.y + obstacle_map [ our_obstacle -> type ] . upper_border, our_obstacle->pos.x + obstacle_map [ our_obstacle -> type ] . right_border, our_obstacle->pos.y + obstacle_map [ our_obstacle -> type ] . lower_border, obstacle_start_x, obstacle_end_x, obstacle_start_y, obstacle_end_y);
220 
221 				int a, b;
222 
223 				for (a = obstacle_start_x; a <=  obstacle_end_x; a++) {
224 					for (b = obstacle_start_y; b <=  obstacle_end_y; b++) {
225 
226 						if (obstacle_spec->block_area_type == COLLISION_TYPE_RECTANGLE) {
227 
228 							if (obstacle_spec->block_area_parm_1 > 0.80) {
229 									Me.Automap[lvl][b][a] |= EW_WALL_BIT;
230 							}
231 							if (obstacle_spec->block_area_parm_2 > 0.80) {
232 								Me.Automap[lvl][b][a] |= NS_WALL_BIT;
233 							}
234 						}
235 					}
236 				}
237 			}
238 			if (visible_event_at_location(x, y, lvl))
239 				Me.Automap[lvl][y][x] |= VISIBLE_EVENT_BIT;
240 
241 			Me.Automap[lvl][y][x] = Me.Automap[lvl][y][x] | SQUARE_SEEN_AT_ALL_BIT;
242 		}
243 	}
244 
245 };				// void CollectAutomapData ( void )
246 
247 /**
248  * Sets all of the squares overlapped as not seen, so they can
249  * be updated.
250  */
update_obstacle_automap(int z,obstacle * our_obstacle)251 void update_obstacle_automap(int z, obstacle *our_obstacle)
252 {
253 	level *automap_level = curShip.AllLevels[z];
254 	int obstacle_start_x;
255 	int obstacle_end_x;
256 	int obstacle_start_y;
257 	int obstacle_end_y;
258 
259 	obstacle_spec *obstacle_spec = get_obstacle_spec(our_obstacle->type);
260 	round_automap_pos(our_obstacle->pos.x + obstacle_spec->left_border,
261 			our_obstacle->pos.x + obstacle_spec->right_border, &obstacle_start_x, &obstacle_end_x);
262 
263 	round_automap_pos(our_obstacle->pos.y + obstacle_spec->upper_border,
264 			our_obstacle->pos.y + obstacle_spec->lower_border, &obstacle_start_y, &obstacle_end_y);
265 
266 	if (obstacle_start_x < 0)
267 		obstacle_start_x = 0;
268 	if (obstacle_start_y < 0)
269 		obstacle_start_y = 0;
270 	if (obstacle_end_x < 0)
271 		obstacle_end_x = 0;
272 	if (obstacle_end_y < 0)
273 		obstacle_end_y = 0;
274 	if (obstacle_start_x >= automap_level->xlen)
275 		obstacle_start_x = automap_level->xlen - 1;
276 	if (obstacle_start_y >= automap_level->ylen)
277 		obstacle_start_y = automap_level->ylen - 1;
278 	if (obstacle_end_x >= automap_level->xlen)
279 		obstacle_end_x = automap_level->xlen - 1;
280 	if (obstacle_end_y >= automap_level->ylen)
281 		obstacle_end_y = automap_level->ylen - 1;
282 
283 	int x, y;
284 	for (x = obstacle_start_x; x <=  obstacle_end_x; x++) {
285 		for (y = obstacle_start_y; y <=  obstacle_end_y; y++) {
286 			Me.Automap[z][y][x] |= UPDATE_SQUARE_BIT;
287 		}
288 	}
289 }
290 
291 /**
292  * Use GL_POINTS primitive in OpenGL mode to render automap points for better performance.
293  * In SDL mode, fallback to PutPixel.
294  */
PutPixel_automap_wrapper(SDL_Surface * abc,int x,int y,int r,int g,int b)295 static inline void PutPixel_automap_wrapper(SDL_Surface * abc, int x, int y, int r, int g, int b)
296 {
297 	if (!use_open_gl)
298 		sdl_put_pixel(abc, x, y, r, g, b, 255);
299 #ifdef HAVE_LIBGL
300 	else {
301 		glColor3ub(r, g, b);
302 		glVertex2i(x, y);
303 		glColor3ub(255, 255, 255);
304 	}
305 #endif
306 }
307 
308 /**
309  * Toggle automap visibility
310  */
toggle_automap(void)311 void toggle_automap(void)
312 {
313 	GameConfig.Automap_Visible = !GameConfig.Automap_Visible;
314 
315 	if (!Me.map_maker_is_present) {
316 		if (GameConfig.Automap_Visible)
317 			append_new_game_message(_("Compass on. You need a Map Maker to enable the minimap."));
318 		else
319 			append_new_game_message(_("Compass off."));
320 	}
321 }
322 
display_automap_compass()323 static void display_automap_compass()
324 {
325 	//load the compass if necessary
326 	if (!image_loaded(&compass)) {
327 		load_image(&compass, "compass.png", NO_MOD);
328 	}
329 
330 	display_image_on_screen(&compass, GameConfig.screen_width - compass.w - 10, 40, IMAGE_NO_TRANSFO);
331 }
332 
333 /**
334  *
335  * This function should display the automap.
336  *
337  * In this case the function only uses pixel operations with the screen.
338  * This is the method of choice when using SDL for graphics output.
339  * For the OpenGL case, there is a completely different function...
340  *
341  */
display_automap(void)342 void display_automap(void)
343 {
344 	int x, y, i, j;
345 	Level automap_level = curShip.AllLevels[Me.pos.z];
346 	int lvl = Me.pos.z;
347 
348 	// Of course we only display the automap on demand of the user...
349 	//
350 	if (GameConfig.Automap_Visible == FALSE)
351 		return;
352 
353 	// Display the compass
354 	if (game_status != INSIDE_LVLEDITOR)
355 		display_automap_compass();
356 
357 	// Also if there is no map-maker present in inventory, then we need not
358 	// do a thing here...
359 	//
360 	if (!Me.map_maker_is_present)
361 		return;
362 
363 #ifdef HAVE_LIBGL
364 	if (use_open_gl) {
365 		glDisable(GL_TEXTURE_2D);
366 		glBegin(GL_POINTS);
367 	}
368 #endif
369 
370 	// At first, we only blit the known data about the pure wall-type
371 	// obstacles on this level.
372 	for (y = 0; y < automap_level->ylen; y++) {
373 		for (x = 0; x < automap_level->xlen; x++) {
374 			if (Me.Automap[lvl][y][x] & EW_WALL_BIT) {
375 				PutPixel_automap_wrapper(Screen,
376 							 1 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * (automap_level->ylen - y),
377 							 1 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * y, WALL_COLOR);
378 				PutPixel_automap_wrapper(Screen,
379 							 2 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * (automap_level->ylen - y),
380 							 2 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * y, WALL_COLOR);
381 			}
382 
383 			if (Me.Automap[lvl][y][x] & NS_WALL_BIT) {
384 				PutPixel_automap_wrapper(Screen,
385 							 1 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * (automap_level->ylen - y),
386 							 2 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * y, WALL_COLOR);
387 				PutPixel_automap_wrapper(Screen,
388 							 2 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * (automap_level->ylen - y),
389 							 1 + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * y, WALL_COLOR);
390 			}
391 
392 			//Now we blit known event triggers
393 			if (Me.Automap[lvl][y][x] & VISIBLE_EVENT_BIT) {
394 				for (i = 0; i < AUTOMAP_SQUARE_SIZE; i++) {
395 					for (j = 0; j < AUTOMAP_SQUARE_SIZE; j++) {
396 						PutPixel_automap_wrapper(Screen,
397 							i + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * (automap_level->ylen - y),
398 							j + AUTOMAP_SQUARE_SIZE * x + AUTOMAP_SQUARE_SIZE * y, EXIT_COLOR);
399 					}
400 				}
401 			}
402 		}
403 	}
404 
405 	// Display enemies
406 	enemy *erot;
407 	BROWSE_LEVEL_BOTS(erot, automap_level->levelnum) {
408 		if (erot->type == (-1))
409 			continue;
410 
411 		for (x = 0; x < AUTOMAP_SQUARE_SIZE; x++) {
412 			for (y = 0; y < AUTOMAP_SQUARE_SIZE; y++) {
413 				if (is_friendly(erot->faction, FACTION_SELF)) {
414 					PutPixel_automap_wrapper(Screen,
415 								 AUTOMAP_SQUARE_SIZE * erot->pos.x +
416 								 AUTOMAP_SQUARE_SIZE * (automap_level->ylen - erot->pos.y) + x,
417 								 AUTOMAP_SQUARE_SIZE * erot->pos.x + AUTOMAP_SQUARE_SIZE * erot->pos.y + y,
418 								 FRIEND_COLOR);
419 				} else if (Me.nmap_duration > 0) {
420 					PutPixel_automap_wrapper(Screen,
421 								 AUTOMAP_SQUARE_SIZE * erot->pos.x +
422 								 AUTOMAP_SQUARE_SIZE * (automap_level->ylen - erot->pos.y) + x,
423 								 AUTOMAP_SQUARE_SIZE * erot->pos.x + AUTOMAP_SQUARE_SIZE * erot->pos.y + y,
424 								 ENEMY_COLOR);
425 				}
426 			}
427 		}
428 	}
429 
430 	// Display tux
431 	for (x = 0; x < AUTOMAP_SQUARE_SIZE; x++) {
432 		for (y = 0; y < AUTOMAP_SQUARE_SIZE; y++) {
433 			PutPixel_automap_wrapper(Screen,
434 						 AUTOMAP_SQUARE_SIZE * Me.pos.x + AUTOMAP_SQUARE_SIZE * (automap_level->ylen - Me.pos.y) +
435 						 x, AUTOMAP_SQUARE_SIZE * Me.pos.x + AUTOMAP_SQUARE_SIZE * Me.pos.y + y, TUX_COLOR);
436 		}
437 	}
438 
439 #ifdef HAVE_LIBGL
440 	if (use_open_gl) {
441 		glEnd();
442 		glEnable(GL_TEXTURE_2D);
443 	}
444 #endif
445 
446 };				// void show_automap_data_sdl ( void )
447