summaryrefslogblamecommitdiffstats
path: root/crawl-ref/source/view.cc
blob: cefbce11a8559b1579e6f9e1a80bb1f218b09f88 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                         


                   
 
                 
                  
 
                   
                   
                
                  
                    
                 
 
                    
 
                          


                     
                     
 
                            
                   
                
                  
                 
                   
                  
                    
                     
                  
                    
                    
                    
                  
                    
                  
                    
                 
                     
                     
                      
                     
                    
                    
                  
                   

                    
                   
                  
                
                    
                  
                  
                   

                    
                       
                    
                   
                     
                
 



                         

                           
                               
 

                                                   
                                                 

 
                                             
 
                                       
               
 
                                         
                                       
                                            



                                                           
                                     
     


                                   
                              
                                                          
     
                                                

                            

 















                                                                   
                               

 
                           
 
                                                       
     

                                                    
         


                                                                      
 

                                  
 


                                               







                                                                              







                                                                                    

     
 
                              
 
                                 
 
                                       
     
                           
         
                                            
                              
 
                                           



                                                                 
                                            
             
                                          
             

                                           
             
                
                                             
         
            
                                         


                                                                      
                                 
     
 



                                                                     
                                                              





                                                                  

                                            
 
 











































                                                                                 























                                                                                   


                          




                         
                                                               









                                                           
                                
                                                                     

                                                                 
 


                        


                                                                 
     

                                                     

                       

     








                                                               

                                          
                                               
                                               
 
                         

                               
 


                                                  




                                                          

                                                                            
     

                        

                                       
                                                             
 



                                              
 
                                            

                         
 
                                    
                                          
 
                                                                            
                     
 


                                                                   

                                                   
                         
 
                                                              

                         
                                                     
             

                                                                          



                                
             
         
 
                 
         

                                                          
 
                           
             


                                                  
                                      
               
                                                                     




                                                                           
                
             
                                        
 





                                                               
                           
         
     
 



                                                            















                                                                          


                     
 
 

                                                    
 

                                                         
                                          
 
 

                                                



                                                           





                                                                              
                                    
     

                                                                   
                                                                       
     
        

                                                         
                                    
     

 
                                                                                
                                                                            

                                                                               
                                       
 
                       
 

                                                                               
                                                         
                                       
 
                                
                      
 


                           
 

                                                                    

                              
 
                                                                        

                             
                                                                        
 
                    
                                                     
                                                                              
                                                            
                                                                       

                                   

                                                                               
                                                          
             
 


                                                      
 
                          


                                       
 

                                                                   
 


                                           
 
                                     
         
 

                                                             
     
 
                                       
                                       
                      
 
                          
                                                


                                
 
                                                         
         



                                                   
         
     
 
                      
 
 
                                  
 
                                    
                          
                           



                     
 








                                                                        
                                
 
                
                                    

                
                                     


                                        
                                               
 
                        
                                                                         



                                                                   
      

 



                                                                        
                         
















                                                          



                                             
                          




                      





                              












                                                   



                                                                
 







                                                                          















                                                                 
                         


      





                                  

                                                                  
                                                     
 
                                       
                                   
                



                                                                     
                                                      



                                                 
                                 





                                                           
                                                             
                                        
                 
                                                              
                                                            

                                                       
                 




                                                   
                               
                                  

                                   
                     
 

                                                       
 
                         
                                                          













                                                                
                                                                         
 
                 

 


                                           
                             
                                                      


                                               
                                           


                                     
                                                           
                                          
                                                       

                                  

 
               









                                                             
                                 


                                                                
 











                                                      
      
 
                                                                    
 
                
                   
                        

                                                


      
                                                                         
 
                


                                          
                           
                                                       
     




                                                               

                                         


      

                                                                 
 
                









                                           

                                       
     


                                                                

 

                                                              
 
                


                                           


                               


      

                                                                     
 
                



                                          
                                                       
     


                                   

                                                             

        
                                                                           


      

                                                                 
                                                         
                       
  

                                                           
  

                                                              
                                                                 
                                                        
 

                                    

                         

                                            
 
               
                                             
                          

      
                     
     
                         

               


                               

      
                        
     
 
                                              
                               
 

                             
 

                                                                            
 
                                      
               
 
                             
 


                                               
 
                                           
                                                                  
                     
                                                                
     



                                                      
                            
                                              
                                             
                                                   

                                                                       
         
                                                  
         
                                  
                                               
            
                                                      
 
                                                          
                         
         
                
                                                                              
                                                              
      
         
                                               
         

                                                              
                                                     
                
                             
                                               
     
                                                 
                                                     
      
         

                                                                  
                                      
                                                 
      
     
 


                                                                        
 
                




                                                         
                          
     


                                                    
      

                         
 
 





                                                                            
 



                                                 
 


                        
/*
 *  File:       view.cc
 *  Summary:    Misc function used to render the dungeon.
 *  Written by: Linley Henzell
 */

