All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flixel.FlxGame Maven / Gradle / Ivy

The newest version!
package org.flixel;

import org.flixel.event.IFlxReplay;
import org.flixel.plugin.TimerManager;
import org.flixel.system.FlxDebugger;
import org.flixel.system.FlxReplay;
import org.flixel.system.FlxSplashScreen;
import org.flixel.system.gdx.GdxStage;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.reflect.ClassReflection;

import flash.display.Stage;
import flash.events.Event;
import flash.events.IEventListener;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TouchEvent;
import flash.ui.Keyboard;

/**
 * FlxGame is the heart of all flixel games, and contains a bunch of basic game loops and things.
 * It is a long and sloppy file that you shouldn't have to worry about too much!
 * It is basically only used to create your game object in the first place,
 * after that FlxG and FlxState have all the useful stuff you actually need.
 * 
 * @author Ka Wing Chin
 * @author Thomas Weston
 */
public class FlxGame
{
	/**
	 * Sets 0, -, and + to control the global volume sound volume.
	 * 
	 * @default true
	 */
	public boolean useSoundHotKeys;
	/**
	 * Tells flixel to use the default system mouse cursor instead of custom Flixel mouse cursors.
	 * @default false
	 */
	public boolean useSystemCursor;
	/**
	 * Initialize and allow the flixel debugger overlay even in release mode.
	 * Also useful if you don't use FlxPreloader!
	 * @default false
	 */
	public boolean forceDebugger;

	/**
	 * Current game state.
	 */
	FlxState _state;
	/**
	 * Mouse cursor.
	 */
	FlxGroup _mouse;

	/**
	 * Class type of the initial/first game state for the game, usually MenuState or something like that.
	 */
	protected Class _iState;
	/**
	 * Whether the game object's basic initialization has finished yet.
	 */
	protected boolean _created;

	/**
	 * Total number of milliseconds elapsed since game start.
	 */
	protected long _total;
	/**
	 * Total number of milliseconds elapsed since last update loop.
	 * Counts down as we step through the game loop.
	 */
	protected int _accumulator;
	/**
	 * Whether the application lost focus.
	 */
	protected boolean _lostFocus;
	/**
	 * Milliseconds of time per step of the game loop.  FlashEvent.g. 60 fps = 16ms.
	 */
	int _step;
	/**
	 * Framerate of the Flash Player (NOT the game loop). Default = 30.
	 */
	int _flashFramerate;
	/**
	 * Max allowable accumulation (see _accumulator).
	 * Should always (and automatically) be set to roughly 2x the flash player framerate.
	 */
	int _maxAccumulation;
	/**
	 * If a state change was requested, the new state object is stored here until we switch to it.
	 */
	FlxState _requestedState;
	/**
	 * A flag for keeping track of whether a game reset was requested or not.
	 */
	boolean _requestedReset;

	/**
	 * The "focus lost" screen (see createFocusScreen()).
	 */
	protected TextureRegion _focus;
	/**
	 * The sound tray display container (see createSoundTray()).
	 */
	protected FlxSprite _soundTray;
	/**
	 * Helps us auto-hide the sound tray after a volume change.
	 */
	protected float _soundTrayTimer;
	/**
	 * Helps display the volume bars on the sound tray.
	 */
	protected Array _soundTrayBars;
	/**
	 * The debugger overlay object.
	 */
	FlxDebugger _debugger;
	/**
	 * A handy boolean that keeps track of whether the debugger exists and is currently visible.
	 */
	boolean _debuggerUp;

	/**
	 * Container for a game replay object.
	 */
	FlxReplay _replay;
	/**
	 * Flag for whether a playback of a recording was requested.
	 */
	boolean _replayRequested;
	/**
	 * Flag for whether a new recording was requested.
	 */
	boolean _recordingRequested;
	/**
	 * Flag for whether a replay is currently playing.
	 */
	boolean _replaying;
	/**
	 * Flag for whether a new recording is being made.
	 */
	boolean _recording;
	/**
	 * Array that keeps track of keypresses that can cancel a replay.
	 * Handy for skipping cutscenes or getting out of attract modes!
	 */
	String[] _replayCancelKeys;
	/**
	 * Helps time out a replay if necessary.
	 */
	int _replayTimer;
	/**
	 * This function, if set, is triggered when the callback stops playing.
	 */
	IFlxReplay _replayCallback;

