egg.cc

Go to the documentation of this file.
00001 /*
00002  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00003  *  Copyright (C) 2000-2003  The Exult Team
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018  */
00019 
00020 #ifdef HAVE_CONFIG_H
00021 #  include <config.h>
00022 #endif
00023 
00024 #include "Audio.h"
00025 #include "monsters.h"
00026 #include "cheat.h"
00027 #include "chunks.h"
00028 #include "animate.h"
00029 #include "effects.h"
00030 #include "egg.h"
00031 #include "exult.h"
00032 #include "game.h"
00033 #include "gameclk.h"
00034 #include "gamewin.h"
00035 #include "gamemap.h"
00036 #include "npctime.h"
00037 #include "paths.h"
00038 #include "ucmachine.h"
00039 #include "ucscriptop.h"
00040 #include "ucsched.h"
00041 #include "Gump_manager.h"
00042 
00043 #ifdef USE_EXULTSTUDIO
00044 #include "server.h"
00045 #include "objserial.h"
00046 #include "mouse.h"
00047 #include "servemsg.h"
00048 #endif
00049 
00050 #ifndef UNDER_CE
00051 using std::cout;
00052 using std::endl;
00053 using std::rand;
00054 using std::ostream;
00055 #endif
00056 
00057 Egg_object *Egg_object::editing = 0;
00058 
00059 /*
00060  *  Timer for a missile egg (type-6 egg).
00061  */
00062 class Missile_launcher : public Time_sensitive, public Game_singletons
00063   {
00064   Egg_object *egg;    // Egg this came from.
00065   int weapon;     // Shape for weapon.
00066   int shapenum;     // Shape for missile.
00067   int dir;      // Direction (0-7).  (8==??).
00068   int delay;      // Delay (msecs) between launches.
00069 public:
00070   Missile_launcher(Egg_object *e, int weap, int shnum, int di, int del)
00071     : egg(e), weapon(weap), shapenum(shnum), dir(di), delay(del)
00072     {  }
00073   virtual void handle_event(unsigned long curtime, long udata);
00074   };
00075 
00076 /*
00077  *  Launch a missile.
00078  */
00079 
00080 void Missile_launcher::handle_event
00081   (
00082   unsigned long curtime,
00083   long udata
00084   )
00085   {
00086   Tile_coord src = egg->get_tile();
00087           // Is egg off the screen?
00088   if (!gwin->get_win_tile_rect().has_point(src.tx, src.ty))
00089     return;     // Return w'out adding back to queue.
00090   Projectile_effect *proj = 0;
00091   if (dir < 8)      // Direction given?
00092     {     // Get adjacent tile in direction.
00093     Tile_coord adj = src.get_neighbor(dir%8);
00094           // Make it go 20 tiles.
00095     int dx = adj.tx - src.tx, dy = adj.ty - src.ty;
00096     Tile_coord dest = src;
00097     dest.tx += 20*dx;
00098     dest.ty += 20*dy;
00099     proj = new Projectile_effect(src, dest, shapenum, weapon);
00100     }
00101   else        // Target a party member.
00102     {
00103     Actor *party[9];
00104     int psize = gwin->get_party(party, 1);
00105     int cnt = psize;
00106     int n = rand()%psize; // Pick one at random.
00107           // Find one we can hit.
00108     for (int i = n; !proj && cnt; cnt--, i = (i + 1)%psize)
00109       if (Fast_pathfinder_client::is_straight_path(src,
00110           party[i]->get_tile()))
00111         proj = new Projectile_effect(
00112           src, party[i], shapenum, weapon);
00113     }
00114   if (proj)
00115     eman->add_effect(proj);
00116           // Add back to queue for next time.
00117   gwin->get_tqueue()->add(curtime + (delay > 0 ? delay : 1), this, udata);
00118   }
00119 
00120 /*
00121  *  Paint at given spot in world.
00122  */
00123 
00124 void Egglike_game_object::paint
00125   (
00126   )
00127   {
00128   if(gwin->paint_eggs)
00129     Game_object::paint();
00130   }
00131 
00132 /*
00133  *  Can this be clicked on?
00134  */
00135 
00136 int Egglike_game_object::is_findable
00137   (
00138   )
00139   {
00140   return gwin->paint_eggs && Ireg_game_object::is_findable();
00141   }
00142 
00143 /*
00144  *  Create an egg from IREG data.
00145  */
00146 
00147 Egg_object::Egg_object
00148   (
00149   int shapenum, int framenum,
00150   unsigned int tilex, unsigned int tiley, 
00151   unsigned int lft, 
00152   unsigned short itype,
00153   unsigned char prob, 
00154   short d1, short d2
00155   ) : Egglike_game_object(shapenum, framenum, tilex, tiley, lft),
00156       probability(prob), data1(d1), data2(d2),
00157       area(Rectangle(0, 0, 0, 0)), launcher(0)
00158   {
00159   type = itype&0xf;
00160           // Teleport destination?
00161   if (type == teleport && framenum == 6 && shapenum == 275)
00162     type = path;    // (Mountains N. of Vesper).
00163   criteria = (itype & (7<<4)) >> 4;
00164   distance = (itype >> 10) & 0x1f;
00165   unsigned char noct = (itype >> 7) & 1;
00166   unsigned char do_once = (itype >> 8) & 1;
00167           // Missile eggs can be rehatched
00168   unsigned char htch = (type == missile) ? 0 : ((itype >> 9) & 1);
00169   solid_area = (criteria == something_on || criteria == cached_in ||
00170           // Teleports need solid area.
00171             type == teleport) ? 1 : 0;
00172   unsigned char ar = (itype >> 15) & 1;
00173   flags = (noct << nocturnal) + (do_once << once) +
00174       (htch << hatched) + (ar << auto_reset);
00175   if (type == usecode || type == teleport || type == path)
00176     set_quality(data1&0xff);
00177           // Party_near & auto_reset don't mix
00178           //   well.
00179   if (criteria == party_near && (flags&(1<<auto_reset)))
00180     criteria = avatar_near;
00181   }
00182 
00183 /*
00184  *  Init. for a field.
00185  */
00186 
00187 inline void Egg_object::init_field
00188   (
00189   unsigned char ty    // Egg (field) type.
00190   )
00191   {
00192   type = ty;
00193   probability = 100;
00194   data1 = data2 = 0;
00195   launcher = 0;
00196   area = Rectangle(0, 0, 0, 0);
00197   criteria = party_footpad;
00198   distance = 0;
00199   solid_area = 0;
00200   flags = (1 << auto_reset);
00201   }
00202 
00203 /*
00204  *  Create an egg representing a field.
00205  */
00206 
00207 Egg_object::Egg_object
00208   (
00209   int shapenum,
00210   int framenum,
00211   unsigned int tilex, unsigned int tiley, 
00212   unsigned int lft, 
00213   unsigned char ty    // Egg (field) type.
00214   ) : Egglike_game_object(shapenum, framenum, tilex, tiley, lft)
00215   {
00216   init_field(ty);
00217   }
00218 
00219 /*
00220  *  Destructor:
00221  */
00222 
00223 Egg_object::~Egg_object
00224   (
00225   )
00226   {
00227   if (launcher)
00228     {
00229     gwin->get_tqueue()->remove(launcher);
00230     delete launcher;
00231     }
00232   }
00233 
00234 /*
00235  *  Set active area after being added to its chunk.
00236  */
00237 
00238 void Egg_object::set_area
00239   (
00240   )
00241   {
00242   if (!probability || type == path)// No chance of normal activation?
00243     {
00244     area = Rectangle(0, 0, 0, 0);
00245     return;
00246     }
00247   Tile_coord t = get_tile();  // Get absolute tile coords.
00248   switch (criteria)   // Set up active area.
00249     {
00250   case cached_in:     // Make it really large.
00251     area = Rectangle(t.tx - 32, t.ty - 32, 64, 64);
00252     break;
00253   case avatar_footpad:
00254   case party_footpad:
00255     {
00256     Shape_info& info = get_info();
00257     int xtiles = info.get_3d_xtiles(), 
00258         ytiles = info.get_3d_ytiles();
00259     area = Rectangle(t.tx - xtiles + 1, t.ty - ytiles + 1,
00260               xtiles, ytiles);
00261     break;
00262     }
00263   case avatar_far:    // Make it 1 tile bigger each dir.
00264     area = Rectangle(t.tx - distance - 1, t.ty - distance - 1, 
00265           2*distance + 3, 2*distance + 3);
00266     break;
00267   default:
00268     {
00269     int width = 2*distance;
00270     width++;    // Added 8/1/01.
00271     if (distance <= 1)  // Small?
00272       {
00273           // More guesswork:
00274       if (criteria == external_criteria)
00275         width += 2;
00276       }
00277     area = Rectangle(t.tx - distance, t.ty - distance, 
00278           width, width);
00279     break;
00280     }
00281     }
00282           // Don't go outside the world.
00283   Rectangle world(0, 0, c_num_chunks*c_tiles_per_chunk,
00284             c_num_chunks*c_tiles_per_chunk);
00285   area = area.intersect(world);
00286   }
00287 
00288 /*
00289  *  Is the egg active when stepping onto a given spot, or placing an obj.
00290  *  on the spot?
00291  */
00292 
00293 int Egg_object::is_active
00294   (
00295   Game_object *obj,   // Object placed (or Actor).
00296   int tx, int ty, int tz,   // Tile stepped onto.
00297   int from_tx, int from_ty  // Tile stepped from.
00298   )
00299   {
00300   if ((flags & (1 << (int) hatched)) &&
00301       !(flags & (1 << (int) auto_reset)))
00302     return (0);   // For now... Already hatched.
00303   if (flags & (1 << (int) nocturnal))
00304     {     // Nocturnal.
00305     int hour = gclock->get_hour();
00306     if (!(hour >= 9 || hour <= 5))
00307       return (0); // It's not night.
00308     }
00309   if (cheat.in_map_editor())
00310     return 0;   // Disable in map-editor.
00311   Egg_criteria cri = (Egg_criteria) get_criteria();
00312 
00313   int deltaz = tz - get_lift();
00314   int absdeltaz = deltaz < 0 ? -deltaz : deltaz;
00315   switch (cri)
00316     {
00317   case cached_in:     // Anywhere in square.
00318     {
00319     if (obj != gwin->get_main_actor() || !area.has_point(tx, ty))
00320       return 0; // Not in square.
00321     if (!(flags & (1 << (int) hatched)))
00322       return 1; // First time.
00323           // Must have autoreset.
00324           // Just activate when reentering.
00325     return !area.has_point(from_tx, from_ty);
00326     }
00327   case avatar_near:
00328     if (obj != gwin->get_main_actor())
00329       return 0;
00330 #ifdef DEBUG
00331     print_debug();
00332 #endif
00333     // fall through
00334   case party_near:    // Avatar or party member.
00335     if (!obj->get_flag(Obj_flags::in_party))
00336       return 0;
00337     if (type == teleport) // Teleports:  Any tile, exact lift.
00338       return absdeltaz == 0 && area.has_point(tx, ty);
00339     if (!((absdeltaz <= 1 || 
00340           // Using trial&error here:
00341        (Game::get_game_type() == SERPENT_ISLE &&
00342             type != missile) ||
00343         (type == missile && tz/5 == get_lift()/5)) &&
00344           // New tile is in, old is out.
00345       area.has_point(tx, ty) &&
00346           !area.has_point(from_tx, from_ty)))
00347       return 0;
00348     return 1;
00349   case avatar_far:    // New tile is outside, old is inside.
00350     {
00351     if (obj != gwin->get_main_actor() || !area.has_point(tx, ty))
00352       return (0);
00353     Rectangle inside(area.x + 1, area.y + 1, 
00354             area.w - 2, area.h - 2);
00355     return inside.has_point(from_tx, from_ty) &&
00356       !inside.has_point(tx, ty);
00357     }
00358   case avatar_footpad:
00359     return obj == gwin->get_main_actor() && deltaz == 0 &&
00360             area.has_point(tx, ty);
00361   case party_footpad:
00362     return area.has_point(tx, ty) && deltaz == 0 &&
00363           obj->get_flag(Obj_flags::in_party);
00364   case something_on:
00365     return      // Guessing.  At SI end, deltaz == -1.
00366       deltaz >= -1 && deltaz <= 3 &&
00367       area.has_point(tx, ty) && !obj->as_actor();
00368   case external_criteria:
00369   default:
00370     return 0;
00371     }
00372   }
00373 
00374 /*
00375  *  Paint at given spot in world.
00376  */
00377 
00378 void Egg_object::paint
00379   (
00380   )
00381   {
00382   Egglike_game_object::paint();
00383           // Make sure launcher is active.
00384   if (launcher && !launcher->in_queue())
00385     gwin->get_tqueue()->add(0L, launcher, 0);
00386   }
00387 
00388 /*
00389  *  Run usecode when double-clicked.
00390  */
00391 
00392 void Egg_object::activate
00393   (
00394   int /* event */
00395   )
00396   {
00397   if (!edit())
00398     activate(0, 0);
00399   }
00400 
00401 /*
00402  *  Edit in ExultStudio.
00403  *
00404  *  Output: True if map-editing & ES is present.
00405  */
00406 
00407 bool Egg_object::edit
00408   (
00409   )
00410   {
00411 #ifdef USE_EXULTSTUDIO
00412   if (client_socket >= 0 && // Talking to ExultStudio?
00413       cheat.in_map_editor())
00414     {
00415     editing = 0;
00416     Tile_coord t = get_tile();
00417     unsigned long addr = (unsigned long) this;
00418     if (Egg_object_out(client_socket, addr, t.tx, t.ty, t.tz,
00419       get_shapenum(), get_framenum(), 
00420       type, criteria, probability, distance,
00421       (flags>>nocturnal)&1, (flags>>once)&1, 
00422       (flags>>hatched)&1, (flags>>auto_reset)&1, 
00423       data1, data2) != -1)
00424       {
00425       cout << "Sent egg data to ExultStudio" << endl;
00426       editing = this;
00427       }
00428     else
00429       cout << "Error sending egg data to ExultStudio" <<endl;
00430     return true;
00431     }
00432 #endif
00433   return false;
00434   }
00435 
00436 
00437 /*
00438  *  Message to update from ExultStudio.
00439  */
00440 
00441 void Egg_object::update_from_studio
00442   (
00443   unsigned char *data,
00444   int datalen
00445   )
00446   {
00447 #ifdef USE_EXULTSTUDIO
00448   unsigned long addr;
00449   int tx, ty, tz;
00450   int shape, frame;
00451   int type;
00452   int criteria;
00453   int probability;
00454   int distance;
00455   bool nocturnal, once, hatched, auto_reset;
00456   int data1, data2;
00457   if (!Egg_object_in(data, datalen, addr, tx, ty, tz, shape, frame,
00458     type, criteria, probability, distance, 
00459     nocturnal, once, hatched, auto_reset,
00460     data1, data2))
00461     {
00462     cout << "Error decoding egg" << endl;
00463     return;
00464     }
00465   Egg_object *egg = (Egg_object *) addr;
00466   if (egg && egg != editing)
00467     {
00468     cout << "Egg from ExultStudio is not being edited" << endl;
00469     return;
00470     }
00471   editing = 0;
00472   if (!egg)     // Create a new one?
00473     {
00474     int x, y;
00475     if (!Get_click(x, y, Mouse::hand, 0))
00476       {
00477       if (client_socket >= 0)
00478         Exult_server::Send_data(client_socket, Exult_server::cancel);
00479       return;
00480       }
00481     if (shape == -1)
00482       shape = 275;  // FOR NOW.
00483           // Create.  Gets initialized below.
00484     egg = new Egg_object(shape, 0, 0, 0, 0, 0, 0, 0, 0);
00485     int lift;   // Try to drop at increasing hts.
00486     for (lift = 0; lift < 12; lift++)
00487       if (gwin->drop_at_lift(egg, x, y, lift))
00488         break;
00489     if (lift == 12)
00490       {
00491       if (client_socket >= 0)
00492         Exult_server::Send_data(client_socket, 
00493               Exult_server::cancel);
00494       delete egg;
00495       return;
00496       }
00497     if (client_socket >= 0)
00498       Exult_server::Send_data(client_socket, 
00499             Exult_server::user_responded);
00500     }
00501   egg->type = type;
00502   if (shape != -1)
00503     egg->set_shape(shape);
00504   if (frame == -1)
00505     switch (type)
00506       {   // (These aren't perfect.)
00507     case monster: frame = 0; break;
00508     case jukebox: frame = 2; break;
00509     case soundsfx:frame = 1; break;
00510     case voice:   frame = 3; break;
00511     case weather: frame = 4; break;
00512     case teleport:frame = 5; break;
00513     case path:    frame = 6; break;
00514         case missile:
00515             egg->set_shape(200); 
00516             if ((data2 & 0xFF) < 8)
00517                 frame = 2 + ((data2 & 0xFF) / 2);
00518             else
00519                 frame = 1;
00520             break;
00521     default:      frame = 7; break;
00522       }
00523   if (frame != -1)
00524     egg->set_frame(frame);
00525   gwin->add_dirty(egg);
00526   egg->criteria = criteria&7;
00527   egg->distance = distance&31;
00528   egg->probability = probability;
00529   egg->flags = ((nocturnal?1:0)<<Egg_object::nocturnal) +
00530     ((once?1:0)<<Egg_object::once) +
00531     ((hatched?1:0)<<Egg_object::hatched) +
00532     ((auto_reset?1:0)<<Egg_object::auto_reset);
00533   egg->data1 = data1;
00534   egg->data2 = data2;
00535   if (type == usecode || type == teleport || type == path)
00536     egg->set_quality(data1&0xff);
00537   Map_chunk *chunk = 
00538       gmap->get_chunk_safely(egg->get_cx(), egg->get_cy());
00539   chunk->remove_egg(egg);   // Got to add it back.
00540   chunk->add_egg(egg);
00541   cout << "Egg updated" << endl;
00542 #endif
00543   }
00544 
00545 /*
00546  *  Create a monster nearby.
00547  */
00548 
00549 static void Create_monster
00550   (
00551   Game_window *gwin,
00552   Egg_object *egg,
00553   int shnum,      // Monster shape.
00554   Monster_info *inf,    // Info.
00555   int sched,
00556   int align
00557   )
00558   {
00559   Tile_coord dest = Map_chunk::find_spot(
00560         egg->get_tile(), 5, shnum, 0, 1);
00561   if (dest.tx != -1)
00562     {
00563     Monster_actor *monster = Monster_actor::create(shnum, dest,
00564                 sched, align);
00565     gwin->add_dirty(monster);
00566     gwin->add_nearby_npc(monster);
00567     }
00568   }
00569 
00570 /*
00571  *  Handle a teleport egg.
00572  */
00573 
00574 void Egg_object::activate_teleport
00575   (
00576   Game_object *obj    // Object (actor) that came near it.
00577   )
00578   {
00579   Tile_coord pos(-1, -1, -1); // Get position to jump to.
00580   int qual = get_quality();
00581   if (qual == 255)
00582     {     // Jump to coords.
00583     int schunk = data1 >> 8;
00584     pos = Tile_coord(
00585       (schunk%12)*c_tiles_per_schunk + (data2&0xff), 
00586       (schunk/12)*c_tiles_per_schunk + (data2>>8), 0);
00587     }
00588   else
00589     {
00590     Egg_vector vec;   // Look for dest. egg (frame == 6).
00591     if (find_nearby_eggs(vec, 275, 256, qual, 6))
00592       {
00593       Egg_object *path = vec[0];
00594       pos = path->get_tile();
00595       }
00596     }
00597   cout << "Should teleport to (" << pos.tx << ", " <<
00598           pos.ty << ')' << endl;
00599   if (pos.tx != -1 && obj && obj->get_flag(Obj_flags::in_party))
00600           // Teleport everyone!!!
00601     gwin->teleport_party(pos);
00602   }
00603 
00604 /*
00605  *  Hatch egg.
00606  */
00607 
00608 void Egg_object::activate
00609   (
00610   Game_object *obj,   // Object (actor) that came near it.
00611   bool must     // If 1, skip dice roll & execute
00612           //   usecode eggs immediately.
00613   )
00614   {
00615 #ifdef DEBUG
00616   print_debug();
00617 #endif
00618           // Flag it as done.
00619   flags |= (1 << (int) hatched);
00620 /*  +++++ I just move the 'hatched' line here from below the 'return' in
00621 an effort to fix the 'monsters spawning too often' bug.   NEED to see if this
00622 breaks anything!  */
00623 
00624   /*
00625     MAJOR HACK!
00626     This is an attempt at a work-around of a potential bug in the original
00627     Serpent Isle. See SourceForge bug #879253
00628 
00629     Prevent the Serpent Staff egg from hatching only once
00630   */
00631   Tile_coord eggpos = get_tile();
00632   if (GAME_SI && eggpos.tx == 1287 && eggpos.ty == 2568 && eggpos.tz == 0) {
00633     flags &= ~(1 << (int) hatched);
00634   }
00635   /* end hack */
00636 
00637 
00638   int roll = must ? 0 : 1 + rand()%100;
00639   if (roll > probability)
00640     return;     // Out of luck.
00641   switch(type)
00642     {
00643   case jukebox:
00644 #ifdef DEBUG
00645     cout << "Audio parameters might be: " << (data1&0xff) << " and " << ((data1>>8)&0x01) << endl;
00646 #endif
00647     Audio::get_ptr()->start_music((data1)&0xff,(data1>>8)&0x01);
00648     break;
00649   case soundsfx:
00650     {
00651     int dir = 0;
00652     if (obj)    // Get direction from obj. to egg.
00653       {
00654       Tile_coord epos = get_tile(), opos = obj->get_tile();
00655       dir = Get_direction16(opos.ty - epos.ty, 
00656               epos.tx - opos.tx);
00657       }
00658     Audio::get_ptr()->play_sound_effect(
00659       Audio::game_sfx(data1&0xff), SDL_MIX_MAXVOLUME, dir,
00660                 (data1>>8)&1);
00661     break;
00662     }
00663   case voice:
00664     ucmachine->do_speech(data1&0xff);
00665     break;
00666   case monster:     // Also creates other objects.
00667     {
00668     int shnum = data2&1023;
00669     int frnum = data2>>10;
00670     Monster_info *inf = 
00671         ShapeID::get_info(shnum).get_monster_info();
00672     if (inf)
00673       {   // Armageddon spell cast?
00674       if (gwin->armageddon)
00675         break;
00676       int sched = data1>>8;
00677       int align = data1&3;
00678       int cnt = (data1&0xff)>>2;
00679       if (cnt > 1)  // Randomize.
00680         cnt = 1 + (rand()%cnt);
00681       while (cnt--)
00682         Create_monster(gwin, this, shnum, inf,
00683               sched, align);
00684       }
00685     else      // Create item.
00686       {
00687       Shape_info& info = ShapeID::get_info(shnum);
00688       Game_object *nobj =gmap->create_ireg_object(info,
00689         shnum, frnum, get_tx(), get_ty(), get_lift());
00690       Map_chunk *chunk = 
00691           gmap->get_chunk(get_cx(), get_cy());
00692       if (nobj->is_egg())
00693         chunk->add_egg((Egg_object *) nobj);
00694       else
00695         chunk->add(nobj);
00696       gwin->add_dirty(nobj);
00697       nobj->set_flag(Obj_flags::okay_to_take);
00698           // Objects are created temporary
00699       nobj->set_flag(Obj_flags::is_temporary);
00700       }
00701     break;
00702     }
00703   case usecode:
00704     {     // Data2 is the usecode function.
00705     if (must)   // From script?  Do immediately.
00706       ucmachine->call_usecode(data2, this,
00707           Usecode_machine::egg_proximity);
00708     else      // Do on next animation frame.
00709       {
00710       Usecode_script *scr = new Usecode_script(this);
00711       scr->add(Ucscript::usecode, data2);
00712       if (flags & (1<<(int)once))
00713         { // Don't remove until done.
00714         scr->add(Ucscript::remove);
00715         flags &= ~(1<<(int)once);
00716         }
00717       scr->start(gwin->get_std_delay());
00718       }
00719     break;
00720     }
00721   case missile:
00722     {
00723           // Get data.  Not sure about delay.
00724     int weapon = data1, dir = data2&0xff, delay = data2>>8;
00725     Shape_info& info = ShapeID::get_info(weapon);
00726     Weapon_info *winf = info.get_weapon_info();
00727     int proj;
00728     if (winf && winf->get_projectile())
00729       proj = winf->get_projectile();
00730     else
00731       proj = 856; // Fireball.  Shouldn't get here.
00732     if (!launcher)
00733       launcher = new Missile_launcher(this, weapon,
00734           proj, dir, gwin->get_std_delay()*delay);
00735     if (!launcher->in_queue())
00736       gwin->get_tqueue()->add(0L, launcher, 0);
00737     break;
00738     }
00739   case teleport:
00740     activate_teleport(obj);
00741     break;
00742   case weather:
00743     {
00744     set_weather(data1&0xff, data1>>8, this);
00745     break;
00746     }
00747   case button:    // Set off all in given area.
00748     {
00749     int dist = data1&0xff;
00750     Egg_vector eggs;
00751     find_nearby_eggs(eggs, 275, dist);
00752     for (Egg_vector::const_iterator it = eggs.begin();
00753           it != eggs.end(); ++it)
00754       {
00755       Egg_object *egg = *it;
00756       if (egg != this &&
00757           egg->criteria == external_criteria && 
00758           !(egg->flags & (1 << (int) hatched))) // Experimental attempting to fix problem in Silver Seed
00759         egg->activate(obj, 0);
00760       }
00761     break;
00762     }
00763   default:
00764     cout << "Egg not actioned" << endl;
00765     }
00766   if (flags & (1 << (int) once))
00767     remove_this(0);
00768   }
00769 
00770 /*
00771  *  Print debug information.
00772  */
00773 
00774 void Egg_object::print_debug
00775   (
00776   )
00777   {
00778   cout << "Egg type is " << (int) type << ", prob = " << 
00779     (int) probability <<
00780     ", distance = " << (int) distance << ", crit = " <<
00781     (int) criteria << ", once = " <<
00782   ((flags & (1<<(int)once)) != 0) << ", hatched = " <<
00783   ((flags & (1<<(int)hatched)) != 0) <<
00784   ", areset = " <<
00785   ((flags & (1<<(int)auto_reset)) != 0) << ", data1 = " << data1
00786     << ", data2 = " << data2 << endl;
00787   }
00788 
00789 /*
00790  *  Set the weather (static).
00791  */
00792 
00793 void Egg_object::set_weather
00794   (
00795   int weather,      // 0-6.
00796   int len,      // In game minutes (I think).
00797   Game_object *egg    // Egg this came from, or null.
00798   )
00799   {
00800   if (!len)     // Means continuous.
00801     len = 120;    // How about a couple game hours?
00802   int cur = eman->get_weather();
00803   cout << "Current weather is " << cur << "; setting " << weather
00804               << endl;
00805   switch (weather)
00806     {
00807   case 0:   // Back to normal.
00808     eman->remove_weather_effects();
00809     break;
00810   case 1:   // ++++++++LATER:  Should be snow, I think (jsf).
00811   case 2:   // Storm.
00812     if (cur != weather)
00813       eman->add_effect(new Storm_effect(len, 0, egg));
00814     break;
00815   case 3:   // (On Ambrosia).
00816     eman->remove_weather_effects();
00817     eman->add_effect(new Sparkle_effect(len, 0, egg));
00818     break;
00819   case 6:   // Clouds.
00820     eman->add_effect(new Clouds_effect(len, 0, egg));
00821     break;
00822   default:
00823     break;
00824     }
00825   }
00826 
00827 /*
00828  *  Move to a new absolute location.  This should work even if the old
00829  *  location is invalid (cx=cy=255).
00830  */
00831 
00832 void Egg_object::move
00833   (
00834   int newtx, 
00835   int newty, 
00836   int newlift
00837   )
00838   {
00839           // Figure new chunk.
00840   int newcx = newtx/c_tiles_per_chunk, newcy = newty/c_tiles_per_chunk;
00841   Map_chunk *newchunk = gmap->get_chunk_safely(newcx, newcy);
00842   if (!newchunk)
00843     return;     // Bad loc.
00844   remove_this(1);     // Remove from old.
00845   set_lift(newlift);    // Set new values.
00846   shape_pos = ((newtx%c_tiles_per_chunk) << 4) + newty%c_tiles_per_chunk;
00847   newchunk->add_egg(this);  // Updates cx, cy.
00848   gwin->add_dirty(this);    // And repaint new area.
00849   }
00850 
00851 /*
00852  *  This is needed since it calls remove_egg().
00853  */
00854 
00855 void Egg_object::remove_this
00856          (
00857          int nodel                       // 1 to not delete.
00858          )
00859   {
00860   if (get_owner())    // Watch for this.
00861     get_owner()->remove(this);
00862   else
00863     {
00864     Map_chunk *chunk = gmap->get_chunk_safely(cx, cy);
00865     if (chunk)
00866       {
00867       gwin->add_dirty(this);  // (Make's ::move() simpler.).
00868       chunk->remove_egg(this);
00869       }
00870     }
00871   if (launcher)     // Stop missiles.
00872     {
00873     gwin->get_tqueue()->remove(launcher);
00874     delete launcher;
00875     launcher = 0;
00876     }
00877    if (!nodel)
00878      gwin->delete_object(this);
00879    }
00880 
00881 /*
00882  *  Write out.
00883  */
00884 
00885 void Egg_object::write_ireg
00886   (
00887   DataSource *out
00888   )
00889   {
00890   unsigned char buf[13];    // 13-byte entry + length-byte.
00891   buf[0] = 12;
00892   uint8 *ptr = &buf[1]; // To avoid confusion about offsets.
00893   write_common_ireg(ptr);   // Fill in bytes 1-4.
00894   ptr += 4;
00895   unsigned short tword = type&0xf;// Set up 'type' word.
00896   tword |= ((criteria&7)<<4);
00897   tword |= (((flags>>nocturnal)&1)<<7);
00898   tword |= (((flags>>once)&1)<<8);
00899   tword |= (((flags>>hatched)&1)<<9);
00900   tword |= ((distance&0x1f)<<10);
00901   tword |= (((flags>>auto_reset)&1)<<15);
00902   Write2(ptr, tword);
00903   *ptr++ = probability;
00904   Write2(ptr, data1);
00905   *ptr++ = (get_lift()&15)<<4;
00906   Write2(ptr, data2);
00907   out->write((char*)buf, sizeof(buf));
00908           // Write scheduled usecode.
00909   Game_map::write_scheduled(out, this); 
00910   }
00911 
00912 // Get size of IREG. Returns -1 if can't write to buffer
00913 int Egg_object::get_ireg_size()
00914 {
00915   // These shouldn't ever happen, but you never know
00916   if (gumpman->find_gump(this) || Usecode_script::find(this))
00917     return -1;
00918 
00919   return 13;
00920 }
00921 
00922 /*
00923  *  Create from IREG data.
00924  */
00925 Animated_egg_object::Animated_egg_object
00926   (
00927   int shapenum, int framenum,
00928   unsigned int tilex,
00929   unsigned int tiley, unsigned int lft, 
00930   unsigned short itype,
00931   unsigned char prob, short d1, short d2
00932   ) : Egg_object(shapenum, framenum, 
00933         tilex, tiley, lft, itype, prob, d1, d2)
00934   { 
00935   animator = new Frame_animator(this); 
00936   }
00937 
00938 /*
00939  *  Create for fields.
00940  */
00941 
00942 Animated_egg_object::Animated_egg_object
00943   (
00944   int shapenum, int framenum, unsigned int tilex, 
00945   unsigned int tiley, unsigned int lft,
00946   unsigned char ty
00947   ) : Egg_object(shapenum, framenum, tilex, tiley, lft, ty)
00948   {
00949   int recycle = 0;    // Frame to begin new cycles after 1st.
00950   switch (type)
00951     {
00952   case poison_field:
00953     recycle = 6; break;
00954   case sleep_field:
00955     recycle = 1; break;
00956   case fire_field:
00957     recycle = 8; break;
00958   case caltrops_field:
00959     animator = 0; return; // This doesn't get animated.
00960     }
00961   animator = new Field_frame_animator(this, recycle); 
00962   }
00963 
00964 /*
00965  *  Delete.
00966  */
00967 Animated_egg_object::~Animated_egg_object()
00968   { 
00969   delete animator; 
00970   }
00971 
00972 /*
00973  *  Render.
00974  */
00975 
00976 void Animated_egg_object::paint
00977   (
00978   )
00979   {
00980   if (animator)
00981     animator->want_animation(); // Be sure animation is on.
00982   Ireg_game_object::paint();  // Always paint these.
00983   }
00984 
00985 /*
00986  *  Run usecode when double-clicked or when activated by proximity.
00987  */
00988 
00989 void Animated_egg_object::activate
00990   (
00991   int event
00992   )
00993   {
00994   Egg_object::activate(event);
00995   flags &= ~(1 << (int) hatched); // Moongate:  reset always.
00996   }
00997 
00998 /*
00999  *  Stop animation.
01000  */
01001 
01002 void Animated_egg_object::stop_animation
01003   (
01004   )
01005   {
01006   delete animator;
01007   animator = 0;
01008   }
01009 
01010 /*
01011  *  Apply field.
01012  *
01013  *  Output: True to delete field.
01014  */
01015 
01016 bool Field_object::field_effect
01017   (
01018   Actor *actor
01019   )
01020   {
01021   bool del = false;   // Only delete poison, sleep fields.
01022   switch (type)
01023     {
01024   case poison_field:
01025     if (rand()%2)
01026       {
01027       actor->set_flag(Obj_flags::poisoned);
01028       del = true;
01029       }
01030     break;
01031   case sleep_field:
01032     if (rand()%2)
01033       {
01034       actor->set_flag(Obj_flags::asleep);
01035       del = true;
01036       }
01037     break;
01038   case fire_field:
01039           // Blue fire (serpent isle)?
01040     if (get_shapenum() == 561)
01041       {
01042       actor->reduce_health(5 + rand()%4);
01043       }
01044     else if (rand()%2)
01045       {
01046       actor->reduce_health(1);
01047       }
01048           // But no sleeping here.
01049     actor->clear_flag(Obj_flags::asleep);
01050     break;
01051   case caltrops_field:
01052     if (actor->get_property(Actor::intelligence)*
01053         (actor->get_flag(Obj_flags::might) ? 2 : 1) < rand()%40)
01054       {
01055       actor->reduce_health(2 + rand()%3);
01056       }
01057     return false;
01058     }
01059   if (!del)     // Tell animator to keep checking.
01060     ((Field_frame_animator *) animator)->activated = true;
01061   return del;
01062   }
01063 
01064 /*
01065  *  Run usecode when double-clicked or when activated by proximity.
01066  *  (Generally, nothing will happen.)
01067  */
01068 
01069 void Field_object::activate
01070   (
01071   int event
01072   )
01073   {
01074           // Field_frame_animator calls us with
01075           //   event==0 to check for damage.
01076   if (event != Usecode_machine::npc_proximity)
01077     {
01078     Ireg_game_object::activate(event);
01079     return;
01080     }
01081   Actor_queue npcs;   // Find all nearby NPC's.
01082   gwin->get_nearby_npcs(npcs);
01083   npcs.push(gwin->get_main_actor());  // Include Avatar.
01084   Rectangle eggfoot = get_footprint();
01085           // Clear flag to check.
01086   ((Field_frame_animator *) animator)->activated = false;
01087   for (Actor_queue::const_iterator it = npcs.begin(); 
01088             it != npcs.end(); ++it)
01089     {
01090     Actor *actor = *it;
01091     if (actor->is_dead() || Game_object::distance(actor) > 4)
01092       continue;
01093     if (actor->get_footprint().intersects(eggfoot))
01094       Field_object::activate(actor);
01095     }
01096   }
01097 
01098 /*
01099  *  Someone stepped on it.
01100  */
01101 
01102 void Field_object::activate
01103   (
01104   Game_object *obj,   // Object (actor) that came near it.
01105   bool /* must */     // If 1, skip dice roll.
01106   )
01107   {
01108   if (field_effect((Actor *) obj))// Apply field.
01109     remove_this(0);   // Delete sleep/poison if applied.
01110   }
01111 
01112 /*
01113  *  Write out.  These are stored as normal game objects.
01114  */
01115 
01116 void Field_object::write_ireg
01117   (
01118   DataSource *out
01119   )
01120   {
01121   Ireg_game_object::write_ireg(out);
01122   }
01123 
01124 // Get size of IREG. Returns -1 if can't write to buffer
01125 int Field_object::get_ireg_size()
01126 {
01127   return Ireg_game_object::get_ireg_size();
01128 }
01129 
01130 
01131 /*
01132  *  It's a Mirror
01133  */
01134 
01135 Mirror_object::Mirror_object(int shapenum, int framenum, unsigned int tilex, 
01136          unsigned int tiley, unsigned int lft) :
01137   Egg_object(shapenum, framenum, tilex, tiley, lft, Egg_object::mirror_object)
01138 {
01139   solid_area = 1;
01140 }
01141 
01142 void Mirror_object::activate(int event)
01143 {
01144   Ireg_game_object::activate(event);
01145 }
01146 
01147 void Mirror_object::activate(Game_object *obj, bool must)
01148 {
01149   // These are broken, so dont touch
01150   if ((get_framenum()%3) == 2)  return;
01151 
01152   int wanted_frame = get_framenum()/3;
01153   wanted_frame *= 3;
01154 
01155   // Find upperleft or our area
01156   Tile_coord t = get_tile();
01157 
01158   // To left or above?
01159   if (get_shapenum()==268)  // Left
01160   {
01161     t.tx++;
01162     t.ty--;
01163   }
01164   else        // Above
01165   {
01166     t.tx--;
01167     t.ty++;
01168   }
01169 
01170   // We just want to know if the area is blocked
01171   int nl = 0;
01172   if (Map_chunk::is_blocked(1, t.tz, t.tx, t.ty, 2, 2, nl, MOVE_WALK, 0))
01173   {
01174     wanted_frame++;
01175   }
01176 
01177   // Only if it changed update the shape
01178   if (get_framenum()!=wanted_frame)
01179     change_frame(wanted_frame);
01180 }
01181 
01182 // Can it be activated?
01183 int Mirror_object::is_active(Game_object *obj, int tx, int ty, int tz, int from_tx, int from_ty)
01184 {
01185   // These are broken, so dont touch
01186   int frnum = get_framenum();
01187   if (frnum%3 == 2)  return 0;
01188   if (frnum >= 3 && GAME_BG)  // Demon mirror in FOV.
01189     return 0;
01190 
01191   return 1;
01192 }
01193 
01194 // Set up active area.
01195 void Mirror_object::set_area()
01196 {
01197   // These are broken, so dont touch
01198   if ((get_framenum()%3) == 2) area = Rectangle(0, 0, 0, 0);
01199 
01200   Tile_coord t = get_tile();  // Get absolute tile coords.
01201 
01202   // To left or above?
01203   if (get_shapenum()==268) area = Rectangle(t.tx-1, t.ty-3, 6, 6);
01204   else  area = Rectangle(t.tx-3 , t.ty-1, 6, 6);
01205 }
01206 
01207 void Mirror_object::paint()
01208 {
01209   Ireg_game_object::paint();  // Always paint these.
01210 }
01211 
01212 /*
01213  *  Write out.  These are stored as normal game objects.
01214  */
01215 
01216 void Mirror_object::write_ireg(DataSource *out)
01217 {
01218   Ireg_game_object::write_ireg(out);
01219 }
01220 
01221 // Get size of IREG. Returns -1 if can't write to buffer
01222 int Mirror_object::get_ireg_size()
01223 {
01224   // TODO!!!!!!!
01225   return Ireg_game_object::get_ireg_size();
01226 }

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