btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
/*
 * Copyright (c) 2002 Thomas Wouters <thomas@xs4all.net>
 *
 * HUDINFO support.
 */

#include <string.h>
#include <math.h>

#include "config.h"
#include "externs.h"
#include "interface.h"

#include "muxevent.h"
#include "mech.h"
#include "map.h"
#include "map.los.h"
#include "p.mech.utils.h"
#include "p.mech.contacts.h"
#include "failures.h"
#include "p.mech.build.h"
#include "p.mech.notify.h"
#include "p.mech.update.h"
#include "p.mech.move.h"
#include "p.mech.los.h"
#include "p.mech.status.h"

extern void auto_reply(MECH * mech, char *buf);

#ifdef HUDINFO_SUPPORT

#include "hudinfo.h"

void fake_hudinfo(dbref player, dbref cause, int key, char *arg)
{
	notify(player, "HUDINFO does not work from scode or macros.");
}

HASHTAB hudinfo_cmdtab;

void init_hudinfo(void)
{
	HUDCMD *cmd;

	hashinit(&hudinfo_cmdtab, 20 * HASH_FACTOR);

	for(cmd = hudinfo_cmds; cmd->cmd; cmd++)
		hashadd(cmd->cmd, (int *) cmd, &hudinfo_cmdtab);
}

static MECH *getMech_forPlayer(dbref player)
{
	dbref inv, tmp;
	MECH *mech = NULL;

	if(Hardcode(player) && !Zombie(player))
		mech = getMech(player);

	if(!mech) {
		tmp = Location(player);
		if(Hardcode(tmp) && !Zombie(tmp))
			mech = getMech(tmp);
	}

	if(!mech) {
		SAFE_DOLIST(inv, tmp, Contents(player)) {
			if(Hardcode(inv) && !Zombie(inv))
				mech = getMech(inv);
			if(mech)
				break;
		}
	}
	return mech;
}

void do_hudinfo(DESC * d, char *arg)
{
	char *subcmd;
	HUDCMD *cmd;
	MECH *mech = NULL;

	while (*arg && isspace(*arg))
		arg++;

	if(!*arg) {
		hudinfo_notify(d, NULL, NULL, "#HUD hudinfo version "
					   HUD_PROTO_VERSION);
		return;
	}

	if(strncmp(arg, "key=", 4) == 0) {
		arg += 4;
		if(!arg || strlen(arg) > 20) {
			hudinfo_notify(d, "KEY", "E", "Invalid key");
			return;
		}
		for(subcmd = arg; *subcmd; subcmd++) {
			if(!isalnum(*subcmd)) {
				hudinfo_notify(d, "KEY", "E", "Invalid key");
				return;
			}
		}
		strcpy(d->hudkey, arg);
		hudinfo_notify(d, "KEY", "R", "Key set");
		return;
	}

	if(!d->hudkey[0]) {
		hudinfo_notify(d, "???", "E", "No session key set");
		return;
	}

	subcmd = arg;

	while (*arg && !isspace(*arg)) {
		if(!isalnum(*arg)) {
			hudinfo_notify(d, "???", "E", "Invalid subcommand");
			return;
		}
		arg++;
	}

	if(*arg) {
		*arg++ = '\0';
	}

	while (*arg && isspace(*arg))
		arg++;

	cmd = (HUDCMD *) hashfind(subcmd, &hudinfo_cmdtab);
	if(!cmd) {
		hudinfo_notify(d, "???", "E",
					   tprintf("%s: subcommand not found", subcmd));
		return;
	}

	if(cmd->flag & HUDCMD_HASARG) {
		if(!*arg) {
			hudinfo_notify(d, cmd->msgclass, "E", "Not enough arguments");
			return;
		}
	} else if(*arg) {
		hudinfo_notify(d, cmd->msgclass, "E", "Command takes no arguments");
		return;
	}

	if(cmd->flag & HUDCMD_NEEDMECH) {
		mech = getMech_forPlayer(d->player);
		if(!mech) {
			hudinfo_notify(d, cmd->msgclass, "E", "Not in a BattleTech unit");
			return;
		}
		if((cmd->flag & HUDCMD_NONDEST) && Destroyed(mech)) {
			hudinfo_notify(d, cmd->msgclass, "E", "You are destroyed!");
			return;
		}
		if((cmd->flag & HUDCMD_STARTED) && !Started(mech)) {
			hudinfo_notify(d, cmd->msgclass, "E", "Reactor is not online");
			return;
		}
		if((cmd->flag & HUDCMD_AWAKE) &&
		   (MechStatus(mech) & (BLINDED | UNCONSCIOUS))) {
			hudinfo_notify(d, cmd->msgclass, "E",
						   "You are unconscious....zzzzzzz");
			return;
		}
	}

	cmd->handler(d, mech, cmd->msgclass, arg);
	return;
}

static void FindRangeAndBearingToCenter(MECH * mech, float *rtc, int *btc)
{
	float fx, fy;

	MapCoordToRealCoord(MechX(mech), MechY(mech), &fx, &fy);
	*rtc = FindHexRange(fx, fy, MechFX(mech), MechFY(mech));
	*btc = FindBearing(MechFX(mech), MechFY(mech), fx, fy);
}

