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: mech.sensor.c,v 1.3 2005/08/08 07:56:08 murrayma 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: Mon Sep  2 14:45:01 1996 fingon
 * Last modified: Thu Jul  9 20:35:16 1998 fingon
 *
 */

#include "mech.h"
#include "map.h"
#define _MECH_SENSOR_C
#include "p.mech.ecm.h"
#include "p.mech.tag.h"
#include "mech.events.h"
#include "mech.sensor.h"
#include "autopilot.h"
#include "p.mech.utils.h"
#include "p.mech.lite.h"
#include "p.mech.los.h"
#include "p.btechstats.h"

#define SEE_SENSOR_PRIMARY        1
#define SEE_SENSOR_SECONDARY      2

int Sensor_ToHitBonus(MECH * mech, MECH * target, int flag, int maplight,
    float range, int wAmmoMode)
{
    int bth1, bth2;
    int wLightMod = (wAmmoMode & AC_INCENDIARY_MODE) ? 1 : 0;
    MAP *map = getMap(mech->mapindex);

    maplight = maplight + wLightMod;

    if (maplight < 0)
	maplight = 0;

    if (maplight > 2)
	maplight = 2;

    if (!(flag & (MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS)))
	return 10000;
    if (!(flag & MECHLOSFLAG_SEESP)) {
	bth2 = 1 + sensors[(int) MechSensor(mech)[1]].tohitbonus_func(mech,
	    target, map, flag, maplight);
#ifdef SENSOR_BTH_DEBUG
	SendDebug(tprintf("%d: BTH S+%d", mech->mynum, bth2));
#endif
	return bth2;
    }
    if (!(flag & MECHLOSFLAG_SEESS) ||
	(MechSensor(mech)[0] == MechSensor(mech)[1])) {
	bth1 = sensors[(int) MechSensor(mech)[0]].tohitbonus_func(mech,
	    target, map, flag, maplight);
#ifdef SENSOR_BTH_DEBUG
	SendDebug(tprintf("%d: BTH P+%d", mech->mynum, bth1));
#endif
	return bth1;
    }
    bth1 = sensors[(int) MechSensor(mech)[0]].tohitbonus_func(mech, target,
	map, flag, maplight);
    bth2 = 1 + sensors[(int) MechSensor(mech)[1]].tohitbonus_func(mech,
	target, map, flag, maplight);
#ifdef SENSOR_BTH_DEBUG
    SendDebug(tprintf("%d: BTH +%d/+%d", mech->mynum, bth1, bth2));
#endif
    return MIN(bth1, bth2);
}

int Sensor_CanSee(MECH * mech, MECH * target, int *flag, int arc,
    float range, int mapvis, int maplight, int cloudbase)
{
    int i, j = 0, sn;
    MAP *map = getMap(mech->mapindex);

    if (!(*flag & MECHLOSFLAG_SEEC2))
	return 0;
    if (target && (MechCritStatus(target) & INVISIBLE))
	return 0;
    /* Ok.. s'pose we can, at that. */
    if (MechSensor(mech)[0] != MechSensor(mech)[1]) {
#define MaxVis(mech,sensor) ((sensors[sensor].maxvis*(((sensor)>=2 && MechMove(mech)==MOVE_NONE)?140:100)) / 100)
#define MaxVar(mech,sensor) sensors[sensor].maxvvar
#define MAXVR(mech,sensor) MaxVis(mech,sensor) - MaxVar(mech, sensor)
	/* Check both seperately */
	for (i = 0; i < 2; i++) {
	    sn = MechSensor(mech)[i];
	    /* No chance */

	    if (!sensors[sn].fullvision && !(arc & (FORWARDARC|TURRETARC)) &&
		!Sees360(mech))
		continue;
	    if (MaxVis(mech, sn) < range)
		continue;

	    /* Okay, so this is a horrible, horrible hack that'll break when new
	       * sensors get added. Thankfully it's not as ugly as the rest of the
	       * constructs in this area, so i dont really care. -Foo */

	    if (target) {
		if (cloudbase && sn < 3 && ((MechZ(mech) < cloudbase)
			? (MechZ(target) >= cloudbase) : (MechZ(target)
			    < cloudbase)))
		    continue;
	    } else {
		if (cloudbase && MechZ(mech) > cloudbase)
		    continue;
	    }

	    if (!sensors[sn].cansee_func(mech, target, map, range, *flag))
		continue;

	    if (!sensors[sn].seechance_func(target, map, sn, range, mapvis,
		    maplight))
		continue;

	    if (MaxVar(mech, sn) && (MAXVR(mech, sn)) < range)
		if (((MAXVR(mech, sn)) + (MechVisMod(mech) * (MaxVar(mech,
				    sn) + 1)) / 100) < range)
		    continue;
	    j += (i + 1);
	}
	return j;
    }
    sn = MechSensor(mech)[0];
    if (MaxVis(mech, sn) < range)
	return 0;
    if (cloudbase && target && sn < 3 &&
	((MechZ(mech) < cloudbase) ? (MechZ(target) >= cloudbase)
	    : (MechZ(target) < cloudbase)))
	return 0;
    if (!sensors[sn].cansee_func(mech, target, map, range, *flag))
	return 0;
    if (!sensors[sn].seechance_func(target, map, sn, range, mapvis, maplight))
	return 0;
    if (MaxVar(mech, sn) && (MAXVR(mech, sn)) < range)
	if (((MAXVR(mech, sn)) + MechVisMod(mech) * (MaxVar(mech,
			sn) + 1) / 100) < range)
	    return 0;
    return 3;
}

