Scripting engine update:

-void pointer is now compatible with any other pointer type
-strings can now be directly compared using '==' and '!=' operators
-added a linguistic chat message parser with some generic messages predefined - to be used for Human<->AI and AI<->AI communication
Note: chat_lexer.l should be compiled using an additional -i switch for case-insensitive lexer

git-svn-id: svn+ssh://svn.gna.org/svn/warzone/trunk@898 4a71c877-e1ca-e34f-864e-861f7616d084
master
Roman C 2006-12-16 14:59:50 +00:00
parent 3f4119994f
commit 14cd4b60cc
12 changed files with 1297 additions and 292 deletions

178
lib/script/chat_lexer.l Normal file
View File

@ -0,0 +1,178 @@
%{
/*
* chat_lexer.l
*
* lexer for multiplayer chat messages.
* IMPORTANT: must be compiled with -i switch (case-insensitive scanner)
*
*/
#include <stdio.h>
#include "lib/framework/frame.h"
#include "src/base.h"
#include "src/droiddef.h"
#include "src/structuredef.h"
//#include "src/scriptfuncs.h"
#include "lib/script/script.h"
#include "src/scriptfuncs.h"
#include "lib/script/chat_processing.h"
/* Get the Yacc definitions */
#include "lib/script/chat_parser.h"
/* Maximum length for any TEXT value */
#ifndef YYLMAX
#define YYLMAX 255
#endif
/* Store for any string values */
static char aText[TEXT_BUFFERS][YYLMAX];
static UDWORD currText=0;
/* Pointer to the input buffer */
static char *pInputBuffer = NULL;
static char *pEndBuffer = NULL;
static SDWORD playerIndex;
static INTERP_VAL parameter;
#define YY_INPUT(buf,result,max_size) \
if (pInputBuffer != pEndBuffer) { \
buf[0] = *(pInputBuffer++); result = 1; \
} else { \
buf[0] = EOF; result = YY_NULL; \
}
#undef chat_getc
#define chat_getc() (pInputBuffer != pEndBuffer ? *(pInputBuffer++) : EOF)
%}
%option yylineno
%option prefix="chat_"
%option nounput
%%
/* Match integer numbers */
-?[0-9]+ { chat_lval.ival = atol(chat_text); return R_INTEGER; }
/* Keywords - Terminals */
"?"+ { return _T_QM; }
"!"+ { return _T_EM; }
"."+ { return _T_FULLSTOP; }
":" { return _T_COLON; };
";" { return _T_SEMICOLON; };
"," { return _T_COMMA; };
"a" { return _T_A; }
"affirmative" { return _T_AFFIRMATIVE; }
"after" { return _T_AFTER; }
"ally" { return _T_ALLY; }
"am" { return _T_AM; }
"and" { return _T_AND; }
"any" { return _T_ANY; }
"attack" { return _T_ATTACK; }
"attacking" { return _T_ATTACKING; }
"beacon" { return _T_BEACON; }
"building" { return _T_BUILDING; }
"can't" { return _T_CANT; }
"center" { return _T_CENTER; }
"dead" { return _T_DEAD; }
"derrick" { return _T_DERRICK; }
"do" { return _T_DO; }
"drop" { return _T_DROP; }
"fine" { return _T_FINE; }
"get" { return _T_GET; }
"getting" { return _T_GETTING; }
"go" { return _T_GO; }
"going" { return _T_GOING; }
"gonna" { return _T_GONNA; }
"got" { return _T_GOT; }
"great" { return _T_GREAT; }
"have" { return _T_HAVE; }
"has" { return _T_HAS; }
"help" { return _T_HELP; }
"i" { return _T_I; }
"i'm" { return _T_IM; }
"is" { return _T_IS; }
"let's" { return _T_LETS; }
"me" { return _T_ME; }
"no" { return _T_NO; }
"of course" { return _T_OFCOURSE; }
"ok"+ { return _T_OK; }
"place" { return _T_PLACE; }
"possession" { return _T_POSSESSION; }
"power" { return _T_POWER; }
"pumping" { return _T_PUMPING; }
"put" { return _T_PUT; }
"roger" { return _T_ROGER; }
"see" { return _T_SEE; }
"some" { return _T_SOME; }
"status" { return _T_STATUS; }
"stop" { return _T_STOP; }
"sure" { return _T_SURE; }
"u" { return _T_U; }
"units" { return _T_UNITS; }
"vtols" { return _T_VTOLS; }
"wait" { return _T_WAIT; }
"where" { return _T_WHERE; }
"yea" { return _T_YEA; }
"yeah" { return _T_YEAH; }
"yes" { return _T_YES; }
"you" { return _T_YOU; }
/* <<EOF>> { return _T_EOF; } */
-?[0-9]+ {
chat_lval.ival = atol(chat_text);
return R_INTEGER;
}
[0-9_a-zA-Z_]* {
playerIndex = getPlayerFromString(chat_text);
if(playerIndex >= 0)
{
//console( "matched 'player'");
chat_lval.ival = playerIndex;
return R_PLAYER;
}
}
/* [^ \t\n\<\>\[\]\(\)]+ { return T_WORD; } */
/* [^ \t\n]+ { console( "matched 'T_WORD'"); return T_WORD; } */
/* Skip white space */
[ \t\n\x0d\x0a] ;
/* Match anything that's been missed and pass it as a char */
/* . {console( "matched 'anything else '%s'", chat_text[0]); return chat_text[0];} */
%%
/* Set the current input buffer for the lexer */
void chatSetInputBuffer(char *pBuffer, UDWORD size)
{
pInputBuffer = pBuffer;
pEndBuffer = pBuffer + size;
/* Reset the lexer in case it's been used before */
chat__flush_buffer(YY_CURRENT_BUFFER);
}
void chatGetErrorData(int *pLine, char **ppText)
{
*pLine = chat_lineno;
*ppText = chat_text;
}
int chat_wrap(void)
{
return 1;
}

687
lib/script/chat_parser.y Normal file
View File

