--- deliantra/server/common/object.C 2007/01/21 21:28:27 1.120 +++ deliantra/server/common/object.C 2007/06/04 12:19:08 1.158 @@ -1,25 +1,25 @@ /* - * CrossFire, A Multiplayer game for X-windows + * This file is part of Crossfire TRT, the Multiplayer Online Role Playing Game. * - * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team - * Copyright (C) 2001 Mark Wedel & Crossfire Development Team - * Copyright (C) 1992 Frank Tore Johansen + * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team + * Copyright (©) 2001,2007 Mark Wedel & Crossfire Development Team + * Copyright (©) 1992,2007 Frank Tore Johansen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * Crossfire TRT is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * The authors can be reached via e-mail at + * You should have received a copy of the GNU General Public License along + * with Crossfire TRT; if not, write to the Free Software Foundation, Inc. 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The authors can be reached via e-mail to */ /* Eneq(@csd.uu.se): Added weight-modifiers in environment of objects. @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -326,10 +327,11 @@ long sum; object *inv; - for (sum = 0, inv = op->inv; inv != NULL; inv = inv->below) + for (sum = 0, inv = op->inv; inv; inv = inv->below) { if (inv->inv) sum_weight (inv); + sum += inv->carrying + inv->weight * (inv->nrof ? inv->nrof : 1); } @@ -359,7 +361,6 @@ * Some error messages. * The result of the dump is stored in the static global errmsg array. */ - char * dump_object (object *op) { @@ -367,7 +368,7 @@ return strdup ("[NULLOBJ]"); object_freezer freezer; - save_object (freezer, op, 1); + op->write (freezer); return freezer.as_string (); } @@ -451,13 +452,79 @@ this->owner = owner; } +int +object::slottype () const +{ + if (type == SKILL) + { + if (IS_COMBAT_SKILL (subtype)) return slot_combat; + if (IS_RANGED_SKILL (subtype)) return slot_ranged; + } + else + { + if (slot [body_combat].info) return slot_combat; + if (slot [body_range ].info) return slot_ranged; + } + + return slot_none; +} + +bool +object::change_weapon (object *ob) +{ + if (current_weapon == ob) + return true; + + if (chosen_skill) + chosen_skill->flag [FLAG_APPLIED] = false; + + current_weapon = ob; + chosen_skill = !ob || ob->type == SKILL ? ob : find_skill_by_name (this, ob->skill); + + if (chosen_skill) + chosen_skill->flag [FLAG_APPLIED] = true; + + update_stats (); + + if (ob) + { + // now check wether any body locations became invalid, in which case + // we cannot apply the weapon at the moment. + for (int i = 0; i < NUM_BODY_LOCATIONS; ++i) + if (slot[i].used < 0) + { + current_weapon = chosen_skill = 0; + update_stats (); + + new_draw_info_format (NDI_UNIQUE, 0, this, + "You try to balance all your items at once, " + "but the %s is just too much for your body. " + "[You need to unapply some items first.]", &ob->name); + return false; + } + + //new_draw_info_format (NDI_UNIQUE, 0, this, "You switch to your %s.", &ob->name); + } + else + ;//new_draw_info_format (NDI_UNIQUE, 0, this, "You unwield your weapons."); + + if (ob && !ob->flag [FLAG_APPLIED] && ob->type != SPELL) + { + LOG (llevError | logBacktrace, "%s changed to unapplied weapon %s", + &name, ob->debug_desc ()); + return false; + } + + return true; +} + /* Zero the key_values on op, decrementing the shared-string * refcounts and freeing the links. */ static void free_key_values (object *op) { - for (key_value *i = op->key_values; i != 0;) + for (key_value *i = op->key_values; i; ) { key_value *next = i->next; delete i; @@ -468,40 +535,24 @@ op->key_values = 0; } -/* - * copy_to first frees everything allocated by the dst object, - * and then copies the contents of itself into the second - * object, allocating what needs to be allocated. Basically, any - * data that is malloc'd needs to be re-malloc/copied. Otherwise, - * if the first object is freed, the pointers in the new object - * will point at garbage. - */ -void -object::copy_to (object *dst) +object & +object::operator =(const object &src) { - bool is_freed = QUERY_FLAG (dst, FLAG_FREED); - bool is_removed = QUERY_FLAG (dst, FLAG_REMOVED); - - *(object_copy *)dst = *this; + bool is_freed = flag [FLAG_FREED]; + bool is_removed = flag [FLAG_REMOVED]; - if (is_freed) - SET_FLAG (dst, FLAG_FREED); + *(object_copy *)this = src; - if (is_removed) - SET_FLAG (dst, FLAG_REMOVED); - - if (speed < 0) - dst->speed_left = speed_left - rndm (); + flag [FLAG_FREED] = is_freed; + flag [FLAG_REMOVED] = is_removed; /* Copy over key_values, if any. */ - if (key_values) + if (src.key_values) { key_value *tail = 0; - key_value *i; + key_values = 0; - dst->key_values = 0; - - for (i = key_values; i; i = i->next) + for (key_value *i = src.key_values; i; i = i->next) { key_value *new_link = new key_value; @@ -510,9 +561,9 @@ new_link->value = i->value; /* Try and be clever here, too. */ - if (!dst->key_values) + if (!key_values) { - dst->key_values = new_link; + key_values = new_link; tail = new_link; } else @@ -522,10 +573,46 @@ } } } +} + +/* + * copy_to first frees everything allocated by the dst object, + * and then copies the contents of itself into the second + * object, allocating what needs to be allocated. Basically, any + * data that is malloc'd needs to be re-malloc/copied. Otherwise, + * if the first object is freed, the pointers in the new object + * will point at garbage. + */ +void +object::copy_to (object *dst) +{ + *dst = *this; + + if (speed < 0) + dst->speed_left = speed_left - rndm (); dst->set_speed (dst->speed); } +void +object::instantiate () +{ + if (!uuid.seq) // HACK + uuid = gen_uuid (); + + speed_left = -0.1f; + /* copy the body_info to the body_used - this is only really + * need for monsters, but doesn't hurt to do it for everything. + * by doing so, when a monster is created, it has good starting + * values for the body_used info, so when items are created + * for it, they can be properly equipped. + */ + for (int i = NUM_BODY_LOCATIONS; i--; ) + slot[i].used = slot[i].info; + + attachable::instantiate (); +} + object * object::clone () { @@ -591,8 +678,6 @@ void update_object (object *op, int action) { - MoveType move_on, move_off, move_block, move_slow; - if (op == NULL) { /* this should never happen */ @@ -672,8 +757,7 @@ object::~object () { - if (index) - unlink (); + unlink (); free_key_values (this); } @@ -692,7 +776,9 @@ void object::unlink () { - assert (index);//D + if (!index) + return; + objects.erase (this); refcnt_dec (); } @@ -776,6 +862,7 @@ if (!drop_to_ground || !map || map->in_memory != MAP_IN_MEMORY + || map->nodrop || ms ().move_block == MOVE_ALL) { while (inv) @@ -901,10 +988,9 @@ * object will have no environment. If the object previously had an * environment, the x and y coordinates will be updated to * the previous environment. - * Beware: This function is called from the editor as well! */ void -object::remove_slow () +object::do_remove () { object *tmp, *last = 0; object *otmp; @@ -957,6 +1043,12 @@ { if (type == PLAYER) { + // leaving a spot always closes any open container on the ground + if (container && !container->env) + // this causes spurious floorbox updates, but it ensures + // that the CLOSE event is being sent. + close_container (); + --map->players; map->touch (); } @@ -1083,6 +1175,29 @@ return 0; } +void +object::expand_tail () +{ + if (more) + return; + + object *prev = this; + + for (archetype *at = arch->more; at; at = at->more) + { + object *op = arch_to_object (at); + + op->name = name; + op->name_pl = name_pl; + op->title = title; + + op->head = this; + prev->more = op; + + prev = op; + } +} + /* * same as insert_ob_in_map except it handles separate coordinates and does a clean * job preparing multi-part monsters. @@ -1122,31 +1237,23 @@ object * insert_ob_in_map (object *op, maptile *m, object *originator, int flag) { - object *tmp, *top, *floor = NULL; + assert (!op->flag [FLAG_FREED]); - if (QUERY_FLAG (op, FLAG_FREED)) - { - LOG (llevError, "Trying to insert freed object!\n"); - return NULL; - } - - if (!QUERY_FLAG (op, FLAG_REMOVED)) - LOG (llevError, "Trying to insert already inserted object %s\n", op->debug_desc ()); + object *top, *floor = NULL; op->remove (); - if (!m) - { - char *dump = dump_object (op); - LOG (llevError, "Trying to insert in null-map!\n%s\n", dump); - free (dump); - return op; - } +#if 0 + if (!m->active != !op->active) + if (m->active) + op->activate_recursive (); + else + op->deactivate_recursive (); +#endif if (out_of_map (m, op->x, op->y)) { - char *dump = dump_object (op); - LOG (llevError, "Trying to insert object outside the map.\n%s\n", dump); + LOG (llevError, "Trying to insert object outside the map.\n%s\n", op->debug_desc ()); #ifdef MANY_CORES /* Better to catch this here, as otherwise the next use of this object * is likely to cause a crash. Better to find out where it is getting @@ -1154,20 +1261,12 @@ */ abort (); #endif - free (dump); return op; } if (object *more = op->more) - { - if (!insert_ob_in_map (more, m, originator, flag)) - { - if (!op->head) - LOG (llevError, "BUG: insert_ob_in_map(): inserting op->more killed op\n"); - - return 0; - } - } + if (!insert_ob_in_map (more, m, originator, flag)) + return 0; CLEAR_FLAG (op, FLAG_REMOVED); @@ -1184,7 +1283,7 @@ /* this has to be done after we translate the coordinates. */ if (op->nrof && !(flag & INS_NO_MERGE)) - for (tmp = ms.bot; tmp; tmp = tmp->above) + for (object *tmp = ms.bot; tmp; tmp = tmp->above) if (object::can_merge (op, tmp)) { op->nrof += tmp->nrof; @@ -1261,14 +1360,14 @@ */ /* Have object 'fall below' other objects that block view. - * Unless those objects are exits, type 66 + * Unless those objects are exits. * If INS_ON_TOP is used, don't do this processing * Need to find the object that in fact blocks view, otherwise * stacking is a bit odd. */ if (!(flag & INS_ON_TOP) && ms.flags () & P_BLOCKSVIEW - && (op->face && !op->face->visibility)) + && (op->face && !faces [op->face].visibility)) { for (last = top; last != floor; last = last->below) if (QUERY_FLAG (last, FLAG_BLOCKSVIEW) && (last->type != EXIT)) @@ -1363,7 +1462,7 @@ */ /* if this is not the head or flag has been passed, don't check walk on status */ - if (!(flag & INS_NO_WALK_ON) && !op->head) + if (!(flag & INS_NO_WALK_ON) && op->head_ () == op) { if (check_move_on (op, originator)) return 0; @@ -1371,7 +1470,7 @@ /* If we are a multi part object, lets work our way through the check * walk on's. */ - for (tmp = op->more; tmp != NULL; tmp = tmp->more) + for (object *tmp = op->more; tmp; tmp = tmp->more) if (check_move_on (tmp, originator)) return 0; } @@ -1391,7 +1490,7 @@ /* first search for itself and remove any old instances */ for (tmp = op->ms ().bot; tmp; tmp = tmp->above) - if (!strcmp (tmp->arch->name, arch_string)) /* same archetype */ + if (!strcmp (tmp->arch->archname, arch_string)) /* same archetype */ tmp->destroy (); tmp1 = arch_to_object (archetype::find (arch_string)); @@ -1404,7 +1503,7 @@ object * object::insert_at (object *where, object *originator, int flags) { - where->map->insert (this, where->x, where->y, originator, flags); + return where->map->insert (this, where->x, where->y, originator, flags); } /* @@ -1454,7 +1553,6 @@ * * Return value: 'op' if something is left, NULL if the amount reached 0 */ - object * decrease_ob_nr (object *op, uint32 i) { @@ -1539,7 +1637,6 @@ * add_weight(object, weight) adds the specified weight to an object, * and also updates how much the environment(s) is/are carrying. */ - void add_weight (object *op, signed long weight) { @@ -1564,9 +1661,9 @@ return op; } - if (where->head) + if (where->head_ () != where) { - LOG (llevDebug, "Warning: Tried to insert object into wrong part of multipart object.\n"); + LOG (llevError | logBacktrace, "Warning: Tried to insert object into wrong part of multipart object.\n"); where = where->head; } @@ -1581,7 +1678,6 @@ * The function returns now pointer to inserted item, and return value can * be != op, if items are merged. -Tero */ - object * object::insert (object *op) { @@ -1908,19 +2004,6 @@ } /* - * set_cheat(object) sets the cheat flag (WAS_WIZ) in the object and in - * all it's inventory (recursively). - * If checksums are used, a player will get set_cheat called for - * him/her-self and all object carried by a call to this function. - */ -void -set_cheat (object *op) -{ - SET_FLAG (op, FLAG_WAS_WIZ); - flag_inv (op, FLAG_WAS_WIZ); -} - -/* * find_free_spot(object, map, x, y, start, stop) will search for * a spot at the given map and coordinates which will be able to contain * the given object. start and stop specifies how many squares @@ -1968,7 +2051,7 @@ if (!index) return -1; - return altern[RANDOM () % index]; + return altern [rndm (index)]; } /* @@ -1999,7 +2082,7 @@ end -= begin; while (--end) - swap (arr [end], arr [RANDOM () % (end + 1)]); + swap (arr [end], arr [rndm (end + 1)]); } /* new function to make monster searching more efficient, and effective! @@ -2045,7 +2128,7 @@ MoveType blocked, move_type; - if (exclude && exclude->head) + if (exclude && exclude->head_ () != exclude) { exclude = exclude->head; move_type = exclude->move_type; @@ -2078,7 +2161,7 @@ { for (tmp = ms.bot; tmp; tmp = tmp->above) if ((tmp->flag [FLAG_MONSTER] || tmp->type == PLAYER) - && (tmp != exclude || (tmp->head && tmp->head != exclude))) + && (tmp != exclude || (tmp->head_ () != tmp && tmp->head_ () != exclude))) break; if (tmp) @@ -2283,17 +2366,15 @@ object * object_create_clone (object *asrc) { - object *dst = 0, *tmp, *src, *part, *prev, *item; + object *dst = 0, *tmp, *src, *prev, *item; if (!asrc) return 0; - src = asrc; - if (src->head) - src = src->head; + src = asrc->head_ (); prev = 0; - for (part = src; part; part = part->more) + for (object *part = src; part; part = part->more) { tmp = part->clone (); tmp->x -= src->x; @@ -2321,44 +2402,6 @@ return dst; } -/* GROS - Creates an object using a string representing its content. */ -/* Basically, we save the content of the string to a temp file, then call */ -/* load_object on it. I admit it is a highly inefficient way to make things, */ -/* but it was simple to make and allows reusing the load_object function. */ -/* Remember not to use load_object_str in a time-critical situation. */ -/* Also remember that multiparts objects are not supported for now. */ -object * -load_object_str (const char *obstr) -{ - object *op; - char filename[MAX_BUF]; - - sprintf (filename, "%s/cfloadobstr2044", settings.tmpdir); - - FILE *tempfile = fopen (filename, "w"); - - if (tempfile == NULL) - { - LOG (llevError, "Error - Unable to access load object temp file\n"); - return NULL; - } - - fprintf (tempfile, obstr); - fclose (tempfile); - - op = object::create (); - - object_thawer thawer (filename); - - if (thawer) - load_object (thawer, op, 0); - - LOG (llevDebug, " load str completed, object=%s\n", &op->name); - CLEAR_FLAG (op, FLAG_REMOVED); - - return op; -} - /* This returns the first object in who's inventory that * has the same type and subtype match. * returns NULL if no match. @@ -2422,7 +2465,6 @@ return 0; } - /* * Updates the canonical_key in op to value. * @@ -2571,7 +2613,7 @@ char info2[256 * 4]; char *p = info; - p += snprintf (p, 512, "{cnt:%d,uuid:<1,%" PRIx64 ">,name:\"%s\"%s%s,flags:[%s],type:%d}", + p += snprintf (p, 512, "{cnt:%d,uuid:<1.%" PRIx64 ">,name:\"%s\"%s%s,flags:[%s],type:%d}", count, uuid.seq, &name, title ? "\",title:\"" : "", @@ -2590,14 +2632,80 @@ const char * object::debug_desc () const { - static char info[256 * 4]; - return debug_desc (info); + static char info[3][256 * 4]; + static int info_idx; + + return debug_desc (info [++info_idx % 3]); } -const char * -object::debug_desc2 () const +struct region * +object::region () const +{ + return map ? map->region (x, y) + : region::default_region (); +} + +const materialtype_t * +object::dominant_material () const +{ + if (materialtype_t *mat = name_to_material (materialname)) + return mat; + + // omfg this is slow, this has to be temporary :) + shstr unknown ("unknown"); + + return name_to_material (unknown); +} + +void +object::open_container (object *new_container) { - static char info[256 * 4]; - return debug_desc (info); + if (container == new_container) + return; + + if (object *old_container = container) + { + if (INVOKE_OBJECT (CLOSE, old_container, ARG_OBJECT (this))) + return; + +#if 0 + // remove the "Close old_container" object. + if (object *closer = old_container->inv) + if (closer->type == CLOSE_CON) + closer->destroy (); +#endif + + old_container->flag [FLAG_APPLIED] = 0; + container = 0; + + esrv_update_item (UPD_FLAGS, this, old_container); + new_draw_info_format (NDI_UNIQUE, 0, this, "You close %s.", query_name (old_container)); + } + + if (new_container) + { + if (INVOKE_OBJECT (OPEN, new_container, ARG_OBJECT (this))) + return; + + // TODO: this does not seem to serve any purpose anymore? +#if 0 + // insert the "Close Container" object. + if (archetype *closer = new_container->other_arch) + { + object *closer = arch_to_object (new_container->other_arch); + closer->flag [FLAG_NO_MAP_SAVE] = 1; + new_container->insert (closer); + } +#endif + + new_draw_info_format (NDI_UNIQUE, 0, this, "You open %s.", query_name (new_container)); + + new_container->flag [FLAG_APPLIED] = 1; + container = new_container; + + esrv_update_item (UPD_FLAGS, this, new_container); + esrv_send_inventory (this, new_container); + } } +