set display mode 1024,768,32
hide mouse
backdrop on
color backdrop 0
sync on
 
rem Create waypoints for movement of escalator
dim ramp1_y#(20)
dim ramp1_z#(20)
dim ramp1_a#(20)
dim Oramp(4)
dim Oleg(4)
dim Opad(10)
dim pad_pos(10)
rem Define Object numbers
temp=1
Oramp(1)=2
Oramp(2)=3
Oramp(3)=4
Oramp(4)=5
Oball=6
Oleg(1)=7
Oleg(2)=8
Oleg(3)=9
Oleg(4)=10
Obase=11
rem Define segments of escalator
obj=12
for x=1 to 10
  Opad(x)=obj
  inc obj
next x
Oramp1=22
Opost=23
global Oswing=24
Obox=25
 
rem Define Mesh numbers
Mboard=2
Mrampend=3
Mrampcyl=4
Mboxseg=5
Mball=6
Mswingarm=7
 
rem Define Image numbers
Iboard=2
Iball=3
Ibase=4
Instructions=5
 
rem Define textures
Tramp1_x#=0.0
Tramp1_y#=0.0
 
gosub make_objects        rem Create and build all the objects
gosub get_elevator        rem Get the waypoints for the escalator movement
 
position camera 0,30,-100
point camera 0,30,0
 
rem Set up the marble movements parameters
ball_x_start#=0.03        rem Starting speed for rolling marble
ball_x#=ball_x_start#
xaccel#=0.0002            rem Rate of acceleration for the marble on the ramps
xmax#=0.4                 rem Maximum speed of the marble on the ramps
ball_y#=0.0               rem Marble speed for other axes
ball_z#=0.0
ball_z_start#=2.75        rem Marble position on the ramps
gravity#=0.008            rem Acceleration due to gravity
dir#=1.0                  rem Direction of roll of the marble
mag#=0.6                  rem Magnitude of force when collision with escalator
pmag#=0.8                 rem Magnitude of force when collision with escalator paddles
rem Set up the swing arm movements
sdir#=1.0                 rem Direction of swing
smax#=0.3                 rem Maximum speed of the swing
sv#=0.0                   rem Swing velocity
sa#=0.01                  rem Acceleration of the swing velocity
sangle#=10.0              rem Starting angle of the swing arm
rangle#=-95.0             rem Limit of the swing arm motion
swingfull=0               rem Swing arm box has the marble 0=no, 1=yes
rem Define activity identifiers for collision detection and physics
ramps=1
escalator=2
swingarm=3
outofbox=4
activity=ramps            rem Start the marble on the ramps
 
gosub position_objects    rem Place all objects in the scene
gosub instructions
 
