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