static void hud_generalstatus(DESC * d, MECH * mech, char *msgclass,
							  char *args)
{
	static char response[LBUF_SIZE];
	char fuel[15];
	char tstat[5];
	char jumpx[8], jumpy[8];
	int btc;
	float rtc;

	if(FlyingT(mech) && !AeroFreeFuel(mech))
		sprintf(fuel, "%d", AeroFuel(mech));
	else
		strcpy(fuel, "-");

	if(MechHasTurret(mech))
		sprintf(tstat, "%d", AcceptableDegree(MechTurretFacing(mech)) - 180);
	else if((MechType(mech) == CLASS_MECH && !MechIsQuad(mech)) ||
			MechType(mech) == CLASS_MW)
		sprintf(tstat, "%d",
				MechStatus(mech) & TORSO_LEFT ? -60 : MechStatus(mech) &
				TORSO_RIGHT ? 60 : 0);
	else
		strcpy(tstat, "-");

	if(Jumping(mech)) {
		snprintf(jumpx, 8, "%d", MechGoingX(mech));
		snprintf(jumpy, 8, "%d", MechGoingY(mech));
	} else {
		strcpy(jumpx, "-");
		strcpy(jumpy, "-");
	};

	FindRangeAndBearingToCenter(mech, &rtc, &btc);

	sprintf(response,
			"%s,%d,%d,%d,%d,%d,%.2f,%.2f,%d,%d,%s,%.2f,%.2f,%.3f,%d,%s,%s,%s,%s",
			MechIDS(mech, 0), MechX(mech), MechY(mech), MechZ(mech),
			MechFacing(mech), MechDesiredFacing(mech),
			MechSpeed(mech), MechDesiredSpeed(mech),
			(int) (10 * MechPlusHeat(mech)),
			(int) (10. * MechActiveNumsinks(mech)),
			fuel, MechVerticalSpeed(mech), MechVerticalSpeed(mech),
			rtc, btc, tstat, getStatusString(mech, 2), jumpx, jumpy);

	hudinfo_notify(d, msgclass, "R", response);
}

static char *hud_getweaponstatus(MECH * mech, int sect, int crit, int data)
{
	static char wstatus[12];

	if(PartIsBroken(mech, sect, crit))
		return "*";
	if(PartIsDisabled(mech, sect, crit))
		return "D";
	switch (PartTempNuke(mech, sect, crit)) {
	case FAIL_JAMMED:
		return "J";
	case FAIL_SHORTED:
		return "S";
	case FAIL_DUD:
		return "d";
	case FAIL_EMPTY:
		return "E";
	case FAIL_AMMOJAMMED:
		return "A";
	case FAIL_DESTROYED:
		return "*";
	}
	if(GetPartFireMode(mech, sect, crit) & IS_JETTISONED_MODE)
		return "j";
	if(data) {
		sprintf(wstatus, "%d", data / WEAPON_TICK);
		return wstatus;
	}
	return "R";
}

static char *hud_getfiremode(MECH * mech, int sect, int crit, int type)
{
	int mode = GetPartFireMode(mech, sect, crit);
	static char wmode[30];
	char *p = wmode;

	if(mode & RAC_TWOSHOT_MODE)
		*p++ = '2';
	if(mode & RAC_FOURSHOT_MODE)
		*p++ = '4';
	if(mode & RAC_SIXSHOT_MODE)
		*p++ = '6';
	if(mode & WILL_JETTISON_MODE)
		*p++ = 'B';
	if(mode & GATTLING_MODE)
		*p++ = 'G';
	if(mode & HOTLOAD_MODE)
		*p++ = 'H';
	if(mode & RFAC_MODE)
		*p++ = 'R';
	if((mode & ON_TC) && !(MechCritStatus(mech) & TC_DESTROYED))
		*p++ = 'T';
	if(mode & ULTRA_MODE)
		*p++ = 'U';
	if(mode & HEAT_MODE)
		*p++ = 'h';

	if(mode & (OS_USED | ROCKET_FIRED))
		*p++ = 'o';
	else if((mode & OS_MODE) || (MechWeapons[type].special & ROCKET))
		*p++ = 'O';

	if(MechWeapons[type].special & INARC)
		*p++ = 'I';
	if(MechWeapons[type].special & NARC)
		*p++ = 'N';
	if(MechWeapons[type].special & STREAK)
		*p++ = 'S';

	if(MechWeapons[type].special & AMS) {
		if(MechStatus(mech) & AMS_ENABLED)
			*p++ = 'A';
		else
			*p++ = 'a';
	}

	/* XXX Do enhanced damage */

	if(p == wmode)
		*p++ = '-';

	*p = '\0';
	return wmode;
}

