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