/*  token.c-- handles metacommand execution         */
/* Copyright 1996,97    Robert Masenten                      */
/*                                                        */
/* This is part of the source for the AGiliTy             */

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

#include "agtread.h"
#include "uagt.h"
#include "exec.h"


#define MAX_SUBCALL 5000  /* Maximum number of subroutine calls.
			     If this is 0, no limit (except for the 
			     program's stack size). */

long recurse_depth=0;  /* Keeps track of subroutine recursion depth */



static void it_newdesc(int 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 bool 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 */

  writeln("Game error: multiplication out of range.");
  return 0;
}



/* ------------------------------------------------------------------- */
/*  TYPE CHECKING ROUTINES        */
/*  Routines used to do type checking for metacommands. */
/* ------------------------------------------------------------------- */


static bool argvalid(int argtype,int arg)
{
  if (argtype&AGT_VAR) {  /* We have a variable */
    /* First, verify that arg actually indexes a variable */
    if (arg<0 || arg>VAR_NUM) return 0;  /* Nope */ 

    if (argtype==AGT_VAR) return 1;  /* Pure variable; contents don't matter */

    /* Next, verify its contents, using the rest of this routine */
    arg=agt_var[arg];
    argtype&=~AGT_VAR;  /* Mask off AGT_VAR */
  } 

  if (argtype<128) {
    if (tnoun(arg)) return (argtype&AGT_ITEM)!=0;
    if (troom(arg)) return (argtype&AGT_ROOM)!=0;
    if (arg==0) return (argtype&AGT_NONE)!=0;
    if (arg==1) return (argtype&AGT_SELF)!=0;
    if (tcreat(arg)) return (argtype&AGT_CREAT)!=0;
    if (arg==1000) return (argtype&AGT_WORN)!=0;
    return 0;
  }
  else switch(argtype) 
    {
    case AGT_NUM:return 1;
    case AGT_DIR:return (arg>=1 && arg<=12);
    case AGT_FLAG:return (arg>=0 && arg<=FLAG_NUM);
    case AGT_CNT:return (arg>=0 && arg<=CNT_NUM);
    case AGT_QUEST:return (arg>=1 && arg<=MaxQuestion);
    case AGT_MSG:return (arg>=1 && arg<=last_message);
    case AGT_ERR:return (arg>=1 && arg<=NUM_ERR);
    case AGT_STR:return (arg>=1 && arg<=MAX_USTR);
    case AGT_SUB:return (arg>=1 && arg<=MAX_SUB);
    case AGT_PIC:return (arg>=1 && arg<=maxpict);
    case AGT_PIX:return (arg>=1 && arg<=maxpix);
    case AGT_FONT:return (arg>=1 && arg<=maxfont);
    case AGT_SONG:return (arg>=1 && arg<=maxsong);
    case AGT_ROOMFLAG:return (arg>=1 && arg<=32);
    case AGT_EXIT: return ( argvalid(AGT_ROOM|AGT_NONE,arg)
			    || argvalid(AGT_MSG,arg-exitmsg_base) );
    default:writeln("INTERNAL ERROR:Unrecognized type specifier.");
  }
  return 0;
}


static bool argfix(int argtype, int *arg, int optype)
{
  switch(optype) 
    {
    case 0:break;  /* Direct: The easy case */
    case 1:  /* Variable */
      if (!argvalid(AGT_VAR,*arg)) return 0;
      *arg=(int)agt_var[*arg];
      break;
    case 2: *arg=dobj; break; /* NOUN */
    case 3: *arg=iobj; break; /* OBJECT */
    default: rprintf("Internal error: Invalid optype.");
      return 0;
  }
  if (!(optype&2)) { /* i.e. we have direct or variable type */
    if (argtype<64 && *arg==-1) /* NAME */
	*arg=actor;
  }
  return argvalid(argtype,*arg);
}



static int argok(const opdef *opdata,int *arg1,int *arg2, int optype)
{
  if ( (opdata->argnum)>0 && !argfix(opdata->arg1,arg1,optype/4) )
    return 0;
  if ( (opdata->argnum)>1 && !argfix(opdata->arg2,arg2,optype%4) )
    return 0;
  return 1;
}





/* ------------------------------------------------------------------- */
/* 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(it_loc(obj)==1000);
    case 2:return player_has(obj);
        /* 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(it_loc(obj)==loc+first_room);
    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(!islit());
    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: cret(start_of_turn);  /* OncePerTurn */
    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 bool 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]=0;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 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)
	writeln("Game error: Attempt to divide by zero.");
      else agt_var[arg1]/=arg2;
      break;
    case 1120:
      if (arg2==0)
	writeln("Game error: Attempt to divide by zero.");
      else agt_var[arg1]%=arg2;
      break;
    case 1121:agt_getkey(0);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:
      sysmsg(arg1,"GAME ERROR: Standard message not defined.");
      break;

    case 1132: /* AND */
      break;  /* These don't do anything under normal circumstances */
    default: return 0;  /* We didn't recognize it */
    }
  return 1;
}





