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: glue.c,v 1.4 2005/08/08 09:43:09 murrayma Exp $
 *
 * Original author: unknown
 *
 * Copyright (c) 1996-2002 Markus Stenberg
 * Copyright (c) 1998-2002 Thomas Wouters 
 * Copyright (c) 2000-2002 Cord Awtry 
 *
 * Last modified: Thu Jul  9 02:40:16 1998 fingon
 *
 * This includes the basic code to allow objects to have hardcoded
 * commands / properties.
 *
 */

#include "config.h"

#include <stdio.h>
#include <sys/file.h>
#include <string.h>
#include <math.h>

#define FAST_WHICHSPECIAL

#include "create.h"

#define _GLUE_C

/*** #include all the prototype here! ****/
#include "mech.h"
#include "mech.events.h"
#include "debug.h"
#include "mechrep.h"
#include "mech.tech.h"
#include "autopilot.h"
#include "turret.h"
#include "p.ds.turret.h"
#include "coolmenu.h"
#include "mech.custom.h"
#include "p.bsuit.h"
#include "scen.h"
#include "glue.h"
#include "mux_tree.h"
#include "powers.h"
#include "ansi.h"
#include "coolmenu.h"
#include "mycool.h"
#include "p.mux_tree.h"
#include "p.mechfile.h"
#include "p.mech.stat.h"
#include "p.mech.partnames.h"

/* Prototypes */

/*************CALLABLE PROTOS*****************/

/* Main entry point */
int HandledCommand(dbref player, dbref loc, char *command);

/* called when user creates/removes hardcode flag */
void CreateNewSpecialObject(dbref player, dbref key);
void DisposeSpecialObject(dbref player, dbref key);
void list_hashstat(dbref player, const char *tab_name, HASHTAB * htab);
void raw_notify(dbref player, const char *msg);
void AddEntry(Tree * tree, muxkey_t key, dtype_t type, dsize_t size,
    void *data);
void DeleteEntry(Tree * tree, muxkey_t key);
int SaveTree(FILE * f, Tree tree);
void UpdateTree(FILE * f, Tree * tree, int (*sizefunc)(int));
void GoThruTree(Tree tree, int (*func) (Node *));


/*************PERSONAL PROTOS*****************/
void *NewSpecialObject(int id, int type);
void *FindObjectsData(dbref key);
static Node *FindObjectsNode(dbref key);
static void DoSpecialObjectHelp(dbref player, char *type, int id, int loc,
    int powerneeded, int objid, char *arg);
void initialize_colorize();

#ifndef FAST_WHICHSPECIAL

#define WhichSpecialS WhichSpecial
#define WhichType(node) WhichSpecial(NodeKey(node))
int WhichSpecial(dbref key);

#else

#define WhichType(node) NodeType(node)
int WhichSpecial(dbref key);
static int WhichSpecialS(dbref key);

#endif

/*********************************************/

HASHTAB SpecialCommandHash[NUM_SPECIAL_OBJECTS];
Tree xcode_tree = NULL;
extern int map_sizefun();


static int Can_Use_Command(MECH * mech, int cmdflag)
{
#define TYPE2FLAG(a) \
((a)==CLASS_MECH?GFLAG_MECH:(a)==CLASS_VEH_GROUND?GFLAG_GROUNDVEH:\
 (a)==CLASS_AERO?GFLAG_AERO:DropShip(a)?GFLAG_DS:(a)==CLASS_VTOL?GFLAG_VTOL:\
 (a)==CLASS_VEH_NAVAL?GFLAG_NAVAL:\
 (a)==CLASS_BSUIT?GFLAG_BSUIT:\
 (a)==CLASS_MW?GFLAG_MW:0)
    int i;

    if (!cmdflag)
	return 1;
    if (!mech || !(i = TYPE2FLAG(MechType(mech))))
	return 0;
    if (cmdflag > 0) {
	if (cmdflag & i)
	    return 1;
    } else if (!((0 - cmdflag) & i))
	return 1;
    return 0;
}

int HandledCommand_sub(dbref player, dbref location, char *command)
{
    struct SpecialObjectStruct *typeOfObject;
    Node *n = NULL;
    int type;
    CommandsStruct *cmd;
    HASHTAB *damnedhash;
    char *tmpc, *tmpchar;
    int ishelp;

    type = WhichSpecial(location);
    if (type < 0 || (SpecialObjects[type].datasize > 0 &&
	    !(n = FindNode(xcode_tree, location)))) {
	if (type >= 0 || !Hardcode(location) || Zombie(location))
	    return 0;
	if ((type = WhichSpecialS(location)) >= 0) {
	    if (SpecialObjects[type].datasize > 0)
		return 0;
	} else
	    return 0;
    }
#ifdef FAST_WHICHSPECIAL
    if (type > NUM_SPECIAL_OBJECTS)
	return 0;
#endif
    typeOfObject = &SpecialObjects[type];
    damnedhash = &SpecialCommandHash[type];
    tmpc = strstr(command, " ");
    if (tmpc)
	*tmpc = 0;
    ishelp = !strcmp(command, "HELP");
    for (tmpchar = command; *tmpchar; tmpchar++)
	*tmpchar = ToLower(*tmpchar);
    cmd = (CommandsStruct *) hashfind(command, &SpecialCommandHash[type]);
    if (tmpc)
	*tmpc = ' ';
    if (cmd && (type != GTYPE_MECH || (type == GTYPE_MECH &&
		Can_Use_Command(((MECH *)
			(NodeData(n))), cmd->flag)))) {
#define SKIPSTUFF(a) while (*a && *a != ' ') a++;while (*a == ' ') a++
	if (cmd->helpmsg[0] != '@' ||
	    Have_MechPower(Owner(player), typeOfObject->power_needed)) {
	    SKIPSTUFF(command);
	    cmd->func(player, !n ? NULL : NodeData(n), command);
	} else
	    notify(player, "Sorry, that command is restricted!");
	return 1;
    } else if (ishelp) {
	SKIPSTUFF(command);
	DoSpecialObjectHelp(player, typeOfObject->type, type, location,
	    typeOfObject->power_needed, location, command);
	return 1;
    }
    return 0;
}

