schedule.cc

Go to the documentation of this file.
00001 /*
00002  *  Schedule.cc - Schedules for characters.
00003  *
00004  *  Copyright (C) 2000-2001  The Exult Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #  include <config.h>
00023 #endif
00024 
00025 #include "SDL_timer.h"
00026 #include "schedule.h"
00027 #include "actors.h"
00028 #include "Zombie.h"
00029 #include "gamewin.h"
00030 #include "gameclk.h"
00031 #include "gamemap.h"
00032 #include "actions.h"
00033 #include "dir.h"
00034 #include "items.h"
00035 #include "game.h"
00036 #include "paths.h"
00037 #include "ucmachine.h"
00038 #include "ucsched.h"
00039 #include "ucscriptop.h"
00040 #include "monstinf.h"
00041 
00042 #ifndef UNDER_CE
00043 using std::cout;
00044 using std::endl;
00045 using std::rand;
00046 #endif
00047 
00048 /*
00049  *  Create. 
00050  */
00051 Schedule::Schedule
00052   (
00053   Actor *n
00054   ) : npc(n), blocked(-1, -1, -1), street_maintenance_failures(0),
00055       street_maintenance_time(0)
00056   {
00057   prev_type = npc ? npc->get_schedule_type() : -1;
00058   }
00059 
00060 
00061 /*
00062  *  Set up an action to get an actor to a location (via pathfinding), and
00063  *  then execute another action when he gets there.
00064  */
00065 
00066 void Schedule::set_action_sequence
00067   (
00068   Actor *actor,     // Whom to activate.
00069   Tile_coord dest,    // Where to walk to.
00070   Actor_action *when_there, // What to do when he gets there.
00071   bool from_off_screen,   // Have actor walk from off-screen.
00072   int delay     // Msecs. to delay.
00073   )
00074   {
00075   actor->set_action(Actor_action::create_action_sequence(
00076       actor, dest, when_there, from_off_screen));
00077   actor->start(250, delay); // Get into time queue.
00078   }
00079 
00080 /*
00081  *  Look for lamps to light/unlight and shutters to open/close.
00082  *
00083  *  Output: 1 if successful, which means npc's schedule has changed!
00084  */
00085 
00086 int Schedule::try_street_maintenance
00087   (
00088   )
00089   {
00090           // What to look for:
00091   static int night[] = {322, 372, 889};
00092   static int sinight[] = {290, 291, 889};
00093   static int day[] = {290, 291, 526};
00094 
00095   long curtime = Game::get_ticks();
00096   if (curtime < street_maintenance_time)
00097     return 0;   // Not time yet.
00098   if (npc->Actor::get_npc_num() <= 0 ||
00099       npc == gwin->get_camera_actor())
00100     return 0;   // Only want normal NPC's.
00101           // At least 30secs. before next one.
00102   street_maintenance_time = curtime + 30000 + 
00103         street_maintenance_failures*5000;
00104   int *shapes;
00105   int hour = gclock->get_hour();
00106   bool bg = (Game::get_game_type() == BLACK_GATE);
00107   if (hour >= 9 && hour < 18)
00108     shapes = &day[0];
00109   else if (hour >= 18 || hour < 6)
00110     shapes = bg ? &night[0] : &sinight[0];
00111   else
00112     return 0;   // Dusk or dawn.
00113   Tile_coord npcpos = npc->get_tile();
00114           // Look at screen + 1/2.
00115   Rectangle winrect = gwin->get_win_tile_rect();
00116   winrect.enlarge(winrect.w/4);
00117   if (!winrect.has_point(npcpos.tx, npcpos.ty))
00118     return 0;
00119           // Get to within 1 tile.
00120   Actor_pathfinder_client cost(npc, 1);
00121   Game_object *found = 0;   // Find one we can get to.
00122   Actor_action *pact;   // Gets ->action to walk there.
00123   for (int i = 0; !found && i < sizeof(night)/sizeof(night[0]); i++)
00124     {
00125     Game_object_vector objs;// Find nearby.
00126     int cnt = npc->find_nearby(objs, shapes[i], 20, 0);
00127     int j;
00128     for (j = 0; j < cnt; j++)
00129       {
00130       Game_object *obj = objs[j];
00131       int shnum = obj->get_shapenum();
00132       if (!bg &&  // Serpent isle?  Shutters?
00133           (shnum == 290 || shnum == 291))
00134           // Want closed during day.
00135         if ((shapes == day) !=
00136           (obj->get_framenum() <= 3))
00137           continue;
00138       if ((pact = Path_walking_actor_action::create_path(
00139           npcpos, obj->get_tile(), cost)) != 0)
00140         {
00141         found = obj;
00142         break;
00143         }
00144       street_maintenance_failures++;
00145       }
00146     }
00147   if (!found)
00148     return 0;   // Failed.
00149           // Set actor to walk there.
00150   npc->set_schedule_type(Schedule::street_maintenance,
00151       new Street_maintenance_schedule(npc, pact, found));
00152   street_maintenance_failures = 0;// We did it, so clear failures.
00153   return 1;
00154   }
00155 
00156 /*
00157  *  Get actual schedule (for Usecode intrinsic).
00158  */
00159 
00160 int Schedule::get_actual_type
00161   (
00162   Actor *npc
00163   )
00164   {
00165   return npc->get_schedule_type();
00166   }
00167 
00168 /*
00169  *  Create schedule to turn lamps on/off, open/close shutters.
00170  */
00171 
00172 Street_maintenance_schedule::Street_maintenance_schedule
00173   (
00174   Actor *n, 
00175   Actor_action *p, 
00176   Game_object *o
00177   ) : Schedule(n), obj(o), shapenum(o->get_shapenum()),
00178         framenum(o->get_framenum()), paction(p)
00179   {
00180   }
00181 
00182 /*
00183  *  Street-maintenance schedule.
00184  */
00185 
00186 void Street_maintenance_schedule::now_what
00187   (
00188   )
00189   {
00190   if (paction)      // First time?
00191     {     // Set to follow given path.
00192     cout << npc->get_name() << 
00193       " walking for street maintenance" << endl;
00194     npc->set_action(paction);
00195     npc->start(250);
00196     paction = 0;
00197     return;
00198     }
00199   if (npc->distance(obj) == 1 &&  // We're there.
00200       obj->get_shapenum() == shapenum && obj->get_framenum() == framenum)
00201     {
00202     cout << npc->get_name() << 
00203       " about to perform street maintenance" << endl;
00204     int dir = npc->get_direction(obj);
00205     signed char frames[2];
00206     frames[0] = npc->get_dir_framenum(dir, Actor::standing);
00207     frames[1] = npc->get_dir_framenum(dir, 3);
00208     signed char standframe = frames[0];
00209     npc->set_action(new Sequence_actor_action(
00210       new Frames_actor_action(frames, sizeof(frames)),
00211       new Activate_actor_action(obj),
00212       new Frames_actor_action(&standframe, 1)));
00213     npc->start(250);
00214     switch (shapenum)
00215       {
00216     case 322:   // Closing shutters.
00217     case 372:
00218       npc->say(first_close_shutters, last_close_shutters);
00219       break;
00220     case 290:   // Open shutters (or both for SI).
00221     case 291:
00222       if (Game::get_game_type() == BLACK_GATE)
00223         npc->say(first_open_shutters, 
00224               last_open_shutters);
00225       else    // SI.
00226         if (framenum <= 3)
00227           npc->say(first_open_shutters, 
00228               last_open_shutters);
00229         else
00230           npc->say(first_close_shutters, 
00231               last_close_shutters);
00232       break;
00233     case 889:   // Turn on lamp.
00234       npc->say(first_lamp_on, last_lamp_on);
00235       break;
00236     case 526:   // Turn off lamp.
00237       npc->say(lamp_off, lamp_off);
00238       break;
00239       }
00240     shapenum = 0;   // Don't want to repeat.
00241     return;
00242     }
00243   cout << npc->get_name() << 
00244       " done with street maintenance" << endl;
00245         // Set back to old schedule.
00246   int period = gclock->get_hour()/3;
00247   npc->update_schedule(period, 7, 0);
00248   }
00249 
00250 /*
00251  *  Get actual schedule (for Usecode intrinsic).
00252  */
00253 
00254 int Street_maintenance_schedule::get_actual_type
00255   (
00256   Actor * /* npc */
00257   )
00258   {
00259   return prev_type;
00260   }
00261 
00262 /*
00263  *  What do we do now?
00264  */
00265 
00266 void Follow_avatar_schedule::now_what
00267   (
00268   )
00269   {
00270   bool is_blocked = blocked.tx != -1;
00271   blocked = Tile_coord(-1, -1, -1);
00272   if (npc->get_flag(Obj_flags::asleep) || npc->is_dead() ||
00273       npc->get_flag(Obj_flags::paralyzed) ||
00274       gwin->main_actor_dont_move()) // Under Usecode control.
00275     return;     // Disabled.
00276   Actor *av = gwin->get_main_actor();
00277   Tile_coord leaderpos = av->get_tile();
00278   Tile_coord pos = npc->get_tile();
00279   int dist2lead = leaderpos.distance(pos);
00280   if (!av->is_moving() &&   // Avatar stopped.
00281       dist2lead <= 6)   // And we're already close enough.
00282     return;
00283   uint32 curtime = SDL_GetTicks();// Want the REAL time here.
00284   if (!is_blocked)    // Not blocked?
00285     {
00286     npc->follow(av);  // Then continue following.
00287     return;
00288     }
00289   if (curtime < next_path_time) // Failed pathfinding recently?
00290     {     // Wait a bit.
00291     npc->start(gwin->get_std_delay(), next_path_time - curtime);
00292     return;
00293     }
00294           // Find a free spot within 3 tiles.
00295   Map_chunk::Find_spot_where where = Map_chunk::anywhere;
00296           // And try to be inside/outside.
00297   where = gwin->is_main_actor_inside() ?
00298           Map_chunk::inside : Map_chunk::outside;
00299   Tile_coord goal = Map_chunk::find_spot(leaderpos, 3, npc, 0, where);
00300   if (goal.tx == -1)    // No free spot?  Give up.
00301     {
00302     cout << npc->get_name() << " can't find free spot" << endl;
00303     next_path_time = SDL_GetTicks() + 1000;
00304     return;
00305     }
00306           // Get his speed.
00307   int speed = av->get_frame_time();
00308   if (!speed)     // Avatar stopped?
00309     speed = gwin->get_std_delay();
00310   if (pos.distance(goal) <= 3)
00311     return;     // Already close enough!
00312 #if 0
00313 #ifdef DEBUG
00314   cout << npc->get_name() << " at distance " << dist2lead 
00315         << " trying to catch up." << endl;
00316 #endif
00317 #endif
00318           // Succeed if within 3 tiles of goal.
00319   if (npc->walk_path_to_tile(goal, speed - speed/4, 0, 3, 1))
00320     return;     // Success.
00321   cout << "... but failed to find path." << endl;
00322           // On screen (roughly)?
00323   int ok;
00324           // Get window rect. in tiles.
00325   Rectangle wrect = gwin->get_win_tile_rect();
00326   if (wrect.has_point(pos.tx - pos.tz/2, pos.ty - pos.tz/2))
00327           // Try walking off-screen.
00328     ok = npc->walk_path_to_tile(Tile_coord(-1, -1, -1),
00329               speed - speed/4, 0);
00330   else        // Off screen already?
00331     ok = npc->approach_another(av);
00332   if (!ok)      // Failed? Don't try again for a bit.
00333     next_path_time = SDL_GetTicks() + 1000;
00334   }
00335 
00336 /*
00337  *  Waiting...
00338  */
00339 
00340 void Wait_schedule::now_what
00341   (
00342   )
00343   {
00344   }
00345 
00346 /*
00347  *  Create a horizontal pace schedule.
00348  */
00349 
00350 Pace_schedule *Pace_schedule::create_horiz
00351   (
00352   Actor *n
00353   )
00354   {
00355   Tile_coord t = n->get_tile(); // Get his position.
00356   return (new Pace_schedule(n, Tile_coord(t.tx - 4, t.ty, t.tz),
00357           Tile_coord(t.tx + 4, t.ty, t.tz)));
00358   }
00359 
00360 /*
00361  *  Create a vertical pace schedule.
00362  */
00363 
00364 Pace_schedule *Pace_schedule::create_vert
00365   (
00366   Actor *n
00367   )
00368   {
00369   Tile_coord t = n->get_tile(); // Get his position.
00370   return (new Pace_schedule(n, Tile_coord(t.tx, t.ty - 4, t.tz),
00371           Tile_coord(t.tx, t.ty + 4, t.tz)));
00372   }
00373 
00374 /*
00375  *  Schedule change for pacing:
00376  */
00377 
00378 void Pace_schedule::now_what
00379   (
00380   )
00381   {
00382   if (rand() % 6 == 0)    // Check for lamps, etc.
00383     if (try_street_maintenance())
00384       return;   // We no longer exist.
00385   which = !which;     // Flip direction.
00386   int delay = 750;    // Delay .75 secs.
00387   if (blocked.tx != -1)   // Blocked?
00388     {
00389     Game_object *obj = Game_object::find_blocking(blocked);
00390     blocked.tx = -1;
00391     Monster_info *minfo = npc->get_info().get_monster_info();
00392     if (obj && obj->as_actor() != 0 &&
00393         (!minfo || !minfo->cant_yell()))
00394       {
00395       npc->say(first_move_aside, last_move_aside);
00396       delay = 1200; // Wait longer.
00397       }
00398     }
00399           // Wait 1 sec. before moving.
00400   npc->walk_to_tile(which ? p1 : p0, gwin->get_std_delay(), delay);
00401   }
00402 
00403 /*
00404  *  Eat at inn.
00405  */
00406 
00407 void Eat_at_inn_schedule::now_what
00408   (
00409   )
00410   {
00411   int frnum = npc->get_framenum();
00412   if ((frnum&0xf) != Actor::sit_frame)
00413     {     // First have to sit down.
00414     if (!Sit_schedule::set_action(npc))
00415       {   // Try again in a while.
00416       npc->start(250, 5000);
00417       return;
00418       }
00419     }
00420   Game_object_vector foods;     // Food nearby?
00421   int cnt = npc->find_nearby(foods, 377, 2, 0);
00422   if (cnt)      // Found?
00423     {     // Find closest.
00424     Game_object *food = 0;
00425     int dist = 500;
00426     for (Game_object_vector::const_iterator it = foods.begin();
00427           it != foods.end(); ++it)
00428       {
00429       Game_object *obj = *it;
00430       int odist = obj->distance(npc);
00431       if (odist < dist)
00432         {
00433         dist = odist;
00434         food = obj;
00435         }
00436       }
00437     if (rand()%5 == 0)
00438       {
00439       gwin->add_dirty(food);
00440       food->remove_this();
00441       }
00442     if (rand()%4)
00443       npc->say(first_munch, last_munch);
00444     }
00445   else if (rand()%4)
00446     npc->say(first_more_food, last_more_food);
00447           // Wake up in a little while.
00448   npc->start(250, 5000 + rand()%12000);
00449   }
00450 
00451 /*
00452  *  Find someone listening to the preacher.
00453  */
00454 
00455 Actor *Find_congregant
00456   (
00457   Actor *npc
00458   )
00459   {
00460   Actor_vector vec, vec2;
00461   if (!npc->find_nearby_actors(vec, c_any_shapenum, 16))
00462     return 0;
00463   vec2.reserve(vec.size()); // Get list of ones to consider.
00464   for (Actor_vector::const_iterator it = vec.begin();
00465             it != vec.end(); ++it)
00466     {
00467     Actor *act = *it;
00468     if (act->get_schedule_type() == Schedule::sit &&
00469               !act->is_in_party())
00470       vec2.push_back(act);
00471     }
00472   return vec2.size() ? vec2[rand()%vec2.size()] : 0;
00473   }
00474 
00475 /*
00476  *  Preach:
00477  */
00478 
00479 void Preach_schedule::now_what
00480   (
00481   )
00482   {
00483   switch (state)
00484     {
00485   case find_podium:
00486     {
00487     Game_object_vector vec;
00488     if (!npc->find_nearby(vec, 697, 17, 0))
00489       {
00490       npc->set_schedule_type(loiter);
00491       return;
00492       }
00493     Game_object *podium = vec[0];
00494     Tile_coord pos = podium->get_tile();
00495     static int deltas[4][2] = {{-1, 0},{1, 0},{0, -2},{0, 1}};
00496     int frnum = podium->get_framenum()%4;
00497     pos.tx += deltas[frnum][0];
00498     pos.ty += deltas[frnum][1];
00499     Actor_pathfinder_client cost(npc, 0);
00500     Actor_action *pact = Path_walking_actor_action::create_path(
00501       npc->get_tile(), pos, cost);
00502     if (pact)
00503       {
00504       state = at_podium;
00505       npc->set_action(new Sequence_actor_action(pact,
00506         new Face_pos_actor_action(podium, 200)));
00507       npc->start(gwin->get_std_delay());
00508       return;
00509       }
00510     npc->start(250, 5000 + rand()%5000);  // Try again later.
00511     return;
00512     }
00513   case at_podium:
00514     if (rand()%2)   // Just wait a little.
00515       npc->start(gwin->get_std_delay(), rand()%3000);
00516     else 
00517       {
00518       if (rand()%3)
00519         state = exhort;
00520       else if ((GAME_SI) || rand()%3)
00521         state = visit;
00522       else
00523         state = find_icon;
00524       npc->start(gwin->get_std_delay(), 2000 + rand()%2000);
00525       }
00526     return;
00527   case exhort:
00528     {
00529     signed char frames[8];    // Frames.
00530     int cnt = 1 + rand()%(sizeof(frames) - 1);
00531           // Frames to choose from:
00532     static char choices[3] = {0, 8, 9};
00533     for (int i = 0; i < cnt - 1; i++)
00534       frames[i] = npc->get_dir_framenum(
00535           choices[rand()%(sizeof(choices))]);
00536           // Make last one standing.
00537     frames[cnt - 1] = npc->get_dir_framenum(Actor::standing);
00538     npc->set_action(new Frames_actor_action(frames, cnt, 250));
00539     npc->start(gwin->get_std_delay());
00540     npc->say(first_preach, last_preach);
00541     state = at_podium;
00542     Actor *member = Find_congregant(npc);
00543     if (member)
00544       {
00545       Usecode_script *scr = new Usecode_script(member);
00546       scr->add(Ucscript::delay_ticks, 3);
00547       scr->add(Ucscript::face_dir, member->get_dir_facing());
00548       scr->add(Ucscript::npc_frame + Actor::standing);
00549       scr->add(Ucscript::say, item_names[first_amen +
00550           rand()%(last_amen - first_amen + 1)]);
00551       scr->add(Ucscript::delay_ticks, 2);
00552       scr->add(Ucscript::npc_frame + Actor::sit_frame);
00553       scr->start(); // Start next tick.
00554       }
00555     return;
00556     }
00557   case visit:
00558     {
00559     state = find_podium;
00560     npc->start(gwin->get_std_delay(), 1000 + rand()%2000);
00561     Actor *member = Find_congregant(npc);
00562     if (!member)
00563       return;
00564     Tile_coord pos = member->get_tile();
00565     Actor_pathfinder_client cost(npc, 1);
00566     Actor_action *pact = Path_walking_actor_action::create_path(
00567       npc->get_tile(), pos, cost);
00568     if (!pact)
00569       return;
00570     npc->set_action(new Sequence_actor_action(pact,
00571         new Face_pos_actor_action(member, 200)));
00572     state = talk_member;
00573     return;
00574     }
00575   case talk_member:
00576     state = find_podium;
00577     npc->say(first_preach2, last_preach2);
00578     npc->start(250, 2000);
00579     return;
00580   case find_icon:
00581     {
00582     state = find_podium;    // In case we fail.
00583     npc->start(2*gwin->get_std_delay());
00584     Game_object *icon = npc->find_closest(724);
00585     if (!icon)
00586       return;
00587     Tile_coord pos = icon->get_tile();
00588     pos.tx += 2;
00589     pos.ty -= 1;
00590     Actor_pathfinder_client cost(npc, 0);
00591     Actor_action *pact = Path_walking_actor_action::create_path(
00592       npc->get_tile(), pos, cost);
00593     if (pact)
00594       {
00595       static char f[] = {Actor::standing, Actor::bow_frame, 
00596         Actor::kneel_frame, Actor::kneel_frame,
00597         Actor::kneel_frame, Actor::kneel_frame,
00598         Actor::bow_frame, Actor::standing};
00599       npc->set_action(pact);
00600       state = pray;
00601       }
00602     return;
00603     }
00604   case pray:
00605     {
00606     Usecode_script *scr = new Usecode_script(npc);
00607     (*scr) << Ucscript::face_dir << 6 // Face west.
00608       << (Ucscript::npc_frame + Actor::standing)
00609       << (Ucscript::npc_frame + Actor::bow_frame)
00610       << Ucscript::delay_ticks << 3
00611       << (Ucscript::npc_frame + Actor::kneel_frame);
00612     scr->add(Ucscript::say, item_names[first_amen + rand()%2]);
00613     (*scr)  << Ucscript::delay_ticks << 5
00614       << (Ucscript::npc_frame + Actor::bow_frame)
00615       << Ucscript::delay_ticks << 3
00616       << (Ucscript::npc_frame + Actor::standing);
00617       scr->start(); // Start next tick.
00618     state = find_podium;
00619     npc->start(2*gwin->get_std_delay(), 4000);
00620     return;
00621     }
00622   default:
00623     state = find_podium;
00624     npc->start(250);
00625     return;
00626     }
00627   }
00628 
00629 /*
00630  *  Schedule change for patroling:
00631  */
00632 
00633 void Patrol_schedule::now_what
00634   (
00635   )
00636   {
00637   if (sitting)      // Sitting?
00638     {
00639           // Stay 5-15 secs.
00640     if ((npc->get_framenum()&0xf) == Actor::sit_frame)
00641       npc->start(250, 5000 + rand()%10000);
00642     else      // Not sitting.
00643       npc->start(250, rand()%1000);
00644     sitting = false;  // Continue on afterward.
00645     find_next = true;
00646     return;
00647     }
00648   if (rand() % 8 == 0)    // Check for lamps, etc.
00649     if (try_street_maintenance())
00650       return;   // We no longer exist.
00651   const int PATH_SHAPE = 607;
00652   Game_object *path;
00653   if (!find_next &&
00654       pathnum >= 0 &&   // Arrived at path?
00655       pathnum < paths.size() && (path = paths[pathnum]) != 0 &&
00656       npc->distance(path) < 2)
00657           // Quality = type.  (I think high bits
00658           //   are flags.
00659     switch (path->get_quality()&31)
00660       {
00661     case 0:     // None.
00662       break;
00663     case 1:     // Wrap to 0.
00664       pathnum = -1;
00665       dir = 1;
00666       break;
00667     case 2:     // Pause.
00668       break;    //++++++++
00669     case 3:     // Sit.
00670       if (Sit_schedule::set_action(npc))
00671         {
00672         sitting = true;
00673         return;
00674         }
00675       break;
00676     case 4:     // Kneel at tombstone.+++++++
00677     case 5:     // Kneel+++++++++++
00678     case 6:     // Loiter.++++++
00679       break;
00680     case 7:     // Left about-face.
00681     case 8:     // Right about-face.
00682       if (pathnum == 0 && dir == -1)
00683         dir = 1;
00684       else if (pathnum > 0 && dir == 1)
00685         dir = -1;
00686           // ++++Turn animations.
00687       break;
00688     case 9:     // Horiz. pace.+++++++
00689     case 10:    // Vert. pace+++++++
00690     case 11:    // 50% reverse.
00691     case 12:    // 50% skip next.
00692     case 13:    // Hammer.++++++
00693     case 14:    // Check area.
00694       break;    //++++++Implement above.
00695     case 15:    // Usecode.
00696       {   // Schedule so we don't get deleted.
00697       Usecode_script *scr = new Usecode_script(npc);
00698           // Don't let this script halt others,
00699           //   as it messes up automaton in
00700           //   SI-Freedom.
00701       (*scr) << Ucscript::dont_halt <<
00702         Ucscript::usecode2 << npc->get_usecode() <<
00703             static_cast<int>(Usecode_machine::npc_proximity);
00704       scr->start(); // Start next tick.
00705       find_next = true; // THEN, find next path.
00706       npc->start(250, 500);
00707       return;
00708       }
00709     case 16:    // Bow to ground.  ++++Implement.
00710     case 17:    // Bow from ground.+++++
00711     case 18:    // Wait for semaphore+++++
00712     case 19:    // Release semaphore+++++
00713     case 20:    // Ready weapon++++++++
00714     case 21:    // Unready weapon+++++++
00715     case 22:    // One-handed swing.+++++
00716     case 23:    // Two-handed swing.
00717     case 24:    // Read++++++
00718     case 25:    // 50% wrap to 0.++++++
00719     case 26:    // Combat near??
00720     case 27:    // Repeat forever??
00721     default:
00722       break;
00723       }
00724   pathnum += dir;     // Find next path.
00725   find_next = false;    // We're doing it.
00726           // Already know its location?
00727   path =  pathnum >= 0 && pathnum < paths.size() ? paths[pathnum] : 0;
00728   if (!path)      // No, so look around.
00729     {
00730     Game_object_vector nearby;
00731     npc->find_nearby(nearby, PATH_SHAPE, 25, 0);
00732     int best_dist = 1000; // Find closest.
00733     for (Game_object_vector::const_iterator it = nearby.begin();
00734             it != nearby.end(); ++it)
00735       {
00736       Game_object *obj = *it;
00737       int framenum = obj->get_framenum();
00738       int dist;
00739       if (framenum == pathnum && 
00740           (dist = obj->distance(npc)) < best_dist)
00741         { // Found it.
00742         path = obj;
00743         best_dist = dist;
00744         }
00745       }
00746     if (path)   // Save it.
00747       {
00748       failures = 0;
00749       paths.put(pathnum, path);
00750       }
00751     else      // Turn back if at end.
00752       {
00753       failures++;
00754       dir = 1;
00755       if (pathnum == 0) // At start?  Retry.
00756         {
00757         if (failures < 4)
00758           {
00759           pathnum = -1;
00760           npc->start(200, 500);
00761           return;
00762           }
00763           // After 4, fall through.
00764         }
00765       else    // At end, go back to start.
00766         {
00767         pathnum = 0;
00768         path = paths.size() ? paths[0] : 0;
00769         }
00770       }
00771     if (!path)    // Still failed?
00772       {
00773           // Wiggle a bit.
00774       Tile_coord pos = npc->get_tile();
00775       int dist = failures + 2;
00776       Tile_coord delta = Tile_coord(rand()%dist - dist/2,
00777           rand()%dist - dist, 0);
00778       npc->walk_to_tile(pos + delta, gwin->get_std_delay(), 
00779                 failures*300);
00780       int pathcnt = paths.size();
00781       pathnum = rand()%(pathcnt < 4 ? 4 : pathcnt);
00782       return;
00783       }
00784     }
00785   Tile_coord d = path->get_tile();
00786       if (!npc->walk_path_to_tile(d, gwin->get_std_delay(), rand()%1000))
00787     {   
00788           // Look for free tile within 1 square.
00789     d = Map_chunk::find_spot(d, 1, npc->get_shapenum(),
00790             npc->get_framenum(), 1);
00791     if (d.tx == -1 || !npc->walk_path_to_tile(d,
00792           gwin->get_std_delay(), rand()%1000))
00793       {   // Failed.  Later.
00794       npc->start(200, 2000);
00795       return;
00796       }
00797     }
00798   }
00799 
00800 /*
00801  *  Schedule change for 'talk':
00802  */
00803 
00804 void Talk_schedule::now_what
00805   (
00806   )
00807   {
00808 
00809   // Switch to phase 3 if we are reasonable close
00810   if (phase != 0 && phase != 4 &&
00811       npc->distance(gwin->get_main_actor()) < 8)
00812     phase = 3;
00813 
00814   switch (phase)
00815     {
00816   case 0:       // Start by approaching Avatar.
00817     {
00818     if (npc->distance(gwin->get_main_actor()) > 50)
00819       {   // Too far?  
00820           // Try a little later.
00821       npc->start(250, 5000);
00822       return;
00823       }
00824           // Aim for within 5 tiles.
00825     Actor_pathfinder_client cost(npc, 5);
00826     Actor_action *pact = Path_walking_actor_action::create_path(
00827       npc->get_tile(),
00828       gwin->get_main_actor()->get_tile(), cost);
00829     if (!pact)
00830       {
00831 #ifdef DEBUG
00832       cout << "Talk: Failed to find path for " << 
00833             npc->get_name() << endl;
00834 #endif
00835       npc->follow(gwin->get_main_actor());
00836       }
00837     else
00838       {
00839           // Walk there, and retry if
00840           //   blocked.
00841       npc->set_action(pact);
00842       npc->start(400, 250); // Start walking.
00843       }
00844     phase++;
00845     return;
00846     }
00847   case 1:       // Wait a second.
00848   case 2:
00849     {
00850     int dx = 1 - 2*(rand()%2);
00851     npc->walk_to_tile(npc->get_tile() +
00852       Tile_coord(dx, -dx, 0), 300, 500);
00853           // Wait til conversation is over.
00854 //    if (ucmachine->get_num_faces_on_screen() == 0)
00855 //      phase++;
00856       phase = 3;
00857     return;
00858     }
00859   case 3:       // Talk.
00860           // Got to be reachable.
00861     if (!Fast_pathfinder_client::is_grabable(
00862       npc->get_tile(),
00863       gwin->get_main_actor()->get_tile()))
00864       {
00865       phase = 0;
00866       npc->start(250, 1000);
00867       return;
00868       }
00869           // But first face Avatar.
00870     npc->change_frame(npc->get_dir_framenum(npc->get_direction(
00871         gwin->get_main_actor()), Actor::standing));
00872     phase++;
00873     npc->start(250, 250); // Wait another 1/4 sec.
00874     break;
00875   case 4:
00876     npc->stop();    // Stop moving.
00877           // NOTE:  This could DESTROY us!
00878     if (Game::get_game_type() == SERPENT_ISLE)
00879       npc->activate(9);
00880     else
00881       npc->activate(1);
00882           // SO don't refer to any instance
00883           //   variables from here on.
00884     gwin->paint();
00885     return;
00886   default:
00887     break;
00888     }
00889   }
00890 
00891 /*
00892  *  Create a loiter schedule.
00893  */
00894 
00895 Loiter_schedule::Loiter_schedule
00896   (
00897   Actor *n,
00898   int d       // Distance in tiles to roam.
00899   ) : Schedule(n), center(n->get_tile()), dist(d)
00900   {
00901   }
00902 
00903 /*
00904  *  Schedule change for 'loiter':
00905  */
00906 
00907 void Loiter_schedule::now_what
00908   (
00909   )
00910   {
00911   if (rand() % 3 == 0)    // Check for lamps, etc.
00912     if (try_street_maintenance())
00913       return;   // We no longer exist.
00914   int newx = center.tx - dist + rand()%(2*dist);
00915   int newy = center.ty - dist + rand()%(2*dist);
00916           // Wait a bit.
00917   npc->walk_to_tile(newx, newy, center.tz, 2*gwin->get_std_delay(), 
00918                 rand()%2000);
00919   }
00920 
00921 /*
00922  *  Schedule change for kid games.
00923  */
00924 
00925 void Kid_games_schedule::now_what
00926   (
00927   )
00928   {
00929   Tile_coord pos = npc->get_tile();
00930   Actor *kid = 0;     // Get a kid to chase.
00931           // But don't run too far.
00932   while (!kids.empty())
00933   {
00934     kid = kids.pop();
00935     if (npc->distance(kid) < 16)
00936       break;
00937     kid = 0;
00938   }
00939 
00940   if (kid)
00941   {
00942     Fast_pathfinder_client cost(1);
00943     Actor_action *pact = Path_walking_actor_action::create_path(
00944         pos, kid->get_tile(), cost);
00945     if (pact)
00946     {
00947       npc->set_action(pact);
00948       npc->start(100, 250); // Run.
00949       return;
00950     }
00951   }
00952   else        // No more kids?  Search.
00953   {
00954     Actor_vector vec;
00955     npc->find_nearby_actors(vec, c_any_shapenum, 16);
00956     for (Actor_vector::const_iterator it = vec.begin();
00957             it != vec.end(); ++it)
00958     {
00959       Actor *act = *it;
00960       if (act->get_schedule_type() == kid_games)
00961         kids.push(act);
00962     }
00963   }
00964   Loiter_schedule::now_what();  // Wander around the start.
00965 }
00966 
00967 /*
00968  *  Schedule change for 'dance':
00969  */
00970 
00971 void Dance_schedule::now_what
00972   (
00973   )
00974   {
00975   Tile_coord dest = center; // Pick new spot to walk to.
00976   dest.tx += -dist + rand()%(2*dist);
00977   dest.ty += -dist + rand()%(2*dist);
00978   Tile_coord cur = npc->get_tile();
00979   int dir = static_cast<int>(Get_direction4(cur.ty - dest.ty, dest.tx - cur.tx));
00980   signed char frames[4];
00981   for (int i = 0; i < 4; i++)
00982           // Spin with 'hands outstretched'.
00983     frames[i] = npc->get_dir_framenum((2*(dir + i))%8, 
00984                       npc->get_shapenum() == 846 ? 15 : 9);
00985           // Create action to walk.
00986   Actor_action *walk = new Path_walking_actor_action(new Zombie());
00987   walk->walk_to_tile(npc, cur, dest);
00988           // Walk, then spin.
00989   npc->set_action(new Sequence_actor_action(walk,
00990     new Frames_actor_action(frames, sizeof(frames), 100)));
00991   npc->start(gwin->get_std_delay(), 500);   // Start in 1/2 sec.
00992   }
00993 
00994 /*
00995  *  Schedule change for mining/farming:
00996  */
00997 
00998 void Tool_schedule::now_what
00999   (
01000   )
01001   {
01002   if (!tool)      // First time?
01003     {
01004     tool = new Ireg_game_object(toolshape, 0, 0, 0, 0);
01005           // Free up both hands.
01006     Game_object *obj = npc->get_readied(Actor::rhand);
01007     if (obj)
01008       obj->remove_this();
01009     if ((obj = npc->get_readied(Actor::lhand)) != 0)
01010       obj->remove_this();
01011     if (!npc->add_readied(tool, Actor::lrhand))
01012       npc->add_readied(tool, Actor::lhand);
01013     }
01014   if (rand()%4 == 0)    // 1/4 time, walk somewhere.
01015     {
01016     Loiter_schedule::now_what();
01017     return;
01018     }
01019   if (rand()%10 == 0)
01020     {
01021     Schedule_types ty = (Schedule_types) npc->get_schedule_type();
01022     if (ty == Schedule::miner)
01023       npc->say(first_miner, last_miner);
01024     else if (ty == Schedule::farm)
01025       npc->say(first_farmer, last_farmer);
01026     }
01027   signed char frames[12];   // Use pick.
01028   int cnt = npc->get_attack_frames(toolshape, false, rand()%8, frames);
01029   npc->set_action(new Frames_actor_action(frames, cnt));
01030   npc->start();     // Get back into time queue.
01031   }
01032 
01033 /*
01034  *  End of mining/farming:
01035  */
01036 
01037 void Tool_schedule::ending
01038   (
01039   int
01040   )
01041   {
01042   if (tool)
01043     tool->remove_this();  // Should safely remove from NPC.
01044   }
01045 
01046 /*
01047  *  Schedule change for hounding the Avatar.
01048  */
01049 
01050 void Hound_schedule::now_what
01051   (
01052   )
01053   {
01054   Actor *av = gwin->get_main_actor();
01055   Tile_coord avpos = av->get_tile(),
01056        npcpos = npc->get_tile();
01057           // How far away is Avatar?
01058   int dist = npcpos.distance(avpos);
01059   if (dist > 20 || dist < 3)  // Too far, or close enough?
01060     {     // Check again in a few seconds.
01061     npc->start(gwin->get_std_delay(), 500 + rand()%1000);
01062     return;
01063     }
01064   int newdist = 1 + rand()%2; // Aim for about 3 tiles from Avatar.
01065   Fast_pathfinder_client cost(newdist);
01066   avpos.tx += rand()%3 - 1; // Vary a bit randomly.
01067   avpos.ty += rand()%3 - 1;
01068   Actor_action *pact = Path_walking_actor_action::create_path(npcpos,
01069               avpos, cost);
01070   if (pact)
01071     {
01072     npc->set_action(pact);
01073     npc->start(gwin->get_std_delay(), 50);
01074     }
01075   else        // Try again.
01076     npc->start(gwin->get_std_delay(), 2000 + rand()%3000);
01077   }
01078 
01079 /*
01080  *  Schedule change for 'wander':
01081  */
01082 
01083 void Wander_schedule::now_what
01084   (
01085   )
01086   {
01087   if (rand() % 2)     // 1/2 time, check for lamps, etc.
01088     if (try_street_maintenance())
01089       return;   // We no longer exist.
01090   Tile_coord pos = npc->get_tile();
01091   const int legdist = 32;
01092           // Go a ways from current pos.
01093   pos.tx += -legdist + rand()%(2*legdist);
01094   pos.ty += -legdist + rand()%(2*legdist);
01095           // Don't go too far from center.
01096   if (pos.tx - center.tx > dist)
01097     pos.tx = center.tx + dist;
01098   else if (center.tx - pos.tx > dist)
01099     pos.tx = center.tx - dist;
01100   if (pos.ty - center.ty > dist)
01101     pos.ty = center.ty + dist;
01102   else if (center.ty - pos.ty > dist)
01103     pos.ty = center.ty - dist;
01104           // Find a free spot.
01105   Tile_coord dest = Map_chunk::find_spot(pos, 4, npc->get_shapenum(), 0,
01106                   1);
01107   if (dest.tx == -1 || !npc->walk_path_to_tile(dest,
01108           gwin->get_std_delay(), rand()%2000))
01109           // Failed?  Try again a little later.
01110     npc->start(250, rand()%3000);
01111   }
01112 
01113 /*
01114  *  Stand up if not already.
01115  */
01116 
01117 static void Stand_up
01118   (
01119   Actor *npc
01120   )
01121   {
01122   if ((npc->get_framenum()&0xf) != Actor::standing)
01123           // Stand.
01124     npc->change_frame(Actor::standing);
01125   }
01126 
01127 /*
01128  *  Create a sleep schedule.
01129  */
01130 
01131 Sleep_schedule::Sleep_schedule
01132   (
01133   Actor *n
01134   ) : Schedule(n), bed(0), state(0)
01135   {
01136   floorloc.tx = -1;   // Set to invalid loc.
01137   if (Game::get_game_type() == BLACK_GATE)
01138     {
01139     spread0 = 3;
01140     spread1 = 16;
01141     }
01142   else        // Serpent Isle.
01143     {
01144     spread0 = 7;
01145     spread1 = 20;
01146     }
01147   }
01148 
01149 /*
01150  *  Schedule change for 'sleep':
01151  */
01152 
01153 void Sleep_schedule::now_what
01154   (
01155   )
01156   {
01157   if (!bed && state == 0)   // Always find bed.
01158     {     // Find closest EW or NS bed.
01159     static int bedshapes[2] = {696, 1011};
01160     Stand_up(npc);
01161     bed = npc->find_closest(bedshapes, 2);
01162     if (!bed && GAME_BG)  // Check for Gargoyle beds.
01163       {
01164       static int gbeds[2] = {363, 312};
01165       bed = npc->find_closest(gbeds, 2);
01166       }
01167     }
01168   int frnum = npc->get_framenum();
01169   if ((frnum&0xf) == Actor::sleep_frame)
01170     return;     // Already sleeping.
01171 
01172   switch (state)
01173     {
01174   case 0:       // Find path to bed.
01175     {
01176     if (!bed)
01177       {   // Just lie down at current spot.
01178       int dirbits = npc->get_framenum()&(0x30);
01179       npc->change_frame(Actor::sleep_frame|dirbits);
01180       return;
01181       }
01182     state = 1;
01183     Game_object_vector tops;  // Want to find top of bed.
01184     bed->find_nearby(tops, bed->get_shapenum(), 1, 0);
01185     for (Game_object_vector::const_iterator it = tops.begin(); 
01186             it != tops.end(); ++it)
01187       {
01188       Game_object *top = *it;
01189       int frnum = top->get_framenum();
01190       if (frnum >= spread0 && frnum <= spread1)
01191         {
01192         bed = top;
01193         break;
01194         }
01195       }
01196     Tile_coord bloc = bed->get_tile();
01197     bloc.tz -= bloc.tz%5; // Round down to floor level.
01198     Shape_info& info = bed->get_info();
01199     bloc.tx -= info.get_3d_xtiles(bed->get_framenum())/2;
01200     bloc.ty -= info.get_3d_ytiles(bed->get_framenum())/2;
01201           // Get within 3 tiles.
01202     Actor_pathfinder_client cost(npc, 3);
01203     Actor_action *pact = Path_walking_actor_action::create_path(
01204         npc->get_tile(), bloc, cost);
01205     if (pact)
01206       npc->set_action(pact);
01207     npc->start(200);  // Start walking.
01208     break;
01209     }
01210   case 1:       // Go to bed.
01211     {
01212     npc->stop();    // Just to be sure.
01213     int bedshape = bed->get_shapenum();
01214     int dir = (bedshape == 696 || bedshape == 363) ? west : north;
01215     npc->set_frame(npc->get_dir_framenum(dir, Actor::sleep_frame));
01216           // Get bed info.
01217     Shape_info& info = bed->get_info();
01218     Tile_coord bedloc = bed->get_tile();
01219     floorloc = npc->get_tile();
01220     int bedframe = bed->get_framenum();// Unmake bed.
01221     if (bedframe >= spread0 && bedframe < spread1 && (bedframe%2))
01222       {
01223       bedframe++;
01224       bed->change_frame(bedframe);
01225       }
01226     int bedspread = (bedframe >= spread0 && !(bedframe%2));
01227           // Put NPC on top of bed.
01228     npc->move(bedloc.tx, bedloc.ty, bedloc.tz + 
01229         (bedspread ? 0 : info.get_3d_height()));
01230     state = 2;
01231     break;
01232     }
01233   default:
01234     break;
01235     }
01236   }
01237 
01238 /*
01239  *  Wakeup time.
01240  */
01241 
01242 void Sleep_schedule::ending
01243   (
01244   int new_type      // New schedule.
01245   )
01246   {
01247           // Needed for Skara Brae.
01248   if (new_type == static_cast<int>(wait) || 
01249           // Not time to get up, Penumbra!
01250       new_type == static_cast<int>(sleep))
01251     return;     // ++++Does this leave NPC's stuck?++++
01252   if (bed &&      // Still in bed?
01253       (npc->get_framenum()&0xf) == Actor::sleep_frame &&
01254       npc->distance(bed) < 8)
01255     {     // Locate free spot.
01256     if (floorloc.tx == -1)
01257           // Want spot on floor.
01258       floorloc = npc->get_tile();
01259     floorloc.tz -= floorloc.tz%5;
01260     Tile_coord pos = Map_chunk::find_spot(floorloc, 
01261         6, npc->get_shapenum(), 
01262         static_cast<int>(Actor::standing), 0);
01263     if (pos.tx == -1) // Failed?  Allow change in lift.
01264       pos = Map_chunk::find_spot(floorloc,
01265         6, npc->get_shapenum(), 
01266         static_cast<int>(Actor::standing), 1);
01267     floorloc = pos;
01268           // Make bed.
01269     int frnum = bed->get_framenum();
01270     Actor_vector occ; // Unless there's another occupant.
01271     if (frnum >= spread0 && frnum <= spread1 && !(frnum%2) &&
01272         bed->find_nearby_actors(occ, c_any_shapenum, 0) < 2)
01273       bed->set_frame(frnum - 1);
01274     }
01275   if (floorloc.tx >= 0)   // Get back on floor.
01276     npc->move(floorloc);
01277   npc->set_frame(Actor::standing);
01278   gwin->set_all_dirty();    // Update all, since Av. stands up.
01279   state = 0;      // In case we go back to sleep.
01280   }
01281 
01282 /*
01283  *  Create a 'sit' schedule.
01284  */
01285 
01286 Sit_schedule::Sit_schedule
01287   (
01288   Actor *n,
01289   Game_object *ch     // Chair, or null to find one.
01290   ) : Schedule(n), chair(ch), sat(false), did_barge_usecode(false)
01291   {
01292   }
01293 
01294 /*
01295  *  Schedule change for 'sit':
01296  */
01297 
01298 void Sit_schedule::now_what
01299   (
01300   )
01301   {
01302   int frnum = npc->get_framenum();
01303   if (chair && (frnum&0xf) == Actor::sit_frame && 
01304       npc->distance(chair) <= 1)
01305     {     // Already sitting.
01306           // Seat on barge?
01307     if (!chair || chair->get_shapenum() != 292)
01308       return;
01309     if (did_barge_usecode)
01310       return;   // But NOT more than once for party.
01311     did_barge_usecode = true;
01312     if (gwin->get_moving_barge())
01313       return;   // Already moving.
01314     if (!npc->is_in_party())
01315       return;   // Not a party member.
01316     Actor *party[9];  // See if all sitting.
01317     int cnt = gwin->get_party(&party[0], 1);
01318     for (int i = 0; i < cnt; i++)
01319       if ((party[i]->get_framenum()&0xf) != Actor::sit_frame)
01320         return; // Nope.
01321           // Find barge.
01322     Game_object *barge = chair->find_closest(961);
01323     if (!barge)
01324       return;
01325     int usefun = 0x634; // I hate using constants like this.
01326     did_barge_usecode = true;
01327           // Special usecode for barge pieces:
01328           // (Call with item=Avatar to avoid
01329           //   running nearby barges.)
01330     ucmachine->call_usecode(usefun, gwin->get_main_actor(),
01331           Usecode_machine::double_click);
01332     return;
01333     }
01334           // Wait a while if we got up.
01335   if (!set_action(npc, chair, sat ? (2000 + rand()%3000) : 0, &chair))
01336     npc->start(200, 5000);  // Failed?  Try again later.
01337   else
01338     sat = true;
01339   }
01340 
01341 /*
01342  *  Action to sit in the chair NPC is in front of.
01343  */
01344 
01345 class Sit_actor_action : public Frames_actor_action, public Game_singletons
01346   {
01347   Game_object *chair;   // Chair.
01348   Tile_coord chairloc;    // Original chair location.
01349   Tile_coord sitloc;    // Actually where NPC sits.
01350   signed char frames[2];
01351   static short offsets[8];  // Offsets where NPC should sit.
01352   signed char *init(Game_object *chairobj, Actor *actor)
01353     {
01354           // Frame 0 faces N, 1 E, etc.
01355     int dir = 2*(chairobj->get_framenum()%4);
01356     frames[0] = actor->get_dir_framenum(dir, Actor::bow_frame);
01357     frames[1] = actor->get_dir_framenum(dir, Actor::sit_frame);
01358     return frames;
01359     }
01360   static bool is_occupied(Tile_coord sitloc, Actor *actor)
01361     {
01362     Game_object_vector occ; // See if occupied.
01363     if (Game_object::find_nearby(occ, sitloc,c_any_shapenum, 0, 8))
01364       for (Game_object_vector::const_iterator it = 
01365           occ.begin(); it != occ.end(); ++it)
01366         {
01367         Game_object *npc = *it;
01368         if (npc == actor)
01369           continue;
01370         int frnum = npc->get_framenum() & 15;
01371         if (frnum == Actor::sit_frame ||
01372             frnum == Actor::bow_frame)
01373           return true;
01374         }
01375 #if 1 /* Seems to work.  Added Nov. 2, 2001 */
01376     if (actor->get_tile() == sitloc)
01377       return false; // We're standing there.
01378           // See if spot is blocked.
01379     Tile_coord pos = sitloc;// Careful, .tz gets updated.
01380     if (Map_chunk::is_blocked(pos, 
01381           actor->get_info().get_3d_height(), MOVE_WALK, 0))
01382       return true;
01383 #endif
01384     return false;
01385     }
01386 public:
01387   Sit_actor_action(Game_object *o, Actor *actor) : chair(o),
01388       Frames_actor_action(init(o, actor), 2)
01389     {
01390     sitloc = chairloc = o->get_tile();
01391           // Frame 0 faces N, 1 E, etc.
01392     int nsew = o->get_framenum()%4;
01393     sitloc.tx += offsets[2*nsew];
01394     sitloc.ty += offsets[2*nsew + 1];
01395     }
01396   Tile_coord get_sitloc() const
01397     { return sitloc; }
01398   static bool is_occupied(Game_object *chair, Actor *actor)
01399     {
01400     int dir = 2*(chair->get_framenum()%4);
01401     return is_occupied(chair->get_tile() +
01402       Tile_coord(offsets[dir], offsets[dir + 1], 0), actor);
01403     }
01404           // Handle time event.
01405   virtual int handle_event(Actor *actor);
01406   };
01407 
01408           // Offsets where NPC should sit:
01409 short Sit_actor_action::offsets[8] = {0,-1, 1,0, 0,1, -1,0};
01410 
01411 /*
01412  *  Show frame for sitting down.
01413  */
01414 
01415 int Sit_actor_action::handle_event
01416   (
01417   Actor *actor
01418   )
01419   {
01420   if (get_index() == 0)   // First time?
01421     {
01422     if (is_occupied(sitloc, actor))
01423       return 0; // Abort.
01424     if (chair->get_tile() != chairloc)
01425       {   // Chair was moved!
01426       actor->say(first_chair_thief, last_chair_thief);
01427       return 0;
01428       }
01429     }
01430   return Frames_actor_action::handle_event(actor);
01431   }
01432 
01433 /*
01434  *  Is chair occupied, other than by 'actor'?
01435  */
01436 
01437 bool Sit_schedule::is_occupied
01438   (
01439   Game_object *chairobj,
01440   Actor *actor
01441   )
01442   {
01443   return Sit_actor_action::is_occupied(chairobj, actor);
01444   }
01445 
01446 /*
01447  *  Set up action.
01448  *
01449  *  Output: false if couldn't find a free chair.
01450  */
01451 
01452 bool Sit_schedule::set_action
01453   (
01454   Actor *actor,
01455   Game_object *chairobj,    // May be 0 to find it.
01456   int delay,      // Msecs. to delay.
01457   Game_object **chair_found // ->chair ret'd if not NULL.
01458   )
01459   {
01460   static int chairshapes[] = {873,292};
01461   Game_object_vector chairs;
01462   if (chair_found)
01463     *chair_found = 0; // Init. in case we fail.
01464   if (!chairobj)      // Find chair if not given.
01465     {
01466     actor->find_closest(chairs, chairshapes,
01467           sizeof(chairs)/sizeof(chairs[0]));
01468     for (Game_object_vector::const_iterator it = chairs.begin();
01469             it != chairs.end(); ++it)
01470       if (!Sit_actor_action::is_occupied((*it), actor))
01471         { // Found an unused one.
01472         chairobj = *it;
01473         break;
01474         }
01475     if (!chairobj)
01476       return false;
01477     }
01478   else if (Sit_actor_action::is_occupied(chairobj, actor))
01479     return false;   // Given chair is occupied.
01480   Sit_actor_action *act = new Sit_actor_action(chairobj, actor);
01481           // Walk there, then sit.
01482   set_action_sequence(actor, act->get_sitloc(), act, false, delay);
01483   if (chair_found)
01484     *chair_found = chairobj;
01485   return true;
01486   }
01487 
01488 /*
01489  *  Desk work.
01490  */
01491 
01492 Desk_schedule::Desk_schedule
01493   (
01494   Actor *n
01495   ) : Schedule(n), chair(0)
01496   {
01497   }
01498 
01499 /*
01500  *  Schedule change for 'desk work':
01501  */
01502 
01503 void Desk_schedule::now_what
01504   (
01505   )
01506   {
01507   if (!chair)     // No chair found yet.
01508     {
01509     static int desks[2] = {283, 407};
01510     static int chairs[2] = {873,292};
01511     Stand_up(npc);
01512     Game_object *desk = npc->find_closest(desks, 2);
01513     if (desk)
01514       chair = desk->find_closest(chairs, 2);
01515     if (!chair)   // Failed.
01516       {   // Try again in a few seconds.
01517       npc->start(200, 5000);
01518       return; 
01519       }
01520     }
01521   int frnum = npc->get_framenum();
01522   if ((frnum&0xf) != Actor::sit_frame)
01523     {
01524     if (!Sit_schedule::set_action(npc, chair, 0))
01525       {
01526       chair = 0;  // Look for any nearby chair.
01527       npc->start(200, 5000);  // Failed?  Try again later.
01528       }
01529     else
01530       npc->start(250, 0);
01531     }
01532   else        // Stand up a second.
01533     {
01534     signed char frames[3];
01535     frames[0] = npc->get_dir_framenum(Actor::standing);
01536     frames[1] = npc->get_dir_framenum(Actor::bow_frame);
01537     frames[2] = npc->get_dir_framenum(Actor::sit_frame);
01538     npc->set_action(new Frames_actor_action(frames,
01539           sizeof(frames)/sizeof(frames[0])));
01540     npc->start(250, 10000 + rand()%5000);
01541     }
01542   }
01543 
01544 /*
01545  *  A class for indexing the perimeter of a rectangle.
01546  */
01547 class Perimeter
01548   {
01549   Rectangle perim;    // Outside given rect.
01550   int sz;       // # squares.
01551 public:
01552   Perimeter(Rectangle &r) : sz(2*r.w + 2*r.h + 4)
01553     {
01554     perim = r;
01555     perim.enlarge(1);
01556     }
01557   int size() { return sz; }
01558           // Get i'th tile.
01559   void get(int i, Tile_coord& ptile, Tile_coord& atile);
01560 #if 0
01561   static void test()
01562     {
01563     Rectangle r(10, 10, 3, 4);
01564     Perimeter p(r);
01565     int cnt = p.size();
01566     for (int i = 0; i < cnt; i++)
01567       {
01568       Tile_coord pt, at;
01569       p.get(i, pt, at);
01570       cout << "pt.tx = " << pt.tx << ", pt.ty = " << pt.ty
01571         << ", at.tx = " << at.tx << ", at.ty = " <<
01572         at.ty << endl;
01573       }
01574     }
01575 #endif
01576   };
01577 
01578 /*
01579  *  Get the i'th perimeter tile and the tile in the original rect.
01580  *  that's adjacent.
01581  */
01582 
01583 void Perimeter::get
01584   (
01585   int i,
01586   Tile_coord& ptile,    // Perim. tile returned.
01587   Tile_coord& atile   // Adjacent tile returned.
01588   )
01589   {
01590   if (i < perim.w - 1)    // Spiral around from top-left.
01591     {
01592     ptile = Tile_coord(perim.x + i, perim.y, 0);
01593     atile = ptile + Tile_coord(!i ? 1 : 0, 1, 0);
01594     return;
01595     }
01596   i -= perim.w - 1;
01597   if (i < perim.h - 1)
01598     {
01599     ptile = Tile_coord(perim.x + perim.w - 1, perim.y + i, 0);
01600     atile = ptile + Tile_coord(-1, !i ? 1 : 0, 0);
01601     return;
01602     }
01603   i -= perim.h - 1;
01604   if (i < perim.w - 1)
01605     {
01606     ptile = Tile_coord(perim.x + perim.w - 1 - i,
01607           perim.y + perim.h - 1, 0);
01608     atile = ptile + Tile_coord(!i ? -1 : 0, -1, 0);
01609     return;
01610     }
01611   i -= perim.w - 1;
01612     {
01613     ptile = Tile_coord(perim.x, perim.y + perim.h - 1 - i, 0);
01614     atile = ptile + Tile_coord(1, !i ? -1 : 0, 0);
01615     return;
01616     }
01617           // Bad index if here.
01618   get(i%sz, ptile, atile);
01619   }
01620 
01621 /*
01622  *  Initialize.
01623  */
01624 
01625 void Lab_schedule::init
01626   (
01627   )
01628   {
01629   chair = book = 0;
01630   cauldron = npc->find_closest(995, 20);
01631           // Find 'lab' tables.
01632   npc->find_nearby(tables, 1003, 20, 0);
01633   npc->find_nearby(tables, 1018, 20, 0);
01634   int cnt = tables.size();  // Look for book, chair.
01635   for (int i = 0; (!book || !chair) && i < cnt; i++)
01636     {
01637     static int chairs[2] = {873,292};
01638     Game_object *table = tables[i];
01639     Rectangle foot = table->get_footprint();
01640           // Book on table?
01641     if (!book && (book = table->find_closest(642, 4)) != 0)
01642       {
01643       Tile_coord p = book->get_tile();
01644       if (!foot.has_point(p.tx, p.ty))
01645         book = 0;
01646       }
01647     if (!chair)
01648       chair = table->find_closest(chairs, 2, 4);
01649     }
01650   }
01651 
01652 /*
01653  *  Create lab schedule.
01654  */
01655 
01656 Lab_schedule::Lab_schedule
01657   (
01658   Actor *n
01659   ) : Schedule(n), state(start)
01660   {
01661   init();
01662   }
01663 
01664 /*
01665  *  Lab work.
01666  */
01667 
01668 void Lab_schedule::now_what
01669   (
01670   )
01671   {
01672   Tile_coord npcpos = npc->get_tile();
01673   int delay = 100;    // 1/10 sec. to next action.
01674           // Often want to get within 1 tile.
01675   Actor_pathfinder_client cost(npc, 1);
01676   switch (state)
01677     {
01678   case start:
01679   default:
01680     {
01681     if (!cauldron)
01682       {   // Try looking again.
01683       init();
01684       if (!cauldron)  // Again a little later.
01685         delay = 6000;
01686       break;
01687       }
01688     int r = rand()%5; // Pick a state.
01689     if (!r)     // Sit less often.
01690       state = sit_down;
01691     else
01692       state = r <= 2 ? walk_to_cauldron : walk_to_table;
01693     break;
01694     }
01695   case walk_to_cauldron:
01696     {
01697     state = start;    // In case we fail.
01698     if (!cauldron)
01699       break;
01700     Actor_action *pact = Path_walking_actor_action::create_path(
01701         npcpos, cauldron->get_tile(), cost);
01702     if (pact)
01703       {
01704       npc->set_action(new Sequence_actor_action(pact,
01705         new Face_pos_actor_action(cauldron, 200)));
01706       state = use_cauldron;
01707       }
01708     break;
01709     }
01710   case use_cauldron:
01711     {
01712     int dir = npc->get_direction(cauldron);
01713           // Set random frame (skip last frame).
01714     cauldron->change_frame(rand()%(cauldron->get_num_frames() -1));
01715     npc->change_frame(
01716       npc->get_dir_framenum(dir, Actor::bow_frame));
01717     int r = rand()%5;
01718     state = !r ? use_cauldron : (r <= 2 ? sit_down
01719             : walk_to_table);
01720     break;
01721     }
01722   case sit_down:
01723     if (!chair || !Sit_schedule::set_action(npc, chair, 200))
01724       state = start;
01725     else
01726       state = read_book;
01727     break;
01728   case read_book:
01729     {
01730     state = stand_up;
01731     if (!book || npc->distance(book) > 4)
01732       break;
01733           // Read a little while.
01734     delay = 1000 + 1000*(rand()%5);
01735           // Open book.
01736     int frnum = book->get_framenum();
01737     book->change_frame(frnum - frnum%3);
01738     break;
01739     }
01740   case stand_up:
01741     {
01742     if (book && npc->distance(book) < 4)
01743       {   // Close book.
01744       int frnum = book->get_framenum();
01745       book->change_frame(frnum - frnum%3 + 1);
01746       }
01747     state = start;
01748     break;
01749     }
01750   case walk_to_table:
01751     {
01752     state = start;    // In case we fail.
01753     int ntables = tables.size();
01754     if (!ntables)
01755       break;
01756     Game_object *table = tables[rand()%ntables];
01757     Rectangle r = table->get_footprint();
01758     Perimeter p(r);   // Find spot adjacent to table.
01759     Tile_coord spot;  // Also get closest spot on table.
01760     p.get(rand()%p.size(), spot, spot_on_table);
01761     Actor_pathfinder_client cost0(npc, 0);
01762     Actor_action *pact = Path_walking_actor_action::create_path(
01763               npcpos, spot, cost0);
01764     if (!pact)
01765       break;    // Failed.
01766     Shape_info& info = table->get_info();
01767     spot_on_table.tz += info.get_3d_height();
01768     npc->set_action(new Sequence_actor_action(pact,
01769       new Face_pos_actor_action(spot_on_table, 200)));
01770     state = use_potion;
01771     break;
01772     }
01773   case use_potion:
01774     {
01775     state = start;
01776     Exult_vector<Game_object *> potions;
01777     Game_object::find_nearby(potions, spot_on_table, 340, 0, 0);
01778     if (potions.size()) // Found a potion.  Remove it.
01779       {
01780       gwin->add_dirty(potions[0]);
01781       potions[0]->remove_this();
01782       }
01783     else      // Create potion if spot is empty.
01784       {
01785       Tile_coord t = Map_chunk::find_spot(spot_on_table, 0,
01786               340, 0, 0);
01787       if (t.tx != -1 && t.tz == spot_on_table.tz)
01788         {
01789         // create a potion randomly, but don't use the last frame
01790         int nframes = ShapeID(340, 0).get_num_frames() - 1;
01791         Game_object *p = gmap->create_ireg_object(
01792           ShapeID::get_info(340), 340,
01793           rand()%nframes, 0, 0, 0);
01794         p->move(t);
01795         }
01796       }
01797     signed char frames[2];
01798     int dir = npc->get_direction(spot_on_table);
01799           // Reach out hand:
01800     frames[0] = npc->get_dir_framenum(dir, 1);
01801     frames[1] = npc->get_dir_framenum(dir, Actor::standing);
01802     npc->set_action(new Frames_actor_action(frames, 2));
01803     break;
01804     }
01805     }
01806   npc->start(gwin->get_std_delay(), delay); // Back in queue.
01807   }
01808 
01809 /*
01810  *  Schedule change for 'shy':
01811  */
01812 
01813 void Shy_schedule::now_what
01814   (
01815   )
01816   {
01817   Actor *av = gwin->get_main_actor();
01818   Tile_coord avpos = av->get_tile(),
01819        npcpos = npc->get_tile();
01820           // How far away is Avatar?
01821   int dist = npcpos.distance(avpos);
01822   if (dist > 10)      // Far enough?
01823     {     // Check again in a few seconds.
01824     if (rand()%3)   // Just wait.
01825       npc->start(250, 1000 + rand()%1000);
01826     else      // Sometimes wander.
01827       npc->walk_to_tile(
01828         Tile_coord(npcpos.tx + rand()%6 - 3,
01829           npcpos.ty + rand()%6 - 3, npcpos.tz));
01830     return;
01831     }
01832           // Get deltas.
01833   int dx = npcpos.tx - avpos.tx, dy = npcpos.ty - avpos.ty;
01834   int adx = dx < 0 ? -dx : dx;
01835   int ady = dy < 0 ? -dy : dy;
01836           // Which is farthest?
01837   int farthest = adx < ady ? ady : adx;
01838   int factor = farthest < 2 ? 9 : farthest < 4 ? 4 
01839         : farthest < 7 ? 2 : 1;
01840           // Walk away.
01841   Tile_coord dest = npcpos + Tile_coord(dx*factor, dy*factor, 0);
01842   Tile_coord delta = Tile_coord(rand()%3, rand()%3, 0);
01843   dest = dest + delta;
01844   Monster_pathfinder_client cost(npc, dest, 4);
01845   Actor_action *pact = Path_walking_actor_action::create_path(
01846               npcpos, dest, cost);
01847   if (pact)     // Found path?
01848     {
01849     npc->set_action(pact);
01850     npc->start(200, 100 + rand()%200);  // Start walking.
01851     }
01852   else          // Try again in a couple secs.
01853     npc->start(250, 500 + rand()%1000);
01854   }
01855 
01856 
01857 /*
01858  *  Create a 'waiter' schedule.
01859  */
01860 
01861 Waiter_schedule::Waiter_schedule
01862   (
01863   Actor *n
01864   ) : Schedule(n), first(1), startpos(n->get_tile()), customer(0)
01865     
01866   {
01867   }
01868 
01869 /*
01870  *  Get a new customer & walk to a prep. table.
01871  */
01872 
01873 void Waiter_schedule::get_customer
01874   (
01875   )
01876 {
01877   if (customers.empty())      // Got to search?
01878   {
01879     Actor_vector vec;   // Look within 32 tiles;
01880     npc->find_nearby_actors(vec, c_any_shapenum, 32);
01881     for (Actor_vector::const_iterator it = vec.begin();
01882               it != vec.end(); ++it)
01883     {   // Filter them.
01884       Actor *each = (Actor *) *it;
01885       if (each->get_schedule_type() == Schedule::eat_at_inn)
01886         customers.push(each);
01887     }
01888   }
01889 
01890   if (!customers.empty())
01891     customer = customers.pop();
01892   npc->say(first_waiter_ask, last_waiter_ask);  
01893   if (prep_tables.size()) // Walk to a 'prep' table.
01894   {
01895     Game_object *table = prep_tables[rand()%prep_tables.size()];
01896     Tile_coord pos = Map_chunk::find_spot(
01897       table->get_tile(), 1, npc);
01898     if (pos.tx != -1 &&
01899         npc->walk_path_to_tile(pos, gwin->get_std_delay(), 
01900               1000 + rand()%1000))
01901       return;
01902   }
01903   const int dist = 8;   // Bad luck?  Walk randomly.
01904   int newx = startpos.tx - dist + rand()%(2*dist);
01905   int newy = startpos.ty - dist + rand()%(2*dist);
01906   npc->walk_to_tile(newx, newy, startpos.tz, 2*gwin->get_std_delay(), 
01907                 rand()%2000);
01908 }
01909 
01910 /*
01911  *  Find tables and categorize them.
01912  */
01913 
01914 void Waiter_schedule::find_tables
01915   (
01916   int shapenum
01917   )
01918   {
01919   Game_object_vector vec;
01920   npc->find_nearby(vec, shapenum, 32, 0);
01921   int floor = npc->get_lift()/5;  // Make sure it's on same floor.
01922   for (Game_object_vector::const_iterator it = vec.begin(); it != vec.end();
01923                 ++it)
01924     {
01925     Game_object *table = *it;
01926     if (table->get_lift()/5 != floor)
01927       continue;
01928     Game_object_vector chairs;    // No chairs by it?
01929     if (!table->find_nearby(chairs, 873, 3, 0) &&
01930         !table->find_nearby(chairs, 292, 3, 0))
01931       prep_tables.append(table);
01932     else
01933       eating_tables.append(table);
01934     }
01935   }
01936 
01937 /*
01938  *  Find serving spot for a customer.
01939  *
01940  *  Output: Plate if found, with spot set.  Plate is created if needed.
01941  */
01942 
01943 Game_object *Waiter_schedule::find_serving_spot
01944   (
01945   Tile_coord& spot
01946   )
01947   {
01948   Game_object_vector plates;  // First look for a nearby plate.
01949   Game_object *plate = 0;
01950   int cnt = npc->find_nearby(plates, 717, 1, 0);
01951   if (!cnt)
01952     cnt = npc->find_nearby(plates, 717, 2, 0);
01953   int floor = npc->get_lift()/5;  // Make sure it's on same floor.
01954   for (Game_object_vector::const_iterator it = plates.begin();
01955           it != plates.end(); ++it)
01956     {
01957     plate = *it;
01958     if (plate->get_lift()/5 == floor)
01959       {
01960       spot = plate->get_tile();
01961       spot.tz++;  // Just above plate.
01962       return plate;
01963       }
01964     }
01965   Tile_coord cpos = customer->get_tile();
01966   
01967   // Blame MSVC
01968   {
01969     // Go through tables.
01970 
01971   for (Game_object_vector::const_iterator it = eating_tables.begin();
01972           it != eating_tables.end(); ++it)
01973     {
01974     Game_object *table = *it;
01975     Rectangle foot = table->get_footprint();
01976     if (foot.distance(cpos.tx, cpos.ty) > 2)
01977       continue;
01978           // Found it.
01979     spot = cpos;    // Start here.
01980           // East/West of table?
01981     if (cpos.ty >= foot.y && cpos.ty < foot.y + foot.h)
01982       spot.tx = cpos.tx <= foot.x ? foot.x
01983               : foot.x + foot.w - 1;
01984     else      // North/south.
01985       spot.ty = cpos.ty <= foot.y ? foot.y
01986             : foot.y + foot.h - 1;
01987     if (foot.has_point(spot.tx, spot.ty))
01988       {   // Passes test.
01989       Shape_info& info = table->get_info();
01990       spot.tz = table->get_lift() + info.get_3d_height();
01991       plate = gmap->create_ireg_object(717, 0);
01992       plate->move(spot);
01993       spot.tz++;  // Food goes above plate.
01994       return plate;
01995       }
01996     }
01997   }
01998   return 0;     // Failed.
01999   }
02000 
02001 /*
02002  *  Schedule change for 'waiter':
02003  */
02004 
02005 void Waiter_schedule::now_what
02006   (
02007   )
02008   {
02009   if (rand() % 4 == 0)    // Check for lamps, etc.
02010     if (try_street_maintenance())
02011       return;   // We no longer exist.
02012   if (first)      // First time?
02013     {
02014     first = 0;    // Find tables.
02015     find_tables(971);
02016     find_tables(633);
02017     find_tables(847);
02018     find_tables(1003);
02019     find_tables(1018);
02020     find_tables(890);
02021     find_tables(964);
02022     find_tables(333);
02023     }
02024   int dist = customer ? npc->distance(customer) : 5000;
02025   if (dist > 32)      // Need a new customer?
02026     {
02027     get_customer();   // Find one, and walk to a prep. table.
02028     return;
02029     }
02030   if (dist < 3)     // Close enough to customer?
02031     {
02032     Game_object_vector foods;
02033     if (customer->find_nearby(foods, 377, 2, 0) > 0)
02034       {
02035       if (rand()%4)
02036         npc->say(first_waiter_banter, 
02037               last_waiter_banter);
02038       }
02039     else      // Needs food.
02040       {
02041       Game_object *food = npc->get_readied(Actor::lhand);
02042       Tile_coord spot;
02043       if (food && food->get_shapenum() == 377 &&
02044           find_serving_spot(spot))
02045         {
02046         npc->change_frame(npc->get_dir_framenum(
02047           npc->get_direction(customer),
02048               Actor::standing));
02049         npc->remove(food);
02050         food->set_invalid();
02051         food->move(spot);
02052         if (rand()%3)
02053           npc->say(first_waiter_serve,
02054              last_waiter_serve);
02055         }
02056       }
02057     customer = 0;   // Done with this one.
02058     npc->start(250, rand()%3000);
02059     return;
02060     }
02061           // Walk to customer with food.
02062   if (!npc->get_readied(Actor::lhand))
02063     {     // Acquire some food.
02064     int nfoods = ShapeID(377, 0).get_num_frames();
02065     int frame = rand()%nfoods;
02066     Game_object *food = new Ireg_game_object(377, frame, 0, 0, 0);
02067     npc->add_readied(food, Actor::lhand);
02068     }
02069   Tile_coord dest = Map_chunk::find_spot(customer->get_tile(),
02070           3, npc);
02071   if (dest.tx != -1 && npc->walk_path_to_tile(dest,
02072           gwin->get_std_delay(), rand()%1000))
02073     return;       // Walking there.
02074 
02075   npc->start(200, 2000 + rand()%4000);  // Failed so try again later.
02076   }
02077 
02078 /*
02079  *  Waiter schedule is done.
02080  */
02081 
02082 void Waiter_schedule::ending
02083   (
02084   int new_type      // New schedule.
02085   )
02086   {
02087           // Remove what he/she is carrying.
02088   Game_object *obj = npc->get_readied(Actor::lhand);
02089   if (obj)
02090     obj->remove_this();
02091   obj = npc->get_readied(Actor::rhand);
02092   if (obj)
02093     obj->remove_this();
02094   }
02095 
02096 /*
02097  *  Sew/weave schedule.
02098  */
02099 
02100 Sew_schedule::Sew_schedule
02101   (
02102   Actor *n
02103   ) : Schedule(n), state(get_wool), bale(0), spinwheel(0),
02104         chair(0), spindle(0), loom(0), cloth(0), work_table(0),
02105         wares_table(0), sew_clothes_cnt(0)
02106   {
02107   }
02108 
02109 /*
02110  *  Sew/weave.
02111  */
02112 
02113 void Sew_schedule::now_what
02114   (
02115   )
02116   {
02117   Tile_coord npcpos = npc->get_tile();
02118           // Often want to get within 1 tile.
02119   Actor_pathfinder_client cost(npc, 1);
02120   switch (state)
02121     {
02122   case get_wool:
02123     {
02124     if (spindle)    // Clean up any remainders.
02125       spindle->remove_this();
02126     if (cloth)
02127       cloth->remove_this();
02128     cloth = spindle = 0;
02129     npc->remove_quantity(2, 654, c_any_qual, c_any_framenum);
02130     npc->remove_quantity(2, 851, c_any_qual, c_any_framenum);
02131 
02132     bale = npc->find_closest(653);
02133     if (!bale)    // Just skip this step.
02134       {
02135       state = sit_at_wheel;
02136       break;
02137       }
02138     Actor_action *pact = Path_walking_actor_action::create_path(
02139         npcpos, bale->get_tile(), cost);
02140     if (pact)
02141       npc->set_action(new Sequence_actor_action(pact,
02142         new Pickup_actor_action(bale, 250),
02143         new Pickup_actor_action(bale,
02144           bale->get_tile(), 250)));
02145     state = sit_at_wheel;
02146     break;
02147     }
02148   case sit_at_wheel:
02149     chair = npc->find_closest(873);
02150     if (!chair || !Sit_schedule::set_action(npc, chair, 200)) {
02151       // uh-oh... try again in a few seconds
02152       npc->start(250, 2500);
02153       return;
02154     }
02155     state = spin_wool;
02156     break;
02157   case spin_wool:     // Cycle spinning wheel 8 times.
02158     spinwheel = npc->find_closest(651);
02159     if (!spinwheel) {
02160       // uh-oh... try again in a few seconds?
02161       npc->start(250, 2500);
02162       return;
02163     }
02164     npc->set_action(new Object_animate_actor_action(spinwheel,
02165                 8, 200));
02166     state = get_thread;
02167     break;
02168   case get_thread:
02169     {
02170     Tile_coord t = Map_chunk::find_spot(
02171         spinwheel->get_tile(), 1, 654, 0);
02172     if (t.tx != -1)   // Space to create thread?
02173       {
02174       spindle = new Ireg_game_object(654, 0, 0, 0);
02175       spindle->move(t);
02176       gwin->add_dirty(spindle);
02177       npc->set_action(
02178         new Pickup_actor_action(spindle, 250));
02179       }
02180     state = weave_cloth;
02181     break;
02182     }
02183   case weave_cloth:
02184     {
02185     if (spindle)    // Should be held by NPC.
02186       spindle->remove_this();
02187     spindle = 0;
02188     loom = npc->find_closest(261);
02189     if (!loom)    // No loom found?
02190       {
02191       state = get_wool;
02192       break;
02193       }
02194     Tile_coord lpos = loom->get_tile() +
02195             Tile_coord(-1, 0, 0);
02196     Actor_action *pact = Path_walking_actor_action::create_path(
02197         npcpos, lpos, cost);
02198     if (pact)
02199       npc->set_action(new Sequence_actor_action(pact,
02200         new Face_pos_actor_action(loom, 250),
02201         new Object_animate_actor_action(loom,
02202                 4, 200)));
02203     state = get_cloth;
02204     break;
02205     }
02206   case get_cloth:
02207     {
02208     Tile_coord t = Map_chunk::find_spot(loom->get_tile(),
02209               1, 851, 0);
02210     if (t.tx != -1)   // Space to create it?
02211       {
02212       cloth = new Ireg_game_object(851, rand()%2, 0, 0);
02213       cloth->move(t);
02214       gwin->add_dirty(cloth);
02215       npc->set_action(
02216         new Pickup_actor_action(cloth, 250));
02217       }
02218     state = to_work_table;
02219     break;
02220     }
02221   case to_work_table:
02222     {
02223     work_table = npc->find_closest(971);
02224     if (!work_table || !cloth)
02225       {
02226       state = get_wool;
02227       break;
02228       }
02229     Tile_coord tpos = work_table->get_tile() +
02230             Tile_coord(1, -2, 0);
02231     Actor_action *pact = Path_walking_actor_action::create_path(
02232           npcpos, tpos, cost);
02233           // Find where to put cloth.
02234     Rectangle foot = work_table->get_footprint();
02235     Shape_info& info = work_table->get_info();
02236     Tile_coord cpos(foot.x + foot.w/2, foot.y + foot.h/2,
02237       work_table->get_lift() + info.get_3d_height());
02238     if (pact)
02239       npc->set_action(new Sequence_actor_action(pact,
02240         new Face_pos_actor_action(
02241             work_table, 250),
02242         new Pickup_actor_action(cloth, cpos, 250)));
02243     state = set_to_sew;
02244     break;
02245     }
02246   case set_to_sew:
02247     {
02248     Game_object *shears = npc->get_readied(Actor::lhand);
02249     if (shears && shears->get_shapenum() != 698)
02250       {   // Something's not right.
02251       shears->remove_this();
02252       shears = 0;
02253       }
02254     if (!shears)
02255       {   // Shears on table?
02256       Game_object_vector vec;
02257       if (npc->find_nearby(vec, 698, 3, 0))
02258         {
02259         shears = vec[0];
02260         gwin->add_dirty(shears);
02261         shears->remove_this(1);
02262         }
02263       else
02264         shears = new Ireg_game_object(698, 0, 0, 0);
02265       npc->add_readied(shears, Actor::lhand);
02266       }
02267     state = sew_clothes;
02268     sew_clothes_cnt = 0;
02269     break;
02270     }
02271   case sew_clothes:
02272     {
02273     int dir = npc->get_direction(cloth);
02274     signed char frames[5];
02275     int nframes = npc->get_attack_frames(698, false, 
02276                 dir, frames);
02277     npc->set_action(new Frames_actor_action(frames, nframes));
02278     sew_clothes_cnt++;
02279     if (sew_clothes_cnt > 1 && sew_clothes_cnt < 5)
02280       {
02281       int num_cloth_frames = ShapeID(851, 0).get_num_frames();
02282       cloth->change_frame(rand()%num_cloth_frames);
02283       }
02284     else if (sew_clothes_cnt == 5)
02285       {
02286       gwin->add_dirty(cloth);
02287       Tile_coord pos = cloth->get_tile();
02288       cloth->remove_this(1);
02289           // Top or pants.
02290       int shnum = GAME_SI ? 403 : (rand()%2 ? 738 : 249);
02291       cloth->set_shape(shnum);
02292       int nframes = ShapeID(shnum, 0).get_num_frames();
02293       cloth->set_frame(rand()%nframes);
02294       cloth->move(pos);
02295       state = get_clothes;
02296       }
02297     break;
02298     }
02299   case get_clothes:
02300     {
02301     Game_object *shears = npc->get_readied(Actor::lhand);
02302     if (shears) {
02303       Tile_coord pos = cloth->get_tile();
02304       npc->set_action(new Sequence_actor_action(
02305                   new Pickup_actor_action(cloth, 250),
02306                   new Pickup_actor_action(shears, pos, 250)));
02307     } else {
02308       // ++++ maybe create shears? anyway, leaving this till after
02309       // possible/probable schedule system rewrite
02310       npc->set_action(new Pickup_actor_action(cloth, 250));
02311     }
02312     state = display_clothes;
02313     break;
02314     }
02315   case display_clothes:
02316     {
02317     state = done;
02318     wares_table = npc->find_closest(890);
02319     if (!wares_table)
02320       {
02321       cloth->remove_this();
02322       cloth = 0;
02323       break;
02324       }
02325     Tile_coord tpos = wares_table->get_tile() +
02326             Tile_coord(1, -2, 0);
02327     Actor_action *pact = Path_walking_actor_action::create_path(
02328           npcpos, tpos, cost);
02329           // Find where to put cloth.
02330     Rectangle foot = wares_table->get_footprint();
02331     Shape_info& info = wares_table->get_info();
02332     Tile_coord cpos(foot.x + rand()%foot.w, foot.y + rand()%foot.h,
02333       wares_table->get_lift() + info.get_3d_height());
02334     if (pact)
02335       npc->set_action(new Sequence_actor_action(pact,
02336         new Pickup_actor_action(cloth, cpos, 250)));
02337     cloth = 0;      // Leave it be.
02338     break;
02339     }
02340   case done:        // Just put down clothing.
02341     {
02342     state = get_wool;
02343     Game_object_vector vec;   // Don't create too many.
02344     int cnt = 0;
02345     if (GAME_SI)
02346       cnt += npc->find_nearby(vec, 403, 4, 0);
02347     else        // BG shapes.
02348       {
02349       cnt += npc->find_nearby(vec, 738, 4, 0);
02350       cnt += npc->find_nearby(vec, 249, 4, 0);
02351       }
02352     if (cnt > 5)
02353       {
02354       Game_object *obj = vec[rand()%cnt];
02355       gwin->add_dirty(obj);
02356       obj->remove_this();
02357       }
02358     break;
02359     }
02360   default:      // Back to start.
02361     state = get_wool;
02362     break;
02363     }
02364   npc->start(250, 100);   // Back in queue.
02365   }
02366 
02367 /*
02368  *  Sewing schedule is done.
02369  */
02370 
02371 void Sew_schedule::ending
02372   (
02373   int new_type      // New schedule.
02374   )
02375   {
02376           // Remove shears.
02377   Game_object *obj = npc->get_readied(Actor::lhand);
02378   if (obj)
02379     obj->remove_this();
02380   if (cloth)      // Don't leave cloth lying around.
02381     {
02382     if (!cloth->get_owner())
02383       gwin->add_dirty(cloth);
02384     cloth->remove_this();
02385     cloth = 0;
02386     }
02387   }
02388 
02389 
02390 /*
02391  *  Bake bread/pastries
02392  *
02393  * TODO: fix for SI tables... (specifically in Moonglow)
02394  */
02395 
02396 Bake_schedule::Bake_schedule(Actor *n) : Schedule(n),
02397   state(to_flour), oven(0), worktable(0), displaytable(0),
02398   flourbag(0), dough(0), dough_in_oven(0), baked_count(0)
02399   { }
02400 
02401 void Bake_schedule::now_what()
02402 {
02403   Tile_coord npcpos = npc->get_tile();
02404   Actor_pathfinder_client cost(npc, 1);
02405   int delay = 100;
02406 
02407   switch (state) {
02408   case to_flour:
02409   {
02410     Game_object_vector items;
02411     npc->find_nearby(items, npcpos, 863, -1, 0, c_any_qual, 0);
02412     npc->find_nearby(items, npcpos, 863, -1, 0, c_any_qual, 13);
02413     npc->find_nearby(items, npcpos, 863, -1, 0, c_any_qual, 14);
02414 
02415     if (items.empty()) {
02416       state = to_table;
02417       break;
02418     }
02419 
02420     int nr = rand()%items.size();
02421     flourbag = items[nr];
02422 
02423     Tile_coord tpos = flourbag->get_tile();
02424     Actor_action *pact = Path_walking_actor_action::create_path(
02425           npcpos, tpos, cost);
02426     if (pact) {
02427       npc->set_action(pact);
02428     } else {
02429       // just ignore it
02430       state = to_table;
02431       break;
02432     }
02433 
02434     state = get_flour;
02435     break;
02436   }
02437   case get_flour:
02438   {
02439     if (!flourbag) {
02440       // what are we doing here then? back to start
02441       state = to_flour;
02442       break;
02443     }
02444 
02445     int dir = npc->get_direction(flourbag);
02446     npc->change_frame(
02447       npc->get_dir_framenum(dir,Actor::bow_frame));
02448 
02449     if (flourbag->get_framenum() != 0)
02450       flourbag->change_frame(0);
02451 
02452     delay = 750;
02453     state = to_table;
02454     break;
02455   }
02456   case to_table:
02457   {
02458     Game_object *table1 = npc->find_closest(1003);
02459     Game_object *table2 = npc->find_closest(1018);
02460 
02461     if (!table1)
02462       worktable = table2;
02463     else if (!table2)
02464       worktable = table1;
02465     else if (table1->distance(npc) < table2->distance(npc))
02466       worktable = table1;
02467     else
02468       worktable = table2;
02469 
02470     if (!worktable)
02471       worktable = npc->find_closest(1018);
02472     if (!worktable) {
02473       // problem... try again in a few seconds
02474       delay = 2500;
02475       state = to_flour;
02476       break;
02477     }
02478 
02479           // Find where to put dough.
02480     Rectangle foot = worktable->get_footprint();
02481     Shape_info& info = worktable->get_info();
02482     Tile_coord cpos(foot.x + rand()%foot.w, foot.y + rand()%foot.h,
02483       worktable->get_lift() + info.get_3d_height());
02484     Tile_coord tablepos = cpos;
02485     cpos.tz = 0;
02486 
02487     Actor_action *pact = Path_walking_actor_action::create_path(
02488           npcpos, cpos, cost);
02489     if (pact) {
02490       if (dough) {
02491         dough->remove_this();
02492         dough = 0;
02493       }
02494       if (Game::get_game_type() == SERPENT_ISLE)
02495         dough = new Ireg_game_object(863, 16, 0, 0);
02496       else
02497         dough = new Ireg_game_object(658, 0, 0, 0);
02498       npc->set_action(new Sequence_actor_action(pact,
02499         new Pickup_actor_action(dough,tablepos,250)));
02500     } else {
02501       // not good... try again
02502       delay = 2500;
02503       state = to_flour;
02504       break;
02505     }
02506 
02507     state = make_dough;
02508     break;
02509   }
02510   case make_dough:
02511   {
02512     if (!dough) {
02513       // better try again...
02514       delay = 2500;
02515       state = to_table;
02516       break;
02517     }
02518 
02519     int dir = npc->get_direction(dough);
02520     signed char fr[2];
02521     fr[0] = npc->get_dir_framenum(dir, 3);
02522     fr[1] = npc->get_dir_framenum(dir, 0);
02523 
02524     npc->set_action(new Sequence_actor_action(
02525         new Frames_actor_action(fr, 2, 500),
02526     ((Game::get_game_type() == SERPENT_ISLE) ?
02527         new Frames_actor_action((signed char *)"\x11",1,250,dough) :
02528         new Frames_actor_action((signed char *)"\x01",1,250,dough)),
02529         new Frames_actor_action(fr, 2, 500),
02530     ((Game::get_game_type() == SERPENT_ISLE) ?
02531         new Frames_actor_action((signed char *)"\x12",1,250,dough) :
02532         new Frames_actor_action((signed char *)"\x02",1,250,dough))
02533         ));
02534     
02535     state = remove_from_oven;
02536     break;
02537   }
02538   case remove_from_oven:
02539   {
02540     if (!dough_in_oven) {
02541       // nothing in oven yet
02542       state = get_dough;
02543       break;
02544     }
02545 
02546     oven = npc->find_closest(831);
02547     if (!oven) {
02548       // this really shouldn't happen...
02549       dough_in_oven->remove_this();
02550       dough_in_oven = 0;
02551 
02552       delay = 2500;
02553       state = to_table;
02554       break;
02555     }
02556 
02557     gwin->add_dirty(dough_in_oven);
02558     dough_in_oven->set_shape(377);
02559     dough_in_oven->set_frame(rand()%7);
02560     gwin->add_dirty(dough_in_oven);
02561 
02562     Tile_coord tpos = oven->get_tile() + 
02563             Tile_coord(1, 1, 0);
02564     Actor_action *pact = Path_walking_actor_action::create_path(
02565           npcpos, tpos, cost);
02566     if (pact) {
02567       npc->set_action(new Sequence_actor_action(
02568         pact,
02569         new Pickup_actor_action(dough_in_oven, 250)));
02570     } else {
02571       // just pick it up
02572       npc->set_action(
02573         new Pickup_actor_action(dough_in_oven, 250));
02574     }
02575 
02576     state = display_wares;
02577     break;
02578   }
02579   case display_wares:
02580   {
02581     if (!dough_in_oven) {
02582       // try again
02583       delay = 2500;
02584       state = to_flour;
02585       break;
02586     }
02587     displaytable = npc->find_closest(633);
02588     if (!displaytable) {
02589       // uh-oh...
02590       dough_in_oven->remove_this();
02591       dough_in_oven = 0;
02592 
02593       delay = 2500;
02594       state = to_flour;
02595       break;
02596     }
02597 
02598     baked_count++;
02599 
02600     Tile_coord tpos = displaytable->get_tile();
02601     Actor_action *pact = Path_walking_actor_action::create_path(
02602           npcpos, tpos, cost);
02603           // Find where to put cloth.
02604     Rectangle foot = displaytable->get_footprint();
02605     Shape_info& info = displaytable->get_info();
02606     Tile_coord cpos(foot.x + rand()%foot.w, foot.y + rand()%foot.h,
02607       displaytable->get_lift() + info.get_3d_height());
02608     if (pact) {
02609       if (baked_count <= 5) {
02610         npc->set_action(new Sequence_actor_action(pact,
02611           new Pickup_actor_action(dough_in_oven,
02612                cpos, 250)));
02613       } else {
02614         npc->set_action(pact);
02615         dough_in_oven->remove_this();
02616       }
02617       dough_in_oven = 0;
02618     } else {
02619       // just make it vanish
02620       dough_in_oven->remove_this();
02621       dough_in_oven = 0;
02622     }
02623 
02624     state = get_dough;
02625     break;
02626   }
02627   case get_dough:
02628   {
02629     if (!dough) {
02630       // try again
02631       delay = 2500;
02632       state = to_flour;
02633       break;
02634     }
02635 
02636     oven = npc->find_closest(831);
02637     if (!oven) {
02638       // wait a while
02639       delay = 2500;
02640       state = to_flour;
02641       break;
02642     }
02643 
02644     Tile_coord tpos = dough->get_tile();
02645     Actor_action *pact = Path_walking_actor_action::create_path(
02646           npcpos, tpos, cost);
02647     if (pact) {
02648       npc->set_action(new Sequence_actor_action(
02649         pact,
02650         new Pickup_actor_action(dough, 250)));
02651     } else {
02652       // just pick it up
02653       npc->set_action(new Pickup_actor_action(dough, 250));
02654     }
02655 
02656     state = put_in_oven;
02657     break;
02658   }
02659   case put_in_oven:
02660   {
02661     if (!dough) {
02662       // try again
02663       delay = 2500;
02664       state = to_flour;
02665       break;
02666     }
02667 
02668     oven = npc->find_closest(831);
02669     if (!oven) {
02670       // oops... retry
02671       dough->remove_this();
02672       dough = 0;
02673 
02674       delay = 2500;
02675       state = to_table;
02676       break;
02677     }
02678 
02679     Tile_coord tpos = oven->get_tile() + 
02680             Tile_coord(1, 1, 0);
02681     Actor_action *pact = Path_walking_actor_action::create_path(
02682           npcpos, tpos, cost);
02683 
02684     Rectangle foot = oven->get_footprint();
02685     Shape_info& info = oven->get_info();
02686     Tile_coord cpos(foot.x + 1, foot.y,
02687         oven->get_lift() + info.get_3d_height());
02688 
02689     if (pact) {
02690       npc->set_action(new Sequence_actor_action(
02691         pact,
02692         new Pickup_actor_action(dough, cpos, 250)));
02693 
02694       dough_in_oven = dough;
02695       dough = 0;
02696     } else {
02697       dough->remove_this();
02698       dough = 0;
02699     }
02700 
02701     state = to_flour;
02702     break;
02703   }
02704   }
02705   npc->start(250, delay);   // Back in queue.
02706 }
02707 
02708 void Bake_schedule::ending(int new_type)
02709 {
02710   if (dough) {
02711     dough->remove_this();
02712     dough = 0;
02713   }
02714 
02715   if (dough_in_oven) {
02716     dough_in_oven->remove_this();
02717     dough_in_oven = 0;
02718   }
02719 }
02720 
02721 /*
02722  *  Notify that an object is no longer present.
02723  */
02724 void Bake_schedule::notify_object_gone(Game_object *obj)
02725 {
02726   if (obj == dough)   // Someone stole the dough!
02727     dough = 0;
02728   if (obj == dough_in_oven)
02729     dough_in_oven = 0;
02730 }
02731 
02732 
02733 /*
02734  *  Blacksmith.
02735  *
02736  * Note: the original kept the tongs & hammer, and put them on a nearby table
02737  */
02738 
02739 Forge_schedule::Forge_schedule(Actor *n) : Schedule(n), 
02740   state(put_sword_on_firepit), tongs(0), hammer(0), blank(0),
02741   firepit(0), anvil(0), trough(0), bellows(0)
02742   { }
02743 
02744 void Forge_schedule::now_what
02745   (
02746   )
02747   {
02748   Tile_coord npcpos = npc->get_tile();
02749           // Often want to get within 1 tile.
02750   Actor_pathfinder_client cost(npc, 1);
02751 
02752   switch (state) {
02753   case put_sword_on_firepit:
02754   {
02755     if (!blank) {
02756       blank = npc->find_closest(668);
02757       //TODO: go and get it...
02758 
02759     }
02760     if (!blank)
02761       blank = new Ireg_game_object(668, 0, 0, 0);
02762 
02763     firepit = npc->find_closest(739);
02764     if (!firepit) {
02765       // uh-oh... try again in a few seconds
02766       npc->start(250, 2500);
02767       return;
02768     }
02769 
02770     Tile_coord tpos = firepit->get_tile();
02771     Actor_action *pact = Path_walking_actor_action::create_path(
02772         npcpos, tpos, cost);
02773 
02774     Rectangle foot = firepit->get_footprint();
02775     Shape_info& info = firepit->get_info();
02776     Tile_coord bpos(foot.x + foot.w/2 + 1, foot.y + foot.h/2,
02777       firepit->get_lift() + info.get_3d_height());
02778     if (pact) {
02779       npc->set_action(new Sequence_actor_action(pact,
02780         new Pickup_actor_action(blank, bpos, 250)));
02781     } else {
02782       npc->set_action(
02783         new Pickup_actor_action(blank, bpos, 250));
02784     }
02785 
02786     state = use_bellows;
02787     break;
02788   }
02789   case use_bellows:
02790   {
02791     bellows = npc->find_closest(431);
02792     firepit = npc->find_closest(739);
02793     if (!bellows || !firepit || !blank) {
02794       // uh-oh... try again in a few second
02795       npc->start(250, 2500);
02796       state = put_sword_on_firepit;
02797       return;
02798     }
02799 
02800     Tile_coord tpos = bellows->get_tile() +
02801           Tile_coord(3,0,0);
02802     Actor_action *pact = Path_walking_actor_action::create_path(
02803         npcpos, tpos, cost);
02804 
02805     Actor_action **a = new Actor_action*[35];
02806     a[0] = pact;
02807     a[1] = new Face_pos_actor_action(bellows, 250);
02808     a[2] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02809     a[3] = new Object_animate_actor_action(bellows, 3, 1, 300);
02810     a[4] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02811     a[5] = new Frames_actor_action((signed char *)"\x01", 1, 0, firepit);
02812     a[6] = new Frames_actor_action((signed char *)"\x01", 1, 0, blank);
02813     a[7] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02814     a[8] = new Object_animate_actor_action(bellows, 3, 1, 300);
02815     a[9] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02816     a[10] = new Frames_actor_action((signed char *)"\x02", 1, 0, blank);
02817     a[11] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02818     a[12] = new Object_animate_actor_action(bellows, 3, 1, 300);
02819     a[13] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02820     a[14] = new Frames_actor_action((signed char *)"\x02", 1, 0, firepit);
02821     a[15] = new Frames_actor_action((signed char *)"\x03", 1, 0, blank);
02822     a[16] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02823     a[17] = new Object_animate_actor_action(bellows, 3, 1, 300);
02824     a[18] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02825     a[19] = new Frames_actor_action((signed char *)"\x03", 1, 0, firepit);
02826     a[20] = new Frames_actor_action((signed char *)"\x04", 1, 0, blank);
02827     a[21] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02828     a[22] = new Object_animate_actor_action(bellows, 3, 1, 300);
02829     a[23] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02830     a[24] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02831     a[25] = new Object_animate_actor_action(bellows, 3, 1, 300);
02832     a[26] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02833     a[27] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02834     a[28] = new Object_animate_actor_action(bellows, 3, 1, 300);
02835     a[29] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02836     a[30] = new Frames_actor_action((signed char *)"\x2b", 1, 0);
02837     a[31] = new Object_animate_actor_action(bellows, 3, 1, 300);
02838     a[32] = new Frames_actor_action((signed char *)"\x20", 1, 0);
02839     a[33] = new Frames_actor_action((signed char *)"\x00", 1, 0, bellows);
02840     a[34] = 0;
02841 
02842 
02843     npc->set_action(new Sequence_actor_action(a));
02844     state = get_tongs;
02845     break;
02846   }
02847   case get_tongs:
02848   {
02849 #if 0
02850     if (!tongs) {
02851       tongs = npc->find_closest(994);
02852       //TODO: go and get it...
02853     }
02854 #endif
02855     if (!tongs)
02856       tongs = new Ireg_game_object(994, 0, 0, 0);
02857 
02858     npc->add_dirty();
02859     npc->add_readied(tongs, Actor::rhand);
02860     npc->add_dirty();
02861 
02862     state = sword_on_anvil;
02863     break;
02864   }
02865   case sword_on_anvil:
02866   {
02867     anvil = npc->find_closest(991);
02868     firepit = npc->find_closest(739);
02869     if (!anvil || !firepit || !blank) {
02870       // uh-oh... try again in a few second
02871       npc->start(250, 2500);
02872       state = put_sword_on_firepit;
02873       return;
02874     }
02875 
02876     Tile_coord tpos = firepit->get_tile();
02877     Actor_action *pact = Path_walking_actor_action::create_path(
02878         npcpos, tpos, cost);
02879     Tile_coord tpos2 = anvil->get_tile() + 
02880           Tile_coord(0,1,0);
02881     Actor_action *pact2 = Path_walking_actor_action::create_path(
02882         tpos, tpos2, cost);
02883 
02884     Rectangle foot = anvil->get_footprint();
02885     Shape_info& info = anvil->get_info();
02886     Tile_coord bpos(foot.x + 2, foot.y,
02887       anvil->get_lift() + info.get_3d_height());
02888     if (pact && pact2) {
02889       npc->set_action(new Sequence_actor_action(pact,
02890         new Pickup_actor_action(blank, 250),
02891         pact2,
02892         new Pickup_actor_action(blank, bpos, 250)));
02893     } else {
02894       npc->set_action(new Sequence_actor_action(
02895         new Pickup_actor_action(blank, 250),
02896         new Pickup_actor_action(blank, bpos, 250)));
02897     }
02898     state = get_hammer;
02899     break;
02900   }
02901   case get_hammer:
02902   {
02903 #if 0
02904     if (!hammer) {
02905       hammer = npc->find_closest(623);
02906       //TODO: go and get it...
02907     }
02908 #endif
02909 
02910     if (!hammer)
02911       hammer = new Ireg_game_object(623, 0, 0, 0);
02912 
02913     npc->add_dirty();
02914     if (tongs) {
02915       tongs->remove_this();
02916       tongs = 0;
02917     }
02918     npc->add_readied(hammer, Actor::rhand);
02919     npc->add_dirty();
02920 
02921     state = use_hammer;
02922     break;
02923   }
02924   case use_hammer:
02925   {
02926     anvil = npc->find_closest(991);
02927     firepit = npc->find_closest(739);
02928     if (!anvil || !firepit || !blank) {
02929       // uh-oh... try again in a few seconds
02930       npc->start(250, 2500);
02931       state = put_sword_on_firepit;
02932       return;
02933     }
02934 
02935     signed char frames[12];
02936     int cnt = npc->get_attack_frames(623, false, 0, frames);
02937     npc->set_action(new Frames_actor_action(frames, cnt));
02938     
02939     Actor_action **a = new Actor_action*[10];
02940     a[0] = new Frames_actor_action(frames, cnt);
02941     a[1] = new Frames_actor_action((signed char *)"\x03", 1, 0, blank);
02942     a[2] = new Frames_actor_action((signed char *)"\x02", 1, 0, firepit);
02943     a[3] = new Frames_actor_action(frames, cnt);
02944     a[4] = new Frames_actor_action((signed char *)"\x02", 1, 0, blank);
02945     a[5] = new Frames_actor_action((signed char *)"\x01", 1, 0, firepit);
02946     a[6] = new Frames_actor_action(frames, cnt);
02947     a[7] = new Frames_actor_action((signed char *)"\x01", 1, 0, blank);
02948     a[8] = new Frames_actor_action((signed char *)"\x00", 1, 0, firepit);
02949     a[9] = 0;
02950     npc->set_action(new Sequence_actor_action(a));
02951 
02952     state = walk_to_trough;
02953     break;
02954   }
02955   case walk_to_trough:
02956   {
02957     npc->add_dirty();
02958     if (hammer) {
02959       hammer->remove_this();
02960       hammer = 0;
02961     }
02962     npc->add_dirty();
02963 
02964     trough = npc->find_closest(719);
02965     if (!trough) {
02966       // uh-oh... try again in a few seconds
02967       npc->start(250, 2500);
02968       state = put_sword_on_firepit;
02969       return;
02970     }
02971 
02972     if (trough->get_framenum() == 0) {
02973       Tile_coord tpos = trough->get_tile() +
02974             Tile_coord(0,2,0);
02975       Actor_action *pact = Path_walking_actor_action::
02976 					create_path(npcpos, tpos, cost);
02977       npc->set_action(pact);
02978       state = fill_trough;
02979       break;
02980     }
02981     
02982     state = get_tongs2;
02983     break;
02984   }
02985   case fill_trough:
02986   {
02987     trough = npc->find_closest(719);
02988     if (!trough) {
02989       // uh-oh... try again in a few seconds
02990       npc->start(250, 2500);
02991       state = put_sword_on_firepit;
02992       return;
02993     }
02994 
02995     int dir = npc->get_direction(trough);
02996     trough->change_frame(3);
02997     npc->change_frame(
02998       npc->get_dir_framenum(dir, Actor::bow_frame));
02999 
03000     state = get_tongs2;
03001     break;
03002   }
03003   case get_tongs2:
03004   {
03005 #if 0
03006     if (!tongs) {
03007       tongs = npc->find_closest(994);
03008       //TODO: go and get it...
03009     }
03010 #endif
03011     if (!tongs)
03012       tongs = new Ireg_game_object(994, 0, 0, 0);
03013 
03014     npc->add_dirty();
03015     npc->add_readied(tongs, Actor::rhand);
03016     npc->add_dirty();
03017 
03018     state = use_trough;
03019     break;
03020   }
03021   case use_trough:
03022   {
03023     trough = npc->find_closest(719);
03024     anvil = npc->find_closest(991);
03025     if (!trough || !anvil || !blank) {
03026       // uh-oh... try again in a few seconds
03027       npc->start(250, 2500);
03028       state = put_sword_on_firepit;
03029       return;
03030     }
03031     
03032     Tile_coord tpos = anvil->get_tile() +
03033         Tile_coord(0,1,0);
03034     Actor_action *pact = Path_walking_actor_action::
03035 				create_path(npcpos, tpos, cost);
03036 
03037     Tile_coord tpos2 = trough->get_tile() +
03038         Tile_coord(0,2,0);
03039     Actor_action *pact2 = Path_walking_actor_action::
03040 				create_path(tpos, tpos2, cost);
03041 
03042     if (pact && pact2) {
03043       signed char troughframe = trough->get_framenum() - 1;
03044       if (troughframe < 0) troughframe = 0;
03045 
03046       int dir = npc->get_direction(trough);
03047       signed char npcframe = npc->get_dir_framenum(dir, Actor::bow_frame);
03048 
03049       Actor_action **a = new Actor_action*[7];
03050       a[0] = pact;
03051       a[1] = new Pickup_actor_action(blank, 250);
03052       a[2] = pact2;
03053       a[3] = new Frames_actor_action(&npcframe, 1, 250);
03054       a[4] = new Frames_actor_action(&troughframe, 1, 0, trough);
03055       a[5] = new Frames_actor_action((signed char *)"\x00", 1, 0, blank);
03056       a[6] = 0;
03057       npc->set_action(new Sequence_actor_action(a));
03058     } else {
03059       // no path found, just pick up sword blank
03060       npc->set_action(new Sequence_actor_action(
03061         new Pickup_actor_action(blank, 250),
03062         new Frames_actor_action((signed char *)"\0", 1, 0, blank)));
03063     } 
03064 
03065     state = done;
03066     break;
03067   }
03068   case done:
03069   {
03070     npc->add_dirty();
03071     if (tongs) {
03072       tongs->remove_this();
03073       tongs = 0;
03074     }
03075     npc->add_dirty();
03076     
03077 
03078     state = put_sword_on_firepit;
03079   }
03080   }
03081 
03082   npc->start(250, 100);   // Back in queue.
03083   }
03084 
03085 /*
03086  *  Forge schedule is done.
03087  */
03088 
03089 void Forge_schedule::ending
03090   (
03091   int new_type      // New schedule.
03092   )
03093   {
03094           // Remove any tools.
03095   if (tongs) {
03096     tongs->remove_this();
03097     tongs = 0;
03098   }
03099   if (hammer) {
03100     hammer->remove_this();
03101     hammer = 0;
03102   }
03103 
03104   firepit = npc->find_closest(739);
03105   bellows = npc->find_closest(431);
03106 
03107   if (firepit && firepit->get_framenum() != 0)
03108     firepit->change_frame(0);
03109   if (bellows && bellows->get_framenum() != 0)
03110     bellows->change_frame(0);
03111   if (blank && blank->get_framenum() != 0)
03112     blank->change_frame(0);
03113 
03114   }
03115 
03116 
03117 /*
03118  *  Modify goal to walk off the screen.
03119  */
03120 
03121 void Walk_to_schedule::walk_off_screen
03122   (
03123   Rectangle& screen,    // In tiles, area slightly larger than
03124           //   actual screen.
03125   Tile_coord& goal    // Modified for path offscreen.
03126   )
03127   {
03128           // Destination.
03129   if (goal.tx >= screen.x + screen.w)
03130     {
03131     goal.tx = screen.x + screen.w - 1;
03132     goal.ty = -1;
03133     }
03134   else if (goal.tx < screen.x)
03135     {
03136     goal.tx = screen.x;
03137     goal.ty = -1;
03138     }
03139   else if (goal.ty >= screen.y + screen.h)
03140     {
03141     goal.ty = screen.y + screen.h - 1;
03142     goal.tx = -1;
03143     }
03144   else if (goal.ty < screen.y)
03145     {
03146     goal.ty = screen.y;
03147     goal.tx = -1;
03148     }
03149   }
03150 
03151 /*
03152  *  Create schedule for walking to the next schedule's destination.
03153  */
03154 
03155 Walk_to_schedule::Walk_to_schedule
03156   (
03157   Actor *n,
03158   Tile_coord d,     // Destination.
03159   int new_sched,      // Schedule when we get there.
03160   int delay     // Msecs, or -1 for random delay.
03161   ) : Schedule(n), dest(d), new_schedule(new_sched), retries(0), legs(0)
03162   {
03163           // Delay 0-20 secs.
03164   first_delay = delay >= 0 ? delay : 2*(rand()%10000);
03165   }
03166 
03167 /*
03168  *  Schedule change for walking to another schedule destination.
03169  */
03170 
03171 void Walk_to_schedule::now_what
03172   (
03173   )
03174   {
03175   if (npc->get_tile().distance(dest) <= 3)
03176     {     // Close enough!
03177     npc->set_schedule_type(new_schedule);
03178     return;
03179     }
03180   if (legs >= 40 || retries >= 2) // Trying too hard?  (Following
03181             //   Patterson takes about 30.)
03182     {     // Going to jump there.
03183     npc->move(dest.tx, dest.ty, dest.tz);
03184     npc->set_schedule_type(new_schedule);
03185     return;
03186     }
03187           // Get screen rect. in tiles.
03188   Rectangle screen = gwin->get_win_tile_rect();
03189   screen.enlarge(6);    // Enlarge in all dirs.
03190           // Might do part of it first.
03191   Tile_coord from = npc->get_tile(),
03192        to = dest;
03193           // Destination off the screen?
03194   if (!screen.has_point(to.tx, to.ty))
03195     {
03196     if (!screen.has_point(from.tx, from.ty))
03197       {   // Force teleport on next tick.
03198       retries = 100;
03199       npc->start(200, 100);
03200       return;
03201       }
03202           // Don't walk off screen if close, or
03203           //   if lots of legs, indicating that
03204           //   Avatar is following this NPC.
03205     if (from.distance(to) > 80 || legs < 10)
03206           // Modify 'dest'. to walk off.
03207       walk_off_screen(screen, to);
03208     }
03209   else if (!screen.has_point(from.tx, from.ty))
03210           // Modify src. to walk from off-screen.
03211     walk_off_screen(screen, from);
03212   blocked = Tile_coord(-1, -1, -1);
03213   cout << "Finding path to schedule for " << npc->get_name() << endl;
03214           // Create path to dest., delaying
03215           //   0 to 1 seconds.
03216   if (!npc->walk_path_to_tile(from, to, gwin->get_std_delay(),
03217             first_delay + rand()%1000))
03218     {     // Wait 1 sec., then try again.
03219 #ifdef DEBUG
03220     cout << "Failed to find path for " << npc->get_name() << endl;
03221 #endif
03222     npc->walk_to_tile(dest, gwin->get_std_delay(), 1000);
03223     retries++;    // Failed.  Try again next tick.
03224     }
03225   else        // Okay.  He's walking there.
03226     {
03227     legs++;
03228     retries = 0;
03229     }
03230   first_delay = 0;
03231   }
03232 
03233 /*
03234  *  Don't go dormant when walking to a schedule.
03235  */
03236 
03237 void Walk_to_schedule::im_dormant
03238   (
03239   )
03240   {
03241   Walk_to_schedule::now_what(); // Get there by any means.
03242   }
03243 
03244 /*
03245  *  Get actual schedule (for Usecode intrinsic).
03246  */
03247 
03248 int Walk_to_schedule::get_actual_type
03249   (
03250   Actor * /* npc */
03251   )
03252   {
03253   return new_schedule;
03254   }
03255 
03256 /*
03257  *  Set a schedule.
03258  */
03259 
03260 void Schedule_change::set
03261   (
03262   unsigned char *entry    // 4 bytes read from schedule.dat.
03263   )
03264   {
03265   time = entry[0]&7;
03266   type = entry[0]>>3;
03267   x = entry[1];
03268   y = entry[2];
03269   superchunk = entry[3];
03270   }
03271 
03272 /*
03273  *  Get a schedule.
03274  */
03275 
03276 void Schedule_change::get
03277   (
03278   unsigned char *entry    // 4 bytes to write to schedule.dat.
03279   )
03280   {
03281   entry[0] = time&7;
03282   entry[0] |= (type&31) << 3;
03283   entry[1] = x;
03284   entry[2] = y;
03285   entry[3] = superchunk;
03286   }
03287 
03288 /*
03289  *  Set a schedule.
03290  */
03291 
03292 void Schedule_change::set
03293   (
03294   int ax,
03295   int ay,
03296   unsigned char stype,
03297   unsigned char stime
03298   )
03299   {
03300   time = stime;
03301   type = stype;
03302   x = static_cast<unsigned char>(ax%c_tiles_per_schunk);
03303   y = static_cast<unsigned char>(ay%c_tiles_per_schunk);
03304   superchunk = static_cast<unsigned char>((ay/c_tiles_per_schunk)*12 + (ax/c_tiles_per_schunk));
03305   }
03306 
03307 /*
03308  *  Get position.
03309  */
03310 
03311 Tile_coord Schedule_change::get_pos() const
03312   {
03313   int cx = 16*(superchunk%12) + x/16,
03314       cy = 16*(superchunk/12) + y/16,
03315       tx = x%16,
03316       ty = y%16;
03317   return Tile_coord(cx*c_tiles_per_chunk + tx, cy*c_tiles_per_chunk + ty, 0);
03318   }
03319 
03320 
03321 

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