ucsched.cc

Go to the documentation of this file.
00001 /*
00002  *  Copyright (C) 2000-2001  The Exult Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (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 #ifdef HAVE_CONFIG_H
00020 #  include <config.h>
00021 #endif
00022 
00023 #include "ucinternal.h"
00024 #include "useval.h"
00025 #include "ucsched.h"
00026 #include "Audio.h"
00027 #include "barge.h"
00028 #include "game.h"
00029 #include "frameseq.h"
00030 #include "gamewin.h"
00031 #include "gameclk.h"
00032 #include "egg.h"
00033 #include "actors.h"
00034 #include "ucscriptop.h"
00035 
00036 #include <iostream>
00037 #include <iomanip>
00038 
00039 using std::cout;
00040 using std::endl;
00041 using std::hex;
00042 using std::setfill;
00043 using std::setw;
00044 
00045 using namespace Ucscript;
00046 
00047 int Usecode_script::count = 0;
00048 Usecode_script *Usecode_script::first = 0;
00049 
00050 /*
00051  *  Create for a 'restore'.
00052  */
00053 
00054 Usecode_script::Usecode_script
00055   (
00056   Game_object *item, 
00057   Usecode_value *cd, 
00058   int findex,
00059   int nhalt, 
00060   int del
00061   ) : obj(item), code(cd), i(0), frame_index(findex), 
00062       no_halt(nhalt != 0), must_finish(false), delay(del)
00063   {
00064   cnt = code->get_array_size();
00065   }
00066 
00067 /*
00068  *  Create.
00069  */
00070 
00071 Usecode_script::Usecode_script
00072   (
00073   Game_object *o,
00074   Usecode_value *cd   // May be NULL for empty script.
00075   ) : obj(o), code(cd), cnt(0), i(0), frame_index(0), no_halt(false),
00076       must_finish(false), delay(0)
00077   {
00078   if (!code)      // Empty?
00079     code = new Usecode_value(0, 0);
00080   else
00081     {
00082     cnt = code->get_array_size();
00083     if (!cnt)   // Not an array??  (This happens.)
00084       {   // Create with single element.
00085       code = new Usecode_value(1, code);
00086       cnt = 1;
00087       }
00088     }
00089   }
00090 
00091 /*
00092  *  Delete.
00093  */
00094 
00095 Usecode_script::~Usecode_script()
00096   {
00097   delete code;
00098   count--;
00099   if (next)
00100     next->prev = prev;
00101   if (prev)
00102     prev->next = next;
00103   else
00104     first = next;
00105   }
00106 
00107 /*
00108  *  Enter into the time-queue and our own chain.  Terminate existing
00109  *  scripts for this object unless 'dont_halt' is set.
00110  */
00111 
00112 void Usecode_script::start
00113   (
00114   long d      // Start after this many msecs.
00115   )
00116   {
00117   Game_window *gwin = Game_window::get_instance();
00118   int cnt = code->get_array_size();// Check initial elems.
00119   for (int i = 0; i < cnt; i++)
00120     {
00121     int opval0 = code->get_elem(i).get_int_value();
00122     if (opval0 == Ucscript::dont_halt)
00123       no_halt = true;
00124     else if (opval0 == Ucscript::finish)
00125       must_finish = true;
00126     else
00127       break;
00128     }
00129   if (!is_no_halt())    // If flag not set,
00130           // Remove other entries that aren't
00131           //   'no_halt'.
00132     Usecode_script::terminate(obj);
00133   count++;      // Keep track of total.
00134   next = first;     // Put in chain.
00135   prev = 0;
00136   if (first)
00137     first->prev = this;
00138   first = this;
00139 //++++ Messes up Moonshade Trial.
00140 //  gwin->get_tqueue()->add(d + Game::get_ticks(), this,
00141   gwin->get_tqueue()->add(d + SDL_GetTicks(), this,
00142           (long) gwin->get_usecode());
00143   }
00144 
00145 /*
00146  *  Set this script to halt.
00147  */
00148 
00149 void Usecode_script::halt
00150   (
00151   )
00152   {
00153   if (!no_halt)
00154     i = cnt;
00155   }
00156 
00157 /*
00158  *  Append instructions.
00159  */
00160 void Usecode_script::add(int v1)
00161   {
00162   code->append(&v1, 1);
00163   cnt++;
00164   }
00165 void Usecode_script::add(int v1, int v2)
00166   {
00167   int vals[2];
00168   vals[0] = v1;
00169   vals[1] = v2;
00170   code->append(vals, 2);
00171   cnt += 2;
00172   }
00173 void Usecode_script::add(int v1, const char *str)
00174   {
00175   int sz = code->get_array_size();
00176   code->resize(sz + 2);
00177   (*code)[sz] = v1;
00178   (*code)[sz + 1] = str;
00179   cnt += 2;
00180   }
00181 void Usecode_script::add(int *vals, int c)
00182   {
00183   code->append(vals, cnt);
00184   cnt += c;
00185   }
00186 
00187 /*
00188  *  Search list for one for a given item.
00189  *
00190  *  Output: ->Usecode_script if found, else 0.
00191  */
00192 
00193 Usecode_script *Usecode_script::find
00194   (
00195   Game_object *srch,
00196   Usecode_script *last_found  // Find next after this.
00197   )
00198   {
00199   Usecode_script *start = last_found ? last_found->next : first;
00200   for (Usecode_script *each = start; each; each = each->next)
00201     if (each->obj == srch)
00202       return each;  // Found it.
00203   return (0);
00204   }
00205 
00206 /*
00207  *  Terminate all scripts for a given object.
00208  */
00209 
00210 void Usecode_script::terminate
00211   (
00212   Game_object *obj
00213   )
00214   {
00215   Usecode_script *next = 0;
00216   for (Usecode_script *each = first; each; each = next)
00217     {
00218     next = each->next;  // Get next in case we delete 'each'.
00219     if (each->obj == obj)
00220       each->halt();
00221     }
00222   }     
00223 
00224 /*
00225  *  Remove all from global list (assuming they've already been cleared
00226  *  from the time queue).
00227  */
00228 
00229 void Usecode_script::clear
00230   (
00231   )
00232   {
00233   while (first)
00234     delete first;
00235   }
00236 
00237 /*
00238  *  Terminate all scripts for objects that are more than a given distance
00239  *  from a particular spot.
00240  */
00241 
00242 void Usecode_script::purge
00243   (
00244   Tile_coord spot,
00245   int dist      // In tiles.
00246   )
00247   {
00248   Usecode_script *next = 0;
00249   Game_window *gwin = Game_window::get_instance();
00250   Usecode_internal *usecode = static_cast<Usecode_internal*>(
00251           gwin->get_usecode());
00252   for (Usecode_script *each = first; each; each = next)
00253     {
00254     next = each->next;  // Get next in case we delete 'each'.
00255           // Only purge if not yet started.
00256     if (each->obj && !each->i &&
00257         each->obj->get_outermost()->get_tile().distance(
00258                 spot) > dist)
00259       {   // Force it to halt.
00260       each->no_halt = false;
00261       if (each->must_finish)
00262         {
00263         cout << "MUST finish this script" << endl;
00264         each->exec(usecode, true);
00265         }
00266       each->halt();
00267       }
00268     }
00269   }     
00270 
00271 inline void Usecode_script::activate_egg(Usecode_internal *usecode,
00272              Game_object *e)
00273 {
00274   if (!e || !e->is_egg())
00275     return;
00276   int type = ((Egg_object *) e)->get_type();
00277           // Guess:  Only certain types:
00278   if (type == Egg_object::monster || type == Egg_object::button ||
00279       type == Egg_object::missile)
00280     ((Egg_object *) e)->activate(
00281       usecode->gwin->get_main_actor(), true);
00282 }
00283 
00284 
00285 /*
00286  *  Execute an array of usecode, generally one instruction per tick.
00287  */
00288 
00289 void Usecode_script::handle_event
00290   (
00291   unsigned long curtime,    // Current time of day.
00292   long udata      // ->usecode machine.
00293   )
00294   {
00295   Usecode_internal *usecode = (Usecode_internal *) udata;
00296   int delay = exec(usecode, false);
00297   if (i < cnt)      // More to do?
00298     {
00299     usecode->gwin->get_tqueue()->add(curtime + delay, this, udata);
00300     return;
00301     }
00302 #if 0 /* ++++Might need this for Test of Love!! */
00303   if (count == 1 &&   // Last one?  GUESSING:
00304       objpos.tx != -1)    // And valid pos.
00305   {
00306     usecode->activate_cached(objpos);
00307   }
00308 #endif
00309   delete this;      // Hope this is safe.
00310   }
00311 
00312 /*
00313  *  Execute an array of usecode, generally one instruction per tick.
00314  *
00315  *  Output: Delay for next execution.
00316  */
00317 
00318 int Usecode_script::exec
00319   (
00320   Usecode_internal *usecode,
00321   bool finish     // If set, keep going to end.
00322   )
00323   {
00324   Game_window *gwin = usecode->gwin;
00325   int delay = gwin->get_std_delay();  // Start with default delay.
00326   bool do_another = true;     // Flag to keep going.
00327   int opcode;
00328           // If a 1 follows, keep going.
00329   for (; i < cnt && ((opcode = code->get_elem(i).get_int_value()) 
00330             == 0x1 || do_another); i++)
00331     {
00332     do_another = finish;
00333     switch (opcode)
00334       {
00335     case cont:    // Means keep going without painting.
00336       do_another = true;
00337       gwin->set_painted();  // Want to paint when done.
00338       break;
00339     case repeat:    // ?? 2 parms, 1st one < 0.
00340       {   // Loop(offset, cnt).
00341       Usecode_value& cntval = code->get_elem(i + 2);
00342       int cnt = cntval.get_int_value();
00343       if (cnt <= 0)
00344           // Done.
00345         i += 2;
00346       else
00347         { // Decr. and loop.
00348         cntval = Usecode_value(cnt - 1);
00349         Usecode_value& offval = code->get_elem(i + 1);
00350         i += offval.get_int_value() - 1;
00351         if (i < -1) // Before start?
00352           i = -1;
00353         do_another = true;
00354         }
00355       break;
00356       }
00357     case repeat2:   // Loop with 3 parms.???
00358       {   // Loop(offset, cnt1, cnt2?).
00359         //Guessing: loop cnt1 each round. use cnt2 as loop var.
00360         //This is necessary for loop nesting.
00361         //(used in mining machine, orb of the moons)
00362 
00363         // maybe cnt1 and cnt2 should be swapped... not sure
00364 
00365       do_another = true;
00366       Usecode_value& cntval = code->get_elem(i + 3);
00367       Usecode_value& origval = code->get_elem(i + 2);
00368       int cnt = cntval.get_int_value();
00369       int orig = origval.get_int_value();
00370       if (cnt > orig) { // ++++ First pass? Set to orig or not?
00371         cntval = origval;
00372         cnt = orig;
00373       }
00374       if (cnt <= 0) {
00375           // Done.
00376         i += 3;
00377         cntval = origval; // restore counter
00378       } else
00379         { // Decr. and loop.
00380         cntval = Usecode_value(cnt - 1);
00381         Usecode_value& offval = code->get_elem(i + 1);
00382         i += offval.get_int_value() - 1;
00383         }
00384       break;
00385       }
00386     case nop:   // Just a nop.
00387       break;
00388     case Ucscript::finish:  // Flag to finish if deleted.
00389       must_finish = true;
00390       do_another = true;
00391       break;
00392     case dont_halt:   // ?? Always appears first.
00393           // Maybe means "don't let
00394           //    intrinsic 5c stop it".
00395       no_halt = true; // PURE GUESS.
00396       do_another = true;
00397       break;
00398     case delay_ticks: // 1 parm.
00399       {   //   delay before next instruction.
00400       Usecode_value& delayval = code->get_elem(++i);
00401           // It's # of ticks.
00402       delay = gwin->get_std_delay()*delayval.get_int_value();
00403       break;    
00404       }
00405     case delay_hours: // 1 parm., game hours.
00406       {
00407       Usecode_value& delayval = code->get_elem(++i);
00408       delay = delayval.get_int_value();
00409           // Convert to real seconds.
00410       delay = (delay*3600)/time_factor;
00411       delay *= 1000;  // Then to msecs.
00412       break;
00413       }
00414 #if 0
00415     case finish:    // Quit if there's already scheduled
00416           //   code for item?
00417           // Or supercede the existing one?
00418       break;
00419 #endif
00420     case Ucscript::remove:  // Remove obj.
00421       usecode->remove_item(obj);
00422       break;
00423     case rise:    // (For flying carpet.
00424       {
00425       Tile_coord t = obj->get_tile();
00426       if (t.tz < 10)
00427         t.tz++;
00428       obj->move(t);
00429       break;
00430       }
00431     case descend:
00432       {
00433       Tile_coord t = obj->get_tile();
00434       if (t.tz > 0)
00435         t.tz--;
00436       obj->move(t);
00437       break;
00438       }
00439     case frame:   // Set frame.
00440       usecode->set_item_frame(obj, 
00441           code->get_elem(++i).get_int_value());
00442       break;
00443     case egg:   // Guessing:  activate egg.
00444       activate_egg(usecode, obj);
00445       break;
00446     case next_frame_max:  // Stop at last frame.
00447       {
00448       int nframes = obj->get_num_frames();
00449       if (obj->get_framenum() < nframes - 1)
00450         usecode->set_item_frame(obj,
00451               1+obj->get_framenum());
00452       break;
00453       }
00454     case next_frame:
00455       {
00456       int nframes = obj->get_num_frames();
00457       usecode->set_item_frame(obj, 
00458           (1 + obj->get_framenum())%nframes);
00459       break;
00460       }
00461     case prev_frame_min:
00462       if (obj->get_framenum() > 0)
00463         usecode->set_item_frame(obj, 
00464             obj->get_framenum() - 1);
00465       break;
00466     case prev_frame:
00467       {
00468       int nframes = obj->get_num_frames();
00469       int pframe = obj->get_framenum() - 1;
00470       usecode->set_item_frame(obj, 
00471             (pframe + nframes)%nframes);
00472       break;
00473       }
00474     case say:   // Say string.
00475       {
00476       Usecode_value& strval = code->get_elem(++i);
00477       Usecode_value objval(obj);
00478       usecode->item_say(objval, strval);
00479       break;
00480       }
00481     case Ucscript::step:  // Parm. is dir. (0-7).  0=north.
00482       {
00483           // Get dir.
00484       Usecode_value& val = code->get_elem(++i);
00485           // It may be 0x3x.
00486       step(usecode, val.get_int_value()&7);
00487       //+++++Might be a 2nd parm, diff in altitude.
00488       // ++++++++++Investigate.
00489       break;
00490       }
00491     case music:   // Unknown.
00492       {
00493       Usecode_value& val = code->get_elem(++i);
00494       Audio::get_ptr()->start_music(val.get_int_value(),
00495                   false);
00496       break;
00497       }
00498     case Ucscript::usecode: // Call?
00499       {
00500       Usecode_value& val = code->get_elem(++i);
00501       int fun = val.get_int_value();
00502           // Watch for eggs:
00503       Usecode_internal::Usecode_events ev = 
00504           Usecode_internal::internal_exec;
00505       if (obj && obj->is_egg() 
00506 #if 0
00507         //removed 20011226, breaks serpent gates in SI without SS -wjp
00508         && ((Egg_object *)obj)->get_type() == Egg_object::usecode
00509 #endif
00510         )
00511         ev = Usecode_internal::egg_proximity;
00512           // And for telekenesis spell fun:
00513       else if (fun == usecode->telekenesis_fun)
00514           {
00515           ev = Usecode_internal::double_click;
00516           usecode->telekenesis_fun = -1;
00517           }
00518       usecode->call_usecode(fun, obj, ev);
00519       break;
00520       }
00521     case Ucscript::usecode2:// Call(fun, eventid).
00522       {
00523       Usecode_value& val = code->get_elem(++i);
00524       int evid = code->get_elem(++i).get_int_value();
00525       usecode->call_usecode(val.get_int_value(), obj, 
00526         (Usecode_internal::Usecode_events) evid);
00527       break;
00528       }
00529     case speech:    // Play speech track.
00530       {
00531       Usecode_value& val = code->get_elem(++i);
00532       int track = val.get_int_value();
00533       if (track >= 0)
00534         Audio::get_ptr()->start_speech(track);
00535       }
00536     case sfx:   // Play sound effect!
00537       {
00538       Usecode_value& val = code->get_elem(++i);
00539       Audio::get_ptr()->play_sound_effect(
00540               val.get_int_value());
00541       break;
00542       }
00543     case face_dir:    // Parm. is dir. (0-7).  0=north.
00544       {
00545           // Look in that dir.
00546       Usecode_value& val = code->get_elem(++i);
00547           // It may be 0x3x.  Face dir?
00548       int dir = val.get_int_value()&7;
00549       Actor *npc = obj->as_actor();
00550       if (npc)
00551         npc->set_usecode_dir(dir);
00552       usecode->set_item_frame(obj, obj->get_dir_framenum(
00553         dir, obj->get_framenum()), 1, 1);
00554       frame_index = 0;// Reset walking frame index.
00555       break;
00556       }
00557     case hit:   // Hit(hps, ??).
00558       {
00559       Usecode_value hps = code->get_elem(++i);
00560       Usecode_value unk = code->get_elem(++i);
00561       Actor *act = usecode->as_actor(obj);
00562       if (act)  // ++++Should apply to any object.
00563         act->reduce_health(hps.get_int_value());
00564       break;
00565       }
00566     case resurrect:
00567       {
00568       Dead_body *body = (Dead_body *) obj;
00569       Actor *act = gwin->get_npc(body->get_live_npc_num());
00570       if (act)
00571         act->resurrect(body);
00572       break;
00573       }
00574     default:
00575           // Frames with dir.  U7-verified!
00576       if (opcode >= 0x61 && opcode <= 0x70)
00577         { // But don't show empty frames.
00578         Actor *npc = obj->as_actor();
00579         npc->clear_rest_time();
00580         int v = obj->get_dir_framenum(
00581           npc ? npc->get_usecode_dir() : 0, 
00582           opcode - 0x61);
00583         usecode->set_item_frame(obj, v, 1, 1);
00584         }
00585       else if (opcode >= 0x30 && opcode < 0x38)
00586         { // Step in dir. opcode&7.
00587         step(usecode, opcode&7);
00588         do_another = true;  // Guessing.
00589         }
00590       else
00591         {
00592               cout << "Und sched. opcode " << hex << 
00593           "0x" << setfill((char)0x30) << setw(2) 
00594           << opcode << std::dec << endl;
00595         do_another = true; // Don't let it delay us.
00596         }
00597       break;
00598       }
00599     }
00600   return delay;
00601   }
00602 
00603 /*
00604  *  Step in given direction.
00605  */
00606 
00607 void Usecode_script::step
00608   (
00609   Usecode_internal *usecode,
00610   int dir       // 0-7.
00611   )
00612   {
00613   int frame = obj->get_framenum();
00614   Barge_object *barge;
00615   Actor *act = usecode->as_actor(obj);
00616   if (act)
00617     {
00618     Frames_sequence *frames = act->get_frames(dir);
00619           // Get frame (updates frame_index).
00620     frame = frames->get_next(frame_index);
00621     Tile_coord tile = obj->get_tile().get_neighbor(dir);
00622     obj->step(tile, frame);
00623     }
00624   else if ((barge = obj->as_barge()) != 0)
00625     {
00626     for (int i = 0; i < 4; i++)
00627       {
00628       Tile_coord t = obj->get_tile().get_neighbor(
00629                   dir);
00630       obj->step(t, 0);
00631       }
00632     }
00633   }
00634 
00635 /*
00636  *  Save (serialize).
00637  *
00638  *  Output: Length written, or -1 if error.
00639  */
00640 
00641 int Usecode_script::save
00642   (
00643   unsigned char *buf,
00644   int buflen
00645   )
00646   {
00647           // Get delay to when due.
00648   long when = Game_window::get_instance()->get_tqueue()->find_delay(
00649               this, SDL_GetTicks());
00650   if (when < 0)
00651     return -1;
00652   uint8 *ptr = buf;
00653   Write2(ptr, cnt);   // # of values we'll store.
00654   Write2(ptr, i);     // Current spot.
00655   for (int j = 0; j < cnt; j++)
00656     {
00657     Usecode_value& val = code->get_elem(j);
00658     int len = val.save(ptr, buflen - (ptr - buf));
00659     if (len < 0)
00660       return -1;
00661     ptr += len;
00662     }
00663   if (buflen - (ptr - buf) < 8) // Enough room left?
00664     return -1;
00665   Write2(ptr, frame_index);
00666   Write2(ptr, no_halt ? 1 : 0);
00667   Write4(ptr, when);
00668   return (ptr - buf);
00669   }
00670 
00671 /*
00672  *  Restore (serialize).
00673  *
00674  *  Output: ->entry, which is also stored in our global chain, but is NOT
00675  *    added to the time queue yet.
00676  */
00677 
00678 Usecode_script *Usecode_script::restore
00679   (
00680   Game_object *item,    // Object this is executed for.
00681   unsigned char *buf,
00682   int buflen
00683   )
00684   {
00685   uint8 *ptr = buf;
00686   int cnt = Read2(ptr);   // Get # instructions.
00687   int curindex = Read2(ptr);  // Where it is.
00688           // Create empty array.
00689   Usecode_value *code = new Usecode_value(cnt, 0);
00690   for (int i = 0; i < cnt; i++)
00691     {
00692     Usecode_value& val = code->get_elem(i);
00693     if (!val.restore(ptr, buflen - (ptr - buf)))
00694       {
00695       delete code;
00696       return 0;
00697       }
00698     }
00699   if (buflen - (ptr - buf) < 8) // Enough room left?
00700     {
00701     delete code;
00702     return 0;
00703     }
00704   int frame_index = Read2(ptr);
00705   int no_halt = Read2(ptr);
00706   int delay = Read4(ptr);
00707   Usecode_script *scr =
00708     new Usecode_script(item, code, frame_index, no_halt, delay);
00709   scr->i = curindex;    // Set index.
00710   return scr;
00711   }
00712 
00713 /*
00714  *  Print for debugging.
00715  */
00716 
00717 void Usecode_script::print
00718   (
00719   std::ostream& out
00720   )
00721   {
00722   out << hex << "Obj = 0x" << setfill((char)0x30) << setw(2)
00723     << (void *) obj << ": " "(";
00724   for (int i = 0; i < cnt; i++)
00725     {
00726     if (i > 0)
00727       out << ", ";
00728     code->get_elem(i).print(out);
00729     }
00730   out <<") = ";
00731   out << std::dec;
00732   }

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