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: aero.move.c,v 1.2 2005/08/10 14:09:34 av1-op 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
 *
 * Last modified: Mon Jul 20 00:34:50 1998 fingon
 *
 */

#define MIN_TAKEOFF_SPEED 3

#include <math.h>
#include "muxevent.h"
#include "mech.h"
#include "mech.events.h"
#include "p.mech.sensor.h"
#include "p.mech.update.h"
#include "p.artillery.h"
#include "p.mech.combat.h"
#include "p.mech.combat.misc.h"
#include "p.mech.utils.h"
#include "p.econ_cmds.h"
#include "p.mech.ecm.h"
#include "p.mech.tag.h"
#include "p.mech.lite.h"
#include "spath.h"
#include "p.mine.h"

struct land_data_type {
    int type;
    int maxvertup;
    int maxvertdown;
    int minhoriz;
    int maxhoriz;
    int launchvert;
    int launchtime;		/* In secs */
    char *landmsg;
    char *landmsg_others;
    char *takeoff;
    char *takeoff_others;
}				/*            maxvertup / maxvertdown / minhoriz / maxhoriz / launchv /

				   launchtime */ land_data[] = {
    {
    CLASS_VTOL, 10, -60, -15, 15, 5, 0,
	    "You bring your VTOL to a safe landing.", "lands.",
	    "The rotor whines overhead as you lift off into the sky.",
	    "takes off!"}, {
    CLASS_AERO, 10, -30, MIN_TAKEOFF_SPEED * MP1, 999, 20, 10,
	    "You land your AeroFighter safely.", "lands safely.",
	    "The Aerofighter launches into the air!",
	    "launches into the air!"}, {
    CLASS_DS, 10, -25, MIN_TAKEOFF_SPEED * MP1, 999, 20, 300,
	    "The DropShip lands safely.", "lands safely.",
	    "The DropShip's nose lurches upward, and it starts climbing to the sky!",
	    "starts climbing to the sky!"}, {
    CLASS_SPHEROID_DS, 15, -40, -40, 40, 20, 300,
	    "The DropShip touches down safely.",
	    "touches down, and settles.",
	    "The DropShip slowly lurches upwards as engines battle the gravity..",
	    "starts climbing up to the sky!"}
};

#define NUM_LAND_TYPES (sizeof(land_data)/sizeof(struct land_data_type))

