/********************************************************************************
						Ultra 64 MARIO Brothers

							  game module

			Copyright 1996 Nintendo co., ltd.  All rights reserved

						   June 24, 1996
 ********************************************************************************/

#include "headers.h"
#include "include/ending.h"


#define GM_NORMAL		0
#define	GM_GAMEOVER		1
#define	GM_PAUSE		2
#define	GM_FREEZE		3
#define	GM_EXIT			4
#define	GM_STILL		5

#define	CHANGE_STAGE	1
#define	CHANGE_SCENE	2
#define	CHANGE_PORT		3

#define	ANGLE180	0x8000

MeterRecord	 playerMeter;					/* player meter record				*/
PlayerRecord playerWorks[1];				/* player control work				*/
PlayerPtr	 marioWorks = &playerWorks[0];	/* pointer to the mario works		*/

static short		gameProcess;			/* game process number				*/
static short		gameCounter;			/* allpurpose counter				*/
static short		freezeCounter;			/* game freezing counter			*/
static FreezeProc	freezeProcedure;		/* pointer to the freezeing proce	*/


static EntrantRecord playerEntry;			/* player entry work				*/
static short		 gameExitCode;			/* game routine exit code			*/

static short		fadeoutMode;			/* scene fade mode					*/
static short		fadeoutCount;			/* scene fade counter				*/
static short		fadeoutPort;			/* scene fade trip port number		*/ 
static ulong		fadeoutInfo;			/* scene fade aux information		*/ 


static short		currentCourse = 0;
static short		messageNo;
static char			stopWatchSw;
static char			middlePointSw = 0;

char mesgCastle;

static void ExecuteGame(short *counter);





/********************************************************************************/
/*																				*/
/*	Control stop watch.															*/
/*																				*/
/********************************************************************************/
extern ushort
GmStopWatch(int mode)
{
	switch (mode) {
		case 0:	setflag(playerMeter.flags, METER_TIMER);
				stopWatchSw		  = FALSE;
				playerMeter.timer = 0;
				break;

		case 1: stopWatchSw = TRUE;
				break;

		case 2: stopWatchSw = FALSE;
				break;

		case 3: clrflag(playerMeter.flags, METER_TIMER);
				stopWatchSw		  = FALSE;
				playerMeter.timer = 0;
				break;
	}
	return(playerMeter.timer);
}






/********************************************************************************/
/*	Check pause request.														*/
/********************************************************************************/
static int
CheckPauseRequest(void)
{
	int DispMesg = (GetMessageNo() >= 0);
	int demoMode = ((marioWorks->status & PS_TYPE_DEMO) != 0);

	if (!(demoMode || DispMesg || wipeControl.active || fadeoutMode)) {
		if (cont1p->trigger & CONT_START)  return(TRUE);
	}
	return(FALSE);
}
/********************************************************************************/
/*	Change game process.														*/
/********************************************************************************/
static void
ChangeProcess(short proc)
{
	gameProcess = proc;
	gameCounter = 0;
}
/********************************************************************************/
/*	Change game process.														*/
/********************************************************************************/
static void
ChangeExitMode(int code)
{
	gameProcess  = GM_EXIT;
	gameCounter  = 0;
	gameExitCode = code;
}
/********************************************************************************/
/*	Exit game mode.																*/
/********************************************************************************/
extern void
PL_ExitGameMode(int code, int color)
{
	if (color != 0)  color = 255;

	AudStopMusic(AudFadeOutTime(16+8));
	SnStartFader(WIPE_FADE_OUT, 16, color, color, color);
//	FreezeGame(15, NULL);
	FreezeGame(30, NULL);
	ChangeExitMode(code);
}
/********************************************************************************/
/*	Check player's reset request.												*/
/********************************************************************************/
static void
CheckReset(void)
{
#if DEVELOP
	if (!wipeControl.active && (cont1p->status == (CONT_BACK | CONT_XL | CONT_XR | CONT_START)) && (cont1p->trigger & CONT_START)) {
		MessageEnforcedDestroy();
		if (sysDebugFlag	)  PL_ExitGameMode(PL_RETURN_SELECT, 1);
						 else  PL_ExitGameMode(PL_RETURN_TITLE , 0);
	}
#endif
}
/********************************************************************************/
/*																				*/
/*	Display message.															*/
/*																				*/
/********************************************************************************/
extern void
PL_DisplayMessage(int mesgno)
{
	int condition;
	int message = snSceneInfo->message[mesgno];

	switch (message) {
		case 129: condition = BuGetItemFlag() & BU_ITEM_BLUESW;						break;
		case 130: condition = BuGetItemFlag() & BU_ITEM_GREENSW;					break;
		case 131: condition = BuGetItemFlag() & BU_ITEM_REDSW;						break;
		case 255: condition = 1;													break;
		 default: condition = BuGetStarFlag(activePlayerNo-1, activeCourseNo-1);	break;
	}
	if (condition == 0) {
		FreezeGame(-1,NULL);
		CallMessage(message);
	}
}
















