/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
/* Crimson2 Mud Server
 * All source written/copyright Ryan Haksi 1995 *
 * This source code is proprietary. Use in whole or in part without
 * explicity permission by the author is strictly prohibited
 *
 * Current email address(es): cryogen@infoserve.net
 * Phone number: (604) 591-5295
 *
 * C4 Script Language written/copyright Cam Lesiuk 1995
 * Email: clesiuk@engr.uvic.ca
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifndef WIN32
  #include <unistd.h> /* for unlink */
#endif

#include "crimson2.h"
#include "macro.h"
#include "log.h"
#include "mem.h"
#include "str.h"
#include "ini.h"
#include "send.h"
#include "extra.h"
#include "property.h"
#include "code.h"
#include "file.h"
#include "thing.h"
#include "world.h"
#include "index.h"
#include "base.h"
#include "affect.h"
#include "effect.h"
#include "fight.h"
#include "object.h"
#include "board.h"
#include "area.h"
#include "cmd_inv.h"

#define OBJECT_INDEX_SIZE 8192

INDEX objectIndex;

  /* this allows for up to 16 different attributes to stored on a given object up
     to a total byte storage of 16 bytes

     first entry is the text name of the object type, followed by 3 arrays:
     an array of type specifiers each type specifier consists of 3 characters
     the first is the data-array (B,W or L), element #,  and then (<space> for no list, T for typelist
     or F for flag list)

NOTE: Everything is manipulated as an unsigned long int, so Big/Little endian is
      irrelevant.

ALSO: numbers in the form \XX seems to be in octal!!! so remember
     \00 0
     \01 1
     \02 2
     \03 3 (Max value for L entries)
     \04 4
     \05 5
     \06 6
     \07 7 (Max value for W entries)
     \10 8
     \11 9
     \12 10
     \13 11
     \14 12
     \15 13
     \16 14
     \17 15 (Max value for B entries)

     ie. Possible locations are:
     |B00 B01 B02 B03 |B04 B05 B06 B07 |B10 B11 B12 B13 |B14 B15 B16 B17|
     |W00     W01     |W02     W03     |W04     W05     |W06     W07    |
     |L00             |L01             |L02             |L03            |
*/

#define LNULL   {NULL,0}
#define L(list) {(BYTE**)list,sizeof(*list)}
OTYPELIST oTypeList[] = {
  { "!RESETCMD", {"B\00 ",       "B\01 ",       "B\02 ",       "L\01 ",       "L\02 ",       "L\03 ",       ""    },
                 {"RCMD",        "RIF",         "RARG4",       "RARG1",       "RARG2",       "RARG3",       ""    },
                 {LNULL,         LNULL,         LNULL,          LNULL,         LNULL,         LNULL,        LNULL } },

  { "LIGHT",     {"L\00 ",       "L\01T",       "L\02 ",       ""    },
                 {"INTENSITY",   "AMMO-TYPE",   "AMMO-USE",    ""    },
                 {LNULL,         L(oAmmoList),  LNULL,         LNULL } },

  { "SCANNER",   {"W\00F",       "B\02T",       "B\03 ",       "L\01 ",       "L\02 ",       "L\03 ",       ""    },
                 {"SCAN-FLAG",   "AMMO-TYPE",   "AMMO-USE",    "SCAN-MAX",    "BIO-SCANNED", "CHIP-SCANNED",""    },
                 {L(oSFlagList), L(oAmmoList),  LNULL,         LNULL,         LNULL,         LNULL,         LNULL } },

  { "DEVICE",    {"L\00 ",       "L\01T",       ""   },
                 {"AMMO-USE",    "AMMO-TYPE",   ""   },
                 {LNULL,         L(oAmmoList),  LNULL} },

  { "!STAFF",    {"L\00 ",       "L\01 ",       "L\02 ",       "L\03 ",       ""    },
                 {"UNDEF1",      "UNDEF2",      "UNDEF3",      "UNDEF4",      ""    },
                 {LNULL,         LNULL,         LNULL,         LNULL,         LNULL } },

  { "WEAPON",    {"L\00F",       "L\01F",       "B\13 ",       "B\12 ",       "B\11 ",       "B\17 ",       "B\16T",       "B\15 ",      "B\14T",       ""   },
                 {"WFLAGS",      "DAMFLAG",     "DAMDIENUM",   "FIRERATE",    "DAMDIESIZE",  "AMMO-USE",    "AMMO-TYPE",   "RANGE",      "WEAPONTYPE",  ""   },
                 {L(oWFlagList), L(resistList), LNULL,         LNULL,         LNULL,         LNULL,         L(oAmmoList),  LNULL,        L(weaponList), LNULL} },

  { "BOARD",     {"L\00 ",      ""   },
                 {"BVIRTUAL",   ""   },
                 {LNULL,        LNULL} },

  { "AMMO",      {"L\00T",       "L\01 ",       "W\05 ",       "W\04 ",       "L\03F",       ""     },
                 {"AMMOTYPE",    "AMMOLEFT",    "HITBONUS",    "DAMBONUS",    "DAMTYPE",     ""     },
                 {L(oAmmoList),  LNULL,         LNULL,         LNULL,         L(resistList), LNULL} },

  { "TREASURE",  {""   },
                 {""   },
                 {LNULL} },

  { "ARMOR",     {"L\00 ",       "B\04 ",       "B\05 ",       "B\06 ",       "B\07 ",       "B\10 ",      "B\11 ",      "B\12 ",      "B\13 ",      "B\14 ",      "B\15 ",       "B\16T",       ""   },
                 {"ARMOR",       "R-PUNCTURE",  "R-SLASH",     "R-CONCUSSIVE","R-HEAT",      "R-EMR",      "R-LASER",    "R-PSYCHIC",  "R-ACID",     "R-POISON",   "AMMO-USE",    "AMMO-TYPE",   ""   },
                 {LNULL,         LNULL,         LNULL,         LNULL,         LNULL,         LNULL,        LNULL,        LNULL,        LNULL,        LNULL,        LNULL,         L(oAmmoList),  LNULL} },

  { "DRUG",      {"L\00 ",       ""   },
                 {"INTOX",       ""   },
                 {LNULL,         LNULL} },

  { "WORN",      {""   },
                 {""   },
                 {LNULL} },

  { "OTHER",     {""   },
                 {""   },
                 {LNULL} },

  { "TRASH",     {""   },
                 {""   },
                 {LNULL} },

  { "!TRAP",     {"L\00 ",       "L\01 ",       "L\02 ",       "L\03 ",       ""   },
                 {"UNDEF1",      "UNDEF2",      "UNDEF3",      "UNDEF4",      ""   },
                 {LNULL,         LNULL,         LNULL,         LNULL,         LNULL} },

  { "CONTAINER", {"W\00 ",       "W\01 ",       "L\01F",       "L\02 ",       "L\03 ",       ""   },
                 {"MAX-CONTAIN", "ROT",         "C-FLAGS",     "KEY-NUM",     "SCAN-VALUE",  ""   },
                 {LNULL,         LNULL,         L(oCFlagList), LNULL,         LNULL,         LNULL} },

  { "!NOTE",     {"L\00 ",       "L\01 ",       "L\02 ",       "L\03 ",       ""   },
                 {"UNDEF1",      "UNDEF2",      "UNDEF3",      "UNDEF4",      ""   },
                 {LNULL,         LNULL,         LNULL,         LNULL,         LNULL} },

  { "DRINKCON",  {"L\00 ",       "L\01 ",       "L\02T",       "L\03 ",       ""   },
                 {"MAX-CONTAIN", "NOW-CONTAIN", "LIQUID",      "POISON",      ""   },
                 {LNULL,         LNULL,         L(oLiquidList),LNULL,         LNULL} },

  { "KEY",       {"L\00 ",       ""   },
                 {"KEYNUMBER",   ""   },
                 {LNULL,         LNULL} },

  { "FOOD",      {"L\00 ",       "L\03 ",       ""   },
                 {"HOWFILLING",  "POISON",      ""   },
                 {LNULL,         LNULL,         LNULL} },

  { "MONEY",     {"L\00 ",       ""   },
                 {"AMOUNT",      ""   },
                 {LNULL,         LNULL} },

  { "!PEN",      {"L\00 ",       "L\01 ",       "L\02 ",       "L\03 ",       ""       },
                 {"UNDEF1",      "UNDEF2",      "UNDEF3",      "UNDEF4",      ""       },
                 {LNULL,         LNULL,         LNULL,         LNULL,         LNULL} },

  { "!BOAT",     {"L\00 ",       "L\01 ",       "L\02 ",       "L\03 ",       ""       },
                 {"UNDEF1",      "UNDEF2",      "UNDEF3",      "UNDEF4",      ""       },
                 {LNULL,         LNULL,         LNULL,         LNULL,         LNULL} },

  { "EXIT",      {"L\00 ",       "L\02 ",       ""     },
                 {"WldVirtual",  "ROT"          ""     },
                 {LNULL,         LNULL,         LNULL} },

  { "VEHICLE",   {"L\00 ",       ""     },
                 {"WldVirtual",  ""     },
                 {LNULL,         LNULL} },

  /* Terminating entry */
  { "",      {"","","","","","","",""},
             {"","","","","","","",""},
             {LNULL,   LNULL,   LNULL,   LNULL,   LNULL,   LNULL,   LNULL,   LNULL} }

};