static void aero_takeoff_event(MUXEVENT * e)
{
    MECH *mech = (MECH *) e->data;
    MAP *map = getMap(mech->mapindex);
    int i = -1;
    int count = (int) e->data2;

    if (IsDS(mech))
	for (i = 0; i < NUM_LAND_TYPES; i++)
	    if (MechType(mech) == land_data[i].type)
		break;
    if (count > 0) {
	if (count > 5) {
	    if (!(count % 10))
		mech_notify(mech, MECHALL, tprintf("Launch countdown: %d.",
			count));
	} else
	    mech_notify(mech, MECHALL, tprintf("Launch countdown: %d.",
		    count));
	if (i >= 0) {
	    if (count == (land_data[i].launchtime / 4))
		DSSpam_O(mech,
		    "'s engines start to glow with unbearable intensity..");
	    switch (count) {
	    case 10:
		DSSpam_O(mech, "'s engines are almost ready to lift off!");
		break;
	    case 6:
		DSSpam_O(mech,
		    "'s engines generate a tremendous heat wave!");
		ScrambleInfraAndLiteAmp(mech, 2, 0,
		    "The blinding flash of light momentarily blinds you!",
		    "The blinding flash of light momentarily blinds you!");
		break;
	    case 2:
		mech_notify(mech, MECHALL,
		    "The engines pulse out a stream of superheated plasma!");
		DSSpam_O(mech,
		    "'s engines send forth a tremendous stream of superheated plasma!");
		ScrambleInfraAndLiteAmp(mech, 4, 0,
		    "The blinding flash of light blinds you!",
		    "The blinding flash of light blinds you!");
		break;
	    case 1:
		DS_BlastNearbyMechsAndTrees(mech,
		    "You receive a direct hit!",
		    "is caught in the middle of the inferno!",
		    "You are hit by the wave!", "gets hit by the wave!",
		    "are instantly burned to ash!", 400);
		break;
	    }
	}
	MECHEVENT(mech, EVENT_TAKEOFF, aero_takeoff_event, 1, (void *)
	    (count - 1));
	return;
    }
    if (i < 0) {
	if (RollingT(mech) && MechSpeed(mech) < (MIN_TAKEOFF_SPEED * MP1)) {
	    mech_notify(mech, MECHALL,
		"You're moving too slowly to lift off!");
	    return;
	}
	for (i = 0; i < NUM_LAND_TYPES; i++)
	    if (MechType(mech) == land_data[i].type)
		break;
    }
    StopSpinning(mech);
    mech_notify(mech, MECHALL, land_data[i].takeoff);
    MechLOSBroadcast(mech, land_data[i].takeoff_others);
    MechStartFX(mech) = 0;
    MechStartFY(mech) = 0;
    MechStartFZ(mech) = 0;
    if (IsDS(mech))
	SendDSInfo(tprintf("DS #%d has lifted off at %d %d "
		"on map #%d", mech->mynum, MechX(mech), MechY(mech),
		map->mynum));
    if (MechCritStatus(mech) & HIDDEN) {
        mech_notify(mech, MECHALL, "You move too much and break your cover!");
        MechLOSBroadcast(mech, "breaks its cover in the brush.");
        MechCritStatus(mech) &= ~(HIDDEN);
    }
    if (MechType(mech) != CLASS_VTOL) {
	MechDesiredAngle(mech) = 90;
	MechDesiredSpeed(mech) = MechMaxSpeed(mech) * 2 / 3;
    } else {
	MechSpeed(mech) = 0;
	MechDesiredSpeed(mech) = 0;
	MechVerticalSpeed(mech) = 60.0;
    }
    ContinueFlying(mech);
    MaybeMove(mech);
}

void aero_takeoff(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    MAP *map = getMap(mech->mapindex);
    int i, j;

    for (i = 0; i < NUM_LAND_TYPES; i++)
	if (MechType(mech) == land_data[i].type)
	    break;

    if ((j = atoi(buffer)))
	    DOCHECK(!WizP(player), "Insufficient access!");

    DOCHECK(TakingOff(mech),
	"The launch sequence has already been initiated!");
    DOCHECK(i == NUM_LAND_TYPES, "This vehicle type cannot takeoff!");
    cch(MECH_USUAL);
    DOCHECK(!FlyingT(mech),
	"Only VTOL, Aerospace fighters and Dropships can take off.");
    DOCHECK(!Landed(mech), "You haven't landed!");
    if (Fallen(mech) || (MMaxSpeed(mech) <= MP1) ||
	(SectIsDestroyed(mech, ROTOR))) {
	DOCHECK(MechType(mech) == CLASS_VTOL, "The rotor's dead!");
	notify(player, "The engines are dead!");
	return;
    }
    if (!AeroFreeFuel(mech) && AeroFuel(mech) < 1) {
	DOCHECK(MechType(mech) == CLASS_VTOL, "Your VTOL's out of fuel!");
	notify(player,
	    "Your craft's out of fuel! No taking off until it's refueled.");
	return;
    }
    DOCHECK(MechType(mech) == CLASS_AERO &&
	MechSpeed(mech) < (MIN_TAKEOFF_SPEED * MP1),
	"You're moving too slowly to take off!");
    DOCHECK(MapIsUnderground(map),
	"Realize the ceiling in this grotto is a bit to low for that!");
    if (land_data[i].launchtime > 0)
	mech_notify(mech, MECHALL,
	    "Launch sequence initiated.. type 'land' to abort it.");
    DSSpam(mech, "starts warming engines for liftoff!");
    if (IsDS(mech))
        SendDSInfo(tprintf("DS #%d has started takeoff at %d %d on map #%d",
            mech->mynum, MechX(mech), MechY(mech), map->mynum));
    if (MechCritStatus(mech) & HIDDEN) {
        mech_notify(mech, MECHALL, "You break your cover to takeoff!");
        MechLOSBroadcast(mech, "breaks its cover as it begins takeoff.");
        MechCritStatus(mech) &= ~(HIDDEN);
    }
    StopHiding(mech);
    MECHEVENT(mech, EVENT_TAKEOFF, aero_takeoff_event, 1, (void *)
	j ? j : land_data[i].launchtime);
}