/*===============================================================================
 *
 *
 *		scene chnage rotuines
 *
 */

/********************************************************************************/
/*	Move player to front of door.												*/
/********************************************************************************/
static void
MoveToFrontDoor(ActorPtr actor, ulong auxinfo)
{
	if (auxinfo & 2)  actor->angle[_AY] += ANGLE180;

	actor->position[_AX] += 300.0f * sin(actor->angle[_AY]);
	actor->position[_AZ] += 300.0f * cos(actor->angle[_AY]);
}
/********************************************************************************/
/*	Change to special cap.														*/
/********************************************************************************/
static void
ChangeSpecialCap(PlayerPtr player)
{
	int index = activeCourseNo - 20;

	switch (index) {
		case 0: setflag(player->flags, PL_FLAG_HEADCAP|PL_FLAG_METALCAP);  player->special =  600;		break;
		case 1: setflag(player->flags, PL_FLAG_HEADCAP|PL_FLAG_WINGCAP );  player->special = 1200;		break;
		case 2: setflag(player->flags, PL_FLAG_HEADCAP|PL_FLAG_GHOSTCAP);  player->special =  600;		break;
	}
}
/********************************************************************************/
/*	Changle port.																*/
/********************************************************************************/
static void
ChangeStatus(PlayerPtr player, int type, ulong auxinfo)
{
	switch (type) {
		case SN_TRIP_DOOR		: PL_ChangePlayerStatus(player, PS_TRIPDOOR  , auxinfo);		break;
		case SN_TRIP_STAR		: PL_ChangePlayerStatus(player, PS_WAITING   ,		 0);		break; 
		case SN_TRIP_CHIMNEY	: PL_ChangePlayerStatus(player, PS_CHIMNEY   ,		 0);		break;
		case SN_WARP_POINT		: PL_ChangePlayerStatus(player, PS_WARPOUT   ,		 0);		break;
		case SN_ENTER_WAITING	: PL_ChangePlayerStatus(player, PS_WAITING   ,		 0);		break;
		case SN_ENTER_LANDING	: PL_ChangePlayerStatus(player, PS_LANDENTER ,		 0);		break;
		case SN_ENTER_FALLING	: PL_ChangePlayerStatus(player, PS_JBACKDOWN ,		 0);		break;
		case SN_ENTER_ROLLING	: PL_ChangePlayerStatus(player, PS_ROLLJUMP  ,		 0);		break;
		case SN_ENTER_DOWNING	: PL_ChangePlayerStatus(player, PS_DOWNLOSER ,		 0);		break;
		case SN_ENTER_PICTURE	: PL_ChangePlayerStatus(player, PS_ROLLJUMP  ,		 0);		break;
		case SN_ENTER_FLIGHT	: PL_ChangePlayerStatus(player, PS_FLIGHT    ,		 2);		break;
		case SN_ENTER_SWIMMING	: PL_ChangePlayerStatus(player, PS_SWIMNWAIT ,		 1);		break;
		case SN_ENTER_WINNER	: PL_ChangePlayerStatus(player, PS_ENTWINNER ,		 0);		break;
		case SN_ENTER_LOSER		: PL_ChangePlayerStatus(player, PS_ENTLOSER  ,		 0);		break;
		case SN_ENTER_LANDWINNER: PL_ChangePlayerStatus(player, PS_LNDWINNER ,		 0);		break;
		case SN_ENTER_LANDLOSER	: PL_ChangePlayerStatus(player, PS_LANDLOSER ,		 0);		break;
		case SN_ENTER_PUSHOUT	: PL_ChangePlayerStatus(player, PS_PUSHOUT   ,		 0);		break;
		case SN_ENTER_PUSHDOWN	: PL_ChangePlayerStatus(player, PS_PUSHDOWN  ,		 0);		break;
	}
	ChangeSpecialCap(player);
}
/********************************************************************************/
/*	Changle port.																*/
/********************************************************************************/
static void
ChangePort(void)
{
	PortPtr port = SnGetPortPtr(playerEntry.port);
	int		type = SnGetPortType(port->stratp);


	if (marioWorks->status != PS_INACTIVE) {

		starringActor[0].position[_AX] = (short)port->stratp->s[stw_worldX].f;
		starringActor[0].position[_AY] = (short)port->stratp->s[stw_worldY].f;
		starringActor[0].position[_AZ] = (short)port->stratp->s[stw_worldZ].f;

		starringActor[0].angle[_AX] = 0;
		starringActor[0].angle[_AY] = port->stratp->s[stw_angleY].d;
		starringActor[0].angle[_AZ] = 0;

		if (type == SN_TRIP_DOOR)  MoveToFrontDoor(&starringActor[0], playerEntry.auxinfo);

		if (playerEntry.type == CHANGE_STAGE || playerEntry.type == CHANGE_SCENE) {
			starringActor[0].sceneNo = playerEntry.scene;
			SnEnterPlayer();
		}
		InitPlayer();
		ChangeStatus(marioWorks, type, playerEntry.auxinfo);
		marioWorks->collide = port->stratp;
		marioWorks->couple  = port->stratp;
	}
	InitGameCamera(snSceneInfo->camera);
	playerEntry.type = 0;
	fadeoutMode		 = 0;

	switch (type) {
		case SN_TRIP_CHIMNEY   : SnStartFader(WIPE_STAR_WIN_OPEN  , 16,  0,  0,  0);	break;
		case SN_TRIP_DOOR	   : SnStartFader(WIPE_CIRCLE_WIN_OPEN, 16,  0,  0,  0);	break;
		case SN_WARP_POINT	   : SnStartFader(WIPE_FADE_IN		  , 20,255,255,255);	break;
		case SN_ENTER_PICTURE  : SnStartFader(WIPE_FADE_IN		  , 26,255,255,255);	break;
		case SN_ENTER_ROLLING  : SnStartFader(WIPE_CIRCLE_WIN_OPEN, 16,  0,  0,  0);	break;
		case SN_ENTER_STAFFROLL: SnStartFader(WIPE_FADE_IN		  , 16,  0,  0,  0);	break;
		default				   : SnStartFader(WIPE_STAR_WIN_OPEN  , 16,  0,  0,  0);	break;
	}
	if (autoDemoPtr == NULL) {
		AudPlayMusic(snSceneInfo->audmode, snSceneInfo->musicno, 0);
		if (marioWorks->flags &  PL_FLAG_METALCAP				  )  AudPlaySpecialMusic(NA_METAL_BGM );
		if (marioWorks->flags & (PL_FLAG_GHOSTCAP|PL_FLAG_WINGCAP))  AudPlaySpecialMusic(NA_MUTEKI_BGM);

		if (activeStageNo == 9 && AudGetPlayingMusic() != NA_RACE_BGM && stopWatchSw) {
			Na_MusicStart(NA_MUS_HANDLE, NA_RACE_BGM, 0);
		}
		if (playerEntry.stage == 6 && playerEntry.scene == 1 && (playerEntry.port == 31 || playerEntry.port == 32)) {
			Na_FixSeFlagEntry(NA_SYS_RETURN_CASTLE);
		}
		if (playerEntry.stage == 16 && playerEntry.scene == 1) {
			if (playerEntry.port == 7 || playerEntry.port == 10 || playerEntry.port == 20 || playerEntry.port == 30) {
				Na_FixSeFlagEntry(NA_SYS_RETURN_CASTLE);
			}
		}
	}
}
/********************************************************************************/
/*	Changle scene.																*/
/********************************************************************************/
static void
ChangeScene(void)
{
	if (playerEntry.type != 0) {
		if (playerEntry.type == CHANGE_SCENE) {
			GmHideTimer();
			SnExitPlayer();
			SnOpenScene(playerEntry.scene);
		}
		ChangePort();
	}
}
/********************************************************************************/
/*	Changle stage.																*/
/********************************************************************************/
static void
ChangeStage(void)
{
	activeStageNo = playerEntry.stage;
	GmHideTimer();
	SnOpenScene(playerEntry.scene);
	ChangePort();
}
/********************************************************************************/
/*	Changle ending stage.														*/
/********************************************************************************/
static void
ChangeEndingStage(void)
{
	ulong status;


	switch (playerEntry.port) {
		case PORT_START_ENDING: status = PS_ENTENDING;	break;
		case PORT_STAFF_ROLL  : status = PS_STAFFROLL;	break;
		case PORT_FIN_ENDING  : status = PS_FINGAME;	break;
	}
	activeStageNo = playerEntry.stage;
	SnOpenScene(playerEntry.scene);

	SetSVector(starringActor[0].position, snEndingScene->posx, snEndingScene->posy, snEndingScene->posz);
	SetSVector(starringActor[0].angle, 0, snEndingScene->angle << 8, 0);

	starringActor[0].sceneNo = playerEntry.scene;
	SnEnterPlayer();
	InitPlayer();
	PL_ChangePlayerStatus(marioWorks, status, 0);
	InitGameCamera(snSceneInfo->camera);
	playerEntry.type = 0;
	fadeoutMode		 = 0;

	SnStartFader(WIPE_FADE_IN, 20, 0, 0, 0);
	if (snEndingScene == NULL || snEndingScene == endingSceneList)  AudPlayMusic(snSceneInfo->audmode, snSceneInfo->musicno, 0);
}
/********************************************************************************/
/*	Do scene connecttion.														*/
/********************************************************************************/
static void
CheckSceneConnection(void)
{
	short		panning;
	BGCheckData	*plane;

	if (activeStageNo == 6 && BuGetTotalStars(activePlayerNo-1) >= 70)  return;

	if((plane = marioWorks->grPlane) != NULL) {
		int number = plane->BGattr - 27;

		if (0 <= number && number <= 3 && snSceneInfo->connect != NULL) {
			ConnectPtr connect = &snSceneInfo->connect[number];

			if (connect->flags) {
				marioWorks->position[_AX]			 += (float)connect->offset[_AX];
				marioWorks->position[_AY]			 += (float)connect->offset[_AY];
				marioWorks->position[_AZ]			 += (float)connect->offset[_AZ];
				marioWorks->strategy->s[stw_worldX].f = marioWorks->position[_AX];
				marioWorks->strategy->s[stw_worldY].f = marioWorks->position[_AY];
				marioWorks->strategy->s[stw_worldZ].f = marioWorks->position[_AZ];

				panning = marioWorks->sceneinf->camera->panning;
				SnChangeScene(connect->scene);
				marioWorks->sceneinf = snSceneInfo;
				Crou_camBlockofs((float)connect->offset[_AX], (float)connect->offset[_AY], (float)connect->offset[_AZ]);
				marioWorks->sceneinf->camera->panning = panning;	/* for BUG */
			}
		}
	}
}
















