// script.c

// Egoboo, Copyright (C) 2000 Aaron Bishop

#include "egoboo.h"

//------------------------------------------------------------------------------
//AI Script Routines------------------------------------------------------------
//------------------------------------------------------------------------------
void insert_space(int position)
{
    // ZZ> This function adds a space into the load line if there isn't one
    //     there already
    unsigned char cTmp, cSwap;

    if(cLineBuffer[position]!=' ')
    {
        cTmp = cLineBuffer[position];
        cLineBuffer[position]=' ';
        position++;
        iLineSize++;
        while(position < iLineSize)
        {
            cSwap=cLineBuffer[position];
            cLineBuffer[position]=cTmp;
            cTmp=cSwap;
            position++;
        }
    }
}

//------------------------------------------------------------------------------
void copy_one_line(int write)
{
    // ZZ> This function copies the line back to the load buffer
    int read;
    unsigned char cTmp;


    read = 0;
    cTmp=cLineBuffer[read];
    while(cTmp != 0)
    {
        cTmp=cLineBuffer[read];  read++;
        cLoadBuffer[write]=cTmp;  write++;
    }



    iNumLine++;
}

//------------------------------------------------------------------------------
int load_one_line(int read)
{
    // ZZ> This function loads a line into the line buffer
    int stillgoing, foundtext;
    unsigned char cTmp;


    // Parse to start to maintain indentation
    iLineSize = 0;
    stillgoing = TRUE;
    while(stillgoing)
    {
        cTmp = cLoadBuffer[read];
        stillgoing = FALSE;
        if(cTmp==' ')
        {
            read++;
            cLineBuffer[iLineSize]=cTmp; iLineSize++;
            stillgoing = TRUE;
        }
    }


    // Parse to comment or end of line
    foundtext = FALSE;
    stillgoing = TRUE;
    while(stillgoing)
    {
        cTmp = cLoadBuffer[read];  read++;
        if(cTmp=='\t')
           cTmp = ' ';
        if(cTmp != ' ' && cTmp != 0x0d && cTmp != 0x0a &&
           (cTmp != '/' || cLoadBuffer[read]!='/'))
            foundtext=TRUE;
        cLineBuffer[iLineSize]=cTmp;
        if(cTmp!=' ' || (cLoadBuffer[read]!=' ' && cLoadBuffer[read]!='\t'))
          iLineSize++;
        if(cTmp == 0x0d || cTmp == 0x0a)
            stillgoing=FALSE;
        if(cTmp == '/' && cLoadBuffer[read] == '/')
            stillgoing=FALSE;
    }
    iLineSize--;  cLineBuffer[iLineSize]=0;
    if(iLineSize>=1)
    {
        if(cLineBuffer[iLineSize-1]==' ')
        {
            iLineSize--;  cLineBuffer[iLineSize]=0;
        }
    }
    iLineSize++;


    // Parse to end of line
    stillgoing=TRUE;
    read--;
    while(stillgoing)
    {
        cTmp = cLoadBuffer[read];  read++;
        if(cTmp == 0x0a || cTmp==0x0d)
            stillgoing=FALSE;
    }

    if(foundtext==FALSE)
    {
        iLineSize=0;
    }


    return read;
}

//------------------------------------------------------------------------------
int load_parsed_line(int read)
{
    // ZZ> This function loads a line into the line buffer
    unsigned char cTmp;


    // Parse to start to maintain indentation
    iLineSize = 0;
    cTmp=cLoadBuffer[read];
    while(cTmp!=0)
    {
        cLineBuffer[iLineSize]=cTmp;  iLineSize++;
        read++;  cTmp = cLoadBuffer[read];
    }
    cLineBuffer[iLineSize]=0;  iLineSize++; read++;
    return read;
}

//------------------------------------------------------------------------------
void surround_space(int position)
{
    insert_space(position+1);
    if(position>0)
    {
        if(cLineBuffer[position-1]!=' ')
        {
            insert_space(position);
        }
    }
}

//------------------------------------------------------------------------------
void parse_null_terminate_comments()
{
    // ZZ> This function removes comments and endline codes, replacing
    //     them with a 0
    int read, write;


    read = 0;
    write = 0;
    while(read < iLoadSize)
    {
        read=load_one_line(read);
        if(iLineSize>2)
        {
            copy_one_line(write);
            write+=iLineSize;
        }
    }
}

//------------------------------------------------------------------------------
int get_indentation()
{
    // ZZ> This function returns the number of starting spaces in a line
    int cnt;
    unsigned char cTmp;

    cnt = 0;
    cTmp=cLineBuffer[cnt];
    while(cTmp==' ')
    {
        cnt++;
        cTmp=cLineBuffer[cnt];
    }
    cnt=cnt>>1;
    if(cnt > 15)
    {
        if(globalparseerr)
        {
            fprintf(globalparseerr, "  %s - %d levels of indentation\n", globalparsename, cnt + 1);
        }
        parseerror = TRUE;
        cnt = 15;
    }
    return cnt;
}

//------------------------------------------------------------------------------
void fix_operators()
{
    // ZZ> This function puts spaces around operators to seperate words
    //     better
    int cnt;
    unsigned char cTmp;

    cnt = 0;
    while(cnt < iLineSize)
    {
        cTmp=cLineBuffer[cnt];
        if(cTmp=='+' || cTmp=='-' || cTmp=='/' || cTmp=='*' ||
           cTmp=='%' || cTmp=='>' || cTmp=='<' || cTmp=='&' ||
           cTmp=='=')
        {
            surround_space(cnt);
            cnt++; 
        }
        cnt++;
    }
}

//------------------------------------------------------------------------------
int starts_with_capital_letter()
{
    // ZZ> This function returns TRUE if the line starts with a capital
    int cnt;
    unsigned char cTmp;

    cnt = 0;
    cTmp=cLineBuffer[cnt];
    while(cTmp==' ')
    {
        cnt++;
        cTmp=cLineBuffer[cnt];
    }
    if(cTmp>='A'&&cTmp<='Z')
      return TRUE;
    return FALSE;
}

//------------------------------------------------------------------------------
unsigned int get_high_bits()
{
    // ZZ> This function looks at the first word and generates the high
    //     bit codes for it
    unsigned int highbits;

    highbits = get_indentation();
    if(starts_with_capital_letter())
    {
        highbits=highbits|16;
    }
    else
    {
    }
    highbits=highbits<<27;
    return highbits;
}

//------------------------------------------------------------------------------
int tell_code(int read)
{
    // ZZ> This function tells what code is being indexed by read, it
    //     will return the next spot to read from and stick the code number
    //     in iCodeIndex
    int cnt, wordsize, codecorrect;
    unsigned char cTmp;
    int idsz, test;
    char cWordBuffer[MAXCODENAMESIZE];


    // Check bounds
    iCodeIndex = MAXCODE;
    if(read >= iLineSize)  return read;


    // Skip spaces
    cTmp=cLineBuffer[read];
    while(cTmp==' ' || cTmp==0)
    {
        read++;
        cTmp=cLineBuffer[read];
    }
    if(read >= iLineSize)  return read;


    // Load the word into the other buffer
    wordsize = 0;
    while(cTmp!=' ' && cTmp!=0)
    {
        cWordBuffer[wordsize] = cTmp;  wordsize++;
        read++;
        cTmp=cLineBuffer[read];
    }
    cWordBuffer[wordsize] = 0;


    // Check for numeric constant
    if(cWordBuffer[0]>='0' && cWordBuffer[0]<='9')
    {
        sscanf(&cWordBuffer[0], "%d", &iCodeValueTmp);
        iCodeIndex=-1;
        return read;
    }


    // Check for IDSZ constant
    if(cWordBuffer[0]=='[')
    {
        idsz = IDSZNONE;
        cTmp = cWordBuffer[1]-'A';  idsz=idsz|(cTmp<<15);
        cTmp = cWordBuffer[2]-'A';  idsz=idsz|(cTmp<<10);
        cTmp = cWordBuffer[3]-'A';  idsz=idsz|(cTmp<<5);
        cTmp = cWordBuffer[4]-'A';  idsz=idsz|(cTmp);
        test = ('N'-'A'<<15)|('O'-'A'<<10)|('N'-'A'<<5)|('E'-'A');  // [NONE]
        if(idsz == test)
        {
            idsz = IDSZNONE;
        }
        iCodeValueTmp = idsz;
        iCodeIndex=-1;
        return read;
    }



    // Compare the word to all the codes
    codecorrect = FALSE;
    iCodeIndex = 0;
    while(iCodeIndex < iNumCode && !codecorrect)
    {
        codecorrect = FALSE;
        if(cWordBuffer[0] == cCodeName[iCodeIndex][0] && !codecorrect)
        {
            codecorrect = TRUE;
            cnt = 1;
            while(cnt < wordsize)
            {
                if(cWordBuffer[cnt]!=cCodeName[iCodeIndex][cnt])
                {
                    codecorrect=FALSE;
                    cnt = wordsize;
                }
                cnt++;
            }
            if(cnt < MAXCODENAMESIZE)
            {
                if(cCodeName[iCodeIndex][cnt]!=0)  codecorrect = FALSE;
            }
        }
        iCodeIndex++;
    }
    if(codecorrect)
    {
        iCodeIndex--;
        iCodeValueTmp=iCodeValue[iCodeIndex];
        if(cCodeType[iCodeIndex]=='C')
        {
            // Check for constants
            iCodeIndex = -1;
        }
    }
    else
    {
        // Throw out an error code if we're loggin' 'em
        if(cWordBuffer[0] != '=' || cWordBuffer[1] != 0)
        {
            if(globalparseerr)
            {
                fprintf(globalparseerr, "  %s - %s undefined\n", globalparsename, cWordBuffer);
            }
            parseerror = TRUE;
        }
    }
    return read;
}

//------------------------------------------------------------------------------
void add_code(unsigned int highbits)
{
    unsigned int value;

    if(iCodeIndex==-1)  highbits=highbits|0x80000000;
    if(iCodeIndex != MAXCODE)
    {
        value = highbits|iCodeValueTmp;
        iCompiledAis[iAisIndex]=value;
        iAisIndex++;
    }
}

//------------------------------------------------------------------------------
void parse_line_by_line()
{
    // ZZ> This function removes comments and endline codes, replacing
    //     them with a 0
    int read, line;
    unsigned int highbits;
    int parseposition;
    int operands;


    line = 0;
    read = 0;
    while(line < iNumLine)
    {
        read=load_parsed_line(read);
        fix_operators();
        highbits = get_high_bits();
        parseposition = 0;
        parseposition=tell_code(parseposition);  // VALUE
        add_code(highbits);
        iCodeValueTmp=0;  // SKIP FOR CONTROL CODES
        add_code(0);
        if((highbits&0x80000000)==0)
        {
            parseposition=tell_code(parseposition);  // EQUALS
            parseposition=tell_code(parseposition);  // VALUE
            add_code(0);
            operands = 1;
            while(parseposition<iLineSize)
            {
                parseposition=tell_code(parseposition);  // OPERATOR
                if(iCodeIndex==-1) iCodeIndex=1;
                else iCodeIndex=0;
                highbits=((iCodeValueTmp&15)<<27)|(iCodeIndex<<31);
                parseposition=tell_code(parseposition);  // VALUE
                add_code(highbits);
                if(iCodeIndex!=MAXCODE)
                  operands++;
            }
            iCompiledAis[iAisIndex-operands-1]=operands;  // Number of operands
        }
        line++;
    }
}

//------------------------------------------------------------------------------
unsigned int jump_goto(int index)
{
    // ZZ> This function figures out where to jump to on a fail based on the
    //     starting location and the following code.  The starting location
    //     should always be a function code with indentation
    unsigned int value;
    int targetindent, indent;


    value = iCompiledAis[index];  index+=2;
    targetindent = (value>>27)&15;
    indent = 100;
    while(indent>targetindent)
    {
        value = iCompiledAis[index];
        indent = (value>>27)&15;
        if(indent>targetindent)
        {
            // Was it a function
            if((value&0x80000000)!=0)
            {
                // Each function needs a jump
                index++;
                index++;
            }
            else
            {
                // Operations cover each operand
                index++;
                value = iCompiledAis[index];
                index++;
                index+=(value&255);
            }
        }
    }
    return index;
}

//------------------------------------------------------------------------------
void parse_jumps(int ainumber)
{
    // ZZ> This function sets up the fail jumps for the down and dirty code
    int index;
    unsigned int value, iTmp;


    index = iAisStartPosition[ainumber];
    value = iCompiledAis[index];
    while(value != 0x80000035)  // End Function
    {
        value = iCompiledAis[index];
        // Was it a function
        if((value&0x80000000)!=0)
        {
            // Each function needs a jump
            iTmp = jump_goto(index);
            index++;
            iCompiledAis[index]=iTmp;
            index++;
        }
        else
        {
            // Operations cover each operand
            index++;
            iTmp = iCompiledAis[index];
            index++;
            index+=(iTmp&255);
        }
    }
}

//------------------------------------------------------------------------------
void log_code(int ainumber, char* savename)
{
    // ZZ> This function shows the actual code, saving it in a file
    int index;
    unsigned int value;
    FILE* filewrite;

    filewrite = fopen(FILENAME(savename), "w");
    if(filewrite)
    {
        index = iAisStartPosition[ainumber];
        value = iCompiledAis[index];
        while(value != 0x80000035)  // End Function
        {
            value = iCompiledAis[index];
            fprintf(filewrite, "0x%08x--0x%08x\n", index, value);
            index++;
        }
        fclose(filewrite);
    }
	SDL_Quit();
}

//------------------------------------------------------------------------------
int ai_goto_colon(int read)
{
    // ZZ> This function goes to spot after the next colon
    unsigned char cTmp;

    cTmp = cLoadBuffer[read];
    while(cTmp!=':' && read < iLoadSize)
    {
        read++;  cTmp=cLoadBuffer[read];
    }
    if(read < iLoadSize)  read++;
    return read;
}

//------------------------------------------------------------------------------
void get_code(int read)
{
    // ZZ> This function gets code names and other goodies
    unsigned char cTmp;
    int iTmp;

    sscanf((char*) &cLoadBuffer[read], "%c%d%s", &cTmp, &iTmp, &cCodeName[iNumCode][0]);
    cCodeType[iNumCode]=cTmp;
    iCodeValue[iNumCode]=iTmp;
}

