
/****************************************************************
 * Project:  Attack of the Crows                                *
 * Created:  December 2016                                      *
 * Developer: Gar Benjamin                                      *
 *                                                              *
 * NOTES: This is basically how I always develop games no       *
 *		  matter what I am using (GLBasic, Unity, MonkeyX,      *
 *		  AGK or whatever.								    	*
 *		  Same for the language whether BASIC or C/C++, etc     *
 *                                                              *
 * FIRST LET ME SAY... this certainly is not perfect.           *
 * It is just something I threw together as a test of developing*
 * a game using AppGameKit.                                     *
 *                                                              *
 * Basically, I create a series of managers. Each manager is    *
 * responsible for managing a set of a distinct type of objects *
 * For example, PlayerManager handles the player.               *
 * EnemyManager handles all of the enemies.                     *
 * States are defined in a single file and are used heavily to  *
 * control the objects.                                         *
 *                                                              *
 * Managers dealing with arrays of objects such as EnemyManager *
 * and BulletManager create all of the sprites one time and set *
 * them up.                                                     *
 *                                                              *
 * Types are used to wrap up objects and contain everything from*
 * the state, position, frame to the id of the sprite used to   *
 * represent the object on the screen.                          *
 * In this way, object pooling becomes automatic. Basically     *
 * these arrays are simply pools of distinct object types.      *
 *                                                              *
 * I hereby release this program and related media into the     *
 * public domain. All graphics and sounds were created by me.   *
 * You may use this for any purpose you desire. You can freely  *
 * borrow the code, add on to the game and publish it as your   *
 * own, etc.                                                    *
 *                                                              *
 * KEEP IN MIND! I have already published this version at       *
 * GameJolt under the name of Attack of the Crows               *
 * So it would be advisable to change the name and create a new *
 * title screen before updating and releasing the game as yours.*
 * Due to the way I structure my programs it should be very easy*
 * to convert this from BASIC to C++ if you so desire. That is  *
 * a big part of why I develop this way. So I can easily jump   *
 * from one language to another with little "downtime".         *
 *                                                              *
 * NOTE: The palette I used is actually the PICO-8 palette that *
 *       I modified to turn into a sort of Extra HalfBrite      *
 *       version. Meaning, there are twice as many colors in my *
 *       version of the palette. Which each new color being a   *
 *       darker (half as bright) version of the original color. *
 *       Those familiar with the Amiga computer will realize I  *
 *       drew inspiration from the EHB mode for my customization*
 ****************************************************************/

#include "types.agc"
#include "StateDefinitions.agc"
#include "EnemyManager.agc"
#include "SpriteImages.agc"
#include "PlayerManager.agc"
#include "EnemyManager.agc"
#include "BulletManager.agc"
#include "CollisionManager.agc"
#include "ExplosionManager.agc"
#include "AudioManager.agc"
#include "StatManager.agc"


// show all errors
SetErrorMode(2)


global gameState as integer


// Set up the display. Note in my tests in order to achieve ~60 fps I need to request 63 fps
InitDisplay("Attack of the Crows", 768, 432, 63)		// 1024,576		768,432
LoadImages()

// No need to clear the screen to a solid color at start of each rendering frame
EnableClearColor(0)


// Set up the Backdrop sprite
global sprBackdrop as integer
sprBackdrop = CreateSprite(IMG_BACKDROP)
SetSpriteTransparency(sprBackdrop, 0)
SetSpritePosition(sprBackdrop, 0, 0)
SetSpriteVisible(sprBackdrop, 0)


// Set up the Title Screen sprite
global sprTitleScreen as integer
sprTitleScreen = CreateSprite(IMG_TITLE)
SetSpriteTransparency(sprTitleScreen, 0)
SetSpritePosition(sprTitleScreen, 0, 0)
SetSpriteVisible(sprTitleScreen, 0)


// Could use one sprite to display all of these screens but I am not doing that here
// Creating a new global variable for the GameOver display
global sprGameOver as integer
sprGameOver = CreateSprite(IMG_GAMELOST)	// use an image here. If use 0 then the sprite will not be auto-scaled by SetViewZoom
SetSpriteTransparency(sprGameOver, 0)
SetSpritePosition(sprGameOver, 0, 0)
SetSpriteVisible(sprGameOver, 0)


Global actionTimer as float
Global score as integer
Global curLevel as integer
Global EnemiesRemaining as integer

Global lastFrame as float
Global FTS as float


PlayerInit()
EnemyInit()
BulletInit()
ParticleInit()
ExplosionInit()
AudioInit()
StatInit()

SetVSync(1)


// start at title screen
GameSetNewState(GAMESTATE_TITLE)

if GetDeviceBaseName() = "android" or GetDeviceBaseName() = "ios"
//left button
	if GetVirtualButtonExists(1) = 0 
		AddVirtualButton(1,75,332,100)
		SetVirtualButtonAlpha(1,75)
	endif
	//right button
	if GetVirtualButtonExists(2) = 0 
		AddVirtualButton(2,200,332,100)
		SetVirtualButtonAlpha(2,75)
	endif
	//fire button
	if GetVirtualButtonExists(3) = 0 
		AddVirtualButton(3,643,332,100)
		SetVirtualButtonAlpha(3,75)
	endif
