empi 0.24 → 0.25

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63caec222a8c3c293532aabadbebc30a9acb2f8a664b4327ba95dae6e5222955
4
- data.tar.gz: '09b1c7b2f163e042ee4e9ecb17547b585c5d8b80480c099f8a253a2c917449e2'
3
+ metadata.gz: abe9081612cf59f547e94e98d5ee4373fab6ded9298bfb20366bc3d67725c0cc
4
+ data.tar.gz: 7f7683c7a94d512965feed1617fc3cb700851e5444862a28069b8c2ddb76cecc
5
5
  SHA512:
6
- metadata.gz: 1662dd058a2e1d8d5e4a8f4cdf48cb7f3b8a45bcc2500c11dd458af7c0d0aff5c9718ce79d5378f47ef6d9d0b9650a1a0a8604b791368371515f68390264e02e
7
- data.tar.gz: abcac90abcfa4d0c72561ddac4c8a074e558d906f82530a7ac7d554f8784d769998b778ed8e5cb3f71c2217f807d6ddcf505217f589a1bfdf2f887c1b3577a0b
6
+ metadata.gz: feac436c82e490b978bac742c546816879fa7c32ef3e14410023078d83b1e8d5366d3ffa7ef051442cf84d5cef728f8c7ee2f2577ddce84c7f3ef16c2661def1
7
+ data.tar.gz: 73ade95b93a6b84b509ecb88ea545aaa80babbd007586bc4d5db52dee03a79eec15c9fd18f7a4422db265398921e1ab33701bd2c2230f9a7592fdc64cae64277
data/lib/empi.rb CHANGED
@@ -19,7 +19,11 @@ class GameWindow < Gosu::Window
19
19
  height = (MAPY + 2) * TILESIZE, \
20
20
  fullscreen = false)
21
21
  super
22
- self.caption = 'Empi: Ruby Edition 0.24'
22
+
23
+ # Set version name
24
+ version = '0.25'
25
+ self.caption = "Empi: Ruby Edition #{version}"
26
+ WelcomeState.instance.version = version
23
27
 
24
28
  # Activate first state
25
29
  $window = self
@@ -46,7 +50,6 @@ class GameWindow < Gosu::Window
46
50
 
47
51
  # Draw according to current state
48
52
  def draw
49
-
50
53
  @button = BUTTON_PROCESSED # draw just once after each button release
51
54
  unless $window.state.nil?
52
55
  @state.draw
@@ -6,7 +6,7 @@ require_relative './play_state'
6
6
  require_relative './../units/army'
7
7
  require_relative './../units/ship'
8
8
 
9
- # Game state of closing the game window
9
+ # Game state of selection of the build project
10
10
  class BuildState < GameState
11
11
  include Singleton
12
12
 
@@ -20,7 +20,7 @@ class BuildState < GameState
20
20
  #def before_end
21
21
  #end
22
22
 
23
- # Process given button to cursor
23
+ # Process given button
24
24
  def update(button)
25
25
  newly_selected = nil
26
26
 
@@ -2,12 +2,12 @@ require 'singleton'
2
2
 
3
3
  require_relative './game_state'
4
4
  require_relative './welcome_state'
5
+ require_relative './win_state'
5
6
 
6
7
  require_relative './../user_interface/cursor'
7
8
  require_relative './../user_interface/infopane'
8
9
  require_relative './../user_interface/map'
9
10
 
10
-
11
11
  XTEXT = 5
12
12
  YTEXT = 5
13
13
  ZTILE = 0
@@ -32,6 +32,7 @@ class PlayState < GameState
32
32
  # Load new scenario or resume game
33
33
  def after_start
34
34
  if @desired_new_map
35
+ WinState.instance.unset_victory!
35
36
  load_new_map!
36
37
  help # show help after each loading of a new map
37
38
  @desired_new_map = nil
@@ -65,7 +66,11 @@ class PlayState < GameState
65
66
  def update(button)
66
67
  case(button)
67
68
  when Gosu::KbEscape then
68
- GameState.switch!(WelcomeState.instance)
69
+ unless WinState.instance.faction
70
+ GameState.switch!(WelcomeState.instance)
71
+ else
72
+ GameState.switch!(WinState.instance)
73
+ end
69
74
  when Gosu::KbPeriod then
70
75
  next_faction_turn!
71
76
  when Gosu::KbH then
@@ -120,6 +125,14 @@ class PlayState < GameState
120
125
  }
121
126
  all_faction_units.each { |uu| uu.reset_moves!}
122
127
 
