/*  token.c-- handles metacommand execution               */
/* Copyright (C) 1996-1999,2001  Robert Masenten          */
/* This program may be redistributed under the terms of the
   GNU General Public License, version 2; see agility.h for details. */
/*                                                        */
/* This is part of the source for the AGiliTy             */

/* 

   This contains the code for actually executing each of the
   individual metacommand tokens as well as the routines for
   doing range checking to prevent a bad metacommand from crashing
   the interpreter.

*/


#include <assert.h>
#include <ctype.h>
#include <stdio.h>

#include "agility.h"
#include "interp.h"
#include "exec.h"




static void it_newdesc(integer item,descr_ptr *newdesc)
{
  descr_ptr *desc;

  if (tnoun(item)) desc=noun_ptr+(item-first_noun);
  else if (tcreat(item)) desc=creat_ptr+(item-first_creat);
  else if (item>=first_room && item<=maxroom)
    desc=room_ptr+(item-first_room);
  else {
    writeln("INTERNAL ERROR: it_newdesc called with invalid object");
    return;
  }
  desc->start=newdesc->start;
  desc->size=newdesc->size;
}

static void changepict(int old_pict,int new_pict)
/* Replace old_pict by new_pict */
{
  pictable[old_pict-1]=new_pict-1;
}


static const int antidir[12]={1,0,3,2,7,6,5,4,9,8,11,10};

static void change_passage(int start,int dir,int newend)
/* dir is 1..12 */
/* START is a room number: i.e. starting at 0. */
/* NEWEND is an object number, so starts at first_room */
{
  int oldend, i;
   
  dir--;  /* Convert direction to 0..11 scale */
  oldend=room[start].path[dir];
  room[start].path[dir]=newend;
  if (newend==0) {
    if (room[oldend-first_room].path[antidir[dir]]==start+first_room)
      room[oldend-first_room].path[antidir[dir]]=0;
    else for(i=0;i<12;i++)
      if (room[oldend-first_room].path[i]==start+first_room)
	{room[oldend-first_room].path[i]=0;break;}
    }
  else
    room[newend-first_room].path[antidir[dir]]=start+first_room;
}


static long ask_for_number(int n1,int n2)
{
  char s[50];
  int n;

  if (n1!=n2)
    sprintf(s,"Enter a number from %d to %d: ",n1,n2);
  else
    sprintf(s,"Enter a number: ");
  for(;;) {
    writestr(s);
    n=read_number();
    if (n1==n2 || ( n>=n1 && n<=n2)) return n;
    writeln("");
  }   
}

/* Check if n1*n2 will fit in 32 bits and print an error message if not */
/* This errs on the side of caution */
static rbool mult_rangecheck(long n1, long n2)
{
  int cnt;

  if (n1==0 || n2==0) return 1;
  if (n1<0) n1=-n1;
  if (n2<0) n2=-n2;

  for(cnt=0;n1!=0;n1>>=1,cnt++);
  for(;n2!=0;n2>>=1,cnt++);
  cnt--;

  if (cnt<=31) return 1;  /* We're okay */

  if (!PURE_ERROR)
    writeln("GAME ERROR: Multiplication out of range.");
  return 0;
}

static rbool is_numeric(parse_rec *objrec)
{
  char *s;

  if (objrec->num!=0 || objrec->info==D_NUM) return 1;
  if (objrec->adj!=0) return 0;
  if (objrec->noun<=0) return 0;
  strtol(dict[objrec->noun],&s,10);
  return (*s==0); /* *s==0 means no error-- it parsed as a number. */
}

static void setcase(char *s, rbool up)
{
  for(;*s!=0;s++) 
    if (up) *s=toupper(*s);
    else *s=tolower(*s);
}


void move_in_dir(int obj, int dir)
{
  int r;

  r=it_room(obj);
  if (!troom(r)) {writeln("GAME ERROR: Object not in a room."); return;}
  r=room[r-first_room].path[dir-1];
  if (!troom(r)) return; /* Can't go that way; Fail silently */
  if (obj==1) 
    goto_room(r);
  else 
    it_move(obj,r);
}


/* ------------------------------------------------------------------- */
/*  Stack routines:   Manipulating the expression stack                */
/* ------------------------------------------------------------------- */

