btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * $Id: artillery.c,v 1.1.1.1 2005/01/11 21:18:01 kstevens Exp $
 *
 * Author: Markus Stenberg <fingon@iki.fi>
 *
 *  Copyright (c) 1996 Markus Stenberg
 *  Copyright (c) 1998-2002 Thomas Wouters
 *  Copyright (c) 2000-2002 Cord Awtry
 *       All rights reserved
 *
 * Created: Thu Sep 12 17:28:45 1996 fingon
 * Last modified: Sat Jul 18 01:54:03 1998 fingon
 *
 */

/* 
   Artillery code for
   - standard rounds (damage to target hex, damage/2 to neighbor hexes)
   - smoke rounds (to be implemented)
   - fascam rounds (to be implemented) 
 */

#include <math.h>

#include "mech.h"
#include "artillery.h"
#include "mech.events.h"
#include "create.h"
#include "p.artillery.h"
#include "p.mech.fire.h"
#include "p.mech.combat.h"
#include "p.mech.combat.misc.h"
#include "p.mech.damage.h"
#include "p.mech.utils.h"
#include "p.map.obj.h"
#include "p.mech.hitloc.h"
#include "p.mine.h"
#include "spath.h"

struct artillery_shot_type *free_shot_list = NULL;
static void artillery_hit(artillery_shot * s);

static const char *artillery_type(artillery_shot * s)
{
    if (s->type == CL_ARROW || s->type == IS_ARROW)
	return "a missile";
    return "a round";
}

static struct {
    int dir;
    char *desc;
} arty_dirs[] = {
    {
    0, "north"}, {
    60, "northeast"}, {
    90, "east"}, {
    120, "southeast"}, {
    180, "south"}, {
    240, "southwest"}, {
    270, "west"}, {
    300, "northwest"}, {
    0, NULL}
};

static const char *artillery_direction(artillery_shot * s)
{
    float fx, fy, tx, ty;
    int b, d, i, best = -1, bestd = 0;

    MapCoordToRealCoord(s->from_x, s->from_y, &fx, &fy);
    MapCoordToRealCoord(s->to_x, s->to_y, &tx, &ty);
    b = FindBearing(fx, fy, tx, ty);
    for (i = 0; arty_dirs[i].desc; i++) {
	d = abs(b - arty_dirs[i].dir);
	if (best < 0 || d < bestd) {
	    best = i;
	    bestd = d;
	}
    }
    if (best < 0)
	return "Invalid";
    return arty_dirs[best].desc;
}

int artillery_round_flight_time(float fx, float fy, float tx, float ty)
{
    int delay = MAX(ARTILLERY_MINIMUM_FLIGHT,
	(FindHexRange(fx, fy, tx, ty) / ARTY_SPEED));

    /* XXX Different weapons, diff. speed? */
    return delay;
}


static void artillery_hit_event(MUXEVENT * e)
{
    artillery_shot *s = (artillery_shot *) e->data;

    artillery_hit(s);
    ADD_TO_LIST_HEAD(free_shot_list, next, s);
}

void artillery_shoot(MECH * mech, int targx, int targy, int windex,
    int wmode, int ishit)
{
    struct artillery_shot_type *s;
    float fx, fy, tx, ty;

    if (free_shot_list) {
	s = free_shot_list;
	free_shot_list = free_shot_list->next;
    } else
	Create(s, artillery_shot, 1);
    s->from_x = MechX(mech);
    s->from_y = MechY(mech);
    s->to_x = targx;
    s->to_y = targy;
    s->type = windex;
    s->mode = wmode;
    s->ishit = ishit;
    s->shooter = mech->mynum;
    s->map = mech->mapindex;
    MechLOSBroadcast(mech, tprintf("shoots %s towards %s!",
	    artillery_type(s), artillery_direction(s)));
    MapCoordToRealCoord(s->from_x, s->from_y, &fx, &fy);
    MapCoordToRealCoord(s->to_x, s->to_y, &tx, &ty);
    muxevent_add(artillery_round_flight_time(fx, fy, tx, ty), 0, EVENT_DHIT,
	artillery_hit_event, (void *) s, NULL);
}

static int blast_arcf(float fx, float fy, MECH * mech)
{
    int b, dir;

    b = FindBearing(MechFX(mech), MechFY(mech), fx, fy);
    dir = AcceptableDegree(b - MechFacing(mech));
    if (dir > 120 && dir < 240)
	return BACK;
    if (dir > 300 || dir < 60)
	return FRONT;
    if (dir > 180)
	return LEFTSIDE;
    return RIGHTSIDE;
}

#define TABLE_GEN   0
#define TABLE_PUNCH 1
#define TABLE_KICK  2


