actors.cc

Go to the documentation of this file.
00001 /*
00002  *  actors.cc - Game actors.
00003  *
00004  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00005  *  Copyright (C) 2000-2001  The Exult Team
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020  */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #  include <config.h>
00024 #endif
00025 
00026 #ifndef ALPHA_LINUX_CXX
00027 #  include <iostream>     /* Debugging. */
00028 #  include <cstdlib>
00029 #  include <cstring>
00030 #endif
00031 #include <algorithm>    /* swap. */
00032 #include "Astar.h"
00033 #include "Audio.h"
00034 #include "Gump_manager.h"
00035 #include "Paperdoll_gump.h"
00036 #include "Zombie.h"
00037 #include "actions.h"
00038 #include "actors.h"
00039 #include "bodies.h"
00040 #include "cheat.h"
00041 #include "chunks.h"
00042 #include "combat.h"
00043 #include "combat_opts.h"
00044 #include "dir.h"
00045 #include "egg.h"
00046 #include "exult.h"
00047 #include "frameseq.h"
00048 #include "game.h"
00049 #include "gamewin.h"
00050 #include "gamemap.h"
00051 #include "gameclk.h"
00052 #include "imagewin.h"
00053 #include "items.h"
00054 #include "npctime.h"
00055 #include "ready.h"
00056 #include "ucmachine.h"
00057 #include "party.h"
00058 #include "monstinf.h"
00059 #include "exult_constants.h"
00060 #include "monsters.h"
00061 #include "effects.h"
00062 #include "palette.h"
00063 #include "ucsched.h"
00064 #include "ucscriptop.h"
00065 
00066 #ifdef USE_EXULTSTUDIO
00067 #include "server.h"
00068 #include "objserial.h"
00069 #include "mouse.h"
00070 #include "servemsg.h"
00071 #endif
00072 
00073 #ifndef UNDER_CE
00074 using std::cerr;
00075 using std::cout;
00076 using std::endl;
00077 using std::memcpy;
00078 using std::rand;
00079 using std::string;
00080 using std::swap;
00081 #endif
00082 
00083 Actor *Actor::editing = 0;
00084 
00085 extern bool combat_trace;
00086 
00087 // Party positions
00088 // Direction, Party num, xy (tile) from leader
00089 //
00090 // Please Don't Touch - Colourless
00091 //
00092 const short Actor::party_pos[4][10][2] = {
00093     // North Facing
00094   {
00095     { -2, 2 },
00096     { 2, 2 },
00097     { 0, 4 },
00098     { -4, 4 },
00099     { 4, 4 },
00100     { -2, 6 },
00101     { 2, 6 },
00102     { 0, 8 },
00103     { -4, 8 },
00104     { 4, 8 }
00105   },
00106     // East Facing,
00107   {
00108     { -2, -2 },
00109     { -2, 2 },
00110     { -4, 0 },
00111     { -4, -4 },
00112     { -4, 4 },
00113     { -6, -2 },
00114     { -6, 2 },
00115     { -8, 0 },
00116     { -8, -4 },
00117     { -8, 4 }
00118   },
00119     // South Facing
00120   {
00121     { -2, -2 },
00122     { 2, -2 },
00123     { 0, -4 },
00124     { -4, -4 },
00125     { 4, -4 },
00126     { -2, -6 },
00127     { 2, -6 },
00128     { 0, -8 },
00129     { -4, -8 },
00130     { 4, -8 }
00131   },
00132     // West Facing
00133   {
00134     { 2, -2 },
00135     { 2, 2 },
00136     { 4, 0 },
00137     { 4, -4 },
00138     { 4, 4 },
00139     { 6, -2 },
00140     { 6, 2 },
00141     { 8, 0 },
00142     { 8, -4 },
00143     { 8, 4 }
00144   }
00145 };
00146 
00147 //  Actor frame to substitute when a frame is empty (as some are):
00148 uint8 visible_frames[16] = {
00149   Actor::standing,    // Standing.
00150   Actor::standing,    // Steps.
00151   Actor::standing,
00152   Actor::standing,    // Ready.
00153   Actor::raise2_frame,    // 1-handed strikes => 2-handed.
00154   Actor::reach2_frame,
00155   Actor::strike2_frame,
00156   Actor::raise1_frame,    // 2-handed => 1-handed.
00157   Actor::reach1_frame,
00158   Actor::strike1_frame,
00159   Actor::standing,    // When you can't sit...
00160   Actor::kneel_frame,   // When you can't bow.
00161   Actor::bow_frame,   // When you can't kneel.
00162   Actor::standing,    // Can't lie.
00163   Actor::strike2_frame,   // Can't raise hands.
00164   Actor::strike2_frame };
00165 
00166 Frames_sequence *Actor::avatar_frames[4] = {0, 0, 0, 0};
00167 Frames_sequence *Actor::npc_frames[4] = {0, 0, 0, 0};
00168 const signed char sea_serpent_attack_frames[] = {13, 12, 11, 0, 1, 2, 3, 11, 12, 
00169                 13, 14};
00170 // inline int Is_attack_frame(int i) { return i >= 3 && i <= 9; }
00171 inline int Is_attack_frame(int i) { return i == 6 || i == 9; }
00172 inline int Get_dir_from_frame(int i)
00173   { return ((((i&16)/8) - ((i&32)/32)) + 4)%4; }
00174 
00175 /*
00176  *  Get/create timers.
00177  */
00178 
00179 Npc_timer_list *Actor::need_timers
00180   (
00181   )
00182   {
00183   if (!timers)
00184     timers = new Npc_timer_list(this);
00185   return timers;
00186   }
00187 
00188 /*
00189  *  Initialize.
00190  */
00191 
00192 void Actor::init
00193   (
00194   )
00195   {
00196   if (!avatar_frames[0])
00197     init_default_frames();
00198   size_t i;
00199   for (i = 0; i < sizeof(properties)/sizeof(properties[0]); i++)
00200     properties[i] = 0;
00201   for (i = 0; i < sizeof(spots)/sizeof(spots[0]); i++)
00202     spots[i] = 0;
00203   }
00204 
00205 /*
00206  *  Ready ammo for weapon being carried.
00207  *
00208  *  Output: 1 if successful, else 0.
00209  */
00210 
00211 int Actor::ready_ammo
00212   (
00213   )
00214   {
00215   int points;
00216   Weapon_info *winf = Actor::get_weapon(points);
00217   int ammo;
00218   if (!winf || (ammo = winf->get_ammo_consumed()) == 0)
00219     return 1;   // No weapon, or ammo not needed.
00220           // See if already have ammo.
00221   Game_object *aobj = get_readied(Actor::ammo);
00222   if (aobj && In_ammo_family(aobj->get_shapenum(), ammo))
00223     return 1;   // Already readied.
00224   Game_object_vector vec(50);   // Get list of all possessions.
00225   get_objects(vec, c_any_shapenum, c_any_qual, c_any_framenum);
00226   Game_object *found = 0;
00227   for (Game_object_vector::const_iterator it = vec.begin(); 
00228               it != vec.end(); ++it)
00229     {
00230     Game_object *obj = *it;
00231     if (In_ammo_family(obj->get_shapenum(), ammo))
00232       found = obj;
00233     }
00234   if (!found)
00235     return 0;
00236   if (aobj)     // Something there already?
00237     aobj->remove_this(1); // Remove it.
00238   found->remove_this(1);
00239   add(found, 1);      // Should go to the right place.
00240   if (aobj)     // Put back old ammo.
00241     add(aobj, 1);
00242   return 1;
00243   }
00244 
00245 /*
00246  *  If no weapon readied, look through all possessions for the best one.
00247  */
00248 
00249 void Actor::ready_best_weapon
00250   (
00251   )
00252   {
00253   int points;
00254   // What about spell book????
00255   if (Actor::get_weapon(points) != 0)
00256     {
00257     ready_ammo();
00258     return;     // Already have one.
00259     }
00260   Game_object_vector vec(50);   // Get list of all possessions.
00261   get_objects(vec, c_any_shapenum, c_any_qual, c_any_framenum);
00262   Game_object *best = 0;
00263   int best_damage = -20;
00264   Ready_type wtype=other;
00265   for (Game_object_vector::const_iterator it = vec.begin(); 
00266               it != vec.end(); ++it)
00267     {
00268     Game_object *obj = *it;
00269     if (obj->get_shapenum() == 595)
00270       continue; // Don't pick the torch. (Don't under-
00271           //   stand weapons.dat well, yet!)
00272     Shape_info& info = obj->get_info();
00273     Weapon_info *winf = info.get_weapon_info();
00274     if (!winf)
00275       continue; // Not a weapon.
00276           // +++Might be a class to check.
00277     int damage = winf->get_damage();
00278     if (damage > best_damage)
00279       {
00280       wtype = static_cast<Ready_type>(info.get_ready_type());
00281       best = obj;
00282       best_damage = damage;
00283       }
00284     }
00285   if (!best)
00286     return;
00287   Game_object *remove1 = 0, *remove2 = 0;
00288   if (wtype == two_handed_weapon)
00289     {
00290     remove1 = spots[lhand];
00291     remove2 = spots[rhand];
00292     }
00293   else
00294     if (free_hand() == -1)
00295       remove1 = spots[lhand];
00296           // Free the spot(s).
00297   if (remove1)
00298     remove1->remove_this(1);
00299   if (remove2)
00300     remove2->remove_this(1);
00301   best->remove_this(1);
00302   add(best, 1);     // Should go to the right place.
00303   if (remove1)      // Put back other things.
00304     add(remove1, 1);
00305   if (remove2)
00306     add(remove2, 1);
00307   ready_ammo();     // Get appropriate ammo.
00308   }
00309 
00310 /*
00311  *  Try to store the weapon.
00312  */
00313 
00314 void Actor::unready_weapon
00315   (
00316   int spot      // Lhand or rhand.
00317   )
00318   {
00319   Game_object *obj = spots[spot];
00320   if (!obj)
00321     return;
00322   Shape_info& info = obj->get_info();
00323   if (!info.get_weapon_info())  // A weapon?
00324     return;
00325   if (!spots[belt])   // Belt free?
00326     {
00327     obj->remove_this(1);
00328     add_readied(obj, belt);
00329     }
00330   }
00331 
00332 /*
00333  *  Add dirty rectangle(s).
00334  *
00335  *  Output: 0 if not on screen.
00336  */
00337 int Actor::add_dirty
00338   (
00339   int figure_rect     // Recompute weapon rectangle.
00340   )
00341   {
00342   if (!gwin->add_dirty(this))
00343     return 0;
00344   int weapon_x, weapon_y, weapon_frame;
00345   if (figure_rect)
00346     if (figure_weapon_pos(weapon_x, weapon_y, weapon_frame))
00347       {
00348       Game_object * weapon = spots[lhand];
00349       int shnum = weapon->get_shapenum();
00350       
00351       Shape_frame *wshape = ShapeID(shnum, weapon_frame).get_shape();
00352 
00353       if (wshape) // Set dirty area rel. to NPC.
00354         weapon_rect = gwin->get_shape_rect(wshape, 
00355               weapon_x, weapon_y);
00356       else
00357         weapon_rect.w = 0;
00358       }
00359     else
00360       weapon_rect.w = 0;
00361   if (weapon_rect.w > 0)    // Repaint weapon area too.
00362     {
00363     Rectangle r = weapon_rect;
00364     int xoff, yoff;
00365     gwin->get_shape_location(this, xoff, yoff);
00366     r.shift(xoff, yoff);
00367     r.enlarge(4);
00368     gwin->add_dirty(gwin->clip_to_win(r));
00369     }
00370   return 1;
00371   }
00372 
00373 /*
00374  *  Change the frame and set to repaint areas.
00375  */
00376 
00377 void Actor::change_frame
00378   (
00379   int frnum
00380   )
00381   {
00382   add_dirty();      // Set to repaint old area.
00383   set_frame(frnum);
00384   add_dirty(1);     // Set to repaint new.
00385   }
00386 
00387 /*
00388  *  See if it's blocked when trying to move to a new tile.
00389  *
00390  *  Output: 1 if so, else 0.
00391  */
00392 
00393 int Actor::is_blocked
00394   (
00395   Tile_coord& t,      // Tz possibly updated.
00396   Tile_coord *f     // Step from here, or curpos if null.
00397   )
00398   {
00399   Shape_info& info = get_info();
00400           // Get dim. in tiles.
00401   int xtiles = info.get_3d_xtiles(), ytiles = info.get_3d_ytiles();
00402   int ztiles = info.get_3d_height();
00403   if (xtiles == 1 && ytiles == 1) // Simple case?
00404     {
00405     Map_chunk *nlist = gmap->get_chunk(
00406       t.tx/c_tiles_per_chunk, t.ty/c_tiles_per_chunk);
00407     nlist->setup_cache();
00408     int new_lift;
00409     int blocked = nlist->is_blocked(ztiles, t.tz,
00410       t.tx%c_tiles_per_chunk, t.ty%c_tiles_per_chunk,
00411           new_lift, get_type_flags());
00412     t.tz = new_lift;
00413     return blocked;
00414     }
00415   return Map_chunk::is_blocked(xtiles, ytiles, ztiles,
00416       f ? *f : get_tile(), t, get_type_flags());
00417   }
00418 
00419 /*
00420  *  Move an object, and possibly change its shape too.
00421  */
00422 inline void Actor::movef
00423   (
00424   Map_chunk *old_chunk, 
00425   Map_chunk *new_chunk, 
00426   int new_sx, int new_sy, int new_frame, 
00427   int new_lift
00428   )
00429   {
00430   if (old_chunk)      // Remove from current chunk.
00431     old_chunk->remove(this);
00432   set_shape_pos(new_sx, new_sy);
00433   if (new_frame >= 0)
00434     set_frame(new_frame);
00435   if (new_lift >= 0)
00436     set_lift(new_lift);
00437   new_chunk->add(this);
00438   }
00439 
00440 /*
00441  *  Create character.
00442  */
00443 
00444 Actor::Actor
00445   (
00446   const std::string &nm, 
00447   int shapenum, 
00448   int num,      // NPC # from npc.dat.
00449   int uc        // Usecode #.
00450   ) : Container_game_object(), name(nm),usecode(uc), 
00451       usecode_assigned(false), unused(false),
00452       npc_num(num), face_num(num), party_id(-1), shape_save(-1), 
00453       oppressor(-1), target(0), attack_mode(nearest),
00454       schedule_type(static_cast<int>(Schedule::loiter)), schedule(0),
00455       dormant(true), hit(false), combat_protected(false), 
00456       user_set_attack(false), alignment(0),
00457       two_handed(false), two_fingered(false), light_sources(0),
00458       usecode_dir(0), siflags(0), type_flags(0), ident(0),
00459       skin_color(-1), action(0), 
00460       frame_time(0), step_index(0), timers(0),
00461       weapon_rect(0, 0, 0, 0), rest_time(0)
00462   {
00463   set_shape(shapenum, 0); 
00464   init();
00465   frames = &npc_frames[0];  // Default:  5-frame walking.
00466   }
00467 
00468 /*
00469  *  Delete.
00470  */
00471 
00472 Actor::~Actor
00473   (
00474   )
00475   {
00476   delete schedule;
00477   delete action;
00478   delete timers;
00479   }
00480 
00481 /*
00482  *  Decrement food level and print complaints if it gets too low.
00483  *  NOTE:  Should be called every hour.
00484  */
00485 
00486 void Actor::use_food
00487   (
00488   )
00489   {
00490   if (Game::get_game_type() == SERPENT_ISLE)
00491     {     // Automatons don't eat.
00492     int shnum = get_shapenum();
00493     if (shnum == 658 || shnum == 734 || shnum == 747)
00494       return;
00495     }
00496   int food = get_property(static_cast<int>(food_level));
00497   food -= rand()%4;   // Average 1.5 level/hour.
00498   set_property(static_cast<int>(food_level), food);
00499   if (food <= 0)      // Really low?
00500     {
00501     if (rand()%4)
00502       say(first_starving, first_starving + 2);
00503     if (food < 0)   // Set timer for damage.
00504       need_timers()->start_hunger();
00505     }
00506   else if (food <= 4)
00507     {
00508     if (rand()%3)
00509       say(first_needfood, first_needfood + 2);
00510     }
00511   else if (food <= 8)
00512     if (rand()%2)
00513       say(first_hunger, first_hunger + 2);
00514   }
00515 
00516 /*
00517  *  Periodic check for freezing.
00518  */
00519 
00520 void Actor::check_temperature
00521   (
00522   bool freeze     // Avatar's flag applies to party.
00523   )
00524   {
00525   if (!freeze)      // Not in a cold area?
00526     {
00527     if (!temperature) // 0 means warm.
00528       return;   // Nothing to do.
00529           // Warming up.
00530     temperature -= (temperature >= 5 ? 5 : temperature);
00531     if (rand()%3 == 0)
00532       if (temperature >= 30)
00533         say(1218, 1221);
00534       else
00535         say(1214, 1217);
00536     return;
00537     }
00538   int shnum = get_shapenum();
00539   if ((shnum == 658 || shnum == 734 || shnum == 747) && GAME_SI)
00540     return;     // Automatons don't get cold.
00541   if (get_schedule_type() == Schedule::wait)
00542     return;     // Not following leader?  Leave alone.
00543   int warmth = figure_warmth(); // (This could be saved for speed.)
00544   if (warmth >= 100)    // Enough clothing?
00545     {
00546     if (!temperature)
00547       return;   // Already warm.
00548     int decr = 1 + (warmth - 100)/10;
00549     decr = decr > temperature ? temperature : decr;
00550     temperature -= decr;
00551     if (rand()%3 == 0)
00552       if (temperature >= 30)
00553         say(1201, 1205);
00554       else
00555         say(1194, 1200);
00556     return;
00557     }
00558   int incr = 1 + (100 - warmth)/20;
00559   temperature += incr;
00560   if (temperature > 63)
00561     temperature = 63;
00562   if (rand()%3 == 0) switch (temperature/10)
00563     {
00564   case 0:
00565     say(1182, 1184);  // A bit chilly.
00566     break;
00567   case 1:
00568     say(1185, 1187);  // It's colder.
00569     break;
00570   case 2:
00571     say(1188, 1190);
00572     break;
00573   case 3:
00574     say(1191, 1193);  // Frostbite.
00575     break;
00576   case 4:
00577     say(1206, 1208);
00578     break;
00579   case 5:
00580     say(1209, 1211);
00581     break;
00582   case 6:
00583     say(1212, 1213);  // Frozen.
00584     reduce_health(1 + rand()%3);
00585     break;
00586     }
00587   }
00588 
00589 /*
00590  *  Get the 4 base frames for striking/shooting/throwing a weapon.
00591  */
00592 
00593 static void Get_weapon_frames
00594   (
00595   int weapon,     // Weapon shape, or 0 for innate.
00596   bool projectile,    // Shooting/throwing.
00597   bool two_handed,    // Held in both hands.
00598   signed char *frames   // Four frames stored here.
00599   ) 
00600   {
00601           // Frames for swinging.
00602   static signed char swing_frames1[3] = {Actor::raise1_frame, 
00603                  Actor::reach1_frame,
00604                  Actor::strike1_frame};
00605   static signed char swing_frames2[3] = {Actor::raise2_frame, 
00606                  Actor::reach2_frame,
00607                  Actor::strike2_frame};
00608   unsigned char frame_flags;  // Get Actor_frame flags.
00609   Weapon_info *winfo;
00610   if (weapon && 
00611       (winfo = ShapeID::get_info(weapon).get_weapon_info()) != 0)
00612     frame_flags = winfo->get_actor_frames(projectile);
00613   else        // Default to normal swing.
00614     frame_flags = projectile ? 0 : Weapon_info::raise|
00615               Weapon_info::reach;
00616           // Use frames for weapon type.
00617   const signed char *swing_frames = two_handed ? swing_frames2 : swing_frames1;
00618   frames[0] = Actor::ready_frame;
00619           // Do 'swing' frames.
00620   frames[1] = (frame_flags&Weapon_info::raise) ? swing_frames[0]
00621               : Actor::ready_frame;
00622   frames[2] = (frame_flags&Weapon_info::reach) ? swing_frames[1]
00623               : Actor::ready_frame;
00624   frames[3] = swing_frames[2];// Always do the 'strike'.
00625   }
00626 
00627 /*
00628  *  Get sequence of frames for an attack.
00629  *
00630  *  Output: # of frames stored.
00631  */
00632 
00633 int Actor::get_attack_frames
00634   (
00635   int weapon,     // Weapon shape, or 0 for innate.
00636   bool projectile,    // Shooting/throwing.
00637   int dir,      // 0-7 (as in dir.h).
00638   signed char *frames     // Frames stored here.
00639   ) const
00640   {
00641   signed char baseframes[4];
00642   const signed char *which = baseframes;
00643   int cnt = 4;
00644   switch (get_shapenum())   // Special cases.
00645     {
00646   case 525:     // Sea serpent.
00647     which = sea_serpent_attack_frames;
00648     cnt = sizeof(sea_serpent_attack_frames);
00649     break;
00650   case 529:     // Slimes.
00651     return 0;   // None, I believe.
00652   default:
00653     Get_weapon_frames(weapon, projectile, two_handed, baseframes);
00654     break;
00655     }
00656   for (int i = 0; i < cnt; i++) // Copy frames with correct dir.
00657     {
00658     int frame = get_dir_framenum(dir, *which++);
00659           // Check for empty shape.
00660     ShapeID id(get_shapenum(), frame, get_shapefile());
00661     Shape_frame *shape = id.get_shape();
00662     if (!shape || shape->is_empty())
00663       {   // Swap 1hand <=> 2hand frames.
00664       frame = get_dir_framenum(dir,visible_frames[frame&15]);
00665       id.set_frame(frame);
00666       if (!(shape = id.get_shape()) || shape->is_empty())
00667         frame = get_dir_framenum(dir, Actor::standing);
00668       }
00669     *frames++ = frame;
00670     }
00671   return (cnt);
00672   }   
00673 
00674 /*
00675  *  Set default set of frames.
00676  */
00677 
00678 void Actor::init_default_frames
00679   (
00680   )
00681   {
00682           // Set up actor's frame lists.
00683           // Most NPC's walk with a 'stand'
00684           //   frame between steps.
00685   const int FRAME_NUM = 5;
00686   uint8   npc_north_frames[FRAME_NUM] = { 0,  1,  0,  2,  0},
00687       npc_south_frames[FRAME_NUM] = {16, 17, 16, 18, 16},
00688       npc_east_frames[FRAME_NUM] = {48, 49, 48, 50, 48},
00689       npc_west_frames[FRAME_NUM] = {32, 33, 32, 34, 32};
00690   npc_frames[static_cast<int> (north)/2] = 
00691       new Frames_sequence(FRAME_NUM, npc_north_frames);
00692   npc_frames[static_cast<int> (south)/2] = 
00693       new Frames_sequence(FRAME_NUM, npc_south_frames);
00694   npc_frames[static_cast<int> (east)/2] = 
00695       new Frames_sequence(FRAME_NUM, npc_east_frames);
00696   npc_frames[static_cast<int> (west)/2] = 
00697       new Frames_sequence(FRAME_NUM, npc_west_frames);
00698           // Avatar just walks left, right.
00699   uint8   avatar_north_frames[3] = {0, 1, 2},
00700       avatar_south_frames[3] = {16, 17, 18},
00701       avatar_east_frames[3] = {48, 49, 50},
00702       avatar_west_frames[3] = {32, 33, 34};
00703   avatar_frames[static_cast<int> (north)/2] = 
00704       new Frames_sequence(3, avatar_north_frames);
00705   avatar_frames[static_cast<int> (south)/2] = 
00706       new Frames_sequence(3, avatar_south_frames);
00707   avatar_frames[static_cast<int> (east)/2] = 
00708       new Frames_sequence(3, avatar_east_frames);
00709   avatar_frames[static_cast<int> (west)/2] = 
00710       new Frames_sequence(3, avatar_west_frames);
00711   }
00712 
00713 /*
00714  *  This is called for the Avatar to return to a normal standing position
00715  *  when not doing anything else.  It could work for other party members,
00716  *  but currently isn't called for them.
00717  */
00718 
00719 void Actor::stand_at_rest
00720   (
00721   )
00722   {
00723   rest_time = 0;      // Reset timer.
00724   int frame = get_framenum()&0xff;// Base frame #.
00725   if (frame == standing || frame == sit_frame || frame == sleep_frame)
00726     return;     // Already standing/sitting/sleeping.
00727   if (!is_dead() && schedule_type == Schedule::follow_avatar &&
00728       !get_flag(Obj_flags::asleep))
00729     change_frame(get_dir_framenum(standing));
00730   }
00731 
00732 /*
00733  *  Set new action.
00734  */
00735 
00736 void Actor::set_action
00737   (
00738   Actor_action *newact
00739   )
00740   {
00741   if (newact != action)
00742     {
00743     delete action;
00744     action = newact;
00745     }
00746   if (!action)      // No action?  We're stopped.
00747     frame_time = 0;
00748   }
00749 
00750 /*
00751  *  Notify scheduler that an object it may be using has disappeared.
00752  */
00753 
00754 void Actor::notify_object_gone
00755   (
00756   Game_object *obj
00757   )
00758   {
00759   if (schedule)
00760     schedule->notify_object_gone(obj);
00761   }
00762 
00763 /*
00764  *  Get destination, or current spot if no destination.
00765  */
00766 
00767 Tile_coord Actor::get_dest
00768   (
00769   )
00770   {
00771   Tile_coord dest;
00772   if (action && action->get_dest(dest))
00773     return dest;
00774   else
00775     return get_tile();
00776   }
00777 
00778 /*
00779  *  Walk towards a given tile.
00780  */
00781 
00782 void Actor::walk_to_tile
00783   (
00784   Tile_coord dest,    // Destination.
00785   int speed,      // Time between frames (msecs).
00786   int delay,      // Delay before starting (msecs) (only
00787           //   if not already moving).
00788   int maxblk      // Max. # retries if blocked.
00789   )
00790   {
00791   if (!action)
00792     action = new Path_walking_actor_action(new Zombie(), maxblk);
00793   set_action(action->walk_to_tile(this, get_tile(), dest));
00794   if (action)     // Successful at setting path?
00795     start(speed, delay);
00796   else
00797     frame_time = 0;   // Not moving.
00798   }
00799 
00800 /*
00801  *  Find a path towards a given tile.
00802  *
00803  *  Output: 0 if failed.
00804  */
00805 
00806 int Actor::walk_path_to_tile
00807   (
00808   Tile_coord src,     // Our location, or an off-screen
00809           //   location to try path from.
00810   Tile_coord dest,    // Destination.
00811   int speed,      // Time between frames (msecs).
00812   int delay,      // Delay before starting (msecs) (only
00813           //   if not already moving).
00814   int dist,     // Distance to get within dest.
00815   int maxblk      // Max. # retries if blocked.
00816   )
00817   {
00818   set_action(new Path_walking_actor_action(new Astar(), maxblk));
00819   set_action(action->walk_to_tile(this, src, dest, dist));
00820   if (action)     // Successful at setting path?
00821     {
00822     start(speed, delay);
00823     return (1);
00824     }
00825   frame_time = 0;     // Not moving.
00826   return (0);
00827   }
00828 
00829 /*
00830  *  Begin animation.
00831  */
00832 
00833 void Actor::start
00834   (
00835   int speed,      // Time between frames (msecs).
00836   int delay     // Delay before starting (msecs) (only
00837           //   if not already moving).
00838   )
00839   {
00840   dormant = false;    // 14-jan-2001 - JSF.
00841   frame_time = speed;
00842   if (!in_queue() || delay) // Not already in queue?
00843     {
00844     if (delay)
00845       gwin->get_tqueue()->remove(this);
00846     uint32 curtime = Game::get_ticks();
00847     gwin->get_tqueue()->add(curtime + delay, this, 
00848           reinterpret_cast<long>(gwin));
00849     }
00850   }
00851 
00852 /*
00853  *  Stop animation.
00854  */
00855 void Actor::stop
00856   (
00857   )
00858   {
00859   /* +++ This might cause jerky walking. Needs to be done above? */
00860   if (action)
00861     {
00862     action->stop(this);
00863     add_dirty();
00864     }
00865   frame_time = 0;
00866   }
00867 
00868 /*
00869  *  Want one value to approach another.
00870  */
00871 
00872 inline int Approach
00873   (
00874   int from,
00875   int to,
00876   int dist      // Desired distance.
00877   )
00878   {
00879   if (from <= to)     // Going forwards?
00880     return (to - from <= dist ? from : to - dist);
00881   else        // Going backwards.
00882     return (from - to <= dist ? from : to + dist);
00883   }
00884 
00885 /*
00886  *  Follow the leader.
00887  */
00888 
00889 void Actor::follow
00890   (
00891   Actor *leader
00892   )
00893   {
00894   if (Actor::is_dead())
00895     return;     // Not when dead.
00896   int delay = 0;
00897   int dist;     // How close to aim for.
00898   Tile_coord leaderpos = leader->get_tile();
00899   Tile_coord pos = get_tile();
00900   Tile_coord goal;
00901   if (leader->is_moving())  // Figure where to aim.
00902     {     // Aim for leader's dest.
00903     dist = 2 + party_id/3;
00904     goal = leader->get_dest();
00905     goal.tx = Approach(pos.tx, goal.tx, dist);
00906     goal.ty = Approach(pos.ty, goal.ty, dist);
00907     }
00908   else        // Leader stopped?
00909     {
00910     goal = leaderpos; // Aim for leader.
00911     if (gwin->walk_in_formation && pos.distance(leaderpos) <= 6)
00912       return;   // In formation, & close enough.
00913 //    cout << "Follow:  Leader is stopped" << endl;
00914     // +++++For formation, why not get correct positions?
00915     static int xoffs[10] = {-1, 1, -2, 2, -3, 3, -4, 4, -5, 5},
00916          yoffs[10] = {1, -1, 2, -2, 3, -3, 4, -4, 5, -5};
00917     goal.tx += xoffs[party_id] + 1 - rand()%3;
00918     goal.ty += yoffs[party_id] + 1 - rand()%3;
00919     dist = 1;
00920     }
00921           // Already aiming along a path?
00922   if (is_moving() && action && action->following_smart_path() &&
00923           // And leader moving, or dest ~= goal?
00924     (leader->is_moving() || goal.distance(get_dest()) <= 5))
00925     return;
00926           // Tiles to goal.
00927   int goaldist = goal.distance(pos);
00928   if (goaldist < dist)    // Already close enough?
00929     {
00930     if (!leader->is_moving())
00931       stop();
00932     return;
00933     }
00934           // Is leader following a path?
00935   bool leaderpath = leader->action && 
00936         leader->action->following_smart_path();
00937           // Get leader's distance from goal.
00938   int leaderdist = goal.distance(leaderpos);
00939           // Get his speed.
00940   int speed = leader->get_frame_time();
00941   if (!speed)     // Not moving?
00942     {
00943     speed = 100;
00944     if (goaldist < leaderdist)  // Closer than leader?
00945           // Delay a bit IF not moving.
00946       delay = (1 + leaderdist - goaldist)*100;
00947     }
00948   if (goaldist - leaderdist >= 5)
00949     speed -= 20;    // Speed up if too far.
00950           // Get window rect. in tiles.
00951   Rectangle wrect = gwin->get_win_tile_rect();
00952   int dist2lead = pos.distance(leaderpos);
00953           // Getting kind of far away?
00954   if (dist2lead > wrect.w + wrect.w/2 &&
00955       party_id >= 0 &&    // And a member of the party.
00956       !leaderpath)    // But leader is not following path.
00957     {     // Approach, or teleport.
00958           // Try to approach from offscreen.
00959     if (approach_another(leader))
00960       return;
00961           // Find a free spot.
00962     goal = Map_chunk::find_spot(
00963         leader->get_tile(), 2, this);
00964     if (goal.tx != -1)
00965       {
00966       move(goal.tx, goal.ty, goal.tz);
00967       if (rand()%2)
00968         say(first_catchup, last_catchup);
00969       gwin->paint();
00970       return;
00971       }
00972     }
00973           // NOTE:  Avoid delay when moving,
00974           //  as it creates jerkiness.  AND,
00975           //  0 retries if blocked.
00976   walk_to_tile(goal, speed, delay, 0);
00977   }
00978 
00979 /*
00980  *  Approach another actor from offscreen.
00981  *
00982  *  Output: 0 if failed.
00983  */
00984 
00985 int Actor::approach_another
00986   (
00987   Actor *other,
00988   bool wait     // If true, game hangs until arrival.
00989   )
00990   {
00991   Tile_coord dest = other->get_tile();
00992           // Look outwards for free spot.
00993   dest = Map_chunk::find_spot(dest, 8, get_shapenum(), get_framenum());
00994   if (dest.tx == -1)
00995     return 0;
00996           // Where are we now?
00997   Tile_coord src = get_tile();
00998   if (!gwin->get_win_tile_rect().has_point(src.tx - src.tz/2, 
00999               src.ty - src.tz/2))
01000           // Off-screen?
01001     src = Tile_coord(-1, -1, 0);
01002   Actor_action *action = new Path_walking_actor_action();
01003   if (!action->walk_to_tile(this, src, dest))
01004     {
01005     delete action;
01006     return 0;
01007     }
01008   set_action(action);
01009   int speed = gwin->get_std_delay()/2;
01010   start(speed);     // Walk fairly fast.
01011   if (wait)     // Only wait ~1/5 sec.
01012     Wait_for_arrival(this, dest, 2*gwin->get_std_delay());
01013   return 1;
01014   }
01015 
01016 /*
01017  *  Get information about a tile that an actor is about to step onto.
01018  */
01019 
01020 void Actor::get_tile_info
01021   (
01022   Actor *actor,     // May be 0 if not known.
01023   Game_window *gwin,
01024   Map_chunk *nlist, // Chunk.
01025   int tx, int ty,     // Tile within chunk.
01026   int& water,     // Returns 1 if water.
01027   int& poison     // Returns 1 if poison.
01028   )
01029   {
01030   ShapeID flat = nlist->get_flat(tx, ty);
01031   if (flat.get_shapenum() == -1)
01032     water = poison = 0;
01033   else
01034     {
01035     Shape_info& finfo = flat.get_info();
01036     water = finfo.is_water();
01037     poison = finfo.is_poisonous();
01038           // Check for swamp/swamp boots.
01039     if (poison && actor)
01040       {
01041       Game_object *boots = actor->Actor::get_readied(
01042               Actor::feet);
01043       if (boots != 0 &&
01044               ((boots->get_shapenum() == 588 && 
01045           Game::get_game_type() == BLACK_GATE) ||
01046                (boots->get_shapenum() == 587 && 
01047           // SI:  Swamp/magic boots save you.
01048         (boots->get_framenum() == 6 ||
01049          boots->get_framenum() == 1) && 
01050         Game::get_game_type() == SERPENT_ISLE)))
01051         poison = 0;
01052       else
01053         { // Safe from poisoning?
01054         Monster_info *minf = 
01055           actor->get_info().get_monster_info();
01056         if (minf && minf->poison_safe())
01057           poison = 0;
01058         }
01059       }
01060     }
01061   }
01062 
01063 /*
01064  *  Set combat opponent.
01065  */
01066 
01067 void Actor::set_target
01068   (
01069   Game_object *obj,
01070   bool start_combat   // If true, set sched. to combat.
01071   )
01072   {
01073   target = obj;
01074   if (start_combat && (schedule_type != Schedule::combat || !schedule))
01075     set_schedule_type(Schedule::combat);
01076   }
01077 
01078 /*
01079  *  Works out if an item can be readied in a spot
01080  *
01081  *  Output: true if it does fit, or false if it can't
01082  */
01083 
01084 bool Actor::fits_in_spot (Game_object *obj, int spot, FIS_Type type)
01085 {
01086   // If occupied, can't place
01087   if (spots[spot])
01088     return false;
01089   // If want to use 2h or a 2h is already equiped, can't go in right
01090   else if ((type == FIS_2Hand || two_handed) && spot == rhand)
01091     return false;
01092   // If want to use 2f or a 2f is already equiped, can't go in right
01093   else if ((type == FIS_2Finger || two_fingered) && spot == rfinger)
01094     return false;
01095   // Can't use 2h in left if right occupied
01096   else if (type == FIS_2Hand && spot == lhand && spots[rhand])
01097     return false;
01098   // Can't use 2f in left if right occupied
01099   else if (type == FIS_2Finger && spot == lfinger && spots[rfinger])
01100     return false;
01101   // If in left or right hand allow it
01102   else if (spot == lhand || spot == rhand)
01103     return true;
01104   // Special Checks for Belt
01105   else if (spot == belt)
01106   {
01107     if (type == FIS_Spell)
01108       return true;
01109     else if (Game::get_game_type() == BLACK_GATE)
01110     {
01111       if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back2h_spot))
01112         return true;
01113       else if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), shield_spot))
01114         return true;
01115     }
01116   }
01117 
01118   // Lastly if we have gotten here, check the paperdoll table 
01119   return Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), spot);
01120 }
01121 
01122 /*
01123  *  Find the spot(s) where an item would prefer to be readied
01124  *
01125  *  Output: prefered slot, alternative slot, FIS_type
01126  */
01127 
01128 void Actor::get_prefered_slots
01129   (
01130   Game_object *obj,
01131   int &prefered,
01132   int &alternate,
01133   FIS_Type &fistype
01134   )
01135 {
01136 
01137   Shape_info& info = obj->get_info();
01138 
01139   // Defaults
01140   fistype = FIS_Other;
01141   prefered = lhand;
01142   alternate = lhand;
01143 
01144   if (Game::get_game_type() == BLACK_GATE)
01145   {
01146     Ready_type type = (Ready_type) info.get_ready_type();
01147     
01148     switch (type)
01149     {
01150       // Weapons, Sheilds, Spells, misc stuff
01151       default:
01152       if (type == spell || type == other_spell) fistype = FIS_Spell;
01153       else if (type == two_handed_weapon) fistype = FIS_2Hand;
01154 
01155       if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), rhand))
01156         prefered = rhand;
01157       else if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back))
01158         prefered = back;
01159       else
01160         alternate = rhand;
01161       break;
01162 
01163 
01164       case gloves:
01165       fistype = FIS_2Finger;
01166 
01167       case ring:
01168       prefered = lfinger;
01169       alternate = rfinger;
01170       break;
01171 
01172       case neck_armor:
01173       prefered = neck;
01174       break;
01175         
01176       case torso_armor:
01177       prefered = torso;
01178       alternate = neck; // This is a hack for cloaks. It shouldn't cause problems
01179       break;
01180         
01181       case ammunition:
01182       prefered = ammo;
01183       break;
01184       
01185       case head_armor:
01186       prefered = head;
01187       break;
01188         
01189       case leg_armor:
01190       prefered = legs;
01191       break;
01192         
01193       case foot_armor:
01194       prefered = feet;
01195       break;
01196     }
01197   }
01198   else if (Game::get_game_type() == SERPENT_ISLE) // Serpent Isle Types
01199   {
01200     Ready_type_SI type = (Ready_type_SI) info.get_ready_type();
01201     
01202     switch (type)
01203     {
01204       // Weapons, Sheilds, Spells, misc stuff
01205       default:
01206       if (type == spell_si|| type == other_spell_si) fistype = FIS_Spell;
01207       else if (type == two_handed_si) fistype = FIS_2Hand;
01208 
01209       if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), rhand))
01210         prefered = rhand;
01211       else
01212         alternate = rhand;
01213       break;
01214 
01215       case other:
01216       if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back2h_spot))
01217         prefered = back2h_spot;
01218       else if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back))
01219         prefered = back;
01220       else 
01221         alternate = rhand;
01222       break;
01223 
01224 
01225       case helm_si:
01226       prefered = head;
01227       break;
01228 
01229       case gloves_si:
01230       prefered = hands2_spot;
01231       break;
01232 
01233       case boots_si:
01234       prefered = feet;
01235       break;
01236 
01237       case leggings_si:
01238       prefered = legs;
01239       break;
01240       
01241       case amulet_si:
01242       prefered = neck;
01243       break;
01244         
01245       case armour_si:
01246       prefered = torso;
01247       break;
01248 
01249       case ring_si:
01250       prefered = lfinger;
01251       alternate = rfinger;
01252       break;
01253 
01254       case ammo_si:
01255       prefered = ammo;
01256       break;
01257   
01258       case cloak_si:
01259       prefered = cloak_spot;
01260       break;
01261 
01262       case usecode_container_si:
01263       prefered = ucont_spot;
01264       break;
01265       
01266       case earrings_si:
01267       prefered = ears_spot;
01268       break;
01269 
01270       case belt_si:
01271       prefered = belt;
01272       break;
01273       
01274       case backpack_si:
01275       prefered = back;
01276       break;
01277     }
01278   }
01279 }
01280 
01281 
01282 /*
01283  *  Find the best spot where an item may be readied.
01284  *
01285  *  Output: Index, or -1 if none found.
01286  */
01287 
01288 int Actor::find_best_spot
01289   (
01290   Game_object *obj
01291   )
01292 {
01293   int prefered;
01294   int alternate;
01295   FIS_Type type;
01296   bool SI = Game::get_game_type() == SERPENT_ISLE;
01297 
01298   // Get the preferences
01299   get_prefered_slots (obj, prefered, alternate, type);
01300 
01301   // Check Prefered
01302   if (fits_in_spot (obj, prefered, type)) return prefered;
01303   // Alternate
01304   else if (fits_in_spot (obj, alternate, type)) return alternate;
01305   // Belt
01306   else if (fits_in_spot (obj, belt, type)) return belt;
01307   // Back - required???
01308   else if (fits_in_spot (obj, back, type)) return back;
01309   // Back2h
01310   else if (SI && fits_in_spot (obj, back2h_spot, type)) return back2h_spot;
01311   // Sheild Spot
01312   else if (SI && fits_in_spot (obj, shield_spot, type)) return shield_spot;
01313   // Left Hand
01314   else if (fits_in_spot (obj, lhand, type)) return lhand;
01315   // Right Hand
01316   else if (fits_in_spot (obj, rhand, type)) return rhand;
01317 
01318   return -1;
01319 }
01320 
01321 /*
01322  *  Get previous schedule type.
01323  *
01324  *  Output: Prev. schedule #, or -1 if not known.
01325  */
01326 
01327 int Actor::get_prev_schedule_type
01328   (
01329   )
01330   {
01331   return schedule ? schedule->get_prev_type() : -1;
01332   }
01333 
01334 /*
01335  *  Restore actor's schedule after reading.  This CANNOT be called from
01336  *  a constructor, since it may call virtual methods when setting up
01337  *  the schedule.
01338  */
01339 
01340 void Actor::restore_schedule
01341   (
01342   )
01343   {
01344           // Make sure it's in valid chunk.
01345   Map_chunk *olist = gmap->get_chunk_safely(get_cx(), get_cy());
01346           // Activate schedule if not in party.
01347   if (olist && party_id < 0)
01348     {
01349     if (next_schedule != 255 && 
01350         schedule_type == Schedule::walk_to_schedule)
01351       set_schedule_and_loc(next_schedule, schedule_loc);
01352     else
01353       set_schedule_type(schedule_type);
01354     }
01355   }
01356 
01357 /*
01358  *  Set new schedule by type.
01359  */
01360 
01361 void Actor::set_schedule_type
01362   (
01363   int new_schedule_type,
01364   Schedule *newsched    // New sched., or 0 to create here.
01365   )
01366   {
01367   stop();       // Stop moving.
01368   if (schedule)     // Finish up old if necessary.
01369     schedule->ending(new_schedule_type);
01370   set_action(0);      // Clear out old action.
01371           // Save old for a moment.
01372   Schedule::Schedule_types old_schedule = (Schedule::Schedule_types)
01373                 schedule_type;
01374   delete schedule;    // Done with the old.
01375   schedule = newsched;
01376   if (!schedule)
01377     switch ((Schedule::Schedule_types) new_schedule_type)
01378       {
01379     case Schedule::combat:
01380       schedule = new Combat_schedule(this, old_schedule);
01381       break;
01382     case Schedule::horiz_pace:
01383       schedule = Pace_schedule::create_horiz(this);
01384       break;
01385     case Schedule::vert_pace:
01386       schedule = Pace_schedule::create_vert(this);
01387       break;
01388     case Schedule::talk:
01389       schedule = new Talk_schedule(this);
01390       break;
01391     case Schedule::dance:
01392       schedule = new Dance_schedule(this);
01393       break;
01394     case Schedule::farm:  // Use a scythe.
01395       schedule = new Tool_schedule(this, 618);
01396       break;
01397     case Schedule::tend_shop:// For now.
01398       schedule = new Loiter_schedule(this, 3);
01399       break;
01400     case Schedule::miner: // Use a pick.
01401       schedule = new Tool_schedule(this, 624);
01402       break;
01403     case Schedule::hound:
01404       schedule = new Hound_schedule(this);
01405       break;
01406     case Schedule::loiter:
01407     case Schedule::graze: // For now.
01408       schedule = new Loiter_schedule(this);
01409       break;
01410     case Schedule::wander:
01411       schedule = new Wander_schedule(this);
01412       break;
01413     case Schedule::blacksmith:
01414       schedule = new Forge_schedule(this);
01415       break;
01416     case Schedule::sleep:
01417       schedule = new Sleep_schedule(this);
01418       break;
01419     case Schedule::wait:
01420       schedule = new Wait_schedule(this);
01421       break;
01422     case Schedule::eat:   // For now.
01423     case Schedule::sit:
01424       schedule = new Sit_schedule(this);
01425       break;
01426     case Schedule::bake:
01427       schedule = new Bake_schedule(this);
01428       break;
01429     case Schedule::sew:
01430       schedule = new Sew_schedule(this);
01431       break;
01432     case Schedule::shy:
01433       schedule = new Shy_schedule(this);
01434       break;
01435     case Schedule::lab:
01436       schedule = new Lab_schedule(this);
01437       break;
01438     case Schedule::thief:   // Just face north, for now.
01439       gwin->add_dirty(this);
01440       unready_weapon(lhand);  // For Krieg in Empath Abbey.
01441       unready_weapon(rhand);
01442       set_frame(get_dir_framenum(0, Actor::standing));
01443       gwin->add_dirty(this);
01444       break;
01445     case Schedule::waiter:
01446       schedule = new Waiter_schedule(this);
01447       break;
01448     case Schedule::kid_games:
01449       schedule = new Kid_games_schedule(this);
01450       break;
01451     case Schedule::eat_at_inn:
01452       schedule = new Eat_at_inn_schedule(this);
01453       break;
01454     case Schedule::duel:
01455       schedule = new Duel_schedule(this);
01456       break;
01457     case Schedule::preach:
01458       schedule = new Preach_schedule(this);
01459       break;
01460     case Schedule::patrol:
01461       schedule = new Patrol_schedule(this);
01462       break;
01463     case Schedule::desk_work:
01464       schedule = new Desk_schedule(this);
01465       break;
01466     case Schedule::follow_avatar:
01467       schedule = new Follow_avatar_schedule(this);
01468       break;
01469     case Schedule::walk_to_schedule:
01470       cerr << "Attempted to set a \"walk to schedule\" activity for NPC "<< get_npc_num() << endl;
01471       break;
01472     default:
01473       break;
01474       }
01475           // Set AFTER creating new schedule.
01476   schedule_type = new_schedule_type;
01477 
01478   // Reset Next Schedule
01479   schedule_loc = Tile_coord(0,0,0);
01480   next_schedule = 255;
01481 
01482   if (!gmap->is_chunk_read(get_cx(), get_cy()))
01483     dormant = true;   // Chunk hasn't been read in yet.
01484   else if (schedule)    // Try to start it.
01485     {
01486     dormant = false;
01487     schedule->now_what();
01488     }
01489   }
01490 
01491 /*
01492  *  Cache out an actor. 
01493  *  Resets the schedule, and makes the actor dormant
01494  */
01495 void Actor::cache_out()
01496 {
01497   // This is a bit of a hack, but it works well enough
01498   if (get_schedule_type() != Schedule::walk_to_schedule)
01499     set_schedule_type(get_schedule_type());
01500 }
01501 
01502 
01503 /*
01504  *  Set new schedule by type AND location.
01505  */
01506 
01507 void Actor::set_schedule_and_loc (int new_schedule_type, Tile_coord dest,
01508         int delay)  // -1 for random delay.
01509 {
01510   stop();       // Stop moving.
01511   if (schedule)     // End prev.
01512     schedule->ending(new_schedule_type);
01513 
01514   if (!gmap->is_chunk_read(get_cx(), get_cy()) &&
01515       !gmap->is_chunk_read(dest.tx/c_tiles_per_chunk,
01516             dest.ty/c_tiles_per_chunk))
01517     {     // Src, dest. are off the screen.
01518     move(dest.tx, dest.ty, dest.tz);
01519     set_schedule_type(new_schedule_type);
01520     return;
01521     }
01522           // Going to walk there.
01523   schedule_loc = dest; 
01524   next_schedule = new_schedule_type;
01525   schedule_type = Schedule::walk_to_schedule;
01526   delete schedule;
01527   schedule = new Walk_to_schedule(this, dest, next_schedule, delay);
01528   dormant = false;
01529   schedule->now_what();
01530 }
01531 
01532 /*
01533  *  Render.
01534  */
01535 
01536 void Actor::paint
01537   (
01538   )
01539   {
01540           // In BG, dont_move means don't render.
01541   if (!(flags & (1L << Obj_flags::dont_move)) ||
01542       Game::get_game_type() == SERPENT_ISLE)
01543     {
01544     int xoff, yoff;
01545     gwin->get_shape_location(this, xoff, yoff);
01546     if (flags & (1L << Obj_flags::invisible))
01547       paint_invisible(xoff, yoff);
01548     else 
01549       paint_shape(xoff, yoff, true);
01550 
01551     paint_weapon();
01552     if (hit)    // Want a momentary red outline.
01553       ShapeID::paint_outline(xoff, yoff, HIT_PIXEL);
01554     else if (flags & ((1L<<Obj_flags::protection) | 
01555         (1L << Obj_flags::poisoned) | (1 << Obj_flags::cursed)))
01556       {
01557       if (flags & (1L << Obj_flags::poisoned))
01558         ShapeID::paint_outline(xoff,yoff,POISON_PIXEL);
01559       else if (flags & (1L << Obj_flags::cursed))
01560         ShapeID::paint_outline(xoff,yoff,CURSED_PIXEL);
01561       else
01562         ShapeID::paint_outline(xoff, yoff,
01563                 PROTECT_PIXEL);
01564       }
01565     }
01566   }
01567 /*
01568  *  Draw the weapon in the actor's hand (if any).
01569  */
01570 void Actor::paint_weapon
01571   (
01572   )
01573   {
01574   int weapon_x, weapon_y, weapon_frame;
01575   if (figure_weapon_pos(weapon_x, weapon_y, weapon_frame))
01576     {
01577     Game_object * weapon = spots[lhand];
01578     int shnum = weapon->get_shapenum();
01579     ShapeID wsid(shnum, weapon_frame);
01580     Shape_frame *wshape = wsid.get_shape();
01581     if (!wshape)
01582       {
01583       weapon_rect.w = 0;
01584       return;
01585       }
01586           // Set dirty area rel. to NPC.
01587     weapon_rect = gwin->get_shape_rect(wshape, weapon_x, weapon_y);
01588     // Paint the weapon shape using the actor's coordinates
01589     int xoff, yoff;
01590     gwin->get_shape_location(this, xoff, yoff);
01591     xoff += weapon_x;
01592     yoff += weapon_y;
01593 
01594     if (flags & (1L<<Obj_flags::invisible))
01595       wsid.paint_invisible(xoff, yoff);
01596     else
01597       wsid.paint_shape(xoff, yoff);
01598     }
01599   else
01600     weapon_rect.w = 0;
01601   }
01602 
01603 /*
01604  *  Figure weapon drawing info.  We need this in advance to set the dirty
01605  *  rectangle.
01606  *
01607  *  Output: 0 if don't need to paint weapon.
01608  */
01609 /* Weapon frames:
01610   0 - normal item
01611   1 - in hand, actor facing north/south
01612   2 - attacking (pointing north)
01613   3 - attacking (pointing east)
01614   4 - attacking (pointing south)
01615 */
01616 
01617 int Actor::figure_weapon_pos
01618   (
01619   int& weapon_x, int& weapon_y, // Pos. rel. to NPC.
01620   int& weapon_frame
01621   )
01622   {
01623   unsigned char actor_x, actor_y;
01624   unsigned char wx, wy;
01625   Game_object * weapon = spots[lhand];
01626   if(weapon == 0)
01627     return 0;
01628   // Get offsets for actor shape
01629   int myframe = get_framenum();
01630   get_info().get_weapon_offset(myframe & 0x1f, actor_x,
01631       actor_y);
01632   // Get offsets for weapon shape
01633   // NOTE: when combat is implemented, weapon frame should depend on
01634   // actor's current attacking frame
01635   weapon_frame = 1;
01636   int baseframe = myframe&0xf;
01637   if (gwin->in_combat() && Is_attack_frame(baseframe))
01638     {     // Get direction (0-4).
01639     int dir = Get_dir_from_frame(myframe);
01640     if (dir < 3)    // N, E, S?
01641       weapon_frame = 2 + dir;
01642     else      // W = N reflected.
01643       weapon_frame = 2 | 32;
01644     }
01645   weapon->get_info().get_weapon_offset(weapon_frame&0xf, wx,
01646       wy);
01647   // actor_x will be 255 if (for example) the actor is lying down
01648   // wx will be 255 if the actor is not holding a proper weapon
01649   if(actor_x != 255 && wx != 255)
01650     {     // Store offsets rel. to NPC.
01651     weapon_x = wx - actor_x;
01652     weapon_y = wy - actor_y;
01653     // Need to swap offsets if actor's shape is reflected
01654     if((get_framenum() & 32))
01655       {
01656       swap(weapon_x, weapon_y);
01657           // Combat frames are already done.
01658       if (weapon_frame == 1)
01659         weapon_frame |= 32;
01660       }
01661 #if 0 /* +++++Philanderer's Wand looks strange. */
01662           // Watch for valid frame.
01663     int nframes = gwin->get_shape_num_frames(
01664             weapon->get_shapenum());
01665     if ((weapon_frame&31) >= nframes)
01666       weapon_frame = (nframes - 1)|(weapon_frame&32);
01667 #endif
01668     return 1;
01669     }
01670   else
01671     return 0;
01672   }
01673 
01674 /*
01675  *  Run usecode when double-clicked.
01676  */
01677 void Actor::activate
01678   (
01679   int event
01680   )
01681   {
01682   if (edit())
01683     return;
01684   // We are serpent if we can use serpent isle paperdolls
01685   bool serpent = Game::get_game_type()==SERPENT_ISLE||
01686     (sman->can_use_paperdolls() && sman->get_bg_paperdolls());
01687   
01688   bool show_party_inv = gumpman->showing_gumps(true) || 
01689               gwin->in_combat();
01690   Schedule::Schedule_types sched = 
01691         (Schedule::Schedule_types) get_schedule_type();
01692   if (!npc_num ||   // Avatar
01693       (show_party_inv && party_id >= 0 && // Party
01694       (serpent || (npc_num >= 1 && npc_num <= 10))) ||
01695           // Pickpocket cheat && double click
01696       (cheat.in_pickpocket() && event == 1))
01697     show_inventory();
01698           // Asleep (but not awakened)?
01699   else if ((sched == Schedule::sleep &&
01700     (get_framenum()&0xf) == Actor::sleep_frame) ||
01701      get_flag(Obj_flags::asleep))
01702     return;
01703   else if (sched == Schedule::combat && party_id < 0)
01704     return;     // Too busy fighting.
01705           // Usecode
01706           // Failed copy-protection?
01707   else if (serpent &&
01708      gwin->get_main_actor()->get_flag(Obj_flags::confused))
01709     ucmachine->call_usecode(0x63d, this,
01710       (Usecode_machine::Usecode_events) event); 
01711   else if (usecode == -1)
01712     ucmachine->call_usecode(get_shapenum(), this,
01713       (Usecode_machine::Usecode_events) event);
01714   else if (party_id >= 0 || !gwin->is_time_stopped())
01715     ucmachine->call_usecode(usecode, this, 
01716       (Usecode_machine::Usecode_events) event);
01717   
01718   }
01719 
01720 /*
01721  *  Edit in ExultStudio.
01722  *
01723  *  Output: True if map-editing & ES is present.
01724  */
01725 
01726 bool Actor::edit
01727   (
01728   )
01729   {
01730 #ifdef USE_EXULTSTUDIO
01731   if (client_socket >= 0 && // Talking to ExultStudio?
01732       cheat.in_map_editor())
01733     {
01734     editing = 0;
01735     Tile_coord t = get_tile();
01736     unsigned long addr = reinterpret_cast<unsigned long>(this);
01737     int num_schedules;  // Set up schedule-change list.
01738     Schedule_change *changes;
01739     get_schedules(changes, num_schedules);
01740     Serial_schedule schedules[8];
01741     for (int i = 0; i < num_schedules; i++)
01742       {
01743       schedules[i].time = changes[i].get_time();
01744       schedules[i].type = changes[i].get_type();
01745       Tile_coord p = changes[i].get_pos();
01746       schedules[i].tx = p.tx;
01747       schedules[i].ty = p.ty;
01748       }
01749     if (Npc_actor_out(client_socket, addr, t.tx, t.ty, t.tz,
01750       get_shapenum(), get_framenum(), get_face_shapenum(),
01751       name, npc_num, ident, usecode, properties, attack_mode,
01752       alignment, flags, siflags, type_flags,
01753         num_schedules, schedules) != -1)
01754       {
01755       cout << "Sent npc data to ExultStudio" << endl;
01756       editing = this;
01757       }
01758     else
01759       cout << "Error sending npc data to ExultStudio" <<endl;
01760     return true;
01761     }
01762 #endif
01763   return false;
01764   }
01765 
01766 /*
01767  *  Message to update from ExultStudio.
01768  */
01769 
01770 void Actor::update_from_studio
01771   (
01772   unsigned char *data,
01773   int datalen
01774   )
01775   {
01776 #ifdef USE_EXULTSTUDIO
01777   unsigned long addr;
01778   int tx, ty, tz;
01779   int shape, frame, face;
01780   std::string name;
01781   short npc_num, ident;
01782   int usecode;
01783   int properties[12];
01784   short attack_mode, alignment;
01785   unsigned long oflags;   // Object flags.
01786   unsigned long siflags;    // Extra flags for SI.
01787   unsigned long type_flags; // Movement flags.
01788   short num_schedules;
01789   Serial_schedule schedules[8];
01790   if (!Npc_actor_in(data, datalen, addr, tx, ty, tz, shape, frame,
01791     face, name, npc_num, ident, usecode, 
01792       properties, attack_mode, alignment,
01793       oflags, siflags, type_flags, num_schedules, schedules))
01794     {
01795     cout << "Error decoding npc" << endl;
01796     return;
01797     }
01798   Actor *npc = (Actor *) addr;
01799   if (npc && npc != editing)
01800     {
01801     cout << "Npc from ExultStudio is not being edited" << endl;
01802     return;
01803     }
01804   editing = 0;
01805   if (!npc)     // Create a new one?
01806     {
01807     int x, y;
01808     if (!Get_click(x, y, Mouse::hand, 0))
01809       {
01810       if (client_socket >= 0)
01811         Exult_server::Send_data(client_socket, Exult_server::cancel);
01812       return;
01813       }
01814           // Create.  Gets initialized below.
01815     npc = new Npc_actor(name, shape, frame, usecode);
01816     npc->set_invalid(); // Set to invalid position.
01817     int lift;   // Try to drop at increasing hts.
01818     for (lift = 0; lift < 12; lift++)
01819       if (gwin->drop_at_lift(npc, x, y, lift))
01820         break;
01821     if (lift == 12)
01822       {
01823       if (client_socket >= 0)
01824         Exult_server::Send_data(client_socket, Exult_server::cancel);
01825       delete npc;
01826       return;
01827       }
01828     npc->npc_num = npc_num;
01829     gwin->add_npc(npc, npc_num);
01830     if (client_socket >= 0)
01831       Exult_server::Send_data(client_socket, Exult_server::user_responded);
01832     }
01833   else        // Old.
01834     {
01835     npc->add_dirty();
01836     npc->set_shape(shape, frame);
01837     npc->add_dirty();
01838     npc->usecode = usecode;
01839     npc->usecode_assigned = true;
01840     npc->set_npc_name(name.c_str());
01841     }
01842   npc->face_num = face;
01843   npc->set_ident(ident);
01844   int i;
01845   for (i = 0; i < 12; i++)
01846     npc->set_property(i, properties[i]);
01847   npc->set_attack_mode((Actor::Attack_mode) attack_mode);
01848   npc->set_alignment(alignment);
01849   npc->flags = oflags;
01850   npc->siflags = siflags;
01851   npc->type_flags = type_flags;
01852   Schedule_change *scheds = num_schedules ? 
01853         new Schedule_change[num_schedules] : 0;
01854   for (i = 0; i < num_schedules; i++)
01855     scheds[i].set(schedules[i].tx, schedules[i].ty, 
01856         schedules[i].type, schedules[i].time);
01857   npc->set_schedules(scheds, num_schedules);
01858   cout << "Npc updated" << endl;
01859 #endif
01860   }
01861 
01862 
01863 void Actor::show_inventory()
01864 {
01865   Gump_manager *gump_man = gumpman;
01866 
01867   int shapenum = inventory_shapenum();
01868   if (shapenum)
01869     gump_man->add_gump(this, shapenum);
01870 }
01871 
01872 int Actor::inventory_shapenum()
01873 {
01874   // We are serpent if we can use serpent isle paperdolls
01875   bool serpent = Game::get_game_type()==SERPENT_ISLE||(sman->can_use_paperdolls() && sman->get_bg_paperdolls());
01876   
01877   if (!npc_num && !serpent && get_type_flag(tf_sex))  // Avatar No paperdolls (female)
01878     return (ACTOR_FIRST_GUMP+1);
01879   else if (!npc_num && !serpent)  // Avatar No paperdolls
01880     return (ACTOR_FIRST_GUMP);
01881   else if (!npc_num && serpent) // Avatar Paperdolls
01882     return (123);
01883           // Gump/combat mode?
01884           // Show companions' pictures. (BG)
01885   else if (party_id >= 0 &&
01886      npc_num >= 1 && npc_num <= 10 && !serpent)
01887       return (ACTOR_FIRST_GUMP + 1 + npc_num);
01888   // Show companions' pictures. (SI)
01889   else if (party_id >= 0 && serpent)
01890     return (123);
01891   // Pickpocket Cheat Female no paperdolls
01892   else if (!serpent && Paperdoll_gump::IsNPCFemale(this->get_shapenum()))
01893     return (66);
01894   // Pickpocket Cheat Male no paperdolls
01895   else if (!serpent && !Paperdoll_gump::IsNPCFemale(this->get_shapenum()))
01896     return (65);
01897   // Pickpocket Cheat paperdolls
01898   else /* if (serpent) */
01899     return (123);
01900 }
01901 
01902 
01903 /*
01904  *  Drop another onto this.
01905  *
01906  *  Output: 0 to reject, 1 to accept.
01907  */
01908 
01909 int Actor::drop
01910   (
01911   Game_object *obj    // MAY be deleted (if combined).
01912   )
01913   {
01914   if (is_in_party())  // In party?
01915     return (add(obj, false, true)); // We'll take it, and combine.
01916   else
01917     return 0;
01918   }
01919 
01920 /*
01921  *  Get name.
01922  */
01923 
01924 string Actor::get_name
01925   (
01926   ) const
01927   {
01928   return !get_flag(Obj_flags::met)?Game_object::get_name():get_npc_name();
01929   }
01930 
01931 /*
01932  *  Get npc name.
01933  */
01934 
01935 string Actor::get_npc_name
01936   (
01937   ) const
01938   {
01939   return name.empty() ? Game_object::get_name() : name;
01940   }
01941 
01942 /*
01943  *  Set npc name.
01944  */
01945 
01946 void Actor::set_npc_name(const char *n)
01947 {
01948   name = n;
01949 }
01950 
01951 /*
01952  *  Set property.
01953  */
01954 void Actor::set_property
01955   (
01956   int prop, 
01957   int val
01958   )
01959   {
01960   if (prop == health && ((party_id != -1) || (npc_num == 0)) && 
01961     cheat.in_god_mode() && val < properties[prop])
01962     return;
01963   switch (static_cast<Item_properties>(prop))
01964     {
01965   case exp:
01966     {     // Experience?  Check for new level.
01967     int old_level = get_level();
01968     properties[static_cast<int>(exp)] = val;
01969     int delta = get_level() - old_level;
01970     if (delta > 0)
01971       properties[static_cast<int>(training)] += 3*delta;
01972     break;
01973     }
01974   case food_level:
01975     if (val > 36)   // Looks like max. in usecode.
01976       val = 36;
01977     else if (val < 0)
01978       val = 0;
01979     properties[prop] = val;
01980     break;
01981   case combat:      // These two are limited to 30.
01982   case magic:
01983     properties[prop] = val > 30 ? 30 : val;
01984     break;
01985   case training:      // Don't let this go negative.
01986     properties[prop] = val < 0 ? 0 : val;
01987     break;
01988   default:
01989     if (prop >= 0 && prop < 12)
01990       properties[prop] = val;
01991     break;
01992     }
01993   if (gumpman->showing_gumps())
01994     gwin->set_all_dirty();
01995   }
01996 
01997 /*
01998  *  A class whose whole purpose is to clear the 'hit' flag.
01999  */
02000 class Clear_hit : public Time_sensitive
02001   {
02002 public:
02003   Clear_hit()
02004     {  }
02005   virtual void handle_event(unsigned long curtime, long udata);
02006   };
02007 void Clear_hit::handle_event(unsigned long curtime, long udata)
02008   { 
02009   Actor *a = reinterpret_cast<Actor*>(udata);
02010   a->hit = false;
02011   a->add_dirty();
02012   delete this;
02013   }
02014 
02015 /*
02016  *  This method should be called to decrement health from attacks, traps.
02017  *
02018  *  Output: true if defeated.
02019  */
02020 
02021 bool Actor::reduce_health
02022   (
02023   int delta,      // # points to lose.
02024   Actor *attacker     // Attacker, or null.
02025   )
02026   {
02027   if (cheat.in_god_mode() && ((party_id != -1) || (npc_num == 0)))
02028     return false;
02029   Monster_info *minf = get_info().get_monster_info();
02030   if (minf && minf->cant_die()) // In BG, this is Batlin/LB.
02031     return false;
02032           // Watch for Skara Brae ghosts.
02033   if (npc_num > 0 && Game::get_game_type() == BLACK_GATE &&
02034       get_info().has_translucency() &&
02035       party_id < 0) // Don't include Spark here!!
02036     return false;
02037           // Being a bully (in BG)?
02038   if (attacker && attacker->is_in_party() && GAME_BG &&
02039       npc_num > 0 &&
02040       (alignment == Actor::friendly || alignment == Actor::neutral) &&
02041       get_info().get_shape_class() == Shape_info::human)
02042     {
02043     static long lastcall = 0L;  // Last time yelled.
02044     long curtime = SDL_GetTicks();
02045     long delta = curtime - lastcall;
02046     if (delta > 10000)  // Call if 10 secs. has passed.
02047       {
02048       eman->remove_text_effect(this);
02049       say(first_call_police, last_call_police);
02050       lastcall = curtime;
02051       gwin->attack_avatar(1 + rand()%2);
02052       }
02053     else if (rand()%4 == 0)
02054       {
02055       cout << "Rand()%4" << endl;
02056       gwin->attack_avatar(1 + rand()%2);
02057       }
02058     }
02059   bool defeated = false;
02060   int oldhp = properties[static_cast<int>(health)];
02061   int maxhp = properties[static_cast<int>(strength)];
02062   int val = oldhp - delta;
02063   properties[static_cast<int>(health)] = val;
02064   if (this == gwin->get_main_actor() && val < maxhp/8 &&
02065           // Flash red if Avatar badly hurt.
02066       rand()%2)
02067     gwin->get_pal()->flash_red();
02068   else
02069     {
02070     hit = true;   // Flash red outline.
02071     add_dirty();
02072     Clear_hit *c = new Clear_hit();
02073     gwin->get_tqueue()->add(Game::get_ticks() + 200, c, 
02074           reinterpret_cast<long>(this));
02075     }
02076   if (oldhp >= maxhp/2 && val < maxhp/2 && rand()%2 != 0)
02077     {     // A little oomph.
02078           // Goblin?
02079     if (GAME_SI &&
02080        (get_shapenum() == 0x1de ||
02081         get_shapenum() == 0x2b3 ||
02082         get_shapenum() == 0x2d5 ||
02083         get_shapenum() == 0x2e8))
02084       say(0x4d2, 0x4da);
02085     else if (!minf || !minf->cant_yell())
02086       say(first_ouch, last_ouch);
02087     }
02088   Game_object_vector vec;   // Create blood.
02089   const int blood = 912;
02090   if (delta >= 3 && (!minf || !minf->cant_bleed()) &&
02091       rand()%2 && find_nearby(vec, blood, 1, 0) < 2)
02092     {     // Create blood where actor stands.
02093     Game_object *bobj = gmap->create_ireg_object(blood, 0);
02094     bobj->set_flag(Obj_flags::is_temporary);
02095     bobj->move(get_tile());
02096     }
02097   if (Actor::is_dying())
02098     {
02099     if (Game::get_game_type() == SERPENT_ISLE &&
02100           // SI 'tournament'?
02101         get_flag(Obj_flags::si_tournament))
02102       {
02103       ucmachine->call_usecode(get_usecode(), this, 
02104               Usecode_machine::died);
02105         // Still 'tournament'?  Set hp = 1.
02106       if (!is_dead() && get_flag(Obj_flags::si_tournament) &&
02107           get_property(static_cast<int>(health)) < 1)
02108         {
02109         set_property(static_cast<int>(health), 1);
02110         if (get_attack_mode() == Actor::flee)
02111           defeated = true;
02112         }
02113       }
02114     else
02115       die(attacker);
02116     defeated = defeated || is_dead();
02117     }
02118   else if (val < 0 && !get_flag(Obj_flags::asleep) &&
02119           !get_flag(Obj_flags::si_tournament))
02120     set_flag(Obj_flags::asleep);
02121   return defeated;
02122   }
02123 
02124 /*
02125  *  Get a property, modified by flags.
02126  */
02127 
02128 int Actor::get_effective_prop
02129   (
02130   int prop        // Property #.
02131   ) const
02132   {
02133   int val = properties[prop];
02134   switch (prop)
02135     {
02136   case Actor::dexterity:
02137   case Actor::intelligence:
02138   case Actor::combat:
02139   case Actor::strength:
02140     if (get_flag(Obj_flags::might))
02141       val += (val < 15 ? val : 15); // Add up to 15.
02142     if (get_flag(Obj_flags::cursed))
02143       val /= 2;
02144     break;
02145     }
02146   return val;
02147   }
02148 
02149 /*
02150  *  Set flag.
02151  */
02152 
02153 void Actor::set_flag
02154   (
02155   int flag
02156   )
02157   {
02158 //  cout << "Set flag for NPC " << get_npc_num() << " = " << flag << endl;
02159 
02160   if (flag >= 0 && flag < 32)
02161     flags |= ((uint32) 1 << flag);
02162   else if (flag >= 32 && flag < 64)
02163     flags2 |= ((uint32) 1 << (flag-32));
02164   switch (flag)
02165     {
02166   case Obj_flags::asleep:
02167           // Check sched. to avoid waking
02168           //   Penumbra.
02169     if (schedule_type == Schedule::sleep)
02170       break;
02171           // Set timer to wake in a few secs.
02172     need_timers()->start_sleep();
02173     if ((get_framenum()&0xf) != Actor::sleep_frame &&
02174           // Watch for slimes.
02175         !get_info().has_strange_movement() &&
02176         get_shapenum() > 0) // (In case not initialized.)
02177           // Lie down.
02178       change_frame(Actor::sleep_frame + ((rand()%4)<<4));
02179     set_action(0);    // Stop what you're doing.
02180     break;
02181   case Obj_flags::poisoned:
02182     need_timers()->start_poison();
02183     break;
02184   case Obj_flags::protection:
02185     need_timers()->start_protection();
02186     break;
02187   case Obj_flags::might:
02188     need_timers()->start_might();
02189     break;
02190   case Obj_flags::cursed:
02191     need_timers()->start_curse();
02192     break;
02193   case Obj_flags::paralyzed:
02194     need_timers()->start_paralyze();
02195     break;
02196   case Obj_flags::invisible:
02197     need_timers()->start_invisibility();
02198     gclock->set_palette();
02199     break;
02200   case Obj_flags::dont_move:
02201     stop();     // Added 7/6/03.
02202     break;
02203     }
02204           // Update stats if open.
02205   if (gumpman->showing_gumps())
02206     gwin->set_all_dirty();
02207   set_actor_shape();
02208   }
02209 
02210 void Actor::set_siflag
02211   (
02212   int flag
02213   )
02214   {
02215   if (flag >= 0 && flag < 32)
02216     siflags |= (static_cast<uint32>(1) << flag);
02217 
02218   set_actor_shape();
02219   }
02220 
02221 void Actor::set_type_flag
02222   (
02223   int flag
02224   )
02225   {
02226   if (flag >= 0 && flag < 16)
02227     type_flags |= (static_cast<uint32>(1) << flag);
02228 
02229   set_actor_shape();
02230   }
02231 
02232 /*
02233  *  Clear flag.
02234  */
02235 
02236 void Actor::clear_flag
02237   (
02238   int flag
02239   )
02240   {
02241 //  cout << "Clear flag for NPC " << get_npc_num() << " = " << flag << endl;
02242   if (flag >= 0 && flag < 32)
02243     flags &= ~(static_cast<uint32>(1) << flag);
02244   else if (flag >= 32 && flag < 64)
02245     flags2 &= ~(static_cast<uint32>(1) << (flag-32));
02246   if (flag == Obj_flags::invisible) // Restore normal palette.
02247     gclock->set_palette();
02248   else if (flag == Obj_flags::asleep)
02249     {
02250     if (schedule_type == Schedule::sleep)
02251       set_schedule_type(Schedule::stand);
02252     else if ((get_framenum()&0xf) == Actor::sleep_frame)
02253       {   // Find spot to stand.
02254       Tile_coord pos = get_tile();
02255       pos.tz -= pos.tz%5; // Want floor level.
02256       pos = Map_chunk::find_spot(pos, 6, get_shapenum(),
02257         Actor::standing, 0);
02258       if (pos.tx >= 0)
02259         move(pos);
02260       change_frame(Actor::standing);
02261       }
02262     }
02263   set_actor_shape();
02264   }
02265 
02266 void Actor::clear_siflag
02267   (
02268   int flag
02269   )
02270   {
02271   if (flag >= 0 && flag < 32)
02272     siflags &= ~(static_cast<uint32>(1) << flag);
02273 
02274   set_actor_shape();
02275   }
02276 
02277 void Actor::clear_type_flag
02278   (
02279   int flag
02280   )
02281   {
02282   if (flag >= 0 && flag < 16)
02283     type_flags &= ~(static_cast<uint32>(1) << flag);
02284 
02285   set_actor_shape();
02286   }
02287 
02288 /*
02289  *  Get flags.
02290  */
02291 
02292 int Actor::get_siflag
02293   (
02294   int flag
02295   ) const
02296   {
02297   return (flag >= 0 && flag < 32) ? (siflags & (static_cast<uint32>(1) << flag))
02298       != 0 : 0;
02299   }
02300 
02301 int Actor::get_type_flag
02302   (
02303   int flag
02304   ) const
02305   {
02306   return (flag >= 0 && flag < 16) ? (type_flags & (static_cast<uint32>(1) << flag))
02307       != 0 : 0;
02308   }
02309 /*
02310  *  SetFlags
02311  */
02312 
02313 void Actor::set_type_flags
02314   (
02315   unsigned short tflags
02316   )
02317   {
02318   type_flags = tflags;
02319   set_actor_shape();
02320   }
02321 
02322 /*
02323  *  Set temperature.
02324  */
02325 
02326 void Actor::set_temperature
02327   (
02328   int v       // Should be 0-63.
02329   )
02330   {
02331   if (v < 0)
02332     v = 0;
02333   else if (v > 63)
02334     v = 63;
02335   temperature = v;
02336   }
02337 
02338 /*
02339  *  Figure warmth based on what's worn.  In trying to mimic the original
02340  *  SI, the base value is -75.
02341  */
02342 
02343 int Actor::figure_warmth
02344   (
02345   )
02346   {
02347   static short hats[5] = {10, 35, -8, 20, 35};
02348   static short cloaks[5] = {30, 70, 70, 70, 70};
02349   static short boots[7] = {85, 65, 50, 90, 85, 75, 80};
02350 
02351   int warmth = -75;   // Base value.
02352   int frnum;
02353   Game_object *worn = spots[static_cast<int>(head)];
02354   if (worn && worn->get_shapenum() == 1004 &&
02355       (frnum = worn->get_framenum()) < sizeof(hats)/sizeof(hats[0]))
02356     warmth += hats[frnum];
02357   if (worn && worn->get_shapenum() == 1013)
02358     warmth += hats[1]; // Helm of Light behaves like fur hat
02359   worn = spots[static_cast<int>(cloak_spot)]; // Cloak.
02360   if (worn && worn->get_shapenum() == 227 &&
02361       (frnum = worn->get_framenum()) < sizeof(cloaks)/sizeof(cloaks[0]))
02362     warmth += cloaks[frnum];
02363   worn = spots[static_cast<int>(feet)];
02364   if (worn && worn->get_shapenum() == 587 &&
02365       (frnum = worn->get_framenum()) < sizeof(boots)/sizeof(boots[0]))
02366     warmth += boots[frnum];
02367           // Leather armor?
02368   worn = spots[static_cast<int>(torso)];
02369   if (worn && worn->get_shapenum() == 569)
02370     warmth += 20;
02371   worn = spots[static_cast<int>(hands2_spot)];// Gloves?
02372   if (worn && worn->get_shapenum() == 579)
02373     warmth += 7;
02374   worn = spots[static_cast<int>(legs)]; // Legs?
02375   if (worn)
02376     switch (worn->get_shapenum())
02377       {
02378     case 686:   // Magic leggings.
02379       warmth += 5; break;
02380     case 574:   // Leather.
02381       warmth += 10; break;
02382       }
02383   return warmth;
02384   }
02385 
02386 /*
02387  *  Get maximum weight in stones that can be held.
02388  *
02389  *  Output: Max. allowed, or 0 if no limit (i.e., not carried by an NPC).
02390  */
02391 
02392 int Actor::get_max_weight
02393   (
02394   )
02395   {
02396   return 2*get_effective_prop(static_cast<int>(Actor::strength));
02397   }
02398 
02399 /*
02400  *  Call usecode function for an object that's readied/unreadied.
02401  */
02402 
02403 void Actor::call_readied_usecode
02404   (
02405   int index,
02406   Game_object *obj,
02407   int eventid
02408   )
02409   {
02410           // Limit to certain types.
02411   if (Game::get_game_type() == BLACK_GATE)
02412     switch (obj->get_shapenum())
02413       {
02414     case 297:   // Fix special case:  ring of protect.
02415       if (eventid == Usecode_machine::readied)
02416         Actor::set_flag(Obj_flags::protection);
02417       else
02418         Actor::clear_flag(Obj_flags::protection);
02419       return;
02420     case 296:   // Ring of invibility.
02421     case 298:   // Ring of regeneration.
02422     case 701:   // Lit torch.
02423     case 338:   // Lit light source.
02424       break;    // We'll do these.
02425     default:
02426       return;   // Nothing else in BG.
02427       }
02428   else if (Game::get_game_type() == SERPENT_ISLE)
02429     switch (obj->get_shapenum())
02430       {
02431     case 209:   // ??
02432     case 296:   // Rings.
02433     case 701:   // Lit torch.
02434     case 338:   // Lit light source.
02435     case 806:   // Black sword.
02436     case 990:   // Erinons Axe.
02437     case 996:   // Belt of Strength.
02438     case 1001:    // Guantlets of Quickness.
02439     case 1013:    // Helm of Light.
02440       break;    // Accept these.
02441     default:
02442       return;
02443       }
02444 
02445   Shape_info& info = obj->get_info();
02446   if (info.get_shape_class() != Shape_info::container)
02447     {
02448     Ready_type type = (Ready_type) info.get_ready_type();
02449     if (type != other)
02450       ucmachine->call_usecode(obj->get_shapenum(),
02451           obj, (Usecode_machine::Usecode_events) eventid);
02452     }
02453   }
02454 
02455 /*
02456  *  Should be called after actors and usecode are initialized.
02457  */
02458 
02459 void Actor::init_readied
02460   (
02461   )
02462   {
02463   if (spots[lfinger])
02464     call_readied_usecode(lfinger, spots[lfinger],
02465             Usecode_machine::readied);
02466   if (spots[rfinger])
02467     call_readied_usecode(rfinger, spots[rfinger],
02468             Usecode_machine::readied);
02469   if (spots[belt])
02470     call_readied_usecode(belt, spots[belt],
02471             Usecode_machine::readied);
02472   if (spots[neck])
02473     call_readied_usecode(neck, spots[neck],
02474             Usecode_machine::readied);
02475   if (spots[head])
02476     call_readied_usecode(head, spots[head],
02477             Usecode_machine::readied);
02478   if (spots[hands2_spot])
02479     call_readied_usecode(hands2_spot, 
02480         spots[hands2_spot], Usecode_machine::readied);
02481   if (spots[lhand])
02482     call_readied_usecode(lhand, spots[lhand],
02483             Usecode_machine::readied);
02484   if (spots[rhand])
02485     call_readied_usecode(rhand, spots[rhand],
02486             Usecode_machine::readied);
02487   }
02488 
02489 /*
02490  *  Remove an object.
02491  */
02492 
02493 void Actor::remove
02494   (
02495   Game_object *obj
02496   )
02497   {
02498   int index = Actor::find_readied(obj); // Remove from spot.
02499           // Note:  gwin->drop() also does this,
02500           //   but it needs to be done before
02501           //   removal too.
02502           // Definitely DO NOT call if dead!
02503   if (!ucmachine->in_usecode() && !is_dead())
02504     call_readied_usecode(index, obj,
02505             Usecode_machine::unreadied);
02506   Container_game_object::remove(obj);
02507   if (index >= 0)
02508     {     // Update light-source count.
02509     if (obj->get_info().is_light_source())
02510       light_sources--;
02511     spots[index] = 0;
02512     if (index == rhand || index == lhand)
02513       two_handed = false;
02514     if (index == rfinger || index == lfinger)
02515       two_fingered = false;
02516     if (index == lhand && schedule)
02517       schedule->set_weapon(); 
02518     }
02519   }
02520 
02521 /*
02522  *  Add an object.
02523  *
02524  *  Output: 1, meaning object is completely contained in this,
02525  *    0 if not enough space.
02526  */
02527 
02528 bool Actor::add
02529   (
02530   Game_object *obj,
02531   bool dont_check,    // 1 to skip volume check (AND also
02532           //   to skip usecode call).
02533   bool combine      // True to try to combine obj.  MAY
02534           //   cause obj to be deleted.
02535   )
02536   {
02537 
02538   int index, a; 
02539   FIS_Type type;
02540   get_prefered_slots (obj, index, a, type);
02541   index = find_best_spot(obj);// Where should it go?
02542     
02543   if (index < 0)      // No free spot?  Look for a bag.
02544   {
02545     if (spots[back] && spots[back]->add(obj, false, combine))
02546       return true;
02547     if (spots[belt] && spots[belt]->add(obj, false, combine))
02548       return true;
02549     if (spots[lhand] && spots[lhand]->add(obj, false, combine))
02550       return true;
02551     if (spots[rhand] && spots[rhand]->add(obj, false, combine))
02552       return true;
02553     if (!dont_check)
02554       return false;
02555 
02556     // try again without checking volume/weight
02557     if (spots[back] && spots[back]->add(obj, true, combine))
02558       return true;
02559     if (spots[belt] && spots[belt]->add(obj, true, combine))
02560       return true;
02561     if (spots[lhand] && spots[lhand]->add(obj, true, combine))
02562       return true;
02563     if (spots[rhand] && spots[rhand]->add(obj, true, combine))
02564       return true;
02565 
02566     if (party_id != -1 || npc_num==0) {
02567       CERR("Warning: adding object (" << obj << ", sh. " << obj->get_shapenum() << ", " << obj->get_name() << ") to actor container (npc " << npc_num << ")"); 
02568     }
02569     return Container_game_object::add(obj, dont_check, combine);
02570   }
02571           // Add to ourself (DON'T combine).
02572   if (!Container_game_object::add(obj, true))
02573     return false;
02574 
02575   if (type == FIS_2Hand)    // Two-handed?
02576     two_handed = true;
02577   if (type == FIS_2Finger) {  // Gloves?
02578     index = lfinger;
02579     two_fingered = true;
02580   }
02581 
02582   spots[index] = obj;   // Store in correct spot.
02583   if (index == lhand && schedule)
02584     schedule->set_weapon(); // Tell combat-schedule about it.
02585   obj->set_chunk(0, 0);   // Clear coords. (set by gump).
02586           // (Readied usecode now in drop().)
02587   if (obj->get_info().is_light_source())
02588     light_sources++;
02589   return true;
02590   }
02591 
02592 /*
02593  *  Add to given spot.
02594  *
02595  *  Output: 1 if successful, else 0.
02596  */
02597 
02598 int Actor::add_readied
02599   (
02600   Game_object *obj,
02601   int index,      // Spot #.
02602   int dont_check,
02603   int force_pos
02604   )
02605 {
02606   // Is Out of range?
02607   if (index < 0 || index >= static_cast<int>(sizeof(spots)/sizeof(spots[0])))
02608     return (0);   
02609 
02610   // Already something there? Try to drop into it.
02611   // +++++Danger:  drop() can potentially delete the object.
02612 //  if (spots[index]) return (spots[index]->drop(obj));
02613   if (spots[index]) return (spots[index]->add(obj));
02614 
02615   int prefered;
02616   int alternate;
02617   FIS_Type type;
02618 
02619   // Get the preferences
02620   get_prefered_slots (obj, prefered, alternate, type);
02621   
02622   // Check Prefered
02623   if (!fits_in_spot (obj, index, type) && !force_pos) return 0;
02624 
02625   // No room, or too heavy.
02626   if (!Container_game_object::add(obj, 1)) return 0;
02627 
02628   // Set the spot to this object
02629   spots[index] = obj;
02630 
02631   // Clear coords. (set by gump).
02632   obj->set_chunk(0, 0);
02633 
02634   // Must be a two-handed weapon.
02635   if (type == FIS_2Hand && index == lhand) two_handed = true;
02636 
02637   // Must be gloves
02638   if (type == FIS_2Finger && index == lfinger) two_fingered = true;
02639 
02640   // Usecode?  NOTE:  Done in gwin->drop() now.
02641 //  if (!dont_check)
02642 //    call_readied_usecode(index, obj,
02643 //            Usecode_machine::readied);
02644 
02645   // Lightsource?
02646   if (obj->get_info().is_light_source()) light_sources++;
02647 
02648   if (index == lhand && schedule)
02649     schedule->set_weapon(); // Tell combat-schedule about it.
02650   return 1;
02651 }
02652 
02653 /*
02654  *  Find index of spot where an object is readied.
02655  *
02656  *  Output: Index, or -1 if not found.
02657  */
02658 
02659 int Actor::find_readied
02660   (
02661   Game_object *obj
02662   )
02663   {
02664   for (size_t i = 0; i < sizeof(spots)/sizeof(spots[0]); i++)
02665     if (spots[i] == obj)
02666       return (i);
02667   return (-1);
02668   }
02669 
02670 /*
02671  *  Change shape of a member.
02672  */
02673 
02674 void Actor::change_member_shape
02675   (
02676   Game_object *obj,
02677   int newshape
02678   )
02679   {
02680   if (obj->get_info().is_light_source())
02681     light_sources--;
02682   Container_game_object::change_member_shape(obj, newshape);
02683   if (obj->get_info().is_light_source())
02684     light_sources++;
02685   }
02686 
02687 /*
02688  *  Step aside to a free tile, or try to swap places.
02689  *
02690  *  Output: 1 if successful, else 0.
02691  */
02692 
02693 int Actor::move_aside
02694   (
02695   Actor *for_actor,   // Moving aside for this one.
02696   int dir       // Direction to avoid (0-7).
02697   )
02698   {
02699   Tile_coord cur = get_tile();
02700   int opp = (dir + 4)%8;    // Don't go in opposite dir. either.
02701   Tile_coord to(-1, -1, -1);
02702   int i;
02703   for (i = 0; i < 8; i++)   // Go through directions.
02704     if (i == dir || i == opp)
02705       continue; // Don't go that way.
02706     else
02707       {
02708       to = cur.get_neighbor(i);
02709           // Assume height = 3.
02710       if (!Map_chunk::is_blocked(
02711             to, 3, get_type_flags()))
02712         break;
02713       }
02714   int stepdir = i;    // This is the direction.
02715   if (i == 8 || to.tx < 0)  // Failed?  Try to swap places.
02716     return swap_positions(for_actor);
02717           // Step, and face direction.
02718   step(to, get_dir_framenum(stepdir,static_cast<int>(Actor::standing)));
02719   Tile_coord newpos = get_tile();
02720   return (newpos.tx == to.tx && newpos.ty == to.ty);
02721   }
02722 
02723 /*
02724  *  Get frame if rotated 1, 2, or 3 quadrants clockwise.
02725  */
02726 
02727 int Actor::get_rotated_frame
02728   (
02729   int quads     // 1=90, 2=180, 3=270.
02730   )
02731   {
02732   int curframe = get_framenum();
02733           // Bit 4=rotate180, 5=rotate-90.
02734   int curdir = (4 + 2*((curframe>>4)&1) - ((curframe>>5)&1))%4;
02735   int newdir = (curdir + quads)%4;
02736           // Convert to 8-value dir & get frame.
02737   return get_dir_framenum(2*newdir, curframe);
02738   }
02739 
02740 /*
02741  *  Get total value of armor being worn.
02742  */
02743 
02744 int Actor::get_armor_points
02745   (
02746   )
02747   {
02748   int points = 0;
02749   static enum Spots aspots[] = {neck, torso, lfinger, rfinger, head,
02750           rhand, legs, feet};
02751   const int num_armor_spots = sizeof(aspots)/sizeof(aspots[0]);
02752   for (int i = 0; i < num_armor_spots; i++)
02753     {
02754     Game_object *armor = spots[static_cast<int>(aspots[i])];
02755     if (armor)
02756       points += armor->get_info().get_armor();
02757     }
02758   return points;
02759   }
02760 
02761 /*
02762  *  Get weapon value.
02763  */
02764 
02765 Weapon_info *Actor::get_weapon
02766   (
02767   int& points,
02768   int& shape
02769   )
02770   {
02771   points = 1;     // Bare hands = 1.
02772   Weapon_info *winf = 0;
02773   Game_object *weapon = spots[static_cast<int>(lhand)];
02774   if (weapon)
02775     if ((winf = weapon->get_info().get_weapon_info()) != 0)
02776       {
02777       points = winf->get_damage();
02778       shape = weapon->get_shapenum();
02779       }
02780           // Try both hands.
02781   weapon = spots[static_cast<int>(rhand)];
02782   if (weapon)
02783     {
02784     Weapon_info *rwinf = weapon->get_info().get_weapon_info();
02785     int rpoints;
02786     if (rwinf && (rpoints = rwinf->get_damage()) > points)
02787       {
02788       winf = rwinf;
02789       points = rpoints;
02790       shape = weapon->get_shapenum();
02791       }
02792     }
02793   return winf;
02794   }
02795 
02796 /*
02797  *  Roll a 25-sided die to determine a win-lose outcome, adding a
02798  *  bias for the attacker and for the defender.
02799  */
02800 
02801 bool Actor::roll_to_win
02802   (
02803   int attacker,     // Points added.
02804   int defender      // Points subtracted.
02805   )
02806   {
02807   const int sides = 25;
02808   int roll = rand()%sides;
02809   if (roll == 0)      // Always lose.
02810     return false;
02811   else if (roll == sides - 1) // High?  Always win.
02812     return true;
02813   else
02814     return roll + attacker - defender >= sides/2;
02815   }
02816 
02817 /*
02818  *  Get effective property, or default value.
02819  */
02820 
02821 inline int Get_effective_prop
02822   (
02823   Actor *npc,     // ...or NULL.
02824   Actor::Item_properties prop,  // Property #.
02825   int defval = 0      // Default val if npc==0.
02826   )
02827   {
02828   return npc ? npc->get_effective_prop(prop) : defval;
02829   }
02830 
02831 /*
02832  *  Figure hit points lost from an attack, and subtract from total.
02833  *
02834  *  Output: True if defeated (dead, or lost battle on List Field).
02835  */
02836 
02837 bool Actor::figure_hit_points
02838   (
02839   Actor *attacker,    // 0 if hit from a missile egg.
02840   int weapon_shape,   // Negative if from an explosion.
02841   int ammo_shape
02842   )
02843   {
02844   bool were_party = party_id != -1 || npc_num == 0;
02845   // godmode effects:
02846   if (were_party && cheat.in_god_mode())
02847     return false;
02848   Monster_info *minf = get_info().get_monster_info();
02849   if (minf && minf->cant_die()) // In BG, this is Batlin/LB.
02850     return false;
02851   bool explosion = false;   // From an explosion?
02852   if (weapon_shape < 0)   // Don't want recusive explosions.
02853     { explosion = true; weapon_shape = -weapon_shape; }
02854   bool theyre_party = attacker &&
02855       (attacker->party_id != -1 || attacker->npc_num == 0);
02856   bool instant_death = (cheat.in_god_mode() && theyre_party);
02857           // Modify using combat difficulty.
02858   int bias = were_party ? Combat::difficulty :
02859       (theyre_party ? -Combat::difficulty : 0);
02860   int armor = get_armor_points() - bias;
02861   int wpoints;
02862   Weapon_info *winf;
02863   if (weapon_shape > 0)
02864     {
02865     winf = ShapeID::get_info(weapon_shape).get_weapon_info();
02866     wpoints = winf ? winf->get_damage() : 0;
02867     }
02868   else if (ammo_shape > 0)
02869     {
02870     winf = ShapeID::get_info(ammo_shape).get_weapon_info();
02871     wpoints = winf ? winf->get_damage() : 0;
02872     }
02873   else
02874     {
02875     winf = attacker->get_weapon(wpoints, weapon_shape);
02876     if (!wpoints)
02877       wpoints = 1;  // Always give at least one.
02878     }
02879           // Get bonus ammo points.
02880   Ammo_info *ainf = ammo_shape > 0 ? 
02881       ShapeID::get_info(ammo_shape).get_ammo_info() : 0;
02882   if (ainf)
02883     wpoints += ainf->get_damage();
02884   int usefun;     // See if there's usecode for it.
02885   if (winf && (usefun = winf->get_usecode()) != 0)
02886     ucmachine->call_usecode(usefun, this,
02887           Usecode_machine::weapon);
02888           // Same for ammo.
02889   if (ammo_shape == 0x238 && Game::get_game_type() == SERPENT_ISLE)
02890           // KLUDGE:  putting Draygan to sleep.
02891     ucmachine->call_usecode(0x7e1, this,
02892           Usecode_machine::weapon);
02893           // Get special attacks (poison, etc.)
02894   unsigned char powers = winf ? winf->get_powers() : 0;
02895   if (ainf)
02896     powers |= ainf->get_powers();
02897   if (!explosion && winf && winf->explodes() &&   // Exploding?
02898       rand()%4)     // Let's do it 3/4 the time.
02899     eman->add_effect(new Explosion_effect(get_tile() + 
02900       Tile_coord(0, 0, get_info().get_3d_height()/2), 0, 0,
02901               weapon_shape));
02902   if (!wpoints && !powers)
02903     return false;   // No harm can be done.
02904 
02905   int prob = 55 + 8*bias +
02906     2*Get_effective_prop(attacker, combat, 10) +
02907     Get_effective_prop(attacker, dexterity, 10) -
02908     2*Get_effective_prop(this, combat, 10) -
02909     Get_effective_prop(this, dexterity, 10);
02910   if (get_flag(Obj_flags::protection))// Defender is protected?
02911     prob -= (40 + rand()%20);
02912   if (prob < 4)     // Always give some chance.
02913     prob = 4;
02914   else if (prob > 96)
02915     prob = 96;
02916           // Attacked by Vesculio in SI?
02917   if (GAME_SI && attacker && attacker->npc_num == 294)
02918     {
02919     int pts, sh;    // Do we have Magebane?
02920         if (get_weapon(pts, sh) && sh == 0xe7)
02921       {
02922       prob -= (70 + rand()%20);
02923       eman->remove_text_effect(attacker);
02924       attacker->say(item_names[0x49b]);
02925       }
02926     }
02927   if (instant_death)
02928     prob = 200; // always hits
02929 
02930   if (combat_trace) {
02931     cout << "Hit probability is " << prob << endl;
02932   }
02933   if (rand()%100 > prob)
02934     {     // Missed.
02935           // See if we should drop ammo.
02936     if (winf && ammo_shape && attacker &&
02937         ((winf->is_thrown() && !winf->returns()) ||
02938       winf->get_ammo_consumed() > 0))
02939       {
02940       Tile_coord pos = Map_chunk::find_spot(get_tile(), 3,
02941               ammo_shape, 0, 1);
02942       if (pos.tx == -1)
02943         return false;
02944       Game_object *aobj = gmap->create_ireg_object(
02945                 ammo_shape, 0);
02946       if (attacker->get_flag( Obj_flags::is_temporary))
02947         aobj->set_flag( Obj_flags::is_temporary);
02948       aobj->set_flag(Obj_flags::okay_to_take);
02949       aobj->move(pos);
02950       }
02951     return false;
02952     }
02953           // Compute hit points to lose.
02954   int attacker_str = Get_effective_prop(attacker, strength, 8);
02955   int hp;
02956   wpoints += 2*bias;    // Apply user's preference.
02957   if (wpoints > 0)    // Some ('curse') do no damage.
02958     {
02959     if (wpoints == 127) // Glass sword?
02960       hp = wpoints; // A killer.
02961     else {
02962       hp = 1 + rand()%wpoints;
02963             if (attacker_str > 2)
02964                 hp += 1 + rand()%(attacker_str/3);
02965             if (armor > 0)
02966                 hp -= 1 + rand()%armor;
02967         }
02968     if (hp < 1)
02969       hp = 1;
02970     }
02971   else
02972     hp = 0;
02973   if (powers)     // Special attacks?
02974     {
02975     if ((powers&Weapon_info::poison) && roll_to_win(
02976       Get_effective_prop(attacker, Actor::strength),
02977       Get_effective_prop(this, Actor::dexterity)) &&
02978         !(minf && minf->poison_safe()))
02979       set_flag(Obj_flags::poisoned);
02980     if (powers&Weapon_info::magebane)
02981       {
02982       int mana = properties[static_cast<int>(Actor::mana)];
02983       set_property(static_cast<int>(Actor::mana), 
02984           mana > 1 ? rand()%(mana - 1) : 0);
02985           // Vasculio the Vampire?
02986       if (npc_num == 294 && GAME_SI)
02987         hp *= 3;
02988       }
02989     if ((powers&Weapon_info::curse) && roll_to_win(
02990       Get_effective_prop(attacker, Actor::intelligence),
02991       Get_effective_prop(this, Actor::intelligence)))
02992       set_flag(Obj_flags::cursed);
02993     if ((powers&Weapon_info::sleep) && roll_to_win(
02994       Get_effective_prop(attacker, Actor::intelligence),
02995       Get_effective_prop(this, Actor::intelligence)))
02996       set_flag(Obj_flags::asleep);
02997     if ((powers&Weapon_info::paralyze) && roll_to_win(
02998       Get_effective_prop(attacker, Actor::intelligence),
02999       Get_effective_prop(this, Actor::intelligence)))
03000       set_flag(Obj_flags::paralyzed);
03001     }
03002   int sfx;      // Play 'hit' sfx.
03003   if (winf && (sfx = winf->get_hitsfx()) >= 0 &&
03004           // But only if Ava. involved.
03005       (this == gwin->get_main_actor() || 
03006         attacker == gwin->get_main_actor()))
03007     Audio::get_ptr()->play_sound_effect(sfx);
03008 
03009   int oldhealth = properties[static_cast<int>(health)];
03010   int maxhealth = properties[static_cast<int>(strength)];
03011 
03012   if (instant_death)    //instant death
03013     hp = properties[static_cast<int>(health)] + 
03014         properties[static_cast<int>(strength)] + 1;
03015   int newhp = oldhealth - hp; // Subtract from health.
03016 
03017   bool defeated = reduce_health(hp, attacker);
03018   if (Combat::show_hits)
03019     {
03020     eman->remove_text_effect(this);
03021     char hpmsg[50];
03022     sprintf(hpmsg, "-%d(%d)", hp, newhp);
03023     eman->add_text(hpmsg, this);
03024     }
03025   string name = "<trap>";
03026   if (attacker)
03027     name = attacker->get_name();
03028 
03029   if (combat_trace) {
03030     cout << name << " hits " << get_name()
03031        << " for " << hp << " hit points, leaving "
03032        << properties[static_cast<int>(health)] << " remaining" << endl;
03033   }
03034   if (!defeated && minf && minf->splits() && rand()%2 == 0 && 
03035       properties[static_cast<int>(health)] > 0)
03036     clone();
03037 
03038   return defeated;
03039   }
03040 
03041 /*
03042  *  Being attacked.
03043  *
03044  *  Output: 0 if defeated, else object itself.
03045  */
03046 
03047 Game_object *Actor::attacked
03048   (
03049   Actor *attacker,    // 0 if from a trap.
03050   int weapon_shape,   // Weapon shape, or 0 to use readied.
03051           //   If < 0, it's an explosion.
03052   int ammo_shape      // Also may be 0.
03053   )
03054   {
03055   if (is_dead() ||    // Already dead?
03056           // Or party member of dead Avatar?
03057       (party_id >= 0 && gwin->get_main_actor()->is_dead()))
03058     return 0;
03059   if (attacker)
03060     { 
03061     if (attacker->get_schedule_type() == Schedule::duel)
03062       return this;  // Just play-fighting.
03063     set_oppressor(attacker->get_npc_num());
03064     if (is_combat_protected() && party_id >= 0 &&
03065         rand()%5 == 0)
03066       say(first_need_help, last_need_help);
03067           // Attack back, but not if in party.
03068     if (!target && !is_in_party())
03069       set_target(attacker,
03070           attacker->get_schedule_type() != Schedule::duel);
03071     }
03072           // Watch for Skara Brae ghosts.
03073   if (npc_num > 0 && Game::get_game_type() == BLACK_GATE &&
03074     get_info().has_translucency() && 
03075       party_id < 0) // But don't include Spark!!
03076     return this;
03077   bool defeated = figure_hit_points(attacker, weapon_shape, ammo_shape);
03078   if (attacker && defeated)
03079     { // ++++++++This should be in reduce_health()+++++++
03080           // Experience gained = strength???
03081     int expval = get_property(static_cast<int>(strength)) +
03082         get_property(static_cast<int>(combat))/4 +
03083         get_property(static_cast<int>(dexterity))/4 +
03084         get_property(static_cast<int>(intelligence))/4;
03085     expval /= 2;  // Users complain we're giving too much.
03086           // Attacker gains experience.
03087     attacker->set_property(static_cast<int>(exp),
03088         attacker->get_property(static_cast<int>(exp)) + expval);
03089     return 0;
03090     }
03091   return this;
03092   }
03093 
03094 /*
03095  *  There's probably a smarter way to do this, but this routine checks
03096  *  for the dragon Draco.
03097  */
03098 
03099 static int Is_draco
03100   (
03101   Actor *dragon
03102   )
03103   {
03104   Game_object_vector vec;   // Gets list.
03105             // Should have a special scroll.
03106   int cnt = dragon->get_objects(vec, 797, 241, 4);
03107   return cnt > 0;
03108   }
03109 
03110 /*
03111  *  We're dead.  We're removed from the world, but not deleted.
03112  */
03113 
03114 void Actor::die
03115   (
03116   Actor *attacker
03117   )
03118   {
03119           // Get location.
03120   Tile_coord pos = get_tile();
03121   set_action(0);
03122   delete schedule;
03123   schedule = 0;
03124   gwin->get_tqueue()->remove(this);// Remove from time queue.
03125   Actor::set_flag(Obj_flags::dead);// IMPORTANT:  Set this before moving
03126           //   objs. so Usecode(eventid=6) isn't
03127           //   called.
03128   int shnum = get_shapenum();
03129           // Special case:  Hook, Dracothraxus.
03130   if (((shnum == 0x1fa || (shnum == 0x1f8 && Is_draco(this))) && 
03131       Game::get_game_type() == BLACK_GATE))
03132     {     // Exec. usecode before dying.
03133     ucmachine->call_usecode(shnum, this, 
03134           Usecode_machine::internal_exec);
03135     if (get_cx() == 255)  // Invalid now?
03136       return;
03137     }
03138   properties[static_cast<int>(health)] = -50;
03139   Shape_info& info = get_info();
03140   Monster_info *minfo = info.get_monster_info();
03141   remove_this(1);     // Remove (but don't delete this).
03142   set_invalid();
03143   Dead_body *body;    // See if we need a body.
03144   if (!minfo || !minfo->has_no_body())
03145     {
03146     int frnum;      // Lookup body shape/frame.
03147     if (!Body_lookup::find(get_shapenum(), shnum, frnum))
03148       {
03149       shnum = 400;
03150       frnum = 3;
03151       }
03152           // Reflect if NPC reflected.
03153     frnum |= (get_framenum()&32);
03154     body = new Dead_body(shnum, frnum, 0, 0, 0, 
03155           npc_num > 0 ? npc_num : -1);
03156     if (npc_num > 0)
03157       {
03158       body->set_quality(1); // Flag for dead body of NPC.
03159       gwin->set_body(npc_num, body);
03160       }
03161           // Tmp. monster => tmp. body.
03162     if (get_flag(Obj_flags::is_temporary))
03163       body->set_flag(Obj_flags::is_temporary);
03164           // Okay to take its contents.
03165     body->set_flag_recursively(Obj_flags::okay_to_take);
03166           // Find a spot within 1 tile.
03167     Tile_coord bp = Map_chunk::find_spot(pos, 1, shnum, frnum, 2);
03168     if (bp.tx == -1)
03169       bp = pos; // Default to NPC pos, even if blocked.
03170     body->move(bp);
03171     }
03172   else
03173     body = 0;
03174   Game_object *item;    // Move/remove all the items.
03175   Game_object_vector tooheavy;  // Some shouldn't be moved.
03176   while ((item = objects.get_first()) != 0)
03177     {
03178     remove(item);
03179     item->set_invalid();
03180     if (!item->is_dragable())
03181       {
03182       tooheavy.push_back(item);
03183       continue;
03184       }
03185     if (body)
03186       body->add(item, 1);// Always succeed at adding.
03187     else      // No body?  Drop on ground.
03188       {
03189       item->set_flag_recursively(Obj_flags::okay_to_take);
03190       Tile_coord pos2 = Map_chunk::find_spot(pos, 5,
03191         item->get_shapenum(), item->get_framenum(), 1);
03192       if (pos.tx != -1)
03193         item->move(pos2);
03194       else    // No room anywhere.
03195         tooheavy.push_back(item);
03196       }
03197     }
03198           // Put the heavy ones back.
03199   for (Game_object_vector::const_iterator it = tooheavy.begin(); 
03200             it != tooheavy.end(); ++it)
03201     add(*it, 1);
03202   if (body)
03203     gwin->add_dirty(body);
03204   add_dirty();      // Want to repaint area.
03205   delete_contents();    // remove what's left of inventory
03206           // Is this a bad guy?
03207           // Party defeated an evil monster?
03208   if (attacker && attacker->is_in_party() && !is_in_party() && 
03209       alignment != neutral && alignment != friendly)
03210     Combat_schedule::monster_died();
03211           // Move party member to 'dead' list.
03212   partyman->update_party_status(this);
03213   }
03214 
03215 /*
03216  *  Create another monster of the same type as this, and adjacent.
03217  *
03218  *  Output: ->monster, or 0 if failed.
03219  */
03220 
03221 Monster_actor *Actor::clone
03222   (
03223   )
03224   {
03225   Shape_info& info = get_info();
03226           // Base distance on greater dim.
03227   int xs = info.get_3d_xtiles(), ys = info.get_3d_ytiles();
03228           // Find spot.
03229   Tile_coord pos = Map_chunk::find_spot(get_tile(), 
03230     xs > ys ? xs : ys, get_shapenum(), 0, 1);
03231   if (pos.tx < 0)
03232     return 0;   // Failed.
03233           // Create, temporary & with equip.
03234   Monster_actor *monst = Monster_actor::create(
03235       get_shapenum(), pos, get_schedule_type(),
03236       get_alignment(), true, true);
03237   return monst;
03238   }
03239 
03240 /*
03241  *  Restore HP's on the hour.
03242  */
03243 
03244 void Actor::mend_hourly
03245   (
03246   )
03247   {
03248   if (is_dead())
03249     return;
03250   int maxhp = properties[static_cast<int>(strength)];
03251   int hp = properties[static_cast<int>(health)];
03252   if (maxhp > 0 && hp < maxhp)
03253     {
03254     if (maxhp >= 3)  
03255       hp += 1 + rand()%(maxhp/3);
03256     else
03257       hp += 1;
03258     if (hp > maxhp)
03259       hp = maxhp;
03260     properties[static_cast<int>(health)] = hp;
03261           // ??If asleep & hps now >= 0, should
03262           //   we awaken?
03263     }
03264           // Restore some mana also.
03265   int maxmana = properties[static_cast<int>(magic)];
03266   int curmana = properties[static_cast<int>(mana)];
03267   if (maxmana > 0 && curmana < maxmana)
03268     {
03269     if (maxmana >= 3) 
03270       curmana += 1 + rand()%(maxmana/3);
03271     else
03272       curmana += 1;
03273     properties[static_cast<int>(mana)] = curmana <= maxmana ? curmana 
03274                 : maxmana;
03275     }
03276   }
03277 
03278 /*
03279  *  Restore from body.  It must not be owned by anyone.
03280  *
03281  *  Output: ->actor if successful, else 0.
03282  */
03283 
03284 Actor *Actor::resurrect
03285   (
03286   Dead_body *body     // Must be this actor's body.
03287   )
03288   {
03289   if (body->get_owner() ||  // Must be on ground.
03290       npc_num <= 0 || gwin->get_body(npc_num) != body)
03291     return (0);
03292   gwin->set_body(npc_num, 0); // Clear from gwin's list.
03293   Game_object *item;    // Get back all the items.
03294   while ((item = body->get_objects().get_first()) != 0)
03295     {
03296     body->remove(item);
03297     add(item, 1);   // Always succeed at adding.
03298     }
03299   gwin->add_dirty(body);    // Need to repaint here.
03300   Tile_coord pos = body->get_tile();
03301   body->remove_this();    // Remove and delete body.
03302   move(pos);      // Move back to life.
03303           // Restore health to max.
03304   properties[static_cast<int>(health)] = 
03305           properties[static_cast<int>(strength)];
03306   Actor::clear_flag(Obj_flags::dead);
03307   Actor::clear_flag(Obj_flags::poisoned);
03308   Actor::clear_flag(Obj_flags::paralyzed);
03309   Actor::clear_flag(Obj_flags::asleep);
03310           // Restore to party if possible.
03311   partyman->update_party_status(this);
03312           // Give a reasonable schedule.
03313   set_schedule_type(is_in_party() ? Schedule::follow_avatar
03314           : Schedule::loiter);
03315           // Stand up.
03316   Usecode_script *scr = new Usecode_script(this);
03317   (*scr) << (Ucscript::npc_frame + Actor::sleep_frame)
03318     << (Ucscript::npc_frame + Actor::kneel_frame)
03319     << (Ucscript::npc_frame + Actor::standing);
03320   scr->start(1);
03321   return (this);
03322   }
03323 
03324 /*
03325  *  Handle a time event (for animation).
03326  */
03327 
03328 void Main_actor::handle_event
03329   (
03330   unsigned long curtime,    // Current time of day.
03331   long udata      // Ignored.
03332   )
03333   {
03334   if (action)     // Doing anything?
03335     {     // Do what we should.
03336     int delay = action->handle_event(this);
03337     if (delay)    // Keep going with same action.
03338       gwin->get_tqueue()->add(
03339           curtime + delay, this, udata);
03340     else
03341       {
03342       set_action(0);
03343       if (schedule)
03344         schedule->now_what();
03345       }
03346     }
03347   else if (schedule)
03348     schedule->now_what();
03349   }
03350 
03351 /*
03352  *  Get the party to follow.
03353  */
03354 
03355 void Main_actor::get_followers
03356   (
03357   )
03358   {
03359   int cnt = partyman->get_count();
03360   for (int i = 0; i < cnt; i++)
03361     {
03362     Actor *npc = gwin->get_npc(partyman->get_member(i));
03363     if (!npc || npc->get_flag(Obj_flags::asleep) ||
03364         npc->is_dead())
03365       continue;
03366     int sched = npc->get_schedule_type();
03367           // Skip if in combat or set to 'wait'.
03368     if (sched != Schedule::combat &&
03369         sched != Schedule::wait &&
03370           // Loiter added for SI.
03371         sched != Schedule::loiter)
03372       {
03373       if (sched != Schedule::follow_avatar)
03374         npc->set_schedule_type(
03375             Schedule::follow_avatar);
03376       else
03377         npc->follow(this);
03378       }
03379     }
03380   }
03381 
03382 /*
03383  *  Step onto an adjacent tile.
03384  *
03385  *  Output: 0 if blocked.
03386  */
03387 
03388 int Main_actor::step
03389   (
03390   Tile_coord t,     // Tile to step onto.
03391   int frame     // New frame #.
03392   )
03393   {
03394   rest_time = 0;      // Reset counter.
03395           // Get chunk.
03396   int cx = t.tx/c_tiles_per_chunk, cy = t.ty/c_tiles_per_chunk;
03397           // Get rel. tile coords.
03398   int tx = t.tx%c_tiles_per_chunk, ty = t.ty%c_tiles_per_chunk;
03399   Map_chunk *nlist = gmap->get_chunk(cx, cy);
03400   int old_lift = get_lift();
03401   int water, poison;    // Get tile info.
03402   get_tile_info(this, gwin, nlist, tx, ty, water, poison);
03403   Game_object *block;
03404   if (is_blocked(t) &&
03405      (!(block = Game_object::find_blocking(t)) || block == this ||
03406           // Try to get blocker to move aside.
03407           !block->move_aside(this, get_direction(block)) ||
03408           // (May have swapped places.)
03409     (t != get_tile() &&
03410           // If okay, try one last time.
03411      is_blocked(t))))
03412     {
03413     stop();
03414     return (0);
03415     }
03416   if (poison && t.tz == 0)
03417     Actor::set_flag(static_cast<int>(Obj_flags::poisoned));
03418           // Check for scrolling.
03419   gwin->scroll_if_needed(this, t);
03420   add_dirty();      // Set to update old location.
03421           // Get old chunk, old tile.
03422   Map_chunk *olist = gmap->get_chunk(get_cx(), get_cy());
03423   Tile_coord oldtile = get_tile();
03424           // Move it.
03425   Actor::movef(olist, nlist, tx, ty, frame, t.tz);
03426   add_dirty(1);     // Set to update new.
03427           // In a new chunk?
03428   if (olist != nlist)
03429     Main_actor::switched_chunks(olist, nlist);
03430   int roof_height = nlist->is_roof (tx, ty, t.tz);
03431   gwin->set_ice_dungeon(nlist->is_ice_dungeon(tx, ty));
03432   if (gwin->set_above_main_actor (roof_height))
03433     {
03434     gwin->set_in_dungeon(nlist->has_dungeon()?
03435           nlist->is_dungeon(tx, ty):0);
03436     gwin->set_all_dirty();
03437     }
03438   else if (roof_height < 31 && gwin->set_in_dungeon(nlist->has_dungeon()?
03439           nlist->is_dungeon(tx, ty):0))
03440     gwin->set_all_dirty();
03441           // Near an egg?  (Do this last, since
03442           //   it may teleport.)
03443   nlist->activate_eggs(this, t.tx, t.ty, t.tz,
03444             oldtile.tx, oldtile.ty);
03445   return (1);
03446   }
03447 
03448 /*
03449  *  Setup cache after a change in chunks.
03450  */
03451 
03452 void Main_actor::switched_chunks
03453   (
03454   Map_chunk *olist, // Old chunk, or null.
03455   Map_chunk *nlist  // New chunk.
03456   )
03457   {
03458   int newcx = nlist->get_cx(), newcy = nlist->get_cy();
03459   int xfrom, xto, yfrom, yto; // Get range of chunks.
03460   if (!olist)     // No old?  Use all 9.
03461     {
03462     xfrom = newcx > 0 ? newcx - 1 : newcx;
03463     xto = newcx < c_num_chunks - 1 ? newcx + 1 : newcx;
03464     yfrom = newcy > 0 ? newcy - 1 : newcy;
03465     yto = newcy < c_num_chunks - 1 ? newcy + 1 : newcy;
03466     }
03467   else
03468     {
03469     int oldcx = olist->get_cx(), oldcy = olist->get_cy();
03470     if (newcx == oldcx + 1)
03471       {
03472       xfrom = newcx;
03473       xto = newcx < c_num_chunks - 1 ? newcx + 1 : newcx;
03474       }
03475     else if (newcx == oldcx - 1)
03476       {
03477       xfrom = newcx > 0 ? newcx - 1 : newcx;
03478       xto = newcx;
03479       }
03480     else
03481       {
03482       xfrom = newcx > 0 ? newcx - 1 : newcx;
03483       xto = newcx < c_num_chunks - 1 ? newcx + 1 : newcx;
03484       }
03485     if (newcy == oldcy + 1)
03486       {
03487       yfrom = newcy;
03488       yto = newcy < c_num_chunks - 1 ? newcy + 1 : newcy;
03489       }
03490     else if (newcy == oldcy - 1)
03491       {
03492       yfrom = newcy > 0 ? newcy - 1 : newcy;
03493       yto = newcy;
03494       }
03495     else
03496       {
03497       yfrom = newcy > 0 ? newcy - 1 : newcy;
03498       yto = newcy < c_num_chunks - 1 ? newcy + 1 : newcy;
03499       }
03500     }
03501   for (int y = yfrom; y <= yto; y++)
03502     for (int x = xfrom; x <= xto; x++)
03503       gmap->get_chunk(x, y)->setup_cache();
03504 
03505   // If change in Superchunk number, apply Old Style caching emulation
03506   if (olist) gwin->emulate_cache(olist->get_cx(), olist->get_cy(), newcx, newcy);
03507   else gwin->emulate_cache(-1, -1, newcx, newcy);
03508   }
03509 
03510 /*
03511  *  Move (teleport) to a new spot.
03512  */
03513 
03514 void Main_actor::move
03515   (
03516   int newtx, 
03517   int newty, 
03518   int newlift
03519   )
03520   {
03521           // Store old chunk list.
03522   Map_chunk *olist = gmap->get_chunk_safely(
03523             get_cx(), get_cy());
03524           // Move it.
03525   Actor::move(newtx, newty, newlift);
03526   Map_chunk *nlist = gmap->get_chunk(get_cx(), get_cy());
03527   if (nlist != olist)
03528     Main_actor::switched_chunks(olist, nlist);
03529   int tx = get_tx(), ty = get_ty();
03530   gwin->set_ice_dungeon(nlist->is_ice_dungeon(tx, ty));
03531   if (gwin->set_above_main_actor(nlist->is_roof(tx, ty, newlift)))
03532     gwin->set_in_dungeon(nlist->has_dungeon()?
03533     nlist->is_dungeon(tx, ty):0);
03534   }
03535 
03536 /*
03537  *  We're dead.
03538  */
03539 
03540 void Main_actor::die
03541   (
03542   Actor * /* attacker */
03543   )
03544   {
03545   if (gwin->in_combat())
03546     gwin->toggle_combat();  // Hope this is safe....
03547   Actor::set_flag(Obj_flags::dead);
03548   gumpman->close_all_gumps(); // Obviously.
03549           // Special function for dying:
03550   if (Game::get_game_type() == BLACK_GATE)
03551     ucmachine->call_usecode(0x60e, this, Usecode_machine::weapon);
03552 
03553     else
03554       ucmachine->call_usecode(0x400, this,
03555           Usecode_machine::died);
03556   }
03557 
03558 /*
03559  *  Set the shapenum based on skin color, sex, naked flag and petra and polymorph flag
03560  */
03561 void Actor::set_actor_shape()
03562 {
03563   if (get_npc_num() != 0 || get_flag (Obj_flags::polymorph))
03564     return;
03565 
03566   int sn;
03567 
03568   ShapeFile the_file = SF_SHAPES_VGA;
03569   int female = get_type_flag(tf_sex)?1:0;
03570 
03571   if (Game::get_game_type() == SERPENT_ISLE||sman->can_use_multiracial())
03572   {
03573     if (Game::get_game_type() == BLACK_GATE) the_file = SF_BG_SISHAPES_VGA;
03574 
03575     if (get_skin_color() == 0) // WH
03576     {
03577       sn = 1028+female+6*get_siflag(naked);
03578     }
03579     else if (get_skin_color() == 1) // BN
03580     {
03581       sn = 1026+female+6*get_siflag(naked);
03582     }
03583     else if (get_skin_color() == 2) // BK
03584     {
03585       sn = 1024+female+6*get_siflag(naked);
03586     }
03587     else if (Game::get_game_type() == SERPENT_ISLE)
03588     {
03589       sn = female?658:747;
03590     }
03591     else
03592     {
03593       the_file = SF_SHAPES_VGA;
03594       sn = female?989:721;
03595     }
03596   }
03597   else if (female)
03598     sn = 989;
03599   else
03600     sn = 721;
03601 #ifdef DEBUG
03602   cerr << "Setting Shape to " << sn << endl;
03603 #endif
03604   set_shape (sn, get_framenum());
03605   set_file(the_file);
03606 }
03607 
03608 // Sets the polymorph to shape
03609 void Actor::set_polymorph (int shape)
03610 {
03611   // Polymorph is only SI
03612   if (Game::get_game_type() != SERPENT_ISLE) return;
03613 
03614 #ifdef DEBUG
03615   cerr << "Setting polymorph for " << get_npc_num() << endl;
03616   cerr << "Shape " << shape << endl;
03617   cerr << "Save shape " << shape_save << endl;
03618 #endif
03619   
03620   // Want to set to Avatar
03621   if (shape == 721 || shape == 989)
03622   {
03623     Actor *avatar = gwin->get_main_actor();
03624     if (!avatar) return;
03625 
03626     if (avatar->get_skin_color() == 0) // WH
03627       shape = 1028+avatar->get_type_flag(tf_sex)+6*avatar->get_siflag(naked);
03628     else if (avatar->get_skin_color() == 1) // BN
03629       shape = 1026+avatar->get_type_flag(tf_sex)+6*avatar->get_siflag(naked);
03630     else // BK
03631       shape = 1024+avatar->get_type_flag(tf_sex)+6*avatar->get_siflag(naked);
03632   }
03633 
03634   if (shape == shape_save)
03635   {
03636     set_shape (shape_save);
03637     shape_save = -1;
03638     clear_flag(Obj_flags::polymorph);
03639     return;
03640   }
03641   if (shape_save == -1) shape_save = get_shapenum();
03642 //  set_shape (shape, get_framenum());
03643           // ++++Taking a guess for SI amulet:
03644   set_shape (shape, get_dir_framenum(Actor::standing));
03645   set_flag (Obj_flags::polymorph);
03646 }
03647 
03648 // Sets polymorph shape to defaults based on flags and npc num
03649 void Actor::set_polymorph_default()
03650 {
03651   // Polymorph is only SI
03652   if (Game::get_game_type() != SERPENT_ISLE || 
03653           !get_flag(Obj_flags::polymorph)
03654         || (get_npc_num() != 0 && get_npc_num() != 28))
03655     return;
03656 
03657   set_actor_shape();
03658 
03659   shape_save = get_shapenum();
03660 
03661   if (get_npc_num() == 28)    // Handle Petra First
03662     set_polymorph (721);
03663   else if (get_flag(Obj_flags::petra))  // Avatar as petra
03664     set_polymorph (658);
03665   else  // Snake
03666     set_polymorph (530);
03667 }
03668 
03669 /*
03670  *  Get 'real' shape for Usecode.
03671  */
03672 
03673 int Actor::get_shape_real
03674   (
03675   )
03676   {
03677 #if 0 /* Messes up start of BG after earthquake if you use an SI shape */
03678   if (Game::get_game_type() == BLACK_GATE)
03679     return get_shapenum();
03680 #endif
03681   if (npc_num != 0)   // Not the avatar?
03682     return shape_save!=-1?shape_save:get_shapenum();
03683           // Taking guess (6/18/01):
03684   if (get_type_flag(Actor::tf_sex))
03685     return 989;
03686   else
03687     return 721;
03688   }
03689 
03690 /*
03691  *  Create NPC.
03692  */
03693 
03694 Npc_actor::Npc_actor
03695   (
03696   const std::string &nm,      // Name.  A copy is made.
03697   int shapenum, 
03698   int num, 
03699   int uc
03700   ) : Actor(nm, shapenum, num, uc), nearby(false),
03701     num_schedules(0),
03702     schedules(0)
03703   {
03704   }
03705 
03706 /*
03707  *  Kill an actor.
03708  */
03709 
03710 Npc_actor::~Npc_actor
03711   (
03712   )
03713   {
03714   delete [] schedules;
03715   }
03716 
03717 /*
03718  *  Set schedule list.
03719  */
03720 
03721 void Npc_actor::set_schedules
03722   (
03723   Schedule_change *sc_list, 
03724   int cnt
03725   )
03726   {
03727   delete [] schedules;
03728   schedules = sc_list;
03729   num_schedules = cnt;
03730   }
03731 
03732 /*
03733  *  Set schedule type.
03734  */
03735 
03736 void Npc_actor::set_schedule_time_type (int time, int type)
03737 {
03738   Tile_coord tile;
03739   int i;
03740 
03741   for (i = 0; i < num_schedules; i++) if (schedules[i].get_time() == time) break;
03742   
03743   if (i == num_schedules) // Didn't find it
03744   {
03745     Schedule_change *scheds = new Schedule_change[num_schedules+1];
03746 
03747     for (i = 0; i < num_schedules; i++)
03748     {
03749       tile = schedules[i].get_pos();
03750       scheds[i].set(tile.tx, tile.ty, schedules[i].get_type(), schedules[i].get_time());
03751     }
03752 
03753     scheds[num_schedules].set(0, 0, static_cast<unsigned char>(type),
03754       static_cast<unsigned char>(time));
03755     set_schedules(scheds, num_schedules+1);
03756   }
03757   else  // Did find it
03758   {
03759     tile = schedules[i].get_pos();
03760     schedules[i].set(tile.tx, tile.ty, static_cast<unsigned char>(type),
03761       static_cast<unsigned char>(time));
03762   }
03763 }
03764 
03765 /*
03766  *  Set schedule location.
03767  */
03768 
03769 void Npc_actor::set_schedule_time_location (int time, int x, int y)
03770 {
03771   int i;
03772 
03773   for (i = 0; i < num_schedules; i++) if (schedules[i].get_time() == time) break;
03774   
03775   if (i == num_schedules) // Didn't find it
03776   {
03777     Tile_coord tile;
03778     Schedule_change *scheds = new Schedule_change[num_schedules+1];
03779 
03780     for (i = 0; i < num_schedules; i++)
03781     {
03782       tile = schedules[i].get_pos();
03783       scheds[i].set(tile.tx, tile.ty, schedules[i].get_type(), schedules[i].get_time());
03784     }
03785 
03786     scheds[num_schedules].set(x, y, 0, static_cast<unsigned char>(time));
03787     set_schedules(scheds, num_schedules+1);
03788   }
03789   else  // Did find it
03790   {
03791     schedules[i].set(x, y, schedules[i].get_type(), static_cast<unsigned char>(time));
03792   }
03793 }
03794 
03795 /*
03796  *  Remove schedule
03797  */
03798 
03799 void Npc_actor::remove_schedule (int time)
03800 {
03801   int i;
03802 
03803   for (i = 0; i < num_schedules; i++) 
03804     if (schedules[i].get_time() == time) break;
03805   if (i != num_schedules) // Found it
03806   {
03807     int todel = i;
03808     Tile_coord tile;
03809     Schedule_change *scheds = new Schedule_change[num_schedules-1];
03810 
03811     for (i = 0; i < todel; i++)
03812     {
03813       tile = schedules[i].get_pos();
03814       scheds[i].set(tile.tx, tile.ty, 
03815           schedules[i].get_type(), schedules[i].get_time());
03816     }
03817 
03818     for (; i < num_schedules - 1; i++)
03819     {
03820       tile = schedules[i+1].get_pos();
03821       scheds[i].set(tile.tx, tile.ty, 
03822           schedules[i+1].get_type(), 
03823           schedules[i+1].get_time());
03824     }
03825 
03826     set_schedules(scheds, num_schedules-1);
03827   }
03828 }
03829 
03830 /*
03831  *  Set schedule list.
03832  */
03833 
03834 void Npc_actor::get_schedules
03835   (
03836   Schedule_change *&sc_list, 
03837   int &cnt
03838   )
03839   {
03840   sc_list = schedules;
03841   cnt = num_schedules;
03842   }
03843 /*
03844  *  Move and change frame.
03845  */
03846 
03847 void Npc_actor::movef
03848   (
03849   Map_chunk *old_chunk,
03850   Map_chunk *new_chunk, 
03851   int new_sx, int new_sy,
03852   int new_frame, 
03853   int new_lift
03854   )
03855   {
03856   Actor::movef(old_chunk, new_chunk,
03857         new_sx, new_sy, new_frame, new_lift);
03858   if (old_chunk != new_chunk) // In new chunk?
03859     Npc_actor::switched_chunks(old_chunk, new_chunk);
03860   }
03861 
03862 /*
03863  *  Find day's schedule for a given time-of-day.
03864  *
03865  *  Output: index of schedule change.
03866  *    -1 if not found, or if a party member.
03867  */
03868 
03869 int Npc_actor::find_schedule_change
03870   (
03871   int hour3     // 0=midnight, 1=3am, etc.
03872   )
03873   {
03874   if (party_id >= 0 || is_dead())
03875     return (-1);    // Fail if a party member or dead.
03876   for (int i = 0; i < num_schedules; i++)
03877     if (schedules[i].get_time() == hour3)
03878       return i;
03879   return -1;
03880   }
03881 
03882 /*
03883  *  Update schedule at a 3-hour time change.
03884  */
03885 
03886 void Npc_actor::update_schedule
03887   (
03888   int hour3,      // 0=midnight, 1=3am, etc.
03889   int backwards,      // Extra periods to look backwards.
03890   int delay     // Delay in msecs, or -1 for random.
03891   )
03892   {
03893   int i = find_schedule_change(hour3);
03894   if (i < 0)
03895     {     // Not found?  Look at prev.?
03896           // Always if noon of first day.
03897     long hour = gclock->get_total_hours();
03898     if (hour == 12 && !backwards)
03899       backwards++;
03900     while (backwards-- && i < 0)
03901       i = find_schedule_change((--hour3 + 8)%8);
03902     if (i < 0)
03903       return;
03904     // This is bad, not always true
03905     // location might be different
03906     //if (schedule_type == schedules[i].get_type())
03907     //  return;   // Already in it.
03908     }
03909   set_schedule_and_loc (schedules[i].get_type(), schedules[i].get_pos(),
03910                 delay);
03911   }
03912 
03913 /*
03914  *  Render.
03915  */
03916 
03917 void Npc_actor::paint
03918   (
03919   )
03920   {
03921   Actor::paint();     // Draw on screen.
03922   if (dormant && schedule &&  // Resume schedule.
03923           // FOR NOW:  Not when in formation.
03924       (party_id < 0 || !gwin->walk_in_formation || 
03925         schedule_type != Schedule::follow_avatar))
03926     {
03927     dormant = false;  // But clear out old entries first.??
03928     gwin->get_tqueue()->remove(this);
03929           // Force schedule->now_what() in .5secs
03930           // DO NOT call now_what here!!!
03931     uint32 curtime = Game::get_ticks();
03932     gwin->get_tqueue()->add(curtime + 500, this, reinterpret_cast<long>(gwin));
03933     }
03934   if (!nearby)      // Make sure we're in 'nearby' list.
03935     gwin->add_nearby_npc(this);
03936   }
03937 
03938 /*
03939  *  Run usecode when double-clicked.
03940  */
03941 void Npc_actor::activate
03942   (
03943   int event
03944   )
03945   {
03946   if (is_dead())
03947     return;
03948           // Converse, etc.
03949   Actor::activate(event);
03950 #if 0 /* +++Sometimes causes former companions to wander off. */
03951   //++++++ This might no longer be needed.  Need to test.++++++ (jsf)
03952           // Want to get BG actors from start
03953           //   to their regular schedules:
03954   int i;        // Past 6:00pm first day?
03955   if (gclock->get_total_hours() >= 18 || 
03956       Game::get_game_type() == SERPENT_ISLE ||
03957           // Or no schedule change.
03958       (i = find_schedule_change(gclock->get_hour()/3)) < 0 ||
03959           // Or schedule is already correct?
03960       schedules[i].get_type() == schedule_type)
03961     return;
03962   cout << "Setting '" << get_name() << "' to 1st schedule" << endl;
03963           // Maybe a delay here?  Okay for now.
03964   update_schedule(gclock->get_hour()/3);
03965 #endif
03966   }
03967 
03968 /*
03969  *  Handle a time event (for animation).
03970  */
03971 
03972 void Npc_actor::handle_event
03973   (
03974   unsigned long curtime,    // Current time of day.
03975   long udata      // Ignored.
03976   )
03977   {
03978   if (!action)      // Not doing anything?
03979     {
03980     if (schedule)
03981       schedule->now_what();
03982     else
03983       dormant = true;
03984     }
03985   else
03986     {     // Do what we should.
03987     int delay = party_id < 0 ? gwin->is_time_stopped() : 0;
03988     if (delay <= 0)   // Time not stopped?
03989       delay = action->handle_event(this);
03990     if (delay)    // Keep going with same action.
03991       gwin->get_tqueue()->add(
03992           curtime + delay, this, udata);
03993     else
03994       {
03995       set_action(0);
03996       if (schedule)
03997         if (dormant)
03998           schedule->im_dormant();
03999         else
04000           schedule->now_what();
04001       }
04002     }
04003   }
04004 
04005 /*
04006  *  Step onto an adjacent tile.
04007  *
04008  *  Output: 0 if blocked (or paralyzed).
04009  *    Dormant is set if off screen.
04010  */
04011 
04012 int Npc_actor::step
04013   (
04014   Tile_coord t,     // Tile to step onto.
04015   int frame     // New frame #.
04016   )
04017   {
04018   if (get_flag(Obj_flags::paralyzed))
04019     return 0;
04020           // Store old chunk.
04021   Tile_coord oldtile = get_tile();
04022   int old_cx = get_cx(), old_cy = get_cy();
04023           // Get chunk.
04024   int cx = t.tx/c_tiles_per_chunk, cy = t.ty/c_tiles_per_chunk;
04025           // Get rel. tile coords.
04026   int tx = t.tx%c_tiles_per_chunk, ty = t.ty%c_tiles_per_chunk;
04027           // Get ->new chunk.
04028   Map_chunk *nlist = gmap->get_chunk_safely(cx, cy);
04029   if (!nlist)     // Shouldn't happen!
04030     {
04031     stop();
04032     return (0);
04033     }
04034   int water, poison;    // Get tile info.
04035   get_tile_info(this, gwin, nlist, tx, ty, water, poison);
04036   if (is_blocked(t))
04037     {
04038     if (schedule)   // Tell scheduler.
04039       schedule->set_blocked(t);
04040     stop();
04041           // Offscreen, but not in party?
04042     if (!gwin->add_dirty(this) && party_id < 0 &&
04043           // And > a screenful away?
04044         distance(gwin->get_camera_actor()) > 1 + 320/c_tilesize)
04045       dormant = true; // Go dormant.
04046     return (0);   // Done.
04047     }
04048   if (poison && t.tz == 0)
04049     Actor::set_flag(static_cast<int>(Obj_flags::poisoned));
04050           // Check for scrolling.
04051   gwin->scroll_if_needed(this, t);
04052   add_dirty();      // Set to repaint old area.
04053           // Get old chunk.
04054   Map_chunk *olist = gmap->get_chunk(old_cx, old_cy);
04055           // Move it.
04056   movef(olist, nlist, tx, ty, frame, t.tz);
04057 
04058           // Near an egg?  (Do this last, since
04059           //   it may teleport.)
04060   nlist->activate_eggs(this, t.tx, t.ty, t.tz, oldtile.tx, oldtile.ty);
04061 
04062           // Offscreen, but not in party?
04063   if (!add_dirty(1) && party_id < 0 &&
04064           // And > a screenful away?
04065       distance(gwin->get_camera_actor()) > 1 + 320/c_tilesize &&
04066       //++++++++Try getting rid of the 'talk' line:
04067       get_schedule_type() != Schedule::talk &&
04068       get_schedule_type() != Schedule::street_maintenance)
04069     {     // No longer on screen.
04070     stop();
04071     dormant = true;
04072     return (0);
04073     }
04074   return (1);     // Add back to queue for next time.
04075   }
04076 
04077 /*
04078  *  Remove an object from its container, or from the world.
04079  *  The object is deleted.
04080  */
04081 
04082 void Npc_actor::remove_this
04083   (
04084   int nodel     // 1 to not delete.
04085   )
04086   {
04087   set_action(0);
04088 //  delete schedule;  // Problems in SI monster creation.
04089 //  schedule = 0;
04090 // Messes up resurrection num_schedules = 0;
04091   gwin->get_tqueue()->remove(this);// Remove from time queue.
04092   gwin->remove_nearby_npc(this);  // Remove from nearby list.
04093           // Store old chunk list.
04094   Map_chunk *olist = gmap->get_chunk_safely(get_cx(), get_cy());
04095   Actor::remove_this(1);  // Remove, but don't ever delete an NPC
04096   Npc_actor::switched_chunks(olist, 0);
04097   cx = cy = 0xff;     // Set to invalid chunk coords.
04098   if (!nodel && npc_num > 0)  // Really going?
04099     unused = true;    // Mark unused if a numbered NPC.
04100   }
04101 
04102 /*
04103  *  Update chunks' npc lists after this has moved.
04104  */
04105 
04106 void Npc_actor::switched_chunks
04107   (
04108   Map_chunk *olist, // Old chunk, or null.
04109   Map_chunk *nlist  // New chunk, or null.
04110   )
04111   {
04112   //++++++++++No longer needed.  Maybe it should go away.
04113   }
04114 
04115 /*
04116  *  Move (teleport) to a new spot.
04117  */
04118 
04119 void Npc_actor::move
04120   (
04121   int newtx, 
04122   int newty, 
04123   int newlift
04124   )
04125   {
04126           // Store old chunk list.
04127   Map_chunk *olist = gmap->get_chunk_safely(get_cx(), get_cy());
04128           // Move it.
04129   Actor::move(newtx, newty, newlift);
04130   Map_chunk *nlist = gmap->get_chunk_safely(get_cx(), get_cy());
04131   if (nlist != olist)
04132     {
04133     Npc_actor::switched_chunks(olist, nlist);
04134     if (!olist)   // Moving back into world?
04135       dormant = true; // Cause activation if painted.
04136     }
04137   }
04138 
04139 /*
04140  *  Delete.
04141  */
04142 
04143 Dead_body::~Dead_body
04144   (
04145   )
04146   {
04147   }
04148 
04149 /*
04150  *  Get # of NPC a body came from (or -1 if not known).
04151  */
04152 
04153 int Dead_body::get_live_npc_num
04154   (
04155   )
04156   {
04157   return npc_num;
04158   }
04159 
04160 /*
04161  *  Create a sequence of frames.
04162  */
04163 
04164 Frames_sequence::Frames_sequence
04165   (
04166   int cnt,      // # of frames.
04167   unsigned char *f    // List of frames.
04168   ) : num_frames(cnt)
04169   {
04170   frames = new unsigned char[cnt];
04171   memcpy(frames, f, cnt);   // Copy in the list.
04172   }
04173 
04174 

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