empi 0.21 → 0.25

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.
@@ -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
@@ -9,9 +9,9 @@ class Army < Unit
9
9
  def initialize(x, y, faction, map, infopane)
10
10
  super
11
11
  dir_path = File.dirname(__FILE__)
12
- @image = Gosu::Image.new(dir_path + '/media/army.png')
12
+ @image = Gosu::Image.new(dir_path + '/../../media/army.png')
13
13
 
14
- @armor_left = @armor_max = 3
14
+ @armour_left = @armour_max = 3
15
15
  @moves_max = 5
16
16
  end
17
17
 
@@ -9,9 +9,9 @@ class Ship < Unit
9
9
  def initialize(x, y, faction, map, infopane)
10
10
  super
11
11
  dir_path = File.dirname(__FILE__)
12
- @image = Gosu::Image.new(dir_path + '/media/ship.png')
12
+ @image = Gosu::Image.new(dir_path + '/../../media/ship.png')
13
13
 
14
- @armor_left = @armor_max = 1
14
+ @armour_left = @armour_max = 1
15
15
  @moves_max = 2
16
16
  @cargo_max = 3
17
17
  end
@@ -1,10 +1,10 @@
1
- require_relative './game_state'
2
- require_relative './build_state'
3
-
4
1
  require_relative './army'
5
2
  require_relative './ship'
6
3
  require_relative './unit'
7
4
 
5
+ require_relative './../game_states/game_state'
6
+ require_relative './../game_states/build_state'
7
+
8
8
  class Town < Unit
9
9
  @name = 'town'
10
10
  @map_symbol = 'T'
@@ -15,18 +15,19 @@ class Town < Unit
15
15
  def initialize(x, y, faction, map, infopane)
16
16
  super
17
17
  dir_path = File.dirname(__FILE__)
18
- @image = Gosu::Image.new(dir_path + '/media/town.png')
18
+ @image = Gosu::Image.new(dir_path + '/../../media/town.png')
19
19
 
20
- @armor_left = @armor_max = 1
20
+ @armour_left = @armour_max = 1
21
21
  @moves_max = 0
22
22
  @cargo_max = 10
23
23
 
24
- @starting_project = Army unless @faction == 0
24
+ @starting_project = Army unless @faction == 0 # used once at the game start
25
+ @default_project = Army # used after capture
25
26
  @project = nil
26
27
  @parts_built = 0
27
28
  @parts_needed = 0
28
29
 
29
- set_function!(FUNCBUILD)
30
+ set_function!(FUNCBUILD, @faction) unless @faction == 0
30
31
  end
31
32
 
32
33
  def can_build?
@@ -47,17 +48,21 @@ class Town < Unit
47
48
  super
48
49
 
49
50
  # Reset build process
51
+ # 1) remove old project so that it is not disclosed
52
+ # 2) set default project so that there is always some set (remove old parts)
53
+ # 3) offer change of the project to the new owner
54
+ @function.func = FUNCNONE
50
55
  @project = nil
51
- @parts_built = 0
52
- set_function!(FUNCBUILD)
56
+ set_project!(@default_project)
57
+ set_function!(FUNCBUILD, @faction)
53
58
  end
54
59
 
55
60
  # Set desired function and possibly also project
56
- def set_function!(func)
61
+ def set_function!(func, commanding_faction)
57
62
  super
58
63
 
59
- if @faction != 0 and func == FUNCBUILD # neutral towns don't need projects
60
- # Set once starting project or ask player about next project
64
+ if @faction != 0 and commanding_faction == @faction and func == FUNCBUILD
65
+ # Set starting project once or ask player about next project
61
66
  if @starting_project
62
67
  set_project!(@starting_project)
63
68
  @starting_project = nil
@@ -70,15 +75,10 @@ class Town < Unit
70
75
 
71
76
  # Set desired project
72
77
  def set_project!(desired_project)