static long *stack=NULL;
static int sp=0; /* Stack pointer */
static int stacksize=0; /* Actual space allocated to the stack */

void init_stack(void)
{
  rfree(stack);
  sp=0; stacksize=0;
}

/* This resets the stack to an empty state. */
void clear_stack(void)
{
  sp=0;
}

static void push_stack(long val)
{
  sp++;
  if (sp>stacksize) {
    stacksize+=10;
    stack=rrealloc(stack,stacksize*sizeof(long));
  }
  stack[sp-1]=val;
}

static long pop_stack(void)
{
  long n;
  if (sp==0) {
    writeln("GAME ERROR: Stack underflow.");
    return 0;
  }
  n=stack[--sp];
  if (sp+100<stacksize) {
    stacksize-=50;
    stack=rrealloc(stack,stacksize*sizeof(long));
  }
  return n;
}

long pop_expr_stack(void)
{
  return pop_stack();
}


/* opnum: 0=+, 1=-, 2=*, 3=/ 4=% */
static void op_stack(int opnum) 
{
  long n1,n2;
  n1=pop_stack(); n2=pop_stack();
  switch(opnum) 
    {
    case 0: n1=n1+n2; break;
    case 1: n1=n1-n2; break;
    case 2: 
      if (mult_rangecheck(n1,n2)) n1=n1*n2; 
      break;
    case 3: 
      if (n2!=0) n1=n1/n2; 
      else writeln("GAME ERROR: Division by zero.");
      break;
    case 4: 
      if (n2!=0) n1=n1%n2; 
      else writeln("GAME ERROR: Division by zero.");
      break;
    default: writeln("INTERNAL ERROR: Invalid stack operation.");
    }
  push_stack(n1);
}

/* This is called from the disassembler */
void print_tos(void)
{
  if (sp>0) 
    dbgprintf("TOS(%d)",stack[sp-1]);
  else 
    debugout("TOS(xxx)");
}



/* ------------------------------------------------------------------- */
/* METACOMMAND ROUTINES  */
/*  Functions for scanning and decoding of metacommands */
/* ------------------------------------------------------------------- */

/* #define cret(b) return ((b) ? -1 : 0)*/
#define cret(b) return (b) 
#define cretn(i,f) cret(tnoun(i) && noun[i-first_noun].f)
#define cretc(i,f) cret(tcreat(i) && creature[i-first_creat].f)
#define icretc(f) cret(do_disambig==1 || \
		       (tcreat(iobj) && creature[iobj-first_creat].f))
 
static int obj_cond(int op, int obj, int arg)
{
  switch(op) {
    case 0:cret(in_scope(obj));  /* Present-- 
				      Do we want to use visible here?? */
    case 1:cret(is_within(obj,1000,1));  /* IsWearing */
    case 2:cret(is_within(obj,1,1)); 
        /* if (PURE_WEAR)  return (it_loc(obj)==1); else */
    case 3:cret(it_loc(obj)==0);  /* Nowhere */
    case 4:cret(it_loc(obj)!=0);
    case 5:cret(!player_has(obj) && in_scope(obj));
    case 6:cret(it_loc(obj)==arg);
    case 7:cret(it_on(obj));
    case 8:cret(!it_on(obj));
    case 9:cret(it_open(obj));
    case 10:cret(!it_open(obj));
    case 11:cretn(obj,locked);
    case 12:cret(!tnoun(obj) || !noun[obj-first_noun].locked);
    case 13:cretn(obj,edible);
    case 14:cretn(obj,drinkable);
    case 15:cretn(obj,poisonous);
    case 16:cretn(obj,movable);
    default:
      writeln("INTERNAL ERROR: Bad obj_cond value.");
      return 2;
  }
}


