--- deliantra/server/common/object.C 2007/01/07 02:39:13 1.108 +++ deliantra/server/common/object.C 2007/02/10 15:20:23 1.131 @@ -1,26 +1,26 @@ /* - 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 -*/ + * 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 @@ -397,9 +397,11 @@ object * find_object (tag_t i) { - return ((unsigned int)i) < objects.size () - ? objects [i] - : 0; + for_all_objects (op) + if (op->count == i) + return op; + + return 0; } /* @@ -489,7 +491,7 @@ SET_FLAG (dst, FLAG_REMOVED); if (speed < 0) - dst->speed_left = speed_left - RANDOM () % 200 / 100.0; + dst->speed_left = speed_left - rndm (); /* Copy over key_values, if any. */ if (key_values) @@ -660,8 +662,6 @@ update_object (op->more, action); } -object *object::first; - object::object () { SET_FLAG (this, FLAG_REMOVED); @@ -672,19 +672,30 @@ object::~object () { + unlink (); + free_key_values (this); } +static int object_count; + void object::link () { + assert (!index);//D uuid = gen_uuid (); + count = ++object_count; + refcnt_inc (); objects.insert (this); } void object::unlink () { + if (!index) + return; + objects.erase (this); + refcnt_dec (); } void @@ -766,6 +777,7 @@ if (!drop_to_ground || !map || map->in_memory != MAP_IN_MEMORY + || map->nodrop || ms ().move_block == MOVE_ALL) { while (inv) @@ -784,7 +796,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); @@ -802,27 +815,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 @@ -893,10 +911,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; @@ -949,17 +966,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->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) @@ -971,17 +995,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; @@ -992,7 +1008,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. @@ -1083,8 +1099,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) @@ -1122,7 +1138,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)) { @@ -1130,6 +1145,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); @@ -1153,36 +1173,9 @@ return op; } - if (!QUERY_FLAG (op, FLAG_REMOVED)) + if (object *more = op->more) { - char *dump = dump_object (op); - LOG (llevError, "Trying to insert (map) inserted object.\n%s\n", dump); - free (dump); - return op; - } - - if (op->more) - { - /* The part may be on a different map. */ - - object *more = op->more; - - /* We really need the caller to normalise 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 normalise 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"); @@ -1197,14 +1190,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; @@ -1231,15 +1226,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; @@ -1255,7 +1252,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; @@ -1268,7 +1265,6 @@ } last = top; - top = top->above; } /* Don't want top to be NULL, so set it to the last valid object */ @@ -1285,12 +1281,14 @@ * 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 && !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 @@ -1302,7 +1300,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; @@ -1313,13 +1311,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 */ @@ -1333,7 +1331,7 @@ } if (!op->above) - op->ms ().top = op; + ms.top = op; } /* else not INS_BELOW_ORIGINATOR */ if (op->type == PLAYER) @@ -1349,7 +1347,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 (); @@ -1768,7 +1766,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)) || @@ -1985,7 +1983,7 @@ if (!index) return -1; - return altern[RANDOM () % index]; + return altern [rndm (index)]; } /* @@ -2016,7 +2014,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! @@ -2160,28 +2158,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) { @@ -2607,10 +2586,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); @@ -2630,3 +2609,83 @@ 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 (contr && contr->ns) + contr->ns->floorbox_reset (); + + 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_container->flag [FLAG_APPLIED] = 1; + container = new_container; + + esrv_update_item (UPD_FLAGS, this, new_container); + esrv_send_inventory (this, new_container); + new_draw_info_format (NDI_UNIQUE, 0, this, "You open %s.", query_name (new_container)); + } +} + +