ucinternal.cc

Go to the documentation of this file.
00001 /*
00002  *  ucinternal.cc - Interpreter for usecode.
00003  *
00004  *  Copyright (C) 1999  Jeffrey S. Freedman
00005  *  Copyright (C) 2000-2002  The Exult Team
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020  */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #  include <config.h>
00024 #endif
00025 
00026 #ifndef ALPHA_LINUX_CXX
00027 #  include <cstdio>     /* Debugging.     */
00028 #  include <fstream>
00029 #  include <cstring>
00030 #  include <cstdlib>
00031 #endif
00032 
00033 #include <iomanip>
00034 
00035 #ifdef XWIN
00036 #include <csignal>
00037 #endif
00038 #include <algorithm>       // STL function things
00039 
00040 #include "Gump.h"
00041 #include "Gump_manager.h"
00042 #include "Text_gump.h"
00043 #include "Audio.h"
00044 #include "animate.h"
00045 #include "barge.h"
00046 #include "chunks.h"
00047 #include "conversation.h"
00048 #include "exult.h"
00049 #include "game.h"
00050 #include "gamewin.h"
00051 #include "gamemap.h"
00052 #include "keyring.h"
00053 #include "mouse.h"
00054 #include "schedule.h"
00055 #include "tqueue.h"
00056 #include "ucinternal.h"
00057 #include "ucsched.h"
00058 #include "useval.h"
00059 #include "utils.h"
00060 #include "vec.h"
00061 #include "actors.h"
00062 #include "egg.h"
00063 #include "actions.h"
00064 #include "stackframe.h"
00065 #include "ucfunction.h"
00066 #include "effects.h"
00067 #include "party.h"
00068 
00069 #if (defined(USE_EXULTSTUDIO) && defined(USECODE_DEBUGGER))
00070 #include "server.h"
00071 #include "servemsg.h"
00072 #include "debugmsg.h"
00073 #include "debugserver.h"
00074 #endif
00075 
00076 #ifndef UNDER_CE
00077 using std::cerr;
00078 using std::cout;
00079 using std::endl;
00080 using std::istream;
00081 using std::ifstream;
00082 using std::ofstream;
00083 using std::istream;
00084 using std::ostream;
00085 using std::exit;
00086 using std::ios;
00087 using std::dec;
00088 using std::hex;
00089 using std::memset;
00090 using std::setfill;
00091 using std::setw;
00092 using std::size_t;
00093 using std::string;
00094 using std::strcat;
00095 using std::strchr;
00096 using std::strcmp;
00097 using std::strcpy;
00098 using std::strlen;
00099 using std::vector;
00100 using std::ostream;
00101 #endif
00102 
00103 // External globals..
00104 
00105 extern bool intrinsic_trace;
00106 extern int usecode_trace;
00107 
00108 #if 0 && USECODE_DEBUGGER
00109 
00110 extern bool usecode_debugging;
00111 std::vector<int> intrinsic_breakpoints;
00112 
00113 void  initialise_usecode_debugger(void)
00114 {
00115   // Summon up the configuration file
00116 
00117   // List all the keys.
00118 
00119   // Render intrinsic names to numbers (unless already given as
00120   // a number (which might be hex. Convert from that.
00121 
00122   // push them all onto the list
00123 
00124 
00125 }
00126 
00127 #endif
00128 
00129 
00130 void Usecode_internal::stack_trace(ostream& out)
00131 {
00132   if (call_stack.empty())
00133     return;
00134 
00135   std::deque<Stack_frame*>::iterator iter = call_stack.begin();
00136 
00137   do {
00138     out << *(*iter) << endl;
00139     if ((*iter)->call_depth == 0)
00140       break;
00141     ++iter;
00142   } while (true);
00143 }
00144 
00145 bool Usecode_internal::call_function(int funcid,
00146                    int eventid,
00147                    Game_object *caller,
00148                    bool entrypoint, bool orig)
00149 {
00150   // locate function
00151   vector<Usecode_function*>& slot = funs[funcid/0x100];
00152   size_t index = funcid%0x100;
00153   Usecode_function *fun = index < slot.size() ? slot[index] : 0;
00154   if (!fun)
00155   {
00156 #ifdef DEBUG
00157     cout << "Usecode " << funcid << " not found." << endl;
00158 #endif
00159     return false;
00160   }
00161   if (orig)
00162     if (!(fun = fun->orig))
00163     {
00164 #ifdef DEBUG
00165       cout << "Original usecode " << funcid << " not found."
00166                 << endl;
00167 #endif
00168       return false;
00169     }
00170 
00171   int depth, oldstack, chain;
00172 
00173   if (entrypoint)
00174   {
00175     depth = 0;
00176     oldstack = 0;
00177     chain = Stack_frame::getCallChainID();
00178 
00179   } else {
00180     Stack_frame *parent = call_stack.front();
00181 
00182     // find new depth
00183     depth = parent->call_depth + 1;
00184 
00185     // find number of elements available to pop from stack (as arguments)
00186     oldstack = sp - parent->save_sp;
00187 
00188     chain = parent->call_chain;
00189     
00190     if (caller == 0)
00191       caller = parent->caller_item; // use parent's
00192   }
00193 
00194   Stack_frame *frame = new Stack_frame(fun, eventid, caller, chain, depth);
00195 
00196   while (frame->num_args > oldstack) // Not enough args pushed?
00197   {
00198     pushi(0); // add zeroes
00199     oldstack++;
00200   }
00201 
00202   // Store args in first num_args locals
00203   int i;
00204   for (i = 0; i < frame->num_args; i++)
00205   {
00206     Usecode_value val = pop();
00207     frame->locals[frame->num_args - i - 1] = val;
00208   }
00209 
00210   // save stack pointer
00211   frame->save_sp = sp;
00212 
00213   // add new stack frame to top of stack
00214   call_stack.push_front(frame);
00215 
00216 
00217 #ifdef DEBUG
00218   cout << "Running usecode " << hex << setfill((char)0x30) 
00219      << setw(4) << funcid << dec << setfill(' ') <<
00220     " (";
00221   for (i = 0; i < frame->num_args; i++)
00222   {
00223     if (i)
00224       cout << ", ";
00225     frame->locals[i].print(cout);
00226   }
00227   cout << ") with event " << eventid 
00228      << ", depth " << frame->call_depth << endl;
00229 #endif
00230 
00231   return true;
00232 }
00233 
00234 void Usecode_internal::previous_stack_frame()
00235 {
00236   // remove current frame from stack
00237   Stack_frame *frame = call_stack.front();
00238   call_stack.pop_front();
00239 
00240   // restore stack pointer
00241   sp = frame->save_sp;
00242 
00243   if (frame->call_depth == 0) {
00244     // this was the function called from 'the outside'
00245     // push a marker (NULL) for the interpreter onto the call stack,
00246     // so it knows it has to return instead of continuing
00247     // further up the call stack
00248     call_stack.push_front(0);
00249   }
00250 
00251   delete frame;
00252 }
00253 
00254 void Usecode_internal::return_from_function(Usecode_value& retval)
00255 {
00256 #ifdef DEBUG
00257   // store old function ID for debugging output
00258   int oldfunction = call_stack.front()->function->id;
00259 #endif
00260 
00261   // back up a stack frame
00262   previous_stack_frame();
00263 
00264   // push the return value
00265   push(retval);
00266 
00267 
00268 #ifdef DEBUG
00269   Stack_frame *parent_frame = call_stack.front();
00270 
00271   cout << "Returning (";
00272   retval.print(cout);
00273   cout << ") from usecode " << hex << setw(4) << 
00274       setfill((char)0x30) << oldfunction << dec << setfill(' ')
00275      << endl;
00276 
00277 
00278   if (parent_frame) {
00279     int newfunction = call_stack.front()->function->id;
00280 
00281     cout << "...back into usecode " << hex << setw(4) << 
00282       setfill((char)0x30) << newfunction << dec << setfill(' ') << endl;
00283   }
00284 #endif
00285 }
00286 
00287 void Usecode_internal::return_from_procedure()
00288 {
00289 #ifdef DEBUG
00290   // store old function ID for debugging output
00291   int oldfunction = call_stack.front()->function->id;
00292 #endif
00293 
00294   // back up a stack frame
00295   previous_stack_frame();
00296 
00297 
00298 #ifdef DEBUG
00299   Stack_frame *parent_frame = call_stack.front();
00300 
00301   cout << "Returning from usecode " << hex << setw(4) << 
00302       setfill((char)0x30) << oldfunction << dec << setfill(' ')
00303      << endl;
00304 
00305   if (parent_frame) {
00306     int newfunction = call_stack.front()->function->id;
00307 
00308     cout << "...back into usecode " << hex << setw(4) << 
00309       setfill((char)0x30) << newfunction << dec << setfill(' ') << endl;
00310   }
00311 #endif
00312 }
00313 
00314 void Usecode_internal::abort_function()
00315 {
00316 #ifdef DEBUG
00317   int functionid = call_stack.front()->function->id;
00318 
00319   cout << "Aborting from usecode " << hex << setw(4)
00320      << setfill((char)0x30) << functionid << dec << setfill(' ')
00321      << endl;
00322 #endif
00323 
00324   // clear the entire call stack up to the entry point
00325   while (call_stack.front() != 0)
00326     previous_stack_frame();
00327 }
00328 
00329 /*
00330  *  Append a string.
00331  */
00332 
00333 void Usecode_internal::append_string
00334   (
00335   const char *str
00336   )
00337   {
00338   if (!str)
00339     return;
00340           // Figure new length.
00341   int len = String ? strlen(String) : 0;
00342   len += strlen(str);
00343   char *newstr = new char[len + 1];
00344   if (String)
00345     {
00346     strcpy(newstr, String);
00347     delete [] String;
00348     String = strcat(newstr, str);
00349     }
00350   else
00351     String = strcpy(newstr, str);
00352   }
00353 
00354 // Push/pop stack.
00355 inline void Usecode_internal::push(Usecode_value& val)
00356 {
00357   *sp++ = val;
00358 }
00359 
00360 inline Usecode_value Usecode_internal::pop()
00361 { 
00362   if (sp <= stack)
00363     {   // Happens in SI #0x939
00364       cerr << "Stack underflow" << endl;
00365       return Usecode_value(0);
00366     }
00367   return *--sp; 
00368 }
00369 
00370 inline void Usecode_internal::pushref(Game_object *obj)
00371 {
00372   Usecode_value v(obj);
00373   push(v);
00374 } 
00375 
00376 inline void Usecode_internal::pushi(long val)   // Push/pop integers.
00377 {
00378   Usecode_value v(val);
00379   push(v);
00380 }
00381 
00382 inline int Usecode_internal::popi()
00383 {
00384   Usecode_value val = pop();
00385   return val.need_int_value();
00386 }
00387 
00388 // Push/pop strings.
00389 inline void Usecode_internal::pushs(char *s)
00390 {
00391   Usecode_value val(s);
00392   push(val);
00393 }
00394 
00395 /*
00396  *  Get a game object from an "itemref", which might be the actual
00397  *  pointer, or might be -(npc number).
00398  *
00399  *  Output: ->game object.
00400  */
00401 
00402 Game_object *Usecode_internal::get_item
00403   (
00404   Usecode_value& itemref
00405   )
00406   {
00407           // If array, take 1st element.
00408   Usecode_value& elemval = itemref.get_elem0();
00409 
00410   if (elemval.is_ptr())
00411     return elemval.get_ptr_value();
00412 
00413   long val = elemval.get_int_value();
00414   if (!val)
00415     return NULL;
00416   Game_object *obj = NULL;
00417   if (val == -356)    // Avatar.
00418     return gwin->get_main_actor();
00419   if (val < 0 && val > -356)
00420     obj = gwin->get_npc(-val);
00421   else if (val >= 0 && val < gwin->get_num_npcs()) {
00422     obj = gwin->get_npc(val);
00423     CERR("Warning: interpreting positive integer as NPCnum");
00424           // Special case:  palace guards.
00425   } else if (val >= 0 && val < 0x400)   // Looks like a shape #?
00426     {
00427     if (!itemref.is_array() &&
00428         caller_item && val == caller_item->get_shapenum())
00429       obj = caller_item;
00430     else
00431       return 0; // Can't be an object.
00432     }
00433   return obj;
00434   }
00435 
00436 /*
00437  *  Check for an actor.
00438  */
00439 
00440 Actor *Usecode_internal::as_actor
00441   (
00442   Game_object *obj
00443   )
00444   {
00445   if (!obj)
00446     return 0;
00447   return (obj->as_actor());
00448   }
00449 
00450 /*
00451  *  Get a position.
00452  */
00453 
00454 Tile_coord Usecode_internal::get_position
00455   (
00456   Usecode_value& itemval
00457   )
00458   {
00459   Game_object *obj;   // An object?
00460   if ((itemval.get_array_size() == 1 || !itemval.get_array_size()) && 
00461             (obj = get_item(itemval)))
00462       return obj->get_outermost()->get_tile();
00463   else if (itemval.get_array_size() == 3)
00464           // An array of coords.?
00465     return Tile_coord(itemval.get_elem(0).get_int_value(),
00466         itemval.get_elem(1).get_int_value(),
00467         itemval.get_elem(2).get_int_value());
00468   else if (itemval.get_array_size() == 4)
00469           // Result of click_on_item() with
00470           //  array = (null, tx, ty, tz)?
00471     return Tile_coord(itemval.get_elem(1).get_int_value(),
00472         itemval.get_elem(2).get_int_value(),
00473         itemval.get_elem(3).get_int_value());
00474   else        // Else assume caller_item.
00475     return caller_item->get_tile();
00476   }
00477 
00478 /*
00479  *  Make sure pending text has been seen.
00480  */
00481 
00482 void Usecode_internal::show_pending_text
00483   (
00484   )
00485   {
00486   if (book)     // Book mode?
00487     {
00488     int x, y;
00489     while (book->show_next_page() && 
00490         Get_click(x, y, Mouse::hand, 0, false, book))
00491       ;
00492     gwin->paint();
00493     }
00494           // Normal conversation:
00495   else if (conv->is_npc_text_pending())
00496     click_to_continue();
00497   }
00498 
00499 /*
00500  *  Show book or scroll text.
00501  */
00502 
00503 void Usecode_internal::show_book
00504   (
00505   )
00506   {
00507   char *str = String;
00508   book->add_text(str);
00509   delete [] String;
00510   String = 0;
00511   }
00512 
00513 /*
00514  *  Say the current string and empty it.
00515  */
00516 
00517 void Usecode_internal::say_string
00518   (
00519   )
00520   {
00521     //  user_choice = 0;    // Clear user's response.
00522   if (!String)
00523     return;
00524   if (book)     // Displaying a book?
00525     {
00526     show_book();
00527     return;
00528     }
00529   show_pending_text();    // Make sure prev. text was seen.
00530   char *str = String;
00531   while (*str)      // Look for stopping points ("~~").
00532     {
00533     if (*str == '*')  // Just gets an extra click.
00534       {
00535       click_to_continue();
00536       str++;
00537       continue;
00538       }
00539     char *eol = strchr(str, '~');
00540     if (!eol)   // Not found?
00541       {
00542       conv->show_npc_message(str);
00543       break;
00544       }
00545     *eol = 0;
00546     conv->show_npc_message(str);
00547     click_to_continue();
00548     str = eol + 1;
00549     if (*str == '~')
00550       str++;    // 2 in a row.
00551     }
00552   delete [] String;
00553   String = 0;
00554   }
00555 
00556 /*
00557  *  Stack error.
00558  */
00559 
00560 void Usecode_internal::stack_error
00561   (
00562   int under     // 1 if underflow.
00563   )
00564   {
00565   if (under)
00566     cerr << "Stack underflow." << endl;
00567   else
00568     cerr << "Stack overflow." << endl;
00569   exit(1);
00570   }
00571 
00572 /*
00573  *  Show an NPC's face.
00574  */
00575 
00576 void Usecode_internal::show_npc_face
00577   (
00578   Usecode_value& arg1,    // Shape (NPC #).
00579   Usecode_value& arg2,    // Frame.
00580   int slot      // 0, 1, or -1 to find free spot.
00581   )
00582   {
00583   show_pending_text();
00584   Actor *npc = as_actor(get_item(arg1));
00585   if (!npc)
00586     return;
00587   int shape = npc->get_face_shapenum();
00588   int frame = arg2.get_int_value();
00589   if (Game::get_game_type() == BLACK_GATE)
00590     {
00591     if (npc->get_npc_num() != -1) 
00592       npc->set_flag (Obj_flags::met);
00593     }
00594   else if (Game::get_game_type() == SERPENT_ISLE)
00595     {     // Special case: Nightmare Smith.
00596     if (npc->get_npc_num() == 296)
00597       shape = -1;
00598     }
00599   if (!conv->get_num_faces_on_screen())
00600     gwin->get_effects()->remove_text_effects();
00601   // Only non persistent
00602   if (gumpman->showing_gumps(true))
00603     {
00604     gumpman->close_all_gumps();
00605     gwin->set_all_dirty();
00606     init_conversation();  // jsf-Added 4/20/01 for SI-Lydia.
00607     }
00608   gwin->paint_dirty();
00609   conv->show_face(shape, frame, slot);
00610 //  user_choice = 0;    // Seems like a good idea.
00611 // Also seems to create a conversation bug in Test of Love :-(
00612 
00613   }
00614 
00615 /*
00616  *  Remove an NPC's face.
00617  */
00618 
00619 void Usecode_internal::remove_npc_face
00620   (
00621   Usecode_value& arg1   // Shape (NPC #).
00622   )
00623   {
00624   show_pending_text();
00625   Actor *npc = as_actor(get_item(arg1));
00626   if (!npc)
00627     return;
00628   int shape = npc->get_face_shapenum();
00629   conv->remove_face(shape);
00630   }
00631 
00632 /*
00633  *  Set an item's shape.
00634  */
00635 
00636 void Usecode_internal::set_item_shape
00637   (
00638   Usecode_value& item_arg,
00639   Usecode_value& shape_arg
00640   )
00641   {
00642   int shape = shape_arg.get_int_value();
00643   Game_object *item = get_item(item_arg);
00644   if (!item)
00645     return;
00646           // See if light turned on/off.
00647   int light_changed = item->get_info().is_light_source() !=
00648           ShapeID::get_info(shape).is_light_source();
00649   if (item->get_owner())    // Inside something?
00650     {
00651     item->get_owner()->change_member_shape(item, shape);
00652     if (light_changed)  // Maybe we should repaint all.
00653       gwin->paint();  // Repaint finds all lights.
00654     else
00655       {
00656       Gump *gump = gumpman->find_gump(item);
00657       if (gump)
00658         gump->paint();
00659       }
00660     return;
00661     }
00662           // Figure area to repaint.
00663 //  Rectangle rect = gwin->get_shape_rect(item);
00664   gwin->add_dirty(item);
00665           // Get chunk it's in.
00666   Map_chunk *chunk = item->get_chunk();
00667   chunk->remove(item);    // Remove and add to update cache.
00668   item->set_shape(shape);
00669   chunk->add(item);
00670   gwin->add_dirty(item);
00671 //  rect = gwin->get_shape_rect(item).add(rect);
00672 //  rect.enlarge(8);
00673 //  rect = gwin->clip_to_win(rect);
00674   if (light_changed)
00675     gwin->paint();    // Complete repaint refigures lights.
00676 //  else
00677 //    gwin->paint(rect);  // Not sure...
00678 //  gwin->show();     // Not sure if this is needed.
00679   }
00680 
00681 /*
00682  *  Set an item's frame.
00683  */
00684 
00685 void Usecode_internal::set_item_frame
00686   (
00687   Game_object *item,
00688   int frame,
00689   int check_empty,    // If 1, don't set empty frame.
00690   int set_rotated     // Set 'rotated' bit to one in 'frame'.
00691   )
00692   {
00693   if (!item)
00694     return;
00695           // Added 9/16/2001:
00696   if (!set_rotated)   // Leave bit alone?
00697     frame = (item->get_framenum()&32)|(frame&31);
00698   if (frame == item->get_framenum())
00699     return;     // Already set to that.
00700           // Check for empty frame.
00701   ShapeID sid(item->get_shapenum(), frame, item->get_shapefile());
00702   Shape_frame *shape = sid.get_shape();
00703   if (!shape || (check_empty && shape->is_empty()))
00704     return;
00705   // cout << "Set_item_frame: " << item->get_shapenum() 
00706   //        << ", " << frame << endl;
00707           // (Don't mess up rotated frames.)
00708   if ((frame&0xf) < item->get_num_frames())
00709     {
00710     if (item->get_owner())  // Inside a container?
00711       {
00712       item->set_frame(frame);
00713       Gump *gump = gumpman->find_gump(item);
00714       if (gump)
00715         gwin->set_all_dirty();
00716       }
00717     else
00718       item->change_frame(frame);
00719     }
00720   gwin->set_painted();    // Make sure paint gets done.
00721   }
00722 
00723 /*
00724  *  Set to repaint an object.
00725  */
00726 
00727 void Usecode_internal::add_dirty
00728   (
00729   Game_object *obj
00730   )
00731   {
00732   if (obj->get_owner())   // Inside a container?
00733     {     // Paint gump if open.
00734     Gump *gump = gumpman->find_gump(obj);
00735     if (gump)
00736       gwin->add_dirty(gump->get_shape_rect(obj));
00737     }
00738   else
00739     gwin->add_dirty(obj);
00740   }
00741 
00742 /*
00743  *  Remove an item from the world.
00744  */
00745 
00746 void Usecode_internal::remove_item
00747   (
00748   Game_object *obj
00749   )
00750   {
00751   if (!obj)
00752     return;
00753   if (!last_created.empty() && obj == last_created.back())
00754     last_created.pop_back();
00755   add_dirty(obj);
00756   obj->remove_this();
00757   }
00758 
00759 /*
00760  *  Return an array containing the party, with the Avatar first.
00761  */
00762 
00763 Usecode_value Usecode_internal::get_party
00764   (
00765   )
00766   {
00767   int cnt = partyman->get_count();
00768   Usecode_value arr(1 + cnt, 0);
00769           // Add avatar.
00770   Usecode_value aval(gwin->get_main_actor());
00771   arr.put_elem(0, aval);  
00772   int num_added = 1;
00773   for (int i = 0; i < cnt; i++)
00774     {
00775     Game_object *obj = gwin->get_npc(partyman->get_member(i));
00776     if (!obj)
00777       continue;
00778     Usecode_value val(obj);
00779     arr.put_elem(num_added++, val);
00780     }
00781   // cout << "Party:  "; arr.print(cout); cout << endl;
00782   return arr;
00783   }
00784 
00785 /*
00786  *  Put text near an item.
00787  */
00788 
00789 void Usecode_internal::item_say
00790   (
00791   Usecode_value& objval,
00792   Usecode_value& strval
00793   )
00794   {
00795   Game_object *obj = get_item(objval);
00796   const char *str = strval.get_str_value();
00797   if (obj && str && *str)
00798     {
00799     Effects_manager *eman = gwin->get_effects();
00800           // Added Nov01,01 to fix 'locate':
00801     eman->remove_text_effect(obj);
00802     eman->add_text(str, obj);
00803     }
00804   }
00805 
00806 /*
00807  *  Activate all cached-in usecode eggs near a given spot.
00808  */
00809 
00810 void Usecode_internal::activate_cached
00811   (
00812   Tile_coord pos
00813   )
00814   {
00815   if (Game::get_game_type() != BLACK_GATE)
00816     return;     // ++++Since we're not sure about it.
00817   const int dist = 16;
00818   Egg_vector vec;     // Find all usecode eggs.
00819   Game_object::find_nearby(vec, pos, 275, dist, 16, c_any_qual, 7);
00820   for (Egg_vector::const_iterator it = vec.begin(); it != vec.end(); 
00821                   ++it)
00822     {
00823     Egg_object *egg = *it;
00824     if (egg->get_criteria() == Egg_object::cached_in)
00825       egg->activate();
00826     }
00827   }
00828 
00829 /*
00830  *  For sorting up-to-down, right-to-left, and near-to-far:
00831  */
00832 class Object_reverse_sorter
00833   {
00834 public:
00835   bool operator()(const Game_object *o1, const Game_object *o2)
00836     {
00837     Tile_coord t1 = o1->get_tile(),
00838          t2 = o2->get_tile();
00839     if (t1.ty > t2.ty)
00840       return true;
00841     else if (t1.ty == t2.ty)
00842       {
00843       if (t1.tx > t2.tx)
00844         return true;
00845       else 
00846         return t1.tx == t2.tx && t1.tz > t2.tz;
00847       }
00848     else
00849       return false;
00850     }
00851   };
00852 
00853 /*
00854  *  Return an array of nearby objects.
00855  */
00856 
00857 Usecode_value Usecode_internal::find_nearby
00858   (
00859   Usecode_value& objval,    // Find them near this.
00860   Usecode_value& shapeval,  // Shape to find, or -1 for any,
00861           //  c_any_shapenum for any npc.
00862   Usecode_value& distval,   // Distance in tiles?
00863   Usecode_value& mval   // Some kind of mask?  Guessing:
00864           //   4 == party members only.
00865           //   8 == non-party NPC's only.
00866           //  16 == something with eggs???
00867           //  32 == monsters? invisible?
00868   )
00869   {
00870   Game_object_vector vec;     // Gets list.
00871 
00872   int shapenum;
00873 
00874   if (shapeval.is_array()) {
00875     // fixes 'lightning whip sacrifice' in Silver Seed
00876     shapenum = shapeval.get_elem(0).get_int_value();
00877     if (shapeval.get_array_size() > 1)
00878       cerr << "Calling find_nearby with an array > 1 !!!!"
00879          << endl;
00880   } else
00881     shapenum = shapeval.get_int_value();
00882 
00883     
00884           // It might be (tx, ty, tz).
00885   int arraysize = objval.get_array_size();
00886   if (arraysize == 4)   // Passed result of click_on_item.
00887     {
00888     Game_object::find_nearby(vec, 
00889       Tile_coord(objval.get_elem(1).get_int_value(),
00890            objval.get_elem(2).get_int_value(),
00891            objval.get_elem(3).get_int_value()),
00892       shapenum,
00893       distval.get_int_value(), mval.get_int_value());
00894     }
00895   else if (arraysize == 3 || arraysize == 5)
00896     {     // Coords(x,y,z) [qual, frame]
00897           // Qual is 4th if there.
00898     int qual = arraysize == 5 ? objval.get_elem(3).get_int_value()
00899               : c_any_qual;
00900           // Frame is 5th if there.
00901     int frnum = arraysize == 5 ? objval.get_elem(4).get_int_value()
00902               : c_any_framenum;
00903     Game_object::find_nearby(vec,
00904       Tile_coord(objval.get_elem(0).get_int_value(),
00905            objval.get_elem(1).get_int_value(),
00906            objval.get_elem(2).get_int_value()),
00907       shapenum,
00908       distval.get_int_value(), mval.get_int_value(), 
00909       qual, frnum);
00910     }
00911   else
00912     {
00913     Game_object *obj = get_item(objval);
00914     if (!obj)
00915       return Usecode_value(0, 0);
00916     obj = obj->get_outermost(); // Might be inside something.
00917     obj->find_nearby(vec, shapenum,
00918       distval.get_int_value(), mval.get_int_value());
00919     }
00920   if (vec.size() > 1)   // Sort right-left, near-far to fix
00921           //   SI/SS cask bug.
00922     std::sort(vec.begin(), vec.end(), Object_reverse_sorter());
00923   Usecode_value nearby(vec.size(), 0);  // Create return array.
00924   int i = 0;
00925   for (Game_object_vector::const_iterator it = vec.begin(); 
00926             it != vec.end(); ++it)
00927     {
00928     Game_object *each = *it;
00929     Usecode_value val(each);
00930     nearby.put_elem(i++, val);
00931     }
00932   return (nearby);
00933   }
00934 
00935 /*
00936  *  Look for a barge that an object is a part of, or on, using the same
00937  *  sort (right-left, front-back) as ::find_nearby().  If there are more
00938  *  than one beneath 'obj', the highest is returned.
00939  *
00940  *  Output: ->barge if found, else 0.
00941  */
00942 
00943 Barge_object *Get_barge
00944   (
00945   Game_object *obj
00946   )
00947   {
00948           // Check object itself.
00949   Barge_object *barge = obj->as_barge();
00950   if (barge)
00951     return barge;
00952   Game_object_vector vec;   // Find it within 20 tiles (egglike).
00953   obj->find_nearby(vec, 961, 20, 0x10);
00954   if (vec.size() > 1)   // Sort right-left, near-far.
00955     std::sort(vec.begin(), vec.end(), Object_reverse_sorter());
00956           // Object must be inside it.
00957   Tile_coord pos = obj->get_tile();
00958   Barge_object *best = 0;
00959   for (Game_object_vector::const_iterator it = vec.begin();
00960               it != vec.end(); it++)
00961     {
00962     barge = (*it)->as_barge();
00963     if (barge && barge->get_tile_footprint().has_point(
00964               pos.tx, pos.ty))
00965       {
00966       int lift = barge->get_lift();
00967       if (!best ||  // First qualifying?
00968           // First beneath obj.?
00969           (best->get_lift() > pos.tz && lift <= pos.tz) ||
00970           // Highest beneath?
00971           (lift <= pos.tz && lift > best->get_lift()))
00972         best = barge;
00973       }
00974     }
00975   return best;
00976   }
00977 
00978 /*
00979  *  Return object of given shape nearest given obj.
00980  */
00981 
00982 Usecode_value Usecode_internal::find_nearest
00983   (
00984   Usecode_value& objval,    // Find near this.
00985   Usecode_value& shapeval,  // Shape to find
00986   Usecode_value& distval    // Guessing it's distance.
00987   )
00988   {
00989   Game_object *obj = get_item(objval);
00990   if (!obj)
00991     return Usecode_value((Game_object*) NULL);
00992   Game_object_vector vec;     // Gets list.
00993   obj = obj->get_outermost(); // Might be inside something.
00994   int dist = distval.get_int_value();
00995   int shnum = shapeval.get_int_value();
00996           // Kludge for Test of Courage:
00997   if (frame->function->id == 0x70a && shnum == 0x9a && dist == 0)
00998     dist = 16;    // Mage may have wandered.
00999   obj->find_nearby(vec, shnum, dist, 0);
01000   Game_object *closest = 0;
01001   uint32 bestdist = 100000;// Distance-squared in tiles.
01002   Tile_coord t1 = obj->get_tile();
01003   for (Game_object_vector::const_iterator it = vec.begin(); 
01004               it != vec.end(); ++it)
01005     {
01006     Game_object *each = *it;
01007     Tile_coord t2 = each->get_tile();
01008     int dx = t1.tx - t2.tx, dy = t1.ty - t2.ty, dz = t1.tz - t2.tz;
01009     uint32 dist = dx*dx + dy*dy + dz*dz;
01010     if (dist < bestdist)
01011       {
01012       bestdist = dist;
01013       closest = each;
01014       }
01015     }
01016   return Usecode_value(closest);
01017   }
01018 
01019 /*
01020  *  Find the angle (0-7) from one object to another.
01021  */
01022 
01023 Usecode_value Usecode_internal::find_direction
01024   (
01025   Usecode_value& from,
01026   Usecode_value& to
01027   )
01028   {
01029   unsigned angle;     // Gets angle 0-7 (north - northwest)
01030   Tile_coord t1 = get_position(from);
01031   Tile_coord t2 = get_position(to);
01032           // Treat as cartesian coords.
01033   angle = (int) Get_direction(t1.ty - t2.ty, t2.tx - t1.tx);
01034   return Usecode_value(angle);
01035   }
01036 
01037 /*
01038  *  Count objects of a given shape in a container, or in the whole party.
01039  */
01040 
01041 Usecode_value Usecode_internal::count_objects
01042   (
01043   Usecode_value& objval,    // The container, or -357 for party.
01044   Usecode_value& shapeval,  // Object shape to count (c_any_shapenum=any).
01045   Usecode_value& qualval,   // Quality (c_any_qual=any).
01046   Usecode_value& frameval   // Frame (c_any_framenum=any).
01047   )
01048   {
01049   long oval = objval.get_int_value();
01050   int shapenum = shapeval.get_int_value();
01051   int qualnum = qualval.get_int_value();
01052   int framenum = frameval.get_int_value();
01053   if (oval != -357)
01054     {
01055     Game_object *obj = get_item(objval);
01056     return (!obj ? 0 : obj->count_objects(
01057           shapenum, qualnum, framenum));
01058     }
01059           // Look through whole party.
01060   Usecode_value party = get_party();
01061   int cnt = party.get_array_size();
01062   int total = 0;
01063   for (int i = 0; i < cnt; i++)
01064     {
01065     Game_object *obj = get_item(party.get_elem(i));
01066     if (obj)
01067       total += obj->count_objects(shapenum, qualnum, 
01068                 framenum);
01069     }
01070   return (total);
01071   }
01072 
01073 /*
01074  *  Get objects of a given shape in a container.
01075  */
01076 
01077 Usecode_value Usecode_internal::get_objects
01078   (
01079   Usecode_value& objval,    // The container.
01080   Usecode_value& shapeval,  // Object shape to get or c_any_shapenum for any.
01081   Usecode_value& qualval,   // Quality (c_any_qual=any).
01082   Usecode_value& frameval   // Frame (c_any_framenum=any).
01083   )
01084   {
01085   Game_object *obj = get_item(objval);
01086   if (!obj)
01087     return Usecode_value((Game_object*) NULL);
01088   int shapenum = shapeval.get_int_value();
01089   int framenum = frameval.get_int_value();
01090   int qual = qualval.get_int_value();
01091   Game_object_vector vec;     // Gets list.
01092   obj->get_objects(vec, shapenum, qual, framenum);
01093 
01094 //  cout << "Container objects found:  " << cnt << << endl;
01095   Usecode_value within(vec.size(), 0);  // Create return array.
01096   int i = 0;
01097   for (Game_object_vector::const_iterator it = vec.begin(); it != vec.end(); ++it)
01098     {
01099     Game_object *each = *it;
01100     Usecode_value val(each);
01101     within.put_elem(i++, val);
01102     }
01103   return (within);
01104   }
01105 
01106 /*
01107  *  Remove a quantity of an item from the party.
01108  *
01109  *  Output: 1 (or the object) if successful, else 0.
01110  */
01111 
01112 Usecode_value Usecode_internal::remove_party_items
01113   (
01114   Usecode_value& quantval,  // Quantity to remove.
01115   Usecode_value& shapeval,  // Shape.
01116   Usecode_value& qualval,   // Quality??
01117   Usecode_value& frameval,  // Frame.
01118   Usecode_value& flagval    // Flag??
01119   )
01120   {
01121   int quantity = quantval.need_int_value();
01122   int shapenum = shapeval.get_int_value();
01123   int framenum = frameval.get_int_value();
01124   int quality = qualval.get_int_value();
01125   Usecode_value party = get_party();
01126   int cnt = party.get_array_size();
01127   if (quantity == -359 && Game::get_game_type() == SERPENT_ISLE)
01128   {       // Special case. (Check party.)
01129     Game_object *obj = 0;
01130     for (int i = 0; i < cnt && !obj; i++)
01131       {
01132       Game_object *actor = get_item(party.get_elem(i));
01133       if (actor)
01134         obj = actor->find_item(shapenum, quality, 
01135                 framenum);
01136       }
01137     if (!obj)
01138       return Usecode_value(0);
01139 
01140     // Problem: we need to really delete this object, but
01141     // it also has to remain long enough to be processed by the
01142     // calling usecode function...
01143     // for now: use temp_to_be_deleted to store the object and
01144     // delete it afterwards (end of Usecode_internal::run)
01145     temp_to_be_deleted = obj;
01146     obj->remove_this(1);
01147     return Usecode_value(obj);
01148   }
01149   Usecode_value all(-357);  // See if they exist.
01150   Usecode_value avail = count_objects(all, shapeval, qualval, frameval);
01151   if (avail.get_int_value() < quantity)
01152     return Usecode_value(0);
01153           // Look through whole party.
01154   for (int i = 0; i < cnt && quantity > 0; i++)
01155     {
01156     Game_object *obj = get_item(party.get_elem(i));
01157     if (obj)
01158       quantity = obj->remove_quantity(quantity, shapenum,
01159               quality, framenum);
01160     }
01161   return Usecode_value(quantity == 0);
01162   }
01163 
01164 /*
01165  *  Add a quantity of an item to the party.
01166  *
01167  *  Output: List of members who got objects.
01168  */
01169 
01170 Usecode_value Usecode_internal::add_party_items
01171   (
01172   Usecode_value& quantval,  // Quantity to add.
01173   Usecode_value& shapeval,  // Shape.
01174   Usecode_value& qualval,   // Quality.
01175   Usecode_value& frameval,  // Frame.
01176   Usecode_value& flagval    // Flag??
01177   )
01178   {
01179   int quantity = quantval.get_int_value();
01180           // ++++++First see if there's room.
01181   int shapenum = shapeval.get_int_value();
01182   int framenum = frameval.get_int_value();
01183   unsigned int quality = (unsigned int) qualval.get_int_value();
01184           // Look through whole party.
01185   Usecode_value party = get_party();
01186   int cnt = party.get_array_size();
01187   Usecode_value result(0, 0); // Start with empty array.
01188   for (int i = 0; i < cnt && quantity > 0; i++)
01189     {
01190     Game_object *obj = get_item(party.get_elem(i));
01191     if (!obj)
01192       continue;
01193     int prev = quantity;
01194     quantity = obj->add_quantity(quantity, shapenum,
01195               quality, framenum);
01196     if (quantity < prev)  // Added to this NPC.
01197       result.concat(party.get_elem(i));
01198     }
01199   if (GAME_BG)      // Black gate?  Just return result.
01200     return result;
01201   int todo = quantity;    // SI:  Put remaining on the ground.
01202   if (framenum == c_any_framenum)
01203     framenum = 0;
01204   while (todo > 0)
01205     {
01206     Tile_coord pos = Map_chunk::find_spot(
01207       gwin->get_main_actor()->get_tile(), 3,
01208         shapenum, framenum, 2);
01209     if (pos.tx == -1) // Hope this rarely happens.
01210       break;
01211     Shape_info& info = ShapeID::get_info(shapenum);
01212           // Create and place.
01213     Game_object *newobj = gmap->create_ireg_object(
01214           info, shapenum, framenum, 0, 0, 0);
01215     if (quality != c_any_qual)
01216       newobj->set_quality(quality); // set quality
01217     newobj->set_flag(Obj_flags::okay_to_take);
01218     newobj->move(pos);
01219     todo--;
01220     if (todo > 0)   // Create quantity if possible.
01221       todo = newobj->modify_quantity(todo);
01222     }
01223           // SI?  Append # left on ground.
01224   Usecode_value ground(quantity - todo);
01225   result.concat(ground);
01226   return result;
01227   }
01228 
01229 /*
01230  *  Add a quantity of an item to a container
01231  *
01232  *  Output: Num created
01233  */
01234 
01235 Usecode_value Usecode_internal::add_cont_items
01236   (
01237   Usecode_value& container, // What do we add to
01238   Usecode_value& quantval,  // Quantity to add.
01239   Usecode_value& shapeval,  // Shape.
01240   Usecode_value& qualval,   // Quality.
01241   Usecode_value& frameval,  // Frame.
01242   Usecode_value& flagval    // Flag??
01243   )
01244   {
01245   int quantity = quantval.get_int_value();
01246   int shapenum = shapeval.get_int_value();
01247   int framenum = frameval.get_int_value();
01248   unsigned int quality = (unsigned int) qualval.get_int_value();
01249 
01250   Game_object *obj = get_item(container);
01251   if (obj) return Usecode_value (obj->add_quantity(quantity, shapenum, quality, framenum));
01252   return Usecode_value(0);
01253   }
01254 
01255 /*
01256  *  Remove a quantity of an item to a container
01257  *
01258  *  Output: Num removed
01259  */
01260 
01261 Usecode_value Usecode_internal::remove_cont_items
01262   (
01263   Usecode_value& container, // What do we add to
01264   Usecode_value& quantval,  // Quantity to add.
01265   Usecode_value& shapeval,  // Shape.
01266   Usecode_value& qualval,   // Quality.
01267   Usecode_value& frameval,  // Frame.
01268   Usecode_value& flagval    // Flag??
01269   )
01270   {
01271   int quantity = quantval.get_int_value();
01272   int shapenum = shapeval.get_int_value();
01273   int framenum = frameval.get_int_value();
01274   unsigned int quality = (unsigned int) qualval.get_int_value();
01275 
01276   Game_object *obj = get_item(container);
01277   if (obj) return Usecode_value (quantity - obj->remove_quantity(quantity, shapenum, quality, framenum));
01278   return Usecode_value(0);
01279   }
01280 
01281 
01282 /*
01283  *  Have an NPC walk somewhere and then execute usecode.
01284  *
01285  *  Output: 1 if successful, else 0.
01286  */
01287 
01288 int Usecode_internal::path_run_usecode
01289   (
01290   Usecode_value& npcval,    // # or ref.
01291   Usecode_value& locval,    // Where to walk to.
01292   Usecode_value& useval,    // Usecode #.
01293   Usecode_value& itemval,   // Use as itemref in Usecode fun.
01294   Usecode_value& eventval,  // Eventid.
01295   int find_free,      // Not sure.  For SI.  
01296   int always      // Always run function, even if failed.
01297   )
01298   {
01299   Actor *npc = as_actor(get_item(npcval));
01300   if (!npc)
01301     return 0;
01302   path_npc = npc;
01303   int usefun = useval.get_elem0().get_int_value();
01304   Game_object *obj = get_item(itemval);
01305   int sz = locval.get_array_size();
01306   if (!npc || sz < 2)
01307     {
01308     CERR("Path_run_usecode: bad inputs");
01309     return 0;
01310     }
01311   Tile_coord src = npc->get_tile();
01312   Tile_coord dest(locval.get_elem(0).get_int_value(),
01313       locval.get_elem(1).get_int_value(),
01314       sz == 3 ? locval.get_elem(2).get_int_value() : 0);
01315   if (dest.tz < 0)    // ++++Don't understand this.
01316     dest.tz = 0;
01317   if (find_free)
01318     {
01319     /* Now works with SI lightning platform */
01320           // Allow rise of 3 (for SI lightning).
01321     Tile_coord d = Map_chunk::find_spot(dest, 3, npc, 3);
01322     if (d.tx == -1)   // No?  Try at source level.
01323       d = Map_chunk::find_spot(
01324         Tile_coord(dest.tx, dest.ty, src.tz), 3, npc,
01325                   0);
01326     if (d.tx != -1)   // Found it?
01327       dest = d;
01328     if (usefun == 0x60a &&  // ++++Added 7/21/01 to fix Iron
01329         src.distance(dest) <= 1)
01330       return 1; // Maiden loop in SI.  Kludge+++++++
01331     }
01332   if (!obj)     // Just skip the usecode part.
01333     return npc->walk_path_to_tile(dest, gwin->get_std_delay(), 0);
01334           // Walk there and execute.
01335   If_else_path_actor_action *action = 
01336     new If_else_path_actor_action(npc, dest,
01337         new Usecode_actor_action(usefun, obj, 
01338             eventval.get_int_value()));
01339   if (always)     // Set failure to same thing.
01340     action->set_failure(
01341         new Usecode_actor_action(usefun, obj, 
01342             eventval.get_int_value()));
01343   npc->set_action(action);  // Get into time queue.
01344   npc->start(gwin->get_std_delay(), 0);
01345   return !action->done_and_failed();
01346 }
01347 
01348 /*
01349  *  Schedule a script.
01350  */
01351 
01352 void Usecode_internal::create_script
01353   (
01354   Usecode_value& objval,
01355   Usecode_value& codeval,
01356   long delay      // Delay from current time.
01357   )
01358   {
01359   Game_object *obj = get_item(objval);
01360           // Pure kludge for SI wells:
01361   if (objval.get_array_size() == 2 && 
01362       Game::get_game_type() == SERPENT_ISLE &&
01363       obj && obj->get_shapenum() == 470 && obj->get_lift() == 0)
01364     {     // We want the TOP of the well.
01365     Usecode_value v2 = objval.get_elem(1);
01366     Game_object *o2 = get_item(v2);
01367     if (o2->get_shapenum() == obj->get_shapenum() && 
01368         o2->get_lift() == 2)
01369       {
01370       objval = v2;
01371       obj = o2;
01372       }
01373     }
01374   if (!obj)
01375     {
01376     cerr << "Can't create script for NULL object" << endl;
01377     return;
01378     }
01379           // ++++Better to 'steal' array; this
01380           //   ends up making a copy.
01381   Usecode_value *code = new Usecode_value(codeval);
01382   Usecode_script *script = new Usecode_script(obj, code);
01383   script->start(delay);
01384   }
01385 
01386 /*
01387  *  Report unhandled intrinsic.
01388  */
01389 
01390 static void Usecode_Trace
01391   (
01392   const char *name,
01393   int intrinsic,
01394   int num_parms,
01395   Usecode_value parms[12]
01396   )
01397   {
01398   cout << hex << "    [0x" << setfill((char)0x30) << setw(2)
01399     << intrinsic << "]: " << name << "(";
01400   for (int i = 0; i < num_parms; i++)
01401     {
01402     parms[i].print(cout);
01403     if(i!=num_parms-1)
01404       cout << ", ";
01405     }
01406   cout <<") = ";
01407   cout << dec;
01408   }
01409 
01410 static  void  Usecode_TraceReturn(Usecode_value &v)
01411 {
01412   v.print(cout);
01413   cout << dec << endl;
01414 }
01415 
01416 #if 0 /* Not used at the moment. */
01417 static void Unhandled
01418   (
01419   int intrinsic,
01420   int num_parms,
01421   Usecode_value parms[12]
01422   )
01423   {
01424   Usecode_Trace("UNKNOWN",intrinsic,num_parms,parms);
01425   }
01426 #endif
01427 
01428 Usecode_value no_ret;
01429 
01430 Usecode_value Usecode_internal::Execute_Intrinsic(UsecodeIntrinsicFn func,const char *name,int event,int intrinsic,int num_parms,Usecode_value parms[12])
01431 {
01432 #ifdef XWIN
01433 #if 0 && USECODE_DEBUGGER
01434   if(usecode_debugging)
01435     {
01436     // Examine the list of intrinsics for function breakpoints.
01437     if(std::find(intrinsic_breakpoints.begin(),intrinsic_breakpoints.end(),intrinsic)!=intrinsic_breakpoints.end())
01438       {
01439       raise(SIGIO); // Breakpoint
01440       }
01441     }
01442 #endif
01443 #endif
01444 #ifdef DEBUG
01445   if(intrinsic_trace)
01446     {
01447     Usecode_Trace(name,intrinsic,num_parms,parms);
01448     cout.flush();
01449     Usecode_value u=((*this).*func)(event,intrinsic,num_parms,parms);
01450     Usecode_TraceReturn(u);
01451     return (u);
01452     }
01453   else
01454 #endif
01455     return ((*this).*func)(event,intrinsic,num_parms,parms);
01456 }
01457 
01458 
01459 typedef Usecode_value (Usecode_internal::*UsecodeIntrinsicFn)(int event,int intrinsic,int num_parms,Usecode_value parms[12]);
01460 
01461 // missing from mingw32 header files, so included manually
01462 #ifndef __STRING
01463 #if defined __STDC__ && __STDC__
01464 #define __STRING(x) #x
01465 #else
01466 #define __STRING(x) "x"
01467 #endif
01468 #endif
01469 
01470 #define USECODE_INTRINSIC_PTR(NAME) { &Usecode_internal::UI_ ## NAME, __STRING(NAME) }
01471 
01472 struct Usecode_internal::IntrinsicTableEntry
01473     Usecode_internal::intrinsic_table[]=
01474   {
01475 #include "bgintrinsics.h"
01476   };
01477 
01478 // Serpent Isle Intrinsic Function Tablee
01479 // It's different to the Black Gate one.
01480 struct Usecode_internal::IntrinsicTableEntry
01481     Usecode_internal::serpent_table[]=
01482   {
01483 #include "siintrinsics.h"
01484   };
01485 
01486 
01487 int max_bundled_intrinsics=0xff;  // Index of the last intrinsic in this table
01488 /*
01489  *  Call an intrinsic function.
01490  */
01491 
01492 Usecode_value Usecode_internal::call_intrinsic
01493   (
01494   int event,      // Event type.
01495   int intrinsic,      // The ID.
01496   int num_parms     // # parms on stack.
01497   )
01498   {
01499   Usecode_value parms[12];  // Get parms.
01500   for (int i = 0; i < num_parms; i++)
01501     {
01502     Usecode_value val = pop();
01503     parms[i] = val;
01504     }
01505   if (intrinsic<=max_bundled_intrinsics)
01506     {
01507     struct Usecode_internal::IntrinsicTableEntry *table_entry;
01508     
01509     if (Game::get_game_type() == SERPENT_ISLE)
01510       table_entry = serpent_table+intrinsic;
01511     else
01512       table_entry = intrinsic_table+intrinsic;
01513     UsecodeIntrinsicFn func=(*table_entry).func;
01514     const char *name=(*table_entry).name;
01515     return Execute_Intrinsic(func,name,event,intrinsic,
01516               num_parms,parms);
01517     }
01518   return(no_ret);
01519   }
01520 
01521 /*
01522  *  Wait for user to click inside a conversation.
01523  */
01524 
01525 void Usecode_internal::click_to_continue
01526   (
01527   )
01528   {
01529   int xx, yy;
01530   char c;
01531   if (!gwin->get_pal()->is_faded_out())// If black screen, skip!
01532     {
01533     gwin->paint();    // Repaint scenery.
01534     Get_click(xx, yy, Mouse::hand, &c, false, conv);
01535     }
01536   conv->clear_text_pending();
01537   //  user_choice = 0;    // Clear it.
01538   }
01539 
01540 /*
01541  *  Set book/scroll to display.
01542  */
01543 
01544 void Usecode_internal::set_book
01545   (
01546   Text_gump *b      // Book/scroll.
01547   )
01548   {
01549   delete book;
01550   book = b;
01551   }
01552 
01553 /*
01554  *  Get user's choice from among the possible responses.
01555  *
01556  *  Output: ->user choice string.
01557  *    0 if no possible choices or user quit.
01558  */
01559 
01560 const char *Usecode_internal::get_user_choice
01561   (
01562   )
01563   {
01564   if (!conv->get_num_answers())
01565     return (0);   // This does happen (Emps-honey).
01566 
01567   //  if (!user_choice)   // May have already been done.
01568   // (breaks conversation with Cyclops on Dagger Isle ('foul magic' option))
01569 
01570   get_user_choice_num();
01571   return (user_choice);
01572   }
01573 
01574 /*
01575  *  Get user's choice from among the possible responses.
01576  *
01577  *  Output: User choice is set, with choice # returned.
01578  *    -1 if no possible choices.
01579  */
01580 
01581 int Usecode_internal::get_user_choice_num
01582   (
01583   )
01584   {
01585   user_choice = 0;
01586   conv->show_avatar_choices();
01587   int x, y;     // Get click.
01588   int choice_num;
01589   do
01590     {
01591     char chr;   // Allow '1', '2', etc.
01592     gwin->paint();    // Paint scenery.
01593     int result=Get_click(x, y, Mouse::hand, &chr, false, conv);
01594     if (result<=0) {  // ESC pressed, select 'bye' if poss.
01595       choice_num = conv->locate_answer("bye");
01596     } else if (chr) {   // key pressed
01597       if (chr>='1' && chr <='0'+conv->get_num_answers()) {
01598         choice_num = chr - '1';
01599       } else
01600         choice_num = -1;  //invalid key
01601     } else
01602       choice_num = conv->conversation_choice(x, y);
01603     }
01604           // Wait for valid choice.
01605   while (choice_num  < 0 || choice_num >= conv->get_num_answers());
01606 
01607   conv->clear_avatar_choices();
01608           // Store ->answer string.
01609   user_choice = conv->get_answer(choice_num);
01610   return (choice_num);    // Return choice #.
01611   }
01612 
01613 /*
01614  *  Create for the outside world.
01615  */
01616 
01617 Usecode_machine *Usecode_machine::create
01618   (
01619   )
01620   {
01621   return new Usecode_internal();
01622   }
01623 
01624 /*
01625  *  Create machine from a 'usecode' file.
01626  */
01627 
01628 Usecode_internal::Usecode_internal
01629   (
01630   ) : Usecode_machine(),
01631       book(0), caller_item(0),
01632       path_npc(0), user_choice(0), 
01633       saved_pos(-1, -1, -1),
01634       String(0), stack(new Usecode_value[1024]), intercept_item(0),
01635     temp_to_be_deleted(0), telekenesis_fun(-1),
01636     modified_map(false)
01637 #ifdef USECODE_DEBUGGER
01638     , on_breakpoint(false)
01639 #endif
01640   {
01641           // Clear timers.
01642   memset((char *) &timers[0], 0, sizeof(timers));
01643   sp = stack;
01644   ifstream file;                // Read in usecode.
01645   try
01646     {
01647           U7open(file, USECODE);
01648     read_usecode(file);
01649     file.close();
01650     }
01651   catch(const file_exception & f)
01652     {
01653     if (!Game::is_editing())  // Ok if map-editing.
01654       throw f;
01655     std::cerr << "Warning (map-editing): Couldn't open '" << 
01656               USECODE << "'" << endl;
01657     }
01658 
01659           // Get custom usecode functions.
01660   if (is_system_path_defined("<PATCH>") && U7exists(PATCH_USECODE))
01661     {
01662     U7open(file, PATCH_USECODE);
01663     read_usecode(file, true);
01664     file.close();
01665     }
01666 
01667   //  set_breakpoint();
01668   }
01669 
01670 /*
01671  *  Read in usecode functions.  These may override previously read
01672  *  functions.
01673  */
01674 
01675 void Usecode_internal::read_usecode
01676   (
01677   istream &file,
01678   bool patch      // True if reading from 'patch'.
01679   )
01680   {
01681   file.seekg(0, ios::end);
01682   int size = file.tellg();  // Get file size.
01683   file.seekg(0);
01684           // Read in all the functions.
01685   while (file.tellg() < size)
01686     {
01687     Usecode_function *fun = new Usecode_function(file);
01688     Exult_vector<Usecode_function *> & vec = funs[fun->id/0x100];
01689     int i = fun->id%0x100;
01690     if (i < vec.size() && vec[i])
01691       {   // Already have one there.
01692       if (patch)  // Patching?
01693         {
01694         if (vec[i]->orig)
01695           { // Patching a patch.
01696           fun->orig = vec[i]->orig;
01697           delete vec[i];
01698           }
01699         else    // Patching fun. from static.
01700           fun->orig = vec[i];
01701         }
01702       else
01703         {
01704         delete vec[i]->orig;
01705         delete vec[i];
01706         }
01707       }
01708     vec.put(i, fun);
01709     }
01710   }
01711 
01712 /*
01713  *  Delete.
01714  */
01715 
01716 Usecode_internal::~Usecode_internal
01717   (
01718   )
01719   {
01720   delete [] stack;
01721   delete [] String;
01722   int num_slots = sizeof(funs)/sizeof(funs[0]);
01723   for (int i = 0; i < num_slots; i++)
01724     {
01725     vector<Usecode_function*>& slot = funs[i];
01726     int cnt = slot.size();
01727     for (int j = 0; j < cnt; j++)
01728       delete slot[j];
01729     }
01730   delete book;
01731   }
01732 
01733 #ifdef DEBUG
01734 int debug = 2;        // 2 for more stuff.
01735 static int ucbp_fun = -1, ucbp_ip = -1; // Breakpoint.
01736 void Setbreak(int fun, int ip)
01737   { ucbp_fun = fun; ucbp_ip = ip; }
01738 void Clearbreak()
01739   { ucbp_fun = ucbp_ip = -1; }
01740 #endif
01741 
01742 
01743 #define CERR_CURRENT_IP()\
01744   cerr << " (at function = " << hex << setw(4) << setfill('0')\
01745      << frame->function->id << ", ip = " \
01746      << current_IP << dec << setfill(' ') << ")" << endl
01747 
01748 #define LOCAL_VAR_ERROR(x)\
01749   cerr << "Local variable #" << (x) << " out of range!";\
01750   CERR_CURRENT_IP()
01751 
01752 #define DATA_SEGMENT_ERROR()\
01753   cerr << "Data pointer out of range!";\
01754   CERR_CURRENT_IP()
01755 
01756 #define EXTERN_ERROR()\
01757   cerr << "Extern offset out of range!";\
01758   CERR_CURRENT_IP()
01759 
01760 #define FLAG_ERROR(x)\
01761   cerr << "Global flag #" << (x) << " out of range!";\
01762   CERR_CURRENT_IP()
01763 
01764 /*
01765  *  The main usecode interpreter
01766  * 
01767  *  Output:
01768  */
01769 
01770 int Usecode_internal::run()
01771 {
01772   bool aborted = false;
01773   bool initializing_loop = false;
01774 
01775   while (frame = call_stack.front())
01776   {
01777     int num_locals = frame->num_vars + frame->num_args;
01778     int offset;
01779     int sval;
01780 
01781     bool frame_changed = false;
01782 
01783     // set some variables for use in other member functions
01784     caller_item = frame->caller_item;
01785 
01786     /*
01787      *  Main loop.
01788      */
01789     while (!frame_changed)
01790     {
01791 
01792 
01793       if ((frame->ip >= frame->endp) ||
01794         (frame->ip < frame->code))
01795       {
01796         cerr << "Usecode: jumped outside of code segment of "
01797            << "function " << hex << setw(4) << setfill('0')
01798            << frame->function->id << dec << setfill(' ')
01799            << " ! Aborting." << endl;
01800 
01801         abort_function();
01802         frame_changed = true;
01803         continue;
01804       }
01805 
01806       int current_IP = frame->ip - frame->code;
01807 
01808       int opcode = *(frame->ip);
01809 
01810       if (frame->ip + get_opcode_length(opcode) > frame->endp) {
01811         cerr << "Operands lie outside of code segment. ";
01812         CERR_CURRENT_IP();
01813         continue;
01814       }
01815 
01816 
01817 #ifdef DEBUG
01818       if (usecode_trace == 2) {
01819         uc_trace_disasm(frame);
01820       } 
01821 #endif
01822 
01823 #ifdef USECODE_DEBUGGER
01824       // check breakpoints
01825 
01826       int bp = breakpoints.check(frame);
01827       if (bp != -1)
01828       {
01829         // we hit a breakpoint
01830 
01831         // allow handling extra debugging messages
01832         on_breakpoint = true;
01833 
01834         cout << "On breakpoint" << endl;
01835 
01836         // signal remote client that we hit a breakpoint
01837         unsigned char c=(unsigned char)Exult_server::dbg_on_breakpoint;
01838         if (client_socket >= 0)
01839           Exult_server::Send_data(client_socket,
01840                       Exult_server::usecode_debugging,
01841                       &c, 1);
01842 
01843 #ifdef XWIN
01844         raise(SIGUSR1); // to allow trapping it in gdb too
01845 #endif
01846 
01847 
01848 #if 0
01849         // little console mode "debugger" (if you can call it that...)
01850         bool done = false;
01851         while (!done) {
01852           char userinput;
01853           cout << "s=step into, o=step over, f=finish, c=continue, "
01854              << "b=stacktrace: ";
01855           cin >> userinput;
01856 
01857           if (userinput == 's') {
01858             breakpoints.add(new AnywhereBreakpoint());
01859             cout << "Stepping into..." << endl;
01860             done = true;
01861           } else if (userinput == 'o') {
01862             breakpoints.add(new StepoverBreakpoint(frame));
01863             cout << "Stepping over..." << endl;
01864             done = true;
01865           } else if (userinput == 'f') {
01866             breakpoints.add(new FinishBreakpoint(frame));
01867             cout << "Finishing function..." << endl;
01868             done = true;
01869           } else if (userinput == 'c') {
01870             done = true;
01871           } else if (userinput == 'b') {
01872             stack_trace(cout);
01873           }
01874         }
01875 #elif (defined(USE_EXULTSTUDIO))
01876         breakpoint_action = -1;
01877         while (breakpoint_action == -1) {
01878           SDL_Delay(20);
01879           Server_delay(Handle_client_debug_message);
01880         }
01881 #endif
01882 
01883 
01884         c = (unsigned char)Exult_server::dbg_continuing;
01885         if (client_socket >= 0)
01886           Exult_server::Send_data(client_socket,
01887                       Exult_server::usecode_debugging,
01888                       &c, 1);
01889         // disable extra debugging messages again
01890         on_breakpoint = false;
01891       }
01892 
01893 #endif
01894 
01895 
01896       frame->ip++;
01897 
01898       switch (opcode)
01899       {
01900       case 0x04:  // start conversation
01901       case 0x84: // (32 bit version)
01902       {
01903         if (opcode < 0x80)
01904           offset = (short) Read2(frame->ip);
01905         else
01906           offset = (sint32) Read4(frame->ip);
01907         
01908         found_answer = false;
01909         if (!get_user_choice())  // Exit conv. if no choices.
01910           frame->ip += offset; // (Emps and honey.)
01911         break;
01912       }
01913       case 0x05:    // JNE.
01914       {
01915         offset = (short) Read2(frame->ip);
01916         Usecode_value val = pop();
01917         if (val.is_false())
01918           frame->ip += offset;
01919         break;
01920       }
01921       case 0x85:    // JNE32
01922       {
01923         offset = (sint32) Read4(frame->ip);
01924         Usecode_value val = pop();
01925         if (val.is_false())
01926           frame->ip += offset;
01927         break;
01928       }
01929       case 0x06:    // JMP.
01930         offset = (short) Read2(frame->ip);
01931         frame->ip += offset;
01932         break;
01933       case 0x86:    // JMP32
01934         offset = (sint32) Read4(frame->ip);
01935         frame->ip += offset;
01936         break;
01937       case 0x07:    // CMPS.
01938       case 0x87: // (32 bit version)
01939       {
01940         int cnt = Read2(frame->ip); // # strings.
01941         if (opcode < 0x80)
01942           offset = (short) Read2(frame->ip);
01943         else
01944           offset = (sint32) Read4(frame->ip);
01945         
01946         bool matched = false;
01947         
01948         // only try to match if we haven't found an answer yet
01949         while (!matched && !found_answer && cnt-- > 0) {
01950           Usecode_value s = pop();
01951           const char *str = s.get_str_value();
01952           if (str && strcmp(str, user_choice) == 0) {
01953             matched = true;
01954             found_answer = true;
01955           }
01956         }
01957         while (cnt-- > 0) // Pop rest of stack.
01958           pop();
01959         if (!matched)   // Jump if no match.
01960           frame->ip += offset;
01961       }
01962       break;
01963       case 0x09:    // ADD.
01964       {
01965         Usecode_value v2 = pop();
01966         Usecode_value v1 = pop();
01967         Usecode_value sum = v1 + v2;
01968         push(sum);
01969         break;
01970       }
01971       case 0x0a:    // SUB.
01972         sval = popi();
01973         pushi(popi() - sval);
01974         break;
01975       case 0x0b:    // DIV.
01976         sval = popi();
01977         pushi(popi()/sval);
01978         break;
01979       case 0x0c:    // MUL.
01980         pushi(popi()*popi());
01981         break;
01982       case 0x0d:    // MOD.
01983         sval = popi();
01984         pushi(popi() % sval);
01985         break;
01986       case 0x0e:    // AND.
01987       {
01988         Usecode_value v1 = pop();
01989         Usecode_value v2 = pop();
01990         int result = v1.is_true() && v2.is_true();
01991         pushi(result);
01992         break;
01993       }
01994       case 0x0f:    // OR.
01995       {
01996         Usecode_value v1 = pop();
01997         Usecode_value v2 = pop();
01998         int result = v1.is_true() || v2.is_true();
01999         pushi(result);
02000         break;
02001       }
02002       case 0x10:    // NOT.
02003         pushi(!pop().is_true());
02004         break;
02005       case 0x12:    // POP into a variable.
02006       {
02007         offset = Read2(frame->ip);
02008         // Get value.
02009         Usecode_value val = pop();
02010         if (offset < 0 || offset >= num_locals) {
02011           LOCAL_VAR_ERROR(offset);
02012         } else {
02013           frame->locals[offset] = val;
02014         }
02015       }
02016       break;
02017       case 0x13:    // PUSH true.
02018         pushi(1);
02019         break;
02020       case 0x14:    // PUSH false.
02021         pushi(0);
02022         break;
02023       case 0x16:    // CMPGT.
02024         sval = popi();
02025         pushi(popi() > sval); // Order?
02026         break;
02027       case 0x17:    // CMPL.
02028         sval = popi();
02029         pushi(popi() < sval);
02030         break;
02031       case 0x18:    // CMPGE.
02032         sval = popi();
02033         pushi(popi() >= sval);
02034         break;
02035       case 0x19:    // CMPLE.
02036         sval = popi();
02037         pushi(popi() <= sval);
02038         break;
02039       case 0x1a:    // CMPNE.
02040       {
02041         Usecode_value val1 = pop();
02042         Usecode_value val2 = pop();
02043         pushi(!(val1 == val2));
02044         break;
02045       }
02046       case 0x1c:    // ADDSI.
02047         offset = Read2(frame->ip);
02048         if (offset < 0 || frame->data + offset >= frame->externs-6) {
02049           DATA_SEGMENT_ERROR();
02050           break;
02051         }
02052         append_string((char*)(frame->data + offset));
02053         break;
02054       case 0x9c:    // ADDSI32
02055         offset = (sint32)Read4(frame->ip);
02056         if (offset < 0 || frame->data + offset >= frame->externs-6) {
02057           DATA_SEGMENT_ERROR();
02058           break;
02059         }
02060         append_string((char*)(frame->data + offset));
02061         break;
02062       case 0x1d:    // PUSHS.
02063         offset = Read2(frame->ip);
02064         if (offset < 0 || frame->data + offset >= frame->externs-6) {
02065           DATA_SEGMENT_ERROR();
02066           break;
02067         }
02068         pushs((char*)(frame->data + offset));
02069         break;
02070       case 0x9d:    // PUSHS32
02071         offset = (sint32)Read4(frame->ip);
02072         if (offset < 0 || frame->data + offset >= frame->externs-6) {
02073           DATA_SEGMENT_ERROR();
02074           break;
02075         }
02076         pushs((char*)(frame->data + offset));
02077         break;
02078       case 0x1e:    // ARRC.
02079       {   // Get # values to pop into array.
02080         int num = Read2(frame->ip);
02081         int cnt = num;
02082         Usecode_value arr(num, 0);
02083         int to = 0; // Store at this index.
02084         while (cnt--)
02085         {
02086           Usecode_value val = pop();
02087           to += arr.add_values(to, val);
02088         }
02089         if (to < num)// 1 or more vals empty arrays?
02090           arr.resize(to);
02091         push(arr);
02092       }
02093       break;
02094       case 0x1f:    // PUSHI.
02095       {   // Might be negative.
02096         short ival = Read2(frame->ip);
02097         pushi(ival);
02098         break;
02099       }
02100       case 0x9f:    // PUSHI32
02101       {
02102         int ival = (sint32)Read4(frame->ip);
02103         pushi(ival);
02104         break;
02105       }
02106       case 0x21:    // PUSH.
02107         offset = Read2(frame->ip);
02108         if (offset < 0 || offset >= num_locals) {
02109           LOCAL_VAR_ERROR(offset);
02110           pushi(0);
02111         }
02112         else {
02113           push(frame->locals[offset]);
02114         }
02115         break;
02116       case 0x22:    // CMPEQ.
02117       {
02118         Usecode_value val1 = pop();
02119         Usecode_value val2 = pop();
02120         pushi(val1 == val2);
02121         break;
02122       }
02123       case 0x24:    // CALL.
02124       {
02125         offset = Read2(frame->ip);
02126         if (offset < 0 || offset >= frame->num_externs) {
02127           EXTERN_ERROR();
02128           break;
02129         }
02130           
02131         uint8 *tempptr = frame->externs + 2*offset;
02132         int funcid = Read2(tempptr);
02133 
02134         call_function(funcid, frame->eventid);
02135         frame_changed = true;
02136         break;
02137       }
02138       case 0x25:    // RET. (End of procedure reached)
02139       case 0x2C:    // RET. (Return from procedure)
02140         show_pending_text();
02141 
02142         return_from_procedure();
02143         frame_changed = true;
02144         break;
02145       case 0x26:    // AIDX.
02146       {
02147         sval = popi();  // Get index into array.
02148         sval--;   // It's 1 based.
02149         // Get # of local to index.
02150         offset = Read2(frame->ip);
02151         if (offset < 0 || offset >= num_locals) {
02152           LOCAL_VAR_ERROR(offset);
02153           pushi(0);
02154           break;
02155         }
02156         if (sval < 0) {
02157           cerr << "AIDX: Negative array index: " << sval << endl;
02158           pushi(0);
02159           break;
02160         }
02161         Usecode_value& val = frame->locals[offset];
02162         
02163         if (val.is_array()) {
02164           push(val.get_elem(sval));
02165         } else if (sval == 0) {
02166           push(val); // needed for SS keyring (among others)
02167         } else {
02168           pushi(0);  // guessing... probably unnecessary
02169         }
02170         break;
02171       }
02172       case 0x2d:    // RET. (Return from function)
02173       {
02174         Usecode_value r = pop();
02175 
02176         return_from_function(r);
02177         frame_changed = true;
02178         break;
02179       }
02180       case 0x2e:    // INITLOOP (1st byte of loop)
02181       case 0xae:    // (32 bit version)   
02182       {
02183         int nextopcode = *(frame->ip);
02184         if ((opcode == 0x2e && nextopcode != 0x02) ||
02185           (opcode == 0xae && nextopcode != 0x82)) {
02186           cerr << "2nd byte in loop isn't a 0x02 (or 0x82)!"<<endl;
02187           break;
02188         } else {
02189           initializing_loop = true;
02190         }
02191         break;
02192       }
02193       case 0x02:  // LOOP (2nd byte of loop)
02194       case 0x82:  // (32 bit version)
02195       {
02196         // Counter (1-based).
02197         int local1 = Read2(frame->ip);
02198         // Total count.
02199         int local2 = Read2(frame->ip);
02200         // Current value of loop var.
02201         int local3 = Read2(frame->ip);
02202         // Array of values to loop over.
02203         int local4 = Read2(frame->ip);
02204         // Get offset to end of loop.
02205         if (opcode < 0x80)
02206           offset = (short) Read2(frame->ip);
02207         else
02208           offset = (sint32) Read4(frame->ip); // 32 bit offset
02209 
02210         if (local1 < 0 || local1 >= num_locals) {
02211           LOCAL_VAR_ERROR(local1);
02212           break;
02213         }
02214         if (local2 < 0 || local2 >= num_locals) {
02215           LOCAL_VAR_ERROR(local1);
02216           break;
02217         }
02218         if (local3 < 0 || local3 >= num_locals) {
02219           LOCAL_VAR_ERROR(local1);
02220           break;
02221         }
02222         if (local4 < 0 || local4 >= num_locals) {
02223           LOCAL_VAR_ERROR(local1);
02224           break;
02225         }
02226         
02227         // Get array to loop over.
02228         Usecode_value& arr = frame->locals[local4];
02229 
02230         int next = frame->locals[local1].get_int_value();
02231 
02232         if (initializing_loop)
02233         { // Initialize loop.
02234           initializing_loop = false;
02235           int cnt = arr.is_array() ?
02236             arr.get_array_size() : 1;
02237           frame->locals[local2] = Usecode_value(cnt);
02238           frame->locals[local1] = Usecode_value(0);
02239 
02240           next = 0;
02241         }
02242         else if (GAME_SI)
02243         {
02244           // in SI, the loop-array can be modified in-loop, it seems
02245           // (conv. with Spektran, 044D:00BE)
02246            
02247           // so, check for changes of the array size, and adjust
02248           // total count and next value accordingly.
02249 
02250           int cnt = arr.is_array() ? arr.get_array_size() : 1;
02251 
02252           if (cnt != frame->locals[local2].get_int_value()) {
02253           
02254             // update new total count
02255             frame->locals[local2] = Usecode_value(cnt);
02256             
02257             if (std::abs(cnt-frame->locals[local2].get_int_value())==1)
02258             {
02259               // small change... we can fix this
02260               Usecode_value& curval = arr.is_array() ?
02261                 arr.get_elem(next - 1) : arr;
02262               
02263               if (curval != frame->locals[local3]) {
02264                 if (cnt>frame->locals[local2].get_int_value()){
02265                   //array got bigger, it seems
02266                   //addition occured before the current value
02267                   next++;
02268                 } else {
02269                   //array got smaller
02270                   //deletion occured before the current value
02271                   next--;
02272                 }
02273               } else {
02274                 //addition/deletion was after the current value
02275                 //so don't need to update 'next'
02276               }
02277             }
02278             else
02279             {
02280                 // big change... 
02281                 // just update total count to make sure
02282                 // we don't crash
02283             }
02284           }
02285 
02286           if (cnt != frame->locals[local2].get_int_value()) {
02287 
02288             // update new total count
02289             frame->locals[local2] = Usecode_value(cnt);
02290 
02291             Usecode_value& curval = arr.is_array() ?
02292               arr.get_elem(next - 1) : arr;
02293             
02294             if (curval != frame->locals[local3]) {
02295               if (cnt > frame->locals[local2].get_int_value()) {
02296                 // array got bigger, it seems
02297                 // addition occured before the current value
02298                 next++;
02299               } else {
02300                 // array got smaller
02301                 // deletion occured before the current value
02302                 next--;
02303               }
02304             } else {
02305               // addition/deletion was after the current value
02306               // so don't need to update 'next'
02307             }
02308           }
02309         }
02310 
02311         // End of loop?
02312         if (next >= frame->locals[local2].get_int_value()) {
02313           frame->ip += offset;
02314         } else    // Get next element.
02315         {
02316           frame->locals[local3] = arr.is_array() ?
02317             arr.get_elem(next) : arr;
02318           frame->locals[local1] = Usecode_value(next + 1);
02319         }
02320         break;
02321       }
02322       case 0x2f:    // ADDSV.
02323       {
02324         offset = Read2(frame->ip);
02325         if (offset < 0 || offset >= num_locals) {
02326           LOCAL_VAR_ERROR(offset);
02327           break;
02328         }
02329 
02330         const char *str = frame->locals[offset].get_str_value();
02331         if (str)
02332           append_string(str);
02333         else    // Convert integer.
02334         {
02335         // 25-09-2001 - Changed to >= 0 to fix money-counting in SI.
02336         //        if (locals[offset].get_int_value() != 0) {
02337           if (frame->locals[offset].get_int_value() >= 0) {
02338             char buf[20];
02339             snprintf(buf, 20, "%ld",
02340                  frame->locals[offset].get_int_value());
02341             append_string(buf);
02342           }
02343         }
02344         break;
02345       }
02346       case 0x30:    // IN.  Is a val. in an array?
02347       {
02348         Usecode_value arr = pop();
02349         // If an array, use 1st elem.
02350         Usecode_value val = pop().get_elem0();
02351         pushi(arr.find_elem(val) >= 0);
02352         break;
02353       }
02354       case 0x31:    // Unknown.
02355       case 0xB1:    // (32 bit version)
02356       // this opcode only occurs in the 'audition' usecode function (BG)
02357       // not sure what it's supposed to do, but this function results
02358       // in the same behaviour as the original
02359         frame->ip += 2;
02360         if (opcode < 0x80)
02361           offset = (short)Read2(frame->ip);
02362         else
02363           offset = (sint32)Read4(frame->ip);
02364         
02365         if (!found_answer)
02366           found_answer = true;
02367         else
02368           frame->ip += offset;
02369         break;
02370 
02371       case 0x32:    // RET. (End of function reached)
02372       {
02373         show_pending_text();
02374 
02375         Usecode_value zero(0);
02376         return_from_function(zero);
02377         frame_changed = true;
02378         break;
02379       }
02380       case 0x33:    // SAY.
02381         say_string();
02382         break;
02383       case 0x38:    // CALLIS.
02384       {
02385         offset = Read2(frame->ip);
02386         sval = *(frame->ip)++;  // # of parameters.
02387         Usecode_value ival = call_intrinsic(frame->eventid,
02388                           offset, sval);
02389         push(ival);
02390         frame_changed = true;
02391         break;
02392       }
02393       case 0x39:    // CALLI.
02394         offset = Read2(frame->ip);
02395         sval = *(frame->ip)++; // # of parameters.
02396         call_intrinsic(frame->eventid, offset, sval);
02397         frame_changed = true;
02398         break;
02399       case 0x3e:    // PUSH ITEMREF.
02400         pushref(frame->caller_item);
02401         break;
02402       case 0x3f:    // ABRT.
02403         show_pending_text();
02404 
02405         abort_function();
02406         frame_changed = true;
02407         aborted = true;
02408         break;
02409       case 0x40:    // end conversation
02410         found_answer = true;
02411         break;
02412       case 0x42:    // PUSHF.
02413         offset = Read2(frame->ip);
02414         if (offset < 0 || offset >= 1024) {
02415           FLAG_ERROR(offset);
02416           pushi(0);
02417         }
02418         pushi(gflags[offset]);
02419         break;
02420       case 0x43:    // POPF.
02421         offset = Read2(frame->ip);
02422         if (offset < 0 || offset >= 1024) {
02423           FLAG_ERROR(offset);
02424         }
02425         gflags[offset] = (unsigned char) popi();
02426         // ++++KLUDGE for Monk Isle:
02427         if (offset == 0x272 && Game::get_game_type() ==
02428           SERPENT_ISLE)
02429           gflags[offset] = 0;
02430         break;
02431       case 0x44:    // PUSHB.
02432         pushi(*(frame->ip)++);
02433         break;
02434       case 0x46:    // Set array element.
02435       {
02436         // Get # of local array.
02437         offset = Read2(frame->ip);
02438         if (offset < 0 || offset >= num_locals) {
02439           LOCAL_VAR_ERROR(offset);
02440           break;
02441         }
02442 
02443         Usecode_value& arr = frame->locals[offset];
02444         short index = popi();
02445         index--;  // It's 1-based.
02446         Usecode_value val = pop();
02447         int size = arr.get_array_size();
02448         if (index >= 0 && 
02449           (index < size || arr.resize(index + 1)))
02450           arr.put_elem(index, val);
02451         break;
02452       }
02453       case 0x47:    // CALLE.  Stack has caller_item.
02454       {
02455         Usecode_value ival = pop();
02456         Game_object *caller = get_item(ival);
02457         push(ival); // put caller_item back on stack
02458         offset = Read2(frame->ip);
02459         call_function(offset, frame->eventid, caller);
02460         frame_changed = true;
02461         break;
02462       }
02463       case 0x48:    // PUSH EVENTID.
02464         pushi(frame->eventid);
02465         break;
02466       case 0x4a:    // ARRA.
02467       {
02468         Usecode_value val = pop();
02469         Usecode_value arr = pop();
02470         push(arr.concat(val));
02471         break;
02472       }
02473       case 0x4b:    // POP EVENTID.
02474         frame->eventid = popi();
02475         break;
02476       case 0x4c: // debugging opcode from spanish SI (line number)
02477       {
02478         frame->line_number = Read2(frame->ip);
02479         break;
02480       }
02481       case 0x4d: // debugging opcode from spanish SI (function init)
02482       {
02483         int funcname = Read2(frame->ip);
02484         int paramnames = Read2(frame->ip);
02485         break;
02486       }
02487       case 0x50:    // PUSH static.
02488         offset = Read2(frame->ip);
02489         if (offset < 0) // Global static.
02490           if (-offset < statics.size())
02491             push(statics[-offset]);
02492           else
02493             pushi(0);
02494         else if (offset <
02495               frame->function->statics.size())
02496           push(frame->function->statics[offset]);
02497         else
02498           pushi(0);
02499         break;
02500       case 0x51:    // POP static.
02501       {
02502         offset = Read2(frame->ip);
02503         // Get value.
02504         Usecode_value val = pop();
02505         if (offset < 0)
02506           statics.put(-offset, val);
02507         else
02508           frame->function->statics.put(
02509                 offset, val);
02510       }
02511         break;
02512       case 0x52:    // CALLO (call original).
02513       {     // Otherwise, like CALLE.
02514         Usecode_value ival = pop();
02515         Game_object *caller = get_item(ival);
02516         push(ival); // put caller_item back on stack
02517 
02518         offset = Read2(frame->ip);
02519         call_function(offset, frame->eventid, caller,
02520                 false, true);
02521         frame_changed = true;
02522         break;
02523       }
02524       case 0xcd: // 32 bit debugging function init
02525       {
02526         int funcname = (sint32)Read4(frame->ip);
02527         int paramnames = (sint32)Read4(frame->ip);
02528         break;
02529       }
02530       default:
02531         cerr << "Opcode " << opcode << " not known. ";
02532         CERR_CURRENT_IP();
02533         break;
02534       }
02535     }   
02536   }
02537 
02538   if (call_stack.front() == 0) {
02539     // pop the NULL frame from the stack
02540     call_stack.pop_front();
02541   }
02542 
02543   if (aborted)
02544     return 0;
02545 
02546   return 1;
02547 }
02548 
02549 /*
02550  *  This is the main entry for outside callers.
02551  *
02552  *  Output: -1 if not found.
02553  *    0 if can't execute now or if aborted.
02554  *    1 otherwise.
02555  */
02556 
02557 int Usecode_internal::call_usecode
02558   (
02559   int id,       // Function #.
02560   Game_object *obj,   // Item ref.
02561   Usecode_events event
02562   )
02563   {
02564           // Avoid these when already execing.
02565   if (!call_stack.empty() && 
02566     event == npc_proximity && Game::get_game_type() ==
02567                 BLACK_GATE)
02568     return (0);
02569 
02570   conv->clear_answers();
02571 
02572   int ret;
02573   if (call_function(id, event, obj, true))
02574     ret = run();
02575   else
02576     ret = -1; // failed to call the function
02577 
02578   set_book(0);
02579 
02580           // Left hanging (BG)?
02581   if (conv->get_num_faces_on_screen() > 0)
02582     {
02583     conv->init_faces(); // Remove them.
02584     gwin->set_all_dirty();  // Force repaint.
02585     }
02586   if (modified_map)
02587     {     // On a barge, and we changed the map.
02588     Barge_object *barge = gwin->get_moving_barge();
02589     if (barge)
02590       barge->set_to_gather(); // Refigure what's on barge.
02591     modified_map = false;
02592     }
02593   return ret;
02594   }
02595 
02596 /*
02597  *  Start speech, or show text if speech isn't enabled.
02598  */
02599 
02600 void Usecode_internal::do_speech
02601   (
02602   int num
02603   )
02604   {
02605   speech_track = num;   // Used in Usecode function.
02606   if (!Audio::get_ptr()->start_speech(num))
02607           // No speech?  Call text function.
02608     call_usecode(0x614, gwin->get_main_actor(), double_click);
02609   }
02610 
02611 /*
02612  *  Write out one usecode value.
02613  */
02614 
02615 static void Write_useval
02616   (
02617   ostream& out,
02618   Usecode_value& val
02619   )
02620   {
02621   unsigned char buf[1024];
02622   int len = val.save(buf, sizeof(buf));
02623   if (len < 0)
02624     throw file_exception("Static usecode value overflows buf");
02625   else
02626     out.write((char *) buf, len);
02627   }
02628 
02629 /*
02630  *  Write out global data to 'gamedat/usecode.dat'.
02631  *  (and 'gamedat/keyring.dat')
02632  *
02633  *  Output: 0 if error.
02634  */
02635 
02636 void Usecode_internal::write
02637   (
02638   )
02639   {
02640           // Assume new games will have keyring.
02641   if (Game::get_game_type() != BLACK_GATE)
02642     keyring->write(); // write keyring data
02643 
02644   ofstream out;
02645   U7open(out, FLAGINIT);  // Write global flags.
02646   out.write((char*)gflags, sizeof(gflags));
02647   out.close();
02648   U7open(out, USEDAT);
02649   Write2(out, partyman->get_count()); // Write party.
02650   int i;  // Blame MSVC
02651   for (i = 0; i < EXULT_PARTY_MAX; i++)
02652     Write2(out, partyman->get_member(i));
02653           // Timers.
02654   for (size_t t = 0; t < sizeof(timers)/sizeof(timers[0]); t++)
02655     Write4(out, timers[t]);
02656   Write2(out, saved_pos.tx);  // Write saved pos.
02657   Write2(out, saved_pos.ty);
02658   Write2(out, saved_pos.tz);
02659   out.flush();
02660   if( !out.good() )
02661     throw file_write_exception(USEDAT);
02662   out.close();
02663   U7open(out, USEVARS);   // Static variables. 1st, globals.
02664   Write4(out, statics.size());  // # globals.
02665   Exult_vector<Usecode_value>::iterator it;
02666   for (it = statics.begin(); it != statics.end(); ++it)
02667     Write_useval(out, *it);
02668           // Now do the local statics.
02669   int num_slots = sizeof(funs)/sizeof(funs[0]);
02670   for (i = 0; i < num_slots; i++)
02671     {
02672     vector<Usecode_function*>& slot = funs[i];
02673     for (std::vector<Usecode_function*>::iterator fit = slot.begin();
02674             fit != slot.end(); ++fit)
02675       {
02676       Usecode_function *fun = *fit;
02677       if (!fun || fun->statics.empty())
02678         continue;
02679       Write4(out, fun->id);
02680       Write4(out, fun->statics.size());
02681       for (it = fun->statics.begin();
02682           it != fun->statics.end(); ++it)
02683         Write_useval(out, *it);
02684       }
02685     }
02686   Write4(out, 0xffffffffU); // End with -1.
02687   out.flush();
02688   if( !out.good() )
02689     throw file_write_exception(USEVARS);
02690   out.close();
02691   }
02692 
02693 /*
02694  *  Read in global data from 'gamedat/usecode.dat'.
02695  *  (and 'gamedat/keyring.dat')
02696  *
02697  *  Output: 0 if error.
02698  */
02699 
02700 void Usecode_internal::read
02701   (
02702   )
02703   {
02704   if (Game::get_game_type() == SERPENT_ISLE)
02705     keyring->read();  // read keyring data
02706   
02707 
02708   ifstream in;
02709   try
02710   {
02711     U7open(in, FLAGINIT); // Read global flags.
02712     in.read((char*)gflags, sizeof(gflags));
02713     in.close();
02714   } catch(exult_exception &e) {
02715     if (!Game::is_editing())
02716       throw e;
02717     memset(&gflags[0], 0, sizeof(gflags));
02718   }
02719   try
02720   {
02721     U7open(in, USEVARS);
02722     read_usevars(in);
02723     in.close();
02724   }
02725   catch(exult_exception &e) {
02726     ;     // Okay if this doesn't exist.
02727           // ++++Maybe we should clear them all.
02728   }
02729   try
02730   {
02731     U7open(in, USEDAT);
02732   }
02733   catch(exult_exception &e) {
02734     partyman->set_count(0);
02735     partyman->link_party(); // Still need to do this.
02736     return;     // Not an error if no saved game yet.
02737   }
02738   partyman->set_count(Read2(in)); // Read party.
02739   size_t i; // Blame MSVC
02740   for (i = 0; i < EXULT_PARTY_MAX; i++)
02741     partyman->set_member(i, Read2(in));
02742   partyman->link_party();
02743           // Timers.
02744   for (size_t t = 0; t < sizeof(timers)/sizeof(timers[0]); t++)
02745     timers[t] = Read4(in);
02746   if (!in.good())
02747     throw file_read_exception(USEDAT);
02748   saved_pos.tx = Read2(in); // Read in saved position.
02749   saved_pos.ty = Read2(in);
02750   saved_pos.tz = Read2(in);
02751   if (!in.good() ||   // Failed.+++++Can remove this later.
02752       saved_pos.tz < 0 || saved_pos.tz > 13)
02753     saved_pos = Tile_coord(-1, -1, -1);
02754   }
02755 
02756 /*
02757  *  Read in static variables from USEVARS.
02758  */
02759 
02760 void Usecode_internal::read_usevars
02761   (
02762   std::istream& in
02763   )
02764   {
02765   in.seekg(0, ios::end);
02766   int size = in.tellg();    // Get file size.
02767   in.seekg(0);
02768   if (!size)
02769     return;
02770   unsigned char *buf = new unsigned char[size]; // Get the whole thing.
02771   in.read((char *) buf, size);
02772   unsigned char *ptr = buf;
02773   unsigned char *ebuf = buf + size;
02774   int cnt = Read4(ptr);   // Global statics.
02775   statics.resize(cnt);
02776   int i;
02777   for (i = 0; i < cnt; i++)
02778     statics[i].restore(ptr, ebuf - ptr);
02779   unsigned long funid;
02780   while (ptr < ebuf && (funid = Read4(ptr)) != 0xffffffffU)
02781     {
02782     int cnt = Read4(ptr);
02783     vector<Usecode_function*>& slot = funs[funid/0x100];
02784     size_t index = funid%0x100;
02785     Usecode_function *fun = index < slot.size() ? slot[index] : 0;
02786     if (!fun)
02787       {
02788       cerr << "Usecode " << funid << " not found" << endl;
02789       continue;
02790       }
02791     fun->statics.resize(cnt);
02792     for (i = 0; i < cnt; i++)
02793       fun->statics[i].restore(ptr, ebuf - ptr);
02794     }
02795   delete [] buf;
02796   }
02797 
02798 #ifdef USECODE_DEBUGGER
02799 
02800 int Usecode_internal::get_callstack_size() const
02801 {
02802   return call_stack.size();
02803 }
02804 
02805 Stack_frame* Usecode_internal::get_stackframe(int i)
02806 {
02807   if (i >= 0 && i < call_stack.size())
02808     return call_stack[i];
02809   else
02810     return 0;
02811 }
02812 
02813 
02814 // return current size of the stack
02815 int Usecode_internal::get_stack_size() const
02816 {
02817   return (int)(sp - stack);
02818 }
02819 
02820 // get an(y) element from the stack. (depth == 0 is top element)
02821 Usecode_value* Usecode_internal::peek_stack(int depth) const
02822 {
02823   if (depth < 0 || depth >= get_stack_size())
02824     return 0;
02825 
02826   return (sp - depth - 1);
02827 }
02828 
02829 // modify an(y) element on the stack. (depth == 0 is top element)
02830 void Usecode_internal::poke_stack(int depth, Usecode_value& val)
02831 {
02832   if (depth < 0 || (sp - depth) < stack)
02833     return;
02834 
02835   *(sp - depth) = val;
02836 }
02837 
02838 
02839 void Usecode_internal::set_breakpoint()
02840 {
02841   breakpoints.add(new AnywhereBreakpoint());
02842 }
02843 
02844 void Usecode_internal::dbg_stepover()
02845 {
02846   if (on_breakpoint)
02847     breakpoints.add(new StepoverBreakpoint(call_stack.front()));
02848 }
02849 
02850 void Usecode_internal::dbg_finish()
02851 {
02852   if (on_breakpoint)
02853     breakpoints.add(new FinishBreakpoint(call_stack.front()));
02854 }
02855 
02856 int Usecode_internal::set_location_breakpoint(int funcid, int ip)
02857 {
02858   Breakpoint *bp = new LocationBreakpoint(funcid, ip);
02859   breakpoints.add(bp);
02860 
02861   return bp->id;
02862 }
02863 
02864 #endif

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