@ -0,0 +1,687 @@
%{
/*
* chat_parser.y
*
* yacc grammar for multiplayer chat messages
*
*/
#include <stdio.h>
#include <string.h>
#include "lib/framework/frame.h"
#include "lib/framework/frameresource.h"
#include "lib/script/script.h"
#include "src/scripttabs.h"
#include "lib/script/script.h"
#include "lib/script/chat_processing.h"
#define MAX_CHAT_ARGUMENTS 10
/* Holds information about a processed player chat message */
CHAT_MSG chat_msg;
// The current script code
static SCRIPT_CODE *psCurrScript;
// The current script context
static SCRIPT_CONTEXT *psCurrContext;
// Parameter variable
static INTERP_VAL scrParameter;
extern int chat_lex(void);
// Store extracted command for use in scripts
static void chat_store_command(char *command);
/* Return value of the chat parsing - to be used in scripts */
static BOOL chat_store_parameter(INTERP_VAL *parameter);
// Store players that were addressed in a command
static void chat_store_player(SDWORD cmdIndex, SDWORD playerIndex);
// Reset a command
static void chat_reset_command(SDWORD cmdIndex);
// Information extracted from message and available for scripts
//static int numMsgParams=0; //number of parameters currently extracted/stored
//static INTERP_VAL msgParams[MAX_CHAT_ARGUMENTS];
/* Store command parameter extracted from the message */
static BOOL chat_store_parameter(INTERP_VAL *cmdParam)
{
SDWORD numCmdParams, numCommands;
//console("chat_store_parameter: new parameter");
/* Make sure we have no overflow */
//if(numMsgParams >= MAX_CHAT_ARGUMENTS)
if(chat_msg.numCommands >= MAX_CHAT_COMMANDS)
{
ASSERT(FALSE, "chat_store_parameter: too many commands in a message");
return FALSE;
}
numCommands = chat_msg.numCommands;
numCmdParams = chat_msg.cmdData[numCommands].numCmdParams;
/* Make sure we still have room for more parameters */
if(numCmdParams >= MAX_CHAT_CMD_PARAMS)
{
ASSERT(FALSE, "chat_store_parameter: out of parameters for command %d", numCommands);
return FALSE;
}
/* Store parameter for command we are currently processing */
//memcpy(&(msgParams[numMsgParams]), parameter, sizeof(INTERP_VAL));
memcpy(&(chat_msg.cmdData[numCommands].parameter[numCmdParams]), cmdParam, sizeof(INTERP_VAL));
chat_msg.cmdData[numCommands].numCmdParams++;
return TRUE;
}
// Store extracted command for use in scripts
static void chat_store_command(char *command)
{
SDWORD numCmdParams, numCommands;
//console("chat_store_command: new command: %s", command);
/* Make sure we have no overflow */
if(chat_msg.numCommands >= MAX_CHAT_COMMANDS)
{
ASSERT(FALSE, "chat_store_command: too many commands in a message");
return;
}
numCommands = chat_msg.numCommands;
numCmdParams = chat_msg.cmdData[numCommands].numCmdParams;
/* Make sure we still have room for more parameters */
if(numCmdParams >= MAX_CHAT_CMD_PARAMS)
{
ASSERT(FALSE, "chat_store_command: out of parameters for command %d", numCommands);
return;
}
/* Store command */
chat_msg.cmdData[numCommands].pCmdDescription = command;
chat_msg.numCommands++;
}
// Store players that were addressed in a command
static void chat_store_player(SDWORD cmdIndex, SDWORD playerIndex)
{
SDWORD i;
//console("chat_store_player: player %d addressd in command %d", playerIndex, cmdIndex);
/* Make sure we have no overflow */
if(cmdIndex < 0 || cmdIndex >= MAX_CHAT_COMMANDS)
{
ASSERT(FALSE, "chat_store_player: command message out of bounds: %d", cmdIndex);
return;
}
if(playerIndex == -1) //no player specified means all players addressed
{
/* Ally players addressed */
for(i=0; i<MAX_PLAYERS; i++)
{
chat_msg.cmdData[cmdIndex].bPlayerAddressed[i] = TRUE;
}
}
else if(playerIndex >= 0 && playerIndex < MAX_PLAYERS)
{
chat_msg.cmdData[cmdIndex].bPlayerAddressed[playerIndex] = TRUE;
}
else /* Wrong player index */
{
ASSERT(FALSE, "chat_store_player: wrong player index: %d", playerIndex);
return;
}
}
static void chat_reset_command(SDWORD cmdIndex)
{
SDWORD i;
ASSERT(cmdIndex >= 0 && cmdIndex < MAX_CHAT_COMMANDS,
"chat_reset_command: command index out of bounds: %d", cmdIndex);
chat_msg.cmdData[cmdIndex].numCmdParams = 0;
for(i=0; i<MAX_PLAYERS; i++)
{
chat_msg.cmdData[cmdIndex].bPlayerAddressed[i] = FALSE;
}
}
%}
%name-prefix="chat_"
%union {
BOOL bval;
INTERP_TYPE tval;
char *sval;
UDWORD vindex;
SDWORD ival;
}
/* Start symbol */
%start R_PHRASE
/* keywords */
%token BRACKET_OPEN
%token BRACKET_CLOSE
%token SQ_BRACKET_OPEN
%token SQ_BRACKET_CLOSE
%token PIPE
%token T_WORD
%token _T_QM
%token _T_EM
%token _T_FULLSTOP
%token _T_COLON
%token _T_SEMICOLON
%token _T_COMMA
%token _T_A
%token _T_AFFIRMATIVE
%token _T_AFTER
%token _T_ALLY
%token _T_AM
%token _T_AND
%token _T_ANY
%token _T_ATTACK
%token _T_ATTACKING
%token _T_BEACON
%token _T_BUILDING
%token _T_CANT
%token _T_CENTER
%token _T_DEAD
%token _T_DERRICK
%token _T_DO
%token _T_DROP
%token _T_FINE
%token _T_GET
%token _T_GETTING
%token _T_GO
%token _T_GOING
%token _T_GONNA
%token _T_GOT
%token _T_GREAT
%token _T_HAVE
%token _T_HAS
%token _T_HELP
%token _T_I
%token _T_IM
%token _T_A
%token _T_IS
%token _T_LETS
%token _T_ME
%token _T_NO
%token _T_OFCOURSE
%token _T_OK
%token _T_PLACE
%token _T_POSSESSION
%token _T_POWER
%token _T_PUMPING
%token _T_PUT
%token _T_ROGER
%token _T_SEE
%token _T_SOME
%token _T_STATUS
%token _T_STOP
%token _T_SURE
%token _T_U
%token _T_UNITS
%token _T_VTOLS
%token _T_WAIT
%token _T_WHERE
%token _T_YEA
%token _T_YEAH
%token _T_YES
%token _T_YOU
%token _T_EOF 0
/* Typed Keywords */
%token <ival> R_PLAYER
%token <ival> R_INTEGER
%%
/* The top-level rule, consists of
* one or more sentences: commands or addressed commands
* or unrecognized messages (handled in R_COMMAND)
*/
R_PHRASE: R_ADDRESSED_COMMAND
| R_COMMAND
{
chat_store_player(chat_msg.numCommands - 1, -1); /* No player list means current command is addressed to everyone */
}
| R_PHRASE R_ADDRESSED_COMMAND
| R_PHRASE R_COMMAND
{
chat_store_player(chat_msg.numCommands - 1, -1);
}
| error
{
//console("-unrecognized phrase");
/* Reset current command */
chat_reset_command(chat_msg.numCommands);
}
;
/* A command addressed to one or more players
*/
R_ADDRESSED_COMMAND: R_PLAYER_LIST _T_COLON R_COMMAND /* "Red, Black: attack yellow" */
//{console("RULE: player list: command");}
| R_PLAYER_LIST _T_SEMICOLON R_COMMAND /* "Red, Black; attack yellow" */
//{console("RULE: player list; command");}
| R_PLAYER_LIST _T_COMMA R_COMMAND /* "Red, Black, attack yellow" */
//{console("RULE: player list, command");}
| R_PLAYER_LIST R_COMMAND /* No delimeter: "Red, Black attack yellow" */
//{console("RULE: player list command");}
;
/* Any of the recognizable commands -
* since there can't be any player list at this stage,
* should address all players receiving this message by default:
* EXAMPLE:
* 1) "Help me!" - addresses all players receiving the message
* 2) "Black and Red, help me!" - addresses players 'Black' and 'red' only
*/
R_COMMAND: R_ALLY_OFFER /* ally me */
{
chat_store_command("ally me"); /* Store current command */
}
| R_ASK_READINESS /* go? */
{
chat_store_command("go?");
}
| R_INITIATE_ACTION /* go ! */
{
chat_store_command("go!");
}
| R_DEMAND_BEACON /* drop a beacon */
{
chat_store_command("drop a beacon");
}
| R_MEET_CENTER /* go center */
{
chat_store_command("go center");
}
| R_ASK_STATUS /* status? */
{
chat_store_command("status?");
}
| R_BUILDING_UNITS /* pumping units */
{
chat_store_command("pumping units");
}
| R_STOP /* stop! */
{
chat_store_command("stop");
}
| R_WONDER_IF_HAVE_POWER /* Ask if player has power */
{
chat_store_command("got power?");
}
| R_DEMAND_HELP /* help me! */
{
chat_store_command("help me");
}
| R_REPORT_SAFETY /* i'm ok */
{
chat_store_command("i'm ok");
}
| R_AFFIRMATIVE /* roger! */
{
chat_store_command("roger");
}
| R_ALLY_PLAYER /* ally blue */
{
chat_store_command("ally player");
}
| R_ATTACK_PLAYER /* attack blue */
{
chat_store_command("attack player");
}
| R_ATTACKING_PLAYER /* attacking blue */
{
chat_store_command("attacking player?");
}
| R_PLAYER_HAS_VTOLS /* blue has VTOLS */
{
chat_store_command("player has vtols");
}
;
/* A reference to certain players: "yellow, help me"
*/
R_PLAYER_LIST: R_PLAYER
{
chat_store_player(chat_msg.numCommands, $1); /* Remember this player was addressed in current command */
}
| R_PLAYER_LIST R_PLAYER /* without any delimeters: "yellow black red" */
{
chat_store_player(chat_msg.numCommands, $2);
}
| R_PLAYER_LIST _T_COMMA R_PLAYER /* "yellow, black, red" */
{
chat_store_player(chat_msg.numCommands, $3);
}
| R_PLAYER_LIST _T_AND R_PLAYER /* "yellow, black and red" */
{
chat_store_player(chat_msg.numCommands, $3);
}
;
/* A Article or nothing
*/
R_A_OR_EMPTY: /* Empty */
| _T_A
;
/* ?????
R_QM_REPETITION: _T_QM
| R_QM_REPETITION _T_QM */
;
/* QM or nothing */
//R_QM_OR_EMPTY: /* Empty */
// | _T_QM /* ????? or nothing */
// ;
/* !!!!!!
R_EM_REPETITION: _T_EM
| R_EM_REPETITION _T_EM */
;
/* EM or nothing */
//R_EM_OR_EMPTY: /* Empty */
// | _T_EM /* !!!!! or nothing */
// ;
/* Punctuation mark */
R_PUNCTUATION_MARK: _T_QM
| _T_EM
| R_PUNCTUATION_MARK _T_QM /* ?????!!!!??! */
| R_PUNCTUATION_MARK _T_EM /* ?????!!!!??! */
;
/* Full stop or end of file
* (a 'virtual' rule)
*/
R_FULLSTOP_OR_EOF: /* Empty */ /* Needed for other rules */
| _T_EOF /* End of input */
| _T_FULLSTOP /* . */
;
/* End of a declarative sentence
* can't be a question
*/
R_EOD: _T_EM /* !!!! */
| R_FULLSTOP_OR_EOF
;
/* End of a question
* can't end with an exclamation mark
*/
R_EOQ: _T_QM
| R_FULLSTOP_OR_EOF
;
/* (any possible) end of the sentence - similar
* to "End of a declarative sentence",
* but can additionally end with a question.
*/
R_EOS: R_EOD /* End of a declarative sentence */
| R_PUNCTUATION_MARK /* ???!!?!?!?!? */
;
/* Express attack intention */
R_ATTACKING: _T_ATTACKING
| _T_GOING
| _T_GOING _T_AFTER
;
/* Attack */
R_INITIATE_ATTACK: _T_ATTACK
| _T_GET
;
R_PUT_DOWN: _T_PUT | _T_DROP | _T_PLACE; /* put */
R_INCREASING_NUMBER: _T_PUMPING /* pumping/getting */
| _T_GETTING
| _T_BUILDING
;
R_YOU: _T_YOU
| _T_U /* u */
;
/* 'You' - pronoun for questions */
R_DO_YOU: R_YOU /* like in "you got any..." */
| _T_DO R_YOU /* do you */
;
/* Used in questions */
R_POSSESSION_Q: _T_HAVE
| _T_GOT
;
R_POSSESSES: _T_HAS
| _T_GOT
;
R_QUANTITY: _T_ANY
| _T_SOME
;
R_DO_YOU_HAVE_ANY: R_DO_YOU R_POSSESSION_Q R_QUANTITY
| R_DO_YOU R_POSSESSION_Q
| R_POSSESSION_Q R_QUANTITY /* got any.. */
| R_POSSESSION_Q /* got <substantive>? */
| R_QUANTITY /* any <substantive>? */
;
R_YES_FORMS: _T_YES
| _T_YEA
| _T_YEAH
;
R_CONFIDENCE_EXPRESSION: _T_SURE
| _T_OFCOURSE
;
R_AGREEMENT_EXPRESSION: R_YES_FORMS
| _T_FINE
| _T_OK
;
R_AFFIRMATIVE_FORMS: R_AGREEMENT_EXPRESSION
| R_CONFIDENCE_EXPRESSION
| _T_ROGER /* roger */
| _T_AFFIRMATIVE
;
/*******************************************/
/* FINAL RULES, SHOULD BE PART OF R_PHRASE */
/*******************************************/
/* Ask a player to ally myself */
R_ALLY_OFFER: _T_ALLY _T_ME R_EOS /* ally me */
| _T_ALLY R_EOS /* "ally" at the end (otherwise breaks "ally me") */
/* | _T_ALLY */
;
/* Chech if player is willing
* to initiate some actions
*/
R_ASK_READINESS: _T_GO _T_QM; /* go? */
/* Tell to start some action */
R_INITIATE_ACTION: _T_GO R_EOD; /* go!! */
/* Tell to drop a beacon */
R_DEMAND_BEACON: R_PUT_DOWN R_A_OR_EMPTY _T_BEACON; /* put a beacon */
/* Tell to meet in the center of the map */
R_MEET_CENTER: _T_GO _T_CENTER R_EOS; /* go center */
/* Ask for the current status */
R_ASK_STATUS: _T_STATUS R_EOS; /* status? */
/* Player is building units */
R_BUILDING_UNITS: R_INCREASING_NUMBER _T_UNITS R_EOD; /* pumping units */
/* Stop command */
R_STOP: _T_STOP R_EOD; /* stop */
/* Ask if player has power */
R_WONDER_IF_HAVE_POWER: R_DO_YOU_HAVE_ANY _T_POWER R_EOQ; /* do you have power? */
/* Ask for help */
R_DEMAND_HELP: _T_HELP _T_ME R_EOS /* help me!!!!! */
| _T_HELP R_EOS /* help!?!? */
;
/* Tell player i'm safe - no danger anymore */
R_REPORT_SAFETY: _T_IM _T_OK; /* i'm ok */
/* Positive order feedback */
R_AFFIRMATIVE: R_AFFIRMATIVE_FORMS /* yes, roger etc */
| R_AFFIRMATIVE R_AFFIRMATIVE_FORMS /* accept repetitions: "yes yes yeah" */
| R_AFFIRMATIVE R_EOD /* can also be emotional: "yea!!! yeah! yes!!!!" */
;
/* Ask to ally a player */
R_ALLY_PLAYER: _T_ALLY R_PLAYER R_EOD /* ally blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
chat_error("chat command: Too many parameters in the message");
}
}
;
/* Ask to start attack a player */
R_ATTACK_PLAYER: R_INITIATE_ATTACK R_PLAYER R_EOD /* attack blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
chat_error("chat command: Too many parameters in the message");
}
}
| _T_GO R_PLAYER R_EOD /* go blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
chat_error("chat command: Too many parameters in the message");
}
}
;
/* Notify i'm attacking player */
R_ATTACKING_PLAYER: R_ATTACKING R_PLAYER R_EOD /* attacking blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
chat_error("chat command: Too many parameters in the message");
}
}
;
/* Notify about player using VTOLs */
R_PLAYER_HAS_VTOLS: R_PLAYER R_POSSESSES _T_VTOLS R_EOD /* blue has VTOLS */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $1;
if(!chat_store_parameter(&scrParameter))
{
chat_error("chat command: Too many parameters in the message");
}
}
;
%%
/* Initialize Bison and start chat processing */
BOOL chatLoad(char *pData, UDWORD size)
{
SDWORD cmdIndex,parseResult;
/* Don't parse the same message again for a different player */
if(strcmp(pData, chat_msg.lastMessage) == 0) //just parsed this message for some other player
{
return TRUE; //keep all the parsed data unmodified
}
/* Tell bison what to parse */
chatSetInputBuffer(pData, size);
/* Reset previous chat processing */
chat_msg.numCommands = 0;
/* Initialize command data */
for(cmdIndex=0; cmdIndex<MAX_CHAT_COMMANDS; cmdIndex++)
{
chat_reset_command(cmdIndex);
}
/* Invoke bison and parse */
parseResult = chat_parse();
/* Remember last message we parsed */
strcpy(&(chat_msg.lastMessage[0]), pData);
/* See if we were successfull parsing */
if (parseResult != 0)
{
return FALSE;
}
return TRUE;
}
/* A simple error reporting routine */
void chat_error(const char *pMessage,...)
{
int line;
char *pText;
char aTxtBuf[1024];
va_list args;
va_start(args, pMessage);
vsprintf(aTxtBuf, pMessage, args);
chatGetErrorData(&line, &pText);
debug(LOG_WARNING, "multiplayer message parse error: %s at line %d, token: %d, text: '%s'",
aTxtBuf, line, chat_char, pText);
va_end(args);
}