/*===============================================================================
 *
 *
 *		Stage exit rotuines
 *
 */

/********************************************************************************/
/*	Set player entrant record.													*/
/********************************************************************************/
static int
CheckSameMusic(short portno)
{
	PortPtr port   = SnGetPortPtr(portno);
	short   stage  = port->stage & 0x7f;
	short	scene  = port->scene;
	short	result = TRUE;

	if (stage == 9 && stage == activeStageNo && scene == activeSceneNo) {
		short music = AudGetPlayingMusic();

		if (music == NA_NOKO_SLIP_BGM || music == NA_MUTEKI_BGM)  result = FALSE;
	}
	else {
		ushort	mode   = stageScenes[scene].audmode;
		ushort	music  = stageScenes[scene].musicno;

		result = (stage == activeStageNo && mode == snSceneInfo->audmode && music == snSceneInfo->musicno);
		if (AudGetPlayingMusic() != music)  result = FALSE;
	}
	return(result);
}
/********************************************************************************/
/*	Set player entrant record.													*/
/********************************************************************************/
static void
SetPlayerEntrant(short stage, short scene, short port, ulong auxinfo)
{
	if		(port  >= PORT_START_ENDING	  )  playerEntry.type = CHANGE_STAGE;
	else if	(stage != activeStageNo		  )  playerEntry.type = CHANGE_STAGE;
	else if (scene != snSceneInfo->sceneNo)  playerEntry.type = CHANGE_SCENE;
	else									 playerEntry.type = CHANGE_PORT;

	playerEntry.stage	= stage;
	playerEntry.scene	= scene;
	playerEntry.port	= port;
	playerEntry.auxinfo = auxinfo;
}
/********************************************************************************/
/*	Check BG change scene.														*/
/********************************************************************************/
static BGPortPtr
GetBGPortPointer(void)
{
	BGPortPtr portpt = NULL;
	int		  portno = marioWorks->grPlane->BGattr - 211;


	if (0 <= portno && portno <= 44) {
		if ((portno < 42) || ((marioWorks->position[_AY] - marioWorks->grLevel) < 80.0f)) {
			portpt = &snSceneInfo->bgport[portno];
		}
	}
	return(portpt);
}
/********************************************************************************/
/*	Check BG change scene.														*/
/********************************************************************************/
static void
CheckBGPort(void)
{
	BGPortRecord bgport, *portptr;

	if (snSceneInfo->bgport != NULL && marioWorks->grPlane != NULL) {
		if ((portptr = GetBGPortPointer()) != NULL) {
			if (marioWorks->status & PS_TYPE_DEMO) {
				AudPictWaveSound();
			}
			else if (portptr->flags) {
				bgport = *portptr;
				if (!(bgport.stage & 0x80))  middlePointSw = BuGetMidPoint(&bgport);
				SetPlayerEntrant(bgport.stage & 0x7f, bgport.scene, bgport.port, 0);
				BuSetMidPoint(&bgport);
				SnDelayFader(WIPE_FADE_OUT, 30,  255,255,255, 45);
				FreezeGame(74, ExecuteGame);
				PL_ChangePlayerStatus(marioWorks, PS_FREEZE, 0);
				MapDisable(&marioWorks->strategy->map);
				Na_FixSeFlagEntry(NA_SE2_SET);
				AudStopMusic(AudFadeOutTime(50));
				SendMotorEvent(80,70);		/* MOTOR 1997.5.27 */
				SendMotorDecay(1);			/* MOTOR 1997.5.30 */
			}
		}
	}
}
/********************************************************************************/
/*																				*/
/*	Exit player with screen fade out.											*/
/*																				*/
/********************************************************************************/
extern int
PL_StartFadeout(PlayerPtr player, int mode)
{
	int audioFadeout = TRUE;

	if (fadeoutMode == 0) {
		player->invincible = -1;		/* player invincible */
		fadeoutInfo		   =  0;
		fadeoutMode		   = mode;

		switch (mode) {
			case PL_FADE_RESTART:
			case PL_FADE_GOTITLE:	fadeoutCount  = 20;
									fadeoutPort   = PORT_GAME_CLEAR;
									ramSaveCourse = 0;
									audioFadeout  = FALSE;
									SnStartFader(WIPE_STAR_WIN_CLOSE, 20, 0, 0, 0);
									break;

			case PL_FADE_GAMEEND:	fadeoutCount  = 60;
									fadeoutPort   = PORT_GAME_CLEAR;
									audioFadeout  = FALSE;
									ramSaveCourse = 0;
									SnStartFader(WIPE_FADE_OUT, 60, 0, 0, 0);
									break;

			case PL_FADE_WINNING:	fadeoutCount  = 32;
									fadeoutPort   = PORT_GAME_CLEAR;
									ramSaveCourse = 0;
									SnStartFader(WIPE_MARIO_WIN_CLOSE, 32, 0, 0, 0);
									break;

			case PL_FADE_LOSING :	if (player->nlifes == 0)  fadeoutMode = PL_FADE_GAMEOVR;
									fadeoutCount = 48;
									fadeoutPort  = PORT_GAME_OVER;
									SnStartFader(WIPE_KOPA_WIN_CLOSE, 48, 0, 0, 0);
									Na_FixSeFlagEntry(NA_SYS_KUPPA_LAUGH);
									break;

			case PL_FADE_FALLING:	fadeoutPort = PORT_COURSE_OUT;
									if (SnGetPortPtr(fadeoutPort) == NULL) {
										if (player->nlifes == 0)  fadeoutMode = PL_FADE_GAMEOVR;
															else  fadeoutPort = PORT_GAME_OVER;
									}
									fadeoutCount = 20;
									SnStartFader(WIPE_CIRCLE_WIN_CLOSE, 20, 0, 0, 0);
									break;

			case PL_FADE_VIEWROOF:	fadeoutCount = 30;
									fadeoutPort  = PORT_VIEW_ROOF;
									SnStartFader(WIPE_FADE_OUT, 30, 255, 255, 255);
									Na_FixSeFlagEntry(NA_SE2_SET);
									break;

			case PL_FADE_TRIPKAGO:	fadeoutCount = 30;
									fadeoutPort  = SnGetPortNumber(player->couple);
									SnStartFader(WIPE_FADE_OUT, 30, 255, 255, 255);
									break;

			case PL_FADE_WARPIN:	fadeoutCount = 20;
									fadeoutPort  = SnGetPortNumber(player->couple);
									audioFadeout = !CheckSameMusic(fadeoutPort);
									SnStartFader(WIPE_FADE_OUT, 20, 255, 255, 255);
									break;

			case PL_FADE_TRIPDOOR:	fadeoutCount = 20;
									fadeoutInfo  = player->auxinfo;
									fadeoutPort  = SnGetPortNumber(player->couple);
									audioFadeout = !CheckSameMusic(fadeoutPort);
									SnStartFader(WIPE_CIRCLE_WIN_CLOSE, 20, 0, 0, 0);
									break;

			case PL_FADE_TRIPTUBE:	fadeoutCount = 20;
									fadeoutPort  = SnGetPortNumber(player->couple);
									audioFadeout = !CheckSameMusic(fadeoutPort);
									SnStartFader(WIPE_STAR_WIN_CLOSE, 20, 0, 0, 0);
									break;

			case PL_FADE_ENDING:	fadeoutCount = 30;
									SnStartFader(WIPE_FADE_OUT, 30, 0, 0, 0);
									break;

			case PL_FADE_STAFFROLL:	if (snEndingScene == endingSceneList) {
										fadeoutCount = 60;
										SnStartFader(WIPE_FADE_OUT, 60,0,0,0);
									} else {
										fadeoutCount = 20;
//										SnStartFader(WIPE_FADE_OUT, 20, 0, 64, 56);
										SnStartFader(WIPE_FADE_OUT, 20, 0,  0,  0);
									}
									audioFadeout = FALSE;
									break;
		}
		if (audioFadeout && autoDemoPtr == NULL) {
			AudStopMusic(AudFadeOutTime(fadeoutCount*3/2));
		}
	}
	return(fadeoutCount);
}
/********************************************************************************/
/*	Check game fade out.														*/
/********************************************************************************/
static void
CheckGameFadeout(void)
{
	PortPtr port;
	int		portno;

	if (fadeoutMode != 0) {
		if (--fadeoutCount == 0) {
			MessageEnforcedDestroy();

			if (sysDebugFlag && (fadeoutMode & 0x10)) {
				ChangeExitMode(PL_RETURN_SELECT);
			}
			else if (autoDemoPtr != NULL) {
				if (fadeoutMode == PL_FADE_RESTART)  ChangeExitMode(PL_RETURN_LOGO );
											   else  ChangeExitMode(PL_RETURN_TITLE);
			}
			else {
				switch (fadeoutMode) {
					case PL_FADE_GAMEOVR:
							BuRestoreBackUp();
							ChangeExitMode(PL_GAME_OVER);
							break;

					case PL_FADE_GAMEEND:
							ChangeExitMode(PL_GAME_CLEAR);
							Na_Enddemo_UnlockSe();
							break;

					case PL_FADE_GOTITLE:
							ChangeExitMode(PL_RETURN_TITLE);
							break;

					case PL_FADE_ENDING:
							snEndingScene = endingSceneList;
							SetPlayerEntrant(snEndingScene->stage, snEndingScene->scene, PORT_START_ENDING, 0);
							break;

					case PL_FADE_STAFFROLL:
							Na_Ending_LockSe();
							snEndingScene++;
							activeLevelNo = snEndingScene->info & 0x07;
							portno = (snEndingScene[1].stage == 0) ? PORT_FIN_ENDING : PORT_STAFF_ROLL;
							SetPlayerEntrant(snEndingScene->stage, snEndingScene->scene, portno, 0);
							break;

					default:
							port = SnGetPortPtr(fadeoutPort);
							SetPlayerEntrant(port->stage & 0x7f, port->scene, port->port, fadeoutInfo);
							BuSetMidPoint((BGPortPtr)port);

							if (playerEntry.type != CHANGE_STAGE) {
								FreezeGame(2, NULL);
							}
							break;
				}
			}
		}
	}
}