	/**
	 * Temporary font to display the fps.
	 */
	private BitmapFont _font;
	/**
	 * Temporary text buffer for the fps.
	 */
	private StringBuilder _stringBuffer;
	/**
	 * Temporary camera to display the fps.
	 */
	private OrthographicCamera _fontCamera;

	/**
	 * Represents the stage.
	 */
	public Stage stage;

	protected boolean showSplashScreen = false;

	/**
	 * Instantiate a new game object.
	 * 
	 * @param	GameSizeX		The width of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	GameSizeY		The height of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	InitialState	The class name of the state you want to create and switch to first (e.g. MenuState).
	 * @param	Zoom			The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x).  Default = 1.
	 * @param	GameFramerate	How frequently the game should update (default is 30 times per second).
	 * @param	FlashFramerate	Sets the actual display framerate for Flash player (default is 30 times per second).
	 * @param	UseSystemCursor	Whether to use the default OS mouse pointer, or to use custom flixel ones.
	 * @param	ScaleMode		How to scale the stage to fit the display (default is FlxCamera.STRETCH).
	 */
	public FlxGame(int GameSizeX,int GameSizeY,Class InitialState,float Zoom,int GameFramerate,int FlashFramerate,boolean UseSystemCursor,int ScaleMode)
	{
		//super high priority init stuff (focus, mouse, etc)
		_lostFocus = false;
		_mouse = new FlxGroup();
		stage = new GdxStage();

		//basic display and update setup stuff
		FlxG.init(this,GameSizeX,GameSizeY,Zoom,ScaleMode);
		FlxG.setFramerate(GameFramerate);
		FlxG.setFlashFramerate(FlashFramerate);
		_accumulator = (int) _step;
		_total = 0;
		_state = null;
		useSoundHotKeys = true;
		useSystemCursor = UseSystemCursor;
		// if(!useSystemCursor)
		// flash.ui.Mouse.hide();
		forceDebugger = false;
		_debuggerUp = false;

		//replay data
		_replay = new FlxReplay();
		_replayRequested = false;
		_recordingRequested = false;
		_replaying = false;
		_recording = false;

		//then get ready to create the game object for real;
		_iState = InitialState;
		_requestedState = null;
		_requestedReset = true;
		_created = false;
		stage.addEventListener(Event.ADDED_TO_STAGE, addedToStageListener);
	}

	/**
	 * Instantiate a new game object.
	 * 
	 * @param	GameSizeX		The width of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	GameSizeY		The height of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	InitialState	The class name of the state you want to create and switch to first (e.g. MenuState).
	 * @param	Zoom			The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x).  Default = 1.
	 * @param	GameFramerate	How frequently the game should update (default is 30 times per second).
	 * @param	FlashFramerate	Sets the actual display framerate for Flash player (default is 30 times per second).
	 * @param	UseSystemCursor	Whether to use the default OS mouse pointer, or to use custom flixel ones.
	 */
	public FlxGame(int GameSizeX,int GameSizeY,Class InitialState,float Zoom,int GameFramerate,int FlashFramerate,boolean UseSystemCursor)
	{
		this(GameSizeX,GameSizeY,InitialState,Zoom,GameFramerate,FlashFramerate,UseSystemCursor,FlxCamera.STRETCH);
	}

	/**
	 * Instantiate a new game object.
	 * 
	 * @param	GameSizeX		The width of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	GameSizeY		The height of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	InitialState	The class name of the state you want to create and switch to first (e.g. MenuState).
	 * @param	Zoom			The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x).  Default = 1.
	 * @param	GameFramerate	How frequently the game should update (default is 30 times per second).
	 * @param	FlashFramerate	Sets the actual display framerate for Flash player (default is 30 times per second).
	 */
	public FlxGame(int GameSizeX,int GameSizeY,Class InitialState,float Zoom,int GameFramerate,int FlashFramerate)
	{
		this(GameSizeX,GameSizeY,InitialState,Zoom,GameFramerate,FlashFramerate,false,FlxCamera.STRETCH);
	}

