--- deliantra/server/common/object.C 2007/08/19 19:34:05 1.181 +++ deliantra/server/common/object.C 2008/04/11 13:59:05 1.203 @@ -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,7 +18,7 @@ * 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. @@ -35,46 +35,45 @@ #include -int nrofallocobjects = 0; -static UUID uuid; -const uint64 UUID_SKIP = 1<<19; +UUID UUID::cur; +static const uint64 UUID_SKIP = 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 @@ -91,8 +90,8 @@ if (errno == ENOENT) { LOG (llevInfo, "RESET uid to 1\n"); - uuid.seq = 0; - write_uuid (); + UUID::cur.seq = 0; + write_uuid (UUID_SKIP, true); return; } @@ -100,35 +99,37 @@ _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_SKIP, true); fclose (fp); } UUID -gen_uuid () +UUID::gen () { UUID uid; - uid.seq = ++uuid.seq; + uid.seq = ++cur.seq; - if (!(uuid.seq & (UUID_SKIP - 1))) - write_uuid (); + if (expect_false (!(cur.seq & (UUID_SKIP - 1)))) + write_uuid (UUID_SKIP, false); return uid; } void -init_uuid () +UUID::init () { read_uuid (); } @@ -256,13 +257,14 @@ */ 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 objexts differ */ /* inventory ok - still need to check rest of this object to see * if it is valid. @@ -307,8 +309,17 @@ 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. */ @@ -376,7 +387,6 @@ * multi-object 1 which is closest to the second object. * If it's not a multi-object, it is returned. */ - object * get_nearest_part (object *op, const object *pl) { @@ -422,31 +432,24 @@ 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. + * ACTUALLY NO! investigate! TODO */ void object::set_owner (object *owner) { - if (!owner) - return; + // allow objects which own objects + if (owner) + while (owner->owner) + owner = owner->owner; - /* next line added to allow objects which own objects */ - /* Add a check for ownercounts in here, as I got into an endless loop - * with the fireball owning a poison cloud which then owned the - * fireball. I believe that was caused by one of the objects getting - * freed and then another object replacing it. Since the ownercounts - * didn't match, this check is valid and I believe that cause is valid. - */ - 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; } @@ -588,7 +591,7 @@ *dst = *this; if (speed < 0) - dst->speed_left = speed_left - rndm (); + dst->speed_left -= rndm (); dst->set_speed (dst->speed); } @@ -597,7 +600,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 @@ -766,7 +769,7 @@ void object::link () { assert (!index);//D - uuid = gen_uuid (); + uuid = UUID::gen (); count = ++object_count; refcnt_inc (); @@ -925,9 +928,11 @@ { freed_map = new maptile; + freed_map->path = ""; 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; @@ -1149,7 +1154,7 @@ if (!op->nrof) return 0; - if (top) + if (!top) for (top = op; top && top->above; top = top->above) ; @@ -1240,17 +1245,14 @@ op->remove (); - if (out_of_map (m, op->x, op->y)) + /* Ideally, the caller figures this out. However, it complicates a lot + * of areas of callers (eg, anything that uses find_free_spot would now + * need extra work + */ + if (!xy_normalise (m, op->x, op->y)) { - LOG (llevError, "Trying to insert object outside the map.\n%s\n", op->debug_desc ()); -#ifdef MANY_CORES - /* Better to catch this here, as otherwise the next use of this object - * is likely to cause a crash. Better to find out where it is getting - * improperly inserted. - */ - abort (); -#endif - return op; + op->destroy (); + return 0; } if (object *more = op->more) @@ -1259,13 +1261,6 @@ CLEAR_FLAG (op, FLAG_REMOVED); - /* Ideally, the caller figures this out. However, it complicates a lot - * of areas of callers (eg, anything that uses find_free_spot would now - * need extra work - */ - if (!xy_normalise (m, op->x, op->y)) - return 0; - op->map = m; mapspace &ms = op->ms (); @@ -1309,7 +1304,7 @@ top = ms.bot; /* If there are other objects, then */ - if ((!(flag & INS_MAP_LOAD)) && top) + if (top) { object *last = 0; @@ -1371,8 +1366,6 @@ top = last->below; } } /* If objects on this space */ - if (flag & INS_MAP_LOAD) - top = ms.top; if (flag & INS_ABOVE_FLOOR_ONLY) top = floor; @@ -1415,9 +1408,8 @@ op->map->dirty = true; - if (!(flag & INS_MAP_LOAD)) - if (object *pl = ms.player ()) - pl->contr->ns->floorbox_update (); + if (object *pl = ms.player ()) + pl->contr->ns->floorbox_update (); /* If this object glows, it may affect lighting conditions that are * visible to others on this map. But update_all_los is really @@ -1665,8 +1657,6 @@ object * object::insert (object *op) { - object *tmp, *otmp; - if (!QUERY_FLAG (op, FLAG_REMOVED)) op->remove (); @@ -1678,9 +1668,10 @@ CLEAR_FLAG (op, FLAG_OBJ_ORIGINAL); CLEAR_FLAG (op, FLAG_REMOVED); + if (op->nrof) { - for (tmp = inv; tmp != NULL; tmp = tmp->below) + for (object *tmp = inv; tmp; tmp = tmp->below) if (object::can_merge (tmp, op)) { /* return the original object and remove inserted object @@ -1709,19 +1700,19 @@ else add_weight (this, (op->weight + op->carrying)); - otmp = this->in_player (); - if (otmp && otmp->contr) - if (!QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) + if (object *otmp = this->in_player ()) + if (otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) otmp->update_stats (); - op->map = 0; - op->env = this; + op->owner = 0; // its his/hers now. period. + op->map = 0; + op->env = this; op->above = 0; op->below = 0; - op->x = 0, op->y = 0; + op->x = op->y = 0; /* reset the light list and los of the players on the map */ - if ((op->glow_radius != 0) && 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); @@ -1965,12 +1956,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); + } } /* @@ -1979,12 +1969,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); + } } /* @@ -1996,10 +1985,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 @@ -2011,14 +1997,30 @@ int find_free_spot (const object *ob, maptile *m, int x, int y, int start, int stop) { - int index = 0, flag; int altern[SIZEOFFREE]; + int index = 0, flag; for (int i = start; i < stop; i++) { - flag = ob_blocked (ob, m, x + freearr_x[i], y + freearr_y[i]); - if (!flag) - altern [index++] = i; + mapxy pos (m, x, y); pos.move (i); + + if (!pos.normalise ()) + continue; + + mapspace &ms = *pos; + + if (ms.flags () & P_IS_ALIVE) + continue; + + /* However, often + * 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 && ob->move_type == 0 && ms.move_block != MOVE_ALL) + { + altern [index++] = i; + continue; + } /* Basically, if we find a wall on a space, we cut down the search size. * In this way, we won't return spaces that are on another side of a wall. @@ -2028,8 +2030,22 @@ * to only the spaces immediately surrounding the target area, and * won't look 2 spaces south of the target space. */ - else if ((flag & P_NO_PASS) && maxfree[i] < stop) - stop = maxfree[i]; + if (ms.move_block == MOVE_ALL && maxfree[i] < stop) + { + stop = maxfree[i]; + continue; + } + + /* Note it is intentional that we check ob - the movement type of the + * head of the object should correspond for the entire object. + */ + if (OB_TYPE_MOVE_BLOCK (ob, ms.move_block)) + continue; + + if (ob->blocked (m, pos.x, pos.y)) + continue; + + altern [index++] = i; } if (!index) @@ -2048,7 +2064,7 @@ find_first_free_spot (const object *ob, maptile *m, int x, int y) { for (int i = 0; i < SIZEOFFREE; i++) - if (!ob_blocked (ob, m, x + freearr_x[i], y + freearr_y[i])) + if (!ob->blocked (m, x + freearr_x[i], y + freearr_y[i])) return i; return -1; @@ -2595,8 +2611,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 : "",