/***************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.diku' as well the Merc *
* license in 'license.merc'. In particular, you may not remove either *
* of these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************/
/*
MurkMUD++ - A Windows compatible, C++ compatible Merc 2.2 Mud.
\author Jon A. Lambert
\date 08/30/2006
\version 1.4
\remarks
This source code copyright (C) 2005, 2006 by Jon A. Lambert
All rights reserved.
Use governed by the MurkMUD++ public license found in license.murk++
*/
#include "os.hpp"
#include "config.hpp"
#include "globals.hpp"
#include "object.hpp"
#include "room.hpp"
#include "affect.hpp"
#include "extra.hpp"
#include "objproto.hpp"
#include "io.hpp"
#include "utils.hpp"
Object::Object() :
in_obj(NULL), carried_by(NULL), pIndexData(NULL),
in_room(NULL), item_type(0), extra_flags(0), wear_flags(0), wear_loc(0),
weight(0), cost(0), level(0), timer(0) {
memset(value, 0, sizeof value);
}
/*
* Return ascii name of an item type.
*/
std::string Object::item_type_name ()
{
switch (item_type) {
case ITEM_LIGHT:
return "light";
case ITEM_SCROLL:
return "scroll";
case ITEM_WAND:
return "wand";
case ITEM_STAFF:
return "staff";
case ITEM_WEAPON:
return "weapon";
case ITEM_TREASURE:
return "treasure";
case ITEM_ARMOR:
return "armor";
case ITEM_POTION:
return "potion";
case ITEM_FURNITURE:
return "furniture";
case ITEM_TRASH:
return "trash";
case ITEM_CONTAINER:
return "container";
case ITEM_DRINK_CON:
return "drink container";
case ITEM_KEY:
return "key";
case ITEM_FOOD:
return "food";
case ITEM_MONEY:
return "money";
case ITEM_BOAT:
return "boat";
case ITEM_CORPSE_NPC:
return "npc corpse";
case ITEM_CORPSE_PC:
return "pc corpse";
case ITEM_FOUNTAIN:
return "fountain";
case ITEM_PILL:
return "pill";
case ITEM_DARKNESS:
return "darkness exuding";
}
bug_printf ("Item_type_name: unknown type %d.", item_type);
return "(unknown)";
}
bool Object::can_wear (sh_int part) {
return wear_flags & part;
}
bool Object::is_obj_stat(sh_int stat) {
return extra_flags & stat;
}
/*
* Return # of objects which an object counts as.
* Thanks to Tony Chamberlain for the correct recursive code here.
*/
int Object::get_obj_number ()
{
int number = 0;
if (item_type == ITEM_CONTAINER) {
for (ObjIter o = contains.begin(); o != contains.end(); o++)
number += (*o)->get_obj_number();
} else
number = 1;
return number;
}
/*
* Return weight of an object, including weight of contents.
*/
int Object::get_obj_weight ()
{
int wt = weight;
for (ObjIter o = contains.begin(); o != contains.end(); o++)
wt += (*o)->get_obj_weight ();
return wt;
}
/*
* Move an obj out of a room.
*/
void Object::obj_from_room ()
{
Room *in_rm = in_room;
if (in_rm == NULL) {
bug_printf ("obj_from_room: NULL.");
return;
}
in_rm->contents.erase(find(in_rm->contents.begin(),in_rm->contents.end(),this));
in_room = NULL;
return;
}
/*
* Move an obj into a room.
*/
void Object::obj_to_room (Room * pRoomIndex)
{
pRoomIndex->contents.push_back(this);
in_room = pRoomIndex;
carried_by = NULL;
in_obj = NULL;
return;
}
/*
* Move an object into an object.
*/
void Object::obj_to_obj (Object * obj_to)
{
obj_to->contains.push_back(this);
in_obj = obj_to;
in_room = NULL;
carried_by = NULL;
for (; obj_to != NULL; obj_to = obj_to->in_obj) {
if (obj_to->carried_by != NULL) {
obj_to->carried_by->carry_number += get_obj_number();
obj_to->carried_by->carry_weight += get_obj_weight();
}
}
return;
}
/*
* Move an object out of an object.
*/
void Object::obj_from_obj ()
{
Object *obj_from = in_obj;
if (obj_from == NULL) {
bug_printf ("Obj_from_obj: null obj_from.");
return;
}
obj_from->contains.erase(find(obj_from->contains.begin(), obj_from->contains.end(), this));
in_obj = NULL;
for (; obj_from != NULL; obj_from = obj_from->in_obj) {
if (obj_from->carried_by != NULL) {
obj_from->carried_by->carry_number -= get_obj_number();
obj_from->carried_by->carry_weight -= get_obj_weight();
}
}
return;
}
/*
* Give an obj to a char.
*/
void Object::obj_to_char (Character * ch)
{
ch->carrying.push_back(this);
carried_by = ch;
in_room = NULL;
in_obj = NULL;
ch->carry_number += get_obj_number();
ch->carry_weight += get_obj_weight();
}
/*
* Find the ac value of an obj, including position effect.
*/
int Object::apply_ac (int iWear)
{
if (item_type != ITEM_ARMOR)
return 0;
switch (iWear) {
case WEAR_BODY:
return 3 * value[0];
case WEAR_HEAD:
return 2 * value[0];
case WEAR_LEGS:
return 2 * value[0];
case WEAR_FEET:
return value[0];
case WEAR_HANDS:
return value[0];
case WEAR_ARMS:
return value[0];
case WEAR_SHIELD:
return value[0];
case WEAR_FINGER_L:
return value[0];
case WEAR_FINGER_R:
return value[0];
case WEAR_NECK_1:
return value[0];
case WEAR_NECK_2:
return value[0];
case WEAR_ABOUT:
return 2 * value[0];
case WEAR_WAIST:
return value[0];
case WEAR_WRIST_L:
return value[0];
case WEAR_WRIST_R:
return value[0];
case WEAR_HOLD:
return value[0];
}
return 0;
}
/*
* Take an obj from its character.
*/
void Object::obj_from_char ()
{
Character *ch = carried_by;
if (ch == NULL) {
bug_printf ("Obj_from_char: null ch.");
return;
}
if (wear_loc != WEAR_NONE)
ch->unequip_char(this);
ch->carrying.erase(find(ch->carrying.begin(),ch->carrying.end(), this));
carried_by = NULL;
ch->carry_number -= get_obj_number();
ch->carry_weight -= get_obj_weight();
return;
}
/*
* Extract an obj from the world.
*/
void Object::extract_obj ()
{
Object *obj_content;
if (in_room != NULL)
obj_from_room ();
else if (carried_by != NULL)
obj_from_char ();
else if (in_obj != NULL)
obj_from_obj ();
ObjIter o, next;
for (o = contains.begin(); o != contains.end(); o = next) {
obj_content = *o;
next = ++o;
obj_content->extract_obj();
}
deepobnext = object_list.erase(find(object_list.begin(), object_list.end(), this));
AffIter af;
for (af = affected.begin(); af != affected.end(); af++) {
delete *af;
}
affected.clear();
std::list<ExtraDescription *>::iterator ed;
for (ed = extra_descr.begin(); ed != extra_descr.end(); ed++) {
delete *ed;
}
extra_descr.clear();
--pIndexData->count;
delete this;
return;
}
/*
* Write an object and its contents.
*/
void Object::fwrite_obj (Character * ch, std::ofstream & fp, int iNest)
{
/*
* Castrate storage characters.
*/
if (ch->level < level || item_type == ITEM_KEY || item_type == ITEM_POTION)
return;
fp << "#OBJECT\n";
fp << "Nest " << iNest << "\n";
fp << "Name " << name << "~\n";
fp << "ShortDescr " << short_descr << "~\n";
fp << "Description " << description << "~\n";
fp << "Vnum " << pIndexData->vnum << "\n";
fp << "ExtraFlags " << extra_flags << "\n";
fp << "WearFlags " << wear_flags << "\n";
fp << "WearLoc " << wear_loc << "\n";
fp << "ItemType " << item_type << "\n";
fp << "Weight " << weight << "\n";
fp << "Level " << level << "\n";
fp << "Timer " << timer << "\n";
fp << "Cost " << cost << "\n";
fp << "Values " << value[0] << " " << value[1] << " " <<
value[2] << " " << value[3] << "\n";
switch (item_type) {
case ITEM_POTION:
case ITEM_SCROLL:
if (value[1] > 0) {
fp << "Spell 1 '" << skill_table[value[1]].name << "'\n";
}
if (value[2] > 0) {
fp << "Spell 2 '" << skill_table[value[2]].name << "'\n";
}
if (value[3] > 0) {
fp << "Spell 3 '" << skill_table[value[3]].name << "'\n";
}
break;
case ITEM_PILL:
case ITEM_STAFF:
case ITEM_WAND:
if (value[3] > 0) {
fp << "Spell 3 '" << skill_table[value[3]].name << "'\n";
}
break;
}
AffIter af;
for (af = affected.begin(); af != affected.end(); af++) {
fp << "Affect " << (*af)->type << " " << (*af)->duration << " " <<
(*af)->modifier << " " << (*af)->location << " " << (*af)->bitvector << "\n";
}
std::list<ExtraDescription *>::iterator ed;
for (ed = extra_descr.begin(); ed != extra_descr.end(); ed++) {
fp << "ExtraDescr " << (*ed)->keyword << "~ " <<
(*ed)->description << "~\n";
}
fp << "End\n\n";
std::list<Object*>::reverse_iterator o;
for (o = contains.rbegin(); o != contains.rend(); o++)
(*o)->fwrite_obj (ch, fp, iNest + 1);
return;
}
bool Object::fread_obj (Character * ch, std::ifstream & fp)
{
std::string word;
int iNest = 0;
bool fMatch;
bool fNest = false;
bool fVnum = true;
for (;;) {
word = fp.eof() ? std::string("End") : fread_word (fp);
fMatch = false;
switch (toupper (word[0])) {
case '*':
fMatch = true;
fread_to_eol (fp);
break;
case 'A':
if (!str_cmp (word, "Affect")) {
Affect *paf;
paf = new Affect();
paf->type = fread_number (fp);
paf->duration = fread_number (fp);
paf->modifier = fread_number (fp);
paf->location = fread_number (fp);
paf->bitvector = fread_number (fp);
affected.push_back(paf);
fMatch = true;
break;
}
break;
case 'C':
KEY ("Cost", cost, fread_number (fp));
break;
case 'D':
KEY ("Description", description, fread_string (fp));
break;
case 'E':
KEY ("ExtraFlags", extra_flags, fread_number (fp));
if (!str_cmp (word, "ExtraDescr")) {
ExtraDescription *ed;
ed = new ExtraDescription();
ed->keyword = fread_string (fp);
ed->description = fread_string (fp);
extra_descr.push_back(ed);
fMatch = true;
}
if (!str_cmp (word, "End")) {
if (!fNest || !fVnum) {
bug_printf ("Fread_obj: incomplete object.");
return false;
} else {
object_list.push_back(this);
pIndexData->count++;
if (iNest == 0 || rgObjNest[iNest] == NULL)
obj_to_char (ch);
else
obj_to_obj (rgObjNest[iNest - 1]);
return true;
}
}
break;
case 'I':
KEY ("ItemType", item_type, fread_number (fp));
break;
case 'L':
KEY ("Level", level, fread_number (fp));
break;
case 'N':
KEY ("Name", name, fread_string (fp));
if (!str_cmp (word, "Nest")) {
iNest = fread_number (fp);
if (iNest < 0 || iNest >= MAX_NEST) {
bug_printf ("Fread_obj: bad nest %d.", iNest);
} else {
rgObjNest[iNest] = this;
fNest = true;
}
fMatch = true;
}
break;
case 'S':
KEY ("ShortDescr", short_descr, fread_string (fp));
if (!str_cmp (word, "Spell")) {
int iValue;
int sn;
iValue = fread_number (fp);
sn = skill_lookup (fread_word (fp));
if (iValue < 0 || iValue > 3) {
bug_printf ("Fread_obj: bad iValue %d.", iValue);
} else if (sn < 0) {
bug_printf ("Fread_obj: unknown skill.");
} else {
value[iValue] = sn;
}
fMatch = true;
break;
}
break;
case 'T':
KEY ("Timer", timer, fread_number (fp));
break;
case 'V':
if (!str_cmp (word, "Values")) {
value[0] = fread_number (fp);
value[1] = fread_number (fp);
value[2] = fread_number (fp);
value[3] = fread_number (fp);
fMatch = true;
break;
}
if (!str_cmp (word, "Vnum")) {
int vnum;
vnum = fread_number (fp);
if ((pIndexData = get_obj_index (vnum)) == NULL)
bug_printf ("Fread_obj: bad vnum %d.", vnum);
else
fVnum = true;
fMatch = true;
break;
}
break;
case 'W':
KEY ("WearFlags", wear_flags, fread_number (fp));
KEY ("WearLoc", wear_loc, fread_number (fp));
KEY ("Weight", weight, fread_number (fp));
break;
}
if (!fMatch) {
bug_printf ("Fread_obj: no match.");
fread_to_eol (fp);
}
}
return false;
}
std::string Object::format_obj_to_char (Character * ch, bool fShort)
{
std::string buf;
if (is_obj_stat(ITEM_INVIS))
buf.append("(Invis) ");
if (ch->is_affected (AFF_DETECT_EVIL) && is_obj_stat(ITEM_EVIL))
buf.append("(Red Aura) ");
if (ch->is_affected (AFF_DETECT_MAGIC) && is_obj_stat(ITEM_MAGIC))
buf.append("(Magical) ");
if (is_obj_stat(ITEM_GLOW))
buf.append("(Glowing) ");
if (is_obj_stat(ITEM_HUM))
buf.append("(Humming) ");
if (fShort) {
if (!short_descr.empty())
buf.append(short_descr);
} else {
if (!description.empty())
buf.append(description);
}
return buf;
}