
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "grp3.h"
#include "spr1.h"
#include "file.h"
#include "snd2.h"
#include "cd.h"
#include "mapv.h"

#define DEFAULT_CONFIG_FILE "slime.cfg"

#define SHIFT 10                /* 固定小数演算用 */
#define FSHIFT (SHIFT >> 1)
#define DEGMAX 180              /* 向きの細かさ   */
#define SPMAX 100
#define PLAYERS 3
#define SLIP 96                 /* スリップ       */
/* map size */
#define MSX 10
#define MSY 8
#define MMASK ((1 << (SHIFT + MSX)) - (1 << SHIFT))
#define MMASK2 ((1 << (SHIFT + MSX + MSY)) - (1 << (SHIFT + MSX)))
#define DATASIZE (1 << MSX + MSY - 8)
/* screen size */

#ifdef SPEED_SLOW
#define SX 64                   /* ３Ｄスクリーンの横ドット数     */
#define SCR 1                   /* ３Ｄスクリーンの数             */
#define ZOOMX 8                 /* ｘ方向のハードウェア画面拡大率 */
#else
#define SX 128
#define SCR 2
#define ZOOMX 4
#endif

#ifdef LARGE_SCREEN
#define SY 112
#undef  SCR
#define SCR 1
#define SCR_VSHIFT 8
#else
#define SY 56
#define SCR_VSHIFT ((2-SCR) << 6)
#endif

#define DOTASP (ZOOMX >> 1)

/* 3D PARAM */
#define VS 10
#define YC (SY * .85)
#define V1 (VS * .1)
#define V2 (VS * 1.2)
#define HS (V2 - V1)
#define ZS (VS)
#define PI 3.1415926535897932384626434
#define MAP(x) (*(gr1 + (x)))
#define LOOPMAX 100
#define STMAX 16
#define STEPMAX 3600*6
#define GLOOP 3
#define PAD (pad(0) & pad(1))

static short chip[320 * 240 + 256];
static short data[DATASIZE * STMAX + 10000];
       short gr1[1 << (MSX + MSY)];          /* mapv.c が参照する */
static char tit[320 * 240 * 2 + 512];;
static short font_data[128][8*8];
static char pcm1[70000];
static int sp_patno[32*3];
static int stage;
static int totalstep;
static int gloop;
static int players;
static int human;
static int demo_mode;
static int scan_mode;
static int mirror;
static int demo_camera;
static int camera_deg[PLAYERS];
static int display_skip;

#define ZC (int)((VS * ZS / (V1 + YC * (double)(V2 - V1) / SY)) * (1 << SHIFT))

 /* 透視投象用のテーブル */
struct {
    int px;
    int py;
    int dwx;
    int dwy;
} table[DEGMAX * SY];       /* mapv.c が参照する */

static struct {
    int st;
    int pos;
    char pad[STEPMAX];
} paddata[STMAX], npaddata[PLAYERS], paddata_another[STMAX];

static int sinb[DEGMAX], cosb[DEGMAX];
static int slsize[SY];

static int px1[PLAYERS], py1[PLAYERS], pdeg[PLAYERS], psp[PLAYERS];
static int ploop[PLAYERS];
static int bcol[PLAYERS], pdx[PLAYERS], pdy[PLAYERS];
static int ph[PLAYERS], pdh[PLAYERS];
static int pki[PLAYERS];
static int startpos[PLAYERS];
static int step[PLAYERS][LOOPMAX];
static int runstate[PLAYERS];
static int nowpad[PLAYERS];
static int tst[PLAYERS];

static int snd_volume = 48;       /* 効果音の音量 */

static int cd_datamax;
static int cd_track[100];

/*//////////////////////////////////////////////////////////////////////*/
/* 初期化 */
static int read_config(char *filename)
{
    FILE *fp;
    char line[256];
    char tok1[256], tok2[256];
    int cd_count;

    scan_mode = 0;
    mirror = 1;
    demo_camera = 0;
    display_skip = 0;
    snd_volume = 30;

    cdstop();
    if (cdtoc());

    fp = fopen(filename, "r");
    if (fp == NULL) {
        fclose(fp);
        return -1;
    }
    cd_count = 1;
    while ((fgets(line, 256, fp) != NULL) && (line[0] != 0)) {
        if (line[0] == '#') continue;
        sscanf(line, "%s %s", tok1, tok2);
        if (strcmp(tok1, "cd_playing") == 0){
            cd_track[cd_count] = atoi(tok2) - 1;
            cd_count++;
        }
        else if (strcmp(tok1, "cd_title") == 0){
            cd_track[0] = atoi(tok2) - 1;
        }
        else if (strcmp(tok1, "mirror") == 0){
            if (strcmp(tok2, "on") == 0)
                mirror = 1;
            else
                mirror = 0;
        }
        else if (strcmp(tok1, "demo_camera") == 0){
            if (strcmp(tok2, "front") == 0)
                demo_camera = 0;
            else if (strcmp(tok2, "roll-l") == 0)
                demo_camera = 3;
            else if (strcmp(tok2, "roll-r") == 0)
                demo_camera = 2;
            else
                demo_camera = 1;
        }
        else if (strcmp(tok1, "display_skip") == 0){
            display_skip = atoi(tok2);
        }
        else if (strcmp(tok1, "sound_volume") == 0){
            snd_volume = atoi(tok2);
        }
    }
    cd_datamax = cd_count - 1;
    fclose(fp);
    return 0;
}

