- Author:
- Andrew Berry
- Twitter:
- @
- GitHub:
- Facebook:
- Google+:
- +
- Reddit:
- /r/
- Pouet:
- Website:
- Compo:
- canvas
- Demo link:
- https://js1k.com/2017-magic/demo/2719
- Shortlink:
- https://js1k.com/2719
- Blog post:
- please update here!
- Bytes:
- 1020
- Chars:
- 1020
- Submission
_='hD52,lDm=n=pD,q,r,t,u,x,y; zUeK7e<r-1;e@NGxO=y-GyH= sqrt(k*k+f*fd{f:gH:k/g,h:f/g,p:]})}kD6,v=g=f=wT[0f79>e;eL;g>wd[fG];)v=w,wd[Lff;c.beginPathVc.arc(p.Ng%-m,p.y-h%,k/2,2* PI1<pC;Vgk--}} AU"#0f;640eK=0Q<qQ@*NmO=*yH=$?16:;$(k8O8$?$C6;kOHHf=312<k+g8>kh<f+ghG6>f)x=(p=$)?5:0;f$||!(-<k)||eq[d])}q=e} B(ed=(e-n)/1E3;n=e;my*d38==t||40==t)h(38<t?1:-1)*y*d*1.5;h= max( min(h,304)r.unshift({x:0+m,y:h+8}r=r.slice(99NTQ=m/640u!T)uTK=4+lLQ;)q{x:20+20*(uG),y:10,t:d>=l?2+2:0})K--;AV1<rzV0<x?(y=300:10p,8,8,48*x/5,4)):(y=20pD:p:(A(),z())}Jdown=(et=e.which};Jup=Ut=0};;c.fillq[dfuncW* random())?"#f0f0"requestAnimaWFrame(B)var Style= Math.);for(d[f.length0,.push(Rect(r[e:"#~~U){].32&&=[]2==00"if(+=p?$t%*(g-v)7;e=0;@Lk=Cfff"D=1G+1H,gJonkeyK,dL++Nx-O,fQ;dT=dU(V(Wtion';for(Y in $='WVUTQONLKJHGDC@7%$ ')with(_.split($[Y]))_=join(pop());eval(_)
- Description
- Controls: up and down to move. Potions: red = faster, green = slower.
Don't hit the bricks! Eat the magic potions!
- Base64 encoded
Xz0nB2hENTIsbEQQbT0Qbj0QcEQscRoschosdCx1LHgseTsDIHpVB2VLGjdlPHIPLTE7ZUATF04TRxd4Tz0TF3ktE0cXeUg9CXNxcnQoayprK2YqZgtkEXtmOmdIOmsvZyxoOmYvZyxwOhNdfSl9B2tENix2PWc9Zj0Qd1RbMBdmNzk+ZTtlTBYMO2c+dxlkW2ZHXTspdj13LHceZFtMZhdmO2MuYmVnaW5QYXRoVmMuYXJjKA5wLk4OZyUtbSwOcC55LQ5oJSxrLzIsEDIqCVBJCwgxPB8bcEM7AVZnHmstLX19AyBBVQgfIiMwHBRmHDsSEBA2NBAYMAsMB2UaSz0wUTxxD1FAGCoCTm1PPRgqAnlIPSQ/MTY6GDskGShrHjhPHjgLCB8kPxskQxQ2HDsSa09ISB1mPTMxMjxrK2cZGDg+axloPGYrZxloRzY+Zil4PShwPSQpPzU6MDtmGSR8fCEoLRg8ayl8fGURcVtkXSl9cT1lfQMgQihlFgdkPShlLW4pLzFFMztuPWU7bR4VeSpkHTM4PT10fHw0MD09dCloHigzODx0PzE6LTEpKnkqZCoxLjU7aD0JbWF4KBAJbWluKGgsMzA0KQtyLnVuc2hpZnQoe3g6GDArbSx5OmgrOH0Lcj1yLnNsaWNlKBA5OQtOVFE9FW0vNjQwHXUhVCkMdVRLPTQrbExROylxEXt4OhUyMAQrMjAqKHVHKSx5OhUxMAQsdDpkPj1sPxUyBCsyOjB9KUstLTtBVjE8cg8ZelYwPHg/KHk9Gx8zMDA6MTAQCBtwBSwSOCw4LDQ4KngvNSw0KSk6KHk9MjAQH3BEOnALHwY6KEEoKSx6KCkpfUpkb3duPQMoZRZ0PWUud2hpY2h9O0p1cD0DVXQ9MH07BjsBYy5maWxsAnFbZBcDZnVuY1cEKglyYW5kb20oKSkFPyIjZhwUMGYwIgZyZXF1ZXN0QW5pbWFXRnJhbWUoQikHdmFyIAgBU3R5bGU9CU1hdGguCyk7DGZvcigOZFtmFw8ubGVuZ3RoEDAsES5wdXNoKBIBUmVjdCgTcltlFDoiIxV+flUpexddLhgzMhkmJho9W10bMj09HDAwIh0LaWYoHis9H3A/JAJ0JSooZy12KTc7DGU9MDtATBYHaz1DBRRmZmYiRD0xRysxSCxnSm9ua2V5SyxkTCsrTngtTyxmUTtkVD1kVSgWVigLV3Rpb24nO2ZvcihZIGluICQ9J1dWVVRRT05MS0pIR0RDQDclJB8eHRwbGhkYFxYVFBMSERAPDgwLCQgHBgUEAwIBJyl3aXRoKF8uc3BsaXQoJFtZXSkpXz1qb2luKHBvcCgpKTtldmFsKF8p
- Original source
// 20x10 grid
// VARS
// ----
// Note, using `let` crushed better than globals
// Width
let w = 640;
// Height
let h = 320;
// Worm x
let x = 312;
// Worm y
let y = 152;
// Entity count
let e = 10;
// Scroll offset
let o = 0;
// Start time
let t = 0;
// Game command
let g = 1;
// Matrix
let m = [];
// Path
let p = [];
// Active key
let k;
// Level
let l;
// Timeout
let v;
// Speed in pixels per second
let s;
// FUNCS
// -----
// Random
// Note, crushing is better with braces...
// $ = max
let R = ($) => {
return ~~(Math.random() * $);
}
// Worm
let W = () => {
let i;
let s = [];
// Build array of line segments
for (i = 0; i < p.length - 1; i++) {
let a = p[i].x - p[i + 1].x;
let b = p[i].y - p[i + 1].y;
let l = Math.sqrt((a * a) + (b * b));
s.push({
// Length
l: l,
// Normalized x vector
v: a / l,
// Normalized y vector
w: b / l,
p: p[i]
});
}
// Segment offset
let a = 16;
// Current segment
let j = 0;
// Point position
let u = 0;
// Previous length
let n = 0;
// Length
let l = s[0].l;
// Draw segments
for (i = 0; i < 9; i++) {
while(u > l && s[j + 1]) {
// If the point is beyond the segment, move to the next one
n = l;
l += s[++j].l;
}
c.beginPath();
c.arc(s[j].p.x - s[j].v * (u - n) - o, s[j].p.y - s[j].w * (u - n), a / 2, 0, 2 * Math.PI);
c.fillStyle = g > 1 ? (g == 2 ? '#f00' : '#0f0') : '#fff';
c.fill();
u += a--;
}
};
let B = () => {
// Fill background
c.fillStyle = g ? '#000' : '#f00';
c.fillRect(0, 0, w, h);
// Render entities
let n = [];
for (let i = 0; i < m.length; i++) {
// Bounding box
let b = {x: (m[i].x * 32) - o, y: m[i].y * 32, s: m[i].t ? 16 : 32};
// Potion?
if (m[i].t) {
// Center potions in grid
b.x += 8;
b.y += 8;
}
// Draw
c.fillStyle = g ? (m[i].t ? (m[i].t == 2 ? '#f00' : '#0f0') : '#fff') : '#600';
c.fillRect(b.x, b.y, b.s, b.s);
// Check for hit
// For brevity we'll combine rendering and hit detection
// Only the worm collides...
let j = x < b.x + b.s && x + 16 > b.x && y < b.y + b.s && y + 16 > b.y;
if (j) {
// Collision!
g = m[i].t;
// Potions last for 5 seconds
v = g ? 5 : 0;
}
// Remove entity if it is a potion and has been hit, or if it is out of bounds
if (!(j && m[i].t) && b.x > -32) {
n.push(m[i]);
}
}
m = n;
};
// Loop
// $ = timestamp
let L = ($) => {
// Calculate delta
let i = ($ - t) / 1e3;
t = $;
// Set horizontal offset
// Use ~~ to floor (we can also get away with s being undefined)
o = o + ~~(s * i);
// Commands
if (k == 38 || k == 40) {
// Set offset (climbs at 1.5x)
y += ((k > 38 ? 1 : -1) * s * i) * 1.5;
}
// Clamp to bounds
y = Math.max(0, Math.min(y, h - 16));
// Save point
// Offset x and y by half of the worm size to match circle offsets
p.unshift({x: x + 8 + o, y: y + 8});
// Stop array bloat in a naive way
// We shouldn't need any more than this to draw the segments
p = p.slice(0, 99);
// Update timeout (do it here so `i` can be reused)
v -= i;
// Get current level
// Use ~~ to floor
i = ~~(o / w);
// Has the level changed?
if (l != i) {
// Save current level
l = i;
// Add level to the matrix
// Ideally this would be limited to 40 to allow infinite game play
// e.g. `i = 4 + e < 40 ? e++ : e;`
// Re-use i
i = 4 + e++;
while(i) {
// Create a matrix of random blocks and potions
m.push({
x: R(20) + 20 * (l + 1),
y: R(10),
// Type
// Add potions last so they're behind bricks if positions overlap
t: i >= e ? R(2) + 2 : 0
});
i--;
}
}
// Background
B();
// Worm
p.length > 1 && W();
// Check for timeout
if (v > 0) {
s = g == 2 ? 300 : 100;
c.fillStyle = g == 2 ? '#f00' : '#0f0';
c.fillRect(8, 8, 48 * v / 5, 4);
} else {
// Default speed
s = 200;
// Default game command
g ? g = 1 : g;
}
// Game over?
if (g) {
// Nope...
requestAnimationFrame(L);
} else {
// Yep...
// Render one more time to show the "death" frame
// This is clunky, but given that we're combining rendering and hit
// detection it is necessary.
B();
W();
}
};
// EVENTS
// ------
// Note, crushing is better with braces...
onkeydown = ($) => {
k = $.which;
}
// Note, crushing is better with braces...
onkeyup = () => {
k = 0;
};
// LOOP
// ----
requestAnimationFrame(L);