--- deliantra/server/common/object.C 2008/04/20 23:25:09 1.207 +++ deliantra/server/common/object.C 2008/04/21 06:16:01 1.208 @@ -1,7 +1,7 @@ /* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * - * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Deliantra team + * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * Copyright (©) 2001,2007 Mark Wedel & Crossfire Development Team * Copyright (©) 1992,2007 Frank Tore Johansen * @@ -21,10 +21,6 @@ * The authors can be reached via e-mail to */ -/* Eneq(@csd.uu.se): Added weight-modifiers in environment of objects. - sub/add_weight will transcend the environment updating the carrying - variable. */ - #include #include #include @@ -208,7 +204,7 @@ return 0; /* Do not merge objects if nrof would overflow. First part checks - * for unsigned overflow (2c), second part checks wether the result + * for unsigned overflow (2c), second part checks whether the result * would fit into a 32 bit signed int, which is often used to hold * nrof values. */ @@ -227,14 +223,11 @@ if (QUERY_FLAG (ob2, FLAG_IDENTIFIED)) SET_FLAG (ob2, FLAG_BEEN_APPLIED); - if ((ob1->flag ^ ob2->flag).reset (FLAG_INV_LOCKED).reset (FLAG_CLIENT_SENT).any () - || ob1->arch != ob2->arch + if (ob1->arch->name != ob2->arch->name || ob1->name != ob2->name || ob1->title != ob2->title || ob1->msg != ob2->msg || ob1->weight != ob2->weight - || memcmp (&ob1->resist, &ob2->resist, sizeof (ob1->resist)) - || memcmp (&ob1->stats , &ob2->stats , sizeof (ob1->stats) ) || ob1->attacktype != ob2->attacktype || ob1->magic != ob2->magic || ob1->slaying != ob2->slaying @@ -251,7 +244,16 @@ || ob1->move_on != ob2->move_on || ob1->move_off != ob2->move_off || ob1->move_slow != ob2->move_slow - || ob1->move_slow_penalty != ob2->move_slow_penalty) + || ob1->move_slow_penalty != ob2->move_slow_penalty + || memcmp (&ob1->resist, &ob2->resist, sizeof (ob1->resist)) + || memcmp (&ob1->stats , &ob2->stats , sizeof (ob1->stats))) + return 0; + + if ((ob1->flag ^ ob2->flag) + .reset (FLAG_INV_LOCKED) + .reset (FLAG_CLIENT_SENT) + .reset (FLAG_REMOVED) + .any ()) return 0; /* This is really a spellbook check - we should in general @@ -300,9 +302,9 @@ { /* At least one of these has key_values. */ if ((!ob1->key_values) != (!ob2->key_values)) - /* One has fields, but the other one doesn't. */ - return 0; - else if (!compare_ob_value_lists (ob1, ob2)) + return 0; /* One has fields, but the other one doesn't. */ + + if (!compare_ob_value_lists (ob1, ob2)) return 0; } @@ -318,9 +320,11 @@ if (k1 != k2) return 0; - else if (k1 == 0) + + if (k1 == 0) return 1; - else if (!cfperl_can_merge (ob1, ob2)) + + if (!cfperl_can_merge (ob1, ob2)) return 0; } } @@ -329,6 +333,7 @@ return 1; } +// adjust weight per container type ("of holding") static sint32 weight_adjust (object *op, sint32 weight) { @@ -338,11 +343,11 @@ } /* - * add_weight(object, weight) adds the specified weight to an object, + * adjust_weight(object, weight) adds the specified weight to an object, * and also updates how much the environment(s) is/are carrying. */ static void -add_weight (object *op, sint32 weight) +adjust_weight (object *op, sint32 weight) { while (op) { @@ -354,16 +359,6 @@ } /* - * sub_weight() recursively (outwards) subtracts a number from the - * weight of an object (and what is carried by it's environment(s)). - */ -static void -sub_weight (object *op, long weight) -{ - add_weight (op, -weight); -} - -/* * this is a recursive function which calculates the weight * an object is carrying. It goes through op and figures out how much * containers are carrying, and sums it up. @@ -385,9 +380,7 @@ } /* - * Used by: Crossedit: dump. Server DM commands: dumpbelow, dump. - * Some error messages. - * The result of the dump is stored in the static global errmsg array. + * Used by: Server DM commands: dumpbelow, dump. Some error messages. */ char * dump_object (object *op) @@ -411,16 +404,21 @@ object *tmp, *closest; int last_dist, i; - if (op->more == NULL) + if (!op->more) return op; - for (last_dist = distance (op, pl), closest = op, tmp = op->more; tmp != NULL; tmp = tmp->more) + + for (last_dist = distance (op, pl), closest = op, tmp = op->more; + tmp; + tmp = tmp->more) if ((i = distance (tmp, pl)) < last_dist) closest = tmp, last_dist = i; + return closest; } /* * Returns the object which has the count-variable equal to the argument. + * VERRRY slow. */ object * find_object (tag_t i) @@ -1022,22 +1020,9 @@ */ if (env) { - if (nrof) - sub_weight (env, weight * nrof); - else - sub_weight (env, weight + carrying); - - /* NO_FIX_PLAYER is set when a great many changes are being - * made to players inventory. If set, avoiding the call - * to save cpu time. - */ - if ((otmp = in_player ()) && otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) - otmp->update_stats (); + adjust_weight (env, -total_weight ()); - if (above) - above->below = below; - else - env->inv = below; + *(above ? &above->below : &env->inv) = below; if (below) below->above = above; @@ -1046,10 +1031,19 @@ * the map, but we don't actually do that - it is up * to the caller to decide what we want to do. */ - x = env->x, y = env->y; - map = env->map; - above = 0, below = 0; - env = 0; + map = env->map; + x = env->x; + y = env->y; + above = 0; + below = 0; + env = 0; + + /* NO_FIX_PLAYER is set when a great many changes are being + * made to players inventory. If set, avoiding the call + * to save cpu time. + */ + if ((otmp = in_player ()) && otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) + otmp->update_stats (); } else if (map) { @@ -1272,7 +1266,7 @@ if (object::can_merge (op, tmp)) { op->nrof += tmp->nrof; - tmp->destroy (); + tmp->destroy (1); } CLEAR_FLAG (op, FLAG_APPLIED); /* hack for fixing F_APPLIED in items of dead people */ @@ -1462,19 +1456,18 @@ void replace_insert_ob_in_map (const char *arch_string, object *op) { - object *tmp, *tmp1; - /* first search for itself and remove any old instances */ - for (tmp = op->ms ().bot; tmp; tmp = tmp->above) + for (object *tmp = op->ms ().bot; tmp; tmp = tmp->above) if (!strcmp (tmp->arch->archname, arch_string)) /* same archetype */ - tmp->destroy (); + tmp->destroy (1); + + object *tmp = arch_to_object (archetype::find (arch_string)); - tmp1 = arch_to_object (archetype::find (arch_string)); + tmp->x = op->x; + tmp->y = op->y; - tmp1->x = op->x; - tmp1->y = op->y; - insert_ob_in_map (tmp1, op->map, op, 0); + insert_ob_in_map (tmp, op->map, op, 0); } object * @@ -1494,125 +1487,117 @@ * global static errmsg array. */ object * -get_split_ob (object *orig_ob, uint32 nr) +object::split_nr (sint32 nr) { - object *newob; - int is_removed = (QUERY_FLAG (orig_ob, FLAG_REMOVED) != 0); + 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 NULL; + return 0; } - newob = object_create_clone (orig_ob); + return orig_ob->split_nr (nr); +} - if ((orig_ob->nrof -= nr) < 1) - orig_ob->destroy (1); - else if (!is_removed) +// find player who can see this object +static object * +visible_to (object *op) +{ + if (!op->flag [FLAG_REMOVED]) { - if (orig_ob->env != NULL) - sub_weight (orig_ob->env, orig_ob->weight * nr); - if (orig_ob->env == NULL && orig_ob->map->in_memory != MAP_ACTIVE) + // 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 { - strcpy (errmsg, "Tried to split object whose map is not in memory."); - LOG (llevDebug, "Error, Tried to split object whose map is not in memory.\n"); - return NULL; + // maybe there is a player standing on the same mapspace + if (object *pl = env->ms ().player ()) + return pl; } } - newob->nrof = nr; - - return newob; + return 0; } /* * decrease_ob_nr(object, number) decreases a specified number from - * the amount of an object. If the amount reaches 0, the object + * 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 */ -object * -decrease_ob_nr (object *op, uint32 i) +bool +object::decrease_nr (sint32 nr) { - object *tmp; + nr = min (nr, nrof); - if (i == 0) /* objects with op->nrof require this check */ - return op; + if (!nr) + return 1; - if (i > op->nrof) - i = op->nrof; + nrof -= nr; - if (QUERY_FLAG (op, FLAG_REMOVED)) - op->nrof -= i; - else if (op->env) - { - /* is this object in the players inventory, or sub container - * therein? - */ - tmp = op->in_player (); - /* nope. Is this a container the player has opened? - * If so, set tmp to that player. - * IMO, searching through all the players will mostly - * likely be quicker than following op->env to the map, - * and then searching the map for a player. - */ - if (!tmp) - for_all_players (pl) - if (pl->ob->container == op->env) - { - tmp = pl->ob; - break; - } + object *visible = visible_to (this); - if (i < op->nrof) - { - sub_weight (op->env, op->weight * i); - op->nrof -= i; - if (tmp) - esrv_send_item (tmp, op); - } - else - { - op->remove (); - op->nrof = 0; - if (tmp) - esrv_del_item (tmp->contr, op->count); - } - } - else + if (nrof) { - object *above = op->above; + adjust_weight (env, -weight * nr); // carrying == 0 - if (i < op->nrof) - op->nrof -= i; - else - { - op->remove (); - op->nrof = 0; - } + if (visible) + esrv_send_item (visible, this); - /* Since we just removed op, op->above is null */ - for (tmp = above; tmp; tmp = tmp->above) - if (tmp->type == PLAYER) - { - if (op->nrof) - esrv_send_item (tmp, op); - else - esrv_del_item (tmp->contr, op->count); - } + return 1; } - - if (op->nrof) - return op; else { - op->destroy (); + if (visible) + esrv_del_item (visible->contr, count); + + this->destroy (1); return 0; } } +//TODO: remove +object * +decrease_ob_nr (object *op, uint32 i) +{ + return op->decrease_nr (i) ? op : 0; +} + object * insert_ob_in_ob (object *op, object *where) { @@ -1644,81 +1629,54 @@ object * object::insert (object *op) { - if (!QUERY_FLAG (op, FLAG_REMOVED)) - op->remove (); - if (op->more) { LOG (llevError, "Tried to insert multipart object %s (%d)\n", &op->name, op->count); return op; } - CLEAR_FLAG (op, FLAG_OBJ_ORIGINAL); - CLEAR_FLAG (op, FLAG_REMOVED); - - if (op->nrof) - { - for (object *tmp = inv; tmp; tmp = tmp->below) - if (object::can_merge (tmp, op)) - { - /* return the original object and remove inserted object - (client needs the original object) */ - tmp->nrof += op->nrof; - /* Weight handling gets pretty funky. Since we are adding to - * tmp->nrof, we need to increase the weight. - */ - add_weight (this, op->weight * op->nrof); - SET_FLAG (op, FLAG_REMOVED); - op->destroy (); /* free the inserted object */ - op = tmp; - op->remove (); /* and fix old object's links */ - CLEAR_FLAG (op, FLAG_REMOVED); - break; - } + op->remove (); - /* I assume combined objects have no inventory - * We add the weight - this object could have just been removed - * (if it was possible to merge). calling remove_ob will subtract - * the weight, so we need to add it in again, since we actually do - * the linking below - */ - add_weight (this, op->weight * op->nrof); - } - else - add_weight (this, (op->weight + op->carrying)); + op->flag [FLAG_OBJ_ORIGINAL] = 0; - if (object *otmp = this->in_player ()) - if (otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) - otmp->update_stats (); + if (op->nrof) + for (object *tmp = inv; tmp; tmp = tmp->below) + if (object::can_merge (tmp, op)) + { + /* return the original object and remove inserted object + (client needs the original object) */ + tmp->nrof += op->nrof; + op->destroy (1); + op = tmp; + goto inserted; + } - op->owner = 0; // its his/hers now. period. + op->owner = 0; // it's his/hers now. period. op->map = 0; - op->env = this; + op->x = 0; + op->y = 0; + op->above = 0; - op->below = 0; - op->x = op->y = 0; + op->below = inv; + op->env = this; + + if (inv) + inv->above = op; + + inv = op; + op->flag [FLAG_REMOVED] = 0; + + adjust_weight (this, op->total_weight ()); + +inserted: /* reset the light list and los of the players on the map */ - if (op->glow_radius && map) - { -#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) - update_all_los (map, x, y); - } + if (op->glow_radius && map && map->darkness) + update_all_los (map, x, y); - /* Client has no idea of ordering so lets not bother ordering it here. - * It sure simplifies this function... - */ - if (!inv) - inv = op; - else - { - op->below = inv; - op->below->above = op; - inv = op; - } + if (object *otmp = in_player ()) + if (otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) + otmp->update_stats (); INVOKE_OBJECT (INSERT, this); @@ -2726,7 +2684,7 @@ } void -object::play_sound (faceidx sound) const +object::play_sound (faceidx sound) { if (!sound) return;