rubywarrior-i18n 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. data/CHANGELOG.rdoc +38 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +44 -0
  4. data/LICENSE +20 -0
  5. data/README.md +199 -0
  6. data/Rakefile +17 -0
  7. data/bin/rubywarrior +5 -0
  8. data/features/command_options.feature +46 -0
  9. data/features/levels.feature +71 -0
  10. data/features/profiles.feature +56 -0
  11. data/features/step_definitions/common_steps.rb +19 -0
  12. data/features/step_definitions/interaction_steps.rb +65 -0
  13. data/features/support/env.rb +12 -0
  14. data/features/support/mockio.rb +57 -0
  15. data/features/towers.feature +14 -0
  16. data/i18n/en.yml +232 -0
  17. data/i18n/es.yml +233 -0
  18. data/lib/ruby_warrior.rb +53 -0
  19. data/lib/ruby_warrior/abilities/attack.rb +25 -0
  20. data/lib/ruby_warrior/abilities/base.rb +44 -0
  21. data/lib/ruby_warrior/abilities/bind.rb +20 -0
  22. data/lib/ruby_warrior/abilities/detonate.rb +34 -0
  23. data/lib/ruby_warrior/abilities/direction_of.rb +13 -0
  24. data/lib/ruby_warrior/abilities/direction_of_stairs.rb +13 -0
  25. data/lib/ruby_warrior/abilities/distance_of.rb +13 -0
  26. data/lib/ruby_warrior/abilities/explode.rb +28 -0
  27. data/lib/ruby_warrior/abilities/feel.rb +14 -0
  28. data/lib/ruby_warrior/abilities/form.rb +25 -0
  29. data/lib/ruby_warrior/abilities/health.rb +13 -0
  30. data/lib/ruby_warrior/abilities/listen.rb +15 -0
  31. data/lib/ruby_warrior/abilities/look.rb +16 -0
  32. data/lib/ruby_warrior/abilities/pivot.rb +17 -0
  33. data/lib/ruby_warrior/abilities/rescue.rb +24 -0
  34. data/lib/ruby_warrior/abilities/rest.rb +20 -0
  35. data/lib/ruby_warrior/abilities/shoot.rb +24 -0
  36. data/lib/ruby_warrior/abilities/walk.rb +21 -0
  37. data/lib/ruby_warrior/config.rb +22 -0
  38. data/lib/ruby_warrior/core_additions.rb +29 -0
  39. data/lib/ruby_warrior/floor.rb +71 -0
  40. data/lib/ruby_warrior/game.rb +209 -0
  41. data/lib/ruby_warrior/level.rb +123 -0
  42. data/lib/ruby_warrior/level_loader.rb +56 -0
  43. data/lib/ruby_warrior/player_generator.rb +41 -0
  44. data/lib/ruby_warrior/position.rb +82 -0
  45. data/lib/ruby_warrior/profile.rb +120 -0
  46. data/lib/ruby_warrior/runner.rb +34 -0
  47. data/lib/ruby_warrior/space.rb +71 -0
  48. data/lib/ruby_warrior/tower.rb +14 -0
  49. data/lib/ruby_warrior/turn.rb +38 -0
  50. data/lib/ruby_warrior/ui.rb +54 -0
  51. data/lib/ruby_warrior/units/archer.rb +34 -0
  52. data/lib/ruby_warrior/units/base.rb +100 -0
  53. data/lib/ruby_warrior/units/captive.rb +17 -0
  54. data/lib/ruby_warrior/units/golem.rb +20 -0
  55. data/lib/ruby_warrior/units/sludge.rb +30 -0
  56. data/lib/ruby_warrior/units/thick_sludge.rb +13 -0
  57. data/lib/ruby_warrior/units/warrior.rb +73 -0
  58. data/lib/ruby_warrior/units/wizard.rb +34 -0
  59. data/lib/ruby_warrior/version.rb +3 -0
  60. data/spec/fixtures/short-tower/level_001.rb +15 -0
  61. data/spec/fixtures/short-tower/level_002.rb +15 -0
  62. data/spec/fixtures/walking_player.rb +5 -0
  63. data/spec/ruby_warrior/abilities/attack_spec.rb +51 -0
  64. data/spec/ruby_warrior/abilities/base_spec.rb +38 -0
  65. data/spec/ruby_warrior/abilities/bind_spec.rb +19 -0
  66. data/spec/ruby_warrior/abilities/direction_of_spec.rb +13 -0
  67. data/spec/ruby_warrior/abilities/direction_of_stairs_spec.rb +13 -0
  68. data/spec/ruby_warrior/abilities/distance_of_spec.rb +13 -0
  69. data/spec/ruby_warrior/abilities/explode_spec.rb +32 -0
  70. data/spec/ruby_warrior/abilities/feel_spec.rb +13 -0
  71. data/spec/ruby_warrior/abilities/form_spec.rb +48 -0
  72. data/spec/ruby_warrior/abilities/health_spec.rb +13 -0
  73. data/spec/ruby_warrior/abilities/listen_spec.rb +17 -0
  74. data/spec/ruby_warrior/abilities/look_spec.rb +15 -0
  75. data/spec/ruby_warrior/abilities/pivot_spec.rb +18 -0
  76. data/spec/ruby_warrior/abilities/rescue_spec.rb +40 -0
  77. data/spec/ruby_warrior/abilities/rest_spec.rb +29 -0
  78. data/spec/ruby_warrior/abilities/shoot_spec.rb +22 -0
  79. data/spec/ruby_warrior/abilities/throw_spec.rb +46 -0
  80. data/spec/ruby_warrior/abilities/walk_spec.rb +25 -0
  81. data/spec/ruby_warrior/core_additions_spec.rb +7 -0
  82. data/spec/ruby_warrior/floor_spec.rb +81 -0
  83. data/spec/ruby_warrior/game_spec.rb +119 -0
  84. data/spec/ruby_warrior/level_loader_spec.rb +54 -0
  85. data/spec/ruby_warrior/level_spec.rb +203 -0
  86. data/spec/ruby_warrior/player_generator_spec.rb +12 -0
  87. data/spec/ruby_warrior/position_spec.rb +108 -0
  88. data/spec/ruby_warrior/profile_spec.rb +171 -0
  89. data/spec/ruby_warrior/space_spec.rb +190 -0
  90. data/spec/ruby_warrior/tower_spec.rb +15 -0
  91. data/spec/ruby_warrior/turn_spec.rb +42 -0
  92. data/spec/ruby_warrior/ui_spec.rb +93 -0
  93. data/spec/ruby_warrior/units/archer_spec.rb +23 -0
  94. data/spec/ruby_warrior/units/base_spec.rb +133 -0
  95. data/spec/ruby_warrior/units/captive_spec.rb +23 -0
  96. data/spec/ruby_warrior/units/golem_spec.rb +28 -0
  97. data/spec/ruby_warrior/units/sludge_spec.rb +27 -0
  98. data/spec/ruby_warrior/units/thick_sludge_spec.rb +19 -0
  99. data/spec/ruby_warrior/units/warrior_spec.rb +65 -0
  100. data/spec/ruby_warrior/units/wizard_spec.rb +23 -0
  101. data/spec/spec_helper.rb +10 -0
  102. data/templates/README.erb +33 -0
  103. data/templates/player.rb +5 -0
  104. data/towers/beginner/level_001.rb +18 -0
  105. data/towers/beginner/level_002.rb +19 -0
  106. data/towers/beginner/level_003.rb +23 -0
  107. data/towers/beginner/level_004.rb +20 -0
  108. data/towers/beginner/level_005.rb +24 -0
  109. data/towers/beginner/level_006.rb +21 -0
  110. data/towers/beginner/level_007.rb +20 -0
  111. data/towers/beginner/level_008.rb +23 -0
  112. data/towers/beginner/level_009.rb +22 -0
  113. data/towers/intermediate/level_001.rb +20 -0
  114. data/towers/intermediate/level_002.rb +22 -0
  115. data/towers/intermediate/level_003.rb +25 -0
  116. data/towers/intermediate/level_004.rb +26 -0
  117. data/towers/intermediate/level_005.rb +21 -0
  118. data/towers/intermediate/level_006.rb +25 -0
  119. data/towers/intermediate/level_007.rb +27 -0
  120. data/towers/intermediate/level_008.rb +25 -0
  121. data/towers/intermediate/level_009.rb +33 -0
  122. metadata +226 -0
