It has a fixed time step and a probably really poor use and abuse of CallFunctionFast for pseudo objects. And it should compile and run in the demo version of purebasic(because that's all I had)! I've been away from this beautiful language for some time now so I expect there to be many mistakes or errors that I haven't encountered yet.
Code: Select all
Enumeration 0
  #Ball
  #Brick0
  #RGB1
  #RGB2
  #RGB3
  #RGB4
  #Text1
  #Text2
  #Text3
  #Paddle1
  #Particle1
  #Sheet1
  #Paddle2
  #Paddle3
  #Pickup0; base image
  #Pickup1
  #Pickup2;
  #Pickup3;
  #Pickup4;
  #Pickup5;
  #Pickup6;
  #Pickup7;
  #Pickup8;
  #Pickup9;
  #Pickup10;
  #Font1
EndEnumeration
;-Object structure
Structure Object
  id.l ; set when added to object list
  type.l ;
  *parent;
  x.d;
  y.d;
  w.d; ; width/height
  h.d;
  dir.d[2] ; movement vector
  speed.l
  intangible.b ; used for skipping objects that don't need collision testing such as particles
  removed.b; removes from object list
  expires.l ; similar to removed.b but delayed!
  ; function pointers
  *fnConstructor ; unused, but could probably replace type
  *fnDestructor
  *fnRender
  *fnStep
  *extends  ; pointer to an object defining structure
EndStructure
Structure BrickObject
  hp.l
  gfx.b;
EndStructure
Structure PickupObject
  type.l
EndStructure
Structure PaddleObject
  size.l
  x2.d
EndStructure
Structure BallObject
  lastParticleCreation.l
EndStructure
Structure ParticleObject
EndStructure
Structure Vector
  x.d
  y.d
EndStructure
Structure Profile
  tps.l
  tpsc.l
  tpst.l
  count1.l
  count2.l
EndStructure
;-Game state structure
Structure State
  width.l ; window dimensions
  height.l
  dt.l
  now.l
  running.b
  active.b ;
  score.l
  balls.l
  bricks.l
  gameover.b;
  continues.l
  drawing.Profile
  logic.Profile
  uuidAccumulator.l ; used to create unique id's for objects
  *paddle.Object; used for
  Array objectsList.Object(1)
  Array objectsQueue.Object(1)
EndStructure
;-Object declarations
Declare Paddle(*state.State, *ref.Object, x.l, y.l) ; constructor
Declare paddleDestructor(*ref.Object)
Declare paddleDraw(*ref.Object, dt.l)
Declare paddleStep(*ref.Object, *state.State)
Declare Ball(*state.State, *ref.Object, x.l, y.l)
Declare ballDestructor(*ref.Object)
Declare ballDraw(*ref.Object, dt.l)
Declare ballParticleSpawner(*this.Object, *state.State, delay.l=100, reverseDir.b = 1)
Declare ballStep(*ref.Object, *state.State)
Declare Brick(*state.State, *ref.Object, x.l, y.l, hp.l)
Declare brickDestructor(*ref.Object)
Declare brickDraw(*ref.Object, dt.l)
Declare brickStep(*ref.Object, *state.State)
Declare brickParticleSpawner(*this.BallObject, *state.State, delay.l=100, reverseDir.b=1)
Declare Particle(*state.State, *ref.Object, *parent.Object, x.l, y.l, *direction.Vector, expires.l)
Declare particleDestructor(*ref.Object)
Declare particleDraw(*ref.Object, dt.l)
Declare particleStep(*ref.Object, *state.State)
Declare Pickup(*state.State, *ref.Object, x.l, y.l, type.l)
Declare pickupDestructor(*ref.Object)
Declare pickupDraw(*ref.Object, dt.l)
Declare pickupStep(*ref.Object, *state.State)
; boring things declarations
Declare intersect2d(*a.Object, *b.Object)
Declare addObject(*game.State, *ref);
Declare flush(*state.State)
Declare update(*state.State)
Declare render(*state.State)
Declare isOffScreen(*this.Object, *state.State)
;Declare
;-Particle definition
Procedure Pickup(*state.State, *ref.Object, x.l, y.l, type.l);, reverseDir.b = 1)
  *ptr.PickupObject = AllocateStructure(PickupObject)
  *ptr\type = #Pickup0 + type
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x
    \y = y
    \w = 32 ;
    \h = 32
    \expires = 0
    \dir[0] = 0.0
    \dir[1] = 1.0
    \speed = 3
    \fnRender = @pickupDraw()
    \fnStep = @pickupStep()
    \fnConstructor = @Pickup()
    \fnDestructor = @pickupDestructor()
    \intangible = 0
  EndWith
