src/ai/ai.cpp
author truebrain
Fri, 13 Jun 2008 20:19:00 +0000
branchnoai
changeset 10958 65088d587094
parent 10891 5ebb6f9068d0
child 11098 37d15a8951b8
permissions -rw-r--r--
(svn r13512) [NoAI] -Fix: don't load a library over and over, but keep track of which libraries we have loaded (per AI) and re-use it where possible (reduces memory-footprint)
[NoAI] -Fix: change the fake-library-name-counter to a per AI value, not global
[NoAI] -Fix: Load the script inside the thread, not in the main thread. This avoids unneeded error-handling
/* $Id$ */

/** @file ai.cpp Handles the communication between the AI layer and the OpenTTD core */

#include "../stdafx.h"
#include "../openttd.h"
#include "../variables.h"
#include "../command_func.h"
#include "../network/network.h"
#include "../core/alloc_func.hpp"
#include "../settings_type.h"
#include "../player_base.h"
#include "../player_func.h"
#include "../debug.h"

#ifndef NO_THREADS
#include <squirrel.h>
#include "../squirrel.hpp"
#include "../squirrel_helper.hpp"
#include "../squirrel_class.hpp"
#include "../squirrel_std.hpp"
#include "api/ai_controller.hpp"
#include "ai_squirrel.hpp"
#include "ai_info.hpp"
#include "ai.h"
#include "ai_threads.h"
#include "api/ai_base.hpp"
#include "../signal_func.h"
#include "../window_func.h"

static AIController *_ai_player[MAX_PLAYERS];
static AIInfo *_ai_info[MAX_PLAYERS];
static AISquirrel *_ai_squirrel = NULL;
static uint _ai_frame_counter;
static bool _ai_enabled;
static char *_forced_ai_name = NULL;

/**
 * The gameloop for AIs.
 *  Handles one tick for all the AIs.
 */
