gamewin.cc

Go to the documentation of this file.
00001 
00007 /*
00008  *
00009  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00010  *  Copyright (C) 2000-2002  The Exult Team
00011  *
00012  *  This program is free software; you can redistribute it and/or modify
00013  *  it under the terms of the GNU General Public License as published by
00014  *  the Free Software Foundation; either version 2 of the License, or
00015  *  (at your option) any later version.
00016  *
00017  *  This program is distributed in the hope that it will be useful,
00018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  *  GNU General Public License for more details.
00021  *
00022  *  You should have received a copy of the GNU General Public License
00023  *  along with this program; if not, write to the Free Software
00024  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00025  */
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #  include <config.h>
00029 #endif
00030 
00031 #ifndef ALPHA_LINUX_CXX
00032 #  include <cstdlib>
00033 #  include <cstring>
00034 #  include <cstdarg>
00035 #  include <cstdio>
00036 #endif
00037 
00038 #include "Astar.h"
00039 #include "Audio.h"
00040 #include "Configuration.h"
00041 #include "Face_stats.h"
00042 #include "Flex.h"
00043 #include "Gump.h"
00044 #include "Gump_manager.h"
00045 #include "actions.h"
00046 #include "monsters.h"
00047 #include "animate.h"
00048 #include "barge.h"
00049 #include "bodies.h"
00050 #include "cheat.h"
00051 #include "chunks.h"
00052 #include "chunkter.h"
00053 #include "combat_opts.h"
00054 #include "delobjs.h"
00055 #include "dir.h"
00056 #include "effects.h"
00057 #include "egg.h"
00058 #include "exult.h"
00059 #include "files/U7file.h"
00060 #include "flic/playfli.h"
00061 #include "fnames.h"
00062 #include "game.h"
00063 #include "gamewin.h"
00064 #include "gamemap.h"
00065 #include "gameclk.h"
00066 #include "gamerend.h"
00067 #include "items.h"
00068 #include "jawbone.h"
00069 #include "keys.h"
00070 #include "mouse.h"
00071 #include "npcnear.h"
00072 #include "objiter.h"
00073 #include "paths.h"
00074 #include "schedule.h"
00075 #include "spellbook.h"
00076 #include "ucmachine.h"
00077 #include "ucsched.h"      /* Only used to flush objects. */
00078 #include "utils.h"
00079 #include "virstone.h"
00080 #include "mappatch.h"
00081 #include "version.h"
00082 #include "drag.h"
00083 #include "glshape.h"
00084 #include "party.h"
00085 
00086 #ifdef USE_EXULTSTUDIO
00087 #include "server.h"
00088 #include "servemsg.h"
00089 #include "objserial.h"
00090 #endif
00091 
00092 #ifndef UNDER_CE
00093 using std::cerr;
00094 using std::cout;
00095 using std::endl;
00096 using std::istream;
00097 using std::ifstream;
00098 using std::ios;
00099 using std::memcpy;
00100 using std::memset;
00101 using std::ofstream;
00102 using std::rand;
00103 using std::strcmp;
00104 using std::strcpy;
00105 using std::string;
00106 using std::strlen;
00107 using std::srand;
00108 using std::vector;
00109 #endif
00110 
00111           // THE game window:
00112 Game_window *Game_window::game_window = 0;
00113 
00114 /*
00115  *  Provide chirping birds.
00116  */
00117 class Background_noise : public Time_sensitive
00118   {
00119   int repeats;      // Repeats in quick succession.
00120   int last_sound;     // # of last sound played.
00121   Game_window *gwin;
00122   int laststate;      // Last state for SFX music tracks, 
00123               // 1 outside, 2 dungeon, 3 nighttime
00124 
00125 public:
00126   Background_noise(Game_window *gw) : repeats(0), last_sound(-1),
00127           gwin(gw), laststate(0)
00128     { gwin->get_tqueue()->add(5000, this, 0L); }
00129   virtual ~Background_noise()
00130     { gwin->get_tqueue()->remove(this); }
00131   virtual void handle_event(unsigned long curtime, long udata);
00132   };
00133 
00134 /*
00135  *  Play background sound.
00136  */
00137 
00138 void Background_noise::handle_event
00139   (
00140   unsigned long curtime,
00141   long udata
00142   )
00143   {
00144   Main_actor *ava = gwin->get_main_actor();
00145   unsigned long delay = 8000;
00146   int currentstate = 0;
00147 
00148 #ifndef COLOURLESS_REALLY_HATES_THE_BG_SFX
00149 
00150   int bghour = gwin->get_clock()->get_hour();
00151   if(gwin->is_in_dungeon())
00152     currentstate = 2;
00153   else
00154     if (bghour < 6 || bghour > 20)
00155       currentstate = 3; //Night time
00156     else
00157       currentstate = 1;
00158 
00159   MyMidiPlayer *player = Audio::get_ptr()->get_midi();
00160   if (player && player->get_output_driver_type() == MIDI_DRIVER_OGG)
00161   {
00162     delay = 1500; //Quickly get back to this function check
00163     //We've got OGG so play the background SFX tracks
00164 
00165     int curr_track = player->get_current_track();
00166 
00167     if(curr_track == 7 && currentstate == 3)
00168     {
00169       //Play the cricket sounds at night 
00170       delay = 3000 + rand()%5000;
00171       Audio::get_ptr()->play_sound_effect(61, MIX_MAX_VOLUME - 30);
00172     }
00173 
00174     if((curr_track == -1 || laststate != currentstate ) && Audio::get_ptr()->is_music_enabled())
00175     {
00176       if(curr_track == -1 || (curr_track >=4 && curr_track <=8) || curr_track == 52)    //Not already playing music
00177       {
00178         int tracknum=255;
00179 
00180         //Get the relevant track number. SI tracks are converted back to BG ones later on
00181         if(gwin->is_in_dungeon())
00182         {
00183           //Start the SFX music track then
00184           if(Game::get_game_type() == BLACK_GATE)
00185             tracknum = 52;
00186           else
00187             tracknum = 42;  
00188           laststate = 2;
00189         }
00190         else        
00191         {
00192           if (bghour < 6 || bghour > 20)
00193           {
00194             if(Game::get_game_type() == BLACK_GATE)         
00195               tracknum = 7;
00196             else
00197               tracknum = 66;
00198             laststate = 3;
00199           }
00200           else
00201           {
00202             //Start the SFX music track then
00203             if(Game::get_game_type() == BLACK_GATE)
00204               tracknum = 6;
00205             else
00206               tracknum = 67;
00207             laststate = 1;
00208           }
00209         }
00210         Audio::get_ptr()->start_music(tracknum,1);
00211       }
00212     }
00213   }
00214   else
00215   {
00216     
00217     //Tests to see if track is playing the SFX tracks, possible 
00218     //when the game has been restored
00219     //and the Audio option was changed from OGG to something else
00220     if(player && player->get_current_track() >=4 && 
00221        player->get_current_track() <= 8)
00222       player->stop_music();
00223 
00224 
00225     //Not OGG so play the SFX sounds manually
00226           // Only if outside.
00227     if (ava && !gwin->is_main_actor_inside()  &&
00228       // +++++SI SFX's don't sound right.
00229       Game::get_game_type() == BLACK_GATE )
00230     {
00231       int sound;    // BG SFX #.
00232       static unsigned char bgnight[] = {61, 61, 255},
00233              bgday[] = {82, 85, 85};
00234       if (repeats > 0)  // Repeating?
00235         sound = last_sound;
00236       else
00237       {
00238         int hour = gwin->get_clock()->get_hour();
00239         if (hour < 6 || hour > 20)
00240           sound = bgnight[rand()%sizeof(bgnight)];
00241         else
00242           sound = bgday[rand()%sizeof(bgday)];
00243             // Translate BG to SI #'s.
00244         sound = Audio::game_sfx(sound);
00245         last_sound = sound;
00246       }
00247       Audio::get_ptr()->play_sound_effect(sound);
00248       repeats++;    // Count it.
00249       if (rand()%(repeats + 1) == 0)
00250             // Repeat.
00251         delay = 500 + rand()%1000;
00252       else
00253       {
00254         delay = 4000 + rand()%3000;
00255         repeats = 0;
00256       }
00257     }
00258   }
00259 #endif
00260 
00261   gwin->get_tqueue()->add(curtime + delay, this, udata);
00262 }
00263 
00264 /*
00265  *  Set renderer (OpenGL or normal SDL).
00266  */
00267 
00268 static void Set_renderer
00269   (
00270   Image_window8 *win,
00271   Palette *pal
00272   )
00273   {
00274   GL_manager *glman = GL_manager::get_instance();
00275 #ifdef HAVE_OPENGL
00276   delete glman;
00277   glman = 0;
00278   if (win->get_scaler() == Image_window::OpenGL)
00279     {
00280     glman = new GL_manager();
00281           // +++++Hope this is okay to do:
00282     pal->load(PALETTES_FLX, 0); // Main palette.
00283           // This should be elsewhere, I think:
00284     unsigned char *newpal = new unsigned char[768];
00285     for (int i = 0; i < 256; i++)
00286       {   // Palette colors are 0-63.
00287       newpal[3*i] = 4*pal->get_red(i);
00288       newpal[3*i+1] = 4*pal->get_green(i);
00289       newpal[3*i+2] = 4*pal->get_blue(i);
00290       }
00291     glman->set_palette(newpal);
00292     glman->resized(win->get_width(), win->get_height(), 
00293               win->get_scale());
00294     }
00295 #endif
00296           // Tell shapes how to render.
00297   Shape_frame::set_to_render(win->get_ib8(), glman);
00298   }
00299 
00300 /*
00301  *  Create game window.
00302  */
00303 
00304 Game_window::Game_window
00305   (
00306   int width, int height, int scale, int scaler    // Window dimensions.
00307   ) : 
00308       win(0), map(new Game_map()), pal(0),
00309       usecode(0), combat(false), armageddon(false), 
00310       walk_in_formation(false),
00311             tqueue(new Time_queue()), time_stopped(0),
00312       std_delay(c_std_delay),
00313       npc_prox(new Npc_proximity_handler(this)),
00314       effects(new Effects_manager(this)), 
00315       gump_man(new Gump_manager), render(new Game_render),
00316       party_man(new Party_manager),
00317       painted(false), focus(true), 
00318       in_dungeon(0), ice_dungeon(false),
00319       moving_barge(0), main_actor(0), skip_above_actor(31),
00320       npcs(0), bodies(0), mouse3rd(false), fastmouse(false),
00321             text_bg(false), step_tile_delta(8), allow_double_right_move(true),
00322       special_light(0),
00323       dragging(0),
00324       theft_warnings(0), theft_cx(255), theft_cy(255),
00325       background_noise(new Background_noise(this)),
00326       double_click_closes_gumps(false),
00327       removed(new Deleted_objects()), 
00328       skip_lift(16), paint_eggs(false), debug(0), camera_actor(0)
00329 #ifdef RED_PLASMA
00330       ,load_palette_timer(0), plasma_start_color(0), plasma_cycle_range(0)
00331 #endif
00332   {
00333   game_window = this;   // Set static ->.
00334   clock = new Game_clock(tqueue);
00335   shape_man = new Shape_manager();// Create the single instance.
00336           // Create window.
00337   string  fullscreenstr;    // Check config. for fullscreen mode.
00338   config->value("config/video/fullscreen",fullscreenstr,"no");
00339   bool  fullscreen = (fullscreenstr=="yes");
00340   config->set("config/video/fullscreen",fullscreenstr,true);
00341   win = new Image_window8(width, height, scale, fullscreen, scaler);
00342   win->set_title("Exult Ultima7 Engine");
00343   pal = new Palette();
00344   Game_singletons::init(this);  // Everything but 'usecode' exists.
00345   Set_renderer(win, pal);
00346 
00347   string str;
00348   config->value("config/gameplay/textbackground", text_bg, -1);
00349   config->value("config/gameplay/mouse3rd", str, "no");
00350   if (str == "yes")
00351     mouse3rd = true;
00352   config->set("config/gameplay/mouse3rd", str, true);
00353   config->value("config/gameplay/fastmouse", str, "no");
00354   if (str == "yes")
00355     fastmouse = true;
00356   config->set("config/gameplay/fastmouse", str, true);
00357   config->value("config/gameplay/double_click_closes_gumps", str, "no");
00358   if (str == "yes")
00359     double_click_closes_gumps = true;
00360   config->set("config/gameplay/double_click_closes_gumps", str, true);
00361   config->value("config/gameplay/combat/difficulty",
00362               Combat::difficulty, 0);
00363   config->set("config/gameplay/combat/difficulty",
00364             Combat::difficulty, true);
00365   config->value("config/gameplay/combat/mode", str, "original");
00366   if (str == "keypause")
00367     Combat::mode = Combat::keypause;
00368   else
00369     Combat::mode = Combat::original;
00370   config->set("config/gameplay/combat/mode", str, true);
00371   config->value("config/gameplay/combat/show_hits", str, "no");
00372   Combat::show_hits = (str == "yes");
00373   config->set("config/gameplay/combat/show_hits", str, true);
00374   config->value("config/audio/disablepause", str, "no");
00375   config->set("config/audio/disablepause", str, true);
00376 
00377   config->value("config/gameplay/step_tile_delta", step_tile_delta, 8);
00378   if (step_tile_delta < 1) step_tile_delta = 1;
00379   config->set("config/gameplay/step_tile_delta", step_tile_delta, true);
00380 
00381   config->value("config/gameplay/allow_double_right_move", str, "yes");
00382   allow_double_right_move = str == "yes";
00383   config->set("config/gameplay/allow_double_right_move", 
00384         allow_double_right_move?"yes":"no", true);
00385           // New 'formation' walking?
00386   config->value("config/gameplay/formation", str, "yes");
00387 //+++++ Everybody gets to test:-) walk_in_formation = (str == "yes");
00388   walk_in_formation = true; // Testing time++++++++++.
00389   config->set("config/gameplay/formation", walk_in_formation?"yes":"no",
00390                 true);
00391   }
00392 
00393 /*
00394  *  Blank out screen.
00395  */
00396 void Game_window::clear_screen(bool update)
00397 {
00398   win->fill8(0,get_width(),get_height(),0,0);
00399 
00400   // update screen
00401   if (update)
00402     show(1);
00403 }
00404 
00405 
00406 
00407 /*
00408  *  Deleting game window.
00409  */
00410 
00411 Game_window::~Game_window
00412   (
00413   )
00414   {
00415       gump_man->close_all_gumps(true);
00416   clear_world();      // Delete all objects, chunks.
00417   int i;  // Blame MSVC
00418   for (i = 0; i < sizeof(save_names)/sizeof(save_names[0]); i++)
00419     delete [] save_names[i];
00420   delete shape_man;
00421   delete gump_man;
00422   delete party_man;
00423   delete background_noise;
00424   delete tqueue;
00425   delete win;
00426   delete dragging;
00427   delete pal;
00428   delete map;
00429   delete usecode;
00430   delete removed;
00431   delete clock;
00432   }
00433 
00434 /*
00435  *  Abort.
00436  */
00437 
00438 void Game_window::abort
00439   (
00440   const char *msg,
00441   ...
00442   )
00443   {
00444   std::va_list ap;
00445   va_start(ap, msg);
00446   char buf[512];
00447   vsprintf(buf, msg, ap);   // Format the message.
00448   cerr << "Exult (fatal): " << buf << endl;
00449   delete this;
00450   throw quit_exception(-1);
00451   }
00452 
00453 #if 0
00454 #define BLEND(alpha, newc, oldc) ((newc*255L) - (oldc*(255L-alpha)))/alpha
00455 void Analyze_xform(unsigned char *xform, int alpha, Palette *pal)
00456   {
00457   long br = 0, bg = 0, bb = 0;  // Trying to figure out blend color.
00458   for (int i = 0; i < 256; i++)
00459     {
00460     int xi = xform[i];  // Index of color mapped to.
00461     br += BLEND(alpha, pal->get_red(xi), pal->get_red(i));
00462     bg += BLEND(alpha, pal->get_green(xi), pal->get_green(i));
00463     bb += BLEND(alpha, pal->get_blue(xi), pal->get_blue(i));
00464     }
00465   br /= 256;      // Take average.
00466   bg /= 256;
00467   bb /= 256;
00468   cout << "Blend (r,g,b) = " << br << ',' << bg << ',' << bb << endl;
00469   }
00470 #endif
00471 
00472 
00473 void Game_window::init_files(bool cycle)
00474 {
00475   
00476 #ifdef RED_PLASMA
00477   // Display red plasma during load...
00478   if (cycle)
00479     setup_load_palette();
00480 #endif
00481 
00482   usecode = Usecode_machine::create();
00483   Game_singletons::init(this);  // Everything should exist here.
00484 
00485   CYCLE_RED_PLASMA();
00486   shape_man->load();    // All the .vga files!
00487   CYCLE_RED_PLASMA();
00488 
00489   ifstream textflx, exultmsg; 
00490   if (is_system_path_defined("<PATCH>") && U7exists(PATCH_TEXT))
00491     U7open(textflx, PATCH_TEXT);
00492   else
00493       U7open(textflx, TEXT_FLX);
00494   if (U7exists(EXULTMSG))
00495     U7open(exultmsg, EXULTMSG, true);
00496   Setup_item_names(textflx, exultmsg);  // Set up list of item names.
00497   unsigned long timer = SDL_GetTicks();
00498   srand(timer);     // Use time to seed rand. generator.
00499           // Force clock to start.
00500   tqueue->add(timer, clock, reinterpret_cast<long>(this));
00501           // Go to starting chunk
00502   scrolltx = game->get_start_tile_x();
00503   scrollty = game->get_start_tile_y();
00504     
00505   // initialize keybinder
00506   if (keybinder)
00507     delete keybinder;
00508   keybinder = new KeyBinder();
00509 
00510   std::string d, keyfilename;
00511   d = "config/disk/game/"+Game::get_gametitle()+"/keys";
00512   config->value(d.c_str(),keyfilename,"(default)");
00513   if (keyfilename == "(default)") {
00514     config->set(d.c_str(), keyfilename, true);
00515     keybinder->LoadDefaults();
00516   } else {
00517     keybinder->LoadFromFile(keyfilename.c_str());
00518   }
00519 
00520   CYCLE_RED_PLASMA();
00521   // initialize .wav SFX pack
00522 
00523 //This is now run from exult.cc
00524 //  Audio::get_ptr()->Init_sfx();
00525   int fps;      // Init. animation speed.
00526   config->value("config/video/fps", fps, 5);
00527   if (fps <= 0)
00528     fps = 5;
00529   std_delay = 1000/fps;   // Convert to msecs. between frames.
00530 }
00531 
00532 /*
00533  *  Get map patch list.
00534  */
00535 Map_patch_collection *Game_window::get_map_patches()
00536 {
00537   return map->get_map_patches();
00538 }
00539 
00540 /*
00541  *  Set/unset barge mode.
00542  */
00543 
00544 void Game_window::set_moving_barge
00545   (
00546   Barge_object *b
00547   )
00548   {
00549   if (b && b != moving_barge)
00550     b->gather();    // Gather up all objects on it.
00551   else if (!b && moving_barge)
00552     moving_barge->done(); // No longer 'barging'.
00553   moving_barge = b;
00554   }
00555 
00556 /*
00557  *  Is character moving?
00558  */
00559 
00560 bool Game_window::is_moving
00561   (
00562   )
00563   {
00564   return moving_barge ? moving_barge->is_moving()
00565           : main_actor->is_moving();
00566   }
00567 
00568 /*
00569  *  Are we in dont_move mode?
00570  */
00571 
00572 bool Game_window::main_actor_dont_move()
00573     { 
00574     return main_actor->get_flag(Obj_flags::dont_move) != 0;
00575     }
00576 
00577 /*
00578  *  Add time for a light spell.
00579  */
00580 
00581 void Game_window::add_special_light
00582   (
00583   int units     // Light=500, GreatLight=5000.
00584   )
00585   {
00586   if (!special_light)   // Nothing in effect now?
00587     {
00588     special_light = clock->get_total_minutes();
00589     clock->set_palette();
00590     }
00591   special_light += units/20;  // Figure ending time.
00592   }
00593 
00594 /*
00595  *  Set 'stop time' value.
00596  */
00597 
00598 void Game_window::set_time_stopped
00599   (
00600   long delay      // Delay in ticks (1/1000 secs.),
00601           //   -1 to stop indefinitely, or 0
00602           //   to end.
00603   )
00604   {
00605   if (delay == -1)
00606     time_stopped = -1;
00607   else if (!delay)
00608     time_stopped = 0;
00609   else
00610     {
00611     long new_expire = Game::get_ticks() + delay;
00612     if (new_expire > time_stopped)  // Set expiration time.
00613       time_stopped = new_expire;
00614     }
00615   }
00616 
00617 /*
00618  *  Return delay to expiration (or 3000 if indefinite).
00619  */
00620 
00621 long Game_window::check_time_stopped
00622   (
00623   )
00624   {
00625   if (time_stopped == -1)
00626     return 3000;
00627   long delay = time_stopped - Game::get_ticks();
00628   if (delay > 0)
00629     return delay;
00630   time_stopped = 0;   // Done.
00631   return 0;
00632   }
00633 
00634 /*
00635  *  Toggle combat mode.
00636  */
00637 
00638 void Game_window::toggle_combat
00639   (
00640   )
00641   { 
00642   combat = !combat;
00643           // Change party member's schedules.
00644   int newsched = combat ? Schedule::combat : Schedule::follow_avatar;
00645   int cnt = party_man->get_count();
00646   for (int i = 0; i < cnt; i++)
00647     {
00648     int party_member=party_man->get_member(i);
00649     Actor *person=get_npc(party_member);
00650     if (!person)
00651       continue;
00652     int sched = person->get_schedule_type();
00653     if (sched != newsched && sched != Schedule::wait &&
00654         sched != Schedule::loiter)
00655       person->set_schedule_type(newsched);
00656     }
00657   if (main_actor->get_schedule_type() != newsched)
00658     main_actor->set_schedule_type(newsched);
00659   if (combat)     // Get rid of flee modes.
00660     {
00661     set_moving_barge(0);  // And get out of barge mode.
00662     Actor *all[9];
00663     int cnt = get_party(all, 1);
00664     for (int i = 0; i < cnt; i++)
00665       {   // Did Usecode set to flee?
00666       Actor *act = all[i];
00667       if (act->get_attack_mode() == Actor::flee &&
00668           !act->did_user_set_attack())
00669         act->set_attack_mode(Actor::nearest);
00670           // And avoid attacking party members,
00671           //  in case of Usecode bug.
00672       Game_object *targ = act->get_target();
00673       if (targ && targ->get_flag(Obj_flags::in_party))
00674         act->set_target(0);
00675       }
00676     }
00677   else        // Ending combat.
00678     Combat::resume(); // Make sure not still paused.
00679   }
00680 
00681 /*
00682  *  Add an NPC.
00683  */
00684 
00685 void Game_window::add_npc
00686   (
00687   Actor *npc,
00688   int num       // Number.  Has to match npc->num.
00689   )
00690   {
00691   assert(num == npc->get_npc_num());
00692   assert(num <= npcs.size());
00693   if (num == npcs.size())   // Add at end.
00694     npcs.append(npc);
00695   else
00696     {     // Better be unused.
00697     assert(!npcs[num] || npcs[num]->is_unused());
00698     delete npcs[num];
00699     npcs[num] = npc;
00700     }
00701   }
00702 
00703 /*
00704  *  Find first unused NPC #.
00705  */
00706 
00707 int Game_window::get_unused_npc
00708   (
00709   )
00710   {
00711   int cnt = npcs.size();    // Get # in list.
00712   for (int i = 0; i < cnt; i++)
00713     {
00714     if (!npcs[i])
00715       return i; // (Don't think this happens.)
00716     if (npcs[i]->is_unused())
00717       return i;
00718     }
00719   return cnt;     // First free one is past the end.
00720   }
00721 
00722 /*
00723  *  Resize event occurred.
00724  */
00725 
00726 void Game_window::resized
00727   (
00728   unsigned int neww, 
00729   unsigned int newh,
00730   unsigned int newsc,
00731   unsigned int newsclr
00732   )
00733   {     
00734   win->resized(neww, newh, newsc, newsclr);
00735   pal->apply(false);
00736   Set_renderer(win, pal);
00737   if (!main_actor)    // In case we're before start.
00738     return;
00739   center_view(main_actor->get_tile());
00740   paint();
00741   // Do the following only if in game (not for menus)
00742   if(!gump_man->gump_mode()) {
00743     char msg[80];
00744     snprintf(msg, 80, "%dx%dx%d", neww, newh, newsc);
00745     effects->center_text(msg);
00746   }
00747   }
00748 
00749 /*
00750  *  Clear out world's contents.  Should be used during a 'restore'.
00751  */
00752 
00753 void Game_window::clear_world
00754   (
00755   )
00756   {
00757   Combat::resume();
00758   tqueue->clear();    // Remove all entries.
00759   clear_dirty();
00760   removed->flush();   // Delete.
00761   Usecode_script::clear();  // Clear out all scheduled usecode.
00762   map->clear();
00763   Monster_actor::delete_all();  // To be safe, del. any still around.
00764   main_actor = 0;
00765   camera_actor = 0;
00766   num_npcs1 = 0;
00767   theft_cx = theft_cy = -1;
00768   combat = 0;
00769   npcs.resize(0);     // NPC's already deleted above.
00770   bodies.resize(0);
00771   moving_barge = 0;   // Get out of barge mode.
00772   special_light = 0;    // Clear out light spells.
00773   effects->remove_all_effects(false);
00774   }
00775 
00776 /*
00777  *  Look throughout the map for a given shape.  The search starts at
00778  *  the first currently-selected shape, if possible.
00779  *
00780  *  Output: true if found, else 0.
00781  */
00782 
00783 bool Game_window::locate_shape
00784   (
00785   int shapenum,     // Desired shape.
00786   bool upwards      // If true, search upwards.
00787   )
00788   {
00789           // Get (first) selected object.
00790   const std::vector<Game_object *>& sel = cheat.get_selected();
00791   Game_object *start = sel.size() ? sel[0] : 0;
00792   char msg[80];
00793   snprintf(msg, sizeof(msg), "Searching for shape %d", shapenum);
00794   effects->center_text(msg);
00795   paint();
00796   show();
00797   Game_object *obj = map->locate_shape(shapenum, upwards, start);
00798   if (!obj)
00799     {
00800     effects->center_text("Not found");
00801     return false;   // Not found.
00802     }
00803   effects->remove_text_effects();
00804   cheat.clear_selected();   // Select obj.
00805   cheat.append_selected(obj);
00806           //++++++++Got to show it.
00807   Game_object *owner = obj->get_outermost(); //+++++TESTING
00808   if (owner != obj)
00809     cheat.append_selected(owner);
00810   center_view(owner->get_tile());
00811   return true;
00812   }
00813 
00814 /*
00815  *  Set location to ExultStudio.
00816  */
00817 
00818 inline void Send_location
00819   (
00820   Game_window *gwin
00821   )
00822   {
00823 #ifdef USE_EXULTSTUDIO
00824   if (client_socket >= 0 && // Talking to ExultStudio?
00825       cheat.in_map_editor())
00826     {
00827     unsigned char data[50];
00828     unsigned char *ptr = &data[0];
00829     Write4(ptr, gwin->get_scrolltx());
00830     Write4(ptr, gwin->get_scrollty());
00831     Write4(ptr, gwin->get_width()/c_tilesize);
00832     Write4(ptr, gwin->get_height()/c_tilesize);
00833     Write4(ptr, gwin->get_win()->get_scale());
00834     Exult_server::Send_data(client_socket, Exult_server::view_pos,
00835           &data[0], ptr - data);
00836     }
00837 #endif
00838   }
00839 
00840 /*
00841  *  Send current location to ExultStudio.
00842  */
00843 
00844 void Game_window::send_location
00845   (
00846   )
00847   {
00848   Send_location(this);
00849   }
00850 
00851 /*
00852  *  Set the scroll position to a given tile.
00853  */
00854 
00855 void Game_window::set_scrolls
00856   (
00857   int newscrolltx, int newscrollty
00858   )
00859   {
00860   scrolltx = newscrolltx;
00861   scrollty = newscrollty;
00862           // Set scroll box.
00863           // Let's try 2x2 tiles.
00864   scroll_bounds.w = scroll_bounds.h = 2;
00865   scroll_bounds.x = scrolltx + 
00866       (get_width()/c_tilesize - scroll_bounds.w)/2;
00867   // OFFSET HERE
00868   scroll_bounds.y = scrollty + 
00869       ((get_height())/c_tilesize - scroll_bounds.h)/2;
00870 
00871   Barge_object *old_active_barge = moving_barge;
00872   map->read_map_data();   // This pulls in objects.
00873           // Found active barge?
00874   if (!old_active_barge && moving_barge)
00875     {     // Do it right.
00876     Barge_object *b = moving_barge;
00877     moving_barge = 0;
00878     set_moving_barge(b);
00879     }
00880           // Set where to skip rendering.
00881   int cx = camera_actor->get_cx(), cy = camera_actor->get_cy(); 
00882   Map_chunk *nlist = map->get_chunk(cx, cy);
00883   nlist->setup_cache();          
00884   int tx = camera_actor->get_tx(), ty = camera_actor->get_ty();
00885   set_above_main_actor(nlist->is_roof (tx, ty,
00886             camera_actor->get_lift()));
00887   set_in_dungeon(nlist->has_dungeon()?nlist->is_dungeon(tx, ty):0);
00888   set_ice_dungeon(nlist->is_ice_dungeon(tx, ty));
00889   send_location();    // Tell ExultStudio.
00890   }
00891 
00892 /*
00893  *  Set the scroll position so that a given tile is centered.  (Used by
00894  *  center_view.)
00895  */
00896 
00897 void Game_window::set_scrolls
00898   (
00899   Tile_coord cent     // Want center here.
00900   )
00901   {
00902           // Figure in tiles.
00903     // OFFSET HERE
00904   int tw = get_width()/c_tilesize, th = (get_height())/c_tilesize;
00905   set_scrolls(DECR_TILE(cent.tx, tw/2), DECR_TILE(cent.ty, th/2));
00906   }
00907 
00908 /*
00909  *  Center view around a given tile.  This is called during a 'restore'
00910  *  to init. stuff as well.
00911  */
00912 
00913 void Game_window::center_view
00914   (
00915   Tile_coord t
00916   )
00917   {
00918   set_scrolls(t);
00919   set_all_dirty();
00920   }
00921 
00922 /*
00923  *  Set actor to center view around.
00924  */
00925 
00926 void Game_window::set_camera_actor
00927   (
00928   Actor *a
00929   )
00930   {
00931   if (a == main_actor &&    // Setting back to main actor?
00932       camera_actor &&   // Change in chunk?
00933       (camera_actor->get_cx() != main_actor->get_cx() ||
00934        camera_actor->get_cy() != main_actor->get_cy()))
00935               // Cache out temp. objects.
00936     emulate_cache(camera_actor->get_cx(), camera_actor->get_cy(),
00937       main_actor->get_cx(), main_actor->get_cy());
00938   camera_actor = a;
00939   Tile_coord t = a->get_tile();
00940   set_scrolls(t);     // Set scrolling around position,
00941           //   and read in map there.
00942   set_all_dirty();
00943   }
00944 
00945 /*
00946  *  Scroll if necessary.
00947  *
00948  *  Output: 1 if scrolled (screen updated).
00949  */
00950 
00951 bool Game_window::scroll_if_needed
00952   (
00953   Tile_coord t
00954   )
00955   {
00956   bool scrolled = false;
00957           // 1 lift = 1/2 tile.
00958   int tx = t.tx - t.tz/2, ty = t.ty - t.tz/2;
00959   if (Tile_coord::gte(DECR_TILE(scroll_bounds.x), tx))
00960     {
00961     view_left();
00962     scrolled = true;
00963     }
00964   else if (Tile_coord::gte(tx, 
00965       (scroll_bounds.x + scroll_bounds.w)%c_num_tiles))
00966     {
00967     view_right();
00968     scrolled = true;
00969     }
00970   if (Tile_coord::gte(DECR_TILE(scroll_bounds.y), ty))
00971     {
00972     view_up();
00973     scrolled = true;
00974     }
00975   else if (Tile_coord::gte(ty, 
00976       (scroll_bounds.y + scroll_bounds.h)%c_num_tiles))
00977     {
00978     view_down();
00979     scrolled = true;
00980     }
00981   return (scrolled);
00982   }
00983 
00984 /*
00985  *  Show the absolute game location, where each coordinate is of the
00986  *  8x8 tiles clicked on.
00987  */
00988 
00989 void Game_window::show_game_location
00990   (
00991   int x, int y      // Point on screen.
00992   )
00993   {
00994   x = get_scrolltx() + x/c_tilesize;
00995   y = get_scrollty() + y/c_tilesize;
00996   cout << "Game location is (" << x << ", " << y << ")"<<endl;
00997   }
00998 
00999 /*
01000  *  Get screen area used by object.
01001  */
01002 
01003 Rectangle Game_window::get_shape_rect(Game_object *obj)
01004 {
01005   Shape_frame *s = obj->get_shape();
01006   if(!s)
01007   {
01008     // This is probably fatal.
01009 #if DEBUG
01010     std::cerr << "DEATH! get_shape() returned a NULL pointer: " << __FILE__ << ":" << __LINE__ << std::endl;
01011     std::cerr << "Betcha it's a little doggie." << std::endl;
01012 #endif
01013     return Rectangle(0,0,0,0);
01014   }
01015   Tile_coord t = obj->get_tile(); // Get tile coords.
01016   int lftpix = 4*t.tz;
01017   t.tx += 1 - get_scrolltx();
01018   t.ty += 1 - get_scrollty();
01019           // Watch for wrapping.
01020   if (t.tx < -c_num_tiles/2)
01021     t.tx += c_num_tiles;
01022   if (t.ty < -c_num_tiles/2)
01023     t.ty += c_num_tiles;
01024   return get_shape_rect(s,
01025     t.tx*c_tilesize - 1 - lftpix,
01026     t.ty*c_tilesize - 1 - lftpix);
01027 }
01028 
01029 /*
01030  *  Get screen location of given tile.
01031  */
01032 
01033 inline void Get_shape_location
01034   (
01035   Tile_coord t,
01036   int scrolltx, int scrollty,
01037   int& x, int& y
01038   )
01039   {
01040   int lft = 4*t.tz;
01041   t.tx += 1 - scrolltx;
01042   t.ty += 1 - scrollty;
01043         // Watch for wrapping.
01044   if (t.tx < -c_num_tiles/2)
01045     t.tx += c_num_tiles;
01046   if (t.ty < -c_num_tiles/2)
01047     t.ty += c_num_tiles;
01048   x = t.tx*c_tilesize - 1 - lft;
01049   y = t.ty*c_tilesize - 1 - lft;
01050   }
01051 
01052 /*
01053  *  Get screen loc. of object which MUST be on the map (no owner).
01054  */
01055 
01056 void Game_window::get_shape_location(Game_object *obj, int& x, int& y)
01057 {
01058   Get_shape_location(obj->get_tile(), scrolltx, scrollty, x, y);
01059 }
01060 void Game_window::get_shape_location(Tile_coord t, int&x, int& y)
01061 {
01062   Get_shape_location(t, scrolltx, scrollty, x, y);
01063 }
01064 
01065 /*
01066  *  Put the actor(s) in the world.
01067  */
01068 
01069 void Game_window::init_actors
01070   (
01071   )
01072   {
01073   if (main_actor)     // Already done?
01074   {
01075     Game::clear_avname();
01076     Game::clear_avsex();
01077     Game::clear_avskin();
01078     return;
01079   }
01080   read_npcs();      // Read in all U7 NPC's.
01081 
01082   // Was a name, sex or skincolor set in Game
01083   // this bascially detects 
01084   bool  changed = false;
01085 
01086   
01087   if (Game::get_avsex() == 0 || Game::get_avsex() == 1 || Game::get_avname()
01088       || (Game::get_avskin() >= 0 && Game::get_avskin() <= 2))
01089     changed = true;
01090 
01091   Game::clear_avname();
01092   Game::clear_avsex();
01093   Game::clear_avskin();
01094 
01095   // Update gamedat if there was a change
01096   if (changed)
01097     {
01098     schedule_npcs(2,7,false);
01099     write_npcs();
01100     }
01101 
01102   }
01103   
01104 /*
01105  *  Create initial 'gamedat' directory if needed
01106  *
01107  */
01108 
01109 bool Game_window::init_gamedat(bool create)
01110   {
01111           // Create gamedat files 1st time.
01112   if (create)
01113     {
01114     cout << "Creating 'gamedat' files."<<endl;
01115     if (is_system_path_defined("<PATCH>") && 
01116             U7exists(PATCH_INITGAME))
01117       restore_gamedat(PATCH_INITGAME);
01118     else
01119       {
01120           // Flag that we're reading U7 file.
01121       Game::set_new_game();
01122       restore_gamedat(INITGAME);
01123       }
01124     ofstream out;
01125           // Editing, and no IDENTITY?
01126     if (Game::is_editing() && !U7exists(IDENTITY))
01127       {
01128       U7open(out, IDENTITY);
01129       std::string gametitlestr = Game::get_gametitle();
01130       out << gametitlestr.c_str() << endl;
01131       out.close();
01132       }
01133 
01134     // log version of exult that was used to start this game
01135     U7open(out, GNEWGAMEVER);
01136     getVersionInfo(out);
01137     out.close();
01138     }
01139           //++++Maybe just test for IDENTITY+++:
01140   else if ((U7exists(U7NBUF_DAT) || !U7exists(NPC_DAT)) &&
01141               !Game::is_editing())
01142     {
01143     return false;
01144     }
01145   else
01146     {
01147       ifstream identity_file;
01148       U7open(identity_file, IDENTITY);
01149       char gamedat_identity[256];
01150       identity_file.read(gamedat_identity, 256);
01151       char *ptr = gamedat_identity;
01152       for(; (*ptr!=0x1a && *ptr!=0x0d &&
01153               *ptr != 0x0a); ptr++)
01154         ;
01155       *ptr = 0;
01156       cout << "Gamedat identity " << gamedat_identity << endl;
01157       char *static_identity = get_game_identity(INITGAME);
01158       if(strcmp(static_identity, gamedat_identity))
01159         {
01160           delete [] static_identity;
01161           return false;
01162         }
01163       delete [] static_identity;
01164           //   scroll coords.
01165     }
01166   read_save_names();    // Read in saved-game names.  
01167   return true;
01168   }
01169 
01170 /*
01171  *  Save game by writing out to the 'gamedat' directory.
01172  *
01173  *  Output: 0 if error, already reported.
01174  */
01175 
01176 void Game_window::write
01177   (
01178   )
01179   {
01180   // Lets just show a nice message on screen first
01181 
01182   int width = get_width();
01183   int centre_x  = width/2;
01184   int height = get_height();
01185   int centre_y = height/2;
01186   int text_height = shape_man->get_text_height(0);
01187   int text_width = shape_man->get_text_width(0, "Saving Game");
01188 
01189   win->fill_translucent8(0, width, height, 0, 0, 
01190           shape_man->get_xform(2));
01191   shape_man->paint_text(0, "Saving Game", centre_x-text_width/2, 
01192               centre_y-text_height);
01193   show(true);
01194   map->write_ireg();    // Write ireg files.
01195   write_npcs();     // Write out npc.dat.
01196   usecode->write();   // Usecode.dat (party, global flags).
01197   write_gwin();     // Write our data.
01198   write_saveinfo();
01199   }
01200 
01201 /*
01202  *  Restore game by reading in 'gamedat'.
01203  *
01204  *  Output: 0 if error, already reported.
01205  */
01206 
01207 void Game_window::read
01208   (
01209   )
01210   {
01211   Audio::get_ptr()->cancel_streams();
01212 #ifdef RED_PLASMA
01213   // Display red plasma during load...
01214   setup_load_palette();
01215 #endif
01216 
01217   clear_world();      // Wipe clean.
01218   read_gwin();      // Read our data.
01219           // DON'T do anything that might paint()
01220           //   before calling read_npcs!!
01221   setup_game();     // Read NPC's, usecode.
01222   }
01223 
01224 /*
01225  *  Write data for the game.
01226  *
01227  *  Output: 0 if error.
01228  */
01229 
01230 void Game_window::write_gwin
01231   (
01232   )
01233   {
01234   ofstream gout_stream;
01235   U7open(gout_stream, GWINDAT); // Gamewin.dat.
01236   StreamDataSource gout(&gout_stream);
01237           // Start with scroll coords (in tiles).
01238   gout.write2(get_scrolltx());
01239   gout.write2(get_scrollty());
01240           // Write clock.
01241   gout.write2(clock->get_day());
01242   gout.write2(clock->get_hour());
01243   gout.write2(clock->get_minute());
01244   gout.write4(special_light); // Write spell expiration minute.
01245   MyMidiPlayer *player = Audio::get_ptr()->get_midi();
01246   if (player) {
01247     gout.write4(static_cast<uint32>(player->get_current_track()));
01248     gout.write4(static_cast<uint32>(player->is_repeating()));
01249   } else {
01250     gout.write4(static_cast<uint32>(-1));
01251     gout.write4(0);
01252   }
01253   gout.write1(armageddon ? 1 : 0);
01254   gout_stream.flush();
01255   if (!gout_stream.good())
01256     throw file_write_exception(GWINDAT);
01257   }
01258 
01259 /*
01260  *  Read data for the game.
01261  *
01262  *  Output: 0 if error.
01263  */
01264 
01265 void Game_window::read_gwin
01266   (
01267   )
01268   {
01269   if (!clock->in_queue())   // Be sure clock is running.
01270     tqueue->add(Game::get_ticks(), clock, 
01271           reinterpret_cast<long>(this));
01272   ifstream gin_stream;
01273   try
01274   {
01275     U7open(gin_stream, GWINDAT);  // Gamewin.dat.
01276   } catch (const file_open_exception&)
01277   {
01278     return;
01279   }
01280   
01281   StreamDataSource gin(&gin_stream);
01282 
01283           // Start with scroll coords (in tiles).
01284   scrolltx = gin.read2();
01285   scrollty = gin.read2();
01286           // Read clock.
01287   clock->set_day(gin.read2());
01288   clock->set_hour(gin.read2());
01289   clock->set_minute(gin.read2());
01290   if (!gin_stream.good())   // Next ones were added recently.
01291     throw file_read_exception(GWINDAT);
01292   special_light = gin.read4();
01293   armageddon = false;   // Old saves may not have this yet.
01294   
01295   if (!gin_stream.good())
01296   {
01297     special_light = 0;
01298     return;
01299   }
01300 
01301   int track_num = gin.read4();
01302   int repeat = gin.read4();
01303   if (!gin_stream.good())
01304   {
01305     Audio::get_ptr()->stop_music();
01306     return;
01307   }
01308 
01309   Audio::get_ptr()->start_music(track_num, repeat != false);
01310   armageddon = gin.read1() == 1 ? true : false;
01311   if (!gin_stream.good())
01312     armageddon = false;
01313 
01314   }
01315 
01316 /*
01317  *  Write out map data (IFIXxx, U7CHUNKS, U7MAP) to static, and also
01318  *  save 'gamedat' to <PATCH>/initgame.dat.
01319  *
01320  *  Note:  This is for map-editing.
01321  *
01322  *  Output: Errors are reported.
01323  */
01324 
01325 void Game_window::write_map
01326   (
01327   )
01328   {
01329   map->write_static();    // Write ifix, map files.
01330   write();      // Write out to 'gamedat' too.
01331   save_gamedat(PATCH_INITGAME, "Saved map");
01332   }
01333 
01334 /*
01335  *  Reinitialize game from map.
01336  */
01337 
01338 void Game_window::read_map
01339   (
01340   )
01341   {
01342   init_gamedat(true);   // Unpack 'initgame.dat'.
01343   read();       // This does the whole restore.
01344   }
01345 
01346 /*
01347  *  Reload (patched) usecode.
01348  */
01349 
01350 void Game_window::reload_usecode
01351   (
01352   )
01353   {
01354           // Get custom usecode functions.
01355   if (is_system_path_defined("<PATCH>") && U7exists(PATCH_USECODE))
01356     {
01357     ifstream file;
01358     U7open(file, PATCH_USECODE);
01359     usecode->read_usecode(file, true);
01360     file.close();
01361     }
01362   }
01363 
01364 /*
01365  *  Shift view by one tile.
01366  */
01367 
01368 void Game_window::view_right
01369   (
01370   )
01371   {
01372   int w = get_width(), h = get_height();
01373           // Get current rightmost chunk.
01374   int old_rcx = ((scrolltx + (w - 1)/c_tilesize)/c_tiles_per_chunk)%
01375               c_num_chunks;
01376   scrolltx = INCR_TILE(scrolltx);
01377   scroll_bounds.x = INCR_TILE(scroll_bounds.x);
01378   if (gump_man->showing_gumps())    // Gump on screen?
01379     {
01380     paint();
01381     return;
01382     }
01383   map->read_map_data();   // Be sure objects are present.
01384 #ifdef HAVE_OPENGL
01385   if (GL_manager::get_instance()) // OpenGL?  Just repaint all.
01386     paint();
01387   else
01388 #endif
01389     {
01390           // Shift image to left.
01391     win->copy(c_tilesize, 0, w - c_tilesize, h, 0, 0);
01392           // Paint 1 column to right.
01393     paint(w - c_tilesize, 0, c_tilesize, h);
01394     }
01395   dirty.x -= c_tilesize;  // Shift dirty rect.
01396   dirty = clip_to_win(dirty);
01397           // New chunk?
01398   int new_rcx = ((scrolltx + (w - 1)/c_tilesize)/c_tiles_per_chunk)%
01399               c_num_chunks;
01400   if (new_rcx != old_rcx)
01401     Send_location(this);
01402   }
01403 void Game_window::view_left
01404   (
01405   )
01406   {
01407   int old_lcx = (scrolltx/c_tiles_per_chunk)%c_num_chunks;
01408           // Want to wrap.
01409   scrolltx = DECR_TILE(scrolltx);
01410   scroll_bounds.x = DECR_TILE(scroll_bounds.x);
01411   if (gump_man->showing_gumps())      // Gump on screen?
01412     {
01413     paint();
01414     return;
01415     }
01416   map->read_map_data();   // Be sure objects are present.
01417 #ifdef HAVE_OPENGL
01418   if (GL_manager::get_instance()) // OpenGL?  Just repaint all.
01419     paint();
01420   else
01421 #endif
01422     {
01423     win->copy(0, 0, get_width() - c_tilesize, get_height(), 
01424                 c_tilesize, 0);
01425     int h = get_height();
01426     paint(0, 0, c_tilesize, h);
01427     }
01428   dirty.x += c_tilesize;    // Shift dirty rect.
01429   dirty = clip_to_win(dirty);
01430           // New chunk?
01431   int new_lcx = (scrolltx/c_tiles_per_chunk)%c_num_chunks;
01432   if (new_lcx != old_lcx)
01433     Send_location(this);
01434   }
01435 void Game_window::view_down
01436   (
01437   )
01438   {
01439   int w = get_width(), h = get_height();
01440           // Get current bottomost chunk.
01441   int old_bcy = ((scrollty + (h - 1)/c_tilesize)/c_tiles_per_chunk)%
01442               c_num_chunks;
01443   scrollty = INCR_TILE(scrollty);
01444   scroll_bounds.y = INCR_TILE(scroll_bounds.y);
01445   if (gump_man->showing_gumps())      // Gump on screen?
01446     {
01447     paint();
01448     return;
01449     }
01450   map->read_map_data();   // Be sure objects are present.
01451 #ifdef HAVE_OPENGL
01452   if (GL_manager::get_instance()) // OpenGL?  Just repaint all.
01453     paint();
01454   else
01455 #endif
01456     {
01457     win->copy(0, c_tilesize, w, h - c_tilesize, 0, 0);
01458     paint(0, h - c_tilesize, w, c_tilesize);
01459     }
01460   dirty.y -= c_tilesize;    // Shift dirty rect.
01461   dirty = clip_to_win(dirty);
01462           // New chunk?
01463   int new_bcy = ((scrollty + (h - 1)/c_tilesize)/c_tiles_per_chunk)%
01464               c_num_chunks;
01465   if (new_bcy != old_bcy)
01466     Send_location(this);
01467   }
01468 void Game_window::view_up
01469   (
01470   )
01471   {
01472   int old_tcy = (scrollty/c_tiles_per_chunk)%c_num_chunks;
01473           // Want to wrap.
01474   scrollty = DECR_TILE(scrollty);
01475   scroll_bounds.y = DECR_TILE(scroll_bounds.y);
01476   if (gump_man->showing_gumps())    // Gump on screen?
01477     {
01478     paint();
01479     return;
01480     }
01481   map->read_map_data();   // Be sure objects are present.
01482 #ifdef HAVE_OPENGL
01483   if (GL_manager::get_instance()) // OpenGL?  Just repaint all.
01484     paint();
01485   else
01486 #endif
01487     {
01488     int w = get_width();
01489     win->copy(0, 0, w, get_height() - c_tilesize, 0, c_tilesize);
01490     paint(0, 0, w, c_tilesize);
01491     }
01492   dirty.y += c_tilesize;    // Shift dirty rect.
01493   dirty = clip_to_win(dirty);
01494           // New chunk?
01495   int new_tcy = (scrollty/c_tiles_per_chunk)%c_num_chunks;
01496   if (new_tcy != old_tcy)
01497     Send_location(this);
01498   }
01499 
01500 /*
01501  *  Get gump being dragged.
01502  */
01503 
01504 Gump *Game_window::get_dragging_gump
01505   (
01506   )
01507   {
01508   return dragging ? dragging->gump : 0;
01509   }
01510 
01511 /*
01512  *  Alternative start actor function
01513  *  Placed in an alternative function to prevent breaking barges
01514  */
01515 void Game_window::start_actor_alt
01516   (
01517   int winx, int winy,     // Mouse position to aim for.
01518   int speed     // Msecs. between frames.
01519   )
01520   {
01521   int ax, ay;
01522   int nlift;
01523   int blocked[8];
01524   get_shape_location(main_actor, ax, ay);
01525   int height = main_actor->get_info().get_3d_height();
01526   
01527   Tile_coord start = main_actor->get_tile();
01528   int dir;
01529   
01530   for (dir = 0; dir < 8; dir++)
01531   {
01532     Tile_coord dest = start.get_neighbor(dir);
01533     int cx = dest.tx/c_tiles_per_chunk, cy = dest.ty/c_tiles_per_chunk;
01534     int tx = dest.tx%c_tiles_per_chunk, ty = dest.ty%c_tiles_per_chunk;
01535 
01536     Map_chunk *clist = map->get_chunk_safely(cx, cy);
01537     clist->setup_cache();
01538     blocked[dir] = clist->is_blocked (height, 
01539       main_actor->get_lift(), tx, ty, nlift, 
01540           main_actor->get_type_flags(), 1);
01541   }
01542 
01543   dir = Get_direction (ay - winy, winx - ax);
01544 
01545   if (blocked[dir] && !blocked[(dir+1)%8])
01546     dir = (dir+1)%8;
01547   else if (blocked[dir] && !blocked[(dir+7)%8])
01548     dir = (dir+7)%8;
01549   else if (blocked[dir])
01550   {
01551       Game_object *block = main_actor->is_moving() ? 0
01552       : Game_object::find_blocking(start.get_neighbor(dir));
01553     if (block == main_actor || !block || !block->move_aside(
01554             main_actor, dir))
01555       {
01556       stop_actor();
01557       if (main_actor->get_lift()%5)// Up on something?
01558         { // See if we're stuck in the air.
01559         int savetz = start.tz;
01560         if (!Map_chunk::is_blocked(start, 1, 
01561             MOVE_WALK, 100) && 
01562             start.tz < savetz)
01563           main_actor->move(start.tx, start.ty, 
01564                 start.tz);
01565         }
01566       return;
01567       }
01568   }
01569 
01570   const int delta = step_tile_delta*c_tilesize;// Bigger # here avoids jerkiness,
01571           //   but causes probs. with followers.
01572   switch (dir)
01573     {
01574     case north:
01575     //cout << "NORTH" << endl;
01576     ay -= delta;
01577     break;
01578 
01579     case northeast:
01580     //cout << "NORTH EAST" << endl;
01581     ay -= delta;
01582     ax += delta;
01583     break;
01584 
01585     case east:
01586     //cout << "EAST" << endl;
01587     ax += delta;
01588     break;
01589 
01590     case southeast:
01591     //cout << "SOUTH EAST" << endl;
01592     ay += delta;
01593     ax += delta;
01594     break;
01595 
01596     case south:
01597     //cout << "SOUTH" << endl;
01598     ay += delta;
01599     break;
01600 
01601     case southwest:
01602     //cout << "SOUTH WEST" << endl;
01603     ay += delta;
01604     ax -= delta;
01605     break;
01606 
01607     case west:
01608     //cout << "WEST" << endl;
01609     ax -= delta;
01610     break;
01611 
01612     case northwest:
01613     //cout << "NORTH WEST" << endl;
01614     ay -= delta;
01615     ax -= delta;
01616     break;
01617     }
01618 
01619   int lift = main_actor->get_lift();
01620   int liftpixels = 4*lift;  // Figure abs. tile.
01621   int tx = get_scrolltx() + (ax + liftpixels)/c_tilesize,
01622       ty = get_scrollty() + (ay + liftpixels)/c_tilesize;
01623           // Wrap:
01624   tx = (tx + c_num_tiles)%c_num_tiles;
01625   ty = (ty + c_num_tiles)%c_num_tiles;
01626   main_actor->walk_to_tile(tx, ty, lift, speed, 0);
01627   if (walk_in_formation && main_actor->get_action())
01628     //++++++In this case, may need to set schedules back to
01629     // follow_avatar after, say, sitting.++++++++++
01630     main_actor->get_action()->set_get_party(true);
01631   else        // "Traditional" Exult walk:-)
01632     main_actor->get_followers();
01633   }
01634 
01635 /*
01636  *  Start the actor.
01637  */
01638 
01639 void Game_window::start_actor
01640   (
01641   int winx, int winy,     // Mouse position to aim for.
01642   int speed     // Msecs. between frames.
01643   )
01644   {
01645   if (main_actor->Actor::get_flag(Obj_flags::asleep) ||
01646       main_actor->Actor::get_flag(Obj_flags::paralyzed) ||
01647       main_actor->get_schedule_type() == Schedule::sleep)
01648     return;     // Zzzzz....
01649   if (main_actor_dont_move() || (gump_man->gump_mode() && !gump_man->gumps_dont_pause_game()))
01650     return;
01651 //  teleported = 0;
01652   if (moving_barge)
01653     {     // Want to move center there.
01654     int lift = main_actor->get_lift();
01655     int liftpixels = 4*lift;  // Figure abs. tile.
01656     int tx = get_scrolltx() + (winx + liftpixels)/c_tilesize,
01657         ty = get_scrollty() + (winy + liftpixels)/c_tilesize;
01658           // Wrap:
01659     tx = (tx + c_num_tiles)%c_num_tiles;
01660     ty = (ty + c_num_tiles)%c_num_tiles;
01661     Tile_coord atile = moving_barge->get_center(),
01662          btile = moving_barge->get_tile();
01663           // Go faster than walking.
01664     moving_barge->travel_to_tile(
01665       Tile_coord(tx + btile.tx - atile.tx, 
01666            ty + btile.ty - atile.ty, btile.tz), 
01667           speed/2);
01668     }
01669   else
01670     {
01671     /*
01672     main_actor->walk_to_tile(tx, ty, lift, speed, 0);
01673     main_actor->get_followers();
01674     */
01675           // Set schedule.
01676     int sched = main_actor->get_schedule_type();
01677     if (sched != Schedule::follow_avatar &&
01678             sched != Schedule::combat &&
01679         !main_actor->get_flag(Obj_flags::asleep))
01680       main_actor->set_schedule_type(Schedule::follow_avatar);
01681     // Going to use the alternative function for this at the moment
01682     start_actor_alt (winx, winy, speed);
01683     }
01684   }
01685 
01686 /*
01687  *  Find path to where user double-right-clicked.
01688  */
01689 
01690 void Game_window::start_actor_along_path
01691   (
01692   int winx, int winy,     // Mouse position to aim for.
01693   int speed     // Msecs. between frames.
01694   )
01695   {
01696   if (main_actor->Actor::get_flag(Obj_flags::asleep) ||
01697       main_actor->Actor::get_flag(Obj_flags::paralyzed) ||
01698       main_actor->get_schedule_type() == Schedule::sleep ||
01699       moving_barge)   // For now, don't do barges.
01700     return;     // Zzzzz....
01701           // Animation in progress?
01702   if (main_actor_dont_move())
01703     return;
01704 //  teleported = 0;
01705   int lift = main_actor->get_lift();
01706   int liftpixels = 4*lift;  // Figure abs. tile.
01707   Tile_coord dest(get_scrolltx() + (winx + liftpixels)/c_tilesize,
01708       get_scrollty() + (winy + liftpixels)/c_tilesize, lift);
01709   if (!main_actor->walk_path_to_tile(dest, speed))
01710     cout << "Couldn't find path for Avatar." << endl;
01711   else
01712     main_actor->get_followers();
01713   }
01714 
01715 /*
01716  *  Stop the actor.
01717  */
01718 
01719 void Game_window::stop_actor
01720   (
01721   )
01722   {
01723   if (moving_barge)
01724     moving_barge->stop();
01725   else
01726     {
01727     main_actor->stop(); // Stop and set resting state.
01728     if (!gump_man->gump_mode() ||gump_man->gumps_dont_pause_game())
01729 //+++++++The following line is for testing:
01730 //      if (!walk_in_formation)
01731         main_actor->get_followers();
01732     }
01733   }
01734 
01735 /*
01736  *  Teleport the party.
01737  */
01738 
01739 void Game_window::teleport_party
01740   (
01741   Tile_coord t,     // Where to go.
01742   bool skip_eggs      // Don't activate eggs at dest.
01743   )
01744   {
01745   Tile_coord oldpos = main_actor->get_tile();
01746   main_actor->set_action(0);  // Definitely need this, or you may
01747           //   step back to where you came from.
01748   moving_barge = 0;   // Calling 'done()' could be risky...
01749   main_actor->move(t.tx, t.ty, t.tz); // Move Avatar.
01750   center_view(t);     // Bring pos. into view, and insure all
01751           //   objs. exist.
01752 
01753   int cnt = party_man->get_count();
01754   for (int i = 0; i < cnt; i++)
01755     {
01756     int party_member=party_man->get_member(i);
01757     Actor *person = get_npc(party_member);
01758     if (person && !person->is_dead() && 
01759         person->get_schedule_type() != Schedule::wait)
01760       {
01761       person->set_action(0);
01762       Tile_coord t1 = Map_chunk::find_spot(t, 8,
01763         person->get_shapenum(), person->get_framenum(),
01764                   1);
01765       if (t1.tx != -1)
01766         person->move(t1);
01767       }
01768     }
01769   main_actor->get_followers();
01770   if (!skip_eggs)     // Check all eggs around new spot.
01771     Map_chunk::try_all_eggs(main_actor, t.tx, t.ty, t.tz,
01772           oldpos.tx, oldpos.ty);
01773 //  teleported = 1;
01774   // generate mousemotion event
01775   int x, y;
01776   SDL_GetMouseState(&x, &y);
01777   SDL_WarpMouse(x, y);
01778   }
01779 
01780 /*
01781  *  Get party members.
01782  *
01783  *  Output: Number returned in 'list'.
01784  */
01785 
01786 int Game_window::get_party
01787   (
01788   Actor **a_list,     // Room for 9.
01789   int avatar_too      // 1 to include Avatar too.
01790   )
01791   {
01792   int n = 0;
01793   if (avatar_too && main_actor)
01794     a_list[n++] = main_actor;
01795   int cnt = party_man->get_count();
01796   for (int i = 0; i < cnt; i++)
01797     {
01798     int party_member = party_man->get_member(i);
01799     Actor *person = get_npc(party_member);
01800     if (person)
01801       a_list[n++] = person;
01802     }
01803   return n;     // Return # actually stored.
01804   }
01805 
01806 /*
01807  *  Find a given shaped item amongst the party, and 'activate' it.  This
01808  *  is used, for example, by the 'f' command to feed.
01809  *
01810  *  Output: True if the item was found, else false.
01811  */
01812 
01813 bool Game_window::activate_item
01814   (
01815   int shnum,      // Desired shape.
01816   int frnum,      // Desired frame
01817   int qual      // Desired quality
01818   )
01819   {
01820   Actor *party[9];    // Get party.
01821   int cnt = get_party(party, 1);
01822   for (int i = 0; i < cnt; i++)
01823     {
01824     Actor *person = party[i];
01825     Game_object *obj = person->find_item(shnum, qual, frnum);
01826     if (obj)
01827       {
01828       obj->activate();
01829       return true;
01830       }
01831     }
01832   return false;
01833   }
01834 /*
01835  *  Find the top object that can be selected, dragged, or activated.
01836  *  The one returned is the 'highest'.
01837  *
01838  *  Output: ->object, or null if none.
01839  */
01840 
01841 Game_object *Game_window::find_object
01842   (
01843   int x, int y      // Pos. on screen.
01844   )
01845   {
01846 #ifdef DEBUG
01847 cout << "Clicked at tile (" << get_scrolltx() + x/c_tilesize << ", " <<
01848     get_scrollty() + y/c_tilesize << ")"<<endl;
01849 #endif
01850   int not_above = get_render_skip_lift();
01851           // Figure chunk #'s.
01852   int start_cx = ((scrolltx + 
01853     x/c_tilesize)/c_tiles_per_chunk)%c_num_chunks;
01854   int start_cy = ((scrollty + 
01855     y/c_tilesize)/c_tiles_per_chunk)%c_num_chunks;
01856           // Check 1 chunk down & right too.
01857   int stop_cx = (2 + (scrolltx + 
01858     (x + 4*not_above)/c_tilesize)/c_tiles_per_chunk)%c_num_chunks;
01859   int stop_cy = (2 + (scrollty + 
01860     (y + 4*not_above)/c_tilesize)/c_tiles_per_chunk)%c_num_chunks;
01861 
01862   Game_object *best = 0;    // Find 'best' one.
01863   bool trans = true;    // Try to avoid 'transparent' objs.
01864           // Go through them.
01865   for (int cy = start_cy; cy != stop_cy; cy = INCR_CHUNK(cy))
01866   for (int cx = start_cx; cx != stop_cx; cx = INCR_CHUNK(cx))
01867     {
01868     Map_chunk *olist = map->get_chunk(cx, cy);
01869     if (!olist)
01870       continue;
01871     Object_iterator next(olist->get_objects());
01872     Game_object *obj;
01873     while ((obj = next.get_next()) != 0)
01874       {
01875       if (obj->get_lift() >= not_above ||
01876           !get_shape_rect(obj).has_point(x, y) || 
01877           !obj->is_findable())
01878         continue;
01879           // Check the shape itself.
01880       Shape_frame *s = obj->get_shape();
01881       int ox, oy;
01882       get_shape_location(obj, ox, oy);
01883       if (!s->has_point(x - ox, y - oy))
01884         continue;
01885       if (!best || best->lt(*obj) == 1 || trans)
01886         {
01887         bool ftrans = obj->get_info().is_transparent() != 0;
01888         if (!ftrans || trans)
01889           {
01890           best = obj;
01891           trans = ftrans;
01892           }
01893         }
01894       }
01895     }
01896   return (best);
01897   }
01898 
01899 /*
01900  *  Show the name of the item the mouse is clicked on.
01901  */
01902 
01903 void Game_window::show_items
01904   (
01905   int x, int y,     // Coords. in window.
01906   bool ctrl     // Control key is pressed.
01907   )
01908   {
01909           // Look for obj. in open gump.
01910   Gump *gump = gump_man->find_gump(x, y);
01911   Game_object *obj;   // What we find.
01912   if (gump)
01913   {
01914     obj = gump->find_object(x, y);
01915     if (!obj) obj = gump->get_cont_or_actor(x, y);
01916   }
01917   else        // Search rest of world.
01918     obj = find_object(x, y);
01919           // Map-editing?
01920   if (obj && cheat.in_map_editor())
01921     {
01922       
01923     if (ctrl)   // Control?  Toggle.
01924       cheat.toggle_selected(obj);
01925     else
01926       {   // In normal mode, sel. just this one.
01927       cheat.clear_selected();
01928       if (cheat.get_edit_mode() == Cheat::move)
01929         cheat.append_selected(obj);
01930       }
01931     }
01932   else        // All other cases:  unselect.
01933     cheat.clear_selected(); 
01934 
01935           // Do we want the NPC number?
01936   Actor *npc = obj ? obj->as_actor() : 0;
01937   if (npc && cheat.number_npcs() &&
01938       (npc->get_npc_num() > 0 || npc==main_actor))
01939   {
01940     char str[64];
01941     std::string namestr = obj->get_name();
01942     snprintf (str, 64, "(%i) %s", npc->get_npc_num(), 
01943           namestr.c_str());
01944     effects->add_text(str, obj);
01945   }
01946   else if (obj)
01947   {     // Show name.
01948     std::string namestr = obj->get_name();
01949     const char *objname = namestr.c_str();
01950     Actor *actor;   // Combat, and an NPC?
01951     if (in_combat() && Combat::mode != Combat::original &&
01952         (actor = obj->as_actor()) != 0)
01953       {
01954       char buf[128];
01955       sprintf(buf, "%s (%d)", objname, 
01956           actor->get_property(Actor::health));
01957       objname = &buf[0];
01958       }
01959     effects->add_text(objname, obj);
01960     }
01961   else if (cheat.in_map_editor() && skip_lift > 0)
01962     {     // Show flat, but not when editing ter.
01963     ShapeID id = get_flat(x, y);
01964     char str[12];
01965     snprintf(str, 12, "Flat %d:%d", id.get_shapenum(), 
01966             id.get_framenum());
01967     effects->add_text(str, x, y);
01968     }
01969   // If it's an actor and we want to grab the actor, grab it.
01970   if (npc && cheat.grabbing_actor() && 
01971       (npc->get_npc_num() || npc==main_actor))
01972     cheat.set_grabbed_actor (npc);
01973 
01974 #ifdef DEBUG
01975   int shnum, frnum;
01976   if (obj)
01977     {
01978     shnum = obj->get_shapenum(), frnum = obj->get_framenum();
01979     Shape_info& info = obj->get_info();
01980     cout << "Object " << shnum << ':' << frnum <<
01981           " has 3d tiles (x, y, z): " <<
01982       info.get_3d_xtiles(frnum) << ", " <<
01983       info.get_3d_ytiles(frnum) << ", " <<
01984       info.get_3d_height();
01985     Actor *npc = obj->as_actor();
01986     if (npc)
01987       cout  << ", sched = " << 
01988       npc->get_schedule_type() << ", align = " <<
01989       npc->get_alignment() << ", npcnum = " <<
01990       npc->get_npc_num();
01991     cout << endl;
01992     Tile_coord t = obj->get_tile();
01993     cout << "tx = " << t.tx << ", ty = " << t.ty << ", tz = " <<
01994       t.tz << ", quality = " <<
01995       obj->get_quality() << 
01996       ", okay_to_take = " <<
01997       static_cast<int>(obj->get_flag(Obj_flags::okay_to_take)) <<
01998       ", flag0x1d = " << static_cast<int>(obj->get_flag(0x1d)) <<
01999       ", hp = " << obj->get_obj_hp() << ", weight = "<< obj->get_weight()
02000        << ", volume = " << obj->get_volume()
02001       << endl;
02002     cout << "obj = " << (void *) obj << endl;
02003     if (obj->get_flag(Obj_flags::asleep))
02004       cout << "ASLEEP" << endl;
02005     if (obj->is_egg())  // Show egg info. here.
02006       ((Egg_object *)obj)->print_debug();
02007     }
02008   else        // Obj==0
02009     {
02010     ShapeID id = get_flat(x, y);
02011     shnum = id.get_shapenum();
02012     cout << "Clicked on flat shape " << 
02013       shnum << ':' << id.get_framenum() << endl;
02014 
02015 #ifdef CHUNK_OBJ_DUMP
02016     Map_chunk *chunk = map->get_chunk_safely(x/c_tiles_per_chunk, y/c_tiles_per_chunk);
02017     Object_iterator it(chunk->get_objects());
02018     Game_object *each;
02019     cout << "Chunk Contents: " << endl;
02020     while ((each = it.get_next()) != 0)
02021       cout << "    " << each->get_name() << ":" << each->get_shapenum() << ":" << each->get_framenum() << endl;
02022 #endif
02023     if (id.is_invalid())
02024       return;
02025     }
02026   Shape_info& info = ShapeID::get_info(shnum);
02027   cout << "TFA[1][0-6]= " << ((static_cast<int>(info.get_tfa(1)))&127) << endl;
02028   cout << "TFA[0][0-1]= " << ((static_cast<int>(info.get_tfa(0))&3)) << endl;
02029   cout << "TFA[0][3-4]= " << ((static_cast<int>((info.get_tfa(0)>>3))&3)) << endl;
02030   if (info.is_animated())
02031     cout << "Object is ANIMATED" << endl;
02032   if (info.has_translucency())
02033     cout << "Object has TRANSLUCENCY" << endl;
02034   if (info.is_transparent())
02035     cout << "Object is TRANSPARENT" << endl;
02036   if (info.is_light_source())
02037     cout << "Object is LIGHT_SOURCE" << endl;
02038   if (info.is_door())
02039     cout << "Object is a DOOR" << endl;
02040   if (info.is_solid())
02041     cout << "Object is SOLID" << endl;
02042 #endif
02043   }
02044 
02045 /*
02046  *  Handle right click when combat is paused.  The user can right-click
02047  *  on a party member, then on an enemy to attack.
02048  */
02049 
02050 void Game_window::paused_combat_select
02051   (
02052   int x, int y      // Coords in window.
02053   )
02054   {
02055   Gump *gump = gump_man->find_gump(x, y);
02056   if (gump)
02057     return;     // Ignore if clicked on gump.
02058   Game_object *obj = find_object(x, y);
02059   Actor *npc = obj ? obj->as_actor() : 0;
02060   if (!npc || !npc->is_in_party() ||
02061       npc->get_flag(Obj_flags::asleep) || npc->is_dead() ||
02062       npc->get_flag(Obj_flags::paralyzed))
02063     return;     // Want an active party member.
02064   npc->paint_outline(PROTECT_PIXEL);
02065   show(true);     // Flash white outline.
02066   SDL_Delay(100);
02067   npc->add_dirty();
02068   paint_dirty();
02069   show();
02070           // Pick a spot.
02071   if (!Get_click(x, y, Mouse::greenselect, 0, true))
02072     return;
02073   obj = find_object(x, y);  // Find it.
02074   if (!obj)     // Nothing?  Walk there.
02075     {     // Needs work if lift > 0.
02076     int lift = npc->get_lift();
02077     int liftpixels = 4*lift;
02078     Tile_coord dest(scrolltx + (x + liftpixels)/c_tilesize,
02079           scrollty + (y + liftpixels)/c_tilesize, lift);
02080           // Aim within 1 tile.
02081     if (!npc->walk_path_to_tile(dest, std_delay, 0, 1))
02082       Mouse::mouse->flash_shape(Mouse::blocked);
02083     else      // Make sure he's in combat mode.
02084       npc->set_target(0, true);
02085     return;
02086     }
02087   Actor *target = obj->as_actor();
02088           // Don't attack party or body.
02089   if ((target && target->is_in_party()) || Is_body(obj->get_shapenum()))
02090     {
02091     Mouse::mouse->flash_shape(Mouse::redx);
02092     return;
02093     }
02094   npc->set_target(obj, true);
02095   obj->paint_outline(HIT_PIXEL);  // Flash red outline.
02096   show(true);
02097   SDL_Delay(100);
02098   add_dirty(obj);
02099   paint_dirty();
02100   show();
02101   }
02102 
02103 /*
02104  *  Get the 'flat' that a screen point is in.
02105  */
02106 
02107 ShapeID Game_window::get_flat
02108   (
02109   int x, int y      // Window point.
02110   )
02111   {
02112   int tx = (get_scrolltx() + x/c_tilesize)%c_num_tiles;
02113   int ty = (get_scrollty() + y/c_tilesize)%c_num_tiles;
02114   int cx = tx/c_tiles_per_chunk, cy = ty/c_tiles_per_chunk;
02115   tx = tx%c_tiles_per_chunk;
02116   ty = ty%c_tiles_per_chunk;
02117   Map_chunk *chunk = map->get_chunk(cx, cy);
02118   ShapeID id = chunk->get_flat(tx, ty);
02119   return id;
02120   }
02121 
02122 /*
02123  *  Remove an item from the world and set it for later deletion.
02124  */
02125 
02126 void Game_window::delete_object
02127   (
02128   Game_object *obj
02129   )
02130   {
02131   obj->set_invalid();   // Set to invalid chunk.
02132   if (!obj->is_monster())   // Don't delete these!
02133     removed->insert(obj); // Add to pool instead.
02134   }
02135 
02136 /*
02137  *  A sign or plaque?
02138  */
02139 
02140 static bool Is_sign
02141   (
02142   int shnum
02143   )
02144   {
02145   switch(shnum)
02146     {
02147   case 820:     // Plaque.
02148   case 360:
02149   case 361:
02150   case 379:
02151     return true;
02152   default:
02153     return false;
02154     }
02155   }
02156 
02157 /*
02158  *  Handle a double-click.
02159  */
02160 
02161 void Game_window::double_clicked
02162   (
02163   int x, int y      // Coords in window.
02164   )
02165   {
02166 #if 0
02167 //++++++++++++TESTING
02168   static int ncnt = 0;
02169   cout << "Showing xform for ncnt = " << ncnt << endl;
02170   std::size_t nxforms = sizeof(xforms)/sizeof(xforms[0]);
02171   pal->load(PALETTES_FLX, 0, XFORMTBL, nxforms - 1 - ncnt);
02172   pal->apply(false);
02173   ncnt = (ncnt + 1)%nxforms;
02174 //^^^^^^^^^^^^TESTING
02175 #endif
02176           // Animation in progress?
02177   if (main_actor_dont_move())
02178     return;
02179           // Nothing going on?
02180   if (!Usecode_script::get_count())
02181     removed->flush(); // Flush removed objects.
02182           // Look for obj. in open gump.
02183   Game_object *obj = 0;
02184   bool gump = gump_man->double_clicked(x, y, obj);
02185 
02186   // If gump manager didn't handle it, we search the world for an object
02187   if (!gump)
02188     {
02189     obj = find_object(x, y);
02190 #ifdef USE_EXULTSTUDIO
02191 
02192     if (cheat.in_map_editor() && cheat.get_edit_mode() == 
02193         Cheat::combo_pick && client_socket >= 0)
02194       {   // Add obj/tile to combo in EStudio.
02195       ShapeID id = obj ? *obj : get_flat(x, y);
02196       Tile_coord t = obj ? obj->get_tile() :
02197           Tile_coord((scrolltx + x/c_tilesize)%c_num_tiles,
02198                    (scrollty + y/c_tilesize)%c_num_tiles, 
02199                   0);
02200       std::string name = item_names[id.get_shapenum()];
02201       if (Object_out(client_socket, Exult_server::combo_pick,
02202           0, t.tx, t.ty, t.tz, id.get_shapenum(),
02203            id.get_framenum(), 0, name) == -1)
02204         cout << "Error sending shape to ExultStudio" 
02205                 << endl;
02206       return;
02207       }
02208 #endif
02209     // Check path, except if an NPC, sign, or if editing.
02210         if (obj && !obj->as_actor() &&
02211       !cheat.in_hack_mover() &&
02212       !Is_sign(obj->get_shapenum()) &&
02213       !Fast_pathfinder_client::is_grabable(
02214         main_actor->get_tile(),
02215         obj->get_tile()))
02216       {
02217       Mouse::mouse->flash_shape(Mouse::blocked);
02218       return;
02219       }
02220     }
02221   if (!obj)
02222     return;     // Nothing found.
02223   if (combat && !gump &&    // In combat?
02224       !Combat::is_paused() &&
02225       (!gump_man->gump_mode() || gump_man->gumps_dont_pause_game()))
02226     {
02227     Actor *npc = obj->as_actor();
02228           // But don't attack party members.
02229     if ((!npc || !npc->is_in_party()) &&
02230           // Or bodies.
02231             !Is_body(obj->get_shapenum()))
02232       {   // In combat mode.
02233       // Want everyone to be in combat.
02234       combat = 0;
02235       main_actor->set_target(obj);
02236       toggle_combat();
02237       return;
02238       }
02239     }
02240   effects->remove_text_effects(); // Remove text msgs. from screen.
02241 #ifdef DEBUG
02242   cout << "Object name is " << obj->get_name() << endl;
02243 #endif
02244   usecode->init_conversation();
02245   obj->activate();
02246   npc_prox->wait(4);    // Delay "barking" for 4 secs.
02247   }
02248 
02249 
02250 /*
02251  *  Add an NPC to the 'nearby' list.
02252  */
02253 
02254 void Game_window::add_nearby_npc
02255   (
02256   Npc_actor *npc
02257   )
02258   {
02259   if (!npc->is_nearby())
02260     {
02261     npc->set_nearby();
02262     npc_prox->add(Game::get_ticks(), npc);
02263     }
02264   }
02265 
02266 /*
02267  *  Remove an NPC from the nearby list.
02268  */
02269 
02270 void Game_window::remove_nearby_npc
02271   (
02272   Npc_actor *npc
02273   )
02274   {
02275   if (npc->is_nearby())
02276     npc_prox->remove(npc);
02277   }
02278 
02279 /*
02280  *  Add all nearby NPC's to the given list.
02281  */
02282 
02283 void Game_window::get_nearby_npcs
02284   (
02285   Actor_queue& a_list
02286   )
02287   {
02288   npc_prox->get_all(a_list);
02289   }
02290 
02291 /*
02292  *  Tell all npc's to update their schedules at a new 3-hour period.
02293  */
02294 
02295 void Game_window::schedule_npcs
02296   (
02297   int hour3,      // 0=midnight, 1=3am, 2=6am, etc.
02298   int backwards,      // Extra periods to look backwards.
02299   bool repaint
02300   )
02301   {
02302           // Go through npc's, skipping Avatar.
02303   for (Actor_vector::iterator it = npcs.begin() + 1; 
02304             it != npcs.end(); it++)
02305     {
02306     Npc_actor *npc = (Npc_actor *) *it;
02307           // Don't want companions leaving.
02308     if (npc && npc->get_schedule_type() != Schedule::wait)
02309       npc->update_schedule(hour3, backwards);
02310     }
02311 
02312   if (repaint)
02313     paint();      // Repaint all.
02314   }
02315 
02316 /*
02317  *  Tell all npc's to restore some of their HP's and/or mana on the hour.
02318  */
02319 
02320 void Game_window::mend_npcs
02321   (
02322   )
02323   {
02324           // Go through npc's.
02325   for (Actor_vector::iterator it = npcs.begin(); 
02326             it != npcs.end(); it++)
02327     {
02328     Npc_actor *npc = (Npc_actor *) *it;
02329     if (npc)
02330       npc->mend_hourly();
02331     }
02332   }
02333 
02334 /*
02335  *  Get guard shape.
02336  */
02337 
02338 int Get_guard_shape
02339   (
02340   Tile_coord pos      // Position to use.
02341   )
02342   {
02343   if (!GAME_SI)     // Default (BG).
02344     return (0x3b2);
02345           // Moonshade?
02346   if (pos.tx >= 2054 && pos.ty >= 1698 &&
02347       pos.tx < 2590 && pos.ty < 2387)
02348     return 0x103;   // Ranger.
02349           // Fawn?
02350   if (pos.tx >= 895 && pos.ty >= 1604 &&
02351       pos.tx < 1173 && pos.ty < 1960)
02352     return 0x17d;   // Fawn guard.
02353   return 0xe4;      // Pikeman.
02354   }
02355 
02356 /*
02357  *  Find a witness to the Avatar's thievery.
02358  *
02359  *  Output: ->witness, or NULL.
02360  *    closest_npc = closest one that's nearby.
02361  */
02362 
02363 Actor *Game_window::find_witness
02364   (
02365   Actor *& closest_npc    // Closest one returned.
02366   )
02367   {
02368   Actor_vector npcs;      // See if someone is nearby.
02369   main_actor->find_nearby_actors(npcs, c_any_shapenum, 12);
02370   closest_npc = 0;    // Look for closest NPC.
02371   int closest_dist = 5000;
02372   Actor *witness = 0;   // And closest facing us.
02373   int closest_witness_dist = 5000;
02374   for (Actor_vector::const_iterator it = npcs.begin(); 
02375               it != npcs.end();++it)
02376     {
02377     Actor *npc = *it;
02378     if (npc->is_monster() || npc->is_in_party() ||
02379         (npc->get_framenum()&15) == Actor::sleep_frame ||
02380         npc->get_npc_num() >= num_npcs1)
02381       continue;
02382     int dist = npc->distance(main_actor);
02383     if (dist >= closest_witness_dist ||
02384         !Fast_pathfinder_client::is_grabable(
02385         npc->get_tile(), main_actor->get_tile()))
02386       continue;
02387           // Looking toward Avatar?
02388     int dir = npc->get_direction(main_actor);
02389     int facing = npc->get_dir_facing();
02390     int dirdiff = (dir - facing + 8)%8;
02391     if (dirdiff < 3 || dirdiff > 5)
02392       {   // Yes.
02393       witness = npc;
02394       closest_witness_dist = dist;
02395       }
02396     else if (dist < closest_dist)
02397       {
02398       closest_npc = npc;
02399       closest_dist = dist;
02400       }
02401     }
02402   return witness;
02403   }
02404 
02405 /*
02406  *  Handle theft.
02407  */
02408 
02409 void Game_window::theft
02410   (
02411   )
02412   {
02413           // See if in a new location.
02414   int cx = main_actor->get_cx(), cy = main_actor->get_cy();
02415   if (cx != theft_cx || cy != theft_cy)
02416     {
02417     theft_cx = cx;
02418     theft_cy = cy;
02419     theft_warnings = 0;
02420     }
02421   Actor *closest_npc;
02422   Actor *witness = find_witness(closest_npc);
02423   if (!witness)
02424     {
02425     if (closest_npc && rand()%2)
02426       closest_npc->say(item_names[heard_something]);
02427     return;     // Didn't get caught.
02428     }
02429   int dir = witness->get_direction(main_actor);
02430           // Face avatar.
02431   witness->change_frame(witness->get_dir_framenum(dir,
02432               Actor::standing));
02433   theft_warnings++;
02434   if (theft_warnings < 2 + rand()%3)
02435     {     // Just a warning this time.
02436     witness->say(first_theft, last_theft);
02437     return;
02438     }
02439   gump_man->close_all_gumps();  // Get gumps off screen.
02440   call_guards(witness);
02441   }
02442 
02443 /*
02444  *  Create a guard to arrest the Avatar.
02445  */
02446 
02447 void Game_window::call_guards
02448   (
02449   Actor *witness      // ->witness, or 0 to find one.
02450   )
02451   {
02452   Actor *closest;
02453   if (!witness && !(witness = find_witness(closest)))
02454     return;     // Nobody saw.
02455   witness->say(first_call_guards, last_call_guards);
02456           // Show guard running up.
02457   int gshape = Get_guard_shape(main_actor->get_tile());
02458           // Create it off-screen.
02459   Monster_actor *guard = Monster_actor::create(gshape,
02460     main_actor->get_tile() + Tile_coord(128, 128, 0));
02461   add_nearby_npc(guard);
02462   Tile_coord actloc = main_actor->get_tile();
02463   Tile_coord dest = Map_chunk::find_spot(actloc, 5, 
02464       guard->get_shapenum(), guard->get_framenum(), 1);
02465   if (dest.tx != -1)
02466     {
02467     int dir = Get_direction(dest.ty - actloc.ty,
02468             actloc.tx - dest.tx);
02469           
02470     signed char frames[2];  // Use frame for starting attack.
02471     frames[0] = guard->get_dir_framenum(dir, Actor::standing);
02472     frames[1] = guard->get_dir_framenum(dir, 3);
02473     Actor_action *action = new Sequence_actor_action(
02474         new Frames_actor_action(frames, 2),
02475         new Usecode_actor_action(0x625, guard,
02476           Usecode_machine::double_click));
02477     Schedule::set_action_sequence(guard, dest, action, true);
02478     }
02479   }
02480 
02481 /*
02482  *  Have nearby residents attack the Avatar.
02483  */
02484 
02485 void Game_window::attack_avatar
02486   (
02487   int create_guards   // # of extra guards to create.
02488   )
02489   {
02490   int gshape = Get_guard_shape(main_actor->get_tile());
02491   while (create_guards--)
02492     {
02493           // Create it off-screen.
02494     Monster_actor *guard = Monster_actor::create(gshape,
02495       main_actor->get_tile() + Tile_coord(128, 128, 0));
02496     add_nearby_npc(guard);
02497     guard->set_target(main_actor, true);
02498     guard->approach_another(main_actor);
02499     }
02500 
02501   Actor_vector npcs;    // See if someone is nearby.
02502   main_actor->find_nearby_actors(npcs, c_any_shapenum, 20);
02503   for (Actor_vector::const_iterator it = npcs.begin(); 
02504               it != npcs.end();++it)
02505     {
02506     Actor *npc = (Actor *) *it;
02507           // No monsters, except guards.
02508     if ((npc->get_shapenum() == gshape || !npc->is_monster()) && 
02509         !npc->is_in_party())
02510       npc->set_target(main_actor, true);
02511     }
02512   }
02513 
02514 /*
02515  *  Gain/lose focus.
02516  */
02517 
02518 void Game_window::get_focus
02519   (
02520   )
02521   {
02522   cout << "Game resumed" << endl;
02523   Audio::get_ptr()->resume_audio();
02524   focus = 1; 
02525   tqueue->resume(Game::get_ticks());
02526   }
02527 void Game_window::lose_focus
02528   (
02529   )
02530   {
02531   if (!focus)
02532     return;     // Fixes SDL bug.
02533   cout << "Game paused" << endl;
02534 
02535   string str;
02536   config->value("config/audio/disablepause", str, "no");
02537   if (str == "no")
02538     Audio::get_ptr()->pause_audio();
02539 
02540   focus = false; 
02541   tqueue->pause(Game::get_ticks());
02542   }
02543 
02544 
02545 /*
02546  *  Prepare for game
02547  */
02548 
02549 void Game_window::setup_game
02550   (
02551   )
02552   {
02553   map->init();
02554         // Init. current 'tick'.
02555   Game::set_ticks(SDL_GetTicks());
02556   init_actors();    // Set up actors if not already done.
02557         // This also sets up initial 
02558         // schedules and positions.
02559 
02560   CYCLE_RED_PLASMA();
02561 
02562   usecode->read();    // Read the usecode flags
02563   CYCLE_RED_PLASMA();
02564 
02565   if (Game::get_game_type() == BLACK_GATE)
02566   {
02567     string yn;    // Override from config. file.
02568           // Skip intro. scene?
02569     config->value("config/gameplay/skip_intro", yn, "no");
02570     if (yn == "yes")
02571       usecode->set_global_flag(
02572         Usecode_machine::did_first_scene, 1);
02573 
02574           // Should Avatar be visible?
02575     if (usecode->get_global_flag(Usecode_machine::did_first_scene))
02576       main_actor->clear_flag(Obj_flags::dont_move);
02577     else
02578       main_actor->set_flag(Obj_flags::dont_move);
02579   }
02580 
02581   CYCLE_RED_PLASMA();
02582 
02583   // Fade out & clear screen before palette change
02584   pal->fade_out(c_fade_out_time);
02585   clear_screen(true);
02586 #ifdef RED_PLASMA
02587   load_palette_timer = 0;
02588 #endif
02589 
02590   // note: we had to stop the plasma here already, because init_readied
02591   // and activate_eggs may update the screen through usecode functions
02592   // (Helm of Light, for example)
02593 
02594   Actor *party[9];
02595   int cnt = get_party(party, 1);  // Get entire party.
02596   for (int i = 0; i < cnt; i++) // Init. rings.
02597   {
02598     party[i]->init_readied();
02599   }
02600   time_stopped = 0;
02601 //+++++The below wasn't prev. done by ::read(), so maybe it should be
02602 //+++++controlled by a 'first-time' flag.
02603         // Want to activate first egg.
02604   Map_chunk *olist = main_actor->get_chunk();
02605   olist->setup_cache();
02606 
02607   Tile_coord t = main_actor->get_tile();
02608           // Do them immediately.
02609   olist->activate_eggs(main_actor, t.tx, t.ty, t.tz, -1, -1, true);
02610   
02611   // Force entire repaint.
02612   set_all_dirty();
02613   painted = true;     // Main loop uses this.
02614   gump_man->close_all_gumps(true);    // Kill gumps.
02615   Face_stats::load_config(config);
02616 
02617   // Set palette for time-of-day.
02618   clock->set_palette();
02619   pal->fade(6, 1, -1);    // Fade back in.
02620 }
02621 
02622 
02623 
02624 void Game_window::plasma(int w, int h, int x, int y, int startc, int endc)
02625 {
02626   Image_buffer8 *ibuf = get_win()->get_ib8();
02627 
02628   ibuf->fill8(startc, w, h, x, y);
02629 
02630   for (int i=0; i < w*h; i++) {
02631     Uint8 pc = startc + rand()%(endc-startc+1);
02632     int px = x + rand()%w;
02633     int py = y + rand()%h;
02634 
02635     for (int j=0; j < 6; j++) {
02636       int px2 = px + rand()%17 - 8;
02637       int py2 = py + rand()%17 - 8;
02638       ibuf->fill8(pc, 3, 1, px2 - 1, py2);
02639       ibuf->fill8(pc, 1, 3, px2, py2 - 1);
02640     }
02641   }
02642   painted = true;
02643 }
02644 
02645 /*
02646  *  Chunk caching emulation:  swap out chunks which are now at least
02647  *  3 chunks away.
02648  */
02649 void Game_window::emulate_cache(int oldx, int oldy, int newx, int newy)
02650 {
02651   if (oldx == -1 || oldy == -1)
02652     return;     // Seems like there's nothing to do.
02653           // Cancel weather from eggs that are
02654           //   far away.
02655   effects->remove_weather_effects(120);
02656           // Cancel scripts 4 chunks from this.
02657   Usecode_script::purge(Tile_coord(newx*c_tiles_per_chunk,
02658       newy*c_tiles_per_chunk, 0), 4*c_tiles_per_chunk);
02659   int nearby[5][5];   // Chunks within 3.
02660   // Set to 0
02661   // No casting _should_ be necessary at this point.
02662   // Who needs this?
02663   memset(reinterpret_cast<char*>(nearby), 0, sizeof(nearby));
02664           // Figure old range.
02665   int old_minx = c_num_chunks + oldx - 2, 
02666       old_maxx = c_num_chunks + oldx + 2;
02667   int old_miny = c_num_chunks + oldy - 2, 
02668       old_maxy = c_num_chunks + oldy + 2;
02669           // Figure new range.
02670   int new_minx = c_num_chunks + newx - 2, 
02671       new_maxx = c_num_chunks + newx + 2;
02672   int new_miny = c_num_chunks + newy - 2, 
02673       new_maxy = c_num_chunks + newy + 2;
02674   // Now we write what we are now near
02675   int x, y;
02676   for (y = new_miny; y <= new_maxy; y++) 
02677     {
02678     if (y > old_maxy)
02679       break;    // Beyond the end.
02680     int dy = y - old_miny;
02681     if (dy < 0)
02682       continue;
02683     assert(dy < 5);
02684     for (x = new_minx; x <= new_maxx; x++)
02685       {
02686       if (x > old_maxx)
02687         break;
02688       int dx = x - old_minx;
02689       if (dx >= 0)
02690         {
02691         assert(dx < 5);
02692         nearby[dx][dy] = 1;
02693         }
02694       }
02695     }
02696   // Swap out chunks no longer nearby (0).
02697   Game_object_vector removes;
02698   for (y = 0; y < 5; y++)
02699     for (x = 0; x < 5; x++)
02700       {
02701       if (nearby[x][y] != 0)
02702         continue;
02703       Map_chunk *list = map->get_chunk_safely(
02704         (old_minx + x)%c_num_chunks,
02705         (old_miny + y)%c_num_chunks);
02706       if (!list) continue;
02707       Object_iterator it(list->get_objects());
02708       Game_object *each;
02709       while ((each = it.get_next()) != 0)
02710         {
02711         if (each->is_egg())
02712           ((Egg_object *) each)->reset();
02713         else if (each->get_flag(Obj_flags::is_temporary))
02714           removes.push_back(each);
02715         }
02716       }
02717   for (Game_object_vector::const_iterator it=removes.begin(); 
02718             it!=removes.end(); ++it)
02719     {
02720 #ifdef DEBUG
02721     Tile_coord t = (*it)->get_tile();
02722     cout << "Culling object: " << (*it)->get_name() <<
02723       '(' << (void *)(*it) << ")@" << 
02724       t.tx << "," << t.ty << "," << t.tz <<endl;
02725 #endif
02726     (*it)->delete_contents();  // first delete item's contents
02727     (*it)->remove_this(0);
02728     }
02729 
02730     get_map()->cache_out(newx, newy);
02731 
02732     // Could cause some problems
02733     removed->flush();
02734   }
02735 
02736 // Tests to see if a move goes out of range of the actors superchunk
02737 bool Game_window::emulate_is_move_allowed(int tx, int ty)
02738 {
02739   int ax = camera_actor->get_cx() / c_chunks_per_schunk;
02740   int ay = camera_actor->get_cy() / c_chunks_per_schunk;
02741   tx /= c_tiles_per_schunk;
02742   ty /= c_tiles_per_schunk;
02743 
02744   int difx = ax - tx;
02745   int dify = ay - ty;
02746   
02747   if (difx < 0) difx = -difx;
02748   if (dify < 0) dify = -dify;
02749 
02750   // Is it within 1 superchunk range?
02751   if ((!difx || difx == 1 || difx == c_num_schunks || difx == c_num_schunks-1) && 
02752     (!dify || dify == 1 || dify == c_num_schunks || dify == c_num_schunks-1))
02753     return true;
02754 
02755   return false;
02756 }
02757 
02758 //create mini-screenshot (96x60) for use in savegames
02759 Shape_file* Game_window::create_mini_screenshot()
02760 {
02761   Shape_file* sh = 0;
02762   Shape_frame* fr = 0;
02763   unsigned char* img = 0;
02764 
02765   set_all_dirty();
02766   render->paint_map(0, 0, get_width(), get_height());
02767 
02768   img = win->mini_screenshot();
02769   
02770   if (img) {
02771     fr = new Shape_frame();
02772     fr->xleft = 0;
02773     fr->yabove = 0;
02774     fr->xright = 95;
02775     fr->ybelow = 59;
02776     fr->create_rle(img, 96, 60);
02777     fr->rle = 1;
02778     delete [] img;
02779 
02780     sh = new Shape_file(fr);
02781   }
02782 
02783   set_all_dirty();
02784   paint();
02785   return sh;
02786 }
02787 
02788 #ifdef RED_PLASMA
02789 
02790 #define BG_PLASMA_START_COLOR 128
02791 #define BG_PLASMA_CYCLE_RANGE 80
02792 
02793 #define SI_PLASMA_START_COLOR 16
02794 #define SI_PLASMA_CYCLE_RANGE 96
02795 
02796 void Game_window::setup_load_palette()
02797 {
02798   if (load_palette_timer != 0)
02799     return;
02800 
02801   if (Game::get_game_type()==BLACK_GATE)
02802     {
02803         plasma_start_color = BG_PLASMA_START_COLOR;
02804         plasma_cycle_range = BG_PLASMA_CYCLE_RANGE;
02805     }
02806   else // Default:  if (Game::get_game_type()==SERPENT_ISLE)
02807     {
02808         plasma_start_color = SI_PLASMA_START_COLOR;
02809         plasma_cycle_range = SI_PLASMA_CYCLE_RANGE;
02810     }
02811     
02812     // Put up the plasma to the screen
02813     plasma(get_width(), get_height(), 0, 0, plasma_start_color, plasma_start_color+plasma_cycle_range-1);
02814 
02815      // Load the palette
02816   if (Game::get_game_type()==BLACK_GATE)
02817     pal->load("<STATIC>/intropal.dat",2);
02818   else if (Game::get_game_type()==SERPENT_ISLE)
02819     pal->load(MAINSHP_FLX,1);
02820 
02821   pal->apply();
02822   load_palette_timer = SDL_GetTicks();
02823 }
02824 
02825 void Game_window::cycle_load_palette()
02826 {
02827   if (load_palette_timer == 0)
02828     return;
02829   uint32 ticks = SDL_GetTicks();
02830   if(ticks > load_palette_timer+75)
02831   {
02832     for(int i = 0; i < 4; ++i)
02833       get_win()->rotate_colors(plasma_start_color, plasma_cycle_range, 1);
02834     show(true);
02835 
02836     // We query the timer here again, as the blit can take easily 50 ms and more
02837     // depending on the chosen scaler and the overall system speed
02838     load_palette_timer = SDL_GetTicks();
02839   }
02840 }
02841 #endif

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