Created an infinite loop. Now refering to brl.system.Confirm

refs gh-17

Rem
	bbdoc: User Input interface
End Rem
Type mxUserInput

	Rem
		bbdoc:Command Line Interface
	End Rem
	Const c_cli:Byte = 0

	Rem
		bbdoc:Graphical User Interface
	End Rem
	Const c_gui:Byte = 1

	Field m_driver:mxUserInputDriver

	Rem
		bbdoc: Factory for initializing mxUserInput object
		about: interface can be either c_cli of c_gui
	End Rem
	Function factory:mxUserInput(interface:Byte)
		Local ui:mxUserInput = New mxUserInput
		Select interface
			Case c_cli
				ui.m_driver = New mxUserInputDriverCLI
			Case c_gui
				ui.m_driver = New mxUserInputDriverGUI
		End Select
		Return ui
	End Function

	Rem
		bbdoc: Ask user to confirm an action
		returns: True or False
	End Rem
	Method Confirm:Byte(question:String)
		Return Self.m_driver.Confirm(question)
	End Method

End Type

Rem
	bbdoc: User Input Driver interface
End Rem
Type mxUserInputDriver Abstract

	Rem
		bbdoc: Ask user to confirm an action
		returns: True or False
	End Rem
	Method Confirm:Byte(question:String) Abstract

End Type

Rem
	bbdoc: UserInpitDriver for Command Line Interface
End Rem
Type mxUserInputDriverCLI Extends mxUserInputDriver

	Rem
		bbdoc: Ask user to confirm an action
		returns: True or False
	End Rem
	Method Confirm:Byte(question:String)
		Local resp:String = Input(question + " ").ToLower()
		If resp = "y" Or resp = "yes"
			Return True
		Else
			Return False
		End If
	End Method

End Type

Type mxUserInputDriverGUI Extends mxUserInputDriver
	Rem
		bbdoc:Ask user to confirm an action
	End Rem
	Method Confirm:Byte(question:String)
		Return brl.system.Confirm(question)
	End Method

End Type

Rem
	bbdoc: User Input interface
End Rem
Type mxUserInput

	Rem
		bbdoc:Command Line Interface
	End Rem
	Const c_cli:Byte = 0

	Rem
		bbdoc:Graphical User Interface
	End Rem
	Const c_gui:Byte = 1

	Field m_driver:mxUserInputDriver

	Rem
		bbdoc: Factory for initializing mxUserInput object
		about: interface can be either c_cli of c_gui
	End Rem
	Function factory:mxUserInput(interface:Byte)
		Local ui:mxUserInput = New mxUserInput
		Select interface
			Case c_cli
				ui.m_driver = New mxUserInputDriverCLI
			Case c_gui
				ui.m_driver = New mxUserInputDriverGUI
		End Select
		Return ui
	End Function

	Rem
		bbdoc: Ask user to confirm an action
		returns: True or False
	End Rem
	Method Confirm:Byte(question:String)
		Return Self.m_driver.Confirm(question)
	End Method

End Type

Rem
	bbdoc: User Input Driver interface
End Rem
Type mxUserInputDriver Abstract

	Rem
		bbdoc: Ask user to confirm an action
		returns: True or False
	End Rem
	Method Confirm:Byte(question:String) Abstract

End Type

Rem
	bbdoc: UserInpitDriver for Command Line Interface
End Rem
Type mxUserInputDriverCLI Extends mxUserInputDriver

	Rem
		bbdoc: Ask user to confirm an action
		returns: True or False
	End Rem
	Method Confirm:Byte(question:String)
		Local resp:String = Input(question + " ").ToLower()
		If resp = "y" Or resp = "yes"
			Return True
		Else
			Return False
		End If
	End Method

End Type

Type mxUserInputDriverGUI Extends mxUserInputDriver
	Rem
		bbdoc:Ask user to confirm an action
	End Rem
	Method Confirm:Byte(question:String)
		Return Confirm(question)
	End Method

End Type


newline was missing

newline was missing

Rem
Copyright (c) 2010 Tim Howard

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
End Rem

Rem
	bbdoc: Maximus 'help' argument implementation.
End Rem
Type mxHelpImpl Extends mxArgumentImplementation

	Method New()
		init(["help", "--help", "-h"])
	End Method

	Rem
		bbdoc: Check the current arguments for errors (according to the specific implementation).
		returns: Nothing.
		about: This method will throw an error if the given arguments are invalid.
	End Rem
	Method CheckArgs()
		Select GetCallConvention()
			Case mxCallConvention.COMMAND ' "help"
				If m_args = Null
					ThrowCommonError(mxCmdErrors.MISSINGPARAMS, "help")
				End If
			Case mxCallConvention.OPTION ' "--help" or "-h"
				If m_args <> Null
					ThrowCommonError(mxOptErrors.DOESNOTTAKEPARAMS, "-h|--help")
				End If
		End Select
	End Method

	Rem
		bbdoc: Get a string describing the typical usage of the argument.
		returns: A string describing the typical usage of the argument.
	End Rem
	Method GetUsage:String()
		Select GetCallConvention()
			Case mxCallConvention.COMMAND
				Return "usage: maximus help <command>~n" + ..
						"e.g. maximus help get"
			Case mxCallConvention.OPTION
				Return "maximus usage: maximus [-v|--version] [-h|--help] COMMAND [ARGS]~n" + ..
						"The most commonly used commands:~n" + ..
						"~tget~tGet the given set modules~n" + ..
						"~tremove~tRemove the given set of modules~n" + ..
						"~tupdate~tUpdate the current set of modules~n" + ..
						"~thelp~tGet help on a specific command~n" + ..
						"Try 'maximus help <command>' for more information on a specific command."
		End Select
	End Method

	Rem
		bbdoc: Execute the implementation's operation.
		returns: Nothing.
	End Rem
	Method Execute()
		Select GetCallConvention()
			Case mxCallConvention.COMMAND
				For Local command:String = EachIn m_args
					If command.ToLower() = "help"
						logger.LogMessage(command + ":~t" + "HELP HELP I'M BEING REPRESSED!")
					Else
						Local argimpl:mxArgumentImplementation = mainapp.m_arghandler.GetArgImplFromAlias(command)
						If argimpl <> Null
							logger.LogMessage(command + ":~t" + argimpl.GetUsage().Replace("~n", "~n~t~t"))
						Else
							logger.LogMessage(command + ":~tCommand not found")
						End If
					End If
				Next
			Case mxCallConvention.OPTION
				logger.LogMessage(GetUsage())
		End Select
	End Method

End Type

Rem
Copyright (c) 2010 Tim Howard

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
End Rem

Rem
	bbdoc: Maximus 'help' argument implementation.
End Rem
Type mxHelpImpl Extends mxArgumentImplementation

	Method New()
		init(["help", "--help", "-h"])
	End Method

	Rem
		bbdoc: Check the current arguments for errors (according to the specific implementation).
		returns: Nothing.
		about: This method will throw an error if the given arguments are invalid.
	End Rem
	Method CheckArgs()
		Select GetCallConvention()
			Case mxCallConvention.COMMAND ' "help"
				If m_args = Null
					ThrowCommonError(mxCmdErrors.MISSINGPARAMS, "help")
				End If
			Case mxCallConvention.OPTION ' "--help" or "-h"
				If m_args <> Null
					ThrowCommonError(mxOptErrors.DOESNOTTAKEPARAMS, "-h|--help")
				End If
		End Select
	End Method

	Rem
		bbdoc: Get a string describing the typical usage of the argument.
		returns: A string describing the typical usage of the argument.
	End Rem
	Method GetUsage:String()
		Select GetCallConvention()
			Case mxCallConvention.COMMAND
				Return "usage: maximus help <command>~n" + ..
						"e.g. maximus help get"
			Case mxCallConvention.OPTION
				Return "maximus usage: maximus [-v|--version] [-h|--help] COMMAND [ARGS]~n" + ..
						"The most commonly used commands:~n" + ..
						"~tget~tGet the given set modules~n" + ..
						"~tremove~tRemove the given set of modules" + ..
						"~tupdate~tUpdate the current set of modules~n" + ..
						"~thelp~tGet help on a specific command~n" + ..
						"Try 'maximus help <command>' for more information on a specific command."
		End Select
	End Method

	Rem
		bbdoc: Execute the implementation's operation.
		returns: Nothing.
	End Rem
	Method Execute()
		Select GetCallConvention()
			Case mxCallConvention.COMMAND
				For Local command:String = EachIn m_args
					If command.ToLower() = "help"
						logger.LogMessage(command + ":~t" + "HELP HELP I'M BEING REPRESSED!")
					Else
						Local argimpl:mxArgumentImplementation = mainapp.m_arghandler.GetArgImplFromAlias(command)
						If argimpl <> Null
							logger.LogMessage(command + ":~t" + argimpl.GetUsage().Replace("~n", "~n~t~t"))
						Else
							logger.LogMessage(command + ":~tCommand not found")
						End If
					End If
				Next
			Case mxCallConvention.OPTION
				logger.LogMessage(GetUsage())
		End Select
	End Method

End Type


Fixed matrix stuff.

Fixed matrix stuff.

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp

