root/games/sail/dr_1.c
/*      $NetBSD: dr_1.c,v 1.27 2009/03/14 22:52:52 dholland Exp $       */

/*
 * Copyright (c) 1983, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)dr_1.c      8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: dr_1.c,v 1.27 2009/03/14 22:52:52 dholland Exp $");
#endif
#endif /* not lint */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extern.h"
#include "driver.h"

static int fightitout(struct ship *, struct ship *, int);

void
unfoul(void)
{
        struct ship *sp;
        struct ship *to;
        int nat;
        int i;

        foreachship(sp) {
                if (sp->file->captain[0])
                        continue;
                nat = capship(sp)->nationality;
                foreachship(to) {
                        if (nat != capship(to)->nationality &&
                            !is_toughmelee(sp, to, 0, 0))
                                continue;
                        for (i = fouled2(sp, to); --i >= 0;)
                                if (dieroll() <= 2)
                                        cleanfoul(sp, to, 0);
                }
        }
}

void
boardcomp(void)
{
        int crew[3];
        struct ship *sp, *sq;

        foreachship(sp) {
                if (*sp->file->captain)
                        continue;
                if (sp->file->dir == 0)
                        continue;
                if (sp->file->struck || sp->file->captured != 0)
                        continue;
                if (!snagged(sp))
                        continue;
                crew[0] = sp->specs->crew1 != 0;
                crew[1] = sp->specs->crew2 != 0;
                crew[2] = sp->specs->crew3 != 0;
                foreachship(sq) {
                        if (!Xsnagged2(sp, sq))
                                continue;
                        if (meleeing(sp, sq))
                                continue;
                        if (!sq->file->dir
                                || sp->nationality == capship(sq)->nationality)
                                continue;
                        switch (sp->specs->class - sq->specs->class) {
                        case -3: case -4: case -5:
                                if (crew[0]) {
                                        /* OBP */
                                        sendbp(sp, sq, crew[0]*100, 0);
                                        crew[0] = 0;
                                } else if (crew[1]) {
                                        /* OBP */
                                        sendbp(sp, sq, crew[1]*10, 0);
                                        crew[1] = 0;
                                }
                                break;
                        case -2:
                                if (crew[0] || crew[1]) {
                                        /* OBP */
                                        sendbp(sp, sq, crew[0]*100+crew[1]*10,
                                                0);
                                        crew[0] = crew[1] = 0;
                                }
                                break;
                        case -1: case 0: case 1:
                                if (crew[0]) {
                                        /* OBP */
                                        sendbp(sp, sq, crew[0]*100+crew[1]*10,
                                                0);
                                        crew[0] = crew[1] = 0;
                                }
                                break;
                        case 2: case 3: case 4: case 5:
                                /* OBP */
                                sendbp(sp, sq, crew[0]*100+crew[1]*10+crew[2],
                                        0);
                                crew[0] = crew[1] = crew[2] = 0;
                                break;
                        }
                }
        }
}

