empi 0.19 → 0.24

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.
@@ -1,14 +1,17 @@
1
1
  require_relative './unit'
2
2
 
3
3
  class Army < Unit
4
+ @name = 'army'
5
+ @map_symbol = 'A'
6
+ @price = 3
7
+ @value = 5
8
+
4
9
  def initialize(x, y, faction, map, infopane)
5
10
  super
6
11
  dir_path = File.dirname(__FILE__)
7
- @image = Gosu::Image.new(dir_path + '/media/army.png')
12
+ @image = Gosu::Image.new(dir_path + '/../../media/army.png')
8
13
 
9
- @name = 'army'
10
- @value = 5
11
- @armor_left = @armor_max = 3
14
+ @armour_left = @armour_max = 3
12
15
  @moves_max = 5
13
16
  end
14
17
 
@@ -1,14 +1,17 @@
1
1
  require_relative './unit'
2
2
 
3
3
  class Ship < Unit
4
+ @name = 'ship'
5
+ @map_symbol = 'S'
6
+ @price = 5
7
+ @value = 10
8
+
4
9
  def initialize(x, y, faction, map, infopane)
5
10
  super
6
11
  dir_path = File.dirname(__FILE__)
7
- @image = Gosu::Image.new(dir_path + '/media/ship.png')
12
+ @image = Gosu::Image.new(dir_path + '/../../media/ship.png')
8
13
 
9
- @name = 'ship'
10
- @value = 10
11
- @armor_left = @armor_max = 1
14
+ @armour_left = @armour_max = 1
12
15
  @moves_max = 2
13
16
  @cargo_max = 3
14
17
  end
@@ -0,0 +1,108 @@
1
+ require_relative './army'
2
+ require_relative './ship'
3
+ require_relative './unit'
4
+
5
+ require_relative './../game_states/game_state'
6
+ require_relative './../game_states/build_state'
7
+
8
+ class Town < Unit
9
+ @name = 'town'
10
+ @map_symbol = 'T'
11
+ @value = 20
12
+
13
+ attr_accessor :project, :parts_built, :parts_needed
14
+
15
+ def initialize(x, y, faction, map, infopane)
16
+ super
17
+ dir_path = File.dirname(__FILE__)
18
+ @image = Gosu::Image.new(dir_path + '/../../media/town.png')
19
+
20
+ @armour_left = @armour_max = 1
21
+ @moves_max = 0
22
+ @cargo_max = 10
23
+
24
+ @starting_project = Army unless @faction == 0 # used once at the game start
25
+ @default_project = Army # used after capture
26
+ @project = nil
27
+ @parts_built = 0
28
+ @parts_needed = 0
29
+
30
+ set_function!(FUNCBUILD, @faction) unless @faction == 0
31
+ end
32
+
33
+ def can_build?
34
+ true
35
+ end
36
+
37
+ def can_be_captured?
38
+ true
39
+ end
40
+
41
+ # Tell the state of current build project
42
+ def build_info
43
+ "#{@parts_built}/#{@parts_needed}"
44
+ end
45
+
46
+ # Process capture targeted at this town and reset build process
47
+ def capture!(by_whom)
48
+ super
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
55
+ @project = nil
56
+ set_project!(@default_project)
57
+ set_function!(FUNCBUILD, @faction)
58
+ end
59
+
60
+ # Set desired function and possibly also project
61
+ def set_function!(func, commanding_faction)
62
+ super
63
+
64
+ if @faction != 0 and commanding_faction == @faction and func == FUNCBUILD
65
+ # Set starting project once or ask player about next project
66
+ if @starting_project
67
+ set_project!(@starting_project)
68
+ @starting_project = nil
69
+ else
70
+ GameState.switch!(BuildState.instance)
71
+ BuildState.instance.unit = self
72
+ end
73
+ end
74
+ end
75
+
76
+ # Set desired project
77
+ def set_project!(desired_project)
78
+ unless price_list.key?(desired_project)
79
+ abort("town.set_project!(): Unknown project (#{desired_project})")
80
+ end
81
+ @parts_needed = price_list[desired_project]
82
+
83
+ # Compare new setting with the old one
84
+ if desired_project == @project
85
+ puts PROMPT + to_s + ": project has already been set to #{@project.name} (#{build_info} done)"
86
+ else
87
+ previous_project = @project
88
+ @project = desired_project
89
+ lost_parts = @parts_built
90
+ @parts_built = 0
91
+
92
+ new_project_set_text = PROMPT + to_s + ": project set to #{@project.name} (#{build_info} done)"
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}"
96
+ else
97
+ puts new_project_set_text
98
+ end
99
+ end
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
108
+ end
@@ -1,10 +1,13 @@
1
1
  require_relative './unitFunction'