BYTE *oActList[] = {
  "GLOW",           /* glows */
  "HUM",            /* hums */
  "DARK",           /* creates darkness */
  "GOOD",           /* detects as good */
  "EVIL",           /* detects as evil */
  "INVISIBLE",      /* is invis */
  "AURA",           /* detects as psionic aura */
  "NODROP",         /* cant drop */
  "BLESS",          /* blessed/dedicated to some power (ie enspell it once) */
  "ANTI-GOOD",      /* good cant get */
  "ANTI-EVIL",      /* evil cant get */
  "ANTI-NEUTRAL",   /* neutral cant get */
  "HIDDEN",         /* hidden */
  "TRACK-OFFLINE",  /* only a limited number in the game */
  "CARRY2USE",      /* Can only use it when its carried */
  "NODEATHTRAP",    /* item will never be lost in a deathtrap */
  ""
};

BYTE *oSFlagList[] = {
  "SCANBIO",
  "SCANCHIP",
  ""
};

BYTE *oWFlagList[] = {
  "UNDEF0",
  ""
};

/* Name, Desc */
OAMMOTYPE oAmmoList[] = {
  { "NONE",                 "absolutely nothing"    },
  { "33-CALIBRE-CLIP",      "a 33 Calibre Clip"     },
  { "45-CALIBRE-CLIP",      "a 45 Calibre Clip"     },
  { "50-CALIBRE-BELT",      "a 50 Calibre Belt"     },
  { "DEUTERIUM-CARTRIDGE",  "a Deuterium Cartridge" },
  { "MICRO-GALCIV-CELL",    "a MicroCell"           },
  { "MINI-GALCIV-CELL",     "a MiniCell"            },
  { "MEDIUM-GALCIV-CELL",   "a MediumCell"          },
  { "LARGE-GALCIV-CELL",    "a MaxiCell"            },
  { "MICRO-BUILDER-CELL",   "a Builder MicroCell"   },
  { "MINI-BUILDER-CELL",    "a Builder MiniCell"    },
  { "MEDIUM-BUILDER-CELL",  "a Builder MediumCell"  },
  { "LARGE-BUILDER-CELL",   "a Builder MaxiCell"    },
  { "DRUG-CARTRIDGE",       "a Drug Cartridge"      },
  { "3INCH-SHOTSHELL-DRUM", "a 3\" Shotshell Drum"  },
  { "NEEDLE-CLIP",          "a Needle Clip"         },
  { "OXYGEN-CANNISTER",     "an Oxygen Cannister"   },
  { "",                     ""                      }
};


BYTE *oCFlagList[] = {
  "CLOSABLE",
  "PICKPROOF",
  "CLOSED",
  "LOCKED",
  "CORPSE",
  "ELECTRONIC",
  "!PLAYERCORPSE",
  "!PLAYERLOOTED",
  ""
};

/* water is the bench mark against which all others are measured,
 * think of things as percents (humans have thirst/hunger/intox of 100)
 * so 10 drinks of water will take you right from dying of thirst to no thirsty
 * think of intox as (double?) percentage of alchohol, also means to get smashed 
 * will take you quite a few beers....
 */

/*   Name              Desc                        Thirst/Hunger/Intox/Poison/Acid */
OLIQUIDTYPE oLiquidList[] = {
  /* regular edibles */
  { "WATER",              "a clear liquid",              20,  2,    0,   0,   0 },
  { "RECYCLED-WATER",     "an almost clear liquid",      15,  1,    0,   0,   2 },
  { "COFFEE",             "a black liquid",              16,  8,    2,   0,   4 },
  { "ORANGE-JUICE",       "an orange liquid",            18, 12,    0,   0,   7 },
  { "GRAPE-JUICE",        "a purple liquid",             18, 12,    0,   0,   3 },
  { "MANGO-JUICE",        "a light orange liquid",       18, 12,    0,   0,   3 },
  { "APPLE-JUICE",        "a pale yellow liquid",        18, 12,    0,   0,   0 },
  { "DAJORIN-JUICE",      "a flourescent green liquid",  18, 12,    0,   0,   3 },
  { "QUALIL-JUICE",       "a neon blue liquid",          30, 16,    5,   1,   3 },
  { "PLEATH-JUICE",       "a sour liquid",                6, 15,    2,   1,  10 },
  { "GAL-CIV-COLA",       "a fizzy brown liquid",        10,  5,    0,   0,  10 },
  { "MILK",               "a milky white liquid",        18, 15,    0,   0,   0 },
  { "MILK-CHOCOLATE",     "a milky brown liquid",        15, 10,    0,   0,   0 },
  { "SYRUP",              "a dark syrup",                 1,  8,    0,   0,   0 },
  { "MAPLE-SYRUP",        "a thick goey brown syrup",     9, 14,    0,   0,   0 },
  { "BLOOD",              "a blood red liquid",           5, 10,    3,   0,   0 },
  { "Artificer-BLOOD",    "a thin yellow liquid",         8,  2,   10,  20,   0 },
  { "Salamander-BLOOD",   "a thick blue liquid",          3,  1,    5,   0,  12 },
  { "OLIVE-OIL",          "an oily yellow liquid",        0, 17,    0,   0,   0 },
  { "KETCHUP",            "a very thick red liquid",      5, 17,    0,   0,   0 },
  { "MUSTARD",            "a very thick yellow liquid",   2, 16,    0,   0,   0 },
  { "MEAL-REPLACEMENT",   "a very thick grey liquid",     2, 25,    1,   0,   0 },

  /* intox */
  { "BEER",               "a fizzy amber liquid",        12,  10,   8,   0,   0 },
  { "LABATTS-BEER",       "a pale amber liquid",         16,   8,  18,   0,   0 },
  { "WEST-COAST-ALE",     "a fizzy dark amber liquid",   15,  12,   9,   0,   0 },
  { "HUMAN-VODKA",        "a clear liquid",               6,   0,  30,   0,   0 },
  { "HUMAN-WHISKEY",      "a light amber liquid",         8,   0,  20,   0,   0 },
  { "HUMAN-SCOTCH",       "a clear amber liquid",         8,   0,  20,   0,   0 },
  { "HUMAN-RUM",          "an amber liquid",              8,   0,  20,   0,   0 },
  { "ARTIFICER-ALE",      "a light green liquid",        15,   5,  15,   0,   0 },
  { "SALAMANDER-GROG",    "a firey red and black liquid", 5,   0,  35,   0,   0 },
  { "LIQUID-SILICON",     "a heavy cloudy liquid",       10,   1,  20,   0,   0 },
  { "ETHANOL",            "a thin clear liquid",          2,   0,  40,  10,   0 },

  /* poisons and acids */
  { "STAGNANT-WATER",     "a smelly liquid",             10,   4,   3,   5,   2 },
  { "POISON-WATER",       "a clear liquid",              20,   2,   5,  35,   0 },
  { "BLUE-PAINT",         "a thick, blue liquid",         2,   5,   6,  15,   2 },
  { "RED-PAINT",          "a thick, red liquid",          2,   5,   6,  15,   2 },
  { "YELLOW-PAINT",       "a thick, yellow liquid",       2,   5,   6,  15,   2 },
  { "GREEN-PAINT",        "a thick, green liquid",        2,   5,   6,  15,   2 },
  { "GREY-PAINT",         "a thick, grey liquid",         2,   5,   6,  15,   2 },
  { "GASOLINE",           "a smelly, amber liquid",       1,   0,  12,  20,   3 },
  { "MACHINE-OIL",        "a dark, oily liquid",          1,   1,   2,  20,   0 },
  { "VERATHANE",          "a smelly, clear liquid",       0,   0,  20,  25,   5 },
  { "CYANIDE",            "a featureless liquid",         0,   0,   9, 200,   0 },
  { "SULFURIC-ACID",      "a thick, clear liquid",        0,   0,   8,  30, 100 },
  { "HYDROCHLORIC-ACID",  "a thick, clear liquid",        0,   0,   8,  30, 100 },

  { "" }
};

/* when you equip an object (or use it) it applies an EFFECT/AFFECT to something(s)
   each EFFECT can apply multiple AFFECT structures, and must apply at least
   one.

   if you specify TAR_NODEFAULT, then that apply will do nothing unless you
   USE the object in question and then pass it a target (without TAR_NODEFAULT
   the player will get a you have to specify a target type message)

   when you equip an item it will apply any applies that are TAR_ON_WEAR
   and direct AFFECTS (ie TAR_AFFECT's, which are implicitly TAR_SELF & only TAR_SELF)
  */