int Sensor_ArcBaseChance(int type, int arc)
{
    int base = 100;

    switch (type) {
    case CLASS_MW:
	if (arc & (LSIDEARC | RSIDEARC | REARARC))
	    return 0;
	break;
    case CLASS_BSUIT:
    case CLASS_MECH:
	if (arc & (LSIDEARC | RSIDEARC))
	    base = 70;
	else if (arc & REARARC)
	    base = 40;
	break;
    default:
	if (arc & (LSIDEARC | RSIDEARC))
	    base = 80;
	else if (arc & REARARC)
	    base = 50;
	if (arc & TURRETARC)
	    base += 15;
	break;
    }
    return base;
}

extern int bth_modifier[];

/* Slow, but sacrifices we make for sake of playability.. :-) */
int Sensor_DriverBaseChance(MECH * mech)
{
    int i = 1;

    if (MechPer(mech) <= 2)
	i = 36;
    else if (MechPer(mech) >= 12)
	i = 1;
    else
	i = (36 - bth_modifier[MechPer(mech) - 2]);
    return 64 + i;		/* Padded a bit */
}


int Sensor_Sees(MECH * mech, MECH * target, int f, int arc, float range,
    int snum, int chance_divisor, int mapvis, int maplight)
{
    MAP *map = getMap(mech->mapindex);
    int chance = (Sensor_ArcBaseChance(MechType(mech), arc) * ((target &&
		MechTeam(mech) != MechTeam(target)) ?
		Sensor_DriverBaseChance(mech) : 100)) / 100;
    int ch2 = sensors[snum].seechance_func(target, map, snum, range,
    		mapvis, maplight);

    if (!ch2 || !sensors[snum].cansee_func(mech, target, map, range, f))
	return 0;
    if (target && IsDS(target))
	chance = chance * 4;
    if (target && MechCritStatus(target) & HIDDEN &&
	MechTeam(mech) != MechTeam(target)) {

	if (sensors[snum].matchletter[0] == 'B' &&
	    (MechInfantrySpecials(target) & STEALTH_TECH) &&
	    (!(MechInfantrySpecials(target) & CS_PURIFIER_STEALTH_TECH)))
	    return 0;

	if (ch2 <= 100) {
	    if (range > 5)
		return 0;
	    chance = chance / 4;
	}
    }
    if (range < 3 || Number(1, 10000) < ((chance * ch2) / chance_divisor)) {
	if (target && In_Character(mech->mynum) &&
	    In_Character(target->mynum) &&
	    MechTeam(mech) != MechTeam(target))
	    if (!Number(0, 5))
		MadePerceptionRoll(mech, -2);
	return 1;
    }
    return 0;
}

