A Wee Tank Game

Miscellaneous Forums/Blitz Showcase/A Wee Tank Game

Was messing about with this over the last few weeks. All graphics generated in code. If you want sound you'll need to add your own in the GAMEinit() function, and set SOUNDon to TRUE. No web space I'm afraid!

It's an ANALOGUE / MOUSE deathmatch for one or two players.

Analogues ... left analogue for movement, right for firing ala Super Smash TV. R3 drops / activates mines, P pauses, ESC to quit.

Mouse / Keyboard ... cursors to move, mouse to move Gun Target, LeftMouse to fire, RightMouse to drop/activate mine.

Use cursors and enter for menus or change all the keys to whatever at the top of the code.

If you trigger an explosion you will get points for anything killed as a result of any chain explosions.

Pick up a bonus and you keep it until dead.

Hud ...

Top bar is Life which will recharge up to a max of the power, both go down when hit, power only a fraction though. Your dead when life is 0. Bottom Bar is laser recharge, if 0 you can't fire. The more bullet bonuses you have the faster this is used up.

Bonuses ..

Tank Speed
Bullet Speed
Bullet Damage
Bullet Spread
Bullet Reload
Life Recharge
Shield
Mine

That's about it I think. Not really planning on expanding as meant to be very simple but feel free to give me feedback of any kind or maybe ideas for more features.

You can change the polycount on the graphics by using the Detail variable.

Here's a screenshot ..



Graphics3D 1280,960,16,1
SetFont LoadFont("Tahoma",32,1)
SeedRnd MilliSecs()

Global JOYSTICK
Global GunTarget
Global KUP = 200
Global KDOWN = 208
Global KLEFT = 203
Global KRIGHT = 205
Global KSELECT = 28
Global HGW = GraphicsWidth()*.5
Global HGH = GraphicsHeight()*.5

Const detail# = 25
Const FPS = 30
Const FadeSpeed# = .02
Const Gravity# = 0.05
Const tanks = 4
Const bullets = 100
Const particles = 750
Const drums = 8
Const menus = 2
Const FireTurn# = .25
Const MoveTurn# = .1
Const BulletLife# = 30
Const T_TANK = 1
Const T_ARENA = 2
Const T_DRUM = 3
Const T_BULLET = 4
Const AI_Attack = 0
Const AI_Retreat = 1
Const AI_GoForBonus = 2
Const AI_Flee = 3
Const AI_Straffe = 4
Global SOUNDon = True
Global SOUNDclang
Global SOUNDlaser
Global SOUNDhit
Global SOUNDexplode
Global SOUNDbonus
Global SOUNDmenu
Global camera=CreateCamera()
Global menu_cam = CreateCamera()
Global Pivot = CreatePivot()
Global Light = CreateLight()
Global AIPivot = CreatePivot()
Global FrameTimer = CreateTimer(FPS)
Global BulletMesh
Global ParticleMesh
Global DrumMesh
Global BubbleMesh
Global NextBullet
Global NextParticle
Global ParticleTemplate
Global Fade
Global FadeStatus#
Global NX#
Global NZ#
Global MINEactivate, FIREactivate
Global zoom#=6.75
Global GAMEOVER
Global QUIT
Global PLAYERS
Global KILLS
Global DIFFICULTY

Collisions T_TANK,T_TANK,1,2
Collisions T_TANK,T_ARENA,2,2
Collisions T_TANK,T_DRUM,1,2
Collisions T_BULLET,T_ARENA,2,1	
Collisions T_BULLET,T_TANK,1,1
Collisions T_BULLET,T_DRUM,1,1

Type tank_type
	Field ID
	Field Model
	Field Turret
	Field MaxSpeed#
	Field BulletReload
	Field BulletSpeed#
	Field BulletSpread
	Field BulletHeat#
	Field BulletTimer
	Field Shield#
	Field Damage#
	Field Score
	Field ScoreTexture
	Field Respawn
	Field BubbleShield
	Field Life#
	Field LifeBar
	Field LifeRecharge#
	Field Power#
	Field PowerBar
	Field Cooler#
	Field CoolerBar
	Field MineCounter
	Field Mine
	Field Control
	Field r#,g#,b#
	;ai stuff
	Field MoveCounter
	Field FireCounter
	Field MX#, MZ#
	Field FX# , FZ#
	Field Target
End Type

Type bullet_type
	Field ID
	Field Model
	Field Life
	Field Speed#
	Field Damage#
End Type

Type drum_type
	Field ID
	Field Model
	Field Life#
End Type

Type particle_type
	Field Vertex
	Field x#,y#,z#
	Field vx#,vy#,vz#
	Field r,g,b
	Field life#
	Field fade#
	Field size#
	Field weight#
End Type

Type bonus_type
	Field ID
	Field Model
	Field timer
End Type

Type arena_type
	Field Model
	Field Hud
	Field Radar
End Type	

Type menu_type
	Field Model
	Field ID
	Field texture
	Field options
	Field title$
	Field initial
	Field current
	Field sub_options[5]
	Field sub_string$[5]
	Field sub_current[5]
End Type

Dim tank.tank_type(tanks-1)
Dim bullet.bullet_type( bullets -1 )
Dim drum.drum_type ( drums - 1)
Dim particle.particle_type ( particles - 1)
Dim menu.menu_type( menus -1 )
Global BONUS.bonus_type = New bonus_type
Global ARENA.arena_type = New arena_type

GAMEinit()

Repeat
	GAMEstart()
	Repeat
		WaitTimer(FrameTimer)
		BULLETSupdate()
		PARTICLESupdate()
		BONUSupdate()
		TANKSupdate()
		UpdateWorld()
		RenderWorld()
		Flip
	Until QUIT Or GAMEOVER  
	If GAMEOVER GAMEend()
Until (1=2)
		
End

;==========================================================
;==========================================================
;==========================================================

Function TANKSupdate()

	;radar
	TurnEntity ARENA\radar,0,5,0

	For t.tank_type = Each tank_type
	
		t\BulletTimer = t\BulletTimer - ( t\BulletTimer > 0 )
								
		If t\Life > 0 
	
			FIREactivate = True
			
			;control
			If t\Control < 2
				If JOYSTICK
					;analogue
					t\MX = JoyX(t\ID)
					t\MZ = -JoyY(t\ID)
					t\FX = JoyRoll(t\ID) / 180.0
					t\FZ = -JoyZ(t\ID)
					MINEactivate = JoyDown( 12,t\ID )
				Else
					;mouse / keyboard
					t\MX = KeyDown( KRIGHT )-KeyDown( KLEFT )
					t\MZ = KeyDown( KUP )-KeyDown( KDOWN )
					FIREactivate = MouseDown(1)
					MINEactivate = MouseDown(2)
				EndIf
			Else
				AI ( t )
			EndIf
				
			;moving
			speed#  = NORMALxz( t\MX , t\MZ ) 
			If speed > 0.3
				PositionEntity Pivot,EntityX(t\Model) + NX * 5 , 0 , EntityZ(t\Model) + NZ * 5
				RotateEntity t\Model,0,EntityYaw(t\Model)+DeltaYaw(t\Model,Pivot) * MoveTurn ,0
				speed = speed * t\MaxSpeed
				TranslateEntity t\Model , NX * speed , (3-EntityY(t\Model)) , NZ * speed 
				PARTICLEnew( t\Model, 0,-1.5,-3, -.1,.1, -.1,.1, -.2,-.1 , 64,80,64,80,64,80, .25,.5, .25 , .1, 1)
			EndIf
			
			;firing - joystick / ai control 
			speed# = NORMALxz( t\FX , t\FZ )
			If speed > 0.5 
				PositionEntity Pivot,EntityX(t\Model) + NX * 5 , 0 , EntityZ(t\Model) + NZ * 5
				yaw# = DeltaYaw(t\Turret,Pivot)
				TurnEntity t\Turret,0, yaw *FireTurn ,0
				If  t\BulletTimer = 0  And  t\Cooler > t\BulletHeat And ( Abs(yaw)< 15 )  
					If t\respawn = 0 And FIREactivate BULLETnew(  t )
				EndIf 
			EndIf
			PositionEntity t\Turret,EntityX(t\Model),3,EntityZ(t\Model),1	
			
			;firing - mouse
			If t\Control < 2 And ( Not JOYSTICK )
				PositionEntity GunTarget,( MouseX() - HGW ) *.145 , 0 , ( HGH - MouseY() ) *.17
				PointEntity t\Turret , GunTarget
				RotateEntity t\Turret,0,EntityYaw( t\Turret ),0
				If t\BulletTimer = 0 And Abs(yaw)< 15  And t\Cooler > t\BulletHeat
					If t\respawn = 0 And FIREactivate BULLETnew(  t )
				EndIf
			End If
			
			;recharge gun & life		
			t\Life = LIMIT( t\Life + t\LifeRecharge, 0, t\Power )
			t\Cooler = LIMIT( t\Cooler + .0075, 0, 1)
	
			;lay / trigger mine
			If t\MineCounter = 1 And MINEactivate MINEnew( t, 0 )
			If t\MineCounter > 1 t\MineCounter = t\mineCounter + 1
			If t\MineCounter > 30 And MINEactivate MINEnew( t,1 )
															
		EndIf
		
		;bonus pickup
		If BONUS\timer > 0 And EntityDistance( BONUS\Model, t\Model ) < 3
			BONUS\timer = 0
			TANKbonus (t , BONUS\ID )
			PARTICLEnew ( BONUS\Model, 0,0,0, 0,0,.5,.5,0,0, 255,255,255,255,0,0, .25,.5, 1 , .01 , 8, 10 )
			SOUNDplay( SOUNDbonus )
		EndIf
			
		;respawn	
		If t\Respawn > 0 TANKrespawn( t )
		
		;update hud
		ScaleEntity t\LifeBar,t\Life,1,1
		ScaleEntity t\PowerBar,t\Power,1,1
		ScaleEntity t\CoolerBar,t\Cooler,1,1

	Next
	
	;quit
	QUIT = KeyDown(1)
	
	;pause
	If KeyDown(25) MENUshow ( MENU(1) )
		
End Function
	
;==========================================================
;==========================================================
;==========================================================

Function BULLETSupdate()

	For b.bullet_type = Each bullet_type
		
		MoveEntity b\Model,0,0,b\speed
		b\Life = b\Life - (b\Life > 0)
		
		;do collisions
		If CountCollisions(b\Model) > 0
			For ID = 1 To 3
				entity = EntityCollided (b\Model,ID)
				If entity <> 0
					Select ID
						Case T_TANK
							t.tank_type = Object.tank_type( EntityName( entity ) )
							damage# = b\Damage * (1.0 - t\Shield)
							t\Power = LIMIT( t\Power - damage*.25,0.0,1.0)
							t\Life = LIMIT( t\Life - damage,0.0,1.0)
							If t\Life = 0 And t\Respawn = 0
								;destroy tank
								EXPLOSION ( t\Turret, 20 , b\ID, t\r,t\g,t\b, True )
							Else
								;hit tank
								h.tank_type = tank( b\ID )
								HIT ( b\Model, h\r,h\g,h\b, False , SOUNDhit,  True )
							EndIf
						Case T_ARENA
							;hit arena
							HIT ( b\Model, 142,100,142, True, SOUNDclang, False ) 
						Case T_DRUM
							d.drum_type = Object.drum_type( EntityName( entity ) )
							d\Life = LIMIT( d\Life - b\Damage,0.0,2.0)
							If d\Life = 0 
								;destroy drum
								EXPLOSION ( d\Model, 20 , b\ID, 168,116,100, False )
							Else
								;hit drum
								HIT ( b\Model, 168,116,100, True, SOUNDclang, False )
							EndIf	
					End Select
				EndIf
			Next
			b\Life =0 
		EndIf
						
		If b\Life = 0 HideEntity b\Model:ResetEntity b\Model
		
	Next
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLESupdate()

	s = GetSurface( ParticleMesh , 1 )
	t = GetSurface( ParticleTemplate , 1 )
	cv = CountVertices(t)-1
		
	For p.particle_type = Each particle_type
	
		p\Life = LIMIT( p\Life - p\fade ,0 , 1.0 ) 
		
		p\vy = p\vy - Gravity * p\weight
		p\x = p\x + p\vx
		p\y = p\y + p\vy
		p\z = p\z + p\vz
		
		If p\y < p\size 
			p\y = p\size
			p\vx = p\vx * .75
			p\vy = -p\vy*.5
			p\vz = p\vz * .75
		EndIf
		
		For v=0 To cv
			px# = p\x + VertexX(t,v) * p\size
			py# = p\y + VertexY(t,v) * p\size
			pz# = p\z + VertexZ(t,v) * p\size
			VertexCoords s , p\Vertex + v , px, py, pz 
			VertexColor s, p\Vertex + v , p\r , p\g , p\b , .75 * p\Life
		Next

	Next

End Function

;==========================================================
;==========================================================
;==========================================================

Function BONUSupdate()
	
	If BONUS\timer = 0 
		HideEntity BONUS\Model
		If Rand(60)=1
			BONUS\ID = Rand(1,8)
			BONUS\timer = 150
			bx = ( Rand(0,1)*2-1 ) * ( 5 + Rand(0,3) * 10 )
			bz = ( Rand(0,1)*2-1 ) * ( 5 + Rand(0,3) * 10 )
			PositionEntity BONUS\Model,bx,3,bz 
			ShowEntity BONUS\Model
		EndIf
	EndIf
	
	If BONUS\timer > 0
		BONUS\timer = BONUS\timer - 1
		alpha# = 1.0
		If BONUS\timer <= 30 alpha# = Float( BONUS\timer)*.033
		If BONUS\timer > 120 alpha# = Float(150.0 - BONUS\timer ) * .033
		EntityAlpha BONUS\Model,alpha
		TurnEntity BONUS\Model,0,10,0
	EndIf

End Function

;==========================================================
;==========================================================
;==========================================================