#define NUM_NEIGHBORS 7

void DS_BlastNearbyMechsAndTrees(MECH * mech, char *hitmsg, char *hitmsg1,
    char *nearhitmsg, char *nearhitmsg1, char *treehitmsg, int damage)
{
    MAP *map = getMap(mech->mapindex);
    int x = MechX(mech), y = MechY(mech), z = MechZ(mech);
    int x1, y1, x2, y2, d;
    int rng = (damage > 100 ? 5 : 3);

    for (x1 = x - rng; x1 <= (x + rng); x1++)
	for (y1 = y - rng; y1 <= (y + rng); y1++) {
	    x2 = BOUNDED(0, x1, map->map_width - 1);
	    y2 = BOUNDED(0, y1, map->map_height - 1);
	    if (x1 != x2 || y1 != y2)
		continue;
	    if ((d = MyHexDist(x, y, x1, y1, 0)) > rng)
		continue;
	    d = MAX(1, d);
	    switch (GetRTerrain(map, x1, y1)) {
	    case LIGHT_FOREST:
	    case HEAVY_FOREST:
		if (!find_decorations(map, x1, y1)) {
		    HexLOSBroadcast(map, x1, y1,
			tprintf("%%ch%%crThe trees in $h %s%%cn",
			    treehitmsg));
		    if ((damage / d) > 100) {
			SetTerrain(map, x1, y1, ROUGH);
		    } else {
			add_decoration(map, x1, y1, TYPE_FIRE, FIRE,
			    FIRE_DURATION);
		    }
		}
		break;
	    }
	}
    MechZ(mech) = z + 6;
    blast_hit_hexesf(map, damage, 5, damage / 2, MechFX(mech),
	MechFY(mech), MechFX(mech), MechFY(mech), hitmsg, hitmsg1,
	nearhitmsg, nearhitmsg1, 0, 4, 4, 1, rng);
    MechZ(mech) = z;
}

enum {
    NO_ERROR, INVALID_TERRAIN, UNEVEN_TERRAIN, BLOCKED_LZ
};

char *reasons[] = {
    "Improper terrain",
    "Uneven ground",
    "Blocked landing zone"
};

static int improper_lz_status;
static int improper_lz_height;

static void ImproperLZ_callback(MAP *map, int x, int y)
{
    if (Elevation(map, x, y) != improper_lz_height)
        improper_lz_status = 0;
    else
        improper_lz_status++;
}

#define MechCheckLZ(m)	ImproperLZ((m), MechX((m)), MechY((m)))

int ImproperLZ(MECH * mech, int x, int y)
{
    MAP * map = getMap(mech->mapindex);

    if (GetRTerrain(map, x, y) != GRASSLAND &&
        GetRTerrain(map, x, y) != ROAD)
        return INVALID_TERRAIN;

    improper_lz_status = 0;
    improper_lz_height = Elevation(map, x, y);
    visit_neighbor_hexes(map, x, y, ImproperLZ_callback);

    if (improper_lz_status != 6)
        return UNEVEN_TERRAIN;
    if (is_blocked_lz(mech, map, x, y))
        return BLOCKED_LZ;
    return NO_ERROR;
}