@@ -0,0 +1,28 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Explode < Base
4
+ attr_accessor :time
5
+
6
+ def description
7
+ "#{R18n.t.explode.description}"
8
+ end
9
+
10
+ def perform
11
+ if @unit.position
12
+ @unit.say "#{R18n.t.explode.collapsing_the_ceiling}"
13
+ @unit.position.floor.units.map do |unit|
14
+ unit.take_damage(100)
15
+ end
16
+ end
17
+ end
18
+
19
+ def pass_turn
20
+ if @time && @unit.position
21
+ @unit.say "#{R18n.t.explode.is_ticking}"
22
+ @time -= 1
23
+ perform if @time.zero?
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Feel < Base
4
+ def description
5
+ "#{R18n.t.feel.description}"
6
+ end
7
+
8
+ def perform(direction = :forward)
9
+ verify_direction(direction)
10
+ space(direction)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Form < Base
4
+ def description
5
+ "#{R18n.t.form.description}"
6
+ end
7
+
8
+ def perform(direction = :forward, &block)
9
+ verify_direction(direction)
10
+ if space(direction).empty?
11
+ x, y = @unit.position.translate_offset(*offset(direction))
12
+ health = (@unit.health/2.0).floor
13
+ golem = @unit.base_golem
14
+ golem.max_health = health
15
+ golem.turn = block
16
+ @unit.health -= health
17
+ @unit.position.floor.add(golem, x, y, @unit.position.direction)
18
+ @unit.say "#{R18n.t.form.golem_gives_health(R18n.t.direction[direction], health)}"
19
+ else
20
+ @unit.say "#{R18n.t.form.fails}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Health < Base
4
+ def description
5
+ "#{R18n.t.health.description}"
6
+ end
7
+
8
+ def perform
9
+ @unit.health
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Listen < Base
4
+ def description
5
+ "#{R18n.t.listen.description}"
6
+ end
7
+
8
+ def perform
9
+ @unit.position.floor.units.map do |unit|
10
+ unit.position.space unless unit == @unit
11
+ end.compact
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Look < Base
4
+ def description
5
+ "#{R18n.t.look.description}"
6
+ end
7
+
8
+ def perform(direction = :forward)
9
+ verify_direction(direction)
10
+ [1, 2, 3].map do |amount|
11
+ space(direction, amount)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Pivot < Base
4
+ ROTATION_DIRECTIONS = [:forward, :right, :backward, :left]
5
+
6
+ def description
7
+ "#{R18n.t.rotate.description}"
8
+ end
9
+
10
+ def perform(direction = :backward)
11
+ verify_direction(direction)
12
+ @unit.position.rotate(ROTATION_DIRECTIONS.index(direction))
13
+ @unit.say "#{R18n.t.rotate.pivots} #{R18n.t.direction[direction]}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Rescue < Base
4
+ def description
5
+ "#{R18n.t.rescue.description}"
6
+ end
7
+
8
+ def perform(direction = :forward)
9
+ verify_direction(direction)
10
+ if space(direction).captive?
11
+ recipient = unit(direction)
12
+ @unit.say R18n.t.rescue.unbinds_and_rescues(R18n.t.direction[direction], "#{recipient}")
13
+ recipient.unbind
14
+ if recipient.kind_of? Units::Captive
15
+ recipient.position = nil
16
+ @unit.earn_points(20)
17
+ end
18
+ else
19
+ @unit.say "#{R18n.t.rescue.unbinds_and_rescues_nothing(direction)}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Rest < Base
4
+ def description
5
+ "#{R18n.t.rest.description}"
6
+ end
7
+
8
+ def perform
9
+ if @unit.health < @unit.max_health
10
+ amount = (@unit.max_health*0.1).round
11
+ amount = @unit.max_health-@unit.health if (@unit.health + amount) > @unit.max_health
12
+ @unit.health += amount
13
+ @unit.say "#{R18n.t.rest.receives_health(amount, @unit.health)}"
14
+ else
15
+ @unit.say "#{R18n.t.rest.fit_fiddle}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Shoot < Base
4
+ def description
5
+ "#{R18n.t.shoot.description}"
6
+ end
7
+
8
+ def perform(direction = :forward)
9
+ verify_direction(direction)
10
+ receiver = multi_unit(direction, 1..3).compact.first
11
+ if receiver
12
+ @unit.say "#{R18n.t.shoot.and_hits(R18n.t.direction[direction], receiver)}"
13
+ damage(receiver, @unit.shoot_power)
14
+ else
15
+ @unit.say "#{R18n.t.shoot.and_hits_nothing}"
16
+ end
17
+ end
18
+
19
+ def multi_unit(direction, range)
20
+ range.map { |n| unit(direction, n) }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Walk < Base
4
+ def description
5
+ "#{R18n.t.direction.move_forward_default}"
6
+ end
7
+
8
+ def perform(direction = :forward)
9
+ verify_direction(direction)
10
+ if @unit.position
11
+ @unit.say "#{R18n.t.walk.s} #{R18n.t.direction[direction]}"
12
+ if space(direction).empty?
13
+ @unit.position.move(*offset(direction))
14
+ else
15
+ @unit.say "#{R18n.t.direction.bumps_into} #{space(direction)}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module RubyWarrior
2
+ class Config
3
+ class << self
4
+ attr_accessor :delay, :in_stream, :out_stream, :practice_level
5
+ attr_writer :path_prefix, :skip_input
6
+
7
+ def path_prefix
8
+ @path_prefix || "."
9
+ end
10
+
11
+ def skip_input?
12
+ @skip_input
13
+ end
14
+
15
+ def reset
16
+ [:@path_prefix, :@skip_input, :@delay, :@in_stream, :@out_stream, :@practice_level].each do |i|
17
+ remove_instance_variable(i) if instance_variable_defined?(i)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ class String
2
+ def camelize
3
+ gsub(/(?:^|_)(.)/) { $1.upcase }
4
+ end
5
+
6
+ def constantize
7
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
8
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
9
+ end
10
+
11
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
12
+ end
13
+
14
+ def underscore
15
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
16
+ end
17
+
18
+ def humanize
19
+ gsub(/_/, " ").capitalize
20
+ end
21
+
22
+ def titleize
23
+ underscore.humanize.gsub(/\b('?[a-z])/) { $1.capitalize }
24
+ end
25
+
26
+ def hard_wrap(width = 80)
27
+ gsub(/(.{1,#{width}})(\s+|$)/, "\\1\n").strip
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ module RubyWarrior
2
+ class Floor
3
+ attr_accessor :width, :height, :grid
4
+ attr_reader :stairs_location
5
+
6
+ def initialize
7
+ @width = 0
8
+ @height = 0
9
+ @units = []
10
+ @stairs_location = [-1, -1]
11
+ end
12
+
13
+ def add(unit, x, y, direction = nil)
14
+ @units << unit
15
+ unit.position = Position.new(self, x, y, direction)
16
+ end
17
+
18
+ def place_stairs(x, y)
19
+ @stairs_location = [x, y]
20
+ end
21
+
22
+ def stairs_space
23
+ space(*@stairs_location)
24
+ end
25
+
26
+ def units
27
+ @units.reject { |u| u.position.nil? }
28
+ end
29
+
30
+ def other_units
31
+ units.reject { |u| u.kind_of? Units::Warrior }
32
+ end
33
+
34
+ def get(x, y)
35
+ units.detect do |unit|
36
+ unit.position.at?(x, y)
37
+ end
38
+ end
39
+
40
+ def space(x, y)
41
+ Space.new(self, x, y)
42
+ end
43
+
44
+ def out_of_bounds?(x, y)
45
+ x < 0 || y < 0 || x > @width-1 || y > @height-1
46
+ end
47
+
48
+ def character
49
+ rows = []
50
+ rows << " " + ("-" * @width)
51
+ @height.times do |y|
52
+ row = "|"
53
+ @width.times do |x|
54
+ row << space(x, y).character
55
+ end
56
+ row << "|"
57
+ rows << row
58
+ end
59
+ rows << " " + ("-" * @width)
60
+ rows.join("\n") + "\n"
61
+ end
62
+
63
+ def unique_units
64
+ unique_units = []
65
+ units.each do |unit|
66
+ unique_units << unit unless unique_units.map { |u| u.class }.include?(unit.class)
67
+ end
68
+ unique_units
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,209 @@
1
+ module RubyWarrior
2
+
3
+ class Game
4
+
5
+ def start
6
+ UI.puts R18n.t.welcome
7
+
8
+ if File.exist?(Config.path_prefix + '/.profile')
9
+ @profile = Profile.load(Config.path_prefix + '/.profile')
10
+ else
11
+ if File.exist?(Config.path_prefix + "/ruby-warrior")
12
+ FileUtils.mv(Config.path_prefix + '/ruby-warrior', Config.path_prefix + '/rubywarrior')
13
+ end
14
+ make_game_directory unless File.exist?(Config.path_prefix + '/rubywarrior')
15
+ end
16
+
17
+ if profile.epic?
18
+ if profile.level_after_epic?
19
+ go_back_to_normal_mode
20
+ else
21
+ play_epic_mode
22
+ end
23
+ else
24
+ play_normal_mode
25
+ end
26
+ end
27
+
28
+ def make_game_directory
29
+ if UI.ask(R18n.t.directory.not_found)
30
+ Dir.mkdir(Config.path_prefix + '/rubywarrior')
31
+ else
32
+ UI.puts R18n.t.directory.cannot_continue
33
+ exit
34
+ end
35
+ end
36
+
37
+ def play_epic_mode
38
+ Config.delay /= 2 if Config.delay # speed up UI since we're going to be doing a lot here
39
+ profile.current_epic_score = 0
40
+ profile.current_epic_grades = {}
41
+ if Config.practice_level
42
+ @current_level = @next_level = nil
43
+ profile.level_number = Config.practice_level
44
+ play_current_level
45
+ else
46
+ playing = true
47
+ while playing
48
+ @current_level = @next_level = nil
49
+ profile.level_number += 1
50
+ playing = play_current_level
51
+ end
52
+ profile.save # saves the score for epic mode
53
+ end
54
+ end
55
+
56
+ def play_normal_mode
57
+ if Config.practice_level
58
+ UI.puts R18n.t.practice_level.unable_not_in_epic_mode
59
+ else
60
+ if current_level.number.zero?
61
+ prepare_next_level
62
+ UI.puts R18n.t.first_level_generated(profile.directory_name)
63
+ else
64
+ play_current_level
65
+ end
66
+ end
67
+ end
68
+
69
+ def play_current_level
70
+ continue = true
71
+ current_level.load_player
72
+ UI.puts R18n.t.level.starting(current_level.number)
73
+ current_level.play
74
+ if current_level.passed?
75
+ if next_level.exists?
76
+ UI.puts R18n.t.stairs.found
77
+ else
78
+ UI.puts R18n.t.end_of_game
79
+ continue = false
80
+ end
81
+ current_level.tally_points
82
+ if profile.epic?
83
+ UI.puts final_report if final_report && !continue
84
+ else
85
+ request_next_level
86
+ end
87
+ else
88
+ continue = false
89
+ UI.puts R18n.t.level.failed(current_level.number)
90
+ if !Config.skip_input? && current_level.clue && UI.ask(R18n.t.level.clues)
91
+ UI.puts current_level.clue.hard_wrap
92
+ end
93
+ end
94
+ continue
95
+ end
96
+
97
+ def request_next_level
98
+ if !Config.skip_input? && (next_level.exists? ? UI.ask(R18n.t.level.continue_next) : UI.ask(R18n.t.level.continue_epic_mode))
99
+ if next_level.exists?
100
+ prepare_next_level
101
+ UI.puts R18n.t.readme.see_updated(profile.directory_name)
102
+ else
103
+ prepare_epic_mode
104
+ UI.puts R18n.t.to_play_epic_mode
105
+ end
106
+ else
107
+ UI.puts R18n.t.level.staying_on_current
108
+ end
109
+ end
110
+
111
+ def prepare_next_level
112
+ next_level.generate_player_files
113
+ profile.level_number += 1
114
+ profile.save # this saves score and new abilities too
115
+ end
116
+
117
+ def prepare_epic_mode
118
+ profile.enable_epic_mode
119
+ profile.level_number = 0
120
+ profile.save # this saves score too
121
+ end
122
+
123
+ def go_back_to_normal_mode
124
+ profile.enable_normal_mode
125
+ prepare_next_level
126
+ UI.puts R18n.t.mode.back_to_normal
127
+ UI.puts R18n.t.readme.see_updated(profile.directory_name)
128
+ end
129
+
130
+
131
+ # profiles
132
+
133
+ def profiles
134
+ profile_paths.map { |profile| Profile.load(profile) }
135
+ end
136
+
137
+ def profile_paths
138
+ Dir[Config.path_prefix + '/rubywarrior/**/.profile']
139
+ end
140
+
141
+ def profile
142
+ @profile ||= choose_profile
143
+ end
144
+
145
+ def new_profile
146
+ profile = Profile.new
147
+ profile.tower_path = UI.choose(R18n.t.game.tower, towers).path
148
+ profile.warrior_name = UI.request(R18n.t.warrior.enter_name)
149
+ profile
150
+ end
151
+
152
+
153
+ # towers
154
+
155
+ def towers
156
+ tower_paths.map { |path| Tower.new(path) }
157
+ end
158
+
159
+ def tower_paths
160
+ Dir[File.expand_path('../../../towers/*', __FILE__)]
161
+ end
162
+
163
+
164
+ # levels
165
+
166
+ def current_level
167
+ @current_level ||= profile.current_level
168
+ end
169
+
170
+ def next_level
171
+ @next_level ||= profile.next_level
172
+ end
173
+
174
+ def final_report
175
+ if profile.calculate_average_grade && !Config.practice_level
176
+ report = ""
177
+ report << R18n.t.game.average_grade_for_tower(Level.grade_letter(profile.calculate_average_grade))
178
+ profile.current_epic_grades.keys.sort.each do |level|
179
+ report << " #{R18n.t.level.number(level)} #{Level.grade_letter(profile.current_epic_grades[level])}\n"
180
+ end
181
+ report << R18n.t.level.to_practice
182
+ report
183
+ end
184
+ end
185
+
186
+ private
187
+
188
+ def choose_profile # REFACTORME
189
+ profile = UI.choose(R18n.t.profile.profile, profiles + [[:new, R18n.t.profile.new]])
190
+ if profile == :new
191
+ profile = new_profile
192
+ if profiles.any? { |p| p.player_path == profile.player_path }
193
+ if UI.ask(R18n.t.profile.replace_existing_for_tower)
194
+ UI.puts R18n.t.profile.replace_existing
195
+ profile
196
+ else
197
+ UI.puts R18n.t.profile.not_replacing
198
+ exit
199
+ end
200
+ else
201
+ profile
202
+ end
203
+ else
204
+ profile
205
+ end
206
+ end
207
+
208
+ end
209
+ end