//------------------------------------------------------------------------------
void load_ai_codes(char* loadname)
{
    // ZZ> This function loads all of the function and variable names
    FILE* fileread;
    int read;

    iNumCode = 0;
    fileread = fopen(FILENAME(loadname), "rb");
    if(fileread)
    {
        iLoadSize = fread(&cLoadBuffer[0], 1, MD2MAXLOADSIZE, fileread);
        read = 0;
        read = ai_goto_colon(read);
        while(read!=iLoadSize)
        {
            get_code(read);
            iNumCode++;
            read = ai_goto_colon(read);
        }
        fclose(fileread);
    }
}

//------------------------------------------------------------------------------
int load_ai_script(char *loadname)
{
    // ZZ> This function loads a script to memory and
    //     returns FALSE if it fails to do so
    FILE* fileread;

    iNumLine = 0;
    globalparsename = loadname;  // For error logging in ParseErr.TXT
    fileread = fopen(FILENAME(loadname), "rb");
    if(fileread && iNumAis < MAXAI)
    {
        // Make room for the code
        iAisStartPosition[iNumAis]=iAisIndex;

        // Load into md2 load buffer
        iLoadSize = fread(&cLoadBuffer[0], 1, MD2MAXLOADSIZE, fileread);
        fclose(fileread);
        parse_null_terminate_comments();
        parse_line_by_line();
        parse_jumps(iNumAis);
        iNumAis++;
        return TRUE;
    }
    return FALSE;
}

//------------------------------------------------------------------------------
void reset_ai_script()
{
    // ZZ> This function starts ai loading in the right spot
    int cnt;

    iAisIndex=0;
    for (cnt = 0; cnt < MAXMODEL; cnt++)
        madai[cnt] = 0;

    iNumAis=0;
}