Function AI ( t.tank_type )

	t\MoveCounter = t\MoveCounter - (t\MoveCounter > 0 )
	t\FireCounter = t\FireCounter - (t\FireCounter > 0 )
	FireRange# =  ( BulletLife * t\BulletSpeed )
	MINEactivate = ( t\MineCounter = 1 And Rand(60)=1 ) 
	
	If t\Target > -1 
		TargetDistance# = EntityDistance( t\Model, tank(t\target)\Model )
		If tank(t\target)\respawn > 0 t\Target = -1
	Else
		TargetDistance# = 1000.0
		t\FireCounter = 0
	EndIf
	
	;find a new target
	If t\FireCounter = 0	
		FleeX# = 0
		FleeZ# = 0	
		For c.tank_type = Each tank_type
			If t<>c 
				FleeX = FleeX + EntityX(c\Model) * .33
				FleeZ = FleeZ + EntityZ(c\Model) * .33
				CheckDistance# = EntityDistance( t\Model, c\Model ) 
				If c\Respawn = 0 And CheckDistance < TargetDistance 
					t\Target = c\ID
					TargetDistance = CheckDistance 
				EndIf
				
				;Detonate the mine?
				If c\Respawn = 0 And t\MineCounter > 1 
					If EntityDistance( t\mine, c\Model ) < Rand(5, 20) And EntityDistance( t\mine, t\model) > 20
						MINEactivate = True
					EndIf
				EndIf
				
			EndIf
		Next
		t\FireCounter = 20
	EndIf
			
	;shoot at target if within range
	If t\Target > -1 And (TargetDistance < FireRange+10 ) And ( t\Cooler > ( Rand(2,8) *  t\BulletHeat ) )
		c.tank_type = tank(t\Target)
		time# = TargetDistance / t\BulletSpeed
		ox# = c\MaxSpeed * time
		oz# = c\MaxSpeed * time
		PositionEntity Pivot,EntityX(c\Model) + Rnd(-ox,ox) , 0, EntityZ(c\Model) + Rnd(-oz,oz)
		Distance# = NORMALxz ( EntityX( Pivot) - EntityX(t\Model) , EntityZ(Pivot) - EntityZ( t\Model) )
		t\FX = NX
		t\FZ = NZ
	Else
		t\FX = 0
		t\FZ = 0
	EndIf			
	
	;move	
	If t\MoveCounter = 0
	
		MoveRange# = t\MaxSpeed * 10.0 
		BonusRange# = MoveRange * 10 * (BONUS\timer > 0 )
		BonusDistance# = EntityDistance(t\Model,BONUS\Model) 	
									
		;decide on action
		If t\Target > -1 
			Action = AI_Straffe
			PositionEntity AIPivot, EntityX( tank(t\Target)\Model ), 0, EntityZ( tank(t\Target)\Model )
			If TargetDistance >  ( FireRange + 10.0) Action = AI_Attack
			If TargetDistance < ( FireRange - 10.0 ) Action = AI_Retreat
		Else
			Action = AI_Flee
			PositionEntity AIPivot, FleeX, 0, FleeZ
		EndIf
		If BonusDistance <  BonusRange
			Action = AI_GoForBonus
			PositionEntity AIPivot, EntityX( BONUS\Model ), 0, EntityZ( BONUS\Model )
		EndIf
		Distance# = NORMALxz( EntityX( AIPivot ) - EntityX( t\Model), EntityZ( AIPivot ) - EntityZ( t\Model ) ) 
		If Action = AI_Attack Or Action = AI_GoForBonus Distance# = 1000.00
		If Action = AI_Flee Or Action = AI_Retreat Distance# =0.0
		
		;check possible moves			
		choice = -1
		angle = Rand( 0,7 )
		For l = 0 To 7
			angle = (angle + 1 ) Mod 8
			vx# = NX * Cos( angle*45) - NZ * Sin( angle*45)
			vz# = NZ * Cos( angle*45) + NX * Sin(angle*45)
			PositionEntity pivot, EntityX(t\Model)	+ vx * MoveRange , 0, EntityZ( t\Model) + vz * MoveRange
			unused = LinePick ( EntityX(t\Model) , 3 , EntityZ(t\Model) , vx * MoveRange, 0 , vz * MoveRange , 2 )
						
			;If Nothing blocking route
			If PickedEntity() = 0
			
				;Is there a live mine near New point?
				NoGo = False		
				For c.tank_type = Each tank_type
					If c <> t And c\MineCounter > 1 
						If EntityDistance( pivot, c\Mine ) < Rand(5,10) NoGo = True
					EndIf
				Next

				If NoGo = False
					CheckDistance# = EntityDistance( pivot, AIPivot )
					Select Action
					Case AI_Attack, AI_GoForBonus
						If CheckDistance < Distance
							Distance = CheckDistance
							choice = angle
						EndIf
					Case AI_Flee, AI_Retreat
						If CheckDistance > Distance
							Distance = CheckDistance
							choice = angle
						EndIf
					Case AI_straffe
						If angle = 2 Or angle = 6 choice = angle 
					End Select
				EndIf
						
			EndIf
	
		Next

		t\MoveCounter = 10

		If choice > -1
			;found a move
			t\MX = NX * Cos( choice * 45) - NZ * Sin( choice*45)
			t\MZ = NZ * Cos( choice * 45 ) + NX * Sin( choice*45)
		Else
			;no move
			choice = Rand(3,5)
			t\MX = NX * Cos( choice * 45) - NZ * Sin( choice*45)
			t\MZ = NZ * Cos( choice * 45 ) + NX * Sin( choice*45)
		EndIf
			
	EndIf
		
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function FADE ( target# )

	direction# = Sgn(target - FadeStatus )

	Repeat
		FadeStatus = LIMIT( FadeStatus + direction * FadeSpeed , 0.0,1.0)
		EntityAlpha fade, (1.0 - FadeStatus)
		RenderWorld()
		Flip
	Until ( FadeStatus >= target And direction = 1) Or (FadeStatus <= target And direction= -1) Or (direction=0)
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function NORMALxz#( jx# , jz# )

	speed# = Sqr ( jx * jx + jz * jz )
	NX = jx / speed
	NZ = jz / speed

	Return speed

End Function

;==========================================================
;==========================================================
;==========================================================

Function HIT ( entity, r, g, b, Mark, Sound, IsTANK )

	SOUNDplay( Sound )
	If IsTANK 
		PARTICLEnew ( entity, 0,0,0, 0,0,.5,.5,0,0, r,r,g,g,b,b, .4,.6, 1 , .01 , 2, 33 )
		PARTICLEnew( entity, 0,0,0, -.25,.25, -.5,.5, -.5,0 , 255,255,128,255,0,0, .25,.5, .5, .02, 3)
	Else
		PARTICLEnew( entity, 0,0,0, -.25,.25, -.5,.5, -.5,0 , 128,192,128,192,128,192, .2,.3, .5, .01, 3)	
	EndIf
		
	If Mark PARTICLEnew( entity, 0,0,-1, 0,0, 0,0, 0,0 ,16,48,16,48,16,48 ,.2,.3, 0, .05, 1)

End Function

;==========================================================
;==========================================================
;==========================================================

Function EXPLOSION( entity, range#, ID ,r,g,b, IsTank )

	;do explosion
	HideEntity entity
	SOUNDplay( SOUNDexplode )
	PARTICLEnew( entity, 0,0,0, -.25,.25, .5,1.5, -.25,.25 , r,r,g,g,b,b, .5,.75,1, .005,10)
	PARTICLEnew( entity, 0,0,0, -.25,.25, .5,1.5, -.25,.25 , 255,255,128,255,0,0, .25,.5, 1, .01,10)
	PARTICLEnew ( entity, 0,0,0, 0,0,.25,.5,0,0, 255,255,255,255,255,255, .75,1, .5 , 0 , 16, range )
	
	;if tank destroyed start respawn & updatescore for the tank that was responsible
	
	If IsTank
		t.tank_type = Object.tank_type( EntityName( entity ) )
		t\Shield = 1
		t\Respawn = 1
		If t\MineCounter > 1 
			EXPLOSION ( t\Mine, 20, t\ID, t\r,t\g,t\b, False )
		Else
			HideEntity t\Mine
		EndIf
			
		TANKbonus ( t )
		c.tank_type = tank( ID )
		If c<>t 
			PARTICLEnew( entity, 0,3,0, 0,0, .25,.25,0,0 , c\r,c\r,c\g,c\g,c\b,c\b, 1.5,1.5, 0, .01,1)
			SCOREupdate( c , 1 )
		Else
			SCOREupdate( c, -1 )
		EndIf
	EndIf
	
	;check tanks
	For t.tank_type = Each tank_type
		If t\Life > 0 
			distance# = EntityDistance( t\Turret, entity )
			If distance < range
				damage# = ( ( range - distance ) / range ) * (1.0-t\Shield)
				t\Power = LIMIT( t\Power - damage*.25,0.0,1.0)
				t\Life = Limit (t\Life - damage, 0 , 1 )
				If t\Life = 0 EXPLOSION( t\Turret, 20, ID, t\r,t\g,t\b, True )
			EndIf
		EndIf
	Next
	
	;check drums
	For d.drum_type = Each drum_type
		If d\Life > 0
			distance# = EntityDistance( d\Model, entity )
			If distance  < range
				damage# = ( ( range - distance ) / range ) 
				d\Life = Limit (d\Life - damage, 0 , 2 )
				If d\Life = 0 EXPLOSION ( d\Model, 20 , ID, 168,116,100, False )
			EndIf
		EndIf
	Next
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKbonus ( t.tank_type , bonus=0 )

	Select bonus
		Case 0
			t\BulletReload = 9
			t\BulletSpeed = 1.25 
			t\MaxSpeed = .4 
			t\Shield = 0.0
			EntityAlpha t\BubbleShield, 0
			t\Damage = .1 
			t\LifeRecharge = .002
			t\BulletSpread=0
			t\MineCounter = 0
		Case 1
			t\BulletReload =LIMIT(  t\BulletReload - 3, 3,9 ) 
		Case 2
			t\BulletSpeed = LIMIT( t\BulletSpeed + .25,  1.25, 1.75 )
		Case 3
			t\MaxSpeed = Limit ( t\MaxSpeed + .1, .4, .6)
		Case 4
			t\Shield = Limit ( t\Shield + .25, 0, .75 )
			EntityAlpha t\BubbleShield, t\Shield*.5
		Case 5
			t\Damage = Limit ( t\Damage + .05, .1,.2 )
		Case 6
			t\LifeRecharge = LIMIT( t\LifeRecharge + .002, .002, .01 )
		Case 7
			t\BulletSpread = LIMIT( t\BulletSpread+1,0,2 )
		Case 8
			If t\MineCounter = 0 
				t\MineCounter = 1
				ShowEntity t\Mine
				EntityParent t\Mine, t\Model
				PositionEntity t\Mine, 0,0,-2.5
			EndIf
	End Select
	
	t\BulletHeat# = .025 * ( (  t\BulletSpread + 1.0 ) + ( t\Damage * 10.0 ) + ( t\BulletSpeed / 1.25 ) )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKrespawn( t.tank_type )

	t\Respawn = t\Respawn + 1
	
	;stage 0 - smoking
	If t\Respawn < 91
		PARTICLEnew( t\Model, 0,1,0, -.1,.1, .2,.3, -.1,.1 , 64,80,64,80,64,80, .25,.5, 0,.02,1)
	EndIf
	;stage 1 - show BubbleShield									
	If t\Respawn > 90 And t\Respawn <= 120 
		scale# = Float(t\Respawn - 91) / 29.0
		EntityAlpha t\BubbleShield, scale*.5
	EndIf
	;stage 2 - scale Turret, fill energy
	If t\Respawn > 120 And t\Respawn <= 150
		ShowEntity t\Turret
		scale# = Float(t\Respawn - 121) / 29.0
		t\Life = scale
		t\Power = Limit (t\Power + .04, 0.0,1.0) 	
	EndIf
	;stage 3 - hide BubbleShield
	If t\Respawn > 150 And t\Respawn <= 180
		scale# = Float(t\Respawn - 151) / 29.0
		EntityAlpha t\BubbleShield, .5 - scale*.5 
	EndIf
	;stage 4 - back in play
	If t\Respawn = 180 
		t\Respawn = 0
		If t\Shield = 1 t\Shield = 0
	EndIf
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function MINEnew( t.tank_type , action )

	Select action
	Case 0	;drop mine
		EntityParent t\Mine,0 
		t\MineCounter = 2
	Case 1	;detonate mine 
		EXPLOSION ( t\Mine, 20 , t\ID , t\r,t\g,t\b , False )
		t\MineCounter = 0
	End Select
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function BULLETnew( t.tank_type )

	angle# = t\BulletSpread * 5
	SOUNDplay( SOUNDlaser ) 
	t\Cooler = Limit ( t\Cooler - t\BulletHeat , 0, 1.0)
	PARTICLEnew( t\Turret, 0,0,3 , -.25,.25, -.25,.25, 0.05,0.1,  255,255,255,255,255,255, .25,.25, 0, .04,t\BulletSpread+1) 
	
	For fired = -Sgn( t\BulletSpread ) To Sgn( t\BulletSpread ) 
		If t\BulletSpread <>1 Or fired <> 0 
			b.bullet_type = bullet( NextBullet)
			EntityParent b\Model,t\Turret
			EntityColor b\Model,t\r,t\g,t\b
			PositionEntity b\Model,0,0,3
			RotateEntity b\Model,0,fired*angle,0
			scale# = t\Damage*10.0
			ScaleEntity b\Model,1,1,scale
			EntityParent b\Model,0
			ShowEntity b\Model
			b\Life = BulletLife
			b\ID = t\ID
			b\speed = t\BulletSpeed
			b\Damage = t\Damage
			t\BulletTimer = t\BulletReload
			NextBullet = ( NextBullet + 1 ) Mod bullets
		EndIf
	Next
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLEnew( mesh, x#,y#,z#, vx1#,vx2#, vy1#,vy2#, vz1#,vz2#, r1,r2, g1,g2, b1,b2 , s1#,s2# , w#, f#, number, range#=0)

	random# = Rand( -180,180 )

	For l = 1 To number
		p.particle_type = particle( NextParticle)
		If range = 0 
			TFormVector x,y,z, mesh, 0 
		Else
			angle# = 360 * l / number + random
			TFormVector Cos(angle),y,Sin(angle), mesh,0
			f# = 1.0 / ( range * 3.0)
		EndIf	
		p\x = EntityX(mesh,1)+TFormedX()
		p\y = EntityY(mesh,1)+TFormedY()
		p\z = EntityZ(mesh,1)+TFormedZ()
		If range = 0 
			TFormVector Rnd(vx1,vx2),Rnd(vy1,vy2),Rnd(vz1,vz2), mesh, 0
		Else
			TFormVector Cos(angle)*.5,Rnd(vy1,vy2),Sin(angle)*.5,mesh,0
		EndIf
		p\vx = TFormedX() 
		p\vy = TFormedY() 
		p\vz = TFormedZ() 
		p\r = Rand(r1,r2) 
		p\g = Rand(g1,g2) 
		p\b = Rand(b1,b2)
		p\size = Rnd(s1,s2)
		p\weight = w
		p\fade = f
		p\Life = 1.0
		NextParticle = ( NextParticle + 1 ) Mod particles
	Next

End Function 

;==========================================================
;==========================================================
;==========================================================

Function SCOREupdate( t.tank_type , add#=0 )

	t\Score = t\Score + add
	If t\Score = KILLS And GAMEOVER = 0 GAMEOVER = t\ID+1
	SetBuffer TextureBuffer (t\ScoreTexture)
	Color 0,0,0:Rect 0,0,32,32
	Color t\r,t\g,t\b:Text 16,16,t\Score,1,1
	SetBuffer BackBuffer()
	
End Function 

;==========================================================
;==========================================================
;==========================================================

Function GAMEend()

	Winner = tank(GAMEOVER-1)\Model
	CAMERApoint( EntityX(Winner),EntityY(Winner),EntityZ(Winner), 36.75 , 150 , .1 )
	CAMERApoint( 0,0,0, 6.75 , 60 , .1 )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEstart()

	MENUshow( MENU(0) )
	FADE( 0 )
	GAMEreset ()
	FADE( 1 )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function CAMERApoint( x#,y#,z#, zoomtarget#, iterations, smooth#, show =True)

	PositionEntity Pivot,x,y,z
		
	For l = 1 To iterations
		TurnEntity camera, DeltaPitch(camera, pivot)*smooth, DeltaYaw( camera, pivot)*smooth, 0
		zoom = zoom + Sgn( zoomtarget - zoom ) 
		CameraZoom camera,  zoom 
		If show
			WaitTimer (FrameTimer)
			PARTICLESupdate()
			RenderWorld()
			Flip
		EndIf
	Next

End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEreset( )

	;tanks
	For t.tank_type = Each tank_type
		t\Score = 0
		t\BulletTimer=0
		t\Life = 1.0
		t\Power = 1.0
		t\Cooler = 1.0
		t\Respawn = 0
		t\Control = ( t\ID < PLAYERS ) + 2 * ( t\ID >=PLAYERS )
		x# = ( (t\ID=0 Or t\ID=2) - (t\ID=1 Or t\ID=3) )*30
		z# = ( (t\ID=1 Or t\ID=2) - (t\ID=0 Or t\ID=3) )*30
		PositionEntity t\Model,x,3,z
		PointEntity t\Model,ARENA\Model:TurnEntity t\Model,0,180,0
		RotateEntity t\Turret,0,EntityYaw(t\Model),0
		ResetEntity t\Model
		PositionEntity t\Turret,x,3,z
		ShowEntity t\Turret:ScaleEntity t\Turret,1,1,1
		ShowEntity t\Model
		SCOREupdate (t)
		TANKbonus (t)
		ScaleEntity t\LifeBar,t\Life,1,1
		ScaleEntity t\PowerBar,t\Power,1,1
		HideEntity t\Mine
		t\FX = 0:t\FZ = 0
		t\MX = 0:t\MZ = 0
		t\MoveCounter = t\ID * 2 
		t\FireCounter = t\ID * 2
		t\Target = -1
	Next
	
	;bullets
	For w.bullet_type = Each bullet_type
		HideEntity w\Model
		ResetEntity w\Model
	Next
		
	;drums
	For b.drum_type = Each drum_type
		x# = Cos( -22.5 + b\ID *45.0) * 27
		z# = Sin(-22.5 + b\ID * 45.0) * 27
		b\Life = 2.0
		PositionEntity b\Model,x,2,z
		ResetEntity b\Model
		ShowEntity b\Model
	Next
		
	;particles
	For p.particle_type = Each particle_type
		p\Life = 0
	Next
	PARTICLESupdate()
		
	;bonus
	HideEntity BONUS\Model
	BONUS\timer = 0
		
	;misc	
	zoom# = 6.75
	GAMEOVER = 0
	
	;Gun Target Reset
	If ( Not JOYSTICK ) 
		ShowEntity GunTarget
		MoveMouse HGW, HGH
	Else
		HideEntity GunTarget
	EndIf
	
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function LIMIT#( q# , low# , hi# )

	If q < low q = low
	If q > hi q = hi
	Return q
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function SOUNDplay ( sound )
	If SOUNDon 
		new_channel = PlaySound( sound )
	EndIf
End Function

;==========================================================
;==========================================================
;==========================================================

Function MENUshow ( m.menu_type )
	
	;reset
	m\current = m\initial
	MENUupdate( m )
	
	;fade in	
	ShowEntity m\model
	For l=  0 To 30
		RotateEntity m\model, 0,0,l*12
		PositionEntity m\model,0,0,2+(30-l)
		RenderWorld()
		Flip
	Next		
	
	Repeat
	
		;user input
		If JOYSTICK
			jx = JoyXDir()
			jy = JoyYDir()
			jb = JoyDown(3)
		Else
			jx = KeyDown( KRIGHT ) - KeyDown( KLEFT )
			jy = KeyDown( KDOWN) - KeyDown( KUP )
			jb = KeyDown( KSELECT)
		EndIf
		If KeyDown(1) End
				
		;change current option
		If jy <> 0 
			old = m\current
			m\current = Limit (m\current + jy, 0, m\options-1 )
			jx=0:jy = (old <> m\current)
		EndIf
		
		;change current sub option
		If jx <> 0  
			old = m\sub_current[m\current]
			m\sub_current[m\current] = Limit ( m\sub_current[m\current] + jx , 0, m\sub_options[m\current]-1 )
			jx = ( old <> m\sub_current[m\current] )
		EndIf
		
		;update menu
		If (jx+jy) <> 0 
			SOUNDplay( SOUNDmenu)
			If m\sub_current[2] = 0 m\sub_current[0] = LIMIT ( m\sub_current[0], 0, 1 )
			MENUupdate ( m )
		EndIf
		
		RenderWorld()
		Flip

	Until m\sub_options[m\current] = 1 And jb

	;fade out
	For l=  0 To 30
		RotateEntity m\model, 0,0,l*12
		PositionEntity m\model,0,0,2+l
		RenderWorld()
		Flip
	Next	
	HideEntity m\model

	;get details
	Select m\ID
	Case 0
		PLAYERS = m\sub_current[0]
		KILLS = ( m\sub_current[1] + 1 ) * 10 
		JOYSTICK = ( m\sub_current[2] = 1 )
	Case 1
		QUIT = ( m\current = 1)
	End Select

End Function

;==========================================================
;==========================================================
;==========================================================

Function MENUupdate(m.menu_type, update_title=False )

	SetBuffer TextureBuffer(m\texture)
	
	If update_title
		Color 64,64,128:Rect 0,0,256,128,1 
		MyText( 128,12,m\title, 1,1, 255,255,0 )
	Else
		Color 64,64,128:Rect 0,28,256,88,1
	EndIf
		
	y = 22 + ( m\options = 2 ) * 48
		
	For option = 0 To m\options - 1
	
		r = 255
		g = 155+100*(m\current = option)
		b = 155+100*(m\current = option)
		x = 8 + 120 * ( m\sub_options[option]=1 )
		
		MyText ( x, y + option * 24, Left$( m\sub_string[option], 12), m\sub_options[option]=1,0, r,g,b )
		
		If m\sub_options[option] > 0
			MyText ( 136, y + option * 24 , Mid$(m\sub_string[option], ( m\sub_current[option] + 1)*12,12 ), 0,0, r,g,b )
		EndIf
	Next
	
	SetBuffer BackBuffer()
		
	Delay 100
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function MyText ( x, y, t$, cx, cy, r, g, b )

	Color 0,0,0
	For l = 0 To 4
		px = ( ( l = 0) - (l = 2) ) * 1
		py = ( ( l = 1) - (l = 3) ) * 1
		If l= 4 Color r,g,b
		Text x+ px , y+ py , t$ , cx , cy
	Next

End Function

;==========================================================
;==========================================================
;==========================================================
;==========================================================
; THESE FUNCTIONS ARE ONLY USED ONCE
;==========================================================
;==========================================================
;==========================================================
;==========================================================

Function MENUinit()

	Restore menu_data
	
	For l = 0 To menus-1
		m.menu_type = New menu_type
		menu( l ) = m
		m\ID = l
		w# = .5
		h# = .25
		m\model = QUADnew(menu_cam,-90, w,h,1 )
		frame = CYLINDERnew( 3, 128,128,128, 0,0,90, .01,w,.01, 0,h+.01,0 )
		CYLINDERnew( 3, 128,128,128, 0,0,90, .01,w,.01, 0,-(h+.01),0, frame )
		CYLINDERnew( 3, 128,128,128, 0,0,0, .01,h,.01, -(w+.01),0,0, frame )
		CYLINDERnew( 3, 128,128,128, 0,0,0, .01,h,.01, w+.01,0,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,-45, .04,.02,.04, w,h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,-45, .04,.02,.04, -w,-h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,45, .04,.02,.04, w,-h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,45, .04,.02,.04, -w,h,0, frame )
		EntityParent frame, m\model
		PositionEntity frame, 0,0,0 
		PositionEntity m\model,0,0,2
		HideEntity m\model
		m\texture = CreateTexture (256,128, 16+32+1)
		EntityTexture m\model, m\texture
		Read m\title
		Read m\options
		Read m\initial
		For option = 0 To m\options-1
			Read m\sub_options[option]
			Read m\sub_string[option]
			m\sub_current[option] = ( m\sub_options[option] > 1 )
		Next
		MENUupdate( m, True )
	Next	
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEinit()

	;light
	RotateEntity light,30,-60,0 
			
	;camera	
	PositionEntity camera,0,250,-250 
	CameraZoom camera, zoom
	CAMERApoint( 0,0,0, 6.75, 1 , 1 , False )
	fade = QUADnew( camera , -90 )
	EntityColor fade,0,0,0
	PositionEntity fade,0,0,6.75
	FADE(0)
	
	;menu camera
	PositionEntity menu_cam,-25000,0,0
	CameraClsMode menu_cam,False,True
	PositionEntity menu_cam,65000,65000,65000
	EntityOrder menu_cam,-999
		
	;bonus
	BONUS\Model = CYLINDERnew(3,255,255,0, -90,0,0, 1,.5,1, 0,0,0, 0,  1,1, 1,1,0 )
						
	;BubbleShield
	BubbleMesh = CYLINDERnew( 3,128,128,128, 0,0,0, 3,2,3, 0,-1,0 ,0, 1,1, 1,1, 0)
	HideEntity BubbleMesh
	EntityShininess BubbleMesh,1
	
	;bullets
	BulletMesh = CYLINDERnew( 6, 128,128,128, -90,0,0, .25,.375,.25, 0,0,0 ,0, 1,2, 1,1, 0, 1)
	HideEntity BulletMesh
	EntityType BulletMesh,T_BULLET
	EntityRadius BulletMesh,.5
	For b=0 To bullets-1
		bullet(b) = New bullet_type
		bullet(b)\Model = CopyEntity ( BulletMesh )
		bullet(b)\Life = 0
		HideEntity bullet(b)\Model
	Next
	
	;tanks
	For l = 0 To tanks-1
		tank(l) = New tank_type
		tank(l)\ID = l
		tank(l)\r = 100+84*(l=1)+42*(l=3)
		tank(l)\g = 100+84*(l=2)+42*(l=3)
		tank(l)\b = 100+84*(l=0)
		TANKinit ( tank(l) )
	Next
	
	;drums
	DrumMesh = CYLINDERnew( 3, 168,116,100, 0,0,0, 1,2,1, 0,0,0 )
	HideEntity DrumMesh
	For l=0 To drums-1
		drum(l) = New drum_type
		drum(l)\Model = CopyEntity ( DrumMesh )
		drum(l)\ID = l
		EntityType drum(l)\Model,T_DRUM
		EntityRadius drum(l)\Model,1.5,2  
		NameEntity drum(l)\Model,Handle(drum(l)) 
		EntityPickMode drum(l)\Model, 1
	Next

	;sounds
	If SOUNDon
		SOUNDclang = LoadSound("Clang.wav")
		SOUNDlaser = LoadSound("Laser.wav")
		SOUNDhit = LoadSound("Hit.wav")	
		SOUNDexplode = LoadSound("Explode.wav")
		SOUNDbonus = LoadSound("Bonus.wav")
		SOUNDmenu = LoadSound("Menu.wav")
	EndIf
	
	;GunTarget
	GunTarget = CreateSphere()
	EntityColor GunTarget,50,20,20
	EntityBlend GunTarget,3
	ScaleEntity GunTarget,2,2,2
	HideEntity GunTarget
	
	ARENAinit()
	PARTICLEinit()	
	MENUinit()
	GAMEreset ()
			
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLEinit()

	ParticleTemplate = CYLINDERnew(6, 0,0,0, 0,30,0, 1,.75,1, 0,0,0	, 0, 2,2, 1,1, 2, 1)
	ParticleMesh = CreateMesh()
	EntityFX ParticleMesh,32+2
	EntityShininess ParticleMesh,1
	s= CreateSurface( ParticleMesh )
	gs = GetSurface(ParticleTemplate,1)
	cv = CountVertices ( gs )
	ct = CountTriangles ( gs )
	For l= 0 To particles-1
		vs= l * cv
		particle(l) = New particle_type
		particle(l)\Vertex = vs
		For v=0 To cv-1
			v0 = AddVertex(s,0,0,0)
			VertexNormal s,v0,VertexNX(gs,v),VertexNY(gs,v),VertexNZ(gs,v) 
		Next
		For t=0 To ct - 1
			AddTriangle s, TriangleVertex( gs,t,0)+vs,TriangleVertex( gs,t,1)+vs,TriangleVertex( gs,t,2)+vs
		Next 
	Next
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function ARENAinit()

	Ground=QUADnew( 0, 0, 45,1,45)
	EntityOrder Ground,1
	EntityColor Ground,0,0,0
	EntityBlend Ground,3 
	;EntityAlpha Ground,.75
	
	Plane = CreatePlane()
	EntityColor Plane,0,0,0 
	PositionEntity Plane,0,-.5,0
	EntityAlpha Plane,.5 ;75
	EntityOrder Plane,1
	Mirror = CreateMirror()
	PositionEntity Mirror,0,-.1,0
	Texture=CreateTexture(64,64)
	SetBuffer TextureBuffer(Texture)
	For y=0 To 1:For x=0 To 1
		If ( x + y ) Mod 2 = 0 Color 128,32,128 Else Color 64,16,64 
		Rect x*32,y*32,32,32,1
	Next:Next
			
	SetBuffer BackBuffer()
	ScaleTexture Texture,1.0/9.0,1.0/9.0
	TextureBlend Texture,3	
	EntityTexture Ground,Texture
	FreeTexture Texture
				
	part = CYLINDERnew( 3, 142,100,142, 0,0,0, 2,3,2, -45,3,45 )
	CYLINDERnew( 3, 142,100,142, 90,0,0, 2,3,2, -45,3,0 ,part)
	CYLINDERnew( 3, 128,128,128, 90,0,0, .5,20.5,.5  ,-45,4,-22.5  ,part)
	CYLINDERnew( 3, 128,128,128, 0,0,90, .5,20.5,.5  ,-22.5,4,-45  ,part)
	CYLINDERnew( 3, 128,128,128, 90,0,0, .5,20.5,.5  ,-45,2,-22.5  ,part)
	CYLINDERnew( 3, 128,128,128, 0,0,90, .5,20.5,.5  ,-22.5,2,-45  ,part)
	CYLINDERnew(3, 128,128,128, 90,0,0, .5,4,.5, -20,4.5,25, part)
	CYLINDERnew(3, 128,128,128, 90,0,0, .5,4,.5, -20,1.5,25, part)
	CYLINDERnew(3, 128,128,128, 0,0,90, .5,4,.5, -25,4.5,20, part)
	CYLINDERnew(3, 128,128,128, 0,0,90, .5,4,.5, -25,1.5,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -20,3,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -30,3,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -20,3,30, part)
	ARENA\model = CYLINDERnew( 3,142,100,142, 0,0,0 ,3,2,3, 0,2,0 ,0, 1,1, 1,.5 )
	ARENA\radar = CYLINDERnew( 3,128,128,128,0,0,0,.5,1,1,0,5,0, 0,1,1 )
	CYLINDERnew( 3,142,100,142,90,0,0,2,2,2,0,8,0, ARENA\radar,2,2, 1, 1 )
	
	EntityType ARENA\Model,T_ARENA
	EntityPickMode ARENA\Model,2
	For l=0 To 3
		temp = CopyMesh(part):RotateMesh temp,0,l*90,0
		AddMesh temp,ARENA\Model:FreeEntity temp
		px = 39*( ( l=3 ) - ( l = 0) ) + 13 * ( (l = 2) - ( l = 1) )
		ARENA\hud = CYLINDERnew( 3, 96,96,96, 0,0,90, .25,8,.25, px,13,45 )
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, 2,.5,1, px - 8.5,12,45, ARENA\hud)
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, 2,.5,1, px + 8.5,12,45, ARENA\hud )
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, .2,8,.2, px ,10.5,45, ARENA\hud )
	Next
	FreeEntity part
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKinit( t.tank_type )
	
	;Turret
	t\Turret = CYLINDERnew( 6 , 128,128,128 , 0,0,0 , 1,.5,1 , 0,0,0 )
	;ariel
	CYLINDERnew( 4 , t\r,t\g,t\b , 0,0,0 , .1,1,.1 , .5,1,-.5 , t\Turret)
	;hatch
	CYLINDERnew( 4 , t\r,t\g,t\b , 0,0,0 , .3,.25,.3 , -.25,.5,.25,  t\Turret)
	;barrel
	CYLINDERnew( 4 , 128,128,128 , 90,0,0 , .25,.5,.25 ,0,0,1.5, t\Turret )
	;nosel
	CYLINDERnew( 6 , t\r,t\g,t\b , 90,0,0 , .5,.5,.5 , 0,0,2.5 , t\Turret ,0,1 , .75,1)
	;Turret base
	 CYLINDERnew( 6, t\r,t\g,t\b , 0,0,0 , 1.5,.25,1.5 , 0,-.75,0 , t\Turret )
	;Base
	t\Model = CYLINDERnew( 6, 128,128,128 , 0,0,0 , .75,.5,1.75 , 0,1.5,-.5 )
	;Exhaust
	CYLINDERnew( 6, t\r,t\g,t\b , 90,0,0 , .25,.25,.25 , 0,1.5,-2.5 , t\Model ,1,0 )
	EntityRadius t\Model,2.75
	EntityPickMode t\Model, 1
	EntityType t\Model,T_TANK
	;tracks
	For x# = -1.25 To 1.25 Step 2.5
		CYLINDERnew( 6, 64,64,64 , 0,0,90 , 1,.5,2 , x ,1,0 , t\Model ,0,0 )
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .5,.5,.5 , x,1,0 , t\Model ,1,1)
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .4,.4,.4 , x,1,-1 , t\Model ,1,1)
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .4,.4,.4 , x,1,1 , t\Model ,1,1)
	Next
	PositionMesh t\Model,0,-3,0
	NameEntity t\Model,Handle(t)
	NameEntity t\Turret,Handle(t) 
	
	;mine model
	t\Mine = CYLINDERnew( 6 , 128,128,128 , 0,0,0 , 1,.1,1 , 0,-2.8,0 )
	CYLINDERnew( 6 , t\r,t\g,t\b , 0,0,0 , .5,.1,.5 , 0,-2.6,0, t\Mine )
		
	;bubble shield for respawn
	t\BubbleShield = CopyEntity (BubbleMesh, t\Model)
	EntityColor t\BubbleShield,t\r,t\g,t\b
			
	;hud
	px = 39*( ( t\ID=3 ) - ( t\ID = 0) ) +13 * ( (t\ID = 2) - ( t\ID = 1) )
	t\LifeBar = CYLINDERnew( 3, t\r,t\g,t\b, 0,0,90, 1,8,1, 0,0,0 )
	PositionEntity t\LifeBar,px,13,45
	t\PowerBar = CYLINDERnew( 3, t\r*.5,t\g*.5,t\b*.5, 0,0,90, .5,8,.5, 0,0,0 )
	PositionEntity t\PowerBar,px,13,45
	t\CoolerBar = CYLINDERnew( 3, t\r*.75,t\g*.75,t\b*.75, 0,0,90,.5,8,.5,0,0,0 )
	PositionEntity t\CoolerBar,px,10.5,45
	ScoreMesh = QUADnew(Plane,-90,2.5,2.5,1)
	PositionEntity ScoreMesh,px,7.5,45
	t\ScoreTexture = CreateTexture(32,32)
	EntityTexture ScoreMesh, t\ScoreTexture
				