128
+ # Win check: current faction has lost all units
129
+ if !WinState.instance.faction and all_faction_units.empty?
130
+ WinState.instance.set_victory!(
131
+ 3 - @infopane.faction, # TODO more factions?
132
+ 'Annihilation', @infopane.turn, @infopane.score
133
+ )
134
+ end
135
+
123
136
  # Lock the cursor to the first waiting unit
124
137
  @cursor.reset!
125
138
  end
@@ -127,9 +140,10 @@ class PlayState < GameState
127
140
  # Printout the controls
128
141
  def help
129
142
  puts "-----------\n" \
130
- "h: help, Esc: menu, Enter: info, j: switch freeroam\n" \
131
- "QWEADZXC or arrow keys: movement, .: end your turn\n" \
132
- "functions: s sentry, b build, n none\n" \
143
+ "H: help, Esc: menu, Enter: info, " \
144
+ ".: end your turn, J: switch freeroam\n" \
145
+ "QWEADZXC or arrow keys: movement, K: previous unit, L: next unit\n" \
146
+ "functions: S sentry, B build, N none\n" \
133
147
  "-----------\n"
134
148
  end
135
149
  end
@@ -15,7 +15,7 @@ class QuitState < GameState
15
15
  #def before_end
16
16
  #end
17
17
 
18
- # Process given button to cursor
18
+ # Process given button
19
19
  def update(button)
20
20
  case(button)
21
21
  when Gosu::KbY then
@@ -0,0 +1,75 @@
1
+ require 'singleton'
2
+
3
+ require_relative './game_state'
4
+ require_relative './welcome_state'
5
+
6
+ # Game state of settings screen
7
+ class SetState < GameState
8
+ include Singleton
9
+
10
+ # TODO move out?
11
+ attr_reader :settings
12
+
13
+ # What to do just after state gets activated
14
+ #def after_start
15
+ #end
16
+
17
+ # What to do just before state gets deactivated
18
+ #def before_end
19
+ #end
20
+
21
+ def initialize
22
+ super
23
+ @settings = { # TODO move out?
24
+ 'axis_top' => true,
25
+ 'axis_bottom' => false,
26
+ 'axis_left' => true,
27
+ 'axis_right' => false
28
+ }
29
+ end
30
+
31
+ # Process given button
32
+ def update(button)
33
+ case(button)
34
+ when Gosu::Kb1, Gosu::Kb2, Gosu::Kb3, Gosu::Kb4 then
35
+ change_setting!(button)
36
+ when Gosu::KbEscape then
37
+ GameState.switch!(WelcomeState.instance)
38
+ end
39
+ end
40
+
41
+ def draw
42
+ header_text = '
43
+ /-- /// /-/ -/-
44
+ /-- ||| /-/ |
45
+ /-- ||| | -/-'
46
+ options_text = "
47
+ 1 – show top axis: #{@settings['axis_top']}
48
+ 2 – show bottom axis: #{@settings['axis_bottom']}
49
+ 3 – show left axis: #{@settings['axis_left']}
50
+ 4 – show right axis: #{@settings['axis_right']}\n
51
+ Esc – return to menu"
52
+ warning_text = '
53
+ Warning: settings are not saved
54
+ when Empi is closed'
55
+
56
+ menu = Gosu::Image.from_text(
57
+ header_text + "\n\n\n\n\n" + options_text + "\n\n\n" + warning_text, 20)
58
+
59
+ menu.draw((3*TILESIZE) + XTEXT, (2*TILESIZE) + YTEXT, ZTEXT)
60
+ end
61
+
62
+ def change_setting!(button)
63
+ changed_setting = {
64
+ Gosu::Kb1 => 'axis_top',
65
+ Gosu::Kb2 => 'axis_bottom',
66
+ Gosu::Kb3 => 'axis_left',
67
+ Gosu::Kb4 => 'axis_right'
68
+ }[button]
69
+
70
+ old_value = @settings[changed_setting]
71
+ @settings[changed_setting] = !@settings[changed_setting] # TODO other than boolean types?
72
+ puts "setting #{changed_setting} changed from #{old_value} " \
73
+ "to #{@settings[changed_setting]}"
74
+ end
75
+ end
@@ -3,11 +3,15 @@ require 'singleton'
3
3
  require_relative './game_state'
4
4
  require_relative './play_state'
5
5
  require_relative './quit_state'
6
+ require_relative './set_state'
7
+ require_relative './win_state'
6
8
 
7
- # Game state of closing the game window
9
+ # Game state of main menu
8
10
  class WelcomeState < GameState
9
11
  include Singleton
10
12
 
13
+ attr_accessor :version
14
+
11
15
  # What to do just after state gets activated
12
16
  #def after_start
13
17
  #end
@@ -16,14 +20,17 @@ class WelcomeState < GameState
16
20
  #def before_end
17
21
  #end
18
22
 
