glimmer-dsl-libui 0.2.15 → 0.2.19

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,39 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ class Tetris
23
+ module Model
24
+ class PastGame
25
+ attr_accessor :name, :score, :lines, :level
26
+
27
+ def initialize(name, score, lines, level)
28
+ @name = name
29
+ @score = score.to_i
30
+ @lines = lines.to_i
31
+ @level = level.to_i
32
+ end
33
+
34
+ def to_a
35
+ [@name, @score, @lines, @level]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,329 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require_relative 'block'
23
+
24
+ require 'matrix'
25
+
26
+ class Tetris
27
+ module Model
28
+ class Tetromino
29
+ ORIENTATIONS = [:north, :east, :south, :west]
30
+
31
+ LETTER_COLORS = {
32
+ I: :cyan,
33
+ J: :blue,
34
+ L: :olive,
35
+ O: :yellow,
36
+ S: :lime,
37
+ T: :magenta,
38
+ Z: :red,
39
+ }
40
+
41
+ attr_reader :game, :letter, :preview
42
+ alias preview? preview
43
+ attr_accessor :orientation, :blocks, :row, :column
44
+
45
+ def initialize(game)
46
+ @game = game
47
+ @letter = LETTER_COLORS.keys.sample
48
+ @orientation = :north
49
+ @blocks = default_blocks
50
+ @preview = true
51
+ new_row = 0
52
+ new_column = (Model::Game::PREVIEW_PLAYFIELD_WIDTH - width)/2
53
+ update_playfield(new_row, new_column)
54
+ end
55
+
56
+ def playfield
57
+ @preview ? game.preview_playfield : game.playfield
58
+ end
59
+
60
+ def launch!
61
+ remove_from_playfield
62
+ @preview = false
63
+ new_row = 1 - height
64
+ new_column = (game.playfield_width - width)/2
65
+ update_playfield(new_row, new_column)
66
+ game.tetrominoes << self
67
+ end
68
+
69
+ def update_playfield(new_row = nil, new_column = nil)
70
+ remove_from_playfield
71
+ if !new_row.nil? && !new_column.nil?
72
+ @row = new_row
73
+ @column = new_column
74
+ add_to_playfield
75
+ end
76
+ end
77
+
78
+ def add_to_playfield
79
+ update_playfield_block do |playfield_row, playfield_column, row_index, column_index|
80
+ playfield[playfield_row][playfield_column].color = blocks[row_index][column_index].color if playfield_row >= 0 && playfield[playfield_row][playfield_column]&.clear? && !blocks[row_index][column_index].clear? && playfield[playfield_row][playfield_column].color != blocks[row_index][column_index].color
81
+ end
82
+ end
83
+
84
+ def remove_from_playfield
85
+ return if @row.nil? || @column.nil?
86
+ update_playfield_block do |playfield_row, playfield_column, row_index, column_index|
87
+ playfield[playfield_row][playfield_column].clear if playfield_row >= 0 && !blocks[row_index][column_index].clear? && playfield[playfield_row][playfield_column]&.color == color
88
+ end
89
+ end
90
+
91
+ def stopped?
92
+ return true if @stopped || @preview
93
+ playfield_remaining_heights = game.playfield_remaining_heights(self)
94
+ result = bottom_most_blocks.any? do |bottom_most_block|
95
+ playfield_column = @column + bottom_most_block[:column_index]
96
+ playfield_remaining_heights[playfield_column] &&
97
+ @row + bottom_most_block[:row_index] >= playfield_remaining_heights[playfield_column] - 1
98
+ end
99
+ if result && !game.hypothetical?
100
+ @stopped = result
101
+ game.consider_eliminating_lines
102
+ @game.consider_adding_tetromino
103
+ end
104
+ result
105
+ end
106
+
107
+ # Returns bottom-most blocks of a tetromino, which could be from multiple rows depending on shape (e.g. T)
108
+ def bottom_most_blocks
109
+ width.times.map do |column_index|
110
+ row_blocks_with_row_index = @blocks.each_with_index.to_a.reverse.detect do |row_blocks, row_index|
111
+ !row_blocks[column_index].clear?
112
+ end
113
+ bottom_most_block = row_blocks_with_row_index[0][column_index]
114
+ bottom_most_block_row = row_blocks_with_row_index[1]
115
+ {
116
+ block: bottom_most_block,
117
+ row_index: bottom_most_block_row,
118
+ column_index: column_index
119
+ }
120
+ end
121
+ end
122
+
123
+ def bottom_most_block_for_column(column)
124
+ bottom_most_blocks.detect {|bottom_most_block| (@column + bottom_most_block[:column_index]) == column}
125
+ end
126
+
127
+ def right_blocked?
128
+ (@column == game.playfield_width - width) ||
129
+ right_most_blocks.any? { |right_most_block|
130
+ (@row + right_most_block[:row_index]) >= 0 &&
131
+ playfield[@row + right_most_block[:row_index]][@column + right_most_block[:column_index] + 1].occupied?
132
+ }
133
+ end
134
+
135
+ # Returns right-most blocks of a tetromino, which could be from multiple columns depending on shape (e.g. T)
136
+ def right_most_blocks
137
+ @blocks.each_with_index.map do |row_blocks, row_index|
138
+ column_block_with_column_index = row_blocks.each_with_index.to_a.reverse.detect do |column_block, column_index|
139
+ !column_block.clear?
140
+ end
141
+ if column_block_with_column_index
142
+ right_most_block = column_block_with_column_index[0]
143
+ {
144
+ block: right_most_block,
145
+ row_index: row_index,
146
+ column_index: column_block_with_column_index[1]
147
+ }
148
+ end
149
+ end.compact
150
+ end
151
+
152
+ def left_blocked?
153
+ (@column == 0) ||
154
+ left_most_blocks.any? { |left_most_block|
155
+ (@row + left_most_block[:row_index]) >= 0 &&
156
+ playfield[@row + left_most_block[:row_index]][@column + left_most_block[:column_index] - 1].occupied?
157
+ }
158
+ end
159
+
160
+ # Returns right-most blocks of a tetromino, which could be from multiple columns depending on shape (e.g. T)
161
+ def left_most_blocks
162
+ @blocks.each_with_index.map do |row_blocks, row_index|
163
+ column_block_with_column_index = row_blocks.each_with_index.to_a.detect do |column_block, column_index|
164
+ !column_block.clear?
165
+ end
166
+ if column_block_with_column_index
167
+ left_most_block = column_block_with_column_index[0]
168
+ {
169
+ block: left_most_block,
170
+ row_index: row_index,
171
+ column_index: column_block_with_column_index[1]
172
+ }
173
+ end
174
+ end.compact
175
+ end
176
+
177
+ def width
178
+ @blocks[0].size
179
+ end
180
+
181
+ def height
182
+ @blocks.size
183
+ end
184
+
185
+ def down!(instant: false)
186
+ launch! if preview?
187
+ unless stopped?
188
+ block_count = 1
189
+ if instant
190
+ remaining_height, bottom_touching_block = remaining_height_and_bottom_touching_block
191
+ block_count = remaining_height - @row
192
+ end
193
+ new_row = @row + block_count
194
+ update_playfield(new_row, @column)
195
+ end
196
+ end
197
+
198
+ def left!
199
+ unless left_blocked?
200
+ new_column = @column - 1
201
+ update_playfield(@row, new_column)
202
+ end
203
+ end
204
+
205
+ def right!
206
+ unless right_blocked?
207
+ new_column = @column + 1
208
+ update_playfield(@row, new_column)
209
+ end
210
+ end
211
+
212
+ # Rotate in specified direcation, which can be :right (clockwise) or :left (counterclockwise)
213
+ def rotate!(direction)
214
+ return if stopped?
215
+ can_rotate = nil
216
+ new_blocks = nil
217
+ game.hypothetical do
218
+ hypothetical_rotated_tetromino = hypothetical_tetromino
219
+ new_blocks = hypothetical_rotated_tetromino.rotate_blocks(direction)
220
+ can_rotate = !hypothetical_rotated_tetromino.stopped? && !hypothetical_rotated_tetromino.right_blocked? && !hypothetical_rotated_tetromino.left_blocked?
221
+ end
222
+ if can_rotate
223
+ remove_from_playfield
224
+ self.orientation = ORIENTATIONS[ORIENTATIONS.rotate(direction == :right ? -1 : 1).index(@orientation)]
225
+ self.blocks = new_blocks
226
+ update_playfield(@row, @column)
227
+ end
228
+ rescue => e
229
+ puts e.full_message
230
+ end
231
+
232
+ def rotate_blocks(direction)
233
+ new_blocks = Matrix[*@blocks].transpose.to_a
234
+ if direction == :right
235
+ new_blocks = new_blocks.map(&:reverse)
236
+ else
237
+ new_blocks = new_blocks.reverse
238
+ end
239
+ Matrix[*new_blocks].to_a
240
+ end
241
+
242
+ def hypothetical_tetromino
243
+ clone.tap do |hypo_clone|
244
+ remove_from_playfield
245
+ hypo_clone.blocks = @blocks.map do |row_blocks|
246
+ row_blocks.map do |column_block|
247
+ column_block.clone
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ def remaining_height_and_bottom_touching_block
254
+ playfield_remaining_heights = game.playfield_remaining_heights(self)
255
+ bottom_most_blocks.map do |bottom_most_block|
256
+ playfield_column = @column + bottom_most_block[:column_index]
257
+ [playfield_remaining_heights[playfield_column] - (bottom_most_block[:row_index] + 1), bottom_most_block]
258
+ end.min_by(&:first)
259
+ end
260
+
261
+ def default_blocks
262
+ case @letter
263
+ when :I
264
+ [
265
+ [block, block, block, block]
266
+ ]
267
+ when :J
268
+ [
269
+ [block, block, block],
270
+ [empty, empty, block],
271
+ ]
272
+ when :L
273
+ [
274
+ [block, block, block],
275
+ [block, empty, empty],
276
+ ]
277
+ when :O
278
+ [
279
+ [block, block],
280
+ [block, block],
281
+ ]
282
+ when :S
283
+ [
284
+ [empty, block, block],
285
+ [block, block, empty],
286
+ ]
287
+ when :T
288
+ [
289
+ [block, block, block],
290
+ [empty, block, empty],
291
+ ]
292
+ when :Z
293
+ [
294
+ [block, block, empty],
295
+ [empty, block, block],
296
+ ]
297
+ end
298
+ end
299
+
300
+ def color
301
+ LETTER_COLORS[@letter]
302
+ end
303
+
304
+ def include_block?(block)
305
+ @blocks.flatten.include?(block)
306
+ end
307
+
308
+ private
309
+
310
+ def block
311
+ Block.new(color)
312
+ end
313
+
314
+ def empty
315
+ Block.new
316
+ end
317
+
318
+ def update_playfield_block(&updater)
319
+ @row.upto(@row + height - 1) do |playfield_row|
320
+ @column.upto(@column + width - 1) do |playfield_column|
321
+ row_index = playfield_row - @row
322
+ column_index = playfield_column - @column
323
+ updater.call(playfield_row, playfield_column, row_index, column_index)
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,262 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ require_relative 'tetris/model/game'
4
+
5
+ class Tetris
6
+ include Glimmer
7
+
8
+ BLOCK_SIZE = 25
9
+ BEVEL_CONSTANT = 20
10
+ COLOR_GRAY = {r: 192, g: 192, b: 192}
11
+
12
+ attr_reader :game
13
+
14
+ def initialize
15
+ @game = Model::Game.new
16
+ end
17
+
18
+ def launch
19
+ create_gui
20
+ register_observers
21
+ @game.start!
22
+ @main_window.show
23
+ end
24
+
25
+ def create_gui
26
+ @main_window = window('Glimmer Tetris') {
27
+ content_size Model::Game::PLAYFIELD_WIDTH * BLOCK_SIZE, Model::Game::PLAYFIELD_HEIGHT * BLOCK_SIZE + 98
28
+
29
+ vertical_box {
30
+ label { # filler
31
+ stretchy false
32
+ }
33
+
34
+ score_board(block_size: BLOCK_SIZE) {
35
+ stretchy false
36
+ }
37
+
38
+ @playfield_blocks = playfield(playfield_width: Model::Game::PLAYFIELD_WIDTH, playfield_height: Model::Game::PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
39
+ }
40
+ }
41
+ end
42
+
43
+ def register_observers
44
+ Glimmer::DataBinding::Observer.proc do |game_over|
45
+ if game_over
46
+ show_game_over_dialog
47
+ else
48
+ start_moving_tetrominos_down
49
+ end
50
+ end.observe(@game, :game_over)
51
+
52
+ Model::Game::PLAYFIELD_HEIGHT.times do |row|
53
+ Model::Game::PLAYFIELD_WIDTH.times do |column|
54
+ Glimmer::DataBinding::Observer.proc do |new_color|
55
+ Glimmer::LibUI.queue_main do
56
+ color = Glimmer::LibUI.interpret_color(new_color)
57
+ block = @playfield_blocks[row][column]
58
+ block[:background_square].fill = color
59
+ block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
60
+ block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
61
+ block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
62
+ block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
63
+ block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
64
+ end
65
+ end.observe(@game.playfield[row][column], :color)
66
+ end
67
+ end
68
+
69
+ Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
70
+ Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
71
+ Glimmer::DataBinding::Observer.proc do |new_color|
72
+ Glimmer::LibUI.queue_main do
73
+ color = Glimmer::LibUI.interpret_color(new_color)
74
+ block = @preview_playfield_blocks[row][column]
75
+ block[:background_square].fill = color
76
+ block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
77
+ block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
78
+ block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
79
+ block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
80
+ block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
81
+ end
82
+ end.observe(@game.preview_playfield[row][column], :color)
83
+ end
84
+ end
85
+
86
+ Glimmer::DataBinding::Observer.proc do |new_score|
87
+ Glimmer::LibUI.queue_main do
88
+ @score_label.text = new_score.to_s
89
+ end
90
+ end.observe(@game, :score)
91
+
92
+ Glimmer::DataBinding::Observer.proc do |new_lines|
93
+ Glimmer::LibUI.queue_main do
94
+ @lines_label.text = new_lines.to_s
95
+ end
96
+ end.observe(@game, :lines)
97
+
98
+ Glimmer::DataBinding::Observer.proc do |new_level|
99
+ Glimmer::LibUI.queue_main do
100
+ @level_label.text = new_level.to_s
101
+ end
102
+ end.observe(@game, :level)
103
+ end
104
+
105
+ def playfield(playfield_width: , playfield_height: , block_size: , &extra_content)
106
+ blocks = []
107
+ vertical_box {
108
+ padded false
109
+
110
+ playfield_height.times.map do |row|
111
+ blocks << []
112
+ horizontal_box {
113
+ padded false
114
+
115
+ playfield_width.times.map do |column|
116
+ blocks.last << block(row: row, column: column, block_size: block_size)
117
+ end
118
+ }
119
+ end
120
+
121
+ extra_content&.call
122
+ }
123
+ blocks
124
+ end
125
+
126
+ def block(row: , column: , block_size: , &extra_content)
127
+ block = {}
128
+ bevel_pixel_size = 0.16 * block_size.to_f
129
+ color = Glimmer::LibUI.interpret_color(Model::Block::COLOR_CLEAR)
130
+ area {
131
+ block[:background_square] = path {
132
+ square(0, 0, block_size)
133
+
134
+ fill color
135
+ }
136
+ block[:top_bevel_edge] = path {
137
+ polygon(0, 0, block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size)
138
+
139
+ fill r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT
140
+ }
141
+ block[:right_bevel_edge] = path {
142
+ polygon(block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size, block_size)
143
+
144
+ fill r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT
145
+ }
146
+ block[:bottom_bevel_edge] = path {
147
+ polygon(block_size, block_size, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size)
148
+
149
+ fill r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT
150
+ }
151
+ block[:left_bevel_edge] = path {
152
+ polygon(0, 0, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size)
153
+
154
+ fill r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT
155
+ }
156
+ block[:border_square] = path {
157
+ square(0, 0, block_size)
158
+
159
+ stroke COLOR_GRAY
160
+ }
161
+
162
+ on_key_down do |key_event|
163
+ case key_event
164
+ in ext_key: :down
165
+ game.down!
166
+ in key: ' '
167
+ game.down!(instant: true)
168
+ in ext_key: :up
169
+ case game.up_arrow_action
170
+ when :instant_down
171
+ game.down!(instant: true)
172
+ when :rotate_right
173
+ game.rotate!(:right)
174
+ when :rotate_left
175
+ game.rotate!(:left)
176
+ end
177
+ in ext_key: :left
178
+ game.left!
179
+ in ext_key: :right
180
+ game.right!
181
+ in modifier: :shift
182
+ game.rotate!(:right)
183
+ in modifier: :control
184
+ game.rotate!(:left)
185
+ else
186
+ # Do Nothing
187
+ end
188
+ end
189
+
190
+ extra_content&.call
191
+ }
192
+ block
193
+ end
194
+
195
+ def score_board(block_size: , &extra_content)
196
+ vertical_box {
197
+ horizontal_box {
198
+ label # filler
199
+ @preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
200
+ label # filler
201
+ }
202
+
203
+ horizontal_box {
204
+ label # filler
205
+ grid {
206
+ stretchy false
207
+
208
+ label('Score') {
209
+ left 0
210
+ top 0
211
+ halign :fill
212
+ }
213
+ @score_label = label {
214
+ left 0
215
+ top 1
216
+ halign :center
217
+ }
218
+
219
+ label('Lines') {
220
+ left 1
221
+ top 0
222
+ halign :fill
223
+ }
224
+ @lines_label = label {
225
+ left 1
226
+ top 1
227
+ halign :center
228
+ }
229
+
230
+ label('Level') {
231
+ left 2
232
+ top 0
233
+ halign :fill
234
+ }
235
+ @level_label = label {
236
+ left 2
237
+ top 1
238
+ halign :center
239
+ }
240
+ }
241
+ label # filler
242
+ }
243
+
244
+ extra_content&.call
245
+ }
246
+ end
247
+
248
+ def start_moving_tetrominos_down
249
+ Glimmer::LibUI.timer(@game.delay) do
250
+ @game.down! if !@game.game_over? && !@game.paused?
251
+ end
252
+ end
253
+
254
+ def show_game_over_dialog
255
+ Glimmer::LibUI.queue_main do
256
+ msg_box('Game Over', "Score: #{@game.high_scores.first.score}\nLines: #{@game.high_scores.first.lines}\nLevel: #{@game.high_scores.first.level}")
257
+ @game.restart!
258
+ end
259
+ end
260
+ end
261
+
262
+ Tetris.new.launch
Binary file
@@ -37,6 +37,7 @@ module Glimmer
37
37
  end
38
38
 
39
39
  def interpret(parent, keyword, *args, &block)
40
+ args = [args] if args.size > 1 && Glimmer::LibUI::Shape.shape_class(keyword).parameters.size == 1
40
41
  Glimmer::LibUI::Shape.create(keyword, parent, args, &block)
41
42
  end
42
43
 
@@ -109,6 +109,31 @@ module Glimmer
109
109
  queue_redraw_all
110
110
  end
111
111
 
112
+ def request_auto_redraw
113
+ # TODO implement functionality to delay queuing area redraws until post_add_content has been called (area definition is done). Maybe offer an option to enable redrawing before area is closed too.
114
+ queue_redraw_all if auto_redraw_enabled?
115
+ end
116
+
117
+ def auto_redraw_enabled(value = nil)
118
+ if value.nil?
119
+ @auto_redraw_enabled = true if @auto_redraw_enabled.nil?
120
+ @auto_redraw_enabled
121
+ else
122
+ @auto_redraw_enabled = !!value
123
+ end
124
+ end
125
+ alias auto_redraw_enabled? auto_redraw_enabled
126
+ alias auto_redraw_enabled= auto_redraw_enabled
127
+ alias set_auto_redraw_enabled auto_redraw_enabled
128
+
129
+ def pause_auto_redraw
130
+ self.auto_redraw_enabled = false
131
+ end
132
+
133
+ def resume_auto_redraw
134
+ self.auto_redraw_enabled = true
135
+ end
136
+
112
137
  private
113
138
 
114
139
  def build_control