--- deliantra/server/common/object.C 2007/06/13 21:09:39 1.161
+++ deliantra/server/common/object.C 2007/10/15 17:50:27 1.193
@@ -1,23 +1,22 @@
/*
- * This file is part of Crossfire TRT, the Multiplayer Online Role Playing Game.
+ * 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 2 of the License, or (at your option)
- * any later version.
+ * 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.
+ * 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 Crossfire TRT; if not, write to the Free Software Foundation, Inc. 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * 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
*/
@@ -257,13 +256,14 @@
*/
if (ob1->inv || ob2->inv)
{
- /* if one object has inventory but the other doesn't, not equiv */
- if ((ob1->inv && !ob2->inv) || (ob2->inv && !ob1->inv))
- return 0;
+ if (!(ob1->inv && ob2->inv))
+ return 0; /* inventories differ in length */
+
+ if (ob1->inv->below || ob2->inv->below)
+ return 0; /* more than one object in inv */
- /* Now check to see if the two inventory objects could merge */
if (!object::can_merge (ob1->inv, ob2->inv))
- return 0;
+ return 0; /* inventory objexts differ */
/* inventory ok - still need to check rest of this object to see
* if it is valid.
@@ -302,14 +302,23 @@
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 (SvRV (ob1->self)) : 0;
+ int k2 = ob2->self ? HvTOTALKEYS (SvRV (ob2->self)) : 0;
+
+ if (k1 != k2)
+ return 0;
+ else if (k1 == 0)
+ return 1;
+ else if (!cfperl_can_merge (ob1, ob2))
+ return 0;
+ }
}
/* Everything passes, must be OK. */
@@ -377,7 +386,6 @@
* multi-object 1 which is closest to the second object.
* If it's not a multi-object, it is returned.
*/
-
object *
get_nearest_part (object *op, const object *pl)
{
@@ -432,22 +440,15 @@
/*
* 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;
-
- /* 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;
+ // allow objects which own objects
+ if (owner)
+ while (owner->owner)
+ owner = owner->owner;
this->owner = owner;
}
@@ -589,7 +590,7 @@
*dst = *this;
if (speed < 0)
- dst->speed_left = speed_left - rndm ();
+ dst->speed_left -= rndm ();
dst->set_speed (dst->speed);
}
@@ -926,6 +927,7 @@
{
freed_map = new maptile;
+ freed_map->path = "";
freed_map->name = "/internal/freed_objects_map";
freed_map->width = 3;
freed_map->height = 3;
@@ -939,18 +941,19 @@
y = 1;
}
- head = 0;
-
if (more)
{
more->destroy ();
more = 0;
}
- // clear those pointers that likely might have circular references to us
- owner = 0;
- enemy = 0;
- attacked_by = 0;
+ head = 0;
+
+ // clear those pointers that likely might cause circular references
+ owner = 0;
+ enemy = 0;
+ attacked_by = 0;
+ current_weapon = 0;
}
void
@@ -962,6 +965,12 @@
if (destroy_inventory)
destroy_inv (false);
+ if (is_head ())
+ if (sound_destroy)
+ play_sound (sound_destroy);
+ else if (flag [FLAG_MONSTER])
+ play_sound (sound_find ("monster_destroy")); // quick hack, too lazy to create a generic mechanism
+
attachable::destroy ();
}
@@ -1085,28 +1094,24 @@
int check_walk_off = !flag [FLAG_NO_APPLY];
+ if (object *pl = ms.player ())
+ {
+ if (pl->container == this)
+ /* If a container that the player is currently using somehow gets
+ * removed (most likely destroyed), update the player view
+ * appropriately.
+ */
+ pl->close_container ();
+
+ pl->contr->ns->floorbox_update ();
+ }
+
for (tmp = ms.bot; tmp; tmp = tmp->above)
{
/* No point updating the players look faces if he is the object
* being removed.
*/
- if (tmp->type == PLAYER && tmp != this)
- {
- /* If a container that the player is currently using somehow gets
- * removed (most likely destroyed), update the player view
- * appropriately.
- */
- if (tmp->container == this)
- {
- flag [FLAG_APPLIED] = 0;
- tmp->container = 0;
- }
-
- if (tmp->contr->ns)
- tmp->contr->ns->floorbox_update ();
- }
-
/* See if object moving off should effect something */
if (check_walk_off
&& ((move_type & tmp->move_off)
@@ -1238,25 +1243,14 @@
op->remove ();
-#if 0
- if (!m->active != !op->active)
- if (m->active)
- op->activate_recursive ();
- else
- op->deactivate_recursive ();
-#endif
-
- if (out_of_map (m, op->x, op->y))
+ /* Ideally, the caller figures this out. However, it complicates a lot
+ * of areas of callers (eg, anything that uses find_free_spot would now
+ * need extra work
+ */
+ if (!xy_normalise (m, op->x, op->y))
{
- LOG (llevError, "Trying to insert object outside the map.\n%s\n", op->debug_desc ());
-#ifdef MANY_CORES
- /* Better to catch this here, as otherwise the next use of this object
- * is likely to cause a crash. Better to find out where it is getting
- * improperly inserted.
- */
- abort ();
-#endif
- return op;
+ op->destroy ();
+ return 0;
}
if (object *more = op->more)
@@ -1265,13 +1259,6 @@
CLEAR_FLAG (op, FLAG_REMOVED);
- /* Ideally, the caller figures this out. However, it complicates a lot
- * of areas of callers (eg, anything that uses find_free_spot would now
- * need extra work
- */
- if (!xy_normalise (m, op->x, op->y))
- return 0;
-
op->map = m;
mapspace &ms = op->ms ();
@@ -1315,7 +1302,7 @@
top = ms.bot;
/* If there are other objects, then */
- if ((!(flag & INS_MAP_LOAD)) && top)
+ if (top)
{
object *last = 0;
@@ -1378,9 +1365,6 @@
}
} /* If objects on this space */
- if (flag & INS_MAP_LOAD)
- top = ms.top;
-
if (flag & INS_ABOVE_FLOOR_ONLY)
top = floor;
@@ -1422,13 +1406,8 @@
op->map->dirty = true;
- /* If we have a floor, we know the player, if any, will be above
- * it, so save a few ticks and start from there.
- */
- if (!(flag & INS_MAP_LOAD))
- if (object *pl = ms.player ())
- if (pl->contr->ns)
- pl->contr->ns->floorbox_update ();
+ if (object *pl = ms.player ())
+ pl->contr->ns->floorbox_update ();
/* If this object glows, it may affect lighting conditions that are
* visible to others on this map. But update_all_los is really
@@ -1676,8 +1655,6 @@
object *
object::insert (object *op)
{
- object *tmp, *otmp;
-
if (!QUERY_FLAG (op, FLAG_REMOVED))
op->remove ();
@@ -1689,9 +1666,10 @@
CLEAR_FLAG (op, FLAG_OBJ_ORIGINAL);
CLEAR_FLAG (op, FLAG_REMOVED);
+
if (op->nrof)
{
- for (tmp = inv; tmp != NULL; tmp = tmp->below)
+ for (object *tmp = inv; tmp; tmp = tmp->below)
if (object::can_merge (tmp, op))
{
/* return the original object and remove inserted object
@@ -1720,19 +1698,19 @@
else
add_weight (this, (op->weight + op->carrying));
- otmp = this->in_player ();
- if (otmp && otmp->contr)
- if (!QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER))
+ if (object *otmp = this->in_player ())
+ if (otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER))
otmp->update_stats ();
- op->map = 0;
- op->env = this;
+ op->owner = 0; // its his/hers now. period.
+ op->map = 0;
+ op->env = this;
op->above = 0;
op->below = 0;
- op->x = 0, op->y = 0;
+ op->x = op->y = 0;
/* reset the light list and los of the players on the map */
- if ((op->glow_radius != 0) && map)
+ if (op->glow_radius && map)
{
#ifdef DEBUG_LIGHTS
LOG (llevDebug, " insert_ob_in_ob(): got %s to insert in map/op\n", op->name);
@@ -2022,14 +2000,30 @@
int
find_free_spot (const object *ob, maptile *m, int x, int y, int start, int stop)
{
- int index = 0, flag;
int altern[SIZEOFFREE];
+ int index = 0, flag;
for (int i = start; i < stop; i++)
{
- flag = ob_blocked (ob, m, x + freearr_x[i], y + freearr_y[i]);
- if (!flag)
- altern [index++] = i;
+ mapxy pos (m, x, y); pos.move (i);
+
+ if (!pos.normalise ())
+ continue;
+
+ mapspace &ms = *pos;
+
+ if (ms.flags () & P_IS_ALIVE)
+ continue;
+
+ /* However, often
+ * ob doesn't have any move type (when used to place exits)
+ * so the AND operation in OB_TYPE_MOVE_BLOCK doesn't work.
+ */
+ if (ob->move_type == 0 && ms.move_block != MOVE_ALL)
+ {
+ altern [index++] = i;
+ continue;
+ }
/* Basically, if we find a wall on a space, we cut down the search size.
* In this way, we won't return spaces that are on another side of a wall.
@@ -2039,8 +2033,19 @@
* to only the spaces immediately surrounding the target area, and
* won't look 2 spaces south of the target space.
*/
- else if ((flag & P_NO_PASS) && maxfree[i] < stop)
- stop = maxfree[i];
+ if (ms.move_block == MOVE_ALL && maxfree[i] < stop)
+ {
+ stop = maxfree[i];
+ continue;
+ }
+
+ /* Note it is intentional that we check ob - the movement type of the
+ * head of the object should correspond for the entire object.
+ */
+ if (OB_TYPE_MOVE_BLOCK (ob, ms.move_block))
+ continue;
+
+ altern [index++] = i;
}
if (!index)
@@ -2059,7 +2064,7 @@
find_first_free_spot (const object *ob, maptile *m, int x, int y)
{
for (int i = 0; i < SIZEOFFREE; i++)
- if (!ob_blocked (ob, m, x + freearr_x[i], y + freearr_y[i]))
+ if (!ob->blocked (m, x + freearr_x[i], y + freearr_y[i]))
return i;
return -1;
@@ -2346,7 +2351,6 @@
*
* Add a check so we can't pick up invisible objects (0.93.8)
*/
-
int
can_pick (const object *who, const object *item)
{
@@ -2571,7 +2575,6 @@
item = item->env;
}
-
const char *
object::flag_desc (char *desc, int len) const
{
@@ -2615,7 +2618,7 @@
title ? (const char *)title : "",
flag_desc (flagdesc, 512), type);
- if (env)
+ if (!this->flag[FLAG_REMOVED] && env)
p += snprintf (p, 256, "(in %s)", env->debug_desc (info2));
if (map)
@@ -2643,13 +2646,10 @@
const materialtype_t *
object::dominant_material () const
{
- if (materialtype_t *mat = name_to_material (materialname))
- return mat;
-
- // omfg this is slow, this has to be temporary :)
- shstr unknown ("unknown");
+ if (materialtype_t *mt = name_to_material (materialname))
+ return mt;
- return name_to_material (unknown);
+ return name_to_material (shstr_unknown);
}
void
@@ -2675,6 +2675,7 @@
esrv_update_item (UPD_FLAGS, this, old_container);
new_draw_info_format (NDI_UNIQUE, 0, this, "You close %s.", query_name (old_container));
+ play_sound (sound_find ("chest_close"));
}
if (new_container)
@@ -2700,7 +2701,57 @@
esrv_update_item (UPD_FLAGS, this, new_container);
esrv_send_inventory (this, new_container);
+ play_sound (sound_find ("chest_open"));
}
}
+object *
+object::force_find (const shstr name)
+{
+ /* cycle through his inventory to look for the MARK we want to
+ * place
+ */
+ for (object *tmp = inv; tmp; tmp = tmp->below)
+ if (tmp->type == FORCE && tmp->slaying == name)
+ return splay (tmp);
+
+ return 0;
+}
+
+void
+object::force_add (const shstr name, int duration)
+{
+ if (object *force = force_find (name))
+ force->destroy ();
+
+ object *force = get_archetype (FORCE_NAME);
+
+ force->slaying = name;
+ force->stats.food = 1;
+ force->speed_left = -1.f;
+
+ force->set_speed (duration ? 1.f / duration : 0.f);
+ force->flag [FLAG_IS_USED_UP] = true;
+ force->flag [FLAG_APPLIED] = true;
+
+ insert (force);
+}
+
+void
+object::play_sound (faceidx sound) const
+{
+ if (!sound)
+ return;
+
+ if (flag [FLAG_REMOVED])
+ return;
+
+ if (env)
+ {
+ if (object *pl = in_player ())
+ pl->contr->play_sound (sound);
+ }
+ else
+ map->play_sound (sound, x, y);
+}