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,252 @@
|
|
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::KbK, Gosu::KB_NUMPAD_MINUS then
|
57
|
+
select_other_unit!(false)
|
58
|
+
when Gosu::KbL, Gosu::KB_NUMPAD_PLUS then
|
59
|
+
select_other_unit!(true)
|
60
|
+
when Gosu::KbReturn, Gosu::KB_NUMPAD_5 then
|
61
|
+
info
|
62
|
+
end
|
63
|
+
|
64
|
+
# If in locked mode, stay at current/jump to next movable unit
|
65
|
+
to_next_unit! unless @freeroam
|
66
|
+
end
|
67
|
+
|
68
|
+
def draw
|
69
|
+
@image.draw(@x * TILESIZE, (@y + 1) * TILESIZE, ZCURSOR)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Move to coordinates of map unit the cursor is locked to
|
73
|
+
def warp_to_locked!()
|
74
|
+
@x = @locked_unit.x
|
75
|
+
@y = @locked_unit.y
|
76
|
+
@selected_unit = @locked_unit
|
77
|
+
end
|
78
|
+
|
79
|
+
# Move by given change of coordinates
|
80
|
+
def move!(xx, yy)
|
81
|
+
if freeroam
|
82
|
+
@x += xx
|
83
|
+
@y += yy
|
84
|
+
@selected_unit = @map.get_unit(@x, @y)
|
85
|
+
# TODO show some basic tile info in infopane
|
86
|
+
else
|
87
|
+
check_unit
|
88
|
+
|
89
|
+
# Move the unit first
|
90
|
+
@locked_unit.x += xx
|
91
|
+
@locked_unit.y += yy
|
92
|
+
@locked_unit.check_movement(@x, @y) # cursor coordinates work like old_x, old_y
|
93
|
+
|
94
|
+
# Is the unit still alive?
|
95
|
+
if @locked_unit.armour_left > 0
|
96
|
+
warp_to_locked! # whether it moved or not
|
97
|
+
else
|
98
|
+
# It got destroyed so clear last links then so that (object of)
|
99
|
+
# given unit can be truly destroyed
|
100
|
+
@selected_unit = nil
|
101
|
+
@locked_unit = nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Tries to set function <func> to selected unit
|
107
|
+
def set_function_to_unit(func)
|
108
|
+
check_unit
|
109
|
+
|
110
|
+
if @selected_unit
|
111
|
+
@selected_unit.set_function!(func, @infopane.faction)
|
112
|
+
|
113
|
+
# Update infopane with the new (possibly changed) state
|
114
|
+
# (visible only in freeroam mode as in locked one the infopane is
|
115
|
+
# overwritten as cursor either jumps away or switches to freeroam mode)
|
116
|
+
@infopane.text = @selected_unit.info
|
117
|
+
else
|
118
|
+
puts "no unit to set that function to (at #{@x}-#{@y})"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Reset to locked mode
|
123
|
+
def reset!
|
124
|
+
@freeroam = true
|
125
|
+
switch_freeroam!
|
126
|
+
@selected_unit = nil
|
127
|
+
@locked_unit = nil
|
128
|
+
to_next_unit!
|
129
|
+
end
|
130
|
+
|
131
|
+
# Find next unit which is still waiting for commands and lock to it
|
132
|
+
# (selected -> last locked to -> next waiting) or switch (back) to freeroaming
|
133
|
+
def to_next_unit!
|
134
|
+
if @selected_unit and @selected_unit.is_waiting_for_commands?
|
135
|
+
# Lock to such unit (though it may have already been locked)
|
136
|
+
@locked_unit = @selected_unit
|
137
|
+
else
|
138
|
+
unless @locked_unit and @locked_unit.is_waiting_for_commands?
|
139
|
+
waiting = @map.all_units.select { |uu| uu.is_waiting_for_commands? }
|
140
|
+
|
141
|
+
# Are there still some units of active faction waiting for commands?
|
142
|
+
if waiting.size <= 0 # == would be enough
|
143
|
+
puts 'all movable units without functions moved'
|
144
|
+
switch_freeroam!
|
145
|
+
return
|
146
|
+
end
|
147
|
+
|
148
|
+
# Pick the first waiting unit (leftmost of topmosts)
|
149
|
+
@locked_unit = waiting[0]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
warp_to_locked! # stay at old or go to new
|
154
|
+
info unless @info_stopped # due to switching out of the play game state
|
155
|
+
end
|
156
|
+
|
157
|
+
# Skip or cycle units, depending on cursor mode
|
158
|
+
def select_other_unit!(search_forward)
|
159
|
+
unless @freeroam
|
160
|
+
skip_to_other_unit!(search_forward)
|
161
|
+
else
|
162
|
+
cycle_to_other_unit!(search_forward)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Skip from selected unit waiting for commands to the next/previous waiting one
|
167
|
+
def skip_to_other_unit!(search_forward = true)
|
168
|
+
if @freeroam
|
169
|
+
abort("Cursor.skip_to_other_unit!(): " \
|
170
|
+
"skipping to #{ search_forward ? 'next' : 'previous'} " \
|
171
|
+
"unit works only in locked mode, not in freeroam one")
|
172
|
+
end
|
173
|
+
|
174
|
+
waiting = @map.all_units.select { |uu| uu.is_waiting_for_commands? }
|
175
|
+
|
176
|
+
# Pick the previous/next waiting unit, relative to the current one
|
177
|
+
# (picking the previous of the first results in index -1 which is the last
|
178
|
+
# so there it is fine but taking the next of the last would go out of range)
|
179
|
+
current_index = waiting.index(@selected_unit)
|
180
|
+
other_index = search_forward ? current_index + 1 : current_index - 1
|
181
|
+
other_index = 0 if other_index >= waiting.size # == would be enough
|
182
|
+
|
183
|
+
@locked_unit = waiting[other_index]
|
184
|
+
warp_to_locked! # so that to_next_unit!() doesn't stay at @selected_unit
|
185
|
+
end
|
186
|
+
|
187
|
+
# Cycle from selected unit to the next/previous local one
|
188
|
+
def cycle_to_other_unit!(search_forward = true)
|
189
|
+
unless @freeroam
|
190
|
+
abort("Cursor.cycle_to_other_unit!():" \
|
191
|
+
"cycling to #{ search_forward ? 'next' : 'previous'} " \
|
192
|
+
"unit works only in freeroam mode, not in locked one")
|
193
|
+
end
|
194
|
+
|
195
|
+
unless @selected_unit
|
196
|
+
@infopane.text = ''
|
197
|
+
puts "no units to cycle through (at #{@x}-#{@y})"
|
198
|
+
return
|
199
|
+
end
|
200
|
+
|
201
|
+
local_pile = @map.all_units_from_tile(@x, @y)
|
202
|
+
|
203
|
+
# Pick the previous/next local unit, relative to the current one
|
204
|
+
# (picking the previous of the first results in index -1 which is the last
|
205
|
+
# so there it is fine but taking the next of the last would go out of range)
|
206
|
+
current_index = local_pile.index(@selected_unit)
|
207
|
+
other_index = search_forward ? current_index + 1 : current_index - 1
|
208
|
+
other_index = 0 if other_index >= local_pile.size # == would be enough
|
209
|
+
|
210
|
+
@selected_unit = local_pile[other_index]
|
211
|
+
info
|
212
|
+
end
|
213
|
+
|
214
|
+
# When cursor is in locked mode there needs to be unit it is locked to
|
215
|
+
# When cursor is in freeroam mode the locked unit should be kept
|
216
|
+
def check_unit
|
217
|
+
if !@freeroam and !@locked_unit
|
218
|
+
abort("Cursor.check_unit(): Cursor is in locked mode " \
|
219
|
+
"but there is no unit it is locked to (at #{@x}-#{@y})")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Switch between being attached to unit and being able to freeroam
|
224
|
+
def switch_freeroam!
|
225
|
+
if freeroam
|
226
|
+
@infopane.text = 'freeroam disabled'
|
227
|
+
puts 'freeroam disabled'
|
228
|
+
@freeroam = false
|
229
|
+
else
|
230
|
+
@infopane.text = 'freeroam enabled'
|
231
|
+
puts 'freeroam enabled'
|
232
|
+
@freeroam = true
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Find some info about units on the current tile
|
237
|
+
def info
|
238
|
+
check_unit
|
239
|
+
|
240
|
+
if @selected_unit
|
241
|
+
@infopane.text = @selected_unit.info
|
242
|
+
puts @selected_unit.info
|
243
|
+
|
244
|
+
if @selected_unit.is_transporting?
|
245
|
+
@selected_unit.cargo.each { |uu| puts '- cargo: ' + uu.info }
|
246
|
+
end
|
247
|
+
else
|
248
|
+
@infopane.text = ''
|
249
|
+
puts "no unit to show info of (at #{@x}-#{@y})"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
LINE_HEIGHT = 20
|
2
|
+
|
3
|
+
# Score, turn and event texts
|
4
|
+
class Infopane
|
5
|
+
attr_reader :faction, :turn, :score
|
6
|
+
attr_writer :cursor, :text
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@turn = 0
|
10
|
+
@faction = FACTIONS # so that FAC1 will be then the first active faction
|
11
|
+
@score = [0, 0]
|
12
|
+
@text = 'ready'
|
13
|
+
end
|
14
|
+
|
15
|
+
def update
|
16
|
+
end
|
17
|
+
|
18
|
+
def draw
|
19
|
+
freeroam_text = ""
|
20
|
+
freeroam_text = " (freeroam)" if (@cursor and @cursor.freeroam)
|
21
|
+
|
22
|
+
state = Gosu::Image.from_text(turnscore + freeroam_text, LINE_HEIGHT)
|
23
|
+
state.draw(XTEXT, YTEXT, ZTEXT)
|
24
|
+
|
25
|
+
text = Gosu::Image.from_text("#{@text}", LINE_HEIGHT)
|
26
|
+
text.draw(XTEXT, (TILESIZE / 2) + YTEXT, ZTEXT)
|
27
|
+
end
|
28
|
+
|
29
|
+
def turnscore
|
30
|
+
"Turn: #{@turn}, " \
|
31
|
+
"Faction: FAC#{@faction}, " \
|
32
|
+
"Score: #{@score[0]} - #{@score[1]}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Increment faction counter (modulo count of factions)
|
36
|
+
def next_faction!
|
37
|
+
@faction += 1
|
38
|
+
@faction = 1 if @faction > FACTIONS # marks the end of turn
|
39
|
+
end
|
40
|
+
|
41
|
+
# Increment turn counter
|
42
|
+
def next_turn!
|
43
|
+
@turn += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_score(to_whom, how_much)
|
47
|
+
@score[to_whom] += how_much
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require_relative './tile'
|
2
|
+
|
3
|
+
require_relative './../units/army'
|
4
|
+
require_relative './../units/ship'
|
5
|
+
require_relative './../units/town'
|
6
|
+
|
7
|
+
MAX_CARGO_LEVEL = 10
|
8
|
+
|
9
|
+
class Map
|
10
|
+
attr_accessor :name, :mapx, :mapy, :infopane
|
11
|
+
|
12
|
+
def initialize(file, infopane)
|
13
|
+
dir_path = File.dirname(__FILE__)
|
14
|
+
|
15
|
+
@infopane = infopane
|
16
|
+
@known_unit_types = Hash[
|
17
|
+
[Army, Ship, Town].collect { |ii| [ii.map_symbol, ii] }
|
18
|
+
]
|
19
|
+
|
20
|
+
load_map!(dir_path + "/../../save/#{file}.esf")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Load map from file
|
24
|
+
def load_map!(file_path)
|
25
|
+
unless File.file?(file_path)
|
26
|
+
abort("Map.load_map!(): File not found (#{file_path})")
|
27
|
+
end
|
28
|
+
|
29
|
+
input = File.open(file_path, 'r')
|
30
|
+
unit_count = load_head!(input.gets)
|
31
|
+
|
32
|
+
# Load tiles
|
33
|
+
@tiles = []
|
34
|
+
0.upto(@mapy - 1) { |rr|
|
35
|
+
@tiles[rr] = []
|
36
|
+
map_row = input.gets
|
37
|
+
0.upto(@mapx - 1) { |cc|
|
38
|
+
@tiles[rr][cc] = Tile.new(cc, rr, map_row[cc], @infopane)
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
# Load units
|
43
|
+
unit_count.times { load_unit!(input.gets) }
|
44
|
+
|
45
|
+
puts("Save loaded: #{@name} with #{unit_count} units")
|
46
|
+
input.close
|
47
|
+
end
|
48
|
+
|
49
|
+
# Load core info from given head row of file
|
50
|
+
# Return number of units to be loaded
|
51
|
+
def load_head!(row)
|
52
|
+
head = []
|
53
|
+
size = []
|
54
|
+
|
55
|
+
head = row.split(' ')
|
56
|
+
size = head[1].split('x')
|
57
|
+
|
58
|
+
@name = head[0]
|
59
|
+
@mapx = size[0].to_i
|
60
|
+
@mapy = size[1].to_i
|
61
|
+
|
62
|
+
head[2].to_i # unit_count
|
63
|
+
end
|
64
|
+
|
65
|
+
# Load one unit from given row
|
66
|
+
def load_unit!(row)
|
67
|
+
unit = []
|
68
|
+
coords = []
|
69
|
+
|
70
|
+
unit = row.split(' ')
|
71
|
+
|
72
|
+
# Check coordinates
|
73
|
+
coords = unit[2].split('-')
|
74
|
+
coords_x = coords[0].to_i
|
75
|
+
coords_y = coords[1].to_i
|
76
|
+
if (coords_x < 0 || coords_x >= @mapx ||
|
77
|
+
coords_y < 0 || coords_y >= @mapy)
|
78
|
+
abort("Map.load_unit!(): Unit out of map borders (#{coords_x}-#{coords_y})")
|
79
|
+
end
|
80
|
+
|
81
|
+
# Check faction
|
82
|
+
fac = unit[1].to_i
|
83
|
+
if(fac < 0 || fac > FACTIONS)
|
84
|
+
abort("Map.load_unit!(): Bad faction id (#{fac})")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Create unit
|
88
|
+
unit_type = unit[0]
|
89
|
+
if @known_unit_types.include?(unit_type)
|
90
|
+
@known_unit_types[unit_type].new(coords_x, coords_y, fac, self, @infopane)
|
91
|
+
else
|
92
|
+
abort("Map.load_unit!(): Unknown unit type symbol (#{unit_type})")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Draw all tiles
|
97
|
+
def draw_tiles
|
98
|
+
all_tiles.each { |tt| tt.draw}
|
99
|
+
end
|
100
|
+
|
101
|
+
# Getter for tile at given coordinates
|
102
|
+
def tile(cc, rr)
|
103
|
+
@tiles[rr][cc]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Return all tiles
|
107
|
+
def all_tiles
|
108
|
+
ret = []
|
109
|
+
ii = 0
|
110
|
+
0.upto(MAPY) { |rr|
|
111
|
+
0.upto(MAPX) { |cc|
|
112
|
+
ret[ii] = @tiles[rr][cc]
|
113
|
+
ii += 1
|
114
|
+
}
|
115
|
+
}
|
116
|
+
ret
|
117
|
+
end
|
118
|
+
|
119
|
+
# Getter for unit at given coordinates
|
120
|
+
def get_unit(cc, rr)
|
121
|
+
@tiles[rr][cc].unit
|
122
|
+
end
|
123
|
+
|
124
|
+
# Setter for unit at given coordinates
|
125
|
+
def set_unit(cc, rr, uu)
|
126
|
+
@tiles[rr][cc].unit = uu
|
127
|
+
end
|
128
|
+
|
129
|
+
# Return all units with cargo level in given range
|
130
|
+
# CL0 = map units, CL1 = units in map units, CL2 = units in CL1 units...
|
131
|
+
# all_units() -> all units, all_units(0, 0) -> only map units
|
132
|
+
def all_units(min_cargo_level = 0, max_cargo_level = MAX_CARGO_LEVEL)
|
133
|
+
ret = []
|
134
|
+
|
135
|
+
unless min_cargo_level.between?(0, MAX_CARGO_LEVEL) and
|
136
|
+
max_cargo_level.between?(0, MAX_CARGO_LEVEL)
|
137
|
+
abort("Map.all_units(): Desired cargo levels need to be " \
|
138
|
+
"from 0 to #{MAX_CARGO_LEVEL}")
|
139
|
+
end
|
140
|
+
unless min_cargo_level <= max_cargo_level
|
141
|
+
abort("Map.all_units(): Min cargo level (#{min_cargo_level}) is higher " \
|
142
|
+
"than max cargo level (#{max_cargo_level})")
|
143
|
+
end
|
144
|
+
|
145
|
+
0.upto(MAPY) { |rr|
|
146
|
+
0.upto(MAPX) { |cc|
|
147
|
+
ret += all_units_from_tile(cc, rr)
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
ret
|
152
|
+
end
|
153
|
+
|
154
|
+
# Return all units from given tile with cargo level in given range
|
155
|
+
def all_units_from_tile(cc, rr,
|
156
|
+
min_cargo_level = 0,
|
157
|
+
max_cargo_level = MAX_CARGO_LEVEL)
|
158
|
+
ret = []
|
159
|
+
|
160
|
+
uu = get_unit(cc, rr)
|
161
|
+
if uu
|
162
|
+
ret += add_unit_to_list(uu, 0, min_cargo_level, max_cargo_level)
|
163
|
+
end
|
164
|
+
|
165
|
+
ret
|
166
|
+
end
|
167
|
+
|
168
|
+
# Recursively add units to the list
|
169
|
+
def add_unit_to_list(unit, cargo_level = 0,
|
170
|
+
min_cargo_level = 0,
|
171
|
+
max_cargo_level = MAX_CARGO_LEVEL)
|
172
|
+
ret = []
|
173
|
+
|
174
|
+
# This unit
|
175
|
+
if cargo_level >= min_cargo_level
|
176
|
+
ret.append(unit)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Transported units
|
180
|
+
cargo_level += 1
|
181
|
+
if cargo_level <= max_cargo_level
|
182
|
+
unit.cargo.each { |uu|
|
183
|
+
ret += add_unit_to_list(uu, cargo_level, min_cargo_level, max_cargo_level)
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
ret
|
188
|
+
end
|
189
|
+
end
|