void AI_RunGameLoop()
{
	/* Don't do anything if ai is disabled */
	if (!_ai_enabled) return;

	/* Don't do anything if we are a network-client, or the AI has been disabled */
	if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;

	/* New tick */
	_ai_frame_counter++;

	/* Make sure the AI follows the difficulty rule.. */
	assert(_settings_game.difficulty.competitor_speed <= 4);
	if ((_ai_frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;

	/* Check for AI-client (so joining a network with an AI) */
	if (!_networking || _network_server) {
		/* Check if we want to run AIs (server or SP only) */
		const Player *p;

		FOR_ALL_PLAYERS(p) {
			if (p->is_active && p->is_ai) {
				/* Run the script */
				if (_ai_player[p->index] == NULL) continue;
				_current_player = p->index;
				AI_RunTick(p->index);
			}
		}
	}

	_current_player = OWNER_NONE;
}

void AI_Event(PlayerID player, AIEvent *event)
{
	if (player >= MAX_PLAYERS || !GetPlayer(player)->is_active || !GetPlayer(player)->is_ai) return;
	DEBUG(ai, 5, "Event (%d) for player %d\n", event->GetEventType(), player);

	PlayerID old_player = _current_player;
	_current_player = player;
	AIEventController::InsertEvent(event);
	_current_player = old_player;
}

/**
 * A new AI sees the day of light. You can do here what ever you think is needed.
 * @return True if the AI is started.
 */
bool AI_StartNewAI(PlayerID player)
{
	assert(IsValidPlayer(player));
	if (!_ai_enabled) return false;
	if (_networking && !_network_server) return false;

	AIInfo *info;
	if (_forced_ai_name == NULL) {
		info = _ai_squirrel->SelectRandomAI();
	} else {
		info = _ai_squirrel->SelectAI(_forced_ai_name);
	}
	if (info == NULL) {
		if (_forced_ai_name == NULL) {
			DEBUG(ai, 0, "Couldn't find a suitable AI to start for company '%d'", player);
		} else {
			DEBUG(ai, 0, "The AI named \"%s\" is unknown or not suitable", _forced_ai_name);
		}
		return false;
	}

	_current_player = player;
	_ai_info[player] = info;
	AIObject::ResetInternalPlayerData();
	_ai_player[player] = info->CreateInstance();
	AI_StartPlayer(player, _ai_player[player]);
	InvalidateWindowData(WC_AI_DEBUG, 0, -1);
	return true;
}

/**
 * This AI player died. Give it some chance to make a final puf.
 */
void AI_PlayerDied(PlayerID player)
{
	if (!_ai_enabled) return;

	_current_player = player;
	AI_StopPlayer(player);
	/* Called if this AI died */
	delete _ai_player[player];
	_ai_player[player] = NULL;
	_ai_info[player] = NULL;
	InvalidateWindowData(WC_AI_DEBUG, 0, -1);
}

/**
 * Initialize some AI-related stuff.
 */
void AI_Initialize()
{
	/* First, make sure all AIs are DEAD! */
	AI_Uninitialize();

	memset(&_ai_player, 0, sizeof(_ai_player));
	memset(&_ai_info, 0, sizeof(_ai_info));
	_ai_frame_counter = 0;
	_ai_enabled = true;

	/* Create the Squirrel collector, which scans for all AIs available */
	assert(_ai_squirrel == NULL);
	_ai_squirrel = new AISquirrel();
}

/**
 * Forces the given AI to be used for all players.
 * Pass NULL to use a random AI.
 * @param forced_ai the AI to force.
 */
void AI_ForceAI(const char *forced_ai)
{
	free(_forced_ai_name);
	_forced_ai_name = (forced_ai == NULL) ? NULL : strdup(forced_ai);
}

char *AI_GetConsoleList(char *p, const char *last)
{
	return _ai_squirrel->GetAIConsoleList(p, last);
}

AIInfo *AI_GetPlayerInfo(PlayerID player)
{
	return _ai_info[player];
}

/**
 * Kill all AIs.
 */
void AI_KillAll()
{
	const Player* p;

	FOR_ALL_PLAYERS(p) {
		if (p->is_active && p->is_ai) AI_PlayerDied(p->index);
	}
}

/**
 * Deinitializer for AI-related stuff.
 */
void AI_Uninitialize()
{
	AI_KillAll();
	delete _ai_squirrel;
	_ai_squirrel = NULL;
}

/**
 * Is it allowed to start a new AI.
 * This function checks some boundries to see if we should launch a new AI.
 * @return True if we can start a new AI.
 */
bool AI_AllowNewAI()
{
	/* If disabled, no AI */
	if (!_ai_enabled) return false;

	/* If in network, but no server, no AI */
	if (_networking && !_network_server) return false;

	/* If in network, and server, possible AI */
	if (_networking && _network_server) {
		/* Do we want AIs in multiplayer? */
		if (!_settings_game.ai.ai_in_multiplayer) return false;
	}

	return true;
}

bool AI_ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
{
	return _ai_squirrel->ImportLibrary(library, class_name, version, vm, _ai_player[_current_player]);
}

void AI_Rescan()
{
	_ai_squirrel->RescanAIDir();
}

#else /* NO_THREADS */
/* Stub functions for when we do not have threads. */
void AI_StartNewAI(PlayerID player) { DEBUG(ai, 0, "Threading is disabled and therefor the AI too."); }
void AI_PlayerDied(PlayerID player) {}
void AI_RunGameLoop() {}
void AI_Event(PlayerID player, AIEvent *event) {}
void AI_Initialize() {}
void AI_Uninitialize() {}
bool AI_AllowNewAI() { return false; }
void AI_ForceAI(const char *forced_ai) {}
char *AI_GetConsoleList(char *p, const char *last) { return p; }
AIInfo *AI_GetPlayerInfo(PlayerID player) { return NULL; }
bool AI_ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm) { return false; }
void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2) {}
void AI_Rescan() {}
#endif /* NO_THREADS */