/* Main idea: If primary & secondary scanner differ,
   check both scanners for chance (with secondary at 1/2 chance).
   Also, if non-360degree scanners are used, check only forward arc for
   them. 

   If both same, multiply chance by 1.1
 */
int Sensor_SeesNow(MECH * mech, MECH * target, int f, int arc, float range,
    int mapvis, int maplight)
{
    int i, sn;

    if (MechSensor(mech)[0] != MechSensor(mech)[1]) {
	/* Check both seperately */
	for (i = 0; i < 2; i++) {
	    sn = MechSensor(mech)[i];
	    /* No chance */
	    if (!sensors[sn].fullvision && !(arc & (FORWARDARC|TURRETARC)) &&
		!Sees360(mech))
		continue;
	    if (MaxVis(mech, sn) < range)
		continue;
	    if (Sensor_Sees(mech, target, f, arc, range, sn, i + 1, mapvis,
		    maplight))
		return (i + 1);
	}
	return 0;
    }
    sn = MechSensor(mech)[0];
    if (MaxVis(mech, sn) < range)
	return 0;
    return (Sensor_Sees(mech, target, f, arc, range, sn, 1, mapvis,
	    maplight));
    return 3;
}

char *my_dump_flag(int i)
{
    int j;
    static char buf[MBUF_SIZE];

    strcpy(buf, "");
    for (j = 0; j < 32; j++)
	if (i & (1 << j)) {
	    if (buf[0] == 0)
		sprintf(buf, "%d", j);
	    else
		sprintf(buf + strlen(buf), ",%d", j);
	}
    return buf;
}

#define AUTOCON_LONG		0x01
#define AUTOCON_WARN		0x02
#define AUTOCON_SHORT		0x04

static int valid_to_notice(MECH * mech, MECH * targ, int los)
{
    int bf = (mech->brief / 4);
    int foe;
    
    if ((los < 0 && MechSeemsFriend(mech, targ)) ||
        (los > 0 && MechTeam(mech) == MechTeam(targ)))
        foe = 0;
    else
        foe = AUTOCON_WARN;

    switch (bf) {
    case 0:
        return AUTOCON_LONG | foe;
    case 1:
	return AUTOCON_SHORT | foe;
    case 2:
	return foe ? (AUTOCON_LONG | foe) : 0;
    case 3:
	return foe ? (AUTOCON_SHORT | foe) : 0;
    case 4:
	return AUTOCON_SHORT;
    case 5:
	return foe ? AUTOCON_SHORT : 0;
    case 6:
    default:
	return 0;
    }
}

