Module: DisplayController
- Includes:
- Remedy
- Defined in:
- lib/terminal_hero/modules/display_controller.rb
Overview
Controls the display of output to the user
Class Method Summary collapse
-
.calc_view_distance(size: Console.size) ⇒ Object
Set the map render distance to fit within a given console size.
-
.calculate_padding(header, content, size) ⇒ Object
Calculate the amount of padding required to center a map view.
-
.cancel_resize_hook ⇒ Object
Cancel the console resize hook (eg. when leaving the map view).
-
.center_view!(header, content, size) ⇒ Object
Given a header, content and console size, pad the header and content to center them in the console.
-
.clear ⇒ Object
Clear the visible terminal display (without clearing terminal history).
-
.display_ascii(msg) ⇒ Object
Displays a message in an ASCII art font.
-
.display_messages(msgs, pause: true) ⇒ Object
Display a series of messages, waiting for keypress input to advance.
-
.display_stat_menu(stats, points, line_no, header, footer) ⇒ Object
Display the stat point allocation menu to the user.
-
.draw_map(map, player, size: Console.size, view_dist: calc_view_distance(size: size)) ⇒ Object
Draws one frame of the visible portion of the map.
-
.filter_visible(grid, camera_coords, size: Console.size, view_dist: calc_view_distance(size: size)) ⇒ Object
Given a grid, camera co-ordinates and view distances, return a grid containing only squares within the camera’s field of view.
-
.level_up(player, levels) ⇒ Object
When the player levels up, display the number of levels gained.
-
.post_combat(outcome, player, xp_amount) ⇒ Object
Display relevant information to the user after the end of a combat encounter.
-
.prompt_character_name ⇒ Object
Prompt the user to enter a character name when creating a character.
-
.prompt_combat_action(player, enemy) ⇒ Object
Display the combat action selection menu and return user’s selection.
-
.prompt_save_name(name = nil) ⇒ Object
Prompt the user to enter the name of the character they want to attempt to load.
-
.prompt_stat_allocation(starting_stats: GameData::DEFAULT_STATS, starting_points: GameData::STAT_POINTS_PER_LEVEL) ⇒ Object
Prompt the user and get their input to allocate stat points using a stat menu.
-
.prompt_title_menu ⇒ Object
Displays the title menu.
-
.prompt_tutorial(repeat: false) ⇒ Object
Ask the user whether they would like to view the tutorial.
-
.prompt_yes_no(msg, default_no: false) ⇒ Object
Prompt the user for whether to re-try a failed action.
-
.set_resize_hook(map, player) ⇒ Object
Sets a hook to draw the map (with adjusted view distance) when the console is resized.
-
.setup_map_view(player) ⇒ Object
Initialise variables required for draw_map.
Class Method Details
.calc_view_distance(size: Console.size) ⇒ Object
Set the map render distance to fit within a given console size
131 132 133 134 135 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 131 def self.calc_view_distance(size: Console.size) horizontal = Utils.collar(2, size.cols / 4 - 2, GameData::MAX_H_VIEW_DIST) vertical = Utils.collar(2, size.rows / 2 - 5, GameData::MAX_V_VIEW_DIST) return [horizontal, vertical] end |
.calculate_padding(header, content, size) ⇒ Object
Calculate the amount of padding required to center a map view
160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 160 def self.calculate_padding(header, content, size) # Top padding is half of the console height minus the view height top_pad = (size.rows - (header.lines.length + content.lines.length)) / 2 # Get length of the longest line of the view (to determine view width) view_width = [ header.lines.map(&:uncolorize).max_by(&:length).length, content.lines.map(&:uncolorize).max_by(&:length).length ].max # Left padding is half of the console width minus the view width. left_pad = (size.cols - view_width) / 2 return top_pad, left_pad end |
.cancel_resize_hook ⇒ Object
Cancel the console resize hook (eg. when leaving the map view)
202 203 204 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 202 def self.cancel_resize_hook Console::Resize.default_console_resized_hook! end |
.center_view!(header, content, size) ⇒ Object
Given a header, content and console size, pad the header and content to center them in the console.
174 175 176 177 178 179 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 174 def self.center_view!(header, content, size) top_pad, left_pad = calculate_padding(header, content, size) top_pad.times { header.lines.unshift(" ") } content.lines.map! { |line| "#{' ' * left_pad}#{line}" } header.lines.map! { |line| "#{' ' * left_pad}#{line}" } end |
.clear ⇒ Object
Clear the visible terminal display (without clearing terminal history)
236 237 238 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 236 def self.clear ANSI::Screen.safe_reset! end |
.display_ascii(msg) ⇒ Object
Displays a message in an ASCII art font. Return the prompt object for use with subsequent prompts.
28 29 30 31 32 33 34 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 28 def self.display_ascii(msg) clear font = TTY::Font.new(:standard) prompt = TTY::Prompt.new prompt.say(msg.call(font, Console.size)) return prompt end |
.display_messages(msgs, pause: true) ⇒ Object
Display a series of messages, waiting for keypress input to advance
43 44 45 46 47 48 49 50 51 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 43 def self.(msgs, pause: true) prompt = TTY::Prompt.new(quiet: true) print "\n" msgs.each do |msg| puts msg print "\n" prompt.keypress("Press any key...") if pause end end |
.display_stat_menu(stats, points, line_no, header, footer) ⇒ Object
Display the stat point allocation menu to the user
104 105 106 107 108 109 110 111 112 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 104 def self.(stats, points, line_no, header, ) screen = Viewport.new = Content.new lines = stats.values.map { |stat| "#{stat[:name]}: #{stat[:value]}" } lines[line_no] = lines[line_no].colorize(:light_blue) .lines.push " ", "Stat points remaining: #{points}", " " lines.each { |line| << line } screen.draw(, [0, 0], header, ) end |
.draw_map(map, player, size: Console.size, view_dist: calc_view_distance(size: size)) ⇒ Object
Draws one frame of the visible portion of the map
182 183 184 185 186 187 188 189 190 191 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 182 def self.draw_map(map, player, size: Console.size, view_dist: calc_view_distance(size: size)) screen, header, map_display = setup_map_view(player) filter_visible(map.grid, player.coords).each do |row| map_display << row.join(" ") end # Pushing additional row prevents truncation in smaller terminal sizes map_display << " " * (view_dist[0] * 2) center_view!(header, map_display, size) screen.draw(map_display, Size.new(0, 0), header) end |
.filter_visible(grid, camera_coords, size: Console.size, view_dist: calc_view_distance(size: size)) ⇒ Object
Given a grid, camera co-ordinates and view distances, return a grid containing only squares within the camera’s field of view
148 149 150 151 152 153 154 155 156 157 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 148 def self.filter_visible(grid, camera_coords, size: Console.size, view_dist: calc_view_distance(size: size)) h_view_dist, v_view_dist = view_dist # Filter rows outside view distance field_of_view = grid.map do |row| row.reject.with_index { |_cell, x_index| (camera_coords[:x] - x_index).abs > h_view_dist } end # Filter columns outside view distance field_of_view.reject!.with_index { |_row, y_index| (camera_coords[:y] - y_index).abs > v_view_dist } return field_of_view end |
.level_up(player, levels) ⇒ Object
When the player levels up, display the number of levels gained
230 231 232 233 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 230 def self.level_up(player, levels) display_ascii(GameData::ASCII_ART[:level_up]) (GameData::MESSAGES[:leveled_up].call(player, levels)) end |
.post_combat(outcome, player, xp_amount) ⇒ Object
Display relevant information to the user after the end of a combat encounter.
217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 217 def self.post_combat(outcome, player, xp_amount) case outcome when :victory (GameData::MESSAGES[:combat_victory].call(xp_amount)) when :defeat (GameData::MESSAGES[:combat_defeat].call(xp_amount)) (GameData::MESSAGES[:level_progress].call(player)) when :escaped (GameData::MESSAGES[:combat_escaped]) end end |
.prompt_character_name ⇒ Object
Prompt the user to enter a character name when creating a character
54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 54 def self.prompt_character_name begin prompt = display_ascii(GameData::ASCII_ART[:title]) name = prompt.ask("Please enter a name for your character: ") unless InputHandler.character_name_valid?(name) raise InvalidInputError.new(requirements: GameData::VALIDATION_REQUIREMENTS[:character_name]) end rescue InvalidInputError => e ([e..colorize(:red), "Please try again.".colorize(:red)]) retry end return name end |
.prompt_combat_action(player, enemy) ⇒ Object
Display the combat action selection menu and return user’s selection
207 208 209 210 211 212 213 214 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 207 def self.prompt_combat_action(player, enemy) clear (GameData::MESSAGES[:combat_status].call(player, enemy), pause: false) prompt = TTY::Prompt.new answer = prompt.select("\nWhat would you like to do?", GameData::COMBAT_MENU_OPTIONS) print "\n" return answer end |
.prompt_save_name(name = nil) ⇒ Object
Prompt the user to enter the name of the character they want to attempt to load
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 86 def self.prompt_save_name(name = nil) display_ascii(GameData::ASCII_ART[:title]) begin name = TTY::Prompt.new.ask("Please enter the name of the character you want to load: ") if name.nil? unless InputHandler.character_name_valid?(name) raise InvalidInputError.new(requirements: GameData::VALIDATION_REQUIREMENTS[:character_name]) end rescue InvalidInputError => e ([e..colorize(:red)]) return false unless prompt_yes_no(GameData::PROMPTS[:re_load]) name = nil retry end return name end |
.prompt_stat_allocation(starting_stats: GameData::DEFAULT_STATS, starting_points: GameData::STAT_POINTS_PER_LEVEL) ⇒ Object
Prompt the user and get their input to allocate stat points using a stat menu
115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 115 def self.prompt_stat_allocation( starting_stats: GameData::DEFAULT_STATS, starting_points: GameData::STAT_POINTS_PER_LEVEL ) = StatMenu.new(starting_stats, starting_points) (*.get_display_parameters) input = Interaction.new input.loop do |key| finished, stats = .process_input(key.name) return stats if finished (*.get_display_parameters) end end |
.prompt_title_menu ⇒ Object
Displays the title menu
37 38 39 40 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 37 def self. prompt = display_ascii(GameData::ASCII_ART[:title]) return prompt.select("What would you like to do?", GameData::TITLE_MENU_OPTIONS) end |
.prompt_tutorial(repeat: false) ⇒ Object
Ask the user whether they would like to view the tutorial
78 79 80 81 82 83 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 78 def self.prompt_tutorial(repeat: false) display_ascii(GameData::ASCII_ART[:title]) verb = repeat ? "repeat" : "see" = "Would you like to #{verb} the tutorial?" return prompt_yes_no(, default_no: repeat) end |
.prompt_yes_no(msg, default_no: false) ⇒ Object
Prompt the user for whether to re-try a failed action
69 70 71 72 73 74 75 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 69 def self.prompt_yes_no(msg, default_no: false) TTY::Prompt.new.select(msg) do || .default default_no ? "No" : "Yes" .choice "Yes", true .choice "No", false end end |
.set_resize_hook(map, player) ⇒ Object
Sets a hook to draw the map (with adjusted view distance) when the console is resized
195 196 197 198 199 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 195 def self.set_resize_hook(map, player) Console.set_console_resized_hook! do |size| draw_map(map, player, size: size) end end |
.setup_map_view(player) ⇒ Object
Initialise variables required for draw_map
138 139 140 141 142 143 144 |
# File 'lib/terminal_hero/modules/display_controller.rb', line 138 def self.setup_map_view(player) screen = Viewport.new header = Header.new map_display = Content.new GameData::MAP_HEADER.call(player).each { |line| header.lines.push(line) } return [screen, header, map_display] end |