`Coding Challenge - ODE Ball Shooter
`Bill Robinson
`2-4-06
 
`Shoot boxes over the wall for 1000 points each
`If a box bounces back behind you and falls, you lose 3000 points
`Sometimes you have to shoot boxes moving toward you, to keep from losing 3000 points
`When the back wall gets higher, you may need to shoot the box in the air, to get it over the wall.
`
`Add your own level if you want - add data statements around line 100, change numlevels on line 28 if you add a level.
`
set display mode 1024,768,32
sync on : sync rate 0
hide mouse
set ambient light 10
color backdrop rgb(0,0,30)
randomize timer()
set camera range 1,20000
 
dim particles(30,5)
dim levelinfo(10)
dim blockinfo(10,30,2)
dim targetinfo(100,5)
 
#Constant WHITE=rgb(255,255,255)
#Constant YELLOW=rgb(255,255,0)
 
global numlevels=5
 
global level
global levelflag
global numballs
global targetsleft
global targetx#=1000.0
global targety#=300.0
global targetz#=-300.0
global hitnum
global gravity#
 
global screenxmax=1024
global screenymax=768
global screenwidth
global screenheight
global screenxcenter
global screenycenter
global screenzcenter
 
`balls
global ballx#
global bally#
global ballz#
global maxballspeed#
global ballflag
global ballsatonce
global maxballs=199
global ballcount
global nextballptr
global lastballtime
global newballtime
global balltiming=100
 
`walls
global leftwall=0
global rightwall=2000
global floor=5
global ceiling=1000
global backwall=2000
global backwallheight
global barrierheight
 
`Camera - position camera 500,200,-800
global camx=1000
global camy=200
global camz=-800
global mxold
 
`ODE - globals
global odeballptr
global numboxes
global targetsleft
 
`Particles
global intParticleDelay
global particlesptr
global particlesflag
 
leveldata:
`level 1 - #boxes, followed by the x,y coords for each box - also change the global 'numlevels' on line 20 if you add a level
data 12,150,0, 300,0, 450,0, 600,0, 750,0, 900,0, 1050,0, 1200,0, 1350,0, 1500,0, 1650,0, 1800,0
`level 2
data 16,400,0, 400,100, 400,200, 400,300, 800,0, 800,100, 800,200, 800,300, 1200,0, 1200,100, 1200,200, 1200,300, 1600,0, 1600,100, 1600,200, 1600,300
`level 3
data 15,700,0, 800,0, 900,0, 1000,0, 1100,0, 1200,0, 750,100, 1150,100, 800,200, 1100,200, 850,300, 1050,300, 900,400, 1000,400, 950,500
`level 4
data 20,900,0,900,100,900,200,900,300,900,400,900,500,900,600,900,700,900,800,900,900
data 1000,0,1000,100,1000,200,1000,300,1000,400,1000,500,1000,600,1000,700,1000,800,1000,900
`level 5
data 27,100,0, 150,100, 200,200, 250,300, 300,400, 350,500, 400,600, 450,700, 500,800, 550,900
data   1000,0, 950,100, 900,200, 850,300, 800,400, 750,500, 700,600, 650,700, 600,800
data  350,300, 400,200, 450,100, 500,0
data  750,300, 700,200, 650,100, 600,0
 
restore leveldata
for i=1 to numlevels
   read levelinfo(i)
   for j=1 to levelinfo(i)
      read blockinfo(i,j,1)
      read blockinfo(i,j,2)
   next j
next i
 
screenwidth=screen width()
screenheight=screen height()
screenxcenter=screenwidth/2
screenycenter=screenheight/2
screenzcenter=0
 
rem --- make left wall
make object box 310,3000,1100,2
yrotate object 310,90
color object 310,rgb(200,200,0)
position object 310,0,500,1000
set object emissive 310,rgb(100,100,0)
 
rem --- make right wall
make object box 311,3000,1100,2
yrotate object 311,-90
color object 311,rgb(255,255,0)
position object 311,2000,500,1000
 