#include "AppHdr.h"

#include "view.h"
#include "shout.h"

#include <stdint.h>
#include <string.h>
#include <cmath>
#include <sstream>
#include <algorithm>
#include <memory>

#include "externs.h"

#include "map_knowledge.h"
#include "viewchar.h"
#include "viewgeom.h"
#include "viewmap.h"
#include "showsymb.h"

#include "attitude-change.h"
#include "branch.h"
#include "cio.h"
#include "cloud.h"
#include "clua.h"
#include "colour.h"
#include "coord.h"
#include "coordit.h"
#include "database.h"
#include "delay.h"
#include "directn.h"
#include "exclude.h"
#include "feature.h"
#include "files.h"
#include "godabil.h"
#include "macro.h"
#include "message.h"
#include "misc.h"
#include "mon-behv.h"
#include "mon-iter.h"
#include "mon-stuff.h"
#include "mon-util.h"
#include "newgame.h"
#include "options.h"
#include "notes.h"
#include "output.h"
#include "overmap.h"
#include "player.h"
#include "random.h"
#include "stuff.h"
#include "env.h"
#include "spells3.h"
#include "stash.h"
#include "tiles.h"
#include "travel.h"
#include "state.h"
#include "terrain.h"
#include "tilemcache.h"
#include "tilesdl.h"
#include "travel.h"
#include "tutorial.h"
#include "xom.h"

#ifdef USE_TILE
#include "tiledef-dngn.h"
#endif

#define DEBUG_PANE_BOUNDS 0

crawl_view_geometry crawl_view;

bool is_notable_terrain(dungeon_feature_type ftype)
{
    return (get_feature_def(ftype).is_notable());
}

void handle_seen_interrupt(monsters* monster)
{
    if (mons_is_unknown_mimic(monster))
        return;

    activity_interrupt_data aid(monster);
    if (!monster->seen_context.empty())
        aid.context = monster->seen_context;
    // XXX: Hack to make the 'seen' monster spec flag work.
    else if (testbits(monster->flags, MF_WAS_IN_VIEW)
        || testbits(monster->flags, MF_SEEN))
    {
        aid.context = "already seen";
    }
    else
        aid.context = "newly seen";

    if (!mons_is_safe(monster)
        && !mons_class_flag(monster->type, M_NO_EXP_GAIN))
    {
        interrupt_activity(AI_SEE_MONSTER, aid);
    }
    seen_monster( monster );
}

void flush_comes_into_view()
{
    if (!you.turn_is_over
        || (!you_are_delayed() && !crawl_state.is_repeating_cmd()))
    {
        return;
    }

    monsters* mon = crawl_state.which_mon_acting();

    if (!mon || !mon->alive() || (mon->flags & MF_WAS_IN_VIEW)
        || !you.can_see(mon))
    {
        return;
    }

    handle_seen_interrupt(mon);
}

void monster_grid_updates()
{
    for (monster_iterator mi(&you.get_los()); mi; ++mi)
    {
        if ((mi->asleep() || mons_is_wandering(*mi))
            && check_awaken(*mi))
        {
            behaviour_event(*mi, ME_ALERT, MHITYOU, you.pos(), false);
            handle_monster_shouts(*mi);
        }

        if (!mi->visible_to(&you))
            continue;

        good_god_follower_attitude_change(*mi);
        beogh_follower_convert(*mi);
        slime_convert(*mi);
        // XXX: Probably quite hackish. Allows for monsters going berserk when
        //      they see the player. Currently only used for Duvessa, see the
        //      function _elven_twin_dies in mon-stuff.cc.
        if (mi->flags & MF_GOING_BERSERK)
        {
            mi->flags &= ~MF_GOING_BERSERK;
            mi->go_berserk(true);
        }

        // XXX: Hack for triggering Dowan's spell changes.
        if (mi->props.exists("dowan_upgrade"))
        {
            mi->add_ench(ENCH_HASTE);
            mi->props.erase("dowan_upgrade");
            simple_monster_message(*mi, " seems to find hidden reserves of power!");
        }
    }
}

