empi 0.17 → 0.22.3
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.
- checksums.yaml +7 -0
- data/lib/empi.rb +22 -108
- data/lib/lib/game_states/build_state.rb +82 -0
- data/lib/lib/game_states/escape_state.rb +34 -0
- data/lib/lib/game_states/game_state.rb +27 -0
- data/lib/lib/game_states/play_state.rb +100 -0
- data/lib/{army.rb → lib/units/army.rb} +6 -3
- data/lib/{ship.rb → lib/units/ship.rb} +6 -3
- data/lib/lib/units/town.rb +106 -0
- data/lib/{unit.rb → lib/units/unit.rb} +103 -106
- data/lib/lib/units/unitFunction.rb +63 -0
- data/lib/lib/user_interface/cursor.rb +168 -0
- data/lib/{infopane.rb → lib/user_interface/infopane.rb} +7 -5
- data/lib/{map.rb → lib/user_interface/map.rb} +32 -37
- data/lib/{tile.rb → lib/user_interface/tile.rb} +7 -4
- data/lib/media/Empi v18.png +0 -0
- data/lib/save/m02-err.esf +26 -0
- metadata +40 -37
- data/lib/cursor.rb +0 -103
- data/lib/docu/Empi v14.png +0 -0
- data/lib/docu/info.txt +0 -219
- data/lib/town.rb +0 -28
- data/lib/unitFunction.rb +0 -47
@@ -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
|
-
#
|
3
|
+
# Both capturable and movable game pieces
|
4
4
|
class Unit
|
5
|
-
|
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
|
-
#
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
#
|
49
|
-
if
|
50
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
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
|
-
#
|
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
|
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
|
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
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
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
|
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
|
-
|
243
|
-
|
244
|
-
|
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
|
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!(@
|
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
|
-
"#{
|
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
|
267
|
+
|
268
|
+
if @moves_max > 0
|
272
269
|
ret = ret + ", moves #{@moves_left}/#{@moves_max}"
|
273
270
|
end
|
274
271
|
|
275
|
-
ret = ret + ",
|
272
|
+
ret = ret + ", #{@function.info}"
|
276
273
|
|
277
|
-
if @
|
278
|
-
ret = ret + ",
|
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
|