rubysketch-solitaire 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -0
  3. data/.gitignore +2 -0
  4. data/.hooks/pre-commit +69 -0
  5. data/ChangeLog.md +21 -0
  6. data/Gemfile.lock +10 -10
  7. data/Podfile.lock +33 -33
  8. data/Rakefile +107 -10
  9. data/RubySolitaire/Ad.swift +88 -0
  10. data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
  11. data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/Contents.json +3 -87
  12. data/RubySolitaire/Base.lproj/Localizable.strings +6 -0
  13. data/RubySolitaire/Extensions.swift +5 -0
  14. data/RubySolitaire/GameView.swift +75 -13
  15. data/RubySolitaire/Helper.swift +70 -0
  16. data/RubySolitaire/MenuScreen.swift +140 -0
  17. data/RubySolitaire/RubySolitaireApp.swift +188 -1
  18. data/RubySolitaire/SafariView.swift +16 -0
  19. data/RubySolitaire/Strings.swift +48 -0
  20. data/RubySolitaire/ja.lproj/InfoPlist.strings +4 -0
  21. data/RubySolitaire/ja.lproj/Localizable.strings +6 -0
  22. data/VERSION +1 -1
  23. data/data/card.png +0 -0
  24. data/data/classicPSPWave.glsl +94 -0
  25. data/data/colorfulUnderwaterBubbles2.glsl +113 -0
  26. data/data/cosmic2.glsl +121 -0
  27. data/data/reflectiveHexes.glsl +219 -0
  28. data/fastlane/Fastfile +76 -0
  29. data/fastlane/metadata/copyright.txt +1 -0
  30. data/fastlane/metadata/en-US/apple_tv_privacy_policy.txt +1 -0
  31. data/fastlane/metadata/en-US/description.txt +5 -0
  32. data/fastlane/metadata/en-US/keywords.txt +1 -0
  33. data/fastlane/metadata/en-US/marketing_url.txt +1 -0
  34. data/fastlane/metadata/en-US/name.txt +1 -0
  35. data/fastlane/metadata/en-US/privacy_url.txt +1 -0
  36. data/fastlane/metadata/en-US/promotional_text.txt +1 -0
  37. data/fastlane/metadata/en-US/release_notes.txt +1 -0
  38. data/fastlane/metadata/en-US/subtitle.txt +1 -0
  39. data/fastlane/metadata/en-US/support_url.txt +1 -0
  40. data/fastlane/metadata/ja/apple_tv_privacy_policy.txt +1 -0
  41. data/fastlane/metadata/ja/description.txt +5 -0
  42. data/fastlane/metadata/ja/keywords.txt +1 -0
  43. data/fastlane/metadata/ja/marketing_url.txt +1 -0
  44. data/fastlane/metadata/ja/name.txt +1 -0
  45. data/fastlane/metadata/ja/privacy_url.txt +1 -0
  46. data/fastlane/metadata/ja/promotional_text.txt +1 -0
  47. data/fastlane/metadata/ja/release_notes.txt +1 -0
  48. data/fastlane/metadata/ja/subtitle.txt +1 -0
  49. data/fastlane/metadata/ja/support_url.txt +1 -0
  50. data/fastlane/metadata/primary_category.txt +1 -0
  51. data/fastlane/metadata/primary_first_sub_category.txt +1 -0
  52. data/fastlane/metadata/primary_second_sub_category.txt +1 -0
  53. data/fastlane/metadata/review_information/demo_password.txt +1 -0
  54. data/fastlane/metadata/review_information/demo_user.txt +1 -0
  55. data/fastlane/metadata/review_information/email_address.txt +1 -0
  56. data/fastlane/metadata/review_information/first_name.txt +0 -0
  57. data/fastlane/metadata/review_information/last_name.txt +0 -0
  58. data/fastlane/metadata/review_information/notes.txt +1 -0
  59. data/fastlane/metadata/review_information/phone_number.txt +0 -0
  60. data/fastlane/metadata/secondary_category.txt +1 -0
  61. data/fastlane/metadata/secondary_first_sub_category.txt +1 -0
  62. data/fastlane/metadata/secondary_second_sub_category.txt +1 -0
  63. data/lib/rubysketch/solitaire/background.rb +115 -12
  64. data/lib/rubysketch/solitaire/card.rb +10 -87
  65. data/lib/rubysketch/solitaire/common/animation.rb +34 -34
  66. data/lib/rubysketch/solitaire/common/button.rb +49 -19
  67. data/lib/rubysketch/solitaire/common/dialog.rb +78 -21
  68. data/lib/rubysketch/solitaire/common/scene.rb +15 -4
  69. data/lib/rubysketch/solitaire/common/settings.rb +10 -2
  70. data/lib/rubysketch/solitaire/common/timer.rb +2 -2
  71. data/lib/rubysketch/solitaire/common/transitions.rb +12 -6
  72. data/lib/rubysketch/solitaire/common/utils.rb +15 -0
  73. data/lib/rubysketch/solitaire/klondike.rb +378 -81
  74. data/lib/rubysketch/solitaire/places.rb +12 -102
  75. data/lib/rubysketch/solitaire/skin.rb +151 -0
  76. data/lib/rubysketch/solitaire.rb +33 -9
  77. data/main.rb +1 -0
  78. data/project.yml +85 -4
  79. metadata +54 -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