void update_monsters_in_view()
{
    unsigned int num_hostile = 0;

    for (monster_iterator mi; mi; ++mi)
    {
        if (mons_near(*mi))
        {
            if (mi->attitude == ATT_HOSTILE)
                num_hostile++;

            if (mons_is_unknown_mimic(*mi))
            {
                // For unknown mimics, don't mark as seen,
                // but do mark it as in view for later messaging.
                // FIXME: is this correct?
                mi->flags |= MF_WAS_IN_VIEW;
            }
            else if (mi->visible_to(&you))
            {
                handle_seen_interrupt(*mi);
                seen_monster(*mi);
            }
            else
                mi->flags &= ~MF_WAS_IN_VIEW;
        }
        else
            mi->flags &= ~MF_WAS_IN_VIEW;

        // If the monster hasn't been seen by the time that the player
        // gets control back then seen_context is out of date.
        mi->seen_context.clear();
    }

    // Xom thinks it's hilarious the way the player picks up an ever
    // growing entourage of monsters while running through the Abyss.
    // To approximate this, if the number of hostile monsters in view
    // is greater than it ever was for this particular trip to the
    // Abyss, Xom is stimulated in proportion to the number of
    // hostile monsters.  Thus if the entourage doesn't grow, then
    // Xom becomes bored.
    if (you.level_type == LEVEL_ABYSS
        && you.attribute[ATTR_ABYSS_ENTOURAGE] < num_hostile)
    {
        you.attribute[ATTR_ABYSS_ENTOURAGE] = num_hostile;
        xom_is_stimulated(16 * num_hostile);
    }
}

// We logically associate a difficulty parameter with each tile on each level,
// to make deterministic magic mapping work.  This function returns the
// difficulty parameters for each tile on the current level, whose difficulty
// is less than a certain amount.
//
// Random difficulties are used in the few cases where we want repeated maps
// to give different results; scrolls and cards, since they are a finite
// resource.
static const FixedArray<char, GXM, GYM>& _tile_difficulties(bool random)
{
    // We will often be called with the same level parameter and cutoff, so
    // cache this (DS with passive mapping autoexploring could be 5000 calls
    // in a second or so).
    static FixedArray<char, GXM, GYM> cache;
    static int cache_seed = -1;

    int seed = random ? -1 :
        (static_cast<int>(you.where_are_you) << 8) + you.your_level - 1731813538;

    if (seed == cache_seed && !random)
    {
        return cache;
    }

    if (!random)
    {
        push_rng_state();
        seed_rng(cache_seed);
    }

    cache_seed = seed;

    for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y)
        for (int x = X_BOUND_1; x <= X_BOUND_2; ++x)
            cache[x][y] = random2(100);

    if (!random)
    {
        pop_rng_state();
    }

    return cache;
}

static std::auto_ptr<FixedArray<bool, GXM, GYM> > _tile_detectability()
{
    std::auto_ptr<FixedArray<bool, GXM, GYM> > map(new FixedArray<bool, GXM, GYM>);

    std::vector<coord_def> flood_from;

    for (int x = X_BOUND_1; x <= X_BOUND_2; ++x)
        for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y)
        {
            (*map)(coord_def(x,y)) = false;

            if (feat_is_stair(grd[x][y]))
            {
                flood_from.push_back(coord_def(x, y));
            }
        }

    flood_from.push_back(you.pos());

    while (!flood_from.empty())
    {
        coord_def p = flood_from.back();
        flood_from.pop_back();

        if (!in_bounds(p))
            continue;

        if ((*map)(p))
            continue;

        (*map)(p) = true;

        if (grd(p) < DNGN_MINSEE && grd(p) != DNGN_CLOSED_DOOR)
            continue;

        for (int dy = -1; dy <= 1; ++dy)
            for (int dx = -1; dx <= 1; ++dx)
                flood_from.push_back(p + coord_def(dx,dy));
    }

    return map;
}