#define OkayHcode(a) (a >= 0 && Hardcode(a) && !Zombie(a))

/* Main entry point */
int HandledCommand(dbref player, dbref loc, char *command)
{
    dbref curr, temp;

    if (Slave(player))
	return 0;
    if (strlen(command) > (LBUF_SIZE - MBUF_SIZE))
	return 0;
    if (OkayHcode(player) && HandledCommand_sub(player, player, command))
	return 1;
    if (OkayHcode(loc) && HandledCommand_sub(player, loc, command))
	return 1;
    SAFE_DOLIST(curr, temp, Contents(player)) {
	if (OkayHcode(curr))
	    if (HandledCommand_sub(player, curr, command))
		return 1;
#if 0				/* Recursion is evil ; let's not do that, this time */
	if (Has_contents(curr))
	    if (HandledCommand_contents(player, curr, command))
		return 1;
#endif
    }
    return 0;
}

void InitSpecialHash(int which);
void initialize_partname_tables();

static MECH *global_kludge_mech;
int global_specials = NUM_SPECIAL_OBJECTS;

static int remove_from_all_maps_func(Node * tmp)
{
    if (WhichType(tmp) == GTYPE_MAP) {
	MAP *map;
	int i;

	if (!(map = getMap(NodeKey(tmp))))
	    return 1;
	for (i = 0; i < map->first_free; i++)
	    if (map->mechsOnMap[i] == global_kludge_mech->mynum)
		map->mechsOnMap[i] = -1;
    }
    return 1;
}

void mech_remove_from_all_maps(MECH * mech)
{
    global_kludge_mech = mech;
    GoThruTree(xcode_tree, remove_from_all_maps_func);
}

static dbref except_map = -1;

static int remove_from_all_maps_except_func(Node * tmp)
{
    if (WhichType(tmp) == GTYPE_MAP) {
	int i;
	MAP *map;

	if (NodeKey(tmp) == except_map)
	    return 1;
	if (!(map = getMap(NodeKey(tmp))))
	    return 1;
	for (i = 0; i < map->first_free; i++)
	    if (map->mechsOnMap[i] == global_kludge_mech->mynum)
		map->mechsOnMap[i] = -1;
    }
    return 1;
}
void mech_remove_from_all_maps_except(MECH * mech, int num)
{
    global_kludge_mech = mech;
    except_map = num;
    GoThruTree(xcode_tree, remove_from_all_maps_except_func);
    except_map = -1;
}

static int load_update2(Node * tmp)
{
    int i = WhichType(tmp);;

    if (i == GTYPE_MECH)
	mech_map_consistency_check(NodeData(tmp));
    return 1;
}

static int load_update4(Node * tmp)
{
    MECH *mech;
    MAP *map;

#ifdef BT_ENABLED
    if (WhichType(tmp) == GTYPE_MECH) {
	mech = NodeData(tmp);
	if (!(map = getMap(mech->mapindex))) {
	    /* Ugly kludge */
	    if ((map = getMap(Location(mech->mynum))))
		mech_Rsetmapindex(GOD, mech, tprintf("%d",
			Location(mech->mynum)));
	    if (!(map = getMap(mech->mapindex)))
		return 1;
	}
	if (!Started(mech))
	    return 1;
	StartSeeing(mech);
	MaybeRecycle(mech, 1);
	MaybeMove(mech);
	if (!FlyingT(mech) && Started(mech) && Jumping(mech))
	    mech_Rsetxy(GOD, (void *) mech, tprintf("%d %d", MechX(mech),
		    MechY(mech)));
    }
#endif
    return 1;
}

static int load_update3(Node * tmp)
{
    int i = WhichType(tmp);

    if (i == GTYPE_MAP) {
	eliminate_empties((MAP *) NodeData(tmp));
	recalculate_minefields((MAP *) NodeData(tmp));
    }
    return 1;
}

static FILE *global_file_kludge;

static int load_update1(Node * tmp)
{
    MAP *map;
    int doh;
    char mapbuffer[MBUF_SIZE];
    MECH *mech;
    int i;

    switch ((i = WhichType(tmp))) {
#ifdef BT_ENABLED
    case GTYPE_MAP:
	map = (MAP *) NodeData(tmp);
	bzero(map->mapobj, sizeof(map->mapobj));
	map->map = NULL;
	strcpy(mapbuffer, map->mapname);
	doh = (map->flags & MAPFLAG_MAPO);
	if (strcmp(map->mapname, "Default Map"))
	    map_loadmap(1, map, mapbuffer);
	if (!strcmp(map->mapname, "Default Map") || !map->map)
	    initialize_map_empty(map, NodeKey(tmp));
	if (!feof(global_file_kludge)) {
	    load_mapdynamic(global_file_kludge, map);
	    if (!feof(global_file_kludge))
		if (doh)
		    load_mapobjs(global_file_kludge, map);
	}
	if (feof(global_file_kludge)) {
	    map->first_free = 0;
	    map->mechflags = NULL;
	    map->mechsOnMap = NULL;
	    map->LOSinfo = NULL;
	}
	debug_fixmap(GOD, map, NULL);
	break;
    case GTYPE_MECH:
	mech = (MECH *) NodeData(tmp);
	if (!(FlyingT(mech) && !Landed(mech))) {
	    MechDesiredSpeed(mech) = 0;
	    MechSpeed(mech) = 0;
	    MechVerticalSpeed(mech) = 0;
	}
	MechCocoon(mech) = 0;
	MechStatus(mech) &= ~(BLINDED | UNCONSCIOUS | JUMPING | TOWED);
	MechSpecials2(mech) &=
	    ~(ECM_ENABLED | ECM_DISTURBANCE | ECM_PROTECTED |
	    ECCM_ENABLED | ANGEL_ECM_ENABLED | ANGEL_ECCM_ENABLED |
	    ANGEL_ECM_PROTECTED | ANGEL_ECM_DISTURBED);
	MechCritStatus(mech) &=
	    ~(JELLIED | LOAD_OK | OWEIGHT_OK | SPEED_OK);
	MechWalkXPFactor(mech) = 999;
	MechCarrying(mech) = -1;
	MechBoomStart(mech) = 0;
	MechC3iNetworkSize(mech) = -1;
	MechHeatLast(mech) = 0;
	MechCommLast(mech) = 0;
	for (i = 0; i < FREQS; i++)
	    if (mech->freq[i] < 0)
		mech->freq[i] = 0;
	break;
#endif
    }
    return 1;
}