- drawCount: nexts.drawCount,
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
- nexts.drawCount = hash['drawCount']
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
- resume
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['bestTime'] || 24 * 60 * 60 - 1
184
+ settings[bestRecordKey :time] || 24 * 60 * 60 - 1
156
185
  end
157
186
 
158
187
  def bestScore()
159
- settings['bestScore'] || 0
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 = elapsedTime < bestTime
164
- newScore = score.value > bestScore
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
211
+
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
221
+
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
165
245
 
166
- settings['bestTime'] = elapsedTime if newTime
167
- settings['bestScore'] = score.value if newScore
246
+ def timeToText(time)
247
+ Time.at(time).strftime('%M:%S')
248
+ end
168
249
 
169
- return newTime, newScore
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, menuButton, finishButton, status, debugButton]
300
+ [undoButton, redoButton, pauseButton, finishButton, status, debugButton]
219
301
  end
220
302
 
221
303
  def undoButton()
222
304
  @undoButton ||= Button.new(
223
- '◀', rgb: [120, 140, 160], fontSize: 28, round: [20, 4, 4, 20]
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
- '▶', rgb: [160, 140, 120], fontSize: 28, round: [4, 20, 20, 4]
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 menuButton()
240
- @menuButton ||= Button.new(
241
- '≡', rgb: [140, 160, 120], fontSize: 36
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 0, 20
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,174 @@ 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 1
372
+ d.addButton str('EASY'), width: 5 do
373
+ start :easy
294
374
  d.close
295
375
  end
296
- d.addButton 'HARD', width: 5 do
297
- start 3
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 showMenuDialog()
304
- pause
305
- add Dialog.new.tap {|d|
306
- d.addButton 'RESUME', width: 5 do
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
396
+ d.close
397
+ end
398
+ d.addButton str('New Game'), width: 6 do
307
399
  d.close
308
- resume
400
+ showNewGameDialog
309
401
  end
310
- d.addButton 'NEW GAME', width: 5 do
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 showCompletedDialog(newBestTime = false, newBestScore = false)
320
- pause
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} #{newBestTime ? '(NEW!)' : ''}",
476
+ "#{str 'Time'}: #{timeToText elapsedTime} #{suffix.call bestTime, dailyBestTime}",
325
477
  fontSize: 28)
326
478
  d.addLabel(
327
- "Score: #{score.value} #{newBestScore ? '(NEW!)' : ''}",
479
+ "#{str 'Score'}: #{score.value} #{suffix.call bestScore, dailyBestScore}",
328
480
  fontSize: 28)
329
481
  d.addSpace 50
330
- d.addButton 'NEW GAME', width: 5 do
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('Close'), width: 6 do
514
+ d.close
515
+ end
516
+ }
517
+ end
518
+
336
519
  def updateLayout(w, h)
337
520
  card = cards.first
338
521
  cw, ch = card.then {|c| [c.w, c.h]}