/*//////////////////////////////////////////////////////////////////////*/
/* 三角関数をテ−ブル化 */
static void sintable()
{
    int i;
    double x;

    for (i = 0; i < DEGMAX; i++) {
        x = ((double) (i) * 2 * PI / DEGMAX);
        sinb[i] = sin(x) * (1 << SHIFT);
        cosb[i] = cos(x) * (1 << SHIFT);
    }
}

/*//////////////////////////////////////////////////////////////////////*/
/* 透視投象のための変換テ−ブルをセットする */
static void tableset()
{
    int a;
    int deg, y;
    double z, v, degx, dw, zc;
    int x, i;

    sintable();

    degx = atan2(HS, ZS);

    v = V1 + YC * (double) (V2 - V1) / SY;
    zc = VS * ZS / v;

    for (y = 0; y < SY; y++) {
        v = V1 + y * (double) (V2 - V1) / SY;
        z = VS * ZS / v;
        dw = HS * z / (ZS * SX);
        for (deg = 0; deg < DEGMAX; deg++) {
            a = deg * SY + y;
            table[a].px = (z - zc) * sinb[deg];
            table[a].py = (zc - z) * cosb[deg];
            table[a].dwx = dw * cosb[deg] + .5;
            table[a].dwy = dw * sinb[deg] + .5;
            table[a].px -= table[a].dwx * (SX / 2);
            table[a].py -= table[a].dwy * (SX / 2);
        }
    }

    x = table[SY - 1].dwx;
    for (i = 0; i < SY; i++) {
        slsize[i] = (31 << SHIFT) * x / table[i].dwx;
    }
}

#define MAPPOINT(x, y) MAP((((x) & MMASK) | ((y) & MMASK2)) >> SHIFT)

/*//////////////////////////////////////////////////////////////////////*/
/* map3d() の原型となった関数 現在は使用していない */
/* この関数は結果を配列に書き込むが、map3d() では VRAM に直接書き込む */
static void map3d_old(short *block, int mx, int my, int deg)
{
    register int poxs, poys, dx, dy;
    int x, y;
    int p = deg * SY;
    
    for (y = 0; y < SY; y++){
        dx = table[p].dwx;
        dy = (table[p].dwy) << MSX;
        poxs = table[p].px + mx;
        poys = (table[p].py + my) << MSX;
        poxs -= dx;
        poys -= dy;
        p++;
        for (x = 0; x < (SX / 4); x++){
            *(block++) = MAPPOINT((poxs += dx), (poys += dy));
            *(block++) = MAPPOINT((poxs += dx), (poys += dy));
            *(block++) = MAPPOINT((poxs += dx), (poys += dy));
            *(block++) = MAPPOINT((poxs += dx), (poys += dy));
        }
    }
}

/*//////////////////////////////////////////////////////////////////////*/
/* スライムを表示する */
static void slput(int a, int x, int y, int yy, int color)
{
    int c = (slsize[yy] >> SHIFT);

    spput(a, sp_patno[c + (color << 5)], 
          x - (c >> 1), y - (c >> 2) + SCR_VSHIFT, 0);
}

#define ABS(x) ((x) > 0 ? (x) : ( - (x)))

/*//////////////////////////////////////////////////////////////////////*/
/* 道路の中かどうか判定する */
static int judge(int x, int y)
{
    int col, cr, cg, cb;

    col = MAPPOINT(x, y << MSX);
    cr = col >> 5;
    cg = col >> 10;
    cb = col;
    cr &= 31;
    cg &= 31;
    cb &= 31;
    if ((cr == cg) && (cg == cb)) {
        /* safe */
        return 1;
    }
    /* hit */
    return 0;
}