void Sensor_DoWeSeeNow(MECH * mech, unsigned short *fl, float range, int x,
    int y, MECH * target, int mapvis, int maplight, int cloudbase,
    int seeanew, int wlf)
{
    int arc;
    float x1, y1;
    int sc, sl, st;
    int f = *fl;
    char buf[MBUF_SIZE];

    if (!Started(mech))
	return;
    if (target) {
	x1 = MechFX(target);
	y1 = MechFY(target);
	if (MechZ(mech) >= ATMO_Z && MechZ(target) >= ATMO_Z)
	    range = range / 3;
    } else
	MapCoordToRealCoord(x, y, &x1, &y1);
    arc = InWeaponArc(mech, x1, y1);
    if (f & MECHLOSFLAG_SEEN) {
	if ((sl =
		Sensor_CanSee(mech, target, &f, arc, range, mapvis,
		    maplight, cloudbase))) {
	    if (sl & 1)
		f |= MECHLOSFLAG_SEESP;
	    if (sl & 2)
		f |= MECHLOSFLAG_SEESS;
	}
	if (!(f & (MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS))) {
	    if (MechTeam(mech) != MechTeam(target))
		MechNumSeen(mech) = MAX(0, MechNumSeen(mech) - 1);
	    f &= ~MECHLOSFLAG_SEEN;
	    if ((Started(target) || SeeWhenShutdown(target) ||
		 MechAutoconSD(mech)) &&
	        (st = valid_to_notice(mech, target, wlf)) && seeanew < 3) {
		if (st & AUTOCON_WARN)
		    strcpy(buf, "%cy");
		else
		    buf[0] = 0;
		if (st & AUTOCON_SHORT)
		    sprintf(buf + strlen(buf), "Lost: %s, %s arc.",
			GetMechToMechID_base(mech, target, wlf),
			GetArcID(mech, arc));
		else
		    sprintf(buf,
			"You have lost %s from your scanners. It was last in your %s arc.",
			GetMechToMechID_base(mech, target, wlf),
			GetArcID(mech, arc));
		if (st & AUTOCON_WARN)
		    strcat(buf, "%cn");
		mech_notify(mech, MECHALL, buf);
	    }
	    if ((MechStatus(mech) & LOCK_TARGET) &&
		target->mynum == MechTarget(mech)) {
		mech_notify(mech, MECHALL,
		    "Weapon system reports the lock has been lost.");
		LoseLock(mech);
	    }
#ifdef SENSOR_DEBUG
	    sprintf(buf, "Notice: #%d lost #%d (Sensor: %d, Flag: %s)",
		mech->mynum, target->mynum,
		(f & (MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS)),
		my_dump_flag(f));
	    SendSensor(buf);
#endif
	    MechPNumSeen(mech)++;
	}
	*fl = f;
	return;
    }
    if ((sc =
	    Sensor_CanSee(mech, target, &f, arc, range, mapvis, maplight,
		cloudbase))) {
	if (!seeanew) {
	    MechPNumSeen(mech)++;	/* Whee, something we _could_ see */
	    *fl = f;
	    return;
	}
	if ((sl =
		Sensor_SeesNow(mech, target, f, arc, range, mapvis,
		    maplight))) {
	    if (sc & 1)
		f |= MECHLOSFLAG_SEESP;
	    if (sc & 2)
		f |= MECHLOSFLAG_SEESS;
	}
	if ((f & (MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS))) {
	    if (MechTeam(mech) != MechTeam(target)) {
		MechNumSeen(mech)++;
		UnZombifyMech(mech);
	    }
	    f |= MECHLOSFLAG_SEEN;
	    *fl = f;
	    if ((Started(target) || SeeWhenShutdown(target) ||
		 MechAutoconSD(mech)) &&
	        (st = valid_to_notice(mech, target, -1)) && seeanew < 2) {
		if (st & AUTOCON_WARN)
		    strcpy(buf, "%cr");
		else
		    buf[0] = 0;
		if (st & AUTOCON_SHORT)
		    sprintf(buf + strlen(buf), "Seen: %s, %s arc.",
			GetMechToMechID(mech, target), GetArcID(mech, arc));
		else
		    sprintf(buf + strlen(buf),
			"You notice %s in your %s arc.",
			GetMechToMechID(mech, target), GetArcID(mech, arc));
		if (st & AUTOCON_WARN)
		    strcat(buf, "%cn");
		mech_notify(mech, MECHALL, buf);
	    }
	    if (MechTeam(mech) != MechTeam(target))
		StopHiding(target);
#ifdef SENSOR_DEBUG
	    sprintf(buf, "Notice: #%d saw #%d (Sensor: %d, Flag: %s C:%d)",
		mech->mynum, target->mynum,
		(f & (MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS)),
		my_dump_flag(f), seeanew);
	    SendSensor(buf);
#endif
	} else
	    MechPNumSeen(mech)++;
    }
    *fl = f;
}