rem --- make floor
make object box 303,2100,2500,3
xrotate object 303,-91                 :`floor is tilted so ball run off floor to be re-used
color object 303,rgb(0,200,0)
position object 303,1000,-7,700       :`was -2 for y
 
rem --- make back wall
make object box 304,2000,1000,150
color object 304,rgb(0,0,255)
backwallcenter=-500
 
rem --- make ceiling
make object box 305,2000,3000,2
xrotate object 305,90
color object 305,rgb(0,255,255)
position object 305,1000,1000,1000
set object emissive 305,rgb(0,70,70)
 
rem --- Create ODE tennis balls ---
for i=1000 to 999+maxballs
   make object sphere i,30
   set object i,1,1,0
   color object i,rgb(255,0,0)
   set object collision off i
   position object i, -2000,-2000,-2000      :`move object WAY off screen
next i
 
rem Create Bazooka Tube
make object cylinder 300,40         :`Tennis ball CANNON
set object 300,1,1,0
scale object 300,42,250,42
color object 300,rgb(0,10,0)
xrotate object 300,90
fix object pivot 300
`texture object 300,406
 
rem Create Pivot Cone for Bazooka
make object cone 301,5              :`pivot for cannon
`hide limb 301,0
glue object to limb 300,301,0
offset limb 300,0,0,25,0
 
rem make target ball
make object sphere 302,10            :`Target ball
set object 302,1,1,0
color object 302,rgb(255,255,255)
`texture object 302,404
 
rem use my own collision for room walls and floor
set object collision off 300     :`Bazooka
set object collision off 301     :`Pivot cone for cannon
set object collision off 302     :`target ball for aiming cannon
 
rem position objects for room
hide object 301                              :`hide cannon pivot
`position object 302,-1000,-1000,-1000        :`TARGET ball - move off screen for now
position object 301,camx,camy,camz+100
 
`-------------------------------------------------------
xpos=500
ypos=300
zpos=500
` Create the ODE World
ode start
ode set world gravity 0,-.3,0         :`-.3,0       :`was 0,-20,0
ode set world step  0.3             :`was 0.05
ode set world erp (0.2)*2.5         :`error correction each step
ode set world cfm (10^-5)*2.5       :`Constriant Force Mixing -
 
` Create a static floor, walls, ceiling.
` Implement in Dark Basic Professional first (above)
` and then ask ODE to do the same...
ode create static box 303     :`floor
ode create static box 305     :`ceiling
ode create static box 310     :`left wall
ode create static box 311     :`right wall
`ode set contact bounce 304,1
 
` Create dynamic sphere
` Implement in Dark Basic Professional first
` and then ask ODE to do the same...
 
make object sphere 400,120,16,16                :`Make sphere in DBPro first
position object 400, xpos-50,ypos+200,zpos      :`Position object in DBPro
 
ode create dynamic sphere 400                   :`Create same object in ODE, will have same position in ODE
ode set body mass 400,10                        :`Set object MASS
ode set contact fdir1 400,30.0                 :`Add FRICTION
`--------------------------------------------------------
 
`ODE floor is higher so balls seem to roll on the floor, and not 'IN' the floor
`Visible DBPro floor is lower, repositon it now, since ODE copy is made
position object 303,1000,-5,700     :`was 1000,-2,1000
 
