Hi,
Here's my entry for the March 20-lines challenge:
Do you want to win the lottery and get rich, but don't know which numbers to choose? Don't cry no more, my gamer friend.
Play my little game, retrieve your familiar feelings (defend your base alone against an overwhelming threat, blah, blah, blah...), and at the end of the game, the program will gave you a set of numbers. Not an ordinary set of random numbers, though:
your lucky numbers, determined by the way you played the game. Cool, uh ?
20 code-packed lines, forced-timing, no media, heaps of comments, and a few neat functions to create asteroids, to make a "legacy digital clock" display, an easy life bar, etc..
20-lines version:
autocam off : sync on : randomize timer() : color backdrop 0 : set text opaque : hide mouse : nbastmax=16 : dim ast(nbastmax) as integer : dim astNumobj(nbastmax) as integer : dim astNumcross(nbastmax) as integer : dim astPosX#(nbastmax) as float : dim astPosY#(nbastmax) as float : dim astPosZ#(nbastmax) as float : dim astMovX#(nbastmax) as float : dim astMovY#(nbastmax) as float : dim astMovZ#(nbastmax) as float : dim spark#(nbastmax) as float : i=50 : sync rate 10 : repeat : text 10,70,"What is the size of the grid ? (UP/DOWN/RETURN) => " + str$(i) : if upkey() and i<200 : inc i : endif : if downkey() and i>5
dec i : endif : if returnkey() : max=i : endif : sync : until max>0 and returnkey()=0 : i=5 : repeat : text 10,90,"How many numbers do you have to choose ? (UP/DOWN/RETURN) => " + str$(i) + " " : if upkey() and i<200 : inc i : endif : if downkey() and i>1 : dec i : endif : if returnkey() : nc=i : endif : sync : until nc>0 : sync rate 0 : for i=1 to sqrt(max)+1 : for j=i to i+2 : if i*j>=max and abs(max-(i*j))<abs(max-(nx#*nz#))
nz#=i : nx#=j : endif : next j : next i : size#=200.0/nx# : nbTiles=nx#*nz# : set ambient light 40 : set point light 0,40,100,0 : for j=0 to nz#-1 : for i=0 to nx#-1 : make object box 1000+n,size#-2,5,size#-2 : position object 1000+n,1+(i+0.5)*size#,0,1+(j+0.5)*size# : inc n : next i : next j : make object cone 99,30 : scale object 99,100,20,100 : color object 99,rgb(0,64,255) : ghost object on 99,1 : disable object zwrite 99 : dim color(8) as integer : for i=0 to 7 : read r,g,b : color(i)=rgb(r,g,b)
next i : set image colorkey 0,0,0 : ink rgb(255,255,255),0 : box 30,1,34,64 : box 1,30,64,34 : get image 101,1,1,64,64,1 : for i=0 to 5 : line i,25-i,i,40+i : line 10-i,25-i,10-i,40+i : next i : get image 2,0,20,10,45 : r=255 : g=1 : for i=1 to 255 : if g=255 : dec r,2 : endif : if r=255 and g<255 : inc g,2 : endif : ink rgb(r,g,0),0 : line i,0,i,16 : next i : get image 3,0,0,255,15 : scoreObj=999
nbDigits=4 : limbsize#=100 : make object plain scoreObj,limbsize#/8,limbsize#/2 : make mesh from object scoreObj,scoreObj : delete object scoreObj : xoffset#=(limbsize#/8)+(nbDigits*limbsize#*0.75)/-2.0 : yoffset#=limbsize#/-4 : for j=0 to nbDigits-1 : numlimb=j*7 : for i=0 to 6 : if numlimb+i=0 : make object scoreObj,scoreObj,0 : else : add limb scoreObj,numlimb+i,scoreObj : endif : next i : offset limb scoreObj,numlimb,xoffset#,yoffset#,0 : rotate limb scoreObj,numlimb,0,180,0 : offset limb scoreObj,numlimb+1,xoffset#+(limbsize#/2),yoffset#,0 : rotate limb scoreObj,numlimb+1,0,180,0 : offset limb scoreObj,numlimb+2,xoffset#,yoffset#+(limbsize#/2),0 : rotate limb scoreObj,numlimb+2,0,180,0 : offset limb scoreObj,numlimb+3,xoffset#+(limbsize#/2),yoffset#+(limbsize#/2),0 : rotate limb scoreObj,numlimb+3,0,180,0 : offset limb scoreObj,numlimb+4,xoffset#+(limbsize#/4),yoffset#+(limbsize#/-4),0
rotate limb scoreObj,numlimb+4,0,180,90 : offset limb scoreObj,numlimb+5,xoffset#+(limbsize#/4),yoffset#+(limbsize#/4),0 : rotate limb scoreObj,numlimb+5,0,180,90 : offset limb scoreObj,numlimb+6,xoffset#+(limbsize#/4),yoffset#+(limbsize#/4)+(limbsize#/2),0 : rotate limb scoreObj,numlimb+6,0,180,90 : inc xoffset#,(limbsize#*0.75) : next j : texture object scoreObj,2 : set object emissive scoreObj,rgb(0,128,0) : position object scoreObj,-50,200,500 : lock object on scoreObj : ghost object on scoreObj
RestartHereWhenPlayAgain: : for i=1 to nbastmax : astNumobj(i)=i : astNumcross(i)=i+100 : xscale#=0.75+(rnd(50.0)/100.0) : yscale#=0.75+(rnd(50.0)/100.0) : roughness=20 : make object sphere astNumobj(i),10,int(10.0*xscale#),int(10.0*yscale#) : scale object astNumobj(i),100*xscale#,100*yscale#,100 : make mesh from object astNumobj(i),astNumobj(i) : delete object astNumobj(i) : make memblock from mesh 255,astNumobj(i) : numvert = memblock dword(255,8)
dim vert(numvert) as boolean : for ii = 0 to numvert-1 : if vert(ii)=0 : pos=12+(ii*32) : X#=memblock float(255,pos) : Y#=memblock float(255,pos+4) : Z#=memblock float(255,pos+8) : newX#=X#*( ((100-roughness)+rnd(roughness*2))/100.00 ) : newy#=Y#*( ((100-roughness)+rnd(roughness*2))/100.00 ) : newZ#=Z#*( ((100-roughness)+rnd(roughness*2))/100.00 ) : for j=0 to numvert-1 : if vert(j)=0 : pos2=12+(j*32) : if memblock float(255,pos2) = X# and memblock float(255,pos2+4) = Y# and memblock float(255,pos2+8) = Z# : write memblock float 255,pos2,newX# : write memblock float 255,pos2+4,newY# : write memblock float 255,pos2+8,newZ# : vert(j)=1 : endif : endif : next j : endif : next ii : undim vert() : make mesh from memblock astNumobj(i),255
delete memblock 255 : make object astNumobj(i),astNumobj(i),0 : delete mesh astNumobj(i) : Set Object Cull astNumobj(i),0 : set object smoothing astNumobj(i),30 : make object plain astNumcross(i),20,20 : rotate object i+100,90,45,0 : position object astNumobj(i),0,0,-300 : position object astNumcross(i),0,0,-300 : texture object astNumcross(i),101 : set object transparency astNumcross(i),2 : astPosY#(i)=-50 : color object astNumobj(i),color(i MOD 8) : set object emissive astNumcross(i),color(i MOD 8) : set object diffuse astNumcross(i),color(i MOD 8) : make particles i, 101, 1, 20.0 : color particles i,255,128,0 : set particle emissions i,0 : set particle life i,30 : next i : make object plain 998,200,10 : texture object 998,3 : zrotate object 998,90 : lock object on 998 : position object 998,180,30,250
ghost object on 998 : dim v(nbTiles) as integer : dim v2(nbTiles) as boolean : v2(0)=1 : for i=0 to nbTiles-1 : repeat : v(i)=rnd(nbTiles) : until v2(v(i))=0 : v2(v(i))=1 : show object 1000+i : next i : numiter=0 : g#=1 : nbast=2 : nbcatch=0 : life#=1 : tmplife#=1 : missed=0 : r$="" : score=0 : tmpscore#=0 : setDigits(scoreObj,4,score) : dim t#(5) as float : lt#=1 : t#(0)=20
t#(1)=20 : t#(2)=20 : t#(3)=20 : t#(4)=20 : ot=timer() : repeat : x2=x+mousemovex() : if x2<10 : x2=10 : endif : if x2>(nx#*size#)-10 : x2=(nx#*size#)-10 : endif : x=x2 : z2=z-mousemovey() : if z2<10 : z2=10 : endif : if z2>(nz#*size#)-10 : z2=(nz#*size#)-10 : endif : z=z2 : position object 99,x,6,z : turn object left 99,lt# : position camera (nx#*size#/2.0)+(( (nx#*size#/2.0)-x )/10.0),100,-100
point camera (nx#*size#/2.0),40,100 : inc numiter : for i=1 to nbast : if numiter>20 : astMovY#(i)=astMovY#(i)-(0.02*g#*lt#) : astPosX#(i)=astPosX#(i)+(astMovX#(i)*lt#) : astPosY#(i)=astPosY#(i)+(astMovY#(i)*lt#) : astPosZ#(i)=astPosZ#(i)+(astMovZ#(i)*lt#) : endif : if astPosY#(i)<-50 : astPosX#(i)=rnd((nx#-1)*size#) : astPosY#(i)=200+rnd(300) : astPosZ#(i)=rnd((nz#-1)*size#) : astMovX#(i)=rnd(30)/40.0 : if astPosX#(i)>((nx#-1)*size#)/2.0 : astMovX#(i)=astMovX#(i)*-1.0 : endif : astMovY#(i)=0 : astMovZ#(i)=rnd(30)/40.0 : if astPosZ#(i)>((nz#-1)*size#)/2.0 : astMovZ#(i)=astMovZ#(i)*-1.0 : endif : endif : position object astNumobj(i),astPosX#(i),astPosY#(i),astPosZ#(i) : roll object left astNumobj(i),lt#
pitch object down astNumobj(i),1.3*lt# : position object astNumcross(i),astPosX#(i),2.6,astPosZ#(i) : scale object astNumcross(i),astPosY#(i)/3.0,astPosY#(i)/3.0,0 : if astPosY#(i)<8 and astPosY#(i)>-5 and astPosX#(i)>-5 and astPosX#(i)<(nx#*size#) and astPosZ#(i)>-5 and astPosZ#(i)<(nz#*size#) : if (x-astPosX#(i))*(x-astPosX#(i))+(z-astPosZ#(i))*(z-astPosZ#(i))<400 : dec score,astMovY#(i)*nbast : inc nbcatch : if nbcatch MOD 5=0 and nbast<nbastmax : inc nbast : endif : astPosY#(i)=8 : astMovX#(i)=(x-astPosX#(i))/-10.0 : astMovY#(i)=astMovY#(i)*-0.8 : astMovZ#(i)=(z-astPosZ#(i))/-10.0 : else : bz=astPosZ#(i)/size# : bx=astPosX#(i)/size# : n=bx+(bz*nx#) : if v2(v(n))=1 : hide object 1000+n : astPosY#(i)=-50 : v2(v(n))=0 : nbpart=(nbpart MOD nbastmax)+1 : position particles nbpart,astPosX#(i),2.5,astPosZ#(i) : spark#(nbpart)=200
if v(n)<=max : inc missed : r$=r$+" - "+str$(v(n)) : life#=1.0-((missed*1.0)/(nc*1.0)) : endif : endif : endif : endif : next i : for i=1 to nbastmax : if spark#(i)>0 : set particle emissions i,int(spark#(i)) : dec spark#(i),lt#*100.0 : else : if spark#(i)<0 : spark#(i)=0 : set particle emissions i,0 : endif : endif : next i : if tmpscore#<score : inc tmpscore#,lt#/3.0 : setDigits(scoreObj,4,int(tmpscore#)) : endif : if tmplife#>life#
scale object texture 998,1.0/tmplife#,1.0 : dec tmplife#,lt#/100.0 : scale object 998,tmplife#*100.0,100,100 : scale object texture 998,tmplife#,1 : position object 998,180,30-((200.0*(1.0-tmplife#))/2),250 : endif : inc nt : if nt=5 : nt=0 : endif : t#(nt)=timer()-ot : ot=timer() : lt#=(t#(0)+t#(1)+t#(2)+t#(3)+t#(4))/100.0 : sync : until missed=nc : hide object 998 : do : if tmpscore#<score : inc tmpscore#,lt#/3.0 : setDigits(scoreObj,4,int(tmpscore#)) : endif : text 10,150,"Your score: "+str$(score)+" ("+str$(nbcatch)+" catches)" : if score>bestScore : text 10,170,"This is your NEW RECORD!" : else
text 10,170,"Best score: "+str$(bestScore) : endif : text 10,190,"Your lucky numbers are:"+r$ : text 10,220,"Press [Return] to play again, [ESC] to quit." : if returnkey() : delete object 998 : for i=1 to nbastmax : delete particles i : delete object astNumobj(i) : delete object astNumcross(i) : astMovY#(i)=0 : next i : undim v() : undim v2() : if score>bestScore : BestScore=score : endif : goto RestartHereWhenPlayAgain : endif : sync : loop : end
function setDigits(numobj as integer,nbDigits as integer,value as integer) : dim digitlimb(10) as integer : digitlimb(0)=95 : digitlimb(1)=10 : digitlimb(2)=121 : digitlimb(3)=122 : digitlimb(4)=46 : digitlimb(5)=118 : digitlimb(6)=119 : digitlimb(7)=74 : digitlimb(8)=127 : digitlimb(9)=126 : digitlimb(10)=0 : ch$=str$(value) : length=len(ch$) : numdigit=1 : if length>nbDigits : inc numdigit,length-nbDigits : endif : for j=0 to nbDigits-1 : if nbDigits-j>length : v=10 : else : v=val(mid$(ch$,numdigit)) : inc numdigit
endif : for i=0 to 6 : if ((2^i) && digitlimb(v)) > 0 : show limb numobj,i+(j*7) : else : hide limb numobj,i+(j*7) : endif : next i : next j : undim digitlimb()
endfunction : data 127,127,0,255,0,0,0,255,0,0,0,255,255,0,255,255,127,0,0,127,127,127,0,127
remstart
This is "Asteroid Alert: Cosmic Lottery", the gamer's way to win the lottery (appealing, isn't it?).
Wanna play Loto and don't know which numbers to choose ? Play my little game !
Set up the size of your loto grid, how much numbers you've got to choose, and here you go...
Story:
You are in charge of a deep-space photovoltaic complex that sends vital energy to all the nearby colonies.
Suddenly, an alert flashes on your screen: an asteroids swarm of unusual size and density comes your way.
Quick, grab your Power-Mouse(tm) and move your Gravitic Quantum Singularity Deflector Shield (tm too, I'm afraid)
to repel the threat.
Meh... Who needs a story, anyway ?
Rules:
Use your mouse to deflect the falling asteroids. The crosshairs help you to locate them on the grid; the smaller they
appear, the closer they are from the ground. Faster asteroids give you more points. The game starts with two asteroids,
and each five asteroids deflected, another one is added.
Code details:
Line 1 (-> col 449): Initial setup and variables definition
In the "uncompressed" version, I use custom datatypes. But they can't be compressed, so I replaced them by "normal" arrays.
End of line 1 and : Ask the user for the number of squares in the grid, and the number of misses before game ends
Line 2 (-> col 333) Sync rate is lowered to 10 fps, so that key hits are not too fast.
End of line 2 and : Compute the "squarest" possible grid for the chosen number of squares, then build it with regulary spaced boxes
Line 3 (-> col 438) Define light, and make the player object (a translucent cone).
End of line 3 and : Define a set of colors for the asteroids
Line 4 Create the few needed textures (one for the crosshairs, one for the score, and one for the energy gauge)
Lines 5 and 6 : Create the score object (It was done in a function in the "uncompressed" version, but like custom datatypes, functions can't be compressed.
So the code is directly pasted where it's used.)
Original function:
*************************************************************************************
Name: makeDigits
Purpose: make a multi-limbed object looking like an old digital clock display
Parameters: Object number
Max length of the number (in digits)
Height of the number
Notes: - Each digit is made of 7 plains
- The first digit in the link hierarchy is the LEFT one
- The pivot (coord 0,0,0) of the object is at its exact center (just like a plain)
**************************************************************************************
Lines 7-8-9 : Program restart here when the player choose to play again
(-> col 167) Create the asteroids (once again the function has been pasted directly into the main code)
Original function:
*************************************************************************************
Name: makeAsteroid
Purpose: make an asteroid (duh!)
Parameters: Object number
Approximative size of the asteroid
Roughness of the asteroid surface (from 0 to 100)
Note: Not as clever as Riidii's function (http://forum.thegamecreators.com/?m=forum_view&t=55484&b=11),
but works well too (and maybe faster)
**************************************************************************************
How it works:
- Scales on X and Y axis are randomly set from 80% to 120% or size# => give an "oval" and more natural aspect
- Make a sphere, get its mesh, then make a memblock from it (note: memblock from DB primitives are in "274" format, not in standard "338")
- For each vertex:
If the vertex has not been altered yet, compute a randomly displaced position
For each vertex at the same position as the current one, apply the same displacement
- Create the final object from the memblock, hide cull polys, smooth, et voilà!
End of line 9: Create crosshairs that indicate asteroids' positions on the grid, the particules used for explosions
and the energy gauge
Line 10 (->col 204): Place a different random number under each square of the grid
Initialize score, energy, asteroids...
Line 11 and line 12: Initialize "Forced-Timing" variables
(-> col 38) Main loop
Move player acrosshair the grid according to mouse moves
Slight camera move; useless, but looks cool
Line 12 and line 13: Asteroids movements ("numiter" is used to wait a few loops before starting => "forced-timing" moves are less chaotic after initialization)
(-> col 169) If the asteroid is below the grid height, its position and movement are randomly reset
Position the asteroid, add a little rotation for the look, position the corresponding crosshair on the grid
End of line 13 and : If the asteroid hits the grid, there's two cases:
line 14 (->col 139) - The player catches it => It bounces, score is increased by the vertical speed of the asteroid,
and every five catches we add an asteroid to the dreadful threat;
- The player misses it => we compute which square has been hit, and if it's still here we hide it,
we start an explosion, energy is decreased, and we add the number hidden
under the square to the "Lucky Numbers" list.
(Not every number counts: it the grid size is greater than the maximum
chosen by the player at program start, some square are "empty". In this
case, energy is not decreased.)
Line 14 (->col 343): Explosions. Particles emission rate is slowly decreased for a better effect
End of line 14 and : Score increases progressively. Life decreases progressively. How very metaphysic!
line 15 (->col 222) If the displayed score is inferior to the real score, we slowly increase it. It looks better that way.
Idem for energy. Again, the function originally used has been pasted into the main code:
*************************************************************************************
Name: decreaseLifeBar
Purpose: reduce the size of the energy gauge and scale its texture
Parameters: Object number
Current percentage of energy displayed by the gauge
Value to substract to this percentage
Note: Quite surprinsingly, the "scale object texture" command does not work like the
"scale object" command: the first one uses the current size of the texture as reference for
rescaling, whereas the later uses the original (thus invariant) size of the object.
**************************************************************************************
First, restore the original scale of the texture
Decrease value, then scale the object and its texture
Line 15 (->col 360): Forced-timing and sync. End of main loop
End of line 15 and : Game is over, pal. Sorry 'bout that.
Line 16 Score goes on moving until it reach the final score
Display score, best score, and lucky numbers list
If the player wants to play again, we delete a few things before going back to the start:
- asteroids and particles, so they can be re-created
- numbers hidden under each square
Line 17-18 : Function used to display the score
*************************************************************************************
Name: setDigits
Purpose: use the previously defined multi-limbed object to display a number
Parameters: Object number
Max length of the number
Value
Notes: - Initial zeroes are not shown
- "dim" and "undim" of the array at each call is awfully un-optimized, but I wanted
the function to be self-contained => easier to understand
How it works:
Each digit can be 0 to 9 (with an extra value of 10 which means "not visible", for the initial zeroes).
Each digit is made of 7 limbs, and those limbs are hidden or shown according to the pattern to display.
So if you say that "1" means the limb is shown and "0" means it's hidden, you can code each number
with a 7 bits sequence like "1111010". That's what I do in the "digitlimb" array: "1111010" is 122, the
pattern for "3".
This done, all you've got to do check each bit of the sequence to know if the corresponding limb must
be shown of not.
**************************************************************************************
Convert the value in string, easier to manipulate
If the value is longer than nbDigits, skip the first digits
for each digits:
if the digit is an initial zero, hide it. Else, get its value
Then check each bit to show/hide limbs
Line 19 : Data set used for the asteroids' colors
Notes on "forced timing"
"Forced timing" (I know the term isn't correct here) is used to ensure that the objects move
at (more or less) the same speed on all computers. Each movement is multiplicated by a factor
which is ideally equal to 1 on the reference computer, superior to 1 on slower cpu, and inferior
to 1 on faster ones.
Ex: the base movements for this game has been designed for a sync rate = 50. "sync rate 50" means
each loop is 20ms long. So, with "sync rate 0", the forced timing factor is equal to [loop duration]/20.0 .
If your CPU is twice as fast as mine and can loop in only 10ms, the factor will be 0.5 and each
movement (in one loop) will be half as long as those on the reference CPU => they will be smoother,
but not faster.
And as the loop durations are irregular (if Windows does something in the background, for example),
I use the mean duration of the five last loops to avoid chaotic behaviours. That's why the "forced
timing" factor is equal to ((t1+t2+t3+t4+t5)/5.0)/20.0 <=> (t1+t2+t3+t4+t5)/100.0 .
remend
And the "uncompressed" version, for an easier understanding:
rem Initial setup and custom types
autocam off:sync on:randomize timer():color backdrop 0:set text opaque:hide mouse
type coord3d
x# as float
y# as float
z# as float
endtype
type asteroid
numobj as integer
numcross as integer
pos as coord3d
mov as coord3d
endtype
nbastmax=16
dim ast(nbastmax) as asteroid
dim spark#(nbastmax) as integer
rem Ask the user for the number of squares in the grid, and the number of misses before game ends
rem Sync rate is lowered to 10 fps, so that key hits are not too fast.
i=50:sync rate 10
repeat
text 10,70,"What is the size of the grid ? (UP/DOWN/RETURN) => " + str$(i)
if upkey() and i<200:inc i:endif
if downkey() and i>5:dec i:endif
if returnkey():max=i:endif
sync
until max>0 and returnkey()=0
i=5
repeat
text 10,90,"How many numbers do you have to choose ? (UP/DOWN/RETURN) => " + str$(i) + " "
if upkey() and i<200:inc i:endif
if downkey() and i>1:dec i:endif
if returnkey():nc=i:endif
sync
until nc>0
sync rate 0
rem Compute the "squarest" possible grid for the chosen number of squares, then build it with regulary spaced boxes
for i=1 to sqrt(max)+1
for j=i to i+2
if i*j>=max and abs(max-(i*j))<abs(max-(nx#*nz#))
nz#=i:nx#=j
endif
next j
next i
size#=200.0/nx#
nbTiles=nx#*nz#
set ambient light 40:set point light 0,40,100,0
for j=0 to nz#-1
for i=0 to nx#-1
make object box 1000+n,size#-2,5,size#-2
position object 1000+n,1+(i+0.5)*size#,0,1+(j+0.5)*size#
inc n
next i
next j
rem Make the player object (a cone), define the max number of asteroids and
make object cone 99,30:scale object 99,100,20,100:color object 99,rgb(0,64,255):ghost object on 99,1
disable object zwrite 99
rem Define a set of colors for the asteroids
dim color(8) as integer
for i=0 to 7
read r,g,b:color(i)=rgb(r,g,b)
next i
rem Create the few needed textures (one for the crosses, one for the score, and one for the energy gauge)
set image colorkey 0,0,0
ink rgb(255,255,255),0:box 30,1,34,64:box 1,30,64,34:get image 101,1,1,64,64,1
for i=0 to 5
line i,25-i,i,40+i
line 10-i,25-i,10-i,40+i
next i
get image 2,0,20,10,45
r=255:g=1
for i=1 to 255
if g=255 then dec r,2
if r=255 and g<255 then inc g,2
ink rgb(r,g,0),0
line i,0,i,16
next i
get image 3,0,0,255,15
rem Create the score object
scoreObj=999
makeDigits(scoreObj,4,100)
texture object scoreObj,2
set object emissive scoreObj,rgb(0,128,0)
position object scoreObj,-50,200,500
lock object on scoreObj
ghost object on scoreObj
rem Program restart here when the player choose to play again
RestartHereWhenPlayAgain:
for i=1 to nbastmax
rem Create the asteroids and the crosses that indicate their position on the grid
ast(i).numobj=i
ast(i).numcross=i+100
makeAsteroid(ast(i).numobj,10,20)
make object plain ast(i).numcross,20,20:rotate object i+100,90,45,0
position object ast(i).numobj,0,0,-300:position object ast(i).numcross,0,0,-300
texture object ast(i).numcross,101
set object transparency ast(i).numcross,2
ast(i).pos.y#=-50
color object ast(i).numobj,color(i MOD 8)
set object emissive ast(i).numcross,color(i MOD 8)
set object diffuse ast(i).numcross,color(i MOD 8)
rem Create the particules used for explosions
make particles i, 101, 1, 20.0
color particles i,255,128,0
set particle emissions i,0
set particle life i,30
next i
rem Create the energy gauge
make object plain 998,200,10
texture object 998,3
zrotate object 998,90
lock object on 998
position object 998,180,30,250
ghost object on 998
rem Place a different random number under each square of the grid
dim v(nbTiles) as integer
dim v2(nbTiles) as boolean
v2(0)=1
for i=0 to nbTiles-1
repeat:v(i)=rnd(nbTiles):until v2(v(i))=0:v2(v(i))=1
show object 1000+i
next i
rem Initialize score, energy, asteroids...
numiter=0:g#=1:nbast=2:nbcatch=0:life#=1:tmplife#=1:missed=0:r$=""
score=0:tmpscore#=0:setDigits(scoreObj,4,score)
rem Initialize "Forced-Timing"
dim t#(5) as float:lt#=1:t#(0)=20:t#(1)=20:t#(2)=20:t#(3)=20:t#(4)=20:ot=timer()
rem Main loop
repeat
rem Move player across the grid according to mouse moves
x2=x+mousemovex()
if x2<10 then x2=10
if x2>(nx#*size#)-10 then x2=(nx#*size#)-10
x=x2
z2=z-mousemovey()
if z2<10 then z2=10
if z2>(nz#*size#)-10 then z2=(nz#*size#)-10
z=z2
position object 99,x,6,z:turn object left 99,lt#
rem Slight camera move; useless, but it looks cool
position camera (nx#*size#/2.0)+(( (nx#*size#/2.0)-x )/10.0),100,-100
point camera (nx#*size#/2.0),40,100
rem Asteroids movements ("numiter" is used to wait a few loops before starting => "forced-timing" moves are less chaotic after initialization)
inc numiter
for i=1 to nbast
if numiter>20
ast(i).mov.y#=ast(i).mov.y#-(0.02*g#*lt#)
ast(i).pos.x#=ast(i).pos.x#+(ast(i).mov.x#*lt#)
ast(i).pos.y#=ast(i).pos.y#+(ast(i).mov.y#*lt#)
ast(i).pos.z#=ast(i).pos.z#+(ast(i).mov.z#*lt#)
endif
rem If the asteroid is below the grid height, its position and movement are randomly reset
if ast(i).pos.y#<-50
ast(i).pos.x#=rnd((nx#-1)*size#)
ast(i).pos.y#=200+rnd(300)
ast(i).pos.z#=rnd((nz#-1)*size#)
ast(i).mov.x#=rnd(30)/40.0
if ast(i).pos.x#>((nx#-1)*size#)/2.0
ast(i).mov.x#=ast(i).mov.x#*-1.0
endif:ast(i).mov.y#=0
ast(i).mov.z#=rnd(30)/40.0
if ast(i).pos.z#>((nz#-1)*size#)/2.0
ast(i).mov.z#=ast(i).mov.z#*-1.0
endif
endif
rem Position the asteroid, add a little rotation for the look, position the corresponding cross on the grid
position object ast(i).numobj,ast(i).pos.x#,ast(i).pos.y#,ast(i).pos.z#
roll object left ast(i).numobj,lt#
pitch object down ast(i).numobj,1.3*lt#
position object ast(i).numcross,ast(i).pos.x#,2.6,ast(i).pos.z#:scale object ast(i).numcross,ast(i).pos.y#/3.0,ast(i).pos.y#/3.0,0
rem If the asteroid hits the grid, there's two cases:
if ast(i).pos.y#<8 and ast(i).pos.y#>-5 and ast(i).pos.x#>-5 and ast(i).pos.x#<(nx#*size#) and ast(i).pos.z#>-5 and ast(i).pos.z#<(nz#*size#)
rem - The player catches it => It bounces, score is increased by the vertical speed of the asteroid, and every five catches we add an asteroid to the dreadful threat
if (x-ast(i).pos.x#)*(x-ast(i).pos.x#)+(z-ast(i).pos.z#)*(z-ast(i).pos.z#)<400
dec score,ast(i).mov.y#*nbast:inc nbcatch:if nbcatch MOD 5=0 and nbast<nbastmax:inc nbast:endif
ast(i).pos.y#=8:ast(i).mov.x#=(x-ast(i).pos.x#)/-10.0:ast(i).mov.y#=ast(i).mov.y#*-0.8:ast(i).mov.z#=(z-ast(i).pos.z#)/-10.0
else
rem - The player misses it => we compute which square has been hit, and if it's still here we hide it, we start an explosion, energy is decreased, and we add the square number to the "Lucky Numbers" list
bz=ast(i).pos.z#/size#:bx=ast(i).pos.x#/size#:n=bx+(bz*nx#)
if v2(v(n))=1
hide object 1000+n
ast(i).pos.y#=-50:v2(v(n))=0
nbpart=(nbpart MOD nbastmax)+1
position particles nbpart,ast(i).pos.x#,2.5,ast(i).pos.z#
spark#(nbpart)=200
rem (Not every number counts: it must be inferior to the maximum chosen by the player at program start)
if v(n)<=max
inc missed
r$=r$+" - "+str$(v(n))
life#=1.0-((missed*1.0)/(nc*1.0))
endif
endif
endif
endif
next i
rem Explosions. Particles emission rate is slowly decreased for a better effect
for i=1 to nbastmax
if spark#(i)>0
set particle emissions i,int(spark#(i)):dec spark#(i),lt#*100.0
else
if spark#(i)<0 then spark#(i)=0:set particle emissions i,0
endif
next i
rem Score increases progressively. Life decreases progressively. Very metaphysic!
if tmpscore#<score then inc tmpscore#,lt#/3.0:setDigits(scoreObj,4,int(tmpscore#))
if tmplife#>life# then tmplife#=decreaseLifeBar(998,tmplife#,lt#/100.0)
rem Forced-timing and sync
inc nt:if nt=5:nt=0:endif:t#(nt)=timer()-ot:ot=timer():lt#=(t#(0)+t#(1)+t#(2)+t#(3)+t#(4))/100.0
sync
until missed=nc
rem Game is over, pal. Sorry 'bout that.
do
rem Score and energy levels go on moving until they reach their final level
if tmpscore#<score then inc tmpscore#,lt#/3.0:setDigits(scoreObj,4,int(tmpscore#))
if tmplife#>life# then tmplife#=decreaseLifeBar(998,tmplife#,lt#/100.0)
rem Show score, best score, and lucky numbers list
text 10,150,"Your score: "+str$(score)+" ("+str$(nbcatch)+" catches)"
if score>bestScore
text 10,170,"This is your NEW RECORD!"
else
text 10,170,"Best score: "+str$(bestScore)
endif
text 10,190,"Your lucky numbers are:"+r$
text 10,220,"Press [Return] to play again, [ESC] to quit."
rem If the player wants to play again, we delete a few things before going back to the start
if returnkey()
delete object 998
for i=1 to nbastmax
delete particles i
delete object ast(i).numobj
delete object ast(i).numcross
ast(i).mov.y#=0
next i
undim v():undim v2()
if score>bestScore then BestScore=score
goto RestartHereWhenPlayAgain
endif
inc nt:if nt=5:nt=0:endif:t#(nt)=timer()-ot:ot=timer():lt#=(t#(0)+t#(1)+t#(2)+t#(3)+t#(4))/100.0
sync
loop
end
remstart *************************************************************************************
Name: makeAsteroid
Purpose: make an asteroid (duh!)
Parameters: Object number
Approximative size of the asteroid
roughness of the asteroid surface (from 0 to 100)
Note: Not as clever as Riidii's function (http://forum.thegamecreators.com/?m=forum_view&t=55484&b=11),
but works well too (and maybe faster)
************************************************************************************** remend
Function makeAsteroid(numobj as integer,size# as float,roughness as integer)
rem Scales on X and Y axis are randomly set from 80% to 120% or size# => give an "oval" and more natural aspect
xscale#=0.75+(rnd(50.0)/100.0):yscale#=0.75+(rnd(50.0)/100.0)
rem Make a sphere, get its mesh, then make a memblock from it (note: memblock from DB primitives are in "274" format, not in standard "338")
make object sphere numobj,size#,int(10.0*xscale#),int(10.0*yscale#)
scale object numobj,100*xscale#,100*yscale#,100
make mesh from object numobj,numobj
delete object numobj
make memblock from mesh 255,numobj
rem For each vertex:
numvert = memblock dword(255,8)
dim vert(numvert) as boolean
for i = 0 to numvert-1
rem If the vertex has not been altered yet, compute a randomly displaced position
if vert(i)=0
pos=12+(i*32)
X#=memblock float(255,pos)
Y#=memblock float(255,pos+4)
Z#=memblock float(255,pos+8)
newX#=X#*( ((100-roughness)+rnd(roughness*2))/100.00 )
newy#=Y#*( ((100-roughness)+rnd(roughness*2))/100.00 )
newZ#=Z#*( ((100-roughness)+rnd(roughness*2))/100.00 )
rem For each vertex at the same position as the current one, apply the same displacement
for j=0 to numvert-1
if vert(j)=0
pos2=12+(j*32)
if memblock float(255,pos2) = X# and memblock float(255,pos2+4) = Y# and memblock float(255,pos2+8) = Z#
write memblock float 255,pos2,newX#
write memblock float 255,pos2+4,newY#
write memblock float 255,pos2+8,newZ#
vert(j)=1
endif
endif
next j
endif
next i
undim vert()
rem Create the final object from the memblock, hide cull polys, smooth, et voilà!
make mesh from memblock numobj,255
delete memblock 255
make object numobj,numobj,0
delete mesh numobj
Set Object Cull numobj,0
set object smoothing numobj,30
EndFunction
remstart *************************************************************************************
Name: makeDigits
Purpose: make a multi-limbed object looking like an old digital clock display
Parameters: Object number
Max length of the number
Height of the number
Notes: - Each digit is made of 7 plains
- The first digit in the link hierarchy is the LEFT one
- The pivot (coord 0,0,0) of the object is at its exact center (just like a plain)
************************************************************************************** remend
function makeDigits(numobj as integer,nbDigits as integer,size# as float)
make object plain numobj,size#/8,size#/2
make mesh from object numobj,numobj
delete object numobj
xoffset#=(size#/8)+(nbDigits*size#*0.75)/-2.0
yoffset#=size#/-4
for j=0 to nbDigits-1
numlimb=j*7
for i=0 to 6
if numlimb+i=0
make object numobj,numobj,0
else
add limb numobj,numlimb+i,numobj
endif
next i
offset limb numobj,numlimb,xoffset#,yoffset#,0:rotate limb numobj,numlimb,0,180,0
offset limb numobj,numlimb+1,xoffset#+(size#/2),yoffset#,0:rotate limb numobj,numlimb+1,0,180,0
offset limb numobj,numlimb+2,xoffset#,yoffset#+(size#/2),0:rotate limb numobj,numlimb+2,0,180,0
offset limb numobj,numlimb+3,xoffset#+(size#/2),yoffset#+(size#/2),0:rotate limb numobj,numlimb+3,0,180,0
offset limb numobj,numlimb+4,xoffset#+(size#/4),yoffset#+(size#/-4),0:rotate limb numobj,numlimb+4,0,180,90
offset limb numobj,numlimb+5,xoffset#+(size#/4),yoffset#+(size#/4),0:rotate limb numobj,numlimb+5,0,180,90
offset limb numobj,numlimb+6,xoffset#+(size#/4),yoffset#+(size#/4)+(size#/2),0:rotate limb numobj,numlimb+6,0,180,90
inc xoffset#,(size#*0.75)
next j
endfunction
remstart *************************************************************************************
Name: setDigits
Purpose: use the previously defined multi-limbed object to display a number
Parameters: Object number
Max length of the number
Value
Notes: - Initial zeroes are not shown
- "dim" and "undim" of the array at each call is awfully un-optimized, but I wanted
the function to be self-contained => easier to understand
How it works:
Each digit can be 0 to 9 (with an extra value of 10 which means "not visible", for the initial zeroes).
Each digit is made of 7 limbs, and those limbs are hidden or shown according to the pattern to display.
So if you say that "1" means the limb is shown and "0" means it's hidden, you can code each number
with a 7 bits sequence like "1111010". That's what I do in the "digitlimb" array: "1111010" is 122, the
pattern for "3".
This done, all you've got to do check each bit of the sequence to know if the corresponding limb must
be shown of not.
************************************************************************************** remend
function setDigits(numobj as integer,nbDigits as integer,value as integer)
dim digitlimb(10) as integer
digitlimb(0)=95:digitlimb(1)=10:digitlimb(2)=121:digitlimb(3)=122:digitlimb(4)=46:digitlimb(5)=118:digitlimb(6)=119:digitlimb(7)=74:digitlimb(8)=127:digitlimb(9)=126:digitlimb(10)=0
rem Convert the value in string, easier to manipulate
ch$=str$(value):length=len(ch$)
rem If the value is more long than nbDigits, skip the first digits
numdigit=1
if length>nbDigits then inc numdigit,length-nbDigits
rem for each digits:
for j=0 to nbDigits-1
rem if the digit is an initial zero, hide it. Else, get its value
if nbDigits-j>length
v=10
else
v=val(mid$(ch$,numdigit))
inc numdigit
endif
rem Then check each bit to show/hide limbs
for i=0 to 6
if ((2^i) && digitlimb(v)) > 0
show limb numobj,i+(j*7)
else
hide limb numobj,i+(j*7)
endif
next i
next j
undim digitlimb()
endfunction
remstart *************************************************************************************
Name: decreaseLifeBar
Purpose: reduce the size of the energy gauge and scale its texture
Parameters: Object number
Current percentage of energy displayed by the gauge
Value to substract to this percentage
Note: Quite surprinsingly, the "scale object texture" command does not work like the
"scale object" command: the first one uses the current size of the texture as reference for
rescaling, whereas the later uses the original (thus invariant) size of the object.
************************************************************************************** remend
function decreaseLifeBar(numObj as integer,currentPercentage# as float,decrease# as float)
rem restore the original scale of the texture
scale object texture numObj,1.0/currentPercentage#,1.0
rem Decrease value, then scale the object and its texture
dec currentPercentage#,decrease#
scale object numObj,currentPercentage#*100.0,100,100
scale object texture numObj,currentPercentage#,1
rem The position of the gauge is hard-coded, which is bad. It's easy to correct, but I didn't have any room left...
position object numObj,180,30-((200.0*(1.0-currentPercentage#))/2),250
endfunction currentPercentage#
rem Color set for the asteroids
data 127,127,0,255,0,0,0,255,0,0,0,255,255,0,255,255,127,0,0,127,127,127,0,127
I usually play it in 800x600 windowed mode, but you can have it your way, I guess.
Have fun!
(And tell me if you find some bug...)
[EDIT 03/05/2006]
I've found a bug in the definition of the crosshairs: with some GPU (Geforce 2 MX), the crosshairs were all white, instead of being of same color of the asteroids. I've replaced the "set object ambience" with a "set object diffuse" and it seems to work. Please tell me if it doesn't work for you...)
[EDIT 03/06/2006]
Another bug fixed (man, it becomes embarrassing...

). At restart, asteroids' vertical speed wasn't reset to zero, so when they were going up (that happens when you loose immediatly after catching an asteroid), they hit the grid
from under.
Please get the new code; I hope it's the last fix (thought I know there's never such thing as a "
last fix" in coding...

)
Ideas: memories of things which did not occur yet...
http://keleb.free.fr/codecorner