drag.cc

Go to the documentation of this file.
00001 /*
00002  *  drag.cc - Dragging objects in Game_window.
00003  *
00004  *  Copyright (C) 2000-2001  The Exult Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #  include <config.h>
00023 #endif
00024 
00025 #include <iostream> /* Debugging */
00026 #include "gamewin.h"
00027 #include "gamemap.h"
00028 #include "drag.h"
00029 #include "Gump_button.h"
00030 #include "Gump.h"
00031 #include "mouse.h"
00032 #include "paths.h"
00033 #include "actors.h"
00034 #include "cheat.h"
00035 #include "chunks.h"
00036 #include "Audio.h"
00037 #include "Gump_manager.h"
00038 #include "ucmachine.h"
00039 #include "barge.h"
00040 
00041 using std::cout;
00042 using std::endl;
00043 
00044 /*
00045  *  Create for a given (newly created) object.
00046  */
00047 
00048 Dragging_info::Dragging_info
00049   (
00050   Game_object *newobj   // Object NOT in world.  This is
00051           //   dropped, or deleted.
00052   ) : obj(newobj), is_new(true), gump(0), button(0), old_pos(-1, -1, -1),
00053       old_foot(0, 0, 0, 0), old_lift(-1), quantity(obj->get_quantity()),
00054       paintx(-1000), painty(-1000),
00055       readied_index(-1), mousex(-1), mousey(-1), rect(0, 0, 0, 0),
00056       save(0), okay(true), possible_theft(false)
00057   {
00058   rect = gwin->get_shape_rect(obj);
00059   rect.enlarge(8);    // Make a little bigger.
00060           // Create buffer to backup background.
00061   save = gwin->get_win()->create_buffer(rect.w, rect.h);
00062   }
00063 
00064 /*
00065  *  Begin a possible drag.
00066  */
00067 
00068 Dragging_info::Dragging_info
00069   (
00070   int x, int y      // Mouse position.
00071   ) : obj(0), is_new(false), gump(0), button(0), old_pos(-1, -1, -1),
00072       old_foot(0, 0, 0, 0), old_lift(-1), quantity(0),
00073       readied_index(-1), mousex(x), mousey(y), rect(0, 0, 0, 0),
00074       save(0), okay(false), possible_theft(false)
00075   {
00076           // First see if it's a gump.
00077   gump = gumpman->find_gump(x, y);
00078   if (gump)
00079     {
00080     obj = gump->find_object(x, y);
00081     if (obj)
00082       {   // Save location info.
00083       gump->get_shape_location(obj, paintx, painty);
00084       old_pos = Tile_coord(obj->get_cx(), obj->get_cy(), 0);
00085       }
00086     else if ((button = gump->on_button(x, y)) != 0)
00087       {
00088       gump = 0;
00089       if (!button->is_draggable())
00090         return;
00091       button->push();
00092           // Pushed button, so make noise.
00093       Audio::get_ptr()->play_sound_effect(
00094           Audio::game_sfx(96));
00095       gwin->set_painted();
00096       }
00097     else if (gump->is_draggable())
00098       {   // Dragging whole gump.
00099       paintx = gump->get_x();
00100       painty = gump->get_y();
00101   cout << "(x,y) rel. to gump is (" << (x-paintx) << ", " <<
00102           (y-painty) << ")"<<endl;
00103       }
00104     else      // the gump isn't draggable
00105       return;
00106     }
00107   else        // Not found in gump?
00108     {
00109     obj = gwin->find_object(x, y);
00110     if (!obj)
00111       return;
00112           // Get coord. where painted.
00113     gwin->get_shape_location(obj, paintx, painty);
00114     old_pos = obj->get_tile();
00115     old_foot = obj->get_footprint();
00116     }
00117   if (obj)
00118     {
00119     quantity = obj->get_quantity();
00120           // Save original lift.
00121     old_lift = obj->get_outermost()->get_lift();
00122     }
00123   okay = true;
00124   }
00125 
00126 /*
00127  *  Delete dragging info.
00128  */
00129 
00130 Dragging_info::~Dragging_info
00131   (
00132   )
00133   {
00134   delete save;
00135   }
00136 
00137 /*
00138  *  First motion.
00139  *
00140  *  Output: false if failed.
00141  */
00142 
00143 bool Dragging_info::start
00144   (
00145   int x, int y      // Mouse position.
00146   )
00147   {
00148   if (x - mousex <= 2 && mousex - x <= 2 &&
00149       y - mousey <= 2 && mousey - y <= 2)
00150     return (false);   // Wait for greater motion.
00151   if (obj)
00152     {     // Don't want to move walls.
00153     if (!cheat.in_hack_mover() && !obj->is_dragable() &&
00154                 !obj->get_owner())
00155       {
00156       Mouse::mouse->flash_shape(Mouse::tooheavy);
00157       obj = 0;
00158       gump = 0;
00159       okay = false;
00160       return (false);
00161       }
00162     Game_object *owner = obj->get_outermost();
00163     if (owner == obj)
00164       {
00165           if (!cheat.in_hack_mover() && 
00166         !Fast_pathfinder_client::is_grabable(
00167            gwin->get_main_actor()->get_tile(), 
00168               obj->get_tile()))
00169         {
00170         Mouse::mouse->flash_shape(Mouse::blocked);
00171         obj = 0;
00172         okay = false;
00173         return (false);
00174         }
00175       }
00176     }
00177           // Store original pos. on screen.
00178   rect = gump ? (obj ? gump->get_shape_rect(obj) : gump->get_dirty())
00179           : gwin->get_shape_rect(obj);
00180   if (gump)     // Remove from actual position.
00181     if (obj)
00182       {
00183       Container_game_object *owner = gump->get_cont_or_actor(x,y);
00184       if (owner)
00185         readied_index = owner->find_readied(obj);
00186       gump->remove(obj);
00187       }
00188     else
00189       gumpman->remove_gump(gump);
00190   else
00191     obj->remove_this(true); // This SHOULD work (jsf 21-12-01).
00192           // Make a little bigger.
00193   rect.enlarge(obj ? 8 : 12);
00194   Rectangle crect = gwin->clip_to_win(rect);
00195   gwin->paint(crect);   // Paint over obj's. area.
00196           // Create buffer to backup background.
00197   save = gwin->get_win()->create_buffer(rect.w, rect.h);
00198   return true;
00199   }
00200 
00201 /*
00202  *  Mouse was moved while dragging.
00203  *
00204  *  Output: true iff movement started/continued.
00205  */
00206 
00207 bool Dragging_info::moved
00208   (
00209   int x, int y      // Mouse pos. in window.
00210   )
00211   {
00212   if (!obj && !gump)
00213     return (false);
00214   if (rect.w == 0)
00215     {
00216     if (!start(x, y))
00217       return false;
00218     }
00219   else        // Not first time?  Restore beneath.
00220     gwin->get_win()->put(save, rect.x, rect.y);
00221   gwin->set_painted();
00222   int deltax = x - mousex, deltay = y - mousey;
00223   mousex = x;
00224   mousey = y;
00225           // Shift to new position.
00226   rect.shift(deltax, deltay);
00227   paintx += deltax;
00228   painty += deltay;
00229   if (gump && !obj)     // Dragging a gump?
00230     gump->set_pos(paintx, painty);
00231   gwin->add_dirty(gwin->clip_to_win(rect));
00232   return (true);
00233   }
00234 
00235 /*
00236  *  Paint object being moved.
00237  */
00238 
00239 void Dragging_info::paint
00240   (
00241   )
00242   {
00243   if (!rect.w)      // Not moved enough yet?
00244     return;
00245   if (save)     // Save background.
00246     gwin->get_win()->get(save, rect.x, rect.y);
00247   if (obj)
00248     {
00249     if (obj->get_flag(Obj_flags::invisible))
00250       obj->paint_invisible(paintx, painty);
00251     else
00252       obj->paint_shape(paintx, painty);
00253     }
00254   else if (gump)
00255     {
00256     gump->paint();
00257     }
00258   }
00259 
00260 /*
00261  *  Mouse was released, so drop object. 
00262  *      Return true iff the dropping mouseclick has been handled. 
00263  *    (by buttonpress, drag)
00264  */
00265 
00266 bool Dragging_info::drop
00267   (
00268   int x, int y,     // Mouse pos.
00269   bool moved      // has mouse moved from starting pos?
00270   )
00271   {
00272   bool handled = moved;
00273   if (button)
00274     {
00275     button->unpush();
00276     if (button->on_button(x, y))
00277           // Clicked on button.
00278       button->activate();
00279     handled = true;
00280     }
00281   else if (!obj)      // Only dragging a gump?
00282     {
00283     if (!gump)
00284       return handled;
00285     if (!moved)   // A click just raises it to the top.
00286       gumpman->remove_gump(gump);
00287     gumpman->add_gump(gump);
00288     }
00289   else if (!moved)    // For now, if not moved, leave it.
00290     return handled;
00291   else if (!drop(x, y))   // Drop it.
00292     put_back();   // Wasn't (all) moved.
00293   obj = 0;      // Clear so we don't paint them.
00294   gump = 0;
00295   gwin->paint();
00296   return handled;
00297   }
00298 
00299 /*
00300  *  Check weight.
00301  *
00302  *  Output: false if too heavy, with mouse flashed.
00303  */
00304 
00305 static bool Check_weight
00306   (
00307   Game_window *gwin,
00308   Game_object *to_drop,
00309   Game_object *owner    // Who the new owner will be.
00310   )
00311   {
00312   if (cheat.in_hack_mover())  // hack-mover  -> no weight checking
00313     return true;
00314 
00315   if (!owner)
00316     return true;
00317   owner = owner->get_outermost();
00318   if (!owner->get_flag(Obj_flags::in_party))
00319     return true;    // Not a party member, so okay.
00320   int wt = owner->get_weight() + to_drop->get_weight();
00321   if (wt/10 > owner->get_max_weight())
00322     {
00323     Mouse::mouse->flash_shape(Mouse::tooheavy);
00324     return false;
00325     }
00326   return true;
00327   }
00328 
00329 /*
00330  *  Put back object where it came from.
00331  */
00332 
00333 void Dragging_info::put_back
00334   (
00335   )
00336   {
00337   if (gump)     // Put back remaining/orig. piece.
00338     {     // And don't check for volume!
00339     obj->set_chunk(old_pos.tx, old_pos.ty); // Restore saved vals.
00340     gump->add(obj, -2, -2, -2, -2, true);
00341     }
00342   else if (is_new)
00343     {
00344     obj->set_invalid(); // It's not in the world.
00345     obj->remove_this();
00346     }
00347   else        // Normal object.  Put it back.
00348     obj->move(old_pos);
00349   obj = 0;      // Just to be safe.
00350   is_new = false;
00351   }
00352 
00353 /*
00354  *  Drop object on a gump.
00355  *
00356  *  Output: False if not (all) of object was dropped.
00357  */
00358 
00359 bool Dragging_info::drop_on_gump
00360   (
00361   int x, int y,     // Mouse position.
00362   Game_object *to_drop,   // == obj if whole thing.
00363   Gump *on_gump     // Gump to drop it on.
00364   )
00365   {
00366   if (!Check_weight(gwin, to_drop, on_gump->get_cont_or_actor(x,y)))
00367     return false;
00368   if (on_gump != gump)    // Not moving within same gump?
00369     possible_theft = true;
00370           // Add, and allow to combine.
00371   if (!on_gump->add(to_drop, x, y, paintx, painty, false, true))
00372     {     // Failed.
00373     if (to_drop != obj)
00374       {   // Watch for partial drop.
00375       int nq = to_drop->get_quantity();
00376       if (nq < quantity)
00377         obj->modify_quantity(quantity - nq);
00378       }
00379     Mouse::mouse->flash_shape(Mouse::wontfit);
00380     return false;
00381     }
00382   return true;
00383   }
00384 
00385 /*
00386  *  Drop object onto the map.
00387  *
00388  *  Output: False if not (all) of object was dropped.
00389  */
00390 
00391 bool Dragging_info::drop_on_map
00392   (
00393   int x, int y,     // Mouse position.
00394   Game_object *to_drop    // == obj if whole thing.
00395   )
00396   {
00397   int max_lift = cheat.in_hack_mover() ? 13 :
00398           gwin->get_main_actor()->get_lift() + 5;
00399   int skip = gwin->get_render_skip_lift();
00400   if (max_lift >= skip)   // Don't drop where we cannot see.
00401     max_lift = skip - 1;
00402           // Drop where we last painted it.
00403   int posx = paintx, posy = painty;
00404   if (posx == -1000)    // Unless we never painted.
00405     { posx = x; posy = y; }
00406   int lift;
00407           // Was it dropped on something?
00408   Game_object *found = gwin->find_object(x, y);
00409   bool dropped = false; // 1 when dropped.
00410   if (found && found != obj)
00411     {
00412     if (!Check_weight(gwin, to_drop, found))
00413       return false;
00414     if (found->drop(to_drop))
00415       dropped = possible_theft = true;
00416           // Try to place on 'found'.
00417     else if ((lift = found->get_lift() +
00418            found->get_info().get_3d_height()) <= max_lift)
00419       dropped = gwin->drop_at_lift(to_drop,posx, posy, lift);
00420     else
00421       {   // Too high.
00422       Mouse::mouse->flash_shape(Mouse::redx);
00423       Audio::get_ptr()->play_sound_effect(
00424               Audio::game_sfx(76));
00425       return false;
00426       }
00427     }
00428           // Find where to drop it.
00429   for (lift = old_lift; !dropped && lift <= max_lift; lift++)
00430     dropped = gwin->drop_at_lift(to_drop, posx, posy, lift);
00431   if (!dropped)
00432     {
00433     Mouse::mouse->flash_shape(Mouse::blocked);
00434     Audio::get_ptr()->play_sound_effect(Audio::game_sfx(76));
00435     return false;
00436     }
00437           // Moved more than 2 tiles.
00438   if (!gump && to_drop->get_tile().distance(old_pos) > 2)
00439     possible_theft = true;
00440   return true;
00441   }
00442 
00443 /*
00444  *  Drop at given position.
00445  *  ++++++NOTE:  Potential problems here with 'to_drop' being deleted by
00446  *    call to add().  Probably add() should provide feedback if obj.
00447  *    is combined with another.
00448  *
00449  *  Output: False if put_back() should be called.
00450  */
00451 
00452 bool Dragging_info::drop
00453   (
00454   int x, int y      // Mouse position.
00455   )
00456   {
00457           // Get orig. loc. info.
00458   int oldcx = old_pos.tx/c_tiles_per_chunk, 
00459       oldcy = old_pos.ty/c_tiles_per_chunk;
00460   Game_object *to_drop = obj; // If quantity, split it off.
00461           // Being liberal about taking stuff:
00462   int okay_to_move = to_drop->get_flag(Obj_flags::okay_to_take);
00463   int old_top = old_pos.tz + obj->get_info().get_3d_height();
00464           // First see if it's a gump.
00465   Gump *on_gump = gumpman->find_gump(x, y);
00466           // Don't prompt if within same gump.
00467   if (quantity > 1 && (!on_gump || on_gump != gump))
00468     quantity = gumpman->prompt_for_number(0, quantity, 
00469                                1, quantity);
00470   if (quantity <= 0)
00471     return false;
00472   if (quantity < obj->get_quantity())
00473     {     // Need to drop a copy.
00474     to_drop = gmap->create_ireg_object(
00475         obj->get_shapenum(), obj->get_framenum());
00476     to_drop->modify_quantity(quantity - 1);
00477     if (okay_to_move) // Make sure copy is okay to take.
00478       to_drop->set_flag(Obj_flags::okay_to_take);
00479     }
00480           // Drop it.
00481   if (!(on_gump ? drop_on_gump(x, y, to_drop, on_gump)
00482         : drop_on_map(x, y, to_drop)))
00483     return false;
00484           // Make a 'dropped' sound.
00485   Audio::get_ptr()->play_sound_effect(Audio::game_sfx(74));
00486   if (!gump)      // Do eggs where it came from.
00487     gmap->get_chunk(oldcx, oldcy)->activate_eggs(obj,
00488           old_pos.tx, old_pos.ty, old_pos.tz, 
00489           old_pos.tx, old_pos.ty);
00490           // Special:  BlackSword in SI.
00491   else if (readied_index >= 0 && obj->get_shapenum() == 806)
00492           // Do 'unreadied' usecode.
00493     gump->get_cont_or_actor(x,y)->call_readied_usecode(
00494       readied_index, obj, Usecode_machine::unreadied);
00495   if (on_gump)      // Do 'readied' usecode.
00496     {
00497     Container_game_object *owner = on_gump->get_cont_or_actor(x,y);
00498     int index = owner ? owner->find_readied(obj) : -1;
00499     if (index >= 0)
00500       owner->call_readied_usecode(index,
00501           obj, Usecode_machine::readied);
00502     }
00503           // On a barge?
00504   Barge_object *barge = gwin->get_moving_barge();
00505   if (barge)
00506     barge->set_to_gather(); // Refigure what's on barge.
00507           // Check for theft.
00508   if (!okay_to_move && !cheat.in_hack_mover() && possible_theft &&
00509       !gwin->is_in_dungeon())
00510     gwin->theft();      
00511   if (to_drop == obj)   // Whole thing?
00512     {     // Watch for stuff on top of it.
00513     if (old_foot.w > 0)
00514       Map_chunk::gravity(old_foot, old_top);
00515     return true;    // All done.
00516     }
00517           // Subtract quantity moved.
00518   obj->modify_quantity(-quantity);
00519   return false;     // Put back the rest.
00520   }
00521 
00522 /*
00523  *  Begin a possible drag when the mouse button is depressed.  Also detect
00524  *  if the 'close' checkmark on a gump is being depressed.
00525  *
00526  *  Output: true iff object selected for dragging
00527  */
00528 
00529 bool Game_window::start_dragging
00530   (
00531   int x, int y      // Position in window.
00532   )
00533   {
00534   delete dragging;
00535   dragging = new Dragging_info(x, y);
00536   if (dragging->okay)
00537     return (true);    // Success, so far.
00538   delete dragging;
00539   dragging = 0;
00540   return false;
00541   }
00542 
00543 /*
00544  *  Mouse moved while dragging.
00545  */
00546 
00547 bool Game_window::drag
00548   (
00549   int x, int y      // Mouse position in window.
00550   )
00551   {
00552   return dragging ? dragging->moved(x, y) : false;
00553   }
00554 
00555 
00556 /*
00557  *  Mouse was released, so drop object. 
00558  *      Return true iff the dropping mouseclick has been handled. 
00559  *    (by buttonpress, drag)
00560  *  Output: MUST set dragging = 0.
00561  */
00562 
00563 bool Game_window::drop_dragged
00564   (
00565   int x, int y,     // Mouse pos.
00566   bool moved      // has mouse moved from starting pos?
00567   )
00568   {
00569   if (!dragging)
00570     return false;
00571   bool handled = dragging->drop(x, y, moved);
00572   delete dragging;
00573   dragging = 0;
00574   return handled;
00575   }
00576 
00577 /*
00578  *  Try to drop at a given lift.  Note:  None of the drag state variables
00579  *  may be used here, as it's also called from the outside.
00580  *
00581  *  Output: true if successful.
00582  */
00583 
00584 bool Game_window::drop_at_lift
00585   (
00586   Game_object *to_drop,
00587   int x, int y,     // Pixel coord. in window.
00588   int at_lift
00589   )
00590   {
00591   x += at_lift*4 - 1;   // Take lift into account, round.
00592   y += at_lift*4 - 1;
00593   int tx = (scrolltx + x/c_tilesize)%c_num_tiles;
00594   int ty = (scrollty + y/c_tilesize)%c_num_tiles;
00595   int cx = tx/c_tiles_per_chunk;
00596   int cy = ty/c_tiles_per_chunk;
00597   Map_chunk *chunk = map->get_chunk(cx, cy);
00598   int lift;     // Can we put it here?
00599   Shape_info& info = to_drop->get_info();
00600   int xtiles = info.get_3d_xtiles(), ytiles = info.get_3d_ytiles();
00601   int max_drop, move_flags;
00602   if (cheat.in_hack_mover())
00603     {
00604     max_drop = at_lift - cheat.get_edit_lift();
00605 //    max_drop = max_drop < 0 ? 0 : max_drop;
00606     if (max_drop < 0) // Below lift we're editing?
00607       return false;
00608     move_flags = MOVE_WALK|MOVE_MAPEDIT;
00609     }
00610   else
00611     {     // Allow drop of 5;
00612     max_drop = 5;
00613     move_flags = MOVE_WALK;
00614     }
00615   if (!Map_chunk::is_blocked(info.get_3d_height(), at_lift,
00616     tx - xtiles + 1, ty - ytiles + 1, xtiles, ytiles, 
00617       lift, move_flags, max_drop) && 
00618      (cheat.in_hack_mover() ||
00619           // Check for path to location.
00620       Fast_pathfinder_client::is_grabable(
00621     main_actor->get_tile(), Tile_coord(tx, ty, lift))))
00622     {
00623     to_drop->set_invalid();
00624     to_drop->move(tx, ty, lift);
00625 #ifdef DEBUG
00626     cout << "Dropping object at (" << tx << ", " << ty << ", " << lift
00627        << ")"<<endl;
00628 #endif
00629           // On an egg?
00630     chunk->activate_eggs(to_drop, tx, ty, lift, tx, ty);
00631 
00632     if (to_drop == main_actor) {
00633       center_view(to_drop->get_tile());
00634       paint();
00635     }
00636 
00637     return (true);
00638     }
00639   return (false);
00640   }
00641 

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