spriteAt :: SpriteEnv -> Point -> Sprite
spriteAt env p = if canPlayerSee p then tileOrMobSprite lvl p
                 else seenTileSprite lvl p
    where
        lvl = spriteLevel env
        canPlayerSee p = canSee lvl (player lvl) p || canSense lvl (player lvl) p

        tileColor Floor = white
        tileColor Cavern = grey
        tileColor Rock = grey
        tileColor (StairUp _) = white
        tileColor (StairDown _) = white

        mobColor "Kobold" = purple
        mobColor "Goblin" = green
        mobColor "Grid Bug" = purple
        mobColor "Orc" = yellow
        mobColor "Zombie" = dgrey
        mobColor "Rat" = brown
        mobColor otherwise = white

        itemColor (Item "Blue"          (Potion _)) = blue
        itemColor (Item "Yellow"        (Potion _)) = yellow
        itemColor (Item "Black"         (Potion _)) = dgrey
        itemColor (Item "Red"           (Potion _)) = red
        itemColor (Item "White"         (Potion _)) = white
        itemColor (Item "Green"         (Potion _)) = green
        itemColor (Item "Orange"        (Potion _)) = orange
        itemColor (Item "Leather Armor" (Armor  _)) = brown
        itemColor (Item "Plate Mail"    (Armor  _)) = white
        itemColor (Item "Full Plate"    (Armor  _)) = white
        itemColor (Item "Small Shield"  (Armor  _)) = grey
        itemColor (Item "Tower Shield"  (Armor  _)) = white
        itemColor (Item n               (Armor  _)) = grey
        itemColor (Item "Quarterstaff"  (Weapon _)) = brown
        itemColor (Item "Bow"           (Weapon _)) = brown
        itemColor (Item "Arrow"         (Weapon _)) = brown
        itemColor (Item "Dagger"        (Weapon _)) = dgrey
        itemColor (Item "Mace"          (Weapon _)) = grey
        itemColor (Item "Ornate Sword"  (Weapon _)) = yellow
        itemColor (Item n               (Weapon _)) = lyellow
        itemColor (Item n               (Scroll _)) = white
        itemColor otherwise = white

        featureColor (Chest    _) = yellow
        featureColor (Fountain 0) = grey
        featureColor (Fountain _) = blue
        featureColor Altar        = grey

        tileSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        tileSprite lvl p = case findTileAt p lvl of
                               Nothing -> Nothing
                               Just  t -> 
                                   let charSpr   = CharSprite p (fromTile t) (SpriteAttr (tileColor t) black)
                                       wallSpr w = WallSprite p w (SpriteAttr (tileColor t) black)
                                   in  Just $ maybe charSpr wallSpr (seenWallType env p)
        itemSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        itemSprite lvl p = case findItemsAt p lvl of
                               (i:_) -> Just (CharSprite p (itemSymbol i) (SpriteAttr (itemColor i) black))
                               []    -> Nothing
        mobSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        mobSprite lvl p = case findTileOrMob p lvl of
                               Right m -> if isVisible m then
                                            Just (CharSprite p (symbol m) (SpriteAttr (mobColor (mobName m)) black))
                                          else if isPlayer m then
                                            Just (CharSprite p ' ' (SpriteAttr white (50,50,50)))
                                          else
                                            Nothing
                               Left _  -> Nothing
        featureSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        featureSprite lvl p = case L.lookup p (features lvl) of
                                Just f  -> Just (CharSprite p (fromFeature f) (SpriteAttr (featureColor f) black))
                                Nothing -> Nothing
        tileOrMobSprite :: DLevel -> (Int, Int) -> Sprite
        tileOrMobSprite lvl p = let sprites = [mobSprite lvl p, featureSprite lvl p, itemSprite lvl p, tileSprite lvl p]
                                    sprite  = listToMaybe (catMaybes sprites)
                                in  if isJust sprite then fromJust sprite
                                    else CharSprite p ' ' (SpriteAttr black black)
        seenTileSprite lvl p = if p `elem` spriteSeen env then stale (fromJust (listToMaybe (catMaybes [featureSprite lvl p, itemSprite lvl p, tileSprite lvl p])))
                               else CharSprite p ' ' (SpriteAttr black black)
        stale (CharSprite    p c _) = CharSprite    p c (SpriteAttr dgrey black)
        stale (MessageSprite p c _) = MessageSprite p c (SpriteAttr dgrey black)
        stale (WallSprite    p c _) = WallSprite    p c (SpriteAttr dgrey black)