/*//////////////////////////////////////////////////////////////////////*/
/* スライムの移動および地面の当たり判定 */
static void move_step_1(int pl)
{
    int col = 0, cr, cg, cb;
    int dx = pdx[pl], dy = pdy[pl];
    int dh = pdh[pl], h = ph[pl];
    int slip = (1 << SHIFT) / SLIP;
    int ddx, ddy;
    int sp = psp[pl];
    int x = px1[pl], y = py1[pl], deg = pdeg[pl];

    ddx = (sinb[deg] >> FSHIFT) * ((sp) >> FSHIFT);
    ddy = -((cosb[deg] >> FSHIFT) * ((sp) >> FSHIFT));

    x += dx;
    y += dy;

    if ((sp < (1 << (SHIFT - 3))) && (runstate[pl] == 1)) {
        runstate[pl] = 3;
    }
    if (bcol[pl] == 1024 * 31 + 31) {
        slip >>= 2;
    }
    col = MAPPOINT(x, y << MSX);
    cr = col >> 5;
    cg = col >> 10;
    cb = col;
    cr &= 31;
    cg &= 31;
    cb &= 31;

    h += dh;
    if (h) {
        dh -= (1 << (SHIFT - 3));
        /* jumping now */
        slip >>= 2;
        if (h <= 0) {
            h = 0;             /* 着地 */
            dh = 0;
            if ((cr == cg) && (cg == cb)) {
                ;
            }
            else if (((cr == 0) || (cr == 31)) &&
                     ((cb == 0) || (cb == 31)) &&
                     ((cg == 0) || (cg == 31))) {
                ;
            }
            else {
                /* lost */
                runstate[pl] = 2;
                sp = 0;
                dx = 0;
                dy = 0;
                ddx = 0;
                ddy = 0;
            }
        }
    }
    if (!h){
        /* running */
        if ((cr == cg) && (cg == cb)) {
            if ((cr == 31) && (sp > (1 << (SHIFT - 3)))) {
                /* side */
                sp -= (1 << (SHIFT - 8));
            }
            else if ((cr == 7) && (sp > (1 << (SHIFT - 3)))) {
                /* black */
                sp -= (1 << (SHIFT - 7));
            }
        }
        else {
            switch (col) {
              case 31:{
                      /* blue */
                      if (bcol[pl] == 32 * 31) {
                          /* goalline */
                          step[pl][ploop[pl]] = -step[pl][ploop[pl]];
                          ploop[pl]++;
                          if (ploop[pl] == gloop) {
                              /* goal !! */
                              runstate[pl] = 1;
                          }
                      }
                      else if (bcol[pl] != 31) {
                          dx = -dx;
                          dy = -dy;
                          x = px1[pl];
                          y = py1[pl];
                          col = bcol[pl];
                          sp >>= 1;
                      }
                      break;
                  }
              case 31 * 32:{
                      if (bcol[pl] == 31) {
                          dx = -dx;
                          dy = -dy;
                          x = px1[pl];
                          y = py1[pl];
                          col = bcol[pl];
                          sp >>= 1;
                      }
                      break;
                  }
              case 31 * 32 + 31:{
                      /* jump */
                      dh = (1 << (SHIFT - 3)) * 32;
                      h += dh;
                      break;
                  }
              case 31 * 1024 + 31:{
                      break;
                  }
              default:{
                      x = px1[pl];
                      y = py1[pl];
                      if (judge(x + dx, y) != 1) {
                          dx = -dx;
                      }
                      if (judge(x, y + dy) != 1) {
                          dy = -dy;
                      }
                      col = bcol[pl];
                      sp >>= 1;
                  }
            }
        }
    }

    if (ABS(ddx - dx) < slip) {
        dx = ddx;
    }
    else if (ddx > dx) {
        dx += slip;
    }
    else {
        dx -= slip;
    }

    if (ABS(ddy - dy) < slip) {
        dy = ddy;
    }
    else if (ddy > dy) {
        dy += slip;
    }
    else {
        dy -= slip;
    }

    px1[pl] = x;
    py1[pl] = y;
    pdeg[pl] = deg;
    psp[pl] = sp;

    pdx[pl] = dx;
    pdy[pl] = dy;
    bcol[pl] = col;
    ph[pl] = h;
    pdh[pl] = dh;
    step[pl][ploop[pl]]++;
}

#define TIMES   1000
#define UP      1
#define DOWN    2
#define LEFT    4
#define RIGHT   8
#define T1      16
#define T2      32

/*//////////////////////////////////////////////////////////////////////*/
/* スライムの制御情報をひとステップぶん読み込んで nowpad[] に格納する */
static void padread()
{
    int i;

    for (i = 0; i < PLAYERS; i++) {
        switch (pki[i]) {
          case 0:{
                  /* human */
                  nowpad[i] = pad(i);
                  if (!(nowpad[i] & LEFT) || !(nowpad[i] & RIGHT)){
                      nowpad[i] |= UP;
                  }
                  break;
              }
          case 1:{
                  /* com */
                  nowpad[i] = paddata[stage].pad[totalstep];
                  break;
              }
          case 2:{
                  /* com 2 */
                  nowpad[i] = paddata_another[stage].pad[totalstep];
                  nowpad[i] |= UP;
                  break;
              }
          default:{
                  nowpad[i] = 255;
              }
        }
        if (nowpad[i] == 0)
            nowpad[i] = 255;
        if (runstate[i] > 0)
            nowpad[i] |= 0xf0;

        npaddata[i].pad[totalstep] = nowpad[i];
    }
}

