--- deliantra/server/common/object.C 2008/04/15 14:21:04 1.206 +++ deliantra/server/common/object.C 2008/04/21 06:35:26 1.209 @@ -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,9 +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 @@ -207,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. */ @@ -226,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 @@ -250,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 @@ -299,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; } @@ -317,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; } } @@ -328,50 +333,54 @@ return 1; } +// adjust weight per container type ("of holding") +static sint32 +weight_adjust (object *op, sint32 weight) +{ + return op->type == CONTAINER + ? lerp (weight, 0, 100, 0, 100 - op->stats.Str) + : weight; +} + /* - * sum_weight() is a recursive function which calculates the weight - * an object is carrying. It goes through in figures out how much - * containers are carrying, and sums it up. + * adjust_weight(object, weight) adds the specified weight to an object, + * and also updates how much the environment(s) is/are carrying. */ -long -sum_weight (object *op) +static void +adjust_weight (object *op, sint32 weight) { - long sum; - object *inv; - - for (sum = 0, inv = op->inv; inv; inv = inv->below) + while (op) { - if (inv->inv) - sum_weight (inv); + weight = weight_adjust (op, weight); - sum += inv->carrying + inv->weight * (inv->nrof ? inv->nrof : 1); + op->carrying += weight; + op = op->env; } - - if (op->type == CONTAINER && op->stats.Str) - sum = (sum * (100 - op->stats.Str)) / 100; - - if (op->carrying != sum) - op->carrying = sum; - - return sum; } -/** - * Return the outermost environment object for a given object. +/* + * 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. */ - -object * -object_get_env_recursive (object *op) +void +object::update_weight () { - while (op->env != NULL) - op = op->env; - return op; + sint32 sum = 0; + + for (object *op = inv; op; op = op->below) + { + if (op->inv) + op->update_weight (); + + sum += op->total_weight (); + } + + carrying = weight_adjust (this, sum); } /* - * 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) @@ -395,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) @@ -978,23 +992,6 @@ attachable::destroy (); } -/* - * sub_weight() recursively (outwards) subtracts a number from the - * weight of an object (and what is carried by it's environment(s)). - */ -void -sub_weight (object *op, signed long weight) -{ - while (op != NULL) - { - if (op->type == CONTAINER) - weight = (signed long) (weight * (100 - op->stats.Str) / 100); - - op->carrying -= weight; - op = op->env; - } -} - /* op->remove (): * This function removes the object op from the linked list of objects * which it is currently tied to. When this function is done, the @@ -1023,22 +1020,9 @@ */ if (env) { - if (nrof) - sub_weight (env, weight * nrof); - else - sub_weight (env, weight + carrying); + adjust_weight (env, -total_weight ()); - /* 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 (); - - if (above) - above->below = below; - else - env->inv = below; + *(above ? &above->below : &env->inv) = below; if (below) below->above = above; @@ -1047,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) { @@ -1273,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 */ @@ -1463,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 * @@ -1487,148 +1479,116 @@ return 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 - * 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 * -get_split_ob (object *orig_ob, uint32 nr) -{ - object *newob; - int is_removed = (QUERY_FLAG (orig_ob, FLAG_REMOVED) != 0); - - if (orig_ob->nrof < nr) - { - sprintf (errmsg, "There are only %d %ss.", orig_ob->nrof ? orig_ob->nrof : 1, &orig_ob->name); - return NULL; - } - - newob = object_create_clone (orig_ob); - - if ((orig_ob->nrof -= nr) < 1) - orig_ob->destroy (1); - else if (!is_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) +// 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 { - 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 + * 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 */ -object * -decrease_ob_nr (object *op, uint32 i) +bool +object::decrease (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; } } /* - * add_weight(object, weight) adds the specified weight to an object, - * and also updates how much the environment(s) is/are carrying. + * 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. */ -void -add_weight (object *op, signed long weight) +object * +object::split (sint32 nr) { - while (op != NULL) + if (nrof < nr) + return 0; + else if (nrof == nr) { - if (op->type == CONTAINER) - weight = (signed long) (weight * (100 - op->stats.Str) / 100); + remove (); + return this; + } + else + { + decrease (nr); - op->carrying += weight; - op = op->env; + 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); } object * @@ -1662,81 +1622,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); @@ -2744,7 +2677,7 @@ } void -object::play_sound (faceidx sound) const +object::play_sound (faceidx sound) { if (!sound) return;