19
- # Process given button to cursor
23
+ # Process given button
20
24
  def update(button)
21
25
  case(button)
22
- when Gosu::Kb0, Gosu::KbQ then
23
- GameState.switch!(QuitState.instance)
24
26
  when Gosu::KbEscape then
27
+ # If there is active map go either to it or to its win screen
25
28
  if PlayState.instance.map
26
- GameState.switch!(PlayState.instance)
29
+ unless WinState.instance.faction
30
+ GameState.switch!(PlayState.instance)
31
+ else
32
+ GameState.switch!(WinState.instance)
33
+ end
27
34
  end
28
35
  when Gosu::Kb1, Gosu::Kb2, Gosu::Kb3 then
29
36
  PlayState.instance.desired_new_map = {
@@ -32,6 +39,10 @@ class WelcomeState < GameState
32
39
  Gosu::Kb3 => 'm03'
33
40
  }[button]
34
41
  GameState.switch!(PlayState.instance)
42
+ when Gosu::Kb9, Gosu::KbS then
43
+ GameState.switch!(SetState.instance)
44
+ when Gosu::Kb0, Gosu::KbQ then
45
+ GameState.switch!(QuitState.instance)
35
46
  end
36
47
  end
37
48
 
@@ -41,24 +52,34 @@ class WelcomeState < GameState
41
52
  /-- ||| /-/ |
42
53
  /-- ||| | -/-'
43
54
 
44
- if PlayState.instance.map # some map is loaded
45
- space_text = "\n\n\n"
46
- options_text = "
47
- Esc Resume"
55
+ # Is there active map? Is it won?
56
+ if PlayState.instance.map
57
+ unless WinState.instance.faction
58
+ resume_text = "
59
+ Esc – resume"
60
+ else
61
+ resume_text = "
62
+ Esc – return to victory screen"
63
+ end
48
64
  else
49
- space_text = "\n\n\n\n"
50
- options_text = ""
65
+ resume_text = "\n"
51
66
  end
52
67
 
53
- options_text += "
54
- 1 – Start New: Map 01
55
- 2 – Start New: Map 02
56
- 3 – Start New: Map 03\n
57
- 0, QQuit"
68
+ options_text = "
69
+ 1 – start new: Map 01
70
+ 2 – start new: Map 02
71
+ 3 – start new: Map 03\n
72
+ 9, Ssettings
73
+ 0, Q – quit"
58
74
 
59
- menu = Gosu::Image.from_text(
60
- header_text + space_text + options_text, 20)
75
+ version_text = "
76
+ version #{self.version}"
61
77
 
78
+ menu = Gosu::Image.from_text(
79
+ header_text + "\n\n\n" +
80
+ resume_text + "\n" +
81
+ options_text + "\n\n\n\n\n\n\n\n" +
82
+ version_text, 20)
62
83
  menu.draw((3*TILESIZE) + XTEXT, (2*TILESIZE) + YTEXT, ZTEXT)
63
84
  end
64
85
  end
@@ -0,0 +1,61 @@
1
+ require 'singleton'
2
+
3
+ require_relative './game_state'
4
+ require_relative './play_state'
5
+ require_relative './welcome_state'
6
+
7
+ # Game state of summary of won game
8
+ class WinState < GameState
9
+ include Singleton
10
+
11
+ attr_reader :faction
12
+
13
+ # What to do just after state gets activated
14
+ #def after_start
15
+ #end
16
+
17
+ # What to do just before state gets deactivated
18
+ #def before_end
19
+ #end
20
+
21
+ # Process given button
22
+ def update(button)
23
+ case(button)
24
+ when Gosu::KbEscape then
25
+ GameState.switch!(WelcomeState.instance)
26
+ when Gosu::KbReturn then
27
+ GameState.switch!(PlayState.instance)
28
+ end
29
+ end
30
+
31
+ def draw
32
+ winner_text = "VICTORY FOR FACTION #{@faction}"
33
+ summary_text = "Summary:
34
+ victory type: #{@type}
35
+ won on turn: #{@turn}
36
+ ending score: #{@score}"
37
+ options_text = 'Do you want to continue playing?
38
+ Enter – resume
39
+ Esc – return to menu'
40
+
41
+ win_summary = Gosu::Image.from_text(
42
+ winner_text + "\n\n\n" + summary_text + "\n\n" + options_text, 20)
43
+ win_summary.draw((2*TILESIZE) + XTEXT, (2*TILESIZE) + YTEXT, ZTEXT)
44
+ end
45
+
46
+ def set_victory!(faction, type, turn, score)
47
+ @faction = faction
48
+ @type = type
49
+ @turn = turn
50
+ @score = score.clone # separate object is neeeded
51
+
52
+ GameState.switch!(WinState.instance)
53
+ end
54
+
55
+ def unset_victory!()
56
+ @faction = nil
57
+ @type = nil
58
+ @turn = nil
59
+ @score = nil
60
+ end
61
+ end
@@ -76,7 +76,7 @@ class Town < Unit
76
76
  # Set desired project
