Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_move_c = 00003 * "$Id: move.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The author can be reached via e-mail to crossfire-devel@real-time.com 00027 */ 00028 00034 #include <global.h> 00035 #ifndef __CEXTRACT__ 00036 #include <sproto.h> 00037 #endif 00038 00039 static int roll_ob(object *op, int dir, object *pusher); 00040 00053 int move_object(object *op, int dir) { 00054 return move_ob(op, dir, op); 00055 } 00056 00072 int move_ob(object *op, int dir, object *originator) { 00073 sint16 newx = op->x+freearr_x[dir]; 00074 sint16 newy = op->y+freearr_y[dir]; 00075 object *tmp; 00076 mapstruct *m; 00077 int mflags; 00078 00079 if (op == NULL) { 00080 LOG(llevError, "Trying to move NULL.\n"); 00081 return 0; 00082 } 00083 00084 m = op->map; 00085 mflags = get_map_flags(m, &m, newx, newy, &newx, &newy); 00086 00087 /* If the space the player is trying to is out of the map, 00088 * bail now - we know it can't work. 00089 */ 00090 if (mflags&P_OUT_OF_MAP) 00091 return 0; 00092 00093 00094 /* Is this space blocked? Players with wizpass are immune to 00095 * this condition. 00096 */ 00097 if (blocked_link(op, m, newx, newy) 00098 && !QUERY_FLAG(op, FLAG_WIZPASS)) 00099 return 0; 00100 00101 /* 0.94.2 - we need to set the direction for the new animation code. 00102 * it uses it to figure out face to use - I can't see it 00103 * breaking anything, but it might. 00104 */ 00105 if (op->more != NULL && !move_ob(op->more, dir, op->more->head)) 00106 return 0; 00107 00108 op->direction = dir; 00109 00110 if (op->will_apply&WILL_APPLY_EARTHWALL) 00111 check_earthwalls(op, m, newx, newy); 00112 if (op->will_apply&WILL_APPLY_DOOR) 00113 check_doors(op, m, newx, newy); 00114 00115 /* 0.94.1 - I got a stack trace that showed it crash with remove_ob trying 00116 * to remove a removed object, and this function was the culprit. A possible 00117 * guess I have is that check_doors above ran into a trap, killing the 00118 * monster. 00119 * 00120 * Unfortunately, it doesn't appear that the calling functions of move_object 00121 * deal very well with op being killed, so all this might do is just 00122 * migrate the problem someplace else. 00123 */ 00124 00125 if (QUERY_FLAG(op, FLAG_REMOVED)) { 00126 LOG(llevDebug, "move_object: monster has been removed - will not process further\n"); 00127 /* Was not successful, but don't want to try and move again */ 00128 return 1; 00129 } 00130 00131 /* If this is a tail portion, just want to tell caller that move is 00132 * ok - the caller will deal with actual object removal/insertion 00133 */ 00134 if (op->head) 00135 return 1; 00136 00137 remove_ob(op); 00138 00139 /* we already have newx, newy, and m, so lets use them. 00140 * In addition, this fixes potential crashes, because multipart object was 00141 * on edge of map, +=x, +=y doesn't make correct coordinates. 00142 */ 00143 for (tmp = op; tmp != NULL; tmp = tmp->more) { 00144 tmp->x += freearr_x[dir]; 00145 tmp->y += freearr_y[dir]; 00146 tmp->map = get_map_from_coord(tmp->map, &tmp->x, &tmp->y); 00147 } 00148 00149 /* insert_ob_in_map will deal with any tiling issues */ 00150 insert_ob_in_map(op, m, originator, 0); 00151 00152 /* Hmmm. Should be possible for multispace players now */ 00153 if (op->type == PLAYER) { 00154 esrv_map_scroll(&op->contr->socket, freearr_x[dir], freearr_y[dir]); 00155 op->contr->socket.update_look = 1; 00156 op->contr->socket.look_position = 0; 00157 } else if (op->type == TRANSPORT) { 00158 object *pl; 00159 00160 for (pl = op->inv; pl; pl = pl->below) { 00161 if (pl->type == PLAYER) { 00162 pl->contr->do_los = 1; 00163 pl->map = op->map; 00164 pl->x = op->x; 00165 pl->y = op->y; 00166 esrv_map_scroll(&pl->contr->socket, freearr_x[dir], freearr_y[dir]); 00167 pl->contr->socket.update_look = 1; 00168 pl->contr->socket.look_position = 0; 00169 } 00170 } 00171 } 00172 00173 return 1; /* this shouldn't be reached */ 00174 } 00175 00197 int transfer_ob(object *op, int x, int y, int randomly, object *originator) { 00198 int i; 00199 object *tmp; 00200 00201 if (randomly) 00202 i = find_free_spot(op, op->map, x, y, 0, SIZEOFFREE); 00203 else 00204 i = find_first_free_spot(op, op->map, x, y); 00205 00206 if (i == -1) 00207 return 0; /* No free spot */ 00208 00209 if (op->head != NULL) 00210 op = op->head; 00211 remove_ob(op); 00212 for (tmp = op; tmp != NULL; tmp = tmp->more) 00213 tmp->x = x+freearr_x[i]+(tmp->arch == NULL ? 0 : tmp->arch->clone.x), 00214 tmp->y = y+freearr_y[i]+(tmp->arch == NULL ? 0 : tmp->arch->clone.y); 00215 00216 tmp = insert_ob_in_map(op, op->map, originator, 0); 00217 if (op && op->type == PLAYER) 00218 map_newmap_cmd(&op->contr->socket); 00219 if (tmp) 00220 return 0; 00221 else 00222 return 1; 00223 } 00224 00244 int teleport(object *teleporter, uint8 tele_type, object *user) { 00245 object *altern[120]; /* Better use c/malloc here in the future */ 00246 int i, j, k, nrofalt = 0; 00247 object *other_teleporter, *tmp; 00248 mapstruct *m; 00249 sint16 sx, sy; 00250 00251 if (user == NULL) 00252 return 0; 00253 if (user->head != NULL) 00254 user = user->head; 00255 00256 /* Find all other teleporters within range. This range 00257 * should really be setable by some object attribute instead of 00258 * using hard coded values. 00259 */ 00260 for (i = -5; i < 6; i++) 00261 for (j = -5; j < 6; j++) { 00262 if (i == 0 && j == 0) 00263 continue; 00264 /* Perhaps this should be extended to support tiled maps */ 00265 if (OUT_OF_REAL_MAP(teleporter->map, teleporter->x+i, teleporter->y+j)) 00266 continue; 00267 other_teleporter = GET_MAP_OB(teleporter->map, teleporter->x+i, teleporter->y+j); 00268 00269 while (other_teleporter) { 00270 if (other_teleporter->type == tele_type) 00271 break; 00272 other_teleporter = other_teleporter->above; 00273 } 00274 if (other_teleporter) 00275 altern[nrofalt++] = other_teleporter; 00276 } 00277 00278 if (!nrofalt) { 00279 LOG(llevError, "No alternative teleporters around!\n"); 00280 return 0; 00281 } 00282 00283 other_teleporter = altern[RANDOM()%nrofalt]; 00284 k = find_free_spot(user, other_teleporter->map, other_teleporter->x, other_teleporter->y, 1, 9); 00285 00286 /* if k==-1, unable to find a free spot. If this is shop 00287 * mat that the player is using, find someplace to move 00288 * the player - otherwise, player can get trapped in the shops 00289 * that appear in random dungeons. We basically just make 00290 * sure the space isn't no pass (eg wall), and don't care 00291 * about is alive. 00292 */ 00293 if (k == -1) { 00294 if (tele_type == SHOP_MAT && user->type == PLAYER) { 00295 for (k = 1; k < 9; k++) { 00296 if (get_map_flags(other_teleporter->map, &m, 00297 other_teleporter->x+freearr_x[k], 00298 other_teleporter->y+freearr_y[k], &sx, &sy)&P_OUT_OF_MAP) 00299 continue; 00300 00301 if (!OB_TYPE_MOVE_BLOCK(user, GET_MAP_MOVE_BLOCK(m, sx, sy))) 00302 break; 00303 00304 } 00305 if (k == 9) { 00306 LOG(llevError, "Shop mat %s (%d, %d) is in solid rock?\n", other_teleporter->name, other_teleporter->x, other_teleporter->y); 00307 /* Teleport player on top of blocked destination: this prevents 00308 * players from being trapped inside shops if the destination 00309 * is blocked with earth walls. 00310 */ 00311 k = 0; 00312 } 00313 } else 00314 return 0; 00315 } 00316 00317 remove_ob(user); 00318 00319 /* Update location for the object */ 00320 for (tmp = user; tmp != NULL; tmp = tmp->more) { 00321 tmp->x = other_teleporter->x+freearr_x[k]+(tmp->arch == NULL ? 0 : tmp->arch->clone.x); 00322 tmp->y = other_teleporter->y+freearr_y[k]+(tmp->arch == NULL ? 0 : tmp->arch->clone.y); 00323 } 00324 tmp = insert_ob_in_map(user, other_teleporter->map, NULL, 0); 00325 if (tmp && tmp->type == PLAYER) 00326 map_newmap_cmd(&tmp->contr->socket); 00327 return (tmp == NULL); 00328 } 00329 00340 void recursive_roll(object *op, int dir, object *pusher) { 00341 char name[MAX_BUF]; 00342 00343 query_name(op, name, MAX_BUF); 00344 if (!roll_ob(op, dir, pusher)) { 00345 draw_ext_info_format(NDI_UNIQUE, 0, pusher, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 00346 "You fail to push the %s.", 00347 "You fail to push the %s.", 00348 name); 00349 return; 00350 } 00351 (void)move_ob(pusher, dir, pusher); 00352 draw_ext_info_format(NDI_BLACK, 0, pusher, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 00353 "You move the %s.", 00354 "You move the %s.", 00355 name); 00356 return; 00357 } 00358 00380 static int try_fit(object *op, mapstruct *m, int x, int y) { 00381 object *tmp, *more; 00382 sint16 tx, ty; 00383 int mflags; 00384 mapstruct *m2; 00385 00386 if (op->head) 00387 op = op->head; 00388 00389 for (more = op; more; more = more->more) { 00390 tx = x+more->x-op->x; 00391 ty = y+more->y-op->y; 00392 00393 mflags = get_map_flags(m, &m2, tx, ty, &tx, &ty); 00394 00395 if (mflags&P_OUT_OF_MAP) 00396 return 1; 00397 00398 for (tmp = GET_MAP_OB(m2, tx, ty); tmp; tmp = tmp->above) { 00399 if (tmp->head == op || tmp == op) 00400 continue; 00401 00402 if ((QUERY_FLAG(tmp, FLAG_ALIVE) && tmp->type != DOOR)) 00403 return 1; 00404 00405 if (OB_MOVE_BLOCK(op, tmp)) 00406 return 1; 00407 00408 } 00409 } 00410 return 0; 00411 } 00412 00432 static int roll_ob(object *op, int dir, object *pusher) { 00433 object *tmp; 00434 sint16 x, y; 00435 int flags; 00436 mapstruct *m; 00437 MoveType move_block; 00438 00439 if (op->head) 00440 op = op->head; 00441 00442 x = op->x+freearr_x[dir]; 00443 y = op->y+freearr_y[dir]; 00444 00445 if (!QUERY_FLAG(op, FLAG_CAN_ROLL) 00446 || (op->weight && random_roll(0, op->weight/50000-1, pusher, PREFER_LOW) > pusher->stats.Str)) 00447 return 0; 00448 00449 m = op->map; 00450 flags = get_map_flags(m, &m, x, y, &x, &y); 00451 00452 if (flags&(P_OUT_OF_MAP|P_IS_ALIVE)) 00453 return 0; 00454 00455 move_block = GET_MAP_MOVE_BLOCK(m, x, y); 00456 00457 /* If the target space is not blocked, no need to look at the objects on it */ 00458 if ((op->move_type&move_block) == op->move_type) { 00459 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) { 00460 if (tmp->head == op) 00461 continue; 00462 if (OB_MOVE_BLOCK(op, tmp) && !roll_ob(tmp, dir, pusher)) 00463 return 0; 00464 } 00465 } 00466 if (try_fit(op, m, x, y)) 00467 return 0; 00468 00469 remove_ob(op); 00470 for (tmp = op; tmp != NULL; tmp = tmp->more) 00471 tmp->x += freearr_x[dir], 00472 tmp->y += freearr_y[dir]; 00473 insert_ob_in_map(op, op->map, pusher, 0); 00474 return 1; 00475 } 00476 00492 int push_ob(object *who, int dir, object *pusher) { 00493 int str1, str2; 00494 object *owner; 00495 00496 if (who->head != NULL) 00497 who = who->head; 00498 owner = get_owner(who); 00499 00500 /* Wake up sleeping monsters that may be pushed */ 00501 CLEAR_FLAG(who, FLAG_SLEEP); 00502 00503 /* player change place with his pets or summoned creature */ 00504 /* TODO: allow multi arch pushing. Can't be very difficult */ 00505 if (who->more == NULL 00506 && (owner == pusher || (owner != NULL && owner->type == PLAYER && owner->contr->party != NULL && owner->contr->party == pusher->contr->party))) { 00507 int temp; 00508 mapstruct *m; 00509 00510 remove_ob(who); 00511 remove_ob(pusher); 00512 temp = pusher->x; 00513 pusher->x = who->x; 00514 who->x = temp; 00515 00516 temp = pusher->y; 00517 pusher->y = who->y; 00518 who->y = temp; 00519 00520 m = pusher->map; 00521 pusher->map = who->map; 00522 who->map = m; 00523 00524 insert_ob_in_map(who, who->map, pusher, 0); 00525 insert_ob_in_map(pusher, pusher->map, pusher, 0); 00526 00527 /* we presume that if the player is pushing his put, he moved in 00528 * direction 'dir'. I can' think of any case where this would not be 00529 * the case. Putting the map_scroll should also improve performance some. 00530 */ 00531 if (pusher->type == PLAYER) { 00532 esrv_map_scroll(&pusher->contr->socket, freearr_x[dir], freearr_y[dir]); 00533 pusher->contr->socket.update_look = 1; 00534 pusher->contr->socket.look_position = 0; 00535 } 00536 return 0; 00537 } 00538 00539 /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */ 00540 /* In original we have here a unaggressive check only - that was the reason why */ 00541 /* we so often become an enemy of friendly monsters... */ 00542 /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */ 00543 if (owner != pusher 00544 && pusher->type == PLAYER 00545 && who->type != PLAYER 00546 && !QUERY_FLAG(who, FLAG_FRIENDLY)&& !QUERY_FLAG(who, FLAG_NEUTRAL)) { 00547 if (pusher->contr->run_on) { /* only when we run */ 00548 draw_ext_info_format(NDI_UNIQUE, 0, pusher, 00549 MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 00550 "You start to attack %s !!", 00551 "You start to attack %s !!", 00552 who->name); 00553 CLEAR_FLAG(who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */ 00554 who->enemy = pusher; 00555 return 1; 00556 } else { 00557 draw_ext_info_format(NDI_UNIQUE, 0, pusher, 00558 MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 00559 "You avoid attacking %s.", 00560 "You avoid attacking %s.", 00561 who->name); 00562 } 00563 } 00564 00565 /* now, lets test stand still. we NEVER can push stand_still monsters. */ 00566 if (QUERY_FLAG(who, FLAG_STAND_STILL)) { 00567 draw_ext_info_format(NDI_UNIQUE, 0, pusher, 00568 MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 00569 "You can't push %s.", 00570 "You can't push %s.", 00571 who->name); 00572 return 0; 00573 } 00574 00575 /* This block is basically if you are pushing friendly but 00576 * non pet creaturs. 00577 * It basically does a random strength comparision to 00578 * determine if you can push someone around. Note that 00579 * this pushes the other person away - its not a swap. 00580 */ 00581 00582 str1 = (who->stats.Str > 0 ? who->stats.Str : who->level); 00583 str2 = (pusher->stats.Str > 0 ? pusher->stats.Str : pusher->level); 00584 if (QUERY_FLAG(who, FLAG_WIZ) 00585 || random_roll(str1, str1/2+str1*2, who, PREFER_HIGH) >= random_roll(str2, str2/2+str2*2, pusher, PREFER_HIGH) 00586 || !move_object(who, dir)) { 00587 if (who->type == PLAYER) { 00588 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_PUSHED, 00589 "%s tried to push you.", 00590 "%s tried to push you.", 00591 pusher->name); 00592 } 00593 return 0; 00594 } 00595 00596 /* If we get here, the push succeeded. 00597 * Let everyone know the status. 00598 */ 00599 if (who->type == PLAYER) { 00600 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_PUSHED, 00601 "%s pushed you.", 00602 "%s pushed you.", 00603 pusher->name); 00604 } 00605 if (pusher->type == PLAYER) { 00606 draw_ext_info_format(NDI_UNIQUE, 0, pusher, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_PUSHED, 00607 "You pushed %s back.", 00608 "You pushed %s back.", 00609 who->name); 00610 } 00611 00612 return 1; 00613 } 00614 00626 int move_to(object *op, int x, int y) { 00627 int direction; 00628 00629 if (op->x == x && op->y == y) 00630 return 0; 00631 00632 if (GET_MAP_FLAGS(op->map, x, y)&P_OUT_OF_MAP) 00633 return 2; 00634 00635 direction = compute_path(op, GET_MAP_OB(op->map, x, y), -1); 00636 if (direction == -1) 00637 return 2; 00638 00639 /* this shouldn't fail, as the direction computing takes into account the blocked state... */ 00640 move_ob(op, direction, op); 00641 return 1; 00642 }