rubysketch-solitaire 0.1.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 +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
|