for(_='a[i][``2]_2]-_)^`1]Z1]-Z,YRect(XonkeyWI>>V1)U2,Lc.KStyle="#JJfff",H8?P?G<5G58:F=(i=>{E`6]D`4]C),B(i?C:DBAKarc(Z@+=~~`5]--;)Math.PI&I?++,0,Z=1/P--&&Kfill7B(`3]0]for(*sin*(VcosBKbeginPath(B4+128*random(Ba.push([a.length Kstroke448,288,8|f():(1==`/2a[g][(C+((T+20*i)/20))B(-atan2(a[[Ya[[^-)a=[[b=T=I=S=P=R=e=26]],Wdown=WupEd=i.which,d-=d>40?d>80?79:59:37,i.type[5]?I|=2**d:I&=~(2**d)}BsetInterval(t=>{if(!b)TJ222",X896,896BHX3L3L83L512B!PKellipse(44263,BText(R,9,9Bi= ;i{if(C+=((C)-(C)>0?-1:U*/60B!i){D=1:2-:40:8:D,m=V4,M=0;m&=m-1;)M=!M;C=(-8&U+6&U4&1?1:-U+4&U)/(M+1B`5]=V4?4:fEif( =1,Rr=3+(R/3|0BR%4);rP1,382212+R/1);else P3,2)}BZ=ZF83Z>83G838:5Z,_=_F51_>51G518:5_,15&IT-S>e(LZ,_,1D,6]BS=T)}Z*_g= ;ghypot(Y^<+3](2==(1]=Bb|=!gB3==`&!g(,e-=3));J"+"bdffabfffbfc".substr(3*`,3@,_,,H`<2@+/2*A_+/2A/L)}},16);';G=/[^ -?IM-T[\]a-~]/.exec(_);)with(_.split(G))_=join(shift());eval(_)
// Zolmeister
// https://zolmeister.com
// W,A,S,D to move
// Arrow keys to shoot
// inspired by The Binding of Isaac (http://bindingofisaac.com/)
// packed with RegPack and UglifyJS
// https://github.com/Siorki/RegPack
// https://github.com/mishoo/UglifyJS2
// Entity
// [0] type {player, enemy, bullet, powerup}
// [1] x
// [2] y
// [3] radius
// [4] rotation
// [5] velocity
// [6] facing_dir (player only)
// Global Variables
// G - entities[]
// H - halt boolean
// F - frame number
// I - input bits
// S - last shot frame number
// E - number of enemies remaining in room
// R - room number
// L - minimum frames per shot
// Local Variables
// K - input key
// N - nextRoom()
// i - entities index (level 1)
// j - entities index (level 2)
// r - number of enemies to spawn
// m - w,a,s,d bits only (from I)
// M - parity of m
// initial world contains only player
G = [[
H = F = I = S = E = R = 0,
896/2,
576/2,
L = 26 // radius, also sets time between shots to 0.416s (at 62.5fps)
]]
// capture keyboard input as a bit array
// [left, up, down, right, s, _, a, _, w, d]
// e.g. w & a & up == 0100001010
onkeydown = onkeyup = e => {
// key codes
// 87 - w
// 65 - a
// 83 - s
// 68 - d
// 38 - up
// 37 - left
// 40 - down
// 39 - right
K = e.which
K -= K > 40 ? K > 80 ? 79 : 59 : 37
// input bits
// 8 - w
// 6 - a
// 4 - s
// 9 - d
// 1 - up
// 0 - left
// 2 - down
// 3 - right
e.type[5] ?
I |= 2 ** K
:
I &= ~(2 ** K)
}
setInterval(e => {
if (H) return // halt
F++
// background
c.fillStyle = '#222'
c.fillRect(0, 0, 896, 896)
// walls
c.strokeStyle = '#fff'
c.strokeRect(32, 32, 896 - 32 * 2, 576 - 32 * 2)
// holes in walls, only show if no enemies in room
!E && c.ellipse(896/2, 576/2, 440, 263, 0, 0, 7)
c.fill()
// display room number
c.strokeText(R, 9, 9)
i = G.length
while(i--) {
// isEnemy
if (G[i][0] == 1) {
// slowly rotate towards player
// cross product determines clockwise vs counterclockwise
// https://www.gamedev.net/forums/topic/445890-clockwise-or-counterclockwise/?tab=comments#comment-3951846
G[i][4] += (
Math.cos(G[i][4]) * Math.sin(
// targetAngle - inlined for RegPack
-Math.atan2(G[0][1] - G[i][1], G[0][2] - G[i][2]) - Math.PI / 2
) - Math.cos(
// targetAngle - inlined for RegPack
-Math.atan2(G[0][1] - G[i][1], G[0][2] - G[i][2]) - Math.PI / 2
) * Math.sin(G[i][4])
> 0 ? -1 : 1) * Math.PI / 60
}
// isPlayer
if (!i) {
// set the direction the player is facing by reading input bits
G[i][6] = I & 1 ? // left
Math.PI
: I & 1 << 1 ? // up
-Math.PI/2
: I & 1 << 2 ? // down
0
: I & 1 << 3 ? // right
Math.PI/2
: G[i][6]
m = I >> 4
// set the direction the player is moving by reading input bits
// if 2 keys pressed (bit parity is even) average summed angles
M = 0
while(m &= m - 1) M = !M
G[i][4] = (
-Math.PI/2 * (I >> 8 & 1) + // w
(Math.PI * (I >> 6 & 1) * (I >> 4 & 1? 1 : -1) ) + // a (s ? -PI : PI)
Math.PI/2 * (I >> 4 & 1) // s
) / (M + 1)
// set a positive velocity for the player if any movement key is being pressed
G[i][5] = I >> 4 ? 4 : 0
// nextRoom()
N = e => {
// remove non-player entities
G.length = 1
R++
r = (R/3|0) + 3
// every 4th room is a powerup room
if (R % 4)
while (r--) {
E++
// position enemies near the center of the room
G.push([
1, // enemy
(896/2 - 128/2) + Math.random() * 128, // x
(576/2 - 128/2) + Math.random() * 128, // y
10, // radius
0, // rotation
2 + R/10 // velocity
])
}
else {
E++
G.push([
3, // powerup
896/2, // x
576/2, // y
20, // radius
0 // rotation
])
}
}
// clamp player x,y and check wall collision (for moving to next room)
G[i][1] = G[i][1] < 32 + 26 ? (!E ? N() | 896 - 32 - 26 : 32 + 26) :
G[i][1] > 896 - 32 - 26 ? (!E ? N() | 32 + 26 : 896 - 32 - 26) : G[i][1]
G[i][2] = G[i][2] < 32 + 26 ? (!E ? N() | 576 - 32 - 26 : 32 + 26) :
G[i][2] > 576 - 32 - 26 ? (!E ? N() | 32 + 26 : 576 - 32 - 26) : G[i][2]
// shoot a bullet if an arrow key is pressed and hasn't shot too recently
if (I & 15 && F - S > L) {
G.push([
2, // Bullet
G[i][1], // x
G[i][2], // y
10, // radius
G[i][6], // rotation
6 // velocity
])
S = F
}
}
// move entity in it's direction
// if it's an enemy, drift slightly in a wave pattern (cosine)
G[i][1] += ~~G[i][5] * Math.cos(G[i][4] + (G[i][0] == 1 && Math.cos((F + i * 20) / 20)))
G[i][2] += ~~G[i][5] * Math.sin(G[i][4] + (G[i][0] == 1 && Math.cos((F + i * 20) / 20)))
j = G.length
while (j--) {
// isColliding
if(Math.hypot(G[j][1] - G[i][1], G[j][2] - G[i][2]) < G[i][3] + G[j][3]) {
G[i][0] == 1 && (
// enemy x bullet
G[j][0] == 2 && (
// remove by setting x to Infinity
G[j][1] = G[i][1] = 1/0,
E--
),
// enemy x player - halt
H |= !j
)
// powerup x player
G[i][0] == 3 & !j && (
// remove by setting x to Infinity
G[i][1] = 1/0,
E--,
// increase player shooting rate
L -= 3
)
}
}
// colors based on entity type
c.fillStyle = '#' + 'bdffabfffbfc'.substr(G[i][0] * 3, 3)
c.beginPath()
c.arc(G[i][1], G[i][2], G[i][3], 0, 7)
c.fill()
c.beginPath()
c.fillStyle = '#fff'
// if player or enemy draw an eye
G[i][0] < 2 && c.arc(
G[i][1] + G[i][3]/2 * Math.cos(i ? G[i][4] : G[i][6]),
G[i][2] + G[i][3]/2 * Math.sin(i ? G[i][4] : G[i][6]),
G[i][3]/2,
0, 7)
c.fill()
}
}, 16) // 62.5fps