nextballptr=100         :`pointer into array - points to next ball to fire
odeballptr=1000
ballcount=0             :`how many balls in use, on the screen
ballsatonce=100         :`how many balls on screen at the same time
numballs=5000           :`number of balls to shoot
maxballspeed#=8         :`how fast should the balls shoot
balltiming=100          :`pause between each ball being fired
gravityon=1             :`gravity is on
levelflag=0
level=1                 :`start at this level
 
gravity# = 0.1
position camera camx,camy,camz
point camera targetx#,targety#,targetz#
position mouse screenxcenter,screenycenter
 
mxold#=mousex()
numboxes=levelinfo(level)
_textures()
texture object 400,3
 
do
   if levelflag=0
      _build_level(level)
      _InitParticles()
   endif
 
   position camera camx,camy,camz
   position object 301,camx,camy-40,camz-30
   mx#=2000.0/screenwidth*mousex()                                     :`get mouse x location
   my#=1000.0/screenheight*mousey()                                    :`get mouse y location
 
   if mx#<>mxold#
      targetx#=mx#
      targety#=screenheight-my#
      mxold=mx
      if targetx#<0 then targetx#=0
      if targetx#>2000.0 then targetx#=2000.0             :`limit target position to 1000, instead of 1024 which is past wall
   endif
 
   point object 301,targetx#,targety#,targetz#     :`pivot cone points to target
   position object 302,targetx#,targety#,targetz#  :`target ball moves as the target object
   point camera targetx#,targety#,targetz#
 
   keypress=scancode()                             :`scan for keys pressed, subroutine to hadle keys
   if keypress>0 then _key_action(keypress)
 
   mouse=mouseclick()                              :`get mouse click for firing tennis ball
   if mouse=1 then gosub _fire_tennis_ball         :`FIRE!
 
   gosub _show_score                               :`show score on screen
 
   intParticleDelay=_check_event_triggers(1,numboxes,1)
 
   `is it time to turn particles emission to zero for an object
   for i=1 to numboxes
      if particles(i,5)>0
         t=timer()
         delay=particles(i,5)
         delay2=delay-500
         if t>delay
            set particle emissions i,0
            particles(i,5)=0
         endif
      endif
   next i
 
`check for Boxes leaving the room
for i=1 to numboxes
      if object exist(i)
         delflag=0
         `Box shot off far end of screen - GOOD!
         if object position y(i)<0 and object position z(i)>backwall
            inc score,1000
            delflag=1
         else
            ` Box has fallen off behind player - deduct 5000 from score - BAD!
            if object position z(i)<camz and object position y(i)<-400
               dec score,3000
               delflag=1
            endif
         endif
         if delflag=1
            ode destroy object i
            delete object i
            dec targetsleft
            targetinfo(i,1)=0
         endif
      endif
next i
 
   if targetsleft=0
      for pf=1 to numboxes
         if particles(pf,5)<>0 then particlesflag=1
      next pf
      if particlesflag=0
         _finished_level(level)
         inc level
         levelflag=0
      endif
   endif
 
   ode update                 :`let ODE update physics
   sync
loop
 
ode end
end
 
 
rem --- show score on screen
_show_score:
ink rgb(255,255,255),0
set cursor 1,1:print screen fps()
center text screenxcenter,2,"Level:"+str$(level)+"     Targets Left: "+str$(targetsleft)+"     Score: "+str$(score)
if level=20 then center text screenxcenter,28,"Targets Left="+str$(level20targets)
return
 
 
rem ---------- fires tennis balls ----------
_fire_tennis_ball:
if ballcount=ballsatonce then return
newballtime=timer()
if newballtime-lastballtime<balltiming then return else lastballtime=newballtime    :`control firing rate of tennis balls
if numballs=0                          :`out of tennis balls to shoot
   retryflag=1                         :`set retry flag, do you want to try this level again?
   return                              :`sound 3 = your gun is empty
endif
 
ballx#=object position x(301)          :`get bazooka pivot location to start ball at
bally#=object position y(301)
ballz#=object position z(301)
 
ballxi#=(targetx#-ballx#)/300.0*maxballspeed#  :`calculate some value to move ball each time toward target
ballyi#=(targety#-bally#)/300.0*maxballspeed#  :`mostly an arbitrary calculation, to get lots of slices
ballzi#=(targetz#-ballz#)/300.0*maxballspeed#  :`build screen routine then says how many slices each move
 
`----------- ODE STUFF -------------------------------------------------------------------
position object odeballptr,1000,100,-800        :`velocity 100 - was 1000,150,-800
 
