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.
- checksums.yaml +4 -4
- data/lib/empi.rb +8 -5
- data/lib/lib/game_states/build_state.rb +68 -0
- data/lib/{game_state.rb → lib/game_states/game_state.rb} +0 -0
- data/lib/lib/game_states/play_state.rb +149 -0
- data/lib/{escape_state.rb → lib/game_states/quit_state.rb} +4 -5
- data/lib/lib/game_states/set_state.rb +75 -0
- data/lib/lib/game_states/welcome_state.rb +85 -0
- data/lib/lib/game_states/win_state.rb +61 -0
- data/lib/{army.rb → lib/units/army.rb} +2 -2
- data/lib/{ship.rb → lib/units/ship.rb} +2 -2
- data/lib/{town.rb → lib/units/town.rb} +31 -23
- data/lib/{unit.rb → lib/units/unit.rb} +73 -47
- data/lib/{unitFunction.rb → lib/units/unit_function.rb} +13 -4
- data/lib/lib/user_interface/cursor.rb +252 -0
- data/lib/lib/user_interface/infopane.rb +49 -0
- data/lib/lib/user_interface/map.rb +189 -0
- data/lib/lib/user_interface/tile.rb +56 -0
- metadata +23 -19
- data/lib/build_state.rb +0 -54
- data/lib/cursor.rb +0 -167
- data/lib/infopane.rb +0 -38
- data/lib/map.rb +0 -153
- data/lib/play_state.rb +0 -98
- data/lib/tile.rb +0 -36
@@ -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 + '
|
12
|
+
@image = Gosu::Image.new(dir_path + '/../../media/army.png')
|
13
13
|
|
14
|
-
@
|
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 + '
|
12
|
+
@image = Gosu::Image.new(dir_path + '/../../media/ship.png')
|
13
13
|
|
14
|
-
@
|
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 + '
|
18
|
+
@image = Gosu::Image.new(dir_path + '/../../media/town.png')
|
19
19
|
|
20
|
-
@
|
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
|
-
@
|
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
|
60
|
-
# Set
|
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
|
-
|
74
|
-
|
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 =
|
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
|
-
|
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
|
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 './
|
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
|
-
|
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
|
-
@
|
19
|
-
@
|
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("
|
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
|
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("
|
68
|
+
abort("Unit.capture!(): This unit can\'t be captured")
|
68
69
|
end
|
69
70
|
|
70
|
-
if by_whom == @faction
|
71
|
-
abort("
|
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("
|
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
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
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,
|
282
|
+
# Set long info string: short info string, armour, moves, function, cargo
|
257
283
|
def info
|
258
|
-
ret = to_s + ":
|
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
|