// BlinkGame - Hunt for the Lost Pixels
// an SDL game in honor of the BlinkenArea crew
//
// (C) 2005-06 Rene Schickbauer
//
// See LICENSE for licensing information
//
#ifdef NOP_CODE
/*--------------------------------------------------------------------
					CVS AUTO-GENERATED INFO
$Author: cavac $
$Date: 2006/03/16 10:47:53 $
$Name: rel-0-3-patches $
$Revision: 1.21 $
$State: Exp $

$Log: engine.cpp,v $
Revision 1.21  2006/03/16 10:47:53  cavac
Changes some .bmp to .jpg for smaller downloads
Re-compressed sisters_orient.ogg for smaller download
Updated .cpp files to reflect changes

Revision 1.20  2006/03/15 22:54:08  cavac
* Added property/methods for setting FG objects as "ontop" which renders
them on top of everything.
* Changed levelhandler.* so that it uses the newly added "AddPixel" function
instead of doing it internally in the file parser state machine.
* Added functionality for bonus live collectables
* Adapted lua bindings so it can utilize above properties
* Removed l2b prefix from lua bindings and adapted scripts
* Added temporary 32x32 explosion graphics
* Adapted level0.bsl (Testlevel/scripting) so it has a small riddle:
  You have to turn on all switches and press the emergency stop button
  while the neon sign is ON (e.g. it lights up yellow). two small
  explosions leave one pixel and one bonus live collectible behind.
  (You can NOT finish the level without collecting the second pixel!)

Revision 1.19  2006/03/10 14:14:54  cavac
Removed "l2b" prefix from BlinkenLUA functions in scripting support

Revision 1.18  2006/03/02 13:03:41  cavac
Re-formatted all .cpp and .h files a bit to get rid of the TAB/SPACE mess
that makes troubles with XCode's "re-format" feature

Revision 1.17  2006/02/26 14:06:04  cavac
Added menu support

Revision 1.16  2006/02/26 11:02:08  cavac
Added delay to maximize FPS to 50FPS

Revision 1.15  2006/02/24 18:48:26  cavac
Finally added real scripting support
Added Testlevel (level0) which can be enabled from globals.h

Revision 1.14  2006/02/23 20:22:07  dauti
Modified hardcoded "8" to SDL constant "SDLK_BACKSPACE"

Revision 1.13  2006/02/18 12:04:21  cavac
Added check to cheatcodes for maximum length of input string

Revision 1.12  2006/02/18 11:03:15  dauti
Bugfixed the cheatcode enter routine:
- fixed the crash when entering illegal characters
  (Bug: [ 1423704 ] Crash when entering cheatcode)
- Support for the backspace key
- switching fullscreenmode is not possible when entering cheatcode
  (so cheatcodes may contain the character 'f')

Revision 1.11  2006/02/17 19:07:59  cavac
Added rudimentary sound effects (FX) support

Revision 1.10  2006/02/13 09:53:51  cavac
Added BOSS-Key (ESC is instant exit)

Revision 1.9  2006/02/12 11:11:35  cavac
Changed INT to LONG for calculating fastcopy pointers
Added (commented out) section for new background handling

Revision 1.8  2006/02/10 18:56:17  cavac
Code Audit: Fixed many 64Bit based problems (mostly untested)

Revision 1.7  2006/02/10 17:50:26  cavac
Added ActionCapture feature (define ALLOW_ACTIONCAPTURE to allow this
resource-hungry extra)

Revision 1.6  2006/02/10 12:11:56  cavac
Fixed Bug: Update lastTick in engine.cpp after displaying Image when
player has lost a live: lastTick was not updated, so 2 seconds of gameplay
where lost because physicsEngine did recompute that timeslot.

Revision 1.5  2006/01/23 19:19:45  cavac
Added define ENABLE_FULLSCREEN to enable/disable the availability
of the full-screen mode
Added define START_WITH_FULLSCREEN to set default screen mode when
ENABLE_FULLSCREEN is defined
User can now switch between windowed and fullscreen with the "f"-key

Revision 1.4  2006/01/23 18:47:55  cavac
Patches for unix make system for building on Linux  and other
   Unix systems (thanks to: Dauti)
Tests for new colission detection (default: Use old one)
Added Joystick/Gamepad support (only tested with a cheap logitech gamepad)
Fixed some warnings on linux/gcc3
Added true-type fonts for planned menu system
Added library SDL_ttf in xcode for planned menu-system

Revision 1.3  2006/01/18 01:22:39  cavac
Added SDL_Image to load additional graphics formats
Updated osdef* and xcode-project to to add SDL_Image
Updated all image-loader functions to use SDL_Image
Added level6 as temporary testlevel
Added code to display start- and goal-points with an animation (needs some fixing)
Removed temporary "marker"-tiles from level-definitions, as we have now an
animated display for that.

Revision 1.2  2006/01/09 00:50:34  cavac
Bugfixed CVS autogenerated entries in background.cpp
Added CVS autogenerated entries to remaining .cpp and .h files
Updated copyright from "2005 Rene Schickbauer" to "2005-06 Rene Schickbauer"


--------------------------------------------------------------------*/
#endif //NOP_CODE