/*//////////////////////////////////////////////////////////////////////*/
/* スライムの制御 */
static void move_step_2(int pl)
{
    int p = nowpad[pl], sp = psp[pl];
    int deg = pdeg[pl];

    if ((p & LEFT) == 0) {
        deg -= 1;
        if (deg < 0)
            deg += DEGMAX;
    }
    if ((p & RIGHT) == 0) {
        deg += 1;
        if (deg >= DEGMAX)
            deg -= DEGMAX;
    }
    if (((p & T1) == 0) && (sp < ((ph[pl] != 0) ? ((1 << (SHIFT - 3)) * 8) :
                                  (1 << (SHIFT - 3))) * 6)) {
        sp += (1 << (SHIFT - 8));
    }
    else if (((p & T2) == 0)) {
        sp -= (1 << (SHIFT - 7));    /* ブレ−キ */
    }
    else {
        sp -= (1 << (SHIFT - 8));    /* 緩やかに減速 */
    }
    if (sp < -(1 << SHIFT - 4)) {
        sp = -(1 << SHIFT - 4);
    }
    else if (sp < 0) {
        sp += (1 << (SHIFT - 8));
    }
    pdeg[pl] = deg;
    psp[pl] = sp;
}

/*//////////////////////////////////////////////////////////////////////*/
/* 画面表示 */
static void write_screen(int pl)
{
    int p = nowpad[pl];
    int x = px1[pl], y = py1[pl];
    int i, j = 0;
    int kx1, ky1, xx, yy;
    int k;
    int view_deg = 0;
    int cdeg;
    int pad_now;
    static pad_old[PLAYERS] = {255, 255,255};

    if (!demo_mode){
        view_deg = pdeg[pl];
        if (((p & UP) == 0) && mirror){
            view_deg += DEGMAX/2;
            if (view_deg >= DEGMAX) view_deg -= DEGMAX;
        }
    }
    else {
        pad_now = pad(pl);
        cdeg = camera_deg[pl];
        if ((pad_now & LEFT) == 0) {
            cdeg -= 1;
            if (cdeg < 0)
                cdeg += DEGMAX;
        }
        if ((pad_now & RIGHT) == 0) {
            cdeg += 1;
            if (cdeg >= DEGMAX)
                cdeg -= DEGMAX;
        }
        if (((pad_now & T1) == 0) && ((pad_old[pl] & T1) != 0)){
            if      (cdeg < DEGMAX/4)
                cdeg = DEGMAX/4;
            else if (cdeg < DEGMAX/4*2)
                cdeg = DEGMAX/4*2;
            else if (cdeg < DEGMAX/4*3)
                cdeg = DEGMAX/4*3;
            else
                cdeg = 0;
        }
        if (((pad_now & T2) == 0) && ((pad_old[pl] & T2) != 0)){
            if      ((cdeg > DEGMAX/4*3) || (cdeg == 0))
                cdeg = DEGMAX/4*3;
            else if (cdeg > DEGMAX/4*2)
                cdeg = DEGMAX/4*2;
            else if (cdeg > DEGMAX/4)
                cdeg = DEGMAX/4;
            else
                cdeg = 0;
        }
        if (demo_camera == 2){
            cdeg++;
            if (cdeg >= DEGMAX)
                cdeg -= DEGMAX;
        }
        else if (demo_camera == 3){
            cdeg--;
            if (cdeg < 0)
                cdeg += DEGMAX;
        }

        view_deg = cdeg;
        if (demo_camera == 0){
            view_deg += pdeg[pl];
            if (view_deg >= DEGMAX)
                view_deg -= DEGMAX;
        }
        pad_old[pl] = pad_now;
        camera_deg[pl] = cdeg;
    }

    if (pl < 2)
        chpitch(64 + pl, (psp[pl] << (14 - SHIFT)) - (1 << 13));
    if (pl < SCR) {
        slput((pl << 2) + (pl << 4), 128,
              (pl << 7) + (YC * 2) - (ph[pl] >> SHIFT) + 1, YC, pl);

        /* other players */
        for (k = 0; k < players; k++) {
            int adj;
            if (k == pl) {
                continue;
            }
            xx = (px1[k] - x);
            yy = (py1[k] - y);

            /* mod screensize (signed) */
            xx &= ((1 << (SHIFT + MSX)) - 1);
            yy &= ((1 << (SHIFT + MSY)) - 1);
            if (xx & (1 << (SHIFT + MSX - 1))) {
                xx -= (1 << (SHIFT + MSX));
            }
            if (yy & (1 << (SHIFT + MSY - 1))) {
                yy -= (1 << (SHIFT + MSY));
            }
            /* 画面外 (4<<4 dot) */
            if (ABS(xx) >= (4 << (SHIFT + 4)) ||
                ABS(yy) >= (4 << (SHIFT + 4))) {
                slput((k << 2) + (pl << 4), 0, 280, 0, 0);
                continue;
            }
            kx1 = (xx >> FSHIFT) * (cosb[view_deg] >> FSHIFT)
                + (yy >> FSHIFT) * (sinb[view_deg] >> FSHIFT);
            ky1 = -(xx >> FSHIFT) * (sinb[view_deg] >> FSHIFT)
                + (yy >> FSHIFT) * (cosb[view_deg] >> FSHIFT);

            for (i = 0; i < SY; i++) {
                if (table[i].py >= ky1)
                    break;
            }
            adj = i;
            if ((i > 0) && (((table[i - 1].py + table[i].py) >> 1) >= ky1))
                adj--;
            if ((ky1 - ZC) >> (FSHIFT - 1) != 0)
                kx1 = kx1 *
                    ((table[i].py + table[adj].py - (ZC << 1)) >> FSHIFT)
                        / ((ky1 - ZC) >> (FSHIFT - 1));

            j = ((kx1 << 2) - ((table[i].px + table[adj].px) << 1))
                / (table[i].dwx + table[adj].dwx) + 1;

            if ((i > 0) && (i < SY) && (j > 0) && (j < (SX << 1))) {
                slput((k << 2) + (pl << 4), ((j * DOTASP) >> 1) - 1,
                      (pl << 7) + (i + adj)
                      - ((ph[k] * slsize[i] >> SHIFT) / slsize[(int) YC]) - 0,
                      i, k);
            }
            else {
                slput((k << 2) + (pl << 4), 0, 280, 0, 0);
            }
        }
    }
    if (pl < SCR) {
        map3d((pl<<(9+6)) + (SCR_VSHIFT<<8), x, y, view_deg);  /* 透視投象 */
    }
}