// Returns true if it succeeded.
bool magic_mapping(int map_radius, int proportion, bool suppress_msg,
                   bool force, bool deterministic, bool circular,
                   coord_def pos)
{
    if (!in_bounds(pos))
        pos = you.pos();

    if (!force
        && (testbits(env.level_flags, LFLAG_NO_MAGIC_MAP)
            || testbits(get_branch_flags(), BFLAG_NO_MAGIC_MAP)))
    {
        if (!suppress_msg)
            mpr("You feel momentarily disoriented.");

        return (false);
    }

    const bool wizard_map = (you.wizard && map_radius == 1000);

    if (!wizard_map)
    {
        if (map_radius > 50)
            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;

    bool did_map = false;
    int  num_altars        = 0;
    int  num_shops_portals = 0;

    const FixedArray<char, GXM, GYM>& difficulty =
        _tile_difficulties(!deterministic);

    std::auto_ptr<FixedArray<bool, GXM, GYM> > detectable;

    if (!deterministic)
        detectable = _tile_detectability();

    for (radius_iterator ri(pos, map_radius, circular ? C_ROUND : C_SQUARE);
         ri; ++ri)
    {
        if (!wizard_map)
        {
            int threshold = proportion;

            const int dist = grid_distance( you.pos(), *ri );

            if (dist > very_far)
                threshold = threshold / 3;
            else if (dist > pfar)
                threshold = threshold * 2 / 3;

            if (difficulty(*ri) > threshold)
                continue;
        }

        if (is_terrain_changed(*ri))
            clear_map_knowledge_grid(*ri);

        if (!wizard_map && (is_terrain_seen(*ri) || is_terrain_mapped(*ri)))
            continue;

        if (!wizard_map && !deterministic && !((*detectable)(*ri)))
            continue;

        const dungeon_feature_type feat = grd(*ri);

        bool open = true;

        if (feat_is_solid(feat) && !feat_is_closed_door(feat))
        {
            open = false;
            for (adjacent_iterator ai(*ri); ai; ++ai)
            {
                if (map_bounds(*ai) && (!feat_is_opaque(grd(*ai))
                                        || feat_is_closed_door(grd(*ai))))
                {
                    open = true;
                    break;
                }
            }
        }

        if (open)
        {
            if (wizard_map || !get_map_knowledge_obj(*ri))
                set_map_knowledge_obj(*ri, grd(*ri));

            if (wizard_map)
            {
                if (is_notable_terrain(feat))
                    seen_notable_thing(feat, *ri);

                set_terrain_seen(*ri);
#ifdef USE_TILE
                // Can't use set_map_knowledge_obj because that would
                // overwrite the gmap.
                env.tile_bk_bg(*ri) = tile_idx_unseen_terrain(ri->x, ri->y,
                                                              grd(*ri));
#endif
            }
            else
            {
                set_terrain_mapped(*ri);

                if (get_feature_dchar(feat) == DCHAR_ALTAR)
                    num_altars++;
                else if (get_feature_dchar(feat) == DCHAR_ARCH)
                    num_shops_portals++;
            }

            did_map = true;
        }
    }

    if (!suppress_msg)
    {
        mpr(did_map ? "You feel aware of your surroundings."
                    : "You feel momentarily disoriented.");

        std::vector<std::string> sensed;

        if (num_altars > 0)
            sensed.push_back(make_stringf("%d altar%s", num_altars,
                                          num_altars > 1 ? "s" : ""));

        if (num_shops_portals > 0)
        {
            const char* plur = num_shops_portals > 1 ? "s" : "";
            sensed.push_back(make_stringf("%d shop%s/portal%s",
                                          num_shops_portals, plur, plur));
        }

        if (!sensed.empty())
            mpr_comma_separated_list("You sensed ", sensed);
    }

    return (did_map);
}

// Is the given monster near (in LOS of) the player?
bool mons_near(const monsters *monster)
{
    if (crawl_state.arena || crawl_state.arena_suspended)
        return (true);
    return (you.see_cell(monster->pos()));
}

bool mon_enemies_around(const monsters *monster)
{
    // If the monster has a foe, return true.
    if (monster->foe != MHITNOT && monster->foe != MHITYOU)
        return (true);

    if (crawl_state.arena)
    {
        // If the arena-mode code in _handle_behaviour() hasn't set a foe then
        // we don't have one.
        return (false);
    }
    else if (monster->wont_attack())
    {
        // Additionally, if an ally is nearby and *you* have a foe,
        // consider it as the ally's enemy too.
        return (mons_near(monster) && there_are_monsters_nearby(true));
    }
    else
    {
        // For hostile monsters *you* are the main enemy.
        return (mons_near(monster));
    }
}

// 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);

    // [ds] Screenshots need to be straight ASCII. We will now proceed to force
    // the char and feature tables back to ASCII.
    FixedVector<unsigned, NUM_DCHAR_TYPES> char_table_bk;
    char_table_bk = Options.char_table;

    init_char_table(CSET_ASCII);
    init_show_table();

    int firstnonspace = -1;
    int firstpopline  = -1;
    int lastpopline   = -1;

    std::vector<std::string> lines(crawl_view.viewsz.y);
    for (int count_y = 1; count_y <= crawl_view.viewsz.y; count_y++)
    {
        int lastnonspace = -1;

        for (int count_x = 1; count_x <= crawl_view.viewsz.x; count_x++)
        {
            // in grid coords
            const coord_def gc = view2grid(coord_def(count_x, count_y));

            int ch =
                  (!map_bounds(gc))             ? 0 :
                  (!crawl_view.in_grid_los(gc)) ? get_map_knowledge_char(gc) :
                  (gc == you.pos())             ? you.symbol
                                                : get_screen_glyph(gc);

            if (ch && !isprint(ch))
            {
                // [ds] Evil hack time again. Peek at grid, use that character.
                ch = get_feat_symbol(grid_appearance(gc));
            }

            // More mangling to accommodate C strings.
            if (!ch)
                ch = ' ';

            if (ch != ' ')
            {
                lastnonspace = count_x;
                lastpopline = count_y;

                if (firstnonspace == -1 || firstnonspace > count_x)
                    firstnonspace = count_x;

                if (firstpopline == -1)
                    firstpopline = count_y;
            }

            lines[count_y - 1] += ch;
        }

        if (lastnonspace < (int) lines[count_y - 1].length())
            lines[count_y - 1].erase(lastnonspace + 1);
    }

    // Restore char and feature tables.
    Options.char_table = char_table_bk;
    init_show_table();

    std::ostringstream ss;
    if (firstpopline != -1 && lastpopline != -1)
    {
        if (firstnonspace == -1)
            firstnonspace = 0;

        for (int i = firstpopline; i <= lastpopline; ++i)
        {
            const std::string &ref = lines[i - 1];
            if (firstnonspace < (int) ref.length())
                ss << ref.substr(firstnonspace);
            ss << EOL;
        }
    }

    return (ss.str());
}