void update_LOSinfo(dbref obj, MAP * map)
{
    int i, j, fl;
    int mapvis = map->mapvis;
    int maplight = map->maplight;
    MECH *mech, *target;
    float range;
    int wlf;

    /* First, for all moved mechs, calculate new LOS flags */
    for (i = 0; i < map->first_free; i++) {
	mech = getMech(map->mechsOnMap[i]);
	if (!mech)
	    continue;
	if (!Started(mech))
	    continue;
	for (j = i + 1; j < map->first_free; j++)
	    if (map->mechflags[i] || map->mechflags[j]) {
		target = getMech(map->mechsOnMap[j]);
		if (!target)
		    continue;
		range = FaMechRange(mech, target);
		if (ECMEnabled(mech) || ECCMEnabled(mech) ||
		    AngelECMEnabled(mech) || AngelECCMEnabled(mech))
		    if (range < ECM_RANGE)
			checkECM(target);
		if (TAGTarget(mech) > 0)
		    checkTAG(mech);
		if (MechStatus2(mech) & SLITE_ON)
		    if (range < LITE_RANGE)
			cause_lite(mech, target);
		if (range > map->maxvis && MechZ(target) < 11 &&
		    MechZ(mech) < 11) {
		    map->LOSinfo[i][j] = MECHLOSFLAG_BLOCK;
		    continue;
		}
		wlf = !(map->LOSinfo[i][j] & MECHLOSFLAG_BLOCK) &&
		    ((map->LOSinfo[i][j] & MECHLOSFLAG_SEESP) ||
		    (map->LOSinfo[i][j] & MECHLOSFLAG_SEESS));
		map->LOSinfo[i][j] = fl =
		    CalculateLOSFlag(mech, target, map, MechX(target),
		    MechY(target), map->LOSinfo[i]
		    [j], (float) range);
		/* Then, we update the SEES* */
#ifdef ADVANCED_LOS
		Sensor_DoWeSeeNow(mech, &map->LOSinfo[i][j], range, -1, -1,
		    target, mapvis, maplight, map->cloudbase, 0, wlf);
#endif
	    }
    }
    for (i = 1; i < map->first_free; i++) {
	if (map->mechsOnMap[i] > 0);
	mech = getMech(map->mechsOnMap[i]);
	if (!mech)
	    continue;
	if (!Started(mech))
	    continue;
	for (j = 0; j < i; j++)
	    if (map->mechflags[i] || map->mechflags[j]) {
		target = getMech(map->mechsOnMap[j]);
		if (!target)
		    continue;
		range = FaMechRange(mech, target);
		if (ECMEnabled(mech) || ECCMEnabled(mech) ||
		    AngelECMEnabled(mech) || AngelECCMEnabled(mech))
		    if (range < ECM_RANGE)
			checkECM(target);
		if (TAGTarget(mech) > 0)
		    checkTAG(mech);
		if (MechStatus2(mech) & SLITE_ON)
		    if (range < LITE_RANGE)
			cause_lite(mech, target);
		if (Started(target))
		    if (map->LOSinfo[j][i] & MECHLOSFLAG_BLOCK) {
			wlf = !(map->LOSinfo[i][j] & MECHLOSFLAG_BLOCK) &&
			    ((map->LOSinfo[i][j] & MECHLOSFLAG_SEESP) ||
			    (map->LOSinfo[i][j] & MECHLOSFLAG_SEESS));
			map->LOSinfo[i][j] =
			    MECHLOSFLAG_BLOCK | (map->LOSinfo[i][j] &
			    (MECHLOSFLAG_SEEN | MECHLOSFLAG_SEEC2));

#ifdef ADVANCED_LOS
			Sensor_DoWeSeeNow(mech, &map->LOSinfo[i][j], range,
			    -1, -1, target, mapvis, maplight,
			    map->cloudbase, 0, wlf);
#endif
			continue;
		    }
		if (range > map->maxvis && MechZ(target) < 11 &&
		    MechZ(mech) < 11) {
		    map->LOSinfo[i][j] = MECHLOSFLAG_BLOCK;
		    continue;
		}
		/* Then, we update the SEES* */
		wlf = !(map->LOSinfo[i][j] & MECHLOSFLAG_BLOCK) &&
		    ((map->LOSinfo[i][j] & MECHLOSFLAG_SEESP) ||
		    (map->LOSinfo[i][j] & MECHLOSFLAG_SEESS));
		map->LOSinfo[i][j] = fl =
		    CalculateLOSFlag(mech, target, map, MechX(target),
		    MechY(target), map->LOSinfo[i]
		    [j], (float) range);
		Sensor_DoWeSeeNow(mech, &map->LOSinfo[i][j], range, -1, -1,
		    target, mapvis, maplight, map->cloudbase, 0, wlf);
	    }
    }
    for (i = 0; i < map->first_free; i++)
	map->mechflags[i] = 0;
}

