diff options
author | truelight <truelight@openttd.org> | 2005-12-05 12:27:58 +0000 |
---|---|---|
committer | truelight <truelight@openttd.org> | 2005-12-05 12:27:58 +0000 |
commit | a7a5a3a8e8a3c1de2ac6c89800b45f79a67ca5e7 (patch) | |
tree | 9ac101fe0118919eae98a05abc9e97de6f1f5e33 | |
parent | cfc4753ba6847d1530bb2656627a0152c21690ce (diff) | |
download | openttd-a7a5a3a8e8a3c1de2ac6c89800b45f79a67ca5e7.tar.xz |
(svn r3260) -Add: add events for AIs to check if a command execution failed or succeeded
-rw-r--r-- | ai/ai.c | 91 | ||||
-rw-r--r-- | ai/ai.h | 2 | ||||
-rw-r--r-- | ai/ai_event.h | 7 | ||||
-rw-r--r-- | command.c | 12 |
4 files changed, 107 insertions, 5 deletions
@@ -15,6 +15,13 @@ #include "ai_event.h" #undef DEF_EVENTS +/* Here we keep track of all commands that are called via AI_DoCommandChecked, + * in special we save the unique_id here. Now this id is given back + * when the command fails or succeeds and is detected as added in this storage. */ +static AICommand *command_uid[MAX_PLAYERS]; +static AICommand *command_uid_tail[MAX_PLAYERS]; +static uint uids[MAX_PLAYERS]; + /** * Dequeues commands put in the queue via AI_PutCommandInQueue. */ @@ -122,6 +129,72 @@ int32 AI_DoCommand(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc) return res; } +int32 AI_DoCommandChecked(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc) +{ + AICommand *new; + uint unique_id = uids[_current_player]++; + int32 res; + + res = AI_DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc); + if (CmdFailed(res)) + return -1; + + /* Save the command and his things, together with the unique_id */ + new = malloc(sizeof(AICommand)); + new->tile = tile; + new->p1 = p1; + new->p2 = p2; + new->procc = procc; + new->next = NULL; + new->dp[0] = unique_id; + + /* Add it to the back of the list */ + if (command_uid_tail[_current_player] == NULL) + command_uid_tail[_current_player] = new; + else + command_uid_tail[_current_player]->next = new; + + if (command_uid[_current_player] == NULL) + command_uid[_current_player] = new; + + AI_DoCommand(tile, p1, p2, flags, procc); + return unique_id; +} + +/** + * A command is executed for real, and is giving us his result (failed yes/no). Inform the AI with it via + * an event.Z + */ +void AI_CommandResult(uint32 cmd, uint32 p1, uint32 p2, TileIndex tile, bool succeeded) +{ + AICommand *command = command_uid[_current_player]; + + if (command == NULL) + return; + + if (command->procc != (cmd & 0xFF) || command->p1 != p1 || command->p2 != p2 || command->tile != tile) { + /* If we come here, we see a command that isn't at top in our list. This is okay, if the command isn't + * anywhere else in our list, else we have a big problem.. so check for that. If it isn't in our list, + * it is okay, else, drop the game. + * Why do we have a big problem in the case it is in our list? Simply, we have a command sooner in + * our list that wasn't triggered to be failed or succeeded, so it is sitting there without an + * event, so the script will never know if it succeeded yes/no, so it can hang.. this is VERY bad + * and should never ever happen. */ + while (command != NULL && (command->procc != (cmd & 0xFF) || command->p1 != p1 || command->p2 != p2 || command->tile != tile)) { + command = command->next; + } + assert(command == NULL); + return; + } + + command_uid[_current_player] = command_uid[_current_player]->next; + if (command_uid[_current_player] == NULL) + command_uid_tail[_current_player] = NULL; + + ai_event(_current_player, succeeded ? ottd_Event_CommandSucceeded : ottd_Event_CommandFailed, tile, command->dp[0]); + free(command); +} + /** * Run 1 tick of the AI. Don't overdo it, keep it realistic. */ @@ -249,7 +322,7 @@ void AI_LoadAIControl(void) */ void AI_PrintErrorStack(gpmi_err_stack_t *entry, char *string) { - DEBUG(ai, 0)(string); + DEBUG(ai, 0)("%s", string); } #endif /* GPMI */ @@ -305,6 +378,17 @@ void AI_PlayerDied(PlayerID player) /* Called if this AI died */ _ai_player[player].active = false; + if (command_uid[player] != NULL) { + while (command_uid[player] != NULL) { + AICommand *command = command_uid[player]; + command_uid[player] = command->next; + free(command); + } + + command_uid[player] = NULL; + command_uid_tail[player] = NULL; + } + #ifdef GPMI if (_ai_player[player].module != NULL) gpmi_mod_unload(_ai_player[player].module); @@ -326,7 +410,10 @@ void AI_Initialize(void) AI_Uninitialize(); memset(&_ai, 0, sizeof(_ai)); - memset(&_ai_player, 0, sizeof(_ai_player)); + memset(_ai_player, 0, sizeof(_ai_player)); + memset(uids, 0, sizeof(uids)); + memset(command_uid, 0, sizeof(command_uid)); + memset(command_uid_tail, 0, sizeof(command_uid_tail)); _ai.network_client = tmp_ai_network_client; _ai.network_playas = OWNER_SPECTATOR; @@ -57,6 +57,8 @@ void AI_RunGameLoop(void); void AI_Initialize(void); void AI_Uninitialize(void); int32 AI_DoCommand(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc); +int32 AI_DoCommandChecked(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc); +void AI_CommandResult(uint32 cmd, uint32 p1, uint32 p2, TileIndex tile, bool failed); /** Is it allowed to start a new AI. * This function checks some boundries to see if we should launch a new AI. diff --git a/ai/ai_event.h b/ai/ai_event.h index 006f7b740..684ce54b5 100644 --- a/ai/ai_event.h +++ b/ai/ai_event.h @@ -9,9 +9,9 @@ /* This is how we call events (with safety-check) to GPMI */ /* XXX -- This macro (vararg macro) isn't supported on old GCCs, but GPMI * is using them too, so it isn't a real problem, yet */ -# define ai_event(player, params...) \ +# define ai_event(player, event, params...) \ if (player < MAX_PLAYERS && _ai_player[player].module != NULL) \ - gpmi_event(_ai_player[player].module, params) + gpmi_event(_ai_player[player].module, event, params) #else /* If GPMI isn't loaded, don't do a thing with the events (for now at least) @@ -40,6 +40,9 @@ #endif /* DEF_EVENTS */ /* ------------ All available events -------------- */ +DEF_EVENTS int ottd_Event_CommandFailed INITIAL_SET; // (tile, unique_id) +DEF_EVENTS int ottd_Event_CommandSucceeded INITIAL_SET; // (tile, unique_id) + DEF_EVENTS int ottd_Event_BuildStation INITIAL_SET; // (station_index, station_tile) DEF_EVENTS int ottd_Event_BuildRoadStation INITIAL_SET; // (station_index, station_tile) @@ -10,6 +10,7 @@ #include "player.h" #include "network.h" #include "variables.h" +#include "ai/ai.h" const char* _cmd_text = NULL; @@ -476,10 +477,16 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, res = proc(x,y, flags, p1, p2); if (CmdFailed(res)) { if (res & 0xFFFF) _error_message = res & 0xFFFF; + /* Trigger an event special for the AI, so it knows the build has failed + * Because the commands are always delayed, this is the only way. */ + AI_CommandResult(cmd, p1, p2, tile, false); goto show_error; } // no money? Only check if notest is off - if (!notest && res != 0 && !CheckPlayerHasMoney(res)) goto show_error; + if (!notest && res != 0 && !CheckPlayerHasMoney(res)) { + AI_CommandResult(cmd, p1, p2, tile, false); + goto show_error; + } } #ifdef ENABLE_NETWORK @@ -514,10 +521,13 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, } else { if (CmdFailed(res2)) { if (res2 & 0xFFFF) _error_message = res2 & 0xFFFF; + AI_CommandResult(cmd, p1, p2, tile, false); goto show_error; } } + AI_CommandResult(cmd, p1, p2, tile, true); + SubtractMoneyFromPlayer(res2); if (IsLocalPlayer() && _game_mode != GM_EDITOR) { |