339
- mx, my = Card.margin, cw * 0.2 # margin x, y
522
+ mx, my = skin.margin, cw * 0.2 # margin x, y
340
523
  y = my
341
524
 
342
- undoButton.pos = [mx, y]
343
- redoButton.pos = [undoButton.x + undoButton.w + 2, y]
344
- menuButton.pos = [width - (menuButton.w + mx), y]
345
- status.pos = [redoButton.right + mx, y]
346
- status.right = menuButton.left - mx
347
- status.height = menuButton.h
525
+ undoButton.pos = [mx, y]
526
+ redoButton.pos = [undoButton.x + undoButton.w + 2, y]
527
+ pauseButton.pos = [width - (pauseButton.w + mx), y]
528
+ status.pos = [redoButton.right + mx, y]
529
+ status.right = pauseButton.left - mx
530
+ status.height = pauseButton.h
348
531
 
349
- y = undoButton.y + undoButton.h + my
532
+ y = undoButton.y + undoButton.h + my * 3
350
533
 
351
534
  deck.pos = [w - (deck.w + mx), y]
352
535
  nexts.pos = [deck.x - (nexts.w + mx), deck.y]
@@ -379,8 +562,9 @@ class Klondike < Scene
379
562
  end
380
563
  end
381
564
 
382
- def start(drawCount = 1)
383
- nexts.drawCount = drawCount
565
+ def start(difficulty = :normal)
566
+ @difficulty = difficulty
567
+ start!
384
568
 
385
569
  history.disable
386
570
  lasts = columns.map(&:last).compact
@@ -390,7 +574,6 @@ class Klondike < Scene
390
574
  if lasts.all? {|card| card.opened?}
391
575
  drawNexts
392
576
  history.enable
393
- resume
394
577
  end
395
578
  end
396
579
  end
@@ -411,7 +594,7 @@ class Klondike < Scene
411
594
 
412
595
  def firstDistribution()
413
596
  n = columns.size
414
- (0...n).map { |row| (row...n).map { |col| [col, row] } }.flatten(1)
597
+ (0...n).map {|row| (row...n).map {|col| [col, row]}}.flatten(1)
415
598
  end
416
599
 
417
600
  def openCard(card, gain: 0.5)
@@ -471,6 +654,19 @@ class Klondike < Scene
471
654
  completed if completed?
472
655
  end
473
656
 
657
+ def start!()
658
+ @started = true
659
+ [*cards, *places].each do |o|
660
+ o.started if o.respond_to? :started
661
+ end
662
+ places.each {|p| p.updateCards 0}
663
+ resume
664
+ end
665
+
666
+ def started?()
667
+ @started ||= false
668
+ end
669
+
474
670
  def canFinish?()
475
671
  deck.empty? && nexts.empty? &&
476
672
  columns.any? {|col| !col.empty?} &&
@@ -525,7 +721,7 @@ class Klondike < Scene
525
721
 
526
722
  def showFinishButton()
527
723
  finishButton.tap do |b|
528
- m = Card.margin
724
+ m = skin.margin
529
725
  b.x = marks.last.then {|mark| mark.x + mark.w} + m * 2
530
726
  b.y = -deck.h
531
727
  b.w = width - b.x - m
@@ -588,6 +784,7 @@ class Klondike < Scene
588
784
  end
589
785
 
590
786
  def backToPlace(card, vel)
787
+ return if vel.mag == 0
591
788
  vec = vel.dup.normalize * sqrt(vel.mag) / 10 * sqrt(card.count)
592
789
  return if vec.mag < 3
593
790
  shakeScreen vector: vec
@@ -623,28 +820,6 @@ class Klondike < Scene
623
820
  end
624
821
  end
625
822
 
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
823
  def undo(action)
649
824
  history.disable do
650
825
  case action
@@ -670,7 +845,129 @@ class Klondike < Scene
670
845
  end
671
846
 
672
847
  def startNewGame()