struct ApplyListType applyList[] = {
  {"NONE",                      0,                   0},
  {"STR",                       AFFECT_STR,          TAR_AFFECT},
  {"DEX",                       AFFECT_DEX,          TAR_AFFECT},
  {"INT",                       AFFECT_INT,          TAR_AFFECT},
  {"WIS",                       AFFECT_WIS,          TAR_AFFECT},
  {"CON",                       AFFECT_CON,          TAR_AFFECT},
  {"HIT",                       AFFECT_HIT,          TAR_AFFECT},
  {"MOVE",                      AFFECT_MOVE,         TAR_AFFECT},
  {"POWER",                     AFFECT_POWER,        TAR_AFFECT},
  {"ARMOR",                     AFFECT_ARMOR,        TAR_AFFECT},
  {"HITROLL",                   AFFECT_HITROLL,      TAR_AFFECT},
  {"DAMROLL",                   AFFECT_DAMROLL,      TAR_AFFECT},
  {"SPEED",                     AFFECT_SPEED,        TAR_AFFECT},

  {"USE-SELF-CELLREPAIR",       EFFECT_CELL_REPAIR,  TAR_SELF_DEF},
  {"USE-SELF-REFRESH",          EFFECT_REFRESH,      TAR_SELF_DEF},
  {"USE-SELF-ENDURANCE",        EFFECT_ENDURANCE,    TAR_SELF_DEF},
  {"USE-SELF-BREATHWATER",      EFFECT_BREATHWATER,  TAR_SELF_DEF},
  {"USE-SELF-STRENGTH",         EFFECT_STRENGTH,     TAR_SELF_DEF},
  {"USE-SELF-DARKVISION",       EFFECT_DARKVISION,   TAR_SELF_DEF},
  {"USE-SELF-SLOW-POISON",      EFFECT_SLOW_POISON,  TAR_SELF_DEF},
  {"USE-SELF-BERSERK",          EFFECT_BERSERK,      TAR_SELF_DEF},
  {"USE-SELF-CURE-POISON",      EFFECT_CURE_POISON,  TAR_SELF_DEF},
  {"USE-SELF-HEAL-MINOR",       EFFECT_HEAL_MINOR,   TAR_SELF_DEF},
  {"USE-SELF-REGENERATION",     EFFECT_REGENERATION, TAR_SELF_DEF},
  {"USE-SELF-HEAL-MAJOR",       EFFECT_HEAL_MAJOR,   TAR_SELF_DEF},
  {"USE-SELF-DEXTERITY",        EFFECT_DEXTERITY,    TAR_SELF_DEF},
  {"USE-SELF-CONSTITUTION",     EFFECT_CONSTITUTION, TAR_SELF_DEF},
  {"USE-SELF-HASTE",            EFFECT_HASTE,        TAR_SELF_DEF},
  {"USE-SELF-REVITALIZE",       EFFECT_REVITALIZE,   TAR_SELF_DEF},
  {"USE-SELF-RECALL",           EFFECT_RECALL,       TAR_SELF_DEF},
  {"USE-SELF-MARK",             EFFECT_MARK,         TAR_SELF_DEF},
  {"USE-SELF-POISON",           EFFECT_POISON,       TAR_SELF_DEF},

  {"WEAR-BREATHWATER",          EFFECT_BREATHWATER,  TAR_ON_WEAR},
  {"WEAR-VACUUMWALK",           EFFECT_VACUUMWALK,   TAR_ON_WEAR},

  {"",0,0}
};

BYTE         objectOfLog;    /* log if a virtual # cant be found */
BYTE         objectReadLog;  /* log each Obj structure as its read */
LWORD        objectNum = 0;  /* number of Obj structures in use - just curious really */
OBJTEMPLATE *corpseTemplate = NULL;
OBJTEMPLATE *resetTemplate = NULL;
OBJTEMPLATE *moneyTemplate = NULL;
OBJTEMPLATE *phantomPocketTemplate = NULL;
OBJTEMPLATE *fireBladeTemplate = NULL;
OBJTEMPLATE *fireShieldTemplate = NULL;
OBJTEMPLATE *fireArmorTemplate = NULL;