void add_sensor_info(char *buf, MECH * mech, int sn, int verbose)
{
    if (!verbose)
	sprintf(buf + strlen(buf), "(R:%s)", sensors[sn].range_desc);
    else {
	sprintf(buf + strlen(buf), "\n\tRange:      %s\n\tBlocked by: %s",
	    sensors[sn].range_desc, sensors[sn].block_desc);
	if (sensors[sn].special_desc)
	    sprintf(buf + strlen(buf), "\n\tNotes:      %s",
		sensors[sn].special_desc);
    }
}

static char *sensor_mode_name(MECH * mech, int sn, int full, int verbose)
{
    static char buf[MBUF_SIZE];

    if (sn < 0 || sn >= NUM_SENSORS)
	return "None";

    if (sensors[sn].fullvision) {
	sprintf(buf, "%s ", sensors[sn].sensorname);
	add_sensor_info(buf, mech, sn, verbose);
    } else {
	if (full || Sees360(mech))
	    sprintf(buf, "%s in 360 degree scanning mode ",
		sensors[sn].sensorname);
	else
	    sprintf(buf, "%s in 120 degree scanning mode (Forward arc) ",
		sensors[sn].sensorname);
	add_sensor_info(buf, mech, sn, verbose);
    }
    return buf;
}

static void sensor_mode(MECH * mech, char *msg, dbref player, int p, int s,
    int verbose)
{
    char buf[MBUF_SIZE];
    int i;

    if (p != s) {
	for (i = 0; i < strlen(msg); i++)
	    buf[i] = '-';
	buf[strlen(msg)] = 0;
	notify(player, msg);
	notify(player, buf);
	notify(player, tprintf("Primary:   %s", sensor_mode_name(mech, p,
		    0, verbose)));
	notify(player, tprintf("Secondary: %s", sensor_mode_name(mech, s,
		    0, verbose)));
    } else
	notify(player, tprintf("%s: %s", msg, sensor_mode_name(mech, p, 1,
		    verbose)));
}

static int tmp_prim;
static int tmp_sec;
static int tmp_found;

static void sensor_check(MUXEVENT * e)
{
    int d = ((int) e->data2);

    tmp_prim = d / NUM_SENSORS;
    tmp_sec = d % NUM_SENSORS;
    tmp_found = 1;
}

static char SensorInf[] = "vliesrbVLIESRB";

char *mechSensorInfo(int mode, MECH * mech, char *arg)
{
    static char buffer[5];

    tmp_found = 0;
    buffer[0] = SensorInf[(short) MechSensor(mech)[0]];
    buffer[1] = SensorInf[(short) MechSensor(mech)[1]];
    if (SensorChange(mech)) {
	muxevent_gothru_type_data(EVENT_SCHANGE, (void *) mech, sensor_check);
	if (tmp_found) {
	    buffer[2] = SensorInf[tmp_prim + NUM_SENSORS];
	    buffer[3] = SensorInf[tmp_sec + NUM_SENSORS];
	    buffer[4] = '\0';
	    return buffer;
	}
    }
    buffer[2] = '\0';
    return buffer;
}


static void show_sensor(dbref player, MECH * mech, int verbose)
{

    tmp_found = 0;
    sensor_mode(mech, "Sensors", player, MechSensor(mech)[0],
	MechSensor(mech)[1], verbose);
    if (SensorChange(mech)) {
	muxevent_gothru_type_data(EVENT_SCHANGE, (void *) mech, sensor_check);
	if (tmp_found)
	    sensor_mode(mech, "Wanted", player, tmp_prim, tmp_sec, 0);
    }
}

static void mech_sensorchange_event(MUXEVENT * e)
{
    int d = (int) e->data2;
    MECH *mech = (MECH *) e->data;
    int prim = d / NUM_SENSORS;
    int sec = d % NUM_SENSORS;

    if (!Started(mech))
	return;
    MechSensor(mech)[0] = prim;
    MechSensor(mech)[1] = sec;
    mech_notify(mech, MECHALL,
	"As your sensors change, your lock clears.");
    MechTarget(mech) = -1;
    MarkForLOSUpdate(mech);
}