End Function

;==========================================================
;==========================================================
;==========================================================

Function CYLINDERnew( segs=12 ,r,g,b, rx,ry,rz, sx#,sy#,sz# , px#,py#,pz# ,parent = 0, b_in#=1.0, t_in#=1.0 , b_wi#=1.0, t_wi#=1.0, fx#=2, de#=detail )

	segs = segs * de
	
	mesh = CreateMesh():s = CreateSurface(mesh)
	For y# = -1 To 1 Step 2
	
		w# = b_wi * (y=-1) + t_wi * (y=1)
	
		For v = 0 To segs-1
			a# = Float(v) * ( 360.0 / Float(segs) )
			v0 = AddVertex( s,w*Cos(a),y,w*Sin(a) )
			VertexColor s,v0,r,g,b
		Next
	Next
	
	vb = AddVertex(s,0,b_in,0):VertexColor s,vb,r*b_in,g*b_in,b*b_in 
	vt = AddVertex(s,0,-t_in,0):VertexColor s,vt,r*t_in,g*t_in,b*t_in
	
	For v1 = 0 To segs-1
		v2 = ( v1 + 1) Mod segs
		v3 = ( v2 + segs )
		v4 = ( v1 + segs )
		AddTriangle s,vt,v1,v2
		AddTriangle s,vb,v3,v4 
		AddTriangle s,v3,v2,v1
		AddTriangle s,v1,v4,v3
	Next
	
	ScaleMesh mesh,sx,sy,sz	
	RotateMesh mesh,rx,ry,rz
	PositionMesh mesh ,px,py,pz
	UpdateNormals mesh
	EntityFX mesh,fx
	EntityShininess mesh,1.0
	If fx=0 EntityColor mesh,r,g,b
	If parent > 0 
		AddMesh mesh,parent:FreeEntity mesh
	Else
		Return mesh
	EndIf

End Function

;==========================================================
;==========================================================
;==========================================================

Function QUADnew( parent=0, rx=0 ,sx#=1, sy#=1, sz#=1 )

	mesh=CreateMesh(parent)
	surface=CreateSurface(mesh)
	AddVertex surface,-1,0,1,0,0
	AddVertex surface,1,0,1,1,0
	AddVertex surface,1,0,-1,1,1
	AddVertex surface,-1,0,-1,0,1
	AddTriangle surface,0,1,2
	AddTriangle surface,2,3,0
	RotateMesh mesh,rx,0,0	
	ScaleMesh mesh,sx,sy,sz
	EntityFX mesh,1
		
	Return mesh
End Function	

;==========================================================
;==========================================================
;==========================================================

.menu_data

Data "TANX  by Stevie G"
Data 4, 0 	
Data 3,"Players     Demo        One         Two         "
Data 5,"Kills       10          20          30          40          50          "
Data 2,"Control     Mouse       JoyPad      "
Data 1,"Start"

Data "Paused"
Data 2,0
Data 1,"Continue"
Data 1,"Quit"


I like this. all generated by code = impressive

Joystick only? Boo, hiss!

Can you post some screenshots at least?

@Swift I don't have WebSpace .. sorry! I could e-mail you one?

I think you should add alternate controls. A keyboard for movement and mouse for shooting should work fine - and can't be hard to add.

I, like many others, only have a game pad (Microsoft Sidewinder) and no analogue pad - in fact I would guess there aren't may people who have a game pad of any sort. I only bought mine for testing my own games.

That said I like the idea of creating an entire game through code and the menu looks ok...

Stevie:
That's okay, you don't need to. :-)

Em.. the Menu is terrible but I always hated doing that part of a game!!! Using both Analogues gives me full 360 control of the turret and the tracks - which was the whole idea of the game. I may change this if I get the time ..

Cheers
Stevie

Hey Stevie G,
If you want to post some screenshots, check this out: http://www.zippyimages.com

Stevie G is this public domain, would you mind if I used the code. ?

I wont if its not public domain, but I have to say its bloody good.

Anyway let me know, I wont make a move on it unless you say its ok first.

Awesome game for being procedural!

Imagine what it'd be like with models and textures!

Anyway, the control scheme is hard for my joystick. at first all I could do is drive and adjust the X of the aim. I changed the code a bit to simply use the rudder on my joystick.

@ Electronin - I'll check this out.

@ Yavin - feel free to use any of it mate. I think the coding is pretty solid. If I just get a mention then that's fine by me. Forgot to mention that the difficulty is the same no matter what you choose.

If anyone fancies adding models / textures it'd be interesting to see what it looks like. Never did get round to using a modeller etc..

Stevie

@ Electronin - I can't seem to get a shot to display - just like you??

Oh yea,... this is really similar to something I wanted to do a while back. Take a look at the bottom of the page here:

http://www.redgreenbob.com/proofs/pongo/main.htm

I'd be glad to make the tank available for this. (although this version does not have a seperate turret) Guess I'll make a new one.

This tank was also made available in the free model pack I offered a while back, but I've since taken it down. Maybe a mirror is still around. In anycase, I'll make a new version with turret if anyone wants.

Your page is down Pongo?

Oops.

Nope. I just had a typo,... should be fixed now

Ah.. remember this one now Pongo - it's Gallus!!! This model WAS my inspiration for doing the game - I've got this saved somewhere with all the free tokomak models you gave us :) .

If you can be arsed making a new one with separate turret ? The only problem here is I don't have any texturing skills or aps , hence the use of vertex colors for differentiating each tank. I don't ask for much but if you're making it - could you make 4 with the same parts as mine in vertex primary colours?

Yeah me and MS also started work on something like this hence the reason id like to pick at your code, as its got some nice features in it.

I did a Combat Remake for my Uni project, this is what spurred me onto doing a 3D version in blitz.

I think im now going to finish it all off though as there seems to be some intrest in the genre.

Anyone got any links to the original 'Combat' game? Wouldn't mind a shifty.

I'll make up a new version with turret. I actually already have one, but it was made for a rendering, so I need to take out some detail (around 20K polys right now)

Might take me a few days,... I've got plans for the weekend already

JoyKey 12, what do you expect? Clean it up a bit will ya?

Sorry, it's just a bit frustrating that you make a game that hardly anyone can play and then post it in the forum for the rest of us to glare at.

Here is a keyboard and mouse version, but 2 player doesn't work.
;Initialization
Global key_num=9 ;How many inputs do you need?
Dim ids(1,key_num)
Dim keynames$(key_num)
Global mx_keys
Global my_keys
Global mz_keys
Global last_mouse_x_pos
Global last_mouse_y_pos
Global last_mouse_z_pos
Global last_mouse_x_neg
Global last_mouse_y_neg
Global last_mouse_z_neg
Global last_mouse_x_hitp
Global last_mouse_y_hitp
Global last_mouse_x_hitn
Global last_mouse_y_hitn
Global mouse_factor_x#=1.0
Global mouse_factor_y#=1.0
Global mouse_factor_z#=1.0
Global joystick_factor_x#=1.0
Global joystick_factor_y#=1.0
Dim joystick_minmax#(7)
joystick_minmax(0)=-.08
joystick_minmax(1)=-.8
joystick_minmax(2)=.08
joystick_minmax(3)=.8
joystick_minmax(4)=-.08
joystick_minmax(5)=-.8
joystick_minmax(6)=.08
joystick_minmax(7)=.8
Dim joy_dir_list(3)
;End of initialization

ids(1,0)=205
ids(1,1)=203
ids(1,2)=200
ids(1,3)=208
ids(1,4)=28
ids(1,5)=57
ids(0,6)=3
ids(1,6)=1
ids(0,7)=3
ids(1,7)=0
ids(0,8)=3
ids(1,8)=2
ids(0,9)=3
ids(1,9)=3


Graphics3D 640,480,16,1
SetFont LoadFont("Tahoma",32,1)
SeedRnd MilliSecs()

Const detail# = 10
Const FPS = 30
Const FadeSpeed# = .02
Const Gravity# = 0.05
Const tanks = 4
Const bullets = 100
Const particles = 750
Const drums = 8
Const menus = 2
Const FireTurn# = .25
Const MoveTurn# = .1
Const BulletLife# = 30
Const T_TANK = 1
Const T_ARENA = 2
Const T_DRUM = 3
Const T_BULLET = 4
Const AI_Attack = 0
Const AI_Retreat = 1
Const AI_GoForBonus = 2
Const AI_Flee = 3
Const AI_Straffe = 4
Global SOUNDon = False ; true
Global SOUNDclang
Global SOUNDlaser
Global SOUNDhit
Global SOUNDexplode
Global SOUNDbonus
Global SOUNDmenu
Global camera=CreateCamera()
Global menu_cam = CreateCamera()
Global Pivot = CreatePivot()
Global Light = CreateLight()
Global AIPivot = CreatePivot()
Global FrameTimer = CreateTimer(FPS)
Global BulletMesh
Global ParticleMesh
Global DrumMesh
Global BubbleMesh
Global NextBullet
Global NextParticle
Global ParticleTemplate
Global Fade
Global FadeStatus#
Global NX#
Global NZ#
Global MINEactivate
Global zoom#=6.75
Global GAMEOVER
Global QUIT
Global PLAYERS
Global KILLS
Global DIFFICULTY

Collisions T_TANK,T_TANK,1,2
Collisions T_TANK,T_ARENA,2,2
Collisions T_TANK,T_DRUM,1,2
Collisions T_BULLET,T_ARENA,2,1	
Collisions T_BULLET,T_TANK,1,1
Collisions T_BULLET,T_DRUM,1,1

Type tank_type
	Field ID
	Field Model
	Field Turret
	Field MaxSpeed#
	Field BulletReload
	Field BulletSpeed#
	Field BulletSpread
	Field BulletHeat#
	Field BulletTimer
	Field Shield#
	Field Damage#
	Field Score
	Field ScoreTexture
	Field Respawn
	Field BubbleShield
	Field Life#
	Field LifeBar
	Field LifeRecharge#
	Field Power#
	Field PowerBar
	Field Cooler#
	Field CoolerBar
	Field MineCounter
	Field Mine
	Field Control
	Field r#,g#,b#
	;ai stuff
	Field MoveCounter
	Field FireCounter
	Field MX#, MZ#
	Field FX# , FZ#
	Field Target
End Type

Type bullet_type
	Field ID
	Field Model
	Field Life
	Field Speed#
	Field Damage#
End Type

Type drum_type
	Field ID
	Field Model
	Field Life#
End Type

Type particle_type
	Field Vertex
	Field x#,y#,z#
	Field vx#,vy#,vz#
	Field r,g,b
	Field life#
	Field fade#
	Field size#
	Field weight#
End Type

Type bonus_type
	Field ID
	Field Model
	Field timer
End Type

Type arena_type
	Field Model
	Field Hud
	Field Radar
End Type	

Type menu_type
	Field Model
	Field ID
	Field texture
	Field options
	Field title$
	Field initial
	Field current
	Field sub_options[5]
	Field sub_string$[5]
	Field sub_current[5]
End Type

Dim tank.tank_type(tanks-1)
Dim bullet.bullet_type( bullets -1 )
Dim drum.drum_type ( drums - 1)
Dim particle.particle_type ( particles - 1)
Dim menu.menu_type( menus -1 )
Global BONUS.bonus_type = New bonus_type
Global ARENA.arena_type = New arena_type

Global targetsphere=CreateSphere()
EntityColor targetsphere,50,20,20
EntityBlend targetsphere,3
ScaleEntity targetsphere,2,2,2

GAMEinit()

Repeat
	GAMEstart()
	Repeat
		WaitTimer(FrameTimer)
		BULLETSupdate()
		PARTICLESupdate()
		BONUSupdate()
		TANKSupdate()
		UpdateWorld()
		RenderWorld()
		Flip
	Until QUIT Or GAMEOVER  
	If GAMEOVER GAMEend()
Until (1=2)
		
End

;==========================================================
;==========================================================
;==========================================================

Function TANKSupdate()

	;radar
	TurnEntity ARENA\radar,0,5,0

	For t.tank_type = Each tank_type
	
		t\BulletTimer = t\BulletTimer - ( t\BulletTimer > 0 )
								
		If t\Life > 0 
			
			;control
			If t\Control < 2
				t\MX = down(0)-down(1)
				t\MZ = down(2)-down(3)
;				t\FX = down(6)-down(7)
;				t\FZ = down(8)-down(9)
				MINEactivate = down(5)
			Else
				AI ( t )
			EndIf
				
			;moving
			speed#  = NORMALxz( t\MX , t\MZ ) 
			If speed > 0.3
				PositionEntity Pivot,EntityX(t\Model) + NX * 5 , 0 , EntityZ(t\Model) + NZ * 5
				RotateEntity t\Model,0,EntityYaw(t\Model)+DeltaYaw(t\Model,Pivot) * MoveTurn ,0
				speed = speed * t\MaxSpeed
				TranslateEntity t\Model , NX * speed , (3-EntityY(t\Model)) , NZ * speed 
				PARTICLEnew( t\Model, 0,-1.5,-3, -.1,.1, -.1,.1, -.2,-.1 , 64,80,64,80,64,80, .25,.5, .25 , .1, 1)
			EndIf
			
			;firing	
			speed# = NORMALxz( t\FX , t\FZ )	
			If speed > 0.5 
				PositionEntity Pivot,EntityX(t\Model) + NX * 5 , 0 , EntityZ(t\Model) + NZ * 5
				yaw# = DeltaYaw(t\Turret,Pivot)
				TurnEntity t\Turret,0, yaw *FireTurn ,0
				If t\BulletTimer = 0 And Abs(yaw)< 15  And t\Cooler > t\BulletHeat
					If t\respawn = 0 BULLETnew(  t )
				EndIf 
			EndIf
			If t\Control < 2
				PositionEntity targetsphere,(MouseX()-320)*.145,0,-(MouseY()-240)*.17
				PointEntity t\turret,targetsphere
				RotateEntity t\turret,0,EntityYaw(t\turret),0
				If t\BulletTimer = 0 And Abs(yaw)< 15  And t\Cooler > t\BulletHeat
					If t\respawn = 0 BULLETnew(  t )
				EndIf
			End If
			PositionEntity t\Turret,EntityX(t\Model),3,EntityZ(t\Model),1	
			
			;recharge gun & life		
			t\Life = LIMIT( t\Life + t\LifeRecharge, 0, t\Power )
			t\Cooler = LIMIT( t\Cooler + .0075, 0, 1)
	
			;lay / trigger mine
			If t\MineCounter = 1 And MINEactivate MINEnew( t, 0 )
			If t\MineCounter > 1 t\MineCounter = t\mineCounter + 1
			If t\MineCounter > 30 And MINEactivate MINEnew( t,1 )
															
		EndIf
		
		;bonus pickup
		If BONUS\timer > 0 And EntityDistance( BONUS\Model, t\Model ) < 3
			BONUS\timer = 0
			TANKbonus (t , BONUS\ID )
			PARTICLEnew ( BONUS\Model, 0,0,0, 0,0,.5,.5,0,0, 255,255,255,255,0,0, .25,.5, 1 , .01 , 8, 10 )
			SOUNDplay( SOUNDbonus )
		EndIf
			
		;respawn	
		If t\Respawn > 0 TANKrespawn( t )
		
		;update hud
		ScaleEntity t\LifeBar,t\Life,1,1
		ScaleEntity t\PowerBar,t\Power,1,1
		ScaleEntity t\CoolerBar,t\Cooler,1,1

	Next
	
	;quit
	QUIT = KeyDown(1)
	
	;pause
	If KeyDown(25) MENUshow ( MENU(1) )
		
End Function
	
;==========================================================
;==========================================================
;==========================================================

Function BULLETSupdate()

	For b.bullet_type = Each bullet_type
		
		MoveEntity b\Model,0,0,b\speed
		b\Life = b\Life - (b\Life > 0)
		
		;do collisions
		If CountCollisions(b\Model) > 0
			For ID = 1 To 3
				entity = EntityCollided (b\Model,ID)
				If entity <> 0
					Select ID
						Case T_TANK
							t.tank_type = Object.tank_type( EntityName( entity ) )
							damage# = b\Damage * (1.0 - t\Shield)
							t\Power = LIMIT( t\Power - damage*.25,0.0,1.0)
							t\Life = LIMIT( t\Life - damage,0.0,1.0)
							If t\Life = 0 And t\Respawn = 0
								;destroy tank
								EXPLOSION ( t\Turret, 20 , b\ID, t\r,t\g,t\b, True )
							Else
								;hit tank
								h.tank_type = tank( b\ID )
								HIT ( b\Model, h\r,h\g,h\b, False , SOUNDhit,  True )
							EndIf
						Case T_ARENA
							;hit arena
							HIT ( b\Model, 142,100,142, True, SOUNDclang, False ) 
						Case T_DRUM
							d.drum_type = Object.drum_type( EntityName( entity ) )
							d\Life = LIMIT( d\Life - b\Damage,0.0,2.0)
							If d\Life = 0 
								;destroy drum
								EXPLOSION ( d\Model, 20 , b\ID, 168,116,100, False )
							Else
								;hit drum
								HIT ( b\Model, 168,116,100, True, SOUNDclang, False )
							EndIf	
					End Select
				EndIf
			Next
			b\Life =0 
		EndIf
						
		If b\Life = 0 HideEntity b\Model:ResetEntity b\Model
		
	Next
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLESupdate()

	s = GetSurface( ParticleMesh , 1 )
	t = GetSurface( ParticleTemplate , 1 )
	cv = CountVertices(t)-1
		
	For p.particle_type = Each particle_type
	
		p\Life = LIMIT( p\Life - p\fade ,0 , 1.0 ) 
		
		p\vy = p\vy - Gravity * p\weight
		p\x = p\x + p\vx
		p\y = p\y + p\vy
		p\z = p\z + p\vz
		
		If p\y < p\size 
			p\y = p\size
			p\vx = p\vx * .75
			p\vy = -p\vy*.5
			p\vz = p\vz * .75
		EndIf
		
		For v=0 To cv
			px# = p\x + VertexX(t,v) * p\size
			py# = p\y + VertexY(t,v) * p\size
			pz# = p\z + VertexZ(t,v) * p\size
			VertexCoords s , p\Vertex + v , px, py, pz 
			VertexColor s, p\Vertex + v , p\r , p\g , p\b , .75 * p\Life
		Next

	Next

End Function

;==========================================================
;==========================================================
;==========================================================

Function BONUSupdate()
	
	If BONUS\timer = 0 
		HideEntity BONUS\Model
		If Rand(60)=1
			BONUS\ID = Rand(1,8)
			BONUS\timer = 150
			bx = ( Rand(0,1)*2-1 ) * ( 5 + Rand(0,3) * 10 )
			bz = ( Rand(0,1)*2-1 ) * ( 5 + Rand(0,3) * 10 )
			PositionEntity BONUS\Model,bx,3,bz 
			ShowEntity BONUS\Model
		EndIf
	EndIf
	
	If BONUS\timer > 0
		BONUS\timer = BONUS\timer - 1
		alpha# = 1.0
		If BONUS\timer <= 30 alpha# = Float( BONUS\timer)*.033
		If BONUS\timer > 120 alpha# = Float(150.0 - BONUS\timer ) * .033
		EntityAlpha BONUS\Model,alpha
		TurnEntity BONUS\Model,0,10,0
	EndIf