	/**
	 * Instantiate a new game object.
	 * 
	 * @param	GameSizeX		The width of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	GameSizeY		The height of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	InitialState	The class name of the state you want to create and switch to first (e.g. MenuState).
	 * @param	Zoom			The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x).  Default = 1.
	 * @param	GameFramerate	How frequently the game should update (default is 30 times per second).
	 */
	public FlxGame(int GameSizeX,int GameSizeY,Class InitialState,float Zoom,int GameFramerate)
	{
		this(GameSizeX,GameSizeY,InitialState,Zoom,GameFramerate,30,false,FlxCamera.STRETCH);
	}

	/**
	 * Instantiate a new game object.
	 * 
	 * @param	GameSizeX		The width of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	GameSizeY		The height of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	InitialState	The class name of the state you want to create and switch to first (e.g. MenuState).
	 * @param	Zoom			The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x).  Default = 1.
	 */
	public FlxGame(int GameSizeX,int GameSizeY,Class InitialState,float Zoom)
	{
		this(GameSizeX,GameSizeY,InitialState,Zoom,30,30,false,FlxCamera.STRETCH);
	}

	/**
	 * Instantiate a new game object.
	 * 
	 * @param	GameSizeX		The width of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	GameSizeY		The height of your game in game pixels, not necessarily final display pixels (see Zoom).
	 * @param	InitialState	The class name of the state you want to create and switch to first (e.g. MenuState).
	 */
	public FlxGame(int GameSizeX,int GameSizeY,Class InitialState)
	{
		this(GameSizeX,GameSizeY,InitialState,1,30,30,false,FlxCamera.STRETCH);
	}

	/**
	 * Makes the little volume tray slide out.
	 * 
	 * @param	Silent	Whether or not it should beep.
	 */
	void showSoundTray(boolean Silent)
	{
		// if(!Silent)
		// FlxG.play(SndBeep);

		if(_soundTray != null)
		{
			_soundTrayTimer = 1;
			_soundTray.y = 0;
			_soundTray.visible = true;
			int globalVolume = Math.round(FlxG.getVolume()*10);
			if(FlxG.mute)
				globalVolume = 0;
			for(int i = 0; i < _soundTrayBars.size; i++)
			{
				if(i < globalVolume) _soundTrayBars.get(i).setAlpha(1);
				else _soundTrayBars.get(i).setAlpha(0.5f);
			}
		}
	}

	/**
	 * Makes the little volume tray slide out.
	 */
	void showSoundTray()
	{
		showSoundTray(false);
	}

	/**
	 * Internal event handler for input and focus.
	 * 
	 * @param	FlashEvent	Flash keyboard event.
	 */
	protected void onKeyUp(KeyboardEvent FlashEvent)
	{
		if(_debuggerUp && _debugger.watch.editing)
			return;
		if(!FlxG.mobile)
		{
			if((_debugger != null) && ((FlashEvent.keyCode == Keyboard.BACKQUOTE) || (FlashEvent.keyCode == Keyboard.BACKSLASH)))
			{
				_debugger.visible = !_debugger.visible;
				_debuggerUp = _debugger.visible;
				if(_debugger.visible)
					flash.ui.Mouse.show();
				else if(!useSystemCursor)
					flash.ui.Mouse.hide();
				// _console.toggle();
				return;
			}
			if(useSoundHotKeys)
			{
				int c = FlashEvent.keyCode;
				switch(c)
				{
					case Keyboard.NUMBER_0:
					case Keyboard.NUMPAD_0:
						FlxG.mute = !FlxG.mute;
						if(FlxG.volumeHandler != null)
							FlxG.volumeHandler.callback(FlxG.mute?0:FlxG.getVolume());
						showSoundTray();
						break;
					case Keyboard.MINUS:
					case Keyboard.NUMPAD_SUBTRACT:
						FlxG.mute = false;
						FlxG.setVolume(FlxG.getVolume() - 0.1f);
						showSoundTray();
						break;
					case Keyboard.EQUAL:
					case Keyboard.NUMPAD_ADD:
						FlxG.mute = false;
						FlxG.setVolume(FlxG.getVolume() + 0.1f);
						showSoundTray();
						break;
					default:
						break;
				}
			}
		}
		if(_replaying)
			return;
		FlxG.keys.handleKeyUp(FlashEvent);
	}