/*
 * Read in autopilot data
 */
static int load_autopilot_data(Node *tmp) {

    AUTO *autopilot;
    int i;

    if (WhichType(tmp) == GTYPE_AUTO) {

        autopilot = (AUTO *) NodeData(tmp);

        /* Save the AI Command List */
        auto_load_commands(global_file_kludge, autopilot);

        /* Reset the Astar Path */
        autopilot->astar_path = NULL;

        /* Reset the weaplist */
        autopilot->weaplist = NULL;

        /* Reset the profile */
        for (i = 0; i < AUTO_PROFILE_MAX_SIZE; i++) {
            autopilot->profile[i] = NULL;
        }

        /* Check to see if the AI is in a mech */
        /* Need to make this better, check if its got a target whatnot */
	    if (!autopilot->mymechnum || 
                !(autopilot->mymech = getMech(autopilot->mymechnum))) {
	        DoStopGun(autopilot);
        } else {
	        if (Gunning(autopilot))
	    	    DoStartGun(autopilot);
        }

    }

    return 1;

}

static int get_specialobjectsize(int type)
{
    if (type < 0 || type >= NUM_SPECIAL_OBJECTS)
    	return -1;
    return SpecialObjects[type].datasize;
}

#ifdef BT_ADVANCED_ECON
static void load_econdb()
{
FILE *f;
/* Econ DB */
    extern unsigned long long int specialcost[SPECIALCOST_SIZE];
    extern unsigned long long int ammocost[AMMOCOST_SIZE];
    extern unsigned long long int weapcost[WEAPCOST_SIZE];
    extern unsigned long long int cargocost[CARGOCOST_SIZE];
    extern unsigned long long int bombcost[BOMBCOST_SIZE];
    int count;

    fprintf(stderr, "LOADING: %s\n", mudconf.econ_db);
    f =  fopen(mudconf.econ_db, "r");
    if (!f) {
        fprintf(stderr, "ERROR: %s not found.\n", mudconf.econ_db);
        return;
        }
    count = fread(&specialcost, sizeof(unsigned long long int), SPECIALCOST_SIZE, f);
    if (count < SPECIALCOST_SIZE) {
        fprintf(stderr, "ERROR: %s specialcost read : %d expected %d\n", mudconf.econ_db, count, SPECIALCOST_SIZE);
        fclose(f);
        return;
        }
    count = fread(&ammocost, sizeof(unsigned long long int), AMMOCOST_SIZE, f);
    if (count < AMMOCOST_SIZE) {
        fprintf(stderr, "ERROR: %s ammocost read : %d expected %d\n", mudconf.econ_db, count, AMMOCOST_SIZE);
        fclose(f);
        return;
        }
    count = fread(&weapcost, sizeof(unsigned long long int), WEAPCOST_SIZE, f);
    if (count < WEAPCOST_SIZE) {
        fprintf(stderr, "ERROR: %s weapcost read : %d expected %d\n", mudconf.econ_db, count, WEAPCOST_SIZE);
        fclose(f);
        return;
        }
    count = fread(&cargocost, sizeof(unsigned long long int), CARGOCOST_SIZE, f);
    if (count < CARGOCOST_SIZE) {
        fprintf(stderr, "ERROR: %s cargocost read : %d expected %d\n", mudconf.econ_db, count, CARGOCOST_SIZE);
        fclose(f);
        return;
        }
    count = fread(&bombcost, sizeof(unsigned long long int), BOMBCOST_SIZE, f);
    if (count < BOMBCOST_SIZE) {
        fprintf(stderr, "ERROR: %s bombcost read : %d expected %d\n", mudconf.econ_db, count, BOMBCOST_SIZE);
        fclose(f);
        return;
        }
    fclose(f);
    fprintf(stderr, "LOADING: %s (done\n", mudconf.econ_db);
}
#endif

static void load_xcode()
{
    FILE *f;
    byte xcode_version;
    int filemode;

    RCache_Flush();
    initialize_colorize();
    fprintf(stderr, "LOADING: %s\n", mudconf.hcode_db);
    f = my_open_file(mudconf.hcode_db, "r", &filemode);
    if (!f) {
	fprintf(stderr, "ERROR: %s not found.\n", mudconf.hcode_db);
	return;
    }
    fread(&xcode_version, 1, 1, f);
    if (xcode_version != XCODE_VERSION) {
	fprintf(stderr,
	    "LOADING: %s (skipped xcodetree - version difference: %d vs %d)\n",
	    mudconf.hcode_db, (int) xcode_version, (int) XCODE_VERSION);
	return;
    }
    UpdateTree(f, &xcode_tree, get_specialobjectsize);
    global_file_kludge = f;
    GoThruTree(xcode_tree, load_update1);
    GoThruTree(xcode_tree, load_update2);
    GoThruTree(xcode_tree, load_update3);
    GoThruTree(xcode_tree, load_update4);

    /* Read in autopilot data */
    GoThruTree(xcode_tree, load_autopilot_data);

#ifdef BT_ENABLED
    if (!feof(f))
	loadrepairs(f);
#endif
    my_close_file(f, &filemode);
    fprintf(stderr, "LOADING: %s (done)\n", mudconf.hcode_db);
#ifdef BT_ADVANCED_ECON
    load_econdb();
#endif
}

static int zappable_node;

static int zap_check(Node * n)
{
    if (zappable_node >= 0)
	return 0;
    if (!Hardcode(NodeKey(n))) {
	zappable_node = NodeKey(n);
	return 0;
    }
    return 1;
}

void zap_unneccessary_hcode()
{
    while (1) {
	zappable_node = -1;
	GoThruTree(xcode_tree, zap_check);
	if (zappable_node >= 0)
	    DeleteEntry(&xcode_tree, zappable_node);
	else
	    break;
    }
}