End Function

;==========================================================
;==========================================================
;==========================================================

Function AI ( t.tank_type )

	t\MoveCounter = t\MoveCounter - (t\MoveCounter > 0 )
	t\FireCounter = t\FireCounter - (t\FireCounter > 0 )
	FireRange# =  ( BulletLife * t\BulletSpeed )
	MINEactivate = ( t\MineCounter = 1 And Rand(60)=1 ) 
	
	If t\Target > -1 
		TargetDistance# = EntityDistance( t\Model, tank(t\target)\Model )
		If tank(t\target)\respawn > 0 t\Target = -1
	Else
		TargetDistance# = 1000.0
		t\FireCounter = 0
	EndIf
	
	;find a new target
	If t\FireCounter = 0	
		FleeX# = 0
		FleeZ# = 0	
		For c.tank_type = Each tank_type
			If t<>c 
				FleeX = FleeX + EntityX(c\Model) * .33
				FleeZ = FleeZ + EntityZ(c\Model) * .33
				CheckDistance# = EntityDistance( t\Model, c\Model ) 
				If c\Respawn = 0 And CheckDistance < TargetDistance 
					t\Target = c\ID
					TargetDistance = CheckDistance 
				EndIf
				
				;Detonate the mine?
				If c\Respawn = 0 And t\MineCounter > 1 
					If EntityDistance( t\mine, c\Model ) < Rand(5, 20) And EntityDistance( t\mine, t\model) > 20
						MINEactivate = True
					EndIf
				EndIf
				
			EndIf
		Next
		t\FireCounter = 20
	EndIf
			
	;shoot at target if within range
	If t\Target > -1 And (TargetDistance < FireRange+10 ) And ( t\Cooler > ( Rand(2,8) *  t\BulletHeat ) )
		c.tank_type = tank(t\Target)
		time# = TargetDistance / t\BulletSpeed
		ox# = c\MaxSpeed * time
		oz# = c\MaxSpeed * time
		PositionEntity Pivot,EntityX(c\Model) + Rnd(-ox,ox) , 0, EntityZ(c\Model) + Rnd(-oz,oz)
		Distance# = NORMALxz ( EntityX( Pivot) - EntityX(t\Model) , EntityZ(Pivot) - EntityZ( t\Model) )
		t\FX = NX
		t\FZ = NZ
	Else
		t\FX = 0
		t\FZ = 0
	EndIf			
	
	;move	
	If t\MoveCounter = 0
	
		MoveRange# = t\MaxSpeed * 10.0 
		BonusRange# = MoveRange * 10 * (BONUS\timer > 0 )
		BonusDistance# = EntityDistance(t\Model,BONUS\Model) 	
									
		;decide on action
		If t\Target > -1 
			Action = AI_Straffe
			PositionEntity AIPivot, EntityX( tank(t\Target)\Model ), 0, EntityZ( tank(t\Target)\Model )
			If TargetDistance >  ( FireRange + 10.0) Action = AI_Attack
			If TargetDistance < ( FireRange - 10.0 ) Action = AI_Retreat
		Else
			Action = AI_Flee
			PositionEntity AIPivot, FleeX, 0, FleeZ
		EndIf
		If BonusDistance <  BonusRange
			Action = AI_GoForBonus
			PositionEntity AIPivot, EntityX( BONUS\Model ), 0, EntityZ( BONUS\Model )
		EndIf
		Distance# = NORMALxz( EntityX( AIPivot ) - EntityX( t\Model), EntityZ( AIPivot ) - EntityZ( t\Model ) ) 
		If Action = AI_Attack Or Action = AI_GoForBonus Distance# = 1000.00
		If Action = AI_Flee Or Action = AI_Retreat Distance# =0.0
		
		;check possible moves			
		choice = -1
		angle = Rand( 0,7 )
		For l = 0 To 7
			angle = (angle + 1 ) Mod 8
			vx# = NX * Cos( angle*45) - NZ * Sin( angle*45)
			vz# = NZ * Cos( angle*45) + NX * Sin(angle*45)
			PositionEntity pivot, EntityX(t\Model)	+ vx * MoveRange , 0, EntityZ( t\Model) + vz * MoveRange
			unused = LinePick ( EntityX(t\Model) , 3 , EntityZ(t\Model) , vx * MoveRange, 0 , vz * MoveRange , 2 )
						
			;If Nothing blocking route
			If PickedEntity() = 0
			
				;Is there a live mine near New point?
				NoGo = False		
				For c.tank_type = Each tank_type
					If c <> t And c\MineCounter > 1 
						If EntityDistance( pivot, c\Mine ) < Rand(5,10) NoGo = True
					EndIf
				Next

				If NoGo = False
					CheckDistance# = EntityDistance( pivot, AIPivot )
					Select Action
					Case AI_Attack, AI_GoForBonus
						If CheckDistance < Distance
							Distance = CheckDistance
							choice = angle
						EndIf
					Case AI_Flee, AI_Retreat
						If CheckDistance > Distance
							Distance = CheckDistance
							choice = angle
						EndIf
					Case AI_straffe
						If angle = 2 Or angle = 6 choice = angle 
					End Select
				EndIf
						
			EndIf
	
		Next

		t\MoveCounter = 10

		If choice > -1
			;found a move
			t\MX = NX * Cos( choice * 45) - NZ * Sin( choice*45)
			t\MZ = NZ * Cos( choice * 45 ) + NX * Sin( choice*45)
		Else
			;no move
			choice = Rand(3,5)
			t\MX = NX * Cos( choice * 45) - NZ * Sin( choice*45)
			t\MZ = NZ * Cos( choice * 45 ) + NX * Sin( choice*45)
		EndIf
			
	EndIf
		
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function FADE ( target# )

	direction# = Sgn(target - FadeStatus )

	Repeat
		FadeStatus = LIMIT( FadeStatus + direction * FadeSpeed , 0.0,1.0)
		EntityAlpha fade, (1.0 - FadeStatus)
		RenderWorld()
		Flip
	Until ( FadeStatus >= target And direction = 1) Or (FadeStatus <= target And direction= -1) Or (direction=0)
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function NORMALxz#( jx# , jz# )

	speed# = Sqr ( jx * jx + jz * jz )
	NX = jx / speed
	NZ = jz / speed

	Return speed

End Function

;==========================================================
;==========================================================
;==========================================================

Function HIT ( entity, r, g, b, Mark, Sound, IsTANK )

	SOUNDplay( Sound )
	If IsTANK 
		PARTICLEnew ( entity, 0,0,0, 0,0,.5,.5,0,0, r,r,g,g,b,b, .4,.6, 1 , .01 , 2, 33 )
		;PARTICLEnew( entity, 0,0,0, -.25,.25, -.5,.5, -.5,0 , r,r,g,g,b,b, .4,.6, .5, .01, 2)
		PARTICLEnew( entity, 0,0,0, -.25,.25, -.5,.5, -.5,0 , 255,255,128,255,0,0, .25,.5, .5, .02, 3)
	Else
		PARTICLEnew( entity, 0,0,0, -.25,.25, -.5,.5, -.5,0 , 128,192,128,192,128,192, .2,.3, .5, .01, 3)	
	EndIf
		
	If Mark PARTICLEnew( entity, 0,0,-1, 0,0, 0,0, 0,0 ,16,48,16,48,16,48 ,.2,.3, 0, .05, 1)

End Function

;==========================================================
;==========================================================
;==========================================================

Function EXPLOSION( entity, range#, ID ,r,g,b, IsTank )

	;do explosion
	HideEntity entity
	SOUNDplay( SOUNDexplode )
	PARTICLEnew( entity, 0,0,0, -.25,.25, .5,1.5, -.25,.25 , r,r,g,g,b,b, .5,.75,1, .005,10)
	PARTICLEnew( entity, 0,0,0, -.25,.25, .5,1.5, -.25,.25 , 255,255,128,255,0,0, .25,.5, 1, .01,10)
	PARTICLEnew ( entity, 0,0,0, 0,0,.25,.5,0,0, 255,255,255,255,255,255, .75,1, .5 , 0 , 16, range )
	
	;if tank destroyed start respawn & updatescore for the tank that was responsible
	If IsTank
		t.tank_type = Object.tank_type( EntityName( entity ) )
		t\Shield = 1
		t\Respawn = 1
		If t\MineCounter > 1 
			EXPLOSION ( t\Mine, 20, t\ID, t\r,t\g,t\b, False )
		Else
			HideEntity t\Mine
		EndIf
			
		TANKbonus ( t )
		c.tank_type = tank( ID )
		If c<>t 
			PARTICLEnew( entity, 0,3,0, 0,0, .25,.25,0,0 , c\r,c\r,c\g,c\g,c\b,c\b, 1.5,1.5, 0, .01,1)
			SCOREupdate( c , 1 )
		Else
			SCOREupdate( c, -1 )
		EndIf
	EndIf
	
	;check tanks
	For t.tank_type = Each tank_type
		If t\Life > 0 
			distance# = EntityDistance( t\Turret, entity )
			If distance < range
				damage# = ( ( range - distance ) / range ) * (1.0-t\Shield)
				t\Power = LIMIT( t\Power - damage*.25,0.0,1.0)
				t\Life = Limit (t\Life - damage, 0 , 1 )
				If t\Life = 0 EXPLOSION( t\Turret, 20, ID, t\r,t\g,t\b, True )
			EndIf
		EndIf
	Next
	
	;check drums
	For d.drum_type = Each drum_type
		If d\Life > 0
			distance# = EntityDistance( d\Model, entity )
			If distance  < range
				damage# = ( ( range - distance ) / range ) 
				d\Life = Limit (d\Life - damage, 0 , 2 )
				If d\Life = 0 EXPLOSION ( d\Model, 20 , ID, 168,116,100, False )
			EndIf
		EndIf
	Next
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKbonus ( t.tank_type , bonus=0 )

	Select bonus
		Case 0
			t\BulletReload = 9
			t\BulletSpeed = 1.25 
			t\MaxSpeed = .4 
			t\Shield = 0.0
			EntityAlpha t\BubbleShield, 0
			t\Damage = .1 
			t\LifeRecharge = .002
			t\BulletSpread=0
			t\MineCounter = 0
		Case 1
			t\BulletReload =LIMIT(  t\BulletReload - 3, 3,9 ) 
		Case 2
			t\BulletSpeed = LIMIT( t\BulletSpeed + .25,  1.25, 1.75 )
		Case 3
			t\MaxSpeed = Limit ( t\MaxSpeed + .1, .4, .6)
		Case 4
			t\Shield = Limit ( t\Shield + .25, 0, .75 )
			EntityAlpha t\BubbleShield, t\Shield*.5
		Case 5
			t\Damage = Limit ( t\Damage + .05, .1,.2 )
		Case 6
			t\LifeRecharge = LIMIT( t\LifeRecharge + .002, .002, .01 )
		Case 7
			t\BulletSpread = LIMIT( t\BulletSpread+1,0,2 )
		Case 8
			If t\MineCounter = 0 
				t\MineCounter = 1
				ShowEntity t\Mine
				EntityParent t\Mine, t\Model
				PositionEntity t\Mine, 0,0,-2.5
			EndIf
	End Select
	
	t\BulletHeat# = .025 * ( (  t\BulletSpread + 1.0 ) + ( t\Damage * 10.0 ) + ( t\BulletSpeed / 1.25 ) )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKrespawn( t.tank_type )

	t\Respawn = t\Respawn + 1
	
	;stage 0 - smoking
	If t\Respawn < 91
		PARTICLEnew( t\Model, 0,1,0, -.1,.1, .2,.3, -.1,.1 , 64,80,64,80,64,80, .25,.5, 0,.02,1)
	EndIf
	;stage 1 - show BubbleShield									
	If t\Respawn > 90 And t\Respawn <= 120 
		scale# = Float(t\Respawn - 91) / 29.0
		EntityAlpha t\BubbleShield, scale*.5
	EndIf
	;stage 2 - scale Turret, fill energy
	If t\Respawn > 120 And t\Respawn <= 150
		ShowEntity t\Turret
		scale# = Float(t\Respawn - 121) / 29.0
		t\Life = scale
		t\Power = Limit (t\Power + .04, 0.0,1.0) 	
	EndIf
	;stage 3 - hide BubbleShield
	If t\Respawn > 150 And t\Respawn <= 180
		scale# = Float(t\Respawn - 151) / 29.0
		EntityAlpha t\BubbleShield, .5 - scale*.5 
	EndIf
	;stage 4 - back in play
	If t\Respawn = 180 
		t\Respawn = 0
		If t\Shield = 1 t\Shield = 0
	EndIf
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function MINEnew( t.tank_type , action )

	Select action
	Case 0	;drop mine
		EntityParent t\Mine,0 
		t\MineCounter = 2
	Case 1	;detonate mine 
		EXPLOSION ( t\Mine, 20 , t\ID , t\r,t\g,t\b , False )
		t\MineCounter = 0
	End Select
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function BULLETnew( t.tank_type )

	angle# = t\BulletSpread * 5
	SOUNDplay( SOUNDlaser ) 
	t\Cooler = Limit ( t\Cooler - t\BulletHeat , 0, 1.0)
	PARTICLEnew( t\Turret, 0,0,3 , -.25,.25, -.25,.25, 0.05,0.1,  255,255,255,255,255,255, .25,.25, 0, .04,t\BulletSpread+1) 
	
	For fired = -Sgn( t\BulletSpread ) To Sgn( t\BulletSpread ) 
		If t\BulletSpread <>1 Or fired <> 0 
			b.bullet_type = bullet( NextBullet)
			EntityParent b\Model,t\Turret
			EntityColor b\Model,t\r,t\g,t\b
			PositionEntity b\Model,0,0,3
			RotateEntity b\Model,0,fired*angle,0
			scale# = t\Damage*10.0
			ScaleEntity b\Model,1,1,scale
			EntityParent b\Model,0
			ShowEntity b\Model
			b\Life = BulletLife
			b\ID = t\ID
			b\speed = t\BulletSpeed
			b\Damage = t\Damage
			t\BulletTimer = t\BulletReload
			NextBullet = ( NextBullet + 1 ) Mod bullets
		EndIf
	Next
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLEnew( mesh, x#,y#,z#, vx1#,vx2#, vy1#,vy2#, vz1#,vz2#, r1,r2, g1,g2, b1,b2 , s1#,s2# , w#, f#, number, range#=0)

	random# = Rand( -180,180 )

	For l = 1 To number
		p.particle_type = particle( NextParticle)
		If range = 0 
			TFormVector x,y,z, mesh, 0 
		Else
			angle# = 360 * l / number + random
			TFormVector Cos(angle),y,Sin(angle), mesh,0
			f# = 1.0 / ( range * 3.0)
		EndIf	
		p\x = EntityX(mesh,1)+TFormedX()
		p\y = EntityY(mesh,1)+TFormedY()
		p\z = EntityZ(mesh,1)+TFormedZ()
		If range = 0 
			TFormVector Rnd(vx1,vx2),Rnd(vy1,vy2),Rnd(vz1,vz2), mesh, 0
		Else
			TFormVector Cos(angle)*.5,Rnd(vy1,vy2),Sin(angle)*.5,mesh,0
		EndIf
		p\vx = TFormedX() 
		p\vy = TFormedY() 
		p\vz = TFormedZ() 
		p\r = Rand(r1,r2) 
		p\g = Rand(g1,g2) 
		p\b = Rand(b1,b2)
		p\size = Rnd(s1,s2)
		p\weight = w
		p\fade = f
		p\Life = 1.0
		NextParticle = ( NextParticle + 1 ) Mod particles
	Next

End Function 

;==========================================================
;==========================================================
;==========================================================

Function SCOREupdate( t.tank_type , add#=0 )

	t\Score = t\Score + add
	If t\Score = KILLS And GAMEOVER = 0 GAMEOVER = t\ID+1
	SetBuffer TextureBuffer (t\ScoreTexture)
	Color 0,0,0:Rect 0,0,32,32
	Color t\r,t\g,t\b:Text 16,16,t\Score,1,1
	SetBuffer BackBuffer()
	
End Function 

;==========================================================
;==========================================================
;==========================================================

Function GAMEend()

	Winner = tank(GAMEOVER-1)\Model
	CAMERApoint( EntityX(Winner),EntityY(Winner),EntityZ(Winner), 36.75 , 150 , .1 )
	CAMERApoint( 0,0,0, 6.75 , 60 , .1 )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEstart()

	MENUshow( MENU(0) )
	FADE( 0 )
	GAMEreset ()
	FADE( 1 )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function CAMERApoint( x#,y#,z#, zoomtarget#, iterations, smooth#, show =True)

	PositionEntity Pivot,x,y,z
		
	For l = 1 To iterations
		TurnEntity camera, DeltaPitch(camera, pivot)*smooth, DeltaYaw( camera, pivot)*smooth, 0
		zoom = zoom + Sgn( zoomtarget - zoom ) 
		CameraZoom camera,  zoom 
		If show
			WaitTimer (FrameTimer)
			PARTICLESupdate()
			RenderWorld()
			Flip
		EndIf
	Next

End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEreset( )

	;tanks
	For t.tank_type = Each tank_type
		t\Score = 0
		t\BulletTimer=0
		t\Life = 1.0
		t\Power = 1.0
		t\Cooler = 1.0
		t\Respawn = 0
		t\Control = ( t\ID < PLAYERS ) + 2 * ( t\ID >=PLAYERS )
		x# = ( (t\ID=0 Or t\ID=2) - (t\ID=1 Or t\ID=3) )*30
		z# = ( (t\ID=1 Or t\ID=2) - (t\ID=0 Or t\ID=3) )*30
		PositionEntity t\Model,x,3,z
		PointEntity t\Model,ARENA\Model:TurnEntity t\Model,0,180,0
		RotateEntity t\Turret,0,EntityYaw(t\Model),0
		ResetEntity t\Model
		PositionEntity t\Turret,x,3,z
		ShowEntity t\Turret:ScaleEntity t\Turret,1,1,1
		ShowEntity t\Model
		SCOREupdate (t)
		TANKbonus (t)
		ScaleEntity t\LifeBar,t\Life,1,1
		ScaleEntity t\PowerBar,t\Power,1,1
		HideEntity t\Mine
		t\FX = 0:t\FZ = 0
		t\MX = 0:t\MZ = 0
		t\MoveCounter = t\ID * 2 
		t\FireCounter = t\ID * 2
		t\Target = -1
	Next
	
	;bullets
	For w.bullet_type = Each bullet_type
		HideEntity w\Model
		ResetEntity w\Model
	Next
		
	;drums
	For b.drum_type = Each drum_type
		x# = Cos( -22.5 + b\ID *45.0) * 27
		z# = Sin(-22.5 + b\ID * 45.0) * 27
		b\Life = 2.0
		PositionEntity b\Model,x,2,z
		ResetEntity b\Model
		ShowEntity b\Model
	Next
		
	;particles
	For p.particle_type = Each particle_type
		p\Life = 0
	Next
	PARTICLESupdate()
		
	;bonus
	HideEntity BONUS\Model
	BONUS\timer = 0
		
	;misc	
	zoom# = 6.75
	GAMEOVER = 0
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function LIMIT#( q# , low# , hi# )

	If q < low q = low
	If q > hi q = hi
	Return q
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function SOUNDplay ( sound )
	If SOUNDon 
		new_channel = PlaySound( sound )
	EndIf
End Function

;==========================================================
;==========================================================
;==========================================================

Function MENUshow ( m.menu_type )
	
	;reset
	m\current = m\initial
	MENUupdate( m )
	
	;fade in	
	ShowEntity m\model
	For l=  0 To 30
		RotateEntity m\model, 0,0,l*12
		PositionEntity m\model,0,0,2+(30-l)
		RenderWorld()
		Flip
	Next		
	
	Repeat
	
		;user input
		jx = hit2(0)-hit2(1)
		jy = hit2(3)-hit2(2)
		jb = down(4)
		If KeyDown(1) End
				
		;change current option
		If jy <> 0 
			old = m\current
			m\current = Limit (m\current + jy, 0, m\options-1 )
			jx=0:jy = (old <> m\current)
		EndIf
		
		;change current sub option
		If jx <> 0  
			old = m\sub_current[m\current]
			m\sub_current[m\current] = Limit ( m\sub_current[m\current] + jx , 0, m\sub_options[m\current]-1 )
			jx = ( old <> m\sub_current[m\current] )
		EndIf
		
		;update menu
		If (jx+jy) <> 0 
			SOUNDplay( SOUNDmenu)
			MENUupdate ( m )
		EndIf
		
		RenderWorld()
		Flip

	Until m\sub_options[m\current] = 1 And jb

	;fade out
	For l=  0 To 30
		RotateEntity m\model, 0,0,l*12
		PositionEntity m\model,0,0,2+l
		RenderWorld()
		Flip
	Next	
	HideEntity m\model

	;get details
	Select m\ID
	Case 0
		PLAYERS = m\sub_current[0]
		KILLS = ( m\sub_current[1] + 1 ) * 10 
	Case 1
		QUIT = ( m\current = 1)
	End Select

End Function

;==========================================================
;==========================================================
;==========================================================

Function MENUupdate(m.menu_type, update_title=False )

	SetBuffer TextureBuffer(m\texture)
	
	If update_title
		Color 64,64,128:Rect 0,0,256,128,1 
		MyText( 128,12,m\title, 1,1, 255,255,0 )
	Else
		Color 64,64,128:Rect 0,28,256,88,1
	EndIf
		
	y = 22 + ( m\options = 2 ) * 48
		
	For option = 0 To m\options - 1
	
		r = 255
		g = 155+100*(m\current = option)
		b = 155+100*(m\current = option)
		x = 8 + 120 * ( m\sub_options[option]=1 )
		
		MyText ( x, y + option * 24, Left$( m\sub_string[option], 12), m\sub_options[option]=1,0, r,g,b )
		
		If m\sub_options[option] > 0
			MyText ( 136, y + option * 24 , Mid$(m\sub_string[option], ( m\sub_current[option] + 1)*12,12 ), 0,0, r,g,b )
		EndIf
	Next
	
	SetBuffer BackBuffer()
		
	Delay 100
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function MyText ( x, y, t$, cx, cy, r, g, b )

	Color 0,0,0
	For l = 0 To 4
		px = ( ( l = 0) - (l = 2) ) * 1
		py = ( ( l = 1) - (l = 3) ) * 1
		If l= 4 Color r,g,b
		Text x+ px , y+ py , t$ , cx , cy
	Next

End Function

;==========================================================
;==========================================================
;==========================================================
;==========================================================
; THESE FUNCTIONS ARE ONLY USED ONCE
;==========================================================
;==========================================================
;==========================================================
;==========================================================

Function MENUinit()

	Restore menu_data
	
	For l = 0 To menus-1
		m.menu_type = New menu_type
		menu( l ) = m
		m\ID = l
		w# = .5
		h# = .25
		m\model = QUADnew(menu_cam,-90, w,h,1 )
		frame = CYLINDERnew( 3, 128,128,128, 0,0,90, .01,w,.01, 0,h+.01,0 )
		CYLINDERnew( 3, 128,128,128, 0,0,90, .01,w,.01, 0,-(h+.01),0, frame )
		CYLINDERnew( 3, 128,128,128, 0,0,0, .01,h,.01, -(w+.01),0,0, frame )
		CYLINDERnew( 3, 128,128,128, 0,0,0, .01,h,.01, w+.01,0,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,-45, .04,.02,.04, w,h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,-45, .04,.02,.04, -w,-h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,45, .04,.02,.04, w,-h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,45, .04,.02,.04, -w,h,0, frame )
		EntityParent frame, m\model
		PositionEntity frame, 0,0,0 
		PositionEntity m\model,0,0,2
		HideEntity m\model
		m\texture = CreateTexture (256,128, 16+32+1)
		EntityTexture m\model, m\texture
		Read m\title
		Read m\options
		Read m\initial
		For option = 0 To m\options-1
			Read m\sub_options[option]
			Read m\sub_string[option]
			m\sub_current[option] = ( m\sub_options[option] > 1 )
		Next
		MENUupdate( m, True )
	Next	
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEinit()

	;light
	RotateEntity light,30,-60,0 
			
	;camera	
	PositionEntity camera,0,250,-250 
	CameraZoom camera, zoom
	CAMERApoint( 0,0,0, 6.75, 1 , 1 , False )
	fade = QUADnew( camera , -90 )
	EntityColor fade,0,0,0
	PositionEntity fade,0,0,6.75
	FADE(0)
	
	;menu camera
	PositionEntity menu_cam,-25000,0,0
	CameraClsMode menu_cam,False,True
	PositionEntity menu_cam,65000,65000,65000
	EntityOrder menu_cam,-999
		
	;bonus
	BONUS\Model = CYLINDERnew(3,255,255,0, -90,0,0, 1,.5,1, 0,0,0, 0,  1,1, 1,1,0 )
						
	;BubbleShield
	BubbleMesh = CYLINDERnew( 3,128,128,128, 0,0,0, 3,2,3, 0,-1,0 ,0, 1,1, 1,1, 0)
	HideEntity BubbleMesh
	EntityShininess BubbleMesh,1
	
	;bullets
	BulletMesh = CYLINDERnew( 6, 128,128,128, -90,0,0, .25,.375,.25, 0,0,0 ,0, 1,2, 1,1, 0, 1)
	HideEntity BulletMesh
	EntityType BulletMesh,T_BULLET
	EntityRadius BulletMesh,.5
	For b=0 To bullets-1
		bullet(b) = New bullet_type
		bullet(b)\Model = CopyEntity ( BulletMesh )
		bullet(b)\Life = 0
		HideEntity bullet(b)\Model
	Next
	
	;tanks
	For l = 0 To tanks-1
		tank(l) = New tank_type
		tank(l)\ID = l
		tank(l)\r = 100+84*(l=1)+42*(l=3)
		tank(l)\g = 100+84*(l=2)+42*(l=3)
		tank(l)\b = 100+84*(l=0)
		TANKinit ( tank(l) )
	Next
	
	;drums
	DrumMesh = CYLINDERnew( 3, 168,116,100, 0,0,0, 1,2,1, 0,0,0 )
	HideEntity DrumMesh
	For l=0 To drums-1
		drum(l) = New drum_type
		drum(l)\Model = CopyEntity ( DrumMesh )
		drum(l)\ID = l
		EntityType drum(l)\Model,T_DRUM
		EntityRadius drum(l)\Model,1.5,2  
		NameEntity drum(l)\Model,Handle(drum(l)) 
		EntityPickMode drum(l)\Model, 1
	Next

	;sounds
	If SOUNDon
		SOUNDclang = LoadSound("Clang.wav")
		SOUNDlaser = LoadSound("Laser.wav")
		SOUNDhit = LoadSound("Hit.wav")	
		SOUNDexplode = LoadSound("Explode.wav")
		SOUNDbonus = LoadSound("Bonus.wav")
		SOUNDmenu = LoadSound("Menu.wav")
	EndIf
	
	ARENAinit()
	PARTICLEinit()	
	MENUinit()
	GAMEreset ()
			
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLEinit()

	ParticleTemplate = CYLINDERnew(6, 0,0,0, 0,30,0, 1,.75,1, 0,0,0	, 0, 2,2, 1,1, 2, 1)
	ParticleMesh = CreateMesh()
	EntityFX ParticleMesh,32+2
	EntityShininess ParticleMesh,1
	s= CreateSurface( ParticleMesh )
	gs = GetSurface(ParticleTemplate,1)
	cv = CountVertices ( gs )
	ct = CountTriangles ( gs )
	For l= 0 To particles-1
		vs= l * cv
		particle(l) = New particle_type
		particle(l)\Vertex = vs
		For v=0 To cv-1
			v0 = AddVertex(s,0,0,0)
			VertexNormal s,v0,VertexNX(gs,v),VertexNY(gs,v),VertexNZ(gs,v) 
		Next
		For t=0 To ct - 1
			AddTriangle s, TriangleVertex( gs,t,0)+vs,TriangleVertex( gs,t,1)+vs,TriangleVertex( gs,t,2)+vs
		Next 
	Next
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function ARENAinit()

	Ground=QUADnew( 0, 0, 45,1,45)
	EntityOrder Ground,1
	EntityColor Ground,0,0,0
	EntityBlend Ground,3 
	Plane = CreatePlane()
	EntityColor Plane,0,0,0 
	PositionEntity Plane,0,-.5,0
	EntityAlpha Plane,.65
	EntityOrder Plane,1
	Mirror = CreateMirror()
	PositionEntity Mirror,0,-.1,0
	Texture=CreateTexture(64,64)
	SetBuffer TextureBuffer(Texture)
	For y=0 To 1:For x=0 To 1
		If ( x + y ) Mod 2 = 0 Color 128,32,128 Else Color 32,32,32
		Rect x*32,y*32,32,32,1
	Next:Next
			
	SetBuffer BackBuffer()
	ScaleTexture Texture,1.0/9.0,1.0/9.0
	TextureBlend Texture,3	
	EntityTexture Ground,Texture
	FreeTexture Texture
				
	part = CYLINDERnew( 3, 142,100,142, 0,0,0, 2,3,2, -45,3,45 )
	CYLINDERnew( 3, 142,100,142, 90,0,0, 2,3,2, -45,3,0 ,part)
	CYLINDERnew( 3, 128,128,128, 90,0,0, .5,20.5,.5  ,-45,4,-22.5  ,part)
	CYLINDERnew( 3, 128,128,128, 0,0,90, .5,20.5,.5  ,-22.5,4,-45  ,part)
	CYLINDERnew( 3, 128,128,128, 90,0,0, .5,20.5,.5  ,-45,2,-22.5  ,part)
	CYLINDERnew( 3, 128,128,128, 0,0,90, .5,20.5,.5  ,-22.5,2,-45  ,part)
	CYLINDERnew(3, 128,128,128, 90,0,0, .5,4,.5, -20,4.5,25, part)
	CYLINDERnew(3, 128,128,128, 90,0,0, .5,4,.5, -20,1.5,25, part)
	CYLINDERnew(3, 128,128,128, 0,0,90, .5,4,.5, -25,4.5,20, part)
	CYLINDERnew(3, 128,128,128, 0,0,90, .5,4,.5, -25,1.5,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -20,3,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -30,3,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -20,3,30, part)
	ARENA\model = CYLINDERnew( 3,142,100,142, 0,0,0 ,3,2,3, 0,2,0 ,0, 1,1, 1,.5 )
	ARENA\radar = CYLINDERnew( 3,128,128,128,0,0,0,.5,1,1,0,5,0, 0,1,1 )
	CYLINDERnew( 3,142,100,142,90,0,0,2,2,2,0,8,0, ARENA\radar,2,2, 1, 1 )
	
	EntityType ARENA\Model,T_ARENA
	EntityPickMode ARENA\Model,2
	For l=0 To 3
		temp = CopyMesh(part):RotateMesh temp,0,l*90,0
		AddMesh temp,ARENA\Model:FreeEntity temp
		px = 39*( ( l=3 ) - ( l = 0) ) + 13 * ( (l = 2) - ( l = 1) )
		ARENA\hud = CYLINDERnew( 3, 96,96,96, 0,0,90, .25,8,.25, px,13,45 )
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, 2,.5,1, px - 8.5,12,45, ARENA\hud)
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, 2,.5,1, px + 8.5,12,45, ARENA\hud )
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, .2,8,.2, px ,10.5,45, ARENA\hud )
	Next
	FreeEntity part
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKinit( t.tank_type )
	
	;Turret
	t\Turret = CYLINDERnew( 6 , 128,128,128 , 0,0,0 , 1,.5,1 , 0,0,0 )
	;ariel
	CYLINDERnew( 4 , t\r,t\g,t\b , 0,0,0 , .1,1,.1 , .5,1,-.5 , t\Turret)
	;hatch
	CYLINDERnew( 4 , t\r,t\g,t\b , 0,0,0 , .3,.25,.3 , -.25,.5,.25,  t\Turret)
	;barrel
	CYLINDERnew( 4 , 128,128,128 , 90,0,0 , .25,.5,.25 ,0,0,1.5, t\Turret )
	;nosel
	CYLINDERnew( 6 , t\r,t\g,t\b , 90,0,0 , .5,.5,.5 , 0,0,2.5 , t\Turret ,0,1 , .75,1)
	;Turret base
	 CYLINDERnew( 6, t\r,t\g,t\b , 0,0,0 , 1.5,.25,1.5 , 0,-.75,0 , t\Turret )
	;Base
	t\Model = CYLINDERnew( 6, 128,128,128 , 0,0,0 , .75,.5,1.75 , 0,1.5,-.5 )
	;Exhaust
	CYLINDERnew( 6, t\r,t\g,t\b , 90,0,0 , .25,.25,.25 , 0,1.5,-2.5 , t\Model ,1,0 )
	EntityRadius t\Model,2.75
	EntityPickMode t\Model, 1
	EntityType t\Model,T_TANK
	;tracks
	For x# = -1.25 To 1.25 Step 2.5
		CYLINDERnew( 6, 64,64,64 , 0,0,90 , 1,.5,2 , x ,1,0 , t\Model ,0,0 )
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .5,.5,.5 , x,1,0 , t\Model ,1,1)
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .4,.4,.4 , x,1,-1 , t\Model ,1,1)
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .4,.4,.4 , x,1,1 , t\Model ,1,1)
	Next
	PositionMesh t\Model,0,-3,0
	NameEntity t\Model,Handle(t)
	NameEntity t\Turret,Handle(t) 
	
	;mine model
	t\Mine = CYLINDERnew( 6 , 128,128,128 , 0,0,0 , 1,.1,1 , 0,-2.8,0 )
	CYLINDERnew( 6 , t\r,t\g,t\b , 0,0,0 , .5,.1,.5 , 0,-2.6,0, t\Mine )
		
	;bubble shield for respawn
	t\BubbleShield = CopyEntity (BubbleMesh, t\Model)
	EntityColor t\BubbleShield,t\r,t\g,t\b
			
	;hud
	px = 39*( ( t\ID=3 ) - ( t\ID = 0) ) +13 * ( (t\ID = 2) - ( t\ID = 1) )
	t\LifeBar = CYLINDERnew( 3, t\r,t\g,t\b, 0,0,90, 1,8,1, 0,0,0 )
	PositionEntity t\LifeBar,px,13,45
	t\PowerBar = CYLINDERnew( 3, t\r*.5,t\g*.5,t\b*.5, 0,0,90, .5,8,.5, 0,0,0 )
	PositionEntity t\PowerBar,px,13,45
	t\CoolerBar = CYLINDERnew( 3, t\r*.75,t\g*.75,t\b*.75, 0,0,90,.5,8,.5,0,0,0 )
	PositionEntity t\CoolerBar,px,10.5,45
	ScoreMesh = QUADnew(Plane,-90,2.5,2.5,1)
	PositionEntity ScoreMesh,px,7.5,45
	t\ScoreTexture = CreateTexture(32,32)
	EntityTexture ScoreMesh, t\ScoreTexture
				
