--- deliantra/server/common/object.C 2008/04/21 06:16:01 1.208 +++ deliantra/server/common/object.C 2008/04/24 00:30:52 1.220 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -333,6 +332,42 @@ return 1; } +// find player who can see this object +object * +object::visible_to () const +{ + if (client_visible () && !flag [FLAG_REMOVED]) + { + // see if we are in a container of sorts + if (env) + { + // the player inventory itself is always visible + if (env->type == PLAYER) + return env; + + // else a player could have our env open + object *envest = env->outer_env (); + + // the player itself is always on a map, so we will find him here + // even if our inv is in a player. + if (envest->is_on_map ()) + if (object *pl = envest->ms ().player ()) + if (pl->container == env) + return pl; + } + else + { + // maybe there is a player standing on the same mapspace + // this will catch the case where "this" is a player + if (object *pl = ms ().player ()) + if (!pl->container || this == pl->container) + return pl; + } + } + + return 0; +} + // adjust weight per container type ("of holding") static sint32 weight_adjust (object *op, sint32 weight) @@ -353,7 +388,15 @@ { weight = weight_adjust (op, weight); + if (!weight) + return; + op->carrying += weight; + + if (object *pl = op->visible_to ()) + if (pl != op) // player is handled lazily + esrv_update_item (UPD_WEIGHT, pl, op); + op = op->env; } } @@ -376,7 +419,16 @@ sum += op->total_weight (); } - carrying = weight_adjust (this, sum); + sum = weight_adjust (this, sum); + + if (sum != carrying) + { + carrying = sum; + + if (object *pl = visible_to ()) + if (pl != this) // player is handled lazily + esrv_update_item (UPD_WEIGHT, pl, this); + } } /* @@ -868,7 +920,7 @@ // 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, + // corollary: if you create arrows etc. with stuff in its inventory, // cf will crash below with off-map x and y if (!inv) return; @@ -885,7 +937,7 @@ { while (inv) { - inv->destroy_inv (drop_to_ground); + inv->destroy_inv (false); inv->destroy (); } } @@ -901,7 +953,7 @@ || op->type == TRAP || op->flag [FLAG_IS_A_TEMPLATE] || op->flag [FLAG_DESTROY_ON_DEATH]) - op->destroy (); + op->destroy (true); else map->insert (op, x, y); } @@ -918,18 +970,15 @@ void object::do_destroy () { - 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 (); + remove (); - destroy_inv (true); + attachable::do_destroy (); deactivate (); unlink (); @@ -980,8 +1029,13 @@ if (destroyed ()) return; - if (destroy_inventory) - destroy_inv (false); + if (!is_head () && !head->destroyed ()) + { + LOG (llevError | logBacktrace, "tried to destroy the tail of an object"); + head->destroy (destroy_inventory); + } + + destroy_inv (!destroy_inventory); if (is_head ()) if (sound_destroy) @@ -1005,12 +1059,13 @@ object *tmp, *last = 0; object *otmp; - if (QUERY_FLAG (this, FLAG_REMOVED)) + if (flag [FLAG_REMOVED]) return; - SET_FLAG (this, FLAG_REMOVED); INVOKE_OBJECT (REMOVE, this); + flag [FLAG_REMOVED] = true; + if (more) more->remove (); @@ -1020,6 +1075,9 @@ */ if (env) { + if (object *pl = visible_to ()) + esrv_del_item (pl->contr, count); + adjust_weight (env, -total_weight ()); *(above ? &above->below : &env->inv) = below; @@ -1047,20 +1105,30 @@ } else if (map) { - if (type == PLAYER) + map->dirty = true; + mapspace &ms = this->ms (); + + if (object *pl = ms.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 (); + if (type == PLAYER) // this == pl(!) + { + // 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->players; + map->touch (); + } + else if (pl->container == this) + { + // removing a container should close it + close_container (); + } - map->dirty = true; - mapspace &ms = this->ms (); + esrv_del_item (pl->contr, count); + } /* link the object above us */ if (above) @@ -1100,7 +1168,8 @@ */ pl->close_container (); - pl->contr->ns->floorbox_update (); + //TODO: the floorbox prev/next might need updating + esrv_del_item (pl->contr, count); } for (tmp = ms.bot; tmp; tmp = tmp->above) @@ -1154,20 +1223,20 @@ ; for (; top; top = top->below) - { - if (top == op) - continue; + if (object::can_merge (op, top)) + { + top->nrof += op->nrof; - if (object::can_merge (op, top)) - { - top->nrof += op->nrof; + if (object *pl = top->visible_to ()) + esrv_update_item (UPD_NROF, pl, top); -/* CLEAR_FLAG(top,FLAG_STARTEQUIP);*/ - op->weight = 0; /* Don't want any adjustements now */ - op->destroy (); - return top; - } - } + op->weight = 0; // cancel the addition above + op->carrying = 0; // must be 0 already + + op->destroy (1); + + return top; + } return 0; } @@ -1236,8 +1305,6 @@ { assert (!op->flag [FLAG_FREED]); - object *top, *floor = NULL; - op->remove (); /* Ideally, the caller figures this out. However, it complicates a lot @@ -1246,7 +1313,7 @@ */ if (!xy_normalise (m, op->x, op->y)) { - op->destroy (); + op->destroy (1); return 0; } @@ -1265,6 +1332,9 @@ for (object *tmp = ms.bot; tmp; tmp = tmp->above) if (object::can_merge (op, tmp)) { + // TODO: we atcually want to update tmp, not op, + // but some caller surely breaks when we return tmp + // from here :/ op->nrof += tmp->nrof; tmp->destroy (1); } @@ -1296,6 +1366,8 @@ } else { + object *top, *floor = NULL; + top = ms.bot; /* If there are other objects, then */ @@ -1404,7 +1476,8 @@ op->map->dirty = true; if (object *pl = ms.player ()) - pl->contr->ns->floorbox_update (); + //TODO: the floorbox prev/next might need updating + esrv_send_item (pl, op); /* If this object glows, it may affect lighting conditions that are * visible to others on this map. But update_all_los is really @@ -1480,122 +1553,64 @@ } /* - * get_split_ob(ob,nr) splits up ob into two parts. The part which - * is returned contains nr objects, and the remaining parts contains - * the rest (or is removed and freed if that number is 0). - * On failure, NULL is returned, and the reason put into the - * global static errmsg array. - */ -object * -object::split_nr (sint32 nr) -{ - if (nrof < nr) - return 0; - else if (nrof == nr) - { - remove (); - return this; - } - else - { - decrease_nr (nr); - - object *op = object_create_clone (this); - op->nrof = nr; - return op; - } -} - -//TODO: remove, but semantics differs from split_nr -object * -get_split_ob (object *orig_ob, uint32 nr) -{ - if (orig_ob->nrof < nr) - { - sprintf (errmsg, "There are only %d %ss.", orig_ob->nrof ? orig_ob->nrof : 1, &orig_ob->name); - return 0; - } - - return orig_ob->split_nr (nr); -} - -// find player who can see this object -static object * -visible_to (object *op) -{ - if (!op->flag [FLAG_REMOVED]) - { - // see if we are in a container of sorts - if (object *env = op->env) - { - // the player inventory itself is always visible - if (env->type == PLAYER) - return env; - - // else a player could have our env open - env = env->outer_env (); - - // the player itself is always on a map, so we will find him here - // even if our inv is in a player. - if (env->is_on_map ()) - if (object *pl = env->ms ().player ()) - if (pl->container == op->env) - return pl; - } - else - { - // maybe there is a player standing on the same mapspace - if (object *pl = env->ms ().player ()) - return pl; - } - } - - return 0; -} - -/* - * decrease_ob_nr(object, number) decreases a specified number from + * decrease(object, number) decreases a specified number from * the amount of an object. If the amount reaches 0, the object * is subsequently removed and freed. * * Return value: 'op' if something is left, NULL if the amount reached 0 */ bool -object::decrease_nr (sint32 nr) +object::decrease (sint32 nr) { - nr = min (nr, nrof); - if (!nr) - return 1; + return true; - nrof -= nr; + nr = min (nr, nrof); - object *visible = visible_to (this); + nrof -= nr; if (nrof) { adjust_weight (env, -weight * nr); // carrying == 0 - if (visible) - esrv_send_item (visible, this); + if (object *pl = visible_to ()) + esrv_update_item (UPD_NROF, pl, this); - return 1; + return true; } else { - if (visible) - esrv_del_item (visible->contr, count); - - this->destroy (1); - return 0; + destroy (1); + return false; } } -//TODO: remove +/* + * split(ob,nr) splits up ob into two parts. The part which + * is returned contains nr objects, and the remaining parts contains + * the rest (or is removed and returned if that number is 0). + * On failure, NULL is returned. + */ object * -decrease_ob_nr (object *op, uint32 i) +object::split (sint32 nr) { - return op->decrease_nr (i) ? op : 0; + int have = number_of (); + + if (have < nr) + return 0; + else if (have == nr) + { + remove (); + return this; + } + else + { + decrease (nr); + + object *op = object_create_clone (this); + op->nrof = nr; + return op; + } } object * @@ -1646,6 +1661,12 @@ /* return the original object and remove inserted object (client needs the original object) */ tmp->nrof += op->nrof; + + if (object *pl = tmp->visible_to ()) + esrv_update_item (UPD_NROF, pl, tmp); + + adjust_weight (this, op->total_weight ()); + op->destroy (1); op = tmp; goto inserted; @@ -1667,6 +1688,9 @@ op->flag [FLAG_REMOVED] = 0; + if (object *pl = op->visible_to ()) + esrv_send_item (pl, op); + adjust_weight (this, op->total_weight ()); inserted: @@ -1674,9 +1698,9 @@ if (op->glow_radius && map && map->darkness) update_all_los (map, x, y); - if (object *otmp = in_player ()) - if (otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) - otmp->update_stats (); + // if this is a player's inventory, update stats + if (type == PLAYER && !flag [FLAG_NO_FIX_PLAYER]) + update_stats (); INVOKE_OBJECT (INSERT, this); @@ -2564,7 +2588,7 @@ title ? (const char *)title : "", flag_desc (flagdesc, 512), type); - if (!this->flag[FLAG_REMOVED] && env) + if (!flag[FLAG_REMOVED] && env) p += snprintf (p, 256, "(in %s)", env->debug_desc (info2)); if (map) @@ -2604,7 +2628,9 @@ if (container == new_container) return; - if (object *old_container = container) + object *old_container = container; + + if (old_container) { if (INVOKE_OBJECT (CLOSE, old_container, ARG_OBJECT (this))) return; @@ -2616,10 +2642,15 @@ closer->destroy (); #endif - old_container->flag [FLAG_APPLIED] = 0; + // make sure the container is available + esrv_send_item (this, old_container); + + old_container->flag [FLAG_APPLIED] = false; container = 0; + // client needs item update to make it work, client bug requires this to be separate esrv_update_item (UPD_FLAGS, this, old_container); + new_draw_info_format (NDI_UNIQUE, 0, this, "You close %s.", query_name (old_container)); play_sound (sound_find ("chest_close")); } @@ -2642,13 +2673,19 @@ new_draw_info_format (NDI_UNIQUE, 0, this, "You open %s.", query_name (new_container)); - new_container->flag [FLAG_APPLIED] = 1; + // make sure the container is available, client bug requires this to be separate + esrv_send_item (this, new_container); + + new_container->flag [FLAG_APPLIED] = true; container = new_container; + // client needs flag change esrv_update_item (UPD_FLAGS, this, new_container); esrv_send_inventory (this, new_container); play_sound (sound_find ("chest_open")); } +// else if (!old_container->env && contr && contr->ns) +// contr->ns->floorbox_reset (); } object *