empi 0.17 → 0.22.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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) 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)
55
+ end
56
+
57
+ # Set desired function and possibly also project
58
+ def set_function!(func)
59
+ super
60
+
61
+ if @faction != 0 and func == FUNCBUILD # neutral towns don't need projects
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
@@ -1,9 +1,12 @@
1
1
  require_relative './unitFunction'
2
2
 
3
- # All capturable game pieces
3
+ # Both capturable and movable game pieces
4
4
  class Unit
5
- attr_accessor :x, :y, :faction, :function, :cargo
5
+ class << self
6
+ attr_reader :name, :map_symbol, :price, :value
7
+ end
6
8
 
9
+ attr_accessor :x, :y, :faction, :function, :cargo, :cargo_max
7
10
 
8
11
  def initialize(x, y, faction, map, infopane)
9
12
  @x = x
@@ -12,17 +15,13 @@ class Unit
12
15
  @map = map
13
16
  @infopane = infopane
14
17
 
15
- @name = 'unit'
16
- @value = 1
17
18
  @armor_max = 1
18
19
  @armor_left = @armor_max
19
20
  @moves_max = 1
20
21
  @moves_left = @moves_max
21
22
  @cargo = [] # transported units
22
23
  @cargo_max = 0
23
- @function = UnitFunction.new(FUNCNONE)
24
-
25
-
24
+ @function = UnitFunction.new(FUNCNONE, self)
26
25
 
27
26
  # Store yourself
28
27
  coords_unit = @map.get_unit(@x, @y)
@@ -33,75 +32,68 @@ class Unit
33
32
  end
34
33
  end
35
34
 
36
- # Add <value> to the other faction
37
- def destroy
38
- @infopane.add_score(3 - @faction - 1, @value) # TODO more factions?
39
- # TODO is this deleted enough?
40
- @map.set_unit(@x, @y, nil)
41
- end
42
-
43
- def capture(by_whom)
44
- # add <value> to the capturing faction
45
- @infopane.add_score(by_whom - 1, @value)
46
- @faction = by_whom
35
+ # Process engagement targeted at this unit
36
+ def engage!(by_whom)
37
+ if by_whom == @faction
38
+ abort("unit.engage!(): Engaging unit is of the same faction as this one (#{@faction})")
39
+ end
47
40
 
48
- # if you are a town, start building units
49
- if can_build?
50
- set_function(FUNCBUILD)
41
+ # Can it be captured and is it without defenders?
42
+ if can_be_captured?
43
+ if is_transporting?
44
+ cargo[0].attack!(by_whom)
45
+ else # then capture it if you can
46
+ if by_whom.can_capture?
47
+ puts PROMPT + by_whom.to_s + ' is capturing ' + to_s
48
+ capture!(by_whom.faction)
49
+ else
50
+ puts PROMPT + by_whom.to_s + ' can\'t capture other units'
51
+ end
52
+ end
53
+ else
54
+ attack!(by_whom) # uncapturable transporters can't get help from their cargo
51
55
  end
52
56
  end
53
57
 
54
- def update(key)
55
- old_x = @x
56
- old_y = @y
57
-
58
- case key
59
- # Cardinal directions
60
- when Gosu::KbLeft, Gosu::KbA then
61
- @x -= 1 unless @x <= 0
62
- when Gosu::KbRight, Gosu::KbD then
63
- @x += 1 unless @x >= MAPX
64
- when Gosu::KbUp, Gosu::KbW then
65
- @y -= 1 unless @y <= 0
66
- when Gosu::KbDown, Gosu::KbX then
67
- @y += 1 unless @y >= MAPY
68
-
69
- # Intercardinal directions
70
- when Gosu::KbQ then
71
- unless @x <= 0 || @y <= 0
72
- @x -= 1
73
- @y -= 1
74
- end
75
- when Gosu::KbE then
76
- unless @x >= MAPX || @y <= 0
77
- @x += 1
78
- @y -= 1
79
- end
80
- when Gosu::KbZ then
81
- unless @x <= 0 || @y >= MAPY
82
- @x -= 1
83
- @y += 1
84
- end
85
- when Gosu::KbC then
86
- unless @x >= MAPX || @y >= MAPY
87
- @x += 1
88
- @y += 1
89
- end
58
+ # Process attack targeted at this unit
59
+ def attack!(by_whom)
60
+ puts PROMPT + by_whom.to_s + ' is attacking ' + to_s
61
+ destroy! # TODO proper combat
62
+ end
90
63
 