	/**
	 * Internal event handler for input and focus.
	 * 
	 * @param	FlashEvent	Flash keyboard event.
	 */
	protected void onKeyDown(KeyboardEvent FlashEvent)
	{
		if(_debuggerUp && _debugger.watch.editing)
			return;
		if(_replaying && (_replayCancelKeys != null) && (_debugger == null) && (FlashEvent.keyCode != Keyboard.BACKQUOTE) && (FlashEvent.keyCode != Keyboard.BACKSLASH))
		{
			// boolean cancel = false;
			String replayCancelKey;
			int i = 0;
			int l = _replayCancelKeys.length;
			while(i < l)
			{
				replayCancelKey = _replayCancelKeys[i++];
				if((replayCancelKey == "ANY") || (FlxG.keys.getKeyCode(replayCancelKey) == FlashEvent.keyCode))
				{
					if(_replayCallback != null)
					{
						_replayCallback.callback();
						_replayCallback = null;
					}
					else
						FlxG.stopReplay();
					break;
				}
			}
			return;
		}
		FlxG.keys.handleKeyDown(FlashEvent);
	}

	/**
	 * Internal event handler for input and focus.
	 * 
	 * @param	FlashEvent	Mouse event.
	 */
	protected void onMouseDown(TouchEvent FlashEvent)
	{
		if(_debuggerUp)
		{
			if(_debugger.hasMouse)
				return;
			if(_debugger.watch.editing)
				_debugger.watch.submit();
		}
		if(_replaying && (_replayCancelKeys != null))
		{
			String replayCancelKey;
			int i = 0;
			int l = _replayCancelKeys.length;
			while(i < l)
			{
				replayCancelKey = _replayCancelKeys[i++];
				if((replayCancelKey.equals("MOUSE")) || (replayCancelKey.equals("ANY")))
				{
					if(_replayCallback != null)
					{
						_replayCallback.callback();
						_replayCallback = null;
					}
					else
						FlxG.stopReplay();
					break;
				}
			}
			return;
		}
		FlxG.mouse.handleMouseDown(FlashEvent);
	}

	/**
	 * Internal event handler for input and focus.
	 * 
	 * @param	FlashEvent	Flash mouse event.
	 */
	protected void onMouseUp(TouchEvent FlashEvent)
	{
		if((_debuggerUp && _debugger.hasMouse) || _replaying)
			return;
		FlxG.mouse.handleMouseUp(FlashEvent);
	}

	/**
	 * Internal event handler for input and focus.
	 * 
	 * @param	FlashEvent	Flash mouse event.
	 */
	protected void onMouseWheel(MouseEvent FlashEvent)
	{
		if((_debuggerUp && _debugger.hasMouse) || _replaying)
			return;
		FlxG.mouse.handleMouseWheel(FlashEvent);
	}

	/**
	 * Internal event handler for input and focus.
	 * 
	 * @param	FlashEvent	Flash event.
	 */
	protected void onFocus(Event FlashEvent)
	{
		if(!_debuggerUp && !useSystemCursor)
			flash.ui.Mouse.hide();
		FlxG.resetInput();
		_lostFocus /* = _focus.visible */= false;
		// stage.frameRate = _flashFramerate;
		FlxG.resumeSounds(true);
		FlxG.restoreShaders();
	}

