{deck}
version:1
card:0
size:[640,380]
name:"breakout.deck"

{fonts}
deckbuilder:"%%[encoded data, 12424 characters]"

{card:title}
image:"%%[encoded data, 5620 characters]"
{widgets}
field1:{"type":"field","size":[287,36],"pos":[179,61],"locked":1,"font":"deckbuilder","border":0,"align":"center","value":"Breakout, Three Ways"}
field2:{"type":"field","size":[287,15],"pos":[179,101],"locked":1,"border":0,"align":"center","value":"Building action games with Decker"}
field3:{"type":"field","size":[188,15],"pos":[24,256],"locked":1,"border":0,"align":"center","value":"Blocking (Synchronous)"}
field4:{"type":"field","size":[188,15],"pos":[226,256],"locked":1,"border":0,"align":"center","value":"Event-Driven (Asynchronous)"}
field5:{"type":"field","size":[188,15],"pos":[428,256],"locked":1,"border":0,"align":"center","value":"Contraption-Oriented"}
button1:{"type":"button","size":[200,152],"pos":[18,125],"script":"title.0","style":"invisible"}
button2:{"type":"button","size":[200,152],"pos":[221,125],"script":"title.1","style":"invisible"}
button3:{"type":"button","size":[200,152],"pos":[424,125],"script":"title.2","style":"invisible"}
field6:{"type":"field","size":[291,69],"pos":[185,297],"locked":1,"border":0,"align":"center","value":{"text":["In this deck we will explore three approaches to building a breakout game with Decker, beginning with a loose adaptation of the \"","2D Breakout Game Uith Pure JavaScript","\" tutorial from MDN.\n\nFor more detailed explanation of how these examples work, see"," the accompanying blog article","."],"font":["","","","",""],"arg":["","https://developer.mozilla.org/en-US/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript","","http://beyondloom.com/blog/breakout.html",""]}}

{script:title.0}
on click do
 go["blocking" "BoxIn"]
end
{end}

{script:title.1}
on click do
 go["eventdriven" "BoxIn"]
end
{end}

{script:title.2}
on click do
 go["contraptionoriented" "BoxIn"]
end
{end}

{card:blocking}
image:"%%IMG2AoABfA3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8N/w3/Df8NuQ=="
script:"blocking.0"
{widgets}
canvas:{"type":"canvas","size":[480,320],"pos":[11,40],"volatile":1,"scale":1}
field1:{"type":"field","size":[135,36],"pos":[498,39],"locked":1,"font":"deckbuilder","show":"invert","border":0,"align":"center","value":"Blocking"}
field2:{"type":"field","size":[135,177],"pos":[498,82],"locked":1,"value":"Click anywhere in the canvas to the left to begin a game. The game will end if you destroy every brick or miss the ball with your paddle.\n\nThis approach \"lives\" in a single event handler, keeping the script running continuously while periodically yielding to Decker with sleep[] to wait for the next 60hz frame. Since all state is part of that event handler's scope, the game automatically resets itself every time you play."}
button1:{"type":"button","size":[135,20],"pos":[498,340],"script":"blocking.1","text":"Back","style":"rect"}

{script:blocking.0}

ball:image["%%IMG0ABUAFQH8AAf/AA//gB//wD//4H//8H//8P//+P//+P//+P//+P//+P//+P//+H//8H//8D//4B//wA//gAf/AAH8AA=="]
ballRadius:first .5*ball.size
ballPos:(.5,1)*canvas.size-0,30
ballVel:2,-2

paddleSize:75,10
paddlePos:first .5*canvas.size-paddleSize

bricks:image[5,3].map[0 dict 1]
brickSize:75,20
brickOffset:30,30
brickPadding:10

running:1

on click do
 while running
  each pos in bricks.size[0] cross bricks.size[1]
   if bricks[pos]
    local brickPos:brickOffset+pos*brickSize+brickPadding
    canvas.rect[brickPos brickSize]
    if min (ballPos>brickPos) & (ballPos<brickPos+brickSize)
     bricks[pos]:0
     ballVel:ballVel*(1,-1)
     if !bricks.hist[1] running:0 end
    end
   end
  end
  canvas.paste[ball ballPos-ballRadius 1]
  paddlePos:first (pointer.pos-canvas.pos)-.5*paddleSize
  canvas.rect[paddlePos,canvas.size[1]-paddleSize[1] paddleSize]
  if ((canvas.size[0]-ballRadius)<ballPos[0]+ballVel[0]) | (ballRadius>ballPos[0]+ballVel[0])
   ballVel:ballVel*(-1,1)
  end
  if ballRadius>last ballPos+ballVel
   ballVel:ballVel*(1,-1)
  elseif (canvas.size[1]-ballRadius)<ballPos[1]+ballVel[1]
    if (ballPos[0]>paddlePos) & (ballPos[0]<paddlePos+paddleSize[0])
      ballVel:ballVel*(1,-1)
    else
      running:0
    end
  end
  ballPos:ballPos+ballVel
  sleep[]
  canvas.clear[]
 end