void aero_land(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    MAP *map = getMap(mech->mapindex);
    int i, t;
    double horiz = 0.0;
    int vert, vertmin = 0, vertmax = 0;

    DOCHECK(MechType(mech) != CLASS_VTOL &&
	MechType(mech) != CLASS_AERO &&
	!IsDS(mech), "You can't land this type of vehicle.");
    DOCHECK(MechType(mech) == CLASS_VTOL &&
	AeroFuel(mech) <= 0 &&
	!AeroFreeFuel(mech), "You lack fuel to maneuver for landing!");

    for (i = 0; i < NUM_LAND_TYPES; i++)
	if (MechType(mech) == land_data[i].type)
	    break;
    if (i == NUM_LAND_TYPES)
	return;
    DOCHECK((Fallen(mech)) &&
	(MechType(mech) == CLASS_VTOL), "The rotor's dead!");
    DOCHECK((Fallen(mech)) &&
	(MechType(mech) != CLASS_VTOL), "The engines are dead!");
    if (MechStatus(mech) & LANDED) {
	if (TakingOff(mech)) {
	    mech_notify(mech, MECHALL, tprintf("Launch aborted by %s.",
		    Name(player)));
	    if (IsDS(mech))
		SendDSInfo(tprintf("DS #%d aborted takeoff at %d %d "
			"on map #%d", mech->mynum, MechX(mech),
			MechY(mech), map->mynum));
	    StopTakeOff(mech);
	    return;
	}
	notify(player, "You're already landed!");
	return;
    }
    DOCHECK(MechZ(mech) > MechElevation(mech) + 1,
	"You are too high to land here.");
    DOCHECK(((horiz =
		my_sqrtm((double) MechDesiredSpeed(mech),
		    (double) MechVerticalSpeed(mech))) >=
	    ((double) 1.0 + land_data[i].maxhoriz)),
	"You're moving too fast to land.");
    DOCHECK(horiz < land_data[i].minhoriz,
	"You're moving too slowly to land.");
    DOCHECK(((vert = MechVerticalSpeed(mech)) > (vertmax =
		land_data[i].maxvertup)) ||
	(MechVerticalSpeed(mech) < (vertmin =
		land_data[i].maxvertdown)),
	"You are moving too fast to land. ");
    if (MechSpeed(mech) < land_data[i].minhoriz) {
	if (MechStartFZ(mech) <= 0)
	    notify(player,
		"You're falling, not landing! Pick up some horizontal speed first.");
	else
	    notify(player, "You're climbing not landing!");
	return;
    }
    t = MechRTerrain(mech);
    DOCHECK(!(t == GRASSLAND || t == ROAD || (MechType(mech) == CLASS_VTOL
		&& t == BUILDING)),
	"You can't land on this type of terrain.");
    if (MechType(mech) != CLASS_VTOL && MechCheckLZ(mech)) {
	mech_notify(mech, MECHALL,
	    "This location is no good for landing!");
	return;
    }
    if (IsDS(mech))
	SendDSInfo(tprintf("DS #%d has landed at %d %d on map #%d",
		mech->mynum, MechX(mech), MechY(mech), map->mynum));

    mech_notify(mech, MECHALL, land_data[i].landmsg);
    MechLOSBroadcast(mech, land_data[i].landmsg_others);
    MechZ(mech) = MechElevation(mech);
    MechFZ(mech) = ZSCALE * MechZ(mech);
    MechStatus(mech) |= LANDED;
    MechSpeed(mech) = 0.0;
    MechVerticalSpeed(mech) = 0.0;
    MechStartFX(mech) = 0.0;
    MechStartFY(mech) = 0.0;
    MechStartFZ(mech) = 0.0;
    possible_mine_poof(mech, MINE_LAND);
}

void aero_ControlEffect(MECH * mech)
{
    if (Spinning(mech))
	return;
    if (Destroyed(mech))
	return;
    if (Landed(mech))
	return;
    mech_notify(mech, MECHALL, "You lose control of your craft!");
    MechLOSBroadcast(mech, "spins out of control!");
    StartSpinning(mech);
    MechStartSpin(mech) = mudstate.now;
}

void ds_BridgeHit(MECH * mech)
{
    /* Implementation: Kill all players on bridge :-) */
    if (Destroyed(mech))
	return;
    if (In_Character(mech->mynum))
	mech_notify(mech, MECHALL,
	    "SHIT! The shot seems to be coming straight for the bridge!");
    KillMechContentsIfIC(mech->mynum);
}