2
2
 
3
- PARTS_TO_BE_BUILT = 0
4
-
5
3
  # Both capturable and movable game pieces
6
4
  class Unit
7
- attr_accessor :x, :y, :faction, :function, :cargo
5
+ class << self
6
+ attr_reader :name, :map_symbol, :price, :value
7
+ end
8
+
9
+ attr_reader :armour_left
10
+ attr_accessor :x, :y, :faction, :function, :cargo, :cargo_max
8
11
 
9
12
  def initialize(x, y, faction, map, infopane)
10
13
  @x = x
@@ -13,10 +16,8 @@ class Unit
13
16
  @map = map
14
17
  @infopane = infopane
15
18
 
16
- @name = 'unit'
17
- @value = 1
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
@@ -72,16 +73,18 @@ class Unit
72
73
  end
73
74
 
74
75
  # Add <value> to the capturing faction
75
- @infopane.add_score(by_whom - 1, @value)
76
+ @infopane.add_score(by_whom - 1, self.class.value)
76
77
  @faction = by_whom
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
 
84
- @infopane.add_score(3 - @faction - 1, @value) # TODO more factions?
87
+ @infopane.add_score(3 - @faction - 1, self.class.value) # TODO more factions?
85
88
 
86
89
  if is_transported?
87
90
  coords_unit = @map.get_unit(@x, @y)
@@ -101,10 +104,11 @@ class Unit
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,39 @@ 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
124
  @cargo.each { |uu|
121
125
  uu.x = @x
122
126
  uu.y = @y
123
127
  }
124
128
 
125
- else # if you are going to be transported
129
+ else # if you are going to be transported
126
130
  newcoords_unit.cargo.insert(-1, self) # -1 = to the end
127
131
  puts PROMPT + to_s + ' got loaded into '+ newcoords_unit.to_s
128
132
  @moves_left = 1 unless newcoords_unit.can_build? # use all your left moves unless you are getting loaded into a town
129
133
  end
130
134
 
131
135
  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
136
+ # Stay on your original tile
137
+ @x = old_x
138
+ @y = old_y
139
+
140
+ # If it was a friend unit
141
+ if newcoords_unit.faction == @faction
142
+ if !newcoords_unit.can_transport?
143
+ puts PROMPT + newcoords_unit.to_s + ' can\'t transport other units'
144
+ else
145
+ if @cargo_max >= newcoords_unit.cargo_max
146
+ puts PROMPT + "#{self.class.name} can\'t fit in #{newcoords_unit.class.name}"
147
+ else # thus newcoords_unit.is_full? is true
141
148
  puts PROMPT + newcoords_unit.to_s + ' is already full'
142
149
  end
143
- else
144
- # Enemy!
145
- newcoords_unit.engage!(self)
146
150
  end
151
+ else
152
+ # Enemy!
153
+ newcoords_unit.engage!(self)
154
+ end
147
155
  end
148
156
  @moves_left -= 1
149
157
 
@@ -155,12 +163,17 @@ class Unit
155
163
  end
156
164
  end
157
165
 
158
-
159
166
  def draw
160
167
  @image.draw(@x * TILESIZE, (@y + 1) * TILESIZE, ZUNIT,
161
168
  scale_x = 1, scale_y = 1, color = COLOUR[@faction])
162
169
  end
163
170
 
171
+ def is_waiting_for_commands?
172
+ @faction == @infopane.faction and
173
+ function == FUNCNONE and
174
+ can_move?
175
+ end
176
+
164
177
  def can_move?
165
178
  (can_fly? || can_sail? || can_ride?) && @moves_left > 0
166
179
  end
@@ -182,7 +195,7 @@ class Unit
182
195
  end
183
196
 
184
197
  def can_be_built?
185
- PARTS_TO_BE_BUILT > 0
198
+ self.class.price > 0
186
199
  end
187
200
 
188
201
  def can_transport?
@@ -227,19 +240,31 @@ class Unit
227
240
  end
228
241
 
229
242
  # 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
243
+ def set_function!(func, commanding_faction)
244
+ # Check your neutrality
245
+ if @faction == 0
246
+ puts PROMPT + to_s + ": neutral units can't have functions set"
247
+ return
248
+ end
236
249
 
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
250
+ # Check origin of command
251
+ if commanding_faction != @faction
252
+ puts PROMPT + to_s + ": this unit does not take commands from other factions"
253
+ return
254
+ end
255
+
256
+ # Check your abilities
257
+ if func == FUNCBUILD and !can_build?
258
+ puts PROMPT + to_s + ": this unit can't build other units"
259
+ return
260
+ end
261
+
262
+ # Check current function and set the new one
263
+ if @function.func == func
264
+ puts PROMPT + to_s + ": function is already set to #{@function.func}"
265
+ else
266
+ @function.func = func
267
+ puts PROMPT + to_s + ": function set to #{@function.func}"
243
268
  end
