empi 0.18 → 0.23

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,13 +1,16 @@
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
14
  @armor_left = @armor_max = 3
12
15
  @moves_max = 5
13
16
  end
@@ -1,13 +1,16 @@
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
14
  @armor_left = @armor_max = 1
12
15
  @moves_max = 2
13
16
  @cargo_max = 3
@@ -0,0 +1,106 @@
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
+ @armor_left = @armor_max = 1
21
+ @moves_max = 0
22
+ @cargo_max = 10
23
+
24
+ @starting_project = Army unless @faction == 0
25
+ @project = nil
26
+ @parts_built = 0
27
+ @parts_needed = 0
28
+
29
+ set_function!(FUNCBUILD, @faction) unless @faction == 0
30
+ end
31
+
32
+ def can_build?
33
+ true
34
+ end
35
+
36
+ def can_be_captured?
37
+ true
38
+ end
39
+
40
+ # Tell the state of current build project
41
+ def build_info
42
+ "#{@parts_built}/#{@parts_needed}"
43
+ end
44
+
45
+ # Process capture targeted at this town and reset build process
46
+ def capture!(by_whom)
47
+ super
48
+
49
+ # Reset build process
50
+ @function.func = FUNCNONE
51
+ @project = nil
52
+ @parts_built = 0
53
+
54
+ set_function!(FUNCBUILD, @faction)
55
+ end
56
+
57
+ # Set desired function and possibly also project
58
+ def set_function!(func, commanding_faction)
59
+ super
60
+
61
+ if @faction != 0 and commanding_faction == @faction and func == FUNCBUILD
62
+ # Set starting project once or ask player about next project
63
+ if @starting_project
64
+ set_project!(@starting_project)
65
+ @starting_project = nil
66
+ else
67
+ GameState.switch!(BuildState.instance)
68
+ BuildState.instance.unit = self
69
+ end
70
+ end
71
+ end
72
+
73
+ # Set desired project
74
+ def set_project!(desired_project)
75
+ unless price_list.key?(desired_project)
76
+ abort("town.set_project!(): Unknown project (#{desired_project})")
77
+ end
78
+ @parts_needed = price_list[desired_project]
79
+
80
+ # Compare new setting with the old one
81
+ if desired_project == @project
82
+ puts PROMPT + to_s + ": project has already been set to #{@project.name} (#{build_info} done)"
83
+ else
84
+ previous_project = @project
85
+ @project = desired_project
86
+ lost_parts = @parts_built
87
+ @parts_built = 0
88
+
89
+ new_project_set_text = PROMPT + to_s + ": project set to #{@project.name} (#{build_info} done)"
90
+ unless lost_parts > 0
91
+ puts new_project_set_text
92
+ else
93
+ puts new_project_set_text + ", losing #{lost_parts} " +
94
+ "part#{ 's' unless lost_parts == 1 } of #{previous_project.name}"
95
+
96
+ end
97
+ end
98
+ end
99
+
100
+ # Load all prices
101
+ def price_list
102
+ prices = Hash[
103
+ [Army, Ship].collect { |ii| [ii, ii.price] } # TODO all subclasses of Unit which can_be_built?
104
+ ]
105
+ end
106
+ end
@@ -0,0 +1,301 @@
1
+ require_relative './unitFunction'
2
+
3
+ # Both capturable and movable game pieces
4
+ class Unit
5
+ class << self
6
+ attr_reader :name, :map_symbol, :price, :value
7
+ end
8
+
9
+ attr_reader :armor_left
10
+ attr_accessor :x, :y, :faction, :function, :cargo, :cargo_max
11
+
12
+ def initialize(x, y, faction, map, infopane)
13
+ @x = x
14
+ @y = y
15
+ @faction = faction
16
+ @map = map
17
+ @infopane = infopane
18
+
19
+ @armor_max = 1
20
+ @armor_left = @armor_max
21
+ @moves_max = 1
22
+ @moves_left = @moves_max
23
+ @cargo = [] # transported units
24
+ @cargo_max = 0
25
+ @function = UnitFunction.new(FUNCNONE, self)
26
+
27
+ # Store yourself
28
+ coords_unit = @map.get_unit(@x, @y)
29
+ if !coords_unit
30
+ @map.set_unit(@x, @y, self)
31
+ else # some town has just built you
32
+ coords_unit.cargo.insert(-1, self) # -1 = to the end
33
+ end
34
+ end
35
+
36
+ # Process engagement targeted at this unit
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})")
40
+ end
41
+
42
+ # Can it be captured and is it without defenders?
43
+ if can_be_captured?
44
+ if is_transporting?
45
+ cargo[0].attack!(by_whom)
46
+ else # then capture it if you can
47
+ if by_whom.can_capture?
48
+ puts PROMPT + by_whom.to_s + ' is capturing ' + to_s
49
+ capture!(by_whom.faction)
50
+ else
51
+ puts PROMPT + by_whom.to_s + ' can\'t capture other units'
52
+ end
53
+ end
54
+ else
55
+ attack!(by_whom) # uncapturable transporters can't get help from their cargo
56
+ end
57
+ end
58
+
59
+ # Process attack targeted at this unit
60
+ def attack!(by_whom)
61
+ puts PROMPT + by_whom.to_s + ' is attacking ' + to_s
62
+ destroy! # TODO proper combat
63
+ end
64
+
65
+ # Process capture targeted at this unit
66
+ def capture!(by_whom)
67
+ unless can_be_captured?
68
+ abort("unit.capture!(): This unit can\'t be captured")
69
+ end
70
+
71
+ if by_whom == @faction
72
+ abort("unit.capture!(): This unit already belongs to faction #{@faction}")
73
+ end
74
+
75
+ # Add <value> to the capturing faction
76
+ @infopane.add_score(by_whom - 1, self.class.value)
77
+ @faction = by_whom
78
+ end
79
+
80
+ # Add <value> to the other faction and remove links to given unit
81
+ def destroy!
82
+ @armor_left = 0 # for non-attack damage
83
+
84
+ # If you are transporting somebody, destroy them first
85
+ @cargo.each { |uu| uu.destroy! }
86
+
87
+ @infopane.add_score(3 - @faction - 1, self.class.value) # TODO more factions?
88
+
89
+ if is_transported?
90
+ coords_unit = @map.get_unit(@x, @y)
91
+ coords_unit.cargo -= [self]
92
+ else
93
+ @map.set_unit(@x, @y, nil)
94
+ end
95
+ end
96
+
97
+ # Process move of unit and take care of its (un)loading or start of engagement
98
+ def check_movement(old_x, old_y)
99
+ if @x != old_x || @y != old_y # only if it moved
100
+ if @moves_left <= 0
101
+ abort("unit.check_movement(): Moving unit does not have enough move points (#{@moves_left} moves)")
102
+ end
103
+
104
+ oldcoords_unit = @map.get_unit(old_x, old_y)
105
+ newcoords_unit = @map.get_unit(@x, @y)
106
+
107
+ # If there is nobody or is there friendly unit with some cargo space (and bigger cargo space)
108
+ if !newcoords_unit || \
109
+ (newcoords_unit.faction == @faction && \
110
+ newcoords_unit.can_transport? && \
111
+ @cargo_max < newcoords_unit.cargo_max && \
112
+ !newcoords_unit.is_full?)
113
+
114
+ # Leave old coordinates
115
+ if oldcoords_unit == self
116
+ @map.set_unit(old_x, old_y, nil)
117
+ else # if you have been transported
118
+ oldcoords_unit.cargo.delete(self)
119
+ end
120
+
121
+ # Get to new coordinates
122
+ if !newcoords_unit
123
+ @map.set_unit(@x, @y, self)
124
+ @cargo.each { |uu|
125
+ uu.x = @x
126
+ uu.y = @y
127
+ }
128
+
129
+ else # if you are going to be transported
130
+ newcoords_unit.cargo.insert(-1, self) # -1 = to the end
131
+ puts PROMPT + to_s + ' got loaded into '+ newcoords_unit.to_s
132
+ @moves_left = 1 unless newcoords_unit.can_build? # use all your left moves unless you are getting loaded into a town
133
+ end
134
+
135
+ else # if there already is somebody that can't transport you (enemy or full friend)
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
148
+ puts PROMPT + newcoords_unit.to_s + ' is already full'
149
+ end
150
+ end
151
+ else
152
+ # Enemy!
153
+ newcoords_unit.engage!(self)
154
+ end
155
+ end
156
+ @moves_left -= 1
157
+
158
+ # Check if you are on an invalid type of terrain (unless transported)
159
+ unless is_terrain_suitable?
160
+ puts PROMPT + to_s + " found itself in a bad place"
161
+ destroy!
162
+ end
163
+ end
164
+ end
165
+
166
+ def draw
167
+ @image.draw(@x * TILESIZE, (@y + 1) * TILESIZE, ZUNIT,
168
+ scale_x = 1, scale_y = 1, color = COLOUR[@faction])
169
+ end
170
+
171
+ def is_waiting_for_commands?
172
+ @faction == @infopane.faction and
173
+ function == FUNCNONE and
174
+ can_move?
175
+ end
176
+
177
+ def can_move?
178
+ (can_fly? || can_sail? || can_ride?) && @moves_left > 0
179
+ end
180
+
181
+ def can_fly?
182
+ false
183
+ end
184
+
185
+ def can_sail?
186
+ false
187
+ end
188
+
189
+ def can_ride?
190
+ false
191
+ end
192
+
193
+ def can_build?
194
+ false
195
+ end
196
+
197
+ def can_be_built?
198
+ self.class.price > 0
199
+ end
200
+
201
+ def can_transport?
202
+ @cargo_max > 0
203
+ end
204
+
205
+ def is_full?
206
+ @cargo.size >= @cargo_max # just == should be enough
207
+ end
208
+
209
+ def is_transporting?
210
+ @cargo.size > 0
211
+ end
212
+
213
+ def is_transported?
214
+ @map.get_unit(@x, @y) != self
215
+ end
216
+
217
+ def can_capture?
218
+ false
219
+ end
220
+
221
+ def can_be_captured?
222
+ false
223
+ end
224
+
225
+ # Checks if unit is on the right type of terrain (not for transported units)
226
+ def is_terrain_suitable?
227
+ unless is_transported?
228
+ case @map.tile(@x, @y).terrain
229
+ when TILE_SEA
230
+ return can_fly? || can_sail?
231
+ when TILE_GROUND
232
+ return can_fly? || can_ride?
233
+ end
234
+ end
235
+ true
236
+ end
237
+
238
+ def function
239
+ @function.func
240
+ end
241
+
242
+ # Set desired function
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
249
+
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}"
268
+ end
269
+ end
270
+
271
+ def function!
272
+ ret = @function.func!(@map, @infopane)
273
+ puts to_s + ret
274
+ end
275
+
276
+ # Set short info string: type, faction, coordinates
277
+ def to_s
278
+ "#{self.class.name} (FAC#{@faction} #{@x}-#{@y})"
279
+ end
280
+
281
+ # Set long info string: short info string, armor, moves, function, cargo
282
+ def info
283
+ ret = to_s + ": armor #{@armor_left}/#{@armor_max}"
284
+
285
+ if @moves_max > 0
286
+ ret = ret + ", moves #{@moves_left}/#{@moves_max}"
287
+ end
288
+
289
+ ret = ret + ", #{@function.info}"
290
+
291
+ if @cargo_max > 0
292
+ ret = ret + ", cargo #{@cargo.size}/#{@cargo_max}"
293
+ end
294
+
295
+ ret
296
+ end
297
+
298
+ def reset_moves!
299
+ @moves_left = @moves_max
300
+ end
301
+ end
@@ -0,0 +1,63 @@
1
+ class UnitFunction
2
+ attr_accessor :func
3
+
4
+ def initialize(function = FUNCNONE, unit)
5
+ @func = function
6
+ @unit = unit
7
+ end
8
+
9
+ # Set function part of long info string of unit
10
+ def info
11
+ case @func
12
+ when FUNCNONE
13
+ "no function"
14
+ when FUNCBUILD
15
+ unless @unit.project
16
+ abort("unitFunction.info(): No project set (" + @unit.to_s + ")")
17
+ end
18
+ "building #{@unit.project.name} (#{@unit.build_info})"
19
+ when FUNCSENTRY
20
+ "sentrying"
21
+ end
22
+ end
23
+
24
+ def func!(map, infopane)
25
+ case @func
26
+ # Build given unit
27
+ when FUNCBUILD
28
+ unless @unit.project
29
+ abort("unitFunction.func!(): No project set (" + @unit.to_s + ")")
30
+ end
31
+
32
+ @unit.parts_built += 1
33
+
34
+ unless @unit.parts_built >= @unit.parts_needed # just == should be enough
35
+ ret = " built next part of #{@unit.project.name} (#{@unit.build_info} done)"
36
+ else
37
+ ret = " completed building of #{@unit.project.name}"
38
+ @unit.parts_built = 0
39
+ @unit.project.new(@unit.x, @unit.y, @unit.faction, map, infopane)
40
+ end
41
+
42
+ # Wake when enemies are nearby
43
+ when FUNCSENTRY
44
+ units_around = map.all_units.select { |uu|
45
+ (uu.x - @unit.x).abs <= 1 &&
46
+ (uu.y - @unit.y).abs <= 1 &&
47
+ uu.faction != @unit.faction
48
+ }
49
+ if units_around.size > 0
50
+ ret = " woke up"
51
+ @func = FUNCNONE
52
+ else
53
+ ret = " was on sentry duty"
54
+ end
55
+ end
56
+
57
+ unless ret
58
+ abort("unitFunction.func!(): Functionable unit didn't function (" + @unit.to_s + ")")
59
+ end
60
+
61
+ ret
62
+ end
63
+ end