91
- # Functions
92
- when Gosu::KbS then
93
- set_function(FUNCSENTRY)
94
- when Gosu::KbB then
95
- set_function(FUNCBUILD)
64
+ # Process capture targeted at this unit
65
+ def capture!(by_whom)
66
+ unless can_be_captured?
67
+ abort("unit.capture!(): This unit can\'t be captured")
96
68
  end
97
69
 
98
- check_movement(old_x, old_y)
70
+ if by_whom == @faction
71
+ abort("unit.capture!(): This unit already belongs to faction #{@faction}")
72
+ end
73
+
74
+ # Add <value> to the capturing faction
75
+ @infopane.add_score(by_whom - 1, self.class.value)
76
+ @faction = by_whom
99
77
  end
100
78
 
79
+ # Add <value> to the other faction and remove links to given unit
80
+ def destroy!
81
+ # If you are transporting somebody, destroy them first
82
+ @cargo.each { |uu| uu.destroy! }
83
+
84
+ @infopane.add_score(3 - @faction - 1, self.class.value) # TODO more factions?
85
+
86
+ if is_transported?
87
+ coords_unit = @map.get_unit(@x, @y)
88
+ coords_unit.cargo -= [self]
89
+ else
90
+ @map.set_unit(@x, @y, nil)
91
+ end
92
+ end
101
93
 
102
- # Processes move of unit and takes care of its (un)loading or attack
94
+ # Process move of unit and take care of its (un)loading or start of engagement
103
95
  def check_movement(old_x, old_y)
104
- if @x != old_x || @y != old_y
96
+ if @x != old_x || @y != old_y # only if it moved
105
97
  if @moves_left <= 0
106
98
  abort("unit.check_movement(): Moving unit does not have enough move points (#{@moves_left} moves)")
107
99
  end
@@ -109,65 +101,61 @@ class Unit
109
101
  oldcoords_unit = @map.get_unit(old_x, old_y)
110
102
  newcoords_unit = @map.get_unit(@x, @y)
111
103
 
112
- # If there is nobody or is there friendly unit with some cargo space
104
+ # If there is nobody or is there friendly unit with some cargo space (and bigger cargo space)
113
105
  if !newcoords_unit || \
114
106
  (newcoords_unit.faction == @faction && \
115
107
  newcoords_unit.can_transport? && \
108
+ @cargo_max < newcoords_unit.cargo_max && \
116
109
  !newcoords_unit.is_full?)
117
110
 
118
111
  # Leave old coordinates
119
- if oldcoords_unit == self
112
+ if oldcoords_unit == self
120
113
  @map.set_unit(old_x, old_y, nil)
121
114
  else # if you have been transported
122
115
  oldcoords_unit.cargo.delete(self)
123
116
  end
124
117
 
125
118
  # Get to new coordinates
126
- if newcoords_unit == nil
119
+ if !newcoords_unit
127
120
  @map.set_unit(@x, @y, self)
128
121
  @cargo.each { |uu|
129
122
  uu.x = @x
130
123
  uu.y = @y
131
124
  }
132
125
 
133
- else # if you are going to be transported
126
+ else # if you are going to be transported
134
127
  newcoords_unit.cargo.insert(-1, self) # -1 = to the end
135
128
  puts PROMPT + to_s + ' got loaded into '+ newcoords_unit.to_s
129
+ @moves_left = 1 unless newcoords_unit.can_build? # use all your left moves unless you are getting loaded into a town
136
130
  end
137
131
 
138
132
  else # if there already is somebody that can't transport you (enemy or full friend)
139
- # Stay on your original tile
140
- @x = old_x
141
- @y = old_y
142
-
143
- # If it was a friend unit
144
- if newcoords_unit.faction == @faction
145
- if !newcoords_unit.can_transport?
146
- puts PROMPT + newcoords_unit.to_s + ' can\'t transport other units'
147
- else # newcoords_unit.can_full?; has to be full then
148
- puts PROMPT + newcoords_unit.to_s + ' is already full'
149
- end
133
+ # Stay on your original tile
134
+ @x = old_x
135
+ @y = old_y
136
+
137
+ # If it was a friend unit
138
+ if newcoords_unit.faction == @faction
139
+ if !newcoords_unit.can_transport?
140
+ puts PROMPT + newcoords_unit.to_s + ' can\'t transport other units'
150
141
  else