End Function

;==========================================================
;==========================================================
;==========================================================

Function CYLINDERnew( segs=12 ,r,g,b, rx,ry,rz, sx#,sy#,sz# , px#,py#,pz# ,parent = 0, b_in#=1.0, t_in#=1.0 , b_wi#=1.0, t_wi#=1.0, fx#=2, de#=detail )

	segs = segs * de
	
	mesh = CreateMesh():s = CreateSurface(mesh)
	For y# = -1 To 1 Step 2
	
		w# = b_wi * (y=-1) + t_wi * (y=1)
	
		For v = 0 To segs-1
			a# = Float(v) * ( 360.0 / Float(segs) )
			v0 = AddVertex( s,w*Cos(a),y,w*Sin(a) )
			VertexColor s,v0,r,g,b
		Next
	Next
	
	vb = AddVertex(s,0,b_in,0):VertexColor s,vb,r*b_in,g*b_in,b*b_in 
	vt = AddVertex(s,0,-t_in,0):VertexColor s,vt,r*t_in,g*t_in,b*t_in
	
	For v1 = 0 To segs-1
		v2 = ( v1 + 1) Mod segs
		v3 = ( v2 + segs )
		v4 = ( v1 + segs )
		AddTriangle s,vt,v1,v2
		AddTriangle s,vb,v3,v4 
		AddTriangle s,v3,v2,v1
		AddTriangle s,v1,v4,v3
	Next
	
	ScaleMesh mesh,sx,sy,sz	
	RotateMesh mesh,rx,ry,rz
	PositionMesh mesh ,px,py,pz
	UpdateNormals mesh
	EntityFX mesh,fx
	EntityShininess mesh,1.0
	If fx=0 EntityColor mesh,r,g,b
	If parent > 0 
		AddMesh mesh,parent:FreeEntity mesh
	Else
		Return mesh
	EndIf

End Function

;==========================================================
;==========================================================
;==========================================================

Function QUADnew( parent=0, rx=0 ,sx#=1, sy#=1, sz#=1 )

	mesh=CreateMesh(parent)
	surface=CreateSurface(mesh)
	AddVertex surface,-1,0,1,0,0
	AddVertex surface,1,0,1,1,0
	AddVertex surface,1,0,-1,1,1
	AddVertex surface,-1,0,-1,0,1
	AddTriangle surface,0,1,2
	AddTriangle surface,2,3,0
	RotateMesh mesh,rx,0,0	
	ScaleMesh mesh,sx,sy,sz
		
	Return mesh
End Function	

;==========================================================
;==========================================================
;==========================================================

Function save_key_settings(file$="keysettings.dat")
	filehandle=WriteFile(file$)
	If filehandle=0 Then Return 0
	WriteFloat filehandle,mouse_factor_x#
	WriteFloat filehandle,mouse_factor_y#
	WriteFloat filehandle,mouse_factor_z#
	WriteFloat filehandle,joystick_factor_x#
	WriteFloat filehandle,joystick_factor_y#
	For a_keys=0 To 7
		WriteFloat filehandle,joystick_minmax(a_keys)
	Next
	For a_keys=0 To key_num
		WriteInt filehandle,ids(0,a_keys)
		WriteInt filehandle,ids(1,a_keys)
		WriteString filehandle,keynames(a_keys)
	Next
	CloseFile filehandle
	Return 1
End Function
Function load_key_settings(file$="keysettings.dat")
	filehandle=ReadFile(file$)
	If filehandle=0 Then Return 0
	mouse_factor_x#=ReadFloat(filehandle)
	mouse_factor_y#=ReadFloat(filehandle)
	mouse_factor_z#=ReadFloat(filehandle)
	joystick_factor_x#=ReadFloat(filehandle)
	joystick_factor_y#=ReadFloat(filehandle)
	For a_keys=0 To 7
		joystick_minmax(a_keys)=ReadFloat(filehandle)
	Next
	For a_keys=0 To key_num
		ids(0,a_keys)=ReadInt(filehandle)
		ids(1,a_keys)=ReadInt(filehandle)
		keynames(a_keys)=ReadString(filehandle)
	Next
	CloseFile filehandle
	Return 1
End Function
Function hit2(id)
	If ids(0,id)=0 Then
		Return KeyHit(ids(1,id))
	Else If ids(0,id)=1 Then
		Return MouseHit(ids(1,id))
	Else If ids(0,id)=2 Then
		Return JoyHit(ids(1,id))
	Else If ids(0,id)=3 Then
		If ids(1,id)=0 Then
			a_keys=MouseX()-last_mouse_x_hitn
			If a_keys<-20 Then
				last_mouse_x_hitn=MouseX()
				Return 1
			Else If a_keys>20 Then
				last_mouse_x_hitn=MouseX()
				Return 0
			Else
				Return 0
			End If
		Else If ids(1,id)=1 Then
			a_keys=MouseX()-last_mouse_x_hitp
			If a_keys>20 Then
				last_mouse_x_hitp=MouseX()
				Return 1
			Else If a_keys<-20 Then
				last_mouse_x_hitp=MouseX()
				Return 0
			Else
				Return 0
			End If
		Else If ids(1,id)=2 Then
			a_keys=MouseY()-last_mouse_y_hitn
			If a_keys<-20 Then
				last_mouse_y_hitn=MouseY()
				Return 1
			Else If a_keys>20 Then
				last_mouse_y_hitn=MouseY()
				Return 0
			Else
				Return 0
			End If
		Else If ids(1,id)=3 Then
			a_keys=MouseY()-last_mouse_y_hitp
			If a_keys>20 Then
				last_mouse_y_hitp=MouseY()
				Return 1
			Else If a_keys<-20 Then
				last_mouse_y_hitp=MouseY()
				Return 0
			Else
				Return 0
			End If
		Else If ids(1,id)=4 Then
			a_keys=MouseZ()-last_mouse_z_neg
			last_mouse_z_neg=MouseZ()
			If a_keys<0 Then
				Return 1
			Else
				Return 0
			End If
		Else If ids(1,id)=5 Then
			a_keys=MouseZ()-last_mouse_z_pos
			last_mouse_z_pos=MouseZ()
			If a_keys>0 Then
				Return 1
			Else
				Return 0
			End If
		Else If ids(1,id)=6
			If JoyXDir()=-1 Then
				If joy_dir_list(0)=1 Then
					Return 0
				Else
					joy_dir_list(0)=1
					Return 1
				End If
			Else
				joy_dir_list(0)=0
				Return 0
			End If
		Else If ids(1,id)=7
			If JoyXDir()=1 Then
				If joy_dir_list(1)=1 Then
					Return 0
				Else
					joy_dir_list(1)=1
					Return 1
				End If
			Else
				joy_dir_list(1)=0
				Return 0
			End If
		Else If ids(1,id)=8
			If JoyYDir()=-1 Then
				If joy_dir_list(2)=1 Then
					Return 0
				Else
					joy_dir_list(2)=1
					Return 1
				End If
			Else
				joy_dir_list(2)=0
				Return 0
			End If
		Else If ids(1,id)=9
			If JoyYDir()=1 Then
				If joy_dir_list(3)=1 Then
					Return 0
				Else
					joy_dir_list(3)=1
					Return 1
				End If
			Else
				joy_dir_list(3)=0
				Return 0
			End If
		End If
	End If
End Function
Function down#(id)
	If ids(0,id)=0 Then
		Return KeyDown(ids(1,id))
	Else If ids(0,id)=1 Then
		Return MouseDown(ids(1,id))
	Else If ids(0,id)=2 Then
		Return JoyDown(ids(1,id))
	Else If ids(0,id)=3 Then
		If ids(1,id)=0 Then
			a_keys=MouseX()-last_mouse_x_neg
			last_mouse_x_neg=MouseX()
			If a_keys<0 Then
				Return -a_keys*mouse_factor_x#
			Else
				Return 0
			End If
		Else If ids(1,id)=1 Then
			a_keys=MouseX()-last_mouse_x_pos
			last_mouse_x_pos=MouseX()
			If a_keys>0 Then
				Return a_keys*mouse_factor_x#
			Else
				Return 0
			End If
		Else If ids(1,id)=2 Then
			a_keys=MouseY()-last_mouse_y_neg
			last_mouse_y_neg=MouseY()
			If a_keys<0 Then
				Return -a_keys*mouse_factor_y#
			Else
				Return 0
			End If
		Else If ids(1,id)=3 Then
			a_keys=MouseY()-last_mouse_y_pos
			last_mouse_y_pos=MouseY()
			If a_keys>0 Then
				Return a_keys*mouse_factor_y#
			Else
				Return 0
			End If
		Else If ids(1,id)=4 Then
			a_keys=MouseZ()-last_mouse_z_neg
			last_mouse_z_neg=MouseZ()
			If a_keys<0 Then
				Return -a_keys*mouse_factor_z#
			Else
				Return 0
			End If
		Else If ids(1,id)=5 Then
			a_keys=MouseZ()-last_mouse_z_pos
			last_mouse_z_pos=MouseZ()
			If a_keys>0 Then
				Return a_keys*mouse_factor_z#
			Else
				Return 0
			End If
		Else If ids(1,id)=6 Then
			a_keyss#=JoyX()
			If a_keyss>joystick_minmax(0) Then
				Return 0
			Else If a_keyss>joystick_minmax(1) Then
				Return joystick_factor_x*(joystick_minmax(0)-a_keyss)/(joystick_minmax(0)-joystick_minmax(1))
			Else
				Return 1
			End If
		Else If ids(1,id)=7 Then
			a_keyss#=JoyX()
			If a_keyss<joystick_minmax(2) Then
				Return 0
			Else If a_keyss<joystick_minmax(3) Then
				Return joystick_factor_x*(joystick_minmax(2)-a_keyss)/(joystick_minmax(2)-joystick_minmax(3))
			Else
				Return 1
			End If
		Else If ids(1,id)=8 Then
			a_keyss#=JoyY()
			If a_keyss>joystick_minmax(4) Then
				Return 0
			Else If a_keyss>joystick_minmax(5) Then
				Return joystick_factor_y*(joystick_minmax(4)-a_keyss)/(joystick_minmax(4)-joystick_minmax(5))
			Else
				Return 1
			End If
		Else If ids(1,id)=9 Then
			a_keyss#=JoyY()
			If a_keyss<joystick_minmax(6) Then
				Return 0
			Else If a_keyss<joystick_minmax(7) Then
				Return joystick_factor_y*(joystick_minmax(6)-a_keyss)/(joystick_minmax(6)-joystick_minmax(7))
			Else
				Return 1
			End If
		End If
	End If
End Function
Function preidsconfigure()
	FlushKeys
	FlushMouse
	MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2) ;Optionanl
	mx_keys=MouseX()
	my_keys=MouseY()
	mz_keys=MouseZ()
	joy_dir_list(0)=(JoyXDir()=-1)
	joy_dir_list(1)=(JoyXDir()=1)
	joy_dir_list(2)=(JoyYDir()=-1)
	joy_dir_list(3)=(JoyYDir()=1)
End Function
Function idsconfigure(id)
;	Repeat
		If JoyXDir()<>-1 Then joy_dir_list(0)=0
		If JoyXDir()<>1 Then joy_dir_list(1)=0
		If JoyYDir()<>-1 Then joy_dir_list(2)=0
		If JoyYDir()<>1 Then joy_dir_list(3)=0
		If MouseX()-mx_keys<-50 Then
			ids(0,id)=3
			ids(1,id)=0
			keynames(id)="Move Mouse Left"
			Return
		Else If MouseX()-mx_keys>50 Then
			ids(0,id)=3
			ids(1,id)=1
			keynames(id)="Move Mouse Right"
			Return
		Else If MouseY()-my_keys<-50 Then
			ids(0,id)=3
			ids(1,id)=2
			keynames(id)="Move Mouse Up"
			Return
		Else If MouseY()-my_keys>50 Then
			ids(0,id)=3
			ids(1,id)=3
			keynames(id)="Move Mouse Down"
			Return
		Else If MouseZ()-mz_keys<0 Then
			ids(0,id)=3
			ids(1,id)=4
			keynames(id)="Roll Whell Backward"
			Return
		Else If MouseZ()-mz_keys>0 Then
			ids(0,id)=3
			ids(1,id)=5
			keynames(id)="Roll Whell Forward"
			Return
		Else If JoyXDir()=-1 And joy_dir_list(0)=0 Then
			ids(0,id)=3
			ids(1,id)=6
			keynames(id)="Joystick Left"
			Return
		Else If JoyXDir()=1 And joy_dir_list(1)=0 Then
			ids(0,id)=3
			ids(1,id)=7
			keynames(id)="Joystick Right"
			Return
		Else If JoyYDir()=-1 And joy_dir_list(2)=0 Then
			ids(0,id)=3
			ids(1,id)=8
			keynames(id)="Joystick Up"
			Return
		Else If JoyYDir()=1 And joy_dir_list(3)=0 Then
			ids(0,id)=3
			ids(1,id)=9
			keynames(id)="Joystick Down"
			Return
		End If
		For a=0 To 255
			If KeyHit(a) Then
				ids(0,id)=0
				ids(1,id)=a
				keys_midvar=GetKey()
;				If keys_midvar=0 Or a=57 Or a>70 And a<84 Or a=55 Or a=181 Or 1=1 Then
					If a=1 Then
						keynames(id)="Escape"
					Else If a=15 Then
						keynames(id)="Tab"
					Else If a=58 Then
						keynames(id)="Caps Lock"
					Else If a=42 Then
						keynames(id)="Left Shift"
					Else If a=29 Then
						keynames(id)="Left Control"
					Else If a=219 Then
						keynames(id)="Left Start"
					Else If a=56 Then
						keynames(id)="Left Alt"
					Else If a=57 Then
						keynames(id)="Space"
					Else If a=184 Then
						keynames(id)="Right Alt Gr"
					Else If a=220 Then
						keynames(id)="Right Start"
					Else If a=221 Then
						keynames(id)="Menu Button"
					Else If a=157 Then
						keynames(id)="Right Control"
					Else If a=54 Then
						keynames(id)="Right Shift"
					Else If a=28 Then
						keynames(id)="Enter"
					Else If a=14 Then
						keynames(id)="Backspace"
					Else If a=59 Then
						keynames(id)="F1"
					Else If a=60 Then
						keynames(id)="F2"
					Else If a=61 Then
						keynames(id)="F3"
					Else If a=62 Then
						keynames(id)="F4"
					Else If a=63 Then
						keynames(id)="F5"
					Else If a=64 Then
						keynames(id)="F6"
					Else If a=65 Then
						keynames(id)="F7"
					Else If a=66 Then
						keynames(id)="F8"
					Else If a=67 Then
						keynames(id)="F9"
					Else If a=68 Then
						keynames(id)="F10"
					Else If a=87 Then
						keynames(id)="F11"
					Else If a=88 Then
						keynames(id)="F12"
					Else If a=183 Then
						keynames(id)="Print Screen"
					Else If a=70 Then
						keynames(id)="Pause"
					Else If a=197 Then
						keynames(id)="Num Lock"
					Else If a=210 Then
						keynames(id)="Insert"
					Else If a=199 Then
						keynames(id)="Home"
					Else If a=201 Then
						keynames(id)="Page Up"
					Else If a=211 Then
						keynames(id)="Delete"
					Else If a=207 Then
						keynames(id)="End"
					Else If a=209 Then
						keynames(id)="Page Down"
					Else If a=200 Then
						keynames(id)="Arrow Up"
					Else If a=203 Then
						keynames(id)="Arrow Left"
					Else If a=208 Then
						keynames(id)="Arrow Down"
					Else If a=205 Then
						keynames(id)="Arrow Right"
					Else If a=69 Then
						keynames(id)="Num Lock"
					Else If a=181 Then
						keynames(id)="Numpad /"
					Else If a=55 Then
						keynames(id)="Numpad *"
					Else If a=71 Then
						keynames(id)="Numpad 7"
					Else If a=72 Then
						keynames(id)="Numpad 8"
					Else If a=73 Then
						keynames(id)="Numpad 9"
					Else If a=74 Then
						keynames(id)="Numpad -"
					Else If a=75 Then
						keynames(id)="Numpad 4"
					Else If a=76 Then
						keynames(id)="Numpad 5"
					Else If a=77 Then
						keynames(id)="Numpad 6"
					Else If a=78 Then
						keynames(id)="Munpad +"
					Else If a=79 Then
						keynames(id)="Numpad 1"
					Else If a=80 Then
						keynames(id)="Numpad 2"
					Else If a=81 Then
						keynames(id)="Numpad 3"
					Else If a=82 Then
						keynames(id)="Numpad 0"
					Else If a=83 Then
						If keys_midvar=0 Then
							keynames(id)="Numpad ."
						Else
							keynames(id)="Numpad "+Chr(keys_midvar)
						End If
					Else If a=156 Then
						keynames(id)="Numpad Enter"
					Else
;						keynames(id)="Unknown key "+a
						If keys_midvar>223 Then keys_midvar=keys_midvar-32
						If keys_midvar=0 Then
							keynames(id)="Unknown Key"
						Else
							keynames(id)=Upper(Chr(keys_midvar));+" Key"
						End If
					End If
;				Else
;					If keys_midvar>223 Then keys_midvar=keys_midvar-32
;					keynames(id)=Upper(Chr(keys_midvar));+" Key"
;				End If
				Return
			End If
		Next
		For a=0 To 15
			If JoyHit(a) Then
				ids(0,id)=2
				ids(1,id)=a
				keynames(id)="Joystick Button "+a
				Return
			End If
		Next
		For a=1 To 3
			If MouseHit(a) Then
				ids(0,id)=1
				ids(1,id)=a
				If a=1 Then
					keynames(id)="Left Mouse Key"
				Else If a=2 Then
					keynames(id)="Right Mouse Key"
				Else
					keynames(id)="Middle Mouse Key"
				End If
				Return
			End If
		Next
;		Delay 5
;	Forever
	Return -1
End Function










.menu_data

Data "TANX  by Stevie G"
Data 4, 0 	
Data 3,"Players     Demo        One         Two         "
Data 5,"Kills       10          20          30          40          50          "
Data 3,"Difficulty  Easy        Medium      Hard        "
Data 1,"Start"

Data "Paused"
Data 2,0
Data 1,"Continue"
Data 1,"Quit"


So you're complaining because you're too cheap to buy an analogue controller or adapter? I've never understood why computer users will drop hundreds on a video card or ram but the moment they need to buy a gamepad they whine.

Thanks for the code Stevie G it's good stuff.

My 2-axis 2-button joystick have so far worked fine for any game that I have played, but this game require a button 12 and a couple of other things.

Edit: And by the way, nice game :)

