paths.cc

Go to the documentation of this file.
00001 /*
00002  *  Paths.cc - Various pathfinding clients.
00003  *
00004  *  Copyright (C) 2000-2001  The Exult Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #  include <config.h>
00023 #endif
00024 
00025 #include "paths.h"
00026 #include "Astar.h"
00027 #include "Zombie.h"
00028 #include "gamewin.h"
00029 #include "gamemap.h"
00030 #include "actors.h"
00031 
00032 /*
00033  *  Create for given NPC.
00034  */
00035 
00036 Actor_pathfinder_client::Actor_pathfinder_client
00037   (
00038   Actor *n,
00039   int d
00040   ) : dist(d), npc(n)
00041   {
00042           // +++++Shouldn't need this anymore?
00043   set_move_flags(npc->get_type_flags());
00044   }
00045 
00046 /*
00047  *  Figure when to give up.
00048  */
00049 
00050 int Actor_pathfinder_client::get_max_cost
00051   (
00052   int cost_to_goal    // From estimate_cost().
00053   )
00054   {
00055   int max_cost = 3*cost_to_goal;
00056   Game_window *gwin = Game_window::get_instance();
00057           // Do at least 3 screens width.
00058   int min_max_cost = (gwin->get_width()/c_tilesize)*2*3;
00059   return max_cost > min_max_cost ? max_cost : min_max_cost;
00060   }
00061 
00062 /*
00063  *  Figure cost going from one tile to an adjacent tile (for pathfinding).
00064  *
00065  *  Output: Cost, or -1 if blocked.
00066  *    The 'tz' field in tile may be modified.
00067  */
00068 
00069 int Actor_pathfinder_client::get_step_cost
00070   (
00071   Tile_coord from,
00072   Tile_coord& to      // The tile we're going to.  The 'tz'
00073           //   field may be modified.
00074   )
00075   {
00076   Game_window *gwin = Game_window::get_instance();
00077   int cx = to.tx/c_tiles_per_chunk, cy = to.ty/c_tiles_per_chunk;
00078   Map_chunk *olist = gwin->get_map()->get_chunk(cx, cy);
00079   int tx = to.tx%c_tiles_per_chunk; // Get tile within chunk.
00080   int ty = to.ty%c_tiles_per_chunk;
00081   int cost = 1;
00082   olist->setup_cache();   // Make sure cache is valid.
00083   int water, poison;    // Get tile info.
00084   Actor::get_tile_info(0, gwin, olist, tx, ty, water, poison);
00085   int old_lift = to.tz;   // Might climb/descend.
00086   if (npc->is_blocked(to, &from))
00087     {     // Blocked, but check for a door.
00088     Game_object *block = Game_object::find_door(to);
00089     if (!block)
00090       return -1;
00091     if (!block->is_closed_door() ||
00092           // Can't get past locked doors.
00093         block->get_framenum()%4 >= 2)
00094       return -1;
00095           // Can't be either end of door.
00096     Rectangle foot = block->get_footprint();
00097     if (foot.h == 1 && (to.tx == foot.x || to.tx == foot.x +
00098               foot.w - 1))
00099       return -1;
00100     else if (foot.w == 1 && (to.ty == foot.y || to.ty == foot.y + 
00101               foot.h - 1))
00102       return -1;
00103     if (foot.has_point(from.tx, from.ty))
00104       return -1;  // Don't walk within doorway.
00105     cost++;     // But try to avoid them.
00106     }
00107   if (old_lift != to.tz)
00108     cost++;
00109           // On the diagonal?
00110   if (from.tx != to.tx || from.ty != to.ty)
00111     cost *= 3;    // Make it 50% more expensive.
00112   else
00113     cost *= 2;
00114   if (poison && to.tz == 0)
00115     cost *= 2;    // And avoid poison if possible.
00116           // Get 'flat' shapenum.
00117   ShapeID flat = olist->get_flat(tx, ty);
00118   int shapenum = flat.get_shapenum();
00119   if (shapenum == 24)   // Cobblestone path in BlackGate?
00120     {
00121     int framenum = flat.get_framenum();
00122     if (framenum <= 1)
00123       cost--;
00124     }
00125   return (cost);
00126   }
00127 
00128 /*
00129  *  Estimate cost from one point to another.
00130  */
00131 
00132 int Actor_pathfinder_client::estimate_cost
00133   (
00134   Tile_coord& from,
00135   Tile_coord& to
00136   )
00137   {
00138   int dx = to.tx - from.tx;
00139   if (dx < -c_num_tiles/2)  // Wrap around the world.
00140     dx += c_num_tiles;
00141   else if (dx < 0)
00142     dx = -dx;
00143   int dy = to.ty - from.ty;
00144   if (dy < -c_num_tiles/2)
00145     dy += c_num_tiles;
00146   else if (dy < 0)
00147     dy = -dy;
00148   int larger, smaller;    // Start with larger.
00149   if (dy <= dx)
00150     {
00151     larger = dx;
00152     smaller = dy;
00153     }
00154   else
00155     {
00156     larger = dy;
00157     smaller = dx;
00158     }
00159   return (2*larger + smaller);  // Straight = 2, diag = 3.
00160   }
00161 
00162 /*
00163  *  Is tile at goal?
00164  */
00165 
00166 int Actor_pathfinder_client::at_goal
00167   (
00168   Tile_coord& tile,
00169   Tile_coord& goal
00170   )
00171   {
00172   return tile.distance(goal) <= dist &&
00173     (goal.tz == -1 || tile.tz == goal.tz);
00174   }
00175 
00176 /*
00177  *  Estimate cost from one point to another.
00178  */
00179 
00180 int Onecoord_pathfinder_client::estimate_cost
00181   (
00182   Tile_coord& from,
00183   Tile_coord& to      // Should be the goal.
00184   )
00185   {
00186   if (to.tx == -1)    // Just care about Y?
00187     {     // Cost = 2/tile.
00188     int dy = to.ty - from.ty;
00189     if (dy < -c_num_tiles/2)
00190       dy += c_num_tiles;
00191     else if (dy < 0)
00192       dy = -dy;
00193     return (2*dy);
00194     }
00195   else if (to.ty == -1)
00196     {
00197     int dx = to.tx - from.tx;
00198     if (dx < -c_num_tiles/2)
00199       dx += c_num_tiles;
00200     else if (dx < 0)
00201       dx = -dx;
00202     return (2*dx);
00203     }
00204   else        // Shouldn't get here.
00205     return Actor_pathfinder_client::estimate_cost(from, to);
00206   }
00207 
00208 /*
00209  *  Is tile at goal?
00210  */
00211 
00212 int Onecoord_pathfinder_client::at_goal
00213   (
00214   Tile_coord& tile,
00215   Tile_coord& goal
00216   )
00217   {
00218   return ((goal.tx == -1 || tile.tx == goal.tx) && 
00219     (goal.ty == -1 || tile.ty == goal.ty) &&
00220     (goal.tz == -1 || tile.tz == goal.tz));
00221   }
00222 
00223 /*
00224  *  Client to get offscreen.
00225  */
00226 
00227 Offscreen_pathfinder_client::Offscreen_pathfinder_client
00228   (
00229   Actor *n
00230   ) : Actor_pathfinder_client(n), screen(
00231         Game_window::get_instance()->get_win_tile_rect().enlarge(3)),
00232       best(-1, -1, -1)
00233   {
00234   }
00235 
00236 /*
00237  *  Client to get offscreen.
00238  */
00239 
00240 Offscreen_pathfinder_client::Offscreen_pathfinder_client
00241   (
00242   Actor *n,
00243   Tile_coord b      // Best offscreen pt. to aim for.
00244   ) : Actor_pathfinder_client(n), screen(
00245         Game_window::get_instance()->get_win_tile_rect().enlarge(3)),
00246       best(b)
00247   {
00248   if (best.tx != -1)    // Scale (roughly) to edge of screen.
00249     {
00250     Rectangle scr = 
00251       Game_window::get_instance()->get_win_tile_rect();
00252           // Get center.
00253     int cx = scr.x + scr.w/2, cy = scr.y + scr.h/2;
00254           // More than 4 screens away?
00255     if (best.distance(Tile_coord(cx, cy, 0)) > 4*scr.w)
00256       {
00257       best.tx = best.ty = -1;
00258       return;
00259       }
00260     if (best.tx > cx + scr.w)
00261           // Too far to right of screen.
00262       best.tx = scr.x + scr.w + 1;
00263     else if (best.tx < cx - scr.w)
00264       best.tx = scr.x - 1;
00265     if (best.ty > cy + scr.h)
00266       best.ty = scr.y + scr.h + 1;
00267     else if (best.ty < cy - scr.h)
00268       best.ty = scr.y - 1;
00269           // Give up if it doesn't look right.
00270     if (best.distance(Tile_coord(cx, cy, 0)) > scr.w)
00271       best.tx = best.ty = -1;
00272     }
00273   }
00274 
00275 /*
00276  *  Figure cost going from one tile to an adjacent tile (for pathfinding).
00277  *
00278  *  Output: Cost, or -1 if blocked.
00279  *    The 'tz' field in tile may be modified.
00280  */
00281 
00282 int Offscreen_pathfinder_client::get_step_cost
00283   (
00284   Tile_coord from,
00285   Tile_coord& to      // The tile we're going to.  The 'tz'
00286           //   field may be modified.
00287   )
00288   {
00289   int cost = Actor_pathfinder_client::get_step_cost(from, to);
00290   if (cost == -1)
00291     return cost;
00292   if (best.tx != -1)    // Penalize for moving away from best.
00293     {
00294     if ((to.tx - from.tx)*(best.tx - from.tx) < 0)
00295       cost++;
00296     if ((to.ty - from.ty)*(best.ty - from.ty) < 0)
00297       cost++;
00298     }
00299   return cost;
00300   }
00301 
00302 /*
00303  *  Estimate cost from one point to another.
00304  */
00305 
00306 int Offscreen_pathfinder_client::estimate_cost
00307   (
00308   Tile_coord& from,
00309   Tile_coord& to      // Should be the goal.
00310   )
00311   {
00312   if (best.tx != -1)    // Off-screen goal?
00313     return Actor_pathfinder_client::estimate_cost(from, best);
00314 //++++++World-wrapping here????
00315   int dx = from.tx - screen.x;  // Figure shortest dist.
00316   int dx1 = screen.x + screen.w - from.tx;
00317   if (dx1 < dx)
00318     dx = dx1;
00319   int dy = from.ty - screen.y;
00320   int dy1 = screen.y + screen.h - from.ty;
00321   if (dy1 < dy)
00322     dy = dy1;
00323   int cost = dx < dy ? dx : dy;
00324   if (cost < 0)
00325     cost = 0;
00326   if (to.tz != -1 && from.tz != to.tz)
00327     cost++;
00328   return 2*cost;
00329   }
00330 
00331 /*
00332  *  Is tile at goal?
00333  */
00334 
00335 int Offscreen_pathfinder_client::at_goal
00336   (
00337   Tile_coord& tile,
00338   Tile_coord& goal
00339   )
00340   {
00341   return !screen.has_point(tile.tx - tile.tz/2, tile.ty - tile.tz/2);//&&
00342           // Z-coord shouldn't matter.
00343 //    (goal.tz == -1 || tile.tz == goal.tz);
00344   }
00345 
00346 /*
00347  *  Figure when to give up.
00348  */
00349 
00350 int Fast_pathfinder_client::get_max_cost
00351   (
00352   int cost_to_goal    // From estimate_cost().
00353   )
00354   {
00355   int max_cost = 2*cost_to_goal;  // Don't try too hard.
00356   if (max_cost < 8)
00357     max_cost = 8;
00358   else if (max_cost > 64)
00359     max_cost = 64;
00360   return max_cost;
00361   }
00362 
00363 /*
00364  *  Figure cost going from one tile to an adjacent tile (for pathfinding).
00365  *
00366  *  Output: Cost, or -1 if blocked.
00367  *    The 'tz' field in tile may be modified.
00368  */
00369 
00370 int Fast_pathfinder_client::get_step_cost
00371   (
00372   Tile_coord from,
00373   Tile_coord& to      // The tile we're going to.  The 'tz'
00374           //   field may be modified.
00375   )
00376   {
00377   Game_window *gwin = Game_window::get_instance();
00378   int cx = to.tx/c_tiles_per_chunk, cy = to.ty/c_tiles_per_chunk;
00379   Map_chunk *olist = gwin->get_map()->get_chunk(cx, cy);
00380   int tx = to.tx%c_tiles_per_chunk; // Get tile within chunk.
00381   int ty = to.ty%c_tiles_per_chunk;
00382   olist->setup_cache();   // Make sure cache is valid.
00383   int new_lift;     // Might climb/descend.
00384           // For now, look at 1 tile's height,
00385           //   and step up/down 2 (needed for SI
00386           //   Crystal Ball).
00387   if (olist->is_blocked(1, to.tz, tx, ty, new_lift, get_move_flags(), 
00388                   2, 2))
00389     return -1;
00390   to.tz = new_lift;   // (I don't think we should do this
00391           //    above...)
00392   return 1;
00393   }
00394 
00395 /*
00396  *  Estimate cost from one point to another.
00397  */
00398 
00399 int Fast_pathfinder_client::estimate_cost
00400   (
00401   Tile_coord& from,
00402   Tile_coord& to
00403   )
00404   {
00405   return from.distance(to); // Distance() does world-wrapping.
00406   }
00407 
00408 /*
00409  *  Is tile at goal?
00410  */
00411 
00412 int Fast_pathfinder_client::at_goal
00413   (
00414   Tile_coord& tile,
00415   Tile_coord& goal
00416   )
00417   {
00418   if (tile.distance(goal) > dist)
00419     return 0;   // Not close enough.
00420   int dz = tile.tz - goal.tz; // Want to be within 1 story.
00421   return dz <= 5 && dz >= -5;
00422   }
00423 
00424 /*
00425  *  Just test to see if an object can be grabbed.
00426  */
00427 
00428 int Fast_pathfinder_client::is_grabable
00429   (
00430   Tile_coord from,    // From this spot.
00431   Tile_coord to     // To this spot.
00432   )
00433   {
00434 //No. from.tz = to.tz;    // Just look along dest's lift.
00435   if (from.distance(to) <= 1)
00436     return 1;   // Already okay.
00437   Fast_pathfinder_client client(1, 
00438      Game_window::get_instance()->get_main_actor()->get_type_flags());
00439   Astar path;
00440   return path.NewPath(from, to, &client);
00441   }
00442 
00443 /*
00444  *  Check for an unblocked straight path.
00445  *  NOTE1:  This really has nothing to do with Fast_pathfinder_client.
00446  *  NOTE2:  This version doesn't check the z-coord at all.
00447  */
00448 
00449 int Fast_pathfinder_client::is_straight_path
00450   (
00451   Tile_coord from,    // From this spot.
00452   Tile_coord to     // To this spot.
00453   )
00454   {
00455   to.tz = from.tz;    // ++++++For now.
00456   Zombie path;
00457   if (!path.NewPath(from, to, 0)) // Should always succeed.
00458     return 0;
00459   Tile_coord t;     // Check each tile.
00460   bool done;
00461   while (path.GetNextStep(t, done))
00462     if (t != from && t != to && Map_chunk::is_blocked(t))
00463       return 0; // Blocked.
00464   return 1;     // Looks okay.
00465   }
00466 
00467 /*
00468  *  Create client for getting within a desired distance of a
00469  *  destination.
00470  */
00471 
00472 Monster_pathfinder_client::Monster_pathfinder_client
00473   (
00474   Actor *npc,     // 'Monster'.
00475   Tile_coord dest,
00476   int dist
00477   ) : Fast_pathfinder_client(dist), destbox(dest.tx, dest.ty, 0, 0)
00478   {
00479   intelligence = npc->get_property(Actor::intelligence);
00480   Shape_info& info1 = npc->get_info();
00481   axtiles = info1.get_3d_xtiles();
00482   aytiles = info1.get_3d_ytiles();
00483   aztiles = info1.get_3d_height();
00484   set_move_flags(npc->get_type_flags());
00485   destbox.enlarge(dist);    // How close we need to get.
00486   }
00487 
00488 /*
00489  *  Create client for combat pathfinding.
00490  */
00491 
00492 Monster_pathfinder_client::Monster_pathfinder_client
00493   (
00494   Actor *attacker,
00495   int reach,      // Weapon reach in tiles.
00496   Game_object *opponent
00497   ) : Fast_pathfinder_client(reach), destbox(0, 0, 0, 0)
00498   {
00499   intelligence = attacker->get_property(Actor::intelligence);
00500   Shape_info& info1 = attacker->get_info();
00501   axtiles = info1.get_3d_xtiles();
00502   aytiles = info1.get_3d_ytiles();
00503   aztiles = info1.get_3d_height();
00504   if (!opponent)
00505     return;     // Means this isn't usable.
00506   set_move_flags(attacker->get_type_flags());
00507   Shape_info& info2 = opponent->get_info();
00508   Tile_coord opos = opponent->get_tile();
00509   int oxtiles = info2.get_3d_xtiles(), oytiles = info2.get_3d_ytiles();
00510   destbox = Rectangle(opos.tx - oxtiles + 1, opos.ty - oytiles + 1,
00511               oxtiles, oytiles);
00512   destbox.enlarge(reach);   // This is how close we need to get.
00513   }
00514 
00515 /*
00516  *  Figure when to give up.
00517  */
00518 
00519 int Monster_pathfinder_client::get_max_cost
00520   (
00521   int cost_to_goal    // From estimate_cost().
00522   )
00523   {
00524   int max_cost = 2*cost_to_goal;  // Don't try to hard.
00525   int icost = 2 + 2*intelligence; // Limit by intelligence.
00526   if (max_cost > icost)   // Note: intel. ranges from 0 to 30.
00527     max_cost = icost;
00528   Game_window *gwin = Game_window::get_instance();
00529           // Limit to 3/4 screen width.
00530   int scost = ((3*gwin->get_width())/4)/c_tilesize;
00531   if (max_cost > scost)
00532     max_cost = scost;
00533   if (max_cost < 15)    // But not too small.
00534     max_cost = 15;
00535   return max_cost;
00536   }
00537 
00538 /*
00539  *  Is tile at goal?
00540  */
00541 
00542 int Monster_pathfinder_client::at_goal
00543   (
00544   Tile_coord& tile,
00545   Tile_coord& goal
00546   )
00547   {
00548   int dz = tile.tz - goal.tz; // Got to be on same floor.
00549   if (dz/5 != 0)
00550     return 0;
00551   Rectangle abox(tile.tx - axtiles + 1, tile.ty - aytiles + 1,
00552             axtiles, aytiles);
00553   return abox.intersects(destbox);
00554   }
00555 
00556 /*
00557  *  Figure cost going from one tile to an adjacent tile (for pathfinding).
00558  *
00559  *  Output: Cost, or -1 if blocked.
00560  *    The 'tz' field in tile may be modified.
00561  */
00562 
00563 int Monster_pathfinder_client::get_step_cost
00564   (
00565   Tile_coord from,
00566   Tile_coord& to      // The tile we're going to.  The 'tz'
00567           //   field may be modified.
00568   )
00569   {
00570   if (Map_chunk::is_blocked(axtiles, aytiles, aztiles,
00571             from, to, get_move_flags()))
00572     return -1;
00573   else
00574     return 1;
00575   }
00576 
00577 

Generated on Mon Jul 9 14:42:49 2007 for ExultEngine by  doxygen 1.5.1