151
- # Can it be captured?
152
- if newcoords_unit.can_be_captured?
153
- if can_capture? # then capture it if you can
154
- puts PROMPT + to_s + ' is capturing ' + newcoords_unit.to_s
155
- newcoords_unit.capture(@faction)
156
- else
157
- puts PROMPT + to_s + ' can\'t capture other units'
158
- end
159
- else # then battle it
160
- puts PROMPT + to_s + ' is battling ' + newcoords_unit.to_s
161
- newcoords_unit.destroy # TODO randomized combat
142
+ if @cargo_max >= newcoords_unit.cargo_max
143
+ puts PROMPT + "#{self.class.name} can\'t fit in #{newcoords_unit.class.name}"
144
+ else # thus newcoords_unit.is_full? is true
145
+ puts PROMPT + newcoords_unit.to_s + ' is already full'
162
146
  end
163
- end
147
+ end
148
+ else
149
+ # Enemy!
150
+ newcoords_unit.engage!(self)
151
+ end
164
152
  end
165
153
  @moves_left -= 1
166
154
 
167
155
  # Check if you are on an invalid type of terrain (unless transported)
168
- unless tile_check == true
156
+ unless is_terrain_suitable?
169
157
  puts PROMPT + to_s + " found itself in a bad place"
170
- destroy
158
+ destroy!
171
159
  end
172
160
  end
173
161
  end
@@ -198,6 +186,10 @@ class Unit
198
186
  false
199
187
  end
200
188
 
189
+ def can_be_built?
190
+ self.class.price > 0
191
+ end
192
+
201
193
  def can_transport?
202
194
  @cargo_max > 0
203
195
  end
@@ -223,7 +215,7 @@ class Unit
223
215
  end
224
216
 
225
217
  # Checks if unit is on the right type of terrain (not for transported units)
226
- def tile_check
218
+ def is_terrain_suitable?
227
219
  unless is_transported?
228
220
  case @map.tile(@x, @y).terrain
229
221
  when TILE_SEA
@@ -239,14 +231,19 @@ class Unit
239
231
  @function.func
240
232
  end
241
233
 
242
- def set_function(func)
243
- unless @faction == 0 # neutral faction doesn't need functions
244
- if (func == FUNCBUILD && !can_build?)
234
+ # Set desired function
235
+ def set_function!(func)
236
+ if @faction == 0
237
+ puts PROMPT + to_s + ": neutral towns can't have functions set"
238
+ else
239
+ if func == FUNCBUILD and !can_build?
245
240
  puts PROMPT + to_s + ": this unit can't build other units"
241
+ return
246
242
  end
247
243
 
244
+ # Check current function and set the new one
248
245
  if @function.func == func
249
- puts PROMPT + to_s + ": function has already been set to #{@function.func}"
246
+ puts PROMPT + to_s + ": function is already set to #{@function.func}"
250
247
  else
251
248
  @function.func = func
252
249
  puts PROMPT + to_s + ": function set to #{@function.func}"
@@ -255,27 +252,27 @@ class Unit
255
252
  end
256
253
 
257
254
  def function!
258
- ret = @function.func!(@x, @y, @map, @infopane)
255
+ ret = @function.func!(@map, @infopane)
259
256
  puts to_s + ret
260
257
  end
261
258
 
262
259
  # Set short info string: type, faction, coordinates
263
260
  def to_s
264
- "#{@name} (FAC#{@faction} #{@x}-#{@y})"
261
+ "#{self.class.name} (FAC#{@faction} #{@x}-#{@y})"
265
262
  end
266
263
 
267
264
  # Set long info string: short info string, armor, moves, function, cargo
268
265
  def info
269
266
  ret = to_s + ": armor #{@armor_left}/#{@armor_max}"
270
-
271
- if can_move?
267
+
268
+ if @moves_max > 0
272
269
  ret = ret + ", moves #{@moves_left}/#{@moves_max}"
273
270
  end
274
271
 
275
- ret = ret + ", func #{@function.func}"
272
+ ret = ret + ", #{@function.info}"
276
273
 
277
- if @cargo.size > 0
278
- ret = ret + ", transports #{@cargo.size}/#{@cargo_max} units"
274
+ if @cargo_max > 0
275
+ ret = ret + ", cargo #{@cargo.size}/#{@cargo_max}"
279
276
  end
280
277
 
281
278
  ret
@@ -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