1"use strict";
2
3const whl = require("whale-core");
4const pyxel = require("whale-pyxel");
5const mainLoop = require("whale/std/main_loop.js");
6
7
8function random(min, max)
9{
10 return min + Math.random() * (max - min);
11}
12
13
14function randomRange(vec)
15{
16 return random(vec.x, vec.y);
17}
18
19
20function midRange(vec)
21{
22 return (vec.x + vec.y) / 2;
23}
24
25
26var game = {
27 init: function()
28 {
29 this.windowSize = whl.Vec2(800, 600);
30 this.window = whl.Window(
31 "WhaleJS", whl.IVec2(100, 100), whl.IVec2(this.windowSize),
32 whl.Window.Options.OPENGL | whl.Window.Options.SHOWN
33 );
34 this.gfx = whl.RenderContext(this.window);
35 this.canvas = whl.Canvas(this.gfx, this.windowSize);
36
37 this.spriteSheet = whl.SpriteSheet(
38 whl.fs.openFile("atlas.json").read(),
39 whl.Texture(whl.loadPNG(whl.fs.openFile("atlas.png").read()))
40 );
41 this.backgroundSprite = this.spriteSheet.sprite("background");
42 this.logoSprite = this.spriteSheet.sprite("logo");
43
44 this.whaleDoc = pyxel.Doc(whl.fs.openFile("whale.pyxel").read());
45 this.bubbleSprite = whl.clone(this.whaleDoc.animation("bubble").frameAt(0).sprite);
46
47 this.whalePos = whl.Vec2(600, 150);
48 this.whaleScale = 8;
49 this.whaleScaleRange = whl.Vec2(3, 15);
50 this.whaleSpeed = -20;
51 this.bubbleSpeed = -5;
52 this.whaleAnim = "swim2";
53 this.offscreenFactor = 2;
54 this.offscreenDelay = Infinity;
55
56 this.bubbleBurstDelayRange = whl.Vec2(2, 5);
57 this.bubbleBurstCountRange = whl.Vec2(2, 4);
58 this.bubbleDelayRange = whl.Vec2(0.15, 0.5);
59 this.bubbles = [];
60 this.scheduleNextBubbleBurst();
61 this.timeToBubbleBurst = 1; // Force first burst early.
62 },
63
64 draw: function(time, dTime)
65 {
66 // Draw background.
67 this.canvas.draw(this.backgroundSprite);
68
69 // Draw whale (if small).
70 if (this.whaleScale < midRange(this.whaleScaleRange))
71 this.drawWhale(time, dTime);
72
73 // Draw bubbles (if small).
74 var bubbleCutoffY = -this.bubbleSprite.size.y * 0.5;
75 this.bubbles = this.bubbles.filter(
76 function(bubble) { return bubble.pos.y > bubbleCutoffY * bubble.scale; }
77 );
78
79 this.timeToBubbleBurst -= dTime;
80 if (this.timeToBubbleBurst < 0)
81 {
82 this.timeToBubble -= dTime;
83 if (this.bubbleBurstCount > 0 && this.timeToBubble <= 0)
84 {
85 this.bubbles.push({
86 pos: this.whalePos.add(whl.Vec2(this.whaleSpeed < 0 ? -8 : 6, -7).mul(this.whaleScale)),
87 scale: random(this.whaleScale * 0.5, this.whaleScale)
88 });
89 this.timeToBubble = randomRange(this.bubbleDelayRange);
90 --this.bubbleBurstCount;
91 }
92 if (this.bubbleBurstCount <= 0)
93 this.scheduleNextBubbleBurst();
94 }
95
96 this.drawBubbles(time, dTime, true);
97
98 // Draw logo.
99 var logoPos = this.windowSize.sub(this.logoSprite.size).div(2);
100 logoPos.y += 25 * Math.sin(time);
101 this.logoSprite.transform = whl.translate(logoPos);
102 this.canvas.draw(this.logoSprite);
103
104 // Draw whale and bubbles (if large).
105 if (this.whaleScale >= midRange(this.whaleScaleRange))
106 this.drawWhale(time, dTime);
107 this.drawBubbles(time, dTime, false);
108
109 this.window.redraw();
110
111 Duktape.gc();
112 },
113
114 scheduleNextBubbleBurst: function()
115 {
116 this.timeToBubbleBurst = randomRange(this.bubbleBurstDelayRange);
117 this.bubbleBurstCount = randomRange(this.bubbleBurstCountRange);
118 this.timeToBubble = 0;
119 },
120
121 drawWhale: function(time, dTime)
122 {
123 var whale = whl.clone(this.whaleDoc.animation(this.whaleAnim).frameAt(time).sprite);
124 this.offscreenDelay += dTime;
125 if (this.offscreenDelay > 2)
126 this.whalePos.x += this.whaleSpeed * this.whaleScale * dTime;
127 var whaleHalfSize = whale.size.mul(0.5 * this.whaleScale);
128 var preClampX = this.whalePos.x;
129 this.whalePos.x = whl.clamp(this.whalePos.x, -whaleHalfSize.x, this.windowSize.x + whaleHalfSize.x);
130 if (this.whalePos.x != preClampX)
131 {
132 this.whaleScale = randomRange(this.whaleScaleRange);
133 whaleHalfSize = whale.size.mul(0.5 * this.whaleScale);
134 this.whaleSpeed = -this.whaleSpeed;
135 this.whalePos.x = this.whaleSpeed > 0 ? -whaleHalfSize.x : this.windowSize.x + whaleHalfSize.x;
136 this.whalePos.y = random(whaleHalfSize.y, this.windowSize.y - whaleHalfSize.y);
137 this.offscreenDelay = 0;
138 }
139 whale.transform =
140 whl.translate(this.whalePos).mul(
141 whl.scale(whl.Vec2(this.whaleScale * -Math.sign(this.whaleSpeed), this.whaleScale)).mul(
142 whl.translate(whale.size.mul(-0.5))));
143
144 this.canvas.draw(whale);
145 },
146
147 drawBubbles: function(time, dTime, drawSmall)
148 {
149 for (var idx in this.bubbles)
150 {
151 var bubble = this.bubbles[idx];
152 var midScale = midRange(this.whaleScaleRange);
153 if ((drawSmall && bubble.scale >= midScale) ||
154 (!drawSmall && bubble.scale < midScale))
155 {
156 continue;
157 }
158 bubble.pos.y += this.bubbleSpeed * bubble.scale * dTime;
159 var xOffset = whl.Vec2((2 * bubble.scale) * Math.sin(bubble.pos.y / 25), 0);
160 this.bubbleSprite.transform = whl.translate(bubble.pos.add(xOffset)).mul(
161 whl.scale(whl.Vec2(bubble.scale, bubble.scale)).mul(
162 whl.translate(this.bubbleSprite.size.mul(-0.5))));
163 this.canvas.draw(this.bubbleSprite);
164 }
165 }
166};
167
168
169function loop()
170{
171 mainLoop.run(game);
172}