static int
fightitout(struct ship *from, struct ship *to, int key)
{
        struct ship *fromcap, *tocap;
        int crewfrom[3], crewto[3], menfrom, mento;
        int pcto, pcfrom, fromstrength, strengthto, frominjured, toinjured;
        int topoints;
        int indx, totalfrom = 0, totalto = 0;
        int count;
        char message[60];

        menfrom = mensent(from, to, crewfrom, &fromcap, &pcfrom, key);
        mento = mensent(to, from, crewto, &tocap, &pcto, 0);
        if (fromcap == 0)
                fromcap = from;
        if (tocap == 0)
                tocap = to;
        if (key) {
                if (!menfrom) {          /* if crew surprised */
                        if (fromcap == from)
                                menfrom = from->specs->crew1
                                        + from->specs->crew2
                                        + from->specs->crew3;
                        else
                                menfrom = from->file->pcrew;
                } else {
                        menfrom *= 2;   /* DBP's fight at an advantage */
                }
        }
        fromstrength = menfrom * fromcap->specs->qual;
        strengthto = mento * tocap->specs->qual;
        for (count = 0;
             ((fromstrength < strengthto * 3 && strengthto < fromstrength * 3)
              || fromstrength == -1) && count < 4;
             count++) {
                indx = fromstrength/10;
                if (indx > 8)
                        indx = 8;
                toinjured = MT[indx][2 - dieroll() / 3];
                totalto += toinjured;
                indx = strengthto/10;
                if (indx > 8)
                        indx = 8;
                frominjured = MT[indx][2 - dieroll() / 3];
                totalfrom += frominjured;
                menfrom -= frominjured;
                mento -= toinjured;
                fromstrength = menfrom * fromcap->specs->qual;
                strengthto = mento * tocap->specs->qual;
        }
        if (fromstrength >= strengthto * 3 || count == 4) {
                unboard(to, from, 0);
                subtract(from, fromcap, totalfrom, crewfrom, pcfrom);
                subtract(to, tocap, totalto, crewto, pcto);
                makemsg(from, "boarders from %s repelled", to->shipname);
                snprintf(message, sizeof(message),
                        "killed in melee: %d.  %s: %d",
                        totalto, from->shipname, totalfrom);
                send_signal(to, message);
                if (key)
                        return 1;
        } else if (strengthto >= fromstrength * 3) {
                unboard(from, to, 0);
                subtract(from, fromcap, totalfrom, crewfrom, pcfrom);
                subtract(to, tocap, totalto, crewto, pcto);
                if (key) {
                        if (fromcap != from)
                                send_points(fromcap,
                                        fromcap->file->points -
                                                from->file->struck
                                                ? from->specs->pts
                                                : 2 * from->specs->pts);

                        send_captured(from, to->file->index);
                        topoints = 2 * from->specs->pts + to->file->points;
                        if (from->file->struck)
                                topoints -= from->specs->pts;
                        send_points(to, topoints);
                        mento = crewto[0] ? crewto[0] : crewto[1];
                        if (mento) {
                                subtract(to, tocap, mento, crewto, pcto);
                                subtract(from, to, - mento, crewfrom, 0);
                        }
                        snprintf(message, sizeof(message),
                                "captured by the %s!", to->shipname);
                        send_signal(from, message);
                        snprintf(message, sizeof(message),
                                "killed in melee: %d.  %s: %d",
                                totalto, from->shipname, totalfrom);
                        send_signal(to, message);
                        mento = 0;
                        return 0;
                }
        }
        return 0;
}

void
resolve(void)
{
        int thwart;
        struct ship *sp, *sq;

        foreachship(sp) {
                if (sp->file->dir == 0)
                        continue;
                for (sq = sp + 1; sq < ls; sq++)
                        if (sq->file->dir && meleeing(sp, sq) &&
                            meleeing(sq, sp))
                                fightitout(sp, sq, 0);
                thwart = 2;
                foreachship(sq) {
                        if (sq->file->dir && meleeing(sq, sp))
                                thwart = fightitout(sp, sq, 1);
                        if (!thwart)
                                break;
                }
                if (!thwart) {
                        foreachship(sq) {
                                if (sq->file->dir && meleeing(sq, sp))
                                        unboard(sq, sp, 0);
                                unboard(sp, sq, 0);
                        }
                        unboard(sp, sp, 1);
                } else if (thwart == 2)
                        unboard(sp, sp, 1);
        }
}

