Diva's Magic Dream game in less than 1K of Javascript
When was announced the newest 2017 edition of the
JS1k contest with magic theme, I've
decided to take part with a horizontal scrolling game. The result was
Diva's Magic Dream.
The game is about a diva, who is dreaming being
chased by her fans, so she uses her magic to defend herself and also
destroy some Moai heads.
It's easy to play: use the arrow keys to move to left and
right, push the space bar like crazy to save the diva.
I had plans to develop the drawings using the curve
functions integrated in the <canvas> marker but at the end I reminded
about the Unicode characters just like in my
JS1K chess, more recent are the emoticons
integrated with full color in the most recent operating systems.
Although the Unicode emoticons implementation is anarchic,
for example, in Mac OS X the diva is blond, in Windows she has black hair,
in Android it appears as a black guy (mysteriously resembling Michael Jackson),
and in most Linux versions all figures appear like line-drawings.
Also used some sine functions to make small animations
inside the game, like the glowing clouds and the jump of the Moai head, and the
score "grows" every time that an enemy is destroyed.
For the third time, my entry didn't make it to the top 10, so I decided
to retire myself from further JS1k contests.
Source code
Note that the JS1K "provides" some variables loaded with important data, similar to this:
<canvas id="c">
</canvas><script>
var c=document.getElementById("c");
var a=c.getContext("2d");
</script>
<script src="diva-magic.js">
</script>
Here is my original commented source code:
// Diva's Magic Dream by Óscar Toledo G. http://nanochess.org
//
// Creation date: Feb/16/2017 4pm-9pm
// Revision date: Feb/17/2017 perfectioning
// Revision date: Feb/26/2017 Further optimization and new packer
// Revision date: Feb/27/2017 Added a happy sun and falling cameras (paparazzi)
//
// Your diva is dreaming in the clouds, stop fans at any price, caveat with moai heads!
// Keyboard, use left and right arrows, press space to shoot magic.
//
// Compressed with https://siorki.github.io/regPack.html (thanks to @siorki)
// Keyboard array
K=[];
// Toons array
o=[];
// Check for key pressed
onkeydown=w=>K[w.which]=5;
// Check for key depressed
onkeyup=w=>K[w.which]=0;
// r = Current score
// f = Current frame
// h = Last frame with a new fan
// Main game at 60hz
setInterval(w=>
{
// Yes, this game needs canvas
with(c)
// Yes, every game needs Math
with(Math){
// Create sky
fillStyle="#28f";
fillRect(0,0,640,240);
// Score color or emoji also if your browser is old enough
fillStyle="#fff";
// Size of the toons list
d=o.length;
// Check for game start
if(!d){
K[37]=K[39]=q=r=h=f=0;
// Put diva
o[o.length]={x:40,y:200,s:64,t:0,h:0};
// Put score
o[o.length]={x:8,y:32,s:24,t:5};
// Put clouds
while(d<18)
o[o.length]={x:d++*40,y:260,s:96,t:6}
}
// A happy sun
font=80+5*sin(.1*f)+'px Emojione,"Segoe UI emoji","Lucida Grande"';
fillText("🌞",480,80);
// Display toons
while(d--)
// Work directly with toon
with(o[d]){
// Each one can be a different size.
// Look for Emojione (Linux), Segoe UI emoji (Win7) or Lucida Grande (Mac)
font=s+'px Emojione,"Segoe UI emoji","Lucida Grande"';
// All our nice graphics courtesy of Unicode
// Diva,Death,Magic,Explosion,Bonus,Score,Clouds,Fan,Moai
fillText(["💃","☠️","🌟","💥","🏅",r*100,"☁️",f&8?"🏃":"🚶","🗿","📹"][t],x,y);
// Check for death
if(l=t-8?0:32,e=0,t>6&abs(x+l-o[e].x)<l+16&abs(y-o[e].y)<24)
o[e].t=1;
// 0:Diva movement
[w=>{
// Left/right
x=min(400,max(24,x+K[39]-K[37])),
// Small wave for walking animation
y=200+5*sin(.1*f);
// Check for magic shoot (Space key)
if(!q&K[32])o[o.length]={x:x+32,y:y-10,s:32,t:2};
q=K[32]},
// 1:Death movement
w=>{
// Bumping
y=200+80*sin(.1*++h);
// Restart game when finished
if(h>49)
o=[]},
// 2:Magic movement
w=>{
// Advance star
x+=10;
// Check for collisions
e=o.length;
l=0;
while(e--)
if(o[e].t>6&abs(x+l-o[e].x)<l+16&abs(y-o[e].y)<24)
// Check how many hits
if(x=640,!--o[e].h)
// Score "grows"
o[1].s=40,
// Convert to explosion or bonus
r+=o[e].t-8?1:(o[o.length]={x:320,y:0,s:64,t:4},250),
o[e].t=3;
x>639&&o.splice(d,1)},
// 3:Explosion animation
w=>{
s+=4;
s%32<1&&o.splice(d,1)},
// 4:Bonus animation
w=>{
if(y<140)
y+=2;
++s>239&&o.splice(d,1)},
// 5:Score animation
w=>{s>24&&--s},
// 6:Clouds displacement
w=>{
// Displace
x=x<-80?640:--x,
// Cute cloud pulsing
s=96+5*sin(.1*x)},
// 7:Fan animation
w=>{
// Falling
if(y<200)y+=20;
// Run
x-=8+f/1e4;
x<0&&o.splice(d,1)},
// 8:Moai head animation
w=>{
// Displace in one direction
x-=z;
// Boink!
x<z|x>400&&(z=-z);
// Spring!
y=100+100*sin(.1*f)},
// 9:Camera
w=>{
y+=y/9;
y>280&&o.splice(d,1)
}][t]()
}
// Insert a new fan or a camera
if(random()<.1&f-h>20){
h=f;
if(random()<.1)o[o.length]={x:o[0].x,y:1,s:64,t:9,h:1};
else o[o.length]={x:640-200*random(),y:0,s:64,t:7,h:2};
}
// Insert a moai head (didn't you played Gradius? ;))
if(f++&&f%1500<1)
o[o.length]={x:400,y:0,s:192,t:8,h:15,z:1+f/2e4}
}
},15);
// Diva's Magic Dream by Óscar Toledo G. http://nanochess.org
//
// Creation date: Feb/16/2017 4pm-9pm
// Revision date: Feb/17/2017 perfectioning
//
// Your diva is dreaming in the clouds, stop fans at any price, caveat with moai heads!
// Keyboard, use left and right arrows, press space to shoot magic.
//
// Compressed with http://www.iteral.com/jscrush/ (thanks to the awesome @aivopass)
// Only removes leading whitespace and comments in a single line.
// Unicode escapes easier thanks to https://mothereff.in/js-escapes
// Keyboard array
K=[];
// Toons array
o=[];
// Check for key pressed
onkeydown=function(w){K[w.which]=1};
// Check for key depressed
onkeyup=function(w){K[w.which]=0};
// r = Current score
// f = Current frame
// h = Last frame with a new fan
// Main game at 60hz
setInterval(function()
{
// Yes, every game needs Math
with(Math){
// Create sky
c.fillStyle="#28f";
c.fillRect(0,0,640,240);
// Score color or emoji also if your browser is old enough
c.fillStyle="#fff";
// Size of the toons list
d=o.length;
// Check for game start
if(!d){
K[37]=K[39]=q=r=h=f=0;
// Put diva
o[o.length]={x:40,y:200,s:64,t:0,h:0};
// Put score
o[o.length]={x:8,y:32,s:24,t:5};
// Put clouds
while(d<18)
o[o.length]={x:d++*40,y:260,s:96,t:6};
}
// Display toons
while(d--){
// Work directly with toon
with(o[d]){
// Each one can be a different size.
// Look for Emojione (Linux), Segoe UI emoji (Win7) or Lucida Grande (Mac)
c.font=s+'px Emojione,"Segoe UI emoji","Lucida Grande"';
// All our nice graphics courtesy of Unicode
// Diva,Death,Magic,Explosion,Bonus,Score,Clouds,Fan,Moai
c.fillText(["\uD83D\uDC83","\u2620\uFE0F","\uD83C\uDF1F","\uD83D\uDCA5","\uD83C\uDFC5",r*100,"\u2601\uFE0F",f&8?"\uD83C\uDFC3":"\uD83D\uDEB6","\uD83D\uDDFF"][k=t],x,y);
// Check for death
if(l=t<<2&32,e=0,t>6&abs(x+l-o[e].x)<l+16&abs(y-o[e].y)<24)
o[e].t=1;
// 0:Diva movement
if(!k--)
// Left/right
x=min(400,max(24,x+5*K[39]-5*K[37])),
// Small wave for walking animation
y=200+5*sin(f/9),
// Check for magic shoot (Space key)
!q&K[32]?o[o.length]={x:x+32,y:y-10,s:32,t:2}:0,q=K[32];
// 1:Death movement
if(!k--){
// Bumping
y=200+80*sin(++h/9);
// Restart game when finished
if(h>49)
o=[];}
// 2:Magic movement
if(!k--){
// Advance star
x+=10;
// Check for collisions
e=o.length;
l=0;
while(e--)
if(o[e].t>6&abs(x+l-o[e].x)<l+16&abs(y-o[e].y)<24)
// Check how many hits
if(x=640,!--o[e].h)
// Score "grows"
o[1].s=40,
// Convert to explosion or bonus
r+=o[e].t<8?1:(o[o.length]={x:320,y:0,s:64,t:4},250),
o[e].t=3;
x>639&&o.splice(d,1);}
// 3:Explosion animation
if(!k--){
s+=4;
s%32<1&&o.splice(d,1);}
// 4:Bonus animation
if(!k--){
if(y<140)
y+=2;
++s>239&&o.splice(d,1);}
// 5:Score animation
if(!k--&s>24)
--s;
// 6:Clouds displacement
if(!k--)
// Displace
x=x<-80?640:--x,
// Cute cloud pulsing
s=96+5*sin(x/9);
// 7:Fan animation
if(!k--){
// Falling
if(y<200)y+=20;
// Run
x-=8+f/1e4;
x<0&&o.splice(d,1);}
// 8:Moai head animation
if(!k--){
// Displace in one direction
x-=z;
// Boink!
x<z|x>400&&(z=-z);
// Spring!
y=100+100*sin(f/9);}
}
}
// Insert a new fan
if(random()<.1&f-h>20)
h=f,o[o.length]={x:640-200*random(),y:0,s:64,t:7,h:2};
// Insert a moai head (didn't you played Gradius? ;))
if(f&&f%1500<1)
o[o.length]={x:400,y:0,s:192,t:8,h:15,z:1+f/2e4};
f++;
}
},15);