for(_='](~c[c.`;`_"_fc~^Math.ZZcos(Y()XfillXWZsin(VVf/U]=TT"#R0,Q,Q7Pba~O;t(NforL.b,KQ2J.aH16G,bB);AA`l~@8B-4@a5,276@=-h*l+Ac.scale(*aX*d-db7aa45"&&(=d+1A0>255, GB+4+e@aaBm(,e,d,g)=32+32*eX,_ta~var 20_Oarc(,Q300)_bC~ 10D)*aH*(1+h)k[a]strokeXfunction_fyRW_OA`m~L(v in c)c[v[0]+[v[6]]Tv;f=Qk=[]; {b=b+2;e=e+1/2;d=d+*aX-8;0>b-dbe-de=d||(0>g?g=0:0<gg=,`fyT"rgba(0.5)",`O),cHrc(b,e,dP),c.W,)} n(a){if(a){b=aKe=X{b=(8*b+9)%7;return b/6},dgl=g-d,h=aH/400*8,CQl15QD=7*aKr=Y;a=VC+r,l+ah,hAm(e,QQg,d1/h,1/h)-C-r,-l-a)}} t(){`sSR_ldT3GPA`m~+G@+0}setInterval(X{f++;L(a=8;a--;)||(={b:ZrandomX,a:9}),H+=.3*K0<H=aabbFF^QQ406D8448"5 GJ,GQ3,087a45a" QQQ 30J9J5Q400N19QN15J3N23J4;k.LEach(nAa=0+0*Yf/5B=150+*U,e=0<U4)?-9:0@a---8B@a+++8Bdbcb53"4P;a=*U5a,7c652e^198,24Q4,6_sSRffe0bd"_ldT7195@18199,2642,305@211,252-a,},17A';G=/[^ -?C-FIMS[\]a-}]/.exec(_);)with(_.split(G))_=join(shift());eval(_)
// a = Canvas Element
// c = 2d Context
const canvasWidth = 400;
const canvasHeight = 300;
let time = 0;
const skyColor = '#aabbFF';
const skinColor = '#ffe0bd';
const broomColor = '#7c652e';
const snitchColor = '#dbcb53';
const snitchWingColor = '#b7aa45';
const cloudColor = 'rgba(255, 255, 255, 0.5)';
const broomWidth = 4;
const broomDepth = 60;
const broomLeft = canvasWidth / 2 - broomWidth / 2;
const broomTop = canvasHeight - broomDepth;
const armDistanceFromBroom = 8;
const maxClouds = 8;
const maxCloudDistance = canvasWidth;
const minCloudRadius = 32;
const maxCloudSpeed = .3;
const wingWidth = 16;
const wingHeight = 8;
const MathPI2 = 7;
const clouds = [];
function renderSkyBackground() {
c.fillStyle = skyColor;
c.fillRect(0, 0, canvasWidth, canvasHeight);
}
function renderArmsAndBroomstick() {
const armsMovement = Math.sin(time / 50) * 20;
c.translate(armsMovement, 0);
// Render broom
c.fillStyle = broomColor;
c.fillRect(
broomLeft,
broomTop,
broomWidth,
broomDepth
);
// Render arms
c.strokeStyle = skinColor;
c.lineWidth = 7;
c.beginPath();
c.moveTo(broomLeft - armDistanceFromBroom, canvasHeight + 5); // add a bit to make offscreen
c.lineTo(broomLeft - armDistanceFromBroom - 5, canvasHeight - broomDepth * .4);
c.lineTo(broomLeft + 1, canvasHeight - broomDepth * .6);
c.stroke();
c.beginPath();
c.moveTo(broomLeft + broomWidth + armDistanceFromBroom, canvasHeight + 5); // add a bit to make offscreen
c.lineTo(broomLeft + broomWidth + armDistanceFromBroom + 5, canvasHeight - broomDepth * .4);
c.lineTo(broomLeft + 3, canvasHeight - broomDepth * .8);
c.stroke();
c.translate(-armsMovement, 0);
}
function renderPartialCloud(nextFloat, xLast, yLast, rLast, lLast) {
let x = xLast + nextFloat() * (2 * rLast) - rLast,
y = yLast + nextFloat() * (1 * rLast) - rLast / 2,
r = rLast + nextFloat() * 10 - 8,
l = lLast;
if (x - r < 0) x = r + 1;
if (y - r < 0) y = r + 1;
if (r <= 0) return;
// TODO: Might be able to do this with some math clamp
if (l < 0) l = 0;
else if (l > 100) l = 100;
// This is the short version of adding transparency
c.fillStyle = cloudColor;
c.beginPath();
c.arc(
x,
y,
r,
0,
MathPI2
);
c.fill();
renderPartialCloud(nextFloat, x, y, r, l);
}
function renderCloud(cloud) {
// If cloud has not been re-generated, skip
if (!cloud) {
return;
}
// Creating a random number generator here, using a seed
// LCG using GCC's constants ~ Or crappy ones...
const rngM = 7;
const rngA = 8;
const rngC = 9;
let rngState = cloud.seed;
const nextFloat = () => {
rngState = (rngA * rngState + rngC) % rngM;
return rngState / (rngM - 1)
};
// End of RNG
const left = minCloudRadius + nextFloat() * minCloudRadius;
const right = minCloudRadius + nextFloat() * minCloudRadius;
const width = right - left;
const scaleAmount = cloud.distance / maxCloudDistance * 8;
const centerScaleX = (-scaleAmount * width) + canvasWidth / 2;
const centerScaleY = (-scaleAmount * width) + canvasHeight / 2;
const direction = cloud.seed * MathPI2;
const dx = Math.cos(direction) * cloud.distance * (1 + scaleAmount);
const dy = Math.sin(direction) * cloud.distance * (1 + scaleAmount);
// Translate and Scale must be done in this order...
c.translate(
centerScaleX + dx,
centerScaleY + dy
);
c.scale(scaleAmount, scaleAmount);
renderPartialCloud(
nextFloat,
0,
0,
// This makes the clouds differ in size
right,
left
);
c.scale(1 / scaleAmount, 1 / scaleAmount);
c.translate(
-centerScaleX - dx,
-centerScaleY - dy
);
}
function renderHoop(x, y) {
c.strokeStyle = snitchWingColor;
c.lineWidth = 3;
c.beginPath();
c.arc(
x,
y,
16,
0,
MathPI2
);
c.moveTo(x, y + 16);
c.lineTo(x, y + 100);
c.stroke();
}
function renderSnitch() {
// Move snitch in a believable flying motion
const cx = canvasWidth / 2 + Math.cos(time / 50) * 100;
const cy = canvasHeight / 2 + Math.sin(time / 100) * 20;
// Using SIN movement as my toggle
const movementAmount = Math.sin(time / 4) > 0 ? -9 : 0;
// Left wing
c.fillStyle = snitchWingColor;
c.beginPath();
c.moveTo(cx, cy);
// top/middle of wing
c.lineTo(cx - wingWidth / 2, cy - wingHeight / 2);
// far left tip
c.lineTo(cx - wingWidth, cy + wingHeight / 2 + movementAmount);
// bottom/middle
c.lineTo(cx - wingWidth / 2, cy);
c.fill();
// Right wing
c.beginPath();
c.moveTo(cx, cy);
// top/middle of wing
c.lineTo(cx + wingWidth / 2, cy - wingHeight / 2);
// far left tip
c.lineTo(cx + wingWidth, cy + wingHeight / 2 + movementAmount);
// bottom/middle
c.lineTo(cx + wingWidth / 2, cy);
c.fill();
// Body
c.fillStyle = snitchColor;
c.beginPath();
c.arc(
cx,
cy,
4,
0,
MathPI2
);
c.fill();
}
function renderMountains() {
// Background
c.fillStyle = '#6D8448';
c.beginPath();
c.moveTo(50, canvasHeight);
c.bezierCurveTo(
110 + 50, canvasHeight - 100 + 10,
110 + 50, canvasHeight + 10,
150 + 50, canvasHeight
);
c.fill();
// Foreground
c.fillStyle = '#87a45a';
c.beginPath();
c.moveTo(0, canvasHeight);
c.bezierCurveTo(
100, canvasHeight - 100,
100, canvasHeight,
200, canvasHeight
);
c.bezierCurveTo(
300, canvasHeight - 10,
300, canvasHeight - 50,
canvasWidth, canvasHeight
);
c.fill();
}
function gameLoop() {
// Update time
time++;
// Update clouds
let i = maxClouds;
while (i--) {
// Generate if needed
if (!clouds[i]) {
clouds[i] = {
seed: Math.random(),
// Starting distance
distance: 9
};
}
// Move cloud closer
const speed = clouds[i].seed * maxCloudSpeed;
clouds[i].distance += speed;
// Replace if offsceen
if (clouds[i].distance > maxCloudDistance / 2) {
clouds[i] = 0;
}
}
// Draw all the things
renderSkyBackground();
renderMountains();
renderHoop(canvasWidth / 2 - 10, canvasHeight - 100);
renderHoop(canvasWidth / 2 - 10 - 40, canvasHeight - 70);
renderHoop(canvasWidth / 2 - 10 + 40, canvasHeight - 60);
clouds.forEach(renderCloud);
renderSnitch();
renderArmsAndBroomstick();
}
// 17 ~ 1000/60 ~ 60 frames per second
setInterval(gameLoop, 17);