rubysketch-solitaire 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.github/workflows/release-gem.yml +62 -0
- data/.github/workflows/test.yml +23 -0
- data/.github/workflows/utils.rb +56 -0
- data/.gitignore +11 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +284 -0
- data/LICENSE +21 -0
- data/Podfile +31 -0
- data/Podfile.lock +59 -0
- data/README.md +21 -0
- data/Rakefile +100 -0
- data/RubySolitaire/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/Contents.json +98 -0
- data/RubySolitaire/Assets.xcassets/Contents.json +6 -0
- data/RubySolitaire/BridgingHeader.h +10 -0
- data/RubySolitaire/GameView.swift +47 -0
- data/RubySolitaire/Preview Content/Preview Assets.xcassets/Contents.json +6 -0
- data/RubySolitaire/RubySolitaireApp.swift +10 -0
- data/RubySolitaireTests/RubySolitaireTests.swift +28 -0
- data/RubySolitaireUITests/RubySolitaireUITests.swift +35 -0
- data/RubySolitaireUITests/RubySolitaireUITestsLaunchTests.swift +25 -0
- data/VERSION +1 -0
- data/data/button.mp3 +0 -0
- data/data/card.png +0 -0
- data/data/deal1.mp3 +0 -0
- data/data/deal2.mp3 +0 -0
- data/data/deal3.mp3 +0 -0
- data/data/flip.mp3 +0 -0
- data/data/noop.mp3 +0 -0
- data/lib/rubysketch/solitaire/background.rb +34 -0
- data/lib/rubysketch/solitaire/card.rb +256 -0
- data/lib/rubysketch/solitaire/common/animation.rb +116 -0
- data/lib/rubysketch/solitaire/common/button.rb +67 -0
- data/lib/rubysketch/solitaire/common/dialog.rb +103 -0
- data/lib/rubysketch/solitaire/common/history.rb +94 -0
- data/lib/rubysketch/solitaire/common/particle.rb +71 -0
- data/lib/rubysketch/solitaire/common/scene.rb +128 -0
- data/lib/rubysketch/solitaire/common/score.rb +37 -0
- data/lib/rubysketch/solitaire/common/settings.rb +31 -0
- data/lib/rubysketch/solitaire/common/shake.rb +48 -0
- data/lib/rubysketch/solitaire/common/sound.rb +6 -0
- data/lib/rubysketch/solitaire/common/timer.rb +35 -0
- data/lib/rubysketch/solitaire/common/transitions.rb +149 -0
- data/lib/rubysketch/solitaire/common/utils.rb +89 -0
- data/lib/rubysketch/solitaire/extension.rb +25 -0
- data/lib/rubysketch/solitaire/klondike.rb +676 -0
- data/lib/rubysketch/solitaire/places.rb +177 -0
- data/lib/rubysketch/solitaire/start.rb +19 -0
- data/lib/rubysketch/solitaire.rb +80 -0
- data/main.rb +1 -0
- data/project.yml +91 -0
- data/solitaire.gemspec +28 -0
- metadata +137 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
using RubySketch
|
2
|
+
|
3
|
+
|
4
|
+
EASINGS = {
|
5
|
+
linear: lambda { |x| x },
|
6
|
+
sineIn: lambda { |x| 1.0 - Math.cos(x * Math::PI / 2) },
|
7
|
+
sineOut: lambda { |x| Math.sin(x * Math::PI / 2) },
|
8
|
+
|
9
|
+
quadIn: lambda { |x| quadIn x },
|
10
|
+
cubicIn: lambda { |x| cubicIn x },
|
11
|
+
quartIn: lambda { |x| quartIn x },
|
12
|
+
quintIn: lambda { |x| quintIn x },
|
13
|
+
circIn: lambda { |x| circIn x },
|
14
|
+
backIn: lambda { |x| backIn x },
|
15
|
+
expoIn: lambda { |x| expoIn x },
|
16
|
+
elasticIn: lambda { |x| elasticIn x },
|
17
|
+
bounceIn: lambda { |x| 1.0 - bounceOut(1.0 - x) },
|
18
|
+
|
19
|
+
quadOut: lambda { |x| 1.0 - quadIn(1.0 - x) },
|
20
|
+
cubicOut: lambda { |x| 1.0 - cubicIn(1.0 - x) },
|
21
|
+
quartOut: lambda { |x| 1.0 - quartIn(1.0 - x) },
|
22
|
+
quintOut: lambda { |x| 1.0 - quintIn(1.0 - x) },
|
23
|
+
circOut: lambda { |x| 1.0 - curcIn(1.0 - x) },
|
24
|
+
backOut: lambda { |x| 1.0 - backIn(1.0 - x) },
|
25
|
+
expoOut: lambda { |x| 1.0 - expoIn(1.0 - x) },
|
26
|
+
elasticOut: lambda { |x| 1.0 - elasticIn(1.0 - x) },
|
27
|
+
bounceOut: lambda { |x| bounceOut x },
|
28
|
+
|
29
|
+
sineInOut: lambda { |x| x < 0.5 ? sineIn(x) : sineOut(x) },
|
30
|
+
quadInOut: lambda { |x| x < 0.5 ? quadIn(x) : quadOut(x) },
|
31
|
+
cubicInOut: lambda { |x| x < 0.5 ? cubicIn(x) : cubicOut(x) },
|
32
|
+
quartInOut: lambda { |x| x < 0.5 ? quartIn(x) : quartOut(x) },
|
33
|
+
quintInOut: lambda { |x| x < 0.5 ? quintIn(x) : quintOut(x) },
|
34
|
+
circInOut: lambda { |x| x < 0.5 ? circIn(x) : circOut(x) },
|
35
|
+
backInOut: lambda { |x| x < 0.5 ? backIn(x) : backOut(x) },
|
36
|
+
expoInOut: lambda { |x| x < 0.5 ? expoIn(x) : expoOut(x) },
|
37
|
+
elasticInOut: lambda { |x| x < 0.5 ? elasticIn(x) : elasticOut(x) },
|
38
|
+
bounceInOut: lambda { |x| x < 0.5 ? bounceIn(x) : bounceOut(x) }
|
39
|
+
}
|
40
|
+
|
41
|
+
def quadIn(x)
|
42
|
+
x ** 2
|
43
|
+
end
|
44
|
+
|
45
|
+
def cubicIn(x)
|
46
|
+
x ** 3
|
47
|
+
end
|
48
|
+
|
49
|
+
def quartIn(x)
|
50
|
+
x ** 4
|
51
|
+
end
|
52
|
+
|
53
|
+
def quintIn(x)
|
54
|
+
x ** 5
|
55
|
+
end
|
56
|
+
|
57
|
+
def circIn(x)
|
58
|
+
1.0 - Math.sqrt(1.0 - x ** 2)
|
59
|
+
end
|
60
|
+
|
61
|
+
def backIn(x)
|
62
|
+
2.70158 * x ** 3 - 1.70158 * x ** 2
|
63
|
+
end
|
64
|
+
|
65
|
+
def expoIn(x)
|
66
|
+
x == 0 ? 0.0 : 2.0 ** (10.0 * x - 10.0)
|
67
|
+
end
|
68
|
+
|
69
|
+
def elasticIn(x)
|
70
|
+
c = Math::PI * 2.0 / 3.0
|
71
|
+
case x
|
72
|
+
when 0 then 0
|
73
|
+
when 1 then 1
|
74
|
+
else -(2 ** (10.0 * x - 10.0)) * Math.sin((x * 10.0 - 10.75) * c)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def bounceOut(x)
|
79
|
+
n1, d1 = 7.5625, 2.75
|
80
|
+
case
|
81
|
+
when x < 1.0 / d1 then n1 * x * x
|
82
|
+
when x < 2.0 / d1 then x -= 1.5 / d1; n1 * x * x + 0.75;
|
83
|
+
when x < 2.5 / d1 then x -= 2.25 / d1; n1 * x * x + 0.9375;
|
84
|
+
else x -= 2.625 / d1; n1 * x * x + 0.984375;
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def animate(name = unique, seconds, ease: :expoOut, &block)
|
90
|
+
fun = EASINGS[ease]
|
91
|
+
start = now
|
92
|
+
eachDrawBlock = lambda do
|
93
|
+
t = (now - start) / seconds
|
94
|
+
if t >= 1.0
|
95
|
+
block.call fun.call(1.0), true, 1.0
|
96
|
+
else
|
97
|
+
block.call fun.call(t), false, t
|
98
|
+
startTimer name, 0, &eachDrawBlock
|
99
|
+
end
|
100
|
+
end
|
101
|
+
startTimer name, 0, &eachDrawBlock
|
102
|
+
end
|
103
|
+
|
104
|
+
def animateValue(name = unique, seconds, from:, to:, **kwargs, &block)
|
105
|
+
animate name, seconds, **kwargs do |t, finished, tt|
|
106
|
+
block.call lerp(from, to, t), finished, t, tt
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def move(obj, toPos, seconds, **kwargs, &block)
|
111
|
+
from, to = obj.pos.dup, toPos.dup
|
112
|
+
animate seconds, **kwargs do |t, *args|
|
113
|
+
obj.pos = Vector.lerp(from, to, t)
|
114
|
+
block&.call t, *args
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
using RubySketch
|
2
|
+
|
3
|
+
|
4
|
+
class Button < Sprite
|
5
|
+
|
6
|
+
include CanDisable
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
label, *args, rgb: 200, width: 1, fontSize: 24, round: 5, **kwargs, &block)
|
10
|
+
|
11
|
+
super 0, 0, 44 * width, 44, *args, **kwargs, &block
|
12
|
+
@label, @rgb, @fontSize, @round = label, [rgb].flatten, fontSize, round
|
13
|
+
@click = nil
|
14
|
+
setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def clicked(&block)
|
18
|
+
@click = block
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def setup()
|
25
|
+
pressing = false
|
26
|
+
mousePressed {pressing = true}
|
27
|
+
mouseReleased {pressing = false}
|
28
|
+
|
29
|
+
draw do
|
30
|
+
offset = 5
|
31
|
+
y = pressing ? (offset - (enabled? ? 2 : 3.5)) : 0
|
32
|
+
h = self.h - y
|
33
|
+
offset -= y
|
34
|
+
fill *@rgb.map {|n| n - 20}
|
35
|
+
rect 0, y, w, h, *@round
|
36
|
+
fill *@rgb
|
37
|
+
rect 0, y, w, h - offset, *@round
|
38
|
+
fill enabled? ? 255 : 180
|
39
|
+
textAlign CENTER, CENTER
|
40
|
+
textSize @fontSize
|
41
|
+
text @label, 0, y, w, h - offset
|
42
|
+
end
|
43
|
+
|
44
|
+
mouseClicked do
|
45
|
+
if enabled?
|
46
|
+
@click.call if @click
|
47
|
+
else
|
48
|
+
shake
|
49
|
+
end
|
50
|
+
sound.play gain: 0.5
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def shake(strength = 10)
|
55
|
+
strength = strength.to_f
|
56
|
+
x = self.x
|
57
|
+
animate 0.3 do |t, finished|
|
58
|
+
self.x = x + (finished ? 0 : strength * (1.0 - t))
|
59
|
+
strength *= -1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def sound()
|
64
|
+
@sound ||= loadSound dataPath 'button.mp3'
|
65
|
+
end
|
66
|
+
|
67
|
+
end# Button
|
@@ -0,0 +1,103 @@
|
|
1
|
+
using RubySketch
|
2
|
+
|
3
|
+
|
4
|
+
class Dialog < Scene
|
5
|
+
|
6
|
+
def initialize(background: 0, alpha: 100, z: 1000)
|
7
|
+
super
|
8
|
+
@background, @alpha = background, 0
|
9
|
+
overlay.z = z
|
10
|
+
animate 0.2 do |t|
|
11
|
+
@alpha = alpha * t
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def addElement(sprite)
|
16
|
+
elements.push sprite
|
17
|
+
addSprite sprite if active?
|
18
|
+
updateLayout
|
19
|
+
end
|
20
|
+
|
21
|
+
def addLabel(label, rgb: [255], fontSize: 24, align: CENTER)
|
22
|
+
bounds = textFont.textBounds label, 0, 0, fontSize
|
23
|
+
addElement Sprite.new(0, 0, bounds.w, bounds.h).tap {|sp|
|
24
|
+
sp.z = overlay.z
|
25
|
+
sp.draw do
|
26
|
+
textAlign align, CENTER
|
27
|
+
textSize fontSize
|
28
|
+
fill *rgb
|
29
|
+
text label, 0, 0, sp.w, sp.h
|
30
|
+
end
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def addButton(label, *args, **kwargs, &block)
|
35
|
+
addElement Button.new(label, *args, **kwargs).tap {|b|
|
36
|
+
b.z = overlay.z
|
37
|
+
b.clicked &block
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def addSpace(height)
|
42
|
+
addElement Sprite.new(0, 0, 1, height).tap {|sp|
|
43
|
+
sp.draw {}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def close()
|
48
|
+
delay {parent.remove self}
|
49
|
+
end
|
50
|
+
|
51
|
+
def sprites()
|
52
|
+
super + [overlay, *elements]
|
53
|
+
end
|
54
|
+
|
55
|
+
def elements()
|
56
|
+
@elements ||= []
|
57
|
+
end
|
58
|
+
|
59
|
+
def draw()
|
60
|
+
sprite overlay, *elements
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
def resized(w, h)
|
65
|
+
updateLayout
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
MARGIN = 10
|
71
|
+
|
72
|
+
def overlay()
|
73
|
+
@overlay ||= Sprite.new(0, 0, width, height).tap do |sp|
|
74
|
+
sp.update do
|
75
|
+
sp.w = width
|
76
|
+
sp.h = height
|
77
|
+
end
|
78
|
+
sp.draw do
|
79
|
+
fill *@background, @alpha
|
80
|
+
rect 0, 0, sp.w, sp.h
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def cancelButton()
|
86
|
+
@cancelButton ||= Button.new('CLOSE', width: 3, fontSize: 28).tap do |sp|
|
87
|
+
sp.z = overlay.z
|
88
|
+
sp.clicked {close}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def updateLayout()
|
93
|
+
w, h = width, height
|
94
|
+
allHeight = elements.map(&:height).reduce {|a, b| a + MARGIN + b} || 0
|
95
|
+
y = (h - allHeight) / 2
|
96
|
+
elements.each do |e|
|
97
|
+
e.x = (w - e.w) / 2
|
98
|
+
e.y = y
|
99
|
+
y += e.h + MARGIN
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end# Dialog
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class History
|
2
|
+
|
3
|
+
include CanDisable
|
4
|
+
|
5
|
+
def initialize(undos = [], redos = [])
|
6
|
+
super()
|
7
|
+
@undos, @redos = undos, redos
|
8
|
+
@group = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def push(*actions)
|
12
|
+
return if actions.empty? || disabled?
|
13
|
+
if @group
|
14
|
+
@group.push *actions
|
15
|
+
else
|
16
|
+
@undos.push actions
|
17
|
+
@redos.clear
|
18
|
+
update
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def group(&block)
|
23
|
+
@group = group = [] unless @group
|
24
|
+
block.call
|
25
|
+
ensure
|
26
|
+
if group
|
27
|
+
@group = nil
|
28
|
+
push *group
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def undo(&block)
|
33
|
+
actions = @undos.pop || return
|
34
|
+
actions.reverse.each {|action| block.call action}
|
35
|
+
@redos.push actions
|
36
|
+
update
|
37
|
+
end
|
38
|
+
|
39
|
+
def redo(&block)
|
40
|
+
actions = @redos.pop || return
|
41
|
+
actions.each {|action| block.call action}
|
42
|
+
@undos.push actions
|
43
|
+
update
|
44
|
+
end
|
45
|
+
|
46
|
+
def canUndo?()
|
47
|
+
!@undos.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def canRedo?()
|
51
|
+
!@redos.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def updated(&block)
|
55
|
+
@updated = block
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_h(&dumpObject)
|
59
|
+
{
|
60
|
+
version: 1,
|
61
|
+
undos: self.class.dump(@undos, &dumpObject),
|
62
|
+
redos: self.class.dump(@redos, &dumpObject)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.load(hash, &restoreObject)
|
67
|
+
undos = restore hash['undos'], &restoreObject
|
68
|
+
redos = restore hash['redos'], &restoreObject
|
69
|
+
self.new undos, redos
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def update()
|
75
|
+
@updated.call if @updated
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.dump(xdos, &dumpObject)
|
79
|
+
xdos.map do |actions|
|
80
|
+
actions.map do |action, *args|
|
81
|
+
[action.to_s, *args.map {|obj| dumpObject.call(obj) || obj}]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.restore(xdos, &restoreObject)
|
87
|
+
xdos.map do |actions|
|
88
|
+
actions.map do |action, *args|
|
89
|
+
[action.intern, *args.map {|obj| restoreObject.call(obj) || obj}]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end# History
|
@@ -0,0 +1,71 @@
|
|
1
|
+
using RubySketch
|
2
|
+
|
3
|
+
|
4
|
+
class Particle
|
5
|
+
|
6
|
+
include HasSprite
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
@sprites = []
|
10
|
+
@pool = []
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :sprites
|
14
|
+
|
15
|
+
def new(x, y, w, h, &block)
|
16
|
+
sp = newSprite x, y, w, h
|
17
|
+
addSprite sp
|
18
|
+
@sprites.push sp
|
19
|
+
block.call sp if block
|
20
|
+
sp
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(sprite)
|
24
|
+
@sprites.delete sprite
|
25
|
+
removeSprite sprite
|
26
|
+
@pool.push sprite
|
27
|
+
sprite
|
28
|
+
end
|
29
|
+
|
30
|
+
def draw()
|
31
|
+
drawSprite @sprites
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def newSprite(x, y, w, h)
|
37
|
+
popSprite(x, y, w, h) || ParticleSprite.new(self, x, y, w, h)
|
38
|
+
end
|
39
|
+
|
40
|
+
def popSprite(x, y, w, h)
|
41
|
+
@pool.pop&.tap do |sp|
|
42
|
+
sp.frame = [x, y, w, h]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end# Particle
|
47
|
+
|
48
|
+
|
49
|
+
class ParticleSprite < Sprite
|
50
|
+
|
51
|
+
def initialize(owner, *args, rgb: [255], alpha: 255, **kwargs, &block)
|
52
|
+
@owner, @rgb, @alpha = owner, rgb, alpha
|
53
|
+
super(*args, **kwargs, &block)
|
54
|
+
draw do |&draw|
|
55
|
+
fill *@rgb, @alpha
|
56
|
+
draw.call
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_accessor :rgb, :alpha
|
61
|
+
|
62
|
+
def frame=(frame)
|
63
|
+
self.pos = frame[0, 2]
|
64
|
+
self.size = frame[2, 2]
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete()
|
68
|
+
@owner.delete self
|
69
|
+
end
|
70
|
+
|
71
|
+
end# ParticleSprite
|
@@ -0,0 +1,128 @@
|
|
1
|
+
using RubySketch
|
2
|
+
|
3
|
+
|
4
|
+
class Scene
|
5
|
+
|
6
|
+
def initialize(name = self.class.name, *scenes)
|
7
|
+
@name = name
|
8
|
+
@scenes = []
|
9
|
+
@active = self.class == RootScene
|
10
|
+
@prevSize = [width, height]
|
11
|
+
resized *@prevSize
|
12
|
+
scenes.each {|scene| add scene}
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :name, :parent
|
16
|
+
|
17
|
+
def add(scene)
|
18
|
+
@scenes.push scene
|
19
|
+
scene.parent = self
|
20
|
+
scene.activated if active?
|
21
|
+
scene
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove(scene)
|
25
|
+
@scenes.delete scene
|
26
|
+
scene.deactivated if active?
|
27
|
+
scene.parent = nil
|
28
|
+
scene
|
29
|
+
end
|
30
|
+
|
31
|
+
def transition(to, effect = nil, *args, **kwargs)
|
32
|
+
if effect
|
33
|
+
add effect.new(to, *args, **kwargs)
|
34
|
+
else
|
35
|
+
parent.add to
|
36
|
+
parent.remove self
|
37
|
+
end
|
38
|
+
to
|
39
|
+
end
|
40
|
+
|
41
|
+
def emitParticle(x, y, w, h, sec = nil, &block)
|
42
|
+
par = particle.new x, y, w, h
|
43
|
+
start = now
|
44
|
+
par.update do
|
45
|
+
time = now - start
|
46
|
+
t = sec ? time / sec : nil
|
47
|
+
result = block&.call time, t
|
48
|
+
if result == false || (t || 1) >= 1
|
49
|
+
par.update {}
|
50
|
+
par.delete
|
51
|
+
end
|
52
|
+
end
|
53
|
+
par
|
54
|
+
end
|
55
|
+
|
56
|
+
def sprites()
|
57
|
+
particle.sprites
|
58
|
+
end
|
59
|
+
|
60
|
+
def particle()
|
61
|
+
@particle ||= Particle.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def update()
|
65
|
+
size = [width, height]
|
66
|
+
if size != @prevSize
|
67
|
+
resized(*size)
|
68
|
+
@prevSize = size
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def draw()
|
73
|
+
update
|
74
|
+
@scenes.each do |scene|
|
75
|
+
push {scene.draw}
|
76
|
+
end
|
77
|
+
push do
|
78
|
+
blendMode ADD
|
79
|
+
particle.draw
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def resized(w, h)
|
84
|
+
end
|
85
|
+
|
86
|
+
def activated()
|
87
|
+
@active = true
|
88
|
+
@scenes.each {|scene| scene.activated}
|
89
|
+
sprites.each {|sprite| addSprite sprite}
|
90
|
+
end
|
91
|
+
|
92
|
+
def deactivated()
|
93
|
+
sprites.each {|sprite| removeSprite sprite}
|
94
|
+
@scenes.each {|scene| scene.deactivated}
|
95
|
+
@active = false
|
96
|
+
end
|
97
|
+
|
98
|
+
def active?()
|
99
|
+
@active
|
100
|
+
end
|
101
|
+
|
102
|
+
def mousePressed(x, y, button)
|
103
|
+
@scenes.each {|scene| scene.mousePressed x, y, button}
|
104
|
+
end
|
105
|
+
|
106
|
+
def mouseReleased(x, y, button)
|
107
|
+
@scenes.each {|scene| scene.mouseReleased x, y, button}
|
108
|
+
end
|
109
|
+
|
110
|
+
def mouseMoved(x, y, dx, dy)
|
111
|
+
@scenes.each {|scene| scene.mouseMoved x, y, dx, dy}
|
112
|
+
end
|
113
|
+
|
114
|
+
def mouseDragged(x, y, dx, dy)
|
115
|
+
@scenes.each {|scene| scene.mouseDragged x, y, dx, dy}
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
def parent=(scene)
|
121
|
+
@parent = scene
|
122
|
+
end
|
123
|
+
|
124
|
+
end# Scene
|
125
|
+
|
126
|
+
|
127
|
+
class RootScene < Scene
|
128
|
+
end# RootScene
|
@@ -0,0 +1,37 @@
|
|
1
|
+
using RubySketch
|
2
|
+
|
3
|
+
|
4
|
+
class Score
|
5
|
+
|
6
|
+
include CanDisable
|
7
|
+
|
8
|
+
def initialize(**definitions)
|
9
|
+
super()
|
10
|
+
@defs, @value = {}, 0
|
11
|
+
define **definitions
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :value
|
15
|
+
|
16
|
+
def define(**definitions)
|
17
|
+
definitions.each do |name, score|
|
18
|
+
@defs[name.intern] = score
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(name)
|
23
|
+
return if disabled?
|
24
|
+
value = @defs[name.intern]
|
25
|
+
@value += value.to_i if value
|
26
|
+
@value = 0 if @value < 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_h()
|
30
|
+
{score: @value}
|
31
|
+
end
|
32
|
+
|
33
|
+
def load(hash)
|
34
|
+
@value = hash['score']
|
35
|
+
end
|
36
|
+
|
37
|
+
end# Score
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Settings
|
2
|
+
|
3
|
+
def initialize(path)
|
4
|
+
@path = path or raise 'invalid path'
|
5
|
+
@hash = load path
|
6
|
+
end
|
7
|
+
|
8
|
+
def []=(key, value)
|
9
|
+
hash[key] = value
|
10
|
+
save @path
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
hash[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def hash()
|
20
|
+
@hash ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def save(path)
|
24
|
+
File.write path, hash.to_json
|
25
|
+
end
|
26
|
+
|
27
|
+
def load(path)
|
28
|
+
@hash = File.exist?(path) ? JSON.parse(File.read path) : {}
|
29
|
+
end
|
30
|
+
|
31
|
+
end# Settings
|
@@ -0,0 +1,48 @@
|
|
1
|
+
using RubySketch
|
2
|
+
|
3
|
+
|
4
|
+
class Shake
|
5
|
+
|
6
|
+
def initialize(length, vector)
|
7
|
+
@length, @vector = length, vector
|
8
|
+
end
|
9
|
+
|
10
|
+
def update()
|
11
|
+
@length *= 0.8 if @length
|
12
|
+
@vector *= -0.8 if @vector
|
13
|
+
v = vector
|
14
|
+
@length = @vector = nil if v && v.mag < 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def vector()
|
18
|
+
return nil unless @length || @vector
|
19
|
+
v = @vector&.dup&.rotate(rand -20.0...20.0) || Vector.random2D
|
20
|
+
v *= @length if @length
|
21
|
+
v
|
22
|
+
end
|
23
|
+
|
24
|
+
end# Shake
|
25
|
+
|
26
|
+
|
27
|
+
def shake(obj, length = nil, vector: nil)
|
28
|
+
shake = Shake.new length, vector
|
29
|
+
pos = obj.pos.dup
|
30
|
+
fun = -> do
|
31
|
+
v = shake.vector
|
32
|
+
break obj.pos = pos unless v
|
33
|
+
obj.pos = pos + v
|
34
|
+
shake.update
|
35
|
+
delay {fun.call}
|
36
|
+
end
|
37
|
+
fun.call
|
38
|
+
end
|
39
|
+
|
40
|
+
def shakeScreen(length = nil, vector: nil)
|
41
|
+
$shake = Shake.new length, vector
|
42
|
+
end
|
43
|
+
|
44
|
+
def drawShake()
|
45
|
+
v = $shake&.vector
|
46
|
+
translate v.x, v.y if v
|
47
|
+
$shake&.update
|
48
|
+
end
|