/* * $Id: mine.c,v 1.1.1.1 2005/01/11 21:18:29 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: Tue Oct 22 18:25:30 1996 fingon * Last modified: Sun Jun 14 22:29:54 1998 fingon * */ /* Different types of mines: 1 = Standard round (infinite explosions, same damage, to everyone in hex) 2 = Inferno (single explosion, adds heat instead) 3 = Command-detonated (single explosion, goes off when hears transmission on predefined freq - damages neighbor hexes 1/2) 4 = Vibra (single explosion, triggered by weight (setting): target<=tons<(target+10) = when stepped on (target+10*n)<=tons = when stepped on n hexes away tons<target = no explosion */ #include "copyright.h" #include "config.h" #include <math.h> #include "mech.h" #include "mine.h" #include "p.artillery.h" #include "p.map.obj.h" #include "scen.h" #include "p.mine.h" #include "p.mech.utils.h" #include "p.btechstats.h" #include "p.template.h" /* Different types of mines * * The Trigger and ScenTrigger mines are used to let the MUX * know if a unit has moved to a certain spot * * The others are the explosive do damage kind */ char *mine_type_names[] = { "Standard", "Inferno", "CD", "Vibra", "Trigger", "ScenTrigger", NULL }; extern int compare_array(char *[], char *); void add_mine(MAP * map, int x, int y, int dam) { mapobj *o, foo; if (is_mine_hex(map, x, y)) { for (o = map->mapobj[TYPE_MINE]; o; o = o->next) if (o->x == x && o->y == y) break; if (o) return; } bzero(&foo, sizeof(foo)); foo.x = x; foo.y = y; foo.datas = dam; foo.datac = MINE_STANDARD; add_mapobj(map, &map->mapobj[TYPE_MINE], &foo, 1); } static void mine_damage_mechs(MAP * map, int tx, int ty, char *tomsg, char *otmsg, char *tomsg1, char *otmsg1, int dam, int heat, int nb) { blast_hit_hexes(map, dam, 5, heat, tx, ty, tomsg, otmsg, tomsg1, otmsg1, MINE_TABLE, 2, 1, 1, 1); } static void update_mine(MAP * map, mapobj * mine) { int i; i = mine->datas; i = i * MINE_NEXT_MODIFIER; if (i >= MINE_MIN) mine->datas = i; } void make_mine_explode(MECH * mech, MAP * map, mapobj * o, int x, int y, int reason) { int cool = (o->datas >= MINE_MIN); if ((o->datac == MINE_TRIGGER || o->datac == MINE_STRIGGER) && reason != MINE_STEP && reason != MINE_LAND) return; if (o->datac != MINE_TRIGGER && o->datac != MINE_STRIGGER) { if (o->datac != MINE_COMMAND) { switch (reason) { case MINE_STEP: MechLOSBroadcast(mech, tprintf("moves to %d,%d, and triggers a mine!", x, y)); mech_notify(mech, MECHALL, tprintf("As you move to %d,%d, you trigger a mine!", x, y)); break; case MINE_LAND: MechLOSBroadcast(mech, tprintf("triggers a mine!", x, y)); mech_notify(mech, MECHALL, "You trigger a mine!"); break; case MINE_DROP: case MINE_FALL: MechLOSBroadcast(mech, tprintf("triggers a mine!", x, y)); mech_notify(mech, MECHALL, "You trigger a mine!"); break; } } else HexLOSBroadcast(map, o->x, o->y, "A mine explodes in $H!"); } switch (o->datac) { case MINE_STANDARD: update_mine(map, o); mine_damage_mechs(map, o->x, o->y, "A blast of shrapnel hits you!", "is hit by shrapnel!", NULL, NULL, o->datas, 0, 0); if (!cool) mapobj_del(map, o->x, o->y, TYPE_MINE); break; case MINE_INFERNO: update_mine(map, o); mine_damage_mechs(map, o->x, o->y, "Globs of flaming gel hit you!", "is hit by globs of flaming gel!", NULL, NULL, o->datas / 3, o->datas, 0); if (!cool) mapobj_del(map, o->x, o->y, TYPE_MINE); break; case MINE_COMMAND: unset_hex_mine(map, o->x, o->y); mine_damage_mechs(map, o->x, o->y, "A blast of shrapnel hits you!", "is hit by shrapnel!", "A little blast of shrapnel hits you!", "is hit by some of the shrapnel!", o->datas, 0, 1); mapobj_del(map, o->x, o->y, TYPE_MINE); break; case MINE_TRIGGER: SendTrigger(tprintf("#%d %s activated trigger at %d,%d.", mech->mynum, GetMechID(mech), o->x, o->y)); return; case MINE_STRIGGER: scen_trigger_mine(map, mech, o->x, o->y); return; case MINE_VIBRA: unset_hex_mine(map, o->x, o->y); if (o->x != x || o->y != y) HexLOSBroadcast(map, o->x, o->y, "A mine explodes in $H!"); mine_damage_mechs(map, o->x, o->y, "A blast of shrapnel hits you!", "is hit by shrapnel!", "A little blast of shrapnel hits you!", "is hit by some of the shrapnel!", o->datas, 0, 1); mapobj_del(map, o->x, o->y, TYPE_MINE); break; } recalculate_minefields(map); } /* we find the mine(s) that cause this (vibras can do it long-distance), and eliminate it */ static void possible_mine_explosion(MECH * mech, MAP * map, int x, int y, int reason) { mapobj *o, *o2; int mdis = (MechRealTons(mech) - 20) / 10; float x1, y1, x2, y2, range; MapCoordToRealCoord(x, y, &x1, &y1); for (o = map->mapobj[TYPE_MINE]; o; o = o2) { int real = 1; o2 = o->next; if (o->x == x && o->y == y) { switch (o->datac) { case MINE_TRIGGER: if (o->datas > MechRealTons(mech)) continue; break; case MINE_VIBRA: if (o->datai > MechRealTons(mech)) continue; /* No message, just boom */ break; case MINE_COMMAND: mech_notify(mech, MECHALL, "You spot small bomblets lying on the ground here.."); real = 0; continue; } if (!real) return; make_mine_explode(mech, map, o, x, y, reason); } else if (VIBRO(o->datac)) { if (o->datac == MINE_TRIGGER) { /* To small let it go */ if (o->datas > (MechRealTons(mech))) continue; MapCoordToRealCoord(o->x, o->y, &x2, &y2); /* Out side of range */ /* Using round here because we get some funky ranges like * 0.999987 and 1.00000072 */ if (nearbyintf(FindHexRange(x1, y1, x2, y2)) > ((float) o->datai)) continue; make_mine_explode(mech, map, o, x, y, reason); } else if (o->datai < MechRealTons(mech)) { if (abs(o->x - x) <= mdis && abs(o->y - y) <= mdis) { /* Possible remote explosion */ MapCoordToRealCoord(o->x, o->y, &x2, &y2); if ((range = FindHexRange(x1, y1, x2, y2)) > (MechRealTons(mech) - o->datai) / 10) continue; make_mine_explode(mech, map, o, x, y, reason); } } } } } void possible_mine_poof(MECH * mech, int reason) { MAP *map = getMap(mech->mapindex); int x = MechX(mech); int y = MechY(mech); if (!is_mine_hex(map, x, y)) return; if (MechZ(mech) > (MechRTerrain(mech) == ICE ? 0 : Elevation(map, x, y))) return; possible_mine_explosion(mech, map, x, y, reason); } void possibly_remove_mines(MECH * mech, int x, int y) { MAP *map = FindObjectsData(mech->mapindex); if (!map) return; if (!is_mine_hex(map, x, y)) return; /* Do the cleaning stuff here */ /* Ok, we're lazy and just decide that roll of <= 4 removes all traces of mines in the hex */ if (Roll() <= 4) { if (mapobj_del(map, x, y, TYPE_MINE)) { /* There _was_ something to clear.. no message, we're evil */ recalculate_minefields(map); } } } /* for now, just put the hexes themselves ; vibras should have larger radius */ /* Added Exile's MINE_TRIGGER changes. Can set a distance for the mine and it will add mines to the hexes within that range - Dany */ static void add_mine_on_map(MAP * map, int x, int y, char type, int data) { int x1, y1; int mdis = (100 - data) / 10; int t = mdis * 3 / 2; if (type == MINE_TRIGGER) { float fx, fy, fx1, fy1; /* Get the main hex's location in floating values */ MapCoordToRealCoord(x, y, &fx, &fy); /* Loop through all the possible hexes within range * and add mines to those hexes if they are within * range */ for (x1 = x - data; x1 <= x + data; x1++) for (y1 = y - data; y1 <= y + data; y1++) { /* Check the range, if in range add a mine */ /* We round because of weirdness with FindHexRange returning * values like 1.00215 */ MapCoordToRealCoord(x1, y1, &fx1, &fy1); if (nearbyintf(FindHexRange(fx, fy, fx1, fy1)) <= ((float) data)) set_hex_mine(map, x1, y1); } } else if (type >= MINE_LOW && type <= MINE_HIGH) { if (VIBRO(type) && mdis) { for (x1 = x - mdis; x1 <= (x + mdis); x1++) for (y1 = y - mdis; y1 <= (y + mdis); y1++) if ((abs(x1 - x) + abs(y1 - y)) <= t) if (!(x1 < 0 || y1 < 0 || x1 >= map->map_width || y1 >= map->map_height)) set_hex_mine(map, x1, y1); } else { set_hex_mine(map, x, y); } } } /* Re-set all the minefield bits on a map */ void recalculate_minefields(MAP * map) { mapobj *o; clear_hex_bits(map, 1); for (o = map->mapobj[TYPE_MINE]; o; o = o->next) add_mine_on_map(map, o->x, o->y, o->datac, o->datai); } /* x y type strength <optvalue> */ void map_add_mine(dbref player, void *data, char *buffer) { char *args[6]; int argc; int x, y, str, type, extra = 0; MAP *map = (MAP *) data; mapobj foo; if (!map) return; #define READINT(from,to) \ DOCHECK(Readnum(to,from), "Invalid number!") argc = mech_parseattributes(buffer, args, 6); DOCHECK(argc < 4 || argc > 5, "Invalid arguments!"); READINT(args[0], x); READINT(args[1], y); READINT(args[3], str); if (argc == 5) READINT(args[4], extra); DOCHECK((type = compare_array(mine_type_names, args[2])) < 0, "Invalid mine type!"); DOCHECK(!((x >= 0) && (x < map->map_width) && (y >= 0) && (y < map->map_height)), "X,Y out of range!"); bzero(&foo, sizeof(foo)); foo.x = x; foo.y = y; foo.datai = extra; foo.datas = str; foo.datac = type + 1; foo.obj = player; add_mapobj(map, &map->mapobj[TYPE_MINE], &foo, 1); notify(player, tprintf("%s mine added to (%d,%d) (strength: %d / extra: %d)", mine_type_names[type], x, y, str, extra)); recalculate_minefields(map); } void explode_mines(MECH * mech, int chn) { MAP *map = getMap(mech->mapindex); mapobj *o, *o2; int count = 0; if (!map) return; for (o = map->mapobj[TYPE_MINE]; o; o = o2) { o2 = o->next; if (o->datac == MINE_COMMAND) if (o->datai == chn) { make_mine_explode(mech, map, o, 0, 0, 0); count++; } } if (count) recalculate_minefields(map); } void show_mines_in_hex(dbref player, MECH * mech, float range, int x, int y) { MAP *map = getMap(mech->mapindex); mapobj *o; DOCHECK(!is_mine_hex(map, x, y), "You see nothing else of interest in the hex, either."); for (o = map->mapobj[TYPE_MINE]; o; o = o->next) if (o->x == x && o->y == y) break; DOCHECK(!o, "You see nothing else of interest in the hex, either."); DOCHECK(Number(2, 9) < ((int) range), "You see nothing else of interest in the hex, either."); DOCHECK(!MadePerceptionRoll(mech, 0), "You see nothing else of interest in the hex, either."); mech_notify(mech, MECHALL, "Small bomblets litter the hex, interesting... You vaguely " "recall them from some class or other."); }