1 /*
2
3 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 and the "Aleph One" developers.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 This license is contained in the file "COPYING",
17 which is included with this source code; it is available online at
18 http://www.gnu.org/licenses/gpl.html
19
20 Overhead-Map Base-Class Renderer,
21 by Loren Petrich,
22 August 3, 2000
23
24 The code had been moved out of overhead_map.c and overhead_map_macintosh.c;
25 all of it is cross-platform except for the font-handling code.
26 */
27
28
29 #include "cseries.h"
30
31 #include "OverheadMapRenderer.h"
32 #include "flood_map.h"
33 #include "media.h"
34 #include "platforms.h"
35 #include "player.h"
36 #include "render.h"
37
38 #include <string.h>
39 #include <stdlib.h>
40 #include <limits.h>
41
42
43 enum /* render flags */
44 {
45 _endpoint_on_automap= 0x2000,
46 _line_on_automap= 0x4000,
47 _polygon_on_automap= 0x8000
48 };
49
50 /* ---------- macros */
51
52 #define WORLD_TO_SCREEN_SCALE_ONE 8
53 #define WORLD_TO_SCREEN(x, x0, scale) (((x)-(x0))>>(WORLD_TO_SCREEN_SCALE_ONE-(scale)))
54
55
56 // Externals:
57 // Changed to link properly with code in pathfinding.c
58 extern world_point2d *path_peek(short path_index, short *step_count);
59 extern short GetNumberOfPaths();
60
61
62 // Main rendering routine
63
Render(overhead_map_data & Control)64 void OverheadMapClass::Render(overhead_map_data& Control)
65 {
66 world_distance x0= Control.origin.x, y0= Control.origin.y;
67 int xoff= Control.left + Control.half_width, yoff= Control.top + Control.half_height;
68 short scale= Control.scale;
69 world_point2d location;
70 short i;
71
72 // LP addition: overall setup
73 begin_overall();
74
75 // LP addition: stuff for setting the game options, since they get defaulted to 0
76 // Made compatible with map cheat
77 assert(ConfigPtr);
78 if (ConfigPtr->ShowAliens) GET_GAME_OPTIONS() |= _overhead_map_shows_monsters;
79 if (ConfigPtr->ShowItems) GET_GAME_OPTIONS() |= _overhead_map_shows_items;
80 if (ConfigPtr->ShowProjectiles) GET_GAME_OPTIONS() |= _overhead_map_shows_projectiles;
81
82 if (Control.mode==_rendering_checkpoint_map) generate_false_automap(Control.origin_polygon_index);
83
84 transform_endpoints_for_overhead_map(Control);
85
86 // LP addition
87 begin_polygons();
88
89 /* shade all visible polygons */
90 for (i=0;i<dynamic_world->polygon_count;++i)
91 {
92 struct polygon_data *polygon= get_polygon_data(i);
93 if (POLYGON_IS_IN_AUTOMAP(i) && TEST_STATE_FLAG(i, _polygon_on_automap)
94 &&(polygon->floor_transfer_mode!=_xfer_landscape||polygon->ceiling_transfer_mode!=_xfer_landscape))
95 {
96
97 if (!POLYGON_IS_DETACHED(polygon))
98 {
99 short color;
100
101 switch (polygon->type)
102 {
103 case _polygon_is_platform:
104 color= PLATFORM_IS_SECRET(get_platform_data(polygon->permutation)) ?
105 _polygon_color : _polygon_platform_color;
106 if (PLATFORM_IS_FLOODED(get_platform_data(polygon->permutation)))
107 {
108 short adj_index = find_flooding_polygon(i);
109 if (adj_index != NONE)
110 {
111 switch (get_polygon_data(adj_index)->type)
112 {
113 case _polygon_is_minor_ouch:
114 color = _polygon_minor_ouch_color;
115 break;
116 case _polygon_is_major_ouch:
117 color = _polygon_major_ouch_color;
118 break;
119 }
120 }
121 }
122 break;
123
124 case _polygon_is_minor_ouch:
125 color = _polygon_minor_ouch_color;
126 break;
127
128 case _polygon_is_major_ouch:
129 color = _polygon_major_ouch_color;
130 break;
131
132 case _polygon_is_teleporter:
133 color = _polygon_teleporter_color;
134 break;
135
136 case _polygon_is_hill:
137 color = _polygon_hill_color;
138 break;
139
140 default:
141 color= _polygon_color;
142 break;
143 }
144
145 if (polygon->media_index!=NONE)
146 {
147 struct media_data *media= get_media_data(polygon->media_index);
148
149 // LP change: idiot-proofing
150 if (media)
151 {
152 if (media->height>=polygon->floor_height)
153 {
154 switch (media->type)
155 {
156 case _media_water: color= _polygon_water_color; break;
157 case _media_lava: color= _polygon_lava_color; break;
158 case _media_goo: color= _polygon_goo_color; break;
159 // LP change: separated sewage and JjaroGoo
160 case _media_sewage: color= _polygon_sewage_color; break;
161 case _media_jjaro: color = _polygon_jjaro_color; break;
162 }
163 }
164 }
165 }
166
167 draw_polygon(polygon->vertex_count, polygon->endpoint_indexes, color, scale);
168 }
169 }
170 }
171
172 // LP addition
173 end_polygons();
174
175 // LP addition
176 begin_lines();
177
178 /* draw all visible lines */
179 for (i=0;i<dynamic_world->line_count;++i)
180 {
181 short line_color= NONE;
182 struct line_data *line= get_line_data(i);
183
184 if (LINE_IS_IN_AUTOMAP(i))
185 {
186 if ((line->clockwise_polygon_owner!=NONE && TEST_STATE_FLAG(line->clockwise_polygon_owner, _polygon_on_automap)) ||
187 (line->counterclockwise_polygon_owner!=NONE && TEST_STATE_FLAG(line->counterclockwise_polygon_owner, _polygon_on_automap)))
188 {
189 struct polygon_data *clockwise_polygon= line->clockwise_polygon_owner==NONE ? NULL : get_polygon_data(line->clockwise_polygon_owner);
190 struct polygon_data *counterclockwise_polygon= line->counterclockwise_polygon_owner==NONE ? NULL : get_polygon_data(line->counterclockwise_polygon_owner);
191
192 if (LINE_IS_SOLID(line) || LINE_IS_VARIABLE_ELEVATION(line))
193 {
194 if (LINE_IS_LANDSCAPED(line))
195 {
196 if ((!clockwise_polygon||clockwise_polygon->floor_transfer_mode!=_xfer_landscape) &&
197 (!counterclockwise_polygon||counterclockwise_polygon->floor_transfer_mode!=_xfer_landscape))
198 {
199 line_color= _elevation_line_color;
200 }
201 }
202 else
203 {
204 line_color= _solid_line_color;
205 }
206 }
207 else
208 {
209 if (clockwise_polygon->floor_height!=counterclockwise_polygon->floor_height)
210 {
211 line_color= LINE_IS_LANDSCAPED(line) ? NONE : static_cast<short>(_elevation_line_color);
212 }
213 }
214 }
215
216 if (line_color!=NONE) draw_line(i, line_color, scale);
217 }
218 }
219
220 // LP addition
221 end_lines();
222
223 /* print all visible tags */
224 if (scale!=OVERHEAD_MAP_MINIMUM_SCALE)
225 {
226 struct map_annotation *annotation;
227
228 i= 0;
229 while ((annotation= get_next_map_annotation(&i))!=NULL)
230 {
231 if (POLYGON_IS_IN_AUTOMAP(annotation->polygon_index) &&
232 TEST_STATE_FLAG(annotation->polygon_index, _polygon_on_automap))
233 {
234 location.x= xoff + WORLD_TO_SCREEN(annotation->location.x, x0, scale);
235 location.y= yoff + WORLD_TO_SCREEN(annotation->location.y, y0, scale);
236
237 draw_annotation(&location, annotation->type, annotation->text, scale);
238 }
239 }
240 }
241
242 if (ConfigPtr->ShowPaths)
243 {
244 short path_index;
245
246 // LP change: made this more general
247 set_path_drawing();
248
249 // LP change: there may be more than 20 paths
250 for (path_index=0;path_index<GetNumberOfPaths();path_index++)
251 {
252 world_point2d *points;
253 short step, count;
254
255 points= path_peek(path_index, &count);
256 if (points)
257 {
258 for (step= 0; step<count; ++step)
259 {
260 location.x= xoff + WORLD_TO_SCREEN(points[step].x, x0, scale);
261 location.y= yoff + WORLD_TO_SCREEN(points[step].y, y0, scale);
262 // LP change: made this more general
263 draw_path(step,location);
264 }
265 }
266 // LP addition: indicate when a path is complete
267 finish_path();
268 }
269 }
270
271 if (Control.mode!=_rendering_checkpoint_map)
272 {
273 struct object_data *object;
274
275 for (i=0, object= objects; i<MAXIMUM_OBJECTS_PER_MAP; ++i, ++object)
276 {
277 if (SLOT_IS_USED(object))
278 {
279 if (!OBJECT_IS_INVISIBLE(object))
280 {
281 short thing_type= NONE;
282
283 switch (GET_OBJECT_OWNER(object))
284 {
285 case _object_is_monster:
286 {
287 struct monster_data *monster= get_monster_data(object->permutation);
288
289 if (MONSTER_IS_PLAYER(monster))
290 {
291 struct player_data *player= get_player_data(monster_index_to_player_index(object->permutation));
292
293 if ((GET_GAME_OPTIONS()&_overhead_map_is_omniscient) || local_player->team==player->team)
294 {
295 location.x= xoff + WORLD_TO_SCREEN(object->location.x, x0, scale);
296 location.y= yoff + WORLD_TO_SCREEN(object->location.y, y0, scale);
297
298 draw_player(&location, object->facing, player->team, scale);
299 }
300 }
301 else
302 {
303 // LP: use the lookup system
304 switch (ConfigPtr->monster_displays[monster->type])
305 {
306 case _civilian_thing:
307 thing_type= _civilian_thing;
308 break;
309
310 case _monster_thing:
311 if (GET_GAME_OPTIONS()&_overhead_map_shows_monsters)
312 thing_type= _monster_thing;
313 break;
314 }
315 }
316 break;
317 }
318
319 case _object_is_projectile:
320 if ((GET_GAME_OPTIONS()&_overhead_map_shows_projectiles) && object->shape!=UNONE)
321 {
322 thing_type= _projectile_thing;
323 }
324 break;
325
326 case _object_is_item:
327 if (GET_GAME_OPTIONS()&_overhead_map_shows_items)
328 {
329 thing_type= _item_thing;
330 }
331 break;
332
333 case _object_is_garbage:
334 // LP change: making this more general
335 switch (ConfigPtr->dead_monster_displays[GET_COLLECTION(GET_DESCRIPTOR_COLLECTION(object->shape))])
336 {
337 case _civilian_thing:
338 thing_type= _civilian_thing;
339 break;
340
341 case _monster_thing:
342 if (GET_GAME_OPTIONS()&_overhead_map_shows_monsters)
343 thing_type= _monster_thing;
344 break;
345 }
346 /*
347 if (GET_COLLECTION(GET_DESCRIPTOR_COLLECTION(object->shape))==_collection_civilian)
348 {
349 thing_type= _civilian_thing;
350 }
351 */
352 break;
353 }
354
355 if (thing_type!=NONE)
356 {
357 // Making this more general, in case we want to see monsters and stuff
358 if (thing_type==_projectile_thing || ((dynamic_world->tick_count+i)&8))
359 // if (thing_type!=_civilian_thing || ((dynamic_world->tick_count+i)&8))
360 {
361 location.x= xoff + WORLD_TO_SCREEN(object->location.x, x0, scale);
362 location.y= yoff + WORLD_TO_SCREEN(object->location.y, y0, scale);
363
364 draw_thing(&location, object->facing, thing_type, scale);
365 }
366 }
367 }
368 }
369 }
370 }
371 else
372 {
373 for (i= 0; i<dynamic_world->initial_objects_count; ++i)
374 {
375 struct map_object *saved_object= saved_objects + i;
376
377 if (saved_object->type==_saved_goal &&
378 saved_object->location.x==Control.origin.x && saved_object->location.y==Control.origin.y)
379 {
380 location.x= xoff + WORLD_TO_SCREEN(saved_object->location.x, x0, scale);
381 location.y= yoff + WORLD_TO_SCREEN(saved_object->location.y, y0, scale);
382 draw_thing(&location, 0, _checkpoint_thing, scale);
383 }
384 }
385 }
386
387 if (Control.mode==_rendering_game_map) draw_map_name(Control, static_world->level_name);
388 if (Control.mode==_rendering_checkpoint_map) replace_real_automap();
389
390 // LP addition: overall cleanup
391 end_overall();
392 }
393
394
transform_endpoints_for_overhead_map(struct overhead_map_data & Control)395 void OverheadMapClass::transform_endpoints_for_overhead_map(
396 struct overhead_map_data& Control)
397 {
398 world_distance x0= Control.origin.x, y0= Control.origin.y;
399 int xoff= Control.left + Control.half_width, yoff = Control.top + Control.half_height;
400 short scale= Control.scale;
401 short i;
402
403 /* transform all our endpoints into screen space, remembering which ones are visible */
404 for (i=0;i<dynamic_world->endpoint_count;++i)
405 {
406 struct endpoint_data *endpoint= get_endpoint_data(i);
407
408 endpoint->transformed.x= xoff + WORLD_TO_SCREEN(endpoint->vertex.x, x0, scale);
409 endpoint->transformed.y= yoff + WORLD_TO_SCREEN(endpoint->vertex.y, y0, scale);
410
411 if (endpoint->transformed.x >= Control.left &&
412 endpoint->transformed.y >= Control.top &&
413 endpoint->transformed.y <= Control.top + Control.height &&
414 endpoint->transformed.x <= Control.left + Control.width)
415 {
416 SET_STATE_FLAG(i, _endpoint_on_automap, true);
417 }
418 }
419
420 /* sweep the polygon array, determining which polygons are visible based on their
421 endpoints */
422 for (i=0;i<dynamic_world->polygon_count;++i)
423 {
424 struct polygon_data *polygon= get_polygon_data(i);
425 short j;
426
427 for (j=0;j<polygon->vertex_count;++j)
428 {
429 if (TEST_STATE_FLAG(polygon->endpoint_indexes[j], _endpoint_on_automap))
430 {
431 SET_STATE_FLAG(i, _polygon_on_automap, true);
432 break;
433 }
434 }
435 }
436 }
437
438 /* --------- the false automap */
439
add_poly_to_false_automap(short polygon_index)440 static void add_poly_to_false_automap(short polygon_index)
441 {
442 struct polygon_data *polygon= get_polygon_data(polygon_index);
443 for (int i = 0; i < polygon->vertex_count; ++i)
444 ADD_LINE_TO_AUTOMAP(polygon->line_indexes[i]);
445 ADD_POLYGON_TO_AUTOMAP(polygon_index);
446 }
447
generate_false_automap(short polygon_index)448 void OverheadMapClass::generate_false_automap(
449 short polygon_index)
450 {
451 int32 automap_line_buffer_size, automap_polygon_buffer_size;
452
453 automap_line_buffer_size= (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0))*sizeof(byte);
454 automap_polygon_buffer_size= (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0))*sizeof(byte);
455
456 /* allocate memory for the old automap memory */
457 saved_automap_lines= new byte[automap_line_buffer_size];
458 saved_automap_polygons= new byte[automap_polygon_buffer_size];
459
460 if (saved_automap_lines && saved_automap_polygons)
461 {
462 memcpy(saved_automap_lines, automap_lines, automap_line_buffer_size);
463 memcpy(saved_automap_polygons, automap_polygons, automap_polygon_buffer_size);
464 memset(automap_lines, 0, automap_line_buffer_size);
465 memset(automap_polygons, 0, automap_polygon_buffer_size);
466
467 add_poly_to_false_automap(polygon_index);
468 polygon_index= flood_map(polygon_index, INT32_MAX, false_automap_cost_proc, _breadth_first, (void *) NULL);
469 do
470 {
471 polygon_index= flood_map(NONE, INT32_MAX, false_automap_cost_proc, _breadth_first, (void *) NULL);
472 }
473 while (polygon_index!=NONE);
474 }
475 }
476
477
replace_real_automap(void)478 void OverheadMapClass::replace_real_automap(
479 void)
480 {
481 if (saved_automap_lines)
482 {
483 int32 automap_line_buffer_size= (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0))*sizeof(byte);
484 memcpy(automap_lines, saved_automap_lines, automap_line_buffer_size);
485 delete []saved_automap_lines;
486 saved_automap_lines= (byte *) NULL;
487 }
488
489 if (saved_automap_polygons)
490 {
491 int32 automap_polygon_buffer_size= (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0))*sizeof(byte);
492
493 memcpy(automap_polygons, saved_automap_polygons, automap_polygon_buffer_size);
494 delete []saved_automap_polygons;
495 saved_automap_polygons= (byte *) NULL;
496 }
497 }
498
499
false_automap_cost_proc(short source_polygon_index,short line_index,short destination_polygon_index,void * caller_data)500 int32 OverheadMapClass::false_automap_cost_proc(
501 short source_polygon_index,
502 short line_index,
503 short destination_polygon_index,
504 void *caller_data)
505 {
506 struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
507 struct polygon_data *source_polygon= get_polygon_data(source_polygon_index);
508 int32 cost= 1;
509
510 (void) (line_index);
511 (void) (caller_data);
512
513 /* can�t leave secret platforms */
514 if (source_polygon->type==_polygon_is_platform &&
515 PLATFORM_IS_SECRET(get_platform_data(source_polygon->permutation)))
516 {
517 cost= -1;
518 }
519
520 /* can�t enter secret platforms which are also doors */
521 if (destination_polygon->type==_polygon_is_platform)
522 {
523 struct platform_data *platform= get_platform_data(destination_polygon->permutation);
524
525 if (PLATFORM_IS_DOOR(platform))
526 {
527 if (PLATFORM_IS_SECRET(platform)) cost= -1;
528 }
529 }
530
531 /* add the destination polygon and all its lines to the automap */
532 if (cost > 0)
533 add_poly_to_false_automap(destination_polygon_index);
534
535 return cost;
536 }
537