--- deliantra/server/common/object.C 2007/08/01 01:07:42 1.169
+++ deliantra/server/common/object.C 2008/04/11 17:01:52 1.204
@@ -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,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 +84,8 @@
sprintf (filename, "%s/uuid", settings.localdir);
+ seq_next_save = 0;
+
FILE *fp;
if (!(fp = fopen (filename, "r")))
@@ -91,8 +93,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,35 +102,41 @@
_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 ();
}
@@ -256,13 +264,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 +316,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 +394,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 +439,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 +598,7 @@
*dst = *this;
if (speed < 0)
- dst->speed_left = speed_left - rndm ();
+ dst->speed_left -= rndm ();
dst->set_speed (dst->speed);
}
@@ -597,7 +607,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 +776,7 @@
void object::link ()
{
assert (!index);//D
- uuid = gen_uuid ();
+ uuid = UUID::gen ();
count = ++object_count;
refcnt_inc ();
@@ -925,9 +935,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;
@@ -962,7 +974,11 @@
if (destroy_inventory)
destroy_inv (false);
- play_sound (sound_die);
+ if (is_head ())
+ if (sound_destroy)
+ play_sound (sound_destroy);
+ else if (flag [FLAG_MONSTER])
+ play_sound (sound_find ("monster_destroy")); // quick hack, too lazy to create a generic mechanism
attachable::destroy ();
}
@@ -1087,28 +1103,24 @@
int check_walk_off = !flag [FLAG_NO_APPLY];
+ if (object *pl = ms.player ())
+ {
+ if (pl->container == this)
+ /* If a container that the player is currently using somehow gets
+ * removed (most likely destroyed), update the player view
+ * appropriately.
+ */
+ pl->close_container ();
+
+ pl->contr->ns->floorbox_update ();
+ }
+
for (tmp = ms.bot; tmp; tmp = tmp->above)
{
/* No point updating the players look faces if he is the object
* being removed.
*/
- if (tmp->type == PLAYER && tmp != this)
- {
- /* If a container that the player is currently using somehow gets
- * removed (most likely destroyed), update the player view
- * appropriately.
- */
- if (tmp->container == this)
- {
- flag [FLAG_APPLIED] = 0;
- tmp->container = 0;
- }
-
- if (tmp->contr->ns)
- tmp->contr->ns->floorbox_update ();
- }
-
/* See if object moving off should effect something */
if (check_walk_off
&& ((move_type & tmp->move_off)
@@ -1149,7 +1161,7 @@
if (!op->nrof)
return 0;
- if (top)
+ if (!top)
for (top = op; top && top->above; top = top->above)
;
@@ -1240,25 +1252,14 @@
op->remove ();
-#if 0
- if (!m->active != !op->active)
- if (m->active)
- op->activate_recursive ();
- else
- op->deactivate_recursive ();
-#endif
-
- 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)
@@ -1267,13 +1268,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 ();
@@ -1317,7 +1311,7 @@
top = ms.bot;
/* If there are other objects, then */
- if ((!(flag & INS_MAP_LOAD)) && top)
+ if (top)
{
object *last = 0;
@@ -1379,8 +1373,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;
@@ -1423,13 +1415,8 @@
op->map->dirty = true;
- /* If we have a floor, we know the player, if any, will be above
- * it, so save a few ticks and start from there.
- */
- if (!(flag & INS_MAP_LOAD))
- if (object *pl = ms.player ())
- if (pl->contr->ns)
- 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
@@ -1677,8 +1664,6 @@
object *
object::insert (object *op)
{
- object *tmp, *otmp;
-
if (!QUERY_FLAG (op, FLAG_REMOVED))
op->remove ();
@@ -1690,9 +1675,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
@@ -1721,19 +1707,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);
@@ -1977,12 +1963,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);
+ }
}
/*
@@ -1991,12 +1976,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);
+ }
}
/*
@@ -2008,10 +1992,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
@@ -2023,14 +2004,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.
@@ -2040,8 +2037,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)
@@ -2060,7 +2071,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;
@@ -2347,7 +2358,6 @@
*
* Add a check so we can't pick up invisible objects (0.93.8)
*/
-
int
can_pick (const object *who, const object *item)
{
@@ -2608,14 +2618,15 @@
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 : "",
flag_desc (flagdesc, 512), type);
- if (env)
+ if (!this->flag[FLAG_REMOVED] && env)
p += snprintf (p, 256, "(in %s)", env->debug_desc (info2));
if (map)
@@ -2672,6 +2683,7 @@
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"));
}
if (new_container)
@@ -2697,6 +2709,7 @@
esrv_update_item (UPD_FLAGS, this, new_container);
esrv_send_inventory (this, new_container);
+ play_sound (sound_find ("chest_open"));
}
}
@@ -2735,6 +2748,18 @@
void
object::play_sound (faceidx sound) const
{
- if (map)
+ if (!sound)
+ return;
+
+ if (flag [FLAG_REMOVED])
+ return;
+
+ if (env)
+ {
+ if (object *pl = in_player ())
+ pl->contr->play_sound (sound);
+ }
+ else
map->play_sound (sound, x, y);
}
+