/*===============================================================================
 *
 *
 *		Game rotuines
 *
 */

/********************************************************************************/
/*	Set player meter.															*/
/********************************************************************************/
static void
SetPlayerMeter(void)
{
	if (snEndingScene == NULL) {
		short power = (marioWorks->npowers > 0) ? marioWorks->npowers >> 8 : 0;

		if (1 <= activeCourseNo)  setflag(playerMeter.flags, METER_COIN);
							else  clrflag(playerMeter.flags, METER_COIN);

		if (playerMeter.coin < marioWorks->ncoins) {
			if (frameCounter & 1) {
				ulong sound = (marioWorks->status & (PS_TYPE_SWIM|PS_TYPE_SINK)) ? NA_SE2_COIN+NA_IN_WATER : NA_SE2_COIN+NA_IN_OUTDOOR;

				playerMeter.coin += 1;
				AudStartSound(marioWorks->strategy, sound);
			}
		}
		if (marioWorks->nlifes > 100)  marioWorks->nlifes = 100;	/* for sefty	*/
		if (marioWorks->ncoins > 999)  marioWorks->ncoins = 999;	/* for sefty	*/	/* JPBUG	*/
		if (playerMeter.coin   > 999)  playerMeter.coin   = 999;	/* for sefty	*/	/* JPBUG	*/

		playerMeter.star  = marioWorks->nstars;
		playerMeter.life  = marioWorks->nlifes;
		playerMeter.key   = marioWorks->nkeys;

		if (power > playerMeter.power)  Na_FixSeFlagEntry(NA_SYS_RECOVER);
		playerMeter.power = power;

		if (marioWorks->damage  > 0)  setflag(playerMeter.flags, METER_DAMAGE);
								else  clrflag(playerMeter.flags, METER_DAMAGE);
	}
}
/********************************************************************************/
/*	execute game for freeze process.											*/
/********************************************************************************/
static void
ExecuteGame(short *counter)
{
	SnExecuteStrategy();
	SetPlayerMeter();
	if (snSceneInfo != NULL)  CtrlGameCamera(snSceneInfo->camera);
}
/********************************************************************************/
/*	Game normal process.														*/
/********************************************************************************/
static int
NormalProcess(void)
{
	if (autoDemoPtr != NULL) {
		SnDisplayDemoMessage();

		if (cont1p->trigger & CONT_EXIT) {
			PL_StartFadeout(marioWorks, (activeStageNo == 27) ? PL_FADE_RESTART : PL_FADE_GOTITLE);
		}
		else if (!wipeControl.active && !fadeoutMode && (cont1p->trigger & CONT_START)) {
			PL_StartFadeout(marioWorks, PL_FADE_GOTITLE);
		}		
	}
	ChangeScene();
	CheckSceneConnection();

	if (stopWatchSw && playerMeter.timer < 10*60*30-1)  playerMeter.timer += 1;

	SnExecuteStrategy();
	SetPlayerMeter();
	if (snSceneInfo != NULL)  CtrlGameCamera(snSceneInfo->camera);

	CheckBGPort();
	CheckGameFadeout();

	if (gameProcess == GM_NORMAL) {
		if (playerEntry.type == CHANGE_STAGE) {
			ChangeProcess(GM_EXIT);
		}
		else if (freezeCounter != 0) {
			ChangeProcess(GM_FREEZE);
		}
		else if (CheckPauseRequest()) {
			AudStartMute(AUD_MUTE_PAUSE);
			ResetMotorPack();
			setflag(gameCameraControl, CAM_FLAG_PAUSE);
			ChangeProcess(GM_PAUSE );
		}
	}
	return(0);
}