while not escapekey()
  rem Move the camera around the object using the arrow keys
  move_camera()
  rem Animate the escalator
  gosub move_ramp
  rem Animate the swing arm
  gosub move_swing
 
  rem Get the current position of the marble
  Oball_x#=object position x(Oball)
  Oball_y#=object position y(Oball)
  Oball_z#=object position z(Oball)
 
  rem Calculate which ramp the marble is on based on the marble's height
  effective_ramp=int(Oball_y#/20)+1
  if effective_ramp>4 then effective_ramp=4
  if effective_ramp<1 then effective_ramp=1
 
  rem Move the marble horizontally
  Oball_x#=Oball_x#+ball_x#*dir#
  ball_x#=ball_x#+xaccel#
  if ball_x#>xmax# then ball_x#=xmax# rem Limit the top speed of the marble
  rem Apply gravity to the marble
  ball_y#=ball_y#+gravity#
  position object Oball,Oball_x#,Oball_y#-ball_y#,Oball_z#-ball_z#
 
  if object position z(Oball)=ball_z_start# and effective_ramp>1
    if activity=swingarm then activity=ramps
    rem Check for collision of the marble with the ramp
    if object collision(Oramp(effective_ramp),Oball)
      if effective_ramp && 1 = 1 rem Check which direction to marble should be rolling on the ramp
        if dir#=1.0
          ball_x#=ball_x_start# rem Set starting speed of the marble if it landed on a new ramp
        endif
        dir#=-1.0
      else
        if dir#=-1.0
          ball_x#=ball_x_start# rem Set starting speed of the marble if it landed on a new ramp
        endif
        dir#=1.0
      endif
      position object Oball,object position x(Oball),object position y(Oball)+ball_y#,object position z(Oball)
      ball_y#=ball_y#*-0.5 rem Apply bounce friction to the marble
    endif
    rem Roll the marble in the direction of motion
    zrotate object Oball,object angle z(Oball)-ball_x#*12.0*dir#
  else
    if object position z(Oball)<=76 and activity<>swingarm rem Marble is on escalator
      if activity=ramps then activity=escalator
      if activity=escalator
        if object position x(Oball)>=ramp1_x#
          dir#=0.0  rem Stop the marble in the center of the escalator
        else
          dir#=1.0  rem Keep the marble rolling until it reaches the center of the escalator
        endif
        rem Check for collision of the marble with the escalator paddles
        found=0
        for i=1 to 10
          if object collision(Opad(i),Oball) then found=i
        next i
        if found>0
          rem Calculate the direction of force on the marble and apply to the marble
          ydir#=cos(object angle x(Opad(found))+90)*pmag#
          zdir#=sin(object angle x(Opad(found))+90)*pmag#
          position object Oball,object position x(Oball),object position y(Oball)+ydir#,object position z(Oball)+zdir#
        endif
        rem Check for collision of the marble with the escalator
        if object collision(Oramp1,Oball)
          if object position z(Oball)<64
            temp#=ball_y#*mag#
            ball_y#=ball_z#*mag#
            ball_z#=temp#
          else
            ball_y#=-0.05
            ball_z#=0.1
          endif
          rem Apply collision force to the marble
          position object Oball,object position x(Oball),object position y(Oball)-ball_y#,object position z(Oball)-ball_z#
        endif
      endif
    else
      if activity=escalator then activity=swingarm
      if activity=swingarm
        ball_z#=0.0             rem Make ball fall straight down
        if swingfull=0          rem Ball has not made contact with box yet
          if object collision(Obox,Oball)  rem If ball has landed in the box
            position object Oball,limb position x(Oswing,Lhorzarm),object position y(Oball)-ball_y#,limb position z(Oswing,Lhorzarm)
            swingfull=1         rem Swing the arm with the ball in the box
          endif
        else
          rem Keep the ball in the box using the hidden limb on the swing arm
          position object Oball,limb position x(Oswing,Lballholder),limb position y(Oswing,Lballholder),limb position z(Oswing,Lballholder)
        endif
      endif
    endif
  endif
  if activity=outofbox  rem Ball is falling out of the box onto the ramp
    position object Oball,object position x(Oball),object position y(Oball)-ball_y#,object position z(Oball)-0.1
    if object position z(Oball)<ball_z_start#   rem When ball reaches the center of the ramp, keep it in the center
      position object Oball,object position x(Oball),object position y(Oball)-ball_y#,ball_z_start#
      activity=ramps
    endif
  endif
  sync
endwhile
end
 
move_swing:
  if activity=swingarm and swingfull=1  rem If ball is in the box, swing the arm
    sv#=sv#-sa#           rem Accelerate the swing arm for realistic physics
    if abs(sv#)>smax# then sv#=sv#+sa#  rem Limit the speed of the swing arm
    rem Swing the arm
    rotate object Oswing,object angle y(Oswing)/2.2,object angle y(Oswing)+sv#,object angle z(Oswing)
    rem Pivot the joint holding the box
    rotate limb Oswing,Lvertjoint,0,object angle y(Oswing)*-1.0+10.0,0
    rem Rotate the box to stay in position with the end of the swing arm
    yrotate object Obox,limb angle y(Oswing,Lvertjoint)
    rem Swing arm reaches limit to dump marble onto ramp
    if object angle y(Oswing)<= rangle#
      swingfull=0
      sv#=0.0
      activity=outofbox
      ball_y#=0.0
      ball_x#=ball_X_start#
    endif
  else    rem Swing arm back into starting position
    sv#=sv#+sa#   rem Accelerate the swing arm for realistic physics
    if abs(sv#)>smax# then sv#=sv#-sa#  rem Limit the speed of the swing arm
    rem Swing the arm
    rotate object Oswing,object angle y(Oswing)/2.2,object angle y(Oswing)+sv#,object angle z(Oswing)
    rem Pivot the joint holding the box
    rotate limb Oswing,Lvertjoint,0,object angle y(Oswing)*-1.0+10.0,0
    rem Rotate the box to stay in position with the end of the swing arm
    yrotate object Obox,limb angle y(Oswing,Lvertjoint)
    rem Swing arm reaches starting position
    if object angle y(Oswing)>= sangle#
      yrotate object Oswing,object angle y(Oswing)-sv#
      sv#=sv#*-0.5      rem Bounce the arm for realistic physics
    endif
  endif
return
 
move_ramp:
  for i=1 to 10
    rem Get position of the escalator segment
    y#=object position y(Opad(i))
    z#=object position z(Opad(i))
    rem Check its distance to the next waypoint
    dist#=sqrt((ramp1_y#(pad_pos(i))-y#)^2+(ramp1_z#(pad_pos(i))-z#)^2)
    rem If it is close enough to the next waypoint
    if dist#<0.5
      rem Place the segment at the waypoint
      position object Opad(i),ramp1_x#,ramp1_y#(pad_pos(i)),ramp1_z#(pad_pos(i))
      rem Correctly orient the segment rotation to the waypoint
      xrotate object Opad(i),ramp1_a#(pad_pos(i))
      rem Point at the next waypoint
      dec pad_pos(i)
      if pad_pos(i)<1 then pad_pos(i)=20
    else
      rem Calculate the step of motion to move the segment towards the next waypoint
      ydir#=(ramp1_y#(pad_pos(i))-y#)/dist#*0.2
      zdir#=(ramp1_z#(pad_pos(i))-z#)/dist#*0.2
      position object Opad(i),ramp1_x#,y#+ydir#,z#+zdir#
      rem Slowly adjust the segment rotation to move towards the waypoint orientation
      if object angle x(Opad(i))<>ramp1_a#(pad_pos(i))
        angle#=ramp1_a#(pad_pos(i))-7*dist#
        xrotate object Opad(i),angle#
      endif
    endif
  next i
return
 
make_objects:
rem Make a board mesh for use with the ramp object
make object box temp,60,2,5
make mesh from object Mboard,temp
delete object temp
 
rem Make a wood texture for use with the objects
create bitmap temp,32,32
ink rgb(120,38,0),0
box 0,0,31,31
ink rgb(180,60,0),0
for x=1 to 50
  dot rnd(31),rnd(31)
next x
get image Iboard,0,0,31,31
delete bitmap temp
 
rem Make ramps
make object Oramp(1),Mboard,Iboard
add limb Oramp(1),1,Mboard
offset limb Oramp(1),1,0,2,5
rotate limb Oramp(1),1,-45,0,0
texture limb Oramp(1),1,Iboard
xrotate object Oramp(1),22.5
fix object pivot Oramp(1)
set object collision to polygons Oramp(1)
instance object Oramp(2),Oramp(1)
instance object Oramp(3),Oramp(1)
instance object Oramp(4),Oramp(1)
set object collision to polygons Oramp(2)
set object collision to polygons Oramp(3)
set object collision to polygons Oramp(4)
 
rem Make marble
create bitmap temp,256,256
ink rgb(192,192,192),0
box 0,0,255,255
ink rgb(64,64,64),0
for x=1 to 500
  dot rnd(255),rnd(255)
next x
get image Iball,0,0,255,255
delete bitmap temp
set current bitmap 0
make object sphere Oball,9,20,20
texture object Oball,Iball
set object Oball, 1,1,1,2,1,0,1
yrotate object Oball, 180
set object collision to spheres Oball
 
rem Make legs
make object cylinder Oleg(1),60
texture object Oleg(1),Iboard
instance object Oleg(2),Oleg(1)
instance object Oleg(3),Oleg(1)
instance object Oleg(4),Oleg(1)
scale object Oleg(1),3,100,3
scale object Oleg(2),3,100,3
scale object Oleg(3),3,120,3
scale object Oleg(4),3,120,3
 
rem Make base
size=1023
create bitmap 1,size+1,size+1
ink rgb(64,64,64),0
box 0,0,size,size
ink rgb(128,128,128),0
for x=0 to size step 3
  line x,0,rnd(size),size
  line 0,x,size,rnd(size)
next x
get image Ibase,0,0,size,size
delete bitmap 1
make object box Obase,70,5,50
texture object Obase,Ibase
 
rem Make escalator
make object box Opad(1),10,10,2
texture object Opad(1),Iboard
offset limb Opad(1),0,0,5,0
set object collision to polygons Opad(1)
rem Make all escalator segments
for x=2 to 10
  instance object Opad(x),Opad(1)
  offset limb Opad(x),0,0,5,0
  set object collision to polygons Opad(x)
next x
rem Make escalator centre (base)
make object cylinder temp,10
scale object temp,140,100,140
make mesh from object Mrampend,temp
delete object temp
make object sphere temp,14
scale object temp,1,100,100
make mesh from object Mrampcyl,temp
delete object temp
make object box Oramp1,10,14,90
add limb Oramp1,1,Mrampend
offset limb Oramp1,1,0,0,45
rotate limb Oramp1,1,0,0,90
add limb Oramp1,2,Mrampend
offset limb Oramp1,2,0,0,-45
rotate limb Oramp1,2,0,0,90
add limb Oramp1,3,Mrampcyl
offset limb Oramp1,3,5,0,-45
add limb Oramp1,4,Mrampcyl
offset limb Oramp1,4,-5,0,-45
add limb Oramp1,5,Mrampcyl
offset limb Oramp1,5,5,0,45
add limb Oramp1,6,Mrampcyl
offset limb Oramp1,6,-5,0,45
texture object Oramp1,Iboard
xrotate object Oramp1,135
set object collision to polygons Oramp1
 
rem Make swing post
make object cylinder Opost,30
scale object Opost,8,100,8
texture object Opost,Iboard
 
rem Make swing
rem Define limbs
Lmainjoint=1
Lvertjoint=2
Lvertarm=3
Lboxjoint=4
Lhorzarm=5
Lballholder=6
make object cylinder temp,60
scale object temp,5,100,5
make mesh from object Mswingarm,temp  rem create a mesh from the scaled object so that the scaling
delete object temp                    rem       does not apply to limbs of the object
make object Oswing,Mswingarm,0        rem remake the object from the mesh of the scaled object
offset limb Oswing,0,0,30,0           rem Shift the main object so that it rotates from its end, not its middle
texture object Oswing,Iboard
make object sphere temp,10,20,20      rem Create object for use with making ball joints
make mesh from object Mball,temp      rem Create mesh to use with add limb command
delete object temp
add limb Oswing,Lmainjoint,Mball        rem Add the main joint for the swing arm
texture limb Oswing,Lmainjoint,Iball
add limb Oswing,Lvertjoint,Mball        rem Add the joint for the vertical arm
scale limb Oswing,Lvertjoint,50,50,50   rem Shrink the vertical joint by half
offset limb Oswing,Lvertjoint,0,60,0    rem Position the vertical joint at the end of the main arm
texture limb Oswing,Lvertjoint,Iball
add limb Oswing,Lvertarm,Mswingarm      rem Add the vertical arm
scale limb Oswing,Lvertarm,100,50,100   rem Shorten the vertical arm to the correct length
rotate limb Oswing,Lvertarm,0,0,90      rem Orient the vertical arm to vertical
offset limb Oswing,Lvertarm,15,0,0     rem Position the vertical arm at the vertical joint
texture limb Oswing,Lvertarm,Iboard
link limb Oswing,Lvertjoint,Lvertarm
add limb Oswing,Lboxjoint,Mball         rem Add the joint for the horizontal arm
scale limb Oswing,Lboxjoint,100,200,100    rem Shrink the horizontal joint by half
offset limb Oswing,Lboxjoint,0,-25,0    rem Position the horizontal joint at the end of the horizontal arm
texture limb Oswing,Lboxjoint,Iball
link limb Oswing,Lvertarm,Lboxjoint
add limb Oswing,Lhorzarm,Mswingarm      rem Add the horizontal arm
scale limb Oswing,Lhorzarm,100,10,100   rem Shorten the horizontal arm to the correct length
rotate limb Oswing,Lhorzarm,90,0,0      rem Orient the horizontal arm
offset limb Oswing,Lhorzarm,0,0,-5      rem Position the horizontal arm at the horizontal joint
texture limb Oswing,Lhorzarm,Iboard
link limb Oswing,Lboxjoint,Lhorzarm
add limb Oswing,Lballholder,Mball       rem Add an invisible limb to hold the marble
offset limb Oswing,Lballholder,0,-150,0   rem Position the invisible limb
link limb Oswing,Lhorzarm,Lballholder
hide limb Oswing,Lballholder
rem Make swingarm catcher box
make object box temp,12,1,12            rem Make a side of the box
make mesh from object temp,temp         rem Create a mesh to be used for all sides of the box
delete object temp
make object Obox,temp,0                 rem Start building the box with one side
add limb Obox,1,temp                    rem Add one side to the box
add limb Obox,2,temp                    rem Add one side to the box
add limb Obox,3,temp                    rem Add one side to the box
add limb Obox,4,temp                    rem Add one side to the box
offset limb Obox,0,0,-3,0             rem Position a side of the box
offset limb Obox,1,5.5,3,0            rem Position a side of the box
offset limb Obox,2,0,3,5.5            rem Position a side of the box
offset limb Obox,3,-5.5,3,0           rem Position a side of the box
offset limb Obox,4,0,3,-5.5           rem Position a side of the box
rotate limb Obox,1,0,0,90               rem Orient the side of the box
rotate limb Obox,2,90,0,0               rem Orient the side of the box
rotate limb Obox,3,0,0,90               rem Orient the side of the box
rotate limb Obox,4,90,0,0               rem Orient the side of the box
make mesh from object temp,Obox         rem Create a mesh so that object can be created with no limbs
delete object Obox
make object Obox,temp,Iboard            rem Create box from mesh so it has no limbs
offset limb Obox,0,0,5,0                rem Change its axis of rotation position
xrotate object Obox,270                 rem Point to open end of the box up
glue object to limb Obox,Oswing,Lhorzarm,1    rem Attach box to the swing arm
rotate object Oswing,90,0,0             rem Orient the entire swing arm obect to sit on the swing pole
fix object pivot Oswing                 rem Define the current orientation of the swing arm as absolute
set object collision to polygons Obox
return
 
get_elevator:
rem Get the waypoints for the escalator animation
ramp1_x#=35.0
for i=1 to 20
  read ramp1_y#(i),ramp1_z#(i),ramp1_a#(i)
  ramp1_y#(i)=ramp1_y#(i)-20.0
next i
return
 
position_objects:
hide object Oramp(1)    rem Original design had 4 ramps, bottom ramp was removed so marble would fall onto escalator
position object Oramp(1),10,0,0
position object Oramp(2),-10,20,0
position object Oramp(3),10,40,0
position object Oramp(4),-10,60,0
zrotate object Oramp(1),5
zrotate object Oramp(2),-5
zrotate object Oramp(3),5
zrotate object Oramp(4),-5
position object Oleg(1),15,28,-3
position object Oleg(2),15,28,8.5
position object Oleg(3),-41,32,-2
position object Oleg(4),-41,32,7.5
position object Obase,-15,-5,0
position object Oball, -35,69,ball_z_start#
rem Place escalator paddles along top and bottom of the escalator
for x=1 to 5
  position object Opad(x),ramp1_x#,ramp1_y#(1)+(x-1)*15,ramp1_z#(1)+(x-1)*15
  xrotate object Opad(x),ramp1_a#(1)
  pad_pos(x)=1
next x
for x=6 to 10
  position object Opad(x),ramp1_x#,ramp1_y#(11)-(x-6)*15,ramp1_z#(11)-(x-6)*15
  xrotate object Opad(x),ramp1_a#(11)
  pad_pos(x)=11
next x
position object Oramp1,ramp1_x#,25,35
position object Opost,10,10,20
position object Oswing,10,25,20
return
 
instructions:
rem Draw cursor key buttons and label with instructions for camera movement
x=0:y=40
create bitmap temp,256,256
ink 0,0
box 0,0,255,255
ink rgb(128,255,128),0
box x,y+1,x+12,y+15
box x+244,y+1,x+255,y+15
box x,y+23,x+12,y+37
box x+244,y+23,x+255,y+37
box x,y+43,x+41,y+57
box x+43,y+43,x+55,y+57
box x+200,y+43,x+241,y+57
box x+243,y+43,x+255,y+57
set cursor x+15,y+0
print "move left"
set cursor x+155,y+0
print "move right"
set cursor x+15,y+22
print "zoom in"
set cursor x+155,y+22
print "zoom out"
set cursor x+70,y+43
print "up"
set cursor x+153,y+43
print "down"
set cursor 50,0
print "CAMERA CONTROLS"
ink 0,0
line x+1,y+8,x+6,y+3:line x+2,y+8,x+7,y+3
line x+1,y+8,x+6,y+13:line x+2,y+8,x+7,y+13
line x+1,y+8,x+11,y+8
line x+254,y+8,x+249,y+3:line x+253,y+8,x+248,y+3
line x+254,y+8,x+249,y+13:line x+253,y+8,x+248,y+13
line x+254,y+8,x+244,y+8
line x+6,y+25,x+3,y+28:line x+6,y+26,x+3,y+29
line x+6,y+25,x+10,y+29:line x+6,y+26,x+10,y+30
line x+6,y+25,x+6,y+35
line x+250,y+35,x+247,y+32:line x+250,y+34,x+247,y+31
line x+250,y+35,x+254,y+31:line x+250,y+34,x+254,y+30
line x+250,y+25,x+250,y+35
line x+50,y+45,x+47,y+48:line x+50,y+46,x+47,y+49
line x+50,y+45,x+54,y+49:line x+50,y+46,x+54,y+50
line x+50,y+45,x+50,y+55
set cursor x,y+43
print "SHIFT"
line x+250,y+55,x+247,y+52:line x+250,y+54,x+247,y+51
line x+250,y+55,x+254,y+51:line x+250,y+54,x+254,y+50
line x+250,y+45,x+250,y+55
set cursor x+200,y+43
print "SHIFT"
 
rem Convert instructions bitmap into a sprite for display on the screen
get image Instructions,0,0,256,256
delete bitmap temp
sprite 1,0,0,Instructions
return
 
rem Waypoints for escalator movement
data 10,10,135,70,70,135,72,71,115,75,72,95,77.5,71.5,75,79,70.5,55,80.5,69,35,81.7,67,15,82,65,355,81.5,62.5,335,80,60,315,20,0,315,17.9,-1.4,295,15,-2,275,12.5,-1.5,255,10.8,-0.6,235,9.4,0.8,215,8.3,2.9,195,8,5,175,8.5,7.5,155
 
function move_camera()
  if leftkey()
    turn camera left 90.0
    move camera 1.0
    point camera 0,30,0
  endif
  if rightkey()
    turn camera right 90.0
    move camera 1.0
    point camera 0,30,0
  endif
  if upkey()
    if not shiftkey()
      move camera 0.5
    else
      position camera camera position x(),camera position y()+1.0,camera position z()
      point camera 0,30,0
    endif
  endif
  if downkey()
    if not shiftkey()
      move camera -0.5
    else
      position camera camera position x(),camera position y()-1.0,camera position z()
      point camera 0,30,0
    endif
  endif
endfunction