/*      $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;
}