# Possible values for the tile property: # :UNEXPLORED, #haven't seen this square yet # :UNKNOWN, #don't know what's on this square because there's something on top of it # :UNKNOWN_PASSABLE, #this is a floor/corridor whatever that you can walk on, but can't tell what's there because there's an object on top of it # :FLOOR, # :WALL, # :OPEN_DOOR, # :BROKEN_DOOR, # :CLOSED_DOOR, # :LOCKED_DOOR, # :CORRIDOR, # :DOORWAY, # :STAIRCASE_UP, # :STAIRCASE_DOWN, # : # :FOUNTAIN, # :SINK, # :GRAVE, # :TREE, # :UNKNOWN_ALTAR, # :NEUTRAL_ALTAR, # :CHAOTIC_ALTAR, # :LAWFUL_ALTAR, # :UNALIGNED_ALTAR, # :UNKNOWN_TRAP, # :PIT, # :ARROW_TRAP, # :BEAR_TRAP, # :DART_TRAP, # :MAGIC_TRAP, # :RUST_TRAP, # :SQUEAKY_BOARD, # :FALLING_ROCK_TRAP, # :ANTI_MAGIC_FIELD, # :FIRE_TRAP, # :HOLE, # :TELEPORT_TRAP, # :SLEEPING_GAS_TRAP, # :ROLLING_BOULDER_TRAP, # :TRAP_DOOR, # :LEVEL_TELEPORTER, # :WEB, class Map < Array $WIDTH = 80 $HEIGHT = 24 def initialize (0...$WIDTH).each do |x| row = [] (0...$HEIGHT).each do |y| row[j] = MapCell.new(self, x, y) end self.insert(x, row) end end def [] (x, y=nil) raise IndexError if !x.is_a?(Integer) || x<0 || x>=$WIDTH return self.at(x) if !y raise IndexError if !y.is_a?(Integer) || y<0 || y>=$HEIGHT return self.at(x)[y] end # Returns a list of map cells that are physically adjacent to a specified position, taking into account map edges and (optionally) diagonals. def neighbours (x,y, diagonals=true) n = [] not_left_edge = (x>0) not_right_edge = (x<$WIDTH-1) not_top_edge = (y>0) not_bottom_edge = (y<$HEIGHT-1) n.push(self[x-1, y ]) if not_left_edge n.push(self[x , y-1]) if not_top_edge n.push(self[x+1, y ]) if not_right_edge n.push(self[x , y+1]) if not_bottom_edge if (diagonals) n.push(self[x-1, y-1]) if not_left_edge && not_top_edge n.push(self[x-1, y+1]) if not_left_edge && not_bottom_edge n.push(self[x+1, y-1]) if not_right_edge && not_top_edge n.push(self[x+1, y+1]) if not_right_edge && not_bottom_edge end n end # Returns a list of map cells that can be physically moved to from a specified position, taking into account both map edges and diagonal doorways. def move_neighbours (x,y) # Make sure to check for doorways that can't be passed through diagonally! # Start with the current list from neighbours() # foreach cell in that list, remove that cell if either of the conditions are met: # it is not passable # it is diagonal, and either it or this square is a doorway n = [] not_left_edge = (x>0) not_right_edge = (x<$WIDTH-1) not_top_edge = (y>0) not_bottom_edge = (y<$HEIGHT-1) n.push(self[x-1, y ]) if not_left_edge n.push(self[x , y-1]) if not_top_edge n.push(self[x+1, y ]) if not_right_edge n.push(self[x , y+1]) if not_bottom_edge if (!self[x,y].is_doorway?) (n.push(self[x-1, y-1]) if (!self[x-1, y-1].is_doorway?)) if not_left_edge && not_top_edge (n.push(self[x-1, y+1]) if (!self[x-1, y+1].is_doorway?)) if not_left_edge && not_bottom_edge (n.push(self[x+1, y-1]) if (!self[x+1, y-1].is_doorway?)) if not_right_edge && not_top_edge (n.push(self[x+1, y+1]) if (!self[x+1, y+1].is_doorway?)) if not_right_edge && not_bottom_edge end n end # Scrape the VT102 screen for usable information. def update () if ($vt102.row_text(0).trim.length == 0) # Only process the map if there's an empty first string. x,y = 0,0 # Go through the entire screen, telling each map cell to use the info present in the character seen on the map. 0.upto($HEIGHT) { |y| row = $vt102.row_text(y+1) 0.upto($WIDTH) { |x| self[x,y].use_info(row[x]) } } row = $vt102.row_text(y+1) 0.upto($WIDTH) end end end class MapCell attr_reader :map # A pointer to the game level that holds this cell attr_reader :tile, # The type of tile that we understand this cell to be (such as wall, floor, lava, door, altar, stairs, etc) :actor, # A pointer to the actor that occupies this tile (since only one actor can be on a tile at a time). If no actor, then nil. :item_list, # An array of all the items we understand to be on this square. :is_shop? # A boolean as to if this square is a shop or not. attr_reader :search_cnt # The number of times that this square has been searched on. More of an informal variable used for dungeon exploration. def has_item? if (item_list != null) return item_list.count > 0 else return false end end def is_doorway? return ((tile == :OPEN_DOOR) || (tile == :CLOSED_DOOR) || (tile == :LOCKED_DOOR)) end def move_neighbours() @map.move_neighbours(@x, @y) end # neighbours() returns a list of cells that are adjacent to/from this square. def neighbours() @map.neighbours(@x, @y, true) end # NOTE: This is just a rough start, and it still recognizes the old Jessie tileset that Eidolos hates even though it made the programming simpler. def use_appearance ( new_appearance ) actor = nothing # Always reset the actor, because they might have moved. case (new_appearance) when '.' tile = :FLOOR when '{' if ((tile == :UNKNOWN) || (tile == :UNEXPLORED)) RequestMoreInfo() end when '-' when '|' RequestMoreInfo() when '#' if ((tile == :UNKNOWN) || (tile == :UNEXPLORED)) tile = :WALL end when '_' RequestMoreInfo() when '^' if (!this.IsTrap()) RequestMoreInfo() end when '+' RequestMoreInfo() when '>' if (tile != :STAIRCASE_DOWN) RequestMoreInfo() # this[cursor] = :STAIRCASE_DOWN end when '<' if (tile != :STAIRCASE_UP) RequestMoreInfo() # this[cursor] = :STAIRCASE_UP end when ' ' if (tile == :UNKNOWN) tile = :UNEXPLORED end else RequestMoreInfo() end end end