/*===============================================================================
 *
 *
 *		Game pause rotuines
 *
 */
/********************************************************************************/
/*	Game pause process.															*/
/********************************************************************************/
static int
PauseProcess(void)
{
#if DEVELOP
	if ((cont1p->trigger & CONT_START) && (cont1p->status & CONT_XB))  ChangeProcess(GM_STILL);
#endif

	if (mesgEvent == 0) {
		CallSelectMessage(1);
	}
	else if (mesgEvent == 1) {
		AudEndMute(AUD_MUTE_PAUSE);
		clrflag(gameCameraControl, CAM_FLAG_PAUSE);
		ChangeProcess(GM_NORMAL);
	}
	else {
		if (sysDebugFlag) {
			PL_ExitGameMode(PL_RETURN_SELECT, 1);
		} else {
			SetPlayerEntrant(6, 1, 31, 0);
			PL_ExitGameMode(0, 0);
			ramSaveCourse = 0;
		}
		clrflag(gameCameraControl, CAM_FLAG_PAUSE);
	}
	return(0);
}
/********************************************************************************/
/*	Game still process.															*/
/********************************************************************************/
static int
StillProcess(void)
{
	if (cont1p->trigger & CONT_DOWN) {
		clrflag(gameCameraControl, CAM_FLAG_PAUSE);
		NormalProcess();
	}
	else if (cont1p->trigger & CONT_START) {
		clrflag(gameCameraControl, CAM_FLAG_PAUSE);
		AudEndMute(AUD_MUTE_PAUSE);
		ChangeProcess(GM_NORMAL);
	}
	else {
		setflag(gameCameraControl, CAM_FLAG_PAUSE);
	}
	return(0);
}
