//--------------------------------------------------------------------------------------------
unsigned char run_function(unsigned int value, int character)
{
	// ZZ> This function runs a script function for the AI.
	//     It returns FALSE if the script should jump over the
	//     indented code that follows

	// Mask out the indentation
	unsigned short valuecode = value;

	// Assume that the function will pass, as most do
	unsigned char returncode = TRUE;

	unsigned short sTmp;
	float fTmp;
	int iTmp, tTmp;
	int distance, volume;
	unsigned int test;
	char szDebug[256];

    // Figure out which function to run
    switch(valuecode)
    {
        case FIFSPAWNED:
            // Proceed only if it's a new character
            returncode = ((chralert[character]&ALERTIFSPAWNED)!=0);
            break;
        case FIFTIMEOUT:
            // Proceed only if time alert is set
            returncode = (chraitime[character]==0);
            break;
        case FIFATWAYPOINT:
            // Proceed only if the character reached a waypoint
            returncode = ((chralert[character]&ALERTIFATWAYPOINT)!=0);
            break;
        case FIFATLASTWAYPOINT:
            // Proceed only if the character reached its last waypoint
            returncode = ((chralert[character]&ALERTIFATLASTWAYPOINT)!=0);
            break;
        case FIFATTACKED:
            // Proceed only if the character was damaged
            returncode = ((chralert[character]&ALERTIFATTACKED)!=0);
            break;
        case FIFBUMPED:
            // Proceed only if the character was bumped
            returncode = ((chralert[character]&ALERTIFBUMPED)!=0);
            break;
        case FIFORDERED:
            // Proceed only if the character was ordered
            returncode = ((chralert[character]&ALERTIFORDERED)!=0);
            break;
        case FIFCALLEDFORHELP:
            // Proceed only if the character was called for help
            returncode = ((chralert[character]&ALERTIFCALLEDFORHELP)!=0);
            break;
        case FSETCONTENT:
            // Set the content
            chraicontent[character] = valuetmpargument;
            break;
        case FIFKILLED:
            // Proceed only if the character's been killed
            returncode = ((chralert[character]&ALERTIFKILLED)!=0);
            break;
        case FIFTARGETKILLED:
            // Proceed only if the character's target has just died
            returncode = ((chralert[character]&ALERTIFTARGETKILLED)!=0);
            break;
        case FCLEARWAYPOINTS:
            // Clear out all waypoints
            chraigoto[character] = 0;
            chraigotoadd[character] = 0;
            chraigotox[character][0] = chrxpos[character];
            chraigotoy[character][0] = chrypos[character];
            break;
        case FADDWAYPOINT:
            // Add a waypoint to the waypoint list
            chraigotox[character][chraigotoadd[character]]=valuetmpx;
            chraigotoy[character][chraigotoadd[character]]=valuetmpy;
            chraigotoadd[character]++;
            if(chraigotoadd[character]>MAXWAY)  chraigotoadd[character]=MAXWAY-1;
            break;
        case FFINDPATH:
            // This function adds enough waypoints to get from one point to another
            // !!!BAD!!!
            break;
        case FCOMPASS:
            // This function changes tmpx and tmpy in a circlular manner according
            // to tmpturn and tmpdistance
            sTmp = (valuetmpturn+16384);
            valuetmpx = valuetmpx-turntosin[sTmp>>2]*valuetmpdistance;
            valuetmpy = valuetmpy-turntosin[valuetmpturn>>2]*valuetmpdistance;
            break;
        case FGETTARGETARMORPRICE:
            // This function gets the armor cost for the given skin
            sTmp = valuetmpargument & 3;
            valuetmpx = capskincost[chrmodel[chraitarget[character]]][sTmp];
            break;
        case FSETTIME:
            // This function resets the time
            chraitime[character]=valuetmpargument;
            break;
        case FGETCONTENT:
            // Get the content
            valuetmpargument = chraicontent[character];
            break;
        case FJOINTARGETTEAM:
            // This function allows the character to leave its own team and join another
            returncode = FALSE;
            if(chron[chraitarget[character]])
            {
                switch_team(character, chrteam[chraitarget[character]]);
                returncode = TRUE;
            }
        case FSETTARGETTONEARBYENEMY:
            // This function finds a nearby enemy, and proceeds only if there is one
            sTmp = get_nearby_target(character, FALSE, FALSE, TRUE, FALSE, IDSZNONE);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FSETTARGETTOTARGETLEFTHAND:
            // This function sets the target to the target's left item
            sTmp = chrholdingwhich[chraitarget[character]][0];
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FSETTARGETTOTARGETRIGHTHAND:
            // This function sets the target to the target's right item
            sTmp = chrholdingwhich[chraitarget[character]][1];
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FSETTARGETTOWHOEVERATTACKED:
            // This function sets the target to whoever attacked the character last,
            // failing for damage tiles
            if(chrattacklast[character]!=MAXCHR)
            {
                chraitarget[character]=chrattacklast[character];
            }
            else
            {
                returncode = FALSE;
            }
            break;
        case FSETTARGETTOWHOEVERBUMPED:
            // This function sets the target to whoever bumped into the
            // character last.  It never fails
            chraitarget[character]=chrbumplast[character];
            break;
        case FSETTARGETTOWHOEVERCALLEDFORHELP:
            // This function sets the target to whoever needs help
            chraitarget[character]=teamsissy[chrteam[character]];
            break;
        case FSETTARGETTOOLDTARGET:
            // This function reverts to the target with whom the script started
            chraitarget[character]=valueoldtarget;
            break;
        case FSETTURNMODETOVELOCITY:
            // This function sets the turn mode
            chrturnmode[character] = TURNMODEVELOCITY;
            break;
        case FSETTURNMODETOWATCH:
            // This function sets the turn mode
            chrturnmode[character] = TURNMODEWATCH;
            break;
        case FSETTURNMODETOSPIN:
            // This function sets the turn mode
            chrturnmode[character] = TURNMODESPIN;
            break;
        case FSETBUMPHEIGHT:
            // This function changes a character's bump height
            chrbumpheight[character] = valuetmpargument*chrfat[character];
            chrbumpheightsave[character] = valuetmpargument;
            break;
        case FIFTARGETHASID:
            // This function proceeds if ID matches tmpargument
            sTmp = chrmodel[chraitarget[character]];
            returncode = capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument;
            returncode = returncode | (capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument);
            break;
        case FIFTARGETHASITEMID:
            // This function proceeds if the target has a matching item in his/her pack
            returncode = FALSE;
            // Check the pack
            sTmp = chrnextinpack[chraitarget[character]];
            while(sTmp != MAXCHR)
            {
                if(capidsz[chrmodel[sTmp]][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[chrmodel[sTmp]][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    returncode = TRUE;
                    sTmp = MAXCHR;
                }
                else
                {
                    sTmp = chrnextinpack[sTmp];
                }
            }
            // Check left hand
            sTmp = chrholdingwhich[chraitarget[character]][0];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                    returncode = TRUE;
            }
            // Check right hand
            sTmp = chrholdingwhich[chraitarget[character]][1];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                    returncode = TRUE;
            }
            break;
        case FIFTARGETHOLDINGITEMID:
            // This function proceeds if ID matches tmpargument and returns the latch for the
            // hand in tmpargument
            returncode = FALSE;
            // Check left hand
            sTmp = chrholdingwhich[chraitarget[character]][0];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    valuetmpargument = LATCHBUTTONLEFT;
                    returncode = TRUE;
                }
            }
            // Check right hand
            sTmp = chrholdingwhich[chraitarget[character]][1];
            if(sTmp != MAXCHR && returncode == FALSE)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    valuetmpargument = LATCHBUTTONRIGHT;
                    returncode = TRUE;
                }
            }
            break;
        case FIFTARGETHASSKILLID:
            // This function proceeds if ID matches tmpargument
            returncode = (capidsz[chrmodel[chraitarget[character]]][IDSZSKILL]==(unsigned int) valuetmpargument);
            break;
        case FELSE:
            // This function fails if the last one was more indented
            if((valuelastindent&0x78000000)>(value&0x78000000))
                returncode = FALSE;
            break;
        case FRUN:
            reset_character_accel(character);
            break;
        case FWALK:
            reset_character_accel(character);
            chrmaxaccel[character] *= .66;
            break;
        case FSNEAK:
            reset_character_accel(character);
            chrmaxaccel[character] *= .33;
            break;
        case FDOACTION:
            // This function starts a new action, if it is valid for the model
            // It will fail if the action is invalid or if the character is doing
            // something else already
            returncode = FALSE;
            if(valuetmpargument < MAXACTION && chractionready[character])
            {
                if(madactionvalid[chrmodel[character]][valuetmpargument])
                {
                    chraction[character] = valuetmpargument;
                    chrlip[character] = 0;
                    chrlastframe[character] = chrframe[character];
                    chrframe[character] = madactionstart[chrmodel[character]][valuetmpargument];
                    chractionready[character] = FALSE;
                    returncode = TRUE;
                }
            }
            break;
        case FKEEPACTION:
            // This function makes the current animation halt on the last frame
            chrkeepaction[character] = TRUE;
            break;
        case FISSUEORDER:
            // This function issues an order to all teammates
            issue_order(character, valuetmpargument);
            break;
        case FDROPWEAPONS:
            // This funtion drops the character's in hand items/riders
            sTmp = chrholdingwhich[character][0];
            if(sTmp != MAXCHR)
            {
                detach_character_from_mount(sTmp, TRUE, TRUE);
                if(chrismount[character])
                {
                    chrzvel[sTmp] = DISMOUNTZVEL;
                    chrzpos[sTmp]+=DISMOUNTZVEL;
                    chrjumptime[sTmp]=JUMPDELAY;
                }
            }
            sTmp = chrholdingwhich[character][1];
            if(sTmp != MAXCHR)
            {
                detach_character_from_mount(sTmp, TRUE, TRUE);
                if(chrismount[character])
                {
                    chrzvel[sTmp] = DISMOUNTZVEL;
                    chrzpos[sTmp]+=DISMOUNTZVEL;
                    chrjumptime[sTmp]=JUMPDELAY;
                }
            }
            break;
        case FTARGETDOACTION:
            // This function starts a new action, if it is valid for the model
            // It will fail if the action is invalid or if the target is doing
            // something else already
            returncode = FALSE;
            if(chralive[chraitarget[character]])
            {
                if(valuetmpargument < MAXACTION && chractionready[chraitarget[character]])
                {
                    if(madactionvalid[chrmodel[chraitarget[character]]][valuetmpargument])
                    {
                        chraction[chraitarget[character]] = valuetmpargument;
                        chrlip[chraitarget[character]] = 0;
                        chrlastframe[chraitarget[character]] = chrframe[chraitarget[character]];
                        chrframe[chraitarget[character]] = madactionstart[chrmodel[chraitarget[character]]][valuetmpargument];
                        chractionready[chraitarget[character]] = FALSE;
                        returncode = TRUE;
                    }
                }
            }
            break;
        case FOPENPASSAGE:
            // This function opens the passage specified by tmpargument, failing if the
            // passage was already open
            returncode = open_passage(valuetmpargument);
            break;
        case FCLOSEPASSAGE:
            // This function closes the passage specified by tmpargument, and proceeds
            // only if the passage is clear of obstructions
            returncode = close_passage(valuetmpargument);
            break;
        case FIFPASSAGEOPEN:
            // This function proceeds only if the passage specified by tmpargument
            // is both valid and open
            returncode = FALSE;
            if(valuetmpargument < numpassage && valuetmpargument >= 0)
            {
                returncode = passopen[valuetmpargument];
            }
            break;
        case FGOPOOF:
            // This function flags the character to be removed from the game
            returncode = FALSE;
            if(chrisplayer[character]==FALSE)
            {
                returncode = TRUE;
                valuegopoof = TRUE;
            }
            break;
        case FCOSTTARGETITEMID:
            // This function checks if the target has a matching item, and poofs it
            returncode = FALSE;
            // Check the pack
            iTmp = MAXCHR;
            tTmp = chraitarget[character];
            sTmp = chrnextinpack[tTmp];
            while(sTmp != MAXCHR)
            {
                if(capidsz[chrmodel[sTmp]][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[chrmodel[sTmp]][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    returncode = TRUE;
                    iTmp = sTmp;
                    sTmp = MAXCHR;
                }
                else
                {
                    tTmp = sTmp;
                    sTmp = chrnextinpack[sTmp];
                }
            }
            // Check left hand
            sTmp = chrholdingwhich[chraitarget[character]][0];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    returncode = TRUE;
                    iTmp = chrholdingwhich[chraitarget[character]][0];
                }
            }
            // Check right hand
            sTmp = chrholdingwhich[chraitarget[character]][1];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    returncode = TRUE;
                    iTmp = chrholdingwhich[chraitarget[character]][1];
                }
            }
            if(returncode)
            {
                if(chrammo[iTmp]<=1)
                {
                    // Poof the item
                    if(chrinpack[iTmp])
                    {
                        // Remove from the pack
                        chrnextinpack[tTmp] = chrnextinpack[iTmp];
                        chrnuminpack[chraitarget[character]]--;
                        free_one_character(iTmp);
                    }
                    else
                    {
                        // Drop from hand
                        detach_character_from_mount(iTmp, TRUE, FALSE);
                        free_one_character(iTmp);
                    }
                }
                else
                {
                    // Cost one ammo
                    chrammo[iTmp]--;
                }
            }
            break;
        case FDOACTIONOVERRIDE:
            // This function starts a new action, if it is valid for the model
            // It will fail if the action is invalid
            returncode = FALSE;
            if(valuetmpargument < MAXACTION)
            {
                if(madactionvalid[chrmodel[character]][valuetmpargument])
                {
                    chraction[character] = valuetmpargument;
                    chrlip[character] = 0;
                    chrlastframe[character] = chrframe[character];
                    chrframe[character] = madactionstart[chrmodel[character]][valuetmpargument];
                    chractionready[character] = FALSE;
                    returncode = TRUE;
                }
            }
            break;
        case FIFHEALED:
            // Proceed only if the character was healed
            returncode = ((chralert[character]&ALERTIFHEALED)!=0);
            break;
        case FSENDMESSAGE:
            // This function sends a message to the players
            display_message(madmsgstart[chrmodel[character]]+valuetmpargument, character);
            break;
        case FCALLFORHELP:
            // This function issues a call for help
            call_for_help(character);
            break;
        case FADDIDSZ:
            // This function adds an idsz to the module's menu.txt file
            add_module_idsz(pickedmodule, valuetmpargument);
            break;
        case FSETSTATE:
            // This function sets the character's state variable
            chraistate[character] = valuetmpargument;
            break;
        case FGETSTATE:
            // This function reads the character's state variable
            valuetmpargument = chraistate[character];
            break;
        case FIFSTATEIS:
            // This function fails if the character's state is inequal to tmpargument
            returncode = (valuetmpargument == chraistate[character]);
            break;
        case FIFTARGETCANOPENSTUFF:
            // This function fails if the target can't open stuff
            returncode = chropenstuff[chraitarget[character]];
            break;
        case FIFGRABBED:
            // Proceed only if the character was picked up
            returncode = ((chralert[character]&ALERTIFGRABBED)!=0);
            break;
        case FIFDROPPED:
            // Proceed only if the character was dropped
            returncode = ((chralert[character]&ALERTIFDROPPED)!=0);
            break;
        case FSETTARGETTOWHOEVERISHOLDING:
            // This function sets the target to the character's mount or holder,
            // failing if the character has no mount or holder
            returncode = FALSE;
            if(chrattachedto[character] < MAXCHR)
            {
                chraitarget[character] = chrattachedto[character];
                returncode = TRUE;
            }
            break;
        case FDAMAGETARGET:
            // This function applies little bit of love to the character's target.
            // The amount is set in tmpargument
            damage_character(chraitarget[character], 0, valuetmpargument, 1, chrdamagetargettype[character], chrteam[character], character, DAMFXBLOC);
            break;
        case FIFXISLESSTHANY:
            // Proceed only if tmpx is less than tmpy
            returncode = (valuetmpx < valuetmpy);
            break;
        case FSETWEATHERTIME:
            // Set the weather timer
            weathertimereset = valuetmpargument;
            weathertime = valuetmpargument;
            break;
        case FGETBUMPHEIGHT:
            // Get the characters bump height
            valuetmpargument = chrbumpheight[character];
            break;
        case FIFREAFFIRMED:
            // Proceed only if the character was reaffirmed
            returncode = ((chralert[character]&ALERTIFREAFFIRMED)!=0);
            break;
        case FUNKEEPACTION:
            // This function makes the current animation start again
            chrkeepaction[character] = FALSE;
            break;
        case FIFTARGETISONOTHERTEAM:
            // This function proceeds only if the target is on another team
            returncode = (chralive[chraitarget[character]] && chrteam[chraitarget[character]]!=chrteam[character]);
            break;
        case FIFTARGETISONHATEDTEAM:
            // This function proceeds only if the target is on an enemy team
            returncode = (chralive[chraitarget[character]] && teamhatesteam[chrteam[character]][chrteam[chraitarget[character]]] && chrinvictus[chraitarget[character]] == FALSE );
            break;
        case FPRESSLATCHBUTTON:
            // This function sets the latch buttons
            chrlatchbutton[character] = chrlatchbutton[character]|valuetmpargument;
            break;
        case FSETTARGETTOTARGETOFLEADER:
            // This function sets the character's target to the target of its leader,
            // or it fails with no change if the leader is dead
            returncode = FALSE;
            if(teamleader[chrteam[character]]!=NOLEADER)
            {
                chraitarget[character] = chraitarget[teamleader[chrteam[character]]];
                returncode = TRUE;
            }
            break;
        case FIFLEADERKILLED:
            // This function proceeds only if the character's leader has just died
            returncode = ((chralert[character]&ALERTIFLEADERKILLED)!=0);
            break;
        case FBECOMELEADER:
            // This function makes the character the team leader
            teamleader[chrteam[character]]=character;
            break;
        case FCHANGETARGETARMOR:
            // This function sets the target's armor type and returns the old type
            // as tmpargument and the new type as tmpx
            iTmp = chrtexture[chraitarget[character]]-madskinstart[chrmodel[chraitarget[character]]];
            valuetmpx = change_armor(chraitarget[character], valuetmpargument);
            valuetmpargument = iTmp;  // The character's old armor
            break;
        case FGIVEMONEYTOTARGET:
            // This function transfers money from the character to the target, and sets
            // tmpargument to the amount transferred
            iTmp = chrmoney[character];
            tTmp = chrmoney[chraitarget[character]];
            iTmp-=valuetmpargument;
            tTmp+=valuetmpargument;
            if(iTmp < 0) { tTmp+=iTmp;  valuetmpargument+=iTmp;  iTmp = 0; }
            if(tTmp < 0) { iTmp+=tTmp;  valuetmpargument+=tTmp;  tTmp = 0; }
            if(iTmp > MAXMONEY) { iTmp = MAXMONEY; }
            if(tTmp > MAXMONEY) { tTmp = MAXMONEY; }
            chrmoney[character] = iTmp;
            chrmoney[chraitarget[character]] = tTmp;
            break;
        case FDROPKEYS:
            drop_keys(character);
            break;
        case FIFLEADERISALIVE:
            // This function fails if there is no team leader
            returncode = (teamleader[chrteam[character]]!=NOLEADER);
            break;
        case FIFTARGETISOLDTARGET:
            // This function returns FALSE if the target has changed
            returncode = (chraitarget[character]==valueoldtarget);
            break;
        case FSETTARGETTOLEADER:
            // This function fails if there is no team leader
            if(teamleader[chrteam[character]]==NOLEADER)
            {
                returncode = FALSE;
            }
            else
            {
                chraitarget[character] = teamleader[chrteam[character]];
            }
            break;
        case FSPAWNCHARACTER:
            // This function spawns a character, failing if x,y is invalid
            sTmp = spawn_one_character(valuetmpx, valuetmpy, 0, chrmodel[character], chrteam[character], 0, valuetmpturn, NULL, MAXCHR);
            returncode = FALSE;
            if(sTmp < MAXCHR)
            {
                if(__chrhitawall(sTmp))
                {
                    free_one_character(sTmp);
                }
                else
                {
                    tTmp = chrturnleftright[character]>>2;
                    chrxvel[sTmp]+=turntosin[(tTmp+12288)&16383]*valuetmpdistance;
                    chryvel[sTmp]+=turntosin[(tTmp+8192)&16383]*valuetmpdistance;
                    chrpassage[sTmp] = chrpassage[character];
                    chriskursed[sTmp] = FALSE;
                    chraichild[character] = sTmp;
                    chraiowner[sTmp] = chraiowner[character];
                    returncode = TRUE;
                }
            }
            break;
        case FRESPAWNCHARACTER:
            // This function respawns the character at its starting location
            respawn_character(character);
            break;
        case FCHANGETILE:
            // This function changes the floor image under the character
            meshtile[chronwhichfan[character]]=valuetmpargument&(255);
            break;
        case FIFUSED:
            // This function proceeds only if the character has been used
            returncode = ((chralert[character]&ALERTIFUSED)!=0);
            break;
        case FDROPMONEY:
            // This function drops some of a character's money
            drop_money(character, valuetmpargument);
            break;
        case FSETOLDTARGET:
            // This function sets the old target to the current target
            valueoldtarget = chraitarget[character];
            break;
        case FDETACHFROMHOLDER:
            // This function drops the character, failing only if it was not held
            if(chrattachedto[character]!=MAXCHR)
            {
                detach_character_from_mount(character, TRUE, TRUE);
            }
            else
            {
                returncode = FALSE;
            }
            break;
        case FIFTARGETHASVULNERABILITYID:
            // This function proceeds if ID matches tmpargument
            returncode = (capidsz[chrmodel[chraitarget[character]]][IDSZVULNERABILITY]==(unsigned int) valuetmpargument);
            break;
        case FCLEANUP:
            // This function issues the clean up order to all teammates
            issue_clean(character);
            break;
        case FIFCLEANEDUP:
            // This function proceeds only if the character was told to clean up
            returncode = ((chralert[character]&ALERTIFCLEANEDUP)!=0);
            break;
        case FIFSITTING:
            // This function proceeds if the character is riding another
            returncode = (chrattachedto[character]!=MAXCHR);
            break;
        case FIFTARGETISHURT:
            // This function passes only if the target is hurt and alive
            if(chralive[chraitarget[character]]==FALSE || chrlife[chraitarget[character]] > chrlifemax[chraitarget[character]]-HURTDAMAGE)
                returncode = FALSE;
            break;
        case FIFTARGETISAPLAYER:
            // This function proceeds only if the target is a player ( may not be local )
            returncode = chrisplayer[chraitarget[character]];
            break;
        case FPLAYSOUND:
            // This function plays a sound
            if(chroldz[character] > PITNOSOUND)
            {
                distance = ABS(camtrackx-chroldx[character])+ABS(camtracky-chroldy[character]);
                volume = -distance;
                volume = volume<<VOLSHIFT;
                if(volume > VOLMIN && moduleactive)
                {

                    play_sound_pvf(capwaveindex[chrmodel[character]][valuetmpargument], PANMID, volume, valuetmpdistance);

                }
            }
            break;
        case FSPAWNPARTICLE:
            // This function spawns a particle
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            tTmp = spawn_one_particle(chrxpos[character], chrypos[character], chrzpos[character], chrturnleftright[character], chrmodel[character], valuetmpargument, character, valuetmpdistance, chrteam[character], tTmp, 0, MAXCHR);
            if(tTmp!=MAXPRT)
            {
                // Detach the particle
                attach_particle_to_character(tTmp, character, valuetmpdistance);
                prtattachedtocharacter[tTmp]=MAXCHR;
                // Correct X, Y, Z spacing
                prtxpos[tTmp]+=valuetmpx;
                prtypos[tTmp]+=valuetmpy;
                prtzpos[tTmp]+=pipzspacingbase[prtpip[tTmp]];
                // Don't spawn in walls
                if(__prthitawall(tTmp))
                {
                    prtxpos[tTmp] = chrxpos[character];
                    if(__prthitawall(tTmp))
                    {
                        prtypos[tTmp] = chrypos[character];
                    }
                }
            }
            break;
        case FIFTARGETISALIVE:
            // This function proceeds only if the target is alive
            returncode = (chralive[chraitarget[character]]==TRUE);
            break;
        case FSTOP:
            chrmaxaccel[character] = 0;
            break;
        case FDISAFFIRMCHARACTER:
            disaffirm_attached_particles(character);
            break;
        case FREAFFIRMCHARACTER:
            reaffirm_attached_particles(character);
            break;
        case FIFTARGETISSELF:
            // This function proceeds only if the target is the character too
            returncode = (chraitarget[character]==character);
            break;
        case FIFTARGETISMALE:
            // This function proceeds only if the target is male
            returncode = (chrgender[character]==GENMALE);
            break;
        case FIFTARGETISFEMALE:
            // This function proceeds only if the target is female
            returncode = (chrgender[character]==GENFEMALE);
            break;
        case FSETTARGETTOSELF:
            // This function sets the target to the character
            chraitarget[character] = character;
            break;
        case FSETTARGETTORIDER:
            // This function sets the target to the character's left/only grip weapon,
            // failing if there is none
            if(chrholdingwhich[character][0]==MAXCHR)
            {
                returncode = FALSE;
            }
            else
            {
                chraitarget[character] = chrholdingwhich[character][0];
            }
            break;
        case FGETATTACKTURN:
            // This function sets tmpturn to the direction of the last attack
            valuetmpturn = chrdirectionlast[character];
            break;
        case FGETDAMAGETYPE:
            // This function gets the last type of damage
            valuetmpargument = chrdamagetypelast[character];
            break;
        case FBECOMESPELL:
            // This function turns the spellbook character into a spell based on its
            // content
            chrmoney[character] = (chrtexture[character]-madskinstart[chrmodel[character]])&3;
            change_character(character, chraicontent[character], 0, LEAVENONE);
            chraicontent[character] = 0;  // Reset so it doesn't mess up
            chraistate[character] = 0;  // Reset so it doesn't mess up
            changed = TRUE;
            break;
        case FBECOMESPELLBOOK:
            // This function turns the spell into a spellbook, and sets the content
            // accordingly
            chraicontent[character] = chrmodel[character];
            change_character(character, SPELLBOOK, chrmoney[character]&3, LEAVENONE);
            chraistate[character] = 0;  // Reset so it doesn't burn up
            changed = TRUE;
            break;
        case FIFSCOREDAHIT:
            // Proceed only if the character scored a hit
            returncode = ((chralert[character]&ALERTIFSCOREDAHIT)!=0);
            break;
        case FIFDISAFFIRMED:
            // Proceed only if the character was disaffirmed
            returncode = ((chralert[character]&ALERTIFDISAFFIRMED)!=0);
            break;
        case FTRANSLATEORDER:
            // This function gets the order and sets tmpx, tmpy, tmpargument and the 
            // target ( if valid )
            sTmp = chrorder[character]>>24;
            if(sTmp < MAXCHR)
            {
                chraitarget[character] = sTmp;
            }
            valuetmpx = ((chrorder[character]>>14)&1023)<<6;
            valuetmpy = ((chrorder[character]>>4)&1023)<<6;
            valuetmpargument = chrorder[character]&15;
            break;
        case FSETTARGETTOWHOEVERWASHIT:
            // This function sets the target to whoever the character hit last,
            chraitarget[character]=chrhitlast[character];
            break;
        case FSETTARGETTOWIDEENEMY:
            // This function finds an enemy, and proceeds only if there is one
            sTmp = get_wide_target(character, FALSE, FALSE, TRUE, FALSE, IDSZNONE, FALSE);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FIFCHANGED:
            // Proceed only if the character was polymorphed
            returncode = ((chralert[character]&ALERTIFCHANGED)!=0);
            break;
        case FIFINWATER:
            // Proceed only if the character got wet
            returncode = ((chralert[character]&ALERTIFINWATER)!=0);
            break;
        case FIFBORED:
            // Proceed only if the character is bored
            returncode = ((chralert[character]&ALERTIFBORED)!=0);
            break;
        case FIFTOOMUCHBAGGAGE:
            // Proceed only if the character tried to grab too much
            returncode = ((chralert[character]&ALERTIFTOOMUCHBAGGAGE)!=0);
            break;
        case FIFGROGGED:
            // Proceed only if the character was grogged
            returncode = ((chralert[character]&ALERTIFGROGGED)!=0);
            break;
        case FIFDAZED:
            // Proceed only if the character was dazed
            returncode = ((chralert[character]&ALERTIFDAZED)!=0);
            break;
        case FIFTARGETHASSPECIALID:
            // This function proceeds if ID matches tmpargument
            returncode = (capidsz[chrmodel[chraitarget[character]]][IDSZSPECIAL]==(unsigned int) valuetmpargument);
            break;
        case FPRESSTARGETLATCHBUTTON:
            // This function sets the target's latch buttons
            chrlatchbutton[chraitarget[character]] = chrlatchbutton[chraitarget[character]]|valuetmpargument;
            break;
        case FIFINVISIBLE:
            // This function passes if the character is invisible
            returncode = (chralpha[character]<=INVISIBLE)||(chrlight[character]<=INVISIBLE);
            break;
        case FIFARMORIS:
            // This function passes if the character's skin is tmpargument
            tTmp = chrtexture[character]-madskinstart[chrmodel[character]];
            returncode = (tTmp==valuetmpargument);
            break;
        case FGETTARGETGROGTIME:
            // This function returns tmpargument as the grog time, and passes if it is not 0
            valuetmpargument = chrgrogtime[character];
            returncode = (valuetmpargument!=0);
            break;
        case FGETTARGETDAZETIME:
            // This function returns tmpargument as the daze time, and passes if it is not 0
            valuetmpargument = chrdazetime[character];
            returncode = (valuetmpargument!=0);
            break;
        case FSETDAMAGETYPE:
            // This function sets the bump damage type
            chrdamagetargettype[character] = valuetmpargument&(MAXDAMAGETYPE-1);
            break;
        case FSETWATERLEVEL:
            // This function raises and lowers the module's water
            fTmp = (valuetmpargument/10.0) - waterdouselevel;
            watersurfacelevel+=fTmp;
            waterdouselevel+=fTmp;
            for (iTmp = 0; iTmp < MAXWATERLAYER; iTmp++)
                waterlayerz[iTmp]+=fTmp;
            break;
        case FENCHANTTARGET:
            // This function enchants the target
            sTmp = spawn_enchant(chraiowner[character], chraitarget[character], character, MAXENCHANT, MAXMODEL);
            returncode = (sTmp != MAXENCHANT);
            break;
        case FENCHANTCHILD:
            // This function can be used with SpawnCharacter to enchant the
            // newly spawned character
            sTmp = spawn_enchant(chraiowner[character], chraichild[character], character, MAXENCHANT, MAXMODEL);
            returncode = (sTmp != MAXENCHANT);
            break;
        case FTELEPORTTARGET:
            // This function teleports the target to the X, Y location, failing if the
            // location is off the map or blocked
            returncode = FALSE;
            if(valuetmpx > EDGE && valuetmpy > EDGE && valuetmpx < meshedgex-EDGE && valuetmpy < meshedgey-EDGE)
            {
                // Yeah!  It worked!
                sTmp = chraitarget[character];
                detach_character_from_mount(sTmp, TRUE, FALSE);
                chroldx[sTmp] = chrxpos[sTmp];
                chroldy[sTmp]= chrypos[sTmp];
                chrxpos[sTmp] = valuetmpx;
                chrypos[sTmp] = valuetmpy;
                if(__chrhitawall(sTmp))
                {
                    // No it didn't...
                    chrxpos[sTmp] = chroldx[sTmp];
                    chrypos[sTmp] = chroldy[sTmp];
                    returncode = FALSE;
                }
                else
                {
                    chroldx[sTmp] = chrxpos[sTmp];
                    chroldy[sTmp]= chrypos[sTmp];
                    returncode = TRUE;
                }
            }
            break;
        case FGIVEEXPERIENCETOTARGET:
            // This function gives the target some experience, xptype from distance,
            // amount from argument...
            give_experience(chraitarget[character], valuetmpargument, valuetmpdistance);
            break;
        case FINCREASEAMMO:
            // This function increases the ammo by one
            if(chrammo[character] < chrammomax[character])
            {
                chrammo[character]++;
            }
            break;
        case FUNKURSETARGET:
            // This function unkurses the target
            chriskursed[chraitarget[character]] = FALSE;
            break;
        case FGIVEEXPERIENCETOTARGETTEAM:
            // This function gives experience to everyone on the target's team
            give_team_experience(chrteam[chraitarget[character]], valuetmpargument, valuetmpdistance);
            break;
        case FIFUNARMED:
            // This function proceeds if the character has no item in hand
            returncode = (chrholdingwhich[character][0] == MAXCHR && chrholdingwhich[character][1] == MAXCHR);
            break;
        case FRESTOCKTARGETAMMOIDALL:
            // This function restocks the ammo of every item the character is holding,
            // if the item matches the ID given ( parent or child type )
            iTmp = 0;  // Amount of ammo given
            sTmp = chrholdingwhich[chraitarget[character]][0];
            iTmp += restock_ammo(sTmp, valuetmpargument);
            sTmp = chrholdingwhich[chraitarget[character]][1];
            iTmp += restock_ammo(sTmp, valuetmpargument);
            sTmp = chrnextinpack[chraitarget[character]];
            while(sTmp != MAXCHR)
            {
                iTmp += restock_ammo(sTmp, valuetmpargument);
                sTmp = chrnextinpack[sTmp];
            }
            valuetmpargument = iTmp;
            returncode = (iTmp != 0);
            break;
        case FRESTOCKTARGETAMMOIDFIRST:
            // This function restocks the ammo of the first item the character is holding,
            // if the item matches the ID given ( parent or child type )
            iTmp = 0;  // Amount of ammo given
            sTmp = chrholdingwhich[chraitarget[character]][0];
            iTmp += restock_ammo(sTmp, valuetmpargument);
            if(iTmp == 0)
            {
                sTmp = chrholdingwhich[chraitarget[character]][1];
                iTmp += restock_ammo(sTmp, valuetmpargument);
                if(iTmp == 0)
                {
                    sTmp = chrnextinpack[chraitarget[character]];
                    while(sTmp != MAXCHR && iTmp == 0)
                    {
                        iTmp += restock_ammo(sTmp, valuetmpargument);
                        sTmp = chrnextinpack[sTmp];
                    }
                }
            }
            valuetmpargument = iTmp;
            returncode = (iTmp != 0);
            break;
        case FFLASHTARGET:
            // This function flashes the character
            flash_character(chraitarget[character], 255);
            break;
        case FSETREDSHIFT:
            // This function alters a character's coloration
            chrredshift[character] = valuetmpargument;
            break;
        case FSETGREENSHIFT:
            // This function alters a character's coloration
            chrgrnshift[character] = valuetmpargument;
            break;
        case FSETBLUESHIFT:
            // This function alters a character's coloration
            chrblushift[character] = valuetmpargument;
            break;
        case FSETLIGHT:
            // This function alters a character's transparency
            chrlight[character] = valuetmpargument;
            break;
        case FSETALPHA:
            // This function alters a character's transparency
            chralpha[character] = valuetmpargument;
            break;
        case FIFHITFROMBEHIND:
            // This function proceeds if the character was attacked from behind
            returncode = FALSE;
            if(chrdirectionlast[character] >= BEHIND-8192 && chrdirectionlast[character] < BEHIND+8192)
                returncode = TRUE;
            break;
        case FIFHITFROMFRONT:
            // This function proceeds if the character was attacked from the front
            returncode = FALSE;
            if(chrdirectionlast[character] >= 49152+8192 || chrdirectionlast[character] < FRONT+8192)
                returncode = TRUE;
            break;
        case FIFHITFROMLEFT:
            // This function proceeds if the character was attacked from the left
            returncode = FALSE;
            if(chrdirectionlast[character] >= LEFT-8192 && chrdirectionlast[character] < LEFT+8192)
                returncode = TRUE;
            break;
        case FIFHITFROMRIGHT:
            // This function proceeds if the character was attacked from the right
            returncode = FALSE;
            if(chrdirectionlast[character] >= RIGHT-8192 && chrdirectionlast[character] < RIGHT+8192)
                returncode = TRUE;
            break;
        case FIFTARGETISONSAMETEAM:
            // This function proceeds only if the target is on another team
            returncode = FALSE;
            if(chrteam[chraitarget[character]]==chrteam[character])
                returncode = TRUE;
            break;
        case FKILLTARGET:
            // This function kills the target
            kill_character(chraitarget[character], character);
            break;
        case FUNDOENCHANT:
            // This function undoes the last enchant
            returncode = (chrundoenchant[character]!=MAXENCHANT);
            remove_enchant(chrundoenchant[character]);
            break;
        case FGETWATERLEVEL:
            // This function gets the douse level for the water, returning it in tmpargument
            valuetmpargument = waterdouselevel*10;
            break;
        case FCOSTTARGETMANA:
            // This function costs the target some mana
            returncode = cost_mana(chraitarget[character], valuetmpargument, character);
            break;
        case FIFTARGETHASANYID:
            // This function proceeds only if one of the target's IDSZ's matches tmpargument
            returncode = 0;
            tTmp = 0;
            while(tTmp < MAXIDSZ)
            {
                returncode |= (capidsz[chrmodel[chraitarget[character]]][tTmp]==(unsigned int) valuetmpargument);
                tTmp++;
            }
            break;
        case FSETBUMPSIZE:
            // This function sets the character's bump size
            fTmp = chrbumpsizebig[character];
            fTmp = fTmp/chrbumpsize[character];  // 1.5 or 2.0
            chrbumpsize[character] = valuetmpargument*chrfat[character];
            chrbumpsizebig[character] = fTmp*chrbumpsize[character];
            chrbumpsizesave[character] = valuetmpargument;
            chrbumpsizebigsave[character] = fTmp*chrbumpsizesave[character];
            break;
        case FIFNOTDROPPED:
            // This function passes if a kursed item could not be dropped
            returncode = ((chralert[character]&ALERTIFNOTDROPPED)!=0);
            break;
        case FIFYISLESSTHANX:
            // This function passes only if tmpy is less than tmpx
            returncode = (valuetmpy < valuetmpx);
            break;
        case FSETFLYHEIGHT:
            // This function sets a character's fly height
            chrflyheight[character] = valuetmpargument;
            break;
        case FIFBLOCKED:
            // This function passes if the character blocked an attack
            returncode = ((chralert[character]&ALERTIFBLOCKED)!=0);
            break;
        case FIFTARGETISDEFENDING:
            returncode = (chraction[chraitarget[character]] >= ACTIONPA && chraction[chraitarget[character]] <= ACTIONPD);
            break;
        case FIFTARGETISATTACKING:
            returncode = (chraction[chraitarget[character]] >= ACTIONUA && chraction[chraitarget[character]] <= ACTIONFD);
            break;
        case FIFSTATEIS0:
            returncode = (0 == chraistate[character]);
            break;
        case FIFSTATEIS1:
            returncode = (1 == chraistate[character]);
            break;
        case FIFSTATEIS2:
            returncode = (2 == chraistate[character]);
            break;
        case FIFSTATEIS3:
            returncode = (3 == chraistate[character]);
            break;
        case FIFSTATEIS4:
            returncode = (4 == chraistate[character]);
            break;
        case FIFSTATEIS5:
            returncode = (5 == chraistate[character]);
            break;
        case FIFSTATEIS6:
            returncode = (6 == chraistate[character]);
            break;
        case FIFSTATEIS7:
            returncode = (7 == chraistate[character]);
            break;
        case FIFCONTENTIS:
            returncode = (valuetmpargument == chraicontent[character]);
            break;
        case FSETTURNMODETOWATCHTARGET:
            // This function sets the turn mode
            chrturnmode[character] = TURNMODEWATCHTARGET;
            break;
        case FIFSTATEISNOT:
            returncode = (valuetmpargument != chraistate[character]);
            break;
        case FIFXISEQUALTOY:
            returncode = (valuetmpx == valuetmpy);
            break;
        case FDEBUGMESSAGE:
            // This function spits out a debug message
            sprintf(szDebug, "aistate %d, aicontent %d, target %d", chraistate[character], chraicontent[character], chraitarget[character]);
            debug_message(szDebug);
            sprintf(szDebug, "tmpx %d, tmpy %d", valuetmpx, valuetmpy);
            debug_message(szDebug);
            sprintf(szDebug, "tmpdistance %d, tmpturn %d", valuetmpdistance, valuetmpturn);
            debug_message(szDebug);
            sprintf(szDebug, "tmpargument %d, selfturn %d", valuetmpargument, chrturnleftright[character]);
            debug_message(szDebug);
            break;
        case FBLACKTARGET:
            // This function makes the target flash black
            flash_character(chraitarget[character], 0);
            break;
        case FSENDMESSAGENEAR:
            // This function sends a message if the camera is in the nearby area.
            iTmp = ABS(chroldx[character]-camtrackx) + ABS(chroldy[character]-camtracky);
            if(iTmp < MSGDISTANCE)
                display_message(madmsgstart[chrmodel[character]]+valuetmpargument, character);
            break;
        case FIFHITGROUND:
            // This function passes if the character just hit the ground
            returncode = ((chralert[character]&ALERTIFHITGROUND)!=0);
            break;
        case FIFNAMEISKNOWN:
            // This function passes if the character's name is known
            returncode = chrnameknown[character];
            break;
        case FIFUSAGEISKNOWN:
            // This function passes if the character's usage is known
            returncode = capusageknown[chrmodel[character]];
            break;
        case FIFHOLDINGITEMID:
            // This function passes if the character is holding an item with the IDSZ given
            // in tmpargument, returning the latch to press to use it
            returncode = FALSE;
            // Check left hand
            sTmp = chrholdingwhich[character][0];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    valuetmpargument = LATCHBUTTONLEFT;
                    returncode = TRUE;
                }
            }
            // Check right hand
            sTmp = chrholdingwhich[character][1];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capidsz[sTmp][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[sTmp][IDSZTYPE]==(unsigned int) valuetmpargument)
                {
                    valuetmpargument = LATCHBUTTONRIGHT;
                    if(returncode == TRUE)  valuetmpargument = LATCHBUTTONLEFT+(rand()&1);
                    returncode = TRUE;
                }
            }
            break;
        case FIFHOLDINGRANGEDWEAPON:
            // This function passes if the character is holding a ranged weapon, returning
            // the latch to press to use it.  This also checks ammo/ammoknown.
            returncode = FALSE;
            valuetmpargument = 0;
            // Check left hand
            tTmp = chrholdingwhich[character][0];
            if(tTmp != MAXCHR)
            {
                sTmp = chrmodel[tTmp];
                if(capisranged[sTmp] && (chrammomax[tTmp]==0 || (chrammo[tTmp]!=0 && chrammoknown[tTmp])))
                {
                    valuetmpargument = LATCHBUTTONLEFT;
                    returncode = TRUE;
                }
            }
            // Check right hand
            tTmp = chrholdingwhich[character][1];
            if(tTmp != MAXCHR)
            {
                sTmp = chrmodel[tTmp];
                if(capisranged[sTmp] && (chrammomax[tTmp]==0 || (chrammo[tTmp]!=0 && chrammoknown[tTmp])))
                {
                    if(valuetmpargument == 0 || (allframe&1))
                    {
                        valuetmpargument = LATCHBUTTONRIGHT;
                        returncode = TRUE;
                    }
                }
            }
            break;
        case FIFHOLDINGMELEEWEAPON:
            // This function passes if the character is holding a melee weapon, returning
            // the latch to press to use it
            returncode = FALSE;
            valuetmpargument = 0;
            // Check left hand
            sTmp = chrholdingwhich[character][0];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capisranged[sTmp]==FALSE && capweaponaction[sTmp]!=ACTIONPA)
                {
                    valuetmpargument = LATCHBUTTONLEFT;
                    returncode = TRUE;
                }
            }
            // Check right hand
            sTmp = chrholdingwhich[character][1];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capisranged[sTmp]==FALSE && capweaponaction[sTmp]!=ACTIONPA)
                {
                    if(valuetmpargument == 0 || (allframe&1))
                    {
                        valuetmpargument = LATCHBUTTONRIGHT;
                        returncode = TRUE;
                    }
                }
            }
            break;
        case FIFHOLDINGSHIELD:
            // This function passes if the character is holding a shield, returning the
            // latch to press to use it
            returncode = FALSE;
            valuetmpargument = 0;
            // Check left hand
            sTmp = chrholdingwhich[character][0];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capweaponaction[sTmp]==ACTIONPA)
                {
                    valuetmpargument = LATCHBUTTONLEFT;
                    returncode = TRUE;
                }
            }
            // Check right hand
            sTmp = chrholdingwhich[character][1];
            if(sTmp != MAXCHR)
            {
                sTmp = chrmodel[sTmp];
                if(capweaponaction[sTmp]==ACTIONPA)
                {
                    valuetmpargument = LATCHBUTTONRIGHT;
                    returncode = TRUE;
                }
            }
            break;
        case FIFKURSED:
            // This function passes if the character is kursed
            returncode = chriskursed[character];
            break;
        case FIFTARGETISKURSED:
            // This function passes if the target is kursed
            returncode = chriskursed[chraitarget[character]];
            break;
        case FIFTARGETISDRESSEDUP:
            // This function passes if the character's skin is dressy
            iTmp = chrtexture[character]-madskinstart[chrmodel[character]];
            iTmp = 1<<iTmp;
            returncode = ((capskindressy[chrmodel[character]]&iTmp)!=0);
            break;
        case FIFOVERWATER:
            // This function passes if the character is on a water tile
            returncode = ((meshfx[chronwhichfan[character]]&MESHFXWATER) != 0 && wateriswater);
            break;
        case FIFTHROWN:
            // This function passes if the character was thrown
            returncode = ((chralert[character]&ALERTIFTHROWN)!=0);
            break;
        case FMAKENAMEKNOWN:
            // This function makes the name of an item/character known.
            chrnameknown[character]=TRUE;