static int _viewmap_flash_colour()
{
    if (you.attribute[ATTR_SHADOWS])
        return (DARKGREY);
    else if (you.berserk())
        return (RED);

    return (BLACK);
}

// Updates one square of the view area. Should only be called for square
// in LOS.
void view_update_at(const coord_def &pos)
{
    if (pos == you.pos())
        return;

    const coord_def vp = grid2view(pos);
    const coord_def ep = view2show(vp);
    env.show.update_at(pos, ep);

#ifndef USE_TILE
    show_type object = env.show(ep);
    if (!object)
        return;
    glyph g = get_show_glyph(object);

    int flash_colour = you.flash_colour;
    if (flash_colour == BLACK)
        flash_colour = _viewmap_flash_colour();

    cgotoxy(vp.x, vp.y);
    put_colour_ch(flash_colour? real_colour(flash_colour) : g.col, g.ch);

    // Force colour back to normal, else clrscr() will flood screen
    // with this colour on DOS.
    textattr(LIGHTGREY);
#endif
}

#ifndef USE_TILE
void flash_monster_colour(const monsters *mon, unsigned char fmc_colour,
                          int fmc_delay)
{
    if (you.can_see(mon))
    {
        unsigned char old_flash_colour = you.flash_colour;
        coord_def c(mon->pos());

        you.flash_colour = fmc_colour;
        view_update_at(c);

        update_screen();
        delay(fmc_delay);

        you.flash_colour = old_flash_colour;
        view_update_at(c);
        update_screen();
    }
}
#endif

