From 1d0f57cbceb778139ca215cc4fcfd1584951f6dd Mon Sep 17 00:00:00 2001 From: dshaligram Date: Wed, 22 Nov 2006 08:41:20 +0000 Subject: Merged stone_soup r15:451 into trunk. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@452 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/view.cc | 4651 ++++++++++++++++++++-------------------------- 1 file changed, 2059 insertions(+), 2592 deletions(-) (limited to 'crawl-ref/source/view.cc') diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index adfb1a62bd..fdbe60d9f3 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -34,10 +34,16 @@ #include "externs.h" +#include "command.h" #include "clua.h" #include "debug.h" +#include "delay.h" +#include "direct.h" +#include "initfile.h" #include "insult.h" +#include "itemprop.h" #include "macro.h" +#include "misc.h" #include "monstuff.h" #include "mon-util.h" #include "overmap.h" @@ -48,37 +54,61 @@ #include "stash.h" #include "travel.h" +// These are hidden from the rest of the world... use the functions +// below to get information about the map grid. +#define MAP_MAGIC_MAPPED_FLAG 0x0100 +#define MAP_SEEN_FLAG 0x0200 +#define MAP_CHANGED_FLAG 0x0400 +#define MAP_DETECTED_MONSTER 0x0800 +#define MAP_DETECTED_ITEM 0x1000 + +#define MAP_CHARACTER_MASK 0x00ff + +struct feature_def +{ + unsigned short symbol; // symbol used for seen terrain + unsigned short magic_symbol; // symbol used for magic-mapped terrain + unsigned short colour; // normal in LoS colour + unsigned short map_colour; // colour when out of LoS on display + unsigned short seen_colour; // map_colour when is_terrain_seen() + bool notable; // gets noted when seen + bool seen_effect; // requires special handling when seen +}; + +struct feature_override +{ + dungeon_feature_type feat; + feature_def override; +}; + +static FixedVector< struct feature_def, NUM_FEATURES > Feature; +static std::vector Feature_Overrides; + #if defined(DOS_TERM) -typedef char screen_buffer_t; +// DOS functions like gettext() and puttext() can only +// work with arrays of characters, not shorts. +typedef unsigned char screen_buffer_t; #else typedef unsigned short screen_buffer_t; #endif -unsigned char your_sign; // accessed as extern in transfor.cc and acr.cc -unsigned char your_colour; // accessed as extern in transfor.cc and acr.cc - -FixedArray < unsigned int, 20, 19 > show_backup; +FixedArray < unsigned int, 20, 19 > Show_Backup; unsigned char show_green; extern int stealth; // defined in acr.cc -extern FixedVector Visible_Statue; // defined in acr.cc // char colour_code_map(unsigned char map_value); screen_buffer_t colour_code_map( int x, int y, bool item_colour = false, bool travel_colour = false ); -extern void (*viewwindow) (char, bool); -unsigned char (*mapch) (unsigned char); -unsigned char (*mapch2) (unsigned char); -unsigned char mapchar(unsigned char ldfk); -unsigned char mapchar2(unsigned char ldfk); -unsigned char mapchar3(unsigned char ldfk); -unsigned char mapchar4(unsigned char ldfk); void cloud_grid(void); void monster_grid(bool do_updates); -static int display_glyph(int env_glyph); -static int item_env_glyph(const item_def &item); +static int get_item_dngn_code(const item_def &item); +static void set_show_backup( int ex, int ey ); + +// Applies EC_ colour substitutions and brands. +static unsigned fix_colour(unsigned raw_colour); //--------------------------------------------------------------- // @@ -105,995 +135,391 @@ int get_number_of_lines(void) { #ifdef UNIX return (get_number_of_lines_from_curses()); -#elif MAC - return (MAC_NUMBER_OF_LINES); #else return (25); #endif } +int get_number_of_cols(void) +{ +#ifdef UNIX + return (get_number_of_cols_from_curses()); +#else + return (80); +#endif +} -//--------------------------------------------------------------- -// -// get_ibm_symbol -// -// Returns the DOS character code and color for everything drawn -// with the IBM graphics option. -// -//--------------------------------------------------------------- -static void get_ibm_symbol(unsigned int object, unsigned short *ch, - unsigned short *color) +unsigned get_envmap_char(int x, int y) { - ASSERT(color != NULL); - ASSERT(ch != NULL); + return static_cast( + env.map[x - 1][y - 1] & MAP_CHARACTER_MASK); +} - switch (object) - { - case DNGN_UNSEEN: - *ch = 0; - break; +void set_envmap_detected_item(int x, int y, bool detected) +{ + if (detected) + env.map[x - 1][y - 1] |= MAP_DETECTED_ITEM; + else + env.map[x - 1][y - 1] &= ~MAP_DETECTED_ITEM; +} - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - *color = env.rock_colour; - *ch = 177; - break; // remember earth elementals - - // stone in the realm of Zot is coloured the same as rock - case DNGN_STONE_WALL: - *color = (player_in_branch( BRANCH_HALL_OF_ZOT ) ? env.rock_colour - : LIGHTGREY); - *ch = 177; - break; +bool is_envmap_detected_item(int x, int y) +{ + return (env.map[x - 1][y - 1] & MAP_DETECTED_ITEM); +} - case DNGN_CLOSED_DOOR: - *ch = 254; - break; +void set_envmap_detected_mons(int x, int y, bool detected) +{ + if (detected) + env.map[x - 1][y - 1] |= MAP_DETECTED_MONSTER; + else + env.map[x - 1][y - 1] &= ~MAP_DETECTED_MONSTER; +} - case DNGN_METAL_WALL: - *ch = 177; - *color = CYAN; - break; +bool is_envmap_detected_mons(int x, int y) +{ + return (env.map[x - 1][y - 1] & MAP_DETECTED_MONSTER); +} - case DNGN_SECRET_DOOR: - *ch = 177; - *color = env.rock_colour; - break; +void set_envmap_char( int x, int y, unsigned char chr ) +{ + env.map[x - 1][y - 1] &= (~MAP_CHARACTER_MASK); // clear old first + env.map[x - 1][y - 1] |= chr; +} - case DNGN_GREEN_CRYSTAL_WALL: - *ch = 177; - *color = GREEN; - break; +bool is_terrain_known( int x, int y ) +{ + return (env.map[x - 1][y - 1] & (MAP_MAGIC_MAPPED_FLAG | MAP_SEEN_FLAG)); +} - case DNGN_ORCISH_IDOL: - *ch = '8'; - *color = DARKGREY; - break; +bool is_terrain_seen( int x, int y ) +{ + return (env.map[x - 1][y - 1] & MAP_SEEN_FLAG); +} - case DNGN_WAX_WALL: - *ch = 177; - *color = YELLOW; - break; // wax wall - /* Anything added here must also be added to the PLAIN_TERMINAL - viewwindow2 below */ - - case DNGN_SILVER_STATUE: - *ch = '8'; - *color = WHITE; - Visible_Statue[ STATUE_SILVER ] = 1; - break; +bool is_terrain_changed( int x, int y ) +{ + return (env.map[x - 1][y - 1] & MAP_CHANGED_FLAG); +} - case DNGN_GRANITE_STATUE: - *ch = '8'; - *color = LIGHTGREY; - break; +// used to mark dug out areas, unset when terrain is seen or mapped again. +void set_terrain_changed( int x, int y ) +{ + env.map[x - 1][y - 1] |= MAP_CHANGED_FLAG; +} - case DNGN_ORANGE_CRYSTAL_STATUE: - *ch = '8'; - *color = LIGHTRED; - Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 1; - break; +void set_terrain_mapped( int x, int y ) +{ + env.map[x - 1][y - 1] &= (~MAP_CHANGED_FLAG); + env.map[x - 1][y - 1] |= MAP_MAGIC_MAPPED_FLAG; +} - case DNGN_LAVA: - *ch = 247; - *color = RED; - break; +void set_terrain_seen( int x, int y ) +{ + env.map[x - 1][y - 1] &= (~MAP_CHANGED_FLAG); + env.map[x - 1][y - 1] |= MAP_SEEN_FLAG; +} - case DNGN_DEEP_WATER: - *ch = 247; // this wavy thing also used for water elemental - *color = BLUE; - break; +void clear_envmap_grid( int x, int y ) +{ + env.map[x - 1][y - 1] = 0; +} - case DNGN_SHALLOW_WATER: - *ch = 247; // this wavy thing also used for water elemental - *color = CYAN; - break; +void clear_envmap( void ) +{ + for (int i = 0; i < GXM; i++) + { + for (int j = 0; j < GYM; j++) + { + env.map[i][j] = 0; + } + } +} - case DNGN_FLOOR: - *color = env.floor_colour; - *ch = 249; - break; +#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) +static unsigned colflag2brand(int colflag) +{ + switch (colflag) + { + case COLFLAG_ITEM_HEAP: + return (Options.heap_brand); + case COLFLAG_FRIENDLY_MONSTER: + return (Options.friend_brand); + case COLFLAG_WILLSTAB: + return (Options.stab_brand); + case COLFLAG_MAYSTAB: + return (Options.may_stab_brand); + default: + return (CHATTR_NORMAL); + } +} +#endif - case DNGN_ENTER_HELL: - *ch = 239; - *color = RED; - seen_other_thing(object); - break; +static unsigned fix_colour(unsigned raw_colour) +{ + // This order is important - is_element_colour() doesn't want to see the + // munged colours returned by dos_brand, so it should always be done + // before applying DOS brands. +#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) + const int colflags = raw_colour & 0xFF00; +#endif - case DNGN_OPEN_DOOR: - *ch = 39; - break; + // Evaluate any elemental colours to guarantee vanilla colour is returned + if (is_element_colour( raw_colour )) + raw_colour = element_colour( raw_colour ); - case DNGN_BRANCH_STAIRS: - *ch = 240; - *color = BROWN; - break; +#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) + if (colflags) + { + unsigned brand = colflag2brand(colflags); + raw_colour = dos_brand(raw_colour & 0xFF, brand); + } +#endif - case DNGN_TRAP_MECHANICAL: - *color = LIGHTCYAN; - *ch = 94; - break; +#ifndef USE_COLOUR_OPTS + // Strip COLFLAGs for systems that can't do anything meaningful with them. + raw_colour &= 0xFF; +#endif - case DNGN_TRAP_MAGICAL: - *color = MAGENTA; - *ch = 94; - break; + return (raw_colour); +} - case DNGN_TRAP_III: - *color = LIGHTGREY; - *ch = 94; - break; +static void get_symbol( unsigned int object, unsigned short *ch, + unsigned short *colour ) +{ + ASSERT( ch != NULL ); + ASSERT( colour != NULL ); - case DNGN_UNDISCOVERED_TRAP: - *ch = 249; - *color = env.floor_colour; - break; + if (object < NUM_FEATURES) + { + *ch = Feature[object].symbol; - case DNGN_ENTER_SHOP: - *ch = 239; - *color = YELLOW; + // Don't clobber with BLACK, because the colour should be already set. + if (Feature[object].colour != BLACK) + *colour = Feature[object].colour; - seen_other_thing(object); - break; - // if I change anything above here, must also change magic mapping! + // Note anything we see that's notable + if (Feature[object].notable) + seen_notable_thing( object ); + } + else + { + ASSERT( object >= DNGN_START_OF_MONSTERS ); + *ch = mons_char( object - DNGN_START_OF_MONSTERS ); + } - case DNGN_ENTER_LABYRINTH: - *ch = 239; - *color = LIGHTGREY; - seen_other_thing(object); - break; + *colour = fix_colour(*colour); +} - // not sure why we have "odd" here, but "ladders" are special in - // that they all lead to the first staircase of the next level - // (and returning from there will take you somewhere different) - // ... that's why they're brown... it's a warning -- bwr - case DNGN_ROCK_STAIRS_DOWN: - *color = BROWN; // ladder // odd {dlb} - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - *ch = '>'; - break; +unsigned char get_sightmap_char( int feature ) +{ + if (feature < NUM_FEATURES) + return (Feature[feature].symbol); - case DNGN_ROCK_STAIRS_UP: - *color = BROWN; // ladder // odd {dlb} - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - *ch = '<'; - break; + return (0); +} - case DNGN_ENTER_DIS: - *color = CYAN; - *ch = 239; - break; +unsigned char get_magicmap_char( int feature ) +{ + if (feature < NUM_FEATURES) + return (Feature[feature].magic_symbol); - case DNGN_ENTER_GEHENNA: - *color = RED; - *ch = 239; - break; + return (0); +} - case DNGN_ENTER_COCYTUS: - *color = LIGHTCYAN; - *ch = 239; - break; +static char get_travel_colour( int x, int y ) +{ + if (is_waypoint(x + 1, y + 1)) + return LIGHTGREEN; - case DNGN_ENTER_TARTARUS: - *color = DARKGREY; - *ch = 239; - break; + short dist = point_distance[x + 1][y + 1]; + return dist > 0? Options.tc_reachable : + dist == PD_EXCLUDED? Options.tc_excluded : + dist == PD_EXCLUDED_RADIUS? Options.tc_exclude_circle : + dist < 0? Options.tc_dangerous : + Options.tc_disconnected; +} + +#if defined(WIN32CONSOLE) || defined(DOS) +static unsigned short dos_reverse_brand(unsigned short colour) +{ + if (Options.dos_use_background_intensity) + { + // If the console treats the intensity bit on background colours + // correctly, we can do a very simple colour invert. - case DNGN_ENTER_ABYSS: - *color = random2(16); - *ch = 239; - seen_other_thing(object); - break; + // Special casery for shadows. + if (colour == BLACK) + colour = (DARKGREY << 4); + else + colour = (colour & 0xF) << 4; + } + else + { + // If we're on a console that takes its DOSness very seriously the + // background high-intensity bit is actually a blink bit. Blinking is + // evil, so we strip the background high-intensity bit. This, sadly, + // limits us to 7 background colours. + + // Strip off high-intensity bit. Special case DARKGREY, since it's the + // high-intensity counterpart of black, and we don't want black on + // black. + // + // We *could* set the foreground colour to WHITE if the background + // intensity bit is set, but I think we've carried the + // angry-fruit-salad theme far enough already. - case DNGN_EXIT_ABYSS: - *color = random2(16); - *ch = 239; - break; + if (colour == DARKGREY) + colour |= (LIGHTGREY << 4); + else if (colour == BLACK) + colour = LIGHTGREY << 4; + else + { + // Zap out any existing background colour, and the high + // intensity bit. + colour &= 7; - case DNGN_STONE_ARCH: - *color = LIGHTGREY; - *ch = 239; - break; + // And swap the foreground colour over to the background + // colour, leaving the foreground black. + colour <<= 4; + } + } - case DNGN_ENTER_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = 239; - seen_other_thing(object); - break; + return (colour); +} - case DNGN_EXIT_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = 239; - break; +static unsigned short dos_hilite_brand(unsigned short colour, + unsigned short hilite) +{ + if (!hilite) + return (colour); - case DNGN_TRANSIT_PANDEMONIUM: - *color = LIGHTGREEN; - *ch = 239; - break; + if (colour == hilite) + colour = 0; - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - *color = YELLOW; - *ch = '>'; - seen_staircase(object); - break; + colour |= (hilite << 4); + return (colour); +} - case DNGN_ENTER_ZOT: - *color = MAGENTA; - *ch = 239; - seen_staircase(object); - break; +unsigned short dos_brand( unsigned short colour, + unsigned brand) +{ + if ((brand & CHATTR_ATTRMASK) == CHATTR_NORMAL) + return (colour); - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - *color = YELLOW; - *ch = '<'; - break; + colour &= 0xFF; + + if ((brand & CHATTR_ATTRMASK) == CHATTR_HILITE) + return dos_hilite_brand(colour, (brand & CHATTR_COLMASK) >> 8); + else + return dos_reverse_brand(colour); + +} +#endif - case DNGN_RETURN_FROM_ZOT: - *color = MAGENTA; - *ch = 239; - break; +// FIXME: Rework this function to use the new terrain known/seen checks +// These are still env.map coordinates, NOT grid coordinates! +screen_buffer_t colour_code_map( int x, int y, bool item_colour, + bool travel_colour ) +{ + // XXX: Yes, the map array and the grid array are off by one. -- bwr + const int map_value = (unsigned char) env.map[x][y]; + const unsigned short map_flags = env.map[x][y]; + const int grid_value = grd[x + 1][y + 1]; - case DNGN_ALTAR_ZIN: - *color = WHITE; - *ch = 220; - seen_altar(GOD_ZIN); - break; + unsigned tc = travel_colour? + get_travel_colour(x, y) + : DARKGREY; - case DNGN_ALTAR_SHINING_ONE: - *color = YELLOW; - *ch = 220; - seen_altar(GOD_SHINING_ONE); - break; + if (map_flags & MAP_DETECTED_ITEM) + tc = Options.detected_item_colour; + + if (map_flags & MAP_DETECTED_MONSTER) + { + tc = Options.detected_monster_colour; + return fix_colour(tc); + } - case DNGN_ALTAR_KIKUBAAQUDGHA: - *color = DARKGREY; - *ch = 220; - seen_altar(GOD_KIKUBAAQUDGHA); - break; + // XXX: [ds] If we've an important colour, override other feature + // colouring. Yes, this is hacky. Story of my life. + if (tc == LIGHTGREEN || tc == LIGHTMAGENTA) + return fix_colour(tc); - case DNGN_ALTAR_YREDELEMNUL: - *color = ((one_chance_in(3)) ? RED : DARKGREY); - *ch = 220; - seen_altar(GOD_YREDELEMNUL); - break; + // XXX: Yeah, this is ugly, but until we have stored layers in the + // map we can't tell if we've seen a square, detected it, or just + // detected the item or monster on top... giving colour here will + // result in detect creature/item detecting features like stairs. -- bwr + if (map_value != get_sightmap_char(grid_value)) + { + // If there's an item on this square, change colour to indicate + // that, iff the item's glyph matches map_value. XXX: Potentially + // abusable? -- ds + int item = igrd[x + 1][y + 1]; + if (item_colour && item != NON_ITEM + && map_value == + get_sightmap_char(get_item_dngn_code(mitm[item]))) + { + unsigned ic = mitm[item].colour; - case DNGN_ALTAR_XOM: - *color = random_colour(); - *ch = 220; - seen_altar(GOD_XOM); - break; + if (mitm[item].link != NON_ITEM ) + ic |= COLFLAG_ITEM_HEAP; - case DNGN_ALTAR_VEHUMET: - *color = LIGHTBLUE; - if (one_chance_in(3)) - *color = LIGHTMAGENTA; - if (one_chance_in(3)) - *color = LIGHTRED; - *ch = 220; - seen_altar(GOD_VEHUMET); - break; + // If the item colour is the background colour, tweak it to WHITE + // instead to catch the player's eye. + return fix_colour( ic == tc? WHITE : ic ); + } + } - case DNGN_ALTAR_OKAWARU: - *color = CYAN; - *ch = 220; - seen_altar(GOD_OKAWARU); - break; + int feature_colour = DARKGREY; + feature_colour = + is_terrain_seen(x + 1, y + 1)? Feature[grid_value].seen_colour + : Feature[grid_value].map_colour; - case DNGN_ALTAR_MAKHLEB: - *color = RED; - if (one_chance_in(3)) - *color = LIGHTRED; - if (one_chance_in(3)) - *color = YELLOW; - *ch = 220; - seen_altar(GOD_MAKHLEB); - break; + if (feature_colour != DARKGREY) + tc = feature_colour; - case DNGN_ALTAR_SIF_MUNA: - *color = BLUE; - *ch = 220; - seen_altar(GOD_SIF_MUNA); - break; + return fix_colour(tc); +} - case DNGN_ALTAR_TROG: - *color = RED; - *ch = 220; - seen_altar(GOD_TROG); - break; +void clear_map() +{ + for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y) + { + for (int x = X_BOUND_1; x <= X_BOUND_2; ++x) + { + // Don't expose new dug out areas: + // Note: assumptions are being made here about how + // terrain can change (eg it used to be solid, and + // thus monster/item free). + if (is_terrain_changed(x, y)) + continue; - case DNGN_ALTAR_NEMELEX_XOBEH: - *color = LIGHTMAGENTA; - *ch = 220; - seen_altar(GOD_NEMELEX_XOBEH); - break; + unsigned short envc = env.map[x][y] & MAP_CHARACTER_MASK; + if (!envc) + continue; - case DNGN_ALTAR_ELYVILON: - *color = LIGHTGREY; - *ch = 220; - seen_altar(GOD_ELYVILON); - break; + const int item = igrd[x + 1][y + 1]; + if (item != NON_ITEM + && envc == + get_sightmap_char(get_item_dngn_code(mitm[item]))) + continue; - case DNGN_BLUE_FOUNTAIN: - *color = BLUE; - *ch = 159; - break; + set_envmap_char(x, y, + is_terrain_seen(x, y)? get_sightmap_char(grd[x][y]) : + is_terrain_known(x, y)? get_magicmap_char(grd[x][y]) : + 0); + set_envmap_detected_mons(x, y, false); + } + } +} - case DNGN_SPARKLING_FOUNTAIN: - *color = LIGHTBLUE; - *ch = 159; - break; +void monster_grid(bool do_updates) +{ + struct monsters *monster = 0; // NULL {dlb} - case DNGN_DRY_FOUNTAIN_I: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_PERMADRY_FOUNTAIN: - *color = LIGHTGREY; - *ch = 159; - break; - - case 256: - *ch = '0'; - break; - - case 257: - *color = CYAN; - *ch = '~'; - break; /* Invis creature walking through water */ - - case 258: - *ch = ')'; - break; // weapon ) - - case 259: - *ch = '['; - break; // armour [ - - case 260: - *ch = '/'; - break; // wands, etc. - - case 261: - *ch = '%'; - break; // food - - case 262: - *ch = '+'; - break; // books + - - case 263: - *ch = '?'; - break; // scroll ? - - case 264: - *ch = '='; - break; // ring = etc - - case 265: - *ch = '!'; - break; // potions ! - - case 266: - *ch = '('; - break; // stones - - case 267: - *ch = '+'; - break; // book + - - case 268: - *ch = '%'; - break; // corpses part 1 - - case 269: - *ch = '\\'; - break; // magical staves - - case 270: - *ch = '}'; - break; // gems - - case 271: - *ch = '%'; - break; // don't know ? - - case 272: - *ch = '$'; - *color = YELLOW; - break; // $ gold - - case 273: - *ch = '"'; - break; // amulet - - default: - *ch = ((object >= 297) ? mons_char(object - 297) : object); - break; - } -} // end get_ibm_symbol() - -//--------------------------------------------------------------- -// -// viewwindow2 -// -// Draws the main window using the extended IBM character set. -// -// This function should not interfer with the game condition, -// unless do_updates is set (ie. stealth checks for visible -// monsters). -// -//--------------------------------------------------------------- -void viewwindow2(char draw_it, bool do_updates) -{ - const long BUFFER_SIZE = 1550; -#ifdef DOS_TERM - // DOS functions like gettext() and puttext() can only - // work with arrays of characters, not shorts. - FixedVector < unsigned char, BUFFER_SIZE > buffy; //[800]; //392]; -#else - FixedVector < unsigned short, BUFFER_SIZE > buffy; //[800]; //392]; -#endif - - unsigned short ch, color; - - losight(env.show, grd, you.x_pos, you.y_pos); - - int count_x, count_y; - - for (count_x = 0; count_x < 18; count_x++) - { - for (count_y = 0; count_y < 18; count_y++) - { - env.show_col[count_x][count_y] = LIGHTGREY; - show_backup[count_x][count_y] = 0; - } - } - - item(); - cloud_grid(); - monster_grid(do_updates); - int bufcount = 0; - - if (draw_it == 1) - { - _setcursortype(_NOCURSOR); - for (count_y = you.y_pos - 8; count_y < you.y_pos + 9; count_y++) - { - bufcount += 16; - - for (count_x = you.x_pos - 8; count_x < you.x_pos + 9; count_x++) - { - // may be overriden by the code below - color = env.show_col[ count_x - you.x_pos + 9 ] - [ count_y - you.y_pos + 9 ]; - - unsigned int object = env.show[ count_x - you.x_pos + 9 ] - [ count_y - you.y_pos + 9 ]; - - get_ibm_symbol(object, &ch, &color); - - if (count_x == you.x_pos && count_y == you.y_pos) - { - ch = your_sign; - - if (player_is_swimming()) - { - color = (grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER) - ? BLUE : CYAN; - } - else - { - color = your_colour; - } - } - - ASSERT(bufcount + 1 < BUFFER_SIZE); - buffy[bufcount] = ch; - buffy[bufcount + 1] = color; - - bufcount += 2; - } - - bufcount += 16; - } - - if (you.level_type != LEVEL_LABYRINTH && you.level_type != LEVEL_ABYSS) - { - bufcount = 0; - - for (count_y = 0; count_y < 17; count_y++) - { - bufcount += 16; - - for (count_x = 0; count_x < 17; count_x++) - { - ASSERT(bufcount < BUFFER_SIZE); - - int mapx = count_x + you.x_pos - 9; - int mapy = count_y + you.y_pos - 9; - if (buffy[bufcount] != 0 && mapx >= 0 && mapx + 1 < GXM - && mapy >= 0 && mapy + 1 < GYM) - { - unsigned short bch = buffy[bufcount]; - if (mgrd[mapx + 1][mapy + 1] != NON_MONSTER) { - const monsters &m = menv[mgrd[mapx + 1][mapy + 1]]; - if (!mons_is_mimic(m.type) - && mons_char(m.type) == bch) - { - bch |= mons_colour(m.type) << 12; - } - } - env.map[mapx][mapy] = bch; - } - - if (Options.clean_map == 1 - && show_backup[count_x + 1][count_y + 1] != 0) - { - get_ibm_symbol(show_backup[count_x + 1][count_y + 1], - &ch, &color); - if (mapx >= 0 && mapx < GXM - && mapy >= 0 && mapy < GYM) - env.map[ count_x + you.x_pos - 9 ] - [ count_y + you.y_pos - 9 ] = ch; - } - bufcount += 2; - } - bufcount += 16; - } - } - - bufcount = 0; - for (count_y = 0; count_y < 17; count_y++) - { - for (count_x = 0; count_x < 33; count_x++) - { - if (count_x + you.x_pos - 17 < 3 - || count_y + you.y_pos - 9 < 3 - || count_x + you.x_pos - 14 > 77 - || count_y + you.y_pos - 9 > 67) - { - ASSERT(bufcount < BUFFER_SIZE); - buffy[bufcount] = 0; - bufcount++; - buffy[bufcount] = 0; - bufcount++; - continue; - } - - if (count_x >= 8 && count_x <= 24 && count_y >= 0 - && count_y <= 16 && buffy[bufcount] != 0) - { - bufcount += 2; - continue; - } - - ASSERT(bufcount + 1 < BUFFER_SIZE); - - buffy[bufcount] = (unsigned char) - env.map[ count_x + you.x_pos - 17 ] - [ count_y + you.y_pos - 9 ]; - - buffy[bufcount + 1] = DARKGREY; - if (Options.colour_map) - { - if (env.map[ count_x + you.x_pos - 17 ] - [ count_y + you.y_pos - 9 ] != 0) - { - buffy[bufcount + 1] - = colour_code_map( count_x + you.x_pos - 17, - count_y + you.y_pos - 9, - Options.item_colour ); - } - } - bufcount += 2; - } - } - - if (you.berserker) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = RED; - } - } - - if (show_green != BLACK) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = show_green; - } - - show_green = BLACK; - - if (you.special_wield == SPWLD_SHADOW) - show_green = DARKGREY; - } - -#ifdef DOS_TERM - puttext(2, 1, 34, 17, buffy.buffer()); -#endif - -#ifdef PLAIN_TERM - gotoxy(2, 1); - bufcount = 0; - - // following lines are purely optional. - // if used, players will 'jump' move. - // Resting will be a LOT faster too. - if (you.running == 0 || (you.running < 0 && Options.travel_delay > -1)) - { - for (count_x = 0; count_x < 1120; count_x += 2) - { // 1056 - ch = buffy[count_x]; - color = buffy[count_x + 1]; -// ASSERT(color < 16); - ASSERT(ch < 255); - - textcolor(color); - putch(ch); - - if (count_x % 66 == 64 && count_x > 0) - gotoxy(2, wherey() + 1); - } - // remember to comment out the line below if you comment out jump move. - } - _setcursortype(_NORMALCURSOR); -#endif - } -} // end viewwindow2() - -static char get_travel_colour( int x, int y ) -{ - if (is_waypoint(x + 1, y + 1)) - return LIGHTGREEN; - - short dist = point_distance[x + 1] - [y + 1]; - return dist > 0 ? BLUE : - dist == PD_EXCLUDED ? LIGHTMAGENTA : - dist == PD_EXCLUDED_RADIUS ? RED : - dist < 0 ? CYAN : - DARKGREY; -} - -#if defined(WIN32CONSOLE) || defined(DOS) -static unsigned short dos_reverse_brand(unsigned short colour) -{ - if (Options.dos_use_background_intensity) - { - // If the console treats the intensity bit on background colours - // correctly, we can do a very simple colour invert. - - // Special casery for shadows. Note this must be matched by the fix - // to libw32c.cc (the unpatched libw32c.cc does not draw spaces of any - // colour). - if (colour == BLACK) - colour = (DARKGREY << 4); - else - colour = (colour & 0xF) << 4; - } - else - { - // If we're on a console that takes its DOSness very seriously the - // background high-intensity bit is actually a blink bit. Blinking is - // evil, so we strip the background high-intensity bit. This, sadly, - // limits us to 7 background colours. - - // Strip off high-intensity bit. Special case DARKGREY, since it's the - // high-intensity counterpart of black, and we don't want black on - // black. - // - // We *could* set the foreground colour to WHITE if the background - // intensity bit is set, but I think we've carried the - // angry-fruit-salad theme far enough already. - - if (colour == DARKGREY) - colour |= (LIGHTGREY << 4); - else if (colour == BLACK) - colour = LIGHTGREY << 4; - else - { - // Zap out any existing background colour, and the high - // intensity bit. - colour &= 7; - - // And swap the foreground colour over to the background - // colour, leaving the foreground black. - colour <<= 4; - } - } - - return (colour); -} - -static unsigned short dos_hilite_brand(unsigned short colour, - unsigned short hilite) -{ - if (!hilite) - return (colour); - - if (colour == hilite) - colour = 0; - - colour |= (hilite << 4); - return (colour); -} - -static unsigned short dos_brand( unsigned short colour, - unsigned brand = CHATTR_REVERSE ) -{ - if ((brand & CHATTR_ATTRMASK) == CHATTR_NORMAL) - return (colour); - - colour &= 0xFF; - - if ((brand & CHATTR_ATTRMASK) == CHATTR_HILITE) - return dos_hilite_brand(colour, (brand & CHATTR_COLMASK) >> 8); - else - return dos_reverse_brand(colour); - -} -#endif - -screen_buffer_t colour_code_map( int x, int y, bool item_colour, - bool travel_colour ) -{ - // XXX: Yes, the map array and the grid array are off by one. -- bwr - const int map_value = (unsigned char) env.map[x][y]; - const unsigned short map_flags = env.map[x][y] & ENVF_FLAGS; - const int grid_value = grd[x + 1][y + 1]; - - char tc = travel_colour? get_travel_colour(x, y) : DARKGREY; - - if (map_flags & ENVF_DETECT_ITEM) - tc = Options.detected_item_colour; - - if (map_flags & ENVF_DETECT_MONS) { - tc = Options.detected_monster_colour; - return (tc); - } - - unsigned char ecol = ENVF_COLOR(map_flags); - if (ecol) { - unsigned rmc = Options.remembered_monster_colour & 0xFFFF; - if (rmc == 0xFFFF) // Use real colour - tc = ecol; - else if (rmc == 0) // Don't colour - ; - else - tc = rmc; - } - - // XXX: [ds] If we've an important colour, override other feature - // colouring. Yes, this is hacky. Story of my life. - if (tc == LIGHTGREEN || tc == LIGHTMAGENTA) - return tc; - - // XXX: Yeah, this is ugly, but until we have stored layers in the - // map we can't tell if we've seen a square, detected it, or just - // detected the item or monster on top... giving colour here will - // result in detect creature/item detecting features like stairs. -- bwr - if (map_value != mapch2( grid_value )) { - // If there's an item on this square, change colour to indicate - // that, iff the item's glyph matches map_value. XXX: Potentially - // abusable? -- ds - int item = igrd[x + 1][y + 1]; - if (item_colour && item != NON_ITEM - && map_value == display_glyph(item_env_glyph(mitm[item]))) - { - screen_buffer_t ic = mitm[item].colour; - -#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) - if (mitm[item].link != NON_ITEM - && Options.heap_brand != CHATTR_NORMAL) - { - ic = dos_brand(ic, Options.heap_brand); - } -#elif defined(USE_COLOUR_OPTS) - if (mitm[item].link != NON_ITEM ) - { - ic |= COLFLAG_ITEM_HEAP; - } -#endif - // If the item colour is the background colour, tweak it to WHITE - // instead to catch the player's eye. - return ic == tc? WHITE : ic; - } - - return tc; - } - - switch (grid_value) - { - case DNGN_TRAP_MECHANICAL: - return (LIGHTCYAN); - - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - return (MAGENTA); - - case DNGN_ENTER_SHOP: - return (YELLOW); - - case DNGN_ENTER_DIS: - return (CYAN); - - case DNGN_ENTER_HELL: - case DNGN_ENTER_GEHENNA: - return (RED); - - case DNGN_ENTER_COCYTUS: - return (LIGHTCYAN); - - case DNGN_ENTER_ABYSS: - return random2(16); // so it can be black - is this right? {dlb} - - case DNGN_ENTER_LABYRINTH: - case DNGN_STONE_ARCH: - return (LIGHTGREY); - - case DNGN_ENTER_PANDEMONIUM: - return (LIGHTBLUE); - - case DNGN_EXIT_PANDEMONIUM: - // Exit pandemonium gates won't show up on the map as light blue - // unless the character has the "gate to pandemonium" demonspawn - // mutation. This is so that the player can't quickly use a - // crystal ball to find their way out. -- bwr - return (you.mutation[MUT_PANDEMONIUM] ? LIGHTBLUE : LIGHTGREEN); - - case DNGN_TRANSIT_PANDEMONIUM: - return (LIGHTGREEN); - - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - return (MAGENTA); - - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - return (RED); - - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - return (GREEN); - - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - return (LIGHTRED); - - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - return (LIGHTBLUE); - - default: - break; - } - - return tc; -} - -void clear_map() -{ - for (int y = 0; y < GYM - 1; ++y) - { - for (int x = 0; x < GXM - 1; ++x) - { - unsigned short envc = env.map[x][y]; - if (!envc) - continue; - - bool unmapped = (envc & ENVF_DETECTED) != 0; - // Discard flags at this point. - envc = (unsigned char) envc; - - const unsigned char &grdc = grd[x + 1][y + 1]; - if (envc == mapch(grdc) || envc == mapch2(grdc)) - continue; - - int item = igrd[x + 1][y + 1]; - if (item != NON_ITEM - && envc == display_glyph(item_env_glyph(mitm[item]))) - continue; - - env.map[x][y] = unmapped? 0 : mapch2(grdc); - } - } -} - -void monster_grid(bool do_updates) -{ - struct monsters *monster = 0; // NULL {dlb} - - for (int s = 0; s < MAX_MONSTERS; s++) - { - monster = &menv[s]; + for (int s = 0; s < MAX_MONSTERS; s++) + { + monster = &menv[s]; if (monster->type != -1 && mons_near(monster)) { @@ -1104,7 +530,7 @@ void monster_grid(bool do_updates) { behaviour_event( monster, ME_ALERT, MHITYOU ); - if (you.turn_is_over == 1 + if (you.turn_is_over && mons_shouts(monster->type) > 0 && random2(30) >= you.skills[SK_STEALTH]) { @@ -1194,127 +620,50 @@ void monster_grid(bool do_updates) } } + const int ex = monster->x - you.x_pos + 9; + const int ey = monster->y - you.y_pos + 9; + if (!player_monster_visible( monster )) { // ripple effect? if (grd[monster->x][monster->y] == DNGN_SHALLOW_WATER && !mons_flies(monster)) { - show_backup[ monster->x - you.x_pos + 9 ] - [ monster->y - you.y_pos + 9] - = env.show[ monster->x - you.x_pos + 9 ] - [ monster->y - you.y_pos + 9 ]; - env.show[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] = 257; + set_show_backup(ex, ey); + env.show[ex][ey] = DNGN_INVIS_EXPOSED; + env.show_col[ex][ey] = BLUE; } continue; } else if (!mons_friendly( monster ) && !mons_is_mimic( monster->type ) - && !mons_flag( monster->type, M_NO_EXP_GAIN )) + && !mons_class_flag( monster->type, M_NO_EXP_GAIN )) { - interrupt_activity( AI_SEE_MONSTER ); - if (you.running != 0 -#ifdef CLUA_BINDINGS - && clua.callbooleanfn(true, "ch_stop_run", - "M", monster) -#endif - ) - { - // Friendly monsters, mimics, or harmless monsters - // don't disturb the player's running/resting. - // - // Doing it this way causes players in run mode 2 - // to move one square, and in mode 1 to stop. This - // means that the character will run one square if - // a monster is in sight... we automatically jump - // to zero if we're resting. -- bwr - if (you.run_x == 0 && you.run_y == 0) - stop_running(); - else if (you.running > 1) - you.running--; - else - stop_running(); - } + interrupt_activity( AI_SEE_MONSTER, monster ); + seen_monster( monster ); } // mimics are always left on map if (!mons_is_mimic( monster->type )) - { - show_backup[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - = env.show[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - } + set_show_backup(ex, ey); - env.show[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] = monster->type + 297; + env.show[ex][ey] = monster->type + DNGN_START_OF_MONSTERS; + env.show_col[ex][ey] = monster->colour; - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - = ((mcolour[monster->type] == BLACK) - ? monster->number : mcolour[monster->type]); -#ifdef USE_COLOUR_OPTS if (mons_friendly(monster)) { - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - |= COLFLAG_FRIENDLY_MONSTER; + env.show_col[ex][ey] |= COLFLAG_FRIENDLY_MONSTER; } else if (Options.stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_is_stabbable(monster)) + && mons_looks_stabbable(monster)) { - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - |= COLFLAG_WILLSTAB; + env.show_col[ex][ey] |= COLFLAG_WILLSTAB; } else if (Options.may_stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_maybe_stabbable(monster)) - { - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - |= COLFLAG_MAYSTAB; - } - -#elif defined(WIN32CONSOLE) || defined(DOS) - if (Options.friend_brand != CHATTR_NORMAL - && mons_friendly(monster)) + && mons_looks_distracted(monster)) { - // We munge the colours right here for DOS and Windows, because - // we know exactly how the colours will be handled, and we don't - // want to change both DOS and Windows port code to handle - // friend branding. - unsigned short &colour = - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - colour = dos_brand(colour, Options.friend_brand); + env.show_col[ex][ey] |= COLFLAG_MAYSTAB; } - - if (Options.stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_is_stabbable(monster)) - { - unsigned short &colour = - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - colour = dos_brand(colour, Options.stab_brand); - } - else if (Options.may_stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_maybe_stabbable(monster)) - { - unsigned short &colour = - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - colour = dos_brand(colour, Options.may_stab_brand); - } -#endif } // end "if (monster->type != -1 && mons_ner)" } // end "for s" } // end monster_grid() @@ -1324,7 +673,7 @@ bool check_awaken(int mons_aw) { int mons_perc = 0; struct monsters *monster = &menv[mons_aw]; - const int mon_holy = mons_holiness( monster->type ); + const int mon_holy = mons_holiness(monster); // berserkers aren't really concerned about stealth if (you.berserker) @@ -1344,9 +693,9 @@ bool check_awaken(int mons_aw) + mons_see_invis(monster) * 5; // critters that are wandering still have MHITYOU as their foe are - // still actively on guard for the player, even if they can't see + // still actively on guard for the player, even if they can't see // him. Give them a large bonus (handle_behaviour() will nuke 'foe' - // after a while, removing this bonus. + // after a while, removing this bonus. if (monster->behaviour == BEH_WANDER && monster->foe == MHITYOU) mons_perc += 15; @@ -1380,58 +729,52 @@ bool check_awaken(int mons_aw) return (random2(stealth) <= mons_perc); } // end check_awaken() -static int display_glyph(int env_glyph) +static void set_show_backup( int ex, int ey ) { - unsigned short ch, color; - if (viewwindow == viewwindow2) - get_ibm_symbol(env_glyph, &ch, &color); - else - get_non_ibm_symbol(env_glyph, &ch, &color); - return ch; + // Must avoid double setting it. + // We want the base terrain/item, not the cloud or monster that replaced it. + if (!Show_Backup[ex][ey]) + Show_Backup[ex][ey] = env.show[ex][ey]; } -static int item_env_glyph(const item_def &item) +static int get_item_dngn_code(const item_def &item) { switch (item.base_type) { case OBJ_ORBS: - return 256; - // need + 6 because show is 0 - 12, not -6 - +6 + return (DNGN_ITEM_ORB); case OBJ_WEAPONS: + return (DNGN_ITEM_WEAPON); case OBJ_MISSILES: - return 258; + return (DNGN_ITEM_MISSILE); case OBJ_ARMOUR: - return 259; + return (DNGN_ITEM_ARMOUR); case OBJ_WANDS: - return 260; + return (DNGN_ITEM_WAND); case OBJ_FOOD: - return 261; - case OBJ_UNKNOWN_I: - return 262; + return (DNGN_ITEM_FOOD); case OBJ_SCROLLS: - return 263; + return (DNGN_ITEM_SCROLL); case OBJ_JEWELLERY: - return item.sub_type >= AMU_RAGE? 273 : 264; + return (jewellery_is_amulet(item)? DNGN_ITEM_AMULET : DNGN_ITEM_RING); case OBJ_POTIONS: - return 265; - case OBJ_UNKNOWN_II: - return 266; + return (DNGN_ITEM_POTION); case OBJ_BOOKS: - return 267; + return (DNGN_ITEM_BOOK); case OBJ_STAVES: - return 269; + return (DNGN_ITEM_STAVE); case OBJ_MISCELLANY: - return 270; + return (DNGN_ITEM_MISCELLANY); case OBJ_CORPSES: - return 271; + return (DNGN_ITEM_CORPSE); case OBJ_GOLD: - return 272; + return (DNGN_ITEM_GOLD); default: - return '8'; + return (DNGN_ITEM_ORB); // bad item character } } -void item() +void item_grid() { char count_x, count_y; @@ -1443,34 +786,22 @@ void item() { if (igrd[count_x][count_y] != NON_ITEM) { - if (env.show[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9] != 0) + const int ix = count_x - you.x_pos + 9; + const int iy = count_y - you.y_pos + 9; + if (env.show[ix][iy]) { const item_def &eitem = mitm[igrd[count_x][count_y]]; - unsigned short &ecol = - env.show_col[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9]; + unsigned short &ecol = env.show_col[ix][iy]; ecol = (grd[count_x][count_y] == DNGN_SHALLOW_WATER)? CYAN : eitem.colour; -#ifdef USE_COLOUR_OPTS if (eitem.link != NON_ITEM) { ecol |= COLFLAG_ITEM_HEAP; } -#elif defined(WIN32CONSOLE) || defined(DOS) - if (eitem.link != NON_ITEM - && Options.heap_brand != CHATTR_NORMAL) - { - // Yes, exact same code as friend-branding. - ecol = dos_brand(ecol, Options.heap_brand); - } -#endif - env.show[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9] = - item_env_glyph( eitem ); + env.show[ix][iy] = get_item_dngn_code( eitem ); } } } @@ -1484,7 +815,7 @@ void cloud_grid(void) int mnc = 0; // btw, this is also the 'default' color {dlb} - unsigned char which_color = LIGHTGREY; + unsigned char which_colour = LIGHTGREY; for (int s = 0; s < MAX_CLOUDS; s++) { @@ -1499,94 +830,105 @@ void cloud_grid(void) if (see_grid(env.cloud[s].x, env.cloud[s].y)) { - show_backup[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9] - = env.show[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9]; - - env.show[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9] = '#'; - + const int ex = env.cloud[s].x - you.x_pos + 9; + const int ey = env.cloud[s].y - you.y_pos + 9; + switch (env.cloud[s].type) { case CLOUD_FIRE: case CLOUD_FIRE_MON: if (env.cloud[s].decay <= 20) - which_color = RED; + which_colour = RED; else if (env.cloud[s].decay <= 40) - which_color = LIGHTRED; + which_colour = LIGHTRED; else if (one_chance_in(4)) - which_color = RED; + which_colour = RED; else if (one_chance_in(4)) - which_color = LIGHTRED; + which_colour = LIGHTRED; else - which_color = YELLOW; + which_colour = YELLOW; break; case CLOUD_STINK: case CLOUD_STINK_MON: - which_color = GREEN; + which_colour = GREEN; break; case CLOUD_COLD: case CLOUD_COLD_MON: if (env.cloud[s].decay <= 20) - which_color = BLUE; + which_colour = BLUE; else if (env.cloud[s].decay <= 40) - which_color = LIGHTBLUE; + which_colour = LIGHTBLUE; else if (one_chance_in(4)) - which_color = BLUE; + which_colour = BLUE; else if (one_chance_in(4)) - which_color = LIGHTBLUE; + which_colour = LIGHTBLUE; else - which_color = WHITE; + which_colour = WHITE; break; case CLOUD_POISON: case CLOUD_POISON_MON: - which_color = (one_chance_in(3) ? LIGHTGREEN : GREEN); + which_colour = (one_chance_in(3) ? LIGHTGREEN : GREEN); break; case CLOUD_BLUE_SMOKE: case CLOUD_BLUE_SMOKE_MON: - which_color = LIGHTBLUE; + which_colour = LIGHTBLUE; break; case CLOUD_PURP_SMOKE: case CLOUD_PURP_SMOKE_MON: - which_color = MAGENTA; + which_colour = MAGENTA; break; case CLOUD_MIASMA: case CLOUD_MIASMA_MON: case CLOUD_BLACK_SMOKE: case CLOUD_BLACK_SMOKE_MON: - which_color = DARKGREY; + which_colour = DARKGREY; break; default: - which_color = LIGHTGREY; + which_colour = LIGHTGREY; break; } - env.show_col[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9] = which_color; + set_show_backup(ex, ey); + env.show[ex][ey] = DNGN_CLOUD; + env.show_col[ex][ey] = which_colour; } } // end 'if != CLOUD_NONE' } // end 'for s' loop } // end cloud_grid() - -void noisy( int loudness, int nois_x, int nois_y ) +// Noisy now has a messenging service for giving messages to the +// player is appropriate. +// +// Returns true if the PC heard the noise. +bool noisy( int loudness, int nois_x, int nois_y, const char *msg ) { int p; struct monsters *monster = 0; // NULL {dlb} + bool ret = false; + // If the origin is silenced there is no noise. if (silenced( nois_x, nois_y )) - return; + return (false); - int dist = loudness * loudness; + const int dist = loudness * loudness; + + // message the player + if (distance( you.x_pos, you.y_pos, nois_x, nois_y ) <= dist + && player_can_hear( nois_x, nois_y )) + { + if (msg) + mpr( msg, MSGCH_SOUND ); + ret = true; + } + for (p = 0; p < MAX_MONSTERS; p++) { monster = &menv[p]; @@ -1605,21 +947,19 @@ void noisy( int loudness, int nois_x, int nois_y ) behaviour_event( monster, ME_DISTURB, MHITNOT, nois_x, nois_y ); } } + + return (ret); } // end noisy() -/* ======================================================================== - * brand new LOS code - * ======================================================================== - * The new LOS works via a new (I think) shadow casting algorithm, - * plus an esthetic tweak for more pleasing corner illumination. More - * detail can be had by contacting its author, Gordon Lipford. */ +/* The LOS code now uses raycasting -- haranp */ -#define MAX_LIGHT_RADIUS 20 -#define CIRC_MAX 32000 -#define BIG_SHADOW 32000 +#define LONGSIZE (sizeof(unsigned long)*8) +#define LOS_MAX_RANGE_X 9 +#define LOS_MAX_RANGE_Y 9 +#define LOS_MAX_RANGE 9 // the following two constants represent the 'middle' of the sh array. -// since the current shown area is 19x19, centering the view at (9,9) +// since the current shown area is 19x19, centering the view at (9,9) // means it will be exactly centered. // This is done to accomodate possible future changes in viewable screen // area - simply change sh_xo and sh_yo to the new view center. @@ -1627,340 +967,648 @@ void noisy( int loudness, int nois_x, int nois_y ) const int sh_xo = 9; // X and Y origins for the sh array const int sh_yo = 9; -// the Cell class, used in the shadow-casting LOS algorithm -class Cell -{ +// Data used for the LOS algorithm +int los_radius_squared = 8*8 + 1; -public: - int up_count; - int up_max; - int low_count; - int low_max; - bool lit; - bool lit_delay; - bool visible; // for blockers only - void init(); - bool reachedLower(); - bool reachedUpper(); - - Cell() - { - init(); - }; -}; +unsigned long* los_blockrays = NULL; +unsigned long* dead_rays = NULL; +std::vector ray_coord_x; +std::vector ray_coord_y; +std::vector compressed_ray_x; +std::vector compressed_ray_y; +std::vector raylengths; +std::vector fullrays; -void Cell::init() +void setLOSRadius(int newLR) { - up_count = 0; - up_max = 0; - low_count = 0; - low_max = 0; - lit = true; - visible = true; - lit_delay = false; + los_radius_squared = newLR * newLR + 1*1; } -bool Cell::reachedLower() +bool get_bit_in_long_array( const unsigned long* data, int where ) { - // integer math: a 'step' has a value of 10 - // see if we're within a half step of the max. VERY important - // to use 'half step' or else things look really stupid. - if (low_max != 0 && low_count + 5 >= low_max && low_count - 5 < low_max) - return true; + int wordloc = where / LONGSIZE; + int bitloc = where % LONGSIZE; + return ((data[wordloc] & (1UL << bitloc)) != 0); +} - return false; +static void set_bit_in_long_array( unsigned long* data, int where ) { + int wordloc = where / LONGSIZE; + int bitloc = where % LONGSIZE; + data[wordloc] |= (1UL << bitloc); } -bool Cell::reachedUpper() +#define EPSILON_VALUE 0.00001 +bool double_is_zero( const double x ) { - // see if we're within a half step of the max. VERY important - // to use 'half step' or else things look really stupid. - if (up_max != 0 && up_count + 5 >= up_max && up_count - 5 < up_max) - return true; - - return false; + return (x > -EPSILON_VALUE) && (x < EPSILON_VALUE); } -// the cell array -static FixedVector < Cell, MAX_LIGHT_RADIUS + 1 > cells; +// note that slope must be nonnegative! +// returns 0 if the advance was in x, 1 if it was in y, 2 if it was +// the diagonal +static int find_next_intercept(double* accx, double* accy, const double slope) +{ + + // handle perpendiculars + if ( double_is_zero(slope) ) + { + *accx += 1.0; + return 0; + } + if ( slope > 100.0 ) + { + *accy += 1.0; + return 1; + } + + const double xtarget = (double)((int)(*accx) + 1); + const double ytarget = (double)((int)(*accy) + 1); + const double xdistance = xtarget - *accx; + const double ydistance = ytarget - *accy; + const double distdiff = (xdistance * slope - ydistance); + + // exact corner + if ( double_is_zero( distdiff ) ) { + // move somewhat away from the corner + if ( slope > 1.0 ) { + *accx = xtarget + EPSILON_VALUE * 2; + *accy = ytarget + EPSILON_VALUE * 2 * slope; + } + else { + *accx = xtarget + EPSILON_VALUE * 2 / slope; + *accy = ytarget + EPSILON_VALUE * 2; + } + return 2; + } + + double traveldist; + int rc = -1; + if ( distdiff > 0.0 ) { + traveldist = ydistance / slope; + rc = 1; + } + else { + traveldist = xdistance; + rc = 0; + } -// the 'circle' array. For any given row, we won't check higher than -// this given cell. -static FixedVector < int, MAX_LIGHT_RADIUS + 1 > circle; + traveldist += EPSILON_VALUE * 10.0; -// current light radius -static int LR = 0; + *accx += traveldist; + *accy += traveldist * slope; + return rc; +} -// View constant -const int view = 2; // 1=widest LOS .. 5=narrowest +void ray_def::advance_and_bounce() +{ + // 0 = down-right, 1 = down-left, 2 = up-left, 3 = up-right + int bouncequad[4][3] = { + { 1, 3, 2 }, { 0, 2, 3 }, { 3, 1, 0 }, { 2, 0, 1 } + }; + int oldx = x(), oldy = y(); + int rc = advance(); + int newx = x(), newy = y(); + ASSERT( grid_is_solid(grd[newx][newy]) ); + if ( double_is_zero(slope) || slope > 100.0 ) + quadrant = bouncequad[quadrant][2]; + else if ( rc != 2 ) + quadrant = bouncequad[quadrant][rc]; + else + { + ASSERT( (oldx != newx) && (oldy != newy) ); + bool blocked_x = grid_is_solid(grd[oldx][newy]); + bool blocked_y = grid_is_solid(grd[newx][oldy]); + if ( blocked_x && blocked_y ) + quadrant = bouncequad[quadrant][rc]; + else if ( blocked_x ) + quadrant = bouncequad[quadrant][1]; + else + quadrant = bouncequad[quadrant][0]; + } + advance(); +} -// initialize LOS code for a given light radius -extern void setLOSRadius(int newLR) +void ray_def::regress() { - int i, j; + int opp_quadrant[4] = { 2, 3, 0, 1 }; + quadrant = opp_quadrant[quadrant]; + advance(); + quadrant = opp_quadrant[quadrant]; +} - // sanity check - also allows multiple calls w/out performance loss - if (LR == newLR) - return; +int ray_def::advance() +{ + int rc; + switch ( quadrant ) + { + case 0: + // going down-right + rc = find_next_intercept( &accx, &accy, slope ); + return rc; + case 1: + // going down-left + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + rc = find_next_intercept( &accx, &accy, slope ); + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + return rc; + case 2: + // going up-left + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + rc = find_next_intercept( &accx, &accy, slope ); + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + return rc; + case 3: + // going up-right + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + rc = find_next_intercept( &accx, &accy, slope ); + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + return rc; + default: + return -1; + } +} - LR = newLR; - // cells should already be initted. calculate the circle array. +// Shoot a ray from the given start point (accx, accy) with the given +// slope, with a maximum distance (in either x or y coordinate) of +// maxrange. Store the visited cells in xpos[] and ypos[], and +// return the number of cells visited. +static int shoot_ray( double accx, double accy, const double slope, + int maxrange, int xpos[], int ypos[] ) +{ + int curx, cury; + int cellnum; + for ( cellnum = 0; true; ++cellnum ) + { + find_next_intercept( &accx, &accy, slope ); + curx = (int)(accx); + cury = (int)(accy); + if ( curx > maxrange || cury > maxrange ) + break; - // note that rows 0 and 1 will always go to infinity. - circle[0] = circle[1] = CIRC_MAX; + // work with the new square + xpos[cellnum] = curx; + ypos[cellnum] = cury; + } + return cellnum; +} - // for the rest, simply calculate max height based on light rad. - for (i = 2; i <= LR; i++) +// check if the passed ray has already been created +static bool is_duplicate_ray( int len, int xpos[], int ypos[] ) +{ + int cur_offset = 0; + for ( unsigned int i = 0; i < raylengths.size(); ++i ) { - // check top - if (2 * i * i <= LR * LR) + // only compare equal-length rays + if ( raylengths[i] != len ) { - circle[i] = CIRC_MAX; + cur_offset += raylengths[i]; continue; } - for (j = i - 1; j >= 0; j--) + int j; + for ( j = 0; j < len; ++j ) { - // check that Distance (I^2 + J^2) is no more than (R+0.5)^2 - // this rounding allows for *much* better looking circles. - if (i * i + j * j <= LR * LR + LR) - { - circle[i] = j; + if ( ray_coord_x[j + cur_offset] != xpos[j] || + ray_coord_y[j + cur_offset] != ypos[j] ) break; - } } + + // exact duplicate? + if ( j == len ) + return true; + + // move to beginning of next ray + cur_offset += raylengths[i]; } + return false; } -static int calcUpper(int bX, int bY) +// is starta...lengtha a subset of startb...lengthb? +static bool is_subset( int starta, int startb, int lengtha, int lengthb ) { - // got a blocker at row bX, cell bY. do all values - // and scale by a factor of 10 for the integer math. - int upper; + int cura = starta, curb = startb; + int enda = starta + lengtha, endb = startb + lengthb; + while ( cura < enda && curb < endb ) + { + if ( ray_coord_x[curb] > ray_coord_x[cura] ) + return false; + if ( ray_coord_y[curb] > ray_coord_y[cura] ) + return false; + if ( ray_coord_x[cura] == ray_coord_x[curb] && + ray_coord_y[cura] == ray_coord_y[curb] ) + ++cura; - upper = (10 * (10 * bX - view)) / (10 * bY + view); - if (upper < 10) // upper bound for blocker on diagonal - upper = 10; + ++curb; + } + return ( cura == enda ); +} - return upper; +// return a vector which lists all the nonduped cellrays (by index) +static std::vector find_nonduped_cellrays() +{ + // a cellray c in a fullray f is duped if there is a fullray g + // such that g contains c and g[:c] is a subset of f[:c] + int raynum, cellnum, curidx, testidx, testray, testcell; + bool is_duplicate; + + std::vector result; + for (curidx=0, raynum=0; + raynum < (int)raylengths.size(); + curidx += raylengths[raynum++]) + { + for (cellnum = 0; cellnum < raylengths[raynum]; ++cellnum) + { + // is the cellray raynum[cellnum] duplicated? + is_duplicate = false; + // XXX We should really check everything up to now + // completely, and all further rays to see if they're + // proper subsets. + const int curx = ray_coord_x[curidx + cellnum]; + const int cury = ray_coord_y[curidx + cellnum]; + for (testidx = 0, testray = 0; testray < raynum; + testidx += raylengths[testray++]) + { + // scan ahead to see if there's an intersect + for ( testcell = 0; testcell < raylengths[raynum]; ++testcell ) + { + const int testx = ray_coord_x[testidx + testcell]; + const int testy = ray_coord_y[testidx + testcell]; + // we can short-circuit sometimes + if ( testx > curx || testy > cury ) + break; + // bingo! + if ( testx == curx && testy == cury ) + { + is_duplicate = is_subset(testidx, curidx, + testcell, cellnum); + break; + } + } + if ( is_duplicate ) + break; // no point in checking further rays + } + if ( !is_duplicate ) + result.push_back(curidx + cellnum); + } + } + return result; } -static int calcLower(int bX, int bY) +// Create and register the ray defined by the arguments. +// Return true if the ray was actually registered (i.e., not a duplicate.) +static bool register_ray( double accx, double accy, double slope ) { - // got a blocker at row bX, cell bY. do all values - // and scale by a factor of 10 for the integer math. + int xpos[LOS_MAX_RANGE * 2 + 1], ypos[LOS_MAX_RANGE * 2 + 1]; + int raylen = shoot_ray( accx, accy, slope, LOS_MAX_RANGE, xpos, ypos ); - if (bY == 0) - return BIG_SHADOW; + // early out if ray already exists + if ( is_duplicate_ray(raylen, xpos, ypos) ) + return false; - return (10 * (10 * bX + view)) / (10 * bY - view); -} + // not duplicate, register + for ( int i = 0; i < raylen; ++i ) + { + // create the cellrays + ray_coord_x.push_back(xpos[i]); + ray_coord_y.push_back(ypos[i]); + } -// for easy x,y octant translation -static int xxcomp[8] = { 1, 0, 0, -1, -1, 0, 0, 1 }; -static int xycomp[8] = { 0, 1, -1, 0, 0, -1, 1, 0 }; -static int yxcomp[8] = { 0, 1, 1, 0, 0, -1, -1, 0 }; -static int yycomp[8] = { 1, 0, 0, 1, -1, 0, 0, -1 }; + // register the fullray + raylengths.push_back(raylen); + ray_def ray; + ray.accx = accx; + ray.accy = accy; + ray.slope = slope; + ray.quadrant = 0; + fullrays.push_back(ray); -static void los_octant(int o, FixedArray < unsigned int, 19, 19 > &sh, - FixedArray < unsigned char, 80, 70 > &gr, int x_p, - int y_p) + return true; +} + +static void create_blockrays() { - int row, cell, top, south; - int tx, ty; // translated x, y deltas for this octant - unsigned char gv; // grid value - bool row_dark, all_dark; - bool blocker, vis_corner; - int up_inc, low_inc; - - // leave [0,0] alone, because the old LOS code seems to. - - // init cell[0]. this is the only one that needs clearing. - cells[0].init(); - all_dark = false; - vis_corner = false; - - // loop through each row - for (row = 1; row <= LR; row++) + // determine nonduplicated rays + std::vector nondupe_cellrays = find_nonduped_cellrays(); + const unsigned int num_nondupe_rays = nondupe_cellrays.size(); + const unsigned int num_nondupe_words = + (num_nondupe_rays + LONGSIZE - 1) / LONGSIZE; + const unsigned int num_cellrays = ray_coord_x.size(); + const unsigned int num_words = (num_cellrays + LONGSIZE - 1) / LONGSIZE; + + // first build all the rays: easier to do blocking calculations there + unsigned long* full_los_blockrays; + full_los_blockrays = new unsigned long[num_words * (LOS_MAX_RANGE_X+1) * + (LOS_MAX_RANGE_Y+1)]; + memset((void*)full_los_blockrays, 0, sizeof(unsigned long) * num_words * + (LOS_MAX_RANGE_X+1) * (LOS_MAX_RANGE_Y+1)); + + int cur_offset = 0; + + for ( unsigned int ray = 0; ray < raylengths.size(); ++ray ) { - row_dark = true; - - // loop through each cell, up to the max allowed by circle[] - top = circle[row]; - if (top > row) - top = row; - - for (cell = 0; cell <= top; cell++) + for ( int i = 0; i < raylengths[ray]; ++i ) { - // translate X,Y co'ord + bounds check - tx = row * xxcomp[o] + cell * xycomp[o]; - ty = row * yxcomp[o] + cell * yycomp[o]; + // every cell blocks... + unsigned long* const inptr = full_los_blockrays + + (ray_coord_x[i + cur_offset] * (LOS_MAX_RANGE_Y + 1) + + ray_coord_y[i + cur_offset]) * num_words; - if (x_p + tx < 0 || x_p + tx > 79 || y_p + ty < 0 || y_p + ty > 69) - continue; + // ...all following cellrays + for ( int j = i+1; j < raylengths[ray]; ++j ) + set_bit_in_long_array( inptr, j + cur_offset ); - // check for all_dark - we've finished the octant but - // have yet to fill in '0' for the rest of the sight grid - if (all_dark == true) - { - sh[sh_xo + tx][sh_yo + ty] = 0; - continue; - } + } + cur_offset += raylengths[ray]; + } - // get grid value.. see if it blocks LOS - gv = gr[x_p + tx][y_p + ty]; - blocker = (gv < MINSEE); + // we've built the basic blockray array; now compress it, keeping + // only the nonduplicated cellrays. - // init some other variables - up_inc = 10; - low_inc = 10; - south = cell - 1; + // allocate and clear memory + los_blockrays = new unsigned long[num_nondupe_words * (LOS_MAX_RANGE_X+1) * (LOS_MAX_RANGE_Y + 1)]; + memset((void*)los_blockrays, 0, sizeof(unsigned long) * num_nondupe_words * + (LOS_MAX_RANGE_X+1) * (LOS_MAX_RANGE_Y+1)); - // STEP 1 - inherit values from immediate West, if possible - if (cell < row) - { - // check for delayed lighting - if (cells[cell].lit_delay) - { - if (!blocker) - { // blockers don't light up with lit_delay. - if (cells[south].lit) - { - if (cells[south].low_max != 0) - { - cells[cell].lit = false; - // steal lower values - cells[cell].low_max = cells[south].low_max; - cells[cell].low_count = cells[south].low_count; - cells[south].low_count = 0; - cells[south].low_max = 0; - low_inc = 0; // avoid double-inc. - } - else - cells[cell].lit = true; - } - } - cells[cell].lit_delay = false; - } - } - else - { - // initialize new cell. - cells[cell].init(); - } + // we want to only keep the cellrays from nondupe_cellrays. + compressed_ray_x.resize(num_nondupe_rays); + compressed_ray_y.resize(num_nondupe_rays); + for ( unsigned int i = 0; i < num_nondupe_rays; ++i ) + { + compressed_ray_x[i] = ray_coord_x[nondupe_cellrays[i]]; + compressed_ray_y[i] = ray_coord_y[nondupe_cellrays[i]]; + } + unsigned long* oldptr = full_los_blockrays; + unsigned long* newptr = los_blockrays; + for ( int x = 0; x <= LOS_MAX_RANGE_X; ++x ) + { + for ( int y = 0; y <= LOS_MAX_RANGE_Y; ++y ) + { + for ( unsigned int i = 0; i < num_nondupe_rays; ++i ) + if ( get_bit_in_long_array(oldptr, nondupe_cellrays[i]) ) + set_bit_in_long_array(newptr, i); + oldptr += num_words; + newptr += num_nondupe_words; + } + } - // STEP 2 - check for blocker - // a dark blocker in shadow's edge will be visible - if (blocker) - { - if (cells[cell].lit || (cell != 0 && cells[south].lit) - || vis_corner) - { - // hack: make 'corners' visible - vis_corner = cells[cell].lit; + // we can throw away full_los_blockrays now + delete [] full_los_blockrays; - cells[cell].lit = false; - cells[cell].visible = true; + dead_rays = new unsigned long[num_nondupe_words]; + +#ifdef DEBUG_DIAGNOSTICS + mprf( MSGCH_DIAGNOSTICS, "Cellrays: %d Fullrays: %u Compressed: %u", + num_cellrays, raylengths.size(), num_nondupe_rays ); +#endif +} - int upper = calcUpper(row, cell); - int lower = calcLower(row, cell); +static int gcd( int x, int y ) +{ + int tmp; + while ( y != 0 ) + { + x %= y; + tmp = x; + x = y; + y = tmp; + } + return x; +} - if (upper < cells[cell].up_max || cells[cell].up_max == 0) - { - // new upper shadow - cells[cell].up_max = upper; - cells[cell].up_count = 0; - up_inc = 0; - } +// Cast all rays +void raycast() +{ + static bool done_raycast = false; + if ( done_raycast ) + return; + + // Creating all rays for first quadrant + // We have a considerable amount of overkill. + done_raycast = true; + + int xangle, yangle; + + // register perpendiculars FIRST, to make them top choice + // when selecting beams + register_ray( 0.5, 0.5, 1000.0 ); + register_ray( 0.5, 0.5, 0.0 ); + + // For a slope of M = y/x, every x we move on the X axis means + // that we move y on the y axis. We want to look at the resolution + // of x/y: in that case, every step on the X axis means an increase + // of 1 in the Y axis at the intercept point. We can assume gcd(x,y)=1, + // so we look at steps of 1/y. + for ( xangle = 1; xangle <= LOS_MAX_RANGE; ++xangle ) { + for ( yangle = 1; yangle <= LOS_MAX_RANGE; ++yangle ) { + + if ( gcd(xangle, yangle) != 1 ) + continue; - if (lower > cells[cell].low_max || cells[cell].low_max == 0) - { - // new lower shadow - cells[cell].low_max = lower; - cells[cell].low_count = -10; - low_inc = 0; - if (lower <= 30) // somewhat arbitrary - cells[cell].lit_delay = true; - // set dark_delay if lower > 20?? how to decide? - } - } - else - { - cells[cell].visible = false; - } - } - else - { - cells[cell].visible = false; // special flags for blockers + const double slope = ((double)(yangle)) / xangle; + const double rslope = ((double)(xangle)) / yangle; + for ( int intercept = 0; intercept <= yangle; ++intercept ) { + double xstart = ((double)(intercept)) / yangle; + if ( intercept == 0 ) + xstart += EPSILON_VALUE / 10.0; + if ( intercept == yangle ) + xstart -= EPSILON_VALUE / 10.0; + // y should be "about to change" + register_ray( xstart, 1.0 - EPSILON_VALUE / 10.0, slope ); + // also draw the identical ray in octant 2 + register_ray( 1.0 - EPSILON_VALUE / 10.0, xstart, rslope ); } + } + } + + // Now create the appropriate blockrays array + create_blockrays(); +} + +static void set_ray_quadrant( ray_def& ray, int sx, int sy, int tx, int ty ) +{ + if ( tx >= sx && ty >= sy ) + ray.quadrant = 0; + else if ( tx < sx && ty >= sy ) + ray.quadrant = 1; + else if ( tx < sx && ty < sy ) + ray.quadrant = 2; + else if ( tx >= sx && ty < sy ) + ray.quadrant = 3; + else + mpr("Bad ray quadrant!", MSGCH_DIAGNOSTICS); +} - // STEP 3 - add increments to upper, lower counts - cells[cell].up_count += up_inc; - cells[cell].low_count += low_inc; - // STEP 4 - check south for dark - if (south >= 0) - if (cells[south].reachedUpper() == true) - { - if (cells[cell].reachedUpper() == false) +// Find a nonblocked ray from sx, sy to tx, ty. Return false if no +// such ray could be found, otherwise return true and fill ray +// appropriately. +// If allow_fallback is true, fall back to a center-to-center ray +// if range is too great or all rays are blocked. +bool find_ray( int sourcex, int sourcey, int targetx, int targety, + bool allow_fallback, ray_def& ray ) +{ + + int cellray, inray; + const int signx = ((targetx - sourcex >= 0) ? 1 : -1); + const int signy = ((targety - sourcey >= 0) ? 1 : -1); + const int absx = signx * (targetx - sourcex); + const int absy = signy * (targety - sourcey); + int cur_offset = 0; + for ( unsigned int fullray = 0; fullray < fullrays.size(); + cur_offset += raylengths[fullray++] ) { + + for ( cellray = 0; cellray < raylengths[fullray]; ++cellray ) + { + if ( ray_coord_x[cellray + cur_offset] == absx && + ray_coord_y[cellray + cur_offset] == absy ) { + + // check if we're blocked so far + bool blocked = false; + for ( inray = 0; inray < cellray; ++inray ) { + if (grid_is_solid(grd[sourcex + signx * ray_coord_x[inray + cur_offset]][sourcey + signy * ray_coord_y[inray + cur_offset]])) { - cells[cell].up_max = cells[south].up_max; - cells[cell].up_count = cells[south].up_count; - cells[cell].up_count -= cells[south].up_max; + blocked = true; + break; } - cells[cell].lit = false; - cells[cell].visible = false; - } - - // STEP 5 - nuke lower if south lower - if (south >= 0) - { - if (cells[south].reachedLower()) - { - cells[cell].low_max = cells[south].low_max; - cells[cell].low_count = cells[south].low_count; - cells[cell].low_count -= cells[south].low_max; - cells[south].low_count = cells[south].low_max = 0; } - if (cells[south].low_max != 0 - || (cells[south].lit == false - && cells[south].low_max == 0)) + if ( !blocked ) { - cells[cell].low_count = cells[cell].low_max + 10; + // success! + ray = fullrays[fullray]; + if ( sourcex > targetx ) + ray.accx = 1.0 - ray.accx; + if ( sourcey > targety ) + ray.accy = 1.0 - ray.accy; + ray.accx += sourcex; + ray.accy += sourcey; + set_ray_quadrant(ray, sourcex, sourcey, targetx, targety); + return true; } } - - // STEP 6 - light up if we've reached lower bound - if (cells[cell].reachedLower() == true) - cells[cell].lit = true; - - // now place appropriate value in sh - if (cells[cell].lit == true - || (blocker == true && cells[cell].visible == true)) - { - sh[sh_xo + tx][sh_yo + ty] = gv; - } - else - sh[sh_xo + tx][sh_yo + ty] = 0; - - if (cells[cell].lit == true) - row_dark = false; - } // end for - cells - - vis_corner = false; // don't carry over to next row. :) - if (row_dark == true) - all_dark = true; - } // end for - rows + } + } + if ( allow_fallback ) { + ray.accx = sourcex + 0.5; + ray.accy = sourcey + 0.5; + if ( targetx == sourcex ) + ray.slope = 10000.0; + else { + ray.slope = targety - sourcey; + ray.slope /= targetx - sourcex; + if ( ray.slope < 0 ) + ray.slope = -ray.slope; + } + set_ray_quadrant(ray, sourcex, sourcey, targetx, targety); + return true; + } + return false; } +// The rule behind LOS is: +// Two cells can see each other if there is any line from some point +// of the first to some point of the second ("generous" LOS.) +// +// We use raycasting. The algorithm: +// PRECOMPUTATION: +// Create a large bundle of rays and cast them. +// Mark, for each one, which cells kill it (and where.) +// Also, for each one, note which cells it passes. +// ACTUAL LOS: +// Unite the ray-killers for the given map; this tells you which rays +// are dead. +// Look up which cells the surviving rays have, and that's your LOS! +// OPTIMIZATIONS: +// WLOG, we can assume that we're in a specific quadrant - say the +// first quadrant - and just mirror everything after that. We can +// likely get away with a single octant, but we don't do that. (To +// do...) +// Rays are actually split by each cell they pass. So each "ray" only +// identifies a single cell, and we can do logical ORs. Once a cell +// kills a cellray, it will kill all remaining cellrays of that ray. +// Also, rays are checked to see if they are duplicates of each +// other. If they are, they're eliminated. +// Some cellrays can also be eliminated. In general, a cellray is +// unnecessary if there is another cellray with the same coordinates, +// and whose path (up to those coordinates) is a subset, not necessarily +// proper, of the original path. We still store the original cellrays +// fully for beam detection and such. +// PERFORMANCE: +// With reasonable values we have around 6000 cellrays, meaning +// around 600Kb (75 KB) of data. This gets cut down to 700 cellrays +// after removing duplicates. That means that we need to do +// around 22*100*4 ~ 9,000 memory reads + writes per LOS call on a +// 32-bit system. Not too bad. void losight(FixedArray < unsigned int, 19, 19 > &sh, FixedArray < unsigned char, 80, 70 > &gr, int x_p, int y_p) { - int o; + raycast(); + // go quadrant by quadrant + int quadrant_x[4] = { 1, -1, -1, 1 }; + int quadrant_y[4] = { 1, 1, -1, -1 }; + + // clear out sh + for ( int i = 0; i < 19; ++i ) + for ( int j = 0; j < 19; ++j ) + sh[i][j] = 0; + + const unsigned int num_cellrays = compressed_ray_x.size(); + const unsigned int num_words = (num_cellrays + LONGSIZE - 1) / LONGSIZE; + + for ( int quadrant = 0; quadrant < 4; ++quadrant ) { + const int xmult = quadrant_x[quadrant]; + const int ymult = quadrant_y[quadrant]; + + // clear out the dead rays array + memset( (void*)dead_rays, 0, sizeof(unsigned long) * num_words); + + // kill all blocked rays + const unsigned long* inptr = los_blockrays; + for ( int xdiff = 0; xdiff <= LOS_MAX_RANGE_X; ++xdiff ) { + for (int ydiff = 0; ydiff <= LOS_MAX_RANGE_Y; + ++ydiff, inptr += num_words ) { + + const int realx = x_p + xdiff * xmult; + const int realy = y_p + ydiff * ymult; + + if (realx < 0 || realx > 79 || realy < 0 || realy > 69) + continue; + + // if this cell is opaque... + if ( grid_is_opaque(gr[realx][realy])) { + // then block the appropriate rays + for ( unsigned int i = 0; i < num_words; ++i ) + dead_rays[i] |= inptr[i]; + } + } + } - for (o = 0; o < 8; o++) - los_octant(o, sh, gr, x_p, y_p); + // ray calculation done, now work out which cells in this + // quadrant are visible + unsigned int rayidx = 0; + for ( unsigned int wordloc = 0; wordloc < num_words; ++wordloc ) { + const unsigned long curword = dead_rays[wordloc]; + // Note: the last word may be incomplete + for ( unsigned int bitloc = 0; bitloc < LONGSIZE; ++bitloc) { + // make the cells seen by this ray at this point visible + if ( ((curword >> bitloc) & 1UL) == 0 ) { + // this ray is alive! + const int realx = xmult * compressed_ray_x[rayidx]; + const int realy = ymult * compressed_ray_y[rayidx]; + // update shadow map + if (x_p + realx >= 0 && x_p + realx < 80 && + y_p + realy >= 0 && y_p + realy < 70 && + realx * realx + realy * realy <= los_radius_squared ) + sh[sh_xo+realx][sh_yo+realy]=gr[x_p+realx][y_p+realy]; + } + ++rayidx; + if ( rayidx == num_cellrays ) + break; + } + } + } } @@ -2206,11 +1854,34 @@ static int find_feature( const std::vector& features, return 0; } +#ifdef USE_CURSES +// NOTE: This affects libunix.cc draw state; use this just before setting +// textcolour and drawing a character and call set_altcharset(false) +// after you're done drawing. +// +static int cset_adjust(int raw) +{ + if (Options.char_set != CSET_ASCII) + { + // switch to alternate char set for 8-bit characters: + set_altcharset( raw > 127 ); + + // shift the DEC line drawing set: + if (Options.char_set == CSET_DEC + && raw >= 0xE0) + { + raw &= 0x7F; + } + } + return (raw); +} +#endif + // show_map() now centers the known map along x or y. This prevents // the player from getting "artificial" location clues by using the // map to see how close to the end they are. They'll need to explore // to get that. This function is still a mess, though. -- bwr -void show_map( FixedVector &spec_place ) +void show_map( FixedVector &spec_place, bool travel_mode ) { int i, j; @@ -2226,7 +1897,8 @@ void show_map( FixedVector &spec_place ) // Vector to track all features we can travel to, in order of distance. std::vector features; - if (!spec_place[0]) { + if (travel_mode) + { travel_cache.update(); find_travel_pos(you.x_pos, you.y_pos, NULL, NULL, &features); @@ -2336,16 +2008,21 @@ void show_map( FixedVector &spec_place ) colour = colour_code_map(start_x + i, start_y + j, Options.item_colour, - !spec_place[0] && Options.travel_colour); + travel_mode && Options.travel_colour); buffer2[bufcount2 + 1] = colour; - - if (start_x + i + 1 == you.x_pos && start_y + j + 1 == you.y_pos) - buffer2[bufcount2 + 1] = WHITE; - buffer2[bufcount2] = (unsigned char) env.map[start_x + i][start_y + j]; + if (start_x + i + 1 == you.x_pos && start_y + j + 1 == you.y_pos) + { + // [dshaligram] Draw the @ symbol on the level-map. It's no + // longer saved into the env.map, so we need to draw it + // directly. + buffer2[bufcount2 + 1] = WHITE; + buffer2[bufcount2] = you.symbol; + } + // If we've a waypoint on the current square, *and* the square is // a normal floor square with nothing on it, show the waypoint // number. @@ -2355,8 +2032,8 @@ void show_map( FixedVector &spec_place ) screen_buffer_t &bc = buffer2[bufcount2]; int gridx = start_x + i + 1, gridy = start_y + j + 1; unsigned char ch = is_waypoint(gridx, gridy); - if (ch && (bc == mapch2(DNGN_FLOOR) || - bc == mapch(DNGN_FLOOR))) + if (ch && (bc == get_sightmap_char(DNGN_FLOOR) || + bc == get_magicmap_char(DNGN_FLOOR))) bc = ch; } @@ -2373,12 +2050,20 @@ void show_map( FixedVector &spec_place ) if (i == 0 && j > 0) gotoxy( 1, j + 1 ); + int ch = buffer2[bufcount2 - 2]; +#ifdef USE_CURSES + ch = cset_adjust( ch ); +#endif textcolor( buffer2[bufcount2 - 1] ); - putch( buffer2[bufcount2 - 2] ); + putch(ch); #endif } } +#ifdef USE_CURSES + set_altcharset(false); +#endif + #ifdef DOS_TERM puttext(1, 1, 80, 25, buffer2); #endif @@ -2389,7 +2074,7 @@ void show_map( FixedVector &spec_place ) gettything: getty = getchm(KC_LEVELMAP); - if (spec_place[0] == 0 && getty != 0 && getty != '+' && getty != '-' + if (travel_mode && getty != 0 && getty != '+' && getty != '-' && getty != 'h' && getty != 'j' && getty != 'k' && getty != 'l' && getty != 'y' && getty != 'u' && getty != 'b' && getty != 'n' && getty != 'H' && getty != 'J' && getty != 'K' && getty != 'L' @@ -2406,12 +2091,13 @@ void show_map( FixedVector &spec_place ) && getty != CONTROL('F') && getty != CONTROL('W') && getty != CONTROL('C') + && getty != '?' && getty != 'X' && getty != 'F' && getty != 'I' && getty != 'W') { goto putty; } - if (spec_place[0] == 1 && getty != 0 && getty != '+' && getty != '-' + if (!travel_mode && getty != 0 && getty != '+' && getty != '-' && getty != 'h' && getty != 'j' && getty != 'k' && getty != 'l' && getty != 'y' && getty != 'u' && getty != 'b' && getty != 'n' && getty != 'H' && getty != 'J' && getty != 'K' && getty != 'L' @@ -2425,9 +2111,13 @@ void show_map( FixedVector &spec_place ) } if (getty == 0) + { getty = getchm(KC_LEVELMAP); + // [dshaligram] DOS madness. + getty = dos_direction_unmunge(getty); + } -#ifdef WIN32CONSOLE +#if defined(WIN32CONSOLE) || defined(DOS) // Translate shifted numpad to shifted vi keys. Yes, // this is horribly hacky. { @@ -2441,6 +2131,10 @@ void show_map( FixedVector &spec_place ) switch (getty) { + case '?': + show_levelmap_help(); + break; + case CONTROL('C'): clear_map(); break; @@ -2591,7 +2285,7 @@ void show_map( FixedVector &spec_place ) search_feat = getty; search_found = 0; } - if (!spec_place[0]) + if (travel_mode) search_found = find_feature(features, getty, curs_x, curs_y, start_x, start_y, search_found, &move_x, &move_y); @@ -2608,7 +2302,7 @@ void show_map( FixedVector &spec_place ) case ';': { int x = start_x + curs_x, y = start_y + curs_y; - if (!spec_place[0] && x == you.x_pos && y == you.y_pos) + if (travel_mode && x == you.x_pos && y == you.y_pos) { if (you.travel_x > 0 && you.travel_y > 0) { move_x = you.travel_x - x; @@ -2701,1404 +2395,1177 @@ void magic_mapping(int map_radius, int proportion) { int i, j, k, l, empty_count; - if (map_radius > 50) + if (map_radius > 50 && map_radius != 1000) map_radius = 50; + else if (map_radius < 5) + map_radius = 5; + + // now gradually weaker with distance: + const int pfar = (map_radius * 7) / 10; + const int very_far = (map_radius * 9) / 10; for (i = you.x_pos - map_radius; i < you.x_pos + map_radius; i++) { for (j = you.y_pos - map_radius; j < you.y_pos + map_radius; j++) { - if (random2(100) > proportion) + if (proportion < 100 && random2(100) >= proportion) continue; // note that proportion can be over 100 - if (i < 5 || j < 5 || i > (GXM - 5) || j > (GYM - 5)) + if (!map_bounds(i, j)) continue; - //if (env.map[i][j] == mapch2(grd[i + 1][j + 1])) - // continue; - if (env.map[i][j]) + const int dist = grid_distance( you.x_pos, you.y_pos, i, j ); + + if (dist > pfar && one_chance_in(3)) + continue; + + if (dist > very_far && coinflip()) + continue; + + if (is_terrain_changed(i, j)) + clear_envmap_grid(i, j); + + if (is_terrain_known(i, j)) continue; empty_count = 8; - if (grd[i][j] < DNGN_LAVA && grd[i][j] != DNGN_CLOSED_DOOR) + if (grid_is_solid(grd[i][j]) && grd[i][j] != DNGN_CLOSED_DOOR) { - for (k = 0; k < 3; k++) + for (k = -1; k <= 1; k++) { - for (l = 0; l < 3; l++) + for (l = -1; l <= 1; l++) { - if (k == 1 && l == 1) + if (k == 0 && l == 0) continue; - if (grd[i + k][j + l] <= 60 - && grd[i + k][j + l] != DNGN_CLOSED_DOOR) + if (!map_bounds( i + k, j + l )) { - empty_count--; + --empty_count; + continue; } + + if (grid_is_solid( grd[i + k][j + l] ) + && grd[i + k][j + l] != DNGN_CLOSED_DOOR) + empty_count--; } } } - if (empty_count > 0) - env.map[i][j] = mapch(grd[i + 1][j + 1]); - } + if (empty_count > 0) + { + if (!get_envmap_char(i, j)) + set_envmap_char(i, j, get_magicmap_char(grd[i][j])); + + // Hack to give demonspawn Pandemonium mutation the ability + // to detect exits magically. + if ((you.mutation[MUT_PANDEMONIUM] > 1 + && grd[i][j] == DNGN_EXIT_PANDEMONIUM) + // Wizmode + || map_radius == 1000) + set_terrain_seen( i, j ); + else + set_terrain_mapped( i, j ); + } + } + } +} // end magic_mapping() + +// realize that this is simply a repackaged version of +// stuff::see_grid() -- make certain they correlate {dlb}: +bool mons_near(struct monsters *monster, unsigned int foe) +{ + // early out -- no foe! + if (foe == MHITNOT) + return (false); + + if (foe == MHITYOU) + { + if (monster->x > you.x_pos - 9 && monster->x < you.x_pos + 9 + && monster->y > you.y_pos - 9 && monster->y < you.y_pos + 9) + { + if (env.show[monster->x - you.x_pos + 9][monster->y - you.y_pos + 9]) + return (true); + } + return (false); + } + + // must be a monster + struct monsters *myFoe = &menv[foe]; + if (myFoe->type >= 0) + { + if (monster->x > myFoe->x - 9 && monster->x < myFoe->x + 9 + && monster->y > myFoe->y - 9 && monster->y < myFoe->y + 9) + { + return (true); + } + } + + return (false); +} // end mons_near() + +// answers the question: "Is a grid within character's line of sight?" +bool see_grid( int grx, int gry ) +{ + // rare case: can player see self? (of course!) + if (grx == you.x_pos && gry == you.y_pos) + return (true); + + // check env.show array + if (grid_distance( grx, gry, you.x_pos, you.y_pos ) < 9) + { + const int ex = grx - you.x_pos + 9; + const int ey = gry - you.y_pos + 9; + + if (env.show[ex][ey]) + return (true); } -} // end magic_mapping() + return (false); +} // end see_grid() -/* mapchars 3 & 4 are for non-ibm char sets */ -unsigned char mapchar(unsigned char ldfk) +static const unsigned char table[ NUM_CSET ][ NUM_DCHAR_TYPES ] = { - unsigned char showed = 0; + // CSET_ASCII + { + '#', '*', '.', ',', '\'', '+', '^', '>', '<', // wall, stairs up + '_', '\\', '}', '{', '8', '~', '~', // altar, item detect + '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb, missile + ':', '|', '}', '%', '$', '"', '#', // book, cloud + }, - switch (ldfk) + // CSET_IBM - this is ANSI 437 { - case DNGN_UNSEEN: - showed = 0; - break; + 177, 176, 249, 250, '\'', 254, '^', '>', '<', // wall, stairs up + 220, 239, 244, 247, '8', '~', '~', // altar, item detect + '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb, missile + '+', '\\', '}', '%', '$', '"', '#', // book, cloud + }, - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = 176; - break; + // CSET_DEC - remember: 224-255 are mapped to shifted 96-127 + { + 225, 224, 254, ':', '\'', 238, '^', '>', '<', // wall, stairs up + 251, 182, 167, 187, '8', 171, 168, // altar, item detect + '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb, missile + '+', '\\', '}', '%', '$', '"', '#', // book, cloud + }, +}; - case DNGN_CLOSED_DOOR: - showed = 206; - break; +static unsigned char cset_override[NUM_CSET][NUM_DCHAR_TYPES]; - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; +dungeon_char_type dchar_by_name(const std::string &name) +{ + const char *dchar_names[] = + { + "wall", "wall_magic", "floor", "floor_magic", "door_open", + "door_closed", "trap", "stairs_down", "stairs_up", "altar", "arch", + "fountain", "wavy", "statue", "invis_exposed", "item_detected", + "item_orb", "item_weapon", "item_armour", "item_wand", "item_food", + "item_scroll", "item_ring", "item_potion", "item_missile", "item_book", + "item_stave", "item_miscellany", "item_corpse", "item_gold", + "item_amulet", "cloud" + }; + for (unsigned i = 0; i < sizeof(dchar_names) / sizeof(*dchar_names); ++i) + { + if (dchar_names[i] == name) + return dungeon_char_type(i); + } + return (NUM_DCHAR_TYPES); +} - case DNGN_LAVA_X: - case DNGN_WATER_X: - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = 247; - break; +void clear_cset_overrides() +{ + memset(cset_override, 0, sizeof cset_override); +} - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = 250; - break; +static unsigned short read_symbol(std::string s) +{ + if (s.empty()) + return (0); + if (s.length() == 1) + return s[0]; - //case 68: showed = '>'; break; // < (60) + if (s[0] == '\\') + s = s.substr(1); + + int feat = atoi(s.c_str()); + if (feat < 0) + feat = 0; + return static_cast(feat); +} - case DNGN_OPEN_DOOR: - showed = 39; - break; +void add_cset_override(char_set_type set, dungeon_char_type dc, + unsigned char symbol) +{ + cset_override[set][dc] = symbol; +} - //case 72: showed = '<'; break; +void add_cset_override(char_set_type set, const std::string &overrides) +{ + std::vector overs = split_string(",", overrides); + for (int i = 0, size = overs.size(); i < size; ++i) + { + std::vector mapping = split_string(":", overs[i]); + if (mapping.size() != 2) + continue; + + dungeon_char_type dc = dchar_by_name(mapping[0]); + if (dc == NUM_DCHAR_TYPES) + continue; + + unsigned char symbol = + static_cast(read_symbol(mapping[1])); - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; + if (set == NUM_CSET) + for (int c = 0; c < NUM_CSET; ++c) + add_cset_override(char_set_type(c), dc, symbol); + else + add_cset_override(set, dc, symbol); + } +} - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; +void init_char_table( char_set_type set ) +{ + for (int i = 0; i < NUM_DCHAR_TYPES; i++) + { + if (cset_override[set][i]) + Options.char_table[i] = cset_override[set][i]; + else + Options.char_table[i] = table[set][i]; + } +} - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; +void clear_feature_overrides() +{ + Feature_Overrides.clear(); +} - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = 239; - break; +void add_feature_override(const std::string &text) +{ + std::string::size_type epos = text.rfind("}"); + if (epos == std::string::npos) + return; - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = 220; - break; + std::string::size_type spos = text.rfind("{", epos); + if (spos == std::string::npos) + return; + + std::string fname = text.substr(0, spos); + std::string props = text.substr(spos + 1, epos - spos - 1); + std::vector iprops = split_string(",", props, true, true); - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = 159; - break; + if (iprops.size() < 1 || iprops.size() > 5) + return; - default: - showed = 0; - break; - } + if (iprops.size() < 5) + iprops.resize(5); - return showed; -} + trim_string(fname); + std::vector feats = features_by_desc(fname); + if (feats.empty()) + return; + + for (int i = 0, size = feats.size(); i < size; ++i) + { + feature_override fov; + fov.feat = feats[i]; + + fov.override.symbol = read_symbol(iprops[0]); + fov.override.magic_symbol = read_symbol(iprops[1]); + fov.override.colour = str_to_colour(iprops[2], BLACK); + fov.override.map_colour = str_to_colour(iprops[3], BLACK); + fov.override.seen_colour = str_to_colour(iprops[4], BLACK); + Feature_Overrides.push_back(fov); + } +} -unsigned char mapchar2(unsigned char ldfk) +void apply_feature_overrides() { - unsigned char showed = 0; + for (int i = 0, size = Feature_Overrides.size(); i < size; ++i) + { + const feature_override &fov = Feature_Overrides[i]; + const feature_def &ofeat = fov.override; + feature_def &feat = Feature[fov.feat]; + + if (ofeat.symbol) + feat.symbol = ofeat.symbol; + if (ofeat.magic_symbol) + feat.magic_symbol = ofeat.magic_symbol; + if (ofeat.colour) + feat.colour = ofeat.colour; + if (ofeat.map_colour) + feat.map_colour = ofeat.map_colour; + if (ofeat.seen_colour) + feat.seen_colour = ofeat.seen_colour; + } +} - switch (ldfk) +void init_feature_table( void ) +{ + for (int i = 0; i < NUM_FEATURES; i++) { - case DNGN_UNSEEN: - showed = 0; - break; + Feature[i].symbol = 0; + Feature[i].colour = BLACK; // means must be set some other way + Feature[i].notable = false; + Feature[i].seen_effect = false; + Feature[i].magic_symbol = 0; // made equal to symbol if untouched + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = BLACK; // marks no special seen map handling + + switch (i) + { + case DNGN_UNSEEN: + default: + break; - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = 177; - break; + case DNGN_ROCK_WALL: + case DNGN_PERMAROCK_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = EC_ROCK; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case DNGN_CLOSED_DOOR: - showed = 254; - break; + case DNGN_STONE_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = EC_STONE; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - //case DNGN_LAVA_X: showed = 247; break; // deprecated? {dlb} - //case DNGN_WATER_X: showed = 247; break; // deprecated? {dlb} + case DNGN_OPEN_DOOR: + Feature[i].symbol = Options.char_table[ DCHAR_DOOR_OPEN ]; + Feature[i].colour = LIGHTGREY; + break; - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; + case DNGN_CLOSED_DOOR: + Feature[i].symbol = Options.char_table[ DCHAR_DOOR_CLOSED ]; + Feature[i].colour = LIGHTGREY; + break; - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = 247; - break; + case DNGN_METAL_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = CYAN; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = 249; - break; + case DNGN_SECRET_DOOR: + // Note: get_secret_door_appearance means this probably isn't used + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = EC_ROCK; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case 68: - showed = '>'; - break; // < + case DNGN_GREEN_CRYSTAL_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = GREEN; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case DNGN_OPEN_DOOR: - showed = 39; - break; + case DNGN_ORCISH_IDOL: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = DARKGREY; + break; - case 72: - showed = '<'; - break; // < + case DNGN_WAX_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = YELLOW; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; // wax wall - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; + case DNGN_SILVER_STATUE: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = WHITE; + Feature[i].seen_effect = true; + break; - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; + case DNGN_GRANITE_STATUE: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = LIGHTGREY; + break; - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; + case DNGN_ORANGE_CRYSTAL_STATUE: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = LIGHTRED; + Feature[i].seen_effect = true; + break; - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = 239; - break; + case DNGN_LAVA: + Feature[i].symbol = Options.char_table[ DCHAR_WAVY ]; + Feature[i].colour = RED; + break; - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = 220; - break; + case DNGN_DEEP_WATER: + Feature[i].symbol = Options.char_table[ DCHAR_WAVY ]; + Feature[i].colour = BLUE; + break; - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = 159; - break; - default: - showed = 0; - break; - } + case DNGN_SHALLOW_WATER: + Feature[i].symbol = Options.char_table[ DCHAR_WAVY ]; + Feature[i].colour = CYAN; + break; - return showed; -} + case DNGN_FLOOR: + Feature[i].symbol = Options.char_table[ DCHAR_FLOOR ]; + Feature[i].colour = EC_FLOOR; + Feature[i].magic_symbol = Options.char_table[ DCHAR_FLOOR_MAGIC ]; + break; + case DNGN_EXIT_HELL: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = LIGHTRED; + Feature[i].notable = false; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTRED; + break; -// realize that this is simply a repackaged version of -// stuff::see_grid() -- make certain they correlate {dlb}: -bool mons_near(struct monsters *monster, unsigned int foe) -{ - // early out -- no foe! - if (foe == MHITNOT) - return (false); + case DNGN_ENTER_HELL: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = RED; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = RED; + break; - if (foe == MHITYOU) - { - if (monster->x > you.x_pos - 9 && monster->x < you.x_pos + 9 - && monster->y > you.y_pos - 9 && monster->y < you.y_pos + 9) - { - if (env.show[monster->x - you.x_pos + 9][monster->y - you.y_pos + 9]) - return (true); - } - return (false); - } + case DNGN_TRAP_MECHANICAL: + Feature[i].colour = LIGHTCYAN; + Feature[i].symbol = Options.char_table[ DCHAR_TRAP ]; + Feature[i].map_colour = LIGHTCYAN; + break; - // must be a monster - struct monsters *myFoe = &menv[foe]; - if (myFoe->type >= 0) - { - if (monster->x > myFoe->x - 9 && monster->x < myFoe->x + 9 - && monster->y > myFoe->y - 9 && monster->y < myFoe->y + 9) - { - return (true); - } - } + case DNGN_TRAP_MAGICAL: + Feature[i].colour = MAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_TRAP ]; + Feature[i].map_colour = MAGENTA; + break; - return (false); -} // end mons_near() + case DNGN_TRAP_III: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_TRAP ]; + Feature[i].map_colour = LIGHTGREY; + break; + case DNGN_UNDISCOVERED_TRAP: + Feature[i].symbol = Options.char_table[ DCHAR_FLOOR ]; + Feature[i].colour = EC_FLOOR; + Feature[i].magic_symbol = Options.char_table[ DCHAR_FLOOR_MAGIC ]; + break; -//--------------------------------------------------------------- -// -// get_non_ibm_symbol -// -// Returns the character code and color for everything drawn -// without the IBM graphics option. -// -//--------------------------------------------------------------- -void get_non_ibm_symbol(unsigned int object, unsigned short *ch, - unsigned short *color) -{ - ASSERT(color != NULL); - ASSERT(ch != NULL); + case DNGN_ENTER_SHOP: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = YELLOW; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = YELLOW; + break; - switch (object) - { + case DNGN_ENTER_LABYRINTH: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = CYAN; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = CYAN; + break; - case DNGN_UNSEEN: - *ch = 0; - break; + case DNGN_ROCK_STAIRS_DOWN: + case DNGN_STONE_STAIRS_DOWN_I: + case DNGN_STONE_STAIRS_DOWN_II: + case DNGN_STONE_STAIRS_DOWN_III: + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_DOWN ]; + Feature[i].colour = ((i == DNGN_ROCK_STAIRS_DOWN) ? BROWN + : LIGHTGREY); + Feature[i].map_colour = RED; + break; - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - *color = env.rock_colour; - *ch = '#'; - break; + case DNGN_ROCK_STAIRS_UP: + case DNGN_STONE_STAIRS_UP_I: + case DNGN_STONE_STAIRS_UP_II: + case DNGN_STONE_STAIRS_UP_III: + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_UP ]; + Feature[i].colour = ((i == DNGN_ROCK_STAIRS_UP) ? BROWN + : LIGHTGREY); + Feature[i].map_colour = GREEN; + break; - case DNGN_STONE_WALL: - if (player_in_branch( BRANCH_HALL_OF_ZOT )) - *color = env.rock_colour; - else - *color = LIGHTGREY; - *ch = '#'; - break; + case DNGN_ENTER_DIS: + Feature[i].colour = CYAN; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = CYAN; + break; - case DNGN_CLOSED_DOOR: - *ch = '+'; - break; + case DNGN_ENTER_GEHENNA: + Feature[i].colour = RED; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = RED; + break; - case DNGN_METAL_WALL: - *ch = '#'; - *color = CYAN; - break; + case DNGN_ENTER_COCYTUS: + Feature[i].colour = LIGHTCYAN; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTCYAN; + break; - case DNGN_SECRET_DOOR: - *ch = '#'; - *color = env.rock_colour; - break; + case DNGN_ENTER_TARTARUS: + Feature[i].colour = DARKGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = DARKGREY; + break; - case DNGN_GREEN_CRYSTAL_WALL: - *ch = '#'; - *color = GREEN; - break; + case DNGN_ENTER_ABYSS: + Feature[i].colour = EC_RANDOM; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = EC_RANDOM; + break; - case DNGN_ORCISH_IDOL: - *ch = '8'; - *color = DARKGREY; - break; + case DNGN_EXIT_ABYSS: + Feature[i].colour = EC_RANDOM; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = EC_RANDOM; + break; - case DNGN_WAX_WALL: - *ch = '#'; - *color = YELLOW; - break; + case DNGN_STONE_ARCH: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + break; - case DNGN_SILVER_STATUE: - *ch = '8'; - *color = WHITE; - Visible_Statue[ STATUE_SILVER ] = 1; - break; + case DNGN_ENTER_PANDEMONIUM: + Feature[i].colour = LIGHTBLUE; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTBLUE; + break; - case DNGN_GRANITE_STATUE: - *ch = '8'; - *color = LIGHTGREY; - break; + case DNGN_EXIT_PANDEMONIUM: + // Note: has special handling for colouring with mutation + Feature[i].colour = LIGHTBLUE; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTBLUE; + break; - case DNGN_ORANGE_CRYSTAL_STATUE: - *ch = '8'; - *color = LIGHTRED; - Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 1; - break; + case DNGN_TRANSIT_PANDEMONIUM: + Feature[i].colour = LIGHTGREEN; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTGREEN; + break; - case DNGN_LAVA: - *ch = '{'; - *color = RED; - break; + case DNGN_ENTER_ORCISH_MINES: + case DNGN_ENTER_HIVE: + case DNGN_ENTER_LAIR: + case DNGN_ENTER_SLIME_PITS: + case DNGN_ENTER_VAULTS: + case DNGN_ENTER_CRYPT: + case DNGN_ENTER_HALL_OF_BLADES: + case DNGN_ENTER_TEMPLE: + case DNGN_ENTER_SNAKE_PIT: + case DNGN_ENTER_ELVEN_HALLS: + case DNGN_ENTER_TOMB: + case DNGN_ENTER_SWAMP: + case DNGN_ENTER_RESERVED_1: + case DNGN_ENTER_RESERVED_2: + case DNGN_ENTER_RESERVED_3: + case DNGN_ENTER_RESERVED_4: + Feature[i].colour = YELLOW; + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_DOWN ]; + Feature[i].notable = true; + Feature[i].map_colour = RED; + Feature[i].seen_colour = LIGHTRED; + break; - case DNGN_DEEP_WATER: - *ch = '{'; - // this wavy thing also used for water elemental - // note that some monsters which use IBM graphics aren't set - // for this function - too tricky for now. - *color = BLUE; - break; + case DNGN_ENTER_ZOT: + Feature[i].colour = MAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = MAGENTA; + break; - case DNGN_SHALLOW_WATER: - *color = CYAN; - *ch = '{'; - break; + case DNGN_RETURN_FROM_ORCISH_MINES: + case DNGN_RETURN_FROM_HIVE: + case DNGN_RETURN_FROM_LAIR: + case DNGN_RETURN_FROM_SLIME_PITS: + case DNGN_RETURN_FROM_VAULTS: + case DNGN_RETURN_FROM_CRYPT: + case DNGN_RETURN_FROM_HALL_OF_BLADES: + case DNGN_RETURN_FROM_TEMPLE: + case DNGN_RETURN_FROM_SNAKE_PIT: + case DNGN_RETURN_FROM_ELVEN_HALLS: + case DNGN_RETURN_FROM_TOMB: + case DNGN_RETURN_FROM_SWAMP: + case DNGN_RETURN_RESERVED_1: + case DNGN_RETURN_RESERVED_2: + case DNGN_RETURN_RESERVED_3: + case DNGN_RETURN_RESERVED_4: + Feature[i].colour = YELLOW; + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_UP ]; + Feature[i].map_colour = BLUE; + Feature[i].seen_colour = LIGHTBLUE; + break; - case DNGN_FLOOR: - *color = env.floor_colour; - *ch = '.'; - break; + case DNGN_RETURN_FROM_ZOT: + Feature[i].colour = MAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = MAGENTA; + break; - case DNGN_ENTER_HELL: - *color = RED; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_HELL); - break; + case DNGN_ALTAR_ZIN: + Feature[i].colour = WHITE; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = WHITE; + break; - case DNGN_OPEN_DOOR: - *ch = '\''; - break; + case DNGN_ALTAR_SHINING_ONE: + Feature[i].colour = YELLOW; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = YELLOW; + break; - case DNGN_BRANCH_STAIRS: - *color = BROWN; - *ch = '>'; - break; + case DNGN_ALTAR_KIKUBAAQUDGHA: + Feature[i].colour = DARKGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = DARKGREY; + break; - case DNGN_TRAP_MECHANICAL: - *color = 11; - *ch = '^'; - break; + case DNGN_ALTAR_YREDELEMNUL: + Feature[i].colour = EC_UNHOLY; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_UNHOLY; + break; - case DNGN_TRAP_MAGICAL: - *color = MAGENTA; - *ch = '^'; - break; + case DNGN_ALTAR_XOM: + Feature[i].colour = EC_RANDOM; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_RANDOM; + break; - case DNGN_TRAP_III: - *color = LIGHTGREY; - *ch = '^'; - break; + case DNGN_ALTAR_VEHUMET: + Feature[i].colour = EC_VEHUMET; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_VEHUMET; + break; - case DNGN_UNDISCOVERED_TRAP: - *color = env.floor_colour; - *ch = '.'; - break; + case DNGN_ALTAR_OKAWARU: + Feature[i].colour = CYAN; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = CYAN; + break; - case DNGN_ENTER_SHOP: - *color = YELLOW; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_SHOP); - break; -// if I change anything above here, must also change magic mapping! + case DNGN_ALTAR_MAKHLEB: + Feature[i].colour = EC_FIRE; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_FIRE; + break; - case DNGN_ENTER_LABYRINTH: - *color = LIGHTGREY; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_LABYRINTH); - break; + case DNGN_ALTAR_SIF_MUNA: + Feature[i].colour = BLUE; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = BLUE; + break; - case DNGN_ROCK_STAIRS_DOWN: - *color = BROWN; // ladder - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - *ch = '>'; - break; + case DNGN_ALTAR_TROG: + Feature[i].colour = RED; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = RED; + break; - case DNGN_ROCK_STAIRS_UP: - *color = BROWN; // ladder - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - *ch = '<'; - break; + case DNGN_ALTAR_NEMELEX_XOBEH: + Feature[i].colour = LIGHTMAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = LIGHTMAGENTA; + break; - case DNGN_ENTER_DIS: - *color = CYAN; - *ch = '\\'; - break; + case DNGN_ALTAR_ELYVILON: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = LIGHTGREY; + break; - case DNGN_ENTER_GEHENNA: - *color = RED; - *ch = '\\'; - break; + case DNGN_BLUE_FOUNTAIN: + Feature[i].colour = BLUE; + Feature[i].symbol = Options.char_table[ DCHAR_FOUNTAIN ]; + break; - case DNGN_ENTER_COCYTUS: - *color = LIGHTCYAN; - *ch = '\\'; - break; + case DNGN_SPARKLING_FOUNTAIN: + Feature[i].colour = LIGHTBLUE; + Feature[i].symbol = Options.char_table[ DCHAR_FOUNTAIN ]; + break; - case DNGN_ENTER_TARTARUS: - *color = DARKGREY; - *ch = '\\'; - break; + case DNGN_DRY_FOUNTAIN_I: + case DNGN_DRY_FOUNTAIN_II: + case DNGN_PERMADRY_FOUNTAIN: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_FOUNTAIN ]; + break; - case DNGN_ENTER_ABYSS: - *color = random2(16); - *ch = '\\'; - seen_other_thing(DNGN_ENTER_ABYSS); - break; + case DNGN_INVIS_EXPOSED: + Feature[i].symbol = Options.char_table[ DCHAR_INVIS_EXPOSED ]; + break; - case DNGN_EXIT_ABYSS: - *color = random2(16); - *ch = '\\'; - break; + case DNGN_ITEM_DETECTED: + Feature[i].magic_symbol = Options.char_table[ DCHAR_ITEM_DETECTED ]; + break; - case DNGN_STONE_ARCH: - *color = LIGHTGREY; - *ch = '\\'; - break; + case DNGN_ITEM_ORB: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_ORB ]; + break; - case DNGN_ENTER_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_PANDEMONIUM); - break; + case DNGN_ITEM_WEAPON: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_WEAPON ]; + break; - case DNGN_EXIT_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = '\\'; - break; + case DNGN_ITEM_ARMOUR: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_ARMOUR ]; + break; - case DNGN_TRANSIT_PANDEMONIUM: - *color = LIGHTGREEN; - *ch = '\\'; - break; // gate to other part of pandemonium - - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - *color = YELLOW; - *ch = '>'; - seen_staircase(object); - break; + case DNGN_ITEM_WAND: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_WAND ]; + break; - case DNGN_ENTER_ZOT: - *color = MAGENTA; - *ch = '\\'; - seen_staircase(object); - break; + case DNGN_ITEM_FOOD: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_FOOD ]; + break; - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - *color = YELLOW; - *ch = '<'; - break; + case DNGN_ITEM_SCROLL: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_SCROLL ]; + break; - case DNGN_RETURN_FROM_ZOT: - *color = MAGENTA; - *ch = '\\'; - break; + case DNGN_ITEM_RING: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_RING ]; + break; - case DNGN_ALTAR_ZIN: - *color = WHITE; - *ch = '_'; - seen_altar(GOD_ZIN); - break; + case DNGN_ITEM_POTION: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_POTION ]; + break; - case DNGN_ALTAR_SHINING_ONE: - *color = YELLOW; - *ch = '_'; - seen_altar(GOD_SHINING_ONE); - break; + case DNGN_ITEM_MISSILE: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_MISSILE ]; + break; - case DNGN_ALTAR_KIKUBAAQUDGHA: - *color = DARKGREY; - *ch = '_'; - seen_altar(GOD_KIKUBAAQUDGHA); - break; + case DNGN_ITEM_BOOK: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_BOOK ]; + break; - case DNGN_ALTAR_YREDELEMNUL: - *color = DARKGREY; - if (one_chance_in(3)) - *color = RED; - *ch = '_'; - seen_altar(GOD_YREDELEMNUL); - break; + case DNGN_ITEM_STAVE: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_STAVE ]; + break; - case DNGN_ALTAR_XOM: - *color = random_colour(); - *ch = '_'; - seen_altar(GOD_XOM); - break; + case DNGN_ITEM_MISCELLANY: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_MISCELLANY ]; + break; - case DNGN_ALTAR_VEHUMET: - *color = LIGHTBLUE; - if (one_chance_in(3)) - *color = LIGHTMAGENTA; - if (one_chance_in(3)) - *color = LIGHTRED; - *ch = '_'; - seen_altar(GOD_VEHUMET); - break; + case DNGN_ITEM_CORPSE: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_CORPSE ]; + break; - case DNGN_ALTAR_OKAWARU: - *color = CYAN; - *ch = '_'; - seen_altar(GOD_OKAWARU); - break; + case DNGN_ITEM_GOLD: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_GOLD ]; + break; - case DNGN_ALTAR_MAKHLEB: - *color = RED; - if (one_chance_in(3)) - *color = LIGHTRED; - if (one_chance_in(3)) - *color = YELLOW; - *ch = '_'; - seen_altar(GOD_MAKHLEB); - break; + case DNGN_ITEM_AMULET: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_AMULET ]; + break; - case DNGN_ALTAR_SIF_MUNA: - *color = BLUE; - *ch = '_'; - seen_altar(GOD_SIF_MUNA); - break; + case DNGN_CLOUD: + Feature[i].symbol = Options.char_table[ DCHAR_CLOUD ]; + break; + } + } - case DNGN_ALTAR_TROG: - *color = RED; - *ch = '_'; - seen_altar(GOD_TROG); - break; + apply_feature_overrides(); - case DNGN_ALTAR_NEMELEX_XOBEH: - *color = LIGHTMAGENTA; - *ch = '_'; - seen_altar(GOD_NEMELEX_XOBEH); - break; + for (int i = 0; i < NUM_FEATURES; ++i) + { + if (!Feature[i].magic_symbol) + Feature[i].magic_symbol = Feature[i].symbol; - case DNGN_ALTAR_ELYVILON: - *color = LIGHTGREY; - *ch = '_'; - seen_altar(GOD_ELYVILON); - break; + if (Feature[i].seen_colour == BLACK) + Feature[i].seen_colour = Feature[i].map_colour; + } +} - case DNGN_BLUE_FOUNTAIN: - *color = BLUE; - *ch = '}'; - break; +static int get_screen_glyph( int x, int y ) +{ + const int ex = x - you.x_pos + 9; + const int ey = y - you.y_pos + 9; - case DNGN_SPARKLING_FOUNTAIN: - *color = LIGHTBLUE; - *ch = '}'; - break; + int object = env.show[ex][ey]; + unsigned short colour = env.show_col[ex][ey]; + unsigned short ch; - case DNGN_DRY_FOUNTAIN_I: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_PERMADRY_FOUNTAIN: - *color = LIGHTGREY; - *ch = '}'; - break; + if (!object) + return get_envmap_char(x, y); - case 256: - *ch = '0'; - break; + if (object == DNGN_SECRET_DOOR) + object = grid_secret_door_appearance( x, y ); - case 257: - *color = CYAN; - *ch = '~'; - break; /* Invis creature walking through water */ + get_symbol( object, &ch, &colour ); + return (ch); +} - case 258: - *ch = ')'; - break; // weapon ) +// Returns a string containing an ASCII representation of the map. If fullscreen +// is set to false, only the viewable area is returned. Leading and trailing +// spaces are trimmed from each line. Leading and trailing empty lines are also +// snipped. +std::string screenshot( bool fullscreen ) +{ + UNUSED( fullscreen ); - case 259: - *ch = '['; - break; // armour [ + const int X_SIZE = VIEW_WIDTH; + const int Y_SIZE = VIEW_HEIGHT; - case 260: - *ch = '/'; - break; // wands, etc. + // [ds] Screenshots need to be straight ASCII. We will now proceed to force + // the char and feature tables back to ASCII. + FixedVector char_table_bk; + char_table_bk = Options.char_table; - case 261: - *ch = '%'; - break; // food + init_char_table(CSET_ASCII); + init_feature_table(); + + int firstnonspace = -1; + int firstpopline = -1; + int lastpopline = -1; - case 262: - *ch = '+'; - break; // books + + char lines[Y_SIZE][X_SIZE + 1]; + for (int count_y = 0; count_y < Y_SIZE; count_y++) + { + int lastnonspace = -1; + + for (int count_x = 0; count_x < X_SIZE; count_x++) + { + // in grid coords + const int gx = count_x + you.x_pos - 16; + const int gy = count_y + you.y_pos - 8; + + int ch = (!map_bounds(gx, gy)) + ? 0 + : (count_x < 8 || count_x > 24) + ? get_envmap_char(gx, gy) + : (gx == you.x_pos && gy == you.y_pos) + ? you.symbol + : get_screen_glyph(gx, gy); + + if (ch && !isprint(ch)) + { + // [ds] Evil hack time again. Peek at grid, use that character. + int object = grd[gx][gy]; + unsigned short glych, glycol; - case 263: - *ch = '?'; - break; // scroll ? + if (object == DNGN_SECRET_DOOR) + object = grid_secret_door_appearance( gx, gy ); - case 264: - *ch = '='; - break; // ring = etc + get_symbol( object, &glych, &glycol ); + ch = glych; + } + + // More mangling to accommodate C strings. + if (!ch) + ch = ' '; - case 265: - *ch = '!'; - break; // potions ! + if (ch != ' ') + { + lastnonspace = count_x; + lastpopline = count_y; - case 266: - *ch = '('; - break; // stones + if (firstnonspace == -1 || firstnonspace > count_x) + firstnonspace = count_x; - case 267: - *ch = ':'; - break; // book + + if (firstpopline == -1) + firstpopline = count_y; + } - case 268: - *ch = '%'; - break; // corpses part 1 + lines[count_y][count_x] = ch; + } - case 269: - *ch = '|'; - break; // magical staves + lines[count_y][lastnonspace + 1] = 0; + } - case 270: - *ch = '}'; - break; // gems + // Restore char and feature tables + Options.char_table = char_table_bk; + init_feature_table(); - case 271: - *ch = '%'; - break; // don't know ? + std::string ss; + if (firstpopline != -1 && lastpopline != -1) + { + if (firstnonspace == -1) + firstnonspace = 0; - case 272: - *ch = '$'; - *color = YELLOW; - break; // $ gold + for (int i = firstpopline; i <= lastpopline; ++i) + { + char *curr = lines[i]; - case 273: - *ch = '"'; - break; // amulet + while (*curr && curr - lines[i] < firstnonspace) + curr++; - default: - int mnr = object; - *ch = ((mnr >= 297) ? mons_char(mnr - 297) : object); // yeah - break; + ss += curr; + ss += EOL; + } } -} + return (ss); +} -/* - This is the viewwindow function for computers without IBM graphic displays. - It is activated by a command line argument, which sets a function pointer. - */ -void viewwindow3(char draw_it, bool do_updates) +static int viewmap_flash_colour() { - int bufcount = 0; - FixedVector < unsigned short, 1500 > buffy; //[800]; //392]; + if (you.special_wield == SPWLD_SHADOW) + return (DARKGREY); + else if (you.berserker) + return (RED); + + return (BLACK); +} - unsigned short ch, color; +//--------------------------------------------------------------- +// +// viewwindow -- now unified and rolled into a single pass +// +// Draws the main window using the character set returned +// by get_symbol(). +// +// This function should not interfere with the game condition, +// unless do_updates is set (ie. stealth checks for visible +// monsters). +// +//--------------------------------------------------------------- +void viewwindow(bool draw_it, bool do_updates) +{ + const int X_SIZE = VIEW_WIDTH; + const int Y_SIZE = VIEW_HEIGHT; + const int BUFFER_SIZE = 1550; + FixedVector < screen_buffer_t, BUFFER_SIZE > buffy; int count_x, count_y; - losight(env.show, grd, you.x_pos, you.y_pos); + losight( env.show, grd, you.x_pos, you.y_pos ); // must be done first + + for (count_x = 0; count_x < NUM_STATUE_TYPES; count_x++) + you.visible_statue[count_x] = 0; for (count_x = 0; count_x < 18; count_x++) { for (count_y = 0; count_y < 18; count_y++) { env.show_col[count_x][count_y] = LIGHTGREY; - show_backup[count_x][count_y] = 0; + Show_Backup[count_x][count_y] = 0; } } - item(); + item_grid(); // must be done before cloud and monster cloud_grid(); - monster_grid(do_updates); - bufcount = 0; + monster_grid( do_updates ); - if (draw_it == 1) + if (draw_it) { _setcursortype(_NOCURSOR); - for (count_y = (you.y_pos - 8); (count_y < you.y_pos + 9); count_y++) - { - bufcount += 16; - for (count_x = (you.x_pos - 8); (count_x < you.x_pos + 9); count_x++) + const bool map = player_in_mappable_area(); + int bufcount = 0; + + int flash_colour = you.flash_colour; + if (flash_colour == BLACK) + flash_colour = viewmap_flash_colour(); + + for (count_y = 0; count_y < Y_SIZE; count_y++) + { + for (count_x = 0; count_x < X_SIZE; count_x++) { - color = env.show_col[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9]; + // in grid coords + const int gx = count_x + you.x_pos - 16; + const int gy = count_y + you.y_pos - 8; - if (count_x == you.x_pos && count_y == you.y_pos) + // order is important here + if (!map_bounds( gx, gy )) + { + // off the map + buffy[bufcount] = 0; + buffy[bufcount + 1] = DARKGREY; + } + else if (count_x < 8 || count_x > 24) { - ch = your_sign; + // outside the env.show area + buffy[bufcount] = get_envmap_char( gx, gy ); + buffy[bufcount + 1] = DARKGREY; + + if (Options.colour_map) + buffy[bufcount + 1] = + colour_code_map(gx - 1, gy - 1, + Options.item_colour); + } + else if (gx == you.x_pos && gy == you.y_pos) + { + // player overrides everything in cell + buffy[bufcount] = you.symbol; + buffy[bufcount + 1] = you.colour; if (player_is_swimming()) { - color = (grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER) - ? BLUE : CYAN; - } - else - { - color = your_colour; + if (grd[gx][gy] == DNGN_DEEP_WATER) + buffy[bufcount + 1] = BLUE; + else + buffy[bufcount + 1] = CYAN; } } else { - unsigned int object = env.show[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9]; + // Note: env.show is set for grids in LoS + // get env coords + const int ex = gx - you.x_pos + 9; + const int ey = gy - you.y_pos + 9; - get_non_ibm_symbol(object, &ch, &color); - } + int object = env.show[ex][ey]; + unsigned short colour = env.show_col[ex][ey]; + unsigned short ch; - buffy[bufcount] = ch; //showed; - buffy[bufcount + 1] = color; - bufcount += 2; - } + if (object == DNGN_SECRET_DOOR) + object = grid_secret_door_appearance( gx, gy ); - bufcount += 16; - } + get_symbol( object, &ch, &colour ); - bufcount = 0; + buffy[bufcount] = ch; + buffy[bufcount + 1] = colour; - if (you.level_type != LEVEL_LABYRINTH - && you.level_type != LEVEL_ABYSS) - { - for (count_y = 0; count_y < 17; count_y++) - { - bufcount += 16; - for (count_x = 0; count_x < 17; count_x++) - { - int enx = count_x + you.x_pos - 9, - eny = count_y + you.y_pos - 9; - if (buffy[bufcount] != 0 && enx >= 0 && eny >= 0 - && enx + 1 < GXM && eny + 1 < GYM) + if (map) { - unsigned short bch = buffy[bufcount]; - if (mgrd[enx + 1][eny + 1] != NON_MONSTER) { - const monsters &m = menv[ mgrd[enx + 1][eny + 1] ]; - if (!mons_is_mimic(m.type) - && mons_char(m.type) == bch) - { - bch |= mons_colour(m.type) << 12; - } + // This section is very tricky because it + // duplicates the old code (which was horrid). + + // if the grid is in LoS env.show was set and + // we set the buffer already, so... + if (buffy[bufcount] != 0) + { + // ... map that we've seen this + set_envmap_char( gx, gy, buffy[bufcount] ); + set_terrain_seen( gx, gy ); + set_envmap_detected_mons(gx, gy, false); + set_envmap_detected_item(gx, gy, false); } - env.map[enx][eny] = bch; - } - - if (Options.clean_map == 1 - && show_backup[count_x + 1][count_y + 1] != 0 - && enx >= 0 - && eny >= 0) - { - get_non_ibm_symbol( show_backup[count_x + 1] - [count_y + 1], - &ch, &color ); - env.map[enx][eny] = ch; - } - bufcount += 2; - } - bufcount += 16; - } - } - bufcount = 0; - - for (count_y = 0; count_y < 17; count_y++) - { - for (count_x = 0; count_x < 33; count_x++) - { - if (count_x + you.x_pos - 17 < 3 - || count_y + you.y_pos - 9 < 3 - || count_x + you.x_pos - 14 > (GXM - 3) - || count_y + you.y_pos - 9 > (GYM - 3)) - { - buffy[bufcount] = 0; - bufcount++; - buffy[bufcount] = 0; - bufcount++; - continue; - } + // Check if we're looking to clean_map... + // but don't touch the buffer to clean it, + // instead we modify the env.map itself so + // that the map stays clean as it moves out + // of the env.show radius. + // + // Note: show_backup is 0 on every square which + // is inside the env.show radius and doesn't + // have a monster or cloud on it, and is equal + // to the grid before monsters and clouds were + // added otherwise. + if (Options.clean_map + && Show_Backup[ex][ey] + && is_terrain_seen( gx, gy )) + { + get_symbol( Show_Backup[ex][ey], &ch, &colour ); + set_envmap_char( gx, gy, ch ); + } - if (count_x >= 8 && count_x <= 24 && count_y >= 0 - && count_y <= 16 && buffy[bufcount] != 0) - { - bufcount += 2; - continue; + // Now we get to filling in both the unseen + // grids in the env.show radius area as + // well doing the clean_map. The clean_map + // is done by having the env.map set to the + // backup character above, and down here we + // procede to override that character if it's + // out of LoS! If it wasn't, buffy would have + // already been set (but we'd still have + // clobbered env.map... which is important + // to do for when we move away from the area!) + if (buffy[bufcount] == 0) + { + // show map + buffy[bufcount] = get_envmap_char( gx, gy ); + buffy[bufcount + 1] = DARKGREY; + + if (Options.colour_map) + buffy[bufcount + 1] = + colour_code_map(gx - 1, gy - 1, + Options.item_colour); + } + } } - - buffy[bufcount] = (unsigned char) - env.map[count_x + you.x_pos - 17] - [count_y + you.y_pos - 9]; - - buffy[bufcount + 1] = DARKGREY; - - if (Options.colour_map) + + // alter colour if flashing the characters vision + if (flash_colour != BLACK + && buffy[bufcount + 1] != DARKGREY) { - if (env.map[count_x + you.x_pos - 17] - [count_y + you.y_pos - 9] != 0) - { - buffy[bufcount + 1] - = colour_code_map( count_x + you.x_pos - 17, - count_y + you.y_pos - 9, - Options.item_colour ); - } + buffy[bufcount + 1] = flash_colour; } bufcount += 2; } } - if (you.berserker) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = RED; - } - } - - if (show_green != BLACK) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = show_green; - } - - show_green = ((you.special_wield == SPWLD_SHADOW) ? DARKGREY - : BLACK); - } + // Leaving it this way because short flashes can occur in long ones, + // and this simply works without requiring a stack. + you.flash_colour = BLACK; #ifdef DOS_TERM - puttext(2, 1, 34, 17, buffy.buffer()); + puttext( 2, 1, X_SIZE + 1, Y_SIZE, buffy.buffer() ); #endif #ifdef PLAIN_TERM - gotoxy(2, 1); - bufcount = 0; - - // this line is purely optional - if (you.running == 0 || (you.running < 0 && Options.travel_delay > -1)) + // avoiding unneeded draws when running + if (!you.running || (you.running < 0 && Options.travel_delay > -1)) { - for (count_x = 0; count_x < 1120; count_x += 2) // 1056 - { - textcolor(buffy[count_x + 1]); - putch(buffy[count_x]); + gotoxy( 2, 1 ); - if (count_x % 66 == 64 && count_x > 0) -#ifdef DOS_TERM - cprintf(EOL " "); + bufcount = 0; + for (count_y = 0; count_y < Y_SIZE; count_y++) + { + for (count_x = 0; count_x < X_SIZE; count_x++) + { +#ifdef USE_CURSES + buffy[bufcount] = cset_adjust( buffy[bufcount] ); #endif + textcolor( buffy[bufcount + 1] ); + putch( buffy[bufcount] ); + bufcount += 2; + } -#ifdef PLAIN_TERM - gotoxy(2, wherey() + 1); -#endif + gotoxy( 2, count_y + 2 ); } } -#endif - _setcursortype(_NORMALCURSOR); - } // end of (if brek...) -} // end viewwindow3() - - -unsigned char mapchar3(unsigned char ldfk) -{ - unsigned char showed = 0; - - switch (ldfk) - { - case DNGN_UNSEEN: - showed = 0; - break; - - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = '*'; - break; - - case DNGN_CLOSED_DOOR: - showed = '+'; - break; - - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; - - case DNGN_LAVA_X: - case DNGN_WATER_X: - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = '{'; - break; - - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = ','; - break; - - //case 68: showed = '>'; break; // < (60) - - case DNGN_OPEN_DOOR: - showed = 39; - break; // open door - - //case 72: showed = '<'; break; - - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; - - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; - - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; - - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = '\\'; - break; - - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = '_'; - break; - - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = '}'; - break; - - default: - showed = 0; - break; - } - - return showed; -} - - -unsigned char mapchar4(unsigned char ldfk) -{ - unsigned char showed = 0; - - switch (ldfk) - { - case DNGN_UNSEEN: - showed = 0; - break; - - case DNGN_CLOSED_DOOR: - showed = '+'; - break; - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = '#'; - break; - - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; - - case DNGN_LAVA_X: - case DNGN_WATER_X: - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = '{'; - break; - - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = '.'; - break; - - case 68: - showed = '>'; // < - break; - - case DNGN_OPEN_DOOR: - showed = 39; - break; - - case 72: - showed = '<'; - break; - - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; - - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; - - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; - - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = '\\'; - break; - - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = '_'; - break; - - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = '}'; - break; +#ifdef USE_CURSES + set_altcharset( false ); +#endif - default: - showed = 0; - break; +#endif + _setcursortype(_NORMALCURSOR); } - - return showed; -} +} // end viewwindow() -- cgit v1.2.3-54-g00ecf