//            chricon[character] = TRUE;
            break;
        case FMAKEUSAGEKNOWN:
            // This function makes the usage of an item known...  For XP gains from
            // using an unknown potion or such
            capusageknown[chrmodel[character]] = TRUE;
            break;
        case FSTOPTARGETMOVEMENT:
            // This function makes the target stop moving temporarily
            chrxvel[chraitarget[character]] = 0;
            chryvel[chraitarget[character]] = 0;
            if(chrzvel[chraitarget[character]] > 0) chrzvel[chraitarget[character]] = gravity;
            break;
        case FSETXY:
            // This function stores tmpx and tmpy in the storage array
            chraix[character][valuetmpargument&STORAND] = valuetmpx;
            chraiy[character][valuetmpargument&STORAND] = valuetmpy;
            break;
        case FGETXY:
            // This function gets previously stored data, setting tmpx and tmpy
            valuetmpx = chraix[character][valuetmpargument&STORAND];
            valuetmpy = chraiy[character][valuetmpargument&STORAND];
            break;
        case FADDXY:
            // This function adds tmpx and tmpy to the storage array
            chraix[character][valuetmpargument&STORAND] += valuetmpx;
            chraiy[character][valuetmpargument&STORAND] += valuetmpy;
            break;
        case FMAKEAMMOKNOWN:
            // This function makes the ammo of an item/character known.
            chrammoknown[character]=TRUE;
            break;
        case FSPAWNATTACHEDPARTICLE:
            // This function spawns an attached particle
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            tTmp = spawn_one_particle(chrxpos[character], chrypos[character], chrzpos[character], chrturnleftright[character], chrmodel[character], valuetmpargument, character, valuetmpdistance, chrteam[character], tTmp, 0, MAXCHR);
            break;
        case FSPAWNEXACTPARTICLE:
            // This function spawns an exactly placed particle
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            spawn_one_particle(valuetmpx, valuetmpy, valuetmpdistance, chrturnleftright[character], chrmodel[character], valuetmpargument, MAXCHR, 0, chrteam[character], tTmp, 0, MAXCHR);
            break;
        case FACCELERATETARGET:
            // This function changes the target's speeds
            chrxvel[chraitarget[character]]+=valuetmpx;
            chryvel[chraitarget[character]]+=valuetmpy;
            break;
        case FIFDISTANCEISMORETHANTURN:
            // This function proceeds tmpdistance is greater than tmpturn
            returncode = (valuetmpdistance > (int) valuetmpturn);
            break;
        case FIFCRUSHED:
            // This function proceeds only if the character was crushed
            returncode = ((chralert[character]&ALERTIFCRUSHED)!=0);
            break;
        case FMAKECRUSHVALID:
            // This function makes doors able to close on this object
            chrcanbecrushed[character] = TRUE;
            break;
        case FSETTARGETTOLOWESTTARGET:
            // This sets the target to whatever the target is being held by,
            // The lowest in the set.  This function never fails
            while(chrattachedto[chraitarget[character]]!=MAXCHR)
            {
                chraitarget[character] = chrattachedto[chraitarget[character]];
            }
            break;
        case FIFNOTPUTAWAY:
            // This function proceeds only if the character couln't be put in the pack
            returncode = ((chralert[character]&ALERTIFNOTPUTAWAY)!=0);
            break;
        case FIFTAKENOUT:
            // This function proceeds only if the character was taken out of the pack
            returncode = ((chralert[character]&ALERTIFTAKENOUT)!=0);
            break;
        case FIFAMMOOUT:
            // This function proceeds only if the character has no ammo
            returncode = (chrammo[character]==0);
            break;
        case FPLAYSOUNDLOOPED:
            // This function plays a looped sound
            if(moduleactive)
            {

                volume = VOLMAX;
                play_sound_pvf_looped(capwaveindex[chrmodel[character]][valuetmpargument], PANMID, volume, valuetmpdistance);

            }
            break;
        case FSTOPSOUND:
            // This function stops playing a sound
            stop_sound(capwaveindex[chrmodel[character]][valuetmpargument]);
            break;
        case FHEALSELF:
            // This function heals the character, without setting the alert or modifying
            // the amount
            if(chralive[character])
            {
                iTmp = chrlife[character] + valuetmpargument;
                if(iTmp > chrlifemax[character]) iTmp = chrlifemax[character];
                if(iTmp < 1) iTmp = 1;
                chrlife[character] = iTmp;
            }
            break;
        case FEQUIP:
            // This function flags the character as being equipped
            chrisequipped[character] = TRUE;
            break;
        case FIFTARGETHASITEMIDEQUIPPED:
            // This function proceeds if the target has a matching item equipped
            returncode = FALSE;
            sTmp = chrnextinpack[chraitarget[character]];
            while(sTmp != MAXCHR)
            {
                if(sTmp != character && chrisequipped[sTmp] && (capidsz[chrmodel[sTmp]][IDSZPARENT]==(unsigned int) valuetmpargument || capidsz[chrmodel[sTmp]][IDSZTYPE]==(unsigned int) valuetmpargument))
                {
                    returncode = TRUE;
                    sTmp = MAXCHR;
                }
                else
                {
                    sTmp = chrnextinpack[sTmp];
                }
            }
            break;
        case FSETOWNERTOTARGET:
            // This function sets the owner
            chraiowner[character] = chraitarget[character];
            break;
        case FSETTARGETTOOWNER:
            // This function sets the target to the owner
            chraitarget[character] = chraiowner[character];
            break;
        case FSETFRAME:
            // This function sets the character's current frame
            sTmp = valuetmpargument & 3;
            iTmp = valuetmpargument >> 2;
            set_frame(character, iTmp, sTmp);
            break;
        case FBREAKPASSAGE:
            // This function makes the tiles fall away ( turns into damage terrain )
            returncode = break_passage(valuetmpargument, valuetmpturn, valuetmpdistance, valuetmpx, valuetmpy);
            break;
        case FSETRELOADTIME:
            // This function makes weapons fire slower
            chrreloadtime[character] = valuetmpargument;
            break;
        case FSETTARGETTOWIDEBLAHID:
            // This function sets the target based on the settings of
            // tmpargument and tmpdistance
            sTmp = get_wide_target(character, ((valuetmpdistance>>3)&1),
                                              ((valuetmpdistance>>2)&1),
                                              ((valuetmpdistance>>1)&1),
                                              ((valuetmpdistance)&1),
                                              valuetmpargument, ((valuetmpdistance>>4)&1));
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FPOOFTARGET:
            // This function makes the target go away
            returncode = FALSE;
            if(chrisplayer[chraitarget[character]]==FALSE)
            {
                returncode = TRUE;
                if(chraitarget[character] == character)
                {
                    // Poof self later
                    valuegopoof = TRUE;
                }
                else
                {
                    // Poof others now
                    if(chrattachedto[chraitarget[character]] != MAXCHR)
                        detach_character_from_mount(chraitarget[character], TRUE, FALSE);
                    if(chrholdingwhich[chraitarget[character]][0] != MAXCHR)
                        detach_character_from_mount(chrholdingwhich[chraitarget[character]][0], TRUE, FALSE);
                    if(chrholdingwhich[chraitarget[character]][1] != MAXCHR)
                        detach_character_from_mount(chrholdingwhich[chraitarget[character]][1], TRUE, FALSE);
                    free_inventory(chraitarget[character]);
                    free_one_character(chraitarget[character]);
                    chraitarget[character] = character;
                }
            }
            break;
        case FCHILDDOACTIONOVERRIDE:
            // This function starts a new action, if it is valid for the model
            // It will fail if the action is invalid
            returncode = FALSE;
            if(valuetmpargument < MAXACTION)
            {
                if(madactionvalid[chrmodel[chraichild[character]]][valuetmpargument])
                {
                    chraction[chraichild[character]] = valuetmpargument;
                    chrlip[chraichild[character]] = 0;
                    chrframe[chraichild[character]] = madactionstart[chrmodel[chraichild[character]]][valuetmpargument];
                    chrlastframe[chraichild[character]] = chrframe[chraichild[character]];
                    chractionready[chraichild[character]] = FALSE;
                    returncode = TRUE;
                }
            }
            break;
        case FSPAWNPOOF:
            // This function makes a lovely little poof at the character's location
            spawn_poof(character, chrmodel[character]);
            break;
        case FSETSPEEDPERCENT:
            reset_character_accel(character);
            chrmaxaccel[character] = chrmaxaccel[character] * valuetmpargument / 100.0;
            break;
        case FSETCHILDSTATE:
            // This function sets the child's state
            chraistate[chraichild[character]] = valuetmpargument;
            break;
        case FSPAWNATTACHEDSIZEDPARTICLE:
            // This function spawns an attached particle, then sets its size
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            tTmp = spawn_one_particle(chrxpos[character], chrypos[character], chrzpos[character], chrturnleftright[character], chrmodel[character], valuetmpargument, character, valuetmpdistance, chrteam[character], tTmp, 0, MAXCHR);
            if(tTmp < MAXPRT)
            {
                prtsize[tTmp] = valuetmpturn;
            }
            break;
        case FCHANGEARMOR:
            // This function sets the character's armor type and returns the old type
            // as tmpargument and the new type as tmpx
            valuetmpx = valuetmpargument;
            iTmp = chrtexture[character]-madskinstart[chrmodel[character]];
            valuetmpx = change_armor(character, valuetmpargument);
            valuetmpargument = iTmp;  // The character's old armor
            break;
        case FSHOWTIMER:
            // This function turns the timer on, using the value for tmpargument
            timeron = TRUE;
            timervalue = valuetmpargument;
            break;
        case FIFFACINGTARGET:
            // This function proceeds only if the character is facing the target
            sTmp = atan2(chrypos[chraitarget[character]]-chrypos[character], chrxpos[chraitarget[character]]-chrxpos[character])*65535/(2*PI);
            sTmp+=32768-chrturnleftright[character];
            returncode = (sTmp > 55535 || sTmp < 10000);
            break;
        case FSETVOLUME:
            // This function sets the volume of the given sound
            if(moduleactive && valuetmpdistance >= 0)
            {
                distance = ABS(camtrackx-chroldx[character])+ABS(camtracky-chroldy[character])+valuetmpdistance;
                volume = -distance;
                volume = volume<<VOLSHIFT;
                if(volume < VOLMIN) volume = VOLMIN;
                iTmp = capwaveindex[chrmodel[character]][valuetmpargument];
/*PORT
                if(iTmp < numsound && iTmp >= 0 && soundon)
                {
                    lpDSBuffer[iTmp]->SetVolume(volume);
                }
*/
            }
            break;
        case FSPAWNATTACHEDFACEDPARTICLE:
            // This function spawns an attached particle with facing
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            tTmp = spawn_one_particle(chrxpos[character], chrypos[character], chrzpos[character], valuetmpturn, chrmodel[character], valuetmpargument, character, valuetmpdistance, chrteam[character], tTmp, 0, MAXCHR);
            break;
        case FIFSTATEISODD:
            returncode = (chraistate[character]&1);
            break;
        case FSETTARGETTODISTANTENEMY:
            // This function finds an enemy, within a certain distance to the character, and
            // proceeds only if there is one
            sTmp = find_distant_target(character, valuetmpdistance);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FTELEPORT:
            // This function teleports the character to the X, Y location, failing if the
            // location is off the map or blocked
            returncode = FALSE;
            if(valuetmpx > EDGE && valuetmpy > EDGE && valuetmpx < meshedgex-EDGE && valuetmpy < meshedgey-EDGE)
            {
                // Yeah!  It worked!
                detach_character_from_mount(character, TRUE, FALSE);
                chroldx[character] = chrxpos[character];
                chroldy[character] = chrypos[character];
                chrxpos[character] = valuetmpx;
                chrypos[character] = valuetmpy;
                if(__chrhitawall(character))
                {
                    // No it didn't...
                    chrxpos[character] = chroldx[character];
                    chrypos[character] = chroldy[character];
                    returncode = FALSE;
                }
                else
                {
                    chroldx[character] = chrxpos[character];
                    chroldy[character]= chrypos[character];
                    returncode = TRUE;
                }
            }
            break;
        case FGIVESTRENGTHTOTARGET:
            // Permanently boost the target's strength
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(0, chrstrength[chraitarget[character]], PERFECTSTAT, &iTmp);
                chrstrength[chraitarget[character]]+=iTmp;
            }
            break;
        case FGIVEWISDOMTOTARGET:
            // Permanently boost the target's wisdom
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(0, chrwisdom[chraitarget[character]], PERFECTSTAT, &iTmp);
                chrwisdom[chraitarget[character]]+=iTmp;
            }
            break;
        case FGIVEINTELLIGENCETOTARGET:
            // Permanently boost the target's intelligence
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(0, chrintelligence[chraitarget[character]], PERFECTSTAT, &iTmp);
                chrintelligence[chraitarget[character]]+=iTmp;
            }
            break;
        case FGIVEDEXTERITYTOTARGET:
            // Permanently boost the target's dexterity
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(0, chrdexterity[chraitarget[character]], PERFECTSTAT, &iTmp);
                chrdexterity[chraitarget[character]]+=iTmp;
            }
            break;
        case FGIVELIFETOTARGET:
            // Permanently boost the target's life
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(LOWSTAT, chrlifemax[chraitarget[character]], PERFECTBIG, &iTmp);
                chrlifemax[chraitarget[character]]+=iTmp;
                if(iTmp < 0)
                {
                    getadd(1, chrlife[chraitarget[character]], PERFECTBIG, &iTmp);
                }
                chrlife[chraitarget[character]]+=iTmp;
            }
            break;
        case FGIVEMANATOTARGET:
            // Permanently boost the target's mana
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(0, chrmanamax[chraitarget[character]], PERFECTBIG, &iTmp);
                chrmanamax[chraitarget[character]]+=iTmp;
                if(iTmp < 0)
                {
                    getadd(0, chrmana[chraitarget[character]], PERFECTBIG, &iTmp);
                }
                chrmana[chraitarget[character]]+=iTmp;
            }
            break;
        case FSHOWMAP:
            // Show the map...  Fails if map already visible
            if(mapon)  returncode = FALSE;
            mapon = TRUE;
            break;
        case FSHOWYOUAREHERE:
            // Show the camera target location
            youarehereon = TRUE;
            break;
        case FSHOWBLIPXY:
            // Add a blip
            if(numblip < MAXBLIP)
            {
                if(valuetmpx > 0 && valuetmpx < meshedgex && valuetmpy > 0 && valuetmpy < meshedgey)
                {
                    if(valuetmpargument < NUMBAR && valuetmpargument >= 0)
                    {
                        blipx[numblip] = valuetmpx*MAPSIZE/meshedgex;
                        blipy[numblip] = valuetmpy*MAPSIZE/meshedgey;
                        blipc[numblip] = valuetmpargument;
                        numblip++;
                    }
                }
            }
            break;
        case FHEALTARGET:
            // Give some life to the target
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(1, chrlife[chraitarget[character]], chrlifemax[chraitarget[character]], &iTmp);
                chrlife[chraitarget[character]]+=iTmp;
                // Check all enchants to see if they are removed
                iTmp = chrfirstenchant[chraitarget[character]];
                while(iTmp != MAXENCHANT)
                {
                    test = ('H'-'A'<<15)|('E'-'A'<<10)|('A'-'A'<<5)|('L'-'A');  // [HEAL]
                    sTmp = encnextenchant[iTmp];
                    if(test == everemovedbyidsz[enceve[iTmp]])
                    {
                        remove_enchant(iTmp);
                    }
                    iTmp = sTmp;
                }
            }
            break;
        case FPUMPTARGET:
            // Give some mana to the target
            if(chralive[chraitarget[character]])
            {
                iTmp = valuetmpargument;
                getadd(0, chrmana[chraitarget[character]], chrmanamax[chraitarget[character]], &iTmp);
                chrmana[chraitarget[character]]+=iTmp;
            }
            break;
        case FCOSTAMMO:
            // Take away one ammo
            if(chrammo[character] > 0)
            {
                chrammo[character]--;
            }
            break;
        case FMAKESIMILARNAMESKNOWN:
            // Make names of matching objects known
            iTmp = 0;
            while(iTmp < MAXCHR)
            {
                sTmp = TRUE;
                tTmp = 0;
                while(tTmp < MAXIDSZ)
                {
                    if(capidsz[chrmodel[character]][tTmp] != capidsz[chrmodel[iTmp]][tTmp])
                    {
                        sTmp = FALSE;
                    }
                    tTmp++;
                }
                if(sTmp)
                {
                    chrnameknown[iTmp] = TRUE;
                }
                iTmp++;
            }
            break;
        case FSPAWNATTACHEDHOLDERPARTICLE:
            // This function spawns an attached particle, attached to the holder
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            tTmp = spawn_one_particle(chrxpos[character], chrypos[character], chrzpos[character], chrturnleftright[character], chrmodel[character], valuetmpargument, tTmp, valuetmpdistance, chrteam[character], tTmp, 0, MAXCHR);
            break;
        case FSETTARGETRELOADTIME:
            // This function sets the target's reload time
            chrreloadtime[chraitarget[character]] = valuetmpargument;
            break;
        case FSETFOGLEVEL:
            // This function raises and lowers the module's fog
            fTmp = (valuetmpargument/10.0) - fogtop;
            fogtop+=fTmp;
            fogdistance+=fTmp;
            fogon = fogallowed;
            if(fogdistance < 1.0)  fogon = FALSE;
            break;
        case FGETFOGLEVEL:
            // This function gets the fog level
            valuetmpargument = fogtop*10;
            break;
        case FSETFOGTAD:
            // This function changes the fog color
            fogred = valuetmpturn;
            foggrn = valuetmpargument;
            fogblu = valuetmpdistance;
            break;
        case FSETFOGBOTTOMLEVEL:
            // This function sets the module's bottom fog level...
            fTmp = (valuetmpargument/10.0) - fogbottom;
            fogbottom+=fTmp;
            fogdistance-=fTmp;
            fogon = fogallowed;
            if(fogdistance < 1.0)  fogon = FALSE;
            break;
        case FGETFOGBOTTOMLEVEL:
            // This function gets the fog level
            valuetmpargument = fogbottom*10;
            break;
        case FCORRECTACTIONFORHAND:
            // This function turns ZA into ZA, ZB, ZC, or ZD...
            // tmpargument must be set to one of the A actions beforehand...
            if(chrattachedto[character]!= MAXCHR)
            {
                if(chrinwhichhand[character]==GRIPLEFT)
                {
                    // A or B
                    valuetmpargument = valuetmpargument + (rand()&1);
                }
                else
                {
                    // C or D
                    valuetmpargument = valuetmpargument + 2 + (rand()&1);
                }
            }
            break;
        case FIFTARGETISMOUNTED:
            // This function proceeds if the target is riding a mount
            returncode = FALSE;
            if(chrattachedto[chraitarget[character]]!=MAXCHR)
            {
                returncode = chrismount[chrattachedto[chraitarget[character]]];
            }
            break;
        case FSPARKLEICON:
            // This function makes a blippie thing go around the icon
            if(valuetmpargument < NUMBAR && valuetmpargument > -1)
            {
                chrsparkle[character] = valuetmpargument;
            }
            break;
        case FUNSPARKLEICON:
            // This function stops the blippie thing
            chrsparkle[character] = NOSPARKLE;
            break;
        case FGETTILEXY:
            // This function gets the tile at x,y
            if(valuetmpx >= 0 && valuetmpx < meshedgex)
            {
                if(valuetmpy >= 0 && valuetmpy < meshedgey)
                {
                    iTmp = meshfanstart[valuetmpy>>7] + (valuetmpx>>7);
                    valuetmpargument = meshtile[iTmp]&255;
                }
            }
            break;
        case FSETTILEXY:
            // This function changes the tile at x,y
            if(valuetmpx >= 0 && valuetmpx < meshedgex)
            {
                if(valuetmpy >= 0 && valuetmpy < meshedgey)
                {
                    iTmp = meshfanstart[valuetmpy>>7] + (valuetmpx>>7);
                    meshtile[iTmp] = (valuetmpargument&255);
                }
            }
            break;
        case FSETSHADOWSIZE:
            // This function changes a character's shadow size
            chrshadowsize[character] = valuetmpargument*chrfat[character];
            chrshadowsizesave[character] = valuetmpargument;
            break;
        case FORDERTARGET:
            // This function orders one specific character...  The target
            // Be careful in using this, always checking IDSZ first
            chrorder[chraitarget[character]] = valuetmpargument;
            chrcounter[chraitarget[character]] = 0;
            chralert[chraitarget[character]]|=ALERTIFORDERED;
            break;
        case FSETTARGETTOWHOEVERISINPASSAGE:
            // This function lets passage rectangles be used as event triggers
            sTmp = who_is_blocking_passage(valuetmpargument);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FIFCHARACTERWASABOOK:
            // This function proceeds if the base model is the same as the current
            // model or if the base model is SPELLBOOK
            returncode = (chrbasemodel[character] == SPELLBOOK ||
                          chrbasemodel[character] == chrmodel[character]);
            break;
        case FSETENCHANTBOOSTVALUES:
            // This function sets the boost values for the last enchantment
            iTmp = chrundoenchant[character];
            if(iTmp != MAXENCHANT)
            {
                encownermana[iTmp] = valuetmpargument;
                encownerlife[iTmp] = valuetmpdistance;
                enctargetmana[iTmp] = valuetmpx;
                enctargetlife[iTmp] = valuetmpy;
            }
            break;
        case FSPAWNCHARACTERXYZ:
            // This function spawns a character, failing if x,y,z is invalid
            sTmp = spawn_one_character(valuetmpx, valuetmpy, valuetmpdistance, chrmodel[character], chrteam[character], 0, valuetmpturn, NULL, MAXCHR);
            returncode = FALSE;
            if(sTmp < MAXCHR)
            {
                if(__chrhitawall(sTmp))
                {
                    free_one_character(sTmp);
                }
                else
                {
                    chriskursed[sTmp] = FALSE;
                    chraichild[character] = sTmp;
                    chrpassage[sTmp] = chrpassage[character];
                    chraiowner[sTmp] = chraiowner[character];
                    returncode = TRUE;
                }
            }
            break;
        case FSPAWNEXACTCHARACTERXYZ:
            // This function spawns a character ( specific model slot ),
            // failing if x,y,z is invalid
            sTmp = spawn_one_character(valuetmpx, valuetmpy, valuetmpdistance, valuetmpargument, chrteam[character], 0, valuetmpturn, NULL, MAXCHR);
            returncode = FALSE;
            if(sTmp < MAXCHR)
            {
                if(__chrhitawall(sTmp))
                {
                    free_one_character(sTmp);
                }
                else
                {
                    chriskursed[sTmp] = FALSE;
                    chraichild[character] = sTmp;
                    chrpassage[sTmp] = chrpassage[character];
                    chraiowner[sTmp] = chraiowner[character];
                    returncode = TRUE;
                }
            }
            break;
        case FCHANGETARGETCLASS:
            // This function changes a character's model ( specific model slot )
            change_character(chraitarget[character], valuetmpargument, 0, LEAVEALL);
            break;
        case FPLAYFULLSOUND:
            // This function plays a sound loud for everyone...  Victory music
            if(moduleactive)
            {

                play_sound_pvf(capwaveindex[chrmodel[character]][valuetmpargument], PANMID, VOLMAX, valuetmpdistance);

            }
            break;
        case FSPAWNEXACTCHASEPARTICLE:
            // This function spawns an exactly placed particle that chases the target
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            tTmp = spawn_one_particle(valuetmpx, valuetmpy, valuetmpdistance, chrturnleftright[character], chrmodel[character], valuetmpargument, MAXCHR, 0, chrteam[character], tTmp, 0, MAXCHR);
            if(tTmp < MAXPRT)
            {
                prttarget[tTmp] = chraitarget[character];
            }
            break;
        case FCREATEORDER:
            // This function packs up an order, using tmpx, tmpy, tmpargument and the 
            // target ( if valid ) to create a new tmpargument
            sTmp = chraitarget[character]<<24;
            sTmp |= ((valuetmpx>>6)&1023)<<14;
            sTmp |= ((valuetmpy>>6)&1023)<<4;
            sTmp |= (valuetmpargument&15);
            valuetmpargument = sTmp;
            break;
        case FORDERSPECIALID:
            // This function issues an order to all with the given special IDSZ
            issue_special_order(valuetmpargument, valuetmpdistance);
            break;
        case FUNKURSETARGETINVENTORY:
            // This function unkurses every item a character is holding
            sTmp = chrholdingwhich[chraitarget[character]][0];
            chriskursed[sTmp] = FALSE;
            sTmp = chrholdingwhich[chraitarget[character]][1];
            chriskursed[sTmp] = FALSE;
            sTmp = chrnextinpack[chraitarget[character]];
            while(sTmp != MAXCHR)
            {
                chriskursed[sTmp] = FALSE;
                sTmp = chrnextinpack[sTmp];
            }
            break;
        case FIFTARGETISSNEAKING:
            // This function proceeds if the target is doing ACTIONDA or ACTIONWA
            returncode = (chraction[chraitarget[character]] == ACTIONDA || chraction[chraitarget[character]] == ACTIONWA);
            break;
        case FDROPITEMS:
            // This function drops all of the character's items
            drop_all_items(character);
            break;
        case FRESPAWNTARGET:
            // This function respawns the target at its current location
            sTmp = chraitarget[character];
            chroldx[sTmp] = chrxpos[sTmp];
            chroldy[sTmp] = chrypos[sTmp];
            chroldz[sTmp] = chrzpos[sTmp];
            respawn_character(sTmp);
            chrxpos[sTmp] = chroldx[sTmp];
            chrypos[sTmp] = chroldy[sTmp];
            chrzpos[sTmp] = chroldz[sTmp];
            break;
        case FTARGETDOACTIONSETFRAME:
            // This function starts a new action, if it is valid for the model and
            // sets the starting frame.  It will fail if the action is invalid
            returncode = FALSE;
            if(valuetmpargument < MAXACTION)
            {
                if(madactionvalid[chrmodel[chraitarget[character]]][valuetmpargument])
                {
                    chraction[chraitarget[character]] = valuetmpargument;
                    chrlip[chraitarget[character]] = 0;
                    chrframe[chraitarget[character]] = madactionstart[chrmodel[chraitarget[character]]][valuetmpargument];
                    chrlastframe[chraitarget[character]] = chrframe[chraitarget[character]];
                    chractionready[chraitarget[character]] = FALSE;
                    returncode = TRUE;
                }
            }
            break;
        case FIFTARGETCANSEEINVISIBLE:
            // This function proceeds if the target can see invisible
            returncode = chrcanseeinvisible[chraitarget[character]];
            break;
        case FSETTARGETTONEARESTBLAHID:
            // This function finds the nearest target that meets the
            // requirements
            sTmp = get_nearest_target(character, ((valuetmpdistance>>3)&1),
                                                 ((valuetmpdistance>>2)&1),
                                                 ((valuetmpdistance>>1)&1),
                                                 ((valuetmpdistance)&1),
                                                 valuetmpargument);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FSETTARGETTONEARESTENEMY:
            // This function finds the nearest target that meets the
            // requirements
            sTmp = get_nearest_target(character, 0, 0, 1, 0, IDSZNONE);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FSETTARGETTONEARESTFRIEND:
            // This function finds the nearest target that meets the
            // requirements
            sTmp = get_nearest_target(character, 0, 1, 0, 0, IDSZNONE);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FSETTARGETTONEARESTLIFEFORM:
            // This function finds the nearest target that meets the
            // requirements
            sTmp = get_nearest_target(character, 0, 1, 1, 0, IDSZNONE);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FFLASHPASSAGE:
            // This function makes the passage light or dark...  For debug...
            flash_passage(valuetmpargument, valuetmpdistance);
            break;
        case FFINDTILEINPASSAGE:
            // This function finds the next tile in the passage, tmpx and tmpy are 
            // required and set on return
            returncode = find_tile_in_passage(valuetmpargument, valuetmpdistance);
            break;
        case FIFHELDINLEFTHAND:
            // This function proceeds if the character is in the left hand of another
            // character
            returncode = FALSE;
            sTmp = chrattachedto[character];
            if(sTmp != MAXCHR)
            {
                returncode = (chrholdingwhich[sTmp][0] == character);
            }
            break;
        case FNOTANITEM:
            // This function makes the character a non-item character
            chrisitem[character] = FALSE;
            break;
        case FSETCHILDAMMO:
            // This function sets the child's ammo
            chrammo[chraichild[character]] = valuetmpargument;
            break;
        case FIFHITVULNERABLE:
            // This function proceeds if the character was hit by a weapon with the
            // correct vulnerability IDSZ...  [SILV] for Werewolves...
            returncode = ((chralert[character]&ALERTIFHITVULNERABLE)!=0);
            break;
        case FIFTARGETISFLYING:
            // This function proceeds if the character target is flying
            returncode = (chrflyheight[chraitarget[character]] > 0);
            break;
        case FIDENTIFYTARGET:
            // This function reveals the target's name, ammo, and usage
            // Proceeds if the target was unknown
            returncode = FALSE;
            sTmp = chraitarget[character];
            if(chrammomax[sTmp] != 0)  chrammoknown[sTmp]=TRUE;
            if(chrname[sTmp][0] != 'B' ||
               chrname[sTmp][1] != 'l' ||
               chrname[sTmp][2] != 'a' ||
               chrname[sTmp][3] != 'h' ||
               chrname[sTmp][4] != 0)
            {
               returncode = !chrnameknown[sTmp];
               chrnameknown[sTmp]=TRUE;
            }
            capusageknown[chrmodel[sTmp]] = TRUE;
            break;
        case FBEATMODULE:
            // This function displays the Module Ended message
            beatmodule = TRUE;
            break;
        case FENDMODULE:
            // This function presses the Escape key
            sdlkeybuffer[SDLK_ESCAPE]=1;
            break;
        case FDISABLEEXPORT:
            // This function turns export off
            exportvalid = FALSE;
            break;
        case FENABLEEXPORT:
            // This function turns export on
            exportvalid = TRUE;
            break;
        case FGETTARGETSTATE:
            // This function sets tmpargument to the state of the target
            valuetmpargument = chraistate[chraitarget[character]];
            break;
        case FSETSPEECH:
            // This function sets all of the RTS speech registers to tmpargument
            sTmp = 0;
            while(sTmp < MAXSPEECH)
            {
                chrwavespeech[character][sTmp] = valuetmpargument;
                sTmp++;
            }
            break;
        case FSETMOVESPEECH:
            // This function sets the RTS move speech register to tmpargument
            chrwavespeech[character][SPEECHMOVE] = valuetmpargument;
            break;
        case FSETSECONDMOVESPEECH:
            // This function sets the RTS movealt speech register to tmpargument
            chrwavespeech[character][SPEECHMOVEALT] = valuetmpargument;
            break;
        case FSETATTACKSPEECH:
            // This function sets the RTS attack speech register to tmpargument
            chrwavespeech[character][SPEECHATTACK] = valuetmpargument;
            break;
        case FSETASSISTSPEECH:
            // This function sets the RTS assist speech register to tmpargument
            chrwavespeech[character][SPEECHASSIST] = valuetmpargument;
            break;
        case FSETTERRAINSPEECH:
            // This function sets the RTS terrain speech register to tmpargument
            chrwavespeech[character][SPEECHTERRAIN] = valuetmpargument;
            break;
        case FSETSELECTSPEECH:
            // This function sets the RTS select speech register to tmpargument
            chrwavespeech[character][SPEECHSELECT] = valuetmpargument;
            break;
        case FCLEARENDMESSAGE:
            // This function empties the end-module text buffer
            endtext[0] = 0;
            endtextwrite = 0;
            break;
        case FADDENDMESSAGE:
            // This function appends a message to the end-module text buffer
            append_end_text(madmsgstart[chrmodel[character]]+valuetmpargument, character);
            break;
        case FSETMUSIC:
            // This function begins playing a new track of music
            if(valuetmpturn < IGTRACKS && valuetmpturn >= 0)
            {
                ignexttrack = valuetmpturn;
                igtrackcount = valuetmpdistance;
            }
            break;
        case FSETMUSICPASSAGE:
            // This function makes the given passage play music if a player enters it
            if(valuetmpargument < numpassage && valuetmpargument >= 0)
            {
                if(valuetmpturn < IGTRACKS && valuetmpturn >= 0)
                {
                    passtracktype[valuetmpargument] = valuetmpturn;
                    passtrackcount[valuetmpargument] = valuetmpdistance;
                }
            }
            break;
        case FMAKECRUSHINVALID:
            // This function makes doors unable to close on this object
            chrcanbecrushed[character] = FALSE;
            break;
        case FSTOPMUSIC:
            // This function stops the interactive music
            igloaded = FALSE;
            break;
        case FFLASHVARIABLE:
            // This function makes the character flash according to tmpargument
            flash_character(character, valuetmpargument);
            break;
        case FACCELERATEUP:
            // This function changes the character's up down velocity
            chrzvel[character]+=valuetmpargument/100.0;
            break;
        case FFLASHVARIABLEHEIGHT:
            // This function makes the character flash, feet one color, head another...
            flash_character_height(character, valuetmpturn, valuetmpx,
                                              valuetmpdistance, valuetmpy);
            break;
        case FSETDAMAGETIME:
            // This function makes the character invincible for a little while
            chrdamagetime[character] = valuetmpargument;
            break;
        case FIFSTATEIS8:
            returncode = (8 == chraistate[character]);
            break;
        case FIFSTATEIS9:
            returncode = (9 == chraistate[character]);
            break;
        case FIFSTATEIS10:
            returncode = (10 == chraistate[character]);
            break;
        case FIFSTATEIS11:
            returncode = (11 == chraistate[character]);
            break;
        case FIFSTATEIS12:
            returncode = (12 == chraistate[character]);
            break;
        case FIFSTATEIS13:
            returncode = (13 == chraistate[character]);
            break;
        case FIFSTATEIS14:
            returncode = (14 == chraistate[character]);
            break;
        case FIFSTATEIS15:
            returncode = (15 == chraistate[character]);
            break;
        case FIFTARGETISAMOUNT:
            returncode = chrismount[chraitarget[character]];
            break;
        case FIFTARGETISAPLATFORM:
            returncode = chrplatform[chraitarget[character]];
            break;
        case FADDSTAT:
            if(chrstaton[character]==FALSE) add_stat(character);
            break;
        case FDISENCHANTTARGET:
            returncode = (chrfirstenchant[chraitarget[character]] != MAXENCHANT);
            disenchant_character(chraitarget[character]);
            break;
        case FDISENCHANTALL:
            iTmp = 0;
            while(iTmp < MAXENCHANT)
            {
                remove_enchant(iTmp);
                iTmp++;
            }
            break;
        case FSETVOLUMENEARESTTEAMMATE:
