party.cc

Go to the documentation of this file.
00001 
00006 /*  Copyright (C) 2000-2003  The Exult Team
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00021  */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #  include <config.h>
00025 #endif
00026 
00027 #include <iostream>
00028 #include <string>
00029 #include "party.h"
00030 #include "actors.h"
00031 #include "gamewin.h"
00032 #include "frameseq.h"
00033 #include "dir.h"
00034 
00035 using std::cout;
00036 using std::endl;
00037 
00038 /*
00039  *  Create.
00040  */
00041 
00042 Party_manager::Party_manager
00043   (
00044   ) : party_count(0), dead_party_count(0), validcnt(0)
00045   {  
00046           // Clear party list.
00047   std::memset((char *) &party[0], 0, sizeof(party));
00048   std::memset((char *) &dead_party[0], 0, sizeof(dead_party));
00049   }
00050 
00051 /*
00052  *  Add NPC to party.
00053  *
00054  *  Output: false if no room or already a member.
00055  */
00056 
00057 bool Party_manager::add_to_party
00058   (
00059   Actor *npc      // (Should not be the Avatar.)
00060   )
00061   {
00062   const int maxparty = sizeof(party)/sizeof(party[0]);
00063   if (!npc || party_count == maxparty || npc->is_in_party())
00064     return false;
00065   remove_from_dead_party(npc);  // Just to be sure.
00066   npc->set_party_id(party_count);
00067   npc->set_flag (Obj_flags::in_party);
00068           // We can take items.
00069   npc->set_flag_recursively(Obj_flags::okay_to_take);
00070   party[party_count++] = npc->get_npc_num();
00071   return true;
00072   }
00073 
00074 /*
00075  *  Remove party member.
00076  *
00077  *  Output: false if not found.
00078  */
00079 
00080 bool Party_manager::remove_from_party
00081   (
00082   Actor *npc
00083   )
00084   {
00085   if (!npc)
00086     return false;
00087   int id = npc->get_party_id();
00088   if (id == -1)     // Not in party?
00089     return false;
00090   int npc_num = npc->get_npc_num();
00091   if (party[id] != npc_num)
00092     {
00093     cout << "Party mismatch!!" << endl;
00094     return false;
00095     }
00096           // Shift the rest down.
00097   for (int i = id + 1; i < party_count; i++)
00098     {
00099     Actor *npc2 = gwin->get_npc(party[i]);
00100     if (npc2)
00101       npc2->set_party_id(i - 1);
00102     party[i - 1] = party[i];
00103     }
00104   npc->clear_flag (Obj_flags::in_party);
00105   party_count--;
00106   party[party_count] = 0;
00107   npc->set_party_id(-1);
00108   return true;
00109   }
00110 
00111 /*
00112  *  Find index of NPC in dead party list.
00113  *
00114  *  Output: Index, or -1 if not found.
00115  */
00116 
00117 int Party_manager::in_dead_party
00118   (
00119   Actor *npc
00120   )
00121   {
00122   int num = npc->get_npc_num();
00123   for (int i = 0; i < dead_party_count; i++)
00124     if (dead_party[i] == num)
00125       return i;
00126   return -1;
00127   }
00128 
00129 /*
00130  *  Add NPC to dead party list.
00131  *
00132  *  Output: false if no room or already a member.
00133  */
00134 
00135 bool Party_manager::add_to_dead_party
00136   (
00137   Actor *npc      // (Should not be the Avatar.)
00138   )
00139   {
00140   const int maxparty = sizeof(dead_party)/sizeof(dead_party[0]);
00141   if (!npc || dead_party_count == maxparty || in_dead_party(npc) >= 0)
00142     return false;
00143   dead_party[dead_party_count++] = npc->get_npc_num();
00144   return true;
00145   }
00146 
00147 /*
00148  *  Remove NPC from dead party list.
00149  *
00150  *  Output: false if not found.
00151  */
00152 
00153 bool Party_manager::remove_from_dead_party
00154   (
00155   Actor *npc
00156   )
00157   {
00158   if (!npc)
00159     return false;
00160   int id = in_dead_party(npc);  // Get index.
00161   if (id == -1)     // Not in list?
00162     return false;
00163   int npc_num = npc->get_npc_num();
00164           // Shift the rest down.
00165   for (int i = id + 1; i < dead_party_count; i++)
00166     dead_party[i - 1] = dead_party[i];
00167   dead_party_count--;
00168   dead_party[dead_party_count] = 0;
00169   return true;
00170   }
00171 
00172 /*
00173  *  Update party status of an NPC that has died or been resurrected.
00174  */
00175 
00176 void Party_manager::update_party_status
00177   (
00178   Actor *npc
00179   )
00180   {
00181   if (npc->is_dead())   // Dead?
00182     {
00183           // Move party members to dead list.
00184     if (remove_from_party(npc))
00185       add_to_dead_party(npc);
00186     }
00187   else        // Alive.
00188     {
00189     if (remove_from_dead_party(npc))
00190       add_to_party(npc);
00191     }
00192   }
00193 
00194 /*
00195  *  In case NPC's were read after usecode, set party members' id's, and
00196  *  move dead members into separate list.
00197  */
00198 
00199 void Party_manager::link_party
00200   (
00201   )
00202   {
00203   // avatar is a party member too
00204   gwin->get_main_actor()->set_flag(Obj_flags::in_party);
00205           // You own your own stuff.
00206   gwin->get_main_actor()->set_flag_recursively(Obj_flags::okay_to_take);
00207   const int maxparty = sizeof(party)/sizeof(party[0]);
00208   int tmp_party[maxparty];
00209   int tmp_party_count = party_count;
00210   int i;
00211   for (i = 0; i < maxparty; i++)
00212     tmp_party[i] = party[i];
00213   party_count = dead_party_count = 0;
00214           // Now process them.
00215   for (i = 0; i < tmp_party_count; i++)
00216     {
00217     if (party[i] <= 0)  // Fix corruption.
00218       continue;
00219     Actor *npc = gwin->get_npc(party[i]);
00220     int oldid;
00221     if (!npc ||   // Shouldn't happen!
00222           // But this has happened:
00223         ((oldid = npc->get_party_id()) >= 0 && 
00224               oldid < party_count))
00225       continue; // Skip bad entry.
00226     int npc_num = npc->get_npc_num();
00227     if (npc->is_dead()) // Put dead in special list.
00228       {
00229       npc->set_party_id(-1);
00230       if (dead_party_count >= 
00231             sizeof(dead_party)/sizeof(dead_party[0]))
00232         continue;
00233       dead_party[dead_party_count++] = npc_num;
00234       continue;
00235       }
00236     npc->set_party_id(party_count);
00237     party[party_count++] = npc_num;
00238 // ++++This messes up places where they should wait, and should be unnecessary.
00239 //    npc->set_schedule_type(Schedule::follow_avatar);
00240           // We can use all his/her items.
00241     npc->set_flag_recursively(Obj_flags::okay_to_take);
00242     npc->set_flag (Obj_flags::in_party);
00243     }
00244   }
00245 
00246 /*
00247  *  For each party member, this array has the party ID's (or -1) of the
00248  *  two member's followers, arrayed as follows:
00249  *      A
00250  *           0 1
00251  *          2 3 4
00252  *         7 5 6 8
00253  */
00254 static int followers[EXULT_PARTY_MAX + 1][2] = {
00255   {0, 1},       // These follow Avatar (ID = -1).
00256   {2, 3},       // Follow 0.
00257   {-1, 4},      // Follow 1.
00258   {7, -1},      // Follow 2.
00259   {5, 6},       // Follow 3.
00260   {-1, 8},      // Follow 4.
00261   {-1, -1}, {-1, -1}, {-1, -1}};
00262 
00263 /*
00264  *  Offsets for the follower, depending on direction (0-3, with
00265  *  0 = North, 1 = East, 2 = South, 3 = West).
00266  */
00267 static int left_offsets[4][2] = { // Follower is behind and to left.
00268   {-2, 2},      // North.
00269   {-2, -2},     // East.
00270   {2, -2},      // South.
00271   {2, 2} };     // West.
00272 static int right_offsets[4][2] = {  // Follower is behind and to right.
00273   {2, 2},       // North.
00274   {-2, 2},      // East.
00275   {-2, -2},     // South.
00276   {2, -2} };      // West.
00277 
00278 /*
00279  *  This should be called each time the Avatar takes a step while under
00280  *  control of the user.
00281  */
00282 
00283 void Party_manager::get_followers
00284   (
00285   int dir       // Direction (0-7) Avatar just stepped.
00286   )
00287   {
00288   validcnt = 0;     // Get party members to control.
00289   for (int i = 0; i < party_count; i++)
00290     {
00291     Actor *npc = gwin->get_npc(party[i]);
00292     if (!npc || npc->get_flag(Obj_flags::asleep) ||
00293         npc->get_flag(Obj_flags::paralyzed) ||
00294         npc->is_dead())
00295       continue; // Not available.
00296     if (npc->in_queue())  // Already walking?
00297       continue; // For now, let him continue...
00298     valid[validcnt++] = npc;
00299     }
00300   if (validcnt)
00301     move_followers(gwin->get_main_actor(), -1, dir);
00302   }
00303 
00304 /*
00305  *  To walk in formation, each party member will have one or two other
00306  *  party members who will follow him on each step.
00307  */
00308 
00309 void Party_manager::move_followers
00310   (
00311   Actor *npc,     // Party member who just stepped.
00312   int vindex,     // Index within 'valid'.
00313   int dir       // Direction (0-7) he stepped in.
00314   )
00315   {
00316   int id = npc->get_party_id(); // (-1 if Avatar).
00317   Tile_coord pos = npc->get_tile();
00318   int lnum = followers[1 + id][0], rnum = followers[1 + id][1];
00319   if (lnum == -1 && rnum == -1)
00320     return;     // Nothing to do.
00321   int dir4 = dir/2;   // 0-3 now.
00322   Actor *lnpc = (lnum == -1 || lnum >= validcnt) ? 0 : valid[lnum];
00323   Actor *rnpc = (rnum == -1 || rnum >= validcnt) ? 0 : valid[rnum];
00324   int ldir = -1, rdir = -1;
00325           // Have each take a step.
00326   if (lnpc)
00327     ldir = step(lnpc, npc, dir, pos + Tile_coord(
00328       left_offsets[dir4][0], left_offsets[dir4][1], 0));
00329   if (rnpc)
00330     rdir = step(rnpc, npc, dir, pos + Tile_coord(
00331       right_offsets[dir4][0], right_offsets[dir4][1], 0));
00332   if (ldir >= 0 && !lnpc->is_dead())
00333     move_followers(lnpc, lnum, ldir);
00334   if (rdir >= 0 && !rnpc->is_dead())
00335     move_followers(rnpc, rnum, rdir);
00336   }
00337 
00338 /*
00339  *  Get tile to step to, given destination tile (possibly more than 1
00340  *  step away), and the party's direction.
00341  */
00342 
00343 inline Tile_coord Get_step_tile
00344   (
00345   Tile_coord pos,     // Current pos.
00346   Tile_coord dest,    // Desired dest.
00347   int dir       // Dir. party is moving (0-7).
00348   )
00349   {
00350   int dx = dest.tx - pos.tx, dy = dest.ty - pos.ty;
00351   if (dx < -1)
00352     dx = -1;    // Limit to 1 tile.
00353   else if (dx > 1)
00354     dx = 1;
00355   if (dy < -1)
00356     dy = -1;
00357   else if (dy > 1)
00358     dy = 1;
00359   return pos + Tile_coord(dx, dy, 0);
00360   }
00361 
00362 /*
00363  *  Find the party member occupying a given tile, starting with a given
00364  *  party #.
00365  *  Note: Maybe it should check a rectangle of tiles someday if we want
00366  *    to have NPC's bigger than 1 tile.
00367  */
00368 
00369 static Actor *Find_member_blocking
00370   (
00371   Tile_coord pos,     // Position to check.
00372   int first     // Party ID to start with.
00373   )
00374   {
00375   Game_window *gwin = Game_window::get_instance();
00376   Party_manager *pman = gwin->get_party_man();
00377   int count = pman->get_count();
00378 
00379   for (int i = first; i < count; i++)
00380     {
00381     Actor *npc = gwin->get_npc(pman->get_member(i));
00382     pos.tz = npc->get_lift();// Use NPC's, since it might be up/dn
00383           //   by a step.
00384     if (npc->blocks(pos))
00385       return npc; // Found.
00386     }
00387   return 0;
00388   }
00389 
00390 /*
00391  *  Get the direction from a tile to NPC's position.
00392  */
00393 
00394 inline int Get_dir_from
00395   (
00396   Actor *npc,
00397   Tile_coord& from
00398   )
00399   {
00400   Tile_coord pos = npc->get_tile();
00401   return Get_direction(from.ty - pos.ty, pos.tx - from.tx);
00402   }
00403 
00404 /*
00405  *  Is the straight path to the leader clear, and less than 5 tiles?
00406  */
00407 
00408 inline bool Clear_to_leader
00409   (
00410   Actor *npc,
00411   Actor *leader,
00412   Tile_coord from     // Start from here.
00413   )
00414   {
00415   Tile_coord dest = leader->get_tile();
00416   int dist = dest.distance(from);
00417   if (dist > 4)
00418     return false;   // Too far.
00419   while (--dist)      // Check tiles up to there.
00420     {
00421     int dir = Get_dir_from(leader, from);
00422     Tile_coord next = from.get_neighbor(dir);
00423     if (npc->is_blocked(next, &from))
00424       {
00425       Actor *bnpc = Find_member_blocking(next, 0);
00426       if (!bnpc)
00427         return false; // Blocked by non-party-member.
00428       next.tz = bnpc->get_lift();
00429       }
00430     from = next;
00431     }
00432   int difftz = from.tz - dest.tz; // Check diff. in z-coords.
00433   return difftz*difftz <= 1;  // Can't be more than 2.
00434   }
00435 
00436 /*
00437  *  Get a notion of 'cost' for stepping to a particular tile.
00438  *
00439  *  Output: Currently, 10000 if blocked, or (dist)**2.
00440  */
00441 
00442 const int max_cost = 10000;
00443 
00444 static int Get_cost
00445   (
00446   Actor *npc,     // NPC to take the step.
00447   Actor *leader,      // NPC he's following.
00448   Tile_coord to,      // Tile to step to.
00449   Actor **find_blocking = 0 // Returns blocking party member.
00450   )
00451   {
00452   int cost = 0;
00453   if (find_blocking)
00454     *find_blocking = 0;
00455   if (npc->is_blocked(to))  // (To.tz is updated.)
00456     {     // Can't go there.
00457     if (find_blocking)
00458       {   // Find member we can swap with.
00459       *find_blocking = Find_member_blocking(to,
00460             1 + npc->get_party_id());
00461       if (!*find_blocking)
00462         return max_cost;
00463       to.tz = (*find_blocking)->get_lift();
00464       cost += 1;  // Assess one point to swap.
00465       }
00466     else
00467       return max_cost;
00468     }
00469   Tile_coord lpos = leader->get_tile();
00470   int difftz = to.tz - lpos.tz, // Measure closeness.
00471       diffty = Tile_coord::delta(to.ty, lpos.ty),
00472       difftx = Tile_coord::delta(to.tx, lpos.tx);
00473           // Get dist**2 in x-y plane.
00474   int xydist2 = diffty*diffty + difftx*difftx;
00475   cost += difftz*difftz + xydist2;
00476   if (xydist2 > 2)    // More than 1 tile away?
00477     {     // Check 1 more tile towards leader.
00478     if (!Clear_to_leader(npc, leader, to))
00479       cost += 16; // If blocked, try to avoid.
00480     }
00481   return cost;
00482   }
00483 
00484 /*
00485  *  Take best step to follow the leader.
00486  *
00487  *  Output: True if a step taken.
00488  */
00489 
00490 static bool Take_best_step
00491   (
00492   Actor *npc,
00493   Actor *leader,
00494   Tile_coord& pos,    // Current pos.
00495   int frame,      // Frame to show.
00496   int dir       // Direction we want to go.
00497   )
00498   {
00499   static int deltadir[] = {0, 1, 7, 2, 6, 3, 5};
00500   const int cnt = sizeof(deltadir)/sizeof(deltadir[0]);
00501 
00502   int best_cost = max_cost + 8;
00503   Tile_coord best(-1, -1, -1);
00504   Actor *best_in_way = 0;
00505   for (int i = 0; i < cnt; i++)
00506     {
00507     int diri = (dir + deltadir[i])%8;
00508     Tile_coord to = pos.get_neighbor(diri);
00509     Actor *in_way;    // Fudge cost with diff. in dir.
00510     int cost = Get_cost(npc, leader, to, &in_way);
00511     if (cost < best_cost)
00512       {
00513       best_cost = cost;
00514       best_in_way = in_way;
00515       best = to;
00516       }
00517     }
00518   if (best_cost >= max_cost)
00519     return false;
00520   if (!best_in_way)   // Nobody in way?
00521     return npc->step(best, frame)!=0;
00522   best = best_in_way->get_tile(); // Swap positions.
00523   npc->remove_this(true);
00524   best_in_way->remove_this(true);
00525   npc->set_frame(frame);    // Appear to take a step.
00526   npc->move(best);
00527   best_in_way->move(pos);
00528   return true;
00529   }
00530 
00531 /*
00532  *  See if a step is reasonable.  This is the first test made.
00533  */
00534 
00535 inline bool Is_step_okay
00536   (
00537   Actor *npc,     // NPC to take the step.
00538   Actor *leader,      // NPC he's following.
00539   Tile_coord to     // Tile to step to.
00540   )
00541   {
00542   if (npc->is_blocked(to))  // (To.tz is updated.)
00543     return false;
00544   int difftz = to.tz - leader->get_lift();
00545   difftz *= difftz;   // Deltaz squared.
00546   if (difftz > 4)     // More than 2?
00547     return false;   // We'll want to find best dir.
00548           // How close in XY?
00549   int dist = to.distance(leader->get_tile());
00550   if (dist == 1)
00551     return (difftz <= 1); // 1 tile away, so want dz <= 1.
00552   if (!Clear_to_leader(npc, leader, to))
00553     return false;   // Couldn't take a 2nd step.
00554   return true;
00555   }
00556 
00557 /*
00558  *  Move one follower to its destination (if possible).
00559  *
00560  *  Output: Direction (0-7) moved (or given 'dir' if we don't move).
00561  */
00562 
00563 int Party_manager::step
00564   (
00565   Actor *npc,
00566   Actor *leader,      // Who NPC is following.
00567   int dir,      // Direction we're walking (0-7).
00568   Tile_coord dest     // Destination tile.
00569   )
00570   {
00571   Tile_coord pos = npc->get_tile(); // Current position.
00572   Tile_coord to = Get_step_tile(pos, dest, dir);
00573   if (to.tx == pos.tx && to.ty == pos.ty)
00574     return dir;   // Not moving.
00575 //TEST:
00576   if (npc->in_queue() || npc->is_moving())
00577     cout << npc->get_name() << " shouldn't be stepping!" << endl;
00578   Frames_sequence *frames = npc->get_frames(dir);
00579   int& step_index = npc->get_step_index();
00580   if (!step_index)    // First time?  Init.
00581     step_index = frames->find_unrotated(npc->get_framenum());
00582           // Get next (updates step_index).
00583   int frame = frames->get_next(step_index);
00584           // Want dz<=1, dx<=2, dy<=2.
00585   if (Is_step_okay(npc, leader, to) && npc->step(to, frame))
00586     ;
00587           // Could have died from stepping on
00588           //   something.
00589   else if (npc->is_dead() ||
00590      !Take_best_step(npc, leader, pos, frame,   
00591             npc->get_direction(dest)))
00592     {     // Failed to take a step.
00593     cout << npc->get_name() << " failed to take a step" << endl;
00594     frames->decrement(step_index);
00595     return dir;
00596     }
00597   return Get_dir_from(npc, pos);
00598   }
00599 

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