int CanChangeTo(MECH * mech, int s)
{
    MAP *map;
    int i;

    if (!(map = getMap(mech->mapindex))) {
	mech_notify(mech, MECHALL, "Where are you? ;-)");
	return 0;
    }
    /* < 0, means you you don't have the sensors if you _do_ have the bit
       > 0, means you have the sensors if you have the bit */
    if ((i = sensors[s].required_special)) {
	/* original specials struct */
	if (sensors[s].specials_set == 1) {
	    if ((i > 0) == ((!(MechSpecials(mech) & abs(i))) != 0)) {
		mech_notify(mech, MECHALL,
		    tprintf("You lack the %s sensors!",
			sensors[s].sensorname));
		return 0;
	    }
	} else {
	    if ((i > 0) == ((!(MechSpecials2(mech) & abs(i))) != 0)) {
		mech_notify(mech, MECHALL,
		    tprintf("You lack the %s sensors!",
			sensors[s].sensorname));
		return 0;
	    }
	}
    }

    if (sensors[s].min_light >= 0 && sensors[s].min_light > map->maplight) {
	if (!Destroyed(mech) && Started(mech))
	    mech_notify(mech, MECHALL,
		tprintf("It's now too dark to use %s!",
		    sensors[s].sensorname));
	return 0;
    }
    if (sensors[s].max_light >= 0 && sensors[s].max_light < map->maplight) {
	if (!Destroyed(mech) && Started(mech))
	    mech_notify(mech, MECHALL,
		tprintf("The light's kinda too bright now to use %s!",
		    sensors[s].sensorname));
	return 0;
    }

    switch (sensors[s].attributeCheck) {
    case SENSOR_ATTR_SEISMIC:
	if ((MechType(mech) == CLASS_MW) ||
	    (MechType(mech) == CLASS_BSUIT) ||
	    (MechType(mech) == CLASS_VEH_NAVAL) ||
	    (MechMove(mech) == MOVE_HOVER)) {
	    mech_notify(mech, MECHALL,
		tprintf("You lack the %s sensors!",
		    sensors[s].sensorname));
	    return 0;
	}

	break;
    }

    return 1;
}

void sensor_light_availability_check(MECH * mech)
{
    int p = MechSensor(mech)[0], s = MechSensor(mech)[1];
    int same = (p == s);

    if (sensors[p].min_light >= 0 || sensors[p].max_light >= 0)
	if (!CanChangeTo(mech, p))
	    MechSensor(mech)[0] = 0;

    if (!same && (sensors[s].min_light >= 0 || sensors[s].max_light >= 0))
	if (!CanChangeTo(mech, s))
	    MechSensor(mech)[1] = 0;
}

static int set_sensor(MECH * mech, char ps, char ss)
{
    int prim = -1, sec = -1;
    int i;

    if (!Started(mech))
	return 0;
    for (i = 0; i < NUM_SENSORS; i++) {
	if (sensors[i].matchletter[0] == ps)
	    prim = i;
	if (sensors[i].matchletter[0] == ss)
	    sec = i;
    }
    if (prim < 0 || sec < 0)
	return -1;
    if (prim != MechSensor(mech)[0] || sec != MechSensor(mech)[1]) {
	if (!CanChangeTo(mech, prim))
	    return -1;
	if (!CanChangeTo(mech, sec))
	    return -1;
	StopSensorChange(mech);
	MECHEVENT(mech, EVENT_SCHANGE, mech_sensorchange_event,
	    SCHANGE_TICK, ((prim * NUM_SENSORS) + sec));
    }
    return 0;
}

