class Natural20::MapRenderer

Constants

DEFAULT_TOKEN_COLOR

Attributes

battle[R]
map[R]

Public Class Methods

new(map, battle = nil) click to toggle source

@param map [Natural20::BattleMap]

# File lib/natural_20/cli/map_renderer.rb, line 7
def initialize(map, battle = nil)
  @map = map
  @battle = battle
end

Public Instance Methods

render(entity: nil, line_of_sight: nil, path: [], acrobatics_checks: [], athletics_checks: [], select_pos: nil, update_on_drop: true, path_char: nil, highlight: {}, viewport_size: nil, top_position: [ 0, 0 ]) click to toggle source

Renders the current map to a string @param line_of_sight [Array,Natural20::Entity] Entity or entities to consier line of sight rendering for @param path [Array] Array of path to render movement @param select_pos [Array] coordinate position to render selection cursor @param update_on_drop [Boolean] If true, only render line of sight if movement has been confirmed @return [String]

# File lib/natural_20/cli/map_renderer.rb, line 18
def render(entity: nil, line_of_sight: nil, path: [], acrobatics_checks: [], athletics_checks: [], select_pos: nil, update_on_drop: true, path_char: nil, highlight: {}, viewport_size: nil, top_position: [
  0, 0
])
  highlight_positions = highlight.keys.map { |entity| @map.entity_squares(entity) }.flatten(1)

  viewport_size ||= map.size

  top_x, top_y = top_position
  top_x = map.size[0] - top_x < viewport_size[0] ? map.size[0] - viewport_size[0] : top_x if viewport_size
  top_y = map.size[0] - top_y < viewport_size[1] ? map.size[1] - viewport_size[1] : top_y if viewport_size

  right_x = top_x + viewport_size[0] >= map.size[1] ? map.size[1] - 1 : top_x + viewport_size[0]
  right_y = top_y + viewport_size[1] >= map.size[0] ? map.size[0] - 1 : top_y + viewport_size[1]

  (top_x..right_x).map do |row_index|
    (top_y..right_y).map do |col_index|
      c = map.base_map[col_index][row_index]
      display = render_position(c, col_index, row_index, path: path, override_path_char: path_char, entity: entity, line_of_sight: line_of_sight,
                                                         update_on_drop: update_on_drop, acrobatics_checks: acrobatics_checks,
                                                         athletics_checks: athletics_checks)

      display = display.colorize(background: :red) if highlight_positions.include?([col_index, row_index])

      if select_pos && select_pos == [col_index, row_index]
        display.colorize(background: :white)
      else
        display
      end
    end.join
  end.join("\n") + "\n"
end

Private Instance Methods

any_line_of_sight?(line_of_sight, entity) click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 147
def any_line_of_sight?(line_of_sight, entity)
  return true if @battle.nil?

  if line_of_sight.is_a?(Array)
    line_of_sight.detect do |l|
      @battle.can_see?(l,
                       entity)
    end
  else
    @battle.can_see?(line_of_sight,
                     entity)
  end
end
base_map() click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 161
def base_map
  @map.base_map
end
location_is_visible?(update_on_drop, pos_x, pos_y, path) click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 140
def location_is_visible?(update_on_drop, pos_x, pos_y, path)
  return @map.line_of_sight?(path.last[0], path.last[1], col_index, row_index, nil, false) unless update_on_drop

  @map.line_of_sight?(path.last[0], path.last[1], pos_x, pos_y,
                      1, false) || @map.line_of_sight?(path.first[0], path.first[1], pos_x, pos_y, nil, false)
end
npc_token(pos_x, pos_y) click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 80
def npc_token(pos_x, pos_y)
  entity = tokens[pos_x][pos_y]
  color = (entity[:entity]&.color.presence || DEFAULT_TOKEN_COLOR).to_sym
  token(entity, pos_x, pos_y).colorize(color)
end
object_token(pos_x, pos_y) click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 63
def object_token(pos_x, pos_y)
  object_meta = @map.object_at(pos_x, pos_y)
  return nil unless object_meta

  m_x, m_y = @map.interactable_objects[object_meta]
  color = (object_meta.color.presence || DEFAULT_TOKEN_COLOR).to_sym

  return nil unless object_meta.token
  return :inherit if object_meta.token.to_s == 'inherit'

  if object_meta.token.is_a?(Array)
    object_meta.token[pos_y - m_y][pos_x - m_x].colorize(color)
  else
    object_meta.token.colorize(color)
  end
end
render_light(pos_x, pos_y) click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 52
def render_light(pos_x, pos_y)
  intensity = @map.light_at(pos_x, pos_y)
  if intensity >= 1.0
    :light_yellow
  elsif intensity >= 0.5
    :light_black
  else
    :black
  end
end
render_position(c, col_index, row_index, path: [], override_path_char: nil, entity: nil, line_of_sight: nil, update_on_drop: true, acrobatics_checks: [], athletics_checks: []) click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 86
def render_position(c, col_index, row_index, path: [], override_path_char: nil, entity: nil, line_of_sight: nil, update_on_drop: true, acrobatics_checks: [],
                    athletics_checks: [])
  background_color = render_light(col_index, row_index)
  default_ground = "\u00B7".encode('utf-8').colorize(color: DEFAULT_TOKEN_COLOR, background: background_color)
  c = case c
      when '.', '?'
        default_ground
      else
        token = object_token(col_index, row_index)
        if token && token != :inherit
          token&.colorize(background: background_color) || default_ground
        else
          c
        end
      end

  # render map layer
  token = if tokens[col_index][row_index]&.fetch(:entity)&.dead?
            '`'.colorize(color: DEFAULT_TOKEN_COLOR)
          elsif tokens[col_index][row_index]
            if any_line_of_sight?(line_of_sight, tokens[col_index][row_index][:entity])
              npc_token(col_index,
                        row_index)
            end
          end

  token = (token&.colorize(background: background_color) || c)

  if !path.empty? && (override_path_char.nil? || path[0] != [col_index, row_index])
    if path.include?([col_index, row_index])
      path_char = override_path_char || token
      path_color = entity && map.jump_required?(entity, col_index, row_index) ? :red : :blue
      path_char = "\u2713" if athletics_checks.include?([col_index,
                                                         row_index]) || acrobatics_checks.include?([col_index,
                                                                                                    row_index])

      colored_path = path_char.colorize(color: path_color, background: :white)
      colored_path = colored_path.blink if path[0] == [col_index, row_index]
      return colored_path
    end
    return ' '.colorize(background: :black) if line_of_sight && !location_is_visible?(update_on_drop, col_index, row_index,
                                                                                      path)
  else
    has_line_of_sight = if line_of_sight.is_a?(Array)
                          line_of_sight.detect { |l| @map.can_see_square?(l, col_index, row_index) }
                        elsif line_of_sight
                          @map.can_see_square?(line_of_sight, col_index, row_index)
                        end
    return ' '.colorize(background: :black) if line_of_sight && !has_line_of_sight
  end

  token
end
token(entity, pos_x, pos_y) click to toggle source

Returns the character to use as token for a square @param entity [Natural20::Entity] @param pos_x [Integer] @param pos_y [Integer] @return [String]

# File lib/natural_20/cli/map_renderer.rb, line 170
def token(entity, pos_x, pos_y)
  if entity[:entity].token
    m_x, m_y = @map.entities[entity[:entity]]
    entity[:entity].token[pos_y - m_y][pos_x - m_x]
  else
    @map.tokens[pos_x][pos_y][:token]
  end
end
tokens() click to toggle source
# File lib/natural_20/cli/map_renderer.rb, line 179
def tokens
  @map.tokens
end