bool view_update()
{
    if (you.num_turns > you.last_view_update)
    {
        viewwindow(false);
        return (true);
    }
    return (false);
}

void flash_view(int colour)
{
    you.flash_colour = colour;
    viewwindow(false, false);
}

void flash_view_delay(int colour, long flash_delay)
{
    flash_view(colour);
    // Scale delay to match change in arena_delay.
    if (crawl_state.arena)
    {
        flash_delay *= Options.arena_delay;
        flash_delay /= 600;
    }

    delay(flash_delay);
}

static void _debug_pane_bounds()
{
#if DEBUG_PANE_BOUNDS
    // Doesn't work for HUD because print_stats() overwrites it.

    if (crawl_view.mlistsz.y > 0)
    {
        textcolor(WHITE);
        cgotoxy(1,1, GOTO_MLIST);
        cprintf("+   L");
        cgotoxy(crawl_view.mlistsz.x-4, crawl_view.mlistsz.y, GOTO_MLIST);
        cprintf("L   +");
    }

    cgotoxy(1,1, GOTO_STAT);
    cprintf("+  H");
    cgotoxy(crawl_view.hudsz.x-3, crawl_view.hudsz.y, GOTO_STAT);
    cprintf("H  +");

    cgotoxy(1,1, GOTO_MSG);
    cprintf("+ M");
    cgotoxy(crawl_view.msgsz.x-2, crawl_view.msgsz.y, GOTO_MSG);
    cprintf("M +");

    cgotoxy(crawl_view.viewp.x, crawl_view.viewp.y);
    cprintf("+V");
    cgotoxy(crawl_view.viewp.x+crawl_view.viewsz.x-2,
            crawl_view.viewp.y+crawl_view.viewsz.y-1);
    cprintf("V+");
    textcolor(LIGHTGREY);
#endif
}

enum update_flags
{
    UF_AFFECT_EXCLUDES = (1 << 0),
    UF_ADDED_EXCLUDE   = (1 << 1)
};

// Do various updates when the player sees a cell. Returns whether
// exclusion LOS might have been affected.
static int player_view_update_at(const coord_def &gc)
{
    const coord_def ep = grid2show(gc);
    maybe_remove_autoexclusion(gc);
    int ret = 0;

    // Set excludes in a radius of 1 around harmful clouds genereated
    // by neither monsters nor the player.
    const int cloudidx = env.cgrid(gc);
    if (cloudidx != EMPTY_CLOUD && !crawl_state.arena)
    {
        cloud_struct &cl   = env.cloud[cloudidx];
        cloud_type   ctype = cl.type;

        bool did_exclude = false;
        if (!is_harmless_cloud(ctype)
            && cl.whose  == KC_OTHER
            && cl.killer == KILL_MISC)
        {
            for (adjacent_iterator ai(gc, false); ai; ++ai)
            {
                // Optionally add exclude, deferring updates.
                if (!cell_is_solid(*ai))
                {
                    bool was_exclusion = is_exclude_root(*ai);
                    set_exclude(*ai, 0, false, false, true);
                    if (!did_exclude && !was_exclusion)
                        ret |= UF_ADDED_EXCLUDE;
                }
            }
        }
    }

    // Print tutorial messages for features in LOS.
    if (Tutorial.tutorial_left)
        tutorial_observe_cell(gc);

    if (!player_in_mappable_area())
        return (ret);

    if (is_terrain_changed(gc) || !is_terrain_seen(gc))
        ret |= UF_AFFECT_EXCLUDES;

    set_terrain_seen(gc);
    set_map_knowledge_obj(gc, to_knowledge(env.show(ep)));
    set_map_knowledge_detected_mons(gc, false);
    set_map_knowledge_detected_item(gc, false);

#ifdef USE_TILE
    // We remove any references to mcache when
    // writing to the background.
    if (Options.clean_map)
        env.tile_bk_fg(gc) = get_clean_map_idx(env.tile_fg(ep));
    else
        env.tile_bk_fg(gc) = env.tile_fg(ep);
    env.tile_bk_bg(gc) = env.tile_bg(ep);
#endif

    if (Options.clean_map && env.show.get_backup(ep))
        set_map_knowledge_obj(gc, to_knowledge(env.show.get_backup(ep)));

    return (ret);
}