spriteAt :: SpriteEnv -> Point -> Sprite
spriteAt env p = if canPlayerSee p then tileOrMobSprite lvl p
                 else seenTileSprite lvl p
    where
        lvl = spriteLevel env
        canPlayerSee p = canSee lvl (player lvl) p || canSense lvl (player lvl) p

        tileColor Floor = white
        tileColor Cavern = grey
        tileColor Rock = grey
        tileColor (StairUp _) = white
        tileColor (StairDown _) = white

        mobColor "Kobold" = purple
        mobColor "Goblin" = green
        mobColor "Grid Bug" = purple
        mobColor "Orc" = yellow
        mobColor "Zombie" = dgrey
        mobColor "Rat" = brown
        mobColor otherwise = white

        itemColor (Item "Blue"          (Potion _)) = blue
        itemColor (Item "Yellow"        (Potion _)) = yellow
        itemColor (Item "Black"         (Potion _)) = dgrey
        itemColor (Item "Red"           (Potion _)) = red
        itemColor (Item "White"         (Potion _)) = white
        itemColor (Item "Green"         (Potion _)) = green
        itemColor (Item "Orange"        (Potion _)) = orange
        itemColor (Item "Leather Armor" (Armor  _)) = brown
        itemColor (Item "Plate Mail"    (Armor  _)) = white
        itemColor (Item "Full Plate"    (Armor  _)) = white
        itemColor (Item "Small Shield"  (Armor  _)) = grey
        itemColor (Item "Tower Shield"  (Armor  _)) = white
        itemColor (Item n               (Armor  _)) = grey
        itemColor (Item "Quarterstaff"  (Weapon _)) = brown
        itemColor (Item "Bow"           (Weapon _)) = brown
        itemColor (Item "Arrow"         (Weapon _)) = brown
        itemColor (Item "Dagger"        (Weapon _)) = dgrey
        itemColor (Item "Mace"          (Weapon _)) = grey
        itemColor (Item "Ornate Sword"  (Weapon _)) = yellow
        itemColor (Item n               (Weapon _)) = lyellow
        itemColor (Item n               (Scroll _)) = white
        itemColor otherwise = white

        featureColor (Chest    _) = yellow
        featureColor (Fountain 0) = grey
        featureColor (Fountain _) = blue
        featureColor Altar        = grey

        tileSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        tileSprite lvl p = case findTileAt p lvl of
                               Nothing -> Nothing
                               Just  t -> 
                                   let charSpr   = CharSprite p (fromTile t) (SpriteAttr (tileColor t) black)
                                       wallSpr w = WallSprite p w (SpriteAttr (tileColor t) black)
                                   in  Just $ maybe charSpr wallSpr (seenWallType env p)
        itemSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        itemSprite lvl p = case findItemsAt p lvl of
                               (i:_) -> Just (CharSprite p (itemSymbol i) (SpriteAttr (itemColor i) black))
                               []    -> Nothing
        mobSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        mobSprite lvl p = case findTileOrMob p lvl of
                               Right m -> if isVisible m then
                                            Just (CharSprite p (symbol m) (SpriteAttr (mobColor (mobName m)) black))
                                          else if isPlayer m then
                                            Just (CharSprite p ' ' (SpriteAttr white (50,50,50)))
                                          else
                                            Nothing
                               Left _  -> Nothing
        featureSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        featureSprite lvl p = case L.lookup p (features lvl) of
                                Just f  -> Just (CharSprite p (fromFeature f) (SpriteAttr (featureColor f) black))
                                Nothing -> Nothing
        tileOrMobSprite :: DLevel -> (Int, Int) -> Sprite
        tileOrMobSprite lvl p = let sprites = [mobSprite lvl p, featureSprite lvl p, itemSprite lvl p, tileSprite lvl p]
                                    sprite  = listToMaybe (catMaybes sprites)
                                in  if isJust sprite then fromJust sprite
                                    else CharSprite p ' ' (SpriteAttr black black)
        seenTileSprite lvl p = if p `elem` spriteSeen env then stale (fromJust (listToMaybe (catMaybes [featureSprite lvl p, itemSprite lvl p, tileSprite lvl p])))
                               else CharSprite p ' ' (SpriteAttr black black)
        stale (CharSprite    p c _) = CharSprite    p c (SpriteAttr dgrey black)
        stale (MessageSprite p c _) = MessageSprite p c (SpriteAttr dgrey black)
        stale (WallSprite    p c _) = WallSprite    p c (SpriteAttr dgrey black)