void LoadSpecialObjects(void)
{
    dbref i;
    int id, brand;
    int type;
    void *tmpdat;

    muxevent_initialize();
    muxevent_count_initialize();
#ifdef BT_ENABLED
    init_stat();
    initialize_partname_tables();
    for (i = 0; MissileHitTable[i].key != -1; i++) {
	if (find_matching_vlong_part(MissileHitTable[i].name, NULL, &id,
		&brand))
	    MissileHitTable[i].key = Weapon2I(id);
	else
	    MissileHitTable[i].key = -2;
    }
#endif
    /* Loop through the entire database, and if it has the special */
    /* object flag, add it to our linked list. */
    for (i = 0; i < mudstate.db_top; i++)
	if (Hardcode(i) && !Going(i) && !Halted(i)) {
	    type = WhichSpecialS(i);
	    if (type >= 0) {
		if (SpecialObjects[type].datasize > 0)
		    tmpdat = NewSpecialObject(i, type);
		else
		    tmpdat = NULL;
	    } else
		c_Hardcode(i);	/* Reset the flag */
	}
    for (i = 0; i < NUM_SPECIAL_OBJECTS; i++) {
	InitSpecialHash(i);
	if (!SpecialObjects[i].updatefunc)
	    SpecialObjects[i].updateTime = 0;
    }
#ifdef BT_ENABLED
    init_btechstats();
#endif
    load_xcode();
    zap_unneccessary_hcode();
}

#ifdef BT_ENABLED
static FILE *global_file_kludge;

static int save_maps_func(Node * tmp)
{
    MAP *map;

    if (WhichType(tmp) == GTYPE_MAP) {
	/* Write mapobjs, if neccessary */
	map = (MAP *) NodeData(tmp);
	save_mapdynamic(global_file_kludge, map);
	if (map->flags & MAPFLAG_MAPO)
	    save_mapobjs(global_file_kludge, map);
    }
    return 1;
}

/* 
 * Save any extra info for the autopilots 
 *
 * Like their command lists
 * or the Astar path if there is one
 *
 */
static int save_autopilot_data(Node *tmp) {

    AUTO *a;

    if (WhichType(tmp) == GTYPE_AUTO) {

        a = (AUTO *) NodeData(tmp);

        /* Save the AI Command List */
        auto_save_commands(global_file_kludge, a);

        /* Save the AI Astar Path */

    }

    return 1;

}
#endif

void ChangeSpecialObjects(int i)
{
    /* XXX Unneccessary for now ; 'latest' db
       (db.new) is equivalent to 'db' because we don't
       _have_ new-db concept ; this is to-be-done project, however */
}

#ifdef BT_ADVANCED_ECON
static void save_econdb(char *target, int i)
{
FILE *f;
    extern unsigned long long int specialcost[SPECIALCOST_SIZE];
    extern unsigned long long int ammocost[AMMOCOST_SIZE];
    extern unsigned long long int weapcost[WEAPCOST_SIZE];
    extern unsigned long long int cargocost[CARGOCOST_SIZE];
    extern unsigned long long int bombcost[BOMBCOST_SIZE];
    int count;

    switch (i) {
    case DUMP_KILLED:
        sprintf(target, "%s.KILLED", mudconf.econ_db);
        break;
    case DUMP_CRASHED:
        sprintf(target, "%s.CRASHED", mudconf.econ_db);
        break;
    default:                    /* RESTART / normal */
        sprintf(target, "%s.tmp", mudconf.econ_db);
        break;
    }
    f =  fopen(target, "w");
    if (!f) {
        log_perror("SAVE", "FAIL", "Opening econ-save file", target);
        SendDB("ERROR occured during opening of new econ-savefile.");
        return;
        }
    count = fwrite(&specialcost, sizeof(unsigned long long int), SPECIALCOST_SIZE, f);
    if (count < SPECIALCOST_SIZE) {
        log_perror("SAVE", "FAIL", tprintf("ERROR: %s specialcost wrote : %d expected %d", target, count, SPECIALCOST_SIZE), target);
        SendDB("ERROR occured during saving of econ-save file");
        fclose(f);
        return;
        }
    count = fwrite(&ammocost, sizeof(unsigned long long int), AMMOCOST_SIZE, f);
    if (count < AMMOCOST_SIZE) {
        log_perror("SAVE", "FAIL", tprintf("ERROR: %s ammocost wrote : %d expected %d", target, count, AMMOCOST_SIZE), target);
        SendDB("ERROR occured during saving of econ-save file");
        fclose(f);
        return;
        }
    count = fwrite(&weapcost, sizeof(unsigned long long int), WEAPCOST_SIZE, f);
    if (count < WEAPCOST_SIZE) {
        log_perror("SAVE", "FAIL", tprintf("ERROR: %s weapcost wrote : %d expected %d", target, count, WEAPCOST_SIZE), target);
        SendDB("ERROR occured during saving of econ-save file");
        fclose(f);
        return;
        }
    count = fwrite(&cargocost, sizeof(unsigned long long int), CARGOCOST_SIZE, f);
    if (count < CARGOCOST_SIZE) {
        log_perror("SAVE", "FAIL", tprintf("ERROR: %s cargocost wrote : %d expected %d", target, count, CARGOCOST_SIZE), target);
        SendDB("ERROR occured during saving of econ-save file");
        fclose(f);
        return;
        }
    count = fwrite(&bombcost, sizeof(unsigned long long int), BOMBCOST_SIZE, f);
    if (count < BOMBCOST_SIZE) {
        log_perror("SAVE", "FAIL", tprintf("ERROR: %s bombcost wrote : %d expected %d", target, count, BOMBCOST_SIZE), target);
        SendDB("ERROR occured during saving of econ-save file");
        fclose(f);
        return;
        }
    fclose(f);
    if (i == DUMP_RESTART || i == DUMP_NORMAL) {
        if (rename(target, mudconf.econ_db) < 0) {
            log_perror("SAV", "FAIL", "Renaming econ-save file ", target);
            SendDB("ERROR occured during renaming of econ save-file.");
            }
        }
}
#endif