`position object odeballptr,ballx#,bally#,ballz#   :`start ball in bazooka
point object odeballptr,targetx#,targety#,-50
ode destroy object odeballptr
ode create dynamic sphere odeballptr
ode set body mass odeballptr,20                 :`was 20
ode set contact bounce odeballptr,1
ode set linear velocity odeballptr,ballxi#*75.0,ballyi#*75.0,1000.0       :`targetz=0,use*48, -300 use 75
`velocity of 1000 - for targetz locataion use - targetz=0,use*48 for xi# and yi#, -300 use 75
`velocity of 2000 - use *75.0 for aiming - fudged numbers, shound fix this later
 
`inc ballcount                                      :`one more ball on screen
dec numballs                                       :`one less ball you can fired
 
inc odeballptr
if odeballptr>999+maxballs
   odeballptr=1000
endif
`-----------------------------------------------------------------------------------------
return
 
 
function _finished_level(level)
set cursor 1,200
print "YOU FINISHED LEVEL - ";level
print "Press a key to continue"
sync
wait key
for i=1 to levelinfo(level)
   if object exist(i)
      ode destroy object i
      delete object i
      delete particles i
   endif
   targetinfo(i,1)=0
next i
if level=numlevels
   print "  "
   print "That's all there is for now - Thanks for Playing!"
   print "Press a key to exit game - Bye!"
   sync
   wait key
   end
endif
endfunction
 
 
function _check_event_triggers(objmin,objmax,trigtype)
for i=objmin to objmax
if  object exist(i)
   objx=object position x(i)          :`get bazooka pivot location to start ball at
   objy=object position y(i)
   objz=object position z(i)
   select trigtype
      case 1
         if objz>backwall and objy<backwallheight    :`and targetinfo(i,1)=1      :`level*200
            intParticleDelay=_sparks(i)
`            dec targetsleft
`            targetinfo(i,1)=0
         endif
      endcase
   endselect
endif
next i
endfunction intParticleDelay
 
 
function _sparks(objnum)
sparkx=object position x(objnum)          :`get bazooka pivot location to start ball at
sparky=object position y(objnum)
sparkz=object position z(objnum)
position particles objnum,sparkx,barrierheight,sparkz
show particles objnum
color particles objnum, rnd(255),rnd(255),rnd(255)
particles(objnum,5)=Timer()+500
endfunction intParticleDelay
 
 
function _InitParticles()
for i=1 to numboxes
   if particles exist(i)=0
      make particles i,0,250,30
      color particles i, 255, 200, 200
      set particle emissions i,30         :`was 30
      set particle speed i, 0.2      :`0.20
      set particle gravity i,.1            :`was 10
      set particle life i,500
      hide particles i
   endif
next i
endfunction
 
 
function _key_action(keypress)
select keypress
   case 20
      if level>=8
         balltiming=balltiming-10
         wait 100
         if balltiming<40 then balltiming=100
      endif
   endcase
endselect
endfunction
 
 
function _textures()
for i=1 to 3
   create bitmap i,50,50
   if i=1 then ink rgb(255,0,0),0
   if i=2 then ink rgb(255,255,0),0
   if i=3 then ink rgb(255,0,255),0
   for x=1 to 1000
      ang=rnd(360)
      rad=rnd(20)
      dot 25+sin(ang)*rad,25+cos(ang)*rad
   next x
   blur bitmap i,1
   get image i,0,0,50,50
   delete bitmap i
next i
endfunction
 
 
function _build_level(level)
   numboxes=levelinfo(level)
   targetsleft=numboxes
   for i=1 to numboxes
      make object box i,100,100,80
      position object i,blockinfo(level,i,1),blockinfo(level,i,2)+50,900      :`+60 for y, because the floor is tilted from back-to-front
      color object i,rgb(rnd(255),rnd(255),rnd(255))
      ode create dynamic box i
      ode set body mass i,20
      ode set contact fdir1 i,25.0
      targetinfo(i,1)=1                               :`target is active
   next i
   if level<6
      barrierheight=level*100
   else
      barrierheight=6*100
   endif
   position object 304,1000,-500+barrierheight,2000   :`position backwall
   ode create static box 304                          :`tell ODE about backwall
   levelflag=1
endfunction