void mech_sensor(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    char *args[3];
    int argc;

    if (!mech)
	return;
#ifdef SIMPLE_SENSORS
    notify(player,
	"Sensors are unavailable, as regrettable as it may be.");
    return;
#endif
    DOCHECK(MechType(mech) == CLASS_MW,
	"You're using your eyes, and nothing you can do changes that!");
    argc = mech_parseattributes(buffer, args, 2);
    DOCHECK(argc > 2, "Invalid number of arguments!");
    switch (argc) {
    case 0:
	show_sensor(player, mech, 0);
	break;
    case 1:
	show_sensor(player, mech, 1);
	break;
    case 2:
	DOCHECK(set_sensor(mech, toupper(args[0][0]), toupper(args[1][0]))
	    < 0, "Invalid arguments!");
	show_sensor(player, mech, 0);
	break;
    }
}

void possibly_see_mech(MECH * mech)
{
    MAP *map = getMap(mech->mapindex);
    int i, j;
    MECH *seer;
    int mapvis;
    int maplight;
    float range;
    int num = mech->mapnumber;

    if (!map)
	return;
    mapvis = map->mapvis;
    maplight = map->maplight;
    /* This is quiet ; no message for noticing foe etc */
    /* Basically, this is a 'bonus' effect in addition to the movement-caused
       effects, but just done once / move of the guy */
    for (i = 0; i < map->first_free; i++)
	if (i != num && (j = map->mechsOnMap[i]) >= 0) {
	    if (!(seer = getMech(j)))
		continue;
	    if (seer->mapindex != map->mynum) {
		SendError(tprintf("Mech #%d was on map #%d but with "
				  "incorrect mapindex (%d)", seer->mynum,
				  map->mynum, seer->mapindex));
		map->mechsOnMap[i] = -1;
		continue;
	    }
	    range = FaMechRange(seer, mech);
	    map->LOSinfo[i][num] =
		CalculateLOSFlag(seer, mech, map, MechX(mech), MechY(mech),
		map->LOSinfo[i][num], (float) range);
	    /* Then, we update the SEES* */
	    /* seeanew used to be 2 ; I want them to know they notice
	       it first not to bug me 'bout it, though */
#ifdef ADVANCED_LOS
	    Sensor_DoWeSeeNow(seer, &map->LOSinfo[i][num], range, -1, -1,
		mech, mapvis, maplight, map->cloudbase, 2, 0);
#endif
	}
}


static void mech_unblind_event(MUXEVENT * e)
{
    MECH *m = (MECH *) e->data;

    MechStatus(m) &= ~BLINDED;
    if (!Uncon(m))
	mech_notify(m, MECHALL, "Your sight recovers.");
}

void ScrambleInfraAndLiteAmp(MECH * mech, int time, int chance,
    char *inframsg, char *liteampmsg)
{
    MAP *mech_map = getMap(mech->mapindex);
    int i;
    MECH *tempMech;

    possibly_see_mech(mech);
    for (i = 0; i < mech_map->first_free; i++)
	if (mech_map->mechsOnMap[i] != -1 &&
	    mech_map->mechsOnMap[i] != mech->mynum)
	    if ((tempMech = getMech(mech_map->mechsOnMap[i])))
		if (InLineOfSight(tempMech, mech, MechX(mech), MechY(mech),
			FaMechRange(tempMech, mech))) {
		    if (Blinded(tempMech) || Uncon(tempMech))
			continue;
		    if (sensors[(int)
			    MechSensor(tempMech)[0]].matchletter[0] == 'I'
			|| sensors[(int)
			    MechSensor(tempMech)
			    [0]].matchletter[1] == 'I') {
			if (chance)
			    if (Number(1, 100) > chance)
				continue;
			/* Infra effect */
			mech_notify(tempMech, MECHALL, inframsg);
		    } else if (sensors[(int)
			    MechSensor(tempMech)[0]].matchletter[0]
			== 'L' || sensors[(int)
			    MechSensor(tempMech)
			    [0]].matchletter[1]
			== 'L') {
			if (chance)
			    if (Number(1, 100) > chance)
				continue;
			/* Liteamp effect */
			mech_notify(tempMech, MECHALL, liteampmsg);
		    } else
			continue;
		    MechStatus(tempMech) |= BLINDED;
		    MECHEVENT(tempMech, EVENT_BLINDREC, mech_unblind_event,
			time, 0);
		}
}