/*//////////////////////////////////////////////////////////////////////*/
/* title.tif に書かれたフォントを使って文字列を表示 */
static void symbol_myfont(int x, int y, int accent, char *t)
{
    int fn;
    while ((*t) != 0){
        fn = ((*t) - ' ') + accent * 64;
        put_block((char *) font_data[fn], x, y-8, x+7, y-1, 0, 0);
        x += 8;
        t++;
    }
}

/*//////////////////////////////////////////////////////////////////////*/
/* 画面の矩形領域をクリア */
static void title_clear(int x, int y, int a, int b)
{
    color(0x6318);
    paint_color(0x6318);
    paint_mode(0x22);
    box(x, y, a, b);
}

/*//////////////////////////////////////////////////////////////////////*/
/* タイムの表示 */
static void result_put_time(int x, int y, int accent, int t)
{
    char time_format[] = "00:00:00";

    time_format[7] = '0' + t % 10;
    t /= 10;
    time_format[6] = '0' + t % 6;
    t /= 6;
    time_format[4] = '0' + t % 10;
    t /= 10;
    time_format[3] = '0' + t % 6;
    t /= 6;
    time_format[1] = '0' + t % 10;
    t /= 10;
    time_format[0] = '0' + t;
    symbol_myfont(x, y, accent, time_format);
}

/*//////////////////////////////////////////////////////////////////////*/
/* 試合結果の表示 */
static void result(int winner)
{
    int i, j;
    char ch[] = "PLAYER 0";

    if (demo_mode)
        return;

    dpage(0, 0);
    wpage(0);
    if (scan_mode) {
        zoom(4, 1);
    }
    else {
        zoom(2, 2);
    }
    sposition(0, 0);
    width(320, 240);
    view(0, 0, 511, 255);
    title_clear(0, 0, 511, 255);
    cdpause();
    dpage(3, 0);

    symbol_size(8, 8, 0);
    symbol_myfont(136, 48, 1, "RESULT");
    for (i = 0; i < PLAYERS; i++) {
        ch[7] = '1' + i;
        symbol_myfont(40, 80 + i * 48, 0, ch);

        if (pki[i] == 0) {
            symbol_myfont(112, 80 + i * 48, 0, "HUMAN");
        }
        else if ((pki[i] == 1) || (pki[i] == 2)) {
            symbol_myfont(112, 80 + i * 48, 0, "COMP");
        }
        if (tst[i] == 0) {
            symbol_myfont(168, 80 + i * 48, 0, "::::");
            symbol_myfont(208, 80 + i * 48, 0, "::::::::");
        }
        else {
            symbol_myfont(168, 80 + i * 48, 1, "GOAL");
            result_put_time(208, 80 + i * 48, (winner==i)?1:0, tst[i]);
            for (j = 0; j < gloop; j++) {
                result_put_time(208, 96 + i * 48 + j * 8, 0, -step[i][j]);
            }
        }
    }

    while ((PAD & T1) == 0) {
    }
    while ((PAD & T1) != 0) {
    }
}

