effects.cc

Go to the documentation of this file.
00001 /*
00002  *  effects.h - Special effects.
00003  *
00004  *  Copyright (C) 2000-2001  The Exult Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #  include <config.h>
00023 #endif
00024 
00025 #include "gamewin.h"
00026 #include "gamemap.h"
00027 #include "gameclk.h"
00028 #include "actors.h"
00029 #include "effects.h"
00030 #include "Zombie.h"
00031 #include "dir.h"
00032 #include "chunks.h"
00033 #include "Audio.h"
00034 #include "Gump_manager.h"
00035 #include "game.h"
00036 #include "Gump.h"
00037 #include "egg.h"
00038 
00039 #include "SDL_timer.h"
00040 
00041 #ifndef UNDER_CE
00042 using std::cout;
00043 using std::endl;
00044 using std::rand;
00045 using std::string;
00046 using std::strlen;
00047 #endif
00048 
00049 int Cloud::randcnt = 0;
00050 int Lightning_effect::active = 0;
00051 
00052 /*
00053  *  Clean up.
00054  */
00055 
00056 Effects_manager::~Effects_manager()
00057   { 
00058   remove_all_effects(false);
00059   }
00060 
00061 /*
00062  *  Add text over a given item.
00063  */
00064 
00065 void Effects_manager::add_text
00066   (
00067   const char *msg,
00068   Game_object *item   // Item text ID's, or null.
00069   )
00070   {
00071   if (!msg)     // Happens with edited games.
00072     return;
00073           // Don't duplicate for item.
00074   for (Text_effect *each = texts; each; each = each->next)
00075     if (each->is_text(item))
00076       return;   // Already have text on this.
00077   Text_effect *txt = new Text_effect(msg, item);
00078 //  txt->paint(this);   // Draw it.
00079 //  painted = 1;
00080   add_text_effect(txt);
00081   }
00082 
00083 /*
00084  *  Add a text object at a given spot.
00085  */
00086 
00087 void Effects_manager::add_text
00088   (
00089   const char *msg,
00090   int x, int y      // Pixel coord. on screen.
00091   )
00092   {
00093   Text_effect *txt = new Text_effect(msg,
00094     gwin->get_scrolltx() + x/c_tilesize, 
00095     gwin->get_scrollty() + y/c_tilesize);
00096   add_text_effect(txt);
00097   }
00098 
00099 /*
00100  *  Add a text object in the center of the screen
00101  */
00102 
00103 void Effects_manager::center_text
00104   (
00105   const char *msg
00106   )
00107   {
00108   remove_text_effects();
00109   Shape_manager *sman = Shape_manager::get_instance();
00110   add_text(msg, (gwin->get_width()-sman->get_text_width(0,msg))/2,
00111        gwin->get_height()/2);
00112   }
00113 
00114 /*
00115  *  Add an effect at the start of the chain.
00116  */
00117 
00118 void Effects_manager::add_effect
00119   (
00120   Special_effect *effect
00121   )
00122   {
00123   effect->next = effects;   // Insert into chain.
00124   effect->prev = 0;
00125   if (effect->next)
00126     effect->next->prev = effect;
00127   effects = effect;
00128   }
00129 
00130 /*
00131  *  Add a text effect at the start of the chain.
00132  */
00133 
00134 void Effects_manager::add_text_effect
00135   (
00136   Text_effect *effect
00137   )
00138   {
00139   effect->next = texts;   // Insert into chain.
00140   effect->prev = 0;
00141   if (effect->next)
00142     effect->next->prev = effect;
00143   texts = effect;
00144   }
00145 
00146 /*
00147  *  Remove a given object's text effect.
00148  */
00149 
00150 void Effects_manager::remove_text_effect
00151   (
00152   Game_object *item   // Item text was added for.
00153   )
00154   {
00155   for (Text_effect *each = texts; each; each = each->next)
00156     if (each->is_text(item))
00157       {   // Found it.
00158       remove_text_effect(each);
00159       gwin->paint();
00160       return;
00161       }
00162   }
00163 
00164 /*
00165  *  Remove a sprite from the chain and delete it.
00166  */
00167 
00168 void Effects_manager::remove_effect
00169   (
00170   Special_effect *effect
00171   )
00172   {
00173   if (effect->in_queue())
00174     gwin->get_tqueue()->remove(effect);
00175   if (effect->next)
00176     effect->next->prev = effect->prev;
00177   if (effect->prev)
00178     effect->prev->next = effect->next;
00179   else        // Head of chain.
00180     effects = effect->next;
00181   delete effect;
00182   }
00183 
00184 /*
00185  *  Remove text from the chain and delete it.
00186  */
00187 
00188 void Effects_manager::remove_text_effect
00189   (
00190   Text_effect *txt
00191   )
00192   {
00193   if (txt->in_queue())
00194     gwin->get_tqueue()->remove(txt);
00195   if (txt->next)
00196     txt->next->prev = txt->prev;
00197   if (txt->prev)
00198     txt->prev->next = txt->next;
00199   else        // Head of chain.
00200     texts = txt->next;
00201   delete txt;
00202   }
00203 
00204 /*
00205  *  Remove all text items.
00206  */
00207 
00208 void Effects_manager::remove_all_effects
00209   (
00210    bool repaint
00211   )
00212   {
00213   if (!effects && !texts)
00214     return;
00215   while (effects)
00216     remove_effect(effects);
00217   while (texts)
00218     remove_text_effect(texts);
00219   if (repaint)
00220     gwin->paint();    // Just paint whole screen.
00221   }
00222 
00223 /*
00224  *  Remove text effects.
00225  */
00226 
00227 void Effects_manager::remove_text_effects
00228   (
00229   )
00230   {
00231   while (texts)
00232     remove_text_effect(texts);
00233   gwin->set_all_dirty();
00234   }
00235 
00236 
00237 /*
00238  *  Remove weather effects.
00239  */
00240 
00241 void Effects_manager::remove_weather_effects
00242   (
00243   int dist      // Only remove those from eggs at
00244           //   least this far away.
00245   )
00246   {
00247   Actor *main_actor = gwin->get_main_actor();
00248   Tile_coord apos = main_actor ? main_actor->get_tile()
00249         : Tile_coord(-1, -1, -1);
00250   Special_effect *each = effects;
00251   while (each)
00252     {
00253     Special_effect *next = each->next;
00254           // See if we're far enough away.
00255     if (each->is_weather() && (!dist ||
00256         ((Weather_effect *) each)->out_of_range(apos, dist)))
00257       remove_effect(each);
00258     each = next;
00259     }
00260   gwin->set_all_dirty();
00261   }
00262 
00263 /*
00264  *  Find last numbered weather effect added.
00265  */
00266 
00267 int Effects_manager::get_weather
00268   (
00269   )
00270   {
00271   Special_effect *each = effects;
00272   while (each)
00273     {
00274     Special_effect *next = each->next;
00275     if (each->is_weather())
00276       {
00277       Weather_effect *weather = (Weather_effect *) each;
00278       if (weather->get_num() >= 0)
00279         return weather->get_num();
00280       }
00281     each = next;
00282     }
00283   return 0;
00284   }
00285 
00286 /*
00287  *  Paint them all.
00288  */
00289 
00290 void Effects_manager::paint
00291   (
00292   )
00293   {
00294   for (Special_effect *effect = effects; effect; effect = effect->next)
00295     effect->paint();
00296   }
00297 
00298 /*
00299  *  Paint all text.
00300  */
00301 
00302 void Effects_manager::paint_text
00303   (
00304   )
00305   {
00306   for (Text_effect *txt = texts; txt; txt = txt->next)
00307     txt->paint();
00308   }
00309 
00310 /*
00311  *  Some special effects may not need painting.
00312  */
00313 
00314 void Special_effect::paint
00315   (
00316   )
00317   {
00318   }
00319 
00320 /*
00321  *  Create an animation from the 'sprites.vga' file.
00322  */
00323 
00324 Sprites_effect::Sprites_effect
00325   (
00326   int num,      // Index.
00327   Tile_coord p,     // Position within world.
00328   int dx, int dy,     // Add to offset for each frame.
00329   int delay     // Delay (msecs) before starting.
00330   ) : sprite(num, 0, SF_SPRITES_VGA), item(0), pos(p), xoff(0), yoff(0),
00331             deltax(dx), deltay(dy)
00332   {
00333   Game_window *gwin = Game_window::get_instance();
00334   frames = sprite.get_num_frames();
00335           // Start.
00336   gwin->get_tqueue()->add(Game::get_ticks() + delay, this, 0L);
00337   }
00338 
00339 /*
00340  *  Create an animation on an object.
00341  */
00342 
00343 Sprites_effect::Sprites_effect
00344   (
00345   int num,      // Index.
00346   Game_object *it,    // Item to put effect by.
00347   int xf, int yf,     // Offset from actor in pixels.
00348   int dx, int dy      // Add to offset on each frame.
00349   ) : sprite(num, 0, SF_SPRITES_VGA), item(it), xoff(xf), 
00350           yoff(yf), deltax(dx), deltay(dy)
00351   {
00352   pos = item->get_tile();
00353   Game_window *gwin = Game_window::get_instance();
00354   frames = sprite.get_num_frames();
00355           // Start immediately.
00356   gwin->get_tqueue()->add(Game::get_ticks(), this, 0L);
00357   }
00358 
00359 /*
00360  *  Add a dirty rectangle for the current position and frame.
00361  */
00362 
00363 inline void Sprites_effect::add_dirty
00364   (
00365   int frnum
00366   )
00367   {
00368   if (pos.tx == -1 || frnum == -1)
00369     return;     // Already at destination.
00370   Shape_frame *shape = sprite.get_shape();
00371   int lp = pos.tz/2;
00372 
00373   gwin->add_dirty(gwin->clip_to_win(gwin->get_shape_rect(shape,
00374     xoff + (pos.tx - lp - gwin->get_scrolltx())*c_tilesize,
00375         yoff + (pos.ty - lp - 
00376       gwin->get_scrollty())*c_tilesize).enlarge(12)));
00377   }
00378 
00379 /*
00380  *  Animation.
00381  */
00382 
00383 void Sprites_effect::handle_event
00384   (
00385   unsigned long curtime,    // Current time of day.
00386   long udata
00387   )
00388   {
00389   int frame_num = sprite.get_framenum();
00390   Game_window *gwin = Game_window::get_instance();
00391   int delay = gwin->get_std_delay();// Delay between frames.  Needs to
00392           //   match usecode animations.
00393   if (frame_num == frames)  // At end?
00394     {     // Remove & delete this.
00395     eman->remove_effect(this);
00396     gwin->set_all_dirty();
00397     return;
00398     }
00399   add_dirty(frame_num);   // Clear out old.
00400   gwin->set_painted();
00401   if (item)     // Following actor?
00402     pos = item->get_tile();
00403   xoff += deltax;     // Add deltas.
00404   yoff += deltay;
00405   frame_num++;      // Next frame.
00406   add_dirty(frame_num);   // Want to paint new frame.
00407   sprite.set_frame(frame_num);
00408           // Add back to queue for next time.
00409   gwin->get_tqueue()->add(curtime + delay, this, udata);
00410   }
00411 
00412 /*
00413  *  Render.
00414  */
00415 
00416 void Sprites_effect::paint
00417   (
00418   )
00419   {
00420   if (sprite.get_framenum() >= frames)
00421     return;
00422   int lp = pos.tz/2;    // Account for lift.
00423   sprite.paint_shape(
00424     xoff + (pos.tx - lp - gwin->get_scrolltx())*c_tilesize,
00425     yoff + (pos.ty - lp - gwin->get_scrollty())*c_tilesize);
00426   }
00427 
00428 /*
00429  *  Start explosion.
00430  */
00431 
00432 Explosion_effect::Explosion_effect
00433   (
00434   Tile_coord p, 
00435   Game_object *exp,
00436   int delay,      // Delay before starting (msecs).
00437   int weap      // Weapon to use for damage calcs., or
00438           //   -1 for default(704 = poweder keg).
00439   ) : Sprites_effect(1, p, 0, 0, delay), explode(exp),
00440     weapon(weap >= 0 ? weap : 704)
00441 {
00442   if (exp && exp->get_shapenum() == 704) { // powderkeg
00443     exp->set_quality(1); // mark as detonating
00444   }
00445 }
00446 
00447 
00448 void Explosion_effect::handle_event
00449   (
00450   unsigned long curtime,    // Current time of day.
00451   long udata
00452   )
00453   {
00454   int frnum = sprite.get_framenum();
00455   if (!frnum)     // Max. volume, with stereo position.
00456     {
00457     Tile_coord apos = gwin->get_main_actor()->get_tile();
00458     int dir = Get_direction16(apos.ty - pos.ty, pos.tx - apos.tx);
00459 
00460     Audio::get_ptr()->play_sound_effect(
00461         Audio::game_sfx(9), SDL_MIX_MAXVOLUME, dir);
00462     }
00463   if (frnum == frames/4) {
00464     // this was in ~Explosion_effect before
00465     if (explode && !explode->is_pos_invalid())
00466       {
00467       Game_window::get_instance()->add_dirty(explode);
00468       explode->remove_this();
00469       explode = 0;
00470       }
00471     Game_object_vector vec; // Find objects near explosion.
00472     Game_object::find_nearby(vec, pos, c_any_shapenum, 5, 0);
00473     for (Game_object_vector::const_iterator it = vec.begin(); it != vec.end(); ++it)
00474       {
00475         (**it).attacked(0, -704, 0);
00476       }
00477   }
00478   Sprites_effect::handle_event(curtime, udata);
00479 }
00480 
00481 /*
00482  *  Get direction in 1/16's starting from North.
00483  */
00484 
00485 inline int Get_dir16
00486   (
00487   Tile_coord& t1,
00488   Tile_coord& t2
00489   )
00490   {
00491           // Treat as cartesian coords.
00492   return Get_direction16(t1.ty - t2.ty, t2.tx - t1.tx);
00493   }
00494 
00495 /*
00496  *  Initialize path & add to time queue.
00497  */
00498 
00499 void Projectile_effect::init
00500   (
00501   Tile_coord s,     // Source,
00502   Tile_coord d      // Destination.
00503   )
00504   {
00505   no_blocking = false;    // We'll check the ammo & weapon.
00506   Game_window *gwin = Game_window::get_instance();
00507   Shape_info& info = ShapeID::get_info(projectile_shape);
00508   Weapon_info *winfo = info.get_weapon_info();
00509   if (winfo)
00510     {
00511     if (winfo->get_projectile())  // Different sprite to show?
00512       sprite.set_shape(winfo->get_projectile());
00513     no_blocking = no_blocking || winfo->no_blocking();
00514     }
00515   Ammo_info *ainfo = info.get_ammo_info();
00516   if (ainfo)
00517     no_blocking = no_blocking || ainfo->no_blocking();
00518   frames = sprite.get_num_frames();
00519   pos = s;      // Get starting position.
00520   if (attacker)     // Try to set start better.
00521     {
00522     Shape_info& info = attacker->get_info();
00523           // Try for around the heat.
00524     pos.tz += info.get_3d_height() - 1;
00525     if (d.tx < pos.tx)  // Start on proper side.
00526       pos.tx -= info.get_3d_xtiles();
00527     else
00528       pos.tx++;
00529     if (d.ty < pos.ty)
00530       pos.ty -= info.get_3d_ytiles();
00531     else
00532       pos.ty++;
00533     }
00534   path = new Zombie();    // Create simple pathfinder.
00535           // Find path.  Should never fail.
00536   path->NewPath(pos, d, 0);
00537   if (frames >= 24)   // Use frames 8-23, for direction
00538     {     //   going clockwise from North.
00539     int dir = Get_dir16(s, d);
00540     sprite.set_frame(8 + dir);
00541     }
00542   else if (frames == 1 && sprite.get_shapenum() != 704)
00543     sprite.set_frame(0);  // (Don't show powder keg!)
00544   else
00545     sprite.set_frame(-1); // We just won't show it.
00546           // Start immediately.
00547   gwin->get_tqueue()->add(Game::get_ticks(), this, 0L);
00548   }
00549 
00550 
00551 /*
00552  *  Create a projectile animation.
00553  */
00554 
00555 Projectile_effect::Projectile_effect
00556   (
00557   Actor *att,     // Source of spell/attack.
00558   Game_object *to,    // End here, 'attack' it with shape.
00559   int shnum,      // Projectile shape # in 'shapes.vga'.
00560   int weap      // Weapon (bow, gun, etc.) shape, or 0.
00561   ) : attacker(att), target(to), projectile_shape(shnum),
00562       sprite(shnum, 0), weapon(weap),
00563       return_path(false)
00564   {
00565   init(attacker->get_tile(), to->get_tile());
00566   }
00567 
00568 /*
00569  *  Constructor used by missile eggs.
00570  */
00571 
00572 Projectile_effect::Projectile_effect
00573   (
00574   Tile_coord s,     // Start here.
00575   Tile_coord d,     // End here.
00576   int shnum,      // Projectile shape
00577   int weap      // Weapon (bow, gun, etc.) shape, or 0.
00578   ) : attacker(0), target(0), projectile_shape(shnum),
00579       sprite(shnum, 0), weapon(weap),
00580       return_path(false)
00581   {
00582   init(s, d);
00583   }
00584 
00585 /* 
00586  *  Another used by missile eggs.
00587  */
00588 
00589 Projectile_effect::Projectile_effect
00590   (
00591   Tile_coord s,     // Start here.
00592   Game_object *to,    // End here, 'attack' it with shape.
00593   int shnum,      // Projectile shape
00594   int weap,     // Weapon (bow, gun, etc.) shape, or 0.
00595   bool retpath      // Return of a boomerang.
00596   ) : attacker(0), target(to), projectile_shape(shnum),
00597       sprite(shnum, 0), weapon(weap),
00598       return_path(retpath)
00599   {
00600   init(s, to->get_tile());
00601   }
00602 
00603 /*
00604  *  Delete.
00605  */
00606 
00607 Projectile_effect::~Projectile_effect
00608   (
00609   )
00610   {
00611   delete path;
00612   }
00613 
00614 /*
00615  *  Add a dirty rectangle for the current position and frame.
00616  */
00617 
00618 inline void Projectile_effect::add_dirty
00619   (
00620   )
00621   {
00622   if (pos.tx == -1 || sprite.get_framenum() == -1)
00623     return;     // Already at destination.
00624   Shape_frame *shape = sprite.get_shape();
00625           // Force repaint of prev. position.
00626   int liftpix = pos.tz*c_tilesize/2;
00627   gwin->add_dirty(gwin->clip_to_win(gwin->get_shape_rect(shape,
00628       (pos.tx - gwin->get_scrolltx())*c_tilesize - liftpix,
00629       (pos.ty - gwin->get_scrollty())*c_tilesize - liftpix).enlarge(4)));
00630   }
00631 
00632 /*
00633  *  See if something was hit.
00634  *
00635  *  Output: ->target hit, or 0.
00636  */
00637 
00638 inline Game_object *Find_target
00639   (
00640   Game_window *gwin,
00641   Tile_coord pos
00642   )
00643   {
00644   if (pos.tz%5 == 0)    // On floor?
00645     pos.tz++;   // Look up 1 tile.
00646   Tile_coord dest = pos;    // This gets modified.
00647   if (!Map_chunk::is_blocked(pos, 1, MOVE_FLY, 0) &&
00648       dest == pos)
00649     return (0);
00650   return Game_object::find_blocking(pos);
00651   }
00652 
00653 /*
00654  *  Animation.
00655  */
00656 
00657 void Projectile_effect::handle_event
00658   (
00659   unsigned long curtime,    // Current time of day.
00660   long udata
00661   )
00662   {
00663   Game_window *gwin = Game_window::get_instance();
00664   int delay = gwin->get_std_delay()/2;
00665   add_dirty();      // Force repaint of old pos.
00666   Tile_coord epos = pos;    // Save pos.
00667   if (!path->GetNextStep(pos) ||  // Get next spot.
00668           // If missile egg, detect target.
00669     (!target && !no_blocking && (target = Find_target(gwin, pos)) != 0))
00670     {     // Done? 
00671     int delay = 0;    // For explosions.
00672     switch (projectile_shape)
00673       {
00674     case 287:   // Swordstrike.
00675       eman->add_effect(new Sprites_effect(23, epos));
00676       break;
00677     case 554:   // Burst arrow.
00678       eman->add_effect(new Sprites_effect(19, epos));
00679       break;
00680     case 565:   // Starburst.
00681       eman->add_effect(new Sprites_effect(18, epos));
00682       break;
00683     case 639:   // Death Vortex.
00684       eman->add_effect(new Death_vortex(target, epos));
00685       target = 0; // Takes care of attack.
00686       break;
00687     case 82:    // Delayed explosion.
00688     case 621:   //    "       "
00689       delay = 3000; // Wait 3 secs.  FALL THROUGH!
00690     case 78:    // Explosion.
00691     case 702:   // Cannon.
00692     case 704:   // Powder keg.
00693       eman->add_effect(new Explosion_effect(epos, 0, delay));
00694       target = 0; // Takes care of attack.
00695       break;
00696       }
00697     if (return_path)  // Returned a boomerang?
00698       target->add(gmap->create_ireg_object(weapon, 0));
00699     else
00700       {   // Not teleported away ?
00701       if (target && epos.distance(target->get_tile()) < 50)
00702         target->attacked(attacker, weapon, 
00703               projectile_shape);
00704       if (attacker && // Check for `boomerangs'
00705           weapon == projectile_shape && 
00706           epos.distance(attacker->get_tile() ) < 50)
00707         {   // not teleported away
00708         Weapon_info *winf = 
00709            ShapeID::get_info(weapon).get_weapon_info();
00710         if (winf && winf->returns())
00711           eman->add_effect(new Projectile_effect(
00712             pos, attacker, weapon, weapon,
00713                   true));
00714         }
00715       }
00716     add_dirty();
00717     pos.tx = -1;    // Signal we're done.
00718     eman->remove_effect(this);
00719     return;
00720     }
00721   add_dirty();      // Paint new spot/frame.
00722           // Add back to queue for next time.
00723   gwin->get_tqueue()->add(curtime + delay, this, udata);
00724   }
00725 
00726 /*
00727  *  Render.
00728  */
00729 
00730 void Projectile_effect::paint
00731   (
00732   )
00733   {
00734   if (pos.tx == -1 || sprite.get_framenum() == -1)
00735     return;     // Already at destination.
00736   int liftpix = pos.tz*c_tilesize/2;
00737   sprite.paint_shape(
00738     (pos.tx - gwin->get_scrolltx())*c_tilesize - liftpix,
00739     (pos.ty - gwin->get_scrollty())*c_tilesize - liftpix);
00740   }
00741 
00742 /*
00743  *  Create a 'death vortex'.
00744  */
00745 
00746 Death_vortex::Death_vortex
00747   (
00748   Game_object *trg,   // What to aim for.
00749   Tile_coord tp     // Target pos, if trg==0.
00750   ) : vortex(8, 0, SF_SPRITES_VGA), next_damage_time(0)
00751   {
00752   pos = trg ? trg->get_tile() : tp;
00753   target = trg ? trg->as_actor() : 0;
00754   Game_window *gwin = Game_window::get_instance();
00755   frames = vortex.get_num_frames();
00756           // Go for 20 seconds.
00757   stop_time = Game::get_ticks() + 20*1000;
00758           // Start immediately.
00759   gwin->get_tqueue()->add(Game::get_ticks(), this, 0L);
00760   }
00761 
00762 /*
00763  *  Add a dirty rectangle for the current position and frame.
00764  *
00765  *  Output: Width in pixels.
00766  */
00767 
00768 inline int Death_vortex::add_dirty
00769   (
00770   )
00771   {
00772   Shape_frame *shape = vortex.get_shape();
00773   int liftpix = pos.tz*c_tilesize/2;
00774   gwin->add_dirty(gwin->clip_to_win(gwin->get_shape_rect(shape,
00775     (pos.tx - gwin->get_scrolltx())*c_tilesize - liftpix,
00776       (pos.ty - gwin->get_scrollty())*c_tilesize - liftpix).enlarge(4)));
00777   return shape->get_width();
00778   }
00779 
00780 /*
00781  *  Animation.
00782  */
00783 
00784 void Death_vortex::handle_event
00785   (
00786   unsigned long curtime,    // Current time of day.
00787   long udata
00788   )
00789   {
00790   Game_window *gwin = Game_window::get_instance();
00791   int width = add_dirty();  // Repaint old area.
00792   if (target && !target->is_dead()) // Follow target.
00793     {
00794     Tile_coord tpos = target->get_tile();
00795     int deltax = tpos.tx - pos.tx, deltay = tpos.ty - pos.ty;
00796     int absx = deltax >= 0 ? deltax : -deltax;
00797     int absy = deltay >= 0 ? deltay : -deltay;
00798     if (absx > 2 || absy > 2)
00799       {
00800       if (deltax)
00801         pos.tx += deltax/absx;
00802       if (deltay)
00803         pos.ty += deltay/absy;
00804       }
00805     }
00806   if (curtime > next_damage_time) // Time to cause damage.
00807     {     // Do it every second.
00808     next_damage_time = curtime + 1000;
00809     Actor_vector npcs;  // Find NPC's.
00810     Game_object::find_nearby(npcs, pos, -1, width/(2*c_tilesize), 
00811                   8);
00812     for (Actor_vector::const_iterator it = npcs.begin();
00813               it != npcs.end(); ++it)
00814       {
00815       Actor *npc = *it;
00816       if (!npc->is_in_party())
00817         npc->reduce_health(40);
00818       }
00819     }
00820   vortex.set_frame((vortex.get_framenum() + 1)%frames);
00821   add_dirty();      // Paint new.
00822   if (curtime < stop_time)  // Keep going?
00823     gwin->get_tqueue()->add(curtime + 100, this, udata);
00824   else
00825     {
00826     gwin->set_all_dirty();
00827     eman->remove_effect(this);
00828     }
00829   }
00830 
00831 /*
00832  *  Render.
00833  */
00834 
00835 void Death_vortex::paint
00836   (
00837   )
00838   {
00839   int liftpix = pos.tz*c_tilesize/2;
00840   vortex.paint_shape(
00841     (pos.tx - gwin->get_scrolltx())*c_tilesize - liftpix,
00842     (pos.ty - gwin->get_scrollty())*c_tilesize - liftpix);
00843   }
00844 
00845 /*
00846  *  Figure text position.
00847  */
00848 
00849 static inline Tile_coord Figure_text_pos
00850   (
00851   Game_object *item
00852   )
00853   {
00854   Game_window *gwin = Game_window::get_instance();
00855   Gump_manager *gumpman = gwin->get_gump_man();
00856   Rectangle box;
00857           // See if it's in a gump.
00858   Gump *gump = gumpman->find_gump(item);
00859   if (gump)
00860     box = gump->get_shape_rect(item);
00861   else
00862     box = gwin->get_shape_rect(item->get_outermost());
00863   return Tile_coord(gwin->get_scrolltx() + box.x/c_tilesize, 
00864         gwin->get_scrollty() + box.y/c_tilesize, 0);
00865   }
00866 
00867 /*
00868  *  Add dirty rectangle for current position.
00869  */
00870 
00871 void Text_effect::add_dirty
00872   (
00873   )
00874   {
00875   Game_window *gwin = Game_window::get_instance();
00876           // Repaint slightly bigger rectangle.
00877   Rectangle rect((pos.tx - gwin->get_scrolltx() - 1)*c_tilesize,
00878            (pos.ty - gwin->get_scrollty() - 1)*c_tilesize,
00879       width + 2*c_tilesize, height + 2*c_tilesize);
00880   gwin->add_dirty(gwin->clip_to_win(rect));
00881   }
00882 
00883 /*
00884  *  Initialize.
00885  */
00886 
00887 void Text_effect::init
00888   (
00889   )
00890   {
00891   set_always(true);   // Always execute in time queue, even
00892           //   when paused.
00893   Game_window *gwin = Game_window::get_instance();
00894   width = 8 + sman->get_text_width(0, msg.c_str());
00895   height = 8 + sman->get_text_height(0);
00896   add_dirty();      // Force first paint.
00897           // Start immediately.
00898   gwin->get_tqueue()->add(Game::get_ticks(), this, 0L);
00899   if (msg[0] == '@')
00900     msg[0] = '"';
00901   int len = msg.size();
00902   if (msg[len - 1] == '@')
00903     msg[len - 1] = '"';
00904   }
00905 
00906 /*
00907  *  Create a text effect for a given object.
00908  */
00909 
00910 Text_effect::Text_effect
00911   (
00912   const string &m,    // A copy is made.
00913   Game_object *it     // Item text is on, or null.
00914   ) : msg(m), item(it), pos(Figure_text_pos(it)), num_ticks(0)
00915   {
00916   init();
00917   }
00918 
00919 /*
00920  *  Create a text object.
00921  */
00922 
00923 Text_effect::Text_effect
00924   (
00925   const string &m,    // A copy is made.
00926   int t_x, int t_y    // Abs. tile coords.
00927   ) : msg(m), item(0), pos(t_x, t_y, 0), num_ticks(0)
00928   {
00929   init();
00930   }
00931 
00932 /*
00933  *  Reposition if necessary.
00934  */
00935 
00936 void Text_effect::handle_event
00937   (
00938   unsigned long curtime,    // Current time of day.
00939   long udata      // Ignored.
00940   )
00941   {
00942   Game_window *gwin = Game_window::get_instance();
00943   if (++num_ticks == 10)    // About 1-2 seconds.
00944     {     // All done.
00945     add_dirty();
00946     eman->remove_text_effect(this);
00947     return;
00948     }
00949           // Back into queue.
00950   gwin->get_tqueue()->add(Game::get_ticks() + gwin->get_std_delay(), 
00951                 this, 0L);
00952   if (!item)      // Nothing to move?
00953     return;
00954           // See if moved.
00955   Tile_coord npos = Figure_text_pos(item);
00956   if (npos == pos)    // No change?
00957     return;
00958   add_dirty();      // Force repaint of old area.
00959   pos = npos;     // Then set to repaint new.
00960   add_dirty();
00961   }
00962 
00963 /*
00964  *  Render.
00965  */
00966 
00967 void Text_effect::paint
00968   (
00969   )
00970   {
00971   const char *ptr = msg.c_str();
00972   int len = strlen(ptr);
00973   sman->paint_text(0, ptr, len, 
00974     (pos.tx - gwin->get_scrolltx())*c_tilesize,
00975         (pos.ty - gwin->get_scrollty())*c_tilesize);
00976   }
00977 
00978 /*
00979  *  Init. a weather effect.
00980  */
00981 
00982 Weather_effect::Weather_effect
00983   (
00984   int duration,     // Length in game minutes.
00985   int delay,      // Delay before starting.
00986   int n,        // Weather number.
00987   Game_object *egg    // Egg that started it, or null.
00988   ) : num(n)
00989   {
00990   Game_window *gwin = Game_window::get_instance();
00991   if (egg)
00992     eggloc = egg->get_tile();
00993   else
00994     eggloc = Tile_coord(-1, -1, -1);
00995   stop_time = Game::get_ticks() + delay + 1000*((duration*60)/time_factor);
00996           // Start immediately.
00997   gwin->get_tqueue()->add(Game::get_ticks() + delay, this, 0L);
00998   }
00999 
01000 /*
01001  *  Are we far enough away from this to cancel it?
01002  */
01003 
01004 int Weather_effect::out_of_range
01005   (
01006   Tile_coord& avpos,    // Avatar's position.
01007   int dist      // Distance in tiles to cancel at.
01008   )
01009   {
01010   if (eggloc.tx == -1)    // Not created by an egg?
01011     return 0;
01012   return eggloc.distance(avpos) >= dist;
01013   }
01014 
01015 /*
01016  *  Paint raindrop.
01017  */
01018 
01019 inline void Raindrop::paint
01020   (
01021   Image_window8 *iwin,    // Where to draw.
01022   int scrolltx, int scrollty, // Tile at top-left corner.
01023   Xform_palette& xform    // Transform array.
01024   )
01025   {
01026   uint32 ascrollx = scrolltx*(uint32)c_tilesize,
01027           ascrolly = scrollty*(uint32)c_tilesize;
01028   int x = ax - ascrollx, y = ay - ascrolly;
01029   if (x < 0 || y < 0 || 
01030       x >= iwin->get_width() || y >= iwin->get_height())
01031     return;
01032   if (oldpix == 255)
01033     oldpix = iwin->get_pixel8(x, y);  // Get pixel.
01034   iwin->put_pixel8(xform[oldpix], x, y);
01035   }
01036 
01037 /*
01038  *  Move raindrop.
01039  */
01040 
01041 inline void Raindrop::next
01042   (
01043   Image_window8 *iwin,    // Where to draw. 
01044   int scrolltx, int scrollty, // Tile at top-left corner.
01045   Xform_palette& xform,   // Transform array.
01046   int w, int h      // Dims. of window.
01047   )
01048   {
01049   uint32 ascrollx = scrolltx*(uint32)c_tilesize,
01050           ascrolly = scrollty*(uint32)c_tilesize;
01051   int x = ax - ascrollx, y = ay - ascrolly;
01052           // Still on screen?  Restore pix.
01053   if (x >= 0 && y >= 0 && x < w && y < h && oldpix != 255)
01054     iwin->put_pixel8(oldpix, x, y);
01055   oldpix = 255;
01056 
01057           // Time to restart?
01058   if (x < 0 || x >= w || y < 0 || y >= h)
01059     {     
01060     int r = rand();
01061           // Have a few fall faster.
01062     yperx = (r%4) ? 1 : 2;
01063     ax = ascrollx + r%(w - w/8);
01064     ay = ascrolly + r%(h - h/4);
01065     }
01066   else        // Next spot.
01067     {
01068     int delta = 1 + rand()%4;
01069     ax += delta;
01070     ay += delta + yperx;
01071     }
01072           // Save old pixel & paint new.
01073   paint(iwin, scrolltx, scrollty, xform);
01074   }
01075 
01076 /*
01077  *  Move raindrop.
01078  */
01079 
01080 inline void Raindrop::next_random
01081   (
01082   Image_window8 *iwin,    // Where to draw. 
01083   int scrolltx, int scrollty, // Tile at top-left corner.
01084   Xform_palette& xform,   // Transform array.
01085   int w, int h      // Dims. of window.
01086   )
01087   {
01088   uint32 ascrollx = scrolltx*(uint32)c_tilesize,
01089           ascrolly = scrollty*(uint32)c_tilesize;
01090   int x = ax - ascrollx, y = ay - ascrolly;
01091           // Still on screen?  Restore pix.
01092   if (x >= 0 && y >= 0 && x < w && y < h && oldpix != 255)
01093     iwin->put_pixel8(oldpix, x, y);
01094   oldpix = 255;
01095           // Pick new spot randomly.
01096   int newx = rand()%w, newy = rand()%h;
01097   ax = ascrollx + newx;
01098   ay = ascrolly + newy;
01099           // Save old pixel & paint new.
01100   paint(iwin, scrolltx, scrollty, xform);
01101   }
01102 
01103 /*
01104  *  Rain.
01105  */
01106 
01107 void Rain_effect::handle_event
01108   (
01109   unsigned long curtime,    // Current time of day.
01110   long udata
01111   )
01112   {
01113   Game_window *gwin = Game_window::get_instance();
01114   if (!gwin->is_main_actor_inside() &&
01115       !gumpman->showing_gumps(true))
01116     {     // Don't show rain inside buildings!
01117     Image_window8 *win = gwin->get_win();
01118     int w = win->get_width(), h = win->get_height();
01119           // Get transform table.
01120     Xform_palette& xform = sman->get_xform(8);//++++Experiment.
01121     int scrolltx = gwin->get_scrolltx(),
01122         scrollty = gwin->get_scrollty();
01123           // Move drops.
01124     for (int i = 0; i < num_drops; i++)
01125       drops[i].next(win, scrolltx, scrollty, xform, w, h);
01126     gwin->set_painted();
01127     }
01128   if (curtime < stop_time)  // Keep going?
01129     gwin->get_tqueue()->add(curtime + 100, this, udata);
01130   else
01131     {
01132     gwin->set_all_dirty();
01133     eman->remove_effect(this);
01134     }
01135   }
01136 
01137 /*
01138  *  Paint rain.
01139  */
01140 
01141 void Rain_effect::paint
01142   (
01143   )
01144   {
01145   if (gwin->is_main_actor_inside())
01146     return;     // Inside.
01147           // Get transform table.
01148   Xform_palette& xform = sman->get_xform(8);//++++Experiment.
01149   int scrolltx = gwin->get_scrolltx(),
01150       scrollty = gwin->get_scrollty();
01151   Image_window8 *win = gwin->get_win();
01152   for (int i = 0; i < num_drops; i++)
01153     drops[i].paint(win, scrolltx, scrollty, xform);
01154   gwin->set_painted();
01155   }
01156 
01157 /*
01158  *  End of lightning.
01159  */
01160 
01161 Lightning_effect::~Lightning_effect
01162   (
01163   )
01164   {
01165   if (flashing)     // Be sure palette is restored.
01166     Game_window::get_instance()->get_clock()->set_palette();
01167   }
01168 
01169 /*
01170  *  Lightning.
01171  */
01172 
01173 void Lightning_effect::handle_event
01174   (
01175   unsigned long curtime,    // Current time of day.
01176   long udata
01177   )
01178   {
01179   Game_window *gwin = Game_window::get_instance();
01180   int r = rand();     // Get a random #.
01181   int delay = 100;    // Delay for next time.
01182   if (flashing)     // Just turned white?  Restore.
01183     {
01184     gclock->set_palette();
01185     flashing = false;
01186     active = false;
01187     if (curtime >= stop_time)
01188       {   // Time to stop.
01189       eman->remove_effect(this);
01190       return;
01191       }
01192     if (r%5 == 0)   // Occassionally flash again.
01193       delay = (1 + r%7)*40;
01194     else      // Otherwise, wait several secs.
01195       delay = (4000 + r%3000);
01196     }
01197   else if (!gwin->is_in_dungeon() && !active)// Time to flash.
01198     {
01199           // Play thunder.
01200     Audio::get_ptr()->play_sound_effect(Audio::game_sfx(62));
01201     active = true;
01202     flashing = true;
01203     gwin->get_pal()->set(PALETTE_LIGHTNING);
01204     delay = (1 + r%2)*25;
01205     }
01206   gwin->get_tqueue()->add(curtime + delay, this, udata);
01207   }
01208 
01209 /*
01210  *  Start a storm.
01211  */
01212 
01213 Storm_effect::Storm_effect
01214   (
01215   int duration,     // In game minutes.
01216   int delay,      // In msecs.
01217   Game_object *egg    // Egg that caused it, or null.
01218   ) : Weather_effect(duration, delay, 2, egg), start(1)
01219   {
01220   Game_window *gwin = Game_window::get_instance();
01221           // Start raining soon.
01222   int rain_delay = 20 + rand()%1000;
01223   eman->add_effect(new Rain_effect(duration - 1, rain_delay));
01224   int lightning_delay = rain_delay + rand()%500;
01225   eman->add_effect(new Lightning_effect(duration - 1, lightning_delay));
01226   }
01227 
01228 /*
01229  *  Start/end of storm.
01230  */
01231 void Storm_effect::handle_event
01232   (
01233   unsigned long curtime,    // Current time of day.
01234   long udata
01235   )
01236   {
01237   Game_window *gwin = Game_window::get_instance();
01238   if (start)
01239     {
01240     start = 0;
01241           // Darken sky.
01242     gwin->get_clock()->set_storm(true);
01243           // Nothing more to do but end.
01244     gwin->get_tqueue()->add(stop_time, this, udata);
01245     }
01246   else        // Must be time to stop.
01247     eman->remove_effect(this);
01248   }
01249 
01250 /*
01251  *  Storm ended.
01252  */
01253 
01254 Storm_effect::~Storm_effect
01255   (
01256   )
01257   {
01258   Game_window *gwin = Game_window::get_instance();
01259           // Restore palette.
01260   gwin->get_clock()->set_storm(false);
01261   }
01262 
01263 /*
01264  *  Sparkles (in Ambrosia).
01265  */
01266 
01267 void Sparkle_effect::handle_event
01268   (
01269   unsigned long curtime,    // Current time of day.
01270   long udata
01271   )
01272   {
01273   Game_window *gwin = Game_window::get_instance();
01274   if (!gwin->is_main_actor_inside())
01275     {     // Don't show rain inside buildings!
01276     Image_window8 *win = gwin->get_win();
01277     int w = win->get_width(), h = win->get_height();
01278           // Get transform table.
01279     Xform_palette& xform = sman->get_xform(8);
01280     int scrolltx = gwin->get_scrolltx(),
01281         scrollty = gwin->get_scrollty();
01282           // Move drops to random spots.
01283     for (int i = 0; i < num_drops; i++)
01284       drops[i].next_random(win, scrolltx, scrollty, 
01285                 xform, w, h);
01286     gwin->set_painted();
01287     }
01288   if (curtime < stop_time)  // Keep going?
01289     gwin->get_tqueue()->add(curtime + 100, this, udata);
01290   else
01291     {
01292     gwin->set_all_dirty();
01293     eman->remove_effect(this);
01294     }
01295   }
01296 
01297 /*
01298  *  Create a cloud.
01299  */
01300 
01301 const int CLOUD = 2;    // Shape #.
01302 
01303 Cloud::Cloud
01304   (
01305   short dx, short dy    // Deltas for movement.
01306   ) : cloud(CLOUD, 0, SF_SPRITES_VGA), wx(0), wy(0), deltax(dx), deltay(dy), count(-1)
01307   {
01308   Game_window *gwin = Game_window::get_instance();
01309           // Get abs. values.
01310   int adx = deltax > 0 ? deltax : -deltax;
01311   int ady = deltay > 0 ? deltay : -deltay;
01312   if (adx < ady)
01313     max_count = 2*gwin->get_height()/ady;
01314   else
01315     max_count = 2*gwin->get_width()/adx;
01316   start_time = 0;
01317   }
01318 
01319 /*
01320  *  Set starting screen position according to direction.
01321  */
01322 
01323 void Cloud::set_start_pos
01324   (
01325   Shape_frame *shape,
01326   int w, int h,     // Dims. of window.
01327   int& x, int& y      // Screen pos. returned.
01328   )
01329   {
01330   if (!deltax)      // Special cases first.
01331     {
01332     x = rand()%w;
01333     y = deltay > 0 ? -shape->get_ybelow() : 
01334             h + shape->get_yabove();
01335     return;
01336     }
01337   if (!deltay)
01338     {
01339     y = rand()%h;
01340     x = deltax > 0 ? -shape->get_xright() : w + shape->get_xleft();
01341     return;
01342     }
01343   int halfp = w + h;    // 1/2 perimeter.
01344   int r = rand()%halfp;   // Start on one of two sides.
01345   if (r > h)      // Start on top/bottom.
01346     {
01347     x = r - h;
01348     y = deltay > 0 ? -shape->get_ybelow() : 
01349             h + shape->get_yabove();
01350     return;
01351     }
01352   y = r;        // On left or right side.
01353   if (deltax > 0)     // Going right?
01354     x = -shape->get_xright();
01355   else        // Going left?
01356     x = w + shape->get_xleft();
01357   }
01358 
01359 /*
01360  *  Move cloud
01361  */
01362 
01363 inline void Cloud::next
01364   (
01365   Game_window *gwin,    // Game window.
01366   unsigned long curtime,    // Current time of day.
01367   int w, int h      // Dims. of window.
01368   )
01369   {
01370   if (curtime < start_time)
01371     return;     // Not yet.
01372           // Get top-left world pos.
01373   long scrollx = gwin->get_scrolltx()*c_tilesize;
01374   long scrolly = gwin->get_scrollty()*c_tilesize;
01375   Shape_frame *shape = cloud.get_shape();
01376   gwin->add_dirty(gwin->clip_to_win(gwin->get_shape_rect(
01377       shape, wx - scrollx, wy - scrolly).enlarge(4)));
01378   if (count <= 0)     // Time to restart?
01379     {
01380           // Set start time randomly.
01381     start_time = curtime + 2000*randcnt + rand()%2000;
01382     randcnt = (randcnt + 1)%4;
01383     start_time = Game::get_ticks() + 2000*randcnt + rand()%500;
01384 cout << "Cloud: start_time = " << start_time << endl;
01385     count = max_count;
01386     cloud.set_frame(rand()%cloud.get_num_frames());
01387     int x, y;   // Get screen pos.
01388     set_start_pos(shape, w, h, x, y);
01389     wx = x + scrollx;
01390     wy = y + scrolly;
01391     }
01392   else
01393     {
01394     wx += deltax;
01395     wy += deltay;
01396     count--;
01397     }
01398   gwin->add_dirty(gwin->clip_to_win(gwin->get_shape_rect(
01399       shape, wx - scrollx, wy - scrolly).enlarge(4)));
01400   }
01401 
01402 /*
01403  *  Paint cloud.
01404  */
01405 
01406 void Cloud::paint
01407   (
01408   )
01409   {
01410   Game_window *gwin = Game_window::get_instance();
01411   if (count > 0)      // Might not have been started.
01412     cloud.paint_shape(wx - gwin->get_scrolltx()*c_tilesize, 
01413       wy - gwin->get_scrollty()*c_tilesize);
01414   }
01415 
01416 /*
01417  *  Create a few clouds to float across the screen.
01418  */
01419 
01420 Clouds_effect::Clouds_effect
01421   (
01422   int duration,     // In game minutes.
01423   int delay,      // In msecs.
01424   Game_object *egg    // Egg that caused it, or null.
01425   ) : Weather_effect(duration, delay, 6, egg)
01426   {
01427   num_clouds = 2 + rand()%5;  // Pick #.
01428   clouds = new Cloud *[num_clouds];
01429           // Figure wind direction.
01430   int dx = rand()%5 - 2;
01431   int dy = rand()%5 - 2;
01432   if (!dx && !dy)
01433     {
01434     dx = 1 + rand()%2;
01435     dy = 1 - rand()%3;
01436     }
01437   for (int i = 0; i < num_clouds; i++)
01438     {     // Modify speed of some.
01439     int deltax = dx, deltay = dy;
01440     if (rand()%2 == 0)
01441       {
01442       deltax += deltax/2;
01443       deltay += deltay/2;
01444       }
01445     clouds[i] = new Cloud(deltax, deltay);
01446     }
01447   }
01448 
01449 /*
01450  *  Cloud drift.
01451  */
01452 
01453 void Clouds_effect::handle_event
01454   (
01455   unsigned long curtime,    // Current time of day.
01456   long udata
01457   )
01458   {
01459   const int delay = 100;
01460   Game_window *gwin = Game_window::get_instance();
01461   if (curtime >= stop_time)
01462     {     // Time to stop.
01463     eman->remove_effect(this);
01464     gwin->set_all_dirty();
01465     return;
01466     }
01467   int w = gwin->get_width(), h = gwin->get_height();
01468   for (int i = 0; i < num_clouds; i++)
01469     clouds[i]->next(gwin, curtime, w, h);
01470   gwin->get_tqueue()->add(curtime + delay, this, udata);
01471   }
01472 
01473 /*
01474  *  Render.
01475  */
01476 
01477 void Clouds_effect::paint
01478   (
01479   )
01480   {
01481   if (!gwin->is_main_actor_inside())
01482     for (int i = 0; i < num_clouds; i++)
01483       clouds[i]->paint();
01484   }
01485 
01486 /*
01487  *  Shake the screen.
01488  */
01489 
01490 void Earthquake::handle_event
01491   (
01492   unsigned long curtime,    // Current time of day.
01493   long udata
01494   )
01495   {
01496   static int eqsoundonce;
01497 
01498   if(eqsoundonce != 1)
01499   {
01500     eqsoundonce = 1;
01501     // Play earthquake SFX once
01502       Audio::get_ptr()->play_sound_effect(Audio::game_sfx(60));
01503   }
01504 
01505   Game_window *gwin = Game_window::get_instance();
01506   Image_window *win = gwin->get_win();
01507   int w = win->get_width(), h = win->get_height();
01508   int sx = 0, sy = 0;
01509   int dx = rand()%9 - 4;
01510   int dy = rand()%9 - 4;
01511   if (dx > 0)
01512     w -= dx;
01513   else
01514     {
01515     w += dx;
01516     sx -= dx;
01517     dx = 0;
01518     }
01519   if (dy > 0)
01520     h -= dy;
01521   else
01522     {
01523     h += dy;
01524     sy -= dy;
01525     dy = 0;
01526     }
01527   win->copy(sx, sy, w, h, dx, dy);
01528   gwin->set_painted();
01529   gwin->show();
01530           // Shake back.
01531   win->copy(dx, dy, w, h, sx, sy);
01532   if (++i < len)      // More to do?  Put back in queue.
01533     gwin->get_tqueue()->add(curtime + 100, this, udata);
01534   else
01535     {
01536     eqsoundonce = 0;  
01537     delete this;
01538     }
01539 
01540   }
01541 
01542 /*
01543  *  Create a fire field that will last for about 4 seconds.
01544  */
01545 
01546 Fire_field_effect::Fire_field_effect
01547   (
01548   Tile_coord t      // Where to create it.
01549   )
01550   {
01551   field = gmap->create_ireg_object(895, 0);
01552   field->set_flag(Obj_flags::is_temporary);
01553   field->move(t.tx, t.ty, t.tz);
01554   gwin->get_tqueue()->add(Game::get_ticks() + 3000 + rand()%2000, this,
01555                 0L);
01556   }
01557 
01558 /*
01559  *  Remove the field.
01560  */
01561 
01562 void Fire_field_effect::handle_event
01563   (
01564   unsigned long curtime,    // Current time of day.
01565   long udata
01566   )
01567   {
01568   int frnum = field->get_framenum();
01569   if (frnum == 0)     // All done?
01570     {
01571     field->remove_this();
01572     eman->remove_effect(this);
01573     }
01574   else
01575     {
01576     if (frnum > 3)    // Starting to wind down?
01577       {
01578       ((Animated_egg_object *) field)->stop_animation();
01579       frnum = 3;
01580       }
01581     else
01582       frnum--;
01583     gwin->add_dirty(field);
01584     field->set_frame(frnum);
01585     gwin->get_tqueue()->add(curtime + gwin->get_std_delay(), 
01586                 this, udata);
01587     }
01588   }
01589 

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