673
- transition self.class.new, [Fade, Curtain, Pixelate].sample
848
+ $newGameCount ||= 0
849
+ $newGameCount += 1
850
+ showAd = $newGameCount % 3 == 0
851
+ transition self.class.new, [Fade, Curtain, Pixelate].sample, showAd: showAd
852
+ end
853
+
854
+ STRINGS = {
855
+ OK: {},
856
+ Cancel: {ja: 'キャンセル'},
857
+ Close: {ja: '閉じる'},
858
+
859
+ Time: {ja: 'タイム'},
860
+ Score: {ja: 'スコア'},
861
+ Move: {ja: '移動回数'},
862
+
863
+ Difficulty: {ja: '難易度'},
864
+ EASY: {ja: '簡単'},
865
+ NORMAL: {ja: '普通'},
866
+ HARD: {ja: '難しい'},
867
+
868
+ 'New Game': {ja: '新規ゲーム'},
869
+ 'Resume': {ja: 'ゲーム再開'},
870
+
871
+ 'Best Time': {ja: 'ベストタイム'},
872
+ 'Best Score': {ja: 'ベストスコア'},
873
+ "Today's Best Time": {ja: '本日のベストタイム'},
874
+ "Today's Best Score": {ja: '本日のベストスコア'},
875
+
876
+ "Start New Game?": {ja: '新しいゲームをはじめますか?'},
877
+ "Start Next Game": {ja: '次のゲームを開始'},
878
+
879
+ "Change Card Design": {ja: 'カードデザインを変更'},
880
+ "Change Background": {ja: 'ゲーム背景を変更'},
881
+
882
+ '(New Record!)': {ja: '(新記録!)'},
883
+ "(Today's Best!)": {ja: '(本日のベスト!)'},
884
+ }
885
+
886
+ def str(s, lang: $language)
887
+ STRINGS.dig(s.intern, lang&.intern) || s.to_s
674
888
  end
675
889
 
676
890
  end# Klondike
891
+
892
+
893
+ class Klondike::NextsPlace < CardPlace
894
+
895
+ def drawCount()
896
+ @game.difficulty == :hard ? 3 : 1
897
+ end
898
+
899
+ def started()
900
+ w = skin.cardSpriteSize[0] + overlap * (drawCount - 1)
901
+ self.x -= w - self.w
902
+ self.w = w
903
+ end
904
+
905
+ def add(*cards, **kwargs)
906
+ super
907
+ updateCards excludes: cards
908
+ end
909
+
910
+ def pop(*args)
911
+ super
912
+ updateCards
913
+ end
914
+
915
+ def posFor(card, index = nil)
916
+ index ||= indexFor card
917
+ super.tap do |pos|
918
+ rindex = cards.size - index
919
+ pos.x += overlap * (drawCount - rindex).clamp(0, drawCount - 1)
920
+ end
921
+ end
922
+
923
+ def overlap()
924
+ skin.cardSpriteSize[0] * 0.4
925
+ end
926
+
927
+ end# Klondike::NextsPlace
928
+
929
+
930
+ class Klondike::MarkPlace < CardPlace
931
+
932
+ def mark()
933
+ last&.mark
934
+ end
935
+
936
+ def accept?(x, y, card)
937
+ return false if !card || card.closed? || !card.canDrop?
938
+ hit?(x, y) &&
939
+ card.last? &&
940
+ card.opened? &&
941
+ (!mark || mark == card.mark) &&
942
+ card.number == last&.number.then {|n| n ? n + 1 : 1}
943
+ end
944
+
945
+ end# Klondike::MarkPlace
946
+
947
+
948
+ class Klondike::ColumnPlace < CardPlace
949
+
950
+ def initialize(*args, **kwargs, &block)
951
+ super(*args, linkCards: true, **kwargs, &block)
952
+ end
953
+
954
+ def accept?(x, y, card)
955
+ return false if !card || card.closed? || !card.canDrop?
956
+ if empty?
957
+ hit?(x, y) &&
958
+ card.number == 13
959
+ else
960
+ any? {|card| card.hit?(x, y)} &&
961
+ card.number == last.number - 1 &&
962
+ (@game.difficulty == :easy || card.color != last.color)
963
+ end
964
+ end
965
+
966
+ def posFor(card, index = nil)
967
+ index ||= indexFor card
968
+ super.tap do |pos|
969
+ pos.y += self.h * 0.3 * index
970
+ end
971
+ end
972
+
973
+ end# Klondike::ColumnPlace