spriteAt env p = if canPlayerSee p then tileOrMobSprite lvl p
                 else seenTileSprite lvl p
    where
        lvl = spriteLevel env
        canPlayerSee p = canSee lvl (player lvl) p || canSense lvl (player lvl) p

        tileColor Floor = white
        tileColor Cavern = grey
        tileColor Rock = grey
        tileColor (StairUp _) = white
        tileColor (StairDown _) = white

        mobColor "Kobold" = purple
        mobColor "Goblin" = green
        mobColor "Grid Bug" = purple
        mobColor "Orc" = yellow
        mobColor "Zombie" = dgrey
        mobColor "Rat" = brown
        mobColor otherwise = white

        itemColor (Item "Blue"          (Potion _)) = blue
        itemColor (Item "Yellow"        (Potion _)) = yellow
        itemColor (Item "Black"         (Potion _)) = dgrey
        itemColor (Item "Red"           (Potion _)) = red
        itemColor (Item "White"         (Potion _)) = white
        itemColor (Item "Green"         (Potion _)) = green
        itemColor (Item "Orange"        (Potion _)) = orange
        itemColor (Item "Leather Armor" (Armor  _)) = brown
        itemColor (Item "Plate Mail"    (Armor  _)) = white
        itemColor (Item "Full Plate"    (Armor  _)) = white
        itemColor (Item "Small Shield"  (Armor  _)) = grey
        itemColor (Item "Tower Shield"  (Armor  _)) = white
        itemColor (Item n               (Armor  _)) = grey
        itemColor (Item "Quarterstaff"  (Weapon _)) = brown
        itemColor (Item "Bow"           (Weapon _)) = brown
        itemColor (Item "Arrow"         (Weapon _)) = brown
        itemColor (Item "Dagger"        (Weapon _)) = dgrey
        itemColor (Item "Mace"          (Weapon _)) = grey
        itemColor (Item "Ornate Sword"  (Weapon _)) = yellow
        itemColor (Item n               (Weapon _)) = lyellow
        itemColor (Item n               (Scroll _)) = white
        itemColor otherwise = white

        featureColor (Chest    _) = yellow
        featureColor (Fountain 0) = grey
        featureColor (Fountain _) = blue
        featureColor Altar        = grey

        tileSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        tileSprite lvl p = case findTileAt p lvl of
                               Nothing -> Nothing
                               Just  t -> 
                                   let charSpr   = CharSprite p (fromTile t) (SpriteAttr (tileColor t) black)
                                       wallSpr w = WallSprite p w (SpriteAttr (tileColor t) black)
                                   in  Just $ maybe charSpr wallSpr (seenWallType env p)
        itemSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        itemSprite lvl p = case findItemsAt p lvl of
                               (i:_) -> Just (CharSprite p (itemSymbol i) (SpriteAttr (itemColor i) black))
                               []    -> Nothing
        mobSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        mobSprite lvl p = case findTileOrMob p lvl of
                               Right m -> if isVisible m then
                                            Just (CharSprite p (symbol m) (SpriteAttr (mobColor (mobName m)) black))
                                          else if isPlayer m then
                                            Just (CharSprite p ' ' (SpriteAttr white (50,50,50)))
                                          else
                                            Nothing
                               Left _  -> Nothing
        featureSprite :: DLevel -> (Int, Int) -> Maybe Sprite
        featureSprite lvl p = case L.lookup p (features lvl) of
                                Just f  -> Just (CharSprite p (fromFeature f) (SpriteAttr (featureColor f) black))
                                Nothing -> Nothing
        tileOrMobSprite :: DLevel -> (Int, Int) -> Sprite
        tileOrMobSprite lvl p = let sprites = [mobSprite lvl p, featureSprite lvl p, itemSprite lvl p, tileSprite lvl p]
                                    sprite  = listToMaybe (catMaybes sprites)
                                in  if isJust sprite then fromJust sprite
                                    else CharSprite p ' ' (SpriteAttr black black)
        seenTileSprite lvl p = if p `elem` spriteSeen env then stale (fromJust (listToMaybe (catMaybes [featureSprite lvl p, itemSprite lvl p, tileSprite lvl p])))
                               else CharSprite p ' ' (SpriteAttr black black)
        stale (CharSprite    p c _) = CharSprite    p c (SpriteAttr dgrey black)
        stale (MessageSprite p c _) = MessageSprite p c (SpriteAttr dgrey black)
        stale (WallSprite    p c _) = WallSprite    p c (SpriteAttr dgrey black)