root/games/warp/move.c
/* Header: move.c,v 7.0.1.2 86/10/20 14:37:06 lwall Exp */

/* Log: move.c,v
 * Revision 7.0.1.2  86/10/20  14:37:06  lwall
 * Picked some lint.
 *
 * Revision 7.0.1.1  86/10/16  10:52:09  lwall
 * Added Damage.  Fixed random bugs.
 *
 * Revision 7.0  86/10/08  15:12:40  lwall
 * Split into separate files.  Added amoebas and pirates.
 *
 */

#include "EXTERN.h"
#include "warp.h"
#include "bang.h"
#include "object.h"
#include "move.h"
#include "play.h"
#include "score.h"
#include "term.h"
#include "them.h"
#include "us.h"
#include "util.h"
#include "weapon.h"
#include "INTERN.h"
#include "move.h"

void
move_init(void)
{
    ;
}

void
bounce(OBJECT *obj)
{
    int x;
    int y;
    int count=0;

    y = (obj->posy - sgn(obj->vely) + YSIZE00) % YSIZE;
    x = (obj->posx - sgn(obj->velx) + XSIZE00) % XSIZE;
    while (occupant[y][x]) {
        y = (y + rand_mod(3) - 1 + YSIZE00) % YSIZE;
        x = (x + rand_mod(3) - 1 + XSIZE00) % XSIZE;
        if (++count > 10000) {     /* if universe full, get out of it fast */
            unmake_object(obj);
            if (ent) unmake_object(ent);
            if (base) unmake_object(base);
            finish = 1;
            return;
        }
    }
    obj->posy = y;
    obj->posx = x;
    obj->vely = 0;
    obj->velx = 0;
    occupant[y][x] = obj;
    if (numamoebas && obj->image == ' ')
        mvaddc(y+1, x*2, amb[y][x]);
    else
        mvaddc(y+1, x*2, obj->image);
}

