############################################################################
#
#	File:     breakout.icn
#
#	Subject:  Program for Breakout game
#
#	Author:   Nathan J. Ranks
#
#	Date:     November 22, 2009
#
############################################################################
#
#  This file is in the public domain.
#
############################################################################
#
#  Breakout game
#
#  Infinite balls, Left or Right click to start or restart after losing ball
#  9 levels - can select any level when not active using 1-9
#  1 hit, 2 hit, 3 hit, and invincible blocks can be used for levels
#
############################################################################
#
#  Requires:  Version 9 graphics
#
############################################################################
#
#  Links: graphics
#
############################################################################

link graphics 
global sphere, blank     #sphere and blank sphere
global X, Y              #coordinates of sphere
global block_positions   #string of whether or not position has block
global path, angle       #direction of sphere travel
global wait              #pause interval used with delay()
global level             #current level
global hit               #sphere and block contact flag
global blockclr1, blockclr2, blockclr3, invincclr

procedure main()
  local e
  blockclr1 := "dark blue"     #default 1 hit block color
  blockclr2 := "dark red"      #default 2 hit block color
  blockclr3 := "dark green"    #default 3 hit block color
  invincclr := "black"         #default invincible block color

  WOpen("size=293,320") | stop("can't open window") 

  sphere := "7,g16,~~000~~_
                   ~00000~_
                   0000000_
                   0000000_
                   0000000_
                   ~00000~_
                   ~~000~~"        #black sphere

  blank  := "7,g16,~~FFF~~_
                   ~FFFFF~_
		   FFFFFFF_
		   FFFFFFF_
		   FFFFFFF_
                   ~FFFFF~_
                   ~~FFF~~"        #white sphere to erase

  level := 1                   #default start level
  create_blocks()              #as the name suggests

  Fg("black")                  #default pad color
  DrawLine(124,310,158,310)    #default pad position
  DrawImage(140, 304, sphere)  #default sphere position
  X := 140                     #default x position
  Y := 304                     #default y position
  path := "up_left"            #default sphere direction
  angle := 60                  #default sphere angle
  hit := 0

repeat {
  if e := Event() then {   
    if ( e === &lpress ) then {
      Fg("black")
      DrawLine(124,310,158,310)    #reset default
      DrawImage(140, 304, sphere)  #reset default
      Y := 304                     #reset default
      path := "up_left"            #reset default
      angle := 60                  #reset default
      hit := 0

      X := &x
      DrawImage(140, 304, blank)
      move_pad()
      move_sphere()
    }
    if ( e === &rpress ) then {
      Fg("black")
      DrawLine(124,310,158,310)    #reset default
      DrawImage(140, 304, sphere)  #reset default
      Y := 304                     #reset default
      path := "up_right"           #reset default
      angle := 60                  #reset default
      hit := 0

      X := &x
      DrawImage(140, 304, blank)
      move_pad()
      move_sphere()
    }
    if ( e === "1" ) then {        #change to level 1
      level := 1
      create_blocks()
    }
    if ( e === "2" ) then {        #change to level 2
      level := 2
      create_blocks()
    }
    if ( e === "3" ) then {        #change to level 3
      level := 3
      create_blocks()
    }
    if ( e === "4" ) then {        #change to level 4
      level := 4
      create_blocks()
    }
    if ( e === "5" ) then {        #change to level 5
      level := 5
      create_blocks()
    }
    if ( e === "6" ) then {        #change to level 6
      level := 6
      create_blocks()
    }
    if ( e === "7" ) then {        #change to level 7
      level := 7
      create_blocks()
    }
    if ( e === "8" ) then {        #change to level 8
      level := 8
      create_blocks()
    }
    if ( e === "9" ) then {        #change to level 9
      level := 9
      create_blocks()
    }
  }
}
end



#this keeps track of where the pad should be according
#to where the mouse pointer is

procedure move_pad()
  &x := image(WAttrib("pointerx"))  #get pointer position
  &y := image(WAttrib("pointery"))  #get pointer position
  EraseArea(0,310,293,310)          #erease old pad
  Fg("black")                       #make sure color is correct
  DrawLine(&x-12,310,&x+12,310)     #draw new pad
return
end



