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.
@@ -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