#define degsin(a) ((double) sin((double) (a) * M_PI / 180.0))
#define degcos(a) ((double) cos((double) (a) * M_PI / 180.0))

void aero_UpdateHeading(MECH * mech)
{
    /* Heading things are done in speed now, odd as though it might 
       seem */
    if (SpheroidDS(mech))
	UpdateHeading(mech);
}

double length_hypotenuse(double x, double y)
{
    if (x < 0)
	x = -x;
    if (y < 0)
	y = -y;
    return sqrt(x * x + y * y);
}

double my_sqrtm(double x, double y)
{
    double d;

    if (x < 0)
	x = -x;
    if (y < 0)
	y = -y;
    if (y > x) {
	d = y;
	y = x;
	x = d;
    }
    return sqrt(x * x - y * y);
}

#define AERO_BONUS 3

void aero_UpdateSpeed(MECH * mech)
{
    float xypart;
    float wx, wy, wz;
    float nx, ny, nz;
    float nh;
    float dx, dy, dz;
    float vlen, mod;
    float sp;
    float ab = 0.7;
    float m = 1.0;

    if (Spinning(mech)) {
	MechDesiredSpeed(mech) =
	    BOUNDED(0, MechDesiredSpeed(mech) + Number(1, 10),
	    MMaxSpeed(mech));
	MechDesiredAngle(mech) =
	    MAX(-90, MechDesiredAngle(mech) - Number(1, 15));
	MechDesiredFacing(mech) =
	    AcceptableDegree(MechDesiredFacing(mech) + Number(-3, 3));
    }
    wz = MechDesiredSpeed(mech) * degsin(MechDesiredAngle(mech));
    if (MechType(mech) == CLASS_AERO)
	ab = 2.5;
    if (MechZ(mech) < ATMO_Z)
	ab = ab / 2;
    /* First, we calculate the vector we want to be going */
    xypart = MechDesiredSpeed(mech) * degcos(MechDesiredAngle(mech));
    if (AeroFuel(mech) < 0) {
	wz = wz / 5.0;
	xypart = xypart / 5.0;
    }
    if (xypart < 0)
	xypart = 0 - xypart;
    m = ACCEL_MOD;
    FindComponents(m * xypart, MechDesiredFacing(mech), &wx, &wy);
    wz = wz * m;

    /* Then, we calculate the present heading / speed */
    nx = MechStartFX(mech);
    ny = MechStartFY(mech);
    nz = MechStartFZ(mech);

    /* Ok, we've present heading / speed */
    /* Next, we make vector from n[xyz] -> w[xyz] */
    dx = wx - nx;
    dy = wy - ny;
    dz = wz - nz;
    vlen = length_hypotenuse(length_hypotenuse(dx, dy), dz);
    if (!(vlen > 0.0))
	return;
    if (vlen > (m * ab * MMaxSpeed(mech) / AERO_SECS_THRUST)) {
	mod = (float) ab *m * MMaxSpeed(mech) / AERO_SECS_THRUST / vlen;

	dx *= mod;
	dy *= mod;
	dz *= mod;
	/* Ok.. we've a new modified speed vector */
    }
    nx += dx;
    ny += dy;
    nz += dz;
    /* Then, we need to calculate present heading / speed / verticalspeed */
    nh = (float) atan2(ny, nx) / TWOPIOVER360;
    if (!SpheroidDS(mech))
	SetFacing(mech, AcceptableDegree((int) nh + 90));
    xypart = length_hypotenuse(nx, ny);
    MechSpeed(mech) = xypart;
    sp = length_hypotenuse(length_hypotenuse(nx, ny), nz);	/* Whole speed */
    MechVerticalSpeed(mech) = nz;
    if (!SpheroidDS(mech) && fabs(MechSpeed(mech)) < MP1)
	SetFacing(mech, MechDesiredFacing(mech));
    MechStartFX(mech) = nx;
    MechStartFY(mech) = ny;
    MechStartFZ(mech) = nz;
}

