root/games/warp/weapon.c
/* Header: weapon.c,v 7.0.1.2 86/10/20 14:36:33 lwall Exp */

/* Log: weapon.c,v
 * Revision 7.0.1.2  86/10/20  14:36:33  lwall
 * Picked some lint.
 *
 * Revision 7.0.1.1  86/10/16  10:54:42  lwall
 * Added Damage.  Fixed random bugs.
 *
 * Revision 7.0  86/10/08  15:18:08  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 "score.h"
#include "sig.h"
#include "term.h"
#include "them.h"
#include "us.h"
#include "util.h"
#include "INTERN.h"
#include "weapon.h"

void
weapon_init(void)
{
    ;
}

void
fire_torp(OBJECT *from, int ydir, int xdir)
{
    OBJECT *to;

    if (from->type == Enemy ||
       (from == ent && etorp > 0) ||
       (from == base && btorp > 0)) {
        to = occupant[(from->posy+from->vely+ydir+YSIZE00)%YSIZE]
                     [(from->posx+from->velx+xdir+XSIZE00)%XSIZE];
        if (from->type != Enemy || !to || to->vely || to->velx) {
            if (from->type != Enemy &&
                 (to = isatorp[from==base][ydir+1][xdir+1])) {
                to->vely += ydir;
                to->velx += xdir;
            }
            else {
                if (from == ent) {
                    to = make_object(Torp, '+', from->posy,from->posx,
                        from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
                    aretorps++;
                    isatorp[0][ydir+1][xdir+1] = to;
                    etorp--;
                }
                else if (from == base) {
                    to = make_object(Torp, '+', from->posy,from->posx,
                        from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
                    aretorps++;
                    isatorp[1][ydir+1][xdir+1] = to;
                    btorp--;
                }
                else if (from->image == 'G') {
                    numos++;
                    to = make_object(Torp, 'o', from->posy,from->posx,
                        from->vely+ydir,from->velx+xdir, 100L, 1L,&root);
                    if (madgorns) {
                        possiblescore += 35;
                        to->image = '0';
                        to->mass = 2000;
                        to->energy = 2000;
                    }
                    else if (rand_mod(120)+10 > smarts)
                        possiblescore += 100;
                    else {
                        possiblescore += 200;
                        to->image = 'O';
                    }
                }
                else {
                    to = make_object(Torp, 'x', from->posy,from->posx,
                        from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
                    if (rand_mod(160)+10 > smarts)
                        possiblescore += 10;
                    else {
                        possiblescore += 100;
                        to->image = 'X';
                        to->mass = 1000+super*20;
                        numxes++;
                    }
                }
            }
        }
    }
}

void
attack(OBJECT *attackee)
{
    int dx;
    int dy;
    int curx;
    int cury;
    int prob;
    OBJECT *obj;
    bool torps;
    bool webnear = false;
    bool thru_stars;
    int nukey;
    int nukex;
    int nukedist;

    if (attackee) {
        if (attackee == nuke) {
            if (amb[attackee->posy][attackee->posx] != '~')
                return;
            nukey = nukex = 0;
            nukedist = 100;
        }
        for (dx= -1; dx<=1 ; dx++) {
            for (dy= -1; dy<=1; dy++) {
                if (dx||dy) {
                    cury = attackee->posy;
                    curx = attackee->posx;
                    torps = thru_stars = false;
                    if (massacre || madgorns || !rand_mod(53-super) )
                        webnear += rand_mod(2);
                    else
                        webnear = false;
                    for (prob = scandist;prob;prob--) {
                        cury = (cury + dy + YSIZE00) % YSIZE;
                        curx = (curx + dx + XSIZE00) % XSIZE;
                        if ((obj = occupant[cury][curx]) != NULL) {
                            switch (obj->image) {
                            case 'P': case 'K': case 'R': case ' ':
                pot_shot:
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/40+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                }
                                if (rand_mod(51 - sm50) <= prob) {
                                    switch (obj->strategy||thru_stars?0:
                                          rand_mod(ent?4:2)) {
                                    case 1: case 2:
                                        if (-dy + attackee->vely == obj->vely
                                         && -dx + attackee->velx == obj->velx)
                                            fire_torp(obj,
                                             -dy + attackee->vely,
                                             -dx + attackee->velx);
                                        else
                                            fire_torp(obj,
                                             -dy + attackee->vely - obj->vely,
                                             -dx + attackee->velx - obj->velx);
                                        if (obj->image == ' ')
                                            setimage(obj,
                                              obj->flags & PIRATE ? 'P' : 'R');
                                        break;
                                    case 3: {
                                        int newspeed =
                                            rand_mod(prob<5&&smarts>70?4:3)-1;

                                        obj->vely = -dy * newspeed;
                                        obj->velx = -dx * newspeed;
                                        if (newspeed >= 0 &&
                                            !rand_mod(82-sm80)) {
                                            obj->vely += attackee->vely;
                                            obj->velx += attackee->velx;
                                        }
                                        break;
                                    }
                                    case 0:
                                        if (!torps && obj->energy > 1000) {
                                            fire_phaser(obj, -dy, -dx);
                                            if (smarts > 40 &&
                                               (scandist-prob > 5
                                                || attackee==base) &&
                                               (massacre || obj->strategy ||
                                                rand_mod(2)))
                                                while (rand_mod(2))
                                                    fire_phaser(obj, -dy, -dx);
                                            if (obj->image == ' ')
                                                setimage(obj,
                                                  obj->flags&PIRATE ? 'P':'R');
                                        }
                                        if (obj->strategy) {
                                            obj->velx = obj->vely = 0;
                                            if (obj->energy < 1000 ||
                                                  bvely || bvelx)
                                                obj->strategy = 0;
                                        }
                                        else if ((attackee==base ||
                                                  (cloaking && attackee==ent)
                                                 ) &&
                                                 scandist-prob > 5 &&
                                                 !(rand_mod(
                                                   ent?antibase*2:antibase)) )
                                            obj->strategy = 1;
                                        break;
                                    }
                                }
                                goto bombout;
                            case 'G':
                                if (thru_stars && obj->strategy < 7)
                                    goto bombout;
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/40+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                    goto bombout;
                                }
                                if (obj->strategy) {
                                    if (madgorns || !rand_mod(4)) {
                                        obj->vely = attackee->vely;
                                        obj->velx = attackee->velx;
                                    }
                                    obj->strategy += (!torps && deados > 10);
                                    if (obj->strategy > 4)
                                        madgorns = true;
                                    if (!torps && obj->strategy > 5) {
                                        do {
                                            fire_phaser(obj, -dy, -dx);
                                        } while (rand_mod(2));
                                    }
                                }
                                else if (numgorns >= numenemies-1 &&
                                    deados > 15+numgorns*5)
                                    obj->strategy = 1;
                                if (madgorns || rand_mod(51 - sm50) <= prob) {
                                    if (-dy + attackee->vely == obj->vely
                                     && -dx + attackee->velx == obj->velx)
                                        fire_torp(obj,
                                         -dy + attackee->vely,
                                         -dx + attackee->velx);
                                    else
                                        fire_torp(obj,
                                         -dy + attackee->vely - obj->vely,
                                         -dx + attackee->velx - obj->velx);
                                }
                                goto bombout;
                            case 'T':
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/40+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                }
                                if (thru_stars)
                                    goto bombout;
                                if (webnear && scandist-prob > 5) {
                                    if (massacre || rand_mod(50) < super) {
                                        if (!torps && obj->energy > 1000) {
                                            fire_phaser(obj, -dy, -dx);
                                            while (!rand_mod(57-sm55))
                                                fire_phaser(obj, -dy, -dx);
                                        }
                                    }
                                }
                                goto bombout;
                            case 'C': case 'c':
                                if (thru_stars)
                                    goto bombout;
                                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 'M': case 'F':
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/40+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                    if (rand_mod(2))
                                        goto pot_shot;
                                }
                                if (madfriends > 1000) {
                                    madfriends -= 200;
                                    goto pot_shot;
                                }
                                /* FALL THROUGH */
                            case '+':
                                if (attackee == nuke) {
                                    if (smarts > 70) {
                                        if (
                                         (obj->posx + obj->velx + XSIZE00)%XSIZE
                                            == attackee->posx &&
                                         (obj->posy + obj->vely + YSIZE00)%YSIZE
                                            == attackee->posy ) {
                                            tract(nuke,dy,dx,-1);
                                        }
                                        else
                                            while (!rand_mod(82-sm80))
                                                tract(nuke,dy,dx,-1);
                                    }
                                    else if (smarts > 60 ||
                                      rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/20+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                }
                                torps = false;
                                thru_stars = false;
                                break;
                            case '|': case '-': case '/': case '\\':
                                if (thru_stars)
                                    goto bombout;
                                webnear = (scandist-prob < 3);
                                torps = false;
                                break;
                            case 'x':
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/20+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                }
                                if (thru_stars)
                                    goto bombout;
                                torps = true;
                                break;
                            case 'o': case 'O': case '0':
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/20+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                }
                                if (thru_stars)
                                    goto bombout;
                                torps = true;
                                if (rand_mod(99+3*scandist) < smarts+3*prob) {
                                    obj->vely = -dy + attackee->vely;
                                    obj->velx = -dx + attackee->velx;
                                    if (obj->flags & STATIC) {/* not a mover? */
                                        obj->flags &= ~STATIC;
                                        obj->prev->next = obj->next;
                                        obj->next->prev = obj->prev;
                                        root.prev->next = obj;
                                        obj->prev = root.prev;
                                        root.prev = obj;
                                        obj->next = &root;
                                    }
                                }
                                if (obj->image != '0')
                                    break;
                            /*FALLTHROUGH*/
                            case 'X':
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/20+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                }
                                torps = true;
                                if (thru_stars)
                                    goto bombout;
                                if (prob == scandist) {
                                    int y, x;

                                    blast[y=(obj->posy+obj->vely+YSIZE00)%YSIZE]
                                         [x=(obj->posx+obj->velx+XSIZE00)%XSIZE]
                                      += (obj->image == '0' ? 2000 : 200);
                                    yblasted[y] |= 1;
                                    xblasted[x] |= 1;
                                    blasted = true;
                                }
                                break;
                            case 'A':
                                if (attackee != nuke) {
                                    if (scandist-prob>1 && !rand_mod(51-super))
                                        tract(obj,-dy,-dx,1);
                                }
                                /* FALL THROUGH */
                            case '*': case '@':
                                if (attackee == nuke) {
                                    if (amb[cury][curx] != '~') {
                                        if (scandist-prob < nukedist) {
                                            nukedist = scandist-prob;
                                            nukey = dy; /* nearest food in */
                                            nukex = dx; /*   this direction */
                                        }
                                        if (smarts > 55 && scandist-prob > 8) {
                                            if (rand_mod(30+scandist-prob) <
                                              rand_mod(smarts/20+1))
                                                tract(nuke,dy,dx,1);
                                        }
                                    }
                                    else if (obj->vely || obj->velx) {
                                        tract(nuke,dy,dx,1); /* for looks */
                                        obj->vely = obj->velx = 0;
                                    }
                                }
                                if (!thru_stars) {
                                    if (rand_mod(97-sm95))
                                        goto bombout;
                                    else
                                        thru_stars = true;
                                }
                                break;
                            case '<': case '>':
                                if (attackee == nuke) {
                                    if ((!dy && scandist-prob < 8) ||
                                      rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/20+1) ) {
                                        nuke->mass += 10000;
                                        tract(nuke,dy,dx,-1);
                                        nuke->mass -= 10000;
                                    }
                                }
                                goto bombout;
                            case 'E': case 'B':
                                if (attackee == nuke) {
                                    if (rand_mod(2+scandist-prob) <
                                      rand_mod(smarts/40+1))
                                        tract(nuke,dy,dx,rand_mod(3)?1:-1);
                                }
                                goto bombout;
                            default:
                                goto bombout;
                            }
                        }
                        else {
                            if (thru_stars)
                                goto bombout;
                        }
                    }