static int exec_cond(int op, int arg1, int arg2)
{
  int i;
  
  switch(op) 
    {
    /* First the conditions */
    case 0: cret(loc+first_room==arg1);  /* AtLoc(Room) */
    case 1: cret(loc+first_room>arg1); 
    case 2: cret(loc+first_room<arg1); 
    case 3: return musiccmd(-1,-1); /* SongPlaying */
    case 4: return musiccmd(-2,-1); /* SoundIsOn */
    case 5: cret(vb<=13 && vb>0 && 
		 room[loc].path[vb-1]>=first_room);/*DirOK*/
    case 6: cret(vb==arg1);  /* DirectionIs */
    case 7: cret(loc+first_room>=arg1 && 
		 loc+first_room<=arg2); /* BetweenRooms  ?? */
    case 8: cret(room[arg1-first_room].seen);
    case 9:  /* Entered Object? i.e. is iobj valid */
      cret(do_disambig==1 || iobj>0); 
    case 10: cret(curr_time>arg1);  /* TimeGT */
    case 11: cret(curr_time<arg1);  /* TimeLT */
    case 12: cret(first_visit_flag);
    case 13: cret(newlife_flag);
    case 14: cret(player_contents!=0);  /* CarrySome */
    case 15: cret(player_contents==0); /* CarryNo */
    case 16: cret(player_worn!=0);  /* WearSome */
    case 18: cret(player_worn==0); /* WearNo */
    case 17:          /* CarryTreas */
      contloop(i,1)
	if (tnoun(i) && noun[i-first_noun].points>=arg1) return 1;
      contloop(i,1000)
	if (tnoun(i) && noun[i-first_noun].points>=arg1) return 1;	
      return 0;
    case 19:cret(totwt==arg1);
    case 20:cret(totwt>arg1);
    case 21:cret(totwt<arg1);
    case 22: case 23: case 24: case 25: case 26: case 27: case 28:
      return obj_cond(op-22,arg1,arg2);
    case 29:cret(it_loc(arg1)==it_loc(arg2));
    case 30: case 31:
      return obj_cond(op-23,arg1,arg2);
    case 32:cret(it_group(arg1));
    case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40:
      return obj_cond(op-24,arg1,arg2);
    case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48:
    case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56:
    case 57:
      return obj_cond(op-41,dobj,arg1);
    case 58:cretn(dobj,points==arg1);
    case 59:cretn(dobj,points>arg1);
    case 60:cretn(dobj,points<arg1);
    case 61:cretn(dobj,weight==arg1);
    case 62:cretn(dobj,weight>arg1);
    case 63:cretn(dobj,weight<arg1);
    case 64:cret(islit());
    case 65:cret(room[loc].light!=0);
    case 66:cret(flag[arg1]);
    case 67:cret(!flag[arg1]);
    case 68:cret(room[loc].flag_noun_bits & (1 << (arg1-1) )  );
    case 70:cret( !(room[loc].flag_noun_bits & (1 << (arg1-1) ) ));
    case 69:cret(room[loc].PIX_bits & (1 << (arg1-1) )); /* Room Pix here? */
    case 71:cret(tscore==arg1);
    case 72:cret(tscore>arg1);
    case 73:cret(tscore<arg1);
    case 74:cret(agt_number==arg1);
    case 75:cret(agt_number>arg1);
    case 76:cret(agt_number<arg1);
    case 77:cret(agt_answer);
    case 78:cret(!agt_answer);
    case 79:cret(turncnt==arg1);
    case 80:cret(turncnt>arg1);
    case 81:cret(turncnt<arg1);
    case 82:cret(cnt_val(agt_counter[arg1])==arg2);
    case 83:cret(cnt_val(agt_counter[arg1])>arg2);
    case 84:cret(cnt_val(agt_counter[arg1])<arg2);
	
    case 85:cret(agt_var[arg1]==arg2);
    case 86:cret(agt_var[arg1]>arg2);
    case 87:cret(agt_var[arg1]<arg2);
    case 88:cret(agt_var[arg1]<agt_var[arg2]);      
    case 89:cret(agt_var[arg1]<agt_rand(1,arg2));
    case 90:cret( (actor!=0) && (it_loc(actor)==loc+first_room));
    case 91:cret(actor==arg1);
    case 92:cret(dobj==arg1);
    case 93:cret(do_disambig==1 || iobj==arg1);
    case 94:cret(it_contents(arg1)!=0);
    case 95:cret(agt_rand(1,100)<=arg1);
    case 96:cret(yesno("Yes or no? "));
    case 97:cret(!yesno("Yes or no? "));
    case 98:cret(vb>0 && vb<=13);
    case 99:cret(tcreat(dobj));
    case 100:cretc(dobj,gender==2); /* man */
    case 101:cretc(dobj,gender==1); /* woman */
    case 102:cretc(dobj,gender==0); /* thing */
    case 103:cretc(iobj,gender==2);
    case 104:cretc(iobj,gender==1); /* woman */
    case 105:cretc(iobj,gender==0); /* thing */      
    case 106:cret(do_disambig==1 || tcreat(iobj));
    case 107:return (do_disambig==1 || obj_cond(0,iobj,0));
      /* OR and NOT are handled higher up. */
      /* The following are all v1.8x metacommands */
    case 110:cret(beforecmd);   
    case 111:cret(!beforecmd); 
    case 112:cret(curr_time/100==arg1); /* HoursEqual */
    case 113:cret(curr_time/100>arg1);  
    case 114:cret(curr_time/100<arg1);
    case 115:cret(curr_time%100==arg1); /* MinutesEqual */
    case 116:cret(curr_time%100>arg1);
    case 117:cret(curr_time%100<arg1);
    case 118:cret(curr_time<1200);    /* IsAM */ 

    case 119:cret(do_disambig);     /* OnDisambig */
    case 120:cretc(arg1,hostile);    /* IsHostile */
    case 121:         /* HostilePresent */
      creatloop(i)
	if (creature[i].location==loc+first_room &&
	    creature[i].hostile) return 1;
      return 0;
      /* Otherwise, we're in trouble. */
    case 122: cret(actor_in_scope); /* NameWasPresent */
    case 123: /* OncePerTurn */
      if (beforecmd)
	cret(start_of_turn);  
      else 
	cret(end_of_turn);
    case 124:  /* IsClass */
      cret(arg2==0 || matchclass(arg1,arg2));
    case 125: cret(getattr(arg1,arg2)); /* IsSet */
    case 126: cret(is_numeric(dobj_rec));
    case 127: cret(is_numeric(iobj_rec));
    case 128: cret(arg1==arg2);
    case 129: cret(arg1>arg2);
    case 130: cret(arg1<arg2);
    case 131: cret(arg1>=arg2);
    case 132: cret(arg1<=arg2);
    case 133: cret(strcmp(userstr[arg1-1],userstr[arg2-1])==0);
    case 134: cret(strcmp(userstr[arg1-1],userstr[arg2-1])<0);
    case 135: cret(strcmp(userstr[arg1-1],userstr[arg2-1])>0);
    case 136: cret(strcasecmp(userstr[arg1-1],userstr[arg2-1])==0);
    case 137: cret(strcasecmp(userstr[arg1-1],userstr[arg2-1])<0);
    case 138: cret(strcasecmp(userstr[arg1-1],userstr[arg2-1])>0);
    case 139: cret(match_answer(rstrdup(userstr[arg1-1]),arg2-1));
      /* Note that match_answer rfrees it's first argument */
    case 140: cret(it_seen(arg1));
    case 141: cret(op_objflag(2,arg1,arg2));
    case 142: cret(!op_objflag(2,arg1,arg2));
    case 143: 
      i=it_room(arg1);
      cret( troom(i) && troom(room[i-first_room].path[arg2-1]) );
    default: writeln("INTERNAL ERROR: Condition token not supported.");
	rprintf("Condition #%d",op);
	writeln(""); 
	return 0;
      }
}

