smagacor 0.0.1 → 0.0.2
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.
- data/ChangeLog +62 -0
- data/Rakefile +86 -71
- data/TODO +19 -2
- data/VERSION +1 -1
- data/data/smagacor/smagacor-logo.png +0 -0
- data/data/smagacor/sokoban/crate.png +0 -0
- data/data/smagacor/sokoban/floor.png +0 -0
- data/data/smagacor/sokoban/game.info +7 -0
- data/data/smagacor/sokoban/levels/loma.lvl +288 -0
- data/data/smagacor/sokoban/levels/mas_microban.lvl +1812 -0
- data/data/smagacor/sokoban/levels/mas_sasquatch.lvl +891 -0
- data/data/smagacor/sokoban/levels/microban.lvl +1834 -0
- data/data/smagacor/sokoban/levels/sasquatch.lvl +846 -0
- data/data/smagacor/sokoban/levels/sasquatch_III.lvl +903 -0
- data/data/smagacor/sokoban/levels/sasquatch_IV.lvl +812 -0
- data/data/smagacor/sokoban/levels/sasquatch_V.lvl +868 -0
- data/data/smagacor/sokoban/levels/sasquatch_VI.lvl +902 -0
- data/data/smagacor/sokoban/levels/sasquatch_VII.lvl +898 -0
- data/data/smagacor/sokoban/man.png +0 -0
- data/data/smagacor/sokoban/sokoban.png +0 -0
- data/data/smagacor/sokoban/sokobanui.rb +597 -0
- data/data/smagacor/sokoban/storage.png +0 -0
- data/data/smagacor/sokoban/wall.png +0 -0
- data/data/smagacor/tictactoe/game.info +1 -1
- data/data/smagacor/tictactoe/tictactoe.png +0 -0
- data/data/smagacor/tictactoe/tictactoe.rb +295 -206
- data/data/smagacor/tictactoe/x.png +0 -0
- data/lib/smagacor/listener.rb +50 -0
- data/lib/smagacor/smagacor-ui.rb +208 -127
- data/lib/smagacor/{controller.rb → util.rb} +106 -7
- metadata +68 -45
- data/install.rb +0 -21
Binary file
|
Binary file
|
@@ -0,0 +1,597 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
#
|
4
|
+
# $Id: sokobanui.rb 363 2005-11-26 18:16:30Z thomas $
|
5
|
+
#
|
6
|
+
# smagacor - a collection of small games in ruby
|
7
|
+
# Copyright (C) 2004 Thomas Leitner
|
8
|
+
#
|
9
|
+
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU
|
10
|
+
# General Public License as published by the Free Software Foundation; either version 2 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
14
|
+
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along with this program; if not,
|
18
|
+
# write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
|
+
#
|
20
|
+
#++
|
21
|
+
#
|
22
|
+
require 'fileutils'
|
23
|
+
require 'smagacor/listener'
|
24
|
+
|
25
|
+
module Smagacor::Sokoban
|
26
|
+
|
27
|
+
Position = Struct.new( :x, :y )
|
28
|
+
CellChange = Struct.new( :pos, :old_obj, :new_obj )
|
29
|
+
LevelChange = Struct.new( :old_cell, :new_cell )
|
30
|
+
|
31
|
+
class Map
|
32
|
+
|
33
|
+
Man = ?@
|
34
|
+
Crate = ?$
|
35
|
+
Wall = ?#
|
36
|
+
Storage = ?.
|
37
|
+
Floor = ?\s
|
38
|
+
Empty = ?-
|
39
|
+
ManOnStorage = ?+
|
40
|
+
CrateOnStorage = ?*
|
41
|
+
|
42
|
+
include Enumerable
|
43
|
+
|
44
|
+
attr_reader :width
|
45
|
+
attr_reader :height
|
46
|
+
attr_reader :name
|
47
|
+
|
48
|
+
def initialize( str_map, name = '' )
|
49
|
+
@name = name
|
50
|
+
create_map( str_map )
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_pos( pos, item )
|
54
|
+
@map[pos.y][pos.x] = item
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_pos( pos )
|
58
|
+
@map[pos.y][pos.x]
|
59
|
+
end
|
60
|
+
|
61
|
+
def each
|
62
|
+
@map.each {|row| row.each {|field| yield field } }
|
63
|
+
end
|
64
|
+
|
65
|
+
def each_row
|
66
|
+
@map.each {|row| yield row}
|
67
|
+
end
|
68
|
+
|
69
|
+
def each_with_pos
|
70
|
+
@map.each_with_index do |row, y|
|
71
|
+
row.each_with_index do |cell, x|
|
72
|
+
yield cell, Position.new( x, y )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#######
|
78
|
+
private
|
79
|
+
#######
|
80
|
+
|
81
|
+
def create_map( str_map )
|
82
|
+
@map = str_map.split( /\n/ ).collect { |row| row.unpack( 'C*' ) }
|
83
|
+
@width = @map.max {|a,b| a.length <=> b.length}.length
|
84
|
+
@height = @map.length
|
85
|
+
|
86
|
+
# remove false floor tiles
|
87
|
+
@map.each {|row| row.each_with_index {|c,i| break if c == Wall; row[i] = Empty } }
|
88
|
+
rows = (0..(@height - 1)).to_a
|
89
|
+
(0..(@width - 1)).each do |col|
|
90
|
+
rows.each { |row| break if @map[row][col] == Wall; next if @map[row][col].nil?; @map[row][col] = Empty }
|
91
|
+
rows.reverse.each { |row| break if @map[row][col] == Wall; next if @map[row][col].nil?; @map[row][col] = Empty }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
class Level
|
99
|
+
|
100
|
+
include Listener
|
101
|
+
|
102
|
+
attr_reader :map
|
103
|
+
attr_reader :man_pos
|
104
|
+
|
105
|
+
def initialize( map, name = '' )
|
106
|
+
@original_map = Map.new( map, name )
|
107
|
+
reset
|
108
|
+
add_msg_name( :level_changed )
|
109
|
+
add_msg_name( :move_impossible )
|
110
|
+
add_msg_name( :level_finished )
|
111
|
+
end
|
112
|
+
|
113
|
+
def move( direction )
|
114
|
+
move_possible = true
|
115
|
+
|
116
|
+
newpos = Level.new_pos( @man_pos, direction )
|
117
|
+
case @map.get_pos( newpos )
|
118
|
+
when Map::Floor, Map::Storage
|
119
|
+
lc = move_man( newpos )
|
120
|
+
dispatch_msg( :level_changed, [lc] )
|
121
|
+
|
122
|
+
when Map::Wall
|
123
|
+
dispatch_msg( :move_impossible )
|
124
|
+
move_possible = false
|
125
|
+
|
126
|
+
when Map::Crate, Map::CrateOnStorage
|
127
|
+
crate_new_pos = Level.new_pos( newpos, direction )
|
128
|
+
case @map.get_pos( crate_new_pos )
|
129
|
+
when Map::Wall, Map::Crate, Map::CrateOnStorage
|
130
|
+
dispatch_msg( :move_impossible )
|
131
|
+
move_possible = false
|
132
|
+
else
|
133
|
+
lc_crate = move_crate( newpos, crate_new_pos )
|
134
|
+
lc_man = move_man( newpos )
|
135
|
+
dispatch_msg( :level_changed, [lc_man, lc_crate] )
|
136
|
+
end
|
137
|
+
end
|
138
|
+
dispatch_msg( :level_finished ) if level_finished?
|
139
|
+
return move_possible
|
140
|
+
end
|
141
|
+
|
142
|
+
def move_man( newpos )
|
143
|
+
n = CellChange.new( newpos, @map.get_pos( newpos ) )
|
144
|
+
o = CellChange.new( @man_pos, @map.get_pos( @man_pos ) )
|
145
|
+
case @map.get_pos( newpos )
|
146
|
+
when Map::Floor then @map.set_pos( newpos, Map::Man )
|
147
|
+
when Map::Storage then @map.set_pos( newpos, Map::ManOnStorage )
|
148
|
+
end
|
149
|
+
case @map.get_pos( @man_pos )
|
150
|
+
when Map::Man then @map.set_pos( @man_pos, Map::Floor )
|
151
|
+
when Map::ManOnStorage then @map.set_pos( @man_pos, Map::Storage )
|
152
|
+
end
|
153
|
+
n.new_obj = @map.get_pos( newpos )
|
154
|
+
o.new_obj = @map.get_pos( @man_pos )
|
155
|
+
|
156
|
+
@man_pos = newpos
|
157
|
+
LevelChange.new( o, n )
|
158
|
+
end
|
159
|
+
|
160
|
+
def move_crate( oldpos, newpos )
|
161
|
+
n = CellChange.new( newpos, @map.get_pos( newpos ) )
|
162
|
+
o = CellChange.new( oldpos, @map.get_pos( oldpos ) )
|
163
|
+
case @map.get_pos( newpos )
|
164
|
+
when Map::Floor then @map.set_pos( newpos, Map::Crate )
|
165
|
+
when Map::Storage then @map.set_pos( newpos, Map::CrateOnStorage )
|
166
|
+
end
|
167
|
+
case @map.get_pos( oldpos )
|
168
|
+
when Map::Crate then @map.set_pos( oldpos, Map::Floor )
|
169
|
+
when Map::CrateOnStorage then @map.set_pos( oldpos, Map::Storage )
|
170
|
+
end
|
171
|
+
n.new_obj = @map.get_pos( newpos )
|
172
|
+
o.new_obj = @map.get_pos( oldpos )
|
173
|
+
LevelChange.new( o, n )
|
174
|
+
end
|
175
|
+
|
176
|
+
def level_finished?
|
177
|
+
!( @map.any? {|item| item == Map::Storage || item == Map::ManOnStorage } )
|
178
|
+
end
|
179
|
+
|
180
|
+
def reset
|
181
|
+
@map = Marshal.load( Marshal.dump( @original_map ) )
|
182
|
+
recalc_man_pos
|
183
|
+
end
|
184
|
+
|
185
|
+
def recalc_man_pos
|
186
|
+
@man_pos = Level.find_man( @map )
|
187
|
+
end
|
188
|
+
|
189
|
+
def Level.find_man( map )
|
190
|
+
map.each_with_pos {|cell, pos| return pos if cell == Map::Man || cell == Map::ManOnStorage }
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def Level.new_pos( pos, direction )
|
195
|
+
case direction
|
196
|
+
when :left then Position.new( pos.x - 1, pos.y )
|
197
|
+
when :right then Position.new( pos.x + 1, pos.y )
|
198
|
+
when :up then Position.new( pos.x, pos.y - 1 )
|
199
|
+
when :down then Position.new( pos.x, pos.y + 1)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
class LevelCollection < Array
|
207
|
+
|
208
|
+
attr_reader :name
|
209
|
+
|
210
|
+
def initialize( data, name )
|
211
|
+
super()
|
212
|
+
@name = name
|
213
|
+
leveldata = data.split( /\n;.*\n('.*?'\n)?\n/ )
|
214
|
+
i = 0
|
215
|
+
while i < leveldata.length
|
216
|
+
if !leveldata[i].empty?
|
217
|
+
name = if leveldata[i][0] == ?'
|
218
|
+
i += 1
|
219
|
+
leveldata[i-1][1..-3]
|
220
|
+
else
|
221
|
+
''
|
222
|
+
end
|
223
|
+
self << Level.new( leveldata[i], name )
|
224
|
+
end
|
225
|
+
i += 1
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
class Sokoban
|
233
|
+
|
234
|
+
include Listener
|
235
|
+
include Enumerable
|
236
|
+
|
237
|
+
attr_reader :collections
|
238
|
+
|
239
|
+
def initialize
|
240
|
+
add_msg_name( :level_selected )
|
241
|
+
@collection_index = -1
|
242
|
+
@level_index = -1
|
243
|
+
@collections = []
|
244
|
+
end
|
245
|
+
|
246
|
+
def load_level_collection( data, name )
|
247
|
+
@collections << LevelCollection.new( data, name )
|
248
|
+
end
|
249
|
+
|
250
|
+
def cur_level
|
251
|
+
@collections[@collection_index][@level_index] if @collection_index >= 0
|
252
|
+
end
|
253
|
+
|
254
|
+
def each
|
255
|
+
@collections.each {|c| c.each {|l| yield( c, l ) } }
|
256
|
+
end
|
257
|
+
|
258
|
+
def each_with_index
|
259
|
+
@collections.each_with_index {|c, ci| c.each_with_index {|l, li| yield( c, ci, l, li ) } }
|
260
|
+
end
|
261
|
+
|
262
|
+
def select_collection( index )
|
263
|
+
init_level( 0, index )
|
264
|
+
end
|
265
|
+
|
266
|
+
def next_collection
|
267
|
+
init_level( 0, @collection_index + 1 )
|
268
|
+
end
|
269
|
+
|
270
|
+
def prev_collection
|
271
|
+
init_level( 0, @collection_index - 1 )
|
272
|
+
end
|
273
|
+
|
274
|
+
def select_level( index, colindex = @collection_index )
|
275
|
+
init_level( index, colindex )
|
276
|
+
end
|
277
|
+
|
278
|
+
def next_level
|
279
|
+
init_level( @level_index + 1 )
|
280
|
+
end
|
281
|
+
|
282
|
+
def prev_level
|
283
|
+
init_level( @level_index - 1 )
|
284
|
+
end
|
285
|
+
|
286
|
+
#######
|
287
|
+
private
|
288
|
+
#######
|
289
|
+
|
290
|
+
def init_level( levelindex, colindex = @collection_index )
|
291
|
+
oldcolindex = @collection_index
|
292
|
+
oldlevelindex = @level_index
|
293
|
+
@collection_index = colindex % @collections.length
|
294
|
+
@level_index = levelindex % @collections[@collection_index].length
|
295
|
+
cur_level.reset
|
296
|
+
dispatch_msg( :level_selected, (oldcolindex == -1 ? nil : @collections[oldcolindex][oldlevelindex]), cur_level )
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
class SokobanCanvas < Qt::Widget
|
303
|
+
|
304
|
+
def initialize( parent, sokoban )
|
305
|
+
super( parent )
|
306
|
+
@sokoban = sokoban
|
307
|
+
@sokoban.add_msg_listener( :level_selected, method(:on_level_selected) )
|
308
|
+
setBackgroundMode( Qt::NoBackground )
|
309
|
+
setFocusPolicy( Qt::Widget::StrongFocus )
|
310
|
+
setFocus
|
311
|
+
|
312
|
+
@wall = @swall = Qt::Image.new( File.join( File.dirname( __FILE__ ), 'wall.png' ) )
|
313
|
+
@man = @sman = Qt::Image.new( File.join( File.dirname( __FILE__ ), 'man.png' ) )
|
314
|
+
@storage = @sstorage = Qt::Image.new( File.join( File.dirname( __FILE__ ), 'storage.png' ) )
|
315
|
+
@crate = @scrate = Qt::Image.new( File.join( File.dirname( __FILE__ ), 'crate.png' ) )
|
316
|
+
@floor = @sfloor = Qt::Image.new( File.join( File.dirname( __FILE__ ), 'floor.png' ) )
|
317
|
+
|
318
|
+
@color_bg = Qt::Brush.new( Qt::white )
|
319
|
+
end
|
320
|
+
|
321
|
+
def on_level_selected( oldlevel, curlevel )
|
322
|
+
oldlevel.del_msg_listener( :level_changed, method(:on_level_changed) ) if oldlevel
|
323
|
+
curlevel.add_msg_listener( :level_changed, method(:on_level_changed) )
|
324
|
+
resizeEvent( Qt::ResizeEvent.new( self.size, self.size ) )
|
325
|
+
update
|
326
|
+
end
|
327
|
+
|
328
|
+
def on_level_changed( changes )
|
329
|
+
p = Qt::Painter.new( self )
|
330
|
+
xoff, yoff = offset( @sokoban.cur_level.map )
|
331
|
+
changes.each do |lc|
|
332
|
+
draw_item( p, @sokoban.cur_level.map.get_pos( lc.old_cell.pos ), xoff + lc.old_cell.pos.x * @tile_size, yoff + lc.old_cell.pos.y * @tile_size )
|
333
|
+
draw_item( p, @sokoban.cur_level.map.get_pos( lc.new_cell.pos ), xoff + lc.new_cell.pos.x * @tile_size, yoff + lc.new_cell.pos.y * @tile_size )
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def draw_man( painter, x, y, tile_size = @tile_size )
|
338
|
+
@sman = @man.smoothScale( tile_size, tile_size ) if @sman.width != tile_size
|
339
|
+
painter.drawImage( x, y, @sman )
|
340
|
+
end
|
341
|
+
|
342
|
+
def draw_crate( painter, x, y, tile_size = @tile_size )
|
343
|
+
@scrate = @crate.smoothScale( tile_size, tile_size ) if @scrate.width != tile_size
|
344
|
+
painter.drawImage( x, y, @scrate )
|
345
|
+
end
|
346
|
+
|
347
|
+
def draw_storage( painter, x, y, tile_size = @tile_size )
|
348
|
+
@sstorage = @storage.smoothScale( tile_size, tile_size ) if @sstorage.width != tile_size
|
349
|
+
painter.drawImage( x, y, @sstorage )
|
350
|
+
end
|
351
|
+
|
352
|
+
def draw_wall( painter, x, y, tile_size = @tile_size )
|
353
|
+
@swall = @wall.smoothScale( tile_size, tile_size ) if @swall.width != tile_size
|
354
|
+
painter.drawImage( x, y, @swall )
|
355
|
+
end
|
356
|
+
|
357
|
+
def draw_floor( painter, x, y, tile_size = @tile_size )
|
358
|
+
@sfloor = @floor.smoothScale( tile_size, tile_size ) if @sfloor.width != tile_size
|
359
|
+
painter.drawImage( x, y, @sfloor )
|
360
|
+
end
|
361
|
+
|
362
|
+
def draw_map( painter, map, w = self.width, h = self.height, tile_size = @tile_size )
|
363
|
+
painter.fillRect( 0, 0, w, h, @color_bg )
|
364
|
+
xoff, yoff = offset( map, w, h, tile_size )
|
365
|
+
y = yoff
|
366
|
+
map.each_row do |row|
|
367
|
+
x = xoff
|
368
|
+
row.each do |item|
|
369
|
+
draw_item( painter, item, x, y, tile_size )
|
370
|
+
x += tile_size
|
371
|
+
end
|
372
|
+
y += tile_size
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def draw_item( painter, item, x, y, tile_size = @tile_size)
|
377
|
+
case item
|
378
|
+
when Map::Wall
|
379
|
+
draw_wall( painter, x, y, tile_size )
|
380
|
+
when Map::Storage
|
381
|
+
draw_floor( painter, x, y, tile_size )
|
382
|
+
draw_storage( painter, x, y, tile_size )
|
383
|
+
when Map::Crate
|
384
|
+
draw_floor( painter, x, y, tile_size )
|
385
|
+
draw_crate( painter, x, y, tile_size )
|
386
|
+
when Map::Man
|
387
|
+
draw_floor( painter, x, y, tile_size )
|
388
|
+
draw_man( painter, x, y, tile_size )
|
389
|
+
when Map::CrateOnStorage
|
390
|
+
draw_floor( painter, x, y, tile_size )
|
391
|
+
draw_storage( painter, x, y, tile_size )
|
392
|
+
draw_crate( painter, x, y, tile_size )
|
393
|
+
when Map::ManOnStorage
|
394
|
+
draw_floor( painter, x, y, tile_size )
|
395
|
+
draw_storage( painter, x, y, tile_size )
|
396
|
+
draw_man( painter, x, y, tile_size )
|
397
|
+
when Map::Floor
|
398
|
+
draw_floor( painter, x, y, tile_size )
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def draw_all( painter, map, w, h )
|
403
|
+
draw_map( painter, map, w, h, tile_size( w, h, map ) )
|
404
|
+
end
|
405
|
+
|
406
|
+
def tile_size( w, h, map )
|
407
|
+
dx = w / map.width
|
408
|
+
dy = h / map.height
|
409
|
+
( dx > dy ? dy : dx )
|
410
|
+
end
|
411
|
+
|
412
|
+
def offset( map, w = self.width, h = self.height, tile_size = @tile_size )
|
413
|
+
[(w - map.width * tile_size) / 2, (h - map.height * tile_size) / 2]
|
414
|
+
end
|
415
|
+
|
416
|
+
def paintEvent( event )
|
417
|
+
pix = Qt::Pixmap.new( width, height )
|
418
|
+
p = Qt::Painter.new
|
419
|
+
p.begin( pix )
|
420
|
+
draw_map( p, @sokoban.cur_level.map ) if @sokoban && @sokoban.cur_level
|
421
|
+
p.end
|
422
|
+
|
423
|
+
painter = Qt::Painter.new( self )
|
424
|
+
painter.drawPixmap( 0, 0, pix )
|
425
|
+
end
|
426
|
+
|
427
|
+
def resizeEvent( event )
|
428
|
+
@tile_size = tile_size( self.width, self.height, @sokoban.cur_level.map ) if @sokoban && @sokoban.cur_level
|
429
|
+
end
|
430
|
+
|
431
|
+
def keyPressEvent( event )
|
432
|
+
case event.key
|
433
|
+
when Qt::Key_Left then @sokoban.cur_level.move( :left )
|
434
|
+
when Qt::Key_Right then @sokoban.cur_level.move( :right )
|
435
|
+
when Qt::Key_Up then @sokoban.cur_level.move( :up )
|
436
|
+
when Qt::Key_Down then @sokoban.cur_level.move( :down )
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
end
|
441
|
+
|
442
|
+
|
443
|
+
class SokobanUI < Qt::Widget
|
444
|
+
|
445
|
+
slots 'next_level()', 'prev_level()', 'select_level(int)', 'reset_level()'
|
446
|
+
slots 'next_collection()', 'prev_collection()'
|
447
|
+
|
448
|
+
attr_reader :canvas
|
449
|
+
attr_reader :sokoban
|
450
|
+
|
451
|
+
def initialize( parent )
|
452
|
+
super( parent )
|
453
|
+
@sokoban = Sokoban.new
|
454
|
+
Dir[File.join( File.dirname( __FILE__ ), 'levels', '*.lvl')].sort.each do |file|
|
455
|
+
@sokoban.load_level_collection( File.read( file ), File.basename( file, '.*').capitalize.tr( '_', ' ') )
|
456
|
+
end
|
457
|
+
@sokoban.add_msg_listener( :level_selected, method(:on_level_selected) )
|
458
|
+
@canvas = SokobanCanvas.new( self, @sokoban )
|
459
|
+
@sokoban.select_level( 0, 0 )
|
460
|
+
|
461
|
+
layout = Qt::HBoxLayout.new( self )
|
462
|
+
layout.addWidget( @canvas )
|
463
|
+
end
|
464
|
+
|
465
|
+
def next_collection
|
466
|
+
@sokoban.next_collection
|
467
|
+
end
|
468
|
+
|
469
|
+
def prev_collection
|
470
|
+
@sokoban.prev_collection
|
471
|
+
end
|
472
|
+
|
473
|
+
def next_level
|
474
|
+
@sokoban.next_level
|
475
|
+
end
|
476
|
+
|
477
|
+
def prev_level
|
478
|
+
@sokoban.prev_level
|
479
|
+
end
|
480
|
+
|
481
|
+
def select_level( calc_index )
|
482
|
+
@sokoban.select_level( calc_index % 1000, calc_index / 1000 )
|
483
|
+
end
|
484
|
+
|
485
|
+
def reset_level
|
486
|
+
@sokoban.cur_level.reset
|
487
|
+
@canvas.update
|
488
|
+
end
|
489
|
+
|
490
|
+
def undo( info )
|
491
|
+
info.each do |lc|
|
492
|
+
@sokoban.cur_level.map.set_pos( lc.old_cell.pos, lc.old_cell.old_obj )
|
493
|
+
@sokoban.cur_level.map.set_pos( lc.new_cell.pos, lc.new_cell.old_obj )
|
494
|
+
end
|
495
|
+
@sokoban.cur_level.recalc_man_pos
|
496
|
+
@canvas.update
|
497
|
+
end
|
498
|
+
|
499
|
+
def redo( info )
|
500
|
+
info.reverse.each do |lc|
|
501
|
+
@sokoban.cur_level.map.set_pos( lc.old_cell.pos, lc.old_cell.new_obj )
|
502
|
+
@sokoban.cur_level.map.set_pos( lc.new_cell.pos, lc.new_cell.new_obj )
|
503
|
+
end
|
504
|
+
@sokoban.cur_level.recalc_man_pos
|
505
|
+
@canvas.update
|
506
|
+
end
|
507
|
+
|
508
|
+
def set_undo_manager( manager )
|
509
|
+
@manager.del_msg_listener( :undo, method(:undo) ) if @manager
|
510
|
+
@manager.del_msg_listener( :redo, method(:redo) ) if @manager
|
511
|
+
@manager = manager
|
512
|
+
@manager.add_msg_listener( :undo, method(:undo) )
|
513
|
+
@manager.add_msg_listener( :redo, method(:redo) )
|
514
|
+
end
|
515
|
+
|
516
|
+
def on_level_selected( oldlevel, curlevel )
|
517
|
+
oldlevel.del_msg_listener( :level_changed, method(:on_level_changed) ) if oldlevel
|
518
|
+
curlevel.add_msg_listener( :level_changed, method(:on_level_changed) )
|
519
|
+
@manager.clear if @manager
|
520
|
+
end
|
521
|
+
|
522
|
+
def on_level_changed( changes )
|
523
|
+
@manager.add_undo_info( changes )
|
524
|
+
end
|
525
|
+
|
526
|
+
end
|
527
|
+
|
528
|
+
|
529
|
+
class SokobanGame < Smagacor::GamePlugin
|
530
|
+
|
531
|
+
def initialize( parent, gameinfo )
|
532
|
+
@parent = parent
|
533
|
+
@widget = SokobanUI.new( parent )
|
534
|
+
Qt::MessageBox.information( parent, "Credits", "All included Sokoban levels have been made by David W. Skinner (sasquatch@bentonrea.com)" )
|
535
|
+
end
|
536
|
+
|
537
|
+
def game_widget
|
538
|
+
@widget
|
539
|
+
end
|
540
|
+
|
541
|
+
def set_undo_manager( manager )
|
542
|
+
@widget.set_undo_manager( manager )
|
543
|
+
end
|
544
|
+
|
545
|
+
def menu
|
546
|
+
colmenu = Qt::PopupMenu.new( @parent )
|
547
|
+
oldcollection = nil
|
548
|
+
menu = nil
|
549
|
+
@widget.sokoban.each_with_index do |collection, col_index, level, level_index|
|
550
|
+
if oldcollection != collection
|
551
|
+
menu = Qt::PopupMenu.new( @parent )
|
552
|
+
colmenu.insertItem( collection.name, menu )
|
553
|
+
oldcollection = collection
|
554
|
+
end
|
555
|
+
|
556
|
+
icon = level_pix( collection, level, level_index )
|
557
|
+
item = menu.insertItem( icon, @widget, SLOT('select_level(int)') ) #"Level #{level.map.name} (#{level_index+1})"
|
558
|
+
#TODO use small icon + text in menu item, big icon in whatsthis text
|
559
|
+
menu.setWhatsThis( item, "Hallo #{level_index}")
|
560
|
+
menu.setItemParameter( item, col_index * 1000 + level_index )
|
561
|
+
end
|
562
|
+
|
563
|
+
levelmenu = Qt::PopupMenu.new( @parent )
|
564
|
+
levelmenu.insertItem( "Select level", colmenu )
|
565
|
+
levelmenu.insertItem( "Reset level", @widget, SLOT('reset_level()'), Qt::KeySequence.new( Qt::Key_Escape ) )
|
566
|
+
levelmenu.insertItem( "Next collection", @widget, SLOT('next_collection()') )
|
567
|
+
levelmenu.insertItem( "Previous collection", @widget, SLOT('prev_collection()') )
|
568
|
+
levelmenu.insertItem( "Next level in collection", @widget, SLOT('next_level()'), Qt::KeySequence.new( Qt::Key_N ) )
|
569
|
+
levelmenu.insertItem( "Previous level in collection", @widget, SLOT('prev_level()'), Qt::KeySequence.new( Qt::Key_P ) )
|
570
|
+
levelmenu
|
571
|
+
end
|
572
|
+
|
573
|
+
#######
|
574
|
+
private
|
575
|
+
#######
|
576
|
+
|
577
|
+
def level_pix( collection, level, level_index )
|
578
|
+
cachefile = File.join( Smagacor::Config.homepath, 'sokoban', 'level_icon_cache', collection.name + level_index.to_s )
|
579
|
+
if File.exists?( cachefile )
|
580
|
+
Qt::Pixmap.new( cachefile )
|
581
|
+
else
|
582
|
+
tilesize = @widget.canvas.tile_size( 50, 50, level.map )
|
583
|
+
painter = Qt::Painter.new
|
584
|
+
icon = Qt::Pixmap.new( tilesize*level.map.width, tilesize*level.map.height )
|
585
|
+
painter.begin( icon )
|
586
|
+
@widget.canvas.draw_all( painter, level.map, icon.width, icon.height )
|
587
|
+
painter.end
|
588
|
+
FileUtils.makedirs( File.dirname( cachefile ) )
|
589
|
+
icon.save( cachefile, "PNG" )
|
590
|
+
icon
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
end
|
595
|
+
|
596
|
+
|
597
|
+
end
|