Item.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2003-2007 The Pentagram team
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 */
00018 
00019 #include "pent_include.h"
00020 
00021 #include "Item.h"
00022 #include "GUIApp.h"
00023 #include "Usecode.h"
00024 #include "GameData.h"
00025 #include "UCMachine.h"
00026 #include "UCList.h"
00027 #include "World.h"
00028 #include "DelayProcess.h"
00029 #include "Container.h"
00030 #include "Actor.h"
00031 #include "Kernel.h"
00032 #include "getObject.h"
00033 #include "MainShapeArchive.h"
00034 #include "GumpShapeArchive.h"
00035 #include "Shape.h"
00036 #include "ShapeInfo.h"
00037 #include "ItemFactory.h"
00038 #include "CurrentMap.h"
00039 #include "UCStack.h"
00040 #include "Direction.h"
00041 #include "BarkGump.h"
00042 #include "AskGump.h"
00043 #include "GumpNotifyProcess.h"
00044 #include "ActorBarkNotifyProcess.h"
00045 #include "ContainerGump.h"
00046 #include "PaperdollGump.h"
00047 #include "GameMapGump.h"
00048 #include "WorldPoint.h"
00049 #include "GravityProcess.h"
00050 #include "LoopScript.h"
00051 #include "IDataSource.h"
00052 #include "ODataSource.h"
00053 #include "CameraProcess.h"
00054 #include "SpriteProcess.h"
00055 #include "SliderGump.h"
00056 #include "UCProcess.h"
00057 #include "DestroyItemProcess.h"
00058 #include "AudioProcess.h"
00059 #include "GameInfo.h"
00060 #include "MainActor.h"
00061 #include "MissileTracker.h"
00062 
00063 #include <cstdlib>
00064 
00065 // p_dynamic_cast stuff
00066 DEFINE_RUNTIME_CLASSTYPE_CODE(Item,Object);
00067 
00068 Item::Item()
00069         : shape(0), frame(0), x(0), y(0), z(0),
00070           flags(0), quality(0), npcnum(0), mapnum(0),
00071           extendedflags(0), parent(0), 
00072           cachedShape(0), cachedShapeInfo(0), gump(0), gravitypid(0),
00073           last_setup(0)
00074 {
00075 
00076 }
00077 
00078 
00079 Item::~Item()
00080 {
00081 
00082 }
00083 
00084 void Item::dumpInfo()
00085 {
00086         pout << "Item " << getObjId() << " (class "
00087                  << GetClassType().class_name << ", shape "
00088                  << getShape() << ", " << getFrame() << ", (";
00089 
00090         if (parent) {
00091                 sint32 gx, gy;
00092                 getGumpLocation(gx, gy);
00093                 pout << gx << "," << gy;
00094         } else {
00095                 pout << x << "," << y << "," << z;
00096         }
00097 
00098         pout << ") q:" << getQuality()
00099                  << ", m:" << getMapNum() << ", n:" << getNpcNum()
00100                  << ", f:" << std::hex << getFlags() << ", ef:"
00101                  << getExtFlags() << ")" << std::dec << std::endl;
00102 }
00103 
00104 Container *Item::getParentAsContainer() const
00105 {
00106         // No parent, no container
00107         if (!parent) return 0;
00108 
00109         Container *p = getContainer(parent);
00110 
00111         if (!p) {
00112                 perr << "Item " << getObjId() << " parent (" << parent << ") is an invalid Container ObjID" << std::endl;
00113                 CANT_HAPPEN();
00114         }
00115 
00116         return p;
00117 }
00118 
00119 Item* Item::getTopItem()
00120 {
00121         Container *parent = getParentAsContainer();
00122 
00123         if (!parent) return this;
00124 
00125         while (parent->getParentAsContainer()) {
00126                 parent = parent->getParentAsContainer();
00127         }
00128 
00129         return parent;
00130 }
00131 
00132 void Item::setLocation(sint32 X, sint32 Y, sint32 Z)
00133 {
00134         x = X;
00135         y = Y;
00136         z = Z;
00137 }
00138 
00139 void Item::move(sint32 X, sint32 Y, sint32 Z)
00140 {
00141         bool no_lerping = false;
00142         CurrentMap * map = World::get_instance()->getCurrentMap();
00143         int mapChunkSize = map->getChunkSize();
00144 
00145         if (getObjId() == 1 && Z < 0) {
00146                 perr.printf("Warning: moving avatar below Z=0. (%d,%d,%d)\n", X, Y, Z);
00147         }
00148 
00149         // It's currently in the ethereal void, remove it from there
00150         if (flags & FLG_ETHEREAL) {
00151 
00152                 // Remove us from the ethereal void
00153                 World::get_instance()->etherealRemove(objid);
00154         }
00155 
00156         // Remove from container (if contained or equiped)
00157         if (flags & (FLG_CONTAINED|FLG_EQUIPPED))
00158         {
00159                 if (parent) {
00160                         // If we are flagged as Ethereal, we are already removed 
00161                         if (!(flags & FLG_ETHEREAL)) {
00162                                 Container *p = getParentAsContainer();
00163                                 if (p) p->removeItem(this);
00164                         }
00165                 }
00166                 else
00167                         perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no parent" << std::endl;
00168 
00169                 // Clear our owner. 
00170                 parent = 0;
00171 
00172                 // No lerping when going from a container to somewhere else
00173                 no_lerping = true;
00174         }
00175         // Item needs to be removed if it in the map, and it is moving to a 
00176         // different chunk
00177         else if ((extendedflags & EXT_INCURMAP) && 
00178                         ((x / mapChunkSize != X / mapChunkSize) || 
00179                         (y / mapChunkSize != Y / mapChunkSize))) {
00180 
00181                 // Remove us from the map
00182                 map->removeItem(this);
00183         }
00184 
00185         // Unset all the various flags that no longer apply
00186         flags &= ~(FLG_CONTAINED|FLG_EQUIPPED|FLG_ETHEREAL);
00187 
00188         // Set the location
00189         x = X;
00190         y = Y;
00191         z = Z;
00192 
00193         // Add it to the map if needed
00194         if (!(extendedflags & EXT_INCURMAP))
00195         {
00196                 // Disposable fast only items get put at the end
00197                 // While normal items get put at start
00198                 if (flags & (FLG_DISPOSABLE|FLG_FAST_ONLY))
00199                         map->addItemToEnd(this);
00200                 else
00201                         map->addItem(this);
00202         }
00203 
00204         // Call just moved
00205         callUsecodeEvent_justMoved();
00206 
00207         // Are we moving somewhere fast
00208         bool dest_fast = map->isChunkFast(X/mapChunkSize, Y/mapChunkSize);
00209 
00210         // No lerping for this move
00211         if (no_lerping) extendedflags |= EXT_LERP_NOPREV;
00212 
00213         // If the destination is not in the fast area, and we are in
00214         // the fast area, we need to call leaveFastArea, as long as we aren't
00215         // being followed by the camera. We also disable lerping in such a case
00216         if (!dest_fast && (flags & Item::FLG_FASTAREA)) {
00217                 extendedflags |= EXT_LERP_NOPREV;
00218                 if (extendedflags & EXT_CAMERA) 
00219                         CameraProcess::GetCameraProcess()->ItemMoved();
00220                 else
00221                         leaveFastArea();
00222 
00223                 return; //we are done
00224         }
00225         // Or if the dest is fast, and we are not, we need to call enterFastArea
00226         else if (dest_fast && !(flags & Item::FLG_FASTAREA)) {
00227                 extendedflags |= EXT_LERP_NOPREV;
00228                 enterFastArea();
00229         }
00230 
00231         // If we are being followed, notify the camera that we moved
00232         // Note that we don't need to 
00233         if (extendedflags & EXT_CAMERA)
00234                 CameraProcess::GetCameraProcess()->ItemMoved();
00235 }
00236 
00237 bool Item::moveToContainer(Container *container, bool checkwghtvol)
00238 {
00239         // Null container, report an error message
00240         if (!container) {
00241                 perr << "NULL container passed to Item::moveToContainer" << std::endl;
00242                 return false;
00243         }
00244 
00245         // Already there, do nothing, but only if not ethereal
00246         bool ethereal_same = false;
00247         if ( container->getObjId() == parent ) {
00248                 // If we are ethereal we'd like to know if we are just being moved back
00249                 if (flags & FLG_ETHEREAL) ethereal_same = true;
00250                 else return true;
00251         }
00252 
00253         // Eh, can't do it
00254         if (!container->CanAddItem(this,checkwghtvol)) return false;
00255 
00256         // It's currently in the ethereal void
00257         if (flags & FLG_ETHEREAL) {
00258 
00259                 // Remove us from the ethereal void
00260                 World::get_instance()->etherealRemove(objid);
00261         }
00262 
00263         // Remove from container (if contained or equiped)
00264         if (flags & (FLG_CONTAINED|FLG_EQUIPPED))
00265         {
00266                 if (parent) {
00267                         // If we are flagged as Ethereal, we are already removed 
00268                         if (!(flags & FLG_ETHEREAL)) {
00269                                 Container *p = getParentAsContainer();
00270                                 if (p) p->removeItem(this);
00271                         }
00272                 }
00273                 else
00274                         perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no parent" << std::endl;
00275 
00276                 // Clear our owner. 
00277                 parent = 0;
00278         }
00279         // Item needs to be removed if it in the map
00280         else if (extendedflags & EXT_INCURMAP) {
00281 
00282                 // Remove us from the map
00283                 World::get_instance()->getCurrentMap()->removeItem(this);
00284         }
00285 
00286         // Unset all the various flags that no longer apply
00287         flags &= ~(FLG_CONTAINED|FLG_EQUIPPED|FLG_ETHEREAL);
00288 
00289         // Set location to 0,0,0 if we aren't an ethereal item moving back
00290         if (!ethereal_same) x = y = 0;
00291         z = 0;
00292 
00293         // Add the item, don't bother with checking weight or vol since we already
00294         // know if it will fit or not
00295         container->addItem(this, false);
00296         
00297         // Set our owner. 
00298         parent = container->getObjId();
00299 
00300         // Set us contained
00301         flags |= FLG_CONTAINED;
00302 
00303         // If moving to avatar, mark as OWNED
00304         Item *p = this;
00305         while (p->getParentAsContainer())
00306                 p = p->getParentAsContainer();
00307         // In Avatar's inventory?
00308         if (p->getObjId() == 1)
00309                 setFlagRecursively(FLG_OWNED);
00310         
00311         // No lerping when moving to a container
00312         extendedflags |= EXT_LERP_NOPREV;
00313 
00314         // Call just moved
00315         callUsecodeEvent_justMoved();
00316 
00317         // For a container, it's fast if it's got a gump open
00318         bool dest_fast = (container->flags & FLG_GUMP_OPEN)!=0;
00319 
00320         // If the destination is not in the fast area, and we are in
00321         // the fast area, we need to call leaveFastArea
00322         if (!dest_fast && (flags & Item::FLG_FASTAREA))
00323                 leaveFastArea();
00324         // Or if the dest is fast, and we are not, we need to call enterFastArea
00325         else if (dest_fast && !(flags & Item::FLG_FASTAREA))
00326                 enterFastArea();
00327 
00328         // Done
00329         return true;
00330 }
00331 
00332 void Item::moveToEtherealVoid()
00333 {
00334         // It's already Ethereal
00335         if (flags & FLG_ETHEREAL) return;
00336 
00337         // Add it to the ethereal void
00338         World::get_instance()->etherealPush(objid);
00339 
00340         // It's owned by something removed it from the something, but keep flags
00341         if (flags & (FLG_CONTAINED|FLG_EQUIPPED)) {
00342 
00343                 if (parent)
00344                 {
00345                         Container *p = getParentAsContainer();
00346                         if (p) p->removeItem(this);
00347                 }
00348                 else
00349                         perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no parent" << std::endl;
00350         }
00351         else if (extendedflags & EXT_INCURMAP) {
00352                 World::get_instance()->getCurrentMap()->removeItem(this);
00353         }
00354 
00355         // Set the ETHEREAL Flag
00356         flags |=  FLG_ETHEREAL;
00357 }
00358 
00359 void Item::returnFromEtherealVoid()
00360 {
00361         // It's not Ethereal
00362         if (!(flags & FLG_ETHEREAL)) return;
00363 
00364         // Ok, we can do 2 things here
00365         // If an item has the contained or Equipped flags set, we return it to it's owner
00366         if (flags & (FLG_CONTAINED|FLG_EQUIPPED)) {
00367                 Container *p = getParentAsContainer();
00368                 if (!p) {
00369                         perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no valid parent" << std::endl;
00370                         CANT_HAPPEN();
00371                 }
00372                 moveToContainer(p);
00373         }
00374         // or we return it to the world
00375         else {
00376                 move(x,y,z);
00377         }
00378 
00379 }
00380 
00381 void Item::movedByPlayer()
00382 {
00383         // owned-by-avatar items can't be stolen
00384         if (flags & FLG_OWNED) return;
00385 
00386         // Otherwise, player is stealing.
00387         // See if anybody is around to notice.
00388         Item* avatar = getItem(1);
00389         UCList itemlist(2);
00390         LOOPSCRIPT(script, LS_TOKEN_TRUE);
00391         CurrentMap* currentmap = World::get_instance()->getCurrentMap();
00392         currentmap->areaSearch(&itemlist, script, sizeof(script),
00393                                                    avatar, 640, false);
00394         
00395         for (unsigned int i = 0; i < itemlist.getSize(); ++i) {
00396                 Actor *actor = getActor(itemlist.getuint16(i));
00397                 if (actor && !actor->isDead())
00398                         actor->callUsecodeEvent_AvatarStoleSomething(getObjId());
00399         }
00400 }
00401 
00402 sint32 Item::getZ() const
00403 {
00404         return z;
00405 }
00406 
00407 void Item::getLocationAbsolute(sint32& X, sint32& Y, sint32& Z) const
00408 {
00409         if (parent) {
00410                 Item *p = getParentAsContainer();
00411 
00412                 if (p) {
00413                         p->getLocationAbsolute(X,Y,Z);
00414                         return;
00415                 }
00416         }
00417 
00418         X = x;
00419         Y = y;
00420         Z = z;
00421 }
00422 
00423 void Item::getGumpLocation(sint32& X, sint32& Y) const
00424 {
00425         if (!parent) return;
00426 
00427         X = y & 0xFF;
00428         Y = (y >> 8) & 0xFF;
00429 }
00430 
00431 void Item::setGumpLocation(sint32 X, sint32 Y)
00432 {
00433         if (!parent) return;
00434 
00435         y = (X & 0xFF) + ((Y & 0xFF) << 8);
00436 }
00437 
00438 void Item::randomGumpLocation()
00439 {
00440         if (!parent) return;
00441 
00442         // This sets the coordinates to (255,255) and lets the ContainerGump
00443         // randomize the position when it is next opened.
00444         y = 0xFFFF;
00445 }
00446 
00447 void Item::getCentre(sint32& X, sint32& Y, sint32& Z) const
00448 {
00449         // constants!
00450         ShapeInfo *shapeinfo = getShapeInfo();
00451         if (flags & FLG_FLIPPED)
00452         {
00453                 X = x - shapeinfo->y * 16;
00454                 Y = y - shapeinfo->x * 16;
00455         }
00456         else
00457         {
00458                 X = x - shapeinfo->x * 16;
00459                 Y = y - shapeinfo->y * 16;
00460         }
00461 
00462         Z = z + shapeinfo->z * 4;
00463 }
00464 
00465 Pentagram::Box Item::getWorldBox() const
00466 {
00467         sint32 xd,yd,zd;
00468         getFootpadWorld(xd,yd,zd);
00469         return Pentagram::Box(x,y,z,xd,yd,zd);
00470 }
00471 
00472 bool Item::overlaps(Item& item2) const
00473 {
00474         sint32 x1a,y1a,z1a,x1b,y1b,z1b;
00475         sint32 x2a,y2a,z2a,x2b,y2b,z2b;
00476         getLocation(x1b,y1b,z1a);
00477         item2.getLocation(x2b,y2b,z2a);
00478 
00479         sint32 xd,yd,zd;
00480         getFootpadWorld(xd,yd,zd);
00481         x1a = x1b - xd;
00482         y1a = y1b - yd;
00483         z1b = z1a + zd;
00484 
00485         item2.getFootpadWorld(xd,yd,zd);
00486         x2a = x2b - xd;
00487         y2a = y2b - yd;
00488         z2b = z2a + zd;
00489 
00490         if (x1b <= x2a || x2b <= x1a) return false;
00491         if (y1b <= y2a || y2b <= y1a) return false;
00492         if (z1b <= z2a || z2b <= z1a) return false;
00493         return true;
00494 }
00495 
00496 bool Item::overlapsxy(Item& item2) const
00497 {
00498         sint32 x1a,y1a,z1a,x1b,y1b;
00499         sint32 x2a,y2a,z2a,x2b,y2b;
00500         getLocation(x1b,y1b,z1a);
00501         item2.getLocation(x2b,y2b,z2a);
00502 
00503         sint32 xd,yd,zd;
00504         getFootpadWorld(xd,yd,zd);
00505         x1a = x1b - xd;
00506         y1a = y1b - yd;
00507 
00508         item2.getFootpadWorld(xd,yd,zd);
00509         x2a = x2b - xd;
00510         y2a = y2b - yd;
00511 
00512         if (x1b <= x2a || x2b <= x1a) return false;
00513         if (y1b <= y2a || y2b <= y1a) return false;
00514         return true;
00515 }
00516 
00517 bool Item::isOn(Item& item2) const
00518 {
00519         sint32 x1a,y1a,z1a,x1b,y1b;
00520         sint32 x2a,y2a,z2a,x2b,y2b,z2b;
00521         getLocation(x1b,y1b,z1a);
00522         item2.getLocation(x2b,y2b,z2a);
00523 
00524         sint32 xd,yd,zd;
00525         getFootpadWorld(xd,yd,zd);
00526         x1a = x1b - xd;
00527         y1a = y1b - yd;
00528 
00529         item2.getFootpadWorld(xd,yd,zd);
00530         x2a = x2b - xd;
00531         y2a = y2b - yd;
00532         z2b = z2a + zd;
00533 
00534         if (x1b <= x2a || x2b <= x1a) return false;
00535         if (y1b <= y2a || y2b <= y1a) return false;
00536         if (z2b == z1a) return true;
00537         return false;
00538 }
00539 
00540 bool Item::canExistAt(sint32 x, sint32 y, sint32 z, bool needsupport) const
00541 {
00542         CurrentMap* cm = World::get_instance()->getCurrentMap();
00543         Item* support;
00544         bool valid = cm->isValidPosition(x, y, z, getShape(), getObjId(),
00545                                                                          &support, 0);
00546         return valid && (!needsupport || support);
00547 }
00548 
00549 int Item::getDirToItemCentre(Item& item2) const
00550 {
00551         sint32 ix,iy,iz;
00552         getCentre(ix,iy,iz);
00553 
00554         sint32 i2x,i2y,i2z;
00555         item2.getCentre(i2x,i2y,i2z);
00556 
00557         return Get_WorldDirection(i2y - iy, i2x - ix);
00558 }
00559 
00560 int Item::getRange(Item& item2, bool checkz) const
00561 {
00562         sint32 thisX, thisY, thisZ;
00563         sint32 otherX, otherY, otherZ;
00564         sint32 thisXd, thisYd, thisZd;
00565         sint32 otherXd, otherYd, otherZd;
00566         sint32 thisXmin, thisYmin, thisZmax;
00567         sint32 otherXmin, otherYmin, otherZmax;
00568 
00569         getLocationAbsolute(thisX, thisY, thisZ);
00570         item2.getLocationAbsolute(otherX, otherY, otherZ);
00571         getFootpadWorld(thisXd, thisYd, thisZd);
00572         item2.getFootpadWorld(otherXd, otherYd, otherZd);
00573 
00574         thisXmin = thisX - thisXd;
00575         thisYmin = thisY - thisYd;
00576         thisZmax = thisZ + thisZd;
00577 
00578         otherXmin = otherX - otherXd;
00579         otherYmin = otherY - otherYd;
00580         otherZmax = otherZ + otherZd;
00581 
00582         sint32 range = 0;
00583 
00584         if (thisXmin - otherX > range)
00585                 range = thisYmin - otherY;
00586         if (otherXmin - thisX > range)
00587                 range = thisXmin - otherX;
00588         if (thisYmin - otherY > range)
00589                 range = otherXmin - thisX;
00590         if (otherYmin - thisY > range)
00591                 range = otherYmin - thisY;
00592         if (checkz && thisZ - otherZmax > range)
00593                 range = thisZ - otherZmax;
00594         if (checkz && otherZ - thisZmax > range)
00595                 range = otherZ - thisZmax;
00596 
00597         return range;
00598 }
00599 
00600 ShapeInfo* Item::getShapeInfoFromGameInstance() const
00601 {
00602         return GameData::get_instance()->getMainShapes()->getShapeInfo(shape);
00603 }
00604 
00605 Shape* Item::getShapeObject() const
00606 {
00607         if (!cachedShape) cachedShape = GameData::get_instance()->getMainShapes()->getShape(shape);
00608         return cachedShape;
00609 }
00610 
00611 uint16 Item::getFamily()
00612 {
00613         return static_cast<uint16>(getShapeInfo()->family);
00614 }
00615 
00616 uint32 Item::getWeight()
00617 {
00618         uint32 weight = getShapeInfo()->weight;
00619 
00620         switch (getShapeInfo()->family) {
00621         case ShapeInfo::SF_QUANTITY:
00622                 return ((getQuality()*weight)+9)/10;
00623         case ShapeInfo::SF_REAGENT:
00624                 return getQuality()*weight;
00625         default:
00626                 return weight*10;
00627         }
00628 }
00629 
00630 uint32 Item::getTotalWeight()
00631 {
00632         return getWeight();
00633 }
00634 
00635 uint32 Item::getVolume()
00636 {
00637         // invisible items (trap markers and such) don't take up volume
00638         if (getFlags() & FLG_INVISIBLE) return 0;
00639 
00640 
00641         uint32 volume = getShapeInfo()->volume;
00642 
00643         switch (getShapeInfo()->family) {
00644         case ShapeInfo::SF_QUANTITY:
00645                 return ((getQuality()*volume)+99)/100;
00646         case ShapeInfo::SF_REAGENT:
00647                 return ((getQuality()*volume)+9)/10;
00648         case ShapeInfo::SF_CONTAINER:
00649                 return (volume == 0) ? 1 : volume;
00650         default:
00651                 return volume;
00652         }
00653 }
00654 
00655 bool Item::checkLoopScript(const uint8* script, uint32 scriptsize)
00656 {
00657         // if really necessary this could be made static to prevent news/deletes
00658         DynamicUCStack stack(0x40); // 64bytes should be plenty of room
00659 
00660         unsigned int i = 0;
00661 
00662         uint16 ui16a, ui16b;
00663 
00664         stack.push2(1); // default to true if script is empty
00665 
00666         while (i < scriptsize) {
00667                 switch(script[i]) {
00668                 case LS_TOKEN_FALSE: // false
00669                         stack.push2(0); break;
00670 
00671                 case LS_TOKEN_TRUE: // true
00672                         stack.push2(1); break;
00673 
00674                 case LS_TOKEN_END: // end
00675                         ui16a = stack.pop2();
00676                         return (ui16a != 0);
00677 
00678                 case LS_TOKEN_INT: // int
00680                         ui16a = script[i+1] + (script[i+2]<<8);
00681                         stack.push2(ui16a);
00682                         i += 2;
00683                         break;
00684 
00685                 case LS_TOKEN_AND: // and
00686                         ui16a = stack.pop2();
00687                         ui16b = stack.pop2();
00688                         if (ui16a != 0 && ui16b != 0)
00689                                 stack.push2(1);
00690                         else
00691                                 stack.push2(0);
00692                         break;
00693 
00694                 case LS_TOKEN_OR: // or
00695                         ui16a = stack.pop2();
00696                         ui16b = stack.pop2();
00697                         if (ui16a != 0 || ui16b != 0)
00698                                 stack.push2(1);
00699                         else
00700                                 stack.push2(0);
00701                         break;                  
00702 
00703                 case LS_TOKEN_NOT: // not
00704                         ui16a = stack.pop2();
00705                         if (ui16a != 0)
00706                                 stack.push2(0);
00707                         else
00708                                 stack.push2(1);
00709                         break;
00710 
00711                 case LS_TOKEN_STATUS: // item status
00712                         stack.push2(getFlags());
00713                         break;
00714 
00715                 case LS_TOKEN_Q: // item quality
00716                         stack.push2(getQuality());
00717                         break;
00718 
00719                 case LS_TOKEN_NPCNUM: // npc num
00720                         stack.push2(getNpcNum());
00721                         break;
00722 
00723                 case LS_TOKEN_EQUAL: // equal
00724                         ui16a = stack.pop2();
00725                         ui16b = stack.pop2();
00726                         if (ui16a == ui16b)
00727                                 stack.push2(1);
00728                         else
00729                                 stack.push2(0);
00730                         break;
00731 
00732                 case LS_TOKEN_GREATER: // greater than
00733                         ui16a = stack.pop2();
00734                         ui16b = stack.pop2();
00735                         if (ui16b > ui16a)
00736                                 stack.push2(1);
00737                         else
00738                                 stack.push2(0);
00739                         break;
00740 
00741                 case LS_TOKEN_LESS: // less than
00742                         ui16a = stack.pop2();
00743                         ui16b = stack.pop2();
00744                         if (ui16b < ui16a)
00745                                 stack.push2(1);
00746                         else
00747                                 stack.push2(0);
00748                         break;
00749 
00750                 case LS_TOKEN_GEQUAL: // greater or equal
00751                         ui16a = stack.pop2();
00752                         ui16b = stack.pop2();
00753                         if (ui16b >= ui16a)
00754                                 stack.push2(1);
00755                         else
00756                                 stack.push2(0);
00757                         break;
00758 
00759                 case LS_TOKEN_LEQUAL: // smaller or equal
00760                         ui16a = stack.pop2();
00761                         ui16b = stack.pop2();
00762                         if (ui16b <= ui16a)
00763                                 stack.push2(1);
00764                         else
00765                                 stack.push2(0);
00766                         break;
00767 
00768                 case LS_TOKEN_FAMILY: // item family
00769                         stack.push2(getFamily());
00770                         break;
00771 
00772                 case LS_TOKEN_SHAPE: // item shape
00773                         stack.push2(static_cast<uint16>(getShape()));
00774                         break;
00775 
00776                 case 'A': case 'B': case 'C': case 'D': case 'E':
00777                 case 'F': case 'G': case 'H': case 'I': case 'J':
00778                 case 'K': case 'L': case 'M': case 'N': case 'O':
00779                 case 'P': case 'Q': case 'R': case 'S': case 'T':
00780                 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
00781                 {
00782                         bool match = false;
00783                         int count = script[i] - '@';
00784                         for (int j = 0; j < count; j++) {
00786                                 if (getShape() == static_cast<uint32>(script[i+1] + (script[i+2]<<8)))
00787                                         match = true;
00788                                 i += 2;
00789                         }
00790                         if (match)
00791                                 stack.push2(1);
00792                         else
00793                                 stack.push2(0);
00794                 }
00795                 break;
00796 
00797                 case LS_TOKEN_FRAME: // item frame
00798                         stack.push2(static_cast<uint16>(getFrame()));
00799                         break;
00800 
00801                 case 'a': case 'b': case 'c': case 'd': case 'e':
00802                 case 'f': case 'g': case 'h': case 'i': case 'j':
00803                 case 'k': case 'l': case 'm': case 'n': case 'o':
00804                 case 'p': case 'q': case 'r': case 's': case 't':
00805                 case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
00806                 {
00807                         bool match = false;
00808                         int count = script[i] - '`';
00809                         for (int j = 0; j < count; j++) {
00811                                 if (getFrame() == static_cast<uint32>(script[i+1] + (script[i+2]<<8)))
00812                                         match = true;
00813                                 i += 2;
00814                         }
00815                         if (match)
00816                                 stack.push2(1);
00817                         else
00818                                 stack.push2(0);
00819                 }
00820                 break;
00821 
00822                 default:
00823                         perr.printf("Unknown loopscript opcode %02X\n", script[i]);
00824                 }
00825 
00826                 i++;
00827         }
00828         perr.printf("Didn't encounter $ in loopscript\n");
00829         return false;
00830 }
00831 
00832 
00833 sint32 Item::collideMove(sint32 dx, sint32 dy, sint32 dz, bool teleport, bool force, ObjId* hititem)
00834 {
00835         GUIApp *guiapp = GUIApp::get_instance();
00836         World *world = World::get_instance();
00837         CurrentMap *map = world->getCurrentMap();
00838 
00839         if (hititem) *hititem = 0;
00840 
00841         sint32 end[3] = { dx, dy, dz };
00842 
00843         sint32 start[3];
00844         if (parent)
00845         {
00846                 // If we are moving from a container, only check the destination
00847                 start[0] = end[0];
00848                 start[1] = end[1];
00849                 start[2] = end[2];
00850         }
00851         else
00852         {
00853                 // Otherwise check from where we are to where we want to go
00854                 getLocation(start[0], start[1], start[2]);
00855         }
00856 
00857         sint32 dims[3];
00858         getFootpadWorld(dims[0], dims[1], dims[2]);
00859 
00860         // Do the sweep test
00861         std::list<CurrentMap::SweepItem> collisions;
00862         std::list<CurrentMap::SweepItem>::iterator it;
00863         map->sweepTest(start, end, dims, getShapeInfo()->flags, objid,
00864                                    false, &collisions);
00865 
00866         // Ok, now to work out what to do
00867 
00868 
00869         // the force of the hit, used for the gotHit/hit usecode calls
00870         int deltax = abs(start[0] - end[0])/4;
00871         int deltay = abs(start[1] - end[1])/4;
00872         int deltaz = abs(start[2] - end[2]);
00873         int maxdirdelta = deltay;
00874         if (deltay > maxdirdelta) maxdirdelta = deltay;
00875         if (deltaz > maxdirdelta) maxdirdelta = deltaz;
00876         int hitforce = (deltax+deltay+deltaz+maxdirdelta)/2;
00877 
00878         // if we are contained, we always teleport
00879         if (teleport || parent)
00880         {
00881                 // If teleporting and not force, work out if we can reach the end
00882                 if (!force) 
00883                 {
00884                         for (it = collisions.begin(); it != collisions.end(); it++)
00885                         {
00886                                 // Uh oh, we hit something, can't move
00887                                 if (it->end_time == 0x4000 && !it->touching && it->blocking) {
00888                                         if (hititem) *hititem = it->item;
00889                                         return 0;
00890                                 }
00891                         }
00892                 }
00893 
00894                 // Trigger all the required events
00895                 bool we_were_released = false;
00896                 for (it = collisions.begin(); it != collisions.end(); it++)
00897                 {
00898                         Item *item = getItem(it->item);
00899 
00900                         // Hitting us at the start and end, don't do anything
00901                         if (!parent && it->hit_time == 0x0000 && 
00902                                         it->end_time == 0x4000)
00903                         {
00904                                 continue;
00905                         }
00906                         // Hitting us at the end (call hit on us, got hit on them)
00907                         else if (it->end_time == 0x4000)
00908                         {
00909                                 if (objid == 1 && guiapp->isShowTouchingItems())
00910                                         item->setExtFlag(Item::EXT_HIGHLIGHT);
00911 
00912                                 item->callUsecodeEvent_gotHit(objid, hitforce);
00913                                 callUsecodeEvent_hit(item->getObjId(), hitforce);
00914                         }
00915                         // Hitting us at the start (call release on us and them)
00916                         else if (!parent && it->hit_time == 0x0000)
00917                         {
00918                                 if (objid == 1) item->clearExtFlag(Item::EXT_HIGHLIGHT);
00919                                 we_were_released = true;
00920                                 item->callUsecodeEvent_release();
00921                         }
00922                 }
00923 
00924                 // Call release on us
00925                 if (we_were_released) callUsecodeEvent_release();
00926 
00927                 // Move US!
00928                 move(end[0], end[1], end[2]);
00929 
00930                 // We reached the end
00931                 return 0x4000;
00932         }
00933         else
00934         {
00935                 sint32 hit = 0x4000;
00936 
00937                 // If not force, work out if we can reach the end
00938                 // if not, need to do 'stuff'
00939                 // We don't care about items hitting us at the start
00940                 if (!force) 
00941                 {
00942                         for (it = collisions.begin(); it != collisions.end(); it++)
00943                         {
00944                                 if (it->blocking && !it->touching) {
00945                                         if (hititem) *hititem = it->item;
00946                                         hit = it->hit_time;
00947                                         break;
00948                                 }
00949                         }
00950                         if (hit < 0) hit = 0;
00951 
00952                         if (hit != 0x4000)
00953                         {
00954 #if 0
00955                                 pout << " Hit time: " << hit << std::endl;
00956                                 pout << "    Start: " << start[0] << ", "<< start[1] << ", " << start[2] << std::endl;
00957                                 pout << "      End: " << end[0] << ", "<< end[1] << ", " << end[2] << std::endl;
00958 #endif
00959                                 it->GetInterpolatedCoords(end,start,end);
00960 #if 0
00961                                 pout << "Collision: " << end[0] << ", "<< end[1] << ", " << end[2] << std::endl;
00962 #endif
00963                         }
00964                 }
00965 
00966                 // Trigger all the required events
00967                 bool we_were_released = false;
00968                 for (it = collisions.begin(); it != collisions.end(); it++)
00969                 {
00970                         Item *item = getItem(it->item);
00971                         if (!item) continue;
00972 
00973                         // Did we go past the endpoint of the move?
00974                         if (it->hit_time > hit) break;
00975 
00976                         uint16 proc_gothit = 0, proc_rel = 0;
00977 
00978                         // If hitting at start, we should have already 
00979                         // called gotHit and hit.
00980                         if ((!it->touching || it->touching_floor) && it->hit_time >= 0)
00981                         {
00982                                 if (objid == 1 && guiapp->isShowTouchingItems())
00983                                         item->setExtFlag(Item::EXT_HIGHLIGHT);
00984 
00985                                 proc_gothit = item->callUsecodeEvent_gotHit(objid, hitforce);
00986                                 callUsecodeEvent_hit(item->getObjId(), hitforce);
00987                         }
00988 
00989                         // If not hitting at end, we will need to call release
00990                         if (it->end_time < hit)
00991                         {
00992                                 if (objid == 1) item->clearExtFlag(Item::EXT_HIGHLIGHT);
00993                                 we_were_released = true;
00994                                 proc_rel = item->callUsecodeEvent_release();
00995                         }
00996 
00997                         // We want to make sure that release is called AFTER gotHit.
00998                         if (proc_rel && proc_gothit)
00999                         {
01000                                 Process *p = Kernel::get_instance()->getProcess(proc_rel);
01001                                 p->waitFor(proc_gothit);
01002                         }
01003                 }
01004 
01005                 // Call release on us
01006                 if (we_were_released) callUsecodeEvent_release();
01007 
01008                 // Move US!
01009                 move(end[0], end[1], end[2]);
01010 
01011                 return hit;
01012         }
01013 
01014         return 0;
01015 }
01016 
01017 unsigned int Item::countNearby(uint32 shape, uint16 range)
01018 {
01019         CurrentMap* currentmap = World::get_instance()->getCurrentMap();
01020         UCList itemlist(2);
01021         LOOPSCRIPT(script, LS_SHAPE_EQUAL(shape));
01022         currentmap->areaSearch(&itemlist, script, sizeof(script),
01023                                                    this, range, false);
01024         return itemlist.getSize();
01025 }
01026 
01027