/*PORT
            if(moduleactive && valuetmpdistance >= 0)
            {
                // Find the closest teammate
                iTmp = 10000;
                sTmp = 0;
                while(sTmp < MAXCHR)
                {
                    if(chron[sTmp] && chralive[sTmp] && chrteam[sTmp] == chrteam[character])
                    {
                        distance = ABS(camtrackx-chroldx[sTmp])+ABS(camtracky-chroldy[sTmp]);
                        if(distance < iTmp)  iTmp = distance;
                    }
                    sTmp++;
                }
                distance=iTmp+valuetmpdistance;
                volume = -distance;
                volume = volume<<VOLSHIFT;
                if(volume < VOLMIN) volume = VOLMIN;
                iTmp = capwaveindex[chrmodel[character]][valuetmpargument];
                if(iTmp < numsound && iTmp >= 0 && soundon)
                {
                    lpDSBuffer[iTmp]->SetVolume(volume);
                }
            }
*/
            break;
        case FADDSHOPPASSAGE:
            // This function defines a shop area
            add_shop_passage(character, valuetmpargument);
            break;
        case FTARGETPAYFORARMOR:
            // This function costs the target some money, or fails if 'e doesn't have
            // enough...
            // tmpx is amount needed
            // tmpy is cost of new skin
            sTmp = chraitarget[character];   // The target
            tTmp = chrmodel[sTmp];           // The target's model
            iTmp =  capskincost[tTmp][valuetmpargument&3];
            valuetmpy = iTmp;                // Cost of new skin
            iTmp -= capskincost[tTmp][(chrtexture[sTmp]-madskinstart[tTmp])&3];  // Refund
            if(iTmp > chrmoney[sTmp])
            {
                // Not enough...
                valuetmpx = iTmp - chrmoney[sTmp];  // Amount needed
                returncode = FALSE;
            }
            else
            {
                // Pay for it...  Cost may be negative after refund...
                chrmoney[sTmp]-=iTmp;
                if(chrmoney[sTmp] > MAXMONEY)  chrmoney[sTmp] = MAXMONEY;
                valuetmpx = 0;
                returncode = TRUE;
            }
            break;
        case FJOINEVILTEAM:
            // This function adds the character to the evil team...
            switch_team(character, EVILTEAM);
            break;
        case FJOINNULLTEAM:
            // This function adds the character to the null team...
            switch_team(character, NULLTEAM);
            break;
        case FJOINGOODTEAM:
            // This function adds the character to the good team...
            switch_team(character, GOODTEAM);
            break;
        case FPITSKILL:
            // This function activates pit deaths...
            pitskill=TRUE;
            break;
        case FSETTARGETTOPASSAGEID:
            // This function finds a character who is both in the passage and who has
            // an item with the given IDSZ
            sTmp = who_is_blocking_passage_ID(valuetmpargument, valuetmpdistance);
            returncode = FALSE;
            if(sTmp != MAXCHR)
            {
                chraitarget[character] = sTmp;
                returncode = TRUE;
            }
            break;
        case FMAKENAMEUNKNOWN:
            // This function makes the name of an item/character unknown.
            chrnameknown[character]=FALSE;
            break;
        case FSPAWNEXACTPARTICLEENDSPAWN:
            // This function spawns a particle that spawns a character...
            tTmp = character;
            if(chrattachedto[character]!=MAXCHR)  tTmp = chrattachedto[character];
            tTmp = spawn_one_particle(valuetmpx, valuetmpy, valuetmpdistance, chrturnleftright[character], chrmodel[character], valuetmpargument, MAXCHR, 0, chrteam[character], tTmp, 0, MAXCHR);
            if(tTmp != MAXPRT)
            {
                prtspawncharacterstate[tTmp] = valuetmpturn;
            }
            break;
        case FSPAWNPOOFSPEEDSPACINGDAMAGE:
            // This function makes a lovely little poof at the character's location,
            // adjusting the xy speed and spacing and the base damage first
            // Temporarily adjust the values for the particle type
            sTmp = chrmodel[character];
            sTmp = madprtpip[sTmp][capgopoofprttype[sTmp]];
            iTmp = pipxyvelbase[sTmp];
            tTmp = pipxyspacingbase[sTmp];
            test = pipdamagebase[sTmp];
            pipxyvelbase[sTmp] = valuetmpx;
            pipxyspacingbase[sTmp] = valuetmpy;
            pipdamagebase[sTmp] = valuetmpargument;
            spawn_poof(character, chrmodel[character]);
            // Restore the saved values
            pipxyvelbase[sTmp] = iTmp;
            pipxyspacingbase[sTmp] = tTmp;
            pipdamagebase[sTmp] = test;
            break;
        case FGIVEEXPERIENCETOGOODTEAM:
            // This function gives experience to everyone on the G Team
            give_team_experience(GOODTEAM, valuetmpargument, valuetmpdistance);
            break;
    }


    return returncode;
}

