--- deliantra/server/common/object.C 2006/12/31 18:10:40 1.99 +++ deliantra/server/common/object.C 2007/04/16 06:23:40 1.137 @@ -1,25 +1,26 @@ /* - CrossFire, A Multiplayer game for X-windows - - Copyright (C) 2001 Mark Wedel & Crossfire Development Team - Copyright (C) 1992 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. - - 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 -*/ + * CrossFire, A Multiplayer game for X-windows + * + * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team + * Copyright (C) 2001 Mark Wedel & Crossfire Development Team + * Copyright (C) 1992 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. + * + * 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 + */ /* Eneq(@csd.uu.se): Added weight-modifiers in environment of objects. sub/add_weight will transcend the environment updating the carrying @@ -38,7 +39,8 @@ static UUID uuid; const uint64 UUID_SKIP = 1<<19; -object *active_objects; /* List of active objects that need to be processed */ +objectvec objects; +activevec actives; short freearr_x[SIZEOFFREE] = { 0, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, -2, -2, -2, -1, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3, -3, -3, -3, -3, -2, -1 @@ -357,7 +359,6 @@ * Some error messages. * The result of the dump is stored in the static global errmsg array. */ - char * dump_object (object *op) { @@ -365,7 +366,7 @@ return strdup ("[NULLOBJ]"); object_freezer freezer; - save_object (freezer, op, 1); + op->write (freezer); return freezer.as_string (); } @@ -392,13 +393,12 @@ /* * Returns the object which has the count-variable equal to the argument. */ - object * find_object (tag_t i) { - for (object *op = object::first; op; op = op->next) - if (op->count == i) - return op; + for_all_objects (op) + if (op->count == i) + return op; return 0; } @@ -408,14 +408,13 @@ * Used only by the patch command, but not all that useful. * Enables features like "patch food 999" */ - object * find_object_name (const char *str) { shstr_cmp str_ (str); object *op; - for (op = object::first; op != NULL; op = op->next) + for_all_objects (op) if (op->name == str_) break; @@ -457,7 +456,7 @@ 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 +467,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; - - if (is_freed) - SET_FLAG (dst, FLAG_FREED); + bool is_freed = flag [FLAG_FREED]; + bool is_removed = flag [FLAG_REMOVED]; - if (is_removed) - SET_FLAG (dst, FLAG_REMOVED); + *(object_copy *)this = src; - if (speed < 0) - dst->speed_left = speed_left - RANDOM () % 200 / 100.0; + 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; - - dst->key_values = 0; + 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 +493,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 +505,45 @@ } } } +} + +/* + * 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. + */ + memcpy (body_used, body_info, sizeof (body_used)); + + attachable::instantiate (); +} + object * object::clone () { @@ -591,8 +609,6 @@ void update_object (object *op, int action) { - MoveType move_on, move_off, move_block, move_slow; - if (op == NULL) { /* this should never happen */ @@ -662,8 +678,6 @@ update_object (op->more, action); } -object *object::first; - object::object () { SET_FLAG (this, FLAG_REMOVED); @@ -674,60 +688,41 @@ object::~object () { + unlink (); + free_key_values (this); } +static int object_count; + void object::link () { - count = ++ob_count; + assert (!index);//D uuid = gen_uuid (); + count = ++object_count; - prev = 0; - next = object::first; - - if (object::first) - object::first->prev = this; - - object::first = this; + refcnt_inc (); + objects.insert (this); } void object::unlink () { - if (this == object::first) - object::first = next; - - /* Remove this object from the list of used objects */ - if (prev) prev->next = next; - if (next) next->prev = prev; + if (!index) + return; - prev = 0; - next = 0; -} - -bool -object::active () const -{ - return active_next || active_prev || this == active_objects; + objects.erase (this); + refcnt_dec (); } void object::activate () { /* If already on active list, don't do anything */ - if (active ()) + if (active) return; if (has_active_speed ()) - { - /* process_events() expects us to insert the object at the beginning - * of the list. */ - active_next = active_objects; - - if (active_next) - active_next->active_prev = this; - - active_objects = this; - } + actives.insert (this); } void @@ -735,7 +730,7 @@ { activate (); - for (object *op = inv; op; op = op->above) + for (object *op = inv; op; op = op->below) op->activate_recursive (); } @@ -751,35 +746,31 @@ object::deactivate () { /* If not on the active list, nothing needs to be done */ - if (!active ()) + if (!active) return; - if (active_prev == 0) - { - active_objects = active_next; - if (active_next) - active_next->active_prev = 0; - } - else - { - active_prev->active_next = active_next; - if (active_next) - active_next->active_prev = active_prev; - } - - active_next = 0; - active_prev = 0; + actives.erase (this); } void object::deactivate_recursive () { - for (object *op = inv; op; op = op->above) + for (object *op = inv; op; op = op->below) op->deactivate_recursive (); deactivate (); } +void +object::set_flag_inv (int flag, int value) +{ + for (object *op = inv; op; op = op->below) + { + op->flag [flag] = value; + op->set_flag_inv (flag, value); + } +} + /* * Remove and free all objects in the inventory of the given object. * object.c ? @@ -802,6 +793,7 @@ if (!drop_to_ground || !map || map->in_memory != MAP_IN_MEMORY + || map->nodrop || ms ().move_block == MOVE_ALL) { while (inv) @@ -820,7 +812,8 @@ || op->flag [FLAG_NO_DROP] || op->type == RUNE || op->type == TRAP - || op->flag [FLAG_IS_A_TEMPLATE]) + || op->flag [FLAG_IS_A_TEMPLATE] + || op->flag [FLAG_DESTROY_ON_DEATH]) op->destroy (); else map->insert (op, x, y); @@ -838,27 +831,32 @@ void object::do_destroy () { + attachable::do_destroy (); + if (flag [FLAG_IS_LINKED]) remove_button_link (this); if (flag [FLAG_FRIENDLY]) - remove_friendly_object (this); + { + remove_friendly_object (this); + + if (type == GOLEM + && owner + && owner->type == PLAYER + && owner->contr->ranges[range_golem] == this) + owner->contr->ranges[range_golem] = 0; + } if (!flag [FLAG_REMOVED]) remove (); - if (flag [FLAG_FREED]) - return; + destroy_inv (true); - set_speed (0); + deactivate (); + unlink (); flag [FLAG_FREED] = 1; - attachable::do_destroy (); - - destroy_inv (true); - unlink (); - // hack to ensure that freed objects still have a valid map { static maptile *freed_map; // freed objects are moved here to avoid crashes @@ -872,6 +870,7 @@ freed_map->height = 3; freed_map->alloc (); + freed_map->in_memory = MAP_IN_MEMORY; } map = freed_map; @@ -891,9 +890,6 @@ owner = 0; enemy = 0; attacked_by = 0; - - // only relevant for players(?), but make sure of it anyways - contr = 0; } void @@ -931,10 +927,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 () +object::do_remove () { object *tmp, *last = 0; object *otmp; @@ -987,17 +982,24 @@ { 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->last_access = runtime; + map->touch (); } map->dirty = true; + mapspace &ms = this->ms (); /* link the object above us */ if (above) above->below = below; else - map->at (x, y).top = below; /* we were top, set new top */ + ms.top = below; /* we were top, set new top */ /* Relink the object below us, if there is one */ if (below) @@ -1009,17 +1011,9 @@ * evident */ if (GET_MAP_OB (map, x, y) != this) - { - char *dump = dump_object (this); - LOG (llevError, - "remove_ob: GET_MAP_OB does not return object to be removed even though it appears to be on the bottom?\n%s\n", dump); - free (dump); - dump = dump_object (GET_MAP_OB (map, x, y)); - LOG (llevError, "%s\n", dump); - free (dump); - } + LOG (llevError, "remove_ob: GET_MAP_OB does not return object to be removed even though it appears to be on the bottom? %s\n", debug_desc ()); - map->at (x, y).bot = above; /* goes on above it. */ + ms.bot = above; /* goes on above it. */ } above = 0; @@ -1030,7 +1024,7 @@ int check_walk_off = !flag [FLAG_NO_APPLY]; - for (tmp = map->at (x, y).bot; tmp; tmp = tmp->above) + for (tmp = ms.bot; tmp; tmp = tmp->above) { /* No point updating the players look faces if he is the object * being removed. @@ -1121,8 +1115,8 @@ } /* - * same as insert_ob_in_map except it handle separate coordinates and do a clean - * job preparing multi-part monsters + * same as insert_ob_in_map except it handles separate coordinates and does a clean + * job preparing multi-part monsters. */ object * insert_ob_in_map_at (object *op, maptile *m, object *originator, int flag, int x, int y) @@ -1160,7 +1154,6 @@ insert_ob_in_map (object *op, maptile *m, object *originator, int flag) { object *tmp, *top, *floor = NULL; - sint16 x, y; if (QUERY_FLAG (op, FLAG_FREED)) { @@ -1168,6 +1161,11 @@ return NULL; } + if (!QUERY_FLAG (op, FLAG_REMOVED)) + LOG (llevError, "Trying to insert already inserted object %s\n", op->debug_desc ()); + + op->remove (); + if (!m) { char *dump = dump_object (op); @@ -1191,36 +1189,9 @@ return op; } - if (!QUERY_FLAG (op, FLAG_REMOVED)) - { - char *dump = dump_object (op); - LOG (llevError, "Trying to insert (map) inserted object.\n%s\n", dump); - free (dump); - return op; - } - - if (op->more) + if (object *more = op->more) { - /* The part may be on a different map. */ - - object *more = op->more; - - /* We really need the caller to normalize coordinates - if - * we set the map, that doesn't work if the location is within - * a map and this is straddling an edge. So only if coordinate - * is clear wrong do we normalize it. - */ - if (OUT_OF_REAL_MAP (more->map, more->x, more->y)) - more->map = get_map_from_coord (m, &more->x, &more->y); - else if (!more->map) - { - /* For backwards compatibility - when not dealing with tiled maps, - * more->map should always point to the parent. - */ - more->map = m; - } - - if (insert_ob_in_map (more, more->map, originator, flag) == NULL) + 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"); @@ -1235,14 +1206,16 @@ * of areas of callers (eg, anything that uses find_free_spot would now * need extra work */ - op->map = get_map_from_coord (m, &op->x, &op->y); - x = op->x; - y = op->y; + if (!xy_normalise (m, op->x, op->y)) + return 0; + + op->map = m; + mapspace &ms = op->ms (); /* this has to be done after we translate the coordinates. */ if (op->nrof && !(flag & INS_NO_MERGE)) - for (tmp = GET_MAP_OB (op->map, x, y); tmp; tmp = tmp->above) + for (tmp = ms.bot; tmp; tmp = tmp->above) if (object::can_merge (op, tmp)) { op->nrof += tmp->nrof; @@ -1269,15 +1242,17 @@ if (op->below) op->below->above = op; else - op->ms ().bot = op; + ms.bot = op; /* since *below* originator, no need to update top */ originator->below = op; } else { + top = ms.bot; + /* If there are other objects, then */ - if ((!(flag & INS_MAP_LOAD)) && ((top = GET_MAP_OB (op->map, op->x, op->y)) != NULL)) + if ((!(flag & INS_MAP_LOAD)) && top) { object *last = 0; @@ -1293,7 +1268,7 @@ * when lots of spells are cast in one area. Currently, it is presumed * that flying non pickable objects are spell objects. */ - while (top) + for (top = ms.bot; top; top = top->above) { if (QUERY_FLAG (top, FLAG_IS_FLOOR) || QUERY_FLAG (top, FLAG_OVERLAY_FLOOR)) floor = top; @@ -1306,7 +1281,6 @@ } last = top; - top = top->above; } /* Don't want top to be NULL, so set it to the last valid object */ @@ -1318,17 +1292,19 @@ */ /* 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) && - (get_map_flags (op->map, 0, op->x, op->y, 0, 0) & P_BLOCKSVIEW) && (op->face && !op->face->visibility)) + if (!(flag & INS_ON_TOP) + && ms.flags () & P_BLOCKSVIEW + && (op->face && !faces [op->face].visibility)) { for (last = top; last != floor; last = last->below) if (QUERY_FLAG (last, FLAG_BLOCKSVIEW) && (last->type != EXIT)) break; + /* Check to see if we found the object that blocks view, * and make sure we have a below pointer for it so that * we can get inserted below this one, which requires we @@ -1340,7 +1316,7 @@ } /* If objects on this space */ if (flag & INS_MAP_LOAD) - top = GET_MAP_TOP (op->map, op->x, op->y); + top = ms.top; if (flag & INS_ABOVE_FLOOR_ONLY) top = floor; @@ -1351,13 +1327,13 @@ /* First object on this space */ if (!top) { - op->above = GET_MAP_OB (op->map, op->x, op->y); + op->above = ms.bot; if (op->above) op->above->below = op; op->below = 0; - op->ms ().bot = op; + ms.bot = op; } else { /* get inserted into the stack above top */ @@ -1371,14 +1347,14 @@ } if (!op->above) - op->ms ().top = op; + ms.top = op; } /* else not INS_BELOW_ORIGINATOR */ if (op->type == PLAYER) { op->contr->do_los = 1; ++op->map->players; - op->map->last_access = runtime; + op->map->touch (); } op->map->dirty = true; @@ -1387,7 +1363,7 @@ * it, so save a few ticks and start from there. */ if (!(flag & INS_MAP_LOAD)) - if (object *pl = op->ms ().player ()) + if (object *pl = ms.player ()) if (pl->contr->ns) pl->contr->ns->floorbox_update (); @@ -1445,7 +1421,7 @@ /* first search for itself and remove any old instances */ - for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) + for (tmp = op->ms ().bot; tmp; tmp = tmp->above) if (!strcmp (tmp->arch->name, arch_string)) /* same archetype */ tmp->destroy (); @@ -1456,6 +1432,7 @@ insert_ob_in_map (tmp1, op->map, op, 0); } +// XXX: function not returning object* object * object::insert_at (object *where, object *originator, int flags) { @@ -1778,7 +1755,7 @@ * Hence, we first go to the top: */ - for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp && tmp->above; tmp = tmp->above) + for (tmp = op->ms ().bot; tmp && tmp->above; tmp = tmp->above) { /* Trim the search when we find the first other spell effect * this helps performance so that if a space has 50 spell objects, @@ -1806,7 +1783,7 @@ { float - diff = tmp->move_slow_penalty * FABS (op->speed); + diff = tmp->move_slow_penalty * fabs (op->speed); if (op->type == PLAYER) if ((QUERY_FLAG (tmp, FLAG_IS_HILLY) && find_skill_by_number (op, SK_CLIMBING)) || @@ -1846,13 +1823,13 @@ object * present_arch (const archetype *at, maptile *m, int x, int y) { - if (m == NULL || out_of_map (m, x, y)) + if (!m || out_of_map (m, x, y)) { LOG (llevError, "Present_arch called outside map.\n"); return NULL; } - for (object *tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above) + for (object *tmp = m->at (x, y).bot; tmp; tmp = tmp->above) if (tmp->arch == at) return tmp; @@ -1873,7 +1850,7 @@ return NULL; } - for (object *tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above) + for (object *tmp = m->at (x, y).bot; tmp; tmp = tmp->above) if (tmp->type == type) return tmp; @@ -2023,7 +2000,7 @@ if (!index) return -1; - return altern[RANDOM () % index]; + return altern [rndm (index)]; } /* @@ -2054,7 +2031,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! @@ -2198,28 +2175,9 @@ } /* - * absdir(int): Returns a number between 1 and 8, which represent - * the "absolute" direction of a number (it actually takes care of - * "overflow" in previous calculations of a direction). - */ - -int -absdir (int d) -{ - while (d < 1) - d += 8; - - while (d > 8) - d -= 8; - - return d; -} - -/* * dirdiff(dir1, dir2) returns how many 45-degrees differences there is * between two directions (which are expected to be absolute (see absdir()) */ - int dirdiff (int dir1, int dir2) { @@ -2395,44 +2353,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. @@ -2496,7 +2416,6 @@ return 0; } - /* * Updates the canonical_key in op to value. * @@ -2615,6 +2534,8 @@ char *p = desc; bool first = true; + *p = 0; + for (int i = 0; i < NUM_FLAGS; i++) { if (len <= 10) // magic constant! @@ -2623,7 +2544,7 @@ break; } - if (flag[i]) + if (flag [i]) { int cnt = snprintf (p, len, "%s%d", first ? "" : ",", i); len -= cnt; @@ -2635,7 +2556,7 @@ return desc; } -// return a suitable string describing an objetc in enough detail to find it +// return a suitable string describing an object in enough detail to find it const char * object::debug_desc (char *info) const { @@ -2643,10 +2564,10 @@ 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:" : "", + title ? "\",title:\"" : "", title ? (const char *)title : "", flag_desc (flagdesc, 512), type); @@ -2662,7 +2583,85 @@ const char * object::debug_desc () const { - static char info[256 * 3]; + static char info[256 * 4]; return debug_desc (info); } +const char * +object::debug_desc2 () const +{ + static char info[256 * 4]; + return debug_desc (info); +} + +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) +{ + 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); + } +} + +