#include "DxLib.h"

#define SCREEN_WIDTH     (640)                          // 画面の横幅
#define SCREEN_HEIGHT    (480)                          // 画面の縦幅
#define CHIP_SIZE        (32)                           // 一つのチップのサイズ
#define MAP_WIDTH        (SCREEN_WIDTH / CHIP_SIZE)     // マップの横幅
#define MAP_HEIGHT       (SCREEN_HEIGHT / CHIP_SIZE)    // マップの縦幅

#define G                (0.3F)                         // キャラに掛かる重力加速度
#define JUMP_POWER       (9.0F)                         // キャラのジャンプ力
#define SPEED            (5.0F)                         // キャラの移動スピード
#define CHAR_SIZE        (60)                           // プレイヤーのサイズ

typedef struct{
        int img,muki;
}ch_t;

struct shot{
    int x2,y2;  //座標
    int flag;
};

// マップデータ
char MapData[MAP_HEIGHT][MAP_WIDTH] =
{
    0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0,
    1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1,
    1,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1,
    1,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,1,
    1,0,0,1,1, 1,1,0,0,0, 0,0,0,0,0, 0,0,1,0,1,

    1,0,0,0,0, 0,0,0,1,1, 0,0,0,0,0, 0,0,1,0,1,
    1,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,1,0,1,
    1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,1,
    1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1,
    1,0,0,0,0, 0,0,1,1,0, 0,0,0,0,0, 1,0,0,0,1,

    1,0,0,0,0, 1,1,1,1,1, 0,0,0,0,1, 1,0,0,0,1,
    1,2,2,0,0, 1,1,1,1,1, 0,0,0,1,1, 1,0,0,0,1,
    1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,1,
    1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1,
    1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1,
} ;

float PlX, PlY ;            // プレイヤーの座標(中心座標)
float PlDownSp ;            // プレイヤーの落下速度
char PlJumpFlag ;           // プレイヤーがジャンプ中か、のフラグ
char walking_flag ;

int Input, EdgeInput ;      // 入力状態
int FrameStartTime ;        // ６０ＦＰＳ固定用、時間保存用変数

// マップチップの値を取得する関数
int GetChipParam( float X, float Y ) ;

// キャラクタをマップとの当たり判定を考慮しながら移動する関数
int CharMove( float *X, float *Y, float *DownSP,
                    float MoveX, float MoveY, float Size, char *JumpFlag ) ;

// アクションサンプルプログラムメイン関数
int ActMain( void ) ;

// マップとの当たり判定( 戻り値 0:当たらなかった  1:左辺に当たった  2:右辺に当たった
//                                                3:上辺に当たった  4:下辺に当たった
int MapHitCheck( float X, float Y, float *MoveX, float *MoveY ) ;




// WinMain関数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow )
{
    ChangeWindowMode( TRUE ) ;

	if( DxLib_Init() == -1 )	// ＤＸライブラリ初期化処理
	{
		 return -1;				// エラーが起きたら直ちに終了
	}

    // アクションゲームのメイン関数を呼ぶ
    ActMain() ;

    // ＤＸライブラリの後始末
    DxLib_End() ;

    // 終了
    return 0 ;
}