77
77
  def set_project!(desired_project)
78
78
  unless price_list.key?(desired_project)
79
- abort("town.set_project!(): Unknown project (#{desired_project})")
79
+ abort("Town.set_project!(): Unknown project (#{desired_project})")
80
80
  end
81
81
  @parts_needed = price_list[desired_project]
82
82
 
@@ -1,4 +1,4 @@
1
- require_relative './unitFunction'
1
+ require_relative './unit_function'
2
2
 
3
3
  # Both capturable and movable game pieces
4
4
  class Unit
@@ -35,8 +35,8 @@ class Unit
35
35
 
36
36
  # Process engagement targeted at this unit
37
37
  def engage!(by_whom)
38
- if by_whom == @faction
39
- abort("unit.engage!(): Engaging unit is of the same faction as this one (#{@faction})")
38
+ if by_whom.faction == @faction
39
+ abort("Unit.engage!(): Engaging unit is of the same faction as this one (#{@faction})")
40
40
  end
41
41
 
42
42
  # Can it be captured and is it without defenders?
@@ -46,7 +46,7 @@ class Unit
46
46
  else # then capture it if you can
47
47
  if by_whom.can_capture?
48
48
  puts PROMPT + by_whom.to_s + ' is capturing ' + to_s
49
- capture!(by_whom.faction)
49
+ capture!(by_whom)
50
50
  else
51
51
  puts PROMPT + by_whom.to_s + ' can\'t capture other units'
52
52
  end
@@ -65,16 +65,16 @@ class Unit
65
65
  # Process capture targeted at this unit
66
66
  def capture!(by_whom)
67
67
  unless can_be_captured?
68
- abort("unit.capture!(): This unit can\'t be captured")
68
+ abort("Unit.capture!(): This unit can\'t be captured")
69
69
  end
70
70
 
71
- if by_whom == @faction
72
- abort("unit.capture!(): This unit already belongs to faction #{@faction}")
71
+ if by_whom.faction == @faction
72
+ abort("Unit.capture!(): This unit already belongs to faction #{@faction}")
73
73
  end
74
74
 
75
75
  # Add <value> to the capturing faction
76
- @infopane.add_score(by_whom - 1, self.class.value)
77
- @faction = by_whom
76
+ @infopane.add_score(by_whom.faction - 1, self.class.value)
77
+ @faction = by_whom.faction
78
78
  end
79
79
 
80
80
  # Add <value> to the other faction and remove links to given unit
@@ -98,7 +98,7 @@ class Unit
98
98
  def check_movement(old_x, old_y)
99
99
  if @x != old_x || @y != old_y # only if it moved
100
100
  if @moves_left <= 0
101
- abort("unit.check_movement(): Moving unit does not have enough move points (#{@moves_left} moves)")
101
+ abort("Unit.check_movement(): Moving unit does not have enough move points (#{@moves_left} moves)")
102
102
  end
103
103
 
104
104
  oldcoords_unit = @map.get_unit(old_x, old_y)
@@ -121,17 +121,18 @@ class Unit
121
121
  # Get to new coordinates
122
122
  if !newcoords_unit
123
123
  @map.set_unit(@x, @y, self)
124
- @cargo.each { |uu|
125
- uu.x = @x
126
- uu.y = @y
127
- }
128
-
129
124
  else # if you are going to be transported
130
125
  newcoords_unit.cargo.insert(-1, self) # -1 = to the end
131
126
  puts PROMPT + to_s + ' got loaded into '+ newcoords_unit.to_s
132
127
  @moves_left = 1 unless newcoords_unit.can_build? # use all your left moves unless you are getting loaded into a town
133
128
  end
134
129
 
130
+ # Update position of your cargo
131
+ @cargo.each { |uu|
132
+ uu.x = @x
133
+ uu.y = @y
134
+ }
135
+
135
136
  else # if there already is somebody that can't transport you (enemy or full friend)
136
137
  # Stay on your original tile
137
138
  @x = old_x
@@ -13,7 +13,7 @@ class UnitFunction
13
13
  "no function"
14
14
  when FUNCBUILD
15
15
  unless @unit.project
16
- abort("unitFunction.info(): No project set (" + @unit.to_s + ")")
16
+ abort("UnitFunction.info(): No project set (" + @unit.to_s + ")")
17
17
  end
18
18
  "building #{@unit.project.name} (#{@unit.build_info})"