EndProcedure
Procedure pickupDestructor(*ref.Object)
  FreeStructure(*ref\extends)
EndProcedure
Procedure pickupApply(*state.State, *this.Object, type.l=-1)
  *self.PickupObject = *this\extends
  *thatObj.PaddleObject = *state\paddle\extends
  If (type.l = -1)
    type = *self\type
  EndIf
 
  Select type
    Case #Pickup1:
      *state\continues+1
    Case  #Pickup4:
      *thatObj\size + 1
    Case  #Pickup5:
      If (*thatObj\size > 1)
        *thatObj\size - 1
      EndIf
    Case  #Pickup7:
      Ball(*state, obj.Object, *this\x, *this\y)
      addObject(*state, obj);
    Case  #Pickup8:
      pickupApply(*state, *this, Random(9,0));
    Default: *state\score = *state\score / 0.25
  EndSelect
  ProcedureReturn 0
EndProcedure
   
Procedure pickupStep(*this.Object, *state.State);
  *self.PickupObject = *this\extends
  With *this
    \x = \x + (\dir[0] * \speed)
    \y = \y + (\dir[1] * \speed)
  EndWith
  If (isOffScreen(*this, *state))
    *this\removed = 1
  EndIf
    *that.Object = *state\paddle
    collision = intersect2d(*this, *that)
    If (*that\fnConstructor = @Paddle() And collision > 0 And *this\removed = 0)
      pickupApply(*state, *this)
      *this\removed = 1
      ballParticleSpawner(*this, *state, 10, 1)
    EndIf
