/* TS: B:\FZ3S.TS */
/* sl p4 v1 */
/* for 486 */
 
#include<grp3.h>
#include<spr1.h>
#include<file.h>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<snd2.h>
#include<cd.h>
#include<mapv.h>
 
#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 */
#define SY 56 
 
/**/
#define SX 64 
/* 3d screen */
#define SCR 1 
#define ZOOMX 8 
/**/
 
#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)) 
#define ALIGN16BYTES asm(".align 4") 
 
short chip[320 * 240 + 256];
short data[DATASIZE * STMAX + 10000];
short gr1[1 << (MSX + MSY)];
short gr2[SX * SY];
char sp[320*240*2+512];
char pcm1[70000];
int sp_patno[99];
int stage;
int totalstep;
int gloop;
int human;
 
extern int cd[100];
 
struct{
        int px;
        int py;
        int dwx;
        int dwy;
    }table[DEGMAX * SY];
 
struct{
        int st;
        int pos;
        char pad[STEPMAX];
    }paddata[STMAX], npaddata[PLAYERS];
 
int sinb[DEGMAX], cosb[DEGMAX];
int slsize[SY];
 
int px1[PLAYERS], py1[PLAYERS], pdeg[PLAYERS], psp[PLAYERS];
int ploop[PLAYERS];
int bcol[PLAYERS], pdx[PLAYERS], pdy[PLAYERS];
int ph[PLAYERS], pdh[PLAYERS];
int pki[PLAYERS];
int startpos[PLAYERS];
int step[PLAYERS][LOOPMAX];
int runstate[PLAYERS];
int nowpad[PLAYERS];
int tst[PLAYERS];
 
void OUTPB(int a, int b);
 
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);
    }
}
 
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];
            /*
                if (deg == 0){
                    printf("%dta%d\n",a, table[a].py);
                }
            */
            table[a].dwx = dw * cosb[deg];
            table[a].dwy = dw * sinb[deg];
            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;
        /*printf("%d\n",slsize[i]);*/
    }
}
 
#define MAPPOINT(x, y) MAP((((x) & MMASK) | ((y) & MMASK2)) >> SHIFT) 
 
/*
    void map3d(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 / 5); 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));
                *(block++) = MAPPOINT((poxs += dx), (poys += dy));
            }
        }
    }
*/
 
void slput(int a, int x, int y, int yy) 
{
    int c = slsize[yy] >> SHIFT;
    /**/
     
    spput(a, sp_patno[c], x - (c >> 1), y - (c >> 2) + ((2 - SCR) << 6), 0);
}
 
#define ABS(x) ((x) > 0 ? (x) : ( - (x))) 
 
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;
}
 
void move_step(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;
     
    if (h){
        /* jumping now */
        dh -= (1 << (SHIFT - 3));
        h += dh;
        slip >>= 2;
        if (h <= 0){
            h = 0;
            /* out ??? */
            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;
            }
        }
    }
    else {
        /* 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 + 1)) * 2;
                    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 
 
void padread() 
{
    int i;
     
    for (i = 0; i < PLAYERS; i++){
        switch (pki[i]){
            case 0:{
                /* human */
                nowpad[i] = pad(i);
                break;
            }
            case 1:{
                /* com */
                nowpad[i] = paddata[stage].pad[totalstep];
                break;
            }
            default:{
                nowpad[i] = 255;
            }
        }
        if (runstate[i] > 0){
            nowpad[i] |= 0xf0;
        }
         
        npaddata[i].pad[totalstep] = nowpad[i];
    }
}
 