int FuelCheck(MECH * mech)
{
    int fuelcost = 1;

    /* We don't do anything particularly nasty to shutdown things */
    if (!Started(mech))
	return 0;
    if (AeroFreeFuel(mech))
	return 0;
    if (fabs(MechSpeed(mech)) > MMaxSpeed(mech)) {
	if (MechZ(mech) < ATMO_Z)
	    fuelcost = abs(MechSpeed(mech) / MMaxSpeed(mech));
    } else if (fabs(MechSpeed(mech)) < MP1 &&
	fabs(MechVerticalSpeed(mech)) < MP2)
	if (Number(0, 1) == 0)
	    return 0;		/* Approximately half of the time free */
    if (AeroFuel(mech) > 0) {
	if (AeroFuel(mech) <= fuelcost)
	    AeroFuel(mech) = 0;
	else
	    AeroFuel(mech) -= fuelcost;
	return 0;
    }
    /* DropShips do not need crash ; they switch to (VERY SLOW) secondary
       power source. */
    if (IsDS(mech)) {
	if (AeroFuel(mech) < 0)
	    return 0;
	AeroFuel(mech)--;
	mech_notify(mech, MECHALL,
	    "As the fuel runs out, the engines switch to backup power.");
	return 0;
    }
    if (AeroFuel(mech) < 0)
	return 1;
    /* Now, the true nastiness begins ;) */
    AeroFuel(mech)--;
    if (!(AeroFuel(mech) % 100) && AeroFuel(mech) >= AeroFuelOrig(mech))
	SetCargoWeight(mech);
    if (MechType(mech) == CLASS_VTOL) {
	MechLOSBroadcast(mech, "'s rotors suddenly stop!");
	mech_notify(mech, MECHALL, "The sound of rotors slowly stops..");
    } else {
	MechLOSBroadcast(mech, "'s engines die suddenly..");
	mech_notify(mech, MECHALL, "Your engines die suddenly..");
    }
    MechSpeed(mech) = 0.0;
    MechDesiredSpeed(mech) = 0.0;
    if (!Landed(mech)) {
	mech_notify(mech, MECHALL,
	    "You ponder F = ma, S = F/m, S = at^2 => S=agt^2 in relation to the ground..");
	/* Start free-fall */
	MechVerticalSpeed(mech) = 0;
	/* Hmm. This _can_ be ugly if things crash in middle of fall. Oh well. */
	mech_notify(mech, MECHALL,
	    "You start free-fall.. Enjoy the ride!");
	MECHEVENT(mech, EVENT_FALL, mech_fall_event, FALL_TICK, -1);
    }
    return 1;
}

void aero_update(MECH * mech)
{
    if (Destroyed(mech))
	return;
    if (Started(mech) || Uncon(mech)) {
	UpdatePilotSkillRolls(mech);
    }
    if (Started(mech) || MechPlusHeat(mech) > 0.)
	UpdateHeat(mech);
    if (!(mudstate.now / 3 % 5)) {
	if (!Spinning(mech))
	    return;
	if (Destroyed(mech))
	    return;
	if (Landed(mech))
	    return;
	if (MadePilotSkillRoll(mech,
		(MechStartSpin(mech) - mudstate.now) / 15 + 8)) {
	    mech_notify(mech, MECHALL,
		"You recover control of your craft.");
	    StopSpinning(mech);
	}
    }
    if (Started(mech))
	MechVisMod(mech) =
	    BOUNDED(0, MechVisMod(mech) + Number(-40, 40), 100);
    checkECM(mech);
    checkTAG(mech);
    end_lite_check(mech);
}

