    /*============================================================================
      NuSYSTEM sample program SNAKE TAIL HACK

      gamemain.c

      Copyright (C) 1997, NINTENDO Co,Ltd.
      ============================================================================*/

    #include <assert.h>
    #include <nusys.h>
    #include <nualsgi_n.h>

    #include "main.h"
    #include "graphic.h"
    #include "math.h"
    #include "font.h"
    #include "para.h"

    #include "./kiyaata1.h"/*Head model data of player 1 */
    #include "./kiyadou1.h"/*Tail model data of player 1*/
    #include "./kiyaata2.h"/*Head model data of player 2 */
    #include "./kiyadou2.h"/*Tail model data of player 2*/
    #include "./kiyaata3.h"/*Head model data of player 3 */
    #include "./kiyadou3.h"/*Tail model data of player 3*/
    #include "./kiyaata4.h"/*Head model data of player 4 */
    #include "./kiyadou4.h"/*Tail model data of player 4*/

    #define START_TIME	100000000LL/* 100 */
    #define TAIL_NUM	0
    #define TIME_UP		1

    /* Function declaration */
    void InitSnake( void );
    void InitShotTail( void );
    void DrawHead( Dynamic* , int );
    void UpdateTailPos( int );
    void RecordHeadPos( int );
    void DrawTail( Dynamic* , int );
    void ChgHeadDir( int, int, int);
    void DrawShotTail( Dynamic* );
    void EntryShotTail( int, int );
    void EraseShotTail( int );
    void ShotTailMove( void );
    void SlideTail( int );
    void ReadMovie( int , Dynamic* );
    void DrawInfo( void );
    int CheckTailNum( int );
    void InitGame( void );

    /* extern declaration */
    /* nusys */
    extern u32 nuScRetraceCounter;    /* The retrace counter */

    /* gameover.c */
    extern int SavePak;
    extern int SavePakEnd;

    /* map.c */
    extern void DrawMap( Dynamic* dynamicp );
    extern void DrawMapSaku( Dynamic* dynamicp );
    extern void DrawMapObj( Dynamic* );
    extern void DrawMovie( Dynamic* dynamicp, unsigned short * );

    /* hitchk.c */
    extern void Snake2Wall( int );
    extern void Snake2Obj( int );
    extern void Snake2Enemy( int );
    extern int ShotTail2Wall( float, float );
    extern int ShotTail2Obj( float, float );
    extern void Snake2ShotTail( int );
    extern void Snake2EnemyTail( int );

    /* main.c */
    extern void SaveContPak( void );
    extern NUContData ContTriger[];
    extern int TraceIdx;
    extern int ReplayMode;
    volatile extern int stage;
    extern int SaveIdx;
    extern int SaveOfsIdx;

    /* view.c */
    extern void SetViewMtx( Dynamic* dynamicp );

    /* spec */
    extern u8 _moviedataSegmentRomStart[];
    extern u8 _moviedataSegmentRomEnd[];

    /* Variable declaration */
    float View;		/* The camera position */
    int MovieNumber;	/* The texture number for the movie */
    SnakePara Snake;	/* Various kinds of parameters of each machine */
    SnakeMove snakemove[4];	/* The buffer for drawing the body of each machine */
    ShotTail mtx_shot[20];	/* Various kinds of parameters of shot tails */

    int Pause;		/* The flag for the pause */
    int MovieUpDate;	/* The counter for updating the movie */

    OSTime	StartTime;	/* Start time of the game */
    OSTime	NowTime;	/* The current time */
    OSTime	NowPauseTime;	/* For keeping time at the pause */
    OSTime	LapTime;	/* Elapsed time */

    int Winner;		/* The winner number */
    int UpTempo;		/* The flag for tempo-up sequence */

    /*--------------------------------------------------------------*/
    /*  makegameDL							*/
    /*  Make the display list and activate the graphic task.	*/
    /*  IN:	None							*/
    /*  RET:None							*/
    /* -------------------------------------------------------------*/
    void
    makegameDL(void)
    {
        int idx;
        Dynamic* dynamicp;

        /* Specify the dynamic buffer */
        dynamicp = &gfx_dynamic[gfx_gtask_no];

        /* Specify the display list buffer */
        glistp = &gfx_glist[gfx_gtask_no][0];

        /* The initialization of RSP and RDP */
        gfxRCPInit();

        /* Clear the frame and Z-buffer */
        gfxClearCfb();

        /* Setting the view-matrix */
        SetViewMtx( dynamicp );

        /* Draw the map */
        DrawMap( dynamicp );
        DrawMapSaku( dynamicp );
        DrawMapObj( dynamicp );

        /* Read the texture for the movie by PI */
        ReadMovie( MovieNumber, dynamicp );

        /* Draw the texture read by MovieBuf (MovieBuf[0]~MovieBuf[3] is unused data) */
        DrawMovie( dynamicp, &(dynamicp->MovieBuf[4]));

        /* Draw the self-machine */
        for(idx = 0; idx < 4; idx++){
    	UpdateTailPos( idx );
    	DrawTail( dynamicp, idx );
    	DrawHead( dynamicp, idx );
        }

        /* Draw the shot tail */
        DrawShotTail( dynamicp );

        /*Display screen information*/
        DrawInfo();

        gDPFullSync(glistp++);
        gSPEndDisplayList(glistp++);

        assert((glistp - gfx_glist[gfx_gtask_no]) < GFX_GLIST_LEN);

        /* Activate the RSP task and switch display buffers*/
        nuGfxTaskStart(&gfx_glist[gfx_gtask_no][0],
    		   (s32)(glistp - gfx_glist[gfx_gtask_no]) * sizeof (Gfx),
    		   NU_GFX_UCODE_F3DEX , NU_SC_SWAPBUFFER);

        /* Specify next graphic task number */
        gfx_gtask_no++;
        gfx_gtask_no %= GFX_GTASK_NUM;
    }

    /*------------------------------*/
    /* updateGame			*/
    /* The progressing process of the game main part	*/
    /*  IN:	None			*/
    /*  RET:None			*/
    /*------------------------------*/
    void
    updateGame(void)
    {
        int padnum;
        int idx;
        int EnemyNum, EnemyTailNum;
        s32 tempo;

        /* PAUSE */
        if(ContTriger[0].trigger & START_BUTTON){
    	nuAuSndPlayerPlay(EFF_PAUSE);
    	nuAuSndPlayerSetPitch(44100.0/32000);
    	Pause ^= 1;
        }

        /* Reset */
        if((ContTriger[0].trigger & L_TRIG) &&
           (ContTriger[0].trigger & R_TRIG)){
    	/* Remove the call-back function */
    	nuGfxFuncRemove();
    	/* Return the title */
    	stage = 0;
        }

        /* Update the timer */
        if( Pause == 0 ){
    	NowTime = osGetTime();
    	NowPauseTime = NowTime - StartTime;
    	LapTime = START_TIME - OS_CYCLES_TO_USEC(NowTime - StartTime);
    	LapTime /= 1000;
        }else{    /* Keep the time */
    	NowTime = osGetTime();
    	StartTime = NowTime - NowPauseTime;
    	LapTime = START_TIME - OS_CYCLES_TO_USEC(NowTime - StartTime);
    	LapTime /= 1000;
        }

        /* The end according to number of tails (when number of tails becomes 20)*/
        if(CheckTailNum(TAIL_NUM)){
    	/* Indicate next stage to main */
    	stage = 2;
    	SavePak = 0;
    	SavePakEnd = 0;
    	/* Remove the call-back function*/
    	nuGfxFuncRemove();
        }

        /* The end according to time-up */
        if(LapTime > NowTime){
    	CheckTailNum(TIME_UP);
    	LapTime = 0;
    	/* Indicate next stage to main */
    	stage = 2;
    	SavePak = 0;
    	SavePakEnd = 0;
    	/* Remove the call-back function*/
    	nuGfxFuncRemove();
        }

        /* Change the camera position */
        if(nuContData[0].button & U_CBUTTONS){
    	if((View += 1.0) > 89.0){
    	    View = 89.0;
    	}
        }
        /* The same as the above */
        if(nuContData[0].button & D_CBUTTONS){
    	if((View -= 1.0) <= -89.0){
    	    View = -89.0;
    	}
        }

        /* If it is not on the pause */
        if(Pause == 0){
    	/* The updating process of each pad */
    	for(padnum = 0; padnum < NU_CONT_MAXCONTROLLERS; padnum++){

    	    /* Go to the next if the controller pad is not connected */
    	    /* Depend on the result of the initialization by nuContInit() of main.c */
    	    if(nuContStatus[padnum].errno != 0){
    		continue;
    	    }

    	    /* Go to the next if it's on fainting */
    	    if(Snake.sleepcnt[padnum] > 0){
    		continue;
    	    }

    	    /* If the head is not piled up on the opponent's tail */
    	    if(Snake.enemytailhit[padnum] == -1 ){
    		/* The button */
    		if(ContTriger[padnum].trigger & A_BUTTON){
    		    /* Do entry if the shooting tail can do entry */
    		    if(Snake.tailcnt[padnum] > 0){
    			nuAuSndPlayerPlay(EFF_SHOT_TAIL);
    			nuAuSndPlayerSetPitch(44100.0/32000);
    			EntryShotTail( padnum, Snake.tail_attr[padnum][1]);
    		    }
    		}
    	    }else{		/* If the head is piled up on the opponent's tail */
    		/* The button */
    		EnemyNum = Snake.enemytailhit[padnum];
    		EnemyTailNum = Snake.enemytailhitnum[padnum];

    		/* Take away the opponent's tail */
    		if(ContTriger[padnum].button & A_BUTTON){
    		    nuAuSndPlayerPlay(EFF_EAT_TAIL);
    		    nuAuSndPlayerSetPitch(44100.0/32000);
    		    for(idx = EnemyTailNum; idx < Snake.tailcnt[EnemyNum]+1; idx++){
    			Snake.tail_attr[padnum][++Snake.tailcnt[padnum]] =
    			  Snake.tail_attr[EnemyNum][idx];
    			Snake.tail_attr[EnemyNum][idx] = -1;
    		    }
    		    /* Shorten the tail that the one who is taken away his tail */
    		    Snake.tailcnt[EnemyNum] = EnemyTailNum - 1;
    		}
    	    }

    	    /* Don't move during each 3D stick of X and Y between -3 to 3 (play) */
    	    if(!((nuContData[padnum].stick_x/10 < 3 &&
    		  nuContData[padnum].stick_x/10 > -3) &&
    		 (nuContData[padnum].stick_y/10 < 3 &&
    		  nuContData[padnum].stick_y/10 > -3 ))){

    		/* Move the head by the 3D stick (directions of up, down, right and left) */
    		if( Zettai(nuContData[padnum].stick_x/5) >
    		   Zettai(nuContData[padnum].stick_y/5) ){
    		    Snake.pos_x[padnum][0] += nuContData[padnum].stick_x/5;
    		    nuContData[padnum].stick_y = 0;
    		    /* Change the head direction corresponding to the direction of the 3D stick */
    		    ChgHeadDir( padnum, nuContData[padnum].stick_x, 0 );
    		}else{
    		    Snake.pos_y[padnum][0] += nuContData[padnum].stick_y/5;
    		    nuContData[padnum].stick_x = 0;
    		    /* Change the head direction corresponding to the direction of the 3D stick */
    		    ChgHeadDir( padnum, 0, nuContData[padnum].stick_y );
    		}
    		Snake.stickon[padnum] = 1;
    	    }else{
    		Snake.stickon[padnum] = 0;
    	    }

    	    Snake2Wall( padnum ); /* Collision detection:  the self-machine vs. map area */
    	    Snake2Obj( padnum ); /* Collision detection:  the self-machine vs. map object */
    	    Snake2Enemy( padnum ); /* Collision detection:  the self-machine vs. enemy */
    	    Snake2EnemyTail( padnum ); /* Collision detection:  the self-machine vs. opponents */

    	    /* Return to the beginning if it hits something */
    	    if(Snake.wallhit[padnum] == 1 ||	/* Hit the wall */
    	       Snake.objhit[padnum] != -1 ||	/* Hit the map object */
    	       Snake.enemyhit[padnum] != -1){	/* Hit the enemy */
    		Snake.pos_x[padnum][0] -= nuContData[padnum].stick_x/5;
    		Snake.pos_y[padnum][0] -= nuContData[padnum].stick_y/5;
    	    }else{
    		if(Snake.stickon[padnum]) /* If control stick has collapsed some degree */
    		  RecordHeadPos( padnum ); /* Record the head locus in the buffer */
    	    }

    	    /* Collision detection between the self-machine and the flown tail */
    	    Snake2ShotTail( padnum );
    	    if(Snake.shottailhit[padnum] != -1){
    		nuAuSndPlayerPlay(EFF_CUR_MOVE);
    		nuAuSndPlayerSetPitch(44100.0/32000);

    		/* Increase one tail connected the self-machine (20 max.)*/
    		if((Snake.tailcnt[padnum]++) >= 20){
    		    Snake.tailcnt[padnum] = 20;
    		}
    		/* Register the hit tail to the own rear side */
    		Snake.tail_attr[padnum][Snake.tailcnt[padnum]] =
    		  mtx_shot[Snake.shottailhit[padnum]].tail_pattern;
    		/* Erase the register of the shot tail */
    		EraseShotTail( Snake.shottailhit[padnum] );
    	    }
    	}
    	ShotTailMove();		/* Move if there are any shot tail */

    	/* Surplus to next texture number at each specifying retrace (0 to 15)*/
    	if((MovieUpDate % MOVIE_UPDATE) == 0) {
    	    if((++MovieNumber) > 15){
    		MovieNumber = 0;
    	    }
    	}
    	MovieUpDate++;
    	/* Tempo up music if the rest of the time becomes less than 30 seconds */
    	if(UpTempo == 0 && (LapTime/1000) < 30){
    	    UpTempo = 1;
    	    tempo = nuAuSeqPlayerGetTempo(0);
    	    tempo /= 13;
    	    tempo *= 10;
    	    nuAuSeqPlayerSetTempo(0,tempo);
    	}
    	/* Count down when the rest of the time is less than 30 seconds */
    	if(((LapTime/1000) < 32) && ((LapTime/1000) > 1)){
    	    if(((LapTime/100) % 10) == 0){
    		nuAuSndPlayerPlay(EFF_TIME_DWN1);
    		nuAuSndPlayerSetPitch(44100.0/32000);
    	    }
    	}
    	if((LapTime/1000) == 1){
    	    if(((LapTime/100) % 10) == 0){
    		nuAuSndPlayerPlay(EFF_TIME_DWN2);
    		nuAuSndPlayerSetPitch(44100.0/32000);
    	    }
    	}
        }
    }

    /*------------------------------*/
    /*  The initialization of various parameters	*/
    /*  IN:	None			*/
    /*  RET:None			*/
    /*------------------------------*/
    void
    InitSnake( void )
    {
        int idx, idx2;
        /* Initialization of self-machine (for the amount of parameters vary for each player) */
        for(idx = 0; idx < SNAKE_MAX_LEN + 1; idx++){
    	/* The position of player 1 */
    	Snake.pos_x[0][idx] = -BASE_MAX_SIZE+50;
    	Snake.pos_y[0][idx] = BASE_MAX_SIZE-50;
    	Snake.pos_z[0][idx] = 30.0;
    	/* The head direction */
    	Snake.head_dir[0] = 0.0;

    	/* The position of player 1 */
    	Snake.pos_x[1][idx] = -BASE_MAX_SIZE+50;
    	Snake.pos_y[1][idx] = -BASE_MAX_SIZE+50;
    	Snake.pos_z[1][idx] = 30.0;
    	/* The head direction */
    	Snake.head_dir[1] = 90.0;

    	/* The position of player 1 */
    	Snake.pos_x[2][idx] = BASE_MAX_SIZE-50;
    	Snake.pos_y[2][idx] = BASE_MAX_SIZE-50;
    	Snake.pos_z[2][idx] = 30.0;
    	/* The head direction */
    	Snake.head_dir[2] = 270.0;

    	/* The position of player 1 */
    	Snake.pos_x[3][idx] = BASE_MAX_SIZE-50;
    	Snake.pos_y[3][idx] = -BASE_MAX_SIZE+50;
    	Snake.pos_z[3][idx] = 30.0;
    	/* The head direction */
    	Snake.head_dir[3] = 180.0;
        }
        /* Positions of the Z-axis are all common */
        Snake.pos_z[0][0] = 40.0;
        Snake.pos_z[1][0] = 40.0;
        Snake.pos_z[2][0] = 40.0;
        Snake.pos_z[3][0] = 40.0;

        for(idx = 0; idx < SNAKE_MAX; idx++){
    	for(idx2 = 0; idx2 < SNAKE_MAX_LEN + 1; idx2++){
    	    /* The direction of the snake (the same direction as that of the head) */
    	    Snake.rot_x[idx][idx2] = 0.0;
    	    Snake.rot_y[idx][idx2] = 0.0;
    	    Snake.rot_z[idx][idx2] = Snake.head_dir[idx];

    	    /* The drawing size */
    	    Snake.size_x[idx][idx2] = 2.0;
    	    Snake.size_y[idx][idx2] = 2.0;
    	    Snake.size_z[idx][idx2] = 2.0;
    	}
        }

        for(idx = 0; idx < SNAKE_MAX; idx++){
    	Snake.tailcnt[idx] = 5;
    	Snake.wallhit[idx] = 0;
    	Snake.objhit[idx] = -1;
    	Snake.enemyhit[idx] = -1;
    	Snake.bufidx[idx] = TAIL_MOVE_BUF-1;
    	Snake.enemytailhit[idx] = -1;
    	Snake.enemytailhitnum[idx] = -1;
    	Snake.sleepcnt[idx] = 0;
        }

        /* Setting the kind of the tail */
        for(idx = 1; idx < 6; idx++){
    	Snake.tail_attr[0][idx] = 0;
    	Snake.tail_attr[1][idx] = 1;
    	Snake.tail_attr[2][idx] = 2;
    	Snake.tail_attr[3][idx] = 3;
        }

        /* The initialization of the buffer for drawing the body of the self-machine */
        /* Set the default position, first */
        for(idx2 = 0; idx2 < 4; idx2++){
    	for(idx = 0; idx < TAIL_MOVE_BUF; idx++){
    	    snakemove[idx2].SnakeX[idx] = Snake.pos_x[idx2][0];
    	    snakemove[idx2].SnakeY[idx] = Snake.pos_y[idx2][0];
    	    snakemove[idx2].SnakeRotZ[idx] = Snake.rot_z[idx2][0];
    	}
        }
    }

    /*------------------------------*/
    /*  The initialization of the shooting tail	*/
    /*  IN:	None			*/
    /*  RET:None			*/
    /*------------------------------*/
    void
    InitShotTail( void )
    {
        int idx;

        for( idx = 0; idx < 20; idx++ ){
    	/* The position */
    	mtx_shot[idx].pos_x = 0.0;
    	mtx_shot[idx].pos_y = 0.0;
    	mtx_shot[idx].pos_z = 20.0;

    	/* The rotation */
    	mtx_shot[idx].rot_x = 0.0;
    	mtx_shot[idx].rot_y = 0.0;
    	mtx_shot[idx].rot_z = 0.0;

    	/* The size */
    	mtx_shot[idx].size_x = 2.0;
    	mtx_shot[idx].size_y = 2.0;
    	mtx_shot[idx].size_z = 2.0;

    	/* The kind */
    	mtx_shot[idx].tail_pattern = -1;

    	/* The direction */
    	mtx_shot[idx].tail_dir = 0.0;

    	/* The status */
    	mtx_shot[idx].status = TAIL_NOTHING;

    	/* The one who shots */
    	mtx_shot[idx].user = -1;
        }
    }
    /*------------------------------------------------------*/
    /*  Entry of the tail for shooting			*/
    /*  IN:	PadNum		The registering player number	*/
    /*  	TailAttr	The kind of registering tail		*/
    /*  RET:None						*/
    /*------------------------------------------------------*/
    void
    EntryShotTail( int PadNum, int TailAttr )
    {
        int idx;

        for(idx = 0; idx < 20; idx++){
    	if(mtx_shot[idx].tail_pattern == -1){
    	    mtx_shot[idx].pos_x =
    	      Snake.pos_x[PadNum][0] + cosf(2.0*M_PI/(360/Snake.head_dir[PadNum]))*HEAD_SIZE;
    	    mtx_shot[idx].pos_y =
    	      Snake.pos_y[PadNum][0] + sinf(2.0*M_PI/(360/Snake.head_dir[PadNum]))*HEAD_SIZE;

    	    /* Don't enter if the entry location is out of range of the map */
    	    /* Don't enter if the entry location is on the map object  */
    	    if(ShotTail2Wall( mtx_shot[idx].pos_x,mtx_shot[idx].pos_y ) ||
    	       ShotTail2Obj( mtx_shot[idx].pos_x,mtx_shot[idx].pos_y ) != -1){
    		return;
    	    }

    	    mtx_shot[idx].pos_z = Snake.pos_z[PadNum][0];
    	    mtx_shot[idx].tail_pattern = TailAttr;

    	    mtx_shot[idx].rot_x = Snake.rot_x[PadNum][0];
    	    mtx_shot[idx].rot_y = Snake.rot_y[PadNum][0];
    	    mtx_shot[idx].rot_z = Snake.rot_z[PadNum][0];

    	    mtx_shot[idx].tail_dir = Snake.head_dir[PadNum];
    	    mtx_shot[idx].status = TAIL_MOVING;
    	    mtx_shot[idx].user = PadNum;

    	    SlideTail( PadNum );
    	    if((--Snake.tailcnt[PadNum]) < 0){
    		Snake.tailcnt[PadNum] = 0;
    	    }
    	    return;
    	}
        }
    }

    /*----------------------------------------------*/
    /*  Erase the register of the tail for shooting	*/
    /*  IN:	ShotTailNum	Registered tail number	*/
    /*  RET:None					*/
    /*----------------------------------------------*/
    void
    EraseShotTail( int ShotTailNum )
    {
        mtx_shot[ShotTailNum].pos_x = 0.0;
        mtx_shot[ShotTailNum].pos_y = 0.0;
        mtx_shot[ShotTailNum].pos_z = 20.0;

        mtx_shot[ShotTailNum].rot_x = 0.0;
        mtx_shot[ShotTailNum].rot_y = 0.0;
        mtx_shot[ShotTailNum].rot_z = 0.0;

        mtx_shot[ShotTailNum].size_x = 2.0;
        mtx_shot[ShotTailNum].size_y = 2.0;
        mtx_shot[ShotTailNum].size_z = 2.0;

        mtx_shot[ShotTailNum].tail_pattern = -1;

        mtx_shot[ShotTailNum].tail_dir = 0.0;

        /* The status */
        mtx_shot[ShotTailNum].status = TAIL_NOTHING;
    }

    /*--------------------------------------*/
    /*  Rotate the tail of the self-machine	*/
    /*  IN:	PadNum	Player number	*/
    /*  RET:None				*/
    /*--------------------------------------*/
    void
    SlideTail( int PadNum )
    {
        int idx;
        if(Snake.tailcnt[PadNum] >= 2){
    	for(idx = 1; idx < Snake.tailcnt[PadNum]+1; idx++){
    	    Snake.tail_attr[PadNum][idx] = Snake.tail_attr[PadNum][idx+1];
    	}
        }
    }

    /*------------------------------*/
    /*  Move of the shot tail	*/
    /*  IN:	None			*/
    /*  RET:None			*/
    /*------------------------------*/
    void
    ShotTailMove( void )
    {
        int idx;
        float ChkX,ChkY;

        for(idx = 0; idx < 20; idx++){
    	if( mtx_shot[idx].tail_pattern == -1){
    	    continue;
    	}
    	/* Get the vector of the shot tail from the head direction */
    	ChkX = cosf(2.0*M_PI/(360/mtx_shot[idx].tail_dir))*SHOT_SPEED;
    	ChkY = sinf(2.0*M_PI/(360/mtx_shot[idx].tail_dir))*SHOT_SPEED;

    	/* Move it practically */
    	mtx_shot[idx].pos_x += ChkX;
    	mtx_shot[idx].pos_y += ChkY;

    	/* Hit the wall or obstacle */
    	if(ShotTail2Wall( mtx_shot[idx].pos_x,mtx_shot[idx].pos_y ) ||
    	   ShotTail2Obj( mtx_shot[idx].pos_x,mtx_shot[idx].pos_y ) != -1){
    	    /* Return the beginning */
    	    mtx_shot[idx].pos_x -= ChkX;
    	    mtx_shot[idx].pos_y -= ChkY;
    	    mtx_shot[idx].status = TAIL_WAITING;
    	}
    	/* Make the shot tail rotate */
    	mtx_shot[idx].rot_z += 10.0;
        }
    }

    /*--------------------------------------*/
    /*  Display the self-machine (the head)	*/
    /*  IN:	dp	The dynamic pointer	*/
    /*      Padnum	The player number	*/
    /*  RET:None				*/
    /*--------------------------------------*/
    void
    DrawHead( Dynamic* dp, int PadNum )
    {
        /* The calculation of the model coordinate system */
        guTranslate(&(dp->snake_trans[PadNum][0]),
    		Snake.pos_x[PadNum][0],
    		Snake.pos_y[PadNum][0],
    		Snake.pos_z[PadNum][0]);
        guScale(&(dp->snake_scale[PadNum][0]),
    	    Snake.size_x[PadNum][0],
    	    Snake.size_y[PadNum][0],
    	    Snake.size_z[PadNum][0]);

        /* On fainting */
        if((--Snake.sleepcnt[PadNum]) > 0){
    	if(0x2 > (nuScRetraceCounter & 0x2)) {
    	    Snake.rot_y[PadNum][0] += 3.0;
    	}else{
    	    Snake.rot_y[PadNum][0] -= 3.0;
    	}
        }else{
    	Snake.sleepcnt[PadNum] = 0;
    	Snake.rot_y[PadNum][0] = 0.0;
        }

        guRotateRPY(&(dp->snake_rotate[PadNum][0]),
    		Snake.rot_x[PadNum][0]+90.0,
    		Snake.rot_y[PadNum][0],
    		Snake.rot_z[PadNum][0]+90.0);

        /* Setting the model-matrix */

        gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->snake_trans[PadNum][0])),
    	      G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);

        gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->snake_scale[PadNum][0])),
    	      G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);

        gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->snake_rotate[PadNum][0])),
    	      G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);

        /* No anti-ailiasing, the opaque surface */
        gDPSetRenderMode(glistp++,G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2);

        /* Clear and set up of geometry */
        gSPClearGeometryMode(glistp++,0xFFFFFFFF);
        gSPSetGeometryMode(glistp++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH | G_LIGHTING |
    		       G_CULL_BACK);

        gDPSetCycleType(glistp++, G_CYC_1CYCLE);
        /* Setting the combiner */
        gDPSetCombineMode(glistp++,G_CC_DECALRGB, G_CC_DECALRGB);

        /* The display list of the model */
        switch(PadNum){
          case 0:
    	gSPDisplayList(glistp++,kiyaata1_mdl_model0);
    	break;
          case 1:
    	gSPDisplayList(glistp++,kiyaata2_mdl_model0);
    	break;
          case 2:
    	gSPDisplayList(glistp++,kiyaata3_mdl_model0);
    	break;
          case 3:
    	gSPDisplayList(glistp++,kiyaata4_mdl_model0);
    	break;
          default:
    	break;
        }

        gDPPipeSync(glistp++);
    }

    /*--------------------------------------*/
    /*  Display the self-machine (the tail)	*/
    /*  IN:	dp	The dynamic pointer	*/
    /*      Padnum	The player number	*/
    /*  RET:None				*/
    /*--------------------------------------*/
    void
    DrawTail( Dynamic* dp, int PadNum )
    {
        int MaxTail,idx;

        MaxTail = Snake.tailcnt[PadNum];

        /* Draw from the most rear tail */
        for(idx = MaxTail; idx >= 1; idx--){
    	/* The calculation of the model coordinate system */
    	guTranslate(&(dp->snake_trans[PadNum][idx]),
    		    Snake.pos_x[PadNum][idx],
    		    Snake.pos_y[PadNum][idx],
    		    Snake.pos_z[PadNum][idx]
    		    );
    	guScale(&(dp->snake_scale[PadNum][idx]),
    		Snake.size_x[PadNum][idx],
    		Snake.size_y[PadNum][idx],
    		Snake.size_z[PadNum][idx]
    		);

    	guRotateRPY(&(dp->snake_rotate[PadNum][idx]),
    		    Snake.rot_x[PadNum][idx]+90.0,
    		    Snake.rot_y[PadNum][idx],
    		    Snake.rot_z[PadNum][idx]+90.0
    		    );

    	/* Setting the model-matrix */
    	gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->snake_trans[PadNum][idx])),
    		  G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);

    	gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->snake_scale[PadNum][idx])),
    		  G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);

    	gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->snake_rotate[PadNum][idx])),
    		  G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);

    	/* No anti-aliasing, the opaque surface */
    	gDPSetRenderMode(glistp++,G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2);

    	/* Clear and set up the geometry */
    	gSPClearGeometryMode(glistp++,0xFFFFFFFF);
    	gSPSetGeometryMode(glistp++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH |
    			   G_LIGHTING | G_CULL_BACK);

    	gDPSetCycleType(glistp++, G_CYC_1CYCLE);
    	/* Setting the combiner */
    	gDPSetCombineMode(glistp++,G_CC_DECALRGB, G_CC_DECALRGB);

    	/* The display list of the model */
    	switch(Snake.tail_attr[PadNum][idx]){
    	  case 0:
    	    gSPDisplayList(glistp++,kiyadou1_mdl_model0);
    	    break;
    	  case 1:
    	    gSPDisplayList(glistp++,kiyadou2_mdl_model0);
    	    break;
    	  case 2:
    	    gSPDisplayList(glistp++,kiyadow3_mdl_model0);
    	    break;
    	  case 3:
    	    gSPDisplayList(glistp++,kiyadow4_mdl_model0);
    	    break;
    	  default:
    	    break;
    	}
    	gDPPipeSync(glistp++);
        }
    }

    /*--------------------------------------*/
    /*  Record the head locus in the buffer	*/
    /*  IN: Padnum	The player number	*/
    /*  RET:None				*/
    /*--------------------------------------*/
    void
    RecordHeadPos( int PadNum )
    {
        snakemove[PadNum].SnakeX[Snake.bufidx[PadNum]] = Snake.pos_x[PadNum][0];
        snakemove[PadNum].SnakeY[Snake.bufidx[PadNum]] = Snake.pos_y[PadNum][0];
        snakemove[PadNum].SnakeRotZ[Snake.bufidx[PadNum]] = Snake.rot_z[PadNum][0];

        if((--Snake.bufidx[PadNum]) < 0){
    	Snake.bufidx[PadNum] = TAIL_MOVE_BUF-1;
        }
    }

    /*----------------------------------------------*/
    /*  Move of the tail ichase the head locusj	*/
    /*  IN: Padnum	The player number		*/
    /*  RET:None					*/
    /*----------------------------------------------*/
    void
    UpdateTailPos( int PadNum )
    {
        int MaxTail,idx;

        MaxTail = Snake.tailcnt[PadNum];

        for(idx = 1; idx <= MaxTail; idx++){
    	if((Snake.bufidx[PadNum] + SUB*idx) > TAIL_MOVE_BUF-1 ){
    	    Snake.bufidx_wk[PadNum] = Snake.bufidx[PadNum] + SUB*idx - TAIL_MOVE_BUF;
    	}else{
    	    Snake.bufidx_wk[PadNum] = Snake.bufidx[PadNum] + SUB*idx;
    	}

    	Snake.pos_x[PadNum][idx] = snakemove[PadNum].SnakeX[Snake.bufidx_wk[PadNum]];
    	Snake.pos_y[PadNum][idx] = snakemove[PadNum].SnakeY[Snake.bufidx_wk[PadNum]];
    	Snake.rot_z[PadNum][idx] = snakemove[PadNum].SnakeRotZ[Snake.bufidx_wk[PadNum]];
        }
    }

    /*--------------------------------------*/
    /*  Display the shot tail		*/
    /*  IN:	dp	The dynamic pointer	*/
    /*  RET:None				*/
    /*--------------------------------------*/
    void
    DrawShotTail( Dynamic* dp )
    {
        int idx;

        for(idx = 0; idx < 20; idx++){
    	if(mtx_shot[idx].tail_pattern == -1){
    	    continue;
    	}else{
    	    guTranslate(&(dp->shot_trans[idx]),
    			mtx_shot[idx].pos_x,
    			mtx_shot[idx].pos_y,
    			mtx_shot[idx].pos_z
    			);

    	    guScale(&(dp->shot_scale[idx]),
    		    mtx_shot[idx].size_x,
    		    mtx_shot[idx].size_y,
    		    mtx_shot[idx].size_z
    		    );

    	    guRotateRPY(&(dp->shot_rotate[idx]),
    			mtx_shot[idx].rot_x+90.0,
    			mtx_shot[idx].rot_y,
    			mtx_shot[idx].rot_z
    			);

    	    /* Setting the model-matrix */
    	    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->shot_trans[idx])),
    		      G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);

    	    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->shot_rotate[idx])),
    		      G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);

    	    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dp->shot_scale[idx])),
    		      G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);

    	    /* No anti-aliasing, the opaque surface */
    	    gDPSetRenderMode(glistp++,G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2);

    	    /* Clear and set up the geometry */
    	    gSPClearGeometryMode(glistp++,0xFFFFFFFF);
    	    gSPSetGeometryMode(glistp++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH |
    			       G_LIGHTING | G_CULL_BACK);

    	    gDPSetCycleType(glistp++, G_CYC_1CYCLE);
    	    /* Setting the combiner */
    	    gDPSetCombineMode(glistp++,G_CC_DECALRGB, G_CC_DECALRGB);

    	    /* The display list of the model */
    	    switch(mtx_shot[idx].tail_pattern){
    	      case 0:
    		gSPDisplayList(glistp++,kiyadou1_mdl_model0);
    		break;
    	      case 1:
    		gSPDisplayList(glistp++,kiyadou2_mdl_model0);
    		break;
    	      case 2:
    		gSPDisplayList(glistp++,kiyadow3_mdl_model0);
    		break;
    	      case 3:
    		gSPDisplayList(glistp++,kiyadow4_mdl_model0);
    		break;
    	      default:
    		break;
    	    }
    	    gDPPipeSync(glistp++);
    	}
        }
    }

    /*------------------------------------------------------*/
    /*  Make the head turn in the direction of the Control Stick (in 90 degree units)*/
    /*  IN:	PadNum		The registering player number	*/
    /*  RET:	None					*/
    /*------------------------------------------------------*/
    void
    ChgHeadDir( int PadNum, int stickx, int sticky )
    {
        Snake.head_dir[PadNum] = Atan2f((float)sticky,
    				    (float)stickx);

        if(stickx < 0 && sticky == 0){
    	Snake.head_dir[PadNum] += 180.0;
        }
        Snake.rot_z[PadNum][0] = Snake.head_dir[PadNum];
    }

    /*----------------------------------------------*/
    /*  Read the movie data				*/
    /*  IN:	MovieNum	The scene number of the movie	*/
    /*      dp		The dynamic pointer	*/
    /*  RET:None					*/
    /*----------------------------------------------*/
    void
    ReadMovie( int MovieNum, Dynamic* dp )
    {
        u32 r2rlen;
        u32 onecutlen;

        /* Compute the stored segment heads of the movie data */
        r2rlen = _moviedataSegmentRomEnd - _moviedataSegmentRomStart;

        /* Compute the length of each scene */
        onecutlen = r2rlen / 16;

        /* Copy data from ROM to RDRAM, practically */
        nuPiReadRom( (u32)(_moviedataSegmentRomStart + onecutlen * MovieNum),
    		(void *)&(dp->MovieBuf[0]),
    		onecutlen
    		);
    }

    /*------------------------------*/
    /*  Display screen information	*/
    /*  IN:	None			*/
    /*  RET:None			*/
    /*------------------------------*/
    void
    DrawInfo( void )
    {
        sprintf(outstring,"TIME:%03llu", LapTime/1000);
        Draw8Font(20,20, TEX_COL_WHITE, 0);

        if(ReplayMode){
    	sprintf(outstring,"NOW PLAYING TRACE DATA");
    	Draw8Font(20,30, TEX_COL_WHITE, 0);
        }

        if(Pause){
    	if(0x30 > (nuScRetraceCounter & 0x30)){
    	    sprintf(outstring,"PAUSE");
    	    Draw8Font(141,111, TEX_COL_BLACK, 0);

     	    sprintf(outstring,"PAUSE");
    	    Draw8Font(140,110, TEX_COL_GREEN, 0);
    	}
        }
    }

    /*------------------------------------------------------------------------------*/
    /* Count the number of tails of each snake (for the judge of the game end)	*/
    /*  IN:	 ChkStat	Checking contents for time-up, all got tails)		*/
    /*  RET: Result	-1:A tie game	0:The normal game end	1:Win with perfect	*/
    /*     : Outside work Winner	The winner number				*/
    /*------------------------------------------------------------------------------*/
    int
    CheckTailNum( int ChkStat )
    {
        int idx, TailNumWk = 0;

        if(ChkStat == TIME_UP){
    	if(Snake.tailcnt[0] == 5 &&
    	   Snake.tailcnt[1] == 5 &&
    	   Snake.tailcnt[2] == 5 &&
    	   Snake.tailcnt[3] == 5){
    	    Winner = -1;
    	    return(-1);
    	}
    	for(idx = 0; idx < 4; idx++){
    	    if(TailNumWk <  Snake.tailcnt[idx]){
    		TailNumWk = Snake.tailcnt[idx];
    		Winner = idx;
    	    }
    	}
    	return(0);
        }else{
    	for(idx = 0; idx < 4; idx++){
    	    if(Snake.tailcnt[idx] == 20){
    		Winner = idx;
    		return(1);
    	    }
    	}
    	return(0);
        }
    }

    /*------------------------------*/
    /* Each initialization of parts of the main game	*/
    /*  IN:	None			*/
    /*  RET:None			*/
    /*------------------------------*/
    void
    InitGame( void )
    {
        InitSnake();
        InitShotTail();
        View = 0.0;
        MovieNumber = 0;
        StartTime = osGetTime();
        NowTime = osGetTime();
        NowPauseTime = 0;
        Pause = 1;
        TraceIdx = 0;
        SaveIdx = 0;
        SaveOfsIdx = 0;
        UpTempo = 0;
    }