#include "globals.h"
#include "engine.h"
#include "levelhandler.h"
#include "background.h"
#include "drawprimitives.h"
#include "showtext.h"
#include "debug.h"
#include "tiles.h"
#include "playersprite.h"
#include "colission.h"
#include "showloading.h"
#include "gameengine.h"
#include "showpicture.h"
#include "joystick.h"
#ifdef ALLOW_ACTIONCAPTURE
#include "actioncapture.h"
#endif // ALLOW_ACTIONCAPTURE
#include "sound.h"
#include "fgobjects.h"
#include "bl_lua.h"
#include "fonthandler.h"

#include <math.h>
#include <string.h>

Uint32 fgXoffs = 0;
Uint32 fgYoffs = 0;
Uint32 spriterelx;
Uint32 spriterely;
Sint32 leftright = 0;
Sint32 updown = 0;
MOVE_TYPE mtype = NO_MOVE;
double spritex, spritey, spritevx, spritevy;
Uint32 mcoltype;
bool isJumping;
bool gameRunning;
double blinkdir = BLINKSPEED;
double blinkbright = 100;
Uint32 foundPixels = 0;
char errorMessage[100];

void initEngine()
{	
	initPlayerSprite();
	initLevel(gamedata.player->level);
	initMonsters();
	gLastTick = SDL_GetTicks();
	
	spritex = lhandle.start_x;
	spritey = lhandle.start_y;
	spritevx = 0;
	spritevy = 0;
	leftright = 0;
	isJumping = false;
	gameRunning = true;
	foundPixels = 0;
	

	/*
	Uint32 idx;
	idx = addFGObjGFX("sister_moveleft.bmp"); // IDX = 0
	printf("Got GFX IDX %d\n", idx);
	idx = addFGObjGFX("sister_moveright.bmp"); // IDX = 1
	printf("Got GFX IDX %d\n", idx);
	
	idx = addFGObj(0, 8 * 32, 19 * 32, true, true);
	printf("Got OBJ IDX %d\n", idx);
	idx = addFGObj(1, 10 * 32, 18 * 32, true, false);
	printf("Got OBJ IDX %d\n", idx);
	*/
}

void deInitEngine ()
{
	deInitLevel();
	deInitPlayerSprite();
	
	// Test FGObjects
	deInitFGObjs();
}


