rubysketch-solitaire 0.1.0 → 0.9.0
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/.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
|