73
- # Figure out the price
74
- prices = Hash[
75
- [Army, Ship].collect { |ii| [ii, ii.price] } # TODO all subclasses of Unit which can_be_built?
76
- ]
77
-
78
- unless prices.key?(desired_project)
79
- abort("town.set_project!(): Unknown project (#{desired_project})")
78
+ unless price_list.key?(desired_project)
79
+ abort("Town.set_project!(): Unknown project (#{desired_project})")
80
80
  end
81
- @parts_needed = prices[desired_project]
81
+ @parts_needed = price_list[desired_project]
82
82
 
83
83
  # Compare new setting with the old one
84
84
  if desired_project == @project
@@ -90,11 +90,19 @@ class Town < Unit
90
90
  @parts_built = 0
91
91
 
92
92
  new_project_set_text = PROMPT + to_s + ": project set to #{@project.name} (#{build_info} done)"
93
- unless lost_parts > 0
94
- puts new_project_set_text
93
+ if previous_project and lost_parts > 0 # parts were lost but not due to capture
94
+ puts new_project_set_text + ", losing #{lost_parts} " +
95
+ "part#{ 's' unless lost_parts == 1 } of #{previous_project.name}"
95
96
  else
96
- puts new_project_set_text + ", losing #{lost_parts} parts of #{previous_project.name} "
97
+ puts new_project_set_text
97
98
  end
98
99
  end
99
100
  end
101
+
102
+ # Load all prices
103
+ def price_list
104
+ prices = Hash[
105
+ [Army, Ship].collect { |ii| [ii, ii.price] } # TODO all subclasses of Unit which can_be_built?
106
+ ]
107
+ end
100
108
  end
@@ -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
@@ -6,7 +6,8 @@ class Unit
6
6
  attr_reader :name, :map_symbol, :price, :value
7
7
  end
8
8
 
9
- attr_accessor :x, :y, :faction, :function, :cargo
9
+ attr_reader :armour_left
10
+ attr_accessor :x, :y, :faction, :function, :cargo, :cargo_max
10
11
 
11
12
  def initialize(x, y, faction, map, infopane)
12
13
  @x = x
@@ -15,8 +16,8 @@ class Unit
15
16
  @map = map
16
17
  @infopane = infopane
17
18
 
18
- @armor_max = 1
19
- @armor_left = @armor_max
19
+ @armour_max = 1
20
+ @armour_left = @armour_max
20
21
  @moves_max = 1
21
22
  @moves_left = @moves_max
22
23
  @cargo = [] # transported units
@@ -34,8 +35,8 @@ class Unit
34
35
 
35
36
  # Process engagement targeted at this unit
36
37
  def engage!(by_whom)
37
- if by_whom == @faction
38
- 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})")
39
40
  end
40
41
 
41
42
  # Can it be captured and is it without defenders?
@@ -45,7 +46,7 @@ class Unit
45
46
  else # then capture it if you can
46
47
  if by_whom.can_capture?
47
48
  puts PROMPT + by_whom.to_s + ' is capturing ' + to_s
48
- capture!(by_whom.faction)
49
+ capture!(by_whom)
49
50
  else
50
51
  puts PROMPT + by_whom.to_s + ' can\'t capture other units'
51
52
  end
@@ -64,20 +65,22 @@ class Unit
64
65
  # Process capture targeted at this unit
65
66
  def capture!(by_whom)
66
67
  unless can_be_captured?
67
- abort("unit.capture!(): This unit can\'t be captured")
68
+ abort("Unit.capture!(): This unit can\'t be captured")
68
69
  end
69
70
 
70
- if by_whom == @faction
71
- 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}")
72
73
  end
73
74
 
74
75
  # Add <value> to the capturing faction
75
- @infopane.add_score(by_whom - 1, self.class.value)
76
- @faction = by_whom
76
+ @infopane.add_score(by_whom.faction - 1, self.class.value)
77
+ @faction = by_whom.faction
77
78
  end
78
79
 
79
80
  # Add <value> to the other faction and remove links to given unit
80
81
  def destroy!
82
+ @armour_left = 0 # for non-attack damage
83
+
81
84
  # If you are transporting somebody, destroy them first
