--- deliantra/server/common/object.C 2010/04/23 09:22:46 1.334
+++ deliantra/server/common/object.C 2012/11/13 01:29:30 1.357
@@ -1,24 +1,24 @@
/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
- *
- * Copyright (©) 2005,2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
+ *
+ * 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
*/
@@ -73,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)
{
@@ -216,14 +235,14 @@
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. */
@@ -257,9 +276,10 @@
{
/* A couple quick sanity checks */
if (ob1 == ob2
- || ob1->type != ob2->type
- || 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;
@@ -355,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))
@@ -428,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)
- - weight_adjust_for (op, op->carrying);
-
- 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
@@ -471,21 +490,18 @@
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)//D
+ if (carrying != sum && carrying)//D
LOG (llevDebug, "updating carrying got %ld, expected %ld (%s)\n",
(long long)sum, (long long)carrying, debug_desc ());
@@ -586,23 +602,6 @@
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)
-{
- 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
@@ -616,36 +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;
- }
- }
- }
+ // maybe move to object_copy?
+ dst->kv = kv;
+ dst->flag [FLAG_REMOVED] = true;
dst->activate ();
}
@@ -655,7 +629,7 @@
if (!uuid.seq) // HACK
uuid = UUID::gen ();
- // TODO: unclean state changes, should nt be done in copy_to AND instantiate
+ // 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
@@ -766,7 +740,7 @@
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
@@ -797,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);
@@ -818,7 +792,7 @@
{
unlink ();
- free_key_values (this);
+ kv.clear ();
}
void object::link ()
@@ -927,7 +901,7 @@
*/
if (!drop_to_ground
|| !map
- || map->in_memory != MAP_ACTIVE
+ || !map->linkable ()
|| map->no_drop
|| ms ().move_block == MOVE_ALL)
{
@@ -1034,25 +1008,6 @@
++free_count;
}
-static struct freed_map : maptile
-{
- freed_map ()
- : maptile (3, 3)
- {
- path = "";
- name = "/internal/freed_objects_map";
- no_drop = 1;
- no_reset = 1;
-
- in_memory = MAP_ACTIVE;
- }
-
- ~freed_map ()
- {
- destroy ();
- }
-} freed_map; // freed objects are moved here to avoid crashes
-
void
object::do_destroy ()
{
@@ -1146,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 ();
@@ -1170,14 +1125,14 @@
{
if (expect_false (pl->contr->combat_ob == this))
{
- pl->apply (pl->contr->combat_ob, AP_UNAPPLY);
+ 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 (expect_false (pl->contr->ranged_ob == this))
{
- pl->apply (pl->contr->ranged_ob, AP_UNAPPLY);
+ 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);
}
@@ -1228,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 ())
@@ -1374,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
@@ -1631,9 +1583,9 @@
&& ms.volume () < m->max_volume))
return true;
- if (originator && originator->is_player ())
- originator->contr->failmsgf (
- "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 ()
);
@@ -1657,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
@@ -1746,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;
@@ -1778,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 */
@@ -1886,7 +1845,9 @@
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 (tmp->type == EXIT && (flags & INS_NO_AUTO_EXIT)) //TODO: temporary, fix exits instead
+ 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);
@@ -2050,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++)
{
@@ -2172,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)
@@ -2433,6 +2394,8 @@
!item->flag [FLAG_ALIVE] && !item->invisible && (who->is_player () || item->weight < who->weight / 3));
}
+//-GPL
+
/*
* create clone from object to another
*/
@@ -2472,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;
@@ -2483,28 +2462,34 @@
}
void
-object::kv_set (shstr_tmp key, shstr_tmp value)
+key_values::add (shstr_tmp key, shstr_tmp value)
+{
+ 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 = key_values; kv; kv = kv->next)
+ 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;
@@ -2514,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)
{
@@ -2605,6 +2618,8 @@
: region::default_region ();
}
+//+GPL
+
void
object::open_container (object *new_container)
{
@@ -2671,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)
{
@@ -2684,8 +2731,6 @@
return 0;
}
-//-GPL
-
void
object::force_set_timer (int duration)
{
@@ -2701,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);
@@ -2795,3 +2840,13 @@
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);
+}
+