244
269
  end
245
270
 
@@ -250,12 +275,12 @@ class Unit
250
275
 
251
276
  # Set short info string: type, faction, coordinates
252
277
  def to_s
253
- "#{@name} (FAC#{@faction} #{@x}-#{@y})"
278
+ "#{self.class.name} (FAC#{@faction} #{@x}-#{@y})"
254
279
  end
255
280
 
256
- # Set long info string: short info string, armor, moves, function, cargo
281
+ # Set long info string: short info string, armour, moves, function, cargo
257
282
  def info
258
- ret = to_s + ": armor #{@armor_left}/#{@armor_max}"
283
+ ret = to_s + ": armour #{@armour_left}/#{@armour_max}"
259
284
 
260
285
  if @moves_max > 0
261
286
  ret = ret + ", moves #{@moves_left}/#{@moves_max}"
@@ -12,26 +12,31 @@ class UnitFunction
12
12
  when FUNCNONE
13
13
  "no function"
14
14
  when FUNCBUILD
15
- "building #{@unit.project} (#{@unit.build_info})"
15
+ unless @unit.project
16
+ abort("unitFunction.info(): No project set (" + @unit.to_s + ")")
17
+ end
18
+ "building #{@unit.project.name} (#{@unit.build_info})"
16
19
  when FUNCSENTRY
17
20
  "sentrying"
18
21
  end
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
30
- ret = " built next part of #{@unit.project} (#{@unit.build_info} done)"
35
+ ret = " built next part of #{@unit.project.name} (#{@unit.build_info} done)"
31
36
  else
32
- ret = " completed building of #{@unit.project}"
37
+ ret = " completed building of #{@unit.project.name}"
33
38
  @unit.parts_built = 0
34
- Army.new(@unit.x, @unit.y, @unit.faction, map, infopane)
39
+ @unit.project.new(@unit.x, @unit.y, @unit.faction, map, infopane)
35
40
  end
36
41
 
37
42
  # Wake when enemies are nearby