void
compcombat(void)
{
        int n;
        struct ship *sp;
        struct ship *closest;
        int crew[3], men = 0, target, temp;
        int r, guns, ready, load, car;
        int indx, rakehim, sternrake;
        int shootat, hit;

        foreachship(sp) {
                if (sp->file->captain[0] || sp->file->dir == 0)
                        continue;
                crew[0] = sp->specs->crew1;
                crew[1] = sp->specs->crew2;
                crew[2] = sp->specs->crew3;
                for (n = 0; n < 3; n++) {
                        if (sp->file->OBP[n].turnsent)
                                men += sp->file->OBP[n].mensent;
                }
                for (n = 0; n < 3; n++) {
                        if (sp->file->DBP[n].turnsent)
                                men += sp->file->DBP[n].mensent;
                }
                if (men) {
                        crew[0] = men/100 ? 0 : crew[0] != 0;
                        crew[1] = (men%100)/10 ? 0 : crew[1] != 0;
                        crew[2] = men%10 ? 0 : crew[2] != 0;
                }
                for (r = 0; r < 2; r++) {
                        if (!crew[2])
                                continue;
                        if (sp->file->struck)
                                continue;
                        if (r) {
                                ready = sp->file->readyR;
                                guns = sp->specs->gunR;
                                car = sp->specs->carR;
                        } else {
                                ready = sp->file->readyL;
                                guns = sp->specs->gunL;
                                car = sp->specs->carL;
                        }
                        if (!guns && !car)
                                continue;
                        if ((ready & R_LOADED) == 0)
                                continue;
                        closest = closestenemy(sp, r ? 'r' : 'l', 0);
                        if (closest == 0)
                                continue;
                        if (range(closest, sp) >
                            range(sp, closestenemy(sp, r ? 'r' : 'l', 1)))
                                continue;
                        if (closest->file->struck)
                                continue;
                        target = range(sp, closest);
                        if (target > 10)
                                continue;
                        if (!guns && target >= 3)
                                continue;
                        load = L_ROUND;
                        if (target == 1 && sp->file->loadwith == L_GRAPE)
                                load = L_GRAPE;
                        if (target <= 3 && closest->file->FS)
                                load = L_CHAIN;
                        if (target == 1 && load != L_GRAPE)
                                load = L_DOUBLE;
                        if (load > L_CHAIN && target < 6)
                                shootat = HULL;
                        else
                                shootat = RIGGING;
                        rakehim = gunsbear(sp, closest)
                                && !gunsbear(closest, sp);
                        temp = portside(closest, sp, 1)
                                - closest->file->dir + 1;
                        if (temp < 1)
                                temp += 8;
                        if (temp > 8)
                                temp -= 8;
                        sternrake = temp > 4 && temp < 6;
                        indx = guns;
                        if (target < 3)
                                indx += car;
                        indx = (indx - 1) / 3;
                        indx = indx > 8 ? 8 : indx;
                        if (!rakehim)
                                hit = HDT[indx][target-1];
                        else
                                hit = HDTrake[indx][target-1];
                        if (rakehim && sternrake)
                                hit++;
                        hit += QUAL[indx][capship(sp)->specs->qual - 1];
                        for (n = 0; n < 3 && sp->file->captured == 0; n++) {
                                if (!crew[n]) {
                                        if (indx <= 5)
                                                hit--;
                                        else
                                                hit -= 2;
                                }
                        }
                        if (ready & R_INITIAL) {
                                if (!r)
                                        sp->file->readyL &= ~R_INITIAL;
                                else
                                        sp->file->readyR &= ~R_INITIAL;
                                if (indx <= 3)
                                        hit++;
                                else
                                        hit += 2;
                        }
                        if (sp->file->captured != 0) {
                                if (indx <= 1)
                                        hit--;
                                else
                                        hit -= 2;
                        }
                        hit += AMMO[indx][load - 1];
                        temp = sp->specs->class;
                        if ((temp >= 5 || temp == 1) && windspeed == 5)
                                hit--;
                        if (windspeed == 6 && temp == 4)
                                hit -= 2;
                        if (windspeed == 6 && temp <= 3)
                                hit--;
                        if (hit >= 0) {
                                if (load != L_GRAPE)
                                        hit = hit > 10 ? 10 : hit;
                                table(sp, closest, shootat, load, hit,
                                      dieroll());
                        }
                }
        }
}

int
next(void)
{
        if (++turn % 55 == 0) {
                if (alive)
                        alive = 0;
                else
                        people = 0;
        }
        if (people <= 0 || windspeed == 7) {
                struct ship *s;
                struct ship *bestship = NULL;
                float net, best = 0.0;
                foreachship(s) {
                        if (*s->file->captain)
                                continue;
                        net = (float)s->file->points / s->specs->pts;
                        if (net > best) {
                                best = net;
                                bestship = s;
                        }
                }
                if (best > 0.0 && bestship) {
                        char *tp = getenv("WOTD");
                        const char *p;
                        if (tp == 0)
                                p = "Driver";
                        else {
                                *tp = toupper((unsigned char)*tp);
                                p = tp;
                        }
                        strlcpy(bestship->file->captain, p,
                                sizeof bestship->file->captain);
                        logger(bestship);
                }
                return -1;
        }
        send_turn(turn);
        if (turn % 7 == 0 && (dieroll() >= cc->windchange || !windspeed)) {
                switch (dieroll()) {
                case 1:
                        winddir = 1;
                        break;
                case 2:
                        break;
                case 3:
                        winddir++;
                        break;
                case 4:
                        winddir--;
                        break;
                case 5:
                        winddir += 2;
                        break;
                case 6:
                        winddir -= 2;
                        break;
                }
                if (winddir > 8)
                        winddir -= 8;
                if (winddir < 1)
                        winddir += 8;
                if (windspeed)
                        switch (dieroll()) {
                        case 1:
                        case 2:
                                windspeed--;
                                break;
                        case 5:
                        case 6:
                                windspeed++;
                                break;
                        }
                else
                        windspeed++;
                send_wind( winddir, windspeed);
        }
        return 0;
}