void SaveSpecialObjects(int i)
{
    FILE *f;
    int filemode, count;
    byte xcode_version = XCODE_VERSION;
    char target[LBUF_SIZE];

    switch (i) {
    case DUMP_KILLED:
	sprintf(target, "%s.KILLED", mudconf.hcode_db);
	break;
    case DUMP_CRASHED:
	sprintf(target, "%s.CRASHED", mudconf.hcode_db);
	break;
    default:			/* RESTART / normal */
	sprintf(target, "%s.tmp", mudconf.hcode_db);
	break;
    }
    f = my_open_file(target, "w", &filemode);
    if (!f) {
	log_perror("SAV", "FAIL", "Opening new hcode-save file", target);
	SendDB("ERROR occured during opening of new hcode-savefile.");
	return;
    }
    fwrite(&xcode_version, 1, 1, f);
    count = SaveTree(f, xcode_tree);
#ifdef BT_ENABLED
    global_file_kludge = f;
    /* Then, check each xcode thing for stuff */
    GoThruTree(xcode_tree, save_maps_func);

    /* Save autopilot data */
    GoThruTree(xcode_tree, save_autopilot_data);

    saverepairs(f);
#endif
    my_close_file(f, &filemode);
    if (i == DUMP_RESTART || i == DUMP_NORMAL) {
	if (rename(mudconf.hcode_db, tprintf("%s.prev", mudconf.hcode_db))
	    < 0) {
	    log_perror("SAV", "FAIL", "Renaming old hcode-save file ",
		target);
	    SendDB
		("ERROR occured during renaming of old hcode save-file.");
	}
	if (rename(target, mudconf.hcode_db) < 0) {
	    log_perror("SAV", "FAIL", "Renaming new hcode-save file ",
		target);
	    SendDB
		("ERROR occured during renaming of new hcode save-file.");
	}
    }
    if (count)
	SendDB(tprintf("Hcode saved. %d xcode entries dumped.", count));
#ifdef BT_ADVANCED_ECON
    save_econdb(target, i);
#endif
}

static int UpdateSpecialObject_func(Node * tmp)
{
    int i;

    i = WhichType(tmp);
    if (!SpecialObjects[i].updateTime)
        return 1;
    if ((mudstate.now % SpecialObjects[i].updateTime))
        return 1;
    SpecialObjects[i].updatefunc(NodeKey(tmp), NodeData(tmp));
    return 1;
}

/* This is called once a second for each special object */

/* Note the new handling for calls being done at <1second intervals,
   or possibly at >1second intervals */

static time_t lastrun = 0;
void UpdateSpecialObjects(void)
{
    char *cmdsave;
    int i;
    int times = lastrun ? (mudstate.now - lastrun) : 1;

    if (times > 20)
        times = 20;		/* Machine's hopelessly lagged,
                           we don't want to make it [much] worse */
    cmdsave = mudstate.debug_cmd;
    for (i = 0; i < times; i++) {
        muxevent_run();
        mudstate.debug_cmd = (char *) "< Generic hcode update handler>";
        GoThruTree(xcode_tree, UpdateSpecialObject_func);
    }
    lastrun = mudstate.now;
    mudstate.debug_cmd = cmdsave;
}

void *NewSpecialObject(int id, int type)
{
    void *foo;
    int i;
    dbref *t;

    if (SpecialObjects[type].datasize) {
	Create(foo, char, (i = SpecialObjects[type].datasize));

	t = (dbref *) foo;
	*t = id;
	if (SpecialObjects[type].allocfreefunc)
	    SpecialObjects[type].allocfreefunc(id, &(foo), SPECIAL_ALLOC);
	AddEntry(&xcode_tree, id, type, i, foo);
    }
    return foo;
}

void CreateNewSpecialObject(dbref player, dbref key)
{
    void *new;
    struct SpecialObjectStruct *typeOfObject;
    int type;
    char *str;

    str = silly_atr_get(key, A_XTYPE);
    if (!(str && *str)) {
	notify(player,
	    "You must first set the XTYPE using @xtype <object>=<type>");
	notify(player, "Valid XTYPEs include: MECH, MECHREP, MAP, DEBUG, "
	    "CHARGEN, AUTOPILOT, TURRET, CUSTOM, SCEN, SSIDE, SSOBJ, "
	    "SSINS, SSEXT");
	notify(player, "Resetting hardcode flag.");
	c_Hardcode(key);	/* Reset the flag */
	return;
    }

    /* Find the special objects */
    type = WhichSpecialS(key);
    if (type > -1) {
	/* We found the proper special object */
	typeOfObject = &SpecialObjects[type];
	if (typeOfObject->datasize) {
	    new = NewSpecialObject(key, type);
	    if (!new)
		notify(player, "Memory allocation failure!");
	}
    } else {
	notify(player, "That is not a valid XTYPE!");
	notify(player, "Valid XTYPEs include: MECH, MECHREP, MAP, DEBUG, "
	    "CHARGEN, AUTOPILOT, TURRET, CUSTOM, SCEN, SSIDE, SSOBJ, "
	    "SSINS, SSEXT");
	notify(player, "Resetting HARDCODE flag.");
	c_Hardcode(key);
    }
}

void DisposeSpecialObject(dbref player, dbref key)
{
    Node *tmp;
    int i;
    struct SpecialObjectStruct *typeOfObject;

    tmp = FindNode(xcode_tree, key);

    i = WhichSpecialS(key);
    if (i < 0) {
	notify(player,
	    "CRITICAL: Unable to free data, inconsistency somewhere. Please");
	notify(player, "contact a wizard about this _NOW_!");
	return;
    }
    typeOfObject = &SpecialObjects[i];

    if (typeOfObject->datasize > 0 && WhichSpecial(key) != i) {
	notify(player,
	    "Semi-critical error has occured. For some reason the object's data differs\nfrom the data on the object. Please contact a wizard about this.");
	i = WhichSpecial(key);
    }
    if (tmp) {
	void *t = NodeData(tmp);

	if (typeOfObject->allocfreefunc)
	    typeOfObject->allocfreefunc(key, &NodeData(tmp), SPECIAL_FREE);
	NodeData(tmp) = NULL;
	DeleteEntry(&xcode_tree, key);
	muxevent_remove_data(t);
	free(t);
    } else if (typeOfObject->datasize > 0) {
	notify(player, "This object is not in the special object DBASE.");
	notify(player, "Please contact a wizard about this bug. ");
    }
}