View File

@ -0,0 +1,58 @@
/*
* chat_processing.h
*
* Misc definitions for chat parser
*/
#ifndef _chat_processing_h
#define _chat_processing_h
#include "lib/framework/frame.h"
#include "src/base.h"
#include "src/droiddef.h"
#include "src/structuredef.h"
#include "lib/script/script.h"
#ifndef MAXSTRLEN
#define MAXSTRLEN 255
#endif
/* Max number of commands in a player chat message */
#define MAX_CHAT_COMMANDS 10
/* Max number of parameters allowed in a single chat message command */
#define MAX_CHAT_CMD_PARAMS 10
/* Holds information for each recognized
* command in a chat message */
typedef struct _chat_command_data
{
char *pCmdDescription; /* String representing a certain command */
BOOL bPlayerAddressed[MAX_PLAYERS]; /* Flag to indicate whether a command was addressed to a certain player */
SDWORD numCmdParams; /* Number of extracted parameters associated with each command */
INTERP_VAL parameter[MAX_CHAT_CMD_PARAMS]; /* Parameters extracted from text - to be used with scripts */
}CHAT_CMD_DATA;
typedef struct _chat_command
{
char lastMessage[MAXSTRLEN]; /* Parse the same mesage only once - in case more than one player is trying to parse */
SDWORD numCommands; /* Total number of commands in chat message */
CHAT_CMD_DATA cmdData[MAX_CHAT_COMMANDS]; /* Holds information for each recognized command */
}CHAT_MSG;
extern CHAT_MSG chat_msg;
/* Store parameter extracted from the message - for scripts */
//extern BOOL chat_store_parameter(INTERP_VAL *parameter);
extern void chat_error(const char *pMessage,...);
extern void chatGetErrorData(int *pLine, char **ppText);
/* Set the current input buffer for the lexer */
extern void chatSetInputBuffer(char *pBuffer, UDWORD size);
// Load message
extern BOOL chatLoad(char *pData, UDWORD size);
#endif