end
{end}

{script:blocking.1}
on click do
 go["title" "BoxOut"]
end
{end}

{card:eventdriven}
image:"%%IMG2AoABfA7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8O/w7/Dv8OuQ=="
script:"eventdriven.0"
{widgets}
canvas:{"type":"canvas","size":[480,320],"pos":[11,40],"volatile":1,"scale":1}
field1:{"type":"field","size":[135,36],"pos":[498,39],"locked":1,"font":"deckbuilder","show":"invert","border":0,"align":"center","value":"Event-Driven"}
field2:{"type":"field","size":[135,168],"pos":[498,82],"locked":1,"value":"Click anywhere in the canvas to the left to begin a game. The game will end if you destroy every brick or miss the ball with your paddle.\n\nThis approach handles a single frame per view[] event pumped during gameplay by marking the canvas \"animated\". Inter-frame state is stashed in widgets below. Note how the \"back\" button remains interactive while the game is playing."}
button1:{"type":"button","size":[135,20],"pos":[498,340],"script":"blocking.1","text":"Back","style":"rect"}
ballPos:{"type":"field","size":[64,20],"pos":[498,264],"locked":1,"volatile":1}
ballVel:{"type":"field","size":[64,20],"pos":[569,264],"locked":1,"volatile":1}
bricks:{"type":"canvas","size":[50,30],"pos":[545,292],"locked":1,"volatile":1,"scale":10}

{script:eventdriven.0}
ball:image["%%IMG0ABUAFQH8AAf/AA//gB//wD//4H//8H//8P//+P//+P//+P//+P//+P//+P//+H//8H//8D//4B//wA//gAf/AAH8AA=="]
ballRadius:first .5*ball.size
paddleSize:75,10
paddlePos:first .5*canvas.size-paddleSize
brickSize:75,20
brickOffset:30,30
brickPadding:10

on reset go do
 bricks.paste[image[bricks.size].map[0 dict 1]]
 ballPos.data:(.5,1)*canvas.size-0,30
 ballVel.data:go*2,-2
 canvas.animated:go
 view[]
end

on click do
 reset[!canvas.animated]
end

on view do
 done:0
 canvas.clear[]
 if canvas.animated
  each pos in bricks.size[0] cross bricks.size[1]
   if bricks[pos]
    local brickPos:brickOffset+pos*brickSize+brickPadding
    canvas.rect[brickPos brickSize]
    if min (ballPos.data>brickPos) & (ballPos.data<brickPos+brickSize)
     bricks[pos]:0
     ballVel.data:ballVel.data*(1,-1)
     if !bricks.copy[].hist[1] done:1 end
    end
   end
  end
  canvas.paste[ball ballPos.data-ballRadius 1]
  paddlePos:first (pointer.pos-canvas.pos)-.5*paddleSize
  canvas.rect[paddlePos,canvas.size[1]-paddleSize[1] paddleSize]
  if ((canvas.size[0]-ballRadius)<ballPos.data[0]+ballVel.data[0]) | (ballRadius>ballPos.data[0]+ballVel.data[0])
   ballVel.data:ballVel.data*(-1,1)
  end
  if ballRadius>last ballPos.data+ballVel.data
   ballVel.data:ballVel.data*(1,-1)
  elseif (canvas.size[1]-ballRadius)<ballPos.data[1]+ballVel.data[1]
   if (ballPos.data[0]>paddlePos) & (ballPos.data[0]<paddlePos+paddleSize[0])
    ballVel.data:ballVel.data*(1,-1)
   else
    done:1
   end
  end
  ballPos.data:ballPos.data+ballVel.data
  if done reset[0] end
 end
end

{end}

