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