|
Atrinik Server 2.5
|
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 }
1.7.4