src/ai/ai.cpp
author rubidium
Sun, 18 Mar 2007 23:14:44 +0000
branchnoai
changeset 9467 730cae121ae3
parent 9454 ee6a65b37b82
child 9621 7654501cf02d
permissions -rw-r--r--
(svn r9305) [NoAI] -Add: option to force-select an AI from the console.
/* $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.h"
#include "../network/network.h"
#include "../helpers.hpp"
#include "../debug.h"
#include "ai.h"
#include "ai_threads.h"
#include "ai_factory.hpp"
#include "api/ai_base.hpp"
#include "api/ai_controller.hpp"

static AIController *_ai_player[MAX_PLAYERS];
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 || !_patches.ai_in_multiplayer)) return;

	/* New tick */
	_ai_frame_counter++;

	/* Make sure the AI follows the difficulty rule.. */
	assert(_opt.diff.competitor_speed <= 4);
	if ((_ai_frame_counter & ((1 << (4 - _opt.diff.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;
}

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

	if (_forced_ai_name == NULL) {
		_ai_player[player] = AIFactoryBase::SelectRandomAI();
	} else {
		_ai_player[player] = AIFactoryBase::SelectAI(_forced_ai_name);
	}
	if (_ai_player[player] == 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);
		}
		/* XXX -- We have no clean way to handle this yet! */
		return;
	}
	_current_player = player;
	AIObject::ResetInternalPlayerData();
	AI_StartPlayer(player, _ai_player[player]);
}

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

	AI_StopPlayer(player);
	/* Called if this AI died */
	delete _ai_player[player];
	_ai_player[player] = NULL;
}

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

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

	AIFactoryBase::RunInitializers();
}

/**
 * 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);
}


/**
 * Deinitializer for AI-related stuff.
 */
void AI_Uninitialize()
{
	const Player* p;

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

/**
 * 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 (!_patches.ai_in_multiplayer) return false;
	}

	return true;
}