void blast_hit_hexf(MAP * map, int dam, int singlehitsize, int heatdam,
    float fx, float fy, float tfx, float tfy, char *tomsg, char *otmsg,
    int table, int safeup, int safedown, int isunderwater)
{
    MECH *tempMech;
    int loop;
    int isrear = 0, iscritical = 0, hitloc;
    int damleft, arc, ndam;
    int ground_zero;
    short tx, ty;

    /* Not on a map so just return */
    if (!map)
        return;

    RealCoordToMapCoord(&tx, &ty, fx, fy);
    if (tx < 0 || ty < 0 || tx >= map->map_width || ty >= map->map_height)
	return;
    if (!tomsg || !otmsg)
	return;
    if (isunderwater)
	ground_zero = Elevation(map, tx, ty);
    else
	ground_zero = MAX(0, Elevation(map, tx, ty));

    for (loop = 0; loop < map->first_free; loop++)
	if (map->mechsOnMap[loop] >= 0) {
	    tempMech = (MECH *)
		FindObjectsData(map->mechsOnMap[loop]);
	    if (!tempMech)
		continue;
	    if (MechX(tempMech) != tx || MechY(tempMech) != ty)
		continue;
	    /* Far too high.. */
	    if (MechZ(tempMech) >= (safeup + ground_zero))
		continue;
	    /* Far too below (underwater, mostly) */
	    if (		/* MechTerrain(tempMech) == WATER &&  */
		MechZ(tempMech) <= (ground_zero - safedown))
		continue;
	    MechLOSBroadcast(tempMech, otmsg);
	    mech_notify(tempMech, MECHALL, tomsg);
	    arc = blast_arcf(tfx, tfy, tempMech);

	    if (arc == BACK)
		isrear = 1;
	    damleft = dam;

	    while (damleft > 0) {
		if (singlehitsize <= damleft)
		    ndam = singlehitsize;
		else
		    ndam = damleft;

		damleft -= ndam;

		switch (table) {
		case TABLE_PUNCH:
		    FindPunchLoc(tempMech, hitloc, arc, iscritical,
			isrear);
		    break;
		case TABLE_KICK:
		    FindKickLoc(tempMech, hitloc, arc, iscritical, isrear);
		    break;
		default:
		    hitloc =
			FindHitLocation(tempMech, arc, &iscritical,
			&isrear);
		}

		DamageMech(tempMech, tempMech, 0, -1, hitloc, isrear,
		    iscritical, ndam, 0, -1, 0, -1, 0, 0);
	    }
	    heat_effect(NULL, tempMech, heatdam, 0);
	}
}

void blast_hit_hex(MAP * map, int dam, int singlehitsize, int heatdam,
    int fx, int fy, int tx, int ty, char *tomsg, char *otmsg, int table,
    int safeup, int safedown, int isunderwater)
{
    float ftx, fty;
    float ffx, ffy;

    MapCoordToRealCoord(tx, ty, &ftx, &fty);
    MapCoordToRealCoord(fx, fy, &ffx, &ffy);
    blast_hit_hexf(map, dam, singlehitsize, heatdam, ffx, ffy, ftx, fty,
	tomsg, otmsg, table, safeup, safedown, isunderwater);
}

void blast_hit_hexesf(MAP * map, int dam, int singlehitsize, int heatdam,
    float fx, float fy, float ftx, float fty, char *tomsg, char *otmsg,
    char *tomsg1, char *otmsg1, int table, int safeup, int safedown,
    int isunderwater, int doneighbors)
{
    int x1, y1, x2, y2;
    int dm;
    short tx, ty;
    float hx, hy;
    float t = FindXYRange(fx, fy, ftx, fty);

    dm = MAX(1, (int) t + 1);
    blast_hit_hexf(map, dam / dm, singlehitsize, heatdam / dm, fx, fy, ftx,
	fty, tomsg, otmsg, table, safeup, safedown, isunderwater);
    if (!doneighbors)
	return;
    RealCoordToMapCoord(&tx, &ty, fx, fy);
    for (x1 = (tx - doneighbors); x1 <= (tx + doneighbors); x1++)
	for (y1 = (ty - doneighbors); y1 <= (ty + doneighbors); y1++) {
	    int spot;

	    if ((dm = MyHexDist(tx, ty, x1, y1, 0)) > doneighbors)
		continue;
	    if ((tx == x1) && (ty == y1))
		continue;
	    x2 = BOUNDED(0, x1, map->map_width - 1);
	    y2 = BOUNDED(0, y1, map->map_height - 1);
	    if (x1 != x2 || y1 != y2)
		continue;
	    spot = (x1 == tx && y1 == ty);
	    MapCoordToRealCoord(x1, y1, &hx, &hy);
	    dm++;
	    if (!(dam / dm))
		continue;
	    blast_hit_hexf(map, dam / dm, singlehitsize, heatdam / dm, hx,
		hy, ftx, fty, spot ? tomsg : tomsg1, spot ? otmsg : otmsg1,
		table, safeup, safedown, isunderwater);

	    /*
	       * Added in burning woods when a mech's engine goes nova
	       *
	       * -Kipsta
	       * 8/4/99
	     */

	    switch (GetRTerrain(map, x1, y1)) {
	    case LIGHT_FOREST:
	    case HEAVY_FOREST:
		if (!find_decorations(map, x1, y1)) {
		    add_decoration(map, x1, y1, TYPE_FIRE, FIRE,
			FIRE_DURATION);
		}

		break;
	    }
	}
}

