AnimationTracker.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2004-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 #include "AnimationTracker.h"
00021 
00022 #include "GameData.h"
00023 #include "Actor.h"
00024 #include "World.h"
00025 #include "CurrentMap.h"
00026 #include "MainShapeArchive.h"
00027 #include "AnimAction.h"
00028 #include "Direction.h"
00029 #include "ShapeInfo.h"
00030 #include "UCList.h"
00031 #include "LoopScript.h"
00032 #include "getObject.h"
00033 
00034 #include "IDataSource.h"
00035 #include "ODataSource.h"
00036 #include "CoreApp.h"
00037 
00038 //#define WATCHACTOR 1
00039 
00040 #ifdef WATCHACTOR
00041 static const int watchactor = WATCHACTOR;
00042 #endif
00043 
00044 AnimationTracker::AnimationTracker()
00045 {
00046 
00047 }
00048 
00049 AnimationTracker::~AnimationTracker()
00050 {
00051 
00052 }
00053 
00054 bool AnimationTracker::init(Actor* actor_, Animation::Sequence action_,
00055                                                         uint32 dir_, PathfindingState* state_)
00056 {
00057         assert(actor_);
00058         actor = actor_->getObjId();
00059         uint32 shape = actor_->getShape();
00060         animaction = GameData::get_instance()->getMainShapes()->
00061                 getAnim(shape, action_);
00062         if (!animaction) return false;
00063 
00064         dir = dir_;
00065 
00066         if (state_ == 0) {
00067                 animaction->getAnimRange(actor_, dir, startframe, endframe);
00068                 actor_->getLocation(x, y, z);
00069                 flipped = (actor_->getFlags() & Item::FLG_FLIPPED) != 0;
00070                 firststep = (actor_->getActorFlags() & Actor::ACT_FIRSTSTEP) != 0;
00071         } else {
00072                 animaction->getAnimRange(state_->lastanim, state_->direction,
00073                                                                  state_->firststep, dir, startframe, endframe);
00074                 flipped = state_->flipped;
00075                 firststep = state_->firststep;
00076                 x = state_->x;
00077                 y = state_->y;
00078                 z = state_->z;
00079         }
00080         startx = x;
00081         starty = y;
00082         startz = z;
00083 
00084 #ifdef WATCHACTOR
00085         if (actor_ && actor_->getObjId() == watchactor) {
00086                 pout << "AnimationTracker: playing " << startframe << "-" << endframe
00087                          << " (animaction flags: " << std::hex << animaction->flags
00088                          << std::dec << ")" << std::endl;
00089                 
00090         }
00091 #endif
00092 
00093         firstframe = true;
00094 
00095         done = false;
00096         blocked = false;
00097         unsupported = false;
00098         hitobject = 0;
00099         mode = NormalMode;
00100 
00101         return true;
00102 }
00103 
00104 unsigned int AnimationTracker::getNextFrame(unsigned int frame)
00105 {
00106         frame++;
00107 
00108         if (frame == endframe)
00109                 return endframe;
00110 
00111         // loop if necessary
00112         if (frame >= animaction->size) {
00113                 if (animaction->flags & (AnimAction::AAF_LOOPING |
00114                                                                  AnimAction::AAF_LOOPING2)) {
00115                         // CHECKME: unknown flag
00116                         frame = 1;
00117                 } else {
00118                         frame = 0;
00119                 }
00120         }
00121 
00122         return frame;
00123 }
00124 
00125 bool AnimationTracker::stepFrom(sint32 x_, sint32 y_, sint32 z_)
00126 {
00127         x = x_;
00128         y = y_;
00129         z = z_;
00130 
00131         return step();
00132 }
00133 
00134 void AnimationTracker::evaluateMaxAnimTravel(sint32& max_endx, sint32& max_endy, uint32 dir)
00135 {
00136         max_endx = x;
00137         max_endy = y;
00138 
00139         if (done) return;
00140         
00141         Actor* a = getActor(actor);
00142         assert(a);
00143 
00144         unsigned int testframe;
00145         if (firstframe)
00146                 testframe = startframe;
00147         else
00148                 testframe = getNextFrame(currentframe);
00149 
00150         for(;;)
00151         {
00152                 AnimFrame& f = animaction->frames[dir][testframe];
00153                 // determine movement for this frame
00154                 sint32 dx = 4 * x_fact[dir] * f.deltadir;
00155                 sint32 dy = 4 * y_fact[dir] * f.deltadir;
00156                 max_endx += dx;
00157                 max_endy += dy;
00158                 testframe = getNextFrame(testframe);
00159                 if(testframe == endframe)
00160                         return;
00161         }
00162 }
00163 
00164 bool AnimationTracker::step()
00165 {
00166         if (done) return false;
00167 
00168         Actor* a = getActor(actor);
00169         assert(a);
00170 
00171         if (firstframe)
00172                 currentframe = startframe;
00173         else
00174                 currentframe = getNextFrame(currentframe);
00175 
00176         if (currentframe == endframe) {
00177                 done = true;
00178 
00179                 // toggle ACT_FIRSTSTEP flag if necessary
00180                 if (animaction->flags & AnimAction::AAF_TWOSTEP)
00181                         firststep = !firststep;
00182                 else
00183                         firststep = true;
00184 
00185                 return false;
00186         }
00187 
00188         prevx = x;
00189         prevy = y;
00190         prevz = z;
00191 
00192         // reset status flags
00193         unsupported = false;
00194         blocked = false;
00195 
00196 
00197         firstframe = false;
00198 
00199         AnimFrame& f = animaction->frames[dir][currentframe];
00200 
00201         shapeframe = f.frame;
00202         flipped = f.is_flipped();
00203 
00204         // determine movement for this frame
00205         sint32 dx = 4 * x_fact[dir] * f.deltadir;
00206         sint32 dy = 4 * y_fact[dir] * f.deltadir;
00207         sint32 dz = f.deltaz;
00208 
00209         if (mode == TargetMode && !(f.flags & AnimFrame::AFF_ONGROUND))
00210         {
00211                 dx += target_dx;
00212                 dy += target_dy;
00213         }
00214 
00215         // determine footpad
00216         bool actorflipped = (a->getFlags() & Item::FLG_FLIPPED) != 0;
00217         sint32 xd, yd, zd;
00218         a->getFootpadWorld(xd, yd, zd);
00219         if (actorflipped != flipped) {
00220                 sint32 t = xd;
00221                 xd = yd;
00222                 yd = t;
00223         }
00224         CurrentMap* cm = World::get_instance()->getCurrentMap();
00225 
00226         // TODO: check if this step is allowed
00227         // * can move?
00228         //   if not:
00229         //     - try to step up a bit
00230         //     - try to shift left/right a bit
00231         //     CHECKME: how often can we do these minor adjustments?
00232         //     CHECKME: for which animation types can we do them?
00233         //   if still fails: blocked
00234         // * if ONGROUND
00235         //     - is supported if ONGROUND?
00236         //       if not:
00237         //         * try to step down a bit
00238         //         * try to shift left/right a bit
00239         //       if still fails: unsupported
00240         //     - if supported by non-land item: unsupported
00241 
00242         // It might be worth it creating a 'scanForValidPosition' function
00243         // (in CurrentMap maybe) that scans a small area around the given
00244         // coordinates for a valid position (with 'must be supported' as a flag).
00245         // Note that it should only check in directions orthogonal to the movement
00246         // direction (to prevent it becoming impossible to step off a ledge).
00247 
00248         // I seem to recall that the teleporter from the Upper Catacombs teleporter
00249         // to the Upper Catacombs places you inside the floor. Using this
00250         // scanForValidPosition after a teleport would work around that problem.
00251 
00252         sint32 tx,ty,tz;
00253         tx = x+dx;
00254         ty = y+dy;
00255         tz = z+dz;
00256 
00257         Item* support;
00258         bool targetok = cm->isValidPosition(tx,ty,tz,
00259                                                                                 startx,starty,startz,
00260                                                                                 xd,yd,zd,
00261                                                                                 a->getShapeInfo()->flags,
00262                                                                                 actor, &support, 0);
00263 
00264         if (GAME_IS_U8 && targetok && support)
00265         {
00266                 // Might need to check for bridge traversal adjustments
00267                 uint32 supportshape = support->getShape();
00268                 if(supportshape >= 675 && supportshape <= 681)
00269                 {
00270                         // Could be a sloping portion of a bridge.  For a bridge along the
00271                         // X axis, positive descent delta is a positive change in Y when
00272                         // moving to higher X (left to right).  Units are 60x the needed
00273                         // dy/dx
00274                         int descentdelta = 0;
00275                         if(supportshape == 675)
00276                                 descentdelta = -20;                     // Descend
00277                         else if(supportshape == 676)
00278                                 descentdelta = 12;                      // Ascend
00279                         else if(supportshape == 681)
00280                                 descentdelta = -20;                     // Descend
00281 
00282                         if(descentdelta)
00283                         {
00284                                 if(dy == 0 && dx != 0 && !(support->getFlags() & Item::FLG_FLIPPED))
00285                                 {
00286                                         // Moving left or right on horizontal bridge
00287                                         // descentdelta = 60*dy/dx
00288                                         // 60*dy = descentdelta * dx
00289                                         // dy = descentdelta * dx / 60;
00290                                         ty += descentdelta * dx / 60;
00291                                 }
00292                                 else if(dx == 0 && dy != 0 && (support->getFlags() & Item::FLG_FLIPPED))
00293                                 {
00294                                         // Moving up or down on vertical bridge
00295                                         tx += descentdelta * dy / 60;
00296                                 }
00297                         }
00298                 }
00299         }
00300 
00301         if (!targetok || ((f.flags & AnimFrame::AFF_ONGROUND) && !support)) {
00302 
00303                 // If Avatar, and on ground, try to adjust properly
00304                 if (actor == 1 && (f.flags & AnimFrame::AFF_ONGROUND)) {
00305                         targetok = cm->scanForValidPosition(tx,ty,tz, a, dir,
00306                                                                                                 true, tx,ty,tz);
00307 
00308                         if (!targetok) {
00309                                 blocked = true;
00310                                 return false;
00311                         } else {
00312 #ifdef WATCHACTOR
00313                                 if (a->getObjId() == watchactor) {
00314                                         pout << "AnimationTracker: adjusted step: "
00315                                                  << tx-(x+dx) << "," << ty-(y+dy) << "," << tz-(z+dz)
00316                                                  << std::endl;
00317                                 }
00318 #endif
00319                         }
00320                 } else {
00321                         if (!targetok) {
00322                                 blocked = true;
00323                                 return false;
00324                         }
00325                 }
00326         }
00327 
00328 #ifdef WATCHACTOR
00329         if (a->getObjId() == watchactor) {
00330                 pout << "AnimationTracker: step (" << tx-x << "," << ty-y
00331                          << "," << tz-z << ")" << std::endl;
00332         }
00333 #endif
00334                 
00335         x = tx;
00336         y = ty;
00337         z = tz;
00338         
00339 
00340         // if attack animation, see if we hit something
00341         if ((animaction->flags & AnimAction::AAF_ATTACK) &&
00342                 (hitobject == 0) && f.attack_range() > 0)
00343         {
00344                 checkWeaponHit();
00345         }
00346 
00347         if (f.flags & AnimFrame::AFF_ONGROUND) {
00348                 // needs support
00349 
00350                 /*bool targetok = */ cm->isValidPosition(tx,ty,tz,
00351                                                                                                  startx,starty,startz,
00352                                                                                                  xd,yd,zd,
00353                                                                                                  a->getShapeInfo()->flags,
00354                                                                                                  actor, &support, 0);
00355                 
00356 
00357                 if (!support) {
00358                         unsupported = true;
00359                         return false;
00360                 } else {
00361 #if 0
00362                         // This check causes really weird behaviour when fall()
00363                         // doesn't make things fall off non-land items, so disabled for now
00364 
00365                         Item* supportitem = getItem(support);
00366                         assert(supportitem);
00367                         if (!supportitem->getShapeInfo()->is_land()) {
00368 //                              pout << "Not land: "; supportitem->dumpInfo();
00369                                 // invalid support
00370                                 unsupported = true;
00371                                 return false;
00372                         }
00373 #endif
00374                 }
00375         }
00376 
00377         return true;
00378 }
00379 
00380 AnimFrame* AnimationTracker::getAnimFrame()
00381 {
00382         return &animaction->frames[dir][currentframe];
00383 }
00384 
00385 void AnimationTracker::setTargetedMode(sint32 x_, sint32 y_, sint32 z_)
00386 {
00387         unsigned int i;
00388         int totaldir = 0;
00389         int offGround = 0;
00390         sint32 end_dx, end_dy;
00391 
00392         for (i=startframe; i != endframe; i = getNextFrame(i))
00393         {
00394                 AnimFrame& f = animaction->frames[dir][i];
00395                 totaldir += f.deltadir;  // This line sometimes seg faults.. ????
00396                 if (!(f.flags & AnimFrame::AFF_ONGROUND))
00397                         ++offGround;
00398         }
00399 
00400         end_dx = 4 * x_fact[dir] * totaldir;
00401         end_dy = 4 * y_fact[dir] * totaldir;
00402 
00403         if (offGround)
00404         {
00405                 mode = TargetMode;
00406                 target_dx = (x_ - x - end_dx) / offGround;
00407                 target_dy = (y_ - y - end_dy) / offGround;
00408         }
00409 
00410 }
00411 
00412 void AnimationTracker::checkWeaponHit()
00413 {
00414         int range = animaction->frames[dir][currentframe].attack_range();
00415 
00416         Actor *a = getActor(actor);
00417         assert(a);
00418 
00419 
00420         Pentagram::Box abox = a->getWorldBox();
00421         abox.MoveAbs(x,y,z);
00422         abox.MoveRel(x_fact[dir]*32*range,y_fact[dir]*32*range,0);
00423 
00424 #ifdef WATCHACTOR
00425         if (a->getObjId() == watchactor) {
00426                 pout << "AnimationTracker: Checking hit, range " << range << ", box "
00427                          << abox.x << "," << abox.y << "," << abox.z << "," << abox.xd
00428                          << "," << abox.yd << "," << abox.zd << ": ";
00429         }
00430 #endif
00431 
00432         CurrentMap* cm = World::get_instance()->getCurrentMap();
00433 
00434         UCList itemlist(2);
00435         LOOPSCRIPT(script, LS_TOKEN_END);
00436 
00437         cm->areaSearch(&itemlist, script, sizeof(script), 0, 320, false, x, y);
00438 
00439         ObjId hit = 0;
00440         for (unsigned int i = 0; i < itemlist.getSize(); ++i) {
00441                 ObjId itemid = itemlist.getuint16(i);
00442                 if (itemid == actor) continue; // don't want to hit self
00443 
00444                 Actor* item = getActor(itemid);
00445                 if (!item) continue;
00446 
00447                 Pentagram::Box ibox = item->getWorldBox();
00448 
00449                 if (abox.Overlaps(ibox))
00450                 {
00451                         hit = itemid;
00452 #ifdef WATCHACTOR
00453                         if (a->getObjId() == watchactor) {
00454                                 pout << "hit: ";
00455                                 item->dumpInfo();
00456                         }
00457 #endif
00458                         break;
00459                 }
00460         }
00461 
00462 #ifdef WATCHACTOR
00463         if (a->getObjId() == watchactor && !hit) {
00464                 pout << "nothing" << std::endl;
00465         }
00466 #endif
00467 
00468         hitobject = hit;
00469 }
00470 
00471 void AnimationTracker::updateState(PathfindingState& state)
00472 {
00473         state.x = x;
00474         state.y = y;
00475         state.z = z;
00476         state.flipped = flipped;
00477         state.firststep = firststep;
00478 }
00479 
00480 
00481 void AnimationTracker::updateActorFlags()
00482 {
00483         Actor* a = getActor(actor);
00484         assert(a);
00485 
00486         if (flipped)
00487                 a->setFlag(Item::FLG_FLIPPED);
00488         else
00489                 a->clearFlag(Item::FLG_FLIPPED);
00490 
00491         if (firststep)
00492                 a->setActorFlag(Actor::ACT_FIRSTSTEP);
00493         else
00494                 a->clearActorFlag(Actor::ACT_FIRSTSTEP);
00495 
00496         if (animaction) {
00497                 bool hanging = (animaction->flags & AnimAction::AAF_HANGING) != 0;
00498                 if (hanging)
00499                         a->setFlag(Item::FLG_HANGING);
00500                 else
00501                         a->clearFlag(Item::FLG_HANGING);
00502         }
00503 
00504         if (currentframe != endframe)
00505                 a->animframe = currentframe;
00506 }
00507 
00508 void AnimationTracker::getInterpolatedPosition(sint32& x_, sint32& y_,
00509                                                                                            sint32& z_, int fc)
00510 {
00511         sint32 dx = x - prevx;
00512         sint32 dy = y - prevy;
00513         sint32 dz = z - prevz;
00514 
00515         x_ = prevx + (dx*fc)/(animaction->framerepeat+1);
00516         y_ = prevy + (dy*fc)/(animaction->framerepeat+1);
00517         z_ = prevz + (dz*fc)/(animaction->framerepeat+1);
00518 }
00519 
00520 void AnimationTracker::getSpeed(sint32& dx, sint32& dy, sint32& dz)
00521 {
00522         dx = x - prevx;
00523         dy = y - prevy;
00524         dz = z - prevz;
00525 }
00526 
00527 
00528 void AnimationTracker::save(ODataSource* ods)
00529 {
00530         ods->write4(startframe);
00531         ods->write4(endframe);
00532         uint8 ff = firstframe ? 1 : 0;
00533         ods->write1(ff);
00534         ods->write4(currentframe);
00535 
00536         ods->write2(actor);
00537         ods->write1(static_cast<uint8>(dir));
00538 
00539         if (animaction) {
00540                 ods->write4(animaction->shapenum);
00541                 ods->write4(animaction->action);
00542         } else {
00543                 ods->write4(0);
00544                 ods->write4(0);
00545         }
00546 
00547         ods->write4(static_cast<uint32>(prevx));
00548         ods->write4(static_cast<uint32>(prevy));
00549         ods->write4(static_cast<uint32>(prevz));
00550         ods->write4(static_cast<uint32>(x));
00551         ods->write4(static_cast<uint32>(y));
00552         ods->write4(static_cast<uint32>(z));
00553 
00554         ods->write2(static_cast<uint16>(mode));
00555         if (mode == TargetMode) {
00556                 ods->write4(static_cast<uint32>(target_dx));
00557                 ods->write4(static_cast<uint32>(target_dy));
00558         }
00559         uint8 fs = firststep ? 1 : 0;
00560         ods->write1(fs);
00561         uint8 fl = flipped ? 1 : 0;
00562         ods->write1(fl);
00563         ods->write4(shapeframe);
00564 
00565         uint8 flag = done ? 1 : 0;
00566         ods->write1(flag);
00567         flag = blocked ? 1 : 0;
00568         ods->write1(flag);
00569         flag = unsupported ? 1 : 0;
00570         ods->write1(flag);
00571         ods->write2(hitobject);
00572 }
00573 
00574 bool AnimationTracker::load(IDataSource* ids, uint32 version)
00575 {
00576         startframe = ids->read4();
00577         endframe = ids->read4();
00578         firstframe = (ids->read1() != 0);
00579         currentframe = ids->read4();
00580 
00581         actor = ids->read2();
00582         dir = ids->read1();
00583 
00584         uint32 shapenum = ids->read4();
00585         uint32 action = ids->read4();
00586         if (shapenum == 0) {
00587                 animaction = 0;
00588         } else {
00589                 animaction = GameData::get_instance()->getMainShapes()->
00590                         getAnim(shapenum, action);
00591                 assert(animaction);
00592         }
00593 
00594         prevx = ids->read4();
00595         prevy = ids->read4();
00596         prevz = ids->read4();
00597         x = ids->read4();
00598         y = ids->read4();
00599         z = ids->read4();
00600 
00601         mode = static_cast<Mode>(ids->read2());
00602         if (mode == TargetMode) {
00603                 target_dx = ids->read4();
00604                 target_dy = ids->read4();
00605         }
00606 
00607         firststep = (ids->read1() != 0);
00608         flipped = (ids->read1() != 0);
00609         shapeframe = ids->read4();
00610 
00611         done = (ids->read1() != 0);
00612         blocked = (ids->read1() != 0);
00613         unsupported = (ids->read1() != 0);
00614         hitobject = ids->read2();
00615 
00616         return true;
00617 }

Generated on Fri Jul 27 22:27:09 2007 for pentagram by  doxygen 1.4.7