void renderEngine()
{
	
	errorMessage[0] = 0;
	
	// Ask SDL for the time in milliseconds
	Uint32 tick = SDL_GetTicks();
	
	// Delay a bit if we're too fast
	while (tick <= gLastTick) {
		SDL_Delay(1);
		//sprintf(errorMessage, "TOO FAST!");
		tick = SDL_GetTicks();
	}

	
	
	// Physics-Engine
	while (gLastTick < tick)
	{
		//fgXoffs+= leftright * 3;
		//fgYoffs+= updown * 3;
		gLastTick += 1000 / PHYSICSFPS;

		// Blinking of pixels
		blinkbright += blinkdir;
		if(blinkbright < 10 || blinkbright > 250) {
			blinkdir = 0 - blinkdir;
		}
		
		monsterPhysics((Uint32)spritex, (Uint32)spritey);
		if(lhandle.hasScript) {
			blLuaCall("scriptPhysics", "");
		}
		
		// Left/Right movement with smooth acceleration/braking
		if(leftright > 0) {
			mtype = MOVE_RIGHT;
			spritevx += SPRITE_ACCEL;
		} else if(leftright < 0) {
			mtype = MOVE_LEFT;
			spritevx -= SPRITE_ACCEL;
		} else {
			mtype = NO_MOVE;
			if(spritevx > SPRITE_ACCEL) {
				spritevx -= SPRITE_ACCEL;
			} else if(spritevx < -SPRITE_ACCEL) {
				spritevx += SPRITE_ACCEL;
			} else {
				spritevx = 0;
			}
		}
		
		
		
		if(spritevx > SPRITE_MAXSPEED) {
			spritevx = SPRITE_MAXSPEED;
		} else if(spritevx < -SPRITE_MAXSPEED) {
			spritevx = -SPRITE_MAXSPEED;
		}
		spritex += spritevx;
		
		// Gravity
		spritevy += SPRITE_GRAVITY;
		
		if(spritevy > SPRITE_MAXSPEED) {
			spritevy = SPRITE_MAXSPEED;
		} else if(spritevy < -SPRITE_MAXSPEED) {
			spritevy = -SPRITE_MAXSPEED;
		}
		
		spritey += spritevy;		
		
		// Limit movement to stay within game-area
		if(spritex > (lhandle.width - 32)) {
			spritevx = 0;
			spritex = lhandle.width - 32;
		} else if(spritex < 0) {
			spritevx = 0;
			spritex = 0;
		}
		
		
		if(spritey > (lhandle.height - 32)) {
			spritevy = 0;
			spritey = lhandle.height - 32;
		} else if(spritey < 0) {
			spritevy = 0;
			spritey = 0;
		}
		
		mcoltype = getPlayerMonsterColission((Uint32)spritex, (Uint32)spritey);
		if(mcoltype & COL_UP || mcoltype & COL_LEFT || mcoltype & COL_RIGHT) {
			spritevx = 0;
			spritevy = 0;
			spritex = lhandle.start_x;
			spritey = lhandle.start_y;
			gamedata.player->lives--;
			soundPlayFX(FX_KILL_PLAYER);
			showPicture("livelost.jpg", 2000);
#ifdef ALLOW_ACTIONCAPTURE
			stopActionCapture();
#endif // ALLOW_ACTIONCAPTURE			
			gLastTick = SDL_GetTicks();
			return;
		}
		
checkcolission:
		mcoltype = getTilesColission((Uint32)spritex, (Uint32)spritey);

		if(mcoltype & COL_DOWN && spritevy > 0) {
			spritevy = 0;
			spritey = round(spritey/TILESIZE)*TILESIZE;
			goto checkcolission;
		}

		else if(mcoltype & COL_DOWN || mcoltype & COL_UP) {
			spritevy = 0;
			spritey = round(spritey/TILESIZE)*TILESIZE;
		}
				
		else if((mcoltype & COL_LEFT || mcoltype & COL_RIGHT) && spritevx != 0) {
			spritevx = 0;
			spritex = round(spritex/TILESIZE)*TILESIZE;
			goto checkcolission;
		}
		
		// Check for "colission" with goal tile
		if(spritex >= lhandle.goal_x && spritex < (lhandle.goal_x + TILESIZE) &&
		   spritey > lhandle.goal_y && spritey < (lhandle.goal_y + TILESIZE)) {
			if(foundPixels == lhandle.numPixels) {
				printf("LEVEL FINISHED\n");
				gameRunning = false;
			} else {
				sprintf(errorMessage, "NOT ENOUGH PIXELS TO EXIT!");
			}
		}
		
		handlePixelCollission((Uint32)spritex, (Uint32)spritey);
		
	}
	
	
	// Calculate relative positions of sprite, tiles and background
	spriterelx = SCR_WIDTH / 2;
	spriterely = SCR_HEIGHT / 2;
	
	if(spritex < spriterelx) {
		fgXoffs = 0;
		spriterelx = (int)spritex;
	} else if(spritex > (lhandle.width - spriterelx)) {
		spriterelx = SCR_WIDTH - (lhandle.width - (int)spritex);
		fgXoffs = lhandle.width - SCR_WIDTH;
	} else {
		fgXoffs = (int)spritex - spriterelx;
	}
	
	if(spritey < spriterely) {
		fgYoffs = 0;
		spriterely = (int)spritey;
	} else if(spritey > (lhandle.height - spriterely)) {
		spriterely = SCR_HEIGHT - (lhandle.height - (int)spritey);
		fgYoffs = lhandle.height - SCR_HEIGHT;
	} else {
		fgYoffs = (int)spritey - spriterely;
	}
	
	
	// Lock screen if needed
	if (SDL_MUSTLOCK(gScreen))
		if (SDL_LockSurface(gScreen) < 0) 
			return;
	
	drawBackground(fgXoffs / 2, fgYoffs / 2);
	paintLevelTiles(fgXoffs, fgYoffs);
	paintFGObjs(fgXoffs, fgYoffs, false);
	paintSpecialTiles(fgXoffs, fgYoffs, (foundPixels == lhandle.numPixels));
	paintLevelPixels(fgXoffs, fgYoffs, (Uint32)blinkbright);
	showMonsterSprites(fgXoffs, fgYoffs);
	showSprite(spriterelx, spriterely, mtype);
	paintFGObjs(fgXoffs, fgYoffs, true);
	
	/*
	//renderFontHandlerText(Sint32 x, Sint32 y, char* text, bool active, bool hcentered, bool vcentered);
	renderFontHandlerText(10, 100, "Hallo", true, true, false);
	renderFontHandlerText(10, 170, "Welt!", false, true, false);
	*/
	
	char pixelsString[100];
	sprintf(pixelsString, "Pixels: %d (%d)", foundPixels, lhandle.numPixels);
	drawstring(20, 20, pixelsString, gScreen);
	
	char livesString[100];
	sprintf(livesString, "Lives: %d", gamedata.player->lives);
	drawstring(200, 20, livesString, gScreen);
	
	char scoreString[100];
	sprintf(scoreString, "Score: %d", gamedata.player->score);
	drawstring(400, 20, scoreString, gScreen);
	
	if(enteringCheat) {
		char cheatString[100];
		sprintf(cheatString, "CHEAT: %s", cheatCode);
		drawstring(20, 450, cheatString, gScreen);
	}
	
	drawstring(20, 40, errorMessage, gScreen);
		
#ifdef SHOWFRAMERATE
	displayFPSCounter();
#endif // SHOWFRAMERATE

#ifdef ALLOW_ACTIONCAPTURE
	renderActionCapture(spriterelx, spriterely);
#endif // ALLOW_ACTIONCAPTURE
	
	// Unlock screen if needed
	if (SDL_MUSTLOCK(gScreen)) 
		SDL_UnlockSurface(gScreen);
	
	// Tell SDL to update the whole screen
	//SDL_Flip(gScreen);
	SDL_UpdateRect(gScreen, 0, 0, SCR_WIDTH, SCR_HEIGHT);
	
}