static void player_view_update()
{
    std::vector<coord_def> update_excludes;
    bool need_update = false;
    for (radius_iterator ri(&you.get_los()); ri; ++ri)
    {
        int flags = player_view_update_at(*ri);
        if (flags & UF_AFFECT_EXCLUDES)
            update_excludes.push_back(*ri);
        if (flags & UF_ADDED_EXCLUDE)
            need_update = true;
    }
    // Update exclusion LOS for possibly affected excludes.
    update_exclusion_los(update_excludes);
    // Catch up on deferred updates for cloud excludes.
    if (need_update)
        deferred_exclude_update();
}

#ifdef USE_TILE
void tile_draw_floor()
{
    for (int cy = 0; cy < env.tile_fg.height(); cy++)
        for (int cx = 0; cx < env.tile_fg.width(); cx++)
        {
            const coord_def ep(cx, cy);
            const coord_def gc = show2grid(ep);

            int bg = TILE_DNGN_UNSEEN | tile_unseen_flag(gc);

            if (you.see_cell(gc))
            {
                dungeon_feature_type feat = grid_appearance(gc);
                bg = tileidx_feature(feat, gc.x, gc.y);

                if (feat == DNGN_DETECTED_SECRET_DOOR)
                     bg |= TILE_FLAG_WAS_SECRET;
                else if (is_unknown_stair(gc))
                     bg |= TILE_FLAG_NEW_STAIR;
            }


            // init tiles
            env.tile_bg[ep.x][ep.y] = bg;
            env.tile_fg[ep.x][ep.y] = 0;
        }
}
#endif

static void draw_unseen(screen_buffer_t* buffy, const coord_def &gc)
{
#ifndef USE_TILE
    buffy[0] = ' ';
    buffy[1] = DARKGREY;
#else
    tileidx_unseen(buffy[0], buffy[1], ' ', gc);
#endif
}

static void draw_outside_los(screen_buffer_t* buffy, const coord_def &gc)
{
#ifndef USE_TILE
    // Outside the env.show area.
    buffy[0] = get_map_knowledge_char(gc);
    buffy[1] = DARKGREY;
    if (Options.colour_map)
        buffy[1] = real_colour(get_map_col(gc, false));
#else
    unsigned int bg = env.tile_bk_bg(gc);
    unsigned int fg = env.tile_bk_fg(gc);
    if (bg == 0 && fg == 0)
        tileidx_unseen(fg, bg, get_map_knowledge_char(gc), gc);

    buffy[0] = fg;
    buffy[1] = bg | tile_unseen_flag(gc);
#endif
}

static void draw_player(screen_buffer_t* buffy,
                        const coord_def& gc, const coord_def& ep)
{
#ifndef USE_TILE
    // Player overrides everything in cell.
    buffy[0] = you.symbol;
    buffy[1] = you.colour;
    if (you.swimming())
    {
        if (grd(gc) == DNGN_DEEP_WATER)
            buffy[1] = BLUE;
        else
            buffy[1] = CYAN;
    }
    if (Options.use_fake_player_cursor)
        buffy[1] |= COLFLAG_REVERSE;
#else
    buffy[0] = env.tile_fg(ep) = tileidx_player(you.char_class);
    buffy[1] = env.tile_bg(ep);
#endif
}

static void draw_los(screen_buffer_t* buffy,
                     const coord_def& gc, const coord_def& ep)
{
#ifndef USE_TILE
    glyph g = get_show_glyph(env.show(ep));
    buffy[0] = g.ch;
    buffy[1] = g.col;
#else
    buffy[0] = env.tile_fg(ep);
    buffy[1] = env.tile_bg(ep);
#endif
}

static void draw_los_backup(screen_buffer_t* buffy,
                            const coord_def& gc, const coord_def& ep)
{
#ifndef USE_TILE
    buffy[0] = get_map_knowledge_char(gc);
    buffy[1] = DARKGREY;

    if (Options.colour_map)
        buffy[1] = real_colour(get_map_col(gc, false));
#else
    if (env.tile_bk_fg(gc) != 0
        || env.tile_bk_bg(gc) != 0)
    {
        buffy[0] = env.tile_bk_fg(gc);
        buffy[1] = env.tile_bk_bg(gc) | tile_unseen_flag(gc);
    }
    else
        tileidx_unseen(buffy[0], buffy[1], get_map_knowledge_char(gc), gc);
#endif
}