void blast_hit_hexes(MAP * map, int dam, int singlehitsize, int heatdam,
    int tx, int ty, char *tomsg, char *otmsg, char *tomsg1, char *otmsg1,
    int table, int safeup, int safedown, int isunderwater, int doneighbors)
{
    float fx, fy;

    MapCoordToRealCoord(tx, ty, &fx, &fy);
    blast_hit_hexesf(map, dam, singlehitsize, heatdam, fx, fy, fx, fy,
	tomsg, otmsg, tomsg1, otmsg1, table, safeup, safedown,
	isunderwater, doneighbors);
}

static void artillery_hit_hex(MAP * map, artillery_shot * s, int type,
    int mode, int dam, int tx, int ty, int isdirect)
{
    char buf[LBUF_SIZE];
    char buf1[LBUF_SIZE];
    char buf2[LBUF_SIZE];


    /* Safety check -- shouldn't happen */
    if (tx < 0 || tx >= map->map_width ||
        ty < 0 || ty >= map->map_height)
	return;

    if ((mode & SMOKE_MODE)) {
	/* Add smoke */
	add_decoration(map, tx, ty, TYPE_SMOKE, SMOKE, SMOKE_DURATION);
	return;
    }
    if (mode & MINE_MODE) {
	add_mine(map, tx, ty, dam);
	return;
    }
    if (!(mode & CLUSTER_MODE)) {
	if (isdirect)
	    sprintf(buf1, "receives a direct hit!");
	else
	    sprintf(buf1, "is hit by fragments!");
	if (isdirect)
	    sprintf(buf2, "You receive a direct hit!");
	else
	    sprintf(buf2, "You are hit by fragments!");
    } else {
	if (dam > 2)
	    strcpy(buf, "bomblets");
	else
	    strcpy(buf, "a bomblet");
	sprintf(buf1, "is hit by %s!", buf);
	sprintf(buf2, "You are hit by %s!", buf);
    }
    blast_hit_hex(map, dam, (mode & CLUSTER_MODE) ? 2 : 5, 0, tx, ty, tx,
	ty, buf2, buf1, (mode & CLUSTER_MODE) ? TABLE_PUNCH : TABLE_GEN,
	10, 4, 0);
}

static artillery_shot * hit_neighbors_s;
static int hit_neighbors_type;
static int hit_neighbors_mode;
static int hit_neighbors_dam;

static void artillery_hit_neighbors_callback(MAP *map, int x, int y)
{
    artillery_hit_hex(map, hit_neighbors_s, hit_neighbors_type,
    		      hit_neighbors_mode, hit_neighbors_dam, x, y, 0);
}

static void artillery_hit_neighbors(MAP * map, artillery_shot * s,
    int type, int mode, int dam, int tx, int ty)
{
    hit_neighbors_s = s;
    hit_neighbors_type = type;
    hit_neighbors_mode = mode;
    hit_neighbors_dam = dam;
    visit_neighbor_hexes(map, tx, ty, artillery_hit_neighbors_callback);
}

static void artillery_cluster_hit(MAP * map, artillery_shot * s, int type,
    int mode, int dam, int tx, int ty)
{
    /* Main idea: Pick <dam/2> bombs of 2pts each, and scatter 'em
       over 5x5 area with weighted numbers */
    int xd, yd, x, y;
    int i;

    int targets[5][5];
    int d;

    bzero(targets, sizeof(targets));
    for (i = 0; i < dam; i++) {
	do {
	    xd = Number(-2, 0) + Number(0, 2);
	    yd = Number(-2, 0) + Number(0, 2);
	    x = tx + xd;
	    y = ty + yd;
	}
	while (x < 0 || x >= map->map_width ||
	       y < 0 || y >= map->map_height);
	/* Whee.. it's time to drop a bomb to the hex */
	targets[xd + 2][yd + 2]++;
    }
    for (xd = 0; xd < 5; xd++)
	for (yd = 0; yd < 5; yd++)
	    if ((d = targets[xd][yd]))
		artillery_hit_hex(map, s, type, mode, d * 2, xd + tx - 2,
		    yd + ty - 2, 1);
}