#undef cret
#undef cretn
#undef cretc


static void obj_act(int op,int obj)
{
  switch(op) {
    case 0: case 1:  /* open and close */
      if (tnoun(obj))
	noun[obj-first_noun].open=(op==0);
      break;
    case 2: case 3: /* lock and unlock */
      if (tnoun(obj))
	noun[obj-first_noun].locked=(op==2);
    }
}


static void exec_action(int op,int arg1,int arg2)
{
  int i, j;
  char *tmpstr;

  switch(op) 
    {
    case 1000:goto_room(arg1-first_room);break;
    case 1001:goto_room(agt_rand(arg1,arg2)-first_room);break;
    case 1002:agt_var[arg1]=loc+first_room;break;
    case 1003:agt_var[arg1]=dobj;break;
    case 1004:agt_var[arg1]=iobj;break;
    case 1005:goto_room(agt_var[arg1]-first_room);break;
    case 1006:it_move(arg1,agt_var[arg2]);break;
    case 1007:get_obj(agt_var[arg1]);break;
    case 1008:msgout(agt_var[arg1],1);break;
    case 1009:get_obj(arg1);break;
    case 1010:get_obj(arg1);it_move(arg1,1000);break;
    case 1011:drop_obj(arg1);break;
    case 1012:
      if (it_loc(arg1)==1000) {
	if (PURE_WEAR) drop_obj(arg1);
	else it_move(arg1,1);
      }
      break;
    case 1013:fontcmd(0,arg1-1);break;  /* Load font */
    case 1014:pictcmd(1,pictable[arg1-1]);break;  /* Show picture */
    case 1015:changepict(arg1,arg2);break; /* ChangePicture */
    case 1016: if (PICT_SUPPORT && 
		     yesno("Would you like to see the picture?"))
      pictcmd(1,pictable[arg1-1]);break;
    case 1017:pictcmd(2,arg1);break;  /* Show room pix */
    case 1018: if (PICT_SUPPORT && 
		   yesno("Would you like to see the picture?"))
      pictcmd(2,arg1-1);break;
    case 1019:musiccmd(1,arg1-1);break;
    case 1020:musiccmd(1,agt_rand(arg1,arg2)-1);break;
    case 1021:musiccmd(2,arg1-1);break;
    case 1022:musiccmd(3,-1); break;  /* Stop Repeat */
    case 1023:musiccmd(4,-1); break;  /* Stop song */
    case 1024:musiccmd(5,-1); break;  /* Suspend song */
    case 1025:musiccmd(6,-1);break;   /* Resume song */
    case 1026: 
      if (tnoun(dobj)) 
	noun[dobj-first_noun].movable=!noun[dobj-first_noun].movable;
      break;
    case 1027: it_newdesc(arg1,&msg_ptr[arg2-1]); break;
    case 1028: 
      if (tnoun(arg1)) noun[arg1-first_noun].points=arg2;
      else if (tcreat(arg1)) creature[arg1-first_creat].points=arg2;
      else if (troom(arg1)) room[arg1-first_room].points=arg2;
      break;
    case 1029:it_destroy(iobj);break;
    case 1030:
      tmpstr=agt_readline(3);
      i=strlen(tmpstr)-1;
      if (i>0 && tmpstr[i]=='\n') tmpstr[i]=0;
      strncpy(userstr[arg1-1],tmpstr,80);
      rfree(tmpstr);
      break;
    case 1031:agt_var[arg1]=read_number();break;
    case 1032:agt_var[arg1]=curr_time;break;
    case 1033:curr_time=normalize_time(agt_var[arg1]);break; 
    case 1034:curr_time=normalize_time(arg1); break;
    case 1035:add_time(arg1);break; 
    case 1036:delta_time=arg1;break;
      /* 1037 and 1038 are subroutine commands */
    case 1039:get_obj(dobj);break;
    case 1040:it_move(dobj,1000);break;
    case 1041:drop_obj(dobj);break;
    case 1042:
      if (it_loc(dobj)==1000) {
	if (PURE_WEAR) it_move(dobj,1);
	else drop_obj(dobj);
      }
      break;
    case 1043: /* drop all */
      safecontloop(i,j,1) drop_obj(i);
      break;
    case 1044: /* remove all */
      safecontloop(i,j,1000) drop_obj(i);
      break;
    case 1045:deadflag=1;break;
    case 1046:it_move(arg1,loc+first_room);break;
    case 1047:it_move(arg1,arg2);break;
    case 1048:it_reposition(arg1,arg2,1);break; /* RePosition */
    case 1049:it_move(dobj,loc+first_room);break;
    case 1050:it_move(dobj,arg1);break;
    case 1051:
      safecontloop(i,j,1) it_move(i,arg1);
      safecontloop(i,j,1000) it_move(i,arg1);
      break;
    case 1052:
      nounloop(i)
	if (player_has(i+first_noun) && noun[i].points>arg2)
	  it_move(i+first_noun,arg1);
      break;
    case 1053:
      safecontloop(i,j,arg1) 
	if (tnoun(i)) it_move(i,arg2);
      break;
    case 1054:it_destroy(arg1);break;
    case 1055:it_destroy(dobj);break;
    case 1056:i=it_loc(arg1);
      it_move(arg1,it_loc(arg2));
      it_move(arg2,i);
      break;
    case 1057:it_move(arg1,it_loc(arg2));break;
    case 1058:it_move(dobj,it_loc(arg2));break;
    case 1059: case 1060: /* Add to/remove from group */
      if (tcreat(arg1))
	creature[arg1-first_creat].groupmemb=(op==1059);
      break;
    case 1061:   /* Move group */
      safecontloop(i,j,loc+first_room)
	if (it_group(i)) it_move(i,arg1);
      break; 
      /* 1062 is RedirectTo */
    case 1063:msgout(agt_rand(arg1,arg2),1);break;
    case 1064:print_contents(arg1,1);break;
    case 1065: case 1066: case 1067: case 1068: 
      obj_act(op-1065,arg1);break;
    case 1069: case 1070: case 1071: case 1072: 
      obj_act(op-1069,dobj);break;
    case 1073:print_score();break;
    case 1074: tscore+=arg1;break;
    case 1075: tscore-=arg1;break;
    case 1076:v_inventory();break;
    case 1077:wait_return();break;
    case 1078:writeln("Time passes...");break;
    case 1079:agt_delay(arg1);break;
    case 1080:agt_clrscr();break;
    case 1081:it_describe(arg1);break;
    case 1082:look_room();break;   /* LOOK */
    case 1083:msgout(arg1,1);break;
    case 1084:writeln("");break;
    case 1085:if (PURE_TONE && sound_on) 
	       agt_tone(arg1,arg2);break; /* Tone */
    case 1086:agt_number=ask_for_number(arg1,arg2);break;
    case 1087:agt_answer=ask_question(arg1);break;
    case 1088:change_passage(loc,arg1,arg2);break;
    case 1089:flag[arg1]=1;break;
    case 1090:flag[arg1]=0;break;
    case 1091:flag[arg1]=!flag[arg1];break;
    case 1092:room[loc].flag_noun_bits|=(1 << (arg1-1) );
      break; /* Roomflag on */ 
    case 1093:room[loc].flag_noun_bits&=~(1 << (arg1-1) );break; /* Off */ 
    case 1094:room[loc].flag_noun_bits^=(1 << (arg1-1) );break; /* Toggle */
    case 1095: /* if (agt_counter[arg1]==-1)*/
      agt_counter[arg1]=1;break;
    case 1096:agt_counter[arg1]=-1;break;
    case 1097:agt_var[arg1]=arg2;break;
    case 1098:agt_var[arg1]+=arg2;break;
    case 1099:agt_var[arg1]-=arg2;break;
    case 1100:agt_var[arg1]+=agt_var[arg2];break;
    case 1101:agt_var[arg1]-=agt_var[arg2];break;
    case 1102:agt_var[arg1]=agt_rand(0,arg2);break;
    case 1103:agt_var[arg1]=dobj_rec->num;break;
    case 1104:agt_var[arg1]=iobj_rec->num;break;
      
      /* The following are v1.8x specific */
    case 1105:quote(arg1);break;
    case 1106:add_time(arg1);break;
    case 1107:add_time(-arg1);break;
    case 1108:curr_time=(curr_time%100)+100*arg1;break;
    case 1109:curr_time=(curr_time/100)*100+arg1;break;
    case 1110:add_time(agt_var[arg1]);break;
    case 1111:add_time(-agt_var[arg1]);break;
    case 1112:curr_time=(curr_time%100)+100*agt_var[arg1];break;
    case 1113:curr_time=(curr_time/100)*100+agt_var[arg1];break;

      /* Now for the AGX additions */
    case 1114:add_time(-arg1);break; /* ME-style SubtractFromTime */
    case 1115: disambig_score=arg1; break; /* SetDisambigPriority */
    case 1116:agt_var[arg1]=delta_time;break;
    case 1117: /* ChangeStatus */
      statusmode=arg1;
      break;
    case 1118:
      if (!mult_rangecheck(agt_var[arg1],arg2)) break;
      agt_var[arg1]*=arg2;break;
    case 1119:
      if (arg2==0) {
	if (!PURE_ERROR)
	  writeln("GAME ERROR: Division by zero.");
      } else agt_var[arg1]/=arg2;
      break;
    case 1120:
      if (arg2==0) {
	if (!PURE_ERROR)
	  writeln("GAME ERROR: Attempt to divide by zero.");
      } else agt_var[arg1]%=arg2;
      break;
    case 1121:agt_waitkey();break;
    case 1122:last_he=arg1;break;   /* SetHE */
    case 1123:last_she=arg1;break;  
    case 1124:last_it=arg1;break;
    case 1125:last_they=arg1;break;
    case 1126:msgout(arg1,0);break;
    case 1127:
      if (!PURE_ERROR)
	sysmsg(arg1,"GAME ERROR: Standard message not defined.");
      break;
    case 1128: msgout(arg1,1);break; /* FailMessage */
    case 1129:  /* StdMessage */
      sysmsg(arg1,"GAME ERROR: Standard message not defined.");
      break;
    case 1130: msgout(arg2,1);break; /* ErrMessage */
    case 1131: /* StdErrMessage */
      sysmsg(arg1,"GAME ERROR: Standard message not defined.");
      break;
    case 1132: /* AND */
      break;  /* These don't do anything under normal circumstances */
    case 1133:  /* SetClass */
      if (troom(arg1)) room[arg1-first_room].oclass=arg2;
      else if (tnoun(arg1)) noun[arg1-first_noun].oclass=arg2;
      else if (tcreat(arg1)) noun[arg1-first_creat].oclass=arg2;
      break;
    case 1134: agt_var[arg1]=it_class(arg2); break; /* SetVariableToClass */
      
      /* Stack commands */
    case 1135: push_stack(arg1); break;
    case 1136: agt_var[arg1]=pop_stack(); break;
    case 1137: case 1138: case 1139: case 1140: case 1141: 
      op_stack(op-1137); /* +,-,*, /,% * */
      break;
    case 1142: { /* DupStack */
      long n;
      n=pop_stack();
      push_stack(n);
      push_stack(n);
      break;
    }    
    case 1143: pop_stack();break; /* Discard TOS */
    case 1144: agt_var[arg1]=agt_number;break; /* SetVariableToInput */
    case 1145: setattr(arg1,arg2,1); break; /* Set */
    case 1146: setattr(arg1,arg2,0); break; /* Clear */
    case 1147: push_stack(getprop(arg1,arg2));break; /* PushProp */
    case 1148: setprop(arg1,arg2,pop_stack());break; /* PopProp */
      /* 1149, 1150 handled by run_metacommand */
      /* 1151 is EndDisambig */
      /* 1152 is XRedirect */
    case 1153: rstrncpy(userstr[arg1-1],userstr[arg2-1],81);break; 
    case 1154: setcase(userstr[arg1-1],1); break;
    case 1155: setcase(userstr[arg1-1],0);break;
    case 1156: op_objflag(1,arg1,arg2);break;
    case 1157: op_objflag(0,arg1,arg2);break;
    case 1158: op_objflag(3,arg1,arg2);break;
    case 1159: push_stack(op_objprop(2,arg1,arg2,0)); break;
    case 1160: op_objprop(1,arg1,arg2,pop_stack()); break;
    case 1161: move_in_dir(arg1,arg2); break;
    default: 
      writeln("INTERNAL ERROR: Action token not supported.");
      rprintf("Action #%d",op);   
      writeln(""); 
    }
}