void aero_thrust(dbref player, void *data, char *arg)
{
    MECH *mech = (MECH *) data;
    char *args[1];
    float newspeed, maxspeed;

    DOCHECK(Landed(mech), "You're landed!");
    DOCHECK(is_aero(mech) && Spinning(mech) &&
	!Landed(mech),
	"You are unable to control your craft at the moment.");
    if (mech_parseattributes(arg, args, 1) != 1) {
	notify(player, tprintf("Your current thrust is %.2f.",
		MechDesiredSpeed(mech)));
	return;
    }
    newspeed = atof(args[0]);
    if (RollingT(mech))
	DOCHECK(newspeed < (MP1 * MIN_TAKEOFF_SPEED / ACCEL_MOD),
	    tprintf("Minimum thrust you stay in air with is %.1f kph.",
		(float) MP1 * MIN_TAKEOFF_SPEED / ACCEL_MOD));
    maxspeed = MMaxSpeed(mech);
    if (!(maxspeed > 0.0))
	maxspeed = 0.0;
    DOCHECK(Fallen(mech), "Your engine's dead, no way to thrust!");
    DOCHECK(newspeed < 0,
	"Doh, thrust backwards.. where's your sense of adventure?");
    if (newspeed > maxspeed) {
	notify(player, tprintf("Maximum thrust: %.2f (%.2f kb/sec2)",
		maxspeed, maxspeed / 10));
	return;
    }
    MechDesiredSpeed(mech) = newspeed;
    mech_notify(mech, MECHALL, tprintf("Thrust set to %.2f.", newspeed));
    MaybeMove(mech);
}

void aero_vheading(dbref player, void *data, char *arg, int flag)
{
    char *args[1];
    int i = 0;
    MECH *mech = (MECH *) data;

    if (mech_parseattributes(arg, args, 1) != 1) {
	notify(player, tprintf("Present angle: %d degrees.",
		MechDesiredAngle(mech)));
	return;
    }
    i = flag * atoi(args[0]);
    if (abs(i) > 90)
	i = 90 * flag;
    DOCHECK(abs(i) != 90 && MechZ(mech) < ATMO_Z &&
	SpheroidDS(mech), tprintf("You can go only up / down at <%d z!",
	    ATMO_Z));
    if (i >= 0)
	mech_notify(mech, MECHALL,
	    tprintf("Climbing angle set to %d degrees.", i));
    else
	mech_notify(mech, MECHALL,
	    tprintf("Diving angle set to %d degrees.", 0 - i));
    MechDesiredAngle(mech) = i;
}

void aero_climb(dbref player, MECH * mech, char *arg)
{
    aero_vheading(player, mech, arg, 1);
}

void aero_dive(dbref player, MECH * mech, char *arg)
{
    aero_vheading(player, mech, arg, -1);
}

static char *colorstr(int serious)
{
    if (serious == 1)
	return "%ch%cr";
    if (serious == 0)
	return "%ch%cy";
    return "";
}

void DS_LandWarning(MECH * mech, int serious)
{
    int ilz = MechCheckLZ(mech);

    if (!ilz)
	return;
    ilz--;
    mech_notify(mech, MECHALL, tprintf("%sWARNING: %s - %s%%cn",
	    colorstr(serious), reasons[ilz],
	    serious == 1 ? "CLIMB UP NOW!!!" : serious ==
	    0 ? "No further descent is advisable." :
	    "Please do not even consider landing here."));
}

void aero_checklz(dbref player, MECH * mech, char *buffer)
{
    int ilz, argc;
    char *args[3];
    int x, y;

    cch(MECH_USUAL);

    argc = mech_parseattributes(buffer, args, 3);
    switch(argc) {
    case 2:
	x = atoi(args[0]);
	y = atoi(args[1]);
	if (!MechIsObservator(mech)) {
	    float fx, fy;
	    MapCoordToRealCoord(x, y, &fx, &fy);
	    DOCHECK(FindHexRange(MechFX(mech), MechFY(mech), fx, fy) >
		    MechTacRange(mech), "Out of range!");
	}
	break;
    case 0:
	x = MechX(mech);
	y = MechY(mech);
	break;
    default:
	notify(player, "Invalid number of parameters!");
	return;
    }

    ilz = ImproperLZ(mech, x, y);
    DOCHECKMA(!ilz,
	      tprintf("The hex (%d,%d) looks good enough for a landing.",
		      x, y));
    ilz--;
    mech_notify(mech, MECHALL,
	tprintf("The hex (%d,%d) doesn't look good for landing: %s.",
		x, y, reasons[ilz]));
}