Thanks Ebusiness - not sure you needed another 500 lines of code to do that though. I was working on this too but I have to confess I liked your mouse targetting code so have included this - thankyou for taking the time :)

I've added a shortened version at the top of this thread where you can select the control type from the main menu.

You are welcome

I just got used to my own keysystem, it's good for flexibility, but I didn't reach that point ;)

this is great stevie G! Much fun :D

Very impressive, add the sound and you got a winner. (gonna be hard with just data statement though)

Is it allright if I use this code to learn how to do online networking, level data files, and sound effects?

Fill your boots Mr P! It'd be good to know how you get on though.

Anyone asked for 'in-code' sound effects?
Print "You have a 12 mm stationary machinegun"
Print "The enemy is attacking!"
Const lenght=9600*6-1
Dim sarr#(lenght)
For a=0 To 180 Step 5
	factor#=Sin(a)
	rate=(1+a)*1
	arrsize=lenght/rate+5
	Dim fixs#(arrsize)
	For b=0 To arrsize
		fixs(b)=Rnd(-1,1)*factor*.99990^(b*rate)
	Next
	For b=0 To lenght
		a1#=fixs(b/rate)
		a2#=fixs(b/rate+1)
		b2#=b Mod rate
		b1#=(rate-b2)/rate
		b2=b2/rate
		sarr(b)=sarr(b)+a1*b1+a2*b2
	Next
Next
out=WriteFile("gun.wav")
WriteByte out,16*5+2
WriteByte out,16*4+9
WriteByte out,16*4+6
WriteByte out,16*4+6
WriteByte out,16*6+14
WriteByte out,16*12+2
WriteByte out,16*0+1
WriteByte out,16*0+0
WriteByte out,16*5+7
WriteByte out,16*4+1
WriteByte out,16*5+6
WriteByte out,16*4+5
WriteByte out,16*6+6
WriteByte out,16*6+13
WriteByte out,16*7+4
WriteByte out,16*2+0
WriteByte out,16*1+0
WriteByte out,16*0+0
WriteByte out,16*0+0
WriteByte out,16*0+0
WriteByte out,16*0+1
WriteByte out,16*0+0
WriteByte out,16*0+1
WriteByte out,16*0+0
WriteByte out,16*0+0
WriteByte out,16*7+7
WriteByte out,16*0+1
WriteByte out,16*0+0
WriteByte out,16*0+0
WriteByte out,16*14+14
WriteByte out,16*0+2
WriteByte out,16*0+0
WriteByte out,16*0+2
WriteByte out,16*0+0
WriteByte out,16*1+0
WriteByte out,16*0+0
WriteByte out,16*6+4
WriteByte out,16*6+1
WriteByte out,16*7+4
WriteByte out,16*6+1
WriteInt out,lenght*2+2
For a=0 To lenght
	If a<300 Then sarr(a)=sarr(a)*a/300.0
	If a>20000 Then sarr(a)=sarr(a)+.12*sarr(a-20000)
	WriteShort out,Int(sarr(a)*4000)
Next
CloseFile out
sound=LoadSound("gun.wav")
DeleteFile("gun.wav")
Print "Fire at will!"
timer=MilliSecs()*2
While Not KeyHit(1)
	If KeyDown(57) Then channel=PlaySound(sound)
	While timer>MilliSecs()*2
		Delay 1
	Wend
	timer=timer+125
Wend


woah ebusiness! that's cool! I had no idea you could do that with code!

Well, I just save a wav file and then load it , the problem was to make it sound like a gun. I still havn't got the right sound for a tank, this one is too much bang, and too little boom, but there are plenty of things to adjust, have a go.

I think I will, it looks fairly straight-forward.

Jesus - thanks superb - I had no idea you could do this either!!! I'll have a play about but it looks way over my head - could you explain a bit about what your sending to the .wav file?

Would be good to get the whole thing done in code.

I'd quite like to add a few more powerups if anyone has any suggestions?

Cheers.

Just altered the code so that you can alter the lenght of the sound (Constant 'lenght' holds the value in 1/96000 seconds). I mix noise on different frequencies, each piece of noise is generated by setting a random value, "drawing a straight line" to the next random value located a certain distance (variable: 'rate'), and so forth. If the distance is big the tones will be deep, and if it's small the tones will be high. All these values I mix in the array 'sarr'. I multiply the numbers by a factor that are large for the middle frequences and small for outer (using sine). This: "*.99990^(b*rate)" make the sound fade. Generally, you should alter the way 'rate' and 'factor' are generated, and the number used in above quotation you can make larger to make the fading slower, or smaller to make the fading faster, 1=no fading.

Around the bottom of the code you will see this:
For a=0 To lenght
	If a<300 Then sarr(a)=sarr(a)*a/300.0
	If a>20000 Then sarr(a)=sarr(a)+.12*sarr(a-20000)
	WriteShort out,Int(sarr(a)*4000)
Next
The piece that write the generated data to the file, in the third line you can alter the volume (the volume might "overflow" if you set it too high). The second line generate eccho, 20000 is the interval from the origin of a piece till the eccho, .12 is the strenght of the eccho. The first line will prevent a click sound that the eccho would otherwise generate.

Good luck messing around with it :)

@eBusiness - Do you know what the 1st 40+ bytes represent - is it just standard to the wav format or have you just set it up to be specific to this sound effect?

Sorta half 'n' half, just copied from another file, this is 16 bit, mono, 96000 Hz (open some files with hex editors and you can figure the general system). Note that if you make the file too long the wav definition says that you shall split it up into some sort of chunks. Some programs will not play a file with too long chunks, others don't care (any eventual reason for this system is utterly outdated). I don't know how to do chunks, but if you got problems, you can save a headerless file and open it with a program like GoldWave, therefrom save it as a valid sound format.

By the way, is the sound making going well?

I've been messing around with it for a few hours and made some bizarre sounds but it's quite difficult getting an explosion sound. I used a low soundpitch once created which was better but can't seem to get the initial 'boom'.

I'm not suggesting for minute that you are the WAV meister but as you know far more than me ... I take it I can only do 'white' noise with this - how would I do something a bit more like picking up a key in Gauntlet noise?

Not shure, and I don't really know what sound you are after, but, you will probably need normal tones. A normal beep tone is just a sine curve:
For b=0 To lenght
sarr(b)=sarr(b)+Sin(b/200.0)
Next
You can make it fade like the boom sounds, and eventually tweak the curve to make it another kind of sound (the ^5, not shure exacly how it works):
For b=0 To lenght
sarr(b)=sarr(b)+Sin(b/200.0)^5*.9999^b
Next

oops, already answered :D
Stevie G, can you please show me how to add an online multiplayer option right below the start option? (maybe just put a label where I should put my multiplayer code).

How about this ... just search for all instances of NET to see what I changed. See GAMEstart, NETGAMEstart etc.. Hope this helps you ;)

Graphics3D 1280,960,16,1

Global FONTbig = LoadFont("Tahoma",32,1)
Global FONTsmall = LoadFont("Tahoma", 16, 1 )
SetFont FONTbig
SeedRnd MilliSecs()

Global NETgame
Global JOYSTICK
Global GunTarget
Global KUP = 200
Global KDOWN = 208
Global KLEFT = 203
Global KRIGHT = 205
Global KSELECT = 28
Global HGW = GraphicsWidth()*.5
Global HGH = GraphicsHeight()*.5

Const Detail# = 15
Const FPS = 30
Const FadeSpeed# = .02
Const Gravity# = 0.05
Const tanks = 4
Const bullets = 100
Const particles = 750
Const drums = 8
Const menus = 2
Const FireTurn# = .25
Const MoveTurn# = .1
Const BulletLife# = 30
Const T_TANK = 1
Const T_ARENA = 2
Const T_DRUM = 3
Const T_BULLET = 4
Const AI_Attack = 0
Const AI_Retreat = 1
Const AI_GoForBonus = 2
Const AI_Flee = 3
Const AI_Straffe = 4
Global SOUNDon = False
Global SOUNDexplode
Global SOUNDclang
Global SOUNDhit
Global SOUNDlaser
Global SOUNDmenu
Global SOUNDbonus
Global camera=CreateCamera()
Global menu_cam = CreateCamera()
Global Pivot = CreatePivot()
Global Light = CreateLight()
Global AIPivot = CreatePivot()
Global FrameTimer = CreateTimer(FPS)
Global BulletMesh
Global ParticleMesh
Global DrumMesh
Global BubbleMesh
Global NextBullet
Global NextParticle
Global ParticleTemplate
Global Fade
Global FadeStatus#
Global NX#
Global NZ#
Global MINEactivate, FIREactivate
Global zoom#=6.75
Global GAMEOVER
Global QUIT
Global PLAYERS
Global KILLS
Global DIFFICULTY

Collisions T_TANK,T_TANK,1,2
Collisions T_TANK,T_ARENA,2,2
Collisions T_TANK,T_DRUM,1,2
Collisions T_BULLET,T_ARENA,2,1	
Collisions T_BULLET,T_TANK,1,1
Collisions T_BULLET,T_DRUM,1,1

Type tank_type

	Field NETname#
	Field NETtag
	Field NETtexture
	
	Field ID
	Field Model
	Field Turret
	Field MaxSpeed#
	Field BulletReload
	Field BulletSpeed#
	Field BulletSpread
	Field BulletHeat#
	Field BulletTimer
	Field Shield#
	Field Damage#
	Field Score
	Field ScoreTexture
	Field Respawn
	Field BubbleShield
	Field Life#
	Field LifeBar
	Field LifeRecharge#
	Field Power#
	Field PowerBar
	Field Cooler#
	Field CoolerBar
	Field MineCounter
	Field Mine
	Field Control
	Field r#,g#,b#
	;ai stuff
	Field MoveCounter
	Field FireCounter
	Field MX#, MZ#
	Field FX# , FZ#
	Field Target
End Type

Type bullet_type
	Field ID
	Field Model
	Field Life
	Field Speed#
	Field Damage#
End Type

Type drum_type
	Field ID
	Field Model
	Field Life#
End Type

Type particle_type
	Field Vertex
	Field x#,y#,z#
	Field vx#,vy#,vz#
	Field r,g,b
	Field life#
	Field fade#
	Field size#
	Field weight#
End Type

Type bonus_type
	Field ID
	Field Model
	Field timer
End Type

Type arena_type
	Field Model
	Field Hud
	Field Radar
End Type	

Type menu_type
	Field Model
	Field ID
	Field texture
	Field options
	Field title$
	Field initial
	Field current
	Field height
	Field sub_options[5]
	Field sub_string$[5]
	Field sub_current[5]
End Type

Dim tank.tank_type(tanks-1)
Dim bullet.bullet_type( bullets -1 )
Dim drum.drum_type ( drums - 1)
Dim particle.particle_type ( particles - 1)
Dim menu.menu_type( menus -1 )
Global BONUS.bonus_type = New bonus_type
Global ARENA.arena_type = New arena_type

GAMEinit()

Repeat
	GAMEstart()
	Repeat
		WaitTimer(FrameTimer)
		BULLETSupdate()
		PARTICLESupdate()
		BONUSupdate()
		TANKSupdate()
		UpdateWorld()
		RenderWorld()
		Flip
	Until QUIT Or GAMEOVER  
	If GAMEOVER GAMEend()
Until (1=2)
		
End

;==========================================================
;==========================================================
;==========================================================

Function TANKSupdate()

	;radar
	TurnEntity ARENA\radar,0,5,0

	For t.tank_type = Each tank_type
	
		t\BulletTimer = t\BulletTimer - ( t\BulletTimer > 0 )
								
		If t\Life > 0 
	
			FIREactivate = True
			
			;control
			If t\Control < 2
				If JOYSTICK
					;analogue
					t\MX = JoyX(t\ID)
					t\MZ = -JoyY(t\ID)
					t\FX = JoyRoll(t\ID) / 180.0
					t\FZ = -JoyZ(t\ID)
					MINEactivate = JoyDown( 12,t\ID )
				Else
					;mouse / keyboard
					t\MX = KeyDown( KRIGHT )-KeyDown( KLEFT )
					t\MZ = KeyDown( KUP )-KeyDown( KDOWN )
					FIREactivate = MouseDown(1)
					MINEactivate = MouseDown(2)
				EndIf
			Else
				AI ( t )
			EndIf
				
			;moving
			speed#  = NORMALxz( t\MX , t\MZ ) 
			If speed > 0.3
				PositionEntity Pivot,EntityX(t\Model) + NX * 5 , 0 , EntityZ(t\Model) + NZ * 5
				RotateEntity t\Model,0,EntityYaw(t\Model)+DeltaYaw(t\Model,Pivot) * MoveTurn ,0
				speed = speed * t\MaxSpeed
				TranslateEntity t\Model , NX * speed , (3-EntityY(t\Model)) , NZ * speed 
				PARTICLEnew( t\Model, 0,-1.5,-3, -.1,.1, -.1,.1, -.2,-.1 , 64,80,64,80,64,80, .25,.5, .25 , .1, 1)
			EndIf
			
			;firing - joystick / ai control 
			speed# = NORMALxz( t\FX , t\FZ )
			If speed > 0.5 
				PositionEntity Pivot,EntityX(t\Model) + NX * 5 , 0 , EntityZ(t\Model) + NZ * 5
				yaw# = DeltaYaw(t\Turret,Pivot)
				TurnEntity t\Turret,0, yaw *FireTurn ,0
				If  t\BulletTimer = 0  And  t\Cooler > t\BulletHeat And ( Abs(yaw)< 15 )  
					If t\respawn = 0 And FIREactivate BULLETnew(  t )
				EndIf 
			EndIf
			PositionEntity t\Turret,EntityX(t\Model),3,EntityZ(t\Model),1	
			
			;firing - mouse
			If t\Control < 2 And ( Not JOYSTICK )
				PositionEntity GunTarget,( MouseX() - HGW ) *.145 , 3 , ( HGH - MouseY() ) *.17
				PointEntity t\Turret , GunTarget
				RotateEntity t\Turret,0,EntityYaw( t\Turret ),0
				If t\BulletTimer = 0 And Abs(yaw)< 15  And t\Cooler > t\BulletHeat
					If t\respawn = 0 And FIREactivate BULLETnew(  t )
				EndIf
			End If
			
			;recharge gun & life		
			t\Life = LIMIT( t\Life + t\LifeRecharge, 0, t\Power )
			t\Cooler = LIMIT( t\Cooler + .0075, 0, 1)
	
			;lay / trigger mine
			If t\MineCounter = 1 And MINEactivate MINEnew( t, 0 )
			If t\MineCounter > 1 t\MineCounter = t\mineCounter + 1
			If t\MineCounter > 30 And MINEactivate MINEnew( t,1 )
															
		EndIf
		
		;bonus pickup
		If BONUS\timer > 0 And EntityDistance( BONUS\Model, t\Model ) < 3
			BONUS\timer = 0
			TANKbonus (t , BONUS\ID )
			PARTICLEnew ( BONUS\Model, 0,0,0, 0,0,.5,.5,0,0, 255,255,255,255,0,0, .25,.5, 1 , .01 , 8, 10 )
			SOUNDplay( SOUNDbonus )
		EndIf
			
		;respawn	
		If t\Respawn > 0 TANKrespawn( t )
		
		;update hud
		ScaleEntity t\LifeBar,t\Life,1,1
		ScaleEntity t\PowerBar,t\Power,1,1
		ScaleEntity t\CoolerBar,t\Cooler,1,1
		
		;Net game
		If NETgame PositionEntity t\NETtag, EntityX(t\Model), 10, EntityZ(t\Model )

	Next
	
	;quit
	QUIT = KeyDown(1)
	
	;pause
	If KeyDown(25) MENUshow ( MENU(1) )
		