endif


lastFrame# = Timer()
repeat
	thisFrame#  = Timer()
	FTS = thisFrame# - lastFrame#	
	lastFrame#  = thisFrame#
	
	// limit the scale factor to prevent glitches when other processes steal time, etc
	if FTS > 0.25 then FTS = 0.25

	// Generally I'd break these out into dedicated functions instead of handling them all in the loop here
	// but this works fine for such a tiny game as this one.
	select gameState
		case GAMESTATE_PAUSE_BEFORE_LOSTSCREEN
			actionTimer = actionTimer + (1 * FTS)
			if actionTimer > 1.5 then GameSetNewState(GAMESTATE_GAME_LOST)
		endcase
		
		case GAMESTATE_TITLE
			if InputRequestShoot()
				GameSetNewState(GAMESTATE_GAMEPLAY)
			endif
		endcase
		
		case GAMESTATE_GAMEPLAY
			PlayerUpdate()
			EnemyUpdate()
			BulletUpdate()
			ParticleUpdate()
			ExplosionUpdate()
			
			// Update the actual AGK sprites positions and images
			PlayerDraw()
			EnemyDraw()
			BulletDraw()
			ParticleDraw()
			ExplosionDraw()
			
			// After the AGK sprites have been updated now check for collisions
			CheckForPlayerBulletHitEnemy()
			CheckForPlayerHitByExplosion()
			CheckForPlayerCollectedBullet()

			if PlayerLostGame()
				GameSetNewState(GAMESTATE_PAUSE_BEFORE_LOSTSCREEN)
			
			elseif EnemyClearedWave()
				if curLevel < 9
					PlaySound(SND_WAVECLEARED)
					curLevel = curLevel + 1
					StatUpdateLevelDisplay()
					EnemyCreateWave()
					
				// player won the game
				else
					GameSetNewState(GAMESTATE_GAME_WON)
				endif
			endif
		endcase
		
		case GAMESTATE_GAME_LOST
			actionTimer = actionTimer + (1 * FTS)
			
			// short delay before player can exit by clicking fire
			if actionTimer > 1.5
				if InputRequestShoot() or actionTimer > 6
					GameSetNewState(GAMESTATE_TITLE)
				endif
			endif
		endcase
		
		case GAMESTATE_GAME_WON
			actionTimer = actionTimer + (1 * FTS)
			
			// short delay before player can exit by clicking fire
			if actionTimer > 1.5
				if InputRequestShoot() or actionTimer > 6
					GameSetNewState(GAMESTATE_TITLE)
				endif
			endif
		endcase
    endselect

	Sync()
	
	// comment this next line when building for HTML5
	if GetRawLastKey() = 27 then GameSetNewState(GAMESTATE_EXITED)	
until GAMESTATE_EXITED = gameState

end


function InitDisplay(title as string, width as integer, height as integer, maxFrameRate as float)
	// set window properties
	SetWindowTitle(title)
	SetWindowSize(width, height, 0)

	// set display properties
	//SetVirtualResolution(768, 432)
	SetVirtualResolution(width, height)
	SetOrientationAllowed(1, 1, 1, 1)
	SetSyncRate(maxFrameRate, 0)

	SetViewZoom(width / 256)
endfunction

function GameSetNewState(newState as integer)
	select newState
		// This state exists to "hold" the display so the player has a moment to see they have lost all lives
		// and kind of create a more dramatic effect for having lost
		case GAMESTATE_PAUSE_BEFORE_LOSTSCREEN
			actionTimer = 0
		endcase
		
		case GAMESTATE_TITLE
			SetSpriteVisible(sprGameOver, 0)
			SetSpriteVisible(sprTitleScreen, 1)
			PlayerHide()
			StatHide()
			EnemyDeactivateAll()
			BulletDeactivateAll()
			ExplosionDeactivateAll()
			ParticleDeactivateAll()
		endcase
		
		case GAMESTATE_GAMEPLAY
			SetSpriteVisible(sprTitleScreen, 0)
			SetSpriteVisible(sprBackdrop, 1)
			curLevel = 1
			score = 0
			PlayerReset()
			StatReset()
			EnemyCreateWave()
			lastFrame# = Timer()
		endcase
		
		case GAMESTATE_GAME_LOST
			PlayerHide()
			StatHide()
			EnemyDeactivateAll()
			BulletDeactivateAll()
			ExplosionDeactivateAll()
			ParticleDeactivateAll()
			SetSpriteImage(sprGameOver, IMG_GAMELOST)
			SetSpriteVisible(sprGameOver, 1)
			SetSpriteVisible(sprBackdrop, 0)
			actionTimer = 0
		endcase
		
		case GAMESTATE_GAME_WON
			PlayerHide()
			StatHide()
			EnemyDeactivateAll()
			BulletDeactivateAll()
			ExplosionDeactivateAll()
			ParticleDeactivateAll()
			SetSpriteImage(sprGameOver, IMG_GAMEWON)
			SetSpriteVisible(sprGameOver, 1)
			SetSpriteVisible(sprBackdrop, 0)
			actionTimer = 0
		endcase
	endselect
	
	gameState = newState
endfunction


