--- deliantra/server/common/object.C 2006/09/12 00:53:56 1.33
+++ deliantra/server/common/object.C 2008/04/23 07:13:23 1.217
@@ -1,59 +1,144 @@
-
/*
- CrossFire, A Multiplayer game for X-windows
-
- Copyright (C) 2001 Mark Wedel & Crossfire Development Team
- Copyright (C) 1992 Frank Tore Johansen
+ * This file is part of Deliantra, the Roguelike Realtime MMORPG.
+ *
+ * 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
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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
+ */
- This program 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 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- The authors can be reached via e-mail at crossfire-devel@real-time.com
-*/
-
-/* 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
#include
#include
-#include
+#include
#include
-int nrofallocobjects = 0;
-
-object *objects; /* Pointer to the list of used objects */
-object *active_objects; /* List of active objects that need to be processed */
+#include
-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
+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_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 (uval64 skip, bool sync)
+{
+ CALL_BEGIN (2);
+ CALL_ARG_SV (newSVval64 (skip));
+ CALL_ARG_SV (boolSV (sync));
+ CALL_CALL ("cf::write_uuid", G_DISCARD);
+ CALL_END;
+}
+
+static void
+read_uuid (void)
+{
+ char filename[MAX_BUF];
+
+ sprintf (filename, "%s/uuid", settings.localdir);
+
+ seq_next_save = 0;
+
+ FILE *fp;
+
+ if (!(fp = fopen (filename, "r")))
+ {
+ if (errno == ENOENT)
+ {
+ LOG (llevInfo, "RESET uid to 1\n");
+ UUID::cur.seq = 0;
+ write_uuid (UUID_GAP, true);
+ return;
+ }
+
+ LOG (llevError, "FATAL: cannot open %s for reading!\n", filename);
+ _exit (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 (%s)!\n", filename, buf);
+ _exit (1);
+ }
+
+ LOG (llevDebug, "read UUID: %s\n", UUID::cur.c_str ());
+
+ write_uuid (UUID_GAP, true);
+ fclose (fp);
+}
+
+UUID
+UUID::gen ()
+{
+ UUID uid;
+
+ 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);
+ }
+
+
+ return uid;
+}
+
+void
+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;
@@ -64,35 +149,29 @@
*/
/* For each field in wants, */
- for (wants_field = wants->key_values; wants_field != NULL; wants_field = wants_field->next)
+ for (wants_field = wants->key_values; wants_field; wants_field = wants_field->next)
{
key_value *has_field;
/* 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,
@@ -108,30 +187,31 @@
* replaces - this is mostly for clarity - a decent compiler should hopefully
* reduce this to the same efficiency.
*
- * Check nrof variable *before* calling CAN_MERGE()
+ * Check nrof variable *before* calling can_merge()
*
* Improvements made with merge: Better checking on potion, and also
* check weight
*/
-
-bool object::can_merge (object *ob1, object *ob2)
+bool object::can_merge_slow (object *ob1, object *ob2)
{
/* A couple quicksanity checks */
- if ((ob1 == ob2) || (ob1->type != ob2->type))
+ if (ob1 == ob2
+ || ob1->type != ob2->type
+ || ob1->speed != ob2->speed
+ || ob1->value != ob2->value
+ || ob1->name != ob2->name)
return 0;
- if (ob1->speed != ob2->speed)
- return 0;
-
- /* 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 whether 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.
@@ -142,51 +222,53 @@
if (QUERY_FLAG (ob2, FLAG_IDENTIFIED))
SET_FLAG (ob2, FLAG_BEEN_APPLIED);
+ if (ob1->arch->name != ob2->arch->name
+ || ob1->name != ob2->name
+ || ob1->title != ob2->title
+ || ob1->msg != ob2->msg
+ || ob1->weight != ob2->weight
+ || ob1->attacktype != ob2->attacktype
+ || ob1->magic != ob2->magic
+ || ob1->slaying != ob2->slaying
+ || ob1->skill != ob2->skill
+ || ob1->value != ob2->value
+ || ob1->animation_id != ob2->animation_id
+ || ob1->client_type != ob2->client_type
+ || ob1->materialname != ob2->materialname
+ || ob1->lore != ob2->lore
+ || ob1->subtype != ob2->subtype
+ || ob1->move_type != ob2->move_type
+ || ob1->move_block != ob2->move_block
+ || ob1->move_allow != ob2->move_allow
+ || 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
+ || memcmp (&ob1->resist, &ob2->resist, sizeof (ob1->resist))
+ || memcmp (&ob1->stats , &ob2->stats , sizeof (ob1->stats)))
+ return 0;
- /* the 0x400000 on flags2 is FLAG_INV_LOCK. I don't think something
- * being locked in inventory should prevent merging.
- * 0x4 in flags3 is CLIENT_SENT
- */
- if ((ob1->arch != ob2->arch) ||
- (ob1->flags[0] != ob2->flags[0]) ||
- (ob1->flags[1] != ob2->flags[1]) ||
- ((ob1->flags[2] & ~0x400000) != (ob2->flags[2] & ~0x400000)) ||
- ((ob1->flags[3] & ~0x4) != (ob2->flags[3] & ~0x4)) ||
- (ob1->name != ob2->name) ||
- (ob1->title != ob2->title) ||
- (ob1->msg != ob2->msg) ||
- (ob1->weight != ob2->weight) ||
- (memcmp (&ob1->resist, &ob2->resist, sizeof (ob1->resist)) != 0) ||
- (memcmp (&ob1->stats, &ob2->stats, sizeof (ob1->stats)) != 0) ||
- (ob1->attacktype != ob2->attacktype) ||
- (ob1->magic != ob2->magic) ||
- (ob1->slaying != ob2->slaying) ||
- (ob1->skill != ob2->skill) ||
- (ob1->value != ob2->value) ||
- (ob1->animation_id != ob2->animation_id) ||
- (ob1->client_type != ob2->client_type) ||
- (ob1->materialname != ob2->materialname) ||
- (ob1->lore != ob2->lore) ||
- (ob1->subtype != ob2->subtype) ||
- (ob1->move_type != ob2->move_type) ||
- (ob1->move_block != ob2->move_block) ||
- (ob1->move_allow != ob2->move_allow) ||
- (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))
+ 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 - 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 */
- /* Now check to see if the two inventory objects could merge */
- if (!CAN_MERGE (ob1->inv, ob2->inv))
- return 0;
+ if (ob1->inv->below || ob2->inv->below)
+ return 0; /* more than one object in inv */
+
+ if (!object::can_merge (ob1->inv, ob2->inv))
+ return 0; /* inventory objects differ */
/* inventory ok - still need to check rest of this object to see
* if it is valid.
@@ -204,7 +286,7 @@
* be animated or have a very low speed. Is this an attempted monster
* check?
*/
- if (!QUERY_FLAG (ob1, FLAG_ANIMATE) && FABS ((ob1)->speed) > MIN_ACTIVE_SPEED)
+ if (!QUERY_FLAG (ob1, FLAG_ANIMATE) && ob1->has_active_speed ())
return 0;
switch (ob1->type)
@@ -215,163 +297,151 @@
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))
- /* One has fields, but the other one doesn't. */
- return 0;
- else if (!compare_ob_value_lists (ob1, ob2))
+ if ((!ob1->key_values) != (!ob2->key_values))
+ return 0; /* One has fields, but the other one doesn't. */
+
+ if (!compare_ob_value_lists (ob1, ob2))
return 0;
}
- //TODO: generate an event or call into perl for additional checks
if (ob1->self || ob2->self)
{
ob1->optimise ();
ob2->optimise ();
if (ob1->self || ob2->self)
- return 0;
+ {
+ int k1 = ob1->self ? HvTOTALKEYS (ob1->self) : 0;
+ int k2 = ob2->self ? HvTOTALKEYS (ob2->self) : 0;
+
+ if (k1 != k2)
+ return 0;
+
+ if (k1 == 0)
+ return 1;
+
+ if (!cfperl_can_merge (ob1, ob2))
+ return 0;
+ }
}
/* Everything passes, must be OK. */
return 1;
}
-/*
- * 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.
- */
-long
-sum_weight (object *op)
+// find player who can see this object
+object *
+object::visible_to () const
{
- long sum;
- object *inv;
-
- for (sum = 0, inv = op->inv; inv != NULL; inv = inv->below)
+ if (!flag [FLAG_REMOVED])
{
- if (inv->inv)
- sum_weight (inv);
- sum += inv->carrying + inv->weight * (inv->nrof ? inv->nrof : 1);
+ // see if we are in a container of sorts
+ if (env)
+ {
+ // the player inventory itself is always visible
+ if (env->type == PLAYER)
+ return env;
+
+ // else a player could have our env open
+ object *envest = 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 (envest->is_on_map ())
+ if (object *pl = envest->ms ().player ())
+ if (pl->container == env)
+ return pl;
+ }
+ else
+ {
+ // maybe there is a player standing on the same mapspace
+ // this will catch the case where "this" is a player
+ if (object *pl = ms ().player ())
+ return pl;
+ }
}
- 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.
- */
+ return 0;
+}
-object *
-object_get_env_recursive (object *op)
+// adjust weight per container type ("of holding")
+static sint32
+weight_adjust (object *op, sint32 weight)
{
- while (op->env != NULL)
- op = op->env;
- return op;
+ return op->type == CONTAINER
+ ? lerp (weight, 0, 100, 0, 100 - op->stats.Str)
+ : weight;
}
/*
- * Eneq(@csd.uu.se): Since we can have items buried in a character we need
- * a better check. We basically keeping traversing up until we can't
- * or find a player.
+ * adjust_weight(object, weight) adds the specified weight to an object,
+ * and also updates how much the environment(s) is/are carrying.
*/
-
-object *
-is_player_inv (object *op)
+static void
+adjust_weight (object *op, sint32 weight)
{
- for (; op != NULL && op->type != PLAYER; op = op->env)
- if (op->env == op)
- op->env = NULL;
- return op;
+ while (op)
+ {
+ weight = weight_adjust (op, weight);
+
+ if (!weight)
+ return;
+
+ op->carrying += weight;
+
+ if (object *pl = op->visible_to ())
+ if (pl != op) // player is handled lazily
+ esrv_update_item (UPD_WEIGHT, pl, op);
+
+ op = op->env;
+ }
}
/*
- * 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.
+ * 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
-dump_object2 (object *op)
+object::update_weight ()
{
- errmsg[0] = 0;
- return;
- //TODO//D#d#
-#if 0
- char *cp;
+ sint32 sum = 0;
-/* object *tmp;*/
-
- if (op->arch != NULL)
+ for (object *op = inv; op; op = op->below)
{
- strcat (errmsg, "arch ");
- strcat (errmsg, op->arch->name ? op->arch->name : "(null)");
- strcat (errmsg, "\n");
- if ((cp = get_ob_diff (op, &empty_archetype->clone)) != NULL)
- strcat (errmsg, cp);
-# if 0
- /* Don't dump player diffs - they are too long, mostly meaningless, and
- * will overflow the buffer.
- * Changed so that we don't dump inventory either. This may
- * also overflow the buffer.
- */
- if (op->type != PLAYER && (cp = get_ob_diff (op, &empty_archetype->clone)) != NULL)
- strcat (errmsg, cp);
- for (tmp = op->inv; tmp; tmp = tmp->below)
- dump_object2 (tmp);
-# endif
- strcat (errmsg, "end\n");
+ if (op->inv)
+ op->update_weight ();
+
+ sum += op->total_weight ();
}
- else
+
+ sum = weight_adjust (this, sum);
+
+ if (sum != carrying)
{
- strcat (errmsg, "Object ");
- if (op->name == NULL)
- strcat (errmsg, "(null)");
- else
- strcat (errmsg, op->name);
- strcat (errmsg, "\n");
-# if 0
- if ((cp = get_ob_diff (op, &empty_archetype->clone)) != NULL)
- strcat (errmsg, cp);
- for (tmp = op->inv; tmp; tmp = tmp->below)
- dump_object2 (tmp);
-# endif
- strcat (errmsg, "end\n");
+ carrying = sum;
+
+ if (object *pl = visible_to ())
+ if (pl != this) // player is handled lazily
+ esrv_update_item (UPD_WEIGHT, pl, this);
}
-#endif
}
/*
- * Dumps an object. Returns output in the static global errmsg array.
+ * Used by: Server DM commands: dumpbelow, dump. Some error messages.
*/
-
-void
+char *
dump_object (object *op)
{
- if (op == NULL)
- {
- strcpy (errmsg, "[NULL pointer]");
- return;
- }
- errmsg[0] = '\0';
- dump_object2 (op);
-}
+ if (!op)
+ return strdup ("[NULLOBJ]");
-void
-dump_all_objects (void)
-{
- object *op;
-
- for (op = objects; op != NULL; op = op->next)
- {
- dump_object (op);
- fprintf (logfile, "Object %d\n:%s\n", op->count, errmsg);
- }
+ object_freezer freezer;
+ op->write (freezer);
+ return freezer.as_string ();
}
/*
@@ -379,34 +449,36 @@
* 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)
{
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)
{
- object *op;
+ for_all_objects (op)
+ if (op->count == i)
+ return op;
- for (op = objects; op != NULL; op = op->next)
- if (op->count == i)
- break;
- return op;
+ return 0;
}
/*
@@ -414,172 +486,142 @@
* Used only by the patch command, but not all that useful.
* Enables features like "patch food 999"
*/
-
object *
find_object_name (const char *str)
{
- const char *name = shstr::find (str);
+ shstr_cmp str_ (str);
object *op;
- for (op = objects; op != NULL; op = op->next)
- if (&op->name == name)
+ for_all_objects (op)
+ if (op->name == str_)
break;
return op;
}
-void
-free_all_object_data ()
-{
- LOG (llevDebug, "%d allocated objects\n", nrofallocobjects);
-}
-
-/*
- * Returns the object which this object marks as being the owner.
- * A id-scheme is used to avoid pointing to objects which have been
- * freed and are now reused. If this is detected, the owner is
- * set to NULL, and NULL is returned.
- * Changed 2004-02-12 - if the player is setting at the play again
- * prompt, he is removed, and we don't want to treat him as an owner of
- * anything, so check removed flag. I don't expect that this should break
- * anything - once an object is removed, it is basically dead anyways.
- */
-object *
-object::get_owner ()
-{
- if (!owner
- || QUERY_FLAG (owner, FLAG_FREED)
- || QUERY_FLAG (owner, FLAG_REMOVED))
- owner = 0;
-
- return owner;
-}
-
/*
* 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;
}
-/* Zero the key_values on op, decrementing the shared-string
- * refcounts and freeing the links.
- */
-static void
-free_key_values (object *op)
+int
+object::slottype () const
{
- for (key_value *i = op->key_values; i != 0;)
+ if (type == SKILL)
{
- key_value *next = i->next;
- delete i;
-
- i = next;
+ if (IS_COMBAT_SKILL (subtype)) return slot_combat;
+ if (IS_RANGED_SKILL (subtype)) return slot_ranged;
}
-
- op->key_values = 0;
+ else
+ {
+ if (slot [body_combat].info) return slot_combat;
+ if (slot [body_range ].info) return slot_ranged;
+ }
+
+ return slot_none;
}
-void object::clear ()
+bool
+object::change_weapon (object *ob)
{
- attachable_base::clear ();
+ if (current_weapon == ob)
+ return true;
- free_key_values (this);
+ if (chosen_skill)
+ chosen_skill->flag [FLAG_APPLIED] = false;
- owner = 0;
- name = 0;
- name_pl = 0;
- title = 0;
- race = 0;
- slaying = 0;
- skill = 0;
- msg = 0;
- lore = 0;
- custom_name = 0;
- materialname = 0;
- contr = 0;
- below = 0;
- above = 0;
- inv = 0;
- container = 0;
- env = 0;
- more = 0;
- head = 0;
- map = 0;
- active_next = 0;
- active_prev = 0;
+ current_weapon = ob;
+ chosen_skill = !ob || ob->type == SKILL ? ob : find_skill_by_name (this, ob->skill);
- memset (static_cast(this), 0, sizeof (object_pod));
+ if (chosen_skill)
+ chosen_skill->flag [FLAG_APPLIED] = true;
- SET_FLAG (this, FLAG_REMOVED);
+ update_stats ();
- /* What is not cleared is next, prev, and count */
+ if (ob)
+ {
+ // now check wether any body locations became invalid, in which case
+ // we cannot apply the weapon at the moment.
+ for (int i = 0; i < NUM_BODY_LOCATIONS; ++i)
+ if (slot[i].used < 0)
+ {
+ current_weapon = chosen_skill = 0;
+ update_stats ();
- expmul = 1.0;
- face = blank_face;
- attacked_by_count = -1;
+ new_draw_info_format (NDI_UNIQUE, 0, this,
+ "You try to balance all your items at once, "
+ "but the %s is just too much for your body. "
+ "[You need to unapply some items first.]", &ob->name);
+ return false;
+ }
- if (settings.casting_time)
- casting_time = -1;
-}
+ //new_draw_info_format (NDI_UNIQUE, 0, this, "You switch to your %s.", &ob->name);
+ }
+ else
+ ;//new_draw_info_format (NDI_UNIQUE, 0, this, "You unwield your weapons.");
-void object::clone (object *destination)
-{
- *(object_copy *)destination = *this;
- *(object_pod *)destination = *this;
+ if (ob && !ob->flag [FLAG_APPLIED] && ob->type != SPELL)
+ {
+ LOG (llevError | logBacktrace, "%s changed to unapplied weapon %s",
+ &name, ob->debug_desc ());
+ return false;
+ }
- if (self || cb)
- INVOKE_OBJECT (CLONE, this, ARG_OBJECT (destination));
+ return true;
}
-/*
- * copy object first frees everything allocated by the second object,
- * and then copies the contends of the first object into the second
- * object, allocating what needs to be allocated. Basically, any
- * data that is malloc'd needs to be re-malloc/copied. Otherwise,
- * if the first object is freed, the pointers in the new object
- * will point at garbage.
+/* Zero the key_values on op, decrementing the shared-string
+ * refcounts and freeing the links.
*/
-void
-copy_object (object *op2, object *op)
+static void
+free_key_values (object *op)
{
- bool is_freed = QUERY_FLAG (op, FLAG_FREED);
- bool is_removed = QUERY_FLAG (op, FLAG_REMOVED);
+ for (key_value *i = op->key_values; i; )
+ {
+ key_value *next = i->next;
+ delete i;
+
+ i = next;
+ }
+
+ op->key_values = 0;
+}
- op2->clone (op);
+object &
+object::operator =(const object &src)
+{
+ bool is_freed = flag [FLAG_FREED];
+ bool is_removed = flag [FLAG_REMOVED];
- if (is_freed)
- SET_FLAG (op, FLAG_FREED);
- if (is_removed)
- SET_FLAG (op, FLAG_REMOVED);
+ *(object_copy *)this = src;
- if (op2->speed < 0)
- op->speed_left = op2->speed_left - RANDOM () % 200 / 100.0;
+ flag [FLAG_FREED] = is_freed;
+ flag [FLAG_REMOVED] = is_removed;
/* Copy over key_values, if any. */
- if (op2->key_values)
+ if (src.key_values)
{
key_value *tail = 0;
- key_value *i;
-
- op->key_values = 0;
+ key_values = 0;
- for (i = op2->key_values; i; i = i->next)
+ for (key_value *i = src.key_values; i; i = i->next)
{
key_value *new_link = new key_value;
@@ -588,9 +630,9 @@
new_link->value = i->value;
/* Try and be clever here, too. */
- if (!op->key_values)
+ if (!key_values)
{
- op->key_values = new_link;
+ key_values = new_link;
tail = new_link;
}
else
@@ -600,8 +642,52 @@
}
}
}
+}
+
+/*
+ * copy_to first frees everything allocated by the dst object,
+ * and then copies the contents of itself into the second
+ * object, allocating what needs to be allocated. Basically, any
+ * data that is malloc'd needs to be re-malloc/copied. Otherwise,
+ * if the first object is freed, the pointers in the new object
+ * will point at garbage.
+ */
+void
+object::copy_to (object *dst)
+{
+ *dst = *this;
+
+ if (speed < 0)
+ dst->speed_left -= rndm ();
+
+ dst->set_speed (dst->speed);
+}
+
+void
+object::instantiate ()
+{
+ if (!uuid.seq) // HACK
+ uuid = UUID::gen ();
+
+ speed_left = -0.1f;
+ /* copy the body_info to the body_used - this is only really
+ * need for monsters, but doesn't hurt to do it for everything.
+ * by doing so, when a monster is created, it has good starting
+ * values for the body_used info, so when items are created
+ * for it, they can be properly equipped.
+ */
+ for (int i = NUM_BODY_LOCATIONS; i--; )
+ slot[i].used = slot[i].info;
+
+ attachable::instantiate ();
+}
- update_ob_speed (op);
+object *
+object::clone ()
+{
+ object *neu = create ();
+ copy_to (neu);
+ return neu;
}
/*
@@ -609,12 +695,12 @@
* to the closest player being on the other side, this function can
* be called to update the face variable, _and_ how it looks on the map.
*/
-
void
update_turn_face (object *op)
{
- if (!QUERY_FLAG (op, FLAG_IS_TURNABLE) || op->arch == NULL)
+ if (!QUERY_FLAG (op, FLAG_IS_TURNABLE) || !op->arch)
return;
+
SET_ANIMATION (op, op->direction);
update_object (op, UP_OBJ_FACE);
}
@@ -625,101 +711,24 @@
* This function needs to be called whenever the speed of an object changes.
*/
void
-update_ob_speed (object *op)
+object::set_speed (float speed)
{
- extern int arch_init;
-
- /* No reason putting the archetypes objects on the speed list,
- * since they never really need to be updated.
- */
-
- if (QUERY_FLAG (op, FLAG_FREED) && op->speed)
+ if (flag [FLAG_FREED] && speed)
{
- LOG (llevError, "Object %s is freed but has speed.\n", &op->name);
-#ifdef MANY_CORES
- abort ();
-#else
- op->speed = 0;
-#endif
+ LOG (llevError, "Object %s is freed but has speed.\n", &name);
+ speed = 0;
}
- if (arch_init)
- return;
+ this->speed = speed;
- if (FABS (op->speed) > MIN_ACTIVE_SPEED)
- {
- /* If already on active list, don't do anything */
- if (op->active_next || op->active_prev || op == active_objects)
- return;
-
- /* process_events() expects us to insert the object at the beginning
- * of the list. */
- op->active_next = active_objects;
-
- if (op->active_next != NULL)
- op->active_next->active_prev = op;
-
- active_objects = op;
- }
+ if (has_active_speed ())
+ activate ();
else
- {
- /* If not on the active list, nothing needs to be done */
- if (!op->active_next && !op->active_prev && op != active_objects)
- return;
-
- if (op->active_prev == NULL)
- {
- active_objects = op->active_next;
-
- if (op->active_next != NULL)
- op->active_next->active_prev = NULL;
- }
- else
- {
- op->active_prev->active_next = op->active_next;
-
- if (op->active_next)
- op->active_next->active_prev = op->active_prev;
- }
-
- op->active_next = NULL;
- op->active_prev = NULL;
- }
-}
-
-/* This function removes object 'op' from the list of active
- * objects.
- * This should only be used for style maps or other such
- * reference maps where you don't want an object that isn't
- * in play chewing up cpu time getting processed.
- * The reverse of this is to call update_ob_speed, which
- * will do the right thing based on the speed of the object.
- */
-void
-remove_from_active_list (object *op)
-{
- /* If not on the active list, nothing needs to be done */
- if (!op->active_next && !op->active_prev && op != active_objects)
- return;
-
- if (op->active_prev == NULL)
- {
- active_objects = op->active_next;
- if (op->active_next != NULL)
- op->active_next->active_prev = NULL;
- }
- else
- {
- op->active_prev->active_next = op->active_next;
- if (op->active_next)
- op->active_next->active_prev = op->active_prev;
- }
- op->active_next = NULL;
- op->active_prev = NULL;
+ deactivate ();
}
/*
- * update_object() updates the array which represents the map.
+ * update_object() updates the the map.
* It takes into account invisible objects (and represent squares covered
* by invisible objects by whatever is below them (unless it's another
* invisible object, etc...)
@@ -728,10 +737,6 @@
* updating that window, though, since update_object() is called _often_)
*
* action is a hint of what the caller believes need to be done.
- * For example, if the only thing that has changed is the face (due to
- * an animation), we don't need to call update_position until that actually
- * comes into view of a player. OTOH, many other things, like addition/removal
- * of walls or living creatures may need us to update the flags now.
* current action are:
* UP_OBJ_INSERT: op was inserted
* UP_OBJ_REMOVE: op was removed
@@ -739,13 +744,9 @@
* as that is easier than trying to look at what may have changed.
* UP_OBJ_FACE: only the objects face has changed.
*/
-
void
update_object (object *op, int action)
{
- int update_now = 0, flags;
- MoveType move_on, move_off, move_block, move_slow;
-
if (op == NULL)
{
/* this should never happen */
@@ -753,7 +754,7 @@
return;
}
- if (op->env != NULL)
+ if (op->env)
{
/* Animation is currently handled by client, so nothing
* to do in this case.
@@ -768,7 +769,7 @@
return;
/* make sure the object is within map boundaries */
- if (op->x < 0 || op->x >= MAP_WIDTH (op->map) || op->y < 0 || op->y >= MAP_HEIGHT (op->map))
+ if (op->x < 0 || op->x >= op->map->width || op->y < 0 || op->y >= op->map->height)
{
LOG (llevError, "update_object() called for object out of map!\n");
#ifdef MANY_CORES
@@ -777,442 +778,411 @@
return;
}
- flags = GET_MAP_FLAGS (op->map, op->x, op->y);
- SET_MAP_FLAGS (op->map, op->x, op->y, flags | P_NEED_UPDATE);
- move_slow = GET_MAP_MOVE_SLOW (op->map, op->x, op->y);
- move_on = GET_MAP_MOVE_ON (op->map, op->x, op->y);
- move_block = GET_MAP_MOVE_BLOCK (op->map, op->x, op->y);
- move_off = GET_MAP_MOVE_OFF (op->map, op->x, op->y);
-
- if (action == UP_OBJ_INSERT)
- {
- if (QUERY_FLAG (op, FLAG_BLOCKSVIEW) && !(flags & P_BLOCKSVIEW))
- update_now = 1;
-
- if (QUERY_FLAG (op, FLAG_NO_MAGIC) && !(flags & P_NO_MAGIC))
- update_now = 1;
-
- if (QUERY_FLAG (op, FLAG_DAMNED) && !(flags & P_NO_CLERIC))
- update_now = 1;
-
- if (QUERY_FLAG (op, FLAG_ALIVE) && !(flags & P_IS_ALIVE))
- update_now = 1;
-
- if (op->type == SAFE_GROUND && !(flags & P_SAFE))
- update_now = 1;
-
- if ((move_on | op->move_on) != move_on)
- update_now = 1;
-
- if ((move_off | op->move_off) != move_off)
- update_now = 1;
-
- /* This isn't perfect, but I don't expect a lot of objects to
- * to have move_allow right now.
- */
- if (((move_block | op->move_block) & ~op->move_allow) != move_block)
- update_now = 1;
+ mapspace &m = op->ms ();
- if ((move_slow | op->move_slow) != move_slow)
- update_now = 1;
- }
- /* if the object is being removed, we can't make intelligent
- * decisions, because remove_ob can't really pass the object
- * that is being removed.
- */
+ if (!(m.flags_ & P_UPTODATE))
+ /* nop */;
+ else if (action == UP_OBJ_INSERT)
+ {
+ // this is likely overkill, TODO: revisit (schmorp)
+ if ((QUERY_FLAG (op, FLAG_BLOCKSVIEW) && !(m.flags_ & P_BLOCKSVIEW))
+ || (QUERY_FLAG (op, FLAG_NO_MAGIC) && !(m.flags_ & P_NO_MAGIC))
+ || (op->type == PLAYER && !(m.flags_ & P_PLAYER))
+ || (op->type == SAFE_GROUND && !(m.flags_ & P_SAFE))
+ || (QUERY_FLAG (op, FLAG_ALIVE) && !(m.flags_ & P_IS_ALIVE))
+ || (QUERY_FLAG (op, FLAG_DAMNED) && !(m.flags_ & P_NO_CLERIC))
+ || (m.move_on | op->move_on ) != m.move_on
+ || (m.move_off | op->move_off ) != m.move_off
+ || (m.move_slow | op->move_slow) != m.move_slow
+ /* This isn't perfect, but I don't expect a lot of objects to
+ * to have move_allow right now.
+ */
+ || ((m.move_block | op->move_block) & ~op->move_allow) != m.move_block
+ || 1) // the above is not strong enough a test to skip updating. los maybe? TODO (Schmorp)
+ m.flags_ = 0;
+ }
+ /* if the object is being removed, we can't make intelligent
+ * decisions, because remove_ob can't really pass the object
+ * that is being removed.
+ */
else if (action == UP_OBJ_CHANGE || action == UP_OBJ_REMOVE)
- update_now = 1;
+ m.flags_ = 0;
else if (action == UP_OBJ_FACE)
/* Nothing to do for that case */ ;
else
LOG (llevError, "update_object called with invalid action: %d\n", action);
- if (update_now)
- {
- SET_MAP_FLAGS (op->map, op->x, op->y, flags | P_NO_ERROR | P_NEED_UPDATE);
- update_position (op->map, op->x, op->y);
- }
-
- if (op->more != NULL)
+ if (op->more)
update_object (op->more, action);
}
-static unordered_vector