	/**
	 * Internal event handler for input and focus.
	 * 
	 * @param	FlashEvent	Flash event.
	 */
	protected void onFocusLost(Event FlashEvent)
	{
		flash.ui.Mouse.show();
		_lostFocus = /* _focus.visible = */true;
		// stage.frameRate = 10;
		FlxG.pauseSounds(true);
	}

	/**
	 * Handles the onEnterFrame call and figures out how many updates and draw calls to do.
	 * 
	 * @param	FlashEvent	Flash event.
	 */
	public void onEnterFrame(Event FlashEvent)
	{
		long mark = System.currentTimeMillis();
		long elapsedMS = mark-_total;
		_total = mark;
		updateSoundTray(elapsedMS);
		if(!_lostFocus)
		{
			if((_debugger != null) && _debugger.vcr.paused)
			{
				if(_debugger.vcr.stepRequested)
				{
					_debugger.vcr.stepRequested = false;
					step();
				}
			}
			else
			{
				_accumulator += elapsedMS;
				if(_accumulator > _maxAccumulation)
					_accumulator = _maxAccumulation;
				while(_accumulator >= _step)
				{
					step();
					_accumulator = _accumulator - _step;
				}
			}

			FlxBasic._VISIBLECOUNT = 0;
			draw();

			if(_debuggerUp)
			{
				_debugger.perf.flash((int) elapsedMS);
				_debugger.perf.visibleObjects(FlxBasic._VISIBLECOUNT);
				_debugger.perf.update();
				_debugger.watch.update();
			}
		}
	}

	/**
	 * If there is a state change requested during the update loop,
	 * this function handles actual destroying the old state and related processes,
	 * and calls creates on the new state and plugs it into the game object.
	 */
	protected void switchState()
	{
		//Basic reset stuff
		FlxG.resetCameras();
		FlxG.resetInput();
		FlxG.destroySounds();
		FlxG.clearBitmapCache();

		//Clear the debugger overlay's Watch window
		if(_debugger != null)
			_debugger.watch.removeAll();

		//Clear any timers left in the timer manager
		TimerManager timerManager = FlxTimer.getManager();
		if(timerManager != null)
			timerManager.clear();

		//Destroy the old state (if there is an old state)
		if(_state != null)
		{
			_state.destroy();
			_state = null;
		}
		if(showSplashScreen && !FlxSplashScreen.splashScreenShown)
		{
			_state = new FlxSplashScreen(_requestedState);
			_requestedState = _state;
			_state.create();
		}
		else
		{
			//Finally assign and create the new state
			_state = _requestedState;
			_state.create();
		}
	}

	/**
	 * This is the main game update logic section.
	 * The onEnterFrame() handler is in charge of calling this
	 * the appropriate number of times each frame.
	 * This block handles state changes, replays, all that good stuff.
	 */
	protected void step()
	{
		//handle game reset request
		if(_requestedReset)
		{
			_requestedReset = false;
			try
			{
				_requestedState = ClassReflection.newInstance(_iState);
			}
			catch(Exception e)
			{
				throw new RuntimeException(e);
			}
			_replayTimer = 0;
			_replayCancelKeys = null;
			if(showSplashScreen)
				FlxSplashScreen.splashScreenShown = false;
			FlxG.reset();
		}

		//handle replay-related requests
		if(_recordingRequested)
		{
			_recordingRequested = false;
			_replay.create(FlxG.globalSeed);
			_recording = true;
			if(_debugger != null)
			{
				_debugger.vcr.recording();
				FlxG.log("FLIXEL: starting new flixel gameplay record.");
			}
		}
		else if(_replayRequested)
		{
			_replayRequested = false;
			_replay.rewind();
			FlxG.globalSeed = _replay.seed;
			if(_debugger != null)
				_debugger.vcr.playing();
			_replaying = true;
		}

		//handle state switching requests
		if(_state != _requestedState)
			switchState();

		//finally actually step through the game physics
		FlxBasic._ACTIVECOUNT = 0;
		if(_replaying)
		{
			_replay.playNextFrame();
			if(_replayTimer > 0)
			{
				_replayTimer -= _step;
				if(_replayTimer <= 0)
				{
					if(_replayCallback != null)
					{
						_replayCallback.callback();
						_replayCallback = null;
					}
					else
						FlxG.stopReplay();
				}
			}
			if(_replaying && _replay.finished)
			{
				FlxG.stopReplay();
				if(_replayCallback != null)
				{
					_replayCallback.callback();
					_replayCallback = null;
				}
			}
			if(_debugger != null)
				_debugger.vcr.updateRuntime(_step);
		}
		else
			FlxG.updateInput();
		if(_recording)
		{
			_replay.recordFrame();
			if(_debugger != null)
				_debugger.vcr.updateRuntime(_step);
		}
		update();
		FlxG.mouse.wheel = 0;
		if(_debuggerUp)
			_debugger.perf.activeObjects(FlxBasic._ACTIVECOUNT);
	}