void ObjectInit(void) {
  BYTE buf[256];
 
  /* INI reads */
  objectOfLog   = INILWordRead("crimson2.ini", "objectOfLog", 0);
  objectReadLog = INILWordRead("crimson2.ini", "objectReadLog", 0);
  sprintf(buf, "Reading object logging defaults\n");
  Log(LOG_BOOT, buf);
  sprintf(buf, "ObjectTemplate structure size is %d bytes\n", sizeof(OBJTEMPLATE));
  Log(LOG_BOOT, buf);

  IndexInit(&objectIndex, OBJECT_INDEX_SIZE, "objectIndex", 0);

  /* *********************************************** */
  /* System object templates must *NOT* be equipable */
  /* *********************************************** */

  /* create a corpse template for fighting */
  MEMALLOC(corpseTemplate, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
  memset( (void*)corpseTemplate, 0, sizeof(OBJTEMPLATE));
  corpseTemplate->oVirtual = OVIRTUAL_CORPSE;
  corpseTemplate->oKey =   STRCREATE("Its dead Jim...");
  corpseTemplate->oSDesc = STRCREATE("Its dead Jim...");
  corpseTemplate->oLDesc = STRCREATE("Its dead Jim...");
  corpseTemplate->oDesc =  STRCREATE("Its dead Jim...\n");
  corpseTemplate->oType = OTYPE_CONTAINER;
  corpseTemplate->oWeight = 150;
  corpseTemplate->oValue = 0;
  corpseTemplate->oRent = 0;
  ObjectSetField(OTYPE_CONTAINER, &corpseTemplate->oDetail, OF_CONTAINER_MAX, 150);
  ObjectSetField(OTYPE_CONTAINER, &corpseTemplate->oDetail, OF_CONTAINER_KEY, -1);
  ObjectSetField(OTYPE_CONTAINER, &corpseTemplate->oDetail, OF_CONTAINER_ROT, 5);

  /* create a reset command template for fighting */
  MEMALLOC(resetTemplate, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
  memset( (void*)resetTemplate, 0, sizeof(OBJTEMPLATE));
  resetTemplate->oVirtual = OVIRTUAL_RESET;
  resetTemplate->oKey =   STRCREATE("reset command rst cmd");
  resetTemplate->oSDesc = STRCREATE("Reset Command");
  resetTemplate->oLDesc = STRCREATE("Reset Command");
  resetTemplate->oDesc =  STRCREATE("A Reset Command.\n");
  resetTemplate->oType =  OTYPE_RESETCMD;
  resetTemplate->oWeight =0;
  resetTemplate->oValue = 0;
  resetTemplate->oRent = -1;

  /* create a reset command template for fighting */
  MEMALLOC(moneyTemplate, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
  memset( (void*)moneyTemplate, 0, sizeof(OBJTEMPLATE));
  moneyTemplate->oVirtual = OVIRTUAL_MONEY;
  moneyTemplate->oKey =   STRCREATE("money galciv credits");
  moneyTemplate->oSDesc = STRCREATE("pile of GalCiv credits");
  moneyTemplate->oLDesc = STRCREATE("A pile of GalCiv credits has been left here");
  moneyTemplate->oDesc =  STRCREATE("GalCiv credits, the standard medium of currency exchange across the entire\ngalaxy. Too bad there isnt more...\n");
  moneyTemplate->oType =  OTYPE_MONEY;
  moneyTemplate->oWeight =0;
  moneyTemplate->oValue = 0;
  moneyTemplate->oRent = -1;

  /* create a template for phantomPocket's */
  MEMALLOC(phantomPocketTemplate, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
  memset( (void*)phantomPocketTemplate, 0, sizeof(OBJTEMPLATE));
  phantomPocketTemplate->oVirtual = OVIRTUAL_PHANTOMPOCKET;
  phantomPocketTemplate->oKey =   STRCREATE("phantom pocket opening");
  phantomPocketTemplate->oSDesc = STRCREATE("pocket-sized opening in space-time");
  phantomPocketTemplate->oLDesc = STRCREATE("A pocket-sized opening in space-time has been left here");
  phantomPocketTemplate->oDesc =  STRCREATE("Its a pocket-sized hole in space-time that leads to another dimension. It's\ngreat for storing stuff in because then you dont have to carry them.\n");
  phantomPocketTemplate->oType =  OTYPE_CONTAINER;
  phantomPocketTemplate->oWear =  TYPEFIND("HELD", wearList);
  phantomPocketTemplate->oWeight =0;
  phantomPocketTemplate->oValue = 0;
  phantomPocketTemplate->oRent = -1;

  /* create a template for fireBlade's */
  MEMALLOC(fireBladeTemplate, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
  memset( (void*)fireBladeTemplate, 0, sizeof(OBJTEMPLATE));
  fireBladeTemplate->oVirtual = OVIRTUAL_FIREBLADE;
  fireBladeTemplate->oKey =   STRCREATE("slender blade fireblade");
  fireBladeTemplate->oSDesc = STRCREATE("Slender blade of fire");
  fireBladeTemplate->oLDesc = STRCREATE("A slender blade of fire has been left here");
  fireBladeTemplate->oDesc =  STRCREATE("Its a slender blade shaped field of fire, the better to attack people with.\n");
  fireBladeTemplate->oType =  OTYPE_WEAPON;
  fireBladeTemplate->oWear =  TYPEFIND("HELD", wearList);
  fireBladeTemplate->oWeight =0;
  fireBladeTemplate->oValue = 0;
  fireBladeTemplate->oRent = -1;

  /* create a template for fireShields's */
  MEMALLOC(fireShieldTemplate, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
  memset( (void*)fireShieldTemplate, 0, sizeof(OBJTEMPLATE));
  fireShieldTemplate->oVirtual = OVIRTUAL_FIRESHIELD;
  fireShieldTemplate->oKey =   STRCREATE("shield fireshield disc large");
  fireShieldTemplate->oSDesc = STRCREATE("large disc of fire");
  fireShieldTemplate->oLDesc = STRCREATE("A disc of fire has been left here");
  fireShieldTemplate->oDesc =  STRCREATE("Its a large disc shaped field of fire that you can you use to help ward off attacks\n");
  fireShieldTemplate->oType =  OTYPE_ARMOR;
  fireShieldTemplate->oWear =  TYPEFIND("HELD", wearList);
  fireShieldTemplate->oWeight =0;
  fireShieldTemplate->oValue = 0;
  fireShieldTemplate->oRent = -1;

  /* create a template for fireArmor's */
  MEMALLOC(fireArmorTemplate, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
  memset( (void*)fireArmorTemplate, 0, sizeof(OBJTEMPLATE));
  fireArmorTemplate->oVirtual = OVIRTUAL_FIREARMOR;
  fireArmorTemplate->oKey =   STRCREATE("armor firearmor column");
  fireArmorTemplate->oSDesc = STRCREATE("column of fire");
  fireArmorTemplate->oLDesc = STRCREATE("A column of fire is standing here");
  fireArmorTemplate->oDesc =  STRCREATE("Its a large column of fire that surrounds you and much like a\nbig suit of fiery armor, protects you from harm.\n");
  fireArmorTemplate->oType =  OTYPE_ARMOR;
  fireArmorTemplate->oWear =  TYPEFIND("OVERBODY", wearList);
  fireArmorTemplate->oWeight =0;
  fireArmorTemplate->oValue = 0;
  fireArmorTemplate->oRent = -1;
}


void ObjectRead(WORD area) {
  FILE        *objectFile;
  BYTE         objFileBuf[256];
  BYTE         buf[256];
  BYTE         tmp[256];
  STR         *sKey;
  STR         *sDesc;
  OBJTEMPLATE *object;
  LWORD        last = -1;
  LWORD        apply;

  sprintf(objFileBuf, "area/%s.obj", areaList[area].aFileName->sText);
  objectFile = fopen(objFileBuf, "rb");
  if (!objectFile) {
    sprintf(buf, "Unable to read %s, killing server\n", objFileBuf);
    Log(LOG_BOOT, buf);
    PERROR("ObjectRead");
    exit(ERROR_BADFILE);
  }
  if (areaList[area].aOffset) {
    sprintf(buf, "Relocating Area/Objects [%s]\n", areaList[area].aFileName->sText);
    Log(LOG_BOOT, buf);
  }

  /* okay we opened it up so read it.... */
  fscanf(objectFile, " %s \n", tmp); /* get virtual number */
  while (!feof(objectFile)) {
    if (tmp[0] == '$' || feof(objectFile))
      break; /* Dikumud file format EOF character */
    if (tmp[0] != '#') { /* whoa... whadda we got here */
      sprintf(buf, "ObjectRead: Unknown virtual %s, aborting\n", tmp);
      Log(LOG_BOOT, buf);
      break;
    }
    MEMALLOC(object, OBJTEMPLATE, OBJECT_ALLOC_SIZE);
    memset( (void*)object, 0, sizeof(OBJTEMPLATE)); /* init to zeros */
    object->oVirtual = atoi(tmp+1);
    object->oVirtual += areaList[area].aOffset;
    if (objectReadLog) {
      sprintf(buf, "Reading object#%ld\n", object->oVirtual);
      Log(LOG_BOOT, buf);
    }
    /* confirm that virtual number is valid */
    if (object->oVirtual < last) {
      sprintf(buf, "%s - Obj%s < than previous\n", objFileBuf, tmp);
      Log(LOG_BOOT, buf);
    }
    last = MAXV(last, object->oVirtual);
    if (object->oVirtual < areaList[area].aVirtualMin) {
      sprintf(buf, "%s - Obj%s < than %ld\n", objFileBuf, tmp, areaList[area].aVirtualMin);
      Log(LOG_BOOT, buf);
      break;
    }
    if (object->oVirtual > areaList[area].aVirtualMax) {
      sprintf(buf, "%s - Obj%s > than %ld\n", objFileBuf, tmp, areaList[area].aVirtualMax);
      Log(LOG_BOOT, buf);
      break;
    }

    /* NOTE that Private Strings are not designated as such, until just prior to editing */
    objectNum++;
    object->oKey   = FileStrRead(objectFile);
    if (fileError) {
      sprintf(buf, "Error reading Key for object#%ld\n", object->oVirtual);
      Log(LOG_BOOT, buf);
    }
    object->oSDesc = FileStrRead(objectFile);
    if (fileError) {
      sprintf(buf, "Error reading SDesc for object#%ld\n", object->oVirtual);
      Log(LOG_BOOT, buf);
    }
    /* under vanilla diku file format object ldesc's dont have \n's on end 
     * but mob lDescs do, in any case Crimson2 ldescs should not have the \n
     */
    object->oLDesc  = FileStrRead(objectFile);
    if (fileError) {
      sprintf(buf, "Error reading LDesc for object#%ld\n", object->oVirtual);
      Log(LOG_BOOT, buf);
    }
    if (object->oLDesc->sText[object->oLDesc->sLen-1] == '\n') {
      strcpy(buf, object->oLDesc->sText);
      buf[object->oLDesc->sLen-1] = '\0';
      STRFREE(object->oLDesc);
      object->oLDesc = STRCREATE(buf);
    }
    /* in vanilla diku format this is an unused string field, and should be blank */
    object->oDesc  = FileStrRead(objectFile);
    if (fileError) {
      sprintf(buf, "Error reading Desc for object#%ld\n", object->oVirtual);
      Log(LOG_BOOT, buf);
    }
    if (object->oDesc->sText[0] == '\0') {
      STRFREE(object->oDesc);
      object->oDesc = STRCREATE("You see nothing special\n");
    }
    object->oType = FILETYPEREAD(objectFile, oTypeList); /* type of this room, ie city, river, underwater etc */
    object->oAct = FileFlagRead(objectFile, oActList); /* flags for this room */
    object->oWear = FILETYPEREAD(objectFile, wearList); /* type of this room, ie city, river, underwater etc */
    if (fileError) {
      sprintf(buf, "Error reading Wear value for object#%ld\n", object->oVirtual);
      Log(LOG_BOOT, buf);
    }
    fscanf(objectFile, " %ld ", &object->oDetail.lValue[0]);
    fscanf(objectFile, " %ld ", &object->oDetail.lValue[1]);
    fscanf(objectFile, " %ld ", &object->oDetail.lValue[2]);
    fscanf(objectFile, " %ld ", &object->oDetail.lValue[3]);
    fscanf(objectFile, " %hd ", &object->oWeight);
    fscanf(objectFile, " %ld ", &object->oValue);
    fscanf(objectFile, " %ld ", &object->oRent);

    /* read attachments ie keywords etc */
    apply = 0;
    while (!feof(objectFile)) {
      fscanf(objectFile, " %s \n", tmp);
      if (tmp[0] == '#') /* well thats it for this chunk of the world */
        break;

      else if (tmp[0] == 'E') { /* haha an extra */
        sKey  = FileStrRead(objectFile);
        if (fileError) {
          sprintf(buf, "Error reading ExtraKey for object#%ld\n", object->oVirtual);
          Log(LOG_BOOT, buf);
        }
        sDesc = FileStrRead(objectFile);
        if (fileError) {
          sprintf(buf, "Error reading ExtraDesc for object#%ld\n", object->oVirtual);
          Log(LOG_BOOT, buf);
        }
        object->oExtra = ExtraAlloc(object->oExtra, sKey, sDesc);
      }

      else if (tmp[0] == 'P') { /* a property of some kind */
        sKey =  FileStrRead(objectFile); /* property name */
        if (fileError) {
          sprintf(buf, "Error reading PropertyKey for object#%ld\n", object->oVirtual);
          Log(LOG_BOOT, buf);
        }
        sDesc = FileStrRead(objectFile);/* property value */
        if (fileError) {
          sprintf(buf, "Error reading PropertyDesc for object#%ld\n", object->oVirtual);
          Log(LOG_BOOT, buf);
        }
        object->oProperty = PropertyCreate(object->oProperty, sKey, sDesc);
      }

      else if (tmp[0] == 'A') { /* an Apply */
        if (apply < OBJECT_MAX_APPLY) {
          object->oApply[apply].aType  = FILETYPEREAD(objectFile, applyList);
          TYPECHECK(object->oApply[apply].aType, applyList);
          object->oApply[apply].aValue = FileByteRead(objectFile);
          apply++;
        } else {
          sprintf(buf, "Object #%ld - too many apply's\n", object->oVirtual);
          Log(LOG_BOOT, buf);
          fgets(buf, sizeof(buf), objectFile);
        }
      }
    }

    /* guess this ones a keeper, update mins and maxes etc... */
    IndexInsert(&areaList[area].aObjIndex, object, ObjectCompareProc);
    if (indexError) {
      sprintf(buf, "%s - obj%s duplicated\n", objFileBuf, tmp);
      Log(LOG_BOOT, buf);
    }

  }
  /* all done close up shop */
  fclose(objectFile);
}


OBJTEMPLATE *ObjectOf(LWORD virtual) {
  THING *search;
  BYTE   buf[256];
  LWORD  area;

  area = AreaOf(virtual);
  if (area == -1) return NULL;

  search = IndexFind(&areaList[area].aObjIndex, (void*)virtual, ObjectFindProc);
  if (search) {
    return ObjTemplate(search);
  } else {
    if (objectOfLog) {
      sprintf(buf, "ObjectOf: virtual %ld doesnt exist\n", virtual);
      Log(LOG_ERROR, buf);
    }
  }
  return NULL;
}


void ObjectWrite(WORD area) {
  FILE        *objectFile;
  BYTE         objFileBuf[256];
  BYTE         buf[256];
  OBJTEMPLATE *object;
  LWORD        i;
  LWORD        apply;
  EXTRA       *extra;
  PROPERTY    *property;
  
  /* take a backup case we crash halfways through this */
  sprintf(buf,
    "mv area/obj/%s.obj area/obj/%s.obj.bak",
    areaList[area].aFileName->sText,
    areaList[area].aFileName->sText);
  system(buf);
  
  sprintf(objFileBuf, "area/%s.obj", areaList[area].aFileName->sText);
  objectFile = fopen(objFileBuf, "wb");
  if (!objectFile) {
    sprintf(buf, "Unable to write %s, killing server\n", objFileBuf);
    Log(LOG_ERROR, buf);
    PERROR("ObjectWrite");
    return;
  }
  
  /* okay we opened it up so write it.... */
  for (i=0; i<areaList[area].aObjIndex.iNum; i++) {
    object = ObjTemplate(areaList[area].aObjIndex.iThing[i]);
    fprintf(objectFile, "#%ld\n", object->oVirtual); /* get virtual number */
    FileStrWrite(objectFile, object->oKey);
    FileStrWrite(objectFile, object->oSDesc);
    FileStrWrite(objectFile, object->oLDesc);
    FileStrWrite(objectFile, object->oDesc);
    FILETYPEWRITE(objectFile, object->oType, oTypeList, ' ');
    FileFlagWrite(objectFile, object->oAct, oActList, ' ');
    FILETYPEWRITE(objectFile, object->oWear, wearList, '\n');
    
    fprintf(objectFile, "%ld %ld %ld %ld\n", object->oDetail.lValue[0], object->oDetail.lValue[1], object->oDetail.lValue[2], object->oDetail.lValue[3]);
    fprintf(objectFile, "%hd %ld %ld\n", object->oWeight, object->oValue, object->oRent);
    
    /* apply's */
    for (apply=0; apply<OBJECT_MAX_APPLY; apply++) {
      if (object->oApply[apply].aType){
        fprintf(objectFile, "A\n");
        FILETYPEWRITE(objectFile, object->oApply[apply].aType, applyList, ' ');
        FileByteWrite(objectFile, object->oApply[apply].aValue, '\n');
      }
    }
    
    /* extra descriptions */
    for (extra=object->oExtra; extra; extra=extra->eNext) {
      fprintf(objectFile, "E\n");
      FileStrWrite(objectFile, extra->eKey);
      FileStrWrite(objectFile, extra->eDesc);
    }
    
    /* property's */
    for (property=object->oProperty; property; property=property->pNext) {
      fprintf(objectFile, "P\n");
      FileStrWrite(objectFile, property->pKey);
      if ( CodeIsCompiled(property) ) {
        if (!areaWriteBinary) {
          /* decompile the property */
          CodeDecompProperty(property, NULL);
          object->oCompile = 1; /* enable compile on demand */
          /* Warn if it didnt decompile */
          if (CodeIsCompiled(property)) {
            sprintf(buf, "ObjectWrite: Property %s failed to decompile for object#%ld!\n",
                         property->pKey->sText, 
                         object->oVirtual);
            Log(LOG_AREA, buf);
          } else {
            FileStrWrite(objectFile, property->pDesc);
          }
        } else {
          FileBinaryWrite(objectFile, property->pDesc);
        }
      } else {
        FileStrWrite(objectFile, property->pDesc);
      }
    }

  }
  /* all done close up shop */
  fprintf(objectFile, "$");
  fclose(objectFile);

  /* turf backup we didnt crash */
  sprintf(buf,
    "area/%s.obj.bak",
    areaList[area].aFileName->sText);
  unlink(buf);

}


INDEXPROC(ObjectCompareProc) { /* BYTE IndexProc(void *index1, void *index2) */
  if ( ObjTemplate(index1)->oVirtual == ObjTemplate(index2)->oVirtual )
    return 0;
  else if ( ObjTemplate(index1)->oVirtual < ObjTemplate(index2)->oVirtual )
    return -1;
  else
    return 1;
}

INDEXFINDPROC(ObjectFindProc) { /* BYTE IFindProc(void *key, void *index) */
  if ( (LWORD)key == ObjTemplate(index)->oVirtual )
    return 0;
  else if ( (LWORD)key < ObjTemplate(index)->oVirtual )
    return -1;
  else
    return 1;
}


THING *ObjectAlloc(void) {
  OBJ *object;

  MEMALLOC(object, OBJ, OBJECT_ALLOC_SIZE);
  memset(object, 0, sizeof(OBJ));
  IndexAppend(&objectIndex, Thing(object));

  Thing(object)->tType = TTYPE_OBJ;
  return Thing(object);
}

THING *ObjectCreate(OBJTEMPLATE *template, THING *within) {
  THING    *object;
  LWORD     i;
  PROPERTY *p;

  if (!template) return NULL;
  object = ObjectAlloc();

  Thing(object)->tSDesc    = StrAlloc(template->oSDesc);
  Thing(object)->tDesc     = StrAlloc(template->oDesc);
  Thing(object)->tExtra    = ExtraCopy(template->oExtra);
  if (template->oCompile) {
    template->oCompile = 0;
    for (p=template->oProperty; p; p=p->pNext) {
      CodeCompileProperty(p, NULL);
    }
  }
  Thing(object)->tProperty = PropertyCopy(template->oProperty);
  Base(object)->bKey       = StrAlloc(template->oKey);
  Base(object)->bLDesc     = StrAlloc(template->oLDesc);
  Base(object)->bWeight    = template->oWeight;
  Obj(object)->oTemplate   = template;

  /* this data changes so needs to be kept on each object */
  Obj(object)->oAct        = template->oAct;
  for (i=0; i<4; i++)
    Obj(object)->oDetail.lValue[i] = template->oDetail.lValue[i];
  for (i=0; i<OBJECT_MAX_APPLY; i++) {
    Obj(object)->oApply[i].aType  = template->oApply[i].aType;
    Obj(object)->oApply[i].aValue = template->oApply[i].aValue;
  }

#ifdef UGLY_VEHICLE
  /* Special hook for VEHICLES */
  if (template->oType == OTYPE_VEHICLE) {
    WLD   *vehicle = NULL;
    THING *wld = WorldOf( OBJECTGETFIELD(object, OF_VEHICLE_WVIRTUAL) );

    if (wld) {
      MEMALLOC(vehicle, WLD, WORLD_ALLOC_SIZE);
      memset(vehicle, 0, sizeof(WLD));
      Thing(vehicle)->tType     = TTYPE_WLD;
      Thing(vehicle)->tSDesc    = StrAlloc(wld->tSDesc);
      Thing(vehicle)->tDesc     = StrAlloc(wld->tDesc);
      Thing(vehicle)->tExtra    = ExtraCopy(wld->tExtra);
      Wld(vehicle)->wVirtual = WVIRTUAL_VEHICLE;
      Wld(vehicle)->wArea = Wld(wld)->wArea;
      Wld(vehicle)->wFlag = Wld(wld)->wFlag;
      Wld(vehicle)->wType = Wld(wld)->wType;
    }
    Obj(object)->oDetail.lValue[3] = (LWORD)vehicle;
  }
#endif

  template->oOnline++;
  CodeCheckFlag(object);
  if (within) ThingTo(object, within);
  return object;
}

THING *ObjectCreateMoney(LWORD amount, THING *within) {
  THING *money;
  BYTE   buf[256];

  money = ObjectCreate(moneyTemplate, within);
  if (!money) return NULL;

  STRFREE(money->tSDesc);
  STRFREE(Base(money)->bLDesc);
  STRFREE(money->tDesc);

  MINSET(amount, 1);
  if (amount <= 1) {
    money->tSDesc = STRCREATE("A single GalCiv credit");
    Base(money)->bLDesc = STRCREATE("Someone has carelessly discarded a single GalCiv credit here");
    money->tDesc =  STRCREATE("One miserable GalCiv credit, the standard medium of currency exchange across\nthe entire galaxy. Too bad there isnt more...\n");
  } else if (amount <= 2) {
    money->tSDesc = STRCREATE("A couple GalCiv credits");
    Base(money)->bLDesc = STRCREATE("Someone has carelessly discarded a couple GalCiv credits here");
    money->tDesc =  STRCREATE("2 miserable GalCiv credits, the standard medium of currency exchange across\nthe entire galaxy. Too bad there isnt more...\n");
  } else if (amount <= 100) {
    sprintf(buf, "%ld GalCiv credits", amount);
    money->tSDesc = STRCREATE(buf);
    sprintf(buf, "Someone has carelessly discarded %ld GalCiv credits here", amount);
    Base(money)->bLDesc = STRCREATE(buf);
    sprintf(buf, "%ld GalCiv credits, the standard medium of currency exchange across\nthe entire galaxy. Too bad there isnt more...\n", amount);
    money->tDesc =  STRCREATE(buf);
  } else if (amount <= 1000) {
    money->tSDesc = STRCREATE("A stack of GalCiv credits");
    Base(money)->bLDesc = STRCREATE("Someone has carelessly discarded a stack of GalCiv credits here");
    sprintf(buf, "A stack of GalCiv credits, the standard medium of currency exchange across\nthe entire galaxy. Looks like there is about %ld credits, too bad there isnt even more...\n", amount/50*50);
    money->tDesc =  STRCREATE(buf);
  } else if (amount <= 20000) {
    money->tSDesc = STRCREATE("A pile of GalCiv credits");
    Base(money)->bLDesc = STRCREATE("Someone has carelessly discarded a pile of GalCiv credits here");
    sprintf(buf, "A pile of GalCiv credits, the standard medium of currency exchange across\nthe entire galaxy. Looks like there is about %ld credits, not too shabby...\n", amount/500*500);
    money->tDesc =  STRCREATE(buf);
  } else if (amount <= 50000) {
    money->tSDesc = STRCREATE("A huge pile of GalCiv credits");
    Base(money)->bLDesc = STRCREATE("Someone has carelessly discarded a huge pile of GalCiv credits here");
    sprintf(buf, "A huge pile of GalCiv credits, the standard medium of currency exchange across\nthe entire galaxy. Looks like there is about %ld credits, now thats more like it...\n", amount/5000*5000);
    money->tDesc =  STRCREATE(buf);
  } else {
    money->tSDesc = STRCREATE("A mountain of GalCiv credits");
    Base(money)->bLDesc = STRCREATE("Someone has carelessly discarded a mountain of GalCiv credits here");
    sprintf(buf, "A mountain of GalCiv credits, the standard medium of currency exchange across\nthe entire galaxy. Looks like there is about %ld credits, better snarf it quick...\n", amount/10000*10000);
    money->tDesc =  STRCREATE(buf);
  }

  OBJECTSETFIELD(money, OF_MONEY_AMOUNT, amount);

  return money;
}

void ObjectFree(THING *thing) {
  Obj(thing)->oTemplate->oOnline--;
  IndexDelete(&objectIndex, thing, NULL);
  if (Obj(thing)->oTemplate->oType == OTYPE_VEHICLE)
    if (Obj(thing)->oDetail.lValue[3])
      MEMFREE( (WLD*) Obj(thing)->oDetail.lValue[3], WLD );
  MEMFREE(Obj(thing), OBJ);
}


LWORD ObjectPresent(OBJTEMPLATE *template, THING *within, FLAG presentFlag){
  LWORD  present = 0;
  THING *i;

  for (i = within->tContain; i; i=i->tNext) {
    if ( (i->tType == TTYPE_OBJ) && (Obj(i)->oTemplate == template) ) {
      if ( (!Obj(i)->oEquip && BIT(presentFlag, OP_INVENTORY))
         ||(Obj(i)->oEquip && BIT(presentFlag, OP_EQUIPPED)))
        present++;
    }
  }
  return present;
}

BYTE ObjectMaxReached(OBJTEMPLATE *template, LWORD maxAllowed) {
  LWORD current;

  if (maxAllowed<0) return FALSE;
  current = template->oOnline;
  if (BIT(template->oAct, OA_TRACK_OFFLINE)) {
    current += template->oOffline;
  }
  if (current<maxAllowed) return FALSE;
  return TRUE;
}

void ObjectTick() {
  LWORD i=0;
  THING *thing=NULL;
  LWORD rotTimer;

  if (objectIndex.iNum == 0) return;
  while (1) {
    if (thing == objectIndex.iThing[i]) i++;
    if (i>=objectIndex.iNum) break;
    thing = objectIndex.iThing[i];

    /* do a tick */

    /* Check for objects "rotting" */
    if (Obj(thing)->oTemplate->oType == OTYPE_CONTAINER) {
      rotTimer = OBJECTGETFIELD(thing, OF_CONTAINER_ROT);
   
      if (rotTimer>0) {
        rotTimer--;
        if (rotTimer==0) {
          FLAG cFlag;

          SendAction("$n decays into a cloud of dust and blows away\n",
                     thing,
                     NULL,
                     SEND_ROOM|SEND_VISIBLE
          );
          /* keep players items (mobs items rot with them) */
          cFlag  = OBJECTGETFIELD(thing, OF_CONTAINER_CFLAG);
          if (BIT(cFlag, OCF_PLAYERCORPSE)) {
            while (thing->tContain)
              ThingTo(thing->tContain, Base(thing)->bInside);
          }
          THINGFREE(thing); /* Extract object from game & free its memory */
        } else
          OBJECTSETFIELD(thing, OF_CONTAINER_ROT, rotTimer);
      }
  
    /* Check for objects "rotting" */
    } else if (Obj(thing)->oTemplate->oType == OTYPE_EXIT) {
      rotTimer = OBJECTGETFIELD(thing, OF_EXIT_ROT);
   
      if (rotTimer>0) {
        rotTimer--;
        if (rotTimer==0) {
          SendAction("$n shimmers briefly and then winks out of existence\n",
                     thing,
                     NULL,
                     SEND_ROOM|SEND_VISIBLE
          );
        } else
          OBJECTSETFIELD(thing, OF_EXIT_ROT, rotTimer);
      }
    }

  }
}

void ObjectIdle(THING *object) {

  /* let code handler get first crack at it */
  if ((CodeParseIdle(object))) return;

  /* Okay now what will objs do in these situations - precious little so far */
}

/* Get the value of an object field */
LWORD ObjectGetField(BYTE oType, ODETAIL *oDetail, WORD fieldNum) {
  OTYPELIST *typeEntry;
  LWORD      entry;
  LWORD      value;
  BYTE       type;

  typeEntry = &oTypeList[oType];
  entry = typeEntry->oField[fieldNum][1];
  type = typeEntry->oField[fieldNum][0];

  switch(type) {
  case 'L':
    value = oDetail->lValue[entry];
    break;
  case 'W':
    value = oDetail->lValue[entry/2];
    value >>= (16*(entry%2));
    value &= 65535;
    value = (LWORD)((WORD) value);
    /* value = oDetail->wValue[entry]; */
    break;
  case 'B':
    value = oDetail->lValue[entry/4];
    value >>= (8*(entry%4));
    value &= 255;
    value = (LWORD)((SBYTE) value);
    /* value = oDetail->bValue[entry]; */
    break;
  }

  return value;
}

/*
 * Set the value of an object field - low level routine, intended to be
 * called by ObjectSetFieldStr mainly
 */
void ObjectSetField(BYTE oType, ODETAIL *oDetail, WORD fieldNum, LWORD value) {
  OTYPELIST *typeEntry;
  LWORD      entry;
  BYTE       type;
  LWORD      lValue;
  ULWORD     mask;
  WORD       wValue;
  BYTE       bValue;

  typeEntry = &oTypeList[oType];
  entry = typeEntry->oField[fieldNum][1];
  type = typeEntry->oField[fieldNum][0];

  switch(type) {
  case 'L':
    oDetail->lValue[entry] = value;
    break;
  case 'W':
    wValue = value;
    lValue = (wValue << (16*(entry%2)));
    mask = 65535;
    mask <<= (16*(entry%2));

    oDetail->lValue[entry/2] &= (~(mask));
    oDetail->lValue[entry/2] |= lValue;
    /* oDetail->wValue[entry] = value; */
    break;
  case 'B':
    bValue = (BYTE)(SBYTE)value;
    lValue = (bValue << (8*(entry%4)));
    mask = 255;
    mask <<= (8*(entry%4));

    oDetail->lValue[entry/4] &= (~(mask));
    oDetail->lValue[entry/4] |= lValue;
    /* oDetail->bValue[entry] = value; */
    break;
  }
}

/* Flip flags in queston */
void ObjectFlipFieldFlag(BYTE oType, ODETAIL *oDetail, WORD fieldNum, LWORD value) {
  OTYPELIST *typeEntry;
  LWORD      entry;
  BYTE       type;
  BYTE       flag;
  BYTE       bValue;
  UWORD      wValue;

  typeEntry = &oTypeList[oType];
  entry = typeEntry->oField[fieldNum][1];
  type = typeEntry->oField[fieldNum][0];
  flag = typeEntry->oField[fieldNum][2];

  /* will do nothing for non-flag fields */
  if (flag=='F') {
    switch(type) {
    case 'L': {
      BITFLIP(oDetail->lValue[entry],value);
      break;
    }
    case 'W':
      wValue = ObjectGetField(oType, oDetail, fieldNum);
      BITFLIP(wValue,value);
      ObjectSetField(oType, oDetail, fieldNum, wValue);
      break;
    case 'B':
      bValue = ObjectGetField(oType, oDetail, fieldNum);
      BITFLIP(bValue,value);
      ObjectSetField(oType, oDetail, fieldNum, bValue);
      break;
    }
  }
}

/* Look up the value of one the 16 bytes of object field information */
LWORD ObjectGetFieldNumber(BYTE *key, BYTE oType) {
  OTYPELIST *typeEntry;
  LWORD      type;

  /* assumes oType is valid */
  typeEntry = &oTypeList[oType];
  if (!*typeEntry->oTypeStr)
    return -1;

  type = TYPEFIND(key, typeEntry->oFieldStr);
  if (type == -1) {
    if (sscanf(key, " %ld", &type) < 1)
      return -1;
  }
  return type;
}

/* Look up the value of one the 16 bytes of object field information */
LWORD ObjectGetFieldStr(BYTE oType, ODETAIL *oDetail, WORD fieldNum, BYTE *fieldStr, WORD maxLen) {
  OTYPELIST *typeEntry;
  BYTE       buf[128];
  LWORD      value;
  LWORD      i;

  if (!fieldStr) return FALSE;
  if (fieldNum < 0) return FALSE;
  if (fieldNum >= OBJECT_MAX_FIELD) return FALSE;

  /* ensure valid type */
  for (i=0; i<=oType; i++) {
    typeEntry = &oTypeList[i];
    if (!*typeEntry->oTypeStr)
      return FALSE;
  }
  /* ensure valid field */
  for (i=0; i<=fieldNum; i++)
    if (!typeEntry->oField[fieldNum][0])
      return FALSE; /* whoops no field info here */

  value = ObjectGetField(oType, oDetail, fieldNum);

  /* assume buf is big enough to contain name at the very least */
  sprintf(fieldStr, "%2d.^g%-13s^G:", fieldNum, typeEntry->oFieldStr[fieldNum]);

  switch(typeEntry->oField[fieldNum][2]) {
  case 'T':
    TypeSprintf(buf, value, (ULWORD)typeEntry->oArray[fieldNum].oList, typeEntry->oArray[fieldNum].oSize, 128);
    strcat(fieldStr, buf);
    strcat(fieldStr, "\n");
    break;
  case 'F':
    FlagSprintf(buf, value, typeEntry->oArray[fieldNum].oList, ' ', 128);
    strcat(fieldStr, buf);
    strcat(fieldStr, "\n");
    break;
  default:
    sprintf(fieldStr+strlen(fieldStr), "%ld\n", value);
    break;
  }

  return TRUE;
}

/*
 * Set the value of one the 16 bytes of object field information
 * error check the fields etc.
 * Return FALSE if we cannot
 * (this is the high-level one, for cmd_obj...)
 */
LWORD ObjectSetFieldStr(THING *thing, BYTE oType, ODETAIL *oDetail, WORD fieldNum, BYTE *fieldStr) {
  OTYPELIST *typeEntry;
  LWORD      value;
  LWORD      i;

  if (fieldNum < 0) return FALSE;
  if (fieldNum >= OBJECT_MAX_FIELD) return FALSE;

  /* ensure valid type */
  for (i=0; i<=oType; i++) {
    typeEntry = &oTypeList[i];
    if (!*typeEntry->oTypeStr)
      return FALSE;
  }
  /* ensure valid fieldNum */
  for (i=0; i<=fieldNum; i++)
    if (!*typeEntry->oField[fieldNum])
      return FALSE; /* whoops no field info here */

  switch(typeEntry->oField[fieldNum][2]) {
  case 'T':
    if (!fieldStr || !*fieldStr) 
      value = -1;
    else 
      value = TypeFind(fieldStr, (ULWORD)typeEntry->oArray[fieldNum].oList, typeEntry->oArray[fieldNum].oSize);
    if (value == -1) { /* show 'em possibles */
      SendArray((ULWORD)typeEntry->oArray[fieldNum].oList, typeEntry->oArray[fieldNum].oSize, 3, thing);
      return FALSE;
    }
    ObjectSetField(oType, oDetail, fieldNum, value);
    break;
  case 'F':
    if (!fieldStr || !*fieldStr) 
      value = 0;
    else 
      value = FlagFind(fieldStr, typeEntry->oArray[fieldNum].oList);
    if (value == 0) { /* show 'em possibles */
      SENDARRAY(typeEntry->oArray[fieldNum].oList, 3, thing);
      return FALSE;
    }

    ObjectFlipFieldFlag(oType, oDetail, fieldNum, value);
    break;
  case ' ':
    if (!fieldStr || !*fieldStr)  return FALSE;
    if (sscanf(fieldStr, " %ld", &value) < 1)
      return FALSE;
    ObjectSetField(oType, oDetail, fieldNum, value);
    break;
  }

  return TRUE;
}


THING *ObjectGetAmmo(THING *thing, LWORD *aType, LWORD *aUse, LWORD *aLeft) {
  THING *ammo     = NULL;
  LWORD  ammoType = 0;
  LWORD  ammoUse  = 0;
  LWORD  ammoLeft = 0;
  
  if (thing && thing->tType==TTYPE_OBJ) {
    /* determine if we use ammo etc */
    switch (Obj(thing)->oTemplate->oType) {
    case OTYPE_LIGHT:
      ammoType = OBJECTGETFIELD(thing, OF_LIGHT_AMMOTYPE);
      ammoUse  = OBJECTGETFIELD(thing, OF_LIGHT_AMMOUSE);
      break;
    case OTYPE_SCANNER:
      ammoType = OBJECTGETFIELD(thing, OF_SCANNER_AMMOTYPE);
      ammoUse  = OBJECTGETFIELD(thing, OF_SCANNER_AMMOUSE);
      break;
    case OTYPE_DEVICE:
      ammoType = OBJECTGETFIELD(thing, OF_DEVICE_AMMOTYPE);
      ammoUse  = OBJECTGETFIELD(thing, OF_DEVICE_AMMOUSE);
      break;
    case OTYPE_WEAPON:
      ammoType = OBJECTGETFIELD(thing, OF_WEAPON_AMMOTYPE);
      ammoUse  = OBJECTGETFIELD(thing, OF_WEAPON_AMMOUSE);
      break;
    case OTYPE_ARMOR:
      ammoType = OBJECTGETFIELD(thing, OF_ARMOR_AMMOTYPE);
      ammoUse  = OBJECTGETFIELD(thing, OF_ARMOR_AMMOUSE);
      break;
    }
  
    if (thing->tContain 
    && thing->tContain->tType == TTYPE_OBJ
    && Obj(thing->tContain)->oTemplate->oType == OTYPE_AMMO)
      ammo = thing->tContain;
    if (ammo)
      ammoLeft = OBJECTGETFIELD(ammo, OF_AMMO_AMMOLEFT);
  }
  
  if (aType) *aType = ammoType;
  if (aUse)  *aUse  = ammoUse;
  if (aLeft) *aLeft = ammoLeft;
  return ammo;
}

void ObjectUseAmmo(THING *thing) {
  THING *ammo = NULL;
  LWORD  ammoType;
  LWORD  ammoUse;
  LWORD  ammoLeft;

  if (!thing) return;
  ammo = ObjectGetAmmo(thing, &ammoType, &ammoUse, &ammoLeft);
  if (!ammo) return;
  if (!ammoType) return;
  if (ammoUse<=0) return;

  ammoLeft -= ammoUse;
  OBJECTSETFIELD(ammo, OF_AMMO_AMMOLEFT, ammoLeft);

  /* get rid of ammo if used up */
  if (ammoLeft<ammoUse)
    THINGFREE(ammo);
}

void ObjectShowLight(THING *show, THING *thing) {
  BYTE   buf[256];
  THING *ammo = NULL;
  LWORD  ammoType;
  LWORD  ammoUse;
  LWORD  ammoLeft;
  ammo = ObjectGetAmmo(show, &ammoType, &ammoUse, &ammoLeft);

  /* Info about ammo */
  if (ammoType) {
    if (ammo) {
      if (ammoUse>0) {
        sprintf(buf, 
                "^gIt has enough power to stay lit for %ld more ticks\n", 
                ammoLeft/ammoUse);
        SendThing(buf, thing);
      } else {
        SendThing("^gIt has enough power for about a zillion more years of light!\n", thing);
      }
    } else {
      SendThing("^rIt needs ", thing);
      SendThing(oAmmoList[ammoType].oADesc, thing);
      SendThing(" to work\n", thing);
    }
  }
}

void ObjectShowScanner(THING *show, THING *thing) {
  BYTE   buf[256];
  FLAG   oFlag       = OBJECTGETFIELD(show, OF_SCANNER_SFLAG);
  LWORD  bioScanned  = OBJECTGETFIELD(show, OF_SCANNER_BIO);
  LWORD  chipScanned = OBJECTGETFIELD(show, OF_SCANNER_CHIP);
  THING *ammo        = NULL;
  LWORD  ammoType;
  LWORD  ammoUse;
  LWORD  ammoLeft;
  ammo = ObjectGetAmmo(show, &ammoType, &ammoUse, &ammoLeft);
        
  if (BIT(oFlag, OSF_SCANBIO)) {
    sprintf(buf, 
            "^gIt contains ^w%ld^g credits worth of scanned biologicals\n", 
            bioScanned);
    SendThing(buf, thing);
  }
  if (BIT(oFlag, OSF_SCANCHIP)) {
    sprintf(buf, 
            "^gIt contains ^w%ld^g credits worth of scanned chip technology\n", 
            chipScanned);
    SendThing(buf, thing);
  }
  sprintf(buf, 
          "^gIt has %ld units of free memory left\n", 
          OBJECTGETFIELD(show, OF_SCANNER_MAX) - bioScanned - chipScanned);
  SendThing(buf, thing);

  /* Info about ammo */
  if (ammoType) {
    if (ammo) {
      if (ammoUse>0) {
        sprintf(buf, 
                "^gIt has enough power for %ld more scans\n", 
                ammoLeft/ammoUse);
        SendThing(buf, thing);
      } else {
        SendThing("^gIt has enough power for about a zillion more scans!\n", thing);
      }
    } else {
      SendThing("^rIt needs ", thing);
      SendThing(oAmmoList[ammoType].oADesc, thing);
      SendThing(" to work\n", thing);
    }
  }
}

void ObjectShowDevice(THING *show, THING *thing) {
  BYTE   buf[256];
  THING *ammo = NULL;
  LWORD  ammoType;
  LWORD  ammoUse;
  LWORD  ammoLeft;
  ammo = ObjectGetAmmo(show, &ammoType, &ammoUse, &ammoLeft);

  /* Info about ammo */
  if (ammoType) {
    if (ammo) {
      if (ammoUse>0) {
        sprintf(buf, 
                "^gIt has enough power for for %ld more uses\n", 
                ammoLeft/ammoUse);
        SendThing(buf, thing);
      } else {
        SendThing("^gIt can be used about a zillion more times!\n", thing);
      }
    } else {
      SendThing("^rIt needs ", thing);
      SendThing(oAmmoList[ammoType].oADesc, thing);
      SendThing(" to work\n", thing);
    }
  }
}

void ObjectShowWeapon(THING *show, THING *thing) {
  BYTE   buf[256];
  THING *ammo = NULL;
  LWORD  ammoType;
  LWORD  ammoUse;
  LWORD  ammoLeft;
  LWORD  weaponType;
  ammo = ObjectGetAmmo(show, &ammoType, &ammoUse, &ammoLeft);

  /* Info about ammo */
  if (ammoType) {
    if (ammo) {
      if (ammoUse>0) {
        sprintf(buf, 
                "^gIt has enough ammo for %ld more shots\n", 
                ammoLeft/ammoUse);
        SendThing(buf, thing);
      } else {
        SendThing("^gIt can be used about a zillion more times!\n", thing);
      }
    } else {
      SendThing("^rIt needs ", thing);
      SendThing(oAmmoList[ammoType].oADesc, thing);
      SendThing(" to work\n", thing);
    }
  }
  /* Skill to use */
  weaponType = OBJECTGETFIELD(show, OF_WEAPON_TYPE);
  SendHint("^;HINT: Practice ^<", thing);
  TYPESPRINTF(buf, weaponType, weaponList, sizeof(buf));
  SendHint(buf, thing);
  SendHint(" ^;to improve with this weapon\n", thing);
}

BYTE ObjectShowContainer(THING *show, THING *thing) {
  FLAG oFlag = OBJECTGETFIELD(show, OF_CONTAINER_CFLAG);
  
  if (BIT(oFlag, OCF_CLOSED))
    SendThing("^gIts Closed...\n", thing);
  if (BIT(oFlag, OCF_LOCKED))
    SendThing("^gIts Locked...\n", thing);
  /* Cant see inside a closed object */
  if (BIT(oFlag, OCF_CLOSED))
    return FALSE;
  if (!show->tContain)
    SendThing("^gIts Empty...\n", thing);
    
  return TRUE;
}

/* Should show "Its about half full of <LDesc>" */
void ObjectShowDrinkcon(THING *show, THING *thing) {
  BYTE  buf[255];
  LWORD dMax     = OBJECTGETFIELD(show, OF_DRINKCON_MAX);
  LWORD dContain = OBJECTGETFIELD(show, OF_DRINKCON_CONTAIN);
  LWORD dLiquid  = OBJECTGETFIELD(show, OF_DRINKCON_LIQUID);
  LWORD ratio;

  /* negative dContain is an infinite source */
  if (dMax <= 0)
    ratio = 0;
  else if (dContain < 0)
    ratio = 100;
  else
    ratio = 100 * dContain / dMax;
  TYPECHECK(dLiquid, oLiquidList); /* ensure no out of range errors */
  
  if (ratio <= 0)
    sprintf(buf, "^wIts empty\n");
  else if (ratio <= 7)
    sprintf(buf, "^wIt contains %s, but its almost empty\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 19)
    sprintf(buf, "^wIts less than a quarter full of %s\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 29)
    sprintf(buf, "^wIts about a quarter full of %s\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 41)
    sprintf(buf, "^wIts about a third full of %s\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 58)
    sprintf(buf, "^wIts about half full of %s\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 71)
    sprintf(buf, "^wIts about two-thirds full of %s\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 81)
    sprintf(buf, "^wIts about three-quarters full of %s\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 93)
    sprintf(buf, "^wIts more than three-quarters full of %s\n", oLiquidList[dLiquid].oLDesc);
  else if (ratio <= 99)
    sprintf(buf, "^wIts almost full of %s\n", oLiquidList[dLiquid].oLDesc);
  else
    sprintf(buf, "^wIts completely full of %s!\n", oLiquidList[dLiquid].oLDesc);

  SendThing(buf, thing);
}

void ObjectShowArmor(THING *show, THING *thing) {
  BYTE   buf[256];
  THING *ammo = NULL;
  LWORD  ammoType;
  LWORD  ammoUse;
  LWORD  ammoLeft;
  ammo = ObjectGetAmmo(show, &ammoType, &ammoUse, &ammoLeft);

  /* Info about ammo */
  if (ammoType) {
    if (ammo) {
      if (ammoUse>0) {
        sprintf(buf, 
                "^gIt has enough power for for %ld more minutes\n", 
                ammoLeft/ammoUse);
        SendThing(buf, thing);
      } else {
        SendThing("^gIt has enough power left to keep running for eternity!\n", thing);
      }
    } else {
      SendThing("^rIt needs ", thing);
      SendThing(oAmmoList[ammoType].oADesc, thing);
      SendThing(" to work\n", thing);
    }
  }
}