void Dump_Mech(dbref player, int type, char *typestr)
{
    notify(player,
	"Support discontinued. Bother a wiz if this bothers you.");
#if 0
    MECH *mech;
    char buff[100];
    int i, running = 0, count = 0;
    Node *temp;

    notify(player, "ID    # STATUS      MAP #      PILOT #");
    notify(player, "----------------------------------------");
    for (temp = TreeTop(xcode_tree); temp; temp = TreeNext(temp))
	if (WhichSpecial((i = NodeKey(temp))) == type) {
	    mech = (MECH *) NodeData(temp);
	    sprintf(buff, "#%5d %-8s    #%5d    #%5d", mech->mynum,
		!Started(mech) ? "SHUTDOWN" : "RUNNING", mech->mapindex,
		MechPilot(mech));
	    notify(player, buff);
	    if (MechStatus(mech) & STARTED)
		running++;
	    count++;
	}
    sprintf(buff, "%d %ss running out of %d %ss allocated.", running,
	typestr, count, typestr);
    notify(player, buff);
    notify(player, "Done listing");
#endif
}

void DumpMechs(dbref player)
{
    Dump_Mech(player, GTYPE_MECH, "mech");
}

void DumpMaps(dbref player)
{
    notify(player,
	"Support discontinued. Bother a wiz if this bothers you.");
#if 0
    MAP *map;
    char buff[100];
    int j, count;
    Node *temp;

    notify(player, "MAP #       NAME              X x Y   MECHS");
    notify(player, "-------------------------------------------");
    for (temp = TreeTop(xcode_tree); temp; temp = TreeNext(temp))
	if (WhichSpecial(NodeKey(temp)) == GTYPE_MAP) {
	    count = 0;
	    map = (MAP *) NodeData(temp);
	    for (j = 0; j < map->first_free; j++)
		if (map->mechsOnMap[j] != -1)
		    count++;
	    sprintf(buff, "#%5d    %-17.17s %3d x%3d       %d", map->mynum,
		map->mapname, map->map_width, map->map_height, count);
	    notify(player, buff);
	}
    notify(player, "Done listing");
#endif
}

/***************** INTERNAL ROUTINES *************/
#ifdef FAST_WHICHSPECIAL
int WhichSpecial(dbref key)
{
    Node *n;

    if (!Hardcode(key))
	return -1;
    if (!(n = FindObjectsNode(key)))
	return -1;
    return NodeType(n);
}

#endif

#ifdef FAST_WHICHSPECIAL
static int WhichSpecialS(dbref key)
#else
int WhichSpecial(dbref key)
#endif
{
    int i;
    int returnValue = -1;
    char *str;

    if (!Hardcode(key))
	return -1;
    str = silly_atr_get(key, A_XTYPE);
    if (str && *str) {
	for (i = 0; i < NUM_SPECIAL_OBJECTS; i++) {
	    if (!strcmp(SpecialObjects[i].type, str)) {
		returnValue = i;
		break;
	    }
	}
    }
    return (returnValue);
}

int IsMech(dbref num)
{
    return WhichSpecial(num) == GTYPE_MECH;
}

int IsAuto(dbref num) {
    return WhichSpecial(num) == GTYPE_AUTO;
}

int IsMap(dbref num) {
    return WhichSpecial(num) == GTYPE_MAP;
}

/*** Support routines ***/
static Node *FindObjectsNode(dbref key)
{
    Node *tmp;

    if ((tmp = FindNode(xcode_tree, (int) key)))
	return tmp;
    return NULL;
}

void *FindObjectsData(dbref key)
{
    Node *tmp;

    if ((tmp = FindObjectsNode(key)))
	return NodeData(tmp);
    return NULL;
}


char *center_string(char *c, int len)
{
    static char buf[LBUF_SIZE];
    int l = strlen(c);
    int p, i;

    p = MAX(0, (len - l) / 2);
    for (i = 0; i < p; i++)
	buf[i] = ' ';
    strcpy(buf + p, c);
    return buf;
}

static void help_color_initialize(char *from, char *to)
{
    int i;
    char buf[LBUF_SIZE];

    for (i = 0; from[i] && from[i] != ' '; i++);
    if (from[i]) {

/*      from[i]=0; */
	strncpy(buf, from, i);
	buf[i] = 0;
	sprintf(to, "%s%s%s %s", "%ch%cb", buf, "%cn", &from[i + 1]);

/*      from[i]=' '; */
    } else
	sprintf(to, "%s%s%s", "%cc", from, "%cn");

}

#define ONE_LINE_TEXTS

#ifdef ONE_LINE_TEXTS
#define MLen CM_ONE
#else
#define MLen CM_TWO
#endif

static char *do_ugly_things(coolmenu ** d, char *msg, int len, int initial)
{
    coolmenu *c = *d;
    char *e;
    int i;
    char buf[LBUF_SIZE];
    char msg2[LBUF_SIZE];

    strcpy(msg2, msg);
#ifndef ONE_LINE_TEXTS
    if (!msg) {
	sim(" ", MLen);
	*d = c;
	return NULL;
    }
#endif
    if (strlen(msg) < len) {
	if (initial) {
	    if (initial > 0)
		help_color_initialize(msg, buf);
	    else {
		for (i = 0; i < -initial; i++)
		    buf[i] = ' ';
		strcpy(&buf[i], msg);
	    }
	    sim(buf, MLen);
	} else
	    sim(msg, MLen);
	*d = c;
	return NULL;
    }
    for (e = msg2 + len - 1; *e != ' '; e--);
    *e = 0;
    if (initial) {
	if (initial > 0)
	    help_color_initialize(msg2, buf);
	else {
	    for (i = 0; i < -initial; i++)
		buf[i] = ' ';
	    strcpy(&buf[i], msg2);
	}
	sim(buf, MLen);
    } else
	sim(msg2, MLen);
    *e = ' ';
    *d = c;
    if ((*e + 1))
	return e + 1;
    return NULL;
}

#define Len(s) ((!s || !*s) ? 0 : strlen(s))

#define TAB 3