/*//////////////////////////////////////////////////////////////////////*/
/* ひと試合して結果を表示するまで */
static void roop()
{
    int i, j, k, a;
    int cdnum;
    int skip_count = 0;

    sprite_on(SPMAX);

    chiptobit(gr1, chip + 256,
              data + (stage * DATASIZE), 1 << (MSX - 4), 1 << (MSY - 4));

    /* start position */
    for (i = 0; i < DATASIZE; i++) {
        if (data[i + (stage * DATASIZE)] == 20) {
            break;
        }
    }
    gloop = GLOOP;
    for (j = 0; j < PLAYERS; j++) {
        pki[j] = -1;
        /* ki=  0 : human / 1 :com / 2 :com2 */
    }
    if (human == 1) {
        /* 1 player */
        players = 2;
        demo_mode = 0;
        pki[0] = 0;
        pki[1] = 1;
        startpos[1] = paddata[stage].pos;
        startpos[2] = 2;
        if (startpos[1] == 0) {
            startpos[0] = 1;
            startpos[2] = 2;
        }
        else if (startpos[1] == 1) {
            startpos[0] = 0;
            startpos[2] = 2;
        }
        else {
            startpos[0] = 0;
            startpos[2] = 1;
        }
    }
    else if (human == 2) {
        /* 2 players */
        players = 3;
        demo_mode = 0;
        pki[0] = 0;
        pki[1] = 0;
        pki[2] = 1;
        startpos[2] = paddata[stage].pos;
        if (startpos[2] == 0) {
            startpos[0] = 1;
            startpos[1] = 2;
        }
        else if (startpos[2] == 1) {
            startpos[0] = 0;
            startpos[1] = 2;
        }
        else {
            startpos[0] = 0;
            startpos[1] = 1;
        }
    }
    else {
        /* demo mode */
        players = 2;
        demo_mode = 1;
        pki[0] = 1;
        pki[1] = 2;
        pki[2] = 0;
        startpos[0] = paddata[stage].pos;
        startpos[1] = paddata_another[stage].pos;
    }
    for (j=0; j<PLAYERS; j++){
        for (k=0; k<STEPMAX; k++){
            npaddata[j].pad[k] = 255;
        }
    }
    for (j = 0; j < PLAYERS; j++) {
        if (j < 2) {
            chgtone(64 + j, j);
            keyon(64 + j, 40, snd_volume);
            chpitch(64 + j, -8192);
            span(64 + j, (j & 1) * 127);
        }
        py1[j] = ((i & 15) << (SHIFT + 4))
            + ((startpos[j] + 2) << (SHIFT + 1));
        px1[j] = ((i >> 4) << (SHIFT + 4)) + (6 << SHIFT);
        pdeg[j] = (DEGMAX * 3) / 4;
        psp[j] = 0;
        ploop[j] = 0;
        bcol[j] = 0;
        pdx[j] = 0;
        pdy[j] = 0;
        pdh[j] = 0;
        ph[j] = 0;
        for (a = 0; a < LOOPMAX; a++) {
            step[j][a] = 0;
        }
        runstate[j] = 0;
    }
    if (players != 3) {
        runstate[2] = 3;
    }
    cdpause();
    cdnum = rnd(cd_datamax - 1);
    cdplay_track(cd_track[cdnum + 1]);

    /* ひと試合する */

    totalstep = 0;
    dpage(3, 1);
    while (1) {
        if (PAD == 127) {
            /* select button */
            if (demo_mode)
                demo_mode = -1;
            break;
        }
        if (totalstep >= STEPMAX) {
            /* time up */
            break;
        }
        i = 0;
        for (j = 0; j < PLAYERS; j++) {
            i += (runstate[j] < 2) ? 1 : 0;
        }
        if (i == 0) {
            /* goal or out */
            break;
        }
        totalstep++;
        padread();

        for (j = 0; j < PLAYERS; j++) {
            move_step_1(j);
            move_step_1(j);
            move_step_2(j);
        }

        if (++skip_count > display_skip){
#ifndef NOSPRITE_WAIT
            sprite_wait(SPMAX);
#endif
            for (j = 0; j < PLAYERS; j++) {
                write_screen(j);
            }
            skip_count = 0;
        }
    }

    for (j = 0; j < PLAYERS; j++) {
        keyoff(64 + j);
    }
    for (i = 0; i < SPMAX; i++) {
        spput(i, 0, 0, 300, 0);
    }
    sprite_off(SPMAX);

    /* count total steps */
    for (j = 0; j < PLAYERS; j++) {
        tst[j] = 0;
        if (step[j][gloop - 1] < 0) {
            for (i = 0; i < gloop; i++) {
                tst[j] -= step[j][i];
            }
        }
    }
    /* 勝者を判定 */
    i = -1;
    a = 100000;
    for (j = 0; j < PLAYERS; j++) {
        if ((tst[j] > 0) && (tst[j] < a)) {
            a = tst[j];
            i = j;
        }
    }

    /* 最速だったプレイヤ−の制御デ−タを記録 */
    if ((i != -1) && (demo_mode == 0) && (pki[i] == 0)) {
        for (j = 0; j < STEPMAX; j++) {
            paddata[stage].pad[j] = npaddata[i].pad[j];
        }
        paddata[stage].st = tst[i];
        paddata[stage].pos = startpos[i];
    }
    while ((PAD & T1) == 0) {
    }

    result(i);
}

