Atrinik Server 2.5
server/object.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00032 #include <skillist.h>
00033 #include <loader.h>
00034 
00038 struct mempool_chunk *removed_objects;
00039 
00041 object *active_objects;
00042 
00043 /* see walk_off/walk_on functions  */
00044 static int static_walk_semaphore = 0;
00045 
00047 object void_container;
00048 
00054 static const int reduction_dir[SIZEOFFREE][3] =
00055 {
00056     {0, 0, 0},
00057     {0, 0, 0},
00058     {0, 0, 0},
00059     {0, 0, 0},
00060     {0, 0, 0},
00061     {0, 0, 0},
00062     {0, 0, 0},
00063     {0, 0, 0},
00064     {0, 0, 0},
00065     {8, 1, 2},
00066     {1, 2, -1},
00067     {2, 10, 12},
00068     {2, 3, -1},
00069     {2, 3, 4},
00070     {3, 4, -1},
00071     {4, 14, 16},
00072     {5, 4, -1},
00073     {4, 5, 6},
00074     {6, 5, -1},
00075     {6, 20, 18},
00076     {7, 6, -1},
00077     {6, 7, 8},
00078     {7, 8, -1},
00079     {8, 22, 24},
00080     {8, 1, -1},
00081     {24, 9, 10},
00082     {9, 10, -1},
00083     {10, 11, -1},
00084     {27, 11, 29},
00085     {11, 12, -1},
00086     {12, 13, -1},
00087     {12, 13, 14},
00088     {13, 14, -1},
00089     {14, 15, -1},
00090     {33, 15, 35},
00091     {16, 15, -1},
00092     {17, 16, -1},
00093     {18, 17, 16},
00094     {18, 17, -1},
00095     {18, 19, -1},
00096     {41, 19, 39},
00097     {19, 20, -1},
00098     {20, 21, -1},
00099     {20, 21, 22},
00100     {21, 22, -1},
00101     {23, 22, -1},
00102     {45, 47, 23},
00103     {23, 24, -1},
00104     {24, 9, -1}
00105 };
00106 
00109 const char *gender_noun[GENDER_MAX] =
00110 {
00111     "neuter", "male", "female", "hermaphrodite"
00112 };
00115 const char *gender_subjective[GENDER_MAX] =
00116 {
00117     "it", "he", "she", "it"
00118 };
00121 const char *gender_subjective_upper[GENDER_MAX] =
00122 {
00123     "It", "He", "She", "It"
00124 };
00127 const char *gender_objective[GENDER_MAX] =
00128 {
00129     "it", "him", "her", "it"
00130 };
00133 const char *gender_possessive[GENDER_MAX] =
00134 {
00135     "its", "his", "her", "its"
00136 };
00139 const char *gender_reflexive[GENDER_MAX] =
00140 {
00141     "itself", "himself", "herself", "itself"
00142 };
00143 
00145 materialtype materials[NROFMATERIALS] =
00146 {
00147     {"paper"},
00148     {"metal"},
00149     {"crystal"},
00150     {"leather"},
00151     {"wood"},
00152     {"organics"},
00153     {"stone"},
00154     {"cloth"},
00155     {"magic material"},
00156     {"liquid"},
00157     {"soft metal"},
00158     {"bone"},
00159     {"ice"}
00160 };
00161 
00162 #define NUM_MATERIALS_REAL NROFMATERIALS * NROFMATERIALS_REAL + 1
00163 
00167 material_real_struct material_real[NUM_MATERIALS_REAL];
00168 
00169 static void remove_ob_inv(object *op);
00170 
00173 void init_materials()
00174 {
00175     int i;
00176     char filename[MAX_BUF], buf[MAX_BUF];
00177     FILE *fp;
00178 
00179     /* First initialize default values to the array */
00180     for (i = 0; i < NUM_MATERIALS_REAL; i++)
00181     {
00182         material_real[i].name[0] = '\0';
00183         material_real[i].quality = 100;
00184         material_real[i].type = M_NONE;
00185         material_real[i].def_race = RACE_TYPE_NONE;
00186     }
00187 
00188     snprintf(filename, sizeof(filename), "%s/materials", settings.datadir);
00189 
00190     if (!(fp = fopen(filename, "r")))
00191     {
00192         LOG(llevBug, "Could not open materials file: %s\n", filename);
00193     }
00194 
00195     while (fgets(buf, sizeof(buf), fp))
00196     {
00197         if (buf[0] == '#' || buf[0] == '\n')
00198         {
00199             continue;
00200         }
00201 
00202         if (sscanf(buf, "material_real %d\n", &i))
00203         {
00204             int def_race = RACE_TYPE_NONE, type = M_NONE, quality = 100;
00205             char name[MAX_BUF];
00206 
00207             if (i > NUM_MATERIALS_REAL)
00208             {
00209                 LOG(llevError, "Materials file contains declaration for material #%d but it doesn't exist.\n", i);
00210             }
00211 
00212             name[0] = '\0';
00213 
00214             while (fgets(buf, sizeof(buf), fp))
00215             {
00216                 if (strcmp(buf, "end\n") == 0)
00217                 {
00218                     break;
00219                 }
00220 
00221                 if (!sscanf(buf, "quality %d\n", &quality) && !sscanf(buf, "type %d\n", &type) && !sscanf(buf, "def_race %d\n", &def_race) && !sscanf(buf, "name %[^\n]", name))
00222                 {
00223                     LOG(llevError, "Bogus line in materials file: %s\n", buf);
00224                 }
00225             }
00226 
00227             if (name[0] != '\0')
00228             {
00229                 snprintf(material_real[i].name, sizeof(material_real[i].name), "%s ", name);
00230             }
00231 
00232             material_real[i].quality = quality;
00233             material_real[i].type = type;
00234             material_real[i].def_race = def_race;
00235         }
00236         else
00237         {
00238             LOG(llevError, "Bogus line in materials file: %s\n", buf);
00239         }
00240     }
00241 
00242     fclose(fp);
00243 }
00244 
00246 int freearr_x[SIZEOFFREE] =
00247 {
00248     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,
00249     0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3, -3, -3, -3, -3, -2, -1
00250 };
00251 
00253 int freearr_y[SIZEOFFREE] =
00254 {
00255     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,
00256     -3, -3, -3, -3, -2, -1, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3
00257 };
00258 
00260 int maxfree[SIZEOFFREE] =
00261 {
00262     0, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 27, 30, 31, 32, 33, 36, 37, 39, 39, 42, 43, 44, 45,
00263     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
00264 };
00265 
00267 int freedir[SIZEOFFREE] =
00268 {
00269     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,
00270     1, 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 7, 8, 8, 8, 8, 8
00271 };
00272 
00280 /* @cparser FLAG_(.*)
00281  * @page plugin_python_object_flags Python object flags
00282  * <h2>Python object flags</h2>
00283  * List of the object flags and their meaning. */
00284 const char *object_flag_names[NUM_FLAGS + 1] =
00285 {
00286     "sleep", "confused", NULL, "scared", "is_blind",
00287     "is_invisible", "is_ethereal", "is_good", "no_pick", "walk_on",
00288     "no_pass", "is_animated", "slow_move", "flying", "monster",
00289     "friendly", NULL, "been_applied", "auto_apply", "is_ready",
00290     "is_neutral", "see_invisible", "can_roll", "connect_reset", "is_turnable",
00291     "walk_off", "fly_on", "fly_off", "is_used_up", "identified",
00292     "reflecting", "changing", "splitting", "hitback", "startequip",
00293     "blocksview", "undead", "can_stack", "unaggressive", "reflect_missile",
00294     "reflect_spell", "no_magic", "no_fix_player", "is_evil", NULL,
00295     "run_away", "pass_thru", "can_pass_thru", "outdoor", "unique",
00296     "no_drop", "is_indestructible", "can_cast_spell", NULL, NULL,
00297     "can_use_bow", "can_use_armour", "can_use_weapon", "connect_no_push", "connect_no_release",
00298     "has_ready_bow", "xrays", NULL, "is_floor", "lifesave",
00299     "is_magical", "alive", "stand_still", "random_move", "only_attack",
00300     "wiz", "stealth", NULL, NULL, "cursed",
00301     "damned", "is_buildable", "no_pvp", NULL, NULL,
00302     "is_thrown", NULL, NULL, "is_male", "is_female",
00303     "applied", "inv_locked", NULL, NULL, NULL,
00304     "has_ready_weapon", "no_skill_ident", "was_wiz", "can_see_in_dark", "is_cauldron",
00305     "is_dust", NULL, "one_hit", "draw_double_always", "berserk",
00306     "no_attack", "invulnerable", "quest_item", "is_trapped", NULL,
00307     NULL, NULL, NULL, NULL, NULL,
00308     "sys_object", "use_fix_pos", "unpaid", "hidden", "make_invisible",
00309     "make_ethereal", "is_player", "is_named", NULL, "no_teleport",
00310     "corpse", "corpse_forced", "player_only", "no_cleric", "one_drop",
00311     "cursed_perm", "damned_perm", "door_closed", NULL, "is_missile",
00312     "draw_direction", "draw_double", "is_assassin", NULL, "no_save",
00313     NULL
00314 };
00315 /* @endcparser */
00316 
00323 void mark_object_removed(object *ob)
00324 {
00325     struct mempool_chunk *mem = MEM_POOLDATA(ob);
00326 
00327     if (OBJECT_FREE(ob))
00328     {
00329         LOG(llevBug, "mark_object_removed() called for free object\n");
00330     }
00331 
00332     SET_FLAG(ob, FLAG_REMOVED);
00333 
00334     /* Don't mark objects twice. */
00335     if (mem->next != NULL)
00336     {
00337         return;
00338     }
00339 
00340     mem->next = removed_objects;
00341     removed_objects = mem;
00342 }
00343 
00346 void object_gc()
00347 {
00348     struct mempool_chunk *current, *next;
00349     object *ob;
00350 
00351     while ((next = removed_objects) != &end_marker)
00352     {
00353         /* destroy_object() may free some more objects (inventory items) */
00354         removed_objects = &end_marker;
00355 
00356         while (next != &end_marker)
00357         {
00358             current = next;
00359             next = current->next;
00360             current->next = NULL;
00361 
00362             ob = (object *) MEM_USERDATA(current);
00363 
00364             if (QUERY_FLAG(ob, FLAG_REMOVED))
00365             {
00366                 if (OBJECT_FREE(ob))
00367                 {
00368                     LOG(llevBug, "Freed object in remove list: %s\n", STRING_OBJ_NAME(ob));
00369                 }
00370                 else
00371                 {
00372                     return_poolchunk(ob, pool_object);
00373                 }
00374             }
00375         }
00376     }
00377 }
00378 
00385 static int compare_ob_value_lists_one(const object *wants, const object *has)
00386 {
00387     key_value *wants_field;
00388 
00389     /* For each field in wants. */
00390     for (wants_field = wants->key_values; wants_field; wants_field = wants_field->next)
00391     {
00392         key_value *has_field = object_get_key_link(has, wants_field->key);
00393 
00394         if (has_field == NULL)
00395         {
00396             return 0;
00397         }
00398 
00399         /* Found the matching field. */
00400         if (has_field->value != wants_field->value)
00401         {
00402             return 0;
00403         }
00404     }
00405 
00406     return 1;
00407 }
00408 
00414 static int compare_ob_value_lists(const object *ob1, const object *ob2)
00415 {
00416     return compare_ob_value_lists_one(ob1, ob2) && compare_ob_value_lists_one(ob2, ob1);
00417 }
00418 
00438 int CAN_MERGE(object *ob1, object *ob2)
00439 {
00440     if (!QUERY_FLAG(ob1, FLAG_CAN_STACK))
00441     {
00442         return 0;
00443     }
00444 
00445     /* Do not merge objects if nrof would overflow. We use SINT32_MAX
00446      * because sint32 is often used to store nrof instead of uint32. */
00447     if (ob1->nrof + ob2->nrof > SINT32_MAX)
00448     {
00449         return 0;
00450     }
00451 
00452     /* just some quick hack */
00453     if (ob1->type == MONEY && ob1->type == ob2->type && ob1->arch == ob2->arch)
00454     {
00455         return 1;
00456     }
00457 
00458     /* Gecko: Moved out special handling of event object nrof */
00459     /* important: don't merge objects with glow_radius set - or we come
00460      * in heavy side effect situations. Because we really not know what
00461      * our calling function will do after this merge (and the calling function
00462      * then must first find out a merge has happen or not). The sense of stacks
00463      * are to store inactive items. Because glow_radius items can be active even
00464      * when not applied, merging is simply wrong here. MT. */
00465     if (((!ob1->nrof || !ob2->nrof) && ob1->type != EVENT_OBJECT) || ob1->glow_radius || ob2->glow_radius)
00466     {
00467         return 0;
00468     }
00469 
00470     /* just a brain dead long check for things NEVER NEVER should be different
00471      * this is true under all circumstances for all objects. */
00472     if (ob1->type != ob2->type || ob1 == ob2 || ob1->arch != ob2->arch || ob1->sub_type != ob2->sub_type || ob1->material != ob2->material || ob1->material_real != ob2->material_real || ob1->magic != ob2->magic || ob1->item_quality != ob2->item_quality || ob1->item_condition != ob2->item_condition || ob1->item_race != ob2->item_race || ob1->speed != ob2->speed || ob1->value !=ob2->value || ob1->weight != ob2->weight)
00473     {
00474         return 0;
00475     }
00476 
00477     /* Gecko: added bad special check for event objects
00478      * Idea is: if inv is identical events only then go ahead and merge)
00479      * This goes hand in hand with the event keeping addition in get_split_ob() */
00480     if (ob1->inv || ob2->inv)
00481     {
00482         object *tmp1, *tmp2;
00483 
00484         if (!ob1->inv || !ob2->inv)
00485         {
00486             return 0;
00487         }
00488 
00489         /* Check that all inv objects are event objects */
00490         for (tmp1 = ob1->inv, tmp2 = ob2->inv; tmp1 && tmp2; tmp1 = tmp1->below, tmp2 = tmp2->below)
00491         {
00492             if (tmp1->type != EVENT_OBJECT || tmp2->type != EVENT_OBJECT)
00493             {
00494                 return 0;
00495             }
00496         }
00497 
00498         /* Same number of events */
00499         if (tmp1 || tmp2)
00500         {
00501             return 0;
00502         }
00503 
00504         for (tmp1 = ob1->inv; tmp1; tmp1 = tmp1->below)
00505         {
00506             for (tmp2 = ob2->inv; tmp2; tmp2 = tmp2->below)
00507             {
00508                 if (CAN_MERGE(tmp1, tmp2))
00509                 {
00510                     break;
00511                 }
00512             }
00513 
00514             /* Couldn't find something to merge event from ob1 with? */
00515             if (!tmp2)
00516             {
00517                 return 0;
00518             }
00519         }
00520     }
00521 
00522     /* Check the refcount pointer */
00523     if (ob1->name != ob2->name || ob1->title != ob2->title || ob1->race != ob2->race || ob1->slaying != ob2->slaying || ob1->msg != ob2->msg || ob1->artifact != ob2->artifact)
00524     {
00525         return 0;
00526     }
00527 
00528     /* Compare the static arrays/structs */
00529     if ((memcmp(&ob1->stats, &ob2->stats, sizeof(living)) != 0) || (memcmp(&ob1->attack, &ob2->attack, sizeof(ob1->attack)) != 0) || (memcmp(&ob1->protection, &ob2->protection, sizeof(ob1->protection)) != 0))
00530     {
00531         return 0;
00532     }
00533 
00534     /* Ignore REMOVED and BEEN_APPLIED */
00535     if (ob1->randomitems != ob2->randomitems || ob1->other_arch != ob2->other_arch || (ob1->flags[0] | 0x300 | (1 << FLAG_IS_READY)) != (ob2->flags[0] | 0x300 | (1 << FLAG_IS_READY)) || ob1->flags[1] != ob2->flags[1] || ob1->flags[2] != ob2->flags[2] || ob1->flags[3] != ob2->flags[3] || ob1->path_attuned != ob2->path_attuned || ob1->path_repelled != ob2->path_repelled || ob1->path_denied != ob2->path_denied || ob1->terrain_type != ob2->terrain_type || ob1->terrain_flag != ob2->terrain_flag || ob1->weapon_speed != ob2->weapon_speed || ob1->magic != ob2->magic || ob1->item_level != ob2->item_level || ob1->item_skill != ob2->item_skill || ob1->glow_radius != ob2->glow_radius  || ob1->level != ob2->level || ob1->item_power != ob2->item_power)
00536     {
00537         return 0;
00538     }
00539 
00540     /* Face can be difficult - but inv_face should never be different or obj is different! */
00541     if (ob1->face != ob2->face || ob1->inv_face != ob2->inv_face || ob1->animation_id != ob2->animation_id || ob1->inv_animation_id != ob2->inv_animation_id)
00542     {
00543         return 0;
00544     }
00545 
00546     /* Avoid merging empty containers. */
00547     if (ob1->type == CONTAINER)
00548     {
00549         return 0;
00550     }
00551 
00552     /* At least one of these has key_values. */
00553     if (ob1->key_values != NULL || ob2->key_values != NULL)
00554     {
00555         /* One has fields, but the other one doesn't. */
00556         if ((ob1->key_values == NULL) != (ob2->key_values == NULL))
00557         {
00558             return 0;
00559         }
00560         else
00561         {
00562             return compare_ob_value_lists(ob1, ob2);
00563         }
00564     }
00565 
00566     /* Don't merge items with differing custom names. */
00567     if (ob1->custom_name != ob2->custom_name)
00568     {
00569         return 0;
00570     }
00571 
00572     /* Can merge! */
00573     return 1;
00574 }
00575 
00583 object *merge_ob(object *op, object *top)
00584 {
00585     if (!op->nrof)
00586     {
00587         return 0;
00588     }
00589 
00590     if (top == NULL)
00591     {
00592         for (top = op; top != NULL && top->above != NULL; top = top->above)
00593         {
00594         }
00595     }
00596 
00597     for (; top != NULL; top = top->below)
00598     {
00599         if (top == op)
00600         {
00601             continue;
00602         }
00603 
00604         if (CAN_MERGE(op, top))
00605         {
00606             top->nrof += op->nrof;
00607 
00608             /* Don't want any adjustments now */
00609             op->weight = 0;
00610 
00611             /* this is right: no check off */
00612             remove_ob(op);
00613 
00614             return top;
00615         }
00616     }
00617 
00618     return NULL;
00619 }
00620 
00628 signed long sum_weight(object *op)
00629 {
00630     sint32 sum;
00631     object *inv;
00632 
00633     if (QUERY_FLAG(op, FLAG_SYS_OBJECT))
00634     {
00635         return 0;
00636     }
00637 
00638     for (sum = 0, inv = op->inv; inv != NULL; inv = inv->below)
00639     {
00640         if (QUERY_FLAG(inv, FLAG_SYS_OBJECT))
00641         {
00642             continue;
00643         }
00644 
00645         if (inv->inv)
00646         {
00647             sum_weight(inv);
00648         }
00649 
00650         sum += WEIGHT_NROF(inv, inv->nrof);
00651     }
00652 
00653     if (op->type == CONTAINER && op->weapon_speed != 1.0f)
00654     {
00655         /* We'll store the calculated value in damage_round_tag, so
00656          * we can use that as 'cache' for unmodified carrying weight.
00657          * This allows us to reliably calculate the weight again in
00658          * add_weight() and sub_weight() without rounding errors. */
00659         op->damage_round_tag = sum;
00660         sum = (sint32) ((float) sum * op->weapon_speed);
00661     }
00662 
00663     op->carrying = sum;
00664     return sum;
00665 }
00666 
00672 void add_weight(object *op, sint32 weight)
00673 {
00674     while (op != NULL)
00675     {
00676         if (op->type == CONTAINER && op->weapon_speed != 1.0f)
00677         {
00678             sint32 old_carrying = op->carrying;
00679 
00680             op->damage_round_tag += weight;
00681             op->carrying = (sint32) ((float) op->damage_round_tag * op->weapon_speed);
00682             weight = op->carrying - old_carrying;
00683         }
00684         else
00685         {
00686             op->carrying += weight;
00687         }
00688 
00689         if (op->env && op->env->type == PLAYER)
00690         {
00691             esrv_update_item(UPD_WEIGHT, op->env, op);
00692         }
00693 
00694         op = op->env;
00695     }
00696 }
00697 
00703 void sub_weight(object *op, sint32 weight)
00704 {
00705     while (op != NULL)
00706     {
00707         if (op->type == CONTAINER && op->weapon_speed != 1.0f)
00708         {
00709             sint32 old_carrying = op->carrying;
00710 
00711             op->damage_round_tag -= weight;
00712             op->carrying = (sint32) ((float) op->damage_round_tag * op->weapon_speed);
00713             weight = old_carrying - op->carrying;
00714         }
00715         else
00716         {
00717             op->carrying -= weight;
00718         }
00719 
00720         if (op->env && op->env->type == PLAYER)
00721         {
00722             esrv_update_item(UPD_WEIGHT, op->env, op);
00723         }
00724 
00725         op = op->env;
00726     }
00727 }
00728 
00733 object *get_env_recursive(object *op)
00734 {
00735     while (op->env)
00736     {
00737         op = op->env;
00738     }
00739 
00740     return op;
00741 }
00742 
00747 object *is_player_inv(object *op)
00748 {
00749     for (; op != NULL && op->type != PLAYER; op = op->env)
00750     {
00751         if (op->env == op)
00752         {
00753             op->env = NULL;
00754         }
00755     }
00756 
00757     return op;
00758 }
00759 
00765 void dump_object(object *op, StringBuffer *sb)
00766 {
00767     if (op == NULL)
00768     {
00769         stringbuffer_append_string(sb, "[NULL pointer]");
00770         return;
00771     }
00772 
00773     if (op->arch != NULL)
00774     {
00775         stringbuffer_append_printf(sb, "arch %s\n", op->arch->name ? op->arch->name : "(null)");
00776         get_ob_diff(sb, op, &empty_archetype->clone);
00777         stringbuffer_append_string(sb, "end\n");
00778     }
00779     else
00780     {
00781         stringbuffer_append_string(sb, "Object ");
00782         stringbuffer_append_string(sb, op->name == NULL ? "(null)" : op->name);
00783         stringbuffer_append_string(sb, "\nend\n");
00784     }
00785 }
00786 
00791 void dump_object_rec(object *op, StringBuffer *sb)
00792 {
00793     archetype *at;
00794     object *tmp;
00795 
00796     if (!op)
00797     {
00798         return;
00799     }
00800 
00801     /* Get the difference from the object's archetype. */
00802     at = op->arch;
00803 
00804     /* No archetype, use empty archetype. */
00805     if (!at)
00806     {
00807         at = empty_archetype;
00808     }
00809 
00810     stringbuffer_append_printf(sb, "arch %s\n", at->name);
00811     get_ob_diff(sb, op, &at->clone);
00812 
00813     /* Recursively dump the inventory. */
00814     for (tmp = op->inv; tmp; tmp = tmp->below)
00815     {
00816         dump_object_rec(tmp, sb);
00817     }
00818 
00819     stringbuffer_append_string(sb, "end\n");
00820 }
00821 
00832 object *get_owner(object *op)
00833 {
00834     if (!op || op->owner == NULL)
00835     {
00836         return NULL;
00837     }
00838 
00839     if (!OBJECT_FREE(op) && op->owner->count == op->ownercount)
00840     {
00841         return op->owner;
00842     }
00843 
00844     op->owner = NULL;
00845 
00846     return NULL;
00847 }
00848 
00852 void clear_owner(object *op)
00853 {
00854     if (!op)
00855     {
00856         return;
00857     }
00858 
00859     op->owner = NULL;
00860     op->ownercount = 0;
00861 }
00862 
00871 static void set_owner_simple(object *op, object *owner)
00872 {
00873     /* next line added to allow objects which own objects */
00874     /* Add a check for ownercounts in here, as I got into an endless loop
00875      * with the fireball owning a poison cloud which then owned the
00876      * fireball.  I believe that was caused by one of the objects getting
00877      * freed and then another object replacing it.  Since the ownercounts
00878      * didn't match, this check is valid and I believe that cause is valid. */
00879     while (owner->owner && owner != owner->owner && owner->ownercount == owner->owner->count)
00880     {
00881         owner = owner->owner;
00882     }
00883 
00884     /* IF the owner still has an owner, we did not resolve to a final owner.
00885      * so lets not add to that. */
00886     if (owner->owner)
00887     {
00888         return;
00889     }
00890 
00891     op->owner = owner;
00892     op->ownercount = owner->count;
00893 }
00894 
00895 static void set_skill_pointers(object *op, object *chosen_skill, object *exp_obj)
00896 {
00897     op->chosen_skill = chosen_skill;
00898     op->exp_obj = exp_obj;
00899 }
00900 
00906 void set_owner(object *op, object *owner)
00907 {
00908     if (owner == NULL || op == NULL)
00909     {
00910         return;
00911     }
00912 
00913     /* Ensure we have a head. */
00914     owner = HEAD(owner);
00915     set_owner_simple(op, owner);
00916 
00917     if (owner->type == PLAYER && owner->chosen_skill)
00918     {
00919         set_skill_pointers(op, owner->chosen_skill, owner->chosen_skill->exp_obj);
00920     }
00921 }
00922 
00935 void copy_owner(object *op, object *clone)
00936 {
00937     object *owner = get_owner(clone);
00938 
00939     if (owner == NULL)
00940     {
00941         /* players don't have owners - they own themselves. Update
00942          * as appropriate. */
00943         if (clone->type == PLAYER)
00944         {
00945             owner = clone;
00946         }
00947         else
00948         {
00949             return;
00950         }
00951     }
00952 
00953     set_owner_simple(op, owner);
00954 
00955     if (clone->chosen_skill)
00956     {
00957         set_skill_pointers(op, clone->chosen_skill, clone->exp_obj);
00958     }
00959 }
00960 
00965 void initialize_object(object *op)
00966 {
00967     /* the memset will clear all these values for us, but we need
00968      * to reduce the refcount on them. */
00969     FREE_ONLY_HASH(op->name);
00970     FREE_ONLY_HASH(op->title);
00971     FREE_ONLY_HASH(op->race);
00972     FREE_ONLY_HASH(op->slaying);
00973     FREE_ONLY_HASH(op->msg);
00974     FREE_ONLY_HASH(op->artifact);
00975     FREE_ONLY_HASH(op->custom_name);
00976 
00977     /* Using this memset is a lot easier (and probably faster)
00978      * than explicitly clearing the fields. */
00979     memset(op, 0, sizeof(object));
00980 
00981     /* Set some values that should not be 0 by default */
00982     /* control the facings 25 animations */
00983     op->anim_enemy_dir = -1;
00984     /* the same for movement */
00985     op->anim_moving_dir = -1;
00986     op->anim_enemy_dir_last = -1;
00987     op->anim_moving_dir_last = -1;
00988     op->anim_last_facing = 4;
00989     op->anim_last_facing_last = -1;
00990 
00991     op->face = blank_face;
00992     op->attacked_by_count = -1;
00993 
00994     /* give the object a new (unique) count tag */
00995     op->count = ++ob_count;
00996 }
00997 
01005 void copy_object(object *op2, object *op, int no_speed)
01006 {
01007     int is_removed = QUERY_FLAG(op, FLAG_REMOVED);
01008 
01009     FREE_ONLY_HASH(op->name);
01010     FREE_ONLY_HASH(op->title);
01011     FREE_ONLY_HASH(op->race);
01012     FREE_ONLY_HASH(op->slaying);
01013     FREE_ONLY_HASH(op->msg);
01014     FREE_ONLY_HASH(op->artifact);
01015     FREE_ONLY_HASH(op->custom_name);
01016 
01017     free_key_values(op);
01018 
01019     memcpy((void *)((char *) op + offsetof(object, name)), (void *) ((char *) op2 + offsetof(object, name)), sizeof(object) - offsetof(object, name));
01020 
01021     if (is_removed)
01022     {
01023         SET_FLAG(op, FLAG_REMOVED);
01024     }
01025 
01026     ADD_REF_NOT_NULL_HASH(op->name);
01027     ADD_REF_NOT_NULL_HASH(op->title);
01028     ADD_REF_NOT_NULL_HASH(op->race);
01029     ADD_REF_NOT_NULL_HASH(op->slaying);
01030     ADD_REF_NOT_NULL_HASH(op->msg);
01031     ADD_REF_NOT_NULL_HASH(op->artifact);
01032     ADD_REF_NOT_NULL_HASH(op->custom_name);
01033 
01034     /* Only alter speed_left when we sure we have not done it before */
01035     if (!no_speed && op->speed < 0 && op->speed_left == op->arch->clone.speed_left)
01036     {
01037         op->speed_left += (RANDOM() % 90) / 100.0f;
01038     }
01039 
01040     /* Copy over key_values, if any. */
01041     if (op2->key_values)
01042     {
01043         key_value *tail = NULL, *i;
01044 
01045         op->key_values = NULL;
01046 
01047         for (i = op2->key_values; i; i = i->next)
01048         {
01049             key_value *new_link = malloc(sizeof(key_value));
01050 
01051             new_link->next = NULL;
01052             new_link->key = add_refcount(i->key);
01053 
01054             if (i->value)
01055             {
01056                 new_link->value = add_refcount(i->value);
01057             }
01058             else
01059             {
01060                 new_link->value = NULL;
01061             }
01062 
01063             /* Try and be clever here, too. */
01064             if (op->key_values == NULL)
01065             {
01066                 op->key_values = new_link;
01067                 tail = new_link;
01068             }
01069             else
01070             {
01071                 tail->next = new_link;
01072                 tail = new_link;
01073             }
01074         }
01075     }
01076 
01077     if (!no_speed)
01078     {
01079         update_ob_speed(op);
01080     }
01081 }
01082 
01087 void copy_object_with_inv(object *src_ob, object *dest_ob)
01088 {
01089     object *walk, *tmp;
01090 
01091     copy_object(src_ob, dest_ob, 0);
01092 
01093     for (walk = src_ob->inv; walk; walk = walk->below)
01094     {
01095         tmp = get_object();
01096         copy_object(walk, tmp, 0);
01097         insert_ob_in_ob(tmp, dest_ob);
01098     }
01099 }
01100 
01107 object *get_object()
01108 {
01109     object *new_obj = (object *) get_poolchunk(pool_object);
01110 
01111     mark_object_removed(new_obj);
01112 
01113     return new_obj;
01114 }
01115 
01122 void update_turn_face(object *op)
01123 {
01124     if (!QUERY_FLAG(op, FLAG_IS_TURNABLE) || op->arch == NULL)
01125     {
01126         return;
01127     }
01128 
01129     SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction);
01130     update_object(op, UP_OBJ_FACE);
01131 }
01132 
01138 void update_ob_speed(object *op)
01139 {
01140     /* No reason putting the archetypes objects on the speed list,
01141      * since they never really need to be updated. */
01142     if (OBJECT_FREE(op) && op->speed)
01143     {
01144         LOG(llevBug, "Object %s is freed but has speed.\n", op->name);
01145         op->speed = 0;
01146     }
01147 
01148     if (arch_init)
01149     {
01150         return;
01151     }
01152 
01153     /* These are special case objects - they have speed set, but should not be put
01154      * on the active list. */
01155     if (op->type == SPAWN_POINT_MOB)
01156     {
01157         return;
01158     }
01159 
01160     if (FABS(op->speed) > MIN_ACTIVE_SPEED)
01161     {
01162         /* If already on active list, don't do anything */
01163         if (op->active_next || op->active_prev || op == active_objects)
01164         {
01165             return;
01166         }
01167 
01168         /* process_events() expects us to insert the object at the beginning
01169          * of the list. */
01170         op->active_next = active_objects;
01171 
01172         if (op->active_next != NULL)
01173         {
01174             op->active_next->active_prev = op;
01175         }
01176 
01177         active_objects = op;
01178         op->active_prev = NULL;
01179     }
01180     else
01181     {
01182         /* If not on the active list, nothing needs to be done */
01183         if (!op->active_next && !op->active_prev && op != active_objects)
01184         {
01185             return;
01186         }
01187 
01188         if (op->active_prev == NULL)
01189         {
01190             active_objects = op->active_next;
01191 
01192             if (op->active_next != NULL)
01193             {
01194                 op->active_next->active_prev = NULL;
01195             }
01196         }
01197         else
01198         {
01199             op->active_prev->active_next = op->active_next;
01200 
01201             if (op->active_next)
01202             {
01203                 op->active_next->active_prev = op->active_prev;
01204             }
01205         }
01206 
01207         op->active_next = NULL;
01208         op->active_prev = NULL;
01209     }
01210 }
01211 
01224 void update_object(object *op, int action)
01225 {
01226     MapSpace *msp;
01227     int flags, newflags;
01228 
01229     if (op == NULL)
01230     {
01231         LOG(llevError, "update_object() called for NULL object.\n");
01232         return;
01233     }
01234 
01235     if (op->env != NULL || !op->map || op->map->in_memory == MAP_SAVING)
01236     {
01237         return;
01238     }
01239 
01240     /* No need to change anything except the map update counter */
01241     if (action == UP_OBJ_FACE)
01242     {
01243         INC_MAP_UPDATE_COUNTER(op->map, op->x, op->y);
01244         return;
01245     }
01246 
01247     msp = GET_MAP_SPACE_PTR(op->map, op->x, op->y);
01248     newflags = msp->flags;
01249     flags = newflags & ~P_NEED_UPDATE;
01250 
01251     /* Always resort layer - but not always flags */
01252     if (action == UP_OBJ_INSERT)
01253     {
01254         /* Force layer rebuild */
01255         newflags |= P_NEED_UPDATE;
01256         msp->update_tile++;
01257 
01258         /* Handle lighting system */
01259         if (op->glow_radius)
01260         {
01261             adjust_light_source(op->map, op->x, op->y, op->glow_radius);
01262         }
01263 
01264         /* This is handled a bit more complex, we must always loop the flags! */
01265         if (QUERY_FLAG(op, FLAG_NO_PASS) || QUERY_FLAG(op, FLAG_PASS_THRU))
01266         {
01267             newflags |= P_FLAGS_UPDATE;
01268         }
01269         /* Floors define our node - force an update */
01270         else if (QUERY_FLAG(op, FLAG_IS_FLOOR))
01271         {
01272             newflags |= P_FLAGS_UPDATE;
01273             msp->light_value += op->last_sp;
01274         }
01275         /* We don't have to use flag loop - we can set it by hand! */
01276         else
01277         {
01278             if (op->type == CHECK_INV)
01279             {
01280                 newflags |= P_CHECK_INV;
01281             }
01282             else if (op->type == MAGIC_EAR)
01283             {
01284                 newflags|= P_MAGIC_EAR;
01285             }
01286 
01287             if (QUERY_FLAG(op, FLAG_ALIVE))
01288             {
01289                 newflags |= P_IS_ALIVE;
01290             }
01291 
01292             if (QUERY_FLAG(op, FLAG_IS_PLAYER))
01293             {
01294                 newflags |= P_IS_PLAYER;
01295             }
01296 
01297             if (QUERY_FLAG(op, FLAG_PLAYER_ONLY))
01298             {
01299                 newflags |= P_PLAYER_ONLY;
01300             }
01301 
01302             if (QUERY_FLAG(op, FLAG_BLOCKSVIEW))
01303             {
01304                 newflags |= P_BLOCKSVIEW;
01305             }
01306 
01307             if (QUERY_FLAG(op, FLAG_NO_MAGIC))
01308             {
01309                 newflags |= P_NO_MAGIC;
01310             }
01311 
01312             if (QUERY_FLAG(op, FLAG_NO_CLERIC))
01313             {
01314                 newflags |= P_NO_CLERIC;
01315             }
01316 
01317             if (QUERY_FLAG(op, FLAG_WALK_ON))
01318             {
01319                 newflags |= P_WALK_ON;
01320             }
01321 
01322             if (QUERY_FLAG(op, FLAG_FLY_ON))
01323             {
01324                 newflags |= P_FLY_ON;
01325             }
01326 
01327             if (QUERY_FLAG(op, FLAG_WALK_OFF))
01328             {
01329                 newflags |= P_WALK_OFF;
01330             }
01331 
01332             if (QUERY_FLAG(op, FLAG_FLY_OFF))
01333             {
01334                 newflags |= P_FLY_OFF;
01335             }
01336 
01337             if (QUERY_FLAG(op, FLAG_DOOR_CLOSED))
01338             {
01339                 newflags |= P_DOOR_CLOSED;
01340             }
01341 
01342             if (QUERY_FLAG(op, FLAG_NO_PVP))
01343             {
01344                 newflags |= P_NO_PVP;
01345             }
01346 
01347             if (op->type == MAGIC_MIRROR)
01348             {
01349                 newflags |= P_MAGIC_MIRROR;
01350             }
01351 
01352             if (QUERY_FLAG(op, FLAG_OUTDOOR))
01353             {
01354                 newflags |= P_OUTDOOR;
01355             }
01356         }
01357     }
01358     else if (action == UP_OBJ_REMOVE)
01359     {
01360         /* Force layer rebuild */
01361         newflags |= P_NEED_UPDATE;
01362         msp->update_tile++;
01363 
01364         /* Handle lighting system */
01365         if (op->glow_radius)
01366         {
01367             adjust_light_source(op->map, op->x, op->y, -(op->glow_radius));
01368         }
01369 
01370         /* We must rebuild the flags when one of these flags is touched from our object */
01371         if (QUERY_FLAG(op, FLAG_ALIVE) || QUERY_FLAG(op, FLAG_IS_PLAYER) || QUERY_FLAG(op, FLAG_BLOCKSVIEW) || QUERY_FLAG(op, FLAG_DOOR_CLOSED) || QUERY_FLAG(op, FLAG_PASS_THRU) || QUERY_FLAG(op, FLAG_NO_PASS) || QUERY_FLAG(op, FLAG_PLAYER_ONLY) || QUERY_FLAG(op, FLAG_NO_MAGIC) || QUERY_FLAG(op, FLAG_NO_CLERIC) || QUERY_FLAG(op, FLAG_WALK_ON) || QUERY_FLAG(op, FLAG_FLY_ON) || QUERY_FLAG(op, FLAG_WALK_OFF) || QUERY_FLAG(op, FLAG_FLY_OFF) || QUERY_FLAG(op,  FLAG_IS_FLOOR) || op->type == CHECK_INV || op->type == MAGIC_EAR)
01372         {
01373             newflags |= P_FLAGS_UPDATE;
01374         }
01375     }
01376     else if (action == UP_OBJ_FLAGS)
01377     {
01378         /* Force flags rebuild but no tile counter */
01379         newflags |= P_FLAGS_UPDATE;
01380     }
01381     else if (action == UP_OBJ_FLAGFACE)
01382     {
01383         /* Force flags rebuild */
01384         newflags |= P_FLAGS_UPDATE;
01385         msp->update_tile++;
01386     }
01387     else if (action == UP_OBJ_LAYER)
01388     {
01389         /* Rebuild layers - most common when we change visibility of the object */
01390         newflags |= P_NEED_UPDATE;
01391         msp->update_tile++;
01392     }
01393     else if (action == UP_OBJ_ALL)
01394     {
01395         /* Force full tile update */
01396         newflags |= (P_FLAGS_UPDATE | P_NEED_UPDATE);
01397         msp->update_tile++;
01398     }
01399     else
01400     {
01401         LOG(llevError, "update_object called with invalid action: %d\n", action);
01402         return;
01403     }
01404 
01405     if (flags != newflags)
01406     {
01407         /* Rebuild flags */
01408         if (newflags & (P_FLAGS_UPDATE))
01409         {
01410             msp->flags |= (newflags | P_NO_ERROR | P_FLAGS_ONLY);
01411             update_position(op->map, op->x, op->y);
01412         }
01413         else
01414         {
01415             msp->flags |= newflags;
01416         }
01417     }
01418 
01419     if (op->more != NULL)
01420     {
01421         update_object(op->more, action);
01422     }
01423 }
01424 
01431 void drop_ob_inv(object *ob)
01432 {
01433     object *corpse = NULL, *enemy = NULL, *tmp_op = NULL, *tmp = NULL;
01434 
01435     /* We don't handle players here */
01436     if (ob->type == PLAYER)
01437     {
01438         LOG(llevBug, "drop_ob_inv() - tried to drop items of %s\n", ob->name);
01439         return;
01440     }
01441 
01442     if (ob->env == NULL && (ob->map == NULL || ob->map->in_memory != MAP_IN_MEMORY))
01443     {
01444         return;
01445     }
01446 
01447     if (ob->enemy && ob->enemy->type == PLAYER)
01448     {
01449         enemy = ob->enemy;
01450     }
01451     else
01452     {
01453         enemy = get_owner(ob->enemy);
01454     }
01455 
01456     /* Create race corpse and/or drop stuff to floor */
01457     if ((QUERY_FLAG(ob, FLAG_CORPSE) && !QUERY_FLAG(ob, FLAG_STARTEQUIP)) || QUERY_FLAG(ob, FLAG_CORPSE_FORCED))
01458     {
01459         ob_race *race_corpse = race_find(ob->race);
01460 
01461         if (race_corpse)
01462         {
01463             corpse = arch_to_object(race_corpse->corpse);
01464             corpse->x = ob->x;
01465             corpse->y = ob->y;
01466             corpse->map = ob->map;
01467             corpse->weight = ob->weight;
01468         }
01469     }
01470 
01471     tmp_op = ob->inv;
01472 
01473     while (tmp_op != NULL)
01474     {
01475         tmp = tmp_op->below;
01476         /* Inv-no check off / This will be destroyed in next loop of object_gc() */
01477         remove_ob(tmp_op);
01478 
01479         if (tmp_op->type == QUEST_CONTAINER)
01480         {
01481             /* Legal, non freed enemy */
01482             if (enemy && enemy->type == PLAYER && enemy->count == ob->enemy_count)
01483             {
01484                 check_quest(enemy, tmp_op);
01485             }
01486         }
01487         else if (!(QUERY_FLAG(ob, FLAG_STARTEQUIP) || (tmp_op->type != RUNE && (QUERY_FLAG(tmp_op, FLAG_SYS_OBJECT) || QUERY_FLAG(tmp_op, FLAG_STARTEQUIP) || QUERY_FLAG(tmp_op, FLAG_NO_DROP)))))
01488         {
01489             tmp_op->x = ob->x, tmp_op->y = ob->y;
01490 
01491             /* Always clear these in case the monster used the item */
01492             CLEAR_FLAG(tmp_op, FLAG_APPLIED);
01493             CLEAR_FLAG(tmp_op, FLAG_BEEN_APPLIED);
01494 
01495             /* If we have a corpse put the item in it */
01496             if (corpse)
01497             {
01498                 insert_ob_in_ob(tmp_op, corpse);
01499             }
01500             else
01501             {
01502                 /* don't drop traps from a container to the floor.
01503                  * removing the container where a trap is applied will
01504                  * neutralize the trap too
01505                  * Also not drop it in env - be safe here */
01506                 if (tmp_op->type != RUNE)
01507                 {
01508                     if (ob->env)
01509                     {
01510                         insert_ob_in_ob(tmp_op, ob->env);
01511 
01512                         /* This should handle in future insert_ob_in_ob() */
01513                         if (ob->env->type == PLAYER)
01514                         {
01515                             esrv_send_item(ob->env, tmp_op);
01516                         }
01517                         else if (ob->env->type == CONTAINER)
01518                         {
01519                             esrv_send_item(ob->env, tmp_op);
01520                         }
01521                     }
01522                     /* Insert in same map as the env */
01523                     else
01524                     {
01525                         insert_ob_in_map(tmp_op, ob->map, NULL, 0);
01526                     }
01527                 }
01528             }
01529         }
01530 
01531         tmp_op = tmp;
01532     }
01533 
01534     /* Drop the corpse */
01535     if (corpse || QUERY_FLAG(ob, FLAG_CORPSE_FORCED))
01536     {
01537         if (enemy && enemy->type == PLAYER)
01538         {
01539             if (enemy->count == ob->enemy_count)
01540             {
01541                 FREE_AND_ADD_REF_HASH(corpse->slaying, enemy->name);
01542             }
01543         }
01544         else if (QUERY_FLAG(ob, FLAG_CORPSE_FORCED))
01545         {
01546             corpse->stats.food = 5;
01547         }
01548 
01549         /* Change sub_type to mark this corpse */
01550         if (corpse->slaying)
01551         {
01552             if (CONTR(enemy)->party)
01553             {
01554                 FREE_AND_ADD_REF_HASH(corpse->slaying, CONTR(enemy)->party->name);
01555                 corpse->sub_type = ST1_CONTAINER_CORPSE_party;
01556             }
01557             else
01558             {
01559                 corpse->sub_type = ST1_CONTAINER_CORPSE_player;
01560             }
01561         }
01562 
01563         /* Store the original food value. */
01564         corpse->last_eat = corpse->stats.food;
01565 
01566         if (ob->env)
01567         {
01568             insert_ob_in_ob(corpse, ob->env);
01569 
01570             /* This should handle in future insert_ob_in_ob() */
01571             if (ob->env->type == PLAYER)
01572             {
01573                 esrv_send_item(ob->env, corpse);
01574             }
01575             else if (ob->env->type == CONTAINER)
01576             {
01577                 esrv_send_item(ob->env, corpse);
01578             }
01579         }
01580         else
01581         {
01582             insert_ob_in_map(corpse, ob->map, NULL, 0);
01583         }
01584     }
01585 }
01586 
01596 void destroy_object(object *ob)
01597 {
01598     if (OBJECT_FREE(ob))
01599     {
01600         StringBuffer *sb = stringbuffer_new();
01601         char *diff;
01602 
01603         dump_object(ob, sb);
01604         diff = stringbuffer_finish(sb);
01605         LOG(llevBug, "Trying to destroy freed object.\n%s\n", diff);
01606         free(diff);
01607         return;
01608     }
01609 
01610     if (!QUERY_FLAG(ob, FLAG_REMOVED))
01611     {
01612         StringBuffer *sb = stringbuffer_new();
01613         char *diff;
01614 
01615         dump_object(ob, sb);
01616         diff = stringbuffer_finish(sb);
01617         LOG(llevBug, "Destroy object called with non removed object\n:%s\n", diff);
01618         free(diff);
01619     }
01620 
01621     free_key_values(ob);
01622 
01623     /* This should be very rare... */
01624     if (QUERY_FLAG(ob, FLAG_IS_LINKED))
01625     {
01626         remove_button_link(ob);
01627     }
01628 
01629     if (ob->type == CONTAINER && ob->attacked_by)
01630     {
01631         container_unlink(NULL, ob);
01632     }
01633 
01634     /* Make sure to get rid of the inventory, too. It will be destroy()ed at the next gc */
01635     /* TODO: maybe destroy() it here too? */
01636     remove_ob_inv(ob);
01637 
01638     /* Remove object from the active list */
01639     ob->speed = 0;
01640     update_ob_speed(ob);
01641 
01642     /* Free attached attrsets */
01643     if (ob->custom_attrset)
01644     {
01645         switch (ob->type)
01646         {
01647             case PLAYER:
01648             /* Players are changed into DEAD_OBJECTs when they logout */
01649             case DEAD_OBJECT:
01650                 return_poolchunk(ob->custom_attrset, pool_player);
01651                 break;
01652 
01653             case MAGIC_MIRROR:
01654                 magic_mirror_deinit(ob);
01655                 break;
01656 
01657             default:
01658                 LOG(llevBug, "destroy_object() custom attrset found in unsupported object %s (type %d)\n", STRING_OBJ_NAME(ob), ob->type);
01659         }
01660 
01661         ob->custom_attrset = NULL;
01662     }
01663 
01664     if (ob->type == BEACON)
01665     {
01666         beacon_remove(ob);
01667     }
01668     else if (ob->type == MAP_EVENT_OBJ)
01669     {
01670         if (ob->map->in_memory == MAP_IN_MEMORY)
01671         {
01672             map_event_obj_deinit(ob);
01673         }
01674     }
01675 
01676     FREE_AND_CLEAR_HASH2(ob->name);
01677     FREE_AND_CLEAR_HASH2(ob->title);
01678     FREE_AND_CLEAR_HASH2(ob->race);
01679     FREE_AND_CLEAR_HASH2(ob->slaying);
01680     FREE_AND_CLEAR_HASH2(ob->msg);
01681     FREE_AND_CLEAR_HASH2(ob->artifact);
01682     FREE_AND_CLEAR_HASH2(ob->custom_name);
01683 
01684     /* Mark object as "do not use" and invalidate all references to it */
01685     ob->count = 0;
01686 }
01687 
01693 void destruct_ob(object *op)
01694 {
01695     if (op->inv)
01696     {
01697         drop_ob_inv(op);
01698     }
01699 
01700     remove_ob(op);
01701     check_walk_off(op, NULL, MOVE_APPLY_DEFAULT);
01702 }
01703 
01715 void remove_ob(object *op)
01716 {
01717     MapSpace *msp;
01718     object *otmp;
01719 
01720     if (QUERY_FLAG(op, FLAG_REMOVED))
01721     {
01722         /*dump_object(op);*/
01723         LOG(llevBug, "Trying to remove removed object.:%s map:%s (%d,%d)\n", query_name(op, NULL), op->map ? (op->map->path ? op->map->path : "op->map->path == NULL") : "op->map == NULL", op->x, op->y);
01724         return;
01725     }
01726 
01727     /* check off is handled outside here */
01728     if (op->more != NULL)
01729     {
01730         remove_ob(op->more);
01731     }
01732 
01733     mark_object_removed(op);
01734     SET_FLAG(op, FLAG_OBJECT_WAS_MOVED);
01735     op->quickslot = 0;
01736     CLEAR_FLAG(op, FLAG_IS_READY);
01737 
01738     /* In this case, the object to be removed is in someones
01739      * inventory. */
01740     if (op->env)
01741     {
01742         if (!QUERY_FLAG(op, FLAG_SYS_OBJECT))
01743         {
01744             sub_weight(op->env, WEIGHT_NROF(op, op->nrof));
01745         }
01746 
01747         /* NO_FIX_PLAYER is set when a great many changes are being
01748          * made to players inventory. If set, avoid the call to save CPU time. */
01749         if ((otmp = is_player_inv(op->env)) != NULL && CONTR(otmp) && !QUERY_FLAG(otmp, FLAG_NO_FIX_PLAYER))
01750         {
01751             fix_player(otmp);
01752         }
01753 
01754         if (op->above)
01755         {
01756             op->above->below = op->below;
01757         }
01758         else
01759         {
01760             op->env->inv = op->below;
01761         }
01762 
01763         if (op->below)
01764         {
01765             op->below->above = op->above;
01766         }
01767 
01768         /* We set up values so that it could be inserted into
01769          * the map, but we don't actually do that - it is up
01770          * to the caller to decide what we want to do. */
01771         op->x = op->env->x, op->y = op->env->y;
01772 
01773 #ifdef POSITION_DEBUG
01774         op->ox = op->x, op->oy = op->y;
01775 #endif
01776 
01777         op->map = op->env->map;
01778         op->above = NULL, op->below = NULL;
01779         op->env = NULL;
01780         return;
01781     }
01782 
01783     /* If we get here, we are removing it from a map */
01784     if (!op->map)
01785     {
01786         LOG(llevBug, "remove_ob(): object %s (%s) not on map or env.\n", query_short_name(op, NULL), op->arch ? (op->arch->name ? op->arch->name : "<nor arch name!>") : "<no arch!>");
01787         return;
01788     }
01789 
01790     /* If this is the base layer object, we assign the next object to be it if it is from same layer type */
01791     msp = GET_MAP_SPACE_PTR(op->map, op->x, op->y);
01792 
01793     if (op->layer)
01794     {
01795         if (GET_MAP_SPACE_LAYER(msp, op->layer - 1) == op)
01796         {
01797             /* Don't kick the inv objects of this layer to normal layer */
01798             if (op->above && op->above->layer == op->layer && GET_MAP_SPACE_LAYER(msp, op->layer + 6) != op->above)
01799             {
01800                 SET_MAP_SPACE_LAYER(msp, op->layer - 1, op->above);
01801             }
01802             else
01803             {
01804                 SET_MAP_SPACE_LAYER(msp, op->layer - 1, NULL);
01805             }
01806         }
01807         /* Inv layer? */
01808         else if (GET_MAP_SPACE_LAYER(msp, op->layer + 6) == op)
01809         {
01810             if (op->above && op->above->layer == op->layer)
01811             {
01812                 SET_MAP_SPACE_LAYER(msp, op->layer + 6, op->above);
01813             }
01814             else
01815             {
01816                 SET_MAP_SPACE_LAYER(msp, op->layer + 6, NULL);
01817             }
01818         }
01819     }
01820 
01821     /* Link the object above us */
01822     if (op->above)
01823     {
01824         op->above->below = op->below;
01825     }
01826     /* Assign below as last one */
01827     else
01828     {
01829         SET_MAP_SPACE_LAST(msp, op->below);
01830     }
01831 
01832     /* Relink the object below us, if there is one */
01833     if (op->below)
01834     {
01835         op->below->above = op->above;
01836     }
01837     /* First object goes on above it. */
01838     else
01839     {
01840         SET_MAP_SPACE_FIRST(msp, op->above);
01841     }
01842 
01843     op->above = NULL;
01844     op->below = NULL;
01845 
01846     /* This is triggered when a map is swapped out and the objects on it get removed too */
01847     if (op->map->in_memory == MAP_SAVING)
01848     {
01849         return;
01850     }
01851 
01852     /* We updated something here - mark this tile as changed! */
01853     msp->update_tile++;
01854 
01855     /* some player only stuff.
01856      * we adjust the ->player map variable and the local map player chain. */
01857     if (op->type == PLAYER)
01858     {
01859         struct pl_player *pltemp = CONTR(op);
01860 
01861         /* now we remove us from the local map player chain */
01862         if (pltemp->map_below)
01863         {
01864             CONTR(pltemp->map_below)->map_above = pltemp->map_above;
01865         }
01866         else
01867         {
01868             op->map->player_first = pltemp->map_above;
01869         }
01870 
01871         if (pltemp->map_above)
01872         {
01873             CONTR(pltemp->map_above)->map_below = pltemp->map_below;
01874         }
01875 
01876         pltemp->map_below = pltemp->map_above = NULL;
01877         pltemp->update_los = 1;
01878 
01879         /* An open container NOT in our player inventory = unlink (close) when we move */
01880         if (pltemp->container && pltemp->container->env != op)
01881         {
01882             container_unlink(pltemp, NULL);
01883         }
01884     }
01885 
01886     update_object(op, UP_OBJ_REMOVE);
01887     op->env = NULL;
01888 }
01889 
01893 static void remove_ob_inv(object *op)
01894 {
01895     object *tmp, *tmp2;
01896 
01897     for (tmp = op->inv; tmp; tmp = tmp2)
01898     {
01899         /* Save pointer, gets NULL in remove_ob */
01900         tmp2 = tmp->below;
01901 
01902         if (tmp->inv)
01903         {
01904             remove_ob_inv(tmp);
01905         }
01906 
01907         /* No map, no check off */
01908         remove_ob(tmp);
01909     }
01910 }
01911 
01920 object *insert_ob_in_map(object *op, mapstruct *m, object *originator, int flag)
01921 {
01922     object *tmp = NULL, *top;
01923     MapSpace *mc;
01924     int x, y, lt, layer, layer_inv;
01925 
01926     /* some tests to check all is ok... some CPU ticks
01927      * which tracks we have problems or not */
01928     if (OBJECT_FREE(op))
01929     {
01930         LOG(llevBug, "insert_ob_in_map(): Trying to insert freed object %s in map %s!\n", query_name(op, NULL), m->name);
01931         return NULL;
01932     }
01933 
01934     if (m == NULL)
01935     {
01936         LOG(llevBug, "insert_ob_in_map(): Trying to insert object %s in null-map!\n", query_name(op, NULL));
01937         return NULL;
01938     }
01939 
01940     if (!QUERY_FLAG(op, FLAG_REMOVED))
01941     {
01942         LOG(llevBug, "insert_ob_in_map(): Trying to insert non removed object %s in map %s.\n", query_name(op, NULL), m->name);
01943         return NULL;
01944     }
01945 
01946     /* tail, but no INS_TAIL_MARKER: we had messed something outside! */
01947     if (op->head && !(flag & INS_TAIL_MARKER))
01948     {
01949         LOG(llevBug, "insert_ob_in_map(): inserting op->more WITHOUT INS_TAIL_MARKER! OB:%s (ARCH: %s) (MAP: %s (%d,%d))\n", query_name(op, NULL), op->arch->name, m->path, op->x, op->y);
01950         return NULL;
01951     }
01952 
01953     if (op->more)
01954     {
01955         if (insert_ob_in_map(op->more, op->more->map, originator, flag | INS_TAIL_MARKER) == NULL)
01956         {
01957             if (!op->head)
01958             {
01959                 LOG(llevBug, "insert_ob_in_map(): inserting op->more killed op %s in map %s\n", query_name(op, NULL), m->name);
01960             }
01961 
01962             return NULL;
01963         }
01964     }
01965 
01966     CLEAR_FLAG(op, FLAG_REMOVED);
01967 
01968 #ifdef POSITION_DEBUG
01969     op->ox = op->x;
01970     op->oy = op->y;
01971 #endif
01972 
01973     /* this is now a key part of this function, because
01974      * we adjust multi arches here when they cross map boarders! */
01975     x = op->x;
01976     y = op->y;
01977     op->map = m;
01978 
01979     if (!(m = get_map_from_coord(m, &x, &y)))
01980     {
01981         LOG(llevBug, "insert_ob_in_map(): Trying to insert object %s outside the map %s (%d,%d).\n\n", query_name(op, NULL), op->map->path, op->x, op->y);
01982         return NULL;
01983     }
01984 
01985     /* x and y will only change when we change the map too - so check the map */
01986     if (op->map != m)
01987     {
01988         op->map = m;
01989         op->x = x;
01990         op->y = y;
01991     }
01992 
01993     if (op->nrof && !(flag & INS_NO_MERGE))
01994     {
01995         for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above)
01996         {
01997             if (CAN_MERGE(op,tmp))
01998             {
01999                 op->nrof += tmp->nrof;
02000                 remove_ob(tmp);
02001             }
02002         }
02003     }
02004 
02005     /* We need this for FLY/MOVE_ON/OFF */
02006     SET_FLAG(op, FLAG_OBJECT_WAS_MOVED);
02007     /* Nothing on the floor can be applied */
02008     CLEAR_FLAG(op, FLAG_APPLIED);
02009     /* Or locked */
02010     CLEAR_FLAG(op, FLAG_INV_LOCKED);
02011 
02012     /* Map layer system */
02013     mc = GET_MAP_SPACE_PTR(op->map, op->x, op->y);
02014 
02015     /* So we have a non system object */
02016     if (op->layer)
02017     {
02018         layer = op->layer - 1;
02019         layer_inv = layer + 7;
02020 
02021         /* Not invisible? */
02022         if (!QUERY_FLAG(op, FLAG_IS_INVISIBLE))
02023         {
02024             /* Do we have another object on this layer? */
02025             if ((top = GET_MAP_SPACE_LAYER(mc, layer)) == NULL && (top = GET_MAP_SPACE_LAYER(mc, layer_inv)) == NULL)
02026             {
02027                 /* No, we are the first on this layer - let's search something above us we can chain with. */
02028                 for (lt = op->layer; lt < NUM_LAYERS && (top = GET_MAP_SPACE_LAYER(mc, lt)) == NULL && (top = GET_MAP_SPACE_LAYER(mc, lt + 7)) == NULL; lt++)
02029                 {
02030                 }
02031             }
02032 
02033             SET_MAP_SPACE_LAYER(mc, layer, op);
02034 
02035             /* Easy - we chain our object before this one */
02036             if (top)
02037             {
02038                 if (top->below)
02039                 {
02040                     top->below->above = op;
02041                 }
02042                 /* If no object before, we are starting new object */
02043                 else
02044                 {
02045                     SET_MAP_SPACE_FIRST(mc, op);
02046                 }
02047 
02048                 op->below = top->below;
02049                 top->below = op;
02050                 op->above = top;
02051             }
02052             /* We are first object here or one is before us - chain to it */
02053             else
02054             {
02055                 if ((top = GET_MAP_SPACE_LAST(mc)) != NULL)
02056                 {
02057                     top->above = op;
02058                     op->below = top;
02059 
02060                 }
02061                 /* First object, set first and last object */
02062                 else
02063                 {
02064                     SET_MAP_SPACE_FIRST(mc, op);
02065                 }
02066 
02067                 SET_MAP_SPACE_LAST(mc, op);
02068             }
02069         }
02070         /* Invisible object */
02071         else
02072         {
02073             int tmp_flag;
02074 
02075             /* Check the layers */
02076             if ((top = GET_MAP_SPACE_LAYER(mc, layer_inv)) != NULL)
02077             {
02078                 tmp_flag = 1;
02079             }
02080             else if ((top = GET_MAP_SPACE_LAYER(mc, layer)) != NULL)
02081             {
02082                 /* In this case, we have 1 or more normal objects on this layer,
02083                  * so we must skip all of them. Easiest way is to get an upper layer
02084                  * valid object. */
02085                 for (lt = op->layer; lt < NUM_LAYERS && (tmp = GET_MAP_SPACE_LAYER(mc, lt)) == NULL && (tmp = GET_MAP_SPACE_LAYER(mc, lt + 7)) == NULL; lt++)
02086                 {
02087                 }
02088 
02089                 tmp_flag = 2;
02090             }
02091             else
02092             {
02093                 /* We are the first on this layer - let's search something above us we can chain with. */
02094                 for (lt = op->layer; lt < NUM_LAYERS && (top = GET_MAP_SPACE_LAYER(mc, lt)) == NULL && (top = GET_MAP_SPACE_LAYER(mc, lt + 7)) == NULL; lt++)
02095                 {
02096                 }
02097 
02098                 tmp_flag = 3;
02099             }
02100 
02101             /* In all cases, we are the new inv base layer */
02102             SET_MAP_SPACE_LAYER(mc, layer_inv, op);
02103 
02104             if (top)
02105             {
02106                 /* If top is set, this can be:
02107                  * - the inv layer of same layer id (and tmp_flag will be 1)
02108                  * - the normal layer of same layer id (tmp_flag == 2)
02109                  * - the inv or normal layer of an upper layer (tmp_flag == 3)
02110                  * If tmp_flag = 1, it's easy - we just get in front of top and use
02111                  * the same links.
02112                  * If tmp_flag = 2 AND tmp is set, tmp is the object we chain before.
02113                  * If tmp is NULL, we get ->last and chain after it.
02114                  * If tmp_flag = 3, we chain all times to top (before). */
02115                 if (tmp_flag == 2)
02116                 {
02117                     if (tmp)
02118                     {
02119                         /* We can't be first, there is always one before us */
02120                         tmp->below->above = op;
02121                         op->below = tmp->below;
02122                         /* And one after us... */
02123                         tmp->below = op;
02124                         op->above = tmp;
02125                     }
02126                     else
02127                     {
02128                         /* There is one before us, so this is always valid */
02129                         tmp = GET_MAP_SPACE_LAST(mc);
02130                         /* New last object */
02131                         SET_MAP_SPACE_LAST(mc, op);
02132                         op->below = tmp;
02133                         tmp->above = op;
02134                     }
02135                 }
02136                 /* tmp_flag 1 and tmp_flag 3 are done the same way */
02137                 else
02138                 {
02139                     if (top->below)
02140                     {
02141                         top->below->above = op;
02142                     }
02143                     /* If no object before, we are starting new object */
02144                     else
02145                     {
02146                         SET_MAP_SPACE_FIRST(mc, op);
02147                     }
02148 
02149                     op->below = top->below;
02150                     top->below = op;
02151                     op->above = top;
02152                 }
02153             }
02154             /* We are first object here or one is before us - chain to it */
02155             else
02156             {
02157                 /* There is something down */
02158                 if ((top = GET_MAP_SPACE_LAST(mc)) != NULL)
02159                 {
02160                     /* Just chain to it */
02161                     top->above = op;
02162                     op->below = top;
02163 
02164                 }
02165                 /* First object, set first and last object */
02166                 else
02167                 {
02168                     SET_MAP_SPACE_FIRST(mc, op);
02169                 }
02170 
02171                 SET_MAP_SPACE_LAST(mc, op);
02172             }
02173         }
02174     }
02175     /* op->layer == 0 - let's just put this object in front of all others */
02176     else
02177     {
02178         /* Is there something else? */
02179         if ((top = GET_MAP_SPACE_FIRST(mc)) != NULL)
02180         {
02181             /* Easy chaining */
02182             top->below = op;
02183             op->above = top;
02184         }
02185         /* No, we are the last object */
02186         else
02187         {
02188             SET_MAP_SPACE_LAST(mc, op);
02189         }
02190 
02191         SET_MAP_SPACE_FIRST(mc, op);
02192     }
02193 
02194     /* Set some specials for players We adjust the ->player map variable
02195      * and the local map player chain. */
02196     if (op->type == PLAYER)
02197     {
02198         CONTR(op)->socket.update_tile = 0;
02199         CONTR(op)->update_los = 1;
02200 
02201         if (op->map->player_first)
02202         {
02203             CONTR(op->map->player_first)->map_below = op;
02204             CONTR(op)->map_above = op->map->player_first;
02205         }
02206 
02207         op->map->player_first = op;
02208     }
02209 
02210     /* We updated something here - mark this tile as changed! */
02211     mc->update_tile++;
02212     /* Updates flags (blocked, alive, no magic, etc) for this map space */
02213     update_object(op, UP_OBJ_INSERT);
02214 
02215     if (!(flag & INS_NO_WALK_ON) && (mc->flags & (P_WALK_ON | P_FLY_ON) || op->more) && !op->head)
02216     {
02217         int event;
02218 
02219         /* We are flying but no fly event here */
02220         if (QUERY_FLAG(op, FLAG_FLY_ON))
02221         {
02222             if (!(mc->flags & P_FLY_ON))
02223             {
02224                 goto check_walk_loop;
02225             }
02226         }
02227         /* We are not flying - check walking only */
02228         else
02229         {
02230             if (!(mc->flags & P_WALK_ON))
02231             {
02232                 goto check_walk_loop;
02233             }
02234         }
02235 
02236         if ((event = check_walk_on(op, originator, MOVE_APPLY_MOVE)))
02237         {
02238             /* Don't return NULL - we are valid but we were moved */
02239             if (event == CHECK_WALK_MOVED)
02240             {
02241                 return op;
02242             }
02243             else
02244             {
02245                 return NULL;
02246             }
02247         }
02248 
02249         /* TODO: check event */
02250 check_walk_loop:
02251         for (tmp = op->more; tmp; tmp = tmp->more)
02252         {
02253             mc = GET_MAP_SPACE_PTR(tmp->map, tmp->x, tmp->y);
02254 
02255             /* We are flying but no fly event here */
02256             if (QUERY_FLAG(op, FLAG_FLY_ON))
02257             {
02258                 if (!(mc->flags & P_FLY_ON))
02259                 {
02260                     continue;
02261                 }
02262             }
02263             /* We are not flying - check walking only */
02264             else
02265             {
02266                 if (!(mc->flags & P_WALK_ON))
02267                 {
02268                     continue;
02269                 }
02270             }
02271 
02272             if ((event = check_walk_on(tmp, originator, MOVE_APPLY_MOVE)))
02273             {
02274                 /* Don't return NULL - we are valid but we were moved */
02275                 if (event == CHECK_WALK_MOVED)
02276                 {
02277                     return op;
02278                 }
02279                 else
02280                 {
02281                     return NULL;
02282                 }
02283             }
02284         }
02285     }
02286 
02287     return op;
02288 }
02289 
02295 void replace_insert_ob_in_map(char *arch_string, object *op)
02296 {
02297     object *tmp, *tmp1;
02298 
02299     /* First search for itself and remove any old instances */
02300     for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp; tmp = tmp->above)
02301     {
02302         if (!strcmp(tmp->arch->name, arch_string))
02303         {
02304             remove_ob(tmp);
02305             tmp->speed = 0;
02306             /* Remove it from active list */
02307             update_ob_speed(tmp);
02308         }
02309     }
02310 
02311     tmp1 = arch_to_object(find_archetype(arch_string));
02312     tmp1->x = op->x;
02313     tmp1->y = op->y;
02314     insert_ob_in_map(tmp1, op->map, op, 0);
02315 }
02316 
02327 object *get_split_ob(object *orig_ob, int nr, char *err, size_t size)
02328 {
02329     object *newob, *tmp, *event;
02330     int is_removed = (QUERY_FLAG(orig_ob, FLAG_REMOVED) != 0);
02331 
02332     if ((int) orig_ob->nrof < nr)
02333     {
02334         if (err)
02335         {
02336             snprintf(err, size, "There are only %d %ss.", orig_ob->nrof ? orig_ob->nrof : 1, query_name(orig_ob, NULL));
02337         }
02338 
02339         return NULL;
02340     }
02341 
02342     newob = get_object();
02343     copy_object(orig_ob, newob, 0);
02344 
02345     /* Copy inventory (event objects) */
02346     for (tmp = orig_ob->inv; tmp; tmp = tmp->below)
02347     {
02348         if (tmp->type == EVENT_OBJECT)
02349         {
02350             event = get_object();
02351             copy_object(tmp, event, 0);
02352             insert_ob_in_ob(event, newob);
02353         }
02354     }
02355 
02356     orig_ob->nrof -= nr;
02357 
02358     if (orig_ob->nrof < 1)
02359     {
02360         if (!is_removed)
02361         {
02362             remove_ob(orig_ob);
02363         }
02364 
02365         check_walk_off(orig_ob, NULL, MOVE_APPLY_VANISHED);
02366     }
02367     else if (!is_removed)
02368     {
02369         if (orig_ob->env && !QUERY_FLAG(orig_ob, FLAG_SYS_OBJECT))
02370         {
02371             sub_weight(orig_ob->env, orig_ob->weight * nr);
02372         }
02373 
02374         if (orig_ob->env == NULL && orig_ob->map->in_memory != MAP_IN_MEMORY)
02375         {
02376             strncpy(err, "Tried to split object whose map is not in memory.", size);
02377             LOG(llevDebug, "Error, Tried to split object whose map is not in memory.\n");
02378             return NULL;
02379         }
02380     }
02381 
02382     CLEAR_FLAG(newob, FLAG_IS_READY);
02383     newob->nrof = nr;
02384     return newob;
02385 }
02386 
02396 object *decrease_ob_nr(object *op, uint32 i)
02397 {
02398     object *tmp;
02399 
02400     /* Objects with op->nrof require this check */
02401     if (i == 0)
02402     {
02403         return op;
02404     }
02405 
02406     if (i > op->nrof)
02407     {
02408         i = op->nrof;
02409     }
02410 
02411     if (QUERY_FLAG(op, FLAG_REMOVED))
02412     {
02413         op->nrof -= i;
02414     }
02415     else if (op->env)
02416     {
02417         tmp = object_need_esrv_update(op);
02418 
02419         if (i < op->nrof)
02420         {
02421             op->nrof -= i;
02422 
02423             if (!QUERY_FLAG(op, FLAG_SYS_OBJECT))
02424             {
02425                 sub_weight(op->env, op->weight * i);
02426             }
02427 
02428             if (tmp)
02429             {
02430                 esrv_send_item(tmp, op);
02431             }
02432         }
02433         else
02434         {
02435             if (tmp)
02436             {
02437                 esrv_del_item(CONTR(tmp), op->count, op->env);
02438             }
02439 
02440             remove_ob(op);
02441             check_walk_off(op, NULL, MOVE_APPLY_VANISHED);
02442             op->nrof = 0;
02443         }
02444     }
02445     else
02446     {
02447         if (i < op->nrof)
02448         {
02449             op->nrof -= i;
02450         }
02451         else
02452         {
02453             remove_ob(op);
02454             check_walk_off(op, NULL, MOVE_APPLY_VANISHED);
02455             op->nrof = 0;
02456         }
02457     }
02458 
02459     if (op->nrof)
02460     {
02461         return op;
02462     }
02463 
02464     return NULL;
02465 }
02466 
02477 object *insert_ob_in_ob(object *op, object *where)
02478 {
02479     object *otmp;
02480 
02481     if (!QUERY_FLAG(op, FLAG_REMOVED))
02482     {
02483         StringBuffer *sb;
02484         char *diff;
02485 
02486         sb = stringbuffer_new();
02487         dump_object(op, sb);
02488         diff = stringbuffer_finish(sb);
02489         LOG(llevBug, "Trying to insert (ob) inserted object.\n%s\n", diff);
02490         free(diff);
02491         return op;
02492     }
02493 
02494     if (where == NULL)
02495     {
02496         StringBuffer *sb;
02497         char *diff;
02498 
02499         sb = stringbuffer_new();
02500         dump_object(op, sb);
02501         diff = stringbuffer_finish(sb);
02502         LOG(llevBug, "Trying to put object in NULL.\n%s\n", diff);
02503         free(diff);
02504         return op;
02505     }
02506 
02507     if (where->head)
02508     {
02509         LOG(llevBug, "Tried to insert object wrong part of multipart object.\n");
02510         where = where->head;
02511     }
02512 
02513     if (op->more)
02514     {
02515         LOG(llevError, "Tried to insert multipart object %s (%d)\n", query_name(op, NULL), op->count);
02516         return op;
02517     }
02518 
02519     CLEAR_FLAG(op, FLAG_REMOVED);
02520 
02521     if (!QUERY_FLAG(op, FLAG_SYS_OBJECT))
02522     {
02523         object *tmp;
02524         int ready;
02525 
02526         for (tmp = where->inv; tmp; tmp = tmp->below)
02527         {
02528             if (!QUERY_FLAG(tmp, FLAG_SYS_OBJECT) && CAN_MERGE(tmp, op))
02529             {
02530                 /* Return the original object and remove inserted object
02531                  * (client needs the original object) */
02532                 tmp->nrof += op->nrof;
02533 
02534                 /* Weight handling gets pretty funky. Since we are adding to
02535                  * tmp->nrof, we need to increase the weight. */
02536                 add_weight(where, WEIGHT_NROF(op, op->nrof));
02537 
02538                 /* Make sure we get rid of the old object */
02539                 SET_FLAG(op, FLAG_REMOVED);
02540 
02541                 op = tmp;
02542                 ready = QUERY_FLAG(op, FLAG_IS_READY);
02543                 CLEAR_FLAG(op, FLAG_IS_READY);
02544                 /* And fix old object's links (we will insert it further down)*/
02545                 remove_ob(op);
02546                 /* Just kidding about previous remove */
02547                 CLEAR_FLAG(op, FLAG_REMOVED);
02548 
02549                 if (ready)
02550                 {
02551                     SET_FLAG(op, FLAG_IS_READY);
02552                 }
02553 
02554                 break;
02555             }
02556         }
02557 
02558         /* I assume stackable objects have no inventory
02559          * We add the weight - this object could have just been removed
02560          * (if it was possible to merge).  calling remove_ob will subtract
02561          * the weight, so we need to add it in again, since we actually do
02562          * the linking below */
02563         add_weight(where, WEIGHT_NROF(op, op->nrof));
02564     }
02565 
02566     SET_FLAG(op, FLAG_OBJECT_WAS_MOVED);
02567     op->map = NULL;
02568     op->env = where;
02569     op->above = NULL;
02570     op->below = NULL;
02571     op->x = 0, op->y = 0;
02572 
02573 #ifdef POSITION_DEBUG
02574     op->ox = 0, op->oy = 0;
02575 #endif
02576 
02577     /* Client has no idea of ordering so let's not bother ordering it here.
02578      * It sure simplifies this function... */
02579     if (where->inv == NULL)
02580     {
02581         where->inv = op;
02582     }
02583     else
02584     {
02585         op->below = where->inv;
02586         op->below->above = op;
02587         where->inv = op;
02588     }
02589 
02590     /* Check for event object and set the owner object
02591      * event flags. */
02592     if (op->type == EVENT_OBJECT && op->sub_type)
02593     {
02594         where->event_flags |= (1U << (op->sub_type - 1));
02595     }
02596     else if (op->type == QUEST_CONTAINER && where->type == CONTAINER)
02597     {
02598         where->event_flags |= EVENT_FLAG(EVENT_QUEST);
02599     }
02600 
02601     /* If player, fix player if not marked as no fix. */
02602     otmp = is_player_inv(where);
02603 
02604     if (otmp && CONTR(otmp) != NULL)
02605     {
02606         if (!QUERY_FLAG(otmp, FLAG_NO_FIX_PLAYER))
02607         {
02608             fix_player(otmp);
02609         }
02610     }
02611 
02612     return op;
02613 }
02614 
02617 int check_walk_on(object *op, object *originator, int flags)
02618 {
02619     object *tmp;
02620     /* when TRUE, this function is root call for static_walk_semaphore setting */
02621     int local_walk_semaphore = 0;
02622     tag_t tag;
02623     int fly, slow_move;
02624 
02625     if (QUERY_FLAG(op, FLAG_NO_APPLY))
02626     {
02627         return 0;
02628     }
02629 
02630     fly = QUERY_FLAG(op, FLAG_FLYING);
02631     slow_move = IS_LIVE(op) && !QUERY_FLAG(op, FLAG_WIZPASS);
02632 
02633     if (fly)
02634     {
02635         flags |= MOVE_APPLY_FLY_ON;
02636     }
02637     else
02638     {
02639         flags |= MOVE_APPLY_WALK_ON;
02640     }
02641 
02642     tag = op->count;
02643 
02644     /* This flags ensures we notice when a moving event has appeared!
02645      * Because the functions who set/clear the flag can be called recursive
02646      * from this function and walk_off() we need a static, global semaphore
02647      * like flag to ensure we don't clear the flag except in the mother call. */
02648     if (!static_walk_semaphore)
02649     {
02650         local_walk_semaphore = 1;
02651         static_walk_semaphore = 1;
02652         CLEAR_FLAG(op, FLAG_OBJECT_WAS_MOVED);
02653     }
02654 
02655     for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp; tmp = tmp->above)
02656     {
02657         /* Can't apply yourself */
02658         if (tmp == op)
02659         {
02660             continue;
02661         }
02662 
02663         if (slow_move && QUERY_FLAG(tmp, FLAG_SLOW_MOVE))
02664         {
02665             op->speed_left -= SLOW_PENALTY(tmp) * FABS(op->speed);
02666         }
02667 
02668         if (fly ? QUERY_FLAG(tmp, FLAG_FLY_ON) : QUERY_FLAG(tmp, FLAG_WALK_ON))
02669         {
02670             move_apply(tmp, op, originator,flags);
02671 
02672             /* This means we got killed, removed or whatever! */
02673             if (was_destroyed(op, tag))
02674             {
02675                 if (local_walk_semaphore)
02676                 {
02677                     static_walk_semaphore = 0;
02678                 }
02679 
02680                 return CHECK_WALK_DESTROYED;
02681             }
02682 
02683             /* And here a remove_ob() or insert_xx() was triggered - we MUST stop now */
02684             if (QUERY_FLAG(op, FLAG_OBJECT_WAS_MOVED))
02685             {
02686                 if (local_walk_semaphore)
02687                 {
02688                     static_walk_semaphore = 0;
02689                 }
02690 
02691                 return CHECK_WALK_MOVED;
02692             }
02693         }
02694     }
02695 
02696     if (local_walk_semaphore)
02697     {
02698         static_walk_semaphore = 0;
02699     }
02700 
02701     return CHECK_WALK_OK;
02702 }
02703 
02706 int check_walk_off(object *op, object *originator, int flags)
02707 {
02708     MapSpace *mc;
02709     object *tmp, *part;
02710     /* when TRUE, this function is root call for static_walk_semaphore setting */
02711     int local_walk_semaphore = 0;
02712     int fly;
02713     tag_t tag;
02714 
02715     /* no map, no walk off - item can be in inventory and/or ... */
02716     if (!op || !op->map)
02717     {
02718         return CHECK_WALK_OK;
02719     }
02720 
02721     if (!QUERY_FLAG(op, FLAG_REMOVED))
02722     {
02723         LOG(llevBug, "check_walk_off: object %s is not removed when called\n", query_name(op, NULL));
02724         return CHECK_WALK_OK;
02725     }
02726 
02727     if (QUERY_FLAG(op, FLAG_NO_APPLY))
02728     {
02729         return CHECK_WALK_OK;
02730     }
02731 
02732     tag = op->count;
02733     fly = QUERY_FLAG(op, FLAG_FLYING);
02734 
02735     if (fly)
02736     {
02737         flags |= MOVE_APPLY_FLY_OFF;
02738     }
02739     else
02740     {
02741         flags |= MOVE_APPLY_WALK_OFF;
02742     }
02743 
02744     /* Check single and multi arches */
02745     for (part = op; part; part = part->more)
02746     {
02747         mc = GET_MAP_SPACE_PTR(part->map, part->x, part->y);
02748 
02749         /* No event on this tile */
02750         if (!(mc->flags & (P_WALK_OFF | P_FLY_OFF)))
02751         {
02752             continue;
02753         }
02754 
02755         /* This flags ensures we notice when a moving event has appeared!
02756          * Because the functions who set/clear the flag can be called recursive
02757          * from this function and walk_off() we need a static, global semaphore
02758          * like flag to ensure we don't clear the flag except in the mother call. */
02759         if (!static_walk_semaphore)
02760         {
02761             local_walk_semaphore = 1;
02762             static_walk_semaphore = 1;
02763             CLEAR_FLAG(part, FLAG_OBJECT_WAS_MOVED);
02764         }
02765 
02766         /* Ok, check objects here... */
02767         for (tmp = mc->first; tmp != NULL; tmp = tmp->above)
02768         {
02769             /* It's the ob part in this space... better not > 1 part in same space of same arch */
02770             if (tmp == part)
02771             {
02772                 continue;
02773             }
02774 
02775             /* Event */
02776             if (fly ? QUERY_FLAG(tmp, FLAG_FLY_OFF) : QUERY_FLAG(tmp, FLAG_WALK_OFF))
02777             {
02778                 move_apply(tmp, part, originator, flags);
02779 
02780                 if (OBJECT_FREE(part) || tag != op->count)
02781                 {
02782                     if (local_walk_semaphore)
02783                     {
02784                         static_walk_semaphore = 0;
02785                     }
02786 
02787                     return CHECK_WALK_DESTROYED;
02788                 }
02789 
02790                 /* And here insert_xx() was triggered - we MUST stop now */
02791                 if (!QUERY_FLAG(part, FLAG_REMOVED) || QUERY_FLAG(part, FLAG_OBJECT_WAS_MOVED))
02792                 {
02793                     if (local_walk_semaphore)
02794                     {
02795                         static_walk_semaphore = 0;
02796                     }
02797 
02798                     return CHECK_WALK_MOVED;
02799                 }
02800             }
02801         }
02802 
02803         if (local_walk_semaphore)
02804         {
02805             local_walk_semaphore = 0;
02806             static_walk_semaphore = 0;
02807         }
02808 
02809     }
02810 
02811     if (local_walk_semaphore)
02812     {
02813         static_walk_semaphore = 0;
02814     }
02815 
02816     return CHECK_WALK_OK;
02817 }
02818 
02827 object *present_arch(archetype *at, mapstruct *m, int x, int y)
02828 {
02829     object *tmp;
02830 
02831     if (!(m = get_map_from_coord(m, &x, &y)))
02832     {
02833         LOG(llevError, "present_arch() called outside map.\n");
02834         return NULL;
02835     }
02836 
02837     for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above)
02838     {
02839         if (tmp->arch == at)
02840         {
02841             return tmp;
02842         }
02843     }
02844 
02845     return NULL;
02846 }
02847 
02856 object *present(uint8 type, mapstruct *m, int x, int y)
02857 {
02858     object *tmp;
02859 
02860     if (!(m = get_map_from_coord(m, &x, &y)))
02861     {
02862         LOG(llevError, "Present called outside map.\n");
02863         return NULL;
02864     }
02865 
02866     for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above)
02867     {
02868         if (tmp->type == type)
02869         {
02870             return tmp;
02871         }
02872     }
02873 
02874     return NULL;
02875 }
02876 
02883 object *present_in_ob(uint8 type, object *op)
02884 {
02885     object *tmp;
02886 
02887     for (tmp = op->inv; tmp; tmp = tmp->below)
02888     {
02889         if (tmp->type == type)
02890         {
02891             return tmp;
02892         }
02893     }
02894 
02895     return NULL;
02896 }
02897 
02904 object *present_arch_in_ob(archetype *at, object *op)
02905 {
02906     object *tmp;
02907 
02908     for (tmp = op->inv; tmp; tmp = tmp->below)
02909     {
02910         if (tmp->arch == at)
02911         {
02912             return tmp;
02913         }
02914     }
02915 
02916     return NULL;
02917 }
02918 
02933 int find_free_spot(archetype *at, object *op, mapstruct *m, int x, int y, int start, int stop)
02934 {
02935     int i, inx = 0;
02936     static int altern[SIZEOFFREE];
02937 
02938     for (i = start; i < stop; i++)
02939     {
02940         if (!arch_blocked(at, op, m, x + freearr_x[i], y + freearr_y[i]))
02941         {
02942             altern[inx++] = i;
02943         }
02944         else if (wall(m, x + freearr_x[i], y + freearr_y[i]) && maxfree[i] < stop)
02945         {
02946             stop = maxfree[i];
02947         }
02948     }
02949 
02950     if (!inx)
02951     {
02952         return -1;
02953     }
02954 
02955     return altern[rndm(1, inx) - 1];
02956 }
02957 
02963 int find_first_free_spot(archetype *at, object *op, mapstruct *m, int x, int y)
02964 {
02965     int i;
02966 
02967     for (i = 0; i < SIZEOFFREE; i++)
02968     {
02969         if (!arch_blocked(at, op, m, x + freearr_x[i], y + freearr_y[i]))
02970         {
02971             return i;
02972         }
02973     }
02974 
02975     return -1;
02976 }
02977 
02980 int find_first_free_spot2(archetype *at, mapstruct *m, int x, int y, int start, int range)
02981 {
02982     int i;
02983 
02984     for (i = start; i < range; i++)
02985     {
02986         if (!arch_blocked(at, NULL, m, x + freearr_x[i], y + freearr_y[i]))
02987         {
02988             return i;
02989         }
02990     }
02991 
02992     return -1;
02993 }
02994 
03000 void permute(int *arr, int begin, int end)
03001 {
03002     int i, j, tmp, len;
03003 
03004     len = end - begin;
03005 
03006     for (i = begin; i < end; i++)
03007     {
03008         j = begin + rndm(1, len) - 1;
03009 
03010         tmp = arr[i];
03011         arr[i] = arr[j];
03012         arr[j] = tmp;
03013     }
03014 }
03015 
03025 void get_search_arr(int *search_arr)
03026 {
03027     int i;
03028 
03029     for (i = 0; i < SIZEOFFREE; i++)
03030     {
03031         search_arr[i] = i;
03032     }
03033 
03034     permute(search_arr, 1, SIZEOFFREE1 + 1);
03035     permute(search_arr, SIZEOFFREE1 + 1, SIZEOFFREE2 + 1);
03036     permute(search_arr, SIZEOFFREE2 + 1, SIZEOFFREE);
03037 }
03038 
03044 int find_dir_2(int x, int y)
03045 {
03046     int q;
03047 
03048     if (!y)
03049     {
03050         q = -300 * x;
03051     }
03052     else
03053     {
03054         q = x * 100 / y;
03055     }
03056 
03057     if (y > 0)
03058     {
03059         if (q < -242)
03060         {
03061             return 3;
03062         }
03063 
03064         if (q < -41)
03065         {
03066             return 2;
03067         }
03068 
03069         if (q < 41)
03070         {
03071             return 1;
03072         }
03073 
03074         if (q < 242)
03075         {
03076             return 8;
03077         }
03078 
03079         return 7;
03080     }
03081 
03082     if (q < -242)
03083     {
03084         return 7;
03085     }
03086 
03087     if (q < -41)
03088     {
03089         return 6;
03090     }
03091 
03092     if (q < 41)
03093     {
03094         return 5;
03095     }
03096 
03097     if (q < 242)
03098     {
03099         return 4;
03100     }
03101 
03102     return 3;
03103 }
03104 
03111 int absdir(int d)
03112 {
03113     while (d < 1)
03114     {
03115         d += 8;
03116     }
03117 
03118     while (d > 8)
03119     {
03120         d -= 8;
03121     }
03122 
03123     return d;
03124 }
03125 
03132 int dirdiff(int dir1, int dir2)
03133 {
03134     int d = abs(dir1 - dir2);
03135 
03136     if (d > 4)
03137     {
03138         d = 8 - d;
03139     }
03140 
03141     return d;
03142 }
03143 
03153 int get_dir_to_target(object *op, object *target, rv_vector *range_vector)
03154 {
03155     int dir;
03156 
03157     if (!get_rangevector(op, target, range_vector, 0))
03158     {
03159         return 0;
03160     }
03161 
03162     dir = range_vector->direction;
03163 
03164     if (op->type == PLAYER)
03165     {
03166         if (op->head)
03167         {
03168             op->head->anim_enemy_dir = dir;
03169             op->head->facing = dir;
03170         }
03171         else
03172         {
03173             op->anim_enemy_dir = dir;
03174             op->facing = dir;
03175         }
03176     }
03177 
03178     return dir;
03179 }
03180 
03187 int can_pick(object *who, object *item)
03188 {
03189     if (item->weight <= 0)
03190     {
03191         return 0;
03192     }
03193 
03194     if (QUERY_FLAG(item, FLAG_NO_PICK) && !QUERY_FLAG(item, FLAG_UNPAID))
03195     {
03196         return 0;
03197     }
03198 
03199     if (QUERY_FLAG(item, FLAG_ALIVE))
03200     {
03201         return 0;
03202     }
03203 
03204     if (IS_INVISIBLE(item, who) && !QUERY_FLAG(who, FLAG_SEE_INVISIBLE))
03205     {
03206         return 0;
03207     }
03208 
03209     /* Weight limit for monsters */
03210     if (who->type != PLAYER && item->weight > (who->weight / 3))
03211     {
03212         return 0;
03213     }
03214 
03215     /* Can not pick up multipart objects */
03216     if (item->head || item->more)
03217     {
03218         return 0;
03219     }
03220 
03221     return 1;
03222 }
03223 
03228 object *object_create_clone(object *asrc)
03229 {
03230     object *dst = NULL, *tmp, *src, *part, *prev, *item;
03231 
03232     if (!asrc)
03233     {
03234         return NULL;
03235     }
03236 
03237     src = asrc;
03238 
03239     if (src->head)
03240     {
03241         src = src->head;
03242     }
03243 
03244     prev = NULL;
03245 
03246     for (part = src; part; part = part->more)
03247     {
03248         tmp = get_object();
03249         copy_object(part, tmp, 0);
03250         tmp->x -= src->x;
03251         tmp->y -= src->y;
03252 
03253         if (!part->head)
03254         {
03255             dst = tmp;
03256             tmp->head = NULL;
03257         }
03258         else
03259         {
03260             tmp->head = dst;
03261         }
03262 
03263         tmp->more = NULL;
03264 
03265         if (prev)
03266         {
03267             prev->more = tmp;
03268         }
03269 
03270         prev = tmp;
03271     }
03272 
03273     /* Copy inventory */
03274     for (item = src->inv; item; item = item->below)
03275     {
03276         insert_ob_in_ob(object_create_clone(item), dst);
03277     }
03278 
03279     return dst;
03280 }
03281 
03288 int was_destroyed(object *op, tag_t old_tag)
03289 {
03290     return (QUERY_FLAG(op, FLAG_REMOVED) || (op->count != old_tag) || OBJECT_FREE(op));
03291 }
03292 
03297 object *load_object_str(char *obstr)
03298 {
03299     object *ob = get_object();
03300 
03301     if (!load_object(obstr, ob, NULL, LO_MEMORYMODE, 0))
03302     {
03303         LOG(llevBug, "load_object_str(): load_object() failed.");
03304         return NULL;
03305     }
03306 
03307     sum_weight(ob);
03308 
03309     return ob;
03310 }
03311 
03319 int auto_apply(object *op)
03320 {
03321     object *tmp = NULL, *tmp2;
03322     int i, level, a_chance;
03323 
03324     /* Because auto_apply will be done only *one* time when a new, base
03325      * map is loaded, we always clear the flag now. */
03326     CLEAR_FLAG(op, FLAG_AUTO_APPLY);
03327 
03328     if (op->env && op->env->type == PLAYER)
03329     {
03330         LOG(llevDebug, "Object with auto_apply (%s, %s) found in %s.\n", op->name, op->arch->name, op->env->name);
03331         return 0;
03332     }
03333 
03334     switch (op->type)
03335     {
03336         case SHOP_FLOOR:
03337             if (op->randomitems == NULL)
03338             {
03339                 return 0;
03340             }
03341 
03342             a_chance = op->randomitems->artifact_chance;
03343 
03344             /* If damned shop floor, force 0 artifact chance. */
03345             if (QUERY_FLAG(op, FLAG_DAMNED))
03346             {
03347                 a_chance = 0;
03348             }
03349 
03350             do
03351             {
03352                 /* Let's give it 10 tries */
03353                 i = 10;
03354                 level = op->stats.exp ? (int) op->stats.exp : get_environment_level(op);
03355 
03356                 while ((tmp = generate_treasure(op->randomitems, level, a_chance)) == NULL && --i)
03357                 {
03358                 }
03359 
03360                 if (tmp == NULL)
03361                 {
03362                     return 0;
03363                 }
03364 
03365                 if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
03366                 {
03367                     tmp = NULL;
03368                 }
03369             }
03370             while (!tmp);
03371 
03372             tmp->x = op->x, tmp->y = op->y;
03373             SET_FLAG(tmp, FLAG_UNPAID);
03374 
03375             /* If this shop floor doesn't have FLAG_CURSED, generate
03376              * shop-clone items. */
03377             if (!QUERY_FLAG(op, FLAG_CURSED))
03378             {
03379                 SET_FLAG(tmp, FLAG_NO_PICK);
03380             }
03381 
03382             insert_ob_in_map(tmp, op->map, NULL, INS_NO_MERGE | INS_NO_WALK_ON);
03383             identify(tmp);
03384 
03385             break;
03386 
03387         case TREASURE:
03388             level = op->stats.exp ? (int) op->stats.exp : get_environment_level(op);
03389             create_treasure(op->randomitems, op, op->map ? GT_ENVIRONMENT : 0, level, T_STYLE_UNSET, ART_CHANCE_UNSET, 0, NULL);
03390 
03391             /* If we generated on object and put it in this object inventory,
03392              * move it to the parent object as the current object is about
03393              * to disappear.  An example of this item is the random_* stuff
03394              * that is put inside other objects. */
03395             for (tmp = op->inv; tmp; tmp = tmp2)
03396             {
03397                 tmp2 = tmp->below;
03398                 remove_ob(tmp);
03399 
03400                 if (op->env)
03401                 {
03402                     insert_ob_in_ob(tmp, op->env);
03403                 }
03404             }
03405 
03406             /* No move off needed */
03407             remove_ob(op);
03408             break;
03409     }
03410 
03411     return tmp ? 1 : 0;
03412 }
03413 
03421 int can_see_monsterP(mapstruct *m, int x, int y, int dir)
03422 {
03423     int dx, dy;
03424 
03425     /* Exit condition: invalid direction */
03426     if (dir < 0)
03427     {
03428         return 0;
03429     }
03430 
03431     dx = x + freearr_x[dir];
03432     dy = y + freearr_y[dir];
03433 
03434     if (!(m = get_map_from_coord(m, &dx, &dy)))
03435     {
03436         return 0;
03437     }
03438 
03439     if (wall(m, dx, dy))
03440     {
03441         return 0;
03442     }
03443 
03444     /* Yes, can see. */
03445     if (dir < 9)
03446     {
03447         return 1;
03448     }
03449 
03450     return can_see_monsterP(m, x, y, reduction_dir[dir][0]) | can_see_monsterP(m, x, y, reduction_dir[dir][1]) | can_see_monsterP(m, x, y, reduction_dir[dir][2]);
03451 }
03452 
03457 void free_key_values(object *op)
03458 {
03459     key_value *i, *next = NULL;
03460 
03461     if (op->key_values == NULL)
03462     {
03463         return;
03464     }
03465 
03466     for (i = op->key_values; i; i = next)
03467     {
03468         /* Store next *first*. */
03469         next = i->next;
03470 
03471         if (i->key)
03472         {
03473             FREE_AND_CLEAR_HASH(i->key);
03474         }
03475 
03476         if (i->value)
03477         {
03478             FREE_AND_CLEAR_HASH(i->value);
03479         }
03480 
03481         i->next = NULL;
03482         free(i);
03483     }
03484 
03485     op->key_values = NULL;
03486 }
03487 
03494 key_value *object_get_key_link(const object *ob, const char *key)
03495 {
03496     key_value *field;
03497 
03498     for (field = ob->key_values; field; field = field->next)
03499     {
03500         if (field->key == key)
03501         {
03502             return field;
03503         }
03504     }
03505 
03506     return NULL;
03507 }
03508 
03516 const char *object_get_value(const object *op, const char *const key)
03517 {
03518     key_value *field;
03519     const char *canonical_key = find_string(key);
03520 
03521     if (canonical_key == NULL)
03522     {
03523         return NULL;
03524     }
03525 
03526     /* This is copied from object_get_key_link() above - only 4 lines, and
03527      * saves the function call overhead. */
03528     for (field = op->key_values; field; field = field->next)
03529     {
03530         if (field->key == canonical_key)
03531         {
03532             return field->value;
03533         }
03534     }
03535 
03536     return NULL;
03537 }
03538 
03546 static int object_set_value_s(object *op, const char *canonical_key, const char *value, int add_key)
03547 {
03548     key_value *field = NULL, *last = NULL;
03549 
03550     for (field = op->key_values; field; field = field->next)
03551     {
03552         if (field->key != canonical_key)
03553         {
03554             last = field;
03555             continue;
03556         }
03557 
03558         if (field->value)
03559         {
03560             FREE_AND_CLEAR_HASH(field->value);
03561         }
03562 
03563         if (value)
03564         {
03565             field->value = add_string(value);
03566         }
03567         else
03568         {
03569             /* Basically, if the archetype has this key set, we need to
03570              * store the NULL value so when we save it, we save the empty
03571              * value so that when we load, we get this value back
03572              * again. */
03573             if (object_get_key_link(&op->arch->clone, canonical_key))
03574             {
03575                 field->value = NULL;
03576             }
03577             else
03578             {
03579                 /* Delete this link */
03580                 if (field->key)
03581                 {
03582                     FREE_AND_CLEAR_HASH(field->key);
03583                 }
03584 
03585                 if (field->value)
03586                 {
03587                     FREE_AND_CLEAR_HASH(field->value);
03588                 }
03589 
03590                 if (last)
03591                 {
03592                     last->next = field->next;
03593                 }
03594                 else
03595                 {
03596                     op->key_values = field->next;
03597                 }
03598 
03599                 free(field);
03600             }
03601         }
03602 
03603         return 1;
03604     }
03605 
03606     if (!add_key)
03607     {
03608         return 0;
03609     }
03610 
03611     /* There isn't any good reason to store a NULL value in the key/value
03612      * list. If the archetype has this key, then we should also have it,
03613      * so shouldn't be here. If user wants to store empty strings, should
03614      * pass in "" */
03615     if (value == NULL)
03616     {
03617         return 1;
03618     }
03619 
03620     field = malloc(sizeof(key_value));
03621 
03622     field->key = add_refcount(canonical_key);
03623     field->value = add_string(value);
03624     /* Usual prepend-addition. */
03625     field->next = op->key_values;
03626     op->key_values = field;
03627 
03628     return 1;
03629 }
03630 
03640 int object_set_value(object *op, const char *key, const char *value, int add_key)
03641 {
03642     const char *canonical_key = find_string(key);
03643     int floating_ref = 0, ret;
03644 
03645     if (canonical_key == NULL)
03646     {
03647         canonical_key = add_string(key);
03648         floating_ref = 1;
03649     }
03650 
03651     ret = object_set_value_s(op, canonical_key, value, add_key);
03652 
03653     if (floating_ref)
03654     {
03655         FREE_ONLY_HASH(canonical_key);
03656     }
03657 
03658     return ret;
03659 }
03660 
03663 void init_object_initializers()
03664 {
03665     object_initializers[BEACON] = beacon_add;
03666     object_initializers[MAP_EVENT_OBJ] = map_event_obj_init;
03667     object_initializers[MAGIC_MIRROR] = magic_mirror_init;
03668     object_initializers[MAP_INFO] = map_info_init;
03669 }
03670 
03693 int item_matched_string(object *pl, object *op, const char *name)
03694 {
03695     char *cp, local_name[MAX_BUF];
03696     int count, retval = 0, book_level, book_level2, weapon_type;
03697 
03698     /* strtok is destructive to name */
03699     strcpy(local_name, name);
03700 
03701     for (cp = strtok(local_name, ","); cp; cp = strtok(NULL, ","))
03702     {
03703         /* Get rid of spaces */
03704         while (cp[0] == ' ')
03705         {
03706             cp++;
03707         }
03708 
03709         /* All is a very generic match - low match value */
03710         if (!strcasecmp(cp, "all"))
03711         {
03712             return 1;
03713         }
03714 
03715         /* Unpaid is a little more specific */
03716         if (!strcasecmp(cp, "unpaid") && QUERY_FLAG(op, FLAG_UNPAID))
03717         {
03718             return 2;
03719         }
03720 
03721         if (!strcasecmp(cp, "cursed") && QUERY_FLAG(op, FLAG_IDENTIFIED) && (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED)))
03722         {
03723             return 2;
03724         }
03725 
03726         if (!strcasecmp(cp, "unlocked") && !QUERY_FLAG(op, FLAG_INV_LOCKED))
03727         {
03728             return 2;
03729         }
03730 
03731         if (QUERY_FLAG(op, FLAG_IDENTIFIED) && !strcasecmp(cp, "identified"))
03732         {
03733             return 2;
03734         }
03735 
03736         if (!QUERY_FLAG(op, FLAG_IDENTIFIED) && !strcasecmp(cp, "unidentified"))
03737         {
03738             return 2;
03739         }
03740 
03741         if ((op->type == FOOD || op->type == DRINK) && !strcasecmp(cp, "food"))
03742         {
03743             return 2;
03744         }
03745 
03746         if ((op->type == GEM || op->type == JEWEL || op->type == NUGGET || op->type == PEARL) && !strcasecmp(cp, "valuables"))
03747         {
03748             return 2;
03749         }
03750 
03751         if (op->type == WEAPON)
03752         {
03753             weapon_type = op->sub_type % 4;
03754 
03755             if (weapon_type == WEAP_1H_IMPACT)
03756             {
03757                 if (!strcasecmp(cp, "impact weapons"))
03758                 {
03759                     return 2;
03760                 }
03761             }
03762             else if (weapon_type == WEAP_1H_SLASH)
03763             {
03764                 if (!strcasecmp(cp, "slash weapons"))
03765                 {
03766                     return 2;
03767                 }
03768             }
03769             else if (weapon_type == WEAP_1H_CLEAVE)
03770             {
03771                 if (!strcasecmp(cp, "cleave weapons"))
03772                 {
03773                     return 2;
03774                 }
03775             }
03776             else if (weapon_type == WEAP_1H_PIERCE)
03777             {
03778                 if (!strcasecmp(cp, "pierce weapons"))
03779                 {
03780                     return 2;
03781                 }
03782             }
03783         }
03784         else if (op->type == BOOK)
03785         {
03786             if (!strcasecmp(cp, "books"))
03787             {
03788                 return 2;
03789             }
03790 
03791             if (!op->msg && !strcasecmp(cp, "empty books"))
03792             {
03793                 return 2;
03794             }
03795 
03796             if (!QUERY_FLAG(op, FLAG_NO_SKILL_IDENT))
03797             {
03798                 if (!strcasecmp(cp, "unread books"))
03799                 {
03800                     return 2;
03801                 }
03802 
03803                 if (sscanf(cp, "unread level %d books", &book_level) == 1 && op->level == book_level)
03804                 {
03805                     return 2;
03806                 }
03807 
03808                 if (sscanf(cp, "unread level %d-%d books", &book_level, &book_level2) == 2 && op->level >= book_level && op->level <= book_level2)
03809                 {
03810                     return 2;
03811                 }
03812             }
03813             else
03814             {
03815                 if (!strcasecmp(cp, "read books"))
03816                 {
03817                     return 2;
03818                 }
03819 
03820                 if (sscanf(cp, "read level %d books", &book_level) == 1 && op->level == book_level)
03821                 {
03822                     return 2;
03823                 }
03824 
03825                 if (sscanf(cp, "read level %d-%d books", &book_level, &book_level2) == 2 && op->level >= book_level && op->level <= book_level2)
03826                 {
03827                     return 2;
03828                 }
03829             }
03830         }
03831 
03832         count = 0;
03833 
03834         /* Allow for things like '100 arrows', but don't accept
03835          * strings like '+2', '-1' as numbers. */
03836         if (isdigit(cp[0]) && (count = atoi(cp)) != 0)
03837         {
03838             cp = strchr(cp, ' ');
03839 
03840             /* Get rid of spaces */
03841             while (cp && cp[0] == ' ')
03842             {
03843                 cp++;
03844             }
03845         }
03846 
03847         if (!cp || cp[0] == '\0' || count < 0)
03848         {
03849             return 0;
03850         }
03851 
03852         /* Base name matched - not bad */
03853         if (strcasecmp(cp, op->name) == 0 && !count)
03854         {
03855             retval = 4;
03856         }
03857         /* Need to plurify name for proper match */
03858         else if (count > 1)
03859         {
03860             char newname[MAX_BUF];
03861             strcpy(newname, op->name);
03862 
03863             if (!strcasecmp(newname, cp))
03864             {
03865                 retval = 6;
03866             }
03867         }
03868         else if (count == 1)
03869         {
03870             if (!strcasecmp(op->name, cp))
03871             {
03872                 retval = 6;
03873             }
03874         }
03875 
03876         if (!strcasecmp(cp, query_name(op, NULL)))
03877         {
03878             retval = 20;
03879         }
03880         else if (!strcasecmp(cp, query_short_name(op, NULL)))
03881         {
03882             retval = 18;
03883         }
03884         else if (!strcasecmp(cp, query_base_name(op, pl)))
03885         {
03886             retval = 16;
03887         }
03888         else if (op->custom_name && !strcasecmp(cp, op->custom_name))
03889         {
03890             retval = 15;
03891         }
03892         else if (!strncasecmp(cp, query_base_name(op, pl), strlen(cp)))
03893         {
03894             retval = 14;
03895         }
03896         /* Do substring checks, so things like 'Str+1' will match.
03897          * retval of these should perhaps be lower - they are lower
03898          * than the specific strcasecmps above, but still higher than
03899          * some other match criteria. */
03900         else if (strstr(query_base_name(op, pl), cp))
03901         {
03902             retval = 12;
03903         }
03904         else if (strstr(query_short_name(op, NULL), cp))
03905         {
03906             retval = 12;
03907         }
03908         /* Check for partial custom name, but give a really low priority. */
03909         else if (op->custom_name && strstr(op->custom_name, cp))
03910         {
03911             retval = 3;
03912         }
03913 
03914         if (retval)
03915         {
03916             if (pl->type == PLAYER)
03917             {
03918                 CONTR(pl)->count = count;
03919             }
03920 
03921             return retval;
03922         }
03923     }
03924 
03925     return 0;
03926 }
03927 
03932 int object_get_gender(object *op)
03933 {
03934     if (QUERY_FLAG(op, FLAG_IS_MALE))
03935     {
03936         return QUERY_FLAG(op, FLAG_IS_FEMALE) ? GENDER_HERMAPHRODITE : GENDER_MALE;
03937     }
03938     else if (QUERY_FLAG(op, FLAG_IS_FEMALE))
03939     {
03940         return GENDER_FEMALE;
03941     }
03942 
03943     return GENDER_NEUTER;
03944 }
03945 
03952 object *object_need_esrv_update(object *op)
03953 {
03954     object *tmp;
03955 
03956     if (!op->env)
03957     {
03958         return NULL;
03959     }
03960 
03961     /* Is this object in the player's inventory, or sub container
03962      * therein? */
03963     tmp = is_player_inv(op->env);
03964 
03965     if (!tmp)
03966     {
03967         if (op->env->type == CONTAINER && op->env->attacked_by && CONTR(op->env->attacked_by) && CONTR(op->env->attacked_by)->container == op->env)
03968         {
03969             tmp = op->env->attacked_by;
03970         }
03971     }
03972 
03973     return tmp;
03974 }
03975 
03979 void object_remove_esrv_update(object *op)
03980 {
03981     object *tmp = object_need_esrv_update(op);
03982 
03983     if (tmp)
03984     {
03985         /* Tell the client(s) that the object has been removed. */
03986         esrv_del_item(CONTR(tmp), op->count, op->env);
03987     }
03988 
03989     /* Remove the object. */
03990     remove_ob(op);
03991     check_walk_off(op, NULL, MOVE_APPLY_VANISHED);
03992 }