static void cut_apart_helpmsgs(coolmenu ** d, char *msg1, char *msg2,
    int len, int initial)
{
    int l1 = Len(msg1);
    int l2 = Len(msg2);
    int nl1, nl2;

#ifndef ONE_LINE_TEXTS

    msg1 = do_ugly_things(d, msg1, len, initial);
    msg2 =
	do_ugly_things(d, msg2, initial ? len : len - TAB,
	initial ? 0 : 0 - TAB);
    if (!msg1 && !msg2)
	return;
    nl1 = Len(msg1);
    nl2 = Len(msg2);
    if (nl1 != l1 || nl2 != l2)	/* To prevent infinite loops */
	cut_apart_helpmsgs(d, msg1, msg2, len, 0);
#else
    int first = 1;

    while (msg1 && *msg1) {
	msg1 = do_ugly_things(d, msg1, len * 2 - 1, first);
	nl1 = Len(msg1);
	if (nl1 == l1)
	    break;
	l1 = nl1;
	first = 0;
    }
    while (msg2 && *msg2) {
	msg2 = do_ugly_things(d, msg2, len * 2 - TAB, 0 - TAB);
	nl2 = Len(msg2);
	if (nl2 == l2)
	    break;
	l2 = nl2;
    }

#endif
}

static void DoSpecialObjectHelp(dbref player, char *type, int id, int loc,
    int powerneeded, int objid, char *arg)
{
    int i, j;
    MECH *mech = NULL;
    int pos[100][2];
    int count = 0, csho = 0;
    coolmenu *c = NULL;
    char buf[LBUF_SIZE];
    char *d;
    int dc;

    if (id == GTYPE_MECH)
	mech = getMech(loc);
    bzero(pos, sizeof(pos));
    for (i = 0; SpecialObjects[id].commands[i].name; i++) {
	if (!SpecialObjects[id].commands[i].func &&
	    (SpecialObjects[id].commands[i].helpmsg[0] != '@' ||
		Have_MechPower(Owner(player), powerneeded)))
	    if (id != GTYPE_MECH ||
		Can_Use_Command(mech,
		    SpecialObjects[id].commands[i].flag)) {
		if (count)
		    pos[count - 1][1] = i - pos[count - 1][0];
		pos[count][0] = i;
		count++;
	    }
    }
    if (count)
	pos[count - 1][1] = i - pos[count - 1][0];
    else {
	pos[0][0] = 0;
	pos[0][1] = i;
	count = 1;
    }
    sim(NULL, CM_ONE | CM_LINE);
    if (!arg || !*arg) {
#define HELPMSG(a) \
      &SpecialObjects[id].commands[a].helpmsg[SpecialObjects[id].commands[a].helpmsg[0]=='@']
	for (i = 0; i < count; i++) {
	    if (count > 1) {
		d = center_string(HELPMSG(pos[i][0]), 70);
		sim(tprintf("%s%s%s", "%cg", d, "%c"), CM_ONE);
	    } else
		sim(tprintf("%s command listing: ", type),
		    CM_ONE | CM_CENTER);
	    for (j = pos[i][0] + (count == 1 ? 0 : 1);
		j < pos[i][0] + pos[i][1]; j++)
		if (SpecialObjects[id].commands[j].helpmsg[0] != '@' ||
		    Have_MechPower(Owner(player), powerneeded))
		    if (id != GTYPE_MECH ||
			Can_Use_Command(mech,
			    SpecialObjects[id].commands[j].flag)) {
			strcpy(buf, SpecialObjects[id].commands[j].name);
			d = buf;
			while (*d && *d != ' ')
			    d++;
			if (*d == ' ')
			    *d = 0;
			sim(buf, CM_FOUR);
			csho++;
		    }
	}
	if (!csho)
	    vsi(tprintf
		("There are no commands you are authorized to use here."));
	else {
	    sim(NULL, CM_ONE | CM_LINE);
	    if (count > 1)
		vsi("Additional info available with 'HELP SUBTOPIC'");
	    else
		vsi("Additional info available with 'HELP ALL'");
	}
    } else {
	/* Try to find matching subtopic, or ALL */
	if (!strcasecmp(arg, "all")) {
	    if (count > 1) {
		vsi("ALL not available for objects with subcategories.");
		dc = -2;
	    } else
		dc = -1;
	} else {
	    if (count == 1) {
		vsi("This object doesn't have any other detailed help than 'HELP ALL'");
		dc = -2;
	    } else {
		for (i = 0; i < count; i++)
		    if (!strcasecmp(arg, HELPMSG(pos[i][0])))
			break;
		if (i == count) {
		    vsi("Subcategory not found.");
		    dc = -2;
		} else
		    dc = i;
	    }
	}
	if (dc > -2) {
	    for (i = 0; i < count; i++)
		if (dc == -1 || i == dc) {
		    if (count > 1)
			vsi(tprintf("%s%s%s", "%cg",
				center_string(HELPMSG(pos[i][0]), 70),
				"%c"));
		    for (j = pos[i][0] + (count == 1 ? 0 : 1);
			j < pos[i][0] + pos[i][1]; j++)
			if (SpecialObjects[id].commands[j].helpmsg[0] !=
			    '@' ||
			    Have_MechPower(Owner(player), powerneeded))
			    if (id != GTYPE_MECH ||
				Can_Use_Command(mech,
				    SpecialObjects[id].commands[j].flag))
				cut_apart_helpmsgs(&c,
				    SpecialObjects[id].commands[j].name,
				    HELPMSG(j), 37, 1);
		}
	}
    }
    sim(NULL, CM_ONE | CM_LINE);
    ShowCoolMenu(player, c);
    KillCoolMenu(c);
}

void InitSpecialHash(int which)
{
    char *tmp, *tmpc;
    int i;
    char buf[MBUF_SIZE];

    hashinit(&SpecialCommandHash[which], 20 * HASH_FACTOR);
    for (i = 0; (tmp = SpecialObjects[which].commands[i].name); i++) {
	if (!SpecialObjects[which].commands[i].func)
	    continue;
	tmpc = buf;
	for (; *tmp && *tmp != ' '; tmp++)
	    *(tmpc++) = ToLower(*tmp);
	*tmpc = 0;
	if ((tmpc = strstr(buf, " ")))
	    *tmpc = 0;
	hashadd(buf, (int *) &SpecialObjects[which].commands[i],
	    &SpecialCommandHash[which]);
    }
}

