|
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 00036 object *get_active_waypoint(object *op) 00037 { 00038 object *wp; 00039 00040 for (wp = op->inv; wp; wp = wp->below) 00041 { 00042 if (wp->type == WAYPOINT_OBJECT && QUERY_FLAG(wp, FLAG_CURSED)) 00043 { 00044 return wp; 00045 } 00046 } 00047 00048 return NULL; 00049 } 00050 00055 object *get_aggro_waypoint(object *op) 00056 { 00057 object *wp; 00058 00059 for (wp = op->inv; wp; wp = wp->below) 00060 { 00061 if (wp->type == WAYPOINT_OBJECT && QUERY_FLAG(wp, FLAG_DAMNED)) 00062 { 00063 return wp; 00064 } 00065 } 00066 00067 return NULL; 00068 } 00069 00075 object *get_return_waypoint(object *op) 00076 { 00077 object *wp; 00078 00079 for (wp = op->inv; wp; wp = wp->below) 00080 { 00081 if (wp->type == WAYPOINT_OBJECT && QUERY_FLAG(wp, FLAG_REFLECTING)) 00082 { 00083 return wp; 00084 } 00085 } 00086 00087 return NULL; 00088 } 00089 00095 static object *find_waypoint(object *op, shstr *name) 00096 { 00097 object *wp; 00098 00099 if (name == NULL) 00100 { 00101 return NULL; 00102 } 00103 00104 for (wp = op->inv; wp; wp = wp->below) 00105 { 00106 if (wp->type == WAYPOINT_OBJECT && wp->name == name) 00107 { 00108 return wp; 00109 } 00110 } 00111 00112 return NULL; 00113 } 00114 00121 static mapstruct *waypoint_load_dest(object *op, object *waypoint) 00122 { 00123 mapstruct *destmap; 00124 int unique = !strncmp(op->map->path, settings.localdir, strlen(settings.localdir)); 00125 00126 /* If path is not normalized, normalize it */ 00127 if (!unique && *waypoint->slaying != '/') 00128 { 00129 char temp_path[HUGE_BUF]; 00130 00131 if (*waypoint->slaying == '\0') 00132 { 00133 return NULL; 00134 } 00135 00136 normalize_path(op->map->path, waypoint->slaying, temp_path); 00137 FREE_AND_COPY_HASH(waypoint->slaying, temp_path); 00138 } 00139 00140 if (waypoint->slaying == op->map->path) 00141 { 00142 destmap = op->map; 00143 } 00144 else 00145 { 00146 destmap = ready_map_name(waypoint->slaying, MAP_NAME_SHARED | (unique ? MAP_PLAYER_UNIQUE : 0)); 00147 } 00148 00149 return destmap; 00150 } 00151 00157 void waypoint_compute_path(object *waypoint) 00158 { 00159 object *op = waypoint->env; 00160 mapstruct *destmap = op->map; 00161 path_node *path; 00162 00163 /* Store final path destination (used by aggro wp) */ 00164 if (QUERY_FLAG(waypoint, FLAG_DAMNED)) 00165 { 00166 if (OBJECT_VALID(waypoint->enemy, waypoint->enemy_count)) 00167 { 00168 FREE_AND_COPY_HASH(waypoint->slaying, waypoint->enemy->map->path); 00169 waypoint->x = waypoint->stats.hp = waypoint->enemy->x; 00170 waypoint->y = waypoint->stats.sp = waypoint->enemy->y; 00171 } 00172 else 00173 { 00174 LOG(llevBug, "waypoint_compute_path(): Dynamic waypoint without valid target: '%s'\n", waypoint->name); 00175 return; 00176 } 00177 } 00178 00179 if (waypoint->slaying) 00180 { 00181 destmap = waypoint_load_dest(op, waypoint); 00182 } 00183 00184 if (!destmap) 00185 { 00186 LOG(llevBug, "waypoint_compute_path(): Invalid destination map '%s'\n", waypoint->slaying); 00187 return; 00188 } 00189 00190 path = compress_path(find_path(op, op->map, op->x, op->y, destmap, waypoint->stats.hp, waypoint->stats.sp)); 00191 00192 if (!path) 00193 { 00194 LOG(llevBug, "waypoint_compute_path(): No path to destination ('%s' -> '%s')\n", op->name, waypoint->name); 00195 return; 00196 } 00197 00198 /* Skip the first path element (always the starting position) */ 00199 path = path->next; 00200 00201 if (!path) 00202 { 00203 return; 00204 } 00205 00206 #ifdef DEBUG_PATHFINDING 00207 { 00208 path_node *tmp; 00209 00210 LOG(llevDebug, "waypoint_compute_path(): '%s' new path -> '%s': ", op->name, waypoint->name); 00211 00212 for (tmp = path; tmp; tmp = tmp->next) 00213 { 00214 LOG(llevDebug, "(%d, %d) ", tmp->x, tmp->y); 00215 } 00216 00217 LOG(llevDebug, "\n"); 00218 } 00219 #endif 00220 00221 /* Textually encoded path */ 00222 FREE_AND_CLEAR_HASH(waypoint->msg); 00223 /* Map file for last local path step */ 00224 FREE_AND_CLEAR_HASH(waypoint->race); 00225 00226 waypoint->msg = encode_path(path); 00227 /* Path offset */ 00228 waypoint->stats.food = 0; 00229 /* Msg boundary */ 00230 waypoint->attacked_by_distance = strlen(waypoint->msg); 00231 00232 /* Number of fails */ 00233 waypoint->stats.Int = 0; 00234 /* Number of fails */ 00235 waypoint->stats.Str = 0; 00236 /* Best distance */ 00237 waypoint->stats.dam = 30000; 00238 CLEAR_FLAG(waypoint, FLAG_CONFUSED); 00239 } 00240 00245 void waypoint_move(object *op, object *waypoint) 00246 { 00247 mapstruct *destmap = op->map; 00248 rv_vector local_rv, global_rv, *dest_rv; 00249 int dir; 00250 sint16 new_offset = 0; 00251 00252 if (!waypoint || !op || !op->map) 00253 { 00254 return; 00255 } 00256 00257 /* Aggro or static waypoint? */ 00258 if (QUERY_FLAG(waypoint, FLAG_DAMNED)) 00259 { 00260 /* Verify enemy */ 00261 if (waypoint->enemy == op->enemy && waypoint->enemy_count == op->enemy_count && OBJECT_VALID(waypoint->enemy, waypoint->enemy_count)) 00262 { 00263 destmap = waypoint->enemy->map; 00264 waypoint->stats.hp = waypoint->enemy->x; 00265 waypoint->stats.sp = waypoint->enemy->y; 00266 } 00267 else 00268 { 00269 /* Owner has either switched or lost enemy. This should work for both cases. 00270 * switched -> similar to if target moved 00271 * lost -> we shouldn't be called again without new data */ 00272 waypoint->enemy = op->enemy; 00273 waypoint->enemy_count = op->enemy_count; 00274 return; 00275 } 00276 } 00277 else if (waypoint->slaying) 00278 { 00279 destmap = waypoint_load_dest(op, waypoint); 00280 } 00281 00282 if (!destmap) 00283 { 00284 LOG(llevBug, "waypoint_move(): Invalid destination map '%s' for '%s' -> '%s'\n", waypoint->slaying, op->name, waypoint->name); 00285 return; 00286 } 00287 00288 if (!get_rangevector_from_mapcoords(op->map, op->x, op->y, destmap, waypoint->stats.hp, waypoint->stats.sp, &global_rv, RV_RECURSIVE_SEARCH | RV_DIAGONAL_DISTANCE)) 00289 { 00290 /* Disable this waypoint */ 00291 CLEAR_FLAG(waypoint, FLAG_CURSED); 00292 return; 00293 } 00294 00295 dest_rv = &global_rv; 00296 00297 /* Reached the final destination? */ 00298 if ((int) global_rv.distance <= waypoint->stats.grace) 00299 { 00300 object *nextwp = NULL; 00301 00302 /* Just arrived? */ 00303 if (waypoint->stats.ac == 0) 00304 { 00305 #ifdef DEBUG_PATHFINDING 00306 LOG(llevDebug, "move_waypoint(): '%s' reached destination '%s'\n", op->name, waypoint->name); 00307 #endif 00308 00309 /* Trigger the TRIGGER event */ 00310 if (trigger_event(EVENT_TRIGGER, op, waypoint, NULL, NULL, 0, 0, 0, SCRIPT_FIX_NOTHING)) 00311 { 00312 return; 00313 } 00314 } 00315 00316 /* Waiting at this waypoint? */ 00317 if (waypoint->stats.ac < waypoint->stats.wc) 00318 { 00319 waypoint->stats.ac++; 00320 return; 00321 } 00322 00323 /* Clear timer */ 00324 waypoint->stats.ac = 0; 00325 /* Set as inactive */ 00326 CLEAR_FLAG(waypoint, FLAG_CURSED); 00327 /* Remove precomputed path */ 00328 FREE_AND_CLEAR_HASH(waypoint->msg); 00329 /* Remove precomputed path data */ 00330 FREE_AND_CLEAR_HASH(waypoint->race); 00331 00332 /* Is it a return-home waypoint? */ 00333 if (QUERY_FLAG(waypoint, FLAG_REFLECTING)) 00334 { 00335 op->move_type = waypoint->move_type; 00336 } 00337 00338 /* Start over with the new waypoint, if any */ 00339 if (!QUERY_FLAG(waypoint, FLAG_DAMNED)) 00340 { 00341 nextwp = find_waypoint(op, waypoint->title); 00342 00343 if (nextwp) 00344 { 00345 #ifdef DEBUG_PATHFINDING 00346 LOG(llevDebug, "waypoint_move(): '%s' next waypoint: '%s'\n", op->name, waypoint->title); 00347 #endif 00348 SET_FLAG(nextwp, FLAG_CURSED); 00349 waypoint_move(op, get_active_waypoint(op)); 00350 } 00351 #ifdef DEBUG_PATHFINDING 00352 else 00353 { 00354 LOG(llevDebug, "waypoint_move(): '%s' is missing next waypoint.\n", op->name); 00355 } 00356 #endif 00357 } 00358 00359 waypoint->enemy = NULL; 00360 return; 00361 } 00362 00363 if (HAS_EVENT(waypoint, EVENT_CLOSE) && trigger_event(EVENT_CLOSE, op, waypoint, NULL, NULL, 0, 0, 0, SCRIPT_FIX_NOTHING)) 00364 { 00365 return; 00366 } 00367 00368 /* Handle precomputed paths */ 00369 00370 /* If we finished our current path, clear it so that we can get a new one. */ 00371 if (waypoint->msg && (waypoint->msg[waypoint->stats.food] == '\0' || global_rv.distance <= 0)) 00372 { 00373 FREE_AND_CLEAR_HASH(waypoint->msg); 00374 } 00375 00376 /* Get new path if target has moved much since the path was created */ 00377 if (QUERY_FLAG(waypoint, FLAG_DAMNED) && waypoint->msg && (waypoint->stats.hp != waypoint->x || waypoint->stats.sp != waypoint->y)) 00378 { 00379 rv_vector rv; 00380 mapstruct *path_destmap = ready_map_name(waypoint->slaying, MAP_NAME_SHARED); 00381 00382 get_rangevector_from_mapcoords(destmap, waypoint->stats.hp, waypoint->stats.sp, path_destmap, waypoint->x, waypoint->y, &rv, RV_DIAGONAL_DISTANCE); 00383 00384 if (rv.distance > 1 && rv.distance > global_rv.distance) 00385 { 00386 #ifdef DEBUG_PATHFINDING 00387 LOG(llevDebug, "waypoint_move(): Path distance = %d for '%s' -> '%s'. Discarding old path.\n", rv.distance, op->name, op->enemy->name); 00388 #endif 00389 FREE_AND_CLEAR_HASH(waypoint->msg); 00390 } 00391 } 00392 00393 /* Are we far enough from the target to require a path? */ 00394 if (global_rv.distance > 1) 00395 { 00396 if (!waypoint->msg) 00397 { 00398 /* Request a path if we don't have one */ 00399 request_new_path(waypoint); 00400 } 00401 else 00402 { 00403 /* If we have precalculated path, take direction to next subwaypoint */ 00404 int destx = waypoint->stats.hp, desty = waypoint->stats.sp; 00405 00406 new_offset = waypoint->stats.food; 00407 00408 if (new_offset < waypoint->attacked_by_distance && get_path_next(waypoint->msg, &new_offset, &waypoint->race, &destmap, &destx, &desty)) 00409 { 00410 get_rangevector_from_mapcoords(op->map, op->x, op->y, destmap, destx, desty, &local_rv, RV_RECURSIVE_SEARCH | RV_DIAGONAL_DISTANCE); 00411 dest_rv = &local_rv; 00412 } 00413 else 00414 { 00415 /* We seem to have an invalid path string or offset. */ 00416 FREE_AND_CLEAR_HASH(waypoint->msg); 00417 request_new_path(waypoint); 00418 } 00419 } 00420 } 00421 00422 /* Did we get closer to our goal last time? */ 00423 if ((int) dest_rv->distance < waypoint->stats.dam) 00424 { 00425 waypoint->stats.dam = dest_rv->distance; 00426 /* Number of times we failed getting closer to (sub)goal */ 00427 waypoint->stats.Str = 0; 00428 } 00429 else if (waypoint->stats.Str++ > 4) 00430 { 00431 /* Discard the current path, so that we can get a new one */ 00432 FREE_AND_CLEAR_HASH(waypoint->msg); 00433 00434 /* For best-effort waypoints don't try too many times. */ 00435 if (QUERY_FLAG(waypoint, FLAG_NO_ATTACK) && waypoint->stats.Int++ > 10) 00436 { 00437 #ifdef DEBUG_PATHFINDING 00438 LOG(llevDebug, "Stuck with a best-effort waypoint (%s). Accepting current position\n", waypoint->name); 00439 #endif 00440 /* A bit ugly, but will work for now (we want to trigger the "reached goal" above) */ 00441 waypoint->stats.hp = op->x; 00442 waypoint->stats.sp = op->y; 00443 return; 00444 } 00445 } 00446 00447 if (global_rv.distance > 1 && !waypoint->msg && QUERY_FLAG(waypoint, FLAG_CONFUSED)) 00448 { 00449 #ifdef DEBUG_PATHFINDING 00450 LOG(llevDebug, "waypoint_move(): No path found. '%s' standing still.\n", op->name); 00451 #endif 00452 return; 00453 } 00454 00455 /* Perform the actual move */ 00456 dir = dest_rv->direction; 00457 00458 if (QUERY_FLAG(op, FLAG_CONFUSED)) 00459 { 00460 dir = get_randomized_dir(dir); 00461 } 00462 00463 if (dir && !QUERY_FLAG(op, FLAG_STAND_STILL)) 00464 { 00465 /* Can the monster move directly toward waypoint? */ 00466 if (!move_object(op, dir)) 00467 { 00468 int diff; 00469 00470 /* Try move around corners otherwise */ 00471 for (diff = 1; diff <= 2; diff++) 00472 { 00473 /* Try left or right first? */ 00474 int m = 1 - (RANDOM() & 2); 00475 00476 if (move_object(op, absdir(dir + diff * m)) || move_object(op, absdir(dir - diff * m))) 00477 { 00478 break; 00479 } 00480 } 00481 } 00482 00483 /* If we had a local destination and we got close enough to it, accept it. */ 00484 if (dest_rv == &local_rv && dest_rv->distance == 1) 00485 { 00486 waypoint->stats.food = new_offset; 00487 /* Number of fails */ 00488 waypoint->stats.Str = 0; 00489 /* Best distance */ 00490 waypoint->stats.dam = 30000; 00491 } 00492 } 00493 }
1.7.4