19
19
  when FUNCSENTRY
@@ -26,7 +26,7 @@ class UnitFunction
26
26
  # Build given unit
27
27
  when FUNCBUILD
28
28
  unless @unit.project
29
- abort("unitFunction.func!(): No project set (" + @unit.to_s + ")")
29
+ abort("UnitFunction.func!(): No project set (" + @unit.to_s + ")")
30
30
  end
31
31
 
32
32
  @unit.parts_built += 1
@@ -39,9 +39,9 @@ class UnitFunction
39
39
  @unit.project.new(@unit.x, @unit.y, @unit.faction, map, infopane)
40
40
  end
41
41
 
42
- # Wake when enemies are nearby
42
+ # Wake when enemies are nearby (checking just map units is enough)
43
43
  when FUNCSENTRY
44
- units_around = map.all_units.select { |uu|
44
+ units_around = map.all_units(0,0).select { |uu|
45
45
  (uu.x - @unit.x).abs <= 1 &&
46
46
  (uu.y - @unit.y).abs <= 1 &&
47
47
  uu.faction != @unit.faction
@@ -55,7 +55,7 @@ class UnitFunction
55
55
  end
56
56
 
57
57
  unless ret
58
- abort("unitFunction.func!(): Functionable unit didn't function (" + @unit.to_s + ")")
58
+ abort("UnitFunction.func!(): Functionable unit didn't function (" + @unit.to_s + ")")
59
59
  end
60
60
 
61
61
  ret
@@ -15,7 +15,7 @@ class Cursor
15
15
 
16
16
  # Give to Infopane link to yourself (for freeroam marker)
17
17
  if !@infopane
18
- abort("Cursor.initialize!(): Infopane is not set")
18
+ abort("Cursor.initialize(): Infopane is not set")
19
19
  end
20
20
  @infopane.cursor = self
21
21
  end
@@ -53,6 +53,10 @@ class Cursor
53
53
  # The rest
54
54
  when Gosu::KbJ, Gosu::KB_NUMPAD_0 then
55
55
  switch_freeroam!
56
+ when Gosu::KbK, Gosu::KB_NUMPAD_MINUS then
57
+ select_other_unit!(false)
58
+ when Gosu::KbL, Gosu::KB_NUMPAD_PLUS then
59
+ select_other_unit!(true)
56
60
  when Gosu::KbReturn, Gosu::KB_NUMPAD_5 then
57
61
  info
58
62
  end
@@ -65,11 +69,11 @@ class Cursor
65
69
  @image.draw(@x * TILESIZE, (@y + 1) * TILESIZE, ZCURSOR)
66
70
  end
67
71
 
68
- # Move to coordinates of unit the cursor is locked to
72
+ # Move to coordinates of map unit the cursor is locked to
69
73
  def warp_to_locked!()
70
- @x = @locked_to.x
71
- @y = @locked_to.y
72
- @local_unit = @locked_to
74
+ @x = @locked_unit.x
75
+ @y = @locked_unit.y
76
+ @selected_unit = @locked_unit
73
77
  end
74
78
 
75
79
  # Move by given change of coordinates
@@ -77,39 +81,39 @@ class Cursor
77
81
  if freeroam
78
82
  @x += xx
79
83
  @y += yy
80
- @local_unit = @map.get_unit(@x, @y)
84
+ @selected_unit = @map.get_unit(@x, @y)
81
85
  # TODO show some basic tile info in infopane
82
86
  else
83
87
  check_unit
84
88
 
85
89
  # Move the unit first
86
- @locked_to.x += xx
87
- @locked_to.y += yy
88
- @locked_to.check_movement(@x, @y) # cursor coordinates work like old_x, old_y
90
+ @locked_unit.x += xx
91
+ @locked_unit.y += yy
92
+ @locked_unit.check_movement(@x, @y) # cursor coordinates work like old_x, old_y
89
93
 
90
94
  # Is the unit still alive?
91
- if @locked_to.armour_left > 0
95
+ if @locked_unit.armour_left > 0
92
96
  warp_to_locked! # whether it moved or not
93
97
  else
94
98
  # It got destroyed so clear last links then so that (object of)
95
99
  # given unit can be truly destroyed
96
- @local_unit = nil
97
- @locked_to = nil
100
+ @selected_unit = nil
101
+ @locked_unit = nil
98
102
  end
99
103
  end
100
104
  end
101
105
 
102
- # Tries to set function <func> to local unit
106
+ # Tries to set function <func> to selected unit
103
107
  def set_function_to_unit(func)
104
108
  check_unit
105
109
 
106
- if @local_unit
107
- @local_unit.set_function!(func, @infopane.faction)
110
+ if @selected_unit
111
+ @selected_unit.set_function!(func, @infopane.faction)
108
112
 
109
113
  # Update infopane with the new (possibly changed) state
110
114
  # (visible only in freeroam mode as in locked one the infopane is
111
115
  # overwritten as cursor either jumps away or switches to freeroam mode)
112
- @infopane.text = @local_unit.info
116
+ @infopane.text = @selected_unit.info
113
117
  else
114
118
  puts "no unit to set that function to (at #{@x}-#{@y})"
115
119
  end
@@ -119,19 +123,19 @@ class Cursor
119
123
  def reset!
120
124
  @freeroam = true
121
125
  switch_freeroam!
122
- @local_unit = nil
123
- @locked_to = nil
126
+ @selected_unit = nil
127
+ @locked_unit = nil
124
128
  to_next_unit!
125
129
  end
126
130
 
127
131
  # Find next unit which is still waiting for commands and lock to it
128
- # (local -> last locked to -> next waiting) or switch (back) to freeroaming
132
+ # (selected -> last locked to -> next waiting) or switch (back) to freeroaming
129
133
  def to_next_unit!
130
- if @local_unit and @local_unit.is_waiting_for_commands?
134
+ if @selected_unit and @selected_unit.is_waiting_for_commands?
131
135
  # Lock to such unit (though it may have already been locked)
132
- @locked_to = @local_unit
136
+ @locked_unit = @selected_unit
133
137
  else
134
- unless @locked_to and @locked_to.is_waiting_for_commands?
138
+ unless @locked_unit and @locked_unit.is_waiting_for_commands?
135
139
  waiting = @map.all_units.select { |uu| uu.is_waiting_for_commands? }
136
140
 
137
141
  # Are there still some units of active faction waiting for commands?
@@ -141,7 +145,8 @@ class Cursor
141
145
  return
142
146
  end
143
147
 
144
- @locked_to = waiting[0] # newly selected one
148
+ # Pick the first waiting unit (leftmost of topmosts)
149
+ @locked_unit = waiting[0]
145
150
  end
146
151
  end
147
152
 
@@ -149,11 +154,68 @@ class Cursor
149
154
  info unless @info_stopped # due to switching out of the play game state
150
155
  end
151
156
 
152
- # When cursor is in locked mode there needs to be a local unit it is locked to
153
- # When cursor is in freeroam mode the local unit should still be loaded
157
+ # Skip or cycle units, depending on cursor mode
158
+ def select_other_unit!(search_forward)
159
+ unless @freeroam
160
+ skip_to_other_unit!(search_forward)
161
+ else
162
+ cycle_to_other_unit!(search_forward)
163
+ end
164
+ end
165
+
166
+ # Skip from selected unit waiting for commands to the next/previous waiting one
167
+ def skip_to_other_unit!(search_forward = true)
168
+ if @freeroam
169
+ abort("Cursor.skip_to_other_unit!(): " \
170
+ "skipping to #{ search_forward ? 'next' : 'previous'} " \
171
+ "unit works only in locked mode, not in freeroam one")
172
+ end
173
+
174
+ waiting = @map.all_units.select { |uu| uu.is_waiting_for_commands? }
175
+
176
+ # Pick the previous/next waiting unit, relative to the current one
177
+ # (picking the previous of the first results in index -1 which is the last
178
+ # so there it is fine but taking the next of the last would go out of range)
179
+ current_index = waiting.index(@selected_unit)
180
+ other_index = search_forward ? current_index + 1 : current_index - 1
181
+ other_index = 0 if other_index >= waiting.size # == would be enough
182
+
183
+ @locked_unit = waiting[other_index]
184
+ warp_to_locked! # so that to_next_unit!() doesn't stay at @selected_unit
185
+ end
186
+
187
+ # Cycle from selected unit to the next/previous local one
188
+ def cycle_to_other_unit!(search_forward = true)
189
+ unless @freeroam
190
+ abort("Cursor.cycle_to_other_unit!():" \
191
+ "cycling to #{ search_forward ? 'next' : 'previous'} " \
192
+ "unit works only in freeroam mode, not in locked one")
193
+ end
194
+
195
+ unless @selected_unit
196
+ @infopane.text = ''
197
+ puts "no units to cycle through (at #{@x}-#{@y})"
198
+ return
199
+ end
200
+
201
+ local_pile = @map.all_units_from_tile(@x, @y)
202
+
203
+ # Pick the previous/next local unit, relative to the current one
204
+ # (picking the previous of the first results in index -1 which is the last
205
+ # so there it is fine but taking the next of the last would go out of range)
206
+ current_index = local_pile.index(@selected_unit)
207
+ other_index = search_forward ? current_index + 1 : current_index - 1
208
+ other_index = 0 if other_index >= local_pile.size # == would be enough
209
+
210
+ @selected_unit = local_pile[other_index]
211
+ info
212
+ end
213
+
214
+ # When cursor is in locked mode there needs to be unit it is locked to
215
+ # When cursor is in freeroam mode the locked unit should be kept
154
216
  def check_unit
155
- if !@freeroam and !@local_unit
156
- abort("cursor.set_function_to_unit(): Cursor is in locked mode " \
217
+ if !@freeroam and !@locked_unit
218
+ abort("Cursor.check_unit(): Cursor is in locked mode " \
157
219
  "but there is no unit it is locked to (at #{@x}-#{@y})")
158
220
  end
159
221
  end
@@ -175,12 +237,12 @@ class Cursor
175
237
  def info
176
238
  check_unit
177
239
 
178
- if @local_unit
179
- @infopane.text = @local_unit.info
180
- puts @local_unit.info
240
+ if @selected_unit
241
+ @infopane.text = @selected_unit.info
242
+ puts @selected_unit.info
181
243
 
182
- if @local_unit.is_transporting?
183
- @local_unit.cargo.each { |uu| puts '- cargo: ' + uu.info }
244
+ if @selected_unit.is_transporting?
245
+ @selected_unit.cargo.each { |uu| puts '- cargo: ' + uu.info }
184
246
  end
185
247
  else
186
248
  @infopane.text = ''
@@ -2,7 +2,7 @@ LINE_HEIGHT = 20
2
2
 
3
3
  # Score, turn and event texts
4
4
  class Infopane
5
- attr_reader :faction
5
+ attr_reader :faction, :turn, :score
6
6
  attr_writer :cursor, :text
7
7
 
8
8
  def initialize
@@ -4,6 +4,8 @@ require_relative './../units/army'
4
4
  require_relative './../units/ship'
5
5
  require_relative './../units/town'
6
6
 
7
+ MAX_CARGO_LEVEL = 10
8
+
7
9
  class Map
8
10
  attr_accessor :name, :mapx, :mapy, :infopane
9
11
 
@@ -73,13 +75,13 @@ class Map
73
75
  coords_y = coords[1].to_i
74
76
  if (coords_x < 0 || coords_x >= @mapx ||
75
77
  coords_y < 0 || coords_y >= @mapy)
76
- abort("map.load_unit!(): Unit out of map borders (#{coords_x}-#{coords_y})")
78
+ abort("Map.load_unit!(): Unit out of map borders (#{coords_x}-#{coords_y})")
77
79
  end
78
80
 
79
81
  # Check faction
80
82
  fac = unit[1].to_i
81
83
  if(fac < 0 || fac > FACTIONS)
82
- abort("map.load_unit!(): Bad faction id (#{fac})")
84
+ abort("Map.load_unit!(): Bad faction id (#{fac})")
83
85
  end
84
86
 
85
87
  # Create unit
@@ -87,7 +89,7 @@ class Map
87
89
  if @known_unit_types.include?(unit_type)
88
90
  @known_unit_types[unit_type].new(coords_x, coords_y, fac, self, @infopane)
89
91
  else
90
- abort("map.load_unit!(): Unknown unit type symbol (#{unit_type})")
92
+ abort("Map.load_unit!(): Unknown unit type symbol (#{unit_type})")
91
93
  end
92
94
  end
93
95
 
@@ -105,7 +107,7 @@ class Map
105
107
  def all_tiles
106
108
  ret = []
107
109
  ii = 0
108
- 0.upto(MAPX) { |rr|
110
+ 0.upto(MAPY) { |rr|
109
111
  0.upto(MAPX) { |cc|
110
112
  ret[ii] = @tiles[rr][cc]
111
113
  ii += 1
@@ -124,34 +126,64 @@ class Map
124
126
  @tiles[rr][cc].unit = uu
125
127
  end
126
128
 
127
- # Return both map units and transported units
128
- def all_units
129
- all_map_units + all_transported_units
130
- end
131
-
132
- # Return only units directly on map
133
- def all_map_units
129
+ # Return all units with cargo level in given range
130
+ # CL0 = map units, CL1 = units in map units, CL2 = units in CL1 units...
131
+ # all_units() -> all units, all_units(0, 0) -> only map units
132
+ def all_units(min_cargo_level = 0, max_cargo_level = MAX_CARGO_LEVEL)
134
133
  ret = []
135
- ii = 0
136
- 0.upto(MAPX) { |rr|
134
+
135
+ unless min_cargo_level.between?(0, MAX_CARGO_LEVEL) and
136
+ max_cargo_level.between?(0, MAX_CARGO_LEVEL)
137
+ abort("Map.all_units(): Desired cargo levels need to be " \
138
+ "from 0 to #{MAX_CARGO_LEVEL}")
139
+ end
140
+ unless min_cargo_level <= max_cargo_level
141
+ abort("Map.all_units(): Min cargo level (#{min_cargo_level}) is higher " \
142
+ "than max cargo level (#{max_cargo_level})")
143
+ end
144
+
145
+ 0.upto(MAPY) { |rr|
137
146
  0.upto(MAPX) { |cc|
138
- if get_unit(cc, rr)
139
- ret[ii] = get_unit(cc, rr)
140
- ii += 1
141
- end
147
+ ret += all_units_from_tile(cc, rr)
142
148
  }
143
149
  }
150
+
144
151
  ret
145
152
  end
146
153
 
147
- # Return only units transported by other units
148
- def all_transported_units
154
+ # Return all units from given tile with cargo level in given range
155
+ def all_units_from_tile(cc, rr,
156
+ min_cargo_level = 0,
157
+ max_cargo_level = MAX_CARGO_LEVEL)
149
158
  ret = []
150
- all_map_units.each { |uu|
151
- if uu.is_transporting?
152
- ret += uu.cargo
153
- end
154
- }
159
+
160
+ uu = get_unit(cc, rr)
161
+ if uu
162
+ ret += add_unit_to_list(uu, 0, min_cargo_level, max_cargo_level)
163
+ end
164
+
165
+ ret
166
+ end
167
+
168
+ # Recursively add units to the list
169
+ def add_unit_to_list(unit, cargo_level = 0,
170
+ min_cargo_level = 0,
171
+ max_cargo_level = MAX_CARGO_LEVEL)
172
+ ret = []
173
+
174
+ # This unit
175
+ if cargo_level >= min_cargo_level
176
+ ret.append(unit)
177
+ end
178
+
179
+ # Transported units
180
+ cargo_level += 1
181
+ if cargo_level <= max_cargo_level
182
+ unit.cargo.each { |uu|
183
+ ret += add_unit_to_list(uu, cargo_level, min_cargo_level, max_cargo_level)
184
+ }
185
+ end
186
+
155
187
  ret
156
188
  end
157
189
  end
@@ -1,3 +1,5 @@
1
+ require_relative './../game_states/set_state'
2
+
1
3
  TILE_SEA = 0
2
4
  TILE_GROUND = 1
3
5
 
@@ -22,7 +24,7 @@ class Tile
22
24
  @terrain = TILE_GROUND
23
25
  @image = Gosu::Image.new(dir_path + '/../../media/ground.png')
24
26
  else
25
- abort("tile.initialize(): Unknown terrain symbol (#{loaded_symbol})")
27
+ abort("Tile.initialize(): Unknown terrain symbol (#{loaded_symbol})")
26
28
  end
27
29
  end
28
30
 
@@ -36,8 +38,10 @@ class Tile
36
38
  end
37
39
 
38
40
  # 3) axes
39
- draw_axis_tick(@x, @y, 0) # TODO 0 -> viewport.y
40
- draw_axis_tick(@y, @x, 0) # TODO 0 -> viewport.x
41
+ draw_axis_tick(@x, @y, 0) if SetState.instance.settings['axis_top'] # TODO 0 -> viewport.y
42
+ draw_axis_tick(@x, @y, MAPY) if SetState.instance.settings['axis_bottom']
43
+ draw_axis_tick(@y, @x, 0) if SetState.instance.settings['axis_left'] # TODO 0 -> viewport.x
44
+ draw_axis_tick(@y, @x, MAPX) if SetState.instance.settings['axis_right']
41
45
  end
42
46
 
43
47
  # Draw one tick of axis for appropriate tiles
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: empi
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.24'
4
+ version: '0.25'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Detros
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-22 00:00:00.000000000 Z
11
+ date: 2021-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gosu
@@ -52,12 +52,14 @@ files:
52
52
  - lib/lib/game_states/game_state.rb
53
53
  - lib/lib/game_states/play_state.rb
54
54
  - lib/lib/game_states/quit_state.rb
55
+ - lib/lib/game_states/set_state.rb
55
56
  - lib/lib/game_states/welcome_state.rb
57
+ - lib/lib/game_states/win_state.rb
56
58
  - lib/lib/units/army.rb
57
59
  - lib/lib/units/ship.rb
58
60
  - lib/lib/units/town.rb
59
61
  - lib/lib/units/unit.rb
60
- - lib/lib/units/unitFunction.rb
62
+ - lib/lib/units/unit_function.rb
61
63
  - lib/lib/user_interface/cursor.rb
62
64
  - lib/lib/user_interface/infopane.rb
63
65
  - lib/lib/user_interface/map.rb