void handle_xcode(dbref player, dbref obj, int from, int to)
{
    if (from == to)
	return;
    if (!to) {
	s_Hardcode(obj);
	DisposeSpecialObject(player, obj);
	c_Hardcode(obj);
    } else
	CreateNewSpecialObject(player, obj);
}


#define DEFAULT 0		/* Normal */
#define ANSI_START "\033["
#define ANSI_START_LEN 2
#define ANSI_END "m"
#define ANSI_END_LEN 1

struct color_entry {
    int bit;
    int negbit;
    char ltr;
    char *string;
    char *sstring;
} color_table[] = {
    {
    0x0008, 7, 'n', ANSI_NORMAL, NULL}, {
    0x0001, 0, 'h', ANSI_HILITE, NULL}, {
    0x0002, 0, 'i', ANSI_INVERSE, NULL}, {
    0x0004, 0, 'f', ANSI_BLINK, NULL}, {
    0x0010, 0, 'x', ANSI_BLACK, NULL}, {
    0x0010, 0x10, 'l', ANSI_BLACK, NULL}, {
    0x0020, 0, 'r', ANSI_RED, NULL}, {
    0x0040, 0, 'g', ANSI_GREEN, NULL}, {
    0x0080, 0, 'y', ANSI_YELLOW, NULL}, {
    0x0100, 0, 'b', ANSI_BLUE, NULL}, {
    0x0200, 0, 'm', ANSI_MAGENTA, NULL}, {
    0x0400, 0, 'c', ANSI_CYAN, NULL}, {
    0x0800, 0, 'w', ANSI_WHITE, NULL}, {
    0, 0, 0, NULL, NULL}
};

#define CHARS 256

char colorc_reverse[CHARS];

void initialize_colorize()
{
    int i;
    char buf[20];
    char *c;

    c = buf + ANSI_START_LEN;
    for (i = 0; i < CHARS; i++)
	colorc_reverse[i] = DEFAULT;
    for (i = 0; color_table[i].string; i++) {
	colorc_reverse[(short) color_table[i].ltr] = i;
	strcpy(buf, color_table[i].string);
	buf[strlen(buf) - ANSI_END_LEN] = 0;
	color_table[i].sstring = strdup(c);
    }

}


#undef notify
char *colorize(dbref player, char *from)
{
    char *to;
    char *p, *q;
    int color_wanted = 0;
    int i;
    int cnt;

    q = to = alloc_lbuf("colorize");
#if 1
    for (p = from; *p; p++) {
	if (*p == '%' && *(p + 1) == 'c') {
	    p += 2;
	    if (*p <= 0)
		i = DEFAULT;
	    else
		i = colorc_reverse[(short) *p];
	    if (i == DEFAULT && *p != 'n')
		p--;
	    color_wanted &= ~color_table[i].negbit;
	    color_wanted |= color_table[i].bit;
	} else {
	    if (color_wanted && Ansi(player)) {
		*q = 0;
		/* Generate efficient color string */
		strcpy(q, ANSI_START);
		q += ANSI_START_LEN;
		cnt = 0;
		for (i = 0; color_table[i].string; i++)
		    if (color_wanted & color_table[i].bit &&
			color_table[i].bit != color_table[i].negbit) {
			if (cnt)
			    *q++ = ';';
			strcpy(q, color_table[i].sstring);
			q += strlen(color_table[i].sstring);
			cnt++;
		    }
		strcpy(q, ANSI_END);
		q += ANSI_END_LEN;
		color_wanted = 0;
	    }
	    *q++ = *p;
	}
    }
    *q = 0;
    if (color_wanted && Ansi(player)) {
	/* Generate efficient color string */
	strcpy(q, ANSI_START);
	q += ANSI_START_LEN;
	cnt = 0;
	for (i = 0; color_table[i].string; i++)
	    if (color_wanted & color_table[i].bit &&
		color_table[i].bit != color_table[i].negbit) {
		if (cnt)
		    *q++ = ';';
		strcpy(q, color_table[i].sstring);
		q += strlen(color_table[i].sstring);
		cnt++;
	    }
	strcpy(q, ANSI_END);
	q += ANSI_END_LEN;
	color_wanted = 0;
    }
#else
    strcpy(to, p);
#endif
    return to;
}

void mecha_notify(dbref player, char *msg)
{
    char *tmp;

    tmp = colorize(player, msg);
    raw_notify(player, tmp);
    free_lbuf(tmp);
}

void mecha_notify_except(dbref loc, dbref player, dbref exception,
    char *msg)
{
    dbref first;

    if (loc != exception)
	notify_checked(loc, player, msg,
	    (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A |
		MSG_COLORIZE));
    DOLIST(first, Contents(loc)) {
	if (first != exception) {
	    notify_checked(first, player, msg,
		(MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | MSG_COLORIZE));
	}
    }
}

void list_chashstats(dbref player)
{
    int i;

    for (i = 0; i < NUM_SPECIAL_OBJECTS; i++)
	list_hashstat(player, tprintf("HCCmd:%s", SpecialObjects[i].type),
	    &SpecialCommandHash[i]);
#ifdef BT_ENABLED
    list_phashstats(player);
#endif
}

/* 
   Basically, finish all the repairs etc in one fell swoop. That's the
   best we can do for now, I'm afraid. 
   */
void ResetSpecialObjects()
{
#if 0				/* Nowadays no longer neccessary, see mech.tech.saverepair.c */
    int i;

    for (i = FIRST_TECH_EVENT; i <= LAST_TECH_EVENT; i++)
	while (muxevent_run_by_type(i));
#endif
    muxevent_run_by_type(EVENT_HIDE);
    muxevent_run_by_type(EVENT_BLINDREC);
}


MAP *getMap(dbref d)
{
    Node *tmp;

    if (!(tmp = FindObjectsNode(d)))
	return NULL;
    if (NodeType(tmp) != GTYPE_MAP)
	return NULL;
    return (MAP *) NodeData(tmp);
}

MECH *getMech(dbref d)
{
    Node *tmp;

    if (d < 0)
    	return NULL;
    if (!(tmp = FindObjectsNode(d)))
	return NULL;
    if (NodeType(tmp) != GTYPE_MECH)
	return NULL;
    return (MECH *) NodeData(tmp);
}