/*===============================================================================
 *
 *
 *		Game freeze rotuines
 *
 */

/********************************************************************************/
/*	Change process mode.														*/
/********************************************************************************/
extern void
FreezeGame(short count, FreezeProc proc)
{
	freezeCounter   = count;
	freezeProcedure = proc;
}
/********************************************************************************/
/*	Game freeze process.														*/
/********************************************************************************/
static int
FreezeProcess(void)
{
	if		(freezeProcedure == (void *)-1)  CtrlGameCamera(snSceneInfo->camera);
	else if (freezeProcedure != 	  NULL)  freezeProcedure(&freezeCounter);

	if (freezeCounter > 0)  freezeCounter -= 1;

	if (freezeCounter == 0) {
		freezeProcedure = NULL;
		ChangeProcess(GM_NORMAL);
	}
	return(0);
}
/********************************************************************************/
/*	Game exit process.															*/
/********************************************************************************/
static int
ExitProcess(void)
{
	if (freezeProcedure != NULL)  freezeProcedure(&freezeCounter);

	if (--freezeCounter == -1) {
		playerMeter.flags = 0;
		freezeCounter     = 0;
		freezeProcedure   = NULL;

		if (playerEntry.type)  return(playerEntry.stage	);
						 else  return(gameExitCode		);
	}
	return(0);
}
/********************************************************************************/
/*	Freeze player process.														*/
/********************************************************************************/
static int
FreezePlayerProcess(void)
{
	if (--freezeCounter == -1) {
		playerMeter.flags = 0;

		if (playerEntry.type)  return(playerEntry.stage	);
						 else  return(gameExitCode		);
	}
	return(0);
}
