char cheatCode[MAX_STRING_LENGTH];
bool enteringCheat = false;
void displayEngine()
{
	
	initEngine();
	
	Uint32 cheatTick = 0;
	bool leftpressed = false;
	bool rightpressed = false;
	
	while (gameRunning && gamedata.player->lives > 0)
	{
		Uint32 ticks = SDL_GetTicks();
		// Render stuff
		
		// Get Joystick moves
		Uint32 joymove = getJoystickMoves();
		if((joymove & JOYSTICK_JUMP) && !isJumping && spritevy < SPRITE_FALLSILENTTHRESHOLD) {
			isJumping = true;
			spritevy = SPRITE_JUMPSPEED;
		}		
		
		// Calculate reftright offset
		if((leftpressed && !rightpressed) || (joymove & JOYSTICK_LEFT)){
			leftright = -1;
		} else if((!leftpressed && rightpressed) || (joymove & JOYSTICK_RIGHT)){
			leftright = 1;
		} else {
			leftright = 0;
		}
		
		if(joymove & JOYSTICK_ACTION) {
			doFGPlayerAction((Uint32)spritex, (Uint32)spritey);
		}
		
		renderEngine();
		if(ticks > cheatTick) {
			cheatTick = SDL_GetTicks() + CHEATTIMEOUT;
			cheatCode[0] = 0;
			enteringCheat = false;
		}
		
		// Poll for events, and handle the ones we care about.
		SDL_Event event;
		while (SDL_PollEvent(&event) && gameRunning) 
		{
			switch (event.type) 
			{
				case SDL_KEYDOWN:
					if (event.key.keysym.sym == SDLK_LEFT) {
						//leftright -= 1;
						leftpressed = true;
					} else if (event.key.keysym.sym == SDLK_RIGHT) {
						//leftright += 1;
						rightpressed = true;
					} else if (event.key.keysym.sym == SDLK_UP) {
						if(!isJumping && spritevy < SPRITE_FALLSILENTTHRESHOLD) {
							isJumping = true;
							spritevy = SPRITE_JUMPSPEED;
						}
					}
					
					
					break;
				case SDL_KEYUP:
					// If escape is pressed, return (and thus, quit)
					if (event.key.keysym.sym == SDLK_LEFT) {
						//leftright += 1;
						leftpressed = false;
					} else if (event.key.keysym.sym == SDLK_RIGHT) {
						rightpressed = false;
						//leftright -= 1;
#ifdef ENABLE_FULLSCREEN
					} else if ((event.key.keysym.sym == SDLK_f) && (enteringCheat == false)) {
								/* Switching between fullscreen and 
								 * windowing not allowed, if we are entering a cheatcode now 
								 * (otherwise a cheatcode may not contain the character 'f' */
						isFullscreen = !isFullscreen;
						if(isFullscreen) {
							gScreen = SDL_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, 32, SDL_SWSURFACE | SDL_FULLSCREEN);
						} else {
							gScreen = SDL_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, 32, SDL_SWSURFACE);
						}
#endif // ENABLE_FULLSCREEN
#ifdef ALLOW_ACTIONCAPTURE
					} else if (event.key.keysym.sym == SDLK_y) {
						startActionCapture();
					} else if (event.key.keysym.sym == SDLK_x) {
						stopActionCapture();
#endif // ALLOW_ACTIONCAPTURE
					} else if (event.key.keysym.sym == SDLK_SPACE) {
						doFGPlayerAction((Uint32)spritex, (Uint32)spritey);
#ifdef ALLOW_CHEATCODES
						
					} else if (event.key.keysym.sym == SDLK_TAB) {
						enteringCheat = true;
					} else if (event.key.keysym.sym == SDLK_RETURN) {
						if(strcmp(cheatCode, "nuke") == 0) {
							printf("Nuking all monsters and adding them to your score...\n");
							MONSTERS* monster = lhandle.monsters;
							while(monster) {
								if(monster->isAlive) {
									gamedata.player->score += monster->score;
									monster->isAlive = false;
									monster->isDying = true;
								}
								monster = monster->next;
							}
						} else if(strcmp(cheatCode, "skip") == 0) {
							gameRunning = false;
						} else if(strcmp(cheatCode, "lives") == 0) {
							gamedata.player->lives = 99;
						} else if(strcmp(cheatCode, "quit") == 0) {
							printf("Trying fast exit....\n");
							exit(0);
						}
						cheatCode[0] = 0;
						enteringCheat = false;
					} else if(enteringCheat) {
						// Add key to cheatcode-string
						char tmp[2];
						if (event.key.keysym.sym >= 33 && strlen(cheatCode) < 30) { /* our font does not contain the first 33 chars */
						  /* (probably better than a hardcoded value: isprintable() or something similar... */
							sprintf(tmp, "%c", event.key.keysym.sym);
							strcat(cheatCode, tmp);
						} ;
						if ((event.key.keysym.sym == SDLK_BACKSPACE)  && (strlen(cheatCode)>0)) { /* Backspace - delete last char */
						  cheatCode[strlen(cheatCode)-1] = '\0' ;
						} ;
						cheatTick = SDL_GetTicks() + CHEATTIMEOUT;
#endif // ALLOW_CHEATCODES
						// BOSS-Key
#ifdef ALLOW_BOSSKEY
					} else if (event.key.keysym.sym == SDLK_ESCAPE) {
						exit(0);
#endif // ALLOW_BOSSKEY
					}
						
					
					break;
				case SDL_QUIT:
					exit(0);
			}
		}
	}
	
	deInitEngine();
}