int exec_instr(op_rec *oprec)
/* This routine is responsible for executing all conditions and action
   tokens. Remember action tokens are numbered from 1000. */
/* Return codes:
   0 to continue running commands
   1 for a failed conditional token
   100 Next metacommand
   101 Stop running metacommands
   102 End turn
   103 Redirection */    
{
  rbool r;

  if (oprec->op<1000) {
    r=exec_cond(oprec->op,oprec->arg1,oprec->arg2);
    return (oprec->negate ? r : !r); /* Is it prefixed by NOT? */
  }
  switch(oprec->op)
    {
    case 1151: /* EndDisambig <num> */
      if (do_disambig) {
	disambig_score=oprec->arg1;
	return 102;  /* "End turn" if disambiguating */
      }
      return 0;  /* ... otherwise do nothing */
    case 1062: case 1152: return 103; /* Redirect */	 	    
    case WIN_ACT: winflag=1; return 0;  /* win game */
    case (WIN_ACT+1): endflag=1; return 102; /* end game */
    case (WIN_ACT+2): return 100; /* end this command */
    case (WIN_ACT+3): return 101; /* end all commands */
    case (WIN_ACT+4): return 102; /* end turn */
    default: 
      exec_action(oprec->op,oprec->arg1,oprec->arg2);
      if (oprec->failmsg) return 102;
      else return 0;
    }
}