bombout:            ; /* end of loop */
                }
            }
        }
        if (attackee == nuke && nukedist < 100) {/* aim amoeba at nearest */
            if (nukey < 0)                      /*   free star */
                nukey = 2;
            if (nukex < 0)
                nukex = 2;
            nuke->strategy = nukey + (nukex << 2);
        }
    }
}

void
fire_phaser(OBJECT *obj, int dy, int dx)
{
    int y;
    int x;
    int skipping;
    int size=5000;
    int decr = 50, oldy, oldx;
    static char curchar[] = "@* ";

    if (obj == ent)
        decr = 100;
    else if (obj == base) {
        decr = 1000;
        size = 200;
    }
    if (!dy)
        curchar[2] = '-';
    else if (!dx)
        curchar[2] = '!';
    else if (dy == dx)
        curchar[2] = '\\';
    else
        curchar[2] = '/';
    if (obj->energy >= decr) {
        obj->energy -= decr;
        for (
          /* initialize */
          skipping = (obj != base),
          y = (obj->posy+(obj==base?dy*2:dy)+YSIZE00)%YSIZE,
          x = (obj->posx+(obj==base?dx*2:dx)+XSIZE00)%XSIZE;
          /* while */
          size && (!occupant[y][x]||(skipping && occupant[y][x]->type==Star));
          /* at end of loop */
          y = (y+dy+YSIZE00) % YSIZE,
          x = (x+dx+XSIZE00) % XSIZE,
          size = size * 3 / 4 ) {
            move(y+1,x*2,0);
            beg_qwrite();
            if (obj == base || obj->image == 'T') {
                *filler = '@';
                qwrite();
                *filler = '#';
                qwrite();
                *filler = '~';
                qwrite();
                *filler = '%';
                qwrite();
                *filler = ':';
                qwrite();
                *filler = '@';
            }
            else {
                *filler = size >= 500 ?
                          *curchar : (size >= 50 ?
                                     curchar[1] :
                                     curchar[2]);
            }
            qwrite();
            if (occupant[y][x])
                qaddc(occupant[y][x]->image);
            else {
                if (numamoebas)
                    qaddc(amb[y][x]);
                else
                    qaddspace();
                if (skipping)
                    skipping = 0;
            }
            end_qwrite();
        }
        if (size) {
            char img;

            assert(occupant[y][x]);
            img = occupant[y][x]->image;
            if (occupant[y][x]->type == Crusher) {
                if (dy)
                    return;
                if (dx==(img == '<' ? 1 : -1) ) {
                    occupant[y][x]->image =
                        (occupant[y][x]->velx *= -1) < 0 ? '>' : '<';
                    return;
                }
            }
            else if (occupant[y][x]->flags & FRIENDLY)
                madfriends += 200;
            if (numamoebas && amb[y][x] == '~' && smarts % 3 &&
                (smarts > 70 || rand_mod(smarts) > rand_mod(20)) ) {
                if (size > 10000)
                    modify_amoeba(y,x,1,'~',10);
                else if (size > 1000)
                    modify_amoeba(y,x,1,'~',7);
                else if (size > 50)
                    modify_amoeba(y,x,1,'~',5);
                else
                    modify_amoeba(y,x,1,'~',2);
                if (occupant[y][x] == nuke) {
                    nuke->strategy = rand_mod(30);
                    nuke->flags |= COUNTDOWN;
                }
                return;
            }
            else {
                move(y+1,x*2,0);
                beg_qwrite();
                if (img == ' ') {
                    *filler = occupant[y][x]->flags & PIRATE ? 'P' : 'R';
                    occupant[y][x]->image = *filler;
                    occupant[y][x]->strategy = 0;
                    qwrite();
                    qwrite();
                }
                else if (img == 'C' || img == 'c') {
                    cloaked = 0;
                    img += 2;
                    occupant[y][x]->image = img;
                    *filler = img;
                    qwrite();
                    qwrite();
                }
                else if (img == 'K' && size > 50)
                    occupant[y][x]->strategy = 0;
                *filler = '@';
                qwrite();
                *filler = '#';
                qwrite();
                *filler = '@';
                qwrite();
                *filler = '#';
                qwrite();
                *filler = '@';
                qwrite();
                qaddc(img);
                end_qwrite();
                oldy = y;
                oldx = x;
                y = (occupant[oldy][oldx]->posy + occupant[oldy][oldx]->vely +
                        YSIZE00) % YSIZE;
                x = (occupant[oldy][oldx]->posx + occupant[oldy][oldx]->velx +
                        XSIZE00) % XSIZE;
                if (occupant[y][x] && occupant[y][x]->type == Star) {
                    y = occupant[oldy][oldx]->posy;
                    x = occupant[oldy][oldx]->posx;
                }
                if (obj==base)
                    blast[y][x] += size>50 ? 15000 : (size>15 ? 1500 : 150);
                else if (obj==ent)
                    blast[y][x] += size*4;
                else if (obj->image=='T')
                    blast[y][x] += 15000;
                else
                    blast[y][x] += size*smarts/25;
                yblasted[y] |= 1;
                xblasted[x] |= 1;
                blasted = true;
            }
        }
    }
}

