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