//--------------------------------------------------------------------------------------------
void set_operand(unsigned char variable)
{
    // ZZ> This function sets one of the tmp* values for scripted AI    
    switch(variable)
    {
        case VARTMPX:
            valuetmpx=valueoperationsum;
            break;
        case VARTMPY:
            valuetmpy=valueoperationsum;
            break;
        case VARTMPDISTANCE:
            valuetmpdistance=valueoperationsum;
            break;
        case VARTMPTURN:
            valuetmpturn=valueoperationsum;
            break;
        case VARTMPARGUMENT:
            valuetmpargument=valueoperationsum;
            break;
    }
}

//--------------------------------------------------------------------------------------------
void run_operand(unsigned int value, int character)
{
    // ZZ> This function does the scripted arithmetic in operator,operand pairs
    unsigned char opcode;
    unsigned char variable;
    int iTmp;


    // Get the operation code
    opcode = (value>>27);
    if(opcode&16)
    {
        // Get the working value from a constant, constants are all but high 5 bits
        iTmp = value&0x07ffffff;
    }
    else
    {
        // Get the working value from a register
        variable = value;
        iTmp = 1;
        switch(variable)
        {
            case VARTMPX:
                iTmp = valuetmpx;
                break;
            case VARTMPY:
                iTmp = valuetmpy;
                break;
            case VARTMPDISTANCE:
                iTmp = valuetmpdistance;
                break;
            case VARTMPTURN:
                iTmp = valuetmpturn;
                break;
            case VARTMPARGUMENT:
                iTmp = valuetmpargument;
                break;
            case VARRAND:
                iTmp = RANDIE;
                break;
            case VARSELFX:
                iTmp = chrxpos[character];
                break;
            case VARSELFY:
                iTmp = chrypos[character];
                break;
            case VARSELFTURN:
                iTmp = chrturnleftright[character];
                break;
            case VARSELFCOUNTER:
                iTmp = chrcounter[character];
                break;
            case VARSELFORDER:
                iTmp = chrorder[character];
                break;
            case VARSELFMORALE:
                iTmp = teammorale[chrbaseteam[character]];
                break;
            case VARSELFLIFE:
                iTmp = chrlife[character];
                break;
            case VARTARGETX:
                iTmp = chrxpos[chraitarget[character]];
                break;
            case VARTARGETY:
                iTmp = chrypos[chraitarget[character]];
                break;
            case VARTARGETDISTANCE:
                iTmp = ABS((int)(chrxpos[chraitarget[character]]-chrxpos[character]))+
                       ABS((int)(chrypos[chraitarget[character]]-chrypos[character]));
                break;
            case VARTARGETTURN:
                iTmp = chrturnleftright[chraitarget[character]];
                break;
            case VARLEADERX:
                iTmp = chrxpos[character];
                if(teamleader[chrteam[character]]!=NOLEADER)
                    iTmp = chrxpos[teamleader[chrteam[character]]];
                break;
            case VARLEADERY:
                iTmp = chrypos[character];
                if(teamleader[chrteam[character]]!=NOLEADER)
                    iTmp = chrypos[teamleader[chrteam[character]]];
                break;
            case VARLEADERDISTANCE:
                iTmp = 10000;
                if(teamleader[chrteam[character]]!=NOLEADER)
                    iTmp = ABS((int)(chrxpos[teamleader[chrteam[character]]]-chrxpos[character]))+
                           ABS((int)(chrypos[teamleader[chrteam[character]]]-chrypos[character]));
                break;
            case VARLEADERTURN:
                iTmp = chrturnleftright[character];
                if(teamleader[chrteam[character]]!=NOLEADER)
                    iTmp = chrturnleftright[teamleader[chrteam[character]]];
                break;
            case VARGOTOX:
                iTmp = chraigotox[character][chraigoto[character]];
                break;
            case VARGOTOY:
                iTmp = chraigotoy[character][chraigoto[character]];
                break;
            case VARGOTODISTANCE:
                iTmp = ABS((int)(chraigotox[character][chraigoto[character]]-chrxpos[character]))+
                       ABS((int)(chraigotoy[character][chraigoto[character]]-chrypos[character]));
                break;
            case VARTARGETTURNTO:
                iTmp = atan2(chrypos[chraitarget[character]]-chrypos[character], chrxpos[chraitarget[character]]-chrxpos[character])*65535/(2*PI);
                iTmp+=32768;
                iTmp = iTmp&65535;
                break;
            case VARPASSAGE:
                iTmp = chrpassage[character];
                break;
            case VARWEIGHT:
                iTmp = chrholdingweight[character];
                break;
            case VARSELFALTITUDE:
                iTmp = chrzpos[character]-chrlevel[character];
                break;
            case VARSELFID:
                iTmp = capidsz[chrmodel[character]][IDSZTYPE];
                break;
            case VARSELFHATEID:
                iTmp = capidsz[chrmodel[character]][IDSZHATE];
                break;
            case VARSELFMANA:
                iTmp = chrmana[character];
                if(chrcanchannel[character])  iTmp += chrlife[character];
                break;
            case VARTARGETSTR:
                iTmp = chrstrength[chraitarget[character]];
                break;
            case VARTARGETWIS:
                iTmp = chrwisdom[chraitarget[character]];
                break;
            case VARTARGETINT:
                iTmp = chrintelligence[chraitarget[character]];
                break;
            case VARTARGETDEX:
                iTmp = chrdexterity[chraitarget[character]];
                break;
            case VARTARGETLIFE:
                iTmp = chrlife[chraitarget[character]];
                break;
            case VARTARGETMANA:
                iTmp = chrmana[chraitarget[character]];
                if(chrcanchannel[chraitarget[character]])  iTmp += chrlife[chraitarget[character]];
                break;
            case VARTARGETLEVEL:
                iTmp = chrexperiencelevel[chraitarget[character]];
                break;
            case VARTARGETSPEEDX:
                iTmp = chrxvel[chraitarget[character]];
                break;
            case VARTARGETSPEEDY:
                iTmp = chryvel[chraitarget[character]];
                break;
            case VARTARGETSPEEDZ:
                iTmp = chrzvel[chraitarget[character]];
                break;
            case VARSELFSPAWNX:
                iTmp = chrxstt[character];
                break;
            case VARSELFSPAWNY:
                iTmp = chrystt[character];
                break;
            case VARSELFSTATE:
                iTmp = chraistate[character];
                break;
            case VARSELFSTR:
                iTmp = chrstrength[character];
                break;
            case VARSELFWIS:
                iTmp = chrwisdom[character];
                break;
            case VARSELFINT:
                iTmp = chrintelligence[character];
                break;
            case VARSELFDEX:
                iTmp = chrdexterity[character];
                break;
            case VARSELFMANAFLOW:
                iTmp = chrmanaflow[character];
                break;
            case VARTARGETMANAFLOW:
                iTmp = chrmanaflow[chraitarget[character]];
                break;
            case VARSELFATTACHED:
                iTmp = number_of_attached_particles(character);
                break;
            case VARSWINGTURN:
                iTmp = camswing<<2;
                break;
            case VARXYDISTANCE:
                iTmp = sqrt(valuetmpx*valuetmpx + valuetmpy*valuetmpy);
                break;
            case VARSELFZ:
                iTmp = chrzpos[character];
                break;
            case VARTARGETALTITUDE:
                iTmp = chrzpos[chraitarget[character]]-chrlevel[chraitarget[character]];
                break;
            case VARTARGETZ:
                iTmp = chrzpos[chraitarget[character]];
                break;
            case VARSELFINDEX:
                iTmp = character;
                break;
            case VAROWNERX:
                iTmp = chrxpos[chraiowner[character]];
                break;
            case VAROWNERY:
                iTmp = chrypos[chraiowner[character]];
                break;
            case VAROWNERTURN:
                iTmp = chrturnleftright[chraiowner[character]];
                break;
            case VAROWNERDISTANCE:
                iTmp = ABS((int)(chrxpos[chraiowner[character]]-chrxpos[character]))+
                       ABS((int)(chrypos[chraiowner[character]]-chrypos[character]));
                break;
            case VAROWNERTURNTO:
                iTmp = atan2(chrypos[chraiowner[character]]-chrypos[character], chrxpos[chraiowner[character]]-chrxpos[character])*65535/(2*PI);
                iTmp+=32768;
                iTmp = iTmp&65535;
                break;
            case VARXYTURNTO:
                iTmp = atan2(valuetmpy-chrypos[character], valuetmpx-chrxpos[character])*65535/(2*PI);
                iTmp+=32768;
                iTmp = iTmp&65535;
                break;
            case VARSELFMONEY:
                iTmp = chrmoney[character];
                break;
            case VARSELFACCEL:
                iTmp = (chrmaxaccel[character]*100.0);
                break;
            case VARTARGETEXP:
                iTmp = chrexperience[chraitarget[character]];
                break;
            case VARSELFAMMO:
                iTmp = chrammo[character];
                break;
        }
    }


    // Now do the math
    switch(opcode&15)
    {
        case OPADD:
            valueoperationsum+=iTmp;
            break;
        case OPSUB:
            valueoperationsum-=iTmp;
            break;
        case OPAND:
            valueoperationsum=valueoperationsum&iTmp;
            break;
        case OPSHR:
            valueoperationsum=valueoperationsum>>iTmp;
            break;
        case OPSHL:
            valueoperationsum=valueoperationsum<<iTmp;
            break;
        case OPMUL:
            valueoperationsum=valueoperationsum*iTmp;
            break;
        case OPDIV:
            if(iTmp != 0)
            {
                valueoperationsum=valueoperationsum/iTmp;
            }
            break;
        case OPMOD:
            if(iTmp != 0)
            {
                valueoperationsum=valueoperationsum%iTmp;
            }
            break;
    }
}