static char *hud_getammomode(MECH * mech, int mode)
{
	static char amode[20];
	char *p = amode;

	if(mode & SWARM1_MODE)
		*p++ = '1';
	if(mode & ARTEMIS_MODE)
		*p++ = 'A';
	if(mode & CLUSTER_MODE)
		*p++ = 'C';
	if(mode & INARC_ECM_MODE)
		*p++ = 'E';
	if(mode & AC_FLECHETTE_MODE)
		*p++ = 'F';
	if(mode & INARC_HAYWIRE_MODE)
		*p++ = 'H';
	if(mode & INFERNO_MODE)
		*p++ = 'I';
	if(mode & LBX_MODE)
		*p++ = 'L';
	if(mode & MINE_MODE)
		*p++ = 'M';
	if(mode & NARC_MODE)
		*p++ = 'N';
	if(mode & AC_PRECISION_MODE)
		*p++ = 'P';
	if(mode & SWARM_MODE)
		*p++ = 'S';
	if(mode & AC_AP_MODE)
		*p++ = 'a';
	if(mode & INARC_EXPLO_MODE)
		*p++ = 'X';
	if(mode & AC_INCENDIARY_MODE)
		*p++ = 'e';
	if(mode & SMOKE_MODE)
		*p++ = 's';
	if(mode & STINGER_MODE)
		*p++ = 'T';
	if(mode & AC_CASELESS_MODE)
		*p++ = 'U';

	if(p == amode)
		*p++ = '-';
	*p = '\0';
	return amode;
}