	/**
	 * This function just updates the sound tray object.
	 */
	protected void updateSoundTray(float MS)
	{
		//animate stupid sound tray thing

		if(_soundTray != null)
		{
			if(_soundTrayTimer > 0)
				_soundTrayTimer -= MS/1000;
			else if(_soundTray.y > -_soundTray.height)
			{
				_soundTray.y -= (MS/1000)*FlxG.height*2;
				if(_soundTray.y <= -_soundTray.height)
				{
					_soundTray.visible = false;

					//Save sound preferences
					FlxSave soundPrefs = new FlxSave();
					if(soundPrefs.bind("flixel"))
					{
						soundPrefs.data.put("mute", FlxG.mute);
						soundPrefs.data.put("volume", FlxG.getVolume());
						soundPrefs.close();
					}
				}
			}
		}
	}

	/**
	 * This function is called by step() and updates the actual game state.
	 * May be called multiple times per "frame" or draw call.
	 */
	protected void update()
	{
		long mark = System.currentTimeMillis();

		FlxG.elapsed = FlxG.timeScale*(_step/1000.f);
		FlxG.updateSounds();
		FlxG.updatePlugins();
		_state.update();
		FlxG.updateCameras();

		// TODO: temporary key for turning on visual debug, delete when
		// FlxDebugger complete
		if(FlxG.keys.justPressed("F2") && (FlxG.debug || forceDebugger))
			FlxG.visualDebug = !FlxG.visualDebug;

		if(_debuggerUp)
			_debugger.perf.flixelUpdate((int)(System.currentTimeMillis()-mark));
	}