void player(int pl) 
{
    int p = 0, sp = psp[pl];
    int x = px1[pl], y = py1[pl], deg = pdeg[pl];
    int i, j = 0;
    int ox, kx1, ky1, xx, yy;
    int k;
     
     
    if (pl < SCR){
        map3d((pl << (9 + 6)) + ((2 - SCR) << 14), x, y, deg);
    }
    p = nowpad[pl];
    if ((p & LEFT) == 0){
        deg = (deg + DEGMAX - 1) % DEGMAX;
    }
    if ((p & RIGHT) == 0){
        deg = (deg + 1) % DEGMAX;
    }
     
    if (((p & T1) == 0) && (sp < 
            ((ph[pl] != 0) ? ((1 << (SHIFT - 3)) * 8) : (1 << (SHIFT - 3))) * 6)){
        sp += (1 << (SHIFT - 9)) * 2;
    }
    else if (((p & T2) == 0)){
        sp -= (1 << (SHIFT - 7)) * 2;
    }
    else {
        sp -= (1 << (SHIFT - 9)) * 2;
    }
     
    if (sp <  - (1 << SHIFT - 4)){
        sp =  - (1 << SHIFT - 4);
    }
    else if (sp < 0){
        sp += (1 << (SHIFT - 9)) * 2;
    }
     
    /*
        move_step(&x, &y, deg, &sp, pl);
        move_step(&x, &y, deg, &sp, pl);
    */
     
    if (pl < SCR){
        chpitch(64 + pl, (sp << (14 - SHIFT)) - (1 << 13));
        slput((pl << 2) + (pl << 4), 128, 
                (pl << 7) + (YC * 2) - (ph[pl] >> SHIFT) + 1, YC);
         
        /* other players */
        for (k = 0; k < PLAYERS; k++){
            if (k == pl){
                continue;
            }
            xx = (px1[k] - x);
            yy = (py1[k] - y);
            /* mod */
            xx &= ((1 << (SHIFT + MSX)) - 1);
            yy &= ((1 << (SHIFT + MSY)) - 1);
            /* 0-ext */
            if (xx & (1 << (SHIFT + MSX - 1))){
                xx -= (1 << (SHIFT + MSX));
            }
            if (yy & (1 << (SHIFT + MSY - 1))){
                yy -= (1 << (SHIFT + MSY));
            }
             
            kx1 = (xx >> FSHIFT) * (cosb[deg] >> FSHIFT)
                 + (yy >> FSHIFT) * (sinb[deg] >> FSHIFT);
            ky1 =  - (xx >> FSHIFT) * (sinb[deg] >> FSHIFT)
                 + (yy >> FSHIFT) * (cosb[deg] >> FSHIFT);
             
            for (i = 0; i < SY; i++){
                yy = table[i].py;
                if (yy > ky1){
                    xx = table[i].px;
                    ox = table[i].dwx >> 1;
                    for (j = 0; i < SX << 1; j++){
                        if (xx > kx1){
                            break;
                        }
                        xx += ox;
                    }
                    break;
                }
            }
            if ((i > 0) && (i < SY) && (j > 0) && (j < (SX << 1))){
                slput((k << 2) + (pl << 4), ((j * DOTASP) >> 1) - 1, 
                        (pl << 7) + (i << 1)
                         - ((ph[k] * slsize[i] >> SHIFT) / slsize[(int )YC]) - 1, i);
            }
            else {
                slput((k << 2) + (pl << 4), 0, 280, 0);
            }
        }
    }
     
    /*
        px1[pl] = x;
        py1[pl] = y;
    */
    pdeg[pl] = deg;
    psp[pl] = sp;
     
    /*printf("%d\t%d\n",t1, t2);*/
}
 
void title_clear(int x, int y, int a, int b) 
{
    color(0x6318);
    paint_color(0x6318);
    paint_mode(0x22);
    box(x, y, a, b);
}
 
void result() 
{
    int i, x;
    char ch[] = "PLAYER-0";
    char time_format[] = "00 : 00 : 00";
     
    dpage(0, 0);
    wpage(0);
    zoom(2, 2);
    sposition(0, 0);
    width(320, 240);
    view(0, 0, 511, 255);
    title_clear(0, 0, 511, 255);
    cdpause();
    /*cdplay(cd[0], cd[1]);*/
    dpage(3, 0);
     
    color(0);
    symbol(96, 48, "-----RESULT-----");
    for (i = 0; i < PLAYERS; i++){
        x = tst[i];
        color(0);
        ch[7] = '1' + i;
        symbol(8, 80 + i * 24, ch);
         
        if (pki[i] == 0){
            symbol(80, 80 + i * 24, "human");
        }
        else if (pki[i] == 1){
            symbol(80, 80 + i * 24, "com");
        }
         
        if (x == 0){
            symbol(136, 80 + i * 24, "retire");
        }
        else {
            color(31);
            symbol(136, 80 + i * 24, "goal");
        }
         
        time_format[11] = '0' + x % 10;
        x /= 10;
        time_format[10] = '0' + x % 6;
        x /= 6;
        time_format[6] = '0' + x % 10;
        x /= 10;
        time_format[5] = '0' + x % 6;
        x /= 6;
        time_format[1] = '0' + x % 10;
        x /= 10;
        time_format[0] = '0' + x;
        symbol(208, 80 + i * 24, time_format);
    }
     
    while ((PAD & T1) == 0){}
    while ((PAD & T1) != 0){}
}
 