//---------------------------------------------------------------
//
// Draws the main window using the character set returned
// by get_show_glyph().
//
// If monster_updates is set, stealth and conversion checks
// take place. Should be set once per turn.
//
// If show_updates is set, env.show and dependent structures
// are updated. Should be set if anything in view has changed.
//---------------------------------------------------------------
void viewwindow(bool monster_updates, bool show_updates)
{
    if (you.duration[DUR_TIME_STEP])
        return;
    flush_prev_message();

    screen_buffer_t *buffy(crawl_view.vbuf);


#ifdef USE_TILE
    tiles.clear_text_tags(TAG_NAMED_MONSTER);
    mcache.clear_nonref();
#endif

    if (show_updates)
    {
        you.update_los();

#ifdef USE_TILE
        tile_draw_floor();
        tile_draw_rays(true);
        tiles.clear_overlays();
#endif

        env.show.init();
    }

    if (monster_updates && !crawl_state.arena)
        monster_grid_updates();

    if (show_updates)
        player_view_update();

    bool run_dont_draw = you.running && Options.travel_delay < 0
                && (!you.running.is_explore() || Options.explore_delay < 0);

    if (run_dont_draw || you.asleep())
        return;

    cursor_control cs(false);

    int flash_colour = you.flash_colour;
    if (flash_colour == BLACK)
        flash_colour = _viewmap_flash_colour();

    const coord_def &tl = crawl_view.viewp;
    const coord_def br  = tl + crawl_view.viewsz - coord_def(1,1);
    int bufcount = 0;
    for (rectangle_iterator ri(tl, br); ri; ++ri, bufcount += 2)
    {
        // in grid coords
        const coord_def gc = view2grid(*ri);
        const coord_def ep = view2show(grid2view(gc));

        if (!map_bounds(gc))
            draw_unseen(&buffy[bufcount], gc);
        else if (!crawl_view.in_grid_los(gc))
            draw_outside_los(&buffy[bufcount], gc);
        else if (gc == you.pos()
                 && !crawl_state.arena && !crawl_state.arena_suspended)
        {
            draw_player(&buffy[bufcount], gc, ep);
        }
        else if (you.see_cell(gc))
            draw_los(&buffy[bufcount], gc, ep);
        else
            draw_los_backup(&buffy[bufcount], gc, ep);

        // Alter colour if flashing the characters vision.
        if (flash_colour)
        {
#ifndef USE_TILE
            buffy[bufcount + 1] = you.see_cell(gc) ? real_colour(flash_colour)
                                                   : DARKGREY;
#endif
        }
        else if (crawl_state.darken_range >= 0)
        {
            bool out_of_range = grid_distance(you.pos(), gc) >
                                    crawl_state.darken_range
                                || !you.see_cell(gc);
#ifndef USE_TILE
            if (out_of_range)
                buffy[bufcount + 1] = DARKGREY;
#else
            if (out_of_range && you.see_cell(gc))
                buffy[bufcount + 1] |= TILE_FLAG_OOR;
#endif
        }
#ifdef USE_TILE
        // Grey out grids that cannot be reached due to beholders.
        else if (you.get_beholder(gc))
            buffy[bufcount + 1] |= TILE_FLAG_OOR;
#endif
    }

    // Leaving it this way because short flashes can occur in long ones,
    // and this simply works without requiring a stack.
    you.flash_colour = BLACK;

#ifndef USE_TILE
    you.last_view_update = you.num_turns;
    puttext(crawl_view.viewp.x, crawl_view.viewp.y,
            crawl_view.viewp.x + crawl_view.viewsz.x - 1,
            crawl_view.viewp.y + crawl_view.viewsz.y - 1,
            buffy);
    update_monster_pane();
#else
    tiles.set_need_redraw();
    tiles.load_dungeon(&buffy[0], crawl_view.vgrdc);
    tiles.update_inventory();
#endif

    _debug_pane_bounds();
}

////////////////////////////////////////////////////////////////////////////
// Term resize handling (generic).

void handle_terminal_resize(bool redraw)
{
    crawl_state.terminal_resized = false;

    if (crawl_state.terminal_resize_handler)
        (*crawl_state.terminal_resize_handler)();
    else
        crawl_view.init_geometry();

    if (redraw)
        redraw_screen();
}