/*===============================================================================
 *
 *
 *		Game main rotuines
 *
 */

/********************************************************************************/
/*	Game main process.															*/
/********************************************************************************/
static ulong
PlayGame(void)
{
	int result;

	switch (gameProcess) {
		case GM_NORMAL	: result = NormalProcess();		break;
		case GM_PAUSE	: result = PauseProcess();		break;
		case GM_FREEZE	: result = FreezeProcess();		break;
		case GM_EXIT	: result = ExitProcess();		break;
		case GM_STILL	: result = StillProcess();		break;
	}
#if DEBUGSW
	if (gameProcess != GM_EXIT) CheckReset();
#endif
	if (result) {
		AudResetMute();
		AudUnlockSound();
	}
	return(result);
}
/********************************************************************************/
/*	Game stage initialize process.												*/
/********************************************************************************/
static ulong
InitGame(void)
{
	int whitein = FALSE;

	ChangeProcess(GM_NORMAL);
	fadeoutMode		  = 0;
	freezeCounter	  = 0;
	gameExitCode	  = 0;
	playerMeter.flags = (snEndingScene == NULL) ? METER_ALL - METER_TIMER : 0;
	stopWatchSw		  = 0;

	if (playerEntry.type != 0) {
		if (playerEntry.port >= PORT_START_ENDING)  ChangeEndingStage();
											  else  ChangeStage();
	}
	else {
		if (starringActor[0].sceneNo >= 0) {
			SnEnterPlayer();
			InitPlayer();
		}
		if (snSceneInfo != NULL) {
			InitGameCamera(snSceneInfo->camera);
			if (autoDemoPtr != NULL) {
				PL_ChangePlayerStatus(marioWorks, PS_WAITING, 0);
			}
			else if (!sysDebugFlag && marioWorks->status != PS_INACTIVE) {
				if (BuIsActive(activePlayerNo-1)) {
					PL_ChangePlayerStatus(marioWorks, PS_WAITING, 0);
				} else {
					PL_ChangePlayerStatus(marioWorks, PS_CAMDEMO, 0);
					whitein = TRUE;
				}
			}
		}
		if (whitein)  SnStartFader(WIPE_FADE_IN		 , 90, 255, 255, 255);
				else  SnStartFader(WIPE_STAR_WIN_OPEN, 16, 255, 255, 255);

		if (autoDemoPtr == NULL)  AudPlayMusic(snSceneInfo->audmode, snSceneInfo->musicno, 0);
	}
	if (autoDemoPtr == NULL)  ResetMotorPack();

//	SnExecuteStrategy();
	if (marioWorks->status == PS_CAMDEMO)  Na_Opening_LockSe();
	return(TRUE);
}
/********************************************************************************/
/*	Game main process.															*/
/********************************************************************************/
extern ulong
GameProcess(short code, long param)
{
	ulong result = 0;

	switch (code) {
		case 0: result = InitGame();	break;
		case 1: result = PlayGame();	break;
	}
	return(result);
}
/********************************************************************************/
/*	Game initialize process.													*/
/********************************************************************************/
extern ulong
GameInitialize(short code, long param)
{
	playerEntry.type = 0;
	fadeoutMode		 = 0;
	mesgCastle		 = !BuIsActive(activePlayerNo-1);
	activeStageNo	 = param;
	activeCourseNo	 = 0;
	ramSaveCourse	 = 0;
	snEndingScene	 = NULL;
	buYosshiJump	 = 0;

	InitPlayerWorks();
	BuClearRamStorage();
	BuRestoreHat();
	InitCamera_1st();
	iwa_StratInit();

	return(param);
}
