	/**
	 * Goes through the game state and draws all the game objects and special effects.
	 */
	protected void draw()
	{
		long mark = System.currentTimeMillis();

		int i = 0;
		int l = FlxG._displayList.size;

		while(i < l)
		{
			FlxG._activeCamera = FlxG._displayList.get(i++);

			FlxG.lockCameras();
			_state.draw();
			FlxG.drawPlugins();
			FlxG.unlockCameras();
		}

		FlxG.batch.setProjectionMatrix(_fontCamera.combined);
		FlxG.batch.begin();
		FlxG._gl.glScissor(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

		// TODO: This is a little silly. Is it worth making a Flash Sprite class
		// for this instead?
		if(!FlxG.mobile && _mouse.visible)
		{
			if(_mouse.length > 0)
				FlxG._activeCamera = _mouse.getFirstAlive().cameras.first();
			_mouse.draw();
		}

		// Draw fps display TODO: needs to be moved to debugger some day.
		if(FlxG.debug)
		{
			_stringBuffer.delete(0, _stringBuffer.length());
			_stringBuffer.append("fps:");
			_stringBuffer.append(Gdx.graphics.getFramesPerSecond());
			_font.draw(FlxG.batch, _stringBuffer.toString(), Gdx.graphics.getWidth() - 80, 0);
		}
		FlxG.batch.end();
		if(_debuggerUp)
			_debugger.perf.flixelDraw((int)(System.currentTimeMillis()-mark));
	}

	/**
	 * Used to instantiate the guts of the flixel game object once we have a valid reference to the root.
	 * 
	 * @param	FlashEvent	Just a Flash system event, not too important for our purposes.
	 */
	protected void create(Event FlashEvent)
	{
		if(_created)
			return;
		stage.removeEventListener(Event.ADDED_TO_STAGE, addedToStageListener);
		_total = System.currentTimeMillis();

		//Enable logging
		Gdx.app.setLogLevel(Application.LOG_DEBUG);

		//TODO: Move this back up to constructor
		if(!useSystemCursor)
			flash.ui.Mouse.hide();

		//Set up OpenGL
		FlxG._gl = Gdx.gl20;

		FlxG.batch = new SpriteBatch();

		//Add basic input event listeners and mouse container
		stage.addEventListener(TouchEvent.TOUCH_BEGIN, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onMouseDown((TouchEvent) e);
			}
		});
		stage.addEventListener(TouchEvent.TOUCH_END, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onMouseUp((TouchEvent) e);
			}
		});
		stage.addEventListener(MouseEvent.MOUSE_WHEEL, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onMouseWheel((MouseEvent) e);
			}
		});
		stage.addEventListener(KeyboardEvent.KEY_DOWN, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onKeyDown((KeyboardEvent) e);
			}
		});
		stage.addEventListener(KeyboardEvent.KEY_UP, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onKeyUp((KeyboardEvent) e);
			}
		});

		//Detect whether or not we're running on a mobile device
		FlxG.mobile = (Gdx.app.getType().equals(ApplicationType.Android) || Gdx.app.getType().equals(ApplicationType.iOS));

		//Let mobile devs opt out of unnecessary overlays.
		if(!FlxG.mobile)
		{
			//Debugger overlay
			if(FlxG.debug || forceDebugger)
			{
				// _debugger = new FlxDebugger(FlxG.width*FlxCamera.defaultZoom,FlxG.height*FlxCamera.defaultZoom);
				// addChild(_debugger);
			}

			//Volume display tab
			createSoundTray();
			
			//Focus gained/lost monitoring
			createFocusScreen();
		}

		stage.addEventListener(Event.DEACTIVATE, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onFocusLost(e);
			}
		});
		stage.addEventListener(Event.ACTIVATE, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onFocus(e);
			}
		});

		//More event listeners
		stage.addEventListener(Event.RESIZE, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onResize();
			}
		});
		stage.addEventListener(Event.REMOVED_FROM_STAGE, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				destroy();
			}
		});

		//Finally, set up an event listener for the actual game loop stuff.
		stage.addEventListener(Event.ENTER_FRAME, new IEventListener()
		{
			@Override
			public void onEvent(Event e)
			{
				onEnterFrame(e);
			}
		});
		
		_created = true;

		// TODO: Move to FlxDebugger
		_font = new BitmapFont(Gdx.files.classpath("org/flixel/data/font/nokiafc22.fnt"), Gdx.files.classpath("org/flixel/data/font/nokiafc22.png"), true);
		_font.getData().setScale(2);
		_fontCamera = new OrthographicCamera();
		_fontCamera.setToOrtho(true);
		_stringBuffer = new StringBuilder();
	}

	/**
	 * Sets up the "sound tray", the little volume meter that pops down sometimes.
	 */
	// TODO: Sound tray
	protected void createSoundTray()
	{
		// _soundTray.visible = false;
		// _soundTray.scaleX = 2;
		// _soundTray.scaleY = 2;
		// var tmp:Bitmap = new Bitmap(new BitmapData(80,30,true,0x7F000000));
		// _soundTray.x =
		// (FlxG.width/2)*FlxCamera.defaultZoom-(tmp.width/2)*_soundTray.scaleX;
		// _soundTray.addChild(tmp);

		// var text:TextField = new TextField();
		// text.width = tmp.width;
		// text.height = tmp.height;
		// text.multiline = true;
		// text.wordWrap = true;
		// text.selectable = false;
		// text.embedFonts = true;
		// text.antiAliasType = AntiAliasType.NORMAL;
		// text.gridFitType = GridFitType.PIXEL;
		// text.defaultTextFormat = new
		// TextFormat("system",8,0xffffff,null,null,null,null,null,"center");;
		// _soundTray.addChild(text);
		// text.text = "VOLUME";
		// text.y = 16;

		// var bx:uint = 10;
		// var by:uint = 14;
		// _soundTrayBars = new Array();
		// var i:uint = 0;
		// while(i < 10)
		// {
		// tmp = new Bitmap(new BitmapData(4,++i,false,0xffffff));
		// tmp.x = bx;
		// tmp.y = by;
		// _soundTrayBars.push(_soundTray.addChild(tmp));
		// bx += 6;
		// by--;
		// }

		// _soundTray.y = -_soundTray.height;
		// _soundTray.visible = false;
		// addChild(_soundTray);

		//load saved sound preferences for this game if they exist
		FlxSave soundPrefs = new FlxSave();
		if(soundPrefs.bind("flixel"))// && (soundPrefs.data.get("sound") != null))
		{
			if(soundPrefs.data.get("volume", Float.class) != null)
				FlxG.setVolume(soundPrefs.data.get("volume", Float.class));
			if(soundPrefs.data.get("mute", Boolean.class) != null)
				FlxG.mute = soundPrefs.data.get("mute", Boolean.class);
			soundPrefs.destroy();
		}
	}

	/**
	 * Sets up the darkened overlay with the big white "play" button that appears when a flixel game loses focus.
	 */
	// TODO: Focus screen
	protected void createFocusScreen()
	{
		// var gfx:Graphics = _focus.graphics;
		// var screenWidth:uint = FlxG.width*FlxCamera.defaultZoom;
		// var screenHeight:uint = FlxG.height*FlxCamera.defaultZoom;

		// draw transparent black backdrop
		// gfx.moveTo(0,0);
		// gfx.beginFill(0,0.5);
		// gfx.lineTo(screenWidth,0);
		// gfx.lineTo(screenWidth,screenHeight);
		// gfx.lineTo(0,screenHeight);
		// gfx.lineTo(0,0);
		// gfx.endFill();

		// draw white arrow
		// var halfWidth:uint = screenWidth/2;
		// var halfHeight:uint = screenHeight/2;
		// var helper:uint = FlxU.min(halfWidth,halfHeight)/3;
		// gfx.moveTo(halfWidth-helper,halfHeight-helper);
		// gfx.beginFill(0xffffff,0.65);
		// gfx.lineTo(halfWidth+helper,halfHeight);
		// gfx.lineTo(halfWidth-helper,halfHeight+helper);
		// gfx.lineTo(halfWidth-helper,halfHeight-helper);
		// gfx.endFill();

		// var logo:Bitmap = new ImgLogo();
		// logo.scaleX = int(helper/10);
		// if(logo.scaleX < 1)
		// logo.scaleX = 1;
		// logo.scaleY = logo.scaleX;
		// logo.x -= logo.scaleX;
		// logo.alpha = 0.35;
		// _focus.addChild(logo);

		// addChild(_focus);
	}

	/**
	 * Internal event handler for stage resizing.
	 */
	protected void onResize()
	{		
		if(FlxCamera.defaultScaleMode == FlxCamera.RESIZE_WIDTH)
			FlxG.width = (int)((stage.getStageWidth()/(float)stage.getStageHeight())*FlxG.height);
		
		//reset all the cameras
		for(FlxCamera camera : FlxG.cameras)
			camera.setScaleMode(camera.getScaleMode());
	}

	/**
	 * Internal event handler for stage destruction.
	 */
	protected void destroy()
	{
		_font.dispose();
		_stringBuffer = null;
		_state.destroy();
		FlxG.reset();
		FlxG.disposeAssetManager();
		FlxG.batch.dispose();
	}
	
	/**
	 * Internal event listener.
	 */
	private final IEventListener addedToStageListener = new IEventListener()
	{
		@Override
		public void onEvent(Event e)
		{
			create(e);
		}
	};
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy