--- deliantra/server/common/object.C 2008/09/29 10:32:50 1.259 +++ deliantra/server/common/object.C 2009/02/21 06:25:46 1.283 @@ -1,7 +1,7 @@ /* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * - * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team + * 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 * @@ -34,6 +34,7 @@ UUID UUID::cur; static uint64_t seq_next_save; static const uint64 UUID_GAP = 1<<19; +uint32_t mapspace::smellcount = 10000; objectvec objects; activevec actives; @@ -325,7 +326,7 @@ if (env) { // the player inventory itself is always visible - if (env->type == PLAYER) + if (env->is_player ()) return env; // else a player could have our env open @@ -760,7 +761,7 @@ // 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->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)) @@ -772,14 +773,14 @@ */ || ((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; + m.invalidate (); } /* 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) - m.flags_ = 0; + m.invalidate (); else if (action == UP_OBJ_FACE) /* Nothing to do for that case */ ; else @@ -964,7 +965,7 @@ object::do_destroy () { if (flag [FLAG_IS_LINKED]) - remove_button_link (this); + remove_link (); if (flag [FLAG_FRIENDLY]) remove_friendly_object (this); @@ -999,7 +1000,7 @@ } void -object::destroy (bool destroy_inventory) +object::destroy () { if (destroyed ()) return; @@ -1007,11 +1008,11 @@ if (!is_head () && !head->destroyed ()) { LOG (llevError | logBacktrace, "tried to destroy the tail of an object"); - head->destroy (destroy_inventory); + head->destroy (); return; } - destroy_inv (!destroy_inventory); + destroy_inv (false); if (is_head ()) if (sound_destroy) @@ -1058,6 +1059,8 @@ adjust_weight (env, -total_weight ()); + object *pl = in_player (); + /* we set up values so that it could be inserted into * the map, but we don't actually do that - it is up * to the caller to decide what we want to do. @@ -1078,8 +1081,14 @@ * made to players inventory. If set, avoiding the call * to save cpu time. */ - if ((otmp = in_player ()) && otmp->contr && !QUERY_FLAG (otmp, FLAG_NO_FIX_PLAYER)) - otmp->update_stats (); + if (pl) + if (pl->is_player () && (glow_radius || !QUERY_FLAG (pl, FLAG_NO_FIX_PLAYER))) + { + pl->update_stats (); + + if (glow_radius && pl->is_on_map ()) + update_all_los (pl->map, pl->x, pl->y); + } } else if (map) { @@ -1088,8 +1097,11 @@ if (object *pl = ms.player ()) { - if (type == PLAYER) // this == pl(!) + if (is_player ()) { + if (!flag [FLAG_WIZPASS]) + ms.smell = ++mapspace::smellcount; // remember the smell of the player + // leaving a spot always closes any open container on the ground if (container && !container->env) // this causes spurious floorbox updates, but it ensures @@ -1116,7 +1128,7 @@ above = 0; below = 0; - ms.flags_ = 0; + ms.invalidate (); if (map->in_memory == MAP_SAVING) return; @@ -1159,7 +1171,7 @@ last = tmp; } - if (flag [FLAG_BLOCKSVIEW] || glow_radius) + if (affects_los ()) update_all_los (map, x, y); } } @@ -1265,17 +1277,7 @@ object * insert_ob_in_map (object *op, maptile *m, object *originator, int flag) { - if (op->is_on_map ()) - { - LOG (llevError, "insert_ob_in_map called for object already on map"); - abort (); - } - - if (op->env) - { - LOG (llevError, "insert_ob_in_map called for object in an inventory (proceeding)"); - op->remove (); - } + op->remove (); if (m == &freed_map)//D TODO: remove soon {//D @@ -1286,7 +1288,8 @@ * of areas of callers (eg, anything that uses find_free_spot would now * need extra work */ - if (!xy_normalise (m, op->x, op->y)) + maptile *newmap = m; + if (!xy_normalise (newmap, op->x, op->y)) { op->head_ ()->destroy ();// remove head_ once all tail object destroyers found return 0; @@ -1296,9 +1299,10 @@ if (!insert_ob_in_map (more, m, originator, flag)) return 0; - CLEAR_FLAG (op, FLAG_REMOVED); + op->flag [FLAG_REMOVED] = false; + op->env = 0; + op->map = newmap; - op->map = m; mapspace &ms = op->ms (); /* this has to be done after we translate the coordinates. @@ -1329,8 +1333,11 @@ } if (!originator->is_on_map ()) - LOG (llevDebug | logBacktrace, "insert_ob_in_map(%s) called with INS_BELOW_ORIGINATOR when originator '%s' not on map", - op->debug_desc (), originator->debug_desc ()); + { + LOG (llevError, "insert_ob_in_map(%s) called with INS_BELOW_ORIGINATOR when originator '%s' not on map", + op->debug_desc (), originator->debug_desc ()); + abort (); + } op->above = originator; op->below = originator->below; @@ -1426,7 +1433,7 @@ } } - if (op->type == PLAYER) + if (op->is_player ()) { op->contr->do_los = 1; ++op->map->players; @@ -1451,8 +1458,11 @@ * or just updating the P_UPTODATE for spaces within this area * of effect may be sufficient. */ - if (op->map->darkness && (op->glow_radius != 0)) - update_all_los (op->map, op->x, op->y); + if (op->affects_los ()) + { + op->ms ().invalidate (); + update_all_los (op->map, op->x, op->y); + } /* updates flags (blocked, alive, no magic, etc) for this map space */ update_object (op, UP_OBJ_INSERT); @@ -1490,15 +1500,15 @@ * op is the object to insert it under: supplies x and the map. */ void -replace_insert_ob_in_map (const char *arch_string, object *op) +replace_insert_ob_in_map (shstr_tmp archname, object *op) { /* first search for itself and remove any old instances */ for (object *tmp = op->ms ().bot; tmp; tmp = tmp->above) - if (!strcmp (tmp->arch->archname, arch_string)) /* same archetype */ + if (tmp->arch->archname == archname) /* same archetype */ tmp->destroy (); - object *tmp = arch_to_object (archetype::find (arch_string)); + object *tmp = arch_to_object (archetype::find (archname)); tmp->x = op->x; tmp->y = op->y; @@ -1657,11 +1667,13 @@ inserted: /* reset the light list and los of the players on the map */ - if (op->glow_radius && map && map->darkness) - update_all_los (map, x, y); - - // if this is a player's inventory, update stats - if (type == PLAYER && !flag [FLAG_NO_FIX_PLAYER]) + if (op->glow_radius && is_on_map ()) + { + update_stats (); + update_all_los (map, x, y); + } + else if (is_player () && !flag [FLAG_NO_FIX_PLAYER]) + // if this is a player's inventory, update stats update_stats (); INVOKE_OBJECT (INSERT, this); @@ -1755,7 +1767,7 @@ float diff = tmp->move_slow_penalty * fabs (op->speed); - if (op->type == PLAYER) + if (op->is_player ()) if ((QUERY_FLAG (tmp, FLAG_IS_HILLY) && find_skill_by_number (op, SK_CLIMBING)) || (QUERY_FLAG (tmp, FLAG_IS_WOODED) && find_skill_by_number (op, SK_WOODSMAN))) diff /= 4.0; @@ -1973,7 +1985,7 @@ if (OB_TYPE_MOVE_BLOCK (ob, ms.move_block)) continue; - if (ob->blocked (m, pos.x, pos.y)) + if (ob->blocked (pos.m, pos.x, pos.y)) continue; altern [index++] = i; @@ -2051,13 +2063,8 @@ int find_dir (maptile *m, int x, int y, object *exclude) { - int i, max = SIZEOFFREE, mflags; - - sint16 nx, ny; - object *tmp; - maptile *mp; - - MoveType blocked, move_type; + int max = SIZEOFFREE, mflags; + MoveType move_type; if (exclude && exclude->head_ () != exclude) { @@ -2070,33 +2077,25 @@ move_type = MOVE_ALL; } - for (i = 1; i < max; i++) + for (int i = 1; i < max; i++) { - mp = m; - nx = x + freearr_x[i]; - ny = y + freearr_y[i]; - - mflags = get_map_flags (m, &mp, nx, ny, &nx, &ny); + mapxy pos (m, x, y); + pos.move (i); - if (mflags & P_OUT_OF_MAP) + if (!pos.normalise ()) max = maxfree[i]; else { - mapspace &ms = mp->at (nx, ny); + mapspace &ms = *pos; - blocked = ms.move_block; - - if ((move_type & blocked) == move_type) - max = maxfree[i]; - else if (mflags & P_IS_ALIVE) + if ((move_type & ms.move_block) == move_type) + max = maxfree [i]; + else if (ms.flags () & P_IS_ALIVE) { - for (tmp = ms.bot; tmp; tmp = tmp->above) - if ((tmp->flag [FLAG_MONSTER] || tmp->type == PLAYER) + for (object *tmp = ms.bot; tmp; tmp = tmp->above) + if ((tmp->flag [FLAG_MONSTER] || tmp->is_player ()) && (tmp != exclude || (tmp->head_ () != tmp && tmp->head_ () != exclude))) - break; - - if (tmp) - return freedir[i]; + return freedir [i]; } } } @@ -2181,7 +2180,7 @@ * Moved from spell_util.c to object.c with the other related direction * functions. */ -int reduction_dir[SIZEOFFREE][3] = { +const int reduction_dir[SIZEOFFREE][3] = { {0, 0, 0}, /* 0 */ {0, 0, 0}, /* 1 */ {0, 0, 0}, /* 2 */ @@ -2287,7 +2286,7 @@ { return /*QUERY_FLAG(who,FLAG_WIZ)|| */ (item->weight > 0 && !QUERY_FLAG (item, FLAG_NO_PICK) && - !QUERY_FLAG (item, FLAG_ALIVE) && !item->invisible && (who->type == PLAYER || item->weight < who->weight / 3)); + !QUERY_FLAG (item, FLAG_ALIVE) && !item->invisible && (who->is_player () || item->weight < who->weight / 3)); } /* @@ -2329,18 +2328,18 @@ return 0; } -const shstr & -object::kv_get (const shstr &key) const +shstr_tmp +object::kv_get (shstr_tmp key) const { for (key_value *kv = key_values; kv; kv = kv->next) if (kv->key == key) return kv->value; - return shstr_null; + return shstr (); } void -object::kv_set (const shstr &key, const shstr &value) +object::kv_set (shstr_tmp key, shstr_tmp value) { for (key_value *kv = key_values; kv; kv = kv->next) if (kv->key == key) @@ -2359,7 +2358,7 @@ } void -object::kv_del (const shstr &key) +object::kv_del (shstr_tmp key) { for (key_value **kvp = &key_values; *kvp; kvp = &(*kvp)->next) if ((*kvp)->key == key) @@ -2500,7 +2499,7 @@ // client needs item update to make it work, client bug requires this to be separate esrv_update_item (UPD_FLAGS, this, old_container); - new_draw_info_format (NDI_UNIQUE, 0, this, "You close %s.", query_name (old_container)); + new_draw_info_format (NDI_UNIQUE, 0, this, "You close %s.", old_container->query_name ()); play_sound (sound_find ("chest_close")); } @@ -2520,7 +2519,7 @@ } #endif - new_draw_info_format (NDI_UNIQUE, 0, this, "You open %s.", query_name (new_container)); + new_draw_info_format (NDI_UNIQUE, 0, this, "You open %s.", new_container->query_name ()); // make sure the container is available, client bug requires this to be separate esrv_send_item (this, new_container); @@ -2538,7 +2537,7 @@ } object * -object::force_find (const shstr name) +object::force_find (shstr_tmp name) { /* cycle through his inventory to look for the MARK we want to * place @@ -2550,8 +2549,8 @@ return 0; } -void -object::force_add (const shstr name, int duration) +object * +object::force_add (shstr_tmp name, int duration) { if (object *force = force_find (name)) force->destroy (); @@ -2566,24 +2565,60 @@ force->flag [FLAG_IS_USED_UP] = true; force->flag [FLAG_APPLIED] = true; - insert (force); + return insert (force); } void -object::play_sound (faceidx sound) +object::play_sound (faceidx sound) const { if (!sound) return; - if (flag [FLAG_REMOVED]) + if (is_on_map ()) + map->play_sound (sound, x, y); + else if (object *pl = in_player ()) + pl->contr->play_sound (sound); +} + +void +object::say_msg (const char *msg) const +{ + if (is_on_map ()) + map->say_msg (msg, x, y); + else if (object *pl = in_player ()) + pl->contr->play_sound (sound); +} + +void +object::make_noise () +{ + // we do not model noise in the map, so instead put + // a temporary light into the noise source + // could use the map instead, but that's less reliable for our + // goal, which is to make invisibility a bit harder to exploit + + // currently only works sensibly for players + if (!is_player ()) return; - if (env) + // find old force, or create new one + object *force = force_find (shstr_noise_force); + + if (force) + force->speed_left = -1.f; // patch old speed up + else { - if (object *pl = in_player ()) - pl->contr->play_sound (sound); + force = archetype::get (shstr_noise_force); + + force->slaying = shstr_noise_force; + force->stats.food = 1; + force->speed_left = -1.f; + + force->set_speed (1.f / 4.f); + force->flag [FLAG_IS_USED_UP] = true; + force->flag [FLAG_APPLIED] = true; + + insert (force); } - else - map->play_sound (sound, x, y); }