--- deliantra/server/common/object.C 2009/11/08 20:55:39 1.299
+++ deliantra/server/common/object.C 2012/12/12 02:13:04 1.359
@@ -1,24 +1,24 @@
/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
- *
- * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
- * Copyright (©) 2001,2007 Mark Wedel & Crossfire Development Team
- * Copyright (©) 1992,2007 Frank Tore Johansen
- *
+ *
+ * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
+ * Copyright (©) 2001 Mark Wedel & Crossfire Development Team
+ * Copyright (©) 1992 Frank Tore Johansen
+ *
* Deliantra is free software: you can redistribute it and/or modify it under
* the terms of the Affero 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 Affero GNU General Public License
* and the GNU General Public License along with this program. If not, see
* .
- *
+ *
* The authors can be reached via e-mail to
*/
@@ -28,7 +28,6 @@
#include
#include
#include
-#include
#include
@@ -40,6 +39,12 @@
objectvec objects;
activevec actives;
+freelist_item *object::freelist;
+uint32_t object::object_count;
+uint32_t object::free_count;
+uint32_t object::create_count;
+uint32_t object::destroy_count;
+
//+GPL
short freearr_x[SIZEOFFREE] = {
@@ -68,6 +73,25 @@
49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49
};
+const char *wall_suffix[16] = {
+ "0",
+ "1_3",
+ "1_4",
+ "2_1_2",
+ "1_2",
+ "2_2_4",
+ "2_2_1",
+ "3_1",
+ "1_1",
+ "2_2_3",
+ "2_2_2",
+ "3_3",
+ "2_1_1",
+ "3_4",
+ "3_2",
+ "4"
+};
+
static void
write_uuid (uval64 skip, bool sync)
{
@@ -79,7 +103,7 @@
}
static void
-read_uuid (void)
+read_uuid ()
{
char filename[MAX_BUF];
@@ -103,7 +127,7 @@
_exit (1);
}
- UUID::BUF buf;
+ char buf [UUID::MAX_LEN];
buf[0] = 0;
fgets (buf, sizeof (buf), fp);
@@ -142,18 +166,83 @@
read_uuid ();
}
+bool
+UUID::parse (const char *s)
+{
+ if (*s++ != '<' || *s++ != '1' || *s++ != '.')
+ return false;
+
+ seq = 0;
+
+ while (*s != '>')
+ {
+ if (*s < '0')
+ return false;
+
+ // this gives nice branchless code with gcc
+ assert ('0' < 'a' && '0' == 48 && 'a' == 97);
+ int digit = (*s + (*s & 0x40 ? 9 : 0)) & 15;
+
+ seq = (seq << 4) | digit;
+
+ ++s;
+ }
+
+ return true;
+}
+
+char *
+UUID::append (char *buf) const
+{
+ *buf++ = '<';
+ *buf++ = '1';
+ *buf++ = '.';
+
+ uint64_t seq = this->seq;
+ const int bits = 64;
+ char nz = 0;
+ static const char tohex [] = "0123456789abcdef";
+
+ // assert (len >= 3 + bits / 4 + 1 + 1);
+ for (int i = bits / 4; --i; )
+ {
+ uint8_t digit = seq >> (bits - 4);
+
+ *buf = tohex [digit];
+ nz |= digit;
+ buf += nz ? 1 : 0;
+ seq <<= 4;
+ }
+
+ // last digit is special - always emit
+ uint8_t digit = seq >> (bits - 4);
+ *buf++ = tohex [digit];
+
+ *buf++ = '>';
+
+ return buf;
+}
+
+char *
+UUID::c_str () const
+{
+ static char buf [MAX_LEN];
+ *append (buf) = 0;
+ return buf;
+}
+
/* Returns TRUE if every key_values in wants has a partner with the same value in has. */
static bool
compare_ob_value_lists_one (const object *wants, const object *has)
{
- /* n-squared behaviour (see kv_get), but I'm hoping both
+ /* n-squared behaviour (see kv.get), 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 (key_value *kv = wants->key_values; kv; kv = kv->next)
- if (has->kv_get (kv->key) != kv->value)
+ for (key_value *kv = wants->kv.first; kv; kv = kv->next)
+ if (has->kv.get (kv->key) != kv->value)
return false;
/* If we get here, every field in wants has a matching field in has. */
@@ -185,12 +274,13 @@
*/
bool object::can_merge_slow (object *ob1, object *ob2)
{
- /* A couple quicksanity checks */
+ /* A couple quick sanity checks */
if (ob1 == ob2
- || ob1->type != ob2->type
- || fabs (ob1->speed - ob2->speed) >= MIN_ACTIVE_SPEED
- || ob1->value != ob2->value
- || ob1->name != ob2->name)
+ || ob1->type != ob2->type
+ || ob1->value != ob2->value
+ || ob1->name != ob2->name
+ || ob1->custom_name != ob2->custom_name
+ || fabs (ob1->speed - ob2->speed) >= MIN_ACTIVE_SPEED)
return 0;
/* Do not merge objects if nrof would overflow, assume nrof
@@ -204,11 +294,11 @@
* are not equal - just if it has been identified, the been_applied
* flags lose any meaning.
*/
- if (QUERY_FLAG (ob1, FLAG_IDENTIFIED))
- SET_FLAG (ob1, FLAG_BEEN_APPLIED);
+ if (ob1->flag [FLAG_IDENTIFIED])
+ ob1->set_flag (FLAG_BEEN_APPLIED);
- if (QUERY_FLAG (ob2, FLAG_IDENTIFIED))
- SET_FLAG (ob2, FLAG_BEEN_APPLIED);
+ if (ob2->flag [FLAG_IDENTIFIED])
+ ob2->set_flag (FLAG_BEEN_APPLIED);
if (ob1->arch->archname != ob2->arch->archname
|| ob1->name != ob2->name
@@ -221,9 +311,9 @@
|| ob1->skill != ob2->skill
|| ob1->value != ob2->value
|| ob1->animation_id != ob2->animation_id
- || (ob1->face != ob2->face && !ob1->animation_id) // face and animation are dependent on each other
+ || (ob1->face != ob2->face && !ob1->animation_id) // face and animation are dependend on each other
|| ob1->client_type != ob2->client_type
- || ob1->materialname != ob2->materialname
+ || ob1->material != ob2->material
|| ob1->lore != ob2->lore
|| ob1->subtype != ob2->subtype
|| ob1->move_type != ob2->move_type
@@ -263,18 +353,18 @@
*/
}
- /* Don't merge objects that are applied. With the new 'body' code,
+ /* Don't merge objects that are applied. With the new 'body' code,
* it is possible for most any character to have more than one of
* some items equipped, and we don't want those to merge.
*/
- if (QUERY_FLAG (ob1, FLAG_APPLIED) || QUERY_FLAG (ob2, FLAG_APPLIED))
+ if (ob1->flag [FLAG_APPLIED] || ob2->flag [FLAG_APPLIED])
return 0;
- /* Note sure why the following is the case - either the object has to
- * be animated or have a very low speed. Is this an attempted monster
+ /* Not sure why the following is the case - either the object has to
+ * be animated or have a very low speed. Is this an attempted monster
* check?
*/
- if (!QUERY_FLAG (ob1, FLAG_ANIMATE) && ob1->has_active_speed ())
+ if (!ob1->flag [FLAG_ANIMATE] && ob1->has_active_speed ())
return 0;
switch (ob1->type)
@@ -285,10 +375,10 @@
break;
}
- if (ob1->key_values || ob2->key_values)
+ if (!ob1->kv.empty () || !ob2->kv.empty ())
{
/* At least one of these has key_values. */
- if ((!ob1->key_values) != (!ob2->key_values))
+ if (ob1->kv.empty () != ob2->kv.empty ())
return 0; /* One has fields, but the other one doesn't. */
if (!compare_ob_value_lists (ob1, ob2))
@@ -358,32 +448,31 @@
}
// adjust weight per container type ("of holding")
-static sint32
-weight_adjust_for (object *op, sint32 weight)
+static weight_t
+weight_adjust_for (object *op, weight_t weight)
{
- return op->type == CONTAINER
- ? lerp (weight, 0, 100, 0, 100 - op->stats.Str)
- : weight;
+ if (op->type == CONTAINER)
+ weight -= weight * op->stats.Str / 100;
+
+ return weight;
}
/*
- * adjust_weight(object, weight) adds the specified weight to an object,
+ * subtracts, then adds, the specified weight to an object,
* and also updates how much the environment(s) is/are carrying.
*/
static void
-adjust_weight (object *op, sint32 weight)
+adjust_weight (object *op, weight_t sub, weight_t add)
{
while (op)
{
- // adjust by actual difference to account for rounding errors
- // i.e. (w2 - w1) / f != w2 / f - w1 / f and the latter is correct
- weight = weight_adjust_for (op, op->carrying)
- - weight_adjust_for (op, op->carrying - weight);
-
- if (!weight)
- return;
-
- op->carrying += weight;
+ weight_t carrying = (weight_t)op->carrying
+ - weight_adjust_for (op, sub)
+ + weight_adjust_for (op, add);
+
+ sub = op->carrying;
+ op->carrying = carrying;
+ add = op->carrying;
if (object *pl = op->visible_to ())
if (pl != op) // player is handled lazily
@@ -401,20 +490,21 @@
void
object::update_weight ()
{
- sint32 sum = 0;
+ weight_t sum = 0;
for (object *op = inv; op; op = op->below)
{
- if (op->inv)
- op->update_weight ();
+ op->update_weight ();
- sum += op->total_weight ();
+ sum += weight_adjust_for (this, op->total_weight ());
}
- sum = weight_adjust_for (this, sum);
-
if (sum != carrying)
{
+ if (carrying != sum && carrying)//D
+ LOG (llevDebug, "updating carrying got %ld, expected %ld (%s)\n",
+ (long long)sum, (long long)carrying, debug_desc ());
+
carrying = sum;
if (object *pl = visible_to ())
@@ -458,6 +548,21 @@
}
/*
+ * Returns the object which has the uuid equal to the argument.
+ * MOAR VERRRY slow.
+ */
+
+object *
+find_object_uuid (UUID i)
+{
+ for_all_objects (op)
+ if (op->uuid == i)
+ return op;
+
+ return 0;
+}
+
+/*
* Returns the first object which has a name equal to the argument.
* Used only by the patch command, but not all that useful.
* Enables features like "patch food 999"
@@ -497,90 +602,6 @@
this->owner = owner;
}
-int
-object::slottype () const
-{
- 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;
-}
-
-bool
-object::change_weapon (object *ob)
-{
- if (current_weapon == ob)
- return true;
-
- if (chosen_skill)
- chosen_skill->flag [FLAG_APPLIED] = false;
-
- current_weapon = ob;
- chosen_skill = !ob || ob->type == SKILL ? ob : find_skill_by_name (this, ob->skill);
-
- if (chosen_skill)
- chosen_skill->flag [FLAG_APPLIED] = true;
-
- 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 - use the 'body' command to see "
- "how many items you cna wera on a specific body part.]", &ob->name);
- return false;
- }
-
- //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;
- }
-
- return true;
-}
-
-/* Zero the key_values on op, decrementing the shared-string
- * refcounts and freeing the links.
- */
-static void
-free_key_values (object *op)
-{
- for (key_value *i = op->key_values; i; )
- {
- key_value *next = i->next;
- delete i;
-
- i = next;
- }
-
- op->key_values = 0;
-}
-
/*
* copy_to first frees everything allocated by the dst object,
* and then copies the contents of itself into the second
@@ -594,39 +615,11 @@
{
dst->remove ();
*(object_copy *)dst = *this;
- dst->flag [FLAG_REMOVED] = true;
-
- /* Copy over key_values, if any. */
- if (key_values)
- {
- key_value *tail = 0;
- dst->key_values = 0;
- for (key_value *i = key_values; i; i = i->next)
- {
- key_value *new_link = new key_value;
-
- new_link->next = 0;
- new_link->key = i->key;
- new_link->value = i->value;
-
- /* Try and be clever here, too. */
- if (!dst->key_values)
- {
- dst->key_values = new_link;
- tail = new_link;
- }
- else
- {
- tail->next = new_link;
- tail = new_link;
- }
- }
- }
-
- if (speed < 0)
- dst->speed_left -= rndm ();
+ // maybe move to object_copy?
+ dst->kv = kv;
+ dst->flag [FLAG_REMOVED] = true;
dst->activate ();
}
@@ -636,11 +629,16 @@
if (!uuid.seq) // HACK
uuid = UUID::gen ();
- speed_left = -0.1f;
+ // TODO: unclean state changes, should not be done in copy_to AND instantiate
+ if (flag [FLAG_RANDOM_SPEED] && speed)
+ speed_left = - speed - rndm (); // TODO animation
+ else
+ speed_left = -1.;
+
/* 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
+ * 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--; )
@@ -654,6 +652,11 @@
{
object *neu = create ();
copy_to (neu);
+
+ // TODO: unclean state changes, should not be done in clone AND instantiate
+ if (neu->flag [FLAG_RANDOM_SPEED] && neu->speed)
+ neu->speed_left = - neu->speed - rndm (); // TODO animation
+
neu->map = map; // not copied by copy_to
return neu;
}
@@ -666,7 +669,7 @@
void
update_turn_face (object *op)
{
- if (!QUERY_FLAG (op, FLAG_IS_TURNABLE) || !op->arch)
+ if (!op->flag [FLAG_IS_TURNABLE] || !op->arch)
return;
SET_ANIMATION (op, op->direction);
@@ -737,17 +740,17 @@
mapspace &m = op->ms ();
if (!(m.flags_ & P_UPTODATE))
- /* nop */;
+ m.update_up (); // nothing to do except copy up
else if (action == UP_OBJ_INSERT)
{
#if 0
// 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))
+ if ((op->flag [FLAG_BLOCKSVIEW] && !(m.flags_ & P_BLOCKSVIEW))
+ || (op->flag [FLAG_NO_MAGIC] && !(m.flags_ & P_NO_MAGIC))
|| (op->is_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))
+ || (op->flag [FLAG_ALIVE] && !(m.flags_ & P_IS_ALIVE))
+ || (op->flag [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
@@ -768,7 +771,7 @@
else if (action == UP_OBJ_CHANGE || action == UP_OBJ_REMOVE)
m.invalidate ();
else if (action == UP_OBJ_FACE)
- /* Nothing to do for that case */ ;
+ m.update_up (); // nothing to do for that case, except copy up
else
LOG (llevError, "update_object called with invalid action: %d\n", action);
@@ -778,29 +781,30 @@
object::object ()
{
- SET_FLAG (this, FLAG_REMOVED);
+ this->set_flag (FLAG_REMOVED);
//expmul = 1.0; declared const for the time being
- face = blank_face;
+ face = blank_face;
+ material = MATERIAL_NULL;
}
object::~object ()
{
unlink ();
- free_key_values (this);
+ kv.clear ();
}
-static int object_count;
-
void object::link ()
{
assert (!index);//D
uuid = UUID::gen ();
- count = ++object_count;
refcnt_inc ();
objects.insert (this);
+
+ ++create_count;
+
}
void object::unlink ()
@@ -808,6 +812,8 @@
if (!index)
return;
+ ++destroy_count;
+
objects.erase (this);
refcnt_dec ();
}
@@ -895,7 +901,7 @@
*/
if (!drop_to_ground
|| !map
- || map->in_memory != MAP_ACTIVE
+ || !map->linkable ()
|| map->no_drop
|| ms ().move_block == MOVE_ALL)
{
@@ -921,33 +927,86 @@
}
}
-object *object::create ()
+/*
+ * Remove and free all objects in the inventory of the given object.
+ * Unlike destroy_inv, this assumes the *this is destroyed as well
+ * well, so we can (and have to!) take shortcuts.
+ */
+void
+object::destroy_inv_fast ()
+{
+ while (object *op = inv)
+ {
+ // remove from object the fast way
+ op->flag [FLAG_REMOVED] = true;
+ op->env = 0;
+ if ((inv = inv->below))
+ inv->above = 0;
+
+ // then destroy
+ op->destroy ();
+ }
+}
+
+void
+object::freelist_free (int count)
{
- object *op = new object;
+ while (count-- && freelist)
+ {
+ freelist_item *next = freelist->next;
+ // count is being "destroyed"
+
+ sfree ((char *)freelist, sizeof (object));
+
+ freelist = next;
+ --free_count;
+ }
+}
+
+object *
+object::create ()
+{
+ object *op;
+
+ if (freelist)
+ {
+ freelist_item li = *freelist;
+ memset (freelist, 0, sizeof (object));
+
+ op = new (freelist) object;
+ op->count = li.count;
+
+ freelist = li.next;
+ --free_count;
+ }
+ else
+ {
+ void *ni = salloc0 (sizeof (object));
+
+ op = new(ni) object;
+
+ op->count = ++object_count;
+ }
+
op->link ();
+
return op;
}
-static struct freed_map : maptile
+void
+object::do_delete ()
{
- freed_map ()
- {
- path = "";
- name = "/internal/freed_objects_map";
- width = 3;
- height = 3;
- no_drop = 1;
- no_reset = 1;
-
- alloc ();
- in_memory = MAP_ACTIVE;
- }
-
- ~freed_map ()
- {
- destroy ();
- }
-} freed_map; // freed objects are moved here to avoid crashes
+ uint32_t count = this->count;
+
+ this->~object ();
+
+ freelist_item *li = (freelist_item *)this;
+ li->next = freelist;
+ li->count = count;
+
+ freelist = li;
+ ++free_count;
+}
void
object::do_destroy ()
@@ -1000,7 +1059,7 @@
return;
}
- destroy_inv (false);
+ destroy_inv_fast ();
if (is_head ())
if (sound_destroy)
@@ -1042,7 +1101,7 @@
esrv_del_item (pl->contr, count);
flag [FLAG_REMOVED] = true; // hack around the issue of visible_to checking flag_removed
- adjust_weight (env, -total_weight ());
+ adjust_weight (env, total_weight (), 0);
object *pl = in_player ();
@@ -1062,18 +1121,27 @@
below = 0;
env = 0;
- /* NO_FIX_PLAYER is set when a great many changes are being
- * made to players inventory. If set, avoiding the call
- * to save cpu time.
- */
- if (pl)
- if (pl->is_player () && (glow_radius || !QUERY_FLAG (pl, FLAG_NO_FIX_PLAYER)))
- {
- pl->update_stats ();
+ if (pl && pl->is_player ())
+ {
+ if (expect_false (pl->contr->combat_ob == this))
+ {
+ pl->apply (pl->contr->combat_ob, AP_UNAPPLY | AP_IGNORE_CURSE);
+ pl->contr->combat_ob = 0;
+ if (pl->contr->ranged_ob) pl->apply (pl->contr->ranged_ob);
+ }
- if (glow_radius && pl->is_on_map ())
- update_all_los (pl->map, pl->x, pl->y);
- }
+ if (expect_false (pl->contr->ranged_ob == this))
+ {
+ pl->apply (pl->contr->ranged_ob, AP_UNAPPLY | AP_IGNORE_CURSE);
+ pl->contr->ranged_ob = 0;
+ if (pl->contr->combat_ob) pl->apply (pl->contr->combat_ob);
+ }
+
+ pl->contr->queue_stats_update ();
+
+ if (expect_false (glow_radius) && pl->is_on_map ())
+ update_all_los (pl->map, pl->x, pl->y);
+ }
}
else if (map)
{
@@ -1101,8 +1169,8 @@
// removing a container should close it
close_container ();
}
-
- esrv_del_item (pl->contr, count);
+ else
+ esrv_del_item (pl->contr, count);
}
/* link the object above us */
@@ -1115,9 +1183,6 @@
ms.invalidate ();
- if (map->in_memory == MAP_SAVING)
- return;
-
int check_walk_off = !flag [FLAG_NO_APPLY];
if (object *pl = ms.player ())
@@ -1203,7 +1268,7 @@
for (archetype *at = (archetype *)arch->more; at; at = (archetype *)at->more)
{
- object *op = arch_to_object (at);
+ object *op = at->instance ();
op->name = name;
op->name_pl = name_pl;
@@ -1251,7 +1316,7 @@
*
* Return value:
* new object if 'op' was merged with other object
- * NULL if 'op' was destroyed
+ * NULL if there was an error (destroyed, blocked etc.)
* just 'op' otherwise
*/
object *
@@ -1261,7 +1326,7 @@
if (m == &freed_map)//D TODO: remove soon
{//D
- LOG (llevError | logBacktrace, "tries to insret object on freed objects map: %s", op->debug_desc ());//D
+ LOG (llevError | logBacktrace, "tries to insert object on freed objects map: %s", op->debug_desc ());//D
}//D
/* Ideally, the caller figures this out. However, it complicates a lot
@@ -1298,11 +1363,11 @@
tmp->destroy ();
}
- CLEAR_FLAG (op, FLAG_APPLIED); /* hack for fixing F_APPLIED in items of dead people */
- CLEAR_FLAG (op, FLAG_INV_LOCKED);
+ op->clr_flag (FLAG_APPLIED); /* hack for fixing F_APPLIED in items of dead people */
+ op->clr_flag (FLAG_INV_LOCKED);
- if (!QUERY_FLAG (op, FLAG_ALIVE))
- CLEAR_FLAG (op, FLAG_NO_STEAL);
+ if (!op->flag [FLAG_ALIVE])
+ op->clr_flag (FLAG_NO_STEAL);
if (flag & INS_BELOW_ORIGINATOR)
{
@@ -1347,10 +1412,10 @@
*/
for (object *tmp = ms.bot; tmp; tmp = tmp->above)
{
- if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) || QUERY_FLAG (tmp, FLAG_OVERLAY_FLOOR))
+ if (tmp->flag [FLAG_IS_FLOOR] || tmp->flag [FLAG_OVERLAY_FLOOR])
floor = tmp;
- if (QUERY_FLAG (tmp, FLAG_NO_PICK) && (tmp->move_type & (MOVE_FLY_LOW | MOVE_FLY_HIGH)) && !QUERY_FLAG (tmp, FLAG_IS_FLOOR))
+ if (tmp->flag [FLAG_NO_PICK] && (tmp->move_type & (MOVE_FLY_LOW | MOVE_FLY_HIGH)) && !tmp->flag [FLAG_IS_FLOOR])
{
/* We insert above top, so we want this object below this */
top = tmp->below;
@@ -1378,7 +1443,7 @@
object *last;
for (last = top; last != floor; last = last->below)
- if (QUERY_FLAG (last, FLAG_BLOCKSVIEW) && (last->type != EXIT))
+ if (last->flag [FLAG_BLOCKSVIEW] && (last->type != EXIT))
break;
/* Check to see if we found the object that blocks view,
@@ -1461,14 +1526,14 @@
/* if this is not the head or flag has been passed, don't check walk on status */
if (!(flag & INS_NO_WALK_ON) && op->is_head ())
{
- if (check_move_on (op, originator))
+ if (check_move_on (op, originator, flag))
return 0;
- /* If we are a multi part object, lets work our way through the check
+ /* If we are a multi part object, let's work our way through the check
* walk on's.
*/
for (object *tmp = op->more; tmp; tmp = tmp->more)
- if (check_move_on (tmp, originator))
+ if (check_move_on (tmp, originator, flag))
return 0;
}
@@ -1488,7 +1553,7 @@
if (tmp->arch->archname == archname) /* same archetype */
tmp->destroy ();
- object *tmp = arch_to_object (archetype::find (archname));
+ object *tmp = archetype::find (archname)->instance ();
tmp->x = op->x;
tmp->y = op->y;
@@ -1505,7 +1570,7 @@
return where->map->insert (this, where->x, where->y, originator, flags);
}
-// check whether we can put this into the map, respect max_nrof, max_volume, max_items
+// check whether we can put this into the map, respect max_volume, max_items
bool
object::can_drop_at (maptile *m, int x, int y, object *originator)
{
@@ -1515,15 +1580,14 @@
if (!items // testing !items ensures we can drop at least one item
|| (items < m->max_items
- && ms.volume () < m->max_volume
- && nrof <= m->max_nrof))
+ && ms.volume () < m->max_volume))
return true;
- if (originator && originator->is_player ())
- originator->contr->failmsg (format (
- "No matter how hard you try, you just cannot put the %s here H",
+ if (originator)
+ originator->failmsgf (
+ "No matter how hard you try, you just cannot put the %s here! H",
query_name ()
- ));
+ );
return false;
}
@@ -1545,12 +1609,15 @@
if (nrof > nr)
{
+ weight_t oweight = total_weight ();
+
nrof -= nr;
- adjust_weight (env, -weight * max (1, nr)); // carrying == 0
if (object *pl = visible_to ())
esrv_update_item (UPD_NROF, pl, this);
+ adjust_weight (env, oweight, total_weight ());
+
return true;
}
else
@@ -1634,13 +1701,17 @@
if (object::can_merge (tmp, op))
{
/* return the original object and remove inserted object
- (client needs the original object) */
+ (client prefers the original object) */
+
+ // carrying must be 0 for mergable objects
+ weight_t oweight = weight_t (tmp->weight) * tmp->nrof;
+
tmp->nrof += op->nrof;
if (object *pl = tmp->visible_to ())
esrv_update_item (UPD_NROF, pl, tmp);
- adjust_weight (this, op->total_weight ());
+ adjust_weight (this, oweight, weight_t (tmp->weight) * tmp->nrof);
op->destroy ();
op = tmp;
@@ -1666,7 +1737,7 @@
if (object *pl = op->visible_to ())
esrv_send_item (pl, op);
- adjust_weight (this, op->total_weight ());
+ adjust_weight (this, 0, op->total_weight ());
inserted:
/* reset the light list and los of the players on the map */
@@ -1675,9 +1746,9 @@
update_stats ();
update_all_los (map, x, y);
}
- else if (is_player () && !flag [FLAG_NO_FIX_PLAYER])
+ else if (is_player ())
// if this is a player's inventory, update stats
- update_stats ();
+ contr->queue_stats_update ();
INVOKE_OBJECT (INSERT, this);
@@ -1705,9 +1776,9 @@
* on top.
*/
int
-check_move_on (object *op, object *originator)
+check_move_on (object *op, object *originator, int flags)
{
- if (QUERY_FLAG (op, FLAG_NO_APPLY))
+ if (op->flag [FLAG_NO_APPLY])
return 0;
object *tmp;
@@ -1754,7 +1825,7 @@
* space doesn't slow down swimming (for example), if you can't actually
* swim on that space, can't use it to avoid the penalty.
*/
- if (!QUERY_FLAG (op, FLAG_WIZPASS))
+ if (!op->flag [FLAG_WIZPASS])
{
if ((!op->move_type && tmp->move_slow & MOVE_WALK) ||
((op->move_type & tmp->move_slow) && (op->move_type & ~tmp->move_slow & ~tmp->move_block) == 0))
@@ -1774,6 +1845,11 @@
if ((!op->move_type && tmp->move_on & MOVE_WALK) ||
((op->move_type & tmp->move_on) && (op->move_type & ~tmp->move_on & ~tmp->move_block) == 0))
{
+ if ((flags & INS_NO_AUTO_EXIT)
+ && (tmp->type == EXIT || tmp->type == TELEPORTER
+ || tmp->type == HOLE || tmp->type == TRAPDOOR)) //TODO: temporary, fix exits instead
+ continue;
+
move_apply (tmp, op, originator);
if (op->destroyed ())
@@ -1895,7 +1971,7 @@
{
for (object *tmp = op->inv; tmp; tmp = tmp->below)
{
- SET_FLAG (tmp, flag);
+ tmp->set_flag (flag);
flag_inv (tmp, flag);
}
}
@@ -1908,7 +1984,7 @@
{
for (object *tmp = op->inv; tmp; tmp = tmp->below)
{
- CLEAR_FLAG (tmp, flag);
+ tmp->clr_flag (flag);
unflag_inv (tmp, flag);
}
}
@@ -1935,7 +2011,7 @@
find_free_spot (const object *ob, maptile *m, int x, int y, int start, int stop)
{
int altern[SIZEOFFREE];
- int index = 0, flag;
+ int index = 0;
for (int i = start; i < stop; i++)
{
@@ -2057,7 +2133,7 @@
int
find_dir (maptile *m, int x, int y, object *exclude)
{
- int max = SIZEOFFREE, mflags;
+ int max = SIZEOFFREE;
MoveType move_type;
if (exclude && exclude->head_ () != exclude)
@@ -2108,45 +2184,84 @@
}
/*
- * find_dir_2(delta-x,delta-y) will return a direction in which
- * an object which has subtracted the x and y coordinates of another
- * object, needs to travel toward it.
+ * find_dir_2(delta-x,delta-y) will return a direction value
+ * for running into direct [dx, dy].
+ * (the opposite of crossfire's find_dir_2!)
*/
int
find_dir_2 (int x, int y)
{
+#if 1 // new algorithm
+ // this works by putting x, y into 16 sectors, which
+ // are not equal sized, but are a better approximation
+ // then the old algorithm, and then using a mapping
+ // table to map it into a direction value.
+ // basically, it maps these comparisons to each bit
+ // bit #3: x < 0
+ // bit #2: y < 0
+ // bit #1: x > y
+ // bit #0: x > 2y
+
+ static const uint8 dir[16] = {
+ 4, 5, 4, 3,
+ 2, 1, 2, 3,
+ 6, 5, 6, 7,
+ 8, 1, 8, 7,
+ };
+ int sector = 0;
+
+ // this is a bit ugly, but more likely to result in branchless code
+ sector |= x < 0 ? 8 : 0;
+ x = x < 0 ? -x : x; // abs
+
+ sector |= y < 0 ? 4 : 0;
+ y = y < 0 ? -y : y; // abs
+
+ if (x > y)
+ {
+ sector |= 2;
+
+ if (x > y * 2)
+ sector |= 1;
+ }
+ else
+ {
+ if (y > x * 2)
+ sector |= 1;
+ else if (!y)
+ return 0; // x == 0 here
+ }
+
+ return dir [sector];
+#else // old algorithm
int q;
if (y)
- q = x * 100 / y;
+ q = 128 * x / y;
else if (x)
- q = -300 * x;
+ q = -512 * x; // to make it > 309
else
return 0;
if (y > 0)
{
- if (q < -242)
- return 3;
- if (q < -41)
- return 2;
- if (q < 41)
- return 1;
- if (q < 242)
- return 8;
- return 7;
- }
+ if (q < -309) return 7;
+ if (q < -52) return 6;
+ if (q < 52) return 5;
+ if (q < 309) return 4;
- if (q < -242)
- return 7;
- if (q < -41)
- return 6;
- if (q < 41)
- return 5;
- if (q < 242)
- return 4;
+ return 3;
+ }
+ else
+ {
+ if (q < -309) return 3;
+ if (q < -52) return 2;
+ if (q < 52) return 1;
+ if (q < 309) return 8;
- return 3;
+ return 7;
+ }
+#endif
}
/*
@@ -2156,13 +2271,9 @@
int
dirdiff (int dir1, int dir2)
{
- int d;
-
- d = abs (dir1 - dir2);
- if (d > 4)
- d = 8 - d;
+ int d = abs (dir1 - dir2);
- return d;
+ return d > 4 ? 8 - d : d;
}
/* peterm:
@@ -2278,11 +2389,13 @@
int
can_pick (const object *who, const object *item)
{
- return /*QUERY_FLAG(who,FLAG_WIZ)|| */
- (item->weight > 0 && !QUERY_FLAG (item, FLAG_NO_PICK) &&
- !QUERY_FLAG (item, FLAG_ALIVE) && !item->invisible && (who->is_player () || item->weight < who->weight / 3));
+ return /*who->flag [FLAG_WIZ]|| */
+ (item->weight > 0 && !item->flag [FLAG_NO_PICK] &&
+ !item->flag [FLAG_ALIVE] && !item->invisible && (who->is_player () || item->weight < who->weight / 3));
}
+//-GPL
+
/*
* create clone from object to another
*/
@@ -2322,10 +2435,26 @@
return 0;
}
+/* Zero the key_values on op, decrementing the shared-string
+ * refcounts and freeing the links.
+ */
+void
+key_values::clear ()
+{
+ for (key_value *kvp = first; kvp; )
+ {
+ key_value *next = kvp->next;
+ delete kvp;
+ kvp = next;
+ }
+
+ first = 0;
+}
+
shstr_tmp
-object::kv_get (shstr_tmp key) const
+key_values::get (shstr_tmp key) const
{
- for (key_value *kv = key_values; kv; kv = kv->next)
+ for (key_value *kv = first; kv; kv = kv->next)
if (kv->key == key)
return kv->value;
@@ -2333,28 +2462,34 @@
}
void
-object::kv_set (shstr_tmp key, shstr_tmp value)
+key_values::add (shstr_tmp key, shstr_tmp value)
{
- for (key_value *kv = key_values; kv; kv = kv->next)
+ key_value *kv = new key_value;
+
+ kv->next = first;
+ kv->key = key;
+ kv->value = value;
+
+ first = kv;
+}
+
+void
+key_values::set (shstr_tmp key, shstr_tmp value)
+{
+ for (key_value *kv = first; kv; kv = kv->next)
if (kv->key == key)
{
kv->value = value;
return;
}
- key_value *kv = new key_value;
-
- kv->next = key_values;
- kv->key = key;
- kv->value = value;
-
- key_values = kv;
+ add (key, value);
}
void
-object::kv_del (shstr_tmp key)
+key_values::del (shstr_tmp key)
{
- for (key_value **kvp = &key_values; *kvp; kvp = &(*kvp)->next)
+ for (key_value **kvp = &first; *kvp; kvp = &(*kvp)->next)
if ((*kvp)->key == key)
{
key_value *kv = *kvp;
@@ -2364,6 +2499,34 @@
}
}
+void
+key_values::reverse ()
+{
+ key_value *prev = 0;
+ key_value *head = first;
+
+ while (head)
+ {
+ key_value *node = head;
+ head = head->next;
+ node->next = prev;
+ prev = node;
+ }
+
+ first = prev;
+}
+
+key_values &
+key_values::operator =(const key_values &kv)
+{
+ clear ();
+
+ for (key_value *kvp = kv.first; kvp; kvp = kvp->next)
+ add (kvp->key, kvp->value);
+
+ reverse ();
+}
+
object::depth_iterator::depth_iterator (object *container)
: iterator_base (container)
{
@@ -2455,14 +2618,7 @@
: region::default_region ();
}
-const materialtype_t *
-object::dominant_material () const
-{
- if (materialtype_t *mt = name_to_material (materialname))
- return mt;
-
- return name_to_material (shstr_unknown);
-}
+//+GPL
void
object::open_container (object *new_container)
@@ -2507,7 +2663,7 @@
// insert the "Close Container" object.
if (archetype *closer = new_container->other_arch)
{
- object *closer = arch_to_object (new_container->other_arch);
+ object *closer = new_container->other_arch->instance ();
closer->flag [FLAG_NO_MAP_SAVE] = 1;
new_container->insert (closer);
}
@@ -2530,6 +2686,38 @@
// contr->ns->floorbox_reset ();
}
+//-GPL
+
+// prefetch some flat area around the player
+static void
+prefetch_surrounding_area (object *op, maptile *map, int range)
+{
+ for (maprect *rect = map->split_to_tiles (mapwalk_buf,
+ op->x - range , op->y - range ,
+ op->x + range + 1, op->y + range + 1);
+ rect->m;
+ ++rect)
+ {
+ rect->m->touch ();
+ rect->m->activate ();
+ }
+}
+
+// prefetch a generous area around the player, also up and down
+void
+object::prefetch_surrounding_maps ()
+{
+ prefetch_surrounding_area (this, map, 40);
+
+ if (maptile *m = map->tile_available (TILE_DOWN))
+ prefetch_surrounding_area (this, m, 20);
+
+ if (maptile *m = map->tile_available (TILE_UP))
+ prefetch_surrounding_area (this, m, 20);
+}
+
+//+GPL
+
object *
object::force_find (shstr_tmp name)
{
@@ -2543,8 +2731,6 @@
return 0;
}
-//-GPL
-
void
object::force_set_timer (int duration)
{
@@ -2560,7 +2746,7 @@
if (object *force = force_find (name))
force->destroy ();
- object *force = get_archetype (FORCE_NAME);
+ object *force = archetype::get (FORCE_NAME);
force->slaying = name;
force->force_set_timer (duration);
@@ -2623,3 +2809,44 @@
}
}
+void object::change_move_type (MoveType mt)
+{
+ if (move_type == mt)
+ return;
+
+ if (is_on_map ())
+ {
+ // we are on the map, so handle move_on/off effects
+ remove ();
+ move_type = mt;
+ map->insert (this, x, y, this);
+ }
+ else
+ move_type = mt;
+}
+
+/* object should be a player.
+ * we return the object the player has marked with the 'mark' command
+ * below. If no match is found (or object has changed), we return
+ * NULL. We leave it up to the calling function to print messages if
+ * nothing is found.
+ */
+object *
+object::mark () const
+{
+ if (contr && contr->mark && contr->mark->env == this)
+ return contr->mark;
+ else
+ return 0;
+}
+
+// put marked object first in the inventory
+// this is used by identify-like spells so players can influence
+// the order a bit.
+void
+object::splay_marked ()
+{
+ if (object *marked = mark ())
+ splay (marked);
+}
+