End Function
	
;==========================================================
;==========================================================
;==========================================================

Function BULLETSupdate()

	For b.bullet_type = Each bullet_type
		
		MoveEntity b\Model,0,0,b\speed
		b\Life = b\Life - (b\Life > 0)
		
		;do collisions
		If CountCollisions(b\Model) > 0
			For ID = 1 To 3
				entity = EntityCollided (b\Model,ID)
				If entity <> 0
					Select ID
						Case T_TANK
							t.tank_type = Object.tank_type( EntityName( entity ) )
							damage# = b\Damage * (1.0 - t\Shield)
							t\Power = LIMIT( t\Power - damage*.25,0.0,1.0)
							t\Life = LIMIT( t\Life - damage,0.0,1.0)
							If t\Life = 0 And t\Respawn = 0
								;destroy tank
								EXPLOSION ( t\Turret, 20 , b\ID, t\r,t\g,t\b, True )
							Else
								;hit tank
								h.tank_type = tank( b\ID )
								HIT ( b\Model, h\r,h\g,h\b, False , SOUNDhit,  True )
							EndIf
						Case T_ARENA
							;hit arena
							HIT ( b\Model, 142,100,142, True, SOUNDclang, False ) 
						Case T_DRUM
							d.drum_type = Object.drum_type( EntityName( entity ) )
							d\Life = LIMIT( d\Life - b\Damage,0.0,2.0)
							If d\Life = 0 
								;destroy drum
								EXPLOSION ( d\Model, 20 , b\ID, 168,116,100, False )
							Else
								;hit drum
								HIT ( b\Model, 168,116,100, True, SOUNDclang, False )
							EndIf	
					End Select
				EndIf
			Next
			b\Life =0 
		EndIf
						
		If b\Life = 0 HideEntity b\Model:ResetEntity b\Model
		
	Next
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLESupdate()

	s = GetSurface( ParticleMesh , 1 )
	t = GetSurface( ParticleTemplate , 1 )
	cv = CountVertices(t)-1
		
	For p.particle_type = Each particle_type
	
		p\Life = LIMIT( p\Life - p\fade ,0 , 1.0 ) 
		
		p\vy = p\vy - Gravity * p\weight
		p\x = p\x + p\vx
		p\y = p\y + p\vy
		p\z = p\z + p\vz
		
		If p\y < p\size 
			p\y = p\size
			p\vx = p\vx * .75
			p\vy = -p\vy*.5
			p\vz = p\vz * .75
		EndIf
		
		For v=0 To cv
			px# = p\x + VertexX(t,v) * p\size
			py# = p\y + VertexY(t,v) * p\size
			pz# = p\z + VertexZ(t,v) * p\size
			VertexCoords s , p\Vertex + v , px, py, pz 
			VertexColor s, p\Vertex + v , p\r , p\g , p\b , .75 * p\Life
		Next

	Next

End Function

;==========================================================
;==========================================================
;==========================================================

Function BONUSupdate()
	
	If BONUS\timer = 0 
		HideEntity BONUS\Model
		If Rand(60)=1
			BONUS\ID = Rand(1,8)
			BONUS\timer = 150
			bx = ( Rand(0,1)*2-1 ) * ( 5 + Rand(0,3) * 10 )
			bz = ( Rand(0,1)*2-1 ) * ( 5 + Rand(0,3) * 10 )
			PositionEntity BONUS\Model,bx,3,bz 
			ShowEntity BONUS\Model
		EndIf
	EndIf
	
	If BONUS\timer > 0
		BONUS\timer = BONUS\timer - 1
		alpha# = 1.0
		If BONUS\timer <= 30 alpha# = Float( BONUS\timer)*.033
		If BONUS\timer > 120 alpha# = Float(150.0 - BONUS\timer ) * .033
		EntityAlpha BONUS\Model,alpha
		TurnEntity BONUS\Model,0,10,0
	EndIf

End Function

;==========================================================
;==========================================================
;==========================================================