static void hud_weapons(DESC * d, MECH * mech, char *msgclass, char *args)
{
	int sect, weapcount, i, weapnum = -1;
	unsigned char weaparray[MAX_WEAPS_SECTION];
	unsigned char weapdata[MAX_WEAPS_SECTION];
	int critical[MAX_WEAPS_SECTION];
	char response[LBUF_SIZE];

	UpdateRecycling(mech);

	for(sect = 0; sect < NUM_SECTIONS; sect++) {
		weapcount = FindWeapons(mech, sect, weaparray, weapdata, critical);
		if(weapcount <= 0)
			continue;
		for(i = 0; i < weapcount; i++) {
			weapnum++;
			sprintf(response, "%d,%d,%d,%s%s,%s,%s,%s",
					weapnum,
					weaparray[i],
					GetPartBrand(mech, sect, critical[i]),
					ShortArmorSectionString(MechType(mech), MechMove(mech),
											sect), GetPartFireMode(mech, sect,
																   critical
																   [i]) &
					REAR_MOUNT ? "r" : "", hud_getweaponstatus(mech, sect,
															   critical[i],
															   weapdata[i]),
					hud_getfiremode(mech, sect, critical[i], weaparray[i]),
					hud_getammomode(mech,
									GetPartAmmoMode(mech, sect,
													critical[i])));
			hudinfo_notify(d, msgclass, "L", response);
		}
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static int get_weaponmodes(int weapindx, char *firemodes, char *ammomodes,
						   char *damagetype)
{
	int spec = MechWeapons[weapindx].special;

	if(spec & PCOMBAT)
		return 0;

	switch (MechWeapons[weapindx].type) {
	case TMISSILE:
		strcat(damagetype, "M");
		if(spec & AMS) {
			strcat(damagetype, "d");
			strcat(firemodes, "Aa");
			break;
		}
		if(spec & INARC) {
			strcat(firemodes, "I");
			strcat(ammomodes, "EHe");
			break;
		}
		if(spec & NARC) {
			strcat(firemodes, "N");
			break;
		}
		if(spec & IDF) {		/* LRM */
			strcat(ammomodes, "1ACMNSs");
			strcat(damagetype, "g");
			strcat(firemodes, "Hi");
		} else if(spec & MRM)	/* MRM */
			strcat(damagetype, "g");
		else					/* SRM/SR_DFM */
			strcat(ammomodes, "AIN");
		if(spec & DFM)
			strcat(damagetype, "D");
		if(spec & ELRM)
			strcat(damagetype, "e");
		if(spec & STREAK)
			strcat(firemodes, "S");
		break;
	case TAMMO:
		if(spec & GAUSS) {
			strcat(damagetype, "G");
			if(spec & HVYGAUSS)
				strcat(damagetype, "H");
			break;
		}
		strcat(damagetype, "B");

		if(spec & CASELESS)
			strcat(damagetype, "C");
		if(spec & HYPER)
			strcat(damagetype, "Y");
		if(spec & RAC)
			strcat(firemodes, "246");
		if(spec & GMG)
			strcat(firemodes, "G");
		if(spec & RFAC) {
			strcat(firemodes, "R");
			strcat(ammomodes, "FPai");
		}
		if(spec & LBX)
			strcat(ammomodes, "L");
		if(spec & ULTRA)
			strcat(firemodes, "U");
		break;
	case TARTILLERY:
		strcat(damagetype, "Ag");
		strcat(firemodes, "Hi");
		strcat(ammomodes, "CMs");
		break;
	case TBEAM:
		strcat(damagetype, "E");
		if(spec & HVYW)
			strcat(damagetype, "h");
		if(spec & PULSE)
			strcat(damagetype, "p");
		if(spec & CHEAT)
			strcat(firemodes, "h");
		if(spec & A_POD)
			strcat(damagetype, "a");
		break;
	}
	if(spec & NOSPA)
		ammomodes[0] = '\0';
	return 1;
}

static void hud_weaponlist(DESC * d, MECH * mech, char *msgclass, char *args)
{
	int i;
	char firemodes[30] = "";
	char ammomodes[20] = "";
	char damagetype[10] = "";
	char response[LBUF_SIZE];
	struct weapon_struct *w;

	for(i = 0; i < num_def_weapons; i++) {
		firemodes[0] = ammomodes[0] = damagetype[0] = '\0';
		if(!get_weaponmodes(i, firemodes, ammomodes, damagetype))
			continue;

		if(strlen(firemodes) == 0)
			strcat(firemodes, "-");
		if(strlen(ammomodes) == 0)
			strcat(ammomodes, "-");

		w = &MechWeapons[i];
		sprintf(response,
				"%d,%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%s,%s,%s,%d", i,
				w->name, w->min, w->shortrange, w->medrange, w->longrange,
				w->min_water, w->shortrange_water, w->medrange_water,
				w->longrange_water, w->criticals, w->weight, w->damage,
				w->vrt, firemodes, ammomodes, damagetype, w->heat * 10);
		hudinfo_notify(d, msgclass, "L", response);
	}

	hudinfo_notify(d, msgclass, "D", "Done");
}

static void hud_limbstatus(DESC * d, MECH * mech, char *msgclass, char *args)
{

	int locs[] = { RLEG, LLEG, RARM, LARM };
	int todo = 3;

	UpdateRecycling(mech);

	if(MechType(mech) == CLASS_MECH) {
		for(; todo >= 0; todo--) {
			char *sect = ShortArmorSectionString(MechType(mech),
												 MechMove(mech), locs[todo]);
			char status[10];

			if(SectIsDestroyed(mech, locs[todo]))
				strcpy(status, "*");
			else if(MechSections(mech)[locs[todo]].recycle > 0)
				sprintf(status, "%d",
						MechSections(mech)[locs[todo]].recycle / WEAPON_TICK);
			else
				strcpy(status, "R");

			hudinfo_notify(d, msgclass, "L", tprintf("%s,%s", sect, status));
		}
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static void hud_ammostatus(DESC * d, MECH * mech, char *msgclass, char *args)
{
	unsigned char weapnums[8 * MAX_WEAPS_SECTION];
	unsigned short curamm[8 * MAX_WEAPS_SECTION];
	unsigned short maxamm[8 * MAX_WEAPS_SECTION];
	unsigned int ammomode[8 * MAX_WEAPS_SECTION];
	int i, ammonum;
	char response[LBUF_SIZE];

	ammonum = FindAmmunition(mech, weapnums, curamm, maxamm, ammomode, 1);

	for(i = 0; i < ammonum; i++) {
		sprintf(response, "%d,%d,%s,%d,%d", i, weapnums[i],
				hud_getammomode(mech, ammomode[i]), curamm[i], maxamm[i]);
		hudinfo_notify(d, msgclass, "L", response);
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static char hud_typechar(MECH * mech)
{

	if(MechMove(mech) == MOVE_NONE)
		return 'i';

	switch (MechType(mech)) {
	case CLASS_MECH:
		if(MechMove(mech) == MOVE_QUAD)
			return 'Q';
		return 'B';
	case CLASS_VEH_GROUND:
	case CLASS_VEH_NAVAL:
		switch (MechMove(mech)) {
		case MOVE_TRACK:
			return 'T';
		case MOVE_WHEEL:
			return 'W';
		case MOVE_HOVER:
			return 'H';
		case MOVE_HULL:
			return 'N';
		case MOVE_FOIL:
			return 'Y';
		case MOVE_SUB:
			return 'U';
		default:
			SendError(tprintf("Unknown movement type on vehicle #%d: %d",
							  mech->mynum, MechMove(mech)));
			return '?';
		}
	case CLASS_VTOL:
		return 'V';
	case CLASS_AERO:
		return 'F';
	case CLASS_DS:
		return 'A';
	case CLASS_SPHEROID_DS:
		return 'D';
	case CLASS_MW:
		return 'I';
	case CLASS_BSUIT:
		return 'S';
	}
	SendError(tprintf("Unknown unit type on unit #%d: %d (move %d)",
					  mech->mynum, MechType(mech), MechMove(mech)));
	return '?';
}

static float MaxSettableSpeed(MECH * mech)
{
	int maxspeed = MMaxSpeed(mech);

	if(MechMove(mech) == MOVE_VTOL)
		maxspeed = sqrt((float) maxspeed * maxspeed -
						MechVerticalSpeed(mech) * MechVerticalSpeed(mech));

	maxspeed = maxspeed > 0.0 ? maxspeed : 0.0;

	if((MechSpecials(mech) & TRIPLE_MYOMER_TECH) && MechHeat(mech) >= 9.)
		maxspeed = ceil((rint((maxspeed / 1.5) / MP1) + 1) * 1.5) * MP1;
	return maxspeed;
}

static float MaxVerticalSpeed(MECH * mech)
{
	int maxspeed = MMaxSpeed(mech);

	if(MechMove(mech) != MOVE_VTOL)
		return 0.0;

	maxspeed = sqrt((float) maxspeed * maxspeed -
					MechDesiredSpeed(mech) * MechDesiredSpeed(mech));
	return maxspeed;
}

static char *hud_advtech(MECH * mech)
{
	static char advtech[30];
	char *p = advtech;
	int spec = MechSpecials(mech);

	if(spec & ECM_TECH)
		*p++ = 'E';
	if(spec & BEAGLE_PROBE_TECH)
		*p++ = 'B';
	if(spec & MASC_TECH)
		*p++ = 'M';
	if(spec & AA_TECH)
		*p++ = 'R';
	if(spec & SLITE_TECH)
		*p++ = 'S';
	if(spec & TRIPLE_MYOMER_TECH)
		*p++ = 't';

	spec = MechSpecials2(mech);
	if(spec & ANGEL_ECM_TECH)
		*p++ = 'A';
	if(spec & NULLSIGSYS_TECH)
		*p++ = 'N';
	if(spec & BLOODHOUND_PROBE_TECH)
		*p++ = 'b';
	if(spec & STEALTH_ARMOR_TECH)
		*p++ = 's';

	spec = MechInfantrySpecials(mech);
	if(spec & FC_INFILTRATORII_STEALTH_TECH) {
		*p++ = 'I';
		*p++ = 'P';
	}
	if(spec & CAN_JETTISON_TECH)
		*p++ = 'J';
	if(spec & DC_KAGE_STEALTH_TECH)
		*p++ = 'K';
	if(spec & INF_ANTILEG_TECH)
		*p++ = 'L';
	if(spec & INF_SWARM_TECH)
		*p++ = 'W';
	if(spec & FWL_ACHILEUS_STEALTH_TECH)
		*p++ = 'a';
	if(spec & INF_MOUNT_TECH)
		*p++ = 'f';
	if(spec & FC_INFILTRATOR_STEALTH_TECH)
		*p++ = 'i';
	if(spec & MUST_JETTISON_TECH)
		*p++ = 'j';
	if(spec & CS_PURIFIER_STEALTH_TECH)
		*p++ = 'p';

	if(HasC3i(mech))
		*p++ = 'C';
	if(HasTAG(mech))
		*p++ = 'T';
	if(HasC3s(mech))
		*p++ = 'c';
	if(HasC3m(mech))
		*p++ = 'm';

	if(p == advtech)
		*p++ = '-';

	*p = '\0';
	return advtech;
}

static void hud_templateinfo(DESC * d, MECH * mech, char *msgclass,
							 char *args)
{
	char response[LBUF_SIZE];
	char fuel[20];

	if(FlyingT(mech) && AeroFuelOrig(mech))
		sprintf(fuel, "%d", AeroFuelOrig(mech));
	else
		strcpy(fuel, "-");

	sprintf(response, "%c,%s,%s,%.3f,%.3f,%.3f,%.3f,%s,%d,%s",
			hud_typechar(mech), MechType_Ref(mech), MechType_Name(mech),
			WalkingSpeed(MaxSettableSpeed(mech)), MaxSettableSpeed(mech),
			-WalkingSpeed(MaxSettableSpeed(mech)),
			MaxVerticalSpeed(mech), fuel, MechRealNumsinks(mech),
			hud_advtech(mech));
	hudinfo_notify(d, msgclass, "R", response);
}

static void hud_templatearmor(DESC * d, MECH * mech, char *msgclass,
							  char *args)
{
	char response[LBUF_SIZE];
	int sect;

	for(sect = 0; sect < NUM_SECTIONS; sect++) {
		if(GetSectOInt(mech, sect)) {
			sprintf(response, "%s,%d,%d,%d",
					ShortArmorSectionString(MechType(mech), MechMove(mech),
											sect), GetSectOArmor(mech, sect),
					GetSectORArmor(mech, sect), GetSectOInt(mech, sect));
			hudinfo_notify(d, msgclass, "L", response);
		}
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static void hud_armorstatus(DESC * d, MECH * mech, char *msgclass, char *args)
{
	char response[LBUF_SIZE];
	int sect;

	for(sect = 0; sect < NUM_SECTIONS; sect++) {
		if(GetSectOInt(mech, sect)) {
			sprintf(response, "%s,%d,%d,%d",
					ShortArmorSectionString(MechType(mech), MechMove(mech),
											sect), GetSectArmor(mech, sect),
					GetSectRArmor(mech, sect), GetSectInt(mech, sect));
			hudinfo_notify(d, msgclass, "L", response);
		}
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static char *hud_arcstring(int arc)
{
	static char arcstr[5];
	char *p = arcstr;

	if(arc & FORWARDARC)
		*p++ = '*';
	if(arc & LSIDEARC)
		*p++ = 'l';
	if(arc & RSIDEARC)
		*p++ = 'r';
	if(arc & REARARC)
		*p++ = 'v';
	if(arc & TURRETARC)
		*p++ = 't';

	if(p == arcstr)
		*p++ = '-';

	*p = '\0';
	return arcstr;
}

static char *hud_sensorstring(MECH * mech, int losflag)
{
	if((losflag & MECHLOSFLAG_SEESP) && (losflag & MECHLOSFLAG_SEESS))
		return "PS";
	if(losflag & MECHLOSFLAG_SEESP)
		return "P";
	if(losflag & MECHLOSFLAG_SEESS)
		return "S";
	return "-";
}

static void hud_contacts(DESC * d, MECH * mech, char *msgclass, char *args)
{
	char response[LBUF_SIZE];
	MECH *other;
	MAP *map = getMap(mech->mapindex);
	int i, losflag, bearing, btc, weaponarc, x, y;
	float range, rtc;
	char jumph[12];
	char *mechname, *constat;

	if(!map) {
		hudinfo_notify(d, msgclass, "E", "You are on no map");
		return;
	}

	for(i = 0; i < map->first_free; i++) {
		if(map->mechsOnMap[i] == mech->mynum || map->mechsOnMap[i] == -1)
			continue;

		other = (MECH *) FindObjectsData(map->mechsOnMap[i]);
		if(!other || !Good_obj(other->mynum))
			continue;

		range = FlMechRange(map, mech, other);
		x = MechX(other);
		y = MechY(other);
		losflag = InLineOfSight(mech, other, x, y, range);
		if(!losflag)
			continue;
		bearing = FindBearing(MechFX(mech), MechFY(mech), MechFX(other),
							  MechFY(other));
		weaponarc = InWeaponArc(mech, MechFX(other), MechFY(other));

		if(Jumping(other))
			sprintf(jumph, "%d", MechJumpHeading(other));
		else
			strcpy(jumph, "-");

		FindRangeAndBearingToCenter(other, &rtc, &btc);

		if(!InLineOfSight_NB(mech, other, MechX(other), MechY(other), 0.0))
			mechname = "something";
		else
			mechname = silly_atr_get(other->mynum, A_MECHNAME);

		if(!mechname || !*mechname)
			mechname = "-";

		constat = getStatusString(other, !MechSeemsFriend(mech, other));
		if(strlen(constat) == 0)
			constat = "-";

		sprintf(response,
				"%s,%s,%s,%c,%s,%d,%d,%d,%.3f,%d,%.3f,%.3f,%d,%s,%.3f,%d,%d,%.0f,%s",
				MechIDS(other, MechSeemsFriend(mech, other)),
				hud_arcstring(weaponarc), hud_sensorstring(mech, losflag),
				hud_typechar(other), mechname, MechX(other),
				MechY(other), MechZ(other), range, bearing, MechSpeed(other),
				MechVerticalSpeed(other), MechVFacing(other), jumph, rtc, btc,
				MechTons(other), MechHeat(other), constat);
		hudinfo_notify(d, msgclass, "L", response);
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static char *building_status(MAP * map, int locked)
{
	static char buildingstatus[7];
	char *p = buildingstatus;

	if(BuildIsCS(map))
		*p++ = 'C';

	if(locked)
		*p++ = 'E';
	else
		*p++ = 'F';

	if(BuildIsHidden(map))
		*p++ = 'H';

	if(BuildIsSafe(map) || (locked && BuildIsCS(map)))
		*p++ = 'X';
	else if(locked)
		*p++ = 'x';

	if(p == buildingstatus)
		*p++ = '-';

	*p = '\0';

	return buildingstatus;
}

static void hud_building_contacts(DESC * d, MECH * mech, char *msgclass,
								  char *args)
{
	char response[LBUF_SIZE];
	MAP *map = getMap(mech->mapindex);
	MAP *building_map;
	char *building_name;
	mapobj *building;
	float fx, fy, range;
	int z, losflag, locked, bearing, weaponarc;

	if(!map) {
		hudinfo_notify(d, msgclass, "E", "You are on no map");
		return;
	}

	for(building = first_mapobj(map, TYPE_BUILD); building;
		building = next_mapobj(building)) {

		MapCoordToRealCoord(building->x, building->y, &fx, &fy);
		z = Elevation(map, building->x, building->y) + 1;
		range = FindRange(MechFX(mech), MechFY(mech), MechFZ(mech), fx, fy,
						  ZSCALE * z);

		if(!building->obj)
			continue;

		building_map = getMap(building->obj);
		if(!building_map)
			continue;

		losflag = InLineOfSight(mech, NULL, building->x, building->y, range);
		if(!losflag || (losflag & MECHLOSFLAG_BLOCK))
			continue;

		if(BuildIsInvis(building_map))
			continue;

		locked = !can_pass_lock(mech->mynum, building->obj, A_LENTER);
		if(locked && BuildIsHidden(building_map))
			continue;

		bearing = FindBearing(MechFX(mech), MechFY(mech), fx, fy);
		weaponarc = InWeaponArc(mech, fx, fy);
		building_name = silly_atr_get(building->obj, A_MECHNAME);
		if(!building_name || !*building_name)
			building_name = strip_ansi(Name(building->obj));

		if(!building_name || !*building_name)
			building_name = "-";

		sprintf(response, "%s,%s,%d,%d,%d,%f,%d,%d,%d,%s",
				hud_arcstring(weaponarc), building_name, building->x,
				building->y, z, range, bearing, building_map->cf,
				building_map->cfmax, building_status(building_map, locked));
		hudinfo_notify(d, msgclass, "L", response);
	}
	hudinfo_notify(d, msgclass, "D", "Done");

};

static char hud_damstr[] = "OoxX*?";
static char hud_damagechar(MECH * mech, int sect, int type)
{
	int dummy;

	switch (type) {
	case 1:
		if(GetSectOArmor(mech, sect))
			return hud_damstr[ArmorEvaluateSerious(mech, sect, 1, &dummy)];
		return '-';
	case 2:
		if(GetSectOInt(mech, sect))
			return hud_damstr[ArmorEvaluateSerious(mech, sect, 2, &dummy)];
		return '-';
	case 4:
		if(GetSectORArmor(mech, sect))
			return hud_damstr[ArmorEvaluateSerious(mech, sect, 4, &dummy)];
		return '-';
	}
	return '?';
}

static MECH *hud_scantarget(DESC * d, MECH * mech, char *msgclass, char *args)
{
	MECH *targ;
	float range;

	if(!MechScanRange(mech)) {
		hudinfo_notify(d, msgclass, "E",
					   "Your system seems to be inoperational");
		return NULL;
	}

	if(strlen(args) != 2 ||
	   !(targ = getMech(FindTargetDBREFFromMapNumber(mech, args)))) {
		hudinfo_notify(d, msgclass, "E", "No such target");
		return NULL;
	}

	range = FaMechRange(mech, targ);

	if(!InLineOfSight(mech, targ, MechX(targ), MechY(targ), range)) {
		hudinfo_notify(d, msgclass, "E", "No such target");
		return NULL;
	}

	if(!MechIsObservator(mech) && (int) range > MechScanRange(mech)) {
		hudinfo_notify(d, msgclass, "E", "Out of range");
		return NULL;
	}

	if(MechType(targ) == CLASS_MW ||
	   !InLineOfSight_NB(mech, targ, MechX(targ), MechY(targ), range)) {
		hudinfo_notify(d, msgclass, "E", "Unable to scan target");
		return NULL;
	}

	if(!MechIsObservator(mech)) {
		mech_notify(targ, MECHSTARTED,
					tprintf("You are being scanned by %s",
							GetMechToMechID(targ, mech)));
		auto_reply(targ, tprintf("%s just scanned me.",
								 GetMechToMechID(targ, mech)));
	}
	return targ;
}

static void hud_armorscan(DESC * d, MECH * mech, char *msgclass, char *args)
{
	char response[LBUF_SIZE];
	int sect;
	MECH *targ;

	targ = hud_scantarget(d, mech, msgclass, args);
	if(!targ)
		return;

	for(sect = 0; sect < NUM_SECTIONS; sect++) {
		if(GetSectOInt(targ, sect)) {
			sprintf(response, "%s,%c,%c,%c",
					ShortArmorSectionString(MechType(targ), MechMove(targ),
											sect), hud_damagechar(targ, sect,
																  1),
					hud_damagechar(targ, sect, 4), hud_damagechar(targ, sect,
																  2));
			hudinfo_notify(d, msgclass, "L", response);
		}
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static char *hud_getweapscanstatus(MECH * mech, int sect, int crit, int data)
{
	if(PartIsNonfunctional(mech, sect, crit))
		return "*";
	if(data)
		return "-";
	return "R";
}

static void hud_weapscan(DESC * d, MECH * mech, char *msgclass, char *args)
{
	int sect, weapcount, i, weapnum = -1;
	unsigned char weaparray[MAX_WEAPS_SECTION];
	unsigned char weapdata[MAX_WEAPS_SECTION];
	int critical[MAX_WEAPS_SECTION];
	char response[LBUF_SIZE];
	MECH *targ;

	targ = hud_scantarget(d, mech, msgclass, args);
	if(!targ)
		return;

	UpdateRecycling(targ);

	for(sect = 0; sect < NUM_SECTIONS; sect++) {
		if(SectIsDestroyed(targ, sect))
			continue;
		weapcount = FindWeapons(targ, sect, weaparray, weapdata, critical);
		if(weapcount <= 0)
			continue;
		for(i = 0; i < weapcount; i++) {
			weapnum++;
			sprintf(response, "%d,%d,%s,%s", weapnum, weaparray[i],
					ShortArmorSectionString(MechType(targ), MechMove(targ),
											sect), hud_getweapscanstatus(targ,
																		 sect,
																		 critical
																		 [i],
																		 weapdata
																		 [i] /
																		 WEAPON_TICK));
			hudinfo_notify(d, msgclass, "L", response);
		}
	}
	hudinfo_notify(d, msgclass, "D", "Done");
}

static void hud_tactical(DESC * d, MECH * mech, char *msgclass, char *args)
{
	MAP *map = getMap(mech->mapindex);
	char *argv[5], result[LBUF_SIZE], *p;
	char mapid[21], mapname[MBUF_SIZE];
	int argc;
	int height = 24;
	short cx = MechX(mech), cy = MechY(mech);
	int sx, sy, ex, ey, x, y, losflag, lostactical = 0;
	hexlosmap_info *losmap = NULL;

	if(!MechLRSRange(mech)) {
		hudinfo_notify(d, msgclass, "E",
					   "Your system seems to be inoperational");
		return;
	}

	if(!map) {
		hudinfo_notify(d, msgclass, "E", "You are on no map");
		return;
	}

	argc = mech_parseattributes(args, argv, 4);

	switch (argc) {
	case 4:
		if(strcmp("l", argv[3])) {
			hudinfo_notify(d, msgclass, "E", "Invalid fourth argument");
			return;
		}
		lostactical = 1;
		/* FALLTHROUGH */
	case 3:{
			float fx, fy;
			int bearing = atoi(argv[1]);
			float range = atof(argv[2]);

			if(!MechIsObservator(mech) &&
			   abs((int) range) > MechLRSRange(mech)) {
				hudinfo_notify(d, msgclass, "E", "Out of range");
				return;
			}
			FindXY(MechFX(mech), MechFY(mech), bearing, range, &fx, &fy);
			RealCoordToMapCoord(&cx, &cy, fx, fy);
		}
		/* FALLTHROUGH */
	case 1:
		height = atoi(argv[0]);
		if(!height || height < 0 || height > 40) {
			hudinfo_notify(d, msgclass, "E", "Invalid 1st argument");
			return;
		}
		break;
	default:
		hudinfo_notify(d, msgclass, "E", "Invalid arguments");
		return;
	}

	height = MIN(height, 2 * MechLRSRange(mech));
	height = MIN(height, map->map_height);

	sy = MAX(0, cy - height / 2);
	ey = MIN(map->map_height, cy + height / 2);

	sx = MAX(0, cx - LRS_DISPLAY_WIDTH / 2);
	ex = MIN(map->map_width, cx + LRS_DISPLAY_WIDTH / 2);

	if(lostactical || MapIsDark(map) || (MechType(mech) == CLASS_MW &&
										 mudconf.btech_mw_losmap))
		losmap = CalculateLOSMap(map, mech, sx, sy, ex - sx, ey - sy);

	if(!mudconf.hudinfo_show_mapinfo ||
	   (mudconf.hudinfo_show_mapinfo == 1 && In_Character(map->mynum))) {
		strcpy(mapid, "-1");
		strcpy(mapname, "-1");
	} else {
		sprintf(mapid, "%d", map->mynum);
		sprintf(mapname, "%s", map->mapname);
	};

	sprintf(result, "%d,%d,%d,%d,%s,%s,-1,%d,%d", sx, sy, ex - 1, ey - 1,
			mapid, mapname, map->map_width, map->map_height);
	hudinfo_notify(d, msgclass, "S", result);

	for(y = sy; y < ey; y++) {
		sprintf(result, "%d,", y);
		p = result + strlen(result);

		for(x = sx; x < ex; x++) {
			if(losmap)
				losflag = LOSMap_GetFlag(losmap, x, y);
			else
				losflag = MAPLOSHEX_SEE;

			if(losflag & MAPLOSHEX_SEETERRAIN) {
				*p = GetTerrain(map, x, y);
				if(*p == ' ')
					*p = '.';
				*p++;
			} else
				*p++ = '?';

			if(losflag & MAPLOSHEX_SEEELEV)
				*p++ = GetElev(map, x, y) + '0';
			else
				*p++ = '?';
		}
		*p = '\0';
		hudinfo_notify(d, msgclass, "L", result);
	}

	hudinfo_notify(d, msgclass, "D", "Done");
}

static char *hud_getmapflags(MAP * map)
{
	static char res[5];
	char *p = res;

	if(map->flags & MAPFLAG_VACUUM)
		*p++ = 'V';
	if(map->flags & MAPFLAG_UNDERGROUND)
		*p++ = 'U';
	if(map->flags & MAPFLAG_DARK)
		*p++ = 'D';

	if(p == res)
		*p++ = '-';

	*p = '\0';
	return res;
}

static void hud_conditions(DESC * d, MECH * mech, char *msgclass, char *args)
{
	MAP *map = getMap(mech->mapindex);
	char res[200];
	char lt;

	switch (map->maplight) {
	case 0:
		lt = 'N';
		break;
	case 1:
		lt = 'T';
		break;
	case 2:
		lt = 'D';
		break;
	default:
		lt = '?';
		SendError(tprintf("Unknown light type %d on map #%d",
						  map->maplight, map->mynum));
		break;
	}

	sprintf(res, "%c,%d,%d,%d,%s", lt, map->mapvis, map->grav,
			map->temp, hud_getmapflags(map));
	hudinfo_notify(d, msgclass, "R", res);
}

#endif