/********************************************************************************/
/*	Check course start menu.													*/
/********************************************************************************/
extern ulong
CheckCourseMenu(short code, long param)
{
	int middlePoint = middlePointSw;


	middlePointSw  = 0;
	activeStageNo  = param;
	activeCourseNo = BuGetCourseNumber(param);

	if (autoDemoPtr	!= NULL || snEndingScene != NULL || activeCourseNo == 0)  return(FALSE);

	if (activeStageNo != 30 && activeStageNo != 33 && activeStageNo != 34) {
		marioWorks->ncoins = 0;
		playerMeter.coin   = 0;
		BuSetStartingStar();
	}
	if (ramSaveCourse != activeCourseNo) {
		ramSaveCourse = activeCourseNo;
		InitCourse();
		BuClearRamStorage();
	}
	if ((activeCourseNo > 15) || middlePoint)  return(FALSE);
	if (sysDebugFlag && !sysProcessMeter	)  return(FALSE);


#if 0
	if (ramSaveCourse == activeCourseNo) {
		/* Restore RAM save data */
		return(FALSE);
	} else {
		ramSaveCourse = activeCourseNo;
		InitCourse();
		BuClearRamStorage();

		if (activeCourseNo > 15)  return(FALSE);
		if (sysDebugFlag && !sysProcessMeter)  return(FALSE);
	}
#endif
	return(TRUE);
}
/********************************************************************************/
/*	Check course start menu.													*/
/********************************************************************************/
extern ulong
GameTheEnd(short code, long param)
{
	Na_FixSeFlagEntry(NA_SYS_END_MESSAGE);
	return(1);
}