Function AI ( t.tank_type )

	t\MoveCounter = t\MoveCounter - (t\MoveCounter > 0 )
	t\FireCounter = t\FireCounter - (t\FireCounter > 0 )
	FireRange# =  ( BulletLife * t\BulletSpeed )
	MINEactivate = ( t\MineCounter = 1 And Rand(60)=1 ) 
	
	If t\Target > -1 
		TargetDistance# = EntityDistance( t\Model, tank(t\target)\Model )
		If tank(t\target)\respawn > 0 t\Target = -1
	Else
		TargetDistance# = 1000.0
		t\FireCounter = 0
	EndIf
	
	;find a new target
	If t\FireCounter = 0	
		FleeX# = 0
		FleeZ# = 0	
		For c.tank_type = Each tank_type
			If t<>c 
				FleeX = FleeX + EntityX(c\Model) * .33
				FleeZ = FleeZ + EntityZ(c\Model) * .33
				CheckDistance# = EntityDistance( t\Model, c\Model ) 
				If c\Respawn = 0 And CheckDistance < TargetDistance 
					t\Target = c\ID
					TargetDistance = CheckDistance 
				EndIf
				
				;Detonate the mine?
				If c\Respawn = 0 And t\MineCounter > 1 
					If EntityDistance( t\mine, c\Model ) < Rand(5, 20) And EntityDistance( t\mine, t\model) > 20
						MINEactivate = True
					EndIf
				EndIf
				
			EndIf
		Next
		t\FireCounter = 20
	EndIf
			
	;shoot at target if within range
	If t\Target > -1 And (TargetDistance < FireRange+10 ) And ( t\Cooler > ( Rand(2,8) *  t\BulletHeat ) )
		c.tank_type = tank(t\Target)
		time# = TargetDistance / t\BulletSpeed
		ox# = c\MaxSpeed * time
		oz# = c\MaxSpeed * time
		PositionEntity Pivot,EntityX(c\Model) + Rnd(-ox,ox) , 0, EntityZ(c\Model) + Rnd(-oz,oz)
		Distance# = NORMALxz ( EntityX( Pivot) - EntityX(t\Model) , EntityZ(Pivot) - EntityZ( t\Model) )
		t\FX = NX
		t\FZ = NZ
	Else
		t\FX = 0
		t\FZ = 0
	EndIf			
	
	;move	
	If t\MoveCounter = 0
	
		MoveRange# = t\MaxSpeed * 10.0 
		BonusRange# = MoveRange * 10 * (BONUS\timer > 0 )
		BonusDistance# = EntityDistance(t\Model,BONUS\Model) 	
									
		;decide on action
		If t\Target > -1 
			Action = AI_Straffe
			PositionEntity AIPivot, EntityX( tank(t\Target)\Model ), 0, EntityZ( tank(t\Target)\Model )
			If TargetDistance >  ( FireRange + 10.0) Action = AI_Attack
			If TargetDistance < ( FireRange - 10.0 ) Action = AI_Retreat
		Else
			Action = AI_Flee
			PositionEntity AIPivot, FleeX, 0, FleeZ
		EndIf
		If BonusDistance <  BonusRange
			Action = AI_GoForBonus
			PositionEntity AIPivot, EntityX( BONUS\Model ), 0, EntityZ( BONUS\Model )
		EndIf
		Distance# = NORMALxz( EntityX( AIPivot ) - EntityX( t\Model), EntityZ( AIPivot ) - EntityZ( t\Model ) ) 
		If Action = AI_Attack Or Action = AI_GoForBonus Distance# = 1000.00
		If Action = AI_Flee Or Action = AI_Retreat Distance# =0.0
		
		;check possible moves			
		choice = -1
		angle = Rand( 0,7 )
		For l = 0 To 7
			angle = (angle + 1 ) Mod 8
			vx# = NX * Cos( angle*45) - NZ * Sin( angle*45)
			vz# = NZ * Cos( angle*45) + NX * Sin(angle*45)
			PositionEntity pivot, EntityX(t\Model)	+ vx * MoveRange , 0, EntityZ( t\Model) + vz * MoveRange
			unused = LinePick ( EntityX(t\Model) , 3 , EntityZ(t\Model) , vx * MoveRange, 0 , vz * MoveRange , 2 )
						
			;If Nothing blocking route
			If PickedEntity() = 0
			
				;Is there a live mine near New point?
				NoGo = False		
				For c.tank_type = Each tank_type
					If c <> t And c\MineCounter > 1 
						If EntityDistance( pivot, c\Mine ) < Rand(5,10) NoGo = True
					EndIf
				Next

				If NoGo = False
					CheckDistance# = EntityDistance( pivot, AIPivot )
					Select Action
					Case AI_Attack, AI_GoForBonus
						If CheckDistance < Distance
							Distance = CheckDistance
							choice = angle
						EndIf
					Case AI_Flee, AI_Retreat
						If CheckDistance > Distance
							Distance = CheckDistance
							choice = angle
						EndIf
					Case AI_straffe
						If angle = 2 Or angle = 6 choice = angle 
					End Select
				EndIf
						
			EndIf
	
		Next

		t\MoveCounter = 10

		If choice > -1
			;found a move
			t\MX = NX * Cos( choice * 45) - NZ * Sin( choice*45)
			t\MZ = NZ * Cos( choice * 45 ) + NX * Sin( choice*45)
		Else
			;no move
			choice = Rand(3,5)
			t\MX = NX * Cos( choice * 45) - NZ * Sin( choice*45)
			t\MZ = NZ * Cos( choice * 45 ) + NX * Sin( choice*45)
		EndIf
			
	EndIf
		
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function FADE ( target# )

	direction# = Sgn(target - FadeStatus )

	Repeat
		FadeStatus = LIMIT( FadeStatus + direction * FadeSpeed , 0.0,1.0)
		EntityAlpha fade, (1.0 - FadeStatus)
		RenderWorld()
		Flip
	Until ( FadeStatus >= target And direction = 1) Or (FadeStatus <= target And direction= -1) Or (direction=0)
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function NORMALxz#( jx# , jz# )

	speed# = Sqr ( jx * jx + jz * jz )
	NX = jx / speed
	NZ = jz / speed

	Return speed

End Function

;==========================================================
;==========================================================
;==========================================================

Function HIT ( entity, r, g, b, Mark, Sound, IsTANK )

	SOUNDplay( Sound )
	If IsTANK 
		PARTICLEnew ( entity, 0,0,0, 0,0,.5,.5,0,0, r,r,g,g,b,b, .4,.6, 1 , .01 , 2, 33 )
		PARTICLEnew( entity, 0,0,0, -.25,.25, -.5,.5, -.5,0 , 255,255,128,255,0,0, .25,.5, .5, .02, 3)
	Else
		PARTICLEnew( entity, 0,0,0, -.25,.25, -.5,.5, -.5,0 , 128,192,128,192,128,192, .2,.3, .5, .01, 3)	
	EndIf
		
	If Mark PARTICLEnew( entity, 0,0,-1, 0,0, 0,0, 0,0 ,16,48,16,48,16,48 ,.2,.3, 0, .05, 1)

End Function

;==========================================================
;==========================================================
;==========================================================

Function EXPLOSION( entity, range#, ID ,r,g,b, IsTank )

	;do explosion
	HideEntity entity
	SOUNDplay( SOUNDexplode )
	PARTICLEnew( entity, 0,0,0, -.25,.25, .5,1.5, -.25,.25 , r,r,g,g,b,b, .5,.75,1, .005,10)
	PARTICLEnew( entity, 0,0,0, -.25,.25, .5,1.5, -.25,.25 , 255,255,128,255,0,0, .25,.5, 1, .01,10)
	PARTICLEnew ( entity, 0,0,0, 0,0,.25,.5,0,0, 255,255,255,255,255,255, .75,1, .5 , 0 , 16, range )
	
	;if tank destroyed start respawn & updatescore for the tank that was responsible
	
	If IsTank
		t.tank_type = Object.tank_type( EntityName( entity ) )
		t\Shield = 1
		t\Respawn = 1
		If t\MineCounter > 1 
			EXPLOSION ( t\Mine, 20, t\ID, t\r,t\g,t\b, False )
		Else
			HideEntity t\Mine
		EndIf
			
		TANKbonus ( t )
		c.tank_type = tank( ID )
		If c<>t 
			PARTICLEnew( entity, 0,3,0, 0,0, .25,.25,0,0 , c\r,c\r,c\g,c\g,c\b,c\b, 1.5,1.5, 0, .01,1)
			SCOREupdate( c , 1 )
		Else
			SCOREupdate( c, -1 )
		EndIf
	EndIf
	
	;check tanks
	For t.tank_type = Each tank_type
		If t\Life > 0 
			distance# = EntityDistance( t\Turret, entity )
			If distance < range
				damage# = ( ( range - distance ) / range ) * (1.0-t\Shield)
				t\Power = LIMIT( t\Power - damage*.25,0.0,1.0)
				t\Life = Limit (t\Life - damage, 0 , 1 )
				If t\Life = 0 EXPLOSION( t\Turret, 20, ID, t\r,t\g,t\b, True )
			EndIf
		EndIf
	Next
	
	;check drums
	For d.drum_type = Each drum_type
		If d\Life > 0
			distance# = EntityDistance( d\Model, entity )
			If distance  < range
				damage# = ( ( range - distance ) / range ) 
				d\Life = Limit (d\Life - damage, 0 , 2 )
				If d\Life = 0 EXPLOSION ( d\Model, 20 , ID, 168,116,100, False )
			EndIf
		EndIf
	Next
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKbonus ( t.tank_type , bonus=0 )

	Select bonus
		Case 0
			t\BulletReload = 9
			t\BulletSpeed = 1.25 
			t\MaxSpeed = .4 
			t\Shield = 0.0
			EntityAlpha t\BubbleShield, 0
			t\Damage = .1 
			t\LifeRecharge = .002
			t\BulletSpread=0
			t\MineCounter = 0
		Case 1
			t\BulletReload =LIMIT(  t\BulletReload - 3, 3,9 ) 
		Case 2
			t\BulletSpeed = LIMIT( t\BulletSpeed + .25,  1.25, 1.75 )
		Case 3
			t\MaxSpeed = Limit ( t\MaxSpeed + .1, .4, .6)
		Case 4
			t\Shield = Limit ( t\Shield + .25, 0, .75 )
			EntityAlpha t\BubbleShield, t\Shield*.5
		Case 5
			t\Damage = Limit ( t\Damage + .05, .1,.2 )
		Case 6
			t\LifeRecharge = LIMIT( t\LifeRecharge + .002, .002, .01 )
		Case 7
			t\BulletSpread = LIMIT( t\BulletSpread+1,0,2 )
		Case 8
			If t\MineCounter = 0 
				t\MineCounter = 1
				ShowEntity t\Mine
				EntityParent t\Mine, t\Model
				PositionEntity t\Mine, 0,0,-2.5
			EndIf
	End Select
	
	t\BulletHeat# = .025 * ( (  t\BulletSpread + 1.0 ) + ( t\Damage * 10.0 ) + ( t\BulletSpeed / 1.25 ) )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKrespawn( t.tank_type )

	t\Respawn = t\Respawn + 1
	
	;stage 0 - smoking
	If t\Respawn < 91
		PARTICLEnew( t\Model, 0,1,0, -.1,.1, .2,.3, -.1,.1 , 64,80,64,80,64,80, .25,.5, 0,.02,1)
	EndIf
	;stage 1 - show BubbleShield									
	If t\Respawn > 90 And t\Respawn <= 120 
		scale# = Float(t\Respawn - 91) / 29.0
		EntityAlpha t\BubbleShield, scale*.5
	EndIf
	;stage 2 - scale Turret, fill energy
	If t\Respawn > 120 And t\Respawn <= 150
		ShowEntity t\Turret
		scale# = Float(t\Respawn - 121) / 29.0
		t\Life = scale
		t\Power = Limit (t\Power + .04, 0.0,1.0) 	
	EndIf
	;stage 3 - hide BubbleShield
	If t\Respawn > 150 And t\Respawn <= 180
		scale# = Float(t\Respawn - 151) / 29.0
		EntityAlpha t\BubbleShield, .5 - scale*.5 
	EndIf
	;stage 4 - back in play
	If t\Respawn = 180 
		t\Respawn = 0
		If t\Shield = 1 t\Shield = 0
	EndIf
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function MINEnew( t.tank_type , action )

	Select action
	Case 0	;drop mine
		EntityParent t\Mine,0 
		t\MineCounter = 2
	Case 1	;detonate mine 
		EXPLOSION ( t\Mine, 20 , t\ID , t\r,t\g,t\b , False )
		t\MineCounter = 0
	End Select
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function BULLETnew( t.tank_type )

	angle# = t\BulletSpread * 5
	SOUNDplay( SOUNDlaser ) 
	t\Cooler = Limit ( t\Cooler - t\BulletHeat , 0, 1.0)
	PARTICLEnew( t\Turret, 0,0,3 , -.25,.25, -.25,.25, 0.05,0.1,  255,255,255,255,255,255, .25,.25, 0, .04,t\BulletSpread+1) 
	
	For fired = -Sgn( t\BulletSpread ) To Sgn( t\BulletSpread ) 
		If t\BulletSpread <>1 Or fired <> 0 
			b.bullet_type = bullet( NextBullet)
			EntityParent b\Model,t\Turret
			EntityColor b\Model,t\r,t\g,t\b
			PositionEntity b\Model,0,0,3
			RotateEntity b\Model,0,fired*angle,0
			scale# = t\Damage*10.0
			ScaleEntity b\Model,1,1,scale
			EntityParent b\Model,0
			ShowEntity b\Model
			b\Life = BulletLife
			b\ID = t\ID
			b\speed = t\BulletSpeed
			b\Damage = t\Damage
			t\BulletTimer = t\BulletReload
			NextBullet = ( NextBullet + 1 ) Mod bullets
		EndIf
	Next
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLEnew( mesh, x#,y#,z#, vx1#,vx2#, vy1#,vy2#, vz1#,vz2#, r1,r2, g1,g2, b1,b2 , s1#,s2# , w#, f#, number, range#=0)

	random# = Rand( -180,180 )

	For l = 1 To number
		p.particle_type = particle( NextParticle)
		If range = 0 
			TFormVector x,y,z, mesh, 0 
		Else
			angle# = 360 * l / number + random
			TFormVector Cos(angle),y,Sin(angle), mesh,0
			f# = 1.0 / ( range * 3.0)
		EndIf	
		p\x = EntityX(mesh,1)+TFormedX()
		p\y = EntityY(mesh,1)+TFormedY()
		p\z = EntityZ(mesh,1)+TFormedZ()
		If range = 0 
			TFormVector Rnd(vx1,vx2),Rnd(vy1,vy2),Rnd(vz1,vz2), mesh, 0
		Else
			TFormVector Cos(angle)*.5,Rnd(vy1,vy2),Sin(angle)*.5,mesh,0
		EndIf
		p\vx = TFormedX() 
		p\vy = TFormedY() 
		p\vz = TFormedZ() 
		p\r = Rand(r1,r2) 
		p\g = Rand(g1,g2) 
		p\b = Rand(b1,b2)
		p\size = Rnd(s1,s2)
		p\weight = w
		p\fade = f
		p\Life = 1.0
		NextParticle = ( NextParticle + 1 ) Mod particles
	Next

End Function 

;==========================================================
;==========================================================
;==========================================================

Function SCOREupdate( t.tank_type , add#=0 )

	t\Score = t\Score + add
	If t\Score = KILLS And ( Not GAMEOVER) GAMEOVER = t\ID+1
	SetBuffer TextureBuffer (t\ScoreTexture)
	Color 0,0,0:Rect 0,0,32,32
	Color t\r,t\g,t\b:Text 16,16,t\Score,1,1
	SetBuffer BackBuffer()
	
End Function 

;==========================================================
;==========================================================
;==========================================================

Function GAMEend()

	Winner = tank(GAMEOVER-1)\Model
	CAMERApoint( EntityX(Winner),EntityY(Winner),EntityZ(Winner), 36.75 , 150 , .1 )
	CAMERApoint( 0,0,0, 6.75 , 60 , .1 )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEstart()

	MENUshow( MENU(0) )
	
	If NETgame NETGAMEstart()
		
	FADE( 0 )
	GAMEreset ()
	FADE( 1 )
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function NETGAMEstart()

	NETtagupdate ( 0, "Stevie G")
	NETtagupdate ( 1, "Mr. Picklesworth")
	NETtagupdate( 2, "BOT")
	NETtagupdate( 3, "BOT")
	
	;blah

End Function

;==========================================================
;==========================================================
;==========================================================

Function NETGAMEupdate()

	;blah
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function NETtagupdate( Player , name$ )

	t.tank_type = tank( Player )

	SetFont FONTsmall

	SetBuffer TextureBuffer(  t\NETtexture )
		Cls
		Color t\r, t\g, t\b
		Text TextureWidth( t\NETtexture )*.5,TextureHeight( t\NETtexture )*.5, name$, 1, 1
	SetBuffer BackBuffer()
	
	SetFont FONTbig
	
End Function

;==========================================================
;==========================================================
;==========================================================


Function CAMERApoint( x#,y#,z#, zoomtarget#, iterations, smooth#, show =True)

	PositionEntity Pivot,x,y,z
		
	For l = 1 To iterations
		TurnEntity camera, DeltaPitch(camera, pivot)*smooth, DeltaYaw( camera, pivot)*smooth, 0
		zoom = zoom + Sgn( zoomtarget - zoom ) 
		CameraZoom camera,  zoom 
		If show
			WaitTimer (FrameTimer)
			PARTICLESupdate()
			RenderWorld()
			Flip
		EndIf
	Next

End Function

;==========================================================
;==========================================================
;==========================================================

Function GAMEreset( )

	;tanks
	For t.tank_type = Each tank_type
		t\Score = 0
		t\BulletTimer=0
		t\Life = 1.0
		t\Power = 1.0
		t\Cooler = 1.0
		t\Respawn = 0
		t\Control = ( t\ID < PLAYERS ) + 2 * ( t\ID >=PLAYERS )
		x# = ( (t\ID=0 Or t\ID=2) - (t\ID=1 Or t\ID=3) )*30
		z# = ( (t\ID=1 Or t\ID=2) - (t\ID=0 Or t\ID=3) )*30
		PositionEntity t\Model,x,3,z
		PointEntity t\Model,ARENA\Model:TurnEntity t\Model,0,180,0
		RotateEntity t\Turret,0,EntityYaw(t\Model),0
		ResetEntity t\Model
		PositionEntity t\Turret,x,3,z
		ShowEntity t\Turret:ScaleEntity t\Turret,1,1,1
		ShowEntity t\Model
		SCOREupdate (t)
		TANKbonus (t)
		ScaleEntity t\LifeBar,t\Life,1,1
		ScaleEntity t\PowerBar,t\Power,1,1
		HideEntity t\Mine
		t\FX = 0:t\FZ = 0
		t\MX = 0:t\MZ = 0
		t\MoveCounter = t\ID * 2 
		t\FireCounter = t\ID * 2
		t\Target = -1
		
		;Net game
		If NETgame
			ShowEntity t\NETtag 
			PositionEntity t\NETtag, x , 10 , z
		Else 
			HideEntity t\NETtag
		EndIf
		
	Next
	
	;bullets
	For w.bullet_type = Each bullet_type
		HideEntity w\Model
		ResetEntity w\Model
	Next
		
	;drums
	For b.drum_type = Each drum_type
		x# = Cos( -22.5 + b\ID *45.0) * 27
		z# = Sin(-22.5 + b\ID * 45.0) * 27
		b\Life = 2.0
		PositionEntity b\Model,x,2,z
		ResetEntity b\Model
		ShowEntity b\Model
	Next
		
	;particles
	For p.particle_type = Each particle_type
		p\Life = 0
	Next
	PARTICLESupdate()
		
	;bonus
	HideEntity BONUS\Model
	BONUS\timer = 0
		
	;misc	
	zoom# = 6.75
	GAMEOVER = False
	
	;Gun Target Reset
	If ( Not JOYSTICK ) And PLAYERS > 0
		ShowEntity GunTarget
		MoveMouse HGW, HGH
	Else
		HideEntity GunTarget
	EndIf
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function LIMIT#( q# , low# , hi# )

	If q < low q = low
	If q > hi q = hi
	Return q
	
End Function

;===================================================
;===================================================
;===================================================

Function WRAP#(q#,lo#,hi#)

	If q < lo Then q = hi + (q-lo)
	If q >= hi Then q = lo + (q-hi)
	Return q
	
End Function


;==========================================================
;==========================================================
;==========================================================

Function SOUNDplay ( sound )
	If SOUNDon 
		new_channel = PlaySound( sound )
	EndIf
End Function

;==========================================================
;==========================================================
;==========================================================

Function MENUshow ( m.menu_type )
	
	;reset
	m\current = m\initial
	MENUupdate( m )
	
	;fade in	
	ShowEntity m\model
	For l=  0 To 30
		RotateEntity m\model, 0,0,l*12
		PositionEntity m\model,0,0,2+(30-l)
		RenderWorld()
		Flip
	Next		
	
	Repeat
	
		;user input
		jx = KeyDown( KRIGHT ) - KeyDown( KLEFT )
		jy = KeyDown( KDOWN) - KeyDown( KUP )
		jb = KeyDown( KSELECT)
		If KeyDown(1) End
				
		;change current option
		If jy <> 0 
			old = m\current
			m\current = LIMIT (m\current + jy, 0, m\options-1 )
			jx=0:jy = (old <> m\current)
		EndIf
		
		;change current sub option
		If jx <> 0  
			old = m\sub_current[m\current]
			m\sub_current[m\current] = WRAP( m\sub_current[m\current] + jx , 0, m\sub_options[m\current] )
			
			If m\id = 0
				;alter players if mouse 
				If m\sub_current[2] = 0 m\sub_current[0] = LIMIT ( m\sub_current[0], 0, 1 )
				;alter players if netgame
				If m\sub_current[3] = 0 m\sub_current[0] = 1
			EndIf
			
			jx = ( old <> m\sub_current[m\current] )
		EndIf
		
		;update menu
		If (jx+jy) <> 0 
			SOUNDplay( SOUNDmenu)
			MENUupdate ( m )
		EndIf
		
		RenderWorld()
		Flip

	Until m\sub_options[m\current] = 1 And jb
	
	SOUNDplay ( SOUNDmenu )

	;fade out
	For l=  0 To 30
		RotateEntity m\model, 0,0,l*12
		PositionEntity m\model,0,0,2+l
		RenderWorld()
		Flip
	Next	
	HideEntity m\model

	;get details
	Select m\ID
	Case 0
		PLAYERS = m\sub_current[0]
		KILLS = ( m\sub_current[1] + 1 ) * 10 
		JOYSTICK = ( m\sub_current[2] = 1 )
		NETgame = 1 - m\sub_current[3] 
	Case 1
		QUIT = ( m\current = 1)
	End Select

End Function

;==========================================================
;==========================================================
;==========================================================

Function MENUupdate(m.menu_type, update_title=False )

	SetBuffer TextureBuffer(m\texture)
	
	If update_title
		Color 64,64,128:Rect 0,0,256,128,1 
		MyText( 128,24,m\title, 1,1, 255,255,0 )
	Else
		Color 64,64,128:Rect 0,44,256,128*m\height-44,1
	EndIf
		
	y= 60
		
	For option = 0 To m\options - 1
	
		r = 255
		g = 155+100*(m\current = option)
		b = 155+100*(m\current = option)
				
		If m\sub_options[option] > 1
			MyText ( 8, 60 + option * 32, Left$( m\sub_string[option], 12), 0,0, r,g,b )
			MyText ( 144, 60 + option * 32 , Mid$(m\sub_string[option], ( m\sub_current[option] + 1)*12,12 ), 0,0, r*.75,g*.75,b*.75 )
		Else
			MyText( 8, 60+option * 32, m\sub_string[option], 0, 0, r, g, b ) 
		EndIf
	Next
	
	SetBuffer BackBuffer()
		
	Delay 100
	
End Function

;==========================================================
;==========================================================
;==========================================================

Function MyText ( x, y, t$, cx, cy, r, g, b )

	Color 48,48,96 
	Text x+4,y+4, t$, cx, cy
	Color 0,0,0
	
	For l = 0 To 4
		px = ( ( l = 0) - (l = 2) ) * 1
		py = ( ( l = 1) - (l = 3) ) * 1
		If l= 4 Color r,g,b
		Text x+ px , y+ py , t$ , cx , cy
	Next

End Function

;==========================================================
;==========================================================
;==========================================================
;==========================================================
; THESE FUNCTIONS ARE ONLY USED ONCE
;==========================================================
;==========================================================
;==========================================================
;==========================================================

Function MENUinit()

	Restore MENUdata
	
	For l = 0 To menus-1
		m.menu_type = New menu_type
		menu( l ) = m
		m\ID = l
		Read m\title
		Read m\options
		Read m\initial
		Read m\height
		w# = .5
		h# = .25 * m\height
		m\model = QUADnew(menu_cam,-90, w,h,1 )
		frame = CYLINDERnew( 3, 128,128,128, 0,0,90, .01,w,.01, 0,h+.01,0 )
		CYLINDERnew( 3, 128,128,128, 0,0,90, .01,w,.01, 0,-(h+.01),0, frame )
		CYLINDERnew( 3, 128,128,128, 0,0,0, .01,h,.01, -(w+.01),0,0, frame )
		CYLINDERnew( 3, 128,128,128, 0,0,0, .01,h,.01, w+.01,0,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,-45, .04,.02,.04, w,h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,-45, .04,.02,.04, -w,-h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,45, .04,.02,.04, w,-h,0, frame )
		CYLINDERnew( 3, 142,100,142, 0,0,45, .04,.02,.04, -w,h,0, frame )
		EntityParent frame, m\model
		PositionEntity frame, 0,0,0 
		PositionEntity m\model,0,0,2
		HideEntity m\model
		m\texture = CreateTexture (256,128 * m\height, 16+32+1)
		EntityTexture m\model, m\texture
		For option = 0 To m\options-1
			Read m\sub_options[option]
			Read m\sub_string[option]
			m\sub_current[option] = ( m\sub_options[option] > 1 )
		Next
		MENUupdate( m, True )
	Next	
	
End Function


;==========================================================
;==========================================================
;==========================================================

Function GAMEinit()

	;light
	RotateEntity light,30,-60,0 
			
	;camera	
	PositionEntity camera,0,250,-250 
	CameraZoom camera, zoom
	CAMERApoint( 0,0,0, 6.75, 1 , 1 , False )
	fade = QUADnew( camera , -90 )
	EntityColor fade,0,0,0
	PositionEntity fade,0,0,6.75
	FADE(0)
	
	;menu camera
	PositionEntity menu_cam,-25000,0,0
	CameraClsMode menu_cam,False,True
	PositionEntity menu_cam,65000,65000,65000
	EntityOrder menu_cam,-999
		
	;bonus
	BONUS\Model = CYLINDERnew(3,255,255,0, -90,0,0, 1,.5,1, 0,0,0, 0,  1,1, 1,1,0 )
						
	;BubbleShield
	BubbleMesh = CYLINDERnew( 3,128,128,128, 0,0,0, 3,2,3, 0,-1,0 ,0, 1,1, 1,1, 0)
	HideEntity BubbleMesh
	EntityShininess BubbleMesh,1
	
	;bullets
	BulletMesh = CYLINDERnew( 6, 128,128,128, -90,0,0, .25,.375,.25, 0,0,0 ,0, 1,2, 1,1, 0, 1)
	HideEntity BulletMesh
	EntityType BulletMesh,T_BULLET
	EntityRadius BulletMesh,.5
	For b=0 To bullets-1
		bullet(b) = New bullet_type
		bullet(b)\Model = CopyEntity ( BulletMesh )
		bullet(b)\Life = 0
		HideEntity bullet(b)\Model
	Next
	
	;tanks
	For l = 0 To tanks-1
		tank(l) = New tank_type
		tank(l)\ID = l
		tank(l)\r = 100+84*(l=1)+42*(l=3)
		tank(l)\g = 100+84*(l=2)+42*(l=3)
		tank(l)\b = 100+84*(l=0)
		TANKinit ( tank(l) )
	Next
	
	;drums
	DrumMesh = CYLINDERnew( 3, 168,116,100, 0,0,0, 1,2,1, 0,0,0 )
	HideEntity DrumMesh
	For l=0 To drums-1
		drum(l) = New drum_type
		drum(l)\Model = CopyEntity ( DrumMesh )
		drum(l)\ID = l
		EntityType drum(l)\Model,T_DRUM
		EntityRadius drum(l)\Model,1.5,2  
		NameEntity drum(l)\Model,Handle(drum(l)) 
		EntityPickMode drum(l)\Model, 1
	Next

	;sounds
	If SOUNDon
		SOUNDexplode = LoadSound("Explode.wav")
		SOUNDclang = LoadSound("Clang.wav")
		SOUNDhit = LoadSound("Hit.wav")
		SOUNDlaser = LoadSound("Laser.wav")
		SOUNDmenu = LoadSound("Menu.wav")
		SOUNDbonus = LoadSound("Bonus.wav")
	EndIf
	
	;GunTarget
	GunTarget = CreateSphere()
	EntityColor GunTarget,50,20,20
	EntityBlend GunTarget,3
	ScaleEntity GunTarget,2,2,2
	HideEntity GunTarget
	
	ARENAinit()
	PARTICLEinit()	
	MENUinit()
	GAMEreset ()
			
End Function

;==========================================================
;==========================================================
;==========================================================

Function PARTICLEinit()

	ParticleTemplate = CYLINDERnew(6, 0,0,0, 0,30,0, 1,.75,1, 0,0,0	, 0, 2,2, 1,1, 2, 1)
	ParticleMesh = CreateMesh()
	EntityFX ParticleMesh,32+2
	EntityShininess ParticleMesh,1
	s= CreateSurface( ParticleMesh )
	gs = GetSurface(ParticleTemplate,1)
	cv = CountVertices ( gs )
	ct = CountTriangles ( gs )
	For l= 0 To particles-1
		vs= l * cv
		particle(l) = New particle_type
		particle(l)\Vertex = vs
		For v=0 To cv-1
			v0 = AddVertex(s,0,0,0)
			VertexNormal s,v0,VertexNX(gs,v),VertexNY(gs,v),VertexNZ(gs,v) 
		Next
		For t=0 To ct - 1
			AddTriangle s, TriangleVertex( gs,t,0)+vs,TriangleVertex( gs,t,1)+vs,TriangleVertex( gs,t,2)+vs
		Next 
	Next
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function ARENAinit()

	Ground=QUADnew( 0, 0, 45,1,45)
	EntityOrder Ground,1
	EntityColor Ground,0,0,0
	EntityBlend Ground,3 
	;EntityAlpha Ground,.75
	
	Plane = CreatePlane()
	EntityColor Plane,0,0,0 
	PositionEntity Plane,0,-.5,0
	EntityAlpha Plane,.5 ;75
	EntityOrder Plane,1
	Mirror = CreateMirror()
	PositionEntity Mirror,0,-.1,0
	Texture=CreateTexture(64,64)
	SetBuffer TextureBuffer(Texture)
	For y=0 To 1:For x=0 To 1
		If ( x + y ) Mod 2 = 0 Color 128,32,128 Else Color 64,16,64 
		Rect x*32,y*32,32,32,1
	Next:Next
			
	SetBuffer BackBuffer()
	ScaleTexture Texture,1.0/9.0,1.0/9.0
	TextureBlend Texture,3	
	EntityTexture Ground,Texture
	FreeTexture Texture
				
	part = CYLINDERnew( 3, 142,100,142, 0,0,0, 2,3,2, -45,3,45 )
	CYLINDERnew( 3, 142,100,142, 90,0,0, 2,3,2, -45,3,0 ,part)
	CYLINDERnew( 3, 128,128,128, 90,0,0, .5,20.5,.5  ,-45,4,-22.5  ,part)
	CYLINDERnew( 3, 128,128,128, 0,0,90, .5,20.5,.5  ,-22.5,4,-45  ,part)
	CYLINDERnew( 3, 128,128,128, 90,0,0, .5,20.5,.5  ,-45,2,-22.5  ,part)
	CYLINDERnew( 3, 128,128,128, 0,0,90, .5,20.5,.5  ,-22.5,2,-45  ,part)
	CYLINDERnew(3, 128,128,128, 90,0,0, .5,4,.5, -20,4.5,25, part)
	CYLINDERnew(3, 128,128,128, 90,0,0, .5,4,.5, -20,1.5,25, part)
	CYLINDERnew(3, 128,128,128, 0,0,90, .5,4,.5, -25,4.5,20, part)
	CYLINDERnew(3, 128,128,128, 0,0,90, .5,4,.5, -25,1.5,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -20,3,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -30,3,20, part)
	CYLINDERnew(3, 142,100,142, 0,0,0, 1,3,1, -20,3,30, part)
	ARENA\model = CYLINDERnew( 3,142,100,142, 0,0,0 ,3,2,3, 0,2,0 ,0, 1,1, 1,.5 )
	ARENA\radar = CYLINDERnew( 3,128,128,128,0,0,0,.5,1,1,0,5,0, 0,1,1 )
	CYLINDERnew( 3,142,100,142,90,0,0,2,2,2,0,8,0, ARENA\radar,2,2, 1, 1 )
	
	EntityType ARENA\Model,T_ARENA
	EntityPickMode ARENA\Model,2
	For l=0 To 3
		temp = CopyMesh(part):RotateMesh temp,0,l*90,0
		AddMesh temp,ARENA\Model:FreeEntity temp
		px = 39*( ( l=3 ) - ( l = 0) ) + 13 * ( (l = 2) - ( l = 1) )
		ARENA\hud = CYLINDERnew( 3, 96,96,96, 0,0,90, .25,8,.25, px,13,45 )
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, 2,.5,1, px - 8.5,12,45, ARENA\hud)
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, 2,.5,1, px + 8.5,12,45, ARENA\hud )
		temp = CYLINDERnew( 3, 96,96,96, 0,0,90, .2,8,.2, px ,10.5,45, ARENA\hud )
	Next
	FreeEntity part
		
End Function

;==========================================================
;==========================================================
;==========================================================

Function TANKinit( t.tank_type )
	
	;Turret
	t\Turret = CYLINDERnew( 6 , 128,128,128 , 0,0,0 , 1,.5,1 , 0,0,0 )
	;ariel
	CYLINDERnew( 4 , t\r,t\g,t\b , 0,0,0 , .1,1,.1 , .5,1,-.5 , t\Turret)
	;hatch
	CYLINDERnew( 4 , t\r,t\g,t\b , 0,0,0 , .3,.25,.3 , -.25,.5,.25,  t\Turret)
	;barrel
	CYLINDERnew( 4 , 128,128,128 , 90,0,0 , .25,.5,.25 ,0,0,1.5, t\Turret )
	;nosel
	CYLINDERnew( 6 , t\r,t\g,t\b , 90,0,0 , .5,.5,.5 , 0,0,2.5 , t\Turret ,0,1 , .75,1)
	;Turret base
	 CYLINDERnew( 6, t\r,t\g,t\b , 0,0,0 , 1.5,.25,1.5 , 0,-.75,0 , t\Turret )
	;Base
	t\Model = CYLINDERnew( 6, 128,128,128 , 0,0,0 , .75,.5,1.75 , 0,1.5,-.5 )
	;Exhaust
	CYLINDERnew( 6, t\r,t\g,t\b , 90,0,0 , .25,.25,.25 , 0,1.5,-2.5 , t\Model ,1,0 )
	EntityRadius t\Model,2.75
	EntityPickMode t\Model, 1
	EntityType t\Model,T_TANK
	;tracks
	For x# = -1.25 To 1.25 Step 2.5
		CYLINDERnew( 6, 64,64,64 , 0,0,90 , 1,.5,2 , x ,1,0 , t\Model ,0,0 )
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .5,.5,.5 , x,1,0 , t\Model ,1,1)
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .4,.4,.4 , x,1,-1 , t\Model ,1,1)
		CYLINDERnew( 3, t\r,t\g,t\b, 0,0,-90 , .4,.4,.4 , x,1,1 , t\Model ,1,1)
	Next
	PositionMesh t\Model,0,-3,0
	NameEntity t\Model,Handle(t)
	NameEntity t\Turret,Handle(t) 
	
	;mine model
	t\Mine = CYLINDERnew( 6 , 128,128,128 , 0,0,0 , 1,.1,1 , 0,-2.8,0 )
	CYLINDERnew( 6 , t\r,t\g,t\b , 0,0,0 , .5,.1,.5 , 0,-2.6,0, t\Mine )
		
	;bubble shield for respawn
	t\BubbleShield = CopyEntity (BubbleMesh, t\Model)
	EntityColor t\BubbleShield,t\r,t\g,t\b
			
	;hud
	px = 39*( ( t\ID=3 ) - ( t\ID = 0) ) +13 * ( (t\ID = 2) - ( t\ID = 1) )
	t\LifeBar = CYLINDERnew( 3, t\r,t\g,t\b, 0,0,90, 1,8,1, 0,0,0 )
	PositionEntity t\LifeBar,px,13,45
	t\PowerBar = CYLINDERnew( 3, t\r*.5,t\g*.5,t\b*.5, 0,0,90, .5,8,.5, 0,0,0 )
	PositionEntity t\PowerBar,px,13,45
	t\CoolerBar = CYLINDERnew( 3, t\r*.75,t\g*.75,t\b*.75, 0,0,90,.5,8,.5,0,0,0 )
	PositionEntity t\CoolerBar,px,10.5,45
	ScoreMesh = QUADnew(Plane,-90,2.5,2.5,1)
	PositionEntity ScoreMesh,px,7.5,45
	t\ScoreTexture = CreateTexture(32,32)
	EntityTexture ScoreMesh, t\ScoreTexture
	
	;Net Tag
	t\NETtag = QUADnew( 0, -90, 8,1,1)
	EntityBlend t\NETtag, 3	
	EntityColor t\NETtag,0,0,0
	t\NETtexture = CreateTexture( 128,16 )
	TextureBlend t\NETtexture,3
	EntityTexture t\NETtag, t\NETtexture
				
End Function

;==========================================================
;==========================================================
;==========================================================

Function CYLINDERnew( segs=12 ,r,g,b, rx,ry,rz, sx#,sy#,sz# , px#,py#,pz# ,parent = 0, b_in#=1.0, t_in#=1.0 , b_wi#=1.0, t_wi#=1.0, fx#=2, de#=Detail )

	segs = segs * de
	
	mesh = CreateMesh():s = CreateSurface(mesh)
	For y# = -1 To 1 Step 2
	
		w# = b_wi * (y=-1) + t_wi * (y=1)
	
		For v = 0 To segs-1
			a# = Float(v) * ( 360.0 / Float(segs) )
			v0 = AddVertex( s,w*Cos(a),y,w*Sin(a) )
			VertexColor s,v0,r,g,b
		Next
	Next
	
	vb = AddVertex(s,0,b_in,0):VertexColor s,vb,r*b_in,g*b_in,b*b_in 
	vt = AddVertex(s,0,-t_in,0):VertexColor s,vt,r*t_in,g*t_in,b*t_in
	
	For v1 = 0 To segs-1
		v2 = ( v1 + 1) Mod segs
		v3 = ( v2 + segs )
		v4 = ( v1 + segs )
		AddTriangle s,vt,v1,v2
		AddTriangle s,vb,v3,v4 
		AddTriangle s,v3,v2,v1
		AddTriangle s,v1,v4,v3
	Next
	
	ScaleMesh mesh,sx,sy,sz	
	RotateMesh mesh,rx,ry,rz
	PositionMesh mesh ,px,py,pz
	UpdateNormals mesh
	EntityFX mesh,fx
	EntityShininess mesh,1.0
	If fx=0 EntityColor mesh,r,g,b
	If parent > 0 
		AddMesh mesh,parent:FreeEntity mesh
	Else
		Return mesh
	EndIf

End Function

;==========================================================
;==========================================================
;==========================================================

Function QUADnew( parent=0, rx=0 ,sx#=1, sy#=1, sz#=1 )

	mesh=CreateMesh(parent)
	surface=CreateSurface(mesh)
	AddVertex surface,-1,0,1,0,0
	AddVertex surface,1,0,1,1,0
	AddVertex surface,1,0,-1,1,1
	AddVertex surface,-1,0,-1,0,1
	AddTriangle surface,0,1,2
	AddTriangle surface,2,3,0
	RotateMesh mesh,rx,0,0	
	ScaleMesh mesh,sx,sy,sz
	EntityFX mesh,1
		
	Return mesh
End Function	

;==========================================================
;==========================================================
;==========================================================

.MENUdata

Data "TANX  by Stevie G"
Data 5, 0, 2
Data 3,"Players     Demo        One         Two         "
Data 5,"Kills       10          20          30          40          50          "
Data 2,"Control     Mouse       JoyPad      "
Data 2,"Net Game    Yes         No          "
Data 1,"Start"

Data "Paused"
Data 2,0,1
Data 1,"Continue"
Data 1,"Quit"


okay thanks for that!