#this keeps track of sphere location and movement within the window.
#hits on walls will change direction
#hit on pad will change direction and possibly angle

procedure move_sphere()
wait := 9
while ( Y < 312 ) do {
  if ( path == "up_right" ) then {
    delay(wait)
    move_pad()
    GO_UP_RIGHT()
    hit := 0
    if ( X > 285 ) then {
      path := "up_left"
    }
    if ( Y < 0 ) then {
      path := "down_right"
    }
  }
  if ( path == "up_left" ) then {
    delay(wait)
    move_pad()
    GO_UP_LEFT()
    hit := 0
    if ( X < 0 ) then {
      path := "up_right"
    }
    if ( Y < 0 ) then {
      path := "down_left"
    }
  }
  if ( path == "down_right" ) then {
    delay(wait)
    move_pad()
    GO_DOWN_RIGHT()
    hit := 0
    if ( X > 285 ) then {
      path := "down_left"
    }
    if ( (Y = 303) | (Y = 304) | (Y = 305) ) then {
      if ( ((X+1) < &x+13) & ((X+1) > &x-13) ) then {
        path := "up_right"
        if ( (X+1) > &x-13 ) then {
          angle := 30
        }
        if ( (X+1) > &x-6 ) then {
          angle := 60
        }
        if ( (X+1) > &x+6 ) then {
          angle := 30
        }
      }
    }
  }
  if ( path == "down_left" ) then {
    delay(wait)
    move_pad()
    GO_DOWN_LEFT()
    hit := 0
    if ( X < 0 ) then {
      path := "down_right"
    }
    if ( (Y = 303) | (Y = 304) | (Y = 305) ) then {
      if ( ((X+1) < &x+13) & ((X+1) > &x-13) ) then {
        path := "up_left"
        if ( (X+1) > &x-13 ) then {
          angle := 30
        }
        if ( (X+1) > &x-6 ) then {
          angle := 60
        }
        if ( (X+1) > &x+6 ) then {
          angle := 30
        }
      }
    }
  }
}
return
end


#these next 4 procedures move the sphere
#and then check for block contact

