exult.cc

Go to the documentation of this file.
00001 
00007 /*
00008  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00009  *  Copyright (C) 2000-2004  The Exult Team
00010  *
00011  *  This program is free software; you can redistribute it and/or modify
00012  *  it under the terms of the GNU General Public License as published by
00013  *  the Free Software Foundation; either version 2 of the License, or
00014  *  (at your option) any later version.
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public License
00022  *  along with this program; if not, write to the Free Software
00023  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00024  */
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include <config.h>
00028 #endif
00029 
00030 #ifndef ALPHA_LINUX_CXX
00031 #  include <cstdlib>
00032 #  include <cctype>
00033 #endif
00034 
00035 #include <SDL.h>
00036 
00037 #define Font _XFont_
00038 #include <SDL_syswm.h>
00039 #undef Font
00040 
00041 #ifdef USE_EXULTSTUDIO  /* Only needed for communication with exult studio */
00042 #if HAVE_SYS_TIME_H
00043 #include <sys/time.h>
00044 #endif
00045 #ifdef WIN32
00046 #include "windrag.h"
00047 #elif defined(XWIN)
00048 #include "xdrag.h"
00049 #endif
00050 #include "server.h"
00051 #include "chunks.h"
00052 #include "chunkter.h"
00053 #endif
00054 
00055 #if (defined(USECODE_DEBUGGER) && defined(XWIN))
00056 #include <csignal>
00057 #endif
00058 
00059 #include "Audio.h"
00060 #include "Configuration.h"
00061 #include "Gump_manager.h"
00062 #include "Scroll_gump.h"
00063 #include "actors.h"
00064 #include "args.h"
00065 #include "cheat.h"
00066 #include "effects.h"
00067 #include "exult.h"
00068 #include "exultmenu.h"
00069 #include "fnames.h"
00070 #include "font.h"
00071 #include "game.h"
00072 #include "gamewin.h"
00073 #include "gamemap.h"
00074 #include "gump_utils.h"
00075 #include "keyactions.h"
00076 #include "keys.h"
00077 #include "mouse.h"
00078 #include "ucmachine.h"
00079 #include "utils.h"
00080 #include "version.h"
00081 #include "u7drag.h"
00082 #include "drag.h"
00083 #include "palette.h"
00084 #include "glshape.h"
00085 #include "combat_opts.h"
00086 
00087 #include "exult_flx.h"
00088 #include "exult_bg_flx.h"
00089 #include "exult_si_flx.h"
00090 #include "crc.h"
00091 
00092 #ifndef UNDER_CE
00093 using std::atof;
00094 using std::cerr;
00095 using std::cout;
00096 using std::endl;
00097 using std::atexit;
00098 using std::exit;
00099 using std::toupper;
00100 using std::string;
00101 using std::vector;
00102 #endif
00103 
00104 Configuration *config = 0;
00105 KeyBinder *keybinder = 0;
00106 
00107 /*
00108  *  Globals:
00109  */
00110 Game_window *gwin = 0;
00111 quitting_time_enum quitting_time = QUIT_TIME_NO;
00112 
00113 bool intrinsic_trace = false;   // Do we trace Usecode-intrinsics?
00114 int usecode_trace = 0;    // Do we trace Usecode-instructions?
00115               // 0 = no, 1 = short, 2 = long
00116 bool combat_trace = false; // show combat messages?
00117 
00118 // Save game compression level
00119 int save_compression = 1;
00120 bool ignore_crc = false;
00121 
00122 const std::string c_empty_string;
00123 
00124 #if 0 && USECODE_DEBUGGER
00125 bool  usecode_debugging=false;  // Do we enable the usecode debugger?
00126 extern void initialise_usecode_debugger(void);
00127 #endif
00128 
00129 struct resolution {
00130   int x;
00131   int y;
00132   int scale;
00133 } res_list[] = { 
00134   { 320, 200, 1 },
00135   { 320, 240, 1 },
00136   { 400, 300, 1 },
00137   { 320, 200, 2 },
00138   { 320, 240, 2 },
00139   { 400, 300, 2 },
00140   { 512, 384, 1 },
00141   { 640, 480, 1 },
00142   { 800, 600, 1 }
00143 };
00144 int num_res = sizeof(res_list)/sizeof(struct resolution);
00145 int current_res = 0;
00146 
00147 #ifdef XWIN
00148 int xfd = 0;      // X connection #.
00149 static class Xdnd *xdnd = 0;
00150 #elif defined(WIN32)
00151 static HWND hgwin;
00152 static class Windnd *windnd = 0;
00153 #endif
00154 
00155 
00156 /*
00157  *  Local functions:
00158  */
00159 static int exult_main(const char *);
00160 static void Init();
00161 static int Play();
00162 static int Get_click(int& x, int& y, char *chr, bool drag_ok, Paintable *p);
00163 static int find_resolution(int w, int h, int s);
00164 static void set_resolution (int new_res, bool save);
00165 #ifdef USE_EXULTSTUDIO
00166 static void Move_dragged_shape(int shape, int frame, int x, int y,
00167         int prevx, int prevy, bool show);
00168 static void Move_dragged_combo(int xtiles, int ytiles, int tiles_right,
00169   int tiles_below, int x, int y, int prevx, int prevy, bool show);
00170 static void Drop_dragged_shape(int shape, int frame, int x, int y, void *d);
00171 static void Drop_dragged_chunk(int chunknum, int x, int y, void *d);
00172 static void Drop_dragged_combo(int cnt, U7_combo_data *combo, 
00173               int x, int y, void *d);
00174 #endif
00175 static void BuildGameMap();
00176 static void Handle_events();
00177 static void Handle_event(SDL_Event& event);
00178 static void get_game_paths(const string &gametitle);
00179 
00180 
00181 
00182 /*
00183  *  Statics:
00184  */
00185 static bool dragging = false;   // Object or gump being moved.
00186 static bool dragged = false;    // Flag for when obj. moved.
00187 static bool run_bg = false;   // skip menu and run bg
00188 static bool run_si = false;   // skip menu and run si
00189 
00190 static string arg_gamename = "default"; // cmdline arguments
00191 static string arg_configfile = "";
00192 static int arg_buildmap = -1;
00193 static bool arg_nomenu = false;
00194 
00195 /*
00196  *  A handy breakpoint.
00197  */
00198 
00199 static void Breakpoint
00200   (
00201   )
00202   {
00203   return;
00204   }
00205 
00206 #if (defined(XWIN) && HAVE_SIGNAL_H && HAVE_SYS_WAIT_H)
00207 
00208 // a SIGCHLD handler to properly clean up forked playmidi processes (if any)
00209 
00210 #include <sys/wait.h>
00211 #include <signal.h>
00212 void sigchld_handler(int sig)
00213 {
00214   waitpid(-1, 0, WNOHANG);
00215 }
00216 
00217 #endif
00218 
00219 
00220 /*
00221  *  Main program.
00222  */
00223 
00224 int main
00225   (
00226   int argc,
00227   char *argv[]
00228   )
00229 {
00230 
00231 #ifdef BEOS
00232   // get exult path
00233   int counti;
00234   char datapath[256];
00235   for (counti=strlen(argv[0]) ; argv[0][counti]!='/' ; counti--);
00236   strncpy(datapath, argv[0], counti);
00237   chdir(datapath);
00238 #endif
00239 
00240 #if (defined(XWIN) && HAVE_SIGNAL_H && HAVE_SYS_WAIT_H)
00241   signal(SIGCHLD, sigchld_handler);
00242 #endif
00243 
00244 
00245   bool  needhelp=false;
00246   bool  showversion=false;
00247   int   result;
00248   Args    parameters;
00249 
00250   // Declare everything from the commandline that we're interested in.
00251   parameters.declare("-h",&needhelp,true);
00252   parameters.declare("--help",&needhelp,true);
00253   parameters.declare("/?",&needhelp,true);
00254   parameters.declare("/h",&needhelp,true);
00255   parameters.declare("--bg",&run_bg,true);
00256   parameters.declare("--si",&run_si,true);
00257   parameters.declare("--nomenu", &arg_nomenu, true);
00258   parameters.declare("-v",&showversion,true);
00259   parameters.declare("--version",&showversion,true);
00260   parameters.declare("--game",&arg_gamename,"default");
00261   parameters.declare("--buildmap",&arg_buildmap,-1);
00262   parameters.declare("--nocrc",&ignore_crc,true);
00263   parameters.declare("-c",&arg_configfile,"");
00264 
00265   // Process the args
00266   parameters.process(argc,argv);
00267 
00268   if(needhelp)
00269   {
00270     cerr << "Usage: exult [--help|-h] [-v|--version] [-c configfile]"<<endl
00271        << "             [--bg|--si] [--buildmap 0|1|2] [--nocrc]" << endl
00272        << "--help\t\tShow this information" << endl
00273        << "--version\tShow version info" << endl
00274        << " -c configfile\tSpecify alternate config file" << endl
00275        << "--bg\t\tSkip menu and run Black Gate" << endl
00276        << "--si\t\tSkip menu and run Serpent Isle" << endl
00277        << "--nomenu\tSkip BG/SI game menu" << endl
00278        << "--game <game>\tRun original game" << endl
00279        << "--buildmap\tCreate a fullsize map of the game world in u7map??.pcx" << endl
00280        << "\t\t(0 = all roofs, 1 = no level 2 roofs, 2 = no roofs)" << endl
00281        << "\t\tonly valid when used together with --bg or --si" << endl
00282        << "\t\t(WARNING: requires big amounts of RAM, HD space and time!)" << endl
00283        << "--nocrc\t\tDon't check crc's of .flx files" << endl;
00284       
00285     exit(1);
00286   }
00287   if (run_bg && run_si) {
00288     cerr << "Error: You may only specify either -bg or -si!" << 
00289                   endl;
00290     exit(1);
00291   }
00292 
00293   if(showversion) {
00294     getVersionInfo(cerr);
00295     return 0;
00296   }
00297   
00298   try
00299   {
00300     result = exult_main(argv[0]);
00301   }
00302   catch( const quit_exception & e )
00303     {
00304         result = 0;
00305     }
00306   catch( const exult_exception & e )
00307   {
00308     cerr << "============================" << endl <<
00309       "An exception occured: " << endl << 
00310       e.what() << endl << 
00311       "errno: " << e.get_errno() << endl;
00312     if( e.get_errno() != 0)
00313       perror("Error Description");
00314     cerr << "============================" << endl;
00315   }
00316   
00317   return result;
00318 }
00319 
00320 
00321 /*
00322  *  Main program.
00323  */
00324 
00325 int exult_main(const char *runpath)
00326 {
00327   string data_path;
00328   string music_path;
00329 
00330   // output version info
00331   getVersionInfo(cout);
00332 
00333   // Read in configuration file
00334   config = new Configuration;
00335 
00336   if (arg_configfile != "") {
00337     config->read_abs_config_file(arg_configfile);
00338   } else {
00339     config->read_config_file(USER_CONFIGURATION_FILE);
00340   }
00341 
00342   // Setup virtual directories
00343   config->value("config/disk/data_path",data_path,EXULT_DATADIR);
00344   add_system_path("<DATA>", data_path);
00345   if (!U7exists("<DATA>/exult.flx"))
00346   {
00347     add_system_path("<DATA>", EXULT_DATADIR);
00348     if (!U7exists("<DATA>/exult.flx"))
00349     {
00350       add_system_path("<DATA>", "data");
00351       if(!U7exists("<DATA>/exult.flx"))
00352       {
00353         char *sep = std::strrchr(runpath,'/');
00354         if (!sep) sep = std::strrchr(runpath,'\\');
00355         int plen = sep-runpath;
00356         char *dpath = new char[plen+10];
00357         std::strncpy(dpath, runpath, plen+1);
00358         dpath[plen+1] = 0;
00359         std::strcat(dpath,"data");
00360         cerr << "dpath = " << dpath << endl;
00361         add_system_path("<DATA>",dpath);
00362         if(!U7exists("<DATA>/exult.flx"))
00363         {
00364           // We've tried them all...
00365           cerr << "Could not find 'exult.flx' anywhere." << endl; 
00366           cerr << "Please make sure Exult is correctly installed," << endl;
00367           cerr << "and the Exult data path is specified in the configuration file." << endl;
00368           cerr << "(See the README file for more information)" << endl;
00369           exit(-1);
00370         }
00371       }
00372     }
00373   }
00374   std::string default_music = get_system_path("<DATA>/music");
00375   config->value("config/disk/music_path",music_path,default_music.c_str());
00376 
00377   add_system_path("<MUSIC>", music_path);
00378   add_system_path("<STATIC>", "static");
00379   add_system_path("<GAMEDAT>", "gamedat");
00380   add_system_path("<PATCH>", "patch");
00381 //  add_system_path("<SAVEGAME>", "savegame");
00382   add_system_path("<SAVEGAME>", ".");
00383 
00384   std::cout << "Exult path settings:" << std::endl;
00385   std::cout << "Data          : " << get_system_path("<DATA>") << std::endl;
00386   std::cout << "Digital music : " << get_system_path("<MUSIC>") << std::endl;
00387   std::cout << std::endl;
00388 
00389 
00390   // Check CRCs of our .flx files
00391   bool crc_ok = true;
00392   uint32 crc = crc32_syspath("<DATA>/exult.flx");
00393   if (crc != EXULT_FLX_CRC32) {
00394     crc_ok = false;
00395     cerr << "exult.flx has a wrong checksum!" << endl;
00396   }
00397   if (U7exists("<DATA>/exult_bg.flx")) {
00398     if (crc32_syspath("<DATA>/exult_bg.flx") != EXULT_BG_FLX_CRC32) {
00399       crc_ok = false;
00400       cerr << "exult_bg.flx has a wrong checksum!" << endl;
00401     }
00402   }
00403   if (U7exists("<DATA>/exult_si.flx")) {
00404     if (crc32_syspath("<DATA>/exult_si.flx") != EXULT_SI_FLX_CRC32) {
00405       crc_ok = false;
00406       cerr << "exult_si.flx has a wrong checksum!" << endl;
00407     }
00408   }
00409 
00410   bool config_ignore_crc;
00411   config->value("config/disk/no_crc",config_ignore_crc);
00412   ignore_crc |= config_ignore_crc;
00413 
00414   if (!ignore_crc && !crc_ok) {
00415     cerr << "This usually means the file(s) mentioned above are "
00416        << "from a different version" << endl
00417        << "of Exult than this one. Please re-install Exult" << endl
00418        << endl
00419        << "(Note: if you modified the .flx files yourself, "
00420        << "you can skip this check" << endl
00421        << "by passing the --nocrc parameter.)" << endl;
00422     
00423     return 1;
00424   }
00425 
00426 
00427   // Convert from old format if needed
00428   vector<string> vs=config->listkeys("config/disk/game",false);
00429   if(vs.size()==0)
00430   {
00431     // Convert from the older format
00432     string data_directory;
00433     config->value("config/disk/u7path",data_directory,".");
00434     config->set("config/disk/game/blackgate/path",data_directory,true);
00435     const string  s("blackgate");
00436     config->set("config/disk/game/blackgate/title",s,true);
00437     vs.push_back(s);
00438   }
00439 
00440   get_game_paths("blackgate");
00441   get_game_paths("serpentisle");
00442 
00443   // Enable tracing of intrinsics?
00444   config->value("config/debug/trace/intrinsics",intrinsic_trace);
00445 
00446   // Enable tracing of UC-instructions?
00447   string uctrace;
00448   config->value("config/debug/trace/usecode", uctrace, "no");
00449   to_uppercase(uctrace);
00450   if (uctrace == "YES")
00451     usecode_trace = 1;
00452   else if (uctrace == "VERBOSE")
00453     usecode_trace = 2;
00454   else
00455     usecode_trace = 0;
00456 
00457   config->value("config/debug/trace/combat",combat_trace);
00458 
00459   // Save game compression level
00460   config->value("config/disk/save_compression_level", save_compression, 1);
00461   if (save_compression < 0 || save_compression > 2) save_compression = 1;
00462   config->set("config/disk/save_compression_level", save_compression, true);
00463 
00464 #if 0 && USECODE_DEBUGGER
00465   // Enable usecode debugger
00466   config->value("config/debug/debugger/enable",usecode_debugging);
00467   initialise_usecode_debugger();
00468 #endif
00469 
00470 #if (defined(USECODE_DEBUGGER) && defined(XWIN))
00471   signal(SIGUSR1, SIG_IGN);
00472 #endif
00473 
00474   cheat.init();
00475 
00476 #ifdef UNDER_CE
00477   GXOpenInput();
00478 
00479   GXKeyList keys = GXGetDefaultKeys(GX_LANDSCAPEKEYS);
00480 
00481   std::cout << "Up " << keys.vkUp << std::endl;
00482   std::cout << "Down " << keys.vkDown << std::endl;
00483   std::cout << "Left " << keys.vkLeft << std::endl;
00484   std::cout << "Right " << keys.vkRight << std::endl;
00485   std::cout << "A " << keys.vkA << std::endl;
00486   std::cout << "B " << keys.vkB << std::endl;
00487   std::cout << "C " << keys.vkC << std::endl;
00488   std::cout << "Start " << keys.vkStart << std::endl;
00489 #endif
00490 
00491   Init();       // Create main window.
00492 
00493   cheat.finish_init();
00494 
00495   Mouse::mouse = new Mouse(gwin);
00496   Mouse::mouse->set_shape(Mouse::hand);
00497 
00498   int result = Play();    // start game
00499   
00500 #ifdef UNDER_CE
00501   GXCloseInput();
00502 #endif
00503 
00504 #if defined(WIN32) && defined(USE_EXULTSTUDIO)
00505   // Currently, leaving the game results in destruction of the window.
00506   //  Maybe sometime in the future, there is an option like "return to
00507   //  main menu and select another scenario". Becaule DnD isn't registered until
00508   //  you really enter the game, we remove it here to prevent possible bugs
00509   //  invilved with registering DnD a second time over an old variable.
00510     RevokeDragDrop(hgwin);
00511   delete windnd;
00512 #endif
00513 
00514   return result;
00515 }
00516 
00517 /*
00518  *  Calculate paths for the given game, using the config file and
00519  *  falling back to defaults if necessary.  These are stored in
00520  *  per-game system_path entries, which are then used later once the
00521  *  game is selected.
00522  */
00523 static void get_game_paths(const string &gametitle)
00524 {
00525   std::string data_directory, static_dir, gamedat_dir, savegame_dir,
00526     default_dir, system_path_tag(to_uppercase(gametitle)),
00527     config_path("config/disk/game/" + gametitle + "/path");
00528 
00529   config->value(config_path.c_str(), data_directory, ".");
00530   if (data_directory == ".")
00531     config->set(config_path.c_str(), data_directory, true);
00532 #if 0
00533   cout << "setting " << gametitle
00534     << " game directories to: " << data_directory << endl;
00535 #endif
00536 
00537   config_path = "config/disk/game/" + gametitle + "/static_path";
00538   default_dir = data_directory + "/static";
00539   config->value(config_path.c_str(), static_dir, default_dir.c_str());
00540   add_system_path("<" + system_path_tag + "_STATIC>", static_dir);
00541 #if 0
00542   cout << "setting " << gametitle
00543     << " static directory to: " << static_dir << endl;
00544 #endif
00545 
00546   const char *home = 0;   // Will get $HOME.
00547   string home_game("");   // Gets $HOME/.exult/gametitle.
00548   config_path = "config/disk/game/" + gametitle + "/gamedat_path";
00549   default_dir = data_directory + "/gamedat";
00550   config->value(config_path.c_str(), gamedat_dir, "");
00551 #if (!defined(WIN32) && !defined(MACOS))
00552   if (gamedat_dir == "" &&  // Not set?
00553           // And default doesn't exist?
00554       !U7exists(default_dir.c_str()) && (home = getenv("HOME")) != 0)
00555     {
00556     home_game = home;
00557     home_game += "/.exult";
00558           // Create $HOME/.exult/gametitle.
00559     U7mkdir(home_game.c_str(), 0755);
00560     home_game = home_game + '/' + gametitle;
00561     U7mkdir(home_game.c_str(), 0755);
00562           // Successfully created dir?
00563     if (U7exists(home_game.c_str()))
00564       {   // Use $HOME/.exult/gametitle/gamedat.
00565       gamedat_dir = home_game + "/gamedat";
00566       config->set(config_path.c_str(), gamedat_dir.c_str(),
00567                 true);
00568       }
00569     else
00570       home_game = ""; // Failed.
00571     }
00572 #endif
00573   if (gamedat_dir == "")    // Didn't create it in $HOME/.exult?
00574     gamedat_dir = default_dir;
00575   add_system_path("<" + system_path_tag + "_GAMEDAT>", gamedat_dir);
00576 #if 0
00577   cout << "setting " << gametitle
00578     << " gamedat directory to: " << gamedat_dir << endl;
00579 #endif
00580 
00581   config_path = "config/disk/game/" + gametitle + "/savegame_path";
00582   if (home_game == "")
00583     config->value(config_path.c_str(), savegame_dir, 
00584             data_directory.c_str());
00585   else
00586     {     // Store saves under $HOME/....
00587     config->value(config_path.c_str(), savegame_dir,
00588             home_game.c_str());
00589     config->set(config_path.c_str(), savegame_dir.c_str(), true);
00590     }
00591   add_system_path("<" + system_path_tag + "_SAVEGAME>", savegame_dir);
00592 #if 0
00593   cout << "setting " << gametitle
00594     << " savegame directory to: " << savegame_dir << endl;
00595 #endif
00596 
00597   config_path = "config/disk/game/" + gametitle + "/patch";
00598   string patch_directory;
00599   default_dir = data_directory + "/patch";
00600   config->value(config_path.c_str(), patch_directory, 
00601               default_dir.c_str());
00602   add_system_path("<" + system_path_tag + "_PATCH>", patch_directory.c_str());
00603 }
00604 
00605 namespace ExultIcon {
00606   #include "exulticon.h"
00607 }
00608 
00609 static void SetIcon()
00610 {
00611   SDL_Surface* iconsurface = SDL_CreateRGBSurfaceFrom(ExultIcon::header_data,
00612                             ExultIcon::width,
00613                             ExultIcon::height,
00614                             8,
00615                             ExultIcon::width,
00616                             0, 0, 0, 0);
00617   SDL_Color iconpal[256];
00618   for (int i = 0; i < 256; ++i) {
00619     iconpal[i].r = ExultIcon::header_data_cmap[i][0];
00620     iconpal[i].g = ExultIcon::header_data_cmap[i][1];
00621     iconpal[i].b = ExultIcon::header_data_cmap[i][2];
00622   }
00623   SDL_SetPalette(iconsurface, SDL_LOGPAL, iconpal, 0, 256);
00624 
00625   SDL_SetColorKey(iconsurface, SDL_SRCCOLORKEY, 0);
00626 
00627   SDL_WM_SetIcon(iconsurface, 0);
00628 
00629   SDL_FreeSurface(iconsurface);
00630 }
00631 
00632 
00633 /*
00634  *  Initialize and create main window.
00635  */
00636 static void Init
00637   (
00638   )
00639 {
00640   Uint32 init_flags = SDL_INIT_VIDEO|SDL_INIT_TIMER;
00641 #ifdef NO_SDL_PARACHUTE
00642   init_flags |= SDL_INIT_NOPARACHUTE;
00643 #endif
00644   if (SDL_Init(init_flags) < 0)
00645   {
00646     cerr << "Unable to initialize SDL: " << SDL_GetError() << endl;
00647     exit(-1);
00648   }
00649   std::atexit(SDL_Quit);
00650   
00651   SDL_SysWMinfo info;   // Get system info.
00652         SDL_GetWMInfo(&info);
00653 #ifdef USE_EXULTSTUDIO
00654           // Want drag-and-drop events.
00655   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
00656 #endif
00657           // KBD repeat should be nice.
00658   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 
00659             SDL_DEFAULT_REPEAT_INTERVAL);
00660   SDL_ShowCursor(0);
00661   SDL_VERSION(&info.version);
00662 
00663   SetIcon();
00664 
00665   int w, h, sc, sclr;
00666 
00667   // Default resolution is 320x200 with 2x scaling
00668   w = 320;
00669   h = 200;
00670   sc = 2;
00671   sclr = Image_window::SaI;
00672 
00673   int sw, sh, scaleval;
00674   string gr, gg, gb, scaler;
00675   config->value("config/video/width", sw, w);
00676   config->value("config/video/height", sh, h);
00677   config->value("config/video/scale_method", scaler, "---");
00678   config->value("config/video/gamma/red", gr, "1.0");
00679   config->value("config/video/gamma/green", gg, "1.0");
00680   config->value("config/video/gamma/blue", gb, "1.0");
00681 
00682   config->value("config/video/scale", scaleval, sc);
00683   sclr = Image_window::get_scaler_for_name(scaler);
00684   if (sclr == Image_window::NoScaler) config->set("config/video/scale_method","2xSaI",true);
00685 
00686   if (arg_buildmap >= 0)
00687     BuildGameMap();
00688 
00689   Image_window8::set_gamma(atof(gr.c_str()), atof(gg.c_str()), atof(gb.c_str())); 
00690   gwin = new Game_window(sw, sh, scaleval, sclr);
00691   current_res = find_resolution(sw, sh, scaleval);
00692   Audio::Init();
00693 
00694   bool disable_fades;
00695   config->value("config/video/disable_fades", disable_fades, false);
00696   gwin->get_pal()->set_fades_enabled(!disable_fades);
00697 
00698 
00699   SDL_SetEventFilter(0);
00700   // Show the banner
00701   Exult_Game mygame;
00702   game = 0;
00703 
00704   // Figure out all the games' paths, before we store them.  That
00705   // way, we don't have to recalculate them if we come back to the
00706   // main menu and make another selection.
00707   if (arg_gamename != "default") {
00708     // The user gave us a game title, so load that one's
00709     // paths.
00710     get_game_paths(arg_gamename);
00711   }
00712 
00713   store_system_paths();
00714 
00715   do {
00716     reset_system_paths();
00717     fontManager.reset();
00718     U7FileManager::get_ptr()->reset();
00719     const char *title = 0;
00720 
00721     if(game)
00722       delete game;
00723     
00724     if (run_bg) {
00725       mygame = BLACK_GATE;
00726       run_bg = false;
00727     } else if (run_si) {
00728       mygame = SERPENT_ISLE;
00729       run_si = false;
00730     } else if (arg_buildmap < 0 && arg_gamename != "default") {
00731       mygame = EXULT_DEVEL_GAME;
00732       title = arg_gamename.c_str();
00733     } else {
00734       ExultMenu exult_menu(gwin);
00735       mygame = exult_menu.run();
00736     }
00737     Game::create_game(mygame, title);
00738 
00739     Audio::get_ptr()->Init_sfx();
00740     
00741           // Skip splash screen?
00742     bool skip_splash;
00743     config->value("config/gameplay/skip_splash", skip_splash);
00744     if(!skip_splash && 
00745           // Skip intro. if devel. game.
00746        (Game::get_game_type() != EXULT_DEVEL_GAME ||
00747           U7exists("<STATIC>/intro.dat")))
00748       game->play_intro();
00749   } while(!game->show_menu(arg_nomenu));
00750   gwin->init_files();
00751   gwin->read_gwin();
00752   gwin->setup_game();   // This will start the scene.
00753           // Get scale factor for mouse.
00754 #ifdef USE_EXULTSTUDIO
00755 #ifndef WIN32
00756         SDL_GetWMInfo(&info);
00757         xfd = ConnectionNumber(info.info.x11.display);
00758   Server_init();      // Initialize server (for map-editor).
00759   xdnd = new Xdnd(info.info.x11.display, info.info.x11.wmwindow,
00760     info.info.x11.window, Move_dragged_shape, Move_dragged_combo,
00761         Drop_dragged_shape, Drop_dragged_chunk, 
00762               Drop_dragged_combo);
00763 #elif !defined(UNDER_CE)
00764   SDL_GetWMInfo(&info);
00765   Server_init();      // Initialize server (for map-editor).
00766   hgwin = info.window;
00767         OleInitialize(NULL);
00768   windnd = new Windnd(hgwin, Move_dragged_shape, Move_dragged_combo,
00769         Drop_dragged_shape, Drop_dragged_chunk,
00770               Drop_dragged_combo);
00771   if (FAILED(RegisterDragDrop(hgwin, windnd))) {
00772        cout << "Something's wrong with OLE2 ..." << endl;
00773   };
00774 #endif
00775 #endif
00776 }
00777 
00778 /*
00779  *  Play game.
00780  */
00781 
00782 static int Play()
00783 {
00784   do
00785   {
00786     quitting_time = QUIT_TIME_NO;
00787     Handle_events();
00788     if( quitting_time == QUIT_TIME_RESTART )
00789     {
00790       Mouse::mouse->hide(); // Turn off mouse.
00791       gwin->read(); // Restart
00795     }
00796   }
00797   while (quitting_time == QUIT_TIME_RESTART);
00798   delete gwin;
00799   delete Mouse::mouse;
00800 
00801   Audio::Destroy(); // Deinit the sound system.
00802 
00803   delete config;
00804   return (0);
00805 }
00806 
00807 #ifdef USE_EXULTSTUDIO      // Shift-click means 'paint'.
00808 /*
00809  *  Add a shape while map-editing.
00810  */
00811 
00812 static void Paint_with_shape
00813   (
00814   SDL_Event& event,
00815   bool dragging     // Painting terrain.
00816   )
00817   {
00818   static int lasttx = -1, lastty = -1;
00819   int scale = gwin->get_win()->get_scale();
00820   int x = event.button.x/scale, y = event.button.y/scale;
00821   int tx = (gwin->get_scrolltx() + x/c_tilesize);
00822   int ty = (gwin->get_scrollty() + y/c_tilesize);
00823   if (dragging)     // See if moving to a new tile.
00824     {
00825     if (tx == lasttx && ty == lastty)
00826       return;
00827     }
00828   lasttx = tx; lastty = ty;
00829   int shnum = cheat.get_edit_shape();
00830   int frnum;
00831   SDLMod mod = SDL_GetModState();
00832   if (mod & KMOD_ALT)   // ALT?  Pick random frame.
00833     {
00834     ShapeID id(shnum, 0);
00835     frnum = std::rand()%id.get_num_frames();
00836     }
00837   else if (mod & KMOD_CTRL) // Cycle through frames.
00838     {
00839     frnum = cheat.get_edit_frame();
00840     ShapeID id(shnum, 0);
00841     int nextframe = (frnum + 1)%id.get_num_frames();
00842     cheat.set_edit_shape(shnum, nextframe);
00843     }
00844   else
00845     frnum = cheat.get_edit_frame();
00846   Drop_dragged_shape(shnum, frnum, event.button.x, event.button.y, 0);
00847   }
00848 
00849 /*
00850  *  Set a complete chunk while map-editing.
00851  */
00852 
00853 static void Paint_with_chunk
00854   (
00855   SDL_Event& event,
00856   bool dragging     // Painting terrain.
00857   )
00858   {
00859   static int lastcx = -1, lastcy = -1;
00860   int scale = gwin->get_win()->get_scale();
00861   int x = event.button.x/scale, y = event.button.y/scale;
00862   int cx = (gwin->get_scrolltx() + x/c_tilesize)/c_tiles_per_chunk;
00863   int cy = (gwin->get_scrollty() + y/c_tilesize)/c_tiles_per_chunk;
00864   if (dragging)     // See if moving to a new chunk.
00865     {
00866     if (cx == lastcx && cy == lastcy)
00867       return;
00868     }
00869   lastcx = cx; lastcy = cy;
00870   int chnum = cheat.get_edit_chunknum();
00871   Drop_dragged_chunk(chnum, event.button.x, event.button.y, 0);
00872   }
00873 #endif
00874 
00875 /*
00876  *  Handle events until a flag is set.
00877  */
00878 
00879 static void Handle_events
00880   (
00881   )
00882   {
00883   uint32 last_repaint = 0;  // For insuring animation repaints.
00884   uint32 last_rotate = 0;
00885   /*
00886    *  Main event loop.
00887    */
00888   while (!quitting_time)
00889     {
00890 #ifdef USE_EXULTSTUDIO
00891     Server_delay();   // Handle requests.
00892 #else
00893     Delay();    // Wait a fraction of a second.
00894 #endif
00895     // Mouse scale factor
00896     int scale = gwin->get_fastmouse() ? 1 : 
00897             gwin->get_win()->get_scale();
00898 
00899     Mouse::mouse->hide();   // Turn off mouse.
00900     Mouse::mouse_update = false;
00901 
00902           // Get current time.
00903     uint32 ticks = SDL_GetTicks();
00904     Game::set_ticks(ticks);
00905 
00906     SDL_Event event;
00907     while (!quitting_time && SDL_PollEvent(&event))
00908       Handle_event(event);
00909 
00910           // Animate unless dormant.
00911     if (gwin->have_focus() && !dragging)
00912       gwin->get_tqueue()->activate(ticks);
00913 
00914     // Moved this out of the animation loop, since we want movement to be
00915     // more responsive. Also, if the step delta is only 1 tile,
00916     // always check every loop
00917     if (!gwin->is_moving() || gwin->get_step_tile_delta() == 1)
00918       {
00919       int x, y;// Check for 'stuck' Avatar.
00920       int ms = SDL_GetMouseState(&x, &y);
00921       if (SDL_BUTTON(3) & ms)
00922         gwin->start_actor(x/scale, y/scale, 
00923           Mouse::mouse->avatar_speed);
00924       else 
00925         gwin->get_main_actor()->resting(50);
00926       }
00927 
00928           // Show animation every 1/20 sec.
00929     if (ticks > last_repaint + 50 || gwin->was_painted())
00930           // This avoids jumpy walking:
00931       {   // OpenGL?  Repaint all each time.
00932       if (GL_manager::get_instance())
00933         gwin->paint();
00934       else
00935         gwin->paint_dirty();
00936       while (ticks > last_repaint+50)last_repaint += 50;
00937 
00938       }
00939 
00940     Mouse::mouse->show(); // Re-display mouse.
00941 
00942           // Rotate less often if scaling and 
00943           //   not paletized.
00944     int rot_speed = 100 << (gwin->get_win()->is_palettized() ||
00945                 scale==1?0:1);
00946     if (ticks > last_rotate + rot_speed &&
00947         !GL_manager::get_instance())  //++++Disable in OGL.
00948       {   // (Blits in simulated 8-bit mode.)
00949       gwin->get_win()->rotate_colors(0xfc, 3, 0);
00950       gwin->get_win()->rotate_colors(0xf8, 4, 0);
00951       gwin->get_win()->rotate_colors(0xf4, 4, 0);
00952       gwin->get_win()->rotate_colors(0xf0, 4, 0);
00953       gwin->get_win()->rotate_colors(0xe8, 8, 0);
00954       gwin->get_win()->rotate_colors(0xe0, 8, 1);
00955       while (ticks > last_rotate + rot_speed) 
00956         last_rotate += rot_speed;
00957           // Non palettized needs explicit blit.
00958       if (!gwin->get_win()->is_palettized())
00959         gwin->set_painted();
00960       }
00961     if (!gwin->show() &&  // Blit to screen if necessary.
00962         Mouse::mouse_update)  // If not, did mouse change?
00963       Mouse::mouse->blit_dirty();
00964     }
00965   }
00966 
00967 /*
00968  *  Handle an event.  This should work for all platforms, and should only
00969  *  be called in 'normal' and 'gump' modes.
00970  */
00971 
00972 static void Handle_event
00973   (
00974   SDL_Event& event
00975   )
00976   {
00977   // Mouse scale factor
00978   int scale = gwin->get_fastmouse() ? 1 : gwin->get_win()->get_scale();
00979   bool dont_move_mode = gwin->main_actor_dont_move();
00980 
00981   // We want this
00982   Gump_manager *gump_man = gwin->get_gump_man();
00983   static bool right_on_gump = false;
00984   Gump *gump = 0;
00985 
00986           // For detecting double-clicks.
00987   static uint32 last_b1_click = 0, last_b3_click = 0;
00988   //cout << "Event " << (int) event.type << " received"<<endl;
00989   switch (event.type)
00990     {
00991   case SDL_MOUSEBUTTONDOWN:
00992     {
00993           if (dont_move_mode)
00994             break;
00995     int x = event.button.x/scale, y = event.button.y/scale;
00996     if (event.button.button == 1)
00997       {
00998 #ifdef USE_EXULTSTUDIO
00999       if (cheat.in_map_editor())
01000         { // Paint if shift-click.
01001         if (cheat.get_edit_shape() >= 0 &&
01002           // But always if painting.
01003                 (cheat.get_edit_mode() == Cheat::paint ||
01004           (SDL_GetModState() & KMOD_SHIFT)))
01005           {
01006           Paint_with_shape(event, false);
01007           break;
01008           }
01009         else if (cheat.get_edit_chunknum() >= 0 &&
01010           cheat.get_edit_mode() == Cheat::paint_chunks)
01011           {
01012           Paint_with_chunk(event, false);
01013           break;
01014           }
01015           // Don't drag if not in 'move' mode.
01016         else if (cheat.get_edit_mode() != Cheat::move)
01017           break;
01018         }
01019 #endif
01020       dragging = gwin->start_dragging(x, y);
01021       //Mouse::mouse->set_shape(Mouse::hand);
01022       dragged = false;
01023       }
01024           // Move sprite toward mouse
01025           //  when right button pressed.
01026     if (gwin->get_mouse3rd())
01027       if (event.button.button == 2)
01028         {
01029           ActionTarget(0);
01030         }
01031     if (event.button.button == 3) {
01032       
01033       // Try removing old queue entry.
01034       gwin->get_tqueue()->remove(gwin->get_main_actor());
01035 
01036       if (!dragging &&  // Causes crash if dragging.
01037         gump_man->can_right_click_close() &&
01038           gump_man->gump_mode() && 
01039           gump_man->find_gump(x, y, false)) {
01040         gump = 0;
01041         right_on_gump = true;
01042       }
01043       else 
01044         gwin->start_actor(x, y, 
01045             Mouse::mouse->avatar_speed);
01046 
01047     }
01048     if (event.button.button == 4 || event.button.button == 5) 
01049       {
01050       if (!cheat()) break;
01051       SDLMod mod = SDL_GetModState();
01052       if (event.button.button == 4)
01053         if (mod & KMOD_ALT)
01054           ActionScrollLeft(0);
01055         else
01056           ActionScrollUp(0);
01057       else
01058         if (mod & KMOD_ALT)
01059           ActionScrollRight(0);
01060         else
01061           ActionScrollDown(0);
01062       }
01063     break;
01064     }
01065   case SDL_MOUSEBUTTONUP:
01066     {
01067           if (dont_move_mode)
01068               break;
01069     int x = event.button.x/scale, y = event.button.y/scale;
01070     if (event.button.button == 3)
01071       {
01072       uint32 curtime = SDL_GetTicks();
01073           // Last click within .5 secs?
01074       if (gwin->get_allow_double_right_move() && curtime - last_b3_click < 500)
01075         gwin->start_actor_along_path(x, y,
01076             Mouse::mouse->avatar_speed);
01077       else if (right_on_gump && 
01078         (gump = gump_man->find_gump(x, y, false))) {
01079         Rectangle dirty = gump->get_dirty();
01080         gwin->add_dirty(dirty);
01081         gump_man->close_gump(gump);
01082         gump = 0;
01083         right_on_gump = false;
01084       }
01085       else
01086         {
01087         gwin->stop_actor();
01088         if (Combat::is_paused() && gwin->in_combat())
01089           gwin->paused_combat_select(x, y);
01090         }
01091       last_b3_click = curtime;
01092       }
01093     else if (event.button.button == 1)
01094       {
01095       uint32 curtime = SDL_GetTicks();
01096       bool click_handled = false;
01097       if (dragging) {
01098         click_handled = gwin->drop_dragged(x, y,
01099                 dragged);
01100       }
01101           // Last click within .5 secs?
01102       if (curtime - last_b1_click < 500)
01103         {
01104         dragging = dragged = false;
01105         gwin->double_clicked(x, y);
01106         Mouse::mouse->set_speed_cursor();
01107         break;
01108         }
01109       if (!dragging || !dragged)
01110         last_b1_click = curtime;
01111 
01112       if (!click_handled) {
01113           // Identify item(s) clicked on.
01114         gwin->show_items(x, y, 
01115           (SDL_GetModState() & KMOD_CTRL) != 0);
01116       }
01117       dragging = dragged = false;
01118       }
01119     break;
01120     }
01121   case SDL_MOUSEMOTION:
01122     {
01123     Mouse::mouse->move(event.motion.x / scale, 
01124             event.motion.y / scale);
01125     Mouse::mouse->set_speed_cursor();
01126     Mouse::mouse_update = true; // Need to blit mouse.
01127     right_on_gump = false;
01128 
01129           // Dragging with left button?
01130     if (event.motion.state & SDL_BUTTON(1))
01131       {
01132 #ifdef USE_EXULTSTUDIO      // Painting?
01133       if (cheat.in_map_editor())
01134         { 
01135         if (cheat.get_edit_shape() >= 0 &&
01136             (cheat.get_edit_mode() == Cheat::paint ||
01137           (SDL_GetModState() & KMOD_SHIFT)))
01138           {
01139           Paint_with_shape(event, true);
01140           break;
01141           }
01142         else if (cheat.get_edit_chunknum() >= 0 &&
01143           cheat.get_edit_mode() == Cheat::paint_chunks)
01144           {
01145           Paint_with_chunk(event, true);
01146           break;
01147           }
01148         }
01149 #endif
01150       dragged = gwin->drag(event.motion.x / scale, 
01151             event.motion.y / scale);
01152       }
01153           // Dragging with right?
01154     else if ((event.motion.state & SDL_BUTTON(3)))
01155       gwin->start_actor(event.motion.x / scale, 
01156       event.motion.y / scale, Mouse::mouse->avatar_speed);
01157 #ifdef USE_EXULTSTUDIO      // Painting?
01158     else if (cheat.in_map_editor() && 
01159        cheat.get_edit_shape() >= 0 &&
01160        (cheat.get_edit_mode() == Cheat::paint ||
01161           (SDL_GetModState() & KMOD_SHIFT)))
01162       {
01163       static int prevx = -1, prevy = -1;
01164       Move_dragged_shape(cheat.get_edit_shape(),
01165         cheat.get_edit_frame(), 
01166         event.motion.x, event.motion.y, 
01167             prevx, prevy, false);
01168       prevx = event.motion.x; prevy = event.motion.y;
01169       }
01170 #endif
01171     break;
01172     }
01173   case SDL_ACTIVEEVENT:
01174 
01175     if (event.active.state & SDL_APPMOUSEFOCUS)
01176       {
01177       if (event.active.gain)
01178         {
01179         int x, y;
01180         SDL_GetMouseState(&x, &y);
01181         Mouse::mouse->set_location(x/scale, y/scale);
01182         }
01183       gwin->set_painted();
01184       }
01185 
01186     if (event.active.state & SDL_APPINPUTFOCUS)
01187       {
01188       if (event.active.gain)
01189         gwin->get_focus();
01190       else
01191         gwin->lose_focus();
01192       }
01193 #if 0
01194     if (event.active.state & SDL_APPACTIVE)
01195           // Became active.
01196       if (event.active.gain)
01197         gwin->init_actors();
01198 #endif
01199     break;
01200 #if 0
01201   case ConfigureNotify:   // Resize.
01202     gwin->resized(event.xconfigure.window,
01203       event.xconfigure.width, event.xconfigure.height);
01204     break;
01205 #endif
01206   case SDL_QUIT:
01207     gwin->get_gump_man()->okay_to_quit();
01208     break;
01209   case SDL_KEYDOWN:   // Keystroke.
01210   case SDL_KEYUP:
01211     if (!dragging)    // ESC while dragging causes crashes.
01212       keybinder->HandleEvent(event);
01213     break;
01214 #ifdef USE_EXULTSTUDIO
01215 #ifndef WIN32
01216   case SDL_SYSWMEVENT:
01217     {
01218     XEvent& ev = event.syswm.msg->event.xevent;
01219     if (ev.type == ClientMessage)
01220       xdnd->client_msg((XClientMessageEvent&) ev);
01221     else if (ev.type == SelectionNotify)
01222       xdnd->select_msg((XSelectionEvent&) ev);
01223     break;
01224     }
01225 #endif
01226 #endif
01227 #if 0
01228 //#ifdef WIN32
01229   case SDL_SYSWMEVENT:
01230 //    printf("SYSWMEVENT received, %x\n", event.syswm.msg->msg);
01231     if (event.syswm.msg->msg == MM_MCINOTIFY) {
01232 #if DEBUG
01233       cerr << "MM_MCINOTIFY message received"<<endl;
01234 #endif
01235       ((Windows_MCI*)(Audio::get_ptr()->get_midi()))->callback(event.syswm.msg->wParam, 
01236               event.syswm.msg->hwnd);
01237     }
01238     break;
01239 #endif
01240     }
01241   }
01242 
01243 
01244 /*
01245  *  Wait for a click, or optionally, a kbd. chr.
01246  *
01247  *  Output: 0 if user hit ESC.
01248  */
01249 static int Get_click
01250   (
01251   int& x, int& y,
01252   char *chr,      // Char. returned if not null.
01253   bool drag_ok,     // Okay to drag/close while here.
01254   Paintable *paint    // Paint this each cycle if OpenGL.
01255   )
01256   {
01257   dragging = false;   // Init.
01258   while (1)
01259     {
01260     SDL_Event event;
01261     Delay();    // Wait a fraction of a second.
01262 
01263     uint32 ticks = SDL_GetTicks();
01264     Game::set_ticks(ticks);
01265     Mouse::mouse->hide();   // Turn off mouse.
01266     Mouse::mouse_update = false;
01267 
01268     // Mouse scale factor
01269     int scale = gwin->get_fastmouse() ? 1 
01270         : gwin->get_win()->get_scale();
01271 
01272     static bool rightclick;
01273     while (SDL_PollEvent(&event))
01274       switch (event.type)
01275         {
01276       case SDL_MOUSEBUTTONDOWN:
01277         if (event.button.button == 3)
01278           rightclick = true;
01279         else if (drag_ok && event.button.button == 1)
01280           {
01281           x = event.button.x / scale;
01282           y = event.button.y / scale;
01283           dragging = gwin->start_dragging(x, y);
01284           dragged = false;
01285           }
01286         break;
01287       case SDL_MOUSEBUTTONUP:
01288         if (event.button.button == 1)
01289           {
01290           x = event.button.x / scale;
01291           y = event.button.y / scale;
01292           bool drg = dragging, drged = dragged;
01293           dragging = dragged = false;
01294           if (!drg ||
01295               !gwin->drop_dragged(x, y, drged))
01296             {
01297             if (chr) *chr = 0;
01298             return (1);
01299             }
01300           }
01301         else if (event.button.button == 3) {
01302           // Just stop.  Don't get followers!
01303           gwin->get_main_actor()->stop();
01304           if (gwin->get_mouse3rd() && rightclick)           {
01305             rightclick = false;
01306             return 0;
01307           }
01308         }
01309         break;
01310       case SDL_MOUSEMOTION:
01311         {
01312         int mx = event.motion.x / scale,
01313             my = event.motion.y / scale;
01314         Mouse::mouse->move(mx, my);
01315         Mouse::mouse_update = true;
01316         if (drag_ok &&
01317             (event.motion.state & SDL_BUTTON(1)))
01318           dragged = gwin->drag(mx, my);
01319         break;
01320         }
01321       case SDL_KEYDOWN:
01322         {
01323         //+++++ convert to unicode first?
01324         int c = event.key.keysym.sym;
01325         switch(c) {
01326         case SDLK_ESCAPE:
01327           return 0;
01328         case SDLK_RSHIFT: case SDLK_LSHIFT:
01329         case SDLK_RCTRL: case SDLK_LCTRL:
01330         case SDLK_RALT: case SDLK_LALT:
01331         case SDLK_RMETA: case SDLK_LMETA:
01332         case SDLK_RSUPER: case SDLK_LSUPER:
01333         case SDLK_NUMLOCK: case SDLK_CAPSLOCK:
01334         case SDLK_SCROLLOCK:
01335           break;
01336         default:
01337           if ((c == 's') && 
01338              (event.key.keysym.mod & KMOD_ALT) &&
01339              (event.key.keysym.mod & KMOD_CTRL)){
01340             make_screenshot(true);
01341             break;
01342           }
01343           if (chr)// Looking for a character?
01344           {
01345             *chr = (event.key.keysym.mod & 
01346               KMOD_SHIFT)
01347               ? toupper(c) : c;
01348             return (1);
01349           }
01350           break;
01351         }
01352         break;
01353         }
01354       case SDL_ACTIVEEVENT:
01355         if (event.active.state & SDL_APPINPUTFOCUS)
01356           {
01357           if (event.active.gain)
01358             gwin->get_focus();
01359           else
01360             gwin->lose_focus();
01361           }
01362         }
01363     if (GL_manager::get_instance())
01364       {
01365       gwin->paint();
01366       if (paint)
01367         paint->paint();
01368       }
01369     else if (dragging)
01370       gwin->paint_dirty();
01371     Mouse::mouse->show();   // Turn on mouse.
01372     if (!gwin->show() &&  // Blit to screen if necessary.
01373         Mouse::mouse_update)
01374       Mouse::mouse->blit_dirty();
01375     }
01376   return (0);     // Shouldn't get here.
01377   }
01378 
01379 /*
01380  *  Get a click, or, optionally, a keyboard char.
01381  *
01382  *  Output: 0 if user hit ESC.
01383  *    Chr gets keyboard char., or 0 if it's was a mouse click.
01384  */
01385 
01386 int Get_click
01387   (
01388   int& x, int& y,     // Location returned (if not ESC).
01389   Mouse::Mouse_shapes shape,  // Mouse shape to use.
01390   char *chr,      // Char. returned if not null.
01391   bool drag_ok,     // Okay to drag/close while here.
01392   Paintable *paint    // Paint this over everything else.
01393   )
01394   {
01395   if (chr)
01396     *chr = 0;   // Init.
01397   Mouse::Mouse_shapes saveshape = Mouse::mouse->get_shape();
01398   if (shape != Mouse::dontchange)
01399     Mouse::mouse->set_shape(shape);
01400   if (paint)
01401     paint->paint();
01402   Mouse::mouse->show();
01403   gwin->show(1);      // Want to see new mouse.
01404   gwin->get_tqueue()->pause(Game::get_ticks());
01405   int ret = Get_click(x, y, chr, drag_ok, paint);
01406   gwin->get_tqueue()->resume(Game::get_ticks());
01407   Mouse::mouse->set_shape(saveshape);
01408   return (ret);
01409   }
01410 
01411 /*
01412  *  Wait for someone to stop walking.  If a timeout is given, at least
01413  *  one animation cycle will still always occur.
01414  */
01415 
01416 void Wait_for_arrival
01417   (
01418   Actor *actor,     // Whom to wait for.
01419   Tile_coord dest,    // Where he's going.
01420   long maxticks     // Max. # msecs. to wait, or 0.
01421   )
01422   {
01423   // Mouse scale factor
01424   int scale = gwin->get_fastmouse() ? 1 : gwin->get_win()->get_scale();
01425 
01426   unsigned char os = Mouse::mouse->is_onscreen();
01427   uint32 last_repaint = 0;  // For insuring animation repaints.
01428   Actor_action *orig_action = actor->get_action();
01429   uint32 stop_time = SDL_GetTicks() + maxticks;
01430   bool timeout = false;
01431   while (actor->is_moving() && actor->get_action() == orig_action &&
01432          actor->get_tile() != dest && !timeout)
01433     {
01434     Delay();    // Wait a fraction of a second.
01435 
01436     Mouse::mouse->hide();   // Turn off mouse.
01437     Mouse::mouse_update = false;
01438 
01439     SDL_Event event;
01440     while (SDL_PollEvent(&event))
01441       switch (event.type)
01442         {
01443       case SDL_MOUSEMOTION:
01444         Mouse::mouse->move(event.motion.x / scale,
01445              event.motion.y / scale);
01446         Mouse::mouse_update = true;
01447         break;
01448         }
01449           // Get current time, & animate.
01450     uint32 ticks = SDL_GetTicks();
01451     Game::set_ticks(ticks);
01452     if (maxticks && ticks > stop_time)
01453       timeout = true;
01454     if (gwin->have_focus())
01455       gwin->get_tqueue()->activate(ticks);
01456           // Show animation every 1/20 sec.
01457     if (ticks > last_repaint + 50 || gwin->was_painted())
01458       {
01459       gwin->paint_dirty();
01460       while (ticks > last_repaint+50)last_repaint += 50;
01461       }
01462 
01463     Mouse::mouse->show();   // Re-display mouse.
01464     if (!gwin->show() &&  // Blit to screen if necessary.
01465         Mouse::mouse_update)  // If not, did mouse change?
01466       Mouse::mouse->blit_dirty();
01467     }
01468 
01469   if (!os)
01470     Mouse::mouse->hide();
01471 
01472   }
01473 
01474 /*
01475  *  Shift 'wizard's view' according to mouse position.
01476  */
01477 
01478 static void Shift_wizards_eye
01479   (
01480   int mx, int my
01481   )
01482   {
01483           // Figure dir. from center.
01484   int cx = gwin->get_width()/2, cy = gwin->get_height()/2;
01485         int dy = cy - my, dx = mx - cx;
01486         Direction dir = Get_direction(dy, dx);
01487   static int deltas[16] = {0,-1, 1,-1, 1,0, 1,1, 0,1, 
01488             -1,1, -1,0, -1,-1};
01489   int dirx = deltas[2*dir], diry = deltas[2*dir + 1];
01490   if (dirx == 1)
01491     gwin->view_right();
01492   else if (dirx == -1)
01493     gwin->view_left();
01494   if (diry == 1)
01495     gwin->view_down();
01496   else if (diry == -1)
01497     gwin->view_up();
01498   }
01499 
01500 /*
01501  *  Do the 'wizard's eye' spell by letting the user browse around.
01502  */
01503 
01504 void Wizard_eye
01505   (
01506   long msecs      // Length of time in milliseconds.
01507   )
01508   {
01509   // Mouse scale factor
01510   int scale = gwin->get_fastmouse() ? 1 : gwin->get_win()->get_scale();
01511           // Center of screen.
01512   int cx = gwin->get_width()/2, cy = gwin->get_height()/2;
01513 
01514   unsigned char os = Mouse::mouse->is_onscreen();
01515   uint32 last_repaint = 0;  // For insuring animation repaints.
01516   uint32 stop_time = SDL_GetTicks() + msecs;
01517   bool timeout = false;
01518   while (!timeout)
01519     {
01520     Delay();    // Wait a fraction of a second.
01521 
01522     Mouse::mouse->hide();   // Turn off mouse.
01523     Mouse::mouse_update = false;
01524 
01525     SDL_Event event;
01526     while (SDL_PollEvent(&event))
01527       switch (event.type)
01528         {
01529       case SDL_MOUSEMOTION:
01530         {
01531         int mx = event.motion.x/scale,
01532             my = event.motion.y/scale;
01533         Mouse::mouse->move(mx, my);
01534         Mouse::mouse->set_shape(
01535           Mouse::mouse->get_short_arrow(
01536           Get_direction(cy - my, mx - cx)));
01537         Mouse::mouse_update = true;
01538         break;
01539         }
01540       case SDL_KEYDOWN:
01541         if (event.key.keysym.sym == SDLK_ESCAPE)
01542           timeout = true;
01543         }
01544           // Get current time, & animate.
01545     uint32 ticks = SDL_GetTicks();
01546     Game::set_ticks(ticks);
01547     if (ticks > stop_time)
01548       timeout = true;
01549     if (gwin->have_focus())
01550       gwin->get_tqueue()->activate(ticks);
01551           // Show animation every 1/20 sec.
01552     if (ticks > last_repaint + 50 || gwin->was_painted())
01553       {   // Right mouse button down?
01554       int x, y;
01555       int ms = SDL_GetMouseState(&x, &y);
01556       if (SDL_BUTTON(3) & ms)
01557         Shift_wizards_eye(x/scale, y/scale);
01558       gwin->set_all_dirty();
01559       gwin->paint_dirty();
01560           // Paint sprite over view.
01561       ShapeID eye(10, 0, SF_SPRITES_VGA);
01562       Shape_frame *spr = eye.get_shape();
01563           // Center it.
01564       int w = gwin->get_width(), h = gwin->get_height();
01565       int sw = spr->get_width(), sh = spr->get_height();
01566       int topx = (w - sw)/2,
01567           topy = (h - sh)/2;
01568       eye.paint_shape(topx + spr->get_xleft(),
01569           topy + spr->get_yabove());
01570       if (topy > 0) // Black-fill area around sprite.
01571         {
01572         gwin->get_win()->fill8(0, w, topy, 0, 0);
01573         gwin->get_win()->fill8(0, w, h - topy - sh,
01574                 0, topy + sh);
01575         }
01576       if (topx > 0)
01577         {
01578         gwin->get_win()->fill8(0, topx, sh, 0, topy);
01579         gwin->get_win()->fill8(0, w - topx - sw, sh,
01580               topx + sw, topy);
01581         }
01582       while (ticks > last_repaint+50)last_repaint += 50;
01583       }
01584 
01585     Mouse::mouse->show();   // Re-display mouse.
01586     if (!gwin->show() &&  // Blit to screen if necessary.
01587         Mouse::mouse_update)  // If not, did mouse change?
01588       Mouse::mouse->blit_dirty();
01589     }
01590 
01591   if (!os)
01592     Mouse::mouse->hide();
01593   gwin->center_view(gwin->get_main_actor()->get_tile());
01594   }
01595 
01596 
01597 int find_resolution(int w, int h, int s)
01598 {
01599   int res = 0;
01600   for(int i=0; i<num_res; i++) {
01601     if(res_list[i].x==w && res_list[i].y==h && res_list[i].scale==s)
01602       res = i;
01603   }
01604   return res;
01605 }
01606 
01607 
01608 void set_resolution (int new_res, bool save)
01609 {
01610   if(new_res>=0 && new_res<num_res) {
01611     int scaler = gwin->get_win()->get_scaler();
01612     current_res = new_res;
01613     gwin->resized(res_list[current_res].x,
01614       res_list[current_res].y,
01615       res_list[current_res].scale, scaler);
01616     if(save) {
01617       char val[20];
01618       snprintf(val, 20, "%d", res_list[current_res].x);
01619       config->set("config/video/width",val,true);
01620       snprintf(val, 20, "%d", res_list[current_res].y);
01621       config->set("config/video/height",val,true);
01622       snprintf(val, 20, "%d", res_list[current_res].scale);
01623       config->set("config/video/scale",val,true);
01624 
01625       // Scaler
01626       if (scaler > Image_window::NoScaler && scaler < Image_window::NumScalers)
01627         config->set("config/video/scale_method",Image_window::get_name_for_scaler(scaler),true);
01628     }
01629   }
01630 }
01631 
01632 void increase_resolution()
01633 {
01634   if (!cheat()) return;
01635 
01636   current_res++;
01637   if(current_res>=num_res)
01638     current_res = 0;
01639   set_resolution(current_res,false);
01640 }
01641 
01642 void decrease_resolution()
01643 {
01644   if (!cheat()) return;
01645 
01646   current_res--;
01647   if(current_res<0)
01648     current_res = num_res-1;
01649   set_resolution(current_res,false);
01650 }
01651 
01652 void make_screenshot (bool silent)
01653 {
01654   char fn[15];
01655   int i;
01656   FILE *f;
01657   bool namefound = false;
01658   Effects_manager *eman = gwin->get_effects();
01659 
01660   // look for the next available exult???.pcx file
01661   for (i = 0; i < 1000 && !namefound; i++) {
01662     snprintf(fn, 15, "exult%03i.pcx", i);
01663     f = fopen(fn, "rb");
01664     if (f) {
01665       fclose(f);
01666     } else {
01667       namefound = true;
01668     }
01669   }
01670 
01671   if (!namefound) {
01672     if (!silent) eman->center_text("Too many screenshots");
01673   } else {
01674     SDL_RWops *dst = SDL_RWFromFile(fn, "wb");
01675 
01676     if (gwin->get_win()->screenshot(dst)) {
01677       cout << "Screenshot saved in " << fn << endl;
01678       if (!silent) eman->center_text("Screenshot");
01679     } else {
01680       if (!silent) eman->center_text("Screenshot failed");
01681     }
01682   } 
01683 }
01684 
01685 void change_gamma (bool down)
01686 {
01687   float r,g,b;
01688   char text[256];
01689   float delta = down?0.05:-0.05;
01690   Image_window8::get_gamma(r, g, b);  
01691   Image_window8::set_gamma(r+delta, g+delta, b+delta);  
01692   gwin->get_pal()->set(-1, -1);
01693 
01694   // Message
01695 #ifdef HAVE_SNPRINTF
01696   Image_window8::get_gamma(r, g, b);  
01697   snprintf (text, 256, "Gamma Set to R: %01.2f G: %01.2f B: %01.2f", r, g, b);
01698 #else
01699   strncpy (text, "Gamma Changed", 256);
01700 #endif
01701   gwin->get_effects()->center_text(text); 
01702 
01703   int igam = (int) ((r*10000)+0.5);
01704   snprintf (text, 256, "%d.%04d", igam/10000, igam%10000);
01705   config->set("config/video/gamma/red", text, true);
01706 
01707   igam = (int) ((b*10000)+0.5);
01708   snprintf (text, 256, "%d.%04d", igam/10000, igam%10000);
01709   config->set("config/video/gamma/green", text, true);
01710 
01711   igam = (int) ((g*10000)+0.5);
01712   snprintf (text, 256, "%d.%04d", igam/10000, igam%10000);
01713   config->set("config/video/gamma/blue", text, true);
01714 }
01715 
01716 void BuildGameMap()
01717 {
01718   int w, h, sc, sclr;
01719 
01720   // create 2048x2048 screenshots of the full Ultima 7 map.
01721   // WARNING!! Takes up lots of memory and diskspace!
01722   if (arg_buildmap >= 0) {
01723     int maplift = 16;
01724     Exult_Game gametype;
01725     switch(arg_buildmap) {
01726       case 0: maplift = 16; break;
01727       case 1: maplift = 10; break;
01728       case 2: maplift = 5; break;
01729     }
01730 
01731     if (run_bg) {
01732       gametype = BLACK_GATE;
01733       get_game_paths("blackgate");
01734     } else if (run_si) {
01735       gametype = SERPENT_ISLE;
01736       get_game_paths("serpentisle");
01737     } else {
01738       cerr << "You have to specify --bg or --si when using --buildmap" << endl;
01739       exit(1);
01740     }
01741 
01742     h = w = c_tilesize * c_tiles_per_schunk; sc = 1, sclr = Image_window::point;
01743     Image_window8::set_gamma(1, 1, 1);
01744 
01745     string  fullscreenstr;    // Check config. for fullscreen mode.
01746     config->value("config/video/fullscreen",fullscreenstr,"no");
01747     // set windowed mode
01748     config->set("config/video/fullscreen","no",false);
01749     gwin = new Game_window(w, h, sc, sclr);
01750     // restore original fullscreen setting
01751     config->set("config/video/fullscreen",fullscreenstr,true);
01752     Audio::Init();
01753     current_res = find_resolution(w, h, sc);
01754     Game::create_game(gametype);
01755     gwin->init_files(false); //init, but don't show plasma  
01756     gwin->get_map()->init();// +++++Got to clean this up.
01757     gwin->get_pal()->set(0);
01758     for (int x = 0; x < c_num_chunks / c_chunks_per_schunk; x++) {
01759       for (int y = 0; y < c_num_chunks / c_chunks_per_schunk; y++) {
01760         gwin->paint_map_at_tile(0,0,w,h,x * c_tiles_per_schunk, y * c_tiles_per_schunk, maplift);
01761         char fn[15];
01762         snprintf(fn, 15, "u7map%x%x.pcx", x, y);
01763         SDL_RWops *dst = SDL_RWFromFile(fn, "wb");
01764         cerr << x << "," << y << ": ";
01765         gwin->get_win()->screenshot(dst);
01766       }
01767     }
01768     Audio::Destroy();
01769     exit(0);
01770   }
01771 }
01772 
01773 
01774 #ifdef USE_EXULTSTUDIO
01775 
01776 /*
01777  *  Show a grid being dragged.
01778  */
01779 
01780 static void Move_grid
01781   (
01782   int x, int y,     // Mouse coords. within window.
01783   int prevx, int prevy,   // Prev. coords, or -1.
01784   bool ireg,      // A single IREG object?
01785   int xtiles, int ytiles,   // Dimension of grid to show.
01786   int tiles_right, int tiles_below// # tiles to show to right of and
01787           //   below (x, y).
01788   )
01789   {
01790   int scale = gwin->get_win()->get_scale();
01791   x /= scale;     // Watch for scaled window.
01792   y /= scale;
01793   int lift = cheat.get_edit_lift();
01794   x += lift*4 - 1;    // Take lift into account, round.
01795   y += lift*4 - 1;
01796   int tx = x/c_tilesize;    // Figure tile on ground.
01797   int ty = y/c_tilesize;
01798   tx += tiles_right;
01799   ty += tiles_below;
01800   if (prevx != -1)    // See if moved to a new tile.
01801     {
01802     prevx /= scale;
01803     prevy /= scale;
01804     prevx += lift*4 - 1;  // Take lift into account, round.
01805     prevy += lift*4 - 1;
01806     int ptx = prevx/c_tilesize, pty = prevy/c_tilesize;
01807     if (tx == ptx && ty == pty)
01808       return;   // Will be in same tile.
01809           // Repaint over old area.
01810     const int pad = 8;
01811     Rectangle r((ptx - xtiles + 1)*c_tilesize - pad,
01812           (pty - ytiles + 1)*c_tilesize - pad,
01813           xtiles*c_tilesize + 2*pad,
01814           ytiles*c_tilesize + 2*pad);
01815     r = gwin->clip_to_win(r);
01816     gwin->add_dirty(r);
01817     gwin->paint_dirty();
01818     }
01819           // First see if it's a gump.
01820   if (ireg && gwin->get_gump_man()->find_gump(x, y))
01821     return;     // Skip if so.
01822   tx -= xtiles - 1;   // Get top-left of footprint.
01823   ty -= ytiles - 1;
01824           // Let's try a green outline.
01825   int pix = Shape_manager::get_instance()->get_special_pixel(
01826                 POISON_PIXEL);
01827   Image_window8 *win = gwin->get_win();
01828   win->set_clip(0, 0, win->get_width(), win->get_height());
01829   for (int Y = 0; Y <= ytiles; Y++)
01830     win->fill8(pix, xtiles*c_tilesize, 1, 
01831           tx*c_tilesize, (ty + Y)*c_tilesize);
01832   for (int X = 0; X <= xtiles; X++)
01833     win->fill8(pix, 1, ytiles*c_tilesize,
01834         (tx + X)*c_tilesize, ty*c_tilesize);
01835   win->clear_clip();
01836   gwin->set_painted();
01837   }
01838 
01839 /*
01840  *  Show where a shape dragged from a shape-chooser will go.
01841  *  ALSO, this is called with shape==-1 to just force a repaint.
01842  */
01843 
01844 static void Move_dragged_shape
01845   (
01846   int shape, int frame,   // What to create, OR -1 to just
01847           //   repaint window.
01848   int x, int y,     // Mouse coords. within window.
01849   int prevx, int prevy,   // Prev. coords, or -1.
01850   bool show     // Blit window.
01851   )
01852   {
01853   if (shape == -1)
01854     {
01855     gwin->set_all_dirty();
01856     return;
01857     }
01858   Shape_info& info = ShapeID::get_info(shape);
01859           // Get footprint in tiles.
01860   int xtiles = info.get_3d_xtiles(frame),
01861       ytiles = info.get_3d_ytiles(frame);
01862   int sclass = info.get_shape_class();
01863           // Is it an ireg (changeable) obj?
01864   bool ireg = (sclass != Shape_info::unusable &&
01865          sclass != Shape_info::building);
01866   Move_grid(x, y, prevx, prevy, ireg, xtiles, ytiles, 0, 0);
01867   if (show)
01868     gwin->show();
01869   }
01870 
01871 /*
01872  *  Show where a shape dragged from a shape-chooser will go.
01873  */
01874 
01875 static void Move_dragged_combo
01876   (
01877   int xtiles, int ytiles,   // Dimensions in tiles.
01878   int tiles_right,    // Tiles right of & below hot-spot.
01879   int tiles_below,
01880   int x, int y,     // Mouse coords. within window.
01881   int prevx, int prevy,   // Prev. coords, or -1.
01882   bool show     // Blit window.
01883   )
01884   {
01885   Move_grid(x, y, prevx, prevy, false, xtiles, ytiles, tiles_right,
01886               tiles_below);
01887   if (show)
01888     gwin->show();
01889   }
01890 
01891 /*
01892  *  Create an object as moveable (IREG) or fixed.
01893  */
01894 
01895 static Game_object *Create_object
01896   (
01897   int shape, int frame,   // What to create.
01898   bool& ireg      // Rets. TRUE if ireg (moveable).
01899   )
01900   {
01901   Shape_info& info = ShapeID::get_info(shape);
01902   int sclass = info.get_shape_class();
01903           // Is it an ireg (changeable) obj?
01904   ireg = (sclass != Shape_info::unusable &&
01905     sclass != Shape_info::building);
01906   Game_object *newobj;
01907   if (ireg)
01908     newobj = gwin->get_map()->create_ireg_object(
01909             info, shape, frame, 0, 0, 0);
01910   else
01911     newobj = gwin->get_map()->create_ifix_object(shape, frame);
01912   return newobj;
01913   }
01914 
01915 /*
01916  *  Drop a shape dragged from a shape-chooser via drag-and-drop.  Dnd is
01917  *  only supported under X for now.
01918  */
01919 
01920 static void Drop_dragged_shape
01921   (
01922   int shape, int frame,   // What to create.
01923   int x, int y,     // Mouse coords. within window.
01924   void *data      // Passed data, unused by exult
01925   )
01926   {
01927   int scale = gwin->get_win()->get_scale();
01928   if (!cheat.in_map_editor()) // Get into editing mode.
01929     cheat.toggle_map_editor();
01930   cheat.clear_selected();   // Remove old selected.
01931   gwin->get_map()->set_map_modified();
01932   x /= scale;     // Watch for scaled window.
01933   y /= scale;
01934   ShapeID sid(shape, frame);
01935   if (gwin->skip_lift == 0) // Editing terrain?
01936     {
01937     int tx = (gwin->get_scrolltx() + x/c_tilesize)%c_num_tiles;
01938     int ty = (gwin->get_scrollty() + y/c_tilesize)%c_num_tiles;
01939     int cx = tx/c_tiles_per_chunk, cy = ty/c_tiles_per_chunk;
01940     Map_chunk *chunk = gwin->get_map()->get_chunk(cx, cy);
01941     Chunk_terrain *ter = chunk->get_terrain();
01942     tx %= c_tiles_per_chunk; ty %= c_tiles_per_chunk;
01943     ShapeID curid = ter->get_flat(tx, ty);
01944     if (sid.get_shapenum() != curid.get_shapenum() ||
01945         sid.get_framenum() != curid.get_framenum())
01946       {
01947       ter->set_flat(tx, ty, sid);
01948       gwin->set_all_dirty();  // ++++++++For now.++++++++++
01949       }
01950     return;
01951     }
01952   Shape_frame *sh = sid.get_shape();
01953   if (!sh || !sh->is_rle()) // Flats are only for terrain.
01954     return;     // Shouldn't happen.
01955   cout << "Last drag pos: (" << x << ", " << y << ')' << endl;
01956   cout << "Create shape (" << shape << '/' << frame << ')' <<
01957                 endl;
01958   bool ireg;      // Create object.
01959   Game_object *newobj = Create_object(shape, frame, ireg);
01960   Dragging_info drag(newobj);
01961   drag.drop(x, y, true);    // (Dels if it fails.)
01962   }
01963 
01964 /*
01965  *  Drop a chunk dragged from a chunk-chooser via drag-and-drop.  Dnd is
01966  *  only supported under X for now.
01967  */
01968 
01969 static void Drop_dragged_chunk
01970   (
01971   int chunknum,     // Index in 'u7chunks'.
01972   int x, int y,     // Mouse coords. within window.
01973   void *data      // Passed data, unused by exult
01974   )
01975   {
01976   int scale = gwin->get_win()->get_scale();
01977   if (!cheat.in_map_editor()) // Get into editing mode.
01978     cheat.toggle_map_editor();
01979   x /= scale;     // Watch for scaled window.
01980   y /= scale;
01981   cout << "Last drag pos: (" << x << ", " << y << ')' << endl;
01982   cout << "Set chunk (" << chunknum << ')' << endl;
01983           // Need chunk-coordinates.
01984   int tx = (gwin->get_scrolltx() + x/c_tilesize)%c_num_tiles,
01985       ty = (gwin->get_scrollty() + y/c_tilesize)%c_num_tiles;
01986   int cx = tx/c_tiles_per_chunk, cy = ty/c_tiles_per_chunk;
01987   gwin->get_map()->set_chunk_terrain(cx, cy, chunknum);
01988   gwin->paint();
01989   }
01990 
01991 /*
01992  *  Drop a combination object dragged from ExultStudio.
01993  */
01994 
01995 void Drop_dragged_combo
01996   (
01997   int cnt,      // # shapes.
01998   U7_combo_data *combo,   // The shapes.
01999   int x, int y,     // Mouse coords. within window.
02000   void *data      // Passed data, unused by exult
02001   )
02002   {
02003   int scale = gwin->get_win()->get_scale();
02004   if (!cheat.in_map_editor()) // Get into editing mode.
02005     cheat.toggle_map_editor();
02006   cheat.clear_selected();   // Remove old selected.
02007   x /= scale;     // Watch for scaled window.
02008   y /= scale;
02009   int at_lift = cheat.get_edit_lift();
02010   x += at_lift*4 - 1;   // Take lift into account, round.
02011   y += at_lift*4 - 1;
02012           // Figure tile at mouse pos.
02013   int tx = (gwin->get_scrolltx() + x/c_tilesize)%c_num_tiles,
02014       ty = (gwin->get_scrollty() + y/c_tilesize)%c_num_tiles;
02015   for (int i = 0; i < cnt; i++)
02016     {     // Drop each shape.
02017     U7_combo_data& elem = combo[i];
02018           // Figure new tile coord.
02019     int ntx = (tx + elem.tx)%c_num_tiles, 
02020         nty = (ty + elem.ty)%c_num_tiles, 
02021         ntz = at_lift + elem.tz;
02022     if (ntz < 0)
02023       ntz = 0;
02024     ShapeID sid(elem.shape, elem.frame);
02025     if (gwin->skip_lift == 0)// Editing terrain?
02026       {
02027       int cx = ntx/c_tiles_per_chunk, 
02028           cy = nty/c_tiles_per_chunk;
02029       Map_chunk *chunk = gwin->get_map()->get_chunk(cx, cy);
02030       Chunk_terrain *ter = chunk->get_terrain();
02031       ntx %= c_tiles_per_chunk; nty %= c_tiles_per_chunk;
02032       ter->set_flat(ntx, nty, sid);
02033       continue;
02034       }
02035     bool ireg;    // Create object.
02036     Game_object *newobj = Create_object(elem.shape, 
02037               elem.frame, ireg);
02038     newobj->set_invalid();  // Not in world.
02039     newobj->move(ntx, nty, ntz);
02040           // Add to selection.
02041     cheat.append_selected(newobj);
02042     }
02043   gwin->set_all_dirty();    // For now, until we clear out grid.
02044   }
02045 
02046 #endif

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