// アクションサンプルプログラムメイン
int ActMain( void )
{
	ch_t ch;
	int Color;
	Color=GetColor(255,255,255);
	int image[16];
	int image1[1];
	char Key[256];
	int image2[16],i=0,counter=0;
	struct shot tama[10];

	if( ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1;

		for(i=0;i<10;i++){                  //初期化処理
                tama[i].x2=(int)PlX; tama[i].y2=(int)PlY; //座標代入
                tama[i].flag=0;                 //飛んでいない事を示すフラグ=0
		}


    // 描画先を裏画面にセット
    SetDrawScreen( DX_SCREEN_BACK ) ;

	walking_flag=FALSE;
	ch.muki=2;
	LoadDivGraph( "chara.png" , 16 , 4 , 4 , 60 , 60 , image );
	LoadDivGraph( "hg.png" , 16 , 4 , 4 , 32, 32 , image2);
	LoadDivGraph( "Mesh.bmp" , 1 , 1 , 1 , 32, 32 , image1);

    // 垂直同期信号を待たない
    SetWaitVSyncFlag( FALSE ) ;

    // プレイヤーの座標を初期化
    PlX = 320.0F ;        PlY = 240.0F ;

    // プレイヤーの落下速度を初期化
    PlDownSp = 0.0F ;

    // ジャンプ中フラグを倒す
    PlJumpFlag = FALSE ;

    // 入力状態の初期化
    Input = 0 ;
    EdgeInput = 0 ;

    // ６０ＦＰＳ固定用、時間保存用変数を現在のカウント値にセット
    FrameStartTime = GetNowCount() ;

    // メインループ開始、ＥＳＣキーで外に出る
    while( ProcessMessage() == 0 && !ClearDrawScreen() && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 && !GetHitKeyStateAll( Key ))
    {
			DrawFormatString(0,0,Color,"ch.muki=%d",ch.muki);

        // １/６０秒立つまで待つ
        while( GetNowCount() - FrameStartTime < 1000 / 60 ){}

        // 現在のカウント値を保存
        FrameStartTime = GetNowCount() ;

		if( (int)PlX%1==0 &&  (int)PlY%1==0){         //座標が32で割り切れたら入力可能
			walking_flag=FALSE;
            if     ( CheckHitKey( KEY_INPUT_UP   )  == 1 )  //上ボタンが押されたら
                    ch.muki=0;         //上向きフラグを立てる
            else if( CheckHitKey( KEY_INPUT_LEFT  )  == 1 )  //左ボタンが押されたら
                    ch.muki=1;         //左向きフラグを立てる
            else if( CheckHitKey( KEY_INPUT_RIGHT  )  == 1 )  //右ボタンが押されたら
                    ch.muki=2;         //右向きフラグを立てる
			else walking_flag=TRUE;
        }

        // プレイヤーの移動処理
        {
            float MoveX, MoveY ;

            // 移動量の初期化
            MoveX = 0.0F ;
            MoveY = 0.0F ;

            // 左右の移動を見る
			if(walking_flag==FALSE){
            if( Key[ KEY_INPUT_LEFT   ] == 1 ) MoveX -= SPEED ;
            if( Key[ KEY_INPUT_RIGHT  ] == 1 ) MoveX += SPEED ;
			}

            // 地に足が着いている場合のみジャンプボタン(ボタン１ or Ｚキー)を見る
            if( PlJumpFlag == FALSE && Key[ KEY_INPUT_SPACE ] == 1 )
            {
                PlDownSp = -JUMP_POWER ;
                PlJumpFlag = TRUE ;
            }

            // 落下処理
            PlDownSp += G ;

            // 落下速度を移動量に加える
            MoveY = PlDownSp ;

            // 移動量に基づいてキャラクタの座標を移動
            CharMove( &PlX, &PlY, &PlDownSp, MoveX, MoveY, CHAR_SIZE, &PlJumpFlag ) ;
        }
		{
                if(counter<5)                        //前にエンターを押してから5カウント未満なら
                        counter++;                       //カウントアップ
                else if( Key[ KEY_INPUT_Z ]  == 1 ){//5カウント以上たっていたら
                        counter=0;                       //カウンターを戻す
                        for(i=0;i<10;i++){             
                                if(tama[i].flag==0){     //発射していない玉を探し、
                                        tama[i].flag=1;  //発射フラグを立てる
										tama[i].x2=(int)PlX;
										tama[i].y2=(int)PlY;
                                        break;
                                }
                        }
                }
				if(ch.muki==2){
                for(i=0;i<10;i++){
                        if(tama[i].flag==1){              //発射している玉なら
                                tama[i].x2+=8;
								DrawGraph( tama[i].x2 , tama[i].y2 , image2[15] , TRUE );//玉を描画
                                if(tama[i].x2 > 640){      //もし画面外まで来たら
                                        tama[i].x2=(int)PlX;    //初期値に戻し、
										tama[i].y2=(int)PlY;
                                        tama[i].flag=0;   //発射フラグを戻す
                                }
                        }
                }
				}
				if(ch.muki==1){
                for(i=0;i<10;i++){
                        if(tama[i].flag==1){              //発射している玉なら
                                tama[i].x2-=8;
								DrawGraph( tama[i].x2-30 , tama[i].y2 , image2[6] , TRUE );//玉を描画
                                if(tama[i].x2 < 0){      //もし画面外まで来たら
                                        tama[i].x2=(int)PlX;    //初期値に戻し、
										tama[i].y2=(int)PlY;
                                        tama[i].flag=0;   //発射フラグを戻す
                                }
                        }
                }
				}
				if(ch.muki==0){
                for(i=0;i<10;i++){
                        if(tama[i].flag==1){              //発射している玉なら
                                tama[i].y2-=8;
								DrawGraph( tama[i].x2 , tama[i].y2 , image2[2] , TRUE );//玉を描画
                                if(tama[i].y2 > 480){      //もし画面外まで来たら
                                        tama[i].x2=(int)PlX;    //初期値に戻し、
										tama[i].y2=(int)PlY;
                                        tama[i].flag=0;   //発射フラグを戻す
                                }
                        }
                }
				}
		}

        // マップの描画
        {
            int i, j ;

            for( i = 0 ; i < MAP_HEIGHT ; i ++ )
            {
                for( j = 0 ; j < MAP_WIDTH ; j ++ )
                {
                    // １は当たり判定チップを表しているので１のところだけ描画
                    if( MapData[i][j] == 1 )
                    {
                        /*DrawBox( j * CHIP_SIZE,                i * CHIP_SIZE,
                                 j * CHIP_SIZE + CHIP_SIZE,    i * CHIP_SIZE + CHIP_SIZE,
                                 GetColor( 255, 255, 255 ), TRUE ) ;*/
						DrawGraph( (int)(j * CHIP_SIZE )  , (int) (i * CHIP_SIZE ) , image1[0]  , TRUE ) ;
                    }
                    if( MapData[i][j] == 2 )
                    {
                        DrawBox( j * CHIP_SIZE,                i * CHIP_SIZE,
                                 j * CHIP_SIZE + CHIP_SIZE,    i * CHIP_SIZE + CHIP_SIZE,
                                 GetColor( 0, 0, 255 ), TRUE ) ;
                    }
                }
            }
        }
		if(ch.muki==2)
		ch.img=image[( (int)PlX%32)/8];
		if(ch.muki==1)
		ch.img=image[( (int)PlX%32)/8+4];

        // キャラクタの描画
		DrawGraph( (int) (PlX - CHAR_SIZE * 0.5F)  , (int) (PlY - CHAR_SIZE * 0.5F) , ch.img  , TRUE ) ; 	

        // 画面の更新
        ScreenFlip() ;
    }

    // 終了
    return 0 ;
}

// キャラクタをマップとの当たり判定を考慮しながら移動する
int CharMove( float *X, float *Y, float *DownSP,
                    float MoveX, float MoveY, float Size, char *JumpFlag )
{
    float Dummy = 0.0F ;
    float hsize ;

    // キャラクタの左上、右上、左下、右下部分が当たり判定のある
    // マップに衝突しているか調べ、衝突していたら補正する

    // 半分のサイズを算出
    hsize = Size * 0.5F ;

    // 先ず上下移動成分だけでチェック
    {
        // 左下のチェック、もしブロックの上辺に着いていたら落下を止める
        if( MapHitCheck( *X - hsize, *Y + hsize, &Dummy, &MoveY ) == 3 ) *DownSP = 0.0F ;

        // 右下のチェック、もしブロックの上辺に着いていたら落下を止める
        if( MapHitCheck( *X + hsize, *Y + hsize, &Dummy, &MoveY ) == 3 ) *DownSP = 0.0F ;

        // 左上のチェック、もしブロックの下辺に当たっていたら落下させる
        if( MapHitCheck( *X - hsize, *Y - hsize, &Dummy, &MoveY ) == 4 ) *DownSP *= -1.0F ;

        // 右上のチェック、もしブロックの下辺に当たっていたら落下させる
        if( MapHitCheck( *X + hsize, *Y - hsize, &Dummy, &MoveY ) == 4 ) *DownSP *= -1.0F ;

		if( MapHitCheck( *X , *Y + hsize, &Dummy, &MoveY ) == 3 ) *DownSP = 0.0F ;

        // 上下移動成分を加算
        *Y += MoveY ;
    }

    // 後に左右移動成分だけでチェック
    {
        // 左下のチェック
        MapHitCheck( *X - hsize, *Y + hsize, &MoveX, &Dummy ) ;

        // 右下のチェック
        MapHitCheck( *X + hsize, *Y + hsize, &MoveX, &Dummy ) ;

        // 左上のチェック
        MapHitCheck( *X - hsize, *Y - hsize, &MoveX, &Dummy ) ;

        // 右上のチェック
        MapHitCheck( *X + hsize, *Y - hsize, &MoveX, &Dummy ) ;

        // 左右移動成分を加算
        *X += MoveX ;
    }

    // 接地判定
    {
        // キャラクタの左下と右下の下に地面があるか調べる
        if( GetChipParam( *X - Size * 0.5F, *Y + Size * 0.5F + 1.0F ) == 0 && 
            GetChipParam( *X + Size * 0.5F, *Y + Size * 0.5F + 1.0F ) == 0 )
        {
            // 足場が無かったらジャンプ中にする
            *JumpFlag = TRUE ;
        }
        else
        {
            // 足場が在ったら接地中にする
            *JumpFlag = FALSE ;
        }
    }

    // 終了
    return 0 ;
}


// マップとの当たり判定( 戻り値 0:当たらなかった  1:左辺に当たった  2:右辺に当たった
//                                                3:上辺に当たった  4:下辺に当たった
// 注意：MoveX と MoveY 、どっちか片方が０じゃないとまともに動作しません(爆)
int MapHitCheck( float X, float Y,
                    float *MoveX, float *MoveY )
{
    float afX, afY ;

    // 移動量を足す
    afX = X + *MoveX ;
    afY = Y + *MoveY ;

    // 当たり判定のあるブロックに当たっているかチェック
    if( GetChipParam( afX, afY ) != 0 )
    {
        float blx, bty, brx, bby ;

        // 当たっていたら壁から離す処理を行う

        // ブロックの上下左右の座標を算出
        blx = (float)( (int)afX / CHIP_SIZE ) * CHIP_SIZE ;        // 左辺の X 座標
        brx = (float)( (int)afX / CHIP_SIZE + 1 ) * CHIP_SIZE ;    // 右辺の X 座標

        bty = (float)( (int)afY / CHIP_SIZE ) * CHIP_SIZE ;        // 上辺の Y 座標
        bby = (float)( (int)afY / CHIP_SIZE + 1 ) * CHIP_SIZE ;    // 下辺の Y 座標

        // 上辺に当たっていた場合
        if( *MoveY > 0.0F )
        {
            // 移動量を補正する
            *MoveY = bty - Y - 1.0F  ;

            // 上辺に当たったと返す
            return 3 ;
        }

        // 下辺に当たっていた場合
        if( *MoveY < 0.0F )
        {
            // 移動量を補正する
            *MoveY = bby - Y + 1.0F ;

            // 下辺に当たったと返す
            return 4 ;
        }

        // 左辺に当たっていた場合
        if( *MoveX > 0.0F )
        {
            // 移動量を補正する
            *MoveX = blx - X - 1.0F ;

            // 左辺に当たったと返す
            return 1 ;
        }

        // 右辺に当たっていた場合
        if( *MoveX < 0.0F )
        {
            // 移動量を補正する
            *MoveX = brx - X + 1.0F ;

            // 右辺に当たったと返す
            return 2 ;
        }

        // ここに来たら適当な値を返す
        return 4 ;
    }

    // どこにも当たらなかったと返す
    return 0 ;
}

// マップチップの値を取得する関数
int GetChipParam( float X, float Y )
{
    int x, y ;

    // 整数値へ変換
    x = (int)X / CHIP_SIZE ;
    y = (int)Y / CHIP_SIZE ;

    // マップからはみ出ていたら 0 を返す
    if( x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0 ) return 0 ;

    // 指定の座標に該当するマップの情報を返す
    return MapData[ y ][ x ] ;
}