int
tract(OBJECT *obj, int dy, int dx, int to_or_fro)
{
    int y;
    int x;
    int size=10;
    static char ch;
    OBJECT *tractee;

    if (!dy)
        ch = '|';
    else if (!dx)
        ch = '-';
    else if (dy == dx)
        ch = '/';
    else
        ch = '\\';
    {
        for (
          y = (obj->posy+dy+YSIZE00)%YSIZE,
          x = (obj->posx+dx+XSIZE00)%XSIZE;
          size && (!occupant[y][x]);
          y = (y+dy+YSIZE00) % YSIZE, x = (x+dx+XSIZE00) % XSIZE, size--) {
            move(y+1,x*2,0);
            beg_qwrite();
            *filler = ch;
            qwrite();
            qwrite();
            if (numamoebas)
                qaddch(amb[y][x]);
            else
                qaddspace();
            end_qwrite();
        }
        tractee = occupant[y][x];
        if (size) {
            assert(tractee);
            if (numamoebas && obj != nuke && amb[y][x] == '~') {
                if (to_or_fro > 0)
                    modify_amoeba(y,x,2,'~',size);
                else
                    modify_amoeba(y,x,1,' ',size);
            }
            if (tractee->type != Web &&
                (tractee->mass < obj->mass * 5 ||
                 (tractee->type == Crusher && !dx) ) ) {
                if (tractee == ent) {
                    evely -= dy * to_or_fro;
                    evelx -= dx * to_or_fro;
                }
                else if (tractee == base) {
                    bvely -= dy * to_or_fro;
                    bvelx -= dx * to_or_fro;
                }
                else {
                    tractee->vely -= dy * to_or_fro;
                    tractee->velx -= dx * to_or_fro;
                }
                if (tractee->type == Torp ||
                    tractee->type == Star) {
                    if (tractee->flags & STATIC) {  /* not a mover? */
                        tractee->flags &= ~STATIC;
                        tractee->prev->next = tractee->next;
                        tractee->next->prev = tractee->prev;
                        root.prev->next = tractee;
                        tractee->prev = root.prev;
                        root.prev = tractee;
                        tractee->next = &root;
                    }
                }
            }
            else if (tractee->type == Crusher && !dy &&
              dx==(tractee->image == '<' ? 1 : -1) ) {
                setimage(tractee, (tractee->velx *= -1) < 0 ? '>' : '<');
            }
            if (tractee->mass * 5 > obj->mass)
                return(1);
        }
    }
    return(0);
}