/*//////////////////////////////////////////////////////////////////////*/
/* 選択メニュー */
static int menu(int a)
{
    int x = 100;
    int z;
    clock_t cl;
    int p;
    int mod = 1;
    char string_st[256] = "STAGE A";
    char string_ds[256] = "DISPLAY SKIP : 0";
    static int menu_selected = 0;

    z = 0;
    if (a == 2) {
        z = stage;
    }
    else if (a == 1) {
        z = menu_selected;
    }
    wpage(0);
    while (PAD != 255) {
    }
    title_clear(0, 120, 511, 255);

    while (1) {
        p = PAD;
        if ((p & DOWN) == 0) {
            z++;
            z %= mod;
#if (SCR != 2)
            if ((z == 1) && (a == 1)){
                z++;
                z %= mod;
            }
#endif
            play(70, 2, snd_volume);
            keyoff(70);
        }
        else if ((p & UP) == 0) {
            z += (mod - 1);
            z %= mod;
#if (SCR != 2)
            if ((z == 1) && (a == 1)){
                z += (mod - 1);
                z %= mod;
            }
#endif
            play(70, 2, snd_volume);
            keyoff(70);
        }
        else if ((p & T1) == 0) {
            play(70, 2, snd_volume);
            keyoff(70);
            break;
        }
        if (a == 1) {
            symbol_size(8, 8, 0);
            symbol_myfont(x, 146, (z==0)?1:0, "1 PLAYER");
            symbol_myfont(x, 156, (z==1)?1:0, "2 PLAYERS");
            symbol_myfont(x, 166, (z==2)?1:0, "DEMO");
            symbol_myfont(x, 176, (z==3)?1:0, "DISPLAY MODE");
            if (mirror){
                symbol_myfont(x, 186, (z==4)?1:0, "MIRROR : ON ");
            }
            else {
                symbol_myfont(x, 186, (z==4)?1:0, "MIRROR : OFF");
            }
            if (demo_camera == 0){
                symbol_myfont(x, 196, (z==5)?1:0, "CAMERA : FRONT ");
            }
            else if (demo_camera == 2){
                symbol_myfont(x, 196, (z==5)?1:0, "CAMERA : ROLL-R");
            }
            else if (demo_camera == 3){
                symbol_myfont(x, 196, (z==5)?1:0, "CAMERA : ROLL-L");
            }
            else if (demo_camera == 1){
                symbol_myfont(x, 196, (z==5)?1:0, "CAMERA : FIXED ");
            }
            string_ds[15] = '0' + display_skip;
            symbol_myfont(x, 206, (z==6)?1:0, string_ds);
            symbol_myfont(x, 216, (z==7)?1:0, "EXIT");
            mod = 8;
        }
        else if (a == 2) {
            symbol_myfont(x, 176, 0, " SELECT STAGE");
            string_st[6] = 'A' + z;
            symbol_myfont(x + 28, 208, 1, string_st);
            mod = STMAX;
        }
        while (PAD != 255);
        while (PAD == 255);
        cl = clock();
        while (clock() == cl);
    }
    if (a == 1){
        menu_selected = z;
    }
    return z;
}

/*//////////////////////////////////////////////////////////////////////*/
/* ディスプレイのスキャンモ−ドを変える */
static void change_scan_mode()
{
    if (scan_mode) {
        /* 15k -> 31k */
        screen(1, 5);
        screen(0, 10);
        wpage(0);
        zoom(2, 2);
        width(320, 240);
        view(0, 0, 511, 255);
        /*sposition((320-256)/2, 0);*/
        wpage(1);
        zoom(2, 2);
        width(256, 240);
        view(0, 0, 255, 255);
        sposition((320 - 256) / 2, 0);
        scroll(0, 2);
        scan_mode = 0;
    }
    else {
        /* 31k -> 15k */
        screen(1, 8);
        screen(0, 11);
        wpage(0);
        zoom(4, 1);
        width(256, 240);
        view(0, 0, 255, 255);
        /*sposition((320-256)/2, 0);*/
        wpage(1);
        zoom(4, 1);
        width(256, 240);
        view(0, 0, 255, 255);
        sposition((320 - 256) / 2, 0);
        scroll(0, 2);
        scan_mode = 1;
    }
}



