/* 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); } } }