--- deliantra/server/common/object.C 2007/09/30 20:22:15 1.191 +++ deliantra/server/common/object.C 2008/04/20 23:25:09 1.207 @@ -1,11 +1,11 @@ /* - * This file is part of Crossfire TRT, the Roguelike Realtime MORPG. + * This file is part of Deliantra, the Roguelike Realtime MMORPG. * - * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team + * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * Copyright (©) 2001,2007 Mark Wedel & Crossfire Development Team * Copyright (©) 1992,2007 Frank Tore Johansen * - * Crossfire TRT is free software: you can redistribute it and/or modify + * Deliantra 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 3 of the License, or * (at your option) any later version. @@ -18,12 +18,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * The authors can be reached via e-mail to + * 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 @@ -35,46 +36,46 @@ #include -int nrofallocobjects = 0; -static UUID uuid; -const uint64 UUID_SKIP = 1<<19; +UUID UUID::cur; +static uint64_t seq_next_save; +static const uint64 UUID_GAP = 1<<19; objectvec objects; activevec actives; -short freearr_x[SIZEOFFREE] = { 0, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, -2, -2, -2, -1, - 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3, -3, -3, -3, -3, -2, -1 +short freearr_x[SIZEOFFREE] = { + 0, + 0, 1, 1, 1, 0, -1, -1, -1, + 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, -2, -2, -2, -1, + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3, -3, -3, -3, -3, -2, -1 }; -short freearr_y[SIZEOFFREE] = { 0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, -2, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, - -3, -3, -3, -3, -2, -1, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3 +short freearr_y[SIZEOFFREE] = { + 0, + -1, -1, 0, 1, 1, 1, 0, -1, + -2, -2, -2, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, + -3, -3, -3, -3, -2, -1, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3 }; -int maxfree[SIZEOFFREE] = { 0, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 27, 30, 31, 32, 33, 36, 37, 39, 39, 42, 43, 44, 45, - 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49 +int maxfree[SIZEOFFREE] = { + 0, + 9, 10, 13, 14, 17, 18, 21, 22, + 25, 26, 27, 30, 31, 32, 33, 36, 37, 39, 39, 42, 43, 44, 45, 48, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49 }; int freedir[SIZEOFFREE] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 2, 2, 3, 4, 4, 4, 5, 6, 6, 6, 7, 8, 8, 8, - 1, 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 7, 8, 8, 8, 8, 8 + 0, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 2, 2, 3, 4, 4, 4, 5, 6, 6, 6, 7, 8, 8, 8, + 1, 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 7, 8, 8, 8, 8, 8 }; static void -write_uuid (void) +write_uuid (uval64 skip, bool sync) { - char filename1[MAX_BUF], filename2[MAX_BUF]; - - sprintf (filename1, "%s/uuid", settings.localdir); - sprintf (filename2, "%s/uuid~", settings.localdir); - - FILE *fp; - - if (!(fp = fopen (filename2, "w"))) - { - LOG (llevError, "ERROR: cannot open %s for writing, unable to write UUID!\n", filename2); - return; - } - - fprintf (fp, "<1,%llx>\n", (unsigned long long)uuid.seq + UUID_SKIP * 2); - fclose (fp); - rename (filename2, filename1); + CALL_BEGIN (2); + CALL_ARG_SV (newSVval64 (skip)); + CALL_ARG_SV (boolSV (sync)); + CALL_CALL ("cf::write_uuid", G_DISCARD); + CALL_END; } static void @@ -84,6 +85,8 @@ sprintf (filename, "%s/uuid", settings.localdir); + seq_next_save = 0; + FILE *fp; if (!(fp = fopen (filename, "r"))) @@ -91,8 +94,8 @@ if (errno == ENOENT) { LOG (llevInfo, "RESET uid to 1\n"); - uuid.seq = 0; - write_uuid (); + UUID::cur.seq = 0; + write_uuid (UUID_GAP, true); return; } @@ -100,41 +103,47 @@ _exit (1); } - int version; - unsigned long long uid; - if (2 != fscanf (fp, "<%d,%llx>\n", &version, &uid) || version != 1) + UUID::BUF buf; + buf[0] = 0; + fgets (buf, sizeof (buf), fp); + + if (!UUID::cur.parse (buf)) { - LOG (llevError, "FATAL: error reading uid from %s!\n", filename); + LOG (llevError, "FATAL: error reading uid from %s (%s)!\n", filename, buf); _exit (1); } - uuid.seq = uid; - write_uuid (); - LOG (llevDebug, "read UID: %" PRId64 "\n", uid); + LOG (llevDebug, "read UUID: %s\n", UUID::cur.c_str ()); + + write_uuid (UUID_GAP, true); fclose (fp); } UUID -gen_uuid () +UUID::gen () { UUID uid; - uid.seq = ++uuid.seq; + uid.seq = ++cur.seq; + + if (expect_false (cur.seq >= seq_next_save)) + { + seq_next_save = UUID::cur.seq + (UUID_GAP >> 1); + write_uuid (UUID_GAP, false); + } - if (!(uuid.seq & (UUID_SKIP - 1))) - write_uuid (); return uid; } void -init_uuid () +UUID::init () { read_uuid (); } /* Returns TRUE if every key_values in wants has a partner with the same value in has. */ -static int +static bool compare_ob_value_lists_one (const object *wants, const object *has) { key_value *wants_field; @@ -152,28 +161,22 @@ /* Look for a field in has with the same key. */ has_field = get_ob_key_link (has, wants_field->key); - if (has_field == NULL) - { - /* No field with that name. */ - return FALSE; - } + if (!has_field) + return 0; /* No field with that name. */ /* Found the matching field. */ if (has_field->value != wants_field->value) - { - /* Values don't match, so this half of the comparison is false. */ - return FALSE; - } + return 0; /* Values don't match, so this half of the comparison is false. */ /* If we get here, we found a match. Now for the next field in wants. */ } /* If we get here, every field in wants has a matching field in has. */ - return TRUE; + return 1; } /* Returns TRUE if ob1 has the same key_values as ob2. */ -static int +static bool compare_ob_value_lists (const object *ob1, const object *ob2) { /* However, there may be fields in has which aren't partnered in wants, @@ -204,16 +207,16 @@ || ob1->name != ob2->name) return 0; - //TODO: this ain't working well, use nicer and correct overflow check - /* Do not merge objects if nrof would overflow. We use 1UL<<31 since that - * value could not be stored in a sint32 (which unfortunately sometimes is - * used to store nrof). + /* Do not merge objects if nrof would overflow. First part checks + * for unsigned overflow (2c), second part checks wether the result + * would fit into a 32 bit signed int, which is often used to hold + * nrof values. */ - if (ob1->nrof + ob2->nrof >= 1UL << 31) + if (~ob1->nrof < ob2->nrof || ob1->nrof + ob2->nrof > (1UL << 31)) return 0; /* If the objects have been identified, set the BEEN_APPLIED flag. - * This is to the comparison of the flags below will be OK. We + * This is to the comparison of the flags below will be OK. We * just can't ignore the been applied or identified flags, as they * are not equal - just if it has been identified, the been_applied * flags lose any meaning. @@ -251,18 +254,20 @@ || ob1->move_slow_penalty != ob2->move_slow_penalty) return 0; - /* This is really a spellbook check - really, we should - * check all objects in the inventory. + /* This is really a spellbook check - we should in general + * not merge objects with real inventories, as splitting them + * is hard. */ if (ob1->inv || ob2->inv) { - /* if one object has inventory but the other doesn't, not equiv */ - if ((ob1->inv && !ob2->inv) || (ob2->inv && !ob1->inv)) - return 0; + if (!(ob1->inv && ob2->inv)) + return 0; /* inventories differ in length */ + + if (ob1->inv->below || ob2->inv->below) + return 0; /* more than one object in inv */ - /* Now check to see if the two inventory objects could merge */ if (!object::can_merge (ob1->inv, ob2->inv)) - return 0; + return 0; /* inventory objects differ */ /* inventory ok - still need to check rest of this object to see * if it is valid. @@ -291,10 +296,10 @@ break; } - if (ob1->key_values != NULL || ob2->key_values != NULL) + if (ob1->key_values || ob2->key_values) { /* At least one of these has key_values. */ - if ((ob1->key_values == NULL) != (ob2->key_values == NULL)) + 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)) @@ -307,52 +312,76 @@ ob2->optimise (); if (ob1->self || ob2->self) - if (!cfperl_can_merge (ob1, ob2)) - return 0; + { + int k1 = ob1->self ? HvTOTALKEYS (ob1->self) : 0; + int k2 = ob2->self ? HvTOTALKEYS (ob2->self) : 0; + + if (k1 != k2) + return 0; + else if (k1 == 0) + return 1; + else if (!cfperl_can_merge (ob1, ob2)) + return 0; + } } /* Everything passes, must be OK. */ return 1; } +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. + * add_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 +add_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. +/* + * 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); +} -object * -object_get_env_recursive (object *op) +/* + * 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. + */ +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); } /* @@ -421,12 +450,6 @@ return op; } -void -free_all_object_data () -{ - LOG (llevDebug, "%d allocated objects\n", nrofallocobjects); -} - /* * Sets the owner and sets the skill and exp pointers to owner's current * skill and experience objects. @@ -440,6 +463,12 @@ while (owner->owner) owner = owner->owner; + if (flag [FLAG_FREED]) + { + LOG (llevError | logBacktrace, "tried to set owner of %s to %s\n", debug_desc (), owner->debug_desc ()); + return; + } + this->owner = owner; } @@ -589,7 +618,7 @@ object::instantiate () { if (!uuid.seq) // HACK - uuid = gen_uuid (); + uuid = UUID::gen (); speed_left = -0.1f; /* copy the body_info to the body_used - this is only really @@ -758,7 +787,7 @@ void object::link () { assert (!index);//D - uuid = gen_uuid (); + uuid = UUID::gen (); count = ++object_count; refcnt_inc (); @@ -852,7 +881,7 @@ */ if (!drop_to_ground || !map - || map->in_memory != MAP_IN_MEMORY + || map->in_memory != MAP_ACTIVE || map->nodrop || ms ().move_block == MOVE_ALL) { @@ -921,9 +950,10 @@ freed_map->name = "/internal/freed_objects_map"; freed_map->width = 3; freed_map->height = 3; + freed_map->nodrop = 1; freed_map->alloc (); - freed_map->in_memory = MAP_IN_MEMORY; + freed_map->in_memory = MAP_ACTIVE; } map = freed_map; @@ -964,23 +994,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 @@ -1142,7 +1155,7 @@ if (!op->nrof) return 0; - if (top) + if (!top) for (top = op; top && top->above; top = top->above) ; @@ -1467,7 +1480,10 @@ object * object::insert_at (object *where, object *originator, int flags) { - return where->map->insert (this, where->x, where->y, originator, flags); + if (where->env) + return where->env->insert (this); + else + return where->map->insert (this, where->x, where->y, originator, flags); } /* @@ -1497,7 +1513,7 @@ { 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_IN_MEMORY) + if (orig_ob->env == NULL && orig_ob->map->in_memory != MAP_ACTIVE) { 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"); @@ -1597,23 +1613,6 @@ } } -/* - * add_weight(object, weight) adds the specified weight to an object, - * and also updates how much the environment(s) is/are carrying. - */ -void -add_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; - } -} - object * insert_ob_in_ob (object *op, object *where) { @@ -1944,12 +1943,11 @@ void flag_inv (object *op, int flag) { - if (op->inv) - for (object *tmp = op->inv; tmp != NULL; tmp = tmp->below) - { - SET_FLAG (tmp, flag); - flag_inv (tmp, flag); - } + for (object *tmp = op->inv; tmp; tmp = tmp->below) + { + SET_FLAG (tmp, flag); + flag_inv (tmp, flag); + } } /* @@ -1958,12 +1956,11 @@ void unflag_inv (object *op, int flag) { - if (op->inv) - for (object *tmp = op->inv; tmp != NULL; tmp = tmp->below) - { - CLEAR_FLAG (tmp, flag); - unflag_inv (tmp, flag); - } + for (object *tmp = op->inv; tmp; tmp = tmp->below) + { + CLEAR_FLAG (tmp, flag); + unflag_inv (tmp, flag); + } } /* @@ -1975,10 +1972,7 @@ * start and stop are where to start relative to the free_arr array (1,9 * does all 4 immediate directions). This returns the index into the * array of the free spot, -1 if no spot available (dir 0 = x,y) - * Note - this only checks to see if there is space for the head of the - * object - if it is a multispace object, this should be called for all - * pieces. - * Note2: This function does correctly handle tiled maps, but does not + * Note: This function does correctly handle tiled maps, but does not * inform the caller. However, insert_ob_in_map will update as * necessary, so the caller shouldn't need to do any special work. * Note - updated to take an object instead of archetype - this is necessary @@ -2009,7 +2003,7 @@ * ob doesn't have any move type (when used to place exits) * so the AND operation in OB_TYPE_MOVE_BLOCK doesn't work. */ - if (ob->move_type == 0 && ms.move_block != MOVE_ALL) + if (ob && ob->move_type == 0 && ms.move_block != MOVE_ALL) { altern [index++] = i; continue; @@ -2035,6 +2029,9 @@ if (OB_TYPE_MOVE_BLOCK (ob, ms.move_block)) continue; + if (ob->blocked (m, pos.x, pos.y)) + continue; + altern [index++] = i; } @@ -2601,8 +2598,9 @@ char info2[256 * 4]; char *p = info; - p += snprintf (p, 512, "{cnt:%d,uuid:<1.%" PRIx64 ">,name:\"%s\"%s%s,flags:[%s],type:%d}", - count, uuid.seq, + p += snprintf (p, 512, "{cnt:%d,uuid:%s,name:\"%s\"%s%s,flags:[%s],type:%d}", + count, + uuid.c_str (), &name, title ? "\",title:\"" : "", title ? (const char *)title : "",