void
move_universe(void)
{
    OBJECT *curobj;
    int x;
    int y;
    OBJECT *temp;
    OBJECT *thenext;

    for (curobj = movers; curobj != &root; curobj = curobj->next) {
        x = curobj->posx;
        y = curobj->posy;
        if (curobj == occupant[y][x]) {
            occupant[y][x] = 0;
        }
        else if (curobj->type != Torp && curobj->type != Web) {
            resetty();
            abort();
        }
    }
    for (curobj = movers; curobj != &root; curobj = thenext) {
        thenext = curobj->next;
        if (curobj->vely || curobj->velx) {
            y = curobj->posy;
            x = curobj->posx;
            if (curobj->image != ' ' &&
              (!(temp=occupant[y][x]) || temp->image==' ') ) {
                move(y+1, x*2, numamoebas ? amb[y][x] : ' ');
            }
            y = (y + curobj->vely + YSIZE00) % YSIZE;
            x = (x + curobj->velx + XSIZE00) % XSIZE;
            if (!(temp=occupant[y][x]) || temp->type != Star ||
              curobj->type != Torp || curobj->image == '+' ||
              curobj->image == 'x') {
                curobj->posy = y;
                curobj->posx = x;
            }
            else {
                if (curobj->image == '0') {
                    curobj->vely = rand_mod(3)-1;
                    curobj->velx = rand_mod(3)-1;
                }
                else
                    curobj->vely = curobj->velx = 0;
                y = curobj->posy;
                x = curobj->posx;
            }
        }
        else {                  /* not moving */
            y = curobj->posy;
            x = curobj->posx;
            if (curobj->type == Torp ||
                curobj->type == Star ||
                curobj->type == Web) {
                curobj->flags |= STATIC;
                curobj->next->prev = curobj->prev;
                curobj->prev->next = curobj->next;
                curobj->prev = movers->prev;
                curobj->next = movers;
                movers->prev->next = curobj;
                movers->prev = curobj;
            }
        }
        if ((temp = occupant[y][x]) != NULL) {          /* already occupied? */
            if (!temp->contend) {
                if (temp->type == Torp) {
                    if (temp->image == '+')
                        blast[y][x] += 1250;
                    else if (temp->image == 'o' && (base||ent))
                        blast[y][x] += 500+super*20;
                    else if (temp->image == 'O' && (base||ent))
                        blast[y][x] += 5000+super*100;
                }
            }
            yblasted[y] |= 1;
            xblasted[x] |= 1;
            blasted = true;
            curobj->contend = temp;
            occupant[y][x] = curobj;
            switch (curobj->type) {
            case Enemy:
                if (numamoebas && curobj == nuke && temp->image == '+')
                    blast[y][x] += 80000;
                else if (temp->type == Enemy)
                    blast[y][x] += 10;
                else
                    goto defblast;
                break;
            case Crusher:
                if (curobj->velx)
                    blast[y][x] += 100000;
                else
                    goto defblast;
                break;
            case Torp:
                if (curobj->image == '+')
                    blast[y][x] += (temp==nuke ? 80000 : 1250);
                else if (curobj->image == 'o')
                    blast[y][x] += 500+super*20;
                else if (curobj->image == 'O')
                    blast[y][x] += 5000+super*100;
                goto defblast;
            case Star:
                if (temp == ent)
                    goto damshield;
                goto defblast;
            case Enterprise:
                if (temp->type == Star) {
              damshield:
                    if (!rand_mod(10)) {
                        if (!damflag[NOSHIELDS])
                            damage++;
                        if (damflag[NOSHIELDS] < 100)
                            damflag[NOSHIELDS] += rand_mod(smarts)/5+2;
                    }
                }
                goto defblast;
            default:
              defblast:
                blast[y][x] += rand_mod(751)+1;
                break;
            }
        }
        else {
            occupant[y][x] = curobj;
            if (curobj->image != ' ' &&
                (curobj->velx || curobj->vely ||
                 curobj->type == Torp || curobj->type == Web) ) {
                mvaddc(y+1, x*2, curobj->image);
            }
            if (curobj->type == Crusher && curobj->velx) {
                blast[y][x] += 100000;
                yblasted[y] |= 1;
                xblasted[x] |= 1;
                blasted = true;
            }
        }
    }
    if (blasted) {
        int minxblast = -1;
        int maxxblast = -2;
        long tmpblast;

        blasted = numamoebas;
        for (x=0; x<XSIZE; x++) {
            if (xblasted[x]) {
                xblasted[x] = 0;
                maxxblast = x;
                if (minxblast < 0)
                    minxblast = x;
            }
        }
        for (y=0; y<YSIZE; y++) {
            if (yblasted[y]) {
                yblasted[y] = 0;
                for (x=minxblast; x<=maxxblast; x++) {
                    tmpblast = blast[y][x];
                    if (numamoebas && amb[y][x] == '~') {
                        if ((temp = occupant[y][x]) != NULL) {
                            if (temp->image == '&')
                                tmpblast >>= 1;
                            else if (temp->type == Web)
                                tmpblast = 100000;
                            else
                                tmpblast += 50 + temp->energy/100;
                            if (tmpblast > 250 && !rand_mod(5+(inumstars>>4)))
                                modify_amoeba(y,x,1,'~',5);
                        }
                        xblasted[x] = 2;
                        yblasted[y] = 2;
                    }
                    if (tmpblast) {
                        OBJECT *biggie = 0;

                        blast[y][x] = 0;
                        temp = occupant[y][x];
                        if (tmpblast < 0) {
                            if (numamoebas && tmpblast < -1000000 &&
                                amb[y][x] == '~' && temp != nuke) {
                                amb[y][x] = ' ';
                                if (!temp)
                                    make_plink(y,x);
                                ambsize--;
                            }
                            tmpblast = 0;
                        }
                        if (temp) {
                            if ((!numamoebas || amb[y][x]==' ') &&
                              tmpblast < 100000)
                                make_plink(y,x);
                            for ( ;temp;
                              temp = curobj->contend,curobj->contend = 0){
                                curobj = temp;
                                switch (curobj->type) {
                                case Enterprise: {
                                    long tmp = curobj->energy;

                                    if (ent->energy>500 || apolloflag & 1)
                                        curobj->energy -= tmpblast /
                                           ((apolloflag & 1)
                                            ? 20
                                            : (5+abs(ent->velx)+abs(ent->vely))
                                               / ((damflag[NOSHIELDS]>>3)+1)+1);
                                    else
                                        curobj->energy -= tmpblast;
                                    if (rand_mod(1 + tmp - curobj->energy) > 100
                                        || ent->energy < (entmax>>1)) {
                                        if (debug & 128 ||
                                          (damage <= smarts/10 &&
                                           !rand_mod(6-smarts/20-massacre) )) {
                                            tmp = rand_mod(MAXDAMAGE);
                                            if (damflag[tmp]) {
                                                if (damflag[tmp] < 60)
                                                  damflag[tmp] += rand_mod(60);
                                            }
                                            else {
                                                damflag[tmp] =
                                                  rand_mod(smarts+10)+2;
                                                damage++;
                                            }
                                        }
                                    }
                                    break;
                                }
                                case Base:
                                    if (base->energy > 1000 || apolloflag & 2)
                                        curobj->energy -= tmpblast /
                                           ((apolloflag & 2)?20:5);
                                    else
                                        curobj->energy -= tmpblast;
                                    break;
                                case Crusher:
                                    if (tmpblast > 132767)
                                        curobj->energy -= (tmpblast - 100000);
                                    else if (tmpblast >= 100000) {
                                        curobj->energy += (tmpblast - 100000);
                                        if (curobj->energy > 32767)
                                            curobj->energy = 32767;
                                    }
                                    else        /* vulnerable while feeding */
                                        curobj->energy -= tmpblast;
                                    break;
                                case Enemy:
                                    curobj->energy -= tmpblast*10/enemshields;
                                    break;
                                default:
                                    curobj->energy -= tmpblast;
                                    break;
                                }
                                if (curobj->energy < 0) {       /* killed it? */
                                    switch (curobj->image) {
                                    case 'A':
                                        tmpblast = 100000;
                                        make_blast(y,x,8192L,1);
                                        numapollos = apolloflag = 0;
                                        numstars--;
                                        numenemies--;
                                        curscore += 5000;
                                        deados = 0;
                                        break;
                                    case 'E': case 'e': case 'C': case 'c':
                                        ent = 0;
                                        numents--;
                                        if (base)
                                            status = 2;
                                        else
                                            status = 3;
                                        deados = 0;
                                        break;
                                    case 'B': case 'b':
                                        base = 0;
                                        numbases--;
                                        if (ent)
                                            status = entmode;
                                        else
                                            status = 3;
                                        deados = 0;
                                        break;
                                    case '&': {
                                        int i, xxx, yyy;

                                        for (i = 0; i < YSIZE; i++)
                                            yblasted[i] &= 1;
                                        for (i = 0; i < XSIZE; i++)
                                            xblasted[i] &= 1;
                                        numamoebas = 0; /* ignore amb[][] now */
                                        for (yyy = 0; yyy < YSIZE; yyy++) {
                                            for (xxx = 0; xxx < XSIZE; xxx++) {
                                                if (amb[yyy][xxx] == '~' &&
                                                    !occupant[yyy][xxx]) {
                                                    mvaddch(yyy+1,xxx*2,' ');
                                                }
                                            }
                                        }
                                        numenemies--;
                                        curscore += 10000;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        deados = 0;
                                        break;
                                    }
                                    case '<': case '>': {
                                        int i;

                                        numenemies--;
                                        numcrushes = 0;
                                        curscore += 10000;
                                        if (curobj == movers)
                                            movers = curobj->next;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        deados = 0;

                                        tmpblast = 100000;
                                        make_blast(y,(x+XSIZE00)%XSIZE,10000L,0);
                                        if (curobj->image == '<') {
                                            for (i=XSIZE00; i<=XSIZE01; i++)
                                                make_blast(y,(x+i)%XSIZE,
                                                    10000L,0);
                                            for (i=XSIZE00; i<=XSIZE02; i++)
                                                make_blast(y,(x+i)%XSIZE,
                                                    10000L,0);
                                            make_blast(y,(x+XSIZE03)%XSIZE,
                                                10000L,1);
                                            for (i=XSIZE00; i<=XSIZE08; i++)
                                                make_blast(y,(x+i)%XSIZE,
                                                    10000L,0);
                                        }
                                        else {
                                            for (i=XSIZE00; i>=XSIZE99; i--)
                                                make_blast(y,(x+i)%XSIZE,
                                                    10000L,0);
                                            for (i=XSIZE00; i>=XSIZE98; i--)
                                                make_blast(y,(x+i)%XSIZE,
                                                    10000L,0);
                                            make_blast(y,(x+XSIZE97)%XSIZE,
                                                10000L,1);
                                            for (i=XSIZE00; i>=XSIZE92; i--)
                                                make_blast(y,(x+i)%XSIZE,
                                                    10000L,0);
                                        }
                                    }
                                    break;
                                    case 'K':
                                        numenemies--;
                                        curscore += curobj->mass;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        deados = 0;
                                        break;
                                    case 'T':
                                        numenemies--;
                                        curscore += curobj->mass*3/2;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        deados = 0;
                                        break;
                                    case 'R': case ' ': case 'P':
                                        numenemies--;
                                        if (curobj->flags & PIRATE)
                                            curscore += curobj->mass;
                                        else
                                            curscore += curobj->mass*3;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        deados = 0;
                                        break;
                                    case 'G':
                                        numenemies--;
                                        numgorns--;
                                        tmpblast = 100000;
                                        if (madgorns)
                                            curscore += curobj->mass/2;
                                        else
                                            curscore += curobj->mass*2;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        {
                                            int xxx,yyy;

                                            for (xxx = -1; xxx<=1; xxx++)
                                                for (yyy = -1; yyy<=1; yyy++)
                                                    if (rand_mod(2+massacre))
                                                        fire_torp(curobj,
                                                            yyy,xxx);
                                        }
                                        deados = 0;
                                        break;
                                    case '@':
                                        numinhab--;
                                        /* FALL THROUGH */
                                    case '*':
                                        banging = true;
                                        numstars--;
                                        break;
                                    case '|': case '-': case '/': case '\\':
                                        tmpblast = 100000;
                                        make_blast(y,x,curobj->mass,1);
                                        banging = true;
                                        deados = 0;
                                        break;
                                    case 'x':
                                        curscore += 10;
                                        deados = 0;
                                        break;
                                    case 'X':
                                        curscore += 100;
                                        numxes--;
                                        deados = 0;
                                        break;
                                    case '0':
                                        curscore += 35;
                                        numos--;
                                        deados += 3;
                                        break;
                                    case 'o':
                                        curscore += 100;
                                        numos--;
                                        deados++;
                                        break;
                                    case 'O':
                                        curscore += 200;
                                        numos--;
                                        deados += 2;
                                        break;
                                    case 'M':
                                        deadmudds++;
                                        inumfriends--;
                                        numfriends--;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        break;
                                    case 'Q': case 'W': case 'Y': case 'U':
                                    case 'I': case 'S': case 'D': case 'H':
                                    case 'J': case 'L': case 'Z': case 'V':
                                    case 'F':
                                        numfriends--;
                                        if (curobj == enemies)
                                            enemies = curobj->next;
                                        if (inumfriends < 10)
                                            madfriends += 500;
                                        else
                                            madfriends += 10000/inumfriends;
                                        break;
                                    }
                                    if (tmpblast < 100000)
                                        make_blast(y,x,curobj->mass,1);
                                    unmake_object(curobj);
                                }
                                else {          /* didn't kill anything */
                                    if (!biggie)
                                        biggie = curobj;
                                    else {
                                        if (biggie->mass > curobj->mass)
                                            bounce(curobj);
                                        else {
                                            bounce(biggie);
                                            biggie = curobj;
                                        }
                                    }
                                }
                            }
                            if (biggie) {
                                occupant[y][x] = biggie;
                                if (numamoebas && biggie->image == ' ')
                                    mvaddch(y+1,x*2, amb[y][x]);
                                else
                                    mvaddch(y+1,x*2, biggie->image);
                            }
                            else {
                                occupant[y][x] = 0;
                                mvaddch(y+1, x*2, numamoebas ? amb[y][x] : ' ');
                            }
                        }
                    }
                }
            }
        }
    }
    do_bangs();
    if (numcrushes && movers->type == Crusher)
        movers->vely = 0;
    if ((curobj = base) != NULL) {
        char ch;

        curobj->velx = 0;
        curobj->vely = 0;
        curobj->energy += 25*lookaround(curobj->posy,curobj->posx,Star);
        if (curobj->energy > basemax)
            curobj->energy = basemax;
        if (curobj->energy >= 1000)
            ch = 'B';
        else
            ch = 'b';
        if (ch != curobj->image) {
            setimage(curobj, ch);
        }
    }
    if ((curobj = ent) != NULL) {
        char ch;

        if (entmode == 0) {
            curobj->velx = 0;
            curobj->vely = 0;
        }
        if (base && !cloaking && !curobj->velx && !curobj->vely &&
          lookfor(curobj->posy,curobj->posx,Base)) {
            int tmp;

#ifdef lint
            tmp = 0;
#else
            tmp = (int) (base->energy - 1000 < entmax - curobj->energy ?
                         base->energy - 1000 : entmax - curobj->energy);
#endif
            if (tmp < 0)
                tmp = 0;
            curobj->energy += tmp;
            base->energy -= tmp;
            tmp = (btorp < 50 - etorp ?
                   btorp : 50 - etorp);
            etorp += tmp;
            btorp -= tmp;
            if (damage) {
                tmp = rand_mod(MAXDAMAGE);
                if (damflag[tmp] > 5) {
                    damflag[tmp] = rand_mod(5)+1;
                }
            }
        }
        if (curobj->energy >= 500 && (!damage || !damflag[NOSHIELDS]))
            ch = cloaked?'C':'E';
        else
            ch = cloaked?'c':'e';
        if (ch != curobj->image) {
            setimage(curobj, ch);
        }
    }
}