82
85
  @cargo.each { |uu| uu.destroy! }
83
86
 
@@ -95,16 +98,17 @@ class Unit
95
98
  def check_movement(old_x, old_y)
96
99
  if @x != old_x || @y != old_y # only if it moved
97
100
  if @moves_left <= 0
98
- 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)")
99
102
  end
100
103
 
101
104
  oldcoords_unit = @map.get_unit(old_x, old_y)
102
105
  newcoords_unit = @map.get_unit(@x, @y)
103
106
 
104
- # If there is nobody or is there friendly unit with some cargo space
107
+ # If there is nobody or is there friendly unit with some cargo space (and bigger cargo space)
105
108
  if !newcoords_unit || \
106
109
  (newcoords_unit.faction == @faction && \
107
110
  newcoords_unit.can_transport? && \
111
+ @cargo_max < newcoords_unit.cargo_max && \
108
112
  !newcoords_unit.is_full?)
109
113
 
110
114
  # Leave old coordinates
@@ -115,35 +119,40 @@ class Unit
115
119
  end
116
120
 
117
121
  # Get to new coordinates
118
- if newcoords_unit == nil
122
+ if !newcoords_unit
119
123
  @map.set_unit(@x, @y, self)
120
- @cargo.each { |uu|
121
- uu.x = @x
122
- uu.y = @y
123
- }
124
-
125
124
  else # if you are going to be transported
126
125
  newcoords_unit.cargo.insert(-1, self) # -1 = to the end
127
126
  puts PROMPT + to_s + ' got loaded into '+ newcoords_unit.to_s
128
127
  @moves_left = 1 unless newcoords_unit.can_build? # use all your left moves unless you are getting loaded into a town
129
128
  end
130
129
 
130
+ # Update position of your cargo
131
+ @cargo.each { |uu|
132
+ uu.x = @x
133
+ uu.y = @y
134
+ }
135
+
131
136
  else # if there already is somebody that can't transport you (enemy or full friend)
132
- # Stay on your original tile
133
- @x = old_x
134
- @y = old_y
135
-
136
- # If it was a friend unit
137
- if newcoords_unit.faction == @faction
138
- if !newcoords_unit.can_transport?
139
- puts PROMPT + newcoords_unit.to_s + ' can\'t transport other units'
140
- else # newcoords_unit.can_transport?; has to be full then
137
+ # Stay on your original tile
138
+ @x = old_x
139
+ @y = old_y
140
+
141
+ # If it was a friend unit
142
+ if newcoords_unit.faction == @faction
143
+ if !newcoords_unit.can_transport?
144
+ puts PROMPT + newcoords_unit.to_s + ' can\'t transport other units'
145
+ else
146
+ if @cargo_max >= newcoords_unit.cargo_max
147
+ puts PROMPT + "#{self.class.name} can\'t fit in #{newcoords_unit.class.name}"
148
+ else # thus newcoords_unit.is_full? is true
141
149
  puts PROMPT + newcoords_unit.to_s + ' is already full'
142
150
  end
143
- else
144
- # Enemy!
145
- newcoords_unit.engage!(self)
146
151
  end
152
+ else
153
+ # Enemy!
154
+ newcoords_unit.engage!(self)
155
+ end
147
156
  end
148
157
  @moves_left -= 1
149
158
 
@@ -155,12 +164,17 @@ class Unit
155
164
  end
156
165
  end
157
166
 
158
-
159
167
  def draw
160
168
  @image.draw(@x * TILESIZE, (@y + 1) * TILESIZE, ZUNIT,
161
169
  scale_x = 1, scale_y = 1, color = COLOUR[@faction])
162
170
  end
163
171
 
172
+ def is_waiting_for_commands?
173
+ @faction == @infopane.faction and
174
+ function == FUNCNONE and
175
+ can_move?
176
+ end
177
+
164
178
  def can_move?
165
179
  (can_fly? || can_sail? || can_ride?) && @moves_left > 0
166
180
  end
@@ -227,19 +241,31 @@ class Unit
227
241
  end
228
242
 
229
243
  # Set desired function