//--------------------------------------------------------------------------------------------
void let_character_think(int character)
{
    // ZZ> This function lets one character do AI stuff
    unsigned short aicode;
    unsigned int index;
    unsigned int value;
    unsigned int iTmp;
    unsigned char functionreturn;
    int operands;


    // Make life easier
    valueoldtarget = chraitarget[character];
    aicode=chraitype[character];


    // Figure out alerts that weren't already set
    set_alerts(character);
    changed = FALSE;


    // Clear the button latches
    if(chrisplayer[character]==FALSE)
    {
        chrlatchbutton[character] = 0;
    }


    // Reset the target if it can't be seen
    if(!chrcanseeinvisible[character] && chralive[character])
    {
        if(chralpha[chraitarget[character]]<=INVISIBLE || chrlight[chraitarget[character]]<=INVISIBLE)
        {
            chraitarget[character] = character;
        }
    }


    // Run the AI Script
    index = iAisStartPosition[aicode];
    valuegopoof = FALSE;


    value = iCompiledAis[index];
    while((value&0x87ffffff) != 0x80000035)  // End Function
    {
        value = iCompiledAis[index];
        // Was it a function
        if((value&0x80000000)!=0)
        {
            // Run the function
            functionreturn = run_function(value, character);
            // Get the jump code
            index++;
            iTmp=iCompiledAis[index];
            if(functionreturn==TRUE)
            {
                // Proceed to the next function
                index++;
            }
            else
            {
                // Jump to where the jump code says to go
                index=iTmp;
            }
        }
        else
        {
            // Get the number of operands
            index++;
            operands = iCompiledAis[index];
            // Now run the operation
            valueoperationsum=0;
            index++;
            while(operands > 0)
            {
                iTmp = iCompiledAis[index];
                run_operand(iTmp,character);  // This sets valueoperationsum
                operands--;
                index++;
            }
            // Save the results in the register that called the arithmetic
            set_operand(value);
        }
        // This is used by the Else function
        valuelastindent = value;
    }


    // Set latches
    if(chrisplayer[character]==FALSE && aicode!=0)
    {
        if(chrismount[character]&&chrholdingwhich[character][0]!=MAXCHR)
        {
            // Mount
            chrlatchx[character] = chrlatchx[chrholdingwhich[character][0]];
            chrlatchy[character] = chrlatchy[chrholdingwhich[character][0]];
        }
        else
        {
            // Normal AI
            chrlatchx[character] = (chraigotox[character][chraigoto[character]]-chrxpos[character])/1000.0;
            chrlatchy[character] = (chraigotoy[character][chraigoto[character]]-chrypos[character])/1000.0;
        }
    }


    // Clear alerts for next time around
    chralert[character] = 0;
    if(changed)  chralert[character] = ALERTIFCHANGED;


    // Do poofing
    if(valuegopoof)
    {
        if(chrattachedto[character] != MAXCHR)
            detach_character_from_mount(character, TRUE, FALSE);
        if(chrholdingwhich[character][0] != MAXCHR)
            detach_character_from_mount(chrholdingwhich[character][0], TRUE, FALSE);
        if(chrholdingwhich[character][1] != MAXCHR)
            detach_character_from_mount(chrholdingwhich[character][1], TRUE, FALSE);
        free_inventory(character);
        free_one_character(character);
        // If this character was killed in another's script, we don't want the poof to
        // carry over...
        valuegopoof = FALSE;
    }
}

//--------------------------------------------------------------------------------------------
void let_ai_think()
{
    // ZZ> This function lets every computer controlled character do AI stuff
    int character;


    numblip = 0;
    character = 0;
    while(character < MAXCHR)
    {
        if(chron[character] && (!chrinpack[character]||capisequipment[chrmodel[character]]) && (chralive[character]||(chralert[character]&ALERTIFCLEANEDUP)||(chralert[character]&ALERTIFCRUSHED)))
        {
            // Cleaned up characters shouldn't be alert to anything else
            if(chralert[character]&ALERTIFCRUSHED)  chralert[character] = ALERTIFCRUSHED;
            if(chralert[character]&ALERTIFCLEANEDUP)  chralert[character] = ALERTIFCLEANEDUP;
            let_character_think(character);
        }
        character++;
    }
}

