rubysketch-solitaire 0.1.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +3 -0
- data/.gitignore +2 -0
- data/.hooks/post-commit +37 -0
- data/.hooks/pre-commit +69 -0
- data/ChangeLog.md +27 -0
- data/Gemfile.lock +10 -10
- data/Podfile.lock +33 -33
- data/README.md +8 -0
- data/Rakefile +107 -10
- data/RubySolitaire/Ad.swift +88 -0
- data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
- data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/Contents.json +3 -87
- data/RubySolitaire/Base.lproj/Localizable.strings +6 -0
- data/RubySolitaire/Extensions.swift +5 -0
- data/RubySolitaire/GameView.swift +75 -13
- data/RubySolitaire/Helper.swift +70 -0
- data/RubySolitaire/MenuScreen.swift +141 -0
- data/RubySolitaire/RubySolitaireApp.swift +188 -1
- data/RubySolitaire/SafariView.swift +16 -0
- data/RubySolitaire/Strings.swift +48 -0
- data/RubySolitaire/ja.lproj/InfoPlist.strings +4 -0
- data/RubySolitaire/ja.lproj/Localizable.strings +6 -0
- data/VERSION +1 -1
- data/data/card.png +0 -0
- data/data/classicPSPWave.glsl +94 -0
- data/data/colorfulUnderwaterBubbles2.glsl +113 -0
- data/data/cosmic2.glsl +121 -0
- data/data/reflectiveHexes.glsl +219 -0
- data/fastlane/Fastfile +76 -0
- data/fastlane/metadata/copyright.txt +1 -0
- data/fastlane/metadata/en-US/apple_tv_privacy_policy.txt +1 -0
- data/fastlane/metadata/en-US/description.txt +5 -0
- data/fastlane/metadata/en-US/keywords.txt +1 -0
- data/fastlane/metadata/en-US/marketing_url.txt +1 -0
- data/fastlane/metadata/en-US/name.txt +1 -0
- data/fastlane/metadata/en-US/privacy_url.txt +1 -0
- data/fastlane/metadata/en-US/promotional_text.txt +1 -0
- data/fastlane/metadata/en-US/release_notes.txt +1 -0
- data/fastlane/metadata/en-US/subtitle.txt +1 -0
- data/fastlane/metadata/en-US/support_url.txt +1 -0
- data/fastlane/metadata/ja/apple_tv_privacy_policy.txt +1 -0
- data/fastlane/metadata/ja/description.txt +5 -0
- data/fastlane/metadata/ja/keywords.txt +1 -0
- data/fastlane/metadata/ja/marketing_url.txt +1 -0
- data/fastlane/metadata/ja/name.txt +1 -0
- data/fastlane/metadata/ja/privacy_url.txt +1 -0
- data/fastlane/metadata/ja/promotional_text.txt +1 -0
- data/fastlane/metadata/ja/release_notes.txt +1 -0
- data/fastlane/metadata/ja/subtitle.txt +1 -0
- data/fastlane/metadata/ja/support_url.txt +1 -0
- data/fastlane/metadata/primary_category.txt +1 -0
- data/fastlane/metadata/primary_first_sub_category.txt +1 -0
- data/fastlane/metadata/primary_second_sub_category.txt +1 -0
- data/fastlane/metadata/review_information/demo_password.txt +1 -0
- data/fastlane/metadata/review_information/demo_user.txt +1 -0
- data/fastlane/metadata/review_information/email_address.txt +1 -0
- data/fastlane/metadata/review_information/first_name.txt +0 -0
- data/fastlane/metadata/review_information/last_name.txt +0 -0
- data/fastlane/metadata/review_information/notes.txt +1 -0
- data/fastlane/metadata/review_information/phone_number.txt +0 -0
- data/fastlane/metadata/secondary_category.txt +1 -0
- data/fastlane/metadata/secondary_first_sub_category.txt +1 -0
- data/fastlane/metadata/secondary_second_sub_category.txt +1 -0
- data/lib/rubysketch/solitaire/background.rb +115 -12
- data/lib/rubysketch/solitaire/card.rb +10 -87
- data/lib/rubysketch/solitaire/common/animation.rb +34 -34
- data/lib/rubysketch/solitaire/common/button.rb +49 -19
- data/lib/rubysketch/solitaire/common/dialog.rb +78 -21
- data/lib/rubysketch/solitaire/common/scene.rb +15 -4
- data/lib/rubysketch/solitaire/common/settings.rb +15 -3
- data/lib/rubysketch/solitaire/common/timer.rb +2 -2
- data/lib/rubysketch/solitaire/common/transitions.rb +12 -6
- data/lib/rubysketch/solitaire/common/utils.rb +15 -0
- data/lib/rubysketch/solitaire/klondike.rb +382 -81
- data/lib/rubysketch/solitaire/places.rb +12 -102
- data/lib/rubysketch/solitaire/skin.rb +151 -0
- data/lib/rubysketch/solitaire.rb +33 -9
- data/main.rb +1 -0
- data/project.yml +85 -4
- metadata +55 -2
@@ -13,6 +13,25 @@ class Klondike < Scene
|
|
13
13
|
super + [*cards, *places].map(&:sprite) + interfaces
|
14
14
|
end
|
15
15
|
|
16
|
+
def difficulty()
|
17
|
+
@difficulty ||= :normal
|
18
|
+
end
|
19
|
+
|
20
|
+
def pause()
|
21
|
+
super
|
22
|
+
@prevTime = nil
|
23
|
+
stopTimer :save
|
24
|
+
end
|
25
|
+
|
26
|
+
def resume()
|
27
|
+
super
|
28
|
+
return if !started? || completed?
|
29
|
+
@prevTime = now
|
30
|
+
startInterval :save, 1, now: true do
|
31
|
+
save
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
16
35
|
def draw()
|
17
36
|
sprite *places.map(&:sprite)
|
18
37
|
sprite *cards.sort {|a, b| a.z <=> b.z}.map(&:sprite)
|
@@ -25,6 +44,10 @@ class Klondike < Scene
|
|
25
44
|
updateLayout w, h
|
26
45
|
end
|
27
46
|
|
47
|
+
def focusChanged(focus)
|
48
|
+
focus ? resume : pause if ios?
|
49
|
+
end
|
50
|
+
|
28
51
|
def canDrop?(card)
|
29
52
|
case card.place
|
30
53
|
when *columns then card.opened?
|
@@ -70,7 +93,7 @@ class Klondike < Scene
|
|
70
93
|
def save()
|
71
94
|
settings['state'] = {
|
72
95
|
version: 1,
|
73
|
-
|
96
|
+
difficulty: difficulty,
|
74
97
|
history: history.to_h {|o| o.id if o.respond_to? :id},
|
75
98
|
score: score.to_h,
|
76
99
|
elapsedTime: elapsedTime,
|
@@ -87,7 +110,7 @@ class Klondike < Scene
|
|
87
110
|
findAll = -> id { all.find {|obj| obj .id == id} or raise "No object '#{id}'"}
|
88
111
|
findCard = -> id {cards.find {|card| card.id == id} or raise "No card '#{id}'"}
|
89
112
|
|
90
|
-
|
113
|
+
@difficulty = hash['difficulty'].intern
|
91
114
|
|
92
115
|
self.history = History.load hash['history'] do |id|
|
93
116
|
(id.respond_to?('=~') && id =~ /^id:/) ? findAll[id] : nil
|
@@ -110,7 +133,7 @@ class Klondike < Scene
|
|
110
133
|
raise "Failed to restore state" unless
|
111
134
|
places.reduce([]) {|a, place| a + place.cards}.size == 52
|
112
135
|
|
113
|
-
|
136
|
+
start!
|
114
137
|
end
|
115
138
|
|
116
139
|
def inspect()
|
@@ -151,22 +174,81 @@ class Klondike < Scene
|
|
151
174
|
}
|
152
175
|
end
|
153
176
|
|
177
|
+
def addScore(name)
|
178
|
+
old = score.value
|
179
|
+
score.add name if history.enabled?
|
180
|
+
history.push [:score, score.value, old] if score.value != old
|
181
|
+
end
|
182
|
+
|
154
183
|
def bestTime()
|
155
|
-
settings[
|
184
|
+
settings[bestRecordKey :time] || 24 * 60 * 60 - 1
|
156
185
|
end
|
157
186
|
|
158
187
|
def bestScore()
|
159
|
-
settings[
|
188
|
+
settings[bestRecordKey :score] || 0
|
189
|
+
end
|
190
|
+
|
191
|
+
def dailyBestTime()
|
192
|
+
key = bestRecordKey :time, true
|
193
|
+
settings[key] = nil if settings["#{key}Date"] != today
|
194
|
+
settings[key] || 24 * 60 * 60 - 1
|
195
|
+
end
|
196
|
+
|
197
|
+
def dailyBestScore()
|
198
|
+
key = bestRecordKey :score, true
|
199
|
+
settings[key] = nil if settings["#{key}Date"] != today
|
200
|
+
settings[key] || 0
|
160
201
|
end
|
161
202
|
|
162
203
|
def updateBests()
|
163
|
-
newTime
|
164
|
-
newScore
|
204
|
+
newTime = elapsedTime < bestTime
|
205
|
+
newScore = score.value > bestScore
|
206
|
+
newDailyTime = elapsedTime < dailyBestTime
|
207
|
+
newDailyScore = score.value > dailyBestScore
|
208
|
+
|
209
|
+
settings[bestRecordKey :time] = elapsedTime if newTime
|
210
|
+
settings[bestRecordKey :score] = score.value if newScore
|
165
211
|
|
166
|
-
|
167
|
-
|
212
|
+
if newDailyTime
|
213
|
+
settings[bestRecordKey(:time, true)] = elapsedTime
|
214
|
+
settings[bestRecordKey(:time, true, 'Date')] = today
|
215
|
+
end
|
216
|
+
|
217
|
+
if newDailyScore
|
218
|
+
settings[bestRecordKey(:score, true)] = score.value
|
219
|
+
settings[bestRecordKey(:score, true, 'Date')] = today
|
220
|
+
end
|
168
221
|
|
169
|
-
return newTime, newScore
|
222
|
+
return newTime, newScore, newDailyTime, newDailyScore
|
223
|
+
end
|
224
|
+
|
225
|
+
def clearAllTimeBests()
|
226
|
+
%i[time score]
|
227
|
+
.map {|type| bestRecordKey type}
|
228
|
+
.each {|key| settings[key] = nil}
|
229
|
+
end
|
230
|
+
|
231
|
+
def clearDailyBests()
|
232
|
+
%i[time score]
|
233
|
+
.map {|type| ['', 'Date'].map {|s| bestRecordKey type, true, s}}
|
234
|
+
.flatten
|
235
|
+
.each {|key| settings[key] = nil}
|
236
|
+
end
|
237
|
+
|
238
|
+
def bestRecordKey(type, daily = false, suffix = '')
|
239
|
+
difficulty.to_s +
|
240
|
+
(daily ? 'Daily' : '') +
|
241
|
+
'Best' +
|
242
|
+
type.to_s.capitalize +
|
243
|
+
suffix
|
244
|
+
end
|
245
|
+
|
246
|
+
def timeToText(time)
|
247
|
+
Time.at(time).strftime('%M:%S')
|
248
|
+
end
|
249
|
+
|
250
|
+
def today()
|
251
|
+
Time.now.strftime '%Y%m%d'
|
170
252
|
end
|
171
253
|
|
172
254
|
def cards()
|
@@ -180,23 +262,23 @@ class Klondike < Scene
|
|
180
262
|
end
|
181
263
|
|
182
264
|
def deck()
|
183
|
-
@deck ||= CardPlace.new(:deck).tap do |deck|
|
265
|
+
@deck ||= CardPlace.new(self, :deck).tap do |deck|
|
184
266
|
deck.sprite.mouseClicked {deckClicked}
|
185
267
|
end
|
186
268
|
end
|
187
269
|
|
188
270
|
def nexts()
|
189
|
-
@nexts ||= NextsPlace.new(:nexts).tap do |nexts|
|
271
|
+
@nexts ||= NextsPlace.new(self, :nexts).tap do |nexts|
|
190
272
|
nexts.sprite.mouseClicked {nextsClicked}
|
191
273
|
end
|
192
274
|
end
|
193
275
|
|
194
276
|
def marks()
|
195
|
-
@marks ||= Card::MARKS.size.times.map {|i| MarkPlace.new "mark_#{i + 1}"}
|
277
|
+
@marks ||= Card::MARKS.size.times.map {|i| MarkPlace.new self, "mark_#{i + 1}"}
|
196
278
|
end
|
197
279
|
|
198
280
|
def columns()
|
199
|
-
@culumns ||= 7.times.map.with_index {|i| ColumnPlace.new "column_#{i + 1}"}
|
281
|
+
@culumns ||= 7.times.map.with_index {|i| ColumnPlace.new self, "column_#{i + 1}"}
|
200
282
|
end
|
201
283
|
|
202
284
|
def dealSound()
|
@@ -215,12 +297,12 @@ class Klondike < Scene
|
|
215
297
|
end
|
216
298
|
|
217
299
|
def interfaces()
|
218
|
-
[undoButton, redoButton,
|
300
|
+
[undoButton, redoButton, pauseButton, finishButton, status, debugButton]
|
219
301
|
end
|
220
302
|
|
221
303
|
def undoButton()
|
222
304
|
@undoButton ||= Button.new(
|
223
|
-
'◀',
|
305
|
+
'◀', fontSize: 28, round: [20, 4, 4, 20]
|
224
306
|
).tap do |b|
|
225
307
|
b.update {b.enable history.canUndo?}
|
226
308
|
b.clicked {history.undo {|action| undo action}}
|
@@ -229,18 +311,16 @@ class Klondike < Scene
|
|
229
311
|
|
230
312
|
def redoButton()
|
231
313
|
@redoButton ||= Button.new(
|
232
|
-
'▶',
|
314
|
+
'▶', fontSize: 28, round: [4, 20, 20, 4]
|
233
315
|
).tap do |b|
|
234
316
|
b.update {b.enable history.canRedo?}
|
235
317
|
b.clicked {history.redo {|action| self.redo action}}
|
236
318
|
end
|
237
319
|
end
|
238
320
|
|
239
|
-
def
|
240
|
-
@
|
241
|
-
|
242
|
-
).tap do |b|
|
243
|
-
b.clicked {showMenuDialog}
|
321
|
+
def pauseButton()
|
322
|
+
@pauseButton ||= Button.new(icon: skin.pauseIcon).tap do |b|
|
323
|
+
b.clicked {showPauseDialog}
|
244
324
|
end
|
245
325
|
end
|
246
326
|
|
@@ -248,7 +328,7 @@ class Klondike < Scene
|
|
248
328
|
@status ||= Sprite.new.tap do |sp|
|
249
329
|
sp.draw do
|
250
330
|
push do
|
251
|
-
fill
|
331
|
+
fill *skin.translucentBackgroundColor
|
252
332
|
rect 0, 0, sp.w, sp.h, 10
|
253
333
|
fill 255
|
254
334
|
|
@@ -260,7 +340,7 @@ class Klondike < Scene
|
|
260
340
|
}.each do |label, value|
|
261
341
|
textSize 12
|
262
342
|
textAlign LEFT, TOP
|
263
|
-
text label, x + mx, my, w - mx, sp.h - my * 2
|
343
|
+
text str(label), x + mx, my, w - mx, sp.h - my * 2
|
264
344
|
textSize 20
|
265
345
|
textAlign LEFT, BOTTOM
|
266
346
|
text value, x + mx, my, w - mx, sp.h - my * 2
|
@@ -282,71 +362,178 @@ class Klondike < Scene
|
|
282
362
|
|
283
363
|
def debugButton()
|
284
364
|
@debugButton ||= Button.new(:DEBUG, width: 3).tap do |b|
|
285
|
-
b.hide
|
286
|
-
b.clicked {}
|
365
|
+
b.hide unless debug?
|
366
|
+
b.clicked {showDebugDialog}
|
287
367
|
end
|
288
368
|
end
|
289
369
|
|
290
370
|
def showReadyDialog()
|
291
371
|
add Dialog.new(alpha: 50).tap {|d|
|
292
|
-
d.addButton 'EASY', width: 5 do
|
293
|
-
start
|
372
|
+
d.addButton str('EASY'), width: 5 do
|
373
|
+
start :easy
|
294
374
|
d.close
|
295
375
|
end
|
296
|
-
d.addButton '
|
297
|
-
start
|
376
|
+
d.addButton str('NORMAL'), width: 5 do
|
377
|
+
start :normal
|
378
|
+
d.close
|
379
|
+
end
|
380
|
+
d.addButton str('HARD'), width: 5 do
|
381
|
+
start :hard
|
298
382
|
d.close
|
299
383
|
end
|
300
384
|
}
|
301
385
|
end
|
302
386
|
|
303
|
-
def
|
304
|
-
|
305
|
-
|
306
|
-
d.
|
387
|
+
def showPauseDialog()
|
388
|
+
add Dialog.new {|d|
|
389
|
+
d.addLabel "#{str 'Difficulty'}: #{str difficulty.upcase}"
|
390
|
+
d.addLabel "#{str 'Best Time'}: #{timeToText bestTime}"
|
391
|
+
d.addLabel "#{str 'Best Score'}: #{bestScore}"
|
392
|
+
d.addLabel "#{str "Today's Best Time"}: #{timeToText dailyBestTime}"
|
393
|
+
d.addLabel "#{str "Today's Best Score"}: #{dailyBestScore}"
|
394
|
+
d.addSpace 20
|
395
|
+
d.addButton str('Resume'), width: 6 do
|
307
396
|
d.close
|
308
|
-
resume
|
309
397
|
end
|
310
|
-
d.addButton '
|
398
|
+
d.addButton str('New Game'), width: 6 do
|
399
|
+
d.close
|
400
|
+
showNewGameDialog
|
401
|
+
end
|
402
|
+
d.addSpace 10
|
403
|
+
d.group do
|
404
|
+
if ios?
|
405
|
+
d.addButton icon: skin.menuIcon do
|
406
|
+
sendCommand :showMenu
|
407
|
+
end
|
408
|
+
end
|
409
|
+
d.addButton icon: skin.settingsIcon do
|
410
|
+
showSettingsDialog
|
411
|
+
end
|
412
|
+
end
|
413
|
+
}
|
414
|
+
end
|
415
|
+
|
416
|
+
def showNewGameDialog()
|
417
|
+
add Dialog.new(alpha: 180).tap {|d|
|
418
|
+
d.addLabel str("Start New Game?")
|
419
|
+
d.addSpace 20
|
420
|
+
d.addButton str('OK'), width: 4 do
|
311
421
|
startNewGame
|
422
|
+
d.close
|
423
|
+
end
|
424
|
+
d.addButton str('Cancel'), width: 4 do
|
425
|
+
d.close
|
312
426
|
end
|
313
|
-
d.addSpace 50
|
314
|
-
d.addLabel "Best Time: #{timeToText bestTime}"
|
315
|
-
d.addLabel "Best Score: #{bestScore}"
|
316
427
|
}
|
317
428
|
end
|
318
429
|
|
319
|
-
def
|
320
|
-
|
430
|
+
def showSettingsDialog()
|
431
|
+
closedImage = -> {
|
432
|
+
i = skin.closedImage
|
433
|
+
resizeImage i, i.width / 2, i.height / 2
|
434
|
+
}
|
435
|
+
add Dialog.new(alpha: 255).tap {|d|
|
436
|
+
bg = d.add Background.new backgroundScene.type
|
437
|
+
cardImage = d.addElement Sprite.new image: closedImage.call
|
438
|
+
d.addButton str('Change Card Design'), width: 7 do
|
439
|
+
skin skin.index + 1
|
440
|
+
settings['skinIndex'] = skin.index
|
441
|
+
cardImage.image = closedImage.call
|
442
|
+
end
|
443
|
+
d.addSpace 10
|
444
|
+
author = -> {bg.author&.then {|author| "by #{author}"} || '-'}
|
445
|
+
bgName = bgAuthor = nil
|
446
|
+
d.group :vertical, space: 0 do
|
447
|
+
bgName = d.addLabel(bg.name, alpha: 0) {sendCommand :openURL, bg.url}
|
448
|
+
bgAuthor = d.addLabel author.call, fontSize: 16, alpha: 0
|
449
|
+
end
|
450
|
+
d.addButton str('Change Background'), width: 7 do
|
451
|
+
bg.set bg.nextType
|
452
|
+
bgName .label = bg.name
|
453
|
+
bgAuthor.label = author.call
|
454
|
+
backgroundScene.set bg.type
|
455
|
+
end
|
456
|
+
d.addSpace 20
|
457
|
+
d.addButton str('Close'), width: 6 do
|
458
|
+
d.close
|
459
|
+
end
|
460
|
+
}
|
461
|
+
end
|
462
|
+
|
463
|
+
def showCompletedDialog(
|
464
|
+
bestTime = false, bestScore = false,
|
465
|
+
dailyBestTime = false, dailyBestScore = false)
|
466
|
+
|
467
|
+
suffix = -> allTime, daily do
|
468
|
+
return str '(New Record!)' if allTime
|
469
|
+
return str "(Today's Best!)" if daily
|
470
|
+
''
|
471
|
+
end
|
472
|
+
|
321
473
|
add Dialog.new.tap {|d|
|
322
|
-
d.addLabel 'Congratulations!', fontSize: 44
|
474
|
+
d.addLabel str('Congratulations!'), fontSize: 44
|
323
475
|
d.addLabel(
|
324
|
-
"Time: #{timeToText elapsedTime} #{
|
476
|
+
"#{str 'Time'}: #{timeToText elapsedTime} #{suffix.call bestTime, dailyBestTime}",
|
325
477
|
fontSize: 28)
|
326
478
|
d.addLabel(
|
327
|
-
"Score: #{score.value} #{
|
479
|
+
"#{str 'Score'}: #{score.value} #{suffix.call bestScore, dailyBestScore}",
|
328
480
|
fontSize: 28)
|
329
481
|
d.addSpace 50
|
330
|
-
d.addButton '
|
482
|
+
d.addButton str('Start Next Game'), width: 5 do
|
331
483
|
startNewGame
|
332
484
|
end
|
333
485
|
}
|
334
486
|
end
|
335
487
|
|
488
|
+
def showDebugDialog()
|
489
|
+
add Dialog.new.tap {|d|
|
490
|
+
d.addButton str('Clear all settings'), width: 6 do
|
491
|
+
settings.clear
|
492
|
+
d.close
|
493
|
+
end
|
494
|
+
d.addButton str('Clear all time bests'), width: 6 do
|
495
|
+
clearAllTimeBests
|
496
|
+
d.close
|
497
|
+
end
|
498
|
+
d.addButton str("Clear today's bests"), width: 6 do
|
499
|
+
clearDailyBests
|
500
|
+
d.close
|
501
|
+
end
|
502
|
+
d.addButton str("One step for completion"), width: 6 do
|
503
|
+
cards.sort.group_by(&:mark).each.with_index do |(mark, cards), index|
|
504
|
+
place = marks[index]
|
505
|
+
place.clear
|
506
|
+
cards.reverse.each.with_index do |card, i|
|
507
|
+
card.z = i
|
508
|
+
place.add card.open
|
509
|
+
end
|
510
|
+
end
|
511
|
+
d.close
|
512
|
+
end
|
513
|
+
d.addButton str("Dump settings"), width: 6 do
|
514
|
+
puts settings.to_json
|
515
|
+
d.close
|
516
|
+
end
|
517
|
+
d.addButton str('Close'), width: 6 do
|
518
|
+
d.close
|
519
|
+
end
|
520
|
+
}
|
521
|
+
end
|
522
|
+
|
336
523
|
def updateLayout(w, h)
|
337
524
|
card = cards.first
|
338
525
|
cw, ch = card.then {|c| [c.w, c.h]}
|
339
|
-
mx, my =
|
526
|
+
mx, my = skin.margin, cw * 0.2 # margin x, y
|
340
527
|
y = my
|
341
528
|
|
342
|
-
undoButton.pos
|
343
|
-
redoButton.pos
|
344
|
-
|
345
|
-
status.pos
|
346
|
-
status.right
|
347
|
-
status.height
|
529
|
+
undoButton.pos = [mx, y]
|
530
|
+
redoButton.pos = [undoButton.x + undoButton.w + 2, y]
|
531
|
+
pauseButton.pos = [width - (pauseButton.w + mx), y]
|
532
|
+
status.pos = [redoButton.right + mx, y]
|
533
|
+
status.right = pauseButton.left - mx
|
534
|
+
status.height = pauseButton.h
|
348
535
|
|
349
|
-
y = undoButton.y + undoButton.h + my
|
536
|
+
y = undoButton.y + undoButton.h + my * 3
|
350
537
|
|
351
538
|
deck.pos = [w - (deck.w + mx), y]
|
352
539
|
nexts.pos = [deck.x - (nexts.w + mx), deck.y]
|
@@ -379,8 +566,9 @@ class Klondike < Scene
|
|
379
566
|
end
|
380
567
|
end
|
381
568
|
|
382
|
-
def start(
|
383
|
-
|
569
|
+
def start(difficulty = :normal)
|
570
|
+
@difficulty = difficulty
|
571
|
+
start!
|
384
572
|
|
385
573
|
history.disable
|
386
574
|
lasts = columns.map(&:last).compact
|
@@ -390,7 +578,6 @@ class Klondike < Scene
|
|
390
578
|
if lasts.all? {|card| card.opened?}
|
391
579
|
drawNexts
|
392
580
|
history.enable
|
393
|
-
resume
|
394
581
|
end
|
395
582
|
end
|
396
583
|
end
|
@@ -411,7 +598,7 @@ class Klondike < Scene
|
|
411
598
|
|
412
599
|
def firstDistribution()
|
413
600
|
n = columns.size
|
414
|
-
(0...n).map {
|
601
|
+
(0...n).map {|row| (row...n).map {|col| [col, row]}}.flatten(1)
|
415
602
|
end
|
416
603
|
|
417
604
|
def openCard(card, gain: 0.5)
|
@@ -471,6 +658,19 @@ class Klondike < Scene
|
|
471
658
|
completed if completed?
|
472
659
|
end
|
473
660
|
|
661
|
+
def start!()
|
662
|
+
@started = true
|
663
|
+
[*cards, *places].each do |o|
|
664
|
+
o.started if o.respond_to? :started
|
665
|
+
end
|
666
|
+
places.each {|p| p.updateCards 0}
|
667
|
+
resume
|
668
|
+
end
|
669
|
+
|
670
|
+
def started?()
|
671
|
+
@started ||= false
|
672
|
+
end
|
673
|
+
|
474
674
|
def canFinish?()
|
475
675
|
deck.empty? && nexts.empty? &&
|
476
676
|
columns.any? {|col| !col.empty?} &&
|
@@ -525,7 +725,7 @@ class Klondike < Scene
|
|
525
725
|
|
526
726
|
def showFinishButton()
|
527
727
|
finishButton.tap do |b|
|
528
|
-
m =
|
728
|
+
m = skin.margin
|
529
729
|
b.x = marks.last.then {|mark| mark.x + mark.w} + m * 2
|
530
730
|
b.y = -deck.h
|
531
731
|
b.w = width - b.x - m
|
@@ -588,6 +788,7 @@ class Klondike < Scene
|
|
588
788
|
end
|
589
789
|
|
590
790
|
def backToPlace(card, vel)
|
791
|
+
return if vel.mag == 0
|
591
792
|
vec = vel.dup.normalize * sqrt(vel.mag) / 10 * sqrt(card.count)
|
592
793
|
return if vec.mag < 3
|
593
794
|
shakeScreen vector: vec
|
@@ -623,28 +824,6 @@ class Klondike < Scene
|
|
623
824
|
end
|
624
825
|
end
|
625
826
|
|
626
|
-
def timeToText(time)
|
627
|
-
Time.at(time).strftime('%M:%S')
|
628
|
-
end
|
629
|
-
|
630
|
-
def pause()
|
631
|
-
@prevTime = nil
|
632
|
-
stopTimer :save
|
633
|
-
end
|
634
|
-
|
635
|
-
def resume()
|
636
|
-
@prevTime = now
|
637
|
-
startInterval :save, 1, now: true do
|
638
|
-
save
|
639
|
-
end
|
640
|
-
end
|
641
|
-
|
642
|
-
def addScore(name)
|
643
|
-
old = score.value
|
644
|
-
score.add name if history.enabled?
|
645
|
-
history.push [:score, score.value, old] if score.value != old
|
646
|
-
end
|
647
|
-
|
648
827
|
def undo(action)
|
649
828
|
history.disable do
|
650
829
|
case action
|
@@ -670,7 +849,129 @@ class Klondike < Scene
|
|
670
849
|
end
|
671
850
|
|
672
851
|
def startNewGame()
|
673
|
-
|
852
|
+
$newGameCount ||= 0
|
853
|
+
$newGameCount += 1
|
854
|
+
showAd = $newGameCount % 3 == 0
|
855
|
+
transition self.class.new, [Fade, Curtain, Pixelate].sample, showAd: showAd
|
856
|
+
end
|
857
|
+
|
858
|
+
STRINGS = {
|
859
|
+
OK: {},
|
860
|
+
Cancel: {ja: 'キャンセル'},
|
861
|
+
Close: {ja: '閉じる'},
|
862
|
+
|
863
|
+
Time: {ja: 'タイム'},
|
864
|
+
Score: {ja: 'スコア'},
|
865
|
+
Move: {ja: '移動回数'},
|
866
|
+
|
867
|
+
Difficulty: {ja: '難易度'},
|
868
|
+
EASY: {ja: '簡単'},
|
869
|
+
NORMAL: {ja: '普通'},
|
870
|
+
HARD: {ja: '難しい'},
|
871
|
+
|
872
|
+
'New Game': {ja: '新規ゲーム'},
|
873
|
+
'Resume': {ja: 'ゲーム再開'},
|
874
|
+
|
875
|
+
'Best Time': {ja: 'ベストタイム'},
|
876
|
+
'Best Score': {ja: 'ベストスコア'},
|
877
|
+
"Today's Best Time": {ja: '本日のベストタイム'},
|
878
|
+
"Today's Best Score": {ja: '本日のベストスコア'},
|
879
|
+
|
880
|
+
"Start New Game?": {ja: '新しいゲームをはじめますか?'},
|
881
|
+
"Start Next Game": {ja: '次のゲームを開始'},
|
882
|
+
|
883
|
+
"Change Card Design": {ja: 'カードデザインを変更'},
|
884
|
+
"Change Background": {ja: 'ゲーム背景を変更'},
|
885
|
+
|
886
|
+
'(New Record!)': {ja: '(新記録!)'},
|
887
|
+
"(Today's Best!)": {ja: '(本日のベスト!)'},
|
888
|
+
}
|
889
|
+
|
890
|
+
def str(s, lang: $language)
|
891
|
+
STRINGS.dig(s.intern, lang&.intern) || s.to_s
|
674
892
|
end
|
675
893
|
|
676
894
|
end# Klondike
|
895
|
+
|
896
|
+
|
897
|
+
class Klondike::NextsPlace < CardPlace
|
898
|
+
|
899
|
+
def drawCount()
|
900
|
+
@game.difficulty == :hard ? 3 : 1
|
901
|
+
end
|
902
|
+
|
903
|
+
def started()
|
904
|
+
w = skin.cardSpriteSize[0] + overlap * (drawCount - 1)
|
905
|
+
self.x -= w - self.w
|
906
|
+
self.w = w
|
907
|
+
end
|
908
|
+
|
909
|
+
def add(*cards, **kwargs)
|
910
|
+
super
|
911
|
+
updateCards excludes: cards
|
912
|
+
end
|
913
|
+
|
914
|
+
def pop(*args)
|
915
|
+
super
|
916
|
+
updateCards
|
917
|
+
end
|
918
|
+
|
919
|
+
def posFor(card, index = nil)
|
920
|
+
index ||= indexFor card
|
921
|
+
super.tap do |pos|
|
922
|
+
rindex = cards.size - index
|
923
|
+
pos.x += overlap * (drawCount - rindex).clamp(0, drawCount - 1)
|
924
|
+
end
|
925
|
+
end
|
926
|
+
|
927
|
+
def overlap()
|
928
|
+
skin.cardSpriteSize[0] * 0.4
|
929
|
+
end
|
930
|
+
|
931
|
+
end# Klondike::NextsPlace
|
932
|
+
|
933
|
+
|
934
|
+
class Klondike::MarkPlace < CardPlace
|
935
|
+
|
936
|
+
def mark()
|
937
|
+
last&.mark
|
938
|
+
end
|
939
|
+
|
940
|
+
def accept?(x, y, card)
|
941
|
+
return false if !card || card.closed? || !card.canDrop?
|
942
|
+
hit?(x, y) &&
|
943
|
+
card.last? &&
|
944
|
+
card.opened? &&
|
945
|
+
(!mark || mark == card.mark) &&
|
946
|
+
card.number == last&.number.then {|n| n ? n + 1 : 1}
|
947
|
+
end
|
948
|
+
|
949
|
+
end# Klondike::MarkPlace
|
950
|
+
|
951
|
+
|
952
|
+
class Klondike::ColumnPlace < CardPlace
|
953
|
+
|
954
|
+
def initialize(*args, **kwargs, &block)
|
955
|
+
super(*args, linkCards: true, **kwargs, &block)
|
956
|
+
end
|
957
|
+
|
958
|
+
def accept?(x, y, card)
|
959
|
+
return false if !card || card.closed? || !card.canDrop?
|
960
|
+
if empty?
|
961
|
+
hit?(x, y) &&
|
962
|
+
card.number == 13
|
963
|
+
else
|
964
|
+
any? {|card| card.hit?(x, y)} &&
|
965
|
+
card.number == last.number - 1 &&
|
966
|
+
(@game.difficulty == :easy || card.color != last.color)
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
def posFor(card, index = nil)
|
971
|
+
index ||= indexFor card
|
972
|
+
super.tap do |pos|
|
973
|
+
pos.y += self.h * 0.3 * index
|
974
|
+
end
|
975
|
+
end
|
976
|
+
|
977
|
+
end# Klondike::ColumnPlace
|