Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_build_map = 00003 * "$Id: build_map.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 /* 00006 CrossFire, A Multiplayer game for X-windows 00007 00008 Copyright (C) 2001-2006 Mark Wedel & Crossfire Development Team 00009 Copyright (C) 1992 Frank Tore Johansen 00010 00011 This program is free software; you can redistribute it and/or modify 00012 it under the terms of the GNU General Public License as published by 00013 the Free Software Foundation; either version 2 of the License, or 00014 (at your option) any later version. 00015 00016 This program is distributed in the hope that it will be useful, 00017 but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 GNU General Public License for more details. 00020 00021 You should have received a copy of the GNU General Public License 00022 along with this program; if not, write to the Free Software 00023 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00024 00025 The authors can be reached via e-mail to crossfire-devel@real-time.com 00026 */ 00027 00037 #include <global.h> 00038 #include <living.h> 00039 #include <spells.h> 00040 #include <skills.h> 00041 #include <tod.h> 00042 #include <sproto.h> 00043 00057 static int can_build_over(struct mapdef *map, object *new_item, short x, short y) { 00058 object *ob; 00059 00060 for (ob = GET_MAP_OB(map, x, y); ob; ob = ob->above) { 00061 if (strcmp(ob->arch->name, "rune_mark") == 0) 00062 /* you can always build on marking runes, used for connected building things. */ 00063 continue; 00064 00065 if ((ob->head && QUERY_FLAG(ob->head, FLAG_IS_BUILDABLE)) || (!ob->head && QUERY_FLAG(ob, FLAG_IS_BUILDABLE))) 00066 /* Check for the flag is required, as this function 00067 * can be called recursively on different spots. 00068 */ 00069 continue; 00070 00071 switch (new_item->type) { 00072 case SIGN: 00073 case MAGIC_EAR: 00074 /* Allow signs and magic ears to be built on books */ 00075 if (ob->type != BOOK) 00076 return 0; 00077 break; 00078 00079 case BUTTON: 00080 case DETECTOR: 00081 case PEDESTAL: 00082 case CF_HANDLE: 00083 /* Allow buttons and levers to be built under gates */ 00084 if (ob->type != GATE && ob->type != DOOR) 00085 return 0; 00086 break; 00087 00088 default: 00089 return 0; 00090 } 00091 } 00092 00093 /* If item being built is multi-tile, need to check other parts too. */ 00094 if (new_item->more) 00095 return can_build_over(map, new_item->more, x+new_item->more->arch->clone.x-new_item->arch->clone.x, y+new_item->more->arch->clone.y-new_item->arch->clone.y); 00096 00097 return 1; 00098 } 00099 00111 static object *get_connection_rune(object *pl, short x, short y) { 00112 object *rune; 00113 00114 rune = GET_MAP_OB(pl->map, x, y); 00115 while (rune && ((rune->type != SIGN) || (strcmp(rune->arch->name, "rune_mark")))) 00116 rune = rune->above; 00117 return rune; 00118 } 00119 00131 static object *get_msg_book(object *pl, short x, short y) { 00132 object *book; 00133 00134 book = GET_MAP_OB(pl->map, x, y); 00135 while (book && (book->type != BOOK)) 00136 book = book->above; 00137 return book; 00138 } 00139 00152 static object *get_wall(struct mapdef *map, int x, int y) { 00153 object *wall; 00154 00155 wall = GET_MAP_OB(map, x, y); 00156 while (wall && (wall->type != WALL)) 00157 wall = wall->above; 00158 00159 return wall; 00160 } 00161 00170 static void remove_marking_runes(struct mapdef *map, short x, short y) { 00171 object *rune; 00172 object *next; 00173 00174 rune = GET_MAP_OB(map, x, y); 00175 while (rune) { 00176 next = rune->above; 00177 if ((rune->type == SIGN) && (!strcmp(rune->arch->name, "rune_mark"))) { 00178 remove_ob(rune); 00179 free_object(rune); 00180 } 00181 rune = next; 00182 } 00183 } 00184 00202 static int adjust_sign_msg(object *pl, short x, short y, object *tmp) { 00203 object *book; 00204 char buf[MAX_BUF]; 00205 char buf2[MAX_BUF]; 00206 00207 book = get_msg_book(pl, x, y); 00208 if (!book) { 00209 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00210 "You need to put a book or scroll with the message.", NULL); 00211 return -1; 00212 } 00213 00214 tmp->msg = book->msg; 00215 add_refcount(tmp->msg); 00216 00217 if (tmp->invisible) { 00218 if (book->custom_name != NULL) { 00219 snprintf(buf, sizeof(buf), "talking %s", book->custom_name); 00220 } else { 00221 snprintf(buf, sizeof(buf), "talking %s", book->name); 00222 } 00223 if (tmp->name) 00224 free_string(tmp->name); 00225 tmp->name = add_string(buf); 00226 00227 if (book->name_pl != NULL) { 00228 snprintf(buf2, sizeof(buf2), "talking %s", book->name_pl); 00229 if (tmp->name_pl) 00230 free_string(tmp->name_pl); 00231 tmp->name_pl = add_string(buf2); 00232 } 00233 00234 tmp->face = book->face; 00235 tmp->invisible = 0; 00236 } 00237 remove_ob(book); 00238 free_object(book); 00239 return 0; 00240 } 00241 00252 static int find_unused_connected_value(struct mapdef *map) { 00253 int connected = 0; 00254 int itest = 0; 00255 oblinkpt *obp; 00256 00257 while (itest++ < 1000) { 00258 connected = 1+rand()%20000; 00259 for (obp = map->buttons; obp && (obp->value != connected); obp = obp->next) 00260 ; 00261 00262 if (!obp) 00263 return connected; 00264 } 00265 00266 return -1; 00267 } 00268 00269 00290 static int find_or_create_connection_for_map(object *pl, short x, short y, object *rune) { 00291 object *force; 00292 int connected; 00293 00294 if (!rune) 00295 rune = get_connection_rune(pl, x, y); 00296 00297 if (!rune || !rune->msg) { 00298 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00299 "You need to put a marking rune with the group name.", NULL); 00300 return -1; 00301 } 00302 00303 /* Now, find force in player's inventory */ 00304 force = pl->inv; 00305 while (force && ((force->type != FORCE) || (!force->slaying) || (strcmp(force->slaying, pl->map->path)) || (!force->msg) || (strcmp(force->msg, rune->msg)))) 00306 force = force->below; 00307 00308 if (!force) { 00309 /* No force, need to create & insert one */ 00310 /* Find unused value */ 00311 connected = find_unused_connected_value(pl->map); 00312 if (connected == -1) { 00313 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00314 "Could not create more groups.", NULL); 00315 return -1; 00316 } 00317 00318 force = create_archetype(FORCE_NAME); 00319 force->speed = 0; 00320 update_ob_speed(force); 00321 force->slaying = add_string(pl->map->path); 00322 force->msg = add_string(rune->msg); 00323 force->path_attuned = connected; 00324 insert_ob_in_ob(force, pl); 00325 00326 return connected; 00327 } 00328 00329 /* Found the force, everything's easy. */ 00330 return force->path_attuned; 00331 } 00332 00348 static void fix_walls(struct mapdef *map, int x, int y) { 00349 int connect; 00350 object *wall; 00351 char archetype[MAX_BUF]; 00352 char *underscore; 00353 uint32 old_flags[4]; 00354 struct archt *new_arch; 00355 int flag; 00356 int len; 00357 int has_window; 00358 00359 /* First, find the wall on that spot */ 00360 wall = get_wall(map, x, y); 00361 if (!wall) 00362 /* Nothing -> bail out */ 00363 return; 00364 00365 /* Find base name */ 00366 strncpy(archetype, wall->arch->name, sizeof(archetype)); 00367 archetype[sizeof(archetype)-1] = '\0'; 00368 underscore = strchr(archetype, '_'); 00369 if (!underscore) 00370 /* Not in a format we can change, bail out */ 00371 return; 00372 has_window = 0; 00373 if (!strcmp(underscore+1, "win1")) 00374 has_window = 1; 00375 else if (!strcmp(underscore+1, "win2")) 00376 has_window = 1; 00377 else if (!isdigit(*(underscore+1))) 00378 return; 00379 00380 underscore++; 00381 *underscore = '\0'; 00382 len = sizeof(archetype)-strlen(archetype)-2; 00383 00384 connect = 0; 00385 00386 if ((x > 0) && get_wall(map, x-1, y)) 00387 connect |= 1; 00388 if ((x < MAP_WIDTH(map)-1) && get_wall(map, x+1, y)) 00389 connect |= 2; 00390 if ((y > 0) && get_wall(map, x, y-1)) 00391 connect |= 4; 00392 if ((y < MAP_HEIGHT(map)-1) && get_wall(map, x, y+1)) 00393 connect |= 8; 00394 00395 switch (connect) { 00396 case 0: 00397 strncat(archetype, "0", len); 00398 break; 00399 00400 case 1: 00401 strncat(archetype, "1_3", len); 00402 break; 00403 00404 case 2: 00405 strncat(archetype, "1_4", len); 00406 break; 00407 00408 case 3: 00409 if (has_window) { 00410 strncat(archetype, "win2", len); 00411 } else { 00412 strncat(archetype, "2_1_2", len); 00413 } 00414 break; 00415 00416 case 4: 00417 strncat(archetype, "1_2", len); 00418 break; 00419 00420 case 5: 00421 strncat(archetype, "2_2_4", len); 00422 break; 00423 00424 case 6: 00425 strncat(archetype, "2_2_1", len); 00426 break; 00427 00428 case 7: 00429 strncat(archetype, "3_1", len); 00430 break; 00431 00432 case 8: 00433 strncat(archetype, "1_1", len); 00434 break; 00435 00436 case 9: 00437 strncat(archetype, "2_2_3", len); 00438 break; 00439 00440 case 10: 00441 strncat(archetype, "2_2_2", len); 00442 break; 00443 00444 case 11: 00445 strncat(archetype, "3_3", len); 00446 break; 00447 00448 case 12: 00449 if (has_window) { 00450 strncat(archetype, "win1", len); 00451 } else { 00452 strncat(archetype, "2_1_1", len); 00453 } 00454 break; 00455 00456 case 13: 00457 strncat(archetype, "3_4", len); 00458 break; 00459 00460 case 14: 00461 strncat(archetype, "3_2", len); 00462 break; 00463 00464 case 15: 00465 strncat(archetype, "4", len); 00466 break; 00467 } 00468 00469 /* 00470 * No need to change anything if the old and new names are identical. 00471 */ 00472 if (!strncmp(archetype, wall->arch->name, sizeof(archetype))) 00473 return; 00474 00475 /* 00476 * Before anything, make sure the archetype does exist... 00477 * If not, prolly an error... 00478 */ 00479 new_arch = find_archetype(archetype); 00480 if (!new_arch) 00481 return; 00482 00483 /* Now delete current wall, and insert new one 00484 * We save flags to avoid any trouble with buildable/non buildable, and so on 00485 */ 00486 for (flag = 0; flag < 4; flag++) 00487 old_flags[flag] = wall->flags[flag]; 00488 remove_ob(wall); 00489 free_object(wall); 00490 00491 wall = arch_to_object(new_arch); 00492 wall->type = WALL; 00493 insert_ob_in_map_at(wall, map, NULL, INS_ABOVE_FLOOR_ONLY, x, y); 00494 for (flag = 0; flag < 4; flag++) 00495 wall->flags[flag] = old_flags[flag]; 00496 } 00497 00519 static int apply_builder_floor(object *pl, object *new_floor, short x, short y) { 00520 object *tmp; 00521 object *above_floor; /* Item above floor, if any */ 00522 object *floor; /* Floor which would be removed if required */ 00523 struct archt *new_wall; 00524 int i, xt, yt, wall_removed; 00525 char message[MAX_BUF]; 00526 00527 snprintf(message, sizeof(message), "You change the floor to better suit your tastes."); 00528 00529 /* 00530 * Now the building part... 00531 * First, remove wall(s) and floor(s) at position x, y 00532 */ 00533 above_floor = NULL; 00534 floor = NULL; 00535 new_wall = NULL; 00536 wall_removed = 0; 00537 tmp = GET_MAP_OB(pl->map, x, y); 00538 while (tmp) { 00539 object *above; 00540 00541 above = tmp->above; 00542 if (WALL == tmp->type) { 00543 /* There was a wall, remove it & keep its archetype to make new walls */ 00544 new_wall = tmp->arch; 00545 remove_ob(tmp); 00546 free_object(tmp); 00547 snprintf(message, sizeof(message), "You destroy the wall and redo the floor."); 00548 wall_removed = 1; 00549 if (floor != NULL) { 00550 remove_ob(floor); 00551 free_object(floor); 00552 floor = NULL; 00553 } 00554 } else if ((FLOOR == tmp->type) || (QUERY_FLAG(tmp, FLAG_IS_FLOOR))) { 00555 floor = tmp; 00556 } else { 00557 if (floor != NULL) 00558 above_floor = tmp; 00559 } 00560 00561 tmp = above; 00562 } 00563 00564 if (wall_removed == 0 && floor != NULL) { 00565 if (floor->arch == new_floor->arch) { 00566 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, "You feel too lazy to redo the exact same floor.", NULL); 00567 free_object(new_floor); 00568 return 0; 00569 } 00570 } 00571 00572 SET_FLAG(new_floor, FLAG_UNIQUE); 00573 SET_FLAG(new_floor, FLAG_IS_FLOOR); 00574 new_floor->type = FLOOR; 00575 insert_ob_in_map_at(new_floor, pl->map, above_floor, above_floor ? INS_BELOW_ORIGINATOR : INS_ON_TOP, x, y); 00576 00577 /* if there was a floor, remove it */ 00578 if (floor) { 00579 remove_ob(floor); 00580 free_object(floor); 00581 floor = NULL; 00582 } 00583 00584 /* 00585 * Next step: make sure there are either walls or floors around the new square 00586 * Since building, you can have: blocking view / floor / wall / nothing 00587 */ 00588 for (i = 1; i <= 8; i++) { 00589 xt = x+freearr_x[i]; 00590 yt = y+freearr_y[i]; 00591 tmp = GET_MAP_OB(pl->map, xt, yt); 00592 if (!tmp) { 00593 /* Must insert floor & wall */ 00594 00595 tmp = arch_to_object(new_floor->arch); 00596 /* Better make the floor unique */ 00597 SET_FLAG(tmp, FLAG_UNIQUE); 00598 SET_FLAG(tmp, FLAG_IS_BUILDABLE); 00599 tmp->type = FLOOR; 00600 insert_ob_in_map_at(tmp, pl->map, NULL, 0, xt, yt); 00601 /* Insert wall if exists. Note: if it doesn't, the map is weird... */ 00602 if (new_wall) { 00603 tmp = arch_to_object(new_wall); 00604 SET_FLAG(tmp, FLAG_IS_BUILDABLE); 00605 tmp->type = WALL; 00606 insert_ob_in_map_at(tmp, pl->map, NULL, 0, xt, yt); 00607 } 00608 } 00609 } 00610 00611 /* Finally fixing walls to ensure nice continuous walls 00612 * Note: 2 squares around are checked, because potentially we added walls 00613 * around the building spot, so need to check that those new walls connect 00614 * correctly 00615 */ 00616 for (xt = x-2; xt <= x+2; xt++) 00617 for (yt = y-2; yt <= y+2; yt++) { 00618 if (!OUT_OF_REAL_MAP(pl->map, xt, yt)) 00619 fix_walls(pl->map, xt, yt); 00620 } 00621 00622 /* Tell player about the fix */ 00623 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, message, NULL); 00624 return 1; 00625 } 00626 00644 static int apply_builder_wall(object *pl, object *new_wall, short x, short y) { 00645 object *current_wall; 00646 char message[MAX_BUF]; 00647 00648 remove_marking_runes(pl->map, x, y); 00649 00650 current_wall = get_wall(pl->map, x, y); 00651 00652 if (current_wall) { 00653 char current_basename[MAX_BUF]; 00654 char new_basename[MAX_BUF]; 00655 char *underscore; 00656 00657 /* Check if the old and new archetypes have the same prefix */ 00658 strncpy(current_basename, current_wall->arch->name, sizeof(current_basename)); 00659 current_basename[sizeof(current_basename)-1] = '\0'; 00660 underscore = strchr(current_basename, '_'); 00661 if (underscore && isdigit(*(underscore+1))) { 00662 underscore++; 00663 *underscore = '\0'; 00664 } 00665 strncpy(new_basename, new_wall->arch->name, sizeof(new_basename)); 00666 new_basename[sizeof(new_basename)-1] = '\0'; 00667 underscore = strchr(new_basename, '_'); 00668 if (underscore && isdigit(*(underscore+1))) { 00669 underscore++; 00670 *underscore = '\0'; 00671 } 00672 if (!strncmp(current_basename, new_basename, sizeof(new_basename))) { 00673 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, "You feel too lazy to redo the exact same wall.", NULL); 00674 free_object(new_wall); 00675 return 0; 00676 } 00677 } 00678 00679 snprintf(message, sizeof(message), "You build a wall."); 00680 new_wall->type = WALL; 00681 00682 if (current_wall) { 00683 /* If existing wall, replace it, no need to fix other walls */ 00684 remove_ob(current_wall); 00685 free_object(current_wall); 00686 insert_ob_in_map_at(new_wall, pl->map, NULL, INS_ABOVE_FLOOR_ONLY, x, y); 00687 fix_walls(pl->map, x, y); 00688 snprintf(message, sizeof(message), "You redecorate the wall to better suit your tastes."); 00689 } else { 00690 int xt, yt; 00691 00692 /* Else insert new wall and fix all walls around */ 00693 insert_ob_in_map_at(new_wall, pl->map, NULL, INS_ABOVE_FLOOR_ONLY, x, y); 00694 for (xt = x-1; xt <= x+1; xt++) 00695 for (yt = y-1; yt <= y+1; yt++) { 00696 if (OUT_OF_REAL_MAP(pl->map, xt, yt)) 00697 continue; 00698 00699 fix_walls(pl->map, xt, yt); 00700 } 00701 } 00702 00703 /* Tell player what happened */ 00704 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, message, NULL); 00705 return 1; 00706 } 00707 00724 static int apply_builder_window(object *pl, object *new_wall_win, short x, short y) { 00725 object *current_wall; 00726 char archetype[MAX_BUF]; 00727 struct archt *new_arch; 00728 object *window; 00729 uint32 old_flags[4]; 00730 int flag; 00731 00732 /* Too bad, we never use the window contained in the building material */ 00733 free_object(new_wall_win); 00734 00735 current_wall = get_wall(pl->map, x, y); 00736 00737 if (current_wall) { 00738 char *underscore; 00739 00740 strncpy(archetype, current_wall->arch->name, sizeof(archetype)); 00741 archetype[sizeof(archetype)-1] = '\0'; 00742 underscore = strchr(archetype, '_'); 00743 if (underscore) { 00744 underscore++; 00745 /* Check if the current wall has a window */ 00746 if (!strcmp(underscore, "win1") 00747 || !strcmp(underscore, "win2")) { 00748 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, "You feel too lazy to redo the window.", NULL); 00749 return 0; 00750 } 00751 if (!strcmp(underscore, "2_1_1")) 00752 strcpy(underscore, "win1"); 00753 else if (!strcmp(underscore, "2_1_2")) 00754 strcpy(underscore, "win2"); 00755 else { 00756 /* Wrong wall orientation */ 00757 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, "You cannot build a window in that wall.", NULL); 00758 return 0; 00759 } 00760 } 00761 } else { 00762 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00763 "There is no wall there.", NULL); 00764 return 0; 00765 } 00766 00767 new_arch = find_archetype(archetype); 00768 if (!new_arch) { 00769 /* That type of wall doesn't have corresponding window archetypes */ 00770 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, "You cannot build a window in that wall.", NULL); 00771 return 0; 00772 } 00773 00774 /* Now delete current wall, and insert new one with a window 00775 * We save flags to avoid any trouble with buildable/non buildable, and so on 00776 */ 00777 for (flag = 0; flag < 4; flag++) 00778 old_flags[flag] = current_wall->flags[flag]; 00779 remove_ob(current_wall); 00780 free_object(current_wall); 00781 00782 window = arch_to_object(new_arch); 00783 window->type = WALL; 00784 insert_ob_in_map_at(window, pl->map, NULL, INS_ABOVE_FLOOR_ONLY, x, y); 00785 for (flag = 0; flag < 4; flag++) 00786 window->flags[flag] = old_flags[flag]; 00787 00788 /* Tell player what happened */ 00789 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, "You build a window in the wall.", NULL); 00790 return 1; 00791 } 00792 00814 static int apply_builder_item(object *pl, object *new_item, short x, short y) { 00815 int insert_flag; 00816 object *floor; 00817 object *con_rune; 00818 int connected; 00819 char name[MAX_BUF]; 00820 00821 /* Find floor */ 00822 floor = GET_MAP_OB(pl->map, x, y); 00823 if (!floor) { 00824 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, "Invalid square.", NULL); 00825 free_object(new_item); 00826 return 0; 00827 } 00828 00829 while (floor && (floor->type != FLOOR) && (!QUERY_FLAG(floor, FLAG_IS_FLOOR))) 00830 floor = floor->above; 00831 00832 if (!floor) { 00833 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00834 "This square has no floor, you can't build here.", NULL); 00835 free_object(new_item); 00836 return 0; 00837 } 00838 00839 SET_FLAG(new_item, FLAG_NO_PICK); 00840 00841 /* 00842 * This doesn't work on non unique maps. pedestals under floor will not be saved... 00843 * insert_flag = (material->stats.Str == 1) ? INS_BELOW_ORIGINATOR : INS_ABOVE_FLOOR_ONLY; 00844 */ 00845 insert_flag = INS_ABOVE_FLOOR_ONLY; 00846 00847 connected = 0; 00848 con_rune = NULL; 00849 switch (new_item->type) { 00850 case DOOR: 00851 case GATE: 00852 case BUTTON: 00853 case DETECTOR: 00854 case TIMED_GATE: 00855 case PEDESTAL: 00856 case CF_HANDLE: 00857 case MAGIC_EAR: 00858 case SIGN: 00859 /* Signs don't need a connection, but but magic mouths do. */ 00860 if (new_item->type == SIGN && strcmp(new_item->arch->name, "magic_mouth")) 00861 break; 00862 con_rune = get_connection_rune(pl, x, y); 00863 connected = find_or_create_connection_for_map(pl, x, y, con_rune); 00864 if (connected == -1) { 00865 /* Player already informed of failure by the previous function */ 00866 free_object(new_item); 00867 return 0; 00868 } 00869 } 00870 00871 /* For magic mouths/ears, and signs, take the msg from a book of scroll */ 00872 if ((new_item->type == SIGN) || (new_item->type == MAGIC_EAR)) { 00873 if (adjust_sign_msg(pl, x, y, new_item) == -1) { 00874 free_object(new_item); 00875 return 0; 00876 } 00877 } 00878 00879 if (con_rune != NULL) { 00880 /* Remove marking rune */ 00881 remove_ob(con_rune); 00882 free_object(con_rune); 00883 } 00884 00885 insert_ob_in_map_at(new_item, pl->map, floor, insert_flag, x, y); 00886 if (connected != 0) 00887 add_button_link(new_item, pl->map, connected); 00888 00889 query_name(new_item, name, MAX_BUF); 00890 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00891 "You build the %s", 00892 "You build the %s", 00893 name); 00894 return 1; 00895 } 00896 00907 void apply_builder_remove(object *pl, int dir) { 00908 object *item; 00909 short x, y; 00910 char name[MAX_BUF]; 00911 00912 x = pl->x+freearr_x[dir]; 00913 y = pl->y+freearr_y[dir]; 00914 00915 /* Check square */ 00916 item = GET_MAP_OB(pl->map, x, y); 00917 if (!item) { 00918 /* Should not happen with previous tests, but we never know */ 00919 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00920 "Invalid square.", NULL); 00921 LOG(llevError, "apply_builder_remove: (null) square at (%d, %d, %s)\n", x, y, pl->map->path); 00922 return; 00923 } 00924 00925 if (item->type == FLOOR || QUERY_FLAG(item, FLAG_IS_FLOOR)) 00926 item = item->above; 00927 00928 if (!item) { 00929 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00930 "Nothing to remove.", NULL); 00931 return; 00932 } 00933 00934 /* Now remove object, with special cases (buttons & such) */ 00935 switch (item->type) { 00936 case WALL: 00937 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00938 "Can't remove a wall with that, build a floor.", NULL); 00939 return; 00940 00941 case DOOR: 00942 case BUTTON: 00943 case GATE: 00944 case TIMED_GATE: 00945 case DETECTOR: 00946 case PEDESTAL: 00947 case CF_HANDLE: 00948 case MAGIC_EAR: 00949 case SIGN: 00950 /* Special case: must unconnect */ 00951 if (QUERY_FLAG(item, FLAG_IS_LINKED)) 00952 remove_button_link(item); 00953 00954 /* Fall through */ 00955 default: 00956 /* Remove generic item */ 00957 query_name(item, name, MAX_BUF); 00958 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00959 "You remove the %s", 00960 "You remove the %s", 00961 name); 00962 remove_ob(item); 00963 free_object(item); 00964 } 00965 } 00966 00978 void apply_map_builder(object *pl, int dir) { 00979 object *builder; 00980 object *tmp; 00981 short x, y; 00982 00983 if (!pl->type == PLAYER) 00984 return; 00985 00986 if (dir == 0) { 00987 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00988 "You can't build or destroy under yourself.", NULL); 00989 return; 00990 } 00991 00992 x = pl->x+freearr_x[dir]; 00993 y = pl->y+freearr_y[dir]; 00994 00995 if ((1 > x) || (1 > y) 00996 || ((MAP_WIDTH(pl->map)-2) < x) || ((MAP_HEIGHT(pl->map)-2) < y)) { 00997 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 00998 "Can't build on map edge.", NULL); 00999 return; 01000 } 01001 01002 /* 01003 * Check specified square 01004 * The square must have only buildable items 01005 * Exception: marking runes are all right, 01006 * since they are used for special things like connecting doors / buttons 01007 */ 01008 01009 tmp = GET_MAP_OB(pl->map, x, y); 01010 if (!tmp) { 01011 /* Nothing, meaning player is standing next to an undefined square. */ 01012 LOG(llevError, "apply_map_builder: undefined square at (%d, %d, %s)\n", x, y, pl->map->path); 01013 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01014 "You'd better not build here, it looks weird.", NULL); 01015 return; 01016 } 01017 01018 builder = pl->contr->ranges[range_builder]; 01019 01020 if (builder->subtype != ST_BD_BUILD) { 01021 while (tmp) { 01022 if (!QUERY_FLAG(tmp, FLAG_IS_BUILDABLE) 01023 && ((tmp->type != SIGN) || (strcmp(tmp->arch->name, "rune_mark")))) { 01024 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01025 "You can't build here.", NULL); 01026 return; 01027 } 01028 tmp = tmp->above; 01029 } 01030 } 01031 01032 /* Now we know the square is ok */ 01033 01034 if (builder->subtype == ST_BD_REMOVE) { 01035 /* Remover -> call specific function and bail out */ 01036 apply_builder_remove(pl, dir); 01037 return; 01038 } 01039 01040 if (builder->subtype == ST_BD_BUILD) { 01041 object *material; 01042 struct archt *new_arch; 01043 object *new_item; 01044 int built = 0; 01045 01046 /* Builder -> find material, get new item, call specific function */ 01047 /* find the marked item to buld */ 01048 material = find_marked_object(pl); 01049 if (!material) { 01050 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01051 "You need to mark raw materials to use.", NULL); 01052 return; 01053 } 01054 01055 if (material->type != MATERIAL) { 01056 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01057 "You can't use the marked item to build.", NULL); 01058 return; 01059 } 01060 01061 /* create a new object from the raw materials */ 01062 new_arch = find_archetype(material->slaying); 01063 if (!new_arch) { 01064 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01065 "You can't use this strange material.", NULL); 01066 LOG(llevError, "apply_map_builder: unable to find archetype %s\n", material->slaying); 01067 return; 01068 } 01069 new_item = object_create_arch(new_arch); 01070 SET_FLAG(new_item, FLAG_IS_BUILDABLE); 01071 01072 if (!can_build_over(pl->map, new_item, x, y)) { 01073 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01074 "You can't build here.", NULL); 01075 return; 01076 } 01077 01078 /* insert the new object in the map */ 01079 switch (material->subtype) { 01080 case ST_MAT_FLOOR: 01081 built = apply_builder_floor(pl, new_item, x, y); 01082 break; 01083 01084 case ST_MAT_WALL: 01085 built = apply_builder_wall(pl, new_item, x, y); 01086 break; 01087 01088 case ST_MAT_ITEM: 01089 built = apply_builder_item(pl, new_item, x, y); 01090 break; 01091 01092 case ST_MAT_WINDOW: 01093 built = apply_builder_window(pl, new_item, x, y); 01094 break; 01095 01096 default: 01097 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01098 "Don't know how to apply this material, sorry.", NULL); 01099 LOG(llevError, "apply_map_builder: invalid material subtype %d\n", material->subtype); 01100 break; 01101 } 01102 if (built) 01103 decrease_ob(material); 01104 return; 01105 } 01106 01107 /* Here, it means the builder has an invalid type */ 01108 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BUILD, 01109 "Don't know how to apply this tool, sorry.", NULL); 01110 LOG(llevError, "apply_map_builder: invalid builder subtype %d\n", builder->subtype); 01111 }