--- deliantra/server/common/object.C 2006/12/25 14:43:22 1.83 +++ deliantra/server/common/object.C 2006/12/31 17:17:22 1.98 @@ -143,7 +143,7 @@ */ /* For each field in wants, */ - for (wants_field = wants->key_values; wants_field != NULL; wants_field = wants_field->next) + for (wants_field = wants->key_values; wants_field; wants_field = wants_field->next) { key_value *has_field; @@ -192,7 +192,6 @@ * Improvements made with merge: Better checking on potion, and also * check weight */ - bool object::can_merge_slow (object *ob1, object *ob2) { /* A couple quicksanity checks */ @@ -279,7 +278,7 @@ * be animated or have a very low speed. Is this an attempted monster * check? */ - if (!QUERY_FLAG (ob1, FLAG_ANIMATE) && FABS ((ob1)->speed) > MIN_ACTIVE_SPEED) + if (!QUERY_FLAG (ob1, FLAG_ANIMATE) && ob1->has_active_speed ()) return 0; switch (ob1->type) @@ -366,7 +365,7 @@ return strdup ("[NULLOBJ]"); object_freezer freezer; - save_object (freezer, op, 3); + save_object (freezer, op, 1); return freezer.as_string (); } @@ -524,7 +523,7 @@ } } - update_ob_speed (dst); + dst->set_speed (dst->speed); } object * @@ -540,12 +539,12 @@ * to the closest player being on the other side, this function can * be called to update the face variable, _and_ how it looks on the map. */ - void update_turn_face (object *op) { - if (!QUERY_FLAG (op, FLAG_IS_TURNABLE) || op->arch == NULL) + if (!QUERY_FLAG (op, FLAG_IS_TURNABLE) || !op->arch) return; + SET_ANIMATION (op, op->direction); update_object (op, UP_OBJ_FACE); } @@ -556,97 +555,20 @@ * This function needs to be called whenever the speed of an object changes. */ void -update_ob_speed (object *op) +object::set_speed (float speed) { - extern int arch_init; - - /* No reason putting the archetypes objects on the speed list, - * since they never really need to be updated. - */ - - if (QUERY_FLAG (op, FLAG_FREED) && op->speed) + if (flag [FLAG_FREED] && speed) { - LOG (llevError, "Object %s is freed but has speed.\n", &op->name); -#ifdef MANY_CORES - abort (); -#else - op->speed = 0; -#endif + LOG (llevError, "Object %s is freed but has speed.\n", &name); + speed = 0; } - if (arch_init) - return; - - if (FABS (op->speed) > MIN_ACTIVE_SPEED) - { - /* If already on active list, don't do anything */ - if (op->active_next || op->active_prev || op == active_objects) - return; - - /* process_events() expects us to insert the object at the beginning - * of the list. */ - op->active_next = active_objects; - - if (op->active_next != NULL) - op->active_next->active_prev = op; + this->speed = speed; - active_objects = op; - } + if (has_active_speed ()) + activate (); else - { - /* If not on the active list, nothing needs to be done */ - if (!op->active_next && !op->active_prev && op != active_objects) - return; - - if (op->active_prev == NULL) - { - active_objects = op->active_next; - - if (op->active_next != NULL) - op->active_next->active_prev = NULL; - } - else - { - op->active_prev->active_next = op->active_next; - - if (op->active_next) - op->active_next->active_prev = op->active_prev; - } - - op->active_next = NULL; - op->active_prev = NULL; - } -} - -/* This function removes object 'op' from the list of active - * objects. - * This should only be used for style maps or other such - * reference maps where you don't want an object that isn't - * in play chewing up cpu time getting processed. - * The reverse of this is to call update_ob_speed, which - * will do the right thing based on the speed of the object. - */ -void -remove_from_active_list (object *op) -{ - /* If not on the active list, nothing needs to be done */ - if (!op->active_next && !op->active_prev && op != active_objects) - return; - - if (op->active_prev == NULL) - { - active_objects = op->active_next; - if (op->active_next != NULL) - op->active_next->active_prev = NULL; - } - else - { - op->active_prev->active_next = op->active_next; - if (op->active_next) - op->active_next->active_prev = op->active_prev; - } - op->active_next = NULL; - op->active_prev = NULL; + deactivate (); } /* @@ -740,7 +662,6 @@ update_object (op->more, action); } -object::vector object::objects; // not yet used object *object::first; object::object () @@ -783,79 +704,80 @@ next = 0; } -object *object::create () +bool +object::active () const { - object *op = new object; - op->link (); - return op; + return active_next || active_prev || this == active_objects; } -/* - * free_object() frees everything allocated by an object, removes - * it from the list of used objects, and puts it on the list of - * free objects. The IS_FREED() flag is set in the object. - * The object must have been removed by remove_ob() first for - * this function to succeed. - * - * If destroy_inventory is set, free inventory as well. Else drop items in - * inventory to the ground. - */ void -object::do_destroy () +object::activate () { - attachable::do_destroy (); - - if (flag [FLAG_IS_LINKED]) - remove_button_link (this); - - if (flag [FLAG_FRIENDLY]) - remove_friendly_object (this); - - if (!flag [FLAG_REMOVED]) - remove (); - - if (flag [FLAG_FREED]) + /* If already on active list, don't do anything */ + if (active ()) return; - flag [FLAG_FREED] = 1; - - // hack to ensure that freed objects still have a valid map - { - static maptile *freed_map; // freed objects are moved here to avoid crashes + if (has_active_speed ()) + { + /* process_events() expects us to insert the object at the beginning + * of the list. */ + active_next = active_objects; - if (!freed_map) - { - freed_map = new maptile; + if (active_next) + active_next->active_prev = this; - freed_map->name = "/internal/freed_objects_map"; - freed_map->width = 3; - freed_map->height = 3; + active_objects = this; + } +} - freed_map->allocate (); - } +void +object::activate_recursive () +{ + activate (); - map = freed_map; - x = 1; - y = 1; - } + for (object *op = inv; op; op = op->above) + op->activate_recursive (); +} - more = 0; - head = 0; - inv = 0; +/* This function removes object 'op' from the list of active + * objects. + * This should only be used for style maps or other such + * reference maps where you don't want an object that isn't + * in play chewing up cpu time getting processed. + * The reverse of this is to call update_ob_speed, which + * will do the right thing based on the speed of the object. + */ +void +object::deactivate () +{ + /* If not on the active list, nothing needs to be done */ + if (!active ()) + return; - // clear those pointers that likely might have circular references to us - owner = 0; - enemy = 0; - attacked_by = 0; + 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; + } - // only relevant for players(?), but make sure of it anyways - contr = 0; + active_next = 0; + active_prev = 0; +} - /* Remove object from the active list */ - speed = 0; - update_ob_speed (this); +void +object::deactivate_recursive () +{ + for (object *op = inv; op; op = op->above) + op->deactivate_recursive (); - unlink (); + deactivate (); } /* @@ -865,6 +787,11 @@ void object::destroy_inv (bool drop_to_ground) { + // need to check first, because the checks below might segfault + // as we might be on an invalid mapspace and crossfire code + // is too buggy to ensure that the inventory is empty. + // corollary: if you create arrows etc. with stuff in tis inventory, + // cf will crash below with off-map x and y if (!inv) return; @@ -872,7 +799,10 @@ * if some form of movement is allowed, let objects * drop on that space. */ - if (!drop_to_ground || !map || map->in_memory != MAP_IN_MEMORY || GET_MAP_MOVE_BLOCK (map, x, y) == MOVE_ALL) + if (!drop_to_ground + || !map + || map->in_memory != MAP_IN_MEMORY + || ms ().move_block == MOVE_ALL) { while (inv) { @@ -893,31 +823,87 @@ || op->flag [FLAG_IS_A_TEMPLATE]) op->destroy (); else - { - op->remove (); - op->x = x; - op->y = y; - insert_ob_in_map (op, map, 0, 0); /* Insert in same map as the envir */ - } + map->insert (op, x, y); } } } +object *object::create () +{ + object *op = new object; + op->link (); + return op; +} + void -object::destroy (bool destroy_inventory) +object::do_destroy () { - if (destroyed ()) + if (flag [FLAG_IS_LINKED]) + remove_button_link (this); + + if (flag [FLAG_FRIENDLY]) + remove_friendly_object (this); + + if (!flag [FLAG_REMOVED]) + remove (); + + if (flag [FLAG_FREED]) return; + set_speed (0); + + 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 + + if (!freed_map) + { + freed_map = new maptile; + + freed_map->name = "/internal/freed_objects_map"; + freed_map->width = 3; + freed_map->height = 3; + + freed_map->alloc (); + } + + map = freed_map; + x = 1; + y = 1; + } + + head = 0; + if (more) { - //TODO: non-head objects must not have inventory - more->destroy (destroy_inventory); + more->destroy (); more = 0; } + // clear those pointers that likely might have circular references to us + owner = 0; + enemy = 0; + attacked_by = 0; + + // only relevant for players(?), but make sure of it anyways + contr = 0; +} + +void +object::destroy (bool destroy_inventory) +{ + if (destroyed ()) + return; + if (destroy_inventory) - destroy_inv (true); + destroy_inv (false); attachable::destroy (); } @@ -980,12 +966,12 @@ if ((otmp = in_player ()) && otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) otmp->update_stats (); - if (above != NULL) + if (above) above->below = below; else env->inv = below; - if (below != NULL) + if (below) below->above = above; /* we set up values so that it could be inserted into @@ -999,9 +985,13 @@ } else if (map) { - /* Re did the following section of code - it looks like it had - * lots of logic for things we no longer care about - */ + if (type == PLAYER) + { + --map->players; + map->last_access = runtime; + } + + map->dirty = true; /* link the object above us */ if (above) @@ -1029,7 +1019,7 @@ free (dump); } - map->at (x, y).bottom = above; /* goes on above it. */ + map->at (x, y).bot = above; /* goes on above it. */ } above = 0; @@ -1040,7 +1030,7 @@ int check_walk_off = !flag [FLAG_NO_APPLY]; - for (tmp = GET_MAP_OB (map, x, y); tmp; tmp = tmp->above) + for (tmp = map->at (x, y).bot; tmp; tmp = tmp->above) { /* No point updating the players look faces if he is the object * being removed. @@ -1062,7 +1052,7 @@ tmp->contr->ns->floorbox_update (); } - /* See if player moving off should effect something */ + /* See if object moving off should effect something */ if (check_walk_off && ((move_type & tmp->move_off) && (move_type & ~tmp->move_off & ~tmp->move_block) == 0)) @@ -1074,14 +1064,15 @@ } /* Eneq(@csd.uu.se): Fixed this to skip tmp->above=tmp */ - + //TODO: why is this horrible hacka fix? get rid of this code=bug! (schmorp) if (tmp->above == tmp) tmp->above = 0; last = tmp; } - /* last == NULL of there are no objects on this space */ + /* last == NULL if there are no objects on this space */ + //TODO: this makes little sense, why only update the topmost object? if (!last) map->at (x, y).flags_ = P_NEED_UPDATE; else @@ -1136,12 +1127,7 @@ object * insert_ob_in_map_at (object *op, maptile *m, object *originator, int flag, int x, int y) { - object *tmp; - - if (op->head) - op = op->head; - - for (tmp = op; tmp; tmp = tmp->more) + for (object *tmp = op->head_ (); tmp; tmp = tmp->more) { tmp->x = x + tmp->arch->clone.x; tmp->y = y + tmp->arch->clone.y; @@ -1182,7 +1168,7 @@ return NULL; } - if (m == NULL) + if (!m) { char *dump = dump_object (op); LOG (llevError, "Trying to insert in null-map!\n%s\n", dump); @@ -1256,7 +1242,7 @@ /* 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 != NULL; tmp = tmp->above) + for (tmp = GET_MAP_OB (op->map, x, y); tmp; tmp = tmp->above) if (object::can_merge (op, tmp)) { op->nrof += tmp->nrof; @@ -1283,7 +1269,7 @@ if (op->below) op->below->above = op; else - op->ms ().bottom = op; + op->ms ().bot = op; /* since *below* originator, no need to update top */ originator->below = op; @@ -1293,7 +1279,7 @@ /* If there are other objects, then */ if ((!(flag & INS_MAP_LOAD)) && ((top = GET_MAP_OB (op->map, op->x, op->y)) != NULL)) { - object *last = NULL; + object *last = 0; /* * If there are multiple objects on this space, we do some trickier handling. @@ -1307,8 +1293,7 @@ * when lots of spells are cast in one area. Currently, it is presumed * that flying non pickable objects are spell objects. */ - - while (top != NULL) + while (top) { if (QUERY_FLAG (top, FLAG_IS_FLOOR) || QUERY_FLAG (top, FLAG_OVERLAY_FLOOR)) floor = top; @@ -1371,8 +1356,8 @@ if (op->above) op->above->below = op; - op->below = NULL; - op->ms ().bottom = op; + op->below = 0; + op->ms ().bot = op; } else { /* get inserted into the stack above top */ @@ -1385,12 +1370,18 @@ top->above = op; } - if (op->above == NULL) + if (!op->above) op->ms ().top = op; } /* else not INS_BELOW_ORIGINATOR */ if (op->type == PLAYER) - op->contr->do_los = 1; + { + op->contr->do_los = 1; + ++op->map->players; + op->map->last_access = runtime; + } + + op->map->dirty = true; /* If we have a floor, we know the player, if any, will be above * it, so save a few ticks and start from there. @@ -1409,7 +1400,7 @@ * or just updating the P_NEED_UPDATE for spaces within this area * of effect may be sufficient. */ - if (MAP_DARKNESS (op->map) && (op->glow_radius != 0)) + if (op->map->darkness && (op->glow_radius != 0)) update_all_los (op->map, op->x, op->y); /* updates flags (blocked, alive, no magic, etc) for this map space */ @@ -1465,6 +1456,12 @@ insert_ob_in_map (tmp1, op->map, op, 0); } +object * +object::insert_at (object *where, object *originator, int flags) +{ + where->map->insert (this, where->x, where->y, originator, flags); +} + /* * get_split_ob(ob,nr) splits up ob into two parts. The part which * is returned contains nr objects, and the remaining parts contains @@ -1472,7 +1469,6 @@ * On failure, NULL is returned, and the reason put into the * global static errmsg array. */ - object * get_split_ob (object *orig_ob, uint32 nr) { @@ -1705,7 +1701,7 @@ #ifdef DEBUG_LIGHTS LOG (llevDebug, " insert_ob_in_ob(): got %s to insert in map/op\n", op->name); #endif /* DEBUG_LIGHTS */ - if (MAP_DARKNESS (map)) + if (map->darkness) update_all_los (map, x, y); } @@ -1746,7 +1742,6 @@ * object being inserted. insert_ob_in_map may not put new objects * on top. */ - int check_move_on (object *op, object *originator) { @@ -1783,7 +1778,7 @@ * Hence, we first go to the top: */ - for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp != NULL && tmp->above != NULL; tmp = tmp->above) + for (tmp = GET_MAP_OB (op->map, op->x, op->y); 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, @@ -1848,21 +1843,19 @@ * a matching archetype at the given map and coordinates. * The first matching object is returned, or NULL if none. */ - object * present_arch (const archetype *at, maptile *m, int x, int y) { - object * - tmp; - if (m == NULL || out_of_map (m, x, y)) { LOG (llevError, "Present_arch called outside map.\n"); return NULL; } - for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above) + + for (object *tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above) if (tmp->arch == at) return tmp; + return NULL; } @@ -1871,21 +1864,19 @@ * a matching type variable at the given map and coordinates. * The first matching object is returned, or NULL if none. */ - object * present (unsigned char type, maptile *m, int x, int y) { - object * - tmp; - if (out_of_map (m, x, y)) { LOG (llevError, "Present called outside map.\n"); return NULL; } - for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above) + + for (object *tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above) if (tmp->type == type) return tmp; + return NULL; } @@ -1894,16 +1885,13 @@ * a matching type variable in the inventory of the given object. * The first matching object is returned, or NULL if none. */ - object * present_in_ob (unsigned char type, const object *op) { - object * - tmp; - - for (tmp = op->inv; tmp != NULL; tmp = tmp->below) + for (object *tmp = op->inv; tmp != NULL; tmp = tmp->below) if (tmp->type == type) return tmp; + return NULL; } @@ -1924,9 +1912,7 @@ object * present_in_ob_by_name (int type, const char *str, const object *op) { - object *tmp; - - for (tmp = op->inv; tmp; tmp = tmp->below) + for (object *tmp = op->inv; tmp; tmp = tmp->below) if ((type == -1 || tmp->type == type) && (!strcmp (str, tmp->name))) return tmp; @@ -2145,7 +2131,7 @@ max = maxfree[i]; else if (mflags & P_IS_ALIVE) { - for (tmp = ms.bottom; tmp; tmp = tmp->above) + for (tmp = ms.bot; tmp; tmp = tmp->above) if ((tmp->flag [FLAG_MONSTER] || tmp->type == PLAYER) && (tmp != exclude || (tmp->head && tmp->head != exclude))) break; @@ -2622,24 +2608,53 @@ item = item->env; } + +const char * +object::flag_desc (char *desc, int len) const +{ + char *p = desc; + bool first = true; + + for (int i = 0; i < NUM_FLAGS; i++) + { + if (len <= 10) // magic constant! + { + snprintf (p, len, ",..."); + break; + } + + if (flag[i]) + { + int cnt = snprintf (p, len, "%s%d", first ? "" : ",", i); + len -= cnt; + p += cnt; + first = false; + } + } + + return desc; +} + // return a suitable string describing an objetc in enough detail to find it const char * object::debug_desc (char *info) const { - char info2[256 * 3]; + char flagdesc[512]; + char info2[256 * 4]; char *p = info; - p += snprintf (p, 256, "%d=\"%s%s%s\"", - count, + p += snprintf (p, 512, "{cnt:%d,uuid:<1,%" PRIx64 ">,name:\"%s%s%s\",flags:[%s],type:%d}", + count, uuid.seq, &name, - title ? " " : "", - title ? (const char *)title : ""); + title ? "\",title:" : "", + title ? (const char *)title : "", + flag_desc (flagdesc, 512), type); if (env) p += snprintf (p, 256, "(in %s)", env->debug_desc (info2)); if (map) - p += snprintf (p, 256, "(on %s@%d+%d)", map->path, x, y); + p += snprintf (p, 256, "(on %s@%d+%d)", &map->path, x, y); return info; }