int exec_token(int ip,int op, int arg1, int arg2, int optype)
/* This routine is responsible for executing all conditions and action
   tokens. Remember action tokens are numbered from 1000. */
/* Return codes: 
   -2=disambiguating and reached action token
   -1=continue running this metacommand
   0=end this metacommand
   1=end all metacommands
   2=end turn 
   3=redirect */
{
  int tmp;

  if (DEBUG_AGT_CMD && !supress_debug)
    debug_cmd_out(ip,op,arg1,arg2,optype);

  if (op<1000) {
    if (!argok(&cond_def[op],&arg1,&arg2,optype)) {
      writeln("GAME ERROR: Invalid argument to conditional token.");
      return 0;}
    if ( (op==96 || op==97) && do_disambig) /* YesNo? */
      return -2;
    if (exec_cond(op,arg1,arg2)) return -1; else return 0;
  } else {
    if (op<WIN_ACT && !argok(&act_def[op-1000],&arg1,&arg2,optype)) {
      writeln("GAME ERROR: Invalid argument to action token.");
      return -1;}
    switch(op) 
      {  /* The following all effect flow of execution and so
		     need to be handled here */
      case 1037: /* Call subroutine SUBROUTINE<arg1> */ 
         recurse_depth++;
	 if (DEBUG_AGT_CMD &&!supress_debug) debugout(" --> CALL\n");
	 if (MAX_SUBCALL!=0 && ++recurse_depth>MAX_SUBCALL) {
	   writeln("GAME ERROR: Too many subroutine calls.");
	   return 2;}
	 tmp=scan_metacommand(0,verb_code(sub_name[arg1-1]),0,0,0,0,0);
	 if (MAX_SUBCALL!=0) recurse_depth--;
	 if (tmp==0) return -1; else return tmp;
	 
      case 1038:return 4; /* Return from subroutine */
	 
      case 1062:return 3; /* Redirect */	 	    

	/* SetDisambigPriority; this does not halt disambiguation,
	 even though it's an action token */
      case 1115:
	disambig_score=arg1; 
	return -1;  /* Keep running comands... */
      

	/* These are only run if the preceding conditional token failed.
	   Otherwise they are skipped over completely */
      case 1130: /* ErrMessage */
	disambig_score=arg1;
	if (do_disambig) return 2;  /* Done with turn */
	/* Fall through... */
      case 1128: /* FailMessage, fall through... */
	if (do_disambig) return -2;
	msgout(arg2,1);
	return 2;  /* Done with turn */

      case 1131: /* ErrStdMessage */	
	disambig_score=arg1;
	if (do_disambig) return 2; /* Done with turn */
	/* Fall through... */
      case 1129: /* FailStdMessage */
	if (do_disambig) return -2;
	sysmsg(arg2,"GAME ERROR: Standard message not defined.");
	return 2;

      case 1132: return -1; /* AND, which should be ignored for
			       disambiguation purposes and acts as a NOP
			       otherwise (it exits purely to separate
			       OR blocks) */

      case WIN_ACT:if (do_disambig) return -2;
       else winflag=1;break;  /* win game */
      case (WIN_ACT+1):if (do_disambig) return -2;
       else endflag=1; return 2; /* end game */
      case (WIN_ACT+2):return 0; /* end this command */
      case (WIN_ACT+3):return 1; /* end all commands */
      case (WIN_ACT+4):return 2; /* end turn */
      default:
	if (do_disambig)  /* Reached action token */
	  return -2;
	if (!exec_action(op,arg1,arg2)) { /* Couldn't find it in std list */
	  writeln("INTERNAL ERROR: Action token not supported.");
	  rprintf("Action #%d",op);   
	  writeln(""); 
	  return -1; /* Continue executing anyhow... */
	}
      }
  }
  return -1;
}
  