EndProcedure
Procedure pickupDraw(*this.Object, dt.l)
  *self.PickupObject = *this\extends
  DisplayTransparentSprite(#Pickup0, *this\x, *this\y);
  DisplayTransparentSprite(*self\type, *this\x, *this\y);
EndProcedure
;-Paddle definition
Procedure Paddle(*state.State, *ref.Object, x.l, y.l)
  *ptr.PaddleObject = AllocateStructure(PaddleObject)
  *ptr\size = 5
  With *ref
    \extends = *ptr
    \x = x
    \y = y
    \removed = 0
    \w = 100
    \h = 16
    \fnRender = @paddleDraw()
    \fnStep = @paddleStep()
    \fnConstructor = @Paddle()
    \fnDestructor = @paddleDestructor()
  EndWith
EndProcedure
Procedure paddleDestructor(*ref.Object)
EndProcedure
Procedure paddleParticleSpawner(*this.Object, *state.State, side.b);
  For k = 1 To 2
    dir.Vector\y = 1.0 + ((-4 + Random(8, 0)) / 10);;
    dir\x = 0.0  + ((-4 + Random(8, 0)) / 10)
    pos.Vector\x = *this\x + 5
    pos\y = *this\y + *this\h;
    If (side = 1)
      pos\x = *this\x + *this\w - 10
    EndIf
    Particle(*state, particleObj.Object, *this, pos\x, pos\y, dir, *state\dt + Random(300, 100))
    addObject(*state, particleObj)
  Next
EndProcedure
Procedure paddleStep(*this.Object, *state.State);
  *state\paddle = *this
  *self.PaddleObject = *this\extends
  *this\x = MouseX()
  *this\w = 32 + (*self\size * 6 * 2)
  If (*this\x + *this\w > *state\width)
    *this\x = *state\width - *this\w 
  EndIf 
  paddleParticleSpawner(*this, *state, 0)
  paddleParticleSpawner(*this, *state, 1)
EndProcedure
Procedure paddleDraw(*this.Object, dt.l)
  *self.PaddleObject = *this\extends
  size = *self\size * 2
  DisplayTransparentSprite(#Paddle1, *this\x, *this\y)
  DisplayTransparentSprite(#Paddle3, *this\x + 17 + (6 * size), *this\y)
  For l = 1 To size
    DisplayTransparentSprite(#Paddle2, *this\x + 5 + ( 6 * l), *this\y + 1)
  Next
EndProcedure
;-Ball definition
Procedure Ball(*state.State, *ref.Object, x.l, y.l)
  *state\balls + 1;
  *ptr.BallObject = AllocateStructure(BallObject)
  *ptr\lastParticleCreation = 0
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x;width / 2
    \y = y;height - 50 ;
    \removed = 0        ;
    \speed = 10
    \w = 16 ;
    \h = 16
    \dir[0] = 0.0
    \dir[1] = -1.0
    \fnRender = @ballDraw()
    \fnStep = @ballStep()
    \fnConstructor = @Ball()
    \fnDestructor = @ballDestructor()
  EndWith
EndProcedure
Procedure ballDestructor(*ref.Object)
  FreeStructure(*ref\extends)
EndProcedure
Procedure ballParticleSpawner(*this.Object, *state.State, delay.l=100, reverseDir.b = 1)
  *self.BallObject = *this\extends
  If (*state\dt > *self\lastParticleCreation + delay)
    *self\lastParticleCreation = *state\dt
    For i = 0 To 10
      dir.Vector\x = -((-8 + Random(16, 0)) / 10)
      dir\y = -((-8 + Random(16, 0)) / 10)
      Particle(*state, particleObj.Object, *this, *this\x, *this\y, dir, *state\dt + Random(300, 50))
      addObject(*state, particleObj)
    Next
  EndIf
EndProcedure
Procedure ballStep(*this.Object, *state.State);
  *self.BallObject = *this\extends
 
  If (*state\active = 1)
    tmp.Object
    With tmp ; a copy of the object with future transforms applied to it
      \x = *this\x + (*this\dir[0] * *this\speed)
      \y = *this\y + (*this\dir[1] * *this\speed)
      \w = *this\w
      \h = *this\h
    EndWith
   
    For i = 0 To ArraySize(*state\objectsList()) - 1
      that.Object = *state\objectsList(i);
      ; todo: collision will fail if a balls speed is more than the width or height of an object it could collide with
      collision = intersect2d(tmp, that)
      If (*this\id <> that\id And that\intangible = 0 And collision > 0)
        If (that\fnConstructor = @Brick())
          *ptr.BrickObject = that\extends
          *ptr\hp = *ptr\hp - 1
         
          top.Object\x = that\x + 1
          top\y = that\y - that\h
          top\w = that\w - 2
          top\h = that\h + 5
          bot.Object\x = that\x + 1;
          bot\y = that\y + that\h - 5
          bot\w = that\w - 2
          bot\h = that\h
         
         
          left.Object\x = that\x - that\w
          left\y = that\y + 1
          left\w = that\w + 5
          left\h = that\h - 2
          right.Object\x = that\x + that\w - 5;
          right\y = that\y + 1
          right\w = that\w
          right\h = that\h - 2
          If (intersect2d(tmp, top) Or intersect2d(tmp, bot))
            *this\dir[1] = -(*this\dir[1])
          ElseIf (intersect2d(tmp, left) Or intersect2d(tmp, right))
            *this\dir[0] = -(*this\dir[0])
          Else
            *this\dir[0] = -(*this\dir[0])
            *this\dir[1] = -(*this\dir[1])
          EndIf
         
          *state\score + 1
          ballParticleSpawner(*this, *state, 10, 1)
        ElseIf (that\fnConstructor = @Paddle())
          rads = (((that\x - *this\x) + ((that\w - *this\w )/2) )) * #PI
          *this\dir[0] = -Sin(rads / 180)
          *this\dir[1] = -Cos(rads / 180)
          ballParticleSpawner(*this, *state, 10, 1)
        EndIf
      EndIf
    Next
   
    c2 = isOffScreen(tmp, *state)
    If (c2 = 4) ; bottom of the screen hit
      *this\removed = 1
      *state\balls - 1
    ElseIf (c2 = 3) ; top
      *this\dir[1] = -(*this\dir[1])
    ElseIf (c2 > 0)
      *this\dir[0] = -(*this\dir[0])
    EndIf
   
    If (c2 > 0)
      ballParticleSpawner(*this, *state, 10, 0)
    EndIf
   
    With *this
      \x = \x + (\dir[0] * \speed)
      \y = \y + (\dir[1] * \speed)
    EndWith
  Else
    With *this
      \x = *state\paddle\x + ((*state\paddle\w/2) - (\w/2))
      \y = *state\paddle\y - 20
    EndWith
  EndIf
EndProcedure
Procedure ballDraw(*this.Object, dt.l)
  DisplayTransparentSprite(#Ball, *this\x, *this\y);
EndProcedure
;-Particle definition
Procedure Particle(*state.State, *ref.Object, *parent.Object, x.l, y.l, *direction.Vector, expires.l);, reverseDir.b = 1)
  *ptr.ParticleObject = AllocateStructure(ParticleObject)
  s = Random(5, 1)
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x
    \y = y
    \w = s ;
    \h = s
    \expires = expires
    \dir[0] = *direction\x
    \dir[1] = *direction\y
    \speed = Random(10, 0);
    \fnRender = @particleDraw()
    \fnStep = @particleStep()
    \fnConstructor = @Particle()
    \fnDestructor = @particleDestructor()
    \intangible = 1
  EndWith
EndProcedure
Procedure particleDestructor(*ref.Object)
  FreeStructure(*ref\extends)
EndProcedure
Procedure particleStep(*this.Object, *state.State);
  *self.ParticleObject = *this\extends
  With *this
    \x = \x + (\dir[0] * \speed)
    \y = \y + (\dir[1] * \speed)
    \speed = \speed/2; - \speed
  EndWith   
EndProcedure
Procedure particleDraw(*this.Object, dt.l)
  ZoomSprite(#Particle1, *this\w, *this\h)
  DisplayTransparentSprite(#Particle1, *this\x, *this\y);
EndProcedure
;-Brick definition
Procedure Brick(*state.State, *ref.Object, x.l, y.l, hp.l)
  *state\bricks + 1
  *ptr.BrickObject = AllocateStructure(BrickObject)
  *ptr\hp = hp
  *ptr\gfx = Random(3, 0);
  With *ref
    \extends = *ptr
    \x = x
    \y = y
    \removed = 0;
    \w = 64;
    \h = 32;
    \fnRender = @brickDraw()
    \fnStep = @brickStep()
    \fnConstructor = @Brick()
    \fnDestructor = @brickDestructor()
  EndWith
EndProcedure
Procedure brickDestructor(*ref.Object)
  ;*self.BrickObject = *ref\extends
  ;FreeMemory(*self\ptrtest)
  FreeStructure(*ref\extends)
EndProcedure
Procedure brickStep(*this.Object, *state.State);
  *self.BrickObject = *this\extends
  If (*self\hp < 1 And *this\removed = 0)
    *this\removed = 1
    *state\bricks - 1
    Pickup(*state, obj.Object, *this\x, *this\y, Random(10, 1))
    addObject(*state, obj);
  EndIf
EndProcedure
Procedure brickDraw(*this.Object, dt.l)
  *self.BrickObject = *this\extends
  DisplayTransparentSprite(#Brick0, *this\x, *this\y);
  DisplayTransparentSprite(#RGB1 + (*self\gfx), *this\x, *this\y);
EndProcedure
Procedure update(*state.State)
  size = ArraySize(*state\objectsList());
  For k = 0 To size - 1
    If (*state\objectsList(k)\removed = 1)
      Continue
    EndIf
   
    *addr = *state\objectsList(k)\fnStep;
    CallFunctionFast(*addr, *state\objectsList(k), *state);
  Next
  If (*state\bricks = 0 And *state\gameover = 0)
    *state\gameover = 1
  EndIf
  If (*state\balls = 0 And *state\gameover = 0)
    If (*state\continues = 0)
      *state\gameover = 2
    Else
      *state\continues - 1
      *state\active = 0
      Ball(*state, obj.Object, 0, 0)
      addObject(*state, obj)
    EndIf
  EndIf
  If (*state\active = 0 And MouseButton(#PB_MouseButton_Left) = 1)
    *state\active = 1
  EndIf
EndProcedure
Procedure render(*state.State)
  ClearScreen(RGB(50, 100, 140));
  For k = 0 To ArraySize(*state\objectsList()) - 1
    item.Object = *state\objectsList(k)
    If (item\removed = 1)
      Continue
    EndIf
    *addr = *state\objectsList(k)\fnRender;
    CallFunctionFast(*addr, *state\objectsList(k), *state);
    ;StartDrawing(ScreenOutput()) ; show bounds
    ;Line(item\x, item\y, item\w, 1, #Red)
    ;Line(item\x, item\y+item\h, item\w, 1, #Red)
    ;Line(item\x, item\y, 1, item\h, #Red)
    ;Line(item\x+item\w, item\y, 1, item\h, #Red)
    ;Line(item\x, item\y, item\w, item\h, #Red)
    ;StopDrawing()
  Next 
  DisplayTransparentSprite(#Text1, *state\width - 150, 10);
  DisplayTransparentSprite(#Text2, 10, 10)
  If (*state\gameover > 0 Or *state\active = 0)
    DisplayTransparentSprite(#Text3, *state\width/2 - 150, *state\height/2)
  EndIf
   
  FlipBuffers();
EndProcedure
Procedure addObject(*game.State, *ref)
  o = ArraySize(*game\objectsQueue());
  ReDim *game\objectsQueue.Object(o+1);
  CopyStructure(*ref, @dref.Object, Object)
  *game\objectsQueue(o) = dref;
EndProcedure
Procedure remakeHUDSprite(*state.State) 
  If (IsSprite(#Text2))
    FreeSprite(#Text2)
  EndIf
 
  CreateSprite(#Text2, 200, 75);
  StartDrawing(SpriteOutput(#Text2))
  DrawingMode(#PB_2DDrawing_Transparent)
  DrawText(0, 0, "Score: " + Str(*state\score), RGB(1, 0, 0));
  DrawText(0, 20, "Continues: " + Str(*state\continues), RGB(1, 0, 0));
  StopDrawing()
EndProcedure
Procedure remakeGameOverSprite(*state.State, msg.s)
  If (IsSprite(#Text3))
    FreeSprite(#Text3)
  EndIf
  CreateSprite(#Text3, 300, 100);
  StartDrawing(SpriteOutput(#Text3))
  DrawingMode(#PB_2DDrawing_Transparent)
  DrawingFont(FontID(#Font1))
  DrawText(0, 0, msg, RGB(1, 0, 0));
  StopDrawing()
EndProcedure
Procedure remakeProfileSprite(*state.State) 
  If (IsSprite(#Text1))
    FreeSprite(#Text1)
  EndIf
 
  CreateSprite(#Text1, 200, 120);
  StartDrawing(SpriteOutput(#Text1))
  DrawingMode(#PB_2DDrawing_Transparent)
  DrawText(0, 0, Str(*state\drawing\tps) + " - frames per sec", RGB(1, 0, 0));
  DrawText(0, 20, Str(*state\logic\tps) + " - steps per sec", RGB(1, 0, 0));
  DrawText(0, 40, Str(*state\logic\count1) + " - object count", RGB(1, 0, 0)) ;
  DrawText(0, 60, Str(*state\logic\count2) + " - queue count", RGB(1, 0, 0)) 
  DrawText(0, 80, Str(*state\balls) + " - balls", RGB(1, 0, 0)) 
  DrawText(0, 100, Str(*state\bricks) + " - bricks", RGB(1, 0, 0))  ;
  StopDrawing()
EndProcedure
Procedure init(*state.State)
  LoadFont(#Font1, "Arial", 32, #PB_Font_Bold)
  remakeProfileSprite(*state);
  remakeHUDSprite(*state)
  remakeGameOverSprite(*state, "Click to Start!")
  LoadSprite(#Sheet1, "E:/assets/brickbreaker.png", #PB_Sprite_AlphaBlending);
 
  CopySprite(#Sheet1, #Paddle1, #PB_Sprite_AlphaBlending) ; left
  ClipSprite(#Paddle1, 0, 0, 16, 16)
  CopySprite(#Sheet1, #Paddle2, #PB_Sprite_AlphaBlending) ; blue thing
  ClipSprite(#Paddle2, 16, 0, 16, 16)
  CopySprite(#Sheet1, #Paddle3, #PB_Sprite_AlphaBlending) ; right
  ClipSprite(#Paddle3, 32, 0, 16, 16) 
 
  CopySprite(#Sheet1, #Ball, #PB_Sprite_AlphaBlending)
  ClipSprite(#Ball, 16 * 3, 0, 16, 16)
 
  CopySprite(#Sheet1, #Brick0, #PB_Sprite_AlphaBlending)
  ClipSprite(#Brick0, 0, 64, 64, 32)
  CreateSprite(#Particle1, 25, 25);
  StartDrawing(SpriteOutput(#Particle1))
  Box(0, 0, 20, 20, RGB(0, 0, 0))       ;
  DrawingMode(#PB_2DDrawing_Default)
  Box(0, 0, 50, 25, RGB(255, 255, 255));
  StopDrawing()
 
  For k = 0 To 3
    CreateSprite(#RGB1 + k, 64, 32, #PB_Sprite_AlphaBlending);
    StartDrawing(SpriteOutput(#RGB1 + k))
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    Box(0, 0, 64, 32, $00000000)
    DrawingMode(#PB_2DDrawing_AlphaBlend)
    Box(0, 0, 64, 32, RGBA(Random(255, 0), Random(255, 0), Random(255, 0), 100))
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    Box(0, 0, 2, 2, $00000000)
    Box(62, 0, 2, 2, $00000000)
    Box(0, 30, 2, 2, $00000000)
    Box(62, 30, 2, 2, $00000000)
    StopDrawing()
  Next
 
  CopySprite(#Sheet1, #Pickup0, #PB_Sprite_AlphaBlending)
  ClipSprite(#Pickup0, 64, 0, 16, 16)
  ZoomSprite(#Pickup0, 32, 32)
  For k = 0 To 10
    CopySprite(#Sheet1, #Pickup1 + k, #PB_Sprite_AlphaBlending)
    ClipSprite(#Pickup1 + k, (16 * k), 16, 16, 16)
    ZoomSprite(#Pickup1 + k, 32, 32)
  Next
   
  Paddle(*state, paddleObj.Object, *state\width / 2 - 50, *state\height - 100)
  addObject(*state, paddleObj)
 
  Ball(*state, ballObj.Object, 0, 0)
  addObject(*state, ballObj)
 
  For x = 0 To 59;99
    tx = 50 + ((x % 10) * 70)
    ty = 100 + ((x / 10) * 50)
    Brick(*state, brickObj.Object, tx, ty, 1+(ty%3))
    addObject(*state, brickObj)
  Next
EndProcedure
Procedure isOffScreen(*this.Object, *state.State)
  If (*this\x < 0) : ProcedureReturn 1 ; left
  ElseIf (*this\x + *this\w > *state\width) : ProcedureReturn 2 ; right
  ElseIf (*this\y < 0) : ProcedureReturn 3 ; top
  ElseIf (*this\y + *this\h > *state\height) : ProcedureReturn 4 : EndIf ; bottom
  ProcedureReturn 0
EndProcedure
Procedure intersect2d(*o.Object, *b.Object)
  ax.d = *o\x
  ay.d = *o\y
  au.d = *o\x + *o\w
  av.d = *o\y + *o\h
   
  bx.d = *b\x
  by.d = *b\y
  bu.d = *b\x + *b\w
  bv.d = *b\y + *b\h
 
  If (ax <= bu And au >= bx And ay <= bv And av >= by)
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure
Procedure flush(*state.State)
  size = ArraySize(*state\objectsList())
  For i = 0 To size - 1 ; clean up objects that were marked for removal
    If (*state\objectsList(i)\removed = 1 Or (*state\objectsList(i)\expires > 0 And *state\dt > *state\objectsList(i)\expires))
      *addr = *state\objectsList(i)\fnDestructor;
      CallFunctionFast(*addr, *state\objectsList(i));
      ; i dont know how to properly resize arrays in purebasic so hopefully this won't be too slow :(
      *state\objectsList(i) = *state\objectsList(size - 1) ; swap removing object with one at end
      ReDim *state\objectsList.Object(size - 1)
      size = size -1; have to modify loop or bad things will happen
      i = i - 1; the object that was swapped must also be checked for removal
    EndIf
  Next
  n = ArraySize(*state\objectsQueue());
  If (n > 0) ; merge queued objects into the object list
    o = ArraySize(*state\objectsList());
    If (o >= 1000) ; this is just an arbitrary size limit for the number of objects to allow
      ReDim *state\objectsQueue(0);
      ProcedureReturn
    EndIf
    ReDim *state\objectsList(o+n);
    For k = 0 To n ; the merge part
      *state\uuidAccumulator = *state\uuidAccumulator + 1
      *state\objectsList(o+k) = *state\objectsQueue(k);
      *state\objectsList(o+k)\id = *state\uuidAccumulator
      If (*state\objectsList(o+k)\fnConstructor = @Paddle())
        *state\paddle = *state\objectsList(o+k)
      EndIf
    Next
    ReDim *state\objectsQueue(0)
  EndIf
EndProcedure
With game.State
  \width = 800
  \height = 800
  \running = 1
  \score = 0
  \continues = 1
  \now = ElapsedMilliseconds()
  \dt = ElapsedMilliseconds();
  Dim \objectsQueue.Object(0)
  Dim \objectsList.Object(0)
EndWith
InitSprite()
InitMouse()
UsePNGImageDecoder()
OpenWindow(0, 0, 0, game\width, game\height, "Nostalgia", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget);
OpenWindowedScreen(WindowID(0), 0, 0, game\width, game\height, #False, #Null, #Null, #PB_Screen_NoSynchronization ); 
init(game)
;-Loop
While game\running = 1
  Select WindowEvent()
    Case #PB_Event_CloseWindow
      game\running = 0
  EndSelect
  ExamineMouse()
  If (game\dt > game\drawing\tpst + 1000)
    game\drawing\tps = game\drawing\tpsc;
    game\drawing\tpsc = 0  ;
    game\logic\tps = game\drawing\tpsc;
    game\logic\tpsc = 0  ;
    game\drawing\tpst = game\dt ;
    game\logic\count1 = ArraySize(game\objectsList())
    game\logic\count2 = ArraySize(game\objectsQueue())
    remakeProfileSprite(game);
  EndIf
 
  flush(game)
 
  game\now = ElapsedMilliseconds();
  If (game\now - game\dt > 1000) ; skip some logic steps because previous one took too long
    game\dt = game\now;
  EndIf
 
  CopyStructure(@game, @prev.State, State) ; a copy of state to compare with after updating
  While game\dt < game\now ; step game objects at a constant rate
    game\dt = game\dt + 1000/30;
    update(game);
    game\logic\tpsc=game\logic\tpsc+1;
  Wend
 
  If (prev\score <> game\score Or prev\continues <> game\continues)
    remakeHUDSprite(game)
  EndIf
  If (prev\gameover <> game\gameover Or prev\active <> game\active)
    msg.s = "Click to start!"
    If (game\gameover = 2)
      msg.s = "Game over!!"
    ElseIf (game\gameover = 1)
      msg.s = "You win!"
    EndIf
    remakeGameOverSprite(game, msg)
  EndIf
   
  render(game)
  game\drawing\tpsc=game\drawing\tpsc+1; 
Wend

