/* This file is part of Warzone 2100. Copyright (C) 2006-2008 Roman Copyright (C) 2006-2010 Warzone 2100 Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* * chat_parser.y * * yacc grammar for multiplayer chat messages * */ #include "lib/framework/frame.h" #include "lib/framework/string_ext.h" #include "lib/framework/frameresource.h" #include "lib/script/chat_processing.h" #define MAX_CHAT_ARGUMENTS 10 /* Holds information about a processed player chat message */ CHAT_MSG chat_msg; // Parameter variable static INTERP_VAL scrParameter; extern int chat_lex(void); // Store extracted command for use in scripts static void chat_store_command(const 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(const 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= 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 R_PLAYER %token R_INTEGER /* Rules */ %type R_PLAYER_POSSESSION %% /* 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"); } | R_GETTING_ENEMY_DERRICK /* gonna get blue's derrick */ { chat_store_command("getting player oil"); } | R_LASSAT_PLAYER /* lassat red */ { chat_store_command("lassat player"); } | R_WAIT_FOR_ME /* wait for me */ { chat_store_command("wait for me"); } | R_RUSH_INITIATION /* rush? */ { chat_store_command("rush"); } ; /* 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); } ; R_PLAYER_POSSESSION: R_PLAYER R_POSSESSION /* blue's */ { $$ = $1; // just pass player index } ; /* A Article or nothing */ R_A_OR_EMPTY: /* Empty */ | _T_A | _T_AN | _T_THE ; /* 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_CONSTRUCTING: _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 ? */ | R_QUANTITY /* any ? */ ; R_CONFIDENCE_EXPRESSION: _T_SURE | _T_OFCOURSE ; R_AGREEMENT_EXPRESSION: _T_YES | _T_FINE | _T_OK ; R_AFFIRMATIVE_FORMS: R_AGREEMENT_EXPRESSION | R_CONFIDENCE_EXPRESSION | _T_ROGER /* roger */ | _T_AFFIRMATIVE ; /* Need */ R_NEED: _T_NEED | _T_REQUIRE ; R_RUSH_INITIATION: _T_RUSH R_EOS /* rush? */ | _T_LETS _T_RUSH R_EOS /* let's rush */ ; /*******************************************/ /* 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? */ | _T_READY R_EOS /* "ready!" or "ready?" */ | _T_IM _T_READY R_EOS /* I'm ready! */ ; /* 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? */ /* Units */ R_UNIT_ARMY: R_A_OR_EMPTY _T_FORCE | R_A_OR_EMPTY _T_ARMY ; /* Player is building units */ R_BUILDING_UNITS: R_CONSTRUCTING _T_UNITS R_EOD /* pumping units */ | R_CONSTRUCTING _T_MORE _T_UNITS R_EOD /* pumping more units */ | R_CONSTRUCTING R_UNIT_ARMY R_EOD /* building army */ ; /* 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!?!? */ | _T_I R_DEMAND_HELP /* I need help */ | R_NEED _T_HELP R_EOS /* need help */ | R_NEED R_QUANTITY _T_HELP R_EOS /* need some help */ ; R_GRATITUDE: _T_THANK_YOU | _T_THANKS ; /* Tell player i'm safe - no danger anymore */ R_REPORT_SAFETY: _T_IM _T_OK R_EOD /* i'm ok */ | _T_IM _T_FINE R_EOD | R_REPORT_SAFETY _T_NOW R_EOD | R_REPORT_SAFETY R_GRATITUDE R_EOD | R_REPORT_SAFETY _T_COMMA R_GRATITUDE R_EOD | R_GRATITUDE R_REPORT_SAFETY R_EOD | R_GRATITUDE _T_COMMA R_REPORT_SAFETY R_EOD | R_GRATITUDE R_GRATITUDE R_EOD ; /* Positive order feedback */ R_AFFIRMATIVE: R_AFFIRMATIVE_FORMS R_EOD /* yes, roger etc */ | R_AFFIRMATIVE R_AFFIRMATIVE_FORMS R_EOD /* accept repetitions: "yes yes yeah" */ // | R_AFFIRMATIVE /* 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)) { yyerror("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)) { yyerror("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)) { yyerror("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)) { yyerror("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)) { yyerror("chat command: Too many parameters in the message"); } } ; R_GETTING_ENEMY_DERRICK: _T_GONNA R_INITIATE_ATTACK R_PLAYER_POSSESSION _T_DERRICK R_EOD /* gonna get blue's derrick */ { /* Store for scripts */ scrParameter.type = VAL_INT; scrParameter.v.ival = $3; if(!chat_store_parameter(&scrParameter)) { yyerror("chat command: Too many parameters in the message"); } } ; R_LASSAT_PLAYER: _T_LASSAT R_PLAYER R_EOD /* gonna get blue's derrick */ { /* Store for scripts */ scrParameter.type = VAL_INT; scrParameter.v.ival = $2; if(!chat_store_parameter(&scrParameter)) { yyerror("chat command: Too many parameters in the message"); } } ; R_WAIT_FOR_ME: _T_WAIT R_EOD /* wait */ | _T_SEC R_EOD /* sec... */ | _T_WAIT _T_FOR _T_ME R_EOD /* wait for me */ | _T_HOLD _T_ON R_EOD /* hold on */ ; %% /* 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])) == 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