@@ -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
@@ -0,0 +1,190 @@
1
+ class Cursor
2
+ attr_accessor :freeroam, :info_stopped
3
+
4
+ def initialize(x, y, map, infopane)
5
+ dir_path = File.dirname(__FILE__)
6
+
7
+ @x = x
8
+ @y = y
9
+ @map = map
10
+ @infopane = infopane
11
+
12
+ @image = Gosu::Image.new(dir_path + '/../../media/cursor.png')
13
+ @freeroam = false
14
+ @info_stopped = false
15
+
16
+ # Give to Infopane link to yourself (for freeroam marker)
17
+ if !@infopane
18
+ abort("Cursor.initialize!(): Infopane is not set")
19
+ end
20
+ @infopane.cursor = self
21
+ end
22
+
23
+ def update(button)
24
+ case button
25
+ # Cardinal directions
26
+ when Gosu::KbLeft, Gosu::KbA, Gosu::KB_NUMPAD_4 then
27
+ move!(-1, 0) unless @x <= 0
28
+ when Gosu::KbRight, Gosu::KbD, Gosu::KB_NUMPAD_6 then
29
+ move!(1, 0) unless @x >= MAPX
30
+ when Gosu::KbUp, Gosu::KbW, Gosu::KB_NUMPAD_8 then
31
+ move!(0, -1) unless @y <= 0
32
+ when Gosu::KbDown, Gosu::KbX, Gosu::KB_NUMPAD_2 then
33
+ move!(0, 1) unless @y >= MAPY
34
+
35
+ # Intercardinal directions
36
+ when Gosu::KbQ, Gosu::KB_NUMPAD_7 then
37
+ move!(-1, -1) unless @x <= 0 || @y <= 0
38
+ when Gosu::KbE, Gosu::KB_NUMPAD_9 then
39
+ move!(1, -1) unless @x >= MAPX || @y <= 0
40
+ when Gosu::KbZ, Gosu::KB_NUMPAD_1 then
41
+ move!(-1, 1) unless @x <= 0 || @y >= MAPY
42
+ when Gosu::KbC, Gosu::KB_NUMPAD_3 then
43
+ move!(1, 1) unless @x >= MAPX || @y >= MAPY
44
+
45
+ # Functions
46
+ when Gosu::KbS then
47
+ set_function_to_unit(FUNCSENTRY)
48
+ when Gosu::KbB then
49
+ set_function_to_unit(FUNCBUILD)
50
+ when Gosu::KbN then
51
+ set_function_to_unit(FUNCNONE)
52
+
53
+ # The rest
54
+ when Gosu::KbJ, Gosu::KB_NUMPAD_0 then
55
+ switch_freeroam!
56
+ when Gosu::KbReturn, Gosu::KB_NUMPAD_5 then
57
+ info
58
+ end
59
+
60
+ # If in locked mode, stay at current/jump to next movable unit
61
+ to_next_unit! unless @freeroam
62
+ end
63
+
64
+ def draw
65
+ @image.draw(@x * TILESIZE, (@y + 1) * TILESIZE, ZCURSOR)
66
+ end
67
+
68
+ # Move to coordinates of unit the cursor is locked to
69
+ def warp_to_locked!()
70
+ @x = @locked_to.x
71
+ @y = @locked_to.y
72
+ @local_unit = @locked_to
73
+ end
74
+
75
+ # Move by given change of coordinates
76
+ def move!(xx, yy)
77
+ if freeroam
78
+ @x += xx
79
+ @y += yy
80
+ @local_unit = @map.get_unit(@x, @y)
81
+ # TODO show some basic tile info in infopane
82
+ else
83
+ check_unit
84
+
85
+ # 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
89
+
90
+ # Is the unit still alive?
91
+ if @locked_to.armour_left > 0
92
+ warp_to_locked! # whether it moved or not
93
+ else
94
+ # It got destroyed so clear last links then so that (object of)
95
+ # given unit can be truly destroyed
96
+ @local_unit = nil
97
+ @locked_to = nil
98
+ end
99
+ end
100
+ end
101
+
102
+ # Tries to set function <func> to local unit
103
+ def set_function_to_unit(func)
104
+ check_unit
105
+
106
+ if @local_unit
107
+ @local_unit.set_function!(func, @infopane.faction)
108
+
109
+ # Update infopane with the new (possibly changed) state
110
+ # (visible only in freeroam mode as in locked one the infopane is
111
+ # overwritten as cursor either jumps away or switches to freeroam mode)
112
+ @infopane.text = @local_unit.info
113
+ else
114
+ puts "no unit to set that function to (at #{@x}-#{@y})"
115
+ end
116
+ end
117
+
118
+ # Reset to locked mode
119
+ def reset!
120
+ @freeroam = true
121
+ switch_freeroam!
122
+ @local_unit = nil
123
+ @locked_to = nil
124
+ to_next_unit!
125
+ end
126
+
127
+ # 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
129
+ def to_next_unit!
130
+ if @local_unit and @local_unit.is_waiting_for_commands?
131
+ # Lock to such unit (though it may have already been locked)
132
+ @locked_to = @local_unit
133
+ else
134
+ unless @locked_to and @locked_to.is_waiting_for_commands?
135
+ waiting = @map.all_units.select { |uu| uu.is_waiting_for_commands? }
136
+
137
+ # Are there still some units of active faction waiting for commands?
138
+ if waiting.size <= 0 # == would be enough
139
+ puts 'all movable units without functions moved'
140
+ switch_freeroam!
141
+ return
142
+ end
143
+
144
+ @locked_to = waiting[0] # newly selected one
145
+ end
146
+ end
147
+
148
+ warp_to_locked! # stay at old or go to new
149
+ info unless @info_stopped # due to switching out of the play game state
150
+ end
151
+
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
154
+ def check_unit
155
+ if !@freeroam and !@local_unit
156
+ abort("cursor.set_function_to_unit(): Cursor is in locked mode " \
157
+ "but there is no unit it is locked to (at #{@x}-#{@y})")
158
+ end
159
+ end
160
+
161
+ # Switch between being attached to unit and being able to freeroam
162
+ def switch_freeroam!
163
+ if freeroam
164
+ @infopane.text = 'freeroam disabled'
165
+ puts 'freeroam disabled'
166
+ @freeroam = false
167
+ else
168
+ @infopane.text = 'freeroam enabled'
169
+ puts 'freeroam enabled'
170
+ @freeroam = true
171
+ end
172
+ end
173
+
174
+ # Find some info about units on the current tile
175
+ def info
176
+ check_unit
177
+
178
+ if @local_unit
179
+ @infopane.text = @local_unit.info
180
+ puts @local_unit.info
181
+
182
+ if @local_unit.is_transporting?
183
+ @local_unit.cargo.each { |uu| puts '- cargo: ' + uu.info }
184
+ end
185
+ else
186
+ @infopane.text = ''
187
+ puts "no unit to show info of (at #{@x}-#{@y})"
188
+ end
189
+ end
190
+ end