|
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 00039 static int can_build_over(mapstruct *m, object *new_item, int x, int y) 00040 { 00041 object *tmp; 00042 00043 for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) 00044 { 00045 tmp = HEAD(tmp); 00046 00047 if (QUERY_FLAG(tmp, FLAG_IS_BUILDABLE) || QUERY_FLAG(tmp, FLAG_SYS_OBJECT)) 00048 { 00049 continue; 00050 } 00051 00052 switch (new_item->type) 00053 { 00054 case SIGN: 00055 /* Allow signs to be built on books. */ 00056 if (tmp->type != BOOK) 00057 { 00058 return 0; 00059 } 00060 00061 break; 00062 00063 default: 00064 return 0; 00065 } 00066 } 00067 00068 /* If item being built is multi-tile, need to check other parts too. */ 00069 if (new_item->more) 00070 { 00071 return can_build_over(m, 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); 00072 } 00073 00074 return 1; 00075 } 00076 00083 static object *get_wall(mapstruct *m, int x, int y) 00084 { 00085 object *tmp; 00086 00087 for (tmp = GET_MAP_OB_LAYER(m, x, y, LAYER_WALL - 1); tmp && tmp->layer == LAYER_WALL; tmp = tmp->above) 00088 { 00089 if (tmp->type == WALL) 00090 { 00091 return tmp; 00092 } 00093 } 00094 00095 return NULL; 00096 } 00097 00105 static int builder_floor(object *op, object *new_floor, int x, int y) 00106 { 00107 object *tmp; 00108 00109 for (tmp = GET_MAP_OB_LAYER(op->map, x, y, LAYER_FLOOR - 1); tmp && tmp->layer == LAYER_FLOOR; tmp = tmp->above) 00110 { 00111 if (tmp->type == FLOOR || QUERY_FLAG(tmp, FLAG_IS_FLOOR)) 00112 { 00113 if (tmp->arch == new_floor->arch) 00114 { 00115 new_draw_info(0, COLOR_WHITE, op, "You feel too lazy to redo the exact same floor."); 00116 return 0; 00117 } 00118 00119 remove_ob(tmp); 00120 break; 00121 } 00122 } 00123 00124 if (tmp && QUERY_FLAG(tmp, FLAG_UNIQUE)) 00125 { 00126 SET_FLAG(new_floor, FLAG_UNIQUE); 00127 } 00128 00129 SET_FLAG(new_floor, FLAG_IS_FLOOR); 00130 new_floor->type = FLOOR; 00131 new_floor->x = x; 00132 new_floor->y = y; 00133 insert_ob_in_map(new_floor, op->map, NULL, 0); 00134 new_draw_info(0, COLOR_WHITE, op, "You change the floor to better suit your tastes."); 00135 00136 return 1; 00137 } 00138 00146 static int builder_item(object *op, object *new_item, int x, int y) 00147 { 00148 object *floor; 00149 int w = wall_blocked(op->map, x, y); 00150 00151 /* If it's not a wallmask, don't allow building on top of blocked squares. */ 00152 if (new_item->type != WALL && w) 00153 { 00154 new_draw_info_format(0, COLOR_WHITE, op, "Something is blocking you from building the %s on that square.", query_name(new_item, NULL)); 00155 return 0; 00156 } 00157 /* Wallmask, only allow building on top of blocked square that only 00158 * contains a wall. */ 00159 else if (new_item->type == WALL) 00160 { 00161 object *wall_ob = get_wall(op->map, x, y); 00162 00163 if (!w || !wall_ob) 00164 { 00165 new_draw_info_format(0, COLOR_WHITE, op, "The %s can only be built on top of a wall.", query_name(new_item, NULL)); 00166 return 0; 00167 } 00168 else if (wall_ob->above && wall_ob->above->type == WALL) 00169 { 00170 new_draw_info_format(0, COLOR_WHITE, op, "You first need to remove the %s before building on top of that wall again.", query_name(wall_ob->above, NULL)); 00171 return 0; 00172 } 00173 } 00174 00175 /* Only allow building if there is a floor. */ 00176 for (floor = GET_MAP_OB_LAYER(op->map, x, y, LAYER_FLOOR - 1); floor && floor->layer == LAYER_FLOOR; floor = floor->above) 00177 { 00178 if (floor->type == FLOOR || QUERY_FLAG(floor, FLAG_IS_FLOOR)) 00179 { 00180 break; 00181 } 00182 } 00183 00184 if (!floor) 00185 { 00186 new_draw_info(0, COLOR_WHITE, op, "This square has no floor, you can't build here."); 00187 return 0; 00188 } 00189 00190 /* For signs, take the msg from a book on the square. */ 00191 if (new_item->type == SIGN) 00192 { 00193 object *book; 00194 00195 for (book = GET_MAP_OB(op->map, x, y); book; book = book->above) 00196 { 00197 if (book->type == BOOK) 00198 { 00199 break; 00200 } 00201 } 00202 00203 if (!book || (!book->msg && !book->custom_name)) 00204 { 00205 new_draw_info(0, COLOR_WHITE, op, "You need to put a book with your message (or custom name) on the floor."); 00206 return 0; 00207 } 00208 00209 /* If the book has a custom name, copy it to the sign's name. */ 00210 if (book->custom_name) 00211 { 00212 FREE_AND_COPY_HASH(new_item->name, book->custom_name); 00213 } 00214 00215 /* Copy the message. */ 00216 if (book->msg) 00217 { 00218 FREE_AND_COPY_HASH(new_item->msg, book->msg); 00219 } 00220 00221 remove_ob(book); 00222 } 00223 00224 /* If the item is turnable, adjust direction. */ 00225 if (QUERY_FLAG(new_item, FLAG_IS_TURNABLE) && op->facing) 00226 { 00227 new_item->direction = op->facing; 00228 SET_ANIMATION(new_item, (NUM_ANIMATIONS(new_item) / NUM_FACINGS(new_item)) * new_item->direction); 00229 } 00230 00231 SET_FLAG(new_item, FLAG_NO_PICK); 00232 new_item->x = x; 00233 new_item->y = y; 00234 insert_ob_in_map(new_item, op->map, NULL, 0); 00235 new_draw_info_format(0, COLOR_WHITE, op, "You build the %s.", query_name(new_item, NULL)); 00236 00237 return 1; 00238 } 00239 00248 static int wall_split_orientation(const object *wall_ob, char *wall_name, size_t wall_name_size, char *orientation, size_t orientation_size) 00249 { 00250 int l; 00251 00252 strncpy(wall_name, wall_ob->arch->name, wall_name_size - 1); 00253 00254 /* Extract the wall name, which is the text up to the last '_'. */ 00255 for (l = wall_name_size; l >= 0; l--) 00256 { 00257 if (wall_name[l] == '_') 00258 { 00259 /* Copy over orientation. */ 00260 strncpy(orientation, wall_name + l, orientation_size - 1); 00261 wall_name[l] = '\0'; 00262 return 1; 00263 } 00264 } 00265 00266 return 0; 00267 } 00268 00277 static void fix_walls(mapstruct *map, int x, int y) 00278 { 00279 int connect_val; 00280 object *wall_ob; 00281 char wall_name[MAX_BUF], orientation[MAX_BUF]; 00282 uint32 old_flags[NUM_FLAGS_32]; 00283 archetype *new_arch; 00284 int flag; 00285 00286 /* First, find the wall on that spot */ 00287 wall_ob = get_wall(map, x, y); 00288 00289 if (!wall_ob || !wall_split_orientation(wall_ob, wall_name, sizeof(wall_name), orientation, sizeof(orientation))) 00290 { 00291 return; 00292 } 00293 00294 connect_val = 0; 00295 00296 if (x > 0 && get_wall(map, x - 1, y)) 00297 { 00298 connect_val |= 1; 00299 } 00300 00301 if (x < MAP_WIDTH(map) - 1 && get_wall(map, x + 1, y)) 00302 { 00303 connect_val |= 2; 00304 } 00305 00306 if (y > 0 && get_wall(map, x, y - 1)) 00307 { 00308 connect_val |= 4; 00309 } 00310 00311 if (y < MAP_HEIGHT(map) - 1 && get_wall(map, x, y + 1)) 00312 { 00313 connect_val |= 8; 00314 } 00315 00316 switch (connect_val) 00317 { 00318 case 0: 00319 return; 00320 00321 case 10: 00322 case 8: 00323 case 2: 00324 strncat(wall_name, "_8", sizeof(wall_name) - strlen(wall_name) - 1); 00325 break; 00326 00327 case 11: 00328 case 9: 00329 case 3: 00330 case 1: 00331 strncat(wall_name, "_1", sizeof(wall_name) - strlen(wall_name) - 1); 00332 break; 00333 00334 case 12: 00335 case 4: 00336 case 14: 00337 case 6: 00338 strncat(wall_name, "_3", sizeof(wall_name) - strlen(wall_name) - 1); 00339 break; 00340 00341 case 5: 00342 case 7: 00343 case 13: 00344 case 15: 00345 strncat(wall_name, "_4", sizeof(wall_name) - strlen(wall_name) - 1); 00346 break; 00347 } 00348 00349 /* No need to change anything if the old and new names are identical. */ 00350 if (!strncmp(wall_name, wall_ob->arch->name, sizeof(wall_name))) 00351 { 00352 return; 00353 } 00354 00355 /* Before anything, make sure the archetype does in fact exist... */ 00356 new_arch = find_archetype(wall_name); 00357 00358 if (!new_arch) 00359 { 00360 return; 00361 } 00362 00363 /* Now delete current wall, and insert new one 00364 * We save flags to avoid any trouble with buildable/non buildable, etc. */ 00365 for (flag = 0; flag < NUM_FLAGS_32; flag++) 00366 { 00367 old_flags[flag] = wall_ob->flags[flag]; 00368 } 00369 00370 remove_ob(wall_ob); 00371 00372 wall_ob = arch_to_object(new_arch); 00373 wall_ob->type = WALL; 00374 wall_ob->x = x; 00375 wall_ob->y = y; 00376 insert_ob_in_map(wall_ob, map, NULL, 0); 00377 00378 for (flag = 0; flag < NUM_FLAGS_32; flag++) 00379 { 00380 wall_ob->flags[flag] = old_flags[flag]; 00381 } 00382 } 00383 00391 static int builder_wall(object *op, object *new_wall, int x, int y) 00392 { 00393 object *wall_ob = get_wall(op->map, x, y); 00394 00395 if (wall_ob) 00396 { 00397 char wall_name[MAX_BUF], orientation[MAX_BUF]; 00398 00399 if (!wall_split_orientation(wall_ob, wall_name, sizeof(wall_name), orientation, sizeof(orientation))) 00400 { 00401 new_draw_info(0, COLOR_WHITE, op, "You don't see a way to redecorate that wall."); 00402 return 0; 00403 } 00404 00405 if (!strncmp(wall_ob->arch->name, wall_name, strlen(wall_name))) 00406 { 00407 new_draw_info(0, COLOR_WHITE, op, "You feel too lazy to redo the exact same wall."); 00408 return 0; 00409 } 00410 } 00411 00412 new_wall->type = WALL; 00413 00414 /* If existing wall, replace it, no need to fix other walls */ 00415 if (wall_ob) 00416 { 00417 remove_ob(wall_ob); 00418 new_wall->x = x; 00419 new_wall->y = y; 00420 insert_ob_in_map(new_wall, op->map, NULL, 0); 00421 fix_walls(op->map, x, y); 00422 new_draw_info(0, COLOR_WHITE, op, "You redecorate the wall to better suit your tastes."); 00423 } 00424 /* Else insert new wall and fix all walls around */ 00425 else 00426 { 00427 int xt, yt; 00428 00429 new_wall->x = x; 00430 new_wall->y = y; 00431 insert_ob_in_map(new_wall, op->map, NULL, 0); 00432 00433 for (xt = x - 1; xt <= x + 1; xt++) 00434 { 00435 for (yt = y - 1; yt <= y + 1; yt++) 00436 { 00437 if (OUT_OF_REAL_MAP(op->map, xt, yt)) 00438 { 00439 continue; 00440 } 00441 00442 fix_walls(op->map, xt, yt); 00443 } 00444 } 00445 00446 new_draw_info(0, COLOR_WHITE, op, "You build a wall."); 00447 } 00448 00449 return 1; 00450 } 00451 00458 static int builder_window(object *op, int x, int y) 00459 { 00460 object *wall_ob; 00461 char wall_name[MAX_BUF], orientation[MAX_BUF]; 00462 archetype *new_arch; 00463 object *window; 00464 uint32 old_flags[NUM_FLAGS_32]; 00465 int flag; 00466 00467 wall_ob = get_wall(op->map, x, y); 00468 00469 if (!wall_ob) 00470 { 00471 new_draw_info(0, COLOR_WHITE, op, "There is no wall there."); 00472 return 0; 00473 } 00474 00475 if (!wall_split_orientation(wall_ob, wall_name, sizeof(wall_name), orientation, sizeof(orientation))) 00476 { 00477 new_draw_info(0, COLOR_WHITE, op, "You don't see a way to build a window in that wall."); 00478 return 0; 00479 } 00480 else if (!strcmp(orientation, "_1") && !strcmp(orientation, "_3")) 00481 { 00482 new_draw_info(0, COLOR_WHITE, op, "You cannot build a window in that wall."); 00483 return 0; 00484 } 00485 00486 strncat(wall_name, "_w", sizeof(wall_name) - strlen(wall_name) - 1); 00487 strncat(wall_name, orientation, sizeof(wall_name) - strlen(wall_name) - 1); 00488 00489 new_arch = find_archetype(wall_name); 00490 00491 /* That type of wall doesn't have corresponding window archetype. */ 00492 if (!new_arch) 00493 { 00494 new_draw_info(0, COLOR_WHITE, op, "You cannot build a window in that wall."); 00495 return 0; 00496 } 00497 00498 /* Now delete current wall, and insert new one with a window. 00499 * We save flags to avoid any trouble with buildable/non buildable, etc. */ 00500 for (flag = 0; flag < NUM_FLAGS_32; flag++) 00501 { 00502 old_flags[flag] = wall_ob->flags[flag]; 00503 } 00504 00505 remove_ob(wall_ob); 00506 00507 window = arch_to_object(new_arch); 00508 window->type = WALL; 00509 window->x = x; 00510 window->y = y; 00511 insert_ob_in_map(window, op->map, NULL, 0); 00512 00513 for (flag = 0; flag < NUM_FLAGS_32; flag++) 00514 { 00515 window->flags[flag] = old_flags[flag]; 00516 } 00517 00518 CLEAR_FLAG(window, FLAG_BLOCKSVIEW); 00519 new_draw_info(0, COLOR_WHITE, op, "You build a window in the wall."); 00520 return 1; 00521 } 00522 00528 static void construction_builder(object *op, int x, int y) 00529 { 00530 object *material, *new_item; 00531 archetype *new_arch; 00532 int built = 0; 00533 00534 material = find_marked_object(op); 00535 00536 if (!material) 00537 { 00538 new_draw_info(0, COLOR_WHITE, op, "You need to mark raw materials to use."); 00539 return; 00540 } 00541 00542 if (material->type != MATERIAL) 00543 { 00544 new_draw_info(0, COLOR_WHITE, op, "You can't use the marked item to build."); 00545 return; 00546 } 00547 00548 /* Create a new object from the raw materials. */ 00549 new_arch = find_archetype(material->slaying); 00550 00551 if (!new_arch) 00552 { 00553 LOG(llevBug, "construction_builder(): Unable to find archetype %s.\n", material->slaying); 00554 new_draw_info(0, COLOR_WHITE, op, "You can't use this strange material."); 00555 return; 00556 } 00557 00558 new_item = arch_to_object(new_arch); 00559 SET_FLAG(new_item, FLAG_IS_BUILDABLE); 00560 00561 if (!can_build_over(op->map, new_item, x, y)) 00562 { 00563 new_draw_info(0, COLOR_WHITE, op, "You can't build there."); 00564 return; 00565 } 00566 00567 /* Insert the new object in the map. */ 00568 switch (material->sub_type) 00569 { 00570 case ST_MAT_FLOOR: 00571 built = builder_floor(op, new_item, x, y); 00572 break; 00573 00574 case ST_MAT_WALL: 00575 built = builder_wall(op, new_item, x, y); 00576 break; 00577 00578 case ST_MAT_ITEM: 00579 built = builder_item(op, new_item, x, y); 00580 break; 00581 00582 case ST_MAT_WIN: 00583 built = builder_window(op, x, y); 00584 break; 00585 00586 default: 00587 LOG(llevBug, "construction_builder(): Invalid material subtype %d.\n", material->sub_type); 00588 new_draw_info(0, COLOR_WHITE, op, "Don't know how to apply this material, sorry."); 00589 break; 00590 } 00591 00592 if (built) 00593 { 00594 decrease_ob(material); 00595 fix_player(op); 00596 } 00597 } 00598 00606 static void construction_destroyer(object *op, int x, int y) 00607 { 00608 object *item; 00609 00610 for (item = GET_MAP_OB_LAST(op->map, x, y); item; item = item->below) 00611 { 00612 if (QUERY_FLAG(item, FLAG_SYS_OBJECT)) 00613 { 00614 continue; 00615 } 00616 00617 if (item->type != FLOOR && QUERY_FLAG(item, FLAG_IS_BUILDABLE)) 00618 { 00619 break; 00620 } 00621 } 00622 00623 if (!item) 00624 { 00625 new_draw_info(0, COLOR_WHITE, op, "Nothing to remove."); 00626 return; 00627 } 00628 00629 /* Do not allow destroying containers with inventory. */ 00630 if (item->type == CONTAINER && item->inv) 00631 { 00632 new_draw_info_format(0, COLOR_WHITE, op, "You cannot remove the %s, since it contains items.", query_name(item, NULL)); 00633 return; 00634 } 00635 00636 remove_ob(item); 00637 00638 /* Fix walls around the one that was removed. */ 00639 if (item->type == WALL) 00640 { 00641 int xt, yt; 00642 00643 for (xt = x - 1; xt <= x + 1; xt++) 00644 { 00645 for (yt = y - 1; yt <= y + 1; yt++) 00646 { 00647 if (!OUT_OF_REAL_MAP(op->map, xt, yt)) 00648 { 00649 fix_walls(op->map, xt, yt); 00650 } 00651 } 00652 } 00653 } 00654 00655 new_draw_info_format(0, COLOR_WHITE, op, "You remove the %s.", query_name(item, NULL)); 00656 } 00657 00662 void construction_do(object *op, int dir) 00663 { 00664 object *skill_item, *floor, *tmp; 00665 int x, y; 00666 00667 if (op->type != PLAYER) 00668 { 00669 LOG(llevBug, "construction_do(): Construction can only be used by players.\n"); 00670 return; 00671 } 00672 00673 skill_item = CONTR(op)->equipment[PLAYER_EQUIP_SKILL_ITEM]; 00674 00675 if (!skill_item) 00676 { 00677 new_draw_info(0, COLOR_WHITE, op, "You need to apply a skill item to use this skill."); 00678 return; 00679 } 00680 00681 if (skill_item->stats.sp != SK_CONSTRUCTION) 00682 { 00683 new_draw_info_format(0, COLOR_WHITE, op, "The %s cannot be used with the construction skill.", query_name(skill_item, NULL)); 00684 return; 00685 } 00686 00687 if (!dir) 00688 { 00689 new_draw_info(0, COLOR_WHITE, op, "You can't build or destroy under yourself."); 00690 return; 00691 } 00692 00693 x = op->x + freearr_x[dir]; 00694 y = op->y + freearr_y[dir]; 00695 00696 if ((1 > x) || (1 > y) || ((MAP_WIDTH(op->map) - 2) < x) || ((MAP_HEIGHT(op->map) - 2) < y)) 00697 { 00698 new_draw_info(0, COLOR_WHITE, op, "Can't build on map edge."); 00699 return; 00700 } 00701 00702 /* Check specified square 00703 * The square must have only buildable items. */ 00704 floor = GET_MAP_OB(op->map, x, y); 00705 00706 if (!floor) 00707 { 00708 LOG(llevBug, "construction_do(): Undefined square on map %s (%d, %d)\n", op->map->path, x, y); 00709 new_draw_info(0, COLOR_WHITE, op, "You'd better not build here, it looks weird."); 00710 return; 00711 } 00712 00713 if (skill_item->sub_type != ST_BD_BUILD) 00714 { 00715 for (tmp = floor; tmp; tmp = tmp->above) 00716 { 00717 if (QUERY_FLAG(tmp, FLAG_SYS_OBJECT)) 00718 { 00719 continue; 00720 } 00721 00722 if (!QUERY_FLAG(tmp, FLAG_IS_BUILDABLE)) 00723 { 00724 new_draw_info(0, COLOR_WHITE, op, "You can't build there."); 00725 return; 00726 } 00727 } 00728 } 00729 00730 /* Prevent players without destroyer from getting themselves stuck. */ 00731 if (skill_item->sub_type != ST_BD_REMOVE) 00732 { 00733 int found_destroyer = 0; 00734 00735 for (tmp = op->inv; tmp; tmp = tmp->below) 00736 { 00737 if (tmp->type == SKILL_ITEM && tmp->sub_type == ST_BD_REMOVE) 00738 { 00739 found_destroyer = 1; 00740 break; 00741 } 00742 } 00743 00744 if (!found_destroyer) 00745 { 00746 new_draw_info(0, COLOR_WHITE, op, "You cannot build without having a destroyer at hand."); 00747 return; 00748 } 00749 } 00750 00751 switch (skill_item->sub_type) 00752 { 00753 case ST_BD_REMOVE: 00754 construction_destroyer(op, x, y); 00755 break; 00756 00757 case ST_BD_BUILD: 00758 construction_builder(op, x, y); 00759 break; 00760 00761 default: 00762 LOG(llevBug, "Skill item %s has invalid subtype.\n", query_name(skill_item, NULL)); 00763 new_draw_info(0, COLOR_WHITE, op, "Don't know how to apply this tool, sorry."); 00764 break; 00765 } 00766 }
1.7.4