GravityProcess.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 "GravityProcess.h"
00022 
00023 #include "Actor.h"
00024 #include "AudioProcess.h"
00025 #include "CurrentMap.h"
00026 #include "Kernel.h"
00027 #include "World.h"
00028 #include "getObject.h"
00029 
00030 #include "IDataSource.h"
00031 #include "ODataSource.h"
00032 
00033 #include <cmath>
00034 
00035 
00036 // p_dynamic_cast stuff
00037 DEFINE_RUNTIME_CLASSTYPE_CODE(GravityProcess,Process);
00038 
00039 GravityProcess::GravityProcess()
00040         : Process()
00041 {
00042 
00043 }
00044 
00045 GravityProcess::GravityProcess(Item* item, int gravity_)
00046         : xspeed(0), yspeed(0), zspeed(0)
00047 {
00048         assert(item);
00049 
00050         gravity = gravity_;
00051         item_num = item->getObjId();
00052 
00053         type = 0x203; // CONSTANT!
00054 }
00055 
00056 void GravityProcess::init()
00057 {
00058         Item* item = getItem(item_num);
00059         assert(item);
00060 
00061         item->setGravityPID(getPid());
00062 
00063         Actor* actor = p_dynamic_cast<Actor*>(item);
00064         if (actor) {
00065                 actor->setFallStart(actor->getZ());
00066         }
00067 }
00068 
00069 void GravityProcess::move(int xs, int ys, int zs)
00070 {
00071         xspeed += xs;
00072         yspeed += ys;
00073         zspeed += zs;
00074 }
00075 
00076 void GravityProcess::setGravity(int gravity_)
00077 {
00078         // only apply gravity if stronger than current gravity
00080         if (gravity_ > gravity)
00081                 gravity = gravity_;
00082 }
00083 
00084 bool GravityProcess::run(uint32 framenum)
00085 {
00086         // move item in (xs,ys,zs) direction
00087         Item* item = getItem(item_num);
00088         if (!item) {
00089                 terminate();
00090                 return false;
00091         }
00092 
00093         Actor* actor = p_dynamic_cast<Actor*>(item);
00094         if (actor && actor->getFallStart() < actor->getZ()) {
00095                 actor->setFallStart(actor->getZ());
00096         }
00097 
00098         // What to do:
00099         //   - check if item can move to the given position
00100         //     (in intervals in the z direction, since the z movement
00101         //      can be quite large)
00102         //   - if item can move, move it
00103     //   - if item can't move, it hit something:
00104     //     - bounce off the item (need to consider FLG_LOW_FRICTION?)
00105     //     - call the relevant events: hit/gothit ?
00106 
00107         sint32 ix,iy,iz;
00108         item->getLocation(ix, iy, iz);
00109         sint32 ixd,iyd,izd;
00110         item->getFootpadWorld(ixd, iyd, izd);
00111 
00112         sint32 tx,ty,tz;
00113         tx = ix + xspeed;
00114         ty = iy + yspeed;
00115         tz = iz + zspeed;
00116 
00117         bool clipped = false;
00118 
00119         // Clip to region. This doesn't work
00120 #if 0
00121         if (tx < 0 && ix >= 0) 
00122         {
00123                 sint32 scale = (ix - tx)>>0x8;
00124                 tx = 0;
00125                 ty = iy + ((yspeed*scale)>>0x2000);
00126                 tz = iz + ((zspeed*scale)>>0x2000);
00127                 clipped = true;
00128         }
00129         if (ty < 0 && iy >= 0) 
00130         {
00131                 sint32 scale = (iy - ty)>>0x8;
00132                 tx = ix + ((xspeed*scale)>>0x2000);
00133                 ty = 0;
00134                 tz = iz + ((zspeed*scale)>>0x2000);
00135                 clipped = true;
00136         }
00137         if (tz < 0 && iz >= 0) 
00138         {
00139                 sint32 scale = (iz - tz)>>0x8;
00140                 tx = ix + ((xspeed*scale)>>0x2000);
00141                 ty = iy + ((yspeed*scale)>>0x2000);
00142                 tz = 0;
00143                 clipped = true;
00144         }
00145 #endif
00146 
00147 //#define BOUNCE_DIAG
00148 
00149         ObjId hititemid;
00150         sint32 dist = item->collideMove(tx,ty,tz, false, false, &hititemid);
00151         
00152         if (dist == 0 || clipped)
00153         {
00154                 // If it landed on top of hititem and hititem is not land, the item
00155                 // should bounce.
00156                 bool terminate = true;
00157                 Item* hititem = getItem(hititemid);
00158                 if(zspeed < -2 && !p_dynamic_cast<Actor*>(item))
00159                 {
00160 #ifdef BOUNCE_DIAG
00161                         pout << "item " << item_num << " bounce [" << framenum
00162                                  << "]: hit " << hititem->getObjId() << std::endl;
00163 #endif
00164 
00165                         sint32 hitx,hity,hitz;
00166                         hititem->getLocation(hitx,hity,hitz);
00167                         sint32 hitdx,hitdy,hitdz;
00168                         hititem->getFootpadWorld(hitdx,hitdy,hitdz);
00169                         sint32 endx,endy,endz;
00170                         item->getLocation(endx,endy,endz);
00171 
00172                         // TODO: bounce off vertical surface impacts too?  This only
00173                         // deals with hitting an item from the top.
00174 
00175                         bool allow_land_bounce = ((0-zspeed) > 2*gravity);
00176                         if(endz == hitz + hitdz &&      // hit an item at the end position
00177                            (allow_land_bounce || !hititem->getShapeInfo()->is_land()))
00178                         {
00179                                 // Bounce!
00180                                 terminate = false;
00181 #ifdef BOUNCE_DIAG
00182                                 int xspeedold = xspeed;
00183                                 int yspeedold = yspeed;
00184                                 int zspeedold = zspeed;
00185 #endif
00186                                 zspeed = 0-zspeed/3;
00187                                 int approx_v = abs(xspeed) + abs(yspeed) + zspeed;
00188 
00189                                 // Apply an impulse on the x/y plane in a random direction
00190                                 // in a 180 degree pie around the orginal vector in x/y
00191                                 double heading_r = atan2((double)yspeed, (double)xspeed);
00192                                 double deltah_r = static_cast<double>(rand())
00193                                                                   * M_PI / RAND_MAX - M_PI/2;
00194 #ifdef BOUNCE_DIAG
00195                                 double headingold_r = heading_r;
00196 #endif
00197                                 heading_r += deltah_r;
00198                                 if(heading_r > M_PI) heading_r -= 2*M_PI;
00199                                 if(heading_r < -M_PI) heading_r += 2*M_PI;
00200                                 yspeed += static_cast<int>(sin(heading_r) *
00201                                                                                    static_cast<double>(approx_v));
00202                                 xspeed += static_cast<int>(cos(heading_r) *
00203                                                                                    static_cast<double>(approx_v));
00204 
00205                                 if(hititem->getShapeInfo()->is_land()) {
00206                                         // Bouncing off land; this bounce approximates what's
00207                                         // seen in the original U8 when the key thrown by
00208                                         // Kilandra's daughters ghost lands on the grass.
00209                                         xspeed /= 4;
00210                                         yspeed /= 4;
00211                                         zspeed /= 2;
00212                                         if(zspeed == 0) terminate = true;
00213                                 } else {
00214                                         // Not on land; this bounce approximates what's seen
00215                                         // in the original U8 when Kilandra's daughters ghost
00216                                         // throws a key at the Avatar's head
00217                                         if(abs(yspeed) > 2) yspeed /= 2;
00218                                         if(abs(xspeed) > 2) xspeed /= 2;
00219                                 }
00220 #ifdef BOUNCE_DIAG
00221                                 pout << "item " << item_num << " bounce [" << framenum
00222                                          << "]: speed was (" << xspeedold << ","
00223                                          << yspeedold << "," << zspeedold << ") new zspeed "
00224                                          << zspeed << " heading " << headingold_r
00225                                          << " impulse " << heading_r << " ("
00226                                          << (xspeed-xspeedold) << "," << (yspeed-yspeedold)
00227                                          << "), terminate: " << terminate << std::endl;
00228 #endif
00229                         } else {
00230 #ifdef BOUNCE_DIAG
00231                                 pout << "item " << item_num << " bounce [" << framenum
00232                                          << "]: no bounce" << std::endl;
00233 #endif
00234                         }
00235                 } else {
00236 #ifdef BOUNCE_DIAG
00237                         pout << "item " << item_num << " bounce [" << framenum
00238                                  << "]: slow hit" << std::endl;
00239 #endif                  
00240                 }
00241                 if(terminate) {
00242                         item->clearFlag(Item::FLG_BOUNCING);
00243                         terminateDeferred();
00244                 }
00245                 else {
00246                         item->setFlag(Item::FLG_BOUNCING);
00247                 }
00248         }
00249         else 
00250                 zspeed -= gravity;
00251 
00252 #if 0
00253 
00254         CurrentMap* cm = World::get_instance()->getCurrentMap();
00255 
00256         // collision detection. Move in steps half the item's height
00257         // (and corresponding amounts in x/y directions)
00258         if (izd == 0) izd = 8; 
00259 
00260 
00261         uint32 shapeflags = item->getShapeInfo()->flags;
00262 
00263         bool valid = true;
00264         int curz = iz;
00265         int zstepsize = izd / 2;
00266         if (tz < iz) zstepsize = -zstepsize;
00267         if (tz == iz) {
00268                 valid = cm->isValidPosition(tx,ty,tz,ixd,iyd,izd,shapeflags,
00269                                                                         item_num,0,0);
00270         } else {
00271                 do {
00272                         curz += zstepsize;
00273                         int curx = ix + ((tx - ix) * (curz-iz))/(tz-iz);
00274                         int cury = iy + ((ty - iy) * (curz-iz))/(tz-iz);
00275                 
00276                         if ((zstepsize > 0 && curz > tz) || (zstepsize < 0 && curz < tz))
00277                                 curz = tz;
00278 
00279                         valid &= cm->isValidPosition(curx, cury, curz, ixd, iyd, izd,
00280                                                                                  shapeflags,item_num, 0, 0);
00281                 } while (valid && curz != tz);
00282         }
00283 
00284         if (valid) {
00285                 item->move(tx, ty, tz);
00286 
00287                 // apply gravity acceleration
00288                 zspeed -= gravity;
00289         } else {
00291                 if (tz != iz) {
00292                         curz -= zstepsize;
00293                         if ((zstepsize > 0 && curz < iz) || (zstepsize < 0 && curz > iz))
00294                                 curz = iz;
00295 
00296                         int curx = ix + ((tx - ix) * (curz-iz))/(tz-iz);
00297                         int cury = iy + ((ty - iy) * (curz-iz))/(tz-iz);
00298 
00299                         if (cm->isValidPosition(curx, cury, curz, ixd, iyd, izd,
00300                                                                         shapeflags, item_num, 0, 0))
00301                         {
00302                                 item->move(curx, cury, curz);
00303                         }
00304                 }
00305 
00307 
00308                 terminate();
00309         }
00310 #endif
00311         return true;
00312 }
00313 
00314 
00315 void GravityProcess::terminate()
00316 {
00317         //signal item GravityProcess is gone
00318         Item* item = getItem(item_num);
00319         if (item) {
00320                 item->setGravityPID(0);
00321                 
00322                 // no longer bouncing
00323                 item->clearFlag(Item::FLG_BOUNCING);
00324         }
00325 
00326         Process::terminate();
00327 
00328         // Note: we need to terminate before calling receiveHit on actor.
00329         // If we don't, receiveHit will try to terminate this GravityProcess,
00330         // which will cause infinite recursion.
00331 
00332         Actor* actor = p_dynamic_cast<Actor*>(item);
00333         if (actor && !actor->isDead()) {
00334                 // actors take a hit if they fall
00335                 // CHECKME: might need to do a 'die' animation even if actor is dead
00336 
00337                 int height = actor->getFallStart() - actor->getZ();
00338 
00339                 if (height >= 80) {
00340                         int damage = 0;
00341 
00342                         if (height < 104) {
00343                                 // medium fall: take some damage
00344                                 damage = (height - 72)/4;
00345                         } else {
00346                                 // high fall: die
00347                                 damage = actor->getHP();
00348                         }
00349 
00350                         actor->receiveHit(0, actor->getDir(), damage,
00351                                                           WeaponInfo::DMG_FALLING|WeaponInfo::DMG_PIERCE);
00352 
00353                         // 'ooof'
00354                         AudioProcess* audioproc = AudioProcess::get_instance();
00355                         if (audioproc) audioproc->playSFX(51, 250, item_num, 0); // CONSTANT!
00356                 }
00357 
00358                 if (!actor->isDead() && actor->getLastAnim() != Animation::die) {
00359 
00360                         // play land animation, overriding other animations
00361                         Kernel::get_instance()->killProcesses(item_num, 0xF0, false); // CONSTANT!
00362                         ProcId lpid = actor->doAnim(Animation::land, 8);
00363 
00364                         if (actor->isInCombat()) {
00365                                 // need to get back to a combat stance to prevent weapon from
00366                                 // being drawn again
00367                                 ProcId spid = actor->doAnim(Animation::combatStand, 8);
00368                                 Process* sp = Kernel::get_instance()->getProcess(spid);
00369                                 sp->waitFor(lpid);
00370                         }
00371                 }
00372         }
00373 }
00374 
00375 void GravityProcess::dumpInfo()
00376 {
00377         Process::dumpInfo();
00378 
00379         pout << "gravity: " << gravity << ", speed: (" << xspeed << ","
00380                  << yspeed << "," << zspeed << ")" << std::endl;
00381 }
00382 
00383 
00384 void GravityProcess::saveData(ODataSource* ods)
00385 {
00386         Process::saveData(ods);
00387 
00388         ods->write4(static_cast<uint32>(gravity));
00389         ods->write4(static_cast<uint32>(xspeed));
00390         ods->write4(static_cast<uint32>(yspeed));
00391         ods->write4(static_cast<uint32>(zspeed));
00392 }
00393 
00394 bool GravityProcess::loadData(IDataSource* ids, uint32 version)
00395 {
00396         if (!Process::loadData(ids, version)) return false;
00397 
00398         gravity = static_cast<int>(ids->read4());
00399         xspeed = static_cast<int>(ids->read4());
00400         yspeed = static_cast<int>(ids->read4());
00401         zspeed = static_cast<int>(ids->read4());
00402 
00403         return true;
00404 }

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