230
- def set_function!(func)
231
- unless @faction == 0 # neutral towns dom't need functions
232
- if func == FUNCBUILD and !can_build?
233
- puts PROMPT + to_s + ": this unit can't build other units"
234
- return
235
- end
244
+ def set_function!(func, commanding_faction)
245
+ # Check your neutrality
246
+ if @faction == 0
247
+ puts PROMPT + to_s + ": neutral units can't have functions set"
248
+ return
249
+ end
236
250
 
237
- if @function.func == func
238
- puts PROMPT + to_s + ": function is already set to #{@function.func}"
239
- else
240
- @function.func = func
241
- puts PROMPT + to_s + ": function set to #{@function.func}"
242
- end
251
+ # Check origin of command
252
+ if commanding_faction != @faction
253
+ puts PROMPT + to_s + ": this unit does not take commands from other factions"
254
+ return
255
+ end
256
+
257
+ # Check your abilities
258
+ if func == FUNCBUILD and !can_build?
259
+ puts PROMPT + to_s + ": this unit can't build other units"
260
+ return
261
+ end
262
+
263
+ # Check current function and set the new one
264
+ if @function.func == func
265
+ puts PROMPT + to_s + ": function is already set to #{@function.func}"
266
+ else
267
+ @function.func = func
268
+ puts PROMPT + to_s + ": function set to #{@function.func}"
243
269
  end
244
270
  end
245
271
 
@@ -253,9 +279,9 @@ class Unit
253
279
  "#{self.class.name} (FAC#{@faction} #{@x}-#{@y})"
254
280
  end
255
281
 
256
- # Set long info string: short info string, armor, moves, function, cargo
282
+ # Set long info string: short info string, armour, moves, function, cargo
257
283
  def info
258
- ret = to_s + ": armor #{@armor_left}/#{@armor_max}"
284
+ ret = to_s + ": armour #{@armour_left}/#{@armour_max}"
259
285
 
260
286
  if @moves_max > 0
261
287
  ret = ret + ", moves #{@moves_left}/#{@moves_max}"
@@ -12,6 +12,9 @@ class UnitFunction
12
12
  when FUNCNONE
13
13
  "no function"
14
14
  when FUNCBUILD
15
+ unless @unit.project
16
+ abort("UnitFunction.info(): No project set (" + @unit.to_s + ")")
17
+ end
15
18
  "building #{@unit.project.name} (#{@unit.build_info})"
16
19
  when FUNCSENTRY
17
20
  "sentrying"
@@ -19,11 +22,13 @@ class UnitFunction
19
22
  end
20
23
 
21
24
  def func!(map, infopane)
22
- ret = " didn't actually do anything (ERROR)"
23
-
24
25
  case @func
25
26
  # Build given unit
26
27
  when FUNCBUILD
28
+ unless @unit.project
29
+ abort("UnitFunction.func!(): No project set (" + @unit.to_s + ")")
30
+ end
31
+
27
32
  @unit.parts_built += 1
28
33
 
29
34
  unless @unit.parts_built >= @unit.parts_needed # just == should be enough
@@ -34,9 +39,9 @@ class UnitFunction
34
39
  @unit.project.new(@unit.x, @unit.y, @unit.faction, map, infopane)
35
40
  end
36
41
 
37
- # Wake when enemies are nearby
42
+ # Wake when enemies are nearby (checking just map units is enough)
38
43
  when FUNCSENTRY
39
- units_around = map.all_units.select { |uu|
44
+ units_around = map.all_units(0,0).select { |uu|
40
45
  (uu.x - @unit.x).abs <= 1 &&
41
46
  (uu.y - @unit.y).abs <= 1 &&
42
47
  uu.faction != @unit.faction
@@ -49,6 +54,10 @@ class UnitFunction
49
54
  end
50
55
  end
51
56
 
57
+ unless ret
58
+ abort("UnitFunction.func!(): Functionable unit didn't function (" + @unit.to_s + ")")
59
+ end
60
+
52
61
  ret
53
62
  end
54
63
  end