/*//////////////////////////////////////////////////////////////////////*/
/* タイトルを表示してメニューが選択されるのを待つ デモ中だとすぐリターン */
static int title()
{
    int x;
    int y;
    int flag = 0;
    int j;

    if (demo_mode > 0) {
        if (stage <= STMAX - 2)
            return (stage + 1);
        return (0);
    }
    dpage(0, 0);
    wpage(0);
    if (scan_mode)
        zoom(4, 1);
    else
        zoom(2, 2);
    sposition(0, 0);
    width(320, 240);
    view(0, 0, 511, 255);
    title_clear(0, 0, 511, 255);

    cdpause();
    cdplay_track(cd_track[0]);

    dpage(1, 0);

    put_block(tit + 512, 0, 16, 319, 117+16, 0, 0);

    do {
        x = menu(1);
        y = 0;
        switch (x) {
          case 0:{
                  human = 1;
                  y = menu(2);
                  flag = 1;
                  break;
              }
          case 1:{
                  human = 2;
                  y = menu(2);
                  flag = 1;
                  break;
              }
          case 2:{
                  human = 0;
                  y = menu(2);
                  flag = 1;
                  break;
              }
          case 3:{
                  change_scan_mode();
                  break;
              }
          case 4:{
                  mirror = 1 - mirror;
                  break;
              }
          case 5:{
                  for (j=0; j<PLAYERS; j++)
                      camera_deg[j] = 0;
                  if ((++demo_camera) >= 4) demo_camera = 0;
                  break;
              }
          case 6:{
                  if ((++display_skip) >= 5) display_skip = 0;
                  break;
              }
          default:{
                  y = -1;
                  flag = 1;
              }
        }
    } while (flag == 0);

    wpage(0);
    color(0x8000);
    paint_color(0x8000);
    paint_mode(0x22);
    box(0, 0, 511, 255);

    if (scan_mode)
        zoom(ZOOMX << 1, 2);
    else
        zoom(ZOOMX, 4);

    return (y);
}

/*//////////////////////////////////////////////////////////////////////*/
/* 画面等を初期化する */
static void egbinit()
{
    int i, j, x, y;
    char mapfile[255] = "sca.map";

    load_block("chip.tif", (char *) chip);
    load_block("fz.tim", (char *) paddata);
    load_block("fz_ano.tim", (char *) paddata_another);
    load_block("sl.tif", tit);
    for (i = 0; i < STMAX; i++) {
        load_block(mapfile, (char *) data + (i * DATASIZE * 2));
        mapfile[2]++;
        x = data[i * DATASIZE];
        for (j = 0; j < (1 << (MSX-4)); j++) {
            data[i * DATASIZE + j * 16 + 15] = x;
        }
    }

    sprite_screen(tit + 512);

    for (i = 0; i < 32*3; i++) {
        x = (i % 10) * 2;
        y = (i / 10);
        sp_patno[i] = spset(x, y, 2, 1);
    }

    load_block("title.tif", tit);
    for (y=0; y<4; y++){
        for (x=0; x<31; x++){
            for (i=0; i<8; i++){
                for (j=0; j<8; j++){
                    font_data[x+y*32][i*8+j] = 
                      ((short *)(tit+512))[(y*8+128+i)*320 + x*8+j];
                }
            }
        }
    }
    stage = 0;
    human = 1;
    demo_mode = 0;
    for (j=0; j<PLAYERS; j++)
        camera_deg[j] = 0;
}

/*//////////////////////////////////////////////////////////////////////*/
int main(int argc, char *argv[])
{
    int pcmsize;

    tableset();
    egbinit();
    if (argc >= 2){
        read_config(argv[1]);
    }
    else {
        read_config(DEFAULT_CONFIG_FILE);
    }
    sinit();
    pcmsize = load_block("sl.pmb", pcm1);
    pmbset(pcm1, pcmsize);

    while ((stage = title()) != -1) {
        roop();
    }

    soff();
    cdstop();
    wpage(1);
    color(0);
    paint_color(0);
    paint_mode(0x22);
    box(0, 0, 511, 255);
    save_block("fz.tim", (char *) paddata, (8 + STEPMAX) * STMAX);
    dpage(3, 0);
    graphix_off();
    return 0;
}