Type gxtkApp
	Field ginput:gxtkInput
	Field gaudio:gxtkAudio
	Field ggraphics:gxtkGraphics

	Field dead:Int=0
	Field suspended:Int=0
	Field vloading:Int=0
	Field maxloading:Int=0
	Field updateRate:Int=0
	Field nextUpdate:Float=0
	Field updatePeriod:Float=0
	Field startMillis:Float=0

	Method New()' gxtkApp()
		app=Self
		ggraphics=New gxtkGraphics
	EndMethod

	Method Setup()
		ginput=New gxtkInput
		gaudio=New gxtkAudio

		bb_input_SetInputDevice(ginput)
		bb_audio_SetAudioDevice(gaudio)

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 )

		InvokeOnCreate()
		InvokeOnRender()
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			Local updates:Int = 0

			Local cont:Int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then cont = 0
				If nextUpdate>BlitzMaxMillisecs() Then cont = 0
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				EndIf
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps
			nextUpdate=BlitzMaxMillisecs() +updatePeriod
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadState:String()
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		var state:String=file.data.state;
'		file.close();
'		if( state ) return state;
		Return ""
	EndMethod

	Method SaveState:Int( state:String )
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		file.data.state=state;
'		file.close();
		Return 0
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading
	EndMethod

	Method OnCreate:Int()
		Return 0
	EndMethod

	Method OnUpdate:Int()
		Return 0
	EndMethod

	Method OnSuspend:Int()
		Return 0
	EndMethod

	Method OnResume:Int()
		Return 0
	EndMethod

	Method OnRender:Int()
		Return 0
	EndMethod

	Method OnLoading:Int()
		Return 0
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1
	Field ix:Float=1,iy:Float,jx:Float,jy:Float=1,tx:Float,ty:Float
	Field sx:Float=1,sy:Float=1,rot:Float=0

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method DrawPoint:Int(x:Float, y:Float)
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		Plot nx, ny
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		SetViewport(x, y, w, h) ' NOT TESTED!
		Return 0
	EndMethod

	Method TransX:Float(x:Float, y:Float)
		Return ix*x + jx*y + tx
	EndMethod

	Method TransY:Float(x:Float, y:Float)
		Return iy*x + jy*y + ty
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Self.ix = ix ; Self.iy = iy
		Self.jx = jx ; Self.jy = jy
		Self.tx = tx ; Self.ty = ty
		sx = Sqr( (ix*ix) + (jx*jx) )
		sy = Sqr( (iy*iy) + (jy*jy) )
		rot = -Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		DrawImage(surface.image, nx, ny, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		DrawSubImageRect(surface.image, nx, ny, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		Local nx1:Float = TransX(x1,y1)
		Local ny1:Float = TransY(x1,y1)
		Local nx2:Float = TransX(x2,y2)
		Local ny2:Float = TransY(x2,y2)
		' Need to reset transform so that BlitzMax doesn't try to apply rotation
		SetTransform( 0, 1, 1 )
		BlitzMaxDrawLine(nx1, ny1, nx2, ny2)
		SetTransform( rot, sx, sy )
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		BlitzMaxDrawOval(nx, ny, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		' setting the origin to use the current rotation translation
		SetOrigin(TransX(0,0), TransY(0,0))
		BlitzMaxDrawPoly( vertices )
		SetOrigin(0, 0)
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		BlitzMaxDrawRect(nx, ny, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB

		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyDown( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method KeyHit:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyHit( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method TouchY:Float( index:Int )
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method AccelX:Int()
		return 0
	EndMethod

	Method AccelY:Int()
		return 0
	EndMethod

	Method AccelZ:Int()
		return 0
	EndMethod

	Method SetKeyboardEnabled:Int( enabled:int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0
	Field channels:gxtkChannel[] = New gxtkChannel[33]

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method ChannelState:int( channel:int )
		Return -1
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method PauseMusic:Int()
		Return 0
	EndMethod

	Method ResumeMusic:Int()
		Return 0
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local extension:String = ExtractExt( path)
		If extension = "ogg" Or extension = "wav" Then
			Local sound:TSound = LoadSound("data/"+path)
			If sound Then
				Local gs:gxtkSample = New gxtkSample
				gs.setSound(sound)
				Return gs
			EndIf
		Else
			RuntimeError "BlitzMax can only use ogg and wav file formats"
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp

Type gxtkApp
	Field ginput:gxtkInput
	Field gaudio:gxtkAudio
	Field ggraphics:gxtkGraphics

	Field dead:Int=0
	Field suspended:Int=0
	Field vloading:Int=0
	Field maxloading:Int=0
	Field updateRate:Int=0
	Field nextUpdate:Float=0
	Field updatePeriod:Float=0
	Field startMillis:Float=0

	Method New()' gxtkApp()
		app=Self
		ggraphics=New gxtkGraphics
	EndMethod

	Method Setup()
		ginput=New gxtkInput
		gaudio=New gxtkAudio

		bb_input_SetInputDevice(ginput)
		bb_audio_SetAudioDevice(gaudio)

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 )

		InvokeOnCreate()
		InvokeOnRender()
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			Local updates:Int = 0

			Local cont:Int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then cont = 0
				If nextUpdate>BlitzMaxMillisecs() Then cont = 0
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				EndIf
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps
			nextUpdate=BlitzMaxMillisecs() +updatePeriod
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadState:String()
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		var state:String=file.data.state;
'		file.close();
'		if( state ) return state;
		Return ""
	EndMethod

	Method SaveState:Int( state:String )
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		file.data.state=state;
'		file.close();
		Return 0
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading
	EndMethod

	Method OnCreate:Int()
		Return 0
	EndMethod

	Method OnUpdate:Int()
		Return 0
	EndMethod

	Method OnSuspend:Int()
		Return 0
	EndMethod

	Method OnResume:Int()
		Return 0
	EndMethod

	Method OnRender:Int()
		Return 0
	EndMethod

	Method OnLoading:Int()
		Return 0
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method DrawPoint:Int(x:Float, y:Float)
		Plot x, y
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		SetViewport(x, y, w, h) ' NOT TESTED!
		Return 0
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Local sx:Float = Sqr( (ix*ix) + (jx*jx) )
		Local sy:Float = Sqr( (iy*iy) + (jy*jy) )
		Local rot:Float = -Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		SetOrigin( tx, ty )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		DrawImage(surface.image, x, y, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		DrawSubImageRect(surface.image, x, y, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		BlitzMaxDrawLine(x1, y1, x2, y2)
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawOval(x, y, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		BlitzMaxDrawPoly( vertices )
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawRect(x, y, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB

		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyDown( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method KeyHit:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyHit( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method TouchY:Float( index:Int )
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method AccelX:Int()
		return 0
	EndMethod

	Method AccelY:Int()
		return 0
	EndMethod

	Method AccelZ:Int()
		return 0
	EndMethod

	Method SetKeyboardEnabled:Int( enabled:int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0
	Field channels:gxtkChannel[] = New gxtkChannel[33]

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method ChannelState:int( channel:int )
		Return -1
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method PauseMusic:Int()
		Return 0
	EndMethod

	Method ResumeMusic:Int()
		Return 0
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local extension:String = ExtractExt( path)
		If extension = "ogg" Or extension = "wav" Then
			Local sound:TSound = LoadSound("data/"+path)
			If sound Then
				Local gs:gxtkSample = New gxtkSample
				gs.setSound(sound)
				Return gs
			EndIf
		Else
			RuntimeError "BlitzMax can only use ogg and wav file formats"
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******


[app] (v0.3) 	Added OnExit method to TDApp


resize tweaks

resize tweaks

' Blue Moon
' Lua-table implementation
' uses simple linear probing for collision resolution and a shockingly naive reallocator

SuperStrict

Import "blueallocator.bmx"

Private
Extern
	Function frexp:Double(d:Double, Exp:Int Ptr) = "frexp"
End Extern
Public

Type BlueTable Final
	' retrieve the slot *for* a key's value, null if no available slot exists (to be used by non-raw index/set operations)
	Function GetSlot:Long Ptr(tbl:Byte Ptr, key:Long, keyslot:Long Ptr Ptr)	'keyslot will return the slot for the key if it's not an array-slot
		Local tag:Int = Int Ptr(Varptr(key))[1], idx:Int
		Const NILTAG:Int = BlueTypeTag.NANBOX | BlueTypeTag.NIL

		If tag = BlueTypeTag.NANBOX | BlueTypeTag.STR	'string - common case
			idx = Int Ptr(Int(key))[1]
		ElseIf tag & BlueTypeTag.NANBOX_CHK <> BlueTypeTag.NANBOX	'number
			Local d:Double ; Long Ptr(Varptr(d))[0] = key ; idx = Abs Int(d)
			If d = idx	'int key
				Local arr:Byte Ptr = Byte Ptr Ptr(tbl)[3]
				If arr And idx < Int Ptr(arr)[-1] Then Return Long Ptr(arr) + idx	'if this is nil, it won't be in the hash part anyway
			Else
				d = frexp(d, Varptr(idx)) * ($7fffffff - 1024)	'INT_MAX - DBL_MAX_EXP
				idx = Abs(idx) + Int(d)
			EndIf
		ElseIf tag = NILTAG	'nil -> nil
			keyslot[0] = Long Ptr(1) ; Return Null
		Else	'pointer (needs improvement)
			idx = Int(key) Shr 3
		EndIf

		Local hashpart:Byte Ptr = Byte Ptr Ptr(tbl)[2]
		If hashpart
			Local hsize:Int = 1 Shl Int Ptr(hashpart)[-1]
			idx = idx & (hsize - 1)	'apparently this is faster than Mod
			For Local i:Int = idx Until hsize	'naive linear probe
				Local kp:Long Ptr = Long Ptr(hashpart) + 2 * i
				If kp[0] = key Or Int Ptr(kp)[1] = NILTAG
					keyslot[0] = kp ; Return kp + 1
				EndIf
			Next
			For Local i:Int = 0 Until idx	'yep
				Local kp:Long Ptr = Long Ptr(hashpart) + 2 * i
				If kp[0] = key Or Int Ptr(kp)[1] = NILTAG
					keyslot[0] = kp ; Return kp + 1
				EndIf
			Next
		EndIf

		Return Null
	End Function

	' retrieve a value from a table, or nil
	Function RawGet:Long(tbl:Byte Ptr, key:Long)
		Local keyp:Long Ptr = Null, retp:Long Ptr = GetSlot(tbl, key, Varptr(keyp)), ret:Long
		If retp And (keyp <> Long Ptr(1))
			ret = retp[0]
		Else
			Int Ptr(Varptr(ret))[1] = BlueTypeTag.NANBOX | BlueTypeTag.NIL	'why can't i shift longs?
		EndIf
		Return ret
	End Function

	' put a value into a table, resizing it if necessary
	Function RawSet(mem:BlueVMMemory, tbl:Byte Ptr, key:Long, val:Long)
		Local keyp:Long Ptr = Null, valp:Long Ptr = GetSlot(tbl, key, Varptr(keyp))
		If valp
			mem.Write(valp, val)
			If keyp
				Local hashpart:Int Ptr = Int Ptr Ptr(tbl)[2]
				If Int Ptr(valp)[1] <> BlueTypeTag.NILBOX
					If keyp[0] <> key Then mem.Write(keyp, key) ; hashpart[-2] :+ 1
				Else	'nil value = remove element
					mem.Write(keyp, val) ; hashpart[-2] :- 1
				EndIf
			EndIf
		ElseIf keyp <> Long Ptr(1)
			Resize(mem, tbl, key) ; RawSet mem, tbl, key, val
		EndIf
	End Function

	' resize a table (adds space for the extra key, tries to reshuffle integer-keyed elements for optimal space usage)
	Function Resize(mem:BlueVMMemory, tbl:Byte Ptr, key:Long)
		Local hashpart:Byte Ptr = Byte Ptr Ptr(tbl)[2], arraypart:Byte Ptr = Byte Ptr Ptr(tbl)[3]
		Const NILTAG:Int = BlueTypeTag.NANBOX | BlueTypeTag.NIL

		Local asize:Int = 0, tsize:Int = 0, numcount:Int[32]
		If arraypart	'compute new array size
			If Int Ptr(arraypart)[1] <> NILTAG Then numcount[0] = 1
			Local cell:Int = 1, cellp2:Int = 2 ^ cell
			For Local i:Int = 1 Until Int Ptr(arraypart)[-1]
				If i >= cellp2 Then cell :+ 1 ; cellp2 = 2 ^ cell
				If Int Ptr(arraypart)[i * 2 + 1] <> NILTAG Then numcount[cell] :+ 1
			Next
		EndIf
		If hashpart
			For Local i:Int = 0 Until Int Ptr(hashpart)[-2]
				Local tag2:Int = Int Ptr(hashpart)[i * 4 + 1]
				If tag2 & BlueTypeTag.NANBOX_CHK <> BlueTypeTag.NANBOX
					Local d:Double = Double Ptr(hashpart)[i * 2]
					If d = Abs Int(d) Then numcount[Ceil(Log(d + 1) / Log(2))] :+ 1 Else tsize :+ 1
				Else
					tsize :+ 1	'get started on new table size
				EndIf
			Next
		EndIf

		Local tag:Int = Int Ptr(Varptr(key))[1]	'add key to the appropriate one
		If tag & BlueTypeTag.NANBOX_CHK <> BlueTypeTag.NANBOX
			Local d:Double = Double Ptr(Varptr(key))[0]
			If d = Abs Int(d) Then numcount[Ceil(Log(d + 1) / Log(2))] :+ 1 Else tsize :+ 1
		Else
			tsize :+ 1
		EndIf
		Local addtotsize:Int = 0
		For Local i:Int = 0 Until 32	'tally up
			addtotsize :+ numcount[i]
			If i Then numcount[i] :+ numcount[i - 1]
			If numcount[i] > 2 ^ i / 2 Then asize = 2 ^ i ; addtotsize = 0
		Next
		tsize :+ addtotsize
		If arraypart <> Null	'complete new table size with any discarded array elements
			For Local i:Int = asize Until Int Ptr(arraypart)[-1]
				If Int Ptr(arraypart)[i * 2 + 1] <> NILTAG Then tsize :+ 1
			Next
		EndIf
		tsize = Ceil(Log(tsize) / Log(2)) + 1	'go from count to power

		Local newarray:Byte Ptr = Null, newtable:Byte Ptr = Null	'allocate and copy
		Local oldasize:Int = 0 ; If arraypart Then oldasize = Int Ptr(arraypart)[-1]
		Local oldtsize:Int = 0 ; If hashpart Then oldtsize = Int Ptr(hashpart)[-1]

		If asize <> oldasize
			newarray = mem.AllocObject(asize * 8 + 8, BlueTypeTag.ARR) + 8
			For Local i:Int = 0 Until asize
				Long Ptr(newarray)[i] = BlueVMMemory.NIL
			Next
			Int Ptr(newarray)[-1] = asize ; Byte Ptr Ptr(tbl)[3] = newarray
		ElseIf asize = 0
			Int Ptr(tbl)[3] = 0
		Else
			newarray = arraypart ; arraypart = Null
		EndIf
		If tsize <> oldtsize
			newtable = mem.AllocObject(2 ^ tsize * 16 + 8, BlueTypeTag.HASH)
			For Local i:Int = 0 Until 2 ^ tsize * 2
				Long Ptr(newtable)[i] = BlueVMMemory.NIL
			Next
			Int Ptr(newtable)[-1] = tsize ; Int Ptr(newtable)[-2] = 0
			Byte Ptr Ptr(tbl)[2] = newtable
		ElseIf tsize = 0
			Int Ptr(tbl)[2] = 0
		Else
			newtable = hashpart ; hashpart = Null
		EndIf

		If arraypart
			For Local i:Int = 0 Until oldasize	'reinsert values
				Local key:Long ; Double Ptr(Varptr(key))[0] = i
				RawSet mem, tbl, key,Long Ptr(arraypart)[i]
			Next
		EndIf
		If hashpart
			For Local i:Int = 0 Until 2 ^ oldtsize
				RawSet mem, tbl, Long Ptr(hashpart)[i * 2], Long Ptr(hashpart)[i * 2 + 1]
			Next
		EndIf
	End Function
End Type

' Blue Moon
' Lua-table implementation
' uses simple linear probing for collision resolution and a shockingly naive reallocator

SuperStrict

Import "blueallocator.bmx"

Private
Extern
	Function frexp:Double(d:Double, Exp:Int Ptr) = "frexp"
End Extern
Public

Type BlueTable Final
	' retrieve the slot *for* a key's value, null if no available slot exists (to be used by non-raw index/set operations)
	Function GetSlot:Long Ptr(tbl:Byte Ptr, key:Long, keyslot:Long Ptr Ptr)	'keyslot will return the slot for the key if it's not an array-slot
		Local tag:Int = Int Ptr(Varptr(key))[1], idx:Int
		Const NILTAG:Int = BlueTypeTag.NANBOX | BlueTypeTag.NIL

		If tag = BlueTypeTag.NANBOX | BlueTypeTag.STR	'string - common case
			idx = Int Ptr(Int(key))[1]
		ElseIf tag & BlueTypeTag.NANBOX_CHK <> BlueTypeTag.NANBOX	'number
			Local d:Double ; Long Ptr(Varptr(d))[0] = key ; idx = Abs Int(d)
			If d = idx	'int key
				Local arr:Byte Ptr = Byte Ptr Ptr(tbl)[3]
				If arr And idx < Int Ptr(arr)[-1] Then Return Long Ptr(arr) + idx	'if this is nil, it won't be in the hash part anyway
			Else
				d = frexp(d, Varptr(idx)) * ($7fffffff - 1024)	'INT_MAX - DBL_MAX_EXP
				idx = Abs(idx) + Int(d)
			EndIf
		ElseIf tag = NILTAG	'nil -> nil
			keyslot[0] = Long Ptr(1) ; Return Null
		Else	'pointer (needs improvement)
			idx = Int(key) Shr 3
		EndIf

		Local hashpart:Byte Ptr = Byte Ptr Ptr(tbl)[2]
		If hashpart
			Local hsize:Int = 1 Shl Int Ptr(hashpart)[-1]
			idx = idx & (hsize - 1)	'apparently this is faster than Mod
			For Local i:Int = idx Until hsize	'naive linear probe
				Local kp:Long Ptr = Long Ptr(hashpart) + 2 * i
				If kp[0] = key Or Int Ptr(kp)[1] = NILTAG
					keyslot[0] = kp ; Return kp + 1
				EndIf
			Next
			For Local i:Int = 0 Until idx	'yep
				Local kp:Long Ptr = Long Ptr(hashpart) + 2 * i
				If kp[0] = key Or Int Ptr(kp)[1] = NILTAG
					keyslot[0] = kp ; Return kp + 1
				EndIf
			Next
		EndIf

		Return Null
	End Function

	' retrieve a value from a table, or nil
	Function RawGet:Long(tbl:Byte Ptr, key:Long)
		Local keyp:Long Ptr = Null, retp:Long Ptr = GetSlot(tbl, key, Varptr(keyp)), ret:Long
		If retp And (keyp <> Long Ptr(1))
			ret = retp[0]
		Else
			Int Ptr(Varptr(ret))[1] = BlueTypeTag.NANBOX | BlueTypeTag.NIL	'why can't i shift longs?
		EndIf
		Return ret
	End Function

	' put a value into a table, resizing it if necessary
	Function RawSet(mem:BlueVMMemory, tbl:Byte Ptr, key:Long, val:Long)
		Local keyp:Long Ptr = Null, valp:Long Ptr = GetSlot(tbl, key, Varptr(keyp))
		If valp
			mem.Write(valp, val)
			If keyp
				Local hashpart:Int Ptr = Int Ptr Ptr(tbl)[2]
				If Int Ptr(valp)[1] <> BlueTypeTag.NILBOX
					If keyp[0] <> key Then mem.Write(keyp, key) ; hashpart[-2] :+ 1
				Else	'nil value = remove element
					mem.Write(keyp, val) ; hashpart[-2] :- 1
				EndIf
			EndIf
		ElseIf keyp <> Long Ptr(1)
			Resize(mem, tbl, key) ; RawSet mem, tbl, key, val
		EndIf
	End Function

	' resize a table (adds space for the extra key, tries to reshuffle integer-keyed elements for optimal space usage)
	Function Resize(mem:BlueVMMemory, tbl:Byte Ptr, key:Long)
		Local hashpart:Byte Ptr = Byte Ptr Ptr(tbl)[2], arraypart:Byte Ptr = Byte Ptr Ptr(tbl)[3]
		Const NILTAG:Int = BlueTypeTag.NANBOX | BlueTypeTag.NIL

		Local asize:Int = 0, tsize:Int = 0, numcount:Int[32]
		If arraypart	'compute new array size
			If Int Ptr(arraypart)[1] <> NILTAG Then numcount[0] = 1
			Local cell:Int = 1, cellp2:Int = 2 ^ cell
			For Local i:Int = 1 Until Int Ptr(arraypart)[-1]
				If i >= cellp2 Then cell :+ 1 ; cellp2 = 2 ^ cell
				If Int Ptr(arraypart)[i * 2 + 1] <> NILTAG Then numcount[cell] :+ 1
			Next
		EndIf
		If hashpart
			For Local i:Int = 0 Until Int Ptr(hashpart)[-2]
				Local tag2:Int = Int Ptr(hashpart)[i * 4 + 1]
				If tag2 & BlueTypeTag.NANBOX_CHK <> BlueTypeTag.NANBOX
					Local d:Double = Double Ptr(hashpart)[i * 2]
					If d = Abs Int(d) Then numcount[Ceil(Log(d + 1) / Log(2))] :+ 1 Else tsize :+ 1
				Else
					tsize :+ 1	'get started on new table size
				EndIf
			Next
		EndIf

		Local tag:Int = Int Ptr(Varptr(key))[1]	'add key to the appropriate one
		If tag & BlueTypeTag.NANBOX_CHK <> BlueTypeTag.NANBOX
			Local d:Double = Double Ptr(Varptr(key))[0]
			If d = Abs Int(d) Then numcount[Ceil(Log(d + 1) / Log(2))] :+ 1 Else tsize :+ 1
		Else
			tsize :+ 1
		EndIf
		Local addtotsize:Int = 0
		For Local i:Int = 0 Until 32	'tally up
			addtotsize :+ numcount[i]
			If i Then numcount[i] :+ numcount[i - 1]
			If numcount[i] > 2 ^ i / 2 Then asize = 2 ^ i ; addtotsize = 0
		Next
		tsize :+ addtotsize
		If arraypart <> Null	'complete new table size with any discarded array elements
			For Local i:Int = asize Until Int Ptr(arraypart)[-1]
				If Int Ptr(arraypart)[i * 2 + 1] <> NILTAG Then tsize :+ 1
			Next
		EndIf
		tsize = Ceil(Log(tsize + 1) / Log(2))	'go from count to power

		Local newarray:Byte Ptr = Null, newtable:Byte Ptr = Null	'allocate and copy
		Local oldasize:Int = 0 ; If arraypart Then oldasize = Int Ptr(arraypart)[-1]
		Local oldtsize:Int = 0 ; If hashpart Then oldtsize = Int Ptr(hashpart)[-1]

		If asize <> oldasize
			newarray = mem.AllocObject(asize * 8 + 8, BlueTypeTag.ARR) + 8
			For Local i:Int = 0 Until asize
				Int Ptr(newarray)[i * 2 + 1] = NILTAG
			Next
			Int Ptr(newarray)[-1] = asize ; Byte Ptr Ptr(tbl)[3] = newarray
		ElseIf asize = 0
			Int Ptr(tbl)[3] = 0
		Else
			newarray = arraypart ; arraypart = Null
		EndIf
		If tsize <> oldtsize
			newtable = mem.AllocObject(2 ^ tsize * 16 + 8, BlueTypeTag.HASH)
			For Local i:Int = 0 Until 2 ^ tsize * 2
				Int Ptr(newtable)[i * 2 + 1] = NILTAG
			Next
			Int Ptr(newtable)[-1] = tsize ; Int Ptr(newtable)[-2] = 0
			Byte Ptr Ptr(tbl)[2] = newtable
		ElseIf tsize = 0
			Int Ptr(tbl)[2] = 0
		Else
			newtable = hashpart ; hashpart = Null
		EndIf

		If arraypart
			For Local i:Int = 0 Until oldasize	'reinsert values
				Local key:Long ; Double Ptr(Varptr(key))[0] = i
				RawSet mem, tbl, key,Long Ptr(arraypart)[i]
			Next
		EndIf
		If hashpart
			For Local i:Int = 0 Until 2 ^ oldtsize
				RawSet mem, tbl, Long Ptr(hashpart)[i * 2], Long Ptr(hashpart)[i * 2 + 1]
			Next
		EndIf
	End Function
End Type


Fixed matrix build issue.

Fixed matrix build issue.

Strict

Import "entity.bmx"
Import "worldconfig.bmx"

Const CAMMODE_NONE	= 0
Const CAMMODE_PERSP	= 1
Const CAMMODE_ORTHO	= 2

Const CLSMODE_COLOR	= 1
Const CLSMODE_DEPTH	= 2

Const FOGMODE_NONE		= 0
Const FOGMODE_LINEAR	= 1

Type TCamera Extends TEntity
	Field _projmode
	Field _fogmode,_fogr#,_fogg#,_fogb#,_fognear#,_fogfar#
	Field _viewx,_viewy,_viewwidth,_viewheight
	Field _clsmode,_near#,_far#
	Field _zoom#

	Field _modelview:TMatrix=New TMatrix, _projection:TMatrix=New TMatrix
	Field _viewport[4], _frustum:TFrustum
	Field _update = True

	Method Init:TCamera(config:TWorldConfig,parent:TEntity)
		Super.Init(config, parent)
		SetMode CAMMODE_PERSP
		SetFogMode FOGMODE_NONE
		SetFogRange 1,1000
		SetViewport 0,0,_config.Width,_config.Height
		SetClsMode CLSMODE_COLOR|CLSMODE_DEPTH
		SetColor 0,0,0
		SetRange 1,1000
		SetZoom 1.0
		Return Self
	End Method

	Method Lists[]()
		Return Super.Lists() + [WORLDLIST_CAMERA]
	End Method

	Method CopyData:TEntity(entity:TEntity)
		Local camera:TCamera = TCamera(entity)
		Local red,green,blue,fognear#,fogfar#,x,y,width,height,near#,far#
		camera.GetFogColor red,green,blue
		camera.GetFogRange fognear,fogfar
		camera.GetViewport x,y,width,height
		camera.GetRange near,far

		SetMode camera.GetMode()
		SetFogMode camera.GetFogMode()
		SetFogColor red,green,blue
		SetFogRange fognear,fogfar
		SetViewport x,y,width,height
		SetClsMode camera.GetClsMode()
		SetRange near,far
		SetZoom camera.GetZoom()
		Return Super.CopyData(entity)
	End Method

	Method Copy:TCamera(parent:TEntity=Null)
		Return TCamera(Super.Copy_(parent))
	End Method

	Method GetMode()
		Return _projmode
	End Method
	Method SetMode(mode)
		_projmode=mode
	End Method

	Method GetFogMode()
		Return _fogmode
	End Method
	Method SetFogMode(mode)
		_fogmode=mode
	End Method

	Method GetFogColor(red Var,green Var,blue Var)
		red=_fogr*255.0;green=_fogg*255.0;blue=_fogb*255.0
	End Method
	Method SetFogColor(red,green,blue)
		_fogr=red/255.0;_fogg=green/255.0;_fogb=blue/255.0
	End Method

	Method GetFogRange(near# Var,far# Var)
		near=_fognear;far=_fogfar
	End Method
	Method SetFogRange(near#,far#)
		_fognear=Max(near,0);_fogfar=Max(far,0.001)
	End Method

	Method GetViewport(x Var,y Var,width Var,height Var)
		x=_viewx;y=_viewy;width=_viewwidth;height=_viewheight
	End Method
	Method SetViewport(x,y,width,height)
		_viewx=x;_viewy=y;_viewwidth=width;_viewheight=height
	End Method

	Method GetClsMode()
		Return _clsmode
	End Method
	Method SetClsMode(mode)
		_clsmode=mode
	End Method

	Method GetRange(near# Var,far# Var)
		near=_near;far=_far
	End Method
	Method SetRange(near#,far#)
		_near=near;_far=far
	End Method

	Method GetZoom#()
		Return _zoom
	End Method
	Method SetZoom(zoom#)
		_zoom=zoom
	End Method

	Method GetFOV#()
		Return ATan(_zoom / 1.0)
	End Method
	Method SetFOV(angle#)
		_zoom = 1.0 / Tan(angle / 2.0)
	End Method

	Method GetEye:TRay()
		Local x#,y#,z#,dx#,dy#,dz#=1.0,o:TVector,d:TVector
		GetPosition x,y,z,True
		_matrix.TransformVec3 dx,dy,dz
		Return New TRay.Create(New TVector.Create3(x,y,z),New TVector.Create3(dx-x,dy-y,dz-x))
	End Method

	Method UpdateMatrices()
		_viewport[0]=_viewx
		_viewport[1]=_viewy
		_viewport[2]=_viewwidth
		_viewport[3]=_viewheight

		_modelview = _matrix.Inverse()

		Local ratio#=(Float(_viewwidth)/_viewheight)
		_projection = TMatrix.Scale(1,1,-1).Multiply(TMatrix.PerspectiveFovRH(ATan((1.0/(_zoom*ratio)))*2.0,ratio,_near,_far))

		_frustum=TFrustum.Extract(_modelview, _projection)

		_update = False
	End Method

	Method Project(target:Object,x# Var,y# Var, offset#[] = Null)
		If _update UpdateMatrices()
		Local z#
		TEntity.GetTargetPosition target,x,y,z

		If offset
			x :+ offset[0]
			y :+ offset[1]
			z :+ offset[2]
		EndIf

		Return TMatrix.Project(_modelview,_projection,_viewport,x,y,z)
	End Method

	Method Unproject(wx#,wy#,wz#,x# Var,y# Var,z# Var)
		If _update UpdateMatrices()
		Return TMatrix.Unproject(_modelview,_projection,_viewport,wx,_viewport[3]-wy,wz,x,y,z)
	End Method

	Method InView#(target:Object)
		Local x#,y#,z#,radius#
		Local entity:TEntity=TEntity(target),point#[]=Float[](target)
		If entity
			entity.GetCullParams x,y,z,radius
		ElseIf point
			x=point[0];y=point[1];z=point[2];radius=point[3]
		Else
			Return 0
		EndIf
		Return _frustum.IntersectsPoint(x,y,z,radius)
	End Method
End Type

Strict

Import "entity.bmx"
Import "worldconfig.bmx"

Const CAMMODE_NONE	= 0
Const CAMMODE_PERSP	= 1
Const CAMMODE_ORTHO	= 2

Const CLSMODE_COLOR	= 1
Const CLSMODE_DEPTH	= 2

Const FOGMODE_NONE		= 0
Const FOGMODE_LINEAR	= 1

Type TCamera Extends TEntity
	Field _projmode
	Field _fogmode,_fogr#,_fogg#,_fogb#,_fognear#,_fogfar#
	Field _viewx,_viewy,_viewwidth,_viewheight
	Field _clsmode,_near#,_far#
	Field _zoom#

	Field _modelview:TMatrix=New TMatrix
	Field _projection:TMatrix=New TMatrix
	Field _viewport[4]
	Field _frustum:TFrustum

	Method Init:TCamera(config:TWorldConfig,parent:TEntity)
		Super.Init(config, parent)
		SetMode CAMMODE_PERSP
		SetFogMode FOGMODE_NONE
		SetFogRange 1,1000
		SetViewport 0,0,_config.Width,_config.Height
		SetClsMode CLSMODE_COLOR|CLSMODE_DEPTH
		SetColor 0,0,0
		SetRange 1,1000
		SetZoom 1.0
		Return Self
	End Method

	Method Lists[]()
		Return Super.Lists() + [WORLDLIST_CAMERA]
	End Method

	Method CopyData:TEntity(entity:TEntity)
		Local camera:TCamera = TCamera(entity)
		Local red,green,blue,fognear#,fogfar#,x,y,width,height,near#,far#
		camera.GetFogColor red,green,blue
		camera.GetFogRange fognear,fogfar
		camera.GetViewport x,y,width,height
		camera.GetRange near,far

		SetMode camera.GetMode()
		SetFogMode camera.GetFogMode()
		SetFogColor red,green,blue
		SetFogRange fognear,fogfar
		SetViewport x,y,width,height
		SetClsMode camera.GetClsMode()
		SetRange near,far
		SetZoom camera.GetZoom()
		Return Super.CopyData(entity)
	End Method

	Method Copy:TCamera(parent:TEntity=Null)
		Return TCamera(Super.Copy_(parent))
	End Method

	Method GetMode()
		Return _projmode
	End Method
	Method SetMode(mode)
		_projmode=mode
	End Method

	Method GetFogMode()
		Return _fogmode
	End Method
	Method SetFogMode(mode)
		_fogmode=mode
	End Method

	Method GetFogColor(red Var,green Var,blue Var)
		red=_fogr*255.0;green=_fogg*255.0;blue=_fogb*255.0
	End Method
	Method SetFogColor(red,green,blue)
		_fogr=red/255.0;_fogg=green/255.0;_fogb=blue/255.0
	End Method

	Method GetFogRange(near# Var,far# Var)
		near=_fognear;far=_fogfar
	End Method
	Method SetFogRange(near#,far#)
		_fognear=Max(near,0);_fogfar=Max(far,0.001)
	End Method

	Method GetViewport(x Var,y Var,width Var,height Var)
		x=_viewx;y=_viewy;width=_viewwidth;height=_viewheight
	End Method
	Method SetViewport(x,y,width,height)
		_viewx=x;_viewy=y;_viewwidth=width;_viewheight=height
	End Method

	Method GetClsMode()
		Return _clsmode
	End Method
	Method SetClsMode(mode)
		_clsmode=mode
	End Method

	Method GetRange(near# Var,far# Var)
		near=_near;far=_far
	End Method
	Method SetRange(near#,far#)
		_near=near;_far=far
	End Method

	Method GetZoom#()
		Return _zoom
	End Method
	Method SetZoom(zoom#)
		_zoom=zoom
	End Method

	Method GetFOV#()
		Return ATan(_zoom / 1.0)
	End Method
	Method SetFOV(angle#)
		_zoom = 1.0 / Tan(angle / 2.0)
	End Method

	Method GetEye:TRay()
		Local x#,y#,z#,dx#,dy#,dz#=1.0,o:TVector,d:TVector
		GetPosition x,y,z,True
		_matrix.TransformVec3 dx,dy,dz
		Return New TRay.Create(New TVector.Create3(x,y,z),New TVector.Create3(dx-x,dy-y,dz-x))
	End Method

	Method UpdateMatrices()
		_viewport[0]=_viewx
		_viewport[1]=_viewy
		_viewport[2]=_viewwidth
		_viewport[3]=_viewheight

		_modelview = _matrix.Inverse()

		Local ratio#=(Float(_viewwidth)/_viewheight)
		_projection = TMatrix.Scale(1,1,-1).Multiply(TMatrix.PerspectiveFovRH(ATan((1.0/(_zoom*ratio)))*2.0,ratio,_near,_far))

		_frustum=TFrustum.Extract(_modelview, _projection)
	End Method

	Method Project(target:Object,x# Var,y# Var, offset#[] = Null)
		Local z#
		TEntity.GetTargetPosition target,x,y,z

		If offset
			x :+ offset[0]
			y :+ offset[1]
			z :+ offset[2]
		EndIf

		Return TMatrix.Project(_modelview,_projection,_viewport,x,y,z)
	End Method

	Method Unproject(wx#,wy#,wz#,x# Var,y# Var,z# Var)
		Return TMatrix.Unproject(_modelview,_projection,_viewport,wx,wy,wz,x,y,z)
	End Method

	Method InView#(target:Object)
		Local x#,y#,z#,radius#
		Local entity:TEntity=TEntity(target),point#[]=Float[](target)
		If entity
			entity.GetCullParams x,y,z,radius
		ElseIf point
			x=point[0];y=point[1];z=point[2];radius=point[3]
		Else
			Return 0
		EndIf
		Return _frustum.IntersectsPoint(x,y,z,radius)
	End Method
End Type


Made last pick priority in TWorld.Pick.

Made last pick priority in TWorld.Pick.

Strict

Import "worldconfig.bmx"
Import "collision.bmx"
Import "body.bmx"
Import "pivot.bmx"
Import "custom_entity.bmx"
Import "driver.bmx"
Import "pick.bmx"
Import "meshloader.bmx"
Import "meshloaderempty.bmx"

Private
Function ModuleLog(message$)
	TMaxB3DLogger.Write "core/world",message
End Function

Public

Type TWorld
	Field _config:TWorldConfig=New TWorldConfig
	Field _collisiondriver:TCollisionDriver

	Method New()
		SetAmbientLight 127,127,127
		SetCollisionDriver TCollisionDriver._default
		AddTextureFilter "", TEXTURE_COLOR|TEXTURE_MIPMAP

		_config.Width  = GraphicsWidth()
		_config.Height = GraphicsHeight()

		TMaxB3DDriver._configs :+ [_config]
	End Method

	Method AddResourcePath(path$)
		_config.ResourcePath=_config.ResourcePath[.._config.ResourcePath.length+1]
		_config.ResourcePath[_config.ResourcePath.length-1]=path
	End Method

	Method ClearTextureFilters()
		_config.TextureFilters = Null
	End Method

	Method AddTextureFilter(text$, flags)
		_config.TextureFilters :+ [[text, String(flags)]]
	End Method

	Method SetCollisionDriver(driver:TCollisionDriver)
		If driver
			driver.Init()
			_collisiondriver=driver
		EndIf
	End Method

	Method GetAmbientLight(red Var,green Var,blue Var)
		red=_config.AmbientRed
		green=_config.AmbientGreen
		blue=_config.AmbientBlue
	End Method
	Method SetAmbientLight(red,green,blue)
		_config.AmbientRed=red
		_config.AmbientGreen=green
		_config.AmbientBlue=blue
	End Method

	Method GetWireFrame()
		Return _config.Wireframe
	End Method
	Method SetWireFrame(enable)
		_config.Wireframe=enable
	End Method

	Method GetDither()
		Return _config.Dither
	End Method
	Method SetDither(enable)
		_config.Dither=enable
	End Method

	Method PickTarget(src:Object,target#[],ax# Var,ay# Var,az# Var,bx# Var,by# Var,bz# Var,radius#)
		If target.length = 1

		ElseIf target.length = 2
			Local camera:TCamera = TCamera(src)
			camera.Unproject target[0], target[1], 1, ax, ay, az
			camera.Unproject target[0], target[1], 0, bx, by, bz
		Else

		EndIf
	End Method

	'EntityPick ( entity,range# )
	'CameraPick ( camera,viewport_x#,viewport_y# )
	'LinePick ( x#,y#,z#,dx#,dy#,dz#[,radius#] )
	Method Picks:TPick[](src:Object, target:Object, sort = False)
		Local ax#,ay#,az#,bx#,by#,bz#,radius#=0.0
		TEntity.GetTargetPosition src,ax,ay,az
		PickTarget src,Float[](target),ax,ay,az,bx,by,bz,radius

		Local picks:TPick[]
		For Local entity:TEntity=EachIn _config.List[WORLDLIST_ENTITY]
			If entity._pickmode=PICKMODE_OFF Or Not entity.GetVisible() Then Continue
			If entity._pickmode=PICKMODE_POLYGON And TMesh(entity)=Null Continue

			Local matrix:TMatrix = entity._matrix

			Local raw:TRawPick = entity.Pick(entity._pickmode,ax,ay,az,bx-ax,by-ay,bz-az,radius)
			If raw
				Local info:TPick = New TPick.FromRaw(raw)
				If TMesh(entity)<>Null And entity._pickmode = PICKMODE_POLYGON info.surface = TMesh(entity)._surfaces[raw.surface]
				picks :+ [info]
			EndIf
		Next

		Return picks
	End Method

	Method Pick:TPick(src:Object, target:Object)
		Local picks:TPick[] = Picks(src, target, True)
		If picks.length > 0 Return picks[picks.length - 1]
	End Method

	Method AddTexture:TTexture(url:Object,flags=TEXTURE_DEFAULT)
		If TTexture(url) Return TTexture(url)
		If Not Int[](url) And Not TPixmap(url) url = _config.GetStream(url)
		Return New TTexture.Init(_config,url,flags)
	End Method

	Method AddBrush:TBrush(url:Object=Null)
		Return New TBrush.Init(_config, url)
	End Method

	Method AddPivot:TPivot(parent:TEntity=Null)
		Return TPivot(New TPivot.Init(_config, parent))
	End Method

	Method AddCamera:TCamera(parent:TEntity=Null)
		Return TCamera(New TCamera.Init(_config, parent))
	End Method

	Method AddLight:TLight(mode,parent:TEntity=Null)
		Local light:TLight=New TLight
		light.SetMode mode
		Return TLight(light.Init(_config, parent))
	End Method

	Method AddMesh:TMesh(url:Object,parent:TEntity=Null)
		Local mesh:TMesh=New TMesh
		If String(url) _config.TmpResourcePath=ExtractDir(String(url))
		If Not TMeshLoader.Load(_config,mesh,_config.GetStream(url))
			_config.TmpResourcePath = ""
			If url ModuleLog "Unable to load mesh url. ("+url.ToString()+")" Else ModuleLog "Unable to load mesh url. (null)"
			Return Null
		EndIf
		_config.TmpResourcePath = ""
		Return TMesh(mesh.Init(_config, parent))
	End Method

	Method AddFlat:TFlat(parent:TEntity=Null)
		Return TFlat(New TFlat.Init(_config, parent))
	End Method

	Method AddSprite:TSprite(url:Object,flags=TEXTURE_DEFAULT,parent:TEntity=Null)
		Local sprite:TSprite=New TSprite
		If url sprite.SetTexture AddTexture(url,flags)
		Return TSprite(sprite.Init(_config, parent))
	End Method

	Method AddTerrain:TTerrain(url:Object,parent:TEntity=Null)
		Local terrain:TTerrain=New TTerrain
		terrain.SetMap url
		Return TTerrain(terrain.Init(_config, parent))
	End Method

	Method AddBody:TBody()
		Return TBody(New TBody.Init(_config, Null))
	End Method

	Method AddBone:TBone(parent:TEntity=Null)
		Return TBone(New TBone.Init(_config, parent))
	End Method

	Method AddBSPModel:TBSPModel(url:Object,parent:TEntity=Null)
		Local bsp:TBSPModel=New TBSPModel
		bsp.SetTree TBSPTree(url)
		Return TBSPModel(bsp.Init(_config, parent))
	End Method

	Method AddCustomEntity:TCustomEntity(entity:TCustomEntity,parent:TEntity=Null)
		Return TCustomEntity(entity.Init(_config, parent))
	End Method

	Method Render:TRenderInfo(tween#=1.0)
		Local driver:TMaxB3DDriver=TMaxB3DDriver(GetGraphicsDriver())

		Global info:TRenderInfo=New TRenderInfo
		info.Triangles=0
		info.Entities=0

		For Local camera:TCamera=EachIn _config.List[WORLDLIST_CAMERA]
			If Not camera.GetVisible() Continue
			Local i:TRenderInfo=RenderCamera(camera)
			info.Triangles:+i.Triangles
			info.Entities:+i.Entities
		Next

		Global _ticks,_lastupdate

		If _lastupdate+1000<MilliSecs()
			info.FPS=_ticks
			_ticks=0
			_lastupdate=MilliSecs()
		Else
			_ticks:+1
		EndIf

		Return info
	End Method

	Method RenderCamera:TRenderInfo(camera:TCamera)
		Local driver:TMaxB3DDriver=TMaxB3DDriver(GetGraphicsDriver())
		Global info:TRenderInfo=New TRenderInfo

		If driver._in_max2d driver.EndMax2D

		driver.SetCamera camera,_config
		SetLighting	driver,camera

		Local list:TList=CreateList()
		For Local entity:TEntity=EachIn _config.List[WORLDLIST_RENDER]
			If entity.GetVisible()=False Or entity._brush._a=0 Continue
			If camera.InView(entity)
				Local link:TLink=list._head
				If entity._order > 0
					Repeat
						link=link._succ
					Until link=list._head Or TEntity(link.Value())._order <= entity._order
					list.InsertBeforeLink(entity,link)
				ElseIf entity._order < 0
					Repeat
						link=link._pred
					Until link=list._head Or TEntity(link.Value())._order >= entity._order
					list.InsertAfterLink(entity,link)
				Else
					If entity.HasAlpha()
						entity._alphaorder = camera.GetDistance(entity)
						Repeat
							link=link._pred
							If link=list._head Then Exit
						Until TEntity(link.Value())._order >= 0 And (TEntity(link.Value())._alphaorder >= entity._alphaorder Or TEntity(link.Value())._alphaorder = 0.0)
						list.InsertAfterLink(entity,link)
					Else
						entity._alphaorder = 0
						Repeat
							link=link._succ
						Until link=list._head Or TEntity(link.Value())._order <= 0
						list.InsertBeforeLink(entity,link)
					EndIf
				EndIf
			EndIf
		Next

		UpdateSprites list,driver,camera

		Return RenderEntities(list,driver,camera)
	End Method

	Method Update(speed#)
		_config.UpdateSpeed = speed#
		RunHooks _config.UpdateHook, _config
		_collisiondriver.Update _config,speed
		For Local mesh:TMesh=EachIn _config.List[WORLDLIST_MESH]
			Local animator:TAnimator=mesh._animator
			If animator=Null Continue
			Local seq:TAnimSeq=animator._current
			If seq
				If animator._mode>0
					animator._frame:+(speed*animator._speed)
					If animator._frame>seq._end Or animator._frame<seq._start
						Select animator._mode
						Case ANIMATION_LOOP
							If animator._frame>seq._end animator._frame=seq._start'+(animator._frame-seq._end)
							If animator._frame<seq._start animator._frame=seq._end'-(seq._begin-animator._frame)
						Case ANIMATION_PINGPONG
							If animator._frame>seq._end animator._frame=seq._end'+(animator._frame-seq._end)
							If animator._frame<seq._start animator._frame=seq._start'-(seq._begin-animator._frame)
							animator._speed:*-1
						Case ANIMATION_SINGLE
							animator._mode=ANIMATION_STOP
						End Select
					EndIf
				EndIf
				animator.Update()
			EndIf
		Next
	End Method

	Method SetCollisions(src,dest,methd,response)
		Local pair:TCollisionPair=New TCollisionPair
		pair.src=src;pair.dest=dest
		pair.methd=methd;pair.response=response

		For Local pair2:TCollisionPair=EachIn _config.CollisionPairs
			If pair2.src=pair2.src And pair2.dest=pair2.dest
				pair2.methd=pair2.methd
				pair2.response=pair2.response
				Return
			EndIf
		Next
		_config.CollisionPairs.AddLast pair
	End Method

	Method RenderEntities:TRenderInfo(list:TList,driver:TMaxB3DDriver,camera:TCamera)
		Global info:TRenderInfo=New TRenderInfo
		info.Triangles=0
		info.Entities=0
		If Not list.IsEmpty() info.Entities=list.Count()
		For Local entity:TEntity=EachIn list
			Local mesh:TMesh=TMesh(entity),flat:TFlat=TFlat(entity),terrain:TTerrain=TTerrain(entity)
			Local sprite:TSprite=TSprite(entity),bsp:TBSPModel=TBSPModel(entity)
			Local custom:TCustomEntity=TCustomEntity(entity), renderer:TCustomRenderer
			If custom
				renderer = TCustomRenderer(custom.Renderer(driver.Abbr()))
				If renderer = Null Continue
			EndIf
			Local brush:TBrush=entity._brush
			If brush._a=0 Continue
			driver.BeginEntityRender entity
			If mesh
				For Local surface:TSurface=EachIn mesh._surfaces
					If surface._brush._a=0 Continue
					Local animation_surface:TSurface,merge_data
					If mesh._animator
						If mesh._animator._current
							animation_surface=mesh._animator.GetSurface(surface)
							merge_data=mesh._animator.GetMergeData()
						EndIf
					EndIf
					Local resource:TSurfaceRes=driver.MergeSurfaceRes(surface,animation_surface,merge_data)
					brush=surface._brush.Merge(mesh._brush)
					driver.SetBrush brush,surface.HasAlpha() Or brush.HasAlpha(),_config
					info.Triangles:+driver.RenderSurface(resource,brush)
				Next
			Else
				driver.SetBrush brush, brush.HasAlpha(),_config
				If flat
					info.Triangles:+driver.RenderFlat(flat)
				ElseIf sprite
					driver.RenderSprite(sprite)
					info.Triangles:+4+(4*(brush._fx&FX_NOCULLING<>0))
				ElseIf terrain
					Local x#,y#,z#
					camera.GetPosition x,y,z,True
					terrain.Update x,y,z,camera._frustum.ToPtr()
					info.Triangles:+driver.RenderTerrain(terrain)
				ElseIf bsp
					Local tree:TBSPTree=bsp.GetRenderTree(camera.GetEye())
					info.Triangles:+driver.RenderBSPTree(tree)
				ElseIf custom
					info.Triangles:+renderer.Render(custom, driver)
				EndIf
			EndIf
			driver.EndEntityRender entity
		Next
		Return info
	End Method

	Method UpdateSprites(sprites:TList,driver:TMaxB3DDriver,camera:TCamera)
		For Local sprite:TSprite=EachIn sprites
			Local matrix:TMatrix
			If True
				If sprite._viewmode<>VIEWMODE_FREE
					Local x#,y#,z#
					sprite.GetPosition x,y,z,True

					matrix = camera.GetMatrix()'.Inverse()
					matrix._m[3,0]=x
					matrix._m[3,1]=y
					matrix._m[3,2]=z

					matrix=TMatrix.YawPitchRoll(180,0,sprite._angle).Multiply(matrix)
					If sprite._sx<>1.0 Or sprite._sy<>1.0 matrix=TMatrix.Scale(sprite._sx,sprite._sy,1.0).Multiply(matrix)
					'If sprite._handlex<>0.0 Or sprite._handley<>0.0 matrix=TMatrix.Translation(-sprite._handlex,-sprite._handley,0.0).Multiply(matrix)
				Else
					matrix = sprite.GetMatrix()
					'If sprite.scale_x#<>1.0 Or sprite.scale_y#<>1.0
					'	sprite.mat_sp.Scale(sprite.scale_x#,sprite.scale_y#,1.0)
					'EndIf
				EndIf
			Else
				matrix=sprite._matrix
			EndIf
			sprite._view_matrix=matrix
		Next
	End Method

	Method SetLighting(driver:TMaxB3DDriver,camera:TCamera)
		Local index
		For Local i=0 To 7
			Local light:TLight
			If index<CountList(_config.List[WORLDLIST_LIGHT])
				Repeat
					light=TLight(_config.List[WORLDLIST_LIGHT].ValueAtIndex(index))
					index:+1
				Until light.GetVisible() Or index>=CountList(_config.List[WORLDLIST_LIGHT])
			EndIf
			driver.SetLight light,i
		Next
	End Method
End Type

Type TRenderInfo
	Field FPS
	Field Triangles
	Field Entities
End Type

Strict

Import "worldconfig.bmx"
Import "collision.bmx"
Import "body.bmx"
Import "pivot.bmx"
Import "custom_entity.bmx"
Import "driver.bmx"
Import "pick.bmx"
Import "meshloader.bmx"
Import "meshloaderempty.bmx"

Private
Function ModuleLog(message$)
	TMaxB3DLogger.Write "core/world",message
End Function

Public

Type TWorld
	Field _config:TWorldConfig=New TWorldConfig
	Field _collisiondriver:TCollisionDriver

	Method New()
		SetAmbientLight 127,127,127
		SetCollisionDriver TCollisionDriver._default
		AddTextureFilter "", TEXTURE_COLOR|TEXTURE_MIPMAP

		_config.Width  = GraphicsWidth()
		_config.Height = GraphicsHeight()

		TMaxB3DDriver._configs :+ [_config]
	End Method

	Method AddResourcePath(path$)
		_config.ResourcePath=_config.ResourcePath[.._config.ResourcePath.length+1]
		_config.ResourcePath[_config.ResourcePath.length-1]=path
	End Method

	Method ClearTextureFilters()
		_config.TextureFilters = Null
	End Method

	Method AddTextureFilter(text$, flags)
		_config.TextureFilters :+ [[text, String(flags)]]
	End Method

	Method SetCollisionDriver(driver:TCollisionDriver)
		If driver
			driver.Init()
			_collisiondriver=driver
		EndIf
	End Method

	Method GetAmbientLight(red Var,green Var,blue Var)
		red=_config.AmbientRed
		green=_config.AmbientGreen
		blue=_config.AmbientBlue
	End Method
	Method SetAmbientLight(red,green,blue)
		_config.AmbientRed=red
		_config.AmbientGreen=green
		_config.AmbientBlue=blue
	End Method

	Method GetWireFrame()
		Return _config.Wireframe
	End Method
	Method SetWireFrame(enable)
		_config.Wireframe=enable
	End Method

	Method GetDither()
		Return _config.Dither
	End Method
	Method SetDither(enable)
		_config.Dither=enable
	End Method

	Method PickTarget(src:Object,target#[],ax# Var,ay# Var,az# Var,bx# Var,by# Var,bz# Var,radius#)
		If target.length = 1

		ElseIf target.length = 2
			Local camera:TCamera = TCamera(src)
			camera.Unproject target[0], target[1], 1, ax, ay, az
			camera.Unproject target[0], target[1], 0, bx, by, bz
		Else

		EndIf
	End Method

	'EntityPick ( entity,range# )
	'CameraPick ( camera,viewport_x#,viewport_y# )
	'LinePick ( x#,y#,z#,dx#,dy#,dz#[,radius#] )
	Method Picks:TPick[](src:Object, target:Object, sort = False)
		Local ax#,ay#,az#,bx#,by#,bz#,radius#=0.0
		TEntity.GetTargetPosition src,ax,ay,az
		PickTarget src,Float[](target),ax,ay,az,bx,by,bz,radius

		Local picks:TPick[]
		For Local entity:TEntity=EachIn _config.List[WORLDLIST_ENTITY]
			If entity._pickmode=PICKMODE_OFF Or Not entity.GetVisible() Then Continue
			If entity._pickmode=PICKMODE_POLYGON And TMesh(entity)=Null Continue

			Local matrix:TMatrix = entity._matrix

			Local raw:TRawPick = entity.Pick(entity._pickmode,ax,ay,az,bx-ax,by-ay,bz-az,radius)
			If raw
				Local info:TPick = New TPick.FromRaw(raw)
				If TMesh(entity)<>Null And entity._pickmode = PICKMODE_POLYGON info.surface = TMesh(entity)._surfaces[raw.surface]
				picks :+ [info]
			EndIf
		Next

		Return picks
	End Method

	Method Pick:TPick(src:Object, target:Object)
		Local picks:TPick[] = Picks(src, target, True)
		If picks.length > 0 Return picks[0]
	End Method

	Method AddTexture:TTexture(url:Object,flags=TEXTURE_DEFAULT)
		If TTexture(url) Return TTexture(url)
		If Not Int[](url) And Not TPixmap(url) url = _config.GetStream(url)
		Return New TTexture.Init(_config,url,flags)
	End Method

	Method AddBrush:TBrush(url:Object=Null)
		Return New TBrush.Init(_config, url)
	End Method

	Method AddPivot:TPivot(parent:TEntity=Null)
		Return TPivot(New TPivot.Init(_config, parent))
	End Method

	Method AddCamera:TCamera(parent:TEntity=Null)
		Return TCamera(New TCamera.Init(_config, parent))
	End Method

	Method AddLight:TLight(mode,parent:TEntity=Null)
		Local light:TLight=New TLight
		light.SetMode mode
		Return TLight(light.Init(_config, parent))
	End Method

	Method AddMesh:TMesh(url:Object,parent:TEntity=Null)
		Local mesh:TMesh=New TMesh
		If String(url) _config.TmpResourcePath=ExtractDir(String(url))
		If Not TMeshLoader.Load(_config,mesh,_config.GetStream(url))
			_config.TmpResourcePath = ""
			If url ModuleLog "Unable to load mesh url. ("+url.ToString()+")" Else ModuleLog "Unable to load mesh url. (null)"
			Return Null
		EndIf
		_config.TmpResourcePath = ""
		Return TMesh(mesh.Init(_config, parent))
	End Method

	Method AddFlat:TFlat(parent:TEntity=Null)
		Return TFlat(New TFlat.Init(_config, parent))
	End Method

	Method AddSprite:TSprite(url:Object,flags=TEXTURE_DEFAULT,parent:TEntity=Null)
		Local sprite:TSprite=New TSprite
		If url sprite.SetTexture AddTexture(url,flags)
		Return TSprite(sprite.Init(_config, parent))
	End Method

	Method AddTerrain:TTerrain(url:Object,parent:TEntity=Null)
		Local terrain:TTerrain=New TTerrain
		terrain.SetMap url
		Return TTerrain(terrain.Init(_config, parent))
	End Method

	Method AddBody:TBody()
		Return TBody(New TBody.Init(_config, Null))
	End Method

	Method AddBone:TBone(parent:TEntity=Null)
		Return TBone(New TBone.Init(_config, parent))
	End Method

	Method AddBSPModel:TBSPModel(url:Object,parent:TEntity=Null)
		Local bsp:TBSPModel=New TBSPModel
		bsp.SetTree TBSPTree(url)
		Return TBSPModel(bsp.Init(_config, parent))
	End Method

	Method AddCustomEntity:TCustomEntity(entity:TCustomEntity,parent:TEntity=Null)
		Return TCustomEntity(entity.Init(_config, parent))
	End Method

	Method Render:TRenderInfo(tween#=1.0)
		Local driver:TMaxB3DDriver=TMaxB3DDriver(GetGraphicsDriver())

		Global info:TRenderInfo=New TRenderInfo
		info.Triangles=0
		info.Entities=0

		For Local camera:TCamera=EachIn _config.List[WORLDLIST_CAMERA]
			If Not camera.GetVisible() Continue
			Local i:TRenderInfo=RenderCamera(camera)
			info.Triangles:+i.Triangles
			info.Entities:+i.Entities
		Next

		Global _ticks,_lastupdate

		If _lastupdate+1000<MilliSecs()
			info.FPS=_ticks
			_ticks=0
			_lastupdate=MilliSecs()
		Else
			_ticks:+1
		EndIf

		Return info
	End Method

	Method RenderCamera:TRenderInfo(camera:TCamera)
		Local driver:TMaxB3DDriver=TMaxB3DDriver(GetGraphicsDriver())
		Global info:TRenderInfo=New TRenderInfo

		If driver._in_max2d driver.EndMax2D

		driver.SetCamera camera,_config
		SetLighting	driver,camera

		Local list:TList=CreateList()
		For Local entity:TEntity=EachIn _config.List[WORLDLIST_RENDER]
			If entity.GetVisible()=False Or entity._brush._a=0 Continue
			If camera.InView(entity)
				Local link:TLink=list._head
				If entity._order > 0
					Repeat
						link=link._succ
					Until link=list._head Or TEntity(link.Value())._order <= entity._order
					list.InsertBeforeLink(entity,link)
				ElseIf entity._order < 0
					Repeat
						link=link._pred
					Until link=list._head Or TEntity(link.Value())._order >= entity._order
					list.InsertAfterLink(entity,link)
				Else
					If entity.HasAlpha()
						entity._alphaorder = camera.GetDistance(entity)
						Repeat
							link=link._pred
							If link=list._head Then Exit
						Until TEntity(link.Value())._order >= 0 And (TEntity(link.Value())._alphaorder >= entity._alphaorder Or TEntity(link.Value())._alphaorder = 0.0)
						list.InsertAfterLink(entity,link)
					Else
						entity._alphaorder = 0
						Repeat
							link=link._succ
						Until link=list._head Or TEntity(link.Value())._order <= 0
						list.InsertBeforeLink(entity,link)
					EndIf
				EndIf
			EndIf
		Next

		UpdateSprites list,driver,camera

		Return RenderEntities(list,driver,camera)
	End Method

	Method Update(speed#)
		_config.UpdateSpeed = speed#
		RunHooks _config.UpdateHook, _config
		_collisiondriver.Update _config,speed
		For Local mesh:TMesh=EachIn _config.List[WORLDLIST_MESH]
			Local animator:TAnimator=mesh._animator
			If animator=Null Continue
			Local seq:TAnimSeq=animator._current
			If seq
				If animator._mode>0
					animator._frame:+(speed*animator._speed)
					If animator._frame>seq._end Or animator._frame<seq._start
						Select animator._mode
						Case ANIMATION_LOOP
							If animator._frame>seq._end animator._frame=seq._start'+(animator._frame-seq._end)
							If animator._frame<seq._start animator._frame=seq._end'-(seq._begin-animator._frame)
						Case ANIMATION_PINGPONG
							If animator._frame>seq._end animator._frame=seq._end'+(animator._frame-seq._end)
							If animator._frame<seq._start animator._frame=seq._start'-(seq._begin-animator._frame)
							animator._speed:*-1
						Case ANIMATION_SINGLE
							animator._mode=ANIMATION_STOP
						End Select
					EndIf
				EndIf
				animator.Update()
			EndIf
		Next
	End Method

	Method SetCollisions(src,dest,methd,response)
		Local pair:TCollisionPair=New TCollisionPair
		pair.src=src;pair.dest=dest
		pair.methd=methd;pair.response=response

		For Local pair2:TCollisionPair=EachIn _config.CollisionPairs
			If pair2.src=pair2.src And pair2.dest=pair2.dest
				pair2.methd=pair2.methd
				pair2.response=pair2.response
				Return
			EndIf
		Next
		_config.CollisionPairs.AddLast pair
	End Method

	Method RenderEntities:TRenderInfo(list:TList,driver:TMaxB3DDriver,camera:TCamera)
		Global info:TRenderInfo=New TRenderInfo
		info.Triangles=0
		info.Entities=0
		If Not list.IsEmpty() info.Entities=list.Count()
		For Local entity:TEntity=EachIn list
			Local mesh:TMesh=TMesh(entity),flat:TFlat=TFlat(entity),terrain:TTerrain=TTerrain(entity)
			Local sprite:TSprite=TSprite(entity),bsp:TBSPModel=TBSPModel(entity)
			Local custom:TCustomEntity=TCustomEntity(entity), renderer:TCustomRenderer
			If custom
				renderer = TCustomRenderer(custom.Renderer(driver.Abbr()))
				If renderer = Null Continue
			EndIf
			Local brush:TBrush=entity._brush
			If brush._a=0 Continue
			driver.BeginEntityRender entity
			If mesh
				For Local surface:TSurface=EachIn mesh._surfaces
					If surface._brush._a=0 Continue
					Local animation_surface:TSurface,merge_data
					If mesh._animator
						If mesh._animator._current
							animation_surface=mesh._animator.GetSurface(surface)
							merge_data=mesh._animator.GetMergeData()
						EndIf
					EndIf
					Local resource:TSurfaceRes=driver.MergeSurfaceRes(surface,animation_surface,merge_data)
					brush=surface._brush.Merge(mesh._brush)
					driver.SetBrush brush,surface.HasAlpha() Or brush.HasAlpha(),_config
					info.Triangles:+driver.RenderSurface(resource,brush)
				Next
			Else
				driver.SetBrush brush, brush.HasAlpha(),_config
				If flat
					info.Triangles:+driver.RenderFlat(flat)
				ElseIf sprite
					driver.RenderSprite(sprite)
					info.Triangles:+4+(4*(brush._fx&FX_NOCULLING<>0))
				ElseIf terrain
					Local x#,y#,z#
					camera.GetPosition x,y,z,True
					terrain.Update x,y,z,camera._frustum.ToPtr()
					info.Triangles:+driver.RenderTerrain(terrain)
				ElseIf bsp
					Local tree:TBSPTree=bsp.GetRenderTree(camera.GetEye())
					info.Triangles:+driver.RenderBSPTree(tree)
				ElseIf custom
					info.Triangles:+renderer.Render(custom, driver)
				EndIf
			EndIf
			driver.EndEntityRender entity
		Next
		Return info
	End Method

	Method UpdateSprites(sprites:TList,driver:TMaxB3DDriver,camera:TCamera)
		For Local sprite:TSprite=EachIn sprites
			Local matrix:TMatrix
			If True
				If sprite._viewmode<>VIEWMODE_FREE
					Local x#,y#,z#
					sprite.GetPosition x,y,z,True

					matrix = camera.GetMatrix()'.Inverse()
					matrix._m[3,0]=x
					matrix._m[3,1]=y
					matrix._m[3,2]=z

					matrix=TMatrix.YawPitchRoll(180,0,sprite._angle).Multiply(matrix)
					If sprite._sx<>1.0 Or sprite._sy<>1.0 matrix=TMatrix.Scale(sprite._sx,sprite._sy,1.0).Multiply(matrix)
					'If sprite._handlex<>0.0 Or sprite._handley<>0.0 matrix=TMatrix.Translation(-sprite._handlex,-sprite._handley,0.0).Multiply(matrix)
				Else
					matrix = sprite.GetMatrix()
					'If sprite.scale_x#<>1.0 Or sprite.scale_y#<>1.0
					'	sprite.mat_sp.Scale(sprite.scale_x#,sprite.scale_y#,1.0)
					'EndIf
				EndIf
			Else
				matrix=sprite._matrix
			EndIf
			sprite._view_matrix=matrix
		Next
	End Method

	Method SetLighting(driver:TMaxB3DDriver,camera:TCamera)
		Local index
		For Local i=0 To 7
			Local light:TLight
			If index<CountList(_config.List[WORLDLIST_LIGHT])
				Repeat
					light=TLight(_config.List[WORLDLIST_LIGHT].ValueAtIndex(index))
					index:+1
				Until light.GetVisible() Or index>=CountList(_config.List[WORLDLIST_LIGHT])
			EndIf
			driver.SetLight light,i
		Next
	End Method
End Type

Type TRenderInfo
	Field FPS
	Field Triangles
	Field Entities
End Type


Sanity check (and because options might take arguments in the future).

Rem
Copyright (c) 2010 Tim Howard

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
End Rem

SuperStrict

Framework brl.blitz
Import brl.standardio
Import brl.maxutil

Import cower.jonk

Import duct.variables
Import duct.objectmap
Import duct.json

Include "src/logger.bmx"
Include "src/errors.bmx"
Include "src/arghandler.bmx"
Include "src/impl/help.bmx"
Include "src/impl/update.bmx"
Include "src/impl/version.bmx"
Include "src/dependencies.bmx"
Include "src/module.bmx"
Include "src/sources.bmx"

Global logger:mxLogger = New mxLogger
Global mainapp:mxApp
New mxApp.Create(AppArgs[1..]) ' Skip the first element because it is the program's location
mainapp.Run()

Rem
	bbdoc: Maximus app.
	about: This handles the basic flow of the program (initiation, parsing, command calling, exiting..)
End Rem
Type mxApp

	Const c_version:String = "0.01"

	Field m_maxpath:String

	Field m_args:String[]
	Field m_arghandler:mxArgumentHandler

	Rem
		bbdoc: Create a new mxApp.
		returns: The new mxApp.
	End Rem
	Method Create:mxApp(args:String[])
		mainapp = Self
		SetArgs(args)
		OnInit()
		Return Self
	End Method

	Rem
		bbdoc: Called when the application is created.
		returns: Nothing.
	End Rem
	Method OnInit()
		Try
			m_maxpath = BlitzMaxPath()
		Catch e:Object
			ThrowError("Unable to determine the path to BlitzMax!~nHave you set the 'BMXPATH' environment variable?")
		End Try
		m_arghandler = New mxArgumentHandler
		m_arghandler.AddArgImpl(New mxHelpImpl)
		m_arghandler.AddArgImpl(New mxVersionImpl)
		m_arghandler.AddArgImpl(New mxUpdateImpl)
	End Method

	Rem
		bbdoc: Called when the application is ended (this includes #ThrowError calls).
		returns: Nothing.
	End Rem
	Method OnExit()
	End Method

	Rem
		bbdoc: Run the application.
		returns: Nothing.
	End Rem
	Method Run()
		Local argimpl:mxArgumentImplementation
		Local i:Int, arg:String, isopt:Int
		For i = 0 To m_args.Length - 1
			arg = m_args[i]
			argimpl = m_arghandler.GetArgImplFromAlias(arg)
			isopt = arg.StartsWith("-")
			If argimpl <> Null
				If isopt = True
					argimpl.SetArgs(Null)
					argimpl.SetCallConvention(mxCallConvention.OPTION)
					argimpl.CheckArgs()
					argimpl.Execute()
				Else ' Must be a command (leaving out parameters for options, as that would get really complicated)
					argimpl.SetCallConvention(mxCallConvention.COMMAND)
					ParseCommandArgs(argimpl, m_args[i + 1..])
					argimpl.Execute()
					Exit ' Commands take all the subsequent arguments
				End If
			Else
				If isopt = True
					ThrowCommonError(mxOptErrors.UNKNOWN, arg)
				Else
					ThrowCommonError(mxCmdErrors.UNKNOWN, arg)
				End If
			End If
		Next
		OnExit()
	End Method

	Rem
		bbdoc: Parse command arguments.
		returns: Nothing.
	End Rem
	Method ParseCommandArgs(argimpl:mxArgumentImplementation, args:String[])
		' This will need to be parsed eventually, but for now..
		argimpl.SetArgs(args)
		argimpl.CheckArgs()
	End Method

	Rem
		bbdoc: Set the application's arguments.
		returns: Nothing.
		about: NOTE: This will resolve to '--help' if the given args are Null.
	End Rem
	Method SetArgs(args:String[])
		If args = Null
			args = ["--help"]
		End If
		m_args = args
	End Method

End Type

Rem
	bbdoc: Throw an error (terminating the application).
	returns: Nothing.
End Rem
Function ThrowError(error:String)
	logger.LogError(error)
	mainapp.OnExit()
	End
End Function

Rem
Copyright (c) 2010 Tim Howard

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
End Rem

SuperStrict

Framework brl.blitz
Import brl.standardio
Import brl.maxutil

Import cower.jonk

Import duct.variables
Import duct.objectmap
Import duct.json

Include "src/logger.bmx"
Include "src/errors.bmx"
Include "src/arghandler.bmx"
Include "src/impl/help.bmx"
Include "src/impl/update.bmx"
Include "src/impl/version.bmx"
Include "src/dependencies.bmx"
Include "src/module.bmx"
Include "src/sources.bmx"

Global logger:mxLogger = New mxLogger
Global mainapp:mxApp
New mxApp.Create(AppArgs[1..]) ' Skip the first element because it is the program's location
mainapp.Run()

Rem
	bbdoc: Maximus app.
	about: This handles the basic flow of the program (initiation, parsing, command calling, exiting..)
End Rem
Type mxApp

	Const c_version:String = "0.01"

	Field m_maxpath:String

	Field m_args:String[]
	Field m_arghandler:mxArgumentHandler

	Rem
		bbdoc: Create a new mxApp.
		returns: The new mxApp.
	End Rem
	Method Create:mxApp(args:String[])
		mainapp = Self
		SetArgs(args)
		OnInit()
		Return Self
	End Method

	Rem
		bbdoc: Called when the application is created.
		returns: Nothing.
	End Rem
	Method OnInit()
		Try
			m_maxpath = BlitzMaxPath()
		Catch e:Object
			ThrowError("Unable to determine the path to BlitzMax!~nHave you set the 'BMXPATH' environment variable?")
		End Try
		m_arghandler = New mxArgumentHandler
		m_arghandler.AddArgImpl(New mxHelpImpl)
		m_arghandler.AddArgImpl(New mxVersionImpl)
		m_arghandler.AddArgImpl(New mxUpdateImpl)
	End Method

	Rem
		bbdoc: Called when the application is ended (this includes #ThrowError calls).
		returns: Nothing.
	End Rem
	Method OnExit()
	End Method

	Rem
		bbdoc: Run the application.
		returns: Nothing.
	End Rem
	Method Run()
		Local argimpl:mxArgumentImplementation
		Local i:Int, arg:String, isopt:Int
		For i = 0 To m_args.Length - 1
			arg = m_args[i]
			argimpl = m_arghandler.GetArgImplFromAlias(arg)
			isopt = arg.StartsWith("-")
			If argimpl <> Null
				If isopt = True
					argimpl.SetArgs(Null)
					argimpl.SetCallConvention(mxCallConvention.OPTION)
					argimpl.Execute()
				Else ' Must be a command (leaving out parameters for options, as that would get really complicated)
					argimpl.SetCallConvention(mxCallConvention.COMMAND)
					ParseCommandArgs(argimpl, m_args[i + 1..])
					argimpl.Execute()
					Exit ' Commands take all the subsequent arguments
				End If
			Else
				If isopt = True
					ThrowCommonError(mxOptErrors.UNKNOWN, arg)
				Else
					ThrowCommonError(mxCmdErrors.UNKNOWN, arg)
				End If
			End If
		Next
		OnExit()
	End Method

	Rem
		bbdoc: Parse command arguments.
		returns: Nothing.
	End Rem
	Method ParseCommandArgs(argimpl:mxArgumentImplementation, args:String[])
		' This will need to be parsed eventually, but for now..
		argimpl.SetArgs(args)
		argimpl.CheckArgs()
	End Method

	Rem
		bbdoc: Set the application's arguments.
		returns: Nothing.
		about: NOTE: This will resolve to '--help' if the given args are Null.
	End Rem
	Method SetArgs(args:String[])
		If args = Null
			args = ["--help"]
		End If
		m_args = args
	End Method

End Type

Rem
	bbdoc: Throw an error (terminating the application).
	returns: Nothing.
End Rem
Function ThrowError(error:String)
	logger.LogError(error)
	mainapp.OnExit()
	End
End Function


Added Karja's SetMatrix code for MonkeyMax.

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp;

Type gxtkApp
	Field ginput:gxtkInput;
	Field gaudio:gxtkAudio;
	Field ggraphics:gxtkGraphics;

	Field dead:int=0;
	Field suspended:int=0;
	Field vloading:int=0;
	Field maxloading:int=0;
	Field updateRate:int=0;
	Field nextUpdate:Float=0;
	Field updatePeriod:Float=0;
	Field startMillis:Float=0;

	Method New()' gxtkApp()
		app=self;
		ggraphics=new gxtkGraphics;
	EndMethod

	Method Setup()

		ginput=new gxtkInput;
		gaudio=new gxtkAudio;

		bb_input_SetInputDevice(ginput);
		bb_audio_SetAudioDevice(gaudio);

		startMillis=BlitzMaxMillisecs()

		'game.stage.addEventListener( Event.ENTER_FRAME,OnEnterFrame );

		SetFrameRate( 0 );

		InvokeOnCreate();
		InvokeOnRender();
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			local updates:Int = 0

			local cont:int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				if not updatePeriod then cont = 0
				if nextUpdate>BlitzMaxMillisecs() then cont = 0
				updates:+1
				if updates = 7 then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				endif
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		if dead return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		if dead or suspended or not updateRate or vloading return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		if dead or suspended return
		ggraphics.BeginRender()
		if vloading
			OnLoading()
		else
			OnRender()
		Endif
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:int )
		if fps
			updatePeriod=1000.0/fps;
			nextUpdate=BlitzMaxMillisecs() +updatePeriod;
		else
			updatePeriod=0
		endif
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path;
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:int(hertz:int)
		updateRate = hertz
		if not vloading then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:int()
		return vloading;
	EndMethod

	Method OnCreate:int()
		return 0;
	EndMethod

	Method OnUpdate:int()
		return 0;
	EndMethod

	Method OnSuspend:int()
		return 0;
	EndMethod

	Method OnResume:int()
		return 0;
	EndMethod

	Method OnRender:int()
		return 0;
	EndMethod

	Method OnLoading:int()
		return 0;
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		return gmode
	EndMethod

	Method Cls:Int(r:int = 0, g:int = 0, b:int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		local image:TImage = LoadImage("data/"+path)
		if image then
			local gs:gxtkSurface = new gxtkSurface
			gs.setImage(image)
			return gs
		endif
		return null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		return 0
	EndMethod

	Method Width:Int()
		'TO-DO
		Return 800
	EndMethod

	Method Height:Int()
		'TO-DO
		Return 600
	EndMethod

	Method SetScissor:Int(x:int, y:int, w:int, h:int)
    'TO-DO
		Return 0
	EndMethod

	Method SetMatrix:Int(ix:float,iy:float,jx:float,jy:float,tx:float,ty:float)
		Local sx:Float = Sqr( (ix*ix) + (jx*jx) )
		Local sy:Float = Sqr( (iy*iy) + (jy*jy) )
		Local rot:Float = Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		SetOrigin( tx, ty )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		DrawImage(surface.image, x, y, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:int, srcy:int, srcw:int, srch:int )

	'	Print "x = "+x
	'	Print "y = "+y
	'	Print "srcx = "+srcx
	'	Print "srcy = "+srcy
	'	Print "srcw = "+srcw
	'	Print "srch = "+srch
		DrawSubImageRect(surface.image, x, y, srcw, srch, srcx, srcy, srcw, srch)

		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		BlitzMaxDrawLine(x1, y1, x2, y2)
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawOval(x, y, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		BlitzMaxDrawPoly( vertices )
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawRect(x, y, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		return 0
	EndMethod

	Method MouseY:Float()
		return 0
	EndMethod

	Method KeyDown:Int( key:Int )
		return BRL.PolledInput.KeyDown( key )
	EndMethod

	Method KeyHit:Int( key:Int )
		return BRL.PolledInput.KeyHit( key )
	EndMethod

	Method GetChar:Int()
		return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		return 0
	EndMethod

	Method TouchY:Float( index:Int )
		return 0
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0;

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		return amusicState;
	EndMethod

	Method PlayMusic:Int( path:String, flags:int )
		return 0
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
	EndMethod

	Method SetRate( channel:Int, rate:Float )
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Return Null
	EndMethod
EndType

Type gxtkSample
	Method Discard()
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	SetBlend(blend)
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		self.image = image
	EndMethod

	Method Width:Int()
		return image.Width
	EndMethod

	Method Height:Int()
		return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp;

Type gxtkApp
	Field ginput:gxtkInput;
	Field gaudio:gxtkAudio;
	Field ggraphics:gxtkGraphics;

	Field dead:int=0;
	Field suspended:int=0;
	Field vloading:int=0;
	Field maxloading:int=0;
	Field updateRate:int=0;
	Field nextUpdate:Float=0;
	Field updatePeriod:Float=0;
	Field startMillis:Float=0;

	Method New()' gxtkApp()
		app=self;
		ggraphics=new gxtkGraphics;
	EndMethod

	Method Setup()

		ginput=new gxtkInput;
		gaudio=new gxtkAudio;

		bb_input_SetInputDevice(ginput);
		bb_audio_SetAudioDevice(gaudio);

		startMillis=BlitzMaxMillisecs()

		'game.stage.addEventListener( Event.ENTER_FRAME,OnEnterFrame );

		SetFrameRate( 0 );

		InvokeOnCreate();
		InvokeOnRender();
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			local updates:Int = 0

			local cont:int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				if not updatePeriod then cont = 0
				if nextUpdate>BlitzMaxMillisecs() then cont = 0
				updates:+1
				if updates = 7 then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				endif
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		if dead return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		if dead or suspended or not updateRate or vloading return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		if dead or suspended return
		ggraphics.BeginRender()
		if vloading
			OnLoading()
		else
			OnRender()
		Endif
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:int )
		if fps
			updatePeriod=1000.0/fps;
			nextUpdate=BlitzMaxMillisecs() +updatePeriod;
		else
			updatePeriod=0
		endif
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path;
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:int(hertz:int)
		updateRate = hertz
		if not vloading then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:int()
		return vloading;
	EndMethod

	Method OnCreate:int()
		return 0;
	EndMethod

	Method OnUpdate:int()
		return 0;
	EndMethod

	Method OnSuspend:int()
		return 0;
	EndMethod

	Method OnResume:int()
		return 0;
	EndMethod

	Method OnRender:int()
		return 0;
	EndMethod

	Method OnLoading:int()
		return 0;
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		return gmode
	EndMethod

	Method Cls:Int(r:int = 0, g:int = 0, b:int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		local image:TImage = LoadImage("data/"+path)
		if image then
			local gs:gxtkSurface = new gxtkSurface
			gs.setImage(image)
			return gs
		endif
		return null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		return 0
	EndMethod

	Method Width:Int()
		'TO-DO
		Return 800
	EndMethod

	Method Height:Int()
		'TO-DO
		Return 600
	EndMethod

	Method SetScissor:Int(x:int, y:int, w:int, h:int)
		Return 0
	EndMethod

	Method SetMatrix:Int(x:float,iy:float,jx:float,jy:float,tx:float,ty:float)
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		DrawImage(surface.image, x, y, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:int, srcy:int, srcw:int, srch:int )

	'	Print "x = "+x
	'	Print "y = "+y
	'	Print "srcx = "+srcx
	'	Print "srcy = "+srcy
	'	Print "srcw = "+srcw
	'	Print "srch = "+srch
		DrawSubImageRect(surface.image, x, y, srcw, srch, srcx, srcy, srcw, srch)

		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		BlitzMaxDrawLine(x1, y1, x2, y2)
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawOval(x, y, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		BlitzMaxDrawPoly( vertices )
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawRect(x, y, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		return 0
	EndMethod

	Method MouseY:Float()
		return 0
	EndMethod

	Method KeyDown:Int( key:Int )
		return BRL.PolledInput.KeyDown( key )
	EndMethod

	Method KeyHit:Int( key:Int )
		return BRL.PolledInput.KeyHit( key )
	EndMethod

	Method GetChar:Int()
		return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		return 0
	EndMethod

	Method TouchY:Float( index:Int )
		return 0
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0;

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		return amusicState;
	EndMethod

	Method PlayMusic:Int( path:String, flags:int )
		return 0
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
	EndMethod

	Method SetRate( channel:Int, rate:Float )
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Return Null
	EndMethod
EndType

Type gxtkSample
	Method Discard()
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	SetBlend(blend)
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		self.image = image
	EndMethod

	Method Width:Int()
		return image.Width
	EndMethod

	Method Height:Int()
		return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******