procedure GO_UP_RIGHT()
  if ( angle = 30 ) then {
    DrawImage(X, Y, blank)
    Y := Y - 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
  if ( angle = 60 ) then {
    DrawImage(X, Y, blank)
    Y := Y - 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    Y := Y - 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
return
end
procedure GO_UP_LEFT()
  if ( angle = 30 ) then {
    DrawImage(X, Y, blank)
    Y := Y - 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
  if ( angle = 60 ) then {
    DrawImage(X, Y, blank)
    Y := Y - 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    Y := Y - 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
return
end
procedure GO_DOWN_RIGHT()
  if ( angle = 30 ) then {
    DrawImage(X, Y, blank)
    Y := Y + 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
  if ( angle = 60 ) then {
    DrawImage(X, Y, blank)
    Y := Y + 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    Y := Y + 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X + 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
return
end
procedure GO_DOWN_LEFT()
  if ( angle = 30 ) then {
    DrawImage(X, Y, blank)
    Y := Y + 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
  if ( angle = 60 ) then {
    DrawImage(X, Y, blank)
    Y := Y + 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
    DrawImage(X, Y, blank)
    Y := Y + 1
    DrawImage(X, Y, sphere)
    DrawImage(X, Y, blank)
    X := X - 1
    DrawImage(X, Y, sphere)
    block_check()
    if ( hit = 1 ) then {
      fix_blocks()
      return
    }
  }
return
end



#this draws the play fields according to what the levels
#are defined as

procedure create_blocks()
  local x, y, z

  if ( level > 9 ) then {
    level := 1
  }

  #different play fields go here
  if ( level = 1 ) then { #icon-squared
    block_positions := "000000000000000000000000000000000000000100000000110100110111000001010010101110101101110101000000000000000111000001110010100000101001110000011100000000000000010101110110101110101001010000011101100101100000000100000000000000000000000000"
  }
  if ( level = 2 ) then { #alternate rows
    block_positions := "111111111111100000000000001111111111111000000000000011111111111110000000000000111111111111100000000000001111111111111000000000000011111111111110000000000000111111111111100000000000001111111111111000000000000011111111111110000000000000"
  }
  if ( level = 3 ) then { #alternating columns
    block_positions := "101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101"
  }
  if ( level = 4 ) then { #heart
    block_positions := "000100000100000101000101000100010100010010001010001001000101000100100001000010010000100001000100000001000010000000100001000000010000010000010000001000001000000010001000000000101000000000001000000000000100000000000010000000000000000000"
  }
  if ( level = 5 ) then { #checker board
    block_positions := "101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010"
  }
  if ( level = 6 ) then { #filled up
    block_positions := "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
  }
  if ( level = 7 ) then { #diamond and a half
    block_positions := "000001110000000001111100000001111111000001111111110001111111111101111111111111011111111111000111111111000001111111000000011111000000000111000000000001000000000001110000000001111100000001111111000001111111110001111111111101111111111111"
  }
  if ( level = 8 ) then { #misc multiple hits
    block_positions := "11111111111111111111111111313131313131311111111111112121212121212X3X2X3X2X3X2X111111111111111111111111113131313131313111111111111112121212121213X2X3X2X3X2X31111111111111111111111111131313131313132121212121212X3X2X3X2X3X2X1111111111111"
  }
  if ( level = 9 ) then { #throw-rug
    block_positions := "21111111111121211111111121112111111121111121111121111111211121111211112121111212111121111211121123211211111223X322111111223X32211111211232112111211112111121211112121111211112111211111112111112111112111111121112111111111212111111111112"
  }


  z := 1
  y := 10
  x := 10
  while not ( y = 208 ) do {
    while not ( x = 283 ) do {
      if ( block_positions[z] == "0" ) then {
        Fg("white")
        FillRectangle(x,y,20,10)
      }
      if ( block_positions[z] == "1" ) then {
        Fg(blockclr1)
        FillRectangle(x,y,20,10)
      }
      if ( block_positions[z] == "2" ) then {
        Fg(blockclr2)
        FillRectangle(x,y,20,10)
      }
      if ( block_positions[z] == "3" ) then {
        Fg(blockclr3)
        FillRectangle(x,y,20,10)
      }
      if ( block_positions[z] == "X" ) then {
        Fg(invincclr)
        FillRectangle(x,y,20,10)
      }
      z := z + 1
      x := x + 21
    }
    x := 10
    y := y + 11
  }
return
end


#this checks to see if the sphere contacts an edge
#of a block, if so, it erases the block and changes
#the sphere's direction accordingly
#it also checks if level is finished

procedure block_check()
  local x, y, z, temp, temp2
  z := 1
  y := 10
  x := 10
  while not ( y = 208 ) do {
    while not ( x = 283 ) do {
      if ( (((X+1)>(x-1))&((X+1)<(x+21)))&(((Y+1)>(y-1))&((Y+1)<(y+11))) ) then {
        if ( block_positions[z] == "X" ) then {
          hit := 1
          Fg(invincclr)
          FillRectangle(x,y,20,10)
          if ( path == "up_right" ) then {
            if ( ((X+1)=x) ) then { #side hit
              path := "up_left"
            }
            if ( ((Y+1)=(y+10)) ) then { #bottom hit
              path := "down_right"
            }
            if ( ((X+1)=x)&((Y+1)=(y+10)) ) then { #diagonal hit
              path := "down_left"
            }
          }
          else {
            if ( path == "up_left" ) then {
              if ( ((X+1)=(x+20)) ) then { #side hit
                path := "up_right"
              }
              if ( ((Y+1)=(y+10)) ) then { #bottom hit
                path := "down_left"
              }
              if ( ((X+1)=(x+20))&((Y+1)=(y+10)) ) then { #diagonal hit
                path := "down_right"
              }
            }
            else {
              if ( path == "down_left" ) then {
                if ( ((X+1)=(x+20)) ) then { #side hit
                  path := "down_right"
                }
                if ( ((Y+1)=y) ) then { #top hit
                  path := "up_left"
                }
                if ( ((X+1)=(x+20))&((Y+1)=y) ) then { #diagonal hit
                  path := "up_right"
                }
              }
              else {
                if ( path == "down_right" ) then {
                  if ( ((X+1)=x) ) then { #side hit
                    path := "down_left"
                  }
                  if ( ((Y+1)=y) ) then { #top hit
                    path := "up_right"
                  }
                  if ( ((X+1)=x)&((Y+1)=y) ) then { #diagonal hit
                    path := "up_left"
                  }
                }
              }
            }
          }
        }
        if ( (block_positions[z] == "1") |
             (block_positions[z] == "2") |
             (block_positions[z] == "3") ) then {
          hit := 1
          if ( block_positions[z] == "1" ) then {
            Fg("white")
            FillRectangle(x,y,20,10)
            block_positions[z] := "0"
          }
          if ( block_positions[z] == "2" ) then {
            Fg(blockclr1)
            FillRectangle(x,y,20,10)
            block_positions[z] := "1"
          }
          if ( block_positions[z] == "3" ) then {
            Fg(blockclr2)
            FillRectangle(x,y,20,10)
            block_positions[z] := "2"
          }
          if ( path == "up_right" ) then {
            if ( ((X+1)=x) ) then { #side hit
              path := "up_left"
            }
            if ( ((Y+1)=(y+10)) ) then { #bottom hit
              path := "down_right"
            }
            if ( ((X+1)=x)&((Y+1)=(y+10)) ) then { #diagonal hit
              path := "down_left"
            }
          }
          else {
            if ( path == "up_left" ) then {
              if ( ((X+1)=(x+20)) ) then { #side hit
                path := "up_right"
              }
              if ( ((Y+1)=(y+10)) ) then { #bottom hit
                path := "down_left"
              }
              if ( ((X+1)=(x+20))&((Y+1)=(y+10)) ) then { #diagonal hit
                path := "down_right"
              }
            }
            else {
              if ( path == "down_left" ) then {
                if ( ((X+1)=(x+20)) ) then { #side hit
                  path := "down_right"
                }
                if ( ((Y+1)=y) ) then { #top hit
                  path := "up_left"
                }
                if ( ((X+1)=(x+20))&((Y+1)=y) ) then { #diagonal hit
                  path := "up_right"
                }
              }
              else {
                if ( path == "down_right" ) then {
                  if ( ((X+1)=x) ) then { #side hit
                    path := "down_left"
                  }
                  if ( ((Y+1)=y) ) then { #top hit
                    path := "up_right"
                  }
                  if ( ((X+1)=x)&((Y+1)=y) ) then { #diagonal hit
                    path := "up_left"
                  }
                }
              }
            }
          }
          #check to see if field is clear for next level
          #reset sphere back to below block height
          temp := 1
          temp2 := 0
          while ( temp < 244 ) do {
            if ( (block_positions[temp] == "1") |
                 (block_positions[temp] == "2") |
                 (block_positions[temp] == "3") ) then {
              temp2 := 1
              temp := 243
            }
            temp := temp + 1
          }
          if ( temp2 = 0 ) then {
            level := level + 1
            create_blocks()
            DrawImage(X,Y,blank)
            DrawImage(140, 304, sphere)
            X := 140
            Y := 304
            path := "up_right"
          }
        }
      }
      z := z + 1
      x := x + 21
    }
    x := 10
    y := y + 11
  }
return
end


#this is an extra check to make sure the blocks stay completely filled
#when the sphere moves out of a block, the DrawImage(X, Y, blank)
#will draw a white sphere over the old sphere, this fixes blocks
#periodically by being called every block hit in the 4 move sphere procedures

procedure fix_blocks()
  local x, y, z

  z := 1
  y := 10
  x := 10
  while not ( y = 208 ) do {
    while not ( x = 283 ) do {
      if ( block_positions[z] == "1" ) then {
        Fg(blockclr1)
        FillRectangle(x,y,20,10)
      }
      if ( block_positions[z] == "2" ) then {
        Fg(blockclr2)
        FillRectangle(x,y,20,10)
      }
      if ( block_positions[z] == "3" ) then {
        Fg(blockclr3)
        FillRectangle(x,y,20,10)
      }
      if ( block_positions[z] == "X" ) then {
        Fg(invincclr)
        FillRectangle(x,y,20,10)
      }
      z := z + 1
      x := x + 21
    }
    x := 10
    y := y + 11
  }
return
end
