/* Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. and the "Aleph One" developers. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This license is contained in the file "COPYING", which is included with this source code; it is available online at http://www.gnu.org/licenses/gpl.html Overhead-Map Base-Class Renderer, by Loren Petrich, August 3, 2000 The code had been moved out of overhead_map.c and overhead_map_macintosh.c; all of it is cross-platform except for the font-handling code. */ #include "cseries.h" #include "OverheadMapRenderer.h" #include "flood_map.h" #include "media.h" #include "platforms.h" #include "player.h" #include "render.h" #include #include #include enum /* render flags */ { _endpoint_on_automap= 0x2000, _line_on_automap= 0x4000, _polygon_on_automap= 0x8000 }; /* ---------- macros */ #define WORLD_TO_SCREEN_SCALE_ONE 8 #define WORLD_TO_SCREEN(x, x0, scale) (((x)-(x0))>>(WORLD_TO_SCREEN_SCALE_ONE-(scale))) // Externals: // Changed to link properly with code in pathfinding.c extern world_point2d *path_peek(short path_index, short *step_count); extern short GetNumberOfPaths(); // Main rendering routine void OverheadMapClass::Render(overhead_map_data& Control) { world_distance x0= Control.origin.x, y0= Control.origin.y; short scale= Control.scale; world_point2d location; short i; // LP addition: overall setup begin_overall(); // LP addition: stuff for setting the game options, since they get defaulted to 0 // Made compatible with map cheat assert(ConfigPtr); if (ConfigPtr->ShowAliens) GET_GAME_OPTIONS() |= _overhead_map_shows_monsters; if (ConfigPtr->ShowItems) GET_GAME_OPTIONS() |= _overhead_map_shows_items; if (ConfigPtr->ShowProjectiles) GET_GAME_OPTIONS() |= _overhead_map_shows_projectiles; if (Control.mode==_rendering_checkpoint_map) generate_false_automap(Control.origin_polygon_index); transform_endpoints_for_overhead_map(Control); // LP addition begin_polygons(); /* shade all visible polygons */ for (i=0;ipolygon_count;++i) { struct polygon_data *polygon= get_polygon_data(i); if (POLYGON_IS_IN_AUTOMAP(i) && TEST_STATE_FLAG(i, _polygon_on_automap) &&(polygon->floor_transfer_mode!=_xfer_landscape||polygon->ceiling_transfer_mode!=_xfer_landscape)) { if (!POLYGON_IS_DETACHED(polygon)) { short color; switch (polygon->type) { case _polygon_is_platform: color= PLATFORM_IS_SECRET(get_platform_data(polygon->permutation)) ? _polygon_color : _polygon_platform_color; break; case _polygon_is_minor_ouch: color = _polygon_minor_ouch_color; break; case _polygon_is_major_ouch: color = _polygon_major_ouch_color; break; case _polygon_is_hill: color = _polygon_hill_color; break; default: color= _polygon_color; break; } if (polygon->media_index!=NONE) { struct media_data *media= get_media_data(polygon->media_index); // LP change: idiot-proofing if (media) { if (media->height>=polygon->floor_height) { switch (media->type) { case _media_water: color= _polygon_water_color; break; case _media_lava: color= _polygon_lava_color; break; case _media_goo: color= _polygon_goo_color; break; // LP change: separated sewage and JjaroGoo case _media_sewage: color= _polygon_sewage_color; break; case _media_jjaro: color = _polygon_jjaro_color; break; } } } } draw_polygon(polygon->vertex_count, polygon->endpoint_indexes, color, scale); } } } // LP addition end_polygons(); // LP addition begin_lines(); /* draw all visible lines */ for (i=0;iline_count;++i) { short line_color= NONE; struct line_data *line= get_line_data(i); if (LINE_IS_IN_AUTOMAP(i)) { if ((line->clockwise_polygon_owner!=NONE && TEST_STATE_FLAG(line->clockwise_polygon_owner, _polygon_on_automap)) || (line->counterclockwise_polygon_owner!=NONE && TEST_STATE_FLAG(line->counterclockwise_polygon_owner, _polygon_on_automap))) { struct polygon_data *clockwise_polygon= line->clockwise_polygon_owner==NONE ? NULL : get_polygon_data(line->clockwise_polygon_owner); struct polygon_data *counterclockwise_polygon= line->counterclockwise_polygon_owner==NONE ? NULL : get_polygon_data(line->counterclockwise_polygon_owner); if (LINE_IS_SOLID(line) || LINE_IS_VARIABLE_ELEVATION(line)) { if (LINE_IS_LANDSCAPED(line)) { if ((!clockwise_polygon||clockwise_polygon->floor_transfer_mode!=_xfer_landscape) && (!counterclockwise_polygon||counterclockwise_polygon->floor_transfer_mode!=_xfer_landscape)) { line_color= _elevation_line_color; } } else { line_color= _solid_line_color; } } else { if (clockwise_polygon->floor_height!=counterclockwise_polygon->floor_height) { line_color= LINE_IS_LANDSCAPED(line) ? NONE : static_cast(_elevation_line_color); } } } if (line_color!=NONE) draw_line(i, line_color, scale); } } // LP addition end_lines(); /* print all visible tags */ if (scale!=OVERHEAD_MAP_MINIMUM_SCALE) { struct map_annotation *annotation; i= 0; while ((annotation= get_next_map_annotation(&i))!=NULL) { if (POLYGON_IS_IN_AUTOMAP(annotation->polygon_index) && TEST_STATE_FLAG(annotation->polygon_index, _polygon_on_automap)) { location.x= Control.half_width + WORLD_TO_SCREEN(annotation->location.x, x0, scale); location.y= Control.half_height + WORLD_TO_SCREEN(annotation->location.y, y0, scale); draw_annotation(&location, annotation->type, annotation->text, scale); } } } if (ConfigPtr->ShowPaths) { short path_index; // LP change: made this more general set_path_drawing(); // LP change: there may be more than 20 paths for (path_index=0;path_indexpermutation); if (MONSTER_IS_PLAYER(monster)) { struct player_data *player= get_player_data(monster_index_to_player_index(object->permutation)); if ((GET_GAME_OPTIONS()&_overhead_map_is_omniscient) || local_player->team==player->team) { location.x= Control.half_width + WORLD_TO_SCREEN(object->location.x, x0, scale); location.y= Control.half_height + WORLD_TO_SCREEN(object->location.y, y0, scale); draw_player(&location, object->facing, player->team, scale); } } else { // LP: use the lookup system switch (ConfigPtr->monster_displays[monster->type]) { case _civilian_thing: thing_type= _civilian_thing; break; case _monster_thing: if (GET_GAME_OPTIONS()&_overhead_map_shows_monsters) thing_type= _monster_thing; break; } } break; } case _object_is_projectile: if ((GET_GAME_OPTIONS()&_overhead_map_shows_projectiles) && object->shape!=UNONE) { thing_type= _projectile_thing; } break; case _object_is_item: if (GET_GAME_OPTIONS()&_overhead_map_shows_items) { thing_type= _item_thing; } break; case _object_is_garbage: // LP change: making this more general switch (ConfigPtr->dead_monster_displays[GET_COLLECTION(GET_DESCRIPTOR_COLLECTION(object->shape))]) { case _civilian_thing: thing_type= _civilian_thing; break; case _monster_thing: if (GET_GAME_OPTIONS()&_overhead_map_shows_monsters) thing_type= _monster_thing; break; } /* if (GET_COLLECTION(GET_DESCRIPTOR_COLLECTION(object->shape))==_collection_civilian) { thing_type= _civilian_thing; } */ break; } if (thing_type!=NONE) { // Making this more general, in case we want to see monsters and stuff if (thing_type==_projectile_thing || ((dynamic_world->tick_count+i)&8)) // if (thing_type!=_civilian_thing || ((dynamic_world->tick_count+i)&8)) { location.x= Control.half_width + WORLD_TO_SCREEN(object->location.x, x0, scale); location.y= Control.half_height + WORLD_TO_SCREEN(object->location.y, y0, scale); draw_thing(&location, object->facing, thing_type, scale); } } } } } } else { for (i= 0; iinitial_objects_count; ++i) { struct map_object *saved_object= saved_objects + i; if (saved_object->type==_saved_goal && saved_object->location.x==Control.origin.x && saved_object->location.y==Control.origin.y) { location.x= Control.half_width + WORLD_TO_SCREEN(saved_object->location.x, x0, scale); location.y= Control.half_height + WORLD_TO_SCREEN(saved_object->location.y, y0, scale); draw_thing(&location, 0, _checkpoint_thing, scale); } } } if (Control.mode==_rendering_game_map) draw_map_name(Control, static_world->level_name); if (Control.mode==_rendering_checkpoint_map) replace_real_automap(); // LP addition: overall cleanup end_overall(); } void OverheadMapClass::transform_endpoints_for_overhead_map( struct overhead_map_data& Control) { world_distance x0= Control.origin.x, y0= Control.origin.y; short screen_width= Control.width, screen_height= Control.height; short scale= Control.scale; short i; /* transform all our endpoints into screen space, remembering which ones are visible */ for (i=0;iendpoint_count;++i) { struct endpoint_data *endpoint= get_endpoint_data(i); endpoint->transformed.x= Control.half_width + WORLD_TO_SCREEN(endpoint->vertex.x, x0, scale); endpoint->transformed.y= Control.half_height + WORLD_TO_SCREEN(endpoint->vertex.y, y0, scale); if (endpoint->transformed.x>=0&&endpoint->transformed.y>=0&&endpoint->transformed.y<=screen_height&&endpoint->transformed.x<=screen_width) { SET_STATE_FLAG(i, _endpoint_on_automap, true); } } /* sweep the polygon array, determining which polygons are visible based on their endpoints */ for (i=0;ipolygon_count;++i) { struct polygon_data *polygon= get_polygon_data(i); short j; for (j=0;jvertex_count;++j) { if (TEST_STATE_FLAG(polygon->endpoint_indexes[j], _endpoint_on_automap)) { SET_STATE_FLAG(i, _polygon_on_automap, true); break; } } } } /* --------- the false automap */ void OverheadMapClass::generate_false_automap( short polygon_index) { long automap_line_buffer_size, automap_polygon_buffer_size; automap_line_buffer_size= (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0))*sizeof(byte); automap_polygon_buffer_size= (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0))*sizeof(byte); /* allocate memory for the old automap memory */ saved_automap_lines= new byte[automap_line_buffer_size]; saved_automap_polygons= new byte[automap_polygon_buffer_size]; if (saved_automap_lines && saved_automap_polygons) { memcpy(saved_automap_lines, automap_lines, automap_line_buffer_size); memcpy(saved_automap_polygons, automap_polygons, automap_polygon_buffer_size); memset(automap_lines, 0, automap_line_buffer_size); memset(automap_polygons, 0, automap_polygon_buffer_size); polygon_index= flood_map(polygon_index, LONG_MAX, false_automap_cost_proc, _breadth_first, (void *) NULL); do { polygon_index= flood_map(NONE, LONG_MAX, false_automap_cost_proc, _breadth_first, (void *) NULL); } while (polygon_index!=NONE); } } void OverheadMapClass::replace_real_automap( void) { if (saved_automap_lines) { long automap_line_buffer_size= (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0))*sizeof(byte); memcpy(automap_lines, saved_automap_lines, automap_line_buffer_size); delete []saved_automap_lines; saved_automap_lines= (byte *) NULL; } if (saved_automap_polygons) { long automap_polygon_buffer_size= (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0))*sizeof(byte); memcpy(automap_polygons, saved_automap_polygons, automap_polygon_buffer_size); delete []saved_automap_polygons; saved_automap_polygons= (byte *) NULL; } } long OverheadMapClass::false_automap_cost_proc( short source_polygon_index, short line_index, short destination_polygon_index, void *caller_data) { struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index); struct polygon_data *source_polygon= get_polygon_data(source_polygon_index); long cost= 1; short i; (void) (line_index); (void) (caller_data); /* canŐt leave secret platforms */ if (source_polygon->type==_polygon_is_platform && PLATFORM_IS_SECRET(get_platform_data(source_polygon->permutation))) { cost= -1; } /* canŐt enter secret platforms which are also doors */ if (destination_polygon->type==_polygon_is_platform) { struct platform_data *platform= get_platform_data(destination_polygon->permutation); if (PLATFORM_IS_DOOR(platform)) { if (PLATFORM_IS_SECRET(platform)) cost= -1; } } /* add the source polygon and all its lines to the automap */ for (i= 0; ivertex_count; ++i) ADD_LINE_TO_AUTOMAP(source_polygon->line_indexes[i]); ADD_POLYGON_TO_AUTOMAP(source_polygon_index); return cost; }