objs.cc

Go to the documentation of this file.
00001 /*
00002  *  objs.cc - Game objects.
00003  *
00004  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00005  *  Copyright (C) 2000-2002  The Exult Team
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020  */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #  include <config.h>
00024 #endif
00025 
00026 #include "objs.h"
00027 #include "chunks.h"
00028 #include "objiter.h"
00029 #include "egg.h"
00030 #include "gamewin.h"
00031 #include "gamemap.h"
00032 #include "actors.h"
00033 #include "ucmachine.h"
00034 #include "items.h"
00035 #include "dir.h"
00036 #include "ordinfo.h"
00037 #include "game.h"
00038 #include "Gump_manager.h"
00039 #include "effects.h"
00040 #include "databuf.h"
00041 
00042 #ifndef ALPHA_LINUX_CXX
00043 #  include <cstring>
00044 #  include <cstdio>
00045 #endif
00046 #include <algorithm>       // STL function things
00047 
00048 #ifdef USE_EXULTSTUDIO
00049 #include "cheat.h"
00050 #include "server.h"
00051 #include "objserial.h"
00052 #include "servemsg.h"
00053 #endif
00054 
00055 #ifndef UNDER_CE
00056 using std::cerr;
00057 using std::cout;
00058 using std::endl;
00059 using std::memcpy;
00060 using std::memset;
00061 using std::rand;
00062 using std::ostream;
00063 using std::strchr;
00064 using std::string;
00065 #endif
00066 
00067           // Offset to each neighbor, dir=0-7.
00068 short Tile_coord::neighbors[16] = {0,-1, 1,-1, 1,0, 1,1, 0,1,
00069               -1,1, -1,0, -1,-1 };
00070 Game_object *Game_object::editing = 0;
00071           // Bit 5=S, Bit6=reflect. on diag.
00072 unsigned char Game_object::rotate[8] = { 0, 0, 48, 48, 16, 16, 32, 32};
00073 
00074 extern bool combat_trace;
00075 
00076 /*
00077  *  Get direction to another object.
00078  */
00079 
00080 int Game_object::get_direction
00081   (
00082   Game_object *o2
00083   ) const
00084   {
00085   Tile_coord t1 = get_tile();
00086   Tile_coord t2 = o2->get_tile();
00087           // Treat as cartesian coords.
00088   return (int) Get_direction(t1.ty - t2.ty, t2.tx - t1.tx);
00089   }
00090 
00091 /*
00092  *  Get direction to a given tile.
00093  */
00094 
00095 int Game_object::get_direction
00096   (
00097   Tile_coord t2
00098   ) const
00099   {
00100   Tile_coord t1 = get_tile();
00101           // Treat as cartesian coords.
00102   return (int) Get_direction(t1.ty - t2.ty, t2.tx - t1.tx);
00103   }
00104 
00105 /*
00106  *  Get chunk this is in.
00107  */
00108 
00109 Map_chunk *Game_object::get_chunk
00110   (
00111   )
00112   {
00113   return gmap->get_chunk(cx, cy);
00114   }
00115 
00116 /*
00117  *  Does a given shape come in quantity.
00118  */
00119 static int Has_quantity
00120   (
00121   int shnum     // Shape number.
00122   )
00123   {
00124   Shape_info& info = ShapeID::get_info(shnum);
00125   return info.has_quantity();
00126   }
00127 
00128 static int Has_hitpoints(int shnum)
00129 {
00130   Shape_info& info = ShapeID::get_info(shnum);
00131   return ((info.get_shape_class() == Shape_info::has_hp) ||
00132       (info.get_shape_class() == Shape_info::container));
00133 
00134   // containers have hitpoints too ('resistance')
00135 }
00136 
00137 const int MAX_QUANTITY = 100;   // Highest quantity possible.
00138 
00139 /*
00140  *  Get the quantity.
00141  */
00142 
00143 int Game_object::get_quantity
00144   (
00145   ) const
00146   {
00147   int shnum = get_shapenum();
00148   if (Has_quantity(shnum))
00149     {
00150     int qual = quality & 0x7f;
00151     return qual ? qual : 1;
00152     }
00153   else
00154     return 1;
00155   }
00156 
00157 int Game_object::get_obj_hp() const
00158 {
00159   int shnum = get_shapenum();
00160   if (Has_hitpoints(shnum))
00161     return quality;
00162   else
00163     return 0;
00164 }
00165 
00166 void Game_object::set_obj_hp(int hp)
00167 {
00168   int shnum = get_shapenum();
00169   if (Has_hitpoints(shnum))
00170     set_quality(hp);
00171 }
00172 
00173 /*
00174  *  Get the volume.
00175  */
00176 
00177 int Game_object::get_volume
00178   (
00179   ) const
00180   {
00181   int vol = get_info().get_volume();
00182   return vol;     // I think U7 ignores quantity!
00183   }
00184 
00185 /*
00186  *  Add or remove from object's 'quantity', and delete if it goes to 0.
00187  *  Also, this sets the correct frame, even if delta == 0.
00188  *
00189  *  Output: Delta decremented/incremented by # added/removed.
00190  *    Container's volume_used field is updated.
00191  */
00192 
00193 int Game_object::modify_quantity
00194   (
00195   int delta,      // >=0 to add, <0 to remove.
00196   bool *del     // If !null, true ret'd if deleted.
00197   )
00198   {
00199   if (del)
00200     *del = false;
00201   if (!Has_quantity(get_shapenum()))
00202     {     // Can't do quantity here.
00203     if (delta > 0)
00204       return (delta);
00205     remove_this();    // Remove from container (or world).
00206     if (del)
00207       *del = true;
00208     return (delta + 1);
00209     }
00210   int quant = quality&0x7f; // Get current quality.
00211   if (!quant)
00212     quant = 1;    // Might not be set.
00213   int newquant = quant + delta;
00214   if (delta >= 0)     // Adding?
00215     {     // Too much?
00216     if (newquant > MAX_QUANTITY)
00217       newquant = MAX_QUANTITY;
00218     }
00219   else if (newquant <= 0)   // Subtracting.
00220     {
00221     remove_this();    // We're done for.
00222     if (del)
00223       *del = true;
00224     return (delta + quant);
00225     }
00226   int oldvol = get_volume();  // Get old volume used.
00227   quality = (char) newquant;  // Store new value.
00228   int shapenum = get_shapenum();
00229           // Set appropriate shape.
00230   int num_frames = get_num_frames();
00231   int new_frame = newquant - 1;
00232   if (new_frame > 7)    // Range is 0-7.
00233     new_frame = 7;
00234   if (shapenum == 565 ||    // Starbursts are special.
00235       shapenum == 636)    // So are serpentine daggers.
00236     set_frame(0);   // (Fixes messed-up games.)
00237   else if (shapenum != 842) // Leave reagants alone.
00238           // Guessing:  Works for ammo, arrows.
00239     set_frame(num_frames == 32 ? 24 + new_frame : new_frame);
00240 
00241   Container_game_object *owner = get_owner();
00242   if (owner)      // Update owner's volume.
00243     owner->modify_volume_used(get_volume() - oldvol);
00244   return (delta - (newquant - quant));
00245   }
00246 
00247 /*
00248  *  Based on frame #, get direction (N, S, E, W, 0-7), this (generally an
00249  *  NPC) is facing.
00250  */
00251 
00252 int Game_object::get_dir_facing
00253   (
00254   ) const
00255   {
00256   int reflect = get_framenum()&(16 | 32);
00257   switch (reflect)
00258     {
00259   case 0:
00260     return (int) north;
00261   case 48:
00262     return (int) east;
00263   case 16:
00264     return (int) south;
00265   case 32:
00266   default:
00267     return (int) west;
00268     }
00269   }
00270 
00271 /*
00272  *  Move to a new absolute location.  This should work even if the old
00273  *  location is invalid (cx=cy=255).
00274  */
00275 
00276 void Game_object::move
00277   (
00278   int newtx, 
00279   int newty, 
00280   int newlift
00281   )
00282   {
00283           // Figure new chunk.
00284   int newcx = newtx/c_tiles_per_chunk, newcy = newty/c_tiles_per_chunk;
00285   Map_chunk *newchunk = gmap->get_chunk_safely(newcx, newcy);
00286   if (!newchunk)
00287     return;     // Bad loc.
00288           // Remove from old.
00289   Map_chunk *oldchunk = gmap->get_chunk_safely(cx, cy);
00290   if (oldchunk)
00291     {
00292     gwin->add_dirty(this);  // Want to repaint old area.
00293     oldchunk->remove(this);
00294     }
00295   set_lift(newlift);    // Set new values.
00296   shape_pos = ((newtx%c_tiles_per_chunk) << 4) + newty%c_tiles_per_chunk;
00297   newchunk->add(this);    // Updates cx, cy.
00298   gwin->add_dirty(this);    // And repaint new area.
00299   }
00300 
00301 /*
00302  *  Change the frame and set to repaint areas.
00303  */
00304 
00305 void Game_object::change_frame
00306   (
00307   int frnum
00308   )
00309   {
00310   gwin->add_dirty(this);    // Set to repaint old area.
00311   set_frame(frnum);
00312   gwin->add_dirty(this);    // Set to repaint new.
00313   }
00314 
00315 /*
00316  *  Swap positions with another object (of the same footprint).
00317  *
00318  *  Output: 1 if successful, else 0.
00319  */
00320 
00321 int Game_object::swap_positions
00322   (
00323   Game_object *obj2
00324   )
00325   {
00326   Shape_info& inf1 = get_info();
00327   Shape_info& inf2 = obj2->get_info();
00328   if (inf1.get_3d_xtiles() != inf2.get_3d_xtiles() ||
00329       inf1.get_3d_ytiles() != inf2.get_3d_ytiles())
00330     return 0;   // Not the same size.
00331   Tile_coord p1 = get_tile();
00332   Tile_coord p2 = obj2->get_tile();
00333   remove_this(1);     // Remove (but don't delete) each.
00334   set_invalid();
00335   obj2->remove_this(1);
00336   obj2->set_invalid();
00337   move(p2.tx, p2.ty, p2.tz);  // Move to new locations.
00338   obj2->move(p1.tx, p1.ty, p1.tz);
00339   return (1);
00340   }
00341 
00342 /*
00343  *  Remove all dependencies.
00344  */
00345 
00346 void Game_object::clear_dependencies
00347   (
00348   )
00349   {
00350   Game_object_vector::const_iterator  X;
00351   
00352   // First do those we depend on.
00353   for(X = dependencies.begin(); X != dependencies.end(); ++X )
00354     (**X).dependors.remove(this);
00355   dependencies.clear();
00356   
00357   // Now those who depend on us.
00358   for(X = dependors.begin(); X != dependors.end(); ++X )
00359     (**X).dependencies.remove(this);
00360   dependors.clear();
00361   }
00362 
00363 /*
00364  *  Check an object in find_nearby() against the mask.
00365  *
00366  *  Output: 1 if it passes.
00367  */
00368 static int Check_mask
00369   (
00370   Game_window *gwin,
00371   Game_object *obj,
00372   int mask
00373   )
00374   {
00375   Shape_info& info = obj->get_info();
00376   if ((mask&(4|8)) &&   // Both seem to be all NPC's.
00377       !info.is_npc())
00378     return 0;
00379   Shape_info::Shape_class sclass = info.get_shape_class();
00380           // Egg/barge?
00381   if ((sclass == Shape_info::hatchable || sclass == Shape_info::barge) &&
00382       !(mask&0x10))   // Only accept if bit 16 set.
00383     return 0;
00384   if (info.is_transparent() &&  // Transparent?
00385       !(mask&0x80))
00386     return 0;
00387           // Invisible object?
00388   if (obj->get_flag(Obj_flags::invisible))
00389     if (!(mask&20)) // Guess:  0x20 == invisible.
00390       {
00391       if (!(mask&0x40)) // Guess:  Inv. party member.
00392         return 0;
00393       if (!obj->get_flag(Obj_flags::in_party))
00394         return 0;
00395       }
00396   return 1;     // Passed all tests.
00397 }
00398 
00399 /*
00400  *  Find objects near a given position.
00401  *
00402  *  Output: # found, appended to vec.
00403  */
00404 
00405 #define FN_VECTOR Egg_vector
00406 #define FN_OBJECT Egg_object
00407 #define FN_CAST ->as_egg()
00408 #include "find_nearby.h"
00409 
00410 int Game_object::find_nearby_eggs
00411   (
00412   Egg_vector& vec,
00413   int shapenum,
00414   int delta,
00415   int qual,
00416   int frnum
00417   ) const
00418   {
00419   return Game_object::find_nearby (vec, get_tile(), shapenum,
00420           delta, 16, qual, frnum);
00421   }
00422 
00423 #define FN_VECTOR Actor_vector
00424 #define FN_OBJECT Actor
00425 #define FN_CAST ->as_actor()
00426 #include "find_nearby.h"
00427 
00428 int Game_object::find_nearby_actors
00429   (
00430   Actor_vector& vec,
00431   int shapenum,
00432   int delta
00433   ) const
00434   {
00435   return Game_object::find_nearby(vec, get_tile(), shapenum,
00436             delta, 8, c_any_qual, c_any_framenum);
00437   }
00438 
00439 #define FN_VECTOR Game_object_vector
00440 #define FN_OBJECT Game_object
00441 #define FN_CAST
00442 #include "find_nearby.h"
00443 
00444 int Game_object::find_nearby
00445   (
00446   Game_object_vector& vec,
00447   int shapenum,
00448   int delta,
00449   int mask,
00450   int qual,
00451   int framenum
00452   ) const
00453   {
00454   return Game_object::find_nearby(vec, get_tile(), shapenum,
00455           delta, mask, qual, framenum);
00456   }
00457 
00458 /*
00459  *  For sorting closest to a given spot.
00460  */
00461 class Object_closest_sorter
00462   {
00463   Tile_coord pos;     // Pos. to get closest to.
00464 public:
00465   Object_closest_sorter(Tile_coord p) : pos(p)
00466     {  }
00467   bool operator()(const Game_object *o1, const Game_object *o2)
00468     {
00469     Tile_coord t1 = o1->get_tile(),
00470          t2 = o2->get_tile();
00471     return t1.distance(pos) < t2.distance(pos);
00472     }
00473   };
00474 
00475 /*
00476  *  Find the closest nearby objects with a shape in a given list.
00477  *
00478  *  Output: ->closest object, or 0 if none found.
00479  */
00480 
00481 Game_object *Game_object::find_closest
00482   (
00483   Game_object_vector& vec,  // List returned here, closest 1st.
00484   int *shapenums,     // Shapes to look for. 
00485           //   c_any_shapenum=any NPC.
00486   int num_shapes,     // Size of shapenums.
00487   int dist      // Distance to look (tiles).
00488   )
00489   {
00490   int i;
00491   for (i = 0; i < num_shapes; i++)
00492           // 0xb0 mask finds anything.
00493     find_nearby(vec, shapenums[i], dist, 0xb0);
00494   int cnt = vec.size();
00495   if (!cnt)
00496     return (0);
00497   if (cnt > 1)
00498     std::sort(vec.begin(), vec.end(), 
00499         Object_closest_sorter(get_tile()));
00500   return *(vec.begin());
00501   }
00502 
00503 /*
00504  *  Find the closest nearby object with a shape in a given list.
00505  *
00506  *  Output: ->object, or 0 if none found.
00507  */
00508 
00509 Game_object *Game_object::find_closest
00510   (
00511   int *shapenums,     // Shapes to look for. 
00512           //   c_any_shapenum=any NPC.
00513   int num_shapes,     // Size of shapenums.
00514   int dist      // Distance to look (tiles).
00515   )
00516   {
00517   Game_object_vector vec;     // Gets objects found.
00518   int i;
00519   for (i = 0; i < num_shapes; i++)
00520           // 0xb0 mask finds anything.
00521     find_nearby(vec, shapenums[i], dist, 0xb0);
00522   int cnt = vec.size();
00523   if (!cnt)
00524     return (0);
00525   Game_object *closest = 0; // Get closest.
00526   int best_dist = 10000;    // In tiles.
00527           // Get our location.
00528   Tile_coord loc = get_tile();
00529   for (Game_object_vector::const_iterator it = vec.begin();
00530             it != vec.end(); ++it)
00531     {
00532     Game_object *obj = *it;
00533     int dist = obj->get_tile().distance(loc);
00534     if (dist < best_dist)
00535       {
00536       closest = obj;
00537       best_dist = dist;
00538       }
00539     }
00540   return (closest);
00541   }
00542 
00543 /*
00544  *  Get footprint in absolute tiles.
00545  */
00546 
00547 Rectangle Game_object::get_footprint
00548   (
00549   )
00550   {
00551   Shape_info& info = get_info();
00552           // Get footprint.
00553   int frame = get_framenum();
00554   int xtiles = info.get_3d_xtiles(frame);
00555   int ytiles = info.get_3d_ytiles(frame);
00556   Tile_coord t = get_tile();
00557   Rectangle foot((t.tx - xtiles + 1 + c_num_tiles)%c_num_tiles, 
00558            (t.ty - ytiles + 1 + c_num_tiles)%c_num_tiles, 
00559               xtiles, ytiles);
00560   return foot;
00561   }
00562 
00563 /*
00564  *  Does this object block a given tile?
00565  */
00566 
00567 bool Game_object::blocks
00568   (
00569   Tile_coord tile
00570   )
00571   {
00572   Tile_coord t = get_tile();
00573   if (t.tx < tile.tx || t.ty < tile.ty || t.tz > tile.tz)
00574     return false;   // Out of range.
00575   Shape_info& info = get_info();
00576   int ztiles = info.get_3d_height(); 
00577   if (!ztiles || !info.is_solid())
00578     return false;   // Skip if not an obstacle.
00579           // Occupies desired tile?
00580   int frame = get_framenum();
00581   if (tile.tx > t.tx - info.get_3d_xtiles(frame) &&
00582       tile.ty > t.ty - info.get_3d_ytiles(frame) &&
00583       tile.tz < t.tz + ztiles)
00584     return true;
00585   return false;
00586   }
00587 
00588 /*
00589  *  Find the game object that's blocking a given tile.
00590  *
00591  *  Output: ->object, or 0 if not found.
00592  */
00593 
00594 Game_object *Game_object::find_blocking
00595   (
00596   Tile_coord tile     // Tile to check.
00597   )
00598   {
00599   Map_chunk *chunk = gmap->get_chunk(tile.tx/c_tiles_per_chunk,
00600                 tile.ty/c_tiles_per_chunk);
00601   Game_object *obj;
00602   Object_iterator next(chunk->get_objects());
00603   while ((obj = next.get_next()) != 0)
00604     if (obj->blocks(tile))
00605       return obj;
00606   return (0);
00607   }
00608 
00609 /*
00610  *  Find door blocking a given tile.
00611  *
00612  *  Output: ->door, or 0 if not found.
00613  */
00614 
00615 Game_object *Game_object::find_door
00616   (
00617   Tile_coord tile
00618   )
00619   {
00620   Map_chunk *chunk = gmap->get_chunk(tile.tx/c_tiles_per_chunk,
00621                 tile.ty/c_tiles_per_chunk);
00622   return chunk->find_door(tile);
00623   }
00624 
00625 /*
00626  *  Is this a closed door?
00627  */
00628 
00629 int Game_object::is_closed_door
00630   (
00631   ) const
00632   {
00633   Shape_info& info = get_info();
00634   if (!info.is_door())
00635     return 0;
00636           // Get door's footprint.
00637   int xtiles = info.get_3d_xtiles(), ytiles = info.get_3d_ytiles();
00638           // Get its location.
00639   Tile_coord doortile = get_tile();
00640   Tile_coord before, after; // Want tiles to both sides.
00641   if (xtiles > ytiles)    // Horizontal footprint?
00642     {
00643     before = doortile + Tile_coord(-xtiles, 0, 0);
00644     after = doortile + Tile_coord(1, 0, 0);
00645     }
00646   else        // Vertical footprint.
00647     {
00648     before = doortile + Tile_coord(0, -ytiles, 0);
00649     after = doortile + Tile_coord(0, 1, 0);
00650     }
00651           // Should be blocked before/after.
00652   return (Map_chunk::is_blocked(before) &&
00653         Map_chunk::is_blocked(after));
00654   }
00655 
00656 /*
00657  *  Get the topmost owner of this object.
00658  *
00659  *  Output: ->topmost owner, or the object itself.
00660  */
00661 
00662 Game_object *Game_object::get_outermost
00663   (
00664   )
00665   {
00666   Game_object *top = this;
00667   Game_object *above;
00668   while ((above = top->get_owner()) != 0)
00669     top = above;
00670   return top;
00671   }
00672 
00673 /*
00674  *  Show text by the object on the screen.
00675  */
00676 
00677 void Game_object::say
00678   (
00679   const char *text
00680   )
00681   {
00682   eman->add_text(text, this);
00683   }
00684 
00685 /*
00686  *  Show a random string from 'text.flx' by the object.
00687  */
00688 
00689 void Game_object::say
00690   (
00691   int from, int to    // Range (inclusive).
00692   )
00693 {
00694   if (from > to) return;
00695   int offset = rand()%(to - from + 1);
00696   if (from + offset < num_item_names)
00697     say(item_names[from + offset]);
00698 }
00699 
00700 /*
00701  *  Paint at given spot in world.
00702  */
00703 
00704 void Game_object::paint
00705   (
00706   )
00707   {
00708   int x, y;
00709   gwin->get_shape_location(this, x, y);
00710   paint_shape(x, y);
00711   }
00712 
00713 /*
00714  *  Paint outline.
00715  */
00716 
00717 void Game_object::paint_outline
00718   (
00719   Pixel_colors pix    // Color to use.
00720   )
00721   {
00722   int x, y;
00723   gwin->get_shape_location(this, x, y);
00724   ShapeID::paint_outline(x, y, pix);
00725   }
00726 
00727 /*
00728  *  Run usecode when double-clicked.
00729  */
00730 
00731 void Game_object::activate
00732   (
00733   int event
00734   )
00735   {
00736   if (edit())
00737     return;     // Map-editing.
00738   int usefun = get_shapenum();
00739           // Serpent Isle spell scrolls:
00740   if (usefun == 0x2cb && Game::get_game_type() == SERPENT_ISLE)
00741     {
00742     gumpman->add_gump(this, 65);
00743     return;
00744     }
00745           // !!!Special case:  books
00746   if (usefun == 0x282 && get_quality() >= 100 && get_quality() < 180)
00747     usefun = 0x638;
00748   else if (usefun == 0x282 && get_quality() >= 180 && 
00749        Game::get_game_type() == SERPENT_ISLE )
00750     usefun = 0x63b;
00751   else if (usefun == 0x2c1 && get_quality() >= 213 &&
00752        Game::get_game_type() == SERPENT_ISLE )
00753     usefun = 0x62a;
00754   ucmachine->call_usecode(usefun, this,
00755       (Usecode_machine::Usecode_events) event);
00756   }
00757 
00758 /*
00759  *  Edit in ExultStudio.
00760  */
00761 
00762 bool Game_object::edit
00763   (
00764   )
00765   {
00766 #ifdef USE_EXULTSTUDIO
00767   if (client_socket >= 0 && // Talking to ExultStudio?
00768       cheat.in_map_editor())
00769     {
00770     editing = 0;
00771     Tile_coord t = get_tile();
00772     unsigned long addr = (unsigned long) this;
00773     std::string name = get_name();
00774     if (Object_out(client_socket, Exult_server::obj, 
00775       addr, t.tx, t.ty, t.tz,
00776       get_shapenum(), get_framenum(), get_quality(),
00777                 name) != -1)
00778       {
00779       cout << "Sent object data to ExultStudio" << endl;
00780       editing = this;
00781       }
00782     else
00783       cout << "Error sending object to ExultStudio" <<endl;
00784     return true;
00785     }
00786 #endif
00787   return false;
00788   }
00789 
00790 /*
00791  *  Message to update from ExultStudio.
00792  */
00793 
00794 void Game_object::update_from_studio
00795   (
00796   unsigned char *data,
00797   int datalen
00798   )
00799   {
00800 #ifdef USE_EXULTSTUDIO
00801   unsigned long addr;
00802   int tx, ty, tz;
00803   int shape, frame, quality;
00804   std::string name;
00805   if (!Object_in(data, datalen, addr, tx, ty, tz, shape, frame,
00806     quality, name))
00807     {
00808     cout << "Error decoding object" << endl;
00809     return;
00810     }
00811   Game_object *obj = (Game_object *) addr;
00812   if (!editing || obj != editing)
00813     {
00814     cout << "Obj from ExultStudio is not being edited" << endl;
00815     return;
00816     }
00817 //  editing = 0;  // He may have chosen 'Apply', so still editing.
00818   gwin->add_dirty(obj);
00819   obj->set_shape(shape, frame);
00820   gwin->add_dirty(obj);
00821   obj->set_quality(quality);
00822           // See if it moved.
00823   Tile_coord oldt = obj->get_tile();
00824   if (oldt.tx != tx || oldt.ty != ty || oldt.tz != tz)
00825     obj->move(tx, ty, tz);
00826   cout << "Object updated" << endl;
00827 #endif
00828   }
00829 
00830 /*
00831  *  Remove an object from the world.
00832  *  The object is deleted.
00833  */
00834 
00835 void Game_object::remove_this
00836   (
00837   int nodel     // 1 to not delete.
00838   )
00839   {
00840   Map_chunk *chunk = gmap->get_chunk_safely(cx, cy);
00841   if (chunk)
00842     chunk->remove(this);
00843   if (!nodel)
00844     gwin->delete_object(this);
00845   }
00846 
00847 /*
00848  *  Can this be dragged?
00849  */
00850 
00851 int Game_object::is_dragable
00852   (
00853   ) const
00854   {
00855   return (0);     // Default is 'no'.
00856   }
00857 
00858 /*
00859  *  Static method to get shape's weight in 1/10 stones.  0 means infinite.
00860  */
00861 
00862 int Game_object::get_weight
00863   (
00864   int shnum,      // Shape #,
00865   int quant     // Quantity.
00866   )
00867   {
00868   int wt = quant * ShapeID::get_info(shnum).get_weight();
00869           // Special case:  reagents, coins.
00870   if (shnum == 842 || shnum == 644 || 
00871       (Game::get_game_type() == SERPENT_ISLE &&
00872           // Monetari/guilders/filari:
00873        (shnum == 951 || shnum == 952 || shnum == 948)))
00874   {
00875     wt /= 10;
00876     if (wt <= 0) wt = 1;
00877   }
00878 
00879   if (Has_quantity(shnum))
00880     if (wt <= 0) wt = 1;
00881 
00882   return wt;
00883   }
00884 
00885 /* 
00886  *  Get weight of object in 1/10 stones.
00887  */
00888 
00889 int Game_object::get_weight
00890   (
00891   )
00892   {
00893   return get_weight(get_shapenum(), get_quantity());
00894   }
00895 
00896 /*
00897  *  Get maximum weight in stones that can be held.
00898  *
00899  *  Output: Max. allowed, or 0 if no limit (i.e., not carried by an NPC).
00900  */
00901 
00902 int Game_object::get_max_weight
00903   (
00904   )
00905   {
00906           // Looking outwards for NPC.
00907   Container_game_object *own = get_owner();
00908   return own ? own->get_max_weight() : 0;
00909   }
00910 
00911 /*
00912  *  Add an object to this one by combining.
00913  *
00914  *  Output: 1, meaning object is completely combined to this.  Obj. is
00915  *      deleted in this case.
00916  *    0 otherwise, although obj's quantity may be
00917  *      reduced if combine==true.
00918  */
00919 
00920 bool Game_object::add
00921   (
00922   Game_object *obj,
00923   bool dont_check,    // 1 to skip volume/recursion check.
00924   bool combine      // True to try to combine obj.  MAY
00925           //   cause obj to be deleted.
00926   )
00927   {
00928   return combine ? drop(obj)!=0 : false;
00929   }
00930 
00931 /*
00932  *  Drop another onto this.
00933  *
00934  *  Output: 0 to reject, 1 to accept.
00935  */
00936 
00937 int Game_object::drop
00938   (
00939   Game_object *obj    // This may be deleted.
00940   )
00941   {
00942   int shapenum = get_shapenum();  // It's possible if shapes match.
00943   if (obj->get_shapenum() != shapenum ||
00944       !Has_quantity(shapenum) ||
00945           // ++++Really should use 
00946           //   Get_combine_info in contain.cc
00947           // Reagents are a special case.
00948       (shapenum == 842 && get_framenum() != obj->get_framenum()))
00949     return (0);
00950   int objq = obj->get_quantity();
00951   int total_quant = get_quantity() + objq;
00952   if (total_quant > MAX_QUANTITY) // Too much?
00953     return (0);
00954   modify_quantity(objq);    // Add to our quantity.
00955   obj->remove_this();   // It's been used up.
00956   return (1);
00957   }
00958 
00959 //#define DEBUGLT
00960 #ifdef DEBUGLT
00961 static int rx1 = -1, ry1 = -1, rx2 = -1, ry2 = -1;
00962 
00963 static void Debug_lt
00964   (
00965   int tx1, int ty1,   // 1st coord.
00966   int tx2, int ty2    // 2nd coord.
00967   )
00968   {
00969   if (tx1 == rx1 && ty1 == ry1)
00970     {
00971     if (tx2 == rx2 && ty2 == ry2)
00972       cout << "Debug_lt" << endl;
00973     }
00974   }
00975 #endif
00976 
00977 /*
00978  *  Compare ranges along a given dimension.
00979  */
00980 inline void Compare_ranges
00981   (
00982   int from1, int to1,   // First object's range.
00983   int from2, int to2,   // Second object's range.
00984           // Returns:
00985   int& cmp,     // -1 if 1st < 2nd, 1 if 1st > 2nd,
00986           //   0 if equal.
00987   bool& overlap     // true returned if they overlap.
00988   )
00989   {
00990   if (to1 < from2)
00991     {
00992     overlap = false;
00993     cmp = -1;
00994     }
00995   else if (to2 < from1)
00996     {
00997     overlap = false;
00998     cmp = 1;
00999     }
01000   else        // X's overlap.
01001     {
01002     overlap = true;
01003     if (from1 < from2)
01004       cmp = -1;
01005     else if (from1 > from2)
01006       cmp = 1;
01007     else if (to1 - from1 < to2 - from2)
01008       cmp = 1;
01009     else if (to1 - from1 > to2 - from2)
01010       cmp = -1;
01011     else
01012       cmp = 0;
01013     }
01014   }
01015 
01016 /*
01017  *  Compare two objects.
01018  *
01019  *  Output: -1 if 1st < 2nd, 0 if dont_care, 1 if 1st > 2nd.
01020  */
01021 
01022 int Game_object::compare
01023   (
01024   Ordering_info& inf1,    // Info. for object 1.
01025   Game_object *obj2
01026   )
01027   {
01028           // See if there's no overlap.
01029   Rectangle r2 = gwin->get_shape_rect(obj2);
01030   if (!inf1.area.intersects(r2))
01031     return (0);   // No overlap on screen.
01032   Ordering_info inf2(gwin, obj2, r2);
01033 #ifdef DEBUGLT
01034   Debug_lt(inf1.tx, inf1.ty, inf2.tx, inf2.ty);
01035 #endif
01036   int xcmp, ycmp, zcmp;   // Comparisons for a given dimension:
01037           //   -1 if o1<o2, 0 if o1==o2,
01038           //    1 if o1>o2.
01039   bool xover, yover, zover; // True if dim's overlap.
01040   Compare_ranges(inf1.xleft, inf1.xright, inf2.xleft, inf2.xright,
01041               xcmp, xover);
01042   Compare_ranges(inf1.yfar, inf1.ynear, inf2.yfar, inf2.ynear,
01043               ycmp, yover);
01044   Compare_ranges(inf1.zbot, inf1.ztop, inf2.zbot, inf2.ztop,
01045               zcmp, zover);
01046   if (!xcmp && !ycmp && !zcmp)
01047           // Same space?
01048           // Paint biggest area sec. (Fixes 
01049           //   plaque at Penumbra's.)
01050     return (inf1.area.w < inf2.area.w  && 
01051       inf1.area.h < inf2.area.h) ? -1 : 
01052       (inf1.area.w > inf2.area.w &&
01053       inf1.area.h > inf2.area.h) ? 1 : 0;
01054 //    return 0;   // Equal.
01055   if (xover & yover & zover)  // Complete overlap?
01056     if (!inf1.zs)   // Flat one is always drawn first.
01057       return !inf2.zs ? 0 : -1;
01058     else if (!inf2.zs)
01059       return 1;
01060   if (xcmp >= 0 && ycmp >= 0 && zcmp >= 0)
01061     return 1;   // GTE in all dimensions.
01062   if (xcmp <= 0 && ycmp <= 0 && zcmp <= 0)
01063     return -1;    // LTE in all dimensions.
01064   if (yover)      // Y's overlap.
01065     {
01066     if (xover)    // X's too?
01067       return zcmp;
01068     else if (zover)   // Y's and Z's?
01069       return xcmp;
01070           // Just Y's overlap.
01071     else if (!zcmp)   // Z's equal?
01072       return xcmp;
01073     else      // See if X and Z dirs. agree.
01074       if (xcmp == zcmp)
01075         return xcmp;
01076 #if 1 /* Woohoo!  Seems to work without messing up N. Trinsic gate. */
01077           // Experiment:  Fixes Trinsic mayor
01078           //   statue-through-roof.
01079     else if (inf1.ztop/5 < inf2.zbot/5 && inf2.info.occludes())
01080       return -1;  // A floor above/below.
01081     else if (inf2.ztop/5 < inf1.zbot/5 && inf1.info.occludes())
01082       return 1;
01083 #endif
01084     else
01085       return 0;
01086     }
01087   else if (xover)     // X's overlap.
01088     {
01089     if (zover)    // X's and Z's?
01090       return ycmp;
01091     else if (!zcmp)   // Z's equal?
01092       return ycmp;
01093     else
01094       return ycmp == zcmp ? ycmp : 0;
01095     }
01096           // Neither X nor Y overlap.
01097   else if (xcmp == -1)    // o1 X before o2 X?
01098     {
01099     if (ycmp == -1)   // o1 Y before o2 Y?
01100           // If Z agrees or overlaps, it's LT.
01101       return (zover || zcmp <= 0) ? -1 : 0;
01102     }
01103   else if (ycmp == 1)   // o1 Y after o2 Y?
01104     if (zover || zcmp >= 0)
01105       return 1;
01106 #if 1 /* So far, this seems to work without causing problems: */
01107           // Experiment:  Fixes Brit. museum
01108           //   statue-through-roof.
01109     else if (inf1.ztop/5 < inf2.zbot/5)
01110       return -1;  // A floor above.
01111     else
01112 #endif
01113       return 0;
01114   return 0;
01115   }
01116 
01117 
01118 /*
01119  *  Should this object be rendered before obj2?
01120  *  NOTE:  This older interface isn't as efficient.
01121  *
01122  *  Output: 1 if so, 0 if not, -1 if cannot compare.
01123  */
01124 int Game_object::lt
01125   (
01126   Game_object& obj2
01127   )
01128   {
01129   Ordering_info ord(gwin, this);
01130   int cmp = compare(ord, &obj2);
01131   return cmp == -1 ? 1 : cmp == 1 ? 0 : -1;
01132   }
01133 
01134 
01135 /*
01136  *  Get frame if rotated 1, 2, or 3 quadrants clockwise.  This is to
01137  *  support barges (ship, cart, flying carpet).
01138  */
01139 
01140 int Game_object::get_rotated_frame
01141   (
01142   int quads     // 1=90, 2=180, 3=270.
01143   )
01144   {
01145   int curframe = get_framenum();
01146   int shapenum = get_shapenum();
01147   Shape_info& info = get_info();
01148   if (shapenum == 292)    // Seat is a special case.
01149     {
01150     int dir = curframe%4; // Current dir (0-3).
01151     return (curframe - dir) + (dir + quads)%4;
01152     }
01153   else if (info.is_barge_part())  // Piece of a barge?
01154     switch (quads)
01155       {
01156     case 1:
01157       return (curframe^32)^((curframe&32) ? 3 : 1);
01158     case 2:
01159       return curframe^2;
01160     case 3:
01161       return (curframe^32)^((curframe&32) ? 1 : 3);
01162     default:
01163       return curframe;
01164       }
01165   else
01166           // Reflect.  Bit 32==horizontal.
01167     return curframe ^ ((quads%2)<<5);
01168   }
01169 
01170 
01171 /*
01172  *  Figure attack points against an object, and also run weapon's usecode.
01173  */
01174 
01175 int Game_object::attack_object
01176   (
01177   Actor *attacker,
01178   int weapon_shape,   // Weapon shape, or 0 to use readied.
01179   int ammo_shape
01180   )
01181   {
01182   int wpoints = 0;
01183   Weapon_info *winf;
01184   if (weapon_shape > 0)
01185     winf = ShapeID::get_info(weapon_shape).get_weapon_info();
01186   else if (ammo_shape > 0)  // Not sure about all this...
01187     winf = ShapeID::get_info(ammo_shape).get_weapon_info();
01188   else
01189     winf = attacker->get_weapon(wpoints);
01190   int usefun;     // Run usecode if present.
01191   if (winf && (usefun = winf->get_usecode()) != 0)
01192     ucmachine->call_usecode(usefun, this,
01193           Usecode_machine::weapon);
01194   if (!wpoints && winf)
01195     wpoints = winf->get_damage();
01196   if (!wpoints)     // Telekenesis should NOT destroy!
01197     return 0;
01198   if (attacker)
01199     wpoints += attacker->get_level() +
01200       attacker->get_effective_prop((int) Actor::strength);
01201   return wpoints;
01202   }
01203 
01204 /*
01205  *  Being attacked.
01206  *
01207  *  Output: 0 if destroyed, else object itself.
01208  */
01209 
01210 Game_object *Game_object::attacked
01211   (
01212   Actor *attacker,
01213   int weapon_shape,   // Weapon shape, or 0 to use readied,
01214           //   or -weapon shape if explosion.
01215   int ammo_shape
01216   )
01217   {
01218   if (weapon_shape < 0)   // We ignore this flag.
01219     weapon_shape = -weapon_shape;
01220   int wpoints = attack_object(attacker, weapon_shape, ammo_shape);
01221   int shnum = get_shapenum();
01222   int frnum = get_framenum();
01223 
01224   int hp = get_obj_hp();    // Returns 0 if doesn't have HP's or is
01225           //   indestructible,
01226   if (!hp) {      //   with exceptions:
01227     // some special cases
01228     // guessing these don't have hitpoints by default because
01229     // doors need their 'quality' field for something else
01230 
01231     if (shnum == 432 || shnum == 433) // doors
01232       {
01233       if (get_quality()==0 || weapon_shape==704)
01234         // only 'normal' doors (or powderkeg)
01235         if (frnum != 3 && frnum < 7) // no magic-locked or steel doors
01236           hp = 6;
01237       }
01238     else if (shnum == 270 || shnum == 376) // more doors
01239       {
01240       if (get_quality()==0 || weapon_shape==704)
01241         // only 'normal' doors (or powderkeg)
01242         if (frnum < 3 || (frnum >= 8 && frnum <= 10) ||
01243           (frnum >= 16 && frnum <= 18)) // no magic or steel doors
01244           hp = 6;
01245       }
01246           // Serpent statue at end of SI:
01247     else if (shnum == 743 && Game::get_game_type() == SERPENT_ISLE)
01248       hp = 1;
01249     else if (shnum == 704 && weapon_shape == 704) { // Powder keg...
01250       // cause chain reaction
01251 
01252       // marked already detonating powderkegs with quality
01253       if (get_quality()==0) {
01254         Tile_coord pos = get_tile();
01255         eman->add_effect(
01256           new Explosion_effect(pos, this));
01257       }
01258     }
01259           // Arrow hitting practice targt?
01260     else if (shnum == 735 && ammo_shape == 722) {
01261       int newframe = !frnum ? (3*(rand()%8) + 1)
01262           : ((frnum%3) != 0 ? frnum + 1 : frnum);
01263       change_frame(newframe);
01264     }
01265 #if 0
01266     else if (shnum == 522 && frnum < 2) { // locked normal chest
01267       if (get_quality() == 0 || get_quality() == 255)
01268         hp = 6;
01269     }
01270 #endif
01271 
01272   }
01273 
01274   string name = "<trap>";
01275   if (attacker)
01276     name = attacker->get_name();
01277 
01278 
01279   if (hp == 0) { // indestructible
01280     if (combat_trace) {
01281       cout << name << " attacks " << get_name()
01282          << ". No effect." << endl;
01283     }
01284     return this;
01285   }
01286 
01287   if (combat_trace) {
01288     cout << name << " hits " << get_name()
01289        << " for " << wpoints << " hit points, leaving "
01290        << hp - wpoints << " remaining" << endl;
01291   }
01292 
01293   if (wpoints >= hp) {
01294     // object destroyed
01295     eman->remove_text_effect(this);
01296     ucmachine->call_usecode(0x626, this, Usecode_machine::weapon);
01297     return 0;
01298   } else {
01299     set_obj_hp(hp - wpoints);
01300 
01301     return this;
01302   }
01303 }
01304 
01305 /*
01306  *  Write the common IREG data for an entry.
01307  */
01308 
01309 void Game_object::write_common_ireg
01310   (
01311   unsigned char *buf    // 4-byte buffer to be filled.
01312   )
01313   {
01314           // Coords:
01315   buf[0] = ((get_cx()%16) << 4) | get_tx();
01316   buf[1] = ((get_cy()%16) << 4) | get_ty();
01317   int shapenum = get_shapenum(), framenum = get_framenum();
01318   buf[2] = shapenum&0xff;
01319   buf[3] = ((shapenum>>8)&3) | (framenum<<2);
01320   }
01321 
01322 /*
01323  *  Paint terrain objects only.
01324  */
01325 
01326 void Terrain_game_object::paint_terrain
01327   (
01328   )
01329   {
01330   paint();
01331   }
01332 
01333 /*
01334  *  Move to a new absolute location.  This should work even if the old
01335  *  location is invalid (cx=cy=255).
01336  */
01337 
01338 void Ifix_game_object::move
01339   (
01340   int newtx, 
01341   int newty, 
01342   int newlift
01343   )
01344   {
01345   Game_object::move(newtx, newty, newlift);
01346           // Mark superchunk as 'modified'.
01347   int cx = get_cx(), cy = get_cy();
01348   if (cx >= 0 && cx < c_num_chunks &&
01349       cy >= 0 && cy < c_num_chunks)
01350     gmap->set_ifix_modified(cx, cy);
01351   }
01352 
01353 /*
01354  *  Remove an object from the world.
01355  *  The object is deleted.
01356  */
01357 
01358 void Ifix_game_object::remove_this
01359   (
01360   int nodel     // 1 to not delete.
01361   )
01362   {
01363           // Mark superchunk as 'modified'.
01364   int cx = get_cx(), cy = get_cy();
01365   if (cx >= 0 && cx < c_num_chunks &&
01366       cy >= 0 && cy < c_num_chunks)
01367     gmap->set_ifix_modified(cx, cy);
01368   Game_object::remove_this(nodel);
01369   }
01370 
01371 /*
01372  *  Write out an IFIX object.
01373  */
01374 
01375 void Ifix_game_object::write_ifix
01376   (
01377   DataSource *ifix      // Where to write.
01378   )
01379   {
01380   unsigned char buf[4];
01381   buf[0] = shape_pos;
01382   buf[1] = lift;
01383   int shapenum = get_shapenum(), framenum = get_framenum();
01384   buf[2] = shapenum&0xff;
01385   buf[3] = ((shapenum>>8)&3) | (framenum<<2);
01386   ifix->write((char*)buf, sizeof(buf));
01387   }
01388 
01389 
01390 
01391 
01392 

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