void roop() 
{
    int i, j, a;
     
    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 */
    }
    if (human == 1){
        /* 1 player */
        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 {
        /* 2 players */
        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;
        }
    }
    /*
        npaddata[0].pos = startpos[0];
        npaddata[1].pos = startpos[1];
        npaddata[2].pos = startpos[2];
    */
    for (j = 0; j < PLAYERS; j++){
        if (j < 2){
            chgtone(64 + j, 0);
            keyon(64 + j, 40, 100);
            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 (human == 1){
        runstate[2] = 3;
    }
     
    cdpause();
    cdplay(cd[2], cd[3]);
     
    /* main roop */
     
    totalstep = 0;
    dpage(3, 1);
    while (1){
        if (PAD == 127){
            /* select button */
            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(j);
            move_step(j);
        }
        for (j = 0; j < PLAYERS; j++){
            player(j);
        }
    }
     
    /*
        printf("runst %d\n",runstate[0]);
        printf("runst %d\n",runstate[1]);
        printf("runst %d\n",runstate[2]);
    */
     
    for (j = 0; j < PLAYERS; j++){
        keyoff(64 + j);
    }
    for (i = 0; i < SPMAX; i++){
        spput(i, 0, 0, 300, 0);
    }
    sprite_off(SPMAX);
     
    /* total step count */
    /*printf("step\n");*/
    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];
            }
        }
        /*printf("%d\n", tst[j]);*/
    }
    /* top player */
    i = -1;
    a = 100000;
    for (j = 0; j < PLAYERS; j++){
        if ((tst[j] > 0) && (tst[j] < a)){
            a = tst[j];
            i = j;
        }
    }
    /*printf("pl%d\n", i);*/
     
    /* paddata copy */
    if (i != -1){
        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();
}
 
int menu(int a) 
{
    int x = 120;
    int z;
    int p;
    int mod = 1;
    char c[256] = "st-A";
     
    z = 0;
    if (a == 2){
        z = stage;
    }
    else if (a == 1){
        z = human - 1;
    }
     
    while (PAD != 255){}
    title_clear(0, 150, 511, 255);
     
    while (1){
        p = PAD;
        if ((p & DOWN) == 0){
            z++;
            z %= mod;
            play(70, 1);
            keyoff(70);
        }
        else if ((p & UP) == 0){
            z += (mod - 1);
            z %= mod;
            play(70, 1);
            keyoff(70);
        }
        else if ((p & T1) == 0){
            play(70, 1);
            keyoff(70);
            break;
        }
         
        if (a == 1){
            color(0);
            symbol(x, 176, "1 PLAYER");
            symbol(x, 192, "2 PLAYERS");
            /*symbol(x, 208, "CONFIG");*/
            symbol(x, 208, "EXIT");
            title_clear(x - 32, 150, x - 1, 224);
            color(31);
            paint_color(31);
            paint_mode(0x22);
            /*box(x - 16, 166, x - 13, 169);*/
            maru(x - 16, 168 + z * 16, 2);
            mod = 3;
        }
        else if (a == 2){
            color(0);
            symbol(x - 32, 176, " SELECT STAGE");
            c[3] = 'A' + z;
            title_clear(x, 180, x + 48, 208);
            color(0);
            symbol(x + 8, 208, c);
            mod = STMAX;
        }
         
        while (PAD != 255){}
        while (PAD == 255){}
    }
    return z;
}
 
int title() 
{
    int x;
    int y;
     
    dpage(0, 0);
    wpage(0);
    zoom(2, 2);
    sposition(0, 0);
    width(320, 240);
    view(0, 0, 511, 255);
    title_clear(0, 0, 511, 255);
    cdpause();
    cdplay(cd[0], cd[1]);
    dpage(1, 0);
     
    put_block(sp + 512, 0, 32, 319, 149, 0, 0);
     
    x = menu(1);
    y = 0;
    switch (x){
        case 0:{
            human = 1;
            y = menu(2);
            break;
        }
        case 1:{
            human = 2;
            y = menu(2);
            break;
        }
        default:{
            y = -1;
        }
    }
     
    wpage(0);
    color(0x8000);
    paint_color(0x8000);
    paint_mode(0x22);
    box(0, 0, 511, 255);
     
    zoom(ZOOMX, 4);
     
    return (y);
}
 
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("sl.tif", sp);
    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); j++){
            data[i * DATASIZE + j * 16 + 15] = x;
        }
    }
     
    sprite_screen(sp + 512);
    for (i = 0; i < 32; i++){
        x = (i % 10) * 2;
        y = (i / 10);
        sp_patno[i] = spset(x, y, 2, 1);
    }
     
    load_block("title.tif", sp);
}
 
void main() 
{
    int pcmsize;
    /*int i;*/
     
    tableset();
    egbinit();
    cdinit("cdtime.txt");
    sinit();
    pcmsize = load_block("sl.pmb", pcm1);
    pmbset(pcm1, pcmsize);
     
    stage = 0;
    human = 1;
    while ((stage = title()) != -1){
        roop();
    }
     
    soff();
    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();
    /*
        printf("%d\n\n", ploop[0]);
        for (i = 0; i < 5; i++){
            printf("%d\n",step[0][i]);
        }
    */
}
/* TS-END */
