--- deliantra/server/common/object.C 2006/09/10 00:51:23 1.22
+++ deliantra/server/common/object.C 2007/08/22 20:46:46 1.184
@@ -1,102 +1,185 @@
/*
- * static char *rcsid_object_c =
- * "$Id: object.C,v 1.22 2006/09/10 00:51:23 root Exp $";
+ * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
+ *
+ * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT 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
+ * 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
*/
-/*
- CrossFire, A Multiplayer game for X-windows
-
- Copyright (C) 2001 Mark Wedel & Crossfire Development Team
- Copyright (C) 1992 Frank Tore Johansen
-
- 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
-#ifndef WIN32 /* ---win32 exclude headers */
#include
#include
#include
-#endif /* win32 */
#include
#include
-#include
+#include
#include
+
+#include
+
int nrofallocobjects = 0;
+static UUID uuid;
+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_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 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
+};
+
+static void
+write_uuid (void)
+{
+ 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);
+}
+
+static void
+read_uuid (void)
+{
+ char filename[MAX_BUF];
+
+ sprintf (filename, "%s/uuid", settings.localdir);
+
+ FILE *fp;
+
+ if (!(fp = fopen (filename, "r")))
+ {
+ if (errno == ENOENT)
+ {
+ LOG (llevInfo, "RESET uid to 1\n");
+ uuid.seq = 0;
+ write_uuid ();
+ return;
+ }
+
+ LOG (llevError, "FATAL: cannot open %s for reading!\n", filename);
+ _exit (1);
+ }
+
+ int version;
+ unsigned long long uid;
+ if (2 != fscanf (fp, "<%d,%llx>\n", &version, &uid) || version != 1)
+ {
+ LOG (llevError, "FATAL: error reading uid from %s!\n", filename);
+ _exit (1);
+ }
+
+ uuid.seq = uid;
+ write_uuid ();
+ LOG (llevDebug, "read UID: %" PRId64 "\n", uid);
+ fclose (fp);
+}
+
+UUID
+gen_uuid ()
+{
+ UUID uid;
-object *objects; /* Pointer to the list of used objects */
-object *active_objects; /* List of active objects that need to be processed */
+ uid.seq = ++uuid.seq;
-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};
-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};
+ if (!(uuid.seq & (UUID_SKIP - 1)))
+ write_uuid ();
+
+ return uid;
+}
+
+void
+init_uuid ()
+{
+ read_uuid ();
+}
/* Returns TRUE if every key_values in wants has a partner with the same value in has. */
-static int compare_ob_value_lists_one(const object * wants, const object * has) {
- key_value * wants_field;
-
- /* n-squared behaviour (see get_ob_key_link()), but I'm hoping both
- * objects with lists are rare, and lists stay short. If not, use a
- * different structure or at least keep the lists sorted...
- */
-
- /* For each field in wants, */
- for (wants_field = wants->key_values; wants_field != NULL; 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;
- }
-
- /* 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;
- }
-
- /* If we get here, we found a match. Now for the next field in wants. */
+static int
+compare_ob_value_lists_one (const object *wants, const object *has)
+{
+ key_value *wants_field;
+
+ /* n-squared behaviour (see get_ob_key_link()), but I'm hoping both
+ * objects with lists are rare, and lists stay short. If not, use a
+ * different structure or at least keep the lists sorted...
+ */
+
+ /* For each field in wants, */
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /* 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;
+
+ /* If we get here, every field in wants has a matching field in has. */
+ return TRUE;
}
/* Returns TRUE if ob1 has the same key_values as ob2. */
-static int compare_ob_value_lists(const object * ob1, const object * ob2) {
- /* However, there may be fields in has which aren't partnered in wants,
- * so we need to run the comparison *twice*. :(
- */
- return compare_ob_value_lists_one(ob1, ob2) && compare_ob_value_lists_one(ob2, ob1);
+static int
+compare_ob_value_lists (const object *ob1, const object *ob2)
+{
+ /* However, there may be fields in has which aren't partnered in wants,
+ * so we need to run the comparison *twice*. :(
+ */
+ return compare_ob_value_lists_one (ob1, ob2) && compare_ob_value_lists_one (ob2, ob1);
}
/* Function examines the 2 objects given to it, and returns true if
@@ -106,22 +189,22 @@
* 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))
- return 0;
-
- if (ob1->speed != ob2->speed)
+ if (ob1 == ob2
+ || ob1->type != ob2->type
+ || ob1->speed != ob2->speed
+ || ob1->value != ob2->value
+ || 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).
@@ -141,39 +224,31 @@
if (QUERY_FLAG (ob2, FLAG_IDENTIFIED))
SET_FLAG (ob2, FLAG_BEEN_APPLIED);
-
- /* 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).any ()
+ || ob1->arch != ob2->arch
+ || 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
+ || 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)
return 0;
/* This is really a spellbook check - really, we should
@@ -183,11 +258,11 @@
{
/* if one object has inventory but the other doesn't, not equiv */
if ((ob1->inv && !ob2->inv) || (ob2->inv && !ob1->inv))
- return 0;
+ return 0;
/* Now check to see if the two inventory objects could merge */
- if (!CAN_MERGE (ob1->inv, ob2->inv))
- return 0;
+ if (!object::can_merge (ob1->inv, ob2->inv))
+ return 0;
/* inventory ok - still need to check rest of this object to see
* if it is valid.
@@ -205,58 +280,66 @@
* 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)
{
- case SCROLL:
- if (ob1->level != ob2->level)
- return 0;
- break;
+ case SCROLL:
+ if (ob1->level != ob2->level)
+ return 0;
+ break;
}
if (ob1->key_values != NULL || ob2->key_values != NULL)
{
/* 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;
+ /* One has fields, but the other one doesn't. */
+ return 0;
else if (!compare_ob_value_lists (ob1, ob2))
- return 0;
+ 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;
+ 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.
*/
-signed long sum_weight(object *op) {
- signed long sum;
+long
+sum_weight (object *op)
+{
+ long sum;
object *inv;
- for(sum = 0, inv = op->inv; inv != NULL; inv = inv->below) {
- if (inv->inv)
- sum_weight(inv);
- sum += inv->carrying + inv->weight * (inv->nrof ? inv->nrof : 1);
- }
+
+ for (sum = 0, inv = op->inv; inv; inv = inv->below)
+ {
+ if (inv->inv)
+ sum_weight (inv);
+
+ sum += inv->carrying + inv->weight * (inv->nrof ? inv->nrof : 1);
+ }
+
if (op->type == CONTAINER && op->stats.Str)
- sum = (sum * (100 - op->stats.Str))/100;
- if(op->carrying != sum)
+ sum = (sum * (100 - op->stats.Str)) / 100;
+
+ if (op->carrying != sum)
op->carrying = sum;
+
return sum;
}
@@ -264,23 +347,12 @@
* Return the outermost environment object for a given object.
*/
-object *object_get_env_recursive (object *op) {
- while (op->env != NULL)
- op = op->env;
- return op;
-}
-
-/*
- * 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.
- */
-
-object *is_player_inv (object *op) {
- for (;op!=NULL&&op->type!=PLAYER; op=op->env)
- if (op->env==op)
- op->env = NULL;
- return op;
+object *
+object_get_env_recursive (object *op)
+{
+ while (op->env != NULL)
+ op = op->env;
+ return op;
}
/*
@@ -288,68 +360,15 @@
* Some error messages.
* The result of the dump is stored in the static global errmsg array.
*/
+char *
+dump_object (object *op)
+{
+ if (!op)
+ return strdup ("[NULLOBJ]");
-void dump_object2(object *op) {
-errmsg[0] = 0;
-return;
- //TODO//D#d#
-#if 0
- char *cp;
-/* object *tmp;*/
-
- if(op->arch!=NULL) {
- 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");
- } else {
- 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");
- }
-#endif
-}
-
-/*
- * Dumps an object. Returns output in the static global errmsg array.
- */
-
-void dump_object(object *op) {
- if(op==NULL) {
- strcpy(errmsg,"[NULL pointer]");
- return;
- }
- errmsg[0]='\0';
- dump_object2(op);
-}
-
-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 ();
}
/*
@@ -357,28 +376,31 @@
* 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;
-object *get_nearest_part(object *op, const object *pl) {
- object *tmp,*closest;
- int last_dist,i;
- if(op->more==NULL)
+ if (op->more == NULL)
return op;
- for(last_dist=distance(op,pl),closest=op,tmp=op->more;tmp!=NULL;tmp=tmp->more)
- if((i=distance(tmp,pl))more; tmp != NULL; 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.
*/
+object *
+find_object (tag_t i)
+{
+ for_all_objects (op)
+ if (op->count == i)
+ return op;
-object *find_object(tag_t i) {
- object *op;
- for(op=objects;op!=NULL;op=op->next)
- if(op->count==i)
- break;
- return op;
+ return 0;
}
/*
@@ -386,231 +408,153 @@
* 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);
+object *
+find_object_name (const char *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 ()
+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.
+ * 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)
+{
+ // allow objects which own objects
+ if (owner)
+ while (owner->owner)
+ owner = owner->owner;
-object *get_owner(object *op) {
- if(op->owner==NULL)
- return NULL;
-
- if (!QUERY_FLAG(op->owner,FLAG_FREED) && !QUERY_FLAG(op->owner, FLAG_REMOVED) &&
- op->owner->count==op->ownercount)
- return op->owner;
-
- op->owner=NULL;
- op->ownercount=0;
- return NULL;
+ this->owner = owner;
}
-void clear_owner(object *op)
+int
+object::slottype () const
{
- if (!op) return;
-
- if (op->owner && op->ownercount == op->owner->count)
- op->owner->refcount--;
-
- op->owner = NULL;
- op->ownercount = 0;
+ if (type == SKILL)
+ {
+ if (IS_COMBAT_SKILL (subtype)) return slot_combat;
+ if (IS_RANGED_SKILL (subtype)) return slot_ranged;
+ }
+ else
+ {
+ if (slot [body_combat].info) return slot_combat;
+ if (slot [body_range ].info) return slot_ranged;
+ }
+
+ return slot_none;
}
-/*
- * Sets the owner and sets the skill and exp pointers to owner's current
- * skill and experience objects.
- */
-void set_owner (object *op, object *owner)
+bool
+object::change_weapon (object *ob)
{
- if(owner==NULL||op==NULL)
- return;
+ if (current_weapon == ob)
+ return true;
- /* 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 &&
- owner->ownercount==owner->owner->count) owner=owner->owner;
+ if (chosen_skill)
+ chosen_skill->flag [FLAG_APPLIED] = false;
- /* IF the owner still has an owner, we did not resolve to a final owner.
- * so lets not add to that.
- */
- if (owner->owner) return;
+ current_weapon = ob;
+ chosen_skill = !ob || ob->type == SKILL ? ob : find_skill_by_name (this, ob->skill);
- op->owner=owner;
+ if (chosen_skill)
+ chosen_skill->flag [FLAG_APPLIED] = true;
- op->ownercount=owner->count;
- owner->refcount++;
+ update_stats ();
-}
+ 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 ();
+
+ 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;
+ }
-/* Set the owner to clone's current owner and set the skill and experience
- * objects to clone's objects (typically those objects that where the owner's
- * current skill and experience objects at the time when clone's owner was
- * set - not the owner's current skill and experience objects).
- *
- * Use this function if player created an object (e.g. fire bullet, swarm
- * spell), and this object creates further objects whose kills should be
- * accounted for the player's original skill, even if player has changed
- * skills meanwhile.
- */
-void copy_owner (object *op, object *clone)
-{
- object *owner = get_owner (clone);
- if (owner == NULL) {
- /* players don't have owners - they own themselves. Update
- * as appropriate.
- */
- if (clone->type == PLAYER) owner=clone;
- else return;
+ //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.");
+
+ if (ob && !ob->flag [FLAG_APPLIED] && ob->type != SPELL)
+ {
+ LOG (llevError | logBacktrace, "%s changed to unapplied weapon %s",
+ &name, ob->debug_desc ());
+ return false;
}
- set_owner(op, owner);
+ return true;
}
/* Zero the key_values on op, decrementing the shared-string
* refcounts and freeing the links.
*/
-static void free_key_values(object * op)
+static void
+free_key_values (object *op)
{
- for (key_value *i = op->key_values; i != 0; )
+ for (key_value *i = op->key_values; i; )
{
key_value *next = i->next;
delete i;
+
i = next;
}
-
- op->key_values = 0;
-}
-
-void object::clear ()
-{
- attachable_base::clear ();
-
- free_key_values (this);
-
- name = 0;
- name_pl = 0;
- title = 0;
- race = 0;
- slaying = 0;
- skill = 0;
- msg = 0;
- lore = 0;
- custom_name = 0;
- materialname = 0;
- memset (static_cast(this), 0, sizeof (object_pod));
-
- SET_FLAG (this, FLAG_REMOVED);
-}
-
-void object::clone (object *destination)
-{
- *(object_copy *)destination = *(object_copy *)this;
- *(object_pod *)destination = *(object_pod *)this;
-
- if (self || cb)
- INVOKE_OBJECT (CLONE, this, ARG_OBJECT (destination));
-}
-
-/*
- * clear_object() frees everything allocated by an object, and also
- * clears all variables and flags to default settings.
- */
-
-void clear_object (object *op)
-{
- op->clear ();
-
- op->contr = NULL;
- op->below = NULL;
- op->above = NULL;
- op->inv = NULL;
- op->container=NULL;
- op->env=NULL;
- op->more=NULL;
- op->head=NULL;
- op->map=NULL;
- op->refcount=0;
- op->active_next = NULL;
- op->active_prev = NULL;
- /* What is not cleared is next, prev, and count */
-
- op->expmul = 1.0;
- op->face = blank_face;
- op->attacked_by_count = -1;
-
- if (settings.casting_time)
- op->casting_time = -1;
+ op->key_values = 0;
}
-/*
- * 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.
- */
-
-void copy_object (object *op2, object *op)
+object &
+object::operator =(const object &src)
{
- bool is_freed = QUERY_FLAG (op, FLAG_FREED);
- bool is_removed = QUERY_FLAG (op, FLAG_REMOVED);
-
- op2->clone (op);
+ 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 != NULL)
+ if (src.key_values)
{
- key_value *tail = NULL;
- key_value *i;
+ key_value *tail = 0;
+ key_values = 0;
- op->key_values = NULL;
-
- for (i = op2->key_values; i != NULL; i = i->next)
+ for (key_value *i = src.key_values; i; i = i->next)
{
key_value *new_link = new key_value;
- new_link->next = NULL;
- new_link->key = i->key;
+ new_link->next = 0;
+ new_link->key = i->key;
new_link->value = i->value;
/* Try and be clever here, too. */
- if (op->key_values == NULL)
+ if (!key_values)
{
- op->key_values = new_link;
+ key_values = new_link;
tail = new_link;
}
else
@@ -620,8 +564,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 = speed_left - rndm ();
- update_ob_speed (op);
+ dst->set_speed (dst->speed);
+}
+
+void
+object::instantiate ()
+{
+ if (!uuid.seq) // HACK
+ uuid = gen_uuid ();
+
+ 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 ();
+}
+
+object *
+object::clone ()
+{
+ object *neu = create ();
+ copy_to (neu);
+ return neu;
}
/*
@@ -629,12 +617,14 @@
* 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)
+ return;
-void update_turn_face(object *op) {
- if(!QUERY_FLAG(op,FLAG_IS_TURNABLE)||op->arch==NULL)
- return;
- SET_ANIMATION(op, op->direction);
- update_object(op,UP_OBJ_FACE);
+ SET_ANIMATION (op, op->direction);
+ update_object (op, UP_OBJ_FACE);
}
/*
@@ -642,87 +632,25 @@
* value, or vice versa, then add/remove the object from the active list.
* This function needs to be called whenever the speed of an object changes.
*/
-
-void update_ob_speed(object *op) {
- 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) {
- LOG(llevError,"Object %s is freed but has speed.\n", &op->name);
-#ifdef MANY_CORES
- abort();
-#else
- op->speed = 0;
-#endif
- }
- if (arch_init) {
- return;
- }
- 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;
- }
- 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;
+void
+object::set_speed (float speed)
+{
+ if (flag [FLAG_FREED] && speed)
+ {
+ LOG (llevError, "Object %s is freed but has speed.\n", &name);
+ speed = 0;
}
-}
-/* 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;
+ this->speed = speed;
- 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;
+ if (has_active_speed ())
+ activate ();
+ else
+ 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...)
@@ -731,10 +659,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
@@ -742,105 +666,76 @@
* 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 */
- LOG(llevDebug,"update_object() called for NULL object.\n");
- return;
+void
+update_object (object *op, int action)
+{
+ if (op == NULL)
+ {
+ /* this should never happen */
+ LOG (llevDebug, "update_object() called for NULL object.\n");
+ return;
}
-
- if(op->env!=NULL) {
- /* Animation is currently handled by client, so nothing
- * to do in this case.
- */
- return;
+
+ if (op->env)
+ {
+ /* Animation is currently handled by client, so nothing
+ * to do in this case.
+ */
+ return;
}
- /* If the map is saving, don't do anything as everything is
- * going to get freed anyways.
- */
- if (!op->map || op->map->in_memory == MAP_SAVING) 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)) {
- LOG(llevError,"update_object() called for object out of map!\n");
+ /* If the map is saving, don't do anything as everything is
+ * going to get freed anyways.
+ */
+ if (!op->map || op->map->in_memory == MAP_SAVING)
+ return;
+
+ /* make sure the object is within map boundaries */
+ 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
- abort();
+ abort ();
#endif
- return;
+ return;
+ }
+
+ mapspace &m = op->ms ();
+
+ 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;
}
-
- 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;
-
- 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.
*/
- else if (action == UP_OBJ_CHANGE || action == UP_OBJ_REMOVE) {
- update_now=1;
- } 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)
- update_object(op->more, action);
-}
-
-static std::vector