###Setup###
// create common funcs
function drawBeaver( x, y, facing, step, depth )
{
var stepSin = Math.sin( step * Math.PI );
// beaver tail
drawRoundedRect( 0.15, 0.2, 0.2, 1, x - facing * 10, y, 10, 25, 5, 5 );
// beaver body
drawRoundedRect( 0.1, 0.2, 0.5, 1, x, y - 15, 10, 15, 5, 5 );
drawRect( 0.1, 0.2, 0.5, 1, x, y - 5, 10, 5 );
drawBlob( 0.1, 0.2, 0.5, 1, x, y, 10, 30 );
// beaver arms
drawBlob( 0.15, 0.2, 0.7, 1, x + 5 + 4 * facing, y + stepSin * 3, 3, 7 );
drawBlob( 0.15, 0.2, 0.7, 1, x - 5 + 4 * facing, y - stepSin * 3, 3, 7 );
// beaver feet
drawBlob( 0.15, 0.2, 0.2, 1, x + 5 + 2 * facing, y - 28 - Math.min( stepSin, 0 ) * 5, 4, 2 );
drawBlob( 0.15, 0.2, 0.2, 1, x - 5 + 2 * facing, y - 28 + Math.max( stepSin, 0 ) * 5, 4, 2 );
// beaver face
drawRoundedRect( 1, 1, 1, 1, x + 4 * facing, y + 15, 4, 5, 2, 2 );
drawBlob( 0.15, 0.2, 0.7, 1, x + 5 * facing, y + 20, 6, 5 );
drawFixedCircle( 0, 0, 0, 1, x + 3 + 4 * facing, y + 25, 2 );
drawFixedCircle( 0, 0, 0, 1, x - 3 + 4 * facing, y + 25, 2 );
// beaver arms
// water overlay
drawRect( 0.6, 0.4, 0.5, 0.75, x, y - 30, 20, depth );
// debug bounds overlay
if( false && Math.random() < 0.5 )
{
drawRect( 1, 1, 1, 0.25, x, y, 10, 30 );
}
}
// start it up
//setPage( "BeaverRenderTest" );
//setPage( "Game" );
setPage( "StartButton" );
###BeaverRenderTest###
var time = 0;
function update()
{
time += delta;
}
function render()
{
fillBackground( 0.6, 0.4, 0.5 );
drawBeaver( 0, 0, Math.sin( time ), ( time % 1 ) * 2 - 1, Math.sin( time ) * 3 + 10 );
}
###StartButton###
showMouse();
var state = "";
var pressing = false;
var cooldown = 1;
function isOver()
{
return mouseX * mouseX + mouseY * mouseY < 80*80;
}
function refresh()
{
if( state == "done" )
{
var s = ( 1 - cooldown ) * 4 + 1;
crosshairTransform();
scale( s, s );
}
fillBackground( 0, 0, 0 );
drawFixedCircle( 1, 0, ( state == "none" ? 0.5 : state == "over" || state == "done" ? 1 : 0.25 ) * cooldown * cooldown, 1, 0, 0, 80 );
drawRect( 0, 0, 0, 1, -15, 0, 10, 30 );
drawRect( 0, 0, 0, 1, 5, 0, 10, 20 );
drawRect( 0, 0, 0, 1, 25, 0, 10, 10 );
}
function setButtonState( newState )
{
if( state != newState && state != "done" )
{
state = newState;
refresh();
}
}
function update()
{
if( state != "done" )
{
if( isOver() )
{
setButtonState( mouseDown && pressing ? "down" : "over" );
}
else
{
setButtonState( "none" );
}
}
else
{
cooldown -= delta;
refresh();
if( cooldown <= 0 )
{
setPage( "Game" );
}
}
}
function onMouseUp()
{
if( isOver() )
{
setButtonState( "done" );
}
}
function onMouseDown()
{
if( isOver() && state != "done" )
{
playNote( 0 );
pressing = true;
}
else
{
pressing = false;
}
}
function feedSynth()
{
if( state == "done" )
{
playNote( 12, 12, cooldown * cooldown, cooldown * cooldown, 1-cooldown, 1-cooldown, 0, 1 );
}
}
mouseX = 1000; // hacky, assume mouse is a long way away at first
setButtonState( "none" );
###Game###
hideMouse();
var players = [];
players.push( {x: -180, y: 0, score: 0, hitCooldown: 0 } );
players.push( {x: 180, y: 0, score: 0, hitCooldown: 0 } );
var ball;
var time = 0;
var winCooldown = 0;
function newBall()
{
ball = { x:0, y:0, dx: Math.random() > 0.5 ? -200 : 200, dy: Math.random() * 200 - 100, hitCount: 0, sx: 0, sy: 0 };
}
function update()
{
// timers
time += delta;
for( player in players )
{
player.hitCooldown = Math.max( player.hitCooldown - delta, 0 );
}
if( winCooldown > 0 )
{
winCooldown -= delta;
if( winCooldown <= 0 )
{
setPage( "StartButton" );
}
}
// move player paddle
if( hasMouse )
{
players[0].y = Math.min( Math.max( mouseY, -120 ), 110 );
}
// move ai paddle
var aiDy = 0;
var aiDelta = ball.y - players[1].y;
if( aiDelta > 20 )
{
aiDy = 300;
}
if( aiDelta < -20 )
{
aiDy = -300;
}
players[1].y = Math.min( Math.max( players[1].y + aiDy * delta, -120 ), 110 );
// move ball
if( winCooldown == 0 )
{
ball.x += ball.dx * delta;
ball.y += ball.dy * delta;
// bounce ball
if( ball.y < -120 )
{
ball.y = -120;
ball.dy = Math.abs( ball.dy );
playNote( 0, 0, 1, 0, 1, 0, ball.x );
}
if( ball.y > 100 )
{
ball.y = 100;
ball.dy = -Math.abs( ball.dy );
playNote( 0, 0, 1, 0, 1, 0, ball.x );
}
// endzones
var scored = false;
if( ball.x > 270 )
{
players[0].score += 1;
scored = true;
if( players[0].score >= 3 )
{
winCooldown = 3;
}
else
{
newBall();
}
}
if( ball.x < -270 )
{
players[1].score += 1;
scored = true;
if( players[1].score >= 3 )
{
winCooldown = 3;
}
else
{
newBall();
}
}
if( scored )
{
for( i in 0...3 )
{
playNote( 12, 12, 1, 0, 0.2, 0.2, -ball.x, 0 + i * 3 );
playNote( 16, 16, 1, 0, 0.2, 0.2, -ball.x, 1 + i * 3 );
playNote( 19, 19, 1, 0, 0.2, 0.2, -ball.x, 2 + i * 3 );
}
}
// paddle collisions
for( player in players )
{
if( Math.abs( ball.x - player.x ) < 20 && Math.abs( ball.y - player.y ) < 40 && ball.dx * player.x > 0)
{
// hit!
ball.dx = ( ball.x < 0 ? 200 : -200 ) * ( 1 + ball.hitCount / 4 );
ball.dy = ( ball.y - player.y ) * 7 * ( 1 + ball.hitCount / 4 );
// ball.x = ( Math.abs( player.x ) - 20 ) * ( ball.x < 0 ? -1 : 1 );
ball.hitCount += 1;
playNote( 12, 12, 0, 1, 1, 0.2, player.x, 0 );
playNote( 12, 24, 1, 1, 0.2, 0.2, player.x, 1 );
playNote( 24, 24, 1, 0, 0.2, 1, player.x, 2 );
player.hitCooldown = 1;
}
}
// feathers
if( ball.x != ball.sx || ball.y != ball.sy )
{
var oldLength = Math.pow( Math.pow( ball.x - ball.sx, 2 ) + Math.pow( ball.y - ball.sy, 2 ), 0.5 );
ball.sx = ball.x + ( ball.sx - ball.x ) * 30 / oldLength;
ball.sy = ball.y + ( ball.sy - ball.y ) * 30 / oldLength;
}
}
}
function render()
{
// calc height of water
var depth = Math.sin( time ) * 3 + 10;
// actual background, as water
fillBackground( 0.6, 0.4, 0.5 );
// upper ground
drawRect( 0.3, 0.7, 0.5, 1, 0, 140, 240, 20 );
drawRect( 0.3, 0.7, 0.6, 1, 0, 115, 240, 5 );
drawRect( 0.1, 0.2, 0.6, 1, 0, 100, 240, 10 );
// water overlay
drawRect( 0.6, 0.4, 0.5, 0.75, 0, 90, 240, depth );
// beavers
for( player in players )
{
var hit = player.hitCooldown * player.hitCooldown;
var facing = player.x < 0 ? 1 : -1;
drawBeaver( player.x, player.y, facing * ( 1 - hit ) - facing * hit, ( ( player.y * 0.02) % 1.0 ) * 2 - 1, depth );
}
// ball
translate( ball.x, ball.y );
stretch( Math.pow( ball.dx * ball.dx + ball.dy * ball.dy, 0.5 ) * 0.0003 + 1, Math.atan2( -ball.dy, ball.dx ) );
if( ball.sx != ball.x || ball.sy != ball.y )
{
var featherAngle = Math.atan2( -( ball.sy - ball.y ), ball.sx - ball.x ) + Math.PI / 2;
drawArc( 1, 1, 1, 0.5, 0, 0, 20, 20, featherAngle - Math.PI / 6, featherAngle + Math.PI / 6, 3 );
}
drawBlob( 1, 0.6, 0.5, 1, 0, 0, 10, 10, 8 );
crosshairTransform();
// score flowers
for( player in players )
{
for( score in 0...3 )
{
var x = ( -score * 30 + 150 ) * ( player.x < 0 ? -1 : 1 );
var y = 150 - Math.sin( x * 0.005 * Math.PI * 2 + 1 ) * 4;
// flower stem
drawRoundedRect( 0.3, 0.8, 0.3, 1, x, y - 10, 2, 10, 2, 2 );
if( score < player.score )
{
// flower!
translate( x, y );
rotate( Math.PI / 4 );
drawRoundedRect( 0.7, 0.8, 0.9, 1, 0, 0, 4, 8, 4, 4 );
rotate( Math.PI / 2 );
drawRoundedRect( 0.7, 0.8, 0.9, 1, 0, 0, 4, 8, 4, 4 );
drawFixedCircle( 0.16, 1, 0.5, 1, 0, 0, 3 );
crosshairTransform();
}
else
{
// bud
drawFixedCircle( 0.3, 0.8, 0.2, 1, x, y, 4 );
}
}
}
// lower ground
drawRect( 0.3, 0.7, 0.4, 1, 0, -140, 240, 20 );
}
newBall();
[/gamesketch]
Is this what beavers look like? I really don't know. Regardless, I think I'm going to call this game done. I'm about three hours in now, and it's been fun. So there's that. I'm less confident about making games in under an hour but I'll keep at it. Maybe I'll speed up.
Any suggestions for the next microgame?
<3
Farbs
EDIT: I added a few new primitives to the renderer, including arcs, which reminded me that I hadn't finished the shuttlecock. So I finished the shuttlecock.
Dystopian themed breakout.
Welcome back! Good to see you about again after quite a while!
Well, we’re both back after a long while!
I suggest maybe “Ninja Nonsense?” (Space invaders style, wherein a ninja fights many types of enemies per level (Like samurai the first, robots the second and policemen the third, so on so forth). 3 levels of this seems like a decent mini-project.)
@Laremere – Hmm… interesting. I have an idea that _might_ work.
@Jiggens – Cheers! I’ve been working pretty hard but just haven’t found time to update the blog much. In fact, I don’t think I ever even posted about finishing and releasing Captain Foraxian EX, which is a nice native windows spin on Captain Foraxian, and part of the Captain Forever supporter stuff.
I wonder how long that twitter plugin has been broken. Erp. I think I’ll just remove that for now…
@RandomCommander – Interestingly, I was thinking the postapocabreakout game might have some space invaderness to it. I would like to build the occasional series of these though, and yeah something like your suggestion would be about right for it.
@farbs
I don’t think you did. I found it by accident when I checked your twitter that day. It was like a hidden present. A captain something present. The two best kinds.
Well i can not win.
Apperanlty,No one wins this game!