Gump_manager.cc

Go to the documentation of this file.
00001 /*
00002  *  Gump_manager.cc - Object that manages all available gumps
00003  *
00004  *  Copyright (C) 2001-2004  The Exult Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #  include <config.h>
00023 #endif
00024 
00025 #include "SDL_events.h"
00026 #include "SDL_keyboard.h"
00027 
00028 #include "Configuration.h"
00029 #include "exult.h"
00030 #include "Gump.h"
00031 #include "Gump_manager.h"
00032 #include "gamewin.h"
00033 
00034 #include "Actor_gump.h"
00035 #include "Paperdoll_gump.h"
00036 #include "Spellbook_gump.h"
00037 #include "Stats_gump.h"
00038 #include "CombatStats_gump.h"
00039 #include "Jawbone_gump.h"
00040 #include "npcnear.h"
00041 #include "actors.h"
00042 #include "game.h"
00043 #include "Audio.h"
00044 #include "Yesno_gump.h"
00045 #include "gump_utils.h"
00046 #include "Slider_gump.h"
00047 
00048 using std::cout;
00049 using std::endl;
00050 
00051 Gump_manager::Gump_manager()
00052   : open_gumps(0), non_persistent_count(0), modal_gump_count(0), right_click_close(true), dont_pause_game(false)
00053 {
00054   std::string str;
00055   config->value("config/gameplay/right_click_closes_gumps", str, "yes");
00056   if (str == "no")
00057     right_click_close = false;
00058   config->set("config/gameplay/right_click_closes_gumps", str, true);
00059 
00060   config->value("config/gameplay/gumps_dont_pause_game", str, "no");
00061   dont_pause_game = str == "yes";
00062   config->set("config/gameplay/gumps_dont_pause_game", dont_pause_game?"yes":"no", true);
00063 }
00064 
00065 
00066 /*
00067  *  Showing gumps.
00068  */
00069 
00070 bool Gump_manager::showing_gumps(bool no_pers) const
00071 {
00072   // If no gumps, or we do want to check for persistent, just check to see if any exist
00073   if (!no_pers || !open_gumps) return open_gumps != 0;
00074 
00075   // If we don't want to check for persistend
00076   for (Gump_list *gump = open_gumps; gump; gump = gump->next)
00077     if (!gump->gump->is_persistent()) return true;
00078 
00079   return false;
00080 }
00081 
00082 
00083 /*
00084  *  Find the highest gump that the mouse cursor is on.
00085  *
00086  *  Output: ->gump, or null if none.
00087  */
00088 
00089 Gump *Gump_manager::find_gump
00090   (
00091   int x, int y,     // Pos. on screen.
00092   bool pers       // Persistent?
00093   )
00094 {
00095   Gump_list *gmp;
00096   Gump *found = 0;    // We want last found in chain.
00097   for (gmp = open_gumps; gmp; gmp = gmp->next)
00098   {
00099     Gump *gump = gmp->gump;
00100     if (gump->has_point(x,y) && (pers || !gump->is_persistent()))
00101       found = gump;
00102   }
00103   return (found);
00104 }
00105 
00106 /*
00107  *  Find gump containing a given object.
00108  */
00109 
00110 Gump *Gump_manager::find_gump
00111   (
00112   Game_object *obj
00113   )
00114 {
00115           // Get container object is in.
00116   Game_object *owner = obj->get_owner();
00117   if (!owner)
00118     return (0);
00119           // Look for container's gump.
00120   for (Gump_list *gmp = open_gumps; gmp; gmp = gmp->next)
00121     if (gmp->gump->get_container() == owner)
00122       return (gmp->gump);
00123   return (0);
00124 }
00125 
00126 /*
00127  *  Find gump with a given owner & shapenum.
00128  */
00129 
00130 Gump *Gump_manager::find_gump
00131   (
00132   Game_object *owner,
00133   int shapenum
00134   )
00135 {
00136   Gump_list *gmp;     // See if already open.
00137   for (gmp = open_gumps; gmp; gmp = gmp->next)
00138     if (gmp->gump->get_owner() == owner &&
00139         gmp->gump->get_shapenum() == shapenum)
00140       return gmp->gump;
00141   return (0);
00142 }
00143 
00144 /*
00145  *  Add a gump to the end of a chain.
00146  */
00147 
00148 void Gump_manager::add_gump(Gump *gump)
00149 {
00150   Gump_list *g = new Gump_list(gump);
00151 
00152   if (!open_gumps)
00153     open_gumps = g;   // First one.
00154   else
00155   {
00156     Gump_list *last = open_gumps;
00157     while (last->next) last = last->next;
00158     last->next = g;
00159   }
00160   if (!gump->is_persistent()) // Count 'gump mode' gumps.
00161     {     // And pause the game, if we want it
00162     non_persistent_count++;
00163     if (!dont_pause_game) gwin->get_tqueue()->pause(Game::get_ticks());
00164     }
00165 }
00166 
00167 /*
00168  *  Close a gump and delete it
00169  */
00170 
00171 bool Gump_manager::close_gump(Gump *gump)
00172 {
00173   bool ret = remove_gump(gump);
00174   delete gump;
00175   return ret;
00176 }
00177 
00178 /*
00179  *  Remove a gump from the chain
00180  */
00181 
00182 bool Gump_manager::remove_gump(Gump *gump)
00183 {
00184   if (open_gumps)
00185   {
00186     if (open_gumps->gump == gump)
00187     {
00188       Gump_list *p = open_gumps->next;
00189       delete open_gumps;
00190       open_gumps = p;
00191     }
00192     else
00193     {
00194       Gump_list *p = open_gumps;    // Find prev. to this.
00195       while (p->next != 0 && p->next->gump != gump) p = p->next;
00196 
00197       if (p->next)
00198       {
00199         Gump_list *g = p->next->next;
00200         delete p->next;
00201         p->next = g;
00202       }
00203       else
00204         return true;
00205     }
00206     if (!gump->is_persistent()) // Count 'gump mode' gumps.
00207       {     // And resume queue if last.
00208           // Gets messed up upon 'load'.
00209       if (non_persistent_count > 0)
00210         non_persistent_count--;
00211       if (!dont_pause_game) gwin->get_tqueue()->resume(Game::get_ticks());
00212       }
00213   }
00214 
00215   return false;
00216 }
00217 
00218 /*
00219  *  Show a gump.
00220  */
00221 
00222 void Gump_manager::add_gump
00223   (
00224   Game_object *obj,   // Object gump represents.
00225   int shapenum      // Shape # in 'gumps.vga'.
00226   )
00227 {
00228   int paperdoll = 0;
00229   
00230   if (shapenum >= ACTOR_FIRST_GUMP && shapenum <= ACTOR_LAST_GUMP
00231     && Game::get_game_type() == BLACK_GATE)
00232     paperdoll = 1;
00233 
00234   // overide for paperdolls
00235   if (shapenum == 123 && (Game::get_game_type() == SERPENT_ISLE ||
00236     (sman->can_use_paperdolls() && sman->get_bg_paperdolls())))
00237     paperdoll=2;
00238   
00239   Gump *dragged = gwin->get_dragging_gump();
00240   
00241   // If we are dragging the same, just return
00242   if (dragged && dragged->get_owner() == obj && dragged->get_shapenum() == shapenum)
00243     return;
00244 
00245   static int cnt = 0;   // For staggering them.
00246   Gump_list *gmp;     // See if already open.
00247   for (gmp = open_gumps; gmp; gmp = gmp->next)
00248     if (gmp->gump->get_owner() == obj &&
00249         gmp->gump->get_shapenum() == shapenum)
00250       break;
00251 
00252   if (gmp)      // Found it?
00253   {     // Move it to end.
00254     if (gmp->next)
00255     {
00256       Gump *gump = gmp->gump;
00257       remove_gump(gump);
00258       add_gump(gump);
00259     }
00260     gwin->paint();
00261     return;
00262   }
00263 
00264   int x = (1 + cnt)*gwin->get_width()/10, 
00265       y = (1 + cnt)*gwin->get_height()/10;
00266 
00267   ShapeID s_id(shapenum, 0, paperdoll == 2 ? SF_PAPERDOL_VGA : SF_GUMPS_VGA);
00268       Shape_frame *shape = s_id.get_shape();
00269     
00270   if (x + shape->get_xright() > gwin->get_width() ||
00271       y + shape->get_ybelow() > gwin->get_height())
00272   {
00273     cnt = 0;
00274     x = gwin->get_width()/10;
00275     y = gwin->get_width()/10;
00276   }
00277 
00278   Gump *new_gump = 0;
00279   Actor *npc = 0;
00280     if (obj)
00281         npc = obj->as_actor();
00282   if (npc && paperdoll == 2)
00283     new_gump = new Paperdoll_gump(npc, x, y, npc->get_npc_num());
00284   else if (npc && paperdoll) 
00285     new_gump = new Actor_gump(npc, x, y, shapenum);
00286   else if (shapenum == game->get_shape("gumps/statsdisplay"))
00287     new_gump = new Stats_gump((Container_game_object *) obj, x, y);
00288   else if (shapenum == game->get_shape("gumps/spellbook"))
00289     new_gump = new Spellbook_gump((Spellbook_object *) obj);
00290   else if (Game::get_game_type() == SERPENT_ISLE)
00291   {
00292     if (shapenum == game->get_shape("gumps/spell_scroll"))
00293       new_gump = new Spellscroll_gump(obj);
00294     else if (shapenum >= game->get_shape("gumps/cstats/1")&&
00295         shapenum <= game->get_shape("gumps/cstats/6"))
00296       new_gump = new CombatStats_gump(x, y);
00297     else if (shapenum == game->get_shape("gumps/jawbone"))
00298       new_gump = new Jawbone_gump(
00299           (Jawbone_object*) obj, x, y);
00300   }
00301 
00302   if (!new_gump)
00303     new_gump = new Container_gump((Container_game_object *) obj, x, y, shapenum);
00304 
00305           // Paint new one last.
00306   add_gump(new_gump);
00307   if (++cnt == 8)
00308     cnt = 0;
00309   int sfx = Audio::game_sfx(14);
00310   Audio::get_ptr()->play_sound_effect(sfx); // The weird noise.
00311   gwin->paint();      // Show everything.
00312 }
00313 
00314 /*
00315  *  End gump mode.
00316  */
00317 
00318 void Gump_manager::close_all_gumps
00319   (
00320   bool pers
00321   )
00322 {
00323   bool removed = false;
00324 
00325   Gump_list *prev = 0;
00326   Gump_list *next = open_gumps;
00327 
00328   while (next)    // Remove all gumps.
00329     {
00330     Gump_list *gump = next;
00331     next = gump->next;
00332 
00333     // Don't delete if persistant or modal.
00334     if ((!gump->gump->is_persistent() || pers) &&
00335         !gump->gump->is_modal())
00336     {
00337       if (!gump->gump->is_persistent())
00338         gwin->get_tqueue()->resume(Game::get_ticks());
00339       if (prev) prev->next = gump->next;
00340       else open_gumps = gump->next;
00341       delete gump->gump;
00342       delete gump;
00343       removed = true;
00344     }
00345     else
00346       prev = gump;
00347   }
00348   non_persistent_count = 0;
00349   gwin->get_npc_prox()->wait(4);    // Delay "barking" for 4 secs.
00350   if (removed) gwin->paint();
00351 }
00352 
00353 
00354 /*
00355  *  Handle a double-click.
00356  */
00357 
00358 bool Gump_manager::double_clicked
00359   (
00360   int x, int y,       // Coords in window.
00361   Game_object *&obj
00362   )
00363   {
00364   Gump *gump = find_gump(x, y);
00365 
00366   if (gump)
00367   {     // Find object in gump.
00368     obj = gump->find_object(x, y);
00369     if (!obj)   // Maybe it's a spell.
00370     {
00371       Gump_button *btn = gump->on_button(x, y);
00372       if (btn) btn->double_clicked(x, y);
00373       else if (gwin->get_double_click_closes_gumps())
00374       {
00375         gump->close();
00376         gwin->paint();
00377       }
00378     }
00379     return true;
00380   }
00381 
00382   return false;
00383 }
00384 
00385 /*
00386  *  Update the gumps
00387  */
00388 void Gump_manager::update_gumps()
00389 {
00390   for (Gump_list *gmp = open_gumps; gmp; gmp = gmp->next)
00391     gmp->gump->update_gump();
00392 }
00393 
00394 /*
00395  *  Paint the gumps
00396  */
00397 void Gump_manager::paint()
00398 {
00399   for (Gump_list *gmp = open_gumps; gmp; gmp = gmp->next)
00400     gmp->gump->paint();
00401 }
00402 
00403 
00404 /*
00405  *  Verify user wants to quit.
00406  *
00407  *  Output: 1 to quit.
00408  */
00409 int Gump_manager::okay_to_quit()
00410 {
00411   if (Yesno_gump::ask("Do you really want to quit?"))
00412     quitting_time = QUIT_TIME_YES;
00413   return quitting_time;
00414 }
00415 
00416 
00417 int Gump_manager::handle_modal_gump_event
00418   (
00419   Modal_gump *gump,
00420   SDL_Event& event
00421   )
00422 {
00423   //  Game_window *gwin = Game_window::get_instance();
00424   int scale_factor = gwin->get_fastmouse() ? 1 
00425         : gwin->get_win()->get_scale();
00426   static bool rightclick;
00427 
00428   switch (event.type)
00429   {
00430   case SDL_MOUSEBUTTONDOWN:
00431 #ifdef DEBUG
00432 cout << "(x,y) rel. to gump is (" << ((event.button.x / scale_factor) - gump->get_x())
00433    << ", " << ((event.button.y / scale_factor) - gump->get_y()) << ")"<<endl;
00434 #endif
00435     if (event.button.button == 1)
00436       gump->mouse_down(event.button.x / scale_factor, 
00437             event.button.y / scale_factor);
00438     else if (event.button.button == 2 && gwin->get_mouse3rd()) {
00439       gump->key_down(SDLK_RETURN);
00440       gump->text_input(SDLK_RETURN, SDLK_RETURN);
00441     } else if (event.button.button == 3)
00442       rightclick = true;
00443     else if (event.button.button == 4) // mousewheel up
00444       gump->mousewheel_up();
00445     else if (event.button.button == 5) // mousewheel down
00446       gump->mousewheel_down();
00447     break;
00448   case SDL_MOUSEBUTTONUP:
00449     if (event.button.button == 1)
00450       gump->mouse_up(event.button.x / scale_factor,
00451             event.button.y / scale_factor);
00452     else if (event.button.button == 3 && gwin->get_mouse3rd() && rightclick) {
00453       rightclick = false;
00454       if (gumpman->can_right_click_close()) return 0;
00455     }
00456     break;
00457   case SDL_MOUSEMOTION:
00458     Mouse::mouse->move(event.motion.x / scale_factor, event.motion.y / scale_factor);
00459     Mouse::mouse_update = true;
00460           // Dragging with left button?
00461     if (event.motion.state & SDL_BUTTON(1))
00462       gump->mouse_drag(event.motion.x / scale_factor,
00463             event.motion.y / scale_factor);
00464     break;
00465   case SDL_QUIT:
00466     if (okay_to_quit())
00467       return (0);
00468   case SDL_KEYDOWN:
00469     {
00470       if (event.key.keysym.sym == SDLK_ESCAPE)
00471         return (0);
00472       if ((event.key.keysym.sym == SDLK_s) &&
00473         (event.key.keysym.mod & KMOD_ALT) &&
00474         (event.key.keysym.mod & KMOD_CTRL)) {
00475           make_screenshot(true);
00476           return 1;
00477       }
00478 
00479       int chr = event.key.keysym.sym;
00480 #if 0
00481       gump->key_down((event.key.keysym.mod & KMOD_SHIFT)
00482             ? toupper(chr) : chr, event);
00483 #else
00484       gump->key_down(event.key.keysym.sym);
00485       gump->text_input(event.key.keysym.sym, event.key.keysym.unicode);
00486 #endif
00487 
00488       break;
00489     }
00490   }
00491   return (1);
00492 }
00493 
00494 /*
00495  *  Handle a modal gump, like the range slider or the save box, until
00496  *  the gump self-destructs.
00497  *
00498  *  Output: 0 if user hit ESC.
00499  */
00500 
00501 int Gump_manager::do_modal_gump
00502   (
00503   Modal_gump *gump,   // What the user interacts with.
00504   Mouse::Mouse_shapes shape,  // Mouse shape to use.
00505   Paintable *paint    // Paint this over everything else.
00506   )
00507 {
00508   if (!modal_gump_count)
00509     SDL_EnableUNICODE(1); // enable unicode translation for text input
00510   modal_gump_count++;
00511 
00512 
00513   //  Game_window *gwin = Game_window::get_instance();
00514 
00515   // maybe make this selective? it's nice for menus, but annoying for sliders
00516   //  gwin->end_gump_mode();
00517 
00518   // Pause the game
00519   gwin->get_tqueue()->pause(SDL_GetTicks());
00520 
00521   Mouse::Mouse_shapes saveshape = Mouse::mouse->get_shape();
00522   if (shape != Mouse::dontchange)
00523     Mouse::mouse->set_shape(shape);
00524   int escaped = 0;
00525   add_gump(gump);
00526   gwin->paint();      // Show everything now.
00527   if (paint)
00528     paint->paint();
00529   Mouse::mouse->show();
00530   gwin->show();
00531   do
00532   {
00533     Delay();    // Wait a fraction of a second.
00534     Mouse::mouse->hide();   // Turn off mouse.
00535     Mouse::mouse_update = false;
00536     SDL_Event event;
00537     while (!escaped && !gump->is_done() && SDL_PollEvent(&event))
00538       escaped = !handle_modal_gump_event(gump, event);
00539     if (GL_manager::get_instance())
00540       {
00541       gwin->paint();  // OpenGL?  Paint each cycle.
00542       if (paint)
00543         paint->paint();
00544       }
00545     Mouse::mouse->show();   // Re-display mouse.
00546     if (!gwin->show() &&  // Blit to screen if necessary.
00547         Mouse::mouse_update)  // If not, did mouse change?
00548       Mouse::mouse->blit_dirty();
00549   }
00550   while (!gump->is_done() && !escaped);
00551   Mouse::mouse->hide();
00552   remove_gump(gump);
00553   Mouse::mouse->set_shape(saveshape);
00554           // Leave mouse off.
00555   gwin->paint();
00556   gwin->show(true);
00557 
00558   // Resume the game
00559   gwin->get_tqueue()->resume(SDL_GetTicks());
00560 
00561   modal_gump_count--;
00562 
00563   if (!modal_gump_count)
00564     SDL_EnableUNICODE(0);
00565 
00566   return (!escaped);
00567 }
00568 
00569 
00570 /*
00571  *  Prompt for a numeric value using a slider.
00572  *
00573  *  Output: Value, or 0 if user hit ESC.
00574  */
00575 
00576 int Gump_manager::prompt_for_number
00577   (
00578   int minval, int maxval,   // Range.
00579   int step,
00580   int defval,     // Default to start with.
00581   Paintable *paint    // Should be the conversation.
00582   )
00583 {
00584   Slider_gump *slider = new Slider_gump(minval, maxval,
00585               step, defval);
00586   int ok = do_modal_gump(slider, Mouse::hand, paint);
00587   int ret = !ok ? 0 : slider->get_val();
00588   delete slider;
00589   return (ret);
00590 }
00591 
00592 
00593 /*
00594  *  Show a number.
00595  */
00596 
00597 void Gump_manager::paint_num
00598   (
00599   int num,
00600   int x,        // Coord. of right edge of #.
00601   int y       // Coord. of top of #.
00602   )
00603 {
00604   //  Shape_manager *sman = Shape_manager::get_instance();
00605   const int font = 2;
00606   char buf[20];
00607     snprintf(buf, 20, "%d", num);
00608   sman->paint_text(font, buf, x - sman->get_text_width(font, buf), y);
00609 }
00610 
00611 /*
00612  *  
00613  */
00614 void Gump_manager::set_gumps_dont_pause_game(bool p)
00615 {
00616   // Don't do anything if they are the same
00617   if (dont_pause_game == p) return;
00618 
00619   dont_pause_game = p;
00620 
00621   // If pausing enabled, we need to go through and pause each gump
00622   if (!dont_pause_game) {
00623     for (Gump_list *gump = open_gumps; gump; gump = gump->next)
00624       if (!gump->gump->is_persistent()) 
00625         gwin->get_tqueue()->pause(Game::get_ticks());
00626   }
00627   // Otherwise we need to go through and resume each gump :-)
00628   else {
00629     for (Gump_list *gump = open_gumps; gump; gump = gump->next)
00630       if (!gump->gump->is_persistent()) 
00631         gwin->get_tqueue()->resume(Game::get_ticks());
00632   }
00633 }

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