void artillery_FriendlyAdjustment(dbref mechnum, MAP * map, int x, int y)
{
    MECH *mech;
    MECH *spotter;
    MECH *tempMech = NULL;

    if (!(mech = getMech(mechnum)))
	return;
    /* Ok.. we've a valid guy */
    spotter = getMech(MechSpotter(mech));
    if (!((MechTargX(mech) == x && MechTargY(mech) == y)
	    || (spotter && (MechTargX(spotter) == x &&
		    MechTargY(spotter) == y))))
	return;
    /* Ok.. we've a valid target to adjust fire on */
    /* Now, see if we've any friendlies in LOS.. NOTE: FRIENDLIES ;-) */
    if (spotter) {
	if (MechSeesHex(spotter, map, x, y))
	    tempMech = spotter;
    } else
	tempMech = find_mech_in_hex(mech, map, x, y, 2);
    if (!tempMech)
	return;
    if (!Started(tempMech) || !Started(mech))
	return;
    if (spotter) {
	mech_notify(mech, MECHSTARTED,
	    tprintf("%s sent you some trajectory-correction data.",
		GetMechToMechID(mech, tempMech)));
	mech_notify(tempMech, MECHSTARTED,
	    tprintf("You provide %s with information about the miss.",
		GetMechToMechID(tempMech, mech)));
    }
    MechFireAdjustment(mech)++;
}

static void artillery_hit(artillery_shot * s)
{
    /* First, we figure where it exactly hits. Our first-hand information
       is only whether it hits or not, not _where_ it hits */
    double dir;
    int di;
    int dist;
    int weight;
    MAP *map = getMap(s->map);
    int original_x = 0, original_y = 0;
    int dam = MechWeapons[s->type].damage;

    if (!map)
	return;
    if (!s->ishit) {
	/* Shit! We missed target ;-) */
	/* Time to calculate a new target hex */
	di = Number(0, 359);
	dir = di * TWOPIOVER360;
	dist = Number(2, 7);
	weight = 100 * (dist * 6) / ((dist * 6 + map->windspeed));
	di = (di * weight + map->winddir * (100 - weight)) / 100;
	dist =
	    (dist * weight + (map->windspeed / 6) * (100 - weight)) / 100;
	original_x = s->to_x;
	original_y = s->to_y;
	s->to_x = s->to_x + dist * cos(dir);
	s->to_y = s->to_y + dist * sin(dir);
	s->to_x = BOUNDED(0, s->to_x, map->map_width - 1);
	s->to_y = BOUNDED(0, s->to_y, map->map_height - 1);
	/* Time to calculate if any friendlies have LOS to hex,
	   and if so, adjust fire adjustment unless you lack information /
	   have changed target */
    }
    /* It's time to run for your lives, lil' ones ;-) */
    if (!(s->mode & ARTILLERY_MODES))
	HexLOSBroadcast(map, s->to_x, s->to_y, tprintf("%s fire hits $H!",
		&MechWeapons[s->type].name[3]));
    else if (s->mode & CLUSTER_MODE)
	HexLOSBroadcast(map, s->to_x, s->to_y,
	    "A rain of small bomblets hits $H's surroundings!");
    else if (s->mode & MINE_MODE)
	HexLOSBroadcast(map, s->to_x, s->to_y,
	    "A rain of small bomblets hits $H!");
    else if (s->mode & SMOKE_MODE)
	HexLOSBroadcast(map, s->to_x, s->to_y,
	    tprintf("A %s %s hits $h, and smoke starts to billow!",
		&MechWeapons[s->type].name[3], &(artillery_type(s)[2])));

    /* Basic theory:
       - smoke / ordinary rounds are spread with the ordinary functions 
       - mines are otherwise ordinary except no hitting of neighbor hexes
       - cluster bombs are special 
     */
    if (!(s->mode & CLUSTER_MODE)) {
	/* Enjoy ourselves in all neighbor hexes, too */
	artillery_hit_hex(map, s, s->type, s->mode, dam, s->to_x, s->to_y,
	    1);
	if (!(s->mode & MINE_MODE))
	    artillery_hit_neighbors(map, s, s->type, s->mode, dam / 2,
		s->to_x, s->to_y);
    } else
	artillery_cluster_hit(map, s, s->type, s->mode, dam, s->to_x,
	    s->to_y);
    if (!s->ishit)
	artillery_FriendlyAdjustment(s->shooter, map, original_x,
	    original_y);
}