View File

@ -523,10 +523,7 @@ BOOL interpRunScript(SCRIPT_CONTEXT *psContext, INTERP_RUNTYPE runType, UDWORD i
goto exit_with_error;
}
/* get local variable */
psVar = &(psContext->psCode->ppsLocalVarVal[CurEvent][data]);
//TODO: fix
sVal.v.oval = &(psVar->v.ival);
sVal.v.oval = &(psContext->psCode->ppsLocalVarVal[CurEvent][data]);
TRCPRINTF(("PUSHREF "));
TRCPRINTVAL(&sVal);
@ -569,9 +566,9 @@ BOOL interpRunScript(SCRIPT_CONTEXT *psContext, INTERP_RUNTYPE runType, UDWORD i
// The type of the variable is stored in with the opcode
sVal.type = (INTERP_TYPE)(InstrPointer->v.ival & OPCODE_DATAMASK);
// store the pointer
psVar = interpGetVarData(psGlobals, ((INTERP_VAL *)(InstrPointer + 1))->v.ival);
sVal.v.oval = &(psVar->v.ival);
// store pointer to INTERP_VAL
sVal.v.oval = interpGetVarData(psGlobals, ((INTERP_VAL *)(InstrPointer + 1))->v.ival);
TRCPRINTF(("PUSHREF "));
TRCPRINTVAL(&sVal);
TRCPRINTF(("\n"));
@ -801,7 +798,7 @@ BOOL interpRunScript(SCRIPT_CONTEXT *psContext, INTERP_RUNTYPE runType, UDWORD i
// jump out of the code
InstrPointer = pCodeEnd;
break;
case OP_PAUSE:
case OP_PAUSE:
ASSERT( InstrPointer->type == VAL_PKOPCODE,
"wrong value type passed for OP_PAUSE: %d", InstrPointer->type);
@ -841,7 +838,7 @@ BOOL interpRunScript(SCRIPT_CONTEXT *psContext, INTERP_RUNTYPE runType, UDWORD i
goto exit_with_error;
}
InstrPointer += aOpSize[opcode];
break;
break;
default:
debug(LOG_ERROR, "interpRunScript: unknown opcode: %d, type: %d", opcode);
ASSERT( FALSE, "interpRunScript: unknown opcode: %d, type: %d", opcode, InstrPointer->type );
@ -1013,6 +1010,15 @@ BOOL interpCheckEquiv(INTERP_TYPE to, INTERP_TYPE from)
return FALSE;
}
/* Void pointer is compatible with any other type */
if(toRef == TRUE && fromRef == TRUE)
{
if(to == VAL_VOID)
{
return TRUE;
}
}
if (to == from)
{
return TRUE;

View File

@ -178,7 +178,7 @@ BOOL scriptGetVarIndex(SCRIPT_CODE *psCode, char *pID, UDWORD *pIndex)
these aren't currently checked for, but it's a lot clearer what's going on if they're all here */
BOOL scriptTypeIsPointer(INTERP_TYPE type)
{
ASSERT( ((type < ST_MAXTYPE) || (type >= VAL_REF)), "scriptTypeIsPointer: invalid type" );
ASSERT( ((type < ST_MAXTYPE) || (type >= VAL_REF)), "scriptTypeIsPointer: invalid type: %d", type );
// any value or'ed with VAL_REF is a pointer
if (type >= VAL_REF) return TRUE;
switch (type) {
@ -225,7 +225,7 @@ BOOL scriptTypeIsPointer(INTERP_TYPE type)
case ST_SOUND:
return FALSE;
default:
ASSERT(FALSE, "scriptTypeIsPointer: unhandled type");
ASSERT(FALSE, "scriptTypeIsPointer: unhandled type: %d", type );
return FALSE;
}
}

View File

@ -87,7 +87,7 @@ extern void cpPrintProgram(SCRIPT_CODE *psProg);
extern BOOL scriptGetVarIndex(SCRIPT_CODE *psCode, char *pID, UDWORD *pIndex);
/* returns true if passed INTERP_TYPE is used as a pointer in INTERP_VAL, false otherwise */
BOOL scriptTypeIsPointer(INTERP_TYPE type);
extern BOOL scriptTypeIsPointer(INTERP_TYPE type);
/* Run a compiled script */
extern BOOL interpRunScript(SCRIPT_CONTEXT *psContext, INTERP_RUNTYPE runType,

View File

@ -5253,6 +5253,14 @@ boolexp: boolexp _AND boolexp
/* Return the code block */
$$ = psCurrBlock;
}
| stringexp BOOLEQUAL stringexp
{
codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock);
CHECK_CODE_ERROR(codeRet);
/* Return the code block */
$$ = psCurrBlock;
}
| userexp BOOLEQUAL userexp
{
if (!interpCheckEquiv($1->type,$3->type))
@ -5295,6 +5303,14 @@ boolexp: boolexp _AND boolexp
/* Return the code block */
$$ = psCurrBlock;
}
| stringexp NOTEQUAL stringexp
{
codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock);
CHECK_CODE_ERROR(codeRet);
/* Return the code block */
$$ = psCurrBlock;
}
| userexp NOTEQUAL userexp
{
if (!interpCheckEquiv($1->type,$3->type))

View File

@ -44,6 +44,9 @@ static UDWORD currEntry=0;
/* The block heap the stack was created in */
static BLOCK_HEAP *psStackBlock;
/* Get rid of the top value without returning it */
static inline BOOL stackRemoveTop(void);
/* Check if the stack is empty */
BOOL stackEmpty(void)
@ -296,8 +299,6 @@ BOOL stackPopParams(SDWORD numParams, ...)
return FALSE;
}
//string support
// Get the values, checking their types
index = currEntry;
for (i=0; i< numParams; i++)
@ -326,9 +327,28 @@ BOOL stackPopParams(SDWORD numParams, ...)
return FALSE;
}
if (scriptTypeIsPointer(psVal->type))
*((void**)pData) = psVal->v.oval;
{
if(psVal->type >= VAL_REF) //if it's a reference
{
INTERP_VAL *refVal;
refVal = psVal->v.oval; //oval is a pointer to INTERP_VAL in this case
/* doublecheck types */
ASSERT(interpCheckEquiv(type & ~VAL_REF, refVal->type), "stackPopParams: type mismatch for a reference: %d/%d",
type & ~VAL_REF, refVal->type); //type of provided container and type of the INTERP_VAL pointed by psVal->v.oval
*((void**)pData) = &(refVal->v.ival); /* get pointer to the union */
}
else //some pointer type
{
*((void**)pData) = psVal->v.oval;
}
}
else
{
*((SDWORD*)pData) = psVal->v.ival;
}
}
else //TODO: allow only compatible types
{
@ -595,17 +615,20 @@ BOOL stackBinaryOp(OPCODE opcode)
psV1->v.bval = psV1->v.bval || psV2->v.bval;
break;
case OP_EQUAL:
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){
if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){
psV1->v.bval = psV1->v.fval == psV2->v.fval;
}else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){
psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) == 0); /* case-insensitive */
}else{
psV1->v.bval = psV1->v.ival == psV2->v.ival;
}
psV1->type = VAL_BOOL;
break;
case OP_NOTEQUAL:
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){
if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){
psV1->v.bval = psV1->v.fval != psV2->v.fval;
}else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){
psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) != 0); /* case-insensitive */
}else{
psV1->v.bval = psV1->v.ival != psV2->v.ival;
}
@ -754,10 +777,15 @@ BOOL stackUnaryOp(OPCODE opcode)
switch (psVal->type)
{
case (VAL_REF | VAL_INT):
*((SDWORD *)psVal->v.oval) = *((SDWORD *)psVal->v.oval) + 1;
psVal = psVal->v.oval; //get variable
ASSERT(psVal->type == VAL_INT, "Invalid type for increment opcode: %d", psVal->type);
psVal->v.ival++;
/* Just get rid of the variable pointer, since already increased it */
if (!stackPop(psVal))
if (!stackRemoveTop())
{
debug( LOG_ERROR, "stackUnaryOpcode: OP_INC: could not pop" );
return FALSE;
@ -774,10 +802,15 @@ BOOL stackUnaryOp(OPCODE opcode)
switch (psVal->type)
{
case (VAL_REF | VAL_INT):
*((SDWORD *)psVal->v.oval) = *((SDWORD *)psVal->v.oval) - 1;
psVal = psVal->v.oval; //get variable
ASSERT(psVal->type == VAL_INT, "Invalid type for decrement opcode: %d", psVal->type);
psVal->v.ival--;
/* Just get rid of the variable pointer, since already decreased it */
if (!stackPop(psVal))
if (!stackRemoveTop())
{
debug( LOG_ERROR, "stackUnaryOpcode: OP_DEC: could not pop" );
return FALSE;
@ -971,6 +1004,31 @@ void stackShutDown(void)
}
}
/* Get rid of the top value without returning it */
static inline BOOL stackRemoveTop(void)
{
if ((psCurrChunk->psPrev == NULL) && (currEntry == 0))
{
debug(LOG_ERROR, "stackRemoveTop: stack empty");
ASSERT( FALSE, "stackRemoveTop: stack empty" );
return FALSE;
}
/* move the stack pointer down one */
if (currEntry == 0)
{
/* have to move onto the previous chunk. */
psCurrChunk = psCurrChunk->psPrev;
currEntry = psCurrChunk->size -1;
}
else
{
currEntry--;
}
return TRUE;
}
/* Reset the stack to an empty state */
void stackReset(void)

View File

@ -72,6 +72,7 @@
#include "aiexperience.h"
#include "display3d.h" //for showRangeAtPos()
#include "multimenu.h"
#include "lib/script/chat_processing.h"
static INTERP_VAL scrFunctionResult; //function return value to be pushed to stack
@ -82,14 +83,11 @@ static INTERP_VAL scrFunctionResult; //function return value to be pushed to sta
// If this is defined then check max number of units not reached before adding more.
#define SCRIPT_CHECK_MAX_UNITS
SDWORD playerFlag[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
char strParam1[MAXSTRLEN], strParam2[MAXSTRLEN]; //these should be used as string parameters for stackPopParams()
static SDWORD playerFlag[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
static char strParam1[MAXSTRLEN], strParam2[MAXSTRLEN]; //these should be used as string parameters for stackPopParams()
// -----------------------------------------------------------------------------------------
BOOL structHasModule(STRUCTURE *psStruct);
SDWORD guessPlayerFromMessage(char **str);
SDWORD getPlayerFromString(char *playerName);
SDWORD getFirstWord(char *sText, char **sWord, SDWORD *readCount);
/******************************************************************************************/
/* Check for objects in areas */
@ -5054,14 +5052,14 @@ BOOL bVisible;
// returns the nearest gateway bottleneck to a specified point
BOOL scrGetNearestGateway( void )
{
SDWORD x,y;
SDWORD gX,gY;
UDWORD nearestSoFar;
UDWORD dist;
GATEWAY *psGateway;
SDWORD retX,retY;
SDWORD *rX,*rY;
BOOL success;
SDWORD x,y;
SDWORD gX,gY;
UDWORD nearestSoFar;
UDWORD dist;
GATEWAY *psGateway;
SDWORD retX,retY;
SDWORD *rX,*rY;
BOOL success;
if(!stackPopParams(4, VAL_INT, &x, VAL_INT, &y, VAL_REF|VAL_INT, &rX, VAL_REF|VAL_INT, &rY))
{
@ -10166,12 +10164,9 @@ BOOL objectInRangeVis(BASE_OBJECT *psList, SDWORD x, SDWORD y, SDWORD range, SDW
BOOL scrPursueResearch(void)
{
RESEARCH *psResearch;
SDWORD player;
UWORD cur,index,tempIndex,foundIndex;
SDWORD foundIndex,index,player,cur,tempIndex,Stack[400];
SWORD top;
UWORD Stack[400];
BOOL found;
PLAYER_RESEARCH *pPlayerRes;
@ -10430,56 +10425,6 @@ BOOL scrSetPlayerName(void)
return TRUE;
}
SDWORD guessPlayerFromMessage(char **str)
{
SDWORD player,count=0;
char *endOfPlayerList;
char playerName[255];
SDWORD storage=0;
BOOL bOK=FALSE;
ASSERT(MAX_PLAYERS <= 8, "guessPlayerFromMessage: MAX_PLAYERS too high");
endOfPlayerList = *str;
player = 0;
debug(LOG_SCRIPT, "now checking string='%s'",*str);
while( sscanf(*str, "%[^',:;?! ]%*[,';?! ]%n", playerName, &count) //didn't reach the end //first field: what to stop on; second field: what to read (skip) afterwards
&& player >= 0 //last string was a player name
//&& *str < copyStr + strlen(copyStr)
)
{
if(count==0) /* nothing read ? */
break;
debug(LOG_SCRIPT, "playerName='%s' count=%d",playerName, count);
*str += count;
player = getPlayerFromString(playerName);
debug(LOG_SCRIPT, "player=%d",player);
if(player >= 0 && player < MAX_PLAYERS)
{
bOK = TRUE;
endOfPlayerList = *str; //remember where target player list ends
storage = storage | playerFlag[player]; //set player flag
debug(LOG_SCRIPT, "storage=%d",storage);
}
debug(LOG_SCRIPT, " ");
debug(LOG_SCRIPT, "now checking string = '%s'",*str);
}
*str = endOfPlayerList; //skip player list
return storage; //Must be 0 if no players
}
SDWORD getPlayerFromString(char *playerName)
{
UDWORD playerIndex;
@ -10488,27 +10433,27 @@ SDWORD getPlayerFromString(char *playerName)
for( playerIndex=0; playerIndex<MAX_PLAYERS; playerIndex++ )
{
/* check name */
debug(LOG_SCRIPT, "checking (%s,%s)",getPlayerName(playerIndex), playerName);
//debug(LOG_SCRIPT, "checking (%s,%s)",getPlayerName(playerIndex), playerName);
if (strncasecmp(getPlayerName(playerIndex),playerName, 255) == 0)
{
debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
//debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
return playerIndex;
}
/* check color */
debug(LOG_SCRIPT, "checking (%s,%s)",getPlayerColourName(playerIndex), playerName);
//debug(LOG_SCRIPT, "checking (%s,%s)",getPlayerColourName(playerIndex), playerName);
if (strncasecmp(getPlayerColourName(playerIndex),playerName, 255) == 0)
{
debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
//debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
return playerIndex;
}
/* check player number */
sprintf(sPlayerNumber,"%d",playerIndex);
debug(LOG_SCRIPT, "checking (%s,%s)",sPlayerNumber, playerName);
//debug(LOG_SCRIPT, "checking (%s,%s)",sPlayerNumber, playerName);
if (strncasecmp(sPlayerNumber,playerName, 255) == 0)
{
debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
//debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
return playerIndex;
}
@ -10517,188 +10462,6 @@ SDWORD getPlayerFromString(char *playerName)
return -1;
}
BOOL scrGetTargetPlayers(void)
{
char *myMsg = NULL,*freeMsg = NULL;
char **ssval = NULL;
SDWORD players=0;
if (!stackPopParams(1, VAL_REF | VAL_STRING, &ssval))
{
debug(LOG_ERROR, "scrGetTargetPlayers(): stack failed");
return FALSE;
}
if(*ssval == NULL)
{
debug(LOG_ERROR, "scrGetTargetPlayers(): passed string was not initialized");
return FALSE;
}
debug(LOG_SCRIPT, "scrGetTargetPlayers: ssval='%s'", *ssval);
myMsg = (char*)MALLOC(255);
freeMsg = myMsg;
strcpy(myMsg,*ssval);
debug(LOG_SCRIPT, "scrGetTargetPlayers: myMsg='%s'", myMsg);
players = guessPlayerFromMessage(&myMsg);
debug(LOG_SCRIPT, "scrGetTargetPlayers: myMsg new='%s'", myMsg);
strcpy(*ssval, myMsg);
debug(LOG_SCRIPT, "scrGetTargetPlayers: ssval='%s'", *ssval);
scrFunctionResult.v.ival = players;
if (!stackPushResult(VAL_INT, &scrFunctionResult))
{
debug(LOG_ERROR, "scrGetTargetPlayers(): failed to push result");
return FALSE;
}
FREE(freeMsg);
return TRUE;
}
BOOL scrMatch(void)
{
char *sToParse = NULL, *sToMatch = NULL;
char *wordNeed = NULL, *wordFound = NULL;
SDWORD readCountParse=0,readCountMatch=0;
SDWORD fieldAssignedParse=0,fieldAssignedMatch=0;
BOOL ok = TRUE;
SDWORD *nResult;
if (!stackPopParams(3, VAL_STRING, &strParam1, VAL_STRING, &strParam2, VAL_REF|VAL_INT, &nResult))
{
debug(LOG_ERROR, "scrMatch(): stack failed");
return FALSE;
}
if(strParam1 == NULL)
{
debug(LOG_ERROR, "scrMatch(): message to parse is null");
return FALSE;
}
if(strParam2 == NULL)
{
debug(LOG_ERROR, "scrMatch(): string to match is null");
return FALSE;
}
sToParse = strParam1;
sToMatch = strParam2;
debug(LOG_SCRIPT, " ");
debug(LOG_SCRIPT, "sOrigToParse='%s'", strParam1);
debug(LOG_SCRIPT, "sOrigToMatch='%s'", strParam2);
wordFound = (char*)MALLOC(255);
wordNeed = (char*)MALLOC(255);
*nResult = -1;
while(ok)
{
/* get next word */
fieldAssignedParse = getFirstWord(sToParse,&wordFound,&readCountParse);
sToParse = sToParse + readCountParse; /* next time start with next word */
if(readCountParse == 0) /* sscanf returns 0 when last word is read */
sToParse = sToParse + strlen(wordFound);
/* get next word */
fieldAssignedMatch = getFirstWord(sToMatch,&wordNeed,&readCountMatch);
sToMatch = sToMatch + readCountMatch; /* next time start with next word */
if(readCountMatch == 0) /* sscanf returns 0 when last word is read */
sToMatch = sToMatch + strlen(wordNeed);
debug(LOG_SCRIPT, "wordFound '%s'", wordFound);
debug(LOG_SCRIPT, "wordNeed '%s'", wordNeed);
/* failed if *one* of the strings ended */
if((fieldAssignedParse > 0 && fieldAssignedMatch <= 0)
|| (fieldAssignedMatch > 0 && fieldAssignedParse <= 0))
{
debug(LOG_SCRIPT, "exit condition FAILED");
ok = FALSE;
break;
}
else if(fieldAssignedParse <= 0 && fieldAssignedMatch <= 0) /* no more words left in either of them */
{
debug(LOG_SCRIPT, "exit condition SUCCESS");
ok = TRUE;
break;
}
/*
* now compare both words
*/
if (strncasecmp(wordNeed,"<player>", 255) == 0) /* if we are looking for player */
{
debug(LOG_SCRIPT, "matching <player>");
*nResult = getPlayerFromString(wordFound);
if(*nResult == -1) /* failed to match player, stop */
{
debug(LOG_SCRIPT, "failed to match <player>");
ok = FALSE;
}
else
{
debug(LOG_SCRIPT, "matched <player>");
}
}
else if (strncasecmp(wordNeed,wordFound,255) != 0) /* just compare words to see if they match */
{
debug(LOG_SCRIPT, "words did not match");
ok = FALSE;
}
debug(LOG_SCRIPT, " ");
}
debug(LOG_SCRIPT, "END");
FREE(wordFound);
FREE(wordNeed);
scrFunctionResult.v.bval = ok;
if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
{
debug(LOG_ERROR, "scrGetTargetPlayers(): failed to push result");
return FALSE;
}
return TRUE;
}
SDWORD getFirstWord(char *sText, char **sWord, SDWORD *readCount)
{
SDWORD count=0,fieldsAssigned=0;
debug(LOG_SCRIPT, "--getWord: now checking string='%s'",sText);
ASSERT(*sWord != NULL, "getFirstWord: sWord is NULL");
strcpy(*sWord,""); /* clear */
fieldsAssigned = sscanf(sText, "%[^',:;?! ]%*[,';?! ]%n", *sWord, &count);
debug(LOG_SCRIPT, "--getWord: matched='%s', count=%d, fieldsAssigned=%d",*sWord, count, fieldsAssigned);
*readCount = count;
return fieldsAssigned;
}
/* Checks if a particular bit is set in an integer */
BOOL scrBitSet(void)
{
@ -10852,3 +10615,208 @@ BOOL scrSetDebugMenuEntry(void)
return TRUE;
}
/* Parse chat message and return number of commands that could be extracted */
BOOL scrProcessChatMsg(void)
{
if (!stackPopParams(1, VAL_STRING, &strParam1))
{
debug(LOG_ERROR, "scrProcessChatMsg(): stack failed");
return FALSE;
}
debug(LOG_WZ, "Now preparing to parse '%s'", strParam1);
if (!chatLoad(strParam1, strlen(strParam1)))
{
ASSERT("Couldn't process chat message: %s", strParam1);
return FALSE;
}
scrFunctionResult.v.ival = chat_msg.numCommands;
if (!stackPushResult(VAL_INT, &scrFunctionResult))
{
debug(LOG_ERROR, "scrProcessChatMsg(): failed to push result");
return FALSE;
}
return TRUE;
}
/* Returns number of command arguments for a certain
* chat command that could be extracted
*/
BOOL scrGetNumArgsInCmd(void)
{
SDWORD cmdIndex;
if (!stackPopParams(1, VAL_INT, &cmdIndex))
{
debug(LOG_ERROR, "scrGetNumArgsInCmd(): stack failed");
return FALSE;
}
/* Check command bounds */
if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
{
ASSERT(FALSE, "scrGetNumArgsInCmd: command inxed out of bounds: %d (num commands: %d)",
cmdIndex, chat_msg.numCommands);
return FALSE;
}
scrFunctionResult.v.ival = chat_msg.cmdData[cmdIndex].numCmdParams;
if (!stackPushResult(VAL_INT, &scrFunctionResult))
{
debug(LOG_ERROR, "scrGetNumArgsInCmd(): failed to push result");
return FALSE;
}
return TRUE;
}
/* Returns a string representing a certain chat command,
* based on the command index provided
*/
BOOL scrGetChatCmdDescription(void)
{
SDWORD cmdIndex;
char *pChatCommand=NULL;
if (!stackPopParams(1, VAL_INT, &cmdIndex))
{
debug(LOG_ERROR, "scrGetCommandDescription(): stack failed");
return FALSE;
}
/* Check command bounds */
if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
{
ASSERT(FALSE, "scrGetCommandDescription: command inxed out of bounds: %d (num commands: %d)",
cmdIndex, chat_msg.numCommands);
return FALSE;
}
/* Allocate memory for the comamnd string */
pChatCommand = (char*)MALLOC(MAXSTRLEN);
ASSERT(pChatCommand != NULL, "scrGetCommandDescription: out of memory");
/* Copy command */
strcpy(pChatCommand, chat_msg.cmdData[cmdIndex].pCmdDescription);
/* Make scrFunctionResult point to the valid command */
scrFunctionResult.v.sval = pChatCommand;
if (!stackPushResult(VAL_STRING, &scrFunctionResult))
{
debug(LOG_ERROR, "scrGetCommandDescription(): failed to push result");
FREE(pChatCommand);
return FALSE;
}
FREE(pChatCommand);
return TRUE;
}
/* Returns a certain parameter of a certain chat command
* Returns FALSE if failed
*/
BOOL scrGetChatCmdParam(void)
{
SDWORD cmdIndex, argIndex;
void *pArgument=NULL;
INTERP_TYPE argType=VAL_VOID;
BOOL bSuccess=TRUE; //failure on type mismatch
//if (!stackPopParams(3, VAL_INT, &cmdIndex, VAL_INT, &argIndex, VAL_REF | VAL_VOID, &pArgument))
//{
// debug(LOG_ERROR, "scrGetChatCmdParam(): stack failed");
// return FALSE;
//}
if (!stackPopParams(2, VAL_INT, &cmdIndex, VAL_INT, &argIndex))
{
debug(LOG_ERROR, "scrGetChatCmdParam(): stack failed");
return FALSE;
}
if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
{
ASSERT(FALSE, "scrGetChatCmdParam: command index out of bounds: %d", cmdIndex);
return FALSE;
}
if(argIndex < 0 || argIndex >= chat_msg.cmdData[cmdIndex].numCmdParams )
{
ASSERT(FALSE, "scrGetChatCmdParam: argument index for command %d is out of bounds: %d", cmdIndex, argIndex);
return FALSE;
}
/* Find out the type of the argument we are going to pass to the script */
argType = chat_msg.cmdData[cmdIndex].parameter[argIndex].type;
if (!stackPopParams(1, VAL_REF | argType, &pArgument))
{
debug(LOG_ERROR, "scrGetChatCmdParam(): stack failed or argument mismatch (expected type of argument: %d)", argType);
bSuccess = FALSE; //return type mismatch
//return FALSE;
}
if(pArgument == NULL)
{
ASSERT(FALSE, "scrGetChatCmdParam: nullpointer check failed");
bSuccess = FALSE;
//return FALSE;
}
/* Return command argument to the script */
if(bSuccess){
memcpy(pArgument, &(chat_msg.cmdData[cmdIndex].parameter[argIndex].v), sizeof(chat_msg.cmdData[cmdIndex].parameter[argIndex].v));
}
scrFunctionResult.v.bval = bSuccess;
if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
{
debug(LOG_ERROR, "scrGetChatCmdParam(): failed to push result");
return FALSE;
}
return TRUE;
}
/* Returns true if a certain command was addressed to a certain player */
BOOL scrChatCmdIsPlayerAddressed(void)
{
SDWORD cmdIndex,playerInQuestion;
if (!stackPopParams(2, VAL_INT, &cmdIndex, VAL_INT, &playerInQuestion))
{
debug(LOG_ERROR, "scrChatCmdIsPlayerAddressed(): stack failed");
return FALSE;
}
/* Check command bounds */
if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
{
ASSERT(FALSE, "scrChatCmdIsPlayerAddressed: command inxed out of bounds: %d (num commands: %d)",
cmdIndex, chat_msg.numCommands);
return FALSE;
}
/* Check player bounds */
if(playerInQuestion < 0 || playerInQuestion >= MAX_PLAYERS)
{
ASSERT(FALSE, "scrChatCmdIsPlayerAddressed: player inxed out of bounds: %d", playerInQuestion);
return FALSE;
}
scrFunctionResult.v.bval = chat_msg.cmdData[cmdIndex].bPlayerAddressed[playerInQuestion];
if (!stackPushResult(VAL_INT, &scrFunctionResult))
{
debug(LOG_ERROR, "scrChatCmdIsPlayerAddressed(): failed to push result");
return FALSE;
}
return TRUE;
}

View File

@ -591,8 +591,6 @@ extern BOOL scrGetStructureType(void);
extern BOOL scrGetPlayerName(void);
extern BOOL scrSetPlayerName(void);
extern BOOL scrGetTargetPlayers(void);
extern BOOL scrMatch(void);
extern BOOL scrBitSet(void);
extern BOOL scrAlliancesLocked(void);
extern BOOL scrASSERT(void);
@ -600,6 +598,11 @@ extern BOOL scrShowRangeAtPos(void);
extern BOOL scrToPow(void);
extern BOOL scrDebugMenu(void);
extern BOOL scrSetDebugMenuEntry(void);
extern BOOL scrProcessChatMsg(void);
extern BOOL scrGetChatCmdDescription(void);
extern BOOL scrGetNumArgsInCmd(void);
extern BOOL scrGetChatCmdParam(void);
extern BOOL scrChatCmdIsPlayerAddressed(void);
extern BOOL beingResearchedByAlly(SDWORD resIndex, SDWORD player);
@ -614,6 +617,8 @@ extern VIEWDATA *HelpViewData(SDWORD sender, char *textMsg, UDWORD LocX, UDWORD
extern MESSAGE * findHelpMsg(UDWORD player, SDWORD sender);
extern SDWORD getNumRepairedBy(DROID *psDroidToCheck, SDWORD player);
extern BOOL objectInRangeVis(BASE_OBJECT *psList, SDWORD x, SDWORD y, SDWORD range, SDWORD lookingPlayer);
extern SDWORD getPlayerFromString(char *playerName);
#endif

View File

@ -37,6 +37,7 @@
#include "intfac.h"
#include "multimenu.h"
#include "lib/framework/input.h" //for key constants
#include "lib/script/chat_processing.h"
/* The table of user defined types
@ -947,15 +948,6 @@ FUNC_SYMBOL asFuncTable[] =
1, { VAL_STRING },
0, 0, NULL, 0, 0, NULL, NULL },
{ "getTargetPlayers", scrGetTargetPlayers, VAL_INT,
1, { VAL_REF|VAL_STRING },
0, 0, NULL, 0, 0, NULL, NULL },
{ "match", scrMatch, VAL_BOOL,
3, { VAL_STRING, VAL_STRING, VAL_REF|VAL_INT },
0, 0, NULL, 0, 0, NULL, NULL },
{ "bitSet", scrBitSet, VAL_BOOL,
2, { VAL_INT, VAL_INT },
0, 0, NULL, 0, 0, NULL, NULL },
@ -1316,12 +1308,29 @@ FUNC_SYMBOL asFuncTable[] =
2, { VAL_STRING, VAL_INT },
0, 0, NULL, 0, 0, NULL, NULL },
//scripting engine chat interface functions
{ "processChatMsg", scrProcessChatMsg, VAL_INT,
1, { VAL_STRING },
0, 0, NULL, 0, 0, NULL, NULL },
{ "getChatCmdDescription", scrGetChatCmdDescription, VAL_STRING,
1, { VAL_INT },
0, 0, NULL, 0, 0, NULL, NULL },
{ "getNumArgsInCmd", scrGetNumArgsInCmd, VAL_INT,
1, { VAL_INT },
0, 0, NULL, 0, 0, NULL, NULL },
{ "getChatCmdParam", scrGetChatCmdParam, VAL_BOOL,
3, { VAL_REF | VAL_VOID, VAL_INT, VAL_INT },
0, 0, NULL, 0, 0, NULL, NULL },
{ "chatCmdIsPlayerAddressed", scrChatCmdIsPlayerAddressed, VAL_BOOL,
2, { VAL_INT, VAL_INT },
0, 0, NULL, 0, 0, NULL, NULL },
/* END new functions */
// { "skOrderDroidLineBuild", scrSkOrderDroidLineBuild, VAL_VOID,
// 6, { (INTERP_TYPE)ST_DROID, (INTERP_TYPE)ST_STRUCTURESTAT, VAL_INT, VAL_INT, VAL_INT, VAL_INT } },
/* This final entry marks the end of the function list */
{ "FUNCTION LIST END", NULL, VAL_VOID, 0, { VAL_VOID }, 0, 0, NULL, 0, 0, NULL, NULL }
};
@ -2155,6 +2164,10 @@ BOOL scrTabInitialise(void)
for(i=0; i<DEBUGMENU_MAX_ENTRIES; i++)
debugMenuEntry[i][0] = '\0';
/* Initialize chat message struct */
chat_msg.numCommands = 0;
strcpy(chat_msg.lastMessage, "");
return TRUE;
}

View File

@ -298,6 +298,14 @@
RelativePath="..\src\cdspan.c"
>
</File>
<File
RelativePath="..\lib\script\chat_lexer.c"
>
</File>
<File
RelativePath="..\lib\script\chat_parser.c"
>
</File>
<File
RelativePath="..\src\cheat.c"
>
@ -1130,6 +1138,14 @@
RelativePath="..\src\cdspan.h"
>
</File>
<File
RelativePath="..\lib\script\chat_parser.h"
>
</File>
<File
RelativePath="..\lib\script\chat_processing.h"
>
</File>
<File
RelativePath="..\src\cheat.h"
>