{card:contraptionoriented}
script:"contraptionoriented.0"
{widgets}
start:{"type":"button","size":[29,30],"pos":[236,253],"show":"none","style":"invisible"}
wall2:{"type":"contraption","size":[25,273],"pos":[464,58],"def":"wall","widgets":{}}
wall1:{"type":"contraption","size":[29,273],"pos":[7,58],"def":"wall","widgets":{}}
wall3:{"type":"contraption","size":[450,19],"pos":[26,39],"def":"wall","widgets":{}}
field1:{"type":"field","size":[135,36],"pos":[498,39],"locked":1,"font":"deckbuilder","show":"invert","border":0,"align":"center","value":"Contraptions"}
field2:{"type":"field","size":[135,245],"pos":[498,82],"locked":1,"value":"Click on the paddle to the left to begin a game. The game will end if you destroy every brick or miss the ball with your paddle.\n\nThis approach factors game logic into contraptions for walls, bricks, the paddle, and the ball, each of which function independently. Using contraptions like this to make game components \"physical\" makes it easier to try variations of a design and reduces the need for hardcoded constants, but may make resetting game state more complex. Here we manipulate the \"show\" property of bricks to remove or restore them and use an invisible \"dummy\" button to control initial ball position."}
button1:{"type":"button","size":[135,20],"pos":[498,340],"script":"blocking.1","text":"Back","style":"rect"}
paddle1:{"type":"contraption","size":[161,30],"pos":[437,292],"show":"transparent","def":"paddle","widgets":{"b":{"size":[161,30]}}}
ball:{"type":"contraption","size":[20,20],"pos":[240,258],"show":"transparent","def":"ball","widgets":{"v":{}}}
brick1:{"type":"contraption","size":[65,21],"pos":[76,87],"def":"brick","widgets":{}}
brick2:{"type":"contraption","size":[65,21],"pos":[170,87],"def":"brick","widgets":{}}
brick3:{"type":"contraption","size":[65,21],"pos":[264,87],"def":"brick","widgets":{}}
brick4:{"type":"contraption","size":[65,21],"pos":[354,87],"def":"brick","widgets":{}}
brick5:{"type":"contraption","size":[65,21],"pos":[76,126],"def":"brick","widgets":{}}
brick6:{"type":"contraption","size":[65,21],"pos":[170,126],"def":"brick","widgets":{}}
brick7:{"type":"contraption","size":[65,21],"pos":[264,126],"def":"brick","widgets":{}}
brick8:{"type":"contraption","size":[65,21],"pos":[354,126],"def":"brick","widgets":{}}
brick9:{"type":"contraption","size":[65,21],"pos":[76,166],"def":"brick","widgets":{}}
brick10:{"type":"contraption","size":[65,21],"pos":[170,166],"def":"brick","widgets":{}}
brick11:{"type":"contraption","size":[65,21],"pos":[264,166],"def":"brick","widgets":{}}
brick12:{"type":"contraption","size":[65,21],"pos":[354,166],"def":"brick","widgets":{}}

{script:contraptionoriented.0}
on get_bricks do
 extract value where value..def.name="brick" from card.widgets
end

on destroy do
 if !sum get_bricks[]..show="solid" reset[] end
end

on begin do
 reset[]
end

on reset do
 ball.pos:start.pos+.5*start.size-ball.size
 ball.vel:if mag ball.vel 0,0 else 2,-2 end
 get_bricks[]..show:"solid"
end
{end}

{contraption:paddle}
size:[86,17]
resizable:1
margin:[5,5,5,5]
description:"a breakout paddle that moves horizontally"
image:"%%IMG2AFYAEQBYAVIAAwFUAAIBVAACAVQAAgEEFEwBBAACAQQUTAEEAAIBBBRMAQQAAgEEFEwBBAACAQQUTAEEAAIBBBRMAQQAAgEEFEwBBAACAVQAAgFUAAIBVAADAVIAWA=="
script:"paddle.0p"
{widgets}
b:{"type":"button","size":[86,17],"pos":[0,0],"animated":1,"show":"transparent","style":"invisible"}

{script:paddle.0p}
on get_thud do
 1
end

on view do
 card.pos:(pointer.pos[0]-.5*card.size[0]),card.pos[1]
end

on click do
 card.parent.event.begin
end
{end}

{contraption:wall}
size:[16,16]
resizable:1
margin:[0,0,0,0]
description:"a breakout wall that balls bounce off of"
image:"%%IMG2ABAAEBX/FQE="
script:"wall.0p"

{script:wall.0p}
on get_thud do
 1
end

{end}

{contraption:brick}
size:[65,21]
resizable:1
margin:[0,0,0,0]
description:"a breakout brick which can be broken if a ball hits it"
image:"%%IMG2AEEAFQH/Af8B/wH/Af8BWg=="
script:"brick.0p"

{script:brick.0p}
on get_thud do
 if card.show~"solid"
  card.show:"none"
  card.parent.event.destroy
  1
 end
end
{end}

{contraption:ball}
size:[20,20]
margin:[0,0,0,0]
description:"a breakout ball that moves based on its velocity and thuds off other contraptions it overlaps."
image:"%%IMG0ABQAFAH4AA//AB//gD//wH//4H//4H//4P//8P//8P//8P//8P//8P//8H//4H//4H//4D//wB//gA//AAH4AA=="
script:"ball.0p"
attributes:{"name":[],"label":[],"type":[]}
{widgets}
v:{"type":"field","size":[44,20],"pos":[39,-19],"locked":1,"animated":1,"value":"[0,0]"}

{script:ball.0p}
on get_vel do v.data end
on set_vel x do v.data:x end
on view do
 c:card.pos+.5*card.size
 n:get_vel[]+c
 p:card.parent
 thuds:0,0
 each w in card.name drop p.widgets
  if min (n>w.pos)&(n<w.pos+w.size)
   if w.thud
    b:!(c>w.pos)&(c<w.pos+w.size)
    if 2=sum b b:0,1 end # prefer vertical bounces
    thuds:thuds|b
   end
  end
 end
 if max (n<0)|(n>p.size)
  card.parent.event.reset
 else
  set_vel[get_vel[]*(1,-1)@thuds]
  card.pos:get_vel[]+card.pos
 end
end
{end}