int
lookaround(int y, int x, char what)
{
    OBJECT *obj;
    int count=0;
    int xp;
    int xm;

    if ((obj=occupant[y][xp=(x+XSIZE01)%XSIZE])&&obj->type == what) /* 0, 1 */
        count++;
    if ((obj=occupant[y][xm=(x+XSIZE99)%XSIZE])&&obj->type == what) /* 0, -1 */
        count++;
    if ((obj=occupant[y=(y+YSIZE99)%YSIZE][xp])&&obj->type == what) /* -1, 1 */
        count++;
    if ((obj=occupant[y][x])&&obj->type == what)                    /* -1, 0 */
        count++;
    if ((obj=occupant[y][xm])&&obj->type == what)                   /* -1, -1 */
        count++;
    if ((obj=occupant[y=(y+2)%YSIZE][xp])&&obj->type == what)       /* 1, 1 */
        count++;
    if ((obj=occupant[y][x])&&obj->type == what)                    /* 1, 0 */
        count++;
    if ((obj=occupant[y][xm])&&obj->type == what)                   /* 1, -1 */
        count++;
    return (count);
}

int
lookfor(int y, int x, char what)
{
    OBJECT *obj;
    int xp;
    int xm;

    if (((obj=occupant[y][xp=(x+XSIZE01)%XSIZE])&&obj->type == what)||/* 0, 1 */
        ((obj=occupant[y][xm=(x+XSIZE99)%XSIZE])&&obj->type == what)||/* 0, -1 */
        ((obj=occupant[y=(y+YSIZE99)%YSIZE][xp])&&obj->type == what)||/* -1, 1 */
        ((obj=occupant[y][x])&&obj->type == what)                   ||/* -1, 0 */
        ((obj=occupant[y][xm])&&obj->type == what)                  ||/* -1,-1 */
        ((obj=occupant[y=(y+2)%YSIZE][xp])&&obj->type == what)      ||/* 1, 1 */
        ((obj=occupant[y][x])&&obj->type == what)                   ||/* 1, 0 */
        ((obj=occupant[y][xm])&&obj->type == what))                   /* 1, -1 */
        return(1);
    return (0);
}

OBJECT*
lookimg(int y, int x, char what)
{
    OBJECT *obj;
    int xp;
    int xm;

    if (((obj=occupant[y][xp=(x+XSIZE01)%XSIZE])&&obj->image==what)||/* 0, 1 */
        ((obj=occupant[y][xm=(x+XSIZE99)%XSIZE])&&obj->image==what)||/* 0, -1 */
        ((obj=occupant[y=(y+YSIZE99)%YSIZE][xp])&&obj->image==what)||/* -1, 1 */
        ((obj=occupant[y][x])&&obj->image==what)                   ||/* -1, 0 */
        ((obj=occupant[y][xm])&&obj->image==what)                  ||/* -1,-1 */
        ((obj=occupant[y=(y+2)%YSIZE][xp])&&obj->image==what)      ||/* 1, 1 */
        ((obj=occupant[y][x])&&obj->image==what)                   ||/* 1, 0 */
        ((obj=occupant[y][xm])&&obj->image==what))                   /* 1, -1 */
        return obj;
    return NULL;
}